@detergent-software/conductor 1.4.0-dev.9 → 1.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/CHANGELOG.md +102 -0
- package/README.md +12 -8
- package/build/cli.js +30 -10
- package/build/cli.js.map +1 -1
- package/build/client.js +35 -14
- package/build/client.js.map +1 -1
- package/build/config.js +7 -1
- package/build/config.js.map +1 -1
- package/build/daemon.js +16 -7
- package/build/daemon.js.map +1 -1
- package/build/help.js +15 -4
- package/build/help.js.map +1 -1
- package/build/inbox.js +24 -0
- package/build/inbox.js.map +1 -1
- package/build/manager.js +124 -10
- package/build/manager.js.map +1 -1
- package/build/paths.js +9 -1
- package/build/paths.js.map +1 -1
- package/build/project-check.js +1 -1
- package/build/project-check.js.map +1 -1
- package/build/pushover.js +11 -0
- package/build/pushover.js.map +1 -1
- package/build/rule-actions.js +17 -6
- package/build/rule-actions.js.map +1 -1
- package/build/rules-service.js +7 -2
- package/build/rules-service.js.map +1 -1
- package/build/rules.js +31 -14
- package/build/rules.js.map +1 -1
- package/build/scheduler.js +46 -1
- package/build/scheduler.js.map +1 -1
- package/build/server.js +35 -0
- package/build/server.js.map +1 -1
- package/build/spend.js +37 -4
- package/build/spend.js.map +1 -1
- package/build/store.js +64 -12
- package/build/store.js.map +1 -1
- package/mod/hooks/model.ts +29 -1
- package/mod/hooks/register.tsx +51 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,108 @@
|
|
|
2
2
|
|
|
3
3
|
Releases are tagged `conductor-v<version>` and staged on npm by `atk-conductor-release` (`pipelines/release-conductor.yml`), which writes each section below. `scripts/check-changelog.mjs` fails when a tag has no section here.
|
|
4
4
|
|
|
5
|
+
## [1.4.0](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.3.0&targetVersion=GTconductor-v1.4.0) (2026-10-15)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **conductor:** `conduct project check <name>` (ATK-490) ([393868c](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/393868c9d346f661c1fddb55788551cf98d66faf))
|
|
10
|
+
* **conductor:** a brief with the phase, the PR and the comments (ATK-456) ([cfd0f11](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/cfd0f11e8881f43eb0d4d4d4f93800738f81d57f))
|
|
11
|
+
* **conductor:** a GitHub Issues tracker kind (ATK-458) ([6028a13](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/6028a136cc39e1ed51072571629e68dead466d35))
|
|
12
|
+
* **conductor:** compact a parked worker whose context grew too big ([4259ec2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4259ec2d570e4dcc13760a8941a219cb60968302))
|
|
13
|
+
* **conductor:** conduct file, in-process notes and the PR reference (ATK-456) ([0b3311b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/0b3311b7ec877cc7846d1c5607c35c653c43b58d))
|
|
14
|
+
* **conductor:** conduct project remove <name> (ATK-499) ([8ec1fda](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8ec1fda3ee10a613840d2e96dc847fbbd416fd6a))
|
|
15
|
+
* **conductor:** conduct runs and conduct wait merge (ATK-455) ([e46a331](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e46a331491ec40d01da860306f910e62b59bdf67))
|
|
16
|
+
* **conductor:** easier-to-scan conduct help (ATK-478) ([8c34660](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8c34660ba7ecbac439312e317eb359767efedb21))
|
|
17
|
+
* **conductor:** phase notes move the tracker state (ATK-456) ([6c93b91](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/6c93b91fc7f0487c41ddfe357248a3bfdc97a463))
|
|
18
|
+
* **conductor:** read a branch's tip and a repo's pipelines (ATK-490) ([40af3c1](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/40af3c1362b16b07ea3c135a69880adcbdb36626))
|
|
19
|
+
* **conductor:** take the whole profile from disk, not only its rules ([b22705a](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b22705ad2adb2bb6e886e5f99e011ccc0767b5c0))
|
|
20
|
+
* **conductor:** tracker writes for Linear and ADO Boards (ATK-456) ([b86911e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b86911ea478c8c4da90b6b5f8cf8586094b1fc4a))
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **conductor:** address review of the context compaction ([51ebe19](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/51ebe1937f83e6cd448b028afac52b6ecb585eef))
|
|
25
|
+
* **conductor:** approve and revise from the console through conduct (ATK-508) ([f3f1785](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f3f1785136e152ade05cad8cab8e938c5eefef08))
|
|
26
|
+
* **conductor:** attach from the console by worker id (ATK-507) ([873dc09](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/873dc09e1aeab9b2006aa7fb157fd1be1f17c20b))
|
|
27
|
+
* **conductor:** attach from the console through conduct attach (ATK-507) ([f51bb7c](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f51bb7cb5bc15a5b6a712d8f13d4438b4acb72cb))
|
|
28
|
+
* **conductor:** back off a partial retire and raise it once (ATK-496) ([7fe082a](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7fe082a0b211b95eddc5e7fd93ced4bd3ff0a925))
|
|
29
|
+
* **conductor:** guard the brief found in review (ATK-456) ([5d7debe](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/5d7debe7cd670bc20798f7d55438bfc24d1095be))
|
|
30
|
+
* **conductor:** guard the note and follow-up paths found in review (ATK-456) ([6d881da](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/6d881daca5d5c5103943fba6a1560f7ae4db36de)), closes [AB#12](https://emergentcode.visualstudio.com/Emergent%20Software/_workitems/edit/12)
|
|
31
|
+
* **conductor:** guard the red-checks inbox paths found in review (ATK-468) ([c567a4d](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/c567a4d63192c1e7b6c630e4292846b4a78166e8))
|
|
32
|
+
* **conductor:** guard the tracker writes found in review (ATK-456) ([50c9987](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/50c998778997f5eb2b914339ed3c58f726fde83c))
|
|
33
|
+
* **conductor:** hold merges on red checks by item, not by worker (ATK-468) ([78c16ea](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/78c16ea33c78e6b8dd1d94b5b2f5d58a3c950b5a))
|
|
34
|
+
* **conductor:** keep a failed atk audit on one check line (ATK-490) ([ae78d30](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/ae78d3012bb68d178f85772f6303f5c1b97d4e32))
|
|
35
|
+
* **conductor:** keep a project's name, and say only what is in force ([51efeb0](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/51efeb0977e005d0daa84c67f6590c7817339b87))
|
|
36
|
+
* **conductor:** keep the client's timeout detail and content-length ([b7e6943](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b7e69438cf8c04f5589883e68359db5f2d5868da))
|
|
37
|
+
* **conductor:** leave a worker's context unknown after a compaction ([e6c79d3](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e6c79d3b48677620403bd9cc32bc1a9599d90b19))
|
|
38
|
+
* **conductor:** only ~/.conductor falls back to the Pushover credential (ATK-506) ([a87117b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/a87117b476b78588f83f5215633422c823f37085))
|
|
39
|
+
* **conductor:** open an inbox link once from the terminal pane (ATK-494) ([1634517](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/16345173815330b2cea5361e964ebdf4f2921c15))
|
|
40
|
+
* **conductor:** open only web links, on Windows only (ATK-494) ([8d2f608](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8d2f6084cc99900b2c012cddbb5e287b488f1213))
|
|
41
|
+
* **conductor:** open Windows Terminal tabs unhidden (ATK-469) ([6f3a69a](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/6f3a69aec82331727367075fe5b622a5f7c57f12))
|
|
42
|
+
* **conductor:** project check review fixes (ATK-490) ([7518d91](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7518d9155066c4e7f7b301b4bee703b3d3a4a0e8))
|
|
43
|
+
* **conductor:** raise red PR checks as a failure and hold the merge (ATK-468) ([5897adb](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/5897adb9033b89d575fcd26f391c012637dd602b))
|
|
44
|
+
* **conductor:** read the default home per call, and pin env keys (ATK-506) ([e83019e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e83019e81875e4783d7c9b5a3ad82e55e754a658))
|
|
45
|
+
* **conductor:** refuse an unreadable stored profile's bad file once ([4e100f1](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4e100f14fb2a9ed36b701cdb8f2a36d91bacacb7))
|
|
46
|
+
* **conductor:** rename by remove-then-add, and guard removal races (ATK-499) ([9ad7289](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/9ad72894e5ac58d0bc5de307fd36892909c59d7c))
|
|
47
|
+
* **conductor:** review fixes for conduct runs and merge waits (ATK-455) ([b1ff160](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b1ff1602962f3bc902f33af57a23b06df081c5df))
|
|
48
|
+
* **conductor:** review fixes for the github-issues tracker (ATK-458) ([79abe60](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/79abe602f80138714098a61821d16dfc015787c0))
|
|
49
|
+
* **conductor:** say when an approve may have posted, on every path (ATK-508) ([8278f23](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8278f23a6adfe6a73904674802c6026a54bba983))
|
|
50
|
+
* **conductor:** send API calls without undici's 300 s timeouts ([61db3e4](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/61db3e4418b6f7525105197e9503223bf7a83719))
|
|
51
|
+
|
|
52
|
+
### Refactoring
|
|
53
|
+
|
|
54
|
+
* **conductor:** help examples a shell can run, review fixes (ATK-478) ([f1be136](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f1be1361a1874a26a91dcfafd42626e25c307943)), closes [#12](https://emergentcode.visualstudio.com/Emergent%20Software/_workitems/edit/12) [#-word](https://emergentcode.visualstudio.com/Emergent%20Software/_workitems/edit/-word)
|
|
55
|
+
* **conductor:** the brief names its PR with the notes' helper (ATK-456) ([7b3f009](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7b3f009715cce46a4ac02697bdca3680ab9f78f5))
|
|
56
|
+
|
|
57
|
+
## [1.4.0-dev.15](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.14&targetVersion=GTconductor-v1.4.0-dev.15) (2026-10-15)
|
|
58
|
+
|
|
59
|
+
### Bug Fixes
|
|
60
|
+
|
|
61
|
+
* **conductor:** approve and revise from the console through conduct (ATK-508) ([f3f1785](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f3f1785136e152ade05cad8cab8e938c5eefef08))
|
|
62
|
+
* **conductor:** say when an approve may have posted, on every path (ATK-508) ([8278f23](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8278f23a6adfe6a73904674802c6026a54bba983))
|
|
63
|
+
|
|
64
|
+
## [1.4.0-dev.14](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.13&targetVersion=GTconductor-v1.4.0-dev.14) (2026-10-15)
|
|
65
|
+
|
|
66
|
+
### Bug Fixes
|
|
67
|
+
|
|
68
|
+
* **conductor:** attach from the console by worker id (ATK-507) ([873dc09](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/873dc09e1aeab9b2006aa7fb157fd1be1f17c20b))
|
|
69
|
+
* **conductor:** attach from the console through conduct attach (ATK-507) ([f51bb7c](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f51bb7cb5bc15a5b6a712d8f13d4438b4acb72cb))
|
|
70
|
+
|
|
71
|
+
## [1.4.0-dev.13](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.12&targetVersion=GTconductor-v1.4.0-dev.13) (2026-10-15)
|
|
72
|
+
|
|
73
|
+
### Bug Fixes
|
|
74
|
+
|
|
75
|
+
* **conductor:** keep the client's timeout detail and content-length ([b7e6943](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b7e69438cf8c04f5589883e68359db5f2d5868da))
|
|
76
|
+
* **conductor:** only ~/.conductor falls back to the Pushover credential (ATK-506) ([a87117b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/a87117b476b78588f83f5215633422c823f37085))
|
|
77
|
+
* **conductor:** read the default home per call, and pin env keys (ATK-506) ([e83019e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e83019e81875e4783d7c9b5a3ad82e55e754a658))
|
|
78
|
+
* **conductor:** send API calls without undici's 300 s timeouts ([61db3e4](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/61db3e4418b6f7525105197e9503223bf7a83719))
|
|
79
|
+
|
|
80
|
+
## [1.4.0-dev.12](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.11&targetVersion=GTconductor-v1.4.0-dev.12) (2026-10-15)
|
|
81
|
+
|
|
82
|
+
### Features
|
|
83
|
+
|
|
84
|
+
* **conductor:** conduct project remove <name> (ATK-499) ([8ec1fda](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8ec1fda3ee10a613840d2e96dc847fbbd416fd6a))
|
|
85
|
+
|
|
86
|
+
### Bug Fixes
|
|
87
|
+
|
|
88
|
+
* **conductor:** rename by remove-then-add, and guard removal races (ATK-499) ([9ad7289](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/9ad72894e5ac58d0bc5de307fd36892909c59d7c))
|
|
89
|
+
|
|
90
|
+
## [1.4.0-dev.11](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.10&targetVersion=GTconductor-v1.4.0-dev.11) (2026-10-15)
|
|
91
|
+
|
|
92
|
+
### Features
|
|
93
|
+
|
|
94
|
+
* **conductor:** compact a parked worker whose context grew too big ([4259ec2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4259ec2d570e4dcc13760a8941a219cb60968302))
|
|
95
|
+
|
|
96
|
+
### Bug Fixes
|
|
97
|
+
|
|
98
|
+
* **conductor:** address review of the context compaction ([51ebe19](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/51ebe1937f83e6cd448b028afac52b6ecb585eef))
|
|
99
|
+
* **conductor:** leave a worker's context unknown after a compaction ([e6c79d3](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e6c79d3b48677620403bd9cc32bc1a9599d90b19))
|
|
100
|
+
|
|
101
|
+
## [1.4.0-dev.10](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.9&targetVersion=GTconductor-v1.4.0-dev.10) (2026-10-15)
|
|
102
|
+
|
|
103
|
+
### Bug Fixes
|
|
104
|
+
|
|
105
|
+
* **conductor:** back off a partial retire and raise it once (ATK-496) ([7fe082a](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7fe082a0b211b95eddc5e7fd93ced4bd3ff0a925))
|
|
106
|
+
|
|
5
107
|
## [1.4.0-dev.9](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.8&targetVersion=GTconductor-v1.4.0-dev.9) (2026-10-15)
|
|
6
108
|
|
|
7
109
|
### Features
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ conduct supervise [--fail-fast] run the daemon and restart it when it dies (se
|
|
|
12
12
|
conduct status [--json] the daemon's pid, port, projects, workers and held locks
|
|
13
13
|
conduct project add <path> register the project whose checkout holds .claude/conductor.json
|
|
14
14
|
conduct project list [--json] the registered projects
|
|
15
|
+
conduct project remove <name> drop a registration (a renamed project's old name), resolving its inbox items
|
|
15
16
|
conduct project check <name> [--no-worktree] [--json]
|
|
16
17
|
whether the project can be conducted, before a launch finds out
|
|
17
18
|
conduct launch <item> start a worker: worktree, background session, tab
|
|
@@ -94,12 +95,14 @@ The session manager owns the `claude --bg` sessions it launches, and only those:
|
|
|
94
95
|
- **launch:** the worktree comes first, through the project's `.claude/hooks/worktree-hooks.mjs`. In a repo with `.atk-lock.json`, the profile's work and verify skills must match the lockfile (`atk install` fixes them when it can). Then `claude --bg --worktree <name> -n <name> --disallowedTools EnterPlanMode ExitPlanMode --permission-mode auto -- <prompt>` runs from the main checkout. A profile with `"workers": { "tab": true }` also gets a Windows Terminal tab attached to the session.
|
|
95
96
|
- **park:** a worker with a wait (`POST /waits`) is stopped once `claude agents` shows its turn has ended. Its status becomes `parked`, and its tab closes.
|
|
96
97
|
- **resume:** when a wait resolves, the daemon lets the stop settle (`sessions.settleSeconds`). It checks the process has exited, then runs `claude --bg --resume <session> -- "[conductor] event: …"`. The session keeps its name, model, permission mode and disallowed tools. A resume of a session that still runs starts a copy; the daemon stops and removes the copy, then tries again.
|
|
98
|
+
- **compact:** a parked worker with no resolved wait, whose context the rules find too big (the default `compact-on-park`: past `sessions.compactTokens`, 300k), is resumed with `/compact <what to keep>` once its stop has settled (`worker.compacting`). It runs until its transcript shows the compaction's boundary (`worker.compacted`, with the context before it and the conversation it kept), then parks again on its waits, which resume it as usual. Each turn of a session re-reads its whole context from cache, so a worker left to grow pays for it every turn (ATK-454 reached 883k). A resume that starts a copy is tried again on a later sweep (`worker.compact-deferred`), as a resume is. A compaction whose resume fails, or whose turn ends with no boundary 10 minutes on, logs `worker.compact-failed`, which raises a `context` item in the inbox. The worker still parks, and the daemon doesn't try it again until it restarts or the session compacts some other way: any boundary in its transcript, Claude Code's own included, logs `worker.compacted` and resolves that item. A worker whose context is unknown is never compacted, so even a rule with no threshold can't compact it on every park.
|
|
97
99
|
- **retire:** refuses uncommitted or unpushed work, and a worker whose turn still runs, unless `--force`. It never retires a session that is still starting. Then it stops the session, removes the worktree and runs `claude rm`. If the stop fails, nothing is removed.
|
|
98
100
|
- **restarts:** a launch the daemon was stopped in adopts its session if one was started, and otherwise fails and removes its clean worktree.
|
|
99
101
|
- **attach:** a parked worker is resumed with no event text, so it wakes idle and keeps running when its tab closes. An attached worker isn't parked until it registers a new wait, or until its turn has been over `sessions.idleMinutes` (`worker.detached`). The daemon can't see the tab, so this also delivers a wait that resolved while it was attached.
|
|
100
102
|
- **park failures:** a session whose process outlives `claude stop` by 30 s logs one `worker.park-failed` per spell, which raises a `stall` in the inbox (see Inbox), and the park is tried again every 5 minutes.
|
|
101
103
|
- **stalls:** a permission prompt open `sessions.permissionMinutes` (5), or idle `sessions.idleMinutes` (20) with no wait, logs one `worker.stalled` event per spell, and `worker.stall-cleared` when that spell ends. `claude agents` names no command, so a permission stall reads the session's transcripts (its own and its subagents') for the call still open: its tool, the command or path, the subagent that made it, and the auto-mode classifier denials just before it. The inbox item and its push say them, for example "ATK-297 has waited 5 min on a permission prompt: Bash `git -C … diff …` in subagent general-purpose (Review ATK-297 diff vs plan), after 2 auto-mode denials". Auto mode asks a person after 3 classifier denials in a row (20 in a session), even in a background subagent, so that count is usually why the prompt came. An open item of a Handoff kind with nothing to watch (`abandoned`, `blocked`, `failed`, `human`, `jason`, `scope`) on the worker, or on its item with no worker, counts as a wait: its idle clock starts once that item resolves. A `merge`, `npm-otp` or `plan` item doesn't: those come with a wait, so a worker idle with only the item still stalls.
|
|
102
104
|
- **spend:** tokens from the session's transcript and its subagents' (`~/.claude/projects/*/<session>.jsonl`), each message counted once, go into `spend` per worker and local day. It keeps input, output, cache read and cache write; `tokens` is their sum. A worker whose tokens in all, not counting cache reads, pass `inbox.runawayTokens` logs `worker.runaway` once per daemon run. Cache reads are left out because they are nearly all of a session's tokens (97% on ATK-312) at a tenth of the price. The spend `conduct status`, `GET /fleet` and the console's fleet pane show (`spend.today`, `spend.total`) and the `spend.*` rule fields count the same tokens: input, output and cache writes. Status shows the worker's cache reads beside them, labelled (`spend 417k today, 1.0M total, 25M cache reads`; `spend.cacheRead` in `GET /fleet`).
|
|
105
|
+
- **context:** the session's context is its main transcript's newest model call: input, cache read, cache write and output. Subagents have contexts of their own, so theirs don't count. After a compaction it is unknown until the next call: the boundary counts only the conversation it kept, and the call adds the system prompt and tools back (a probe that kept 2.8k next read 68k). Status shows it as `context 312k` (`context` in `GET /fleet`), and the rules read it as `worker.context`.
|
|
103
106
|
- **probes:** `--probe` names the worktree and session `probe-<item>` and gives it a prompt that works no issue, so tests stay out of the live fleet.
|
|
104
107
|
|
|
105
108
|
Worker statuses: `launching`, `running`, `parked`, `stopped-unexpectedly`, `failed`, `exited`. Every step is an event (`worker.launched`, `worker.parked`, `worker.resumed`, …) on `GET /events`.
|
|
@@ -218,6 +221,7 @@ An item resolves itself when its condition is seen, and records what resolved it
|
|
|
218
221
|
| `conflict` | `pr.conflict-stuck`: a watched PR has conflicted with its base for `watchers.conflictMinutes` (30) since its worker was woken for it, and the wake never reached the worker or a pass still sees the conflict; with `conduct attach` and the PR link | the PR merging cleanly again, merged or abandoned (`pr.conflict-cleared`), its retirement, or a daemon restart (the spell starts over) |
|
|
219
222
|
| `login` | `provider.login-failed`: a project's provider login stopped working (see Accounts), once per provider and account however many projects and calls find it, with the command that signs in again | the login working again, found by the daemon's re-probe, any call, or a `login` wait (`provider.login-restored`), `conduct done`, or a daemon restart (the spell starts over) |
|
|
220
223
|
| `runaway` | a worker passing `inbox.runawayTokens` (1.5M) input, output and cache-write tokens in all, once per worker | `conduct done`, or its retirement |
|
|
224
|
+
| `context` | `worker.compact-failed`: the daemon couldn't compact a worker whose context passed the `compact-on-park` rule (see Workers), while one is open, with `conduct attach` (run `/compact` in it) and `conduct retire` | the session compacting (`worker.compacted`), `conduct done`, or its retirement |
|
|
221
225
|
| `ready-to-release`, `queue-drained` | the scheduler's default rules (see Scheduler) | `conduct done` |
|
|
222
226
|
| `triage` | the scheduler's default rule `triage`: an item with the profile's triage label, once per item (see Scheduler) | a read of its project that no longer shows the label on the item (removed, or the item closed or left the queue), in `scheduler.mode` `on`; or `conduct done`, after which it is raised again the next day while the label stays. Once resolved, a label put back raises it again at once |
|
|
223
227
|
| `console` | the console's finding on an escalation, with `conduct escalations done <id> --ask` (see Console) | `conduct done` |
|
|
@@ -242,7 +246,7 @@ If the tracker refuses the comment, nothing is resolved. Only a plan takes appro
|
|
|
242
246
|
The notifier routes each new inbox item to the phone, on two channels:
|
|
243
247
|
|
|
244
248
|
- **Remote Control:** the daemon appends `notify.requested` (`channel: "remote-control"`), and the conductor mod in the console pushes it with PushNotification. It reaches the phone only while the console is connected to Remote Control (see Mod). On Claude Code 2.1.296 these pushes are reported sent but never reach the phone (ATK-359), so the built-in `default` and `focus` also send to Pushover until a Claude Code release fixes it.
|
|
245
|
-
- **Pushover:** sent from the daemon. The keys are `PUSHOVER_TOKEN` and `PUSHOVER_USER`, or else the Windows generic credential `atk-conductor-pushover` (`notify.pushover.credential`): its **user name is your Pushover user key** and its **password is the application's API token**. Save it with `cmdkey /generic:atk-conductor-pushover /user:<user key> /pass:<app token>` or in Credential Manager. A credential saved while the daemon runs is read on the next push. The keys are never logged or stored.
|
|
249
|
+
- **Pushover:** sent from the daemon. The keys are `PUSHOVER_TOKEN` and `PUSHOVER_USER`, or else the Windows generic credential `atk-conductor-pushover` (`notify.pushover.credential`): its **user name is your Pushover user key** and its **password is the application's API token**. Save it with `cmdkey /generic:atk-conductor-pushover /user:<user key> /pass:<app token>` or in Credential Manager. A credential saved while the daemon runs is read on the next push. The keys are never logged or stored. Only `~/.conductor` falls back to `atk-conductor-pushover`: a daemon on another `CONDUCTOR_HOME` (a scratch home for a test) reads a credential only when its `config.json` names `notify.pushover.credential`, so it pushes to your phone only with that or with `PUSHOVER_TOKEN` and `PUSHOVER_USER` in its environment (ATK-506).
|
|
246
250
|
|
|
247
251
|
**Profiles** pick the channels. A profile is a list of rules, and the first rule that matches an item says where it goes; when none matches, the item stays in the inbox, unpushed. Three are built in, and `notify.profiles` in `config.json` replaces one by name or adds more:
|
|
248
252
|
|
|
@@ -289,11 +293,11 @@ Rules say what the conductor does when something happens (design note, section 9
|
|
|
289
293
|
]
|
|
290
294
|
```
|
|
291
295
|
|
|
292
|
-
- **Sources, in order:** the item's project's `rules` (in its `.claude/conductor.json`, changed by PR), then `~/.conductor/rules.json`, then the built-in defaults: conduct-atk's behavior written as rules (`launch`, `retire-closed`, `park-on-wait`, `resume-on-wait`, `runaway`, `daily-cap`, `ready-to-release`, `queue-drained`, `triage`, which raises a new item that waits on the profile's triage label in the inbox, `conductor-handoff`, which escalates a worker's `Handoff · conductor` note to the console, and `hold-pre-start-handoff`, which adds the human label when a worker hands off as `human`, `jason`, `blocked` or `scope` before its `Started` note, so the scheduler never launches work no worker can do again; remove the label if the item should be picked later). A rule with an `id` replaces the rule with the same id further down, so a file holds only the differences.
|
|
293
|
-
- **when:** `and`, `or`, `not` and parentheses over comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`, `in [a, b]`, `<label> in item.labels`, `Started in item.phases`) and events. An event rule fires when one of its events happens: `note`, `plan`, `wait.resolved`, `run.failed` (a failed launch, a session that stopped unexpectedly, or a PR's red checks: `pr.checks-failed`), `worker.idle`, `worker.exited`, `worker.permission`, `worker.waiting`, `inbox.raised`, `pr.comment`, `item.unblocked`, `item.closed`, `provider.login-failed` (a project's login lost, once per spell; see Accounts), `queue.pick`, `queue.drained`, `queue.ready-to-release`, `queue.triage`. A rule without one is a state rule, checked on each scheduler tick against each wait, inbox item, worker or project, or once for the machine, by the fields it reads. Fields: `project`, `item.id/area/labels/tier/priority`, `item.phases` (the phases of every note the item has had, the one being posted too), `plan.tier/landing`, `note.phase/kind`, `wait.kind/age`, `inbox.kind/age`, `worker.retries/phase`, `spend.worker/item/project/today` (input, output and cache-write tokens, without cache reads: the worker's and the item's in all, the project's and the machine's today), `time`, `day`, `notify.profile`. Values carry units: `15m`, `2h`, `500k`, `2M`, `22:00`, `saturday`.
|
|
296
|
+
- **Sources, in order:** the item's project's `rules` (in its `.claude/conductor.json`, changed by PR), then `~/.conductor/rules.json`, then the built-in defaults: conduct-atk's behavior written as rules (`launch`, `retire-closed`, `park-on-wait`, `resume-on-wait`, `compact-on-park`, `runaway`, `daily-cap`, `ready-to-release`, `queue-drained`, `triage`, which raises a new item that waits on the profile's triage label in the inbox, `conductor-handoff`, which escalates a worker's `Handoff · conductor` note to the console, and `hold-pre-start-handoff`, which adds the human label when a worker hands off as `human`, `jason`, `blocked` or `scope` before its `Started` note, so the scheduler never launches work no worker can do again; remove the label if the item should be picked later). A rule with an `id` replaces the rule with the same id further down, so a file holds only the differences.
|
|
297
|
+
- **when:** `and`, `or`, `not` and parentheses over comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`, `in [a, b]`, `<label> in item.labels`, `Started in item.phases`) and events. An event rule fires when one of its events happens: `note`, `plan`, `wait.resolved`, `run.failed` (a failed launch, a session that stopped unexpectedly, or a PR's red checks: `pr.checks-failed`), `worker.idle`, `worker.exited`, `worker.permission`, `worker.waiting`, `worker.parked` (a park; the session manager also asks it of each parked worker on a sweep, for `compact`), `inbox.raised`, `pr.comment`, `item.unblocked`, `item.closed`, `provider.login-failed` (a project's login lost, once per spell; see Accounts), `queue.pick`, `queue.drained`, `queue.ready-to-release`, `queue.triage`. A rule without one is a state rule, checked on each scheduler tick against each wait, inbox item, worker or project, or once for the machine, by the fields it reads. Fields: `project`, `item.id/area/labels/tier/priority`, `item.phases` (the phases of every note the item has had, the one being posted too), `plan.tier/landing`, `note.phase/kind`, `wait.kind/age`, `inbox.kind/age`, `worker.retries/phase`, `worker.context` (the session's context in tokens, see Workers), `spend.worker/item/project/today` (input, output and cache-write tokens, without cache reads: the worker's and the item's in all, the project's and the machine's today), `time`, `day`, `notify.profile`. Values carry units: `15m`, `2h`, `500k`, `2M`, `22:00`, `saturday`.
|
|
294
298
|
- **do:** `launch`, `retire`, `park`, `resume worker`, `retry`, `pause launches [in <project>] [until <when> | for <duration>]`, `pause all launches …`, `unpause launches`, `focus [<project>] weight|slots <n> until <when>`, `auto-approve now`, `auto-approve after <duration>`, `inbox <kind> ["<text>"]`, `remind jason`, `notify <channel> ["<text>"]`, `notify profile <name> [until <when>]`, `label add|remove human|triage|shipped-open`, `flag shipped-open`, `escalate console "<ask>"`, `none`. One string, or a list.
|
|
295
|
-
- **Guardrails:** rules act inside Area locks, slots, quiet hours and config.json's limits. A rule that would go past one is refused when it loads: `focus … slots` over `scheduler.slots`, an override with no end or one over 14 days, `retire` on anything but `item.closed`, `launch` outside `queue.pick` / `item.unblocked`, `park` outside `worker.waiting`, `resume` outside `wait.resolved` / `pr.comment`, `retry` outside `run.failed`. `merge`, `push`, `publish`, `approve`, `delete` and `run` aren't in the vocabulary: rules never merge, push, publish, approve prod, delete or run commands, and the daemon writes to a code host only when the owning worker asks (see Writes).
|
|
296
|
-
- **Reload:** saving `rules.json` or a project's profile reloads its rules. A profile save, or a profile changed while the daemon was down, is taken whole: the daemon stores it and logs `project.updated` with the fields that `changed`, and the scheduler, watchers and the rest use it from then on. A profile that renames the project is refused: a project keeps the name it was registered under. A file the conductor refuses leaves its last good rules (for a profile, the whole last good profile) in force, logs `rules.rejected` and raises a `failed` inbox item until it loads again (`rules.loaded`), including after a restart. `conduct project add` refuses a profile whose rules don't load, with or without the daemon.
|
|
299
|
+
- **Guardrails:** rules act inside Area locks, slots, quiet hours and config.json's limits. A rule that would go past one is refused when it loads: `focus … slots` over `scheduler.slots`, an override with no end or one over 14 days, `retire` on anything but `item.closed`, `launch` outside `queue.pick` / `item.unblocked`, `park` outside `worker.waiting`, `resume` outside `wait.resolved` / `pr.comment`, `compact` outside `worker.parked`, `retry` outside `run.failed`. `merge`, `push`, `publish`, `approve`, `delete` and `run` aren't in the vocabulary: rules never merge, push, publish, approve prod, delete or run commands, and the daemon writes to a code host only when the owning worker asks (see Writes).
|
|
300
|
+
- **Reload:** saving `rules.json` or a project's profile reloads its rules. A profile save, or a profile changed while the daemon was down, is taken whole: the daemon stores it and logs `project.updated` with the fields that `changed`, and the scheduler, watchers and the rest use it from then on. A profile that renames the project is refused: a project keeps the name it was registered under. To rename one, `conduct project remove <old name>`, then `conduct project add` it under the new name (in that order: with both registered, both would read the same queue and could launch the same item). The remove drops the old registration and its queue read, resolves its open inbox items (the refusal's among them) and logs `project.removed`, on which the daemon drops its rules and its watch of the file. It is refused while the project has workers in flight, and works without the daemon. A file the conductor refuses leaves its last good rules (for a profile, the whole last good profile) in force, logs `rules.rejected` and raises a `failed` inbox item until it loads again (`rules.loaded`), including after a restart. `conduct project add` refuses a profile whose rules don't load, with or without the daemon.
|
|
297
301
|
- **Checking:** `conduct rules` lists the rules in force (`project.atk:#2`, `machine:runaway`, `default:launch`) and what replaces what. `conduct rules test <file>` dry-runs a sample, such as `{ "event": "pr.comment", "project": "atk", "fields": { "wait.kind": "merge", "wait.age": "3h" } }` (leave out `event` for a state rule), and shows what fires, each action's guardrail, and why every other rule of the trigger didn't. Both read the files directly when the daemon isn't running.
|
|
298
302
|
|
|
299
303
|
- **Why:** every action the conductor takes is an event that names its rule, directly (`rule`) or through the event that caused it (`data.cause`). `conduct why <event id>` follows that chain back and shows the rule as it fired; `conduct why ATK-315` does it for the item's newest actions.
|
|
@@ -334,7 +338,7 @@ The scheduler moves conduct-atk's tick into the daemon (design note, section 8).
|
|
|
334
338
|
- **Release waits:** a Linear profile's `tracker.releaseWait: { "label": "waits-release", "shipped": "release-atk · Shipped" }` marks work that needs a dependency released, not only merged (a CLI change on npm `latest`, a prod deploy). An item with that label waits, once its open blockers have closed, until every Done blocker has a comment whose first line starts with the `shipped` text, under any heading or bold (`waits for ATK-391 to ship`), and starts on the first tick after. A comment that only quotes the text doesn't count. A blocker still open in another team waits the same way; a canceled one holds nothing. A blocker that shipped before release-atk wrote its notes (2026-10-06) holds until you add the note or remove the label. With the label and no blocker at all it waits until you act (`labelled waits-release with no blocker to wait on`), for something outside the tracker such as a Claude Code release. Such an item doesn't keep its tracker project from `queue.ready-to-release`, since the release is what it waits for. The read asks about the Done blockers in one more request, only when a labelled item has one. Without the setting, a Done blocker never holds anything.
|
|
335
339
|
- **Slots:** `scheduler.slots` (4) across projects and the profile's `slots` per project (ATK's is 4), or `conduct focus --slots`, never more than the global ones. Slots count running workers only: a parked worker keeps its Area and frees its slot.
|
|
336
340
|
- **Holds:** quiet hours and `conduct pause` turn picks into waits that say why.
|
|
337
|
-
3. **Act.** It runs the rules on what it found: `queue.pick` → `launch` (default `launch`), `item.closed` → `retire` (default `retire-closed`; a refused retire raises an inbox item once, except one refused while the worker's turn runs or its session starts, which is tried again each tick and raised only after 15 minutes), `queue.drained` and `queue.ready-to-release` → an inbox item once a day, `queue.triage` → a `triage` inbox item per item (default `triage`), resolved once the label goes. Each action is a `rule.fired` event, then `rule.done` or `rule.failed`. A launch that failed isn't tried again while its inbox item is open.
|
|
341
|
+
3. **Act.** It runs the rules on what it found: `queue.pick` → `launch` (default `launch`), `item.closed` → `retire` (default `retire-closed`; a refused retire raises an inbox item once, except one refused while the worker's turn runs or its session starts, which is tried again each tick and raised only after 15 minutes; a partial retire is tried again after 15 minutes, and after 3 in a row it raises one inbox item that names the failed steps and any worktree left on disk, which the conductor never deletes, and is tried once a day), `queue.drained` and `queue.ready-to-release` → an inbox item once a day, `queue.triage` → a `triage` inbox item per item (default `triage`), resolved once the label goes. Each action is a `rule.fired` event, then `rule.done` or `rule.failed`. A launch that failed isn't tried again while its inbox item is open.
|
|
338
342
|
|
|
339
343
|
`scheduler.mode` decides how far it goes:
|
|
340
344
|
- **`on`** (the default): it acts.
|
|
@@ -543,7 +547,7 @@ Everything lives in `~/.conductor` (`CONDUCTOR_HOME` overrides it):
|
|
|
543
547
|
|
|
544
548
|
| File | Holds |
|
|
545
549
|
|---|---|
|
|
546
|
-
| `config.json` | machine settings, JSONC (comments allowed), validated at startup; `inbox.runawayTokens` (1500000, the `runaway` rule's number); `notify` and `quietHours` (see Notifications); `scheduler` holds `mode` (`on`, `observe` or `off`), `slots` (4, across projects, and the most any rule can give one project), `dailyTokens` (20000000, the `daily-cap` rule's number) and `tickSeconds` (120); `sessions` holds `pollSeconds` (10), `settleSeconds` (5), `permissionMinutes` (5), `idleMinutes` (20); `watchers` holds `ado`, `github`, `health`, `linear` and `npm`, each `{ intervalSeconds, perMinute }` (30/60, 30/30, 30/30, 30/10, 20/30), `statsMinutes` (10) and `conflictMinutes` (30) |
|
|
550
|
+
| `config.json` | machine settings, JSONC (comments allowed), validated at startup; `inbox.runawayTokens` (1500000, the `runaway` rule's number); `notify` and `quietHours` (see Notifications); `scheduler` holds `mode` (`on`, `observe` or `off`), `slots` (4, across projects, and the most any rule can give one project), `dailyTokens` (20000000, the `daily-cap` rule's number) and `tickSeconds` (120); `sessions` holds `pollSeconds` (10), `settleSeconds` (5), `permissionMinutes` (5), `idleMinutes` (20), `compactTokens` (300000, the `compact-on-park` rule's number); `watchers` holds `ado`, `github`, `health`, `linear` and `npm`, each `{ intervalSeconds, perMinute }` (30/60, 30/30, 30/30, 30/10, 20/30), `statsMinutes` (10) and `conflictMinutes` (30) |
|
|
547
551
|
| `rules.json` | machine rules, JSONC, reloaded on save (see Rules) |
|
|
548
552
|
| `token` | the API token, readable by this user only; every request sends `Authorization: Bearer <token>` |
|
|
549
553
|
| `conductor.pid`, `port` | the single-instance lock and where the API listens (`127.0.0.1`) |
|
|
@@ -552,7 +556,7 @@ Everything lives in `~/.conductor` (`CONDUCTOR_HOME` overrides it):
|
|
|
552
556
|
|
|
553
557
|
## API
|
|
554
558
|
|
|
555
|
-
- `GET /projects`: the registered projects. `POST /projects` with `{ "path": "<absolute path>" }` registers one. `POST /projects/<name>/check` with `{ "worktree": false }` or `{}` runs `conduct project check` as the daemon's accounts: `{ project, results: [{ check, status, summary, details, fix }] }`, or 404 for a name no project is registered under.
|
|
559
|
+
- `GET /projects`: the registered projects. `POST /projects` with `{ "path": "<absolute path>" }` registers one. `DELETE /projects/<name>` removes one: `{ project, resolved }` (the inbox items it resolved), 404 for a name no project is registered under, or 409 naming its workers in flight. `POST /projects/<name>/check` with `{ "worktree": false }` or `{}` runs `conduct project check` as the daemon's accounts: `{ project, results: [{ check, status, summary, details, fix }] }`, or 404 for a name no project is registered under.
|
|
556
560
|
- `GET /events`: the event log as server-sent events. Add `?since=<id>` (or send `Last-Event-ID`) to replay from an id. Without either, the stream starts at the live end. `conduct events` prints it one JSON event per line: the events after `--since` (all of them without it), or with `--follow` everything from there on (live without `--since`). `--follow` reconnects with `Last-Event-ID` when the daemon goes away, so a restart loses nothing, and prints the stream's state as lines without an id: `{"kind":"conduct.connected"}` and, once per outage, `{"kind":"conduct.disconnected","error":…}`. `?console=1` (`conduct events --follow --console`, which the conductor mod runs, or `conduct events --follow` with `CONDUCTOR_CONSOLE=1`, as a mod from before `--console` runs it until `/reload-plugins`) marks the console's stream: while one is open, escalations have a console to wake (see Console).
|
|
557
561
|
- `POST /shutdown`: a clean stop (`conduct stop`).
|
|
558
562
|
- `GET /console/escalations`: the open escalations, oldest first (see Console).
|
package/build/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import { inboxLines } from "./inbox.js";
|
|
|
20
20
|
import { parseJsonc } from "./jsonc.js";
|
|
21
21
|
import { processAlive, readPid } from "./lock.js";
|
|
22
22
|
import { rotatingLog } from "./log.js";
|
|
23
|
-
import { fleetWorkerSchema, storedFleet, workerLine } from "./manager.js";
|
|
23
|
+
import { fleetWorkerSchema, storedFleet, workerByRef, workerLine } from "./manager.js";
|
|
24
24
|
import { installMod, removeMod } from "./mod.js";
|
|
25
25
|
import { noteCommand } from "./note.js";
|
|
26
26
|
import { isQuiet, notifyLines, notifyStatusSchema, parseUntil, testResultSchema } from "./notify.js";
|
|
@@ -28,7 +28,8 @@ import { overrideViewSchema, PAUSE } from "./overrides.js";
|
|
|
28
28
|
import { packageInfo } from "./package-info.js";
|
|
29
29
|
import { conductorPaths } from "./paths.js";
|
|
30
30
|
import { projectCheckCommand } from "./project-check-cli.js";
|
|
31
|
-
import {
|
|
31
|
+
import { noProject } from "./project-check.js";
|
|
32
|
+
import { pushoverCredential, pushoverSender } from "./pushover.js";
|
|
32
33
|
import { registerProject } from "./registry.js";
|
|
33
34
|
import { rulesLines, rulesService, rulesTestResultSchema, rulesViewSchema, testLines, } from "./rules-service.js";
|
|
34
35
|
import { resolveUntil } from "./rules.js";
|
|
@@ -60,6 +61,7 @@ const ATTACH_TIMEOUT_MS = 9 * 60_000;
|
|
|
60
61
|
const ANSWER_TIMEOUT_MS = 60_000;
|
|
61
62
|
const projectsBody = z.object({ projects: z.array(projectSchema) });
|
|
62
63
|
const addBody = z.object({ created: z.boolean(), project: projectSchema });
|
|
64
|
+
const removeBody = z.object({ project: projectSchema, resolved: z.array(inboxItemSchema) });
|
|
63
65
|
const errorBody = z.object({ details: z.array(z.string()).optional(), error: z.string() });
|
|
64
66
|
const fleetBody = z.object({ workers: z.array(fleetWorkerSchema) });
|
|
65
67
|
const workerBody = z.object({ worker: workerSchema });
|
|
@@ -268,13 +270,7 @@ async function expectOk(call) {
|
|
|
268
270
|
// A worker in flight by item or name. A probe and a live worker can share an item, so a name match wins.
|
|
269
271
|
async function findWorker(paths, ref) {
|
|
270
272
|
const { workers } = fleetBody.parse(await expectOk(callApi(paths, { method: 'GET', path: '/fleet' })));
|
|
271
|
-
|
|
272
|
-
const matches = byName.length > 0 ? byName : workers.filter((w) => w.item === ref);
|
|
273
|
-
if (matches.length === 1 && matches[0])
|
|
274
|
-
return matches[0];
|
|
275
|
-
if (matches.length === 0)
|
|
276
|
-
throw new ConductorError(`no worker "${ref}" in flight`, ['`conduct status` lists them']);
|
|
277
|
-
throw new ConductorError(`"${ref}" names several workers`, matches.map((w) => `${w.name} in ${w.project}`));
|
|
273
|
+
return workerByRef(workers, ref);
|
|
278
274
|
}
|
|
279
275
|
// `conduct help [<name>]` and `conduct <name> --help`: the listing (also `conduct help help`) or one
|
|
280
276
|
// command's help, with a blank line either side, styled when stdout takes color.
|
|
@@ -652,6 +648,8 @@ async function main(argv) {
|
|
|
652
648
|
case 'project':
|
|
653
649
|
if (sub === 'add' && arg !== undefined)
|
|
654
650
|
return addProject(paths, arg);
|
|
651
|
+
if (sub === 'remove' && arg !== undefined)
|
|
652
|
+
return removeProject(paths, arg);
|
|
655
653
|
if (sub === 'list')
|
|
656
654
|
return listProjects(paths, values.json === true);
|
|
657
655
|
if (sub === 'check' && arg !== undefined) {
|
|
@@ -893,6 +891,28 @@ async function queue(paths, json, fresh) {
|
|
|
893
891
|
return out(JSON.stringify(view, null, 2));
|
|
894
892
|
queueLines(view).forEach((line) => out(line));
|
|
895
893
|
}
|
|
894
|
+
// `conduct project remove <name>`: a renamed project's old registration, or one no longer conducted.
|
|
895
|
+
async function removeProject(paths, name) {
|
|
896
|
+
let result;
|
|
897
|
+
try {
|
|
898
|
+
result = removeBody.parse(await expectOk(callApi(paths, { method: 'DELETE', path: `/projects/${encodeURIComponent(name)}` })));
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
if (!(error instanceof NotRunningError))
|
|
902
|
+
throw error;
|
|
903
|
+
// No daemon: write the store directly. It doesn't load the project when it starts.
|
|
904
|
+
result = withStore(paths, (store) => {
|
|
905
|
+
const removed = store.removeProject(name);
|
|
906
|
+
if (removed)
|
|
907
|
+
return removed;
|
|
908
|
+
throw noProject(name, store.listProjects().map((p) => p.name));
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
const { project, resolved } = result;
|
|
912
|
+
out(`removed ${project.name} (${project.root})`);
|
|
913
|
+
for (const item of resolved)
|
|
914
|
+
out(` resolved #${item.id} (${item.kind}${item.item ? ` ${item.item}` : ''})`);
|
|
915
|
+
}
|
|
896
916
|
// What a supervisor said about the serve it restarted (supervise.ts). One it can't read is left out:
|
|
897
917
|
// the restart goes on without its event. Read once and removed, so the sessions and processes the
|
|
898
918
|
// daemon starts don't inherit it.
|
|
@@ -1206,7 +1226,7 @@ async function superviseCommand(paths, failFast) {
|
|
|
1206
1226
|
now: () => Date.now(),
|
|
1207
1227
|
push: (message) => {
|
|
1208
1228
|
const { notify } = config();
|
|
1209
|
-
return pushoverSender({ credential: notify.pushover.credential })({
|
|
1229
|
+
return pushoverSender({ credential: pushoverCredential(notify.pushover.credential, paths.home) })({
|
|
1210
1230
|
...message,
|
|
1211
1231
|
priority: notify.pushover.priority,
|
|
1212
1232
|
});
|