agency-orchestrator 0.5.1 → 0.5.3

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 (37) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +2 -2
  3. package/dist/cli/compose.js +48 -13
  4. package/dist/cli.js +29 -71
  5. package/dist/connectors/claude-code.js +2 -1
  6. package/dist/connectors/cli-base.js +3 -2
  7. package/dist/core/dag.js +7 -6
  8. package/dist/core/executor.js +1 -1
  9. package/dist/core/parser.js +9 -11
  10. package/dist/i18n.d.ts +19 -0
  11. package/dist/i18n.js +256 -0
  12. package/integrations/aider/README.md +2 -0
  13. package/integrations/antigravity/README.md +2 -0
  14. package/integrations/claude-code/README.md +2 -0
  15. package/integrations/codex/README.md +2 -0
  16. package/integrations/copilot/README.md +2 -0
  17. package/integrations/cursor/README.md +2 -0
  18. package/integrations/deerflow/README.md +2 -0
  19. package/integrations/gemini-cli/README.md +2 -0
  20. package/integrations/kiro/README.md +2 -0
  21. package/integrations/openclaw/README.md +2 -0
  22. package/integrations/opencode/README.md +2 -0
  23. package/integrations/qwen/README.md +2 -0
  24. package/integrations/trae/README.md +2 -0
  25. package/integrations/windsurf/README.md +2 -0
  26. package/package.json +1 -1
  27. package/workflows/academic-paper-outline.yaml +168 -0
  28. package/workflows/douyin-script.yaml +137 -0
  29. package/workflows/investment-analysis.yaml +128 -0
  30. package/workflows/legal-consultation.yaml +143 -0
  31. package/workflows/resume-and-interview-prep.yaml +116 -0
  32. package/workflows/xiaohongshu-viral-post.yaml +103 -0
  33. package/workflows//345/210/206/346/236/220/346/212/226/351/237/263/347/237/255/350/247/206/351/242/221/350/265/233/351/201/223/347/232/204/345/210/233/344/270/232/346/234/272/344/274/232/347/273/231/345/207/272/345/256/214/346/225/264/345/225/206/344/270/232/346/226/271/346/241/210.yaml +0 -190
  34. package/workflows//345/220/210/345/220/214/345/256/241/346/237/245.yaml +0 -106
  35. package/workflows//346/210/221/346/230/257/344/270/200/344/270/252/347/250/213/345/272/217/345/221/230/346/203/263/347/224/250ai/345/201/232/350/207/252/345/252/222/344/275/223/345/211/257/344/270/232/347/233/256/346/240/207/346/234/210/345/205/2452/344/270/207/345/270/256/346/210/221/345/201/232/345/256/214/346/225/264/347/232/204/345/217/257/350/241/214/346/200/247/345/210/206/346/236/220/345/271/263/345/217/260/351/200/211/346/213/251/345/206/205/345/256/271.yaml +0 -116
  36. package/workflows//346/210/221/346/230/257/344/270/200/344/270/252/347/250/213/345/272/217/345/221/230/346/203/263/347/224/250ai/345/201/232/350/207/252/345/252/222/344/275/223/345/211/257/344/270/232/347/233/256/346/240/207/346/234/210/345/205/2452/344/270/207/345/270/256/346/210/221/345/201/232/345/256/214/346/225/264/350/247/204/345/210/222.yaml +0 -98
  37. package/workflows//346/210/221/346/234/210/350/226/2521/344/270/207/344/270/213/347/217/255/345/220/216/346/203/263/346/220/236/345/211/257/344/270/232/345/270/256/346/210/221/345/210/206/346/236/220/351/200/202/345/220/210/346/210/221/347/232/204/346/226/271/345/220/221/345/271/266/347/273/231/345/207/272/345/205/267/344/275/223/346/211/247/350/241/214/350/256/241/345/210/222.yaml +0 -136
package/README.en.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  **One sentence → full plan · 211 expert AI roles · Zero-code YAML · 10 LLM providers · 7 need no API key**
13
13
 
