agency-orchestrator 0.5.2 → 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.
- package/dist/cli/compose.js +7 -4
- package/dist/cli.js +29 -71
- package/dist/connectors/claude-code.js +2 -1
- package/dist/connectors/cli-base.js +2 -1
- package/dist/core/dag.js +7 -6
- package/dist/core/parser.js +9 -11
- package/dist/i18n.d.ts +19 -0
- package/dist/i18n.js +256 -0
- package/package.json +1 -1
- package/workflows/academic-paper-outline.yaml +168 -0
- package/workflows/douyin-script.yaml +137 -0
- package/workflows/investment-analysis.yaml +128 -0
- package/workflows/legal-consultation.yaml +143 -0
- package/workflows/resume-and-interview-prep.yaml +116 -0
- 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
- package/workflows//345/220/210/345/220/214/345/256/241/346/237/245.yaml +0 -106
- 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
- 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
- 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/dist/cli/compose.js
CHANGED
|
@@ -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(
|
|
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(`
|
|
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('
|
|
324
|
+
throw new Error(t('compose.invalid_yaml'));
|
|
324
325
|
}
|
|
325
326
|
// 5. 保存(避免覆盖)
|
|
326
327
|
const workflowsDir = resolve('workflows');
|
|
@@ -333,7 +334,9 @@ 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
|
-
|
|
337
|
+
if (process.env.AO_VERBOSE) {
|
|
338
|
+
console.log(` ${t('compose.tokens', { in: result.usage.input_tokens, out: result.usage.output_tokens })}`);
|
|
339
|
+
}
|
|
337
340
|
// 6. 校验生成的 YAML(含角色路径真实性校验,防 LLM 幻觉)
|
|
338
341
|
const warnings = [];
|
|
339
342
|
const validRolePaths = new Set(roles.map(r => r.path));
|
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('
|
|
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
|
|
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}
|
|
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(
|
|
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('
|
|
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(
|
|
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(
|
|
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
|
|
254
|
+
console.log(`\n ${t('compose.generated', { path: relativePath })}\n`);
|
|
246
255
|
// 校验警告
|
|
247
256
|
if (warnings.length > 0) {
|
|
248
|
-
console.log(
|
|
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(
|
|
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(
|
|
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
|
|
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(`
|
|
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 会严重变慢
|
|
@@ -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(`
|
|
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 = ['
|
|
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(`
|
|
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(`
|
|
97
|
+
lines.push(` ${t('dag.deps')}: ${node.dependencies.join(', ')}`);
|
|
97
98
|
}
|
|
98
99
|
if (step.condition) {
|
|
99
|
-
lines.push(`
|
|
100
|
+
lines.push(` ${t('dag.condition')}: ${step.condition}`);
|
|
100
101
|
}
|
|
101
102
|
if (step.loop) {
|
|
102
|
-
lines.push(`
|
|
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)
|
package/dist/core/parser.js
CHANGED
|
@@ -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(
|
|
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('
|
|
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('
|
|
18
|
+
throw new Error(t('parse.missing_steps'));
|
|
18
19
|
}
|
|
19
20
|
if (!doc.llm || typeof doc.llm !== 'object') {
|
|
20
|
-
throw new Error('
|
|
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('
|
|
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('
|
|
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('
|
|
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 {};
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
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
|
+
let currentLang = null;
|
|
11
|
+
export function detectLang(argv = process.argv) {
|
|
12
|
+
if (currentLang)
|
|
13
|
+
return currentLang;
|
|
14
|
+
const flag = argv.find(a => a === '--lang=en' || a === '--lang=zh');
|
|
15
|
+
if (flag) {
|
|
16
|
+
currentLang = flag.endsWith('en') ? 'en' : 'zh';
|
|
17
|
+
return currentLang;
|
|
18
|
+
}
|
|
19
|
+
const idx = argv.indexOf('--lang');
|
|
20
|
+
if (idx >= 0 && argv[idx + 1]) {
|
|
21
|
+
currentLang = argv[idx + 1].toLowerCase().startsWith('en') ? 'en' : 'zh';
|
|
22
|
+
return currentLang;
|
|
23
|
+
}
|
|
24
|
+
const envAo = process.env.AO_LANG;
|
|
25
|
+
if (envAo) {
|
|
26
|
+
currentLang = envAo.toLowerCase().startsWith('en') ? 'en' : 'zh';
|
|
27
|
+
return currentLang;
|
|
28
|
+
}
|
|
29
|
+
const envLang = process.env.LANG || process.env.LC_ALL || '';
|
|
30
|
+
if (/^en/i.test(envLang)) {
|
|
31
|
+
currentLang = 'en';
|
|
32
|
+
return currentLang;
|
|
33
|
+
}
|
|
34
|
+
currentLang = 'zh';
|
|
35
|
+
return currentLang;
|
|
36
|
+
}
|
|
37
|
+
export function setLang(lang) {
|
|
38
|
+
currentLang = lang;
|
|
39
|
+
}
|
|
40
|
+
const dict = {
|
|
41
|
+
'compose.generating': {
|
|
42
|
+
zh: '正在用 AI 编排工作流...({n} 个角色可选)',
|
|
43
|
+
en: 'Composing workflow with AI... ({n} roles available)',
|
|
44
|
+
},
|
|
45
|
+
'compose.tokens': {
|
|
46
|
+
zh: 'Token 用量: 输入 {in}, 输出 {out}',
|
|
47
|
+
en: 'Token usage: input {in}, output {out}',
|
|
48
|
+
},
|
|
49
|
+
'compose.generated': {
|
|
50
|
+
zh: '✅ 工作流已生成: {path}',
|
|
51
|
+
en: '✅ Workflow generated: {path}',
|
|
52
|
+
},
|
|
53
|
+
'compose.warnings_header': {
|
|
54
|
+
zh: '⚠️ 校验发现问题(AI 生成的 YAML 可能需要手动调整):',
|
|
55
|
+
en: '⚠️ Validation warnings (AI-generated YAML may need manual tweaks):',
|
|
56
|
+
},
|
|
57
|
+
'compose.invalid_yaml': {
|
|
58
|
+
zh: 'AI 生成的内容不是有效的 workflow YAML,请重试或调整描述',
|
|
59
|
+
en: 'AI output is not a valid workflow YAML — retry or refine your description',
|
|
60
|
+
},
|
|
61
|
+
'compose.empty_catalog': {
|
|
62
|
+
zh: '角色目录为空: {dir}\n请先运行 ao init 下载角色定义',
|
|
63
|
+
en: 'Role catalog is empty: {dir}\nRun `ao init` first to download role definitions',
|
|
64
|
+
},
|
|
65
|
+
'compose.retry_yaml_bad': {
|
|
66
|
+
zh: '严重错误无法自动运行,请手动修复后执行:',
|
|
67
|
+
en: 'Severe errors detected — auto-run disabled. Fix manually then:',
|
|
68
|
+
},
|
|
69
|
+
'compose.auto_running': {
|
|
70
|
+
zh: '开始执行工作流...',
|
|
71
|
+
en: 'Running workflow...',
|
|
72
|
+
},
|
|
73
|
+
'compose.preview': {
|
|
74
|
+
zh: '预览:',
|
|
75
|
+
en: 'Preview:',
|
|
76
|
+
},
|
|
77
|
+
'compose.next_steps': {
|
|
78
|
+
zh: '接下来可以:',
|
|
79
|
+
en: 'Next steps:',
|
|
80
|
+
},
|
|
81
|
+
'compose.next.validate': {
|
|
82
|
+
zh: '校验工作流',
|
|
83
|
+
en: 'validate the workflow',
|
|
84
|
+
},
|
|
85
|
+
'compose.next.plan': {
|
|
86
|
+
zh: '查看执行计划',
|
|
87
|
+
en: 'view execution plan',
|
|
88
|
+
},
|
|
89
|
+
'compose.next.run': {
|
|
90
|
+
zh: '运行工作流',
|
|
91
|
+
en: 'run the workflow',
|
|
92
|
+
},
|
|
93
|
+
'stream.received': {
|
|
94
|
+
zh: '📡 已接收 {size}KB...',
|
|
95
|
+
en: '📡 Received {size}KB...',
|
|
96
|
+
},
|
|
97
|
+
'error.prefix': {
|
|
98
|
+
zh: '错误',
|
|
99
|
+
en: 'Error',
|
|
100
|
+
},
|
|
101
|
+
'validate.usage': {
|
|
102
|
+
zh: '用法: ao validate <workflow.yaml>',
|
|
103
|
+
en: 'Usage: ao validate <workflow.yaml>',
|
|
104
|
+
},
|
|
105
|
+
'validate.ok': {
|
|
106
|
+
zh: '{name} — 校验通过',
|
|
107
|
+
en: '{name} — validation passed',
|
|
108
|
+
},
|
|
109
|
+
'validate.stats': {
|
|
110
|
+
zh: '{steps} 个步骤, {inputs} 个输入',
|
|
111
|
+
en: '{steps} step(s), {inputs} input(s)',
|
|
112
|
+
},
|
|
113
|
+
'validate.failed': {
|
|
114
|
+
zh: '{name} — 校验失败:',
|
|
115
|
+
en: '{name} — validation failed:',
|
|
116
|
+
},
|
|
117
|
+
'plan.usage': {
|
|
118
|
+
zh: '用法: ao plan <workflow.yaml>',
|
|
119
|
+
en: 'Usage: ao plan <workflow.yaml>',
|
|
120
|
+
},
|
|
121
|
+
'plan.validate_failed': {
|
|
122
|
+
zh: '校验失败:',
|
|
123
|
+
en: 'Validation failed:',
|
|
124
|
+
},
|
|
125
|
+
'parse.bad_yaml': { zh: '工作流文件格式错误: {path}', en: 'Invalid workflow YAML: {path}' },
|
|
126
|
+
'parse.missing_name': { zh: '工作流缺少 name 字段', en: 'Workflow is missing the `name` field' },
|
|
127
|
+
'parse.missing_steps': { zh: '工作流缺少 steps 或 steps 为空', en: 'Workflow is missing `steps` or `steps` is empty' },
|
|
128
|
+
'parse.missing_llm': { zh: '工作流缺少 llm 配置', en: 'Workflow is missing the `llm` block' },
|
|
129
|
+
'parse.missing_provider': { zh: 'llm 配置缺少 provider', en: '`llm.provider` is required' },
|
|
130
|
+
'parse.missing_model': { zh: 'llm 配置缺少 model(CLI provider 可省略)', en: '`llm.model` is required (optional for CLI providers)' },
|
|
131
|
+
'parse.missing_step_id': { zh: 'step 缺少 id', en: 'Each step needs an `id`' },
|
|
132
|
+
'dag.title': { zh: '执行计划:', en: 'Execution plan:' },
|
|
133
|
+
'dag.layer': { zh: '第{n}层', en: 'Layer {n}' },
|
|
134
|
+
'dag.parallel': { zh: ' (并行)', en: ' (parallel)' },
|
|
135
|
+
'dag.deps': { zh: '依赖', en: 'depends on' },
|
|
136
|
+
'dag.condition': { zh: '条件', en: 'condition' },
|
|
137
|
+
'dag.loop': { zh: '循环: → {to} (最多 {n} 次)', en: 'loop: → {to} (max {n} iter)' },
|
|
138
|
+
'help.text': {
|
|
139
|
+
zh: `
|
|
140
|
+
agency-orchestrator — 多智能体工作流引擎
|
|
141
|
+
基于 agency-agents-zh 的多智能体编排引擎
|
|
142
|
+
|
|
143
|
+
快速开始:
|
|
144
|
+
ao demo 零配置体验多智能体协作
|
|
145
|
+
ao init 下载 AI 角色定义(中文)
|
|
146
|
+
ao init --lang en 下载英文角色定义
|
|
147
|
+
ao roles 查看所有可用角色
|
|
148
|
+
ao plan <workflow.yaml> 查看执行计划 (DAG)
|
|
149
|
+
ao run <workflow.yaml> [options] 执行工作流
|
|
150
|
+
|
|
151
|
+
命令:
|
|
152
|
+
demo 零配置体验多智能体协作(mock + 真实 AI)
|
|
153
|
+
init 下载/更新角色库 (--lang en 下载英文版)
|
|
154
|
+
init --workflow 交互式创建新工作流
|
|
155
|
+
compose "描述" AI 智能编排工作流(一句话生成 YAML)
|
|
156
|
+
compose "描述" --run 生成并立即运行(一句话出结果)
|
|
157
|
+
serve 启动 MCP Server(供 Claude Code / Cursor 调用)
|
|
158
|
+
run <workflow.yaml> 执行工作流
|
|
159
|
+
validate <workflow.yaml> 校验工作流定义
|
|
160
|
+
plan <workflow.yaml> 查看执行计划
|
|
161
|
+
explain <workflow.yaml> 用自然语言解释执行计划
|
|
162
|
+
roles [--agents-dir path] 列出可用角色
|
|
163
|
+
|
|
164
|
+
选项:
|
|
165
|
+
--input, -i key=value 传入输入变量
|
|
166
|
+
--input, -i key=@file 从文件读取变量值
|
|
167
|
+
--provider <name> 覆盖 YAML 中的 LLM provider (如 claude-code, deepseek)
|
|
168
|
+
--model <name> 覆盖 YAML 中的模型名
|
|
169
|
+
--output dir 输出目录 (默认 ao-output/)
|
|
170
|
+
--resume <dir|last> 从上次运行恢复(加载已完成步骤的输出)
|
|
171
|
+
--from <step-id> 配合 --resume,从指定步骤重新执行
|
|
172
|
+
--watch 实时进度显示(终端 UI)
|
|
173
|
+
--quiet, -q 静默模式
|
|
174
|
+
--lang <zh|en> 界面语言(默认根据系统 LANG 检测,也可用 AO_LANG 环境变量)
|
|
175
|
+
--version, -v 版本号
|
|
176
|
+
|
|
177
|
+
示例:
|
|
178
|
+
ao init
|
|
179
|
+
ao compose "PR 代码审查,覆盖安全和性能"
|
|
180
|
+
ao run workflows/story-creation.yaml -i premise='一个时间旅行的故事' -i style='悬疑'
|
|
181
|
+
ao run workflows/product-review.yaml -i prd_content=@prd.md
|
|
182
|
+
ao plan workflows/content-pipeline.yaml
|
|
183
|
+
|
|
184
|
+
Resume (基于上次结果迭代):
|
|
185
|
+
ao run workflow.yaml --resume last # 跳过上次已完成的步骤
|
|
186
|
+
ao run workflow.yaml --resume last --from summary # 从 summary 步骤重新执行
|
|
187
|
+
ao run workflow.yaml --resume ao-output/xxx/ # 指定具体输出目录
|
|
188
|
+
|
|
189
|
+
Agents (中文): https://github.com/jnMetaCode/agency-agents-zh
|
|
190
|
+
Agents (英文): https://github.com/msitarzewski/agency-agents
|
|
191
|
+
`,
|
|
192
|
+
en: `
|
|
193
|
+
agency-orchestrator — Multi-Agent Workflow Engine
|
|
194
|
+
YAML-defined workflows · 170+ English roles · auto DAG parallelism
|
|
195
|
+
|
|
196
|
+
Quick Start:
|
|
197
|
+
ao demo Zero-config multi-agent demo
|
|
198
|
+
ao init --lang en Download English AI roles
|
|
199
|
+
ao roles List all available roles
|
|
200
|
+
ao plan <workflow.yaml> Show execution plan (DAG)
|
|
201
|
+
ao run <workflow.yaml> [options] Execute a workflow
|
|
202
|
+
|
|
203
|
+
Commands:
|
|
204
|
+
demo Zero-config demo (mock + real AI)
|
|
205
|
+
init Download/update role library (--lang en for English)
|
|
206
|
+
init --workflow Interactively create a new workflow
|
|
207
|
+
compose "desc" AI-compose a workflow from one sentence
|
|
208
|
+
compose "desc" --run Compose and run immediately
|
|
209
|
+
serve Start MCP server (for Claude Code / Cursor)
|
|
210
|
+
run <workflow.yaml> Execute a workflow
|
|
211
|
+
validate <workflow.yaml> Validate a workflow definition
|
|
212
|
+
plan <workflow.yaml> Show execution plan
|
|
213
|
+
explain <workflow.yaml> Explain the plan in natural language
|
|
214
|
+
roles [--agents-dir path] List available roles
|
|
215
|
+
|
|
216
|
+
Options:
|
|
217
|
+
--input, -i key=value Pass an input variable
|
|
218
|
+
--input, -i key=@file Read variable value from a file
|
|
219
|
+
--provider <name> Override LLM provider (e.g. claude-code, deepseek)
|
|
220
|
+
--model <name> Override model name from YAML
|
|
221
|
+
--output dir Output directory (default: ao-output/)
|
|
222
|
+
--resume <dir|last> Resume from previous run (reuse completed step outputs)
|
|
223
|
+
--from <step-id> With --resume, re-run starting from this step
|
|
224
|
+
--watch Live progress display (terminal UI)
|
|
225
|
+
--quiet, -q Quiet mode
|
|
226
|
+
--lang <zh|en> Interface language (auto-detected from LANG; also AO_LANG env)
|
|
227
|
+
--version, -v Show version
|
|
228
|
+
|
|
229
|
+
Examples:
|
|
230
|
+
ao init --lang en
|
|
231
|
+
ao compose "PR code review covering security and performance" --provider claude-code
|
|
232
|
+
ao run workflows/story-creation.yaml -i premise='A time-travel story' -i style='mystery'
|
|
233
|
+
ao run workflows/product-review.yaml -i prd_content=@prd.md
|
|
234
|
+
ao plan workflows/content-pipeline.yaml
|
|
235
|
+
|
|
236
|
+
Resume (iterate on previous results):
|
|
237
|
+
ao run workflow.yaml --resume last # Skip completed steps
|
|
238
|
+
ao run workflow.yaml --resume last --from summary # Re-run from the summary step
|
|
239
|
+
ao run workflow.yaml --resume ao-output/xxx/ # Point at a specific output dir
|
|
240
|
+
|
|
241
|
+
Agents (Chinese): https://github.com/jnMetaCode/agency-agents-zh
|
|
242
|
+
Agents (English): https://github.com/msitarzewski/agency-agents
|
|
243
|
+
`,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
export function t(key, params = {}) {
|
|
247
|
+
const lang = detectLang();
|
|
248
|
+
const entry = dict[key];
|
|
249
|
+
if (!entry)
|
|
250
|
+
return String(key);
|
|
251
|
+
let s = entry[lang];
|
|
252
|
+
for (const [k, v] of Object.entries(params)) {
|
|
253
|
+
s = s.replace(new RegExp(`\\{${k}\\}`, 'g'), String(v));
|
|
254
|
+
}
|
|
255
|
+
return s;
|
|
256
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agency-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Multi-agent YAML workflow engine — 211 AI roles, auto DAG parallelism, zero code. One sentence → multiple AI roles collaborate → complete plan in minutes. 10 LLM providers, 7 need no API key.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"multi-agent",
|