pi-crew 0.9.46 → 0.9.48

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +16 -2
  3. package/dist/build-meta.json +289 -164
  4. package/dist/index.mjs +1744 -2732
  5. package/dist/index.mjs.map +4 -4
  6. package/docs/decisions/2026-07-21-broker-phase4-default-on.md +77 -0
  7. package/docs/decisions/2026-07-21-broker-windows-perms.md +91 -0
  8. package/docs/decisions/2026-07-22-broker-phase4-gated-on.md +99 -0
  9. package/docs/decisions/README.md +3 -0
  10. package/docs/publishing.md +29 -0
  11. package/package.json +3 -1
  12. package/scripts/build-bundle.mjs +7 -0
  13. package/scripts/postinstall.mjs +60 -1
  14. package/scripts/pty_probe.py +174 -0
  15. package/skills/real-test-pi-crew/SKILL.md +659 -0
  16. package/src/config/config.ts +42 -1
  17. package/src/config/defaults.ts +45 -1
  18. package/src/config/types.ts +19 -0
  19. package/src/extension/register.ts +6 -1
  20. package/src/extension/registration/context-builder.ts +4 -0
  21. package/src/extension/registration/lifecycle-handlers.ts +166 -3
  22. package/src/extension/registration/registration-types.ts +9 -0
  23. package/src/prompt/prompt-runtime.ts +108 -0
  24. package/src/runtime/broker-issuer.ts +37 -0
  25. package/src/runtime/child-pi-spawn.ts +53 -0
  26. package/src/runtime/child-pi.ts +42 -11
  27. package/src/runtime/crew-broker-child.ts +88 -0
  28. package/src/runtime/crew-broker-client.ts +673 -0
  29. package/src/runtime/crew-broker-tokens.ts +84 -0
  30. package/src/runtime/crew-broker.ts +1276 -0
  31. package/src/runtime/dynamic-workflow-context.ts +7 -3
  32. package/src/runtime/dynamic-workflow-runner.ts +1 -1
  33. package/src/runtime/plan-templates.ts +8 -6
  34. package/src/schema/config-schema.ts +14 -0
  35. package/src/state/mailbox.ts +43 -0
  36. package/src/ui/key-utils.ts +42 -0
  37. package/src/ui/keybinding-map.ts +29 -3
  38. package/src/ui/run-dashboard.ts +28 -0
  39. package/src/ui/settings-overlay.ts +42 -22
  40. package/src/utils/ndjson.ts +115 -0
  41. package/src/utils/session-utils.ts +30 -0
  42. package/src/utils/socket-path.ts +127 -0
  43. package/workflows/default.workflow.md +1 -1
  44. package/workflows/fast-fix.workflow.md +1 -1
  45. package/workflows/plan-execute.workflow.md +1 -1
  46. package/workflows/review.workflow.md +1 -1
