@nodetool-ai/reliability-harness 0.8.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.
Files changed (167) hide show
  1. package/README.md +34 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/cli.d.ts +73 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +201 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/compare.d.ts +98 -0
  8. package/dist/compare.d.ts.map +1 -0
  9. package/dist/compare.js +267 -0
  10. package/dist/compare.js.map +1 -0
  11. package/dist/core/diff.d.ts +39 -0
  12. package/dist/core/diff.d.ts.map +1 -0
  13. package/dist/core/diff.js +114 -0
  14. package/dist/core/diff.js.map +1 -0
  15. package/dist/core/golden.d.ts +50 -0
  16. package/dist/core/golden.d.ts.map +1 -0
  17. package/dist/core/golden.js +150 -0
  18. package/dist/core/golden.js.map +1 -0
  19. package/dist/core/invariants/index.d.ts +32 -0
  20. package/dist/core/invariants/index.d.ts.map +1 -0
  21. package/dist/core/invariants/index.js +44 -0
  22. package/dist/core/invariants/index.js.map +1 -0
  23. package/dist/core/invariants/leaks.d.ts +22 -0
  24. package/dist/core/invariants/leaks.d.ts.map +1 -0
  25. package/dist/core/invariants/leaks.js +55 -0
  26. package/dist/core/invariants/leaks.js.map +1 -0
  27. package/dist/core/invariants/lifecycle.d.ts +19 -0
  28. package/dist/core/invariants/lifecycle.d.ts.map +1 -0
  29. package/dist/core/invariants/lifecycle.js +95 -0
  30. package/dist/core/invariants/lifecycle.js.map +1 -0
  31. package/dist/core/invariants/protocol-validity.d.ts +4 -0
  32. package/dist/core/invariants/protocol-validity.d.ts.map +1 -0
  33. package/dist/core/invariants/protocol-validity.js +131 -0
  34. package/dist/core/invariants/protocol-validity.js.map +1 -0
  35. package/dist/core/invariants/state-machine.d.ts +29 -0
  36. package/dist/core/invariants/state-machine.d.ts.map +1 -0
  37. package/dist/core/invariants/state-machine.js +79 -0
  38. package/dist/core/invariants/state-machine.js.map +1 -0
  39. package/dist/core/invariants/status-sets.d.ts +27 -0
  40. package/dist/core/invariants/status-sets.d.ts.map +1 -0
  41. package/dist/core/invariants/status-sets.js +51 -0
  42. package/dist/core/invariants/status-sets.js.map +1 -0
  43. package/dist/core/invariants/terminal-uniqueness.d.ts +23 -0
  44. package/dist/core/invariants/terminal-uniqueness.d.ts.map +1 -0
  45. package/dist/core/invariants/terminal-uniqueness.js +99 -0
  46. package/dist/core/invariants/terminal-uniqueness.js.map +1 -0
  47. package/dist/core/invariants/types.d.ts +25 -0
  48. package/dist/core/invariants/types.d.ts.map +1 -0
  49. package/dist/core/invariants/types.js +2 -0
  50. package/dist/core/invariants/types.js.map +1 -0
  51. package/dist/core/journey.d.ts +121 -0
  52. package/dist/core/journey.d.ts.map +1 -0
  53. package/dist/core/journey.js +166 -0
  54. package/dist/core/journey.js.map +1 -0
  55. package/dist/core/normalize.d.ts +86 -0
  56. package/dist/core/normalize.d.ts.map +1 -0
  57. package/dist/core/normalize.js +182 -0
  58. package/dist/core/normalize.js.map +1 -0
  59. package/dist/core/record.d.ts +160 -0
  60. package/dist/core/record.d.ts.map +1 -0
  61. package/dist/core/record.js +74 -0
  62. package/dist/core/record.js.map +1 -0
  63. package/dist/core/stable-json.d.ts +13 -0
  64. package/dist/core/stable-json.d.ts.map +1 -0
  65. package/dist/core/stable-json.js +28 -0
  66. package/dist/core/stable-json.js.map +1 -0
  67. package/dist/drivers/anchors.d.ts +29 -0
  68. package/dist/drivers/anchors.d.ts.map +1 -0
  69. package/dist/drivers/anchors.js +48 -0
  70. package/dist/drivers/anchors.js.map +1 -0
  71. package/dist/drivers/app-headless.d.ts +39 -0
  72. package/dist/drivers/app-headless.d.ts.map +1 -0
  73. package/dist/drivers/app-headless.js +123 -0
  74. package/dist/drivers/app-headless.js.map +1 -0
  75. package/dist/drivers/browser.d.ts +34 -0
  76. package/dist/drivers/browser.d.ts.map +1 -0
  77. package/dist/drivers/browser.js +176 -0
  78. package/dist/drivers/browser.js.map +1 -0
  79. package/dist/drivers/cli.d.ts +13 -0
  80. package/dist/drivers/cli.d.ts.map +1 -0
  81. package/dist/drivers/cli.js +82 -0
  82. package/dist/drivers/cli.js.map +1 -0
  83. package/dist/drivers/fixture-nodes.d.ts +124 -0
  84. package/dist/drivers/fixture-nodes.d.ts.map +1 -0
  85. package/dist/drivers/fixture-nodes.js +189 -0
  86. package/dist/drivers/fixture-nodes.js.map +1 -0
  87. package/dist/drivers/index.d.ts +18 -0
  88. package/dist/drivers/index.d.ts.map +1 -0
  89. package/dist/drivers/index.js +12 -0
  90. package/dist/drivers/index.js.map +1 -0
  91. package/dist/drivers/kernel.d.ts +8 -0
  92. package/dist/drivers/kernel.d.ts.map +1 -0
  93. package/dist/drivers/kernel.js +247 -0
  94. package/dist/drivers/kernel.js.map +1 -0
  95. package/dist/drivers/packaged.d.ts +28 -0
  96. package/dist/drivers/packaged.d.ts.map +1 -0
  97. package/dist/drivers/packaged.js +303 -0
  98. package/dist/drivers/packaged.js.map +1 -0
  99. package/dist/drivers/python-bridge.d.ts +22 -0
  100. package/dist/drivers/python-bridge.d.ts.map +1 -0
  101. package/dist/drivers/python-bridge.js +48 -0
  102. package/dist/drivers/python-bridge.js.map +1 -0
  103. package/dist/drivers/registry.d.ts +11 -0
  104. package/dist/drivers/registry.d.ts.map +1 -0
  105. package/dist/drivers/registry.js +18 -0
  106. package/dist/drivers/registry.js.map +1 -0
  107. package/dist/drivers/relay-fields.d.ts +70 -0
  108. package/dist/drivers/relay-fields.d.ts.map +1 -0
  109. package/dist/drivers/relay-fields.js +104 -0
  110. package/dist/drivers/relay-fields.js.map +1 -0
  111. package/dist/drivers/repo-root.d.ts +9 -0
  112. package/dist/drivers/repo-root.d.ts.map +1 -0
  113. package/dist/drivers/repo-root.js +39 -0
  114. package/dist/drivers/repo-root.js.map +1 -0
  115. package/dist/drivers/spawn-cli.d.ts +8 -0
  116. package/dist/drivers/spawn-cli.d.ts.map +1 -0
  117. package/dist/drivers/spawn-cli.js +41 -0
  118. package/dist/drivers/spawn-cli.js.map +1 -0
  119. package/dist/drivers/types.d.ts +18 -0
  120. package/dist/drivers/types.d.ts.map +1 -0
  121. package/dist/drivers/types.js +2 -0
  122. package/dist/drivers/types.js.map +1 -0
  123. package/dist/drivers/ws-server.d.ts +8 -0
  124. package/dist/drivers/ws-server.d.ts.map +1 -0
  125. package/dist/drivers/ws-server.js +382 -0
  126. package/dist/drivers/ws-server.js.map +1 -0
  127. package/dist/faults/bridge-faults.d.ts +7 -0
  128. package/dist/faults/bridge-faults.d.ts.map +1 -0
  129. package/dist/faults/bridge-faults.js +90 -0
  130. package/dist/faults/bridge-faults.js.map +1 -0
  131. package/dist/faults/host-faults.d.ts +40 -0
  132. package/dist/faults/host-faults.d.ts.map +1 -0
  133. package/dist/faults/host-faults.js +70 -0
  134. package/dist/faults/host-faults.js.map +1 -0
  135. package/dist/faults/index.d.ts +21 -0
  136. package/dist/faults/index.d.ts.map +1 -0
  137. package/dist/faults/index.js +29 -0
  138. package/dist/faults/index.js.map +1 -0
  139. package/dist/faults/net-proxy.d.ts +75 -0
  140. package/dist/faults/net-proxy.d.ts.map +1 -0
  141. package/dist/faults/net-proxy.js +305 -0
  142. package/dist/faults/net-proxy.js.map +1 -0
  143. package/dist/faults/provider-faults.d.ts +12 -0
  144. package/dist/faults/provider-faults.d.ts.map +1 -0
  145. package/dist/faults/provider-faults.js +110 -0
  146. package/dist/faults/provider-faults.js.map +1 -0
  147. package/dist/faults/registry.d.ts +17 -0
  148. package/dist/faults/registry.d.ts.map +1 -0
  149. package/dist/faults/registry.js +15 -0
  150. package/dist/faults/registry.js.map +1 -0
  151. package/dist/faults/types.d.ts +42 -0
  152. package/dist/faults/types.d.ts.map +1 -0
  153. package/dist/faults/types.js +2 -0
  154. package/dist/faults/types.js.map +1 -0
  155. package/dist/faults/ws-faults.d.ts +6 -0
  156. package/dist/faults/ws-faults.d.ts.map +1 -0
  157. package/dist/faults/ws-faults.js +48 -0
  158. package/dist/faults/ws-faults.js.map +1 -0
  159. package/dist/faults/ws-proxy.d.ts +36 -0
  160. package/dist/faults/ws-proxy.d.ts.map +1 -0
  161. package/dist/faults/ws-proxy.js +40 -0
  162. package/dist/faults/ws-proxy.js.map +1 -0
  163. package/dist/index.d.ts +12 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +12 -0
  166. package/dist/index.js.map +1 -0
  167. package/package.json +75 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Repo-root resolution shared by every C4 driver that speaks a real entry
