@edgehero/pi-dispatch-admin 1.2.0 → 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/README.md +4 -3
- package/dist/index.mjs +1061 -486
- package/package.json +1 -1
- package/skills/operate-pi-dispatch/SKILL.md +66 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgehero/pi-dispatch-admin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Operator console (a pi extension) + skill for pi-dispatch: run the pi coding agent as a self-hosted service. A /dispatch TUI for the queue, spend caps, run history, editable GitHub, GitLab, Forgejo and Azure DevOps triggers (cron/label/comment/PR/MR/work item), and scheduled pause windows — plus AI-operable, human-confirmed controls. Runs against a live pi-dispatch deployment.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -120,6 +120,36 @@ cannot, and that it is an edit they make to `triggers.json` themselves.** Do not
|
|
|
120
120
|
built or pulled on the worker's own host, because jobs run with `--pull=never` and nothing is fetched at
|
|
121
121
|
job time; and `pi-dispatch doctor` lists every image their triggers name and flags one that is missing.
|
|
122
122
|
|
|
123
|
+
## Vault secrets — `run.secrets`, and why you cannot set it
|
|
124
|
+
|
|
125
|
+
A trigger may carry `run.secrets`: a map of environment variable name to an opaque **reference**, plus
|
|
126
|
+
`run.secretsProfile` naming which of the operator's resolvers reads them. Before the container starts, the
|
|
127
|
+
worker runs that resolver once per reference on the HOST and injects the values into the job's environment.
|
|
128
|
+
It exists so one trigger can hold a deploy key while every other job on the deployment holds none.
|
|
129
|
+
|
|
130
|
+
Report it when asked, and be precise about the shape of the trade. **The job holds no vault credential.** It
|
|
131
|
+
receives values, never the thing that can fetch values, so it cannot enumerate the vault or reach anything
|
|
132
|
+
the operator did not name in a reviewed file. What that does *not* do is bound where a value can go once the
|
|
133
|
+
agent has it: the agent can read its own environment, and the forge is on the egress allowlist by necessity.
|
|
134
|
+
The panel shows the count and the profile name in the trigger list and in the drill-in, never the references.
|
|
135
|
+
|
|
136
|
+
**You cannot set it, in either direction.** No `dispatch_*` tool has a `secrets` parameter, and none has a
|
|
137
|
+
`secretsProfile` parameter either. The second absence is worth understanding rather than treating as an
|
|
138
|
+
oversight: a profile that resolves nothing is refused at load, and no tool can write `run.secrets`, so a
|
|
139
|
+
profile picker could never produce a valid trigger even once. It would be a control that looks like a grant
|
|
140
|
+
and is only ever an error.
|
|
141
|
+
|
|
142
|
+
You also cannot declare a **resolver profile**. That is `/dispatch secrets add`, which the operator types
|
|
143
|
+
themselves, because declaring one means naming an absolute host path the worker executes.
|
|
144
|
+
|
|
145
|
+
So if a user asks you to give a trigger access to a vault, or to wire up 1Password: **say plainly that you
|
|
146
|
+
cannot, and that both halves are theirs.** Three useful things you *can* say: declaring the manager is
|
|
147
|
+
`/dispatch secrets add`, and it is two questions (a name and the path to a one-line script such as
|
|
148
|
+
`exec op read --no-newline "$1"`); binding it to a trigger is an edit to `triggers.json` beside that
|
|
149
|
+
trigger's `flow`; and `pi-dispatch doctor` lists every declared profile, fails loudly when a trigger names
|
|
150
|
+
one that is not declared, and warns when a **local** trigger binds secrets, because a local job edits the
|
|
151
|
+
operator's own folder in place and a credential the agent writes into `.env` lands in their real repository.
|
|
152
|
+
|
|
123
153
|
## The forge a trigger listens to — `run.kind`
|
|
124
154
|
|
|
125
155
|
A webhook trigger names its forge: `"kind"` is `github`, `gitlab`, `forgejo` or `azure`. Everything else
|
|
@@ -129,11 +159,14 @@ about the trigger is the same — the `on.type`, the `{any, all, none}` label pr
|
|
|
129
159
|
Three things are NOT the same, and all of them refuse at load rather than misbehaving quietly:
|
|
130
160
|
|
|
131
161
|
- **`pull_request` actions are the forge's own words.** GitHub takes
|
|
132
|
-
`labeled | opened | synchronize | reopened | review_submitted`; GitLab takes
|
|
133
|
-
`open | update | reopen | approved`; Forgejo takes
|
|
134
|
-
`label_updated | opened | synchronized | reopened`; Azure takes `created | updated
|
|
162
|
+
`labeled | opened | synchronize | reopened | review_submitted | closed`; GitLab takes
|
|
163
|
+
`open | update | reopen | approved | close`; Forgejo takes
|
|
164
|
+
`label_updated | opened | synchronized | reopened | closed`; Azure takes `created | updated` and has
|
|
165
|
+
no close word (a close trigger on azure is refused at load — not yet covered, not declined). A word
|
|
135
166
|
from the wrong forge is refused when the file is written. It would not break anything otherwise — it
|
|
136
167
|
would simply never match an event, and the trigger would look configured while doing nothing.
|
|
168
|
+
The close word rides **alone**: a rule mixing it with other actions is refused, because a close is
|
|
169
|
+
gated on the actor who closed the item where every other action gates on the author or a label.
|
|
137
170
|
`review_submitted` is GitHub's `pull_request_review` event: it fires on **every** submitted review, so
|
|
138
171
|
add `reviewState` (`approved | changes_requested | commented`) to narrow which verdicts are worth paying
|
|
139
172
|
for. That field is github-only and legal only beside `review_submitted`; anywhere else it refuses at
|
|
@@ -146,6 +179,32 @@ Three things are NOT the same, and all of them refuse at load rather than misbeh
|
|
|
146
179
|
requests, so `any`/`all`/`none` could never match and a rule that loads clean and never fires reads as a
|
|
147
180
|
broken harness.
|
|
148
181
|
|
|
182
|
+
## Close triggers and one-shots — `on.once`, and the re-arm
|
|
183
|
+
|
|
184
|
+
Two shapes fire on a close: the `issue` trigger type (an issue closing, `on.action` in the forge's close
|
|
185
|
+
word), and a `pull_request` rule whose only action is the close word (on GitHub and Forgejo a merged PR
|
|
186
|
+
counts as closed; on GitLab only an explicit close fires it). Both
|
|
187
|
+
may carry `on.number` to pin one specific item, and `once: true` (which requires `number`) makes the rule
|
|
188
|
+
a **one-shot**: after one run the worker marks the entry spent by adding
|
|
189
|
+
`on.disarmed: { at, jobId }` to it in `triggers.json`. The entry is never deleted — run history
|
|
190
|
+
attributes by array position — so `dispatch_triggers` and the panel keep showing it, marked spent,
|
|
191
|
+
matching nothing.
|
|
192
|
+
|
|
193
|
+
Four things to say when an operator asks:
|
|
194
|
+
|
|
195
|
+
- **A failed run still spends it.** "Fired" means "produced a run record", so a one-shot whose job failed
|
|
196
|
+
is spent too. The fix is the re-arm below, after they have read why it failed.
|
|
197
|
+
- **Re-arming is deleting `on.disarmed` from the entry** in `triggers.json`, nothing else. It is an
|
|
198
|
+
operator file edit: no tool and no panel key writes or removes that mark, so say so plainly rather than
|
|
199
|
+
reaching for `dispatch_trigger_edit` (which changes the flow only).
|
|
200
|
+
- **Authoring one**: `dispatch_trigger_add` takes `kind: issue` (plus `number` and `once`), behind the
|
|
201
|
+
same confirm dialog as every trigger write, and a close-only `pull_request` rule accepts the same two
|
|
202
|
+
fields. The shared validator refuses anything malformed, and `once` requires `number`.
|
|
203
|
+
- **Both services must read the same file.** The disarm is a worker write, so a receiver pointed at a
|
|
204
|
+
different `triggers.json` stays armed; `PI_TRIGGERS_FILE` set to one absolute path is the fix, and
|
|
205
|
+
`pi-dispatch doctor` warns about it. In the compose topology the receiver's read-only mount lags a
|
|
206
|
+
disarm until restart, and the worker's own pre-spend check is what prevents a second run meanwhile.
|
|
207
|
+
|
|
149
208
|
## Racing two agents on one event — `run.replicas`
|
|
150
209
|
|
|
151
210
|
`"replicas": 2` on a `label`, `comment` or `pull_request` trigger turns one delivery into **two independent
|
|
@@ -160,9 +219,10 @@ Three things to say when an operator asks about it:
|
|
|
160
219
|
- **You cannot set it from here.** There is no `replicas` parameter on `dispatch_trigger_add` or
|
|
161
220
|
`_edit`, and no panel key. It is a reviewed edit to `triggers.json`, deliberately: a spend multiplier is
|
|
162
221
|
plainly a capability a model should not gain. Say so rather than looking for a way around it.
|
|
163
|
-
- **It refuses on cron, and beside `
|
|
164
|
-
replicas would edit one working tree with no gate and no undo.
|
|
165
|
-
where replicas exist to fork it.
|
|
222
|
+
- **It refuses on cron, beside `resume`, and beside `once: true`.** A local job's `/workspace` IS the
|
|
223
|
+
operator's folder, so two replicas would edit one working tree with no gate and no undo. A resumed run
|
|
224
|
+
continues one lineage where replicas exist to fork it. And a one-shot promises exactly one run, which N
|
|
225
|
+
racing sandboxes contradict.
|
|
166
226
|
|
|
167
227
|
`dispatch_trigger_add` takes an optional `forge` parameter, defaulting to `github`. Unlike `image`, this
|
|
168
228
|
one IS offered to the model — a model that can already add a GitHub trigger can already arm a paid run,
|