@fitlab-ai/agent-infra 0.5.10 → 0.6.0
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 +2 -2
- package/README.zh-CN.md +2 -2
- package/bin/{cli.js → cli.ts} +21 -17
- package/dist/bin/cli.js +116 -0
- package/dist/lib/defaults.json +61 -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/{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} +55 -10
- 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/lib/paths.js +0 -9
- package/lib/sandbox/engines/index.js +0 -27
- /package/lib/{version.js → version.ts} +0 -0
|
@@ -11,7 +11,7 @@ Commands:
|
|
|
11
11
|
|
|
12
12
|
Run 'ai sandbox <command> --help' for details.`;
|
|
13
13
|
|
|
14
|
-
export async function runSandbox(args) {
|
|
14
|
+
export async function runSandbox(args: string[]): Promise<void> {
|
|
15
15
|
const [subcommand, ...rest] = args;
|
|
16
16
|
|
|
17
17
|
if (!subcommand) {
|
|
@@ -27,12 +27,12 @@ export async function runSandbox(args) {
|
|
|
27
27
|
|
|
28
28
|
switch (subcommand) {
|
|
29
29
|
case 'create': {
|
|
30
|
-
const { create } = await import('./commands/create.
|
|
30
|
+
const { create } = await import('./commands/create.ts');
|
|
31
31
|
await create(rest);
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
case 'exec': {
|
|
35
|
-
const { enter } = await import('./commands/enter.
|
|
35
|
+
const { enter } = await import('./commands/enter.ts');
|
|
36
36
|
const exitCode = enter(rest);
|
|
37
37
|
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
38
38
|
process.exitCode = exitCode;
|
|
@@ -40,7 +40,7 @@ export async function runSandbox(args) {
|
|
|
40
40
|
break;
|
|
41
41
|
}
|
|
42
42
|
case 'refresh': {
|
|
43
|
-
const { refresh } = await import('./commands/refresh.
|
|
43
|
+
const { refresh } = await import('./commands/refresh.ts');
|
|
44
44
|
const exitCode = await refresh(rest);
|
|
45
45
|
if (typeof exitCode === 'number' && exitCode !== 0) {
|
|
46
46
|
process.exitCode = exitCode;
|
|
@@ -48,22 +48,22 @@ export async function runSandbox(args) {
|
|
|
48
48
|
break;
|
|
49
49
|
}
|
|
50
50
|
case 'ls': {
|
|
51
|
-
const { ls } = await import('./commands/ls.
|
|
51
|
+
const { ls } = await import('./commands/ls.ts');
|
|
52
52
|
ls(rest);
|
|
53
53
|
break;
|
|
54
54
|
}
|
|
55
55
|
case 'rm': {
|
|
56
|
-
const { rm } = await import('./commands/rm.
|
|
56
|
+
const { rm } = await import('./commands/rm.ts');
|
|
57
57
|
await rm(rest);
|
|
58
58
|
break;
|
|
59
59
|
}
|
|
60
60
|
case 'vm': {
|
|
61
|
-
const { vm } = await import('./commands/vm.
|
|
61
|
+
const { vm } = await import('./commands/vm.ts');
|
|
62
62
|
await vm(rest);
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
65
|
case 'rebuild': {
|
|
66
|
-
const { rebuild } = await import('./commands/rebuild.
|
|
66
|
+
const { rebuild } = await import('./commands/rebuild.ts');
|
|
67
67
|
await rebuild(rest);
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import type { ExecFileSyncOptions, StdioOptions, SpawnSyncOptions, SpawnSyncReturns } from 'node:child_process';
|
|
4
5
|
|
|
5
6
|
const DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
type CommandOptions = {
|
|
9
|
+
cwd?: string;
|
|
10
|
+
encoding?: BufferEncoding;
|
|
11
|
+
stdio?: StdioOptions;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
shell?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type RunProbeOptions = CommandOptions & {
|
|
17
|
+
spawnFn?: typeof spawnSync;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function normalizeOptions(opts: CommandOptions = {}, stdio: StdioOptions): CommandOptions {
|
|
8
21
|
return {
|
|
9
22
|
cwd: opts.cwd,
|
|
10
23
|
encoding: opts.encoding,
|
|
@@ -13,7 +26,7 @@ function normalizeOptions(opts = {}, stdio) {
|
|
|
13
26
|
};
|
|
14
27
|
}
|
|
15
28
|
|
|
16
|
-
function resolveCommand(cmd) {
|
|
29
|
+
function resolveCommand(cmd: string): string {
|
|
17
30
|
if (process.platform !== 'win32' || path.extname(cmd)) {
|
|
18
31
|
return cmd;
|
|
19
32
|
}
|
|
@@ -39,14 +52,14 @@ function resolveCommand(cmd) {
|
|
|
39
52
|
return cmd;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
function commandOptions(cmd, opts) {
|
|
55
|
+
function commandOptions<T extends CommandOptions>(cmd: string, opts: T): T | (T & { shell: true }) {
|
|
43
56
|
if (process.platform === 'win32' && /\.(?:bat|cmd)$/i.test(cmd)) {
|
|
44
57
|
return { ...opts, shell: true };
|
|
45
58
|
}
|
|
46
59
|
return opts;
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
export function run(cmd, args, opts = {}) {
|
|
62
|
+
export function run(cmd: string, args: string[], opts: CommandOptions = {}): string {
|
|
50
63
|
const resolved = resolveCommand(cmd);
|
|
51
64
|
return execFileSync(resolved, args, commandOptions(resolved, {
|
|
52
65
|
...normalizeOptions(opts, ['pipe', 'pipe', 'pipe']),
|
|
@@ -54,13 +67,13 @@ export function run(cmd, args, opts = {}) {
|
|
|
54
67
|
})).trim();
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
export function runOk(cmd, args, opts = {}) {
|
|
70
|
+
export function runOk(cmd: string, args: string[], opts: CommandOptions = {}): boolean {
|
|
58
71
|
const resolved = resolveCommand(cmd);
|
|
59
72
|
const result = spawnSync(resolved, args, commandOptions(resolved, normalizeOptions(opts, 'pipe')));
|
|
60
73
|
return result.status === 0;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
|
-
export function restoreTerminal() {
|
|
76
|
+
export function restoreTerminal(): void {
|
|
64
77
|
if (!process.stdout.isTTY) {
|
|
65
78
|
return;
|
|
66
79
|
}
|
|
@@ -90,7 +103,7 @@ export function restoreTerminal() {
|
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
|
|
93
|
-
export function runInteractive(cmd, args, opts = {}) {
|
|
106
|
+
export function runInteractive(cmd: string, args: string[], opts: CommandOptions = {}): number {
|
|
94
107
|
const resolved = resolveCommand(cmd);
|
|
95
108
|
try {
|
|
96
109
|
const result = spawnSync(resolved, args, commandOptions(resolved, normalizeOptions(opts, 'inherit')));
|
|
@@ -100,7 +113,7 @@ export function runInteractive(cmd, args, opts = {}) {
|
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
|
|
103
|
-
export function runVerbose(cmd, args, opts = {}) {
|
|
116
|
+
export function runVerbose(cmd: string, args: string[], opts: CommandOptions = {}): void {
|
|
104
117
|
const resolved = resolveCommand(cmd);
|
|
105
118
|
const result = spawnSync(resolved, args, commandOptions(resolved, normalizeOptions(opts, 'inherit')));
|
|
106
119
|
|
|
@@ -112,7 +125,7 @@ export function runVerbose(cmd, args, opts = {}) {
|
|
|
112
125
|
}
|
|
113
126
|
}
|
|
114
127
|
|
|
115
|
-
export function runSafe(cmd, args, opts = {}) {
|
|
128
|
+
export function runSafe(cmd: string, args: string[], opts: CommandOptions = {}): string {
|
|
116
129
|
const resolved = resolveCommand(cmd);
|
|
117
130
|
const result = spawnSync(resolved, args, commandOptions(resolved, {
|
|
118
131
|
...normalizeOptions(opts, ['pipe', 'pipe', 'pipe']),
|
|
@@ -124,7 +137,7 @@ export function runSafe(cmd, args, opts = {}) {
|
|
|
124
137
|
return (result.stdout ?? '').trim();
|
|
125
138
|
}
|
|
126
139
|
|
|
127
|
-
export function commandForEngine(engine, cmd, args = []) {
|
|
140
|
+
export function commandForEngine(engine: string, cmd: string, args: string[] = []): { cmd: string; args: string[] } {
|
|
128
141
|
if (engine === 'wsl2') {
|
|
129
142
|
const resolvedWrapper = resolveCommand('wsl.exe');
|
|
130
143
|
return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
|
|
@@ -133,37 +146,37 @@ export function commandForEngine(engine, cmd, args = []) {
|
|
|
133
146
|
return { cmd, args };
|
|
134
147
|
}
|
|
135
148
|
|
|
136
|
-
export function runEngine(engine, cmd, args, opts = {}) {
|
|
149
|
+
export function runEngine(engine: string, cmd: string, args: string[], opts: CommandOptions = {}): string {
|
|
137
150
|
const command = commandForEngine(engine, cmd, args);
|
|
138
151
|
return run(command.cmd, command.args, opts);
|
|
139
152
|
}
|
|
140
153
|
|
|
141
|
-
export function execEngine(engine, cmd, args, opts = {}) {
|
|
154
|
+
export function execEngine(engine: string, cmd: string, args: string[], opts: ExecFileSyncOptions = {}) {
|
|
142
155
|
const command = commandForEngine(engine, cmd, args);
|
|
143
156
|
return execFileSync(command.cmd, command.args, opts);
|
|
144
157
|
}
|
|
145
158
|
|
|
146
|
-
export function runOkEngine(engine, cmd, args, opts = {}) {
|
|
159
|
+
export function runOkEngine(engine: string, cmd: string, args: string[], opts: CommandOptions = {}): boolean {
|
|
147
160
|
const command = commandForEngine(engine, cmd, args);
|
|
148
161
|
return runOk(command.cmd, command.args, opts);
|
|
149
162
|
}
|
|
150
163
|
|
|
151
|
-
export function runSafeEngine(engine, cmd, args, opts = {}) {
|
|
164
|
+
export function runSafeEngine(engine: string, cmd: string, args: string[], opts: CommandOptions = {}): string {
|
|
152
165
|
const command = commandForEngine(engine, cmd, args);
|
|
153
166
|
return runSafe(command.cmd, command.args, opts);
|
|
154
167
|
}
|
|
155
168
|
|
|
156
|
-
export function runVerboseEngine(engine, cmd, args, opts = {}) {
|
|
169
|
+
export function runVerboseEngine(engine: string, cmd: string, args: string[], opts: CommandOptions = {}): void {
|
|
157
170
|
const command = commandForEngine(engine, cmd, args);
|
|
158
171
|
return runVerbose(command.cmd, command.args, opts);
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
export function runInteractiveEngine(engine, cmd, args, opts = {}) {
|
|
174
|
+
export function runInteractiveEngine(engine: string, cmd: string, args: string[], opts: CommandOptions = {}): number {
|
|
162
175
|
const command = commandForEngine(engine, cmd, args);
|
|
163
176
|
return runInteractive(command.cmd, command.args, opts);
|
|
164
177
|
}
|
|
165
178
|
|
|
166
|
-
export function runProbe(cmd, args, opts = {}) {
|
|
179
|
+
export function runProbe(cmd: string, args: string[], opts: RunProbeOptions = {}): SpawnSyncReturns<string | Buffer> {
|
|
167
180
|
const { spawnFn = spawnSync, ...commandOpts } = opts;
|
|
168
181
|
const resolved = resolveCommand(cmd);
|
|
169
182
|
return spawnFn(resolved, args, commandOptions(resolved, normalizeOptions(
|
|
@@ -4,11 +4,11 @@ import path from 'node:path';
|
|
|
4
4
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
5
5
|
const WORKSPACE_DIRS = ['active', 'completed', 'blocked', 'archive'];
|
|
6
6
|
|
|
7
|
-
function stripQuotes(value) {
|
|
7
|
+
function stripQuotes(value: string): string {
|
|
8
8
|
return value.replace(/^(["'])(.*)\1$/, '$2');
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function readTaskContent(repoRoot, taskId) {
|
|
11
|
+
function readTaskContent(repoRoot: string, taskId: string): string {
|
|
12
12
|
for (const dir of WORKSPACE_DIRS) {
|
|
13
13
|
const taskPath = path.join(repoRoot, '.agents', 'workspace', dir, taskId, 'task.md');
|
|
14
14
|
if (fs.existsSync(taskPath)) {
|
|
@@ -18,21 +18,21 @@ function readTaskContent(repoRoot, taskId) {
|
|
|
18
18
|
throw new Error(`Task not found: ${taskId}`);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function resolveBranchFromTaskContent(content, taskId) {
|
|
21
|
+
function resolveBranchFromTaskContent(content: string, taskId: string): string {
|
|
22
22
|
const frontmatterBranch = content.match(/^branch:\s*(.+)$/m);
|
|
23
|
-
if (frontmatterBranch
|
|
23
|
+
if (frontmatterBranch?.[1]?.trim()) {
|
|
24
24
|
return stripQuotes(frontmatterBranch[1].trim());
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const contextBranch = content.match(/^- \*\*(?:分支|Branch)\*\*:[ \t]*`?([^`\n]+)`?$/m);
|
|
28
|
-
if (contextBranch
|
|
28
|
+
if (contextBranch?.[1]?.trim()) {
|
|
29
29
|
return stripQuotes(contextBranch[1].trim());
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
throw new Error(`Task ${taskId} has no branch field in task.md`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function resolveTaskBranch(arg, repoRoot) {
|
|
35
|
+
export function resolveTaskBranch(arg: string, repoRoot: string): string {
|
|
36
36
|
if (!TASK_ID_RE.test(arg)) {
|
|
37
37
|
return arg;
|
|
38
38
|
}
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import { safeNameCandidates, sanitizeBranchName } from './constants.
|
|
2
|
-
import { hostJoin } from './engines/wsl2-paths.
|
|
1
|
+
import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
|
|
2
|
+
import { hostJoin } from './engines/wsl2-paths.ts';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
4
|
+
export type SandboxTool = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
npmPackage: string;
|
|
8
|
+
sandboxBase: string;
|
|
9
|
+
containerMount: string;
|
|
10
|
+
versionCmd: string;
|
|
11
|
+
setupHint: string;
|
|
12
|
+
envVars?: Record<string, string>;
|
|
13
|
+
hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
|
|
14
|
+
hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
|
|
15
|
+
pathRewriteFiles?: string[];
|
|
16
|
+
hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
|
|
17
|
+
postSetupCmds?: string[];
|
|
18
|
+
};
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
type ToolsConfig = {
|
|
21
|
+
home: string;
|
|
22
|
+
project: string;
|
|
23
|
+
tools: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function createBuiltinTools(home: string, project: string): Record<string, SandboxTool> {
|
|
23
27
|
return {
|
|
24
28
|
'claude-code': {
|
|
25
29
|
id: 'claude-code',
|
|
@@ -104,13 +108,13 @@ function createBuiltinTools(home, project) {
|
|
|
104
108
|
};
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
function validateTool(tool) {
|
|
111
|
+
function validateTool(tool: SandboxTool): void {
|
|
108
112
|
if (!tool.npmPackage || !tool.containerMount.startsWith('/')) {
|
|
109
113
|
throw new Error(`Invalid sandbox tool descriptor: ${tool.id}`);
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
export function resolveTools(config) {
|
|
117
|
+
export function resolveTools(config: ToolsConfig): SandboxTool[] {
|
|
114
118
|
const builtins = createBuiltinTools(config.home, config.project);
|
|
115
119
|
return config.tools.map((id) => {
|
|
116
120
|
const tool = builtins[id];
|
|
@@ -122,18 +126,18 @@ export function resolveTools(config) {
|
|
|
122
126
|
});
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
export function toolConfigDir(tool, project, branch) {
|
|
129
|
+
export function toolConfigDir(tool: SandboxTool, project: string, branch: string): string {
|
|
126
130
|
return hostJoin(tool.sandboxBase, project, sanitizeBranchName(branch));
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
export function toolConfigDirCandidates(tool, project, branch) {
|
|
133
|
+
export function toolConfigDirCandidates(tool: SandboxTool, project: string, branch: string): string[] {
|
|
130
134
|
return safeNameCandidates(branch).map((name) => hostJoin(tool.sandboxBase, project, name));
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
export function toolProjectDirCandidates(tool, project) {
|
|
137
|
+
export function toolProjectDirCandidates(tool: SandboxTool, project: string): string[] {
|
|
134
138
|
return [hostJoin(tool.sandboxBase, project)];
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
export function toolNpmPackagesArg(tools) {
|
|
141
|
+
export function toolNpmPackagesArg(tools: SandboxTool[]): string {
|
|
138
142
|
return tools.map((tool) => tool.npmPackage).join(' ');
|
|
139
143
|
}
|
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { info, ok, err } from './log.
|
|
4
|
-
import { resolveTemplateDir } from './paths.
|
|
5
|
-
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.
|
|
3
|
+
import { info, ok, err } from './log.ts';
|
|
4
|
+
import { resolveTemplateDir } from './paths.ts';
|
|
5
|
+
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.ts';
|
|
6
|
+
|
|
7
|
+
type FileRegistry = {
|
|
8
|
+
managed: string[];
|
|
9
|
+
merged: string[];
|
|
10
|
+
ejected: string[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type UpdateConfig = {
|
|
14
|
+
project: string;
|
|
15
|
+
org: string;
|
|
16
|
+
language: string;
|
|
17
|
+
platform?: { type?: string };
|
|
18
|
+
sandbox?: Record<string, unknown>;
|
|
19
|
+
labels?: Record<string, unknown>;
|
|
20
|
+
files?: Partial<FileRegistry>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Defaults = {
|
|
24
|
+
platform: { type: string };
|
|
25
|
+
sandbox: Record<string, unknown>;
|
|
26
|
+
labels: Record<string, unknown>;
|
|
27
|
+
files: FileRegistry;
|
|
28
|
+
};
|
|
6
29
|
|
|
7
30
|
const defaults = JSON.parse(
|
|
8
31
|
fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8')
|
|
9
|
-
);
|
|
32
|
+
) as Defaults;
|
|
10
33
|
|
|
11
34
|
const CONFIG_DIR = '.agents';
|
|
12
35
|
const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
|
|
13
36
|
|
|
14
|
-
function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
15
|
-
const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0];
|
|
37
|
+
function isPathOwnedByOtherPlatform(relativePath: string, platformType: string): boolean {
|
|
38
|
+
const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
|
|
16
39
|
if (!top.startsWith('.')) return false;
|
|
17
40
|
|
|
18
41
|
const candidate = top.slice(1);
|
|
@@ -20,7 +43,7 @@ function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
|
20
43
|
return candidate !== platformType;
|
|
21
44
|
}
|
|
22
45
|
|
|
23
|
-
function syncFileRegistry(config, platformType) {
|
|
46
|
+
function syncFileRegistry(config: UpdateConfig, platformType: string) {
|
|
24
47
|
config.files ||= {};
|
|
25
48
|
const before = JSON.stringify({
|
|
26
49
|
files: {
|
|
@@ -38,7 +61,7 @@ function syncFileRegistry(config, platformType) {
|
|
|
38
61
|
...config.files.merged,
|
|
39
62
|
...config.files.ejected
|
|
40
63
|
];
|
|
41
|
-
const added = { managed: [], merged: [] };
|
|
64
|
+
const added: Pick<FileRegistry, 'managed' | 'merged'> = { managed: [], merged: [] };
|
|
42
65
|
|
|
43
66
|
for (const entry of defaults.files.managed) {
|
|
44
67
|
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
@@ -66,7 +89,7 @@ function syncFileRegistry(config, platformType) {
|
|
|
66
89
|
return { added, changed: before !== after };
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
async function cmdUpdate() {
|
|
92
|
+
async function cmdUpdate(): Promise<void> {
|
|
70
93
|
console.log('');
|
|
71
94
|
console.log(' agent-infra update');
|
|
72
95
|
console.log(' ==================================');
|
|
@@ -90,7 +113,7 @@ async function cmdUpdate() {
|
|
|
90
113
|
}
|
|
91
114
|
|
|
92
115
|
// read project config
|
|
93
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
116
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) as UpdateConfig;
|
|
94
117
|
const { project, org, language } = config;
|
|
95
118
|
const platformType = config.platform?.type || defaults.platform.type;
|
|
96
119
|
const replacements = { project, org };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
"registry": "https://registry.npmjs.org/"
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
|
-
"agent-infra": "./bin/cli.js",
|
|
22
|
-
"ai": "./bin/cli.js"
|
|
21
|
+
"agent-infra": "./dist/bin/cli.js",
|
|
22
|
+
"ai": "./dist/bin/cli.js"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
|
-
"
|
|
25
|
+
"dist/",
|
|
26
|
+
"!dist/**/*.map",
|
|
27
|
+
"bin/cli.ts",
|
|
26
28
|
"lib/",
|
|
27
29
|
"templates/"
|
|
28
30
|
],
|
|
@@ -47,12 +49,18 @@
|
|
|
47
49
|
"smol-toml": "^1.6.1"
|
|
48
50
|
},
|
|
49
51
|
"scripts": {
|
|
50
|
-
"build": "node scripts/build-inline.js",
|
|
52
|
+
"build": "tsc -p tsconfig.json && node scripts/build.js && node scripts/build-inline.js",
|
|
51
53
|
"demo:regen": "sh scripts/demo-regen.sh",
|
|
52
54
|
"prepare": "git config core.hooksPath .git-hooks || true",
|
|
53
|
-
"
|
|
54
|
-
"test:
|
|
55
|
-
"test": "node
|
|
56
|
-
"
|
|
55
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit && tsc -p tsconfig.jschecks.json --noEmit",
|
|
56
|
+
"test:smoke": "npm run build && node --experimental-strip-types --no-warnings --test tests/templates/*.test.ts tests/core/airc.test.ts tests/core/release.test.ts tests/core/metadata-sync-workflow.test.ts tests/core/pr-label-workflow.test.ts tests/core/status-label-workflow.test.ts tests/core/test-tier-coverage.test.ts tests/cli/lib.test.ts tests/cli/sync-templates.test.ts tests/scripts/sync-templates-platform-gating.test.ts",
|
|
57
|
+
"test:core": "npm run build && node --experimental-strip-types --no-warnings --test tests/templates/*.test.ts tests/core/airc.test.ts tests/core/release.test.ts tests/core/metadata-sync-workflow.test.ts tests/core/pr-label-workflow.test.ts tests/core/status-label-workflow.test.ts tests/core/test-tier-coverage.test.ts tests/cli/lib.test.ts tests/cli/sync-templates.test.ts tests/scripts/sync-templates-platform-gating.test.ts tests/cli/cli.test.ts tests/cli/merge.test.ts tests/cli/sandbox.test.ts tests/core/custom-skills.test.ts tests/core/custom-tuis.test.ts tests/core/demo-regen.test.ts tests/scripts/find-existing-task.test.ts tests/scripts/platform-adapter-defaults.test.ts",
|
|
58
|
+
"test": "npm run build && node --experimental-strip-types --no-warnings --test tests/cli/*.test.ts tests/templates/*.test.ts tests/core/*.test.ts tests/scripts/*.test.ts",
|
|
59
|
+
"prepublishOnly": "npm run build && node --experimental-strip-types --no-warnings --test tests/cli/*.test.ts tests/templates/*.test.ts tests/core/*.test.ts tests/scripts/*.test.ts"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/cross-spawn": "^6.0.6",
|
|
63
|
+
"@types/node": "^22.19.19",
|
|
64
|
+
"typescript": "~5.9"
|
|
57
65
|
}
|
|
58
66
|
}
|
package/lib/paths.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
|
|
4
|
-
function resolveTemplateDir() {
|
|
5
|
-
const bundledDir = fileURLToPath(new URL('../templates', import.meta.url));
|
|
6
|
-
return fs.existsSync(bundledDir) ? bundledDir : null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export { resolveTemplateDir };
|
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
export const ADAPTERS = Object.freeze({
|
|
8
|
-
colima: colimaAdapter,
|
|
9
|
-
orbstack: orbstackAdapter,
|
|
10
|
-
'docker-desktop': dockerDesktopAdapter,
|
|
11
|
-
native: nativeAdapter,
|
|
12
|
-
wsl2: wsl2Adapter
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export function getAdapter(engineId) {
|
|
16
|
-
const adapter = ADAPTERS[engineId];
|
|
17
|
-
if (!adapter) {
|
|
18
|
-
throw new Error(`No adapter registered for engine '${engineId}'`);
|
|
19
|
-
}
|
|
20
|
-
return adapter;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function enginesForPlatform(platformName) {
|
|
24
|
-
return Object.values(ADAPTERS)
|
|
25
|
-
.filter((adapter) => adapter.supportedPlatforms.includes(platformName))
|
|
26
|
-
.map((adapter) => adapter.id);
|
|
27
|
-
}
|
|
File without changes
|