3
+ * point instead of touching internals (§12: "drivers speak entry points,
4
+ * never internals — the kernel driver is the only one allowed to touch
5
+ * `ExecutionSession` directly"). Spawning the real `nodetool` CLI (and, for
6
+ * `browser.ts`, the real Playwright harness) is what keeps `cli.ts` and
7
+ * `app-headless.ts` from taking a static import on `@nodetool-ai/cli` — that
8
+ * package's own `reliability` command (C4 item 6) imports *this* package, so
9
+ * a static import the other way would be a circular workspace dependency.
10
+ */
11
+ import { existsSync } from "node:fs";
12
+ import { dirname, join } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ /** Walks up from this module to the checked-out repo root (the directory
15
+ * whose `packages/cli/src/nodetool.ts` exists) — works from both `src/`
16
+ * (ts-node/tsx dev) and `dist/` (built) locations since it only depends on
17
+ * directory depth, not file extension. */
18
+ export function findRepoRoot() {
19
+ const start = dirname(fileURLToPath(import.meta.url));
20
+ let dir = start;
21
+ for (let i = 0; i < 12; i++) {
22
+ if (existsSync(join(dir, "package.json")) &&
23
+ existsSync(join(dir, "packages", "cli", "src", "nodetool.ts"))) {
24
+ return dir;
25
+ }
26
+ const parent = dirname(dir);
27
+ if (parent === dir)
28
+ break;
29
+ dir = parent;
30
+ }
31
+ throw new Error(`could not locate the nodetool repo root above ${start}`);
32
+ }
33
+ /** The workspace-hoisted `tsx` binary, falling back to a bare `tsx` for a
34
+ * shell to resolve if the root install ever changes shape. */
35
+ export function resolveTsxBin(repoRoot) {
36
+ const bin = join(repoRoot, "node_modules", ".bin", process.platform === "win32" ? "tsx.cmd" : "tsx");
37
+ return existsSync(bin) ? bin : "tsx";
38
+ }
39
+ //# sourceMappingURL=repo-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-root.js","sourceRoot":"","sources":["../../src/drivers/repo-root.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;0CAG0C;AAC1C,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IACE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YACrC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAC9D,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED;8DAC8D;AAC9D,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,GAAG,GAAG,IAAI,CACd,QAAQ,EACR,cAAc,EACd,MAAM,EACN,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CACjD,CAAC;IACF,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;AACvC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface CliRunResult {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ repoRoot: string;
6
+ }
7
+ export declare function spawnNodetoolCli(args: string[], timeoutMs: number): Promise<CliRunResult>;
8
+ //# sourceMappingURL=spawn-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-cli.d.ts","sourceRoot":"","sources":["../../src/drivers/spawn-cli.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAgCvB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Spawns the real `nodetool` CLI against the checked-out source, via `tsx` —
3
+ * the same door a user gets (`npm run dev:nodetool -- <args>`), shared by
4
+ * `cli.ts` and `app-headless.ts`. No compiled `dist/` dependency, so it works
5
+ * in the same tree `npm run dev` does.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ import { join } from "node:path";
9
+ import { findRepoRoot, resolveTsxBin } from "./repo-root.js";
10
+ export async function spawnNodetoolCli(args, timeoutMs) {
11
+ const repoRoot = findRepoRoot();
12
+ const tsxBin = resolveTsxBin(repoRoot);
13
+ const nodetoolEntry = join(repoRoot, "packages", "cli", "src", "nodetool.ts");
14
+ return new Promise((resolvePromise) => {
15
+ const child = spawn(tsxBin, [nodetoolEntry, ...args], {
16
+ cwd: repoRoot,
17
+ stdio: ["ignore", "pipe", "pipe"]
18
+ });
19
+ let stdout = "";
20
+ let stderr = "";
21
+ child.stdout.on("data", (buf) => (stdout += buf.toString()));
22
+ child.stderr.on("data", (buf) => (stderr += buf.toString()));
23
+ const timer = setTimeout(() => {
24
+ try {
25
+ child.kill("SIGKILL");
26
+ }
27
+ catch {
28
+ // Already exited.
29
+ }
30
+ }, timeoutMs);
31
+ child.on("exit", (code) => {
32
+ clearTimeout(timer);
33
+ resolvePromise({ code: code ?? 1, stdout, stderr, repoRoot });
34
+ });
35
+ child.on("error", (err) => {
36
+ clearTimeout(timer);
37
+ resolvePromise({ code: 127, stdout, stderr: stderr + String(err), repoRoot });
38
+ });
39
+ });
40
+ }
41
+ //# sourceMappingURL=spawn-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-cli.js","sourceRoot":"","sources":["../../src/drivers/spawn-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAS7D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAc,EACd,SAAiB;IAEjB,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;IAE9E,OAAO,IAAI,OAAO,CAAe,CAAC,cAAc,EAAE,EAAE;QAClD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,EAAE;YACpD,GAAG,EAAE,QAAQ;YACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACrE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAErE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB;YACpB,CAAC;QACH,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,cAAc,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The driver contract every execution surface implements (§12).
3
+ *
4
+ * A driver runs one loaded {@link Journey} to completion on its surface and
5
+ * returns a {@link RunRecord} — every frame, both directions, surface-tagged
6
+ * — so the same journey compares cleanly across surfaces via `normalize.ts`
7
+ * + `diff.ts`. C2 ships `kernel` (the oracle) and `ws-server`; C4 adds
8
+ * `cli`/`browser`/`app-headless`/`packaged` behind this same interface.
9
+ */
10
+ import type { Journey } from "../core/journey.js";
11
+ import type { RunRecord } from "../core/record.js";
12
+ export interface RunDriver {
13
+ /** Matches `JourneySurface` (`core/journey.ts`) — e.g. "kernel", "ws-server". */
14
+ readonly name: string;
15
+ /** Runs `journey` to a terminal state on this surface and records every frame. */
16
+ run(journey: Journey): Promise<RunRecord>;
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/drivers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAC3C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/drivers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { Journey } from "../core/journey.js";
2
+ import { type RunRecord } from "../core/record.js";
3
+ import type { RunDriver } from "./types.js";
4
+ export declare class WsServerDriver implements RunDriver {
5
+ readonly name = "ws-server";
6
+ run(journey: Journey): Promise<RunRecord>;
7
+ }
8
+ //# sourceMappingURL=ws-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../../src/drivers/ws-server.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,OAAO,EAAsB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoE5C,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,eAAe;IAEtB,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAgV9C;CACF"}
@@ -0,0 +1,382 @@
1
+ /**
2
+ * WS-server driver (§12) — starts the hermetic test backend
3
+ * (`createTestUiServer`, the helper `packages/websocket/src/e2e-server.ts`
4
+ * and the websocket test suite both build on) and drives it with a real
5
+ * msgpack `ws` client, exactly the wire protocol a real client speaks. Never
6
+ * constructs `ExecutionSession`/`WorkflowRunner` directly — the kernel
7
+ * driver (`kernel.ts`) is the only place allowed to do that (§12: "kernel
8
+ * surface is the oracle").
9
+ *
10
+ * Records both directions: outbound command envelopes as
11
+ * `client_to_server` frames, every inbound `ProcessingMessage` as
12
+ * `server_to_client` — the ws-server driver is the one surface where that
13
+ * distinction is real (the kernel driver has no wire).
14
+ */
15
+ import { WebSocket } from "ws";
16
+ import { createTestUiServer, packWebSocketMessage, unpackWebSocketMessage } from "@nodetool-ai/websocket";
17
+ import { NodeRegistry } from "@nodetool-ai/node-sdk";
18
+ import { registerBaseNodes } from "@nodetool-ai/base-nodes";
19
+ import { resolvePythonNodeExecutor } from "@nodetool-ai/runtime";
20
+ import { makeFrame } from "../core/record.js";
21
+ import { AnchorWaiter } from "./anchors.js";
22
+ import { registerFixtureNodes } from "./fixture-nodes.js";
23
+ import { journeyPythonBridgeFactory } from "./python-bridge.js";
24
+ import { isConnectionControlMessage, stripRelayOnlyFields } from "./relay-fields.js";
25
+ import { maybeFrontWithProxy } from "../faults/ws-proxy.js";
26
+ const TERMINAL_JOB_STATUSES = new Set([
27
+ "completed",
28
+ "failed",
29
+ "cancelled",
30
+ "suspended"
31
+ ]);
32
+ /** How long to let the server's post-disconnect cleanup settle before
33
+ * recording the slot counters as final. Cleanup is asynchronous (the runner
34
+ * finishes streaming, then frees the slot), so a snapshot taken the instant
35
+ * the socket closes would report a slot that is merely about to be freed. */
36
+ const SLOT_SETTLE_TIMEOUT_MS = 2000;
37
+ const SLOT_SETTLE_POLL_MS = 10;
38
+ /**
39
+ * Post-run WS slot accounting (§6 "Cleanup and leaks"), summed over every
40
+ * connection this run opened. Polls until both counters reach zero or the
41
+ * settle window elapses, then reports whatever it measured — a genuine leak
42
+ * survives the wait and is recorded as non-zero.
43
+ */
44
+ async function settledSlotCounters(runners) {
45
+ const total = () => runners.reduce((acc, runner) => {
46
+ const slots = runner.jobs.slotCounters;
47
+ return {
48
+ activeJobs: acc.activeJobs + slots.activeJobs,
49
+ startingJobs: acc.startingJobs + slots.startingJobs
50
+ };
51
+ }, { activeJobs: 0, startingJobs: 0 });
52
+ const deadline = Date.now() + SLOT_SETTLE_TIMEOUT_MS;
53
+ let slots = total();
54
+ while ((slots.activeJobs > 0 || slots.startingJobs > 0) && Date.now() < deadline) {
55
+ await new Promise((resolve) => setTimeout(resolve, SLOT_SETTLE_POLL_MS));
56
+ slots = total();
57
+ }
58
+ return {
59
+ at: "after",
60
+ // Kernel-internal counters belong to the process the ws-server runs the
61
+ // kernel in; this driver reaches the server only over the wire, so it
62
+ // reports the accounting it can actually observe and leaves the rest at
63
+ // zero (the kernel driver is the surface that measures those).
64
+ liveActors: 0,
65
+ pendingControlResponses: 0,
66
+ pendingTimers: 0,
67
+ pythonBridgePendingRequests: 0,
68
+ ...slots
69
+ };
70
+ }
71
+ function requireJobId(jobId, journeyName, action) {
72
+ if (!jobId) {
73
+ throw new Error(`journey "${journeyName}": "${action}" interaction fired before a job_id was observed`);
74
+ }
75
+ return jobId;
76
+ }
77
+ export class WsServerDriver {
78
+ name = "ws-server";
79
+ async run(journey) {
80
+ // Task D3: mirror the kernel driver's Python-bridge wiring
81
+ // (`python-bridge.ts`) so journey 7 (`python-node-workflow`) behaves the
82
+ // same on both surfaces. `createTestUiServer`'s default `resolveExecutor`
83
+ // only ever calls `registry.resolve` — unlike `ExecutionSession`, it has
84
+ // no built-in bridge fallback — so this driver builds one itself: a
85
+ // throwaway registry (same real base nodes + harness fixtures the actual
86
+ // server registry ends up with, via `configureRegistry` below) just to
87
+ // answer "can the TS registry resolve this type", exactly the predicate
88
+ // `connectPythonBridgeForGraph` needs. A graph with no unresolved node
89
+ // type (every journey but #7) never spawns anything.
90
+ const bridgeCheckRegistry = new NodeRegistry();
91
+ registerBaseNodes(bridgeCheckRegistry);
92
+ registerFixtureNodes(bridgeCheckRegistry);
93
+ const workflowNodes = (journey.workflow.nodes ??
94
+ []);
95
+ const bridge = await journeyPythonBridgeFactory(workflowNodes, (t) => bridgeCheckRegistry.has(t));
96
+ let liveRegistry = null;
97
+ // §6 "Cleanup and leaks" needs the server's own WS slot accounting, which
98
+ // lives on the per-connection runner. `createTestUiServer` builds one per
99
+ // upgrade and keeps it private; this hook is the only way to read it.
100
+ const runners = [];
101
+ const srv = createTestUiServer({
102
+ host: "127.0.0.1",
103
+ port: 0,
104
+ onRunnerCreated: (runner) => runners.push(runner),
105
+ configureRegistry: (registry) => {
106
+ registerFixtureNodes(registry);
107
+ liveRegistry = registry;
108
+ },
109
+ resolveExecutor: (node) => {
110
+ const registry = liveRegistry;
111
+ if (registry?.has(node.type))
112
+ return registry.resolve(node);
113
+ const pythonExecutor = resolvePythonNodeExecutor(bridge, node);
114
+ if (pythonExecutor)
115
+ return pythonExecutor;
116
+ // Falls through to the registry's own "Unknown node type" error —
117
+ // same failure shape a bridge-less run would produce.
118
+ return registry.resolve(node);
119
+ },
120
+ // Without this, `Graph.loadFromDict` (node-sdk) silently drops a
121
+ // bridge-only node type (and its edges) during hydration — it never
122
+ // even reaches `resolveExecutor` above, so a bridge CONNECT failure
123
+ // (never-ready/epipe/version-mismatch: `bridge` ends up `null`) would
124
+ // otherwise make the node vanish from the graph entirely instead of
125
+ // failing at execution the way the kernel driver's
126
+ // `hydrateGraphNodeFlags` does (it defaults metadata for ANY
127
+ // class-less node rather than dropping it — see that function's own
128
+ // doc comment). Mirrors that: bridge metadata when connected, a
129
+ // generic pass-through shape when not, so either way the node stays in
130
+ // the graph and the real failure surfaces later, at
131
+ // `resolveExecutor`/`execute()` — the same failure shape on both
132
+ // surfaces, just like the kernel driver.
133
+ resolveUnknownNodeType: async (nodeType) => {
134
+ const meta = bridge?.hasNodeType(nodeType)
135
+ ? bridge.getNodeMetadata().find((m) => m.node_type === nodeType)
136
+ : undefined;
137
+ if (meta) {
138
+ return {
139
+ nodeType,
140
+ propertyTypes: Object.fromEntries(meta.properties.map((p) => [p.name, p.type.type])),
141
+ outputs: Object.fromEntries(meta.outputs.map((o) => [o.name, o.type.type])),
142
+ supportsDynamicInputs: false,
143
+ descriptorDefaults: {}
144
+ };
145
+ }
146
+ return {
147
+ nodeType,
148
+ propertyTypes: { value: "any" },
149
+ outputs: { out: "any" },
150
+ supportsDynamicInputs: true,
151
+ descriptorDefaults: {}
152
+ };
153
+ }
154
+ });
155
+ await srv.listen();
156
+ const address = srv.server.address();
157
+ const serverPort = typeof address === "object" && address !== null ? address.port : 7777;
158
+ // Task D2: when a ws-* fault (`faults/ws-faults.ts`) is currently active,
159
+ // front the real server with a `WsFaultProxy` and connect this driver's
160
+ // client to the proxy instead — every ws-* fault applies transparently
161
+ // to this one client-connection code path below. `wsFrontEnd` stays
162
+ // `null` (the overwhelming majority of runs) when no ws fault is
163
+ // configured, in which case this is exactly the pre-D2 direct connection.
164
+ const wsFrontEnd = await maybeFrontWithProxy("127.0.0.1", serverPort);
165
+ const port = wsFrontEnd ? wsFrontEnd.port : serverPort;
166
+ const frames = [];
167
+ let counters = null;
168
+ let seq = 0;
169
+ const waiter = new AnchorWaiter();
170
+ let jobId = null;
171
+ let terminalStatus = null;
172
+ let terminalError = null;
173
+ // ws-server relays two `job_update`s per status the kernel driver only
174
+ // ever sees once: an eager "running" ack sent before the kernel's own
175
+ // relayed `job_update running` ("Authoritative SDK runs can use [the
176
+ // kernel's] update and avoid an otherwise duplicate WebSocket frame.
177
+ // Legacy clients keep the eager acknowledgement" —
178
+ // `websocket-client-session.ts`), and, for any non-"completed" terminal
179
+ // status, an "authoritative terminal snapshot" appended after the
180
+ // relayed one because the kernel's own terminal frame never carries a
181
+ // `result` field.
182
+ //
183
+ // Both are recorded, tagged `redundant` (see `RunFrame.redundant`) rather
184
+ // than dropped: dropping every repeat, as this driver used to, also drops
185
+ // the duplicates that would be real bugs, leaving "exactly one terminal
186
+ // job_update" with nothing to detect on the one surface that has a wire.
187
+ // The tag is narrow — a repeat only earns it by matching the documented
188
+ // shape below — so any other duplicate stays visible to the invariants
189
+ // and to the cross-surface diff.
190
+ const jobUpdateStatusCounts = new Map();
191
+ const taggedReasons = new Set();
192
+ let cancelRequested = false;
193
+ /** The reason tag for a repeated `job_update`, or undefined when this
194
+ * surface has no documented reason to have sent it twice. */
195
+ const documentedRedundancy = (status, message) => {
196
+ const claim = (reason) => {
197
+ // One repeat per documented reason. A third frame is not something
198
+ // the protocol documents, so it stays visible.
199
+ if (taggedReasons.has(reason))
200
+ return undefined;
201
+ taggedReasons.add(reason);
202
+ return reason;
203
+ };
204
+ if (status === "running")
205
+ return claim("ws-eager-running-ack");
206
+ if (TERMINAL_JOB_STATUSES.has(status) && "result" in message) {
207
+ return claim("ws-authoritative-terminal-snapshot");
208
+ }
209
+ // `cancel_job` is acknowledged with a `cancelled` job_update right away
210
+ // (`websocket-client-session.ts`'s cancel handler: "the runner's own
211
+ // cleanup can lag"), which the kernel's own relayed terminal then
212
+ // repeats. Only ever allowed for a cancel this driver actually asked
213
+ // for.
214
+ if (status === "cancelled" && cancelRequested) {
215
+ return claim("ws-eager-cancel-ack");
216
+ }
217
+ return undefined;
218
+ };
219
+ let resolveTerminal;
220
+ const terminalPromise = new Promise((resolve) => {
221
+ resolveTerminal = resolve;
222
+ });
223
+ const ws = new WebSocket(`ws://127.0.0.1:${port}/ws`);
224
+ ws.on("message", (data, isBinary) => {
225
+ const raw = (isBinary
226
+ ? unpackWebSocketMessage(data)
227
+ : JSON.parse(data.toString("utf8")));
228
+ // This surface runs the same relay as `packaged.ts`, so it receives the
229
+ // same connection-scoped control frames on the same wall-clock timers.
230
+ if (isConnectionControlMessage(raw))
231
+ return;
232
+ const message = stripRelayOnlyFields(raw);
233
+ waiter.notify(message);
234
+ if (typeof message["job_id"] === "string") {
235
+ jobId = message["job_id"];
236
+ }
237
+ // Command acks (`{message: "Job started", ...}` for run_job/cancel_job/
238
+ // stream_input/end_input_stream) carry no `type` at all — they are not
239
+ // `ProcessingMessage`s, just a reply to the command envelope this
240
+ // driver already recorded as its own `client_to_server` frame. Not
241
+ // recording them keeps this surface's `server_to_client` frames
242
+ // apples-to-apples with the kernel driver's raw message stream.
243
+ if (typeof message["type"] !== "string")
244
+ return;
245
+ if (message["type"] === "job_update") {
246
+ const status = String(message["status"]);
247
+ const seenBefore = (jobUpdateStatusCounts.get(status) ?? 0) > 0;
248
+ jobUpdateStatusCounts.set(status, (jobUpdateStatusCounts.get(status) ?? 0) + 1);
249
+ const redundant = seenBefore
250
+ ? documentedRedundancy(status, message)
251
+ : undefined;
252
+ const frame = makeFrame(seq++, "ws-server", "server_to_client", message);
253
+ frames.push(redundant === undefined ? frame : { ...frame, redundant });
254
+ if (!seenBefore && TERMINAL_JOB_STATUSES.has(status)) {
255
+ terminalStatus = status;
256
+ terminalError =
257
+ typeof message["error"] === "string" ? message["error"] : null;
258
+ resolveTerminal();
259
+ }
260
+ return;
261
+ }
262
+ frames.push(makeFrame(seq++, "ws-server", "server_to_client", message));
263
+ });
264
+ await new Promise((resolve, reject) => {
265
+ ws.once("open", () => resolve());
266
+ ws.once("error", reject);
267
+ });
268
+ const send = (command, data) => {
269
+ const envelope = { command, data };
270
+ frames.push(makeFrame(seq++, "ws-server", "client_to_server", envelope));
271
+ ws.send(packWebSocketMessage(envelope));
272
+ };
273
+ try {
274
+ const interactions = journey.interactions.length > 0 ? journey.interactions : [{ action: "run" }];
275
+ for (const interaction of interactions) {
276
+ if (interaction.at) {
277
+ // Same race-free ordering rule as the kernel driver (anchors.ts):
278
+ // no `await` happens between a "run"'s `send()` and this wait, so
279
+ // the waiter is registered before the ws `message` handler (which
280
+ // runs on Node's event loop, not inline) can deliver the frame.
281
+ await waiter.wait(interaction.at);
282
+ }
283
+ switch (interaction.action) {
284
+ case "run":
285
+ send("run_job", {
286
+ graph: journey.workflow,
287
+ workflow_id: journey.manifest.name,
288
+ params: journey.manifest.params,
289
+ // Hermetic and driver-symmetric: the kernel driver never
290
+ // touches the jobs DB either, so neither surface depends on
291
+ // persistence to complete a journey. Deliberately NOT
292
+ // `require_terminal_result` — that also renames Output nodes'
293
+ // terminal keys to their public `properties.name`
294
+ // (`rewriteOutputNames`), which the kernel driver doesn't do
295
+ // either; every journey's `Output` nodes already carry an
296
+ // explicit top-level `name` so both surfaces agree without it.
297
+ execution_options: { persistence: "session" }
298
+ });
299
+ break;
300
+ case "cancel":
301
+ cancelRequested = true;
302
+ send("cancel_job", {
303
+ job_id: requireJobId(jobId, journey.manifest.name, "cancel")
304
+ });
305
+ break;
306
+ case "stream_input":
307
+ if (!interaction.nodeId) {
308
+ throw new Error(`journey "${journey.manifest.name}": "stream_input" interaction needs nodeId`);
309
+ }
310
+ send("stream_input", {
311
+ job_id: requireJobId(jobId, journey.manifest.name, "stream_input"),
312
+ input: interaction.nodeId,
313
+ value: interaction.value
314
+ });
315
+ break;
316
+ case "end_input_stream":
317
+ if (!interaction.nodeId) {
318
+ throw new Error(`journey "${journey.manifest.name}": "end_input_stream" interaction needs nodeId`);
319
+ }
320
+ send("end_input_stream", {
321
+ job_id: requireJobId(jobId, journey.manifest.name, "end_input_stream"),
322
+ input: interaction.nodeId
323
+ });
324
+ break;
325
+ case "reconnect":
326
+ throw new Error(`journey "${journey.manifest.name}": ws-server driver does not yet ` +
327
+ `support the "reconnect" interaction (C4)`);
328
+ }
329
+ }
330
+ // Task D2: a black-hole-style ws fault (`ws-drop-no-fin`,
331
+ // `ws-stall-reads`) can leave `terminalPromise` unresolved forever —
332
+ // the whole point of those faults is that no more frames arrive. §9's
333
+ // "no run hangs forever" applies to fault runs too, so race the wait
334
+ // against the journey's own declared timeout instead of adding one
335
+ // only for the happy path.
336
+ let timedOut = false;
337
+ let timeoutTimer;
338
+ await Promise.race([
339
+ terminalPromise,
340
+ new Promise((resolve) => {
341
+ timeoutTimer = setTimeout(() => {
342
+ timedOut = true;
343
+ resolve();
344
+ }, journey.manifest.timeoutMs);
345
+ })
346
+ ]);
347
+ clearTimeout(timeoutTimer);
348
+ if (timedOut && terminalStatus === null) {
349
+ terminalStatus = "timeout";
350
+ terminalError =
351
+ `journey "${journey.manifest.name}": ws-server driver timed out after ` +
352
+ `${journey.manifest.timeoutMs}ms waiting for a terminal job_update`;
353
+ }
354
+ }
355
+ finally {
356
+ // Leak accounting is measured after the connection is gone, not at the
357
+ // terminal frame: tearing the socket down is what ends a run whose
358
+ // client stopped hearing from the server at all (the black-hole ws
359
+ // faults), and the server's own cleanup runs on disconnect.
360
+ ws.close();
361
+ await wsFrontEnd?.close();
362
+ counters = await settledSlotCounters(runners);
363
+ await srv.close();
364
+ bridge?.close();
365
+ }
366
+ return {
367
+ journeyId: journey.manifest.name,
368
+ surface: this.name,
369
+ jobId,
370
+ workflowId: journey.manifest.name,
371
+ startedAt: frames.length > 0 ? frames[0].ts : null,
372
+ finishedAt: frames.length > 0 ? frames[frames.length - 1].ts : null,
373
+ durationMs: null,
374
+ status: terminalStatus ?? "unknown",
375
+ error: terminalError,
376
+ params: journey.manifest.params,
377
+ frames,
378
+ resourceCounters: counters ? [counters] : []
379
+ };
380
+ }
381
+ }
382
+ //# sourceMappingURL=ws-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../../src/drivers/ws-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { FaultModule } from "./types.js";
2
+ export declare const BRIDGE_FAULT_MODULES: readonly FaultModule[];
3
+ /** Registers every bridge-seam fault module. Called once from
4
+ * `faults/index.ts`'s module-load side effect (task D1's "call once, own
5
+ * module state" contract) — safe to call again, last-write-wins. */
6
+ export declare function registerBridgeFaultModules(): void;
7
+ //# sourceMappingURL=bridge-faults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-faults.d.ts","sourceRoot":"","sources":["../../src/faults/bridge-faults.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAgB,WAAW,EAAiB,MAAM,YAAY,CAAC;AAgE3E,eAAO,MAAM,oBAAoB,EAAE,SAAS,WAAW,EAE3B,CAAC;AAE7B;;oEAEoE;AACpE,wBAAgB,0BAA0B,IAAI,IAAI,CAIjD"}