@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.
Files changed (239) hide show
  1. package/dist/extensions/telegram/xopc.extension.json +1 -1
  2. package/dist/extensions/weixin/src/adapters/onboard-cli.d.ts +7 -0
  3. package/dist/extensions/weixin/src/adapters/onboard-cli.js +61 -0
  4. package/dist/extensions/weixin/src/adapters/onboard-cli.js.map +1 -0
  5. package/dist/extensions/weixin/src/cli/qr-login.d.ts +5 -0
  6. package/dist/extensions/weixin/src/cli/qr-login.js +1 -1
  7. package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
  8. package/dist/extensions/weixin/src/index.js +1 -1
  9. package/dist/extensions/weixin/src/plugin.d.ts +1 -0
  10. package/dist/extensions/weixin/src/plugin.js +2 -0
  11. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  12. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js +216 -0
  13. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +1 -0
  14. package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js → apps-page-Bmq19MS-.js} +2 -2
  15. package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js.map → apps-page-Bmq19MS-.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +9 -0
  17. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +1 -0
  18. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +2 -0
  19. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +1 -0
  20. package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js → cron-utils-N1PqD2DB.js} +2 -2
  21. package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js.map → cron-utils-N1PqD2DB.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{dist-C1MrygQH.js → dist--p2HQ2QF.js} +2 -2
  23. package/dist/gateway/static/root/assets/{dist-C1MrygQH.js.map → dist--p2HQ2QF.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js → extension-debug-page-DwHCB_6T.js} +2 -2
  25. package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js.map → extension-debug-page-DwHCB_6T.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js → extension-page-BsYwQIex.js} +2 -2
  27. package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js.map → extension-page-BsYwQIex.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js → extension-settings-page-nsisEgjB.js} +2 -2
  29. package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js.map → extension-settings-page-nsisEgjB.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/index-CR8zUHGR.js +4734 -0
  31. package/dist/gateway/static/root/assets/{index-PfkB8N37.js.map → index-CR8zUHGR.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/index-Dnfha4O2.css +1 -0
  33. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +2 -0
  34. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js.map +1 -0
  35. package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +2 -0
  36. package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js.map +1 -0
  37. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +2 -0
  38. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +1 -0
  39. package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +3 -0
  40. package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js.map → skills-page-Clg8deH0.js.map} +1 -1
  41. package/dist/gateway/static/root/index.html +2 -2
  42. package/dist/package.js +1 -1
  43. package/dist/src/agent/lifecycle/hook-handler.d.ts +2 -0
  44. package/dist/src/agent/lifecycle/hook-handler.js +24 -0
  45. package/dist/src/agent/lifecycle/hook-handler.js.map +1 -1
  46. package/dist/src/agent/messaging/command-handler.js +10 -2
  47. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  48. package/dist/src/agent/service/process-direct-streaming.js +77 -20
  49. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  50. package/dist/src/agent/service.d.ts +15 -0
  51. package/dist/src/agent/service.js +21 -1
  52. package/dist/src/agent/service.js.map +1 -1
  53. package/dist/src/channels/weixin/index.js +1 -1
  54. package/dist/src/cli/agent-chat-log-level-preset.d.ts +8 -0
  55. package/dist/src/cli/agent-chat-log-level-preset.js +25 -0
  56. package/dist/src/cli/agent-chat-log-level-preset.js.map +1 -0
  57. package/dist/src/cli/commands/agent/interactive.js +4 -2
  58. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  59. package/dist/src/cli/commands/agent/stream-renderer.d.ts +14 -0
  60. package/dist/src/cli/commands/agent/stream-renderer.js +99 -0
  61. package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -0
  62. package/dist/src/cli/commands/agent.js +2 -2
  63. package/dist/src/cli/commands/agent.js.map +1 -1
  64. package/dist/src/cli/commands/onboard.js +77 -93
  65. package/dist/src/cli/commands/onboard.js.map +1 -1
  66. package/dist/src/cli/commands/tui.d.ts +1 -0
  67. package/dist/src/cli/commands/tui.js +40 -0
  68. package/dist/src/cli/commands/tui.js.map +1 -0
  69. package/dist/src/cli/index.d.ts +2 -0
  70. package/dist/src/cli/index.js +7 -3
  71. package/dist/src/cli/index.js.map +1 -1
  72. package/dist/src/config/schema.d.ts +6 -0
  73. package/dist/src/config/schema.js +11 -3
  74. package/dist/src/config/schema.js.map +1 -1
  75. package/dist/src/extensions/hooks.js +5 -1
  76. package/dist/src/extensions/hooks.js.map +1 -1
  77. package/dist/src/extensions/loader.d.ts +1 -0
  78. package/dist/src/extensions/loader.js +3 -1
  79. package/dist/src/extensions/loader.js.map +1 -1
  80. package/dist/src/extensions/sdk/index.d.ts +1 -1
  81. package/dist/src/extensions/sdk/index.js.map +1 -1
  82. package/dist/src/extensions/types/core.d.ts +8 -0
  83. package/dist/src/extensions/types/hooks.d.ts +16 -1
  84. package/dist/src/extensions/types/hooks.js +1 -0
  85. package/dist/src/extensions/types/hooks.js.map +1 -1
  86. package/dist/src/gateway/agents-admin.d.ts +19 -1
  87. package/dist/src/gateway/agents-admin.js +164 -3
  88. package/dist/src/gateway/agents-admin.js.map +1 -1
  89. package/dist/src/gateway/auth.d.ts +17 -3
  90. package/dist/src/gateway/auth.js +35 -16
  91. package/dist/src/gateway/auth.js.map +1 -1
  92. package/dist/src/gateway/hono/app.js +31 -1
  93. package/dist/src/gateway/hono/app.js.map +1 -1
  94. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
  95. package/dist/src/gateway/hono/middleware/auth.js +4 -3
  96. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  97. package/dist/src/gateway/hono/middleware/scopes.d.ts +15 -0
  98. package/dist/src/gateway/hono/middleware/scopes.js +41 -0
  99. package/dist/src/gateway/hono/middleware/scopes.js.map +1 -0
  100. package/dist/src/gateway/hono/routes/agents.js +59 -5
  101. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  102. package/dist/src/gateway/hono/routes/config.js +2 -2
  103. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  104. package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
  105. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  106. package/dist/src/gateway/hono/routes/sessions.js +17 -0
  107. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  108. package/dist/src/gateway/security/audit.d.ts +18 -0
  109. package/dist/src/gateway/security/audit.js +68 -0
  110. package/dist/src/gateway/security/audit.js.map +1 -0
  111. package/dist/src/gateway/security/csp.d.ts +19 -0
  112. package/dist/src/gateway/security/csp.js +52 -0
  113. package/dist/src/gateway/security/csp.js.map +1 -0
  114. package/dist/src/gateway/security/dangerous-tools.d.ts +20 -0
  115. package/dist/src/gateway/security/dangerous-tools.js +46 -0
  116. package/dist/src/gateway/security/dangerous-tools.js.map +1 -0
  117. package/dist/src/gateway/security/flood-guard.d.ts +28 -0
  118. package/dist/src/gateway/security/flood-guard.js +42 -0
  119. package/dist/src/gateway/security/flood-guard.js.map +1 -0
  120. package/dist/src/gateway/security/index.d.ts +9 -0
  121. package/dist/src/gateway/security/index.js +10 -0
  122. package/dist/src/gateway/security/known-weak-secrets.d.ts +10 -0
  123. package/dist/src/gateway/security/known-weak-secrets.js +36 -0
  124. package/dist/src/gateway/security/known-weak-secrets.js.map +1 -0
  125. package/dist/src/gateway/security/operator-scopes.d.ts +37 -0
  126. package/dist/src/gateway/security/operator-scopes.js +137 -0
  127. package/dist/src/gateway/security/operator-scopes.js.map +1 -0
  128. package/dist/src/gateway/security/origin-check.d.ts +21 -0
  129. package/dist/src/gateway/security/origin-check.js +56 -0
  130. package/dist/src/gateway/security/origin-check.js.map +1 -0
  131. package/dist/src/gateway/security/preauth-connection-budget.d.ts +17 -0
  132. package/dist/src/gateway/security/preauth-connection-budget.js +49 -0
  133. package/dist/src/gateway/security/preauth-connection-budget.js.map +1 -0
  134. package/dist/src/gateway/security/secret-equal.d.ts +8 -0
  135. package/dist/src/gateway/security/secret-equal.js +30 -0
  136. package/dist/src/gateway/security/secret-equal.js.map +1 -0
  137. package/dist/src/gateway/service.d.ts +3 -1
  138. package/dist/src/gateway/service.js +40 -4
  139. package/dist/src/gateway/service.js.map +1 -1
  140. package/dist/src/session/client-history.d.ts +21 -0
  141. package/dist/src/session/client-history.js +89 -0
  142. package/dist/src/session/client-history.js.map +1 -0
  143. package/dist/src/session/index.d.ts +1 -0
  144. package/dist/src/session/index.js +2 -1
  145. package/dist/src/session/manager.d.ts +2 -0
  146. package/dist/src/session/manager.js +5 -0
  147. package/dist/src/session/manager.js.map +1 -1
  148. package/dist/src/session/thinking-resolve.js +1 -1
  149. package/dist/src/session/thinking-resolve.js.map +1 -1
  150. package/dist/src/tui/backends/embedded-backend.d.ts +42 -0
  151. package/dist/src/tui/backends/embedded-backend.js +173 -0
  152. package/dist/src/tui/backends/embedded-backend.js.map +1 -0
  153. package/dist/src/tui/backends/gateway-sse-backend.d.ts +53 -0
  154. package/dist/src/tui/backends/gateway-sse-backend.js +256 -0
  155. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -0
  156. package/dist/src/tui/chat-history.d.ts +4 -0
  157. package/dist/src/tui/chat-history.js +29 -0
  158. package/dist/src/tui/chat-history.js.map +1 -0
  159. package/dist/src/tui/components/assistant-message.d.ts +6 -0
  160. package/dist/src/tui/components/assistant-message.js +19 -0
  161. package/dist/src/tui/components/assistant-message.js.map +1 -0
  162. package/dist/src/tui/components/chat-log.d.ts +21 -0
  163. package/dist/src/tui/components/chat-log.js +113 -0
  164. package/dist/src/tui/components/chat-log.js.map +1 -0
  165. package/dist/src/tui/components/custom-editor.d.ts +14 -0
  166. package/dist/src/tui/components/custom-editor.js +50 -0
  167. package/dist/src/tui/components/custom-editor.js.map +1 -0
  168. package/dist/src/tui/components/fuzzy-filter.d.ts +17 -0
  169. package/dist/src/tui/components/fuzzy-filter.js +85 -0
  170. package/dist/src/tui/components/fuzzy-filter.js.map +1 -0
  171. package/dist/src/tui/components/searchable-select-list.d.ts +39 -0
  172. package/dist/src/tui/components/searchable-select-list.js +257 -0
  173. package/dist/src/tui/components/searchable-select-list.js.map +1 -0
  174. package/dist/src/tui/components/tool-execution.d.ts +16 -0
  175. package/dist/src/tui/components/tool-execution.js +76 -0
  176. package/dist/src/tui/components/tool-execution.js.map +1 -0
  177. package/dist/src/tui/components/user-message.d.ts +6 -0
  178. package/dist/src/tui/components/user-message.js +22 -0
  179. package/dist/src/tui/components/user-message.js.map +1 -0
  180. package/dist/src/tui/sse-consumer.d.ts +15 -0
  181. package/dist/src/tui/sse-consumer.js +75 -0
  182. package/dist/src/tui/sse-consumer.js.map +1 -0
  183. package/dist/src/tui/stream-assembler.d.ts +22 -0
  184. package/dist/src/tui/stream-assembler.js +63 -0
  185. package/dist/src/tui/stream-assembler.js.map +1 -0
  186. package/dist/src/tui/theme.d.ts +73 -0
  187. package/dist/src/tui/theme.js +157 -0
  188. package/dist/src/tui/theme.js.map +1 -0
  189. package/dist/src/tui/tui-agent-events.d.ts +7 -0
  190. package/dist/src/tui/tui-agent-events.js +103 -0
  191. package/dist/src/tui/tui-agent-events.js.map +1 -0
  192. package/dist/src/tui/tui-backend.d.ts +80 -0
  193. package/dist/src/tui/tui-backend.js +1 -0
  194. package/dist/src/tui/tui-commands.d.ts +23 -0
  195. package/dist/src/tui/tui-commands.js +165 -0
  196. package/dist/src/tui/tui-commands.js.map +1 -0
  197. package/dist/src/tui/tui-lifecycle.d.ts +26 -0
  198. package/dist/src/tui/tui-lifecycle.js +57 -0
  199. package/dist/src/tui/tui-lifecycle.js.map +1 -0
  200. package/dist/src/tui/tui-local-shell.d.ts +28 -0
  201. package/dist/src/tui/tui-local-shell.js +147 -0
  202. package/dist/src/tui/tui-local-shell.js.map +1 -0
  203. package/dist/src/tui/tui-overlays.d.ts +8 -0
  204. package/dist/src/tui/tui-overlays.js +22 -0
  205. package/dist/src/tui/tui-overlays.js.map +1 -0
  206. package/dist/src/tui/tui-picker-overlay.d.ts +26 -0
  207. package/dist/src/tui/tui-picker-overlay.js +69 -0
  208. package/dist/src/tui/tui-picker-overlay.js.map +1 -0
  209. package/dist/src/tui/tui-stdio-filter.d.ts +17 -0
  210. package/dist/src/tui/tui-stdio-filter.js +96 -0
  211. package/dist/src/tui/tui-stdio-filter.js.map +1 -0
  212. package/dist/src/tui/tui-submit.d.ts +25 -0
  213. package/dist/src/tui/tui-submit.js +102 -0
  214. package/dist/src/tui/tui-submit.js.map +1 -0
  215. package/dist/src/tui/tui-suspend.d.ts +10 -0
  216. package/dist/src/tui/tui-suspend.js +18 -0
  217. package/dist/src/tui/tui-suspend.js.map +1 -0
  218. package/dist/src/tui/tui-types.d.ts +86 -0
  219. package/dist/src/tui/tui-types.js +21 -0
  220. package/dist/src/tui/tui-types.js.map +1 -0
  221. package/dist/src/tui/tui.d.ts +5 -0
  222. package/dist/src/tui/tui.js +389 -0
  223. package/dist/src/tui/tui.js.map +1 -0
  224. package/package.json +5 -3
  225. package/dist/gateway/static/root/assets/agents-w8_jzuiX.js +0 -216
  226. package/dist/gateway/static/root/assets/agents-w8_jzuiX.js.map +0 -1
  227. package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js +0 -9
  228. package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js.map +0 -1
  229. package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js +0 -2
  230. package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js.map +0 -1
  231. package/dist/gateway/static/root/assets/index-OT4cGzon.css +0 -1
  232. package/dist/gateway/static/root/assets/index-PfkB8N37.js +0 -4734
  233. package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js +0 -2
  234. package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js.map +0 -1
  235. package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js +0 -2
  236. package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js.map +0 -1
  237. package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js +0 -2
  238. package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js.map +0 -1
  239. package/dist/gateway/static/root/assets/skills-page-BmBDCEbY.js +0 -3
