diff --git a/packages/api/src/openshell-gateway-info.ts b/packages/api/src/openshell-gateway-info.ts index 9b2e654104..f50e3a778f 100644 --- a/packages/api/src/openshell-gateway-info.ts +++ b/packages/api/src/openshell-gateway-info.ts @@ -50,6 +50,7 @@ export interface CreateSandboxOptions { memory?: string; providers?: string[]; labels?: Record; + uploads?: Array<{ local: string; remote: string }>; command?: string[]; } diff --git a/packages/main/src/plugin/openshell-cli/openshell-cli.spec.ts b/packages/main/src/plugin/openshell-cli/openshell-cli.spec.ts index 37743b5d87..fa4d45d417 100644 --- a/packages/main/src/plugin/openshell-cli/openshell-cli.spec.ts +++ b/packages/main/src/plugin/openshell-cli/openshell-cli.spec.ts @@ -187,6 +187,62 @@ describe('createSandbox', () => { ]); }); + test('includes single --upload flag when provided', async () => { + vi.spyOn(console, 'log').mockImplementation(() => undefined); + vi.mocked(exec.exec).mockResolvedValue(mockExecResult('')); + + await openshellCli.createSandbox({ + uploads: [{ local: '.agents/skills', remote: '.agents/skills' }], + }); + + expect(exec.exec).toHaveBeenCalledWith(OPENSHELL_CLI_PATH, [ + 'sandbox', + 'create', + '--upload', + '.agents/skills:.agents/skills', + ]); + }); + + test('includes multiple --upload flags when provided', async () => { + vi.spyOn(console, 'log').mockImplementation(() => undefined); + vi.mocked(exec.exec).mockResolvedValue(mockExecResult('')); + + await openshellCli.createSandbox({ + uploads: [ + { local: '.agents/skills/generate-sandbox-policy', remote: '.agents/skills/generate-sandbox-policy' }, + { local: '.agents/skills/openshell-cli', remote: '.agents/skills/openshell-cli' }, + ], + }); + + expect(exec.exec).toHaveBeenCalledWith(OPENSHELL_CLI_PATH, [ + 'sandbox', + 'create', + '--upload', + '.agents/skills/generate-sandbox-policy:.agents/skills/generate-sandbox-policy', + '--upload', + '.agents/skills/openshell-cli:.agents/skills/openshell-cli', + ]); + }); + + test('places --upload flags before -- command separator', async () => { + vi.spyOn(console, 'log').mockImplementation(() => undefined); + vi.mocked(exec.exec).mockResolvedValue(mockExecResult('')); + + await openshellCli.createSandbox({ + uploads: [{ local: '.agents/skills', remote: '.agents/skills' }], + command: ['bash'], + }); + + expect(exec.exec).toHaveBeenCalledWith(OPENSHELL_CLI_PATH, [ + 'sandbox', + 'create', + '--upload', + '.agents/skills:.agents/skills', + '--', + 'bash', + ]); + }); + test('appends command after -- separator', async () => { vi.spyOn(console, 'log').mockImplementation(() => undefined); vi.mocked(exec.exec).mockResolvedValue(mockExecResult('')); diff --git a/packages/main/src/plugin/openshell-cli/openshell-cli.ts b/packages/main/src/plugin/openshell-cli/openshell-cli.ts index 2e10298083..f9469a8d89 100644 --- a/packages/main/src/plugin/openshell-cli/openshell-cli.ts +++ b/packages/main/src/plugin/openshell-cli/openshell-cli.ts @@ -139,6 +139,11 @@ export class OpenshellCli { args.push('--label', `${key}=${value}`); } } + if (options.uploads) { + for (const upload of options.uploads) { + args.push('--upload', `${upload.local}:${upload.remote}`); + } + } if (options.command?.length) { args.push('--', ...options.command); }