@fitlab-ai/agent-infra 0.5.10 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/README.zh-CN.md +13 -13
- package/bin/{cli.js → cli.ts} +26 -18
- package/dist/bin/cli.js +121 -0
- package/dist/lib/defaults.json +62 -0
- package/dist/lib/init.js +238 -0
- package/dist/lib/log.js +18 -0
- package/dist/lib/merge.js +747 -0
- package/dist/lib/paths.js +18 -0
- package/dist/lib/prompt.js +85 -0
- package/dist/lib/render.js +139 -0
- package/dist/lib/sandbox/commands/create.js +1173 -0
- package/dist/lib/sandbox/commands/enter.js +98 -0
- package/dist/lib/sandbox/commands/ls.js +93 -0
- package/dist/lib/sandbox/commands/rebuild.js +101 -0
- package/dist/lib/sandbox/commands/refresh.js +85 -0
- package/dist/lib/sandbox/commands/rm.js +226 -0
- package/dist/lib/sandbox/commands/vm.js +144 -0
- package/dist/lib/sandbox/config.js +85 -0
- package/dist/lib/sandbox/constants.js +104 -0
- package/dist/lib/sandbox/credentials.js +437 -0
- package/dist/lib/sandbox/dockerfile.js +76 -0
- package/dist/lib/sandbox/dotfiles.js +170 -0
- package/dist/lib/sandbox/engine.js +155 -0
- package/dist/lib/sandbox/engines/colima.js +64 -0
- package/dist/lib/sandbox/engines/docker-desktop.js +27 -0
- package/dist/lib/sandbox/engines/index.js +25 -0
- package/dist/lib/sandbox/engines/native.js +96 -0
- package/dist/lib/sandbox/engines/orbstack.js +63 -0
- package/dist/lib/sandbox/engines/selinux.js +48 -0
- package/dist/lib/sandbox/engines/wsl2-paths.js +47 -0
- package/dist/lib/sandbox/engines/wsl2.js +57 -0
- package/dist/lib/sandbox/index.js +70 -0
- package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +39 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +178 -0
- package/dist/lib/sandbox/runtimes/java17.dockerfile +3 -0
- package/dist/lib/sandbox/runtimes/java21.dockerfile +3 -0
- package/dist/lib/sandbox/runtimes/node20.dockerfile +3 -0
- package/dist/lib/sandbox/runtimes/node22.dockerfile +3 -0
- package/dist/lib/sandbox/runtimes/python3.dockerfile +3 -0
- package/dist/lib/sandbox/shell.js +148 -0
- package/dist/lib/sandbox/task-resolver.js +35 -0
- package/dist/lib/sandbox/tools.js +115 -0
- package/dist/lib/update.js +186 -0
- package/dist/lib/version.js +5 -0
- package/dist/package.json +5 -0
- package/lib/defaults.json +4 -3
- package/lib/{init.js → init.ts} +48 -18
- package/lib/{log.js → log.ts} +4 -4
- package/lib/{merge.js → merge.ts} +129 -63
- package/lib/paths.ts +18 -0
- package/lib/{prompt.js → prompt.ts} +12 -12
- package/lib/{render.js → render.ts} +30 -17
- package/lib/sandbox/commands/{create.js → create.ts} +224 -118
- package/lib/sandbox/commands/{enter.js → enter.ts} +17 -14
- package/lib/sandbox/commands/{ls.js → ls.ts} +10 -10
- package/lib/sandbox/commands/{rebuild.js → rebuild.ts} +38 -21
- package/lib/sandbox/commands/{refresh.js → refresh.ts} +16 -7
- package/lib/sandbox/commands/{rm.js → rm.ts} +15 -13
- package/lib/sandbox/commands/{vm.js → vm.ts} +14 -11
- package/lib/sandbox/{config.js → config.ts} +56 -11
- package/lib/sandbox/{constants.js → constants.ts} +30 -18
- package/lib/sandbox/{credentials.js → credentials.ts} +160 -46
- package/lib/sandbox/{dockerfile.js → dockerfile.ts} +13 -6
- package/lib/sandbox/{dotfiles.js → dotfiles.ts} +66 -19
- package/lib/sandbox/{engine.js → engine.ts} +57 -25
- package/lib/sandbox/engines/{colima.js → colima.ts} +9 -7
- package/lib/sandbox/engines/{docker-desktop.js → docker-desktop.ts} +5 -3
- package/lib/sandbox/engines/index.ts +74 -0
- package/lib/sandbox/engines/{native.js → native.ts} +25 -6
- package/lib/sandbox/engines/{orbstack.js → orbstack.ts} +7 -5
- package/lib/sandbox/engines/{selinux.js → selinux.ts} +11 -5
- package/lib/sandbox/engines/{wsl2-paths.js → wsl2-paths.ts} +15 -9
- package/lib/sandbox/engines/{wsl2.js → wsl2.ts} +9 -7
- package/lib/sandbox/{index.js → index.ts} +8 -8
- package/lib/sandbox/{shell.js → shell.ts} +30 -17
- package/lib/sandbox/{task-resolver.js → task-resolver.ts} +6 -6
- package/lib/sandbox/{tools.js → tools.ts} +30 -26
- package/lib/{update.js → update.ts} +33 -10
- package/package.json +17 -9
- package/templates/.agents/README.en.md +8 -8
- package/templates/.agents/README.zh-CN.md +8 -8
- package/templates/{.claude → .agents}/hooks/check-version-format.sh +3 -3
- package/templates/.agents/rules/create-issue.github.en.md +6 -0
- package/templates/.agents/rules/create-issue.github.zh-CN.md +6 -0
- package/templates/.agents/rules/issue-fields.github.en.md +155 -0
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +155 -0
- package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -0
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -0
- package/templates/.agents/rules/issue-sync.github.en.md +2 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -1
- package/templates/.agents/rules/task-management.en.md +17 -9
- package/templates/.agents/rules/task-management.zh-CN.md +17 -9
- package/templates/.agents/rules/testing-discipline.en.md +40 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +40 -0
- package/templates/.agents/rules/version-stamp.en.md +29 -0
- package/templates/.agents/rules/version-stamp.zh-CN.md +29 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +143 -6
- package/templates/.agents/scripts/validate-artifact.js +32 -5
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/analyze-task/config/verify.json +2 -0
- package/templates/.agents/skills/block-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/block-task/config/verify.json +1 -0
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +10 -0
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +10 -0
- package/templates/.agents/skills/commit/config/verify.json +1 -0
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +5 -0
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +5 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-task/config/verify.json +2 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +9 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -0
- package/templates/.agents/skills/create-task/config/verify.json +1 -0
- package/templates/.agents/skills/implement-task/SKILL.en.md +16 -1
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/implement-task/config/verify.json +2 -0
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -0
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +10 -0
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -0
- package/templates/.agents/skills/import-issue/config/verify.json +1 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/plan-task/config/verify.json +2 -0
- package/templates/.agents/skills/refine-task/SKILL.en.md +15 -1
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +15 -1
- package/templates/.agents/skills/refine-task/config/verify.json +2 -0
- package/templates/.agents/skills/refine-task/reference/fix-workflow.en.md +9 -0
- package/templates/.agents/skills/refine-task/reference/fix-workflow.zh-CN.md +9 -0
- package/templates/.agents/skills/refine-task/reference/report-template.en.md +11 -0
- package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +11 -0
- package/templates/.agents/skills/restore-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/restore-task/config/verify.json +1 -0
- package/templates/.agents/skills/review-task/SKILL.en.md +16 -1
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/review-task/config/verify.json +3 -0
- package/templates/.agents/skills/review-task/reference/output-templates.en.md +20 -5
- package/templates/.agents/skills/review-task/reference/output-templates.zh-CN.md +20 -5
- package/templates/.agents/skills/review-task/reference/report-template.en.md +13 -0
- package/templates/.agents/skills/review-task/reference/report-template.zh-CN.md +13 -0
- package/templates/.agents/skills/review-task/reference/review-criteria.en.md +18 -0
- package/templates/.agents/skills/review-task/reference/review-criteria.zh-CN.md +18 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -3
- package/templates/.agents/templates/task.en.md +5 -0
- package/templates/.agents/templates/task.zh-CN.md +5 -0
- package/templates/.claude/settings.json +1 -1
- package/templates/.codex/hooks.json +17 -0
- package/lib/paths.js +0 -9
- package/lib/sandbox/engines/index.js +0 -27
- /package/lib/{version.js → version.ts} +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { platform } from 'node:os';
|
|
2
|
+
import { detectHostResources } from "./constants.js";
|
|
3
|
+
import { ADAPTERS, enginesForPlatform, getAdapter } from "./engines/index.js";
|
|
4
|
+
import { run, runOk, runSafe, runVerbose } from "./shell.js";
|
|
5
|
+
export const ENGINES = Object.freeze({
|
|
6
|
+
COLIMA: 'colima',
|
|
7
|
+
ORBSTACK: 'orbstack',
|
|
8
|
+
DOCKER_DESKTOP: 'docker-desktop',
|
|
9
|
+
NATIVE: 'native',
|
|
10
|
+
WSL2: 'wsl2'
|
|
11
|
+
});
|
|
12
|
+
const PLATFORM_DEFAULTS = Object.freeze({
|
|
13
|
+
linux: ENGINES.NATIVE,
|
|
14
|
+
darwin: ENGINES.COLIMA,
|
|
15
|
+
win32: ENGINES.WSL2
|
|
16
|
+
});
|
|
17
|
+
function runFns({ runFn = run, runOkFn = runOk, runSafeFn = runSafe, runVerboseFn = runVerbose } = {}) {
|
|
18
|
+
return {
|
|
19
|
+
run: runFn,
|
|
20
|
+
runOk: runOkFn,
|
|
21
|
+
runSafe: runSafeFn,
|
|
22
|
+
runVerbose: runVerboseFn
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function applyDockerContext(adapter) {
|
|
26
|
+
if (adapter.dockerContext) {
|
|
27
|
+
process.env.DOCKER_CONTEXT = adapter.dockerContext;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function validateSandboxEngine(engine, { platformFn = platform } = {}) {
|
|
31
|
+
if (engine === null || engine === undefined) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const os = platformFn();
|
|
35
|
+
if (!(engine in ADAPTERS)) {
|
|
36
|
+
const known = Object.keys(ADAPTERS).join(', ');
|
|
37
|
+
throw new Error(`sandbox: invalid "sandbox.engine" value "${engine}" (unknown sandbox engine). `
|
|
38
|
+
+ `Valid engines: ${known}.`);
|
|
39
|
+
}
|
|
40
|
+
const adapter = ADAPTERS[engine];
|
|
41
|
+
if (!adapter.supportedPlatforms.includes(os)) {
|
|
42
|
+
const supported = enginesForPlatform(os);
|
|
43
|
+
const supportedList = supported.length > 0 ? supported.join(', ') : 'none';
|
|
44
|
+
throw new Error(`sandbox: "sandbox.engine" value "${engine}" is not supported on ${os}. `
|
|
45
|
+
+ `Supported engines on ${os}: ${supportedList}.`);
|
|
46
|
+
}
|
|
47
|
+
return engine;
|
|
48
|
+
}
|
|
49
|
+
export function detectEngine(config = {}, { platformFn = platform } = {}) {
|
|
50
|
+
const configured = validateSandboxEngine(config.engine, { platformFn });
|
|
51
|
+
if (configured) {
|
|
52
|
+
return configured;
|
|
53
|
+
}
|
|
54
|
+
const os = platformFn();
|
|
55
|
+
const fallback = PLATFORM_DEFAULTS[os];
|
|
56
|
+
if (fallback) {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`sandbox: platform "${os}" is not supported. `
|
|
60
|
+
+ 'Supported platforms: linux (native), darwin (colima/orbstack/docker-desktop), win32 (wsl2). '
|
|
61
|
+
+ 'Please open an issue at https://github.com/fitlab-ai/agent-infra/issues/new '
|
|
62
|
+
+ 'with your platform details if you need this added.');
|
|
63
|
+
}
|
|
64
|
+
export function hasUserVmConfig(vm = {}) {
|
|
65
|
+
return vm.cpu != null || vm.memory != null || vm.disk != null;
|
|
66
|
+
}
|
|
67
|
+
export function resolveEffectiveVm(adapter, userVm = {}, { detectHostResourcesFn = detectHostResources } = {}) {
|
|
68
|
+
let host = null;
|
|
69
|
+
const getHost = () => {
|
|
70
|
+
host ??= detectHostResourcesFn();
|
|
71
|
+
return host;
|
|
72
|
+
};
|
|
73
|
+
const defaults = adapter.defaultResources?.(getHost) ?? {};
|
|
74
|
+
return {
|
|
75
|
+
cpu: userVm.cpu ?? defaults.cpu ?? null,
|
|
76
|
+
memory: userVm.memory ?? defaults.memory ?? null,
|
|
77
|
+
disk: userVm.disk ?? defaults.disk ?? null
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function effectiveConfigFor(adapter, config) {
|
|
81
|
+
const userVm = config.vm ?? {};
|
|
82
|
+
return {
|
|
83
|
+
...config,
|
|
84
|
+
userVm,
|
|
85
|
+
hasUserVmConfig,
|
|
86
|
+
vm: resolveEffectiveVm(adapter, userVm)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export async function ensureDocker(config, onMessage, dependencies = {}) {
|
|
90
|
+
const engine = detectEngine(config, dependencies);
|
|
91
|
+
const adapter = getAdapter(engine);
|
|
92
|
+
const effectiveConfig = effectiveConfigFor(adapter, config);
|
|
93
|
+
applyDockerContext(adapter);
|
|
94
|
+
const vmJustStarted = await adapter.ensure(effectiveConfig, onMessage, runFns(dependencies));
|
|
95
|
+
adapter.syncResources(effectiveConfig, onMessage, runFns(dependencies), { vmJustStarted });
|
|
96
|
+
}
|
|
97
|
+
export function isVmManaged(config = {}, dependencies = {}) {
|
|
98
|
+
try {
|
|
99
|
+
const engine = detectEngine(config, dependencies);
|
|
100
|
+
return isManagedEngine(engine);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const message = error instanceof Error ? error.message : '';
|
|
104
|
+
if (message.startsWith('sandbox: platform "')
|
|
105
|
+
|| / is not supported on [^.]+\. Supported engines on [^:]+: none\./.test(message)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function isManagedEngine(engine) {
|
|
112
|
+
try {
|
|
113
|
+
return getAdapter(engine).managed;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function engineDisplayName(engine) {
|
|
120
|
+
try {
|
|
121
|
+
return getAdapter(engine).displayName;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return engine;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function startManagedVm(config, { platformFn = platform, runOkFn = runOk, runSafeFn = runSafe, runVerboseFn = runVerbose, onMessage } = {}) {
|
|
128
|
+
const engine = detectEngine(config, { platformFn });
|
|
129
|
+
const adapter = getAdapter(engine);
|
|
130
|
+
if (!adapter.managed) {
|
|
131
|
+
throw new Error(`VM management is unavailable for engine '${adapter.displayName}'.`);
|
|
132
|
+
}
|
|
133
|
+
const effectiveConfig = effectiveConfigFor(adapter, config);
|
|
134
|
+
applyDockerContext(adapter);
|
|
135
|
+
if (!adapter.startVm) {
|
|
136
|
+
throw new Error(`VM management is unavailable for engine '${adapter.displayName}'.`);
|
|
137
|
+
}
|
|
138
|
+
const result = adapter.startVm(effectiveConfig, onMessage, runFns({ runOkFn, runSafeFn, runVerboseFn }));
|
|
139
|
+
adapter.syncResources(effectiveConfig, onMessage, runFns({ runOkFn, runSafeFn, runVerboseFn }), { vmJustStarted: result === 'started' });
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
export function stopManagedVm(config, { platformFn = platform, runFn = run } = {}) {
|
|
143
|
+
const engine = detectEngine(config, { platformFn });
|
|
144
|
+
const adapter = getAdapter(engine);
|
|
145
|
+
if (!adapter.managed) {
|
|
146
|
+
throw new Error(`VM management is unavailable for engine '${adapter.displayName}'.`);
|
|
147
|
+
}
|
|
148
|
+
if (!adapter.stopVm) {
|
|
149
|
+
throw new Error(`VM management is unavailable for engine '${adapter.displayName}'.`);
|
|
150
|
+
}
|
|
151
|
+
// Stop commands do not read Docker context or VM resource values; keep the
|
|
152
|
+
// previous environment unchanged and pass the original config intentionally.
|
|
153
|
+
return adapter.stopVm(config, null, runFns({ runFn }));
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
function colimaArgs(config, runSafeFn) {
|
|
2
|
+
const arch = runSafeFn('uname', ['-m']);
|
|
3
|
+
const vm = config.vm ?? {};
|
|
4
|
+
const args = ['start', '--cpu', String(vm.cpu), '--memory', String(vm.memory), '--disk', String(vm.disk)];
|
|
5
|
+
if (arch === 'arm64') {
|
|
6
|
+
args.push('--arch', 'aarch64', '--vm-type=vz', '--mount-type=virtiofs');
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
args.push('--arch', 'x86_64');
|
|
10
|
+
}
|
|
11
|
+
return args;
|
|
12
|
+
}
|
|
13
|
+
export const colimaAdapter = {
|
|
14
|
+
id: 'colima',
|
|
15
|
+
displayName: 'Colima',
|
|
16
|
+
supportedPlatforms: ['darwin'],
|
|
17
|
+
dockerContext: 'colima',
|
|
18
|
+
managed: true,
|
|
19
|
+
canApplyResources: 'on-start',
|
|
20
|
+
defaultResources(getHost) {
|
|
21
|
+
const host = getHost();
|
|
22
|
+
return {
|
|
23
|
+
cpu: host.cpu,
|
|
24
|
+
memory: host.memory,
|
|
25
|
+
disk: 60
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
async ensure(config, onMessage, { runOk, runSafe, runVerbose }) {
|
|
29
|
+
let started = false;
|
|
30
|
+
if (!runOk('which', ['colima'])) {
|
|
31
|
+
onMessage?.('Installing colima + docker via Homebrew...');
|
|
32
|
+
runVerbose('brew', ['install', 'colima', 'docker']);
|
|
33
|
+
}
|
|
34
|
+
if (!runOk('colima', ['status'])) {
|
|
35
|
+
onMessage?.('Starting Colima VM...');
|
|
36
|
+
runVerbose('colima', colimaArgs(config, runSafe));
|
|
37
|
+
started = true;
|
|
38
|
+
}
|
|
39
|
+
if (!runOk('docker', ['info'])) {
|
|
40
|
+
throw new Error('Docker daemon is not available after starting Colima');
|
|
41
|
+
}
|
|
42
|
+
return started;
|
|
43
|
+
},
|
|
44
|
+
startVm(config, _onMessage, { runOk, runSafe, runVerbose }) {
|
|
45
|
+
if (runOk('colima', ['status'])) {
|
|
46
|
+
return 'already-running';
|
|
47
|
+
}
|
|
48
|
+
runVerbose('colima', colimaArgs(config, runSafe));
|
|
49
|
+
return 'started';
|
|
50
|
+
},
|
|
51
|
+
stopVm(_config, _onMessage, { run }) {
|
|
52
|
+
run('colima', ['stop']);
|
|
53
|
+
return 'stopped';
|
|
54
|
+
},
|
|
55
|
+
syncResources(config, onMessage, _runFns, { vmJustStarted } = {}) {
|
|
56
|
+
if (vmJustStarted || !config.hasUserVmConfig?.(config.userVm)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
onMessage?.('Warning: Colima VM is already running; restart with '
|
|
60
|
+
+ '`ai sandbox vm stop && ai sandbox vm start` to apply new sandbox.vm.* values.');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
export default colimaAdapter;
|
|
64
|
+
//# sourceMappingURL=colima.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const dockerDesktopAdapter = {
|
|
2
|
+
id: 'docker-desktop',
|
|
3
|
+
displayName: 'Docker Desktop',
|
|
4
|
+
supportedPlatforms: ['darwin', 'linux', 'win32'],
|
|
5
|
+
dockerContext: 'desktop-linux',
|
|
6
|
+
managed: false,
|
|
7
|
+
canApplyResources: 'never',
|
|
8
|
+
defaultResources() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
async ensure(_config, _onMessage, { runOk }) {
|
|
12
|
+
if (!runOk('docker', ['info'])) {
|
|
13
|
+
throw new Error('Docker Desktop is not running. Please start Docker Desktop manually.');
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
},
|
|
17
|
+
syncResources(config, onMessage) {
|
|
18
|
+
if (!config.hasUserVmConfig?.(config.userVm)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
onMessage?.('Warning: Docker Desktop manages CPU/memory/disk via Settings -> Resources. '
|
|
22
|
+
+ 'sandbox.vm.* values and --cpu/--memory flags are not applied for this engine. '
|
|
23
|
+
+ 'Please configure resources in Docker Desktop GUI to match.');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export default dockerDesktopAdapter;
|
|
27
|
+
//# sourceMappingURL=docker-desktop.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { colimaAdapter } from "./colima.js";
|
|
2
|
+
import { dockerDesktopAdapter } from "./docker-desktop.js";
|
|
3
|
+
import { nativeAdapter } from "./native.js";
|
|
4
|
+
import { orbstackAdapter } from "./orbstack.js";
|
|
5
|
+
import { wsl2Adapter } from "./wsl2.js";
|
|
6
|
+
export const ADAPTERS = Object.freeze({
|
|
7
|
+
colima: colimaAdapter,
|
|
8
|
+
orbstack: orbstackAdapter,
|
|
9
|
+
'docker-desktop': dockerDesktopAdapter,
|
|
10
|
+
native: nativeAdapter,
|
|
11
|
+
wsl2: wsl2Adapter
|
|
12
|
+
});
|
|
13
|
+
export function getAdapter(engineId) {
|
|
14
|
+
const adapter = ADAPTERS[engineId];
|
|
15
|
+
if (!adapter) {
|
|
16
|
+
throw new Error(`No adapter registered for engine '${engineId}'`);
|
|
17
|
+
}
|
|
18
|
+
return adapter;
|
|
19
|
+
}
|
|
20
|
+
export function enginesForPlatform(platformName) {
|
|
21
|
+
return Object.values(ADAPTERS)
|
|
22
|
+
.filter((adapter) => adapter.supportedPlatforms.includes(platformName))
|
|
23
|
+
.map((adapter) => adapter.id);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export function isRootlessDocker({ env = process.env, runSafe } = {}) {
|
|
2
|
+
const dockerHost = env.DOCKER_HOST ?? '';
|
|
3
|
+
if (dockerHost.startsWith('unix:///run/user/')) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
if (!runSafe) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const securityOptions = runSafe('docker', ['info', '--format', '{{.SecurityOptions}}']);
|
|
11
|
+
return securityOptions.includes('rootless');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function resolveBuildUid({ engine, runFn, runSafeFn, env = process.env }) {
|
|
18
|
+
const runSafe = runSafeFn
|
|
19
|
+
? (cmd, args) => runSafeFn(engine, cmd, args)
|
|
20
|
+
: undefined;
|
|
21
|
+
if (engine === 'native' && isRootlessDocker({ env, runSafe })) {
|
|
22
|
+
return { uid: '0', gid: '0' };
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
uid: runFn(engine, 'id', ['-u']),
|
|
26
|
+
gid: runFn(engine, 'id', ['-g'])
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export const nativeAdapter = {
|
|
30
|
+
id: 'native',
|
|
31
|
+
displayName: 'native Docker',
|
|
32
|
+
supportedPlatforms: ['linux', 'win32'],
|
|
33
|
+
dockerContext: null,
|
|
34
|
+
managed: false,
|
|
35
|
+
canApplyResources: 'never',
|
|
36
|
+
defaultResources() {
|
|
37
|
+
return null;
|
|
38
|
+
},
|
|
39
|
+
async ensure(_config, _onMessage, { runOk, runSafe }) {
|
|
40
|
+
if (!runOk('which', ['docker'])) {
|
|
41
|
+
throw new Error([
|
|
42
|
+
'Docker is not installed.',
|
|
43
|
+
'Install Docker Engine for your distribution: https://docs.docker.com/engine/install/',
|
|
44
|
+
'Then start the daemon with: sudo systemctl enable --now docker',
|
|
45
|
+
'If you want to run Docker without sudo, add your user to the docker group: sudo usermod -aG docker $USER'
|
|
46
|
+
].join('\n'));
|
|
47
|
+
}
|
|
48
|
+
if (runOk('docker', ['info'])) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const serverVersion = runSafe('docker', ['version', '--format', '{{.Server.Version}}']);
|
|
52
|
+
const rootless = isRootlessDocker({ runSafe });
|
|
53
|
+
if (!serverVersion) {
|
|
54
|
+
if (rootless) {
|
|
55
|
+
throw new Error([
|
|
56
|
+
'Docker rootless daemon is not running or is unreachable.',
|
|
57
|
+
'Start it with: systemctl --user start docker',
|
|
58
|
+
'Enable it on login with: systemctl --user enable docker',
|
|
59
|
+
'Verify DOCKER_HOST points at $XDG_RUNTIME_DIR/docker.sock.',
|
|
60
|
+
'Then retry: ai sandbox create <branch>'
|
|
61
|
+
].join('\n'));
|
|
62
|
+
}
|
|
63
|
+
throw new Error([
|
|
64
|
+
'Docker daemon is not running or is unreachable.',
|
|
65
|
+
'Start it with: sudo systemctl start docker',
|
|
66
|
+
'Enable it on boot with: sudo systemctl enable docker',
|
|
67
|
+
'If you use rootless or remote Docker, verify DOCKER_HOST points at a reachable socket.',
|
|
68
|
+
'For rootless Docker, export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock and run: systemctl --user start docker',
|
|
69
|
+
'Then retry: ai sandbox create <branch>'
|
|
70
|
+
].join('\n'));
|
|
71
|
+
}
|
|
72
|
+
if (rootless) {
|
|
73
|
+
throw new Error([
|
|
74
|
+
'docker info failed even though the rootless daemon responded to version.',
|
|
75
|
+
'This usually means DOCKER_HOST or XDG_RUNTIME_DIR is misconfigured.',
|
|
76
|
+
'Verify DOCKER_HOST matches $XDG_RUNTIME_DIR/docker.sock.',
|
|
77
|
+
'Check the daemon with: systemctl --user status docker',
|
|
78
|
+
'Then retry: ai sandbox create <branch>'
|
|
79
|
+
].join('\n'));
|
|
80
|
+
}
|
|
81
|
+
throw new Error([
|
|
82
|
+
'Docker is installed, but the current user may lack permission to use the daemon.',
|
|
83
|
+
'Add your user to the docker group: sudo usermod -aG docker $USER',
|
|
84
|
+
'Open a new login shell or run: newgrp docker'
|
|
85
|
+
].join('\n'));
|
|
86
|
+
},
|
|
87
|
+
syncResources(config, onMessage) {
|
|
88
|
+
if (!config.hasUserVmConfig?.(config.userVm)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
onMessage?.('Warning: Linux native Docker has no managed VM; sandbox.vm.* is not applicable. '
|
|
92
|
+
+ 'Use docker run --cpus / --memory per container or host cgroups.');
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
export default nativeAdapter;
|
|
96
|
+
//# sourceMappingURL=native.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const orbstackAdapter = {
|
|
2
|
+
id: 'orbstack',
|
|
3
|
+
displayName: 'OrbStack',
|
|
4
|
+
supportedPlatforms: ['darwin'],
|
|
5
|
+
dockerContext: 'orbstack',
|
|
6
|
+
managed: true,
|
|
7
|
+
canApplyResources: 'hot',
|
|
8
|
+
defaultResources() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
async ensure(_config, onMessage, { runOk, runVerbose }) {
|
|
12
|
+
let started = false;
|
|
13
|
+
if (!runOk('which', ['orb'])) {
|
|
14
|
+
onMessage?.('Installing OrbStack via Homebrew...');
|
|
15
|
+
runVerbose('brew', ['install', '--cask', 'orbstack']);
|
|
16
|
+
}
|
|
17
|
+
if (!runOk('docker', ['info'])) {
|
|
18
|
+
onMessage?.('Starting OrbStack...');
|
|
19
|
+
runVerbose('orb', ['start']);
|
|
20
|
+
started = true;
|
|
21
|
+
}
|
|
22
|
+
if (!runOk('docker', ['info'])) {
|
|
23
|
+
throw new Error('Docker daemon is not available after starting OrbStack');
|
|
24
|
+
}
|
|
25
|
+
return started;
|
|
26
|
+
},
|
|
27
|
+
startVm(_config, _onMessage, { runOk, runVerbose }) {
|
|
28
|
+
if (runOk('orb', ['status'])) {
|
|
29
|
+
return 'already-running';
|
|
30
|
+
}
|
|
31
|
+
runVerbose('orb', ['start']);
|
|
32
|
+
return 'started';
|
|
33
|
+
},
|
|
34
|
+
stopVm(_config, _onMessage, { run }) {
|
|
35
|
+
run('orb', ['stop']);
|
|
36
|
+
return 'stopped';
|
|
37
|
+
},
|
|
38
|
+
syncResources(config, onMessage, { runVerbose }) {
|
|
39
|
+
const vm = config?.vm ?? {};
|
|
40
|
+
if (vm.cpu != null) {
|
|
41
|
+
try {
|
|
42
|
+
runVerbose('orb', ['config', 'set', 'cpu', String(vm.cpu)]);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
onMessage?.(`Warning: failed to apply OrbStack cpu=${vm.cpu}; resource limit may not take effect.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (vm.memory != null) {
|
|
49
|
+
try {
|
|
50
|
+
runVerbose('orb', ['config', 'set', 'memory_mib', String(vm.memory * 1024)]);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
onMessage?.(`Warning: failed to apply OrbStack memory=${vm.memory}GiB; resource limit may not take effect.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (vm.disk != null) {
|
|
57
|
+
onMessage?.(`Warning: OrbStack does not expose a fixed disk size; sandbox.vm.disk=${vm.disk} is ignored. `
|
|
58
|
+
+ 'Manage storage via OrbStack settings GUI.');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
export default orbstackAdapter;
|
|
63
|
+
//# sourceMappingURL=orbstack.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
const SELINUX_ENFORCE_PATH = '/sys/fs/selinux/enforce';
|
|
3
|
+
const detectionCache = new WeakMap();
|
|
4
|
+
const VALID_DISABLE_VALUES = new Set([undefined, '', '0', '1']);
|
|
5
|
+
function isDisabled(env) {
|
|
6
|
+
return env?.AGENT_INFRA_SELINUX_DISABLE === '1';
|
|
7
|
+
}
|
|
8
|
+
function readEnforceFlag(fsImpl) {
|
|
9
|
+
try {
|
|
10
|
+
return fsImpl.readFileSync(SELINUX_ENFORCE_PATH, 'utf8').trim();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function isSelinuxEnforcing(fsImpl, platform) {
|
|
17
|
+
let cache = detectionCache.get(fsImpl);
|
|
18
|
+
if (!cache) {
|
|
19
|
+
cache = new Map();
|
|
20
|
+
detectionCache.set(fsImpl, cache);
|
|
21
|
+
}
|
|
22
|
+
if (cache.has(platform)) {
|
|
23
|
+
return cache.get(platform);
|
|
24
|
+
}
|
|
25
|
+
const enforcing = readEnforceFlag(fsImpl) === '1';
|
|
26
|
+
cache.set(platform, enforcing);
|
|
27
|
+
return enforcing;
|
|
28
|
+
}
|
|
29
|
+
export function selinuxLabelForMount(engine, options = {}) {
|
|
30
|
+
const { fs: fsImpl = fs, platform = process.platform, env = process.env } = options;
|
|
31
|
+
if (engine !== 'native' || platform !== 'linux') {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (isDisabled(env)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (!isSelinuxEnforcing(fsImpl, platform)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return 'z';
|
|
41
|
+
}
|
|
42
|
+
export function validateSelinuxDisableEnv(env = process.env) {
|
|
43
|
+
const value = env?.AGENT_INFRA_SELINUX_DISABLE;
|
|
44
|
+
if (!VALID_DISABLE_VALUES.has(value)) {
|
|
45
|
+
throw new Error('Invalid AGENT_INFRA_SELINUX_DISABLE value. Expected 1 to disable, or unset/0 for default.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=selinux.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { selinuxLabelForMount } from "./selinux.js";
|
|
3
|
+
const WINDOWS_DRIVE_PATH_PATTERN = /^([A-Za-z]):[\\/](.*)$/;
|
|
4
|
+
const UNC_PATH_PATTERN = /^(?:\\\\|\/\/)[^\\/]+[\\/][^\\/]+/;
|
|
5
|
+
export function hostJoin(basePath, ...segments) {
|
|
6
|
+
return basePath.startsWith('/') ? path.posix.join(basePath, ...segments) : path.join(basePath, ...segments);
|
|
7
|
+
}
|
|
8
|
+
export function isWindowsDrivePath(value) {
|
|
9
|
+
return typeof value === 'string' && WINDOWS_DRIVE_PATH_PATTERN.test(value);
|
|
10
|
+
}
|
|
11
|
+
export function isUncPath(value) {
|
|
12
|
+
return typeof value === 'string' && UNC_PATH_PATTERN.test(value);
|
|
13
|
+
}
|
|
14
|
+
export function windowsPathToWslPath(value) {
|
|
15
|
+
if (value.length === 0) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (isUncPath(value)) {
|
|
19
|
+
throw new Error(`UNC paths are not supported for WSL2 sandbox mounts: ${value}`);
|
|
20
|
+
}
|
|
21
|
+
const match = value.match(WINDOWS_DRIVE_PATH_PATTERN);
|
|
22
|
+
if (!match) {
|
|
23
|
+
return value.replace(/\\/g, '/');
|
|
24
|
+
}
|
|
25
|
+
const [, drive = '', rest = ''] = match;
|
|
26
|
+
const normalizedRest = rest.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
27
|
+
return `/mnt/${drive.toLowerCase()}/${normalizedRest}`;
|
|
28
|
+
}
|
|
29
|
+
export function toEnginePath(engine, value) {
|
|
30
|
+
if (engine !== 'wsl2') {
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
return windowsPathToWslPath(value);
|
|
34
|
+
}
|
|
35
|
+
export function volumeArg(engine, hostPath, containerPath, suffix = '', options = {}) {
|
|
36
|
+
const { selinux = 'shared', ...selinuxOptions } = options;
|
|
37
|
+
const flags = suffix.replace(/^:/, '').split(',').filter(Boolean);
|
|
38
|
+
if (selinux !== 'none') {
|
|
39
|
+
const label = selinuxLabelForMount(engine, selinuxOptions);
|
|
40
|
+
if (label && !flags.includes(label)) {
|
|
41
|
+
flags.push(label);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const composedSuffix = flags.length > 0 ? `:${flags.join(',')}` : '';
|
|
45
|
+
return `${toEnginePath(engine, hostPath)}:${containerPath}${composedSuffix}`;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=wsl2-paths.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function ensureWslAvailable(runOk) {
|
|
2
|
+
if (runOk('wsl.exe', ['--status']) || runOk('wsl.exe', ['--', 'true'])) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
throw new Error([
|
|
6
|
+
'WSL2 is required for Windows sandbox support.',
|
|
7
|
+
'Install WSL2, configure a default Linux distribution, and re-run "ai sandbox create".'
|
|
8
|
+
].join('\n'));
|
|
9
|
+
}
|
|
10
|
+
function ensureDockerAvailable(runOk) {
|
|
11
|
+
if (runOk('wsl.exe', ['--', 'docker', 'info'])) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
throw new Error([
|
|
15
|
+
'Docker is not available inside WSL2.',
|
|
16
|
+
'Start Docker Desktop and enable WSL integration for your default distribution.'
|
|
17
|
+
].join('\n'));
|
|
18
|
+
}
|
|
19
|
+
function wsl2BackendCheck(runOk, onMessage) {
|
|
20
|
+
ensureWslAvailable(runOk);
|
|
21
|
+
onMessage?.('Checking Docker Desktop from WSL2...');
|
|
22
|
+
ensureDockerAvailable(runOk);
|
|
23
|
+
}
|
|
24
|
+
export const wsl2Adapter = {
|
|
25
|
+
id: 'wsl2',
|
|
26
|
+
displayName: 'WSL2',
|
|
27
|
+
supportedPlatforms: ['win32'],
|
|
28
|
+
dockerContext: null,
|
|
29
|
+
managed: true,
|
|
30
|
+
canApplyResources: 'never',
|
|
31
|
+
defaultResources() {
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
async ensure(config, onMessage, { runOk }) {
|
|
35
|
+
wsl2BackendCheck(runOk, onMessage);
|
|
36
|
+
void config;
|
|
37
|
+
return false;
|
|
38
|
+
},
|
|
39
|
+
startVm(config, onMessage, { runOk }) {
|
|
40
|
+
wsl2BackendCheck(runOk, onMessage);
|
|
41
|
+
void config;
|
|
42
|
+
return 'already-running';
|
|
43
|
+
},
|
|
44
|
+
stopVm() {
|
|
45
|
+
throw new Error('Windows uses Docker Desktop with WSL2. Stop it from Docker Desktop or run "wsl --shutdown" manually.');
|
|
46
|
+
},
|
|
47
|
+
syncResources(config, onMessage) {
|
|
48
|
+
if (!config.hasUserVmConfig?.(config.userVm)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
onMessage?.('Warning: Docker Desktop manages CPU/memory/disk via Settings -> Resources. '
|
|
52
|
+
+ 'sandbox.vm.* values and --cpu/--memory flags are not applied for this engine. '
|
|
53
|
+
+ 'Please configure resources in Docker Desktop GUI to match.');
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
export default wsl2Adapter;
|
|
57
|
+
//# sourceMappingURL=wsl2.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const USAGE = `Usage: ai sandbox <command> [options]
|
|
2
|
+
|
|
3
|
+
Commands:
|
|
4
|
+
create <branch> [base] Create a sandbox (VM + image + worktree + container)
|
|
5
|
+
exec <branch> [cmd...] Enter sandbox or run a command
|
|
6
|
+
refresh Sync host Claude Code credentials to all sandbox copies
|
|
7
|
+
ls List sandboxes for the current project
|
|
8
|
+
rm <branch> [--all] Remove a sandbox or all sandboxes
|
|
9
|
+
vm status|start|stop Manage the sandbox VM (macOS) or check the backend (Windows)
|
|
10
|
+
rebuild [--quiet] Rebuild the sandbox image
|
|
11
|
+
|
|
12
|
+
Run 'ai sandbox <command> --help' for details.`;
|
|
13
|
+
export async function runSandbox(args) {
|
|
14
|
+
const [subcommand, ...rest] = args;
|
|
15
|
+
if (!subcommand) {
|
|
16
|
+
process.stdout.write(`${USAGE}\n`);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
|
|
21
|
+
process.stdout.write(`${USAGE}\n`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
switch (subcommand) {
|
|
25
|
+
case 'create': {
|
|
26
|
+
const { create } = await import("./commands/create.js");
|
|
27
|
+
await create(rest);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case 'exec': {
|
|
31
|
+
const { enter } = await import("./commands/enter.js");
|
|
32
|
+
const exitCode = enter(rest);
|
|
33
|
+
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
34
|
+
process.exitCode = exitCode;
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case 'refresh': {
|
|
39
|
+
const { refresh } = await import("./commands/refresh.js");
|
|
40
|
+
const exitCode = await refresh(rest);
|
|
41
|
+
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
42
|
+
process.exitCode = exitCode;
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case 'ls': {
|
|
47
|
+
const { ls } = await import("./commands/ls.js");
|
|
48
|
+
ls(rest);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case 'rm': {
|
|
52
|
+
const { rm } = await import("./commands/rm.js");
|
|
53
|
+
await rm(rest);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'vm': {
|
|
57
|
+
const { vm } = await import("./commands/vm.js");
|
|
58
|
+
await vm(rest);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case 'rebuild': {
|
|
62
|
+
const { rebuild } = await import("./commands/rebuild.js");
|
|
63
|
+
await rebuild(rest);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
throw new Error(`Unknown sandbox command: ${subcommand}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=index.js.map
|