@@ -0,0 +1,86 @@
1
+ /** TUI configuration options passed from CLI. */
2
+ export interface TuiOptions {
3
+ /** Connect to a running gateway instead of embedded mode. */
4
+ url?: string;
5
+ /** Gateway bearer token. */
6
+ token?: string;
7
+ /** Session key to resume. */
8
+ session?: string;
9
+ /** Thinking level override. */
10
+ thinking?: string;
11
+ /** Single message to send on start, then stay open. */
12
+ message?: string;
13
+ /** Run in embedded (local) mode — no gateway required. */
14
+ local?: boolean;
15
+ }
16
+ export type TuiExitReason = 'exit' | 'signal';
17
+ export interface TuiResult {
18
+ exitReason: TuiExitReason;
19
+ }
20
+ /** SSE events emitted by POST /api/agent. */
21
+ export interface AgentSSEStatusEvent {
22
+ status: string;
23
+ runId: string;
24
+ }
25
+ export interface AgentSSETokenEvent {
26
+ content: string;
27
+ }
28
+ export interface AgentSSEThinkingEvent {
29
+ content: string;
30
+ isDelta?: boolean;
31
+ }
32
+ export interface AgentSSEToolStartEvent {
33
+ toolName: string;
34
+ toolCallId: string;
35
+ args?: unknown;
36
+ }
37
+ export interface AgentSSEToolEndEvent {
38
+ toolName: string;
39
+ toolCallId: string;
40
+ isError: boolean;
41
+ result?: string;
42
+ }
43
+ export interface AgentSSEErrorEvent {
44
+ content: string;
45
+ }
46
+ export interface AgentSSEResultEvent {
47
+ ok: boolean;
48
+ payload?: {
49
+ status?: string;
50
+ summary?: string;
51
+ };
52
+ }
53
+ /** Parsed SSE event from the stream. */
54
+ export interface ParsedSSEEvent {
55
+ event: string;
56
+ data: string;
57
+ id?: string;
58
+ }
59
+ /** Activity status for the TUI status bar. */
60
+ export type ActivityStatus = 'idle' | 'sending' | 'waiting' | 'streaming' | 'running';
61
+ /** Session metadata shown in the TUI footer. */
62
+ export interface SessionInfo {
63
+ model?: string;
64
+ modelProvider?: string;
65
+ thinkingLevel?: string;
66
+ contextTokens?: number | null;
67
+ totalTokens?: number | null;
68
+ displayName?: string;
69
+ }
70
+ /** Mutable state bag for the TUI runtime. */
71
+ export interface TuiState {
72
+ currentSessionKey: string;
73
+ activeRunId: string | null;
74
+ isConnected: boolean;
75
+ activityStatus: ActivityStatus;
76
+ connectionStatus: string;
77
+ sessionInfo: SessionInfo;
78
+ autoMessageSent: boolean;
79
+ historyLoaded: boolean;
80
+ toolsExpanded: boolean;
81
+ showThinking: boolean;
82
+ /** Last Ctrl+C timestamp for double-press exit (see `resolveCtrlCAction`). */
83
+ lastCtrlCAt: number;
84
+ exitRequested: boolean;
85
+ }
86
+ export declare function createInitialState(sessionKey: string): TuiState;
@@ -0,0 +1,21 @@
1
+ //#region src/tui/tui-types.ts
2
+ function createInitialState(sessionKey) {
3
+ return {
4
+ currentSessionKey: sessionKey,
5
+ activeRunId: null,
6
+ isConnected: false,
7
+ activityStatus: "idle",
8
+ connectionStatus: "connecting",
9
+ sessionInfo: {},
10
+ autoMessageSent: false,
11
+ historyLoaded: false,
12
+ toolsExpanded: false,
13
+ showThinking: false,
14
+ lastCtrlCAt: 0,
15
+ exitRequested: false
16
+ };
17
+ }
18
+ //#endregion
19
+ export { createInitialState };
20
+
21
+ //# sourceMappingURL=tui-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-types.js","names":[],"sources":["../../../src/tui/tui-types.ts"],"sourcesContent":["/** TUI configuration options passed from CLI. */\nexport interface TuiOptions {\n /** Connect to a running gateway instead of embedded mode. */\n url?: string;\n /** Gateway bearer token. */\n token?: string;\n /** Session key to resume. */\n session?: string;\n /** Thinking level override. */\n thinking?: string;\n /** Single message to send on start, then stay open. */\n message?: string;\n /** Run in embedded (local) mode — no gateway required. */\n local?: boolean;\n}\n\nexport type TuiExitReason = 'exit' | 'signal';\n\nexport interface TuiResult {\n exitReason: TuiExitReason;\n}\n\n/** SSE events emitted by POST /api/agent. */\nexport interface AgentSSEStatusEvent {\n status: string;\n runId: string;\n}\n\nexport interface AgentSSETokenEvent {\n content: string;\n}\n\nexport interface AgentSSEThinkingEvent {\n content: string;\n isDelta?: boolean;\n}\n\nexport interface AgentSSEToolStartEvent {\n toolName: string;\n toolCallId: string;\n args?: unknown;\n}\n\nexport interface AgentSSEToolEndEvent {\n toolName: string;\n toolCallId: string;\n isError: boolean;\n result?: string;\n}\n\nexport interface AgentSSEErrorEvent {\n content: string;\n}\n\nexport interface AgentSSEResultEvent {\n ok: boolean;\n payload?: { status?: string; summary?: string };\n}\n\n/** Parsed SSE event from the stream. */\nexport interface ParsedSSEEvent {\n event: string;\n data: string;\n id?: string;\n}\n\n/** Activity status for the TUI status bar. */\nexport type ActivityStatus =\n | 'idle'\n | 'sending'\n | 'waiting'\n | 'streaming'\n | 'running';\n\n/** Session metadata shown in the TUI footer. */\nexport interface SessionInfo {\n model?: string;\n modelProvider?: string;\n thinkingLevel?: string;\n contextTokens?: number | null;\n totalTokens?: number | null;\n displayName?: string;\n}\n\n/** Mutable state bag for the TUI runtime. */\nexport interface TuiState {\n currentSessionKey: string;\n activeRunId: string | null;\n isConnected: boolean;\n activityStatus: ActivityStatus;\n connectionStatus: string;\n sessionInfo: SessionInfo;\n autoMessageSent: boolean;\n historyLoaded: boolean;\n toolsExpanded: boolean;\n showThinking: boolean;\n /** Last Ctrl+C timestamp for double-press exit (see `resolveCtrlCAction`). */\n lastCtrlCAt: number;\n exitRequested: boolean;\n}\n\nexport function createInitialState(sessionKey: string): TuiState {\n return {\n currentSessionKey: sessionKey,\n activeRunId: null,\n isConnected: false,\n activityStatus: 'idle',\n connectionStatus: 'connecting',\n sessionInfo: {},\n autoMessageSent: false,\n historyLoaded: false,\n toolsExpanded: false,\n showThinking: false,\n lastCtrlCAt: 0,\n exitRequested: false,\n };\n}\n"],"mappings":";AAqGA,SAAgB,mBAAmB,YAA8B;AAC/D,QAAO;EACL,mBAAmB;EACnB,aAAa;EACb,aAAa;EACb,gBAAgB;EAChB,kBAAkB;EAClB,aAAa,EAAE;EACf,iBAAiB;EACjB,eAAe;EACf,eAAe;EACf,cAAc;EACd,aAAa;EACb,eAAe;EAChB"}
@@ -0,0 +1,5 @@
1
+ import { type TuiOptions, type TuiResult } from './tui-types.js';
2
+ export type { TuiOptions, TuiResult };
3
+ export { createBackspaceDeduper, drainAndStopTuiSafely, type DrainableTui, isIgnorableTuiStopError, resolveCtrlCAction, stopTuiSafely, } from './tui-lifecycle.js';
4
+ export { withTuiSuspended } from './tui-suspend.js';
5
+ export declare function runTui(opts: TuiOptions): Promise<TuiResult>;
@@ -0,0 +1,389 @@
1
+ import { appendHistoryToChatLog } from "./chat-history.js";
2
+ import { StreamAssembler } from "./stream-assembler.js";
3
+ import { editorTheme, theme } from "./theme.js";
4
+ import { clearPendingToolCallIds, dispatchAgentSSE } from "./tui-agent-events.js";
5
+ import { createTuiCommandHandler, getSlashCommands } from "./tui-commands.js";
6
+ import { createBackspaceDeduper, drainAndStopTuiSafely, isIgnorableTuiStopError, resolveCtrlCAction, stopTuiSafely } from "./tui-lifecycle.js";
7
+ import { createLocalShellRunner } from "./tui-local-shell.js";
8
+ import { createOverlayHandlers } from "./tui-overlays.js";
9
+ import { openModelPickerOverlay, openSessionPickerOverlay } from "./tui-picker-overlay.js";
10
+ import { installTuiStdioFilter } from "./tui-stdio-filter.js";
11
+ import { createEditorSubmitHandler, createSubmitBurstCoalescer, shouldEnableWindowsGitBashPasteFallback } from "./tui-submit.js";
12
+ import { withTuiSuspended } from "./tui-suspend.js";
13
+ import { createInitialState } from "./tui-types.js";
14
+ import { EmbeddedBackend } from "./backends/embedded-backend.js";
15
+ import { GatewaySseBackend } from "./backends/gateway-sse-backend.js";
16
+ import { ChatLog } from "./components/chat-log.js";
17
+ import { CustomEditor } from "./components/custom-editor.js";
18
+ import { CombinedAutocompleteProvider, Container, Key, Loader, ProcessTerminal, TUI, Text, getKeybindings, isKeyRelease, matchesKey, parseKey } from "@mariozechner/pi-tui";
19
+ //#region src/tui/tui.ts
20
+ function matchesCtrlCSequence(data) {
21
+ if (isKeyRelease(data)) return false;
22
+ if (data === "") return true;
23
+ if (parseKey(data) === "ctrl+c") return true;
24
+ const kb = getKeybindings();
25
+ return matchesKey(data, Key.ctrl("c")) || kb.matches(data, "tui.input.copy");
26
+ }
27
+ async function runTui(opts) {
28
+ const stdioFilter = installTuiStdioFilter();
29
+ const restoreStdio = () => stdioFilter.restore();
30
+ const isLocalMode = opts.local === true;
31
+ const state = createInitialState(opts.session ?? "cli:tui");
32
+ const assembler = new StreamAssembler();
33
+ const client = isLocalMode ? new EmbeddedBackend() : new GatewaySseBackend({
34
+ url: opts.url ?? "http://localhost:3120",
35
+ token: opts.token
36
+ });
37
+ const tui = new TUI(new ProcessTerminal());
38
+ const dedupeBackspace = createBackspaceDeduper();
39
+ tui.addInputListener((data) => {
40
+ const next = dedupeBackspace(data);
41
+ if (next.length === 0) return { consume: true };
42
+ return { data: next };
43
+ });
44
+ const header = new Text("", 1, 0);
45
+ const statusContainer = new Container();
46
+ const footer = new Text("", 1, 0);
47
+ const chatLog = new ChatLog();
48
+ const editor = new CustomEditor(tui, editorTheme);
49
+ const root = new Container();
50
+ root.addChild(header);
51
+ root.addChild(chatLog);
52
+ root.addChild(statusContainer);
53
+ root.addChild(footer);
54
+ root.addChild(editor);
55
+ tui.addChild(root);
56
+ tui.setFocus(editor);
57
+ const { openOverlay, closeOverlay } = createOverlayHandlers(tui, editor);
58
+ const slashCommands = getSlashCommands(isLocalMode);
59
+ editor.setAutocompleteProvider(new CombinedAutocompleteProvider(slashCommands.map((c) => ({
60
+ name: c.name,
61
+ description: c.description
62
+ })), process.cwd()));
63
+ let statusText = null;
64
+ let statusLoader = null;
65
+ let statusStartedAt = null;
66
+ let lastActivityStatus = "";
67
+ let elapsedTimerId = null;
68
+ const busyStates = new Set([
69
+ "sending",
70
+ "waiting",
71
+ "streaming",
72
+ "running"
73
+ ]);
74
+ let lastStreamActivityAt = Date.now();
75
+ let streamWatchdogId = null;
76
+ const touchStreamingActivity = () => {
77
+ lastStreamActivityAt = Date.now();
78
+ };
79
+ const formatElapsed = (startMs) => {
80
+ const totalSeconds = Math.max(0, Math.floor((Date.now() - startMs) / 1e3));
81
+ if (totalSeconds < 60) return `${totalSeconds}s`;
82
+ return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`;
83
+ };
84
+ const renderStatus = () => {
85
+ if (busyStates.has(state.activityStatus)) {
86
+ if (!statusStartedAt || lastActivityStatus !== state.activityStatus) statusStartedAt = Date.now();
87
+ if (!statusLoader) {
88
+ statusContainer.clear();
89
+ statusText = null;
90
+ statusLoader = new Loader(tui, (spinner) => theme.accent(spinner), (text) => theme.bold(theme.accentSoft(text)), "");
91
+ statusContainer.addChild(statusLoader);
92
+ }
93
+ const elapsed = formatElapsed(statusStartedAt);
94
+ statusLoader.setMessage(`${state.activityStatus} • ${elapsed} | ${state.connectionStatus}`);
95
+ if (!elapsedTimerId) elapsedTimerId = setInterval(() => {
96
+ if (statusStartedAt && statusLoader) {
97
+ const el = formatElapsed(statusStartedAt);
98
+ statusLoader.setMessage(`${state.activityStatus} • ${el} | ${state.connectionStatus}`);
99
+ }
100
+ }, 1e3);
101
+ } else {
102
+ statusStartedAt = null;
103
+ if (elapsedTimerId) {
104
+ clearInterval(elapsedTimerId);
105
+ elapsedTimerId = null;
106
+ }
107
+ statusLoader?.stop();
108
+ statusLoader = null;
109
+ if (!statusText) {
110
+ statusContainer.clear();
111
+ statusText = new Text("", 1, 0);
112
+ statusContainer.addChild(statusText);
113
+ }
114
+ const text = state.activityStatus ? `${state.connectionStatus} | ${state.activityStatus}` : state.connectionStatus;
115
+ statusText.setText(theme.dim(text));
116
+ }
117
+ lastActivityStatus = state.activityStatus;
118
+ };
119
+ const setActivityStatus = (status) => {
120
+ state.activityStatus = status;
121
+ renderStatus();
122
+ };
123
+ const setConnectionStatus = (text) => {
124
+ state.connectionStatus = text;
125
+ renderStatus();
126
+ };
127
+ const updateHeader = () => {
128
+ header.setText(theme.header(`xopc tui — ${client.connectionLabel} — session ${state.currentSessionKey}`));
129
+ };
130
+ const updateFooter = () => {
131
+ const modelLabel = state.sessionInfo.model ? state.sessionInfo.modelProvider ? `${state.sessionInfo.modelProvider}/${state.sessionInfo.model}` : state.sessionInfo.model : "unknown";
132
+ const tokens = state.sessionInfo.totalTokens != null ? `${state.sessionInfo.totalTokens} tokens` : "";
133
+ const thinking = state.showThinking ? "thinking:on" : "";
134
+ const parts = [
135
+ `session ${state.currentSessionKey}`,
136
+ modelLabel,
137
+ thinking,
138
+ tokens
139
+ ].filter(Boolean);
140
+ footer.setText(theme.dim(parts.join(" | ")));
141
+ };
142
+ const refreshSessionInfo = async () => {
143
+ try {
144
+ state.sessionInfo = await client.getSessionInfo(state.currentSessionKey);
145
+ updateFooter();
146
+ tui.requestRender();
147
+ } catch {}
148
+ };
149
+ let finishTui = null;
150
+ let exitResult = { exitReason: "exit" };
151
+ const requestExit = () => {
152
+ if (state.exitRequested) return;
153
+ state.exitRequested = true;
154
+ if (elapsedTimerId) {
155
+ clearInterval(elapsedTimerId);
156
+ elapsedTimerId = null;
157
+ }
158
+ if (streamWatchdogId) {
159
+ clearInterval(streamWatchdogId);
160
+ streamWatchdogId = null;
161
+ }
162
+ client.stop();
163
+ drainAndStopTuiSafely(tui).then(() => {
164
+ restoreStdio();
165
+ finishTui?.();
166
+ });
167
+ };
168
+ const abortActive = async () => {
169
+ if (!state.activeRunId) return;
170
+ const runId = state.activeRunId;
171
+ state.activeRunId = null;
172
+ assembler.drop(runId);
173
+ chatLog.dropAssistant(runId);
174
+ setActivityStatus("idle");
175
+ tui.requestRender();
176
+ await client.abortChat({
177
+ sessionKey: state.currentSessionKey,
178
+ runId
179
+ }).catch(() => {});
180
+ };
181
+ const sendMessage = (text) => {
182
+ if (state.activeRunId) {
183
+ chatLog.addSystem("A response is still in progress. Use /abort or press Escape to cancel.");
184
+ tui.requestRender();
185
+ return;
186
+ }
187
+ chatLog.addUser(text);
188
+ setActivityStatus("sending");
189
+ touchStreamingActivity();
190
+ tui.requestRender();
191
+ client.sendChat({
192
+ sessionKey: state.currentSessionKey,
193
+ message: text,
194
+ thinking: opts.thinking
195
+ }).catch((error) => {
196
+ const errorMessage = error instanceof Error ? error.message : String(error);
197
+ chatLog.addSystem(`❌ Failed to send: ${errorMessage}`);
198
+ setActivityStatus("idle");
199
+ tui.requestRender();
200
+ });
201
+ };
202
+ const handleCommand = createTuiCommandHandler({
203
+ state,
204
+ chatLog,
205
+ tui,
206
+ assembler,
207
+ isLocalMode,
208
+ abortActive,
209
+ sendMessage,
210
+ requestExit,
211
+ updateFooter
212
+ });
213
+ const { runLocalShellLine } = createLocalShellRunner({
214
+ chatLog,
215
+ tui,
216
+ editor,
217
+ openOverlay,
218
+ closeOverlay,
219
+ pauseStdioFilter: () => stdioFilter.pause(),
220
+ resumeStdioFilter: () => stdioFilter.resume(),
221
+ runWithInheritedStdio: async (work) => {
222
+ await withTuiSuspended(tui, work);
223
+ }
224
+ });
225
+ editor.onSubmit = createSubmitBurstCoalescer({
226
+ submit: createEditorSubmitHandler({
227
+ editor,
228
+ handleCommand,
229
+ sendMessage,
230
+ handleBangLine: runLocalShellLine
231
+ }),
232
+ enabled: shouldEnableWindowsGitBashPasteFallback()
233
+ });
234
+ const setSessionKey = (key) => {
235
+ state.currentSessionKey = key;
236
+ };
237
+ const clearChatForSessionSwitch = () => {
238
+ assembler.clear();
239
+ chatLog.clearAll();
240
+ clearPendingToolCallIds();
241
+ state.historyLoaded = false;
242
+ };
243
+ const loadSessionHistory = async () => {
244
+ try {
245
+ const { messages } = await client.loadHistory({
246
+ sessionKey: state.currentSessionKey,
247
+ limit: 200
248
+ });
249
+ appendHistoryToChatLog(chatLog, messages, state.toolsExpanded);
250
+ } catch {} finally {
251
+ state.historyLoaded = true;
252
+ tui.requestRender();
253
+ }
254
+ };
255
+ const pickerSvc = {
256
+ tui,
257
+ editor,
258
+ openOverlay,
259
+ closeOverlay,
260
+ chatLog,
261
+ client,
262
+ sendMessage,
263
+ refreshSessionInfo,
264
+ updateHeader,
265
+ state,
266
+ setSessionKey,
267
+ clearChatForSessionSwitch,
268
+ loadSessionHistory
269
+ };
270
+ editor.onEscape = () => void abortActive();
271
+ editor.onCtrlD = () => requestExit();
272
+ editor.onCtrlL = () => void openModelPickerOverlay(pickerSvc);
273
+ editor.onCtrlP = () => void openSessionPickerOverlay(pickerSvc);
274
+ editor.onCtrlO = () => {
275
+ state.toolsExpanded = !state.toolsExpanded;
276
+ chatLog.setToolsExpanded(state.toolsExpanded);
277
+ setActivityStatus(state.toolsExpanded ? "tools expanded" : "tools collapsed");
278
+ tui.requestRender();
279
+ };
280
+ editor.onCtrlT = () => {
281
+ state.showThinking = !state.showThinking;
282
+ updateFooter();
283
+ tui.requestRender();
284
+ };
285
+ const handleCtrlC = () => {
286
+ const now = Date.now();
287
+ const decision = resolveCtrlCAction({
288
+ hasInput: editor.getText().trim().length > 0,
289
+ now,
290
+ lastCtrlCAt: state.lastCtrlCAt
291
+ });
292
+ state.lastCtrlCAt = decision.nextLastCtrlCAt;
293
+ if (decision.action === "clear") {
294
+ editor.setText("");
295
+ setActivityStatus("cleared input; press ctrl+c again to exit");
296
+ tui.requestRender();
297
+ return;
298
+ }
299
+ if (decision.action === "exit") {
300
+ requestExit();
301
+ return;
302
+ }
303
+ setActivityStatus("press ctrl+c again to exit");
304
+ tui.requestRender();
305
+ };
306
+ editor.onCtrlC = handleCtrlC;
307
+ tui.addInputListener((data) => {
308
+ if (!matchesCtrlCSequence(data)) return void 0;
309
+ handleCtrlC();
310
+ return { consume: true };
311
+ });
312
+ streamWatchdogId = setInterval(() => {
313
+ if (!state.activeRunId) return;
314
+ if (!busyStates.has(state.activityStatus)) return;
315
+ if (Date.now() - lastStreamActivityAt < 3e4) return;
316
+ const rid = state.activeRunId;
317
+ const finalText = assembler.finalize(rid, state.showThinking);
318
+ if (finalText) chatLog.finalizeAssistant(finalText, rid);
319
+ chatLog.addSystem("⚠️ No stream activity for 30s; UI reset (connection may have stalled). Retry or check gateway.");
320
+ state.activeRunId = null;
321
+ setActivityStatus("idle");
322
+ tui.requestRender();
323
+ }, 5e3);
324
+ client.onEvent = (evt) => {
325
+ const data = evt.data ?? {};
326
+ dispatchAgentSSE(evt.event, data, state, chatLog, assembler, tui, setActivityStatus, touchStreamingActivity);
327
+ };
328
+ client.onConnected = () => {
329
+ state.isConnected = true;
330
+ setConnectionStatus(isLocalMode ? "local ready" : "gateway connected");
331
+ touchStreamingActivity();
332
+ (async () => {
333
+ await refreshSessionInfo();
334
+ await loadSessionHistory();
335
+ updateHeader();
336
+ updateFooter();
337
+ tui.requestRender();
338
+ if (!state.autoMessageSent && opts.message) {
339
+ state.autoMessageSent = true;
340
+ sendMessage(opts.message);
341
+ }
342
+ })();
343
+ };
344
+ client.onDisconnected = (reason) => {
345
+ const wasConnected = state.isConnected;
346
+ state.isConnected = false;
347
+ touchStreamingActivity();
348
+ if (isLocalMode) setConnectionStatus(`local stopped: ${reason}`);
349
+ else {
350
+ setConnectionStatus(`disconnected${wasConnected || state.historyLoaded ? ` (${reason}). Reconnecting broadcast stream…` : `. Ensure gateway is running (xopc gateway) or use --local.`}`);
351
+ if (!wasConnected && !state.historyLoaded) {
352
+ const gatewayUrl = opts.url ?? "http://localhost:3120";
353
+ chatLog.addSystem(`Cannot reach gateway at ${gatewayUrl}.\nStart the gateway (\`xopc gateway\`) or run \`xopc tui --local\` for embedded mode.`);
354
+ }
355
+ }
356
+ tui.requestRender();
357
+ };
358
+ client.onGap = (info) => {
359
+ chatLog.addSystem(`⚠️ Event gap: expected ${info.expected}, received ${info.received}. Some updates may be missing.`);
360
+ setConnectionStatus(`event gap: expected ${info.expected}, got ${info.received}`);
361
+ tui.requestRender();
362
+ };
363
+ const sigintHandler = () => handleCtrlC();
364
+ const sigtermHandler = () => requestExit();
365
+ process.on("SIGINT", sigintHandler);
366
+ process.on("SIGTERM", sigtermHandler);
367
+ updateHeader();
368
+ setConnectionStatus(isLocalMode ? "starting local runtime" : "connecting");
369
+ updateFooter();
370
+ tui.start();
371
+ client.start();
372
+ await new Promise((resolve) => {
373
+ finishTui = () => {
374
+ process.removeListener("SIGINT", sigintHandler);
375
+ process.removeListener("SIGTERM", sigtermHandler);
376
+ if (streamWatchdogId) {
377
+ clearInterval(streamWatchdogId);
378
+ streamWatchdogId = null;
379
+ }
380
+ finishTui = null;
381
+ resolve();
382
+ };
383
+ });
384
+ return exitResult;
385
+ }
386
+ //#endregion
387
+ export { createBackspaceDeduper, drainAndStopTuiSafely, isIgnorableTuiStopError, resolveCtrlCAction, runTui, stopTuiSafely, withTuiSuspended };
388
+
389
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","names":[],"sources":["../../../src/tui/tui.ts"],"sourcesContent":["import {\n CombinedAutocompleteProvider,\n Container,\n getKeybindings,\n isKeyRelease,\n Key,\n Loader,\n matchesKey,\n parseKey,\n ProcessTerminal,\n Text,\n TUI,\n} from '@mariozechner/pi-tui';\n\nimport type { TuiBackend, TuiEvent } from './tui-backend.js';\nimport { EmbeddedBackend } from './backends/embedded-backend.js';\nimport { GatewaySseBackend } from './backends/gateway-sse-backend.js';\nimport {\n clearPendingToolCallIds,\n DEFAULT_STREAMING_WATCHDOG_MS,\n dispatchAgentSSE,\n} from './tui-agent-events.js';\nimport { ChatLog } from './components/chat-log.js';\nimport { CustomEditor } from './components/custom-editor.js';\nimport { StreamAssembler } from './stream-assembler.js';\nimport { createTuiCommandHandler, getSlashCommands } from './tui-commands.js';\nimport { createLocalShellRunner } from './tui-local-shell.js';\nimport {\n createBackspaceDeduper,\n drainAndStopTuiSafely,\n resolveCtrlCAction,\n} from './tui-lifecycle.js';\nimport { openModelPickerOverlay, openSessionPickerOverlay } from './tui-picker-overlay.js';\nimport { createOverlayHandlers } from './tui-overlays.js';\nimport {\n createEditorSubmitHandler,\n createSubmitBurstCoalescer,\n shouldEnableWindowsGitBashPasteFallback,\n} from './tui-submit.js';\nimport { appendHistoryToChatLog } from './chat-history.js';\nimport { installTuiStdioFilter } from './tui-stdio-filter.js';\nimport { withTuiSuspended } from './tui-suspend.js';\nimport { editorTheme, theme } from './theme.js';\nimport { createInitialState, type TuiOptions, type TuiResult, type TuiState } from './tui-types.js';\n\nexport type { TuiOptions, TuiResult };\n\nexport {\n createBackspaceDeduper,\n drainAndStopTuiSafely,\n type DrainableTui,\n isIgnorableTuiStopError,\n resolveCtrlCAction,\n stopTuiSafely,\n} from './tui-lifecycle.js';\n\nexport { withTuiSuspended } from './tui-suspend.js';\n\nfunction matchesCtrlCSequence(data: string): boolean {\n if (isKeyRelease(data)) return false;\n if (data === '\\x03') return true;\n if (parseKey(data) === 'ctrl+c') return true;\n const kb = getKeybindings();\n return matchesKey(data, Key.ctrl('c')) || kb.matches(data, 'tui.input.copy');\n}\n\nexport async function runTui(opts: TuiOptions): Promise<TuiResult> {\n const stdioFilter = installTuiStdioFilter();\n const restoreStdio = () => stdioFilter.restore();\n\n const isLocalMode = opts.local === true;\n const sessionKey = opts.session ?? 'cli:tui';\n const state = createInitialState(sessionKey);\n const assembler = new StreamAssembler();\n\n const client: TuiBackend = isLocalMode\n ? new EmbeddedBackend()\n : new GatewaySseBackend({ url: opts.url ?? 'http://localhost:3120', token: opts.token });\n\n const tui = new TUI(new ProcessTerminal());\n const dedupeBackspace = createBackspaceDeduper();\n tui.addInputListener((data) => {\n const next = dedupeBackspace(data);\n if (next.length === 0) {\n return { consume: true };\n }\n return { data: next };\n });\n\n const header = new Text('', 1, 0);\n const statusContainer = new Container();\n const footer = new Text('', 1, 0);\n const chatLog = new ChatLog();\n const editor = new CustomEditor(tui, editorTheme);\n const root = new Container();\n root.addChild(header);\n root.addChild(chatLog);\n root.addChild(statusContainer);\n root.addChild(footer);\n root.addChild(editor);\n tui.addChild(root);\n tui.setFocus(editor);\n\n const { openOverlay, closeOverlay } = createOverlayHandlers(tui, editor);\n\n const slashCommands = getSlashCommands(isLocalMode);\n editor.setAutocompleteProvider(\n new CombinedAutocompleteProvider(\n slashCommands.map((c) => ({ name: c.name, description: c.description })),\n process.cwd(),\n ),\n );\n\n let statusText: Text | null = null;\n let statusLoader: Loader | null = null;\n let statusStartedAt: number | null = null;\n let lastActivityStatus = '';\n let elapsedTimerId: ReturnType<typeof setInterval> | null = null;\n const busyStates = new Set(['sending', 'waiting', 'streaming', 'running']);\n\n let lastStreamActivityAt = Date.now();\n let streamWatchdogId: ReturnType<typeof setInterval> | null = null;\n\n const touchStreamingActivity = () => {\n lastStreamActivityAt = Date.now();\n };\n\n const formatElapsed = (startMs: number) => {\n const totalSeconds = Math.max(0, Math.floor((Date.now() - startMs) / 1000));\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return `${minutes}m ${seconds}s`;\n };\n\n const renderStatus = () => {\n const isBusy = busyStates.has(state.activityStatus);\n if (isBusy) {\n if (!statusStartedAt || lastActivityStatus !== state.activityStatus) {\n statusStartedAt = Date.now();\n }\n if (!statusLoader) {\n statusContainer.clear();\n statusText = null;\n statusLoader = new Loader(\n tui,\n (spinner) => theme.accent(spinner),\n (text) => theme.bold(theme.accentSoft(text)),\n '',\n );\n statusContainer.addChild(statusLoader);\n }\n const elapsed = formatElapsed(statusStartedAt);\n statusLoader.setMessage(`${state.activityStatus} • ${elapsed} | ${state.connectionStatus}`);\n if (!elapsedTimerId) {\n elapsedTimerId = setInterval(() => {\n if (statusStartedAt && statusLoader) {\n const el = formatElapsed(statusStartedAt);\n statusLoader.setMessage(`${state.activityStatus} • ${el} | ${state.connectionStatus}`);\n }\n }, 1000);\n }\n } else {\n statusStartedAt = null;\n if (elapsedTimerId) {\n clearInterval(elapsedTimerId);\n elapsedTimerId = null;\n }\n statusLoader?.stop();\n statusLoader = null;\n if (!statusText) {\n statusContainer.clear();\n statusText = new Text('', 1, 0);\n statusContainer.addChild(statusText);\n }\n const text = state.activityStatus\n ? `${state.connectionStatus} | ${state.activityStatus}`\n : state.connectionStatus;\n statusText.setText(theme.dim(text));\n }\n lastActivityStatus = state.activityStatus;\n };\n\n const setActivityStatus = (status: string) => {\n state.activityStatus = status as TuiState['activityStatus'];\n renderStatus();\n };\n\n const setConnectionStatus = (text: string) => {\n state.connectionStatus = text;\n renderStatus();\n };\n\n const updateHeader = () => {\n const title = 'xopc tui';\n header.setText(\n theme.header(`${title} — ${client.connectionLabel} — session ${state.currentSessionKey}`),\n );\n };\n\n const updateFooter = () => {\n const modelLabel = state.sessionInfo.model\n ? state.sessionInfo.modelProvider\n ? `${state.sessionInfo.modelProvider}/${state.sessionInfo.model}`\n : state.sessionInfo.model\n : 'unknown';\n const tokens =\n state.sessionInfo.totalTokens != null ? `${state.sessionInfo.totalTokens} tokens` : '';\n const thinking = state.showThinking ? 'thinking:on' : '';\n const parts = [`session ${state.currentSessionKey}`, modelLabel, thinking, tokens].filter(\n Boolean,\n );\n footer.setText(theme.dim(parts.join(' | ')));\n };\n\n const refreshSessionInfo = async () => {\n try {\n state.sessionInfo = await client.getSessionInfo(state.currentSessionKey);\n updateFooter();\n tui.requestRender();\n } catch {\n // ignore\n }\n };\n\n let finishTui: (() => void) | null = null;\n let exitResult: TuiResult = { exitReason: 'exit' };\n\n const requestExit = () => {\n if (state.exitRequested) return;\n state.exitRequested = true;\n if (elapsedTimerId) {\n clearInterval(elapsedTimerId);\n elapsedTimerId = null;\n }\n if (streamWatchdogId) {\n clearInterval(streamWatchdogId);\n streamWatchdogId = null;\n }\n client.stop();\n void drainAndStopTuiSafely(tui).then(() => {\n restoreStdio();\n finishTui?.();\n });\n };\n\n const abortActive = async () => {\n if (!state.activeRunId) return;\n const runId = state.activeRunId;\n state.activeRunId = null;\n assembler.drop(runId);\n chatLog.dropAssistant(runId);\n setActivityStatus('idle');\n tui.requestRender();\n await client.abortChat({ sessionKey: state.currentSessionKey, runId }).catch(() => {});\n };\n\n const sendMessage = (text: string) => {\n if (state.activeRunId) {\n chatLog.addSystem('A response is still in progress. Use /abort or press Escape to cancel.');\n tui.requestRender();\n return;\n }\n\n chatLog.addUser(text);\n setActivityStatus('sending');\n touchStreamingActivity();\n tui.requestRender();\n\n void client\n .sendChat({\n sessionKey: state.currentSessionKey,\n message: text,\n thinking: opts.thinking,\n })\n .catch((error: unknown) => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n chatLog.addSystem(`❌ Failed to send: ${errorMessage}`);\n setActivityStatus('idle');\n tui.requestRender();\n });\n };\n\n const handleCommand = createTuiCommandHandler({\n state,\n chatLog,\n tui,\n assembler,\n isLocalMode,\n abortActive,\n sendMessage,\n requestExit,\n updateFooter,\n });\n\n const { runLocalShellLine } = createLocalShellRunner({\n chatLog,\n tui,\n editor,\n openOverlay,\n closeOverlay,\n pauseStdioFilter: () => stdioFilter.pause(),\n resumeStdioFilter: () => stdioFilter.resume(),\n runWithInheritedStdio: async (work) => {\n await withTuiSuspended(tui, work);\n },\n });\n\n const submitCore = createEditorSubmitHandler({\n editor,\n handleCommand,\n sendMessage,\n handleBangLine: runLocalShellLine,\n });\n\n editor.onSubmit = createSubmitBurstCoalescer({\n submit: submitCore,\n enabled: shouldEnableWindowsGitBashPasteFallback(),\n });\n\n const setSessionKey = (key: string) => {\n state.currentSessionKey = key;\n };\n\n const clearChatForSessionSwitch = () => {\n assembler.clear();\n chatLog.clearAll();\n clearPendingToolCallIds();\n state.historyLoaded = false;\n };\n\n const loadSessionHistory = async () => {\n try {\n const { messages } = await client.loadHistory({\n sessionKey: state.currentSessionKey,\n limit: 200,\n });\n appendHistoryToChatLog(chatLog, messages, state.toolsExpanded);\n } catch {\n // ignore; footer already hints on disconnect\n } finally {\n state.historyLoaded = true;\n tui.requestRender();\n }\n };\n\n const pickerSvc = {\n tui,\n editor,\n openOverlay,\n closeOverlay,\n chatLog,\n client,\n sendMessage,\n refreshSessionInfo,\n updateHeader,\n state,\n setSessionKey,\n clearChatForSessionSwitch,\n loadSessionHistory,\n };\n\n editor.onEscape = () => void abortActive();\n editor.onCtrlD = () => requestExit();\n editor.onCtrlL = () => void openModelPickerOverlay(pickerSvc);\n editor.onCtrlP = () => void openSessionPickerOverlay(pickerSvc);\n editor.onCtrlO = () => {\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n setActivityStatus(state.toolsExpanded ? 'tools expanded' : 'tools collapsed');\n tui.requestRender();\n };\n editor.onCtrlT = () => {\n state.showThinking = !state.showThinking;\n updateFooter();\n tui.requestRender();\n };\n\n const handleCtrlC = () => {\n const now = Date.now();\n const decision = resolveCtrlCAction({\n hasInput: editor.getText().trim().length > 0,\n now,\n lastCtrlCAt: state.lastCtrlCAt,\n });\n state.lastCtrlCAt = decision.nextLastCtrlCAt;\n if (decision.action === 'clear') {\n editor.setText('');\n setActivityStatus('cleared input; press ctrl+c again to exit');\n tui.requestRender();\n return;\n }\n if (decision.action === 'exit') {\n requestExit();\n return;\n }\n setActivityStatus('press ctrl+c again to exit');\n tui.requestRender();\n };\n editor.onCtrlC = handleCtrlC;\n\n tui.addInputListener((data) => {\n if (!matchesCtrlCSequence(data)) return undefined;\n handleCtrlC();\n return { consume: true };\n });\n\n streamWatchdogId = setInterval(() => {\n if (!state.activeRunId) return;\n if (!busyStates.has(state.activityStatus)) return;\n if (Date.now() - lastStreamActivityAt < DEFAULT_STREAMING_WATCHDOG_MS) return;\n\n const rid = state.activeRunId;\n const finalText = assembler.finalize(rid, state.showThinking);\n if (finalText) {\n chatLog.finalizeAssistant(finalText, rid);\n }\n chatLog.addSystem(\n '⚠️ No stream activity for 30s; UI reset (connection may have stalled). Retry or check gateway.',\n );\n state.activeRunId = null;\n setActivityStatus('idle');\n tui.requestRender();\n }, 5000);\n\n client.onEvent = (evt: TuiEvent) => {\n const data = (evt.data ?? {}) as Record<string, unknown>;\n dispatchAgentSSE(evt.event, data, state, chatLog, assembler, tui, setActivityStatus, touchStreamingActivity);\n };\n\n client.onConnected = () => {\n state.isConnected = true;\n setConnectionStatus(isLocalMode ? 'local ready' : 'gateway connected');\n touchStreamingActivity();\n void (async () => {\n await refreshSessionInfo();\n await loadSessionHistory();\n updateHeader();\n updateFooter();\n tui.requestRender();\n if (!state.autoMessageSent && opts.message) {\n state.autoMessageSent = true;\n sendMessage(opts.message);\n }\n })();\n };\n\n client.onDisconnected = (reason: string) => {\n const wasConnected = state.isConnected;\n state.isConnected = false;\n touchStreamingActivity();\n if (isLocalMode) {\n setConnectionStatus(`local stopped: ${reason}`);\n } else {\n const hint =\n wasConnected || state.historyLoaded\n ? ` (${reason}). Reconnecting broadcast stream…`\n : `. Ensure gateway is running (xopc gateway) or use --local.`;\n setConnectionStatus(`disconnected${hint}`);\n if (!wasConnected && !state.historyLoaded) {\n const gatewayUrl = opts.url ?? 'http://localhost:3120';\n chatLog.addSystem(\n `Cannot reach gateway at ${gatewayUrl}.\\n` +\n 'Start the gateway (`xopc gateway`) or run `xopc tui --local` for embedded mode.',\n );\n }\n }\n tui.requestRender();\n };\n\n client.onGap = (info) => {\n chatLog.addSystem(\n `⚠️ Event gap: expected ${info.expected}, received ${info.received}. Some updates may be missing.`,\n );\n setConnectionStatus(`event gap: expected ${info.expected}, got ${info.received}`);\n tui.requestRender();\n };\n\n const sigintHandler = () => handleCtrlC();\n const sigtermHandler = () => requestExit();\n process.on('SIGINT', sigintHandler);\n process.on('SIGTERM', sigtermHandler);\n\n updateHeader();\n setConnectionStatus(isLocalMode ? 'starting local runtime' : 'connecting');\n updateFooter();\n tui.start();\n client.start();\n\n await new Promise<void>((resolve) => {\n finishTui = () => {\n process.removeListener('SIGINT', sigintHandler);\n process.removeListener('SIGTERM', sigtermHandler);\n if (streamWatchdogId) {\n clearInterval(streamWatchdogId);\n streamWatchdogId = null;\n }\n finishTui = null;\n resolve();\n };\n });\n\n return exitResult;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "Personal AI assistant: CLI, gateway (HTTP/WebSocket + React console), Telegram and WeChat (Weixin) channels — TypeScript, 20+ LLM providers via pi-ai, extensions and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -54,17 +54,19 @@
54
54
  "node": ">=22.0.0"
55
55
  },
56
56
  "dependencies": {
57
- "@larksuiteoapi/node-sdk": "^1.39.0",
58
57
  "@grammyjs/runner": "^2.0.3",
59
58
  "@grammyjs/types": "^3.26.0",
60
59
  "@hono/node-server": "^2.0.0",
61
60
  "@inquirer/prompts": "^8.4.2",
61
+ "@larksuiteoapi/node-sdk": "^1.39.0",
62
62
  "@mariozechner/pi-agent-core": "^0.70.2",
63
63
  "@mariozechner/pi-ai": "^0.70.2",
64
+ "@mariozechner/pi-tui": "^0.70.6",
64
65
  "@mozilla/readability": "^0.6.0",
65
66
  "@sinclair/typebox": "^0.34.49",
66
67
  "@vscode/ripgrep": "^1.17.1",
67
68
  "adm-zip": "^0.5.17",
69
+ "chalk": "^5.6.2",
68
70
  "chromium-bidi": "15.0.0",
69
71
  "cli-table3": "^0.6.5",
70
72
  "commander": "^14.0.3",
@@ -136,7 +138,7 @@
136
138
  "skills:test": "tsx src/cli/index.ts skills test",
137
139
  "skills:validate": "tsx src/cli/index.ts skills test validate",
138
140
  "skills:security": "tsx src/cli/index.ts skills test security",
139
- "lint": "eslint",
141
+ "lint": "eslint && pnpm -C web run lint",
140
142
  "docs:dev": "vitepress dev docs",
141
143
  "docs:build": "vitepress build docs",
142
144
  "docs:preview": "vitepress preview docs",