@xopcai/xopc 0.0.20 → 0.0.22

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 (197) hide show
  1. package/dist/extensions/feishu/src/adapters/cli-login.d.ts +8 -0
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +225 -0
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -0
  4. package/dist/extensions/feishu/src/adapters/onboard-cli.js +1 -105
  5. package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -1
  6. package/dist/extensions/feishu/src/auth/app-registration.d.ts +47 -0
  7. package/dist/extensions/feishu/src/auth/app-registration.js +122 -0
  8. package/dist/extensions/feishu/src/auth/app-registration.js.map +1 -0
  9. package/dist/extensions/feishu/src/plugin.d.ts +2 -0
  10. package/dist/extensions/feishu/src/plugin.js +2 -0
  11. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  12. package/dist/extensions/telegram/src/inbound-processor.js +1 -1
  13. package/dist/extensions/telegram/src/plugin.d.ts +1 -1
  14. package/dist/extensions/telegram/src/plugin.js +1 -1
  15. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  16. package/dist/extensions/telegram/xopc.extension.json +1 -1
  17. package/dist/extensions/weixin/src/plugin.js +1 -1
  18. package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js → agents-BcLv59-r.js} +2 -2
  19. package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js.map → agents-BcLv59-r.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js → apps-page-Bl-yxbQo.js} +2 -2
  21. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js.map → apps-page-Bl-yxbQo.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js +9 -0
  23. package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js.map +1 -0
  24. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js → cron-page-DsVZzPqv.js} +2 -2
  25. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js.map → cron-page-DsVZzPqv.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js → cron-utils-zbRs2yND.js} +2 -2
  27. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js.map → cron-utils-zbRs2yND.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js → dist-CDA7gR_M.js} +2 -2
  29. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js.map → dist-CDA7gR_M.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js → extension-debug-page-CDLp4DAs.js} +2 -2
  31. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js.map → extension-debug-page-CDLp4DAs.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js → extension-page-DwSCjzHO.js} +2 -2
  33. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js.map → extension-page-DwSCjzHO.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js → extension-settings-page-Rdmxe24_.js} +2 -2
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js.map → extension-settings-page-Rdmxe24_.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/index-D9Wmfh2f.css +1 -0
  37. package/dist/gateway/static/root/assets/index-DG8WvMbu.js +150 -0
  38. package/dist/gateway/static/root/assets/index-DG8WvMbu.js.map +1 -0
  39. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js → logs-page-ChJ0nsPh.js} +2 -2
  40. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js.map → logs-page-ChJ0nsPh.js.map} +1 -1
  41. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js → sessions-page-Cle4fPla.js} +2 -2
  42. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js.map → sessions-page-Cle4fPla.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js +2 -0
  44. package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js.map +1 -0
  45. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js → skills-page-B-smhcB2.js} +2 -2
  46. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js.map → skills-page-B-smhcB2.js.map} +1 -1
  47. package/dist/gateway/static/root/index.html +2 -2
  48. package/dist/package.js +1 -1
  49. package/dist/src/agent/agent-manager.js +6 -6
  50. package/dist/src/agent/context/workspace-seed.js +1 -1
  51. package/dist/src/agent/ipc/bus.js +1 -1
  52. package/dist/src/agent/ipc/inbox.js +1 -1
  53. package/dist/src/agent/ipc/socket.js +1 -1
  54. package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -1
  55. package/dist/src/agent/memory/builtin-memory-store.js +7 -6
  56. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  57. package/dist/src/agent/models/manager.js +1 -1
  58. package/dist/src/agent/prompt/memory/index.d.ts +4 -2
  59. package/dist/src/agent/prompt/memory/index.js +22 -10
  60. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  61. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  62. package/dist/src/agent/service.js +5 -5
  63. package/dist/src/agent/skills/index.js +1 -1
  64. package/dist/src/agent/skills/scanner.js +1 -1
  65. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  66. package/dist/src/agent/skills/skill-manager.js +1 -1
  67. package/dist/src/agent/tools/factory.js +10 -3
  68. package/dist/src/agent/tools/factory.js.map +1 -1
  69. package/dist/src/agent/tools/index.d.ts +1 -1
  70. package/dist/src/agent/tools/memory-tool.d.ts +7 -2
  71. package/dist/src/agent/tools/memory-tool.js +11 -5
  72. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  73. package/dist/src/agent/tools/send-media.js +1 -1
  74. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  75. package/dist/src/agent/tools/write.js +1 -1
  76. package/dist/src/auth/credentials.js +2 -2
  77. package/dist/src/auth/sync-provider-auth.js +1 -1
  78. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  79. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  80. package/dist/src/channels/registry.d.ts +1 -1
  81. package/dist/src/channels/registry.js +25 -1
  82. package/dist/src/channels/registry.js.map +1 -1
  83. package/dist/src/chat-commands/builtins/config.js +3 -3
  84. package/dist/src/chat-commands/builtins/session.js +1 -1
  85. package/dist/src/chat-commands/context.js +1 -1
  86. package/dist/src/chat-commands/index.js +1 -1
  87. package/dist/src/chat-commands/processor.js +1 -1
  88. package/dist/src/cli/commands/agent.js +1 -1
  89. package/dist/src/cli/commands/channels.js +20 -2
  90. package/dist/src/cli/commands/channels.js.map +1 -1
  91. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  92. package/dist/src/cli/commands/gateway/call.d.ts +2 -0
  93. package/dist/src/cli/commands/gateway/call.js +90 -0
  94. package/dist/src/cli/commands/gateway/call.js.map +1 -0
  95. package/dist/src/cli/commands/gateway/health.d.ts +2 -0
  96. package/dist/src/cli/commands/gateway/health.js +77 -0
  97. package/dist/src/cli/commands/gateway/health.js.map +1 -0
  98. package/dist/src/cli/commands/gateway/index.d.ts +3 -0
  99. package/dist/src/cli/commands/gateway/index.js +4 -1
  100. package/dist/src/cli/commands/gateway/probe.d.ts +2 -0
  101. package/dist/src/cli/commands/gateway/probe.js +102 -0
  102. package/dist/src/cli/commands/gateway/probe.js.map +1 -0
  103. package/dist/src/cli/commands/gateway/status.d.ts +0 -3
  104. package/dist/src/cli/commands/gateway/status.js +107 -24
  105. package/dist/src/cli/commands/gateway/status.js.map +1 -1
  106. package/dist/src/cli/commands/gateway.js +7 -1
  107. package/dist/src/cli/commands/gateway.js.map +1 -1
  108. package/dist/src/cli/commands/init.js +3 -3
  109. package/dist/src/cli/commands/update.js +19 -1
  110. package/dist/src/cli/commands/update.js.map +1 -1
  111. package/dist/src/cli/utils/gateway-client.d.ts +28 -0
  112. package/dist/src/cli/utils/gateway-client.js +115 -0
  113. package/dist/src/cli/utils/gateway-client.js.map +1 -0
  114. package/dist/src/config/index.js +2 -2
  115. package/dist/src/config/loader.js +1 -1
  116. package/dist/src/config/models-json.js +1 -1
  117. package/dist/src/config/paths-state.d.ts +4 -0
  118. package/dist/src/config/paths-state.js +9 -1
  119. package/dist/src/config/paths-state.js.map +1 -1
  120. package/dist/src/config/profile.js +2 -2
  121. package/dist/src/config/reload.d.ts +2 -0
  122. package/dist/src/config/reload.js +9 -1
  123. package/dist/src/config/reload.js.map +1 -1
  124. package/dist/src/config/rules.js +12 -2
  125. package/dist/src/config/rules.js.map +1 -1
  126. package/dist/src/cron/executor.js +2 -2
  127. package/dist/src/cron/persistence.js +1 -1
  128. package/dist/src/cron/run-log-store.js +1 -1
  129. package/dist/src/extensions/api.d.ts +6 -1
  130. package/dist/src/extensions/api.js +52 -1
  131. package/dist/src/extensions/api.js.map +1 -1
  132. package/dist/src/extensions/health.js +1 -1
  133. package/dist/src/extensions/loader.d.ts +6 -1
  134. package/dist/src/extensions/loader.js +21 -2
  135. package/dist/src/extensions/loader.js.map +1 -1
  136. package/dist/src/extensions/lockfile.js +1 -1
  137. package/dist/src/extensions/normalize-manifest.js +33 -0
  138. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  139. package/dist/src/extensions/sdk/index.d.ts +1 -1
  140. package/dist/src/extensions/sdk/index.js.map +1 -1
  141. package/dist/src/extensions/types/core.d.ts +35 -1
  142. package/dist/src/extensions/types/manifest.d.ts +14 -0
  143. package/dist/src/gateway/agents-admin.js +1 -1
  144. package/dist/src/gateway/hono/lib/config-payload.d.ts +3 -0
  145. package/dist/src/gateway/hono/lib/config-payload.js +1 -0
  146. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  147. package/dist/src/gateway/hono/oauth.js +1 -1
  148. package/dist/src/gateway/hono/routes/channels.js +111 -0
  149. package/dist/src/gateway/hono/routes/channels.js.map +1 -1
  150. package/dist/src/gateway/hono/routes/commands-skills.js +13 -2
  151. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  152. package/dist/src/gateway/hono/routes/config.js +82 -1
  153. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  154. package/dist/src/gateway/hono/routes/public-gateway.js +17 -0
  155. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  156. package/dist/src/gateway/hono/routes/sessions.js +16 -0
  157. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  158. package/dist/src/gateway/hono/routes/status.js +31 -7
  159. package/dist/src/gateway/hono/routes/status.js.map +1 -1
  160. package/dist/src/gateway/hono/routes/update.js +118 -15
  161. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  162. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  163. package/dist/src/gateway/hono/sse.js +2 -2
  164. package/dist/src/gateway/index.js +1 -1
  165. package/dist/src/gateway/server.js +3 -0
  166. package/dist/src/gateway/server.js.map +1 -1
  167. package/dist/src/gateway/service.d.ts +23 -0
  168. package/dist/src/gateway/service.js +111 -4
  169. package/dist/src/gateway/service.js.map +1 -1
  170. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  171. package/dist/src/infra/update-check.js +54 -21
  172. package/dist/src/infra/update-check.js.map +1 -1
  173. package/dist/src/infra/update-lock.d.ts +13 -0
  174. package/dist/src/infra/update-lock.js +67 -0
  175. package/dist/src/infra/update-lock.js.map +1 -0
  176. package/dist/src/infra/update-runner.d.ts +6 -5
  177. package/dist/src/infra/update-runner.js +93 -13
  178. package/dist/src/infra/update-runner.js.map +1 -1
  179. package/dist/src/infra/update-startup.js +37 -11
  180. package/dist/src/infra/update-startup.js.map +1 -1
  181. package/dist/src/providers/index.js +2 -2
  182. package/dist/src/providers/model-registry.js +1 -1
  183. package/dist/src/session/config-store.js +1 -1
  184. package/dist/src/session/session-title.js +1 -1
  185. package/dist/src/session/store.js +3 -3
  186. package/dist/src/utils/logger/audit.js +1 -1
  187. package/dist/src/utils/logger/log-store.js +1 -1
  188. package/dist/src/utils/logger/rotation.js +1 -1
  189. package/dist/src/voice/tts/audio.js +1 -1
  190. package/package.json +1 -1
  191. package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js +0 -9
  192. package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js.map +0 -1
  193. package/dist/gateway/static/root/assets/index-BQNdJlkw.css +0 -1
  194. package/dist/gateway/static/root/assets/index-fGYWcYhm.js +0 -144
  195. package/dist/gateway/static/root/assets/index-fGYWcYhm.js.map +0 -1
  196. package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js +0 -2
  197. package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { Command } from 'commander';
