@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
@@ -0,0 +1,115 @@
1
+ import { createLogger } from "../../utils/logger/index.js";
2
+ import { init_logger } from "../../utils/logger.js";
3
+ import { init_paths, resolveConfigPath } from "../../config/paths.js";
4
+ import { loadConfig } from "../../config/loader.js";
5
+ import "../../config/index.js";
6
+ //#region src/cli/utils/gateway-client.ts
7
+ init_paths();
8
+ init_logger();
9
+ const log = createLogger("GatewayClient");
10
+ function resolveGatewayUrl(opts) {
11
+ if (opts?.url) return opts.url.replace(/\/+$/, "");
12
+ try {
13
+ const config = loadConfig(opts?.configPath ?? resolveConfigPath());
14
+ const host = config?.gateway?.host || "127.0.0.1";
15
+ const port = config?.gateway?.port ?? 18790;
16
+ return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
17
+ } catch {
18
+ return "http://127.0.0.1:18790";
19
+ }
20
+ }
21
+ function resolveGatewayToken(opts) {
22
+ if (opts?.token) return opts.token;
23
+ const envToken = process.env.XOPC_GATEWAY_TOKEN;
24
+ if (envToken) return envToken;
25
+ try {
26
+ return loadConfig(opts?.configPath ?? resolveConfigPath())?.gateway?.auth?.token;
27
+ } catch {
28
+ return;
29
+ }
30
+ }
31
+ async function callGatewayApi(method, path, opts, body) {
32
+ const baseUrl = resolveGatewayUrl(opts);
33
+ const token = resolveGatewayToken(opts);
34
+ const timeoutMs = opts?.timeoutMs ?? 1e4;
35
+ const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
36
+ const headers = { Accept: "application/json" };
37
+ if (token) headers.Authorization = `Bearer ${token}`;
38
+ if (body !== void 0) headers["Content-Type"] = "application/json";
39
+ const startedAt = Date.now();
40
+ try {
41
+ const controller = new AbortController();
42
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
43
+ const response = await fetch(url, {
44
+ method,
45
+ headers,
46
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
47
+ signal: controller.signal
48
+ });
49
+ clearTimeout(timer);
50
+ const durationMs = Date.now() - startedAt;
51
+ if (!response.ok) {
52
+ let errorMessage;
53
+ try {
54
+ const errorBody = await response.json();
55
+ errorMessage = errorBody.error || errorBody.message || response.statusText;
56
+ } catch {
57
+ errorMessage = response.statusText;
58
+ }
59
+ log.debug({
60
+ url,
61
+ status: response.status,
62
+ durationMs
63
+ }, `Gateway call failed: ${errorMessage}`);
64
+ return {
65
+ ok: false,
66
+ status: response.status,
67
+ error: errorMessage,
68
+ durationMs
69
+ };
70
+ }
71
+ const data = await response.json();
72
+ log.debug({
73
+ url,
74
+ status: response.status,
75
+ durationMs
76
+ }, "Gateway call succeeded");
77
+ return {
78
+ ok: true,
79
+ status: response.status,
80
+ data,
81
+ durationMs
82
+ };
83
+ } catch (err) {
84
+ const durationMs = Date.now() - startedAt;
85
+ const errorMessage = err instanceof Error && err.name === "AbortError" ? `Request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err);
86
+ log.debug({
87
+ url,
88
+ err,
89
+ durationMs
90
+ }, `Gateway call error: ${errorMessage}`);
91
+ return {
92
+ ok: false,
93
+ status: 0,
94
+ error: errorMessage,
95
+ durationMs
96
+ };
97
+ }
98
+ }
99
+ function addGatewayClientOptions(cmd) {
100
+ return cmd.option("--url <url>", "Gateway HTTP URL (defaults to config or http://127.0.0.1:18790)").option("--token <token>", "Gateway auth token").option("--timeout <ms>", "Request timeout in ms", "10000").option("--json", "Output raw JSON", false);
101
+ }
102
+ function parseGatewayClientOptions(opts) {
103
+ const rawTimeout = opts.timeout;
104
+ const timeoutMs = typeof rawTimeout === "string" ? Number.parseInt(rawTimeout, 10) || 1e4 : typeof rawTimeout === "number" && Number.isFinite(rawTimeout) ? rawTimeout : 1e4;
105
+ return {
106
+ url: typeof opts.url === "string" ? opts.url : void 0,
107
+ token: typeof opts.token === "string" ? opts.token : void 0,
108
+ timeoutMs,
109
+ json: Boolean(opts.json)
110
+ };
111
+ }
112
+ //#endregion
113
+ export { addGatewayClientOptions, callGatewayApi, parseGatewayClientOptions, resolveGatewayToken, resolveGatewayUrl };
114
+
115
+ //# sourceMappingURL=gateway-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-client.js","names":[],"sources":["../../../../src/cli/utils/gateway-client.ts"],"sourcesContent":["/**\n * Gateway API client — CLI-side HTTP client for gateway REST routes.\n */\n\nimport type { Command } from 'commander';\n\nimport { loadConfig } from '../../config/index.js';\nimport { resolveConfigPath } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('GatewayClient');\n\nexport interface GatewayClientOptions {\n url?: string;\n token?: string;\n timeoutMs?: number;\n json?: boolean;\n}\n\nexport interface GatewayCallResult<T = unknown> {\n ok: boolean;\n status: number;\n data?: T;\n error?: string;\n durationMs: number;\n}\n\nexport function resolveGatewayUrl(opts?: { url?: string; configPath?: string }): string {\n if (opts?.url) {\n return opts.url.replace(/\\/+$/, '');\n }\n\n try {\n const configPath = opts?.configPath ?? resolveConfigPath();\n const config = loadConfig(configPath);\n const host = config?.gateway?.host || '127.0.0.1';\n const port = config?.gateway?.port ?? 18790;\n const displayHost = host === '0.0.0.0' ? '127.0.0.1' : host;\n return `http://${displayHost}:${port}`;\n } catch {\n return 'http://127.0.0.1:18790';\n }\n}\n\nexport function resolveGatewayToken(opts?: { token?: string; configPath?: string }): string | undefined {\n if (opts?.token) return opts.token;\n\n const envToken = process.env.XOPC_GATEWAY_TOKEN;\n if (envToken) return envToken;\n\n try {\n const configPath = opts?.configPath ?? resolveConfigPath();\n const config = loadConfig(configPath);\n return config?.gateway?.auth?.token;\n } catch {\n return undefined;\n }\n}\n\nexport async function callGatewayApi<T = unknown>(\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE',\n path: string,\n opts?: GatewayClientOptions,\n body?: unknown,\n): Promise<GatewayCallResult<T>> {\n const baseUrl = resolveGatewayUrl(opts);\n const token = resolveGatewayToken(opts);\n const timeoutMs = opts?.timeoutMs ?? 10_000;\n const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const startedAt = Date.now();\n\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n const durationMs = Date.now() - startedAt;\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorBody = (await response.json()) as { error?: string; message?: string };\n errorMessage = errorBody.error || errorBody.message || response.statusText;\n } catch {\n errorMessage = response.statusText;\n }\n\n log.debug({ url, status: response.status, durationMs }, `Gateway call failed: ${errorMessage}`);\n return { ok: false, status: response.status, error: errorMessage, durationMs };\n }\n\n const data = (await response.json()) as T;\n log.debug({ url, status: response.status, durationMs }, 'Gateway call succeeded');\n return { ok: true, status: response.status, data, durationMs };\n } catch (err) {\n const durationMs = Date.now() - startedAt;\n const isAbort = err instanceof Error && err.name === 'AbortError';\n const errorMessage = isAbort\n ? `Request timed out after ${timeoutMs}ms`\n : err instanceof Error\n ? err.message\n : String(err);\n\n log.debug({ url, err, durationMs }, `Gateway call error: ${errorMessage}`);\n return { ok: false, status: 0, error: errorMessage, durationMs };\n }\n}\n\nexport function addGatewayClientOptions(cmd: Command): Command {\n return cmd\n .option('--url <url>', 'Gateway HTTP URL (defaults to config or http://127.0.0.1:18790)')\n .option('--token <token>', 'Gateway auth token')\n .option('--timeout <ms>', 'Request timeout in ms', '10000')\n .option('--json', 'Output raw JSON', false);\n}\n\nexport function parseGatewayClientOptions(opts: Record<string, unknown>): GatewayClientOptions {\n const rawTimeout = opts.timeout;\n const timeoutMs =\n typeof rawTimeout === 'string'\n ? Number.parseInt(rawTimeout, 10) || 10_000\n : typeof rawTimeout === 'number' && Number.isFinite(rawTimeout)\n ? rawTimeout\n : 10_000;\n return {\n url: typeof opts.url === 'string' ? opts.url : undefined,\n token: typeof opts.token === 'string' ? opts.token : undefined,\n timeoutMs,\n json: Boolean(opts.json),\n };\n}\n"],"mappings":"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"}
@@ -2,13 +2,13 @@ import { ENV_VARS, resolveHomeDir, resolveStateDir } from "./paths-state.js";
2
2
  import { expandWorkspacePathString, init_workspace_path, normalizeWorkspaceDir, resolveWorkspaceRoot } from "./workspace-path.js";
3
3
  import { resolveDefaultAgentWorkspaceDir } from "./workspace-defaults.js";
4
4
  import { resolveAgentBootstrapDir, resolveAgentDir as resolveAgentDir$1, resolveAgentHomeDir as resolveAgentHomeDir$1, resolveAgentWorkspaceDir, resolveSessionsDir as resolveSessionsDir$1 } from "../agent/agent-scope.js";
5
- import { FILENAMES, WORKSPACE_FILES, existsSync, init_paths, resolveAgentAuthProfilesPath, resolveAgentCredentialsDir, resolveAgentDir, resolveAgentHomeDir, resolveAgentMetadataPath, resolveAuthProfilesPath, resolveBinDir, resolveBundledExtensionsDir, resolveBundledSkillsDir, resolveConfigPath, resolveCredentialsDir, resolveCronDir, resolveCronJobsPath, resolveCronLogPath, resolveCronLogsDir, resolveCronRunsDir, resolveExtensionSdkPath, resolveExtensionsDir, resolveExtensionsLockPath, resolveInboxDir, resolveInboxMessagePath, resolveInboxPendingDir, resolveInboxProcessedDir, resolveLogPath, resolveLogsDir, resolveMemoryDir, resolveMemoryPath, resolveModelsJsonPath, resolveNodeBinDir, resolveNodeBinPath, resolveNodeToolsDir, resolveNpmBinPath, resolveOAuthPath, resolvePidPath, resolveSessionTranscriptPath, resolveSessionTranscriptPathInDir, resolveSessionsArchiveDir, resolveSessionsDir, resolveSessionsIndexPath, resolveSkillPath, resolveSkillsCachePath, resolveSkillsDir, resolveSkillsLockPath, resolveSocketPath, resolveStatusPath, resolveToolsDir, resolveWorkspaceExtensionsDir, resolveWorkspaceFile, resolveWorkspaceStateDir, resolveWorkspaceStatePath, resolveXopcBinPath } from "./paths.js";
6
5
  import { TelegramAccountConfigSchema, TelegramConfigSchema, TelegramGroupConfigSchema, TelegramTopicConfigSchema } from "../../extensions/telegram/src/config-schema.js";
7
6
  import { WeixinAccountConfigSchema, WeixinConfigSchema } from "../../extensions/weixin/src/config-schema.js";
8
7
  import { AgentConfigSchema, AgentDefaultsSchema, AgentModelRefSchema, AgentsConfigSchema, BindingMatchSchema, BindingRuleSchema, BindingsConfigSchema, ChannelsConfigSchema, ConfigSchema, CronConfigSchema, ExtensionSecurityConfigSchema, ExtensionSlotsConfigSchema, ExtensionsConfigSchema, GatewayAuthRateLimitSchema, GatewayAuthSchema, GatewayConfigSchema, HeartbeatConfigSchema, ModelsDevConfigSchema, STTConfigSchema, STTFallbackConfigSchema, STTProviderConfigSchema, SearchProviderEntrySchema, SessionConfigSchema, SessionDmScopeSchema, SessionStorageConfigSchema, TTSConfigSchema, TTSEdgeConfigSchema, TTSFallbackConfigSchema, TTSModelOverridesConfigSchema, TTSProviderConfigSchema, TTSSummarizationConfigSchema, ToolsConfigSchema, UpdateAutoConfigSchema, UpdateConfigSchema, WebSearchConfigSchema, WebToolsConfigSchema, getAgentDefaultModelRef, getWorkspacePath, init_schema, parseModelRef } from "./schema.js";
9
- import { init_loader, loadConfig, registerChannelConfigValidator, saveConfig } from "./loader.js";
10
8
  import { applyConfigOverrides, getConfigOverrides, resetConfigOverrides, setConfigOverride, unsetConfigOverride } from "./runtime-overrides.js";
11
9
  import { extractProfileAgentId, resolveEffectiveAgentProfile, resolveEffectiveAgentProfileForSession } from "./agent-profile.js";
10
+ import { FILENAMES, WORKSPACE_FILES, existsSync, init_paths, resolveAgentAuthProfilesPath, resolveAgentCredentialsDir, resolveAgentDir, resolveAgentHomeDir, resolveAgentMetadataPath, resolveAuthProfilesPath, resolveBinDir, resolveBundledExtensionsDir, resolveBundledSkillsDir, resolveConfigPath, resolveCredentialsDir, resolveCronDir, resolveCronJobsPath, resolveCronLogPath, resolveCronLogsDir, resolveCronRunsDir, resolveExtensionSdkPath, resolveExtensionsDir, resolveExtensionsLockPath, resolveInboxDir, resolveInboxMessagePath, resolveInboxPendingDir, resolveInboxProcessedDir, resolveLogPath, resolveLogsDir, resolveMemoryDir, resolveMemoryPath, resolveModelsJsonPath, resolveNodeBinDir, resolveNodeBinPath, resolveNodeToolsDir, resolveNpmBinPath, resolveOAuthPath, resolvePidPath, resolveSessionTranscriptPath, resolveSessionTranscriptPathInDir, resolveSessionsArchiveDir, resolveSessionsDir, resolveSessionsIndexPath, resolveSkillPath, resolveSkillsCachePath, resolveSkillsDir, resolveSkillsLockPath, resolveSocketPath, resolveStatusPath, resolveToolsDir, resolveWorkspaceExtensionsDir, resolveWorkspaceFile, resolveWorkspaceStateDir, resolveWorkspaceStatePath, resolveXopcBinPath } from "./paths.js";
11
+ import { init_loader, loadConfig, registerChannelConfigValidator, saveConfig } from "./loader.js";
12
12
  import { clearConfigValueCache, getAllowedCommands, getCacheStats, init_resolve_config_value, resolveConfigValue, resolveHeaders, testApiKeyResolution } from "./resolve-config-value.js";
13
13
  import { CustomModelSchema, ModelOverrideSchema, ModelsJsonSchema, OpenAICompatSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenRouterRoutingSchema, ProviderConfigSchema, VercelGatewayRoutingSchema, getDefaultModelValues, init_models_json, loadModelsJson, modelsJsonExists, saveModelsJson, validateModelsConfig } from "./models-json.js";
14
14
  import { formatThinkingLevels, listThinkingLevels, normalizeElevatedMode, normalizeReasoningLevel, normalizeThinkLevel, normalizeVerboseLevel, thinkLevelToNumber } from "../agent/transcript/thinking-types.js";
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ import { ConfigSchema, init_schema } from "./schema.js";
2
3
  import { createLogger } from "../utils/logger/index.js";
3
4
  import { init_logger } from "../utils/logger.js";
4
5
  import { init_paths, resolveConfigPath } from "./paths.js";
5
- import { ConfigSchema, init_schema } from "./schema.js";
6
6
  import { dirname } from "path";
7
7
  import { existsSync, mkdirSync, promises, readFileSync } from "fs";
8
8
  import { config } from "dotenv";
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { init_paths, resolveModelsJsonPath } from "./paths.js";
3
+ import { z } from "zod";
3
4
  import { dirname } from "path";
4
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
- import { z } from "zod";
6
6
  //#region src/config/models-json.ts
7
7
  /**
8
8
  * Models.json configuration types and schema
@@ -13,3 +13,7 @@ export declare const ENV_VARS: {
13
13
  };
14
14
  export declare function resolveHomeDir(env?: NodeJS.ProcessEnv): string;
15
15
  export declare function resolveStateDir(env?: NodeJS.ProcessEnv): string;
16
+ /** Persisted npm update check throttle / notification state (`~/.xopc/update-check.json`). */
17
+ export declare function resolveUpdateCheckStatePath(env?: NodeJS.ProcessEnv): string;
18
+ /** Cross-process lock for one-click / CLI / auto npm updates (`~/.xopc/update.lock`). */
19
+ export declare function resolveUpdateLockPath(env?: NodeJS.ProcessEnv): string;
@@ -12,6 +12,14 @@ function resolveStateDir(env = process.env) {
12
12
  if (profile && profile !== "default") return join(home, `.xopc-${profile}`);
13
13
  return join(home, ".xopc");
14
14
  }
15
+ /** Persisted npm update check throttle / notification state (`~/.xopc/update-check.json`). */
16
+ function resolveUpdateCheckStatePath(env = process.env) {
17
+ return join(resolveStateDir(env), "update-check.json");
18
+ }
19
+ /** Cross-process lock for one-click / CLI / auto npm updates (`~/.xopc/update.lock`). */
20
+ function resolveUpdateLockPath(env = process.env) {
21
+ return join(resolveStateDir(env), "update.lock");
22
+ }
15
23
  var ENV_VARS;
16
24
  var init_paths_state = __esmMin((() => {
17
25
  ENV_VARS = {
@@ -30,6 +38,6 @@ var init_paths_state = __esmMin((() => {
30
38
  }));
31
39
  //#endregion
32
40
  init_paths_state();
33
- export { ENV_VARS, init_paths_state, resolveHomeDir, resolveStateDir };
41
+ export { ENV_VARS, init_paths_state, resolveHomeDir, resolveStateDir, resolveUpdateCheckStatePath, resolveUpdateLockPath };
34
42
 
35
43
  //# sourceMappingURL=paths-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths-state.js","names":[],"sources":["../../../src/config/paths-state.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport const ENV_VARS = {\n STATE_DIR: 'XOPC_STATE_DIR',\n PROFILE: 'XOPC_PROFILE',\n HOME: 'XOPC_HOME',\n CONFIG_PATH: 'XOPC_CONFIG_PATH',\n CREDENTIALS_DIR: 'XOPC_CREDENTIALS_DIR',\n LOG_LEVEL: 'XOPC_LOG_LEVEL',\n LOG_DIR: 'XOPC_LOG_DIR',\n LOG_CONSOLE: 'XOPC_LOG_CONSOLE',\n LOG_FILE: 'XOPC_LOG_FILE',\n LOG_RETENTION_DAYS: 'XOPC_LOG_RETENTION_DAYS',\n PRETTY_LOGS: 'XOPC_PRETTY_LOGS',\n} as const;\n\nexport function resolveHomeDir(env: NodeJS.ProcessEnv = process.env): string {\n return env[ENV_VARS.HOME] || homedir();\n}\n\nexport function resolveStateDir(env: NodeJS.ProcessEnv = process.env): string {\n if (env[ENV_VARS.STATE_DIR]) {\n return env[ENV_VARS.STATE_DIR]!;\n }\n\n const profile = env[ENV_VARS.PROFILE];\n const home = resolveHomeDir(env);\n\n if (profile && profile !== 'default') {\n return join(home, `.xopc-${profile}`);\n }\n\n return join(home, '.xopc');\n}\n"],"mappings":";;;;AAiBA,SAAgB,eAAe,MAAyB,QAAQ,KAAa;AAC3E,QAAO,IAAI,SAAS,SAAS,SAAS;;AAGxC,SAAgB,gBAAgB,MAAyB,QAAQ,KAAa;AAC5E,KAAI,IAAI,SAAS,WACf,QAAO,IAAI,SAAS;CAGtB,MAAM,UAAU,IAAI,SAAS;CAC7B,MAAM,OAAO,eAAe,IAAI;AAEhC,KAAI,WAAW,YAAY,UACzB,QAAO,KAAK,MAAM,SAAS,UAAU;AAGvC,QAAO,KAAK,MAAM,QAAQ;;;;AA9Bf,YAAW;EACtB,WAAW;EACX,SAAS;EACT,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,SAAS;EACT,aAAa;EACb,UAAU;EACV,oBAAoB;EACpB,aAAa;EACd"}
1
+ {"version":3,"file":"paths-state.js","names":[],"sources":["../../../src/config/paths-state.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport const ENV_VARS = {\n STATE_DIR: 'XOPC_STATE_DIR',\n PROFILE: 'XOPC_PROFILE',\n HOME: 'XOPC_HOME',\n CONFIG_PATH: 'XOPC_CONFIG_PATH',\n CREDENTIALS_DIR: 'XOPC_CREDENTIALS_DIR',\n LOG_LEVEL: 'XOPC_LOG_LEVEL',\n LOG_DIR: 'XOPC_LOG_DIR',\n LOG_CONSOLE: 'XOPC_LOG_CONSOLE',\n LOG_FILE: 'XOPC_LOG_FILE',\n LOG_RETENTION_DAYS: 'XOPC_LOG_RETENTION_DAYS',\n PRETTY_LOGS: 'XOPC_PRETTY_LOGS',\n} as const;\n\nexport function resolveHomeDir(env: NodeJS.ProcessEnv = process.env): string {\n return env[ENV_VARS.HOME] || homedir();\n}\n\nexport function resolveStateDir(env: NodeJS.ProcessEnv = process.env): string {\n if (env[ENV_VARS.STATE_DIR]) {\n return env[ENV_VARS.STATE_DIR]!;\n }\n\n const profile = env[ENV_VARS.PROFILE];\n const home = resolveHomeDir(env);\n\n if (profile && profile !== 'default') {\n return join(home, `.xopc-${profile}`);\n }\n\n return join(home, '.xopc');\n}\n\n/** Persisted npm update check throttle / notification state (`~/.xopc/update-check.json`). */\nexport function resolveUpdateCheckStatePath(env: NodeJS.ProcessEnv = process.env): string {\n return join(resolveStateDir(env), 'update-check.json');\n}\n\n/** Cross-process lock for one-click / CLI / auto npm updates (`~/.xopc/update.lock`). */\nexport function resolveUpdateLockPath(env: NodeJS.ProcessEnv = process.env): string {\n return join(resolveStateDir(env), 'update.lock');\n}\n"],"mappings":";;;;AAiBA,SAAgB,eAAe,MAAyB,QAAQ,KAAa;AAC3E,QAAO,IAAI,SAAS,SAAS,SAAS;;AAGxC,SAAgB,gBAAgB,MAAyB,QAAQ,KAAa;AAC5E,KAAI,IAAI,SAAS,WACf,QAAO,IAAI,SAAS;CAGtB,MAAM,UAAU,IAAI,SAAS;CAC7B,MAAM,OAAO,eAAe,IAAI;AAEhC,KAAI,WAAW,YAAY,UACzB,QAAO,KAAK,MAAM,SAAS,UAAU;AAGvC,QAAO,KAAK,MAAM,QAAQ;;;AAI5B,SAAgB,4BAA4B,MAAyB,QAAQ,KAAa;AACxF,QAAO,KAAK,gBAAgB,IAAI,EAAE,oBAAoB;;;AAIxD,SAAgB,sBAAsB,MAAyB,QAAQ,KAAa;AAClF,QAAO,KAAK,gBAAgB,IAAI,EAAE,cAAc;;;;AAxCrC,YAAW;EACtB,WAAW;EACX,SAAS;EACT,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,SAAS;EACT,aAAa;EACb,UAAU;EACV,oBAAoB;EACpB,aAAa;EACd"}
@@ -1,9 +1,9 @@
1
+ import { ENV_VARS, init_paths_state, resolveHomeDir } from "./paths-state.js";
1
2
  import { createLogger } from "../utils/logger/index.js";
2
3
  import { init_logger } from "../utils/logger.js";
3
- import { ENV_VARS, init_paths_state, resolveHomeDir } from "./paths-state.js";
4
- import { mkdir, readdir, rm, stat } from "fs/promises";
5
4
  import { join } from "path";
6
5
  import { existsSync } from "fs";
6
+ import { mkdir, readdir, rm, stat } from "fs/promises";
7
7
  //#region src/config/profile.ts
8
8
  init_logger();
9
9
  init_paths_state();
@@ -24,6 +24,8 @@ export interface ReloadCallbacks {
24
24
  onHeartbeatReload?: ReloadCallback;
25
25
  onToolsReload?: ReloadCallback;
26
26
  onWebSearchReload?: ReloadCallback;
27
+ /** All `extensions.*` hot paths in one batch (deduplicated in applyReload). */
28
+ onExtensionsReload?: (newConfig: Config, changedPaths: string[]) => void | Promise<void>;
27
29
  onFullRestart?: ReloadCallback;
28
30
  }
29
31
  /**
@@ -147,7 +147,15 @@ var ConfigHotReloader = class {
147
147
  if (this.callbacks.onFullRestart) this.callbacks.onFullRestart(newConfig);
148
148
  return;
149
149
  }
150
- if (plan.requiresHotReload) for (const path of plan.hotPaths) await this.applyHotPath(path, newConfig);
150
+ if (!plan.requiresHotReload) {
151
+ logger.info({ plan }, "Config hot reload completed");
152
+ return;
153
+ }
154
+ const isExtensionPath = (p) => p === "extensions" || p.startsWith("extensions.");
155
+ const extensionPaths = plan.hotPaths.filter(isExtensionPath);
156
+ const otherPaths = plan.hotPaths.filter((p) => !isExtensionPath(p));
157
+ for (const path of otherPaths) await this.applyHotPath(path, newConfig);
158
+ if (extensionPaths.length > 0 && this.callbacks.onExtensionsReload) await Promise.resolve(this.callbacks.onExtensionsReload(newConfig, extensionPaths));
151
159
  logger.info({ plan }, "Config hot reload completed");
152
160
  }
153
161
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"reload.js","names":[],"sources":["../../../src/config/reload.ts"],"sourcesContent":["/**\n * Configuration hot reload manager\n */\n\nimport { watch, type FSWatcher } from 'fs';\nimport { loadConfig } from './loader.js';\nimport type { Config } from './schema.js';\nimport { diffConfigPaths } from './diff.js';\nimport { buildReloadPlan, type ReloadPlan } from './rules.js';\nimport { resolveModelsJsonPath } from './paths.js';\nimport { logger as log } from '../utils/logger.js';\n\nexport interface HotReloadConfig {\n debounceMs: number;\n enabled: boolean;\n}\n\nexport interface ReloadResult {\n success: boolean;\n plan?: ReloadPlan;\n error?: string;\n}\n\n/**\n * Callback types for different reload actions\n */\nexport type ReloadCallback = (newConfig: Config) => void | Promise<void>;\n\nexport interface ReloadCallbacks {\n onModelsReload?: ReloadCallback;\n onAgentDefaultsReload?: ReloadCallback;\n onChannelsReload?: ReloadCallback;\n onCronReload?: ReloadCallback;\n onHeartbeatReload?: ReloadCallback;\n onToolsReload?: ReloadCallback;\n onWebSearchReload?: ReloadCallback;\n onFullRestart?: ReloadCallback;\n}\n\n/**\n * Configuration hot reload manager\n */\nexport class ConfigHotReloader {\n private configPath: string;\n private callbacks: ReloadCallbacks;\n private watcher: FSWatcher | null = null;\n private modelsJsonWatcher: FSWatcher | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private currentConfig: Config;\n private debounceMs: number;\n private enabled: boolean;\n\n constructor(\n configPath: string,\n initialConfig: Config,\n callbacks: ReloadCallbacks,\n options: HotReloadConfig = { debounceMs: 300, enabled: true }\n ) {\n this.configPath = configPath;\n this.currentConfig = initialConfig;\n this.callbacks = callbacks;\n this.debounceMs = options.debounceMs;\n this.enabled = options.enabled;\n }\n\n /**\n * Start watching config file for changes\n */\n start(): void {\n if (!this.enabled) {\n log.info('Config hot reload disabled');\n return;\n }\n\n try {\n this.watcher = watch(this.configPath, (eventType) => {\n if (eventType === 'change') {\n this.scheduleReload();\n }\n });\n log.info({ path: this.configPath }, 'Config hot reload enabled');\n } catch (err) {\n log.error({ err }, 'Failed to setup config watcher');\n }\n\n this.startModelsJsonWatcher();\n }\n\n /**\n * Stop watching config file\n */\n async stop(): Promise<void> {\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n if (this.modelsJsonWatcher) {\n this.modelsJsonWatcher.close();\n this.modelsJsonWatcher = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n log.info('Config hot reload stopped');\n }\n\n /**\n * Schedule a reload with debounce\n */\n private scheduleReload(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n void this.reload();\n }, this.debounceMs);\n }\n\n /**\n * Watch models.json independently so direct file edits are picked up without restarting.\n * The watcher is best-effort: if the file does not exist yet, it will not be watched.\n */\n private startModelsJsonWatcher(): void {\n const modelsJsonPath = resolveModelsJsonPath();\n try {\n this.modelsJsonWatcher = watch(modelsJsonPath, (eventType) => {\n if (eventType === 'change') {\n this.scheduleModelsJsonReload();\n }\n });\n log.info({ path: modelsJsonPath }, 'models.json hot reload enabled');\n } catch {\n log.debug({ path: modelsJsonPath }, 'models.json not found, skipping watcher');\n }\n }\n\n /**\n * Debounced handler for models.json changes.\n * Calls onModelsReload with the current config so the registry is refreshed.\n */\n private scheduleModelsJsonReload(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n log.info('models.json changed on disk — refreshing ModelRegistry');\n if (this.callbacks.onModelsReload) {\n void Promise.resolve(this.callbacks.onModelsReload(this.currentConfig));\n }\n }, this.debounceMs);\n }\n\n /**\n * Reload configuration and apply changes\n */\n async reload(): Promise<ReloadResult> {\n try {\n log.info('Reloading configuration...');\n \n // Load new config\n const newConfig = loadConfig(this.configPath);\n \n // Diff with current config\n const changedPaths = diffConfigPaths(this.currentConfig, newConfig);\n \n if (changedPaths.length === 0) {\n log.debug('No config changes detected');\n return { success: true };\n }\n \n log.info({ changedPaths }, 'Config changes detected');\n \n // Build reload plan\n const plan = buildReloadPlan(changedPaths);\n \n // Apply changes based on plan\n await this.applyReload(plan, newConfig);\n \n // Update current config\n this.currentConfig = newConfig;\n \n return { success: true, plan };\n \n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ err, configPath: this.configPath, errorMessage: error }, `Config hot reload failed: ${error}`);\n return { success: false, error };\n }\n }\n\n /**\n * Apply reload based on plan\n */\n private async applyReload(plan: ReloadPlan, newConfig: Config): Promise<void> {\n // Handle restart-required changes first\n if (plan.requiresRestart) {\n log.info(\n { restartPaths: plan.restartPaths },\n 'Config changes require gateway restart'\n );\n \n if (this.callbacks.onFullRestart) {\n this.callbacks.onFullRestart(newConfig);\n }\n return;\n }\n\n // Handle hot reload changes\n if (plan.requiresHotReload) {\n for (const path of plan.hotPaths) {\n await this.applyHotPath(path, newConfig);\n }\n }\n\n log.info({ plan }, 'Config hot reload completed');\n }\n\n /**\n * Apply a single hot-reloadable path\n */\n private async applyHotPath(path: string, newConfig: Config): Promise<void> {\n if (path.startsWith('models.')) {\n if (this.callbacks.onModelsReload) {\n await Promise.resolve(this.callbacks.onModelsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('agents.defaults.')) {\n if (this.callbacks.onAgentDefaultsReload) {\n await Promise.resolve(this.callbacks.onAgentDefaultsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('channels.')) {\n if (this.callbacks.onChannelsReload) {\n await Promise.resolve(this.callbacks.onChannelsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('cron.')) {\n if (this.callbacks.onCronReload) {\n await Promise.resolve(this.callbacks.onCronReload(newConfig));\n }\n return;\n }\n\n if (path === 'gateway.heartbeat' || path.startsWith('gateway.heartbeat.')) {\n if (this.callbacks.onHeartbeatReload) {\n await Promise.resolve(this.callbacks.onHeartbeatReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('tools.')) {\n if (this.callbacks.onToolsReload) {\n await Promise.resolve(this.callbacks.onToolsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('webSearch.') || path.startsWith('webTools.')) {\n if (this.callbacks.onWebSearchReload) {\n await Promise.resolve(this.callbacks.onWebSearchReload(newConfig));\n }\n return;\n }\n\n log.debug({ path }, 'No handler for hot reload path');\n }\n\n /**\n * Manually trigger a reload\n */\n async triggerReload(): Promise<ReloadResult> {\n return this.reload();\n }\n\n /**\n * Align diff baseline with a freshly loaded config (e.g. Weixin QR wrote token files but JSON unchanged).\n */\n syncCurrentConfig(config: Config): void {\n this.currentConfig = config;\n }\n\n /**\n * Get current config\n */\n getConfig(): Config {\n return this.currentConfig;\n }\n\n /**\n * Check if hot reload is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n}\n\nexport { diffConfigPaths } from './diff.js';\nexport { buildReloadPlan, matchReloadRule, BASE_RELOAD_RULES } from './rules.js';\n"],"mappings":"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"}
1
+ {"version":3,"file":"reload.js","names":[],"sources":["../../../src/config/reload.ts"],"sourcesContent":["/**\n * Configuration hot reload manager\n */\n\nimport { watch, type FSWatcher } from 'fs';\nimport { loadConfig } from './loader.js';\nimport type { Config } from './schema.js';\nimport { diffConfigPaths } from './diff.js';\nimport { buildReloadPlan, type ReloadPlan } from './rules.js';\nimport { resolveModelsJsonPath } from './paths.js';\nimport { logger as log } from '../utils/logger.js';\n\nexport interface HotReloadConfig {\n debounceMs: number;\n enabled: boolean;\n}\n\nexport interface ReloadResult {\n success: boolean;\n plan?: ReloadPlan;\n error?: string;\n}\n\n/**\n * Callback types for different reload actions\n */\nexport type ReloadCallback = (newConfig: Config) => void | Promise<void>;\n\nexport interface ReloadCallbacks {\n onModelsReload?: ReloadCallback;\n onAgentDefaultsReload?: ReloadCallback;\n onChannelsReload?: ReloadCallback;\n onCronReload?: ReloadCallback;\n onHeartbeatReload?: ReloadCallback;\n onToolsReload?: ReloadCallback;\n onWebSearchReload?: ReloadCallback;\n /** All `extensions.*` hot paths in one batch (deduplicated in applyReload). */\n onExtensionsReload?: (\n newConfig: Config,\n changedPaths: string[],\n ) => void | Promise<void>;\n onFullRestart?: ReloadCallback;\n}\n\n/**\n * Configuration hot reload manager\n */\nexport class ConfigHotReloader {\n private configPath: string;\n private callbacks: ReloadCallbacks;\n private watcher: FSWatcher | null = null;\n private modelsJsonWatcher: FSWatcher | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private currentConfig: Config;\n private debounceMs: number;\n private enabled: boolean;\n\n constructor(\n configPath: string,\n initialConfig: Config,\n callbacks: ReloadCallbacks,\n options: HotReloadConfig = { debounceMs: 300, enabled: true }\n ) {\n this.configPath = configPath;\n this.currentConfig = initialConfig;\n this.callbacks = callbacks;\n this.debounceMs = options.debounceMs;\n this.enabled = options.enabled;\n }\n\n /**\n * Start watching config file for changes\n */\n start(): void {\n if (!this.enabled) {\n log.info('Config hot reload disabled');\n return;\n }\n\n try {\n this.watcher = watch(this.configPath, (eventType) => {\n if (eventType === 'change') {\n this.scheduleReload();\n }\n });\n log.info({ path: this.configPath }, 'Config hot reload enabled');\n } catch (err) {\n log.error({ err }, 'Failed to setup config watcher');\n }\n\n this.startModelsJsonWatcher();\n }\n\n /**\n * Stop watching config file\n */\n async stop(): Promise<void> {\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n if (this.modelsJsonWatcher) {\n this.modelsJsonWatcher.close();\n this.modelsJsonWatcher = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n log.info('Config hot reload stopped');\n }\n\n /**\n * Schedule a reload with debounce\n */\n private scheduleReload(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n void this.reload();\n }, this.debounceMs);\n }\n\n /**\n * Watch models.json independently so direct file edits are picked up without restarting.\n * The watcher is best-effort: if the file does not exist yet, it will not be watched.\n */\n private startModelsJsonWatcher(): void {\n const modelsJsonPath = resolveModelsJsonPath();\n try {\n this.modelsJsonWatcher = watch(modelsJsonPath, (eventType) => {\n if (eventType === 'change') {\n this.scheduleModelsJsonReload();\n }\n });\n log.info({ path: modelsJsonPath }, 'models.json hot reload enabled');\n } catch {\n log.debug({ path: modelsJsonPath }, 'models.json not found, skipping watcher');\n }\n }\n\n /**\n * Debounced handler for models.json changes.\n * Calls onModelsReload with the current config so the registry is refreshed.\n */\n private scheduleModelsJsonReload(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n log.info('models.json changed on disk — refreshing ModelRegistry');\n if (this.callbacks.onModelsReload) {\n void Promise.resolve(this.callbacks.onModelsReload(this.currentConfig));\n }\n }, this.debounceMs);\n }\n\n /**\n * Reload configuration and apply changes\n */\n async reload(): Promise<ReloadResult> {\n try {\n log.info('Reloading configuration...');\n \n // Load new config\n const newConfig = loadConfig(this.configPath);\n \n // Diff with current config\n const changedPaths = diffConfigPaths(this.currentConfig, newConfig);\n \n if (changedPaths.length === 0) {\n log.debug('No config changes detected');\n return { success: true };\n }\n \n log.info({ changedPaths }, 'Config changes detected');\n \n // Build reload plan\n const plan = buildReloadPlan(changedPaths);\n \n // Apply changes based on plan\n await this.applyReload(plan, newConfig);\n \n // Update current config\n this.currentConfig = newConfig;\n \n return { success: true, plan };\n \n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ err, configPath: this.configPath, errorMessage: error }, `Config hot reload failed: ${error}`);\n return { success: false, error };\n }\n }\n\n /**\n * Apply reload based on plan\n */\n private async applyReload(plan: ReloadPlan, newConfig: Config): Promise<void> {\n // Handle restart-required changes first\n if (plan.requiresRestart) {\n log.info(\n { restartPaths: plan.restartPaths },\n 'Config changes require gateway restart'\n );\n \n if (this.callbacks.onFullRestart) {\n this.callbacks.onFullRestart(newConfig);\n }\n return;\n }\n\n if (!plan.requiresHotReload) {\n log.info({ plan }, 'Config hot reload completed');\n return;\n }\n\n const isExtensionPath = (p: string) => p === 'extensions' || p.startsWith('extensions.');\n const extensionPaths = plan.hotPaths.filter(isExtensionPath);\n const otherPaths = plan.hotPaths.filter((p) => !isExtensionPath(p));\n\n for (const path of otherPaths) {\n await this.applyHotPath(path, newConfig);\n }\n\n if (extensionPaths.length > 0 && this.callbacks.onExtensionsReload) {\n await Promise.resolve(this.callbacks.onExtensionsReload(newConfig, extensionPaths));\n }\n\n log.info({ plan }, 'Config hot reload completed');\n }\n\n /**\n * Apply a single hot-reloadable path\n */\n private async applyHotPath(path: string, newConfig: Config): Promise<void> {\n if (path.startsWith('models.')) {\n if (this.callbacks.onModelsReload) {\n await Promise.resolve(this.callbacks.onModelsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('agents.defaults.')) {\n if (this.callbacks.onAgentDefaultsReload) {\n await Promise.resolve(this.callbacks.onAgentDefaultsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('channels.')) {\n if (this.callbacks.onChannelsReload) {\n await Promise.resolve(this.callbacks.onChannelsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('cron.')) {\n if (this.callbacks.onCronReload) {\n await Promise.resolve(this.callbacks.onCronReload(newConfig));\n }\n return;\n }\n\n if (path === 'gateway.heartbeat' || path.startsWith('gateway.heartbeat.')) {\n if (this.callbacks.onHeartbeatReload) {\n await Promise.resolve(this.callbacks.onHeartbeatReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('tools.')) {\n if (this.callbacks.onToolsReload) {\n await Promise.resolve(this.callbacks.onToolsReload(newConfig));\n }\n return;\n }\n\n if (path.startsWith('webSearch.') || path.startsWith('webTools.')) {\n if (this.callbacks.onWebSearchReload) {\n await Promise.resolve(this.callbacks.onWebSearchReload(newConfig));\n }\n return;\n }\n\n log.debug({ path }, 'No handler for hot reload path');\n }\n\n /**\n * Manually trigger a reload\n */\n async triggerReload(): Promise<ReloadResult> {\n return this.reload();\n }\n\n /**\n * Align diff baseline with a freshly loaded config (e.g. Weixin QR wrote token files but JSON unchanged).\n */\n syncCurrentConfig(config: Config): void {\n this.currentConfig = config;\n }\n\n /**\n * Get current config\n */\n getConfig(): Config {\n return this.currentConfig;\n }\n\n /**\n * Check if hot reload is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n}\n\nexport { diffConfigPaths } from './diff.js';\nexport { buildReloadPlan, matchReloadRule, BASE_RELOAD_RULES } from './rules.js';\n"],"mappings":"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"}
@@ -136,9 +136,19 @@ const BASE_RELOAD_RULES = [
136
136
  description: "Web tools settings"
137
137
  },
138
138
  {
139
- prefix: "extensions",
139
+ prefix: "extensions.enabled",
140
+ kind: "restart",
141
+ description: "Extension enable list (requires restart)"
142
+ },
143
+ {
144
+ prefix: "extensions.disabled",
140
145
  kind: "restart",
141
- description: "Extension configuration"
146
+ description: "Extension disable list (requires restart)"
147
+ },
148
+ {
149
+ prefix: "extensions",
150
+ kind: "hot",
151
+ description: "Extension configuration (hot-reloaded via extension handlers)"
142
152
  },
143
153
  {
144
154
  prefix: "tools",
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","names":[],"sources":["../../../src/config/rules.ts"],"sourcesContent":["/**\n * Configuration reload rules\n * \n * Defines how different config paths are handled:\n * - hot: Apply changes immediately without restart\n * - restart: Require gateway restart\n * - none: Ignore changes (no action needed)\n */\n\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport { bundledChannelPlugins } from '../generated/bundled-channel-plugins.js';\nimport { listChannelPlugins } from '../channels/plugins/registry.js';\n\nexport type ReloadKind = 'hot' | 'restart' | 'none';\n\nexport interface ReloadRule {\n prefix: string;\n kind: ReloadKind;\n description?: string;\n}\n\nexport interface ReloadPlan {\n changedPaths: string[];\n hotPaths: string[];\n restartPaths: string[];\n noopPaths: string[];\n requiresRestart: boolean;\n requiresHotReload: boolean;\n}\n\n/**\n * Base reload rules for config paths\n */\nexport const BASE_RELOAD_RULES: ReloadRule[] = [\n // Models config - hot reload\n { prefix: 'models.providers', kind: 'hot', description: 'Model provider API keys, base URLs' },\n { prefix: 'models.mode', kind: 'hot', description: 'Model merge mode' },\n \n // Agent defaults - hot reload\n { prefix: 'agents.defaults.model', kind: 'hot', description: 'Model configuration' },\n { prefix: 'agents.defaults.maxTaskDurationMs', kind: 'hot', description: 'Per-turn wall-clock timeout (ms)' },\n { prefix: 'agents.defaults.maxTokens', kind: 'hot', description: 'Max tokens' },\n { prefix: 'agents.defaults.temperature', kind: 'hot', description: 'Temperature' },\n { prefix: 'agents.defaults.maxToolIterations', kind: 'hot', description: 'Max tool iterations' },\n { prefix: 'agents.defaults.compaction', kind: 'hot', description: 'Compaction settings' },\n { prefix: 'agents.defaults.pruning', kind: 'hot', description: 'Pruning settings' },\n { prefix: 'agents.defaults.webExtract', kind: 'hot', description: 'Web extract model and limits' },\n { prefix: 'agents.defaults.browser', kind: 'hot', description: 'Browser automation (Playwright) tools' },\n { prefix: 'agents.defaults.delegate', kind: 'hot', description: 'delegate_task sub-agent tool' },\n { prefix: 'agents.defaults.executeCode', kind: 'hot', description: 'execute_code sandbox tool' },\n {\n prefix: 'agents.defaults.backgroundReview',\n kind: 'hot',\n description: 'Post-turn memory/skill nudge + background review',\n },\n { prefix: 'agents.defaults.workspace', kind: 'none', description: 'Workspace path - no runtime effect' },\n \n // Gateway - restart required\n { prefix: 'gateway.host', kind: 'restart', description: 'Host address' },\n { prefix: 'gateway.port', kind: 'restart', description: 'Port number' },\n { prefix: 'gateway.auth', kind: 'restart', description: 'Authentication settings' },\n { prefix: 'gateway.cors', kind: 'restart', description: 'CORS settings' },\n {\n prefix: 'gateway.skillsStoreBaseUrl',\n kind: 'restart',\n description: 'Skills marketplace API base URL',\n },\n { prefix: 'gateway.enableHotReload', kind: 'hot', description: 'Hot reload toggle' },\n \n // Channels - hot reload (channel-specific prefixes are registered by channel plugins)\n { prefix: 'channels', kind: 'hot', description: 'Any channel subtree (e.g. future extensions)' },\n \n // Cron - hot reload\n { prefix: 'cron', kind: 'hot', description: 'Scheduled tasks' },\n \n // Heartbeat lives under gateway.heartbeat in config JSON (not top-level `heartbeat`)\n { prefix: 'gateway.heartbeat', kind: 'hot', description: 'Heartbeat settings' },\n \n // Web search - hot reload\n { prefix: 'webSearch', kind: 'hot', description: 'Web search settings' },\n { prefix: 'webTools', kind: 'hot', description: 'Web tools settings' },\n \n // Extensions - restart required\n { prefix: 'extensions', kind: 'restart', description: 'Extension configuration' },\n \n // Tools - hot reload (for tool-specific settings)\n { prefix: 'tools', kind: 'hot', description: 'Tools configuration' },\n];\n\nfunction pluginsForReloadRules(): ChannelPlugin[] {\n const listed = listChannelPlugins();\n return listed.length > 0 ? [...listed] : [...bundledChannelPlugins];\n}\n\nfunction getChannelReloadRules(): ReloadRule[] {\n return pluginsForReloadRules()\n .filter((plugin) => plugin.reload?.configPrefixes?.length)\n .flatMap((plugin) =>\n plugin.reload!.configPrefixes.map((prefix) => ({\n prefix,\n kind: 'hot' as const,\n description: `${plugin.meta.label} settings`,\n })),\n );\n}\n\nfunction mergedReloadRules(): ReloadRule[] {\n return [...BASE_RELOAD_RULES, ...getChannelReloadRules()];\n}\n\n/**\n * Find matching rule for a config path\n */\nexport function matchReloadRule(path: string): ReloadRule | null {\n const merged = mergedReloadRules();\n const exact = merged.find((r) => r.prefix === path);\n if (exact) return exact;\n const sorted = [...merged].sort((a, b) => b.prefix.length - a.prefix.length);\n for (const rule of sorted) {\n if (path === rule.prefix || path.startsWith(`${rule.prefix}.`)) {\n return rule;\n }\n }\n return null;\n}\n\n/**\n * Build reload plan from changed paths\n */\nexport function buildReloadPlan(changedPaths: string[]): ReloadPlan {\n const plan: ReloadPlan = {\n changedPaths,\n hotPaths: [],\n restartPaths: [],\n noopPaths: [],\n requiresRestart: false,\n requiresHotReload: false,\n };\n\n for (const path of changedPaths) {\n const rule = matchReloadRule(path);\n \n if (!rule) {\n // No rule matched - default to restart for safety\n plan.restartPaths.push(path);\n plan.requiresRestart = true;\n continue;\n }\n\n switch (rule.kind) {\n case 'hot':\n plan.hotPaths.push(path);\n plan.requiresHotReload = true;\n break;\n case 'restart':\n plan.restartPaths.push(path);\n plan.requiresRestart = true;\n break;\n case 'none':\n plan.noopPaths.push(path);\n break;\n }\n }\n\n return plan;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","names":[],"sources":["../../../src/config/rules.ts"],"sourcesContent":["/**\n * Configuration reload rules\n * \n * Defines how different config paths are handled:\n * - hot: Apply changes immediately without restart\n * - restart: Require gateway restart\n * - none: Ignore changes (no action needed)\n */\n\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport { bundledChannelPlugins } from '../generated/bundled-channel-plugins.js';\nimport { listChannelPlugins } from '../channels/plugins/registry.js';\n\nexport type ReloadKind = 'hot' | 'restart' | 'none';\n\nexport interface ReloadRule {\n prefix: string;\n kind: ReloadKind;\n description?: string;\n}\n\nexport interface ReloadPlan {\n changedPaths: string[];\n hotPaths: string[];\n restartPaths: string[];\n noopPaths: string[];\n requiresRestart: boolean;\n requiresHotReload: boolean;\n}\n\n/**\n * Base reload rules for config paths\n */\nexport const BASE_RELOAD_RULES: ReloadRule[] = [\n // Models config - hot reload\n { prefix: 'models.providers', kind: 'hot', description: 'Model provider API keys, base URLs' },\n { prefix: 'models.mode', kind: 'hot', description: 'Model merge mode' },\n \n // Agent defaults - hot reload\n { prefix: 'agents.defaults.model', kind: 'hot', description: 'Model configuration' },\n { prefix: 'agents.defaults.maxTaskDurationMs', kind: 'hot', description: 'Per-turn wall-clock timeout (ms)' },\n { prefix: 'agents.defaults.maxTokens', kind: 'hot', description: 'Max tokens' },\n { prefix: 'agents.defaults.temperature', kind: 'hot', description: 'Temperature' },\n { prefix: 'agents.defaults.maxToolIterations', kind: 'hot', description: 'Max tool iterations' },\n { prefix: 'agents.defaults.compaction', kind: 'hot', description: 'Compaction settings' },\n { prefix: 'agents.defaults.pruning', kind: 'hot', description: 'Pruning settings' },\n { prefix: 'agents.defaults.webExtract', kind: 'hot', description: 'Web extract model and limits' },\n { prefix: 'agents.defaults.browser', kind: 'hot', description: 'Browser automation (Playwright) tools' },\n { prefix: 'agents.defaults.delegate', kind: 'hot', description: 'delegate_task sub-agent tool' },\n { prefix: 'agents.defaults.executeCode', kind: 'hot', description: 'execute_code sandbox tool' },\n {\n prefix: 'agents.defaults.backgroundReview',\n kind: 'hot',\n description: 'Post-turn memory/skill nudge + background review',\n },\n { prefix: 'agents.defaults.workspace', kind: 'none', description: 'Workspace path - no runtime effect' },\n \n // Gateway - restart required\n { prefix: 'gateway.host', kind: 'restart', description: 'Host address' },\n { prefix: 'gateway.port', kind: 'restart', description: 'Port number' },\n { prefix: 'gateway.auth', kind: 'restart', description: 'Authentication settings' },\n { prefix: 'gateway.cors', kind: 'restart', description: 'CORS settings' },\n {\n prefix: 'gateway.skillsStoreBaseUrl',\n kind: 'restart',\n description: 'Skills marketplace API base URL',\n },\n { prefix: 'gateway.enableHotReload', kind: 'hot', description: 'Hot reload toggle' },\n \n // Channels - hot reload (channel-specific prefixes are registered by channel plugins)\n { prefix: 'channels', kind: 'hot', description: 'Any channel subtree (e.g. future extensions)' },\n \n // Cron - hot reload\n { prefix: 'cron', kind: 'hot', description: 'Scheduled tasks' },\n \n // Heartbeat lives under gateway.heartbeat in config JSON (not top-level `heartbeat`)\n { prefix: 'gateway.heartbeat', kind: 'hot', description: 'Heartbeat settings' },\n \n // Web search - hot reload\n { prefix: 'webSearch', kind: 'hot', description: 'Web search settings' },\n { prefix: 'webTools', kind: 'hot', description: 'Web tools settings' },\n \n // Extension list toggles — still require process restart to load/unload modules\n {\n prefix: 'extensions.enabled',\n kind: 'restart',\n description: 'Extension enable list (requires restart)',\n },\n {\n prefix: 'extensions.disabled',\n kind: 'restart',\n description: 'Extension disable list (requires restart)',\n },\n // Extension instance config — hot-reloaded via extension registerReload handlers\n {\n prefix: 'extensions',\n kind: 'hot',\n description: 'Extension configuration (hot-reloaded via extension handlers)',\n },\n \n // Tools - hot reload (for tool-specific settings)\n { prefix: 'tools', kind: 'hot', description: 'Tools configuration' },\n];\n\nfunction pluginsForReloadRules(): ChannelPlugin[] {\n const listed = listChannelPlugins();\n return listed.length > 0 ? [...listed] : [...bundledChannelPlugins];\n}\n\nfunction getChannelReloadRules(): ReloadRule[] {\n return pluginsForReloadRules()\n .filter((plugin) => plugin.reload?.configPrefixes?.length)\n .flatMap((plugin) =>\n plugin.reload!.configPrefixes.map((prefix) => ({\n prefix,\n kind: 'hot' as const,\n description: `${plugin.meta.label} settings`,\n })),\n );\n}\n\nfunction mergedReloadRules(): ReloadRule[] {\n return [...BASE_RELOAD_RULES, ...getChannelReloadRules()];\n}\n\n/**\n * Find matching rule for a config path\n */\nexport function matchReloadRule(path: string): ReloadRule | null {\n const merged = mergedReloadRules();\n const exact = merged.find((r) => r.prefix === path);\n if (exact) return exact;\n const sorted = [...merged].sort((a, b) => b.prefix.length - a.prefix.length);\n for (const rule of sorted) {\n if (path === rule.prefix || path.startsWith(`${rule.prefix}.`)) {\n return rule;\n }\n }\n return null;\n}\n\n/**\n * Build reload plan from changed paths\n */\nexport function buildReloadPlan(changedPaths: string[]): ReloadPlan {\n const plan: ReloadPlan = {\n changedPaths,\n hotPaths: [],\n restartPaths: [],\n noopPaths: [],\n requiresRestart: false,\n requiresHotReload: false,\n };\n\n for (const path of changedPaths) {\n const rule = matchReloadRule(path);\n \n if (!rule) {\n // No rule matched - default to restart for safety\n plan.restartPaths.push(path);\n plan.requiresRestart = true;\n continue;\n }\n\n switch (rule.kind) {\n case 'hot':\n plan.hotPaths.push(path);\n plan.requiresHotReload = true;\n break;\n case 'restart':\n plan.restartPaths.push(path);\n plan.requiresRestart = true;\n break;\n case 'none':\n plan.noopPaths.push(path);\n break;\n }\n }\n\n return plan;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { createLogger } from "../utils/logger/index.js";
2
- import { init_logger } from "../utils/logger.js";
3
1
  import { init_agent_scope, normalizeAgentId } from "../agent/agent-scope.js";
4
2
  import { buildSessionKey, init_session_key } from "../routing/session-key.js";
3
+ import { createLogger } from "../utils/logger/index.js";
4
+ import { init_logger } from "../utils/logger.js";
5
5
  import { bundledChannelPlugins } from "../generated/bundled-channel-plugins.js";
6
6
  import { shouldSilence, stripHeartbeatToken } from "../heartbeat/tokens.js";
7
7
  import { getChannelPlugin, listChannelPlugins, syncChannelPluginsFromManager } from "../channels/plugins/registry.js";
@@ -1,8 +1,8 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { JobDataSchema } from "./validation.js";
4
- import { access, mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
5
4
  import { dirname } from "path";
5
+ import { access, mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
6
6
  //#region src/cron/persistence.ts
7
7
  init_logger();
8
8
  const log = createLogger("CronPersistence");
@@ -1,8 +1,8 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { init_paths, resolveCronRunsDir } from "../config/paths.js";
4
- import { appendFile, mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
5
4
  import { join } from "path";
5
+ import { appendFile, mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
6
6
  //#region src/cron/run-log-store.ts
7
7
  init_paths();
8
8
  init_logger();
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import type { AgentTool } from '@mariozechner/pi-agent-core';
7
7
  import type { Command } from 'commander';
8
- import type { ExtensionApi, ExtensionLogger, ExtensionRuntime, GatewayMethodHandler, HttpRequestHandler, ExtensionCommand, ExtensionService, FlagConfig, FlagValue, ShortcutConfig, HookHandlerMap, ExtensionHookEvent } from './types/index.js';
8
+ import type { ExtensionApi, ExtensionLogger, ExtensionRuntime, GatewayMethodHandler, HttpRequestHandler, ExtensionCommand, ExtensionReloadHandler, ExtensionService, FlagConfig, FlagValue, ShortcutConfig, HookHandlerMap, ExtensionHookEvent } from './types/index.js';
9
9
  import type { ChannelPlugin } from '../channels/plugin-types.js';
10
10
  import type { Config } from '../config/config-surface.js';
11
11
  import { EventEmitter } from 'events';
@@ -28,6 +28,8 @@ export declare class ExtensionApiImpl implements ExtensionApi {
28
28
  private _typedEventBus;
29
29
  private _registry;
30
30
  private readonly _runtime;
31
+ private _reloadConfigPrefixes;
32
+ private _registeredCommandIds;
31
33
  constructor(id: string, name: string, version: string | undefined, source: string, config: Config, extensionConfig: Record<string, unknown>, _logger: ExtensionLogger, _resolvePath: (input: string) => string, _coreRegistry?: ExtensionRegistryImpl, runtime?: ExtensionRuntime);
32
34
  get logger(): ExtensionLogger;
33
35
  get runtime(): ExtensionRuntime;
@@ -59,6 +61,9 @@ export declare class ExtensionApiImpl implements ExtensionApi {
59
61
  }): void;
60
62
  registerHttpRoute(path: string, handler: HttpRequestHandler): void;
61
63
  registerCommand(command: ExtensionCommand): void;
64
+ registerReload(handler: ExtensionReloadHandler): void;
65
+ /** Called from loader when manifest declares `reload.configPrefixes`. */
66
+ _setReloadConfigPrefixes(prefixes: string[]): void;
62
67
  registerService(service: ExtensionService): void;
63
68
  registerGatewayMethod(method: string, handler: GatewayMethodHandler): void;
64
69
  registerCli(factory: (ctx: {
@@ -1,6 +1,7 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { HOOK_EXECUTION_MODES } from "./types/hooks.js";
4
+ import { commandRegistry } from "../chat-commands/registry.js";
4
5
  import { TypedEventBus } from "./typed-event-bus.js";
5
6
  import { ExtensionRegistryImpl } from "./loader.js";
6
7
  import { isAbsolute, resolve } from "path";
@@ -15,6 +16,8 @@ var ExtensionApiImpl = class {
15
16
  _typedEventBus;
16
17
  _registry;
17
18
  _runtime;
19
+ _reloadConfigPrefixes = [];
20
+ _registeredCommandIds = [];
18
21
  constructor(id, name, version, source, config, extensionConfig, _logger, _resolvePath, _coreRegistry, runtime) {
19
22
  this.id = id;
20
23
  this.name = name;
@@ -93,8 +96,53 @@ var ExtensionApiImpl = class {
93
96
  this._logger.info(`Registered HTTP route: ${path}`);
94
97
  }
95
98
  registerCommand(command) {
99
+ const commandId = `ext.${this.id}.${command.name}`;
100
+ const definition = {
101
+ id: commandId,
102
+ name: command.name,
103
+ aliases: command.aliases,
104
+ description: command.description,
105
+ category: "extension",
106
+ scope: command.scope ?? ["global"],
107
+ acceptsArgs: command.acceptsArgs,
108
+ examples: command.examples,
109
+ handler: async (ctx, args) => {
110
+ const extensionCtx = {
111
+ sessionKey: ctx.sessionKey,
112
+ source: ctx.source,
113
+ isGroup: ctx.isGroup,
114
+ config: ctx.config,
115
+ reply: (text) => ctx.reply(text)
116
+ };
117
+ const result = await command.handler(args, extensionCtx);
118
+ if (!result) return {
119
+ content: "",
120
+ success: true
121
+ };
122
+ return {
123
+ content: result.content,
124
+ success: result.success ?? true
125
+ };
126
+ }
127
+ };
128
+ commandRegistry.register(definition);
129
+ this._registeredCommandIds.push(commandId);
130
+ this._registry.addCommand(command);
96
131
  this._eventBus.emit("command:register", command);
97
- this._logger.info(`Registered command: /${command.name}`);
132
+ this._logger.info(`Registered chat command: /${command.name}`);
133
+ }
134
+ registerReload(handler) {
135
+ const prefixes = this._reloadConfigPrefixes.length > 0 ? this._reloadConfigPrefixes : [`extensions.${this.id}`];
136
+ this._registry.addReloadRegistration({
137
+ extensionId: this.id,
138
+ handler,
139
+ configPrefixes: prefixes
140
+ });
141
+ this._logger.info("Registered reload handler");
142
+ }
143
+ /** Called from loader when manifest declares `reload.configPrefixes`. */
144
+ _setReloadConfigPrefixes(prefixes) {
145
+ this._reloadConfigPrefixes = [...prefixes];
98
146
  }
99
147
  registerService(service) {
100
148
  this._eventBus.emit("service:register", service);
@@ -162,6 +210,9 @@ var ExtensionApiImpl = class {
162
210
  return this._eventBus;
163
211
  }
164
212
  _cleanup() {
213
+ for (const commandId of this._registeredCommandIds) commandRegistry.unregister(commandId);
214
+ this._registeredCommandIds = [];
215
+ this._registry.removeReloadRegistration(this.id);
165
216
  this._typedEventBus.cleanupAll();
166
217
  this._eventBus.removeAllListeners();
167
218
  this._hooks.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../../src/extensions/api.ts"],"sourcesContent":["/**\n * Extension API Implementation\n * \n * Complete extension API with strong typing, security, and provider support.\n */\n\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\nimport type { Command } from 'commander';\nimport type {\n ExtensionApi,\n ExtensionLogger,\n ExtensionRuntime,\n ExtensionCliRegistration,\n GatewayMethodHandler,\n HttpRequestHandler,\n ExtensionCommand,\n ExtensionService,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n HookHandlerMap,\n ExtensionHookEvent,\n HookExecutionMode,\n} from './types/index.js';\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport {\n HOOK_EXECUTION_MODES,\n} from './types/hooks.js';\nimport type { Config } from '../config/config-surface.js';\nimport { resolve, isAbsolute } from 'path';\nimport { EventEmitter } from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { TypedEventBus } from './typed-event-bus.js';\nimport { ExtensionRegistryImpl } from './loader.js';\n\nexport class ExtensionApiImpl implements ExtensionApi {\n private _tools: Map<string, AgentTool> = new Map();\n private _hooks: Map<string, Set<Function>> = new Map();\n // Strongly typed hooks (for on() method)\n private _typedHooks: Map<ExtensionHookEvent, Set<Function>> = new Map();\n private _eventBus = new EventEmitter();\n private _typedEventBus: TypedEventBus;\n \n // Unified Registry\n private _registry: ExtensionRegistryImpl;\n\n private readonly _runtime: ExtensionRuntime;\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly version: string | undefined,\n public readonly source: string,\n public readonly config: Config,\n public readonly extensionConfig: Record<string, unknown>,\n private readonly _logger: ExtensionLogger,\n private readonly _resolvePath: (input: string) => string,\n private readonly _coreRegistry?: ExtensionRegistryImpl,\n runtime?: ExtensionRuntime,\n ) {\n // Initialize typed event bus\n this._typedEventBus = new TypedEventBus({\n logger: _logger,\n });\n \n this._registry = _coreRegistry ?? new ExtensionRegistryImpl();\n this._runtime =\n runtime ??\n ({\n config: this.config,\n log: this._logger,\n } as ExtensionRuntime);\n }\n\n get logger(): ExtensionLogger {\n return this._logger;\n }\n\n get runtime(): ExtensionRuntime {\n return this._runtime;\n }\n\n registerTool(tool: AgentTool): void {\n if (this._tools.has(tool.name)) {\n this._logger.warn(`Tool ${tool.name} already registered, overwriting`);\n }\n this._tools.set(tool.name, tool);\n this._logger.info(`Registered tool: ${tool.name}`);\n }\n\n registerHook(event: string, handler: Function, opts?: { priority?: number; once?: boolean }): void {\n if (!this._hooks.has(event)) {\n this._hooks.set(event, new Set());\n }\n this._hooks.get(event)!.add(handler);\n\n if (opts?.once) {\n const wrapper = async (...args: unknown[]) => {\n await handler(...args);\n this._hooks.get(event)?.delete(wrapper);\n };\n this._hooks.get(event)!.add(wrapper);\n }\n\n this._logger.info(`Registered hook: ${event}`);\n }\n\n /**\n * Strongly typed hook registration\n * Provides compile-time type checking and IDE autocomplete\n * \n * @example\n * api.on('before_agent_start', async (event, ctx) => {\n * console.log('Agent starting with prompt:', event.prompt);\n * return { systemPrompt: event.systemPrompt };\n * });\n */\n onHook<K extends ExtensionHookEvent>(\n hookName: K,\n handler: HookHandlerMap[K],\n _opts?: { priority?: number },\n ): void {\n // Get execution mode for this hook\n const mode = (HOOK_EXECUTION_MODES as Record<string, HookExecutionMode>)[hookName];\n \n if (!this._typedHooks.has(hookName)) {\n this._typedHooks.set(hookName, new Set());\n }\n \n this._typedHooks.get(hookName)!.add(handler);\n\n this._logger.debug(`Registered typed hook: ${hookName} (mode: ${mode})`);\n }\n\n /**\n * Get typed hooks for a specific event\n */\n _getTypedHooks<K extends ExtensionHookEvent>(hookName: K): HookHandlerMap[K][] {\n const hooks = this._typedHooks.get(hookName);\n return hooks ? Array.from(hooks) as HookHandlerMap[K][] : [];\n }\n\n /** Adds a ChannelPlugin to the extension registry; emits `channel:register` for observability. */\n registerChannel(registration: { plugin: ChannelPlugin }): void {\n const plugin = registration.plugin;\n this._registry.addChannelPlugin(plugin);\n this._eventBus.emit('channel:register', plugin);\n this._logger.info(`Registered channel plugin: ${plugin.id}`);\n }\n\n registerHttpRoute(path: string, handler: HttpRequestHandler): void {\n this._eventBus.emit('http:route', { path, handler });\n this._logger.info(`Registered HTTP route: ${path}`);\n }\n\n registerCommand(command: ExtensionCommand): void {\n this._eventBus.emit('command:register', command);\n this._logger.info(`Registered command: /${command.name}`);\n }\n\n registerService(service: ExtensionService): void {\n this._eventBus.emit('service:register', service);\n this._logger.info(`Registered service: ${service.id}`);\n }\n\n registerGatewayMethod(method: string, handler: GatewayMethodHandler): void {\n this._eventBus.emit('gateway:method', { method, handler });\n this._logger.info(`Registered gateway method: ${method}`);\n }\n\n registerCli(\n factory: (ctx: { program: Command }) => void,\n opts?: { commands: string[] },\n ): void {\n const reg: ExtensionCliRegistration = {\n extensionId: this.id,\n commands: opts?.commands ?? [],\n factory,\n };\n this._registry.addCliRegistration(reg);\n this._eventBus.emit('cli:register', reg);\n this._logger.info(\n `Registered CLI factory${opts?.commands?.length ? ` (${opts.commands.join(', ')})` : ''}`,\n );\n }\n\n resolvePath(input: string): string {\n return this._resolvePath(input);\n }\n\n emit(event: string, data: unknown): void {\n this._eventBus.emit(event, data);\n }\n\n on(event: string, handler: (data: unknown) => void): void {\n this._eventBus.on(event, handler);\n }\n\n off(event: string, handler: (data: unknown) => void): void {\n this._eventBus.off(event, handler);\n }\n\n // Typed Event Bus\n get events(): TypedEventBus {\n return this._typedEventBus;\n }\n\n // Unified Registry Methods\n \n /**\n * Register a full ProviderPlugin\n */\n registerProvider(plugin: import('./types/providers.js').ProviderPlugin): void {\n import('../providers/plugin-registry.js').then(({ getProviderRegistry }) => {\n const registry = getProviderRegistry();\n registry.register(plugin);\n this._logger.info(`Extension registered provider: ${plugin.id}`);\n }).catch((err: unknown) => {\n this._logger.error(`Failed to register provider: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n /**\n * Register a full ProviderPlugin (alias for registerProvider)\n */\n registerProviderPlugin(plugin: import('./types/providers.js').ProviderPlugin): void {\n this.registerProvider(plugin);\n }\n\n registerFlag(_name: string, _config: FlagConfig): void {\n // this._registry.registerFlag(name, config, this.id);\n }\n\n getFlag(_name: string): FlagValue {\n return undefined; // this._registry.getFlag(name);\n }\n\n registerShortcut(_key: string, _config: ShortcutConfig): void {\n // this._registry.registerShortcut(key, config, { extensionId: this.id });\n }\n\n // Internal methods for extension manager\n _getTools(): Map<string, AgentTool> {\n return this._tools;\n }\n\n _getHooks(): Map<string, Set<Function>> {\n return this._hooks;\n }\n\n _getEventBus(): EventEmitter {\n return this._eventBus;\n }\n\n _cleanup(): void {\n this._typedEventBus.cleanupAll();\n this._eventBus.removeAllListeners();\n this._hooks.clear();\n this._typedHooks.clear();\n }\n}\n\n// ============================================================================\n// Default Logger\n// ============================================================================\n\nexport function createExtensionLogger(prefix: string): ExtensionLogger {\n const childLogger = createLogger(`Extension:${prefix}`);\n\n return {\n debug: (msg: string) => childLogger.debug(msg),\n info: (msg: string) => childLogger.info(msg),\n warn: (msg: string) => childLogger.warn(msg),\n error: (msg: string) => childLogger.error(msg),\n };\n}\n\n// ============================================================================\n// Path Resolver\n// ============================================================================\n\nexport function createPathResolver(extensionDir: string, workspaceDir: string) {\n return (input: string): string => {\n if (input.startsWith('~')) {\n return input.replace('~', process.env.HOME || '');\n }\n if (input.startsWith('.')) {\n return resolve(extensionDir, input);\n }\n if (!isAbsolute(input)) {\n return resolve(workspaceDir, input);\n }\n return input;\n };\n}\n"],"mappings":";;;;;;;;aA+BkD;AAIlD,IAAa,mBAAb,MAAsD;CACpD,yBAAyC,IAAI,KAAK;CAClD,yBAA6C,IAAI,KAAK;CAEtD,8BAA8D,IAAI,KAAK;CACvE,YAAoB,IAAI,cAAc;CACtC;CAGA;CAEA;CAEA,YACE,IACA,MACA,SACA,QACA,QACA,iBACA,SACA,cACA,eACA,SACA;AAVgB,OAAA,KAAA;AACA,OAAA,OAAA;AACA,OAAA,UAAA;AACA,OAAA,SAAA;AACA,OAAA,SAAA;AACA,OAAA,kBAAA;AACC,OAAA,UAAA;AACA,OAAA,eAAA;AACA,OAAA,gBAAA;AAIjB,OAAK,iBAAiB,IAAI,cAAc,EACtC,QAAQ,SACT,CAAC;AAEF,OAAK,YAAY,iBAAiB,IAAI,uBAAuB;AAC7D,OAAK,WACH,WACC;GACC,QAAQ,KAAK;GACb,KAAK,KAAK;GACX;;CAGL,IAAI,SAA0B;AAC5B,SAAO,KAAK;;CAGd,IAAI,UAA4B;AAC9B,SAAO,KAAK;;CAGd,aAAa,MAAuB;AAClC,MAAI,KAAK,OAAO,IAAI,KAAK,KAAK,CAC5B,MAAK,QAAQ,KAAK,QAAQ,KAAK,KAAK,kCAAkC;AAExE,OAAK,OAAO,IAAI,KAAK,MAAM,KAAK;AAChC,OAAK,QAAQ,KAAK,oBAAoB,KAAK,OAAO;;CAGpD,aAAa,OAAe,SAAmB,MAAoD;AACjG,MAAI,CAAC,KAAK,OAAO,IAAI,MAAM,CACzB,MAAK,OAAO,IAAI,uBAAO,IAAI,KAAK,CAAC;AAEnC,OAAK,OAAO,IAAI,MAAM,CAAE,IAAI,QAAQ;AAEpC,MAAI,MAAM,MAAM;GACd,MAAM,UAAU,OAAO,GAAG,SAAoB;AAC5C,UAAM,QAAQ,GAAG,KAAK;AACtB,SAAK,OAAO,IAAI,MAAM,EAAE,OAAO,QAAQ;;AAEzC,QAAK,OAAO,IAAI,MAAM,CAAE,IAAI,QAAQ;;AAGtC,OAAK,QAAQ,KAAK,oBAAoB,QAAQ;;;;;;;;;;;;CAahD,OACE,UACA,SACA,OACM;EAEN,MAAM,OAAQ,qBAA2D;AAEzE,MAAI,CAAC,KAAK,YAAY,IAAI,SAAS,CACjC,MAAK,YAAY,IAAI,0BAAU,IAAI,KAAK,CAAC;AAG3C,OAAK,YAAY,IAAI,SAAS,CAAE,IAAI,QAAQ;AAE5C,OAAK,QAAQ,MAAM,0BAA0B,SAAS,UAAU,KAAK,GAAG;;;;;CAM1E,eAA6C,UAAkC;EAC7E,MAAM,QAAQ,KAAK,YAAY,IAAI,SAAS;AAC5C,SAAO,QAAQ,MAAM,KAAK,MAAM,GAA0B,EAAE;;;CAI9D,gBAAgB,cAA+C;EAC7D,MAAM,SAAS,aAAa;AAC5B,OAAK,UAAU,iBAAiB,OAAO;AACvC,OAAK,UAAU,KAAK,oBAAoB,OAAO;AAC/C,OAAK,QAAQ,KAAK,8BAA8B,OAAO,KAAK;;CAG9D,kBAAkB,MAAc,SAAmC;AACjE,OAAK,UAAU,KAAK,cAAc;GAAE;GAAM;GAAS,CAAC;AACpD,OAAK,QAAQ,KAAK,0BAA0B,OAAO;;CAGrD,gBAAgB,SAAiC;AAC/C,OAAK,UAAU,KAAK,oBAAoB,QAAQ;AAChD,OAAK,QAAQ,KAAK,wBAAwB,QAAQ,OAAO;;CAG3D,gBAAgB,SAAiC;AAC/C,OAAK,UAAU,KAAK,oBAAoB,QAAQ;AAChD,OAAK,QAAQ,KAAK,uBAAuB,QAAQ,KAAK;;CAGxD,sBAAsB,QAAgB,SAAqC;AACzE,OAAK,UAAU,KAAK,kBAAkB;GAAE;GAAQ;GAAS,CAAC;AAC1D,OAAK,QAAQ,KAAK,8BAA8B,SAAS;;CAG3D,YACE,SACA,MACM;EACN,MAAM,MAAgC;GACpC,aAAa,KAAK;GAClB,UAAU,MAAM,YAAY,EAAE;GAC9B;GACD;AACD,OAAK,UAAU,mBAAmB,IAAI;AACtC,OAAK,UAAU,KAAK,gBAAgB,IAAI;AACxC,OAAK,QAAQ,KACX,yBAAyB,MAAM,UAAU,SAAS,KAAK,KAAK,SAAS,KAAK,KAAK,CAAC,KAAK,KACtF;;CAGH,YAAY,OAAuB;AACjC,SAAO,KAAK,aAAa,MAAM;;CAGjC,KAAK,OAAe,MAAqB;AACvC,OAAK,UAAU,KAAK,OAAO,KAAK;;CAGlC,GAAG,OAAe,SAAwC;AACxD,OAAK,UAAU,GAAG,OAAO,QAAQ;;CAGnC,IAAI,OAAe,SAAwC;AACzD,OAAK,UAAU,IAAI,OAAO,QAAQ;;CAIpC,IAAI,SAAwB;AAC1B,SAAO,KAAK;;;;;CAQd,iBAAiB,QAA6D;AAC5E,SAAO,mCAAmC,MAAM,EAAE,0BAA0B;AACzD,wBACT,CAAC,SAAS,OAAO;AACzB,QAAK,QAAQ,KAAK,kCAAkC,OAAO,KAAK;IAChE,CAAC,OAAO,QAAiB;AACzB,QAAK,QAAQ,MAAM,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;IACtG;;;;;CAMJ,uBAAuB,QAA6D;AAClF,OAAK,iBAAiB,OAAO;;CAG/B,aAAa,OAAe,SAA2B;CAIvD,QAAQ,OAA0B;CAIlC,iBAAiB,MAAc,SAA+B;CAK9D,YAAoC;AAClC,SAAO,KAAK;;CAGd,YAAwC;AACtC,SAAO,KAAK;;CAGd,eAA6B;AAC3B,SAAO,KAAK;;CAGd,WAAiB;AACf,OAAK,eAAe,YAAY;AAChC,OAAK,UAAU,oBAAoB;AACnC,OAAK,OAAO,OAAO;AACnB,OAAK,YAAY,OAAO;;;AAQ5B,SAAgB,sBAAsB,QAAiC;CACrE,MAAM,cAAc,aAAa,aAAa,SAAS;AAEvD,QAAO;EACL,QAAQ,QAAgB,YAAY,MAAM,IAAI;EAC9C,OAAO,QAAgB,YAAY,KAAK,IAAI;EAC5C,OAAO,QAAgB,YAAY,KAAK,IAAI;EAC5C,QAAQ,QAAgB,YAAY,MAAM,IAAI;EAC/C;;AAOH,SAAgB,mBAAmB,cAAsB,cAAsB;AAC7E,SAAQ,UAA0B;AAChC,MAAI,MAAM,WAAW,IAAI,CACvB,QAAO,MAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAEnD,MAAI,MAAM,WAAW,IAAI,CACvB,QAAO,QAAQ,cAAc,MAAM;AAErC,MAAI,CAAC,WAAW,MAAM,CACpB,QAAO,QAAQ,cAAc,MAAM;AAErC,SAAO"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../../src/extensions/api.ts"],"sourcesContent":["/**\n * Extension API Implementation\n * \n * Complete extension API with strong typing, security, and provider support.\n */\n\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\nimport type { Command } from 'commander';\nimport type {\n ExtensionApi,\n ExtensionLogger,\n ExtensionRuntime,\n ExtensionCliRegistration,\n GatewayMethodHandler,\n HttpRequestHandler,\n ExtensionCommand,\n ExtensionCommandContext,\n ExtensionReloadHandler,\n ExtensionService,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n HookHandlerMap,\n ExtensionHookEvent,\n HookExecutionMode,\n} from './types/index.js';\nimport type { CommandDefinition } from '../chat-commands/types.js';\nimport { commandRegistry } from '../chat-commands/registry.js';\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport {\n HOOK_EXECUTION_MODES,\n} from './types/hooks.js';\nimport type { Config } from '../config/config-surface.js';\nimport type { Config as SchemaConfig } from '../config/schema.js';\nimport { resolve, isAbsolute } from 'path';\nimport { EventEmitter } from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { TypedEventBus } from './typed-event-bus.js';\nimport { ExtensionRegistryImpl } from './loader.js';\n\nexport class ExtensionApiImpl implements ExtensionApi {\n private _tools: Map<string, AgentTool> = new Map();\n private _hooks: Map<string, Set<Function>> = new Map();\n // Strongly typed hooks (for on() method)\n private _typedHooks: Map<ExtensionHookEvent, Set<Function>> = new Map();\n private _eventBus = new EventEmitter();\n private _typedEventBus: TypedEventBus;\n \n // Unified Registry\n private _registry: ExtensionRegistryImpl;\n\n private readonly _runtime: ExtensionRuntime;\n\n private _reloadConfigPrefixes: string[] = [];\n private _registeredCommandIds: string[] = [];\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly version: string | undefined,\n public readonly source: string,\n public readonly config: Config,\n public readonly extensionConfig: Record<string, unknown>,\n private readonly _logger: ExtensionLogger,\n private readonly _resolvePath: (input: string) => string,\n private readonly _coreRegistry?: ExtensionRegistryImpl,\n runtime?: ExtensionRuntime,\n ) {\n // Initialize typed event bus\n this._typedEventBus = new TypedEventBus({\n logger: _logger,\n });\n \n this._registry = _coreRegistry ?? new ExtensionRegistryImpl();\n this._runtime =\n runtime ??\n ({\n config: this.config,\n log: this._logger,\n } as ExtensionRuntime);\n }\n\n get logger(): ExtensionLogger {\n return this._logger;\n }\n\n get runtime(): ExtensionRuntime {\n return this._runtime;\n }\n\n registerTool(tool: AgentTool): void {\n if (this._tools.has(tool.name)) {\n this._logger.warn(`Tool ${tool.name} already registered, overwriting`);\n }\n this._tools.set(tool.name, tool);\n this._logger.info(`Registered tool: ${tool.name}`);\n }\n\n registerHook(event: string, handler: Function, opts?: { priority?: number; once?: boolean }): void {\n if (!this._hooks.has(event)) {\n this._hooks.set(event, new Set());\n }\n this._hooks.get(event)!.add(handler);\n\n if (opts?.once) {\n const wrapper = async (...args: unknown[]) => {\n await handler(...args);\n this._hooks.get(event)?.delete(wrapper);\n };\n this._hooks.get(event)!.add(wrapper);\n }\n\n this._logger.info(`Registered hook: ${event}`);\n }\n\n /**\n * Strongly typed hook registration\n * Provides compile-time type checking and IDE autocomplete\n * \n * @example\n * api.on('before_agent_start', async (event, ctx) => {\n * console.log('Agent starting with prompt:', event.prompt);\n * return { systemPrompt: event.systemPrompt };\n * });\n */\n onHook<K extends ExtensionHookEvent>(\n hookName: K,\n handler: HookHandlerMap[K],\n _opts?: { priority?: number },\n ): void {\n // Get execution mode for this hook\n const mode = (HOOK_EXECUTION_MODES as Record<string, HookExecutionMode>)[hookName];\n \n if (!this._typedHooks.has(hookName)) {\n this._typedHooks.set(hookName, new Set());\n }\n \n this._typedHooks.get(hookName)!.add(handler);\n\n this._logger.debug(`Registered typed hook: ${hookName} (mode: ${mode})`);\n }\n\n /**\n * Get typed hooks for a specific event\n */\n _getTypedHooks<K extends ExtensionHookEvent>(hookName: K): HookHandlerMap[K][] {\n const hooks = this._typedHooks.get(hookName);\n return hooks ? Array.from(hooks) as HookHandlerMap[K][] : [];\n }\n\n /** Adds a ChannelPlugin to the extension registry; emits `channel:register` for observability. */\n registerChannel(registration: { plugin: ChannelPlugin }): void {\n const plugin = registration.plugin;\n this._registry.addChannelPlugin(plugin);\n this._eventBus.emit('channel:register', plugin);\n this._logger.info(`Registered channel plugin: ${plugin.id}`);\n }\n\n registerHttpRoute(path: string, handler: HttpRequestHandler): void {\n this._eventBus.emit('http:route', { path, handler });\n this._logger.info(`Registered HTTP route: ${path}`);\n }\n\n registerCommand(command: ExtensionCommand): void {\n const commandId = `ext.${this.id}.${command.name}`;\n\n const definition: CommandDefinition = {\n id: commandId,\n name: command.name,\n aliases: command.aliases,\n description: command.description,\n category: 'extension',\n scope: command.scope ?? ['global'],\n acceptsArgs: command.acceptsArgs,\n examples: command.examples,\n handler: async (ctx, args) => {\n const extensionCtx: ExtensionCommandContext = {\n sessionKey: ctx.sessionKey,\n source: ctx.source,\n isGroup: ctx.isGroup,\n config: ctx.config as SchemaConfig as ExtensionCommandContext['config'],\n reply: (text: string) => ctx.reply(text),\n };\n\n const result = await command.handler(args, extensionCtx);\n\n if (!result) {\n return { content: '', success: true };\n }\n return {\n content: result.content,\n success: result.success ?? true,\n };\n },\n };\n\n commandRegistry.register(definition);\n this._registeredCommandIds.push(commandId);\n this._registry.addCommand(command);\n\n this._eventBus.emit('command:register', command);\n this._logger.info(`Registered chat command: /${command.name}`);\n }\n\n registerReload(handler: ExtensionReloadHandler): void {\n const prefixes =\n this._reloadConfigPrefixes.length > 0\n ? this._reloadConfigPrefixes\n : [`extensions.${this.id}`];\n\n this._registry.addReloadRegistration({\n extensionId: this.id,\n handler,\n configPrefixes: prefixes,\n });\n this._logger.info('Registered reload handler');\n }\n\n /** Called from loader when manifest declares `reload.configPrefixes`. */\n _setReloadConfigPrefixes(prefixes: string[]): void {\n this._reloadConfigPrefixes = [...prefixes];\n }\n\n registerService(service: ExtensionService): void {\n this._eventBus.emit('service:register', service);\n this._logger.info(`Registered service: ${service.id}`);\n }\n\n registerGatewayMethod(method: string, handler: GatewayMethodHandler): void {\n this._eventBus.emit('gateway:method', { method, handler });\n this._logger.info(`Registered gateway method: ${method}`);\n }\n\n registerCli(\n factory: (ctx: { program: Command }) => void,\n opts?: { commands: string[] },\n ): void {\n const reg: ExtensionCliRegistration = {\n extensionId: this.id,\n commands: opts?.commands ?? [],\n factory,\n };\n this._registry.addCliRegistration(reg);\n this._eventBus.emit('cli:register', reg);\n this._logger.info(\n `Registered CLI factory${opts?.commands?.length ? ` (${opts.commands.join(', ')})` : ''}`,\n );\n }\n\n resolvePath(input: string): string {\n return this._resolvePath(input);\n }\n\n emit(event: string, data: unknown): void {\n this._eventBus.emit(event, data);\n }\n\n on(event: string, handler: (data: unknown) => void): void {\n this._eventBus.on(event, handler);\n }\n\n off(event: string, handler: (data: unknown) => void): void {\n this._eventBus.off(event, handler);\n }\n\n // Typed Event Bus\n get events(): TypedEventBus {\n return this._typedEventBus;\n }\n\n // Unified Registry Methods\n \n /**\n * Register a full ProviderPlugin\n */\n registerProvider(plugin: import('./types/providers.js').ProviderPlugin): void {\n import('../providers/plugin-registry.js').then(({ getProviderRegistry }) => {\n const registry = getProviderRegistry();\n registry.register(plugin);\n this._logger.info(`Extension registered provider: ${plugin.id}`);\n }).catch((err: unknown) => {\n this._logger.error(`Failed to register provider: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n /**\n * Register a full ProviderPlugin (alias for registerProvider)\n */\n registerProviderPlugin(plugin: import('./types/providers.js').ProviderPlugin): void {\n this.registerProvider(plugin);\n }\n\n registerFlag(_name: string, _config: FlagConfig): void {\n // this._registry.registerFlag(name, config, this.id);\n }\n\n getFlag(_name: string): FlagValue {\n return undefined; // this._registry.getFlag(name);\n }\n\n registerShortcut(_key: string, _config: ShortcutConfig): void {\n // this._registry.registerShortcut(key, config, { extensionId: this.id });\n }\n\n // Internal methods for extension manager\n _getTools(): Map<string, AgentTool> {\n return this._tools;\n }\n\n _getHooks(): Map<string, Set<Function>> {\n return this._hooks;\n }\n\n _getEventBus(): EventEmitter {\n return this._eventBus;\n }\n\n _cleanup(): void {\n for (const commandId of this._registeredCommandIds) {\n commandRegistry.unregister(commandId);\n }\n this._registeredCommandIds = [];\n\n this._registry.removeReloadRegistration(this.id);\n\n this._typedEventBus.cleanupAll();\n this._eventBus.removeAllListeners();\n this._hooks.clear();\n this._typedHooks.clear();\n }\n}\n\n// ============================================================================\n// Default Logger\n// ============================================================================\n\nexport function createExtensionLogger(prefix: string): ExtensionLogger {\n const childLogger = createLogger(`Extension:${prefix}`);\n\n return {\n debug: (msg: string) => childLogger.debug(msg),\n info: (msg: string) => childLogger.info(msg),\n warn: (msg: string) => childLogger.warn(msg),\n error: (msg: string) => childLogger.error(msg),\n };\n}\n\n// ============================================================================\n// Path Resolver\n// ============================================================================\n\nexport function createPathResolver(extensionDir: string, workspaceDir: string) {\n return (input: string): string => {\n if (input.startsWith('~')) {\n return input.replace('~', process.env.HOME || '');\n }\n if (input.startsWith('.')) {\n return resolve(extensionDir, input);\n }\n if (!isAbsolute(input)) {\n return resolve(workspaceDir, input);\n }\n return input;\n };\n}\n"],"mappings":"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"}