@usine/orchestrator 0.3.0 → 0.4.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 +661 -21
- package/README.md +32 -9
- package/dist/board/items.d.ts +14 -3
- package/dist/board/items.d.ts.map +1 -1
- package/dist/board/items.js +29 -1
- package/dist/board/items.js.map +1 -1
- package/dist/board/projection.d.ts +2 -1
- package/dist/board/projection.d.ts.map +1 -1
- package/dist/board/projection.js +20 -10
- package/dist/board/projection.js.map +1 -1
- package/dist/board/tasks.d.ts +1 -1
- package/dist/board/tasks.d.ts.map +1 -1
- package/dist/board/tasks.js +7 -3
- package/dist/board/tasks.js.map +1 -1
- package/dist/cloudflare.d.ts +32 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +68 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/current-instance/changes.d.ts +3 -3
- package/dist/current-instance/changes.d.ts.map +1 -1
- package/dist/current-instance/changes.js +6 -3
- package/dist/current-instance/changes.js.map +1 -1
- package/dist/current-instance/current-instance.d.ts +3 -3
- package/dist/exe-dev.d.ts +1 -1
- package/dist/exe-dev.d.ts.map +1 -1
- package/dist/exe-dev.js +3 -1
- package/dist/exe-dev.js.map +1 -1
- package/dist/http-client.d.ts +8 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +8 -0
- package/dist/http-client.js.map +1 -0
- package/dist/lease-order.d.ts +60 -0
- package/dist/lease-order.d.ts.map +1 -0
- package/dist/lease-order.js +76 -0
- package/dist/lease-order.js.map +1 -0
- package/dist/local.d.ts +1 -1
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +3 -1
- package/dist/local.js.map +1 -1
- package/dist/operator/attempt.d.ts +22 -3
- package/dist/operator/attempt.d.ts.map +1 -1
- package/dist/operator/attempt.js +22 -3
- package/dist/operator/attempt.js.map +1 -1
- package/dist/operator/board.d.ts +8 -3
- package/dist/operator/board.d.ts.map +1 -1
- package/dist/operator/board.js +8 -4
- package/dist/operator/board.js.map +1 -1
- package/dist/operator/configuration.d.ts +65 -9
- package/dist/operator/configuration.d.ts.map +1 -1
- package/dist/operator/configuration.js +90 -10
- package/dist/operator/configuration.js.map +1 -1
- package/dist/operator/desk.d.ts +33 -8
- package/dist/operator/desk.d.ts.map +1 -1
- package/dist/operator/desk.js +73 -11
- package/dist/operator/desk.js.map +1 -1
- package/dist/operator/http.d.ts +14 -79
- package/dist/operator/http.d.ts.map +1 -1
- package/dist/operator/http.js +20 -14
- package/dist/operator/http.js.map +1 -1
- package/dist/operator/machines.d.ts +71 -0
- package/dist/operator/machines.d.ts.map +1 -0
- package/dist/operator/machines.js +103 -0
- package/dist/operator/machines.js.map +1 -0
- package/dist/operator/need.d.ts +15 -1
- package/dist/operator/need.d.ts.map +1 -1
- package/dist/operator/need.js +15 -0
- package/dist/operator/need.js.map +1 -1
- package/dist/operator/operator.d.ts +36 -10
- package/dist/operator/operator.d.ts.map +1 -1
- package/dist/operator/operator.js +136 -31
- package/dist/operator/operator.js.map +1 -1
- package/dist/operator/overview.d.ts +2 -2
- package/dist/operator/projection.d.ts +2 -0
- package/dist/operator/projection.d.ts.map +1 -1
- package/dist/operator/projection.js +12 -4
- package/dist/operator/projection.js.map +1 -1
- package/dist/operator/run.d.ts +30 -0
- package/dist/operator/run.d.ts.map +1 -1
- package/dist/operator/run.js +31 -1
- package/dist/operator/run.js.map +1 -1
- package/dist/operator/statistics/cache.d.ts +7 -0
- package/dist/operator/statistics/cache.d.ts.map +1 -0
- package/dist/operator/statistics/cache.js +40 -0
- package/dist/operator/statistics/cache.js.map +1 -0
- package/dist/operator/statistics/runs.d.ts +36 -0
- package/dist/operator/statistics/runs.d.ts.map +1 -0
- package/dist/operator/statistics/runs.js +284 -0
- package/dist/operator/statistics/runs.js.map +1 -0
- package/dist/operator/statistics/statistics.d.ts +20 -0
- package/dist/operator/statistics/statistics.d.ts.map +1 -0
- package/dist/operator/statistics/statistics.js +138 -0
- package/dist/operator/statistics/statistics.js.map +1 -0
- package/dist/operator/statistics/tasks.d.ts +30 -0
- package/dist/operator/statistics/tasks.d.ts.map +1 -0
- package/dist/operator/statistics/tasks.js +244 -0
- package/dist/operator/statistics/tasks.js.map +1 -0
- package/dist/operator/statistics/view.d.ts +302 -0
- package/dist/operator/statistics/view.d.ts.map +1 -0
- package/dist/operator/statistics/view.js +243 -0
- package/dist/operator/statistics/view.js.map +1 -0
- package/dist/operator/statistics/window.d.ts +24 -0
- package/dist/operator/statistics/window.d.ts.map +1 -0
- package/dist/operator/statistics/window.js +47 -0
- package/dist/operator/statistics/window.js.map +1 -0
- package/dist/operator/task.d.ts +2 -1
- package/dist/operator/task.d.ts.map +1 -1
- package/dist/operator/task.js +3 -3
- package/dist/operator/task.js.map +1 -1
- package/dist/operator/usage.d.ts +10 -18
- package/dist/operator/usage.d.ts.map +1 -1
- package/dist/operator/usage.js +57 -42
- package/dist/operator/usage.js.map +1 -1
- package/dist/polling/closed-item-asks.d.ts +8 -0
- package/dist/polling/closed-item-asks.d.ts.map +1 -0
- package/dist/polling/closed-item-asks.js +28 -0
- package/dist/polling/closed-item-asks.js.map +1 -0
- package/dist/polling/open-pull-requests.d.ts +28 -0
- package/dist/polling/open-pull-requests.d.ts.map +1 -0
- package/dist/polling/open-pull-requests.js +131 -0
- package/dist/polling/open-pull-requests.js.map +1 -0
- package/dist/polling/polling.d.ts.map +1 -1
- package/dist/polling/polling.js +31 -18
- package/dist/polling/polling.js.map +1 -1
- package/dist/polling/repository-sync.d.ts +1 -0
- package/dist/polling/repository-sync.d.ts.map +1 -1
- package/dist/polling/repository-sync.js +1 -1
- package/dist/polling/repository-sync.js.map +1 -1
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +10 -10
- package/dist/runtime.js.map +1 -1
- package/dist/scheduler.d.ts +13 -4
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +312 -55
- package/dist/scheduler.js.map +1 -1
- package/dist/ssh.d.ts +71 -7
- package/dist/ssh.d.ts.map +1 -1
- package/dist/ssh.js +84 -15
- package/dist/ssh.js.map +1 -1
- package/dist/worker-engine.d.ts +7 -4
- package/dist/worker-engine.d.ts.map +1 -1
- package/dist/worker-engine.js +36 -5
- package/dist/worker-engine.js.map +1 -1
- package/dist/worker-status.d.ts +80 -10
- package/dist/worker-status.d.ts.map +1 -1
- package/dist/worker-status.js +31 -10
- package/dist/worker-status.js.map +1 -1
- package/package.json +29 -25
package/README.md
CHANGED
|
@@ -10,15 +10,19 @@ The platform-free entry points are `PollingTriggers` (`/polling-triggers`), `Sch
|
|
|
10
10
|
|
|
11
11
|
`Operator` provides run and board observation, transcript pages, label-backed board moves, approve, retry, cancel, and loop pause. It returns typed data, and consuming apps own the wording. Captured executor defects appear as `DefectFailure` with a safe message, without the original cause. `OperatorObservation` gives the same read-only projection without execution services.
|
|
12
12
|
|
|
13
|
-
`OperatorConfiguration.view(instance)` returns the instance folder as already parsed: each loop, agent, worker, registered repo with its lanes, and the settings; every quarantined loop and invalid agent with its error; each loop's pause; each worker's slots
|
|
13
|
+
`OperatorConfiguration.view(instance)` returns the instance folder as already parsed: each loop, agent, worker, registered repo with its lanes, and the settings; every quarantined loop and invalid agent with its error; each loop's pause; each worker's slots, last preflight per harness, and Attempt container limits (from `WorkerStatus`); and each repo's labels as polling last read them. A gateway's admin token appears only as its variable name.
|
|
14
14
|
|
|
15
15
|
`Operator.overview(instance)` keeps the latest polling and schedule failures visible after sibling work continues. Concurrent reads at one durable revision share a computation.
|
|
16
16
|
|
|
17
|
+
`Operator.statistics(instance, window)` and `Operator.runStatistics(instance, window)` cache their computed views in memory for up to five minutes, separately for `7d`, `30d`, and `all`. Concurrent requests for the same view and window share a computation. Each request checks the store's projection revision: run closure, retry, failure resolution, and close-time changes invalidate cached views, as do item and budget changes. Reloading the instance also invalidates them. If the revision record is missing, requests bypass the cache. Debug logs report cache hits and misses with the window.
|
|
18
|
+
|
|
19
|
+
`GET /api/statistics/runs` includes `needsAttention.failureWait`, the median time from failure to the first recorded operator answer. `retryWait` and `resolutionWait` report those answers separately. A resolution followed by a retry counts once, as a resolution. Unanswered failures and older resolutions without timestamps contribute no wait.
|
|
20
|
+
|
|
17
21
|
### Overview and board
|
|
18
22
|
|
|
19
23
|
The overview joins the latest 500 polled items into task cards in each repo's authored lanes. Same-repo issue-closing links and saved run PR results join an issue to a PR. Repos without lanes get proposals from the label catalog, including unused labels and repos without items. Items matching no source go to a read-only `Unmapped` lane; label matches beat state sources. Each card keeps every saved run on its items, oldest first.
|
|
20
24
|
|
|
21
|
-
The newest open run sets the current item; between runs, an open PR, then the latest settled PR, replaces the issue. Its labels and state pick the lane, so a task whose PR is in review stays put when the issue loses its trigger label. While the newest open run is `Running` on an open item, the card goes to the first label lane matching the labels in that run's event payload (`RunReader.events`), until the run ends; if
|
|
25
|
+
The newest open run sets the current item; between runs, an open PR, then the latest settled PR, replaces the issue. Its labels and state pick the lane, so a task whose PR is in review stays put when the issue loses its trigger label. While the newest open run is `Running` on an open item, the card goes to the first label lane matching the item's current labels; if none matches, to the first label lane matching the labels in that run's event payload (`RunReader.events`), until the run ends; if neither matches, the item's state decides. `Operator.task` places tasks the same way, and `Operator.moveBoardItem` records the lane the card left (ADR 0024).
|
|
22
26
|
|
|
23
27
|
A card also has `pullRequest` (the open PR, else the newest merged one) and `usage` from `RunUsageReader`: ended attempt time, working attempts' starts, and tokens and dollars per billing pool over its runs' sessions. A pool's dollars are its priced sessions'; `unpriced` marks a session in a priced pool without a price yet, such as a Claude session before its first result, so the cost is a lower bound. The overview is kept until the revision or instance changes; token reports change neither, so each read re-reads working attempts' reports and re-sums cards whose runs used more.
|
|
24
28
|
|
|
@@ -31,8 +35,10 @@ A malformed item projection is quarantined and reported on the board and the nee
|
|
|
31
35
|
`Operator.detail` takes distinct cold, live-tail, and earlier-page requests. A cold read keeps the newest 500 rows per attempt, with an earlier cursor per truncated attempt for paging back. A live tail returns at most 500 rows across the run and advances its cursor from the last row. Each response also has:
|
|
32
36
|
|
|
33
37
|
- the newest attempt's worker;
|
|
38
|
+
- `run.agentBrain`: the saved agent definition's harness, model, and effort, which Retry reuses, including any edit-and-retry amendment;
|
|
39
|
+
- `agentDefinitionChanged`: whether the loaded agent definition differs from the saved one (false if the agent is no longer loaded). Edit and retry takes the current loaded definition, even when the task and agent name stay the same;
|
|
34
40
|
- `RunDurationView`: ended attempts' time, the running one's start, and how many ended without a recorded end or transcript row;
|
|
35
|
-
- `RunUsageView`: each session's highest token report, summed per session, pool, and run,
|
|
41
|
+
- `RunUsageView`: each session's highest token report, summed per session, pool, and run, at the harness's price or, for a Codex session, at OpenAI's API rates for its token breakdown (`@usine/agents/openai-prices`), as an estimate on the Claude and ChatGPT plans (ADR 0013). A Codex model without rates stays unpriced, with one warning per model. Pools come from `sessionPool` (`@usine/core/agent/session`), so a Claude Code child on its gateway's API key counts in API keys at the model's rate. Ended attempts' reports are kept in memory until restart, so polling reads only the attempt in flight.
|
|
36
42
|
|
|
37
43
|
Each `AttemptView` has `start`, the last reattach time, and `workerAccess`: `LocalWorkerAccess`, `SshWorkerAccess` with `destination` and `port` from `usine.yaml` (exe.dev reads as `<name>.exe.xyz`), or null if the worker is no longer declared. `detail`, like the overview, task, and desk reads, takes the loaded instance. `Operator.retry` and `Operator.editAndRetry` pass the instance's `operator` login to the next attempt.
|
|
38
44
|
|
|
@@ -52,9 +58,11 @@ One read asks GitHub once, from the earliest uncached attempt's start. Attempts
|
|
|
52
58
|
|
|
53
59
|
Cycle exhaustion is a `CycleBudgetNeed` in the overview and marks the card as needing attention. `Operator.resetCycleBudget` answers the exact ask, at `POST /api/cycle-budgets/reset` with `{ "askId": "…" }`; a stale or differently answered ask returns 409. Polling then restarts the repo cursor, fetches current items, and evaluates active loops before opening runs, retrying a failed refresh. Each item fetch captures its budget generation, resets supersede in-flight observations, and new events use the current loop configuration. GitHub delivery of these asks is [#36](https://github.com/basaltbytes/usine/issues/36).
|
|
54
60
|
|
|
61
|
+
Each poll cycle closes the ask of an item that is closed, merged, or gone from GitHub with the reason `ItemClosed`, without a reset, so the ask leaves the overview and the item keeps its budget. A failed run on such an item stays in `ProjectView.runs` with `canResolveFailure`, but the overview lists no `RunFailureNeed` for it, and it doesn't count toward `ProjectView.status`, the card's `needsYou`, or the run the card shows (`BoardTaskView.run`). `RunDetailView.settledItemState` gives the item's state. A waiting run on a closed item is revoked when its loop's `if:` stops matching, as with a label change. The overview lists no `ApprovalNeed` or `RunFailureNeed` for a run in a repository that `usine.yaml` no longer lists, which has no `ProjectView`.
|
|
62
|
+
|
|
55
63
|
`Operator.runAgain(instance, runId, edit)` opens a new run for a succeeded or canceled run's item state, with the edit's task and roster agent and the loaded loop's step, approval, and cycle limit, as the `operator` login. It returns `Claimed` or `CycleBlocked` with the ask, and fails with `LoopNotFoundError` if the loop isn't loaded. API: `POST /api/runs/:runId/run-again` with `{ "task": "...", "agent": "..." }`. `RunView.canRunAgain` says when it applies; `RunView.runAgain` names the original run.
|
|
56
64
|
|
|
57
|
-
`Operator.resolveFailure(runId, attemptId)` clears attention for the current failed attempt, at `POST /api/runs/:runId/resolve-failure` with `{ "attemptId": "..." }`. The failure stays in history with retry available; Needs attention, project status, and board attention reflect it, and a later failure asks again. For a question (`QuestionFailureView`) or blocker (`BlockedFailureView`), it lifts the loop's suppression of the item and gives the newest queued event its turn.
|
|
65
|
+
`Operator.resolveFailure(instance, runId, attemptId)` clears attention for the current failed attempt, at `POST /api/runs/:runId/resolve-failure` with `{ "attemptId": "..." }`. The resolution records its time and the instance's configured operator login. The failure stays in history with retry available; Needs attention, project status, and board attention reflect it, and a later failure asks again. For a question (`QuestionFailureView`) or blocker (`BlockedFailureView`), it lifts the loop's suppression of the item and gives the newest queued event its turn.
|
|
58
66
|
|
|
59
67
|
## Operator HTTP
|
|
60
68
|
|
|
@@ -76,6 +84,10 @@ It then gets changed items from `GitHubPolling`, fetches one snapshot each, upda
|
|
|
76
84
|
|
|
77
85
|
Change detection never lists deleted or transferred issues, so at most every 10 minutes per repo, the cycle checks every stored open issue with `GitHubPolling.missingIssues`. Missing ones are marked gone, leaving the open lanes, and their pending or approval-waiting work is revoked. A failed check is logged and reported, and retried 10 minutes later.
|
|
78
86
|
|
|
87
|
+
A pull request's mergeability changes when its base branch moves, without a change that detection lists. The cursor keeps each open pull request with its base branch, `mergeable`, and whether checks are unsettled, plus the head of each base branch. Each cycle reads every base head with `GitHubPolling.baseRefHead`, and fetches an open pull request again when its base head moved or can't be read, or while its `mergeable` is `UNKNOWN`. The first read of a head counts as moved.
|
|
88
|
+
|
|
89
|
+
Polling also fetches an open pull request on every cycle while its check rollup is `PENDING` or `EXPECTED`, or while the rollup is absent and `mergeStateStatus` is `BLOCKED`. An absent rollup on a blocked pull request conservatively means checks are expected, including when reviews also block merging. A terminal rollup stops these retries; an absent rollup with another merge state does not cause them. Each retry costs one snapshot request, uses the existing projection and loop evaluation path, and respects GitHub backoff. Older cursors refresh open pull requests once to learn their check state. A later check rerun on a settled pull request still needs an item update or base movement to trigger a fetch.
|
|
90
|
+
|
|
79
91
|
An `if:` or prompt that fails on an item affects only that loop and item, and the cursor advances. The cursor records the failing item, so each cycle reports it until the loop evaluates, logging the error once per loop, item, and content hash. The first cycle after a restart or instance change fetches failing items again, so a fix applies without waiting for a change.
|
|
80
92
|
|
|
81
93
|
Cursors live in the engine's `CursorStore`. When a loop starts watching a repo or its trigger changes, the orchestrator drops that repo's cursor and polls cold; the event ledger absorbs repeats.
|
|
@@ -122,10 +134,14 @@ The event key covers the loop, canonical repo, and scheduled time. An in-memory
|
|
|
122
134
|
|
|
123
135
|
## Worker scheduling
|
|
124
136
|
|
|
125
|
-
`stepExecutorLayer(config, brains, options)` (`/scheduler`, platform-free) takes the roster's `BrainSpec` values. At startup it checks every worker against the roster and the instance's agents, gives each `max_concurrent_attempts` slots, and assigns a free slot of the first worker in `usine.yaml` order to the run
|
|
137
|
+
`stepExecutorLayer(config, brains, options)` (`/scheduler`, platform-free) takes the roster's `BrainSpec` values. At startup it checks every worker against the roster and the instance's agents, gives each `max_concurrent_attempts` slots, and assigns a free slot of the first worker in `usine.yaml` order to the first waiting run in `leaseOrder` (`lease-order.ts`): a reservation naming a worker, then a run of a loop with `priority`, higher first, then a run by the lane its trigger matched, later lane first, then any other run, each group in event order. The position is read from the current loop file and the repository's `lanes` when the run starts waiting. Runs stay pending while all slots are taken. Each lease logs the run, its position and its source, and how many waiting runs with earlier events it passed.
|
|
126
138
|
|
|
127
139
|
A reservation naming a worker, as a reattaching run's does, takes only that worker's slot, before unnamed ones; it waits while that worker is full, and later runs can take other workers. For an undeclared worker, it logs a warning and waits for a reload that declares it. `occupy` takes a slot at once for an attempt already running, as at engine startup, even with none free, logging a warning; the worker then takes no new attempt until enough end, and each extra slot retires with its attempt. An unknown worker gets no slot, with a warning.
|
|
128
140
|
|
|
141
|
+
With `options.machines` (`WorkerMachines`, which the SSH composition passes), a worker that creates a machine per attempt gets one before its lease: when a slot frees, the scheduler takes the next waiting run in lease order, creates the machine, and then leases the slot with it; the run's attempt gets the machine in `execute`, and the lease's release deletes it. A reservation naming a worker, a reattachment, creates no machine, and `occupy` holds the attempt's machine instead. A failed creation isn't an attempt failure: the slot frees, the run keeps its place in the queue, and the worker creates no machine for a pause of 30 seconds that doubles with each failure in a row, up to 10 minutes. A failed first-machine check is recorded as a preflight failure too. A run that stops waiting interrupts its creation, which deletes the machine. `StepExecutor.sweep` runs `Executor.sweep`, which removes the Codex homes that a crash left on SSH workers, then `WorkerMachines.sweep`.
|
|
142
|
+
|
|
143
|
+
`StepExecutor.discard` runs `options.discard`, or does nothing without it. The local, SSH, Cloudflare, and exe.dev compositions pass the `discard` of `sshWorkerRecordsFor` on `.usine/ssh-workers` in the instance directory, where the SSH executor records each long-lived SSH worker's machine (`withSshWorkerDiscard`). So **Discard leftovers** removes the discarded attempts' Codex homes from an SSH worker that `usine.yaml` no longer declares, even after it replaced the SSH workers with workers of another kind.
|
|
144
|
+
|
|
129
145
|
The layer registers with `CurrentInstance`, so a reload changes workers without touching running attempts:
|
|
130
146
|
|
|
131
147
|
- A worker with the same name, kind, and settings and a higher `max_concurrent_attempts` is preflighted with the new count and every rostered brain, and its new slots take waiting runs at once. With a lower count, it takes new attempts only while it runs fewer than the new count, and each extra slot retires with its attempt.
|
|
@@ -135,7 +151,7 @@ The layer registers with `CurrentInstance`, so a reload changes workers without
|
|
|
135
151
|
|
|
136
152
|
Other workers are preflighted with the brains the reload adds; any failed preflight refuses the reload. Resizes, additions, removals, and retired slots are logged with counts. Attempts read `max_children`, the runaway ceilings, and the parent effort from `CurrentInstance` at start, so reloads apply to the next attempt. The layer rejects worker kinds its executor can't run. Capability failures during preparation become non-retryable `AgentFailure` outcomes, like missing model credentials.
|
|
137
153
|
|
|
138
|
-
The layer also provides `WorkerStatus` (`/worker-status`): each worker's slots and how many are taken, and the last preflight of each worker's own prerequisites and of each harness, with time and result. It records the startup preflight and every reload's, refused ones included. The local, SSH, and exe.dev compositions pass it on; `WorkerStatus.idle` stands in where no worker runs attempts.
|
|
154
|
+
The layer also provides `WorkerStatus` (`/worker-status`): each worker's slots and how many are taken, the waiting runs in lease order, and the last preflight of each worker's own prerequisites and of each harness, with time and result: passed, failed, for an SSH worker without Docker, degraded, or, for a provisioned worker's harnesses before its first machine of the day, deferred. With them, it keeps the limits of each attempt container from the last passed preflight that checked the worker's machine, which a provisioned worker's first machine of the day reports; a preflight without a machine keeps them. It also lists the provisioned workers' machines in flight and each provisioned worker's last failed creation with its next try. It records the startup preflight and every reload's, refused ones included. The local, SSH, and exe.dev compositions pass it on; `WorkerStatus.idle` stands in where no worker runs attempts.
|
|
139
155
|
|
|
140
156
|
## Compositions
|
|
141
157
|
|
|
@@ -153,19 +169,26 @@ Provide `CredentialProvider` and `CurrentInstance` to `EngineLive`, then request
|
|
|
153
169
|
|
|
154
170
|
All three compositions run each attempt's Pi parent in the orchestrator as a durable task (`@usine/durable`). `layer` needs the `AttemptRuntime` and the `ClaudeLoginLock` shared by its Claude children and the worker's preflight, and builds the runtime before the engine, so tasks are resumed before run recovery and a restart reattaches live attempts. Stored v3–v6 transcripts stay readable.
|
|
155
171
|
|
|
156
|
-
`runtimeLayer(instanceDirectory)` (`/runtime`) composes the whole application, adding GitHub polling, schedule triggers, CEL parsing, `Orchestrator`, and `Operator`. It uses the SSH composition if every worker is `ssh`, otherwise the local one, which refuses other kinds at startup. One `ClaudeLoginLock` serves both the attempt runtime and the worker composition. Preflight requirements come from the instance's agents at build time. It needs `GitHubCredentials` and `CurrentInstance`, which the host loads. `operatorObservationLayer(instanceDirectory)` is the read-only Node composition for setup and maintenance: it provides `OperatorObservation` and `OperatorConfiguration` from durable runs and loop state, with triggers idle and no worker running attempts.
|
|
172
|
+
`runtimeLayer(instanceDirectory, instanceId)` (`/runtime`) composes the whole application, adding GitHub polling, schedule triggers, CEL parsing, `Orchestrator`, and `Operator`. It uses the SSH composition if every worker is `ssh`, the Cloudflare composition if every worker is `cloudflare`, otherwise the local one, which refuses other kinds at startup. One `ClaudeLoginLock` serves both the attempt runtime and the worker composition. Preflight requirements come from the instance's agents at build time. It needs `GitHubCredentials` and `CurrentInstance`, which the host loads. `operatorObservationLayer(instanceDirectory)` is the read-only Node composition for setup and maintenance: it provides `OperatorObservation` and `OperatorConfiguration` from durable runs and loop state, with triggers idle and no worker running attempts.
|
|
157
173
|
|
|
158
|
-
`/ssh` composes the scheduler with the SSH executor, and also needs a `CredentialProvider` for the token each attempt writes on its machine or attaches to its gateway. `sshWorkersOf(config)` returns the declared SSH workers; `sshWorkerAddressesOf(config)` turns them into executor addresses, reading each gateway's admin token from its `admin_token_env` variable, and fails with a `gateway` capability error naming an unset one. Admin APIs are called with `fetch` through the executor's forward to each gateway's `destination`.
|
|
174
|
+
`/ssh` composes the scheduler with the SSH executor, and also needs a `CredentialProvider` for the token each attempt writes on its machine or attaches to its gateway. `sshWorkersOf(config)` returns the declared SSH workers; `sshWorkerAddressesOf(config)` turns them into executor addresses, with their `container` settings, reading each gateway's admin token from its `admin_token_env` variable, and fails with a `gateway` capability error naming an unset one. Admin APIs are called with `fetch` through the executor's forward to each gateway's `destination`. `workerEngineLayer` labels the SSH executor's attempt containers with `github-app-<App ID>` from `GitHubCredentials`, the instance's identity (ADR 0011), and with the `InstanceId` that the host passes, which tells apart instances that share the App.
|
|
159
175
|
|
|
160
176
|
`sshStepExecutorLayer(config, brains)` lets a reload resize, add, and remove SSH workers through `SshWorkers`. An added worker joins the executor before preflight, and its gateway's token is read then; an unset variable refuses the reload with `ReloadPreflightError` naming it, since a running process doesn't see variables set after it started. A removed worker leaves the executor once its last attempt ends, and its gateway's forward closes if no other worker uses it.
|
|
161
177
|
|
|
178
|
+
`/cloudflare` composes the scheduler with the Cloudflare executor, with the GitHub App's ID as the instance that owns the sandboxes. `cloudflareWorkersOf(config)` returns the declared Cloudflare workers; `cloudflareWorkerAddressesOf(config)` turns them into executor addresses, reading each worker's Access service token and provider API keys from the variables that it names, and fails with a `credentials` capability error naming an unset one. The executor reads these secrets once, so a reload can resize or remove a Cloudflare worker, and add back only one that `usine up` started with; any other addition refuses the reload with `ReloadPreflightError`.
|
|
179
|
+
|
|
162
180
|
```ts
|
|
181
|
+
import { InstanceId } from "@usine/core/instance"
|
|
163
182
|
import { layer as sshLayer, sshWorkerAddressesOf } from "@usine/orchestrator/ssh"
|
|
164
183
|
import { Effect, Layer } from "effect"
|
|
165
184
|
|
|
166
185
|
const EngineLive = Layer.unwrap(
|
|
167
186
|
Effect.map(sshWorkerAddressesOf(config), (workers) =>
|
|
168
|
-
sshLayer(instanceDirectory, config, brains, {
|
|
187
|
+
sshLayer(instanceDirectory, config, brains, {
|
|
188
|
+
workers,
|
|
189
|
+
instance: "github-app-123456",
|
|
190
|
+
instanceId: InstanceId.make("0b4e7f3c-5a2d-4c1e-9f6b-8d7a2e1c3b4f"),
|
|
191
|
+
}),
|
|
169
192
|
),
|
|
170
193
|
)
|
|
171
194
|
```
|
package/dist/board/items.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { GitHubLabelName } from "@usine/config/label";
|
|
2
|
-
import type
|
|
2
|
+
import { type LaneItemState } from "@usine/config/board";
|
|
3
3
|
import { ItemKey, ItemNumber } from "@usine/core/item";
|
|
4
|
-
import type
|
|
5
|
-
import { Effect, Schema } from "effect";
|
|
4
|
+
import { type ItemProjection, ItemProjectionReader } from "@usine/engine/item-projection";
|
|
5
|
+
import { Effect, Option, Schema } from "effect";
|
|
6
6
|
import { BoardProjectionError } from "../operator/board.ts";
|
|
7
|
+
import type { RunView } from "../operator/run.ts";
|
|
7
8
|
export interface ProjectedItem {
|
|
8
9
|
readonly itemKey: ItemKey;
|
|
9
10
|
readonly kind: "issue" | "pr";
|
|
@@ -23,4 +24,14 @@ export interface ProjectedItem {
|
|
|
23
24
|
export declare const payloadLabels: (kind: ProjectedItem["kind"], payload: Schema.Json) => Effect.Effect<readonly (string & import("effect/Brand").Brand<"GitHubLabelName">)[], Schema.SchemaError, never>;
|
|
24
25
|
/** Returns the Board's view of one stored Item. An Item gone from GitHub counts as closed. */
|
|
25
26
|
export declare const projectedItem: (item: ItemProjection) => Effect.Effect<ProjectedItem, BoardProjectionError>;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the state of the stored Item `itemKey` if it's closed, merged, or gone from GitHub.
|
|
29
|
+
* Returns `None` for an open Item, an Item that no trigger stored, or a payload that doesn't decode.
|
|
30
|
+
*/
|
|
31
|
+
export declare const settledItemState: (itemKey: string & import("effect/Brand").Brand<"ItemKey">) => Effect.Effect<Option.Option<"issue-closed" | "pr-closed" | "pr-merged">, import("@usine/engine/engine").EnginePersistenceError, ItemProjectionReader>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns whether `run` needs the operator: it waits for approval, or its failure is unresolved and
|
|
34
|
+
* `itemSettled` is false. An Item is settled when it's closed, merged, or gone from GitHub.
|
|
35
|
+
*/
|
|
36
|
+
export declare const runNeedsAttention: (run: RunView, itemSettled: boolean) => boolean;
|
|
26
37
|
//# sourceMappingURL=items.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../src/board/items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../src/board/items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAsB,KAAK,aAAa,EAAyB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,KAAK,cAAc,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAChD,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAQD;;;GAGG;AACH,eAAO,MAAM,aAAa,SAAU,aAAa,CAAC,MAAM,CAAC,WAAW,MAAM,CAAC,IAAI,oHAG5E,CAAC;AASJ,8FAA8F;AAC9F,eAAO,MAAM,aAAa,SAClB,cAAc,KACnB,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,oBAAoB,CA6ChD,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sNAe3B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAS,OAAO,eAAe,OAAO,KAAG,OACZ,CAAC"}
|
package/dist/board/items.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { GitHubLabelName } from "@usine/config/label";
|
|
2
|
+
import { isSettledItemState } from "@usine/config/board";
|
|
2
3
|
import { ItemKey, ItemNumber } from "@usine/core/item";
|
|
4
|
+
import { ItemProjectionReader } from "@usine/engine/item-projection";
|
|
3
5
|
import { IssuePayload, PullRequestPayload } from "@usine/github/polling";
|
|
4
|
-
import { Effect, Option, Schema } from "effect";
|
|
6
|
+
import { Effect, Option, Result, Schema } from "effect";
|
|
5
7
|
import { BoardProjectionError } from "../operator/board.js";
|
|
6
8
|
const labelsFrom = Schema.decodeUnknownEffect(Schema.Array(GitHubLabelName));
|
|
7
9
|
// Only the labels are read, so a payload stored before later fields were added still decodes.
|
|
@@ -55,4 +57,30 @@ export const projectedItem = (item) => Effect.gen(function* () {
|
|
|
55
57
|
}
|
|
56
58
|
return yield* projectionError(item, `Unsupported item projection source: ${item.source}`);
|
|
57
59
|
});
|
|
60
|
+
/**
|
|
61
|
+
* Returns the state of the stored Item `itemKey` if it's closed, merged, or gone from GitHub.
|
|
62
|
+
* Returns `None` for an open Item, an Item that no trigger stored, or a payload that doesn't decode.
|
|
63
|
+
*/
|
|
64
|
+
export const settledItemState = Effect.fn("settledItemState")(function* (itemKey) {
|
|
65
|
+
yield* Effect.annotateCurrentSpan({ itemKey });
|
|
66
|
+
const items = yield* ItemProjectionReader;
|
|
67
|
+
const stored = yield* items.get(itemKey);
|
|
68
|
+
if (Option.isNone(stored))
|
|
69
|
+
return Option.none();
|
|
70
|
+
const projected = yield* Effect.result(projectedItem(stored.value));
|
|
71
|
+
if (Result.isFailure(projected)) {
|
|
72
|
+
yield* Effect.logDebug("Stored Item payload doesn't decode; treating the Item as open", {
|
|
73
|
+
itemKey,
|
|
74
|
+
message: projected.failure.message,
|
|
75
|
+
});
|
|
76
|
+
return Option.none();
|
|
77
|
+
}
|
|
78
|
+
const { state } = projected.success;
|
|
79
|
+
return isSettledItemState(state) ? Option.some(state) : Option.none();
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Returns whether `run` needs the operator: it waits for approval, or its failure is unresolved and
|
|
83
|
+
* `itemSettled` is false. An Item is settled when it's closed, merged, or gone from GitHub.
|
|
84
|
+
*/
|
|
85
|
+
export const runNeedsAttention = (run, itemSettled) => run.canApprove || (run.canResolveFailure && !itemSettled);
|
|
58
86
|
//# sourceMappingURL=items.js.map
|
package/dist/board/items.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"items.js","sourceRoot":"","sources":["../../src/board/items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"items.js","sourceRoot":"","sources":["../../src/board/items.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,8 @@ import { type RunView } from "../operator/run.ts";
|
|
|
8
8
|
import type { RunUsageView } from "../operator/usage.ts";
|
|
9
9
|
/**
|
|
10
10
|
* Groups the repository's stored Items into Task cards and the cards into lanes. A card with a
|
|
11
|
-
* running Run
|
|
11
|
+
* running Run goes to the label lane that its current Item's labels match. If none matches, the
|
|
12
|
+
* card stays in the lane its Run's trigger matched, read from `runEvents`.
|
|
12
13
|
*/
|
|
13
14
|
export declare const projectBoard: (repository: ManagedRepo, repositoryLabels: readonly string[], storedItems: readonly ItemProjection[], runs: readonly RunView[], cycleBlockedItems?: readonly (string & import("effect/Brand").Brand<"ItemKey">)[] | undefined, itemLoops?: ReadonlyMap<string & import("effect/Brand").Brand<"ItemKey">, readonly (string & import("effect/Brand").Brand<"LoopName">)[]> | undefined, runUsage?: ReadonlyMap<string & import("effect/Brand").Brand<"RunId">, RunUsageView> | undefined, runEvents?: ReadonlyMap<string & import("effect/Brand").Brand<"RunId">, Event> | undefined) => Effect.Effect<BoardView, never, never>;
|
|
14
15
|
export declare const labelMutationForBoardMove: (board: BoardView, target: WritableBoardLaneView) => ItemLabelMutation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/board/projection.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAChD,OAAO,EAIL,SAAS,EAET,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/board/projection.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAChD,OAAO,EAIL,SAAS,EAET,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAwHzD;;;;GAIG;AACH,eAAO,MAAM,YAAY,gmBAiFxB,CAAC;AAEF,eAAO,MAAM,yBAAyB,UAC7B,SAAS,UACR,qBAAqB,KAC5B,iBAaF,CAAC"}
|
package/dist/board/projection.js
CHANGED
|
@@ -35,10 +35,11 @@ const laneFor = (item, lanes) => isSettledItemState(item.state)
|
|
|
35
35
|
? stateLaneFor(item, lanes)
|
|
36
36
|
: (labelLaneFor(item, lanes) ?? stateLaneFor(item, lanes));
|
|
37
37
|
/**
|
|
38
|
-
* Returns the
|
|
39
|
-
*
|
|
38
|
+
* Returns the lane of a card whose newest open Run is `running`: the label lane that the current
|
|
39
|
+
* Item's labels match, else the label lane that the labels of the Run's Event match. Returns
|
|
40
|
+
* `undefined` if the current Item is closed or merged, or if neither set of labels matches a lane.
|
|
40
41
|
*/
|
|
41
|
-
const
|
|
42
|
+
const runningLaneFor = Effect.fn("Board.runningLane")(function* (repository, view, running, lanes, runEvents) {
|
|
42
43
|
const fields = {
|
|
43
44
|
repository: repository.name,
|
|
44
45
|
itemKey: view.itemKey,
|
|
@@ -48,23 +49,32 @@ const triggerLaneFor = Effect.fn("Board.triggerLane")(function* (repository, vie
|
|
|
48
49
|
yield* Effect.annotateCurrentSpan(fields);
|
|
49
50
|
if (isSettledItemState(view.state))
|
|
50
51
|
return undefined;
|
|
52
|
+
const current = labelLaneFor(view, lanes);
|
|
53
|
+
if (current !== undefined) {
|
|
54
|
+
yield* Effect.logDebug("Placing the running card in the lane its Item's labels match", {
|
|
55
|
+
...fields,
|
|
56
|
+
lane: current.lane.name,
|
|
57
|
+
});
|
|
58
|
+
return current;
|
|
59
|
+
}
|
|
51
60
|
const event = runEvents.get(running.id);
|
|
52
61
|
if (event === undefined) {
|
|
53
|
-
yield* Effect.logDebug("No Event read for the card's running Run; placing it by its Item's
|
|
62
|
+
yield* Effect.logDebug("No Event read for the card's running Run; placing it by its Item's state", fields);
|
|
54
63
|
return undefined;
|
|
55
64
|
}
|
|
56
|
-
const labels = yield* payloadLabels(view.kind, event.payload).pipe(Effect.map(Option.some), Effect.catch((error) => Effect.logWarning("The running Run's Event has no labels to read; placing its card by its Item's
|
|
65
|
+
const labels = yield* payloadLabels(view.kind, event.payload).pipe(Effect.map(Option.some), Effect.catch((error) => Effect.logWarning("The running Run's Event has no labels to read; placing its card by its Item's state", { ...fields, eventId: event.id, errorTag: error._tag, message: error.message }).pipe(Effect.as(Option.none()))));
|
|
57
66
|
if (Option.isNone(labels))
|
|
58
67
|
return undefined;
|
|
59
68
|
const lane = labelLaneFor({ labels: labels.value }, lanes);
|
|
60
69
|
yield* Effect.logDebug(lane === undefined
|
|
61
|
-
? "No lane label when the card's running Run was triggered; placing it by its Item's
|
|
70
|
+
? "No lane label when the card's running Run was triggered; placing it by its Item's state"
|
|
62
71
|
: "Keeping the card in the lane its running Run was triggered from", { ...fields, lane: lane?.lane.name ?? "none" });
|
|
63
72
|
return lane;
|
|
64
73
|
});
|
|
65
74
|
/**
|
|
66
75
|
* Groups the repository's stored Items into Task cards and the cards into lanes. A card with a
|
|
67
|
-
* running Run
|
|
76
|
+
* running Run goes to the label lane that its current Item's labels match. If none matches, the
|
|
77
|
+
* card stays in the lane its Run's trigger matched, read from `runEvents`.
|
|
68
78
|
*/
|
|
69
79
|
export const projectBoard = Effect.fn("Board.project")((repository, repositoryLabels, storedItems, runs, cycleBlockedItems = [], itemLoops = new Map(), runUsage = new Map(), runEvents = new Map()) => Effect.gen(function* () {
|
|
70
80
|
yield* Effect.annotateCurrentSpan({ repository: repository.name });
|
|
@@ -99,10 +109,10 @@ export const projectBoard = Effect.fn("Board.project")((repository, repositoryLa
|
|
|
99
109
|
grouped.set(laneId(definition), []);
|
|
100
110
|
const unmatched = [];
|
|
101
111
|
for (const { view, running } of cards) {
|
|
102
|
-
const
|
|
112
|
+
const runningLane = running === undefined
|
|
103
113
|
? undefined
|
|
104
|
-
: yield*
|
|
105
|
-
const definition =
|
|
114
|
+
: yield* runningLaneFor(repository, view, running, lanes, runEvents);
|
|
115
|
+
const definition = runningLane ?? laneFor(view, lanes);
|
|
106
116
|
if (definition === undefined)
|
|
107
117
|
unmatched.push(view);
|
|
108
118
|
else
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../src/board/projection.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../src/board/projection.ts"],"names":[],"mappings":"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"}
|
package/dist/board/tasks.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { RunId } from "@usine/core/run";
|
|
|
5
5
|
import { BoardTaskView } from "../operator/board.ts";
|
|
6
6
|
import type { RunView } from "../operator/run.ts";
|
|
7
7
|
import { type RunUsageView } from "../operator/usage.ts";
|
|
8
|
-
import type
|
|
8
|
+
import { type ProjectedItem } from "./items.ts";
|
|
9
9
|
/** A Task card and the running Run that sets its lane. */
|
|
10
10
|
export interface TaskCard {
|
|
11
11
|
readonly view: BoardTaskView;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/board/tasks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/board/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAuC,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,YAAY,CAAC;AAYnE,0DAA0D;AAC1D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;CACvC;AAYD;;;GAGG;AACH,eAAO,MAAM,SAAS,eACR,cAAc,SACnB,aAAa,CAAC,aAAa,CAAC,QAC7B,aAAa,CAAC,OAAO,CAAC,qBACT,aAAa,CAAC,OAAO,CAAC,aAC9B,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC,YAC9C,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,KACzC,aAAa,CAAC,QAAQ,CAmGxB,CAAC"}
|
package/dist/board/tasks.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isSettledItemState } from "@usine/config/board";
|
|
1
2
|
import { canonicalRepositoryName } from "@usine/core/repository";
|
|
2
3
|
import { Array as EffectArray, Option, Order, Schema } from "effect";
|
|
3
4
|
import { BoardItemLink, BoardPullRequestView, BoardTaskView } from "../operator/board.js";
|
|
4
5
|
import { taskUsage } from "../operator/usage.js";
|
|
6
|
+
import { runNeedsAttention } from "./items.js";
|
|
5
7
|
const decodeUrl = Schema.decodeUnknownOption(Schema.URLFromString);
|
|
6
8
|
const itemLink = (item) => new BoardItemLink({
|
|
7
9
|
itemKey: item.itemKey,
|
|
@@ -9,6 +11,7 @@ const itemLink = (item) => new BoardItemLink({
|
|
|
9
11
|
number: item.number,
|
|
10
12
|
title: item.title,
|
|
11
13
|
url: item.url,
|
|
14
|
+
state: item.state,
|
|
12
15
|
});
|
|
13
16
|
/**
|
|
14
17
|
* Joins explicit issue and pull request relationships into Task cards, without stored Task
|
|
@@ -77,18 +80,19 @@ export const taskCards = (repository, items, runs, cycleBlockedItems, itemLoops,
|
|
|
77
80
|
const [pullRequest = null] = prs.flatMap(({ item: { number, url, state } }) => state === "pr-open" || state === "pr-merged"
|
|
78
81
|
? [new BoardPullRequestView({ number, url, state })]
|
|
79
82
|
: []);
|
|
80
|
-
const preferred = history.find(({ run }) => run
|
|
83
|
+
const preferred = history.find(({ item, run }) => runNeedsAttention(run, isSettledItemState(item.state))) ??
|
|
84
|
+
active ??
|
|
85
|
+
history[0];
|
|
81
86
|
const runs = history.map(({ run }) => run).reverse();
|
|
82
87
|
const view = new BoardTaskView({
|
|
83
88
|
...itemLink(current),
|
|
84
89
|
title: origin.item.title,
|
|
85
|
-
state: current.state,
|
|
86
90
|
labels: current.labels,
|
|
87
91
|
items: ordered.map(({ item }) => itemLink(item)),
|
|
88
92
|
pullRequest,
|
|
89
93
|
runs,
|
|
90
94
|
run: preferred?.run ?? null,
|
|
91
|
-
needsYou: history.some(({ run }) => run
|
|
95
|
+
needsYou: history.some(({ item, run }) => runNeedsAttention(run, isSettledItemState(item.state))) ||
|
|
92
96
|
members.some(({ item }) => blocked.has(item.itemKey)),
|
|
93
97
|
conflicting: current.conflicting,
|
|
94
98
|
loops: [...new Set(ordered.flatMap(({ item }) => itemLoops.get(item.itemKey) ?? []))],
|
package/dist/board/tasks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/board/tasks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/board/tasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CloudflareWorker, UsineConfig } from "@usine/config/factory";
|
|
2
|
+
import type { BrainSpec } from "@usine/core/agent/brain";
|
|
3
|
+
import type { InstanceDirectory } from "@usine/core/instance";
|
|
4
|
+
import { ExecutorOperationError, WorkerCapabilityError } from "@usine/executor";
|
|
5
|
+
import { type CloudflareExecutorOptions } from "@usine/executor/cloudflare";
|
|
6
|
+
import { Effect, Layer } from "effect";
|
|
7
|
+
/** Returns the Cloudflare Workers that `usine.yaml` declares, in declared order. */
|
|
8
|
+
export declare const cloudflareWorkersOf: (config: UsineConfig) => ReadonlyArray<CloudflareWorker>;
|
|
9
|
+
/**
|
|
10
|
+
* Returns how the executor reaches each declared Cloudflare Worker, reading the Access service
|
|
11
|
+
* tokens and provider keys at startup.
|
|
12
|
+
*/
|
|
13
|
+
export declare const cloudflareWorkerAddressesOf: (config: UsineConfig) => Effect.Effect<{
|
|
14
|
+
name: string & import("effect/Brand").Brand<"WorkerName">;
|
|
15
|
+
workerUrl: URL;
|
|
16
|
+
accessClientId: import("effect/Redacted").Redacted<string>;
|
|
17
|
+
accessClientSecret: import("effect/Redacted").Redacted<string>;
|
|
18
|
+
image: string & import("effect/Brand").Brand<"CloudflareImageName">;
|
|
19
|
+
instanceType: "basic" | "lite" | "standard-1" | "standard-2" | "standard-3" | "standard-4";
|
|
20
|
+
providers: {
|
|
21
|
+
provider: "anthropic" | "openai";
|
|
22
|
+
apiKey: import("effect/Redacted").Redacted<string>;
|
|
23
|
+
}[];
|
|
24
|
+
}[], WorkerCapabilityError, never>;
|
|
25
|
+
/**
|
|
26
|
+
* Provides the full engine and `WorkerStatus` for Cloudflare Workers. The executor reads its
|
|
27
|
+
* Workers' secrets once, so a reload adds back only a Worker that `usine up` started with.
|
|
28
|
+
* Requires `CredentialProvider`, `GitHubApp`, `TriggerCondition`, `AttemptRuntime` (built before
|
|
29
|
+
* the engine), `ClaudeLoginLock`, and `CurrentInstance`.
|
|
30
|
+
*/
|
|
31
|
+
export declare const layer: (instanceDirectory: InstanceDirectory, config: UsineConfig, brains: ReadonlyArray<BrainSpec>, options: CloudflareExecutorOptions) => Layer.Layer<import("@usine/engine/board-moves").BoardMoves | import("@usine/engine/cursor").CursorStore | import("@usine/engine/cycle-budget").CycleBudgets | import("@usine/engine/item-projection").ItemProjectionReader | import("@usine/engine/item-projection").ItemProjectionWriter | import("@usine/engine/loop-control").LoopControl | import("@usine/engine/repository-label-projection").RepositoryLabelProjectionReader | import("@usine/engine/repository-label-projection").RepositoryLabelProjectionWriter | import("@usine/engine/engine").RunEngine | import("@usine/engine/run-reader").RunReader | import("@usine/engine/step-executor").StepExecutor | import("@usine/engine/engine").UnconfirmedStops | import("./worker-status.ts").WorkerStatus, import("@usine/engine/engine").AttemptCancellationError | import("@usine/executor").CredentialUnavailableError | import("@usine/engine/engine").EnginePersistenceError | ExecutorOperationError | import("@usine/engine/engine").RunInterventionRejectedError | import("@usine/engine/engine").RunNotFoundError | import("@usine/engine/engine").RunOperationRejectedError | import("@usine/engine/engine").RunRetryBlockedError | import("./scheduler.ts").UnsupportedWorkerError | WorkerCapabilityError, import("@usine/durable/attempt-runtime").AttemptRuntime | import("@usine/agents/runtime").ClaudeLoginLock | import("@usine/executor").CredentialProvider | import("./current-instance/current-instance.ts").CurrentInstance | import("@usine/github/github-app").GitHubApp | import("@usine/engine/trigger-condition").TriggerCondition>;
|
|
32
|
+
//# sourceMappingURL=cloudflare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EACL,KAAK,yBAAyB,EAG/B,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAU,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAK/C,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,WAAY,WAAW,KAAG,aAAa,CAAC,gBAAgB,CACK,CAAC;AAmC9F;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;kCAIvC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,sBACG,iBAAiB,UAC5B,WAAW,UACX,aAAa,CAAC,SAAS,CAAC,WACvB,yBAAyB,giDA4BnC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { NodeServices } from "@effect/platform-node";
|
|
2
|
+
import { ExecutorOperationError, WorkerCapabilityError } from "@usine/executor";
|
|
3
|
+
import { layer as cloudflareExecutorLayer, } from "@usine/executor/cloudflare";
|
|
4
|
+
import { layer as localEngineLayer } from "@usine/engine/local-sqlite";
|
|
5
|
+
import { Config, Effect, Layer } from "effect";
|
|
6
|
+
import { externalHttpClientLayer } from "./http-client.js";
|
|
7
|
+
import { stepExecutorLayer } from "./scheduler.js";
|
|
8
|
+
import { withSshWorkerDiscard } from "./ssh.js";
|
|
9
|
+
/** Returns the Cloudflare Workers that `usine.yaml` declares, in declared order. */
|
|
10
|
+
export const cloudflareWorkersOf = (config) => config.workers.filter((worker) => worker.kind === "cloudflare");
|
|
11
|
+
/**
|
|
12
|
+
* Returns how the executor reaches `worker`, reading its Access service token and provider keys
|
|
13
|
+
* from the orchestrator's environment. Fails with a `credentials` capability error naming the
|
|
14
|
+
* first unset variable.
|
|
15
|
+
*/
|
|
16
|
+
const cloudflareWorkerAddressOf = (worker) => {
|
|
17
|
+
const variable = (name) => Config.Redacted(name).pipe(Effect.mapError((cause) => new WorkerCapabilityError({
|
|
18
|
+
worker: worker.name,
|
|
19
|
+
capability: "credentials",
|
|
20
|
+
message: `The orchestrator's environment has no ${name}, which cloudflare worker "${worker.name}" names in usine.yaml. Set it and restart usine up.`,
|
|
21
|
+
cause,
|
|
22
|
+
})));
|
|
23
|
+
return Effect.gen(function* () {
|
|
24
|
+
return {
|
|
25
|
+
name: worker.name,
|
|
26
|
+
workerUrl: worker.workerUrl,
|
|
27
|
+
accessClientId: yield* variable(worker.accessClientIdEnv),
|
|
28
|
+
accessClientSecret: yield* variable(worker.accessClientSecretEnv),
|
|
29
|
+
image: worker.image,
|
|
30
|
+
instanceType: worker.instanceType,
|
|
31
|
+
providers: yield* Effect.forEach(worker.llmProviders, ({ provider, apiKeyEnv }) => Effect.map(variable(apiKeyEnv), (apiKey) => ({ provider, apiKey }))),
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Returns how the executor reaches each declared Cloudflare Worker, reading the Access service
|
|
37
|
+
* tokens and provider keys at startup.
|
|
38
|
+
*/
|
|
39
|
+
export const cloudflareWorkerAddressesOf = Effect.fn("Orchestrator.cloudflareWorkerAddressesOf")(function* (config) {
|
|
40
|
+
return yield* Effect.forEach(cloudflareWorkersOf(config), cloudflareWorkerAddressOf);
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Provides the full engine and `WorkerStatus` for Cloudflare Workers. The executor reads its
|
|
44
|
+
* Workers' secrets once, so a reload adds back only a Worker that `usine up` started with.
|
|
45
|
+
* Requires `CredentialProvider`, `GitHubApp`, `TriggerCondition`, `AttemptRuntime` (built before
|
|
46
|
+
* the engine), `ClaudeLoginLock`, and `CurrentInstance`.
|
|
47
|
+
*/
|
|
48
|
+
export const layer = (instanceDirectory, config, brains, options) => {
|
|
49
|
+
const { workers } = options;
|
|
50
|
+
const executor = cloudflareExecutorLayer(options).pipe(Layer.provide(NodeServices.layer), Layer.provide(externalHttpClientLayer));
|
|
51
|
+
const started = new Set(workers.map(({ name }) => name));
|
|
52
|
+
// Discarding leftovers still removes the Codex homes on an SSH Worker that `usine.yaml` dropped.
|
|
53
|
+
const stepExecutor = withSshWorkerDiscard(instanceDirectory, (discard) => stepExecutorLayer(config, brains, {
|
|
54
|
+
discard,
|
|
55
|
+
workers: {
|
|
56
|
+
add: (worker) => started.has(worker.name)
|
|
57
|
+
? Effect.void
|
|
58
|
+
: Effect.fail(new ExecutorOperationError({
|
|
59
|
+
operation: "preflight",
|
|
60
|
+
message: `A reload can't add cloudflare worker "${worker.name}", whose secrets usine up reads when it starts. Restart usine up to add it.`,
|
|
61
|
+
cause: new Error(worker.name),
|
|
62
|
+
})),
|
|
63
|
+
remove: () => Effect.void,
|
|
64
|
+
},
|
|
65
|
+
})).pipe(Layer.provide(executor));
|
|
66
|
+
return localEngineLayer(instanceDirectory).pipe(Layer.provideMerge(stepExecutor));
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ import { Schema } from "effect";
|
|
|
7
7
|
* A `usine.yaml` block with changes that apply only when `usine up` starts, so a reload refuses
|
|
8
8
|
* them.
|
|
9
9
|
*/
|
|
10
|
-
export declare const RestartBlock: Schema.Literals<readonly ["workers", "webhooks"]>;
|
|
10
|
+
export declare const RestartBlock: Schema.Literals<readonly ["workers", "webhooks", "tracing"]>;
|
|
11
11
|
export type RestartBlock = typeof RestartBlock.Type;
|
|
12
12
|
/** A change to `usine.yaml` that only a restart of `usine up` applies. */
|
|
13
13
|
export interface RestartChange {
|
|
@@ -59,8 +59,8 @@ export declare const instanceReload: (running: LoadedInstance, loaded: LoadedIns
|
|
|
59
59
|
/** Returns the Agents of `loaded` whose brain no running Agent has. */
|
|
60
60
|
export declare const agentsWithNewBrains: (running: LoadedInstance, loaded: LoadedInstance) => ReadonlyArray<AgentFile>;
|
|
61
61
|
/**
|
|
62
|
-
* Returns the changes in `loaded` that only a restart applies: a changed `webhooks`
|
|
63
|
-
* changed `workers` block while no Worker runs Attempts, as in setup mode.
|
|
62
|
+
* Returns the changes in `loaded` that only a restart applies: a changed `webhooks` or `tracing`
|
|
63
|
+
* block, and a changed `workers` block while no Worker runs Attempts, as in setup mode.
|
|
64
64
|
*/
|
|
65
65
|
export declare const restartChanges: (running: LoadedInstance, loaded: LoadedInstance, workersRun: boolean) => ReadonlyArray<RestartChange>;
|
|
66
66
|
/** A Worker that a reload keeps with a new `max_concurrent_attempts`. */
|