@stoneforge/smithy 0.1.0
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/LICENSE +13 -0
- package/README.md +114 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +7 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/orchestrator-api.d.ts +153 -0
- package/dist/api/orchestrator-api.d.ts.map +1 -0
- package/dist/api/orchestrator-api.js +374 -0
- package/dist/api/orchestrator-api.js.map +1 -0
- package/dist/bin/sf.d.ts +3 -0
- package/dist/bin/sf.d.ts.map +1 -0
- package/dist/bin/sf.js +10 -0
- package/dist/bin/sf.js.map +1 -0
- package/dist/cli/commands/agent.d.ts +20 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +861 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +14 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +272 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/dispatch.d.ts +9 -0
- package/dist/cli/commands/dispatch.d.ts.map +1 -0
- package/dist/cli/commands/dispatch.js +128 -0
- package/dist/cli/commands/dispatch.js.map +1 -0
- package/dist/cli/commands/merge.d.ts +11 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +246 -0
- package/dist/cli/commands/merge.js.map +1 -0
- package/dist/cli/commands/pool.d.ts +21 -0
- package/dist/cli/commands/pool.d.ts.map +1 -0
- package/dist/cli/commands/pool.js +762 -0
- package/dist/cli/commands/pool.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +54 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +57 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/task.d.ts +36 -0
- package/dist/cli/commands/task.d.ts.map +1 -0
- package/dist/cli/commands/task.js +889 -0
- package/dist/cli/commands/task.js.map +1 -0
- package/dist/cli/commands/test-orchestration.d.ts +32 -0
- package/dist/cli/commands/test-orchestration.d.ts.map +1 -0
- package/dist/cli/commands/test-orchestration.js +392 -0
- package/dist/cli/commands/test-orchestration.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/plugin.d.ts +23 -0
- package/dist/cli/plugin.d.ts.map +1 -0
- package/dist/cli/plugin.js +36 -0
- package/dist/cli/plugin.js.map +1 -0
- package/dist/git/index.d.ts +10 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +12 -0
- package/dist/git/index.js.map +1 -0
- package/dist/git/merge.d.ts +79 -0
- package/dist/git/merge.d.ts.map +1 -0
- package/dist/git/merge.js +254 -0
- package/dist/git/merge.js.map +1 -0
- package/dist/git/worktree-manager.d.ts +299 -0
- package/dist/git/worktree-manager.d.ts.map +1 -0
- package/dist/git/worktree-manager.js +744 -0
- package/dist/git/worktree-manager.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/director.md +272 -0
- package/dist/prompts/index.d.ts +100 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +294 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/message-triage.md +50 -0
- package/dist/prompts/persistent-worker.md +240 -0
- package/dist/prompts/steward-base.md +64 -0
- package/dist/prompts/steward-docs.md +118 -0
- package/dist/prompts/steward-health.md +39 -0
- package/dist/prompts/steward-merge.md +168 -0
- package/dist/prompts/steward-ops.md +28 -0
- package/dist/prompts/steward-reminder.md +26 -0
- package/dist/prompts/worker.md +282 -0
- package/dist/providers/claude/headless.d.ts +18 -0
- package/dist/providers/claude/headless.d.ts.map +1 -0
- package/dist/providers/claude/headless.js +307 -0
- package/dist/providers/claude/headless.js.map +1 -0
- package/dist/providers/claude/index.d.ts +24 -0
- package/dist/providers/claude/index.d.ts.map +1 -0
- package/dist/providers/claude/index.js +80 -0
- package/dist/providers/claude/index.js.map +1 -0
- package/dist/providers/claude/interactive.d.ts +21 -0
- package/dist/providers/claude/interactive.d.ts.map +1 -0
- package/dist/providers/claude/interactive.js +142 -0
- package/dist/providers/claude/interactive.js.map +1 -0
- package/dist/providers/codex/event-mapper.d.ts +91 -0
- package/dist/providers/codex/event-mapper.d.ts.map +1 -0
- package/dist/providers/codex/event-mapper.js +299 -0
- package/dist/providers/codex/event-mapper.js.map +1 -0
- package/dist/providers/codex/headless.d.ts +20 -0
- package/dist/providers/codex/headless.d.ts.map +1 -0
- package/dist/providers/codex/headless.js +174 -0
- package/dist/providers/codex/headless.js.map +1 -0
- package/dist/providers/codex/index.d.ts +30 -0
- package/dist/providers/codex/index.d.ts.map +1 -0
- package/dist/providers/codex/index.js +55 -0
- package/dist/providers/codex/index.js.map +1 -0
- package/dist/providers/codex/interactive.d.ts +21 -0
- package/dist/providers/codex/interactive.d.ts.map +1 -0
- package/dist/providers/codex/interactive.js +141 -0
- package/dist/providers/codex/interactive.js.map +1 -0
- package/dist/providers/codex/jsonrpc-client.d.ts +52 -0
- package/dist/providers/codex/jsonrpc-client.d.ts.map +1 -0
- package/dist/providers/codex/jsonrpc-client.js +141 -0
- package/dist/providers/codex/jsonrpc-client.js.map +1 -0
- package/dist/providers/codex/server-manager.d.ts +100 -0
- package/dist/providers/codex/server-manager.d.ts.map +1 -0
- package/dist/providers/codex/server-manager.js +153 -0
- package/dist/providers/codex/server-manager.js.map +1 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/opencode/async-queue.d.ts +21 -0
- package/dist/providers/opencode/async-queue.d.ts.map +1 -0
- package/dist/providers/opencode/async-queue.js +51 -0
- package/dist/providers/opencode/async-queue.js.map +1 -0
- package/dist/providers/opencode/event-mapper.d.ts +132 -0
- package/dist/providers/opencode/event-mapper.d.ts.map +1 -0
- package/dist/providers/opencode/event-mapper.js +204 -0
- package/dist/providers/opencode/event-mapper.js.map +1 -0
- package/dist/providers/opencode/headless.d.ts +25 -0
- package/dist/providers/opencode/headless.d.ts.map +1 -0
- package/dist/providers/opencode/headless.js +190 -0
- package/dist/providers/opencode/headless.js.map +1 -0
- package/dist/providers/opencode/index.d.ts +33 -0
- package/dist/providers/opencode/index.d.ts.map +1 -0
- package/dist/providers/opencode/index.js +42 -0
- package/dist/providers/opencode/index.js.map +1 -0
- package/dist/providers/opencode/interactive.d.ts +21 -0
- package/dist/providers/opencode/interactive.d.ts.map +1 -0
- package/dist/providers/opencode/interactive.js +135 -0
- package/dist/providers/opencode/interactive.js.map +1 -0
- package/dist/providers/opencode/server-manager.d.ts +145 -0
- package/dist/providers/opencode/server-manager.d.ts.map +1 -0
- package/dist/providers/opencode/server-manager.js +163 -0
- package/dist/providers/opencode/server-manager.js.map +1 -0
- package/dist/providers/registry.d.ts +38 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +82 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +144 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +25 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/runtime/event-utils.d.ts +8 -0
- package/dist/runtime/event-utils.d.ts.map +1 -0
- package/dist/runtime/event-utils.js +23 -0
- package/dist/runtime/event-utils.js.map +1 -0
- package/dist/runtime/handoff.d.ts +195 -0
- package/dist/runtime/handoff.d.ts.map +1 -0
- package/dist/runtime/handoff.js +332 -0
- package/dist/runtime/handoff.js.map +1 -0
- package/dist/runtime/index.d.ts +17 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +60 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/message-mapper.d.ts +99 -0
- package/dist/runtime/message-mapper.d.ts.map +1 -0
- package/dist/runtime/message-mapper.js +202 -0
- package/dist/runtime/message-mapper.js.map +1 -0
- package/dist/runtime/predecessor-query.d.ts +212 -0
- package/dist/runtime/predecessor-query.d.ts.map +1 -0
- package/dist/runtime/predecessor-query.js +283 -0
- package/dist/runtime/predecessor-query.js.map +1 -0
- package/dist/runtime/session-manager.d.ts +466 -0
- package/dist/runtime/session-manager.d.ts.map +1 -0
- package/dist/runtime/session-manager.js +986 -0
- package/dist/runtime/session-manager.js.map +1 -0
- package/dist/runtime/spawner.d.ts +407 -0
- package/dist/runtime/spawner.d.ts.map +1 -0
- package/dist/runtime/spawner.js +781 -0
- package/dist/runtime/spawner.js.map +1 -0
- package/dist/server/config.d.ts +22 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +59 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/daemon-state.d.ts +50 -0
- package/dist/server/daemon-state.d.ts.map +1 -0
- package/dist/server/daemon-state.js +100 -0
- package/dist/server/daemon-state.js.map +1 -0
- package/dist/server/events-websocket.d.ts +32 -0
- package/dist/server/events-websocket.d.ts.map +1 -0
- package/dist/server/events-websocket.js +96 -0
- package/dist/server/events-websocket.js.map +1 -0
- package/dist/server/formatters.d.ts +94 -0
- package/dist/server/formatters.d.ts.map +1 -0
- package/dist/server/formatters.js +142 -0
- package/dist/server/formatters.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +153 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lsp-websocket.d.ts +33 -0
- package/dist/server/lsp-websocket.d.ts.map +1 -0
- package/dist/server/lsp-websocket.js +161 -0
- package/dist/server/lsp-websocket.js.map +1 -0
- package/dist/server/routes/agents.d.ts +9 -0
- package/dist/server/routes/agents.d.ts.map +1 -0
- package/dist/server/routes/agents.js +369 -0
- package/dist/server/routes/agents.js.map +1 -0
- package/dist/server/routes/daemon.d.ts +13 -0
- package/dist/server/routes/daemon.d.ts.map +1 -0
- package/dist/server/routes/daemon.js +187 -0
- package/dist/server/routes/daemon.js.map +1 -0
- package/dist/server/routes/events.d.ts +23 -0
- package/dist/server/routes/events.d.ts.map +1 -0
- package/dist/server/routes/events.js +282 -0
- package/dist/server/routes/events.js.map +1 -0
- package/dist/server/routes/extensions.d.ts +9 -0
- package/dist/server/routes/extensions.d.ts.map +1 -0
- package/dist/server/routes/extensions.js +202 -0
- package/dist/server/routes/extensions.js.map +1 -0
- package/dist/server/routes/health.d.ts +7 -0
- package/dist/server/routes/health.d.ts.map +1 -0
- package/dist/server/routes/health.js +33 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/index.d.ts +21 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +21 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/lsp.d.ts +9 -0
- package/dist/server/routes/lsp.d.ts.map +1 -0
- package/dist/server/routes/lsp.js +50 -0
- package/dist/server/routes/lsp.js.map +1 -0
- package/dist/server/routes/plugins.d.ts +9 -0
- package/dist/server/routes/plugins.d.ts.map +1 -0
- package/dist/server/routes/plugins.js +109 -0
- package/dist/server/routes/plugins.js.map +1 -0
- package/dist/server/routes/pools.d.ts +9 -0
- package/dist/server/routes/pools.d.ts.map +1 -0
- package/dist/server/routes/pools.js +189 -0
- package/dist/server/routes/pools.js.map +1 -0
- package/dist/server/routes/scheduler.d.ts +9 -0
- package/dist/server/routes/scheduler.d.ts.map +1 -0
- package/dist/server/routes/scheduler.js +162 -0
- package/dist/server/routes/scheduler.js.map +1 -0
- package/dist/server/routes/sessions.d.ts +27 -0
- package/dist/server/routes/sessions.d.ts.map +1 -0
- package/dist/server/routes/sessions.js +773 -0
- package/dist/server/routes/sessions.js.map +1 -0
- package/dist/server/routes/tasks.d.ts +9 -0
- package/dist/server/routes/tasks.d.ts.map +1 -0
- package/dist/server/routes/tasks.js +954 -0
- package/dist/server/routes/tasks.js.map +1 -0
- package/dist/server/routes/upload.d.ts +8 -0
- package/dist/server/routes/upload.d.ts.map +1 -0
- package/dist/server/routes/upload.js +40 -0
- package/dist/server/routes/upload.js.map +1 -0
- package/dist/server/routes/workflows.d.ts +9 -0
- package/dist/server/routes/workflows.d.ts.map +1 -0
- package/dist/server/routes/workflows.js +532 -0
- package/dist/server/routes/workflows.js.map +1 -0
- package/dist/server/routes/workspace-files.d.ts +12 -0
- package/dist/server/routes/workspace-files.d.ts.map +1 -0
- package/dist/server/routes/workspace-files.js +520 -0
- package/dist/server/routes/workspace-files.js.map +1 -0
- package/dist/server/routes/worktrees.d.ts +9 -0
- package/dist/server/routes/worktrees.d.ts.map +1 -0
- package/dist/server/routes/worktrees.js +94 -0
- package/dist/server/routes/worktrees.js.map +1 -0
- package/dist/server/server.d.ts +14 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +258 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/services/lsp-manager.d.ts +93 -0
- package/dist/server/services/lsp-manager.d.ts.map +1 -0
- package/dist/server/services/lsp-manager.js +291 -0
- package/dist/server/services/lsp-manager.js.map +1 -0
- package/dist/server/services/session-messages.d.ts +61 -0
- package/dist/server/services/session-messages.d.ts.map +1 -0
- package/dist/server/services/session-messages.js +101 -0
- package/dist/server/services/session-messages.js.map +1 -0
- package/dist/server/services.d.ts +35 -0
- package/dist/server/services.d.ts.map +1 -0
- package/dist/server/services.js +159 -0
- package/dist/server/services.js.map +1 -0
- package/dist/server/static.d.ts +18 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +71 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/types.d.ts +20 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +7 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/websocket.d.ts +16 -0
- package/dist/server/websocket.d.ts.map +1 -0
- package/dist/server/websocket.js +143 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/services/agent-pool-service.d.ts +181 -0
- package/dist/services/agent-pool-service.d.ts.map +1 -0
- package/dist/services/agent-pool-service.js +590 -0
- package/dist/services/agent-pool-service.js.map +1 -0
- package/dist/services/agent-registry.d.ts +185 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +432 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/dispatch-daemon.d.ts +429 -0
- package/dist/services/dispatch-daemon.d.ts.map +1 -0
- package/dist/services/dispatch-daemon.js +1833 -0
- package/dist/services/dispatch-daemon.js.map +1 -0
- package/dist/services/dispatch-service.d.ts +148 -0
- package/dist/services/dispatch-service.d.ts.map +1 -0
- package/dist/services/dispatch-service.js +170 -0
- package/dist/services/dispatch-service.js.map +1 -0
- package/dist/services/docs-steward-service.d.ts +199 -0
- package/dist/services/docs-steward-service.d.ts.map +1 -0
- package/dist/services/docs-steward-service.js +599 -0
- package/dist/services/docs-steward-service.js.map +1 -0
- package/dist/services/health-steward-service.d.ts +446 -0
- package/dist/services/health-steward-service.d.ts.map +1 -0
- package/dist/services/health-steward-service.js +866 -0
- package/dist/services/health-steward-service.js.map +1 -0
- package/dist/services/index.d.ts +26 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +111 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/merge-request-provider.d.ts +59 -0
- package/dist/services/merge-request-provider.d.ts.map +1 -0
- package/dist/services/merge-request-provider.js +89 -0
- package/dist/services/merge-request-provider.js.map +1 -0
- package/dist/services/merge-steward-service.d.ts +268 -0
- package/dist/services/merge-steward-service.d.ts.map +1 -0
- package/dist/services/merge-steward-service.js +568 -0
- package/dist/services/merge-steward-service.js.map +1 -0
- package/dist/services/plugin-executor.d.ts +247 -0
- package/dist/services/plugin-executor.d.ts.map +1 -0
- package/dist/services/plugin-executor.js +451 -0
- package/dist/services/plugin-executor.js.map +1 -0
- package/dist/services/role-definition-service.d.ts +117 -0
- package/dist/services/role-definition-service.d.ts.map +1 -0
- package/dist/services/role-definition-service.js +289 -0
- package/dist/services/role-definition-service.js.map +1 -0
- package/dist/services/steward-scheduler.d.ts +336 -0
- package/dist/services/steward-scheduler.d.ts.map +1 -0
- package/dist/services/steward-scheduler.js +732 -0
- package/dist/services/steward-scheduler.js.map +1 -0
- package/dist/services/task-assignment-service.d.ts +291 -0
- package/dist/services/task-assignment-service.d.ts.map +1 -0
- package/dist/services/task-assignment-service.js +454 -0
- package/dist/services/task-assignment-service.js.map +1 -0
- package/dist/services/worker-task-service.d.ts +202 -0
- package/dist/services/worker-task-service.d.ts.map +1 -0
- package/dist/services/worker-task-service.js +228 -0
- package/dist/services/worker-task-service.js.map +1 -0
- package/dist/testing/index.d.ts +13 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/orchestration-tests.d.ts +62 -0
- package/dist/testing/orchestration-tests.d.ts.map +1 -0
- package/dist/testing/orchestration-tests.js +1115 -0
- package/dist/testing/orchestration-tests.js.map +1 -0
- package/dist/testing/test-context.d.ts +171 -0
- package/dist/testing/test-context.d.ts.map +1 -0
- package/dist/testing/test-context.js +665 -0
- package/dist/testing/test-context.js.map +1 -0
- package/dist/testing/test-prompts.d.ts +46 -0
- package/dist/testing/test-prompts.d.ts.map +1 -0
- package/dist/testing/test-prompts.js +140 -0
- package/dist/testing/test-prompts.js.map +1 -0
- package/dist/testing/test-utils.d.ts +200 -0
- package/dist/testing/test-utils.d.ts.map +1 -0
- package/dist/testing/test-utils.js +378 -0
- package/dist/testing/test-utils.js.map +1 -0
- package/dist/types/agent-pool.d.ts +215 -0
- package/dist/types/agent-pool.d.ts.map +1 -0
- package/dist/types/agent-pool.js +143 -0
- package/dist/types/agent-pool.js.map +1 -0
- package/dist/types/agent.d.ts +265 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +127 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +40 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message-types.d.ts +294 -0
- package/dist/types/message-types.d.ts.map +1 -0
- package/dist/types/message-types.js +354 -0
- package/dist/types/message-types.js.map +1 -0
- package/dist/types/role-definition.d.ts +272 -0
- package/dist/types/role-definition.d.ts.map +1 -0
- package/dist/types/role-definition.js +144 -0
- package/dist/types/role-definition.js.map +1 -0
- package/dist/types/task-meta.d.ts +248 -0
- package/dist/types/task-meta.d.ts.map +1 -0
- package/dist/types/task-meta.js +213 -0
- package/dist/types/task-meta.js.map +1 -0
- package/package.json +120 -0
- package/web/assets/abap-BrgZPUOV.js +6 -0
- package/web/assets/apex-DyP6w7ZV.js +6 -0
- package/web/assets/azcli-BaLxmfj-.js +6 -0
- package/web/assets/bat-CFOPXBzS.js +6 -0
- package/web/assets/bicep-BfEKNvv3.js +7 -0
- package/web/assets/cameligo-BFG1Mk7z.js +6 -0
- package/web/assets/clojure-DTECt2xU.js +6 -0
- package/web/assets/codicon-DCmgc-ay.ttf +0 -0
- package/web/assets/coffee-CDGzqUPQ.js +6 -0
- package/web/assets/cpp-CLLBncYj.js +6 -0
- package/web/assets/csharp-dUCx_-0o.js +6 -0
- package/web/assets/csp-5Rap-vPy.js +6 -0
- package/web/assets/css-D3h14YRZ.js +8 -0
- package/web/assets/cssMode-DMo-5YLA.js +9 -0
- package/web/assets/cypher-DrQuvNYM.js +6 -0
- package/web/assets/dart-CFKIUWau.js +6 -0
- package/web/assets/dockerfile-Zznr-cwX.js +6 -0
- package/web/assets/ecl-Ce3n6wWz.js +6 -0
- package/web/assets/elixir-deUWdS0T.js +6 -0
- package/web/assets/flow9-i9-g7ZhI.js +6 -0
- package/web/assets/freemarker2-D4qgkQzN.js +8 -0
- package/web/assets/fsharp-CzKuDChf.js +6 -0
- package/web/assets/go-Cphgjts3.js +6 -0
- package/web/assets/graphql-Cg7bfA9N.js +6 -0
- package/web/assets/handlebars-CXFvNjQC.js +6 -0
- package/web/assets/hcl-0cvrggvQ.js +6 -0
- package/web/assets/html-oyuB_D-B.js +6 -0
- package/web/assets/htmlMode-iWuZ24-r.js +9 -0
- package/web/assets/index-DqP-_E4F.css +32 -0
- package/web/assets/index-R1cylSgw.js +1665 -0
- package/web/assets/ini-Drc7WvVn.js +6 -0
- package/web/assets/java-B_fMsGYe.js +6 -0
- package/web/assets/javascript-CRIkN2Pg.js +6 -0
- package/web/assets/jsonMode-DVDkDgex.js +15 -0
- package/web/assets/julia-Bqgm2twL.js +6 -0
- package/web/assets/kotlin-BSkB5QuD.js +6 -0
- package/web/assets/less-BsTHnhdd.js +7 -0
- package/web/assets/lexon-YWi4-JPR.js +6 -0
- package/web/assets/liquid-CSfldbB5.js +6 -0
- package/web/assets/lua-nf6ki56Z.js +6 -0
- package/web/assets/m3-Cpb6xl2v.js +6 -0
- package/web/assets/markdown-DSZPf7rp.js +6 -0
- package/web/assets/mdx-Dd58iymR.js +6 -0
- package/web/assets/mips-B_c3zf-v.js +6 -0
- package/web/assets/monaco-editor-B4lwqA13.js +751 -0
- package/web/assets/monaco-editor-CQpyCxOA.css +1 -0
- package/web/assets/msdax-rUNN04Wq.js +6 -0
- package/web/assets/mysql-DDwshQtU.js +6 -0
- package/web/assets/objective-c-B5zXfXm9.js +6 -0
- package/web/assets/pascal-CXOwvkN_.js +6 -0
- package/web/assets/pascaligo-Bc-ZgV77.js +6 -0
- package/web/assets/perl-CwNk8-XU.js +6 -0
- package/web/assets/pgsql-tGk8EFnU.js +6 -0
- package/web/assets/php-CpIb_Oan.js +6 -0
- package/web/assets/pla-B03wrqEc.js +6 -0
- package/web/assets/postiats-BKlk5iyT.js +6 -0
- package/web/assets/powerquery-Bhzvs7bI.js +6 -0
- package/web/assets/powershell-Dd3NCNK9.js +6 -0
- package/web/assets/protobuf-COyEY5Pt.js +7 -0
- package/web/assets/pug-BaJupSGV.js +6 -0
- package/web/assets/python-XWrMqdhO.js +6 -0
- package/web/assets/qsharp-DXyYeYxl.js +6 -0
- package/web/assets/r-CdQndTaG.js +6 -0
- package/web/assets/razor-DPlhCpIs.js +6 -0
- package/web/assets/redis-CVwtpugi.js +6 -0
- package/web/assets/redshift-25W9uPmb.js +6 -0
- package/web/assets/restructuredtext-DfzH4Xui.js +6 -0
- package/web/assets/router-vendor-DHlGizSU.js +41 -0
- package/web/assets/ruby-Cp1zYvxS.js +6 -0
- package/web/assets/rust-D5C2fndG.js +6 -0
- package/web/assets/sb-CDntyWJ8.js +6 -0
- package/web/assets/scala-BoFRg7Ot.js +6 -0
- package/web/assets/scheme-Bio4gycK.js +6 -0
- package/web/assets/scss-4Ik7cdeQ.js +8 -0
- package/web/assets/shell-CX-rkNHf.js +6 -0
- package/web/assets/solidity-Tw7wswEv.js +6 -0
- package/web/assets/sophia-C5WLch3f.js +6 -0
- package/web/assets/sparql-DHaeiCBh.js +6 -0
- package/web/assets/sql-CCSDG5nI.js +6 -0
- package/web/assets/st-pnP8ivHi.js +6 -0
- package/web/assets/swift-DwJ7jVG9.js +8 -0
- package/web/assets/systemverilog-B9Xyijhd.js +6 -0
- package/web/assets/tcl-DnHyzjbg.js +6 -0
- package/web/assets/tsMode-BbA1Jbf3.js +16 -0
- package/web/assets/twig-CPajHgWi.js +6 -0
- package/web/assets/typescript-DcLHYzvH.js +6 -0
- package/web/assets/typespec-D-MeaMDU.js +6 -0
- package/web/assets/ui-vendor-BSco96uv.js +51 -0
- package/web/assets/utils-vendor-DaJ2Dubl.js +911 -0
- package/web/assets/vb-DgyLZaXg.js +6 -0
- package/web/assets/wgsl-DYQUnd45.js +303 -0
- package/web/assets/xml-xKYS3dO6.js +6 -0
- package/web/assets/yaml-CNmlXqzH.js +6 -0
- package/web/favicon.ico +0 -0
- package/web/index.html +22 -0
- package/web/logo.png +0 -0
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Steward Scheduler Service
|
|
3
|
+
*
|
|
4
|
+
* This service executes stewards on schedule (cron) or in response to events.
|
|
5
|
+
* It manages the lifecycle of scheduled steward executions and tracks execution history.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Cron-based scheduling using node-cron
|
|
9
|
+
* - Event-driven triggers with condition evaluation
|
|
10
|
+
* - Execution history tracking
|
|
11
|
+
* - Graceful shutdown support
|
|
12
|
+
*
|
|
13
|
+
* TB-O23: Steward Scheduler Service
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from 'node:events';
|
|
18
|
+
import { createTimestamp } from '@stoneforge/core';
|
|
19
|
+
import { isCronTrigger, isEventTrigger, } from '../types/index.js';
|
|
20
|
+
import { getAgentMetadata } from './agent-registry.js';
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Cron Schedule Utilities
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Validates a cron expression.
|
|
26
|
+
* This is a basic validation - we check the format but not all edge cases.
|
|
27
|
+
*/
|
|
28
|
+
export function isValidCronExpression(schedule) {
|
|
29
|
+
// Standard cron format: * * * * * (minute hour day month weekday)
|
|
30
|
+
// Extended format with seconds: * * * * * * (second minute hour day month weekday)
|
|
31
|
+
const parts = schedule.trim().split(/\s+/);
|
|
32
|
+
if (parts.length < 5 || parts.length > 6) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
// Basic validation: each part should be a valid cron field
|
|
36
|
+
const cronFieldPattern = /^(\*|\d+(-\d+)?(,\d+(-\d+)?)*)(\/\d+)?$/;
|
|
37
|
+
return parts.every(part => cronFieldPattern.test(part) || part === '*');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Calculates the next run time for a cron expression.
|
|
41
|
+
* This is a simplified implementation - for production use node-cron handles this.
|
|
42
|
+
*/
|
|
43
|
+
export function getNextCronRunTime(_schedule) {
|
|
44
|
+
// This is a placeholder - node-cron provides this via the scheduled task
|
|
45
|
+
// For now we return undefined; actual implementation uses node-cron internals
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Condition Evaluation
|
|
50
|
+
// ============================================================================
|
|
51
|
+
/**
|
|
52
|
+
* Validates that a condition string contains only safe expression tokens.
|
|
53
|
+
* Allows: property access, comparisons, string/number literals, booleans,
|
|
54
|
+
* logical operators, parentheses.
|
|
55
|
+
* Rejects: function calls, assignments, semicolons, template literals,
|
|
56
|
+
* dangerous globals.
|
|
57
|
+
*/
|
|
58
|
+
function isSafeCondition(condition) {
|
|
59
|
+
// Strip string literals so their contents don't trigger false positives
|
|
60
|
+
const withoutStrings = condition
|
|
61
|
+
.replace(/'[^']*'/g, '""')
|
|
62
|
+
.replace(/"[^"]*"/g, '""');
|
|
63
|
+
// Reject dangerous patterns
|
|
64
|
+
const dangerousPatterns = [
|
|
65
|
+
/[;{}]/, // statements, blocks
|
|
66
|
+
/\b(import|require|eval|Function|constructor|__proto__|prototype)\b/,
|
|
67
|
+
/\b(process|global|globalThis|window|document)\b/,
|
|
68
|
+
/\b(setTimeout|setInterval|fetch|XMLHttpRequest)\b/,
|
|
69
|
+
/=>|\.\.\.|\+\+|--/, // arrow functions, spread, increment/decrement
|
|
70
|
+
/\[.*\]/, // bracket notation (property access escape vector)
|
|
71
|
+
/`/, // template literals
|
|
72
|
+
/\$\{/, // template expressions
|
|
73
|
+
/\bthis\b/, // this reference
|
|
74
|
+
/\bnew\b/, // constructor calls
|
|
75
|
+
/\bdelete\b/, // delete operator
|
|
76
|
+
/\bvoid\b/, // void operator
|
|
77
|
+
/\btypeof\b/, // typeof operator
|
|
78
|
+
/\bin\b/, // in operator
|
|
79
|
+
/\binstanceof\b/, // instanceof operator
|
|
80
|
+
/[^=!<>]=[^=]/, // assignment (but not ==, !=, <=, >=)
|
|
81
|
+
];
|
|
82
|
+
for (const pattern of dangerousPatterns) {
|
|
83
|
+
if (pattern.test(withoutStrings)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Only allow safe characters after string removal
|
|
88
|
+
const safeTokenPattern = /^[\w\s.$?!&|><=()'",-]+$/;
|
|
89
|
+
if (!safeTokenPattern.test(withoutStrings)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
// Reject function calls: identifier followed by (
|
|
93
|
+
if (/\w\s*\(/.test(withoutStrings)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Evaluates a simple condition expression against event data.
|
|
100
|
+
* Supports basic JavaScript-like expressions with property access.
|
|
101
|
+
*
|
|
102
|
+
* Examples:
|
|
103
|
+
* - "task.status === 'closed'"
|
|
104
|
+
* - "task.assignedAgent?.role === 'worker'"
|
|
105
|
+
* - "branch.tests === 'passing'"
|
|
106
|
+
*/
|
|
107
|
+
export function evaluateCondition(condition, context) {
|
|
108
|
+
try {
|
|
109
|
+
// Validate condition contains only safe expression tokens
|
|
110
|
+
if (!isSafeCondition(condition)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const contextKeys = Object.keys(context);
|
|
114
|
+
const contextValues = Object.values(context);
|
|
115
|
+
const fn = new Function(...contextKeys, `"use strict"; try { return Boolean(${condition}); } catch { return false; }`);
|
|
116
|
+
return fn(...contextValues);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Steward Scheduler Implementation
|
|
124
|
+
// ============================================================================
|
|
125
|
+
/**
|
|
126
|
+
* Default configuration values
|
|
127
|
+
*/
|
|
128
|
+
const DEFAULT_CONFIG = {
|
|
129
|
+
maxHistoryPerSteward: 100,
|
|
130
|
+
defaultTimeoutMs: 5 * 60 * 1000, // 5 minutes
|
|
131
|
+
startImmediately: false,
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Implementation of the Steward Scheduler Service.
|
|
135
|
+
*/
|
|
136
|
+
export class StewardSchedulerImpl {
|
|
137
|
+
agentRegistry;
|
|
138
|
+
config;
|
|
139
|
+
executor;
|
|
140
|
+
emitter;
|
|
141
|
+
running = false;
|
|
142
|
+
cronJobs = new Map(); // key: stewardId-triggerIndex
|
|
143
|
+
eventSubscriptions = new Map(); // key: eventName
|
|
144
|
+
executionHistory = [];
|
|
145
|
+
runningExecutions = new Set();
|
|
146
|
+
executionCounter = 0;
|
|
147
|
+
constructor(agentRegistry, executor, config = {}) {
|
|
148
|
+
this.agentRegistry = agentRegistry;
|
|
149
|
+
this.executor = executor;
|
|
150
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
151
|
+
this.emitter = new EventEmitter();
|
|
152
|
+
}
|
|
153
|
+
// ----------------------------------------
|
|
154
|
+
// Lifecycle
|
|
155
|
+
// ----------------------------------------
|
|
156
|
+
async start() {
|
|
157
|
+
if (this.running) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.running = true;
|
|
161
|
+
// Start all registered cron jobs
|
|
162
|
+
for (const job of this.cronJobs.values()) {
|
|
163
|
+
this.startCronJob(job);
|
|
164
|
+
}
|
|
165
|
+
// Activate all event subscriptions
|
|
166
|
+
for (const subs of this.eventSubscriptions.values()) {
|
|
167
|
+
for (const sub of subs) {
|
|
168
|
+
sub.isActive = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Register all stewards if configured to do so
|
|
172
|
+
if (this.config.startImmediately) {
|
|
173
|
+
await this.registerAllStewards();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async stop() {
|
|
177
|
+
if (!this.running) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.running = false;
|
|
181
|
+
// Stop all cron jobs
|
|
182
|
+
for (const job of this.cronJobs.values()) {
|
|
183
|
+
this.stopCronJob(job);
|
|
184
|
+
}
|
|
185
|
+
// Deactivate event subscriptions
|
|
186
|
+
for (const subs of this.eventSubscriptions.values()) {
|
|
187
|
+
for (const sub of subs) {
|
|
188
|
+
sub.isActive = false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
isRunning() {
|
|
193
|
+
return this.running;
|
|
194
|
+
}
|
|
195
|
+
// ----------------------------------------
|
|
196
|
+
// Steward Management
|
|
197
|
+
// ----------------------------------------
|
|
198
|
+
async registerSteward(stewardId) {
|
|
199
|
+
const agent = await this.agentRegistry.getAgent(stewardId);
|
|
200
|
+
if (!agent) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
const metadata = getAgentMetadata(agent);
|
|
204
|
+
if (!metadata || metadata.agentRole !== 'steward') {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
const stewardMeta = metadata;
|
|
208
|
+
const triggers = stewardMeta.triggers ?? [];
|
|
209
|
+
// Remove existing registrations for this steward
|
|
210
|
+
await this.unregisterSteward(stewardId);
|
|
211
|
+
// Register each trigger
|
|
212
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
213
|
+
const trigger = triggers[i];
|
|
214
|
+
if (isCronTrigger(trigger)) {
|
|
215
|
+
const jobKey = `${stewardId}-${i}`;
|
|
216
|
+
const jobState = {
|
|
217
|
+
stewardId,
|
|
218
|
+
stewardName: agent.name,
|
|
219
|
+
trigger,
|
|
220
|
+
isRunning: false,
|
|
221
|
+
};
|
|
222
|
+
this.cronJobs.set(jobKey, jobState);
|
|
223
|
+
// Start job if scheduler is running
|
|
224
|
+
if (this.running) {
|
|
225
|
+
this.startCronJob(jobState);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (isEventTrigger(trigger)) {
|
|
229
|
+
const eventName = trigger.event;
|
|
230
|
+
if (!this.eventSubscriptions.has(eventName)) {
|
|
231
|
+
this.eventSubscriptions.set(eventName, []);
|
|
232
|
+
}
|
|
233
|
+
this.eventSubscriptions.get(eventName).push({
|
|
234
|
+
stewardId,
|
|
235
|
+
stewardName: agent.name,
|
|
236
|
+
trigger,
|
|
237
|
+
isActive: this.running,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.emitter.emit('steward:registered', stewardId);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
async unregisterSteward(stewardId) {
|
|
245
|
+
let unregistered = false;
|
|
246
|
+
// Remove cron jobs
|
|
247
|
+
for (const [key, job] of this.cronJobs.entries()) {
|
|
248
|
+
if (job.stewardId === stewardId) {
|
|
249
|
+
this.stopCronJob(job);
|
|
250
|
+
this.cronJobs.delete(key);
|
|
251
|
+
unregistered = true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Remove event subscriptions
|
|
255
|
+
for (const [eventName, subs] of this.eventSubscriptions.entries()) {
|
|
256
|
+
const remaining = subs.filter(s => s.stewardId !== stewardId);
|
|
257
|
+
if (remaining.length !== subs.length) {
|
|
258
|
+
unregistered = true;
|
|
259
|
+
}
|
|
260
|
+
if (remaining.length === 0) {
|
|
261
|
+
this.eventSubscriptions.delete(eventName);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
this.eventSubscriptions.set(eventName, remaining);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (unregistered) {
|
|
268
|
+
this.emitter.emit('steward:unregistered', stewardId);
|
|
269
|
+
}
|
|
270
|
+
return unregistered;
|
|
271
|
+
}
|
|
272
|
+
async refreshSteward(stewardId) {
|
|
273
|
+
await this.unregisterSteward(stewardId);
|
|
274
|
+
await this.registerSteward(stewardId);
|
|
275
|
+
}
|
|
276
|
+
async registerAllStewards() {
|
|
277
|
+
const stewards = await this.agentRegistry.getStewards();
|
|
278
|
+
let registered = 0;
|
|
279
|
+
for (const steward of stewards) {
|
|
280
|
+
const success = await this.registerSteward(steward.id);
|
|
281
|
+
if (success) {
|
|
282
|
+
registered++;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return registered;
|
|
286
|
+
}
|
|
287
|
+
// ----------------------------------------
|
|
288
|
+
// Manual Execution
|
|
289
|
+
// ----------------------------------------
|
|
290
|
+
async executeSteward(stewardId, context) {
|
|
291
|
+
const agent = await this.agentRegistry.getAgent(stewardId);
|
|
292
|
+
if (!agent) {
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
error: `Steward not found: ${stewardId}`,
|
|
296
|
+
durationMs: 0,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const metadata = getAgentMetadata(agent);
|
|
300
|
+
if (!metadata || metadata.agentRole !== 'steward') {
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
error: `Entity is not a steward: ${stewardId}`,
|
|
304
|
+
durationMs: 0,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// Create a manual trigger
|
|
308
|
+
const manualTrigger = {
|
|
309
|
+
type: 'event',
|
|
310
|
+
event: 'manual',
|
|
311
|
+
};
|
|
312
|
+
return this.runExecution(agent, manualTrigger, true, context);
|
|
313
|
+
}
|
|
314
|
+
// ----------------------------------------
|
|
315
|
+
// Event Publishing
|
|
316
|
+
// ----------------------------------------
|
|
317
|
+
async publishEvent(eventName, eventData) {
|
|
318
|
+
if (!this.running) {
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
const subscriptions = this.eventSubscriptions.get(eventName) ?? [];
|
|
322
|
+
let triggered = 0;
|
|
323
|
+
for (const sub of subscriptions) {
|
|
324
|
+
if (!sub.isActive) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
// Check condition if present
|
|
328
|
+
if (sub.trigger.condition) {
|
|
329
|
+
const matches = evaluateCondition(sub.trigger.condition, eventData);
|
|
330
|
+
if (!matches) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Execute the steward
|
|
335
|
+
const agent = await this.agentRegistry.getAgent(sub.stewardId);
|
|
336
|
+
if (agent) {
|
|
337
|
+
// Run execution asynchronously
|
|
338
|
+
this.runExecution(agent, sub.trigger, false, eventData).catch(() => {
|
|
339
|
+
// Errors are handled in runExecution
|
|
340
|
+
});
|
|
341
|
+
triggered++;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return triggered;
|
|
345
|
+
}
|
|
346
|
+
// ----------------------------------------
|
|
347
|
+
// Status & Queries
|
|
348
|
+
// ----------------------------------------
|
|
349
|
+
getScheduledJobs(stewardId) {
|
|
350
|
+
const jobs = [];
|
|
351
|
+
for (const job of this.cronJobs.values()) {
|
|
352
|
+
if (stewardId && job.stewardId !== stewardId) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
jobs.push({
|
|
356
|
+
stewardId: job.stewardId,
|
|
357
|
+
stewardName: job.stewardName,
|
|
358
|
+
trigger: job.trigger,
|
|
359
|
+
isRunning: job.isRunning,
|
|
360
|
+
nextRunAt: job.nextRunAt,
|
|
361
|
+
lastRunAt: job.lastRunAt,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return jobs;
|
|
365
|
+
}
|
|
366
|
+
getEventSubscriptions(stewardId) {
|
|
367
|
+
const subscriptions = [];
|
|
368
|
+
for (const subs of this.eventSubscriptions.values()) {
|
|
369
|
+
for (const sub of subs) {
|
|
370
|
+
if (stewardId && sub.stewardId !== stewardId) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
subscriptions.push({
|
|
374
|
+
stewardId: sub.stewardId,
|
|
375
|
+
stewardName: sub.stewardName,
|
|
376
|
+
trigger: sub.trigger,
|
|
377
|
+
isActive: sub.isActive,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return subscriptions;
|
|
382
|
+
}
|
|
383
|
+
getExecutionHistory(filter) {
|
|
384
|
+
let entries = [...this.executionHistory];
|
|
385
|
+
if (filter) {
|
|
386
|
+
if (filter.stewardId) {
|
|
387
|
+
entries = entries.filter(e => e.stewardId === filter.stewardId);
|
|
388
|
+
}
|
|
389
|
+
if (filter.triggerType) {
|
|
390
|
+
entries = entries.filter(e => e.trigger.type === filter.triggerType);
|
|
391
|
+
}
|
|
392
|
+
if (filter.success !== undefined) {
|
|
393
|
+
entries = entries.filter(e => e.result?.success === filter.success);
|
|
394
|
+
}
|
|
395
|
+
if (filter.startedAfter) {
|
|
396
|
+
entries = entries.filter(e => e.startedAt >= filter.startedAfter);
|
|
397
|
+
}
|
|
398
|
+
if (filter.startedBefore) {
|
|
399
|
+
entries = entries.filter(e => e.startedAt <= filter.startedBefore);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Sort by startedAt descending
|
|
403
|
+
entries.sort((a, b) => (b.startedAt > a.startedAt ? 1 : -1));
|
|
404
|
+
if (filter?.limit) {
|
|
405
|
+
entries = entries.slice(0, filter.limit);
|
|
406
|
+
}
|
|
407
|
+
return entries;
|
|
408
|
+
}
|
|
409
|
+
getLastExecution(stewardId) {
|
|
410
|
+
const entries = this.getExecutionHistory({ stewardId, limit: 1 });
|
|
411
|
+
return entries[0];
|
|
412
|
+
}
|
|
413
|
+
getStats() {
|
|
414
|
+
const registeredStewards = new Set();
|
|
415
|
+
for (const job of this.cronJobs.values()) {
|
|
416
|
+
registeredStewards.add(job.stewardId);
|
|
417
|
+
}
|
|
418
|
+
for (const subs of this.eventSubscriptions.values()) {
|
|
419
|
+
for (const sub of subs) {
|
|
420
|
+
registeredStewards.add(sub.stewardId);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const successfulExecutions = this.executionHistory.filter(e => e.result?.success === true).length;
|
|
424
|
+
const failedExecutions = this.executionHistory.filter(e => e.result?.success === false).length;
|
|
425
|
+
return {
|
|
426
|
+
registeredStewards: registeredStewards.size,
|
|
427
|
+
activeCronJobs: this.cronJobs.size,
|
|
428
|
+
activeEventSubscriptions: [...this.eventSubscriptions.values()].flat().length,
|
|
429
|
+
totalExecutions: this.executionHistory.length,
|
|
430
|
+
successfulExecutions,
|
|
431
|
+
failedExecutions,
|
|
432
|
+
runningExecutions: this.runningExecutions.size,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
on(event, listener) {
|
|
436
|
+
this.emitter.on(event, listener);
|
|
437
|
+
}
|
|
438
|
+
off(event, listener) {
|
|
439
|
+
this.emitter.off(event, listener);
|
|
440
|
+
}
|
|
441
|
+
// ----------------------------------------
|
|
442
|
+
// Private Helpers
|
|
443
|
+
// ----------------------------------------
|
|
444
|
+
startCronJob(job) {
|
|
445
|
+
if (job.intervalId) {
|
|
446
|
+
return; // Already running
|
|
447
|
+
}
|
|
448
|
+
this.scheduleNextRun(job);
|
|
449
|
+
}
|
|
450
|
+
scheduleNextRun(job) {
|
|
451
|
+
const nextTime = this.getNextCronTime(job.trigger.schedule);
|
|
452
|
+
if (!nextTime)
|
|
453
|
+
return;
|
|
454
|
+
job.nextRunAt = nextTime;
|
|
455
|
+
const delayMs = Math.max(0, nextTime.getTime() - Date.now());
|
|
456
|
+
job.intervalId = setTimeout(async () => {
|
|
457
|
+
if (!this.running)
|
|
458
|
+
return;
|
|
459
|
+
if (job.isRunning) {
|
|
460
|
+
// Skip this run but schedule the next one
|
|
461
|
+
job.intervalId = undefined;
|
|
462
|
+
this.scheduleNextRun(job);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const agent = await this.agentRegistry.getAgent(job.stewardId);
|
|
466
|
+
if (agent && this.running) {
|
|
467
|
+
await this.runExecution(agent, job.trigger, false);
|
|
468
|
+
job.lastRunAt = createTimestamp();
|
|
469
|
+
}
|
|
470
|
+
if (this.running) {
|
|
471
|
+
job.intervalId = undefined;
|
|
472
|
+
this.scheduleNextRun(job);
|
|
473
|
+
}
|
|
474
|
+
}, delayMs);
|
|
475
|
+
}
|
|
476
|
+
stopCronJob(job) {
|
|
477
|
+
if (job.intervalId) {
|
|
478
|
+
clearTimeout(job.intervalId);
|
|
479
|
+
job.intervalId = undefined;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Calculates the next fire time for a cron expression after the given date.
|
|
484
|
+
* Supports 5-field cron (minute hour dom month dow) with optional 6th seconds field (ignored).
|
|
485
|
+
* Supports: *, specific values, step (* /N), range (N-M), and lists (N,M).
|
|
486
|
+
* Returns null for invalid expressions.
|
|
487
|
+
*/
|
|
488
|
+
getNextCronTime(schedule, after) {
|
|
489
|
+
const parts = schedule.trim().split(/\s+/);
|
|
490
|
+
if (parts.length < 5 || parts.length > 6) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
// If 6 fields, ignore the first (seconds) field
|
|
494
|
+
const fields = parts.length === 6 ? parts.slice(1) : parts;
|
|
495
|
+
const [minuteField, hourField, domField, monthField, dowField] = fields;
|
|
496
|
+
// Parse each field into a set of allowed values
|
|
497
|
+
const minutes = this.parseCronField(minuteField, 0, 59);
|
|
498
|
+
const hours = this.parseCronField(hourField, 0, 23);
|
|
499
|
+
const doms = this.parseCronField(domField, 1, 31);
|
|
500
|
+
const months = this.parseCronField(monthField, 1, 12);
|
|
501
|
+
const dows = this.parseCronField(dowField, 0, 6); // 0=Sunday
|
|
502
|
+
if (!minutes || !hours || !doms || !months || !dows) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
// Start from the next minute after 'after'
|
|
506
|
+
const start = after ? new Date(after) : new Date();
|
|
507
|
+
start.setSeconds(0, 0);
|
|
508
|
+
start.setMinutes(start.getMinutes() + 1);
|
|
509
|
+
// Iterate forward up to 366 days to find the next matching time
|
|
510
|
+
const maxIterations = 366 * 24 * 60; // worst case: check every minute for a year
|
|
511
|
+
const candidate = new Date(start);
|
|
512
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
513
|
+
const month = candidate.getMonth() + 1; // 1-12
|
|
514
|
+
const dom = candidate.getDate();
|
|
515
|
+
const dow = candidate.getDay(); // 0=Sunday
|
|
516
|
+
const hour = candidate.getHours();
|
|
517
|
+
const minute = candidate.getMinutes();
|
|
518
|
+
if (months.has(month) &&
|
|
519
|
+
doms.has(dom) &&
|
|
520
|
+
dows.has(dow) &&
|
|
521
|
+
hours.has(hour) &&
|
|
522
|
+
minutes.has(minute)) {
|
|
523
|
+
return candidate;
|
|
524
|
+
}
|
|
525
|
+
// Advance by 1 minute
|
|
526
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
527
|
+
}
|
|
528
|
+
return null; // No match found within 366 days
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Parses a single cron field into a set of valid values.
|
|
532
|
+
* Supports: *, N, N-M, N,M, * /N, N-M/S
|
|
533
|
+
*/
|
|
534
|
+
parseCronField(field, min, max) {
|
|
535
|
+
const result = new Set();
|
|
536
|
+
// Handle comma-separated list
|
|
537
|
+
const segments = field.split(',');
|
|
538
|
+
for (const segment of segments) {
|
|
539
|
+
// Handle step: */N or N-M/S
|
|
540
|
+
const stepMatch = segment.match(/^(.+)\/(\d+)$/);
|
|
541
|
+
if (stepMatch) {
|
|
542
|
+
const step = parseInt(stepMatch[2], 10);
|
|
543
|
+
if (step <= 0)
|
|
544
|
+
return null;
|
|
545
|
+
let rangeStart = min;
|
|
546
|
+
let rangeEnd = max;
|
|
547
|
+
if (stepMatch[1] !== '*') {
|
|
548
|
+
const rangeMatch = stepMatch[1].match(/^(\d+)-(\d+)$/);
|
|
549
|
+
if (rangeMatch) {
|
|
550
|
+
rangeStart = parseInt(rangeMatch[1], 10);
|
|
551
|
+
rangeEnd = parseInt(rangeMatch[2], 10);
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
rangeStart = parseInt(stepMatch[1], 10);
|
|
555
|
+
rangeEnd = max;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
for (let v = rangeStart; v <= rangeEnd; v += step) {
|
|
559
|
+
if (v >= min && v <= max)
|
|
560
|
+
result.add(v);
|
|
561
|
+
}
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
// Handle wildcard
|
|
565
|
+
if (segment === '*') {
|
|
566
|
+
for (let v = min; v <= max; v++)
|
|
567
|
+
result.add(v);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
// Handle range: N-M
|
|
571
|
+
const rangeMatch = segment.match(/^(\d+)-(\d+)$/);
|
|
572
|
+
if (rangeMatch) {
|
|
573
|
+
const start = parseInt(rangeMatch[1], 10);
|
|
574
|
+
const end = parseInt(rangeMatch[2], 10);
|
|
575
|
+
for (let v = start; v <= end; v++) {
|
|
576
|
+
if (v >= min && v <= max)
|
|
577
|
+
result.add(v);
|
|
578
|
+
}
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
// Handle specific value
|
|
582
|
+
const value = parseInt(segment, 10);
|
|
583
|
+
if (isNaN(value) || value < min || value > max)
|
|
584
|
+
return null;
|
|
585
|
+
result.add(value);
|
|
586
|
+
}
|
|
587
|
+
return result.size > 0 ? result : null;
|
|
588
|
+
}
|
|
589
|
+
async runExecution(agent, trigger, manual, eventContext) {
|
|
590
|
+
const executionId = `exec-${++this.executionCounter}-${Date.now()}`;
|
|
591
|
+
const stewardId = agent.id;
|
|
592
|
+
const startedAt = createTimestamp();
|
|
593
|
+
const entry = {
|
|
594
|
+
executionId,
|
|
595
|
+
stewardId,
|
|
596
|
+
stewardName: agent.name,
|
|
597
|
+
trigger,
|
|
598
|
+
manual,
|
|
599
|
+
startedAt,
|
|
600
|
+
eventContext,
|
|
601
|
+
};
|
|
602
|
+
this.addHistoryEntry(entry);
|
|
603
|
+
this.runningExecutions.add(executionId);
|
|
604
|
+
this.emitter.emit('execution:started', entry);
|
|
605
|
+
// Update job state if this is a cron job
|
|
606
|
+
for (const job of this.cronJobs.values()) {
|
|
607
|
+
if (job.stewardId === stewardId && isCronTrigger(trigger)) {
|
|
608
|
+
job.isRunning = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const startTime = Date.now();
|
|
612
|
+
try {
|
|
613
|
+
// Execute the steward
|
|
614
|
+
const result = await this.executor(agent, { trigger, eventContext });
|
|
615
|
+
// Complete the entry
|
|
616
|
+
const completedEntry = {
|
|
617
|
+
...entry,
|
|
618
|
+
completedAt: createTimestamp(),
|
|
619
|
+
result,
|
|
620
|
+
};
|
|
621
|
+
this.updateHistoryEntry(executionId, completedEntry);
|
|
622
|
+
this.runningExecutions.delete(executionId);
|
|
623
|
+
if (result.success) {
|
|
624
|
+
this.emitter.emit('execution:completed', completedEntry);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
this.emitter.emit('execution:failed', completedEntry);
|
|
628
|
+
}
|
|
629
|
+
// Update job state
|
|
630
|
+
for (const job of this.cronJobs.values()) {
|
|
631
|
+
if (job.stewardId === stewardId) {
|
|
632
|
+
job.isRunning = false;
|
|
633
|
+
job.lastRunAt = completedEntry.completedAt;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
// Update steward metadata with last execution time
|
|
637
|
+
await this.agentRegistry.updateAgentMetadata(stewardId, {
|
|
638
|
+
lastExecutedAt: completedEntry.completedAt,
|
|
639
|
+
});
|
|
640
|
+
return result;
|
|
641
|
+
}
|
|
642
|
+
catch (error) {
|
|
643
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
644
|
+
const result = {
|
|
645
|
+
success: false,
|
|
646
|
+
error: errorMessage,
|
|
647
|
+
durationMs: Date.now() - startTime,
|
|
648
|
+
};
|
|
649
|
+
const failedEntry = {
|
|
650
|
+
...entry,
|
|
651
|
+
completedAt: createTimestamp(),
|
|
652
|
+
result,
|
|
653
|
+
};
|
|
654
|
+
this.updateHistoryEntry(executionId, failedEntry);
|
|
655
|
+
this.runningExecutions.delete(executionId);
|
|
656
|
+
this.emitter.emit('execution:failed', failedEntry);
|
|
657
|
+
// Update job state
|
|
658
|
+
for (const job of this.cronJobs.values()) {
|
|
659
|
+
if (job.stewardId === stewardId) {
|
|
660
|
+
job.isRunning = false;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return result;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
addHistoryEntry(entry) {
|
|
667
|
+
this.executionHistory.push(entry);
|
|
668
|
+
this.pruneHistory();
|
|
669
|
+
}
|
|
670
|
+
updateHistoryEntry(executionId, entry) {
|
|
671
|
+
const index = this.executionHistory.findIndex(e => e.executionId === executionId);
|
|
672
|
+
if (index !== -1) {
|
|
673
|
+
this.executionHistory[index] = entry;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
pruneHistory() {
|
|
677
|
+
// Group by steward and keep only maxHistoryPerSteward entries per steward
|
|
678
|
+
const bysteward = new Map();
|
|
679
|
+
for (const entry of this.executionHistory) {
|
|
680
|
+
if (!bysteward.has(entry.stewardId)) {
|
|
681
|
+
bysteward.set(entry.stewardId, []);
|
|
682
|
+
}
|
|
683
|
+
bysteward.get(entry.stewardId).push(entry);
|
|
684
|
+
}
|
|
685
|
+
const prunedHistory = [];
|
|
686
|
+
for (const entries of bysteward.values()) {
|
|
687
|
+
// Sort by startedAt descending and keep only the first maxHistoryPerSteward
|
|
688
|
+
entries.sort((a, b) => (b.startedAt > a.startedAt ? 1 : -1));
|
|
689
|
+
prunedHistory.push(...entries.slice(0, this.config.maxHistoryPerSteward));
|
|
690
|
+
}
|
|
691
|
+
this.executionHistory = prunedHistory;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// ============================================================================
|
|
695
|
+
// Factory Function
|
|
696
|
+
// ============================================================================
|
|
697
|
+
/**
|
|
698
|
+
* Creates a StewardScheduler instance
|
|
699
|
+
*
|
|
700
|
+
* @param agentRegistry - The agent registry for looking up stewards
|
|
701
|
+
* @param executor - Function to execute steward logic
|
|
702
|
+
* @param config - Optional configuration
|
|
703
|
+
*/
|
|
704
|
+
export function createStewardScheduler(agentRegistry, executor, config) {
|
|
705
|
+
return new StewardSchedulerImpl(agentRegistry, executor, config);
|
|
706
|
+
}
|
|
707
|
+
// ============================================================================
|
|
708
|
+
// Default Executor (placeholder)
|
|
709
|
+
// ============================================================================
|
|
710
|
+
/**
|
|
711
|
+
* Creates a default steward executor that delegates to the appropriate
|
|
712
|
+
* service based on the steward's focus.
|
|
713
|
+
*
|
|
714
|
+
* This is a placeholder - the actual executor would integrate with:
|
|
715
|
+
* - MergeStewardService for 'merge' focus
|
|
716
|
+
* - HealthStewardService for 'health' focus (TB-O24)
|
|
717
|
+
* - etc.
|
|
718
|
+
*/
|
|
719
|
+
export function createDefaultStewardExecutor() {
|
|
720
|
+
return async (steward, _context) => {
|
|
721
|
+
const metadata = getAgentMetadata(steward);
|
|
722
|
+
const focus = metadata?.stewardFocus;
|
|
723
|
+
// Placeholder - returns success with info about what would be done
|
|
724
|
+
return {
|
|
725
|
+
success: true,
|
|
726
|
+
output: `Steward ${steward.name} (${focus}) executed successfully`,
|
|
727
|
+
durationMs: 0,
|
|
728
|
+
itemsProcessed: 0,
|
|
729
|
+
};
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
//# sourceMappingURL=steward-scheduler.js.map
|