@xopcai/xopc 0.0.27 → 0.0.29
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-CkgFSiCY.js +216 -0
- package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js → apps-page-Bmq19MS-.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js.map → apps-page-Bmq19MS-.js.map} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +9 -0
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +2 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +1 -0
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js → cron-utils-N1PqD2DB.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js.map → cron-utils-N1PqD2DB.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js → dist--p2HQ2QF.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js.map → dist--p2HQ2QF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js → extension-debug-page-DwHCB_6T.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js.map → extension-debug-page-DwHCB_6T.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js → extension-page-BsYwQIex.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js.map → extension-page-BsYwQIex.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js → extension-settings-page-nsisEgjB.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js.map → extension-settings-page-nsisEgjB.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-CR8zUHGR.js +4734 -0
- package/dist/gateway/static/root/assets/{index-PfkB8N37.js.map → index-CR8zUHGR.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Dnfha4O2.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +2 -0
- package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js.map +1 -0
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +2 -0
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js.map +1 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js.map → skills-page-Clg8deH0.js.map} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/lifecycle/hook-handler.d.ts +2 -0
- package/dist/src/agent/lifecycle/hook-handler.js +24 -0
- package/dist/src/agent/lifecycle/hook-handler.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.js +10 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +77 -20
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +15 -0
- package/dist/src/agent/service.js +21 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/channels/weixin/index.js +1 -1
- package/dist/src/cli/agent-chat-log-level-preset.d.ts +8 -0
- package/dist/src/cli/agent-chat-log-level-preset.js +25 -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 +7 -3
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.d.ts +6 -0
- package/dist/src/config/schema.js +11 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/hooks.js +5 -1
- package/dist/src/extensions/hooks.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +1 -0
- package/dist/src/extensions/loader.js +3 -1
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +8 -0
- package/dist/src/extensions/types/hooks.d.ts +16 -1
- package/dist/src/extensions/types/hooks.js +1 -0
- package/dist/src/extensions/types/hooks.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +19 -1
- package/dist/src/gateway/agents-admin.js +164 -3
- package/dist/src/gateway/agents-admin.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 +31 -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/hono/routes/agents.js +59 -5
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +2 -2
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +17 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- 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 +3 -1
- package/dist/src/gateway/service.js +40 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/session/client-history.d.ts +21 -0
- package/dist/src/session/client-history.js +89 -0
- package/dist/src/session/client-history.js.map +1 -0
- package/dist/src/session/index.d.ts +1 -0
- package/dist/src/session/index.js +2 -1
- package/dist/src/session/manager.d.ts +2 -0
- package/dist/src/session/manager.js +5 -0
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/thinking-resolve.js +1 -1
- package/dist/src/session/thinking-resolve.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +42 -0
- package/dist/src/tui/backends/embedded-backend.js +173 -0
- package/dist/src/tui/backends/embedded-backend.js.map +1 -0
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +53 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +256 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -0
- package/dist/src/tui/chat-history.d.ts +4 -0
- package/dist/src/tui/chat-history.js +29 -0
- package/dist/src/tui/chat-history.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 +21 -0
- package/dist/src/tui/components/chat-log.js +113 -0
- package/dist/src/tui/components/chat-log.js.map +1 -0
- package/dist/src/tui/components/custom-editor.d.ts +14 -0
- package/dist/src/tui/components/custom-editor.js +50 -0
- package/dist/src/tui/components/custom-editor.js.map +1 -0
- package/dist/src/tui/components/fuzzy-filter.d.ts +17 -0
- package/dist/src/tui/components/fuzzy-filter.js +85 -0
- package/dist/src/tui/components/fuzzy-filter.js.map +1 -0
- package/dist/src/tui/components/searchable-select-list.d.ts +39 -0
- package/dist/src/tui/components/searchable-select-list.js +257 -0
- package/dist/src/tui/components/searchable-select-list.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 +73 -0
- package/dist/src/tui/theme.js +157 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/tui-agent-events.d.ts +7 -0
- package/dist/src/tui/tui-agent-events.js +103 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -0
- package/dist/src/tui/tui-backend.d.ts +80 -0
- package/dist/src/tui/tui-backend.js +1 -0
- package/dist/src/tui/tui-commands.d.ts +23 -0
- package/dist/src/tui/tui-commands.js +165 -0
- package/dist/src/tui/tui-commands.js.map +1 -0
- package/dist/src/tui/tui-lifecycle.d.ts +26 -0
- package/dist/src/tui/tui-lifecycle.js +57 -0
- package/dist/src/tui/tui-lifecycle.js.map +1 -0
- package/dist/src/tui/tui-local-shell.d.ts +28 -0
- package/dist/src/tui/tui-local-shell.js +147 -0
- package/dist/src/tui/tui-local-shell.js.map +1 -0
- package/dist/src/tui/tui-overlays.d.ts +8 -0
- package/dist/src/tui/tui-overlays.js +22 -0
- package/dist/src/tui/tui-overlays.js.map +1 -0
- package/dist/src/tui/tui-picker-overlay.d.ts +26 -0
- package/dist/src/tui/tui-picker-overlay.js +69 -0
- package/dist/src/tui/tui-picker-overlay.js.map +1 -0
- package/dist/src/tui/tui-stdio-filter.d.ts +17 -0
- package/dist/src/tui/tui-stdio-filter.js +96 -0
- package/dist/src/tui/tui-stdio-filter.js.map +1 -0
- package/dist/src/tui/tui-submit.d.ts +25 -0
- package/dist/src/tui/tui-submit.js +102 -0
- package/dist/src/tui/tui-submit.js.map +1 -0
- package/dist/src/tui/tui-suspend.d.ts +10 -0
- package/dist/src/tui/tui-suspend.js +18 -0
- package/dist/src/tui/tui-suspend.js.map +1 -0
- package/dist/src/tui/tui-types.d.ts +86 -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 +5 -0
- package/dist/src/tui/tui.js +389 -0
- package/dist/src/tui/tui.js.map +1 -0
- package/package.json +5 -3
- package/dist/gateway/static/root/assets/agents-w8_jzuiX.js +0 -216
- package/dist/gateway/static/root/assets/agents-w8_jzuiX.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js +0 -9
- package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js.map +0 -1
- package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js.map +0 -1
- package/dist/gateway/static/root/assets/index-OT4cGzon.css +0 -1
- package/dist/gateway/static/root/assets/index-PfkB8N37.js +0 -4734
- 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
- package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-BmBDCEbY.js +0 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { prependEnvelopeTimestamp } from "../../../channels/envelope-timestamp.js";
|
|
2
|
+
//#region src/cli/commands/agent/stream-renderer.ts
|
|
3
|
+
/** Styled labels for terminal output */
|
|
4
|
+
const STYLE = {
|
|
5
|
+
dim: "\x1B[2m",
|
|
6
|
+
reset: "\x1B[0m",
|
|
7
|
+
cyan: "\x1B[36m",
|
|
8
|
+
yellow: "\x1B[33m",
|
|
9
|
+
green: "\x1B[32m",
|
|
10
|
+
red: "\x1B[31m",
|
|
11
|
+
bold: "\x1B[1m",
|
|
12
|
+
magenta: "\x1B[35m"
|
|
13
|
+
};
|
|
14
|
+
function formatToolArgs(args) {
|
|
15
|
+
if (!args || typeof args !== "object") return "";
|
|
16
|
+
const entries = Object.entries(args);
|
|
17
|
+
if (entries.length === 0) return "";
|
|
18
|
+
return entries.map(([key, value]) => {
|
|
19
|
+
const stringValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
20
|
+
return `${key}=${stringValue.length > 80 ? stringValue.slice(0, 77) + "..." : stringValue}`;
|
|
21
|
+
}).join(", ");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Consume the processDirectStreaming async generator and render
|
|
25
|
+
* thinking / tool-call / response tokens to stdout.
|
|
26
|
+
*
|
|
27
|
+
* Returns the final aggregated response text.
|
|
28
|
+
*/
|
|
29
|
+
async function renderStreamToTerminal(agent, message, sessionKey) {
|
|
30
|
+
const stamped = message.trimStart().startsWith("/") ? message : prependEnvelopeTimestamp(message);
|
|
31
|
+
const stream = agent.processDirectStreaming(stamped, sessionKey);
|
|
32
|
+
let responseText = "";
|
|
33
|
+
let isFirstToken = true;
|
|
34
|
+
let isInThinking = false;
|
|
35
|
+
let hasThinking = false;
|
|
36
|
+
for await (const event of stream) switch (event.type) {
|
|
37
|
+
case "thinking": {
|
|
38
|
+
const content = event.content;
|
|
39
|
+
if (event.status === "started") {
|
|
40
|
+
isInThinking = true;
|
|
41
|
+
hasThinking = true;
|
|
42
|
+
process.stdout.write(`\n${STYLE.dim}${STYLE.magenta}💭 Thinking...${STYLE.reset}\n`);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (content) {
|
|
46
|
+
if (!hasThinking) {
|
|
47
|
+
isInThinking = true;
|
|
48
|
+
hasThinking = true;
|
|
49
|
+
process.stdout.write(`\n${STYLE.dim}${STYLE.magenta}💭 Thinking...${STYLE.reset}\n`);
|
|
50
|
+
}
|
|
51
|
+
process.stdout.write(`${STYLE.dim}${content}${STYLE.reset}`);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "tool_start": {
|
|
56
|
+
const toolName = event.toolName;
|
|
57
|
+
const args = event.args;
|
|
58
|
+
if (isInThinking) {
|
|
59
|
+
isInThinking = false;
|
|
60
|
+
process.stdout.write("\n");
|
|
61
|
+
}
|
|
62
|
+
const argsStr = formatToolArgs(args);
|
|
63
|
+
process.stdout.write(`\n${STYLE.cyan}🔧 Tool: ${STYLE.bold}${toolName}${STYLE.reset}` + (argsStr ? `${STYLE.dim} (${argsStr})${STYLE.reset}` : "") + "\n");
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "tool_end": {
|
|
67
|
+
const toolName = event.toolName;
|
|
68
|
+
if (event.isError) process.stdout.write(`${STYLE.red} ✗ ${toolName} failed${STYLE.reset}\n`);
|
|
69
|
+
else process.stdout.write(`${STYLE.green} ✓ ${toolName} done${STYLE.reset}\n`);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "token": {
|
|
73
|
+
const content = event.content;
|
|
74
|
+
if (!content) break;
|
|
75
|
+
if (isInThinking) {
|
|
76
|
+
isInThinking = false;
|
|
77
|
+
process.stdout.write("\n");
|
|
78
|
+
}
|
|
79
|
+
if (isFirstToken) {
|
|
80
|
+
process.stdout.write("\n🤖: ");
|
|
81
|
+
isFirstToken = false;
|
|
82
|
+
}
|
|
83
|
+
process.stdout.write(content);
|
|
84
|
+
responseText += content;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "message_end":
|
|
88
|
+
if (!isFirstToken) process.stdout.write("\n");
|
|
89
|
+
break;
|
|
90
|
+
case "progress": break;
|
|
91
|
+
default: break;
|
|
92
|
+
}
|
|
93
|
+
if (isFirstToken && !hasThinking) process.stdout.write("\n🤖: (no response)\n");
|
|
94
|
+
return responseText;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { renderStreamToTerminal };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=stream-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-renderer.js","names":[],"sources":["../../../../../src/cli/commands/agent/stream-renderer.ts"],"sourcesContent":["/**\n * CLI stream renderer for agent events\n *\n * Consumes processDirectStreaming events and renders thinking,\n * tool calls, and response tokens to the terminal in real time.\n */\n\nimport type { AgentService } from '../../../agent/index.js';\nimport { prependEnvelopeTimestamp } from '../../../channels/envelope-timestamp.js';\n\n/** Styled labels for terminal output */\nconst STYLE = {\n dim: '\\x1b[2m',\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n yellow: '\\x1b[33m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n bold: '\\x1b[1m',\n magenta: '\\x1b[35m',\n};\n\nfunction formatToolArgs(args: unknown): string {\n if (!args || typeof args !== 'object') return '';\n const entries = Object.entries(args as Record<string, unknown>);\n if (entries.length === 0) return '';\n const parts = entries.map(([key, value]) => {\n const stringValue = typeof value === 'string' ? value : JSON.stringify(value);\n const truncated = stringValue.length > 80 ? stringValue.slice(0, 77) + '...' : stringValue;\n return `${key}=${truncated}`;\n });\n return parts.join(', ');\n}\n\n/**\n * Consume the processDirectStreaming async generator and render\n * thinking / tool-call / response tokens to stdout.\n *\n * Returns the final aggregated response text.\n */\nexport async function renderStreamToTerminal(\n agent: AgentService,\n message: string,\n sessionKey: string,\n): Promise<string> {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines and webchat gateway.\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const stamped = message.trimStart().startsWith('/')\n ? message\n : prependEnvelopeTimestamp(message);\n const stream = agent.processDirectStreaming(stamped, sessionKey);\n\n let responseText = '';\n let isFirstToken = true;\n let isInThinking = false;\n let hasThinking = false;\n\n for await (const event of stream) {\n switch (event.type) {\n case 'thinking': {\n const content = event.content as string | undefined;\n const status = event.status as string | undefined;\n\n if (status === 'started') {\n // Thinking phase started\n isInThinking = true;\n hasThinking = true;\n process.stdout.write(`\\n${STYLE.dim}${STYLE.magenta}💭 Thinking...${STYLE.reset}\\n`);\n break;\n }\n\n if (content) {\n if (!hasThinking) {\n isInThinking = true;\n hasThinking = true;\n process.stdout.write(`\\n${STYLE.dim}${STYLE.magenta}💭 Thinking...${STYLE.reset}\\n`);\n }\n process.stdout.write(`${STYLE.dim}${content}${STYLE.reset}`);\n }\n break;\n }\n\n case 'tool_start': {\n const toolName = event.toolName as string;\n const args = event.args;\n\n // Close thinking section if open\n if (isInThinking) {\n isInThinking = false;\n process.stdout.write('\\n');\n }\n\n const argsStr = formatToolArgs(args);\n process.stdout.write(\n `\\n${STYLE.cyan}🔧 Tool: ${STYLE.bold}${toolName}${STYLE.reset}` +\n (argsStr ? `${STYLE.dim} (${argsStr})${STYLE.reset}` : '') +\n '\\n',\n );\n break;\n }\n\n case 'tool_end': {\n const toolName = event.toolName as string;\n const isError = event.isError as boolean;\n\n if (isError) {\n process.stdout.write(`${STYLE.red} ✗ ${toolName} failed${STYLE.reset}\\n`);\n } else {\n process.stdout.write(`${STYLE.green} ✓ ${toolName} done${STYLE.reset}\\n`);\n }\n break;\n }\n\n case 'token': {\n const content = event.content as string;\n if (!content) break;\n\n // Close thinking section if open\n if (isInThinking) {\n isInThinking = false;\n process.stdout.write('\\n');\n }\n\n if (isFirstToken) {\n process.stdout.write('\\n🤖: ');\n isFirstToken = false;\n }\n process.stdout.write(content);\n responseText += content;\n break;\n }\n\n case 'message_end': {\n // Ensure newline after streaming response\n if (!isFirstToken) {\n process.stdout.write('\\n');\n }\n break;\n }\n\n case 'progress': {\n // Optionally show progress updates (e.g. \"Thinking...\")\n break;\n }\n\n default:\n break;\n }\n }\n\n // If no tokens were streamed, print empty line\n if (isFirstToken && !hasThinking) {\n process.stdout.write('\\n🤖: (no response)\\n');\n }\n\n return responseText;\n}\n"],"mappings":";;;AAWA,MAAM,QAAQ;CACZ,KAAK;CACL,OAAO;CACP,MAAM;CACN,QAAQ;CACR,OAAO;CACP,KAAK;CACL,MAAM;CACN,SAAS;CACV;AAED,SAAS,eAAe,MAAuB;AAC7C,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;CAC9C,MAAM,UAAU,OAAO,QAAQ,KAAgC;AAC/D,KAAI,QAAQ,WAAW,EAAG,QAAO;AAMjC,QALc,QAAQ,KAAK,CAAC,KAAK,WAAW;EAC1C,MAAM,cAAc,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,MAAM;AAE7E,SAAO,GAAG,IAAI,GADI,YAAY,SAAS,KAAK,YAAY,MAAM,GAAG,GAAG,GAAG,QAAQ;GAGrE,CAAC,KAAK,KAAK;;;;;;;;AASzB,eAAsB,uBACpB,OACA,SACA,YACiB;CAIjB,MAAM,UAAU,QAAQ,WAAW,CAAC,WAAW,IAAI,GAC/C,UACA,yBAAyB,QAAQ;CACrC,MAAM,SAAS,MAAM,uBAAuB,SAAS,WAAW;CAEhE,IAAI,eAAe;CACnB,IAAI,eAAe;CACnB,IAAI,eAAe;CACnB,IAAI,cAAc;AAElB,YAAW,MAAM,SAAS,OACxB,SAAQ,MAAM,MAAd;EACE,KAAK,YAAY;GACf,MAAM,UAAU,MAAM;AAGtB,OAFe,MAAM,WAEN,WAAW;AAExB,mBAAe;AACf,kBAAc;AACd,YAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM,QAAQ,gBAAgB,MAAM,MAAM,IAAI;AACpF;;AAGF,OAAI,SAAS;AACX,QAAI,CAAC,aAAa;AAChB,oBAAe;AACf,mBAAc;AACd,aAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM,QAAQ,gBAAgB,MAAM,MAAM,IAAI;;AAEtF,YAAQ,OAAO,MAAM,GAAG,MAAM,MAAM,UAAU,MAAM,QAAQ;;AAE9D;;EAGF,KAAK,cAAc;GACjB,MAAM,WAAW,MAAM;GACvB,MAAM,OAAO,MAAM;AAGnB,OAAI,cAAc;AAChB,mBAAe;AACf,YAAQ,OAAO,MAAM,KAAK;;GAG5B,MAAM,UAAU,eAAe,KAAK;AACpC,WAAQ,OAAO,MACb,KAAK,MAAM,KAAK,WAAW,MAAM,OAAO,WAAW,MAAM,WACxD,UAAU,GAAG,MAAM,IAAI,IAAI,QAAQ,GAAG,MAAM,UAAU,MACvD,KACD;AACD;;EAGF,KAAK,YAAY;GACf,MAAM,WAAW,MAAM;AAGvB,OAFgB,MAAM,QAGpB,SAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,OAAO,SAAS,SAAS,MAAM,MAAM,IAAI;OAE3E,SAAQ,OAAO,MAAM,GAAG,MAAM,MAAM,OAAO,SAAS,OAAO,MAAM,MAAM,IAAI;AAE7E;;EAGF,KAAK,SAAS;GACZ,MAAM,UAAU,MAAM;AACtB,OAAI,CAAC,QAAS;AAGd,OAAI,cAAc;AAChB,mBAAe;AACf,YAAQ,OAAO,MAAM,KAAK;;AAG5B,OAAI,cAAc;AAChB,YAAQ,OAAO,MAAM,SAAS;AAC9B,mBAAe;;AAEjB,WAAQ,OAAO,MAAM,QAAQ;AAC7B,mBAAgB;AAChB;;EAGF,KAAK;AAEH,OAAI,CAAC,aACH,SAAQ,OAAO,MAAM,KAAK;AAE5B;EAGF,KAAK,WAEH;EAGF,QACE;;AAKN,KAAI,gBAAgB,CAAC,YACnB,SAAQ,OAAO,MAAM,wBAAwB;AAG/C,QAAO"}
|
|
@@ -11,6 +11,7 @@ import "../../agent/index.js";
|
|
|
11
11
|
import "../../config/index.js";
|
|
12
12
|
import { formatExamples, register } from "../registry.js";
|
|
13
13
|
import { listSessions } from "./agent/sessions.js";
|
|
14
|
+
import { renderStreamToTerminal } from "./agent/stream-renderer.js";
|
|
14
15
|
import { startInteractiveChat } from "./agent/interactive.js";
|
|
15
16
|
import { getContextWithOpts } from "../index.js";
|
|
16
17
|
import { join } from "path";
|
|
@@ -111,8 +112,7 @@ function createAgentCommand(_ctx) {
|
|
|
111
112
|
if (options.message) {
|
|
112
113
|
const oneShotModel = options.model?.trim();
|
|
113
114
|
if (oneShotModel) await agent.switchModelForSession(sessionKey, oneShotModel);
|
|
114
|
-
|
|
115
|
-
console.log("\n🤖:", response);
|
|
115
|
+
await renderStreamToTerminal(agent, options.message, sessionKey);
|
|
116
116
|
if (oneShotModel) await agent.resetSessionModelToAgentDefault(sessionKey);
|
|
117
117
|
await shutdown();
|
|
118
118
|
} else if (options.interactive) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","names":[],"sources":["../../../../src/cli/commands/agent.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { AgentService } from '../../agent/index.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../index.js';\nimport { ExtensionLoader } from '../../extensions/index.js';\nimport { join } from 'path';\nimport { listSessions } from './agent/sessions.js';\nimport { startInteractiveChat } from './agent/interactive.js';\n\nconst log = createLogger('AgentCommand');\n\ninterface AgentCommandOptions {\n message?: string;\n model?: string;\n interactive?: boolean;\n session?: string;\n list?: boolean;\n}\n\nfunction createAgentCommand(_ctx: CLIContext): Command {\n const cmd = new Command('agent')\n .description('Chat with the AI agent')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc agent -m \"Hello\" # Single message',\n 'xopc agent --model demo/demo-chat-7b -m \"Hi\" # Override model for one shot',\n 'xopc agent -i # Interactive chat mode',\n 'xopc agent -i --session telegram:dm:123456 # Continue existing session',\n 'xopc agent --list # List available sessions',\n ])\n )\n .option('--model <id>', 'Model ref for this run (e.g. demo/demo-chat-7b, anthropic/claude-sonnet-4-5)')\n .option('-m, --message <text>', 'Single message to send')\n .option('-i, --interactive', 'Interactive chat mode')\n .option('-s, --session <key>', 'Continue an existing session (use --list to see available sessions)')\n .option('-l, --list', 'List available sessions and exit')\n .action(async (options: AgentCommandOptions) => {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const workspace = getWorkspacePath(config) || ctx.workspacePath;\n\n // Handle --list option\n if (options.list) {\n await listSessions();\n return;\n }\n\n const modelConfig = config.agents?.defaults?.model;\n const modelFromConfig = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n const modelId = (options.model?.trim() || modelFromConfig) as string | undefined;\n const bus = new MessageBus();\n\n if (ctx.isVerbose) {\n log.info({ model: modelId, workspace, session: options.session }, 'Starting agent');\n }\n\n // Validate session key if provided\n let sessionKey = options.session || 'cli:direct';\n if (options.session) {\n const { getSessionManager } = await import('../utils/session.js');\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(options.session);\n if (!session) {\n console.error(`❌ Session not found: ${options.session}`);\n console.log('Use --list to see available sessions.');\n process.exit(1);\n }\n console.log(`📂 Continuing session: ${options.session} (${session.messageCount} messages)\\n`);\n }\n\n // Initialize extension loader (manifest-first activation: env, channels, model, extensions.*)\n let extensionLoader: ExtensionLoader | null = null;\n try {\n extensionLoader = new ExtensionLoader({\n workspaceDir: workspace,\n extensionsDir: join(workspace, '.extensions'),\n });\n extensionLoader.setConfig(config as Parameters<ExtensionLoader['setConfig']>[0]);\n extensionLoader.setRuntimeContext({ bus });\n await extensionLoader.loadByActivationPlan();\n const n = extensionLoader.getRegistry().extensions.size;\n if (n > 0) {\n log.info({ count: n }, 'Extensions loaded');\n }\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage: em }, `CLI agent: failed to load extensions: ${em}`);\n }\n\n const { createCliReadlineClarifyRequestFn } = await import('../../agent/tools/cli-clarify.js');\n\n const agent = new AgentService(bus, {\n workspace,\n model: modelId,\n config,\n extensionRegistry: extensionLoader?.getRegistry(),\n gatewayClarify: {\n requestClarification: createCliReadlineClarifyRequestFn(),\n },\n });\n\n // Start agent service in background\n agent.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `CLI agent service exited: ${em}`);\n });\n\n // Start outbound message processor for CLI mode\n let running = true;\n const _outboundProcessor = (async () => {\n while (running) {\n try {\n const msg = await bus.consumeOutbound();\n console.log(`\\n📤 [${msg.channel}] ${msg.chat_id}: ${msg.content.slice(0, 100)}...`);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error({ err: error, errorMessage: em }, `CLI outbound processor failed: ${em}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n\n const shutdown = async () => {\n running = false;\n bus.shutdown();\n await agent.stop();\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n if (options.message) {\n const oneShotModel = options.model?.trim();\n if (oneShotModel) {\n await agent.switchModelForSession(sessionKey, oneShotModel);\n }\n
|
|
1
|
+
{"version":3,"file":"agent.js","names":[],"sources":["../../../../src/cli/commands/agent.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { AgentService } from '../../agent/index.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../index.js';\nimport { ExtensionLoader } from '../../extensions/index.js';\nimport { join } from 'path';\nimport { listSessions } from './agent/sessions.js';\nimport { startInteractiveChat } from './agent/interactive.js';\nimport { renderStreamToTerminal } from './agent/stream-renderer.js';\n\nconst log = createLogger('AgentCommand');\n\ninterface AgentCommandOptions {\n message?: string;\n model?: string;\n interactive?: boolean;\n session?: string;\n list?: boolean;\n}\n\nfunction createAgentCommand(_ctx: CLIContext): Command {\n const cmd = new Command('agent')\n .description('Chat with the AI agent')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc agent -m \"Hello\" # Single message',\n 'xopc agent --model demo/demo-chat-7b -m \"Hi\" # Override model for one shot',\n 'xopc agent -i # Interactive chat mode',\n 'xopc agent -i --session telegram:dm:123456 # Continue existing session',\n 'xopc agent --list # List available sessions',\n ])\n )\n .option('--model <id>', 'Model ref for this run (e.g. demo/demo-chat-7b, anthropic/claude-sonnet-4-5)')\n .option('-m, --message <text>', 'Single message to send')\n .option('-i, --interactive', 'Interactive chat mode')\n .option('-s, --session <key>', 'Continue an existing session (use --list to see available sessions)')\n .option('-l, --list', 'List available sessions and exit')\n .action(async (options: AgentCommandOptions) => {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const workspace = getWorkspacePath(config) || ctx.workspacePath;\n\n // Handle --list option\n if (options.list) {\n await listSessions();\n return;\n }\n\n const modelConfig = config.agents?.defaults?.model;\n const modelFromConfig = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n const modelId = (options.model?.trim() || modelFromConfig) as string | undefined;\n const bus = new MessageBus();\n\n if (ctx.isVerbose) {\n log.info({ model: modelId, workspace, session: options.session }, 'Starting agent');\n }\n\n // Validate session key if provided\n let sessionKey = options.session || 'cli:direct';\n if (options.session) {\n const { getSessionManager } = await import('../utils/session.js');\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(options.session);\n if (!session) {\n console.error(`❌ Session not found: ${options.session}`);\n console.log('Use --list to see available sessions.');\n process.exit(1);\n }\n console.log(`📂 Continuing session: ${options.session} (${session.messageCount} messages)\\n`);\n }\n\n // Initialize extension loader (manifest-first activation: env, channels, model, extensions.*)\n let extensionLoader: ExtensionLoader | null = null;\n try {\n extensionLoader = new ExtensionLoader({\n workspaceDir: workspace,\n extensionsDir: join(workspace, '.extensions'),\n });\n extensionLoader.setConfig(config as Parameters<ExtensionLoader['setConfig']>[0]);\n extensionLoader.setRuntimeContext({ bus });\n await extensionLoader.loadByActivationPlan();\n const n = extensionLoader.getRegistry().extensions.size;\n if (n > 0) {\n log.info({ count: n }, 'Extensions loaded');\n }\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage: em }, `CLI agent: failed to load extensions: ${em}`);\n }\n\n const { createCliReadlineClarifyRequestFn } = await import('../../agent/tools/cli-clarify.js');\n\n const agent = new AgentService(bus, {\n workspace,\n model: modelId,\n config,\n extensionRegistry: extensionLoader?.getRegistry(),\n gatewayClarify: {\n requestClarification: createCliReadlineClarifyRequestFn(),\n },\n });\n\n // Start agent service in background\n agent.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `CLI agent service exited: ${em}`);\n });\n\n // Start outbound message processor for CLI mode\n let running = true;\n const _outboundProcessor = (async () => {\n while (running) {\n try {\n const msg = await bus.consumeOutbound();\n console.log(`\\n📤 [${msg.channel}] ${msg.chat_id}: ${msg.content.slice(0, 100)}...`);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error({ err: error, errorMessage: em }, `CLI outbound processor failed: ${em}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n\n const shutdown = async () => {\n running = false;\n bus.shutdown();\n await agent.stop();\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n if (options.message) {\n const oneShotModel = options.model?.trim();\n if (oneShotModel) {\n await agent.switchModelForSession(sessionKey, oneShotModel);\n }\n await renderStreamToTerminal(agent, options.message, sessionKey);\n if (oneShotModel) {\n await agent.resetSessionModelToAgentDefault(sessionKey);\n }\n await shutdown();\n } else if (options.interactive) {\n const interactiveModel = options.model?.trim();\n if (interactiveModel) {\n await agent.switchModelForSession(sessionKey, interactiveModel);\n }\n await startInteractiveChat(agent, {\n workspace,\n sessionKey,\n continuingSession: !!options.session,\n });\n } else {\n await shutdown();\n cmd.help();\n }\n });\n\n return cmd;\n}\n\nregister({\n id: 'agent',\n name: 'agent',\n description: 'Chat with the AI agent',\n factory: createAgentCommand,\n metadata: {\n category: 'runtime',\n examples: [\n 'xopc agent -m \"Hello\"',\n 'xopc agent --model demo/demo-chat-7b -m \"Hello demo!\"',\n 'xopc agent -i',\n ],\n },\n});\n"],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ConfigSchema, init_schema } from "../../config/schema.js";
|
|
1
2
|
import { saveConfig } from "../../config/loader.js";
|
|
3
|
+
import { isWeixinOnboardConfigured } from "../../../extensions/weixin/src/adapters/onboard-cli.js";
|
|
2
4
|
import "../../config/index.js";
|
|
3
5
|
import { formatExamples, register } from "../registry.js";
|
|
4
6
|
import { seedMainAgentBootstrap } from "../../agent/context/workspace-seed.js";
|
|
@@ -9,9 +11,10 @@ import { GatewayLockError, acquireGatewayLock } from "../../gateway/lock.js";
|
|
|
9
11
|
import { getChannelConfigurators } from "./onboard/channels/registry.js";
|
|
10
12
|
import { setupChannels } from "./onboard/channels/index.js";
|
|
11
13
|
import { join } from "path";
|
|
12
|
-
import {
|
|
14
|
+
import { select } from "@inquirer/prompts";
|
|
13
15
|
import { Command } from "commander";
|
|
14
16
|
//#region src/cli/commands/onboard.ts
|
|
17
|
+
init_schema();
|
|
15
18
|
function isInteractive() {
|
|
16
19
|
return process.stdin.isTTY && process.stdout.isTTY;
|
|
17
20
|
}
|
|
@@ -23,11 +26,11 @@ async function setupNonInteractive(_configPath, existingConfig) {
|
|
|
23
26
|
return existingConfig;
|
|
24
27
|
}
|
|
25
28
|
function createOnboardCommand(ctx) {
|
|
26
|
-
return new Command("onboard").description("Interactive setup wizard for xopc").addHelpText("after", formatExamples([
|
|
29
|
+
return new Command("onboard").description("Interactive setup wizard for xopc (gateway uses schema defaults)").addHelpText("after", formatExamples([
|
|
27
30
|
"xopc onboard # Full interactive setup",
|
|
28
31
|
"xopc onboard --model # Configure LLM model only",
|
|
29
|
-
"xopc onboard --channels # Configure channels
|
|
30
|
-
"xopc onboard --gateway #
|
|
32
|
+
"xopc onboard --channels # Configure channels (incl. Weixin QR)",
|
|
33
|
+
"xopc onboard --gateway # Apply default gateway settings (quiet)"
|
|
31
34
|
])).option("--model", "Configure LLM provider and model").option("--channels", "Configure messaging channels").option("--gateway", "Configure gateway WebUI").option("--all", "Configure everything (default)").action(async (options) => {
|
|
32
35
|
try {
|
|
33
36
|
await runOnboard(options, ctx);
|
|
@@ -54,6 +57,8 @@ async function runOnboard(options, ctx) {
|
|
|
54
57
|
const doChannels = options.channels || options.all || !options.model && !options.gateway;
|
|
55
58
|
const doGateway = options.gateway || options.all || !options.model && !options.channels;
|
|
56
59
|
const runFullWizard = !options.model && !options.channels && !options.gateway;
|
|
60
|
+
/** Any setup step besides the unified launch prompt ran in interactive flow. */
|
|
61
|
+
const didConfigurableSteps = doModel || doChannels || doGateway;
|
|
57
62
|
if (!isInteractive()) {
|
|
58
63
|
if (doModel) config = await setupNonInteractive(configPath, config);
|
|
59
64
|
if (doChannels) {
|
|
@@ -83,8 +88,7 @@ async function runOnboard(options, ctx) {
|
|
|
83
88
|
const port = config?.gateway?.port ?? 18790;
|
|
84
89
|
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
85
90
|
const gwToken = gatewayConfigured ? gatewayAuth.token : void 0;
|
|
86
|
-
|
|
87
|
-
if (showGatewaySummary && gwToken) {
|
|
91
|
+
if (Boolean(gatewayConfigured && gwToken && (doGateway || runFullWizard)) && gwToken) {
|
|
88
92
|
const webuiUrl = `http://${displayHost}:${port}?token=${gwToken}`;
|
|
89
93
|
console.log("🌐 Web console (browser) — start here");
|
|
90
94
|
console.log(` Open: http://${displayHost}:${port}`);
|
|
@@ -96,8 +100,8 @@ async function runOnboard(options, ctx) {
|
|
|
96
100
|
if (runFullWizard) {
|
|
97
101
|
console.log("🚀 Next steps:");
|
|
98
102
|
if (gatewayConfigured) {
|
|
99
|
-
console.log(" 1.
|
|
100
|
-
console.log(" 2. Or chat
|
|
103
|
+
console.log(" 1. Choose how to launch below (gateway or terminal UI)");
|
|
104
|
+
console.log(" 2. Or chat with: xopc agent -i");
|
|
101
105
|
console.log(" 3. Optional: read BOOTSTRAP.md in your workspace for workspace tips");
|
|
102
106
|
} else {
|
|
103
107
|
console.log(" 1. Chat in the terminal: xopc agent -i");
|
|
@@ -119,12 +123,12 @@ async function runOnboard(options, ctx) {
|
|
|
119
123
|
console.log(" Config:", configPath);
|
|
120
124
|
console.log(" Workspace:", workspacePath);
|
|
121
125
|
if (runFullWizard) console.log(" Bootstrap:", join(workspacePath, "BOOTSTRAP.md"));
|
|
122
|
-
if (
|
|
126
|
+
if (isInteractive() && didConfigurableSteps) await promptLaunchAfterOnboard(config, ctx, { doChannels });
|
|
123
127
|
process.exit(0);
|
|
124
128
|
}
|
|
125
|
-
async function
|
|
126
|
-
const host = config
|
|
127
|
-
const port = config
|
|
129
|
+
async function startGatewayInBackground(config, ctx) {
|
|
130
|
+
const host = config.gateway?.host ?? "127.0.0.1";
|
|
131
|
+
const port = config.gateway?.port ?? 18790;
|
|
128
132
|
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
129
133
|
let isRunning = false;
|
|
130
134
|
try {
|
|
@@ -141,10 +145,7 @@ async function startGatewayNow(config, ctx) {
|
|
|
141
145
|
console.log("");
|
|
142
146
|
console.log("📝 To apply the new configuration, restart gateway:");
|
|
143
147
|
console.log(" xopc gateway restart");
|
|
144
|
-
} else
|
|
145
|
-
message: "Start Gateway WebUI now (background mode)?",
|
|
146
|
-
default: true
|
|
147
|
-
})) {
|
|
148
|
+
} else {
|
|
148
149
|
console.log("\n🚀 Starting Gateway WebUI in background...");
|
|
149
150
|
console.log("");
|
|
150
151
|
const { spawn } = await import("child_process");
|
|
@@ -169,97 +170,80 @@ async function startGatewayNow(config, ctx) {
|
|
|
169
170
|
console.log("✅ Gateway started in background");
|
|
170
171
|
console.log(` PID: ${child.pid}`);
|
|
171
172
|
console.log(` URL: http://${displayHost}:${port}`);
|
|
172
|
-
const token = config
|
|
173
|
+
const token = config.gateway?.auth?.token;
|
|
173
174
|
if (token) console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);
|
|
174
175
|
} else {
|
|
175
176
|
console.log("⚠️ Failed to start gateway automatically.");
|
|
176
|
-
console.log("
|
|
177
|
-
console.log(
|
|
177
|
+
console.log(" Start manually with:");
|
|
178
|
+
console.log(" xopc gateway --background");
|
|
178
179
|
}
|
|
179
|
-
} else {
|
|
180
|
-
console.log("\n⏭️ Skipping gateway startup.");
|
|
181
|
-
console.log(" You can start it later with:");
|
|
182
|
-
console.log(` xopc gateway --background`);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
console.log("\n🚀 Gateway is configured but not running.");
|
|
186
|
-
console.log("");
|
|
187
|
-
console.log("📝 To start the gateway in background:");
|
|
188
|
-
console.log(` xopc gateway --background`);
|
|
189
|
-
console.log("");
|
|
190
|
-
console.log("📝 To start in foreground (development mode):");
|
|
191
|
-
console.log(` pnpm run dev -- gateway --host ${host} --port ${port}`);
|
|
192
180
|
}
|
|
193
181
|
console.log("");
|
|
194
|
-
console.log("📚
|
|
182
|
+
console.log("📚 Useful commands:");
|
|
195
183
|
console.log(" xopc gateway status # Check gateway status");
|
|
196
184
|
console.log(" xopc gateway stop # Stop gateway");
|
|
197
185
|
console.log(" xopc gateway restart # Restart gateway");
|
|
198
186
|
console.log(" xopc gateway logs # View logs");
|
|
199
187
|
}
|
|
200
|
-
async function
|
|
201
|
-
console.log(
|
|
202
|
-
|
|
203
|
-
message: "
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
default: "18790",
|
|
223
|
-
validate: (input) => {
|
|
224
|
-
const port = parseInt(input, 10);
|
|
225
|
-
return !isNaN(port) && port > 0 && port < 65536 || "Invalid port number";
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
const port = parseInt(portInput, 10);
|
|
229
|
-
const authMode = await select({
|
|
230
|
-
message: "Authentication:",
|
|
231
|
-
choices: [{
|
|
232
|
-
name: "Token (recommended)",
|
|
233
|
-
value: "token"
|
|
234
|
-
}, {
|
|
235
|
-
name: "None (local only)",
|
|
236
|
-
value: "none"
|
|
237
|
-
}],
|
|
238
|
-
default: "token"
|
|
188
|
+
async function promptLaunchAfterOnboard(config, ctx, flags) {
|
|
189
|
+
console.log("");
|
|
190
|
+
const choice = await select({
|
|
191
|
+
message: "How do you want to launch xopc now?",
|
|
192
|
+
choices: [
|
|
193
|
+
{
|
|
194
|
+
value: "tui",
|
|
195
|
+
name: "Terminal UI (embedded)",
|
|
196
|
+
description: "xopc tui --local — no gateway process required"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
value: "gateway",
|
|
200
|
+
name: "Gateway WebUI (background)",
|
|
201
|
+
description: "Start the HTTP gateway for the browser console"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
value: "none",
|
|
205
|
+
name: "Exit — I will start manually",
|
|
206
|
+
description: "Finish setup without starting a runtime"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
default: "tui"
|
|
239
210
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (existingToken) {
|
|
244
|
-
if (await confirm({
|
|
245
|
-
message: "Use existing token?",
|
|
246
|
-
default: true
|
|
247
|
-
})) token = existingToken;
|
|
248
|
-
}
|
|
249
|
-
if (!token) {
|
|
250
|
-
token = (await import("crypto")).randomBytes(24).toString("hex");
|
|
251
|
-
console.log(`\n🔑 Generated token: ${token.slice(0, 8)}...${token.slice(-8)}`);
|
|
252
|
-
}
|
|
211
|
+
if (choice === "gateway") {
|
|
212
|
+
await startGatewayInBackground(config, ctx);
|
|
213
|
+
return;
|
|
253
214
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
215
|
+
if (choice === "tui") {
|
|
216
|
+
if (flags.doChannels && !isWeixinOnboardConfigured(config)) console.log(colors.gray("\n💡 Weixin is not logged in yet. When ready run: xopc channels login --channel weixin\n"));
|
|
217
|
+
const { runTui } = await import("../../tui/tui.js");
|
|
218
|
+
await runTui({ local: true });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
console.log("\n⏭️ You can start later:");
|
|
222
|
+
console.log(" xopc gateway --background");
|
|
223
|
+
console.log(" xopc tui --local");
|
|
224
|
+
}
|
|
225
|
+
async function setupGateway(config) {
|
|
226
|
+
console.log(colors.cyan("\n🌐 Gateway WebUI\n"));
|
|
227
|
+
console.log(colors.gray("Applying defaults from config schema (127.0.0.1:18790, token auth; token generated if missing).\n"));
|
|
228
|
+
const gw = config.gateway ?? {};
|
|
229
|
+
const { randomBytes } = await import("node:crypto");
|
|
230
|
+
const authMode = gw.auth?.mode === "none" ? "none" : "token";
|
|
231
|
+
const token = authMode === "token" ? typeof gw.auth?.token === "string" && gw.auth.token.length > 0 ? gw.auth.token : randomBytes(24).toString("hex") : void 0;
|
|
232
|
+
const merged = {
|
|
233
|
+
...config,
|
|
234
|
+
gateway: {
|
|
235
|
+
...gw,
|
|
236
|
+
host: gw.host ?? "127.0.0.1",
|
|
237
|
+
port: gw.port ?? 18790,
|
|
238
|
+
auth: authMode === "none" ? { mode: "none" } : {
|
|
239
|
+
mode: "token",
|
|
240
|
+
token
|
|
241
|
+
}
|
|
242
|
+
}
|
|
260
243
|
};
|
|
261
|
-
|
|
262
|
-
|
|
244
|
+
const parsed = ConfigSchema.parse(merged);
|
|
245
|
+
console.log("✅ Gateway defaults applied.\n");
|
|
246
|
+
return parsed;
|
|
263
247
|
}
|
|
264
248
|
register({
|
|
265
249
|
id: "onboard",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.js","names":["runModelSetup","runChannelOnboard"],"sources":["../../../../src/cli/commands/onboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'path';\nimport { input, select, confirm } from '@inquirer/prompts';\nimport { saveConfig } from '../../config/index.js';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport type { Config } from '../../config/schema.js';\nimport { setupModel as runModelSetup } from './onboard/model.js';\nimport { colors } from '../utils/colors.js';\nimport { acquireGatewayLock, GatewayLockError } from '../../gateway/lock.js';\nimport { setupChannels as runChannelOnboard, getChannelConfigurators } from './onboard/channels/index.js';\nimport { seedMainAgentBootstrap } from '../../agent/context/workspace-seed.js';\nimport { initWorkspace } from '../utils/init-workspace.js';\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\nasync function setupNonInteractive(_configPath: string, existingConfig: Config): Promise<Config> {\n console.log('\\n🤖 AI Model Configuration (Non-Interactive Mode)\\n');\n console.log('Current config:', JSON.stringify(existingConfig.agents?.defaults?.model, null, 2));\n console.log('\\n💡 To configure in interactive mode, run: xopc onboard');\n console.log('💡 Or set up manually in:', _configPath);\n return existingConfig;\n}\n\nfunction createOnboardCommand(ctx: CLIContext): Command {\n const cmd = new Command('onboard')\n .description('Interactive setup wizard for xopc')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc onboard # Full interactive setup',\n 'xopc onboard --model # Configure LLM model only',\n 'xopc onboard --channels # Configure channels only',\n 'xopc onboard --gateway # Configure gateway only',\n ])\n )\n .option('--model', 'Configure LLM provider and model')\n .option('--channels', 'Configure messaging channels')\n .option('--gateway', 'Configure gateway WebUI')\n .option('--all', 'Configure everything (default)')\n .action(async (options) => {\n try {\n await runOnboard(options, ctx);\n } catch (error: unknown) {\n const err = error as { name?: string; code?: string };\n if (err?.name === 'ExitPromptError' || err?.code === 'EXIT_PROMPT') {\n console.log('\\n\\n👋 Setup cancelled.');\n process.exit(0);\n }\n throw error;\n }\n });\n\n return cmd;\n}\n\nasync function runOnboard(\n options: { model?: boolean; channels?: boolean; gateway?: boolean; all?: boolean },\n ctx: CLIContext\n): Promise<void> {\n console.log(colors.cyan('\\n🚀 Welcome to xopc setup!\\n'));\n console.log('═'.repeat(50));\n\n const workspacePath = ctx.workspacePath;\n const configPath = ctx.configPath;\n\n const initResult = await initWorkspace({ configPath, workspacePath });\n let config = initResult.config;\n\n // Determine what to configure based on options\n const doModel = options.model || options.all || (!options.channels && !options.gateway);\n const doChannels = options.channels || options.all || (!options.model && !options.gateway);\n const doGateway = options.gateway || options.all || (!options.model && !options.channels);\n const runFullWizard = !options.model && !options.channels && !options.gateway;\n\n if (!isInteractive()) {\n // Non-interactive mode\n if (doModel) {\n config = await setupNonInteractive(configPath, config);\n }\n if (doChannels) {\n console.log('\\n💬 Channels Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure channels, edit the config file manually.');\n }\n if (doGateway) {\n console.log('\\n🌐 Gateway Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure gateway, edit the config file manually.');\n }\n } else {\n // Interactive mode\n if (doModel) {\n config = await runModelSetup(config, ctx);\n }\n\n if (doChannels) {\n const channelIds = getChannelConfigurators().map(c => c.id);\n console.log(colors.gray(`\\nChannel onboarding: ${channelIds.join(', ')}\\n`));\n config = await runChannelOnboard(config);\n }\n\n if (doGateway) {\n config = await setupGateway(config);\n }\n }\n\n // Save config once at the end\n await saveConfig(config as Config, configPath);\n\n seedMainAgentBootstrap(config as Config);\n\n console.log('\\n' + '═'.repeat(50));\n console.log('\\n🎉 Setup Complete!\\n');\n\n const gatewayAuth = (config as any)?.gateway?.auth;\n const gatewayConfigured =\n gatewayAuth?.mode === 'token' &&\n typeof gatewayAuth?.token === 'string' &&\n gatewayAuth.token.length > 0;\n const host = (config as any)?.gateway?.host || '0.0.0.0';\n const port = (config as any)?.gateway?.port ?? 18790;\n const displayHost = host === '0.0.0.0' ? 'localhost' : host;\n const gwToken = gatewayConfigured ? (gatewayAuth.token as string) : undefined;\n\n const showGatewaySummary = Boolean(gatewayConfigured && gwToken && (doGateway || runFullWizard));\n\n if (showGatewaySummary && gwToken) {\n const webuiUrl = `http://${displayHost}:${port}?token=${gwToken}`;\n console.log('🌐 Web console (browser) — start here');\n console.log(` Open: http://${displayHost}:${port}`);\n console.log(` Token: ${gwToken.slice(0, 8)}...${gwToken.slice(-8)}`);\n console.log(' Bookmark link (token is saved in the browser when you open it):');\n console.log(` ${webuiUrl}`);\n console.log('');\n }\n\n if (runFullWizard) {\n console.log('🚀 Next steps:');\n if (gatewayConfigured) {\n console.log(' 1. Open the Web console in your browser (URL above; start the gateway below if needed)');\n console.log(' 2. Or chat in the terminal: xopc agent -i');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace for workspace tips');\n } else {\n console.log(' 1. Chat in the terminal: xopc agent -i');\n console.log(' 2. Optional: add the Web console: xopc onboard --gateway');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace');\n }\n console.log('');\n } else if (doGateway && gatewayConfigured) {\n console.log('🚀 Next step:');\n console.log(' Start the gateway if it is not running, then open the Web console URL above.');\n console.log('');\n }\n\n console.log('📝 Usage:');\n console.log(' xopc agent -m \"Hello\" # Chat with AI');\n console.log(' xopc agent -i # Interactive mode');\n console.log(' xopc models list # List models');\n console.log(' xopc auth list # View authentication');\n\n console.log('\\n📁 Files:');\n console.log(' Config:', configPath);\n console.log(' Workspace:', workspacePath);\n if (runFullWizard) {\n console.log(' Bootstrap:', join(workspacePath, 'BOOTSTRAP.md'));\n }\n\n if (showGatewaySummary) {\n await startGatewayNow(config as Config, ctx);\n }\n\n process.exit(0);\n}\n\nasync function startGatewayNow(config: Config, ctx: CLIContext): Promise<void> {\n const host = (config as any)?.gateway?.host || '0.0.0.0';\n const port = (config as any)?.gateway?.port || 18790;\n const displayHost = host === '0.0.0.0' ? 'localhost' : host;\n\n let isRunning = false;\n try {\n const lock = await acquireGatewayLock(ctx.configPath, { timeoutMs: 100, port });\n await lock.release();\n } catch (err) {\n if (err instanceof GatewayLockError) {\n isRunning = true;\n }\n }\n\n if (isRunning) {\n console.log('\\n🌐 Gateway is already running!');\n console.log(` URL: http://${displayHost}:${port}`);\n console.log('');\n console.log('📝 To apply the new configuration, restart gateway:');\n console.log(' xopc gateway restart');\n } else {\n if (isInteractive()) {\n const shouldStart = await confirm({\n message: 'Start Gateway WebUI now (background mode)?',\n default: true,\n });\n\n if (shouldStart) {\n console.log('\\n🚀 Starting Gateway WebUI in background...');\n console.log('');\n\n const { spawn } = await import('child_process');\n const args = [\n ...process.execArgv,\n ...process.argv.slice(1).filter(arg => !arg.includes('onboard') && arg !== '--quick'),\n 'gateway',\n '--background',\n '--host', host,\n '--port', String(port),\n ];\n\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n\n child.unref();\n\n await new Promise(resolve => setTimeout(resolve, 500));\n\n if (child.pid && !child.killed) {\n console.log('✅ Gateway started in background');\n console.log(` PID: ${child.pid}`);\n console.log(` URL: http://${displayHost}:${port}`);\n const token = (config as any)?.gateway?.auth?.token;\n if (token) {\n console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);\n }\n } else {\n console.log('⚠️ Failed to start gateway automatically.');\n console.log(' You can start it manually with:');\n console.log(` xopc gateway --background`);\n }\n } else {\n console.log('\\n⏭️ Skipping gateway startup.');\n console.log(' You can start it later with:');\n console.log(` xopc gateway --background`);\n }\n } else {\n console.log('\\n🚀 Gateway is configured but not running.');\n console.log('');\n console.log('📝 To start the gateway in background:');\n console.log(` xopc gateway --background`);\n console.log('');\n console.log('📝 To start in foreground (development mode):');\n console.log(` pnpm run dev -- gateway --host ${host} --port ${port}`);\n }\n }\n\n console.log('');\n console.log('📚 Other useful commands:');\n console.log(' xopc gateway status # Check gateway status');\n console.log(' xopc gateway stop # Stop gateway');\n console.log(' xopc gateway restart # Restart gateway');\n console.log(' xopc gateway logs # View logs');\n}\n\nasync function setupGateway(config: Config): Promise<Config> {\n console.log(colors.cyan('\\n🌐 Step 4: Gateway WebUI\\n'));\n\n const enableGateway = await confirm({\n message: 'Enable Gateway WebUI?',\n default: true,\n });\n\n if (!enableGateway) {\n console.log('ℹ️ Gateway skipped.');\n return config;\n }\n\n const host = await select({\n message: 'Bind address:',\n choices: [\n { name: 'Localhost only (127.0.0.1)', value: '127.0.0.1' },\n { name: 'All interfaces (0.0.0.0)', value: '0.0.0.0' },\n ],\n default: '0.0.0.0',\n });\n\n const portInput = await input({\n message: 'Port:',\n default: '18790',\n validate: (input) => {\n const port = parseInt(input, 10);\n return !isNaN(port) && port > 0 && port < 65536 || 'Invalid port number';\n },\n });\n\n const port = parseInt(portInput, 10);\n\n const authMode = await select({\n message: 'Authentication:',\n choices: [\n { name: 'Token (recommended)', value: 'token' },\n { name: 'None (local only)', value: 'none' },\n ],\n default: 'token',\n });\n\n let token: string | undefined;\n if (authMode === 'token') {\n const existingToken = (config as any)?.gateway?.auth?.token;\n if (existingToken) {\n const reuse = await confirm({\n message: 'Use existing token?',\n default: true,\n });\n if (reuse) {\n token = existingToken;\n }\n }\n\n if (!token) {\n const crypto = await import('crypto');\n token = crypto.randomBytes(24).toString('hex');\n console.log(`\\n🔑 Generated token: ${token.slice(0, 8)}...${token.slice(-8)}`);\n }\n }\n\n (config as any).gateway = (config as any).gateway || {};\n (config as any).gateway.host = host;\n (config as any).gateway.port = port;\n (config as any).gateway.auth = {\n mode: authMode,\n ...(token ? { token } : {}),\n };\n\n console.log('✅ Gateway configuration saved.\\n');\n\n return config;\n}\n\nregister({\n id: 'onboard',\n name: 'onboard',\n description: 'Interactive setup wizard',\n factory: createOnboardCommand,\n metadata: {\n category: 'setup',\n examples: [\n 'xopc onboard',\n 'xopc onboard --model',\n 'xopc onboard --channels',\n ],\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"onboard.js","names":["runModelSetup","runChannelOnboard"],"sources":["../../../../src/cli/commands/onboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'path';\nimport { select } from '@inquirer/prompts';\nimport { saveConfig } from '../../config/index.js';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport type { Config } from '../../config/schema.js';\nimport { setupModel as runModelSetup } from './onboard/model.js';\nimport { colors } from '../utils/colors.js';\nimport { acquireGatewayLock, GatewayLockError } from '../../gateway/lock.js';\nimport { setupChannels as runChannelOnboard, getChannelConfigurators } from './onboard/channels/index.js';\nimport { seedMainAgentBootstrap } from '../../agent/context/workspace-seed.js';\nimport { initWorkspace } from '../utils/init-workspace.js';\nimport { ConfigSchema } from '../../config/schema.js';\nimport { isWeixinOnboardConfigured } from '../../../extensions/weixin/src/adapters/onboard-cli.js';\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\nasync function setupNonInteractive(_configPath: string, existingConfig: Config): Promise<Config> {\n console.log('\\n🤖 AI Model Configuration (Non-Interactive Mode)\\n');\n console.log('Current config:', JSON.stringify(existingConfig.agents?.defaults?.model, null, 2));\n console.log('\\n💡 To configure in interactive mode, run: xopc onboard');\n console.log('💡 Or set up manually in:', _configPath);\n return existingConfig;\n}\n\nfunction createOnboardCommand(ctx: CLIContext): Command {\n const cmd = new Command('onboard')\n .description('Interactive setup wizard for xopc (gateway uses schema defaults)')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc onboard # Full interactive setup',\n 'xopc onboard --model # Configure LLM model only',\n 'xopc onboard --channels # Configure channels (incl. Weixin QR)',\n 'xopc onboard --gateway # Apply default gateway settings (quiet)',\n ])\n )\n .option('--model', 'Configure LLM provider and model')\n .option('--channels', 'Configure messaging channels')\n .option('--gateway', 'Configure gateway WebUI')\n .option('--all', 'Configure everything (default)')\n .action(async (options) => {\n try {\n await runOnboard(options, ctx);\n } catch (error: unknown) {\n const err = error as { name?: string; code?: string };\n if (err?.name === 'ExitPromptError' || err?.code === 'EXIT_PROMPT') {\n console.log('\\n\\n👋 Setup cancelled.');\n process.exit(0);\n }\n throw error;\n }\n });\n\n return cmd;\n}\n\ntype OnboardOptions = {\n model?: boolean;\n channels?: boolean;\n gateway?: boolean;\n all?: boolean;\n};\n\nasync function runOnboard(\n options: OnboardOptions,\n ctx: CLIContext\n): Promise<void> {\n console.log(colors.cyan('\\n🚀 Welcome to xopc setup!\\n'));\n console.log('═'.repeat(50));\n\n const workspacePath = ctx.workspacePath;\n const configPath = ctx.configPath;\n\n const initResult = await initWorkspace({ configPath, workspacePath });\n let config = initResult.config;\n\n // Determine what to configure based on options\n const doModel = options.model || options.all || (!options.channels && !options.gateway);\n const doChannels = options.channels || options.all || (!options.model && !options.gateway);\n const doGateway = options.gateway || options.all || (!options.model && !options.channels);\n const runFullWizard = !options.model && !options.channels && !options.gateway;\n /** Any setup step besides the unified launch prompt ran in interactive flow. */\n const didConfigurableSteps = doModel || doChannels || doGateway;\n\n if (!isInteractive()) {\n // Non-interactive mode\n if (doModel) {\n config = await setupNonInteractive(configPath, config);\n }\n if (doChannels) {\n console.log('\\n💬 Channels Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure channels, edit the config file manually.');\n }\n if (doGateway) {\n console.log('\\n🌐 Gateway Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure gateway, edit the config file manually.');\n }\n } else {\n // Interactive mode\n if (doModel) {\n config = await runModelSetup(config, ctx);\n }\n\n if (doChannels) {\n const channelIds = getChannelConfigurators().map(c => c.id);\n console.log(colors.gray(`\\nChannel onboarding: ${channelIds.join(', ')}\\n`));\n config = await runChannelOnboard(config);\n }\n\n if (doGateway) {\n config = await setupGateway(config);\n }\n }\n\n // Save config once at the end\n await saveConfig(config as Config, configPath);\n\n seedMainAgentBootstrap(config as Config);\n\n console.log('\\n' + '═'.repeat(50));\n console.log('\\n🎉 Setup Complete!\\n');\n\n const gatewayAuth = (config as any)?.gateway?.auth;\n const gatewayConfigured =\n gatewayAuth?.mode === 'token' &&\n typeof gatewayAuth?.token === 'string' &&\n gatewayAuth.token.length > 0;\n const host = (config as any)?.gateway?.host || '0.0.0.0';\n const port = (config as any)?.gateway?.port ?? 18790;\n const displayHost = host === '0.0.0.0' ? 'localhost' : host;\n const gwToken = gatewayConfigured ? (gatewayAuth.token as string) : undefined;\n\n const showGatewaySummary = Boolean(gatewayConfigured && gwToken && (doGateway || runFullWizard));\n\n if (showGatewaySummary && gwToken) {\n const webuiUrl = `http://${displayHost}:${port}?token=${gwToken}`;\n console.log('🌐 Web console (browser) — start here');\n console.log(` Open: http://${displayHost}:${port}`);\n console.log(` Token: ${gwToken.slice(0, 8)}...${gwToken.slice(-8)}`);\n console.log(' Bookmark link (token is saved in the browser when you open it):');\n console.log(` ${webuiUrl}`);\n console.log('');\n }\n\n if (runFullWizard) {\n console.log('🚀 Next steps:');\n if (gatewayConfigured) {\n console.log(' 1. Choose how to launch below (gateway or terminal UI)');\n console.log(' 2. Or chat with: xopc agent -i');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace for workspace tips');\n } else {\n console.log(' 1. Chat in the terminal: xopc agent -i');\n console.log(' 2. Optional: add the Web console: xopc onboard --gateway');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace');\n }\n console.log('');\n } else if (doGateway && gatewayConfigured) {\n console.log('🚀 Next step:');\n console.log(' Start the gateway if it is not running, then open the Web console URL above.');\n console.log('');\n }\n\n console.log('📝 Usage:');\n console.log(' xopc agent -m \"Hello\" # Chat with AI');\n console.log(' xopc agent -i # Interactive mode');\n console.log(' xopc models list # List models');\n console.log(' xopc auth list # View authentication');\n\n console.log('\\n📁 Files:');\n console.log(' Config:', configPath);\n console.log(' Workspace:', workspacePath);\n if (runFullWizard) {\n console.log(' Bootstrap:', join(workspacePath, 'BOOTSTRAP.md'));\n }\n\n if (isInteractive() && didConfigurableSteps) {\n await promptLaunchAfterOnboard(config as Config, ctx, { doChannels });\n }\n\n process.exit(0);\n}\n\nasync function startGatewayInBackground(config: Config, ctx: CLIContext): Promise<void> {\n const host = (config as { gateway?: { host?: string } }).gateway?.host ?? '127.0.0.1';\n const port = (config as { gateway?: { port?: number } }).gateway?.port ?? 18790;\n const displayHost = host === '0.0.0.0' ? 'localhost' : host;\n\n let isRunning = false;\n try {\n const lock = await acquireGatewayLock(ctx.configPath, { timeoutMs: 100, port });\n await lock.release();\n } catch (err) {\n if (err instanceof GatewayLockError) {\n isRunning = true;\n }\n }\n\n if (isRunning) {\n console.log('\\n🌐 Gateway is already running!');\n console.log(` URL: http://${displayHost}:${port}`);\n console.log('');\n console.log('📝 To apply the new configuration, restart gateway:');\n console.log(' xopc gateway restart');\n } else {\n console.log('\\n🚀 Starting Gateway WebUI in background...');\n console.log('');\n\n const { spawn } = await import('child_process');\n const args = [\n ...process.execArgv,\n ...process.argv.slice(1).filter((arg) => !arg.includes('onboard') && arg !== '--quick'),\n 'gateway',\n '--background',\n '--host',\n host,\n '--port',\n String(port),\n ];\n\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n\n child.unref();\n\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n if (child.pid && !child.killed) {\n console.log('✅ Gateway started in background');\n console.log(` PID: ${child.pid}`);\n console.log(` URL: http://${displayHost}:${port}`);\n const token = (config as { gateway?: { auth?: { token?: string } } }).gateway?.auth?.token;\n if (token) {\n console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);\n }\n } else {\n console.log('⚠️ Failed to start gateway automatically.');\n console.log(' Start manually with:');\n console.log(' xopc gateway --background');\n }\n }\n\n console.log('');\n console.log('📚 Useful commands:');\n console.log(' xopc gateway status # Check gateway status');\n console.log(' xopc gateway stop # Stop gateway');\n console.log(' xopc gateway restart # Restart gateway');\n console.log(' xopc gateway logs # View logs');\n}\n\nasync function promptLaunchAfterOnboard(\n config: Config,\n ctx: CLIContext,\n flags: { doChannels: boolean },\n): Promise<void> {\n console.log('');\n const choice = await select<'tui' | 'gateway' | 'none'>({\n message: 'How do you want to launch xopc now?',\n choices: [\n {\n value: 'tui',\n name: 'Terminal UI (embedded)',\n description: 'xopc tui --local — no gateway process required',\n },\n {\n value: 'gateway',\n name: 'Gateway WebUI (background)',\n description: 'Start the HTTP gateway for the browser console',\n },\n {\n value: 'none',\n name: 'Exit — I will start manually',\n description: 'Finish setup without starting a runtime',\n },\n ],\n default: 'tui',\n });\n\n if (choice === 'gateway') {\n await startGatewayInBackground(config, ctx);\n return;\n }\n\n if (choice === 'tui') {\n if (flags.doChannels && !isWeixinOnboardConfigured(config)) {\n console.log(\n colors.gray(\n '\\n💡 Weixin is not logged in yet. When ready run: xopc channels login --channel weixin\\n',\n ),\n );\n }\n const { runTui } = await import('../../tui/tui.js');\n await runTui({ local: true });\n return;\n }\n\n console.log('\\n⏭️ You can start later:');\n console.log(' xopc gateway --background');\n console.log(' xopc tui --local');\n}\n\nasync function setupGateway(config: Config): Promise<Config> {\n console.log(colors.cyan('\\n🌐 Gateway WebUI\\n'));\n console.log(\n colors.gray(\n 'Applying defaults from config schema (127.0.0.1:18790, token auth; token generated if missing).\\n',\n ),\n );\n\n const gw = config.gateway ?? {};\n const { randomBytes } = await import('node:crypto');\n const authMode = gw.auth?.mode === 'none' ? ('none' as const) : ('token' as const);\n const token =\n authMode === 'token'\n ? typeof gw.auth?.token === 'string' && gw.auth.token.length > 0\n ? gw.auth.token\n : randomBytes(24).toString('hex')\n : undefined;\n\n const merged: Config = {\n ...config,\n gateway: {\n ...gw,\n host: gw.host ?? '127.0.0.1',\n port: gw.port ?? 18790,\n auth:\n authMode === 'none'\n ? { mode: 'none' as const }\n : { mode: 'token' as const, token: token! },\n },\n };\n\n const parsed = ConfigSchema.parse(merged);\n console.log('✅ Gateway defaults applied.\\n');\n return parsed;\n}\n\nregister({\n id: 'onboard',\n name: 'onboard',\n description: 'Interactive setup wizard',\n factory: createOnboardCommand,\n metadata: {\n category: 'setup',\n examples: ['xopc onboard', 'xopc onboard --model', 'xopc onboard --channels'],\n },\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { formatExamples, register } from "../registry.js";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
//#region src/cli/commands/tui.ts
|
|
4
|
+
function createTuiCommand(_ctx) {
|
|
5
|
+
return new Command("tui").description("Interactive terminal UI (pi-tui)").addHelpText("after", formatExamples([
|
|
6
|
+
"xopc tui # Connect to local gateway (default)",
|
|
7
|
+
"xopc tui --local # Embedded mode, no gateway needed",
|
|
8
|
+
"xopc tui --url http://host:3120 --token xxx # Connect to remote gateway",
|
|
9
|
+
"xopc tui -s telegram:dm:123456 # Resume a session",
|
|
10
|
+
"xopc tui -m \"Summarize my inbox\" # Send a message on launch"
|
|
11
|
+
])).option("--url <url>", "Gateway URL (default: http://localhost:3120)").option("--token <token>", "Gateway bearer token").option("-s, --session <key>", "Session key to resume").option("-m, --message <text>", "Send a message on launch").option("--local", "Run in embedded mode (no gateway required)").option("--thinking <level>", "Thinking level override").action(async (options) => {
|
|
12
|
+
const { runTui } = await import("../../tui/tui.js");
|
|
13
|
+
await runTui({
|
|
14
|
+
url: typeof options.url === "string" ? options.url : void 0,
|
|
15
|
+
token: typeof options.token === "string" ? options.token : void 0,
|
|
16
|
+
session: typeof options.session === "string" ? options.session : void 0,
|
|
17
|
+
message: typeof options.message === "string" ? options.message : void 0,
|
|
18
|
+
local: options.local === true,
|
|
19
|
+
thinking: typeof options.thinking === "string" ? options.thinking : void 0
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
register({
|
|
24
|
+
id: "tui",
|
|
25
|
+
name: "tui",
|
|
26
|
+
description: "Interactive terminal UI (pi-tui)",
|
|
27
|
+
factory: createTuiCommand,
|
|
28
|
+
metadata: {
|
|
29
|
+
category: "runtime",
|
|
30
|
+
examples: [
|
|
31
|
+
"xopc tui",
|
|
32
|
+
"xopc tui --local",
|
|
33
|
+
"xopc tui --url http://host:3120"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export {};
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=tui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui.js","names":[],"sources":["../../../../src/cli/commands/tui.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nfunction createTuiCommand(_ctx: CLIContext): Command {\n const cmd = new Command('tui')\n .description('Interactive terminal UI (pi-tui)')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc tui # Connect to local gateway (default)',\n 'xopc tui --local # Embedded mode, no gateway needed',\n 'xopc tui --url http://host:3120 --token xxx # Connect to remote gateway',\n 'xopc tui -s telegram:dm:123456 # Resume a session',\n 'xopc tui -m \"Summarize my inbox\" # Send a message on launch',\n ]),\n )\n .option('--url <url>', 'Gateway URL (default: http://localhost:3120)')\n .option('--token <token>', 'Gateway bearer token')\n .option('-s, --session <key>', 'Session key to resume')\n .option('-m, --message <text>', 'Send a message on launch')\n .option('--local', 'Run in embedded mode (no gateway required)')\n .option('--thinking <level>', 'Thinking level override')\n .action(async (options: Record<string, string | boolean | undefined>) => {\n const { runTui } = await import('../../tui/tui.js');\n await runTui({\n url: typeof options.url === 'string' ? options.url : undefined,\n token: typeof options.token === 'string' ? options.token : undefined,\n session: typeof options.session === 'string' ? options.session : undefined,\n message: typeof options.message === 'string' ? options.message : undefined,\n local: options.local === true,\n thinking: typeof options.thinking === 'string' ? options.thinking : undefined,\n });\n });\n\n return cmd;\n}\n\nregister({\n id: 'tui',\n name: 'tui',\n description: 'Interactive terminal UI (pi-tui)',\n factory: createTuiCommand,\n metadata: {\n category: 'runtime',\n examples: [\n 'xopc tui',\n 'xopc tui --local',\n 'xopc tui --url http://host:3120',\n ],\n },\n});\n"],"mappings":";;;AAIA,SAAS,iBAAiB,MAA2B;AA+BnD,QA9BY,IAAI,QAAQ,MAAM,CAC3B,YAAY,mCAAmC,CAC/C,YACC,SACA,eAAe;EACb;EACA;EACA;EACA;EACA;EACD,CAAC,CACH,CACA,OAAO,eAAe,+CAA+C,CACrE,OAAO,mBAAmB,uBAAuB,CACjD,OAAO,uBAAuB,wBAAwB,CACtD,OAAO,wBAAwB,2BAA2B,CAC1D,OAAO,WAAW,6CAA6C,CAC/D,OAAO,sBAAsB,0BAA0B,CACvD,OAAO,OAAO,YAA0D;EACvE,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,QAAM,OAAO;GACX,KAAK,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM,KAAA;GACrD,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ,KAAA;GAC3D,SAAS,OAAO,QAAQ,YAAY,WAAW,QAAQ,UAAU,KAAA;GACjE,SAAS,OAAO,QAAQ,YAAY,WAAW,QAAQ,UAAU,KAAA;GACjE,OAAO,QAAQ,UAAU;GACzB,UAAU,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,KAAA;GACrE,CAAC;GAGI;;AAGZ,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU;GACR;GACA;GACA;GACD;EACF;CACF,CAAC"}
|
package/dist/src/cli/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import './agent-chat-log-level-preset.js';
|
|
2
3
|
import { type CLIContext } from './registry.js';
|
|
3
4
|
import './commands/setup.js';
|
|
4
5
|
import './commands/onboard.js';
|
|
5
6
|
import './commands/agent.js';
|
|
7
|
+
import './commands/tui.js';
|
|
6
8
|
import './commands/gateway.js';
|
|
7
9
|
import './commands/session.js';
|
|
8
10
|
import './commands/cron.js';
|
package/dist/src/cli/index.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import { version } from "../../package.js";
|
|
3
3
|
import { flushAndClose } from "../utils/logger/shutdown.js";
|
|
4
4
|
import { init_logger } from "../utils/logger.js";
|
|
5
|
+
import "./agent-chat-log-level-preset.js";
|
|
5
6
|
import { createDefaultContext, registry } from "./registry.js";
|
|
6
7
|
import { registerExtensionCliCommands } from "./bootstrap-extensions.js";
|
|
7
8
|
import "./commands/setup.js";
|
|
8
9
|
import "./commands/onboard.js";
|
|
9
10
|
import "./commands/agent.js";
|
|
11
|
+
import "./commands/tui.js";
|
|
10
12
|
import "./commands/gateway.js";
|
|
11
13
|
import "./commands/session.js";
|
|
12
14
|
import "./commands/cron.js";
|
|
@@ -31,15 +33,17 @@ function getContextWithOpts(argv = process.argv) {
|
|
|
31
33
|
const LONG_RUNNING_COMMANDS = new Set([
|
|
32
34
|
"gateway",
|
|
33
35
|
"agent",
|
|
36
|
+
"tui",
|
|
34
37
|
"extension:dev"
|
|
35
38
|
]);
|
|
36
39
|
const program = new Command().name("xopc").description("Ultra-Lightweight Personal AI Assistant").version(version).option("--verbose", "Enable verbose logging", false).option("--config <path>", "Config file path").option("--workspace <path>", "Workspace directory");
|
|
37
40
|
program.hook("preAction", (thisCommand) => {
|
|
38
41
|
parsedOpts = thisCommand.opts();
|
|
39
42
|
});
|
|
40
|
-
program.hook("postAction", async (
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
+
program.hook("postAction", async (_hookOwner, actionCommand) => {
|
|
44
|
+
const cmd = actionCommand ?? program;
|
|
45
|
+
const args = cmd.args;
|
|
46
|
+
const subCommandName = args.length > 0 ? args[0] : cmd.name();
|
|
43
47
|
if (LONG_RUNNING_COMMANDS.has(subCommandName)) {
|
|
44
48
|
if (subCommandName === "agent") {
|
|
45
49
|
if (!(process.argv.includes("-i") || process.argv.includes("--interactive"))) {
|