@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.
Files changed (82) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/bin/{cli.js → cli.ts} +21 -17
  4. package/dist/bin/cli.js +116 -0
  5. package/dist/lib/defaults.json +61 -0
  6. package/dist/lib/init.js +238 -0
  7. package/dist/lib/log.js +18 -0
  8. package/dist/lib/merge.js +747 -0
  9. package/dist/lib/paths.js +18 -0
  10. package/dist/lib/prompt.js +85 -0
  11. package/dist/lib/render.js +139 -0
  12. package/dist/lib/sandbox/commands/create.js +1173 -0
  13. package/dist/lib/sandbox/commands/enter.js +98 -0
  14. package/dist/lib/sandbox/commands/ls.js +93 -0
  15. package/dist/lib/sandbox/commands/rebuild.js +101 -0
  16. package/dist/lib/sandbox/commands/refresh.js +85 -0
  17. package/dist/lib/sandbox/commands/rm.js +226 -0
  18. package/dist/lib/sandbox/commands/vm.js +144 -0
  19. package/dist/lib/sandbox/config.js +85 -0
  20. package/dist/lib/sandbox/constants.js +104 -0
  21. package/dist/lib/sandbox/credentials.js +437 -0
  22. package/dist/lib/sandbox/dockerfile.js +76 -0
  23. package/dist/lib/sandbox/dotfiles.js +170 -0
  24. package/dist/lib/sandbox/engine.js +155 -0
  25. package/dist/lib/sandbox/engines/colima.js +64 -0
  26. package/dist/lib/sandbox/engines/docker-desktop.js +27 -0
  27. package/dist/lib/sandbox/engines/index.js +25 -0
  28. package/dist/lib/sandbox/engines/native.js +96 -0
  29. package/dist/lib/sandbox/engines/orbstack.js +63 -0
  30. package/dist/lib/sandbox/engines/selinux.js +48 -0
  31. package/dist/lib/sandbox/engines/wsl2-paths.js +47 -0
  32. package/dist/lib/sandbox/engines/wsl2.js +57 -0
  33. package/dist/lib/sandbox/index.js +70 -0
  34. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +39 -0
  35. package/dist/lib/sandbox/runtimes/base.dockerfile +178 -0
  36. package/dist/lib/sandbox/runtimes/java17.dockerfile +3 -0
  37. package/dist/lib/sandbox/runtimes/java21.dockerfile +3 -0
  38. package/dist/lib/sandbox/runtimes/node20.dockerfile +3 -0
  39. package/dist/lib/sandbox/runtimes/node22.dockerfile +3 -0
  40. package/dist/lib/sandbox/runtimes/python3.dockerfile +3 -0
  41. package/dist/lib/sandbox/shell.js +148 -0
  42. package/dist/lib/sandbox/task-resolver.js +35 -0
  43. package/dist/lib/sandbox/tools.js +115 -0
  44. package/dist/lib/update.js +186 -0
  45. package/dist/lib/version.js +5 -0
  46. package/dist/package.json +5 -0
  47. package/lib/{init.js → init.ts} +48 -18
  48. package/lib/{log.js → log.ts} +4 -4
  49. package/lib/{merge.js → merge.ts} +129 -63
  50. package/lib/paths.ts +18 -0
  51. package/lib/{prompt.js → prompt.ts} +12 -12
  52. package/lib/{render.js → render.ts} +30 -17
  53. package/lib/sandbox/commands/{create.js → create.ts} +224 -118
  54. package/lib/sandbox/commands/{enter.js → enter.ts} +17 -14
  55. package/lib/sandbox/commands/{ls.js → ls.ts} +10 -10
  56. package/lib/sandbox/commands/{rebuild.js → rebuild.ts} +38 -21
  57. package/lib/sandbox/commands/{refresh.js → refresh.ts} +16 -7
  58. package/lib/sandbox/commands/{rm.js → rm.ts} +15 -13
  59. package/lib/sandbox/commands/{vm.js → vm.ts} +14 -11
  60. package/lib/sandbox/{config.js → config.ts} +55 -10
  61. package/lib/sandbox/{constants.js → constants.ts} +30 -18
  62. package/lib/sandbox/{credentials.js → credentials.ts} +160 -46
  63. package/lib/sandbox/{dockerfile.js → dockerfile.ts} +13 -6
  64. package/lib/sandbox/{dotfiles.js → dotfiles.ts} +66 -19
  65. package/lib/sandbox/{engine.js → engine.ts} +57 -25
  66. package/lib/sandbox/engines/{colima.js → colima.ts} +9 -7
  67. package/lib/sandbox/engines/{docker-desktop.js → docker-desktop.ts} +5 -3
  68. package/lib/sandbox/engines/index.ts +74 -0
  69. package/lib/sandbox/engines/{native.js → native.ts} +25 -6
  70. package/lib/sandbox/engines/{orbstack.js → orbstack.ts} +7 -5
  71. package/lib/sandbox/engines/{selinux.js → selinux.ts} +11 -5
  72. package/lib/sandbox/engines/{wsl2-paths.js → wsl2-paths.ts} +15 -9
  73. package/lib/sandbox/engines/{wsl2.js → wsl2.ts} +9 -7
  74. package/lib/sandbox/{index.js → index.ts} +8 -8
  75. package/lib/sandbox/{shell.js → shell.ts} +30 -17
  76. package/lib/sandbox/{task-resolver.js → task-resolver.ts} +6 -6
  77. package/lib/sandbox/{tools.js → tools.ts} +30 -26
  78. package/lib/{update.js → update.ts} +33 -10
  79. package/package.json +17 -9
  80. package/lib/paths.js +0 -9
  81. package/lib/sandbox/engines/index.js +0 -27
  82. /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.js');
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.js');
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.js');
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.js');
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.js');
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.js');
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.js');
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
- function normalizeOptions(opts = {}, stdio) {
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 && frontmatterBranch[1].trim()) {
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 && contextBranch[1].trim()) {
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.js';
2
- import { hostJoin } from './engines/wsl2-paths.js';
1
+ import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
2
+ import { hostJoin } from './engines/wsl2-paths.ts';
3
3
 
4
- /**
5
- * @typedef {Object} SandboxTool
6
- * @property {string} id
7
- * @property {string} name
8
- * @property {string} npmPackage
9
- * @property {string} sandboxBase
10
- * @property {string} containerMount
11
- * @property {string} versionCmd
12
- * @property {string} setupHint
13
- * @property {Record<string, string>=} envVars
14
- * @property {Array<{ hostPath: string, sandboxName: string }>=} hostPreSeedFiles
15
- * @property {Array<{ hostDir: string, sandboxSubdir: string }>=} hostPreSeedDirs
16
- * @property {string[]=} pathRewriteFiles
17
- * @property {Array<{ hostPath: string, containerSubpath: string }>=} hostLiveMounts
18
- * @property {string[]=} postSetupCmds
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
- function createBuiltinTools(home, project) {
22
- /** @type {Record<string, SandboxTool>} */
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.js';
4
- import { resolveTemplateDir } from './paths.js';
5
- import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.js';
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.5.10",
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
- "bin/cli.js",
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
- "test:smoke": "node scripts/build-inline.js --check && node --test tests/templates/*.test.js tests/core/airc.test.js tests/core/release.test.js tests/core/metadata-sync-workflow.test.js tests/core/pr-label-workflow.test.js tests/core/status-label-workflow.test.js tests/core/test-tier-coverage.test.js tests/cli/lib.test.js tests/cli/sync-templates.test.js tests/scripts/sync-templates-platform-gating.test.js",
54
- "test:core": "node scripts/build-inline.js --check && node --test tests/templates/*.test.js tests/core/airc.test.js tests/core/release.test.js tests/core/metadata-sync-workflow.test.js tests/core/pr-label-workflow.test.js tests/core/status-label-workflow.test.js tests/core/test-tier-coverage.test.js tests/cli/lib.test.js tests/cli/sync-templates.test.js tests/scripts/sync-templates-platform-gating.test.js tests/cli/cli.test.js tests/cli/merge.test.js tests/cli/sandbox.test.js tests/core/custom-skills.test.js tests/core/custom-tuis.test.js tests/core/demo-regen.test.js tests/scripts/find-existing-task.test.js tests/scripts/platform-adapter-defaults.test.js",
55
- "test": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js tests/scripts/*.test.js",
56
- "prepublishOnly": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js tests/scripts/*.test.js"
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