@migor/cli 0.5.0 → 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/dist/npm-bin.js CHANGED
@@ -1,7 +1,44 @@
1
1
  /**
2
- * Windows 上 npm / npx 是 .cmd shim:不带 shell 的 spawn / spawnSync 无法直接执行
3
- * (ENOENT)。统一在这里按平台点名 —— 保持 CLI 零依赖(不引 cross-spawn)。
2
+ * Windows 上 npm / npx 是 .cmd shim:Node 修补 CVE-2024-27980(18.20.2 / 20.12.2 / 21.7.3+)后,
3
+ * 不带 shell 直接 spawn .cmd 会 EINVAL。统一在这里产出 spawn 描述(cross-spawn 同思路,
4
+ * 保持零依赖):win32 走 cmd.exe 包装 + 逐参数脱敏,其余平台原样直 spawn。
5
+ *
6
+ * 为什么不用 options.shell:true —— shell:true 时 Node 把「命令 + 参数」用空格 join 后
7
+ * 整体塞给 cmd.exe,参数**不做任何转义**;而 add 的参数是用户输入的包名/本地路径,
8
+ * 含 `&` / `|` / `%` 即成命令注入。所以这里走 cmd.exe 包装并逐参数脱敏
9
+ * (算法取自 cross-spawn lib/util/escape.js,https://qntm.org/cmd)。
4
10
  */
