c0de-agent 1.2.0 → 1.3.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.
@@ -0,0 +1,232 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join, relative, resolve } from 'node:path';
3
+ /** SubAgentResult → WorkflowAgentResult 映射。 */
4
+ function mapResult(result) {
5
+ if (result._tag === 'success') {
6
+ return { ok: true, output: result.output, data: result.data };
7
+ }
8
+ if (result._tag === 'error') {
9
+ return { ok: false, error: result.error };
10
+ }
11
+ return { ok: false, error: 'subagent returned running (background not supported in workflows)' };
12
+ }
13
+ /** 构建 WorkflowContext,注入 runSubagent/utils/progress。 */
14
+ function buildWorkflowContext(opts) {
15
+ const { deps, parent, args, onProgress, projectName, runSubAgentFn } = opts;
16
+ const rootDir = deps.cwd;
17
+ // 默认 runSubAgent:通过动态 import 避免循环依赖
18
+ const doRunSubAgent = runSubAgentFn ??
19
+ (async (request) => {
20
+ const { runSubAgent } = await import('../loop.js');
21
+ return runSubAgent(deps, parent, request);
22
+ });
23
+ return {
24
+ project: {
25
+ rootDir,
26
+ name: projectName ?? 'project',
27
+ },
28
+ args,
29
+ runSubagent: async (type, params) => {
30
+ const result = await doRunSubAgent({
31
+ agentType: type,
32
+ prompt: params.assignment,
33
+ description: params.description,
34
+ model: params.model,
35
+ });
36
+ return mapResult(result);
37
+ },
38
+ runSubagents: async (type, tasks, context) => {
39
+ const { mapWithConcurrencyLimit } = await import('../agents/parallel.js');
40
+ const concurrency = 3;
41
+ const { results } = await mapWithConcurrencyLimit(tasks, concurrency, async (task) => {
42
+ try {
43
+ const result = await doRunSubAgent({
44
+ agentType: type,
45
+ prompt: task.assignment,
46
+ description: task.description,
47
+ role: task.role,
48
+ context,
49
+ });
50
+ return mapResult(result);
51
+ }
52
+ catch (e) {
53
+ // 隔离单个任务的异常:不向上抛,避免 mapWithConcurrencyLimit 的
54
+ // fail-fast 终止所有尚未启动的兄弟任务。按合约返回 per-task { ok: false }。
55
+ return {
56
+ ok: false,
57
+ error: e instanceof Error ? e.message : String(e),
58
+ };
59
+ }
60
+ });
61
+ return results.filter((r) => r !== undefined);
62
+ },
63
+ progress: onProgress,
64
+ utils: {
65
+ glob: async (pattern) => {
66
+ return globRecursive(rootDir, pattern);
67
+ },
68
+ grep: async (pattern, searchPath) => {
69
+ const baseDir = searchPath ? resolve(rootDir, searchPath) : rootDir;
70
+ return grepRecursive(baseDir, pattern, rootDir);
71
+ },
72
+ read: async (filePath, range) => {
73
+ const absPath = resolve(rootDir, filePath);
74
+ const content = await readFile(absPath, 'utf-8');
75
+ if (!range)
76
+ return content;
77
+ const lines = content.split('\n');
78
+ return lines.slice(range.start - 1, range.end).join('\n');
79
+ },
80
+ splitByDirectory: async (dir, opts) => {
81
+ return splitByDir(resolve(rootDir, dir), opts?.depth ?? 1, opts?.ignore ?? []);
82
+ },
83
+ },
84
+ };
85
+ }
86
+ // ── 工具函数 ──
87
+ /** 递归 glob(简单实现,匹配文件名后缀或通配符)。 */
88
+ async function globRecursive(rootDir, pattern) {
89
+ const results = [];
90
+ const regex = new RegExp(pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'));
91
+ async function walk(dir) {
92
+ let entries;
93
+ try {
94
+ entries = await readdir(dir, { withFileTypes: true });
95
+ }
96
+ catch {
97
+ return;
98
+ }
99
+ for (const entry of entries) {
100
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
101
+ continue;
102
+ const fullPath = join(dir, entry.name);
103
+ if (entry.isDirectory()) {
104
+ await walk(fullPath);
105
+ }
106
+ else if (regex.test(entry.name)) {
107
+ results.push(relative(rootDir, fullPath));
108
+ }
109
+ }
110
+ }
111
+ await walk(rootDir);
112
+ return results;
113
+ }
114
+ /** 递归 grep(正则搜索文件内容)。 */
115
+ async function grepRecursive(baseDir, pattern, rootDir) {
116
+ const results = [];
117
+ let regex;
118
+ try {
119
+ regex = new RegExp(pattern);
120
+ }
121
+ catch {
122
+ regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
123
+ }
124
+ async function walk(dir) {
125
+ let entries;
126
+ try {
127
+ entries = await readdir(dir, { withFileTypes: true });
128
+ }
129
+ catch {
130
+ return;
131
+ }
132
+ for (const entry of entries) {
133
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
134
+ continue;
135
+ const fullPath = join(dir, entry.name);
136
+ if (entry.isDirectory()) {
137
+ await walk(fullPath);
138
+ }
139
+ else {
140
+ try {
141
+ const content = await readFile(fullPath, 'utf-8');
142
+ const lines = content.split('\n');
143
+ for (let i = 0; i < lines.length; i++) {
144
+ const line = lines[i];
145
+ if (line && regex.test(line)) {
146
+ results.push({
147
+ path: relative(rootDir, fullPath),
148
+ line: i + 1,
149
+ text: line.trim(),
150
+ });
151
+ }
152
+ }
153
+ }
154
+ catch {
155
+ // 二进制文件等,跳过
156
+ }
157
+ }
158
+ }
159
+ }
160
+ await walk(baseDir);
161
+ return results;
162
+ }
163
+ /**
164
+ * 按目录拆分模块。depth=N 时从 rootDir 向下走 N 层,每棵深度为 N 的子目录成为一个模块;
165
+ * 深度不足 N 的叶子目录(没有子目录)也成为一个模块,避免被跳过。
166
+ * 模块名 = 相对 rootDir 的路径(如 "src/a");rootDir 自身成为模块时命名为 "root"。
167
+ */
168
+ async function splitByDir(rootDir, depth, ignore) {
169
+ const modules = [];
170
+ async function collectFiles(dir) {
171
+ const files = [];
172
+ let entries;
173
+ try {
174
+ entries = await readdir(dir, { withFileTypes: true });
175
+ }
176
+ catch {
177
+ return files;
178
+ }
179
+ for (const entry of entries) {
180
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
181
+ continue;
182
+ if (ignore.includes(entry.name))
183
+ continue;
184
+ const fullPath = join(dir, entry.name);
185
+ if (entry.isDirectory()) {
186
+ files.push(...(await collectFiles(fullPath)));
187
+ }
188
+ else {
189
+ files.push(relative(rootDir, fullPath));
190
+ }
191
+ }
192
+ return files;
193
+ }
194
+ async function readSubdirs(dir) {
195
+ let entries;
196
+ try {
197
+ entries = await readdir(dir, { withFileTypes: true });
198
+ }
199
+ catch {
200
+ return [];
201
+ }
202
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith('.') && !ignore.includes(e.name));
203
+ }
204
+ async function pushModule(dir) {
205
+ const rel = relative(rootDir, dir);
206
+ modules.push({
207
+ name: rel === '' ? 'root' : rel,
208
+ path: dir,
209
+ files: await collectFiles(dir),
210
+ });
211
+ }
212
+ async function collectModules(currentDir, currentDepth) {
213
+ // 到达目标深度:当前目录成为模块
214
+ if (currentDepth >= depth) {
215
+ await pushModule(currentDir);
216
+ return;
217
+ }
218
+ // 未到达目标深度:继续向下走
219
+ const subdirs = await readSubdirs(currentDir);
220
+ // 深度不足 N 的叶子目录(无子目录):成为模块,避免被跳过
221
+ if (subdirs.length === 0) {
222
+ await pushModule(currentDir);
223
+ return;
224
+ }
225
+ for (const subdir of subdirs) {
226
+ await collectModules(join(currentDir, subdir.name), currentDepth + 1);
227
+ }
228
+ }
229
+ await collectModules(rootDir, 0);
230
+ return modules;
231
+ }
232
+ export { buildWorkflowContext };
@@ -0,0 +1,11 @@
1
+ import type { WorkflowEntry } from './types.js';
2
+ /**
3
+ * 扫描项目目录下的 `.c0de/workflows/*.js` 文件,source 标记为 'project'。
4
+ */
5
+ declare function discoverWorkflows(projectDir: string): Promise<WorkflowEntry[]>;
6
+ /**
7
+ * 扫描全局 `~/.c0de/workflows/*.js` 文件,source 标记为 'user'。
8
+ * 目录不存在时返回空数组(与项目级一致)。
9
+ */
10
+ declare function discoverGlobalWorkflows(): Promise<WorkflowEntry[]>;
11
+ export { discoverGlobalWorkflows, discoverWorkflows };
@@ -0,0 +1,69 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { basename, join } from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
5
+ /** 项目级工作流目录相对路径。 */
6
+ const PROJECT_WORKFLOWS_DIR = '.c0de/workflows';
7
+ /** 用户级(全局)工作流目录:~/.c0de/workflows。 */
8
+ const GLOBAL_WORKFLOWS_DIR = join('.c0de', 'workflows');
9
+ /**
10
+ * 扫描指定目录下的 `*.js` 工作流文件,dynamic import 后转为 WorkflowEntry。
11
+ * import 失败的文件跳过(warn),不阻塞其他工作流加载。
12
+ *
13
+ * 由 discoverWorkflows(projectDir)与 discoverGlobalWorkflows(~/.c0de/workflows)复用,
14
+ * 仅 dirPath 与 source 字段不同。
15
+ */
16
+ async function discoverFromDir(dirPath, source) {
17
+ const entries = [];
18
+ let files;
19
+ try {
20
+ const dirents = await readdir(dirPath);
21
+ files = dirents.filter((f) => f.endsWith('.js'));
22
+ }
23
+ catch {
24
+ // 目录不存在或不可读 → 返回空
25
+ return entries;
26
+ }
27
+ for (const file of files) {
28
+ const filePath = join(dirPath, file);
29
+ try {
30
+ const sourceCode = await readFile(filePath, 'utf-8');
31
+ const fileUrl = pathToFileURL(filePath).href;
32
+ const mod = (await import(fileUrl));
33
+ if (!mod.meta || typeof mod.default !== 'function') {
34
+ console.warn(`[workflow] skipping ${file}: missing meta or default export`);
35
+ continue;
36
+ }
37
+ // meta.name 缺省时取文件名(去 .js)
38
+ const meta = {
39
+ ...mod.meta,
40
+ name: mod.meta.name ?? basename(file, '.js'),
41
+ };
42
+ entries.push({
43
+ meta,
44
+ source,
45
+ filePath,
46
+ sourceCode,
47
+ execute: mod.default,
48
+ });
49
+ }
50
+ catch (e) {
51
+ console.warn(`[workflow] failed to load ${file}: ${e instanceof Error ? e.message : e}`);
52
+ }
53
+ }
54
+ return entries;
55
+ }
56
+ /**
57
+ * 扫描项目目录下的 `.c0de/workflows/*.js` 文件,source 标记为 'project'。
58
+ */
59
+ async function discoverWorkflows(projectDir) {
60
+ return discoverFromDir(join(projectDir, PROJECT_WORKFLOWS_DIR), 'project');
61
+ }
62
+ /**
63
+ * 扫描全局 `~/.c0de/workflows/*.js` 文件,source 标记为 'user'。
64
+ * 目录不存在时返回空数组(与项目级一致)。
65
+ */
66
+ async function discoverGlobalWorkflows() {
67
+ return discoverFromDir(join(homedir(), GLOBAL_WORKFLOWS_DIR), 'user');
68
+ }
69
+ export { discoverGlobalWorkflows, discoverWorkflows };
@@ -0,0 +1,7 @@
1
+ export { BUILTIN_WORKFLOWS, createBuiltinWorkflows } from './builtins.js';
2
+ export { buildWorkflowContext } from './context.js';
3
+ export { discoverGlobalWorkflows, discoverWorkflows } from './discovery.js';
4
+ export type { WorkflowRegistry } from './registry.js';
5
+ export { createAndPopulateRegistry, createWorkflowRegistry } from './registry.js';
6
+ export { executeWorkflow } from './runtime.js';
7
+ export type { WorkflowAgentResult, WorkflowContext, WorkflowEntry, WorkflowMeta, WorkflowModule, WorkflowResult, WorkflowSource, WorkflowUtils, } from './types.js';
@@ -0,0 +1,5 @@
1
+ export { BUILTIN_WORKFLOWS, createBuiltinWorkflows } from './builtins.js';
2
+ export { buildWorkflowContext } from './context.js';
3
+ export { discoverGlobalWorkflows, discoverWorkflows } from './discovery.js';
4
+ export { createAndPopulateRegistry, createWorkflowRegistry } from './registry.js';
5
+ export { executeWorkflow } from './runtime.js';
@@ -0,0 +1,20 @@
1
+ import type { WorkflowEntry } from './types.js';
2
+ /** 工作流注册表:内存 Map<name, WorkflowEntry>,后注册覆盖同名。 */
3
+ declare function createWorkflowRegistry(): {
4
+ register(entry: WorkflowEntry): void;
5
+ get(name: string): WorkflowEntry | undefined;
6
+ list(): WorkflowEntry[];
7
+ has(name: string): boolean;
8
+ delete(name: string): boolean;
9
+ };
10
+ type WorkflowRegistry = ReturnType<typeof createWorkflowRegistry>;
11
+ /**
12
+ * 创建并填充工作流注册表(三级发现,后注册覆盖同名):
13
+ * 1. 注册内置工作流(builtin)
14
+ * 2. 发现并注册全局 `~/.c0de/workflows/*.js`(user)
15
+ * 3. 发现并注册项目 `.c0de/workflows/*.js`(project)
16
+ * 覆盖优先级:project > user > builtin。
17
+ */
18
+ declare function createAndPopulateRegistry(projectDir: string): Promise<WorkflowRegistry>;
19
+ export type { WorkflowRegistry };
20
+ export { createAndPopulateRegistry, createWorkflowRegistry };
@@ -0,0 +1,49 @@
1
+ import { createBuiltinWorkflows } from './builtins.js';
2
+ import { discoverGlobalWorkflows, discoverWorkflows } from './discovery.js';
3
+ /** 工作流注册表:内存 Map<name, WorkflowEntry>,后注册覆盖同名。 */
4
+ function createWorkflowRegistry() {
5
+ const entries = new Map();
6
+ return {
7
+ register(entry) {
8
+ entries.set(entry.meta.name, entry);
9
+ },
10
+ get(name) {
11
+ return entries.get(name);
12
+ },
13
+ list() {
14
+ return Array.from(entries.values());
15
+ },
16
+ has(name) {
17
+ return entries.has(name);
18
+ },
19
+ delete(name) {
20
+ return entries.delete(name);
21
+ },
22
+ };
23
+ }
24
+ /**
25
+ * 创建并填充工作流注册表(三级发现,后注册覆盖同名):
26
+ * 1. 注册内置工作流(builtin)
27
+ * 2. 发现并注册全局 `~/.c0de/workflows/*.js`(user)
28
+ * 3. 发现并注册项目 `.c0de/workflows/*.js`(project)
29
+ * 覆盖优先级:project > user > builtin。
30
+ */
31
+ async function createAndPopulateRegistry(projectDir) {
32
+ const registry = createWorkflowRegistry();
33
+ // 1. 内置(由源码字符串动态导入生成,show === run)
34
+ for (const wf of await createBuiltinWorkflows()) {
35
+ registry.register(wf);
36
+ }
37
+ // 2. 全局级(~/.c0de/workflows)
38
+ const globalWorkflows = await discoverGlobalWorkflows();
39
+ for (const wf of globalWorkflows) {
40
+ registry.register(wf);
41
+ }
42
+ // 3. 项目级(.c0de/workflows)
43
+ const projectWorkflows = await discoverWorkflows(projectDir);
44
+ for (const wf of projectWorkflows) {
45
+ registry.register(wf);
46
+ }
47
+ return registry;
48
+ }
49
+ export { createAndPopulateRegistry, createWorkflowRegistry };
@@ -0,0 +1,18 @@
1
+ import type { AgentDependencies, AgentState, CommandResult } from '../types.js';
2
+ import type { WorkflowRegistry } from './registry.js';
3
+ /** executeWorkflow 的参数。 */
4
+ type ExecuteWorkflowOpts = {
5
+ registry: WorkflowRegistry;
6
+ name: string;
7
+ args: string;
8
+ deps: AgentDependencies;
9
+ parent: AgentState;
10
+ onProgress?: (message: string, detail?: unknown) => void;
11
+ };
12
+ /**
13
+ * 执行工作流:查注册表 → 构建 ctx → 调用 entry.execute → 返回 CommandResult。
14
+ *
15
+ * 工作流 return 的 output 作为 text 返回;异常捕获为 error。
16
+ */
17
+ declare function executeWorkflow(opts: ExecuteWorkflowOpts): Promise<CommandResult>;
18
+ export { executeWorkflow };
@@ -0,0 +1,70 @@
1
+ import { buildWorkflowContext } from './context.js';
2
+ /** 工作流超时错误(用于 Promise.race 中识别超时)。 */
3
+ class WorkflowTimeoutError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = 'WorkflowTimeoutError';
7
+ }
8
+ }
9
+ /** 等待 ms 后 reject,timer.unref() 保证不阻塞 Node 退出。 */
10
+ function createTimeoutPromise(timeoutMs, timeoutSeconds) {
11
+ return new Promise((_, reject) => {
12
+ const timer = setTimeout(() => reject(new WorkflowTimeoutError(`timed out after ${timeoutSeconds}s`)), timeoutMs);
13
+ if (typeof timer === 'object' && 'unref' in timer)
14
+ timer.unref();
15
+ });
16
+ }
17
+ /**
18
+ * 执行工作流:查注册表 → 构建 ctx → 调用 entry.execute → 返回 CommandResult。
19
+ *
20
+ * 工作流 return 的 output 作为 text 返回;异常捕获为 error。
21
+ */
22
+ async function executeWorkflow(opts) {
23
+ const { registry, name, args, deps, parent, onProgress } = opts;
24
+ const entry = registry.get(name);
25
+ if (!entry) {
26
+ const available = registry
27
+ .list()
28
+ .map((e) => e.meta.name)
29
+ .join(', ');
30
+ return {
31
+ _tag: 'error',
32
+ message: `Unknown workflow: "${name}". Available: ${available || '(none)'}`,
33
+ };
34
+ }
35
+ const ctx = buildWorkflowContext({
36
+ deps,
37
+ parent,
38
+ args,
39
+ onProgress: onProgress ?? (() => { }),
40
+ });
41
+ const { timeout } = entry.meta;
42
+ const hasTimeout = typeof timeout === 'number' && timeout > 0;
43
+ const timeoutSeconds = hasTimeout ? timeout : undefined;
44
+ try {
45
+ const execPromise = entry.execute(ctx);
46
+ const result = timeoutSeconds !== undefined
47
+ ? await Promise.race([
48
+ execPromise,
49
+ createTimeoutPromise(timeoutSeconds * 1000, timeoutSeconds),
50
+ ])
51
+ : await execPromise;
52
+ return {
53
+ _tag: 'text',
54
+ text: result.output ?? 'Workflow completed (no output).',
55
+ };
56
+ }
57
+ catch (e) {
58
+ if (e instanceof WorkflowTimeoutError) {
59
+ return {
60
+ _tag: 'error',
61
+ message: `Workflow "${name}" timed out after ${timeoutSeconds}s`,
62
+ };
63
+ }
64
+ return {
65
+ _tag: 'error',
66
+ message: `Workflow "${name}" failed: ${e instanceof Error ? e.message : String(e)}`,
67
+ };
68
+ }
69
+ }
70
+ export { executeWorkflow };
@@ -0,0 +1,95 @@
1
+ /** 工作流元数据(脚本导出的 meta 对象)。 */
2
+ interface WorkflowMeta {
3
+ /** 唯一标识,同时也是 slash 命令名。 */
4
+ name: string;
5
+ /** 显示用描述。 */
6
+ description: string;
7
+ /** 参数提示(如 '[扫描目标描述]')。 */
8
+ argsHint?: string;
9
+ /** 执行阶段标签(用于进度展示)。 */
10
+ phases?: string[];
11
+ /** 超时(秒),超时终止。 */
12
+ timeout?: number;
13
+ }
14
+ /** 工作流执行结果。 */
15
+ type WorkflowResult = {
16
+ /** 人类可读总结(显示给用户)。 */
17
+ output?: string;
18
+ /** 结构化数据(存档/程序化消费)。 */
19
+ data?: unknown;
20
+ };
21
+ /** 子 agent 返回结果(区分成功/失败)。 */
22
+ type WorkflowAgentResult = {
23
+ ok: true;
24
+ output: string;
25
+ data?: unknown;
26
+ } | {
27
+ ok: false;
28
+ error: string;
29
+ };
30
+ /** 工作流内置工具集(受限文件系统操作)。 */
31
+ interface WorkflowUtils {
32
+ glob: (pattern: string) => Promise<string[]>;
33
+ grep: (pattern: string, path?: string) => Promise<Array<{
34
+ path: string;
35
+ line: number;
36
+ text: string;
37
+ }>>;
38
+ read: (filePath: string, range?: {
39
+ start: number;
40
+ end: number;
41
+ }) => Promise<string>;
42
+ splitByDirectory: (rootDir: string, opts?: {
43
+ depth?: number;
44
+ ignore?: string[];
45
+ }) => Promise<Array<{
46
+ name: string;
47
+ path: string;
48
+ files: string[];
49
+ }>>;
50
+ }
51
+ /** 工作流上下文(注入给脚本 default 函数的参数)。 */
52
+ interface WorkflowContext {
53
+ /** 项目信息。 */
54
+ project: {
55
+ rootDir: string;
56
+ name: string;
57
+ gitBranch?: string;
58
+ };
59
+ /** 用户传入的参数字符串。 */
60
+ args: string;
61
+ /** 派发单个子 agent。委托 runSubAgent。 */
62
+ runSubagent: (type: string, params: {
63
+ assignment: string;
64
+ description?: string;
65
+ model?: string;
66
+ }) => Promise<WorkflowAgentResult>;
67
+ /** 批量并行派发子 agent。委托 runSubAgent,concurrency pool。 */
68
+ runSubagents: (type: string, tasks: Array<{
69
+ assignment: string;
70
+ description?: string;
71
+ role?: string;
72
+ }>, context?: string) => Promise<WorkflowAgentResult[]>;
73
+ /** 进度上报(→ SSE → 前端)。 */
74
+ progress: (message: string, detail?: unknown) => void;
75
+ /** 内置工具。 */
76
+ utils: WorkflowUtils;
77
+ }
78
+ /** 工作流脚本模块(dynamic import 后的形状)。 */
79
+ interface WorkflowModule {
80
+ meta: WorkflowMeta;
81
+ default: (ctx: WorkflowContext) => Promise<WorkflowResult>;
82
+ }
83
+ /** 工作流来源层级(后注册覆盖同名:project > user > builtin)。 */
84
+ type WorkflowSource = 'builtin' | 'user' | 'project';
85
+ /** 注册表中的条目。 */
86
+ interface WorkflowEntry {
87
+ meta: WorkflowMeta;
88
+ source: WorkflowSource;
89
+ filePath?: string;
90
+ /** 执行器。 */
91
+ execute: (ctx: WorkflowContext) => Promise<WorkflowResult>;
92
+ /** 源码文本(show 命令和编辑用)。 */
93
+ sourceCode?: string;
94
+ }
95
+ export type { WorkflowAgentResult, WorkflowContext, WorkflowEntry, WorkflowMeta, WorkflowModule, WorkflowResult, WorkflowSource, WorkflowUtils, };
@@ -0,0 +1 @@
1
+ export {};
@@ -21,6 +21,7 @@ import { createSessionRoute } from './routes/session.js';
21
21
  import { createTerminalRoute } from './routes/terminal.js';
