@botbotgo/agent-harness 0.0.309 → 0.0.311
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/README.md +14 -0
- package/README.zh.md +14 -0
- package/dist/acp.d.ts +1 -116
- package/dist/acp.js +1 -310
- package/dist/api.d.ts +1 -1
- package/dist/api.js +1 -1
- package/dist/cli/chat-interactive.d.ts +24 -0
- package/dist/cli/chat-interactive.js +244 -0
- package/dist/cli/chat-rendering.d.ts +9 -0
- package/dist/cli/chat-rendering.js +102 -0
- package/dist/cli/chat-stream.d.ts +23 -0
- package/dist/cli/chat-stream.js +330 -0
- package/dist/cli/chat-ui.d.ts +20 -0
- package/dist/cli/chat-ui.js +198 -0
- package/dist/cli/chat-workspace.d.ts +15 -0
- package/dist/cli/chat-workspace.js +205 -0
- package/dist/cli/main.d.ts +52 -0
- package/dist/cli/main.js +323 -0
- package/dist/cli/managed-service-commands.d.ts +23 -0
- package/dist/cli/managed-service-commands.js +63 -0
- package/dist/cli/managed-service.d.ts +27 -0
- package/dist/cli/managed-service.js +61 -0
- package/dist/cli/options-init-chat.d.ts +16 -0
- package/dist/cli/options-init-chat.js +108 -0
- package/dist/cli/options-runtime.d.ts +27 -0
- package/dist/cli/options-runtime.js +158 -0
- package/dist/cli/options-serve.d.ts +24 -0
- package/dist/cli/options-serve.js +166 -0
- package/dist/cli/options.d.ts +5 -0
- package/dist/cli/options.js +47 -0
- package/dist/cli/process-guards.d.ts +14 -0
- package/dist/cli/process-guards.js +139 -0
- package/dist/cli/request-tree.d.ts +12 -0
- package/dist/cli/request-tree.js +296 -0
- package/dist/cli/runtime-commands.d.ts +15 -0
- package/dist/cli/runtime-commands.js +247 -0
- package/dist/cli/runtime-output.d.ts +5 -0
- package/dist/cli/runtime-output.js +124 -0
- package/dist/cli/server-commands.d.ts +36 -0
- package/dist/cli/server-commands.js +250 -0
- package/dist/cli/workspace.d.ts +6 -0
- package/dist/cli/workspace.js +71 -0
- package/dist/cli.d.ts +1 -77
- package/dist/cli.js +2 -3023
- package/dist/client/acp.d.ts +1 -50
- package/dist/client/acp.js +1 -219
- package/dist/client/in-process.d.ts +5 -5
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +1 -1
- package/dist/contracts/runtime-evaluation.d.ts +103 -0
- package/dist/contracts/runtime-evaluation.js +1 -0
- package/dist/contracts/runtime-memory.d.ts +162 -0
- package/dist/contracts/runtime-memory.js +1 -0
- package/dist/contracts/runtime-observability.d.ts +248 -0
- package/dist/contracts/runtime-observability.js +1 -0
- package/dist/contracts/runtime-requests.d.ts +342 -0
- package/dist/contracts/runtime-requests.js +1 -0
- package/dist/contracts/runtime-scheduling.d.ts +146 -0
- package/dist/contracts/runtime-scheduling.js +1 -0
- package/dist/contracts/runtime.d.ts +5 -1042
- package/dist/contracts/runtime.js +27 -1
- package/dist/flow/build-flow-graph.js +4 -875
- package/dist/flow/flow-graph-normalization.d.ts +56 -0
- package/dist/flow/flow-graph-normalization.js +214 -0
- package/dist/flow/flow-graph-runtime.d.ts +8 -0
- package/dist/flow/flow-graph-runtime.js +107 -0
- package/dist/flow/flow-graph-upstream.d.ts +18 -0
- package/dist/flow/flow-graph-upstream.js +498 -0
- package/dist/flow/types.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/init-project.d.ts +1 -12
- package/dist/init-project.js +1 -651
- package/dist/{procedural → knowledge/procedural}/manager.d.ts +3 -3
- package/dist/{procedural → knowledge/procedural}/manager.js +6 -6
- package/dist/mcp.d.ts +2 -76
- package/dist/mcp.js +2 -428
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.js +1 -1
- package/dist/persistence/sqlite-runtime.d.ts +19 -0
- package/dist/persistence/sqlite-runtime.js +86 -0
- package/dist/persistence/sqlite-store.js +11 -99
- package/dist/{request-events.d.ts → projections/request-events.d.ts} +1 -1
- package/dist/{upstream-events.js → projections/upstream-events.js} +1 -1
- package/dist/protocol/a2a/http-discovery.d.ts +39 -0
- package/dist/protocol/a2a/http-discovery.js +178 -0
- package/dist/protocol/a2a/http-rpc.d.ts +28 -0
- package/dist/protocol/a2a/http-rpc.js +623 -0
- package/dist/protocol/a2a/http.d.ts +72 -1
- package/dist/protocol/a2a/http.js +14 -1124
- package/dist/protocol/a2a/task-state.d.ts +29 -0
- package/dist/protocol/a2a/task-state.js +317 -0
- package/dist/protocol/acp/client.js +1 -1
- package/dist/protocol/acp/harness-client.d.ts +50 -0
- package/dist/protocol/acp/harness-client.js +219 -0
- package/dist/protocol/acp/server.d.ts +116 -0
- package/dist/protocol/acp/server.js +310 -0
- package/dist/protocol/ag-ui/http.js +1 -1
- package/dist/protocol/mcp/server.d.ts +76 -0
- package/dist/protocol/mcp/server.js +428 -0
- package/dist/resource/backend/workspace-scoped-backend.d.ts +40 -0
- package/dist/resource/backend/workspace-scoped-backend.js +296 -0
- package/dist/resource/mcp/tool-support.d.ts +35 -0
- package/dist/resource/mcp/tool-support.js +296 -0
- package/dist/resource/mcp-tool-support.d.ts +2 -35
- package/dist/resource/mcp-tool-support.js +2 -296
- package/dist/resource/providers/resource-provider.d.ts +22 -0
- package/dist/resource/providers/resource-provider.js +215 -0
- package/dist/resource/resource-impl.d.ts +3 -33
- package/dist/resource/resource-impl.js +2 -808
- package/dist/resource/resource-types.d.ts +33 -0
- package/dist/resource/resource-types.js +1 -0
- package/dist/resource/tools/function-tool-resolver.d.ts +2 -0
- package/dist/resource/tools/function-tool-resolver.js +306 -0
- package/dist/runtime/adapter/middleware-assembly.js +1 -1
- package/dist/runtime/adapter/model/invocation-request.js +2 -2
- package/dist/runtime/adapter/model/message-assembly.js +1 -1
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -63
- package/dist/runtime/agent-runtime-adapter.js +7 -235
- package/dist/runtime/agent-runtime-assembly.d.ts +67 -0
- package/dist/runtime/agent-runtime-assembly.js +211 -0
- package/dist/runtime/harness/background-runtime.d.ts +1 -1
- package/dist/runtime/harness/events/event-sink.js +1 -1
- package/dist/runtime/harness/events/runtime-event-operations.d.ts +1 -1
- package/dist/runtime/harness/events/streaming.js +1 -1
- package/dist/runtime/harness/public-shapes.d.ts +43 -0
- package/dist/runtime/harness/public-shapes.js +186 -0
- package/dist/runtime/harness/run/inspection.js +2 -2
- package/dist/runtime/harness/run/resources.js +1 -1
- package/dist/runtime/harness/run/surface-semantics.js +1 -1
- package/dist/runtime/harness/system/inventory.d.ts +1 -1
- package/dist/runtime/harness/system/inventory.js +2 -2
- package/dist/runtime/harness/system/policy-engine.js +1 -1
- package/dist/runtime/harness/system/runtime-memory-manager.js +1 -1
- package/dist/runtime/harness/system/skill-requirements.d.ts +1 -1
- package/dist/runtime/harness/system/skill-requirements.js +1 -1
- package/dist/runtime/harness.d.ts +2 -2
- package/dist/runtime/harness.js +7 -191
- package/dist/runtime/maintenance/checkpoint-maintenance.js +1 -1
- package/dist/runtime/maintenance/runtime-record-maintenance.js +1 -1
- package/dist/runtime/parsing/output-content.d.ts +11 -0
- package/dist/runtime/parsing/output-content.js +442 -0
- package/dist/runtime/parsing/output-parsing.d.ts +3 -29
- package/dist/runtime/parsing/output-parsing.js +3 -806
- package/dist/runtime/parsing/output-recovery.d.ts +14 -0
- package/dist/runtime/parsing/output-recovery.js +288 -0
- package/dist/runtime/parsing/output-tool-args.d.ts +4 -0
- package/dist/runtime/parsing/output-tool-args.js +120 -0
- package/dist/runtime/support/runtime-factories.js +1 -1
- package/dist/scaffold/init-project.d.ts +12 -0
- package/dist/scaffold/init-project.js +651 -0
- package/dist/{extensions.d.ts → tooling/extensions.d.ts} +1 -1
- package/dist/{extensions.js → tooling/extensions.js} +3 -3
- package/dist/{tool-modules.d.ts → tooling/module-loader.d.ts} +1 -1
- package/dist/{tool-modules.js → tooling/module-loader.js} +2 -2
- package/dist/workspace/agent-binding-compiler.js +2 -2
- package/dist/workspace/compile.js +2 -2
- package/dist/workspace/object-loader-paths.d.ts +11 -0
- package/dist/workspace/object-loader-paths.js +75 -0
- package/dist/workspace/object-loader-readers.d.ts +21 -0
- package/dist/workspace/object-loader-readers.js +187 -0
- package/dist/workspace/object-loader.d.ts +0 -1
- package/dist/workspace/object-loader.js +6 -260
- package/dist/workspace/resource-compilers.js +1 -1
- package/dist/workspace/support/discovery.js +1 -1
- package/package.json +1 -1
- package/dist/runtime/adapter/index.d.ts +0 -13
- package/dist/runtime/adapter/index.js +0 -13
- package/dist/runtime/harness/index.d.ts +0 -19
- package/dist/runtime/harness/index.js +0 -19
- package/dist/runtime/maintenance/index.d.ts +0 -4
- package/dist/runtime/maintenance/index.js +0 -4
- package/dist/runtime/parsing/index.d.ts +0 -2
- package/dist/runtime/parsing/index.js +0 -2
- package/dist/runtime/support/index.d.ts +0 -4
- package/dist/runtime/support/index.js +0 -4
- package/dist/workspace/support/index.d.ts +0 -2
- package/dist/workspace/support/index.js +0 -2
- /package/dist/{procedural → knowledge/procedural}/config.d.ts +0 -0
- /package/dist/{procedural → knowledge/procedural}/config.js +0 -0
- /package/dist/{procedural → knowledge/procedural}/index.d.ts +0 -0
- /package/dist/{procedural → knowledge/procedural}/index.js +0 -0
- /package/dist/{presentation.d.ts → projections/presentation.d.ts} +0 -0
- /package/dist/{presentation.js → projections/presentation.js} +0 -0
- /package/dist/{request-events.js → projections/request-events.js} +0 -0
- /package/dist/{upstream-events.d.ts → projections/upstream-events.d.ts} +0 -0
- /package/dist/runtime/{support → env}/runtime-env.d.ts +0 -0
- /package/dist/runtime/{support → env}/runtime-env.js +0 -0
- /package/dist/runtime/{support → layout}/runtime-layout.d.ts +0 -0
- /package/dist/runtime/{support → layout}/runtime-layout.js +0 -0
- /package/dist/runtime/{support → prompts}/runtime-prompts.d.ts +0 -0
- /package/dist/runtime/{support → prompts}/runtime-prompts.js +0 -0
- /package/dist/runtime/{support → skills}/skill-metadata.d.ts +0 -0
- /package/dist/runtime/{support → skills}/skill-metadata.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,3027 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import { existsSync, openSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
5
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
-
import { createInterface as createReadlineInterface } from "node:readline";
|
|
7
|
-
import { Writable } from "node:stream";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { pathToFileURL } from "node:url";
|
|
11
|
-
import YAML from "yaml";
|
|
12
|
-
import { createAgentHarness } from "./api.js";
|
|
13
|
-
import { createAcpHttpHarnessClient, createInProcessHarnessClient, } from "./client.js";
|
|
14
|
-
import { initProject } from "./init-project.js";
|
|
15
|
-
import { serveA2aOverHttp } from "./protocol/a2a/http.js";
|
|
16
|
-
import { serveAgUiOverHttp } from "./protocol/ag-ui/http.js";
|
|
17
|
-
import { serveAcpOverHttp } from "./protocol/acp/http.js";
|
|
18
|
-
import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
|
|
19
|
-
import { serveRuntimeMcpOverStdio, serveRuntimeMcpOverStreamableHttp } from "./mcp.js";
|
|
20
|
-
import { interpolateEnvPlaceholders } from "./workspace/yaml-object-reader.js";
|
|
21
|
-
function renderUsage() {
|
|
22
|
-
return `Usage:
|
|
23
|
-
agent-harness init <project-name> [--template deep-research|single-agent] [--provider <provider>] [--model <model>] [--with-web-search|--no-web-search]
|
|
24
|
-
agent-harness chat [-w <path>] [--transport stdio|http] [--host <hostname>] [--port <port>] [--agent <agentId>] [--session <sessionId>] [--message <text>]
|
|
25
|
-
agent-harness [-w <path>] [prompt]
|
|
26
|
-
botbotgo [-w <path>] [prompt]
|
|
27
|
-
agent-harness acp serve [--workspace <path>] [--transport stdio|http] [--host <hostname>] [--port <port>]
|
|
28
|
-
agent-harness acp start [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
29
|
-
agent-harness acp stop [--workspace <path>]
|
|
30
|
-
agent-harness a2a serve [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
31
|
-
agent-harness a2a start [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
32
|
-
agent-harness a2a stop [--workspace <path>]
|
|
33
|
-
agent-harness ag-ui serve [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
34
|
-
agent-harness runtime overview [--workspace <path>] [--limit <n>] [--json]
|
|
35
|
-
agent-harness runtime health [--workspace <path>] [--json]
|
|
36
|
-
agent-harness runtime approvals list [--workspace <path>] [--status <pending|approved|edited|rejected|expired>] [--json]
|
|
37
|
-
agent-harness runtime approvals watch [--workspace <path>] [--status <pending|approved|edited|rejected|expired>] [--poll-ms <ms>] [--once] [--json]
|
|
38
|
-
agent-harness runtime requests list [--workspace <path>] [--agent <agentId>] [--session <sessionId>] [--state <state>] [--json]
|
|
39
|
-
agent-harness runtime requests tail [--workspace <path>] [--agent <agentId>] [--session <sessionId>] [--state <state>] [--poll-ms <ms>] [--once] [--json]
|
|
40
|
-
agent-harness runtime scheduled-run --workspace <path> --schedule <scheduleId>
|
|
41
|
-
agent-harness runtime export request --workspace <path> --session <sessionId> --request <requestId> [--artifacts] [--artifact-contents] [--health] [--json]
|
|
42
|
-
agent-harness runtime export session --workspace <path> --session <sessionId> [--artifacts] [--artifact-contents] [--health] [--json]
|
|
43
|
-
agent-harness runtime-mcp serve [--workspace <path>] [--transport stdio|streamable-http] [--host <hostname>] [--port <port>]
|
|
44
|
-
agent-harness runtime-mcp start [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
45
|
-
agent-harness runtime-mcp stop [--workspace <path>]
|
|
46
|
-
agent-harness mcp serve [--workspace <path>] [--transport stdio|streamable-http] [--host <hostname>] [--port <port>]
|
|
47
|
-
agent-harness mcp start [--workspace <path>] [--host <hostname>] [--port <port>]
|
|
48
|
-
agent-harness mcp stop [--workspace <path>]
|
|
49
|
-
|
|
50
|
-
Run botbotgo or agent-harness from any folder.
|
|
51
|
-
If ./config/ is absent, the runtime falls back to the bundled system defaults and bundled resources.
|
|
52
|
-
Chat defaults to the current directory as the workspace. Use -w to point at another workspace.
|
|
53
|
-
`;
|
|
54
|
-
}
|
|
55
|
-
function isTemplate(value) {
|
|
56
|
-
return value === "deep-research" || value === "single-agent";
|
|
57
|
-
}
|
|
58
|
-
function parseInitOptions(args) {
|
|
59
|
-
const options = {};
|
|
60
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
61
|
-
const arg = args[index];
|
|
62
|
-
if (arg === "--with-web-search") {
|
|
63
|
-
options.withWebSearch = true;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (arg === "--no-web-search") {
|
|
67
|
-
options.withWebSearch = false;
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (arg === "--template" || arg === "--provider" || arg === "--model") {
|
|
71
|
-
const value = args[index + 1];
|
|
72
|
-
if (!value) {
|
|
73
|
-
return { error: `Missing value for ${arg}` };
|
|
74
|
-
}
|
|
75
|
-
if (arg === "--template") {
|
|
76
|
-
if (!isTemplate(value)) {
|
|
77
|
-
return { error: `Unsupported template: ${value}` };
|
|
78
|
-
}
|
|
79
|
-
options.template = value;
|
|
80
|
-
}
|
|
81
|
-
else if (arg === "--provider") {
|
|
82
|
-
options.provider = value;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
options.model = value;
|
|
86
|
-
}
|
|
87
|
-
index += 1;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
return { error: `Unknown option: ${arg}` };
|
|
91
|
-
}
|
|
92
|
-
return { options };
|
|
93
|
-
}
|
|
94
|
-
function parseAcpServeOptions(args, defaultTransport = "stdio") {
|
|
95
|
-
let workspaceRoot;
|
|
96
|
-
let transport = defaultTransport;
|
|
97
|
-
let hostname;
|
|
98
|
-
let port;
|
|
99
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
100
|
-
const arg = args[index];
|
|
101
|
-
if (arg === "--workspace") {
|
|
102
|
-
const value = args[index + 1];
|
|
103
|
-
if (!value) {
|
|
104
|
-
return { transport, error: "Missing value for --workspace" };
|
|
105
|
-
}
|
|
106
|
-
workspaceRoot = value;
|
|
107
|
-
index += 1;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (arg === "--transport") {
|
|
111
|
-
const value = args[index + 1];
|
|
112
|
-
if (!value) {
|
|
113
|
-
return { transport, hostname, port, error: "Missing value for --transport" };
|
|
114
|
-
}
|
|
115
|
-
if (value !== "stdio" && value !== "http") {
|
|
116
|
-
return { transport, hostname, port, error: `Unsupported ACP transport: ${value}` };
|
|
117
|
-
}
|
|
118
|
-
transport = value;
|
|
119
|
-
index += 1;
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
if (arg === "--host") {
|
|
123
|
-
const value = args[index + 1];
|
|
124
|
-
if (!value) {
|
|
125
|
-
return { transport, hostname, port, error: "Missing value for --host" };
|
|
126
|
-
}
|
|
127
|
-
hostname = value;
|
|
128
|
-
index += 1;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (arg === "--port") {
|
|
132
|
-
const value = args[index + 1];
|
|
133
|
-
if (!value) {
|
|
134
|
-
return { transport, hostname, port, error: "Missing value for --port" };
|
|
135
|
-
}
|
|
136
|
-
const parsedPort = Number.parseInt(value, 10);
|
|
137
|
-
if (!Number.isFinite(parsedPort) || parsedPort < 0) {
|
|
138
|
-
return { transport, hostname, port, error: `Invalid ACP port: ${value}` };
|
|
139
|
-
}
|
|
140
|
-
port = parsedPort;
|
|
141
|
-
index += 1;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
return { transport, hostname, port, error: `Unknown option: ${arg}` };
|
|
145
|
-
}
|
|
146
|
-
return { workspaceRoot, transport, hostname, port };
|
|
147
|
-
}
|
|
148
|
-
function parseWorkspaceOnlyOptions(args) {
|
|
149
|
-
let workspaceRoot;
|
|
150
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
151
|
-
const arg = args[index];
|
|
152
|
-
if (arg === "--workspace") {
|
|
153
|
-
const value = args[index + 1];
|
|
154
|
-
if (!value) {
|
|
155
|
-
return { error: "Missing value for --workspace" };
|
|
156
|
-
}
|
|
157
|
-
workspaceRoot = value;
|
|
158
|
-
index += 1;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
return { error: `Unknown option: ${arg}` };
|
|
162
|
-
}
|
|
163
|
-
return { workspaceRoot };
|
|
164
|
-
}
|
|
165
|
-
function parseHttpServeOptions(args, serviceLabel = "HTTP") {
|
|
166
|
-
let workspaceRoot;
|
|
167
|
-
let hostname;
|
|
168
|
-
let port;
|
|
169
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
170
|
-
const arg = args[index];
|
|
171
|
-
if (arg === "--workspace") {
|
|
172
|
-
const value = args[index + 1];
|
|
173
|
-
if (!value) {
|
|
174
|
-
return { hostname, port, error: "Missing value for --workspace" };
|
|
175
|
-
}
|
|
176
|
-
workspaceRoot = value;
|
|
177
|
-
index += 1;
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
if (arg === "--host") {
|
|
181
|
-
const value = args[index + 1];
|
|
182
|
-
if (!value) {
|
|
183
|
-
return { workspaceRoot, port, error: "Missing value for --host" };
|
|
184
|
-
}
|
|
185
|
-
hostname = value;
|
|
186
|
-
index += 1;
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (arg === "--port") {
|
|
190
|
-
const value = args[index + 1];
|
|
191
|
-
if (!value) {
|
|
192
|
-
return { workspaceRoot, hostname, error: "Missing value for --port" };
|
|
193
|
-
}
|
|
194
|
-
const parsedPort = Number.parseInt(value, 10);
|
|
195
|
-
if (!Number.isFinite(parsedPort) || parsedPort < 0) {
|
|
196
|
-
return { workspaceRoot, hostname, error: `Invalid ${serviceLabel} port: ${value}` };
|
|
197
|
-
}
|
|
198
|
-
port = parsedPort;
|
|
199
|
-
index += 1;
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
return { workspaceRoot, hostname, port, error: `Unknown option: ${arg}` };
|
|
203
|
-
}
|
|
204
|
-
return { workspaceRoot, hostname, port };
|
|
205
|
-
}
|
|
206
|
-
function parseRuntimeMcpServeOptions(args, defaultTransport = "stdio") {
|
|
207
|
-
let workspaceRoot;
|
|
208
|
-
let transport = defaultTransport;
|
|
209
|
-
let hostname;
|
|
210
|
-
let port;
|
|
211
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
212
|
-
const arg = args[index];
|
|
213
|
-
if (arg === "--workspace") {
|
|
214
|
-
const value = args[index + 1];
|
|
215
|
-
if (!value) {
|
|
216
|
-
return { transport, hostname, port, error: "Missing value for --workspace" };
|
|
217
|
-
}
|
|
218
|
-
workspaceRoot = value;
|
|
219
|
-
index += 1;
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (arg === "--transport") {
|
|
223
|
-
const value = args[index + 1];
|
|
224
|
-
if (!value) {
|
|
225
|
-
return { transport, hostname, port, error: "Missing value for --transport" };
|
|
226
|
-
}
|
|
227
|
-
if (value !== "stdio" && value !== "streamable-http") {
|
|
228
|
-
return { transport, hostname, port, error: `Unsupported runtime MCP transport: ${value}` };
|
|
229
|
-
}
|
|
230
|
-
transport = value;
|
|
231
|
-
index += 1;
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (arg === "--host") {
|
|
235
|
-
const value = args[index + 1];
|
|
236
|
-
if (!value) {
|
|
237
|
-
return { transport, hostname, port, error: "Missing value for --host" };
|
|
238
|
-
}
|
|
239
|
-
hostname = value;
|
|
240
|
-
index += 1;
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
if (arg === "--port") {
|
|
244
|
-
const value = args[index + 1];
|
|
245
|
-
if (!value) {
|
|
246
|
-
return { transport, hostname, port, error: "Missing value for --port" };
|
|
247
|
-
}
|
|
248
|
-
const parsedPort = Number.parseInt(value, 10);
|
|
249
|
-
if (!Number.isFinite(parsedPort) || parsedPort < 0) {
|
|
250
|
-
return { transport, hostname, port, error: `Invalid runtime MCP port: ${value}` };
|
|
251
|
-
}
|
|
252
|
-
port = parsedPort;
|
|
253
|
-
index += 1;
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
return { transport, hostname, port, error: `Unknown option: ${arg}` };
|
|
257
|
-
}
|
|
258
|
-
return { workspaceRoot, transport, hostname, port };
|
|
259
|
-
}
|
|
260
|
-
function parseChatOptions(args) {
|
|
261
|
-
let workspaceRoot;
|
|
262
|
-
let agentId;
|
|
263
|
-
let sessionId;
|
|
264
|
-
let message;
|
|
265
|
-
let requestEvents = false;
|
|
266
|
-
let transport = "stdio";
|
|
267
|
-
let hostname;
|
|
268
|
-
let port;
|
|
269
|
-
const positional = [];
|
|
270
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
271
|
-
const arg = args[index];
|
|
272
|
-
if (arg === "--workspace"
|
|
273
|
-
|| arg === "-w"
|
|
274
|
-
|| arg === "--agent"
|
|
275
|
-
|| arg === "--session"
|
|
276
|
-
|| arg === "--message"
|
|
277
|
-
|| arg === "--transport"
|
|
278
|
-
|| arg === "--host"
|
|
279
|
-
|| arg === "--port") {
|
|
280
|
-
const value = args[index + 1];
|
|
281
|
-
if (!value) {
|
|
282
|
-
return { workspaceRoot, agentId, sessionId, message, requestEvents, transport, hostname, port, error: `Missing value for ${arg}` };
|
|
283
|
-
}
|
|
284
|
-
if (arg === "--workspace" || arg === "-w") {
|
|
285
|
-
workspaceRoot = value;
|
|
286
|
-
}
|
|
287
|
-
else if (arg === "--agent") {
|
|
288
|
-
agentId = value;
|
|
289
|
-
}
|
|
290
|
-
else if (arg === "--session") {
|
|
291
|
-
sessionId = value;
|
|
292
|
-
}
|
|
293
|
-
else if (arg === "--message") {
|
|
294
|
-
message = value;
|
|
295
|
-
}
|
|
296
|
-
else if (arg === "--transport") {
|
|
297
|
-
if (value !== "stdio" && value !== "http") {
|
|
298
|
-
return { workspaceRoot, agentId, sessionId, message, requestEvents, transport, hostname, port, error: `Unsupported chat transport: ${value}` };
|
|
299
|
-
}
|
|
300
|
-
transport = value;
|
|
301
|
-
}
|
|
302
|
-
else if (arg === "--host") {
|
|
303
|
-
hostname = value;
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
const parsedPort = Number.parseInt(value, 10);
|
|
307
|
-
if (!Number.isFinite(parsedPort) || parsedPort <= 0) {
|
|
308
|
-
return { workspaceRoot, agentId, sessionId, message, requestEvents, transport, hostname, port, error: `Invalid chat port: ${value}` };
|
|
309
|
-
}
|
|
310
|
-
port = parsedPort;
|
|
311
|
-
}
|
|
312
|
-
index += 1;
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
if (arg === "--request-events") {
|
|
316
|
-
requestEvents = true;
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
if (arg.startsWith("-")) {
|
|
320
|
-
return { workspaceRoot, agentId, sessionId, message, requestEvents, transport, hostname, port, error: `Unknown option: ${arg}` };
|
|
321
|
-
}
|
|
322
|
-
positional.push(arg);
|
|
323
|
-
}
|
|
324
|
-
if (!message && positional.length > 0) {
|
|
325
|
-
message = positional.join(" ");
|
|
326
|
-
}
|
|
327
|
-
return { workspaceRoot, agentId, sessionId, message, requestEvents, transport, hostname, port };
|
|
328
|
-
}
|
|
329
|
-
function isTopLevelCliCommand(value) {
|
|
330
|
-
return value === "init"
|
|
331
|
-
|| value === "chat"
|
|
332
|
-
|| value === "acp"
|
|
333
|
-
|| value === "a2a"
|
|
334
|
-
|| value === "ag-ui"
|
|
335
|
-
|| value === "mcp"
|
|
336
|
-
|| value === "runtime"
|
|
337
|
-
|| value === "runtime-mcp";
|
|
338
|
-
}
|
|
339
|
-
function resolveManagedServiceRoot(workspaceRoot) {
|
|
340
|
-
return path.join(workspaceRoot, ".botbotgo", "services");
|
|
341
|
-
}
|
|
342
|
-
function managedServiceStatePath(workspaceRoot, service) {
|
|
343
|
-
return path.join(resolveManagedServiceRoot(workspaceRoot), `${service}.json`);
|
|
344
|
-
}
|
|
345
|
-
function managedServiceLogPath(workspaceRoot, service, stream) {
|
|
346
|
-
return path.join(resolveManagedServiceRoot(workspaceRoot), `${service}.${stream}.log`);
|
|
347
|
-
}
|
|
348
|
-
async function readManagedServiceState(workspaceRoot, service) {
|
|
349
|
-
const statePath = managedServiceStatePath(workspaceRoot, service);
|
|
350
|
-
if (!existsSync(statePath)) {
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
try {
|
|
354
|
-
return JSON.parse(await readFile(statePath, "utf8"));
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
async function writeManagedServiceState(state) {
|
|
361
|
-
const statePath = managedServiceStatePath(state.workspaceRoot, state.service);
|
|
362
|
-
await mkdir(path.dirname(statePath), { recursive: true });
|
|
363
|
-
await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
364
|
-
}
|
|
365
|
-
async function clearManagedServiceState(workspaceRoot, service) {
|
|
366
|
-
await rm(managedServiceStatePath(workspaceRoot, service), { force: true });
|
|
367
|
-
}
|
|
368
|
-
function defaultIsManagedProcessRunning(pid) {
|
|
369
|
-
try {
|
|
370
|
-
process.kill(pid, 0);
|
|
371
|
-
return true;
|
|
372
|
-
}
|
|
373
|
-
catch {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
function defaultSignalManagedProcess(pid, signal) {
|
|
378
|
-
process.kill(pid, signal);
|
|
379
|
-
}
|
|
380
|
-
async function defaultSpawnManagedCliProcess(input) {
|
|
381
|
-
await mkdir(path.dirname(input.stdoutPath), { recursive: true });
|
|
382
|
-
const stdoutFd = openSync(input.stdoutPath, "a");
|
|
383
|
-
const stderrFd = openSync(input.stderrPath, "a");
|
|
384
|
-
const child = spawn(process.execPath, [fileURLToPath(import.meta.url), ...input.args], {
|
|
385
|
-
cwd: input.cwd,
|
|
386
|
-
detached: true,
|
|
387
|
-
stdio: ["ignore", stdoutFd, stderrFd],
|
|
388
|
-
});
|
|
389
|
-
child.unref();
|
|
390
|
-
return { pid: child.pid };
|
|
391
|
-
}
|
|
392
|
-
async function sleep(ms) {
|
|
393
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
394
|
-
}
|
|
395
|
-
function parseRuntimeInspectOptions(args) {
|
|
396
|
-
let workspaceRoot;
|
|
397
|
-
let json = false;
|
|
398
|
-
let once = false;
|
|
399
|
-
let pollMs = 1000;
|
|
400
|
-
let limit = 5;
|
|
401
|
-
let status;
|
|
402
|
-
let state;
|
|
403
|
-
let agentId;
|
|
404
|
-
let sessionId;
|
|
405
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
406
|
-
const arg = args[index];
|
|
407
|
-
if (arg === "--workspace" || arg === "--status" || arg === "--state" || arg === "--agent" || arg === "--session" || arg === "--poll-ms" || arg === "--limit") {
|
|
408
|
-
const value = args[index + 1];
|
|
409
|
-
if (!value) {
|
|
410
|
-
return { json, once, pollMs, limit, status, state, agentId, sessionId, error: `Missing value for ${arg}` };
|
|
411
|
-
}
|
|
412
|
-
if (arg === "--workspace") {
|
|
413
|
-
workspaceRoot = value;
|
|
414
|
-
}
|
|
415
|
-
else if (arg === "--status") {
|
|
416
|
-
status = value;
|
|
417
|
-
}
|
|
418
|
-
else if (arg === "--state") {
|
|
419
|
-
state = value;
|
|
420
|
-
}
|
|
421
|
-
else if (arg === "--agent") {
|
|
422
|
-
agentId = value;
|
|
423
|
-
}
|
|
424
|
-
else if (arg === "--session") {
|
|
425
|
-
sessionId = value;
|
|
426
|
-
}
|
|
427
|
-
else if (arg === "--limit") {
|
|
428
|
-
const parsedLimit = Number.parseInt(value, 10);
|
|
429
|
-
if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
|
|
430
|
-
return { json, once, pollMs, limit, status, state, agentId, sessionId, error: `Invalid limit: ${value}` };
|
|
431
|
-
}
|
|
432
|
-
limit = parsedLimit;
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
const parsedPoll = Number.parseInt(value, 10);
|
|
436
|
-
if (!Number.isFinite(parsedPoll) || parsedPoll <= 0) {
|
|
437
|
-
return { json, once, pollMs, limit, status, state, agentId, sessionId, error: `Invalid poll interval: ${value}` };
|
|
438
|
-
}
|
|
439
|
-
pollMs = parsedPoll;
|
|
440
|
-
}
|
|
441
|
-
index += 1;
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
if (arg === "--json") {
|
|
445
|
-
json = true;
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
if (arg === "--once") {
|
|
449
|
-
once = true;
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
return { workspaceRoot, json, once, pollMs, limit, status, state, agentId, sessionId, error: `Unknown option: ${arg}` };
|
|
453
|
-
}
|
|
454
|
-
return { workspaceRoot, json, once, pollMs, limit, status, state, agentId, sessionId };
|
|
455
|
-
}
|
|
456
|
-
function parseRuntimeExportOptions(args) {
|
|
457
|
-
let workspaceRoot;
|
|
458
|
-
let sessionId;
|
|
459
|
-
let requestId;
|
|
460
|
-
let includeArtifacts = false;
|
|
461
|
-
let includeArtifactContents = false;
|
|
462
|
-
let includeRuntimeHealth = false;
|
|
463
|
-
let json = false;
|
|
464
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
465
|
-
const arg = args[index];
|
|
466
|
-
if (arg === "--artifacts") {
|
|
467
|
-
includeArtifacts = true;
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
if (arg === "--artifact-contents") {
|
|
471
|
-
includeArtifacts = true;
|
|
472
|
-
includeArtifactContents = true;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
if (arg === "--health") {
|
|
476
|
-
includeRuntimeHealth = true;
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
if (arg === "--json") {
|
|
480
|
-
json = true;
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (arg === "--workspace" || arg === "--session" || arg === "--request") {
|
|
484
|
-
const value = args[index + 1];
|
|
485
|
-
if (!value) {
|
|
486
|
-
return {
|
|
487
|
-
workspaceRoot,
|
|
488
|
-
sessionId,
|
|
489
|
-
requestId,
|
|
490
|
-
includeArtifacts,
|
|
491
|
-
includeArtifactContents,
|
|
492
|
-
includeRuntimeHealth,
|
|
493
|
-
json,
|
|
494
|
-
error: `Missing value for ${arg}`,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (arg === "--workspace") {
|
|
498
|
-
workspaceRoot = value;
|
|
499
|
-
}
|
|
500
|
-
else if (arg === "--session") {
|
|
501
|
-
sessionId = value;
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
requestId = value;
|
|
505
|
-
}
|
|
506
|
-
index += 1;
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
workspaceRoot,
|
|
511
|
-
sessionId,
|
|
512
|
-
requestId,
|
|
513
|
-
includeArtifacts,
|
|
514
|
-
includeArtifactContents,
|
|
515
|
-
includeRuntimeHealth,
|
|
516
|
-
json,
|
|
517
|
-
error: `Unknown option: ${arg}`,
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
return {
|
|
521
|
-
workspaceRoot,
|
|
522
|
-
sessionId,
|
|
523
|
-
requestId,
|
|
524
|
-
includeArtifacts,
|
|
525
|
-
includeArtifactContents,
|
|
526
|
-
includeRuntimeHealth,
|
|
527
|
-
json,
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
function parseScheduledRunOptions(args) {
|
|
531
|
-
let workspaceRoot;
|
|
532
|
-
let scheduleId;
|
|
533
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
534
|
-
const arg = args[index];
|
|
535
|
-
if (arg === "--workspace" || arg === "--schedule") {
|
|
536
|
-
const value = args[index + 1];
|
|
537
|
-
if (!value) {
|
|
538
|
-
return { workspaceRoot, scheduleId, error: `Missing value for ${arg}` };
|
|
539
|
-
}
|
|
540
|
-
if (arg === "--workspace") {
|
|
541
|
-
workspaceRoot = value;
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
scheduleId = value;
|
|
545
|
-
}
|
|
546
|
-
index += 1;
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
return { workspaceRoot, scheduleId, error: `Unknown option: ${arg}` };
|
|
550
|
-
}
|
|
551
|
-
return { workspaceRoot, scheduleId };
|
|
552
|
-
}
|
|
553
|
-
function resolveCliWorkspaceRoot(cwd, inputPath) {
|
|
554
|
-
const resolved = path.resolve(cwd, inputPath ?? ".");
|
|
555
|
-
if (existsSync(path.join(resolved, "config", "runtime", "workspace.yaml"))) {
|
|
556
|
-
return resolved;
|
|
557
|
-
}
|
|
558
|
-
if (path.basename(resolved) === "config" && hasCliConfigYaml(resolved)) {
|
|
559
|
-
return path.dirname(resolved);
|
|
560
|
-
}
|
|
561
|
-
return resolved;
|
|
562
|
-
}
|
|
563
|
-
function getWorkspaceConfigPath(workspaceRoot) {
|
|
564
|
-
return path.join(workspaceRoot, "config", "runtime", "workspace.yaml");
|
|
565
|
-
}
|
|
566
|
-
function hasCliConfigYaml(configRoot) {
|
|
567
|
-
if (!existsSync(configRoot) || !statSync(configRoot).isDirectory()) {
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
const pending = [configRoot];
|
|
571
|
-
while (pending.length > 0) {
|
|
572
|
-
const current = pending.pop();
|
|
573
|
-
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
574
|
-
if (entry.isDirectory()) {
|
|
575
|
-
if (entry.name === "node_modules") {
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
pending.push(path.join(current, entry.name));
|
|
579
|
-
continue;
|
|
580
|
-
}
|
|
581
|
-
if (entry.isFile() && /\.ya?ml$/i.test(entry.name)) {
|
|
582
|
-
return true;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return false;
|
|
587
|
-
}
|
|
588
|
-
function validateCliWorkspaceRoot(workspaceRoot, inputPath) {
|
|
589
|
-
if (!existsSync(workspaceRoot)) {
|
|
590
|
-
return undefined;
|
|
591
|
-
}
|
|
592
|
-
if (!statSync(workspaceRoot).isDirectory()) {
|
|
593
|
-
const workspaceLabel = inputPath ? "Workspace path" : "Current directory";
|
|
594
|
-
return `${workspaceLabel} is not a directory: ${workspaceRoot}`;
|
|
595
|
-
}
|
|
596
|
-
return undefined;
|
|
597
|
-
}
|
|
598
|
-
function resolveCliConfigRoot(workspaceRoot) {
|
|
599
|
-
if (path.basename(workspaceRoot) === "config") {
|
|
600
|
-
return workspaceRoot;
|
|
601
|
-
}
|
|
602
|
-
return path.join(workspaceRoot, "config");
|
|
603
|
-
}
|
|
604
|
-
function frameworkCliWorkspaceRoot() {
|
|
605
|
-
const resolved = fileURLToPath(new URL("..", import.meta.url));
|
|
606
|
-
const distSegment = `${path.sep}dist`;
|
|
607
|
-
if (!resolved.endsWith(distSegment)) {
|
|
608
|
-
return resolved;
|
|
609
|
-
}
|
|
610
|
-
const sourceRoot = resolved.slice(0, -distSegment.length);
|
|
611
|
-
if (!existsSync(sourceRoot)) {
|
|
612
|
-
return resolved;
|
|
613
|
-
}
|
|
614
|
-
try {
|
|
615
|
-
return statSync(sourceRoot).mtimeMs >= statSync(resolved).mtimeMs ? sourceRoot : resolved;
|
|
616
|
-
}
|
|
617
|
-
catch {
|
|
618
|
-
return sourceRoot;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
function renderJson(value) {
|
|
622
|
-
return `${JSON.stringify(value, null, 2)}\n`;
|
|
623
|
-
}
|
|
624
|
-
function formatTreeBranch(isLast) {
|
|
625
|
-
return isLast ? "└─" : "├─";
|
|
626
|
-
}
|
|
627
|
-
function formatTreeIndent(ancestorLast) {
|
|
628
|
-
return ancestorLast ? " " : "│ ";
|
|
629
|
-
}
|
|
630
|
-
function applyAnsi(text, code, enabled) {
|
|
631
|
-
if (!enabled) {
|
|
632
|
-
return text;
|
|
633
|
-
}
|
|
634
|
-
return `\x1b[${code}m${text}\x1b[0m`;
|
|
635
|
-
}
|
|
636
|
-
function renderRequestStateBadge(state, color) {
|
|
637
|
-
if (state === "completed") {
|
|
638
|
-
return color ? `\x1b[32;1m✔ DONE\x1b[0m` : "✔ DONE";
|
|
639
|
-
}
|
|
640
|
-
if (state === "running") {
|
|
641
|
-
return color ? `\x1b[36;1m▶ RUNNING\x1b[0m` : "▶ RUNNING";
|
|
642
|
-
}
|
|
643
|
-
if (state === "waiting" || state === "waiting_for_approval") {
|
|
644
|
-
return color ? `\x1b[33;1m⚠ WAITING\x1b[0m` : "⚠ WAITING";
|
|
645
|
-
}
|
|
646
|
-
if (state === "failed") {
|
|
647
|
-
return color ? `\x1b[31;1m✘ FAILED\x1b[0m` : "✘ FAILED";
|
|
648
|
-
}
|
|
649
|
-
if (state === "cancelled") {
|
|
650
|
-
return color ? `\x1b[31m○ CANCELLED\x1b[0m` : "○ CANCELLED";
|
|
651
|
-
}
|
|
652
|
-
if (state === "queued") {
|
|
653
|
-
return color ? `\x1b[38;5;242m· QUEUED\x1b[0m` : "· QUEUED";
|
|
654
|
-
}
|
|
655
|
-
return color ? `\x1b[37m${state.toUpperCase()}\x1b[0m` : state.toUpperCase();
|
|
656
|
-
}
|
|
657
|
-
function summarizeRequestPlan(snapshot) {
|
|
658
|
-
const summary = snapshot.plan.summary;
|
|
659
|
-
const eventCount = flattenRequestExecutionSteps(snapshot).length;
|
|
660
|
-
const parts = [
|
|
661
|
-
`${summary.total} todo${summary.total === 1 ? "" : "s"}`,
|
|
662
|
-
summary.inProgress > 0 ? `${summary.inProgress} active` : undefined,
|
|
663
|
-
summary.completed > 0 ? `${summary.completed} done` : undefined,
|
|
664
|
-
summary.failed > 0 ? `${summary.failed} failed` : undefined,
|
|
665
|
-
eventCount > 0 ? `${eventCount} event${eventCount === 1 ? "" : "s"}` : undefined,
|
|
666
|
-
].filter((part) => typeof part === "string");
|
|
667
|
-
return parts.join(" · ");
|
|
668
|
-
}
|
|
669
|
-
function summarizeRequestOutput(output, color) {
|
|
670
|
-
const trimmed = output.trim();
|
|
671
|
-
if (!trimmed) {
|
|
672
|
-
return applyAnsi("empty", "2", color);
|
|
673
|
-
}
|
|
674
|
-
const normalized = trimmed.replace(/\s+/g, " ");
|
|
675
|
-
const preview = normalized.length > 72 ? `${normalized.slice(0, 69)}...` : normalized;
|
|
676
|
-
return applyAnsi(preview, "2", color);
|
|
677
|
-
}
|
|
678
|
-
function formatCompactClockTime(value) {
|
|
679
|
-
const date = new Date(value);
|
|
680
|
-
if (Number.isNaN(date.getTime())) {
|
|
681
|
-
return value;
|
|
682
|
-
}
|
|
683
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
684
|
-
hour12: false,
|
|
685
|
-
hour: "2-digit",
|
|
686
|
-
minute: "2-digit",
|
|
687
|
-
second: "2-digit",
|
|
688
|
-
}).format(date);
|
|
689
|
-
}
|
|
690
|
-
function renderTimeRange(startedAt, endedAt, detail, color) {
|
|
691
|
-
if (!startedAt && !endedAt) {
|
|
692
|
-
return "";
|
|
693
|
-
}
|
|
694
|
-
const from = startedAt ? formatCompactClockTime(startedAt) : "?";
|
|
695
|
-
const to = endedAt ? formatCompactClockTime(endedAt) : "now";
|
|
696
|
-
const durationMs = typeof detail?.durationMs === "number" ? detail.durationMs : undefined;
|
|
697
|
-
const durationLabel = durationMs !== undefined
|
|
698
|
-
? ` · ${(durationMs / 1000).toFixed(durationMs >= 10_000 ? 0 : durationMs >= 1_000 ? 1 : 3)}s`
|
|
699
|
-
: "";
|
|
700
|
-
return ` ${applyAnsi(`[${from} -> ${to}${durationLabel}]`, "2", color)}`;
|
|
701
|
-
}
|
|
702
|
-
function readStepSkillNames(step) {
|
|
703
|
-
const value = step.detail?.skillNames;
|
|
704
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
705
|
-
}
|
|
706
|
-
function buildRequestStepEventKey(step) {
|
|
707
|
-
return `${step.kind}:${step.id}`;
|
|
708
|
-
}
|
|
709
|
-
function resolveEventTimestamp(step) {
|
|
710
|
-
return step.occurredAt ?? step.endedAt ?? step.startedAt;
|
|
711
|
-
}
|
|
712
|
-
function summarizeRequestStepDetail(step, displayName, color) {
|
|
713
|
-
if (step.kind === "memory" && step.detail?.source === "runtime-durable-memory") {
|
|
714
|
-
const count = typeof step.detail.count === "number" ? step.detail.count : undefined;
|
|
715
|
-
const scopes = Array.isArray(step.detail.scopes)
|
|
716
|
-
? step.detail.scopes.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
717
|
-
: [];
|
|
718
|
-
const countLabel = count !== undefined ? `${count} record${count === 1 ? "" : "s"}` : undefined;
|
|
719
|
-
const scopeLabel = scopes.length > 0 ? scopes.join(", ") : undefined;
|
|
720
|
-
const summary = typeof step.detail.summary === "string" ? step.detail.summary.trim() : "";
|
|
721
|
-
const detailLabel = [countLabel, scopeLabel].filter((value) => Boolean(value)).join(" · ");
|
|
722
|
-
if (detailLabel) {
|
|
723
|
-
return ` ${applyAnsi(`(${detailLabel})`, "2", color)}`;
|
|
724
|
-
}
|
|
725
|
-
if (summary && !summary.toLowerCase().includes(displayName.toLowerCase())) {
|
|
726
|
-
const preview = summary.length > 48 ? `${summary.slice(0, 45)}...` : summary;
|
|
727
|
-
return ` ${applyAnsi(`(${preview})`, "2", color)}`;
|
|
728
|
-
}
|
|
729
|
-
return "";
|
|
730
|
-
}
|
|
731
|
-
if (step.ownerAgentId && step.ownerAgentId !== step.agentId) {
|
|
732
|
-
return ` ${applyAnsi(`(from ${step.ownerAgentId})`, "2", color)}`;
|
|
733
|
-
}
|
|
734
|
-
return "";
|
|
735
|
-
}
|
|
736
|
-
function renderRequestStepLine(step, prefix, isLast, _activeEventId, color = false) {
|
|
737
|
-
const skillNames = step.kind === "skill" ? readStepSkillNames(step) : [];
|
|
738
|
-
const profileDisplayName = step.id.startsWith("profile:") && step.kind === "agent"
|
|
739
|
-
? `${step.name} ${step.action ?? ""}`.trim()
|
|
740
|
-
: undefined;
|
|
741
|
-
const displayName = profileDisplayName
|
|
742
|
-
? profileDisplayName
|
|
743
|
-
: step.kind === "skill" && skillNames.length === 1
|
|
744
|
-
? skillNames[0]
|
|
745
|
-
: step.kind === "skill" && skillNames.length > 1
|
|
746
|
-
? skillNames.join(", ")
|
|
747
|
-
: step.name;
|
|
748
|
-
const icon = STEP_KIND_ICON[step.kind] ?? "·";
|
|
749
|
-
const kindLabel = color
|
|
750
|
-
? `\x1b[38;5;242m${icon} ${step.kind}\x1b[0m`
|
|
751
|
-
: `${icon} ${step.kind}`;
|
|
752
|
-
const stIcon = statusIcon(step.status === "started" ? "in_progress" : step.status, color);
|
|
753
|
-
const nameFormatted = color ? `\x1b[38;5;252m${displayName}\x1b[0m` : displayName;
|
|
754
|
-
const eventTime = resolveEventTimestamp(step);
|
|
755
|
-
const timeLabel = eventTime ? applyAnsi(`[${formatCompactClockTime(eventTime)}]`, "2", color) : applyAnsi("[time?]", "2", color);
|
|
756
|
-
const detailSuffix = summarizeRequestStepDetail(step, displayName, color);
|
|
757
|
-
return `${prefix}${formatTreeBranch(isLast)} ${timeLabel} ${stIcon} ${kindLabel} ${nameFormatted}${detailSuffix}`;
|
|
758
|
-
}
|
|
759
|
-
function resolveRequestStepDisplayName(step) {
|
|
760
|
-
const skillNames = step.kind === "skill" ? readStepSkillNames(step) : [];
|
|
761
|
-
if (step.id.startsWith("profile:") && step.kind === "agent") {
|
|
762
|
-
return `${step.name} ${step.action}`.trim();
|
|
763
|
-
}
|
|
764
|
-
if (step.kind === "skill" && skillNames.length === 1) {
|
|
765
|
-
return skillNames[0];
|
|
766
|
-
}
|
|
767
|
-
if (step.kind === "skill" && skillNames.length > 1) {
|
|
768
|
-
return skillNames.join(", ");
|
|
769
|
-
}
|
|
770
|
-
return step.name;
|
|
771
|
-
}
|
|
772
|
-
function isInternalAgentLifecycleStep(step) {
|
|
773
|
-
return step.kind === "agent" && step.id.startsWith("profile:agent:");
|
|
774
|
-
}
|
|
775
|
-
function isVisibleRequestTreeStep(step) {
|
|
776
|
-
if (step.kind === "skill") {
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
return !isInternalAgentLifecycleStep(step);
|
|
780
|
-
}
|
|
781
|
-
function getVisibleTodoEvents(todo) {
|
|
782
|
-
return todo.events.filter(isVisibleRequestTreeStep);
|
|
783
|
-
}
|
|
784
|
-
function getVisibleRootEvents(snapshot) {
|
|
785
|
-
return snapshot.events.filter(isVisibleRequestTreeStep);
|
|
786
|
-
}
|
|
787
|
-
function flattenRequestExecutionSteps(snapshot) {
|
|
788
|
-
return [
|
|
789
|
-
...snapshot.plan.items.flatMap((item) => getVisibleTodoEvents(item)),
|
|
790
|
-
...getVisibleRootEvents(snapshot),
|
|
791
|
-
];
|
|
792
|
-
}
|
|
793
|
-
function buildExecutionStepEventKey(step) {
|
|
794
|
-
return [
|
|
795
|
-
buildRequestStepEventKey(step),
|
|
796
|
-
step.status,
|
|
797
|
-
step.startedAt ?? "",
|
|
798
|
-
step.endedAt ?? "",
|
|
799
|
-
].join("|");
|
|
800
|
-
}
|
|
801
|
-
function renderRequestSnapshotTree(snapshot, color = false, timingSummary) {
|
|
802
|
-
const lines = [];
|
|
803
|
-
const eventSteps = flattenRequestExecutionSteps(snapshot);
|
|
804
|
-
const rootEvents = getVisibleRootEvents(snapshot);
|
|
805
|
-
// Header row: status badge + agent
|
|
806
|
-
const badge = renderRequestStateBadge(snapshot.state, color);
|
|
807
|
-
const agentLabel = snapshot.agentId
|
|
808
|
-
? (color ? `\x1b[38;5;33m${snapshot.agentId}\x1b[0m` : snapshot.agentId)
|
|
809
|
-
: applyAnsi("unknown", "2", color);
|
|
810
|
-
const headerLabel = color ? `\x1b[1;37mREQUEST\x1b[0m` : "REQUEST";
|
|
811
|
-
lines.push(`${headerLabel} ${badge} ${applyAnsi("agent:", "2", color)}${agentLabel}`);
|
|
812
|
-
// IDs (compact, muted)
|
|
813
|
-
const muted = (s) => applyAnsi(s, "2", color);
|
|
814
|
-
lines.push(`├─ ${muted("session")} ${applyAnsi(ellipsizeChatId(snapshot.sessionId, 24), "2", color)}`);
|
|
815
|
-
lines.push(`├─ ${muted("request")} ${applyAnsi(ellipsizeChatId(snapshot.requestId, 24), "2", color)}`);
|
|
816
|
-
// Summary + timing on one line
|
|
817
|
-
const planSummary = summarizeRequestPlan(snapshot);
|
|
818
|
-
const timing = timingSummary ?? "collecting";
|
|
819
|
-
lines.push(`├─ ${muted("plan")} ${applyAnsi(`v${snapshot.plan.version}`, "2", color)} ${muted("·")} ${planSummary ? applyAnsi(planSummary, "2", color) : muted("no tracked work yet")}`);
|
|
820
|
-
lines.push(`├─ ${muted("timing")} ${applyAnsi(timing, "2", color)}`);
|
|
821
|
-
// Todo items
|
|
822
|
-
lines.push(`├─ ${muted("todos")}`);
|
|
823
|
-
const planPrefix = `${formatTreeIndent(false)}`;
|
|
824
|
-
if (snapshot.plan.items.length === 0) {
|
|
825
|
-
lines.push(`${planPrefix}└─ ${muted("none yet")}`);
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
snapshot.plan.items.forEach((todo, todoIndex) => {
|
|
829
|
-
const todoEvents = getVisibleTodoEvents(todo);
|
|
830
|
-
const todoIsLast = todoIndex === snapshot.plan.items.length - 1
|
|
831
|
-
&& rootEvents.length === 0
|
|
832
|
-
&& !snapshot.approval;
|
|
833
|
-
const todoActive = todo.key === snapshot.activeTodoKey
|
|
834
|
-
? (color ? ` \x1b[36m●\x1b[0m` : " ●")
|
|
835
|
-
: "";
|
|
836
|
-
const tIcon = statusIcon(todo.status, color);
|
|
837
|
-
const todoTimeRange = renderTimeRange(todo.startedAt, todo.endedAt, undefined, color);
|
|
838
|
-
const todoContent = color ? `\x1b[38;5;253m${todo.content}\x1b[0m` : todo.content;
|
|
839
|
-
lines.push(`${planPrefix}${formatTreeBranch(todoIsLast)} ${tIcon} ${todoContent}${todoTimeRange}${todoActive}`);
|
|
840
|
-
if (todoEvents.length > 0) {
|
|
841
|
-
const todoEventPrefix = `${planPrefix}${formatTreeIndent(todoIsLast)}`;
|
|
842
|
-
todoEvents.forEach((step, eventIndex) => {
|
|
843
|
-
lines.push(renderRequestStepLine(step, todoEventPrefix, eventIndex === todoEvents.length - 1, snapshot.activeEventId, color));
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
// Root-level execution events only: keep streamed data outside the tree.
|
|
849
|
-
if (rootEvents.length > 0) {
|
|
850
|
-
lines.push(`├─ ${muted("events")}`);
|
|
851
|
-
rootEvents.forEach((step, index) => {
|
|
852
|
-
lines.push(renderRequestStepLine(step, `${formatTreeIndent(false)}`, index === rootEvents.length - 1 && !snapshot.approval, snapshot.activeEventId, color));
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
// Approval
|
|
856
|
-
if (snapshot.approval) {
|
|
857
|
-
const approvalStatus = snapshot.approval.status ?? "pending";
|
|
858
|
-
const approvalColor = approvalStatus === "approved" ? "32" : approvalStatus === "rejected" ? "31" : "33";
|
|
859
|
-
lines.push(`└─ ${color ? `\x1b[33m⚠ approval\x1b[0m` : "⚠ approval"} ${muted("·")} ${snapshot.approval.toolName ?? "unknown-tool"} ${applyAnsi(`(${approvalStatus})`, approvalColor, color)}`);
|
|
860
|
-
}
|
|
861
|
-
return `${lines.join("\n")}\n`;
|
|
862
|
-
}
|
|
863
|
-
function renderRequestEventContinuation(snapshot, fromIndex, color = false) {
|
|
864
|
-
const eventSteps = flattenRequestExecutionSteps(snapshot).slice(fromIndex);
|
|
865
|
-
if (eventSteps.length === 0) {
|
|
866
|
-
return "";
|
|
867
|
-
}
|
|
868
|
-
const muted = (s) => applyAnsi(s, "2", color);
|
|
869
|
-
const lines = [`${muted("events (continued)")}`];
|
|
870
|
-
eventSteps.forEach((step, index) => {
|
|
871
|
-
lines.push(renderRequestStepLine(step, "", index === eventSteps.length - 1, snapshot.activeEventId, color));
|
|
872
|
-
});
|
|
873
|
-
return `${lines.join("\n")}\n`;
|
|
874
|
-
}
|
|
875
|
-
function buildTodoContinuationSignature(snapshot) {
|
|
876
|
-
return snapshot.plan.items
|
|
877
|
-
.map((item) => [
|
|
878
|
-
item.id ?? "",
|
|
879
|
-
item.content,
|
|
880
|
-
item.status,
|
|
881
|
-
item.ownerAgentId ?? "",
|
|
882
|
-
item.startedAt ?? "",
|
|
883
|
-
item.endedAt ?? "",
|
|
884
|
-
].join("|"))
|
|
885
|
-
.join(";");
|
|
886
|
-
}
|
|
887
|
-
function renderRequestTodoContinuation(snapshot, previousSignature, color = false) {
|
|
888
|
-
const nextSignature = buildTodoContinuationSignature(snapshot);
|
|
889
|
-
if (!nextSignature || nextSignature === previousSignature || snapshot.plan.items.length === 0) {
|
|
890
|
-
return "";
|
|
891
|
-
}
|
|
892
|
-
const muted = (s) => applyAnsi(s, "2", color);
|
|
893
|
-
const lines = [`${muted("todos (continued)")}`];
|
|
894
|
-
snapshot.plan.items.forEach((todo, index) => {
|
|
895
|
-
const tIcon = statusIcon(todo.status, color);
|
|
896
|
-
const todoTimeRange = renderTimeRange(todo.startedAt, todo.endedAt, undefined, color);
|
|
897
|
-
const todoContent = color ? `\x1b[38;5;253m${todo.content}\x1b[0m` : todo.content;
|
|
898
|
-
lines.push(`${formatTreeBranch(index === snapshot.plan.items.length - 1)} ${tIcon} ${todoContent}${todoTimeRange}`);
|
|
899
|
-
});
|
|
900
|
-
return `${lines.join("\n")}\n`;
|
|
901
|
-
}
|
|
902
|
-
function buildTerminalRequestSnapshot(snapshot, result) {
|
|
903
|
-
return {
|
|
904
|
-
...snapshot,
|
|
905
|
-
state: result.state,
|
|
906
|
-
updatedAt: new Date().toISOString(),
|
|
907
|
-
output: result.output,
|
|
908
|
-
activeEventId: undefined,
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
function countRenderedLines(text) {
|
|
912
|
-
return text.replace(/\n$/, "").split("\n").length;
|
|
913
|
-
}
|
|
914
|
-
function isObject(value) {
|
|
915
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
916
|
-
}
|
|
917
|
-
function formatTimestamp(value) {
|
|
918
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
919
|
-
}
|
|
920
|
-
function renderHealthSnapshot(snapshot, workspacePath) {
|
|
921
|
-
const lines = [];
|
|
922
|
-
const status = typeof snapshot.status === "string" ? snapshot.status : "unknown";
|
|
923
|
-
lines.push(`Runtime health ${workspacePath}: ${status}`);
|
|
924
|
-
const checks = isObject(snapshot.checks) ? snapshot.checks : {};
|
|
925
|
-
const checkEntries = Object.entries(checks)
|
|
926
|
-
.filter(([, value]) => isObject(value))
|
|
927
|
-
.map(([name, value]) => {
|
|
928
|
-
const check = value;
|
|
929
|
-
const reason = typeof check.reason === "string" && check.reason.trim().length > 0 ? ` (${check.reason})` : "";
|
|
930
|
-
return ` - ${name}: ${typeof check.status === "string" ? check.status : "unknown"}${reason}`;
|
|
931
|
-
});
|
|
932
|
-
if (checkEntries.length > 0) {
|
|
933
|
-
lines.push("Checks:");
|
|
934
|
-
lines.push(...checkEntries);
|
|
935
|
-
}
|
|
936
|
-
const stats = isObject(snapshot.stats) ? snapshot.stats : {};
|
|
937
|
-
const statEntries = [
|
|
938
|
-
["activeRequestSlots", stats.activeRequestSlots],
|
|
939
|
-
["pendingRequestSlots", stats.pendingRequestSlots],
|
|
940
|
-
["pendingApprovals", stats.pendingApprovals],
|
|
941
|
-
["stuckRequests", stats.stuckRequests],
|
|
942
|
-
["llmSuccessRate1m", stats.llmSuccessRate1m],
|
|
943
|
-
["llmP95LatencyMs1m", stats.llmP95LatencyMs1m],
|
|
944
|
-
]
|
|
945
|
-
.filter(([, value]) => typeof value === "number")
|
|
946
|
-
.map(([name, value]) => ` - ${name}: ${value}`);
|
|
947
|
-
if (statEntries.length > 0) {
|
|
948
|
-
lines.push("Stats:");
|
|
949
|
-
lines.push(...statEntries);
|
|
950
|
-
}
|
|
951
|
-
const symptoms = Array.isArray(snapshot.symptoms) ? snapshot.symptoms.filter(isObject) : [];
|
|
952
|
-
if (symptoms.length > 0) {
|
|
953
|
-
lines.push("Symptoms:");
|
|
954
|
-
lines.push(...symptoms.map((symptom) => {
|
|
955
|
-
const code = typeof symptom.code === "string" ? symptom.code : "unknown";
|
|
956
|
-
const severity = typeof symptom.severity === "string" ? symptom.severity : "unknown";
|
|
957
|
-
const message = typeof symptom.message === "string" ? symptom.message : "";
|
|
958
|
-
return ` - ${code}: ${severity}${message ? ` (${message})` : ""}`;
|
|
959
|
-
}));
|
|
960
|
-
}
|
|
961
|
-
return `${lines.join("\n")}\n`;
|
|
962
|
-
}
|
|
963
|
-
function renderApprovalList(approvals) {
|
|
964
|
-
if (approvals.length === 0) {
|
|
965
|
-
return "No approvals matched.\n";
|
|
966
|
-
}
|
|
967
|
-
return approvals.map((approval) => {
|
|
968
|
-
const status = typeof approval.status === "string" ? approval.status : "unknown";
|
|
969
|
-
const toolName = typeof approval.toolName === "string" ? approval.toolName : "unknown_tool";
|
|
970
|
-
const approvalId = typeof approval.approvalId === "string" ? approval.approvalId : "unknown";
|
|
971
|
-
const sessionId = typeof approval.sessionId === "string" ? ` session=${approval.sessionId}` : "";
|
|
972
|
-
const requestId = typeof approval.requestId === "string" ? ` request=${approval.requestId}` : "";
|
|
973
|
-
const reason = typeof approval.approvalReason === "string" ? ` reason=${approval.approvalReason}` : "";
|
|
974
|
-
const requestedAt = formatTimestamp(approval.requestedAt);
|
|
975
|
-
const resolvedAt = formatTimestamp(approval.resolvedAt);
|
|
976
|
-
const requested = requestedAt ? ` requested=${requestedAt}` : "";
|
|
977
|
-
const resolved = resolvedAt ? ` resolved=${resolvedAt}` : "";
|
|
978
|
-
return `${approvalId} status=${status} tool=${toolName}${sessionId}${requestId}${reason}${requested}${resolved}`;
|
|
979
|
-
}).join("\n") + "\n";
|
|
980
|
-
}
|
|
981
|
-
function renderPlanResultSummary(result) {
|
|
982
|
-
if (typeof result === "string" && result.trim().length > 0) {
|
|
983
|
-
return result.trim();
|
|
984
|
-
}
|
|
985
|
-
if (!isObject(result)) {
|
|
986
|
-
return undefined;
|
|
987
|
-
}
|
|
988
|
-
const summary = typeof result.summary === "string" && result.summary.trim().length > 0
|
|
989
|
-
? result.summary.trim()
|
|
990
|
-
: typeof result.message === "string" && result.message.trim().length > 0
|
|
991
|
-
? result.message.trim()
|
|
992
|
-
: undefined;
|
|
993
|
-
if (summary) {
|
|
994
|
-
return summary;
|
|
995
|
-
}
|
|
996
|
-
const serialized = JSON.stringify(result);
|
|
997
|
-
return serialized && serialized !== "{}" ? serialized : undefined;
|
|
998
|
-
}
|
|
999
|
-
function renderChatPlanState(planState) {
|
|
1000
|
-
const lines = [`[plan v${planState.version}] ${planState.summary.total} todo(s)`];
|
|
1001
|
-
if (planState.summary.total === 0) {
|
|
1002
|
-
lines.push("No todos tracked yet.");
|
|
1003
|
-
return `${lines.join("\n")}\n`;
|
|
1004
|
-
}
|
|
1005
|
-
for (const [index, item] of planState.items.entries()) {
|
|
1006
|
-
lines.push(`${index + 1}. [${item.status}] ${item.content}`);
|
|
1007
|
-
const resultSummary = renderPlanResultSummary(item.result);
|
|
1008
|
-
if (resultSummary) {
|
|
1009
|
-
lines.push(` result: ${resultSummary}`);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
return `${lines.join("\n")}\n`;
|
|
1013
|
-
}
|
|
1014
|
-
function renderRequestList(requests) {
|
|
1015
|
-
if (requests.length === 0) {
|
|
1016
|
-
return "No requests matched.\n";
|
|
1017
|
-
}
|
|
1018
|
-
return requests.map((request) => {
|
|
1019
|
-
const requestId = typeof request.requestId === "string" ? request.requestId : "unknown";
|
|
1020
|
-
const sessionId = typeof request.sessionId === "string" ? request.sessionId : "unknown";
|
|
1021
|
-
const agentId = typeof request.agentId === "string" ? request.agentId : "unknown";
|
|
1022
|
-
const state = typeof request.state === "string" ? request.state : "unknown";
|
|
1023
|
-
const currentAgent = typeof request.currentAgentId === "string" ? ` current=${request.currentAgentId}` : "";
|
|
1024
|
-
const resumable = typeof request.resumable === "boolean" ? ` resumable=${request.resumable}` : "";
|
|
1025
|
-
const updatedAt = formatTimestamp(request.updatedAt);
|
|
1026
|
-
const lastActivityAt = formatTimestamp(request.lastActivityAt);
|
|
1027
|
-
const updated = updatedAt ? ` updated=${updatedAt}` : "";
|
|
1028
|
-
const lastActivity = lastActivityAt ? ` activity=${lastActivityAt}` : "";
|
|
1029
|
-
return `${requestId} session=${sessionId} agent=${agentId}${currentAgent} state=${state}${resumable}${updated}${lastActivity}`;
|
|
1030
|
-
}).join("\n") + "\n";
|
|
1031
|
-
}
|
|
1032
|
-
function renderOperatorOverview(overview, workspacePath) {
|
|
1033
|
-
const lines = [];
|
|
1034
|
-
const summary = typeof overview.summary === "string" ? overview.summary : "";
|
|
1035
|
-
lines.push(`Runtime overview ${workspacePath}`);
|
|
1036
|
-
if (summary) {
|
|
1037
|
-
lines.push(`Summary: ${summary}`);
|
|
1038
|
-
}
|
|
1039
|
-
const health = isObject(overview.health) ? overview.health : {};
|
|
1040
|
-
if (Object.keys(health).length > 0) {
|
|
1041
|
-
lines.push("");
|
|
1042
|
-
lines.push(renderHealthSnapshot(health, workspacePath).trimEnd());
|
|
1043
|
-
}
|
|
1044
|
-
const queue = isObject(overview.queue) ? overview.queue : {};
|
|
1045
|
-
if (Object.keys(queue).length > 0) {
|
|
1046
|
-
lines.push("");
|
|
1047
|
-
lines.push(`Queue: ${typeof queue.summary === "string" ? queue.summary : "unavailable"}`);
|
|
1048
|
-
}
|
|
1049
|
-
const governance = isObject(overview.governance) ? overview.governance : {};
|
|
1050
|
-
if (Object.keys(governance).length > 0) {
|
|
1051
|
-
lines.push("Governance:");
|
|
1052
|
-
lines.push(` - ${typeof governance.summary === "string" ? governance.summary : "unavailable"}`);
|
|
1053
|
-
}
|
|
1054
|
-
const approvals = isObject(overview.approvals) ? overview.approvals : {};
|
|
1055
|
-
const approvalItems = Array.isArray(approvals.items) ? approvals.items.filter(isObject) : [];
|
|
1056
|
-
lines.push(`Pending approvals: ${typeof approvals.total === "number" ? approvals.total : approvalItems.length}`);
|
|
1057
|
-
if (approvalItems.length > 0) {
|
|
1058
|
-
lines.push(renderApprovalList(approvalItems).trimEnd());
|
|
1059
|
-
}
|
|
1060
|
-
const requests = isObject(overview.requests) ? overview.requests : {};
|
|
1061
|
-
const requestItems = Array.isArray(requests.items) ? requests.items.filter(isObject) : [];
|
|
1062
|
-
lines.push(`Active requests: ${typeof requests.total === "number" ? requests.total : requestItems.length}`);
|
|
1063
|
-
if (requestItems.length > 0) {
|
|
1064
|
-
lines.push(renderRequestList(requestItems).trimEnd());
|
|
1065
|
-
}
|
|
1066
|
-
return `${lines.join("\n")}\n`;
|
|
1067
|
-
}
|
|
1068
|
-
function renderChatHelp() {
|
|
1069
|
-
const sections = [
|
|
1070
|
-
{
|
|
1071
|
-
heading: "Context & navigation",
|
|
1072
|
-
rows: [
|
|
1073
|
-
["/context", "Show current agent / session / request"],
|
|
1074
|
-
["/new", "Clear session — start a fresh conversation"],
|
|
1075
|
-
["/agent <id>", "Switch agent for new requests"],
|
|
1076
|
-
["/session", "Print current session id"],
|
|
1077
|
-
["/sessions", "List recent sessions"],
|
|
1078
|
-
["/resume <id>", "Resume an existing session"],
|
|
1079
|
-
["/request [id]", "Show or select active request"],
|
|
1080
|
-
["/requests", "List requests for current session / agent"],
|
|
1081
|
-
],
|
|
1082
|
-
},
|
|
1083
|
-
{
|
|
1084
|
-
heading: "Approvals",
|
|
1085
|
-
rows: [
|
|
1086
|
-
["/approvals", "List pending approvals"],
|
|
1087
|
-
["/approve <id>", "Approve a pending approval"],
|
|
1088
|
-
["/reject <id>", "Reject a pending approval"],
|
|
1089
|
-
["/cancel", "Cancel the latest active request"],
|
|
1090
|
-
],
|
|
1091
|
-
},
|
|
1092
|
-
{
|
|
1093
|
-
heading: "Diagnostics",
|
|
1094
|
-
rows: [
|
|
1095
|
-
["/events", "Persisted events for the latest request"],
|
|
1096
|
-
["/trace", "Trace items for the latest request"],
|
|
1097
|
-
["/health", "Runtime health snapshot"],
|
|
1098
|
-
["/overview", "Runtime operator overview"],
|
|
1099
|
-
],
|
|
1100
|
-
},
|
|
1101
|
-
{
|
|
1102
|
-
heading: "Session",
|
|
1103
|
-
rows: [
|
|
1104
|
-
["/help", "Show this help"],
|
|
1105
|
-
["/exit", "Quit chat"],
|
|
1106
|
-
],
|
|
1107
|
-
},
|
|
1108
|
-
];
|
|
1109
|
-
const allRows = sections.flatMap((s) => s.rows);
|
|
1110
|
-
const cmdW = Math.max(...allRows.map(([cmd]) => cmd.length));
|
|
1111
|
-
const out = [""];
|
|
1112
|
-
for (const section of sections) {
|
|
1113
|
-
out.push(` ${section.heading}`);
|
|
1114
|
-
out.push(` ${"─".repeat(section.heading.length)}`);
|
|
1115
|
-
for (const [cmd, desc] of section.rows) {
|
|
1116
|
-
out.push(` ${cmd.padEnd(cmdW)} ${desc}`);
|
|
1117
|
-
}
|
|
1118
|
-
out.push("");
|
|
1119
|
-
}
|
|
1120
|
-
out.push(" Starter tasks:");
|
|
1121
|
-
out.push(" ─────────────");
|
|
1122
|
-
out.push(" Inspect this workspace and explain the main entry points.");
|
|
1123
|
-
out.push(" Review this project structure before making any edits.");
|
|
1124
|
-
out.push(" Find the likeliest config issue and propose the smallest fix.");
|
|
1125
|
-
out.push("");
|
|
1126
|
-
return out.join("\n");
|
|
1127
|
-
}
|
|
1128
|
-
function trimAsciiBlock(block) {
|
|
1129
|
-
return block
|
|
1130
|
-
.split("\n")
|
|
1131
|
-
.map((line) => line.replace(/\s+$/u, ""))
|
|
1132
|
-
.join("\n");
|
|
1133
|
-
}
|
|
1134
|
-
/** Figlet Small font — trimmed for clean right edge */
|
|
1135
|
-
const CHAT_ASCII_BOTBOTGO = trimAsciiBlock(" ____ ___ _____ ____ ___ _____ ____ ___ \n" +
|
|
1136
|
-
" | __ ) / _ \\_ _| __ ) / _ \\_ _/ ___|/ _ \\ \n" +
|
|
1137
|
-
" | _ \\| | | || | | _ \\| | | || || | _| | | |\n" +
|
|
1138
|
-
" | |_) | |_| || | | |_) | |_| || || |_| | |_| |\n" +
|
|
1139
|
-
" |____/ \\___/ |_| |____/ \\___/ |_| \\____|\\___/");
|
|
1140
|
-
const CHAT_ASCII_AGENT_HARNESS = trimAsciiBlock(" _ ____ _____ _ _ _____ _ _ _ ____ _ _ _____ ____ ____ \n" +
|
|
1141
|
-
" / \\ / ___| ____| \\ | |_ _| | | | | / \\ | _ \\| \\ | | ____/ ___/ ___| \n" +
|
|
1142
|
-
" / _ \\| | _| _| | \\| | | | | |_| | / _ \\ | |_) | \\| | _| \\___ \\___ \\ \n" +
|
|
1143
|
-
" / ___ \\ |_| | |___| |\\ | | | | _ |/ ___ \\| _ <| |\\ | |___ ___) |__) |\n" +
|
|
1144
|
-
" /_/ \\_\\____|_____|_| \\_| |_| |_| |_/_/ \\_\\_| \\_\\_| \\_|_____|____/____/");
|
|
1145
|
-
/**
|
|
1146
|
-
* Solid fills (one 256-color per Figlet row, no gradient):
|
|
1147
|
-
* BOTBOTGO ≈ US flag: blue / white / red / white / red stripes top→bottom.
|
|
1148
|
-
* AGENT HARNESS ≈ 中国国旗: upper rows gold (星区黄), lower rows field red.
|
|
1149
|
-
*/
|
|
1150
|
-
const CHAT_LOGO_BRAND_LINE_COLORS = [25, 238, 160, 238, 160];
|
|
1151
|
-
const CHAT_LOGO_PRODUCT_LINE_COLORS = [136, 136, 160, 160, 160];
|
|
1152
|
-
/** Step-kind icons for the request tree */
|
|
1153
|
-
const STEP_KIND_ICON = {
|
|
1154
|
-
llm: "◆",
|
|
1155
|
-
tool: "⚙",
|
|
1156
|
-
skill: "★",
|
|
1157
|
-
agent: "◉",
|
|
1158
|
-
memory: "◈",
|
|
1159
|
-
approval: "⚠",
|
|
1160
|
-
};
|
|
1161
|
-
/** Status icons for todos and steps */
|
|
1162
|
-
function statusIcon(status, color) {
|
|
1163
|
-
if (status === "completed" || status === "done")
|
|
1164
|
-
return applyAnsi("✔", "32", color);
|
|
1165
|
-
if (status === "in_progress" || status === "started")
|
|
1166
|
-
return applyAnsi("▶", "36", color);
|
|
1167
|
-
if (status === "failed")
|
|
1168
|
-
return applyAnsi("✘", "31", color);
|
|
1169
|
-
if (status === "cancelled")
|
|
1170
|
-
return applyAnsi("○", "31", color);
|
|
1171
|
-
if (status === "pending" || status === "queued")
|
|
1172
|
-
return applyAnsi("·", "238", color);
|
|
1173
|
-
return applyAnsi("?", "37", color);
|
|
1174
|
-
}
|
|
1175
|
-
/** One foreground color per entire line — flat, no relief shading */
|
|
1176
|
-
function colorizeSolidAsciiBlock(block, lineColors, enabled) {
|
|
1177
|
-
const trimmed = trimAsciiBlock(block);
|
|
1178
|
-
const lines = trimmed.split("\n");
|
|
1179
|
-
if (!enabled) {
|
|
1180
|
-
return trimmed;
|
|
1181
|
-
}
|
|
1182
|
-
return lines
|
|
1183
|
-
.map((line, i) => {
|
|
1184
|
-
const code = lineColors[Math.min(i, lineColors.length - 1)] ?? lineColors[lineColors.length - 1];
|
|
1185
|
-
return `\x1b[38;5;${code}m${line}\x1b[0m`;
|
|
1186
|
-
})
|
|
1187
|
-
.join("\n");
|
|
1188
|
-
}
|
|
1189
|
-
function ellipsizeChatId(value, maxChars) {
|
|
1190
|
-
if (value.length <= maxChars) {
|
|
1191
|
-
return value;
|
|
1192
|
-
}
|
|
1193
|
-
if (maxChars <= 1) {
|
|
1194
|
-
return "…";
|
|
1195
|
-
}
|
|
1196
|
-
return `${value.slice(0, maxChars - 1)}…`;
|
|
1197
|
-
}
|
|
1198
|
-
export function renderChatPromptLine(input) {
|
|
1199
|
-
const c = input.color;
|
|
1200
|
-
const agent = input.agentId ?? "—";
|
|
1201
|
-
const sessionShort = input.sessionId ? ellipsizeChatId(input.sessionId, 8) : null;
|
|
1202
|
-
if (!c) {
|
|
1203
|
-
const ctx = sessionShort ? ` [${sessionShort}]` : "";
|
|
1204
|
-
return `\n ─────────────────────────────────────────────\n ❯ agent:${agent}${ctx} › `;
|
|
1205
|
-
}
|
|
1206
|
-
const sep = ` \x1b[38;5;236m${"─".repeat(45)}\x1b[0m`;
|
|
1207
|
-
// left: brand tag + agent
|
|
1208
|
-
const brand = `\x1b[38;5;33m◆\x1b[0m \x1b[1;37magent\x1b[0m\x1b[38;5;242m:\x1b[0m\x1b[38;5;252m${agent}\x1b[0m`;
|
|
1209
|
-
// right: session pill (muted)
|
|
1210
|
-
const sessionTag = sessionShort
|
|
1211
|
-
? ` \x1b[38;5;238m[\x1b[0m\x1b[38;5;244m${sessionShort}\x1b[0m\x1b[38;5;238m]\x1b[0m`
|
|
1212
|
-
: "";
|
|
1213
|
-
const arrow = `\x1b[38;5;33m›\x1b[0m`;
|
|
1214
|
-
return `\n${sep}\n ${brand}${sessionTag} ${arrow} `;
|
|
1215
|
-
}
|
|
1216
|
-
async function* iterateChatLines(rl, nextPrompt) {
|
|
1217
|
-
rl.setPrompt(nextPrompt());
|
|
1218
|
-
rl.prompt();
|
|
1219
|
-
const asyncIterator = rl[Symbol.asyncIterator]();
|
|
1220
|
-
while (true) {
|
|
1221
|
-
const step = await asyncIterator.next();
|
|
1222
|
-
if (step.done) {
|
|
1223
|
-
break;
|
|
1224
|
-
}
|
|
1225
|
-
yield step.value;
|
|
1226
|
-
rl.setPrompt(nextPrompt());
|
|
1227
|
-
rl.prompt();
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
export function renderChatBanner(input) {
|
|
1231
|
-
const color = input.color === true;
|
|
1232
|
-
// — Logo block (single ASCII art) —
|
|
1233
|
-
const brandArt = colorizeSolidAsciiBlock(CHAT_ASCII_BOTBOTGO, CHAT_LOGO_BRAND_LINE_COLORS, color);
|
|
1234
|
-
const productArt = colorizeSolidAsciiBlock(CHAT_ASCII_AGENT_HARNESS, CHAT_LOGO_PRODUCT_LINE_COLORS, color);
|
|
1235
|
-
const logoWidth = Math.max(...CHAT_ASCII_BOTBOTGO.split("\n").map((l) => l.length), ...CHAT_ASCII_AGENT_HARNESS.split("\n").map((l) => l.length));
|
|
1236
|
-
const ruleLen = Math.min(logoWidth + 4, 96);
|
|
1237
|
-
const dimRule = (ch, len) => color ? `\x1b[38;5;237m${ch.repeat(len)}\x1b[0m` : ch.repeat(len);
|
|
1238
|
-
// — Info panel —
|
|
1239
|
-
const labelColor = color ? "\x1b[38;5;242m" : "";
|
|
1240
|
-
const valueColor = color ? "\x1b[38;5;252m" : "";
|
|
1241
|
-
const accentColor = color ? "\x1b[38;5;33m" : ""; // bright cyan-blue
|
|
1242
|
-
const resetColor = color ? "\x1b[0m" : "";
|
|
1243
|
-
const mutedColor = color ? "\x1b[38;5;238m" : "";
|
|
1244
|
-
const borderColor = color ? "\x1b[38;5;237m" : "";
|
|
1245
|
-
const kvRow = (label, value, labelW, accent = false) => {
|
|
1246
|
-
const lPad = label.padEnd(labelW);
|
|
1247
|
-
const lFormatted = `${labelColor}${lPad}${resetColor}`;
|
|
1248
|
-
const vFormatted = accent
|
|
1249
|
-
? `${accentColor}${value}${resetColor}`
|
|
1250
|
-
: `${valueColor}${value}${resetColor}`;
|
|
1251
|
-
return ` ${borderColor}│${resetColor} ${lFormatted} ${mutedColor}·${resetColor} ${vFormatted}`;
|
|
1252
|
-
};
|
|
1253
|
-
const rows = [
|
|
1254
|
-
["workspace", input.workspacePath, false],
|
|
1255
|
-
["transport", input.transport, false],
|
|
1256
|
-
];
|
|
1257
|
-
if (input.agentId)
|
|
1258
|
-
rows.push(["agent", input.agentId, true]);
|
|
1259
|
-
if (input.sessionId)
|
|
1260
|
-
rows.push(["session", input.sessionId, false]);
|
|
1261
|
-
const labelW = Math.max(...rows.map(([l]) => l.length));
|
|
1262
|
-
const panelInner = Math.max(...rows.map(([, v]) => labelW + 4 + v.length), 44);
|
|
1263
|
-
const hLine = (l, m, r) => color
|
|
1264
|
-
? ` ${borderColor}${l}${resetColor}${borderColor}${m.repeat(panelInner + 2)}${r}${resetColor}`
|
|
1265
|
-
: ` ${l}${m.repeat(panelInner + 2)}${r}`;
|
|
1266
|
-
const versionTagText = "agent-harness";
|
|
1267
|
-
const versionTag = color
|
|
1268
|
-
? `${mutedColor}agent-harness${resetColor}`
|
|
1269
|
-
: versionTagText;
|
|
1270
|
-
const versionDividerSpan = Math.max(36, ruleLen);
|
|
1271
|
-
const versionGap = 2;
|
|
1272
|
-
const versionRuleBudget = Math.max(24, versionDividerSpan - versionTagText.length - versionGap * 2);
|
|
1273
|
-
const versionRuleLeftLen = Math.floor(versionRuleBudget / 2);
|
|
1274
|
-
const versionRuleRightLen = versionRuleBudget - versionRuleLeftLen;
|
|
1275
|
-
const hint = color
|
|
1276
|
-
? ` ${mutedColor}Type \x1b[38;5;246m/help\x1b[0m${mutedColor} for commands · \x1b[38;5;246m/exit\x1b[0m${mutedColor} to quit${resetColor}`
|
|
1277
|
-
: ` Type /help for commands · /exit to quit`;
|
|
1278
|
-
const lines = [
|
|
1279
|
-
"",
|
|
1280
|
-
brandArt,
|
|
1281
|
-
"",
|
|
1282
|
-
` ${dimRule("─", Math.max(36, ruleLen - 4))}`,
|
|
1283
|
-
"",
|
|
1284
|
-
productArt,
|
|
1285
|
-
"",
|
|
1286
|
-
` ${dimRule("─", versionRuleLeftLen)}${" ".repeat(versionGap)}${versionTag}${" ".repeat(versionGap)}${dimRule("─", versionRuleRightLen)}`,
|
|
1287
|
-
"",
|
|
1288
|
-
hLine("╭", "─", "╮"),
|
|
1289
|
-
...rows.map(([l, v, accent]) => kvRow(l, v, labelW, accent)),
|
|
1290
|
-
hLine("╰", "─", "╯"),
|
|
1291
|
-
"",
|
|
1292
|
-
hint,
|
|
1293
|
-
"",
|
|
1294
|
-
];
|
|
1295
|
-
return lines.join("\n");
|
|
1296
|
-
}
|
|
1297
|
-
function renderRequestEvents(events) {
|
|
1298
|
-
if (events.length === 0) {
|
|
1299
|
-
return "No events recorded.\n";
|
|
1300
|
-
}
|
|
1301
|
-
return events.map((event) => {
|
|
1302
|
-
const timestamp = formatTimestamp(event.timestamp) ?? "unknown-time";
|
|
1303
|
-
const eventType = typeof event.eventType === "string" ? event.eventType : "unknown-event";
|
|
1304
|
-
return `${timestamp} ${eventType}`;
|
|
1305
|
-
}).join("\n") + "\n";
|
|
1306
|
-
}
|
|
1307
|
-
function renderSessionSummaries(summaries) {
|
|
1308
|
-
if (summaries.length === 0) {
|
|
1309
|
-
return "No sessions recorded.\n";
|
|
1310
|
-
}
|
|
1311
|
-
return summaries.map((summary) => {
|
|
1312
|
-
const sessionId = typeof summary.sessionId === "string" ? summary.sessionId : "unknown";
|
|
1313
|
-
const entryAgentId = typeof summary.entryAgentId === "string" ? ` agent=${summary.entryAgentId}` : "";
|
|
1314
|
-
const state = typeof summary.currentState === "string" ? ` state=${summary.currentState}` : "";
|
|
1315
|
-
const messageCount = typeof summary.messageCount === "number" ? ` messages=${summary.messageCount}` : "";
|
|
1316
|
-
const title = typeof summary.title === "string" && summary.title.trim().length > 0 ? ` title=${summary.title}` : "";
|
|
1317
|
-
const snippet = typeof summary.snippet === "string" && summary.snippet.trim().length > 0 ? ` snippet=${summary.snippet}` : "";
|
|
1318
|
-
return `${sessionId}${entryAgentId}${state}${messageCount}${title}${snippet}`;
|
|
1319
|
-
}).join("\n") + "\n";
|
|
1320
|
-
}
|
|
1321
|
-
function renderRequestTraceItems(items) {
|
|
1322
|
-
if (items.length === 0) {
|
|
1323
|
-
return "No trace items recorded.\n";
|
|
1324
|
-
}
|
|
1325
|
-
return items.map((item) => {
|
|
1326
|
-
const surfaceItem = isObject(item.surfaceItem) ? item.surfaceItem : {};
|
|
1327
|
-
const kind = typeof surfaceItem.kind === "string" ? surfaceItem.kind : "unknown";
|
|
1328
|
-
const id = typeof surfaceItem.id === "string" ? surfaceItem.id : typeof surfaceItem.name === "string" ? surfaceItem.name : "unknown";
|
|
1329
|
-
const agentId = typeof surfaceItem.agentId === "string" ? ` agent=${surfaceItem.agentId}` : "";
|
|
1330
|
-
return `${kind}:${id}${agentId}`;
|
|
1331
|
-
}).join("\n") + "\n";
|
|
1332
|
-
}
|
|
1333
|
-
function renderChatContext(input) {
|
|
1334
|
-
const rows = [
|
|
1335
|
-
["agent", input.agentId ?? "—"],
|
|
1336
|
-
["session", input.sessionId ?? "—"],
|
|
1337
|
-
["request", input.requestId ?? "—"],
|
|
1338
|
-
];
|
|
1339
|
-
const labelW = Math.max(...rows.map(([l]) => l.length));
|
|
1340
|
-
const lines = rows.map(([label, value]) => ` ${label.padEnd(labelW)} ${value}`);
|
|
1341
|
-
return ["", " Context", " ───────", ...lines, ""].join("\n");
|
|
1342
|
-
}
|
|
1343
|
-
function normalizeChatCommand(line) {
|
|
1344
|
-
const trimmed = line.trim();
|
|
1345
|
-
if (!trimmed.startsWith("/")) {
|
|
1346
|
-
return null;
|
|
1347
|
-
}
|
|
1348
|
-
const [name, ...rest] = trimmed.slice(1).split(/\s+/);
|
|
1349
|
-
return {
|
|
1350
|
-
name,
|
|
1351
|
-
arg: rest.length > 0 ? rest.join(" ") : undefined,
|
|
1352
|
-
};
|
|
1353
|
-
}
|
|
1354
|
-
function asRecord(value) {
|
|
1355
|
-
return typeof value === "object" && value !== null ? value : undefined;
|
|
1356
|
-
}
|
|
1357
|
-
function readYamlFile(filePath) {
|
|
1358
|
-
const parsed = YAML.parse(readFileSync(filePath, "utf8"));
|
|
1359
|
-
return interpolateEnvPlaceholders(parsed, filePath);
|
|
1360
|
-
}
|
|
1361
|
-
function readCliConfigObjects(workspaceRoot) {
|
|
1362
|
-
const roots = [
|
|
1363
|
-
resolveCliConfigRoot(frameworkCliWorkspaceRoot()),
|
|
1364
|
-
resolveCliConfigRoot(workspaceRoot),
|
|
1365
|
-
];
|
|
1366
|
-
const merged = new Map();
|
|
1367
|
-
for (const configRoot of roots) {
|
|
1368
|
-
if (!hasCliConfigYaml(configRoot)) {
|
|
1369
|
-
continue;
|
|
1370
|
-
}
|
|
1371
|
-
const files = [];
|
|
1372
|
-
const pending = [configRoot];
|
|
1373
|
-
while (pending.length > 0) {
|
|
1374
|
-
const current = pending.pop();
|
|
1375
|
-
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
1376
|
-
const entryPath = path.join(current, entry.name);
|
|
1377
|
-
if (entry.isDirectory()) {
|
|
1378
|
-
if (entry.name === "node_modules") {
|
|
1379
|
-
continue;
|
|
1380
|
-
}
|
|
1381
|
-
pending.push(entryPath);
|
|
1382
|
-
continue;
|
|
1383
|
-
}
|
|
1384
|
-
if (entry.isFile() && /\.ya?ml$/i.test(entry.name)) {
|
|
1385
|
-
files.push(entryPath);
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
for (const filePath of files.sort()) {
|
|
1390
|
-
const content = readFileSync(filePath, "utf8");
|
|
1391
|
-
for (const document of YAML.parseAllDocuments(content)) {
|
|
1392
|
-
const resolved = interpolateEnvPlaceholders(document.toJSON(), filePath);
|
|
1393
|
-
const typed = asRecord(resolved);
|
|
1394
|
-
if (!typed) {
|
|
1395
|
-
continue;
|
|
1396
|
-
}
|
|
1397
|
-
if (typed.kind === "Models" && Array.isArray(typed.spec)) {
|
|
1398
|
-
for (const item of typed.spec) {
|
|
1399
|
-
const record = asRecord(item);
|
|
1400
|
-
if (!record) {
|
|
1401
|
-
continue;
|
|
1402
|
-
}
|
|
1403
|
-
const id = typeof record.name === "string" ? record.name : record.id;
|
|
1404
|
-
if (typeof id !== "string" || !id) {
|
|
1405
|
-
continue;
|
|
1406
|
-
}
|
|
1407
|
-
merged.set(`Model/${id}`, {
|
|
1408
|
-
...record,
|
|
1409
|
-
kind: "Model",
|
|
1410
|
-
id,
|
|
1411
|
-
});
|
|
1412
|
-
}
|
|
1413
|
-
continue;
|
|
1414
|
-
}
|
|
1415
|
-
const metadata = asRecord(typed.metadata);
|
|
1416
|
-
const spec = asRecord(typed.spec);
|
|
1417
|
-
if (typeof typed.kind === "string" && spec) {
|
|
1418
|
-
const id = typeof metadata?.name === "string" ? metadata.name : typed.id;
|
|
1419
|
-
if (typeof id !== "string" || !id) {
|
|
1420
|
-
continue;
|
|
1421
|
-
}
|
|
1422
|
-
merged.set(`${typed.kind}/${id}`, {
|
|
1423
|
-
...spec,
|
|
1424
|
-
kind: typed.kind,
|
|
1425
|
-
id,
|
|
1426
|
-
});
|
|
1427
|
-
continue;
|
|
1428
|
-
}
|
|
1429
|
-
const kind = typeof typed.kind === "string" ? typed.kind : undefined;
|
|
1430
|
-
const id = typeof typed.id === "string" ? typed.id : undefined;
|
|
1431
|
-
if (!kind || !id) {
|
|
1432
|
-
continue;
|
|
1433
|
-
}
|
|
1434
|
-
merged.set(`${kind}/${id}`, typed);
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
return Array.from(merged.values());
|
|
1439
|
-
}
|
|
1440
|
-
function getWorkspaceDefaultAgentId(workspaceRoot) {
|
|
1441
|
-
const runtime = readCliConfigObjects(workspaceRoot).find((item) => item.kind === "Runtime" && item.id === "default");
|
|
1442
|
-
const routing = asRecord(runtime?.routing);
|
|
1443
|
-
return typeof routing?.defaultAgentId === "string" && routing.defaultAgentId.trim().length > 0 ? routing.defaultAgentId.trim() : undefined;
|
|
1444
|
-
}
|
|
1445
|
-
function getAgentModelRef(workspaceRoot, agentId) {
|
|
1446
|
-
const agent = readCliConfigObjects(workspaceRoot).find((item) => item.kind === "Agent" && item.id === agentId);
|
|
1447
|
-
return typeof agent?.modelRef === "string" && agent.modelRef.trim().length > 0 ? agent.modelRef.trim() : undefined;
|
|
1448
|
-
}
|
|
1449
|
-
function getModelInfo(workspaceRoot, modelRef) {
|
|
1450
|
-
const modelName = modelRef.startsWith("model/") ? modelRef.slice("model/".length) : modelRef;
|
|
1451
|
-
const model = readCliConfigObjects(workspaceRoot).find((item) => item.kind === "Model" && item.id === modelName);
|
|
1452
|
-
if (!model) {
|
|
1453
|
-
return undefined;
|
|
1454
|
-
}
|
|
1455
|
-
return {
|
|
1456
|
-
provider: typeof model.provider === "string" ? model.provider : undefined,
|
|
1457
|
-
model: typeof model.model === "string" ? model.model : undefined,
|
|
1458
|
-
baseUrl: typeof model.baseUrl === "string" ? model.baseUrl : undefined,
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
function readChatWorkspaceModelInfo(workspaceRoot, agentId) {
|
|
1462
|
-
try {
|
|
1463
|
-
const resolvedAgentId = agentId ?? getWorkspaceDefaultAgentId(workspaceRoot);
|
|
1464
|
-
if (!resolvedAgentId) {
|
|
1465
|
-
return getModelInfo(workspaceRoot, "model/default");
|
|
1466
|
-
}
|
|
1467
|
-
const modelRef = getAgentModelRef(workspaceRoot, resolvedAgentId) ?? "model/default";
|
|
1468
|
-
return getModelInfo(workspaceRoot, modelRef);
|
|
1469
|
-
}
|
|
1470
|
-
catch {
|
|
1471
|
-
return undefined;
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
function renderProviderFailureHint(modelInfo) {
|
|
1475
|
-
if (!modelInfo?.provider) {
|
|
1476
|
-
return undefined;
|
|
1477
|
-
}
|
|
1478
|
-
if (modelInfo.provider === "ollama") {
|
|
1479
|
-
const modelText = modelInfo.model ? ` and ensure \`${modelInfo.model}\` is available` : "";
|
|
1480
|
-
return `Hint: start Ollama${modelInfo.baseUrl ? ` at ${modelInfo.baseUrl}` : ""}${modelText}. Example: \`ollama serve\`${modelInfo.model ? ` and \`ollama pull ${modelInfo.model}\`` : ""}.`;
|
|
1481
|
-
}
|
|
1482
|
-
if (modelInfo.provider === "openai") {
|
|
1483
|
-
return "Hint: verify network access and that `OPENAI_API_KEY` is set for this shell.";
|
|
1484
|
-
}
|
|
1485
|
-
if (modelInfo.provider === "anthropic") {
|
|
1486
|
-
return "Hint: verify network access and that `ANTHROPIC_API_KEY` is set for this shell.";
|
|
1487
|
-
}
|
|
1488
|
-
if (modelInfo.provider === "google" || modelInfo.provider === "google-genai" || modelInfo.provider === "gemini") {
|
|
1489
|
-
return "Hint: verify network access and that the configured Google API key is available in this shell.";
|
|
1490
|
-
}
|
|
1491
|
-
if (modelInfo.provider === "openai-compatible") {
|
|
1492
|
-
return `Hint: verify the configured endpoint${modelInfo.baseUrl ? ` (${modelInfo.baseUrl})` : ""} and the required API key for that provider.`;
|
|
1493
|
-
}
|
|
1494
|
-
return undefined;
|
|
1495
|
-
}
|
|
1496
|
-
export function renderChatRuntimeFailure(output, modelInfo) {
|
|
1497
|
-
const trimmed = output.trim();
|
|
1498
|
-
if (!trimmed.startsWith("runtime_error=")) {
|
|
1499
|
-
return output;
|
|
1500
|
-
}
|
|
1501
|
-
const normalized = trimmed.toLowerCase();
|
|
1502
|
-
if (!normalized.includes("fetch failed") &&
|
|
1503
|
-
!normalized.includes("connection error") &&
|
|
1504
|
-
!normalized.includes("timed out") &&
|
|
1505
|
-
!normalized.includes("404 page not found")) {
|
|
1506
|
-
return output;
|
|
1507
|
-
}
|
|
1508
|
-
const lines = [trimmed];
|
|
1509
|
-
if (modelInfo?.provider || modelInfo?.model) {
|
|
1510
|
-
lines.push(`provider=${modelInfo?.provider ?? "unknown"}${modelInfo?.model ? ` model=${modelInfo.model}` : ""}`);
|
|
1511
|
-
}
|
|
1512
|
-
if (modelInfo?.baseUrl) {
|
|
1513
|
-
lines.push(`endpoint=${modelInfo.baseUrl}`);
|
|
1514
|
-
}
|
|
1515
|
-
if (normalized.includes("404 page not found") && modelInfo?.provider === "ollama") {
|
|
1516
|
-
lines.push("Hint: the configured endpoint responded, but it does not look like an Ollama API route. Check that `baseUrl` points at the Ollama server root and that no other service is bound to this port.");
|
|
1517
|
-
}
|
|
1518
|
-
const hint = renderProviderFailureHint(modelInfo);
|
|
1519
|
-
if (hint) {
|
|
1520
|
-
lines.push(hint);
|
|
1521
|
-
}
|
|
1522
|
-
return lines.join("\n");
|
|
1523
|
-
}
|
|
1524
|
-
function renderChatTextChunk(text, modelInfo) {
|
|
1525
|
-
return renderChatRuntimeFailure(text, modelInfo);
|
|
1526
|
-
}
|
|
1527
|
-
function renderChatRequestRunning(input) {
|
|
1528
|
-
const parts = [
|
|
1529
|
-
input.sessionId ? `session=${input.sessionId}` : undefined,
|
|
1530
|
-
input.requestId ? `request=${input.requestId}` : undefined,
|
|
1531
|
-
input.agentId ? `agent=${input.agentId}` : undefined,
|
|
1532
|
-
"running: waiting for model output",
|
|
1533
|
-
].filter((part) => typeof part === "string" && part.length > 0);
|
|
1534
|
-
return `\n${parts.join(" ")}\n`;
|
|
1535
|
-
}
|
|
1536
|
-
function summarizeChatToolError(output) {
|
|
1537
|
-
if (typeof output === "string") {
|
|
1538
|
-
const trimmed = output.trim();
|
|
1539
|
-
return trimmed.length > 0 ? truncateChatToolPreview(trimmed, 240) : "failed";
|
|
1540
|
-
}
|
|
1541
|
-
if (typeof output === "number" || typeof output === "boolean") {
|
|
1542
|
-
return String(output);
|
|
1543
|
-
}
|
|
1544
|
-
if (!output || typeof output !== "object") {
|
|
1545
|
-
return "failed";
|
|
1546
|
-
}
|
|
1547
|
-
const typed = output;
|
|
1548
|
-
const content = extractChatToolTextContent(output);
|
|
1549
|
-
if (content && content.trim().length > 0) {
|
|
1550
|
-
return truncateChatToolPreview(content.trim(), 240);
|
|
1551
|
-
}
|
|
1552
|
-
const summary = typeof typed.summary === "object" && typed.summary !== null ? typed.summary : undefined;
|
|
1553
|
-
if (summary) {
|
|
1554
|
-
return truncateChatToolPreview(JSON.stringify(summary, null, 2), 240);
|
|
1555
|
-
}
|
|
1556
|
-
return truncateChatToolPreview(JSON.stringify(output, null, 2), 240);
|
|
1557
|
-
}
|
|
1558
|
-
function truncateChatToolPreview(value, maxChars = 800) {
|
|
1559
|
-
if (value.length <= maxChars) {
|
|
1560
|
-
return value;
|
|
1561
|
-
}
|
|
1562
|
-
return `${value.slice(0, maxChars - 15)}\n...[truncated]`;
|
|
1563
|
-
}
|
|
1564
|
-
function extractChatToolTextContent(value) {
|
|
1565
|
-
if (typeof value === "string") {
|
|
1566
|
-
return value;
|
|
1567
|
-
}
|
|
1568
|
-
if (Array.isArray(value)) {
|
|
1569
|
-
return value
|
|
1570
|
-
.map((item) => extractChatToolTextContent(item))
|
|
1571
|
-
.filter((item) => item.trim().length > 0)
|
|
1572
|
-
.join("\n");
|
|
1573
|
-
}
|
|
1574
|
-
if (!value || typeof value !== "object") {
|
|
1575
|
-
return "";
|
|
1576
|
-
}
|
|
1577
|
-
const typed = value;
|
|
1578
|
-
if (typeof typed.text === "string") {
|
|
1579
|
-
return typed.text;
|
|
1580
|
-
}
|
|
1581
|
-
if (typeof typed.content === "string") {
|
|
1582
|
-
return typed.content;
|
|
1583
|
-
}
|
|
1584
|
-
if (typed.content !== undefined) {
|
|
1585
|
-
const nestedContent = extractChatToolTextContent(typed.content);
|
|
1586
|
-
if (nestedContent.trim().length > 0) {
|
|
1587
|
-
return nestedContent;
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
if (typed.kwargs !== undefined) {
|
|
1591
|
-
const nestedKwargs = extractChatToolTextContent(typed.kwargs);
|
|
1592
|
-
if (nestedKwargs.trim().length > 0) {
|
|
1593
|
-
return nestedKwargs;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
if (typed.message !== undefined) {
|
|
1597
|
-
const nestedMessage = extractChatToolTextContent(typed.message);
|
|
1598
|
-
if (nestedMessage.trim().length > 0) {
|
|
1599
|
-
return nestedMessage;
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
if (typed.body !== undefined) {
|
|
1603
|
-
const nestedBody = extractChatToolTextContent(typed.body);
|
|
1604
|
-
if (nestedBody.trim().length > 0) {
|
|
1605
|
-
return nestedBody;
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
if (typed.answer !== undefined) {
|
|
1609
|
-
const nestedAnswer = extractChatToolTextContent(typed.answer);
|
|
1610
|
-
if (nestedAnswer.trim().length > 0) {
|
|
1611
|
-
return nestedAnswer;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return "";
|
|
1615
|
-
}
|
|
1616
|
-
function summarizeChatToolResult(output, isError) {
|
|
1617
|
-
if (!isError) {
|
|
1618
|
-
return "completed";
|
|
1619
|
-
}
|
|
1620
|
-
return summarizeChatToolError(output);
|
|
1621
|
-
}
|
|
1622
|
-
export async function probeChatWorkspace(input) {
|
|
1623
|
-
const modelInfo = readChatWorkspaceModelInfo(input.workspaceRoot, input.agentId);
|
|
1624
|
-
if (!modelInfo?.provider || modelInfo.provider !== "ollama" || !modelInfo.baseUrl) {
|
|
1625
|
-
return undefined;
|
|
1626
|
-
}
|
|
1627
|
-
try {
|
|
1628
|
-
const response = await fetch(new URL("/api/tags", modelInfo.baseUrl), {
|
|
1629
|
-
method: "GET",
|
|
1630
|
-
headers: { accept: "application/json" },
|
|
1631
|
-
});
|
|
1632
|
-
if (response.ok) {
|
|
1633
|
-
return undefined;
|
|
1634
|
-
}
|
|
1635
|
-
if (response.status === 404) {
|
|
1636
|
-
return renderChatRuntimeFailure("runtime_error=404 page not found", modelInfo);
|
|
1637
|
-
}
|
|
1638
|
-
return renderChatRuntimeFailure(`runtime_error=HTTP ${response.status} ${response.statusText}`.trim(), modelInfo);
|
|
1639
|
-
}
|
|
1640
|
-
catch (error) {
|
|
1641
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1642
|
-
return renderChatRuntimeFailure(`runtime_error=${message}`, modelInfo);
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
export function isChatServerNoiseLine(line) {
|
|
1646
|
-
const trimmed = line.trim();
|
|
1647
|
-
if (!trimmed) {
|
|
1648
|
-
return true;
|
|
1649
|
-
}
|
|
1650
|
-
return (trimmed.startsWith("Serving ACP over stdio from ") ||
|
|
1651
|
-
trimmed === "langsmith/experimental/sandbox is in alpha. This feature is experimental, and breaking changes are expected." ||
|
|
1652
|
-
trimmed === "llamaindex was already imported. This breaks constructor checks and will lead to issues!");
|
|
1653
|
-
}
|
|
1654
|
-
const CLI_IGNORED_STDERR_WARNING_SNIPPETS = [
|
|
1655
|
-
"llamaindex was already imported. This breaks constructor checks and will lead to issues!",
|
|
1656
|
-
"MaxListenersExceededWarning: Possible EventEmitter memory leak detected.",
|
|
1657
|
-
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
|
1658
|
-
];
|
|
1659
|
-
function installCliWriteListenerGuard(streams, minimum = 64) {
|
|
1660
|
-
const restore = [];
|
|
1661
|
-
for (const stream of streams) {
|
|
1662
|
-
if (!stream?.getMaxListeners || !stream?.setMaxListeners) {
|
|
1663
|
-
continue;
|
|
1664
|
-
}
|
|
1665
|
-
const previous = stream.getMaxListeners();
|
|
1666
|
-
if (previous === 0 || previous >= minimum) {
|
|
1667
|
-
continue;
|
|
1668
|
-
}
|
|
1669
|
-
stream.setMaxListeners(minimum);
|
|
1670
|
-
restore.push(() => stream.setMaxListeners?.(previous));
|
|
1671
|
-
}
|
|
1672
|
-
return () => {
|
|
1673
|
-
for (const reset of restore.reverse()) {
|
|
1674
|
-
reset();
|
|
1675
|
-
}
|
|
1676
|
-
};
|
|
1677
|
-
}
|
|
1678
|
-
function installCliEmitterListenerGuard(minimum = 64) {
|
|
1679
|
-
const previous = EventEmitter.defaultMaxListeners;
|
|
1680
|
-
const hadPrototypeMaxListeners = Object.prototype.hasOwnProperty.call(EventEmitter.prototype, "_maxListeners");
|
|
1681
|
-
const previousPrototypeMaxListeners = EventEmitter.prototype._maxListeners;
|
|
1682
|
-
if (previous === 0 || previous >= minimum) {
|
|
1683
|
-
return () => undefined;
|
|
1684
|
-
}
|
|
1685
|
-
EventEmitter.defaultMaxListeners = minimum;
|
|
1686
|
-
EventEmitter.prototype._maxListeners = minimum;
|
|
1687
|
-
return () => {
|
|
1688
|
-
EventEmitter.defaultMaxListeners = previous;
|
|
1689
|
-
if (hadPrototypeMaxListeners) {
|
|
1690
|
-
EventEmitter.prototype._maxListeners = previousPrototypeMaxListeners;
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1693
|
-
delete EventEmitter.prototype._maxListeners;
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
|
-
function installCliStderrNoiseFilter(enabled) {
|
|
1697
|
-
if (!enabled) {
|
|
1698
|
-
return () => undefined;
|
|
1699
|
-
}
|
|
1700
|
-
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
1701
|
-
process.stderr.write = ((chunk, encoding, callback) => {
|
|
1702
|
-
const text = typeof chunk === "string"
|
|
1703
|
-
? chunk
|
|
1704
|
-
: chunk instanceof Uint8Array
|
|
1705
|
-
? Buffer.from(chunk).toString(typeof encoding === "string" ? encoding : undefined)
|
|
1706
|
-
: "";
|
|
1707
|
-
if (CLI_IGNORED_STDERR_WARNING_SNIPPETS.some((snippet) => text.includes(snippet))) {
|
|
1708
|
-
if (typeof encoding === "function") {
|
|
1709
|
-
encoding();
|
|
1710
|
-
}
|
|
1711
|
-
else if (typeof callback === "function") {
|
|
1712
|
-
callback();
|
|
1713
|
-
}
|
|
1714
|
-
return true;
|
|
1715
|
-
}
|
|
1716
|
-
if (typeof encoding === "function") {
|
|
1717
|
-
return originalStderrWrite(chunk, encoding);
|
|
1718
|
-
}
|
|
1719
|
-
return originalStderrWrite(chunk, encoding, callback);
|
|
1720
|
-
});
|
|
1721
|
-
return () => {
|
|
1722
|
-
process.stderr.write = originalStderrWrite;
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
function installCliWarningFilter() {
|
|
1726
|
-
const originalEmitWarning = process.emitWarning.bind(process);
|
|
1727
|
-
process.emitWarning = ((warning, ...args) => {
|
|
1728
|
-
const text = typeof warning === "string" ? warning : warning?.message ?? "";
|
|
1729
|
-
const name = typeof warning === "string" ? String(args[1] ?? args[0] ?? "") : warning?.name ?? "";
|
|
1730
|
-
if (CLI_IGNORED_STDERR_WARNING_SNIPPETS.some((snippet) => text.includes(snippet))
|
|
1731
|
-
|| name === "MaxListenersExceededWarning") {
|
|
1732
|
-
return;
|
|
1733
|
-
}
|
|
1734
|
-
return originalEmitWarning(warning, ...args);
|
|
1735
|
-
});
|
|
1736
|
-
return () => {
|
|
1737
|
-
process.emitWarning = originalEmitWarning;
|
|
1738
|
-
};
|
|
1739
|
-
}
|
|
1740
|
-
function terminateChatSubprocess(child, signal = "SIGTERM") {
|
|
1741
|
-
if (child.killed || child.exitCode !== null || child.signalCode !== null) {
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
try {
|
|
1745
|
-
child.kill(signal);
|
|
1746
|
-
}
|
|
1747
|
-
catch {
|
|
1748
|
-
// Ignore termination races during shutdown.
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
export function installChatSubprocessExitGuard(child, host = process) {
|
|
1752
|
-
let cleanedUp = false;
|
|
1753
|
-
let forceKillTimer;
|
|
1754
|
-
const handleParentExit = () => {
|
|
1755
|
-
terminateChatSubprocess(child, "SIGTERM");
|
|
1756
|
-
if (!forceKillTimer && child.exitCode === null && child.signalCode === null) {
|
|
1757
|
-
forceKillTimer = setTimeout(() => {
|
|
1758
|
-
terminateChatSubprocess(child, "SIGKILL");
|
|
1759
|
-
}, 2_000);
|
|
1760
|
-
forceKillTimer.unref?.();
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1763
|
-
const cleanup = () => {
|
|
1764
|
-
if (cleanedUp) {
|
|
1765
|
-
return;
|
|
1766
|
-
}
|
|
1767
|
-
cleanedUp = true;
|
|
1768
|
-
if (forceKillTimer) {
|
|
1769
|
-
clearTimeout(forceKillTimer);
|
|
1770
|
-
forceKillTimer = undefined;
|
|
1771
|
-
}
|
|
1772
|
-
host.off("beforeExit", handleParentExit);
|
|
1773
|
-
host.off("exit", handleParentExit);
|
|
1774
|
-
child.off("close", cleanup);
|
|
1775
|
-
child.off("exit", cleanup);
|
|
1776
|
-
};
|
|
1777
|
-
host.on("beforeExit", handleParentExit);
|
|
1778
|
-
host.on("exit", handleParentExit);
|
|
1779
|
-
child.on("close", cleanup);
|
|
1780
|
-
child.on("exit", cleanup);
|
|
1781
|
-
return cleanup;
|
|
1782
|
-
}
|
|
1783
|
-
async function streamChatMessage(input) {
|
|
1784
|
-
const requestStartedAt = Date.now();
|
|
1785
|
-
let firstSnapshotAt;
|
|
1786
|
-
let firstDataAt;
|
|
1787
|
-
let latestSnapshot;
|
|
1788
|
-
let latestSessionId = input.sessionId;
|
|
1789
|
-
let latestRequestId;
|
|
1790
|
-
let latestAgentId = input.agentId;
|
|
1791
|
-
let wroteContent = false;
|
|
1792
|
-
let wroteRenderableBlocks = false;
|
|
1793
|
-
let lastRenderedRequestTreeKey;
|
|
1794
|
-
let lastRenderedRequestTree = "";
|
|
1795
|
-
let lastRenderedRequestTreeLineCount = 0;
|
|
1796
|
-
let lastRenderedRequestTreeAt = 0;
|
|
1797
|
-
let announcedRunningState = false;
|
|
1798
|
-
let requestTreeVisible = false;
|
|
1799
|
-
let requestTreePersisted = false;
|
|
1800
|
-
let liveRequestAnnotations = [];
|
|
1801
|
-
let persistedRequestTreeEventCount = 0;
|
|
1802
|
-
let persistedRequestTreeTodoSignature = "";
|
|
1803
|
-
const requestTreeRenderThrottleMs = 75;
|
|
1804
|
-
let suppressRequestTreeRendering = false;
|
|
1805
|
-
let lastStableRequestTreeKey;
|
|
1806
|
-
let stdoutWriteChain = Promise.resolve();
|
|
1807
|
-
let stderrWriteChain = Promise.resolve();
|
|
1808
|
-
const enqueueChatWrite = (sink, _stream, chain, message) => chain.then(async () => {
|
|
1809
|
-
sink(message);
|
|
1810
|
-
});
|
|
1811
|
-
const summarizeRequestTreeSteps = (steps) => steps
|
|
1812
|
-
.map((step) => [
|
|
1813
|
-
step.id ?? "",
|
|
1814
|
-
step.kind,
|
|
1815
|
-
step.name,
|
|
1816
|
-
step.status,
|
|
1817
|
-
step.startedAt ?? "",
|
|
1818
|
-
step.endedAt ?? "",
|
|
1819
|
-
].join("|"))
|
|
1820
|
-
.join(";");
|
|
1821
|
-
const buildRequestSnapshotRenderKey = (snapshot) => {
|
|
1822
|
-
const todoSignatures = snapshot.plan.items
|
|
1823
|
-
.map((item) => [
|
|
1824
|
-
item.id ?? "",
|
|
1825
|
-
item.content,
|
|
1826
|
-
item.status,
|
|
1827
|
-
item.ownerAgentId ?? "",
|
|
1828
|
-
item.startedAt ?? "",
|
|
1829
|
-
item.endedAt ?? "",
|
|
1830
|
-
item.result === undefined ? "" : String(item.result),
|
|
1831
|
-
summarizeRequestTreeSteps(item.events),
|
|
1832
|
-
].join("|"))
|
|
1833
|
-
.join(";");
|
|
1834
|
-
const approvalSignature = snapshot.approval
|
|
1835
|
-
? [
|
|
1836
|
-
snapshot.approval.approvalId,
|
|
1837
|
-
snapshot.approval.status,
|
|
1838
|
-
snapshot.approval.toolName ?? "",
|
|
1839
|
-
].join("|")
|
|
1840
|
-
: "";
|
|
1841
|
-
return [
|
|
1842
|
-
snapshot.sessionId,
|
|
1843
|
-
snapshot.requestId,
|
|
1844
|
-
snapshot.state,
|
|
1845
|
-
snapshot.agentId ?? "",
|
|
1846
|
-
snapshot.plan.version,
|
|
1847
|
-
snapshot.plan.updatedAt,
|
|
1848
|
-
snapshot.plan.summary.total,
|
|
1849
|
-
snapshot.plan.summary.inProgress,
|
|
1850
|
-
snapshot.plan.summary.completed,
|
|
1851
|
-
snapshot.plan.summary.failed,
|
|
1852
|
-
approvalSignature,
|
|
1853
|
-
summarizeRequestTreeSteps(snapshot.events),
|
|
1854
|
-
todoSignatures,
|
|
1855
|
-
].join("||");
|
|
1856
|
-
};
|
|
1857
|
-
const buildPlanSnapshotRenderKey = (snapshot) => [
|
|
1858
|
-
snapshot.plan.version,
|
|
1859
|
-
snapshot.plan.updatedAt,
|
|
1860
|
-
snapshot.plan.summary.total,
|
|
1861
|
-
snapshot.plan.summary.inProgress,
|
|
1862
|
-
snapshot.plan.summary.completed,
|
|
1863
|
-
snapshot.plan.summary.failed,
|
|
1864
|
-
snapshot.plan.items
|
|
1865
|
-
.map((item) => [
|
|
1866
|
-
item.id ?? "",
|
|
1867
|
-
item.content,
|
|
1868
|
-
item.status,
|
|
1869
|
-
item.ownerAgentId ?? "",
|
|
1870
|
-
item.startedAt ?? "",
|
|
1871
|
-
item.endedAt ?? "",
|
|
1872
|
-
item.result === undefined ? "" : String(item.result),
|
|
1873
|
-
].join("|"))
|
|
1874
|
-
.join(";"),
|
|
1875
|
-
].join("||");
|
|
1876
|
-
const formatPerfClock = (timestamp) => {
|
|
1877
|
-
const value = new Date(timestamp);
|
|
1878
|
-
return value.toLocaleTimeString("en-US", {
|
|
1879
|
-
hour12: false,
|
|
1880
|
-
hour: "2-digit",
|
|
1881
|
-
minute: "2-digit",
|
|
1882
|
-
second: "2-digit",
|
|
1883
|
-
fractionalSecondDigits: 3,
|
|
1884
|
-
});
|
|
1885
|
-
};
|
|
1886
|
-
const formatElapsed = (timestamp) => `${((timestamp - requestStartedAt) / 1000).toFixed(3)}s`;
|
|
1887
|
-
const buildTimingSummary = (completedAt) => {
|
|
1888
|
-
const parts = [`start ${formatPerfClock(requestStartedAt)}`];
|
|
1889
|
-
if (firstSnapshotAt) {
|
|
1890
|
-
parts.push(`first event +${formatElapsed(firstSnapshotAt)}`);
|
|
1891
|
-
}
|
|
1892
|
-
if (firstDataAt) {
|
|
1893
|
-
parts.push(`first data +${formatElapsed(firstDataAt)}`);
|
|
1894
|
-
}
|
|
1895
|
-
if (completedAt) {
|
|
1896
|
-
parts.push(`done +${formatElapsed(completedAt)}`);
|
|
1897
|
-
}
|
|
1898
|
-
else {
|
|
1899
|
-
parts.push(`live +${formatElapsed(Date.now())}`);
|
|
1900
|
-
}
|
|
1901
|
-
return parts.join(" · ");
|
|
1902
|
-
};
|
|
1903
|
-
const buildLiveRequestTreeBlock = () => {
|
|
1904
|
-
if (!lastRenderedRequestTree) {
|
|
1905
|
-
return "";
|
|
1906
|
-
}
|
|
1907
|
-
const annotationBlock = liveRequestAnnotations.length > 0
|
|
1908
|
-
? `\n${liveRequestAnnotations.join("")}`
|
|
1909
|
-
: "";
|
|
1910
|
-
return `${lastRenderedRequestTree}${annotationBlock}`;
|
|
1911
|
-
};
|
|
1912
|
-
const clearLiveRequestTree = () => {
|
|
1913
|
-
if (!input.requestEvents || !input.liveRequestTree || !requestTreeVisible || lastRenderedRequestTreeLineCount <= 0) {
|
|
1914
|
-
return;
|
|
1915
|
-
}
|
|
1916
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, `\x1b[${lastRenderedRequestTreeLineCount}F\x1b[0J`);
|
|
1917
|
-
requestTreeVisible = false;
|
|
1918
|
-
};
|
|
1919
|
-
const drawLiveRequestTree = () => {
|
|
1920
|
-
if (!input.requestEvents || !input.liveRequestTree || !lastRenderedRequestTree) {
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
const block = buildLiveRequestTreeBlock();
|
|
1924
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, block);
|
|
1925
|
-
lastRenderedRequestTreeLineCount = countRenderedLines(block);
|
|
1926
|
-
requestTreeVisible = true;
|
|
1927
|
-
};
|
|
1928
|
-
const persistLiveRequestTree = () => {
|
|
1929
|
-
if (!input.requestEvents || !input.liveRequestTree || requestTreePersisted || !lastRenderedRequestTree) {
|
|
1930
|
-
return;
|
|
1931
|
-
}
|
|
1932
|
-
if (requestTreeVisible && lastRenderedRequestTreeLineCount > 0) {
|
|
1933
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, `\x1b[${lastRenderedRequestTreeLineCount}F\x1b[0J`);
|
|
1934
|
-
requestTreeVisible = false;
|
|
1935
|
-
}
|
|
1936
|
-
const combinedBlock = buildLiveRequestTreeBlock();
|
|
1937
|
-
const treeBlock = combinedBlock.endsWith("\n")
|
|
1938
|
-
? combinedBlock
|
|
1939
|
-
: `${combinedBlock}\n`;
|
|
1940
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, treeBlock);
|
|
1941
|
-
persistedRequestTreeEventCount = latestSnapshot ? flattenRequestExecutionSteps(latestSnapshot).length : 0;
|
|
1942
|
-
persistedRequestTreeTodoSignature = latestSnapshot ? buildTodoContinuationSignature(latestSnapshot) : "";
|
|
1943
|
-
requestTreePersisted = true;
|
|
1944
|
-
};
|
|
1945
|
-
const enqueueOrderedStdout = (message) => {
|
|
1946
|
-
const barrier = Promise.allSettled([stdoutWriteChain, stderrWriteChain]).then(() => undefined);
|
|
1947
|
-
stdoutWriteChain = enqueueChatWrite(input.stdout, input.stdoutStream, barrier, message);
|
|
1948
|
-
};
|
|
1949
|
-
const writeChatStdout = (message) => {
|
|
1950
|
-
if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering) {
|
|
1951
|
-
clearLiveRequestTree();
|
|
1952
|
-
enqueueOrderedStdout(message);
|
|
1953
|
-
drawLiveRequestTree();
|
|
1954
|
-
return;
|
|
1955
|
-
}
|
|
1956
|
-
if (input.requestEvents && input.liveRequestTree) {
|
|
1957
|
-
enqueueOrderedStdout(message);
|
|
1958
|
-
return;
|
|
1959
|
-
}
|
|
1960
|
-
stdoutWriteChain = enqueueChatWrite(input.stdout, input.stdoutStream, stdoutWriteChain, message);
|
|
1961
|
-
};
|
|
1962
|
-
const writeChatStderr = (message) => {
|
|
1963
|
-
if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering) {
|
|
1964
|
-
clearLiveRequestTree();
|
|
1965
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
|
|
1966
|
-
drawLiveRequestTree();
|
|
1967
|
-
return;
|
|
1968
|
-
}
|
|
1969
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
|
|
1970
|
-
};
|
|
1971
|
-
const renderPlanSnapshot = (snapshot) => {
|
|
1972
|
-
return;
|
|
1973
|
-
};
|
|
1974
|
-
const suspendRequestTreeRendering = () => {
|
|
1975
|
-
if (!input.requestEvents || !input.liveRequestTree) {
|
|
1976
|
-
return;
|
|
1977
|
-
}
|
|
1978
|
-
persistLiveRequestTree();
|
|
1979
|
-
suppressRequestTreeRendering = true;
|
|
1980
|
-
clearLiveRequestTree();
|
|
1981
|
-
};
|
|
1982
|
-
const renderExecutionStepEvents = (snapshot) => {
|
|
1983
|
-
return;
|
|
1984
|
-
};
|
|
1985
|
-
const renderContentBlocks = (contentBlocks, agentId) => {
|
|
1986
|
-
latestAgentId = agentId || latestAgentId;
|
|
1987
|
-
if (!wroteContent) {
|
|
1988
|
-
const rendered = contentBlocks
|
|
1989
|
-
.map((block) => {
|
|
1990
|
-
if (typeof block === "string") {
|
|
1991
|
-
return block;
|
|
1992
|
-
}
|
|
1993
|
-
if (block && typeof block === "object" && "text" in block && typeof block.text === "string") {
|
|
1994
|
-
return block.text;
|
|
1995
|
-
}
|
|
1996
|
-
return "";
|
|
1997
|
-
})
|
|
1998
|
-
.filter((block) => block.trim().length > 0)
|
|
1999
|
-
.join("");
|
|
2000
|
-
if (rendered) {
|
|
2001
|
-
if (input.requestEvents) {
|
|
2002
|
-
suspendRequestTreeRendering();
|
|
2003
|
-
}
|
|
2004
|
-
writeChatStdout(renderChatTextChunk(rendered, input.modelInfo));
|
|
2005
|
-
wroteRenderableBlocks = true;
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
};
|
|
2009
|
-
let result;
|
|
2010
|
-
try {
|
|
2011
|
-
result = await input.client.request({
|
|
2012
|
-
...(input.agentId ? { agentId: input.agentId } : {}),
|
|
2013
|
-
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
2014
|
-
input: input.message,
|
|
2015
|
-
eventListener(snapshot) {
|
|
2016
|
-
latestSessionId = snapshot.sessionId || latestSessionId;
|
|
2017
|
-
latestRequestId = snapshot.requestId || latestRequestId;
|
|
2018
|
-
latestAgentId = snapshot.agentId || latestAgentId;
|
|
2019
|
-
latestSnapshot = snapshot;
|
|
2020
|
-
firstSnapshotAt ??= Date.now();
|
|
2021
|
-
if (input.requestEvents && !suppressRequestTreeRendering) {
|
|
2022
|
-
const now = Date.now();
|
|
2023
|
-
const nextTreeKey = buildRequestSnapshotRenderKey(snapshot);
|
|
2024
|
-
const terminalSnapshot = snapshot.state !== "queued" && snapshot.state !== "claimed" && snapshot.state !== "running";
|
|
2025
|
-
const snapshotCarriesRenderableOutput = snapshot.output.trim().length > 0;
|
|
2026
|
-
const shouldRenderSnapshot = !snapshotCarriesRenderableOutput
|
|
2027
|
-
&& nextTreeKey !== lastRenderedRequestTreeKey
|
|
2028
|
-
&& nextTreeKey !== lastStableRequestTreeKey
|
|
2029
|
-
&& ((terminalSnapshot && !input.liveRequestTree)
|
|
2030
|
-
|| !input.liveRequestTree
|
|
2031
|
-
|| now - lastRenderedRequestTreeAt >= requestTreeRenderThrottleMs);
|
|
2032
|
-
if (shouldRenderSnapshot) {
|
|
2033
|
-
lastRenderedRequestTree = renderRequestSnapshotTree(snapshot, input.colorRequestTree === true, buildTimingSummary());
|
|
2034
|
-
if (input.liveRequestTree) {
|
|
2035
|
-
clearLiveRequestTree();
|
|
2036
|
-
drawLiveRequestTree();
|
|
2037
|
-
}
|
|
2038
|
-
else {
|
|
2039
|
-
input.stderr(`\n${lastRenderedRequestTree}`);
|
|
2040
|
-
}
|
|
2041
|
-
lastRenderedRequestTreeKey = nextTreeKey;
|
|
2042
|
-
if (!snapshot.activeEventId && snapshot.state !== "running") {
|
|
2043
|
-
lastStableRequestTreeKey = nextTreeKey;
|
|
2044
|
-
}
|
|
2045
|
-
lastRenderedRequestTreeAt = now;
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
if ((input.showRunningState ?? true) && !input.requestEvents && !announcedRunningState && !wroteContent && !wroteRenderableBlocks) {
|
|
2049
|
-
input.stderr(renderChatRequestRunning({
|
|
2050
|
-
sessionId: snapshot.sessionId,
|
|
2051
|
-
requestId: snapshot.requestId,
|
|
2052
|
-
agentId: snapshot.agentId,
|
|
2053
|
-
}));
|
|
2054
|
-
announcedRunningState = true;
|
|
2055
|
-
}
|
|
2056
|
-
renderExecutionStepEvents(snapshot);
|
|
2057
|
-
renderPlanSnapshot({
|
|
2058
|
-
sessionId: snapshot.sessionId,
|
|
2059
|
-
requestId: snapshot.requestId,
|
|
2060
|
-
plan: {
|
|
2061
|
-
version: snapshot.plan.version,
|
|
2062
|
-
updatedAt: snapshot.plan.updatedAt,
|
|
2063
|
-
items: snapshot.plan.items.map((item) => ({
|
|
2064
|
-
id: item.id,
|
|
2065
|
-
content: item.content,
|
|
2066
|
-
status: item.status,
|
|
2067
|
-
ownerAgentId: item.ownerAgentId,
|
|
2068
|
-
startedAt: item.startedAt,
|
|
2069
|
-
endedAt: item.endedAt,
|
|
2070
|
-
result: item.result,
|
|
2071
|
-
metadata: item.metadata,
|
|
2072
|
-
})),
|
|
2073
|
-
summary: snapshot.plan.summary,
|
|
2074
|
-
},
|
|
2075
|
-
});
|
|
2076
|
-
},
|
|
2077
|
-
dataListener(delta) {
|
|
2078
|
-
latestSessionId = delta.sessionId || latestSessionId;
|
|
2079
|
-
latestRequestId = delta.requestId || latestRequestId;
|
|
2080
|
-
firstDataAt ??= Date.now();
|
|
2081
|
-
if (delta.type === "output.text.delta") {
|
|
2082
|
-
latestAgentId = delta.agentId || latestAgentId;
|
|
2083
|
-
if (input.requestEvents) {
|
|
2084
|
-
suspendRequestTreeRendering();
|
|
2085
|
-
}
|
|
2086
|
-
writeChatStdout(renderChatTextChunk(delta.text, input.modelInfo));
|
|
2087
|
-
wroteContent = true;
|
|
2088
|
-
return;
|
|
2089
|
-
}
|
|
2090
|
-
if (delta.type === "output.content-blocks") {
|
|
2091
|
-
suspendRequestTreeRendering();
|
|
2092
|
-
renderContentBlocks(delta.contentBlocks, delta.agentId);
|
|
2093
|
-
return;
|
|
2094
|
-
}
|
|
2095
|
-
if (delta.type === "tool.result") {
|
|
2096
|
-
latestAgentId = delta.agentId || latestAgentId;
|
|
2097
|
-
if ((input.showToolResults ?? true) && !input.requestEvents) {
|
|
2098
|
-
writeChatStderr(`\n[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}] [tool:${delta.toolName}] ${summarizeChatToolResult(delta.output, delta.isError === true)}${delta.isError ? " (error)" : ""}\n`);
|
|
2099
|
-
}
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
if (delta.type === "progress.commentary") {
|
|
2103
|
-
latestAgentId = delta.agentId || latestAgentId;
|
|
2104
|
-
if (wroteContent || wroteRenderableBlocks) {
|
|
2105
|
-
return;
|
|
2106
|
-
}
|
|
2107
|
-
const progressLine = `[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}] ${delta.text}\n`;
|
|
2108
|
-
if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering && lastRenderedRequestTree) {
|
|
2109
|
-
liveRequestAnnotations.push(progressLine);
|
|
2110
|
-
clearLiveRequestTree();
|
|
2111
|
-
drawLiveRequestTree();
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
writeChatStderr(`[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}] ${delta.text}\n`);
|
|
2115
|
-
}
|
|
2116
|
-
},
|
|
2117
|
-
});
|
|
2118
|
-
}
|
|
2119
|
-
catch (error) {
|
|
2120
|
-
throw error;
|
|
2121
|
-
}
|
|
2122
|
-
if (!result) {
|
|
2123
|
-
throw new Error("chat request completed without a terminal result");
|
|
2124
|
-
}
|
|
2125
|
-
latestSessionId = result.sessionId;
|
|
2126
|
-
latestRequestId = result.requestId;
|
|
2127
|
-
latestAgentId = result.agentId ?? latestAgentId;
|
|
2128
|
-
const completedAt = Date.now();
|
|
2129
|
-
if (input.requestEvents && latestSnapshot) {
|
|
2130
|
-
const terminalSnapshot = buildTerminalRequestSnapshot(latestSnapshot, { state: result.state, output: result.output });
|
|
2131
|
-
lastRenderedRequestTree = renderRequestSnapshotTree(terminalSnapshot, input.colorRequestTree === true, buildTimingSummary(completedAt));
|
|
2132
|
-
if (input.liveRequestTree && !requestTreePersisted) {
|
|
2133
|
-
clearLiveRequestTree();
|
|
2134
|
-
drawLiveRequestTree();
|
|
2135
|
-
}
|
|
2136
|
-
else if (input.liveRequestTree && requestTreePersisted) {
|
|
2137
|
-
const todoContinuation = renderRequestTodoContinuation(terminalSnapshot, persistedRequestTreeTodoSignature, input.colorRequestTree === true);
|
|
2138
|
-
const continuation = renderRequestEventContinuation(terminalSnapshot, persistedRequestTreeEventCount, input.colorRequestTree === true);
|
|
2139
|
-
if (todoContinuation) {
|
|
2140
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, `\n${todoContinuation}`);
|
|
2141
|
-
}
|
|
2142
|
-
if (continuation) {
|
|
2143
|
-
stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, `\n${continuation}`);
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
else if (!input.liveRequestTree) {
|
|
2147
|
-
input.stderr(`\n${lastRenderedRequestTree}`);
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
if (!wroteContent && !wroteRenderableBlocks && result.output.trim().length > 0) {
|
|
2151
|
-
writeChatStdout(renderChatTextChunk(result.output, input.modelInfo));
|
|
2152
|
-
}
|
|
2153
|
-
if (result.state === "waiting_for_approval") {
|
|
2154
|
-
writeChatStderr(`\nRequest is waiting for approval${result.approvalId ? ` (${result.approvalId})` : ""}.\n`);
|
|
2155
|
-
}
|
|
2156
|
-
else if ((wroteContent || wroteRenderableBlocks || result.output.trim().length > 0)) {
|
|
2157
|
-
writeChatStdout("\n");
|
|
2158
|
-
}
|
|
2159
|
-
await Promise.allSettled([stdoutWriteChain, stderrWriteChain]);
|
|
2160
|
-
return { sessionId: latestSessionId, requestId: latestRequestId, agentId: latestAgentId };
|
|
2161
|
-
}
|
|
2162
|
-
export async function runCli(argv, io = {}, deps = {}) {
|
|
2163
|
-
const cwd = io.cwd ?? process.cwd();
|
|
2164
|
-
const stdin = io.stdin ?? process.stdin;
|
|
2165
|
-
const stdout = io.stdout ?? ((message) => process.stdout.write(message));
|
|
2166
|
-
const stderr = io.stderr ?? ((message) => process.stderr.write(message));
|
|
2167
|
-
const stdoutStream = io.stdout === undefined ? process.stdout : undefined;
|
|
2168
|
-
const stderrStream = io.stderr === undefined ? process.stderr : undefined;
|
|
2169
|
-
const [command, projectName, ...rest] = argv;
|
|
2170
|
-
const probeWorkspace = deps.probeChatWorkspace ?? probeChatWorkspace;
|
|
2171
|
-
const createChatLineReader = deps.createReadlineInterface ?? createReadlineInterface;
|
|
2172
|
-
const createHarness = deps.createAgentHarness ?? createAgentHarness;
|
|
2173
|
-
const serveA2a = deps.serveA2aOverHttp ?? serveA2aOverHttp;
|
|
2174
|
-
const serveAgUi = deps.serveAgUiOverHttp ?? serveAgUiOverHttp;
|
|
2175
|
-
const serveAcpHttp = deps.serveAcpOverHttp ?? serveAcpOverHttp;
|
|
2176
|
-
const serveAcp = deps.serveAcpOverStdio ?? serveAcpOverStdio;
|
|
2177
|
-
const serveRuntimeMcp = deps.serveRuntimeMcpOverStdio ?? serveRuntimeMcpOverStdio;
|
|
2178
|
-
const serveRuntimeMcpStreamableHttp = deps.serveRuntimeMcpOverStreamableHttp ?? serveRuntimeMcpOverStreamableHttp;
|
|
2179
|
-
const spawnManagedCliProcess = deps.spawnManagedCliProcess ?? defaultSpawnManagedCliProcess;
|
|
2180
|
-
const isManagedProcessRunning = deps.isManagedProcessRunning ?? defaultIsManagedProcessRunning;
|
|
2181
|
-
const signalManagedProcess = deps.signalManagedProcess ?? defaultSignalManagedProcess;
|
|
2182
|
-
const createChatClient = deps.createChatClient ?? (async (input) => {
|
|
2183
|
-
if (input.transport === "http") {
|
|
2184
|
-
return createAcpHttpHarnessClient({
|
|
2185
|
-
rpcUrl: `http://${input.hostname ?? "127.0.0.1"}:${input.port ?? 8787}/rpc`,
|
|
2186
|
-
eventsUrl: `http://${input.hostname ?? "127.0.0.1"}:${input.port ?? 8787}/events`,
|
|
2187
|
-
});
|
|
2188
|
-
}
|
|
2189
|
-
const runtime = await createHarness(input.workspaceRoot);
|
|
2190
|
-
return createInProcessHarnessClient(runtime);
|
|
2191
|
-
});
|
|
2192
|
-
const executeChat = async (parsed) => {
|
|
2193
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, parsed.workspaceRoot);
|
|
2194
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, parsed.workspaceRoot);
|
|
2195
|
-
if (workspaceError) {
|
|
2196
|
-
stderr(`${workspaceError}\n`);
|
|
2197
|
-
return 1;
|
|
2198
|
-
}
|
|
2199
|
-
const workspaceModelInfo = readChatWorkspaceModelInfo(workspacePath, parsed.agentId);
|
|
2200
|
-
const chatStdinStream = stdin;
|
|
2201
|
-
let client;
|
|
2202
|
-
let restoreWriteListenerGuard;
|
|
2203
|
-
let restoreEmitterListenerGuard;
|
|
2204
|
-
let restoreStderrNoiseFilter;
|
|
2205
|
-
let restoreWarningFilter;
|
|
2206
|
-
try {
|
|
2207
|
-
restoreEmitterListenerGuard = installCliEmitterListenerGuard();
|
|
2208
|
-
restoreStderrNoiseFilter = installCliStderrNoiseFilter(io.stderr === undefined);
|
|
2209
|
-
restoreWarningFilter = installCliWarningFilter();
|
|
2210
|
-
client = await createChatClient({
|
|
2211
|
-
workspaceRoot: workspacePath,
|
|
2212
|
-
transport: parsed.transport,
|
|
2213
|
-
hostname: parsed.hostname,
|
|
2214
|
-
port: parsed.port,
|
|
2215
|
-
stderr,
|
|
2216
|
-
});
|
|
2217
|
-
restoreWriteListenerGuard = installCliWriteListenerGuard([stdoutStream, stderrStream]);
|
|
2218
|
-
let activeAgentId = parsed.agentId;
|
|
2219
|
-
let activeSessionId = parsed.sessionId;
|
|
2220
|
-
let latestRequestId;
|
|
2221
|
-
const useColor = false;
|
|
2222
|
-
const preflightWarning = parsed.message
|
|
2223
|
-
? undefined
|
|
2224
|
-
: await probeWorkspace({
|
|
2225
|
-
workspaceRoot: workspacePath,
|
|
2226
|
-
agentId: activeAgentId,
|
|
2227
|
-
});
|
|
2228
|
-
if (parsed.message) {
|
|
2229
|
-
await streamChatMessage({
|
|
2230
|
-
client,
|
|
2231
|
-
stdout,
|
|
2232
|
-
stdoutStream,
|
|
2233
|
-
stderr,
|
|
2234
|
-
stderrStream,
|
|
2235
|
-
agentId: activeAgentId,
|
|
2236
|
-
sessionId: activeSessionId,
|
|
2237
|
-
message: parsed.message,
|
|
2238
|
-
modelInfo: workspaceModelInfo,
|
|
2239
|
-
requestEvents: parsed.requestEvents,
|
|
2240
|
-
liveRequestTree: parsed.requestEvents && chatStdinStream.isTTY === true,
|
|
2241
|
-
colorRequestTree: useColor,
|
|
2242
|
-
showToolResults: parsed.requestEvents,
|
|
2243
|
-
showRunningState: false,
|
|
2244
|
-
});
|
|
2245
|
-
await client.stop();
|
|
2246
|
-
restoreWriteListenerGuard?.();
|
|
2247
|
-
restoreEmitterListenerGuard?.();
|
|
2248
|
-
restoreStderrNoiseFilter?.();
|
|
2249
|
-
restoreWarningFilter?.();
|
|
2250
|
-
return 0;
|
|
2251
|
-
}
|
|
2252
|
-
stdout(renderChatBanner({
|
|
2253
|
-
workspacePath,
|
|
2254
|
-
transport: parsed.transport,
|
|
2255
|
-
agentId: activeAgentId,
|
|
2256
|
-
sessionId: activeSessionId,
|
|
2257
|
-
color: useColor,
|
|
2258
|
-
}));
|
|
2259
|
-
if (preflightWarning) {
|
|
2260
|
-
stdout(`${preflightWarning}\n\n`);
|
|
2261
|
-
}
|
|
2262
|
-
const stdoutSink = new Writable({
|
|
2263
|
-
decodeStrings: false,
|
|
2264
|
-
write(chunk, _encoding, callback) {
|
|
2265
|
-
stdout(typeof chunk === "string" ? chunk : String(chunk));
|
|
2266
|
-
callback();
|
|
2267
|
-
},
|
|
2268
|
-
});
|
|
2269
|
-
const lineReader = createChatLineReader({
|
|
2270
|
-
input: stdin,
|
|
2271
|
-
output: stdoutSink,
|
|
2272
|
-
crlfDelay: Infinity,
|
|
2273
|
-
terminal: chatStdinStream.isTTY === true,
|
|
2274
|
-
});
|
|
2275
|
-
try {
|
|
2276
|
-
for await (const raw of iterateChatLines(lineReader, () => renderChatPromptLine({
|
|
2277
|
-
agentId: activeAgentId,
|
|
2278
|
-
sessionId: activeSessionId,
|
|
2279
|
-
requestId: latestRequestId,
|
|
2280
|
-
color: useColor,
|
|
2281
|
-
}))) {
|
|
2282
|
-
const trimmed = raw.trim();
|
|
2283
|
-
if (!trimmed) {
|
|
2284
|
-
continue;
|
|
2285
|
-
}
|
|
2286
|
-
const chatCommand = normalizeChatCommand(trimmed);
|
|
2287
|
-
if (!chatCommand) {
|
|
2288
|
-
const streamed = await streamChatMessage({
|
|
2289
|
-
client,
|
|
2290
|
-
stdout,
|
|
2291
|
-
stdoutStream,
|
|
2292
|
-
stderr,
|
|
2293
|
-
stderrStream,
|
|
2294
|
-
agentId: activeAgentId,
|
|
2295
|
-
sessionId: activeSessionId,
|
|
2296
|
-
message: trimmed,
|
|
2297
|
-
modelInfo: workspaceModelInfo,
|
|
2298
|
-
requestEvents: parsed.requestEvents,
|
|
2299
|
-
liveRequestTree: parsed.requestEvents && chatStdinStream.isTTY === true,
|
|
2300
|
-
colorRequestTree: useColor,
|
|
2301
|
-
showToolResults: parsed.requestEvents,
|
|
2302
|
-
showRunningState: false,
|
|
2303
|
-
});
|
|
2304
|
-
activeSessionId = streamed.sessionId;
|
|
2305
|
-
latestRequestId = streamed.requestId;
|
|
2306
|
-
activeAgentId = streamed.agentId ?? activeAgentId;
|
|
2307
|
-
continue;
|
|
2308
|
-
}
|
|
2309
|
-
if (chatCommand.name === "exit" || chatCommand.name === "quit") {
|
|
2310
|
-
break;
|
|
2311
|
-
}
|
|
2312
|
-
if (chatCommand.name === "help") {
|
|
2313
|
-
stdout(renderChatHelp());
|
|
2314
|
-
continue;
|
|
2315
|
-
}
|
|
2316
|
-
if (chatCommand.name === "context") {
|
|
2317
|
-
stdout(renderChatContext({
|
|
2318
|
-
agentId: activeAgentId,
|
|
2319
|
-
sessionId: activeSessionId,
|
|
2320
|
-
requestId: latestRequestId,
|
|
2321
|
-
}));
|
|
2322
|
-
continue;
|
|
2323
|
-
}
|
|
2324
|
-
if (chatCommand.name === "new") {
|
|
2325
|
-
activeSessionId = undefined;
|
|
2326
|
-
latestRequestId = undefined;
|
|
2327
|
-
stdout(renderChatContext({
|
|
2328
|
-
agentId: activeAgentId,
|
|
2329
|
-
sessionId: activeSessionId,
|
|
2330
|
-
requestId: latestRequestId,
|
|
2331
|
-
}));
|
|
2332
|
-
continue;
|
|
2333
|
-
}
|
|
2334
|
-
if (chatCommand.name === "agent") {
|
|
2335
|
-
if (!chatCommand.arg) {
|
|
2336
|
-
stdout(activeAgentId ? `${activeAgentId}\n` : "No active agent override.\n");
|
|
2337
|
-
continue;
|
|
2338
|
-
}
|
|
2339
|
-
activeAgentId = chatCommand.arg;
|
|
2340
|
-
stdout(`agent=${activeAgentId}\n`);
|
|
2341
|
-
continue;
|
|
2342
|
-
}
|
|
2343
|
-
if (chatCommand.name === "session") {
|
|
2344
|
-
stdout(activeSessionId ? `${activeSessionId}\n` : "No active session.\n");
|
|
2345
|
-
continue;
|
|
2346
|
-
}
|
|
2347
|
-
if (chatCommand.name === "request") {
|
|
2348
|
-
if (!chatCommand.arg) {
|
|
2349
|
-
stdout(latestRequestId ? `${latestRequestId}\n` : "No active request.\n");
|
|
2350
|
-
continue;
|
|
2351
|
-
}
|
|
2352
|
-
const selected = await client.getRequest(chatCommand.arg);
|
|
2353
|
-
if (!selected) {
|
|
2354
|
-
stdout(`Request not found: ${chatCommand.arg}\n`);
|
|
2355
|
-
continue;
|
|
2356
|
-
}
|
|
2357
|
-
latestRequestId = selected.requestId;
|
|
2358
|
-
activeSessionId = selected.sessionId;
|
|
2359
|
-
activeAgentId = selected.agentId;
|
|
2360
|
-
stdout(`request=${latestRequestId} session=${activeSessionId}\n`);
|
|
2361
|
-
continue;
|
|
2362
|
-
}
|
|
2363
|
-
if (chatCommand.name === "sessions") {
|
|
2364
|
-
const summaries = await client.listSessionSummaries(parsed.agentId ? { agentId: parsed.agentId } : undefined);
|
|
2365
|
-
stdout(renderSessionSummaries(summaries));
|
|
2366
|
-
continue;
|
|
2367
|
-
}
|
|
2368
|
-
if (chatCommand.name === "requests") {
|
|
2369
|
-
const requests = await client.listRequests(activeSessionId
|
|
2370
|
-
? { sessionId: activeSessionId }
|
|
2371
|
-
: activeAgentId
|
|
2372
|
-
? { agentId: activeAgentId }
|
|
2373
|
-
: undefined);
|
|
2374
|
-
stdout(renderRequestList(requests));
|
|
2375
|
-
continue;
|
|
2376
|
-
}
|
|
2377
|
-
if (chatCommand.name === "resume") {
|
|
2378
|
-
if (!chatCommand.arg) {
|
|
2379
|
-
stdout("Usage: /resume <sessionId>\n");
|
|
2380
|
-
continue;
|
|
2381
|
-
}
|
|
2382
|
-
const session = await client.getSession(chatCommand.arg);
|
|
2383
|
-
if (!session) {
|
|
2384
|
-
stdout(`Session not found: ${chatCommand.arg}\n`);
|
|
2385
|
-
continue;
|
|
2386
|
-
}
|
|
2387
|
-
activeSessionId = chatCommand.arg;
|
|
2388
|
-
latestRequestId = session.latestRequestId;
|
|
2389
|
-
activeAgentId = session.currentAgentId ?? session.entryAgentId ?? activeAgentId;
|
|
2390
|
-
stdout(`session=${activeSessionId}\n`);
|
|
2391
|
-
continue;
|
|
2392
|
-
}
|
|
2393
|
-
if (chatCommand.name === "cancel") {
|
|
2394
|
-
if (!latestRequestId) {
|
|
2395
|
-
stdout("No active request.\n");
|
|
2396
|
-
continue;
|
|
2397
|
-
}
|
|
2398
|
-
const result = await client.cancelRequest({
|
|
2399
|
-
requestId: latestRequestId,
|
|
2400
|
-
reason: "Cancelled from chat CLI",
|
|
2401
|
-
});
|
|
2402
|
-
activeSessionId = result.sessionId;
|
|
2403
|
-
latestRequestId = result.requestId;
|
|
2404
|
-
stdout(`${result.state}: ${result.output}\n`);
|
|
2405
|
-
continue;
|
|
2406
|
-
}
|
|
2407
|
-
if (chatCommand.name === "approvals") {
|
|
2408
|
-
const approvals = await client.listApprovals(activeSessionId ? { sessionId: activeSessionId, status: "pending" } : { status: "pending" });
|
|
2409
|
-
stdout(renderApprovalList(approvals));
|
|
2410
|
-
continue;
|
|
2411
|
-
}
|
|
2412
|
-
if ((chatCommand.name === "approve" || chatCommand.name === "reject") && chatCommand.arg) {
|
|
2413
|
-
const result = await client.resolveApproval({
|
|
2414
|
-
approvalId: chatCommand.arg,
|
|
2415
|
-
decision: chatCommand.name === "approve" ? "approve" : "reject",
|
|
2416
|
-
...(activeSessionId ? { sessionId: activeSessionId } : {}),
|
|
2417
|
-
});
|
|
2418
|
-
activeSessionId = result.sessionId;
|
|
2419
|
-
latestRequestId = result.requestId;
|
|
2420
|
-
stdout(`${result.state}: ${result.output}\n`);
|
|
2421
|
-
continue;
|
|
2422
|
-
}
|
|
2423
|
-
if (chatCommand.name === "events") {
|
|
2424
|
-
if (!activeSessionId || !latestRequestId) {
|
|
2425
|
-
stdout("No active request.\n");
|
|
2426
|
-
continue;
|
|
2427
|
-
}
|
|
2428
|
-
const events = await client.listRequestEvents({
|
|
2429
|
-
sessionId: activeSessionId,
|
|
2430
|
-
requestId: latestRequestId,
|
|
2431
|
-
});
|
|
2432
|
-
stdout(renderRequestEvents(events));
|
|
2433
|
-
continue;
|
|
2434
|
-
}
|
|
2435
|
-
if (chatCommand.name === "trace") {
|
|
2436
|
-
if (!activeSessionId || !latestRequestId) {
|
|
2437
|
-
stdout("No active request.\n");
|
|
2438
|
-
continue;
|
|
2439
|
-
}
|
|
2440
|
-
const traceItems = await client.listRequestTraceItems({
|
|
2441
|
-
sessionId: activeSessionId,
|
|
2442
|
-
requestId: latestRequestId,
|
|
2443
|
-
});
|
|
2444
|
-
stdout(renderRequestTraceItems(traceItems));
|
|
2445
|
-
continue;
|
|
2446
|
-
}
|
|
2447
|
-
if (chatCommand.name === "health") {
|
|
2448
|
-
const health = await client.getHealth();
|
|
2449
|
-
stdout(renderHealthSnapshot(health, workspacePath));
|
|
2450
|
-
continue;
|
|
2451
|
-
}
|
|
2452
|
-
if (chatCommand.name === "overview") {
|
|
2453
|
-
const overview = await client.getOperatorOverview({ limit: 5 });
|
|
2454
|
-
stdout(renderOperatorOverview(overview, workspacePath));
|
|
2455
|
-
continue;
|
|
2456
|
-
}
|
|
2457
|
-
stdout("Unknown chat command. Use /help.\n");
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
finally {
|
|
2461
|
-
lineReader.close();
|
|
2462
|
-
}
|
|
2463
|
-
await client.stop();
|
|
2464
|
-
restoreWriteListenerGuard?.();
|
|
2465
|
-
restoreEmitterListenerGuard?.();
|
|
2466
|
-
restoreStderrNoiseFilter?.();
|
|
2467
|
-
restoreWarningFilter?.();
|
|
2468
|
-
return 0;
|
|
2469
|
-
}
|
|
2470
|
-
catch (error) {
|
|
2471
|
-
restoreWriteListenerGuard?.();
|
|
2472
|
-
restoreEmitterListenerGuard?.();
|
|
2473
|
-
restoreStderrNoiseFilter?.();
|
|
2474
|
-
restoreWarningFilter?.();
|
|
2475
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2476
|
-
stderr(`${message}\n`);
|
|
2477
|
-
if (client) {
|
|
2478
|
-
await client.stop().catch(() => undefined);
|
|
2479
|
-
}
|
|
2480
|
-
return 1;
|
|
2481
|
-
}
|
|
2482
|
-
};
|
|
2483
|
-
const startManagedHttpService = async (service, workspacePath, args) => {
|
|
2484
|
-
const existing = await readManagedServiceState(workspacePath, service);
|
|
2485
|
-
if (existing && isManagedProcessRunning(existing.pid)) {
|
|
2486
|
-
stderr(`${service.toUpperCase()} service is already running for ${workspacePath} (pid ${existing.pid}).\n`);
|
|
2487
|
-
return 0;
|
|
2488
|
-
}
|
|
2489
|
-
if (existing) {
|
|
2490
|
-
await clearManagedServiceState(workspacePath, service);
|
|
2491
|
-
}
|
|
2492
|
-
const stdoutPath = managedServiceLogPath(workspacePath, service, "stdout");
|
|
2493
|
-
const stderrPath = managedServiceLogPath(workspacePath, service, "stderr");
|
|
2494
|
-
const spawned = await spawnManagedCliProcess({
|
|
2495
|
-
args,
|
|
2496
|
-
cwd: workspacePath,
|
|
2497
|
-
stdoutPath,
|
|
2498
|
-
stderrPath,
|
|
2499
|
-
});
|
|
2500
|
-
if (!spawned.pid || spawned.pid <= 0) {
|
|
2501
|
-
stderr(`Failed to start ${service.toUpperCase()} service for ${workspacePath}.\n`);
|
|
2502
|
-
return 1;
|
|
2503
|
-
}
|
|
2504
|
-
await writeManagedServiceState({
|
|
2505
|
-
pid: spawned.pid,
|
|
2506
|
-
service,
|
|
2507
|
-
workspaceRoot: workspacePath,
|
|
2508
|
-
transport: service === "mcp" ? "streamable-http" : "http",
|
|
2509
|
-
startedAt: new Date().toISOString(),
|
|
2510
|
-
stdoutPath,
|
|
2511
|
-
stderrPath,
|
|
2512
|
-
});
|
|
2513
|
-
await sleep(250);
|
|
2514
|
-
if (!isManagedProcessRunning(spawned.pid)) {
|
|
2515
|
-
await clearManagedServiceState(workspacePath, service);
|
|
2516
|
-
stderr(`Failed to keep ${service.toUpperCase()} service running for ${workspacePath}. Check ${stderrPath}.\n`);
|
|
2517
|
-
return 1;
|
|
2518
|
-
}
|
|
2519
|
-
stderr(`Started ${service.toUpperCase()} service for ${workspacePath} (pid ${spawned.pid}). Logs: ${stdoutPath}, ${stderrPath}\n`);
|
|
2520
|
-
return 0;
|
|
2521
|
-
};
|
|
2522
|
-
const stopManagedHttpService = async (service, workspacePath) => {
|
|
2523
|
-
const existing = await readManagedServiceState(workspacePath, service);
|
|
2524
|
-
if (!existing) {
|
|
2525
|
-
stderr(`No managed ${service.toUpperCase()} service is recorded for ${workspacePath}.\n`);
|
|
2526
|
-
return 0;
|
|
2527
|
-
}
|
|
2528
|
-
if (!isManagedProcessRunning(existing.pid)) {
|
|
2529
|
-
await clearManagedServiceState(workspacePath, service);
|
|
2530
|
-
stderr(`Managed ${service.toUpperCase()} service for ${workspacePath} is already stopped.\n`);
|
|
2531
|
-
return 0;
|
|
2532
|
-
}
|
|
2533
|
-
signalManagedProcess(existing.pid, "SIGTERM");
|
|
2534
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
2535
|
-
await sleep(100);
|
|
2536
|
-
if (!isManagedProcessRunning(existing.pid)) {
|
|
2537
|
-
await clearManagedServiceState(workspacePath, service);
|
|
2538
|
-
stderr(`Stopped ${service.toUpperCase()} service for ${workspacePath}.\n`);
|
|
2539
|
-
return 0;
|
|
2540
|
-
}
|
|
2541
|
-
}
|
|
2542
|
-
stderr(`Timed out while stopping ${service.toUpperCase()} service for ${workspacePath} (pid ${existing.pid}).\n`);
|
|
2543
|
-
return 1;
|
|
2544
|
-
};
|
|
2545
|
-
if (command === "init") {
|
|
2546
|
-
if (!projectName?.trim()) {
|
|
2547
|
-
stderr(renderUsage());
|
|
2548
|
-
return 1;
|
|
2549
|
-
}
|
|
2550
|
-
const parsed = parseInitOptions(rest);
|
|
2551
|
-
if (parsed.error) {
|
|
2552
|
-
stderr(`${parsed.error}\n`);
|
|
2553
|
-
stderr(renderUsage());
|
|
2554
|
-
return 1;
|
|
2555
|
-
}
|
|
2556
|
-
try {
|
|
2557
|
-
const projectRoot = path.resolve(cwd, projectName);
|
|
2558
|
-
const result = await initProject(projectRoot, projectName, parsed.options);
|
|
2559
|
-
stdout(`Created ${result.projectSlug} at ${result.projectRoot}\n`);
|
|
2560
|
-
stdout("Next steps:\n");
|
|
2561
|
-
stdout(` cd ${projectName}\n`);
|
|
2562
|
-
stdout(" npm install\n");
|
|
2563
|
-
if ((parsed.options?.provider ?? "openai") === "openai") {
|
|
2564
|
-
stdout(" export OPENAI_API_KEY=your_key_here\n");
|
|
2565
|
-
}
|
|
2566
|
-
stdout(' npm run start -- "Research a topic"\n');
|
|
2567
|
-
return 0;
|
|
2568
|
-
}
|
|
2569
|
-
catch (error) {
|
|
2570
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2571
|
-
stderr(`${message}\n`);
|
|
2572
|
-
return 1;
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
if (command === "chat") {
|
|
2576
|
-
const parsed = parseChatOptions([projectName, ...rest].filter((item) => typeof item === "string"));
|
|
2577
|
-
if (parsed.error) {
|
|
2578
|
-
stderr(`${parsed.error}\n`);
|
|
2579
|
-
stderr(renderUsage());
|
|
2580
|
-
return 1;
|
|
2581
|
-
}
|
|
2582
|
-
return executeChat(parsed);
|
|
2583
|
-
}
|
|
2584
|
-
if (!isTopLevelCliCommand(command)) {
|
|
2585
|
-
const parsed = parseChatOptions(argv);
|
|
2586
|
-
if (parsed.error) {
|
|
2587
|
-
stderr(`${parsed.error}\n`);
|
|
2588
|
-
stderr(renderUsage());
|
|
2589
|
-
return 1;
|
|
2590
|
-
}
|
|
2591
|
-
return executeChat(parsed);
|
|
2592
|
-
}
|
|
2593
|
-
if (command === "acp") {
|
|
2594
|
-
const [subcommand, ...subcommandArgs] = [projectName, ...rest];
|
|
2595
|
-
if (subcommand !== "serve" && subcommand !== "start" && subcommand !== "stop") {
|
|
2596
|
-
stderr(renderUsage());
|
|
2597
|
-
return 1;
|
|
2598
|
-
}
|
|
2599
|
-
const parsedStop = subcommand === "stop" ? parseWorkspaceOnlyOptions(subcommandArgs) : undefined;
|
|
2600
|
-
const parsedServe = subcommand !== "stop"
|
|
2601
|
-
? parseAcpServeOptions(subcommandArgs, subcommand === "start" ? "http" : "stdio")
|
|
2602
|
-
: undefined;
|
|
2603
|
-
const parseError = parsedStop?.error ?? parsedServe?.error;
|
|
2604
|
-
if (parseError) {
|
|
2605
|
-
stderr(`${parseError}\n`);
|
|
2606
|
-
stderr(renderUsage());
|
|
2607
|
-
return 1;
|
|
2608
|
-
}
|
|
2609
|
-
try {
|
|
2610
|
-
const workspaceRoot = parsedStop?.workspaceRoot ?? parsedServe?.workspaceRoot;
|
|
2611
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, workspaceRoot);
|
|
2612
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, workspaceRoot);
|
|
2613
|
-
if (workspaceError) {
|
|
2614
|
-
stderr(`${workspaceError}\n`);
|
|
2615
|
-
return 1;
|
|
2616
|
-
}
|
|
2617
|
-
if (subcommand === "start") {
|
|
2618
|
-
if (!parsedServe) {
|
|
2619
|
-
stderr(renderUsage());
|
|
2620
|
-
return 1;
|
|
2621
|
-
}
|
|
2622
|
-
if (parsedServe.transport !== "http") {
|
|
2623
|
-
stderr("ACP start only supports --transport http. Use `acp serve --transport stdio` for stdio clients.\n");
|
|
2624
|
-
return 1;
|
|
2625
|
-
}
|
|
2626
|
-
const args = ["acp", "serve", "--workspace", workspacePath, "--transport", "http"];
|
|
2627
|
-
if (parsedServe.hostname) {
|
|
2628
|
-
args.push("--host", parsedServe.hostname);
|
|
2629
|
-
}
|
|
2630
|
-
if (typeof parsedServe.port === "number") {
|
|
2631
|
-
args.push("--port", String(parsedServe.port));
|
|
2632
|
-
}
|
|
2633
|
-
return startManagedHttpService("acp", workspacePath, args);
|
|
2634
|
-
}
|
|
2635
|
-
if (subcommand === "stop") {
|
|
2636
|
-
return stopManagedHttpService("acp", workspacePath);
|
|
2637
|
-
}
|
|
2638
|
-
if (!parsedServe) {
|
|
2639
|
-
stderr(renderUsage());
|
|
2640
|
-
return 1;
|
|
2641
|
-
}
|
|
2642
|
-
const runtime = await createHarness(workspacePath);
|
|
2643
|
-
if (parsedServe.transport === "http") {
|
|
2644
|
-
const server = await serveAcpHttp(runtime, {
|
|
2645
|
-
hostname: parsedServe.hostname,
|
|
2646
|
-
port: parsedServe.port,
|
|
2647
|
-
});
|
|
2648
|
-
stderr(`Serving ACP over http from ${workspacePath} at ${server.rpcUrl} (events ${server.eventsUrl})\n`);
|
|
2649
|
-
await server.completed;
|
|
2650
|
-
}
|
|
2651
|
-
else {
|
|
2652
|
-
stderr(`Serving ACP over stdio from ${workspacePath}\n`);
|
|
2653
|
-
const server = serveAcp(runtime);
|
|
2654
|
-
await server.completed;
|
|
2655
|
-
}
|
|
2656
|
-
await runtime.stop();
|
|
2657
|
-
return 0;
|
|
2658
|
-
}
|
|
2659
|
-
catch (error) {
|
|
2660
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2661
|
-
stderr(`${message}\n`);
|
|
2662
|
-
return 1;
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
if (command === "ag-ui") {
|
|
2666
|
-
const [subcommand, ...subcommandArgs] = [projectName, ...rest];
|
|
2667
|
-
if (subcommand !== "serve") {
|
|
2668
|
-
stderr(renderUsage());
|
|
2669
|
-
return 1;
|
|
2670
|
-
}
|
|
2671
|
-
const parsed = parseHttpServeOptions(subcommandArgs, "AG-UI");
|
|
2672
|
-
if (parsed.error) {
|
|
2673
|
-
stderr(`${parsed.error}\n`);
|
|
2674
|
-
stderr(renderUsage());
|
|
2675
|
-
return 1;
|
|
2676
|
-
}
|
|
2677
|
-
try {
|
|
2678
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, parsed.workspaceRoot);
|
|
2679
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, parsed.workspaceRoot);
|
|
2680
|
-
if (workspaceError) {
|
|
2681
|
-
stderr(`${workspaceError}\n`);
|
|
2682
|
-
return 1;
|
|
2683
|
-
}
|
|
2684
|
-
const runtime = await createHarness(workspacePath);
|
|
2685
|
-
const server = await serveAgUi(runtime, {
|
|
2686
|
-
hostname: parsed.hostname,
|
|
2687
|
-
port: parsed.port,
|
|
2688
|
-
});
|
|
2689
|
-
stderr(`Serving AG-UI over http from ${workspacePath} at ${server.runUrl}\n`);
|
|
2690
|
-
await server.completed;
|
|
2691
|
-
await runtime.stop();
|
|
2692
|
-
return 0;
|
|
2693
|
-
}
|
|
2694
|
-
catch (error) {
|
|
2695
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2696
|
-
stderr(`${message}\n`);
|
|
2697
|
-
return 1;
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
if (command === "a2a") {
|
|
2701
|
-
const [subcommand, ...subcommandArgs] = [projectName, ...rest];
|
|
2702
|
-
if (subcommand !== "serve" && subcommand !== "start" && subcommand !== "stop") {
|
|
2703
|
-
stderr(renderUsage());
|
|
2704
|
-
return 1;
|
|
2705
|
-
}
|
|
2706
|
-
const parsedStop = subcommand === "stop" ? parseWorkspaceOnlyOptions(subcommandArgs) : undefined;
|
|
2707
|
-
const parsedServe = subcommand !== "stop" ? parseHttpServeOptions(subcommandArgs, "A2A") : undefined;
|
|
2708
|
-
const parseError = parsedStop?.error ?? parsedServe?.error;
|
|
2709
|
-
if (parseError) {
|
|
2710
|
-
stderr(`${parseError}\n`);
|
|
2711
|
-
stderr(renderUsage());
|
|
2712
|
-
return 1;
|
|
2713
|
-
}
|
|
2714
|
-
try {
|
|
2715
|
-
const workspaceRoot = parsedStop?.workspaceRoot ?? parsedServe?.workspaceRoot;
|
|
2716
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, workspaceRoot);
|
|
2717
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, workspaceRoot);
|
|
2718
|
-
if (workspaceError) {
|
|
2719
|
-
stderr(`${workspaceError}\n`);
|
|
2720
|
-
return 1;
|
|
2721
|
-
}
|
|
2722
|
-
if (subcommand === "start") {
|
|
2723
|
-
const args = ["a2a", "serve", "--workspace", workspacePath];
|
|
2724
|
-
if (parsedServe?.hostname) {
|
|
2725
|
-
args.push("--host", parsedServe.hostname);
|
|
2726
|
-
}
|
|
2727
|
-
if (typeof parsedServe?.port === "number") {
|
|
2728
|
-
args.push("--port", String(parsedServe.port));
|
|
2729
|
-
}
|
|
2730
|
-
return startManagedHttpService("a2a", workspacePath, args);
|
|
2731
|
-
}
|
|
2732
|
-
if (subcommand === "stop") {
|
|
2733
|
-
return stopManagedHttpService("a2a", workspacePath);
|
|
2734
|
-
}
|
|
2735
|
-
if (!parsedServe) {
|
|
2736
|
-
stderr(renderUsage());
|
|
2737
|
-
return 1;
|
|
2738
|
-
}
|
|
2739
|
-
const runtime = await createHarness(workspacePath);
|
|
2740
|
-
const server = await serveA2a(runtime, {
|
|
2741
|
-
hostname: parsedServe.hostname,
|
|
2742
|
-
port: parsedServe.port,
|
|
2743
|
-
});
|
|
2744
|
-
stderr(`Serving A2A over http from ${workspacePath} at ${server.rpcUrl} (card ${server.agentCardUrl})\n`);
|
|
2745
|
-
await server.completed;
|
|
2746
|
-
await runtime.stop();
|
|
2747
|
-
return 0;
|
|
2748
|
-
}
|
|
2749
|
-
catch (error) {
|
|
2750
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2751
|
-
stderr(`${message}\n`);
|
|
2752
|
-
return 1;
|
|
2753
|
-
}
|
|
2754
|
-
}
|
|
2755
|
-
if (command === "mcp" || command === "runtime-mcp") {
|
|
2756
|
-
const [subcommand, ...subcommandArgs] = [projectName, ...rest];
|
|
2757
|
-
if (subcommand !== "serve" && subcommand !== "start" && subcommand !== "stop") {
|
|
2758
|
-
stderr(renderUsage());
|
|
2759
|
-
return 1;
|
|
2760
|
-
}
|
|
2761
|
-
const parsedStop = subcommand === "stop" ? parseWorkspaceOnlyOptions(subcommandArgs) : undefined;
|
|
2762
|
-
const parsedServe = subcommand !== "stop"
|
|
2763
|
-
? parseRuntimeMcpServeOptions(subcommandArgs, subcommand === "start" ? "streamable-http" : "stdio")
|
|
2764
|
-
: undefined;
|
|
2765
|
-
const parseError = parsedStop?.error ?? parsedServe?.error;
|
|
2766
|
-
if (parseError) {
|
|
2767
|
-
stderr(`${parseError}\n`);
|
|
2768
|
-
stderr(renderUsage());
|
|
2769
|
-
return 1;
|
|
2770
|
-
}
|
|
2771
|
-
try {
|
|
2772
|
-
const workspaceRoot = parsedStop?.workspaceRoot ?? parsedServe?.workspaceRoot;
|
|
2773
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, workspaceRoot);
|
|
2774
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, workspaceRoot);
|
|
2775
|
-
if (workspaceError) {
|
|
2776
|
-
stderr(`${workspaceError}\n`);
|
|
2777
|
-
return 1;
|
|
2778
|
-
}
|
|
2779
|
-
if (subcommand === "start") {
|
|
2780
|
-
if (!parsedServe) {
|
|
2781
|
-
stderr(renderUsage());
|
|
2782
|
-
return 1;
|
|
2783
|
-
}
|
|
2784
|
-
if (parsedServe.transport !== "streamable-http") {
|
|
2785
|
-
stderr("MCP start only supports --transport streamable-http. Use `mcp serve --transport stdio` for stdio clients.\n");
|
|
2786
|
-
return 1;
|
|
2787
|
-
}
|
|
2788
|
-
const args = ["mcp", "serve", "--workspace", workspacePath, "--transport", "streamable-http"];
|
|
2789
|
-
if (parsedServe.hostname) {
|
|
2790
|
-
args.push("--host", parsedServe.hostname);
|
|
2791
|
-
}
|
|
2792
|
-
if (typeof parsedServe.port === "number") {
|
|
2793
|
-
args.push("--port", String(parsedServe.port));
|
|
2794
|
-
}
|
|
2795
|
-
return startManagedHttpService("mcp", workspacePath, args);
|
|
2796
|
-
}
|
|
2797
|
-
if (subcommand === "stop") {
|
|
2798
|
-
return stopManagedHttpService("mcp", workspacePath);
|
|
2799
|
-
}
|
|
2800
|
-
if (!parsedServe) {
|
|
2801
|
-
stderr(renderUsage());
|
|
2802
|
-
return 1;
|
|
2803
|
-
}
|
|
2804
|
-
const runtime = await createHarness(workspacePath);
|
|
2805
|
-
if (parsedServe.transport === "streamable-http") {
|
|
2806
|
-
const server = await serveRuntimeMcpStreamableHttp(runtime, {
|
|
2807
|
-
hostname: parsedServe.hostname,
|
|
2808
|
-
port: parsedServe.port,
|
|
2809
|
-
});
|
|
2810
|
-
stderr(`Serving runtime MCP over streamable HTTP from ${workspacePath} at ${server.url}\n`);
|
|
2811
|
-
await server.completed;
|
|
2812
|
-
}
|
|
2813
|
-
else {
|
|
2814
|
-
stderr(`Serving runtime MCP over stdio from ${workspacePath}\n`);
|
|
2815
|
-
const server = await serveRuntimeMcp(runtime);
|
|
2816
|
-
await new Promise((resolve, reject) => {
|
|
2817
|
-
process.stdin.on("end", resolve);
|
|
2818
|
-
process.stdin.on("error", reject);
|
|
2819
|
-
});
|
|
2820
|
-
await server.close();
|
|
2821
|
-
}
|
|
2822
|
-
await runtime.stop();
|
|
2823
|
-
return 0;
|
|
2824
|
-
}
|
|
2825
|
-
catch (error) {
|
|
2826
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2827
|
-
stderr(`${message}\n`);
|
|
2828
|
-
return 1;
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
if (command === "runtime") {
|
|
2832
|
-
const [subcommand, possibleNestedCommand, possibleThirdCommand, ...remainingArgs] = [projectName, ...rest];
|
|
2833
|
-
if (!subcommand) {
|
|
2834
|
-
stderr(renderUsage());
|
|
2835
|
-
return 1;
|
|
2836
|
-
}
|
|
2837
|
-
if (subcommand === "scheduled-run") {
|
|
2838
|
-
const parsed = parseScheduledRunOptions([possibleNestedCommand, possibleThirdCommand, ...remainingArgs].filter((item) => typeof item === "string"));
|
|
2839
|
-
if (parsed.error) {
|
|
2840
|
-
stderr(`${parsed.error}\n`);
|
|
2841
|
-
stderr(renderUsage());
|
|
2842
|
-
return 1;
|
|
2843
|
-
}
|
|
2844
|
-
if (!parsed.scheduleId) {
|
|
2845
|
-
stderr("Missing value for --schedule\n");
|
|
2846
|
-
stderr(renderUsage());
|
|
2847
|
-
return 1;
|
|
2848
|
-
}
|
|
2849
|
-
try {
|
|
2850
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, parsed.workspaceRoot);
|
|
2851
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, parsed.workspaceRoot);
|
|
2852
|
-
if (workspaceError) {
|
|
2853
|
-
stderr(`${workspaceError}\n`);
|
|
2854
|
-
return 1;
|
|
2855
|
-
}
|
|
2856
|
-
const runtime = await createHarness(workspacePath);
|
|
2857
|
-
try {
|
|
2858
|
-
const result = await runtime.runScheduledTask(parsed.scheduleId);
|
|
2859
|
-
stdout(`${String(result.output ?? "").trim()}\n`);
|
|
2860
|
-
}
|
|
2861
|
-
finally {
|
|
2862
|
-
await runtime.stop();
|
|
2863
|
-
}
|
|
2864
|
-
return 0;
|
|
2865
|
-
}
|
|
2866
|
-
catch (error) {
|
|
2867
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2868
|
-
stderr(`${message}\n`);
|
|
2869
|
-
return 1;
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
if (subcommand === "export") {
|
|
2873
|
-
const exportTarget = possibleNestedCommand;
|
|
2874
|
-
const parsed = parseRuntimeExportOptions([possibleThirdCommand, ...remainingArgs].filter((item) => typeof item === "string"));
|
|
2875
|
-
if (parsed.error) {
|
|
2876
|
-
stderr(`${parsed.error}\n`);
|
|
2877
|
-
stderr(renderUsage());
|
|
2878
|
-
return 1;
|
|
2879
|
-
}
|
|
2880
|
-
if (!parsed.sessionId) {
|
|
2881
|
-
stderr("Missing value for --session\n");
|
|
2882
|
-
stderr(renderUsage());
|
|
2883
|
-
return 1;
|
|
2884
|
-
}
|
|
2885
|
-
if (exportTarget !== "request" && exportTarget !== "session") {
|
|
2886
|
-
stderr(renderUsage());
|
|
2887
|
-
return 1;
|
|
2888
|
-
}
|
|
2889
|
-
if (exportTarget === "request" && !parsed.requestId) {
|
|
2890
|
-
stderr("Missing value for --request\n");
|
|
2891
|
-
stderr(renderUsage());
|
|
2892
|
-
return 1;
|
|
2893
|
-
}
|
|
2894
|
-
try {
|
|
2895
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, parsed.workspaceRoot);
|
|
2896
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, parsed.workspaceRoot);
|
|
2897
|
-
if (workspaceError) {
|
|
2898
|
-
stderr(`${workspaceError}\n`);
|
|
2899
|
-
return 1;
|
|
2900
|
-
}
|
|
2901
|
-
const runtime = await createHarness(workspacePath);
|
|
2902
|
-
try {
|
|
2903
|
-
if (exportTarget === "request") {
|
|
2904
|
-
const pkg = await runtime.exportRequestPackage({
|
|
2905
|
-
sessionId: parsed.sessionId,
|
|
2906
|
-
requestId: parsed.requestId,
|
|
2907
|
-
includeArtifacts: parsed.includeArtifacts,
|
|
2908
|
-
includeArtifactContents: parsed.includeArtifactContents,
|
|
2909
|
-
includeRuntimeHealth: parsed.includeRuntimeHealth,
|
|
2910
|
-
});
|
|
2911
|
-
stdout(renderJson(pkg));
|
|
2912
|
-
}
|
|
2913
|
-
else {
|
|
2914
|
-
const pkg = await runtime.exportSessionPackage({
|
|
2915
|
-
sessionId: parsed.sessionId,
|
|
2916
|
-
includeArtifacts: parsed.includeArtifacts,
|
|
2917
|
-
includeArtifactContents: parsed.includeArtifactContents,
|
|
2918
|
-
includeRuntimeHealth: parsed.includeRuntimeHealth,
|
|
2919
|
-
});
|
|
2920
|
-
stdout(renderJson(pkg));
|
|
2921
|
-
}
|
|
2922
|
-
}
|
|
2923
|
-
finally {
|
|
2924
|
-
await runtime.stop();
|
|
2925
|
-
}
|
|
2926
|
-
return 0;
|
|
2927
|
-
}
|
|
2928
|
-
catch (error) {
|
|
2929
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2930
|
-
stderr(`${message}\n`);
|
|
2931
|
-
return 1;
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
const nestedCommand = (subcommand === "approvals" || subcommand === "requests") && possibleNestedCommand
|
|
2935
|
-
? possibleNestedCommand
|
|
2936
|
-
: undefined;
|
|
2937
|
-
const subcommandArgs = nestedCommand
|
|
2938
|
-
? [possibleThirdCommand, ...remainingArgs].filter((item) => typeof item === "string")
|
|
2939
|
-
: [possibleNestedCommand, possibleThirdCommand, ...remainingArgs].filter((item) => typeof item === "string");
|
|
2940
|
-
const parsed = parseRuntimeInspectOptions(subcommandArgs);
|
|
2941
|
-
if (parsed.error) {
|
|
2942
|
-
stderr(`${parsed.error}\n`);
|
|
2943
|
-
stderr(renderUsage());
|
|
2944
|
-
return 1;
|
|
2945
|
-
}
|
|
2946
|
-
try {
|
|
2947
|
-
const workspacePath = resolveCliWorkspaceRoot(cwd, parsed.workspaceRoot);
|
|
2948
|
-
const workspaceError = validateCliWorkspaceRoot(workspacePath, parsed.workspaceRoot);
|
|
2949
|
-
if (workspaceError) {
|
|
2950
|
-
stderr(`${workspaceError}\n`);
|
|
2951
|
-
return 1;
|
|
2952
|
-
}
|
|
2953
|
-
const runtime = await createHarness(workspacePath);
|
|
2954
|
-
if (subcommand === "health") {
|
|
2955
|
-
const snapshot = await runtime.getHealth();
|
|
2956
|
-
stdout(parsed.json ? renderJson(snapshot) : renderHealthSnapshot(snapshot, workspacePath));
|
|
2957
|
-
await runtime.stop();
|
|
2958
|
-
return 0;
|
|
2959
|
-
}
|
|
2960
|
-
if (subcommand === "overview") {
|
|
2961
|
-
const overview = await runtime.getOperatorOverview({ limit: parsed.limit });
|
|
2962
|
-
stdout(parsed.json ? renderJson(overview) : renderOperatorOverview(overview, workspacePath));
|
|
2963
|
-
await runtime.stop();
|
|
2964
|
-
return 0;
|
|
2965
|
-
}
|
|
2966
|
-
if (subcommand === "approvals" && (nestedCommand === "list" || nestedCommand === "watch")) {
|
|
2967
|
-
const renderApprovals = async () => {
|
|
2968
|
-
const approvals = await runtime.listApprovals(parsed.status ? { status: parsed.status } : undefined);
|
|
2969
|
-
stdout(parsed.json ? renderJson(approvals) : renderApprovalList(approvals));
|
|
2970
|
-
};
|
|
2971
|
-
await renderApprovals();
|
|
2972
|
-
if (nestedCommand === "watch" && !parsed.once) {
|
|
2973
|
-
for (;;) {
|
|
2974
|
-
await sleep(parsed.pollMs);
|
|
2975
|
-
await renderApprovals();
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
await runtime.stop();
|
|
2979
|
-
return 0;
|
|
2980
|
-
}
|
|
2981
|
-
if (subcommand === "requests" && (nestedCommand === "list" || nestedCommand === "tail")) {
|
|
2982
|
-
const renderRequests = async () => {
|
|
2983
|
-
const listRequests = runtime.listRequests?.bind(runtime);
|
|
2984
|
-
if (!listRequests) {
|
|
2985
|
-
throw new Error("Runtime does not support request listing");
|
|
2986
|
-
}
|
|
2987
|
-
const requests = await listRequests({
|
|
2988
|
-
...(parsed.agentId ? { agentId: parsed.agentId } : {}),
|
|
2989
|
-
...(parsed.sessionId ? { sessionId: parsed.sessionId } : {}),
|
|
2990
|
-
...(parsed.state ? { state: parsed.state } : {}),
|
|
2991
|
-
});
|
|
2992
|
-
stdout(parsed.json ? renderJson(requests) : renderRequestList(requests));
|
|
2993
|
-
};
|
|
2994
|
-
await renderRequests();
|
|
2995
|
-
if (nestedCommand === "tail" && !parsed.once) {
|
|
2996
|
-
for (;;) {
|
|
2997
|
-
await sleep(parsed.pollMs);
|
|
2998
|
-
await renderRequests();
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3001
|
-
await runtime.stop();
|
|
3002
|
-
return 0;
|
|
3003
|
-
}
|
|
3004
|
-
await runtime.stop();
|
|
3005
|
-
stderr(renderUsage());
|
|
3006
|
-
return 1;
|
|
3007
|
-
}
|
|
3008
|
-
catch (error) {
|
|
3009
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3010
|
-
stderr(`${message}\n`);
|
|
3011
|
-
return 1;
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
stderr(renderUsage());
|
|
3015
|
-
return 1;
|
|
3016
|
-
}
|
|
3017
|
-
export function resolveInvokedCliHref(argvPath) {
|
|
3018
|
-
if (!argvPath) {
|
|
3019
|
-
return "";
|
|
3020
|
-
}
|
|
3021
|
-
const resolved = path.resolve(argvPath);
|
|
3022
|
-
const realPath = existsSync(resolved) ? realpathSync(resolved) : resolved;
|
|
3023
|
-
return pathToFileURL(realPath).href;
|
|
3024
|
-
}
|
|
2
|
+
export * from "./cli/main.js";
|
|
3
|
+
import { runCli, resolveInvokedCliHref } from "./cli/main.js";
|
|
3025
4
|
const invokedPath = resolveInvokedCliHref(process.argv[1]);
|
|
3026
5
|
if (import.meta.url === invokedPath) {
|
|
3027
6
|
const exitCode = await runCli(process.argv.slice(2));
|