@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,144 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { loadConfig } from "../config.js";
|
|
5
|
+
import { parsePositiveIntegerOption } from "../constants.js";
|
|
6
|
+
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, startManagedVm, stopManagedVm } from "../engine.js";
|
|
7
|
+
import { runOk, runSafe } from "../shell.js";
|
|
8
|
+
const USAGE = `Usage: ai sandbox vm <status|start|stop> [--cpu <n>] [--memory <n>]`;
|
|
9
|
+
export function ensureManagedVm(engine) {
|
|
10
|
+
if (engine === ENGINES.NATIVE) {
|
|
11
|
+
throw new Error("Linux native Docker does not use a managed VM. Use 'ai sandbox create' directly.");
|
|
12
|
+
}
|
|
13
|
+
if (!isManagedEngine(engine)) {
|
|
14
|
+
throw new Error(`VM management is unavailable for engine '${engineDisplayName(engine)}'. `
|
|
15
|
+
+ (engine === ENGINES.DOCKER_DESKTOP
|
|
16
|
+
? 'Docker Desktop is managed via its GUI (Settings -> Resources).'
|
|
17
|
+
: ''));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function wsl2BackendStatus({ runOkFn = runOk } = {}) {
|
|
21
|
+
const wslAvailable = runOkFn('wsl.exe', ['--status']) || runOkFn('wsl.exe', ['--', 'true']);
|
|
22
|
+
const dockerAvailable = wslAvailable && runOkFn('wsl.exe', ['--', 'docker', 'info']);
|
|
23
|
+
return { wslAvailable, dockerAvailable };
|
|
24
|
+
}
|
|
25
|
+
function status() {
|
|
26
|
+
const config = loadConfig();
|
|
27
|
+
const engine = detectEngine(config);
|
|
28
|
+
const name = engineDisplayName(engine);
|
|
29
|
+
ensureManagedVm(engine);
|
|
30
|
+
p.intro(pc.cyan('Sandbox VM status'));
|
|
31
|
+
if (engine === ENGINES.WSL2) {
|
|
32
|
+
const backend = wsl2BackendStatus();
|
|
33
|
+
if (backend.wslAvailable) {
|
|
34
|
+
p.log.info('WSL2 is available');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
p.log.warn('WSL2 is not available. Install WSL2 and configure a default Linux distribution.');
|
|
38
|
+
}
|
|
39
|
+
if (backend.dockerAvailable) {
|
|
40
|
+
p.log.info('Docker Desktop WSL integration is available');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
p.log.warn('Docker is not available inside WSL2. Start Docker Desktop and enable WSL integration.');
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (engine === ENGINES.COLIMA) {
|
|
48
|
+
if (runOk('colima', ['status'])) {
|
|
49
|
+
process.stdout.write(`${runSafe('colima', ['status'])}\n`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
p.log.warn('Colima VM is not running');
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!runOk('orb', ['status'])) {
|
|
57
|
+
p.log.warn(`${name} VM is not running`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
process.stdout.write(`${runSafe('orb', ['status'])}\n`);
|
|
61
|
+
}
|
|
62
|
+
async function start(args) {
|
|
63
|
+
const { values } = parseArgs({
|
|
64
|
+
args,
|
|
65
|
+
allowPositionals: true,
|
|
66
|
+
strict: true,
|
|
67
|
+
options: {
|
|
68
|
+
cpu: { type: 'string' },
|
|
69
|
+
memory: { type: 'string' },
|
|
70
|
+
help: { type: 'boolean', short: 'h' }
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (values.help) {
|
|
74
|
+
process.stdout.write(`${USAGE}\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const config = loadConfig();
|
|
78
|
+
const engine = detectEngine(config);
|
|
79
|
+
ensureManagedVm(engine);
|
|
80
|
+
p.intro(pc.cyan('Starting sandbox VM'));
|
|
81
|
+
if (engine === ENGINES.WSL2) {
|
|
82
|
+
p.log.warn('WSL2 Docker backend is managed by Docker Desktop. '
|
|
83
|
+
+ 'Start it from Docker Desktop GUI, then run "ai sandbox vm status" to check readiness.');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const effectiveConfig = {
|
|
87
|
+
...config,
|
|
88
|
+
vm: {
|
|
89
|
+
...config.vm,
|
|
90
|
+
cpu: parsePositiveIntegerOption(values.cpu, '--cpu') ?? config.vm.cpu,
|
|
91
|
+
memory: parsePositiveIntegerOption(values.memory, '--memory') ?? config.vm.memory
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const onMessage = (detail) => {
|
|
95
|
+
p.log.info(detail);
|
|
96
|
+
};
|
|
97
|
+
startManagedVm(effectiveConfig, { onMessage });
|
|
98
|
+
p.outro(pc.green('VM ready'));
|
|
99
|
+
}
|
|
100
|
+
function stop() {
|
|
101
|
+
const config = loadConfig();
|
|
102
|
+
const engine = detectEngine(config);
|
|
103
|
+
const name = engineDisplayName(engine);
|
|
104
|
+
ensureManagedVm(engine);
|
|
105
|
+
p.intro(pc.cyan('Stopping sandbox VM'));
|
|
106
|
+
if (engine === ENGINES.WSL2) {
|
|
107
|
+
p.log.warn('Windows uses Docker Desktop with WSL2. Stop it from Docker Desktop or run "wsl --shutdown" manually.');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (engine === ENGINES.COLIMA && !runOk('colima', ['status'])) {
|
|
111
|
+
p.log.warn(`${name} VM is not running`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (engine === ENGINES.ORBSTACK && !runOk('orb', ['status'])) {
|
|
115
|
+
p.log.warn(`${name} VM is not running`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
stopManagedVm(config);
|
|
119
|
+
p.outro(pc.green('VM stopped'));
|
|
120
|
+
}
|
|
121
|
+
export async function vm(args) {
|
|
122
|
+
const [subcommand, ...rest] = args;
|
|
123
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
124
|
+
process.stdout.write(`${USAGE}\n`);
|
|
125
|
+
if (!subcommand) {
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
switch (subcommand) {
|
|
131
|
+
case 'status':
|
|
132
|
+
status();
|
|
133
|
+
break;
|
|
134
|
+
case 'start':
|
|
135
|
+
await start(rest);
|
|
136
|
+
break;
|
|
137
|
+
case 'stop':
|
|
138
|
+
stop();
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
throw new Error(USAGE);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=vm.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { homedir, platform } from 'node:os';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { validateSandboxEngine } from "./engine.js";
|
|
6
|
+
import { hostJoin } from "./engines/wsl2-paths.js";
|
|
7
|
+
const DEFAULTS = Object.freeze({
|
|
8
|
+
engine: null,
|
|
9
|
+
runtimes: ['node20'],
|
|
10
|
+
tools: ['claude-code', 'codex', 'gemini-cli', 'opencode'],
|
|
11
|
+
dockerfile: null,
|
|
12
|
+
vm: {
|
|
13
|
+
cpu: null,
|
|
14
|
+
memory: null,
|
|
15
|
+
disk: null
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
function detectRepoRoot() {
|
|
19
|
+
try {
|
|
20
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
23
|
+
}).trim();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new Error('sandbox: current directory is not inside a git repository');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function asPositiveNumberOrNull(value) {
|
|
30
|
+
return typeof value === 'number' ? value : null;
|
|
31
|
+
}
|
|
32
|
+
function cloneDefaults() {
|
|
33
|
+
return {
|
|
34
|
+
engine: DEFAULTS.engine,
|
|
35
|
+
runtimes: [...DEFAULTS.runtimes],
|
|
36
|
+
tools: [...DEFAULTS.tools],
|
|
37
|
+
dockerfile: DEFAULTS.dockerfile,
|
|
38
|
+
vm: { ...DEFAULTS.vm }
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function loadConfig({ platformFn = platform } = {}) {
|
|
42
|
+
const repoRoot = detectRepoRoot();
|
|
43
|
+
const home = homedir();
|
|
44
|
+
if (!home) {
|
|
45
|
+
throw new Error('sandbox: home directory is required');
|
|
46
|
+
}
|
|
47
|
+
const configPath = path.join(repoRoot, '.agents', '.airc.json');
|
|
48
|
+
if (!fs.existsSync(configPath)) {
|
|
49
|
+
throw new Error('No .agents/.airc.json found. Run "ai init" first.');
|
|
50
|
+
}
|
|
51
|
+
const airc = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
52
|
+
const defaults = cloneDefaults();
|
|
53
|
+
const sandbox = airc.sandbox ?? {};
|
|
54
|
+
const engine = validateSandboxEngine(sandbox.engine ?? defaults.engine, { platformFn });
|
|
55
|
+
const project = airc.project;
|
|
56
|
+
if (!project || typeof project !== 'string') {
|
|
57
|
+
throw new Error('sandbox: .agents/.airc.json is missing a valid "project" field');
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
repoRoot,
|
|
61
|
+
configPath,
|
|
62
|
+
project,
|
|
63
|
+
org: typeof airc.org === 'string' ? airc.org : '',
|
|
64
|
+
home,
|
|
65
|
+
containerPrefix: `${project}-dev`,
|
|
66
|
+
imageName: `${project}-sandbox:latest`,
|
|
67
|
+
worktreeBase: hostJoin(home, '.agent-infra', 'worktrees', project),
|
|
68
|
+
shareBase: hostJoin(home, '.agent-infra', 'share', project),
|
|
69
|
+
dotfilesDir: hostJoin(home, '.agent-infra', 'dotfiles'),
|
|
70
|
+
engine,
|
|
71
|
+
runtimes: Array.isArray(sandbox.runtimes) && sandbox.runtimes.length > 0
|
|
72
|
+
? [...sandbox.runtimes]
|
|
73
|
+
: defaults.runtimes,
|
|
74
|
+
tools: Array.isArray(sandbox.tools) && sandbox.tools.length > 0
|
|
75
|
+
? [...sandbox.tools]
|
|
76
|
+
: defaults.tools,
|
|
77
|
+
dockerfile: typeof sandbox.dockerfile === 'string' ? sandbox.dockerfile : defaults.dockerfile ?? null,
|
|
78
|
+
vm: {
|
|
79
|
+
cpu: asPositiveNumberOrNull(sandbox.vm?.cpu) ?? defaults.vm.cpu,
|
|
80
|
+
memory: asPositiveNumberOrNull(sandbox.vm?.memory) ?? defaults.vm.memory,
|
|
81
|
+
disk: asPositiveNumberOrNull(sandbox.vm?.disk) ?? defaults.vm.disk
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { hostJoin } from "./engines/wsl2-paths.js";
|
|
4
|
+
const validatedBranches = new Set();
|
|
5
|
+
function dedupe(items) {
|
|
6
|
+
return [...new Set(items)];
|
|
7
|
+
}
|
|
8
|
+
export function assertValidBranchName(branch) {
|
|
9
|
+
if (validatedBranches.has(branch)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (!branch || branch.trim().length === 0) {
|
|
13
|
+
throw new Error('Branch name is required');
|
|
14
|
+
}
|
|
15
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(branch)) {
|
|
16
|
+
throw new Error(`Invalid branch name '${branch}': only letters, digits, ., _, -, and / are allowed`);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
execFileSync('git', ['check-ref-format', '--branch', branch], {
|
|
20
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`Invalid branch name '${branch}': does not satisfy git branch naming rules`);
|
|
25
|
+
}
|
|
26
|
+
validatedBranches.add(branch);
|
|
27
|
+
}
|
|
28
|
+
export function sanitizeBranchName(branch) {
|
|
29
|
+
assertValidBranchName(branch);
|
|
30
|
+
return branch.replace(/\//g, '..');
|
|
31
|
+
}
|
|
32
|
+
export function legacySanitizeBranchName(branch) {
|
|
33
|
+
assertValidBranchName(branch);
|
|
34
|
+
return branch.replace(/\//g, '-');
|
|
35
|
+
}
|
|
36
|
+
export function safeNameCandidates(branch) {
|
|
37
|
+
return dedupe([sanitizeBranchName(branch), legacySanitizeBranchName(branch)]);
|
|
38
|
+
}
|
|
39
|
+
export function containerName(config, branch) {
|
|
40
|
+
return `${config.containerPrefix}-${sanitizeBranchName(branch)}`;
|
|
41
|
+
}
|
|
42
|
+
export function containerNameCandidates(config, branch) {
|
|
43
|
+
return safeNameCandidates(branch).map((name) => `${config.containerPrefix}-${name}`);
|
|
44
|
+
}
|
|
45
|
+
export function worktreeDir(config, branch) {
|
|
46
|
+
return hostJoin(config.worktreeBase, sanitizeBranchName(branch));
|
|
47
|
+
}
|
|
48
|
+
export function worktreeDirCandidates(config, branch) {
|
|
49
|
+
return safeNameCandidates(branch).map((name) => hostJoin(config.worktreeBase, name));
|
|
50
|
+
}
|
|
51
|
+
export function shareDir(config) {
|
|
52
|
+
return config.shareBase;
|
|
53
|
+
}
|
|
54
|
+
export function shareCommonDir(config) {
|
|
55
|
+
return hostJoin(config.shareBase, 'common');
|
|
56
|
+
}
|
|
57
|
+
export function shareBranchDir(config, branch) {
|
|
58
|
+
return hostJoin(config.shareBase, 'branches', sanitizeBranchName(branch));
|
|
59
|
+
}
|
|
60
|
+
export function sandboxLabel(config) {
|
|
61
|
+
return `${config.project}.sandbox`;
|
|
62
|
+
}
|
|
63
|
+
export function sandboxBranchLabel(config) {
|
|
64
|
+
return `${sandboxLabel(config)}.branch`;
|
|
65
|
+
}
|
|
66
|
+
export function sandboxImageConfigLabel(config) {
|
|
67
|
+
return `${sandboxLabel(config)}.image-config`;
|
|
68
|
+
}
|
|
69
|
+
export function parsePositiveIntegerOption(value, optionName) {
|
|
70
|
+
if (value === undefined || value === null) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const parsed = Number(value);
|
|
74
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
75
|
+
throw new Error(`${optionName} must be a positive integer, got: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
export function detectHostResources() {
|
|
80
|
+
// Resource hints are for engines that pre-allocate a managed VM. macOS uses
|
|
81
|
+
// sysctl for Colima defaults, while the generic fallback supports WSL2 or
|
|
82
|
+
// other direct callers that need conservative CPU and memory defaults.
|
|
83
|
+
if (process.platform === 'darwin') {
|
|
84
|
+
try {
|
|
85
|
+
const hostCpu = Number(execFileSync('sysctl', ['-n', 'hw.ncpu'], { encoding: 'utf8' }).trim());
|
|
86
|
+
const hostMemBytes = Number(execFileSync('sysctl', ['-n', 'hw.memsize'], { encoding: 'utf8' }).trim());
|
|
87
|
+
const hostMemGb = Math.floor(hostMemBytes / 1024 / 1024 / 1024);
|
|
88
|
+
return {
|
|
89
|
+
cpu: Math.max(1, hostCpu - 2),
|
|
90
|
+
memory: Math.max(2, Math.floor(hostMemGb / 2))
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Fall through to generic detection below.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const hostCpu = os.cpus()?.length ?? 4;
|
|
98
|
+
const hostMemGb = Math.floor(os.totalmem() / 1024 / 1024 / 1024);
|
|
99
|
+
return {
|
|
100
|
+
cpu: Math.max(1, Math.min(hostCpu, hostCpu - 1 || 1)),
|
|
101
|
+
memory: Math.max(2, Math.floor(hostMemGb / 2))
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=constants.js.map
|