adispatchd 0.0.0-stage → 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 +21 -0
- package/README.md +195 -2
- package/dist/api/errors.d.ts +27 -0
- package/dist/api/errors.js +80 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +11 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/logs.d.ts +30 -0
- package/dist/api/logs.js +88 -0
- package/dist/api/logs.js.map +1 -0
- package/dist/api/serialize.d.ts +65 -0
- package/dist/api/serialize.js +135 -0
- package/dist/api/serialize.js.map +1 -0
- package/dist/api/server.d.ts +89 -0
- package/dist/api/server.js +751 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/sse.d.ts +90 -0
- package/dist/api/sse.js +225 -0
- package/dist/api/sse.js.map +1 -0
- package/dist/app.d.ts +173 -0
- package/dist/app.js +488 -0
- package/dist/app.js.map +1 -0
- package/dist/cli/config-validate.d.ts +6 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/config-validate.js.map +1 -0
- package/dist/cli/control.d.ts +31 -0
- package/dist/cli/control.js +186 -0
- package/dist/cli/control.js.map +1 -0
- package/dist/cli/http-client.d.ts +57 -0
- package/dist/cli/http-client.js +174 -0
- package/dist/cli/http-client.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-templates.d.ts +20 -0
- package/dist/cli/init-templates.js +275 -0
- package/dist/cli/init-templates.js.map +1 -0
- package/dist/cli/init.d.ts +16 -0
- package/dist/cli/init.js +107 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +25 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +102 -0
- package/dist/cli/program.js +133 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/service.d.ts +27 -0
- package/dist/cli/service.js +252 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/cli/start.d.ts +22 -0
- package/dist/cli/start.js +133 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/workflow-dry-run.d.ts +13 -0
- package/dist/cli/workflow-dry-run.js +144 -0
- package/dist/cli/workflow-dry-run.js.map +1 -0
- package/dist/config/field-catalog.d.ts +33 -0
- package/dist/config/field-catalog.js +172 -0
- package/dist/config/field-catalog.js.map +1 -0
- package/dist/config/filter-schema.d.ts +15 -0
- package/dist/config/filter-schema.js +110 -0
- package/dist/config/filter-schema.js.map +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/issues.d.ts +27 -0
- package/dist/config/issues.js +37 -0
- package/dist/config/issues.js.map +1 -0
- package/dist/config/loader.d.ts +32 -0
- package/dist/config/loader.js +250 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +153 -0
- package/dist/config/manager.js +276 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/model.d.ts +334 -0
- package/dist/config/model.js +2 -0
- package/dist/config/model.js.map +1 -0
- package/dist/config/normalize.d.ts +20 -0
- package/dist/config/normalize.js +200 -0
- package/dist/config/normalize.js.map +1 -0
- package/dist/config/parameters.d.ts +37 -0
- package/dist/config/parameters.js +71 -0
- package/dist/config/parameters.js.map +1 -0
- package/dist/config/public.d.ts +6 -0
- package/dist/config/public.js +18 -0
- package/dist/config/public.js.map +1 -0
- package/dist/config/schemas.d.ts +538 -0
- package/dist/config/schemas.js +321 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/config/snapshot.d.ts +38 -0
- package/dist/config/snapshot.js +57 -0
- package/dist/config/snapshot.js.map +1 -0
- package/dist/config/validate.d.ts +33 -0
- package/dist/config/validate.js +346 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/core/clock.d.ts +59 -0
- package/dist/core/clock.js +133 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/duration.d.ts +19 -0
- package/dist/core/duration.js +93 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/errors.d.ts +62 -0
- package/dist/core/errors.js +72 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/ids.d.ts +57 -0
- package/dist/core/ids.js +88 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +100 -0
- package/dist/core/logger.js +207 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/paths.d.ts +84 -0
- package/dist/core/paths.js +138 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/template.d.ts +136 -0
- package/dist/core/template.js +261 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/types.d.ts +544 -0
- package/dist/core/types.js +172 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/worker-protocol.d.ts +102 -0
- package/dist/core/worker-protocol.js +75 -0
- package/dist/core/worker-protocol.js.map +1 -0
- package/dist/executors/classify.d.ts +25 -0
- package/dist/executors/classify.js +54 -0
- package/dist/executors/classify.js.map +1 -0
- package/dist/executors/dispatcher.d.ts +35 -0
- package/dist/executors/dispatcher.js +68 -0
- package/dist/executors/dispatcher.js.map +1 -0
- package/dist/executors/env.d.ts +43 -0
- package/dist/executors/env.js +66 -0
- package/dist/executors/env.js.map +1 -0
- package/dist/executors/event-parser.d.ts +41 -0
- package/dist/executors/event-parser.js +106 -0
- package/dist/executors/event-parser.js.map +1 -0
- package/dist/executors/index.d.ts +12 -0
- package/dist/executors/index.js +13 -0
- package/dist/executors/index.js.map +1 -0
- package/dist/executors/log-capture.d.ts +57 -0
- package/dist/executors/log-capture.js +155 -0
- package/dist/executors/log-capture.js.map +1 -0
- package/dist/executors/process-identity.d.ts +124 -0
- package/dist/executors/process-identity.js +283 -0
- package/dist/executors/process-identity.js.map +1 -0
- package/dist/executors/shell-executor.d.ts +35 -0
- package/dist/executors/shell-executor.js +478 -0
- package/dist/executors/shell-executor.js.map +1 -0
- package/dist/executors/stream-redactor.d.ts +40 -0
- package/dist/executors/stream-redactor.js +89 -0
- package/dist/executors/stream-redactor.js.map +1 -0
- package/dist/executors/workspace.d.ts +10 -0
- package/dist/executors/workspace.js +47 -0
- package/dist/executors/workspace.js.map +1 -0
- package/dist/filters/evaluate.d.ts +77 -0
- package/dist/filters/evaluate.js +187 -0
- package/dist/filters/evaluate.js.map +1 -0
- package/dist/filters/fields.d.ts +35 -0
- package/dist/filters/fields.js +75 -0
- package/dist/filters/fields.js.map +1 -0
- package/dist/filters/index.d.ts +11 -0
- package/dist/filters/index.js +12 -0
- package/dist/filters/index.js.map +1 -0
- package/dist/filters/iso-date.d.ts +10 -0
- package/dist/filters/iso-date.js +42 -0
- package/dist/filters/iso-date.js.map +1 -0
- package/dist/filters/predicate.d.ts +24 -0
- package/dist/filters/predicate.js +178 -0
- package/dist/filters/predicate.js.map +1 -0
- package/dist/filters/priority.d.ts +51 -0
- package/dist/filters/priority.js +91 -0
- package/dist/filters/priority.js.map +1 -0
- package/dist/filters/pushdown.d.ts +12 -0
- package/dist/filters/pushdown.js +52 -0
- package/dist/filters/pushdown.js.map +1 -0
- package/dist/filters/shell-check.d.ts +110 -0
- package/dist/filters/shell-check.js +304 -0
- package/dist/filters/shell-check.js.map +1 -0
- package/dist/identities/github-app.d.ts +55 -0
- package/dist/identities/github-app.js +160 -0
- package/dist/identities/github-app.js.map +1 -0
- package/dist/identities/index.d.ts +6 -0
- package/dist/identities/index.js +7 -0
- package/dist/identities/index.js.map +1 -0
- package/dist/identities/jwt.d.ts +24 -0
- package/dist/identities/jwt.js +56 -0
- package/dist/identities/jwt.js.map +1 -0
- package/dist/lifecycle/context.d.ts +106 -0
- package/dist/lifecycle/context.js +170 -0
- package/dist/lifecycle/context.js.map +1 -0
- package/dist/lifecycle/engine.d.ts +185 -0
- package/dist/lifecycle/engine.js +551 -0
- package/dist/lifecycle/engine.js.map +1 -0
- package/dist/lifecycle/errors.d.ts +48 -0
- package/dist/lifecycle/errors.js +71 -0
- package/dist/lifecycle/errors.js.map +1 -0
- package/dist/lifecycle/index.d.ts +9 -0
- package/dist/lifecycle/index.js +9 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/performers.d.ts +66 -0
- package/dist/lifecycle/performers.js +179 -0
- package/dist/lifecycle/performers.js.map +1 -0
- package/dist/notifications/index.d.ts +6 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +115 -0
- package/dist/notifications/service.js +364 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/persistence/actions.d.ts +101 -0
- package/dist/persistence/actions.js +196 -0
- package/dist/persistence/actions.js.map +1 -0
- package/dist/persistence/blocks.d.ts +85 -0
- package/dist/persistence/blocks.js +140 -0
- package/dist/persistence/blocks.js.map +1 -0
- package/dist/persistence/cron-firings.d.ts +55 -0
- package/dist/persistence/cron-firings.js +70 -0
- package/dist/persistence/cron-firings.js.map +1 -0
- package/dist/persistence/database.d.ts +60 -0
- package/dist/persistence/database.js +124 -0
- package/dist/persistence/database.js.map +1 -0
- package/dist/persistence/errors.d.ts +39 -0
- package/dist/persistence/errors.js +67 -0
- package/dist/persistence/errors.js.map +1 -0
- package/dist/persistence/events.d.ts +72 -0
- package/dist/persistence/events.js +83 -0
- package/dist/persistence/events.js.map +1 -0
- package/dist/persistence/index.d.ts +18 -0
- package/dist/persistence/index.js +19 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/instance-lock.d.ts +26 -0
- package/dist/persistence/instance-lock.js +117 -0
- package/dist/persistence/instance-lock.js.map +1 -0
- package/dist/persistence/lock-keys.d.ts +16 -0
- package/dist/persistence/lock-keys.js +41 -0
- package/dist/persistence/lock-keys.js.map +1 -0
- package/dist/persistence/notifications.d.ts +113 -0
- package/dist/persistence/notifications.js +213 -0
- package/dist/persistence/notifications.js.map +1 -0
- package/dist/persistence/persistence.d.ts +46 -0
- package/dist/persistence/persistence.js +42 -0
- package/dist/persistence/persistence.js.map +1 -0
- package/dist/persistence/retention.d.ts +46 -0
- package/dist/persistence/retention.js +68 -0
- package/dist/persistence/retention.js.map +1 -0
- package/dist/persistence/runs.d.ts +215 -0
- package/dist/persistence/runs.js +417 -0
- package/dist/persistence/runs.js.map +1 -0
- package/dist/persistence/schema.d.ts +16 -0
- package/dist/persistence/schema.js +183 -0
- package/dist/persistence/schema.js.map +1 -0
- package/dist/persistence/sqlite.d.ts +49 -0
- package/dist/persistence/sqlite.js +131 -0
- package/dist/persistence/sqlite.js.map +1 -0
- package/dist/runs/controller.d.ts +307 -0
- package/dist/runs/controller.js +1078 -0
- package/dist/runs/controller.js.map +1 -0
- package/dist/runs/errors.d.ts +20 -0
- package/dist/runs/errors.js +26 -0
- package/dist/runs/errors.js.map +1 -0
- package/dist/runs/event-bus.d.ts +106 -0
- package/dist/runs/event-bus.js +105 -0
- package/dist/runs/event-bus.js.map +1 -0
- package/dist/runs/execution.d.ts +39 -0
- package/dist/runs/execution.js +146 -0
- package/dist/runs/execution.js.map +1 -0
- package/dist/runs/index.d.ts +12 -0
- package/dist/runs/index.js +13 -0
- package/dist/runs/index.js.map +1 -0
- package/dist/runs/retention.d.ts +64 -0
- package/dist/runs/retention.js +232 -0
- package/dist/runs/retention.js.map +1 -0
- package/dist/runs/workspace.d.ts +20 -0
- package/dist/runs/workspace.js +51 -0
- package/dist/runs/workspace.js.map +1 -0
- package/dist/scheduler/checks.d.ts +41 -0
- package/dist/scheduler/checks.js +111 -0
- package/dist/scheduler/checks.js.map +1 -0
- package/dist/scheduler/cron.d.ts +32 -0
- package/dist/scheduler/cron.js +83 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/dry-run.d.ts +42 -0
- package/dist/scheduler/dry-run.js +167 -0
- package/dist/scheduler/dry-run.js.map +1 -0
- package/dist/scheduler/errors.d.ts +22 -0
- package/dist/scheduler/errors.js +22 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/format.d.ts +3 -0
- package/dist/scheduler/format.js +73 -0
- package/dist/scheduler/format.js.map +1 -0
- package/dist/scheduler/index.d.ts +13 -0
- package/dist/scheduler/index.js +14 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/planner.d.ts +88 -0
- package/dist/scheduler/planner.js +255 -0
- package/dist/scheduler/planner.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +160 -0
- package/dist/scheduler/scheduler.js +680 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/state.d.ts +26 -0
- package/dist/scheduler/state.js +25 -0
- package/dist/scheduler/state.js.map +1 -0
- package/dist/scheduler/types.d.ts +197 -0
- package/dist/scheduler/types.js +3 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/service/index.d.ts +6 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/install.d.ts +93 -0
- package/dist/service/install.js +250 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/unit.d.ts +50 -0
- package/dist/service/unit.js +171 -0
- package/dist/service/unit.js.map +1 -0
- package/dist/sources/github/adapter.d.ts +99 -0
- package/dist/sources/github/adapter.js +336 -0
- package/dist/sources/github/adapter.js.map +1 -0
- package/dist/sources/github/fields.d.ts +8 -0
- package/dist/sources/github/fields.js +72 -0
- package/dist/sources/github/fields.js.map +1 -0
- package/dist/sources/github/http.d.ts +100 -0
- package/dist/sources/github/http.js +288 -0
- package/dist/sources/github/http.js.map +1 -0
- package/dist/sources/github/index.d.ts +6 -0
- package/dist/sources/github/index.js +7 -0
- package/dist/sources/github/index.js.map +1 -0
- package/dist/sources/github/pushdown.d.ts +14 -0
- package/dist/sources/github/pushdown.js +47 -0
- package/dist/sources/github/pushdown.js.map +1 -0
- package/dist/sources/github/refs.d.ts +17 -0
- package/dist/sources/github/refs.js +61 -0
- package/dist/sources/github/refs.js.map +1 -0
- package/dist/sources/index.d.ts +6 -0
- package/dist/sources/index.js +7 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +28 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/public/app.css +239 -0
- package/dist/ui/public/favicon.svg +1 -0
- package/dist/ui/public/index.html +27 -0
- package/dist/ui/public/js/client/api.js +68 -0
- package/dist/ui/public/js/client/app.js +146 -0
- package/dist/ui/public/js/client/dom.js +34 -0
- package/dist/ui/public/js/client/page.js +9 -0
- package/dist/ui/public/js/client/pages/actions.js +35 -0
- package/dist/ui/public/js/client/pages/blocked.js +58 -0
- package/dist/ui/public/js/client/pages/config.js +126 -0
- package/dist/ui/public/js/client/pages/dashboard.js +175 -0
- package/dist/ui/public/js/client/pages/run.js +367 -0
- package/dist/ui/public/js/client/pages/runs.js +72 -0
- package/dist/ui/public/js/client/pages/views.js +80 -0
- package/dist/ui/public/js/client/pages/workflows.js +291 -0
- package/dist/ui/public/js/client/types.js +1 -0
- package/dist/ui/public/js/client/ui.js +152 -0
- package/dist/ui/public/js/shared/dom.js +77 -0
- package/dist/ui/public/js/shared/events.js +88 -0
- package/dist/ui/public/js/shared/format.js +213 -0
- package/dist/ui/public/js/shared/logs.js +136 -0
- package/dist/ui/public/js/shared/params.js +90 -0
- package/dist/ui/public/js/shared/route.js +36 -0
- package/dist/ui/public/js/shared/sort.js +70 -0
- package/dist/ui/public/js/shared/sse.js +193 -0
- package/dist/ui/public/js/shared/throttle.js +65 -0
- package/dist/views/index.d.ts +6 -0
- package/dist/views/index.js +7 -0
- package/dist/views/index.js.map +1 -0
- package/dist/views/service.d.ts +131 -0
- package/dist/views/service.js +264 -0
- package/dist/views/service.js.map +1 -0
- package/docs/api.md +239 -0
- package/docs/configuration-reference.md +1785 -0
- package/docs/cron-semantics.md +96 -0
- package/docs/manual-tests/github-fixture/config.yaml +30 -0
- package/docs/manual-tests/github-fixture/helpers.sh +93 -0
- package/docs/manual-tests/github-fixture/identities.yaml +18 -0
- package/docs/manual-tests/github-fixture/scripts/test-worker.sh +100 -0
- package/docs/manual-tests/github-fixture/sources.yaml +13 -0
- package/docs/manual-tests/github-fixture/views/dependencies.yaml +17 -0
- package/docs/manual-tests/github-fixture/views/needs-maintainer.yaml +17 -0
- package/docs/manual-tests/github-fixture/workflows/coder-test.yaml +76 -0
- package/docs/manual-tests/github-fixture/workflows/commenter-test.yaml +63 -0
- package/docs/manual-tests/github-fixture/workflows/reviewer-test.yaml +60 -0
- package/docs/manual-tests/github.md +1308 -0
- package/docs/manual-tests/process-supervision.md +64 -0
- package/docs/manual-tests/systemd.md +68 -0
- package/docs/operations.md +271 -0
- package/docs/worker-protocol.md +145 -0
- package/examples/config/config.yaml +35 -0
- package/examples/config/identities.yaml +18 -0
- package/examples/config/sources.yaml +13 -0
- package/examples/config/views/needs-maintainer.yaml +15 -0
- package/examples/config/workflows/coder-project-a.yaml +86 -0
- package/examples/config/workflows/deploy.yaml +15 -0
- package/examples/config/workflows/nightly-maintenance.yaml +13 -0
- package/package.json +75 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 brokoskokoli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,196 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agent Dispatch Daemon (`adispatchd`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Konfigurationsgetriebener Orchestrator für Shell-Prozesse und KI-Agenten. Er findet Aufgaben in externen Quellen
|
|
4
|
+
(V1: GitHub Issues/PRs), wählt sie über Filter und Prioritäten aus, prüft Startbedingungen, führt unabhängige
|
|
5
|
+
Shell-Workflows in eigenen Workspaces aus, überwacht und protokolliert jeden Run, führt Lifecycle-Aktionen aus
|
|
6
|
+
(Labels, Kommentare, Shell-Hooks) und zeigt alles in einer kleinen Weboberfläche mit Live-Updates.
|
|
7
|
+
|
|
8
|
+
- **GitHub bleibt Source of Truth** – kein eigener Ticketstore; SQLite hält nur Runs, Locks, Events, Blocks, Aktionsjournale.
|
|
9
|
+
- **Unabhängige Workflows** mit Source-, Cron- oder manuellem Trigger, globalen und workflowbezogenen Limits.
|
|
10
|
+
- **Robust:** technische Fehler vs. fachliches `failure`, Retry-Blocks, Aktions-Retries, Cancel/Timeout per
|
|
11
|
+
Prozessgruppe, Drain, Crash-Recovery, systemd-Integration.
|
|
12
|
+
|
|
13
|
+
Dokumentation: [Betrieb](docs/operations.md) · [Worker-Protokoll](docs/worker-protocol.md) · [HTTP-API/SSE](docs/api.md) ·
|
|
14
|
+
[Cron-Semantik](docs/cron-semantics.md) · [Spezifikation](docs/spec-v0.1.md) · Manuelle Tests:
|
|
15
|
+
[systemd](docs/manual-tests/systemd.md), [Prozess-Supervision](docs/manual-tests/process-supervision.md),
|
|
16
|
+
[GitHub](docs/manual-tests/github.md)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Voraussetzungen: Linux (Produktion, systemd) oder macOS (lokal), **Node.js ≥ 22.13** (empfohlen 24 LTS; SQLite kommt aus
|
|
21
|
+
`node:sqlite`, kein natives Addon). Windows wird nicht unterstützt.
|
|
22
|
+
|
|
23
|
+
Paket `adispatchd` auf npm, Befehl `adispatch`. Für Dienste immer eine **feste Version** global installieren (kein `npx`):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g adispatchd@0.1.0 # aus der npm-Registry
|
|
27
|
+
|
|
28
|
+
# alternativ aus dem Repository-Checkout:
|
|
29
|
+
git clone https://github.com/brokoskokoli/adispatchd.git && cd adispatchd
|
|
30
|
+
npm install
|
|
31
|
+
npm pack # baut (prepack) und erzeugt adispatchd-0.1.0.tgz
|
|
32
|
+
npm install -g ./adispatchd-0.1.0.tgz # feste Version aus dem Tarball (für Dienste Pflicht)
|
|
33
|
+
# lokal ohne globale Installation: npm run build && node dist/cli/main.js <kommando> …
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quickstart (lokal)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
adispatch init --config-dir ./config --data-dir ./data # legt Beispielkonfiguration + Skripte an
|
|
40
|
+
adispatch start --config-dir ./config --data-dir ./data # Vordergrund, Ctrl-C = Drain
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- Weboberfläche/API: <http://127.0.0.1:8787/> (`/health`, `/ready`, `/api/v1/…`).
|
|
44
|
+
- Workflow **hello** (manuell, ohne GitHub) starten – in der UI unter *Workflows* oder:
|
|
45
|
+
```bash
|
|
46
|
+
curl -X POST -H 'X-Adispatch-Request: 1' -H 'Content-Type: application/json' \
|
|
47
|
+
-d '{"parameters":{"name":"Ada"}}' http://127.0.0.1:8787/api/v1/workflows/hello/start
|
|
48
|
+
```
|
|
49
|
+
Logs liegen unter `data/logs/<run-id>/`, Kontext/Ergebnis unter `data/runs/<run-id>/`.
|
|
50
|
+
- Steuerung: `adispatch status|pause|resume|drain|cancel-all --config-dir ./config`.
|
|
51
|
+
- GitHub anbinden: GitHub App je Rolle anlegen (Rechte: Issues r/w, Pull requests write, Metadata read), App-/Installation-ID in
|
|
52
|
+
`identities.yaml`, Repo in `sources.yaml`, Private Key als Umgebungsvariable (`export GITHUB_CODER_PRIVATE_KEY="$(cat key.pem)"`),
|
|
53
|
+
dann in `workflows/coder.yaml` `enabled: true` setzen und `adispatch config reload --config-dir ./config`.
|
|
54
|
+
|
|
55
|
+
`init` überschreibt nie bestehende Dateien (vorhandene werden als „kept“ gemeldet) und validiert danach die Konfiguration.
|
|
56
|
+
|
|
57
|
+
## CLI
|
|
58
|
+
|
|
59
|
+
| Kommando | Zweck |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `init --config-dir D --data-dir D [--port P]` | Verzeichnisstruktur + kommentierte Beispielkonfiguration anlegen |
|
|
62
|
+
| `start --config-dir D --data-dir D [--drain-timeout 10m] [--cancel-timeout 2m] [--host H] [--port P]` | Orchestrator im Vordergrund; SIGTERM/SIGINT = Drain, danach Cancel-All; 2. Signal = sofort Cancel-All; Exit 0 nach geordnetem Stop |
|
|
63
|
+
| `status`, `pause`, `resume` | Zustand / automatische Starts pausieren und fortsetzen |
|
|
64
|
+
| `drain [--wait] [--timeout T]` | keine neuen Runs, aktive zu Ende, dann Stop |
|
|
65
|
+
| `cancel-all [--reason R] [--wait]` | alle Runs abbrechen (`on_cancel`), dann Stop |
|
|
66
|
+
| `config validate --config-dir D` | Offline-Validierung (Syntax, Schema, Referenzen, Platzhalter) mit Datei:Zeile |
|
|
67
|
+
| `config reload` | laufende Instanz lädt die Konfiguration atomar neu (ungültig → alte bleibt aktiv) |
|
|
68
|
+
| `workflow dry-run <id> [--json] [--local]` | Kandidatenauswahl ohne Start/Actions/Shell-Prüfungen; gegen die laufende Instanz, sonst lokal |
|
|
69
|
+
| `service install\|status\|uninstall [--dry-run]` | systemd-Dienst (Linux), siehe [Betrieb](docs/operations.md#installation-als-systemd-dienst-linux) |
|
|
70
|
+
|
|
71
|
+
HTTP-Kommandos finden die Instanz über `--url` oder Host/Port aus `config.yaml` von `--config-dir` (Default
|
|
72
|
+
`http://127.0.0.1:8787`). Exit-Codes: 0 ok, 1 Fehler, 2 Aufruffehler, 3 Instanz nicht erreichbar, 4 `--wait`-Timeout.
|
|
73
|
+
|
|
74
|
+
## Konfiguration
|
|
75
|
+
|
|
76
|
+
**Verbindliche Referenz aller Dateien, Felder, Defaults, Filter, Platzhalter und des Auswertungsablaufs:
|
|
77
|
+
[docs/configuration-reference.md](docs/configuration-reference.md)** (ersetzt die Formatvorschläge der Spec; die
|
|
78
|
+
Beispiele darin werden per Test gegen den echten Loader geprüft).
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
config-dir/ data-dir/
|
|
82
|
+
config.yaml global (§5.1) adispatchd.sqlite Runs, Locks, Events, Blocks, Journale
|
|
83
|
+
sources.yaml GitHub-Repos adispatchd.lock Instanzsperre (nie anfassen)
|
|
84
|
+
identities.yaml GitHub Apps logs/<run>/stdout.log, stderr.log
|
|
85
|
+
workflows/*.yaml runs/<run>/context.json, result.json
|
|
86
|
+
views/*.yaml workspaces/<workflow>/<run>/
|
|
87
|
+
scripts/*.sh backups/ DB-Backups vor Migrationen
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Ein Workflow (vollständiges Beispiel: `workflows/coder.yaml` nach `init`, Referenz: [Konfigurationsreferenz](docs/configuration-reference.md)):
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
id: coder
|
|
94
|
+
priority: 50 # Workflow-Priorität vor Item-Priorität
|
|
95
|
+
max_concurrent: 2
|
|
96
|
+
identity: coder # GitHub-App für Labels/Kommentare
|
|
97
|
+
trigger: { type: source, source: project_a } # oder { type: schedule, cron: '0 3 * * *' } / { type: manual }
|
|
98
|
+
filter:
|
|
99
|
+
all:
|
|
100
|
+
- { field: labels, operator: contains, value: ready-for-coding }
|
|
101
|
+
- { field: dependencies.blocked_by_open, operator: equals, value: 0 }
|
|
102
|
+
conditions:
|
|
103
|
+
workflow: [ { type: shell, command: scripts/check-credits.sh, cache: 60s } ]
|
|
104
|
+
execute:
|
|
105
|
+
type: shell
|
|
106
|
+
command: scripts/coder.sh # relativ zum config-dir; ohne Shell, Argumente als Liste
|
|
107
|
+
secrets: { GH_TOKEN: { identity: coder } }
|
|
108
|
+
timeout: { max_duration: 6h, idle_timeout: 45m }
|
|
109
|
+
retry: { max_technical_failures: 3 }
|
|
110
|
+
on_start: [ { type: source.update, labels: { add: [coding], remove: [ready-for-coding] } } ]
|
|
111
|
+
on_success: [ { type: source.comment, message: 'Completed: {result.message}' } ]
|
|
112
|
+
on_failure: [ { type: source.update, labels: { add: [needs-maintainer], remove: [coding] } } ]
|
|
113
|
+
on_crash: [ { type: source.update, labels: { add: [ready-for-coding], remove: [coding] } } ]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- Hooks: `on_start`, `on_success`, `on_failure`, `on_crash`, `on_start_error`, `on_timeout`, `on_cancel`; Actions
|
|
117
|
+
`source.update`, `source.comment`, `shell`; Retry 3 × 30 s (`lifecycle.action_retry`), `continue_on_error`.
|
|
118
|
+
- Platzhalter (feste Liste): `{run.id}`, `{workflow.id}`, `{item.number}`, `{item.url}`, `{result.message}`,
|
|
119
|
+
`{parameters.<name>}`, `{workspace.base_dir}` … – unbekannte Platzhalter sind Konfigurationsfehler.
|
|
120
|
+
- Manuelle Starts (UI/API) umgehen Filter, Conditions, Limits und Retry-Blocks, nie aber den aktiven Lock pro Workflow+Item.
|
|
121
|
+
- Views (`views/*.yaml`) sind read-only Tabellen über mehrere Sources inkl. `run.last_*`-Feldern.
|
|
122
|
+
- Änderungen gelten erst nach `config reload` (oder UI „Reload“); laufende Runs nutzen ihren Snapshot. Runtime-Overrides aus
|
|
123
|
+
der UI gehen bei Neustart verloren.
|
|
124
|
+
- Secrets stehen nie in YAML: Private Keys/Webhook-URLs nur über Umgebungsvariablen (`private_key_env`, `url_env`, `headers_env`).
|
|
125
|
+
|
|
126
|
+
## Worker-Protokoll (Kurzfassung)
|
|
127
|
+
|
|
128
|
+
Details: [docs/worker-protocol.md](docs/worker-protocol.md).
|
|
129
|
+
|
|
130
|
+
- **Input:** Minimalumgebung (`PATH`, `HOME`, …) + `execute.env` + deklarierte Secrets + `ADISPATCH_RUN_ID`, `ADISPATCH_WORKFLOW_ID`,
|
|
131
|
+
`ADISPATCH_SOURCE_*`, `ADISPATCH_WORKSPACE`, `ADISPATCH_CONTEXT_FILE` (JSON-Kontext), `ADISPATCH_RESULT_FILE`. Keine Controller-Umgebung.
|
|
132
|
+
- **Exit-Code = technischer Status:** `0` = ok, alles andere = `crashed` (technischer Fehler, zählt zum Retry-Block).
|
|
133
|
+
- **Fachliches Ergebnis** nur über die Ergebnisdatei (atomar schreiben):
|
|
134
|
+
`{"schema_version":1,"result":"failure","message":"Requirements unclear"}` + `exit 0` → `on_failure` („Needs Maintainer“),
|
|
135
|
+
kein technischer Fehler. Ohne Datei gilt Exit 0 als `success`.
|
|
136
|
+
- **Events:** Zeilen `@@ADISPATCH_EVENT {"type":"status","message":"Tests laufen"}` auf stdout/stderr – nur informativ.
|
|
137
|
+
- **Cancel/Timeout:** SIGTERM an die Prozessgruppe, nach `cancel_grace` (30 s) SIGKILL. Eigene Sessions/Container räumt das
|
|
138
|
+
Skript selbst auf.
|
|
139
|
+
|
|
140
|
+
## Sicherheit
|
|
141
|
+
|
|
142
|
+
- **Keine Anmeldung in V1.** Die API bindet standardmäßig nur an `127.0.0.1`. Fernzugriff ausschließlich über einen
|
|
143
|
+
**authentifizierenden** Reverse Proxy, VPN oder Tailscale; dessen Hostnamen in `server.allowed_hosts` eintragen
|
|
144
|
+
(DNS-Rebinding-Schutz). Bei Nicht-Loopback-Bind warnt `start`. Anleitung: [Betrieb → Fernzugriff](docs/operations.md#fernzugriff-reverse-proxy-spec-15-201).
|
|
145
|
+
- Mutierende Requests brauchen den Header `X-Adispatch-Request: 1`; es gibt keine CORS-Freigaben.
|
|
146
|
+
- **Skripte und YAML sind vertrauenswürdiger Code** mit den Rechten des Dienstes – keine Sandbox. Schreibrecht auf das
|
|
147
|
+
config-dir ist gleichbedeutend mit Codeausführung; `service install` legt es `root`-eigen an und lässt den Dienst unter einem
|
|
148
|
+
eigenen Benutzer laufen.
|
|
149
|
+
- Worker erhalten nur ausdrücklich deklarierte Secrets (z. B. kurzlebiges `GH_TOKEN`); Secrets werden nie in Kontext-/
|
|
150
|
+
Ergebnisdateien, API-Antworten oder Logs persistiert, Redaction in Prozesslogs ist best-effort. Ein Worker kann ihm
|
|
151
|
+
übergebene Secrets dennoch ausgeben.
|
|
152
|
+
- GitHub-Apps mit minimalen Rechten; fehlende Rechte bei Cross-Repo-Abhängigkeiten führen zu `unknown` (kein Start), nicht zu „0“.
|
|
153
|
+
- Event-, Result- und Log-Inhalte werden in der UI nur als Text dargestellt.
|
|
154
|
+
|
|
155
|
+
## Betrieb
|
|
156
|
+
|
|
157
|
+
Kurzfassung – Details in [docs/operations.md](docs/operations.md):
|
|
158
|
+
|
|
159
|
+
- **systemd:** `sudo adispatch service install --config-dir /etc/adispatchd --data-dir /var/lib/adispatchd --create-user`
|
|
160
|
+
(vorher `--dry-run`). Unit mit absolutem Node-/Paketpfad, dediziertem Benutzer, `Restart=on-failure`, `KillMode=mixed`,
|
|
161
|
+
`TimeoutStopSec` > Drain + Cancel + Grace. Secrets in `/etc/default/adispatchd`.
|
|
162
|
+
- **Geplante Stopps/Updates:** `adispatch drain --wait`, dann die neue Version installieren (`npm install -g adispatchd@<neu>` oder Tarball), starten.
|
|
163
|
+
Migrationen laufen automatisch mit Backup in `data-dir/backups/`; config-dir und data-dir bleiben erhalten.
|
|
164
|
+
- **Backups:** config-dir (git), Env-Datei, `adispatchd.sqlite` per `sqlite3 … ".backup …"`, `logs/`, `runs/`.
|
|
165
|
+
`adispatchd.lock*` nie kopieren oder öffnen. data-dir nur auf lokalem Dateisystem (kein NFS).
|
|
166
|
+
- **Recovery:** nach Absturz/Neustart werden verwaiste Runs `crashed` + `on_crash`, nicht sicher identifizierbare Prozesse
|
|
167
|
+
`recovery_pending` (UI „Retry Recovery“); der Scheduler startet erst danach.
|
|
168
|
+
|
|
169
|
+
## Entwicklung
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm install
|
|
173
|
+
npm run check # Gate: tsc --noEmit && vitest run (vor jedem Commit)
|
|
174
|
+
npm run test:pack # npm pack → Installation im Temp-Verzeichnis → init → start → drain → Update (≈ 10–60 s)
|
|
175
|
+
npm run dev -- start --config-dir ./config --data-dir ./data # ohne Build (tsx)
|
|
176
|
+
npm run build # nach dist/
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
src/
|
|
181
|
+
core/ gemeinsame Typen, Verträge, Hilfsfunktionen
|
|
182
|
+
config/ YAML-Konfiguration, Schemas, Reload persistence/ SQLite, Migrationen, Repositories, Instanz-Lock
|
|
183
|
+
filters/ Filter-AST, Prioritäten, Shell-Checks executors/ Shell-Executor, Worker-Protokoll, Prozessidentität
|
|
184
|
+
sources/ GitHub-Adapter identities/ GitHub-App-Credentials
|
|
185
|
+
lifecycle/ Lifecycle-Aktionen, Action-Journal runs/ Run-Controller, Recovery, Shutdown, Retention
|
|
186
|
+
scheduler/ Scheduler, Trigger, Dry-Run views/ notifications/ api/ (HTTP/SSE) ui/ (Weboberfläche)
|
|
187
|
+
cli/ Kommandos service/ systemd-Unit und Installationsplan
|
|
188
|
+
app.ts Bootstrap einer Instanz
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Konventionen: [docs/conventions.md](docs/conventions.md) · Arbeitspakete: [docs/work-items/](docs/work-items/README.md) ·
|
|
192
|
+
Übergaben: `docs/handoff/`.
|
|
193
|
+
|
|
194
|
+
## Lizenz
|
|
195
|
+
|
|
196
|
+
[MIT](LICENSE) © 2026 brokoskokoli
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consistent API error form `{ error: { code, message, details? } }` and the
|
|
3
|
+
* mapping of component error codes to HTTP status codes.
|
|
4
|
+
*/
|
|
5
|
+
import { type Redactor } from '../core/index.js';
|
|
6
|
+
/** Error raised by route handlers with an explicit HTTP status. */
|
|
7
|
+
export declare class ApiError extends Error {
|
|
8
|
+
readonly statusCode: number;
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
11
|
+
constructor(statusCode: number, code: string, message: string, details?: Record<string, unknown> | undefined);
|
|
12
|
+
}
|
|
13
|
+
/** JSON body of an error response. */
|
|
14
|
+
export interface ApiErrorBody {
|
|
15
|
+
error: {
|
|
16
|
+
code: string;
|
|
17
|
+
message: string;
|
|
18
|
+
details?: unknown;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** HTTP status per component error code (WI-03/07/08/09 contracts). */
|
|
22
|
+
export declare const ERROR_STATUS: Readonly<Record<string, number>>;
|
|
23
|
+
/** Status + body for any thrown value. Unknown errors become 500 without internals. */
|
|
24
|
+
export declare function toErrorResponse(err: unknown, redact?: Redactor): {
|
|
25
|
+
status: number;
|
|
26
|
+
body: ApiErrorBody;
|
|
27
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consistent API error form `{ error: { code, message, details? } }` and the
|
|
3
|
+
* mapping of component error codes to HTTP status codes.
|
|
4
|
+
*/
|
|
5
|
+
import { errorMessage, isOrchestratorError, redactValue } from '../core/index.js';
|
|
6
|
+
/** Error raised by route handlers with an explicit HTTP status. */
|
|
7
|
+
export class ApiError extends Error {
|
|
8
|
+
statusCode;
|
|
9
|
+
code;
|
|
10
|
+
details;
|
|
11
|
+
constructor(statusCode, code, message, details) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.statusCode = statusCode;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.details = details;
|
|
16
|
+
this.name = 'ApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** HTTP status per component error code (WI-03/07/08/09 contracts). */
|
|
20
|
+
export const ERROR_STATUS = {
|
|
21
|
+
validation_error: 400,
|
|
22
|
+
invalid_request: 400,
|
|
23
|
+
manual_item_required: 400,
|
|
24
|
+
manual_item_not_supported: 400,
|
|
25
|
+
manual_item_invalid_ref: 400,
|
|
26
|
+
invalid_parameters: 400,
|
|
27
|
+
lifecycle_not_skippable: 400,
|
|
28
|
+
not_found: 404,
|
|
29
|
+
config_unknown_workflow: 404,
|
|
30
|
+
manual_item_not_found: 404,
|
|
31
|
+
view_not_found: 404,
|
|
32
|
+
lock_conflict: 409,
|
|
33
|
+
blocked: 409,
|
|
34
|
+
block_exists: 409,
|
|
35
|
+
invalid_transition: 409,
|
|
36
|
+
run_not_reserved: 409,
|
|
37
|
+
run_already_started: 409,
|
|
38
|
+
run_not_active: 409,
|
|
39
|
+
run_not_cancellable: 409,
|
|
40
|
+
run_not_controlled: 409,
|
|
41
|
+
run_not_recovery_pending: 409,
|
|
42
|
+
lifecycle_busy: 409,
|
|
43
|
+
lifecycle_not_retryable: 409,
|
|
44
|
+
config_invalid: 422,
|
|
45
|
+
source_unavailable: 502,
|
|
46
|
+
run_controller_not_ready: 503,
|
|
47
|
+
run_controller_not_accepting: 503,
|
|
48
|
+
scheduler_not_accepting: 503,
|
|
49
|
+
not_ready: 503,
|
|
50
|
+
kill_failed: 500,
|
|
51
|
+
};
|
|
52
|
+
/** Status + body for any thrown value. Unknown errors become 500 without internals. */
|
|
53
|
+
export function toErrorResponse(err, redact = (s) => s) {
|
|
54
|
+
if (err instanceof ApiError) {
|
|
55
|
+
return {
|
|
56
|
+
status: err.statusCode,
|
|
57
|
+
body: { error: compact(err.code, redact(err.message), err.details === undefined ? undefined : redactValue(err.details, redact)) },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (isOrchestratorError(err)) {
|
|
61
|
+
const status = ERROR_STATUS[err.code] ?? 500;
|
|
62
|
+
return {
|
|
63
|
+
status,
|
|
64
|
+
body: { error: compact(err.code, redact(err.message), err.details === undefined ? undefined : redactValue(err.details, redact)) },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Fastify errors (body too large, bad JSON, unsupported media type, ...).
|
|
68
|
+
const fe = err;
|
|
69
|
+
if (typeof fe.statusCode === 'number' && fe.statusCode >= 400 && fe.statusCode < 500) {
|
|
70
|
+
return {
|
|
71
|
+
status: fe.statusCode,
|
|
72
|
+
body: { error: compact(typeof fe.code === 'string' ? fe.code.toLowerCase() : 'invalid_request', redact(errorMessage(err))) },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { status: 500, body: { error: { code: 'internal_error', message: 'Internal server error' } } };
|
|
76
|
+
}
|
|
77
|
+
function compact(code, message, details) {
|
|
78
|
+
return details === undefined ? { code, message } : { code, message, details };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component `api` – public API (WI-10): Fastify HTTP API under `/api/v1`,
|
|
3
|
+
* `/health`, `/ready`, SSE event hub and log paging (Spec §15, §21).
|
|
4
|
+
* Other components import from this module only.
|
|
5
|
+
*/
|
|
6
|
+
export { createApiServer, CSRF_HEADER, isAllowedHost, type ApiDeps, type ReadinessReport } from './server.js';
|
|
7
|
+
export { ApiError, ERROR_STATUS, toErrorResponse, type ApiErrorBody } from './errors.js';
|
|
8
|
+
export { EventHub, DEFAULT_SSE_HEARTBEAT_MS, DEFAULT_SSE_MAX_BUFFER_BYTES, DEFAULT_SSE_MAX_REPLAY, type EventHubOptions, type StreamEvent, } from './sse.js';
|
|
9
|
+
export { readLogPage, DEFAULT_LOG_PAGE_BYTES, MAX_LOG_PAGE_BYTES, type LogPage } from './logs.js';
|
|
10
|
+
export { toRunSummary, runOutcome, type RunOutcome, type RunSummary, type ActionView } from './serialize.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component `api` – public API (WI-10): Fastify HTTP API under `/api/v1`,
|
|
3
|
+
* `/health`, `/ready`, SSE event hub and log paging (Spec §15, §21).
|
|
4
|
+
* Other components import from this module only.
|
|
5
|
+
*/
|
|
6
|
+
export { createApiServer, CSRF_HEADER, isAllowedHost } from './server.js';
|
|
7
|
+
export { ApiError, ERROR_STATUS, toErrorResponse } from './errors.js';
|
|
8
|
+
export { EventHub, DEFAULT_SSE_HEARTBEAT_MS, DEFAULT_SSE_MAX_BUFFER_BYTES, DEFAULT_SSE_MAX_REPLAY, } from './sse.js';
|
|
9
|
+
export { readLogPage, DEFAULT_LOG_PAGE_BYTES, MAX_LOG_PAGE_BYTES } from './logs.js';
|
|
10
|
+
export { toRunSummary, runOutcome } from './serialize.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAsC,MAAM,aAAa,CAAC;AAC9G,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAC;AACzF,OAAO,EACL,QAAQ,EACR,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,GAGvB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,kBAAkB,EAAgB,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAqD,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Default page size. */
|
|
2
|
+
export declare const DEFAULT_LOG_PAGE_BYTES: number;
|
|
3
|
+
/** Max page size per request. */
|
|
4
|
+
export declare const MAX_LOG_PAGE_BYTES: number;
|
|
5
|
+
/** One page of a log stream. */
|
|
6
|
+
export interface LogPage {
|
|
7
|
+
stream: 'stdout' | 'stderr';
|
|
8
|
+
/** Byte offset of `data` in the file. */
|
|
9
|
+
offset: number;
|
|
10
|
+
/** Byte length of `data`. */
|
|
11
|
+
length: number;
|
|
12
|
+
/** Offset to request next (`offset + length`). */
|
|
13
|
+
nextOffset: number;
|
|
14
|
+
/** Current file size in bytes (0 if the file does not exist). */
|
|
15
|
+
size: number;
|
|
16
|
+
/** True if `nextOffset` reached the current end of the file. */
|
|
17
|
+
eof: boolean;
|
|
18
|
+
/** False if the log file does not exist (yet). */
|
|
19
|
+
exists: boolean;
|
|
20
|
+
data: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read a page. `tail` (bytes) reads the last bytes of the file and takes
|
|
24
|
+
* precedence over `offset`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readLogPage(file: string, stream: 'stdout' | 'stderr', options?: {
|
|
27
|
+
offset?: number;
|
|
28
|
+
limit?: number;
|
|
29
|
+
tail?: number;
|
|
30
|
+
}): Promise<LogPage>;
|
package/dist/api/logs.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paged access to run log files (Spec §15, §16): byte offsets per stream,
|
|
3
|
+
* bounded page size, tail mode. Never reads a whole (up to 100 MB) file.
|
|
4
|
+
*
|
|
5
|
+
* Pages are cut at UTF-8 character boundaries: an incomplete multi-byte
|
|
6
|
+
* sequence at the end of a page is left for the next page, and a page that
|
|
7
|
+
* starts inside a sequence (tail mode) skips the continuation bytes.
|
|
8
|
+
*/
|
|
9
|
+
import { open, stat } from 'node:fs/promises';
|
|
10
|
+
/** Default page size. */
|
|
11
|
+
export const DEFAULT_LOG_PAGE_BYTES = 64 * 1024;
|
|
12
|
+
/** Max page size per request. */
|
|
13
|
+
export const MAX_LOG_PAGE_BYTES = 1024 * 1024;
|
|
14
|
+
/**
|
|
15
|
+
* Read a page. `tail` (bytes) reads the last bytes of the file and takes
|
|
16
|
+
* precedence over `offset`.
|
|
17
|
+
*/
|
|
18
|
+
export async function readLogPage(file, stream, options = {}) {
|
|
19
|
+
const limit = Math.min(Math.max(1, Math.floor(options.limit ?? DEFAULT_LOG_PAGE_BYTES)), MAX_LOG_PAGE_BYTES);
|
|
20
|
+
let size;
|
|
21
|
+
try {
|
|
22
|
+
const st = await stat(file);
|
|
23
|
+
if (!st.isFile())
|
|
24
|
+
throw Object.assign(new Error('not a file'), { code: 'ENOENT' });
|
|
25
|
+
size = st.size;
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err.code === 'ENOENT') {
|
|
29
|
+
return { stream, offset: 0, length: 0, nextOffset: 0, size: 0, eof: true, exists: false, data: '' };
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
let offset;
|
|
34
|
+
let length;
|
|
35
|
+
if (options.tail !== undefined) {
|
|
36
|
+
const tail = Math.min(Math.max(0, Math.floor(options.tail)), MAX_LOG_PAGE_BYTES);
|
|
37
|
+
offset = Math.max(0, size - tail);
|
|
38
|
+
length = size - offset;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
offset = Math.min(Math.max(0, Math.floor(options.offset ?? 0)), size);
|
|
42
|
+
length = Math.min(limit, size - offset);
|
|
43
|
+
}
|
|
44
|
+
if (length === 0)
|
|
45
|
+
return { stream, offset, length: 0, nextOffset: offset, size, eof: offset >= size, exists: true, data: '' };
|
|
46
|
+
const handle = await open(file, 'r');
|
|
47
|
+
let buf;
|
|
48
|
+
try {
|
|
49
|
+
buf = Buffer.alloc(length);
|
|
50
|
+
const { bytesRead } = await handle.read(buf, 0, length, offset);
|
|
51
|
+
buf = buf.subarray(0, bytesRead);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await handle.close();
|
|
55
|
+
}
|
|
56
|
+
// Skip leading continuation bytes (page started inside a character) – only when not at the start of the file.
|
|
57
|
+
let start = 0;
|
|
58
|
+
if (offset > 0)
|
|
59
|
+
while (start < buf.length && start < 3 && isContinuation(buf[start]))
|
|
60
|
+
start++;
|
|
61
|
+
// Leave an incomplete trailing sequence for the next page; if the page consists of nothing
|
|
62
|
+
// else and it is the end of the file (invalid trailing bytes), return it anyway so readers progress.
|
|
63
|
+
let end = completeLength(buf, start);
|
|
64
|
+
if (end <= start && buf.length > start && offset + buf.length >= size)
|
|
65
|
+
end = buf.length;
|
|
66
|
+
const data = buf.subarray(start, end).toString('utf8');
|
|
67
|
+
const pageOffset = offset + start;
|
|
68
|
+
const pageLength = end - start;
|
|
69
|
+
const nextOffset = pageOffset + pageLength;
|
|
70
|
+
return { stream, offset: pageOffset, length: pageLength, nextOffset, size, eof: nextOffset >= size, exists: true, data };
|
|
71
|
+
}
|
|
72
|
+
function isContinuation(byte) {
|
|
73
|
+
return (byte & 0xc0) === 0x80;
|
|
74
|
+
}
|
|
75
|
+
/** Length of `buf` without an incomplete UTF-8 sequence at the end. */
|
|
76
|
+
function completeLength(buf, start) {
|
|
77
|
+
const n = buf.length;
|
|
78
|
+
// Find the start byte of the last (possibly incomplete) sequence within the last 4 bytes.
|
|
79
|
+
for (let i = n - 1; i >= Math.max(start, n - 4); i--) {
|
|
80
|
+
const byte = buf[i];
|
|
81
|
+
if (isContinuation(byte))
|
|
82
|
+
continue;
|
|
83
|
+
const need = byte >= 0xf0 ? 4 : byte >= 0xe0 ? 3 : byte >= 0xc0 ? 2 : 1;
|
|
84
|
+
return i + need <= n ? n : i;
|
|
85
|
+
}
|
|
86
|
+
return n;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/api/logs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE9C,yBAAyB;AACzB,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAChD,iCAAiC;AACjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;AAoB9C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,MAA2B,EAC3B,OAAO,GAAuD,EAAE;IAEhE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC7G,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnF,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACtG,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,MAAc,CAAC;IACnB,IAAI,MAAc,CAAC;IACnB,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;QACjF,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;QAClC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACtE,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAE9H,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAChE,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACnC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IACD,8GAA8G;IAC9G,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAW,CAAC;YAAE,KAAK,EAAE,CAAC;IACxG,2FAA2F;IAC3F,qGAAqG;IACrG,IAAI,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,IAAI;QAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACxF,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;IAClC,MAAM,UAAU,GAAG,GAAG,GAAG,KAAK,CAAC;IAC/B,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,IAAI,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC3H,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;AAChC,CAAC;AAED,uEAAuE;AACvE,SAAS,cAAc,CAAC,GAAW,EAAE,KAAa;IAChD,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,0FAA0F;IAC1F,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAW,CAAC;QAC9B,IAAI,cAAc,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API response shapes (documented in docs/api.md). Responses never contain
|
|
3
|
+
* secrets: run snapshots hold identity IDs only, worker context/result files
|
|
4
|
+
* are redacted by the executor, error details pass `redactValue`.
|
|
5
|
+
*/
|
|
6
|
+
import { type TechnicalStatus } from '../core/index.js';
|
|
7
|
+
import type { EffectiveWorkflow } from '../config/index.js';
|
|
8
|
+
import type { ActionAttemptRecord, BlockRecord, RunActionRecord, RunRecord } from '../persistence/index.js';
|
|
9
|
+
import type { CronWorkflowStatus, WorkflowPlanningDiagnosis } from '../scheduler/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Unambiguous outcome of a run for the UI (Spec §21): business `failure` vs.
|
|
12
|
+
* technical errors vs. cancelled; `lifecycleFailed` separately.
|
|
13
|
+
*/
|
|
14
|
+
export type RunOutcome = 'active' | 'success' | 'failure' | 'technical_error' | 'cancelled';
|
|
15
|
+
/** Run in lists. */
|
|
16
|
+
export interface RunSummary {
|
|
17
|
+
id: string;
|
|
18
|
+
workflowId: string;
|
|
19
|
+
workflowName: string | null;
|
|
20
|
+
triggerType: RunRecord['triggerType'];
|
|
21
|
+
sourceId: string | null;
|
|
22
|
+
sourceItemKey: string | null;
|
|
23
|
+
sourceUrl: string | null;
|
|
24
|
+
scheduledFor: string | null;
|
|
25
|
+
identity: string | null;
|
|
26
|
+
status: TechnicalStatus;
|
|
27
|
+
active: boolean;
|
|
28
|
+
outcome: RunOutcome;
|
|
29
|
+
finalStatus: RunRecord['finalStatus'];
|
|
30
|
+
businessResult: RunRecord['businessResult'];
|
|
31
|
+
finalReason: string | null;
|
|
32
|
+
/** `result.message` of the worker result file, if any. */
|
|
33
|
+
resultMessage: string | null;
|
|
34
|
+
statusMessage: string | null;
|
|
35
|
+
exitCode: number | null;
|
|
36
|
+
lifecycleStatus: RunRecord['lifecycleStatus'];
|
|
37
|
+
lifecycleFailed: boolean;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
startedAt: string | null;
|
|
40
|
+
finishedAt: string | null;
|
|
41
|
+
durationMs: number | null;
|
|
42
|
+
}
|
|
43
|
+
/** Action with its attempts. */
|
|
44
|
+
export interface ActionView extends RunActionRecord {
|
|
45
|
+
attemptsDetail: ActionAttemptRecord[];
|
|
46
|
+
}
|
|
47
|
+
/** Outcome classification (see {@link RunOutcome}). */
|
|
48
|
+
export declare function runOutcome(run: Pick<RunRecord, 'status' | 'businessResult'>): RunOutcome;
|
|
49
|
+
/** Summary of a run. */
|
|
50
|
+
export declare function toRunSummary(run: RunRecord): RunSummary;
|
|
51
|
+
/** Extra fields of the run detail. */
|
|
52
|
+
export declare function runDetailFields(run: RunRecord): Record<string, unknown>;
|
|
53
|
+
/** Workflow in lists / detail. */
|
|
54
|
+
export declare function toWorkflowView(wf: EffectiveWorkflow, extra: {
|
|
55
|
+
activeRuns: number;
|
|
56
|
+
activeBlocks: number;
|
|
57
|
+
cron: CronWorkflowStatus | null;
|
|
58
|
+
diagnosis: WorkflowPlanningDiagnosis | undefined;
|
|
59
|
+
}): Record<string, unknown>;
|
|
60
|
+
/** Block with UI helpers. */
|
|
61
|
+
export declare function toBlockView(block: BlockRecord, extra: {
|
|
62
|
+
affectedRunIds: string[];
|
|
63
|
+
sourceUrl: string | null;
|
|
64
|
+
workflowName: string | null;
|
|
65
|
+
}): Record<string, unknown>;
|