chatccc 0.2.192 → 0.2.194
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/bin/cccagent.mjs +17 -0
- package/package.json +3 -2
- package/src/__tests__/builtin-cli-json.test.ts +39 -0
- package/src/__tests__/builtin-context.test.ts +37 -0
- package/src/__tests__/builtin-session-select.test.ts +116 -0
- package/src/__tests__/cards.test.ts +32 -11
- package/src/__tests__/ccc-adapter.test.ts +73 -0
- package/src/__tests__/feishu-avatar.test.ts +40 -6
- package/src/__tests__/orchestrator.test.ts +34 -9
- package/src/__tests__/session.test.ts +23 -13
- package/src/__tests__/sim-platform.test.ts +12 -7
- package/src/adapters/ccc-adapter.ts +99 -0
- package/src/builtin/cli.ts +299 -69
- package/src/builtin/context.ts +112 -14
- package/src/builtin/index.ts +1 -0
- package/src/builtin/session-select.ts +48 -0
- package/src/cards.ts +58 -35
- package/src/config.ts +24 -18
- package/src/feishu-api.ts +49 -43
- package/src/orchestrator.ts +1 -1
- package/src/session.ts +31 -15
package/src/builtin/cli.ts
CHANGED
|
@@ -1,100 +1,305 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* builtin
|
|
2
|
+
* ChatCCC builtin Agent terminal REPL and JSONL streaming entrypoint.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Usage:
|
|
5
5
|
* npx tsx src/builtin/cli.ts
|
|
6
|
-
* npx tsx src/builtin/cli.ts --model deepseek-
|
|
7
|
-
* npx tsx src/builtin/cli.ts --
|
|
6
|
+
* npx tsx src/builtin/cli.ts --model deepseek-v4-pro
|
|
7
|
+
* npx tsx src/builtin/cli.ts --stream-json --prompt "hello"
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as readline from "node:readline";
|
|
11
11
|
import * as process from "node:process";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { resolve as resolvePath } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
import { listBuiltinContextSessions } from "./context.js";
|
|
16
|
+
import { resolveBuiltinSession, type BuiltinResumeRequest } from "./session-select.js";
|
|
14
17
|
import { createCtrlCState } from "./sigint.js";
|
|
18
|
+
import type { ChatEvent, ChatSessionConfig, ChatSessionOptions } from "./index.js";
|
|
19
|
+
|
|
20
|
+
interface ParsedArgs {
|
|
21
|
+
config: ChatSessionConfig;
|
|
22
|
+
options: ChatSessionOptions;
|
|
23
|
+
listSessions: boolean;
|
|
24
|
+
resume: BuiltinResumeRequest;
|
|
25
|
+
help: boolean;
|
|
26
|
+
streamJson: boolean;
|
|
27
|
+
prompt: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface RuntimeDeps {
|
|
31
|
+
ChatSession: typeof import("./index.js").ChatSession;
|
|
32
|
+
appConfig: typeof import("../config.ts").config;
|
|
33
|
+
}
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
interface JsonLine {
|
|
36
|
+
type: string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
19
39
|
|
|
20
|
-
function parseArgs(
|
|
21
|
-
const args = process.argv.slice(2);
|
|
40
|
+
function parseArgs(argv = process.argv.slice(2)): ParsedArgs {
|
|
22
41
|
const config: ChatSessionConfig = {};
|
|
23
42
|
const options: ChatSessionOptions = {};
|
|
43
|
+
let listSessions = false;
|
|
44
|
+
let resume: BuiltinResumeRequest;
|
|
45
|
+
let help = false;
|
|
46
|
+
let streamJson = false;
|
|
47
|
+
let prompt: string | null = null;
|
|
24
48
|
|
|
25
|
-
for (let i = 0; i <
|
|
26
|
-
const arg =
|
|
27
|
-
const next =
|
|
28
|
-
if (arg === "--model" && next) {
|
|
49
|
+
for (let i = 0; i < argv.length; i++) {
|
|
50
|
+
const arg = argv[i];
|
|
51
|
+
const next = argv[i + 1];
|
|
52
|
+
if (arg === "--model" && next !== undefined) {
|
|
29
53
|
config.model = next;
|
|
30
54
|
i++;
|
|
31
|
-
} else if (arg === "--base-url" && next) {
|
|
55
|
+
} else if (arg === "--base-url" && next !== undefined) {
|
|
32
56
|
config.baseURL = next;
|
|
33
57
|
i++;
|
|
34
|
-
} else if (arg === "--api-key" && next) {
|
|
58
|
+
} else if (arg === "--api-key" && next !== undefined) {
|
|
35
59
|
config.apiKey = next;
|
|
36
60
|
i++;
|
|
37
|
-
} else if (arg === "--cwd" && next) {
|
|
61
|
+
} else if (arg === "--cwd" && next !== undefined) {
|
|
38
62
|
options.cwd = next;
|
|
39
63
|
i++;
|
|
64
|
+
} else if (arg === "--resume") {
|
|
65
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
66
|
+
resume = next;
|
|
67
|
+
i++;
|
|
68
|
+
} else {
|
|
69
|
+
resume = true;
|
|
70
|
+
}
|
|
71
|
+
} else if (arg === "--list-sessions") {
|
|
72
|
+
listSessions = true;
|
|
73
|
+
} else if (arg === "--stream-json") {
|
|
74
|
+
streamJson = true;
|
|
75
|
+
} else if (arg === "--prompt" && next !== undefined) {
|
|
76
|
+
prompt = next;
|
|
77
|
+
i++;
|
|
40
78
|
} else if (arg === "--help" || arg === "-h") {
|
|
41
|
-
|
|
42
|
-
process.exit(0);
|
|
79
|
+
help = true;
|
|
43
80
|
}
|
|
44
81
|
}
|
|
45
82
|
|
|
46
|
-
return { config, options };
|
|
83
|
+
return { config, options, listSessions, resume, help, streamJson, prompt };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function loadRuntime(): Promise<RuntimeDeps> {
|
|
87
|
+
const [{ ChatSession }, { config: appConfig }] = await Promise.all([
|
|
88
|
+
import("./index.js"),
|
|
89
|
+
import("../config.ts"),
|
|
90
|
+
]);
|
|
91
|
+
return { ChatSession, appConfig };
|
|
47
92
|
}
|
|
48
93
|
|
|
49
|
-
function printHelp(): void {
|
|
94
|
+
function printHelp(appConfig: RuntimeDeps["appConfig"]): void {
|
|
50
95
|
console.log([
|
|
51
|
-
"ChatCCC
|
|
96
|
+
"ChatCCC builtin Agent terminal REPL",
|
|
52
97
|
"",
|
|
53
|
-
"
|
|
98
|
+
"Usage: npx tsx src/builtin/cli.ts [options]",
|
|
54
99
|
"",
|
|
55
|
-
"
|
|
56
|
-
` --model <name>
|
|
57
|
-
` --base-url <url>
|
|
58
|
-
" --api-key <key>
|
|
59
|
-
" --cwd <path>
|
|
60
|
-
" --
|
|
100
|
+
"Options:",
|
|
101
|
+
` --model <name> Model name (overrides config.ccc.model, current default ${appConfig.ccc.model})`,
|
|
102
|
+
` --base-url <url> API base URL (current default ${appConfig.ccc.DEEPSEEK_BASE_URL})`,
|
|
103
|
+
" --api-key <key> API key (overrides config.ccc.DEEPSEEK_API_KEY)",
|
|
104
|
+
" --cwd <path> Working directory",
|
|
105
|
+
" --resume [id] Resume latest cwd session, or the explicit session id",
|
|
106
|
+
" --list-sessions List saved ccc sessions and exit",
|
|
107
|
+
" --stream-json One-shot mode: write JSONL events to stdout",
|
|
108
|
+
" --prompt <text> Prompt text for --stream-json",
|
|
109
|
+
" --help, -h Show help",
|
|
61
110
|
"",
|
|
62
|
-
"
|
|
63
|
-
" ~/.chatccc/config.json
|
|
111
|
+
"Default config source:",
|
|
112
|
+
" ~/.chatccc/config.json ccc.DEEPSEEK_API_KEY / ccc.DEEPSEEK_BASE_URL / ccc.model",
|
|
64
113
|
"",
|
|
65
114
|
].join("\n"));
|
|
66
115
|
}
|
|
67
116
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
117
|
+
function formatTime(ms: number): string {
|
|
118
|
+
return new Date(ms).toLocaleString();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function printSessions(streamJson = false): void {
|
|
122
|
+
const sessions = listBuiltinContextSessions();
|
|
123
|
+
if (streamJson) {
|
|
124
|
+
writeJsonLine({
|
|
125
|
+
type: "sessions",
|
|
126
|
+
sessions: sessions.map((session) => ({
|
|
127
|
+
session_id: session.sessionId,
|
|
128
|
+
turns: session.totalMessages,
|
|
129
|
+
compacted_messages: session.compactedMessages,
|
|
130
|
+
has_summary: session.hasSummary,
|
|
131
|
+
updated_at: session.updatedAt,
|
|
132
|
+
cwd: session.cwd,
|
|
133
|
+
})),
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (sessions.length === 0) {
|
|
139
|
+
console.log("No saved ccc sessions");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const session of sessions) {
|
|
144
|
+
const summary = session.hasSummary ? " summary=yes" : "";
|
|
145
|
+
const cwd = session.cwd ? ` cwd=${session.cwd}` : "";
|
|
146
|
+
console.log(`${session.sessionId} turns=${session.totalMessages} compacted=${session.compactedMessages}${summary} updated=${formatTime(session.updatedAt)}${cwd}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stringifyConsoleArg(value: unknown): string {
|
|
151
|
+
if (typeof value === "string") return value;
|
|
152
|
+
if (value instanceof Error) return value.stack ?? value.message;
|
|
153
|
+
try {
|
|
154
|
+
return JSON.stringify(value);
|
|
155
|
+
} catch {
|
|
156
|
+
return String(value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function redirectConsoleLogsToStderr(): void {
|
|
161
|
+
const write = (...args: unknown[]) => {
|
|
162
|
+
process.stderr.write(`${args.map(stringifyConsoleArg).join(" ")}\n`);
|
|
163
|
+
};
|
|
164
|
+
console.log = write;
|
|
165
|
+
console.info = write;
|
|
166
|
+
console.warn = write;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function writeJsonLine(event: JsonLine): void {
|
|
170
|
+
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function streamJsonEvent(event: ChatEvent): void {
|
|
174
|
+
if (event.type === "text") {
|
|
175
|
+
writeJsonLine({
|
|
176
|
+
type: "text_delta",
|
|
177
|
+
text: event.text,
|
|
178
|
+
accumulated: event.accumulated,
|
|
179
|
+
});
|
|
180
|
+
} else if (event.type === "compact") {
|
|
181
|
+
writeJsonLine({
|
|
182
|
+
type: "compact",
|
|
183
|
+
compacted_messages: event.compactedMessages,
|
|
184
|
+
});
|
|
185
|
+
} else if (event.type === "done") {
|
|
186
|
+
writeJsonLine({
|
|
187
|
+
type: "done",
|
|
188
|
+
text: event.text,
|
|
189
|
+
});
|
|
190
|
+
} else if (event.type === "error") {
|
|
191
|
+
writeJsonLine({
|
|
192
|
+
type: "error",
|
|
193
|
+
message: event.message,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function readPromptFromStdin(): Promise<string> {
|
|
199
|
+
const chunks: Buffer[] = [];
|
|
200
|
+
for await (const chunk of process.stdin) {
|
|
201
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
202
|
+
}
|
|
203
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function runStreamJson(args: ParsedArgs): Promise<number> {
|
|
207
|
+
redirectConsoleLogsToStderr();
|
|
208
|
+
|
|
209
|
+
if (args.listSessions) {
|
|
210
|
+
printSessions(true);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const prompt = args.prompt ?? (!process.stdin.isTTY ? await readPromptFromStdin() : "");
|
|
215
|
+
if (!prompt.trim()) {
|
|
216
|
+
writeJsonLine({ type: "error", message: "--stream-json requires --prompt <text> or stdin input" });
|
|
217
|
+
return 1;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let runtime: RuntimeDeps;
|
|
221
|
+
try {
|
|
222
|
+
runtime = await loadRuntime();
|
|
223
|
+
} catch (err) {
|
|
224
|
+
writeJsonLine({ type: "error", message: (err as Error).message });
|
|
225
|
+
return 1;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const cwd = resolvePath(args.options.cwd ?? process.cwd());
|
|
229
|
+
let resolvedSession;
|
|
230
|
+
try {
|
|
231
|
+
resolvedSession = resolveBuiltinSession({ cwd, resume: args.resume });
|
|
232
|
+
} catch (err) {
|
|
233
|
+
writeJsonLine({ type: "error", message: (err as Error).message });
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let session: InstanceType<RuntimeDeps["ChatSession"]>;
|
|
238
|
+
try {
|
|
239
|
+
session = new runtime.ChatSession(args.config, {
|
|
240
|
+
...args.options,
|
|
241
|
+
cwd,
|
|
242
|
+
persist: true,
|
|
243
|
+
sessionId: resolvedSession.sessionId,
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
writeJsonLine({ type: "error", message: (err as Error).message });
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
writeJsonLine({
|
|
251
|
+
type: "start",
|
|
252
|
+
session_id: resolvedSession.sessionId,
|
|
253
|
+
mode: resolvedSession.mode,
|
|
254
|
+
cwd,
|
|
255
|
+
model: args.config.model ?? runtime.appConfig.ccc.model,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
for await (const event of session.chat(prompt)) {
|
|
260
|
+
streamJsonEvent(event);
|
|
261
|
+
}
|
|
262
|
+
return 0;
|
|
263
|
+
} catch (err) {
|
|
264
|
+
writeJsonLine({ type: "error", message: (err as Error).message });
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
71
268
|
|
|
72
269
|
const C = {
|
|
73
270
|
reset: "\x1b[0m",
|
|
74
271
|
dim: "\x1b[2m",
|
|
75
272
|
green: "\x1b[32m",
|
|
76
|
-
cyan: "\x1b[36m",
|
|
77
273
|
yellow: "\x1b[33m",
|
|
78
274
|
};
|
|
79
275
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
276
|
+
async function runRepl(args: ParsedArgs): Promise<void> {
|
|
277
|
+
const { ChatSession, appConfig } = await loadRuntime();
|
|
83
278
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
279
|
+
const cwd = resolvePath(args.options.cwd ?? process.cwd());
|
|
280
|
+
let resolvedSession;
|
|
281
|
+
try {
|
|
282
|
+
resolvedSession = resolveBuiltinSession({ cwd, resume: args.resume });
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error(`${C.yellow}${(err as Error).message}${C.reset}`);
|
|
285
|
+
process.exit(1);
|
|
91
286
|
}
|
|
92
|
-
|
|
287
|
+
|
|
288
|
+
console.log(`${C.dim}ChatCCC builtin Agent${C.reset}`);
|
|
289
|
+
console.log(`${C.dim}Model: ${args.config.model ?? appConfig.ccc.model}${C.reset}`);
|
|
290
|
+
console.log(`${C.dim}Directory: ${cwd}${C.reset}`);
|
|
291
|
+
console.log(`${C.dim}Session: ${resolvedSession.sessionId} (${resolvedSession.mode === "new" ? "new" : "resumed"})${C.reset}`);
|
|
292
|
+
console.log(`${C.dim}Type a message to chat. Double Ctrl+C interrupts generation or exits. Type exit to quit.${C.reset}`);
|
|
93
293
|
console.log("");
|
|
94
294
|
|
|
95
|
-
let session: ChatSession
|
|
295
|
+
let session: InstanceType<typeof ChatSession>;
|
|
96
296
|
try {
|
|
97
|
-
session = new ChatSession(config, {
|
|
297
|
+
session = new ChatSession(args.config, {
|
|
298
|
+
...args.options,
|
|
299
|
+
cwd,
|
|
300
|
+
persist: true,
|
|
301
|
+
sessionId: resolvedSession.sessionId,
|
|
302
|
+
});
|
|
98
303
|
} catch (err) {
|
|
99
304
|
console.error(`${C.yellow}${(err as Error).message}${C.reset}`);
|
|
100
305
|
process.exit(1);
|
|
@@ -106,7 +311,6 @@ async function main(): Promise<void> {
|
|
|
106
311
|
prompt: `${C.green}>${C.reset} `,
|
|
107
312
|
});
|
|
108
313
|
|
|
109
|
-
// 用于中断当前 LLM 调用的 AbortController
|
|
110
314
|
let currentAbort: AbortController | null = null;
|
|
111
315
|
const ctrlCState = createCtrlCState();
|
|
112
316
|
|
|
@@ -120,27 +324,25 @@ async function main(): Promise<void> {
|
|
|
120
324
|
return;
|
|
121
325
|
}
|
|
122
326
|
|
|
123
|
-
// 特殊命令
|
|
124
327
|
if (input === "exit") {
|
|
125
|
-
console.log(`${C.dim}
|
|
328
|
+
console.log(`${C.dim}bye${C.reset}`);
|
|
126
329
|
rl.close();
|
|
127
330
|
return;
|
|
128
331
|
}
|
|
129
332
|
|
|
130
333
|
if (input === "/clear") {
|
|
131
334
|
session.reset();
|
|
132
|
-
console.log(`${C.dim}
|
|
335
|
+
console.log(`${C.dim}session cleared${C.reset}`);
|
|
133
336
|
rl.prompt();
|
|
134
337
|
return;
|
|
135
338
|
}
|
|
136
339
|
|
|
137
340
|
if (input === "/history") {
|
|
138
|
-
console.log(`${C.dim}
|
|
341
|
+
console.log(`${C.dim}${session.turnCount} conversation turns${C.reset}`);
|
|
139
342
|
rl.prompt();
|
|
140
343
|
return;
|
|
141
344
|
}
|
|
142
345
|
|
|
143
|
-
// 发送消息
|
|
144
346
|
currentAbort = new AbortController();
|
|
145
347
|
const signal = currentAbort.signal;
|
|
146
348
|
|
|
@@ -148,21 +350,20 @@ async function main(): Promise<void> {
|
|
|
148
350
|
let lastAccumulated = "";
|
|
149
351
|
for await (const event of session.chat(input, signal)) {
|
|
150
352
|
if (event.type === "text") {
|
|
151
|
-
// 增量输出(仅在首次和行首时不换行)
|
|
152
353
|
const newText = event.accumulated.slice(lastAccumulated.length);
|
|
153
354
|
process.stdout.write(newText);
|
|
154
355
|
lastAccumulated = event.accumulated;
|
|
155
356
|
} else if (event.type === "done") {
|
|
156
357
|
if (lastAccumulated) console.log("");
|
|
157
|
-
console.log(`${C.dim}[
|
|
358
|
+
console.log(`${C.dim}[done]${C.reset}`);
|
|
158
359
|
} else if (event.type === "compact") {
|
|
159
|
-
console.log(`${C.dim}[
|
|
360
|
+
console.log(`${C.dim}[context compacted: ${event.compactedMessages} old messages]${C.reset}`);
|
|
160
361
|
} else if (event.type === "error") {
|
|
161
|
-
console.log(`\n${C.yellow}[
|
|
362
|
+
console.log(`\n${C.yellow}[error] ${event.message}${C.reset}`);
|
|
162
363
|
}
|
|
163
364
|
}
|
|
164
365
|
} catch (err) {
|
|
165
|
-
console.log(`\n${C.yellow}[
|
|
366
|
+
console.log(`\n${C.yellow}[error] ${(err as Error).message}${C.reset}`);
|
|
166
367
|
} finally {
|
|
167
368
|
currentAbort = null;
|
|
168
369
|
ctrlCState.reset();
|
|
@@ -171,30 +372,29 @@ async function main(): Promise<void> {
|
|
|
171
372
|
rl.prompt();
|
|
172
373
|
});
|
|
173
374
|
|
|
174
|
-
// Ctrl+C -> generating requires confirmation to interrupt; idle requires confirmation to exit.
|
|
175
375
|
rl.on("SIGINT", () => {
|
|
176
376
|
const action = ctrlCState.press(currentAbort !== null);
|
|
177
377
|
|
|
178
378
|
if (action === "exit") {
|
|
179
|
-
console.log(`\n${C.dim}
|
|
379
|
+
console.log(`\n${C.dim}bye${C.reset}`);
|
|
180
380
|
rl.close();
|
|
181
381
|
return;
|
|
182
382
|
}
|
|
183
383
|
|
|
184
384
|
if (action === "interrupt") {
|
|
185
|
-
console.log(`\n${C.yellow}[
|
|
385
|
+
console.log(`\n${C.yellow}[interrupting...]${C.reset}`);
|
|
186
386
|
currentAbort?.abort();
|
|
187
387
|
currentAbort = null;
|
|
188
388
|
return;
|
|
189
389
|
}
|
|
190
390
|
|
|
191
391
|
if (action === "arm-interrupt") {
|
|
192
|
-
console.log(`\n${C.dim}
|
|
392
|
+
console.log(`\n${C.dim}Press Ctrl+C again to interrupt current response${C.reset}`);
|
|
193
393
|
return;
|
|
194
394
|
}
|
|
195
395
|
|
|
196
396
|
if (action === "arm-exit") {
|
|
197
|
-
console.log(`\n${C.dim}
|
|
397
|
+
console.log(`\n${C.dim}Press Ctrl+C again to exit, or type exit${C.reset}`);
|
|
198
398
|
rl.prompt();
|
|
199
399
|
}
|
|
200
400
|
});
|
|
@@ -205,7 +405,37 @@ async function main(): Promise<void> {
|
|
|
205
405
|
});
|
|
206
406
|
}
|
|
207
407
|
|
|
208
|
-
main()
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
408
|
+
async function main(): Promise<void> {
|
|
409
|
+
const args = parseArgs();
|
|
410
|
+
|
|
411
|
+
if (args.streamJson) {
|
|
412
|
+
const code = await runStreamJson(args);
|
|
413
|
+
process.exit(code);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (args.help) {
|
|
417
|
+
const { appConfig } = await loadRuntime();
|
|
418
|
+
printHelp(appConfig);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (args.listSessions) {
|
|
423
|
+
printSessions();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
await runRepl(args);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function isDirectCliInvocation(): boolean {
|
|
431
|
+
const current = resolvePath(fileURLToPath(import.meta.url));
|
|
432
|
+
const invoked = process.argv[1] ? resolvePath(process.argv[1]) : "";
|
|
433
|
+
return current === invoked;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (isDirectCliInvocation()) {
|
|
437
|
+
main().catch((err) => {
|
|
438
|
+
console.error(`${C.yellow}startup failed: ${(err as Error).message}${C.reset}`);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
});
|
|
441
|
+
}
|