5
- export function npmBin(bin, platform = process.platform) {
6
- return platform === 'win32' ? `${bin}.cmd` : bin;
11
+ // cmd.exe 元字符(见 http://www.robvanderwoude.com/escapechars.php)
12
+ const CMD_META_CHARS = /([()\][%!^"`<>&|;, *?])/g;
13
+ /** 命令名脱敏(npm.cmd 本身):不加引号,只 ^ 脱敏元字符 */
14
+ function escapeCmdCommand(command) {
15
+ return command.replace(CMD_META_CHARS, '^$1');
16
+ }
17
+ /**
18
+ * 参数脱敏:反斜杠+引号序列按 CreateProcess 规则翻倍转义,整体加双引号,
19
+ * 再对 cmd 元字符加 ^(`^` 落在引号外才生效,所以先加引号后脱敏)。
20
+ */
21
+ function escapeCmdArg(arg) {
22
+ let out = arg
23
+ // 反斜杠序列 + 引号:反斜杠翻倍,引号加反斜杠
24
+ .replace(/(?=(\\+?)?)\1"/g, '$1$1\\"')
25
+ // 末尾反斜杠序列翻倍(随后会拼上收尾引号)
26
+ .replace(/(?=(\\+?)?)\1$/, '$1$1');
27
+ out = `"${out}"`;
28
+ return out.replace(CMD_META_CHARS, '^$1');
29
+ }
30
+ /**
31
+ * npm / npx 的 spawn 描述:win32 返回 cmd.exe 包装(参数已脱敏,
32
+ * 全局安装的 npm.cmd 非 node_modules/.bin 代理 shim,单层 ^ 脱敏即可);
33
+ * 其余平台原样返回(直接 spawn 可执行文件)。
34
+ */
35
+ export function npmSpawn(bin, args, platform = process.platform, comspec = process.env.comspec) {
36
+ if (platform !== 'win32')
37
+ return { command: bin, args, options: {} };
38
+ const shellCommand = [escapeCmdCommand(`${bin}.cmd`), ...args.map(escapeCmdArg)].join(' ');
39
+ return {
40
+ command: comspec ?? 'cmd.exe',
41
+ args: ['/d', '/s', '/c', `"${shellCommand}"`],
42
+ options: { windowsVerbatimArguments: true },
43
+ };
7
44
  }
package/dist/report.js CHANGED
@@ -30,12 +30,14 @@ export function extractTrace(v) {
30
30
  return null;
31
31
  }
32
32
  /** 打印一张按总耗时降序的能力排行(跨多行记录时按能力合并) */
33
+ /** 用法串(cli.ts 的子命令 `--help` 也从这里取,避免两处各写一份) */
34
+ export const USAGE = '用法:agentia report <trace.jsonl>';
33
35
  export async function reportCommand(args) {
34
36
  const file = args[0];
35
37
  // 失败一律**抛错**(而不是就地设 process.exitCode)—— 本命令是异步的,
36
38
  // 就地设的 exitCode 会被 cli.ts 末尾那句 `process.exitCode = main(...)` 覆盖成 0。
37
39
  if (file === undefined || args.length > 1) {
38
- throw new Error('用法:agentia report <trace.jsonl>');
40
+ throw new Error(USAGE);
39
41
  }
40
42
  let raw;
41
43
  try {
@@ -66,7 +68,17 @@ export async function reportCommand(args) {
66
68
  (badLines > 0 ? `;另有 ${badLines} 行无法解析` : ''));
67
69
  }
68
70
  // 聚合口径与 inspector 面板 / 官网 playground 同源(构建期拷进来的 trace-view 产物)
69
- const { summarizeTrace } = (await import(new URL('./inspector/summary.js', import.meta.url).href));
71
+ // 这份聚合实现由 packages/cli/scripts/copy-assets.mjs 在构建期从 trace-view 产物拷进来。
72
+ // 缺它只可能是「装坏了」或「直接跑未构建的源码」—— 原始 ERR_MODULE_NOT_FOUND 里全是
73
+ // dist 内部路径,用户读不出该做什么,故换成带补救动作的报错。
74
+ let summarizeTrace;
75
+ try {
76
+ ({ summarizeTrace } = (await import(new URL('./inspector/summary.js', import.meta.url).href)));
77
+ }
78
+ catch (e) {
79
+ throw new Error(`缺少 CLI 资源(inspector/summary.js):${e.message};` +
80
+ '请重装 @migor/cli 或重新构建(npm run build:cli)');
81
+ }
70
82
  const merged = new Map();
71
83
  let okRuns = 0;
72
84
  for (const t of traces) {
package/dist/templates.js CHANGED
@@ -58,9 +58,11 @@ export function projectPackageJson(name) {
58
58
  type: 'module',
59
59
  scripts: {
60
60
  dev: 'tsx src/main.ts',
61
+ build: 'tsc -p tsconfig.json && node scripts/copy-assets.mjs',
62
+ start: 'node dist/main.js',
61
63
  typecheck: 'tsc --noEmit -p tsconfig.json',
62
64
  },
63
- dependencies: { '@migor/agentia': '^0.5.0' },
65
+ dependencies: { '@migor/agentia': '^0.6.1' },
64
66
  devDependencies: {
65
67
  tsx: '^4.19.0',
66
68
  typescript: '^7.0.2',
@@ -76,6 +78,14 @@ export function projectTsconfig() {
76
78
  moduleResolution: 'NodeNext',
77
79
  lib: ['ES2022'],
78
80
  strict: true,
81
+ // 编译产物落 dist/(npm run build → npm start 跑 dist/main.js);
82
+ // .md 文本资产由 scripts/copy-assets.mjs 跟随同相对路径拷过去
83
+ // (asset(import.meta.url, './x.md') 按**文件位置**解析,.md 必须跟着 .js 走)
84
+ rootDir: 'src',
85
+ outDir: 'dist',
86
+ // 框架零运行时依赖(不再有厂商 SDK 经传递链把 @types/node 带进编译程序),
87
+ // node 全局类型必须显式声明
88
+ types: ['node'],
79
89
  esModuleInterop: true,
80
90
  skipLibCheck: true,
81
91
  },
@@ -158,6 +168,13 @@ ANTHROPIC_API_KEY=sk-ant-...
158
168
  npm run dev -- "你的问题"
159
169
  \`\`\`
160
170
 
171
+ ## 构建与生产运行
172
+
173
+ \`\`\`bash
174
+ npm run build # tsc → dist/ + .md 文本资产跟随拷贝(asset() 按文件位置解析,必须跟着 .js 走)
175
+ npm start -- "你的问题" # 跑编译产物 dist/main.js(部署/Docker 用这条)
176
+ \`\`\`
177
+
161
178
  也可以用环境变量(适合 CI / 容器)——**真实环境变量优先,不会被 \`.env\` 覆盖**:
162
179
 
163
180
  \`\`\`bash
@@ -195,6 +212,36 @@ ANTHROPIC_API_KEY=
195
212
  # AGENTIA_MODEL=claude-opus-5
196
213
  `;
197
214
  }
215
+ /** 脚手架的 scripts/copy-assets.mjs:把 src 下的 .md 文本资产拷进 dist/(同相对路径) */
216
+ export function copyAssetsMjs() {
217
+ return `// 把能力文件夹里的 .md 文本资产拷进 dist/(与编译产物同相对路径)。
218
+ // asset(import.meta.url, './x.md') 按**文件位置**解析,所以 .md 必须跟着 .js 走。
219
+ import { cpSync, readdirSync } from 'node:fs';
220
+ import { join } from 'node:path';
221
+ import { fileURLToPath } from 'node:url';
222
+
223
+ const here = fileURLToPath(new URL('.', import.meta.url));
224
+ const srcDir = join(here, '..', 'src');
225
+ const outDir = join(here, '..', 'dist');
226
+
227
+ function walk(dir) {
228
+ const out = [];
229
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
230
+ const p = join(dir, e.name);
231
+ if (e.isDirectory()) out.push(...walk(p));
232
+ else if (e.name.endsWith('.md')) out.push(p);
233
+ }
234
+ return out;
235
+ }
236
+
237
+ let n = 0;
238
+ for (const f of walk(srcDir)) {
239
+ cpSync(f, join(outDir, f.slice(srcDir.length + 1)));
240
+ n++;
241
+ }
242
+ console.log(\`[copy-assets] \${n} 个 .md 资产 → dist/\`);
243
+ `;
244
+ }
198
245
  // ---------- g 命令能力模板 ----------
199
246
  export function toolIndexTs(name) {
200
247
  const cls = kebabToPascal(name);
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@migor/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Agentia 框架命令行工具:create 脚手架 + g 能力生成 + dev 本地可观测面板(trace inspector)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "agentia": "./dist/cli.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "CHANGELOG.md"
11
12
  ],
12
13
  "repository": "retrychx/agentia",
13
14
  "homepage": "https://agentia-web.pages.dev",
@@ -33,7 +34,7 @@
33
34
  "prepublishOnly": "node ../../scripts/check-release.mjs && npm run build"
34
35
  },
35
36
  "devDependencies": {
36
- "@types/node": "^22.0.2",
37
+ "@types/node": "^26.5.1",
37
38
  "typescript": "^7.0.2"
38
39
  }
39
40
  }