@blackbelt-technology/pi-agent-dashboard 0.4.2 → 0.4.3

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/AGENTS.md CHANGED
@@ -391,7 +391,8 @@ make clean # Destroy all cloned VMs
391
391
  | `packages/electron/scripts/test-electron-install.sh` | Full e2e Docker test: install, wizard, server launch, health check |
392
392
  | `packages/electron/scripts/test-electron-install-inner.sh` | Inner test script run inside Docker container |
393
393
  | `packages/electron/resources/icon.png` | Master 1024×1024 app icon (π on dark navy) |
394
- | `.github/workflows/publish.yml` | CI: builds DMG (macOS), DEB+AppImage (Linux), NSIS+ZIP+portable (Windows) on native runners; publishes npm + GitHub Release. **Two triggers**: (a) push of any `v*` tag (the original path — release-cut skill / hand tag), (b) `workflow_dispatch` from the GitHub Actions UI with a single required `version` input (e.g. `"0.4.1"`). The `prepare` job branches on `github.event_name`: tag-push extracts the version from `GITHUB_REF_NAME`; dispatch validates the input as semver, checks tag uniqueness on origin, bumps every workspace `package.json` via `npm version -ws`, syncs cross-ref specifiers via `scripts/sync-versions.js`, promotes `## [Unreleased]` to a dated `## [<version>]` section in `CHANGELOG.md`, commits + tags + pushes the branch. The publish, electron, and github-release jobs all `needs: prepare` and check out `ref: ${{ needs.prepare.outputs.tag }}` so both trigger paths publish the same tree. **Idempotent ordered npm publish (commit b9fcea9)**: the publish step replaced the bulk `npm publish --workspaces --include-workspace-root` call with a per-package loop that (a) **skips** already-published versions via `npm view <pkg>@<ver>` (so a re-run after partial-publish failure resumes cleanly instead of aborting on "cannot publish over previously published"), (b) publishes the **4 stable sub-packages first** (`pi-dashboard-shared` → `extension` → `server` → `web`), then the brand-new `dashboard-plugin-runtime`, then the **root metapackage last** so the registry already serves matching sub-package versions before the root tarball lands and resolves dependents like `@blackbelt-technology/pi-dashboard-extension@^X.Y.Z`. v0.4.0 and v0.4.1 shipped broken because the bulk call aborted on the first error and only the root tarball landed — `npm install @blackbelt-technology/pi-agent-dashboard@0.4.1` returned ETARGET on the sub-deps. Single-failure isolation: any non-skip failure marks the step failed via a `FAIL=1` accumulator but lets the loop finish so logs show every package's outcome. Also (commit b9fcea9): `packages/server/package.json#dependencies` now declares `@blackbelt-technology/dashboard-plugin-runtime: ^<ver>` — previously imported via workspace symlinks but missing from the published tarball, so a clean `npm install` of just the server crashed with `MODULE_NOT_FOUND` on first start. Also (commit 2728c31): every workspace `package.json` (`shared`, `extension`, `server`, `client`, `dashboard-plugin-runtime`, plus `electron`) now declares a `repository` field — required for npm provenance attestation validation when publishing with `--provenance` from GitHub Actions OIDC. |
394
+ | `.github/workflows/publish.yml` | CI: builds DMG (macOS), DEB+AppImage (Linux), NSIS+ZIP+portable (Windows) on native runners; publishes npm + GitHub Release. **Two triggers**: (a) push of any `v*` tag (the original path — release-cut skill / hand tag), (b) `workflow_dispatch` from the GitHub Actions UI with a single required `version` input (e.g. `"0.4.1"`). The `prepare` job branches on `github.event_name`: tag-push extracts the version from `GITHUB_REF_NAME`; dispatch validates the input as semver, checks tag uniqueness on origin, bumps every workspace `package.json` via `npm version -ws`, syncs cross-ref specifiers via `scripts/sync-versions.js`, promotes `## [Unreleased]` to a dated `## [<version>]` section in `CHANGELOG.md`, commits + tags + pushes the branch. The publish, electron, and github-release jobs all `needs: prepare` and check out `ref: ${{ needs.prepare.outputs.tag }}` so both trigger paths publish the same tree. **Idempotent ordered npm publish (commit b9fcea9)**: the publish step replaced the bulk `npm publish --workspaces --include-workspace-root` call with a per-package loop that (a) **skips** already-published versions via `npm view <pkg>@<ver>` (so a re-run after partial-publish failure resumes cleanly instead of aborting on "cannot publish over previously published"), (b) publishes the **4 stable sub-packages first** (`pi-dashboard-shared` → `extension` → `server` → `web`), then the brand-new `dashboard-plugin-runtime`, then the **root metapackage last** so the registry already serves matching sub-package versions before the root tarball lands and resolves dependents like `@blackbelt-technology/pi-dashboard-extension@^X.Y.Z`. v0.4.0 and v0.4.1 shipped broken because the bulk call aborted on the first error and only the root tarball landed — `npm install @blackbelt-technology/pi-agent-dashboard@0.4.1` returned ETARGET on the sub-deps. Single-failure isolation: any non-skip failure marks the step failed via a `FAIL=1` accumulator but lets the loop finish so logs show every package's outcome. Also (commit b9fcea9): `packages/server/package.json#dependencies` now declares `@blackbelt-technology/dashboard-plugin-runtime: ^<ver>` — previously imported via workspace symlinks but missing from the published tarball, so a clean `npm install` of just the server crashed with `MODULE_NOT_FOUND` on first start. Also (commit 2728c31): every workspace `package.json` (`shared`, `extension`, `server`, `client`, `dashboard-plugin-runtime`, plus `electron`) now declares a `repository` field — required for npm provenance attestation validation when publishing with `--provenance` from GitHub Actions OIDC. **Electron-publish dependency-graph contract** (change: publish-fix-macos): the `electron` matrix job declares `needs: [prepare, publish]` and `strategy.fail-fast: false`. The `needs: publish` gate closes the ETARGET race that broke release run #34 — `bundle-server.sh` runs `npm install --omit=dev` against the live npm registry and resolves `@blackbelt-technology/dashboard-plugin-runtime@^<ver>` (added in commit b9fcea9 to fix `MODULE_NOT_FOUND` on clean server installs), so it must run AFTER publish has uploaded the just-bumped sub-packages. `fail-fast: false` keeps a single-OS failure from cancelling the other four matrix variants — release engineers see the full diagnostic per OS instead of one error and four cancellations. Locked by `packages/shared/src/__tests__/publish-workflow-contract.test.ts`. |
395
+ | `packages/shared/src/__tests__/publish-workflow-contract.test.ts` | Repo-level lint: parses `.github/workflows/publish.yml`, asserts the electron job's `needs:` array contains both `prepare` and `publish` AND `strategy.fail-fast` is the literal `false`. Failure messages cite change `publish-fix-macos` so the contributor knows where to look. Mirrors `no-direct-process-kill.test.ts` and `no-raw-node-import.test.ts`. See change: publish-fix-macos. |
395
396
 
