echo-cli-agent 0.1.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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/src/config.d.ts +33 -0
  4. package/dist/src/config.js +154 -0
  5. package/dist/src/config.js.map +1 -0
  6. package/dist/src/dispatch/dispatcher.d.ts +34 -0
  7. package/dist/src/dispatch/dispatcher.js +216 -0
  8. package/dist/src/dispatch/dispatcher.js.map +1 -0
  9. package/dist/src/health/server.d.ts +25 -0
  10. package/dist/src/health/server.js +122 -0
  11. package/dist/src/health/server.js.map +1 -0
  12. package/dist/src/index.d.ts +2 -0
  13. package/dist/src/index.js +82 -0
  14. package/dist/src/index.js.map +1 -0
  15. package/dist/src/logging/diagnostic-log.d.ts +15 -0
  16. package/dist/src/logging/diagnostic-log.js +92 -0
  17. package/dist/src/logging/diagnostic-log.js.map +1 -0
  18. package/dist/src/providers/claude.d.ts +2 -0
  19. package/dist/src/providers/claude.js +20 -0
  20. package/dist/src/providers/claude.js.map +1 -0
  21. package/dist/src/providers/codex.d.ts +2 -0
  22. package/dist/src/providers/codex.js +39 -0
  23. package/dist/src/providers/codex.js.map +1 -0
  24. package/dist/src/providers/kiro.d.ts +2 -0
  25. package/dist/src/providers/kiro.js +33 -0
  26. package/dist/src/providers/kiro.js.map +1 -0
  27. package/dist/src/providers/model-catalog.d.ts +16 -0
  28. package/dist/src/providers/model-catalog.js +156 -0
  29. package/dist/src/providers/model-catalog.js.map +1 -0
  30. package/dist/src/providers/provider.d.ts +32 -0
  31. package/dist/src/providers/provider.js +57 -0
  32. package/dist/src/providers/provider.js.map +1 -0
  33. package/dist/src/providers/registry.d.ts +9 -0
  34. package/dist/src/providers/registry.js +44 -0
  35. package/dist/src/providers/registry.js.map +1 -0
  36. package/dist/src/runtime/agent-runtime.d.ts +13 -0
  37. package/dist/src/runtime/agent-runtime.js +55 -0
  38. package/dist/src/runtime/agent-runtime.js.map +1 -0
  39. package/dist/src/runtime/cli/hydrate-shell-path.d.ts +33 -0
  40. package/dist/src/runtime/cli/hydrate-shell-path.js +173 -0
  41. package/dist/src/runtime/cli/hydrate-shell-path.js.map +1 -0
  42. package/dist/src/session/final-output.d.ts +1 -0
  43. package/dist/src/session/final-output.js +16 -0
  44. package/dist/src/session/final-output.js.map +1 -0
  45. package/dist/src/session/session-manager.d.ts +42 -0
  46. package/dist/src/session/session-manager.js +339 -0
  47. package/dist/src/session/session-manager.js.map +1 -0
  48. package/dist/src/shared/node-cli-command-resolution.d.ts +11 -0
  49. package/dist/src/shared/node-cli-command-resolution.js +163 -0
  50. package/dist/src/shared/node-cli-command-resolution.js.map +1 -0
  51. package/dist/src/state/state-store.d.ts +48 -0
  52. package/dist/src/state/state-store.js +125 -0
  53. package/dist/src/state/state-store.js.map +1 -0
  54. package/dist/src/transport/taskmcp-client.d.ts +29 -0
  55. package/dist/src/transport/taskmcp-client.js +115 -0
  56. package/dist/src/transport/taskmcp-client.js.map +1 -0
  57. package/dist/src/types/protocol.d.ts +98 -0
  58. package/dist/src/types/protocol.js +38 -0
  59. package/dist/src/types/protocol.js.map +1 -0
  60. package/dist/tests/dispatch-integration.test.d.ts +1 -0
  61. package/dist/tests/dispatch-integration.test.js +209 -0
  62. package/dist/tests/dispatch-integration.test.js.map +1 -0
  63. package/dist/tests/final-output.test.d.ts +1 -0
  64. package/dist/tests/final-output.test.js +9 -0
  65. package/dist/tests/final-output.test.js.map +1 -0
  66. package/dist/tests/health-server.test.d.ts +1 -0
  67. package/dist/tests/health-server.test.js +38 -0
  68. package/dist/tests/health-server.test.js.map +1 -0
  69. package/dist/tests/model-catalog.test.d.ts +1 -0
  70. package/dist/tests/model-catalog.test.js +59 -0
  71. package/dist/tests/model-catalog.test.js.map +1 -0
  72. package/dist/tests/providers.test.d.ts +1 -0
  73. package/dist/tests/providers.test.js +95 -0
  74. package/dist/tests/providers.test.js.map +1 -0
  75. package/dist/tests/state-store.test.d.ts +1 -0
  76. package/dist/tests/state-store.test.js +22 -0
  77. package/dist/tests/state-store.test.js.map +1 -0
  78. package/dist/vitest.config.d.ts +2 -0
  79. package/dist/vitest.config.js +11 -0
  80. package/dist/vitest.config.js.map +1 -0
  81. package/package.json +42 -0
  82. package/scripts/service.mjs +158 -0