@@ -0,0 +1,127 @@
1
+ /**
2
+ * socket-path.ts — Canonical broker Unix-socket / named-pipe path utilities.
3
+ *
4
+ * Moved out of the parallel-work stub `src/runtime/crew-broker-deps.ts`.
5
+ * The public surface (hashSessionId, getBrokerSocketPath,
6
+ * prepareBrokerSocketDir, removeStaleBrokerSocket) is preserved verbatim
7
+ * so importers can be updated with a single import-path change.
8
+ *
9
+ * No internal dependencies on other src/ modules — only Node built-ins.
10
+ */
11
+
12
+ import { createHash } from "node:crypto";
13
+ import * as fsp from "node:fs/promises";
14
+ import * as net from "node:net";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+
18
+ /** Default hash length for the short socket filename (8 hex chars). */
19
+ const DEFAULT_PATH_HASH_LEN = 8;
20
+
21
+ /** POSIX sun_path cap (108 bytes including the null terminator). Use 107 for the
22
+ * string portion of the path. */
23
+ const POSIX_SUN_PATH_BUDGET = 107;
24
+
25
+ /** SHA-256 hex prefix of `sessionId`. `length` defaults to 8; must be in [4, 32]. */
26
+ export function hashSessionId(sessionId: string, length: number = DEFAULT_PATH_HASH_LEN): string {
27
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
28
+ throw new Error("hashSessionId: sessionId must be a non-empty string");
29
+ }
30
+ if (!Number.isInteger(length) || length < 4 || length > 32) {
31
+ throw new Error(`hashSessionId: length must be an integer in [4, 32] (got ${length})`);
32
+ }
33
+ const hex = createHash("sha256").update(sessionId, "utf8").digest("hex");
34
+ return hex.substring(0, length);
35
+ }
36
+
37
+ /** Resolve the broker endpoint for the given session.
38
+ *
39
+ * - POSIX: `${XDG_RUNTIME_DIR || os.tmpdir()}/pi-crew-<hash8>.sock` (dir
40
+ * 0700 enforced by `prepareBrokerSocketDir`; socket 0600 by the server).
41
+ * - Windows: `\\\\.\\pipe\\pi-crew-broker-<hash8>`.
42
+ * - Throws if the encoded POSIX path exceeds sun_path (108 bytes) — the
43
+ * caller cannot fix this without changing the hash length, so fail fast. */
44
+ export function getBrokerSocketPath(sessionId: string, platform: NodeJS.Platform = process.platform): string {
45
+ const hash = hashSessionId(sessionId);
46
+ if (platform === "win32") {
47
+ return `\\\\.\\pipe\\pi-crew-broker-${hash}`;
48
+ }
49
+ const base = process.env.XDG_RUNTIME_DIR || os.tmpdir();
50
+ const sock = path.join(base, `pi-crew-${hash}.sock`);
51
+ const encoded = Buffer.byteLength(sock, "utf8");
52
+ if (encoded > POSIX_SUN_PATH_BUDGET) {
53
+ throw new Error(
54
+ `broker socket path ${encoded} bytes exceeds sun_path budget (${POSIX_SUN_PATH_BUDGET}); check XDG_RUNTIME_DIR or use a shorter hash`,
55
+ );
56
+ }
57
+ return sock;
58
+ }
59
+
60
+ /** Create the parent directory of a broker socket with mode 0700 (POSIX).
61
+ * Idempotent: if the directory already exists with the correct mode, leaves
62
+ * it alone. Refuses to operate on a symlink. Windows is a no-op (named pipes
63
+ * do not have an enclosing dir). */
64
+ export async function prepareBrokerSocketDir(sockPath: string): Promise<void> {
65
+ if (process.platform === "win32") return;
66
+ const dir = path.dirname(sockPath);
67
+ // mkdir with mode 0o700; recursive:true so nested paths work.
68
+ await fsp.mkdir(dir, { recursive: true, mode: 0o700 });
69
+ // Tighten mode if it already existed (mkdir with mode ignores on existing).
70
+ try {
71
+ await fsp.chmod(dir, 0o700);
72
+ } catch {
73
+ // ENOENT or EPERM on non-POSIX — best-effort.
74
+ }
75
+ }
76
+
77
+ /** Connect-then-unlink stale socket (herdr pattern). If a live broker is
78
+ * listening, leave the endpoint intact (EADDRINUSE will surface on bind).
79
+ * If a stale file exists with no listener, remove it. If the path is a
80
+ * symlink, refuse rather than follow.
81
+ *
82
+ * Returns "removed" when the stale entry was unlinked, "kept" when a live
83
+ * listener was detected, "absent" when no entry existed, "refused"
84
+ * when the entry is a symlink. */
85
+ export async function removeStaleBrokerSocket(
86
+ sockPath: string,
87
+ probeTimeoutMs: number = 250,
88
+ ): Promise<"removed" | "kept" | "absent" | "refused"> {
89
+ // Reject symlinks outright.
90
+ let st: Awaited<ReturnType<typeof fsp.lstat>>;
91
+ try {
92
+ st = await fsp.lstat(sockPath);
93
+ } catch (e) {
94
+ const code = (e as NodeJS.ErrnoException).code;
95
+ if (code === "ENOENT") return "absent";
96
+ throw e;
97
+ }
98
+ if (st.isSymbolicLink()) return "refused";
99
+ // Bound the probe: connect with a short timeout. If anything answers, treat as live.
100
+ const live = await new Promise<boolean>((resolve) => {
101
+ let settled = false;
102
+ const sock = net.createConnection(sockPath);
103
+ const finish = (v: boolean) => {
104
+ if (settled) return;
105
+ settled = true;
106
+ try {
107
+ sock.destroy();
108
+ } catch {
109
+ /* ignore */
110
+ }
111
+ resolve(v);
112
+ };
113
+ sock.once("connect", () => finish(true));
114
+ sock.once("error", () => finish(false));
115
+ setTimeout(() => finish(false), probeTimeoutMs);
116
+ });
117
+ if (live) return "kept";
118
+ // Stale: remove.
119
+ try {
120
+ await fsp.unlink(sockPath);
121
+ return "removed";
122
+ } catch (e) {
123
+ const code = (e as NodeJS.ErrnoException).code;
124
+ if (code === "ENOENT") return "absent";
125
+ throw e;
126
+ }
127
+ }
@@ -28,4 +28,4 @@ dependsOn: execute
28
28
  verify: true