396
397
  ## Build & Restart Workflow
397
398
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackbelt-technology/pi-agent-dashboard",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Web dashboard for monitoring and interacting with pi agent sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -66,9 +66,9 @@
66
66
  "screenshots": "npm --prefix site run screenshots"
67
67
  },
68
68
  "dependencies": {
69
- "@blackbelt-technology/pi-dashboard-extension": "^0.4.2",
70
- "@blackbelt-technology/pi-dashboard-server": "^0.4.2",
71
- "@blackbelt-technology/pi-dashboard-web": "^0.4.2"
69
+ "@blackbelt-technology/pi-dashboard-extension": "^0.4.3",
70
+ "@blackbelt-technology/pi-dashboard-server": "^0.4.3",
71
+ "@blackbelt-technology/pi-dashboard-web": "^0.4.3"
72
72
  },
73
73
  "devDependencies": {
74
74
  "jsdom": "^29.0.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackbelt-technology/pi-dashboard-extension",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Pi bridge extension for pi-dashboard",
5
5
  "type": "module",
6
6
  "repository": {
@@ -24,7 +24,7 @@
24
24
  ".pi/skills/pi-dashboard/"
25
25
  ],
26
26
  "dependencies": {
27
- "@blackbelt-technology/pi-dashboard-shared": "^0.4.2",
27
+ "@blackbelt-technology/pi-dashboard-shared": "^0.4.3",
28
28
  "ws": "^8.18.0"
29
29
  },
30
30
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackbelt-technology/pi-dashboard-server",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Dashboard server for monitoring and interacting with pi agent sessions",
5
5
  "type": "module",
6
6
  "repository": {
@@ -31,9 +31,9 @@
31
31
  "postinstall": "node scripts/fix-pty-permissions.cjs"
32
32
  },
33
33
  "dependencies": {
34
- "@blackbelt-technology/dashboard-plugin-runtime": "^0.4.2",
35
- "@blackbelt-technology/pi-dashboard-extension": "^0.4.2",
36
- "@blackbelt-technology/pi-dashboard-shared": "^0.4.2",
34
+ "@blackbelt-technology/dashboard-plugin-runtime": "^0.4.3",
35
+ "@blackbelt-technology/pi-dashboard-extension": "^0.4.3",
36
+ "@blackbelt-technology/pi-dashboard-shared": "^0.4.3",
37
37
  "@fastify/compress": "^8.3.1",
38
38
  "@fastify/cookie": "^11.0.2",
39
39
  "@fastify/cors": "^11.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackbelt-technology/pi-dashboard-shared",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Shared types and utilities for pi-dashboard",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Repo-level invariant: `.github/workflows/publish.yml`'s `electron` job
3
+ * MUST `needs: [prepare, publish]` and MUST set `strategy.fail-fast: false`.
4
+ *
5
+ * Why: the bundled-server step in the electron matrix runs `npm install`
6
+ * against the live npm registry, which depends on `@blackbelt-technology/*`
7
+ * sub-packages being uploaded by the `publish` job FIRST. Without this gate
8
+ * the electron job races publish and ETARGETs on the just-bumped version
9
+ * (release run #34 — macOS hit ETARGET 1m 45s before publish finished).
10
+ *
11
+ * Without `fail-fast: false`, a single OS failure cascades and cancels the
12
+ * other four matrix variants — losing diagnostic output and wasting runner
13
+ * minutes.
14
+ *
15
+ * If this test fails, restore the two lines in `publish.yml`:
16
+ * electron:
17
+ * needs: [prepare, publish]
18
+ * strategy:
19
+ * fail-fast: false
20
+ * matrix: ...
21
+ *
22
+ * See change: publish-fix-macos.
23
+ */
24
+ import { describe, it, expect } from "vitest";
25
+ import fs from "node:fs";
26
+ import path from "node:path";
27
+ import url from "node:url";
28
+
29
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
30
+ const REPO_ROOT = path.resolve(__dirname, "..", "..", "..", "..");
31
+ const WORKFLOW_PATH = path.join(REPO_ROOT, ".github", "workflows", "publish.yml");
32
+
33
+ /**
34
+ * Extract the YAML body of a top-level job by name. Returns the lines
35
+ * between ` <jobName>:` and the next sibling-indent (` `) job, or EOF.
36
+ *
37
+ * We avoid pulling in a YAML library — the test only needs to inspect two
38
+ * specific scalar/list keys on a known job, and the file format is stable
39
+ * (2-space indent, no tabs, no anchors). Same pattern as
40
+ * `no-direct-process-kill.test.ts` and `no-raw-node-import.test.ts`.
41
+ */
42
+ function extractJobBlock(yaml: string, jobName: string): string {
43
+ const lines = yaml.split("\n");
44
+ const headerRe = new RegExp(`^ ${jobName}:\\s*$`);
45
+ let start = -1;
46
+ for (let i = 0; i < lines.length; i++) {
47
+ if (headerRe.test(lines[i])) {
48
+ start = i;
49
+ break;
50
+ }
51
+ }
52
+ if (start === -1) {
53
+ throw new Error(`job '${jobName}' not found in publish.yml`);
54
+ }
55
+ // Walk forward until next line at the same 2-space indent that is a
56
+ // job header (`^ [a-z][a-z0-9-]*:\s*$`) or EOF.
57
+ const siblingRe = /^ [a-z][a-z0-9-]*:\s*$/;
58
+ let end = lines.length;
59
+ for (let i = start + 1; i < lines.length; i++) {
60
+ if (siblingRe.test(lines[i])) {
61
+ end = i;
62
+ break;
63
+ }
64
+ }
65
+ return lines.slice(start, end).join("\n");
66
+ }
67
+
68
+ describe("publish.yml — electron job dependency-graph contract", () => {
69
+ const yaml = fs.readFileSync(WORKFLOW_PATH, "utf8");
70
+ const electronBlock = extractJobBlock(yaml, "electron");
71
+
72
+ it("electron job's `needs:` includes both `prepare` and `publish`", () => {
73
+ // Accept either flow-list (`needs: [prepare, publish]`) or
74
+ // block-list:
75
+ // needs:
76
+ // - prepare
77
+ // - publish
78
+ // (Currently flow-list — but the test should not lock the surface
79
+ // syntax, only the dependency contract.)
80
+ const flowMatch = electronBlock.match(/^\s{4}needs:\s*\[([^\]]*)\]/m);
81
+ const blockMatch = electronBlock.match(
82
+ /^\s{4}needs:\s*\n((?:\s{6}-\s+\S+\s*\n)+)/m,
83
+ );
84
+
85
+ let names: string[] = [];
86
+ if (flowMatch) {
87
+ names = flowMatch[1]
88
+ .split(",")
89
+ .map((s) => s.trim())
90
+ .filter(Boolean);
91
+ } else if (blockMatch) {
92
+ names = blockMatch[1]
93
+ .split("\n")
94
+ .map((l) => l.replace(/^\s*-\s+/, "").trim())
95
+ .filter(Boolean);
96
+ } else {
97
+ throw new Error(
98
+ "electron job has no `needs:` key — must declare `needs: [prepare, publish]`. " +
99
+ "See change: publish-fix-macos. Job block was:\n" +
100
+ electronBlock,
101
+ );
102
+ }
103
+
104
+ expect(names).toContain("prepare");
105
+ expect(names).toContain("publish");
106
+ });
107
+
108
+ it("electron job's `strategy.fail-fast` is `false`", () => {
109
+ // Match `fail-fast: false` (any whitespace after the colon, but the
110
+ // value must be the literal `false` — not `False`, not absent).
111
+ const m = electronBlock.match(/^\s{6}fail-fast:\s*(\S+)\s*$/m);
112
+ if (!m) {
113
+ throw new Error(
114
+ "electron job's `strategy.fail-fast` is absent — the GitHub Actions " +
115
+ "default of `true` would re-introduce the run-#34 cascade. " +
116
+ "Set `fail-fast: false`. See change: publish-fix-macos.\n" +
117
+ "Job block was:\n" +
118
+ electronBlock,
119
+ );
120
+ }
121
+ expect(m[1]).toBe("false");
122
+ });
123
+ });