From aa9523656708bd10c43e1d8fa508b6a80a89aef1 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 8 Jun 2026 14:59:13 -0400 Subject: [PATCH] feat(openshell-cli): add --upload flag support to sandbox create Adds upload support to CreateSandboxOptions and createSandbox() for future skill/MCP injection when workspace creation is wired directly through the openshell CLI. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Brian --- packages/api/src/openshell-gateway-info.ts | 1 + .../openshell-cli/openshell-cli.spec.ts | 56 +++++++++++++++++++ .../src/plugin/openshell-cli/openshell-cli.ts | 5 ++ 3 files changed, 62 insertions(+) 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); }