@fidelios/server 0.0.1
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/adapters/codex-models.d.ts +4 -0
- package/dist/adapters/codex-models.d.ts.map +1 -0
- package/dist/adapters/codex-models.js +98 -0
- package/dist/adapters/codex-models.js.map +1 -0
- package/dist/adapters/cursor-models.d.ts +13 -0
- package/dist/adapters/cursor-models.d.ts.map +1 -0
- package/dist/adapters/cursor-models.js +148 -0
- package/dist/adapters/cursor-models.js.map +1 -0
- package/dist/adapters/http/execute.d.ts +3 -0
- package/dist/adapters/http/execute.d.ts.map +1 -0
- package/dist/adapters/http/execute.js +39 -0
- package/dist/adapters/http/execute.js.map +1 -0
- package/dist/adapters/http/index.d.ts +3 -0
- package/dist/adapters/http/index.d.ts.map +1 -0
- package/dist/adapters/http/index.js +20 -0
- package/dist/adapters/http/index.js.map +1 -0
- package/dist/adapters/http/test.d.ts +3 -0
- package/dist/adapters/http/test.d.ts.map +1 -0
- package/dist/adapters/http/test.js +106 -0
- package/dist/adapters/http/test.js.map +1 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/process/execute.d.ts +3 -0
- package/dist/adapters/process/execute.d.ts.map +1 -0
- package/dist/adapters/process/execute.js +63 -0
- package/dist/adapters/process/execute.js.map +1 -0
- package/dist/adapters/process/index.d.ts +3 -0
- package/dist/adapters/process/index.d.ts.map +1 -0
- package/dist/adapters/process/index.js +23 -0
- package/dist/adapters/process/index.js.map +1 -0
- package/dist/adapters/process/test.d.ts +3 -0
- package/dist/adapters/process/test.d.ts.map +1 -0
- package/dist/adapters/process/test.js +77 -0
- package/dist/adapters/process/test.js.map +1 -0
- package/dist/adapters/registry.d.ts +14 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +164 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/adapters/utils.d.ts +10 -0
- package/dist/adapters/utils.d.ts.map +1 -0
- package/dist/adapters/utils.js +14 -0
- package/dist/adapters/utils.js.map +1 -0
- package/dist/agent-auth-jwt.d.ts +14 -0
- package/dist/agent-auth-jwt.d.ts.map +1 -0
- package/dist/agent-auth-jwt.js +117 -0
- package/dist/agent-auth-jwt.js.map +1 -0
- package/dist/app.d.ts +25 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +265 -0
- package/dist/app.js.map +1 -0
- package/dist/attachment-types.d.ts +33 -0
- package/dist/attachment-types.d.ts.map +1 -0
- package/dist/attachment-types.js +67 -0
- package/dist/attachment-types.js.map +1 -0
- package/dist/auth/better-auth.d.ts +24 -0
- package/dist/auth/better-auth.d.ts.map +1 -0
- package/dist/auth/better-auth.js +108 -0
- package/dist/auth/better-auth.js.map +1 -0
- package/dist/board-claim.d.ts +23 -0
- package/dist/board-claim.d.ts.map +1 -0
- package/dist/board-claim.js +115 -0
- package/dist/board-claim.js.map +1 -0
- package/dist/config-file.d.ts +3 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +16 -0
- package/dist/config-file.js.map +1 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +171 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-server-status.d.ts +27 -0
- package/dist/dev-server-status.d.ts.map +1 -0
- package/dist/dev-server-status.js +70 -0
- package/dist/dev-server-status.js.map +1 -0
- package/dist/dev-watch-ignore.d.ts +2 -0
- package/dist/dev-watch-ignore.d.ts.map +1 -0
- package/dist/dev-watch-ignore.js +33 -0
- package/dist/dev-watch-ignore.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -0
- package/dist/home-paths.d.ts +17 -0
- package/dist/home-paths.d.ts.map +1 -0
- package/dist/home-paths.js +75 -0
- package/dist/home-paths.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +642 -0
- package/dist/index.js.map +1 -0
- package/dist/log-redaction.d.ts +11 -0
- package/dist/log-redaction.d.ts.map +1 -0
- package/dist/log-redaction.js +118 -0
- package/dist/log-redaction.js.map +1 -0
- package/dist/middleware/auth.d.ts +12 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +144 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/board-mutation-guard.d.ts +3 -0
- package/dist/middleware/board-mutation-guard.d.ts.map +1 -0
- package/dist/middleware/board-mutation-guard.js +59 -0
- package/dist/middleware/board-mutation-guard.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +17 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +37 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logger.d.ts +4 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +87 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/private-hostname-guard.d.ts +11 -0
- package/dist/middleware/private-hostname-guard.d.ts.map +1 -0
- package/dist/middleware/private-hostname-guard.js +78 -0
- package/dist/middleware/private-hostname-guard.js.map +1 -0
- package/dist/middleware/validate.d.ts +4 -0
- package/dist/middleware/validate.d.ts.map +1 -0
- package/dist/middleware/validate.js +7 -0
- package/dist/middleware/validate.js.map +1 -0
- package/dist/onboarding-assets/ceo/AGENTS.md +54 -0
- package/dist/onboarding-assets/ceo/HEARTBEAT.md +72 -0
- package/dist/onboarding-assets/ceo/SOUL.md +33 -0
- package/dist/onboarding-assets/ceo/TOOLS.md +3 -0
- package/dist/onboarding-assets/default/AGENTS.md +3 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +31 -0
- package/dist/paths.js.map +1 -0
- package/dist/realtime/live-events-ws.d.ts +28 -0
- package/dist/realtime/live-events-ws.d.ts.map +1 -0
- package/dist/realtime/live-events-ws.js +187 -0
- package/dist/realtime/live-events-ws.js.map +1 -0
- package/dist/redaction.d.ts +4 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +63 -0
- package/dist/redaction.js.map +1 -0
- package/dist/routes/access.d.ts +61 -0
- package/dist/routes/access.d.ts.map +1 -0
- package/dist/routes/access.js +2265 -0
- package/dist/routes/access.js.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -0
- package/dist/routes/activity.js +78 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/agents.d.ts +3 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +1828 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/approvals.d.ts +3 -0
- package/dist/routes/approvals.d.ts.map +1 -0
- package/dist/routes/approvals.js +275 -0
- package/dist/routes/approvals.js.map +1 -0
- package/dist/routes/assets.d.ts +4 -0
- package/dist/routes/assets.d.ts.map +1 -0
- package/dist/routes/assets.js +309 -0
- package/dist/routes/assets.js.map +1 -0
- package/dist/routes/authz.d.ts +16 -0
- package/dist/routes/authz.d.ts.map +1 -0
- package/dist/routes/authz.js +47 -0
- package/dist/routes/authz.js.map +1 -0
- package/dist/routes/companies.d.ts +4 -0
- package/dist/routes/companies.d.ts.map +1 -0
- package/dist/routes/companies.js +303 -0
- package/dist/routes/companies.js.map +1 -0
- package/dist/routes/company-skills.d.ts +3 -0
- package/dist/routes/company-skills.d.ts.map +1 -0
- package/dist/routes/company-skills.js +228 -0
- package/dist/routes/company-skills.js.map +1 -0
- package/dist/routes/costs.d.ts +3 -0
- package/dist/routes/costs.d.ts.map +1 -0
- package/dist/routes/costs.js +268 -0
- package/dist/routes/costs.js.map +1 -0
- package/dist/routes/dashboard.d.ts +3 -0
- package/dist/routes/dashboard.d.ts.map +1 -0
- package/dist/routes/dashboard.js +15 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/execution-workspaces.d.ts +3 -0
- package/dist/routes/execution-workspaces.d.ts.map +1 -0
- package/dist/routes/execution-workspaces.js +165 -0
- package/dist/routes/execution-workspaces.js.map +1 -0
- package/dist/routes/goals.d.ts +3 -0
- package/dist/routes/goals.d.ts.map +1 -0
- package/dist/routes/goals.js +95 -0
- package/dist/routes/goals.js.map +1 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +69 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +18 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +18 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/instance-settings.d.ts +3 -0
- package/dist/routes/instance-settings.d.ts.map +1 -0
- package/dist/routes/instance-settings.js +71 -0
- package/dist/routes/instance-settings.js.map +1 -0
- package/dist/routes/issues-checkout-wakeup.d.ts +9 -0
- package/dist/routes/issues-checkout-wakeup.d.ts.map +1 -0
- package/dist/routes/issues-checkout-wakeup.js +12 -0
- package/dist/routes/issues-checkout-wakeup.js.map +1 -0
- package/dist/routes/issues.d.ts +4 -0
- package/dist/routes/issues.d.ts.map +1 -0
- package/dist/routes/issues.js +1520 -0
- package/dist/routes/issues.js.map +1 -0
- package/dist/routes/llms.d.ts +3 -0
- package/dist/routes/llms.d.ts.map +1 -0
- package/dist/routes/llms.js +78 -0
- package/dist/routes/llms.js.map +1 -0
- package/dist/routes/org-chart-svg.d.ts +25 -0
- package/dist/routes/org-chart-svg.d.ts.map +1 -0
- package/dist/routes/org-chart-svg.js +656 -0
- package/dist/routes/org-chart-svg.js.map +1 -0
- package/dist/routes/plugin-ui-static.d.ts +69 -0
- package/dist/routes/plugin-ui-static.d.ts.map +1 -0
- package/dist/routes/plugin-ui-static.js +411 -0
- package/dist/routes/plugin-ui-static.js.map +1 -0
- package/dist/routes/plugins.d.ts +120 -0
- package/dist/routes/plugins.d.ts.map +1 -0
- package/dist/routes/plugins.js +1784 -0
- package/dist/routes/plugins.js.map +1 -0
- package/dist/routes/projects.d.ts +3 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +257 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/routines.d.ts +3 -0
- package/dist/routes/routines.d.ts.map +1 -0
- package/dist/routes/routines.js +277 -0
- package/dist/routes/routines.js.map +1 -0
- package/dist/routes/secrets.d.ts +3 -0
- package/dist/routes/secrets.d.ts.map +1 -0
- package/dist/routes/secrets.js +128 -0
- package/dist/routes/secrets.js.map +1 -0
- package/dist/routes/sidebar-badges.d.ts +3 -0
- package/dist/routes/sidebar-badges.d.ts.map +1 -0
- package/dist/routes/sidebar-badges.js +45 -0
- package/dist/routes/sidebar-badges.js.map +1 -0
- package/dist/secrets/external-stub-providers.d.ts +5 -0
- package/dist/secrets/external-stub-providers.d.ts.map +1 -0
- package/dist/secrets/external-stub-providers.js +21 -0
- package/dist/secrets/external-stub-providers.js.map +1 -0
- package/dist/secrets/local-encrypted-provider.d.ts +3 -0
- package/dist/secrets/local-encrypted-provider.d.ts.map +1 -0
- package/dist/secrets/local-encrypted-provider.js +116 -0
- package/dist/secrets/local-encrypted-provider.js.map +1 -0
- package/dist/secrets/provider-registry.d.ts +5 -0
- package/dist/secrets/provider-registry.d.ts.map +1 -0
- package/dist/secrets/provider-registry.js +20 -0
- package/dist/secrets/provider-registry.js.map +1 -0
- package/dist/secrets/types.d.ts +21 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +2 -0
- package/dist/secrets/types.js.map +1 -0
- package/dist/services/access.d.ts +113 -0
- package/dist/services/access.d.ts.map +1 -0
- package/dist/services/access.js +247 -0
- package/dist/services/access.js.map +1 -0
- package/dist/services/activity-log.d.ts +17 -0
- package/dist/services/activity-log.d.ts.map +1 -0
- package/dist/services/activity-log.js +74 -0
- package/dist/services/activity-log.js.map +1 -0
- package/dist/services/activity.d.ts +764 -0
- package/dist/services/activity.d.ts.map +1 -0
- package/dist/services/activity.js +105 -0
- package/dist/services/activity.js.map +1 -0
- package/dist/services/agent-instructions.d.ts +91 -0
- package/dist/services/agent-instructions.d.ts.map +1 -0
- package/dist/services/agent-instructions.js +580 -0
- package/dist/services/agent-instructions.js.map +1 -0
- package/dist/services/agent-permissions.d.ts +6 -0
- package/dist/services/agent-permissions.d.ts.map +1 -0
- package/dist/services/agent-permissions.js +18 -0
- package/dist/services/agent-permissions.js.map +1 -0
- package/dist/services/agents.d.ts +1670 -0
- package/dist/services/agents.d.ts.map +1 -0
- package/dist/services/agents.js +566 -0
- package/dist/services/agents.js.map +1 -0
- package/dist/services/approvals.d.ts +546 -0
- package/dist/services/approvals.d.ts.map +1 -0
- package/dist/services/approvals.js +212 -0
- package/dist/services/approvals.js.map +1 -0
- package/dist/services/assets.d.ts +33 -0
- package/dist/services/assets.d.ts.map +1 -0
- package/dist/services/assets.js +17 -0
- package/dist/services/assets.js.map +1 -0
- package/dist/services/board-auth.d.ts +234 -0
- package/dist/services/board-auth.d.ts.map +1 -0
- package/dist/services/board-auth.js +295 -0
- package/dist/services/board-auth.js.map +1 -0
- package/dist/services/budgets.d.ts +38 -0
- package/dist/services/budgets.d.ts.map +1 -0
- package/dist/services/budgets.js +784 -0
- package/dist/services/budgets.js.map +1 -0
- package/dist/services/companies.d.ts +124 -0
- package/dist/services/companies.d.ts.map +1 -0
- package/dist/services/companies.js +256 -0
- package/dist/services/companies.js.map +1 -0
- package/dist/services/company-export-readme.d.ts +17 -0
- package/dist/services/company-export-readme.d.ts.map +1 -0
- package/dist/services/company-export-readme.js +148 -0
- package/dist/services/company-export-readme.js.map +1 -0
- package/dist/services/company-portability.d.ts +23 -0
- package/dist/services/company-portability.d.ts.map +1 -0
- package/dist/services/company-portability.js +3739 -0
- package/dist/services/company-portability.js.map +1 -0
- package/dist/services/company-skills.d.ts +77 -0
- package/dist/services/company-skills.d.ts.map +1 -0
- package/dist/services/company-skills.js +2042 -0
- package/dist/services/company-skills.js.map +1 -0
- package/dist/services/costs.d.ts +114 -0
- package/dist/services/costs.d.ts.map +1 -0
- package/dist/services/costs.js +294 -0
- package/dist/services/costs.js.map +1 -0
- package/dist/services/cron.d.ts +80 -0
- package/dist/services/cron.d.ts.map +1 -0
- package/dist/services/cron.js +300 -0
- package/dist/services/cron.js.map +1 -0
- package/dist/services/dashboard.d.ts +26 -0
- package/dist/services/dashboard.d.ts.map +1 -0
- package/dist/services/dashboard.js +98 -0
- package/dist/services/dashboard.js.map +1 -0
- package/dist/services/default-agent-instructions.d.ts +9 -0
- package/dist/services/default-agent-instructions.d.ts.map +1 -0
- package/dist/services/default-agent-instructions.js +20 -0
- package/dist/services/default-agent-instructions.js.map +1 -0
- package/dist/services/documents.d.ts +164 -0
- package/dist/services/documents.d.ts.map +1 -0
- package/dist/services/documents.js +382 -0
- package/dist/services/documents.js.map +1 -0
- package/dist/services/execution-workspace-policy.d.ts +21 -0
- package/dist/services/execution-workspace-policy.d.ts.map +1 -0
- package/dist/services/execution-workspace-policy.js +177 -0
- package/dist/services/execution-workspace-policy.js.map +1 -0
- package/dist/services/execution-workspaces.d.ts +19 -0
- package/dist/services/execution-workspaces.d.ts.map +1 -0
- package/dist/services/execution-workspaces.js +87 -0
- package/dist/services/execution-workspaces.js.map +1 -0
- package/dist/services/finance.d.ts +93 -0
- package/dist/services/finance.d.ts.map +1 -0
- package/dist/services/finance.js +120 -0
- package/dist/services/finance.js.map +1 -0
- package/dist/services/goals.d.ts +433 -0
- package/dist/services/goals.d.ts.map +1 -0
- package/dist/services/goals.js +54 -0
- package/dist/services/goals.js.map +1 -0
- package/dist/services/heartbeat-run-summary.d.ts +2 -0
- package/dist/services/heartbeat-run-summary.d.ts.map +1 -0
- package/dist/services/heartbeat-run-summary.js +30 -0
- package/dist/services/heartbeat-run-summary.js.map +1 -0
- package/dist/services/heartbeat.d.ts +812 -0
- package/dist/services/heartbeat.d.ts.map +1 -0
- package/dist/services/heartbeat.js +3156 -0
- package/dist/services/heartbeat.js.map +1 -0
- package/dist/services/hire-hook.d.ts +14 -0
- package/dist/services/hire-hook.d.ts.map +1 -0
- package/dist/services/hire-hook.js +85 -0
- package/dist/services/hire-hook.js.map +1 -0
- package/dist/services/index.d.ts +33 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +33 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/instance-settings.d.ts +11 -0
- package/dist/services/instance-settings.d.ts.map +1 -0
- package/dist/services/instance-settings.js +116 -0
- package/dist/services/instance-settings.js.map +1 -0
- package/dist/services/issue-approvals.d.ts +56 -0
- package/dist/services/issue-approvals.d.ts.map +1 -0
- package/dist/services/issue-approvals.js +153 -0
- package/dist/services/issue-approvals.js.map +1 -0
- package/dist/services/issue-assignment-wakeup.d.ts +29 -0
- package/dist/services/issue-assignment-wakeup.d.ts.map +1 -0
- package/dist/services/issue-assignment-wakeup.js +22 -0
- package/dist/services/issue-assignment-wakeup.js.map +1 -0
- package/dist/services/issue-goal-fallback.d.ts +18 -0
- package/dist/services/issue-goal-fallback.d.ts.map +1 -0
- package/dist/services/issue-goal-fallback.js +33 -0
- package/dist/services/issue-goal-fallback.js.map +1 -0
- package/dist/services/issues.d.ts +560 -0
- package/dist/services/issues.d.ts.map +1 -0
- package/dist/services/issues.js +1478 -0
- package/dist/services/issues.js.map +1 -0
- package/dist/services/live-events.d.ts +17 -0
- package/dist/services/live-events.d.ts.map +1 -0
- package/dist/services/live-events.js +33 -0
- package/dist/services/live-events.js.map +1 -0
- package/dist/services/plugin-capability-validator.d.ts +108 -0
- package/dist/services/plugin-capability-validator.d.ts.map +1 -0
- package/dist/services/plugin-capability-validator.js +268 -0
- package/dist/services/plugin-capability-validator.js.map +1 -0
- package/dist/services/plugin-config-validator.d.ts +26 -0
- package/dist/services/plugin-config-validator.d.ts.map +1 -0
- package/dist/services/plugin-config-validator.js +41 -0
- package/dist/services/plugin-config-validator.js.map +1 -0
- package/dist/services/plugin-dev-watcher.d.ts +30 -0
- package/dist/services/plugin-dev-watcher.d.ts.map +1 -0
- package/dist/services/plugin-dev-watcher.js +241 -0
- package/dist/services/plugin-dev-watcher.js.map +1 -0
- package/dist/services/plugin-event-bus.d.ts +149 -0
- package/dist/services/plugin-event-bus.d.ts.map +1 -0
- package/dist/services/plugin-event-bus.js +258 -0
- package/dist/services/plugin-event-bus.js.map +1 -0
- package/dist/services/plugin-host-service-cleanup.d.ts +14 -0
- package/dist/services/plugin-host-service-cleanup.d.ts.map +1 -0
- package/dist/services/plugin-host-service-cleanup.js +37 -0
- package/dist/services/plugin-host-service-cleanup.js.map +1 -0
- package/dist/services/plugin-host-services.d.ts +13 -0
- package/dist/services/plugin-host-services.d.ts.map +1 -0
- package/dist/services/plugin-host-services.js +969 -0
- package/dist/services/plugin-host-services.js.map +1 -0
- package/dist/services/plugin-job-coordinator.d.ts +81 -0
- package/dist/services/plugin-job-coordinator.d.ts.map +1 -0
- package/dist/services/plugin-job-coordinator.js +172 -0
- package/dist/services/plugin-job-coordinator.js.map +1 -0
- package/dist/services/plugin-job-scheduler.d.ts +163 -0
- package/dist/services/plugin-job-scheduler.d.ts.map +1 -0
- package/dist/services/plugin-job-scheduler.js +454 -0
- package/dist/services/plugin-job-scheduler.js.map +1 -0
- package/dist/services/plugin-job-store.d.ts +208 -0
- package/dist/services/plugin-job-store.d.ts.map +1 -0
- package/dist/services/plugin-job-store.js +350 -0
- package/dist/services/plugin-job-store.js.map +1 -0
- package/dist/services/plugin-lifecycle.d.ts +203 -0
- package/dist/services/plugin-lifecycle.d.ts.map +1 -0
- package/dist/services/plugin-lifecycle.js +476 -0
- package/dist/services/plugin-lifecycle.js.map +1 -0
- package/dist/services/plugin-loader.d.ts +441 -0
- package/dist/services/plugin-loader.d.ts.map +1 -0
- package/dist/services/plugin-loader.js +1192 -0
- package/dist/services/plugin-loader.js.map +1 -0
- package/dist/services/plugin-log-retention.d.ts +20 -0
- package/dist/services/plugin-log-retention.d.ts.map +1 -0
- package/dist/services/plugin-log-retention.js +63 -0
- package/dist/services/plugin-log-retention.js.map +1 -0
- package/dist/services/plugin-manifest-validator.d.ts +79 -0
- package/dist/services/plugin-manifest-validator.d.ts.map +1 -0
- package/dist/services/plugin-manifest-validator.js +84 -0
- package/dist/services/plugin-manifest-validator.js.map +1 -0
- package/dist/services/plugin-registry.d.ts +2542 -0
- package/dist/services/plugin-registry.d.ts.map +1 -0
- package/dist/services/plugin-registry.js +539 -0
- package/dist/services/plugin-registry.js.map +1 -0
- package/dist/services/plugin-runtime-sandbox.d.ts +40 -0
- package/dist/services/plugin-runtime-sandbox.d.ts.map +1 -0
- package/dist/services/plugin-runtime-sandbox.js +154 -0
- package/dist/services/plugin-runtime-sandbox.js.map +1 -0
- package/dist/services/plugin-secrets-handler.d.ts +81 -0
- package/dist/services/plugin-secrets-handler.d.ts.map +1 -0
- package/dist/services/plugin-secrets-handler.js +275 -0
- package/dist/services/plugin-secrets-handler.js.map +1 -0
- package/dist/services/plugin-state-store.d.ts +92 -0
- package/dist/services/plugin-state-store.d.ts.map +1 -0
- package/dist/services/plugin-state-store.js +190 -0
- package/dist/services/plugin-state-store.js.map +1 -0
- package/dist/services/plugin-stream-bus.d.ts +29 -0
- package/dist/services/plugin-stream-bus.d.ts.map +1 -0
- package/dist/services/plugin-stream-bus.js +48 -0
- package/dist/services/plugin-stream-bus.js.map +1 -0
- package/dist/services/plugin-tool-dispatcher.d.ts +180 -0
- package/dist/services/plugin-tool-dispatcher.d.ts.map +1 -0
- package/dist/services/plugin-tool-dispatcher.js +224 -0
- package/dist/services/plugin-tool-dispatcher.js.map +1 -0
- package/dist/services/plugin-tool-registry.d.ts +192 -0
- package/dist/services/plugin-tool-registry.d.ts.map +1 -0
- package/dist/services/plugin-tool-registry.js +224 -0
- package/dist/services/plugin-tool-registry.js.map +1 -0
- package/dist/services/plugin-worker-manager.d.ts +260 -0
- package/dist/services/plugin-worker-manager.d.ts.map +1 -0
- package/dist/services/plugin-worker-manager.js +835 -0
- package/dist/services/plugin-worker-manager.js.map +1 -0
- package/dist/services/projects.d.ts +87 -0
- package/dist/services/projects.d.ts.map +1 -0
- package/dist/services/projects.js +656 -0
- package/dist/services/projects.js.map +1 -0
- package/dist/services/quota-windows.d.ts +9 -0
- package/dist/services/quota-windows.d.ts.map +1 -0
- package/dist/services/quota-windows.js +56 -0
- package/dist/services/quota-windows.js.map +1 -0
- package/dist/services/routines.d.ts +135 -0
- package/dist/services/routines.d.ts.map +1 -0
- package/dist/services/routines.js +1105 -0
- package/dist/services/routines.js.map +1 -0
- package/dist/services/run-log-store.d.ts +34 -0
- package/dist/services/run-log-store.d.ts.map +1 -0
- package/dist/services/run-log-store.js +109 -0
- package/dist/services/run-log-store.js.map +1 -0
- package/dist/services/secrets.d.ts +511 -0
- package/dist/services/secrets.d.ts.map +1 -0
- package/dist/services/secrets.js +289 -0
- package/dist/services/secrets.js.map +1 -0
- package/dist/services/sidebar-badges.d.ts +9 -0
- package/dist/services/sidebar-badges.d.ts.map +1 -0
- package/dist/services/sidebar-badges.js +33 -0
- package/dist/services/sidebar-badges.js.map +1 -0
- package/dist/services/work-products.d.ts +14 -0
- package/dist/services/work-products.d.ts.map +1 -0
- package/dist/services/work-products.js +100 -0
- package/dist/services/work-products.js.map +1 -0
- package/dist/services/workspace-operation-log-store.d.ts +33 -0
- package/dist/services/workspace-operation-log-store.d.ts.map +1 -0
- package/dist/services/workspace-operation-log-store.js +110 -0
- package/dist/services/workspace-operation-log-store.js.map +1 -0
- package/dist/services/workspace-operations.d.ts +44 -0
- package/dist/services/workspace-operations.d.ts.map +1 -0
- package/dist/services/workspace-operations.js +211 -0
- package/dist/services/workspace-operations.js.map +1 -0
- package/dist/services/workspace-runtime.d.ts +164 -0
- package/dist/services/workspace-runtime.d.ts.map +1 -0
- package/dist/services/workspace-runtime.js +1235 -0
- package/dist/services/workspace-runtime.js.map +1 -0
- package/dist/startup-banner.d.ts +31 -0
- package/dist/startup-banner.d.ts.map +1 -0
- package/dist/startup-banner.js +117 -0
- package/dist/startup-banner.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +29 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local-disk-provider.d.ts +3 -0
- package/dist/storage/local-disk-provider.d.ts.map +1 -0
- package/dist/storage/local-disk-provider.js +79 -0
- package/dist/storage/local-disk-provider.js.map +1 -0
- package/dist/storage/provider-registry.d.ts +4 -0
- package/dist/storage/provider-registry.d.ts.map +1 -0
- package/dist/storage/provider-registry.js +15 -0
- package/dist/storage/provider-registry.js.map +1 -0
- package/dist/storage/s3-provider.d.ts +11 -0
- package/dist/storage/s3-provider.d.ts.map +1 -0
- package/dist/storage/s3-provider.js +123 -0
- package/dist/storage/s3-provider.js.map +1 -0
- package/dist/storage/service.d.ts +3 -0
- package/dist/storage/service.d.ts.map +1 -0
- package/dist/storage/service.js +120 -0
- package/dist/storage/service.js.map +1 -0
- package/dist/storage/types.d.ts +55 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/ui-branding.d.ts +13 -0
- package/dist/ui-branding.d.ts.map +1 -0
- package/dist/ui-branding.js +187 -0
- package/dist/ui-branding.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/dist/worktree-config.d.ts +19 -0
- package/dist/worktree-config.d.ts.map +1 -0
- package/dist/worktree-config.js +365 -0
- package/dist/worktree-config.js.map +1 -0
- package/package.json +95 -0
- package/ui-dist/android-chrome-192x192.png +0 -0
- package/ui-dist/android-chrome-512x512.png +0 -0
- package/ui-dist/apple-touch-icon.png +0 -0
- package/ui-dist/assets/_basePickBy-BB1S19s0.js +1 -0
- package/ui-dist/assets/_baseUniq-BNk0p-bq.js +1 -0
- package/ui-dist/assets/apl-B4CMkyY2.js +1 -0
- package/ui-dist/assets/arc-Ds13x1NW.js +1 -0
- package/ui-dist/assets/architectureDiagram-VXUJARFQ-D8Br-_jt.js +36 -0
- package/ui-dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/ui-dist/assets/asn1-EdZsLKOL.js +1 -0
- package/ui-dist/assets/asterisk-B-8jnY81.js +1 -0
- package/ui-dist/assets/blockDiagram-VD42YOAC-CCYsAhQ5.js +122 -0
- package/ui-dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/ui-dist/assets/c4Diagram-YG6GDRKO-CsrnTJYB.js +10 -0
- package/ui-dist/assets/channel-Df4ReTUQ.js +1 -0
- package/ui-dist/assets/chunk-4BX2VUAB-s-S3bm2a.js +1 -0
- package/ui-dist/assets/chunk-55IACEB6-AlPeSG3C.js +1 -0
- package/ui-dist/assets/chunk-B4BG7PRW-Dlv3zmp0.js +165 -0
- package/ui-dist/assets/chunk-DI55MBZ5-y3Hfc2F6.js +220 -0
- package/ui-dist/assets/chunk-FMBD7UC4-ZbmFZ8uD.js +15 -0
- package/ui-dist/assets/chunk-QN33PNHL-g7XrAaL5.js +1 -0
- package/ui-dist/assets/chunk-QZHKN3VN-DQz2X_ZR.js +1 -0
- package/ui-dist/assets/chunk-TZMSLE5B-NRbDhryd.js +1 -0
- package/ui-dist/assets/classDiagram-2ON5EDUG-CdLb01dH.js +1 -0
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CdLb01dH.js +1 -0
- package/ui-dist/assets/clike-B9uivgTg.js +1 -0
- package/ui-dist/assets/clojure-BMjYHr_A.js +1 -0
- package/ui-dist/assets/clone-ycTwxHSX.js +1 -0
- package/ui-dist/assets/cmake-BQqOBYOt.js +1 -0
- package/ui-dist/assets/cobol-CWcv1MsR.js +1 -0
- package/ui-dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/ui-dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/ui-dist/assets/cose-bilkent-S5V4N54A-CifA3UGC.js +1 -0
- package/ui-dist/assets/crystal-SjHAIU92.js +1 -0
- package/ui-dist/assets/css-BnMrqG3P.js +1 -0
- package/ui-dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/ui-dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/ui-dist/assets/d-pRatUO7H.js +1 -0
- package/ui-dist/assets/dagre-6UL2VRFP-Cy4_402x.js +4 -0
- package/ui-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/ui-dist/assets/diagram-PSM6KHXK-CUA-Vqxe.js +24 -0
- package/ui-dist/assets/diagram-QEK2KX5R-D763Ackt.js +43 -0
- package/ui-dist/assets/diagram-S2PKOQOG-Cu1xEKHt.js +24 -0
- package/ui-dist/assets/diff-DbItnlRl.js +1 -0
- package/ui-dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/ui-dist/assets/dtd-DF_7sFjM.js +1 -0
- package/ui-dist/assets/dylan-DwRh75JA.js +1 -0
- package/ui-dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/ui-dist/assets/ecl-Cabwm37j.js +1 -0
- package/ui-dist/assets/eiffel-CnydiIhH.js +1 -0
- package/ui-dist/assets/elm-vLlmbW-K.js +1 -0
- package/ui-dist/assets/erDiagram-Q2GNP2WA-9RlN9oCi.js +60 -0
- package/ui-dist/assets/erlang-BNw1qcRV.js +1 -0
- package/ui-dist/assets/factor-kuTfRLto.js +1 -0
- package/ui-dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/ui-dist/assets/flowDiagram-NV44I4VS-Ddv1tq-H.js +162 -0
- package/ui-dist/assets/forth-Ffai-XNe.js +1 -0
- package/ui-dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/ui-dist/assets/ganttDiagram-JELNMOA3-DAw7UfVT.js +267 -0
- package/ui-dist/assets/gas-Bneqetm1.js +1 -0
- package/ui-dist/assets/gherkin-heZmZLOM.js +1 -0
- package/ui-dist/assets/gitGraphDiagram-V2S2FVAM-CLPkzwpF.js +65 -0
- package/ui-dist/assets/graph-B1ThnnK5.js +1 -0
- package/ui-dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/ui-dist/assets/haskell-Cw1EW3IL.js +1 -0
- package/ui-dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/ui-dist/assets/http-DBlCnlav.js +1 -0
- package/ui-dist/assets/idl-BEugSyMb.js +1 -0
- package/ui-dist/assets/index-0DYmQxT3.js +2 -0
- package/ui-dist/assets/index-1BRIjwwa.js +1 -0
- package/ui-dist/assets/index-4pxn9bje.js +1 -0
- package/ui-dist/assets/index-B02pjBpR.js +7 -0
- package/ui-dist/assets/index-BGjMkZzC.js +1 -0
- package/ui-dist/assets/index-BHsjaYJ1.js +1 -0
- package/ui-dist/assets/index-BTwpjL-6.js +1 -0
- package/ui-dist/assets/index-BZ72uG4K.js +6 -0
- package/ui-dist/assets/index-BpC8VHcj.js +3 -0
- package/ui-dist/assets/index-BrvXvCkd.js +1 -0
- package/ui-dist/assets/index-CEBcI-2f.js +1 -0
- package/ui-dist/assets/index-CkmjCahV.js +1 -0
- package/ui-dist/assets/index-Cp84QmJD.css +1 -0
- package/ui-dist/assets/index-CpVjtxma.js +1 -0
- package/ui-dist/assets/index-D3AJPUjv.js +1 -0
- package/ui-dist/assets/index-DRkeP4vs.js +13 -0
- package/ui-dist/assets/index-DXeNhnre.js +1180 -0
- package/ui-dist/assets/index-DXvXmooU.js +1 -0
- package/ui-dist/assets/index-DZdwValG.js +1 -0
- package/ui-dist/assets/index-DbWj5-qO.js +1 -0
- package/ui-dist/assets/index-DbcKBTbp.js +1 -0
- package/ui-dist/assets/index-DdAQdjTR.js +1 -0
- package/ui-dist/assets/index-DlImcHKo.js +1 -0
- package/ui-dist/assets/index-X9LdJDbl.js +1 -0
- package/ui-dist/assets/infoDiagram-HS3SLOUP-CbJm6kuq.js +2 -0
- package/ui-dist/assets/init-Gi6I4Gst.js +1 -0
- package/ui-dist/assets/javascript-iXu5QeM3.js +1 -0
- package/ui-dist/assets/journeyDiagram-XKPGCS4Q-CeNVFpGu.js +139 -0
- package/ui-dist/assets/julia-DuME0IfC.js +1 -0
- package/ui-dist/assets/kanban-definition-3W4ZIXB7-DRqPoDRI.js +89 -0
- package/ui-dist/assets/katex-O9d3_IXG.js +261 -0
- package/ui-dist/assets/layout-CZTKj8OD.js +1 -0
- package/ui-dist/assets/linear-BDJjeIco.js +1 -0
- package/ui-dist/assets/livescript-BwQOo05w.js +1 -0
- package/ui-dist/assets/lua-BgMRiT3U.js +1 -0
- package/ui-dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/ui-dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/ui-dist/assets/mermaid.core-B5v7dPHY.js +256 -0
- package/ui-dist/assets/mindmap-definition-VGOIOE7T-zjw0AyzL.js +68 -0
- package/ui-dist/assets/mirc-CjQqDB4T.js +1 -0
- package/ui-dist/assets/mllike-CXdrOF99.js +1 -0
- package/ui-dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/ui-dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/ui-dist/assets/mumps-BT43cFF4.js +1 -0
- package/ui-dist/assets/nginx-DdIZxoE0.js +1 -0
- package/ui-dist/assets/nsis-LdVXkNf5.js +1 -0
- package/ui-dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/ui-dist/assets/octave-Ck1zUtKM.js +1 -0
- package/ui-dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/ui-dist/assets/oz-BzwKVEFT.js +1 -0
- package/ui-dist/assets/pascal--L3eBynH.js +1 -0
- package/ui-dist/assets/perl-CdXCOZ3F.js +1 -0
- package/ui-dist/assets/pieDiagram-ADFJNKIX-ojNQ8Ukr.js +30 -0
- package/ui-dist/assets/pig-CevX1Tat.js +1 -0
- package/ui-dist/assets/powershell-CFHJl5sT.js +1 -0
- package/ui-dist/assets/properties-C78fOPTZ.js +1 -0
- package/ui-dist/assets/protobuf-ChK-085T.js +1 -0
- package/ui-dist/assets/pug-DeIclll2.js +1 -0
- package/ui-dist/assets/puppet-DMA9R1ak.js +1 -0
- package/ui-dist/assets/python-BuPzkPfP.js +1 -0
- package/ui-dist/assets/q-pXgVlZs6.js +1 -0
- package/ui-dist/assets/quadrantDiagram-AYHSOK5B-B8K2F86x.js +7 -0
- package/ui-dist/assets/r-B6wPVr8A.js +1 -0
- package/ui-dist/assets/requirementDiagram-UZGBJVZJ-DA_Bjcpk.js +64 -0
- package/ui-dist/assets/rpm-CTu-6PCP.js +1 -0
- package/ui-dist/assets/ruby-B2Rjki9n.js +1 -0
- package/ui-dist/assets/sankeyDiagram-TZEHDZUN-yQfMgroQ.js +10 -0
- package/ui-dist/assets/sas-B4kiWyti.js +1 -0
- package/ui-dist/assets/scheme-C41bIUwD.js +1 -0
- package/ui-dist/assets/sequenceDiagram-WL72ISMW-CWQm0UQc.js +145 -0
- package/ui-dist/assets/shell-CjFT_Tl9.js +1 -0
- package/ui-dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/ui-dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/ui-dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/ui-dist/assets/solr-DehyRSwq.js +1 -0
- package/ui-dist/assets/sparql-DkYu6x3z.js +1 -0
- package/ui-dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/ui-dist/assets/sql-D0XecflT.js +1 -0
- package/ui-dist/assets/stateDiagram-FKZM4ZOC-C9L_ELvE.js +1 -0
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-D3i22gRM.js +1 -0
- package/ui-dist/assets/stex-C3f8Ysf7.js +1 -0
- package/ui-dist/assets/stylus-B533Al4x.js +1 -0
- package/ui-dist/assets/swift-BzpIVaGY.js +1 -0
- package/ui-dist/assets/tcl-DVfN8rqt.js +1 -0
- package/ui-dist/assets/textile-CnDTJFAw.js +1 -0
- package/ui-dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/ui-dist/assets/tiki-DGYXhP31.js +1 -0
- package/ui-dist/assets/timeline-definition-IT6M3QCI-Bfva-2zq.js +61 -0
- package/ui-dist/assets/toml-Bm5Em-hy.js +1 -0
- package/ui-dist/assets/treemap-GDKQZRPO-6wTQWQt4.js +162 -0
- package/ui-dist/assets/troff-wAsdV37c.js +1 -0
- package/ui-dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/ui-dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/ui-dist/assets/turtle-B1tBg_DP.js +1 -0
- package/ui-dist/assets/vb-CmGdzxic.js +1 -0
- package/ui-dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/ui-dist/assets/velocity-D8B20fx6.js +1 -0
- package/ui-dist/assets/verilog-C6RDOZhf.js +1 -0
- package/ui-dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/ui-dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/ui-dist/assets/xquery-DzFWVndE.js +1 -0
- package/ui-dist/assets/xychartDiagram-PRI3JC2R-DNsmIw3v.js +7 -0
- package/ui-dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/ui-dist/assets/z80-Hz9HOZM7.js +1 -0
- package/ui-dist/brands/opencode-logo-dark-square.svg +18 -0
- package/ui-dist/brands/opencode-logo-light-square.svg +18 -0
- package/ui-dist/favicon-16x16.png +0 -0
- package/ui-dist/favicon-32x32.png +0 -0
- package/ui-dist/favicon.ico +0 -0
- package/ui-dist/favicon.svg +9 -0
- package/ui-dist/index.html +48 -0
- package/ui-dist/site.webmanifest +30 -0
- package/ui-dist/sw.js +42 -0
- package/ui-dist/worktree-favicon-16x16.png +0 -0
- package/ui-dist/worktree-favicon-32x32.png +0 -0
- package/ui-dist/worktree-favicon.ico +0 -0
- package/ui-dist/worktree-favicon.svg +9 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginWorkerManager — spawns and manages out-of-process plugin worker child
|
|
3
|
+
* processes, routes JSON-RPC 2.0 calls over stdio, and handles lifecycle
|
|
4
|
+
* management including crash recovery with exponential backoff.
|
|
5
|
+
*
|
|
6
|
+
* Each installed plugin gets one dedicated worker process. The host sends
|
|
7
|
+
* JSON-RPC requests over the child's stdin and reads responses from stdout.
|
|
8
|
+
* Worker stderr is captured and forwarded to the host logger.
|
|
9
|
+
*
|
|
10
|
+
* Process Model (from PLUGIN_SPEC.md §12):
|
|
11
|
+
* - One worker process per installed plugin
|
|
12
|
+
* - Failure isolation: plugin crashes do not affect the host
|
|
13
|
+
* - Graceful shutdown: 10-second drain, then SIGTERM, then SIGKILL
|
|
14
|
+
* - Automatic restart with exponential backoff on unexpected exits
|
|
15
|
+
*
|
|
16
|
+
* @see PLUGIN_SPEC.md §12 — Process Model
|
|
17
|
+
* @see PLUGIN_SPEC.md §12.5 — Graceful Shutdown Policy
|
|
18
|
+
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
|
19
|
+
*/
|
|
20
|
+
import { fork } from "node:child_process";
|
|
21
|
+
import { EventEmitter } from "node:events";
|
|
22
|
+
import { createInterface } from "node:readline";
|
|
23
|
+
import { JSONRPC_VERSION, JSONRPC_ERROR_CODES, PLUGIN_RPC_ERROR_CODES, createRequest, createErrorResponse, parseMessage, serializeMessage, isJsonRpcResponse, isJsonRpcRequest, isJsonRpcNotification, isJsonRpcSuccessResponse, JsonRpcParseError, JsonRpcCallError, } from "@fidelios/plugin-sdk";
|
|
24
|
+
import { logger } from "../middleware/logger.js";
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Constants
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/** Default timeout for RPC calls in milliseconds. */
|
|
29
|
+
const DEFAULT_RPC_TIMEOUT_MS = 30_000;
|
|
30
|
+
/** Hard upper bound for any RPC timeout (5 minutes). Prevents unbounded waits. */
|
|
31
|
+
const MAX_RPC_TIMEOUT_MS = 5 * 60 * 1_000;
|
|
32
|
+
/** Timeout for the initialize RPC call. */
|
|
33
|
+
const INITIALIZE_TIMEOUT_MS = 15_000;
|
|
34
|
+
/** Timeout for the shutdown RPC call before escalating to SIGTERM. */
|
|
35
|
+
const SHUTDOWN_DRAIN_MS = 10_000;
|
|
36
|
+
/** Time to wait after SIGTERM before sending SIGKILL. */
|
|
37
|
+
const SIGTERM_GRACE_MS = 5_000;
|
|
38
|
+
/** Minimum backoff delay for crash recovery (1 second). */
|
|
39
|
+
const MIN_BACKOFF_MS = 1_000;
|
|
40
|
+
/** Maximum backoff delay for crash recovery (5 minutes). */
|
|
41
|
+
const MAX_BACKOFF_MS = 5 * 60 * 1_000;
|
|
42
|
+
/** Backoff multiplier on each consecutive crash. */
|
|
43
|
+
const BACKOFF_MULTIPLIER = 2;
|
|
44
|
+
/** Maximum number of consecutive crashes before giving up on auto-restart. */
|
|
45
|
+
const MAX_CONSECUTIVE_CRASHES = 10;
|
|
46
|
+
/** Time window in which crashes are considered consecutive (10 minutes). */
|
|
47
|
+
const CRASH_WINDOW_MS = 10 * 60 * 1_000;
|
|
48
|
+
/** Maximum number of stderr characters retained for worker failure context. */
|
|
49
|
+
const MAX_STDERR_EXCERPT_CHARS = 8_000;
|
|
50
|
+
export function appendStderrExcerpt(current, chunk) {
|
|
51
|
+
const next = current ? `${current}\n${chunk}` : chunk;
|
|
52
|
+
return next.length <= MAX_STDERR_EXCERPT_CHARS
|
|
53
|
+
? next
|
|
54
|
+
: next.slice(-MAX_STDERR_EXCERPT_CHARS);
|
|
55
|
+
}
|
|
56
|
+
export function formatWorkerFailureMessage(message, stderrExcerpt) {
|
|
57
|
+
const excerpt = stderrExcerpt.trim();
|
|
58
|
+
if (!excerpt)
|
|
59
|
+
return message;
|
|
60
|
+
if (message.includes(excerpt))
|
|
61
|
+
return message;
|
|
62
|
+
return `${message}\n\nWorker stderr:\n${excerpt}`;
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Implementation: createPluginWorkerHandle
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
/**
|
|
68
|
+
* Create a handle for a single plugin worker process.
|
|
69
|
+
*
|
|
70
|
+
* @internal Exported for testing; consumers should use `createPluginWorkerManager`.
|
|
71
|
+
*/
|
|
72
|
+
export function createPluginWorkerHandle(pluginId, options) {
|
|
73
|
+
const log = logger.child({ service: "plugin-worker", pluginId });
|
|
74
|
+
const emitter = new EventEmitter();
|
|
75
|
+
/**
|
|
76
|
+
* Higher than default (10) to accommodate multiple subscribers to
|
|
77
|
+
* crash/ready/exit events during integration tests and runtime monitoring.
|
|
78
|
+
*/
|
|
79
|
+
emitter.setMaxListeners(50);
|
|
80
|
+
// Worker process state
|
|
81
|
+
let childProcess = null;
|
|
82
|
+
let readline = null;
|
|
83
|
+
let stderrReadline = null;
|
|
84
|
+
let status = "stopped";
|
|
85
|
+
let startedAt = null;
|
|
86
|
+
let stderrExcerpt = "";
|
|
87
|
+
// Pending RPC requests awaiting a response
|
|
88
|
+
const pendingRequests = new Map();
|
|
89
|
+
let nextRequestId = 1;
|
|
90
|
+
// Optional methods reported by the worker during initialization
|
|
91
|
+
let supportedMethods = [];
|
|
92
|
+
// Crash tracking for exponential backoff
|
|
93
|
+
let consecutiveCrashes = 0;
|
|
94
|
+
let totalCrashes = 0;
|
|
95
|
+
let lastCrashAt = null;
|
|
96
|
+
let backoffTimer = null;
|
|
97
|
+
let nextRestartAt = null;
|
|
98
|
+
// Track open stream channels so we can emit synthetic close on crash.
|
|
99
|
+
// Maps channel → companyId.
|
|
100
|
+
const openStreamChannels = new Map();
|
|
101
|
+
// Shutdown coordination
|
|
102
|
+
let intentionalStop = false;
|
|
103
|
+
const rpcTimeoutMs = options.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS;
|
|
104
|
+
const autoRestart = options.autoRestart ?? true;
|
|
105
|
+
// -----------------------------------------------------------------------
|
|
106
|
+
// Status management
|
|
107
|
+
// -----------------------------------------------------------------------
|
|
108
|
+
function setStatus(newStatus) {
|
|
109
|
+
const prev = status;
|
|
110
|
+
if (prev === newStatus)
|
|
111
|
+
return;
|
|
112
|
+
status = newStatus;
|
|
113
|
+
log.debug({ from: prev, to: newStatus }, "worker status change");
|
|
114
|
+
emitter.emit("status", { pluginId, status: newStatus, previousStatus: prev });
|
|
115
|
+
}
|
|
116
|
+
// -----------------------------------------------------------------------
|
|
117
|
+
// JSON-RPC message sending
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
function sendMessage(message) {
|
|
120
|
+
if (!childProcess?.stdin?.writable) {
|
|
121
|
+
throw new Error(`Worker process for plugin "${pluginId}" is not writable`);
|
|
122
|
+
}
|
|
123
|
+
const serialized = serializeMessage(message);
|
|
124
|
+
childProcess.stdin.write(serialized);
|
|
125
|
+
}
|
|
126
|
+
// -----------------------------------------------------------------------
|
|
127
|
+
// Incoming message handling
|
|
128
|
+
// -----------------------------------------------------------------------
|
|
129
|
+
function handleLine(line) {
|
|
130
|
+
if (!line.trim())
|
|
131
|
+
return;
|
|
132
|
+
let message;
|
|
133
|
+
try {
|
|
134
|
+
message = parseMessage(line);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (err instanceof JsonRpcParseError) {
|
|
138
|
+
log.warn({ rawLine: line.slice(0, 200) }, "unparseable message from worker");
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
log.warn({ err }, "error parsing worker message");
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (isJsonRpcResponse(message)) {
|
|
146
|
+
handleResponse(message);
|
|
147
|
+
}
|
|
148
|
+
else if (isJsonRpcRequest(message)) {
|
|
149
|
+
handleWorkerRequest(message);
|
|
150
|
+
}
|
|
151
|
+
else if (isJsonRpcNotification(message)) {
|
|
152
|
+
handleWorkerNotification(message);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
log.warn("unknown message type from worker");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Handle a JSON-RPC response from the worker (matching a pending request).
|
|
160
|
+
*/
|
|
161
|
+
function handleResponse(response) {
|
|
162
|
+
const id = response.id;
|
|
163
|
+
if (id === null || id === undefined) {
|
|
164
|
+
log.warn("received response with null/undefined id");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const pending = pendingRequests.get(id);
|
|
168
|
+
if (!pending) {
|
|
169
|
+
log.warn({ id }, "received response for unknown request id");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
clearTimeout(pending.timer);
|
|
173
|
+
pendingRequests.delete(id);
|
|
174
|
+
pending.resolve(response);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handle a JSON-RPC request from the worker (worker→host call).
|
|
178
|
+
*/
|
|
179
|
+
async function handleWorkerRequest(request) {
|
|
180
|
+
const method = request.method;
|
|
181
|
+
const handler = options.hostHandlers[method];
|
|
182
|
+
if (!handler) {
|
|
183
|
+
log.warn({ method }, "worker called unregistered host method");
|
|
184
|
+
try {
|
|
185
|
+
sendMessage(createErrorResponse(request.id, JSONRPC_ERROR_CODES.METHOD_NOT_FOUND, `Host does not handle method "${method}"`));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Worker may have exited, ignore send error
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const result = await handler(request.params);
|
|
194
|
+
sendMessage({
|
|
195
|
+
jsonrpc: JSONRPC_VERSION,
|
|
196
|
+
id: request.id,
|
|
197
|
+
result: result ?? null,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
202
|
+
log.error({ method, err: errorMessage }, "host handler error");
|
|
203
|
+
try {
|
|
204
|
+
sendMessage(createErrorResponse(request.id, JSONRPC_ERROR_CODES.INTERNAL_ERROR, errorMessage));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Worker may have exited, ignore send error
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Handle a JSON-RPC notification from the worker (fire-and-forget).
|
|
213
|
+
*
|
|
214
|
+
* The `log` notification is the primary case — worker `ctx.logger` calls
|
|
215
|
+
* arrive here. We append structured plugin context (pluginId, timestamp,
|
|
216
|
+
* level) so that every log entry is queryable per the spec (§26.1).
|
|
217
|
+
*/
|
|
218
|
+
function handleWorkerNotification(notification) {
|
|
219
|
+
if (notification.method === "log") {
|
|
220
|
+
const params = notification.params;
|
|
221
|
+
const level = params?.level ?? "info";
|
|
222
|
+
const msg = params?.message ?? "";
|
|
223
|
+
const meta = params?.meta;
|
|
224
|
+
// Build a structured log object that includes the plugin context fields
|
|
225
|
+
// required by §26.1: pluginId, timestamp, level, message, and metadata.
|
|
226
|
+
// The child logger already carries `pluginId` in its bindings, but we
|
|
227
|
+
// add explicit `pluginLogLevel` and `pluginTimestamp` so downstream
|
|
228
|
+
// consumers (log storage, UI queries) can filter without parsing.
|
|
229
|
+
const logFields = {
|
|
230
|
+
...meta,
|
|
231
|
+
pluginLogLevel: level,
|
|
232
|
+
pluginTimestamp: new Date().toISOString(),
|
|
233
|
+
};
|
|
234
|
+
if (level === "error") {
|
|
235
|
+
log.error(logFields, `[plugin] ${msg}`);
|
|
236
|
+
}
|
|
237
|
+
else if (level === "warn") {
|
|
238
|
+
log.warn(logFields, `[plugin] ${msg}`);
|
|
239
|
+
}
|
|
240
|
+
else if (level === "debug") {
|
|
241
|
+
log.debug(logFields, `[plugin] ${msg}`);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
log.info(logFields, `[plugin] ${msg}`);
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Stream notifications: forward to the stream bus via callback
|
|
249
|
+
if (notification.method === "streams.open" ||
|
|
250
|
+
notification.method === "streams.emit" ||
|
|
251
|
+
notification.method === "streams.close") {
|
|
252
|
+
const params = (notification.params ?? {});
|
|
253
|
+
// Track open channels so we can emit synthetic close on crash
|
|
254
|
+
if (notification.method === "streams.open") {
|
|
255
|
+
const ch = String(params.channel ?? "");
|
|
256
|
+
const co = String(params.companyId ?? "");
|
|
257
|
+
if (ch)
|
|
258
|
+
openStreamChannels.set(ch, co);
|
|
259
|
+
}
|
|
260
|
+
else if (notification.method === "streams.close") {
|
|
261
|
+
openStreamChannels.delete(String(params.channel ?? ""));
|
|
262
|
+
}
|
|
263
|
+
if (options.onStreamNotification) {
|
|
264
|
+
try {
|
|
265
|
+
options.onStreamNotification(notification.method, params);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
log.error({
|
|
269
|
+
method: notification.method,
|
|
270
|
+
err: err instanceof Error ? err.message : String(err),
|
|
271
|
+
}, "stream notification handler failed");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
log.debug({ method: notification.method }, "received notification from worker");
|
|
277
|
+
}
|
|
278
|
+
// -----------------------------------------------------------------------
|
|
279
|
+
// Process lifecycle
|
|
280
|
+
// -----------------------------------------------------------------------
|
|
281
|
+
function spawnProcess() {
|
|
282
|
+
// Security: Do NOT spread process.env into the worker. Plugins should only
|
|
283
|
+
// receive a minimal, controlled environment to prevent leaking host
|
|
284
|
+
// secrets (like DATABASE_URL, internal API keys, etc.).
|
|
285
|
+
const workerEnv = {
|
|
286
|
+
...options.env,
|
|
287
|
+
PATH: process.env.PATH ?? "",
|
|
288
|
+
NODE_PATH: process.env.NODE_PATH ?? "",
|
|
289
|
+
FIDELIOS_PLUGIN_ID: pluginId,
|
|
290
|
+
NODE_ENV: process.env.NODE_ENV ?? "production",
|
|
291
|
+
TZ: process.env.TZ ?? "UTC",
|
|
292
|
+
};
|
|
293
|
+
const child = fork(options.entrypointPath, [], {
|
|
294
|
+
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
295
|
+
execArgv: options.execArgv ?? [],
|
|
296
|
+
env: workerEnv,
|
|
297
|
+
// Don't let the child keep the parent alive
|
|
298
|
+
detached: false,
|
|
299
|
+
});
|
|
300
|
+
return child;
|
|
301
|
+
}
|
|
302
|
+
function attachStdioHandlers(child) {
|
|
303
|
+
// Read NDJSON from stdout
|
|
304
|
+
if (child.stdout) {
|
|
305
|
+
readline = createInterface({ input: child.stdout });
|
|
306
|
+
readline.on("line", handleLine);
|
|
307
|
+
}
|
|
308
|
+
// Capture stderr for logging
|
|
309
|
+
if (child.stderr) {
|
|
310
|
+
stderrReadline = createInterface({ input: child.stderr });
|
|
311
|
+
stderrReadline.on("line", (line) => {
|
|
312
|
+
stderrExcerpt = appendStderrExcerpt(stderrExcerpt, line);
|
|
313
|
+
log.warn({ stream: "stderr" }, `[plugin stderr] ${line}`);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// Handle process exit
|
|
317
|
+
child.on("exit", (code, signal) => {
|
|
318
|
+
handleProcessExit(code, signal);
|
|
319
|
+
});
|
|
320
|
+
// Handle process errors (e.g. spawn failure)
|
|
321
|
+
child.on("error", (err) => {
|
|
322
|
+
log.error({ err: err.message }, "worker process error");
|
|
323
|
+
emitter.emit("error", { pluginId, error: err });
|
|
324
|
+
if (status === "starting") {
|
|
325
|
+
setStatus("crashed");
|
|
326
|
+
rejectAllPending(new Error(formatWorkerFailureMessage(`Worker process failed to start: ${err.message}`, stderrExcerpt)));
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function handleProcessExit(code, signal) {
|
|
331
|
+
const wasIntentional = intentionalStop;
|
|
332
|
+
// Clean up readline interfaces
|
|
333
|
+
if (readline) {
|
|
334
|
+
readline.close();
|
|
335
|
+
readline = null;
|
|
336
|
+
}
|
|
337
|
+
if (stderrReadline) {
|
|
338
|
+
stderrReadline.close();
|
|
339
|
+
stderrReadline = null;
|
|
340
|
+
}
|
|
341
|
+
childProcess = null;
|
|
342
|
+
startedAt = null;
|
|
343
|
+
// Reject all pending requests
|
|
344
|
+
rejectAllPending(new Error(formatWorkerFailureMessage(`Worker process exited (code=${code}, signal=${signal})`, stderrExcerpt)));
|
|
345
|
+
// Emit synthetic close for any orphaned stream channels so SSE clients
|
|
346
|
+
// are notified instead of hanging indefinitely.
|
|
347
|
+
if (openStreamChannels.size > 0 && options.onStreamNotification) {
|
|
348
|
+
for (const [channel, companyId] of openStreamChannels) {
|
|
349
|
+
try {
|
|
350
|
+
options.onStreamNotification("streams.close", { channel, companyId });
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// Best-effort cleanup — don't let it interfere with exit handling
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
openStreamChannels.clear();
|
|
357
|
+
}
|
|
358
|
+
emitter.emit("exit", { pluginId, code, signal });
|
|
359
|
+
if (wasIntentional) {
|
|
360
|
+
// Graceful stop — status is already "stopping" or will be set to "stopped"
|
|
361
|
+
setStatus("stopped");
|
|
362
|
+
log.info({ code, signal }, "worker process stopped");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
// Unexpected exit — crash recovery
|
|
366
|
+
totalCrashes++;
|
|
367
|
+
const now = Date.now();
|
|
368
|
+
// Reset consecutive crash counter if enough time passed
|
|
369
|
+
if (lastCrashAt !== null && now - lastCrashAt > CRASH_WINDOW_MS) {
|
|
370
|
+
consecutiveCrashes = 0;
|
|
371
|
+
}
|
|
372
|
+
consecutiveCrashes++;
|
|
373
|
+
lastCrashAt = now;
|
|
374
|
+
log.error({ code, signal, consecutiveCrashes, totalCrashes }, "worker process crashed");
|
|
375
|
+
const willRestart = autoRestart && consecutiveCrashes <= MAX_CONSECUTIVE_CRASHES;
|
|
376
|
+
setStatus("crashed");
|
|
377
|
+
emitter.emit("crash", { pluginId, code, signal, willRestart });
|
|
378
|
+
if (willRestart) {
|
|
379
|
+
scheduleRestart();
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
log.error({ consecutiveCrashes, maxCrashes: MAX_CONSECUTIVE_CRASHES }, "max consecutive crashes reached, not restarting");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function rejectAllPending(error) {
|
|
386
|
+
for (const [id, pending] of pendingRequests) {
|
|
387
|
+
clearTimeout(pending.timer);
|
|
388
|
+
pending.resolve(createErrorResponse(pending.id, PLUGIN_RPC_ERROR_CODES.WORKER_UNAVAILABLE, error.message));
|
|
389
|
+
}
|
|
390
|
+
pendingRequests.clear();
|
|
391
|
+
}
|
|
392
|
+
// -----------------------------------------------------------------------
|
|
393
|
+
// Crash recovery with exponential backoff
|
|
394
|
+
// -----------------------------------------------------------------------
|
|
395
|
+
function computeBackoffMs() {
|
|
396
|
+
// Exponential backoff: MIN_BACKOFF * MULTIPLIER^(consecutiveCrashes - 1)
|
|
397
|
+
const delay = MIN_BACKOFF_MS * Math.pow(BACKOFF_MULTIPLIER, consecutiveCrashes - 1);
|
|
398
|
+
// Add jitter: ±25%
|
|
399
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
400
|
+
return Math.min(Math.round(delay + jitter), MAX_BACKOFF_MS);
|
|
401
|
+
}
|
|
402
|
+
function scheduleRestart() {
|
|
403
|
+
const delay = computeBackoffMs();
|
|
404
|
+
nextRestartAt = Date.now() + delay;
|
|
405
|
+
setStatus("backoff");
|
|
406
|
+
log.info({ delayMs: delay, consecutiveCrashes }, "scheduling restart with backoff");
|
|
407
|
+
backoffTimer = setTimeout(async () => {
|
|
408
|
+
backoffTimer = null;
|
|
409
|
+
nextRestartAt = null;
|
|
410
|
+
try {
|
|
411
|
+
await startInternal();
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
log.error({ err: err instanceof Error ? err.message : String(err) }, "restart after backoff failed");
|
|
415
|
+
}
|
|
416
|
+
}, delay);
|
|
417
|
+
}
|
|
418
|
+
function cancelPendingRestart() {
|
|
419
|
+
if (backoffTimer !== null) {
|
|
420
|
+
clearTimeout(backoffTimer);
|
|
421
|
+
backoffTimer = null;
|
|
422
|
+
nextRestartAt = null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// -----------------------------------------------------------------------
|
|
426
|
+
// Start / Stop
|
|
427
|
+
// -----------------------------------------------------------------------
|
|
428
|
+
async function startInternal() {
|
|
429
|
+
if (status === "running" || status === "starting") {
|
|
430
|
+
throw new Error(`Worker for plugin "${pluginId}" is already ${status}`);
|
|
431
|
+
}
|
|
432
|
+
intentionalStop = false;
|
|
433
|
+
setStatus("starting");
|
|
434
|
+
stderrExcerpt = "";
|
|
435
|
+
const child = spawnProcess();
|
|
436
|
+
childProcess = child;
|
|
437
|
+
attachStdioHandlers(child);
|
|
438
|
+
startedAt = Date.now();
|
|
439
|
+
// Send the initialize RPC call
|
|
440
|
+
const initParams = {
|
|
441
|
+
manifest: options.manifest,
|
|
442
|
+
config: options.config,
|
|
443
|
+
instanceInfo: options.instanceInfo,
|
|
444
|
+
apiVersion: options.apiVersion,
|
|
445
|
+
};
|
|
446
|
+
try {
|
|
447
|
+
const result = await callInternal("initialize", initParams, INITIALIZE_TIMEOUT_MS);
|
|
448
|
+
if (!result || !result.ok) {
|
|
449
|
+
throw new Error("Worker initialize returned ok=false");
|
|
450
|
+
}
|
|
451
|
+
supportedMethods = result.supportedMethods ?? [];
|
|
452
|
+
}
|
|
453
|
+
catch (err) {
|
|
454
|
+
// Initialize failed — kill the process and propagate
|
|
455
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
456
|
+
log.error({ err: msg }, "worker initialize failed");
|
|
457
|
+
await killProcess();
|
|
458
|
+
setStatus("crashed");
|
|
459
|
+
throw new Error(`Worker initialize failed for "${pluginId}": ${msg}`);
|
|
460
|
+
}
|
|
461
|
+
// Reset crash counter on successful start
|
|
462
|
+
consecutiveCrashes = 0;
|
|
463
|
+
setStatus("running");
|
|
464
|
+
emitter.emit("ready", { pluginId });
|
|
465
|
+
log.info({ pid: child.pid }, "worker process started and initialized");
|
|
466
|
+
}
|
|
467
|
+
async function stopInternal() {
|
|
468
|
+
cancelPendingRestart();
|
|
469
|
+
if (status === "stopped" || status === "stopping") {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
intentionalStop = true;
|
|
473
|
+
setStatus("stopping");
|
|
474
|
+
if (!childProcess) {
|
|
475
|
+
setStatus("stopped");
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
// Step 1: Send shutdown RPC and wait for the worker to exit gracefully.
|
|
479
|
+
// We race the shutdown call against a timeout. The worker should process
|
|
480
|
+
// the shutdown and exit on its own within the drain period.
|
|
481
|
+
try {
|
|
482
|
+
await Promise.race([
|
|
483
|
+
callInternal("shutdown", {}, SHUTDOWN_DRAIN_MS),
|
|
484
|
+
waitForExit(SHUTDOWN_DRAIN_MS),
|
|
485
|
+
]);
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
// Shutdown call failed or timed out — proceed to kill
|
|
489
|
+
log.warn("shutdown RPC failed or timed out, escalating to SIGTERM");
|
|
490
|
+
}
|
|
491
|
+
// Give the process a brief moment to exit after the shutdown response
|
|
492
|
+
if (childProcess) {
|
|
493
|
+
await waitForExit(500);
|
|
494
|
+
}
|
|
495
|
+
// Check if process already exited
|
|
496
|
+
if (!childProcess) {
|
|
497
|
+
setStatus("stopped");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// Step 2: Send SIGTERM and wait
|
|
501
|
+
log.info("worker did not exit after shutdown RPC, sending SIGTERM");
|
|
502
|
+
await killWithSignal("SIGTERM", SIGTERM_GRACE_MS);
|
|
503
|
+
if (!childProcess) {
|
|
504
|
+
setStatus("stopped");
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
// Step 3: Forcefully kill with SIGKILL
|
|
508
|
+
log.warn("worker did not exit after SIGTERM, sending SIGKILL");
|
|
509
|
+
await killWithSignal("SIGKILL", 2_000);
|
|
510
|
+
if (childProcess) {
|
|
511
|
+
log.error("worker process still alive after SIGKILL — this should not happen");
|
|
512
|
+
}
|
|
513
|
+
setStatus("stopped");
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Wait for the child process to exit, up to `timeoutMs`.
|
|
517
|
+
* Resolves immediately if the process is already gone.
|
|
518
|
+
*/
|
|
519
|
+
function waitForExit(timeoutMs) {
|
|
520
|
+
return new Promise((resolve) => {
|
|
521
|
+
if (!childProcess) {
|
|
522
|
+
resolve();
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
let settled = false;
|
|
526
|
+
const timer = setTimeout(() => {
|
|
527
|
+
if (settled)
|
|
528
|
+
return;
|
|
529
|
+
settled = true;
|
|
530
|
+
resolve();
|
|
531
|
+
}, timeoutMs);
|
|
532
|
+
childProcess.once("exit", () => {
|
|
533
|
+
if (settled)
|
|
534
|
+
return;
|
|
535
|
+
settled = true;
|
|
536
|
+
clearTimeout(timer);
|
|
537
|
+
resolve();
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
function killWithSignal(signal, waitMs) {
|
|
542
|
+
return new Promise((resolve) => {
|
|
543
|
+
if (!childProcess) {
|
|
544
|
+
resolve();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const timer = setTimeout(() => {
|
|
548
|
+
resolve();
|
|
549
|
+
}, waitMs);
|
|
550
|
+
childProcess.once("exit", () => {
|
|
551
|
+
clearTimeout(timer);
|
|
552
|
+
resolve();
|
|
553
|
+
});
|
|
554
|
+
try {
|
|
555
|
+
childProcess.kill(signal);
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
clearTimeout(timer);
|
|
559
|
+
resolve();
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
async function killProcess() {
|
|
564
|
+
if (!childProcess)
|
|
565
|
+
return;
|
|
566
|
+
intentionalStop = true;
|
|
567
|
+
try {
|
|
568
|
+
childProcess.kill("SIGKILL");
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
// Process may already be dead
|
|
572
|
+
}
|
|
573
|
+
// Wait briefly for exit event
|
|
574
|
+
await new Promise((resolve) => {
|
|
575
|
+
if (!childProcess) {
|
|
576
|
+
resolve();
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const timer = setTimeout(() => {
|
|
580
|
+
resolve();
|
|
581
|
+
}, 1_000);
|
|
582
|
+
childProcess.once("exit", () => {
|
|
583
|
+
clearTimeout(timer);
|
|
584
|
+
resolve();
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
// -----------------------------------------------------------------------
|
|
589
|
+
// RPC call implementation
|
|
590
|
+
// -----------------------------------------------------------------------
|
|
591
|
+
function callInternal(method, params, timeoutMs) {
|
|
592
|
+
return new Promise((resolve, reject) => {
|
|
593
|
+
if (!childProcess?.stdin?.writable) {
|
|
594
|
+
reject(new Error(`Cannot call "${method}" — worker for "${pluginId}" is not running`));
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const id = nextRequestId++;
|
|
598
|
+
const timeout = Math.min(timeoutMs ?? rpcTimeoutMs, MAX_RPC_TIMEOUT_MS);
|
|
599
|
+
// Guard against double-settlement. When a process exits all pending
|
|
600
|
+
// requests are rejected via rejectAllPending(), but the timeout timer
|
|
601
|
+
// may still be running. Without this guard the timer's reject fires on
|
|
602
|
+
// an already-settled promise, producing an unhandled rejection.
|
|
603
|
+
let settled = false;
|
|
604
|
+
const settle = (fn, value) => {
|
|
605
|
+
if (settled)
|
|
606
|
+
return;
|
|
607
|
+
settled = true;
|
|
608
|
+
clearTimeout(timer);
|
|
609
|
+
pendingRequests.delete(id);
|
|
610
|
+
fn(value);
|
|
611
|
+
};
|
|
612
|
+
const timer = setTimeout(() => {
|
|
613
|
+
settle(reject, new JsonRpcCallError({
|
|
614
|
+
code: PLUGIN_RPC_ERROR_CODES.TIMEOUT,
|
|
615
|
+
message: `RPC call "${method}" timed out after ${timeout}ms`,
|
|
616
|
+
}));
|
|
617
|
+
}, timeout);
|
|
618
|
+
const pending = {
|
|
619
|
+
id,
|
|
620
|
+
method,
|
|
621
|
+
resolve: (response) => {
|
|
622
|
+
if (isJsonRpcSuccessResponse(response)) {
|
|
623
|
+
settle(resolve, response.result);
|
|
624
|
+
}
|
|
625
|
+
else if ("error" in response && response.error) {
|
|
626
|
+
settle(reject, new JsonRpcCallError(response.error));
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
settle(reject, new Error(`Unexpected response format for "${method}"`));
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
timer,
|
|
633
|
+
sentAt: Date.now(),
|
|
634
|
+
};
|
|
635
|
+
pendingRequests.set(id, pending);
|
|
636
|
+
try {
|
|
637
|
+
const request = createRequest(method, params, id);
|
|
638
|
+
sendMessage(request);
|
|
639
|
+
}
|
|
640
|
+
catch (err) {
|
|
641
|
+
clearTimeout(timer);
|
|
642
|
+
pendingRequests.delete(id);
|
|
643
|
+
reject(new Error(`Failed to send "${method}" to worker: ${err instanceof Error ? err.message : String(err)}`));
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
// -----------------------------------------------------------------------
|
|
648
|
+
// Public API
|
|
649
|
+
// -----------------------------------------------------------------------
|
|
650
|
+
const handle = {
|
|
651
|
+
get pluginId() {
|
|
652
|
+
return pluginId;
|
|
653
|
+
},
|
|
654
|
+
get status() {
|
|
655
|
+
return status;
|
|
656
|
+
},
|
|
657
|
+
get supportedMethods() {
|
|
658
|
+
return supportedMethods;
|
|
659
|
+
},
|
|
660
|
+
async start() {
|
|
661
|
+
await startInternal();
|
|
662
|
+
},
|
|
663
|
+
async stop() {
|
|
664
|
+
await stopInternal();
|
|
665
|
+
},
|
|
666
|
+
async restart() {
|
|
667
|
+
await stopInternal();
|
|
668
|
+
await startInternal();
|
|
669
|
+
},
|
|
670
|
+
call(method, params, timeoutMs) {
|
|
671
|
+
if (status !== "running" && status !== "starting") {
|
|
672
|
+
return Promise.reject(new Error(`Cannot call "${method}" — worker for "${pluginId}" is ${status}`));
|
|
673
|
+
}
|
|
674
|
+
return callInternal(method, params, timeoutMs);
|
|
675
|
+
},
|
|
676
|
+
notify(method, params) {
|
|
677
|
+
if (status !== "running")
|
|
678
|
+
return;
|
|
679
|
+
try {
|
|
680
|
+
sendMessage({
|
|
681
|
+
jsonrpc: JSONRPC_VERSION,
|
|
682
|
+
method,
|
|
683
|
+
params,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
log.warn({ method }, "failed to send notification to worker");
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
on(event, listener) {
|
|
691
|
+
emitter.on(event, listener);
|
|
692
|
+
},
|
|
693
|
+
off(event, listener) {
|
|
694
|
+
emitter.off(event, listener);
|
|
695
|
+
},
|
|
696
|
+
diagnostics() {
|
|
697
|
+
return {
|
|
698
|
+
pluginId,
|
|
699
|
+
status,
|
|
700
|
+
pid: childProcess?.pid ?? null,
|
|
701
|
+
uptime: startedAt !== null && status === "running"
|
|
702
|
+
? Date.now() - startedAt
|
|
703
|
+
: null,
|
|
704
|
+
consecutiveCrashes,
|
|
705
|
+
totalCrashes,
|
|
706
|
+
pendingRequests: pendingRequests.size,
|
|
707
|
+
lastCrashAt,
|
|
708
|
+
nextRestartAt,
|
|
709
|
+
};
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
return handle;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Create a new PluginWorkerManager.
|
|
716
|
+
*
|
|
717
|
+
* The manager holds all plugin worker handles and provides a unified API for
|
|
718
|
+
* starting, stopping, and communicating with plugin workers.
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```ts
|
|
722
|
+
* const manager = createPluginWorkerManager();
|
|
723
|
+
*
|
|
724
|
+
* const handle = await manager.startWorker("acme.linear", {
|
|
725
|
+
* entrypointPath: "/path/to/worker.cjs",
|
|
726
|
+
* manifest,
|
|
727
|
+
* config: resolvedConfig,
|
|
728
|
+
* instanceInfo: { instanceId: "inst-1", hostVersion: "1.0.0" },
|
|
729
|
+
* apiVersion: 1,
|
|
730
|
+
* hostHandlers: { "config.get": async () => resolvedConfig, ... },
|
|
731
|
+
* });
|
|
732
|
+
*
|
|
733
|
+
* // Send RPC call to the worker
|
|
734
|
+
* const health = await manager.call("acme.linear", "health", {});
|
|
735
|
+
*
|
|
736
|
+
* // Shutdown all workers on server exit
|
|
737
|
+
* await manager.stopAll();
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
740
|
+
export function createPluginWorkerManager(managerOptions) {
|
|
741
|
+
const log = logger.child({ service: "plugin-worker-manager" });
|
|
742
|
+
const workers = new Map();
|
|
743
|
+
/** Per-plugin startup locks to prevent concurrent spawn races. */
|
|
744
|
+
const startupLocks = new Map();
|
|
745
|
+
return {
|
|
746
|
+
async startWorker(pluginId, options) {
|
|
747
|
+
// Mutex: if a start is already in-flight for this plugin, wait for it
|
|
748
|
+
const inFlight = startupLocks.get(pluginId);
|
|
749
|
+
if (inFlight) {
|
|
750
|
+
log.warn({ pluginId }, "concurrent startWorker call — waiting for in-flight start");
|
|
751
|
+
return inFlight;
|
|
752
|
+
}
|
|
753
|
+
const existing = workers.get(pluginId);
|
|
754
|
+
if (existing && existing.status !== "stopped") {
|
|
755
|
+
throw new Error(`Worker already registered for plugin "${pluginId}" (status: ${existing.status})`);
|
|
756
|
+
}
|
|
757
|
+
const handle = createPluginWorkerHandle(pluginId, options);
|
|
758
|
+
workers.set(pluginId, handle);
|
|
759
|
+
// Subscribe to crash/ready events for live event forwarding
|
|
760
|
+
if (managerOptions?.onWorkerEvent) {
|
|
761
|
+
const notify = managerOptions.onWorkerEvent;
|
|
762
|
+
handle.on("crash", (payload) => {
|
|
763
|
+
notify({
|
|
764
|
+
type: "plugin.worker.crashed",
|
|
765
|
+
pluginId: payload.pluginId,
|
|
766
|
+
code: payload.code,
|
|
767
|
+
signal: payload.signal,
|
|
768
|
+
willRestart: payload.willRestart,
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
handle.on("ready", (payload) => {
|
|
772
|
+
// Only emit restarted if this was a crash recovery (totalCrashes > 0)
|
|
773
|
+
const diag = handle.diagnostics();
|
|
774
|
+
if (diag.totalCrashes > 0) {
|
|
775
|
+
notify({
|
|
776
|
+
type: "plugin.worker.restarted",
|
|
777
|
+
pluginId: payload.pluginId,
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
log.info({ pluginId }, "starting plugin worker");
|
|
783
|
+
// Set the lock before awaiting start() to prevent concurrent spawns
|
|
784
|
+
const startPromise = handle.start().then(() => handle).finally(() => {
|
|
785
|
+
startupLocks.delete(pluginId);
|
|
786
|
+
});
|
|
787
|
+
startupLocks.set(pluginId, startPromise);
|
|
788
|
+
return startPromise;
|
|
789
|
+
},
|
|
790
|
+
async stopWorker(pluginId) {
|
|
791
|
+
const handle = workers.get(pluginId);
|
|
792
|
+
if (!handle) {
|
|
793
|
+
log.warn({ pluginId }, "no worker registered for plugin, nothing to stop");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
log.info({ pluginId }, "stopping plugin worker");
|
|
797
|
+
await handle.stop();
|
|
798
|
+
workers.delete(pluginId);
|
|
799
|
+
},
|
|
800
|
+
getWorker(pluginId) {
|
|
801
|
+
return workers.get(pluginId);
|
|
802
|
+
},
|
|
803
|
+
isRunning(pluginId) {
|
|
804
|
+
const handle = workers.get(pluginId);
|
|
805
|
+
return handle?.status === "running";
|
|
806
|
+
},
|
|
807
|
+
async stopAll() {
|
|
808
|
+
log.info({ count: workers.size }, "stopping all plugin workers");
|
|
809
|
+
const promises = Array.from(workers.values()).map(async (handle) => {
|
|
810
|
+
try {
|
|
811
|
+
await handle.stop();
|
|
812
|
+
}
|
|
813
|
+
catch (err) {
|
|
814
|
+
log.error({
|
|
815
|
+
pluginId: handle.pluginId,
|
|
816
|
+
err: err instanceof Error ? err.message : String(err),
|
|
817
|
+
}, "error stopping worker during shutdown");
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
await Promise.all(promises);
|
|
821
|
+
workers.clear();
|
|
822
|
+
},
|
|
823
|
+
diagnostics() {
|
|
824
|
+
return Array.from(workers.values()).map((h) => h.diagnostics());
|
|
825
|
+
},
|
|
826
|
+
call(pluginId, method, params, timeoutMs) {
|
|
827
|
+
const handle = workers.get(pluginId);
|
|
828
|
+
if (!handle) {
|
|
829
|
+
return Promise.reject(new Error(`No worker registered for plugin "${pluginId}"`));
|
|
830
|
+
}
|
|
831
|
+
return handle.call(method, params, timeoutMs);
|
|
832
|
+
},
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
//# sourceMappingURL=plugin-worker-manager.js.map
|