@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.
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BSNzJWbQ.js} +2 -2
- package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js.map → agents-BSNzJWbQ.js.map} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-BKk9SB4D.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-BKk9SB4D.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-_J6cQN6G.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-_J6cQN6G.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-DPb_0O8M.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-DPb_0O8M.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-BUJOuuKX.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-BUJOuuKX.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Cn0YVg8x.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Cn0YVg8x.js.map} +1 -1
- package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js +2 -0
- package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js.map +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-DTz4O5Ua.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-DTz4O5Ua.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-Cs1Kde9o.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-Cs1Kde9o.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-G52iX0Bo.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-G52iX0Bo.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-CO2jxBA9.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-CO2jxBA9.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-D9Ul8uSt.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-D9Ul8uSt.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Bc8SVD15.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Bc8SVD15.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-BXUJbteW.js +16 -0
- package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +1 -0
- package/dist/gateway/static/root/assets/index-CQLMxWSA.css +2 -0
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-5V25JkQY.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-5V25JkQY.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-he3aQfme.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-he3aQfme.js.map} +1 -1
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
- package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-DJHD9Ean.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-n-4O5d9U.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-n-4O5d9U.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-B6dHLvbr.js} +3 -3
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-B6dHLvbr.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-rBUfTdm3.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-rBUfTdm3.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-vxtE8kI6.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-vxtE8kI6.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-D36_O2Ub.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-D36_O2Ub.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CmiSsYBd.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CmiSsYBd.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js +2 -0
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-DYORQ7x6.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-DYORQ7x6.js.map} +1 -1
- package/dist/gateway/static/root/index.html +16 -16
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +4 -4
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/image/index.d.ts +0 -1
- package/dist/src/agent/image/index.js +1 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
- package/dist/src/agent/service.js +3 -3
- package/dist/src/agent/skills/hub-hash.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tools.js +2 -2
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/image-tool.js +2 -2
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -1
- package/dist/src/agent/tools/index.js +2 -2
- package/dist/src/agent/tools/read.d.ts +0 -2
- package/dist/src/agent/tools/read.js +1 -3
- package/dist/src/agent/tools/read.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/plugin-types.d.ts +14 -0
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
- package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
- package/dist/src/cli/commands/doctor/flow.js +51 -0
- package/dist/src/cli/commands/doctor/flow.js.map +1 -0
- package/dist/src/cli/commands/doctor/format.d.ts +6 -0
- package/dist/src/cli/commands/doctor/format.js +61 -0
- package/dist/src/cli/commands/doctor/format.js.map +1 -0
- package/dist/src/cli/commands/doctor/index.js +44 -0
- package/dist/src/cli/commands/doctor/index.js.map +1 -0
- package/dist/src/cli/commands/doctor/types.d.ts +20 -0
- package/dist/src/cli/commands/doctor/types.js +1 -0
- package/dist/src/cli/commands/init.js +2 -2
- package/dist/src/cli/index.d.ts +1 -1
- package/dist/src/cli/index.js +1 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/init-workspace.js +1 -1
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +15 -15
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/profile.js +1 -1
- package/dist/src/config/schema.d.ts +0 -105
- package/dist/src/config/schema.js +3 -40
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
- package/dist/src/gateway/hono/routes/doctor.js +35 -0
- package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +2 -2
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +98 -3
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/service.d.ts +0 -4
- package/dist/src/gateway/service.js +4 -23
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/routing/bindings.js +1 -1
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +2 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/session-key.d.ts +0 -5
- package/dist/src/routing/session-key.js +1 -27
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.js +2 -6
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/types.d.ts +0 -10
- package/dist/src/session/types.js.map +1 -1
- package/package.json +1 -2
- package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
- package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
- package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
- package/dist/src/acp/commands.d.ts +0 -11
- package/dist/src/acp/commands.js +0 -17
- package/dist/src/acp/commands.js.map +0 -1
- package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
- package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
- package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
- package/dist/src/acp/control-plane/index.d.ts +0 -10
- package/dist/src/acp/control-plane/index.js +0 -6
- package/dist/src/acp/control-plane/manager.d.ts +0 -86
- package/dist/src/acp/control-plane/manager.js +0 -502
- package/dist/src/acp/control-plane/manager.js.map +0 -1
- package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
- package/dist/src/acp/control-plane/manager.types.js +0 -14
- package/dist/src/acp/control-plane/manager.types.js.map +0 -1
- package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
- package/dist/src/acp/control-plane/manager.utils.js +0 -46
- package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
- package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
- package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
- package/dist/src/acp/control-plane/runtime-cache.js +0 -58
- package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
- package/dist/src/acp/control-plane/runtime-options.js +0 -92
- package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
- package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
- package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
- package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
- package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
- package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
- package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
- package/dist/src/acp/control-plane/session-store.d.ts +0 -39
- package/dist/src/acp/control-plane/session-store.js +0 -149
- package/dist/src/acp/control-plane/session-store.js.map +0 -1
- package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
- package/dist/src/acp/control-plane/turn-manager.js +0 -134
- package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
- package/dist/src/acp/event-mapper.d.ts +0 -48
- package/dist/src/acp/event-mapper.js +0 -94
- package/dist/src/acp/event-mapper.js.map +0 -1
- package/dist/src/acp/index.d.ts +0 -10
- package/dist/src/acp/index.js +0 -5
- package/dist/src/acp/meta.d.ts +0 -15
- package/dist/src/acp/meta.js +0 -36
- package/dist/src/acp/meta.js.map +0 -1
- package/dist/src/acp/routing-integration.d.ts +0 -37
- package/dist/src/acp/routing-integration.js +0 -58
- package/dist/src/acp/routing-integration.js.map +0 -1
- package/dist/src/acp/runtime/backends/index.d.ts +0 -4
- package/dist/src/acp/runtime/backends/index.js +0 -2
- package/dist/src/acp/runtime/backends/local.d.ts +0 -136
- package/dist/src/acp/runtime/backends/local.js +0 -603
- package/dist/src/acp/runtime/backends/local.js.map +0 -1
- package/dist/src/acp/runtime/error-text.d.ts +0 -16
- package/dist/src/acp/runtime/error-text.js +0 -40
- package/dist/src/acp/runtime/error-text.js.map +0 -1
- package/dist/src/acp/runtime/errors.d.ts +0 -31
- package/dist/src/acp/runtime/errors.js +0 -47
- package/dist/src/acp/runtime/errors.js.map +0 -1
- package/dist/src/acp/runtime/index.d.ts +0 -7
- package/dist/src/acp/runtime/index.js +0 -4
- package/dist/src/acp/runtime/registry.d.ts +0 -35
- package/dist/src/acp/runtime/registry.js +0 -85
- package/dist/src/acp/runtime/registry.js.map +0 -1
- package/dist/src/acp/runtime/session-identity.d.ts +0 -35
- package/dist/src/acp/runtime/session-identity.js +0 -134
- package/dist/src/acp/runtime/session-identity.js.map +0 -1
- package/dist/src/acp/runtime/types.d.ts +0 -214
- package/dist/src/acp/secret-file.d.ts +0 -7
- package/dist/src/acp/secret-file.js +0 -19
- package/dist/src/acp/secret-file.js.map +0 -1
- package/dist/src/acp/server.d.ts +0 -48
- package/dist/src/acp/server.js +0 -300
- package/dist/src/acp/server.js.map +0 -1
- package/dist/src/acp/session.d.ts +0 -29
- package/dist/src/acp/session.js +0 -30
- package/dist/src/acp/session.js.map +0 -1
- package/dist/src/acp/types.d.ts +0 -39
- package/dist/src/acp/types.js +0 -13
- package/dist/src/acp/types.js.map +0 -1
- package/dist/src/agent/image/describe-images.d.ts +0 -18
- package/dist/src/agent/image/describe-images.js +0 -19
- package/dist/src/agent/image/describe-images.js.map +0 -1
- package/dist/src/cli/commands/acp.d.ts +0 -4
- package/dist/src/cli/commands/acp.js +0 -200
- package/dist/src/cli/commands/acp.js.map +0 -1
- /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
|
|
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
|
|
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,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,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,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,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,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"}
|