@router-agent/runtime 0.0.0-stage → 0.3.32

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/dist/agents/claude.d.ts +51 -0
  2. package/dist/agents/claude.js +270 -0
  3. package/dist/agents/claude.js.map +1 -0
  4. package/dist/agents/codex.d.ts +36 -0
  5. package/dist/agents/codex.js +292 -0
  6. package/dist/agents/codex.js.map +1 -0
  7. package/dist/agents/profile.d.ts +39 -0
  8. package/dist/agents/profile.js +2 -0
  9. package/dist/agents/profile.js.map +1 -0
  10. package/dist/agents/registry.d.ts +21 -0
  11. package/dist/agents/registry.js +78 -0
  12. package/dist/agents/registry.js.map +1 -0
  13. package/dist/errors.d.ts +4 -0
  14. package/dist/errors.js +9 -0
  15. package/dist/errors.js.map +1 -0
  16. package/dist/evidence-gate.d.ts +78 -0
  17. package/dist/evidence-gate.js +196 -0
  18. package/dist/evidence-gate.js.map +1 -0
  19. package/dist/index.d.ts +22 -0
  20. package/dist/index.js +21 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/naming.d.ts +23 -0
  23. package/dist/naming.js +40 -0
  24. package/dist/naming.js.map +1 -0
  25. package/dist/permissions/acp.d.ts +21 -0
  26. package/dist/permissions/acp.js +73 -0
  27. package/dist/permissions/acp.js.map +1 -0
  28. package/dist/permissions.d.ts +1 -0
  29. package/dist/permissions.js +2 -0
  30. package/dist/permissions.js.map +1 -0
  31. package/dist/plugins/loader.d.ts +46 -0
  32. package/dist/plugins/loader.js +117 -0
  33. package/dist/plugins/loader.js.map +1 -0
  34. package/dist/plugins/types.d.ts +52 -0
  35. package/dist/plugins/types.js +2 -0
  36. package/dist/plugins/types.js.map +1 -0
  37. package/dist/port.d.ts +295 -0
  38. package/dist/port.js +17 -0
  39. package/dist/port.js.map +1 -0
  40. package/dist/registry.d.ts +13 -0
  41. package/dist/registry.js +35 -0
  42. package/dist/registry.js.map +1 -0
  43. package/dist/report-poller.d.ts +58 -0
  44. package/dist/report-poller.js +130 -0
  45. package/dist/report-poller.js.map +1 -0
  46. package/dist/resume.d.ts +52 -0
  47. package/dist/resume.js +109 -0
  48. package/dist/resume.js.map +1 -0
  49. package/dist/runtimes/acp-agents.d.ts +85 -0
  50. package/dist/runtimes/acp-agents.js +167 -0
  51. package/dist/runtimes/acp-agents.js.map +1 -0
  52. package/dist/runtimes/acp-detect-utils.d.ts +4 -0
  53. package/dist/runtimes/acp-detect-utils.js +35 -0
  54. package/dist/runtimes/acp-detect-utils.js.map +1 -0
  55. package/dist/runtimes/builtin-options.d.ts +3 -0
  56. package/dist/runtimes/builtin-options.js +44 -0
  57. package/dist/runtimes/builtin-options.js.map +1 -0
  58. package/dist/runtimes/catalog.d.ts +31 -0
  59. package/dist/runtimes/catalog.js +75 -0
  60. package/dist/runtimes/catalog.js.map +1 -0
  61. package/dist/runtimes/options.d.ts +60 -0
  62. package/dist/runtimes/options.js +89 -0
  63. package/dist/runtimes/options.js.map +1 -0
  64. package/dist/spec/contract.md +120 -0
  65. package/dist/spec/index.d.ts +35 -0
  66. package/dist/spec/index.js +59 -0
  67. package/dist/spec/index.js.map +1 -0
  68. package/dist/spec/prompt-mode.md +176 -0
  69. package/dist/spec/reader.d.ts +5 -0
  70. package/dist/spec/reader.js +32 -0
  71. package/dist/spec/reader.js.map +1 -0
  72. package/dist/status.d.ts +8 -0
  73. package/dist/status.js +22 -0
  74. package/dist/status.js.map +1 -0
  75. package/dist/workspaces/git-worktree.d.ts +44 -0
  76. package/dist/workspaces/git-worktree.js +183 -0
  77. package/dist/workspaces/git-worktree.js.map +1 -0
  78. package/dist/workspaces/overview.d.ts +66 -0
  79. package/dist/workspaces/overview.js +12 -0
  80. package/dist/workspaces/overview.js.map +1 -0
  81. package/package.json +36 -4
  82. package/README.md +0 -3