22
22
  import { createToolRoute } from './routes/tool.js';
23
23
  import { createUpdateRoute } from './routes/update.js';
24
+ import { createWorkflowsRoute } from './routes/workflows.js';
24
25
  /** 创建完整的 Hono 应用,挂载所有路由 + 中间件。 */
25
26
  function createApp(ctx) {
26
27
  const app = new Hono();
@@ -46,6 +47,7 @@ function createApp(ctx) {
46
47
  app.route('/api/permissions', createPermissionsRoute(ctx));
47
48
  app.route('/api/files', createFilesRoute(ctx));
48
49
  app.route('/api/terminal', createTerminalRoute(ctx));
50
+ app.route('/api/workflows', createWorkflowsRoute(ctx));
49
51
  // 根路径
50
52
  app.get('/', (c) => c.json({
51
53
  name: 'c0de-agent',
@@ -66,6 +68,7 @@ function createApp(ctx) {
66
68
  '/api/permissions',
67
69
  '/api/files',
68
70
  '/api/terminal',
71
+ '/api/workflows',
69
72
  ],
70
73
  }));
71
74
  // 静态文件服务(生产环境 dist-web/ 存在时启用)
@@ -1,6 +1,7 @@
1
1
  // src/server/context.ts
2
2
  import { BUILTIN_AGENTS, createAgentRegistry } from '../core/agents/index.js';
3
3
  import { DEFAULT_CONFIG, mergeConfig } from '../core/config.js';
4
+ import { BUILTIN_WORKFLOWS, createWorkflowRegistry } from '../core/workflows/index.js';
4
5
  import { createHookRunner, createPluginRegistry } from '../plugins/index.js';
5
6
  import { createDefaultRegistry, createDefaultURLRegistry } from '../tools/index.js';
6
7
  import { createUpdateScheduler } from '../update/index.js';
@@ -21,6 +22,7 @@ function createServerContext(opts) {
21
22
  reg.register(def);
22
23
  return reg;
23
24
  })();
25
+ let _workflowRegistry;
24
26
  return {
25
27
  db: opts.db,
26
28
  config,
@@ -33,6 +35,15 @@ function createServerContext(opts) {
33
35
  permissionStore: createPermissionStore(),
34
36
  permissionMode: config.permission.defaultMode,
35
37
  agentRegistry,
38
+ // 工作流注册表:惰性初始化,只含内置(项目级 discovery 由 bootstrap 或 API 触发热加载)。
39
+ get workflowRegistry() {
40
+ if (!_workflowRegistry) {
41
+ _workflowRegistry = createWorkflowRegistry();
42
+ for (const wf of BUILTIN_WORKFLOWS)
43
+ _workflowRegistry.register(wf);
44
+ }
45
+ return _workflowRegistry;
46
+ },
36
47
  // 测试上下文:默认 scheduler 不启动(enabled=false 由调用方控制)。
37
48
  updateScheduler: createUpdateScheduler({
38
49
  checkFn: async () => ({