@xopcai/xopc 0.0.20 → 0.0.21

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 (195) 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-MbH57-L9.js} +2 -2
  19. package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js.map → agents-MbH57-L9.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js → apps-page-3i3DvI7i.js} +2 -2
  21. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js.map → apps-page-3i3DvI7i.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/channels-settings-CcuSzoB6.js +9 -0
  23. package/dist/gateway/static/root/assets/channels-settings-CcuSzoB6.js.map +1 -0
  24. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js → cron-page-Be1h9Yub.js} +2 -2
  25. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js.map → cron-page-Be1h9Yub.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js → cron-utils-CR97EvZS.js} +2 -2
  27. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js.map → cron-utils-CR97EvZS.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js → dist-r_Gy-XJv.js} +2 -2
  29. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js.map → dist-r_Gy-XJv.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js → extension-debug-page-QfYEYruq.js} +2 -2
  31. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js.map → extension-debug-page-QfYEYruq.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js → extension-page-4FW-BmKG.js} +2 -2
  33. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js.map → extension-page-4FW-BmKG.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js → extension-settings-page-E_Wq9LL8.js} +2 -2
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js.map → extension-settings-page-E_Wq9LL8.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/{index-fGYWcYhm.js → index-CcQtNJKo.js} +60 -54
  37. package/dist/gateway/static/root/assets/{index-fGYWcYhm.js.map → index-CcQtNJKo.js.map} +1 -1
  38. package/dist/gateway/static/root/assets/index-D9Wmfh2f.css +1 -0
  39. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js → logs-page-DFhTU-kG.js} +2 -2
  40. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js.map → logs-page-DFhTU-kG.js.map} +1 -1
  41. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js → sessions-page-wmnnIj6Z.js} +2 -2
  42. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js.map → sessions-page-wmnnIj6Z.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/settings-page-BTmUXY4s.js +2 -0
  44. package/dist/gateway/static/root/assets/settings-page-BTmUXY4s.js.map +1 -0
  45. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js → skills-page-D-fRbJG0.js} +2 -2
  46. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js.map → skills-page-D-fRbJG0.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/settings-page-CSIVMAJE.js +0 -2
  195. package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"update.js","names":[],"sources":["../../../../../src/gateway/hono/routes/update.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { loadConfig } from '../../../config/index.js';\nimport { detectInstallKind, resolvePackageRoot } from '../../../infra/update-check.js';\nimport { DEFAULT_PACKAGE_CHANNEL, normalizeUpdateChannel } from '../../../infra/update-channels.js';\nimport { runAutoUpdateCommand } from '../../../infra/update-runner.js';\nimport { getUpdateAvailable, runGatewayUpdateCheck } from '../../../infra/update-startup.js';\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayUpdate');\n\nlet updateRunInFlight = false;\n\nfunction parseUpdateCliJson(stdout: string): Record<string, unknown> | null {\n const t = stdout.trim();\n if (!t) return null;\n try {\n const parsed = JSON.parse(t) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n const lines = t.split('\\n').filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed = JSON.parse(line) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // try previous line\n }\n }\n }\n return null;\n}\n\nexport function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware, service } = deps;\n\n /**\n * GET /api/update/status\n */\n authenticated.get('/api/update/status', (c) => {\n const update = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: update !== null,\n latestVersion: update?.latestVersion ?? null,\n channel: update?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/check\n */\n authenticated.post('/api/update/check', strictRateLimitMiddleware, async (c) => {\n const config = loadConfig(service.getHealth().configPath);\n await runGatewayUpdateCheck({\n config,\n force: true,\n onUpdateAvailableChange: (update) => {\n service.emit('update.available', update);\n },\n });\n const result = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: result !== null,\n latestVersion: result?.latestVersion ?? null,\n channel: result?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.\n */\n authenticated.post('/api/update/run', strictRateLimitMiddleware, async (c) => {\n if (updateRunInFlight) {\n return c.json(\n {\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n },\n 409,\n );\n }\n\n const configPath = service.getHealth().configPath;\n const config = loadConfig(configPath);\n const channel =\n normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n\n const root = await resolvePackageRoot();\n if (root) {\n const kind = await detectInstallKind(root);\n if (kind === 'git') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message:\n 'Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update.',\n },\n 400,\n );\n }\n }\n\n updateRunInFlight = true;\n try {\n log.info({ channel }, 'Gateway: starting one-click npm update');\n const result = await runAutoUpdateCommand({ channel, root });\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n },\n 400,\n );\n }\n\n if (!result.ok) {\n log.warn(\n { channel, exitCode: result.exitCode, reason: result.reason },\n 'Gateway: one-click npm update failed',\n );\n return c.json({\n ok: false,\n error: 'update-failed',\n message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n });\n }\n\n log.info({ channel }, 'Gateway: one-click npm update finished');\n // Do not run registry check here: the process still reports the old `PACKAGE_VERSION`\n // until the gateway is restarted, which would keep `update available` set incorrectly.\n return c.json({ ok: true, result: parsed });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: one-click npm update threw');\n return c.json(\n {\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n },\n 500,\n );\n } finally {\n updateRunInFlight = false;\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"update.js","names":[],"sources":["../../../../../src/gateway/hono/routes/update.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { loadConfig } from '../../../config/index.js';\nimport { acquireUpdateLock } from '../../../infra/update-lock.js';\nimport { detectInstallKind, resolvePackageRoot } from '../../../infra/update-check.js';\nimport {\n DEFAULT_PACKAGE_CHANNEL,\n normalizeUpdateChannel,\n type UpdateChannel,\n} from '../../../infra/update-channels.js';\nimport { runAutoUpdateCommand, runAutoUpdateCommandWithProgress } from '../../../infra/update-runner.js';\nimport { getUpdateAvailable, runGatewayUpdateCheck } from '../../../infra/update-startup.js';\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayUpdate');\n\nfunction parseUpdateCliJson(stdout: string): Record<string, unknown> | null {\n const t = stdout.trim();\n if (!t) return null;\n try {\n const parsed = JSON.parse(t) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n const lines = t.split('\\n').filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed = JSON.parse(line) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // try previous line\n }\n }\n }\n return null;\n}\n\ntype PreconditionOk = {\n ok: true;\n channel: UpdateChannel;\n root: string | null;\n};\n\nfunction isPreconditionFail(\n x: PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> },\n): x is { ok: false; status: 400; body: Record<string, unknown> } {\n return !x.ok;\n}\n\nasync function npmUpdatePreconditions(\n service: AuthenticatedRouteDeps['service'],\n): Promise<PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> }> {\n const config = loadConfig(service.getHealth().configPath);\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n\n const root = await resolvePackageRoot();\n if (root) {\n const kind = await detectInstallKind(root);\n if (kind === 'git') {\n return {\n ok: false,\n status: 400,\n body: {\n ok: false,\n error: 'git-checkout',\n message:\n 'Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update.',\n },\n };\n }\n }\n\n return { ok: true, channel, root };\n}\n\nexport function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware, service } = deps;\n\n /**\n * GET /api/update/status\n */\n authenticated.get('/api/update/status', (c) => {\n const update = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: update !== null,\n latestVersion: update?.latestVersion ?? null,\n channel: update?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/check\n */\n authenticated.post('/api/update/check', strictRateLimitMiddleware, async (c) => {\n const config = loadConfig(service.getHealth().configPath);\n await runGatewayUpdateCheck({\n config,\n force: true,\n onUpdateAvailableChange: (update) => {\n service.emit('update.available', update);\n },\n });\n const result = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: result !== null,\n latestVersion: result?.latestVersion ?? null,\n channel: result?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.\n */\n authenticated.post('/api/update/run', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n return c.json(\n {\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n },\n 409,\n );\n }\n\n const { channel, root } = pre;\n try {\n log.info({ channel }, 'Gateway: starting one-click npm update');\n const result = await runAutoUpdateCommand({ channel, root });\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n },\n 400,\n );\n }\n\n if (!result.ok) {\n log.warn(\n { channel, exitCode: result.exitCode, reason: result.reason },\n 'Gateway: one-click npm update failed',\n );\n return c.json({\n ok: false,\n error: 'update-failed',\n message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n });\n }\n\n log.info({ channel }, 'Gateway: one-click npm update finished');\n return c.json({ ok: true, result: parsed });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: one-click npm update threw');\n return c.json(\n {\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n },\n 500,\n );\n } finally {\n await lock.release();\n }\n });\n\n /**\n * POST /api/update/run/stream — SSE-streamed npm update with progress lines.\n */\n authenticated.post('/api/update/run/stream', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const { channel, root } = pre;\n\n return streamSSE(c, async (stream) => {\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n }),\n });\n return;\n }\n\n try {\n log.info({ channel }, 'Gateway: starting streamed one-click npm update');\n const result = await runAutoUpdateCommandWithProgress({\n channel,\n root,\n onProgress: async (line, source) => {\n await stream.writeSSE({\n event: 'progress',\n data: JSON.stringify({ line, source }),\n });\n },\n });\n\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n }),\n });\n return;\n }\n\n if (!result.ok) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'update-failed',\n message:\n result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n exitCode: result.exitCode,\n reason: result.reason,\n }),\n });\n return;\n }\n\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: true, result: parsed }),\n });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: streamed npm update threw');\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n }),\n });\n } finally {\n await lock.release();\n }\n });\n });\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { createLogger } from "../../../utils/logger/index.js";
