@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeWeixinCronDeliveryTo, normalizeWeixinCronDeliveryToResolved, resolveWeixinAccountIdFromSessions } from "../../../extensions/weixin/src/delivery-to.js";
|
|
2
|
-
import { WeixinChannelPlugin, weixinPlugin } from "../../../extensions/weixin/src/plugin.js";
|
|
3
2
|
import { runWeixinQrLoginCli } from "../../../extensions/weixin/src/cli/qr-login.js";
|
|
3
|
+
import { WeixinChannelPlugin, weixinPlugin } from "../../../extensions/weixin/src/plugin.js";
|
|
4
4
|
import { getWeixinGatewayQrLoginStatus, startWeixinGatewayQrLogin } from "../../../extensions/weixin/src/cli/gateway-qr-login.js";
|
|
5
5
|
import "../../../extensions/weixin/src/index.js";
|
|
6
6
|
export { WeixinChannelPlugin, getWeixinGatewayQrLoginStatus, normalizeWeixinCronDeliveryTo, normalizeWeixinCronDeliveryToResolved, resolveWeixinAccountIdFromSessions, runWeixinQrLoginCli, startWeixinGatewayQrLogin, weixinPlugin };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quieter defaults for `xopc agent -m` / `agent -i`: use `warn` when no log level
|
|
3
|
+
* env is set, so `info` chatter stays off the console in dev.
|
|
4
|
+
* This module must load before `../utils/logger.js` (imported from `cli/index.ts`).
|
|
5
|
+
*/
|
|
6
|
+
declare function argvHasAgentMessageOrInteractive(argv: string[]): boolean;
|
|
7
|
+
declare const env: NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/cli/agent-chat-log-level-preset.ts
|
|
2
|
+
/**
|
|
3
|
+
* Quieter defaults for `xopc agent -m` / `agent -i`: use `warn` when no log level
|
|
4
|
+
* env is set, so `info` chatter stays off the console in dev.
|
|
5
|
+
* This module must load before `../utils/logger.js` (imported from `cli/index.ts`).
|
|
6
|
+
*/
|
|
7
|
+
function argvHasAgentMessageOrInteractive(argv) {
|
|
8
|
+
const agentIndex = argv.findIndex((a) => a === "agent");
|
|
9
|
+
if (agentIndex < 0) return false;
|
|
10
|
+
for (let i = agentIndex + 1; i < argv.length; i++) {
|
|
11
|
+
const a = argv[i];
|
|
12
|
+
if (a === "-i" || a === "--interactive") return true;
|
|
13
|
+
if (a === "-m" || a === "--message") return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const env = process.env;
|
|
18
|
+
if (!env.VITEST && !env.TEST && !env.XOPC_LOG_LEVEL && !env.LOG_LEVEL && !env.DEBUG && !process.argv.includes("--verbose") && !process.argv.includes("-v") && argvHasAgentMessageOrInteractive(process.argv)) env.XOPC_LOG_LEVEL = "warn";
|
|
19
|
+
//#endregion
|
|
20
|
+
export {};
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=agent-chat-log-level-preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-chat-log-level-preset.js","names":[],"sources":["../../../src/cli/agent-chat-log-level-preset.ts"],"sourcesContent":["/**\n * Quieter defaults for `xopc agent -m` / `agent -i`: use `warn` when no log level\n * env is set, so `info` chatter stays off the console in dev.\n * This module must load before `../utils/logger.js` (imported from `cli/index.ts`).\n */\nfunction argvHasAgentMessageOrInteractive(argv: string[]): boolean {\n const agentIndex = argv.findIndex((a) => a === 'agent');\n if (agentIndex < 0) return false;\n for (let i = agentIndex + 1; i < argv.length; i++) {\n const a = argv[i];\n if (a === '-i' || a === '--interactive') return true;\n if (a === '-m' || a === '--message') return true;\n }\n return false;\n}\n\nconst env = process.env;\nif (\n !env.VITEST &&\n !env.TEST &&\n !env.XOPC_LOG_LEVEL &&\n !env.LOG_LEVEL &&\n !env.DEBUG &&\n !process.argv.includes('--verbose') &&\n !process.argv.includes('-v') &&\n argvHasAgentMessageOrInteractive(process.argv)\n) {\n env.XOPC_LOG_LEVEL = 'warn';\n}\n"],"mappings":";;;;;;AAKA,SAAS,iCAAiC,MAAyB;CACjE,MAAM,aAAa,KAAK,WAAW,MAAM,MAAM,QAAQ;AACvD,KAAI,aAAa,EAAG,QAAO;AAC3B,MAAK,IAAI,IAAI,aAAa,GAAG,IAAI,KAAK,QAAQ,KAAK;EACjD,MAAM,IAAI,KAAK;AACf,MAAI,MAAM,QAAQ,MAAM,gBAAiB,QAAO;AAChD,MAAI,MAAM,QAAQ,MAAM,YAAa,QAAO;;AAE9C,QAAO;;AAGT,MAAM,MAAM,QAAQ;AACpB,IACE,CAAC,IAAI,UACL,CAAC,IAAI,QACL,CAAC,IAAI,kBACL,CAAC,IAAI,aACL,CAAC,IAAI,SACL,CAAC,QAAQ,KAAK,SAAS,YAAY,IACnC,CAAC,QAAQ,KAAK,SAAS,KAAK,IAC5B,iCAAiC,QAAQ,KAAK,CAE9C,KAAI,iBAAiB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getSessionManager } from "../../utils/session.js";
|
|
2
2
|
import { listSessions } from "./sessions.js";
|
|
3
|
+
import { renderStreamToTerminal } from "./stream-renderer.js";
|
|
3
4
|
//#region src/cli/commands/agent/interactive.ts
|
|
4
5
|
/**
|
|
5
6
|
* Start interactive chat mode
|
|
@@ -41,8 +42,9 @@ async function startInteractiveChat(agent, options) {
|
|
|
41
42
|
rl.close();
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
rl.pause();
|
|
46
|
+
await renderStreamToTerminal(agent, input, sessionKey);
|
|
47
|
+
rl.resume();
|
|
46
48
|
rl.prompt();
|
|
47
49
|
});
|
|
48
50
|
rl.on("close", async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.js","names":[],"sources":["../../../../../src/cli/commands/agent/interactive.ts"],"sourcesContent":["/**\n * Interactive chat mode for agent command\n */\n\nimport type { Interface as _Interface } from 'readline';\nimport type { AgentService } from '../../../agent/index.js';\nimport { getSessionManager } from '../../utils/session.js';\nimport { listSessions } from './sessions.js';\n\nexport interface InteractiveOptions {\n workspace: string;\n sessionKey: string;\n continuingSession: boolean;\n}\n\n/**\n * Start interactive chat mode\n */\nexport async function startInteractiveChat(\n agent: AgentService,\n options: InteractiveOptions\n): Promise<void> {\n const { sessionKey: initialSessionKey, continuingSession } = options;\n \n let sessionKey = initialSessionKey;\n\n if (continuingSession) {\n console.log('🧠 Interactive chat mode - Continuing session\\n');\n } else {\n console.log('🧠 Interactive chat mode (Ctrl+C to exit)\\n');\n }\n\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n\n rl.on('line', async (input) => {\n const trimmed = input.trim();\n \n // Handle special commands\n if (trimmed === ':sessions' || trimmed === ':list') {\n rl.pause();\n await listSessions();\n rl.resume();\n rl.prompt();\n return;\n }\n \n if (trimmed.startsWith(':session ')) {\n const newSessionKey = trimmed.slice(9).trim();\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(newSessionKey);\n if (session) {\n sessionKey = newSessionKey;\n console.log(`🔄 Switched to session: ${sessionKey}\\n`);\n } else {\n console.log(`❌ Session not found: ${newSessionKey}\\n`);\n }\n rl.prompt();\n return;\n }\n\n if (trimmed === ':help') {\n printHelp();\n rl.prompt();\n return;\n }\n\n if (trimmed === ':quit' || trimmed === ':exit') {\n rl.close();\n return;\n }\n\n
|
|
1
|
+
{"version":3,"file":"interactive.js","names":[],"sources":["../../../../../src/cli/commands/agent/interactive.ts"],"sourcesContent":["/**\n * Interactive chat mode for agent command\n */\n\nimport type { Interface as _Interface } from 'readline';\nimport type { AgentService } from '../../../agent/index.js';\nimport { getSessionManager } from '../../utils/session.js';\nimport { listSessions } from './sessions.js';\nimport { renderStreamToTerminal } from './stream-renderer.js';\n\nexport interface InteractiveOptions {\n workspace: string;\n sessionKey: string;\n continuingSession: boolean;\n}\n\n/**\n * Start interactive chat mode\n */\nexport async function startInteractiveChat(\n agent: AgentService,\n options: InteractiveOptions\n): Promise<void> {\n const { sessionKey: initialSessionKey, continuingSession } = options;\n \n let sessionKey = initialSessionKey;\n\n if (continuingSession) {\n console.log('🧠 Interactive chat mode - Continuing session\\n');\n } else {\n console.log('🧠 Interactive chat mode (Ctrl+C to exit)\\n');\n }\n\n const readline = await import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n\n rl.on('line', async (input) => {\n const trimmed = input.trim();\n \n // Handle special commands\n if (trimmed === ':sessions' || trimmed === ':list') {\n rl.pause();\n await listSessions();\n rl.resume();\n rl.prompt();\n return;\n }\n \n if (trimmed.startsWith(':session ')) {\n const newSessionKey = trimmed.slice(9).trim();\n const manager = await getSessionManager();\n const session = await manager.getSessionMetadata(newSessionKey);\n if (session) {\n sessionKey = newSessionKey;\n console.log(`🔄 Switched to session: ${sessionKey}\\n`);\n } else {\n console.log(`❌ Session not found: ${newSessionKey}\\n`);\n }\n rl.prompt();\n return;\n }\n\n if (trimmed === ':help') {\n printHelp();\n rl.prompt();\n return;\n }\n\n if (trimmed === ':quit' || trimmed === ':exit') {\n rl.close();\n return;\n }\n\n rl.pause();\n await renderStreamToTerminal(agent, input, sessionKey);\n rl.resume();\n rl.prompt();\n });\n\n rl.on('close', async () => {\n console.log('\\n👋 Goodbye!');\n process.exit(0);\n });\n\n rl.setPrompt('You: ');\n rl.prompt();\n}\n\nfunction printHelp(): void {\n console.log(`\n📖 Available commands:\n :sessions, :list - List available sessions\n :session <key> - Switch to another session\n :quit, :exit - Exit interactive mode\n :help - Show this help\n`);\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,qBACpB,OACA,SACe;CACf,MAAM,EAAE,YAAY,mBAAmB,sBAAsB;CAE7D,IAAI,aAAa;AAEjB,KAAI,kBACF,SAAQ,IAAI,kDAAkD;KAE9D,SAAQ,IAAI,8CAA8C;CAI5D,MAAM,MAAK,MADY,OAAO,aACV,gBAAgB;EAClC,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,UAAU;EACX,CAAC;AAEF,IAAG,GAAG,QAAQ,OAAO,UAAU;EAC7B,MAAM,UAAU,MAAM,MAAM;AAG5B,MAAI,YAAY,eAAe,YAAY,SAAS;AAClD,MAAG,OAAO;AACV,SAAM,cAAc;AACpB,MAAG,QAAQ;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,QAAQ,WAAW,YAAY,EAAE;GACnC,MAAM,gBAAgB,QAAQ,MAAM,EAAE,CAAC,MAAM;AAG7C,OAAI,OADkB,MADA,mBAAmB,EACX,mBAAmB,cAAc,EAClD;AACX,iBAAa;AACb,YAAQ,IAAI,2BAA2B,WAAW,IAAI;SAEtD,SAAQ,IAAI,wBAAwB,cAAc,IAAI;AAExD,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,SAAS;AACvB,cAAW;AACX,MAAG,QAAQ;AACX;;AAGF,MAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,MAAG,OAAO;AACV;;AAGF,KAAG,OAAO;AACV,QAAM,uBAAuB,OAAO,OAAO,WAAW;AACtD,KAAG,QAAQ;AACX,KAAG,QAAQ;GACX;AAEF,IAAG,GAAG,SAAS,YAAY;AACzB,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,KAAK,EAAE;GACf;AAEF,IAAG,UAAU,QAAQ;AACrB,IAAG,QAAQ;;AAGb,SAAS,YAAkB;AACzB,SAAQ,IAAI;;;;;;EAMZ"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI stream renderer for agent events
|
|
3
|
+
*
|
|
4
|
+
* Consumes processDirectStreaming events and renders thinking,
|
|
5
|
+
* tool calls, and response tokens to the terminal in real time.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentService } from '../../../agent/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Consume the processDirectStreaming async generator and render
|
|
10
|
+
* thinking / tool-call / response tokens to stdout.
|
|
11
|
+
*
|
|
12
|
+
* Returns the final aggregated response text.
|
|
13
|
+
*/
|
|
14
|
+
export declare function renderStreamToTerminal(agent: AgentService, message: string, sessionKey: string): Promise<string>;
|
|
@@ -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",
|