@moikapy/lich 0.7.1 → 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 +15 -0
- package/dist/{chunk-WNFBIX4E.js → chunk-EDRUZF22.js} +147 -130
- 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-CVX7LZWC.js → chunk-QOTECFCN.js} +2 -2
- package/dist/chunk-VKEOHUCB.js +86 -0
- package/dist/chunk-VKEOHUCB.js.map +1 -0
- package/dist/{chunk-PZNYVGD4.js → chunk-W6JXZBUE.js} +2 -2
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +50 -10
- package/dist/cli.js.map +1 -1
- package/dist/{gateway-44QTIJTJ.js → gateway-WU5G4ODO.js} +3 -2
- package/dist/{gateway-44QTIJTJ.js.map → gateway-WU5G4ODO.js.map} +1 -1
- package/dist/index.d.ts +17 -2
- 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-MEJGEILU.js → tui-4BP3TI7J.js} +204 -25
- package/dist/tui-4BP3TI7J.js.map +1 -0
- package/docs/architecture/agent-loop.md +28 -9
- package/docs/architecture/overview.md +8 -7
- package/docs/user-guide/cli.md +8 -1
- package/docs/user-guide/games.md +3 -3
- package/docs/user-guide/library.md +5 -2
- package/docs/user-guide/tui.md +1 -0
- package/package.json +1 -1
- package/dist/chunk-WNFBIX4E.js.map +0 -1
- package/dist/tui-MEJGEILU.js.map +0 -1
- /package/dist/{chunk-CVX7LZWC.js.map → chunk-QOTECFCN.js.map} +0 -0
- /package/dist/{chunk-PZNYVGD4.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) {
|
|
@@ -1155,7 +1182,7 @@ async function run_bare(options) {
|
|
|
1155
1182
|
if (stopped !== void 0) {
|
|
1156
1183
|
return stopped;
|
|
1157
1184
|
}
|
|
1158
|
-
return run_tui_entry(build_config_for(options, "tui"));
|
|
1185
|
+
return run_tui_entry(build_config_for(options, "tui"), options.resume);
|
|
1159
1186
|
}
|
|
1160
1187
|
function model_still_placeholder(config) {
|
|
1161
1188
|
const providers = config["providers"];
|
|
@@ -1179,9 +1206,17 @@ function run_init(options) {
|
|
|
1179
1206
|
}
|
|
1180
1207
|
return 0;
|
|
1181
1208
|
}
|
|
1182
|
-
async function run_tui_entry(config) {
|
|
1183
|
-
const { run_tui } = await import("./tui-
|
|
1184
|
-
|
|
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 });
|
|
1185
1220
|
}
|
|
1186
1221
|
function gateway_platforms(config, cli_platforms) {
|
|
1187
1222
|
if (cli_platforms.length > 0) {
|
|
@@ -1191,7 +1226,7 @@ function gateway_platforms(config, cli_platforms) {
|
|
|
1191
1226
|
return configured.length === 0 ? ["webhook"] : configured;
|
|
1192
1227
|
}
|
|
1193
1228
|
async function run_gateway_entry(config, platforms) {
|
|
1194
|
-
const { run_gateway } = await import("./gateway-
|
|
1229
|
+
const { run_gateway } = await import("./gateway-WU5G4ODO.js");
|
|
1195
1230
|
return run_gateway(config, gateway_platforms(config, platforms));
|
|
1196
1231
|
}
|
|
1197
1232
|
function is_cli_entry(input) {
|
|
@@ -1237,6 +1272,10 @@ async function run_cli(argv) {
|
|
|
1237
1272
|
return 0;
|
|
1238
1273
|
}
|
|
1239
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
|
+
}
|
|
1240
1279
|
if (first === void 0) {
|
|
1241
1280
|
return run_bare(options);
|
|
1242
1281
|
}
|
|
@@ -1264,7 +1303,7 @@ async function run_cli(argv) {
|
|
|
1264
1303
|
return run_chat(build_config_for(options, first));
|
|
1265
1304
|
}
|
|
1266
1305
|
if (first === "tui") {
|
|
1267
|
-
return run_tui_entry(build_config_for(options, first));
|
|
1306
|
+
return run_tui_entry(build_config_for(options, first), options.resume);
|
|
1268
1307
|
}
|
|
1269
1308
|
if (first === "gateway") {
|
|
1270
1309
|
return run_gateway_entry(build_config_for(options, first), options.positionals.slice(1));
|
|
@@ -1283,6 +1322,7 @@ if (is_main_module() === true) {
|
|
|
1283
1322
|
}
|
|
1284
1323
|
export {
|
|
1285
1324
|
is_cli_entry,
|
|
1325
|
+
parse_args,
|
|
1286
1326
|
run_chat,
|
|
1287
1327
|
run_cli,
|
|
1288
1328
|
run_one_shot
|