@@ -0,0 +1,122 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { createServer } from 'node:http';
3
+ export class HealthServer {
4
+ host;
5
+ port;
6
+ snapshot;
7
+ logFile;
8
+ logger;
9
+ server;
10
+ constructor(host, port, snapshot, logFile, logger) {
11
+ this.host = host;
12
+ this.port = port;
13
+ this.snapshot = snapshot;
14
+ this.logFile = logFile;
15
+ this.logger = logger;
16
+ }
17
+ get boundPort() {
18
+ const address = this.server?.address();
19
+ return typeof address === 'object' && address ? address.port : this.port;
20
+ }
21
+ async start() {
22
+ this.server = createServer((request, response) => void this.handle(request, response));
23
+ await new Promise((resolveStart, rejectStart) => {
24
+ this.server?.once('error', rejectStart);
25
+ this.server?.listen(this.port, this.host, () => resolveStart());
26
+ });
27
+ const address = this.server.address();
28
+ this.logger.info('health-server-started', {
29
+ host: this.host,
30
+ port: typeof address === 'object' && address ? address.port : this.port
31
+ });
32
+ }
33
+ async stop() {
34
+ const server = this.server;
35
+ this.server = undefined;
36
+ if (!server)
37
+ return;
38
+ await new Promise((resolveStop) => server.close(() => resolveStop()));
39
+ }
40
+ async handle(request, response) {
41
+ const url = new URL(request.url ?? '/', 'http://localhost');
42
+ if (request.method !== 'GET') {
43
+ response.writeHead(405).end();
44
+ return;
45
+ }
46
+ if (url.pathname === '/') {
47
+ response.writeHead(200, {
48
+ 'Content-Type': 'text/html; charset=utf-8',
49
+ 'Cache-Control': 'no-store',
50
+ 'Content-Security-Policy': "default-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'"
51
+ });
52
+ response.end(dashboardHtml());
53
+ return;
54
+ }
55
+ if (url.pathname === '/api/status') {
56
+ this.json(response, 200, this.snapshot());
57
+ return;
58
+ }
59
+ if (url.pathname === '/api/events') {
60
+ this.json(response, 200, { events: await this.recentEvents() });
61
+ return;
62
+ }
63
+ if (url.pathname === '/health' || url.pathname === '/ready') {
64
+ const status = this.snapshot();
65
+ const ready = url.pathname === '/health' || status.connected;
66
+ this.json(response, ready ? 200 : 503, { ok: ready, ...status });
67
+ return;
68
+ }
69
+ response.writeHead(404).end();
70
+ }
71
+ json(response, status, body) {
72
+ response.writeHead(status, {
73
+ 'Content-Type': 'application/json; charset=utf-8',
74
+ 'Cache-Control': 'no-store'
75
+ });
76
+ response.end(JSON.stringify(body));
77
+ }
78
+ async recentEvents() {
79
+ try {
80
+ const raw = await readFile(this.logFile, 'utf8');
81
+ return raw
82
+ .trim()
83
+ .split('\n')
84
+ .slice(-60)
85
+ .flatMap((line) => {
86
+ try {
87
+ const parsed = JSON.parse(line);
88
+ return [{
89
+ ts: parsed.ts,
90
+ level: parsed.level,
91
+ event: parsed.event,
92
+ runId: parsed.runId,
93
+ taskId: parsed.taskId,
94
+ sessionId: parsed.sessionId,
95
+ agentType: parsed.agentType,
96
+ error: parsed.error,
97
+ code: parsed.code
98
+ }];
99
+ }
100
+ catch {
101
+ return [];
102
+ }
103
+ })
104
+ .reverse();
105
+ }
106
+ catch {
107
+ return [];
108
+ }
109
+ }
110
+ }
111
+ function dashboardHtml() {
112
+ return `<!doctype html>
113
+ <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
114
+ <title>Orca Agent</title><style>
115
+ :root{color:#17202a;background:#f5f7f7;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}*{box-sizing:border-box}body{margin:0}.shell{max-width:1120px;margin:0 auto;padding:28px 20px 44px}.top{display:flex;justify-content:space-between;align-items:center;gap:18px;border-bottom:1px solid #d7dfdf;padding-bottom:18px}.brand{font-size:21px;font-weight:700}.sub{color:#5d6c72;font-size:14px;margin-top:4px}.refresh{width:34px;height:34px;border:1px solid #aebbbb;background:#fff;border-radius:5px;font-size:20px;line-height:1;cursor:pointer}.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin:20px 0}.stat,.section{background:#fff;border:1px solid #d7dfdf;border-radius:6px}.stat{padding:15px}.label{color:#607077;font-size:12px}.value{font-size:22px;font-weight:650;margin-top:7px}.ok{color:#087f5b}.bad{color:#c0392b}.section{padding:18px;margin-top:14px}.section h2{font-size:15px;margin:0 0 13px}table{border-collapse:collapse;width:100%;font-size:13px}th{text-align:left;color:#607077;font-weight:500;border-bottom:1px solid #d7dfdf;padding:0 8px 9px}td{padding:10px 8px;border-bottom:1px solid #edf0f0;vertical-align:top}.tag{display:inline-block;border-radius:3px;padding:3px 6px;font-size:12px;background:#eef3f2;color:#365054}.tag.delivered,.tag.completed{background:#e5f6ee;color:#087f5b}.tag.failed{background:#fbeaea;color:#b33a32}.event{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:#34454b}.muted{color:#718087}.empty{color:#718087;padding:12px 0}@media(max-width:700px){.stats{grid-template-columns:repeat(2,minmax(0,1fr))}.shell{padding:20px 12px}.hide-small{display:none}th,td{padding-left:5px;padding-right:5px}}
116
+ </style></head><body><main class="shell"><header class="top"><div><div class="brand">Orca Agent</div><div class="sub" id="device">正在连接本机服务</div></div><button class="refresh" id="refresh" title="刷新状态" aria-label="刷新状态">↻</button></header><section class="stats"><div class="stat"><div class="label">连接</div><div class="value" id="connected">-</div></div><div class="stat"><div class="label">活跃窗口</div><div class="value" id="sessions">-</div></div><div class="stat"><div class="label">本地记录</div><div class="value" id="runs">-</div></div><div class="stat"><div class="label">启动时间</div><div class="value" id="started">-</div></div></section><section class="section"><h2>最近派发</h2><div id="run-list" class="empty">加载中</div></section><section class="section"><h2>本机事件</h2><div id="event-list" class="empty">加载中</div></section></main><script>
117
+ const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const time=v=>v?new Date(v).toLocaleTimeString('zh-CN',{hour12:false}):'-';
118
+ function table(headers,rows,klass=''){return rows.length?'<table><thead><tr>'+headers.map(h=>'<th>'+h+'</th>').join('')+'</tr></thead><tbody>'+rows.map(r=>'<tr>'+r.map(c=>'<td>'+c+'</td>').join('')+'</tr>').join('')+'</tbody></table>':'<div class="empty">暂无记录</div>'}
119
+ async function load(){try{const [s,e]=await Promise.all([fetch('/api/status',{cache:'no-store'}),fetch('/api/events',{cache:'no-store'})]);const status=await s.json(),events=(await e.json()).events||[];document.querySelector('#device').textContent=status.deviceId||'未注册设备';const con=document.querySelector('#connected');con.textContent=status.connected?'已连接':'未连接';con.className='value '+(status.connected?'ok':'bad');document.querySelector('#sessions').textContent=status.activeSessions;document.querySelector('#runs').textContent=status.storedRuns;document.querySelector('#started').textContent=time(status.startedAt);document.querySelector('#run-list').innerHTML=table(['时间','任务','状态','会话'],(status.runs||[]).map(r=>[time(r.updatedAt),'<span class="event">'+esc(r.runId).slice(0,8)+'</span>','<span class="tag '+esc(r.status)+'">'+esc(r.status)+'</span>','<span class="hide-small">'+esc(r.sessionId||'-').slice(0,8)+'</span>']));document.querySelector('#event-list').innerHTML=table(['时间','事件','任务','状态'],events.map(x=>[time(x.ts),'<span class="event">'+esc(x.event||'-')+'</span>','<span class="hide-small">'+esc(x.runId||x.taskId||'-').slice(0,8)+'</span>',x.error?'<span class="bad">'+esc(x.error)+'</span>':esc(x.level||'-')]));}catch{document.querySelector('#device').textContent='无法连接本机服务'}}document.querySelector('#refresh').addEventListener('click',load);load();setInterval(load,3000);
120
+ </script></body></html>`;
121
+ }
122
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/health/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import { enrollDevice, loadAgentConfig, redactedConfig } from './config.js';
3
+ import { probeProviders } from './providers/registry.js';
4
+ import { AgentRuntime } from './runtime/agent-runtime.js';
5
+ import { hydrateShellPath, mergePathSegments } from './runtime/cli/hydrate-shell-path.js';
6
+ async function run() {
7
+ const config = loadAgentConfig();
8
+ const runtime = new AgentRuntime(config);
9
+ await runtime.start();
10
+ let shuttingDown = false;
11
+ const shutdown = async () => {
12
+ if (shuttingDown)
13
+ return;
14
+ shuttingDown = true;
15
+ await runtime.stop();
16
+ process.exitCode = 0;
17
+ };
18
+ process.once('SIGINT', () => void shutdown());
19
+ process.once('SIGTERM', () => void shutdown());
20
+ }
21
+ async function doctor() {
22
+ const hydration = await hydrateShellPath({ force: true });
23
+ if (hydration.ok)
24
+ mergePathSegments(hydration.segments);
25
+ let config;
26
+ let configError;
27
+ try {
28
+ config = loadAgentConfig();
29
+ }
30
+ catch (error) {
31
+ configError = error instanceof Error ? error.message : String(error);
32
+ }
33
+ process.stdout.write(`${JSON.stringify({
34
+ ok: !configError,
35
+ config: config ? redactedConfig(config) : undefined,
36
+ configError,
37
+ providers: probeProviders()
38
+ }, null, 2)}\n`);
39
+ if (configError)
40
+ process.exitCode = 1;
41
+ }
42
+ async function enroll(args) {
43
+ const serverUrl = option(args, '--server');
44
+ const code = option(args, '--code');
45
+ if (!serverUrl || !code)
46
+ throw new Error('enroll requires --server and --code');
47
+ const enrollment = {
48
+ serverUrl,
49
+ code
50
+ };
51
+ const deviceName = option(args, '--device-name');
52
+ const configPath = option(args, '--config');
53
+ const result = await enrollDevice({
54
+ ...enrollment,
55
+ ...(deviceName ? { deviceName } : {}),
56
+ ...(configPath ? { configPath } : {})
57
+ });
58
+ process.stdout.write(`${JSON.stringify({ enrolled: true, deviceId: result.deviceId, configPath: result.configPath })}\n`);
59
+ }
60
+ function option(args, name) {
61
+ const index = args.indexOf(name);
62
+ const value = index >= 0 ? args[index + 1] : undefined;
63
+ return value && !value.startsWith('--') ? value : undefined;
64
+ }
65
+ const command = process.argv[2] ?? 'run';
66
+ if (command === 'doctor')
67
+ void doctor();
68
+ else if (command === 'run')
69
+ void run().catch((error) => {
70
+ process.stderr.write(`echo-cli-agent failed: ${error instanceof Error ? error.message : String(error)}\n`);
71
+ process.exitCode = 1;
72
+ });
73
+ else if (command === 'enroll')
74
+ void enroll(process.argv.slice(3)).catch((error) => {
75
+ process.stderr.write(`echo-cli-agent enrollment failed: ${error instanceof Error ? error.message : String(error)}\n`);
76
+ process.exitCode = 1;
77
+ });
78
+ else {
79
+ process.stderr.write('Usage: echo-cli-agent [run|doctor|enroll]\n');
80
+ process.exitCode = 2;
81
+ }
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export type LogLevel = 'error' | 'warn' | 'info' | 'debug';
2
+ export type DiagnosticLogger = {
3
+ error(event: string, details?: Record<string, unknown>): void;
4
+ warn(event: string, details?: Record<string, unknown>): void;
5
+ info(event: string, details?: Record<string, unknown>): void;
6
+ debug(event: string, details?: Record<string, unknown>): void;
7
+ close(): void;
8
+ };
9
+ type DiagnosticLogOptions = {
10
+ level?: LogLevel;
11
+ maxBytes?: number;
12
+ maxRotatedFiles?: number;
13
+ };
14
+ export declare function createDiagnosticLogger(filePath: string, options?: DiagnosticLogOptions): DiagnosticLogger;
15
+ export {};
@@ -0,0 +1,92 @@
1
+ import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
4
+ const DEFAULT_MAX_ROTATED_FILES = 2;
5
+ const PRIVATE_FILE_MODE = 0o600;
6
+ const LEVEL_WEIGHT = {
7
+ error: 40,
8
+ warn: 30,
9
+ info: 20,
10
+ debug: 10
11
+ };
12
+ function safeDetails(details) {
13
+ const blocked = /prompt|content|token|secret|password|api[_-]?key|credential|authorization/i;
14
+ return Object.fromEntries(Object.entries(details)
15
+ .filter(([key]) => !blocked.test(key))
16
+ .map(([key, value]) => [key, typeof value === 'string' ? value.slice(0, 512) : value]));
17
+ }
18
+ // Adapted from Orca's fail-open, size-capped daemon NDJSON logger.
19
+ export function createDiagnosticLogger(filePath, options = {}) {
20
+ const level = options.level ?? 'info';
21
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
22
+ const maxRotatedFiles = options.maxRotatedFiles ?? DEFAULT_MAX_ROTATED_FILES;
23
+ let disabled = false;
24
+ let currentBytes = 0;
25
+ try {
26
+ mkdirSync(dirname(filePath), { recursive: true });
27
+ currentBytes = existsSync(filePath) ? statSync(filePath).size : 0;
28
+ }
29
+ catch {
30
+ disabled = true;
31
+ }
32
+ function rotate() {
33
+ if (maxRotatedFiles < 1)
34
+ return;
35
+ try {
36
+ for (let index = maxRotatedFiles; index >= 1; index -= 1) {
37
+ const source = index === 1 ? filePath : `${filePath}.${index - 1}`;
38
+ const destination = `${filePath}.${index}`;
39
+ if (!existsSync(source))
40
+ continue;
41
+ if (existsSync(destination))
42
+ unlinkSync(destination);
43
+ renameSync(source, destination);
44
+ }
45
+ currentBytes = 0;
46
+ }
47
+ catch {
48
+ disabled = true;
49
+ }
50
+ }
51
+ function write(entryLevel, event, details = {}) {
52
+ if (disabled || LEVEL_WEIGHT[entryLevel] < LEVEL_WEIGHT[level])
53
+ return;
54
+ let line;
55
+ try {
56
+ line = `${JSON.stringify({
57
+ ts: new Date().toISOString(),
58
+ src: 'echo-cli-agent',
59
+ pid: process.pid,
60
+ level: entryLevel,
61
+ event,
62
+ ...safeDetails(details)
63
+ })}\n`;
64
+ }
65
+ catch {
66
+ return;
67
+ }
68
+ const bytes = Buffer.byteLength(line, 'utf8');
69
+ if (currentBytes > 0 && currentBytes + bytes > maxBytes)
70
+ rotate();
71
+ if (disabled)
72
+ return;
73
+ try {
74
+ appendFileSync(filePath, line, { mode: PRIVATE_FILE_MODE });
75
+ currentBytes += bytes;
76
+ }
77
+ catch {
78
+ disabled = true;
79
+ }
80
+ }
81
+ return {
82
+ error: (event, details) => write('error', event, details),
83
+ warn: (event, details) => write('warn', event, details),
84
+ info: (event, details) => write('info', event, details),
85
+ debug: (event, details) => write('debug', event, details),
86
+ close() {
87
+ write('info', 'diagnostic-log-closed');
88
+ disabled = true;
89
+ }
90
+ };
91
+ }
92
+ //# sourceMappingURL=diagnostic-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic-log.js","sourceRoot":"","sources":["../../../src/logging/diagnostic-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AgentProvider } from './provider.js';
2
+ export declare const claudeProvider: AgentProvider;
@@ -0,0 +1,20 @@
1
+ import { extractNamedSessionId, normalizeArguments, providerEnvironment, selectExecutable } from './provider.js';
2
+ export const claudeProvider = {
3
+ id: 'claude',
4
+ versionArguments: () => ['--version'],
5
+ extractSessionId: extractNamedSessionId,
6
+ buildLaunchPlan(request, context) {
7
+ const args = normalizeArguments(request.config.arguments);
8
+ if (request.session_id)
9
+ args.push('--resume', request.session_id);
10
+ if (request.model)
11
+ args.push('--model', request.model);
12
+ args.push(...normalizeArguments(request.config.protocol_arguments));
13
+ return {
14
+ executable: selectExecutable('claude', request.config.executable, context.allowExecutableOverride),
15
+ args,
16
+ env: providerEnvironment(request)
17
+ };
18
+ }
19
+ };
20
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../../src/providers/claude.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,eAAe,CAAA;AAEtB,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,EAAE,EAAE,QAAQ;IACZ,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC;IACrC,gBAAgB,EAAE,qBAAqB;IACvC,eAAe,CAAC,OAAO,EAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACzD,IAAI,OAAO,CAAC,UAAU;YAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;QACjE,IAAI,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtD,IAAI,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACnE,OAAO;YACL,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC;YAClG,IAAI;YACJ,GAAG,EAAE,mBAAmB,CAAC,OAAO,CAAC;SAClC,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { AgentProvider } from './provider.js';
2
+ export declare const codexProvider: AgentProvider;
@@ -0,0 +1,39 @@
1
+ import { extractNamedSessionId, normalizeArguments, providerEnvironment, resolveCodexExecutable, selectExecutable } from './provider.js';
2
+ const CODEX_SESSION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3
+ export const codexProvider = {
4
+ id: 'codex',
5
+ versionArguments: () => ['--version'],
6
+ extractSessionId(output) {
7
+ const sessionId = extractNamedSessionId(output);
8
+ return sessionId && CODEX_SESSION_ID.test(sessionId) ? sessionId : undefined;
9
+ },
10
+ buildLaunchPlan(request, context) {
11
+ const args = ['exec', ...normalizeArguments(request.config.arguments)];
12
+ if (request.model)
13
+ args.push('--model', request.model);
14
+ if (request.reasoning_effort)
15
+ args.push('-c', `model_reasoning_effort=${JSON.stringify(request.reasoning_effort)}`);
16
+ const protocolArguments = normalizeArguments(request.config.protocol_arguments);
17
+ // Codex sessions are local, user-created workstations. Keep every new
18
+ // session in the requested Full Access mode unless explicitly configured
19
+ // through protocol arguments in a future provider-specific policy.
20
+ if (!protocolArguments.includes('--dangerously-bypass-approvals-and-sandbox')) {
21
+ args.push('--dangerously-bypass-approvals-and-sandbox');
22
+ }
23
+ if (!protocolArguments.includes('--skip-git-repo-check'))
24
+ args.push('--skip-git-repo-check');
25
+ args.push(...protocolArguments);
26
+ if (request.session_id) {
27
+ args.push('resume', request.session_id, '--', request.prompt ?? '');
28
+ }
29
+ else {
30
+ args.push('--', request.prompt ?? '');
31
+ }
32
+ const env = providerEnvironment(request);
33
+ const launch = context.allowExecutableOverride && request.config.executable?.trim()
34
+ ? { executable: selectExecutable('codex', request.config.executable, true), args }
35
+ : resolveCodexExecutable(args, env);
36
+ return { ...launch, env, promptDelivery: 'startup', reuseSession: false };
37
+ }
38
+ };
39
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/providers/codex.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,eAAe,CAAA;AAEtB,MAAM,gBAAgB,GAAG,iEAAiE,CAAA;AAE1F,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,EAAE,EAAE,OAAO;IACX,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC;IACrC,gBAAgB,CAAC,MAAM;QACrB,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAA;QAC/C,OAAO,SAAS,IAAI,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9E,CAAC;IACD,eAAe,CAAC,OAAO,EAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QACtE,IAAI,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtD,IAAI,OAAO,CAAC,gBAAgB;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,0BAA0B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAA;QACnH,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAC/E,sEAAsE;QACtE,yEAAyE;QACzE,mEAAmE;QACnE,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,CAAC;YAC9E,IAAI,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAA;QACzD,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;QAC5F,IAAI,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAA;QAC/B,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;QACrE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC;QACD,MAAM,GAAG,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE;YACjF,CAAC,CAAC,EAAE,UAAU,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;YAClF,CAAC,CAAC,sBAAsB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QACrC,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,CAAA;IAC3E,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { AgentProvider } from './provider.js';
2
+ export declare const kiroProvider: AgentProvider;
@@ -0,0 +1,33 @@
1
+ import { extractNamedSessionId, normalizeArguments, providerEnvironment, selectExecutable } from './provider.js';
2
+ export const kiroProvider = {
3
+ id: 'kiro',
4
+ reuseSession: false,
5
+ versionArguments: () => ['--version'],
6
+ extractSessionId: extractNamedSessionId,
7
+ buildLaunchPlan(request, context) {
8
+ const executable = selectExecutable('kiro-cli', request.config.executable, context.allowExecutableOverride);
9
+ const args = [
10
+ ...normalizeArguments(request.config.arguments),
11
+ 'chat',
12
+ '--no-interactive',
13
+ '--trust-all-tools'
14
+ ];
15
+ if (request.model)
16
+ args.push('--model', request.model);
17
+ if (request.reasoning_effort)
18
+ args.push('--effort', request.reasoning_effort);
19
+ args.push(...normalizeArguments(request.config.protocol_arguments), '--', request.prompt ?? '');
20
+ return {
21
+ executable,
22
+ args,
23
+ env: providerEnvironment(request),
24
+ promptDelivery: 'startup',
25
+ reuseSession: false,
26
+ // Kiro can still present a one-time "trust all tools" confirmation even
27
+ // when --trust-all-tools is supplied. The session manager handles that
28
+ // screen before the remote prompt is delivered.
29
+ autoApproveTrustPrompt: true
30
+ };
31
+ }
32
+ };
33
+ //# sourceMappingURL=kiro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kiro.js","sourceRoot":"","sources":["../../../src/providers/kiro.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhH,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,EAAE,EAAE,MAAM;IACV,YAAY,EAAE,KAAK;IACnB,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC;IACrC,gBAAgB,EAAE,qBAAqB;IACvC,eAAe,CAAC,OAAO,EAAE,OAAO;QAC9B,MAAM,UAAU,GAAG,gBAAgB,CACjC,UAAU,EACV,OAAO,CAAC,MAAM,CAAC,UAAU,EACzB,OAAO,CAAC,uBAAuB,CAChC,CAAA;QACD,MAAM,IAAI,GAAG;YACX,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;YAC/C,MAAM;YACN,kBAAkB;YAClB,mBAAmB;SACpB,CAAA;QACD,IAAI,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtD,IAAI,OAAO,CAAC,gBAAgB;YAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC7E,IAAI,CAAC,IAAI,CACP,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,EACxD,IAAI,EACJ,OAAO,CAAC,MAAM,IAAI,EAAE,CACrB,CAAA;QACD,OAAO;YACL,UAAU;YACV,IAAI;YACJ,GAAG,EAAE,mBAAmB,CAAC,OAAO,CAAC;YACjC,cAAc,EAAE,SAAS;YACzB,YAAY,EAAE,KAAK;YACnB,wEAAwE;YACxE,uEAAuE;YACvE,gDAAgD;YAChD,sBAAsB,EAAE,IAAI;SAC7B,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,16 @@
1
+ import type { AgentCliModelCatalog } from '../types/protocol.js';
2
+ /**
3
+ * Codex writes the locally available model metadata to models_cache.json. This is
4
+ * intentionally read on the device, so the server never guesses another machine's
5
+ * model access or supported reasoning levels.
6
+ */
7
+ export declare function readCodexModelCatalog(): Promise<AgentCliModelCatalog>;
8
+ /** Normalizes the JSON emitted by `kiro-cli chat --list-models --format json`. */
9
+ export declare function parseKiroModelCatalog(payload: unknown, fetchedAt?: string): AgentCliModelCatalog;
10
+ /**
11
+ * Kiro is queried from the physical device. Its current CLI returns models but
12
+ * does not advertise reasoning efforts, so the empty effort lists deliberately
13
+ * preserve Kiro's device default rather than inventing unsupported choices.
14
+ */
15
+ export declare function readKiroModelCatalog(): Promise<AgentCliModelCatalog>;
16
+ export declare function readAgentModelCatalog(agentType: string): Promise<AgentCliModelCatalog>;
@@ -0,0 +1,156 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { resolveCliCommand } from '../shared/node-cli-command-resolution.js';
6
+ function stringValue(value) {
7
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
8
+ }
9
+ function configValue(content, key) {
10
+ const match = content.match(new RegExp(`^\\s*${key}\\s*=\\s*[\"']([^\"']+)[\"']\\s*(?:#.*)?$`, 'm'));
11
+ return match?.[1]?.trim() || undefined;
12
+ }
13
+ function toModel(entry) {
14
+ const id = stringValue(entry.slug);
15
+ if (!id)
16
+ return undefined;
17
+ const supported = Array.isArray(entry.supported_reasoning_levels)
18
+ ? entry.supported_reasoning_levels.flatMap((value) => {
19
+ const level = stringValue(value) ?? (value && typeof value === 'object'
20
+ ? stringValue(value.effort)
21
+ : undefined);
22
+ return level ? [level] : [];
23
+ })
24
+ : [];
25
+ const defaultReasoning = stringValue(entry.default_reasoning_level);
26
+ const description = stringValue(entry.description);
27
+ const model = {
28
+ id,
29
+ name: stringValue(entry.display_name) ?? id,
30
+ supported_reasoning_efforts: supported
31
+ };
32
+ if (description)
33
+ model.description = description;
34
+ if (defaultReasoning)
35
+ model.default_reasoning_effort = defaultReasoning;
36
+ return model;
37
+ }
38
+ /**
39
+ * Codex writes the locally available model metadata to models_cache.json. This is
40
+ * intentionally read on the device, so the server never guesses another machine's
41
+ * model access or supported reasoning levels.
42
+ */
43
+ export async function readCodexModelCatalog() {
44
+ const codexHome = process.env.CODEX_HOME?.trim() || join(homedir(), '.codex');
45
+ const [config, cache] = await Promise.all([
46
+ readFile(join(codexHome, 'config.toml'), 'utf8').catch(() => ''),
47
+ readFile(join(codexHome, 'models_cache.json'), 'utf8').catch(() => '')
48
+ ]);
49
+ let entries = [];
50
+ if (cache) {
51
+ try {
52
+ const parsed = JSON.parse(cache);
53
+ if (parsed && typeof parsed === 'object' && Array.isArray(parsed.models)) {
54
+ entries = parsed.models;
55
+ }
56
+ }
57
+ catch {
58
+ // A partially refreshed cache is equivalent to an empty catalog until the next refresh.
59
+ }
60
+ }
61
+ const models = entries.flatMap((entry) => {
62
+ const model = toModel(entry);
63
+ return model ? [model] : [];
64
+ });
65
+ const defaultModel = configValue(config, 'model');
66
+ const defaultReasoningEffort = configValue(config, 'model_reasoning_effort');
67
+ const catalog = {
68
+ agent_type: 'codex',
69
+ fetched_at: new Date().toISOString(),
70
+ models
71
+ };
72
+ if (defaultModel)
73
+ catalog.default_model = defaultModel;
74
+ if (defaultReasoningEffort)
75
+ catalog.default_reasoning_effort = defaultReasoningEffort;
76
+ return catalog;
77
+ }
78
+ /** Normalizes the JSON emitted by `kiro-cli chat --list-models --format json`. */
79
+ export function parseKiroModelCatalog(payload, fetchedAt = new Date().toISOString()) {
80
+ const source = payload && typeof payload === 'object' ? payload : {};
81
+ const models = Array.isArray(source.models)
82
+ ? source.models.flatMap((item) => {
83
+ const entry = item && typeof item === 'object' ? item : {};
84
+ const id = stringValue(entry.model_id) ?? stringValue(entry.model_name);
85
+ if (!id)
86
+ return [];
87
+ const model = {
88
+ id,
89
+ name: stringValue(entry.model_name) ?? id,
90
+ supported_reasoning_efforts: []
91
+ };
92
+ const description = stringValue(entry.description);
93
+ if (description)
94
+ model.description = description;
95
+ return [model];
96
+ })
97
+ : [];
98
+ const catalog = {
99
+ agent_type: 'kiro',
100
+ fetched_at: fetchedAt,
101
+ models
102
+ };
103
+ const defaultModel = stringValue(source.default_model);
104
+ if (defaultModel)
105
+ catalog.default_model = defaultModel;
106
+ return catalog;
107
+ }
108
+ function runKiroModelList(executable) {
109
+ return new Promise((resolve, reject) => {
110
+ const child = spawn(executable, ['chat', '--list-models', '--format', 'json'], {
111
+ shell: false,
112
+ stdio: ['ignore', 'pipe', 'pipe']
113
+ });
114
+ let stdout = '';
115
+ let stderr = '';
116
+ child.stdout.setEncoding('utf8');
117
+ child.stderr.setEncoding('utf8');
118
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
119
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
120
+ child.once('error', reject);
121
+ child.once('close', (code) => {
122
+ if (code === 0)
123
+ resolve(stdout);
124
+ else
125
+ reject(new Error((stderr || stdout || `kiro-cli exited with code ${code ?? 'unknown'}`).trim()));
126
+ });
127
+ });
128
+ }
129
+ /**
130
+ * Kiro is queried from the physical device. Its current CLI returns models but
131
+ * does not advertise reasoning efforts, so the empty effort lists deliberately
132
+ * preserve Kiro's device default rather than inventing unsupported choices.
133
+ */
134
+ export async function readKiroModelCatalog() {
135
+ const executable = resolveCliCommand('kiro-cli');
136
+ const output = await runKiroModelList(executable);
137
+ let payload;
138
+ try {
139
+ payload = JSON.parse(output);
140
+ }
141
+ catch {
142
+ throw new Error('kiro-cli 返回的模型目录不是有效 JSON');
143
+ }
144
+ return parseKiroModelCatalog(payload);
145
+ }
146
+ export async function readAgentModelCatalog(agentType) {
147
+ switch (agentType.toLowerCase()) {
148
+ case 'codex':
149
+ return readCodexModelCatalog();
150
+ case 'kiro':
151
+ return readKiroModelCatalog();
152
+ default:
153
+ throw new Error(`当前 Agent 暂不支持 ${agentType} 的模型目录`);
154
+ }
155
+ }
156
+ //# sourceMappingURL=model-catalog.js.map