14
- > **Note:** `ao compose --run` auto-detects your language. For English input, it uses [agency-agents](https://github.com/msitarzewski/agency-agents) (170+ English roles). Use `ao init --lang en` to download the English role library. **6 English workflow templates** are ready in `workflows/en/` — more coming in v0.6.
14
+ > **Note:** `ao compose --run` auto-detects your language. Both 211 Chinese roles and 170+ English roles ([agency-agents](https://github.com/msitarzewski/agency-agents), MIT) are **bundled in the npm package no extra download needed**. **6 English workflow templates** are ready in `workflows/en/` — more coming in v0.6.
15
15
 
16
16
  > 📖 [Full Tutorial](https://dev.to/jnmetacode/agency-orchestrator-one-sentence-five-ai-agents-a-complete-plan-in-3-minutes-1ij6) — from install to real-world use in 10 minutes
17
17
 
@@ -217,8 +217,8 @@ All API providers support custom `base_url` and `api_key`, compatible with any O
217
217
 
218
218
  ```bash
219
219
  ao demo # Zero-config multi-agent demo
220
- ao init # Download 211 Chinese AI roles
221
- ao init --lang en # Download 170+ English AI roles
220
+ ao init # (Optional) Copy 211 Chinese roles locally for editing
221
+ ao init --lang en # (Optional) Copy 170+ English roles locally for editing
222
222
  ao init --workflow # Interactive workflow creator
223
223
  ao compose "description" # AI-powered workflow generation
224
224
  ao compose "description" --run # Generate AND execute in one command
package/README.md CHANGED
@@ -216,8 +216,8 @@ analyze ──→ tech_review ──→ summary
216
216
 
217
217
  ```bash
218
218
  ao demo # 零配置体验多智能体协作
219
- ao init # 下载 211 个中文 AI 角色
220
- ao init --lang en # 下载 170+ 个英文 AI 角色
219
+ ao init # (可选)复制 211 个中文角色到本地以便编辑
220
+ ao init --lang en # (可选)复制 170+ 个英文角色到本地以便编辑
221
221
  ao init --workflow # 交互式创建工作流
222
222
  ao compose "一句话描述" # AI 智能编排工作流
223
223
  ao compose "一句话描述" --run # 编排并立即执行
@@ -8,6 +8,7 @@ import { listAgents } from '../agents/loader.js';
8
8
  import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
9
9
  import { resolve, relative } from 'node:path';
10
10
  import { createConnector } from '../connectors/factory.js';
11
+ import { t } from '../i18n.js';
11
12
  /**
12
13
  * 从 agents 目录构建精简的角色目录
13
14
  */
@@ -299,7 +300,7 @@ export async function composeWorkflow(options) {
299
300
  // 1. 构建角色目录
300
301
  const roles = buildRoleCatalog(agentsDir);
301
302
  if (roles.length === 0) {
302
- throw new Error(`角色目录为空: ${agentsDir}\n请先运行 ao init 下载角色定义`);
303
+ throw new Error(t('compose.empty_catalog', { dir: agentsDir }));
303
304
  }
304
305
  const catalog = formatCatalogForPrompt(roles);
305
306
  // 2. 构建 prompt
@@ -311,7 +312,7 @@ export async function composeWorkflow(options) {
311
312
  });
312
313
  const userPrompt = buildComposeUserPrompt(description, lang);
313
314
  // 3. 调用 LLM
314
- console.log(` 正在用 AI 编排工作流...(${roles.length} 个角色可选)\n`);
315
+ console.log(` ${t('compose.generating', { n: roles.length })}\n`);
315
316
  const connector = createConnector(llmConfig);
316
317
  const result = await connector.chat(systemPrompt, userPrompt, {
317
318
  ...llmConfig,
@@ -320,7 +321,7 @@ export async function composeWorkflow(options) {
320
321
  // 4. 提取 YAML
321
322
  const yaml = extractYamlFromResponse(result.content);
322
323
  if (!yaml || !yaml.includes('steps:')) {
323
- throw new Error('AI 生成的内容不是有效的 workflow YAML,请重试或调整描述');
324
+ throw new Error(t('compose.invalid_yaml'));
324
325
  }
325
326
  // 5. 保存(避免覆盖)
326
327
  const workflowsDir = resolve('workflows');
@@ -333,21 +334,55 @@ export async function composeWorkflow(options) {
333
334
  const savedPath = resolve(workflowsDir, fileName);
334
335
  writeFileSync(savedPath, yaml + '\n', 'utf-8');
335
336
  const relativePath = relative(process.cwd(), savedPath);
336
- console.log(` Token 用量: 输入 ${result.usage.input_tokens}, 输出 ${result.usage.output_tokens}`);
337
- // 6. 校验生成的 YAML
337
+ if (process.env.AO_VERBOSE) {
338
+ console.log(` ${t('compose.tokens', { in: result.usage.input_tokens, out: result.usage.output_tokens })}`);
339
+ }
340
+ // 6. 校验生成的 YAML(含角色路径真实性校验,防 LLM 幻觉)
338
341
  const warnings = [];
339
- try {
342
+ const validRolePaths = new Set(roles.map(r => r.path));
343
+ async function validateGenerated(path) {
340
344
  const { parseWorkflow, validateWorkflow } = await import('../core/parser.js');
341
- const workflow = parseWorkflow(savedPath);
342
- const errors = validateWorkflow(workflow);
343
- if (errors.length > 0) {
344
- for (const e of errors) {
345
- warnings.push(e);
345
+ const errors = [];
346
+ const invalidRoles = [];
347
+ try {
348
+ const workflow = parseWorkflow(path);
349
+ errors.push(...validateWorkflow(workflow));
350
+ for (const step of workflow.steps) {
351
+ if (step.role && !validRolePaths.has(step.role)) {
352
+ invalidRoles.push(step.role);
353
+ errors.push(`step "${step.id}" 的 role "${step.role}" 不存在于角色库中`);
354
+ }
346
355
  }
347
356
  }
357
+ catch (err) {
358
+ errors.push(`YAML 解析失败: ${err instanceof Error ? err.message : err}`);
359
+ }
360
+ return { errors, invalidRoles };
348
361
  }
349
- catch (err) {
350
- warnings.push(`YAML 解析失败: ${err instanceof Error ? err.message : err}`);
362
+ const first = await validateGenerated(savedPath);
363
+ // 发现幻觉角色 LLM 修一次
364
+ if (first.invalidRoles.length > 0) {
365
+ console.log(` 检测到 ${first.invalidRoles.length} 个不存在的角色,自动重新生成...`);
366
+ const retryPrompt = lang === 'en'
367
+ ? `The following role paths in your previous YAML do NOT exist in the catalog: ${first.invalidRoles.join(', ')}.\n\nRegenerate the FULL YAML. Use ONLY role paths from the catalog above. Do not invent new paths.`
368
+ : `你上次生成的 YAML 里有不存在的 role 路径:${first.invalidRoles.join('、')}。\n\n请重新生成完整 YAML,role 必须严格使用上方角色目录中列出的路径,不要编造。`;
369
+ try {
370
+ const retryResult = await connector.chat(systemPrompt, `${userPrompt}\n\n${retryPrompt}`, {
371
+ ...llmConfig,
372
+ max_tokens: llmConfig.max_tokens || 4096,
373
+ });
374
+ const retryYaml = extractYamlFromResponse(retryResult.content);
375
+ if (retryYaml && retryYaml.includes('steps:')) {
376
+ writeFileSync(savedPath, retryYaml + '\n', 'utf-8');
377
+ const second = await validateGenerated(savedPath);
378
+ warnings.push(...second.errors);
379
+ return { yaml: retryYaml, savedPath, relativePath, warnings };
380
+ }
381
+ }
382
+ catch (err) {
383
+ warnings.push(`自动修正失败(保留原始输出): ${err instanceof Error ? err.message : err}`);
384
+ }
351
385
  }
386
+ warnings.push(...first.errors);
352
387
  return { yaml, savedPath, relativePath, warnings };
353
388
  }
package/dist/cli.js CHANGED
@@ -16,8 +16,17 @@ import { buildDAG, formatDAG } from './core/dag.js';
16
16
  import { listAgents } from './agents/loader.js';
17
17
  import { run } from './index.js';
18
18
  import { scheduleUpdateCheck } from './utils/version-check.js';
19
+ import { t, detectLang } from './i18n.js';
20
+ // Suppress Node's DEP0190 warning from legitimate shell:true on Windows (.cmd shims).
21
+ const origEmit = process.emit.bind(process);
22
+ process.emit = function (name, data, ...rest) {
23
+ if (name === 'warning' && data && data.code === 'DEP0190')
24
+ return false;
25
+ return origEmit(name, data, ...rest);
26
+ };
19
27
  const args = process.argv.slice(2);
20
28
  const command = args[0];
29
+ detectLang(process.argv);
21
30
  async function main() {
22
31
  // 启动时提示新版本(仅 TTY,24h 缓存,失败静默)
23
32
  scheduleUpdateCheck(getVersion());
@@ -123,18 +132,18 @@ async function handleRun() {
123
132
  function handleValidate() {
124
133
  const filePath = args[1];
125
134
  if (!filePath) {
126
- console.error('用法: ao validate <workflow.yaml>');
135
+ console.error(t('validate.usage'));
127
136
  process.exit(1);
128
137
  }
129
138
  try {
130
139
  const workflow = parseWorkflow(resolve(filePath));
131
140
  const errors = validateWorkflow(workflow);
132
141
  if (errors.length === 0) {
133
- console.log(` ${workflow.name} — 校验通过`);
134
- console.log(` ${workflow.steps.length} 个步骤, ${(workflow.inputs || []).length} 个输入`);
142
+ console.log(` ${t('validate.ok', { name: workflow.name })}`);
143
+ console.log(` ${t('validate.stats', { steps: workflow.steps.length, inputs: (workflow.inputs || []).length })}`);
135
144
  }
136
145
  else {
137
- console.error(` ${workflow.name} — 校验失败:\n`);
146
+ console.error(` ${t('validate.failed', { name: workflow.name })}\n`);
138
147
  for (const err of errors) {
139
148
  console.error(` - ${err}`);
140
149
  }
@@ -142,21 +151,21 @@ function handleValidate() {
142
151
  }
143
152
  }
144
153
  catch (err) {
145
- console.error(`错误: ${err instanceof Error ? err.message : err}`);
154
+ console.error(`${t('error.prefix')}: ${err instanceof Error ? err.message : err}`);
146
155
  process.exit(1);
147
156
  }
148
157
  }
149
158
  function handlePlan() {
150
159
  const filePath = args[1];
151
160
  if (!filePath) {
152
- console.error('用法: ao plan <workflow.yaml>');
161
+ console.error(t('plan.usage'));
153
162
  process.exit(1);
154
163
  }
155
164
  try {
156
165
  const workflow = parseWorkflow(resolve(filePath));
157
166
  const errors = validateWorkflow(workflow);
158
167
  if (errors.length > 0) {
159
- console.error(`校验失败:\n${errors.map(e => ` - ${e}`).join('\n')}`);
168
+ console.error(`${t('plan.validate_failed')}\n${errors.map(e => ` - ${e}`).join('\n')}`);
160
169
  process.exit(1);
161
170
  }
162
171
  const dag = buildDAG(workflow);
@@ -164,7 +173,7 @@ function handlePlan() {
164
173
  console.log(formatDAG(dag));
165
174
  }
166
175
  catch (err) {
167
- console.error(`错误: ${err instanceof Error ? err.message : err}`);
176
+ console.error(`${t('error.prefix')}: ${err instanceof Error ? err.message : err}`);
168
177
  process.exit(1);
169
178
  }
170
179
  }
@@ -242,10 +251,10 @@ async function handleCompose() {
242
251
  autoRun,
243
252
  lang: composeLang,
244
253
  });
245
- console.log(`\n 工作流已生成: ${relativePath}\n`);
254
+ console.log(`\n ${t('compose.generated', { path: relativePath })}\n`);
246
255
  // 校验警告
247
256
  if (warnings.length > 0) {
248
- console.log(' ⚠️ 校验发现问题(AI 生成的 YAML 可能需要手动调整):');
257
+ console.log(` ${t('compose.warnings_header')}`);
249
258
  for (const w of warnings) {
250
259
  console.log(` - ${w}`);
251
260
  }
@@ -253,13 +262,13 @@ async function handleCompose() {
253
262
  }
254
263
  if (autoRun) {
255
264
  // --run 模式:校验有严重问题时不执行
256
- if (warnings.some(w => w.includes('解析失败'))) {
257
- console.error(' 生成的 YAML 有解析错误,无法自动运行。请手动修复后执行:');
265
+ if (warnings.some(w => w.includes('解析失败') || w.toLowerCase().includes('parse failed'))) {
266
+ console.error(` ${t('compose.retry_yaml_bad')}`);
258
267
  console.error(` ao run ${relativePath}`);
259
268
  process.exit(1);
260
269
  }
261
270
  console.log('─'.repeat(50));
262
- console.log(' 开始执行工作流...\n');
271
+ console.log(` ${t('compose.auto_running')}\n`);
263
272
  // 保底:如果 LLM 仍然生成了 required inputs,用用户描述填充
264
273
  const { parseWorkflow } = await import('./core/parser.js');
265
274
  const workflow = parseWorkflow(resolve(savedPath));
@@ -278,7 +287,7 @@ async function handleCompose() {
278
287
  process.exit(result.success ? 0 : 1);
279
288
  }
280
289
  // 非 --run 模式:显示预览和下一步提示
281
- console.log(' 预览:');
290
+ console.log(` ${t('compose.preview')}`);
282
291
  const previewLines = yaml.split('\n').slice(0, 30);
283
292
  for (const line of previewLines) {
284
293
  console.log(` ${line}`);
@@ -287,14 +296,14 @@ async function handleCompose() {
287
296
  console.log(' ...');
288
297
  }
289
298
  console.log('');
290
- console.log(' 接下来可以:');
291
- console.log(` ao validate ${relativePath} 校验工作流`);
292
- console.log(` ao plan ${relativePath} 查看执行计划`);
293
- console.log(` ao run ${relativePath} 运行工作流`);
299
+ console.log(` ${t('compose.next_steps')}`);
300
+ console.log(` ao validate ${relativePath} ${t('compose.next.validate')}`);
301
+ console.log(` ao plan ${relativePath} ${t('compose.next.plan')}`);
302
+ console.log(` ao run ${relativePath} ${t('compose.next.run')}`);
294
303
  console.log('');
295
304
  }
296
305
  catch (err) {
297
- console.error(`\n错误: ${err instanceof Error ? err.message : err}`);
306
+ console.error(`\n${t('error.prefix')}: ${err instanceof Error ? err.message : err}`);
298
307
  process.exit(1);
299
308
  }
300
309
  }
@@ -512,57 +521,6 @@ function getVersion() {
512
521
  }
513
522
  }
514
523
  function printHelp() {
515
- console.log(`
516
- agency-orchestrator — Multi-Agent Workflow Engine
517
- 基于 agency-agents-zh 的多智能体编排引擎
518
-
519
- Quick Start:
520
- ao demo 零配置体验多智能体协作
521
- ao init 下载 AI 角色定义(中文)
522
- ao init --lang en Download English AI roles
523
- ao roles 查看所有可用角色
524
- ao plan <workflow.yaml> 查看执行计划 (DAG)
525
- ao run <workflow.yaml> [options] 执行工作流
526
-
527
- Commands:
528
- demo 零配置体验多智能体协作(mock + 真实 AI)
529
- init 下载/更新角色库 (--lang en 下载英文版)
530
- init --workflow 交互式创建新工作流
531
- compose "描述" AI 智能编排工作流(一句话生成 YAML)
532
- compose "描述" --run 生成并立即运行(一句话出结果)
533
- serve 启动 MCP Server(供 Claude Code / Cursor 调用)
534
- run <workflow.yaml> 执行工作流
535
- validate <workflow.yaml> 校验工作流定义
536
- plan <workflow.yaml> 查看执行计划
537
- explain <workflow.yaml> 用自然语言解释执行计划
538
- roles [--agents-dir path] 列出可用角色
539
-
540
- Options:
541
- --input, -i key=value 传入输入变量
542
- --input, -i key=@file 从文件读取变量值
543
- --provider <name> 覆盖 YAML 中的 LLM provider (如 claude-code, deepseek)
544
- --model <name> 覆盖 YAML 中的模型名
545
- --output dir 输出目录 (默认 ao-output/)
546
- --resume <dir|last> 从上次运行恢复(加载已完成步骤的输出)
547
- --from <step-id> 配合 --resume,从指定步骤重新执行
548
- --watch 实时进度显示(终端 UI)
549
- --quiet, -q 静默模式
550
- --version, -v 版本号
551
-
552
- Examples:
553
- ao init
554
- ao compose "PR 代码审查,覆盖安全和性能"
555
- ao run workflows/story-creation.yaml -i premise='一个时间旅行的故事' -i style='悬疑'
556
- ao run workflows/product-review.yaml -i prd_content=@prd.md
557
- ao plan workflows/content-pipeline.yaml
558
-
559
- Resume (基于上次结果迭代):
560
- ao run workflow.yaml --resume last # 跳过上次已完成的步骤
561
- ao run workflow.yaml --resume last --from summary # 从 summary 步骤重新执行
562
- ao run workflow.yaml --resume ao-output/xxx/ # 指定具体输出目录
563
-
564
- Agents (中文): https://github.com/jnMetaCode/agency-agents-zh
565
- Agents (English): https://github.com/msitarzewski/agency-agents
566
- `);
524
+ console.log(t('help.text'));
567
525
  }
568
526
  main();
@@ -13,6 +13,7 @@ import { spawn } from 'node:child_process';
13
13
  import { writeFileSync, unlinkSync } from 'node:fs';
14
14
  import { tmpdir } from 'node:os';
15
15
  import { join } from 'node:path';
16
+ import { t } from '../i18n.js';
16
17
  export class ClaudeCodeConnector {
17
18
  async chat(systemPrompt, userMessage, config) {
18
19
  const timeout = config.timeout || 600_000; // 默认 10 分钟
@@ -69,7 +70,7 @@ export class ClaudeCodeConnector {
69
70
  if (now - lastProgressTime > 10_000) {
70
71
  lastProgressTime = now;
71
72
  const kb = (receivedBytes / 1024).toFixed(1);
72
- process.stderr.write(` 📡 已接收 ${kb}KB...\n`);
73
+ process.stderr.write(` ${t('stream.received', { size: kb })}\n`);
73
74
  }
74
75
  });
75
76
  child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
@@ -8,6 +8,7 @@
8
8
  * 避免 ENAMETOOLONG 错误(GitHub issue #1)
9
9
  */
10
10
  import { spawn } from 'node:child_process';
11
+ import { t } from '../i18n.js';
11
12
  /**
12
13
  * 命令行参数安全长度上限
13
14
  * claude -p 等 CLI 工具通过命令行参数传大 prompt 会严重变慢
@@ -29,7 +30,7 @@ export class CLIBaseConnector {
29
30
  const args = useStdin
30
31
  ? (this.cfg.buildStdinArgs?.(config) ?? this.cfg.buildArgs('-', config))
31
32
  : this.cfg.buildArgs(fullPrompt, config);
32
- const timeout = config.timeout || 300_000; // 默认 5 分钟,effort=low 正常 1-3 分钟内响应
33
+ const timeout = config.timeout || 600_000; // 默认 10 分钟(gateway/MiniMax CLI provider 可能单步 5+ 分钟)
33
34
  return new Promise((resolve, reject) => {
34
35
  const child = spawn(this.cfg.command, args, {
35
36
  env: { ...process.env },
@@ -60,7 +61,7 @@ export class CLIBaseConnector {
60
61
  if (now - lastProgressTime > 10_000) {
61
62
  lastProgressTime = now;
62
63
  const kb = (receivedBytes / 1024).toFixed(1);
63
- process.stderr.write(` 📡 已接收 ${kb}KB...\n`);
64
+ process.stderr.write(` ${t('stream.received', { size: kb })}\n`);
64
65
  }
65
66
  });
66
67
  child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
package/dist/core/dag.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { t } from '../i18n.js';
1
2
  /**
2
3
  * 从 WorkflowDefinition 构建 DAG
3
4
  */
@@ -82,7 +83,7 @@ function topologicalLevels(nodes) {
82
83
  * 格式化 DAG 为可读文本(用于 `ao plan` 命令)
83
84
  */
84
85
  export function formatDAG(dag) {
85
- const lines = ['执行计划:\n'];
86
+ const lines = [`${t('dag.title')}\n`];
86
87
  for (let i = 0; i < dag.levels.length; i++) {
87
88
  const level = dag.levels[i];
88
89
  const parallel = level.length > 1;
@@ -90,16 +91,16 @@ export function formatDAG(dag) {
90
91
  const node = dag.nodes.get(level[j]);
91
92
  const step = node.step;
92
93
  const prefix = parallel ? (j === 0 ? '┌' : j === level.length - 1 ? '└' : '├') : '→';
93
- const tag = parallel ? ' (并行)' : '';
94
- lines.push(` 第${i + 1} ${prefix} [${step.id}] ${step.role || step.type}${tag}`);
94
+ const tag = parallel ? t('dag.parallel') : '';
95
+ lines.push(` ${t('dag.layer', { n: i + 1 })} ${prefix} [${step.id}] ${step.role || step.type}${tag}`);
95
96
  if (node.dependencies.length > 0) {
96
- lines.push(` 依赖: ${node.dependencies.join(', ')}`);
97
+ lines.push(` ${t('dag.deps')}: ${node.dependencies.join(', ')}`);
97
98
  }
98
99
  if (step.condition) {
99
- lines.push(` 条件: ${step.condition}`);
100
+ lines.push(` ${t('dag.condition')}: ${step.condition}`);
100
101
  }
101
102
  if (step.loop) {
102
- lines.push(` 循环: ${step.loop.back_to} (最多 ${step.loop.max_iterations})`);
103
+ lines.push(` ${t('dag.loop', { to: step.loop.back_to, n: step.loop.max_iterations })}`);
103
104
  }
104
105
  }
105
106
  if (i < dag.levels.length - 1)
@@ -10,7 +10,7 @@ export async function executeDAG(dag, options) {
10
10
  const startTime = Date.now();
11
11
  const stepResults = [];
12
12
  const isCLI = llmConfig.provider.endsWith('-cli') || llmConfig.provider === 'claude-code';
13
- const timeout = llmConfig.timeout || (isCLI ? 300_000 : 120_000); // CLI 5分钟,API 2分钟
13
+ const timeout = llmConfig.timeout || (isCLI ? 600_000 : 120_000); // CLI 10分钟(gateway/MiniMax 等可能单步 5+ 分钟),API 2分钟
14
14
  const maxRetry = llmConfig.retry ?? 5;
15
15
  // CLI provider 强制串行:共享同一账户额度,并发会触发限速反而更慢
16
16
  const effectiveConcurrency = isCLI ? 1 : concurrency;
@@ -3,37 +3,38 @@
3
3
  */
4
4
  import { readFileSync } from 'node:fs';
5
5
  import yaml from 'js-yaml';
6
+ import { t } from '../i18n.js';
6
7
  export function parseWorkflow(filePath) {
7
8
  const raw = readFileSync(filePath, 'utf-8');
8
9
  const doc = yaml.load(raw);
9
10
  // 基本校验
10
11
  if (!doc || typeof doc !== 'object') {
11
- throw new Error(`工作流文件格式错误: ${filePath}`);
12
+ throw new Error(t('parse.bad_yaml', { path: filePath }));
12
13
  }
13
14
  if (!doc.name || typeof doc.name !== 'string') {
14
- throw new Error('工作流缺少 name 字段');
15
+ throw new Error(t('parse.missing_name'));
15
16
  }
16
17
  if (!doc.steps || !Array.isArray(doc.steps) || doc.steps.length === 0) {
17
- throw new Error('工作流缺少 steps 或 steps 为空');
18
+ throw new Error(t('parse.missing_steps'));
18
19
  }
19
20
  if (!doc.llm || typeof doc.llm !== 'object') {
20
- throw new Error('工作流缺少 llm 配置');
21
+ throw new Error(t('parse.missing_llm'));
21
22
  }
22
23
  const llm = doc.llm;
23
24
  if (!llm.provider) {
24
- throw new Error('llm 配置缺少 provider');
25
+ throw new Error(t('parse.missing_provider'));
25
26
  }
26
27
  // CLI providers (claude-code, gemini-cli, copilot-cli, codex-cli, openclaw-cli, hermes-cli) 和 ollama 不需要 model
27
28
  const cliProviders = ['claude-code', 'gemini-cli', 'copilot-cli', 'codex-cli', 'openclaw-cli', 'hermes-cli', 'ollama'];
28
29
  if (!llm.model && !cliProviders.includes(llm.provider)) {
29
- throw new Error('llm 配置缺少 model(CLI provider 可省略)');
30
+ throw new Error(t('parse.missing_model'));
30
31
  }
31
32
  // 校验每个 step
32
33
  const stepIds = new Set();
33
34
  const steps = doc.steps;
34
35
  for (const step of steps) {
35
36
  if (!step.id)
36
- throw new Error('step 缺少 id');
37
+ throw new Error(t('parse.missing_step_id'));
37
38
  if (stepIds.has(step.id))
38
39
  throw new Error(`step id 重复: ${step.id}`);
39
40
  stepIds.add(step.id);
@@ -101,10 +102,7 @@ export function validateWorkflow(workflow) {
101
102
  if (!inputDef && !isOutput && varName !== '_loop_iteration') {
102
103
  errors.push(`step "${step.id}" 引用了未定义的变量: {{${varName}}}`);
103
104
  }
104
- // 可选输入无默认值用在模板中 警告
105
- if (inputDef && !inputDef.required && inputDef.default === undefined) {
106
- errors.push(`step "${step.id}" 使用了可选输入 {{${varName}}},但未设置默认值(未提供时为空字符串)`);
107
- }
105
+ // 可选输入无默认值时,运行时自动填空字符串 非错误,不再警告
108
106
  }
109
107
  }
110
108
  // 检查循环依赖
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Minimal i18n for CLI user-facing strings.
3
+ *
4
+ * Language detection priority:
5
+ * 1. --lang=en CLI flag
6
+ * 2. AO_LANG env var
7
+ * 3. LANG env var (en_US.UTF-8 → en)
8
+ * 4. Default: zh
9
+ */
10
+ type Lang = 'zh' | 'en';
11
+ export declare function detectLang(argv?: string[]): Lang;
12
+ export declare function setLang(lang: Lang): void;
13
+ type Dict = Record<string, {
14
+ zh: string;
15
+ en: string;
16
+ }>;
17
+ declare const dict: Dict;
18
+ export declare function t(key: keyof typeof dict, params?: Record<string, string | number>): string;
19
+ export {};