diff --git a/packages/core/package.json b/packages/core/package.json index 43c5cad3..67b18411 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -80,6 +80,7 @@ "image-size": "^1.1.1", "ioredis": "^5.4.1", "isbinaryfile": "^5.0.2", + "isolated-vm": "^6.0.0", "joi": "^17.13.1", "js-yaml": "^4.1.0", "jsonrepair": "^3.8.0", diff --git a/packages/core/src/Core/ConnectorsService.ts b/packages/core/src/Core/ConnectorsService.ts index 4c7231ff..f2dcfab1 100644 --- a/packages/core/src/Core/ConnectorsService.ts +++ b/packages/core/src/Core/ConnectorsService.ts @@ -18,6 +18,7 @@ import { ManagedVaultConnector } from '@sre/Security/ManagedVault.service/Manage import { LogConnector } from '@sre/IO/Log.service/LogConnector'; import { ComponentConnector } from '@sre/AgentManager/Component.service/ComponentConnector'; import { ModelsProviderConnector } from '@sre/LLMManager/ModelsProvider.service/ModelsProviderConnector'; +import { CodeConnector } from '@sre/ComputeManager/Code.service/CodeConnector'; const console = Logger('ConnectorService'); let ServiceRegistry: TServiceRegistry = {}; @@ -182,9 +183,8 @@ export class ConnectorService { return ConnectorService.getInstance(TConnectorService.Router, name); } - - static getCodeConnector(name?: string): RouterConnector { - return ConnectorService.getInstance(TConnectorService.Code, name); + static getCodeConnector(name?: string): CodeConnector { + return ConnectorService.getInstance(TConnectorService.Code, name); } } diff --git a/packages/core/src/helpers/ECMASandbox.helper.ts b/packages/core/src/helpers/ECMASandbox.helper.ts new file mode 100644 index 00000000..ac56b15a --- /dev/null +++ b/packages/core/src/helpers/ECMASandbox.helper.ts @@ -0,0 +1,126 @@ +import ivm from 'isolated-vm'; +import https from 'https'; + +function extractFetchUrls(str) { + const regex = /\/\/@fetch\((https?:\/\/[^\s]+)\)/g; + let match; + const urls = []; + + while ((match = regex.exec(str)) !== null) { + urls.push(match[1]); + } + + return urls; +} +function fetchCodeFromCDN(url) { + return new Promise((resolve, reject) => { + https + .get(url, (res) => { + let data = ''; + res.on('data', (chunk) => (data += chunk)); + res.on('end', () => resolve(data)); + }) + .on('error', reject); + }); +} + +async function setupIsolate() { + try { + const isolate = new ivm.Isolate({ memoryLimit: 128 }); + const context = await isolate.createContext(); + const jail = context.global; + await jail.set('global', jail.derefInto()); + // Define a SafeBuffer object + const ___internal = { + b64decode: (str) => Buffer.from(str, 'base64').toString('utf8'), + b64encode: (str) => Buffer.from(str, 'utf8').toString('base64'), + }; + + //const closureStr = + const keys = Object.keys(___internal); + const functions = keys.map((key) => ___internal[key]); + const closure = ` + globalThis.___internal = { + ${keys.map((key, i) => `${key}: $${i}`).join(',\n')} + }`; + + await context.evalClosure(closure, functions); + + return { isolate, context, jail }; + } catch (error) { + console.error(error); + throw error; + } +} + +export async function runJs(code: string) { + try { + if (!code) { + throw new Error('No code provided'); + } + + if (!code.endsWith(';')) code += ';'; + + const { isolate, context, jail } = await setupIsolate(); + const remoteUrls = await extractFetchUrls(code); + for (const url of remoteUrls) { + const remoteCode = await fetchCodeFromCDN(url); + await context.eval(`${remoteCode}`); + } + + const executionCode = ` + (async () => { + ${code} + globalThis.__finalResult = result; + })(); + `; + + // Execute the original code + const executeScript = await isolate.compileScript(executionCode).catch((err) => { + console.error(err); + return { error: 'Compile Error - ' + err.message }; + }); + if ('error' in executeScript) { + throw new Error(executeScript.error); + } + + await executeScript.run(context).catch((err) => { + console.error(err); + throw new Error('Run Error - ' + err.message); + }); + + // Try to get the result from the global variable first, then fallback to 'result' + let rawResult = await context.eval('globalThis.__finalResult').catch((err) => { + console.error('Failed to get __finalResult:', err); + return null; + }); + + if (rawResult?.error) { + throw new Error(rawResult.error); + } + return { Output: rawResult }; + } catch (error) { + console.error(error); + throw new Error(error.message); + } +} + +function getParametersString(parameters: string[], inputs: Record) { + let params = []; + for (const parameter of parameters) { + if (typeof inputs[parameter] === 'string') { + params.push(`'${inputs[parameter]}'`); + } else { + params.push(`${inputs[parameter]};`); + } + } + return params.join(','); +} + +export function generateExecutableCode(code: string, parameters: string[], inputs: Record) { + const executableCode = ` + ${code} + const result = await main(${getParametersString(parameters, inputs)}); + ` + return executableCode; +} \ No newline at end of file diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f330efc5..0faa4f32 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -56,6 +56,7 @@ export * from './Core/SystemEvents'; export * from './helpers/AWSLambdaCode.helper'; export * from './helpers/BinaryInput.helper'; export * from './helpers/Conversation.helper'; +export * from './helpers/ECMASandbox.helper'; export * from './helpers/JsonContent.helper'; export * from './helpers/LocalCache.helper'; export * from './helpers/Log.helper'; @@ -149,6 +150,7 @@ export * from './subsystems/AgentManager/AgentData.service/connectors/LocalAgent export * from './subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class'; export * from './subsystems/AgentManager/Component.service/connectors/LocalComponentConnector.class'; export * from './subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class'; +export * from './subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class'; export * from './subsystems/IO/Log.service/connectors/ConsoleLog.class'; export * from './subsystems/IO/NKV.service/connectors/NKVLocalStorage.class'; export * from './subsystems/IO/NKV.service/connectors/NKVRAM.class'; diff --git a/packages/core/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts b/packages/core/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts new file mode 100644 index 00000000..62249e66 --- /dev/null +++ b/packages/core/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts @@ -0,0 +1,118 @@ +import { IAccessCandidate, TAccessLevel } from '@sre/types/ACL.types'; +import { ACL } from '@sre/Security/AccessControl/ACL.class'; +import { CodeConfig, CodePreparationResult, CodeConnector, CodeInput, CodeDeployment, CodeExecutionResult } from '../CodeConnector'; +import { AccessRequest } from '@sre/Security/AccessControl/AccessRequest.class'; +import { Logger } from '@sre/helpers/Log.helper'; +import axios from 'axios'; +import { generateExecutableCode, runJs } from '@sre/helpers/ECMASandbox.helper'; +import { validateAsyncMainFunction } from '@sre/helpers/AWSLambdaCode.helper'; + +const console = Logger('ECMASandbox'); +export class ECMASandbox extends CodeConnector { + public name = 'ECMASandbox'; + private sandboxUrl: string; + + constructor(config: { sandboxUrl: string }) { + super(config); + this.sandboxUrl = config.sandboxUrl; + } + public async prepare(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise { + return { + prepared: true, + errors: [], + warnings: [], + }; + } + + public async deploy(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise { + return { + id: codeUID, + runtime: config.runtime, + createdAt: new Date(), + status: 'Deployed', + }; + } + + public async execute(acRequest: AccessRequest, codeUID: string, inputs: Record, config: CodeConfig): Promise { + try { + const { isValid, error, parameters } = validateAsyncMainFunction(inputs.code); + if (!isValid) { + return { + output: undefined, + executionTime: 0, + success: false, + errors: [error], + } + } + const executableCode = generateExecutableCode(inputs.code, parameters, inputs.inputs); + if (!this.sandboxUrl) { + // run js code in isolated vm + console.debug('Running code in isolated vm'); + const result = await runJs(executableCode); + console.debug(`Code result: ${result}`); + return { + output: result?.Output, + executionTime: 0, + success: true, + errors: [], + }; + } else { + console.debug('Running code in remote sandbox'); + const result: any = await axios.post(this.sandboxUrl, { code: executableCode }).catch((error) => ({ error })); + if (result.error) { + + const error = result.error?.response?.data || result.error?.message || result.error.toString() || 'Unknown error'; + console.error(`Error running code: ${JSON.stringify(error, null, 2)}`); + return { + output: undefined, + executionTime: 0, + success: false, + errors: [error], + }; + } else { + console.debug(`Code result: ${result?.data?.Output}`); + return { + output: result.data?.Output, + executionTime: 0, + success: true, + errors: [], + }; + } + } + } catch (error) { + console.error(`Error running code: ${error}`); + return { + output: undefined, + executionTime: 0, + success: false, + errors: [error], + }; + } + } + public async executeDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, inputs: Record, config: CodeConfig): Promise { + const result = await this.execute(acRequest, codeUID, inputs, config); + return result; + } + + public async listDeployments(acRequest: AccessRequest, codeUID: string, config: CodeConfig): Promise { + return []; + } + + public async getDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, config: CodeConfig): Promise { + return null; + } + + public async deleteDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string): Promise { + return; + } + + public async getResourceACL(resourceId: string, candidate: IAccessCandidate): Promise { + const acl = new ACL(); + + //give Read access everytime + //FIXME: !!!!!! IMPORTANT !!!!!! this implementation have to be changed in order to reflect the security model of AWS Lambda + acl.addAccess(candidate.role, candidate.id, TAccessLevel.Read); + + return acl; + } +} diff --git a/packages/core/src/subsystems/ComputeManager/Code.service/index.ts b/packages/core/src/subsystems/ComputeManager/Code.service/index.ts index 3d0e8a29..ceeb9646 100644 --- a/packages/core/src/subsystems/ComputeManager/Code.service/index.ts +++ b/packages/core/src/subsystems/ComputeManager/Code.service/index.ts @@ -3,9 +3,11 @@ import { ConnectorService, ConnectorServiceProvider } from '@sre/Core/ConnectorsService'; import { TConnectorService } from '@sre/types/SRE.types'; import { AWSLambdaCode } from './connectors/AWSLambdaCode.class'; +import { ECMASandbox } from './connectors/ECMASandbox.class'; export class CodeService extends ConnectorServiceProvider { public register() { ConnectorService.register(TConnectorService.Code, 'AWSLambda', AWSLambdaCode); + ConnectorService.register(TConnectorService.Code, 'ECMASandbox', ECMASandbox); } } diff --git a/packages/core/tests/unit/core/ecma-sandbox.test.ts b/packages/core/tests/unit/core/ecma-sandbox.test.ts new file mode 100644 index 00000000..ec0a93fa --- /dev/null +++ b/packages/core/tests/unit/core/ecma-sandbox.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from 'vitest'; +import { setupSRE } from '../../utils/sre'; +import { ConnectorService } from '@sre/Core/ConnectorsService'; +import { IAccessCandidate, TAccessRole } from 'index'; + +setupSRE({ + Code: { + Connector: 'ECMASandbox', + Settings: { + sandboxUrl: 'http://localhost:6100/run-js/v2', + }, + }, + Log: { + Connector: 'ConsoleLog', + }, +}); + +describe('ECMASandbox Tests', () => { + it( + 'Runs a simple code and returns the output', + async () => { + const mockCandidate: IAccessCandidate = { + id: 'test-user', + role: TAccessRole.User, + }; + + const codeConnector = ConnectorService.getCodeConnector('ECMASandbox'); + const result = await codeConnector.agent(mockCandidate.id).execute(Date.now().toString(), { + code: `async function main(prompt) { return prompt + ' ' + 'Hello World'; }`, + inputs: { + prompt: 'Say' + } + }); + + const output = result.output; + expect(output).toBe('Say Hello World'); + }, + ); + it( + 'Try to run a simple code without main function', + async () => { + const mockCandidate: IAccessCandidate = { + id: 'test-user', + role: TAccessRole.User, + }; + + const codeConnector = ConnectorService.getCodeConnector('ECMASandbox'); + const result = await codeConnector.agent(mockCandidate.id).execute(Date.now().toString(), { + code: `async function testFunction(prompt) { return prompt + ' ' + 'Hello World'; }`, + inputs: { + prompt: 'Say' + } + }); + const error = result.errors; + expect(error).toContain('No main function found at root level'); + }, + ); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7bc6ee65..45ef82e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,10 @@ overrides: importers: .: + dependencies: + list: + specifier: link:C:/Users/kaddouri/AppData/Local/pnpm/global/5/node_modules/list + version: link:packages/core/C:/Users/kaddouri/AppData/Local/pnpm/global/5/node_modules/list devDependencies: '@vitest/coverage-v8': specifier: ^3.1.4 @@ -249,6 +253,9 @@ importers: isbinaryfile: specifier: ^5.0.2 version: 5.0.4 + isolated-vm: + specifier: ^6.0.0 + version: 6.0.0 joi: specifier: ^17.13.1 version: 17.13.3 @@ -2356,6 +2363,9 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -2629,6 +2639,10 @@ packages: resolution: {integrity: sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==} engines: {node: '>=12.20'} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + detect-newline@4.0.1: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2811,6 +2825,10 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} @@ -2994,6 +3012,9 @@ packages: fromentries@1.3.2: resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -3077,6 +3098,9 @@ packages: git-hooks-list@3.2.0: resolution: {integrity: sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -3406,6 +3430,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isolated-vm@6.0.0: + resolution: {integrity: sha512-MMhgIssDZ2g3eD5tyqiWPx0k8Ps2H5halFOGGE9cUjNymJ1xL24ebU8EV7J6z2gEXiiX51VNcXM7BJp3355/LA==} + engines: {node: '>=24.0.0'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -3798,6 +3826,9 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} @@ -3846,6 +3877,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-orderby@2.0.3: resolution: {integrity: sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==} @@ -3867,6 +3901,10 @@ packages: resolution: {integrity: sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==} engines: {node: '>= 10.13'} + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + engines: {node: '>=10'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -4126,6 +4164,11 @@ packages: resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4474,6 +4517,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -4669,6 +4718,13 @@ packages: resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} engines: {node: '>=6'} + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + teamcity-service-messages@0.1.14: resolution: {integrity: sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==} @@ -8238,6 +8294,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@1.1.4: {} + clean-stack@2.2.0: {} clean-stack@3.0.1: @@ -8515,6 +8573,8 @@ snapshots: detect-indent@7.0.1: {} + detect-libc@2.0.4: {} + detect-newline@4.0.1: {} diff@4.0.2: {} @@ -8678,6 +8738,8 @@ snapshots: dependencies: eventsource-parser: 3.0.2 + expand-template@2.0.3: {} + expect-type@1.2.1: {} express-rate-limit@7.5.0(express@5.1.0): @@ -8941,6 +9003,8 @@ snapshots: fromentries@1.3.2: {} + fs-constants@1.0.0: {} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 @@ -9029,6 +9093,8 @@ snapshots: git-hooks-list@3.2.0: {} + github-from-package@0.0.0: {} + github-slugger@2.0.0: {} glob-parent@5.1.2: @@ -9382,6 +9448,10 @@ snapshots: isexe@2.0.0: {} + isolated-vm@6.0.0: + dependencies: + prebuild-install: 7.1.3 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-hook@3.0.0: @@ -9816,6 +9886,8 @@ snapshots: minipass@7.1.2: {} + mkdirp-classic@0.5.3: {} + mkdirp@3.0.1: {} mock-stdin@1.0.0: {} @@ -9856,6 +9928,8 @@ snapshots: nanoid@3.3.11: {} + napi-build-utils@2.0.0: {} + natural-orderby@2.0.3: {} negotiator@0.6.3: {} @@ -9883,6 +9957,10 @@ snapshots: transitivePeerDependencies: - supports-color + node-abi@3.75.0: + dependencies: + semver: 7.7.2 + node-domexception@1.0.0: {} node-fetch@2.7.0(encoding@0.1.13): @@ -10193,6 +10271,21 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.4 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.75.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + prelude-ls@1.2.1: {} prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.3): @@ -10600,6 +10693,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -10803,6 +10904,21 @@ snapshots: tapable@2.2.2: {} + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + teamcity-service-messages@0.1.14: {} terser@5.41.0: