@xopcai/xopc 0.0.6 → 0.0.8

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 (291) hide show
  1. package/dist/extensions/telegram/src/plugin.js +1 -1
  2. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  3. package/dist/extensions/weixin/src/api/api.js +1 -1
  4. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  5. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  6. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  7. package/dist/extensions/weixin/src/plugin.js +1 -1
  8. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BSNzJWbQ.js} +2 -2
  9. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js.map → agents-BSNzJWbQ.js.map} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-BKk9SB4D.js} +2 -2
  11. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-BKk9SB4D.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +1 -0
  13. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-_J6cQN6G.js} +2 -2
  14. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-_J6cQN6G.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-DPb_0O8M.js} +2 -2
  16. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-DPb_0O8M.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-BUJOuuKX.js} +2 -2
  18. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-BUJOuuKX.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Cn0YVg8x.js} +2 -2
  20. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Cn0YVg8x.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js +2 -0
  22. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js.map +1 -0
  23. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-DTz4O5Ua.js} +2 -2
  24. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-DTz4O5Ua.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-Cs1Kde9o.js} +2 -2
  26. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-Cs1Kde9o.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-G52iX0Bo.js} +2 -2
  28. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-G52iX0Bo.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-CO2jxBA9.js} +2 -2
  30. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-CO2jxBA9.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-D9Ul8uSt.js} +2 -2
  32. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-D9Ul8uSt.js.map} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Bc8SVD15.js} +2 -2
  34. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Bc8SVD15.js.map} +1 -1
  35. package/dist/gateway/static/root/assets/index-BXUJbteW.js +16 -0
  36. package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +1 -0
  37. package/dist/gateway/static/root/assets/index-CQLMxWSA.css +2 -0
  38. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-5V25JkQY.js} +2 -2
  39. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-5V25JkQY.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-he3aQfme.js} +2 -2
  41. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-he3aQfme.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
  43. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
  44. package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +2 -0
  45. package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-DJHD9Ean.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-n-4O5d9U.js} +2 -2
  47. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-n-4O5d9U.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-B6dHLvbr.js} +3 -3
  49. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-B6dHLvbr.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-rBUfTdm3.js} +2 -2
  51. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-rBUfTdm3.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js +2 -0
  53. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +1 -0
  54. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-vxtE8kI6.js} +2 -2
  55. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-vxtE8kI6.js.map} +1 -1
  56. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-D36_O2Ub.js} +2 -2
  57. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-D36_O2Ub.js.map} +1 -1
  58. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CmiSsYBd.js} +2 -2
  59. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CmiSsYBd.js.map} +1 -1
  60. package/dist/gateway/static/root/assets/url-CtSqjF9J.js +2 -0
  61. package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +1 -0
  62. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-DYORQ7x6.js} +2 -2
  63. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-DYORQ7x6.js.map} +1 -1
  64. package/dist/gateway/static/root/index.html +16 -16
  65. package/dist/package.js +1 -1
  66. package/dist/src/agent/agent-manager.js +4 -4
  67. package/dist/src/agent/context/workspace-seed.js +2 -2
  68. package/dist/src/agent/image/index.d.ts +0 -1
  69. package/dist/src/agent/image/index.js +1 -2
  70. package/dist/src/agent/ipc/bus.js +1 -1
  71. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  72. package/dist/src/agent/models/manager.js +1 -1
  73. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  74. package/dist/src/agent/service.js +3 -3
  75. package/dist/src/agent/skills/hub-hash.js +1 -1
  76. package/dist/src/agent/skills/hub-pull.js +1 -1
  77. package/dist/src/agent/skills/index.js +1 -1
  78. package/dist/src/agent/skills/skill-manager.js +1 -1
  79. package/dist/src/agent/tools/browser/tools.js +2 -2
  80. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  81. package/dist/src/agent/tools/factory.js +1 -1
  82. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  83. package/dist/src/agent/tools/image-tool.js +2 -2
  84. package/dist/src/agent/tools/image-tool.js.map +1 -1
  85. package/dist/src/agent/tools/index.d.ts +1 -1
  86. package/dist/src/agent/tools/index.js +2 -2
  87. package/dist/src/agent/tools/read.d.ts +0 -2
  88. package/dist/src/agent/tools/read.js +1 -3
  89. package/dist/src/agent/tools/read.js.map +1 -1
  90. package/dist/src/agent/tools/send-media.js +1 -1
  91. package/dist/src/auth/sync-provider-auth.js +1 -1
  92. package/dist/src/channels/plugin-types.d.ts +14 -0
  93. package/dist/src/cli/commands/agent.js +1 -1
  94. package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
  95. package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
  96. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
  97. package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
  98. package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
  99. package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
  100. package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
  101. package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
  102. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
  103. package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
  104. package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
  105. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
  106. package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
  107. package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
  108. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
  109. package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
  110. package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
  111. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
  112. package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
  113. package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
  114. package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
  115. package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
  116. package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
  117. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
  118. package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
  119. package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
  120. package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
  121. package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
  122. package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
  123. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
  124. package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
  125. package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
  126. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
  127. package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
  128. package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
  129. package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
  130. package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
  131. package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
  132. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
  133. package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
  134. package/dist/src/cli/commands/doctor/flow.js +51 -0
  135. package/dist/src/cli/commands/doctor/flow.js.map +1 -0
  136. package/dist/src/cli/commands/doctor/format.d.ts +6 -0
  137. package/dist/src/cli/commands/doctor/format.js +61 -0
  138. package/dist/src/cli/commands/doctor/format.js.map +1 -0
  139. package/dist/src/cli/commands/doctor/index.js +44 -0
  140. package/dist/src/cli/commands/doctor/index.js.map +1 -0
  141. package/dist/src/cli/commands/doctor/types.d.ts +20 -0
  142. package/dist/src/cli/commands/doctor/types.js +1 -0
  143. package/dist/src/cli/commands/init.js +2 -2
  144. package/dist/src/cli/index.d.ts +1 -1
  145. package/dist/src/cli/index.js +1 -1
  146. package/dist/src/cli/index.js.map +1 -1
  147. package/dist/src/cli/utils/init-workspace.js +1 -1
  148. package/dist/src/config/index.js +3 -3
  149. package/dist/src/config/loader.js +1 -1
  150. package/dist/src/config/models-json.d.ts +15 -15
  151. package/dist/src/config/models-json.js +1 -1
  152. package/dist/src/config/profile.js +1 -1
  153. package/dist/src/config/schema.d.ts +0 -105
  154. package/dist/src/config/schema.js +3 -40
  155. package/dist/src/config/schema.js.map +1 -1
  156. package/dist/src/cron/executor.js +2 -2
  157. package/dist/src/daemon/launchd.js +2 -2
  158. package/dist/src/daemon/launchd.js.map +1 -1
  159. package/dist/src/daemon/systemd.js +2 -2
  160. package/dist/src/daemon/systemd.js.map +1 -1
  161. package/dist/src/extensions/loader.js +1 -1
  162. package/dist/src/gateway/agents-admin.js +1 -1
  163. package/dist/src/gateway/hono/lib/static-ui.js +1 -1
  164. package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
  165. package/dist/src/gateway/hono/routes/doctor.js +35 -0
  166. package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
  167. package/dist/src/gateway/hono/routes/index.js +2 -0
  168. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  169. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  170. package/dist/src/gateway/hono/sse.js +2 -2
  171. package/dist/src/gateway/lock.js +1 -1
  172. package/dist/src/gateway/ports.js +98 -3
  173. package/dist/src/gateway/ports.js.map +1 -1
  174. package/dist/src/gateway/service.d.ts +0 -4
  175. package/dist/src/gateway/service.js +4 -23
  176. package/dist/src/gateway/service.js.map +1 -1
  177. package/dist/src/routing/bindings.js +1 -1
  178. package/dist/src/routing/index.d.ts +1 -1
  179. package/dist/src/routing/index.js +2 -2
  180. package/dist/src/routing/index.js.map +1 -1
  181. package/dist/src/routing/resolve-route.js +1 -1
  182. package/dist/src/routing/session-key.d.ts +0 -5
  183. package/dist/src/routing/session-key.js +1 -27
  184. package/dist/src/routing/session-key.js.map +1 -1
  185. package/dist/src/session/session-title.js +1 -1
  186. package/dist/src/session/store.js +2 -6
  187. package/dist/src/session/store.js.map +1 -1
  188. package/dist/src/session/types.d.ts +0 -10
  189. package/dist/src/session/types.js.map +1 -1
  190. package/package.json +1 -2
  191. package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
  192. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
  193. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
  194. package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
  195. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
  196. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
  197. package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
  198. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
  199. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
  200. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
  201. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
  202. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
  203. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
  204. package/dist/src/acp/commands.d.ts +0 -11
  205. package/dist/src/acp/commands.js +0 -17
  206. package/dist/src/acp/commands.js.map +0 -1
  207. package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
  208. package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
  209. package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
  210. package/dist/src/acp/control-plane/index.d.ts +0 -10
  211. package/dist/src/acp/control-plane/index.js +0 -6
  212. package/dist/src/acp/control-plane/manager.d.ts +0 -86
  213. package/dist/src/acp/control-plane/manager.js +0 -502
  214. package/dist/src/acp/control-plane/manager.js.map +0 -1
  215. package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
  216. package/dist/src/acp/control-plane/manager.types.js +0 -14
  217. package/dist/src/acp/control-plane/manager.types.js.map +0 -1
  218. package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
  219. package/dist/src/acp/control-plane/manager.utils.js +0 -46
  220. package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
  221. package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
  222. package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
  223. package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
  224. package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
  225. package/dist/src/acp/control-plane/runtime-cache.js +0 -58
  226. package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
  227. package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
  228. package/dist/src/acp/control-plane/runtime-options.js +0 -92
  229. package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
  230. package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
  231. package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
  232. package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
  233. package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
  234. package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
  235. package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
  236. package/dist/src/acp/control-plane/session-store.d.ts +0 -39
  237. package/dist/src/acp/control-plane/session-store.js +0 -149
  238. package/dist/src/acp/control-plane/session-store.js.map +0 -1
  239. package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
  240. package/dist/src/acp/control-plane/turn-manager.js +0 -134
  241. package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
  242. package/dist/src/acp/event-mapper.d.ts +0 -48
  243. package/dist/src/acp/event-mapper.js +0 -94
  244. package/dist/src/acp/event-mapper.js.map +0 -1
  245. package/dist/src/acp/index.d.ts +0 -10
  246. package/dist/src/acp/index.js +0 -5
  247. package/dist/src/acp/meta.d.ts +0 -15
  248. package/dist/src/acp/meta.js +0 -36
  249. package/dist/src/acp/meta.js.map +0 -1
  250. package/dist/src/acp/routing-integration.d.ts +0 -37
  251. package/dist/src/acp/routing-integration.js +0 -58
  252. package/dist/src/acp/routing-integration.js.map +0 -1
  253. package/dist/src/acp/runtime/backends/index.d.ts +0 -4
  254. package/dist/src/acp/runtime/backends/index.js +0 -2
  255. package/dist/src/acp/runtime/backends/local.d.ts +0 -136
  256. package/dist/src/acp/runtime/backends/local.js +0 -603
  257. package/dist/src/acp/runtime/backends/local.js.map +0 -1
  258. package/dist/src/acp/runtime/error-text.d.ts +0 -16
  259. package/dist/src/acp/runtime/error-text.js +0 -40
  260. package/dist/src/acp/runtime/error-text.js.map +0 -1
  261. package/dist/src/acp/runtime/errors.d.ts +0 -31
  262. package/dist/src/acp/runtime/errors.js +0 -47
  263. package/dist/src/acp/runtime/errors.js.map +0 -1
  264. package/dist/src/acp/runtime/index.d.ts +0 -7
  265. package/dist/src/acp/runtime/index.js +0 -4
  266. package/dist/src/acp/runtime/registry.d.ts +0 -35
  267. package/dist/src/acp/runtime/registry.js +0 -85
  268. package/dist/src/acp/runtime/registry.js.map +0 -1
  269. package/dist/src/acp/runtime/session-identity.d.ts +0 -35
  270. package/dist/src/acp/runtime/session-identity.js +0 -134
  271. package/dist/src/acp/runtime/session-identity.js.map +0 -1
  272. package/dist/src/acp/runtime/types.d.ts +0 -214
  273. package/dist/src/acp/secret-file.d.ts +0 -7
  274. package/dist/src/acp/secret-file.js +0 -19
  275. package/dist/src/acp/secret-file.js.map +0 -1
  276. package/dist/src/acp/server.d.ts +0 -48
  277. package/dist/src/acp/server.js +0 -300
  278. package/dist/src/acp/server.js.map +0 -1
  279. package/dist/src/acp/session.d.ts +0 -29
  280. package/dist/src/acp/session.js +0 -30
  281. package/dist/src/acp/session.js.map +0 -1
  282. package/dist/src/acp/types.d.ts +0 -39
  283. package/dist/src/acp/types.js +0 -13
  284. package/dist/src/acp/types.js.map +0 -1
  285. package/dist/src/agent/image/describe-images.d.ts +0 -18
  286. package/dist/src/agent/image/describe-images.js +0 -19
  287. package/dist/src/agent/image/describe-images.js.map +0 -1
  288. package/dist/src/cli/commands/acp.d.ts +0 -4
  289. package/dist/src/cli/commands/acp.js +0 -200
  290. package/dist/src/cli/commands/acp.js.map +0 -1
  291. /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
@@ -77,9 +77,7 @@ async function executeReadFile(workspace, bootstrapDir, params) {
77
77
  };
78
78
  }
79
79
  }
80
- /** @deprecated Use {@link createReadFileTool}(process.cwd()) — default cwd; prefer factory with workspace. */
81
- const readFileTool = createReadFileTool(process.cwd());
82
80
  //#endregion
83
- export { createReadFileTool, readFileTool };
81
+ export { createReadFileTool };
84
82
 
85
83
  //# sourceMappingURL=read.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","names":[],"sources":["../../../../src/agent/tools/read.ts"],"sourcesContent":["// Read file tool\nimport { Type, type Static } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { readFile, stat } from 'fs/promises';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { truncateHead, formatSize, DEFAULT_MAX_BYTES } from './truncate.js';\nimport {\n isBareBootstrapFileName,\n resolveBootstrapPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst DEFAULT_MAX_LINES = 500;\n\nconst ReadFileSchema = Type.Object({\n path: Type.String({ description: 'File path to read' }),\n limit: Type.Optional(Type.Number({ description: 'Max lines (default: 500)' })),\n});\n\nexport interface CreateReadFileToolOptions {\n /** When set and the path is a bare bootstrap name (e.g. SOUL.md), try this dir if not in workspace. */\n bootstrapDir?: string;\n}\n\nexport function createReadFileTool(\n workspace: string,\n options?: CreateReadFileToolOptions,\n): AgentTool<typeof ReadFileSchema, {}> {\n return {\n name: 'read_file',\n description:\n 'Read file contents. Relative paths are from the current agent workspace; persona files (SOUL.md, etc.) may live in agent bootstrap and are found automatically when given by filename.',\n parameters: ReadFileSchema,\n label: '📄 Read',\n\n async execute(\n _toolCallId: string,\n params: Static<typeof ReadFileSchema>,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n return executeReadFile(workspace, options?.bootstrapDir, params);\n },\n };\n}\n\nasync function executeReadFile(\n workspace: string,\n bootstrapDir: string | undefined,\n params: Static<typeof ReadFileSchema>,\n): Promise<AgentToolResult<{}>> {\n try {\n const safety = checkFileSafety('read', params.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n let normalized = resolvePathUnderWorkspace(params.path, workspace);\n let stats;\n try {\n stats = await stat(normalized);\n } catch (e) {\n const code = (e as NodeJS.ErrnoException)?.code;\n if (\n code === 'ENOENT' &&\n bootstrapDir &&\n isBareBootstrapFileName(params.path)\n ) {\n const alt = resolveBootstrapPathIfBareName(params.path, bootstrapDir);\n try {\n stats = await stat(alt);\n normalized = alt;\n } catch {\n throw e;\n }\n } else {\n throw e;\n }\n }\n\n if (stats.size > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${formatSize(stats.size)}` }], details: {} };\n }\n\n const content = await readFile(normalized, 'utf-8');\n const truncation = truncateHead(content, { maxLines: params.limit || DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });\n\n let outputText = truncation.content;\n if (truncation.truncated) {\n if (truncation.firstLineExceedsLimit) {\n outputText = `(Line exceeds ${formatSize(DEFAULT_MAX_BYTES)})`;\n } else {\n outputText += `\\n\\n[${truncation.outputLines}/${truncation.totalLines} lines]`;\n }\n }\n\n return { content: [{ type: 'text', text: outputText }], details: {} };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n}\n\n/** @deprecated Use {@link createReadFileTool}(process.cwd()) — default cwd; prefer factory with workspace. */\nexport const readFileTool: AgentTool<typeof ReadFileSchema, {}> = createReadFileTool(process.cwd());\n"],"mappings":";;;;;;AAYA,MAAM,gBAAgB,KAAK,OAAO;AAClC,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,KAAK,OAAO;CACjC,MAAM,KAAK,OAAO,EAAE,aAAa,qBAAqB,CAAC;CACvD,OAAO,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC,CAAC;CAC/E,CAAC;AAOF,SAAgB,mBACd,WACA,SACsC;AACtC,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,UAAO,gBAAgB,WAAW,SAAS,cAAc,OAAO;;EAEnE;;AAGH,eAAe,gBACb,WACA,cACA,QAC8B;AAC9B,KAAI;EACF,MAAM,SAAS,gBAAgB,QAAQ,OAAO,KAAK;AACnD,MAAI,CAAC,OAAO,QACV,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,MAAM,OAAO;IAAW,CAAC;GAAE,SAAS,EAAE;GAAE;EAGnF,IAAI,aAAa,0BAA0B,OAAO,MAAM,UAAU;EAClE,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,KAAK,WAAW;WACvB,GAAG;AAEV,OADc,GAA6B,SAEhC,YACT,gBACA,wBAAwB,OAAO,KAAK,EACpC;IACA,MAAM,MAAM,+BAA+B,OAAO,MAAM,aAAa;AACrE,QAAI;AACF,aAAQ,MAAM,KAAK,IAAI;AACvB,kBAAa;YACP;AACN,WAAM;;SAGR,OAAM;;AAIV,MAAI,MAAM,OAAO,cACf,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,sBAAsB,WAAW,MAAM,KAAK;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE;EAI3G,MAAM,aAAa,aADH,MAAM,SAAS,YAAY,QAAQ,EACV;GAAE,UAAU,OAAO,SAAS;GAAmB,UAAU;GAAmB,CAAC;EAEtH,IAAI,aAAa,WAAW;AAC5B,MAAI,WAAW,UACb,KAAI,WAAW,sBACb,cAAa,iBAAiB,WAAW,kBAAkB,CAAC;MAE5D,eAAc,QAAQ,WAAW,YAAY,GAAG,WAAW,WAAW;AAI1E,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAY,CAAC;GAAE,SAAS,EAAE;GAAE;UAC9D,OAAO;AACd,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE;;;;AAKjI,MAAa,eAAqD,mBAAmB,QAAQ,KAAK,CAAC"}
1
+ {"version":3,"file":"read.js","names":[],"sources":["../../../../src/agent/tools/read.ts"],"sourcesContent":["// Read file tool\nimport { Type, type Static } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { readFile, stat } from 'fs/promises';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { truncateHead, formatSize, DEFAULT_MAX_BYTES } from './truncate.js';\nimport {\n isBareBootstrapFileName,\n resolveBootstrapPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst DEFAULT_MAX_LINES = 500;\n\nconst ReadFileSchema = Type.Object({\n path: Type.String({ description: 'File path to read' }),\n limit: Type.Optional(Type.Number({ description: 'Max lines (default: 500)' })),\n});\n\nexport interface CreateReadFileToolOptions {\n /** When set and the path is a bare bootstrap name (e.g. SOUL.md), try this dir if not in workspace. */\n bootstrapDir?: string;\n}\n\nexport function createReadFileTool(\n workspace: string,\n options?: CreateReadFileToolOptions,\n): AgentTool<typeof ReadFileSchema, {}> {\n return {\n name: 'read_file',\n description:\n 'Read file contents. Relative paths are from the current agent workspace; persona files (SOUL.md, etc.) may live in agent bootstrap and are found automatically when given by filename.',\n parameters: ReadFileSchema,\n label: '📄 Read',\n\n async execute(\n _toolCallId: string,\n params: Static<typeof ReadFileSchema>,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n return executeReadFile(workspace, options?.bootstrapDir, params);\n },\n };\n}\n\nasync function executeReadFile(\n workspace: string,\n bootstrapDir: string | undefined,\n params: Static<typeof ReadFileSchema>,\n): Promise<AgentToolResult<{}>> {\n try {\n const safety = checkFileSafety('read', params.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n let normalized = resolvePathUnderWorkspace(params.path, workspace);\n let stats;\n try {\n stats = await stat(normalized);\n } catch (e) {\n const code = (e as NodeJS.ErrnoException)?.code;\n if (\n code === 'ENOENT' &&\n bootstrapDir &&\n isBareBootstrapFileName(params.path)\n ) {\n const alt = resolveBootstrapPathIfBareName(params.path, bootstrapDir);\n try {\n stats = await stat(alt);\n normalized = alt;\n } catch {\n throw e;\n }\n } else {\n throw e;\n }\n }\n\n if (stats.size > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${formatSize(stats.size)}` }], details: {} };\n }\n\n const content = await readFile(normalized, 'utf-8');\n const truncation = truncateHead(content, { maxLines: params.limit || DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });\n\n let outputText = truncation.content;\n if (truncation.truncated) {\n if (truncation.firstLineExceedsLimit) {\n outputText = `(Line exceeds ${formatSize(DEFAULT_MAX_BYTES)})`;\n } else {\n outputText += `\\n\\n[${truncation.outputLines}/${truncation.totalLines} lines]`;\n }\n }\n\n return { content: [{ type: 'text', text: outputText }], details: {} };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,gBAAgB,KAAK,OAAO;AAClC,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,KAAK,OAAO;CACjC,MAAM,KAAK,OAAO,EAAE,aAAa,qBAAqB,CAAC;CACvD,OAAO,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC,CAAC;CAC/E,CAAC;AAOF,SAAgB,mBACd,WACA,SACsC;AACtC,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,UAAO,gBAAgB,WAAW,SAAS,cAAc,OAAO;;EAEnE;;AAGH,eAAe,gBACb,WACA,cACA,QAC8B;AAC9B,KAAI;EACF,MAAM,SAAS,gBAAgB,QAAQ,OAAO,KAAK;AACnD,MAAI,CAAC,OAAO,QACV,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,MAAM,OAAO;IAAW,CAAC;GAAE,SAAS,EAAE;GAAE;EAGnF,IAAI,aAAa,0BAA0B,OAAO,MAAM,UAAU;EAClE,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,KAAK,WAAW;WACvB,GAAG;AAEV,OADc,GAA6B,SAEhC,YACT,gBACA,wBAAwB,OAAO,KAAK,EACpC;IACA,MAAM,MAAM,+BAA+B,OAAO,MAAM,aAAa;AACrE,QAAI;AACF,aAAQ,MAAM,KAAK,IAAI;AACvB,kBAAa;YACP;AACN,WAAM;;SAGR,OAAM;;AAIV,MAAI,MAAM,OAAO,cACf,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,sBAAsB,WAAW,MAAM,KAAK;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE;EAI3G,MAAM,aAAa,aADH,MAAM,SAAS,YAAY,QAAQ,EACV;GAAE,UAAU,OAAO,SAAS;GAAmB,UAAU;GAAmB,CAAC;EAEtH,IAAI,aAAa,WAAW;AAC5B,MAAI,WAAW,UACb,KAAI,WAAW,sBACb,cAAa,iBAAiB,WAAW,kBAAkB,CAAC;MAE5D,eAAc,QAAQ,WAAW,YAAY,GAAG,WAAW,WAAW;AAI1E,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAY,CAAC;GAAE,SAAS,EAAE;GAAE;UAC9D,OAAO;AACd,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE"}
@@ -1,7 +1,7 @@
1
1
  import { checkFileSafety } from "../prompt/safety.js";
2
2
  import { resolvePathUnderWorkspace } from "./tool-paths.js";
3
- import { readFile } from "fs/promises";
4
3
  import { basename } from "node:path";
4
+ import { readFile } from "fs/promises";
5
5
  import { Type } from "@sinclair/typebox";
6
6
  //#region src/agent/tools/send-media.ts
7
7
  const SendMediaSchema = Type.Object({
@@ -1,7 +1,7 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
2
3
  import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveOAuthPath } from "../config/paths.js";
3
4
  import { init_loader, loadConfig } from "../config/loader.js";
4
- import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
5
5
  import { existsSync, readFileSync } from "node:fs";
6
6
  //#region src/auth/sync-provider-auth.ts
7
7
  /**
@@ -69,6 +69,18 @@ export interface ChannelPluginReloadMeta {
69
69
  /** Config path prefixes that hot-reload this plugin (for docs and tooling). */
70
70
  configPrefixes: string[];
71
71
  }
72
+ export interface ChannelDoctorCheckResult {
73
+ id: string;
74
+ label: string;
75
+ status: 'pass' | 'warn' | 'fail' | 'skip';
76
+ message: string;
77
+ hints: string[];
78
+ }
79
+ export interface ChannelDoctorAdapter {
80
+ check(params: {
81
+ cfg: Config;
82
+ }): Promise<ChannelDoctorCheckResult[]>;
83
+ }
72
84
  export interface ChannelPlugin<ResolvedAccount = any> {
73
85
  id: ChannelId;
74
86
  meta: ChannelMeta;
@@ -117,6 +129,8 @@ export interface ChannelPlugin<ResolvedAccount = any> {
117
129
  heartbeat?: ChannelHeartbeatAdapter;
118
130
  agentPrompt?: ChannelAgentPromptAdapter;
119
131
  agentTools?: ChannelAgentTool[];
132
+ /** Optional doctor health-check contributed by this channel plugin. */
133
+ doctor?: ChannelDoctorAdapter;
120
134
  }
121
135
  export interface ChannelConfigAdapter<ResolvedAccount> {
122
136
  /** List all account IDs */
@@ -1,6 +1,6 @@
1
+ import { getWorkspacePath } from "../../config/schema.js";
1
2
  import { createLogger } from "../../utils/logger/index.js";
2
3
  import { init_logger } from "../../utils/logger.js";
3
- import { getWorkspacePath } from "../../config/schema.js";
4
4
  import { loadConfig } from "../../config/loader.js";
5
5
  import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
6
6
  import "../../infra/bus/index.js";
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkChannelConfig(ctx: DoctorContext): Promise<CheckResult>;
@@ -0,0 +1,113 @@
1
+ import { init_loader, loadConfig } from "../../../../config/loader.js";
2
+ import { existsSync } from "node:fs";
3
+ //#region src/cli/commands/doctor/checks/channel-config.ts
4
+ init_loader();
5
+ function checkTelegram(cfg) {
6
+ const tg = cfg.channels?.telegram;
7
+ if (!tg) return {
8
+ ok: true,
9
+ messages: [],
10
+ hints: []
11
+ };
12
+ const legacyToken = tg.botToken?.trim() ?? "";
13
+ const defaultAcc = tg.accounts?.default;
14
+ const token = (defaultAcc?.botToken?.trim() || legacyToken) ?? "";
15
+ if (!(tg.enabled === true || token.length > 0)) return {
16
+ ok: true,
17
+ messages: [],
18
+ hints: []
19
+ };
20
+ const messages = [];
21
+ const hints = [];
22
+ if (!token) {
23
+ messages.push("Telegram is enabled but no bot token is set.");
24
+ hints.push("Set channels.telegram.accounts.default.botToken or legacy channels.telegram.botToken.");
25
+ }
26
+ const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || "pairing";
27
+ if (![
28
+ "pairing",
29
+ "allowlist",
30
+ "open",
31
+ "disabled"
32
+ ].includes(dm)) messages.push(`Telegram dmPolicy "${dm}" is not valid.`);
33
+ if (dm === "open") {
34
+ messages.push("Telegram DM policy is \"open\" (any user can message the bot).");
35
+ hints.push("Consider \"pairing\" or \"allowlist\" for stricter access.");
36
+ }
37
+ return {
38
+ ok: messages.length === 0,
39
+ messages,
40
+ hints
41
+ };
42
+ }
43
+ function checkWeixin(cfg) {
44
+ const wx = cfg.channels?.weixin;
45
+ if (!wx || wx.enabled !== true) return {
46
+ ok: true,
47
+ messages: [],
48
+ hints: []
49
+ };
50
+ const messages = [];
51
+ const hints = [];
52
+ if ((wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : []).length === 0) {
53
+ messages.push("Weixin is enabled but no accounts are defined in config.");
54
+ hints.push("Run: xopc channels weixin login (or add channels.weixin.accounts).");
55
+ }
56
+ return {
57
+ ok: messages.length === 0,
58
+ messages,
59
+ hints
60
+ };
61
+ }
62
+ async function checkChannelConfig(ctx) {
63
+ if (!existsSync(ctx.configPath)) return {
64
+ id: "channel-config",
65
+ label: "Channels",
66
+ status: "skip",
67
+ message: "No config file; skipped.",
68
+ hints: []
69
+ };
70
+ let cfg;
71
+ try {
72
+ cfg = loadConfig(ctx.configPath);
73
+ } catch {
74
+ return {
75
+ id: "channel-config",
76
+ label: "Channels",
77
+ status: "skip",
78
+ message: "Config could not be loaded; skipped.",
79
+ hints: []
80
+ };
81
+ }
82
+ const tg = checkTelegram(cfg);
83
+ const wx = checkWeixin(cfg);
84
+ const allMsg = [...tg.messages, ...wx.messages];
85
+ const allHints = [...tg.hints, ...wx.hints];
86
+ const tgEnabled = (cfg.channels?.telegram)?.enabled === true || Boolean((cfg.channels?.telegram)?.botToken?.trim());
87
+ const wxOn = (cfg.channels?.weixin)?.enabled === true;
88
+ if (!tgEnabled && !wxOn) return {
89
+ id: "channel-config",
90
+ label: "Channels",
91
+ status: "skip",
92
+ message: "No channels enabled; skipped.",
93
+ hints: []
94
+ };
95
+ if (allMsg.length === 0) return {
96
+ id: "channel-config",
97
+ label: "Channels",
98
+ status: "pass",
99
+ message: "Enabled channel configuration looks valid.",
100
+ hints: []
101
+ };
102
+ return {
103
+ id: "channel-config",
104
+ label: "Channels",
105
+ status: allMsg.some((m) => m.includes("no bot token") || m.includes("no accounts")) ? "fail" : "warn",
106
+ message: allMsg.join(" "),
107
+ hints: allHints
108
+ };
109
+ }
110
+ //#endregion
111
+ export { checkChannelConfig };
112
+
113
+ //# sourceMappingURL=channel-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-config.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ntype TelegramCfg = {\n enabled?: boolean;\n botToken?: string;\n accounts?: Record<string, { botToken?: string; dmPolicy?: string; enabled?: boolean }>;\n dmPolicy?: string;\n};\n\ntype WeixinCfg = {\n enabled?: boolean;\n accounts?: Record<string, unknown>;\n};\n\nfunction checkTelegram(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const tg = cfg.channels?.telegram as TelegramCfg | undefined;\n if (!tg) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const legacyToken = tg.botToken?.trim() ?? '';\n const defaultAcc = tg.accounts?.default;\n const token = (defaultAcc?.botToken?.trim() || legacyToken) ?? '';\n const enabled = tg.enabled === true || token.length > 0;\n\n if (!enabled) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n\n if (!token) {\n messages.push('Telegram is enabled but no bot token is set.');\n hints.push('Set channels.telegram.accounts.default.botToken or legacy channels.telegram.botToken.');\n }\n\n const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || 'pairing';\n if (!['pairing', 'allowlist', 'open', 'disabled'].includes(dm)) {\n messages.push(`Telegram dmPolicy \"${dm}\" is not valid.`);\n }\n if (dm === 'open') {\n messages.push('Telegram DM policy is \"open\" (any user can message the bot).');\n hints.push('Consider \"pairing\" or \"allowlist\" for stricter access.');\n }\n\n return {\n ok: messages.length === 0,\n messages,\n hints,\n };\n}\n\nfunction checkWeixin(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const wx = cfg.channels?.weixin as WeixinCfg | undefined;\n if (!wx || wx.enabled !== true) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n const accountKeys = wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : [];\n if (accountKeys.length === 0) {\n messages.push('Weixin is enabled but no accounts are defined in config.');\n hints.push('Run: xopc channels weixin login (or add channels.weixin.accounts).');\n }\n\n return { ok: messages.length === 0, messages, hints };\n}\n\nexport async function checkChannelConfig(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const tg = checkTelegram(cfg);\n const wx = checkWeixin(cfg);\n const allMsg = [...tg.messages, ...wx.messages];\n const allHints = [...tg.hints, ...wx.hints];\n\n const tgEnabled =\n (cfg.channels?.telegram as TelegramCfg | undefined)?.enabled === true ||\n Boolean((cfg.channels?.telegram as TelegramCfg | undefined)?.botToken?.trim());\n const wxOn = (cfg.channels?.weixin as WeixinCfg | undefined)?.enabled === true;\n if (!tgEnabled && !wxOn) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No channels enabled; skipped.',\n hints: [],\n };\n }\n\n if (allMsg.length === 0) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'pass',\n message: 'Enabled channel configuration looks valid.',\n hints: [],\n };\n }\n\n const hasFail = allMsg.some((m) => m.includes('no bot token') || m.includes('no accounts'));\n return {\n id: 'channel-config',\n label: 'Channels',\n status: hasFail ? 'fail' : 'warn',\n message: allMsg.join(' '),\n hints: allHints,\n };\n}\n"],"mappings":";;;aAE0D;AAgB1D,SAAS,cAAc,KAAmE;CACxF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,GACH,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,cAAc,GAAG,UAAU,MAAM,IAAI;CAC3C,MAAM,aAAa,GAAG,UAAU;CAChC,MAAM,SAAS,YAAY,UAAU,MAAM,IAAI,gBAAgB;AAG/D,KAAI,EAFY,GAAG,YAAY,QAAQ,MAAM,SAAS,GAGpD,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,OAAO;AACV,WAAS,KAAK,+CAA+C;AAC7D,QAAM,KAAK,wFAAwF;;CAGrG,MAAM,MAAM,YAAY,YAAY,GAAG,aAAa;AACpD,KAAI,CAAC;EAAC;EAAW;EAAa;EAAQ;EAAW,CAAC,SAAS,GAAG,CAC5D,UAAS,KAAK,sBAAsB,GAAG,iBAAiB;AAE1D,KAAI,OAAO,QAAQ;AACjB,WAAS,KAAK,iEAA+D;AAC7E,QAAM,KAAK,6DAAyD;;AAGtE,QAAO;EACL,IAAI,SAAS,WAAW;EACxB;EACA;EACD;;AAGH,SAAS,YAAY,KAAmE;CACtF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,MAAM,GAAG,YAAY,KACxB,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,MADoB,GAAG,WAAW,OAAO,KAAK,GAAG,SAAS,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,EACvE,WAAW,GAAG;AAC5B,WAAS,KAAK,2DAA2D;AACzE,QAAM,KAAK,qEAAqE;;AAGlF,QAAO;EAAE,IAAI,SAAS,WAAW;EAAG;EAAU;EAAO;;AAGvD,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,KAAK,cAAc,IAAI;CAC7B,MAAM,KAAK,YAAY,IAAI;CAC3B,MAAM,SAAS,CAAC,GAAG,GAAG,UAAU,GAAG,GAAG,SAAS;CAC/C,MAAM,WAAW,CAAC,GAAG,GAAG,OAAO,GAAG,GAAG,MAAM;CAE3C,MAAM,aACH,IAAI,UAAU,WAAsC,YAAY,QACjE,SAAS,IAAI,UAAU,WAAsC,UAAU,MAAM,CAAC;CAChF,MAAM,QAAQ,IAAI,UAAU,SAAkC,YAAY;AAC1E,KAAI,CAAC,aAAa,CAAC,KACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAGH,KAAI,OAAO,WAAW,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAIH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAJc,OAAO,MAAM,MAAM,EAAE,SAAS,eAAe,IAAI,EAAE,SAAS,cAAc,CAAC,GAIvE,SAAS;EAC3B,SAAS,OAAO,KAAK,IAAI;EACzB,OAAO;EACR"}
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkChannelPlugins(ctx: DoctorContext): Promise<CheckResult[]>;
@@ -0,0 +1,47 @@
1
+ import { init_loader, loadConfig } from "../../../../config/loader.js";
2
+ import { bundledChannelPlugins } from "../../../../generated/bundled-channel-plugins.js";
3
+ import { existsSync } from "node:fs";
4
+ //#region src/cli/commands/doctor/checks/channel-plugins.ts
5
+ init_loader();
6
+ function toCheckResults(plugin, raw) {
7
+ const pid = String(plugin.id);
8
+ return raw.map((r) => ({
9
+ id: `channel:${pid}:${r.id}`,
10
+ label: `${pid}: ${r.label}`,
11
+ status: r.status,
12
+ message: r.message,
13
+ hints: r.hints
14
+ }));
15
+ }
16
+ async function checkChannelPlugins(ctx) {
17
+ if (!existsSync(ctx.configPath)) return [];
18
+ let cfg;
19
+ try {
20
+ cfg = loadConfig(ctx.configPath);
21
+ } catch {
22
+ return [];
23
+ }
24
+ const out = [];
25
+ for (const plugin of bundledChannelPlugins) {
26
+ const doctor = plugin.doctor;
27
+ if (!doctor?.check) continue;
28
+ try {
29
+ const res = await doctor.check({ cfg });
30
+ out.push(...toCheckResults(plugin, res));
31
+ } catch (e) {
32
+ const msg = e instanceof Error ? e.message : String(e);
33
+ out.push({
34
+ id: `channel:${plugin.id}:error`,
35
+ label: `${String(plugin.id)}: doctor`,
36
+ status: "warn",
37
+ message: `Channel doctor check failed: ${msg}`,
38
+ hints: []
39
+ });
40
+ }
41
+ }
42
+ return out;
43
+ }
44
+ //#endregion
45
+ export { checkChannelPlugins };
46
+
47
+ //# sourceMappingURL=channel-plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-plugins.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-plugins.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport type { CheckResult, DoctorContext } from '../types.js';\nimport { loadConfig } from '../../../../config/loader.js';\nimport { bundledChannelPlugins } from '../../../../generated/bundled-channel-plugins.js';\nimport type { ChannelDoctorCheckResult, ChannelPlugin } from '../../../../channels/plugin-types.js';\n\nfunction toCheckResults(plugin: ChannelPlugin, raw: ChannelDoctorCheckResult[]): CheckResult[] {\n const pid = String(plugin.id);\n return raw.map((r) => ({\n id: `channel:${pid}:${r.id}`,\n label: `${pid}: ${r.label}`,\n status: r.status,\n message: r.message,\n hints: r.hints,\n }));\n}\n\nexport async function checkChannelPlugins(ctx: DoctorContext): Promise<CheckResult[]> {\n if (!existsSync(ctx.configPath)) {\n return [];\n }\n\n let cfg;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return [];\n }\n\n const out: CheckResult[] = [];\n for (const plugin of bundledChannelPlugins) {\n const doctor = plugin.doctor;\n if (!doctor?.check) continue;\n try {\n const res = await doctor.check({ cfg });\n out.push(...toCheckResults(plugin, res));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n out.push({\n id: `channel:${plugin.id}:error`,\n label: `${String(plugin.id)}: doctor`,\n status: 'warn',\n message: `Channel doctor check failed: ${msg}`,\n hints: [],\n });\n }\n }\n return out;\n}\n"],"mappings":";;;;aAG0D;AAI1D,SAAS,eAAe,QAAuB,KAAgD;CAC7F,MAAM,MAAM,OAAO,OAAO,GAAG;AAC7B,QAAO,IAAI,KAAK,OAAO;EACrB,IAAI,WAAW,IAAI,GAAG,EAAE;EACxB,OAAO,GAAG,IAAI,IAAI,EAAE;EACpB,QAAQ,EAAE;EACV,SAAS,EAAE;EACX,OAAO,EAAE;EACV,EAAE;;AAGL,eAAsB,oBAAoB,KAA4C;AACpF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO,EAAE;CAGX,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO,EAAE;;CAGX,MAAM,MAAqB,EAAE;AAC7B,MAAK,MAAM,UAAU,uBAAuB;EAC1C,MAAM,SAAS,OAAO;AACtB,MAAI,CAAC,QAAQ,MAAO;AACpB,MAAI;GACF,MAAM,MAAM,MAAM,OAAO,MAAM,EAAE,KAAK,CAAC;AACvC,OAAI,KAAK,GAAG,eAAe,QAAQ,IAAI,CAAC;WACjC,GAAG;GACV,MAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AACtD,OAAI,KAAK;IACP,IAAI,WAAW,OAAO,GAAG;IACzB,OAAO,GAAG,OAAO,OAAO,GAAG,CAAC;IAC5B,QAAQ;IACR,SAAS,gCAAgC;IACzC,OAAO,EAAE;IACV,CAAC;;;AAGN,QAAO"}
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkConfigHealth(ctx: DoctorContext): Promise<CheckResult>;
@@ -0,0 +1,82 @@
1
+ import { ConfigSchema, init_schema } from "../../../../config/schema.js";
2
+ import { init_loader, loadConfig, saveConfig } from "../../../../config/loader.js";
3
+ import { dirname } from "node:path";
4
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
5
+ //#region src/cli/commands/doctor/checks/config-health.ts
6
+ init_loader();
7
+ init_schema();
8
+ async function checkConfigHealth(ctx) {
9
+ const path = ctx.configPath;
10
+ if (!existsSync(path)) {
11
+ if (ctx.options.fix) try {
12
+ mkdirSync(dirname(path), { recursive: true });
13
+ await saveConfig(loadConfig(path), path);
14
+ return {
15
+ id: "config-health",
16
+ label: "Config",
17
+ status: "pass",
18
+ message: "Created default config file.",
19
+ hints: [path],
20
+ fixed: true
21
+ };
22
+ } catch (e) {
23
+ return {
24
+ id: "config-health",
25
+ label: "Config",
26
+ status: "fail",
27
+ message: `Config file missing and could not create default: ${e instanceof Error ? e.message : String(e)}`,
28
+ hints: [path]
29
+ };
30
+ }
31
+ return {
32
+ id: "config-health",
33
+ label: "Config",
34
+ status: "fail",
35
+ message: "Config file not found.",
36
+ hints: [`Run: xopc init`, `Or: xopc doctor --fix`]
37
+ };
38
+ }
39
+ let raw;
40
+ try {
41
+ raw = readFileSync(path, "utf-8");
42
+ } catch (e) {
43
+ return {
44
+ id: "config-health",
45
+ label: "Config",
46
+ status: "fail",
47
+ message: `Cannot read config file: ${e instanceof Error ? e.message : String(e)}`,
48
+ hints: [path]
49
+ };
50
+ }
51
+ let json;
52
+ try {
53
+ json = JSON.parse(raw);
54
+ } catch {
55
+ return {
56
+ id: "config-health",
57
+ label: "Config",
58
+ status: "fail",
59
+ message: "Config file is not valid JSON.",
60
+ hints: ["Fix syntax or restore from backup (.bak).", path]
61
+ };
62
+ }
63
+ const parsed = ConfigSchema.safeParse(json);
64
+ if (!parsed.success) return {
65
+ id: "config-health",
66
+ label: "Config",
67
+ status: "fail",
68
+ message: "Config does not match the expected schema.",
69
+ hints: parsed.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`)
70
+ };
71
+ return {
72
+ id: "config-health",
73
+ label: "Config",
74
+ status: "pass",
75
+ message: "Config file exists and validates.",
76
+ hints: [path]
77
+ };
78
+ }
79
+ //#endregion
80
+ export { checkConfigHealth };
81
+
82
+ //# sourceMappingURL=config-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/config-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\nimport { loadConfig, saveConfig } from '../../../../config/loader.js';\nimport { ConfigSchema } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkConfigHealth(ctx: DoctorContext): Promise<CheckResult> {\n const path = ctx.configPath;\n\n if (!existsSync(path)) {\n if (ctx.options.fix) {\n try {\n const dir = dirname(path);\n mkdirSync(dir, { recursive: true });\n const defaults = loadConfig(path);\n await saveConfig(defaults, path);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Created default config file.',\n hints: [path],\n fixed: true,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Config file missing and could not create default: ${msg}`,\n hints: [path],\n };\n }\n }\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file not found.',\n hints: [`Run: xopc init`, `Or: xopc doctor --fix`],\n };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Cannot read config file: ${msg}`,\n hints: [path],\n };\n }\n\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file is not valid JSON.',\n hints: ['Fix syntax or restore from backup (.bak).', path],\n };\n }\n\n const parsed = ConfigSchema.safeParse(json);\n if (!parsed.success) {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config does not match the expected schema.',\n hints: parsed.error.issues.slice(0, 5).map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`),\n };\n }\n\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Config file exists and validates.',\n hints: [path],\n };\n}\n"],"mappings":";;;;;aAIsE;aACV;AAG5D,eAAsB,kBAAkB,KAA0C;CAChF,MAAM,OAAO,IAAI;AAEjB,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,MAAI,IAAI,QAAQ,IACd,KAAI;AAEF,aADY,QAAQ,KAAK,EACV,EAAE,WAAW,MAAM,CAAC;AAEnC,SAAM,WADW,WAAW,KAAK,EACN,KAAK;AAChC,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,KAAK;IACb,OAAO;IACR;WACM,GAAG;AAEV,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS,qDALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IAMpD,OAAO,CAAC,KAAK;IACd;;AAGL,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,kBAAkB,wBAAwB;GACnD;;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,aAAa,MAAM,QAAQ;UAC1B,GAAG;AAEV,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,4BALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;GAMpD,OAAO,CAAC,KAAK;GACd;;CAGH,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,6CAA6C,KAAK;GAC3D;;CAGH,MAAM,SAAS,aAAa,UAAU,KAAK;AAC3C,KAAI,CAAC,OAAO,QACV,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,OAAO,MAAM,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE,UAAU;EACnG;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,KAAK;EACd"}
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkCronHealth(ctx: DoctorContext): Promise<CheckResult>;
@@ -0,0 +1,116 @@
1
+ import { init_paths, resolveCronDir, resolveCronJobsPath } from "../../../../config/paths.js";
2
+ import { init_loader, loadConfig } from "../../../../config/loader.js";
3
+ import { JobDataSchema } from "../../../../cron/validation.js";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ //#region src/cli/commands/doctor/checks/cron-health.ts
6
+ init_loader();
7
+ init_paths();
8
+ async function checkCronHealth(ctx) {
9
+ if (!existsSync(ctx.configPath)) return {
10
+ id: "cron-health",
11
+ label: "Cron",
12
+ status: "skip",
13
+ message: "No config file; skipped.",
14
+ hints: []
15
+ };
16
+ let cfg;
17
+ try {
18
+ cfg = loadConfig(ctx.configPath);
19
+ } catch {
20
+ return {
21
+ id: "cron-health",
22
+ label: "Cron",
23
+ status: "skip",
24
+ message: "Config could not be loaded; skipped.",
25
+ hints: []
26
+ };
27
+ }
28
+ if (cfg.cron?.enabled === false) return {
29
+ id: "cron-health",
30
+ label: "Cron",
31
+ status: "skip",
32
+ message: "Cron is disabled in config; skipped.",
33
+ hints: []
34
+ };
35
+ const cronDir = resolveCronDir();
36
+ if (!existsSync(cronDir)) return {
37
+ id: "cron-health",
38
+ label: "Cron",
39
+ status: "warn",
40
+ message: "Cron directory does not exist.",
41
+ hints: [cronDir, "Run: xopc init"]
42
+ };
43
+ const jobsPath = resolveCronJobsPath();
44
+ if (!existsSync(jobsPath)) return {
45
+ id: "cron-health",
46
+ label: "Cron",
47
+ status: "warn",
48
+ message: "Cron jobs file is missing.",
49
+ hints: [jobsPath]
50
+ };
51
+ let raw;
52
+ try {
53
+ raw = JSON.parse(readFileSync(jobsPath, "utf-8"));
54
+ } catch {
55
+ return {
56
+ id: "cron-health",
57
+ label: "Cron",
58
+ status: "warn",
59
+ message: "Cron jobs file is not valid JSON.",
60
+ hints: [jobsPath]
61
+ };
62
+ }
63
+ if (!raw || typeof raw !== "object" || !("jobs" in raw) || !Array.isArray(raw.jobs)) return {
64
+ id: "cron-health",
65
+ label: "Cron",
66
+ status: "warn",
67
+ message: "Cron jobs file has invalid structure (expected { jobs: [] }).",
68
+ hints: [jobsPath]
69
+ };
70
+ const jobs = raw.jobs;
71
+ const hints = [];
72
+ let valid = 0;
73
+ let enabled = 0;
74
+ let scheduleMissing = 0;
75
+ for (const j of jobs) {
76
+ const r = JobDataSchema.safeParse(j);
77
+ if (r.success) {
78
+ valid++;
79
+ if (r.data.enabled) {
80
+ enabled++;
81
+ if (!r.data.schedule?.trim()) {
82
+ scheduleMissing++;
83
+ hints.push(`Job "${r.data.name || r.data.id}" is enabled but has no schedule.`);
84
+ }
85
+ }
86
+ } else {
87
+ hints.push("One or more job entries failed validation (check jobs.json).");
88
+ break;
89
+ }
90
+ }
91
+ if (valid !== jobs.length) return {
92
+ id: "cron-health",
93
+ label: "Cron",
94
+ status: "warn",
95
+ message: "Some cron jobs are invalid or could not be validated.",
96
+ hints: hints.length ? hints.slice(0, 5) : [jobsPath]
97
+ };
98
+ if (scheduleMissing > 0) return {
99
+ id: "cron-health",
100
+ label: "Cron",
101
+ status: "warn",
102
+ message: `${scheduleMissing} enabled job(s) are missing a schedule.`,
103
+ hints: hints.slice(0, 5)
104
+ };
105
+ return {
106
+ id: "cron-health",
107
+ label: "Cron",
108
+ status: "pass",
109
+ message: `Cron jobs file is valid (${enabled} enabled, ${jobs.length} total).`,
110
+ hints: [jobsPath]
111
+ };
112
+ }
113
+ //#endregion
114
+ export { checkCronHealth };
115
+
116
+ //# sourceMappingURL=cron-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/cron-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport { resolveCronDir, resolveCronJobsPath } from '../../../../config/paths.js';\nimport { JobDataSchema } from '../../../../cron/validation.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkCronHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n if (cfg.cron?.enabled === false) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Cron is disabled in config; skipped.',\n hints: [],\n };\n }\n\n const cronDir = resolveCronDir();\n if (!existsSync(cronDir)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron directory does not exist.',\n hints: [cronDir, 'Run: xopc init'],\n };\n }\n\n const jobsPath = resolveCronJobsPath();\n if (!existsSync(jobsPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is missing.',\n hints: [jobsPath],\n };\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(jobsPath, 'utf-8'));\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is not valid JSON.',\n hints: [jobsPath],\n };\n }\n\n if (!raw || typeof raw !== 'object' || !('jobs' in raw) || !Array.isArray((raw as { jobs: unknown }).jobs)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file has invalid structure (expected { jobs: [] }).',\n hints: [jobsPath],\n };\n }\n\n const jobs = (raw as { jobs: unknown[] }).jobs;\n const hints: string[] = [];\n let valid = 0;\n let enabled = 0;\n let scheduleMissing = 0;\n\n for (const j of jobs) {\n const r = JobDataSchema.safeParse(j);\n if (r.success) {\n valid++;\n if (r.data.enabled) {\n enabled++;\n const sched = r.data.schedule?.trim();\n if (!sched) {\n scheduleMissing++;\n hints.push(`Job \"${r.data.name || r.data.id}\" is enabled but has no schedule.`);\n }\n }\n } else {\n hints.push('One or more job entries failed validation (check jobs.json).');\n break;\n }\n }\n\n if (valid !== jobs.length) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Some cron jobs are invalid or could not be validated.',\n hints: hints.length ? hints.slice(0, 5) : [jobsPath],\n };\n }\n\n if (scheduleMissing > 0) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: `${scheduleMissing} enabled job(s) are missing a schedule.`,\n hints: hints.slice(0, 5),\n };\n }\n\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'pass',\n message: `Cron jobs file is valid (${enabled} enabled, ${jobs.length} total).`,\n hints: [jobsPath],\n };\n}\n"],"mappings":";;;;;aAE0D;YAEwB;AAIlF,eAAsB,gBAAgB,KAA0C;AAC9E,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;AAGH,KAAI,IAAI,MAAM,YAAY,MACxB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,MAAM,UAAU,gBAAgB;AAChC,KAAI,CAAC,WAAW,QAAQ,CACtB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS,iBAAiB;EACnC;CAGH,MAAM,WAAW,qBAAqB;AACtC,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS;EAClB;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,KAAK,MAAM,aAAa,UAAU,QAAQ,CAAC;SAC3C;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,SAAS;GAClB;;AAGH,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,EAAE,UAAU,QAAQ,CAAC,MAAM,QAAS,IAA0B,KAAK,CACxG,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS;EAClB;CAGH,MAAM,OAAQ,IAA4B;CAC1C,MAAM,QAAkB,EAAE;CAC1B,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI,kBAAkB;AAEtB,MAAK,MAAM,KAAK,MAAM;EACpB,MAAM,IAAI,cAAc,UAAU,EAAE;AACpC,MAAI,EAAE,SAAS;AACb;AACA,OAAI,EAAE,KAAK,SAAS;AAClB;AAEA,QAAI,CADU,EAAE,KAAK,UAAU,MAAM,EACzB;AACV;AACA,WAAM,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,GAAG,mCAAmC;;;SAG9E;AACL,SAAM,KAAK,+DAA+D;AAC1E;;;AAIJ,KAAI,UAAU,KAAK,OACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,MAAM,SAAS,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,SAAS;EACrD;AAGH,KAAI,kBAAkB,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,GAAG,gBAAgB;EAC5B,OAAO,MAAM,MAAM,GAAG,EAAE;EACzB;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,4BAA4B,QAAQ,YAAY,KAAK,OAAO;EACrE,OAAO,CAAC,SAAS;EAClB"}
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkGatewayHealth(ctx: DoctorContext): Promise<CheckResult>;
@@ -0,0 +1,64 @@
1
+ import { init_loader, loadConfig } from "../../../../config/loader.js";
2
+ import { existsSync } from "node:fs";
3
+ //#region src/cli/commands/doctor/checks/gateway-health.ts
4
+ init_loader();
5
+ function resolveGatewayBaseUrl(cfg) {
6
+ return `http://${cfg.gateway?.host?.trim() || "127.0.0.1"}:${cfg.gateway?.port ?? 18790}`;
7
+ }
8
+ const FETCH_TIMEOUT_MS = 5e3;
9
+ async function checkGatewayHealth(ctx) {
10
+ if (!existsSync(ctx.configPath)) return {
11
+ id: "gateway-health",
12
+ label: "Gateway HTTP",
13
+ status: "skip",
14
+ message: "No config file; skipped.",
15
+ hints: []
16
+ };
17
+ let cfg;
18
+ try {
19
+ cfg = loadConfig(ctx.configPath);
20
+ } catch {
21
+ return {
22
+ id: "gateway-health",
23
+ label: "Gateway HTTP",
24
+ status: "skip",
25
+ message: "Config could not be loaded; skipped.",
26
+ hints: []
27
+ };
28
+ }
29
+ const base = resolveGatewayBaseUrl(cfg);
30
+ const url = `${base.replace(/\/$/, "")}/health`;
31
+ const controller = new AbortController();
32
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
33
+ try {
34
+ const res = await fetch(url, { signal: controller.signal });
35
+ clearTimeout(timer);
36
+ if (res.ok) return {
37
+ id: "gateway-health",
38
+ label: "Gateway HTTP",
39
+ status: "pass",
40
+ message: `Gateway responded OK at ${url}.`,
41
+ hints: []
42
+ };
43
+ return {
44
+ id: "gateway-health",
45
+ label: "Gateway HTTP",
46
+ status: "warn",
47
+ message: `Gateway returned HTTP ${res.status} at ${url}.`,
48
+ hints: ["Start the gateway: xopc gateway start"]
49
+ };
50
+ } catch (e) {
51
+ clearTimeout(timer);
52
+ return {
53
+ id: "gateway-health",
54
+ label: "Gateway HTTP",
55
+ status: "warn",
56
+ message: e instanceof Error && e.name === "AbortError" ? `Gateway did not respond within ${FETCH_TIMEOUT_MS / 1e3}s (${url}).` : `Gateway not reachable (${url}).`,
57
+ hints: ["Start the gateway: xopc gateway start", `Configured base: ${base}`]
58
+ };
59
+ }
60
+ }
61
+ //#endregion
62
+ export { checkGatewayHealth };
63
+
64
+ //# sourceMappingURL=gateway-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/gateway-health.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nfunction resolveGatewayBaseUrl(cfg: Config): string {\n const host = cfg.gateway?.host?.trim() || '127.0.0.1';\n const port = cfg.gateway?.port ?? 18790;\n return `http://${host}:${port}`;\n}\n\nconst FETCH_TIMEOUT_MS = 5000;\n\nexport async function checkGatewayHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const base = resolveGatewayBaseUrl(cfg);\n const url = `${base.replace(/\\/$/, '')}/health`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n if (res.ok) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'pass',\n message: `Gateway responded OK at ${url}.`,\n hints: [],\n };\n }\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: `Gateway returned HTTP ${res.status} at ${url}.`,\n hints: ['Start the gateway: xopc gateway start'],\n };\n } catch (e) {\n clearTimeout(timer);\n const isAbort = e instanceof Error && e.name === 'AbortError';\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: isAbort\n ? `Gateway did not respond within ${FETCH_TIMEOUT_MS / 1000}s (${url}).`\n : `Gateway not reachable (${url}).`,\n hints: ['Start the gateway: xopc gateway start', `Configured base: ${base}`],\n };\n }\n}\n"],"mappings":";;;aAE0D;AAI1D,SAAS,sBAAsB,KAAqB;AAGlD,QAAO,UAFM,IAAI,SAAS,MAAM,MAAM,IAAI,YAEpB,GADT,IAAI,SAAS,QAAQ;;AAIpC,MAAM,mBAAmB;AAEzB,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,OAAO,sBAAsB,IAAI;CACvC,MAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,CAAC;CACvC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,iBAAiB;AAEpE,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC;AAC3D,eAAa,MAAM;AACnB,MAAI,IAAI,GACN,QAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,2BAA2B,IAAI;GACxC,OAAO,EAAE;GACV;AAEH,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,yBAAyB,IAAI,OAAO,MAAM,IAAI;GACvD,OAAO,CAAC,wCAAwC;GACjD;UACM,GAAG;AACV,eAAa,MAAM;AAEnB,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SALc,aAAa,SAAS,EAAE,SAAS,eAM3C,kCAAkC,mBAAmB,IAAK,KAAK,IAAI,MACnE,0BAA0B,IAAI;GAClC,OAAO,CAAC,yCAAyC,oBAAoB,OAAO;GAC7E"}
@@ -0,0 +1,2 @@
1
+ import type { CheckResult, DoctorContext } from '../types.js';
2
+ export declare function checkGatewayService(ctx: DoctorContext): Promise<CheckResult>;