29
29
 
30
30
  Verify completion for: {goal}
31
- Run tests ONCE (cache to .crew/cache/), read changed files from executor context. Cross-reference test output with the changes. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
31
+ Run FAST checks ONCE (cache output to .crew/cache/): `npm run test:critical && npx tsc --noEmit` (completes in <60s). Do NOT run `npm run test:unit` or `npm test` — too slow (642 files, >4 min). Cross-reference cached output with the changes. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
@@ -21,4 +21,4 @@ dependsOn: execute
21
21
  verify: true
22
22
 
23
23
  Verify the fix with available evidence.
24
- Run tests ONCE (cache to .crew/cache/), read changed files from executor context. Cross-reference test output with the fix. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
24
+ Run FAST checks ONCE (cache output to .crew/cache/): `npm run test:critical && npx tsc --noEmit` (completes in <60s). Do NOT run `npm run test:unit` or `npm test` — too slow (642 files, >4 min). Cross-reference cached output with the fix. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
@@ -27,4 +27,4 @@ dependsOn: execute
27
27
  verify: true
28
28
 
29
29
  Verify completion for: {goal}
30
- Run tests ONCE (cache to .crew/cache/), read changed files from executor context. Cross-reference test output with the changes. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
30
+ Run FAST checks ONCE (cache output to .crew/cache/): `npm run test:critical && npx tsc --noEmit` (completes in <60s). Do NOT run `npm run test:unit` or `npm test` — too slow (642 files, >4 min). Cross-reference cached output with the changes. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
@@ -28,4 +28,4 @@ role: verifier
28
28
  dependsOn: code-review, security-review
29
29
  verify: true
30
30
 
31
- Run the project test suite ONCE (cache to .crew/cache/), then cross-reference test results with reviewer and security-reviewer findings. Confirm each finding against real test output. Give PASS if findings match evidence, FAIL if critical findings are false positives or tests reveal new issues.
31
+ Run FAST checks ONCE (cache output to .crew/cache/): `npm run test:critical && npx tsc --noEmit` (completes in <60s). Do NOT run `npm run test:unit` or `npm test` — too slow (642 files, >4 min). Cross-reference cached output with reviewer and security-reviewer findings. Confirm each finding against real test output. Give PASS if findings match evidence, FAIL if critical findings are false positives or tests reveal new issues.