@moikapy/lich 0.7.0 → 0.8.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/CHANGELOG.md +75 -5
- package/README.md +38 -9
- package/dist/{chunk-QVJCIZIF.js → chunk-EDRUZF22.js} +763 -286
- package/dist/chunk-EDRUZF22.js.map +1 -0
- package/dist/chunk-PL6MKRKE.js +75 -0
- package/dist/chunk-PL6MKRKE.js.map +1 -0
- package/dist/{chunk-JYURFAGB.js → chunk-QOTECFCN.js} +2 -2
- package/dist/chunk-VKEOHUCB.js +86 -0
- package/dist/chunk-VKEOHUCB.js.map +1 -0
- package/dist/{chunk-SAEB3QL3.js → chunk-W6JXZBUE.js} +2 -2
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +53 -11
- package/dist/cli.js.map +1 -1
- package/dist/{gateway-5BG3YCZF.js → gateway-WU5G4ODO.js} +109 -49
- package/dist/gateway-WU5G4ODO.js.map +1 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.js +3 -2
- package/dist/resolve-H22TOVB5.js +7 -0
- package/dist/resolve-H22TOVB5.js.map +1 -0
- package/dist/store-COOLBAHB.js +9 -0
- package/dist/store-COOLBAHB.js.map +1 -0
- package/dist/{tui-L6RABP2J.js → tui-4BP3TI7J.js} +224 -30
- package/dist/tui-4BP3TI7J.js.map +1 -0
- package/docs/architecture/agent-loop.md +28 -9
- package/docs/architecture/overview.md +10 -10
- package/docs/architecture/tools.md +30 -3
- package/docs/getting-started.md +3 -3
- package/docs/index.md +1 -1
- package/docs/user-guide/cli.md +13 -5
- package/docs/user-guide/games.md +3 -3
- package/docs/user-guide/gateway.md +70 -16
- package/docs/user-guide/library.md +6 -3
- package/docs/user-guide/redot.md +2 -2
- package/docs/user-guide/tui.md +3 -2
- package/package.json +1 -1
- package/dist/chunk-QVJCIZIF.js.map +0 -1
- package/dist/gateway-5BG3YCZF.js.map +0 -1
- package/dist/tui-L6RABP2J.js.map +0 -1
- /package/dist/{chunk-JYURFAGB.js.map → chunk-QOTECFCN.js.map} +0 -0
- /package/dist/{chunk-SAEB3QL3.js.map → chunk-W6JXZBUE.js.map} +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/session/resolve.ts
|
|
2
|
+
import { readdir, stat } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var CANDIDATE_CAP = 5;
|
|
5
|
+
function is_enoent(error) {
|
|
6
|
+
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
7
|
+
}
|
|
8
|
+
async function list_session_files(dir) {
|
|
9
|
+
let names;
|
|
10
|
+
try {
|
|
11
|
+
names = await readdir(dir);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (is_enoent(error) === true) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
const entries = [];
|
|
19
|
+
for (const name of names) {
|
|
20
|
+
if (name.endsWith(".jsonl") === false) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const info = await stat(path.join(dir, name));
|
|
25
|
+
if (info.isFile() === false) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
entries.push({ name, id: name.slice(0, -".jsonl".length), mtime_ms: info.mtimeMs });
|
|
29
|
+
} catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return entries.sort((a, b) => b.mtime_ms - a.mtime_ms);
|
|
34
|
+
}
|
|
35
|
+
function candidate_ids(entries) {
|
|
36
|
+
if (entries.length === 0) {
|
|
37
|
+
return "(none)";
|
|
38
|
+
}
|
|
39
|
+
return entries.slice(0, CANDIDATE_CAP).map((entry) => entry.id).join(", ");
|
|
40
|
+
}
|
|
41
|
+
function missing_error(dir, value, entries) {
|
|
42
|
+
return new Error(`session not found: "${value}" in ${dir} (candidates: ${candidate_ids(entries)})`);
|
|
43
|
+
}
|
|
44
|
+
function ambiguous_error(dir, value, matches) {
|
|
45
|
+
const sorted = [...matches].sort((a, b) => b.mtime_ms - a.mtime_ms);
|
|
46
|
+
return new Error(`ambiguous session prefix: "${value}" in ${dir} matches: ${candidate_ids(sorted)}`);
|
|
47
|
+
}
|
|
48
|
+
async function resolve_session_path(dir, value) {
|
|
49
|
+
const entries = await list_session_files(dir);
|
|
50
|
+
if (value === "latest") {
|
|
51
|
+
const newest = entries[0];
|
|
52
|
+
if (newest === void 0) {
|
|
53
|
+
throw missing_error(dir, value, entries);
|
|
54
|
+
}
|
|
55
|
+
return path.join(dir, newest.name);
|
|
56
|
+
}
|
|
57
|
+
const exact_name = `${value}.jsonl`;
|
|
58
|
+
const exact = entries.find((entry) => entry.name === exact_name);
|
|
59
|
+
if (exact !== void 0) {
|
|
60
|
+
return path.join(dir, exact.name);
|
|
61
|
+
}
|
|
62
|
+
const prefix_matches = entries.filter((entry) => entry.id.startsWith(value));
|
|
63
|
+
if (prefix_matches.length === 1) {
|
|
64
|
+
return path.join(dir, prefix_matches[0].name);
|
|
65
|
+
}
|
|
66
|
+
if (prefix_matches.length > 1) {
|
|
67
|
+
throw ambiguous_error(dir, value, prefix_matches);
|
|
68
|
+
}
|
|
69
|
+
throw missing_error(dir, value, entries);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
resolve_session_path
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=chunk-PL6MKRKE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session/resolve.ts"],"sourcesContent":["/**\n * Resolve a `--resume` value to an existing session transcript path.\n * Pure filesystem lookup: latest by mtime, exact id, or unique prefix.\n */\nimport { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface SessionFileInfo {\n readonly name: string;\n readonly id: string;\n readonly mtime_ms: number;\n}\n\nconst CANDIDATE_CAP = 5;\n\nfunction is_enoent(error: unknown): boolean {\n return typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === \"ENOENT\";\n}\n\nasync function list_session_files(dir: string): Promise<SessionFileInfo[]> {\n let names: string[];\n try {\n names = await readdir(dir);\n } catch (error) {\n if (is_enoent(error) === true) {\n return [];\n }\n throw error;\n }\n const entries: SessionFileInfo[] = [];\n for (const name of names) {\n if (name.endsWith(\".jsonl\") === false) {\n continue;\n }\n try {\n const info = await stat(path.join(dir, name));\n if (info.isFile() === false) {\n continue;\n }\n entries.push({ name, id: name.slice(0, -\".jsonl\".length), mtime_ms: info.mtimeMs });\n } catch {\n continue;\n }\n }\n return entries.sort((a, b) => b.mtime_ms - a.mtime_ms);\n}\n\nfunction candidate_ids(entries: readonly SessionFileInfo[]): string {\n if (entries.length === 0) {\n return \"(none)\";\n }\n return entries\n .slice(0, CANDIDATE_CAP)\n .map((entry) => entry.id)\n .join(\", \");\n}\n\nfunction missing_error(dir: string, value: string, entries: readonly SessionFileInfo[]): Error {\n return new Error(`session not found: \"${value}\" in ${dir} (candidates: ${candidate_ids(entries)})`);\n}\n\nfunction ambiguous_error(dir: string, value: string, matches: readonly SessionFileInfo[]): Error {\n const sorted = [...matches].sort((a, b) => b.mtime_ms - a.mtime_ms);\n return new Error(`ambiguous session prefix: \"${value}\" in ${dir} matches: ${candidate_ids(sorted)}`);\n}\n\n/** Map `(dir, value)` to an absolute transcript path, or throw with candidates. */\nexport async function resolve_session_path(dir: string, value: string): Promise<string> {\n const entries = await list_session_files(dir);\n if (value === \"latest\") {\n const newest = entries[0];\n if (newest === undefined) {\n throw missing_error(dir, value, entries);\n }\n return path.join(dir, newest.name);\n }\n const exact_name = `${value}.jsonl`;\n const exact = entries.find((entry) => entry.name === exact_name);\n if (exact !== undefined) {\n return path.join(dir, exact.name);\n }\n const prefix_matches = entries.filter((entry) => entry.id.startsWith(value));\n if (prefix_matches.length === 1) {\n return path.join(dir, prefix_matches[0]!.name);\n }\n if (prefix_matches.length > 1) {\n throw ambiguous_error(dir, value, prefix_matches);\n }\n throw missing_error(dir, value, entries);\n}\n"],"mappings":";AAIA,SAAS,SAAS,YAAY;AAC9B,OAAO,UAAU;AAQjB,IAAM,gBAAgB;AAEtB,SAAS,UAAU,OAAyB;AAC1C,SAAO,OAAO,UAAU,YAAY,UAAU,QAAS,MAA6B,SAAS;AAC/F;AAEA,eAAe,mBAAmB,KAAyC;AACzE,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,QAAQ,GAAG;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,UAAU,KAAK,MAAM,MAAM;AAC7B,aAAO,CAAC;AAAA,IACV;AACA,UAAM;AAAA,EACR;AACA,QAAM,UAA6B,CAAC;AACpC,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAQ,MAAM,OAAO;AACrC;AAAA,IACF;AACA,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,KAAK,KAAK,KAAK,IAAI,CAAC;AAC5C,UAAI,KAAK,OAAO,MAAM,OAAO;AAC3B;AAAA,MACF;AACA,cAAQ,KAAK,EAAE,MAAM,IAAI,KAAK,MAAM,GAAG,CAAC,SAAS,MAAM,GAAG,UAAU,KAAK,QAAQ,CAAC;AAAA,IACpF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AACvD;AAEA,SAAS,cAAc,SAA6C;AAClE,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AACA,SAAO,QACJ,MAAM,GAAG,aAAa,EACtB,IAAI,CAAC,UAAU,MAAM,EAAE,EACvB,KAAK,IAAI;AACd;AAEA,SAAS,cAAc,KAAa,OAAe,SAA4C;AAC7F,SAAO,IAAI,MAAM,uBAAuB,KAAK,QAAQ,GAAG,iBAAiB,cAAc,OAAO,CAAC,GAAG;AACpG;AAEA,SAAS,gBAAgB,KAAa,OAAe,SAA4C;AAC/F,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAClE,SAAO,IAAI,MAAM,8BAA8B,KAAK,QAAQ,GAAG,aAAa,cAAc,MAAM,CAAC,EAAE;AACrG;AAGA,eAAsB,qBAAqB,KAAa,OAAgC;AACtF,QAAM,UAAU,MAAM,mBAAmB,GAAG;AAC5C,MAAI,UAAU,UAAU;AACtB,UAAM,SAAS,QAAQ,CAAC;AACxB,QAAI,WAAW,QAAW;AACxB,YAAM,cAAc,KAAK,OAAO,OAAO;AAAA,IACzC;AACA,WAAO,KAAK,KAAK,KAAK,OAAO,IAAI;AAAA,EACnC;AACA,QAAM,aAAa,GAAG,KAAK;AAC3B,QAAM,QAAQ,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,UAAU;AAC/D,MAAI,UAAU,QAAW;AACvB,WAAO,KAAK,KAAK,KAAK,MAAM,IAAI;AAAA,EAClC;AACA,QAAM,iBAAiB,QAAQ,OAAO,CAAC,UAAU,MAAM,GAAG,WAAW,KAAK,CAAC;AAC3E,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO,KAAK,KAAK,KAAK,eAAe,CAAC,EAAG,IAAI;AAAA,EAC/C;AACA,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM,gBAAgB,KAAK,OAAO,cAAc;AAAA,EAClD;AACA,QAAM,cAAc,KAAK,OAAO,OAAO;AACzC;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
logger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EDRUZF22.js";
|
|
4
4
|
|
|
5
5
|
// src/util/theme.ts
|
|
6
6
|
import { readFileSync } from "fs";
|
|
@@ -89,4 +89,4 @@ export {
|
|
|
89
89
|
notice_flavor,
|
|
90
90
|
load_theme
|
|
91
91
|
};
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-QOTECFCN.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/session/store.ts
|
|
2
|
+
import { appendFile, mkdir, readFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
// src/util/json.ts
|
|
6
|
+
function safe_json_parse(raw) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(raw);
|
|
9
|
+
} catch {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function safe_stringify(value, space) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.stringify(value, null, space) ?? String(value);
|
|
16
|
+
} catch {
|
|
17
|
+
return String(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function truncate_text(text, max_chars) {
|
|
21
|
+
if (text.length <= max_chars) {
|
|
22
|
+
return text;
|
|
23
|
+
}
|
|
24
|
+
const omitted = text.length - max_chars;
|
|
25
|
+
return `${text.slice(0, max_chars)}
|
|
26
|
+
[... truncated, ${omitted} chars omitted ...]`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/session/store.ts
|
|
30
|
+
var counter_state = { value: 0 };
|
|
31
|
+
var MESSAGE_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
|
|
32
|
+
function slugify_label(label) {
|
|
33
|
+
const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
34
|
+
return slug.length > 0 ? `-${slug}` : "";
|
|
35
|
+
}
|
|
36
|
+
async function open_session(dir, label) {
|
|
37
|
+
await mkdir(dir, { recursive: true });
|
|
38
|
+
counter_state.value += 1;
|
|
39
|
+
const label_part = label === void 0 ? "" : slugify_label(label);
|
|
40
|
+
const id = `${Date.now().toString(36)}-${counter_state.value}${label_part}`;
|
|
41
|
+
const file_path = path.join(dir, `${id}.jsonl`);
|
|
42
|
+
return {
|
|
43
|
+
id,
|
|
44
|
+
path: file_path,
|
|
45
|
+
append: async (record) => {
|
|
46
|
+
await appendFile(file_path, `${safe_stringify(record)}
|
|
47
|
+
`, "utf8");
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function is_message(value) {
|
|
52
|
+
if (typeof value !== "object" || value === null) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const role = value.role;
|
|
56
|
+
return typeof role === "string" && MESSAGE_ROLES.has(role);
|
|
57
|
+
}
|
|
58
|
+
async function read_session_messages(file_path) {
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
raw = await readFile(file_path, "utf8");
|
|
62
|
+
} catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const messages = [];
|
|
66
|
+
for (const line of raw.split("\n")) {
|
|
67
|
+
const record = safe_json_parse(line);
|
|
68
|
+
if (record?.message !== void 0 && is_message(record.message)) {
|
|
69
|
+
messages.push(record.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const last = messages.at(-1);
|
|
73
|
+
if (last?.role === "user") {
|
|
74
|
+
messages.pop();
|
|
75
|
+
}
|
|
76
|
+
return messages;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
safe_json_parse,
|
|
81
|
+
safe_stringify,
|
|
82
|
+
truncate_text,
|
|
83
|
+
open_session,
|
|
84
|
+
read_session_messages
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=chunk-VKEOHUCB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session/store.ts","../src/util/json.ts"],"sourcesContent":["/**\n * JSONL transcript persistence for agent sessions. Each session is one\n * append-only .jsonl file; records carry either a message or arbitrary meta.\n */\nimport { appendFile, mkdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Message } from \"../providers/types.js\";\nimport { safe_json_parse, safe_stringify } from \"../util/json.js\";\n\nexport interface SessionRecord {\n ts: string;\n kind: \"message\" | \"meta\";\n message?: Message;\n meta?: Record<string, unknown>;\n}\n\nexport interface SessionHandle {\n id: string;\n path: string;\n append(record: SessionRecord): Promise<void>;\n}\n\n/** Mutable via holder object: conventions require const bindings. */\nconst counter_state = { value: 0 };\n\nconst MESSAGE_ROLES: ReadonlySet<string> = new Set([\"system\", \"user\", \"assistant\", \"tool\"]);\n\nfunction slugify_label(label: string): string {\n const slug = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40);\n return slug.length > 0 ? `-${slug}` : \"\";\n}\n\nexport async function open_session(dir: string, label?: string): Promise<SessionHandle> {\n await mkdir(dir, { recursive: true });\n counter_state.value += 1;\n const label_part = label === undefined ? \"\" : slugify_label(label);\n const id = `${Date.now().toString(36)}-${counter_state.value}${label_part}`;\n const file_path = path.join(dir, `${id}.jsonl`);\n return {\n id,\n path: file_path,\n append: async (record: SessionRecord): Promise<void> => {\n await appendFile(file_path, `${safe_stringify(record)}\\n`, \"utf8\");\n },\n };\n}\n\nfunction is_message(value: unknown): value is Message {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const role = (value as { role?: unknown }).role;\n return typeof role === \"string\" && MESSAGE_ROLES.has(role);\n}\n\nexport async function read_session_messages(file_path: string): Promise<Message[]> {\n let raw: string;\n try {\n raw = await readFile(file_path, \"utf8\");\n } catch {\n return [];\n }\n const messages: Message[] = [];\n for (const line of raw.split(\"\\n\")) {\n const record = safe_json_parse<SessionRecord>(line);\n if (record?.message !== undefined && is_message(record.message)) {\n messages.push(record.message);\n }\n }\n // Provider throw / abort-before-turn can leave a dangling user seed with no\n // assistant reply. Drop it so resume does not start with two consecutive users.\n const last = messages.at(-1);\n if (last?.role === \"user\") {\n messages.pop();\n }\n return messages;\n}","export function safe_json_parse<T>(raw: string): T | undefined {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nexport function safe_stringify(value: unknown, space?: number): string {\n try {\n return JSON.stringify(value, null, space) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nexport function truncate_text(text: string, max_chars: number): string {\n if (text.length <= max_chars) {\n return text;\n }\n const omitted = text.length - max_chars;\n return `${text.slice(0, max_chars)}\\n[... truncated, ${omitted} chars omitted ...]`;\n}"],"mappings":";AAIA,SAAS,YAAY,OAAO,gBAAgB;AAC5C,OAAO,UAAU;;;ACLV,SAAS,gBAAmB,KAA4B;AAC7D,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,OAAgB,OAAwB;AACrE,MAAI;AACF,WAAO,KAAK,UAAU,OAAO,MAAM,KAAK,KAAK,OAAO,KAAK;AAAA,EAC3D,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAEO,SAAS,cAAc,MAAc,WAA2B;AACrE,MAAI,KAAK,UAAU,WAAW;AAC5B,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,SAAS;AAC9B,SAAO,GAAG,KAAK,MAAM,GAAG,SAAS,CAAC;AAAA,kBAAqB,OAAO;AAChE;;;ADCA,IAAM,gBAAgB,EAAE,OAAO,EAAE;AAEjC,IAAM,gBAAqC,oBAAI,IAAI,CAAC,UAAU,QAAQ,aAAa,MAAM,CAAC;AAE1F,SAAS,cAAc,OAAuB;AAC5C,QAAM,OAAO,MACV,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE;AACd,SAAO,KAAK,SAAS,IAAI,IAAI,IAAI,KAAK;AACxC;AAEA,eAAsB,aAAa,KAAa,OAAwC;AACtF,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,gBAAc,SAAS;AACvB,QAAM,aAAa,UAAU,SAAY,KAAK,cAAc,KAAK;AACjE,QAAM,KAAK,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,cAAc,KAAK,GAAG,UAAU;AACzE,QAAM,YAAY,KAAK,KAAK,KAAK,GAAG,EAAE,QAAQ;AAC9C,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,QAAQ,OAAO,WAAyC;AACtD,YAAM,WAAW,WAAW,GAAG,eAAe,MAAM,CAAC;AAAA,GAAM,MAAM;AAAA,IACnE;AAAA,EACF;AACF;AAEA,SAAS,WAAW,OAAkC;AACpD,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,OAAQ,MAA6B;AAC3C,SAAO,OAAO,SAAS,YAAY,cAAc,IAAI,IAAI;AAC3D;AAEA,eAAsB,sBAAsB,WAAuC;AACjF,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,SAAS,WAAW,MAAM;AAAA,EACxC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,WAAsB,CAAC;AAC7B,aAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,UAAM,SAAS,gBAA+B,IAAI;AAClD,QAAI,QAAQ,YAAY,UAAa,WAAW,OAAO,OAAO,GAAG;AAC/D,eAAS,KAAK,OAAO,OAAO;AAAA,IAC9B;AAAA,EACF;AAGA,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,QAAQ;AACzB,aAAS,IAAI;AAAA,EACf;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
catalog_by_name
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EDRUZF22.js";
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
import { readFileSync } from "fs";
|
|
@@ -55,4 +55,4 @@ export {
|
|
|
55
55
|
catalog_client_entry,
|
|
56
56
|
LICH_VERSION
|
|
57
57
|
};
|
|
58
|
-
//# sourceMappingURL=chunk-
|
|
58
|
+
//# sourceMappingURL=chunk-W6JXZBUE.js.map
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/** MCP-only flags. Values are not logged. */
|
|
3
|
+
interface McpCliFlags {
|
|
4
|
+
command?: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
url?: string;
|
|
7
|
+
project_path?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface CliOptions {
|
|
11
|
+
config_path?: string;
|
|
12
|
+
resume?: string;
|
|
13
|
+
help?: boolean;
|
|
14
|
+
version?: boolean;
|
|
15
|
+
overrides: Record<string, string>;
|
|
16
|
+
positionals: string[];
|
|
17
|
+
mcp_flags: McpCliFlags;
|
|
18
|
+
}
|
|
19
|
+
declare function parse_args(argv: string[]): CliOptions;
|
|
2
20
|
declare function run_one_shot(config: unknown, input: string): Promise<number>;
|
|
3
21
|
declare function run_chat(config: unknown): Promise<number>;
|
|
4
22
|
interface CliEntryInput {
|
|
@@ -11,4 +29,4 @@ interface CliEntryInput {
|
|
|
11
29
|
declare function is_cli_entry(input: CliEntryInput): boolean;
|
|
12
30
|
declare function run_cli(argv: string[]): Promise<number>;
|
|
13
31
|
|
|
14
|
-
export { type CliEntryInput, is_cli_entry, run_chat, run_cli, run_one_shot };
|
|
32
|
+
export { type CliEntryInput, is_cli_entry, parse_args, run_chat, run_cli, run_one_shot };
|
package/dist/cli.js
CHANGED
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
import {
|
|
3
3
|
load_theme,
|
|
4
4
|
notice_flavor
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QOTECFCN.js";
|
|
6
6
|
import {
|
|
7
7
|
LICH_VERSION,
|
|
8
8
|
catalog_client_entry
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-W6JXZBUE.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_GATEWAY_TOKEN_ENVS,
|
|
12
12
|
create_agent_with_plugins,
|
|
13
13
|
is_env_var_name,
|
|
14
14
|
parse_agent_config,
|
|
15
|
-
refuse_mcp_entry
|
|
15
|
+
refuse_mcp_entry
|
|
16
|
+
} from "./chunk-EDRUZF22.js";
|
|
17
|
+
import {
|
|
16
18
|
safe_json_parse
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VKEOHUCB.js";
|
|
18
20
|
|
|
19
21
|
// src/cli.ts
|
|
20
22
|
import { existsSync as existsSync4, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
@@ -831,6 +833,7 @@ function usage_text() {
|
|
|
831
833
|
" --api-key-env <NAME> env var holding the api key (default LICH_API_KEY_ENV; unused by ollama)",
|
|
832
834
|
" --system-prompt <s> system prompt override",
|
|
833
835
|
" --session-dir <path> session transcript directory",
|
|
836
|
+
" --resume <id|latest> TUI only: load an existing session transcript",
|
|
834
837
|
" --log-level <level> debug | info | warn | error",
|
|
835
838
|
" --theme <name> display theme (default lich; files in ~/.lich/themes)",
|
|
836
839
|
" --command <bin> mcp add: local stdio binary",
|
|
@@ -842,6 +845,21 @@ function usage_text() {
|
|
|
842
845
|
function error_message(error) {
|
|
843
846
|
return error instanceof Error ? error.message : String(error);
|
|
844
847
|
}
|
|
848
|
+
function reject_resume_outside_tui(resume, mode) {
|
|
849
|
+
if (resume === void 0) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
throw new Error(`--resume is only supported in TUI mode (not ${mode})`);
|
|
853
|
+
}
|
|
854
|
+
function non_tui_resume_mode(first) {
|
|
855
|
+
if (first === void 0 || first === "tui") {
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
if (first === "init" || first === "config" || first === "mcp" || first === "update" || first === "chat" || first === "gateway") {
|
|
859
|
+
return first;
|
|
860
|
+
}
|
|
861
|
+
return "one-shot";
|
|
862
|
+
}
|
|
845
863
|
function error_for_mode(mode, base_message) {
|
|
846
864
|
if (mode === "tui") {
|
|
847
865
|
return "lich tui: no model configured \u2014 set LICH_MODEL (e.g. glm-5.3-flash:cloud), pass --model, or create .lich/config.json (`lich config` prints a template)";
|
|
@@ -879,6 +897,15 @@ function parse_args(argv) {
|
|
|
879
897
|
index += 1;
|
|
880
898
|
continue;
|
|
881
899
|
}
|
|
900
|
+
if (arg === "--resume") {
|
|
901
|
+
const value2 = argv[index + 1];
|
|
902
|
+
if (value2 === void 0) {
|
|
903
|
+
throw new Error("--resume requires a value");
|
|
904
|
+
}
|
|
905
|
+
options.resume = value2;
|
|
906
|
+
index += 1;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
882
909
|
if (mcp === true) {
|
|
883
910
|
const consumed = take_mcp_flag(argv, index, options.mcp_flags);
|
|
884
911
|
if (consumed !== void 0) {
|
|
@@ -983,7 +1010,7 @@ function build_config(options) {
|
|
|
983
1010
|
apply_overrides(base, options.overrides);
|
|
984
1011
|
const providers = base["providers"];
|
|
985
1012
|
if (Array.isArray(providers) === false || providers.length === 0) {
|
|
986
|
-
|
|
1013
|
+
base["providers"] = [env_provider(options.overrides)];
|
|
987
1014
|
}
|
|
988
1015
|
return parse_agent_config(base);
|
|
989
1016
|
}
|
|
@@ -1021,6 +1048,7 @@ async function run_one_shot(config, input) {
|
|
|
1021
1048
|
result = await agent.run({ input });
|
|
1022
1049
|
} finally {
|
|
1023
1050
|
stop_progress();
|
|
1051
|
+
agent.close();
|
|
1024
1052
|
}
|
|
1025
1053
|
const final = result.outcome.final;
|
|
1026
1054
|
if (final !== void 0 && final.content.length > 0) {
|
|
@@ -1091,6 +1119,7 @@ async function run_chat(config) {
|
|
|
1091
1119
|
}
|
|
1092
1120
|
} finally {
|
|
1093
1121
|
rl.close();
|
|
1122
|
+
agent.close();
|
|
1094
1123
|
}
|
|
1095
1124
|
}
|
|
1096
1125
|
function model_hint(options) {
|
|
@@ -1153,7 +1182,7 @@ async function run_bare(options) {
|
|
|
1153
1182
|
if (stopped !== void 0) {
|
|
1154
1183
|
return stopped;
|
|
1155
1184
|
}
|
|
1156
|
-
return run_tui_entry(build_config_for(options, "tui"));
|
|
1185
|
+
return run_tui_entry(build_config_for(options, "tui"), options.resume);
|
|
1157
1186
|
}
|
|
1158
1187
|
function model_still_placeholder(config) {
|
|
1159
1188
|
const providers = config["providers"];
|
|
@@ -1177,9 +1206,17 @@ function run_init(options) {
|
|
|
1177
1206
|
}
|
|
1178
1207
|
return 0;
|
|
1179
1208
|
}
|
|
1180
|
-
async function run_tui_entry(config) {
|
|
1181
|
-
const { run_tui } = await import("./tui-
|
|
1182
|
-
|
|
1209
|
+
async function run_tui_entry(config, resume) {
|
|
1210
|
+
const { run_tui } = await import("./tui-4BP3TI7J.js");
|
|
1211
|
+
if (resume === void 0) {
|
|
1212
|
+
return run_tui(config);
|
|
1213
|
+
}
|
|
1214
|
+
const { resolve_session_path } = await import("./resolve-H22TOVB5.js");
|
|
1215
|
+
const { read_session_messages } = await import("./store-COOLBAHB.js");
|
|
1216
|
+
const transcript = await resolve_session_path(config.session_dir, resume);
|
|
1217
|
+
const initial_history = await read_session_messages(transcript);
|
|
1218
|
+
const resumed_id = path4.basename(transcript, ".jsonl");
|
|
1219
|
+
return run_tui(config, { initial_history, resumed_id });
|
|
1183
1220
|
}
|
|
1184
1221
|
function gateway_platforms(config, cli_platforms) {
|
|
1185
1222
|
if (cli_platforms.length > 0) {
|
|
@@ -1189,7 +1226,7 @@ function gateway_platforms(config, cli_platforms) {
|
|
|
1189
1226
|
return configured.length === 0 ? ["webhook"] : configured;
|
|
1190
1227
|
}
|
|
1191
1228
|
async function run_gateway_entry(config, platforms) {
|
|
1192
|
-
const { run_gateway } = await import("./gateway-
|
|
1229
|
+
const { run_gateway } = await import("./gateway-WU5G4ODO.js");
|
|
1193
1230
|
return run_gateway(config, gateway_platforms(config, platforms));
|
|
1194
1231
|
}
|
|
1195
1232
|
function is_cli_entry(input) {
|
|
@@ -1235,6 +1272,10 @@ async function run_cli(argv) {
|
|
|
1235
1272
|
return 0;
|
|
1236
1273
|
}
|
|
1237
1274
|
const [first] = options.positionals;
|
|
1275
|
+
const blocked_resume_mode = non_tui_resume_mode(first);
|
|
1276
|
+
if (blocked_resume_mode !== void 0) {
|
|
1277
|
+
reject_resume_outside_tui(options.resume, blocked_resume_mode);
|
|
1278
|
+
}
|
|
1238
1279
|
if (first === void 0) {
|
|
1239
1280
|
return run_bare(options);
|
|
1240
1281
|
}
|
|
@@ -1262,7 +1303,7 @@ async function run_cli(argv) {
|
|
|
1262
1303
|
return run_chat(build_config_for(options, first));
|
|
1263
1304
|
}
|
|
1264
1305
|
if (first === "tui") {
|
|
1265
|
-
return run_tui_entry(build_config_for(options, first));
|
|
1306
|
+
return run_tui_entry(build_config_for(options, first), options.resume);
|
|
1266
1307
|
}
|
|
1267
1308
|
if (first === "gateway") {
|
|
1268
1309
|
return run_gateway_entry(build_config_for(options, first), options.positionals.slice(1));
|
|
@@ -1281,6 +1322,7 @@ if (is_main_module() === true) {
|
|
|
1281
1322
|
}
|
|
1282
1323
|
export {
|
|
1283
1324
|
is_cli_entry,
|
|
1325
|
+
parse_args,
|
|
1284
1326
|
run_chat,
|
|
1285
1327
|
run_cli,
|
|
1286
1328
|
run_one_shot
|