@xopcai/xopc 0.0.27 → 0.0.28
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/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/adapters/onboard-cli.d.ts +7 -0
- package/dist/extensions/weixin/src/adapters/onboard-cli.js +61 -0
- package/dist/extensions/weixin/src/adapters/onboard-cli.js.map +1 -0
- package/dist/extensions/weixin/src/cli/qr-login.d.ts +5 -0
- package/dist/extensions/weixin/src/cli/qr-login.js +1 -1
- package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
- package/dist/extensions/weixin/src/index.js +1 -1
- package/dist/extensions/weixin/src/plugin.d.ts +1 -0
- package/dist/extensions/weixin/src/plugin.js +2 -0
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/gateway/static/root/assets/{agents-w8_jzuiX.js → agents-DplaQYS2.js} +2 -2
- package/dist/gateway/static/root/assets/{agents-w8_jzuiX.js.map → agents-DplaQYS2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js → apps-page-Co95hLOJ.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js.map → apps-page-Co95hLOJ.js.map} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-DUKRPC7C.js → channels-settings-CkfSST0k.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-DUKRPC7C.js.map → channels-settings-CkfSST0k.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-S18t1yG-.js → cron-page-D9q6KqL8.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-S18t1yG-.js.map → cron-page-D9q6KqL8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js → cron-utils-BmzF4m1y.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js.map → cron-utils-BmzF4m1y.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js → dist-Dn-ufXyc.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js.map → dist-Dn-ufXyc.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js → extension-debug-page-BZ8xQ74_.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js.map → extension-debug-page-BZ8xQ74_.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js → extension-page-BlNgKxwW.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js.map → extension-page-BlNgKxwW.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js → extension-settings-page-CWTdW_oY.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js.map → extension-settings-page-CWTdW_oY.js.map} +1 -1
- package/dist/gateway/static/root/assets/{index-PfkB8N37.js → index-lV8FGWlt.js} +4 -4
- package/dist/gateway/static/root/assets/{index-PfkB8N37.js.map → index-lV8FGWlt.js.map} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DG31RpvG.js +2 -0
- package/dist/gateway/static/root/assets/logs-page-DG31RpvG.js.map +1 -0
- package/dist/gateway/static/root/assets/sessions-page-CdmjxDEM.js +2 -0
- package/dist/gateway/static/root/assets/{sessions-page-2uOYwEwd.js.map → sessions-page-CdmjxDEM.js.map} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-fQWswCuq.js → settings-page-DU2XLf5s.js} +2 -2
- package/dist/gateway/static/root/assets/{settings-page-fQWswCuq.js.map → settings-page-DU2XLf5s.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js → skills-page-lb7vYtlP.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js.map → skills-page-lb7vYtlP.js.map} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/manager.js +2 -2
- package/dist/src/channels/weixin/index.js +1 -1
- package/dist/src/cli/agent-chat-log-level-preset.d.ts +7 -0
- package/dist/src/cli/agent-chat-log-level-preset.js +22 -0
- package/dist/src/cli/agent-chat-log-level-preset.js.map +1 -0
- package/dist/src/cli/commands/agent/interactive.js +4 -2
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent/stream-renderer.d.ts +14 -0
- package/dist/src/cli/commands/agent/stream-renderer.js +99 -0
- package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -0
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +77 -93
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/tui.d.ts +1 -0
- package/dist/src/cli/commands/tui.js +40 -0
- package/dist/src/cli/commands/tui.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +3 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.d.ts +6 -0
- package/dist/src/config/schema.js +6 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/auth.d.ts +17 -3
- package/dist/src/gateway/auth.js +35 -16
- package/dist/src/gateway/auth.js.map +1 -1
- package/dist/src/gateway/hono/app.js +30 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/auth.js +4 -3
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/middleware/scopes.d.ts +15 -0
- package/dist/src/gateway/hono/middleware/scopes.js +41 -0
- package/dist/src/gateway/hono/middleware/scopes.js.map +1 -0
- package/dist/src/gateway/security/audit.d.ts +18 -0
- package/dist/src/gateway/security/audit.js +68 -0
- package/dist/src/gateway/security/audit.js.map +1 -0
- package/dist/src/gateway/security/csp.d.ts +19 -0
- package/dist/src/gateway/security/csp.js +52 -0
- package/dist/src/gateway/security/csp.js.map +1 -0
- package/dist/src/gateway/security/dangerous-tools.d.ts +20 -0
- package/dist/src/gateway/security/dangerous-tools.js +46 -0
- package/dist/src/gateway/security/dangerous-tools.js.map +1 -0
- package/dist/src/gateway/security/flood-guard.d.ts +28 -0
- package/dist/src/gateway/security/flood-guard.js +42 -0
- package/dist/src/gateway/security/flood-guard.js.map +1 -0
- package/dist/src/gateway/security/index.d.ts +9 -0
- package/dist/src/gateway/security/index.js +10 -0
- package/dist/src/gateway/security/known-weak-secrets.d.ts +10 -0
- package/dist/src/gateway/security/known-weak-secrets.js +36 -0
- package/dist/src/gateway/security/known-weak-secrets.js.map +1 -0
- package/dist/src/gateway/security/operator-scopes.d.ts +37 -0
- package/dist/src/gateway/security/operator-scopes.js +137 -0
- package/dist/src/gateway/security/operator-scopes.js.map +1 -0
- package/dist/src/gateway/security/origin-check.d.ts +21 -0
- package/dist/src/gateway/security/origin-check.js +56 -0
- package/dist/src/gateway/security/origin-check.js.map +1 -0
- package/dist/src/gateway/security/preauth-connection-budget.d.ts +17 -0
- package/dist/src/gateway/security/preauth-connection-budget.js +49 -0
- package/dist/src/gateway/security/preauth-connection-budget.js.map +1 -0
- package/dist/src/gateway/security/secret-equal.d.ts +8 -0
- package/dist/src/gateway/security/secret-equal.js +30 -0
- package/dist/src/gateway/security/secret-equal.js.map +1 -0
- package/dist/src/gateway/service.d.ts +1 -1
- package/dist/src/gateway/service.js +11 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +42 -0
- package/dist/src/tui/backends/embedded-backend.js +160 -0
- package/dist/src/tui/backends/embedded-backend.js.map +1 -0
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +49 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +226 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -0
- package/dist/src/tui/components/assistant-message.d.ts +6 -0
- package/dist/src/tui/components/assistant-message.js +19 -0
- package/dist/src/tui/components/assistant-message.js.map +1 -0
- package/dist/src/tui/components/chat-log.d.ts +19 -0
- package/dist/src/tui/components/chat-log.js +99 -0
- package/dist/src/tui/components/chat-log.js.map +1 -0
- package/dist/src/tui/components/custom-editor.d.ts +13 -0
- package/dist/src/tui/components/custom-editor.js +44 -0
- package/dist/src/tui/components/custom-editor.js.map +1 -0
- package/dist/src/tui/components/tool-execution.d.ts +16 -0
- package/dist/src/tui/components/tool-execution.js +76 -0
- package/dist/src/tui/components/tool-execution.js.map +1 -0
- package/dist/src/tui/components/user-message.d.ts +6 -0
- package/dist/src/tui/components/user-message.js +22 -0
- package/dist/src/tui/components/user-message.js.map +1 -0
- package/dist/src/tui/sse-consumer.d.ts +15 -0
- package/dist/src/tui/sse-consumer.js +75 -0
- package/dist/src/tui/sse-consumer.js.map +1 -0
- package/dist/src/tui/stream-assembler.d.ts +22 -0
- package/dist/src/tui/stream-assembler.js +63 -0
- package/dist/src/tui/stream-assembler.js.map +1 -0
- package/dist/src/tui/theme.d.ts +71 -0
- package/dist/src/tui/theme.js +151 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/tui-backend.d.ts +84 -0
- package/dist/src/tui/tui-backend.js +1 -0
- package/dist/src/tui/tui-types.d.ts +85 -0
- package/dist/src/tui/tui-types.js +21 -0
- package/dist/src/tui/tui-types.js.map +1 -0
- package/dist/src/tui/tui.d.ts +3 -0
- package/dist/src/tui/tui.js +526 -0
- package/dist/src/tui/tui.js.map +1 -0
- package/package.json +9 -3
- package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js.map +0 -1
- package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js +0 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { markdownTheme, theme } from "../theme.js";
|
|
2
|
+
import { Container, Markdown, Spacer } from "@mariozechner/pi-tui";
|
|
3
|
+
//#region src/tui/components/user-message.ts
|
|
4
|
+
var UserMessageComponent = class extends Container {
|
|
5
|
+
body;
|
|
6
|
+
constructor(text) {
|
|
7
|
+
super();
|
|
8
|
+
this.body = new Markdown(text, 1, 0, markdownTheme, {
|
|
9
|
+
bgColor: (line) => theme.userBg(line),
|
|
10
|
+
color: (line) => theme.userText(line)
|
|
11
|
+
});
|
|
12
|
+
this.addChild(new Spacer(1));
|
|
13
|
+
this.addChild(this.body);
|
|
14
|
+
}
|
|
15
|
+
setText(text) {
|
|
16
|
+
this.body.setText(text);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { UserMessageComponent };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=user-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-message.js","names":[],"sources":["../../../../src/tui/components/user-message.ts"],"sourcesContent":["import { Container, Markdown, Spacer } from '@mariozechner/pi-tui';\n\nimport { markdownTheme, theme } from '../theme.js';\n\nexport class UserMessageComponent extends Container {\n private body: Markdown;\n\n constructor(text: string) {\n super();\n this.body = new Markdown(text, 1, 0, markdownTheme, {\n bgColor: (line) => theme.userBg(line),\n color: (line) => theme.userText(line),\n });\n this.addChild(new Spacer(1));\n this.addChild(this.body);\n }\n\n setText(text: string): void {\n this.body.setText(text);\n }\n}\n"],"mappings":";;;AAIA,IAAa,uBAAb,cAA0C,UAAU;CAClD;CAEA,YAAY,MAAc;AACxB,SAAO;AACP,OAAK,OAAO,IAAI,SAAS,MAAM,GAAG,GAAG,eAAe;GAClD,UAAU,SAAS,MAAM,OAAO,KAAK;GACrC,QAAQ,SAAS,MAAM,SAAS,KAAK;GACtC,CAAC;AACF,OAAK,SAAS,IAAI,OAAO,EAAE,CAAC;AAC5B,OAAK,SAAS,KAAK,KAAK;;CAG1B,QAAQ,MAAoB;AAC1B,OAAK,KAAK,QAAQ,KAAK"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ParsedSSEEvent } from './tui-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Consume an SSE stream from a `ReadableStream<Uint8Array>` (Node.js 22+ fetch body).
|
|
4
|
+
*
|
|
5
|
+
* Parses the standard SSE wire format:
|
|
6
|
+
* event: <name>\n
|
|
7
|
+
* data: <json>\n
|
|
8
|
+
* id: <id>\n
|
|
9
|
+
* \n
|
|
10
|
+
*
|
|
11
|
+
* Calls `onEvent` for each complete event block.
|
|
12
|
+
*/
|
|
13
|
+
export declare function consumeSSEStream(body: ReadableStream<Uint8Array>, onEvent: (event: ParsedSSEEvent) => void, signal?: AbortSignal): Promise<void>;
|
|
14
|
+
/** Parse SSE data field as JSON, returning null on failure. */
|
|
15
|
+
export declare function parseSSEData<T = unknown>(data: string): T | null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/tui/sse-consumer.ts
|
|
2
|
+
/**
|
|
3
|
+
* Consume an SSE stream from a `ReadableStream<Uint8Array>` (Node.js 22+ fetch body).
|
|
4
|
+
*
|
|
5
|
+
* Parses the standard SSE wire format:
|
|
6
|
+
* event: <name>\n
|
|
7
|
+
* data: <json>\n
|
|
8
|
+
* id: <id>\n
|
|
9
|
+
* \n
|
|
10
|
+
*
|
|
11
|
+
* Calls `onEvent` for each complete event block.
|
|
12
|
+
*/
|
|
13
|
+
async function consumeSSEStream(body, onEvent, signal) {
|
|
14
|
+
const reader = body.getReader();
|
|
15
|
+
const decoder = new TextDecoder();
|
|
16
|
+
let buffer = "";
|
|
17
|
+
let currentEvent = "";
|
|
18
|
+
let currentData = "";
|
|
19
|
+
let currentId = "";
|
|
20
|
+
try {
|
|
21
|
+
while (true) {
|
|
22
|
+
if (signal?.aborted) break;
|
|
23
|
+
const { done, value } = await reader.read();
|
|
24
|
+
if (done) break;
|
|
25
|
+
buffer += decoder.decode(value, { stream: true });
|
|
26
|
+
const lines = buffer.split("\n");
|
|
27
|
+
buffer = lines.pop() ?? "";
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
if (line === "") {
|
|
30
|
+
if (currentData) onEvent({
|
|
31
|
+
event: currentEvent || "message",
|
|
32
|
+
data: currentData,
|
|
33
|
+
id: currentId || void 0
|
|
34
|
+
});
|
|
35
|
+
currentEvent = "";
|
|
36
|
+
currentData = "";
|
|
37
|
+
currentId = "";
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (line.startsWith(":")) continue;
|
|
41
|
+
const colonIndex = line.indexOf(":");
|
|
42
|
+
if (colonIndex === -1) continue;
|
|
43
|
+
const field = line.slice(0, colonIndex);
|
|
44
|
+
const valueStart = line[colonIndex + 1] === " " ? colonIndex + 2 : colonIndex + 1;
|
|
45
|
+
const fieldValue = line.slice(valueStart);
|
|
46
|
+
switch (field) {
|
|
47
|
+
case "event":
|
|
48
|
+
currentEvent = fieldValue;
|
|
49
|
+
break;
|
|
50
|
+
case "data":
|
|
51
|
+
currentData = currentData ? `${currentData}\n${fieldValue}` : fieldValue;
|
|
52
|
+
break;
|
|
53
|
+
case "id":
|
|
54
|
+
currentId = fieldValue;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
buffer += decoder.decode();
|
|
61
|
+
reader.releaseLock();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Parse SSE data field as JSON, returning null on failure. */
|
|
65
|
+
function parseSSEData(data) {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(data);
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { consumeSSEStream, parseSSEData };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=sse-consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-consumer.js","names":[],"sources":["../../../src/tui/sse-consumer.ts"],"sourcesContent":["import type { ParsedSSEEvent } from './tui-types.js';\n\n/**\n * Consume an SSE stream from a `ReadableStream<Uint8Array>` (Node.js 22+ fetch body).\n *\n * Parses the standard SSE wire format:\n * event: <name>\\n\n * data: <json>\\n\n * id: <id>\\n\n * \\n\n *\n * Calls `onEvent` for each complete event block.\n */\nexport async function consumeSSEStream(\n body: ReadableStream<Uint8Array>,\n onEvent: (event: ParsedSSEEvent) => void,\n signal?: AbortSignal,\n): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let currentEvent = '';\n let currentData = '';\n let currentId = '';\n\n try {\n while (true) {\n if (signal?.aborted) break;\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last (possibly incomplete) line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line === '') {\n // Empty line = end of event block\n if (currentData) {\n onEvent({\n event: currentEvent || 'message',\n data: currentData,\n id: currentId || undefined,\n });\n }\n currentEvent = '';\n currentData = '';\n currentId = '';\n continue;\n }\n\n if (line.startsWith(':')) {\n // Comment line, skip\n continue;\n }\n\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) continue;\n\n const field = line.slice(0, colonIndex);\n // SSE spec: if there's a space after the colon, skip it\n const valueStart = line[colonIndex + 1] === ' ' ? colonIndex + 2 : colonIndex + 1;\n const fieldValue = line.slice(valueStart);\n\n switch (field) {\n case 'event':\n currentEvent = fieldValue;\n break;\n case 'data':\n currentData = currentData ? `${currentData}\\n${fieldValue}` : fieldValue;\n break;\n case 'id':\n currentId = fieldValue;\n break;\n }\n }\n }\n } finally {\n buffer += decoder.decode();\n reader.releaseLock();\n }\n}\n\n/** Parse SSE data field as JSON, returning null on failure. */\nexport function parseSSEData<T = unknown>(data: string): T | null {\n try {\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,eAAsB,iBACpB,MACA,SACA,QACe;CACf,MAAM,SAAS,KAAK,WAAW;CAC/B,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,SAAS;CACb,IAAI,eAAe;CACnB,IAAI,cAAc;CAClB,IAAI,YAAY;AAEhB,KAAI;AACF,SAAO,MAAM;AACX,OAAI,QAAQ,QAAS;GACrB,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,OAAI,KAAM;AAEV,aAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;GACjD,MAAM,QAAQ,OAAO,MAAM,KAAK;AAEhC,YAAS,MAAM,KAAK,IAAI;AAExB,QAAK,MAAM,QAAQ,OAAO;AACxB,QAAI,SAAS,IAAI;AAEf,SAAI,YACF,SAAQ;MACN,OAAO,gBAAgB;MACvB,MAAM;MACN,IAAI,aAAa,KAAA;MAClB,CAAC;AAEJ,oBAAe;AACf,mBAAc;AACd,iBAAY;AACZ;;AAGF,QAAI,KAAK,WAAW,IAAI,CAEtB;IAGF,MAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,QAAI,eAAe,GAAI;IAEvB,MAAM,QAAQ,KAAK,MAAM,GAAG,WAAW;IAEvC,MAAM,aAAa,KAAK,aAAa,OAAO,MAAM,aAAa,IAAI,aAAa;IAChF,MAAM,aAAa,KAAK,MAAM,WAAW;AAEzC,YAAQ,OAAR;KACE,KAAK;AACH,qBAAe;AACf;KACF,KAAK;AACH,oBAAc,cAAc,GAAG,YAAY,IAAI,eAAe;AAC9D;KACF,KAAK;AACH,kBAAY;AACZ;;;;WAIA;AACR,YAAU,QAAQ,QAAQ;AAC1B,SAAO,aAAa;;;;AAKxB,SAAgB,aAA0B,MAAwB;AAChE,KAAI;AACF,SAAO,KAAK,MAAM,KAAK;SACjB;AACN,SAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembles incremental LLM streaming deltas into displayable text.
|
|
3
|
+
*
|
|
4
|
+
* Tracks per-run thinking + content text, composes them for display,
|
|
5
|
+
* and handles finalization.
|
|
6
|
+
*/
|
|
7
|
+
export declare class StreamAssembler {
|
|
8
|
+
private runs;
|
|
9
|
+
private getOrCreate;
|
|
10
|
+
/** Append a content token delta. Returns updated display text, or null if unchanged. */
|
|
11
|
+
ingestToken(runId: string, delta: string, showThinking: boolean): string | null;
|
|
12
|
+
/** Update thinking content. Returns updated display text, or null if unchanged. */
|
|
13
|
+
ingestThinking(runId: string, content: string, isDelta: boolean, showThinking: boolean): string | null;
|
|
14
|
+
/** Finalize a run and return the final display text. */
|
|
15
|
+
finalize(runId: string, showThinking: boolean): string;
|
|
16
|
+
/** Get current display text for a run. */
|
|
17
|
+
getDisplayText(runId: string): string;
|
|
18
|
+
/** Drop a run without finalizing. */
|
|
19
|
+
drop(runId: string): void;
|
|
20
|
+
/** Clear all run state. */
|
|
21
|
+
clear(): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/tui/stream-assembler.ts
|
|
2
|
+
function composeDisplay(thinking, content, showThinking) {
|
|
3
|
+
if (!showThinking || !thinking) return content;
|
|
4
|
+
const thinkingBlock = `<thinking>\n${thinking}\n</thinking>\n\n`;
|
|
5
|
+
return content ? `${thinkingBlock}${content}` : thinkingBlock;
|
|
6
|
+
}
|
|
7
|
+
var StreamAssembler = class {
|
|
8
|
+
runs = /* @__PURE__ */ new Map();
|
|
9
|
+
getOrCreate(runId) {
|
|
10
|
+
let state = this.runs.get(runId);
|
|
11
|
+
if (!state) {
|
|
12
|
+
state = {
|
|
13
|
+
thinkingText: "",
|
|
14
|
+
contentText: "",
|
|
15
|
+
displayText: ""
|
|
16
|
+
};
|
|
17
|
+
this.runs.set(runId, state);
|
|
18
|
+
}
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
/** Append a content token delta. Returns updated display text, or null if unchanged. */
|
|
22
|
+
ingestToken(runId, delta, showThinking) {
|
|
23
|
+
const state = this.getOrCreate(runId);
|
|
24
|
+
state.contentText += delta;
|
|
25
|
+
const next = composeDisplay(state.thinkingText, state.contentText, showThinking);
|
|
26
|
+
if (next === state.displayText) return null;
|
|
27
|
+
state.displayText = next;
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
/** Update thinking content. Returns updated display text, or null if unchanged. */
|
|
31
|
+
ingestThinking(runId, content, isDelta, showThinking) {
|
|
32
|
+
const state = this.getOrCreate(runId);
|
|
33
|
+
state.thinkingText = isDelta ? state.thinkingText + content : content;
|
|
34
|
+
const next = composeDisplay(state.thinkingText, state.contentText, showThinking);
|
|
35
|
+
if (next === state.displayText) return null;
|
|
36
|
+
state.displayText = next;
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
39
|
+
/** Finalize a run and return the final display text. */
|
|
40
|
+
finalize(runId, showThinking) {
|
|
41
|
+
const state = this.runs.get(runId);
|
|
42
|
+
if (!state) return "";
|
|
43
|
+
const final = composeDisplay(state.thinkingText, state.contentText, showThinking);
|
|
44
|
+
this.runs.delete(runId);
|
|
45
|
+
return final;
|
|
46
|
+
}
|
|
47
|
+
/** Get current display text for a run. */
|
|
48
|
+
getDisplayText(runId) {
|
|
49
|
+
return this.runs.get(runId)?.displayText ?? "";
|
|
50
|
+
}
|
|
51
|
+
/** Drop a run without finalizing. */
|
|
52
|
+
drop(runId) {
|
|
53
|
+
this.runs.delete(runId);
|
|
54
|
+
}
|
|
55
|
+
/** Clear all run state. */
|
|
56
|
+
clear() {
|
|
57
|
+
this.runs.clear();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { StreamAssembler };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=stream-assembler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-assembler.js","names":[],"sources":["../../../src/tui/stream-assembler.ts"],"sourcesContent":["/**\n * Assembles incremental LLM streaming deltas into displayable text.\n *\n * Tracks per-run thinking + content text, composes them for display,\n * and handles finalization.\n */\n\ninterface RunStreamState {\n thinkingText: string;\n contentText: string;\n displayText: string;\n}\n\nfunction composeDisplay(thinking: string, content: string, showThinking: boolean): string {\n if (!showThinking || !thinking) return content;\n const thinkingBlock = `<thinking>\\n${thinking}\\n</thinking>\\n\\n`;\n return content ? `${thinkingBlock}${content}` : thinkingBlock;\n}\n\nexport class StreamAssembler {\n private runs = new Map<string, RunStreamState>();\n\n private getOrCreate(runId: string): RunStreamState {\n let state = this.runs.get(runId);\n if (!state) {\n state = { thinkingText: '', contentText: '', displayText: '' };\n this.runs.set(runId, state);\n }\n return state;\n }\n\n /** Append a content token delta. Returns updated display text, or null if unchanged. */\n ingestToken(runId: string, delta: string, showThinking: boolean): string | null {\n const state = this.getOrCreate(runId);\n state.contentText += delta;\n const next = composeDisplay(state.thinkingText, state.contentText, showThinking);\n if (next === state.displayText) return null;\n state.displayText = next;\n return next;\n }\n\n /** Update thinking content. Returns updated display text, or null if unchanged. */\n ingestThinking(\n runId: string,\n content: string,\n isDelta: boolean,\n showThinking: boolean,\n ): string | null {\n const state = this.getOrCreate(runId);\n state.thinkingText = isDelta ? state.thinkingText + content : content;\n const next = composeDisplay(state.thinkingText, state.contentText, showThinking);\n if (next === state.displayText) return null;\n state.displayText = next;\n return next;\n }\n\n /** Finalize a run and return the final display text. */\n finalize(runId: string, showThinking: boolean): string {\n const state = this.runs.get(runId);\n if (!state) return '';\n const final = composeDisplay(state.thinkingText, state.contentText, showThinking);\n this.runs.delete(runId);\n return final;\n }\n\n /** Get current display text for a run. */\n getDisplayText(runId: string): string {\n return this.runs.get(runId)?.displayText ?? '';\n }\n\n /** Drop a run without finalizing. */\n drop(runId: string): void {\n this.runs.delete(runId);\n }\n\n /** Clear all run state. */\n clear(): void {\n this.runs.clear();\n }\n}\n"],"mappings":";AAaA,SAAS,eAAe,UAAkB,SAAiB,cAA+B;AACxF,KAAI,CAAC,gBAAgB,CAAC,SAAU,QAAO;CACvC,MAAM,gBAAgB,eAAe,SAAS;AAC9C,QAAO,UAAU,GAAG,gBAAgB,YAAY;;AAGlD,IAAa,kBAAb,MAA6B;CAC3B,uBAAe,IAAI,KAA6B;CAEhD,YAAoB,OAA+B;EACjD,IAAI,QAAQ,KAAK,KAAK,IAAI,MAAM;AAChC,MAAI,CAAC,OAAO;AACV,WAAQ;IAAE,cAAc;IAAI,aAAa;IAAI,aAAa;IAAI;AAC9D,QAAK,KAAK,IAAI,OAAO,MAAM;;AAE7B,SAAO;;;CAIT,YAAY,OAAe,OAAe,cAAsC;EAC9E,MAAM,QAAQ,KAAK,YAAY,MAAM;AACrC,QAAM,eAAe;EACrB,MAAM,OAAO,eAAe,MAAM,cAAc,MAAM,aAAa,aAAa;AAChF,MAAI,SAAS,MAAM,YAAa,QAAO;AACvC,QAAM,cAAc;AACpB,SAAO;;;CAIT,eACE,OACA,SACA,SACA,cACe;EACf,MAAM,QAAQ,KAAK,YAAY,MAAM;AACrC,QAAM,eAAe,UAAU,MAAM,eAAe,UAAU;EAC9D,MAAM,OAAO,eAAe,MAAM,cAAc,MAAM,aAAa,aAAa;AAChF,MAAI,SAAS,MAAM,YAAa,QAAO;AACvC,QAAM,cAAc;AACpB,SAAO;;;CAIT,SAAS,OAAe,cAA+B;EACrD,MAAM,QAAQ,KAAK,KAAK,IAAI,MAAM;AAClC,MAAI,CAAC,MAAO,QAAO;EACnB,MAAM,QAAQ,eAAe,MAAM,cAAc,MAAM,aAAa,aAAa;AACjF,OAAK,KAAK,OAAO,MAAM;AACvB,SAAO;;;CAIT,eAAe,OAAuB;AACpC,SAAO,KAAK,KAAK,IAAI,MAAM,EAAE,eAAe;;;CAI9C,KAAK,OAAqB;AACxB,OAAK,KAAK,OAAO,MAAM;;;CAIzB,QAAc;AACZ,OAAK,KAAK,OAAO"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { EditorTheme, MarkdownTheme, SelectListTheme } from '@mariozechner/pi-tui';
|
|
2
|
+
export declare const lightMode: boolean;
|
|
3
|
+
export declare const palette: {
|
|
4
|
+
readonly text: "#f5f5f7";
|
|
5
|
+
readonly dim: "#a1a1a6";
|
|
6
|
+
readonly accent: "#3b82f6";
|
|
7
|
+
readonly accentSoft: "#60a5fa";
|
|
8
|
+
readonly border: "#48484a";
|
|
9
|
+
readonly userBg: "#3a3a3c";
|
|
10
|
+
readonly userText: "#f5f5f7";
|
|
11
|
+
readonly systemText: "#8e8e93";
|
|
12
|
+
readonly toolPendingBg: "#2c2c2e";
|
|
13
|
+
readonly toolSuccessBg: "#1e2a22";
|
|
14
|
+
readonly toolErrorBg: "#2a2222";
|
|
15
|
+
readonly toolTitle: "#3b82f6";
|
|
16
|
+
readonly toolOutput: "#d1d1d6";
|
|
17
|
+
readonly quote: "#60a5fa";
|
|
18
|
+
readonly quoteBorder: "#48484a";
|
|
19
|
+
readonly code: "#d1d1d6";
|
|
20
|
+
readonly codeBlock: "#1c1c1e";
|
|
21
|
+
readonly codeBorder: "#48484a";
|
|
22
|
+
readonly link: "#60a5fa";
|
|
23
|
+
readonly error: "#f87171";
|
|
24
|
+
readonly success: "#34d399";
|
|
25
|
+
} | {
|
|
26
|
+
readonly text: "#1d1d1f";
|
|
27
|
+
readonly dim: "#6e6e73";
|
|
28
|
+
readonly accent: "#2563eb";
|
|
29
|
+
readonly accentSoft: "#3b82f6";
|
|
30
|
+
readonly border: "#d2d2d7";
|
|
31
|
+
readonly userBg: "#ffffff";
|
|
32
|
+
readonly userText: "#1d1d1f";
|
|
33
|
+
readonly systemText: "#86868b";
|
|
34
|
+
readonly toolPendingBg: "#f0f5ff";
|
|
35
|
+
readonly toolSuccessBg: "#ecfdf5";
|
|
36
|
+
readonly toolErrorBg: "#fef2f2";
|
|
37
|
+
readonly toolTitle: "#2563eb";
|
|
38
|
+
readonly toolOutput: "#6e6e73";
|
|
39
|
+
readonly quote: "#2563eb";
|
|
40
|
+
readonly quoteBorder: "#d2d2d7";
|
|
41
|
+
readonly code: "#92400e";
|
|
42
|
+
readonly codeBlock: "#ffffff";
|
|
43
|
+
readonly codeBorder: "#d2d2d7";
|
|
44
|
+
readonly link: "#2563eb";
|
|
45
|
+
readonly error: "#dc2626";
|
|
46
|
+
readonly success: "#059669";
|
|
47
|
+
};
|
|
48
|
+
export declare const theme: {
|
|
49
|
+
fg: (text: string) => string;
|
|
50
|
+
assistantText: (text: string) => string;
|
|
51
|
+
dim: (text: string) => string;
|
|
52
|
+
accent: (text: string) => string;
|
|
53
|
+
accentSoft: (text: string) => string;
|
|
54
|
+
success: (text: string) => string;
|
|
55
|
+
error: (text: string) => string;
|
|
56
|
+
header: (text: string) => string;
|
|
57
|
+
system: (text: string) => string;
|
|
58
|
+
userBg: (text: string) => string;
|
|
59
|
+
userText: (text: string) => string;
|
|
60
|
+
toolTitle: (text: string) => string;
|
|
61
|
+
toolOutput: (text: string) => string;
|
|
62
|
+
toolPendingBg: (text: string) => string;
|
|
63
|
+
toolSuccessBg: (text: string) => string;
|
|
64
|
+
toolErrorBg: (text: string) => string;
|
|
65
|
+
border: (text: string) => string;
|
|
66
|
+
bold: (text: string) => string;
|
|
67
|
+
italic: (text: string) => string;
|
|
68
|
+
};
|
|
69
|
+
export declare const markdownTheme: MarkdownTheme;
|
|
70
|
+
export declare const selectListTheme: SelectListTheme;
|
|
71
|
+
export declare const editorTheme: EditorTheme;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
//#region src/tui/theme.ts
|
|
3
|
+
const XTERM_LEVELS = [
|
|
4
|
+
0,
|
|
5
|
+
95,
|
|
6
|
+
135,
|
|
7
|
+
175,
|
|
8
|
+
215,
|
|
9
|
+
255
|
|
10
|
+
];
|
|
11
|
+
function channelToSrgb(value) {
|
|
12
|
+
const normalized = value / 255;
|
|
13
|
+
return normalized <= .03928 ? normalized / 12.92 : ((normalized + .055) / 1.055) ** 2.4;
|
|
14
|
+
}
|
|
15
|
+
function relativeLuminanceRgb(red, green, blue) {
|
|
16
|
+
return .2126 * channelToSrgb(red) + .7152 * channelToSrgb(green) + .0722 * channelToSrgb(blue);
|
|
17
|
+
}
|
|
18
|
+
function relativeLuminanceHex(hex) {
|
|
19
|
+
return relativeLuminanceRgb(Number.parseInt(hex.slice(1, 3), 16), Number.parseInt(hex.slice(3, 5), 16), Number.parseInt(hex.slice(5, 7), 16));
|
|
20
|
+
}
|
|
21
|
+
function contrastRatio(bgLuminance, fgHex) {
|
|
22
|
+
const fgLuminance = relativeLuminanceHex(fgHex);
|
|
23
|
+
const lighter = Math.max(bgLuminance, fgLuminance);
|
|
24
|
+
const darker = Math.min(bgLuminance, fgLuminance);
|
|
25
|
+
return (lighter + .05) / (darker + .05);
|
|
26
|
+
}
|
|
27
|
+
function isLightBackground() {
|
|
28
|
+
const explicit = (process.env.XOPC_THEME ?? "").toLowerCase().trim();
|
|
29
|
+
if (explicit === "light") return true;
|
|
30
|
+
if (explicit === "dark") return false;
|
|
31
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
32
|
+
if (colorfgbg && colorfgbg.length <= 64) {
|
|
33
|
+
const sep = colorfgbg.lastIndexOf(";");
|
|
34
|
+
const bg = Number.parseInt(sep >= 0 ? colorfgbg.slice(sep + 1) : colorfgbg, 10);
|
|
35
|
+
if (bg >= 0 && bg <= 255) {
|
|
36
|
+
if (bg <= 15) return bg === 7 || bg === 15;
|
|
37
|
+
if (bg >= 232) return bg >= 244;
|
|
38
|
+
const cubeIndex = bg - 16;
|
|
39
|
+
const bVal = XTERM_LEVELS[cubeIndex % 6];
|
|
40
|
+
const gVal = XTERM_LEVELS[Math.floor(cubeIndex / 6) % 6];
|
|
41
|
+
const rVal = XTERM_LEVELS[Math.floor(cubeIndex / 36)];
|
|
42
|
+
const bgLum = relativeLuminanceRgb(rVal, gVal, bVal);
|
|
43
|
+
return contrastRatio(bgLum, "#1d1d1f") >= contrastRatio(bgLum, "#f5f5f7");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const lightMode = isLightBackground();
|
|
49
|
+
const palette = lightMode ? {
|
|
50
|
+
text: "#1d1d1f",
|
|
51
|
+
dim: "#6e6e73",
|
|
52
|
+
accent: "#2563eb",
|
|
53
|
+
accentSoft: "#3b82f6",
|
|
54
|
+
border: "#d2d2d7",
|
|
55
|
+
userBg: "#ffffff",
|
|
56
|
+
userText: "#1d1d1f",
|
|
57
|
+
systemText: "#86868b",
|
|
58
|
+
toolPendingBg: "#f0f5ff",
|
|
59
|
+
toolSuccessBg: "#ecfdf5",
|
|
60
|
+
toolErrorBg: "#fef2f2",
|
|
61
|
+
toolTitle: "#2563eb",
|
|
62
|
+
toolOutput: "#6e6e73",
|
|
63
|
+
quote: "#2563eb",
|
|
64
|
+
quoteBorder: "#d2d2d7",
|
|
65
|
+
code: "#92400e",
|
|
66
|
+
codeBlock: "#ffffff",
|
|
67
|
+
codeBorder: "#d2d2d7",
|
|
68
|
+
link: "#2563eb",
|
|
69
|
+
error: "#dc2626",
|
|
70
|
+
success: "#059669"
|
|
71
|
+
} : {
|
|
72
|
+
text: "#f5f5f7",
|
|
73
|
+
dim: "#a1a1a6",
|
|
74
|
+
accent: "#3b82f6",
|
|
75
|
+
accentSoft: "#60a5fa",
|
|
76
|
+
border: "#48484a",
|
|
77
|
+
userBg: "#3a3a3c",
|
|
78
|
+
userText: "#f5f5f7",
|
|
79
|
+
systemText: "#8e8e93",
|
|
80
|
+
toolPendingBg: "#2c2c2e",
|
|
81
|
+
toolSuccessBg: "#1e2a22",
|
|
82
|
+
toolErrorBg: "#2a2222",
|
|
83
|
+
toolTitle: "#3b82f6",
|
|
84
|
+
toolOutput: "#d1d1d6",
|
|
85
|
+
quote: "#60a5fa",
|
|
86
|
+
quoteBorder: "#48484a",
|
|
87
|
+
code: "#d1d1d6",
|
|
88
|
+
codeBlock: "#1c1c1e",
|
|
89
|
+
codeBorder: "#48484a",
|
|
90
|
+
link: "#60a5fa",
|
|
91
|
+
error: "#f87171",
|
|
92
|
+
success: "#34d399"
|
|
93
|
+
};
|
|
94
|
+
const fg = (hex) => (text) => chalk.hex(hex)(text);
|
|
95
|
+
const bg = (hex) => (text) => chalk.bgHex(hex)(text);
|
|
96
|
+
function highlightCode(code) {
|
|
97
|
+
return code.split("\n").map((line) => fg(palette.code)(line));
|
|
98
|
+
}
|
|
99
|
+
const theme = {
|
|
100
|
+
fg: fg(palette.text),
|
|
101
|
+
assistantText: (text) => text,
|
|
102
|
+
dim: fg(palette.dim),
|
|
103
|
+
accent: fg(palette.accent),
|
|
104
|
+
accentSoft: fg(palette.accentSoft),
|
|
105
|
+
success: fg(palette.success),
|
|
106
|
+
error: fg(palette.error),
|
|
107
|
+
header: (text) => chalk.bold(fg(palette.accent)(text)),
|
|
108
|
+
system: fg(palette.systemText),
|
|
109
|
+
userBg: bg(palette.userBg),
|
|
110
|
+
userText: fg(palette.userText),
|
|
111
|
+
toolTitle: fg(palette.toolTitle),
|
|
112
|
+
toolOutput: fg(palette.toolOutput),
|
|
113
|
+
toolPendingBg: bg(palette.toolPendingBg),
|
|
114
|
+
toolSuccessBg: bg(palette.toolSuccessBg),
|
|
115
|
+
toolErrorBg: bg(palette.toolErrorBg),
|
|
116
|
+
border: fg(palette.border),
|
|
117
|
+
bold: (text) => chalk.bold(text),
|
|
118
|
+
italic: (text) => chalk.italic(text)
|
|
119
|
+
};
|
|
120
|
+
const markdownTheme = {
|
|
121
|
+
heading: (text) => chalk.bold(fg(palette.accent)(text)),
|
|
122
|
+
link: (text) => fg(palette.link)(text),
|
|
123
|
+
linkUrl: (text) => chalk.dim(text),
|
|
124
|
+
code: (text) => fg(palette.code)(text),
|
|
125
|
+
codeBlock: (text) => fg(palette.code)(text),
|
|
126
|
+
codeBlockBorder: (text) => fg(palette.codeBorder)(text),
|
|
127
|
+
quote: (text) => fg(palette.quote)(text),
|
|
128
|
+
quoteBorder: (text) => fg(palette.quoteBorder)(text),
|
|
129
|
+
hr: (text) => fg(palette.border)(text),
|
|
130
|
+
listBullet: (text) => fg(palette.accentSoft)(text),
|
|
131
|
+
bold: (text) => chalk.bold(text),
|
|
132
|
+
italic: (text) => chalk.italic(text),
|
|
133
|
+
strikethrough: (text) => chalk.strikethrough(text),
|
|
134
|
+
underline: (text) => chalk.underline(text),
|
|
135
|
+
highlightCode
|
|
136
|
+
};
|
|
137
|
+
const selectListTheme = {
|
|
138
|
+
selectedPrefix: (text) => fg(palette.accent)(text),
|
|
139
|
+
selectedText: (text) => chalk.bold(fg(palette.accent)(text)),
|
|
140
|
+
description: (text) => fg(palette.dim)(text),
|
|
141
|
+
scrollInfo: (text) => fg(palette.dim)(text),
|
|
142
|
+
noMatch: (text) => fg(palette.dim)(text)
|
|
143
|
+
};
|
|
144
|
+
const editorTheme = {
|
|
145
|
+
borderColor: (text) => fg(palette.border)(text),
|
|
146
|
+
selectList: selectListTheme
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { editorTheme, lightMode, markdownTheme, palette, selectListTheme, theme };
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/tui/theme.ts"],"sourcesContent":["import type { EditorTheme, MarkdownTheme, SelectListTheme } from '@mariozechner/pi-tui';\nimport chalk from 'chalk';\n\nconst XTERM_LEVELS = [0, 95, 135, 175, 215, 255] as const;\n\nfunction channelToSrgb(value: number): number {\n const normalized = value / 255;\n return normalized <= 0.03928\n ? normalized / 12.92\n : ((normalized + 0.055) / 1.055) ** 2.4;\n}\n\nfunction relativeLuminanceRgb(red: number, green: number, blue: number): number {\n return (\n 0.2126 * channelToSrgb(red) +\n 0.7152 * channelToSrgb(green) +\n 0.0722 * channelToSrgb(blue)\n );\n}\n\nfunction relativeLuminanceHex(hex: string): number {\n return relativeLuminanceRgb(\n Number.parseInt(hex.slice(1, 3), 16),\n Number.parseInt(hex.slice(3, 5), 16),\n Number.parseInt(hex.slice(5, 7), 16),\n );\n}\n\nfunction contrastRatio(bgLuminance: number, fgHex: string): number {\n const fgLuminance = relativeLuminanceHex(fgHex);\n const lighter = Math.max(bgLuminance, fgLuminance);\n const darker = Math.min(bgLuminance, fgLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction isLightBackground(): boolean {\n const explicit = (process.env.XOPC_THEME ?? '').toLowerCase().trim();\n if (explicit === 'light') return true;\n if (explicit === 'dark') return false;\n\n const colorfgbg = process.env.COLORFGBG;\n if (colorfgbg && colorfgbg.length <= 64) {\n const sep = colorfgbg.lastIndexOf(';');\n const bg = Number.parseInt(sep >= 0 ? colorfgbg.slice(sep + 1) : colorfgbg, 10);\n if (bg >= 0 && bg <= 255) {\n if (bg <= 15) return bg === 7 || bg === 15;\n if (bg >= 232) return bg >= 244;\n const cubeIndex = bg - 16;\n const bVal = XTERM_LEVELS[cubeIndex % 6]!;\n const gVal = XTERM_LEVELS[Math.floor(cubeIndex / 6) % 6]!;\n const rVal = XTERM_LEVELS[Math.floor(cubeIndex / 36)]!;\n const bgLum = relativeLuminanceRgb(rVal, gVal, bVal);\n return (\n contrastRatio(bgLum, '#1d1d1f') >= contrastRatio(bgLum, '#f5f5f7')\n );\n }\n }\n return false;\n}\n\nexport const lightMode = isLightBackground();\n\n// Palette tokens align with DESIGN.md (§2 surface/text/border, §2.5 accent, §12.1).\nconst darkPalette = {\n text: '#f5f5f7',\n dim: '#a1a1a6',\n accent: '#3b82f6',\n accentSoft: '#60a5fa',\n border: '#48484a',\n userBg: '#3a3a3c',\n userText: '#f5f5f7',\n systemText: '#8e8e93',\n toolPendingBg: '#2c2c2e',\n toolSuccessBg: '#1e2a22',\n toolErrorBg: '#2a2222',\n toolTitle: '#3b82f6',\n toolOutput: '#d1d1d6',\n quote: '#60a5fa',\n quoteBorder: '#48484a',\n code: '#d1d1d6',\n codeBlock: '#1c1c1e',\n codeBorder: '#48484a',\n link: '#60a5fa',\n error: '#f87171',\n success: '#34d399',\n} as const;\n\nconst lightPalette = {\n text: '#1d1d1f',\n dim: '#6e6e73',\n accent: '#2563eb',\n accentSoft: '#3b82f6',\n border: '#d2d2d7',\n userBg: '#ffffff',\n userText: '#1d1d1f',\n systemText: '#86868b',\n toolPendingBg: '#f0f5ff',\n toolSuccessBg: '#ecfdf5',\n toolErrorBg: '#fef2f2',\n toolTitle: '#2563eb',\n toolOutput: '#6e6e73',\n quote: '#2563eb',\n quoteBorder: '#d2d2d7',\n code: '#92400e',\n codeBlock: '#ffffff',\n codeBorder: '#d2d2d7',\n link: '#2563eb',\n error: '#dc2626',\n success: '#059669',\n} as const;\n\nexport const palette = lightMode ? lightPalette : darkPalette;\n\nconst fg = (hex: string) => (text: string) => chalk.hex(hex)(text);\nconst bg = (hex: string) => (text: string) => chalk.bgHex(hex)(text);\n\nfunction highlightCode(code: string): string[] {\n return code.split('\\n').map((line) => fg(palette.code)(line));\n}\n\nexport const theme = {\n fg: fg(palette.text),\n assistantText: (text: string) => text,\n dim: fg(palette.dim),\n accent: fg(palette.accent),\n accentSoft: fg(palette.accentSoft),\n success: fg(palette.success),\n error: fg(palette.error),\n header: (text: string) => chalk.bold(fg(palette.accent)(text)),\n system: fg(palette.systemText),\n userBg: bg(palette.userBg),\n userText: fg(palette.userText),\n toolTitle: fg(palette.toolTitle),\n toolOutput: fg(palette.toolOutput),\n toolPendingBg: bg(palette.toolPendingBg),\n toolSuccessBg: bg(palette.toolSuccessBg),\n toolErrorBg: bg(palette.toolErrorBg),\n border: fg(palette.border),\n bold: (text: string) => chalk.bold(text),\n italic: (text: string) => chalk.italic(text),\n};\n\nexport const markdownTheme: MarkdownTheme = {\n heading: (text) => chalk.bold(fg(palette.accent)(text)),\n link: (text) => fg(palette.link)(text),\n linkUrl: (text) => chalk.dim(text),\n code: (text) => fg(palette.code)(text),\n codeBlock: (text) => fg(palette.code)(text),\n codeBlockBorder: (text) => fg(palette.codeBorder)(text),\n quote: (text) => fg(palette.quote)(text),\n quoteBorder: (text) => fg(palette.quoteBorder)(text),\n hr: (text) => fg(palette.border)(text),\n listBullet: (text) => fg(palette.accentSoft)(text),\n bold: (text) => chalk.bold(text),\n italic: (text) => chalk.italic(text),\n strikethrough: (text) => chalk.strikethrough(text),\n underline: (text) => chalk.underline(text),\n highlightCode,\n};\n\nexport const selectListTheme: SelectListTheme = {\n selectedPrefix: (text) => fg(palette.accent)(text),\n selectedText: (text) => chalk.bold(fg(palette.accent)(text)),\n description: (text) => fg(palette.dim)(text),\n scrollInfo: (text) => fg(palette.dim)(text),\n noMatch: (text) => fg(palette.dim)(text),\n};\n\nexport const editorTheme: EditorTheme = {\n borderColor: (text) => fg(palette.border)(text),\n selectList: selectListTheme,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { SessionInfo } from './tui-types.js';
|
|
2
|
+
/** Options for sending a chat message. */
|
|
3
|
+
export interface ChatSendOptions {
|
|
4
|
+
sessionKey: string;
|
|
5
|
+
message: string;
|
|
6
|
+
thinking?: string;
|
|
7
|
+
}
|
|
8
|
+
/** SSE event from the agent stream or broadcast channel. */
|
|
9
|
+
export interface TuiEvent {
|
|
10
|
+
event: string;
|
|
11
|
+
data: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** Minimal session list item. */
|
|
14
|
+
export interface TuiSessionItem {
|
|
15
|
+
key: string;
|
|
16
|
+
updatedAt?: number | null;
|
|
17
|
+
model?: string | null;
|
|
18
|
+
totalTokens?: number | null;
|
|
19
|
+
displayName?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Model choice for the selector overlay. */
|
|
22
|
+
export interface TuiModelChoice {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Abstraction over the gateway (SSE) or embedded agent backend.
|
|
29
|
+
*
|
|
30
|
+
* Both implementations expose the same surface so the TUI core stays
|
|
31
|
+
* transport-agnostic.
|
|
32
|
+
*/
|
|
33
|
+
export interface TuiBackend {
|
|
34
|
+
/** Connection metadata (for header display). */
|
|
35
|
+
readonly connectionLabel: string;
|
|
36
|
+
/** Lifecycle callbacks wired by the TUI. */
|
|
37
|
+
onEvent?: (evt: TuiEvent) => void;
|
|
38
|
+
onConnected?: () => void;
|
|
39
|
+
onDisconnected?: (reason: string) => void;
|
|
40
|
+
/** Start the backend (open SSE streams / start agent service). */
|
|
41
|
+
start(): void;
|
|
42
|
+
/** Stop the backend. */
|
|
43
|
+
stop(): void;
|
|
44
|
+
/** Send a chat message, returns the run id. */
|
|
45
|
+
sendChat(opts: ChatSendOptions): Promise<{
|
|
46
|
+
runId: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Abort an active run. */
|
|
49
|
+
abortChat(opts: {
|
|
50
|
+
sessionKey: string;
|
|
51
|
+
runId: string;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
ok: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
/** Load chat history for a session. */
|
|
56
|
+
loadHistory(opts: {
|
|
57
|
+
sessionKey: string;
|
|
58
|
+
limit?: number;
|
|
59
|
+
}): Promise<{
|
|
60
|
+
messages: HistoryMessage[];
|
|
61
|
+
}>;
|
|
62
|
+
/** List sessions. */
|
|
63
|
+
listSessions(): Promise<TuiSessionItem[]>;
|
|
64
|
+
/** Fetch session info (model, tokens, thinking). */
|
|
65
|
+
getSessionInfo(sessionKey: string): Promise<SessionInfo>;
|
|
66
|
+
/** List available models. */
|
|
67
|
+
listModels(): Promise<TuiModelChoice[]>;
|
|
68
|
+
/** Reset / create new session. */
|
|
69
|
+
resetSession(sessionKey: string): Promise<void>;
|
|
70
|
+
/** Patch session settings (e.g. model). */
|
|
71
|
+
patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
/** A single message in chat history. */
|
|
74
|
+
export interface HistoryMessage {
|
|
75
|
+
role: 'user' | 'assistant' | 'system';
|
|
76
|
+
content: string;
|
|
77
|
+
timestamp?: number;
|
|
78
|
+
toolCalls?: Array<{
|
|
79
|
+
name: string;
|
|
80
|
+
args?: unknown;
|
|
81
|
+
result?: string;
|
|
82
|
+
isError?: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** TUI configuration options passed from CLI. */
|
|
2
|
+
export interface TuiOptions {
|
|
3
|
+
/** Connect to a running gateway instead of embedded mode. */
|
|
4
|
+
url?: string;
|
|
5
|
+
/** Gateway bearer token. */
|
|
6
|
+
token?: string;
|
|
7
|
+
/** Session key to resume. */
|
|
8
|
+
session?: string;
|
|
9
|
+
/** Thinking level override. */
|
|
10
|
+
thinking?: string;
|
|
11
|
+
/** Single message to send on start, then stay open. */
|
|
12
|
+
message?: string;
|
|
13
|
+
/** Run in embedded (local) mode — no gateway required. */
|
|
14
|
+
local?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export type TuiExitReason = 'exit' | 'signal';
|
|
17
|
+
export interface TuiResult {
|
|
18
|
+
exitReason: TuiExitReason;
|
|
19
|
+
}
|
|
20
|
+
/** SSE events emitted by POST /api/agent. */
|
|
21
|
+
export interface AgentSSEStatusEvent {
|
|
22
|
+
status: string;
|
|
23
|
+
runId: string;
|
|
24
|
+
}
|
|
25
|
+
export interface AgentSSETokenEvent {
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentSSEThinkingEvent {
|
|
29
|
+
content: string;
|
|
30
|
+
isDelta?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface AgentSSEToolStartEvent {
|
|
33
|
+
toolName: string;
|
|
34
|
+
toolCallId: string;
|
|
35
|
+
args?: unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface AgentSSEToolEndEvent {
|
|
38
|
+
toolName: string;
|
|
39
|
+
toolCallId: string;
|
|
40
|
+
isError: boolean;
|
|
41
|
+
result?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface AgentSSEErrorEvent {
|
|
44
|
+
content: string;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentSSEResultEvent {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
payload?: {
|
|
49
|
+
status?: string;
|
|
50
|
+
summary?: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Parsed SSE event from the stream. */
|
|
54
|
+
export interface ParsedSSEEvent {
|
|
55
|
+
event: string;
|
|
56
|
+
data: string;
|
|
57
|
+
id?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Activity status for the TUI status bar. */
|
|
60
|
+
export type ActivityStatus = 'idle' | 'sending' | 'waiting' | 'streaming' | 'running';
|
|
61
|
+
/** Session metadata shown in the TUI footer. */
|
|
62
|
+
export interface SessionInfo {
|
|
63
|
+
model?: string;
|
|
64
|
+
modelProvider?: string;
|
|
65
|
+
thinkingLevel?: string;
|
|
66
|
+
contextTokens?: number | null;
|
|
67
|
+
totalTokens?: number | null;
|
|
68
|
+
displayName?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Mutable state bag for the TUI runtime. */
|
|
71
|
+
export interface TuiState {
|
|
72
|
+
currentSessionKey: string;
|
|
73
|
+
activeRunId: string | null;
|
|
74
|
+
isConnected: boolean;
|
|
75
|
+
activityStatus: ActivityStatus;
|
|
76
|
+
connectionStatus: string;
|
|
77
|
+
sessionInfo: SessionInfo;
|
|
78
|
+
autoMessageSent: boolean;
|
|
79
|
+
historyLoaded: boolean;
|
|
80
|
+
toolsExpanded: boolean;
|
|
81
|
+
showThinking: boolean;
|
|
82
|
+
lastCtrlCAt: number;
|
|
83
|
+
exitRequested: boolean;
|
|
84
|
+
}
|
|
85
|
+
export declare function createInitialState(sessionKey: string): TuiState;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/tui/tui-types.ts
|
|
2
|
+
function createInitialState(sessionKey) {
|
|
3
|
+
return {
|
|
4
|
+
currentSessionKey: sessionKey,
|
|
5
|
+
activeRunId: null,
|
|
6
|
+
isConnected: false,
|
|
7
|
+
activityStatus: "idle",
|
|
8
|
+
connectionStatus: "connecting",
|
|
9
|
+
sessionInfo: {},
|
|
10
|
+
autoMessageSent: false,
|
|
11
|
+
historyLoaded: false,
|
|
12
|
+
toolsExpanded: false,
|
|
13
|
+
showThinking: false,
|
|
14
|
+
lastCtrlCAt: 0,
|
|
15
|
+
exitRequested: false
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { createInitialState };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=tui-types.js.map
|