@edgehero/pi-dispatch-admin 4.3.0 → 4.3.1
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 -4
- package/dist/index.mjs +36 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ Four things to know before you build on it:
|
|
|
129
129
|
One command puts a live terminal view over the whole deployment:
|
|
130
130
|
|
|
131
131
|
<p align="center">
|
|
132
|
-
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-dashboard.png?v=2026-10-
|
|
132
|
+
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-dashboard.png?v=2026-10-14" alt="The /dispatch panel: status, spend meters, dollar windows, triggers with a portfolio trigger, pause windows, scoped limits with their dollar caps, held and failed jobs, runs with their projects and the u hosts key, and settings" width="820">
|
|
133
133
|
</p>
|
|
134
134
|
|
|
135
135
|
- **MCP servers, read-only.** A trigger that names MCP servers says so on its row (`[mcp ...]`), and its
|
|
@@ -146,7 +146,7 @@ One command puts a live terminal view over the whole deployment:
|
|
|
146
146
|
always marked as one ([`docs/insights.md`](https://github.com/edgehero/pi-dispatch/blob/main/docs/insights.md)).
|
|
147
147
|
|
|
148
148
|
<p align="center">
|
|
149
|
-
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/insights-view.png?v=2026-10-
|
|
149
|
+
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/insights-view.png?v=2026-10-14" alt="The insights page: KPI tiles, budget dials, the budget split, a plan verdict, spend charts, the five breakdowns, how busy each host was, and the topology with spend badges" width="820">
|
|
150
150
|
</p>
|
|
151
151
|
|
|
152
152
|
- **Triggers, editable live.** Add, edit and delete triggers without a restart. Drill-ins show what
|
|
@@ -184,7 +184,7 @@ One command puts a live terminal view over the whole deployment:
|
|
|
184
184
|
It counts jobs only: a machine busy with other work reads as idle.
|
|
185
185
|
|
|
186
186
|
<p align="center">
|
|
187
|
-
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-hosts.png?v=2026-10-
|
|
187
|
+
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-hosts.png?v=2026-10-14" alt="The panel's hosts view: three machines, each with its slots in use, the memory and CPU its budget has promised, its running jobs with project, size and age, a stale host with an orphaned container, and the last 7 days as busy share, jobs at once, time full, the wait and the top projects" width="820">
|
|
188
188
|
</p>
|
|
189
189
|
|
|
190
190
|
- **The budget split.** With an allocation envelope (a dollar total per window and a floor per project), `b`
|
|
@@ -198,7 +198,7 @@ One command puts a live terminal view over the whole deployment:
|
|
|
198
198
|
plan. A run's detail names the plan a portfolio run wrote and the dollars it settled.
|
|
199
199
|
|
|
200
200
|
<p align="center">
|
|
201
|
-
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-allocation.png?v=2026-10-
|
|
201
|
+
<img src="https://raw.githubusercontent.com/edgehero/pi-dispatch/main/docs/images/dispatch-allocation.png?v=2026-10-14" alt="The panel's budget split view: the weekly envelope, each project's floor, weight, share and spend, the headroom, the applied plan with its reasons, and the history across two hosts with a revert and a refused plan" width="820">
|
|
202
202
|
</p>
|
|
203
203
|
|
|
204
204
|
- **The portfolio manager.** A weekly flow you own reads the budget and writes a plan of weights. The
|
package/dist/index.mjs
CHANGED
|
@@ -8810,7 +8810,7 @@ function isForgeKind(kind) {
|
|
|
8810
8810
|
function forgeSpec(kind) {
|
|
8811
8811
|
return isForgeKind(kind) ? FORGES[kind] : void 0;
|
|
8812
8812
|
}
|
|
8813
|
-
var FORGES, FORGE_KINDS, RUN_KINDS, MINTED_TOKEN_VARS, FORGE_HOST_VARS;
|
|
8813
|
+
var FORGES, FORGE_KINDS, RUN_KINDS, MINTED_TOKEN_VARS, FORGE_HOST_VARS, RECEIVER_ONLY_VARS;
|
|
8814
8814
|
var init_forges = __esm({
|
|
8815
8815
|
"../worker/src/forges.mjs"() {
|
|
8816
8816
|
FORGES = {
|
|
@@ -8819,7 +8819,8 @@ var init_forges = __esm({
|
|
|
8819
8819
|
deliveryIdName: "X-GitHub-Delivery GUID",
|
|
8820
8820
|
pullRequestSep: "#",
|
|
8821
8821
|
tokenVars: ["GITHUB_TOKEN", "GH_TOKEN"],
|
|
8822
|
-
|
|
8822
|
+
hostVars: [],
|
|
8823
|
+
receiverOnlyVars: [],
|
|
8823
8824
|
prLabelAction: "labeled"
|
|
8824
8825
|
},
|
|
8825
8826
|
gitlab: {
|
|
@@ -8827,7 +8828,8 @@ var init_forges = __esm({
|
|
|
8827
8828
|
deliveryIdName: "webhook-id / Idempotency-Key",
|
|
8828
8829
|
pullRequestSep: "!",
|
|
8829
8830
|
tokenVars: ["GITLAB_TOKEN", "GL_TOKEN"],
|
|
8830
|
-
|
|
8831
|
+
hostVars: ["GITLAB_HOST"],
|
|
8832
|
+
receiverOnlyVars: [],
|
|
8831
8833
|
// A label added to a merge request arrives as a plain `update`, indistinguishable from any other
|
|
8832
8834
|
// edit, so there is no action for a positive-selector rule to attach to. (Separately, a GitLab label
|
|
8833
8835
|
// is not an approval at all -- a Guest can set one at issue creation -- which is why every gitlab
|
|
@@ -8844,9 +8846,23 @@ var init_forges = __esm({
|
|
|
8844
8846
|
// Forgejo numbers issues and pull requests from ONE per-repository sequence, exactly as GitHub does
|
|
8845
8847
|
// -- an issue and a PR cannot share an index. So `#` names one thing and no discriminator is needed.
|
|
8846
8848
|
pullRequestSep: "#",
|
|
8847
|
-
// `tea`
|
|
8848
|
-
|
|
8849
|
-
|
|
8849
|
+
// `tea` logs in from the environment ONLY through GITEA_TOKEN plus GITEA_INSTANCE_URL. Measured on the
|
|
8850
|
+
// pinned tea 0.15.0 in the 4.3.0 job image against a live Forgejo 14.0.5: that pair lists issues, while
|
|
8851
|
+
// GITEA_SERVER_URL + GITEA_SERVER_TOKEN (with or without GITEA_LOGIN_VIA_ENV=1), FORGEJO_HOST +
|
|
8852
|
+
// GITEA_SERVER_TOKEN, and GITEA_TOKEN alone inside a clone all fail "Error: no available login". So
|
|
8853
|
+
// from be1cc42e until this row changed, every `tea` command the forgejo envelope tells the agent to run
|
|
8854
|
+
// failed. GITEA_SERVER_TOKEN stays: it is the default of `tea login add --token` (its --help says
|
|
8855
|
+
// `[$GITEA_SERVER_TOKEN]`), so an agent that adds a login explicitly still gets the job's token, and it
|
|
8856
|
+
// stays reserved. FORGEJO_TOKEN is what the API examples and most scripts use.
|
|
8857
|
+
// worker/test/forges.test.mjs pins these names to the tea version image/Dockerfile installs.
|
|
8858
|
+
tokenVars: ["FORGEJO_TOKEN", "GITEA_TOKEN", "GITEA_SERVER_TOKEN"],
|
|
8859
|
+
// GITEA_INSTANCE_URL is tea's, and must be the full URL with its scheme (FORGEJO_URL is). FORGEJO_HOST
|
|
8860
|
+
// is read by no CLI; it is kept because it has been in every forgejo job since #61 and a skill may use it.
|
|
8861
|
+
hostVars: ["GITEA_INSTANCE_URL", "FORGEJO_HOST"],
|
|
8862
|
+
// The author gate's own lookup token (OQ-043): Forgejo answers a lookup of another user's permission only to a
|
|
8863
|
+
// repository admin, so this is a read:repository token of an account that is one, and the job's FORGEJO_TOKEN
|
|
8864
|
+
// can stay a write collaborator's. A job holding it would hold an admin's token, which is the whole point lost.
|
|
8865
|
+
receiverOnlyVars: ["FORGEJO_PERMISSION_TOKEN"],
|
|
8850
8866
|
// Forgejo names the action, so the positive-selector rule applies exactly as it does on GitHub.
|
|
8851
8867
|
// Note this is HYGIENE, not the gate: unlike GitHub, every forgejo trigger is additionally gated on
|
|
8852
8868
|
// the actor's resolved repository permission -- see filter-forgejo.mjs for why that is not
|
|
@@ -8867,7 +8883,8 @@ var init_forges = __esm({
|
|
|
8867
8883
|
pullRequestSep: "!",
|
|
8868
8884
|
// `az repos` reads AZURE_DEVOPS_EXT_PAT; SYSTEM_ACCESSTOKEN is what a pipeline-shaped script expects.
|
|
8869
8885
|
tokenVars: ["AZURE_DEVOPS_EXT_PAT", "SYSTEM_ACCESSTOKEN"],
|
|
8870
|
-
|
|
8886
|
+
hostVars: ["AZURE_DEVOPS_ORG_URL"],
|
|
8887
|
+
receiverOnlyVars: [],
|
|
8871
8888
|
// Azure attaches no labels to a pull request at all, so there is no action to attach the rule to and
|
|
8872
8889
|
// a predicated PR rule could never match. The loader refuses one rather than letting it load dead.
|
|
8873
8890
|
prLabelAction: null
|
|
@@ -8876,7 +8893,8 @@ var init_forges = __esm({
|
|
|
8876
8893
|
FORGE_KINDS = Object.freeze(Object.keys(FORGES));
|
|
8877
8894
|
RUN_KINDS = Object.freeze(["local", ...FORGE_KINDS]);
|
|
8878
8895
|
MINTED_TOKEN_VARS = new Set(FORGE_KINDS.flatMap((kind) => FORGES[kind].tokenVars));
|
|
8879
|
-
FORGE_HOST_VARS = new Set(FORGE_KINDS.
|
|
8896
|
+
FORGE_HOST_VARS = new Set(FORGE_KINDS.flatMap((kind) => FORGES[kind].hostVars));
|
|
8897
|
+
RECEIVER_ONLY_VARS = new Set(FORGE_KINDS.flatMap((kind) => FORGES[kind].receiverOnlyVars));
|
|
8880
8898
|
}
|
|
8881
8899
|
});
|
|
8882
8900
|
|
|
@@ -9473,6 +9491,12 @@ function forwardEnvList(raw, egressArmed2 = false) {
|
|
|
9473
9491
|
`PI_FORWARD_ENV must not forward ${minted.join(", ")} -- the worker mints a per-job token (CONST-TOKEN-SCOPED-PER-JOB) and a forwarded operator token would silently override it`
|
|
9474
9492
|
);
|
|
9475
9493
|
}
|
|
9494
|
+
const receiverOnly = names.filter((n) => RECEIVER_ONLY_VARS.has(n));
|
|
9495
|
+
if (receiverOnly.length > 0) {
|
|
9496
|
+
throw configError4(
|
|
9497
|
+
`PI_FORWARD_ENV must not forward ${receiverOnly.join(", ")} -- only the receiver reads it, for the author gate's permission lookup, and it belongs to an account that is an admin of the repositories, which is exactly what a job must not hold (OQ-043)`
|
|
9498
|
+
);
|
|
9499
|
+
}
|
|
9476
9500
|
const workerOnly = names.filter((n) => WORKER_ONLY_SECRET_VARS.has(n));
|
|
9477
9501
|
if (workerOnly.length > 0) {
|
|
9478
9502
|
throw configError4(
|
|
@@ -17363,7 +17387,7 @@ var init_triggers = __esm({
|
|
|
17363
17387
|
REPLICAS_MAX = 3;
|
|
17364
17388
|
SECRETS_MAX = 16;
|
|
17365
17389
|
ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17366
|
-
RESERVED_ENV_NAMES = /* @__PURE__ */ new Set([...MINTED_TOKEN_VARS, ...FORGE_HOST_VARS, ...WORKER_ONLY_SECRET_VARS, ...EGRESS_ENV_VARS, ...CONTAINER_ENV_NAMES, ...RUNNER_ENV_NAMES, ...PROVIDER_STEERING_VARS]);
|
|
17390
|
+
RESERVED_ENV_NAMES = /* @__PURE__ */ new Set([...MINTED_TOKEN_VARS, ...FORGE_HOST_VARS, ...RECEIVER_ONLY_VARS, ...WORKER_ONLY_SECRET_VARS, ...EGRESS_ENV_VARS, ...CONTAINER_ENV_NAMES, ...RUNNER_ENV_NAMES, ...PROVIDER_STEERING_VARS]);
|
|
17367
17391
|
INSTRUCTIONS_MAX = 2e3;
|
|
17368
17392
|
RUN_MODEL_NEAR_MISS_TARGETS = Object.freeze({
|
|
17369
17393
|
provider: "run.provider",
|
|
@@ -131864,8 +131888,8 @@ function runRow(row, sel, inner, styler) {
|
|
|
131864
131888
|
return fitLine(`${cursor} ${tree}${rep}${cells.join(sep4)}`, inner, styler);
|
|
131865
131889
|
}
|
|
131866
131890
|
function outcomeColored(outcome, reason, styler) {
|
|
131867
|
-
if (outcome === "completed") return styler.fg("success", "\u2714 done");
|
|
131868
131891
|
const shown2 = (v) => scrubControl(String(v));
|
|
131892
|
+
if (outcome === "completed") return reason ? styler.fg("warning", `\u2714 ${shown2(reason)}`) : styler.fg("success", "\u2714 done");
|
|
131869
131893
|
if (outcome === "policy") return styler.fg("warning", `\u26A0 ${shown2(reason ?? "policy")}`);
|
|
131870
131894
|
return styler.fg("error", `\u2718 ${shown2(reason ?? outcome ?? "failed")}`);
|
|
131871
131895
|
}
|
|
@@ -133219,8 +133243,8 @@ var init_setup_wizard = __esm({
|
|
|
133219
133243
|
init_connection();
|
|
133220
133244
|
init_service_env();
|
|
133221
133245
|
init_index();
|
|
133222
|
-
RUNTIME_VERSION = "4.3.
|
|
133223
|
-
RECEIVER_VERSION = "4.3.
|
|
133246
|
+
RUNTIME_VERSION = "4.3.1";
|
|
133247
|
+
RECEIVER_VERSION = "4.3.1";
|
|
133224
133248
|
RUNTIME_PKG = "@edgehero/pi-dispatch";
|
|
133225
133249
|
RECEIVER_PKG = "@edgehero/pi-dispatch-receiver";
|
|
133226
133250
|
NUDGE_MARKER_BASENAME = "pi-dispatch-setup.nudged";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgehero/pi-dispatch-admin",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.1",
|
|
4
4
|
"description": "Operator console for pi-dispatch, as a pi extension: a /dispatch panel over the queue, spend caps, scoped limits, run history and insights, editable cron and GitHub, GitLab, Forgejo and Azure DevOps triggers, pause windows, and human-confirmed tools your AI can operate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|