@@ -0,0 +1,35 @@
1
+ import { WorkerRuntimeError } from './port.js';
2
+ import { normalizeRuntimeId } from './runtimes/catalog.js';
3
+ /** 按 id 持有运行时;内核只通过它取运行时,不直接 new OrcaRuntime。 */
4
+ export class RuntimeRegistry {
5
+ runtimes;
6
+ defaultId;
7
+ constructor(runtimes, defaultId) {
8
+ this.runtimes = runtimes;
9
+ // 默认值可能来自旧配置(claude-cli),先归一再校验。
10
+ this.defaultId = normalizeRuntimeId(defaultId);
11
+ if (!runtimes.has(this.defaultId)) {
12
+ throw new WorkerRuntimeError('runtime:unknown', `默认 runtime 未注册: ${this.defaultId}`);
13
+ }
14
+ }
15
+ get(id) {
16
+ const runtime = this.runtimes.get(normalizeRuntimeId(id));
17
+ if (!runtime)
18
+ throw new WorkerRuntimeError('runtime:unknown', `未知 runtime: ${id}`);
19
+ return runtime;
20
+ }
21
+ has(id) {
22
+ return this.runtimes.has(normalizeRuntimeId(id));
23
+ }
24
+ ids() {
25
+ return [...this.runtimes.keys()];
26
+ }
27
+ default() {
28
+ return this.get(this.defaultId);
29
+ }
30
+ }
31
+ /** 用插件创建好的 adapter 组装注册表;插件发现与加载由 plugins/loader 负责。 */
32
+ export function createRuntimes(runtimes, defaultId) {
33
+ return new RuntimeRegistry(new Map([...runtimes].map(runtime => [runtime.id, runtime])), defaultId);
34
+ }
35
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../packages/runtime/src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAsB,MAAM,WAAW,CAAA;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,iDAAiD;AACjD,MAAM,OAAO,eAAe;IAIP;IAHF,SAAS,CAAQ;IAElC,YACmB,QAAoC,EACrD,SAAiB;QADA,aAAQ,GAAR,QAAQ,CAA4B;QAGrD,iCAAiC;QACjC,IAAI,CAAC,SAAS,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;QAC9C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,kBAAkB,CAAC,iBAAiB,EAAE,mBAAmB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QACtF,CAAC;IACH,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAA;QACzD,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,kBAAkB,CAAC,iBAAiB,EAAE,eAAe,EAAE,EAAE,CAAC,CAAA;QAClF,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAClC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACjC,CAAC;CACF;AAED,wDAAwD;AACxD,MAAM,UAAU,cAAc,CAAC,QAAiC,EAAE,SAAiB;IACjF,OAAO,IAAI,eAAe,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;AACrG,CAAC"}
@@ -0,0 +1,58 @@
1
+ import type { ReportState } from '@router-agent/core/model.js';
2
+ export interface WorkerStateFile {
3
+ ticketId: string;
4
+ assignmentId: string;
5
+ turn: number;
6
+ state: ReportState;
7
+ summary: string;
8
+ humanAction?: string;
9
+ prNumber: number;
10
+ prUrl: string;
11
+ /** Worker 显式声明是否改了 UI;缺省时由 Router 按改动文件推断。 */
12
+ uiChange?: boolean;
13
+ reply?: string;
14
+ ackedInputs: string[];
15
+ updatedAt: string;
16
+ }
17
+ export interface WorkerMeta {
18
+ ticketId: string;
19
+ assignmentId: string;
20
+ handle: string;
21
+ workdir: string;
22
+ /** 运行时 id;ACP / Claude Code 会写入。 */
23
+ runtime?: string;
24
+ /** 底层 Agent 的真实会话 id;用于人工恢复。 */
25
+ sessionId?: string;
26
+ repo?: string;
27
+ [key: string]: unknown;
28
+ }
29
+ export interface PolledState {
30
+ state: WorkerStateFile;
31
+ meta: WorkerMeta | null;
32
+ mtimeMs: number;
33
+ }
34
+ export interface ReportPollerOptions {
35
+ workersDir: string;
36
+ onState: (state: WorkerStateFile, meta: WorkerMeta | null, path: string) => void;
37
+ onError?: (ticketId: string, error: Error) => void;
38
+ }
39
+ /** 轮询 ~/.router-agent/workers/<ticketId>/state.json,按文件内容增量上报。 */
40
+ export declare class ReportPoller {
41
+ private workersDir;
42
+ private onState;
43
+ private onError?;
44
+ private seen;
45
+ constructor(opts: ReportPollerOptions);
46
+ /** 扫描一轮,返回新上报数量。 */
47
+ scan(): number;
48
+ /** 清空基线,让下一轮重新上报(Router 重启对账时用)。 */
49
+ reset(): void;
50
+ }
51
+ export declare function parseStateFile(content: string, expectedTicketId?: string): {
52
+ ticketId: string;
53
+ state: WorkerStateFile;
54
+ };
55
+ /** 读取 worker 的 meta.json;供报告轮询与恢复命令共用。 */
56
+ export declare function readWorkerMeta(workersDir: string, ticketId: string): WorkerMeta | null;
57
+ /** 追加一条审计事件到 events.log。 */
58
+ export declare function appendEvent(path: string, event: Record<string, unknown>): void;
@@ -0,0 +1,130 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ /** 旧版 state.json 的交付物字段,仅用于向后兼容,不再参与新协议。 */
5
+ const LegacyDeliverableSchema = z.object({
6
+ kind: z.string().optional(),
7
+ content: z.string().default(''),
8
+ title: z.string().optional(),
9
+ url: z.string().default(''),
10
+ }).nullable().optional();
11
+ const StateSchema = z.object({
12
+ ticket_id: z.string().min(1),
13
+ assignment_id: z.string().default(''),
14
+ turn: z.coerce.number().int().min(0).default(0),
15
+ state: z.enum(['progress', 'ready', 'blocked', 'failed', 'dropped', 'waiting']),
16
+ summary: z.string().min(1),
17
+ human_action: z.string().nullable().optional(),
18
+ pr_number: z.coerce.number().int().min(0).default(0),
19
+ pr_url: z.string().default(''),
20
+ /** Worker 显式声明是否改了 UI;缺省时由 Router 按改动文件推断。 */
21
+ ui_change: z.boolean().optional(),
22
+ /** Worker 要发给发起人的完整回复(Markdown)。 */
23
+ reply: z.string().optional().default(''),
24
+ /** 旧版交付物字段,读取时拼进 reply。 */
25
+ deliverable: LegacyDeliverableSchema,
26
+ acked_inputs: z.array(z.string()).default([]),
27
+ updated_at: z.string().default(() => new Date().toISOString()),
28
+ });
29
+ /** 轮询 ~/.router-agent/workers/<ticketId>/state.json,按文件内容增量上报。 */
30
+ export class ReportPoller {
31
+ workersDir;
32
+ onState;
33
+ onError;
34
+ seen = new Map();
35
+ constructor(opts) {
36
+ this.workersDir = opts.workersDir;
37
+ this.onState = opts.onState;
38
+ this.onError = opts.onError;
39
+ }
40
+ /** 扫描一轮,返回新上报数量。 */
41
+ scan() {
42
+ if (!existsSync(this.workersDir))
43
+ return 0;
44
+ let count = 0;
45
+ const entries = readdirSync(this.workersDir, { withFileTypes: true });
46
+ for (const entry of entries) {
47
+ if (!entry.isDirectory())
48
+ continue;
49
+ const ticketId = entry.name;
50
+ const statePath = join(this.workersDir, ticketId, 'state.json');
51
+ if (!existsSync(statePath))
52
+ continue;
53
+ try {
54
+ const content = readFileSync(statePath, 'utf8');
55
+ const fingerprint = `${statSync(statePath).mtimeMs}:${content}`;
56
+ if (this.seen.get(ticketId) === fingerprint)
57
+ continue;
58
+ const { ticketId: parsedId, state } = parseStateFile(content, ticketId);
59
+ this.seen.set(ticketId, fingerprint);
60
+ const meta = readWorkerMeta(this.workersDir, ticketId);
61
+ this.onState({ ...state, ticketId: parsedId }, meta, statePath);
62
+ count += 1;
63
+ }
64
+ catch (error) {
65
+ this.onError?.(ticketId, error instanceof Error ? error : new Error(String(error)));
66
+ }
67
+ }
68
+ return count;
69
+ }
70
+ /** 清空基线,让下一轮重新上报(Router 重启对账时用)。 */
71
+ reset() {
72
+ this.seen.clear();
73
+ }
74
+ }
75
+ export function parseStateFile(content, expectedTicketId) {
76
+ let json;
77
+ try {
78
+ json = JSON.parse(content);
79
+ }
80
+ catch {
81
+ throw new Error('state:invalid-json');
82
+ }
83
+ const parsed = StateSchema.safeParse(json);
84
+ if (!parsed.success)
85
+ throw new Error(`state:invalid: ${parsed.error.issues.map(i => i.path.join('.')).join(',')}`);
86
+ if (expectedTicketId && parsed.data.ticket_id !== expectedTicketId) {
87
+ throw new Error(`state:ticket-mismatch: 目录 ${expectedTicketId} 收到 ${parsed.data.ticket_id}`);
88
+ }
89
+ const data = parsed.data;
90
+ const legacy = data.deliverable;
91
+ // 旧版 deliverable 升级成纯回复:正文 + 链接,不再保留 kind。
92
+ const reply = data.reply.trim() || [legacy?.content?.trim(), legacy?.url?.trim()].filter(Boolean).join('\n\n');
93
+ return {
94
+ ticketId: data.ticket_id,
95
+ state: {
96
+ ticketId: data.ticket_id,
97
+ assignmentId: data.assignment_id,
98
+ turn: data.turn,
99
+ state: data.state,
100
+ summary: data.summary,
101
+ ...(data.human_action ? { humanAction: data.human_action } : {}),
102
+ prNumber: data.pr_number,
103
+ prUrl: data.pr_url,
104
+ ...(data.ui_change !== undefined ? { uiChange: data.ui_change } : {}),
105
+ ...(reply ? { reply } : {}),
106
+ ackedInputs: data.acked_inputs,
107
+ updatedAt: data.updated_at,
108
+ },
109
+ };
110
+ }
111
+ /** 读取 worker 的 meta.json;供报告轮询与恢复命令共用。 */
112
+ export function readWorkerMeta(workersDir, ticketId) {
113
+ const path = join(workersDir, ticketId, 'meta.json');
114
+ if (!existsSync(path))
115
+ return null;
116
+ try {
117
+ return JSON.parse(readFileSync(path, 'utf8'));
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ /** 追加一条审计事件到 events.log。 */
124
+ export function appendEvent(path, event) {
125
+ mkdirSync(join(path, '..'), { recursive: true });
126
+ if (!existsSync(path))
127
+ writeFileSync(path, '', 'utf8');
128
+ appendFileSync(path, `${JSON.stringify(event)}\n`, 'utf8');
129
+ }
130
+ //# sourceMappingURL=report-poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-poller.js","sourceRoot":"","sources":["../../../../packages/runtime/src/report-poller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,4CAA4C;AAC5C,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC5B,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAA;AAExB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9B,8CAA8C;IAC9C,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACjC,oCAAoC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACxC,2BAA2B;IAC3B,WAAW,EAAE,uBAAuB;IACpC,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;CAC/D,CAAC,CAAA;AA2CF,kEAAkE;AAClE,MAAM,OAAO,YAAY;IACf,UAAU,CAAQ;IAClB,OAAO,CAAgC;IACvC,OAAO,CAAiC;IACxC,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IAExC,YAAY,IAAyB;QACnC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;IAC7B,CAAC;IAED,oBAAoB;IACpB,IAAI;QACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,OAAO,CAAC,CAAA;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACrE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;gBAAE,SAAQ;YAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAA;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;YAC/D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,SAAQ;YACpC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;gBAC/C,MAAM,WAAW,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,IAAI,OAAO,EAAE,CAAA;gBAC/D,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW;oBAAE,SAAQ;gBACrD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;gBACvE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;gBACpC,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;gBACtD,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;gBAC/D,KAAK,IAAI,CAAC,CAAA;YACZ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACrF,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,oCAAoC;IACpC,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IACnB,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,gBAAyB;IACvE,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;IACvC,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAClH,IAAI,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,gBAAgB,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,6BAA6B,gBAAgB,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC9F,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA;IAC/B,2CAA2C;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC9G,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,SAAS;QACxB,KAAK,EAAE;YACL,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,YAAY,EAAE,IAAI,CAAC,aAAa;YAChC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,SAAS,EAAE,IAAI,CAAC,UAAU;SAC3B;KACF,CAAA;AACH,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,QAAgB;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAA;IACpD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAe,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAA8B;IACtE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;IACtD,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAC5D,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 人工恢复命令的拼装与运行时映射。
3
+ *
4
+ * 恢复命令只依赖三件事:工作目录、底层 Agent 的真实 session id、该 Agent 的 CLI
5
+ * 语法。这里用一张静态表把这些知识集中起来,不要求每个运行时实现额外方法。
6
+ * 只生成 POSIX shell(sh/bash/zsh)可执行的一行命令;动态值全部单引号转义。
7
+ */
8
+ import type { ResumeHint } from './port.js';
9
+ /** 单个运行时的 CLI 恢复模板。 */
10
+ export interface ResumeSpec {
11
+ /** 有真实 sessionId 时执行的命令。 */
12
+ command?: string;
13
+ /** 有真实 sessionId 时的参数模板,支持 {sessionId}。 */
14
+ args?: readonly string[];
15
+ /** 没有 sessionId、但有运行时 handle 时的备用命令;缺省复用 command。 */
16
+ handleCommand?: string;
17
+ /** 没有 sessionId、但有运行时 handle 时的备用模板,支持 {handle}。 */
18
+ handleArgs?: readonly string[];
19
+ /** handle 兜底命令是否需要在 workdir 里执行;默认 true。Orca 的终端切换与目录无关。 */
20
+ handleRequiresWorkdir?: boolean;
21
+ /** 有 sessionId 时附加的提示。 */
22
+ note?: string;
23
+ /** 走 handle 备用模板时附加的提示。 */
24
+ handleNote?: string;
25
+ }
26
+ /** 按运行时 id 取恢复模板;未知/未核实返回 null。 */
27
+ export declare function resumeSpecOf(runtimeId: string, agentId?: string): ResumeSpec | null;
28
+ export interface ResumeSource {
29
+ workdir?: string;
30
+ /** 底层 Agent 的真实会话 id;优先级最高。 */
31
+ sessionId?: string;
32
+ /** 运行时自己的短句柄;没有 sessionId 时用于 attach 类命令。 */
33
+ handle?: string;
34
+ /** 由外部决定用哪个 Agent 的运行时(如 Orca)需要它来选恢复命令。 */
35
+ agentId?: string;
36
+ }
37
+ /**
38
+ * 根据运行时与会话信息生成恢复命令。
39
+ *
40
+ * @param workdirExists 由调用方传入,保持本模块为纯函数;false 时追加工作区提示。
41
+ */
42
+ export declare function buildResumeHint(runtimeId: string, source: ResumeSource, opts?: {
43
+ workdirExists?: boolean;
44
+ }): ResumeHint | null;
45
+ /** 单引号转义;内部单引号写成 '\''。 */
46
+ export declare function shellQuote(value: string): string;
47
+ /**
48
+ * 生成 `cd <workdir> && <command> <args...>`。
49
+ *
50
+ * `command` 只应来自上面的可信常量,不作为用户输入参与转义;workdir 与参数一律转义。
51
+ */
52
+ export declare function resumeCommand(workdir: string, command: string, args?: readonly string[]): string;
package/dist/resume.js ADDED
@@ -0,0 +1,109 @@
1
+ import { CLAUDE_CODE_RUNTIME_ID, ORCA_RUNTIME_ID, normalizeRuntimeId } from './runtimes/catalog.js';
2
+ import { agentProfileOf } from './agents/registry.js';
3
+ /**
4
+ * 由 Agent 档案生成恢复模板:Agent 决定「用什么命令接回去」,
5
+ * 运行时只补充自己的兜底(handle 接管方式)。
6
+ */
7
+ function agentResumeSpec(agentId, extra = {}) {
8
+ const template = agentProfileOf(agentId)?.resume;
9
+ const note = [template?.note, extra.note].filter(Boolean).join(' ');
10
+ return {
11
+ ...(template ? { command: template.command, args: template.args } : {}),
12
+ ...extra,
13
+ ...(note ? { note } : {}),
14
+ };
15
+ }
16
+ /**
17
+ * runtime id → CLI 恢复模板。
18
+ *
19
+ * 只收录已核实 session id 语义和 CLI 语法的运行时;未收录的运行时不出按钮,
20
+ * 避免复制出打不开正确会话的命令。
21
+ */
22
+ const RESUME_SPECS = {
23
+ [CLAUDE_CODE_RUNTIME_ID]: agentResumeSpec('claude', {
24
+ handleArgs: ['attach', '{handle}'],
25
+ handleNote: '未读到 sessionId,改用 attach 接管后台会话。',
26
+ }),
27
+ 'acp-claude': agentResumeSpec('claude'),
28
+ 'acp-codex': agentResumeSpec('codex'),
29
+ 'codex-app': agentResumeSpec('codex', {
30
+ note: '也可以在 Codex 桌面端用 codex://threads/<threadId> 打开。',
31
+ }),
32
+ 'acp-opencode': agentResumeSpec('opencode'),
33
+ };
34
+ /**
35
+ * Orca 的恢复模板:用哪个 Agent 由配置决定,meta 里只有终端句柄。
36
+ *
37
+ * sessionId 由 service 按 workdir 从 Agent 自己的会话记录里补;
38
+ * 找不到会话记录时退回 Orca 的终端切换命令,作为最后的兜底。
39
+ */
40
+ function orcaResumeSpec(agentId) {
41
+ const base = {
42
+ handleCommand: 'orca',
43
+ handleArgs: ['terminal', 'switch', '--terminal', '{handle}'],
44
+ handleRequiresWorkdir: false,
45
+ handleNote: '未找到 Agent 会话记录,改为在 Orca 中切回原终端。',
46
+ };
47
+ if (!agentId)
48
+ return base;
49
+ return agentResumeSpec(agentId, {
50
+ ...base,
51
+ note: '按工作目录定位到的 Agent 会话恢复;接回的是普通终端会话,不再由 Orca 托管。',
52
+ });
53
+ }
54
+ /** 按运行时 id 取恢复模板;未知/未核实返回 null。 */
55
+ export function resumeSpecOf(runtimeId, agentId) {
56
+ const id = normalizeRuntimeId(runtimeId.trim());
57
+ if (id === ORCA_RUNTIME_ID)
58
+ return orcaResumeSpec(agentId);
59
+ return RESUME_SPECS[id] ?? null;
60
+ }
61
+ /**
62
+ * 根据运行时与会话信息生成恢复命令。
63
+ *
64
+ * @param workdirExists 由调用方传入,保持本模块为纯函数;false 时追加工作区提示。
65
+ */
66
+ export function buildResumeHint(runtimeId, source, opts = {}) {
67
+ const spec = resumeSpecOf(runtimeId, source.agentId);
68
+ if (!spec || !source.workdir)
69
+ return null;
70
+ const useSession = Boolean(spec.command && spec.args && source.sessionId);
71
+ // 没有单独的 handle 命令时,沿用 session 命令(例如 claude attach <handle>)。
72
+ const handleCommand = spec.handleCommand ?? spec.command;
73
+ const useHandle = !useSession && Boolean(handleCommand && spec.handleArgs && source.handle);
74
+ if (!useSession && !useHandle)
75
+ return null;
76
+ const template = (useSession ? spec.args : spec.handleArgs);
77
+ const args = template.map(arg => arg
78
+ .replaceAll('{sessionId}', source.sessionId ?? '')
79
+ .replaceAll('{handle}', source.handle ?? ''));
80
+ const command = useSession ? spec.command : handleCommand;
81
+ const requiresWorkdir = useSession || (spec.handleRequiresWorkdir ?? true);
82
+ const note = [
83
+ useSession ? spec.note : spec.handleNote,
84
+ opts.workdirExists === false ? '工作目录已不存在,请先恢复 worktree 再执行。' : undefined,
85
+ ].filter((item) => Boolean(item)).join(' ');
86
+ return {
87
+ command: requiresWorkdir ? resumeCommand(source.workdir, command, args) : commandLine(command, args),
88
+ ...(useSession && source.sessionId ? { sessionId: source.sessionId } : {}),
89
+ ...(note ? { note } : {}),
90
+ };
91
+ }
92
+ /** 单引号转义;内部单引号写成 '\''。 */
93
+ export function shellQuote(value) {
94
+ return `'${value.replace(/'/g, "'\\''")}'`;
95
+ }
96
+ /**
97
+ * 生成 `cd <workdir> && <command> <args...>`。
98
+ *
99
+ * `command` 只应来自上面的可信常量,不作为用户输入参与转义;workdir 与参数一律转义。
100
+ */
101
+ export function resumeCommand(workdir, command, args = []) {
102
+ return `cd ${shellQuote(workdir)} && ${commandLine(command, args)}`;
103
+ }
104
+ /** 不带 cd 的一行命令;参数逐个转义。 */
105
+ function commandLine(command, args = []) {
106
+ const tail = args.map(shellQuote).join(' ');
107
+ return `${command}${tail ? ` ${tail}` : ''}`;
108
+ }
109
+ //# sourceMappingURL=resume.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../../packages/runtime/src/resume.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * ACP Agent 静态目录。
3
+ *
4
+ * 这里只放纯常量与纯函数,不能依赖任何 Node 内置模块 —— Web 面板会把它
5
+ * 一起打进前端包。探测、安装、spawn 等 Node-only 逻辑放在 detect.ts / install.ts。
6
+ */
7
+ export interface AcpAgentRequirement {
8
+ /** 外部依赖命令,例如 codex。 */
9
+ command: string;
10
+ label: string;
11
+ /** 最低版本;仅作提示,真正兼容性以能力探测 / 完整握手为准。 */
12
+ minVersion?: string;
13
+ /** 能力探测参数,例如 ['app-server', '--help'];退出 0 视为具备该能力。 */
14
+ capabilityArgs?: readonly string[];
15
+ hint: string;
16
+ }
17
+ export interface AcpAgentMeta {
18
+ /** Agent 短 id,用于安装目录名(不能含冒号,Windows 文件名要合法)。 */
19
+ id: string;
20
+ /** 运行时 id,写入 runtime.default / runtime.projects / runtime.options。 */
21
+ runtimeId: string;
22
+ /** 产品名(自然大小写);Web 面板按首字母大写展示,CLI 与文档保持原样。 */
23
+ name: string;
24
+ label: string;
25
+ hint: string;
26
+ /** PATH 上的命令名;managed 安装时也是 node_modules/.bin 下的文件名。 */
27
+ command: string;
28
+ args: readonly string[];
29
+ /** 可由 ra 按需安装的 npm 包;缺省表示只能由用户自行安装。 */
30
+ package?: {
31
+ name: string;
32
+ version: string;
33
+ };
34
+ /** npm 包(ACP 扩展)的官方页面;Web 卡片用它跳转。 */
35
+ packageUrl?: string;
36
+ /** 底层 CLI;用于 Web 区分「安装 CLI」与「安装 ACP 扩展」。 */
37
+ cli?: {
38
+ command: string;
39
+ label: string;
40
+ installUrl: string;
41
+ };
42
+ /** 工具本身支持 ACP 时的官方安装页(没有独立 ACP 扩展)。 */
43
+ installUrl?: string;
44
+ /** 版本探测参数;默认 ['--version']。 */
45
+ probeArgs?: readonly string[];
46
+ /** 外部依赖检查。 */
47
+ requires?: readonly AcpAgentRequirement[];
48
+ /** 传给 Agent 的默认环境变量。 */
49
+ env?: Readonly<Record<string, string>>;
50
+ /** 登录命令(交互式);Web 只展示提示,不代跑。 */
51
+ login?: {
52
+ command: string;
53
+ args: readonly string[];
54
+ hint: string;
55
+ /** 非交互登录状态检查;退出 0 视为已登录。 */
56
+ check?: {
57
+ command: string;
58
+ args: readonly string[];
59
+ };
60
+ };
61
+ /** 无法在线安装时的提示。 */
62
+ installHint?: string;
63
+ /** 已测 / 文档声明的 session/load 能力。 */
64
+ loadSession?: boolean;
65
+ /** L0 版本探测不足以确认 ACP 模式,需要 `ra runtime check` 完整握手。 */
66
+ verifyRequired?: boolean;
67
+ /**
68
+ * 已有原生运行时替代品:Web 面板不再把它作为可选运行时展示,
69
+ * 但运行时 id、配置与已装扩展全部保留,老配置继续可用。
70
+ */
71
+ deprecated?: boolean;
72
+ }
73
+ export declare const ACP_AGENTS: readonly AcpAgentMeta[];
74
+ /** 按 agent id 或 runtime id 查目录项。 */
75
+ export declare function acpAgentOf(idOrRuntimeId: string): AcpAgentMeta | undefined;
76
+ /** 是否是目录内的动态 ACP 运行时 id(不含旧的通用 `acp`)。 */
77
+ export declare function isAcpRuntimeId(id: string): boolean;
78
+ /**
79
+ * 是否是已有原生运行时替代品的 ACP 运行时(Claude Code / Codex)。
80
+ *
81
+ * 这类运行时 id 依旧合法、依旧能派工,只是 Web 面板不再把它们列为可选项。
82
+ */
83
+ export declare function isDeprecatedAcpRuntimeId(id: string): boolean;
84
+ /** agent id → runtime id;未知 id 返回 null。 */
85
+ export declare function acpRuntimeIdOf(idOrRuntimeId: string): string | null;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * ACP Agent 静态目录。
3
+ *
4
+ * 这里只放纯常量与纯函数,不能依赖任何 Node 内置模块 —— Web 面板会把它
5
+ * 一起打进前端包。探测、安装、spawn 等 Node-only 逻辑放在 detect.ts / install.ts。
6
+ */
7
+ export const ACP_AGENTS = [
8
+ {
9
+ id: 'claude',
10
+ runtimeId: 'acp-claude',
11
+ name: 'Claude Code',
12
+ label: 'Claude Code (ACP)',
13
+ hint: '通过 claude-agent-acp 接入,支持断线后 session/load 恢复',
14
+ command: 'claude-agent-acp',
15
+ args: [],
16
+ package: { name: '@agentclientprotocol/claude-agent-acp', version: '0.101.0' },
17
+ packageUrl: 'https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp',
18
+ cli: {
19
+ command: 'claude',
20
+ label: 'Claude Code CLI',
21
+ installUrl: 'https://docs.claude.com/en/docs/claude-code/setup',
22
+ },
23
+ probeArgs: ['--version'],
24
+ env: { CLAUDE_AGENT_ACP_PROTOCOL: 'v1' },
25
+ login: { command: 'claude', args: [], hint: '先完成 Claude Code 登录(claude 能正常对话)' },
26
+ loadSession: true,
27
+ // 已有 claude-code 内置运行时,Web 面板不再把它作为可选项展示。
28
+ deprecated: true,
29
+ },
30
+ {
31
+ id: 'codex',
32
+ runtimeId: 'acp-codex',
33
+ name: 'Codex',
34
+ label: 'Codex (ACP)',
35
+ hint: 'baldaworks codex-acp;依赖本机 codex CLI,不支持断线恢复',
36
+ command: 'codex-acp',
37
+ args: ['--message-streaming'],
38
+ package: { name: 'codex-acp', version: '1.11.0' },
39
+ packageUrl: 'https://www.npmjs.com/package/codex-acp',
40
+ cli: {
41
+ command: 'codex',
42
+ label: 'OpenAI Codex CLI',
43
+ installUrl: 'https://github.com/openai/codex',
44
+ },
45
+ probeArgs: ['version'],
46
+ requires: [
47
+ {
48
+ command: 'codex',
49
+ label: 'Codex CLI',
50
+ capabilityArgs: ['app-server', '--help'],
51
+ hint: '需要本机安装 codex CLI,且版本包含 app-server 子命令',
52
+ },
53
+ ],
54
+ login: {
55
+ command: 'codex-acp',
56
+ args: ['login'],
57
+ hint: '先运行 codex-acp login',
58
+ check: { command: 'codex', args: ['login', 'status'] },
59
+ },
60
+ loadSession: false,
61
+ // 已有 codex-app 内置运行时,Web 面板不再把它作为可选项展示。
62
+ deprecated: true,
63
+ },
64
+ {
65
+ id: 'opencode',
66
+ runtimeId: 'acp-opencode',
67
+ name: 'OpenCode',
68
+ label: 'OpenCode',
69
+ hint: 'opencode acp 原生 ACP 模式',
70
+ command: 'opencode',
71
+ args: ['acp'],
72
+ probeArgs: ['--version'],
73
+ installHint: 'brew install opencode(或参考 opencode 官方安装说明)',
74
+ installUrl: 'https://opencode.ai/docs/',
75
+ cli: { command: 'opencode', label: 'OpenCode CLI', installUrl: 'https://opencode.ai/docs/' },
76
+ verifyRequired: true,
77
+ loadSession: true,
78
+ },
79
+ {
80
+ id: 'gemini',
81
+ runtimeId: 'acp-gemini',
82
+ name: 'Gemini CLI',
83
+ label: 'Gemini CLI',
84
+ hint: 'gemini --acp 实验性 ACP 模式',
85
+ command: 'gemini',
86
+ args: ['--acp'],
87
+ probeArgs: ['--version'],
88
+ installHint: 'npm i -g @google/gemini-cli',
89
+ installUrl: 'https://github.com/google-gemini/gemini-cli',
90
+ cli: { command: 'gemini', label: 'Gemini CLI', installUrl: 'https://github.com/google-gemini/gemini-cli' },
91
+ verifyRequired: true,
92
+ },
93
+ {
94
+ id: 'copilot',
95
+ runtimeId: 'acp-copilot',
96
+ name: 'GitHub Copilot CLI',
97
+ label: 'GitHub Copilot CLI',
98
+ hint: 'copilot --acp --stdio',
99
+ command: 'copilot',
100
+ args: ['--acp', '--stdio'],
101
+ probeArgs: ['--version'],
102
+ installHint: '参考 GitHub Copilot CLI 官方安装说明',
103
+ installUrl: 'https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli',
104
+ cli: {
105
+ command: 'copilot',
106
+ label: 'GitHub Copilot CLI',
107
+ installUrl: 'https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli',
108
+ },
109
+ verifyRequired: true,
110
+ },
111
+ {
112
+ id: 'cursor',
113
+ runtimeId: 'acp-cursor',
114
+ name: 'Cursor CLI',
115
+ label: 'Cursor CLI',
116
+ hint: 'cursor-agent acp;旧版本可能没有 acp 子命令',
117
+ command: 'cursor-agent',
118
+ args: ['acp'],
119
+ probeArgs: ['--version'],
120
+ installHint: '升级 cursor-agent 到支持 acp 子命令的版本',
121
+ installUrl: 'https://cursor.com/cli',
122
+ cli: { command: 'cursor-agent', label: 'Cursor CLI', installUrl: 'https://cursor.com/cli' },
123
+ verifyRequired: true,
124
+ },
125
+ {
126
+ id: 'goose',
127
+ runtimeId: 'acp-goose',
128
+ name: 'Goose',
129
+ label: 'Goose',
130
+ hint: 'goose acp 原生 ACP 模式',
131
+ command: 'goose',
132
+ args: ['acp'],
133
+ probeArgs: ['--version'],
134
+ installHint: 'brew install block-goose-cli(或参考 Goose 官方安装说明)',
135
+ installUrl: 'https://block.github.io/goose/docs/getting-started/installation',
136
+ cli: {
137
+ command: 'goose',
138
+ label: 'Goose CLI',
139
+ installUrl: 'https://block.github.io/goose/docs/getting-started/installation',
140
+ },
141
+ verifyRequired: true,
142
+ },
143
+ ];
144
+ /** 按 agent id 或 runtime id 查目录项。 */
145
+ export function acpAgentOf(idOrRuntimeId) {
146
+ const value = idOrRuntimeId.trim();
147
+ return ACP_AGENTS.find(agent => agent.id === value || agent.runtimeId === value);
148
+ }
149
+ /** 是否是目录内的动态 ACP 运行时 id(不含旧的通用 `acp`)。 */
150
+ export function isAcpRuntimeId(id) {
151
+ const value = id.trim();
152
+ return ACP_AGENTS.some(agent => agent.runtimeId === value);
153
+ }
154
+ /**
155
+ * 是否是已有原生运行时替代品的 ACP 运行时(Claude Code / Codex)。
156
+ *
157
+ * 这类运行时 id 依旧合法、依旧能派工,只是 Web 面板不再把它们列为可选项。
158
+ */
159
+ export function isDeprecatedAcpRuntimeId(id) {
160
+ const value = id.trim();
161
+ return ACP_AGENTS.some(agent => agent.runtimeId === value && agent.deprecated === true);
162
+ }
163
+ /** agent id → runtime id;未知 id 返回 null。 */
164
+ export function acpRuntimeIdOf(idOrRuntimeId) {
165
+ return acpAgentOf(idOrRuntimeId)?.runtimeId ?? null;
166
+ }
167
+ //# sourceMappingURL=acp-agents.js.map