2
- import { init_logger } from "../../../utils/logger.js";
3
1
  import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentHomeDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
4
2
  import { getWorkspacePath, init_schema } from "../../../config/schema.js";
5
3
  import { extractProfileAgentId } from "../../../config/agent-profile.js";
4
+ import { createLogger } from "../../../utils/logger/index.js";
5
+ import { init_logger } from "../../../utils/logger.js";
6
6
  import { resolveSafeInboundFilePath } from "../../../channels/attachments/inbound-persist.js";
7
7
  import { isPathUnderWorkspace, resolveWorkspaceSafePath, toWorkspaceRelativePosix } from "../../workspace-editor-path.js";
8
8
  import { resolveSafeTtsFilePath } from "../../../channels/attachments/outbound-tts-persist.js";
@@ -1,8 +1,8 @@
1
+ import { buildSessionKey, init_session_key, parseSessionKey } from "../../routing/session-key.js";
2
+ import { getDefaultAgentId, init_resolve_route } from "../../routing/resolve-route.js";
1
3
  import { updateAsyncLogContext } from "../../utils/logger/context.js";
2
4
  import { createLogger } from "../../utils/logger/index.js";
3
5
  import { init_logger } from "../../utils/logger.js";
4
- import { buildSessionKey, init_session_key, parseSessionKey } from "../../routing/session-key.js";
5
- import { getDefaultAgentId, init_resolve_route } from "../../routing/resolve-route.js";
6
6
  import { MAX_WEBCHAT_ATTACHMENT_FILE_BYTES } from "../chat-limits.js";