7
7
  import type { Config } from '../../config/config-surface.js';
8
+ import type { Config as FullConfig } from '../../config/schema.js';
8
9
  import type { MessageBus } from '../../infra/bus/index.js';
9
10
  import type { TypedEventBus } from './events.js';
10
11
  import type { AgentTool } from '@mariozechner/pi-agent-core';
@@ -78,6 +79,10 @@ export interface ExtensionApi {
78
79
  }): void;
79
80
  registerHttpRoute(path: string, handler: HttpRequestHandler): void;
80
81
  registerCommand(command: ExtensionCommand): void;
82
+ /**
83
+ * Register a reload handler when config paths matching this extension change during hot reload.
84
+ */
85
+ registerReload(handler: ExtensionReloadHandler): void;
81
86
  registerService(service: ExtensionService): void;
82
87
  registerGatewayMethod(method: string, handler: GatewayMethodHandler): void;
83
88
  /**
@@ -121,11 +126,40 @@ export interface HttpResponse {
121
126
  }
122
127
  export type HttpRequestHandler = (req: HttpRequest) => HttpResponse | Promise<HttpResponse>;
123
128
  export type GatewayMethodHandler = (params: unknown) => unknown | Promise<unknown>;
129
+ export interface ExtensionReloadResult {
130
+ success: boolean;
131
+ error?: string;
132
+ }
133
+ export type ExtensionReloadHandler = (newConfig: FullConfig, changedPaths: string[]) => ExtensionReloadResult | Promise<ExtensionReloadResult>;
134
+ export interface ExtensionReloadRegistration {
135
+ extensionId: string;
136
+ handler: ExtensionReloadHandler;
137
+ /** Config path prefixes this handler cares about. Empty = any extension-related change passed to matcher. */
138
+ configPrefixes: string[];
139
+ }
140
+ /**
141
+ * Chat command registered by an extension (bridged into CommandRegistry with category `extension`).
142
+ */
124
143
  export interface ExtensionCommand {
125
144
  name: string;
126
145
  description: string;
127
- handler: (args: string[]) => void | Promise<void>;
128
146
  aliases?: string[];
147
+ scope?: Array<'global' | 'private' | 'group'>;
148
+ acceptsArgs?: boolean;
149
+ examples?: string[];
150
+ handler: ExtensionCommandHandler;
151
+ }
152
+ export type ExtensionCommandHandler = (args: string, context: ExtensionCommandContext) => Promise<ExtensionCommandResult | void> | ExtensionCommandResult | void;
153
+ export interface ExtensionCommandContext {
154
+ sessionKey: string;
155
+ source: string;
156
+ isGroup: boolean;
157
+ config: Config;
158
+ reply(text: string): Promise<void>;
159
+ }
160
+ export interface ExtensionCommandResult {
161
+ content: string;
162
+ success?: boolean;
129
163
  }
