@agentskit/cli 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/bin.cjs +622 -59
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-KXI7545I.js → chunk-V7E4HWTG.js} +617 -53
- package/dist/chunk-V7E4HWTG.js.map +1 -0
- package/dist/index.cjs +623 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -6
- package/dist/index.d.ts +61 -6
- package/dist/index.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-KXI7545I.js.map +0 -1
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, writeFile, readFile } from 'fs/promises';
|
|
3
|
-
import
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import path3, { join, resolve, basename } from 'path';
|
|
4
5
|
import { kimi, grok, deepseek, ollama, gemini, anthropic, openai } from '@agentskit/adapters';
|
|
5
|
-
import React3, { useMemo,
|
|
6
|
+
import React3, { useState, useMemo, useRef, useEffect } from 'react';
|
|
6
7
|
import { Box, Text, render } from 'ink';
|
|
7
|
-
import { useChat, ChatContainer, Message, ToolCallView, ThinkingIndicator, InputBar } from '@agentskit/ink';
|
|
8
|
-
import { shell, filesystem, webSearch } from '@agentskit/tools';
|
|
8
|
+
import { useChat, StatusHeader, ChatContainer, Message, ToolCallView, ToolConfirmation, ThinkingIndicator, InputBar } from '@agentskit/ink';
|
|
9
|
+
import { shell, filesystem, fetchUrl, webSearch } from '@agentskit/tools';
|
|
9
10
|
import { summarizer, critic, planner, coder, researcher, composeSkills } from '@agentskit/skills';
|
|
10
11
|
import { fileChatMemory, sqliteChatMemory } from '@agentskit/memory';
|
|
12
|
+
import { randomBytes, createHash } from 'crypto';
|
|
13
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
11
14
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
12
15
|
import { createRuntime } from '@agentskit/runtime';
|
|
13
|
-
import { existsSync } from 'fs';
|
|
14
16
|
import { spawn } from 'child_process';
|
|
15
17
|
import chokidar from 'chokidar';
|
|
16
18
|
import kleur3 from 'kleur';
|
|
@@ -45,16 +47,39 @@ async function loadPackageJsonConfig(dir) {
|
|
|
45
47
|
return void 0;
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
function mergeConfigs(base, override) {
|
|
51
|
+
if (!base && !override) return void 0;
|
|
52
|
+
if (!base) return override;
|
|
53
|
+
if (!override) return base;
|
|
54
|
+
return {
|
|
55
|
+
...base,
|
|
56
|
+
...override,
|
|
57
|
+
tools: { ...base.tools, ...override.tools },
|
|
58
|
+
defaults: { ...base.defaults, ...override.defaults },
|
|
59
|
+
runtime: { ...base.runtime, ...override.runtime },
|
|
60
|
+
observability: { ...base.observability, ...override.observability }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function loadLocalConfig(cwd) {
|
|
64
|
+
const tsConfig = await loadTsConfig(join(cwd, ".agentskit.config.ts"));
|
|
52
65
|
if (tsConfig) return tsConfig;
|
|
53
|
-
const
|
|
54
|
-
const jsonConfig = await loadJsonConfig(jsonPath);
|
|
66
|
+
const jsonConfig = await loadJsonConfig(join(cwd, ".agentskit.config.json"));
|
|
55
67
|
if (jsonConfig) return jsonConfig;
|
|
56
68
|
return await loadPackageJsonConfig(cwd);
|
|
57
69
|
}
|
|
70
|
+
async function loadGlobalConfig(home) {
|
|
71
|
+
if (home === null) return void 0;
|
|
72
|
+
const globalDir = join(home ?? homedir(), ".agentskit");
|
|
73
|
+
const tsConfig = await loadTsConfig(join(globalDir, "config.ts"));
|
|
74
|
+
if (tsConfig) return tsConfig;
|
|
75
|
+
return await loadJsonConfig(join(globalDir, "config.json"));
|
|
76
|
+
}
|
|
77
|
+
async function loadConfig(options) {
|
|
78
|
+
const cwd = resolve(options?.cwd ?? process.cwd());
|
|
79
|
+
const global = await loadGlobalConfig(options?.home);
|
|
80
|
+
const local = await loadLocalConfig(cwd);
|
|
81
|
+
return mergeConfigs(global, local);
|
|
82
|
+
}
|
|
58
83
|
var providers = {
|
|
59
84
|
openai: {
|
|
60
85
|
label: "OpenAI",
|
|
@@ -172,19 +197,34 @@ var skillRegistry = {
|
|
|
172
197
|
critic,
|
|
173
198
|
summarizer
|
|
174
199
|
};
|
|
200
|
+
function instantiate(kind) {
|
|
201
|
+
switch (kind) {
|
|
202
|
+
case "web_search":
|
|
203
|
+
return [webSearch()];
|
|
204
|
+
case "fetch_url":
|
|
205
|
+
return [fetchUrl()];
|
|
206
|
+
case "filesystem":
|
|
207
|
+
return filesystem({ basePath: process.cwd() });
|
|
208
|
+
case "shell":
|
|
209
|
+
return [shell({ timeout: 3e4 })];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function gateTool(tool) {
|
|
213
|
+
if (tool.requiresConfirmation === false) return tool;
|
|
214
|
+
return { ...tool, requiresConfirmation: true };
|
|
215
|
+
}
|
|
175
216
|
function resolveTools(toolNames) {
|
|
176
|
-
if (!toolNames)
|
|
217
|
+
if (!toolNames) {
|
|
218
|
+
return [...instantiate("web_search"), ...instantiate("fetch_url")].map(gateTool);
|
|
219
|
+
}
|
|
177
220
|
const tools = [];
|
|
178
|
-
for (const name of toolNames.split(",").map((s) => s.trim())) {
|
|
221
|
+
for (const name of toolNames.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
179
222
|
switch (name) {
|
|
180
223
|
case "web_search":
|
|
181
|
-
|
|
182
|
-
break;
|
|
224
|
+
case "fetch_url":
|
|
183
225
|
case "filesystem":
|
|
184
|
-
tools.push(...filesystem({ basePath: process.cwd() }));
|
|
185
|
-
break;
|
|
186
226
|
case "shell":
|
|
187
|
-
tools.push(
|
|
227
|
+
tools.push(...instantiate(name));
|
|
188
228
|
break;
|
|
189
229
|
default:
|
|
190
230
|
process.stderr.write(`Unknown tool: ${name}
|
|
@@ -224,41 +264,475 @@ function resolveMemory(backend, memoryPath) {
|
|
|
224
264
|
return fileChatMemory(memoryPath);
|
|
225
265
|
}
|
|
226
266
|
}
|
|
267
|
+
var ROOT = join(homedir(), ".agentskit", "sessions");
|
|
268
|
+
var META_SUFFIX = ".meta.json";
|
|
269
|
+
function cwdHash(cwd = process.cwd()) {
|
|
270
|
+
return createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
271
|
+
}
|
|
272
|
+
function dirFor(cwd = process.cwd()) {
|
|
273
|
+
return join(ROOT, cwdHash(cwd));
|
|
274
|
+
}
|
|
275
|
+
function ensureDir(dir) {
|
|
276
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
function generateSessionId() {
|
|
279
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
280
|
+
const suffix = randomBytes(3).toString("hex");
|
|
281
|
+
return `${ts}-${suffix}`;
|
|
282
|
+
}
|
|
283
|
+
function sessionFilePath(id, cwd = process.cwd()) {
|
|
284
|
+
ensureDir(dirFor(cwd));
|
|
285
|
+
return join(dirFor(cwd), `${id}.json`);
|
|
286
|
+
}
|
|
287
|
+
function metaPath(id, cwd = process.cwd()) {
|
|
288
|
+
return join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
289
|
+
}
|
|
290
|
+
function readMeta(id, cwd = process.cwd()) {
|
|
291
|
+
const path4 = metaPath(id, cwd);
|
|
292
|
+
if (!existsSync(path4)) return null;
|
|
293
|
+
try {
|
|
294
|
+
return JSON.parse(readFileSync(path4, "utf8"));
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function writeSessionMeta(meta, cwd = process.cwd()) {
|
|
300
|
+
ensureDir(dirFor(cwd));
|
|
301
|
+
writeFileSync(metaPath(meta.id, cwd), JSON.stringify(meta, null, 2));
|
|
302
|
+
}
|
|
303
|
+
function derivePreview(messages) {
|
|
304
|
+
const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
|
|
305
|
+
if (!firstUser) return "(empty)";
|
|
306
|
+
const single = firstUser.content.replace(/\s+/g, " ").trim();
|
|
307
|
+
return single.length > 80 ? `${single.slice(0, 80)}\u2026` : single;
|
|
308
|
+
}
|
|
309
|
+
function listSessions(cwd = process.cwd()) {
|
|
310
|
+
const dir = dirFor(cwd);
|
|
311
|
+
if (!existsSync(dir)) return [];
|
|
312
|
+
const entries = readdirSync(dir);
|
|
313
|
+
const records = [];
|
|
314
|
+
for (const entry of entries) {
|
|
315
|
+
if (!entry.endsWith(".json") || entry.endsWith(META_SUFFIX)) continue;
|
|
316
|
+
const id = entry.replace(/\.json$/, "");
|
|
317
|
+
const meta = readMeta(id, cwd);
|
|
318
|
+
const file = join(dir, entry);
|
|
319
|
+
if (meta) {
|
|
320
|
+
records.push({ metadata: meta, file });
|
|
321
|
+
} else {
|
|
322
|
+
const stats = statSync(file);
|
|
323
|
+
records.push({
|
|
324
|
+
metadata: {
|
|
325
|
+
id,
|
|
326
|
+
cwd,
|
|
327
|
+
createdAt: stats.birthtime.toISOString(),
|
|
328
|
+
updatedAt: stats.mtime.toISOString(),
|
|
329
|
+
messageCount: 0,
|
|
330
|
+
preview: "(legacy session)"
|
|
331
|
+
},
|
|
332
|
+
file
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
records.sort((a, b) => b.metadata.updatedAt.localeCompare(a.metadata.updatedAt));
|
|
337
|
+
return records;
|
|
338
|
+
}
|
|
339
|
+
function findLatestSession(cwd = process.cwd()) {
|
|
340
|
+
const all = listSessions(cwd);
|
|
341
|
+
return all[0] ?? null;
|
|
342
|
+
}
|
|
343
|
+
function findSession(id, cwd = process.cwd()) {
|
|
344
|
+
const exact = listSessions(cwd).find((s) => s.metadata.id === id);
|
|
345
|
+
if (exact) return exact;
|
|
346
|
+
const prefix = listSessions(cwd).find((s) => s.metadata.id.startsWith(id));
|
|
347
|
+
return prefix ?? null;
|
|
348
|
+
}
|
|
349
|
+
function resolveSession(input2) {
|
|
350
|
+
const cwd = input2.cwd ?? process.cwd();
|
|
351
|
+
if (input2.explicitPath) {
|
|
352
|
+
return { id: "custom", file: input2.explicitPath, isNew: !existsSync(input2.explicitPath) };
|
|
353
|
+
}
|
|
354
|
+
if (input2.forceNew) {
|
|
355
|
+
const id2 = generateSessionId();
|
|
356
|
+
return { id: id2, file: sessionFilePath(id2, cwd), isNew: true };
|
|
357
|
+
}
|
|
358
|
+
if (input2.resumeId) {
|
|
359
|
+
const target = input2.resumeId === true ? findLatestSession(cwd) : findSession(input2.resumeId, cwd);
|
|
360
|
+
if (target) {
|
|
361
|
+
return { id: target.metadata.id, file: target.file, isNew: false };
|
|
362
|
+
}
|
|
363
|
+
process.stderr.write(
|
|
364
|
+
`No session matching "${String(input2.resumeId)}" \u2014 starting a new one.
|
|
365
|
+
`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
const latest = findLatestSession(cwd);
|
|
369
|
+
if (latest) return { id: latest.metadata.id, file: latest.file, isNew: false };
|
|
370
|
+
const id = generateSessionId();
|
|
371
|
+
return { id, file: sessionFilePath(id, cwd), isNew: true };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/slash-commands.ts
|
|
375
|
+
function parseSlashCommand(input2) {
|
|
376
|
+
if (!input2.startsWith("/")) return null;
|
|
377
|
+
const match = input2.slice(1).match(/^(\S+)\s*([\s\S]*)$/);
|
|
378
|
+
if (!match) return null;
|
|
379
|
+
return { name: match[1], args: match[2] ?? "" };
|
|
380
|
+
}
|
|
381
|
+
function createSlashRegistry(commands) {
|
|
382
|
+
const map = /* @__PURE__ */ new Map();
|
|
383
|
+
for (const cmd of commands) {
|
|
384
|
+
map.set(cmd.name, cmd);
|
|
385
|
+
for (const alias of cmd.aliases ?? []) map.set(alias, cmd);
|
|
386
|
+
}
|
|
387
|
+
return map;
|
|
388
|
+
}
|
|
389
|
+
var builtinSlashCommands = [
|
|
390
|
+
{
|
|
391
|
+
name: "help",
|
|
392
|
+
aliases: ["?"],
|
|
393
|
+
description: "List available slash commands.",
|
|
394
|
+
run(ctx) {
|
|
395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
396
|
+
const lines = [];
|
|
397
|
+
for (const cmd of ctx.commands) {
|
|
398
|
+
if (seen.has(cmd.name)) continue;
|
|
399
|
+
seen.add(cmd.name);
|
|
400
|
+
const suffix = cmd.usage ? ` (${cmd.usage})` : "";
|
|
401
|
+
lines.push(` /${cmd.name.padEnd(10)} ${cmd.description}${suffix}`);
|
|
402
|
+
}
|
|
403
|
+
ctx.feedback(`Slash commands:
|
|
404
|
+
${lines.join("\n")}`, "info");
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: "model",
|
|
409
|
+
description: "Switch the active model.",
|
|
410
|
+
usage: "/model <name>",
|
|
411
|
+
run(ctx, args) {
|
|
412
|
+
const value = args.trim();
|
|
413
|
+
if (!value) {
|
|
414
|
+
ctx.feedback(
|
|
415
|
+
`Current model: ${ctx.runtime.model ?? "unset"}. Usage: /model <name>`,
|
|
416
|
+
"warn"
|
|
417
|
+
);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
ctx.setModel(value);
|
|
421
|
+
ctx.feedback(`Model \u2192 ${value}`, "success");
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: "provider",
|
|
426
|
+
description: "Switch the adapter provider.",
|
|
427
|
+
usage: "/provider openai|anthropic|gemini|ollama|deepseek|grok|kimi|demo",
|
|
428
|
+
run(ctx, args) {
|
|
429
|
+
const value = args.trim();
|
|
430
|
+
if (!value) {
|
|
431
|
+
ctx.feedback(
|
|
432
|
+
`Current provider: ${ctx.runtime.provider}. Usage: /provider <name>`,
|
|
433
|
+
"warn"
|
|
434
|
+
);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
ctx.setProvider(value);
|
|
438
|
+
ctx.feedback(`Provider \u2192 ${value}`, "success");
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: "base-url",
|
|
443
|
+
aliases: ["baseurl"],
|
|
444
|
+
description: "Override provider base URL.",
|
|
445
|
+
usage: "/base-url <url|clear>",
|
|
446
|
+
run(ctx, args) {
|
|
447
|
+
const value = args.trim();
|
|
448
|
+
if (!value || value === "clear") {
|
|
449
|
+
ctx.setBaseUrl(void 0);
|
|
450
|
+
ctx.feedback("Base URL cleared.", "success");
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
ctx.setBaseUrl(value);
|
|
454
|
+
ctx.feedback(`Base URL \u2192 ${value}`, "success");
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "tools",
|
|
459
|
+
description: "Set active tools (comma-separated) or clear them.",
|
|
460
|
+
usage: "/tools web_search,fetch_url | /tools clear",
|
|
461
|
+
run(ctx, args) {
|
|
462
|
+
const value = args.trim();
|
|
463
|
+
if (!value || value === "clear") {
|
|
464
|
+
ctx.setTools(void 0);
|
|
465
|
+
ctx.feedback("Tools reset to defaults.", "success");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
ctx.setTools(value);
|
|
469
|
+
ctx.feedback(`Tools \u2192 ${value}`, "success");
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "skill",
|
|
474
|
+
description: "Set active skill(s) (comma-separated) or clear them.",
|
|
475
|
+
usage: "/skill researcher,coder | /skill clear",
|
|
476
|
+
run(ctx, args) {
|
|
477
|
+
const value = args.trim();
|
|
478
|
+
if (!value || value === "clear") {
|
|
479
|
+
ctx.setSkill(void 0);
|
|
480
|
+
ctx.feedback("Skills cleared.", "success");
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
ctx.setSkill(value);
|
|
484
|
+
ctx.feedback(`Skills \u2192 ${value}`, "success");
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: "clear",
|
|
489
|
+
aliases: ["reset"],
|
|
490
|
+
description: "Clear the conversation history in this session.",
|
|
491
|
+
async run(ctx) {
|
|
492
|
+
await ctx.chat.clear();
|
|
493
|
+
ctx.feedback("History cleared.", "success");
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "exit",
|
|
498
|
+
aliases: ["quit", "q"],
|
|
499
|
+
description: "Exit the chat.",
|
|
500
|
+
run() {
|
|
501
|
+
process.exit(0);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
];
|
|
505
|
+
function groupIntoTurns(messages) {
|
|
506
|
+
const turns = [];
|
|
507
|
+
let current = [];
|
|
508
|
+
for (const message of messages) {
|
|
509
|
+
if (message.role === "user") {
|
|
510
|
+
if (current.length > 0) turns.push(current);
|
|
511
|
+
current = [message];
|
|
512
|
+
} else if (message.role === "system") {
|
|
513
|
+
if (current.length > 0) turns.push(current);
|
|
514
|
+
turns.push([message]);
|
|
515
|
+
current = [];
|
|
516
|
+
} else {
|
|
517
|
+
current.push(message);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (current.length > 0) turns.push(current);
|
|
521
|
+
return turns;
|
|
522
|
+
}
|
|
227
523
|
function ChatApp(options) {
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
524
|
+
const [provider, setProvider] = useState(options.provider);
|
|
525
|
+
const [model, setModel] = useState(options.model);
|
|
526
|
+
const [apiKey, setApiKey] = useState(options.apiKey);
|
|
527
|
+
const [baseUrl, setBaseUrl] = useState(options.baseUrl);
|
|
528
|
+
const [toolsFlag, setToolsFlag] = useState(options.tools);
|
|
529
|
+
const [skillFlag, setSkillFlag] = useState(options.skill);
|
|
530
|
+
const runtime = useMemo(
|
|
531
|
+
() => resolveChatProvider({ provider, model, apiKey, baseUrl }),
|
|
532
|
+
[provider, model, apiKey, baseUrl]
|
|
231
533
|
);
|
|
232
534
|
const memory = useMemo(
|
|
233
535
|
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
234
536
|
[options.memoryPath, options.memoryBackend]
|
|
235
537
|
);
|
|
236
|
-
const tools = useMemo(() => resolveTools(
|
|
538
|
+
const tools = useMemo(() => resolveTools(toolsFlag), [toolsFlag]);
|
|
237
539
|
const skills = useMemo(() => {
|
|
238
|
-
if (!
|
|
239
|
-
const names =
|
|
540
|
+
if (!skillFlag) return void 0;
|
|
541
|
+
const names = skillFlag.split(",").map((s) => s.trim());
|
|
240
542
|
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
241
543
|
if (resolved.length === 0) return void 0;
|
|
242
544
|
return resolved;
|
|
243
|
-
}, [
|
|
545
|
+
}, [skillFlag]);
|
|
244
546
|
const chat = useChat({
|
|
245
|
-
adapter,
|
|
547
|
+
adapter: runtime.adapter,
|
|
246
548
|
memory,
|
|
247
549
|
systemPrompt: options.system,
|
|
248
550
|
tools: tools.length > 0 ? tools : void 0,
|
|
249
551
|
skills
|
|
250
552
|
});
|
|
553
|
+
const [sessionAllowed, setSessionAllowed] = useState(/* @__PURE__ */ new Set());
|
|
554
|
+
const autoApprovedRef = useRef(/* @__PURE__ */ new Set());
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
if (sessionAllowed.size === 0) return;
|
|
557
|
+
for (const message of chat.messages) {
|
|
558
|
+
for (const call of message.toolCalls ?? []) {
|
|
559
|
+
if (call.status === "requires_confirmation" && sessionAllowed.has(call.name) && !autoApprovedRef.current.has(call.id)) {
|
|
560
|
+
autoApprovedRef.current.add(call.id);
|
|
561
|
+
void chat.approve(call.id);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}, [chat.messages, sessionAllowed, chat.approve]);
|
|
566
|
+
const handleApproveAlways = (toolCallId, toolName) => {
|
|
567
|
+
setSessionAllowed((prev) => {
|
|
568
|
+
if (prev.has(toolName)) return prev;
|
|
569
|
+
const next = new Set(prev);
|
|
570
|
+
next.add(toolName);
|
|
571
|
+
return next;
|
|
572
|
+
});
|
|
573
|
+
autoApprovedRef.current.add(toolCallId);
|
|
574
|
+
void chat.approve(toolCallId);
|
|
575
|
+
};
|
|
576
|
+
const sessionCreatedAtRef = useRef(void 0);
|
|
577
|
+
const messageCount = chat.messages.length;
|
|
578
|
+
const firstUserContent = chat.messages.find((m) => m.role === "user")?.content ?? "";
|
|
579
|
+
useEffect(() => {
|
|
580
|
+
const sessionId = options.sessionId;
|
|
581
|
+
if (!sessionId || sessionId === "custom") return;
|
|
582
|
+
if (!sessionCreatedAtRef.current) {
|
|
583
|
+
sessionCreatedAtRef.current = (/* @__PURE__ */ new Date()).toISOString();
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
writeSessionMeta({
|
|
587
|
+
id: sessionId,
|
|
588
|
+
cwd: process.cwd(),
|
|
589
|
+
createdAt: sessionCreatedAtRef.current,
|
|
590
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
591
|
+
messageCount,
|
|
592
|
+
preview: derivePreview(chat.messages),
|
|
593
|
+
provider: runtime.provider,
|
|
594
|
+
model: runtime.model
|
|
595
|
+
});
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
}, [options.sessionId, messageCount, firstUserContent, runtime.provider, runtime.model]);
|
|
599
|
+
const turns = useMemo(() => groupIntoTurns(chat.messages), [chat.messages]);
|
|
600
|
+
const toolNames = toolsFlag ? toolsFlag.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
601
|
+
const [feedback, setFeedback] = useState(null);
|
|
602
|
+
const slashCommands = useMemo(
|
|
603
|
+
() => [...builtinSlashCommands, ...options.slashCommands ?? []],
|
|
604
|
+
[options.slashCommands]
|
|
605
|
+
);
|
|
606
|
+
const slashRegistry = useMemo(() => createSlashRegistry(slashCommands), [slashCommands]);
|
|
607
|
+
const handleSubmitInput = async (raw) => {
|
|
608
|
+
const parsed = parseSlashCommand(raw);
|
|
609
|
+
if (!parsed) {
|
|
610
|
+
setFeedback(null);
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
const cmd = slashRegistry.get(parsed.name);
|
|
614
|
+
if (!cmd) {
|
|
615
|
+
setFeedback({
|
|
616
|
+
message: `Unknown command: /${parsed.name}. Type /help for the list.`,
|
|
617
|
+
kind: "error"
|
|
618
|
+
});
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
const ctx = {
|
|
622
|
+
chat,
|
|
623
|
+
runtime: {
|
|
624
|
+
provider: runtime.provider,
|
|
625
|
+
model: runtime.model,
|
|
626
|
+
mode: runtime.mode,
|
|
627
|
+
baseUrl,
|
|
628
|
+
tools: toolsFlag,
|
|
629
|
+
skill: skillFlag
|
|
630
|
+
},
|
|
631
|
+
setProvider,
|
|
632
|
+
setModel,
|
|
633
|
+
setApiKey,
|
|
634
|
+
setBaseUrl,
|
|
635
|
+
setTools: setToolsFlag,
|
|
636
|
+
setSkill: setSkillFlag,
|
|
637
|
+
feedback: (message, kind = "info") => setFeedback({ message, kind }),
|
|
638
|
+
commands: slashCommands
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
await cmd.run(ctx, parsed.args);
|
|
642
|
+
} catch (err) {
|
|
643
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
644
|
+
setFeedback({ message: `/${parsed.name} failed: ${message}`, kind: "error" });
|
|
645
|
+
}
|
|
646
|
+
return true;
|
|
647
|
+
};
|
|
648
|
+
const awaitingConfirmation = useMemo(
|
|
649
|
+
() => chat.messages.some(
|
|
650
|
+
(message) => message.toolCalls?.some(
|
|
651
|
+
(call) => call.status === "requires_confirmation" && !sessionAllowed.has(call.name)
|
|
652
|
+
)
|
|
653
|
+
),
|
|
654
|
+
[chat.messages, sessionAllowed]
|
|
655
|
+
);
|
|
251
656
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
|
|
252
|
-
/* @__PURE__ */ jsx(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
657
|
+
/* @__PURE__ */ jsx(
|
|
658
|
+
StatusHeader,
|
|
659
|
+
{
|
|
660
|
+
provider: runtime.provider,
|
|
661
|
+
model: runtime.model,
|
|
662
|
+
mode: runtime.mode,
|
|
663
|
+
tools: toolNames,
|
|
664
|
+
messageCount: chat.messages.length,
|
|
665
|
+
sessionId: options.sessionId
|
|
666
|
+
}
|
|
667
|
+
),
|
|
668
|
+
/* @__PURE__ */ jsx(ChatContainer, { children: turns.map((turn, turnIdx) => {
|
|
669
|
+
const assistantSteps = turn.filter((m) => m.role === "assistant").length;
|
|
670
|
+
let stepIndex = 0;
|
|
671
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", gap: 1, children: turn.map((message) => {
|
|
672
|
+
const showStep = message.role === "assistant" && assistantSteps > 1;
|
|
673
|
+
if (showStep) stepIndex++;
|
|
674
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
675
|
+
showStep ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
676
|
+
"\u21BB step ",
|
|
677
|
+
stepIndex,
|
|
678
|
+
"/",
|
|
679
|
+
assistantSteps
|
|
680
|
+
] }) : null,
|
|
681
|
+
/* @__PURE__ */ jsx(Message, { message }),
|
|
682
|
+
message.toolCalls?.map((toolCall) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
683
|
+
/* @__PURE__ */ jsx(ToolCallView, { toolCall, expanded: true }),
|
|
684
|
+
toolCall.status === "requires_confirmation" && !sessionAllowed.has(toolCall.name) ? /* @__PURE__ */ jsx(
|
|
685
|
+
ToolConfirmation,
|
|
686
|
+
{
|
|
687
|
+
toolCall,
|
|
688
|
+
onApprove: chat.approve,
|
|
689
|
+
onDeny: chat.deny,
|
|
690
|
+
onApproveAlways: handleApproveAlways
|
|
691
|
+
}
|
|
692
|
+
) : null
|
|
693
|
+
] }, toolCall.id))
|
|
694
|
+
] }, message.id);
|
|
695
|
+
}) }, `turn-${turnIdx}`);
|
|
696
|
+
}) }),
|
|
697
|
+
/* @__PURE__ */ jsx(
|
|
698
|
+
ThinkingIndicator,
|
|
699
|
+
{
|
|
700
|
+
visible: chat.status === "streaming",
|
|
701
|
+
label: toolNames.length > 0 ? "agent working" : "thinking"
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
chat.error ? /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", children: [
|
|
705
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", bold: true, children: [
|
|
706
|
+
"\u2717 ",
|
|
707
|
+
chat.error.name || "Error"
|
|
708
|
+
] }),
|
|
709
|
+
/* @__PURE__ */ jsx(Text, { color: "red", children: chat.error.message })
|
|
710
|
+
] }) : null,
|
|
711
|
+
feedback ? /* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: feedbackBorder(feedback.kind), paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: feedbackBorder(feedback.kind), children: feedback.message }) }) : null,
|
|
712
|
+
/* @__PURE__ */ jsx(
|
|
713
|
+
InputBar,
|
|
714
|
+
{
|
|
715
|
+
chat,
|
|
716
|
+
placeholder: "Type a message or /help for commands",
|
|
717
|
+
disabled: awaitingConfirmation,
|
|
718
|
+
onSubmitInput: handleSubmitInput
|
|
719
|
+
}
|
|
720
|
+
)
|
|
260
721
|
] });
|
|
261
722
|
}
|
|
723
|
+
function feedbackBorder(kind) {
|
|
724
|
+
switch (kind) {
|
|
725
|
+
case "error":
|
|
726
|
+
return "red";
|
|
727
|
+
case "warn":
|
|
728
|
+
return "yellow";
|
|
729
|
+
case "success":
|
|
730
|
+
return "green";
|
|
731
|
+
case "info":
|
|
732
|
+
default:
|
|
733
|
+
return "cyan";
|
|
734
|
+
}
|
|
735
|
+
}
|
|
262
736
|
function renderChatHeader(options) {
|
|
263
737
|
const runtime = resolveChatProvider(options);
|
|
264
738
|
const parts = [`provider=${runtime.provider}`];
|
|
@@ -367,7 +841,7 @@ function reactStarter(ctx) {
|
|
|
367
841
|
return {
|
|
368
842
|
"package.json": JSON.stringify(
|
|
369
843
|
{
|
|
370
|
-
name:
|
|
844
|
+
name: path3.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
371
845
|
private: true,
|
|
372
846
|
type: "module",
|
|
373
847
|
scripts: {
|
|
@@ -736,8 +1210,8 @@ async function writeStarterProject(options) {
|
|
|
736
1210
|
await mkdir(options.targetDir, { recursive: true });
|
|
737
1211
|
await Promise.all(
|
|
738
1212
|
Object.entries(files).map(async ([relativePath, content]) => {
|
|
739
|
-
const absolutePath =
|
|
740
|
-
await mkdir(
|
|
1213
|
+
const absolutePath = path3.join(options.targetDir, relativePath);
|
|
1214
|
+
await mkdir(path3.dirname(absolutePath), { recursive: true });
|
|
741
1215
|
await writeFile(absolutePath, content, "utf8");
|
|
742
1216
|
})
|
|
743
1217
|
);
|
|
@@ -1245,7 +1719,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1245
1719
|
default: defaults.dir ?? "agentskit-app",
|
|
1246
1720
|
validate: (value) => {
|
|
1247
1721
|
if (!value.trim()) return "A directory name is required.";
|
|
1248
|
-
const abs =
|
|
1722
|
+
const abs = path3.resolve(process.cwd(), value);
|
|
1249
1723
|
if (existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
1250
1724
|
return true;
|
|
1251
1725
|
}
|
|
@@ -1323,7 +1797,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1323
1797
|
return {
|
|
1324
1798
|
cancelled: false,
|
|
1325
1799
|
options: {
|
|
1326
|
-
targetDir:
|
|
1800
|
+
targetDir: path3.resolve(process.cwd(), targetDir),
|
|
1327
1801
|
template,
|
|
1328
1802
|
provider,
|
|
1329
1803
|
tools,
|
|
@@ -1340,7 +1814,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1340
1814
|
}
|
|
1341
1815
|
}
|
|
1342
1816
|
function printNextSteps(options) {
|
|
1343
|
-
const dir =
|
|
1817
|
+
const dir = path3.relative(process.cwd(), options.targetDir) || ".";
|
|
1344
1818
|
const pm = options.packageManager ?? "pnpm";
|
|
1345
1819
|
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
1346
1820
|
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
@@ -1463,34 +1937,87 @@ function RunApp({ task, options }) {
|
|
|
1463
1937
|
// src/commands.ts
|
|
1464
1938
|
function mergeWithConfig(options, config) {
|
|
1465
1939
|
if (!config) return options;
|
|
1940
|
+
const d = config.defaults ?? {};
|
|
1941
|
+
const resolvedApiKey = options.apiKey ?? (d.apiKeyEnv ? process.env[d.apiKeyEnv] : void 0) ?? d.apiKey;
|
|
1466
1942
|
return {
|
|
1467
1943
|
...options,
|
|
1468
1944
|
// Config defaults — only apply if CLI flag wasn't set
|
|
1469
|
-
provider: options.provider !== "demo" ? options.provider :
|
|
1470
|
-
model: options.model ??
|
|
1945
|
+
provider: options.provider !== "demo" ? options.provider : d.provider ?? options.provider,
|
|
1946
|
+
model: options.model ?? d.model,
|
|
1947
|
+
apiKey: resolvedApiKey,
|
|
1948
|
+
baseUrl: options.baseUrl ?? d.baseUrl,
|
|
1949
|
+
tools: options.tools ?? d.tools,
|
|
1950
|
+
skill: options.skill ?? d.skill,
|
|
1951
|
+
system: options.system ?? d.system,
|
|
1952
|
+
memoryBackend: options.memoryBackend ?? d.memoryBackend
|
|
1471
1953
|
};
|
|
1472
1954
|
}
|
|
1473
1955
|
function createCli() {
|
|
1474
1956
|
const program = new Command();
|
|
1475
1957
|
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
1476
|
-
program.command("chat").description("Start a terminal chat session.").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--system <prompt>", "System prompt").option("--memory <path>", "
|
|
1958
|
+
program.command("chat").description("Start a terminal chat session.").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--system <prompt>", "System prompt").option("--memory <path>", "Explicit memory file path (overrides session management)").option("--tools <tools>", "Comma-separated tools: web_search,fetch_url,filesystem,shell").option("--skill <skills>", "Comma-separated skills: researcher,coder,planner,critic,summarizer").option("--memory-backend <backend>", "Memory backend: file (default), sqlite").option("--new", "Start a fresh chat session (ignore previous conversations in this directory)").option("--resume [id]", "Resume a prior session by id; omit id to resume the latest").option("--list-sessions", "List saved sessions for this directory and exit").option("--no-config", "Skip loading .agentskit.config.json").action(async (options) => {
|
|
1959
|
+
if (options.listSessions) {
|
|
1960
|
+
const sessions = listSessions();
|
|
1961
|
+
if (sessions.length === 0) {
|
|
1962
|
+
process.stdout.write("No saved sessions for this directory.\n");
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
for (const s of sessions) {
|
|
1966
|
+
const { id, updatedAt, messageCount, preview, model } = s.metadata;
|
|
1967
|
+
process.stdout.write(
|
|
1968
|
+
`${id} ${updatedAt} msgs=${messageCount}${model ? ` model=${model}` : ""}
|
|
1969
|
+
${preview}
|
|
1970
|
+
`
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1477
1975
|
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1478
1976
|
const merged = mergeWithConfig(options, config);
|
|
1977
|
+
const session = resolveSession({
|
|
1978
|
+
explicitPath: options.memory,
|
|
1979
|
+
forceNew: Boolean(options.new),
|
|
1980
|
+
resumeId: options.resume
|
|
1981
|
+
});
|
|
1982
|
+
if (!session.isNew && !options.memory) {
|
|
1983
|
+
process.stdout.write(
|
|
1984
|
+
`Resuming session ${session.id}. Start fresh with --new or list with --list-sessions.
|
|
1985
|
+
`
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1479
1988
|
const chatOptions = {
|
|
1480
1989
|
apiKey: merged.apiKey ?? options.apiKey,
|
|
1481
1990
|
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1482
1991
|
provider: merged.provider,
|
|
1483
1992
|
model: merged.model,
|
|
1484
|
-
system: options.system,
|
|
1485
|
-
memoryPath:
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1993
|
+
system: merged.system ?? options.system,
|
|
1994
|
+
memoryPath: session.file,
|
|
1995
|
+
sessionId: session.id,
|
|
1996
|
+
tools: merged.tools ?? options.tools,
|
|
1997
|
+
skill: merged.skill ?? options.skill,
|
|
1998
|
+
memoryBackend: merged.memoryBackend ?? options.memoryBackend,
|
|
1489
1999
|
agentsKitConfig: config
|
|
1490
2000
|
};
|
|
1491
2001
|
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1492
2002
|
`);
|
|
1493
|
-
render(React3.createElement(ChatApp, chatOptions));
|
|
2003
|
+
const instance = render(React3.createElement(ChatApp, chatOptions));
|
|
2004
|
+
await instance.waitUntilExit();
|
|
2005
|
+
if (options.memory) {
|
|
2006
|
+
process.stdout.write(
|
|
2007
|
+
`
|
|
2008
|
+
Session saved to ${session.file}. Resume with --memory ${session.file}
|
|
2009
|
+
`
|
|
2010
|
+
);
|
|
2011
|
+
} else {
|
|
2012
|
+
process.stdout.write(
|
|
2013
|
+
`
|
|
2014
|
+
Session saved. Resume with:
|
|
2015
|
+
agentskit chat --resume ${session.id}
|
|
2016
|
+
Or start fresh with:
|
|
2017
|
+
agentskit chat --new
|
|
2018
|
+
`
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
1494
2021
|
});
|
|
1495
2022
|
program.command("run [task]").description("Execute an agent task and output the result.").option("--task <task>", "Task string (alternative to positional argument)").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--skill <skill>", "Single skill to use").option("--skills <skills>", "Comma-separated skills (composed together)").option("--tools <tools>", "Comma-separated tools: web_search,filesystem,shell").option("--memory <path>", "Path for memory persistence").option("--memory-backend <backend>", "Memory backend: file (default), sqlite").option("--system-prompt <prompt>", "System prompt").option("--max-steps <steps>", "Maximum agent steps", "10").option("--verbose", "Stream agent steps to stderr").option("--pretty", "Use rich Ink-based output").option("--no-config", "Skip loading .agentskit.config.json").action(async (positionalTask, options) => {
|
|
1496
2023
|
const task = options.task ?? positionalTask;
|
|
@@ -1518,7 +2045,7 @@ function createCli() {
|
|
|
1518
2045
|
if (isCi) {
|
|
1519
2046
|
const template = rawOptions.template ?? "react";
|
|
1520
2047
|
resolved = {
|
|
1521
|
-
targetDir:
|
|
2048
|
+
targetDir: path3.resolve(process.cwd(), rawOptions.dir),
|
|
1522
2049
|
template,
|
|
1523
2050
|
provider: rawOptions.provider ?? "demo",
|
|
1524
2051
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
@@ -1538,7 +2065,7 @@ function createCli() {
|
|
|
1538
2065
|
await writeStarterProject(resolved);
|
|
1539
2066
|
if (isCi) {
|
|
1540
2067
|
process.stdout.write(
|
|
1541
|
-
`Created ${resolved.template} starter in ${
|
|
2068
|
+
`Created ${resolved.template} starter in ${path3.relative(process.cwd(), resolved.targetDir) || "."}
|
|
1542
2069
|
`
|
|
1543
2070
|
);
|
|
1544
2071
|
} else {
|
|
@@ -1579,6 +2106,43 @@ function createCli() {
|
|
|
1579
2106
|
process.exit(1);
|
|
1580
2107
|
}
|
|
1581
2108
|
});
|
|
2109
|
+
program.command("config").description("Show or scaffold the AgentsKit config.").argument("[action]", 'Action: "init" to create a template, "show" to print the merged config.', "show").option("--global", "Write/read the global config at ~/.agentskit/config.json (default)").option("--local", "Write/read a project-level .agentskit.config.json in the current directory").option("--force", "Overwrite an existing config file").action(async (action, options) => {
|
|
2110
|
+
const isLocal = Boolean(options.local);
|
|
2111
|
+
const targetPath = isLocal ? path3.join(process.cwd(), ".agentskit.config.json") : path3.join(homedir(), ".agentskit", "config.json");
|
|
2112
|
+
if (action === "show") {
|
|
2113
|
+
const config = await loadConfig();
|
|
2114
|
+
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
if (action !== "init") {
|
|
2118
|
+
process.stderr.write(`Unknown action: ${action}. Use "init" or "show".
|
|
2119
|
+
`);
|
|
2120
|
+
process.exit(2);
|
|
2121
|
+
}
|
|
2122
|
+
if (existsSync(targetPath) && !options.force) {
|
|
2123
|
+
process.stderr.write(`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
2124
|
+
`);
|
|
2125
|
+
process.exit(1);
|
|
2126
|
+
}
|
|
2127
|
+
const template = {
|
|
2128
|
+
defaults: {
|
|
2129
|
+
provider: "openai",
|
|
2130
|
+
baseUrl: "https://openrouter.ai/api",
|
|
2131
|
+
apiKeyEnv: "OPENROUTER_API_KEY",
|
|
2132
|
+
model: "openai/gpt-oss-120b:free",
|
|
2133
|
+
tools: "web_search,fetch_url"
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
mkdirSync(path3.dirname(targetPath), { recursive: true });
|
|
2137
|
+
writeFileSync(targetPath, JSON.stringify(template, null, 2) + "\n");
|
|
2138
|
+
process.stdout.write(
|
|
2139
|
+
`Wrote ${targetPath}
|
|
2140
|
+
Edit it to taste, then run:
|
|
2141
|
+
agentskit chat
|
|
2142
|
+
(flags on the CLI still win over config values.)
|
|
2143
|
+
`
|
|
2144
|
+
);
|
|
2145
|
+
});
|
|
1582
2146
|
program.command("tunnel <port>").description("Open a public URL pointing to a local port (great for webhooks).").option("--subdomain <name>", "Hint for a stable subdomain (provider may decline)").option("--host <host>", "Local hostname", "localhost").action(async (port, options) => {
|
|
1583
2147
|
const portNum = Number(port);
|
|
1584
2148
|
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
@@ -1603,5 +2167,5 @@ function createCli() {
|
|
|
1603
2167
|
}
|
|
1604
2168
|
|
|
1605
2169
|
export { ChatApp, createCli, loadConfig, renderChatHeader, renderReport, resolveChatProvider, runAgent, runDoctor, startDev, startTunnel, writeStarterProject };
|
|
1606
|
-
//# sourceMappingURL=chunk-
|
|
1607
|
-
//# sourceMappingURL=chunk-
|
|
2170
|
+
//# sourceMappingURL=chunk-V7E4HWTG.js.map
|
|
2171
|
+
//# sourceMappingURL=chunk-V7E4HWTG.js.map
|