@xopcai/xopc 0.0.47 → 0.0.49

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 (98) hide show
  1. package/dist/extensions/dingtalk/src/accounts.js +1 -1
  2. package/dist/extensions/dingtalk/src/accounts.js.map +1 -1
  3. package/dist/extensions/dingtalk/src/merge-config.js +1 -1
  4. package/dist/extensions/dingtalk/src/merge-config.js.map +1 -1
  5. package/dist/extensions/dingtalk/src/plugin.js +1 -1
  6. package/dist/extensions/dingtalk/src/plugin.js.map +1 -1
  7. package/dist/extensions/feishu/src/adapters/cli-login.js +8 -8
  8. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  9. package/dist/extensions/feishu/src/adapters/onboard-cli.js +8 -8
  10. package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -1
  11. package/dist/extensions/feishu/src/plugin.js +1 -1
  12. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  13. package/dist/extensions/feishu/src/schema/config-schema.js +2 -2
  14. package/dist/extensions/feishu/src/schema/config-schema.js.map +1 -1
  15. package/dist/extensions/feishu/src/state/accounts.js +1 -1
  16. package/dist/extensions/feishu/src/state/accounts.js.map +1 -1
  17. package/dist/extensions/feishu/src/streaming/streaming-adapter.js +82 -36
  18. package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -1
  19. package/dist/extensions/telegram/src/command-handler.js +1 -1
  20. package/dist/extensions/telegram/src/command-handler.js.map +1 -1
  21. package/dist/extensions/telegram/xopc.extension.json +1 -1
  22. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  23. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  24. package/dist/extensions/weixin/src/config-schema.js +2 -2
  25. package/dist/extensions/weixin/src/config-schema.js.map +1 -1
  26. package/dist/extensions/weixin/src/config-surface.js +1 -1
  27. package/dist/extensions/weixin/src/config-surface.js.map +1 -1
  28. package/dist/extensions/weixin/src/messaging/process-message.js +2 -2
  29. package/dist/extensions/weixin/src/messaging/process-message.js.map +1 -1
  30. package/dist/extensions/weixin/src/plugin.js +1 -1
  31. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  32. package/dist/gateway/static/root/assets/{agents-DdWPgyn-.js → agents-CQllyJhj.js} +2 -2
  33. package/dist/gateway/static/root/assets/{agents-DdWPgyn-.js.map → agents-CQllyJhj.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{apps-page-BTi_W1y1.js → apps-page-BWI3RVMh.js} +2 -2
  35. package/dist/gateway/static/root/assets/{apps-page-BTi_W1y1.js.map → apps-page-BWI3RVMh.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/channels-settings-rfmhXfC4.js +2 -0
  37. package/dist/gateway/static/root/assets/channels-settings-rfmhXfC4.js.map +1 -0
  38. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-DinMur-Z.js → cron-dreaming-jobs-BzCQy56Z.js} +2 -2
  39. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-DinMur-Z.js.map → cron-dreaming-jobs-BzCQy56Z.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{cron-page-Bu05Z2oL.js → cron-page-CjTuH_av.js} +2 -2
  41. package/dist/gateway/static/root/assets/{cron-page-Bu05Z2oL.js.map → cron-page-CjTuH_av.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/{dist-wDej8fSi.js → dist-DIeOihYP.js} +2 -2
  43. package/dist/gateway/static/root/assets/{dist-wDej8fSi.js.map → dist-DIeOihYP.js.map} +1 -1
  44. package/dist/gateway/static/root/assets/{extension-debug-page-CZBu7-zM.js → extension-debug-page-CR-4lZOn.js} +2 -2
  45. package/dist/gateway/static/root/assets/{extension-debug-page-CZBu7-zM.js.map → extension-debug-page-CR-4lZOn.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{extension-page-CnOyLPrh.js → extension-page-BNwcYj2Q.js} +2 -2
  47. package/dist/gateway/static/root/assets/{extension-page-CnOyLPrh.js.map → extension-page-BNwcYj2Q.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/{extension-settings-page-BOHn3S1a.js → extension-settings-page-CDpiZPV4.js} +2 -2
  49. package/dist/gateway/static/root/assets/{extension-settings-page-BOHn3S1a.js.map → extension-settings-page-CDpiZPV4.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{heartbeat-config-api-OqFXdrMO.js → heartbeat-config-api-CSbqK-L_.js} +2 -2
  51. package/dist/gateway/static/root/assets/{heartbeat-config-api-OqFXdrMO.js.map → heartbeat-config-api-CSbqK-L_.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/{index-DeELk--t.js → index-D3sMd_aw.js} +11 -11
  53. package/dist/gateway/static/root/assets/{index-DeELk--t.js.map → index-D3sMd_aw.js.map} +1 -1
  54. package/dist/gateway/static/root/assets/{logs-page-DiN42-yE.js → logs-page-DLcWAXU5.js} +2 -2
  55. package/dist/gateway/static/root/assets/{logs-page-DiN42-yE.js.map → logs-page-DLcWAXU5.js.map} +1 -1
  56. package/dist/gateway/static/root/assets/{sessions-page-B5oxRfRm.js → sessions-page-Ck3lS3N_.js} +2 -2
  57. package/dist/gateway/static/root/assets/{sessions-page-B5oxRfRm.js.map → sessions-page-Ck3lS3N_.js.map} +1 -1
  58. package/dist/gateway/static/root/assets/{settings-page-DaRY3XEp.js → settings-page-Dp_eFgub.js} +2 -2
  59. package/dist/gateway/static/root/assets/{settings-page-DaRY3XEp.js.map → settings-page-Dp_eFgub.js.map} +1 -1
  60. package/dist/gateway/static/root/assets/{skills-page-BGDLiQZ6.js → skills-page-ClRj9e6K.js} +2 -2
  61. package/dist/gateway/static/root/assets/{skills-page-BGDLiQZ6.js.map → skills-page-ClRj9e6K.js.map} +1 -1
  62. package/dist/gateway/static/root/assets/{use-image-provider-credentials-DcP2SYn3.js → use-image-provider-credentials-DAi5Iu8c.js} +2 -2
  63. package/dist/gateway/static/root/assets/{use-image-provider-credentials-DcP2SYn3.js.map → use-image-provider-credentials-DAi5Iu8c.js.map} +1 -1
  64. package/dist/gateway/static/root/index.html +1 -1
  65. package/dist/package.js +1 -1
  66. package/dist/src/agent/goals/checklist-judge.js +21 -17
  67. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  68. package/dist/src/agent/goals/evaluate-turn.js +6 -11
  69. package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
  70. package/dist/src/agent/goals/judge.d.ts +16 -0
  71. package/dist/src/agent/goals/judge.js +61 -13
  72. package/dist/src/agent/goals/judge.js.map +1 -1
  73. package/dist/src/agent/goals/state.d.ts +7 -0
  74. package/dist/src/agent/goals/state.js +24 -1
  75. package/dist/src/agent/goals/state.js.map +1 -1
  76. package/dist/src/agent/service.js +2 -0
  77. package/dist/src/agent/service.js.map +1 -1
  78. package/dist/src/chat-commands/builtins/model.d.ts +2 -2
  79. package/dist/src/chat-commands/builtins/model.js +10 -8
  80. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  81. package/dist/src/chat-commands/builtins/system.js +1 -1
  82. package/dist/src/chat-commands/builtins/system.js.map +1 -1
  83. package/dist/src/gateway/hono/routes/channels.js +2 -2
  84. package/dist/src/gateway/hono/routes/channels.js.map +1 -1
  85. package/dist/src/gateway/hono/routes/config.js +3 -3
  86. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  87. package/dist/src/tui/backends/embedded-backend.d.ts +1 -1
  88. package/dist/src/tui/backends/embedded-backend.js +24 -3
  89. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  90. package/dist/src/tui/backends/gateway-sse-backend.js +36 -10
  91. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  92. package/dist/src/tui/tui-commands.js +1 -1
  93. package/dist/src/tui/tui-commands.js.map +1 -1
  94. package/dist/src/tui/tui.js +12 -1
  95. package/dist/src/tui/tui.js.map +1 -1
  96. package/package.json +1 -1
  97. package/dist/gateway/static/root/assets/channels-settings-CjUmKQrC.js +0 -2
  98. package/dist/gateway/static/root/assets/channels-settings-CjUmKQrC.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
4
+ import { parseModelRef } from "../../agent/models/selection.js";
4
5
  import { consumeSSEStream, parseSSEData } from "../sse-consumer.js";
5
6
  //#region src/tui/backends/gateway-sse-backend.ts
6
7
  init_logger();
@@ -160,17 +161,42 @@ var GatewaySseBackend = class {
160
161
  }
161
162
  }
162
163
  async getSessionInfo(sessionKey) {
164
+ const out = {};
163
165
  try {
164
- const res = await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}`, this.token);
165
- if (!res.ok) return {};
166
- const s = (await res.json()).session;
167
- if (!s) return {};
168
- return {
169
- displayName: s.name,
170
- totalTokens: s.estimatedTokens ?? void 0,
171
- model: typeof s.customData?.model === "string" ? s.customData.model : typeof s.customData?.modelRef === "string" ? s.customData.modelRef : void 0,
172
- modelProvider: typeof s.customData?.modelProvider === "string" ? s.customData.modelProvider : void 0
173
- };
166
+ const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;
167
+ const [sessionRes, agentCfgRes] = await Promise.all([gatewayFetch(this.baseUrl, sessionPath, this.token), gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token)]);
168
+ let session;
169
+ if (sessionRes.ok) {
170
+ session = (await sessionRes.json()).session;
171
+ if (session) {
172
+ if (session.name) out.displayName = session.name;
173
+ if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;
174
+ }
175
+ }
176
+ if (agentCfgRes.ok) {
177
+ const p = (await agentCfgRes.json()).payload;
178
+ if (p?.model && typeof p.model === "string") {
179
+ const parsed = parseModelRef(p.model);
180
+ if (parsed) {
181
+ out.model = parsed.model;
182
+ out.modelProvider = parsed.provider;
183
+ } else out.model = p.model;
184
+ }
185
+ if (p?.thinkingLevel && typeof p.thinkingLevel === "string") out.thinkingLevel = p.thinkingLevel;
186
+ }
187
+ if (!out.model && session?.customData) {
188
+ const cd = session.customData;
189
+ const ref = typeof cd.model === "string" ? cd.model : typeof cd.modelRef === "string" ? cd.modelRef : void 0;
190
+ if (ref) {
191
+ const parsed = parseModelRef(ref);
192
+ if (parsed) {
193
+ out.model = parsed.model;
194
+ out.modelProvider = parsed.provider;
195
+ } else out.model = ref;
196
+ }
197
+ if (!out.modelProvider && typeof cd.modelProvider === "string") out.modelProvider = cd.modelProvider;
198
+ }
199
+ return out;
174
200
  } catch {
175
201
  return {};
176
202
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n // Match EmbeddedBackend: set activeRunId before any token/tool events so TUI state stays on one\n // runId (avoids assistant under \"default\" and tools under the real uuid).\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: { content: body.error?.message ?? `Gateway error: ${res.status}` },\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n this.onEvent?.({\n event: 'token',\n data: { content: json.payload.content },\n });\n this.onEvent?.({ event: 'result', data: { ok: true } });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ''}`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as { ok?: boolean; payload?: { messages?: HistoryMessage[] } };\n return { messages: json.payload?.messages ?? [] };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n }));\n } catch {\n return [];\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n );\n if (!res.ok) return {};\n const json = (await res.json()) as {\n session?: {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n };\n const s = json.session;\n if (!s) return {};\n return {\n displayName: s.name,\n totalTokens: s.estimatedTokens ?? undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : undefined,\n modelProvider:\n typeof s.customData?.modelProvider === 'string' ? s.customData.modelProvider : undefined,\n };\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return json.payload?.models ?? [];\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n ).catch(() => {});\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n ).catch(() => {});\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n // Match EmbeddedBackend: set activeRunId before any token/tool events so TUI state stays on one\n // runId (avoids assistant under \"default\" and tools under the real uuid).\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: { content: body.error?.message ?? `Gateway error: ${res.status}` },\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n this.onEvent?.({\n event: 'token',\n data: { content: json.payload.content },\n });\n this.onEvent?.({ event: 'result', data: { ok: true } });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ''}`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as { ok?: boolean; payload?: { messages?: HistoryMessage[] } };\n return { messages: json.payload?.messages ?? [] };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n }));\n } catch {\n return [];\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n const out: SessionInfo = {};\n try {\n const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;\n const [sessionRes, agentCfgRes] = await Promise.all([\n gatewayFetch(this.baseUrl, sessionPath, this.token),\n gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token),\n ]);\n\n type SessionRow = {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n let session: SessionRow | undefined;\n\n if (sessionRes.ok) {\n const json = (await sessionRes.json()) as { session?: SessionRow };\n session = json.session;\n if (session) {\n if (session.name) out.displayName = session.name;\n if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;\n }\n }\n\n if (agentCfgRes.ok) {\n const json = (await agentCfgRes.json()) as {\n ok?: boolean;\n payload?: { model?: string; thinkingLevel?: string };\n };\n const p = json.payload;\n if (p?.model && typeof p.model === 'string') {\n const parsed = parseModelRef(p.model);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = p.model;\n }\n }\n if (p?.thinkingLevel && typeof p.thinkingLevel === 'string') {\n out.thinkingLevel = p.thinkingLevel;\n }\n }\n\n if (!out.model && session?.customData) {\n const cd = session.customData;\n const ref =\n typeof cd.model === 'string'\n ? cd.model\n : typeof cd.modelRef === 'string'\n ? cd.modelRef\n : undefined;\n if (ref) {\n const parsed = parseModelRef(ref);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = ref;\n }\n }\n if (!out.modelProvider && typeof cd.modelProvider === 'string') {\n out.modelProvider = cd.modelProvider;\n }\n }\n\n return out;\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return json.payload?.models ?? [];\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n ).catch(() => {});\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n ).catch(() => {});\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":";;;;;;aAEqD;AAYrD,MAAM,MAAM,aAAa,iBAAiB;;AAQ1C,eAAe,aACb,SACA,MACA,OACA,MACmB;CACnB,MAAM,UAAkC;EACtC,gBAAgB;EAChB,GAAI,QAAQ,EAAE,eAAe,UAAU,SAAS,GAAG,EAAE;EACrD,GAAI,MAAM;EACX;AACD,QAAO,MAAM,GAAG,UAAU,QAAQ;EAAE,GAAG;EAAM;EAAS,CAAC;;;;;;;;;AAUzD,IAAa,oBAAb,MAAqD;CACnD;CACA;CACA,aAA6C;CAC7C,YAA4C;CAE5C;CACA;CACA;CACA;CAEA,YAAY,MAAyB;AACnC,OAAK,UAAU,KAAK,IAAI,QAAQ,QAAQ,GAAG;AAC3C,OAAK,QAAQ,KAAK;;CAGpB,IAAI,kBAA0B;AAC5B,SAAO,KAAK;;CAGd,QAAc;AACZ,OAAK,kBAAkB;;CAGzB,OAAa;AACX,OAAK,YAAY,OAAO;AACxB,OAAK,aAAa;AAClB,OAAK,WAAW,OAAO;AACvB,OAAK,YAAY;;CAKnB,MAAM,SAAS,MAAmD;AAChE,OAAK,WAAW,OAAO;AACvB,OAAK,YAAY,IAAI,iBAAiB;EACtC,MAAM,SAAS,KAAK,UAAU;EAC9B,MAAM,QAAQ,OAAO,YAAY;AAIjC,OAAK,UAAU;GAAE,OAAO;GAAU,MAAM;IAAE,QAAQ;IAAW;IAAO;GAAE,CAAC;AAIvE,GAAM,YAAY;AAChB,OAAI;IACF,MAAM,MAAM,MAAM,aAAa,KAAK,SAAS,cAAc,KAAK,OAAO;KACrE,QAAQ;KACR,SAAS,EAAE,QAAQ,qBAAqB;KACxC,MAAM,KAAK,UAAU;MAInB,SAAS,KAAK,QAAQ,WAAW,CAAC,WAAW,IAAI,GAC7C,KAAK,UACL,yBAAyB,KAAK,QAAQ;MAC1C,SAAS;MACT,YAAY,KAAK;MACjB,UAAU,KAAK;MAChB,CAAC;KACF;KACD,CAAC;AAEF,QAAI,CAAC,IAAI,IAAI;KACX,MAAM,OAAQ,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;AAChD,UAAK,UAAU;MACb,OAAO;MACP,MAAM,EAAE,SAAS,KAAK,OAAO,WAAW,kBAAkB,IAAI,UAAU;MACzE,CAAC;AACF;;AAKF,SAFoB,IAAI,QAAQ,IAAI,eAAe,IAAI,IAEvC,SAAS,oBAAoB,IAAI,IAAI,KACnD,OAAM,iBACJ,IAAI,OACH,aAAa;AACZ,SAAI,OAAO,QAAS;KACpB,MAAM,OAAO,aAAsC,SAAS,KAAK;AACjE,SAAI,CAAC,KAAM;AACX,UAAK,UAAU;MAAE,OAAO,SAAS;MAAO;MAAM,CAAC;OAEjD,OACD;SACI;KACL,MAAM,OAAQ,MAAM,IAAI,MAAM;AAC9B,SAAI,KAAK,MAAM,KAAK,SAAS,SAAS;AACpC,WAAK,UAAU;OACb,OAAO;OACP,MAAM,EAAE,SAAS,KAAK,QAAQ,SAAS;OACxC,CAAC;AACF,WAAK,UAAU;OAAE,OAAO;OAAU,MAAM,EAAE,IAAI,MAAM;OAAE,CAAC;;;YAGpD,OAAO;AACd,QAAI,OAAO,QAAS;IACpB,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,SAAK,UAAU;KAAE,OAAO;KAAS,MAAM,EAAE,SAAS,cAAc;KAAE,CAAC;;MAEnE;AAEJ,SAAO,EAAE,OAAO;;CAGlB,MAAM,UAAU,MAAuE;AACrF,OAAK,WAAW,OAAO;AACvB,OAAK,YAAY;AACjB,MAAI;AAMF,UAAO,EAAE,KAAI,OADO,MAJF,aAAa,KAAK,SAAS,oBAAoB,KAAK,OAAO;IAC3E,QAAQ;IACR,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC;IAC5C,CAAC,EACsB,MAAM,EACZ,MAAM,OAAO;UACzB;AACN,UAAO,EAAE,IAAI,OAAO;;;CAMxB,MAAM,YAAY,MAG0B;AAC1C,MAAI;GACF,MAAM,SAAS,IAAI,iBAAiB;AACpC,OAAI,KAAK,MAAO,QAAO,IAAI,SAAS,OAAO,KAAK,MAAM,CAAC;GACvD,MAAM,KAAK,OAAO,UAAU;GAC5B,MAAM,MAAM,MAAM,aAChB,KAAK,SACL,iBAAiB,mBAAmB,KAAK,WAAW,CAAC,WAAW,KAAK,IAAI,OAAO,MAChF,KAAK,MACN;AACD,OAAI,CAAC,IAAI,GAAI,QAAO,EAAE,UAAU,EAAE,EAAE;AAEpC,UAAO,EAAE,WAAU,MADC,IAAI,MAAM,EACN,SAAS,YAAY,EAAE,EAAE;WAC1C,OAAO;GACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,OAAI,KAAK;IAAE,KAAK;IAAO;IAAc,EAAE,2BAA2B,eAAe;AACjF,UAAO,EAAE,UAAU,EAAE,EAAE;;;CAI3B,MAAM,eAA0C;AAC9C,MAAI;GACF,MAAM,MAAM,MAAM,aAAa,KAAK,SAAS,iBAAiB,KAAK,MAAM;AACzE,OAAI,CAAC,IAAI,GAAI,QAAO,EAAE;AAUtB,YAAQ,MATY,IAAI,MAAM,EASjB,SAAS,EAAE,EAAE,KAAK,OAAO;IACpC,KAAK,EAAE;IACP,aAAa,EAAE;IACf,WAAW,EAAE,YAAY,KAAK,MAAM,EAAE,UAAU,GAAG,KAAA;IACnD,aAAa,EAAE,mBAAmB;IAClC,OACE,OAAO,EAAE,YAAY,UAAU,WAC3B,EAAE,WAAW,QACb,OAAO,EAAE,YAAY,aAAa,WAChC,EAAE,WAAW,WACb;IACT,EAAE;UACG;AACN,UAAO,EAAE;;;CAIb,MAAM,eAAe,YAA0C;EAC7D,MAAM,MAAmB,EAAE;AAC3B,MAAI;GACF,MAAM,cAAc,iBAAiB,mBAAmB,WAAW;GACnE,MAAM,CAAC,YAAY,eAAe,MAAM,QAAQ,IAAI,CAClD,aAAa,KAAK,SAAS,aAAa,KAAK,MAAM,EACnD,aAAa,KAAK,SAAS,GAAG,YAAY,gBAAgB,KAAK,MAAM,CACtE,CAAC;GAOF,IAAI;AAEJ,OAAI,WAAW,IAAI;AAEjB,eAAU,MADU,WAAW,MAAM,EACtB;AACf,QAAI,SAAS;AACX,SAAI,QAAQ,KAAM,KAAI,cAAc,QAAQ;AAC5C,SAAI,QAAQ,mBAAmB,KAAM,KAAI,cAAc,QAAQ;;;AAInE,OAAI,YAAY,IAAI;IAKlB,MAAM,KAAI,MAJU,YAAY,MAAM,EAIvB;AACf,QAAI,GAAG,SAAS,OAAO,EAAE,UAAU,UAAU;KAC3C,MAAM,SAAS,cAAc,EAAE,MAAM;AACrC,SAAI,QAAQ;AACV,UAAI,QAAQ,OAAO;AACnB,UAAI,gBAAgB,OAAO;WAE3B,KAAI,QAAQ,EAAE;;AAGlB,QAAI,GAAG,iBAAiB,OAAO,EAAE,kBAAkB,SACjD,KAAI,gBAAgB,EAAE;;AAI1B,OAAI,CAAC,IAAI,SAAS,SAAS,YAAY;IACrC,MAAM,KAAK,QAAQ;IACnB,MAAM,MACJ,OAAO,GAAG,UAAU,WAChB,GAAG,QACH,OAAO,GAAG,aAAa,WACrB,GAAG,WACH,KAAA;AACR,QAAI,KAAK;KACP,MAAM,SAAS,cAAc,IAAI;AACjC,SAAI,QAAQ;AACV,UAAI,QAAQ,OAAO;AACnB,UAAI,gBAAgB,OAAO;WAE3B,KAAI,QAAQ;;AAGhB,QAAI,CAAC,IAAI,iBAAiB,OAAO,GAAG,kBAAkB,SACpD,KAAI,gBAAgB,GAAG;;AAI3B,UAAO;UACD;AACN,UAAO,EAAE;;;CAIb,MAAM,aAAwC;AAC5C,MAAI;GACF,MAAM,MAAM,MAAM,aAAa,KAAK,SAAS,eAAe,KAAK,MAAM;AACvE,OAAI,CAAC,IAAI,GAAI,QAAO,EAAE;AAKtB,WAAO,MAJa,IAAI,MAAM,EAIlB,SAAS,UAAU,EAAE;UAC3B;AACN,UAAO,EAAE;;;CAIb,MAAM,aAAa,YAAmC;AACpD,QAAM,aACJ,KAAK,SACL,iBAAiB,mBAAmB,WAAW,IAC/C,KAAK,OACL,EAAE,QAAQ,UAAU,CACrB,CAAC,YAAY,GAAG;;CAGnB,MAAM,aAAa,YAAoB,OAA+C;AACpF,QAAM,aACJ,KAAK,SACL,iBAAiB,mBAAmB,WAAW,IAC/C,KAAK,OACL;GAAE,QAAQ;GAAS,MAAM,KAAK,UAAU,MAAM;GAAE,CACjD,CAAC,YAAY,GAAG;;CAKnB,mBAAiC;AAC/B,OAAK,YAAY,OAAO;AACxB,OAAK,aAAa,IAAI,iBAAiB;EAEvC,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,aAAa;AACjD,MAAI,KAAK,MAAO,KAAI,aAAa,IAAI,SAAS,KAAK,MAAM;EAEzD,MAAM,UAAU,YAAY;AAC1B,OAAI;IACF,MAAM,MAAM,MAAM,MAAM,IAAI,UAAU,EAAE;KACtC,QAAQ,KAAK,WAAY;KACzB,SAAS,EAAE,QAAQ,qBAAqB;KACzC,CAAC;AAEF,QAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM;AACxB,UAAK,iBAAiB,uBAAuB,IAAI,SAAS;AAC1D,UAAK,mBAAmB;AACxB;;AAGF,SAAK,eAAe;AAEpB,UAAM,iBACJ,IAAI,OACH,aAAa;AACZ,SAAI,SAAS,UAAU,YAAa;AACpC,SAAI,SAAS,UAAU,OAAO;MAC5B,MAAM,UAAU,aAAa,SAAS,KAAK;AAI3C,UACE,WACA,OAAO,QAAQ,aAAa,YAC5B,OAAO,QAAQ,aAAa,SAE5B,MAAK,QAAQ;OAAE,UAAU,QAAQ;OAAU,UAAU,QAAQ;OAAU,CAAC;AAE1E;;KAEF,MAAM,OAAO,aAAa,SAAS,KAAK;AACxC,SAAI,SAAS,KACX,MAAK,UAAU;MAAE,OAAO,SAAS;MAAO;MAAM,CAAC;OAGnD,KAAK,WAAY,OAClB;AAGD,QAAI,CAAC,KAAK,YAAY,OAAO,SAAS;AACpC,UAAK,iBAAiB,gBAAgB;AACtC,UAAK,mBAAmB;;YAEnB,OAAO;AACd,QAAI,KAAK,YAAY,OAAO,QAAS;IACrC,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,QAAI,KAAK;KAAE,KAAK;KAAO;KAAc,EAAE,wBAAwB,eAAe;AAC9E,SAAK,iBAAiB,aAAa;AACnC,SAAK,mBAAmB;;;AAIvB,WAAS;;CAGhB,oBAAkC;AAChC,MAAI,KAAK,YAAY,OAAO,QAAS;AACrC,mBAAiB;AACf,OAAI,CAAC,KAAK,YAAY,OAAO,QAC3B,MAAK,kBAAkB;KAExB,IAAK"}
@@ -28,7 +28,7 @@ function getSlashCommands(_isLocal) {
28
28
  },
29
29
  {
30
30
  name: "switch",
31
- description: "Switch model (e.g. /switch openai/gpt-4o)"
31
+ description: "Switch model copy `provider/model` from /models"
32
32
  },
33
33
  {
34
34
  name: "usage",
@@ -1 +1 @@
1
- {"version":3,"file":"tui-commands.js","names":[],"sources":["../../../src/tui/tui-commands.ts"],"sourcesContent":["import type { KeybindingsManager, TUI } from '@earendil-works/pi-tui';\n\nimport type { ChatLog } from './components/chat-log.js';\nimport { formatXopcTuiHotkeys } from './format-tui-hotkeys.js';\nimport type { StreamAssembler } from './stream-assembler.js';\nimport type { TuiState } from './tui-types.js';\n\ninterface SlashCommandDef {\n name: string;\n description: string;\n}\n\nexport function getSlashCommands(_isLocal: boolean): SlashCommandDef[] {\n return [\n { name: 'help', description: 'Show available commands' },\n { name: 'abort', description: 'Abort active run (or press Escape)' },\n { name: 'tools', description: 'Toggle tool output expanded/collapsed (or Ctrl+O)' },\n { name: 'thinking', description: 'Toggle thinking display (or Ctrl+T)' },\n { name: 'exit', description: 'Exit the TUI' },\n { name: 'models', description: 'List available models' },\n { name: 'switch', description: 'Switch model (e.g. /switch openai/gpt-4o)' },\n { name: 'usage', description: 'Show token usage statistics' },\n { name: 'new', description: 'Start a new session' },\n { name: 'clear', description: 'Clear current session' },\n { name: 'list', description: 'List sessions' },\n { name: 'compact', description: 'Compact session history' },\n { name: 'think', description: 'Set thinking level (e.g. /think high)' },\n { name: 'reasoning', description: 'Set reasoning visibility (e.g. /reasoning stream)' },\n { name: 'verbose', description: 'Toggle verbose mode' },\n { name: 'status', description: 'Show agent status' },\n { name: 'config', description: 'Show or update configuration' },\n { name: 'context', description: 'Show context budget' },\n { name: 'btw', description: 'Side question without saving to session' },\n { name: 'export', description: 'Export session (markdown/html/json)' },\n { name: 'settings', description: 'Show current settings' },\n { name: 'start', description: 'Show welcome message' },\n { name: 'hotkeys', description: 'Show resolved keyboard shortcuts (pi-style)' },\n ];\n}\n\nexport function formatTuiHelpText(isLocal: boolean): string {\n const commands = getSlashCommands(isLocal);\n const lines = ['Available commands:'];\n for (const c of commands) {\n lines.push(` /${c.name} — ${c.description}`);\n }\n lines.push('', 'Keyboard shortcuts (defaults align with pi coding-agent where noted):');\n lines.push(' Escape — Abort active run');\n lines.push(' Shift+Tab — Cycle /think level');\n lines.push(' Ctrl+P / Shift+Ctrl+P — Next / previous model (/switch)');\n lines.push(' Ctrl+L — Model picker');\n lines.push(' Ctrl+Shift+P — Session picker');\n lines.push(' Ctrl+O — Toggle tool output');\n lines.push(' Ctrl+T — Toggle thinking block display');\n lines.push(' Ctrl+G — Edit draft in $EDITOR');\n lines.push(' Ctrl+Z — Suspend to shell (Unix)');\n lines.push(' Alt+Enter — Queue follow-up while busy (same as Enter when idle)');\n lines.push(' Alt+Up — Restore queued messages into the editor');\n lines.push(' Ctrl+V (mac/Linux) / Alt+V (Win) — Clipboard image (stub in xopc)');\n lines.push(' Ctrl+C — Clear input; repeat within ~1s to exit when empty');\n lines.push(' Ctrl+D — Exit when input empty');\n lines.push(' !cmd — Local shell (gated; runs on this machine)');\n lines.push('', 'Use /hotkeys for the resolved binding list from the active keymap.');\n return lines.join('\\n');\n}\n\nexport type CommandHandlerDeps = {\n state: TuiState;\n chatLog: ChatLog;\n tui: TUI;\n assembler: StreamAssembler;\n isLocalMode: boolean;\n abortActive: () => Promise<void>;\n sendMessage: (text: string) => void;\n requestExit: () => void;\n updateFooter: () => void;\n keybindings: KeybindingsManager;\n};\n\nexport function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void {\n const {\n state,\n chatLog,\n tui,\n assembler,\n isLocalMode,\n abortActive,\n sendMessage,\n requestExit,\n updateFooter,\n keybindings,\n } = deps;\n\n return (input: string) => {\n const trimmed = input.replace(/^\\//, '').trim();\n const [commandName] = trimmed.split(/\\s+/);\n const normalizedCommand = (commandName ?? '').toLowerCase();\n\n switch (normalizedCommand) {\n case 'help':\n chatLog.addSystem(formatTuiHelpText(isLocalMode));\n tui.requestRender();\n return;\n case 'hotkeys':\n case 'keys':\n chatLog.addSystem(formatXopcTuiHotkeys(keybindings));\n tui.requestRender();\n return;\n case 'exit':\n case 'quit':\n requestExit();\n return;\n case 'abort':\n case 'stop':\n case 'cancel':\n void abortActive().then(() => {\n chatLog.addSystem('Aborted.');\n tui.requestRender();\n });\n return;\n case 'tools':\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n chatLog.addSystem(`Tools: ${state.toolsExpanded ? 'expanded' : 'collapsed'}`);\n tui.requestRender();\n return;\n case 'thinking':\n state.showThinking = !state.showThinking;\n chatLog.addSystem(`Thinking display: ${state.showThinking ? 'on' : 'off'}`);\n updateFooter();\n tui.requestRender();\n return;\n default:\n break;\n }\n\n switch (normalizedCommand) {\n case 'new':\n case 'reset':\n case 'restart':\n case 'clear': {\n void abortActive().then(() => {\n assembler.clear();\n chatLog.clearAll();\n state.messageFollowUpQueue.length = 0;\n tui.requestRender();\n sendMessage(input);\n });\n return;\n }\n default:\n break;\n }\n\n sendMessage(input);\n };\n}\n"],"mappings":";;AAYA,SAAgB,iBAAiB,UAAsC;AACrE,QAAO;EACL;GAAE,MAAM;GAAQ,aAAa;GAA2B;EACxD;GAAE,MAAM;GAAS,aAAa;GAAsC;EACpE;GAAE,MAAM;GAAS,aAAa;GAAqD;EACnF;GAAE,MAAM;GAAY,aAAa;GAAuC;EACxE;GAAE,MAAM;GAAQ,aAAa;GAAgB;EAC7C;GAAE,MAAM;GAAU,aAAa;GAAyB;EACxD;GAAE,MAAM;GAAU,aAAa;GAA6C;EAC5E;GAAE,MAAM;GAAS,aAAa;GAA+B;EAC7D;GAAE,MAAM;GAAO,aAAa;GAAuB;EACnD;GAAE,MAAM;GAAS,aAAa;GAAyB;EACvD;GAAE,MAAM;GAAQ,aAAa;GAAiB;EAC9C;GAAE,MAAM;GAAW,aAAa;GAA2B;EAC3D;GAAE,MAAM;GAAS,aAAa;GAAyC;EACvE;GAAE,MAAM;GAAa,aAAa;GAAqD;EACvF;GAAE,MAAM;GAAW,aAAa;GAAuB;EACvD;GAAE,MAAM;GAAU,aAAa;GAAqB;EACpD;GAAE,MAAM;GAAU,aAAa;GAAgC;EAC/D;GAAE,MAAM;GAAW,aAAa;GAAuB;EACvD;GAAE,MAAM;GAAO,aAAa;GAA2C;EACvE;GAAE,MAAM;GAAU,aAAa;GAAuC;EACtE;GAAE,MAAM;GAAY,aAAa;GAAyB;EAC1D;GAAE,MAAM;GAAS,aAAa;GAAwB;EACtD;GAAE,MAAM;GAAW,aAAa;GAA+C;EAChF;;AAGH,SAAgB,kBAAkB,SAA0B;CAC1D,MAAM,WAAW,iBAAiB,QAAQ;CAC1C,MAAM,QAAQ,CAAC,sBAAsB;AACrC,MAAK,MAAM,KAAK,SACd,OAAM,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,cAAc;AAE/C,OAAM,KAAK,IAAI,wEAAwE;AACvF,OAAM,KAAK,8BAA8B;AACzC,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,4DAA4D;AACvE,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,kCAAkC;AAC7C,OAAM,KAAK,gCAAgC;AAC3C,OAAM,KAAK,2CAA2C;AACtD,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,qCAAqC;AAChD,OAAM,KAAK,qEAAqE;AAChF,OAAM,KAAK,qDAAqD;AAChE,OAAM,KAAK,sEAAsE;AACjF,OAAM,KAAK,+DAA+D;AAC1E,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,qDAAqD;AAChE,OAAM,KAAK,IAAI,qEAAqE;AACpF,QAAO,MAAM,KAAK,KAAK;;AAgBzB,SAAgB,wBAAwB,MAAmD;CACzF,MAAM,EACJ,OACA,SACA,KACA,WACA,aACA,aACA,aACA,aACA,cACA,gBACE;AAEJ,SAAQ,UAAkB;EAExB,MAAM,CAAC,eADS,MAAM,QAAQ,OAAO,GAAG,CAAC,MACZ,CAAC,MAAM,MAAM;EAC1C,MAAM,qBAAqB,eAAe,IAAI,aAAa;AAE3D,UAAQ,mBAAR;GACE,KAAK;AACH,YAAQ,UAAU,kBAAkB,YAAY,CAAC;AACjD,QAAI,eAAe;AACnB;GACF,KAAK;GACL,KAAK;AACH,YAAQ,UAAU,qBAAqB,YAAY,CAAC;AACpD,QAAI,eAAe;AACnB;GACF,KAAK;GACL,KAAK;AACH,iBAAa;AACb;GACF,KAAK;GACL,KAAK;GACL,KAAK;AACE,iBAAa,CAAC,WAAW;AAC5B,aAAQ,UAAU,WAAW;AAC7B,SAAI,eAAe;MACnB;AACF;GACF,KAAK;AACH,UAAM,gBAAgB,CAAC,MAAM;AAC7B,YAAQ,iBAAiB,MAAM,cAAc;AAC7C,YAAQ,UAAU,UAAU,MAAM,gBAAgB,aAAa,cAAc;AAC7E,QAAI,eAAe;AACnB;GACF,KAAK;AACH,UAAM,eAAe,CAAC,MAAM;AAC5B,YAAQ,UAAU,qBAAqB,MAAM,eAAe,OAAO,QAAQ;AAC3E,kBAAc;AACd,QAAI,eAAe;AACnB;GACF,QACE;;AAGJ,UAAQ,mBAAR;GACE,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;AACE,iBAAa,CAAC,WAAW;AAC5B,eAAU,OAAO;AACjB,aAAQ,UAAU;AAClB,WAAM,qBAAqB,SAAS;AACpC,SAAI,eAAe;AACnB,iBAAY,MAAM;MAClB;AACF;GAEF,QACE;;AAGJ,cAAY,MAAM"}
1
+ {"version":3,"file":"tui-commands.js","names":[],"sources":["../../../src/tui/tui-commands.ts"],"sourcesContent":["import type { KeybindingsManager, TUI } from '@earendil-works/pi-tui';\n\nimport type { ChatLog } from './components/chat-log.js';\nimport { formatXopcTuiHotkeys } from './format-tui-hotkeys.js';\nimport type { StreamAssembler } from './stream-assembler.js';\nimport type { TuiState } from './tui-types.js';\n\ninterface SlashCommandDef {\n name: string;\n description: string;\n}\n\nexport function getSlashCommands(_isLocal: boolean): SlashCommandDef[] {\n return [\n { name: 'help', description: 'Show available commands' },\n { name: 'abort', description: 'Abort active run (or press Escape)' },\n { name: 'tools', description: 'Toggle tool output expanded/collapsed (or Ctrl+O)' },\n { name: 'thinking', description: 'Toggle thinking display (or Ctrl+T)' },\n { name: 'exit', description: 'Exit the TUI' },\n { name: 'models', description: 'List available models' },\n { name: 'switch', description: 'Switch model copy `provider/model` from /models' },\n { name: 'usage', description: 'Show token usage statistics' },\n { name: 'new', description: 'Start a new session' },\n { name: 'clear', description: 'Clear current session' },\n { name: 'list', description: 'List sessions' },\n { name: 'compact', description: 'Compact session history' },\n { name: 'think', description: 'Set thinking level (e.g. /think high)' },\n { name: 'reasoning', description: 'Set reasoning visibility (e.g. /reasoning stream)' },\n { name: 'verbose', description: 'Toggle verbose mode' },\n { name: 'status', description: 'Show agent status' },\n { name: 'config', description: 'Show or update configuration' },\n { name: 'context', description: 'Show context budget' },\n { name: 'btw', description: 'Side question without saving to session' },\n { name: 'export', description: 'Export session (markdown/html/json)' },\n { name: 'settings', description: 'Show current settings' },\n { name: 'start', description: 'Show welcome message' },\n { name: 'hotkeys', description: 'Show resolved keyboard shortcuts (pi-style)' },\n ];\n}\n\nexport function formatTuiHelpText(isLocal: boolean): string {\n const commands = getSlashCommands(isLocal);\n const lines = ['Available commands:'];\n for (const c of commands) {\n lines.push(` /${c.name} — ${c.description}`);\n }\n lines.push('', 'Keyboard shortcuts (defaults align with pi coding-agent where noted):');\n lines.push(' Escape — Abort active run');\n lines.push(' Shift+Tab — Cycle /think level');\n lines.push(' Ctrl+P / Shift+Ctrl+P — Next / previous model (/switch)');\n lines.push(' Ctrl+L — Model picker');\n lines.push(' Ctrl+Shift+P — Session picker');\n lines.push(' Ctrl+O — Toggle tool output');\n lines.push(' Ctrl+T — Toggle thinking block display');\n lines.push(' Ctrl+G — Edit draft in $EDITOR');\n lines.push(' Ctrl+Z — Suspend to shell (Unix)');\n lines.push(' Alt+Enter — Queue follow-up while busy (same as Enter when idle)');\n lines.push(' Alt+Up — Restore queued messages into the editor');\n lines.push(' Ctrl+V (mac/Linux) / Alt+V (Win) — Clipboard image (stub in xopc)');\n lines.push(' Ctrl+C — Clear input; repeat within ~1s to exit when empty');\n lines.push(' Ctrl+D — Exit when input empty');\n lines.push(' !cmd — Local shell (gated; runs on this machine)');\n lines.push('', 'Use /hotkeys for the resolved binding list from the active keymap.');\n return lines.join('\\n');\n}\n\nexport type CommandHandlerDeps = {\n state: TuiState;\n chatLog: ChatLog;\n tui: TUI;\n assembler: StreamAssembler;\n isLocalMode: boolean;\n abortActive: () => Promise<void>;\n sendMessage: (text: string) => void;\n requestExit: () => void;\n updateFooter: () => void;\n keybindings: KeybindingsManager;\n};\n\nexport function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void {\n const {\n state,\n chatLog,\n tui,\n assembler,\n isLocalMode,\n abortActive,\n sendMessage,\n requestExit,\n updateFooter,\n keybindings,\n } = deps;\n\n return (input: string) => {\n const trimmed = input.replace(/^\\//, '').trim();\n const [commandName] = trimmed.split(/\\s+/);\n const normalizedCommand = (commandName ?? '').toLowerCase();\n\n switch (normalizedCommand) {\n case 'help':\n chatLog.addSystem(formatTuiHelpText(isLocalMode));\n tui.requestRender();\n return;\n case 'hotkeys':\n case 'keys':\n chatLog.addSystem(formatXopcTuiHotkeys(keybindings));\n tui.requestRender();\n return;\n case 'exit':\n case 'quit':\n requestExit();\n return;\n case 'abort':\n case 'stop':\n case 'cancel':\n void abortActive().then(() => {\n chatLog.addSystem('Aborted.');\n tui.requestRender();\n });\n return;\n case 'tools':\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n chatLog.addSystem(`Tools: ${state.toolsExpanded ? 'expanded' : 'collapsed'}`);\n tui.requestRender();\n return;\n case 'thinking':\n state.showThinking = !state.showThinking;\n chatLog.addSystem(`Thinking display: ${state.showThinking ? 'on' : 'off'}`);\n updateFooter();\n tui.requestRender();\n return;\n default:\n break;\n }\n\n switch (normalizedCommand) {\n case 'new':\n case 'reset':\n case 'restart':\n case 'clear': {\n void abortActive().then(() => {\n assembler.clear();\n chatLog.clearAll();\n state.messageFollowUpQueue.length = 0;\n tui.requestRender();\n sendMessage(input);\n });\n return;\n }\n default:\n break;\n }\n\n sendMessage(input);\n };\n}\n"],"mappings":"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"}
@@ -457,12 +457,23 @@ async function runTui(opts) {
457
457
  chatLog.addSystem("⚠️ No stream activity for 30s; UI reset (connection may have stalled). Retry or check gateway.");
458
458
  state.activeRunId = null;
459
459
  setActivityStatus("idle");
460
+ refreshSessionInfo().finally(() => {
461
+ updateFooter();
462
+ tui.requestRender();
463
+ });
460
464
  flushFollowUpQueue();
461
465
  tui.requestRender();
462
466
  }, 5e3);
467
+ const onAgentRunEnded = () => {
468
+ refreshSessionInfo().finally(() => {
469
+ updateFooter();
470
+ tui.requestRender();
471
+ });
472
+ flushFollowUpQueue();
473
+ };
463
474
  client.onEvent = (evt) => {
464
475
  const data = evt.data ?? {};
465
- dispatchAgentSSE(evt.event, data, state, chatLog, assembler, tui, setActivityStatus, touchStreamingActivity, flushFollowUpQueue);
476
+ dispatchAgentSSE(evt.event, data, state, chatLog, assembler, tui, setActivityStatus, touchStreamingActivity, onAgentRunEnded);
466
477
  };
467
478
  client.onConnected = () => {
468
479
  state.isConnected = true;
@@ -1 +1 @@
1
- {"version":3,"file":"tui.js","names":[],"sources":["../../../src/tui/tui.ts"],"sourcesContent":["import {\n CombinedAutocompleteProvider,\n Container,\n Loader,\n ProcessTerminal,\n setKeybindings,\n Text,\n TUI,\n} from '@earendil-works/pi-tui';\nimport { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { spawnSync } from 'node:child_process';\n\nimport type { ThinkLevel } from '../agent/transcript/thinking-types.js';\nimport type { TuiBackend, TuiEvent, TuiModelChoice } 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 { TuiBottomBar } from './components/tui-bottom-bar.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';\nimport { createXopcTuiKeybindingsManager } from './xopc-tui-keybindings.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\nconst THINK_LEVEL_CYCLE: ThinkLevel[] = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'adaptive',\n];\n\nfunction nextThinkLevel(current: string | undefined): ThinkLevel {\n const c = (current ?? 'medium').toLowerCase();\n const idx = THINK_LEVEL_CYCLE.indexOf(c as ThinkLevel);\n const mediumIdx = THINK_LEVEL_CYCLE.indexOf('medium');\n const base = idx >= 0 ? idx : mediumIdx >= 0 ? mediumIdx : 0;\n return THINK_LEVEL_CYCLE[(base + 1) % THINK_LEVEL_CYCLE.length]!;\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 keybindings = createXopcTuiKeybindingsManager();\n setKeybindings(keybindings);\n\n let modelChoices: TuiModelChoice[] = [];\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 bottomBar = new TuiBottomBar(() => state, () => opts.thinking);\n const chatLog = new ChatLog();\n const editor = new CustomEditor(tui, editorTheme, keybindings);\n const root = new Container();\n root.addChild(header);\n root.addChild(chatLog);\n root.addChild(statusContainer);\n root.addChild(editor);\n root.addChild(bottomBar);\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 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 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 statusContainer.clear();\n }\n lastActivityStatus = state.activityStatus;\n bottomBar.invalidate();\n tui.requestRender();\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 bottomBar.invalidate();\n };\n\n const refreshModelChoices = async () => {\n try {\n modelChoices = await client.listModels();\n } catch {\n modelChoices = [];\n }\n bottomBar.invalidate();\n tui.requestRender();\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 resolveModelChoiceIndex = (): number => {\n if (modelChoices.length === 0) return -1;\n const p = state.sessionInfo.modelProvider;\n const m = state.sessionInfo.model;\n if (p && m) {\n const byParts = modelChoices.findIndex((x) => x.provider === p && x.id === m);\n if (byParts >= 0) return byParts;\n }\n if (m?.includes('/')) {\n const [a, b] = m.split('/', 2);\n const bySlash = modelChoices.findIndex((x) => x.provider === a && x.id === b);\n if (bySlash >= 0) return bySlash;\n }\n return 0;\n };\n\n const cycleModel = (dir: 'forward' | 'backward') => {\n if (modelChoices.length === 0) {\n void refreshModelChoices().then(() => {\n if (modelChoices.length === 0) {\n chatLog.addSystem('No models available to cycle.');\n tui.requestRender();\n return;\n }\n cycleModel(dir);\n });\n return;\n }\n const idx = resolveModelChoiceIndex();\n const base = idx >= 0 ? idx : 0;\n const delta = dir === 'forward' ? 1 : -1;\n const next = modelChoices[(base + delta + modelChoices.length) % modelChoices.length]!;\n sendMessage(`/switch ${next.provider}/${next.id}`);\n };\n\n const handleCtrlZ = () => {\n if (process.platform === 'win32') {\n chatLog.addSystem('Suspend (Ctrl+Z) is not supported on Windows.');\n tui.requestRender();\n return;\n }\n const suspendKeepAlive = setInterval(() => {}, 2 ** 30);\n const ignoreSigint = () => {};\n process.on('SIGINT', ignoreSigint);\n process.once('SIGCONT', () => {\n clearInterval(suspendKeepAlive);\n process.removeListener('SIGINT', ignoreSigint);\n tui.start();\n tui.setFocus(editor);\n tui.requestRender(true);\n });\n try {\n tui.stop();\n process.kill(0, 'SIGTSTP');\n } catch {\n clearInterval(suspendKeepAlive);\n process.removeListener('SIGINT', ignoreSigint);\n }\n };\n\n const openExternalEditor = () => {\n const dir = mkdtempSync(join(tmpdir(), 'xopc-tui-edit-'));\n const filePath = join(dir, 'message.md');\n writeFileSync(filePath, editor.getText(), 'utf8');\n const editorBin = process.env.EDITOR || process.env.VISUAL || 'vi';\n void (async () => {\n await withTuiSuspended(tui, async () => {\n spawnSync(editorBin, [filePath], { stdio: 'inherit' });\n });\n try {\n const next = readFileSync(filePath, 'utf8');\n editor.setText(next.replace(/\\r\\n/g, '\\n'));\n } catch {\n // ignore\n }\n try {\n unlinkSync(filePath);\n } catch {\n // ignore\n }\n tui.setFocus(editor);\n tui.requestRender(true);\n })();\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 keybindings,\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 const submitBurst = createSubmitBurstCoalescer({\n submit: submitCore,\n enabled: shouldEnableWindowsGitBashPasteFallback(),\n });\n editor.onSubmit = submitBurst;\n\n const flushFollowUpQueue = () => {\n if (state.exitRequested) return;\n if (state.activeRunId) return;\n const next = state.messageFollowUpQueue.shift();\n if (next === undefined) return;\n sendMessage(next);\n };\n\n const isAgentBusy = () =>\n state.activeRunId != null || busyStates.has(state.activityStatus);\n\n const handleFollowUp = () => {\n const text = editor.getText().trim();\n if (!text) return;\n if (isAgentBusy()) {\n editor.addToHistory(text);\n state.messageFollowUpQueue.push(text);\n editor.setText('');\n chatLog.addSystem(\n theme.dim(\n `Queued follow-up (${state.messageFollowUpQueue.length} in queue). Next sends when this reply finishes.`,\n ),\n );\n bottomBar.invalidate();\n tui.requestRender();\n return;\n }\n submitBurst(text);\n };\n\n const handleDequeue = () => {\n if (state.messageFollowUpQueue.length === 0) {\n chatLog.addSystem(theme.dim('No queued messages to restore.'));\n tui.requestRender();\n return;\n }\n const queued = [...state.messageFollowUpQueue];\n state.messageFollowUpQueue.length = 0;\n const current = editor.getText().trim();\n const combined = [queued.join('\\n\\n'), current].filter(Boolean).join('\\n\\n');\n editor.setText(combined);\n chatLog.addSystem(\n theme.dim(\n `Restored ${queued.length} queued message${queued.length > 1 ? 's' : ''} to editor.`,\n ),\n );\n bottomBar.invalidate();\n tui.requestRender();\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 state.messageFollowUpQueue.length = 0;\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 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\n const setModelChoices = (models: TuiModelChoice[]) => {\n modelChoices = models;\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 setModelChoices,\n };\n\n editor.onEscape = () => void abortActive();\n editor.onCtrlD = () => requestExit();\n\n editor.onAction('app.clear', handleCtrlC);\n editor.onAction('app.exit', () => requestExit());\n editor.onAction('app.suspend', handleCtrlZ);\n editor.onAction('app.thinking.cycle', () => {\n const cur = state.sessionInfo.thinkingLevel ?? opts.thinking ?? 'medium';\n sendMessage(`/think ${nextThinkLevel(cur)}`);\n });\n editor.onAction('app.model.cycleForward', () => cycleModel('forward'));\n editor.onAction('app.model.cycleBackward', () => cycleModel('backward'));\n editor.onAction('app.model.select', () => void openModelPickerOverlay(pickerSvc));\n editor.onAction('app.session.resume', () => void openSessionPickerOverlay(pickerSvc));\n editor.onAction('app.tools.expand', () => {\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n setActivityStatus(state.toolsExpanded ? 'tools expanded' : 'tools collapsed');\n tui.requestRender();\n });\n editor.onAction('app.thinking.toggle', () => {\n state.showThinking = !state.showThinking;\n updateFooter();\n tui.requestRender();\n });\n editor.onAction('app.editor.external', openExternalEditor);\n editor.onPasteImage = () => {\n chatLog.addSystem(\n theme.dim(\n 'Clipboard image paste is not implemented in xopc TUI yet. Paste a file path or use the gateway UI.',\n ),\n );\n tui.requestRender();\n };\n editor.onAction('app.message.followUp', handleFollowUp);\n editor.onAction('app.message.dequeue', handleDequeue);\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 flushFollowUpQueue();\n tui.requestRender();\n }, 5000);\n\n client.onEvent = (evt: TuiEvent) => {\n const data = (evt.data ?? {}) as Record<string, unknown>;\n dispatchAgentSSE(\n evt.event,\n data,\n state,\n chatLog,\n assembler,\n tui,\n setActivityStatus,\n touchStreamingActivity,\n flushFollowUpQueue,\n );\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 refreshModelChoices();\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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"}
1
+ {"version":3,"file":"tui.js","names":[],"sources":["../../../src/tui/tui.ts"],"sourcesContent":["import {\n CombinedAutocompleteProvider,\n Container,\n Loader,\n ProcessTerminal,\n setKeybindings,\n Text,\n TUI,\n} from '@earendil-works/pi-tui';\nimport { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { spawnSync } from 'node:child_process';\n\nimport type { ThinkLevel } from '../agent/transcript/thinking-types.js';\nimport type { TuiBackend, TuiEvent, TuiModelChoice } 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 { TuiBottomBar } from './components/tui-bottom-bar.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';\nimport { createXopcTuiKeybindingsManager } from './xopc-tui-keybindings.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\nconst THINK_LEVEL_CYCLE: ThinkLevel[] = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'adaptive',\n];\n\nfunction nextThinkLevel(current: string | undefined): ThinkLevel {\n const c = (current ?? 'medium').toLowerCase();\n const idx = THINK_LEVEL_CYCLE.indexOf(c as ThinkLevel);\n const mediumIdx = THINK_LEVEL_CYCLE.indexOf('medium');\n const base = idx >= 0 ? idx : mediumIdx >= 0 ? mediumIdx : 0;\n return THINK_LEVEL_CYCLE[(base + 1) % THINK_LEVEL_CYCLE.length]!;\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 keybindings = createXopcTuiKeybindingsManager();\n setKeybindings(keybindings);\n\n let modelChoices: TuiModelChoice[] = [];\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 bottomBar = new TuiBottomBar(() => state, () => opts.thinking);\n const chatLog = new ChatLog();\n const editor = new CustomEditor(tui, editorTheme, keybindings);\n const root = new Container();\n root.addChild(header);\n root.addChild(chatLog);\n root.addChild(statusContainer);\n root.addChild(editor);\n root.addChild(bottomBar);\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 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 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 statusContainer.clear();\n }\n lastActivityStatus = state.activityStatus;\n bottomBar.invalidate();\n tui.requestRender();\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 bottomBar.invalidate();\n };\n\n const refreshModelChoices = async () => {\n try {\n modelChoices = await client.listModels();\n } catch {\n modelChoices = [];\n }\n bottomBar.invalidate();\n tui.requestRender();\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 resolveModelChoiceIndex = (): number => {\n if (modelChoices.length === 0) return -1;\n const p = state.sessionInfo.modelProvider;\n const m = state.sessionInfo.model;\n if (p && m) {\n const byParts = modelChoices.findIndex((x) => x.provider === p && x.id === m);\n if (byParts >= 0) return byParts;\n }\n if (m?.includes('/')) {\n const [a, b] = m.split('/', 2);\n const bySlash = modelChoices.findIndex((x) => x.provider === a && x.id === b);\n if (bySlash >= 0) return bySlash;\n }\n return 0;\n };\n\n const cycleModel = (dir: 'forward' | 'backward') => {\n if (modelChoices.length === 0) {\n void refreshModelChoices().then(() => {\n if (modelChoices.length === 0) {\n chatLog.addSystem('No models available to cycle.');\n tui.requestRender();\n return;\n }\n cycleModel(dir);\n });\n return;\n }\n const idx = resolveModelChoiceIndex();\n const base = idx >= 0 ? idx : 0;\n const delta = dir === 'forward' ? 1 : -1;\n const next = modelChoices[(base + delta + modelChoices.length) % modelChoices.length]!;\n sendMessage(`/switch ${next.provider}/${next.id}`);\n };\n\n const handleCtrlZ = () => {\n if (process.platform === 'win32') {\n chatLog.addSystem('Suspend (Ctrl+Z) is not supported on Windows.');\n tui.requestRender();\n return;\n }\n const suspendKeepAlive = setInterval(() => {}, 2 ** 30);\n const ignoreSigint = () => {};\n process.on('SIGINT', ignoreSigint);\n process.once('SIGCONT', () => {\n clearInterval(suspendKeepAlive);\n process.removeListener('SIGINT', ignoreSigint);\n tui.start();\n tui.setFocus(editor);\n tui.requestRender(true);\n });\n try {\n tui.stop();\n process.kill(0, 'SIGTSTP');\n } catch {\n clearInterval(suspendKeepAlive);\n process.removeListener('SIGINT', ignoreSigint);\n }\n };\n\n const openExternalEditor = () => {\n const dir = mkdtempSync(join(tmpdir(), 'xopc-tui-edit-'));\n const filePath = join(dir, 'message.md');\n writeFileSync(filePath, editor.getText(), 'utf8');\n const editorBin = process.env.EDITOR || process.env.VISUAL || 'vi';\n void (async () => {\n await withTuiSuspended(tui, async () => {\n spawnSync(editorBin, [filePath], { stdio: 'inherit' });\n });\n try {\n const next = readFileSync(filePath, 'utf8');\n editor.setText(next.replace(/\\r\\n/g, '\\n'));\n } catch {\n // ignore\n }\n try {\n unlinkSync(filePath);\n } catch {\n // ignore\n }\n tui.setFocus(editor);\n tui.requestRender(true);\n })();\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 keybindings,\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 const submitBurst = createSubmitBurstCoalescer({\n submit: submitCore,\n enabled: shouldEnableWindowsGitBashPasteFallback(),\n });\n editor.onSubmit = submitBurst;\n\n const flushFollowUpQueue = () => {\n if (state.exitRequested) return;\n if (state.activeRunId) return;\n const next = state.messageFollowUpQueue.shift();\n if (next === undefined) return;\n sendMessage(next);\n };\n\n const isAgentBusy = () =>\n state.activeRunId != null || busyStates.has(state.activityStatus);\n\n const handleFollowUp = () => {\n const text = editor.getText().trim();\n if (!text) return;\n if (isAgentBusy()) {\n editor.addToHistory(text);\n state.messageFollowUpQueue.push(text);\n editor.setText('');\n chatLog.addSystem(\n theme.dim(\n `Queued follow-up (${state.messageFollowUpQueue.length} in queue). Next sends when this reply finishes.`,\n ),\n );\n bottomBar.invalidate();\n tui.requestRender();\n return;\n }\n submitBurst(text);\n };\n\n const handleDequeue = () => {\n if (state.messageFollowUpQueue.length === 0) {\n chatLog.addSystem(theme.dim('No queued messages to restore.'));\n tui.requestRender();\n return;\n }\n const queued = [...state.messageFollowUpQueue];\n state.messageFollowUpQueue.length = 0;\n const current = editor.getText().trim();\n const combined = [queued.join('\\n\\n'), current].filter(Boolean).join('\\n\\n');\n editor.setText(combined);\n chatLog.addSystem(\n theme.dim(\n `Restored ${queued.length} queued message${queued.length > 1 ? 's' : ''} to editor.`,\n ),\n );\n bottomBar.invalidate();\n tui.requestRender();\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 state.messageFollowUpQueue.length = 0;\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 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\n const setModelChoices = (models: TuiModelChoice[]) => {\n modelChoices = models;\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 setModelChoices,\n };\n\n editor.onEscape = () => void abortActive();\n editor.onCtrlD = () => requestExit();\n\n editor.onAction('app.clear', handleCtrlC);\n editor.onAction('app.exit', () => requestExit());\n editor.onAction('app.suspend', handleCtrlZ);\n editor.onAction('app.thinking.cycle', () => {\n const cur = state.sessionInfo.thinkingLevel ?? opts.thinking ?? 'medium';\n sendMessage(`/think ${nextThinkLevel(cur)}`);\n });\n editor.onAction('app.model.cycleForward', () => cycleModel('forward'));\n editor.onAction('app.model.cycleBackward', () => cycleModel('backward'));\n editor.onAction('app.model.select', () => void openModelPickerOverlay(pickerSvc));\n editor.onAction('app.session.resume', () => void openSessionPickerOverlay(pickerSvc));\n editor.onAction('app.tools.expand', () => {\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n setActivityStatus(state.toolsExpanded ? 'tools expanded' : 'tools collapsed');\n tui.requestRender();\n });\n editor.onAction('app.thinking.toggle', () => {\n state.showThinking = !state.showThinking;\n updateFooter();\n tui.requestRender();\n });\n editor.onAction('app.editor.external', openExternalEditor);\n editor.onPasteImage = () => {\n chatLog.addSystem(\n theme.dim(\n 'Clipboard image paste is not implemented in xopc TUI yet. Paste a file path or use the gateway UI.',\n ),\n );\n tui.requestRender();\n };\n editor.onAction('app.message.followUp', handleFollowUp);\n editor.onAction('app.message.dequeue', handleDequeue);\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 void refreshSessionInfo().finally(() => {\n updateFooter();\n tui.requestRender();\n });\n flushFollowUpQueue();\n tui.requestRender();\n }, 5000);\n\n const onAgentRunEnded = () => {\n void refreshSessionInfo().finally(() => {\n updateFooter();\n tui.requestRender();\n });\n flushFollowUpQueue();\n };\n\n client.onEvent = (evt: TuiEvent) => {\n const data = (evt.data ?? {}) as Record<string, unknown>;\n dispatchAgentSSE(\n evt.event,\n data,\n state,\n chatLog,\n assembler,\n tui,\n setActivityStatus,\n touchStreamingActivity,\n onAgentRunEnded,\n );\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 refreshModelChoices();\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.47",
3
+ "version": "0.0.49",
4
4
  "description": "The OPC workstation that grows with you: AI assistant for One Person Companies — CLI, gateway, multi-channel (Telegram/WeChat), 20+ LLM providers via pi-ai, extensions and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -1,2 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{i as r}from"./vendor-swr-B5fPo7KK.js";import{An as i,At as a,Cn as o,Dn as s,Dt as c,Gn as l,In as u,Jr as d,Mn as f,Mt as p,Nt as m,Ot as h,Rn as g,Sr as _,St as v,Xr as y,Yr as b,Zr as x,_ as S,ar as C,dn as w,gn as T,hi as E,jn as D,jt as O,kn as k,kt as A,mi as j,ti as M,v as N,vn as P,vr as F,w as ee,wr as te,xt as ne,y as re,yn as ie,yt as ae}from"./index-DeELk--t.js";import{a as oe,i as se,n as I,r as ce,t as le}from"./dist-wDej8fSi.js";import{t as ue}from"./browser-DNXWYwzc.js";var L=n();function de({icon:e,title:t,subtitle:n,configured:r,enabled:i,onToggle:a,toggleDisabled:o,onConfigure:s,onEdit:c,onRemove:d,ch:p}){return(0,L.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-2xl border border-edge bg-surface-base px-4 py-4 dark:border-edge sm:flex-row sm:items-center sm:gap-4`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-start gap-4`,children:[(0,L.jsx)(`div`,{className:`flex size-12 shrink-0 items-center justify-center rounded-xl bg-surface-hover`,"aria-hidden":!0,children:e}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:t}),r?(0,L.jsx)(`span`,{className:`inline-flex items-center rounded-full bg-success-soft px-2 py-0.5 text-xs font-medium text-success`,children:p.hubConnectedBadge}):null]}),(0,L.jsx)(`p`,{className:`mt-1 text-xs text-fg-muted`,children:n})]})]}),r?(0,L.jsxs)(`div`,{className:`flex shrink-0 flex-wrap items-center justify-end gap-2 sm:gap-3`,children:[(0,L.jsxs)(se,{children:[(0,L.jsx)(oe,{asChild:!0,children:(0,L.jsx)(f,{type:`button`,variant:`ghost`,className:`size-9 shrink-0 p-0`,"aria-label":p.menuMoreAria,children:(0,L.jsx)(x,{className:`size-5 text-fg-muted`,strokeWidth:1.75})})}),(0,L.jsx)(ce,{children:(0,L.jsxs)(le,{className:u(`min-w-[11rem] rounded-xl border border-edge bg-surface-panel p-1 shadow-popover dark:border-edge`,re),sideOffset:6,align:`end`,children:[(0,L.jsx)(I,{className:`cursor-pointer rounded-lg px-3 py-2 text-sm text-fg outline-none hover:bg-surface-hover data-[highlighted]:bg-surface-hover`,onSelect:()=>c(),children:(0,L.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(F,{className:`size-4 shrink-0 text-fg-muted`,strokeWidth:1.75}),p.menuEditConfig]})}),(0,L.jsx)(I,{className:`cursor-pointer rounded-lg px-3 py-2 text-sm text-danger outline-none hover:bg-surface-hover data-[highlighted]:bg-surface-hover`,onSelect:()=>d(),children:(0,L.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(l,{className:`size-4 shrink-0`,strokeWidth:1.75}),p.menuRemoveConfig]})})]})})]}),(0,L.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":i,"aria-label":`${t} — ${p.enableChannelAria}`,disabled:o,className:u(`inline-flex h-6 w-10 shrink-0 items-center rounded-full border border-edge p-0.5 transition-colors`,i?`justify-end bg-accent`:`justify-start bg-surface-hover`,o&&`cursor-not-allowed opacity-50`),onClick:()=>void a(!i),children:(0,L.jsx)(`span`,{className:`size-4 rounded-full bg-surface-panel shadow-surface ring-1 ring-edge/40 dark:ring-edge/55`})})]}):(0,L.jsx)(`div`,{className:`flex shrink-0 justify-end sm:justify-end`,children:(0,L.jsx)(f,{type:`button`,variant:`primary`,className:`shrink-0`,onClick:s,children:p.hubConfigureButton})})]})}function fe({open:e,onOpenChange:t,ch:n,removeTarget:r,onCancel:i,saving:o,onConfirmRemove:s}){return(0,L.jsx)(p,{open:e,onOpenChange:t,children:(0,L.jsxs)(O,{children:[(0,L.jsx)(a,{className:u(`xopc-dialog-overlay fixed inset-0 bg-scrim backdrop-blur-[1px]`,N)}),(0,L.jsxs)(h,{className:u(`fixed left-1/2 top-1/2 w-[min(100%-2rem,28rem)] -translate-x-1/2 -translate-y-1/2`,S,`rounded-2xl border border-edge bg-surface-panel p-6 shadow-popover outline-none dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsx)(m,{className:`text-base font-semibold text-fg`,children:n.removeChannelTitle}),(0,L.jsx)(A,{className:`mt-2 text-sm text-fg-muted`,children:r?n.removeChannelConfirm.replace(`{{name}}`,r===`weixin`?n.weixinTitle:r===`telegram`?n.telegramTitle:r===`feishu`?n.feishuTitle:n.dingtalkTitle):`\xA0`}),(0,L.jsxs)(`div`,{className:`mt-6 flex justify-end gap-2`,children:[(0,L.jsx)(f,{type:`button`,variant:`secondary`,onClick:i,children:n.modalCancel}),(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`border-danger/40 bg-danger text-white hover:bg-danger/90 dark:border-danger/40`,disabled:o,onClick:s,children:o?n.saving:n.removeChannelAction})]})]})]})})}function R(e){return(e??``).trim().toLowerCase()}function z(e){let t=e.match;return!(!t?.channel||!t.accountId||t.peerId||t.peerKind||t.guildId||t.teamId||t.memberRoleIds&&t.memberRoleIds.length>0)}function pe(e){return Object.keys(e.accounts??{}).sort()}function me(e){return Object.keys(e.accounts??{}).sort()}function B(e){let t=Object.keys(e.accounts??{});return t.length>0?[...t].sort():typeof e.appId==`string`&&e.appId.trim()&&typeof e.appSecret==`string`&&e.appSecret.trim()?[`default`]:[]}function he(e){let t=Object.keys(e.accounts??{});return t.length>0?[...t].sort():typeof e.clientId==`string`&&e.clientId.trim()&&typeof e.clientSecret==`string`&&e.clientSecret.trim()?[`default`]:[]}function V(e,t,n,r,i,a){let o={},s={},c={},l={};for(let n of t)o[n]=(e.find(e=>R(e.match?.channel)===`telegram`&&R(e.match?.accountId)===R(n)&&z(e))?.agentId??a).trim().toLowerCase();for(let t of n)s[t]=(e.find(e=>R(e.match?.channel)===`weixin`&&R(e.match?.accountId)===R(t)&&z(e))?.agentId??a).trim().toLowerCase();for(let t of r)c[t]=(e.find(e=>R(e.match?.channel)===`feishu`&&R(e.match?.accountId)===R(t)&&z(e))?.agentId??a).trim().toLowerCase();for(let t of i)l[t]=(e.find(e=>R(e.match?.channel)===`dingtalk`&&R(e.match?.accountId)===R(t)&&z(e))?.agentId??a).trim().toLowerCase();return{telegram:o,weixin:s,feishu:c,dingtalk:l}}function ge(e,t,n,r,i,a,o){let s=new Set(n.map(R)),c=new Set(r.map(R)),l=new Set(i.map(R)),u=new Set(a.map(R)),d=e.filter(e=>{if(e.id?.startsWith(`ui:route:account:`))return!1;if(!z(e))return!0;let t=R(e.match.channel),n=R(e.match.accountId);return!n||n===`*`?!0:!(t===`telegram`&&s.has(n)||t===`weixin`&&c.has(n)||t===`feishu`&&l.has(n)||t===`dingtalk`&&u.has(n))}),f=[];for(let e of n){let n=(t.telegram[e]??t.telegram[R(e)]??o).trim().toLowerCase();f.push({id:`ui:route:account:telegram:${e}`,agentId:n,priority:45,enabled:!0,match:{channel:`telegram`,accountId:e}})}for(let e of r){let n=(t.weixin[e]??t.weixin[R(e)]??o).trim().toLowerCase();f.push({id:`ui:route:account:weixin:${e}`,agentId:n,priority:45,enabled:!0,match:{channel:`weixin`,accountId:e}})}for(let e of i){let n=(t.feishu[e]??t.feishu[R(e)]??o).trim().toLowerCase();f.push({id:`ui:route:account:feishu:${e}`,agentId:n,priority:45,enabled:!0,match:{channel:`feishu`,accountId:e}})}for(let e of a){let n=(t.dingtalk[e]??t.dingtalk[R(e)]??o).trim().toLowerCase();f.push({id:`ui:route:account:dingtalk:${e}`,agentId:n,priority:45,enabled:!0,match:{channel:`dingtalk`,accountId:e}})}return[...d,...f]}function H(){return u(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,i,`dark:border-edge`)}function _e(){return u(k,s)}function U(e){return e.split(/[,\n]/).map(e=>e.trim()).filter(Boolean).map(e=>/^-?\d+$/.test(e)?Number(e):e)}function W(e){return e.map(String).join(`, `)}function ve(e){let t=e.accounts?.default?.botToken;return typeof t==`string`?t:``}function ye(e){return Object.values(e.accounts??{}).some(e=>typeof e.botToken==`string`&&e.botToken.trim().length>0)}function be(e){return Object.keys(e.accounts??{}).length>0||e.allowFrom.length>0}function G(e){return!!(e.appId?.trim()&&e.appSecret?.trim())||Object.keys(e.accounts??{}).length>0}function xe(e){return!!(e.clientId?.trim()&&e.clientSecret?.trim())||Object.keys(e.accounts??{}).length>0}function K({children:e}){return(0,L.jsx)(`div`,{className:`text-sm font-medium text-fg`,children:e})}function q({children:e}){return(0,L.jsx)(`p`,{className:`text-xs leading-relaxed text-fg-subtle`,children:e})}function J({label:e,value:t,onChange:n,options:r}){return(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e}),(0,L.jsx)(`select`,{className:_e(),value:t,onChange:e=>n(e.target.value),children:r.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})]})}function Se({accountIds:e,routes:t,defaultAgentId:n,agentItems:r,disabled:i,onChange:a,ch:c}){let l=D(o(e=>e.language)).agentsSettings;if(e.length===0)return null;let d=r.length>0?r:[{id:n}];return(0,L.jsxs)(`div`,{className:`space-y-3 border-t border-edge-subtle pt-4 dark:border-edge`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(K,{children:c.agentRoutingTitle}),(0,L.jsx)(q,{children:c.agentRoutingHint})]}),(0,L.jsx)(`div`,{className:`space-y-2`,children:e.map(e=>(0,L.jsxs)(`div`,{className:`grid grid-cols-1 items-start gap-2 rounded-lg border border-edge-subtle bg-surface-base px-3 py-2.5 sm:grid-cols-2 sm:items-center dark:border-edge`,children:[(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`p`,{className:`text-xs font-medium text-fg-muted`,children:c.agentRoutingAccountLabel}),(0,L.jsx)(`p`,{className:`mt-0.5 truncate font-mono text-sm text-fg`,title:e,children:e})]}),(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`label`,{className:`sr-only`,htmlFor:`agent-route-${e}`,children:c.agentRoutingAgentLabel}),(0,L.jsx)(`select`,{id:`agent-route-${e}`,className:u(H(),s),disabled:i,value:(t[e]??n).toLowerCase(),onChange:t=>a(e,t.target.value),children:d.map(e=>(0,L.jsx)(`option`,{value:e.id,children:`${ee(e,l)} (${e.id})`},e.id))})]})]},e))})]})}function Ce({ch:e,form:t,baseline:n,showDingtalkSecret:r,setShowDingtalkSecret:i,dingtalkCopied:a,copyDingtalkSecret:o,updateDingtalk:s,updateChannelAgentRoute:c,dingtalkAccountsDraft:l,setDingtalkAccountsDraft:p,dingtalkAccountsError:m,onDingtalkAccountsBlur:h,dmOpts:g,groupOpts:_,chatAgents:v,saving:y,dirty:x,save:S,discard:C}){let w=H,T=t.dingtalk,D=n?.dingtalk?.clientSecret??``,O=!r&&!!String(D).trim()&&T.clientSecret===D;return(0,L.jsxs)(`details`,{className:`group rounded-xl border border-edge-subtle bg-surface-base open:pb-3 dark:border-edge`,children:[(0,L.jsx)(`summary`,{className:`cursor-pointer list-none rounded-xl px-3 py-2.5 text-sm font-medium text-fg transition-colors hover:bg-surface-hover group-open:rounded-b-none [&::-webkit-details-marker]:hidden`,children:(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,L.jsx)(j,{className:`size-4 shrink-0 text-fg-muted transition-transform group-open:rotate-180`}),e.advancedShow]})}),(0,L.jsxs)(`div`,{className:`space-y-4 border-t border-edge-subtle px-3 pb-3 pt-3 dark:border-edge-subtle`,children:[(0,L.jsx)(`p`,{className:`text-xs leading-relaxed text-fg-muted`,children:e.dingtalkAdvancedHint}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-start gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox mt-0.5`,checked:T.enabled,onChange:e=>s({enabled:e.target.checked})}),(0,L.jsx)(`span`,{children:e.enableDingtalkAria})]}),(0,L.jsxs)(`div`,{className:`space-y-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(K,{children:[e.dingtalkClientId,(0,L.jsx)(`span`,{className:`text-red-600 dark:text-red-400`,children:` *`})]}),(0,L.jsx)(`input`,{className:u(w(),`min-w-0 flex-1 font-mono text-xs`),value:T.clientId,onChange:e=>s({clientId:e.target.value}),placeholder:`dingxxx`}),(0,L.jsx)(q,{children:e.dingtalkClientIdDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(K,{children:[e.dingtalkClientSecret,(0,L.jsx)(`span`,{className:`text-red-600 dark:text-red-400`,children:` *`})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,L.jsx)(`input`,{className:u(w(),`min-w-0 flex-1 font-mono text-xs`),type:r?`text`:`password`,autoComplete:`off`,readOnly:O,value:O?`*`.repeat(Math.max(1,T.clientSecret.length)):T.clientSecret,onChange:e=>{O||s({clientSecret:e.target.value})},placeholder:`••••••••`}),T.clientSecret?(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void o(),children:[a?(0,L.jsx)(E,{className:`size-3.5`}):(0,L.jsx)(M,{className:`size-3.5`}),a?e.copied:e.copy]}):null,(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>i(e=>!e),children:[r?(0,L.jsx)(b,{className:`size-3.5`}):(0,L.jsx)(d,{className:`size-3.5`}),r?e.hide:e.show]})]}),(0,L.jsx)(q,{children:e.dingtalkClientSecretDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.dingtalkEndpoint}),(0,L.jsx)(`input`,{className:u(w(),`min-w-0 flex-1 font-mono text-xs`),value:T.endpoint,onChange:e=>s({endpoint:e.target.value}),placeholder:`https://api.dingtalk.com`}),(0,L.jsx)(q,{children:e.dingtalkEndpointDesc})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:T.debug,onChange:e=>s({debug:e.target.checked})}),e.dingtalkDebug]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.dmPolicy}),(0,L.jsx)(`select`,{className:w(),value:T.dmPolicy,onChange:e=>s({dmPolicy:e.target.value}),children:g.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.groupPolicy}),(0,L.jsx)(`select`,{className:w(),value:T.groupPolicy,onChange:e=>s({groupPolicy:e.target.value}),children:_.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})]})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:T.requireMention,onChange:e=>s({requireMention:e.target.checked})}),e.requireMention]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.allowFromDm}),(0,L.jsx)(`textarea`,{className:u(w(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`userId1, userId2`,value:W(T.allowFrom),onChange:e=>s({allowFrom:U(e.target.value)})}),(0,L.jsx)(q,{children:e.allowFromDmDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.allowFromGroups}),(0,L.jsx)(`textarea`,{className:u(w(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`openConversationId1`,value:W(T.groupAllowFrom),onChange:e=>s({groupAllowFrom:U(e.target.value)})}),(0,L.jsx)(q,{children:e.allowFromGroupsDesc})]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.historyLimit}),(0,L.jsx)(`input`,{className:u(w(),`min-w-0 flex-1 font-mono text-xs`),type:`number`,inputMode:`numeric`,value:String(T.historyLimit),onChange:e=>s({historyLimit:Number(e.target.value||`0`)||0})})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.textChunkLimit}),(0,L.jsx)(`input`,{className:u(w(),`min-w-0 flex-1 font-mono text-xs`),type:`number`,inputMode:`numeric`,value:String(T.textChunkLimit),onChange:e=>s({textChunkLimit:Number(e.target.value||`0`)||0})})]})]}),(0,L.jsx)(Se,{accountIds:he(T),routes:t.channelAgentRoutes.dingtalk,defaultAgentId:t.defaultAgentId,agentItems:v?.items??[],disabled:y,onChange:(e,t)=>c(`dingtalk`,e,t),ch:e}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.multiAccountJson}),(0,L.jsx)(`textarea`,{className:u(w(),`min-h-[140px] resize-y font-mono text-xs`),spellCheck:!1,value:l,onChange:e=>p(e.target.value),onBlur:h,placeholder:`{ "default": { "clientId": "...", "clientSecret": "...", "enabled": true } }`}),m?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:m}):(0,L.jsx)(q,{children:e.multiAccountJsonDesc})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:justify-end`,children:[(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`w-full sm:w-auto`,disabled:!x||y,onClick:C,children:e.discard}),(0,L.jsx)(f,{type:`button`,variant:`primary`,className:`w-full sm:w-auto`,disabled:!x||y,onClick:async()=>{await S()},children:y?e.saving:e.save})]})]})]})}var Y=e(t(),1),X=e(ue(),1);function Z(e){return{accountId:e,name:``,enabled:!0,botToken:``,allowFrom:[],dmPolicy:`pairing`,groupPolicy:`open`,replyToMode:`off`,apiRoot:``,proxy:``,historyLimit:50,textChunkLimit:4e3,streamMode:`partial`}}function we(){return{bindingsFull:[],channelAgentRoutes:{telegram:{},weixin:{},feishu:{},dingtalk:{}},defaultAgentId:`main`,telegram:{enabled:!1,apiRoot:``,debug:!1,allowFrom:[],groupAllowFrom:[],dmPolicy:`pairing`,groupPolicy:`open`,replyToMode:`off`,streamMode:`partial`,historyLimit:50,textChunkLimit:4e3,proxy:``,accounts:{default:Z(`default`)}},weixin:{enabled:!1,dmPolicy:`pairing`,allowFrom:[],debug:!1,streamMode:`partial`,historyLimit:50,textChunkLimit:4e3,routeTag:``,accounts:{}},feishu:{enabled:!1,defaultAccount:``,appId:``,appSecret:``,domain:`feishu`,connectionMode:`websocket`,verificationToken:``,encryptKey:``,webhookHost:`127.0.0.1`,webhookPort:3e3,webhookPath:`/feishu/events`,dmPolicy:`pairing`,groupPolicy:`allowlist`,allowFrom:[],groupAllowFrom:[],requireMention:!0,historyLimit:50,textChunkLimit:4e3,renderMode:`auto`,streaming:!1,reactionNotifications:`own`,tools:{doc:!0,wiki:!0,drive:!0,scopes:!0,bitable:!0,perm:!1},actions:{reactions:!0},accounts:{}},dingtalk:{enabled:!1,defaultAccount:``,clientId:``,clientSecret:``,dmPolicy:`pairing`,groupPolicy:`open`,allowFrom:[],groupAllowFrom:[],requireMention:!1,debug:!1,endpoint:`https://api.dingtalk.com`,historyLimit:50,textChunkLimit:4e3,accounts:{}}}}function Te(e){let t=e&&typeof e==`object`?e.channels:void 0,n=t&&typeof t==`object`?t:{},r=n.telegram,i=n.weixin,a=n.feishu,o=n.dingtalk,s=r?.accounts,c=s&&typeof s==`object`&&!Array.isArray(s)?{...s}:{},l=typeof r?.botToken==`string`?r.botToken.trim():``,u=c.default?.botToken?.trim()??``;l&&!u&&(c={...c,default:{...c.default??Z(`default`),accountId:`default`,botToken:l}}),c.default||(c={...c,default:Z(`default`)});let d=i?.accounts,f=d&&typeof d==`object`&&!Array.isArray(d)?d:{},p=(()=>{let t=(e&&typeof e==`object`?e:{}).bindings;return Array.isArray(t)?t:[]})(),m=(()=>{let t=(e&&typeof e==`object`?e:{}).agents;return t&&typeof t==`object`?t:{}})(),h=typeof m.defaultId==`string`&&m.defaultId.trim()?m.defaultId.trim().toLowerCase():`main`,g={telegram:{enabled:!!r?.enabled,apiRoot:typeof r?.apiRoot==`string`?r.apiRoot:``,debug:!!r?.debug,allowFrom:Array.isArray(r?.allowFrom)?[...r.allowFrom]:[],groupAllowFrom:Array.isArray(r?.groupAllowFrom)?[...r.groupAllowFrom]:[],dmPolicy:r?.dmPolicy||`pairing`,groupPolicy:r?.groupPolicy||`open`,replyToMode:r?.replyToMode||`off`,streamMode:r?.streamMode??`partial`,historyLimit:typeof r?.historyLimit==`number`?r.historyLimit:50,textChunkLimit:typeof r?.textChunkLimit==`number`?r.textChunkLimit:4e3,proxy:typeof r?.proxy==`string`?r.proxy:``,accounts:{...c}},weixin:{enabled:!!i?.enabled,dmPolicy:i?.dmPolicy||`pairing`,allowFrom:Array.isArray(i?.allowFrom)?[...i.allowFrom]:[],debug:!!i?.debug,streamMode:i?.streamMode??`partial`,historyLimit:typeof i?.historyLimit==`number`?i.historyLimit:50,textChunkLimit:typeof i?.textChunkLimit==`number`?i.textChunkLimit:4e3,routeTag:i?.routeTag==null?``:String(i.routeTag),accounts:{...f}},feishu:{enabled:!!a?.enabled,defaultAccount:typeof a?.defaultAccount==`string`?a.defaultAccount:``,appId:typeof a?.appId==`string`?a.appId:``,appSecret:typeof a?.appSecret==`string`?a.appSecret:``,domain:typeof a?.domain==`string`&&a.domain||`feishu`,connectionMode:a?.connectionMode||`websocket`,verificationToken:typeof a?.verificationToken==`string`?a.verificationToken:``,encryptKey:typeof a?.encryptKey==`string`?a.encryptKey:``,webhookHost:typeof a?.webhookHost==`string`?a.webhookHost:`127.0.0.1`,webhookPort:typeof a?.webhookPort==`number`?a.webhookPort:3e3,webhookPath:typeof a?.webhookPath==`string`?a.webhookPath:`/feishu/events`,dmPolicy:a?.dmPolicy||`pairing`,groupPolicy:a?.groupPolicy||`allowlist`,allowFrom:Array.isArray(a?.allowFrom)?[...a.allowFrom]:[],groupAllowFrom:Array.isArray(a?.groupAllowFrom)?[...a.groupAllowFrom]:[],requireMention:a?.requireMention===void 0?!0:!!a.requireMention,historyLimit:typeof a?.historyLimit==`number`?a.historyLimit:50,textChunkLimit:typeof a?.textChunkLimit==`number`?a.textChunkLimit:4e3,renderMode:a?.renderMode||`auto`,streaming:a?.streaming===void 0?!1:!!a.streaming,reactionNotifications:a?.reactionNotifications||`own`,tools:a?.tools&&typeof a.tools==`object`&&!Array.isArray(a.tools)?{...a.tools}:void 0,actions:a?.actions&&typeof a.actions==`object`&&!Array.isArray(a.actions)?{...a.actions}:void 0,accounts:a?.accounts&&typeof a.accounts==`object`&&!Array.isArray(a.accounts)?{...a.accounts}:{}},dingtalk:{enabled:!!o?.enabled,defaultAccount:typeof o?.defaultAccount==`string`?o.defaultAccount:``,clientId:typeof o?.clientId==`string`?o.clientId:``,clientSecret:typeof o?.clientSecret==`string`?o.clientSecret:``,dmPolicy:o?.dmPolicy||`pairing`,groupPolicy:o?.groupPolicy||`open`,allowFrom:Array.isArray(o?.allowFrom)?[...o.allowFrom]:[],groupAllowFrom:Array.isArray(o?.groupAllowFrom)?[...o.groupAllowFrom]:[],requireMention:o?.requireMention===!0,debug:!!o?.debug,endpoint:typeof o?.endpoint==`string`&&o.endpoint.trim()?o.endpoint.trim():`https://api.dingtalk.com`,historyLimit:typeof o?.historyLimit==`number`?o.historyLimit:50,textChunkLimit:typeof o?.textChunkLimit==`number`?o.textChunkLimit:4e3,accounts:o?.accounts&&typeof o.accounts==`object`&&!Array.isArray(o.accounts)?{...o.accounts}:{}}},_=V(p,pe(g.telegram),me(g.weixin),B(g.feishu),he(g.dingtalk),h);return{...g,bindingsFull:p.map(e=>({...e})),channelAgentRoutes:_,defaultAgentId:h}}async function Ee(e){return(await T(w(`/api/channels/weixin/login/start`),{method:`POST`,body:JSON.stringify(e??{})})).payload}async function Q(e){return(await T(w(`/api/channels/weixin/login/${encodeURIComponent(e)}`))).payload.status}async function De(e){let t=e.telegram,n=e.weixin,r=e.feishu,i=e.dingtalk,a=ge(e.bindingsFull,e.channelAgentRoutes,pe(t),me(n),B(r),he(i),e.defaultAgentId),o=(()=>{let e=n.routeTag.trim();return e?/^\d+$/.test(e)?Number(e):e:null})(),s=(await T(w(`/api/config`),{method:`PATCH`,body:JSON.stringify({bindings:a,channels:{telegram:{enabled:t.enabled,apiRoot:t.apiRoot||void 0,debug:t.debug,allowFrom:t.allowFrom,groupAllowFrom:t.groupAllowFrom.length?t.groupAllowFrom:void 0,dmPolicy:t.dmPolicy,groupPolicy:t.groupPolicy,replyToMode:t.replyToMode,streamMode:t.streamMode,historyLimit:t.historyLimit,textChunkLimit:t.textChunkLimit,proxy:t.proxy||void 0,accounts:t.accounts},weixin:{enabled:n.enabled,dmPolicy:n.dmPolicy,allowFrom:n.allowFrom,debug:n.debug,streamMode:n.streamMode,historyLimit:n.historyLimit,textChunkLimit:n.textChunkLimit,routeTag:o,accounts:n.accounts},feishu:{enabled:r.enabled,defaultAccount:r.defaultAccount||void 0,appId:r.appId,appSecret:r.appSecret||void 0,domain:r.domain||void 0,connectionMode:r.connectionMode,verificationToken:r.verificationToken?.trim()?r.verificationToken:void 0,encryptKey:r.encryptKey?.trim()?r.encryptKey:void 0,webhookHost:r.webhookHost?.trim()?r.webhookHost:void 0,webhookPort:typeof r.webhookPort==`number`?r.webhookPort:void 0,webhookPath:r.webhookPath?.trim()?r.webhookPath:void 0,dmPolicy:r.dmPolicy,groupPolicy:r.groupPolicy,allowFrom:r.allowFrom,groupAllowFrom:r.groupAllowFrom.length?r.groupAllowFrom:void 0,requireMention:r.requireMention,historyLimit:r.historyLimit,textChunkLimit:r.textChunkLimit,renderMode:r.renderMode,streaming:r.streaming,reactionNotifications:r.reactionNotifications,tools:r.tools,actions:r.actions,accounts:r.accounts},dingtalk:{enabled:i.enabled,defaultAccount:i.defaultAccount||void 0,clientId:i.clientId,clientSecret:i.clientSecret||void 0,dmPolicy:i.dmPolicy,groupPolicy:i.groupPolicy,allowFrom:i.allowFrom,groupAllowFrom:i.groupAllowFrom.length?i.groupAllowFrom:void 0,requireMention:i.requireMention,debug:i.debug,endpoint:i.endpoint?.trim()?i.endpoint:void 0,historyLimit:i.historyLimit,textChunkLimit:i.textChunkLimit,accounts:i.accounts}}})})).payload?.config;return ne(),s?Te(s):{...e,bindingsFull:a}}async function Oe(e){return(await T(w(`/api/channels/feishu/setup/start`),{method:`POST`,body:JSON.stringify(e??{})})).payload}async function ke(e){return(await T(w(`/api/channels/feishu/setup/${encodeURIComponent(e)}`))).payload.status}async function Ae(){return(await T(w(`/api/channels/dingtalk/setup/start`),{method:`POST`,body:JSON.stringify({})})).payload}async function je(e){return(await T(w(`/api/channels/dingtalk/setup/${encodeURIComponent(e)}`))).payload.status}function Me({open:e,onOpenChange:t,ch:n,onSetupSuccess:r,moreSettings:i}){let[o,s]=(0,Y.useState)(!1),[l,d]=(0,Y.useState)(null),[_,v]=(0,Y.useState)(null),[b,x]=(0,Y.useState)(null),[C,w]=(0,Y.useState)(null),[T,E]=(0,Y.useState)(!1),D=(0,Y.useCallback)(async()=>{x(null),d(null),v(null),s(!0);try{let e=await Ae();v(e.qrUrl),d(e.sessionKey)}catch(e){x(e instanceof Error?e.message:`Start failed`)}finally{s(!1)}},[]);(0,Y.useEffect)(()=>{if(!e){d(null),v(null),x(null),w(null),E(!1),s(!1);return}D()},[e,D]),(0,Y.useEffect)(()=>{if(!l)return;let e=!1,n,i=async()=>{try{let i=await je(l);if(e||i.phase===`polling`)return;if(i.phase===`done`){n!==void 0&&(window.clearInterval(n),n=void 0),d(null),i.ok?(v(null),t(!1),r({clientId:i.clientId})):(x(i.message),v(null));return}i.phase===`unknown`&&(n!==void 0&&window.clearInterval(n),x(i.message),d(null),v(null))}catch(t){e||(n!==void 0&&window.clearInterval(n),x(t instanceof Error?t.message:`Request failed`),d(null),v(null))}};return n=window.setInterval(()=>void i(),3e3),i(),()=>{e=!0,n!==void 0&&window.clearInterval(n)}},[l,t,r]),(0,Y.useEffect)(()=>{if(!_){w(null),E(!1);return}let e=!1;return E(!1),X.toDataURL(_,{width:208,margin:2,errorCorrectionLevel:`M`,color:{dark:`#000000ff`,light:`#ffffffff`}}).then(t=>{e||w(t)}).catch(()=>{e||(E(!0),w(null))}),()=>{e=!0}},[_]);let k=!!(_&&l),j=e&&!b&&!k;return(0,L.jsx)(p,{open:e,onOpenChange:t,children:(0,L.jsxs)(O,{children:[(0,L.jsx)(a,{className:u(`xopc-dialog-overlay fixed inset-0 bg-scrim backdrop-blur-[1px]`,N)}),(0,L.jsxs)(h,{className:u(`fixed left-1/2 top-1/2 max-h-[min(90vh,52rem)] w-[min(100%-2rem,32rem)] -translate-x-1/2 -translate-y-1/2`,S,`overflow-y-auto rounded-2xl border border-edge bg-surface-panel p-6 shadow-popover outline-none dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsx)(c,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`absolute right-3 top-3 z-20 rounded-lg p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`,"aria-label":n.dingtalkQrCloseAria,children:(0,L.jsx)(g,{className:`size-4`})})}),(0,L.jsx)(m,{className:`sr-only`,children:n.dingtalkQrModalTitle}),(0,L.jsx)(A,{className:`sr-only`,children:n.dingtalkQrModalSubtitle}),(0,L.jsxs)(`div`,{className:`text-center`,children:[(0,L.jsx)(`p`,{className:`text-lg font-semibold tracking-tight text-fg`,children:n.dingtalkQrModalTitle}),(0,L.jsx)(`p`,{className:`mt-1.5 text-sm text-fg-muted`,children:n.dingtalkQrModalSubtitle})]}),(0,L.jsxs)(`div`,{className:`mt-6 flex min-h-[17.5rem] flex-col items-center justify-center gap-3`,children:[b?(0,L.jsx)(`p`,{className:`text-center text-sm text-red-600 dark:text-red-400`,children:b}):null,!b&&(k||j)?(0,L.jsxs)(`div`,{className:`flex w-full flex-col items-center gap-3`,children:[(0,L.jsx)(`p`,{className:`text-sm text-fg-muted`,children:j?n.dingtalkQrStarting:n.dingtalkQrScanHint}),(0,L.jsxs)(`div`,{className:u(`relative flex h-52 w-52 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-edge-subtle bg-white p-3 dark:border-edge`,j&&`bg-surface-muted/40 dark:bg-surface-base`),children:[j?(0,L.jsx)(`div`,{className:`absolute inset-3 animate-pulse rounded-md bg-surface-muted dark:bg-surface-hover`,"aria-hidden":!0}):null,k&&_&&!b&&C&&!T?(0,L.jsx)(`img`,{src:C,alt:``,className:`relative z-[1] size-full object-contain`}):null,k&&_&&!b&&!C&&!T&&!j?(0,L.jsx)(`p`,{className:`relative z-[1] px-2 text-center text-sm text-fg-muted`,children:n.dingtalkQrEncoding}):null,k&&_&&!b&&T?(0,L.jsxs)(`div`,{className:`relative z-[1] flex size-full flex-col items-center justify-center gap-2 px-1`,children:[(0,L.jsx)(`p`,{className:`text-center text-xs text-fg-muted`,children:n.dingtalkQrImageError}),(0,L.jsxs)(`a`,{href:_,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 text-xs text-accent underline-offset-2 hover:underline`,children:[(0,L.jsx)(y,{className:`size-3 shrink-0`}),n.dingtalkQrOpenLink]})]}):null]})]}):null]}),(0,L.jsx)(`div`,{className:`mt-6`,children:(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`h-11 w-full rounded-full border-0 bg-fg text-surface-panel hover:opacity-90 dark:bg-fg dark:text-surface-panel`,disabled:o,onClick:()=>void D(),children:o?n.dingtalkQrStarting:n.dingtalkQrRegenerate})}),i?(0,L.jsx)(`div`,{className:`mt-6 border-t border-edge-subtle pt-4 dark:border-edge-subtle`,children:i}):null]})]})})}function Ne({ch:e,form:t,baseline:n,showFeishuSecret:r,setShowFeishuSecret:i,showFeishuWebhookSecrets:a,setShowFeishuWebhookSecrets:o,feishuCopied:s,feishuWebhookCopied:c,copyFeishuSecret:l,copyFeishuWebhookConfig:p,updateFeishu:m,updateChannelAgentRoute:h,feishuAccountsDraft:g,setFeishuAccountsDraft:_,feishuAccountsError:v,onFeishuAccountsBlur:y,dmOpts:x,groupOpts:S,chatAgents:C,saving:w,dirty:T,save:D,discard:O}){let k=H,A=t.feishu,N=n?.feishu?.appSecret??``,P=!r&&!!String(N).trim()&&A.appSecret===N;return(0,L.jsxs)(`details`,{className:`group rounded-xl border border-edge-subtle bg-surface-base open:pb-3 dark:border-edge`,children:[(0,L.jsx)(`summary`,{className:`cursor-pointer list-none rounded-xl px-3 py-2.5 text-sm font-medium text-fg transition-colors hover:bg-surface-hover group-open:rounded-b-none [&::-webkit-details-marker]:hidden`,children:(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,L.jsx)(j,{className:`size-4 shrink-0 text-fg-muted transition-transform group-open:rotate-180`}),e.advancedShow]})}),(0,L.jsxs)(`div`,{className:`space-y-4 border-t border-edge-subtle px-3 pb-3 pt-3 dark:border-edge-subtle`,children:[(0,L.jsx)(`p`,{className:`text-xs leading-relaxed text-fg-muted`,children:e.feishuAdvancedHint}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-start gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox mt-0.5`,checked:A.enabled,onChange:e=>m({enabled:e.target.checked})}),(0,L.jsx)(`span`,{children:e.enableFeishuAria})]}),(0,L.jsxs)(`div`,{className:`space-y-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(K,{children:[e.feishuAppId,(0,L.jsx)(`span`,{className:`text-red-600 dark:text-red-400`,children:` *`})]}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),value:A.appId,onChange:e=>m({appId:e.target.value}),placeholder:`cli_xxx`}),(0,L.jsx)(q,{children:e.feishuAppIdDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(K,{children:[e.feishuAppSecret,(0,L.jsx)(`span`,{className:`text-red-600 dark:text-red-400`,children:` *`})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:r?`text`:`password`,autoComplete:`off`,readOnly:P,value:P?`*`.repeat(Math.max(1,A.appSecret.length)):A.appSecret,onChange:e=>{P||m({appSecret:e.target.value})},placeholder:`••••••••`}),A.appSecret?(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void l(),children:[s?(0,L.jsx)(E,{className:`size-3.5`}):(0,L.jsx)(M,{className:`size-3.5`}),s?e.copied:e.copy]}):null,(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>i(e=>!e),children:[r?(0,L.jsx)(b,{className:`size-3.5`}):(0,L.jsx)(d,{className:`size-3.5`}),r?e.hide:e.show]})]}),(0,L.jsx)(q,{children:e.feishuAppSecretDesc})]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.feishuDomain}),(0,L.jsxs)(`select`,{className:k(),value:String(A.domain||`feishu`),onChange:e=>m({domain:e.target.value}),children:[(0,L.jsx)(`option`,{value:`feishu`,children:`feishu`}),(0,L.jsx)(`option`,{value:`lark`,children:`lark`})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.connectionMode}),(0,L.jsxs)(`select`,{className:k(),value:A.connectionMode,onChange:e=>m({connectionMode:e.target.value}),children:[(0,L.jsx)(`option`,{value:`websocket`,children:`websocket`}),(0,L.jsx)(`option`,{value:`webhook`,children:`webhook`})]}),(0,L.jsx)(q,{children:e.connectionModeDesc})]})]}),A.connectionMode===`webhook`?(0,L.jsxs)(`div`,{className:`rounded-xl border border-edge-subtle bg-surface px-4 py-3 dark:border-edge-subtle`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between gap-3`,children:[(0,L.jsx)(`div`,{className:`text-sm font-medium text-fg`,children:e.webhookTitle}),(0,L.jsxs)(`div`,{className:`flex gap-2`,children:[(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>o(e=>!e),children:[a?(0,L.jsx)(b,{className:`size-3.5`}):(0,L.jsx)(d,{className:`size-3.5`}),a?e.hide:e.show]}),(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void p(),children:[c?(0,L.jsx)(E,{className:`size-3.5`}):(0,L.jsx)(M,{className:`size-3.5`}),c?e.copied:e.copy]})]})]}),(0,L.jsxs)(`div`,{className:`mt-3 grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.verificationToken}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:a?`text`:`password`,autoComplete:`off`,value:A.verificationToken??``,onChange:e=>m({verificationToken:e.target.value})}),(0,L.jsx)(q,{children:e.verificationTokenDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.encryptKey}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:a?`text`:`password`,autoComplete:`off`,value:A.encryptKey??``,onChange:e=>m({encryptKey:e.target.value})}),(0,L.jsx)(q,{children:e.encryptKeyDesc})]})]}),(0,L.jsxs)(`div`,{className:`mt-3 grid gap-3 sm:grid-cols-3`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5 sm:col-span-2`,children:[(0,L.jsx)(K,{children:e.webhookHost}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),value:A.webhookHost??``,onChange:e=>m({webhookHost:e.target.value}),placeholder:`127.0.0.1`})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.webhookPort}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:`number`,inputMode:`numeric`,value:String(A.webhookPort??``),onChange:e=>m({webhookPort:Number(e.target.value||`0`)||0}),placeholder:`3000`})]})]}),(0,L.jsxs)(`div`,{className:`mt-3 flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.webhookPath}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),value:A.webhookPath??``,onChange:e=>m({webhookPath:e.target.value}),placeholder:`/feishu/events`}),(0,L.jsx)(q,{children:e.webhookPathDesc})]})]}):null,(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.renderMode}),(0,L.jsxs)(`select`,{className:k(),value:A.renderMode,onChange:e=>m({renderMode:e.target.value}),children:[(0,L.jsx)(`option`,{value:`auto`,children:`auto`}),(0,L.jsx)(`option`,{value:`raw`,children:`raw`}),(0,L.jsx)(`option`,{value:`card`,children:`card`})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.reactionNotifications}),(0,L.jsxs)(`select`,{className:k(),value:A.reactionNotifications,onChange:e=>m({reactionNotifications:e.target.value}),children:[(0,L.jsx)(`option`,{value:`off`,children:`off`}),(0,L.jsx)(`option`,{value:`own`,children:`own`}),(0,L.jsx)(`option`,{value:`all`,children:`all`})]})]})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:A.streaming,onChange:e=>m({streaming:e.target.checked})}),e.enableStreaming]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.dmPolicy}),(0,L.jsx)(`select`,{className:k(),value:A.dmPolicy,onChange:e=>m({dmPolicy:e.target.value}),children:x.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.groupPolicy}),(0,L.jsx)(`select`,{className:k(),value:A.groupPolicy,onChange:e=>m({groupPolicy:e.target.value}),children:S.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})]})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:A.requireMention,onChange:e=>m({requireMention:e.target.checked})}),e.requireMention]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.allowFromDm}),(0,L.jsx)(`textarea`,{className:u(k(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`ou_xxx, on_xxx`,value:W(A.allowFrom),onChange:e=>m({allowFrom:U(e.target.value)})}),(0,L.jsx)(q,{children:e.allowFromDmDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.allowFromGroups}),(0,L.jsx)(`textarea`,{className:u(k(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`oc_xxx, oc_yyy`,value:W(A.groupAllowFrom),onChange:e=>m({groupAllowFrom:U(e.target.value)})}),(0,L.jsx)(q,{children:e.allowFromGroupsDesc})]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.historyLimit}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:`number`,inputMode:`numeric`,value:String(A.historyLimit),onChange:e=>m({historyLimit:Number(e.target.value||`0`)||0})})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.textChunkLimit}),(0,L.jsx)(`input`,{className:u(k(),`min-w-0 flex-1 font-mono text-xs`),type:`number`,inputMode:`numeric`,value:String(A.textChunkLimit),onChange:e=>m({textChunkLimit:Number(e.target.value||`0`)||0})})]})]}),(0,L.jsxs)(`div`,{className:`rounded-xl border border-edge-subtle bg-surface px-4 py-3 dark:border-edge-subtle`,children:[(0,L.jsx)(`div`,{className:`text-sm font-medium text-fg`,children:e.feishuToolsTitle}),(0,L.jsx)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[[`doc`,e.feishuToolDoc],[`wiki`,e.feishuToolWiki],[`drive`,e.feishuToolDrive],[`perm`,e.feishuToolPerm],[`bitable`,e.feishuToolBitable],[`scopes`,e.feishuToolScopes]].map(([e,t])=>(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:!!A.tools?.[e],onChange:t=>m({tools:{...A.tools,[e]:t.target.checked}})}),t]},e))}),(0,L.jsx)(`div`,{className:`mt-2`,children:(0,L.jsx)(q,{children:e.feishuToolsDesc})})]}),(0,L.jsxs)(`div`,{className:`rounded-xl border border-edge-subtle bg-surface px-4 py-3 dark:border-edge-subtle`,children:[(0,L.jsx)(`div`,{className:`text-sm font-medium text-fg`,children:e.feishuActionsTitle}),(0,L.jsxs)(`label`,{className:`mt-2 flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:!!A.actions?.reactions,onChange:e=>m({actions:{...A.actions,reactions:e.target.checked}})}),e.feishuActionReactions]})]}),(0,L.jsx)(Se,{accountIds:B(A),routes:t.channelAgentRoutes.feishu,defaultAgentId:t.defaultAgentId,agentItems:C?.items??[],disabled:w,onChange:(e,t)=>h(`feishu`,e,t),ch:e}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:e.multiAccountJson}),(0,L.jsx)(`textarea`,{className:u(k(),`min-h-[140px] resize-y font-mono text-xs`),spellCheck:!1,value:g,onChange:e=>_(e.target.value),onBlur:y,placeholder:`{ "default": { "appId": "...", "appSecret": "...", "enabled": true } }`}),v?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:v}):(0,L.jsx)(q,{children:e.multiAccountJsonDesc})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:justify-end`,children:[(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`w-full sm:w-auto`,disabled:!T||w,onClick:O,children:e.discard}),(0,L.jsx)(f,{type:`button`,variant:`primary`,className:`w-full sm:w-auto`,disabled:!T||w,onClick:async()=>{await D()},children:w?e.saving:e.save})]})]})]})}function Pe({open:e,onOpenChange:t,ch:n,onSetupSuccess:r,moreSettings:i}){let[o,s]=(0,Y.useState)(!1),[l,d]=(0,Y.useState)(`feishu`),[_,v]=(0,Y.useState)(null),[b,x]=(0,Y.useState)(null),[C,w]=(0,Y.useState)(null),[T,E]=(0,Y.useState)(null),[D,k]=(0,Y.useState)(!1),j=(0,Y.useCallback)(async e=>{d(e),w(null),v(null),x(null),s(!0);try{let t=await Oe({domain:e});x(t.qrUrl),v(t.sessionKey)}catch(e){w(e instanceof Error?e.message:`Start failed`)}finally{s(!1)}},[]);(0,Y.useEffect)(()=>{if(!e){v(null),x(null),w(null),E(null),k(!1),d(`feishu`),s(!1);return}j(`feishu`)},[e,j]),(0,Y.useEffect)(()=>{if(!_)return;let e=!1,n,i=async()=>{try{let i=await ke(_);if(e||i.phase===`polling`)return;if(i.phase===`done`){n!==void 0&&(window.clearInterval(n),n=void 0),v(null),i.ok?(x(null),t(!1),r({appId:i.appId,domain:i.domain,openId:i.openId})):(w(i.message),x(null));return}i.phase===`unknown`&&(n!==void 0&&window.clearInterval(n),w(i.message),v(null),x(null))}catch(t){e||(n!==void 0&&window.clearInterval(n),w(t instanceof Error?t.message:`Request failed`),v(null),x(null))}};return n=window.setInterval(()=>void i(),3e3),i(),()=>{e=!0,n!==void 0&&window.clearInterval(n)}},[_,t,r]),(0,Y.useEffect)(()=>{if(!b){E(null),k(!1);return}let e=!1;return k(!1),X.toDataURL(b,{width:208,margin:2,errorCorrectionLevel:`M`,color:{dark:`#000000ff`,light:`#ffffffff`}}).then(t=>{e||E(t)}).catch(()=>{e||(k(!0),E(null))}),()=>{e=!0}},[b]);let M=!!(b&&_),P=e,F=e&&!C&&!M;return(0,L.jsx)(p,{open:e,onOpenChange:t,children:(0,L.jsxs)(O,{children:[(0,L.jsx)(a,{className:u(`xopc-dialog-overlay fixed inset-0 bg-scrim backdrop-blur-[1px]`,N)}),(0,L.jsxs)(h,{className:u(`fixed left-1/2 top-1/2 max-h-[min(90vh,52rem)] w-[min(100%-2rem,32rem)] -translate-x-1/2 -translate-y-1/2`,S,`overflow-y-auto rounded-2xl border border-edge bg-surface-panel p-6 shadow-popover outline-none dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsx)(c,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`absolute right-3 top-3 z-20 rounded-lg p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`,"aria-label":n.feishuQrCloseAria,children:(0,L.jsx)(g,{className:`size-4`})})}),(0,L.jsx)(m,{className:`sr-only`,children:n.feishuQrModalTitle}),(0,L.jsx)(A,{className:`sr-only`,children:n.feishuQrModalSubtitle}),(0,L.jsxs)(`div`,{className:`text-center`,children:[(0,L.jsx)(`p`,{className:`text-lg font-semibold tracking-tight text-fg`,children:n.feishuQrModalTitle}),(0,L.jsx)(`p`,{className:`mt-1.5 text-sm text-fg-muted`,children:n.feishuQrModalSubtitle})]}),(0,L.jsxs)(`div`,{className:`mt-6 flex min-h-[17.5rem] flex-col items-center justify-center gap-3`,children:[C?(0,L.jsx)(`p`,{className:`text-center text-sm text-red-600 dark:text-red-400`,children:C}):null,!C&&(M||F)?(0,L.jsxs)(`div`,{className:`flex w-full flex-col items-center gap-3`,children:[(0,L.jsx)(`p`,{className:`text-sm text-fg-muted`,children:F?n.feishuQrStarting:n.feishuQrScanHint}),(0,L.jsxs)(`div`,{className:u(`relative flex h-52 w-52 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-edge-subtle bg-white p-3 dark:border-edge`,F&&`bg-surface-muted/40 dark:bg-surface-base`),children:[F?(0,L.jsx)(`div`,{className:`absolute inset-3 animate-pulse rounded-md bg-surface-muted dark:bg-surface-hover`,"aria-hidden":!0}):null,M&&b&&!C&&T&&!D?(0,L.jsx)(`img`,{src:T,alt:``,className:`relative z-[1] size-full object-contain`}):null,M&&b&&!C&&!T&&!D&&!F?(0,L.jsx)(`p`,{className:`relative z-[1] px-2 text-center text-sm text-fg-muted`,children:n.feishuQrEncoding}):null,M&&b&&!C&&D?(0,L.jsxs)(`div`,{className:`relative z-[1] flex size-full flex-col items-center justify-center gap-2 px-1`,children:[(0,L.jsx)(`p`,{className:`text-center text-xs text-fg-muted`,children:n.feishuQrImageError}),(0,L.jsxs)(`a`,{href:b,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 text-xs text-accent underline-offset-2 hover:underline`,children:[(0,L.jsx)(y,{className:`size-3 shrink-0`}),n.feishuQrOpenLink]})]}):null]})]}):null]}),P?(0,L.jsx)(`div`,{className:`mx-auto mt-4 flex w-full max-w-[20rem] justify-center px-1`,role:`group`,"aria-label":n.feishuRegionSwitchAria,children:(0,L.jsxs)(`div`,{className:`inline-flex w-full rounded-full border border-edge bg-surface-muted/60 p-1 dark:border-edge dark:bg-surface-base`,children:[(0,L.jsx)(`button`,{type:`button`,disabled:o,className:u(`min-w-0 flex-1 rounded-full px-3 py-2 text-center text-sm font-medium transition-colors`,l===`feishu`?`bg-surface-panel text-fg shadow-sm dark:bg-surface-panel`:`text-fg-muted hover:text-fg`),onClick:()=>void j(`feishu`),children:n.feishuRegionChina}),(0,L.jsx)(`button`,{type:`button`,disabled:o,className:u(`min-w-0 flex-1 rounded-full px-3 py-2 text-center text-sm font-medium transition-colors`,l===`lark`?`bg-surface-panel text-fg shadow-sm dark:bg-surface-panel`:`text-fg-muted hover:text-fg`),onClick:()=>void j(`lark`),children:n.feishuRegionInternational})]})}):null,(0,L.jsx)(`div`,{className:`mt-6`,children:(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`h-11 w-full rounded-full border-0 bg-fg text-surface-panel hover:opacity-90 dark:bg-fg dark:text-surface-panel`,disabled:o,onClick:()=>void j(l),children:o?n.feishuQrStarting:n.feishuQrRegenerate})}),i?(0,L.jsx)(`div`,{className:`mt-6 border-t border-edge-subtle pt-4 dark:border-edge-subtle`,children:i}):null]})]})})}function $({tg:e,updateTelegram:t,ch:n,dmOpts:r,groupOpts:i,replyOpts:a,streamOpts:o,tgAccountsDraft:s,setTgAccountsDraft:c,tgAccountsError:l,onTgAccountsBlur:d}){let f=H;return(0,L.jsxs)(`div`,{className:`space-y-4 border-t border-edge-subtle pt-4 dark:border-edge`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.apiRoot}),(0,L.jsx)(`input`,{className:f(),value:e.apiRoot,onChange:e=>t({apiRoot:e.target.value}),placeholder:`https://api.telegram.org`})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.proxy}),(0,L.jsx)(`input`,{className:f(),value:e.proxy,onChange:e=>t({proxy:e.target.value}),placeholder:`http://proxy.example.com:8080`})]}),(0,L.jsx)(J,{label:n.dmPolicy,value:e.dmPolicy,onChange:e=>t({dmPolicy:e}),options:r}),(0,L.jsx)(J,{label:n.groupPolicy,value:e.groupPolicy,onChange:e=>t({groupPolicy:e}),options:i}),(0,L.jsx)(J,{label:n.replyToMode,value:e.replyToMode,onChange:e=>t({replyToMode:e}),options:a}),(0,L.jsx)(J,{label:n.streamMode,value:e.streamMode,onChange:e=>t({streamMode:e}),options:o}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.allowFromGroups}),(0,L.jsx)(`textarea`,{className:u(f(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`-1001234567890`,value:W(e.groupAllowFrom),onChange:e=>t({groupAllowFrom:U(e.target.value)})})]}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.historyLimit}),(0,L.jsx)(`input`,{type:`number`,min:10,max:200,className:f(),value:e.historyLimit,onChange:e=>t({historyLimit:parseInt(e.target.value,10)||50})})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.textChunkLimit}),(0,L.jsx)(`input`,{type:`number`,min:1e3,max:1e4,step:100,className:f(),value:e.textChunkLimit,onChange:e=>t({textChunkLimit:parseInt(e.target.value,10)||4e3})})]})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:e.debug,onChange:e=>t({debug:e.target.checked})}),n.telegramDebug]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.multiAccountJson}),(0,L.jsx)(`textarea`,{className:u(f(),`min-h-[140px] resize-y font-mono text-xs`),spellCheck:!1,value:s,onChange:e=>c(e.target.value),onBlur:d,placeholder:`{ "personal": { "accountId": "personal", "botToken": "...", ... } }`}),l?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:l}):(0,L.jsx)(q,{children:n.multiAccountJsonDesc})]})]})}function Fe({open:e,onOpenChange:t,ch:n,form:r,baseline:i,tgAdvanced:o,setTgAdvanced:s,showToken:l,setShowToken:_,copied:v,copyToken:y,updateTelegram:x,updateChannelAgentRoute:C,tgAccountsDraft:w,setTgAccountsDraft:T,tgAccountsError:D,onTgAccountsBlur:k,dmOpts:P,groupOpts:F,replyOpts:ee,streamOpts:te,chatAgents:ne,saving:re,dirty:ie,save:ae,discard:oe}){let se=H,I=r.telegram,ce=i?ve(i.telegram):``,le=ve(I),ue=!l&&!!String(ce).trim()&&le===ce;return(0,L.jsx)(p,{open:e,onOpenChange:t,children:(0,L.jsxs)(O,{children:[(0,L.jsx)(a,{className:u(`xopc-dialog-overlay fixed inset-0 bg-scrim backdrop-blur-[1px]`,N)}),(0,L.jsxs)(h,{className:u(`fixed left-1/2 top-1/2 max-h-[min(90vh,48rem)] w-[min(100%-2rem,36rem)] -translate-x-1/2 -translate-y-1/2`,S,`overflow-y-auto rounded-2xl border border-edge bg-surface-panel p-6 shadow-popover outline-none dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(m,{className:`text-lg font-semibold tracking-tight text-fg`,children:n.telegramTitle}),(0,L.jsx)(A,{className:`mt-1 text-sm text-fg-muted`,children:n.telegramSubtitle})]}),(0,L.jsx)(c,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`rounded-lg p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`,"aria-label":n.modalCancel,children:(0,L.jsx)(g,{className:`size-4`})})})]}),(0,L.jsxs)(`label`,{className:`mt-6 flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:I.enabled,onChange:e=>x({enabled:e.target.checked})}),(0,L.jsx)(`span`,{children:n.enableTelegramAria})]}),(0,L.jsxs)(`div`,{className:`mt-6 space-y-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(K,{children:[n.telegramToken,(0,L.jsx)(`span`,{className:`text-red-600 dark:text-red-400`,children:` *`})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,L.jsx)(`input`,{className:u(se(),`min-w-0 flex-1 font-mono text-xs`),type:l?`text`:`password`,autoComplete:`off`,readOnly:ue,value:ue?`*`.repeat(Math.max(1,ce.length)):le,onChange:e=>{if(ue)return;let t=I.accounts?.default??Z(`default`);x({accounts:{...I.accounts,default:{...t,accountId:`default`,botToken:e.target.value}}})},placeholder:`123456789:ABCdefGHIjklMNOpqrsTUVwxyz`}),le?(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void y(),children:[v?(0,L.jsx)(E,{className:`size-3.5`}):(0,L.jsx)(M,{className:`size-3.5`}),v?n.copied:n.copy]}):null,(0,L.jsxs)(f,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>_(e=>!e),children:[l?(0,L.jsx)(b,{className:`size-3.5`}):(0,L.jsx)(d,{className:`size-3.5`}),l?n.hide:n.show]})]}),(0,L.jsx)(q,{children:n.telegramTokenDesc})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.allowFromDm}),(0,L.jsx)(`textarea`,{className:u(se(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`123456789, 987654321`,value:W(I.allowFrom),onChange:e=>x({allowFrom:U(e.target.value)})}),(0,L.jsx)(q,{children:n.allowFromDmDesc})]}),(0,L.jsx)(Se,{accountIds:pe(I),routes:r.channelAgentRoutes.telegram,defaultAgentId:r.defaultAgentId,agentItems:ne?.items??[],disabled:re,onChange:(e,t)=>C(`telegram`,e,t),ch:n}),(0,L.jsxs)(f,{type:`button`,variant:`ghost`,className:`-ml-2 h-auto justify-start px-2 py-1 text-sm text-fg-muted hover:text-fg`,onClick:()=>s(e=>!e),children:[(0,L.jsx)(j,{className:u(`mr-1 size-4 transition-transform`,o&&`rotate-180`)}),o?n.advancedHide:n.advancedShow]}),o?(0,L.jsx)($,{tg:I,updateTelegram:x,ch:n,dmOpts:P,groupOpts:F,replyOpts:ee,streamOpts:te,tgAccountsDraft:w,setTgAccountsDraft:T,tgAccountsError:D,onTgAccountsBlur:k}):null]}),(0,L.jsxs)(`div`,{className:`mt-8 flex flex-wrap justify-end gap-2 border-t border-edge-subtle pt-4 dark:border-edge-subtle`,children:[(0,L.jsx)(f,{type:`button`,variant:`secondary`,onClick:()=>t(!1),children:n.modalCancel}),(0,L.jsx)(f,{type:`button`,variant:`secondary`,disabled:!ie||re,onClick:oe,children:n.discard}),(0,L.jsx)(f,{type:`button`,variant:`primary`,disabled:!ie||re,onClick:async()=>{await ae()&&t(!1)},children:re?n.saving:n.save})]})]})]})})}function Ie(){let e=o(e=>e.language),t=D(e),n=t.channelsSettings,i=!!P(e=>e.token),[a,s]=(0,Y.useState)(null),[c,l]=(0,Y.useState)(null),[u,d]=(0,Y.useState)(!1),[f,p]=(0,Y.useState)(null),[m,h]=(0,Y.useState)(!1),[g,_]=(0,Y.useState)(!1),[y,b]=(0,Y.useState)(!1),[x,S]=(0,Y.useState)(!1),[C,w]=(0,Y.useState)(!1),[T,E]=(0,Y.useState)(null),[O,k]=(0,Y.useState)(null),[A,j]=(0,Y.useState)(null),[M,N]=(0,Y.useState)(null),[F,ee]=(0,Y.useState)(!1),[te,ne]=(0,Y.useState)(!1),[re,ie]=(0,Y.useState)(!1),[oe,se]=(0,Y.useState)(!1),[I,ce]=(0,Y.useState)(!1),[le,ue]=(0,Y.useState)(!1),[L,de]=(0,Y.useState)(!1),[fe,R]=(0,Y.useState)(!1),[z,pe]=(0,Y.useState)(!1),[me,B]=(0,Y.useState)(``),[he,V]=(0,Y.useState)(``),[ge,H]=(0,Y.useState)(``),[_e,U]=(0,Y.useState)(``),[W,ye]=(0,Y.useState)(``),[be,G]=(0,Y.useState)(``),[xe,K]=(0,Y.useState)(``),[q,J]=(0,Y.useState)(``),{data:Se}=r(i?`gateway-chat-agents-ch`:null,ae,{revalidateOnFocus:!1}),{data:Ce,error:X,isLoading:Z,mutate:Ee}=v(i),Q=(0,Y.useMemo)(()=>Ce?.payload?.config===void 0?null:Te(Ce.payload.config),[Ce]),Oe=(0,Y.useMemo)(()=>!a||!c?!1:JSON.stringify(a)!==JSON.stringify(c),[a,c]);(0,Y.useEffect)(()=>{if(!i){s(null),l(null);return}Q!==null&&(Oe||(s(Q),l(structuredClone(Q)),B(JSON.stringify(Q.telegram.accounts??{},null,2)),V(``),H(JSON.stringify(Q.weixin.accounts??{},null,2)),U(``),ye(JSON.stringify(Q.feishu?.accounts??{},null,2)),G(``),K(JSON.stringify(Q.dingtalk?.accounts??{},null,2)),J(``),h(!1)))},[i,Q,Oe]);let ke=!!(i&&Z&&Ce===void 0&&!X),Ae=X instanceof Error?X.message:X?String(X):null,je=(0,Y.useCallback)((e,t,n)=>{s(r=>{if(!r)return null;let i=e===`telegram`?`telegram`:e===`weixin`?`weixin`:e===`feishu`?`feishu`:`dingtalk`;return{...r,channelAgentRoutes:{...r.channelAgentRoutes,[i]:{...r.channelAgentRoutes[i],[t]:n.trim().toLowerCase()}}}})},[]),Me=(0,Y.useCallback)(e=>{s(t=>t?{...t,telegram:{...t.telegram,...e}}:null)},[]),Ne=(0,Y.useCallback)(e=>{s(t=>t?{...t,weixin:{...t.weixin,...e}}:null)},[]),Pe=(0,Y.useCallback)(e=>{s(t=>t?{...t,feishu:{...t.feishu,...e}}:null)},[]),$=(0,Y.useCallback)(e=>{s(t=>t?{...t,dingtalk:{...t.dingtalk,...e}}:null)},[]),Fe=(0,Y.useCallback)(async()=>{if(!a||u)return!1;d(!0),p(null),h(!1);try{let e=await De(a);s(e);let t=structuredClone(e);return l(t),B(JSON.stringify(t.telegram.accounts??{},null,2)),V(``),H(JSON.stringify(t.weixin.accounts??{},null,2)),U(``),ye(JSON.stringify(t.feishu?.accounts??{},null,2)),G(``),K(JSON.stringify(t.dingtalk?.accounts??{},null,2)),J(``),h(!0),window.setTimeout(()=>h(!1),2500),!0}catch(e){return p(e instanceof Error?e.message:n.saveError),!1}finally{d(!1)}},[a,u,n.saveError]),Ie=(0,Y.useCallback)(()=>{if(!c)return;let e=structuredClone(c);s(e),B(JSON.stringify(e.telegram.accounts??{},null,2)),V(``),H(JSON.stringify(e.weixin.accounts??{},null,2)),U(``),ye(JSON.stringify(e.feishu?.accounts??{},null,2)),G(``),K(JSON.stringify(e.dingtalk?.accounts??{},null,2)),J(``),p(null),h(!1)},[c]),Le=(0,Y.useCallback)(async(e,t)=>{if(!a||u)return;let r=a,i=e===`weixin`?{...a,weixin:{...a.weixin,enabled:t}}:e===`telegram`?{...a,telegram:{...a.telegram,enabled:t}}:e===`feishu`?{...a,feishu:{...a.feishu,enabled:t}}:{...a,dingtalk:{...a.dingtalk,enabled:t}};s(i),d(!0),p(null);try{let e=await De(i);s(e);let t=structuredClone(e);l(t),B(JSON.stringify(t.telegram.accounts??{},null,2)),H(JSON.stringify(t.weixin.accounts??{},null,2)),ye(JSON.stringify(t.feishu?.accounts??{},null,2)),K(JSON.stringify(t.dingtalk?.accounts??{},null,2))}catch(e){p(e instanceof Error?e.message:n.saveError),s(r)}finally{d(!1)}},[a,u,n.saveError]),Re=(0,Y.useCallback)(async()=>{if(!a||!T||u)return;let e=we(),t=T===`weixin`?{...a,weixin:e.weixin}:T===`telegram`?{...a,telegram:e.telegram}:T===`feishu`?{...a,feishu:e.feishu}:{...a,dingtalk:e.dingtalk};d(!0),p(null);try{let e=await De(t);s(e);let n=structuredClone(e);l(n),B(JSON.stringify(n.telegram.accounts??{},null,2)),H(JSON.stringify(n.weixin.accounts??{},null,2)),V(``),U(``),ye(JSON.stringify(n.feishu?.accounts??{},null,2)),G(``),K(JSON.stringify(n.dingtalk?.accounts??{},null,2)),J(``),E(null),h(!0),window.setTimeout(()=>h(!1),2500)}catch(e){p(e instanceof Error?e.message:n.saveError)}finally{d(!1)}},[a,T,u,n.saveError]),ze=(0,Y.useCallback)(async()=>{let e=a?ve(a.telegram):``;e&&(await navigator.clipboard.writeText(e).catch(()=>{}),ue(!0),window.setTimeout(()=>ue(!1),2e3))},[a]),Be=(0,Y.useCallback)(e=>{Pe({appId:e.appId,domain:e.domain,enabled:!0}),Ee(),j(n.feishuQrSetupSuccess),window.setTimeout(()=>j(null),4e3)},[Pe,Ee,n.feishuQrSetupSuccess]),Ve=(0,Y.useCallback)(e=>{$({clientId:e.clientId,enabled:!0}),Ee(),N(n.dingtalkQrSetupSuccess),window.setTimeout(()=>N(null),4e3)},[$,Ee,n.dingtalkQrSetupSuccess]),He=(0,Y.useCallback)(async()=>{let e=a?.dingtalk?.clientSecret;e&&(await navigator.clipboard.writeText(e).catch(()=>{}),R(!0),window.setTimeout(()=>R(!1),2e3))},[a]);return{language:e,m:t,ch:n,hasToken:i,loading:ke,fetchError:Ae,mutate:Ee,form:a,baseline:c,dirty:Oe,saving:u,error:f,saveOk:m,weixinModalOpen:g,setWeixinModalOpen:_,telegramModalOpen:y,setTelegramModalOpen:b,feishuModalOpen:x,setFeishuModalOpen:S,dingtalkModalOpen:C,setDingtalkModalOpen:w,removeTarget:T,setRemoveTarget:E,weixinSuccessBanner:O,setWeixinSuccessBanner:k,feishuSetupSuccessBanner:A,dingtalkSetupSuccessBanner:M,tgAdvanced:F,setTgAdvanced:ee,showToken:te,setShowToken:ne,showFeishuSecret:re,setShowFeishuSecret:ie,showFeishuWebhookSecrets:oe,setShowFeishuWebhookSecrets:se,showDingtalkSecret:I,setShowDingtalkSecret:ce,copied:le,feishuCopied:L,dingtalkCopied:fe,feishuWebhookCopied:z,tgAccountsDraft:me,setTgAccountsDraft:B,tgAccountsError:he,wxAccountsDraft:ge,setWxAccountsDraft:H,wxAccountsError:_e,feishuAccountsDraft:W,setFeishuAccountsDraft:ye,feishuAccountsError:be,dingtalkAccountsDraft:xe,setDingtalkAccountsDraft:K,dingtalkAccountsError:q,chatAgents:Se,updateChannelAgentRoute:je,updateTelegram:Me,updateWeixin:Ne,updateFeishu:Pe,updateDingtalk:$,save:Fe,discard:Ie,toggleChannelEnabled:Le,removeChannel:Re,copyToken:ze,handleFeishuQrSetupSuccess:Be,handleDingtalkQrSetupSuccess:Ve,copyFeishuSecret:(0,Y.useCallback)(async()=>{let e=a?.feishu?.appSecret;e&&(await navigator.clipboard.writeText(e).catch(()=>{}),de(!0),window.setTimeout(()=>de(!1),2e3))},[a]),copyDingtalkSecret:He,copyFeishuWebhookConfig:(0,Y.useCallback)(async()=>{let e=a?.feishu;if(!e)return;let t={connectionMode:e.connectionMode,verificationToken:e.verificationToken||``,encryptKey:e.encryptKey||``,webhookHost:e.webhookHost||``,webhookPort:e.webhookPort||0,webhookPath:e.webhookPath||``};await navigator.clipboard.writeText(JSON.stringify(t,null,2)).catch(()=>{}),pe(!0),window.setTimeout(()=>pe(!1),2e3)},[a]),onTgAccountsBlur:(0,Y.useCallback)(()=>{if(!a)return;let e=me.trim();if(!e){Me({accounts:{}}),V(``);return}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw Error(n.jsonObjectAccounts);Me({accounts:t}),V(``)}catch(e){V(e instanceof Error?e.message:n.jsonInvalid)}},[a,me,Me,n.jsonObjectAccounts,n.jsonInvalid]),onWxAccountsBlur:(0,Y.useCallback)(()=>{if(!a)return;let e=ge.trim();if(!e){Ne({accounts:{}}),U(``);return}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw Error(n.jsonObjectAccounts);Ne({accounts:t}),U(``)}catch(e){U(e instanceof Error?e.message:n.jsonInvalid)}},[a,ge,Ne,n.jsonObjectAccounts,n.jsonInvalid]),onFeishuAccountsBlur:(0,Y.useCallback)(()=>{if(!a)return;let e=W.trim();if(!e){Pe({accounts:{}}),G(``);return}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw Error(n.jsonObjectAccounts);Pe({accounts:t}),G(``)}catch(e){G(e instanceof Error?e.message:n.jsonInvalid)}},[a,W,Pe,n.jsonObjectAccounts,n.jsonInvalid]),onDingtalkAccountsBlur:(0,Y.useCallback)(()=>{if(!a)return;let e=xe.trim();if(!e){$({accounts:{}}),J(``);return}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw Error(n.jsonObjectAccounts);$({accounts:t}),J(``)}catch(e){J(e instanceof Error?e.message:n.jsonInvalid)}},[a,xe,$,n.jsonObjectAccounts,n.jsonInvalid]),dmOpts:(0,Y.useMemo)(()=>[`pairing`,`allowlist`,`open`,`disabled`].map(e=>({value:e,label:n.policy.dm[e]})),[n.policy.dm]),groupOpts:(0,Y.useMemo)(()=>[`open`,`disabled`,`allowlist`].map(e=>({value:e,label:n.policy.group[e]})),[n.policy.group]),replyOpts:(0,Y.useMemo)(()=>[`off`,`first`,`all`].map(e=>({value:e,label:n.policy.reply[e]})),[n.policy.reply]),streamOpts:(0,Y.useMemo)(()=>[`off`,`partial`,`block`].map(e=>({value:e,label:n.policy.stream[e]})),[n.policy.stream])}}function Le({open:e,onOpenChange:t,ch:n,onLoginSuccess:r,moreSettings:i}){let[o,s]=(0,Y.useState)(!1),[l,d]=(0,Y.useState)(null),[_,v]=(0,Y.useState)(null),[b,x]=(0,Y.useState)(null),[C,w]=(0,Y.useState)(null),[T,E]=(0,Y.useState)(null),[D,k]=(0,Y.useState)(!1),j=(0,Y.useCallback)(async()=>{x(null),w(null),d(null),s(!0);try{let e=await Ee();v(e.qrcodeUrl),d(e.sessionKey)}catch(e){x(e instanceof Error?e.message:`Start failed`)}finally{s(!1)}},[]);(0,Y.useEffect)(()=>{if(!e){d(null),v(null),x(null),E(null),k(!1),w(null);return}j()},[e,j]),(0,Y.useEffect)(()=>{if(!l)return;let e=!1,i,a=async()=>{try{let a=await Q(l);if(e)return;if(a.phase===`polling`){v(a.qrcodeUrl),a.qrStatus===`scaned`?w(n.weixinQrLoginScanned):w(null);return}if(a.phase===`done`){i!==void 0&&(window.clearInterval(i),i=void 0),d(null),a.ok?(v(null),t(!1),await r()):(x(a.message),v(null));return}a.phase===`unknown`&&w(null)}catch(t){e||(i!==void 0&&window.clearInterval(i),x(t instanceof Error?t.message:`Request failed`),d(null),v(null))}};return i=window.setInterval(()=>void a(),2e3),a(),()=>{e=!0,i!==void 0&&window.clearInterval(i)}},[l,n.weixinQrLoginScanned,n.weixinQrLoginSuccess,r,t]),(0,Y.useEffect)(()=>{if(!_){E(null),k(!1);return}let e=!1;return k(!1),X.toDataURL(_,{width:208,margin:2,errorCorrectionLevel:`M`,color:{dark:`#000000ff`,light:`#ffffffff`}}).then(t=>{e||E(t)}).catch(()=>{e||(k(!0),E(null))}),()=>{e=!0}},[_]);let M=!!(_&&l),P=e&&!b&&!M;return(0,L.jsx)(p,{open:e,onOpenChange:t,children:(0,L.jsxs)(O,{children:[(0,L.jsx)(a,{className:u(`xopc-dialog-overlay fixed inset-0 bg-scrim backdrop-blur-[1px]`,N)}),(0,L.jsxs)(h,{className:u(`fixed left-1/2 top-1/2 max-h-[min(90vh,52rem)] w-[min(100%-2rem,32rem)] -translate-x-1/2 -translate-y-1/2`,S,`overflow-y-auto rounded-2xl border border-edge bg-surface-panel p-6 shadow-popover outline-none dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsx)(c,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`absolute right-3 top-3 z-20 rounded-lg p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`,"aria-label":n.weixinQrModalCloseAria,children:(0,L.jsx)(g,{className:`size-4`})})}),(0,L.jsx)(m,{className:`sr-only`,children:n.weixinQrModalTitle}),(0,L.jsx)(A,{className:`sr-only`,children:n.weixinQrModalSubtitle}),(0,L.jsxs)(`div`,{className:`text-center`,children:[(0,L.jsx)(`p`,{className:`text-lg font-semibold tracking-tight text-fg`,children:n.weixinQrModalTitle}),(0,L.jsx)(`p`,{className:`mt-1.5 text-sm text-fg-muted`,children:n.weixinQrModalSubtitle})]}),(0,L.jsxs)(`div`,{className:`mt-6 flex min-h-[17.5rem] flex-col items-center justify-center gap-3`,children:[b?(0,L.jsx)(`p`,{className:`text-center text-sm text-red-600 dark:text-red-400`,children:b}):null,!b&&(M||P)?(0,L.jsxs)(`div`,{className:`flex w-full flex-col items-center gap-3`,children:[(0,L.jsx)(`p`,{className:u(`min-h-[1.25rem] text-center text-sm`,C&&!P?`text-accent`:`text-fg-muted`),children:P?n.weixinQrLoginBusy:C??`\xA0`}),(0,L.jsxs)(`div`,{className:u(`relative flex h-52 w-52 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-edge-subtle bg-white p-3 dark:border-edge`,P&&`bg-surface-muted/40 dark:bg-surface-base`),children:[P?(0,L.jsx)(`div`,{className:`absolute inset-3 animate-pulse rounded-md bg-surface-muted dark:bg-surface-hover`,"aria-hidden":!0}):null,M&&_&&!b&&T&&!D?(0,L.jsx)(`img`,{src:T,alt:``,className:`relative z-[1] size-full object-contain`}):null,M&&_&&!b&&!T&&!D&&!P?(0,L.jsx)(`p`,{className:`relative z-[1] px-2 text-center text-sm text-fg-muted`,children:n.weixinQrEncoding}):null,M&&_&&!b&&D?(0,L.jsxs)(`div`,{className:`relative z-[1] flex size-full flex-col items-center justify-center gap-2 px-1`,children:[(0,L.jsx)(`p`,{className:`text-center text-xs text-fg-muted`,children:n.weixinQrImageError}),(0,L.jsxs)(`a`,{href:_,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 text-xs text-accent underline-offset-2 hover:underline`,children:[(0,L.jsx)(y,{className:`size-3 shrink-0`}),n.weixinQrOpenLink]})]}):null]})]}):null]}),(0,L.jsx)(`div`,{className:`mt-6`,children:(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`h-11 w-full rounded-full border-0 bg-fg text-surface-panel hover:opacity-90 dark:bg-fg dark:text-surface-panel`,disabled:o,onClick:()=>void j(),children:o?n.weixinQrLoginBusy:n.weixinQrRegenerate})}),i?(0,L.jsx)(`div`,{className:`mt-6 border-t border-edge-subtle pt-4 dark:border-edge-subtle`,children:i}):null]})]})})}function Re({wx:e,updateWeixin:t,ch:n,dmOpts:r,streamOpts:i,wxAccountsDraft:a,setWxAccountsDraft:o,wxAccountsError:s,onWxAccountsBlur:c,channelAgentRoutesWx:l,defaultAgentId:d,agentItems:f,onAgentRouteChange:p,routingDisabled:m}){let h=H;return(0,L.jsxs)(`div`,{className:`space-y-4 border-t border-edge-subtle pt-4 dark:border-edge`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.weixinAllowFrom}),(0,L.jsx)(`textarea`,{className:u(h(),`min-h-[2.75rem] resize-y font-mono text-xs`),rows:2,placeholder:`wxid_..., openid_...`,value:e.allowFrom.join(`, `),onChange:e=>t({allowFrom:e.target.value.split(/[,\n]/).map(e=>e.trim()).filter(Boolean)})}),(0,L.jsx)(q,{children:n.weixinAllowFromDesc})]}),(0,L.jsx)(J,{label:n.dmPolicy,value:e.dmPolicy,onChange:e=>t({dmPolicy:e}),options:r}),(0,L.jsx)(J,{label:n.streamMode,value:e.streamMode,onChange:e=>t({streamMode:e}),options:i}),(0,L.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.historyLimit}),(0,L.jsx)(`input`,{type:`number`,min:10,max:200,className:h(),value:e.historyLimit,onChange:e=>t({historyLimit:parseInt(e.target.value,10)||50})})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.textChunkLimit}),(0,L.jsx)(`input`,{type:`number`,min:1e3,max:1e4,step:100,className:h(),value:e.textChunkLimit,onChange:e=>t({textChunkLimit:parseInt(e.target.value,10)||4e3})})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.weixinRouteTag}),(0,L.jsx)(`input`,{className:h(),value:e.routeTag,onChange:e=>t({routeTag:e.target.value}),placeholder:n.routeTagPlaceholder}),(0,L.jsx)(q,{children:n.weixinRouteTagDesc})]}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox`,checked:e.debug,onChange:e=>t({debug:e.target.checked})}),n.weixinDebug]}),(0,L.jsx)(q,{children:n.weixinDebugDesc}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(K,{children:n.weixinAccountsJson}),(0,L.jsx)(`textarea`,{className:u(h(),`min-h-[140px] resize-y font-mono text-xs`),spellCheck:!1,value:a,onChange:e=>o(e.target.value),onBlur:c,placeholder:`{ "personal": { "name": "...", "cdnBaseUrl": "...", "enabled": true } }`}),s?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:s}):(0,L.jsx)(q,{children:n.weixinAccountsJsonDesc})]}),(0,L.jsx)(Se,{accountIds:me(e),routes:l,defaultAgentId:d,agentItems:f,disabled:m,onChange:p,ch:n})]})}function ze({ch:e,wx:t,updateWeixin:n,dmOpts:r,streamOpts:i,wxAccountsDraft:a,setWxAccountsDraft:o,wxAccountsError:s,onWxAccountsBlur:c,form:l,chatAgents:u,onAgentRouteChange:d,saving:p,dirty:m,save:h,discard:g}){return(0,L.jsxs)(`details`,{className:`group rounded-xl border border-edge-subtle bg-surface-base open:pb-3 dark:border-edge`,children:[(0,L.jsx)(`summary`,{className:`cursor-pointer list-none rounded-xl px-3 py-2.5 text-sm font-medium text-fg transition-colors hover:bg-surface-hover group-open:rounded-b-none [&::-webkit-details-marker]:hidden`,children:(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,L.jsx)(j,{className:`size-4 shrink-0 text-fg-muted transition-transform group-open:rotate-180`}),e.advancedShow]})}),(0,L.jsxs)(`div`,{className:`space-y-4 border-t border-edge-subtle px-3 pb-3 pt-3 dark:border-edge-subtle`,children:[(0,L.jsx)(`p`,{className:`text-xs leading-relaxed text-fg-muted`,children:e.weixinAdvancedHint}),(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-start gap-2 text-sm text-fg`,children:[(0,L.jsx)(`input`,{type:`checkbox`,className:`ui-checkbox mt-0.5`,checked:t.enabled,onChange:e=>n({enabled:e.target.checked})}),(0,L.jsx)(`span`,{children:e.enableWeixinAria})]}),(0,L.jsx)(`div`,{className:`[&>div]:border-0 [&>div]:pt-0`,children:(0,L.jsx)(Re,{wx:t,updateWeixin:n,ch:e,dmOpts:r,streamOpts:i,wxAccountsDraft:a,setWxAccountsDraft:o,wxAccountsError:s,onWxAccountsBlur:c,channelAgentRoutesWx:l.channelAgentRoutes.weixin,defaultAgentId:l.defaultAgentId,agentItems:u?.items??[],onAgentRouteChange:d,routingDisabled:p})}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:justify-end`,children:[(0,L.jsx)(f,{type:`button`,variant:`secondary`,className:`w-full sm:w-auto`,disabled:!m||p,onClick:g,children:e.discard}),(0,L.jsx)(f,{type:`button`,variant:`primary`,className:`w-full sm:w-auto`,disabled:!m||p,onClick:async()=>{await h()},children:p?e.saving:e.save})]})]})]})}function Be(){let{language:e,m:t,ch:n,hasToken:r,loading:i,fetchError:a,mutate:o,form:s,baseline:c,dirty:l,saving:u,error:d,saveOk:p,weixinModalOpen:m,setWeixinModalOpen:h,telegramModalOpen:g,setTelegramModalOpen:v,feishuModalOpen:b,setFeishuModalOpen:x,removeTarget:S,setRemoveTarget:w,weixinSuccessBanner:T,setWeixinSuccessBanner:E,dingtalkModalOpen:D,setDingtalkModalOpen:O,feishuSetupSuccessBanner:k,dingtalkSetupSuccessBanner:A,tgAdvanced:j,setTgAdvanced:M,showToken:N,setShowToken:P,showFeishuSecret:F,setShowFeishuSecret:ee,showFeishuWebhookSecrets:ne,setShowFeishuWebhookSecrets:re,showDingtalkSecret:ae,setShowDingtalkSecret:oe,copied:se,feishuCopied:I,dingtalkCopied:ce,feishuWebhookCopied:le,tgAccountsDraft:ue,setTgAccountsDraft:R,tgAccountsError:z,wxAccountsDraft:pe,setWxAccountsDraft:me,wxAccountsError:B,feishuAccountsDraft:he,setFeishuAccountsDraft:V,feishuAccountsError:ge,dingtalkAccountsDraft:H,setDingtalkAccountsDraft:_e,dingtalkAccountsError:U,chatAgents:W,updateChannelAgentRoute:ve,updateTelegram:K,updateWeixin:q,updateFeishu:J,updateDingtalk:Se,save:Y,discard:X,toggleChannelEnabled:Z,removeChannel:we,copyToken:Te,handleFeishuQrSetupSuccess:Ee,handleDingtalkQrSetupSuccess:Q,copyFeishuSecret:De,copyDingtalkSecret:Oe,copyFeishuWebhookConfig:ke,onTgAccountsBlur:Ae,onWxAccountsBlur:je,onFeishuAccountsBlur:$,onDingtalkAccountsBlur:Re,dmOpts:Be,groupOpts:Ve,replyOpts:He,streamOpts:Ue}=Ie();if(!r)return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:[(0,L.jsx)(`h1`,{className:`text-lg font-semibold text-fg`,children:t.settingsSections.channels}),(0,L.jsx)(`p`,{className:`text-sm text-fg-muted`,children:n.needToken})]});if(i)return(0,L.jsxs)(`div`,{className:`mx-auto w-full max-w-app-main px-4 py-8`,children:[(0,L.jsx)(`div`,{className:`h-8 w-48 animate-pulse rounded bg-surface-hover`}),(0,L.jsx)(`div`,{className:`mt-6 h-32 animate-pulse rounded-xl bg-surface-hover`}),(0,L.jsx)(`p`,{className:`mt-4 text-sm text-fg-muted`,children:n.loading})]});if(!s)return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:[(0,L.jsx)(`p`,{className:`text-sm text-fg-muted`,children:d??a??n.loadError}),(0,L.jsx)(f,{type:`button`,variant:`secondary`,onClick:()=>void o(),children:n.retry})]});let We=s.weixin,Ge=s.telegram,Ke=s.feishu,qe=s.dingtalk,Je=be(We),Ye=ye(Ge),Xe=G(Ke),Ze=xe(qe),Qe=(0,L.jsx)(Ne,{ch:n,form:s,baseline:c,showFeishuSecret:F,setShowFeishuSecret:ee,showFeishuWebhookSecrets:ne,setShowFeishuWebhookSecrets:re,feishuCopied:I,feishuWebhookCopied:le,copyFeishuSecret:De,copyFeishuWebhookConfig:ke,updateFeishu:J,updateChannelAgentRoute:ve,feishuAccountsDraft:he,setFeishuAccountsDraft:V,feishuAccountsError:ge,onFeishuAccountsBlur:$,dmOpts:Be,groupOpts:Ve,chatAgents:W,saving:u,dirty:l,save:Y,discard:X}),$e=(0,L.jsx)(Ce,{ch:n,form:s,baseline:c,showDingtalkSecret:ae,setShowDingtalkSecret:oe,dingtalkCopied:ce,copyDingtalkSecret:Oe,updateDingtalk:Se,updateChannelAgentRoute:ve,dingtalkAccountsDraft:H,setDingtalkAccountsDraft:_e,dingtalkAccountsError:U,onDingtalkAccountsBlur:Re,dmOpts:Be,groupOpts:Ve,chatAgents:W,saving:u,dirty:l,save:Y,discard:X}),et=(0,L.jsx)(ze,{ch:n,wx:We,updateWeixin:q,dmOpts:Be,streamOpts:Ue,wxAccountsDraft:pe,setWxAccountsDraft:me,wxAccountsError:B,onWxAccountsBlur:je,form:s,chatAgents:W,onAgentRouteChange:(e,t)=>ve(`weixin`,e,t),saving:u,dirty:l,save:Y,discard:X});return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-6 px-4 py-6`,children:[(0,L.jsxs)(`header`,{className:`flex flex-col gap-1`,children:[(0,L.jsx)(`h1`,{className:`text-lg font-semibold tracking-tight text-fg`,children:t.settingsSections.channels}),(0,L.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:n.subtitle}),(0,L.jsxs)(`a`,{href:ie(e,`channels`),target:`_blank`,rel:`noreferrer`,className:`mt-1 inline-flex items-center gap-1 text-sm text-accent hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`,children:[n.docsLink,(0,L.jsx)(y,{className:`size-3.5`})]})]}),l?(0,L.jsx)(`p`,{className:`text-xs text-amber-800 dark:text-amber-200`,children:n.unsavedHint}):null,p?(0,L.jsx)(`p`,{className:`text-xs text-fg-muted`,children:n.saved}):null,d?(0,L.jsx)(`p`,{className:`text-sm text-red-600 dark:text-red-400`,children:d}):null,T?(0,L.jsx)(`p`,{className:`text-xs text-accent`,children:T}):null,k?(0,L.jsx)(`div`,{className:`rounded-xl border border-success/30 bg-success-soft px-4 py-3 text-sm text-success`,children:k}):null,A?(0,L.jsx)(`div`,{className:`rounded-xl border border-success/30 bg-success-soft px-4 py-3 text-sm text-success`,children:A}):null,(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,L.jsx)(de,{icon:(0,L.jsx)(_,{className:`size-6 text-accent`,strokeWidth:1.75}),title:n.weixinTitle,subtitle:n.weixinSubtitle,configured:Je,enabled:We.enabled,toggleDisabled:u,onToggle:e=>void Z(`weixin`,e),onConfigure:()=>h(!0),onEdit:()=>h(!0),onRemove:()=>w(`weixin`),ch:n}),(0,L.jsx)(de,{icon:(0,L.jsx)(C,{className:`size-6 text-accent`,strokeWidth:1.75}),title:n.telegramTitle,subtitle:n.telegramSubtitle,configured:Ye,enabled:Ge.enabled,toggleDisabled:u,onToggle:e=>void Z(`telegram`,e),onConfigure:()=>v(!0),onEdit:()=>v(!0),onRemove:()=>w(`telegram`),ch:n}),(0,L.jsx)(de,{icon:(0,L.jsx)(_,{className:`size-6 text-accent`,strokeWidth:1.75}),title:n.feishuTitle,subtitle:n.feishuSubtitle,configured:Xe,enabled:Ke.enabled,toggleDisabled:u,onToggle:e=>void Z(`feishu`,e),onConfigure:()=>x(!0),onEdit:()=>x(!0),onRemove:()=>w(`feishu`),ch:n}),(0,L.jsx)(de,{icon:(0,L.jsx)(te,{className:`size-6 text-accent`,strokeWidth:1.75}),title:n.dingtalkTitle,subtitle:n.dingtalkSubtitle,configured:Ze,enabled:qe.enabled,toggleDisabled:u,onToggle:e=>void Z(`dingtalk`,e),onConfigure:()=>O(!0),onEdit:()=>O(!0),onRemove:()=>w(`dingtalk`),ch:n})]}),(0,L.jsx)(Le,{open:m,onOpenChange:h,ch:n,onLoginSuccess:async()=>{await o(),E(n.weixinQrLoginSuccess),window.setTimeout(()=>E(null),4e3)},moreSettings:et}),(0,L.jsx)(Fe,{open:g,onOpenChange:v,ch:n,form:s,baseline:c,tgAdvanced:j,setTgAdvanced:M,showToken:N,setShowToken:P,copied:se,copyToken:Te,updateTelegram:K,updateChannelAgentRoute:ve,tgAccountsDraft:ue,setTgAccountsDraft:R,tgAccountsError:z,onTgAccountsBlur:Ae,dmOpts:Be,groupOpts:Ve,replyOpts:He,streamOpts:Ue,chatAgents:W,saving:u,dirty:l,save:Y,discard:X}),(0,L.jsx)(Pe,{open:b,onOpenChange:x,ch:n,onSetupSuccess:Ee,moreSettings:Qe}),(0,L.jsx)(Me,{open:D,onOpenChange:O,ch:n,onSetupSuccess:Q,moreSettings:$e}),(0,L.jsx)(fe,{open:S!==null,onOpenChange:e=>{e||w(null)},ch:n,removeTarget:S,onCancel:()=>w(null),saving:u,onConfirmRemove:()=>void we()})]})}export{Be as ChannelsSettingsPanel};
2
- //# sourceMappingURL=channels-settings-CjUmKQrC.js.map