130
164
  export interface ExtensionService {
131
165
  id: string;
@@ -24,9 +24,23 @@ export interface ExtensionManifest {
24
24
  activation?: ActivationDeclaration;
25
25
  contracts?: ContractDeclaration;
26
26
  setup?: SetupDeclaration;
27
+ /** Hot-reload: config path prefixes and capability flags (optional; handlers can register at runtime). */
28
+ reload?: {
29
+ configPrefixes?: string[];
30
+ supportsHotReload?: boolean;
31
+ };
32
+ /** Chat commands metadata (runtime registration still required in `register()`). */
33
+ commands?: ExtensionManifestCommand[];
27
34
  /** Frontend UI declaration — enables extensions to render custom UI in the Gateway Console. */
28
35
  ui?: ExtensionUiManifest;
29
36
  }
37
+ export interface ExtensionManifestCommand {
38
+ name: string;
39
+ description: string;
40
+ aliases?: string[];
41
+ scope?: Array<'global' | 'private' | 'group'>;
42
+ examples?: string[];
43
+ }
30
44
  /** Top-level UI declaration within an extension manifest. */
31
45
  export interface ExtensionUiManifest {
32
46
  /** Frontend entry HTML (relative to extension root). */
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_AGENT_ID, init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentBootstrapDir, resolveAgentDir, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveUserPath, validateAgentIdForNewAgent } from "../agent/agent-scope.js";
2
- import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
3
2
  import { resolveEffectiveAgentProfile } from "../config/agent-profile.js";
3
+ import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
4
4
  import { BOOTSTRAP_FILES } from "../agent/context/workspace.js";
5
5
  import { isPathUnderWorkspace, resolveWorkspaceSafePath } from "./workspace-editor-path.js";
6
6
  import { seedWorkspaceBootstrapFiles } from "../agent/context/workspace-seed.js";
@@ -114,6 +114,9 @@ export declare function buildSafeWebConfigPayload(service: GatewayService): Prom
114
114
  cron: {
115
115
  enabled: boolean;
116
116
  };
117
+ update: {
118
+ channel: "stable" | "beta" | "dev";
119
+ };
117
120
  stt: {
118
121
  enabled: boolean;
119
122
  provider: "openai" | "alibaba";
@@ -83,6 +83,7 @@ async function buildSafeWebConfigPayload(service) {
83
83
  }
84
84
  },
85
85
  cron: { enabled: config.cron?.enabled },
86
+ update: { channel: config.update?.channel ?? "stable" },
86
87
  stt: config.stt,
87
88
  tts: config.tts,
88
89
  tools: safeToolsWebForGet(config),
@@ -1 +1 @@
1
- {"version":3,"file":"config-payload.js","names":[],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../../generated/bundled-channel-plugins.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport { agentModelFallbacksToArray, agentModelRefToString } from './agent-model.js';\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService) {\n const config = service.currentConfig;\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n const channelsPayload = Object.fromEntries(\n listChannelPlugins().map((plugin) => {\n if (plugin.configSurface) {\n return [plugin.id, plugin.configSurface.buildConfigSurface(config)];\n }\n const channelCfg = config.channels?.[plugin.id] as Record<string, unknown> | undefined;\n return [\n plugin.id,\n {\n enabled: channelCfg?.enabled ?? false,\n configured: plugin.config.listAccountIds(config).length > 0,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n ...(typeof e.name === 'string' && e.name.trim() ? { name: e.name.trim() } : {}),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.model) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.model),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: {\n enabled: config.agents?.defaults?.browser?.enabled === true,\n headless: config.agents?.defaults?.browser?.headless !== false,\n },\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n (await isProviderConfigured(provider)) ? '***' : '',\n ]),\n ),\n ),\n gateway: {\n host: config.gateway?.host,\n port: config.gateway?.port,\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token || '',\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n },\n cron: { enabled: config.cron?.enabled },\n stt: config.stt,\n tts: config.tts,\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-payload.js","names":[],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../../generated/bundled-channel-plugins.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport { agentModelFallbacksToArray, agentModelRefToString } from './agent-model.js';\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService) {\n const config = service.currentConfig;\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n const channelsPayload = Object.fromEntries(\n listChannelPlugins().map((plugin) => {\n if (plugin.configSurface) {\n return [plugin.id, plugin.configSurface.buildConfigSurface(config)];\n }\n const channelCfg = config.channels?.[plugin.id] as Record<string, unknown> | undefined;\n return [\n plugin.id,\n {\n enabled: channelCfg?.enabled ?? false,\n configured: plugin.config.listAccountIds(config).length > 0,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n ...(typeof e.name === 'string' && e.name.trim() ? { name: e.name.trim() } : {}),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.model) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.model),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: {\n enabled: config.agents?.defaults?.browser?.enabled === true,\n headless: config.agents?.defaults?.browser?.headless !== false,\n },\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n (await isProviderConfigured(provider)) ? '***' : '',\n ]),\n ),\n ),\n gateway: {\n host: config.gateway?.host,\n port: config.gateway?.port,\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token || '',\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n },\n cron: { enabled: config.cron?.enabled },\n update: {\n channel: config.update?.channel ?? 'stable',\n },\n stt: config.stt,\n tts: config.tts,\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { CredentialResolver, init_credentials } from "../../auth/credentials.js";
2
- import { anthropicOAuthProvider } from "../../auth/oauth/anthropic.js";
3
2
  import { init_providers, isProviderConfigured } from "../../providers/index.js";
3
+ import { anthropicOAuthProvider } from "../../auth/oauth/anthropic.js";
4
4
  import { minimaxOAuthProvider } from "../../auth/oauth/minimax.js";
5
5
  import { minimaxCnOAuthProvider } from "../../auth/oauth/minimax-cn.js";
6
6
  import { kimiCodingOAuthProvider } from "../../auth/oauth/kimi-coding.js";
@@ -1,4 +1,45 @@
1
1
  //#region src/gateway/hono/routes/channels.ts
2
+ const feishuSetupSessions = /* @__PURE__ */ new Map();
3
+ async function startFeishuSetupPolling(sessionKey, service) {
4
+ const session = feishuSetupSessions.get(sessionKey);
5
+ if (!session) return;
6
+ const { pollAppRegistration } = await import("../../../../extensions/feishu/src/auth/app-registration.js");
7
+ session.phase = "polling";
8
+ const outcome = await pollAppRegistration({
9
+ deviceCode: session.deviceCode,
10
+ intervalSec: session.intervalSec,
11
+ expireInSec: session.expireInSec,
12
+ initialDomain: session.domain
13
+ });
14
+ if (outcome.status === "success") {
15
+ session.phase = "done";
16
+ session.result = outcome.result;
17
+ try {
18
+ const fs = await import("node:fs");
19
+ const configPath = service.getHealth().configPath;
20
+ const raw = fs.readFileSync(configPath, "utf8");
21
+ const config = JSON.parse(raw);
22
+ const existingFeishu = config.channels?.feishu ?? {};
23
+ config.channels = {
24
+ ...config.channels,
25
+ feishu: {
26
+ ...existingFeishu,
27
+ enabled: true,
28
+ appId: outcome.result.appId,
29
+ appSecret: outcome.result.appSecret,
30
+ domain: outcome.result.domain,
31
+ connectionMode: existingFeishu.connectionMode || "websocket"
32
+ }
33
+ };
34
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
35
+ await service.afterFeishuCredentialsPersisted();
36
+ } catch {}
37
+ } else {
38
+ session.phase = "error";
39
+ session.error = outcome.status === "access_denied" ? "User denied authorization." : outcome.status === "expired" ? "Session expired." : outcome.status === "timeout" ? "Scan timed out." : "message" in outcome ? outcome.message : "Unknown error.";
40
+ }
41
+ setTimeout(() => feishuSetupSessions.delete(sessionKey), 3e4);
42
+ }
2
43
  function registerChannelRoutes(authenticated, deps) {
3
44
  const { service, strictRateLimitMiddleware } = deps;
4
45
  authenticated.get("/api/channels/status", (c) => {
@@ -53,6 +94,76 @@ function registerChannelRoutes(authenticated, deps) {
53
94
  payload: { status }
54
95
  });
55
96
  });
97
+ authenticated.post("/api/channels/feishu/setup/start", strictRateLimitMiddleware, async (c) => {
98
+ const body = await c.req.json().catch(() => ({}));
99
+ const domain = (body && typeof body === "object" && typeof body.domain === "string" ? body.domain.trim().toLowerCase() : "") === "lark" ? "lark" : "feishu";
100
+ const { initAppRegistration, beginAppRegistration } = await import("../../../../extensions/feishu/src/auth/app-registration.js");
101
+ if (!await initAppRegistration(domain)) return c.json({
102
+ ok: false,
103
+ error: {
104
+ code: "FEISHU_SCAN_NOT_SUPPORTED",
105
+ message: "Scan-to-create is not available."
106
+ }
107
+ }, 400);
108
+ const begin = await beginAppRegistration(domain);
109
+ const sessionKey = `feishu-setup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
110
+ feishuSetupSessions.set(sessionKey, {
111
+ deviceCode: begin.deviceCode,
112
+ domain,
113
+ intervalSec: begin.intervalSec,
114
+ expireInSec: begin.expireInSec,
115
+ createdAt: Date.now(),
116
+ phase: "idle"
117
+ });
118
+ startFeishuSetupPolling(sessionKey, service);
119
+ return c.json({
120
+ ok: true,
121
+ payload: {
122
+ sessionKey,
123
+ qrUrl: begin.qrUrl
124
+ }
125
+ });
126
+ });
127
+ authenticated.get("/api/channels/feishu/setup/:sessionKey", async (c) => {
128
+ const sessionKey = c.req.param("sessionKey")?.trim() ?? "";
129
+ if (!sessionKey) return c.json({
130
+ ok: false,
131
+ error: {
132
+ code: "BAD_REQUEST",
133
+ message: "Missing sessionKey"
134
+ }
135
+ }, 400);
136
+ const session = feishuSetupSessions.get(sessionKey);
137
+ if (!session) return c.json({
138
+ ok: true,
139
+ payload: { status: {
140
+ phase: "unknown",
141
+ message: "Session not found or expired."
142
+ } }
143
+ });
144
+ if (session.phase === "done" && session.result) return c.json({
145
+ ok: true,
146
+ payload: { status: {
147
+ phase: "done",
148
+ ok: true,
149
+ appId: session.result.appId,
150
+ domain: session.result.domain,
151
+ openId: session.result.openId
152
+ } }
153
+ });
154
+ if (session.phase === "error") return c.json({
155
+ ok: true,
156
+ payload: { status: {
157
+ phase: "done",
158
+ ok: false,
159
+ message: session.error ?? "Setup failed."
160
+ } }
161
+ });
162
+ return c.json({
163
+ ok: true,
164
+ payload: { status: { phase: "polling" } }
165
+ });
166
+ });
56
167
  }
57
168
  //#endregion
58
169
  export { registerChannelRoutes };
@@ -1 +1 @@
1
- {"version":3,"file":"channels.js","names":[],"sources":["../../../../../src/gateway/hono/routes/channels.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerChannelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n authenticated.get('/api/channels/status', (c) => {\n const channels = service.getChannelsStatus();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.post('/api/channels/weixin/login/start', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const account =\n body && typeof body === 'object' && typeof (body as { account?: unknown }).account === 'string'\n ? (body as { account: string }).account.trim() || undefined\n : undefined;\n const rawTimeout =\n body && typeof body === 'object' ? (body as { timeoutMs?: unknown }).timeoutMs : undefined;\n const timeoutMs =\n typeof rawTimeout === 'number' && Number.isFinite(rawTimeout) ? Math.max(60_000, rawTimeout) : undefined;\n\n const { startWeixinGatewayQrLogin } = await import('../../../channels/weixin/index.js');\n const result = await startWeixinGatewayQrLogin({\n configPath: service.getHealth().configPath,\n account,\n timeoutMs,\n onPersisted: async (r) => {\n if (r.ok) {\n await service.afterWeixinCredentialsPersisted();\n }\n },\n });\n\n if (result.ok === false) {\n return c.json(\n { ok: false, error: { code: 'WEIXIN_LOGIN_FAILED', message: result.message } },\n 400,\n );\n }\n return c.json({\n ok: true,\n payload: { sessionKey: result.sessionKey, qrcodeUrl: result.qrcodeUrl },\n });\n });\n\n authenticated.get('/api/channels/weixin/login/:sessionKey', async (c) => {\n const sessionKey = c.req.param('sessionKey')?.trim() ?? '';\n if (!sessionKey) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing sessionKey' } }, 400);\n }\n const { getWeixinGatewayQrLoginStatus } = await import('../../../channels/weixin/index.js');\n const status = getWeixinGatewayQrLoginStatus(sessionKey);\n return c.json({ ok: true, payload: { status } });\n });\n}\n"],"mappings":";AAIA,SAAgB,sBAAsB,eAAqB,MAAoC;CAC7F,MAAM,EAAE,SAAS,8BAA8B;AAE/C,eAAc,IAAI,yBAAyB,MAAM;EAC/C,MAAM,WAAW,QAAQ,mBAAmB;AAC5C,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,UAAU;GAAE,CAAC;GAClD;AAEF,eAAc,KAAK,oCAAoC,2BAA2B,OAAO,MAAM;EAC7F,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;EACjD,MAAM,UACJ,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAA+B,YAAY,WAClF,KAA6B,QAAQ,MAAM,IAAI,KAAA,IAChD,KAAA;EACN,MAAM,aACJ,QAAQ,OAAO,SAAS,WAAY,KAAiC,YAAY,KAAA;EACnF,MAAM,YACJ,OAAO,eAAe,YAAY,OAAO,SAAS,WAAW,GAAG,KAAK,IAAI,KAAQ,WAAW,GAAG,KAAA;EAEjG,MAAM,EAAE,8BAA8B,MAAM,OAAO;EACnD,MAAM,SAAS,MAAM,0BAA0B;GAC7C,YAAY,QAAQ,WAAW,CAAC;GAChC;GACA;GACA,aAAa,OAAO,MAAM;AACxB,QAAI,EAAE,GACJ,OAAM,QAAQ,iCAAiC;;GAGpD,CAAC;AAEF,MAAI,OAAO,OAAO,MAChB,QAAO,EAAE,KACP;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAuB,SAAS,OAAO;IAAS;GAAE,EAC9E,IACD;AAEH,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE,YAAY,OAAO;IAAY,WAAW,OAAO;IAAW;GACxE,CAAC;GACF;AAEF,eAAc,IAAI,0CAA0C,OAAO,MAAM;EACvE,MAAM,aAAa,EAAE,IAAI,MAAM,aAAa,EAAE,MAAM,IAAI;AACxD,MAAI,CAAC,WACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAe,SAAS;IAAsB;GAAE,EAAE,IAAI;EAElG,MAAM,EAAE,kCAAkC,MAAM,OAAO;EACvD,MAAM,SAAS,8BAA8B,WAAW;AACxD,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,QAAQ;GAAE,CAAC;GAChD"}
1
+ {"version":3,"file":"channels.js","names":[],"sources":["../../../../../src/gateway/hono/routes/channels.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\ntype FeishuSetupDomain = 'feishu' | 'lark';\n\ninterface FeishuSetupSession {\n deviceCode: string;\n domain: FeishuSetupDomain;\n intervalSec: number;\n expireInSec: number;\n createdAt: number;\n phase: 'idle' | 'polling' | 'scanned' | 'done' | 'error';\n result?: {\n appId: string;\n appSecret: string;\n domain: FeishuSetupDomain;\n openId?: string;\n };\n error?: string;\n}\n\nconst feishuSetupSessions = new Map<string, FeishuSetupSession>();\n\nasync function startFeishuSetupPolling(sessionKey: string, service: GatewayService): Promise<void> {\n const session = feishuSetupSessions.get(sessionKey);\n if (!session) return;\n\n const { pollAppRegistration } = await import(\n '../../../../extensions/feishu/src/auth/app-registration.js'\n );\n\n session.phase = 'polling';\n\n const outcome = await pollAppRegistration({\n deviceCode: session.deviceCode,\n intervalSec: session.intervalSec,\n expireInSec: session.expireInSec,\n initialDomain: session.domain,\n });\n\n if (outcome.status === 'success') {\n session.phase = 'done';\n session.result = outcome.result;\n try {\n const fs = await import('node:fs');\n const configPath = service.getHealth().configPath;\n const raw = fs.readFileSync(configPath, 'utf8');\n const config = JSON.parse(raw) as {\n channels?: Record<string, unknown>;\n };\n const existingFeishu = (config.channels?.feishu ?? {}) as Record<string, unknown>;\n\n config.channels = {\n ...config.channels,\n feishu: {\n ...existingFeishu,\n enabled: true,\n appId: outcome.result.appId,\n appSecret: outcome.result.appSecret,\n domain: outcome.result.domain,\n connectionMode: (existingFeishu.connectionMode as string) || 'websocket',\n },\n };\n\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf8');\n await service.afterFeishuCredentialsPersisted();\n } catch {\n // Config write / reload failure is non-blocking; session still carries credentials for debugging.\n }\n } else {\n session.phase = 'error';\n session.error =\n outcome.status === 'access_denied'\n ? 'User denied authorization.'\n : outcome.status === 'expired'\n ? 'Session expired.'\n : outcome.status === 'timeout'\n ? 'Scan timed out.'\n : 'message' in outcome\n ? outcome.message\n : 'Unknown error.';\n }\n\n setTimeout(() => feishuSetupSessions.delete(sessionKey), 30_000);\n}\n\nexport function registerChannelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n authenticated.get('/api/channels/status', (c) => {\n const channels = service.getChannelsStatus();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.post('/api/channels/weixin/login/start', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const account =\n body && typeof body === 'object' && typeof (body as { account?: unknown }).account === 'string'\n ? (body as { account: string }).account.trim() || undefined\n : undefined;\n const rawTimeout =\n body && typeof body === 'object' ? (body as { timeoutMs?: unknown }).timeoutMs : undefined;\n const timeoutMs =\n typeof rawTimeout === 'number' && Number.isFinite(rawTimeout) ? Math.max(60_000, rawTimeout) : undefined;\n\n const { startWeixinGatewayQrLogin } = await import('../../../channels/weixin/index.js');\n const result = await startWeixinGatewayQrLogin({\n configPath: service.getHealth().configPath,\n account,\n timeoutMs,\n onPersisted: async (r) => {\n if (r.ok) {\n await service.afterWeixinCredentialsPersisted();\n }\n },\n });\n\n if (result.ok === false) {\n return c.json(\n { ok: false, error: { code: 'WEIXIN_LOGIN_FAILED', message: result.message } },\n 400,\n );\n }\n return c.json({\n ok: true,\n payload: { sessionKey: result.sessionKey, qrcodeUrl: result.qrcodeUrl },\n });\n });\n\n authenticated.get('/api/channels/weixin/login/:sessionKey', async (c) => {\n const sessionKey = c.req.param('sessionKey')?.trim() ?? '';\n if (!sessionKey) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing sessionKey' } }, 400);\n }\n const { getWeixinGatewayQrLoginStatus } = await import('../../../channels/weixin/index.js');\n const status = getWeixinGatewayQrLoginStatus(sessionKey);\n return c.json({ ok: true, payload: { status } });\n });\n\n authenticated.post('/api/channels/feishu/setup/start', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const rawDomain =\n body && typeof body === 'object' && typeof (body as { domain?: unknown }).domain === 'string'\n ? (body as { domain: string }).domain.trim().toLowerCase()\n : '';\n const domain: FeishuSetupDomain = rawDomain === 'lark' ? 'lark' : 'feishu';\n\n const { initAppRegistration, beginAppRegistration } = await import(\n '../../../../extensions/feishu/src/auth/app-registration.js'\n );\n\n const supported = await initAppRegistration(domain);\n if (!supported) {\n return c.json(\n {\n ok: false,\n error: { code: 'FEISHU_SCAN_NOT_SUPPORTED', message: 'Scan-to-create is not available.' },\n },\n 400,\n );\n }\n\n const begin = await beginAppRegistration(domain);\n\n const sessionKey = `feishu-setup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n feishuSetupSessions.set(sessionKey, {\n deviceCode: begin.deviceCode,\n domain,\n intervalSec: begin.intervalSec,\n expireInSec: begin.expireInSec,\n createdAt: Date.now(),\n phase: 'idle',\n });\n\n void startFeishuSetupPolling(sessionKey, service);\n\n return c.json({\n ok: true,\n payload: { sessionKey, qrUrl: begin.qrUrl },\n });\n });\n\n authenticated.get('/api/channels/feishu/setup/:sessionKey', async (c) => {\n const sessionKey = c.req.param('sessionKey')?.trim() ?? '';\n if (!sessionKey) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing sessionKey' } }, 400);\n }\n\n const session = feishuSetupSessions.get(sessionKey);\n if (!session) {\n return c.json({\n ok: true,\n payload: {\n status: { phase: 'unknown' as const, message: 'Session not found or expired.' },\n },\n });\n }\n\n if (session.phase === 'done' && session.result) {\n return c.json({\n ok: true,\n payload: {\n status: {\n phase: 'done' as const,\n ok: true,\n appId: session.result.appId,\n domain: session.result.domain,\n openId: session.result.openId,\n },\n },\n });\n }\n\n if (session.phase === 'error') {\n return c.json({\n ok: true,\n payload: {\n status: {\n phase: 'done' as const,\n ok: false,\n message: session.error ?? 'Setup failed.',\n },\n },\n });\n }\n\n return c.json({\n ok: true,\n payload: {\n status: { phase: 'polling' as const },\n },\n });\n });\n}\n"],"mappings":";AAuBA,MAAM,sCAAsB,IAAI,KAAiC;AAEjE,eAAe,wBAAwB,YAAoB,SAAwC;CACjG,MAAM,UAAU,oBAAoB,IAAI,WAAW;AACnD,KAAI,CAAC,QAAS;CAEd,MAAM,EAAE,wBAAwB,MAAM,OACpC;AAGF,SAAQ,QAAQ;CAEhB,MAAM,UAAU,MAAM,oBAAoB;EACxC,YAAY,QAAQ;EACpB,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,eAAe,QAAQ;EACxB,CAAC;AAEF,KAAI,QAAQ,WAAW,WAAW;AAChC,UAAQ,QAAQ;AAChB,UAAQ,SAAS,QAAQ;AACzB,MAAI;GACF,MAAM,KAAK,MAAM,OAAO;GACxB,MAAM,aAAa,QAAQ,WAAW,CAAC;GACvC,MAAM,MAAM,GAAG,aAAa,YAAY,OAAO;GAC/C,MAAM,SAAS,KAAK,MAAM,IAAI;GAG9B,MAAM,iBAAkB,OAAO,UAAU,UAAU,EAAE;AAErD,UAAO,WAAW;IAChB,GAAG,OAAO;IACV,QAAQ;KACN,GAAG;KACH,SAAS;KACT,OAAO,QAAQ,OAAO;KACtB,WAAW,QAAQ,OAAO;KAC1B,QAAQ,QAAQ,OAAO;KACvB,gBAAiB,eAAe,kBAA6B;KAC9D;IACF;AAED,MAAG,cAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAE,GAAG,MAAM,OAAO;AAC5E,SAAM,QAAQ,iCAAiC;UACzC;QAGH;AACL,UAAQ,QAAQ;AAChB,UAAQ,QACN,QAAQ,WAAW,kBACf,+BACA,QAAQ,WAAW,YACjB,qBACA,QAAQ,WAAW,YACjB,oBACA,aAAa,UACX,QAAQ,UACR;;AAGd,kBAAiB,oBAAoB,OAAO,WAAW,EAAE,IAAO;;AAGlE,SAAgB,sBAAsB,eAAqB,MAAoC;CAC7F,MAAM,EAAE,SAAS,8BAA8B;AAE/C,eAAc,IAAI,yBAAyB,MAAM;EAC/C,MAAM,WAAW,QAAQ,mBAAmB;AAC5C,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,UAAU;GAAE,CAAC;GAClD;AAEF,eAAc,KAAK,oCAAoC,2BAA2B,OAAO,MAAM;EAC7F,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;EACjD,MAAM,UACJ,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAA+B,YAAY,WAClF,KAA6B,QAAQ,MAAM,IAAI,KAAA,IAChD,KAAA;EACN,MAAM,aACJ,QAAQ,OAAO,SAAS,WAAY,KAAiC,YAAY,KAAA;EACnF,MAAM,YACJ,OAAO,eAAe,YAAY,OAAO,SAAS,WAAW,GAAG,KAAK,IAAI,KAAQ,WAAW,GAAG,KAAA;EAEjG,MAAM,EAAE,8BAA8B,MAAM,OAAO;EACnD,MAAM,SAAS,MAAM,0BAA0B;GAC7C,YAAY,QAAQ,WAAW,CAAC;GAChC;GACA;GACA,aAAa,OAAO,MAAM;AACxB,QAAI,EAAE,GACJ,OAAM,QAAQ,iCAAiC;;GAGpD,CAAC;AAEF,MAAI,OAAO,OAAO,MAChB,QAAO,EAAE,KACP;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAuB,SAAS,OAAO;IAAS;GAAE,EAC9E,IACD;AAEH,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE,YAAY,OAAO;IAAY,WAAW,OAAO;IAAW;GACxE,CAAC;GACF;AAEF,eAAc,IAAI,0CAA0C,OAAO,MAAM;EACvE,MAAM,aAAa,EAAE,IAAI,MAAM,aAAa,EAAE,MAAM,IAAI;AACxD,MAAI,CAAC,WACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAe,SAAS;IAAsB;GAAE,EAAE,IAAI;EAElG,MAAM,EAAE,kCAAkC,MAAM,OAAO;EACvD,MAAM,SAAS,8BAA8B,WAAW;AACxD,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,QAAQ;GAAE,CAAC;GAChD;AAEF,eAAc,KAAK,oCAAoC,2BAA2B,OAAO,MAAM;EAC7F,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;EAKjD,MAAM,UAHJ,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAA8B,WAAW,WAChF,KAA4B,OAAO,MAAM,CAAC,aAAa,GACxD,QAC0C,SAAS,SAAS;EAElE,MAAM,EAAE,qBAAqB,yBAAyB,MAAM,OAC1D;AAIF,MAAI,CAAC,MADmB,oBAAoB,OAAO,CAEjD,QAAO,EAAE,KACP;GACE,IAAI;GACJ,OAAO;IAAE,MAAM;IAA6B,SAAS;IAAoC;GAC1F,EACD,IACD;EAGH,MAAM,QAAQ,MAAM,qBAAqB,OAAO;EAEhD,MAAM,aAAa,gBAAgB,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;AAEvF,sBAAoB,IAAI,YAAY;GAClC,YAAY,MAAM;GAClB;GACA,aAAa,MAAM;GACnB,aAAa,MAAM;GACnB,WAAW,KAAK,KAAK;GACrB,OAAO;GACR,CAAC;AAEG,0BAAwB,YAAY,QAAQ;AAEjD,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE;IAAY,OAAO,MAAM;IAAO;GAC5C,CAAC;GACF;AAEF,eAAc,IAAI,0CAA0C,OAAO,MAAM;EACvE,MAAM,aAAa,EAAE,IAAI,MAAM,aAAa,EAAE,MAAM,IAAI;AACxD,MAAI,CAAC,WACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAe,SAAS;IAAsB;GAAE,EAAE,IAAI;EAGlG,MAAM,UAAU,oBAAoB,IAAI,WAAW;AACnD,MAAI,CAAC,QACH,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS,EACP,QAAQ;IAAE,OAAO;IAAoB,SAAS;IAAiC,EAChF;GACF,CAAC;AAGJ,MAAI,QAAQ,UAAU,UAAU,QAAQ,OACtC,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS,EACP,QAAQ;IACN,OAAO;IACP,IAAI;IACJ,OAAO,QAAQ,OAAO;IACtB,QAAQ,QAAQ,OAAO;IACvB,QAAQ,QAAQ,OAAO;IACxB,EACF;GACF,CAAC;AAGJ,MAAI,QAAQ,UAAU,QACpB,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS,EACP,QAAQ;IACN,OAAO;IACP,IAAI;IACJ,SAAS,QAAQ,SAAS;IAC3B,EACF;GACF,CAAC;AAGJ,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS,EACP,QAAQ,EAAE,OAAO,WAAoB,EACtC;GACF,CAAC;GACF"}
@@ -4,18 +4,29 @@ import "../../../chat-commands/index.js";
4
4
  function registerCommandsSkillsRoutes(authenticated, deps) {
5
5
  const { service } = deps;
6
6
  authenticated.get("/api/commands", (c) => {
7
- const commands = commandRegistry.list().filter((cmd) => cmd.scope.includes("global") || cmd.scope.includes("private")).map((cmd) => ({
7
+ const all = commandRegistry.list();
8
+ const commands = all.filter((cmd) => cmd.scope.includes("global") || cmd.scope.includes("private")).map((cmd) => ({
8
9
  id: cmd.id,
9
10
  name: cmd.name,
10
11
  aliases: cmd.aliases ?? [],
11
12
  description: cmd.description,
12
13
  category: cmd.category,
14
+ scope: cmd.scope,
13
15
  acceptsArgs: cmd.acceptsArgs ?? false,
14
16
  examples: cmd.examples ?? []
15
17
  }));
18
+ const extensionCommands = all.filter((cmd) => cmd.category === "extension").map((cmd) => ({
19
+ id: cmd.id,
20
+ name: cmd.name,
21
+ description: cmd.description,
22
+ extensionId: cmd.id.startsWith("ext.") ? cmd.id.split(".")[1] : void 0
23
+ }));
16
24
  return c.json({
17
25
  ok: true,
18
- payload: { commands }
26
+ payload: {
27
+ commands,
28
+ extensionCommands
29
+ }
19
30
  });
20
31
  });
21
32
  authenticated.get("/api/skills", (c) => {
@@ -1 +1 @@
1
- {"version":3,"file":"commands-skills.js","names":[],"sources":["../../../../../src/gateway/hono/routes/commands-skills.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { commandRegistry } from '../../../chat-commands/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerCommandsSkillsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Chat slash commands (CommandRegistry) ==========\n\n authenticated.get('/api/commands', (c) => {\n const commands = commandRegistry\n .list()\n .filter((cmd) => cmd.scope.includes('global') || cmd.scope.includes('private'))\n .map((cmd) => ({\n id: cmd.id,\n name: cmd.name,\n aliases: cmd.aliases ?? [],\n description: cmd.description,\n category: cmd.category,\n acceptsArgs: cmd.acceptsArgs ?? false,\n examples: cmd.examples ?? [],\n }));\n return c.json({ ok: true, payload: { commands } });\n });\n\n // ========== Skills (managed global skills under ~/.xopc/skills) ==========\n\n authenticated.get('/api/skills', (c) => {\n const payload = service.getSkillsApi();\n return c.json({ ok: true, payload });\n });\n\n authenticated.get('/api/skills/:skillName/content', (c) => {\n const raw = c.req.param('skillName');\n if (!raw) {\n return c.json({ ok: false, error: 'Missing skill name' }, 400);\n }\n let skillName: string;\n try {\n skillName = decodeURIComponent(raw);\n } catch {\n return c.json({ ok: false, error: 'Invalid skill name' }, 400);\n }\n const data = service.getSkillMarkdownSource(skillName);\n if (!data) {\n return c.json({ ok: false, error: 'Skill not found' }, 404);\n }\n return c.json({ ok: true, payload: data });\n });\n\n authenticated.post('/api/skills/reload', (c) => {\n service.reloadSkillsFromDisk();\n return c.json({ ok: true });\n });\n\n authenticated.patch('/api/skills/enabled', async (c) => {\n let body: { skillName?: unknown; enabled?: unknown };\n try {\n body = (await c.req.json()) as { skillName?: unknown; enabled?: unknown };\n } catch {\n return c.json({ ok: false, error: 'Invalid JSON' }, 400);\n }\n const skillName = typeof body.skillName === 'string' ? body.skillName.trim() : '';\n const enabled = body.enabled;\n if (!skillName || typeof enabled !== 'boolean') {\n return c.json({ ok: false, error: 'Expected { skillName: string, enabled: boolean }' }, 400);\n }\n try {\n service.patchSkillEnabled(skillName, enabled);\n return c.json({ ok: true });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Update failed' },\n 400,\n );\n }\n });\n\n authenticated.get('/api/skills/marketplace', async (c) => {\n const q = c.req.query('q')?.trim() ?? '';\n const pageRaw = c.req.query('page');\n const pageSizeRaw = c.req.query('pageSize');\n const sortRaw = c.req.query('sort');\n const page = pageRaw != null && pageRaw !== '' ? Math.max(1, Number(pageRaw) || 1) : undefined;\n const pageSize =\n pageSizeRaw != null && pageSizeRaw !== ''\n ? Math.min(50, Math.max(1, Number(pageSizeRaw) || 20))\n : undefined;\n const sort =\n sortRaw === 'newest' || sortRaw === 'downloads' ? sortRaw : undefined;\n try {\n const payload = await service.fetchSkillsMarketplaceCatalog({\n q: q || undefined,\n page,\n pageSize,\n sort,\n });\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Marketplace request failed' },\n 502,\n );\n }\n });\n\n authenticated.get('/api/skills/marketplace/packages/:pkgName', async (c) => {\n const raw = c.req.param('pkgName');\n if (!raw) {\n return c.json({ ok: false, error: 'Missing package name' }, 400);\n }\n let pkgName: string;\n try {\n pkgName = decodeURIComponent(raw);\n } catch {\n return c.json({ ok: false, error: 'Invalid package name' }, 400);\n }\n try {\n const payload = await service.fetchSkillsMarketplacePackageDetail(pkgName);\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Marketplace request failed' },\n 502,\n );\n }\n });\n\n authenticated.post('/api/skills/marketplace/install', async (c) => {\n let body: { name?: unknown; version?: unknown; overwrite?: unknown };\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: 'Invalid JSON' }, 400);\n }\n const name = typeof body.name === 'string' ? body.name.trim() : '';\n const version = typeof body.version === 'string' ? body.version.trim() : undefined;\n const overwrite =\n body.overwrite === true ||\n body.overwrite === 'true' ||\n body.overwrite === '1';\n if (!name) {\n return c.json({ ok: false, error: 'Expected { name: string, version?: string, overwrite?: boolean }' }, 400);\n }\n try {\n const payload = await service.installSkillFromMarketplace({ name, version, overwrite });\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Install failed' },\n 400,\n );\n }\n });\n\n authenticated.post('/api/skills/upload', async (c) => {\n let body: Record<string, unknown>;\n try {\n body = await c.req.parseBody({ all: true });\n } catch {\n return c.json({ ok: false, error: 'Invalid multipart body' }, 400);\n }\n const file = body['file'];\n if (!file || typeof file !== 'object') {\n return c.json({ ok: false, error: 'Missing file field' }, 400);\n }\n let buf: Buffer;\n if (file instanceof File) {\n buf = Buffer.from(await file.arrayBuffer());\n } else if (typeof (file as Blob).arrayBuffer === 'function') {\n buf = Buffer.from(await (file as Blob).arrayBuffer());\n } else {\n return c.json({ ok: false, error: 'Invalid file upload' }, 400);\n }\n const skillIdRaw = body['skillId'];\n const overwriteRaw = body['overwrite'];\n const skillId = typeof skillIdRaw === 'string' && skillIdRaw.trim() ? skillIdRaw.trim() : undefined;\n const overwrite =\n overwriteRaw === 'true' ||\n overwriteRaw === true ||\n overwriteRaw === '1';\n\n try {\n const result = service.installManagedSkillZip(buf, { skillId, overwrite });\n return c.json({ ok: true, payload: result });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Install failed' },\n 400,\n );\n }\n });\n\n authenticated.delete('/api/skills/:id', (c) => {\n const id = c.req.param('id');\n if (!id) {\n return c.json({ ok: false, error: 'Missing id' }, 400);\n }\n try {\n service.deleteManagedSkill(id);\n return c.json({ ok: true });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Delete failed' },\n 400,\n );\n }\n });\n}\n"],"mappings":";;;AAKA,SAAgB,6BAA6B,eAAqB,MAAoC;CACpG,MAAM,EAAE,YAAY;AAIpB,eAAc,IAAI,kBAAkB,MAAM;EACxC,MAAM,WAAW,gBACd,MAAM,CACN,QAAQ,QAAQ,IAAI,MAAM,SAAS,SAAS,IAAI,IAAI,MAAM,SAAS,UAAU,CAAC,CAC9E,KAAK,SAAS;GACb,IAAI,IAAI;GACR,MAAM,IAAI;GACV,SAAS,IAAI,WAAW,EAAE;GAC1B,aAAa,IAAI;GACjB,UAAU,IAAI;GACd,aAAa,IAAI,eAAe;GAChC,UAAU,IAAI,YAAY,EAAE;GAC7B,EAAE;AACL,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,UAAU;GAAE,CAAC;GAClD;AAIF,eAAc,IAAI,gBAAgB,MAAM;EACtC,MAAM,UAAU,QAAQ,cAAc;AACtC,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM;GAAS,CAAC;GACpC;AAEF,eAAc,IAAI,mCAAmC,MAAM;EACzD,MAAM,MAAM,EAAE,IAAI,MAAM,YAAY;AACpC,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAsB,EAAE,IAAI;EAEhE,IAAI;AACJ,MAAI;AACF,eAAY,mBAAmB,IAAI;UAC7B;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;IAAsB,EAAE,IAAI;;EAEhE,MAAM,OAAO,QAAQ,uBAAuB,UAAU;AACtD,MAAI,CAAC,KACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAmB,EAAE,IAAI;AAE7D,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS;GAAM,CAAC;GAC1C;AAEF,eAAc,KAAK,uBAAuB,MAAM;AAC9C,UAAQ,sBAAsB;AAC9B,SAAO,EAAE,KAAK,EAAE,IAAI,MAAM,CAAC;GAC3B;AAEF,eAAc,MAAM,uBAAuB,OAAO,MAAM;EACtD,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;IAAgB,EAAE,IAAI;;EAE1D,MAAM,YAAY,OAAO,KAAK,cAAc,WAAW,KAAK,UAAU,MAAM,GAAG;EAC/E,MAAM,UAAU,KAAK;AACrB,MAAI,CAAC,aAAa,OAAO,YAAY,UACnC,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAoD,EAAE,IAAI;AAE9F,MAAI;AACF,WAAQ,kBAAkB,WAAW,QAAQ;AAC7C,UAAO,EAAE,KAAK,EAAE,IAAI,MAAM,CAAC;WACpB,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAAiB,EAC1E,IACD;;GAEH;AAEF,eAAc,IAAI,2BAA2B,OAAO,MAAM;EACxD,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,MAAM,IAAI;EACtC,MAAM,UAAU,EAAE,IAAI,MAAM,OAAO;EACnC,MAAM,cAAc,EAAE,IAAI,MAAM,WAAW;EAC3C,MAAM,UAAU,EAAE,IAAI,MAAM,OAAO;EACnC,MAAM,OAAO,WAAW,QAAQ,YAAY,KAAK,KAAK,IAAI,GAAG,OAAO,QAAQ,IAAI,EAAE,GAAG,KAAA;EACrF,MAAM,WACJ,eAAe,QAAQ,gBAAgB,KACnC,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,OAAO,YAAY,IAAI,GAAG,CAAC,GACpD,KAAA;EACN,MAAM,OACJ,YAAY,YAAY,YAAY,cAAc,UAAU,KAAA;AAC9D,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,8BAA8B;IAC1D,GAAG,KAAK,KAAA;IACR;IACA;IACA;IACD,CAAC;AACF,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM;IAAS,CAAC;WAC7B,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAA8B,EACvF,IACD;;GAEH;AAEF,eAAc,IAAI,6CAA6C,OAAO,MAAM;EAC1E,MAAM,MAAM,EAAE,IAAI,MAAM,UAAU;AAClC,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAwB,EAAE,IAAI;EAElE,IAAI;AACJ,MAAI;AACF,aAAU,mBAAmB,IAAI;UAC3B;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;IAAwB,EAAE,IAAI;;AAElE,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,oCAAoC,QAAQ;AAC1E,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM;IAAS,CAAC;WAC7B,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAA8B,EACvF,IACD;;GAEH;AAEF,eAAc,KAAK,mCAAmC,OAAO,MAAM;EACjE,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;IAAgB,EAAE,IAAI;;EAE1D,MAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,KAAK,MAAM,GAAG;EAChE,MAAM,UAAU,OAAO,KAAK,YAAY,WAAW,KAAK,QAAQ,MAAM,GAAG,KAAA;EACzE,MAAM,YACJ,KAAK,cAAc,QACnB,KAAK,cAAc,UACnB,KAAK,cAAc;AACrB,MAAI,CAAC,KACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAoE,EAAE,IAAI;AAE9G,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,4BAA4B;IAAE;IAAM;IAAS;IAAW,CAAC;AACvF,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM;IAAS,CAAC;WAC7B,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAAkB,EAC3E,IACD;;GAEH;AAEF,eAAc,KAAK,sBAAsB,OAAO,MAAM;EACpD,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,EAAE,IAAI,UAAU,EAAE,KAAK,MAAM,CAAC;UACrC;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;IAA0B,EAAE,IAAI;;EAEpE,MAAM,OAAO,KAAK;AAClB,MAAI,CAAC,QAAQ,OAAO,SAAS,SAC3B,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAsB,EAAE,IAAI;EAEhE,IAAI;AACJ,MAAI,gBAAgB,KAClB,OAAM,OAAO,KAAK,MAAM,KAAK,aAAa,CAAC;WAClC,OAAQ,KAAc,gBAAgB,WAC/C,OAAM,OAAO,KAAK,MAAO,KAAc,aAAa,CAAC;MAErD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAuB,EAAE,IAAI;EAEjE,MAAM,aAAa,KAAK;EACxB,MAAM,eAAe,KAAK;EAC1B,MAAM,UAAU,OAAO,eAAe,YAAY,WAAW,MAAM,GAAG,WAAW,MAAM,GAAG,KAAA;EAC1F,MAAM,YACJ,iBAAiB,UACjB,iBAAiB,QACjB,iBAAiB;AAEnB,MAAI;GACF,MAAM,SAAS,QAAQ,uBAAuB,KAAK;IAAE;IAAS;IAAW,CAAC;AAC1E,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM,SAAS;IAAQ,CAAC;WACrC,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAAkB,EAC3E,IACD;;GAEH;AAEF,eAAc,OAAO,oBAAoB,MAAM;EAC7C,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;AAC5B,MAAI,CAAC,GACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;GAAc,EAAE,IAAI;AAExD,MAAI;AACF,WAAQ,mBAAmB,GAAG;AAC9B,UAAO,EAAE,KAAK,EAAE,IAAI,MAAM,CAAC;WACpB,KAAK;AACZ,UAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU;IAAiB,EAC1E,IACD;;GAEH"}
1
+ {"version":3,"file":"commands-skills.js","names":[],"sources":["../../../../../src/gateway/hono/routes/commands-skills.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { commandRegistry } from '../../../chat-commands/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerCommandsSkillsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Chat slash commands (CommandRegistry) ==========\n\n authenticated.get('/api/commands', (c) => {\n const all = commandRegistry.list();\n const commands = all\n .filter((cmd) => cmd.scope.includes('global') || cmd.scope.includes('private'))\n .map((cmd) => ({\n id: cmd.id,\n name: cmd.name,\n aliases: cmd.aliases ?? [],\n description: cmd.description,\n category: cmd.category,\n scope: cmd.scope,\n acceptsArgs: cmd.acceptsArgs ?? false,\n examples: cmd.examples ?? [],\n }));\n const extensionCommands = all\n .filter((cmd) => cmd.category === 'extension')\n .map((cmd) => ({\n id: cmd.id,\n name: cmd.name,\n description: cmd.description,\n extensionId: cmd.id.startsWith('ext.') ? cmd.id.split('.')[1] : undefined,\n }));\n return c.json({ ok: true, payload: { commands, extensionCommands } });\n });\n\n // ========== Skills (managed global skills under ~/.xopc/skills) ==========\n\n authenticated.get('/api/skills', (c) => {\n const payload = service.getSkillsApi();\n return c.json({ ok: true, payload });\n });\n\n authenticated.get('/api/skills/:skillName/content', (c) => {\n const raw = c.req.param('skillName');\n if (!raw) {\n return c.json({ ok: false, error: 'Missing skill name' }, 400);\n }\n let skillName: string;\n try {\n skillName = decodeURIComponent(raw);\n } catch {\n return c.json({ ok: false, error: 'Invalid skill name' }, 400);\n }\n const data = service.getSkillMarkdownSource(skillName);\n if (!data) {\n return c.json({ ok: false, error: 'Skill not found' }, 404);\n }\n return c.json({ ok: true, payload: data });\n });\n\n authenticated.post('/api/skills/reload', (c) => {\n service.reloadSkillsFromDisk();\n return c.json({ ok: true });\n });\n\n authenticated.patch('/api/skills/enabled', async (c) => {\n let body: { skillName?: unknown; enabled?: unknown };\n try {\n body = (await c.req.json()) as { skillName?: unknown; enabled?: unknown };\n } catch {\n return c.json({ ok: false, error: 'Invalid JSON' }, 400);\n }\n const skillName = typeof body.skillName === 'string' ? body.skillName.trim() : '';\n const enabled = body.enabled;\n if (!skillName || typeof enabled !== 'boolean') {\n return c.json({ ok: false, error: 'Expected { skillName: string, enabled: boolean }' }, 400);\n }\n try {\n service.patchSkillEnabled(skillName, enabled);\n return c.json({ ok: true });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Update failed' },\n 400,\n );\n }\n });\n\n authenticated.get('/api/skills/marketplace', async (c) => {\n const q = c.req.query('q')?.trim() ?? '';\n const pageRaw = c.req.query('page');\n const pageSizeRaw = c.req.query('pageSize');\n const sortRaw = c.req.query('sort');\n const page = pageRaw != null && pageRaw !== '' ? Math.max(1, Number(pageRaw) || 1) : undefined;\n const pageSize =\n pageSizeRaw != null && pageSizeRaw !== ''\n ? Math.min(50, Math.max(1, Number(pageSizeRaw) || 20))\n : undefined;\n const sort =\n sortRaw === 'newest' || sortRaw === 'downloads' ? sortRaw : undefined;\n try {\n const payload = await service.fetchSkillsMarketplaceCatalog({\n q: q || undefined,\n page,\n pageSize,\n sort,\n });\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Marketplace request failed' },\n 502,\n );\n }\n });\n\n authenticated.get('/api/skills/marketplace/packages/:pkgName', async (c) => {\n const raw = c.req.param('pkgName');\n if (!raw) {\n return c.json({ ok: false, error: 'Missing package name' }, 400);\n }\n let pkgName: string;\n try {\n pkgName = decodeURIComponent(raw);\n } catch {\n return c.json({ ok: false, error: 'Invalid package name' }, 400);\n }\n try {\n const payload = await service.fetchSkillsMarketplacePackageDetail(pkgName);\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Marketplace request failed' },\n 502,\n );\n }\n });\n\n authenticated.post('/api/skills/marketplace/install', async (c) => {\n let body: { name?: unknown; version?: unknown; overwrite?: unknown };\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: 'Invalid JSON' }, 400);\n }\n const name = typeof body.name === 'string' ? body.name.trim() : '';\n const version = typeof body.version === 'string' ? body.version.trim() : undefined;\n const overwrite =\n body.overwrite === true ||\n body.overwrite === 'true' ||\n body.overwrite === '1';\n if (!name) {\n return c.json({ ok: false, error: 'Expected { name: string, version?: string, overwrite?: boolean }' }, 400);\n }\n try {\n const payload = await service.installSkillFromMarketplace({ name, version, overwrite });\n return c.json({ ok: true, payload });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Install failed' },\n 400,\n );\n }\n });\n\n authenticated.post('/api/skills/upload', async (c) => {\n let body: Record<string, unknown>;\n try {\n body = await c.req.parseBody({ all: true });\n } catch {\n return c.json({ ok: false, error: 'Invalid multipart body' }, 400);\n }\n const file = body['file'];\n if (!file || typeof file !== 'object') {\n return c.json({ ok: false, error: 'Missing file field' }, 400);\n }\n let buf: Buffer;\n if (file instanceof File) {\n buf = Buffer.from(await file.arrayBuffer());\n } else if (typeof (file as Blob).arrayBuffer === 'function') {\n buf = Buffer.from(await (file as Blob).arrayBuffer());\n } else {\n return c.json({ ok: false, error: 'Invalid file upload' }, 400);\n }\n const skillIdRaw = body['skillId'];\n const overwriteRaw = body['overwrite'];\n const skillId = typeof skillIdRaw === 'string' && skillIdRaw.trim() ? skillIdRaw.trim() : undefined;\n const overwrite =\n overwriteRaw === 'true' ||\n overwriteRaw === true ||\n overwriteRaw === '1';\n\n try {\n const result = service.installManagedSkillZip(buf, { skillId, overwrite });\n return c.json({ ok: true, payload: result });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Install failed' },\n 400,\n );\n }\n });\n\n authenticated.delete('/api/skills/:id', (c) => {\n const id = c.req.param('id');\n if (!id) {\n return c.json({ ok: false, error: 'Missing id' }, 400);\n }\n try {\n service.deleteManagedSkill(id);\n return c.json({ ok: true });\n } catch (err) {\n return c.json(\n { ok: false, error: err instanceof Error ? err.message : 'Delete failed' },\n 400,\n );\n }\n });\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
2
1
  import { BindingsConfigSchema, init_schema } from "../../../config/schema.js";
2
+ import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
3
3
  import { normalizePatchAgentModel } from "../lib/agent-model.js";
4
4
  import { applyToolsWebPatch } from "../../config-tools-web.js";
5
5
  import { buildSafeWebConfigPayload } from "../lib/config-payload.js";
@@ -375,6 +375,79 @@ function registerConfigRoutes(authenticated, deps) {
375
375
  if (wx.accounts !== void 0) wxTarget.accounts = wx.accounts;
376
376
  }
377
377
  }
378
+ if ("feishu" in patchChannels) {
379
+ const fsRaw = patchChannels.feishu;
380
+ if (fsRaw === null) {
381
+ if (config.channels) delete config.channels.feishu;
382
+ } else if (typeof fsRaw === "object" && !Array.isArray(fsRaw)) {
383
+ const fs = fsRaw;
384
+ if (!config.channels) config.channels = {};
385
+ if (!config.channels.feishu) config.channels.feishu = {
386
+ enabled: false,
387
+ appId: "",
388
+ appSecret: "",
389
+ domain: "feishu",
390
+ connectionMode: "websocket",
391
+ dmPolicy: "pairing",
392
+ groupPolicy: "allowlist",
393
+ allowFrom: [],
394
+ groupAllowFrom: [],
395
+ requireMention: true,
396
+ historyLimit: 50,
397
+ textChunkLimit: 4e3,
398
+ accounts: {}
399
+ };
400
+ const fsTarget = config.channels.feishu;
401
+ if (fs.enabled !== void 0) fsTarget.enabled = fs.enabled;
402
+ if (fs.defaultAccount !== void 0) {
403
+ const da = fs.defaultAccount;
404
+ if (da === null || da === "") delete fsTarget.defaultAccount;
405
+ else fsTarget.defaultAccount = String(da);
406
+ }
407
+ if (fs.appId !== void 0) fsTarget.appId = fs.appId;
408
+ if (fs.appSecret !== void 0) fsTarget.appSecret = fs.appSecret;
409
+ if (fs.domain !== void 0) fsTarget.domain = fs.domain;
410
+ if (fs.connectionMode !== void 0) fsTarget.connectionMode = fs.connectionMode;
411
+ if (fs.verificationToken !== void 0) {
412
+ const v = fs.verificationToken;
413
+ if (v === null || typeof v === "string" && !String(v).trim()) delete fsTarget.verificationToken;
414
+ else fsTarget.verificationToken = v;
415
+ }
416
+ if (fs.encryptKey !== void 0) {
417
+ const v = fs.encryptKey;
418
+ if (v === null || typeof v === "string" && !String(v).trim()) delete fsTarget.encryptKey;
419
+ else fsTarget.encryptKey = v;
420
+ }
421
+ if (fs.webhookHost !== void 0) {
422
+ const v = fs.webhookHost;
423
+ if (v === null || typeof v === "string" && !String(v).trim()) delete fsTarget.webhookHost;
424
+ else fsTarget.webhookHost = v;
425
+ }
426
+ if (fs.webhookPort !== void 0) fsTarget.webhookPort = fs.webhookPort;
427
+ if (fs.webhookPath !== void 0) {
428
+ const v = fs.webhookPath;
429
+ if (v === null || typeof v === "string" && !String(v).trim()) delete fsTarget.webhookPath;
430
+ else fsTarget.webhookPath = v;
431
+ }
432
+ if (fs.dmPolicy !== void 0) fsTarget.dmPolicy = fs.dmPolicy;
433
+ if (fs.groupPolicy !== void 0) fsTarget.groupPolicy = fs.groupPolicy;
434
+ if (fs.allowFrom !== void 0) fsTarget.allowFrom = fs.allowFrom;
435
+ if (fs.groupAllowFrom !== void 0) {
436
+ const ga = fs.groupAllowFrom;
437
+ if (ga === null || Array.isArray(ga) && ga.length === 0) delete fsTarget.groupAllowFrom;
438
+ else fsTarget.groupAllowFrom = ga;
439
+ }
440
+ if (fs.requireMention !== void 0) fsTarget.requireMention = fs.requireMention;
441
+ if (fs.historyLimit !== void 0) fsTarget.historyLimit = fs.historyLimit;
442
+ if (fs.textChunkLimit !== void 0) fsTarget.textChunkLimit = fs.textChunkLimit;
443
+ if (fs.renderMode !== void 0) fsTarget.renderMode = fs.renderMode;
444
+ if (fs.streaming !== void 0) fsTarget.streaming = fs.streaming;
445
+ if (fs.reactionNotifications !== void 0) fsTarget.reactionNotifications = fs.reactionNotifications;
446
+ if (fs.tools !== void 0) fsTarget.tools = fs.tools;
447
+ if (fs.actions !== void 0) fsTarget.actions = fs.actions;
448
+ if (fs.accounts !== void 0) fsTarget.accounts = fs.accounts;
449
+ }
450
+ }
378
451
  }
379
452
  if (body.gateway?.heartbeat !== void 0 && typeof body.gateway.heartbeat === "object") {
380
453
  if (!config.gateway) config.gateway = {
@@ -442,6 +515,14 @@ function registerConfigRoutes(authenticated, deps) {
442
515
  if (a.mode !== void 0) config.gateway.auth.mode = a.mode;
443
516
  if (a.token !== void 0) config.gateway.auth.token = a.token;
444
517
  }
518
+ if (body.update !== void 0 && typeof body.update === "object" && body.update !== null) {
519
+ const p = body.update;
520
+ if (p.channel === "stable" || p.channel === "beta" || p.channel === "dev") if (!config.update) config.update = {
521
+ checkOnStart: true,
522
+ channel: p.channel
523
+ };
524
+ else config.update.channel = p.channel;
525
+ }
445
526
  if (body.providers) {
446
527
  const resolver = new CredentialResolver();
447
528
  for (const [key, apiKey] of Object.entries(body.providers)) if (apiKey !== void 0 && typeof apiKey === "string" && apiKey.trim() && apiKey !== "***" && apiKey !== "••••••••••••") await resolver.saveApiKey(key, apiKey, { profileName: "default" });