7
7
  import { stringifySSEData } from "./sse-json.js";
8
8
  import { randomUUID } from "node:crypto";
@@ -1,10 +1,10 @@
1
1
  import { GatewayLockError, acquireGatewayLock } from "./lock.js";
2
2
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, safeCompare, validateToken } from "./auth.js";
3
+ import { restartGatewayProcessWithFreshPid } from "./respawn.js";
3
4
  import { GatewayService } from "./service.js";
4
5
  import { createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createSendHandler } from "./hono/sse.js";
5
6
  import { createHonoApp } from "./hono/app.js";
6
7
  import { GatewayServer } from "./server.js";
7
- import { restartGatewayProcessWithFreshPid } from "./respawn.js";
8
8
  import { runGatewayLoop } from "./run-loop.js";
9
9
  import { checkPortAvailable, forceFreePortAndWait, listPortListeners, parseLsofOutput } from "./ports.js";
10
10
  import { apiError, apiOk } from "./protocol.js";
@@ -16,6 +16,9 @@ var GatewayServer = class {
16
16
  async start() {
17
17
  console.log(`[GatewayServer] Starting gateway server on ${this.config.host}:${this.config.port}...`);
18
18
  await this.service.start();
19
+ this.service.registerGatewayShutdownForRestart(async () => {
20
+ await this.stop();
21
+ });
19
22
  const effectiveToken = this.config.token || this.service.getAuthToken();
20
23
  const app = createHonoApp({
21
24
  service: this.service,
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":[],"sources":["../../../src/gateway/server.ts"],"sourcesContent":["import { serve, type ServerType } from '@hono/node-server';\nimport { GatewayService } from './service.js';\nimport { createHonoApp } from './hono/app.js';\n\nexport interface GatewayServerConfig {\n host: string;\n port: number;\n token?: string;\n verbose?: boolean;\n configPath?: string;\n enableHotReload?: boolean;\n}\n\nexport class GatewayServer {\n private server?: ServerType;\n private config: GatewayServerConfig;\n private service: GatewayService;\n\n constructor(config: GatewayServerConfig) {\n this.config = config;\n this.service = new GatewayService({\n configPath: config.configPath,\n enableHotReload: config.enableHotReload,\n });\n }\n\n async start(): Promise<void> {\n console.log(`[GatewayServer] Starting gateway server on ${this.config.host}:${this.config.port}...`);\n\n // Start the underlying service first\n await this.service.start();\n\n // Create Hono app\n // Priority: CLI token > service auto-generated token\n const effectiveToken = this.config.token || this.service.getAuthToken();\n const app = createHonoApp({\n service: this.service,\n token: effectiveToken,\n });\n\n // Create Node.js HTTP server (no WebSocket upgrade needed)\n this.server = serve({\n fetch: app.fetch,\n port: this.config.port,\n hostname: this.config.host,\n }, () => {\n console.log(`[GatewayServer] Gateway server running at http://${this.config.host}:${this.config.port}`);\n });\n }\n\n async close(opts?: { reason?: string; restartExpectedMs?: number | null }): Promise<void> {\n const reason = opts?.reason ?? 'gateway stopping';\n console.log(`[GatewayServer] Closing gateway server: ${reason}`);\n await this.stop();\n }\n\n async stop(): Promise<void> {\n console.log('[GatewayServer] Stopping gateway server...');\n\n // Stop the HTTP server\n if (this.server) {\n // Force server close after a short delay to allow connections to drain\n const forceClose = setTimeout(() => {\n if (this.server) {\n (this.server as any).closeAllConnections?.();\n }\n }, 2000);\n \n await new Promise<void>((resolve) => {\n this.server!.close(() => {\n clearTimeout(forceClose);\n resolve();\n });\n });\n this.server = undefined;\n }\n\n // Stop the underlying service\n await this.service.stop();\n\n console.log('[GatewayServer] Gateway server stopped');\n }\n\n get isRunning(): boolean {\n return this.server !== undefined;\n }\n\n get serviceInstance(): GatewayService {\n return this.service;\n }\n}\n"],"mappings":";;;;AAaA,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CAEA,YAAY,QAA6B;AACvC,OAAK,SAAS;AACd,OAAK,UAAU,IAAI,eAAe;GAChC,YAAY,OAAO;GACnB,iBAAiB,OAAO;GACzB,CAAC;;CAGJ,MAAM,QAAuB;AAC3B,UAAQ,IAAI,8CAA8C,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,KAAK;AAGpG,QAAM,KAAK,QAAQ,OAAO;EAI1B,MAAM,iBAAiB,KAAK,OAAO,SAAS,KAAK,QAAQ,cAAc;EACvE,MAAM,MAAM,cAAc;GACxB,SAAS,KAAK;GACd,OAAO;GACR,CAAC;AAGF,OAAK,SAAS,MAAM;GAClB,OAAO,IAAI;GACX,MAAM,KAAK,OAAO;GAClB,UAAU,KAAK,OAAO;GACvB,QAAQ;AACP,WAAQ,IAAI,oDAAoD,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,OAAO;IACvG;;CAGJ,MAAM,MAAM,MAA8E;EACxF,MAAM,SAAS,MAAM,UAAU;AAC/B,UAAQ,IAAI,2CAA2C,SAAS;AAChE,QAAM,KAAK,MAAM;;CAGnB,MAAM,OAAsB;AAC1B,UAAQ,IAAI,6CAA6C;AAGzD,MAAI,KAAK,QAAQ;GAEf,MAAM,aAAa,iBAAiB;AAClC,QAAI,KAAK,OACN,MAAK,OAAe,uBAAuB;MAE7C,IAAK;AAER,SAAM,IAAI,SAAe,YAAY;AACnC,SAAK,OAAQ,YAAY;AACvB,kBAAa,WAAW;AACxB,cAAS;MACT;KACF;AACF,QAAK,SAAS,KAAA;;AAIhB,QAAM,KAAK,QAAQ,MAAM;AAEzB,UAAQ,IAAI,yCAAyC;;CAGvD,IAAI,YAAqB;AACvB,SAAO,KAAK,WAAW,KAAA;;CAGzB,IAAI,kBAAkC;AACpC,SAAO,KAAK"}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../../src/gateway/server.ts"],"sourcesContent":["import { serve, type ServerType } from '@hono/node-server';\nimport { GatewayService } from './service.js';\nimport { createHonoApp } from './hono/app.js';\n\nexport interface GatewayServerConfig {\n host: string;\n port: number;\n token?: string;\n verbose?: boolean;\n configPath?: string;\n enableHotReload?: boolean;\n}\n\nexport class GatewayServer {\n private server?: ServerType;\n private config: GatewayServerConfig;\n private service: GatewayService;\n\n constructor(config: GatewayServerConfig) {\n this.config = config;\n this.service = new GatewayService({\n configPath: config.configPath,\n enableHotReload: config.enableHotReload,\n });\n }\n\n async start(): Promise<void> {\n console.log(`[GatewayServer] Starting gateway server on ${this.config.host}:${this.config.port}...`);\n\n // Start the underlying service first\n await this.service.start();\n this.service.registerGatewayShutdownForRestart(async () => {\n await this.stop();\n });\n\n // Create Hono app\n // Priority: CLI token > service auto-generated token\n const effectiveToken = this.config.token || this.service.getAuthToken();\n const app = createHonoApp({\n service: this.service,\n token: effectiveToken,\n });\n\n // Create Node.js HTTP server (no WebSocket upgrade needed)\n this.server = serve({\n fetch: app.fetch,\n port: this.config.port,\n hostname: this.config.host,\n }, () => {\n console.log(`[GatewayServer] Gateway server running at http://${this.config.host}:${this.config.port}`);\n });\n }\n\n async close(opts?: { reason?: string; restartExpectedMs?: number | null }): Promise<void> {\n const reason = opts?.reason ?? 'gateway stopping';\n console.log(`[GatewayServer] Closing gateway server: ${reason}`);\n await this.stop();\n }\n\n async stop(): Promise<void> {\n console.log('[GatewayServer] Stopping gateway server...');\n\n // Stop the HTTP server\n if (this.server) {\n // Force server close after a short delay to allow connections to drain\n const forceClose = setTimeout(() => {\n if (this.server) {\n (this.server as any).closeAllConnections?.();\n }\n }, 2000);\n \n await new Promise<void>((resolve) => {\n this.server!.close(() => {\n clearTimeout(forceClose);\n resolve();\n });\n });\n this.server = undefined;\n }\n\n // Stop the underlying service\n await this.service.stop();\n\n console.log('[GatewayServer] Gateway server stopped');\n }\n\n get isRunning(): boolean {\n return this.server !== undefined;\n }\n\n get serviceInstance(): GatewayService {\n return this.service;\n }\n}\n"],"mappings":";;;;AAaA,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CAEA,YAAY,QAA6B;AACvC,OAAK,SAAS;AACd,OAAK,UAAU,IAAI,eAAe;GAChC,YAAY,OAAO;GACnB,iBAAiB,OAAO;GACzB,CAAC;;CAGJ,MAAM,QAAuB;AAC3B,UAAQ,IAAI,8CAA8C,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,KAAK;AAGpG,QAAM,KAAK,QAAQ,OAAO;AAC1B,OAAK,QAAQ,kCAAkC,YAAY;AACzD,SAAM,KAAK,MAAM;IACjB;EAIF,MAAM,iBAAiB,KAAK,OAAO,SAAS,KAAK,QAAQ,cAAc;EACvE,MAAM,MAAM,cAAc;GACxB,SAAS,KAAK;GACd,OAAO;GACR,CAAC;AAGF,OAAK,SAAS,MAAM;GAClB,OAAO,IAAI;GACX,MAAM,KAAK,OAAO;GAClB,UAAU,KAAK,OAAO;GACvB,QAAQ;AACP,WAAQ,IAAI,oDAAoD,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,OAAO;IACvG;;CAGJ,MAAM,MAAM,MAA8E;EACxF,MAAM,SAAS,MAAM,UAAU;AAC/B,UAAQ,IAAI,2CAA2C,SAAS;AAChE,QAAM,KAAK,MAAM;;CAGnB,MAAM,OAAsB;AAC1B,UAAQ,IAAI,6CAA6C;AAGzD,MAAI,KAAK,QAAQ;GAEf,MAAM,aAAa,iBAAiB;AAClC,QAAI,KAAK,OACN,MAAK,OAAe,uBAAuB;MAE7C,IAAK;AAER,SAAM,IAAI,SAAe,YAAY;AACnC,SAAK,OAAQ,YAAY;AACvB,kBAAa,WAAW;AACxB,cAAS;MACT;KACF;AACF,QAAK,SAAS,KAAA;;AAIhB,QAAM,KAAK,QAAQ,MAAM;AAEzB,UAAQ,IAAI,yCAAyC;;CAGvD,IAAI,YAAqB;AACvB,SAAO,KAAK,WAAW,KAAA;;CAGzB,IAAI,kBAAkC;AACpC,SAAO,KAAK"}
@@ -40,6 +40,8 @@ export declare class GatewayService {
40
40
  /** Per-run abort for webchat (POST /api/agent/abort or client disconnect). */
41
41
  private runAbortControllers;
42
42
  private stopGatewayUpdateCheck;
43
+ /** When set (e.g. by `GatewayServer`), `triggerGatewayProcessRestart` can stop HTTP then exit. */
44
+ private gatewayShutdownForRestart;
43
45
  private readonly clarifyBridge;
44
46
  /** Maps webchat session key → active `runId` for `clarify` tool routing. */
45
47
  private activeWebchatRunBySession;
@@ -94,6 +96,10 @@ export declare class GatewayService {
94
96
  * Handle tools config hot reload
95
97
  */
96
98
  private handleToolsReload;
99
+ /**
100
+ * Dispatch config hot reload to extensions that registered `registerReload`, matching changed paths.
101
+ */
102
+ private handleExtensionsReload;
97
103
  /**
98
104
  * Reload configuration from disk (manual trigger)
99
105
  */
@@ -106,6 +112,11 @@ export declare class GatewayService {
106
112
  * channel apply as an API save, then force Weixin long-poll restart (see `reloadMonitorsWithConfig`).
107
113
  */
108
114
  afterWeixinCredentialsPersisted(): Promise<void>;
115
+ /**
116
+ * After Feishu WebUI QR setup: `channels.feishu` was written directly to disk; reload into memory
117
+ * and apply channel plugins (same baseline as PATCH /api/config).
118
+ */
119
+ afterFeishuCredentialsPersisted(): Promise<void>;
109
120
  /**
110
121
  * Save current config to disk
111
122
  */
@@ -193,6 +204,18 @@ export declare class GatewayService {
193
204
  requestHeartbeatNow(opts?: {
194
205
  reason?: string;
195
206
  }): void;
207
+ /**
208
+ * Register graceful shutdown used after spawning a replacement gateway process (foreground CLI server).
209
+ */
210
+ registerGatewayShutdownForRestart(handler: () => Promise<void>): void;
211
+ /**
212
+ * Respawn the gateway process when supported (spawn + exit, supervisor exit, or disabled when XOPC_NO_RESPAWN).
213
+ */
214
+ triggerGatewayProcessRestart(): {
215
+ ok: boolean;
216
+ mode: string;
217
+ message?: string;
218
+ };
196
219
  /**
197
220
  * Get health status
198
221
  */
@@ -1,16 +1,16 @@
1
1
  import { __toCommonJS } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
3
+ import { resolveStateDir } from "../config/paths-state.js";
4
+ import { getWorkspacePath, init_schema } from "../config/schema.js";
5
+ import { buildSessionKey, init_session_key, parseSessionKey } from "../routing/session-key.js";
6
+ import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
3
7
  import { getLogDir } from "../utils/logger/config.js";
4
8
  import { getLogStats } from "../utils/logger/stats.js";
5
9
  import { inboundCorrelationMetadataFromAsyncLogContext } from "../utils/logger/context.js";
6
10
  import { createLogger } from "../utils/logger/index.js";
7
11
  import { init_logger } from "../utils/logger.js";
8
- import { resolveStateDir } from "../config/paths-state.js";
9
12
  import { init_paths, resolveAgentDir, resolveConfigPath, resolveCronJobsPath, resolveWorkspaceExtensionsDir } from "../config/paths.js";
10
- import { getWorkspacePath, init_schema } from "../config/schema.js";
11
13
  import { loadConfig, saveConfig } from "../config/loader.js";
12
- import { buildSessionKey, init_session_key, parseSessionKey } from "../routing/session-key.js";
13
- import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
14
14
  import { getModelRegistry } from "../providers/model-registry.js";
15
15
  import { init_providers, providers_exports } from "../providers/index.js";
16
16
  import { createSkillConfigManager } from "../agent/skills/config.js";
@@ -39,6 +39,7 @@ import { AgentRunRelay } from "./agent-run-relay.js";
39
39
  import { ClarifyBridge } from "./clarify-bridge.js";
40
40
  import { downloadSkillZipBuffer, fetchMarketplacePackageDetail, listSkillPackages, resolveSkillZipDownloadUrl, resolveSkillsStoreBaseUrl, skillIdForMarketplaceInstall } from "../agent/skills/skills-store-client.js";
41
41
  import { scheduleGatewayUpdateCheck } from "../infra/update-startup.js";
42
+ import { restartGatewayProcessWithFreshPid } from "./respawn.js";
42
43
  import "./chat-limits.js";
43
44
  import crypto from "crypto";
44
45
  //#region src/gateway/service.ts
@@ -73,6 +74,8 @@ var GatewayService = class {
73
74
  /** Per-run abort for webchat (POST /api/agent/abort or client disconnect). */
74
75
  runAbortControllers = /* @__PURE__ */ new Map();
75
76
  stopGatewayUpdateCheck = null;
77
+ /** When set (e.g. by `GatewayServer`), `triggerGatewayProcessRestart` can stop HTTP then exit. */
78
+ gatewayShutdownForRestart = null;
76
79
  clarifyBridge = new ClarifyBridge();
77
80
  /** Maps webchat session key → active `runId` for `clarify` tool routing. */
78
81
  activeWebchatRunBySession = /* @__PURE__ */ new Map();
@@ -286,6 +289,9 @@ var GatewayService = class {
286
289
  onCronReload: (newConfig) => this.handleCronReload(newConfig),
287
290
  onHeartbeatReload: (newConfig) => this.handleHeartbeatReload(newConfig),
288
291
  onToolsReload: (newConfig) => this.handleToolsReload(newConfig),
292
+ onExtensionsReload: async (newConfig, changedPaths) => {
293
+ await this.handleExtensionsReload(newConfig, changedPaths);
294
+ },
289
295
  onFullRestart: (newConfig) => {
290
296
  log.warn({
291
297
  requiresProcessRestart: true,
@@ -377,6 +383,58 @@ var GatewayService = class {
377
383
  log.debug("Tools config reloaded");
378
384
  }
379
385
  /**
386
+ * Dispatch config hot reload to extensions that registered `registerReload`, matching changed paths.
387
+ */
388
+ async handleExtensionsReload(newConfig, changedPaths) {
389
+ this.config = newConfig;
390
+ this.extensionLoader?.setConfig(this.config);
391
+ if (!this.extensionLoader) {
392
+ this.emit("config.reload", {
393
+ section: "extensions",
394
+ source: "extension-reload",
395
+ changedPaths
396
+ });
397
+ return;
398
+ }
399
+ const matchingRegs = this.extensionLoader.getRegistry().getMatchingReloadRegistrations(changedPaths);
400
+ if (matchingRegs.length === 0) {
401
+ log.debug({ changedPaths }, "No extension reload handlers matched");
402
+ this.emit("config.reload", {
403
+ section: "extensions",
404
+ source: "extension-reload",
405
+ changedPaths
406
+ });
407
+ return;
408
+ }
409
+ for (const reg of matchingRegs) {
410
+ const relevantPaths = changedPaths.filter((p) => reg.configPrefixes.length === 0 || reg.configPrefixes.some((prefix) => p === prefix || p.startsWith(`${prefix}.`)));
411
+ log.info({
412
+ extensionId: reg.extensionId,
413
+ relevantPaths
414
+ }, "Calling extension reload handler");
415
+ try {
416
+ const result = await reg.handler(newConfig, relevantPaths);
417
+ if (result.success) log.info({ extensionId: reg.extensionId }, "Extension reload succeeded");
418
+ else log.warn({
419
+ extensionId: reg.extensionId,
420
+ error: result.error
421
+ }, `Extension reload reported failure: ${result.error ?? "unknown"}`);
422
+ } catch (err) {
423
+ const errorMessage = err instanceof Error ? err.message : String(err);
424
+ log.error({
425
+ err,
426
+ extensionId: reg.extensionId,
427
+ errorMessage
428
+ }, `Extension reload handler threw: ${errorMessage}`);
429
+ }
430
+ }
431
+ this.emit("config.reload", {
432
+ section: "extensions",
433
+ source: "extension-reload",
434
+ changedPaths
435
+ });
436
+ }
437
+ /**
380
438
  * Reload configuration from disk (manual trigger)
381
439
  */
382
440
  async reloadConfig() {
@@ -405,6 +463,18 @@ var GatewayService = class {
405
463
  log.info("Weixin monitors restarted after credential login");
406
464
  }
407
465
  /**
466
+ * After Feishu WebUI QR setup: `channels.feishu` was written directly to disk; reload into memory
467
+ * and apply channel plugins (same baseline as PATCH /api/config).
468
+ */
469
+ async afterFeishuCredentialsPersisted() {
470
+ const next = loadConfig(this.configPath);
471
+ this.config = next;
472
+ this.agentService.applyAgentDefaultsFromConfig(next);
473
+ this.configReloader?.syncCurrentConfig(next);
474
+ await this.handleChannelsReload(next);
475
+ log.info("Feishu config applied after QR setup");
476
+ }
477
+ /**
408
478
  * Save current config to disk
409
479
  */
410
480
  /**
@@ -744,6 +814,43 @@ var GatewayService = class {
744
814
  this.heartbeatService.requestNow({ reason: opts?.reason ?? "manual" });
745
815
  }
746
816
  /**
817
+ * Register graceful shutdown used after spawning a replacement gateway process (foreground CLI server).
818
+ */
819
+ registerGatewayShutdownForRestart(handler) {
820
+ this.gatewayShutdownForRestart = handler;
821
+ }
822
+ /**
823
+ * Respawn the gateway process when supported (spawn + exit, supervisor exit, or disabled when XOPC_NO_RESPAWN).
824
+ */
825
+ triggerGatewayProcessRestart() {
826
+ const result = restartGatewayProcessWithFreshPid();
827
+ if (result.mode === "failed") return {
828
+ ok: false,
829
+ mode: result.mode,
830
+ message: result.detail ?? "spawn failed"
831
+ };
832
+ if (result.mode === "disabled") return {
833
+ ok: false,
834
+ mode: "disabled",
835
+ message: "Process respawn is disabled (XOPC_NO_RESPAWN). Restart the gateway manually (e.g. xopc gateway restart)."
836
+ };
837
+ const shutdown = this.gatewayShutdownForRestart;
838
+ if (!shutdown) return {
839
+ ok: false,
840
+ mode: result.mode,
841
+ message: "Gateway restart is not available in this process."
842
+ };
843
+ setImmediate(() => {
844
+ shutdown().finally(() => {
845
+ process.exit(0);
846
+ });
847
+ });
848
+ return {
849
+ ok: true,
850
+ mode: result.mode
851
+ };
852
+ }
853
+ /**
747
854
  * Get health status
748
855
  */
749
856
  getHealth() {