patchwork-os 0.2.0-beta.2 → 0.2.0-beta.4

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 (261) hide show
  1. package/README.bridge.md +5 -5
  2. package/README.md +244 -30
  3. package/dist/activityLog.d.ts +6 -0
  4. package/dist/activityLog.js +10 -1
  5. package/dist/activityLog.js.map +1 -1
  6. package/dist/analyticsPrefs.d.ts +35 -2
  7. package/dist/analyticsPrefs.js +120 -21
  8. package/dist/analyticsPrefs.js.map +1 -1
  9. package/dist/analyticsSend.js +5 -1
  10. package/dist/analyticsSend.js.map +1 -1
  11. package/dist/approvalHttp.js +25 -8
  12. package/dist/approvalHttp.js.map +1 -1
  13. package/dist/approvalQueue.d.ts +44 -1
  14. package/dist/approvalQueue.js +117 -0
  15. package/dist/approvalQueue.js.map +1 -1
  16. package/dist/automation.d.ts +3 -3
  17. package/dist/automation.js +12 -5
  18. package/dist/automation.js.map +1 -1
  19. package/dist/bridge.d.ts +2 -0
  20. package/dist/bridge.js +140 -8
  21. package/dist/bridge.js.map +1 -1
  22. package/dist/bridgeLockDiscovery.d.ts +27 -1
  23. package/dist/bridgeLockDiscovery.js +38 -11
  24. package/dist/bridgeLockDiscovery.js.map +1 -1
  25. package/dist/claudeOrchestrator.js +27 -10
  26. package/dist/claudeOrchestrator.js.map +1 -1
  27. package/dist/commands/dashboard.js +8 -1
  28. package/dist/commands/dashboard.js.map +1 -1
  29. package/dist/commands/install.js +3 -0
  30. package/dist/commands/install.js.map +1 -1
  31. package/dist/commands/patchworkInit.d.ts +5 -0
  32. package/dist/commands/patchworkInit.js +89 -7
  33. package/dist/commands/patchworkInit.js.map +1 -1
  34. package/dist/commands/recipe.d.ts +51 -0
  35. package/dist/commands/recipe.js +353 -2
  36. package/dist/commands/recipe.js.map +1 -1
  37. package/dist/commands/recipeInstall.js +6 -3
  38. package/dist/commands/recipeInstall.js.map +1 -1
  39. package/dist/commands/task.js +2 -2
  40. package/dist/commands/task.js.map +1 -1
  41. package/dist/commitIssueLinkLog.d.ts +16 -0
  42. package/dist/commitIssueLinkLog.js +87 -4
  43. package/dist/commitIssueLinkLog.js.map +1 -1
  44. package/dist/config.d.ts +29 -3
  45. package/dist/config.js +77 -21
  46. package/dist/config.js.map +1 -1
  47. package/dist/connectorRoutes.js +1 -1
  48. package/dist/connectorRoutes.js.map +1 -1
  49. package/dist/connectors/asana.js +4 -3
  50. package/dist/connectors/asana.js.map +1 -1
  51. package/dist/connectors/confluence.js +35 -0
  52. package/dist/connectors/confluence.js.map +1 -1
  53. package/dist/connectors/datadog.js +33 -4
  54. package/dist/connectors/datadog.js.map +1 -1
  55. package/dist/connectors/discord.js +5 -4
  56. package/dist/connectors/discord.js.map +1 -1
  57. package/dist/connectors/gitlab.js +7 -1
  58. package/dist/connectors/gitlab.js.map +1 -1
  59. package/dist/connectors/mcpOAuth.js +71 -6
  60. package/dist/connectors/mcpOAuth.js.map +1 -1
  61. package/dist/connectors/slack.d.ts +1 -1
  62. package/dist/connectors/slack.js +56 -4
  63. package/dist/connectors/slack.js.map +1 -1
  64. package/dist/connectors/tokenStorage.js +56 -14
  65. package/dist/connectors/tokenStorage.js.map +1 -1
  66. package/dist/decisionTraceLog.d.ts +28 -0
  67. package/dist/decisionTraceLog.js +115 -7
  68. package/dist/decisionTraceLog.js.map +1 -1
  69. package/dist/drivers/claude/subprocess.js +22 -3
  70. package/dist/drivers/claude/subprocess.js.map +1 -1
  71. package/dist/drivers/gemini/index.js +19 -3
  72. package/dist/drivers/gemini/index.js.map +1 -1
  73. package/dist/extensionClient.d.ts +29 -4
  74. package/dist/extensionClient.js +26 -11
  75. package/dist/extensionClient.js.map +1 -1
  76. package/dist/featureFlags.d.ts +76 -0
  77. package/dist/featureFlags.js +153 -3
  78. package/dist/featureFlags.js.map +1 -1
  79. package/dist/fileLockSync.d.ts +67 -0
  80. package/dist/fileLockSync.js +126 -0
  81. package/dist/fileLockSync.js.map +1 -0
  82. package/dist/fp/automationInterpreter.d.ts +6 -0
  83. package/dist/fp/automationInterpreter.js +15 -2
  84. package/dist/fp/automationInterpreter.js.map +1 -1
  85. package/dist/fp/automationState.d.ts +1 -1
  86. package/dist/fp/automationState.js +10 -0
  87. package/dist/fp/automationState.js.map +1 -1
  88. package/dist/fp/commandDescription.js +7 -1
  89. package/dist/fp/commandDescription.js.map +1 -1
  90. package/dist/fsWatchWithFallback.d.ts +36 -0
  91. package/dist/fsWatchWithFallback.js +127 -0
  92. package/dist/fsWatchWithFallback.js.map +1 -0
  93. package/dist/index.js +797 -75
  94. package/dist/index.js.map +1 -1
  95. package/dist/installGuard.js +6 -2
  96. package/dist/installGuard.js.map +1 -1
  97. package/dist/lockfile.js +31 -4
  98. package/dist/lockfile.js.map +1 -1
  99. package/dist/patchworkConfig.js +13 -3
  100. package/dist/patchworkConfig.js.map +1 -1
  101. package/dist/pluginLoader.js +10 -1
  102. package/dist/pluginLoader.js.map +1 -1
  103. package/dist/pluginWatcher.js +6 -13
  104. package/dist/pluginWatcher.js.map +1 -1
  105. package/dist/preToolUseHook.js +3 -2
  106. package/dist/preToolUseHook.js.map +1 -1
  107. package/dist/processTree.d.ts +34 -0
  108. package/dist/processTree.js +105 -0
  109. package/dist/processTree.js.map +1 -0
  110. package/dist/prompts.js +3 -3
  111. package/dist/prompts.js.map +1 -1
  112. package/dist/recipeOrchestration.js +35 -1
  113. package/dist/recipeOrchestration.js.map +1 -1
  114. package/dist/recipeRoutes.d.ts +37 -0
  115. package/dist/recipeRoutes.js +236 -33
  116. package/dist/recipeRoutes.js.map +1 -1
  117. package/dist/recipes/agentExecutor.d.ts +25 -5
  118. package/dist/recipes/agentExecutor.js.map +1 -1
  119. package/dist/recipes/chainedRunner.js +16 -2
  120. package/dist/recipes/chainedRunner.js.map +1 -1
  121. package/dist/recipes/connectorPreflight.d.ts +53 -0
  122. package/dist/recipes/connectorPreflight.js +143 -0
  123. package/dist/recipes/connectorPreflight.js.map +1 -0
  124. package/dist/recipes/githubInstallSource.d.ts +62 -0
  125. package/dist/recipes/githubInstallSource.js +125 -0
  126. package/dist/recipes/githubInstallSource.js.map +1 -0
  127. package/dist/recipes/haltCategory.d.ts +80 -0
  128. package/dist/recipes/haltCategory.js +125 -0
  129. package/dist/recipes/haltCategory.js.map +1 -0
  130. package/dist/recipes/idempotencyKey.d.ts +126 -0
  131. package/dist/recipes/idempotencyKey.js +297 -0
  132. package/dist/recipes/idempotencyKey.js.map +1 -0
  133. package/dist/recipes/installer.js +48 -2
  134. package/dist/recipes/installer.js.map +1 -1
  135. package/dist/recipes/judgeSummary.d.ts +50 -0
  136. package/dist/recipes/judgeSummary.js +47 -0
  137. package/dist/recipes/judgeSummary.js.map +1 -0
  138. package/dist/recipes/judgeVerdict.d.ts +48 -0
  139. package/dist/recipes/judgeVerdict.js +174 -0
  140. package/dist/recipes/judgeVerdict.js.map +1 -0
  141. package/dist/recipes/migrations/index.d.ts +9 -0
  142. package/dist/recipes/migrations/index.js +133 -0
  143. package/dist/recipes/migrations/index.js.map +1 -1
  144. package/dist/recipes/parser.js +82 -4
  145. package/dist/recipes/parser.js.map +1 -1
  146. package/dist/recipes/runBudget.d.ts +70 -0
  147. package/dist/recipes/runBudget.js +109 -0
  148. package/dist/recipes/runBudget.js.map +1 -0
  149. package/dist/recipes/scheduler.d.ts +17 -0
  150. package/dist/recipes/scheduler.js +34 -2
  151. package/dist/recipes/scheduler.js.map +1 -1
  152. package/dist/recipes/schema.d.ts +30 -0
  153. package/dist/recipes/toolRegistry.js +19 -0
  154. package/dist/recipes/toolRegistry.js.map +1 -1
  155. package/dist/recipes/tools/http.d.ts +10 -0
  156. package/dist/recipes/tools/http.js +176 -0
  157. package/dist/recipes/tools/http.js.map +1 -0
  158. package/dist/recipes/tools/index.d.ts +1 -0
  159. package/dist/recipes/tools/index.js +1 -0
  160. package/dist/recipes/tools/index.js.map +1 -1
  161. package/dist/recipes/validation.js +1 -1
  162. package/dist/recipes/validation.js.map +1 -1
  163. package/dist/recipes/yamlRunner.d.ts +75 -8
  164. package/dist/recipes/yamlRunner.js +174 -28
  165. package/dist/recipes/yamlRunner.js.map +1 -1
  166. package/dist/resources.js +21 -13
  167. package/dist/resources.js.map +1 -1
  168. package/dist/runLog.d.ts +28 -0
  169. package/dist/runLog.js +19 -3
  170. package/dist/runLog.js.map +1 -1
  171. package/dist/sanitizeParsedJson.d.ts +39 -0
  172. package/dist/sanitizeParsedJson.js +55 -0
  173. package/dist/sanitizeParsedJson.js.map +1 -0
  174. package/dist/server.d.ts +79 -0
  175. package/dist/server.js +356 -3
  176. package/dist/server.js.map +1 -1
  177. package/dist/sessionCheckpoint.d.ts +8 -0
  178. package/dist/sessionCheckpoint.js +18 -2
  179. package/dist/sessionCheckpoint.js.map +1 -1
  180. package/dist/streamableHttp.js +17 -6
  181. package/dist/streamableHttp.js.map +1 -1
  182. package/dist/tools/bridgeDoctor.js +6 -2
  183. package/dist/tools/bridgeDoctor.js.map +1 -1
  184. package/dist/tools/detectUnusedCode.js +9 -7
  185. package/dist/tools/detectUnusedCode.js.map +1 -1
  186. package/dist/tools/editText.js +2 -1
  187. package/dist/tools/editText.js.map +1 -1
  188. package/dist/tools/fileOperations.js +2 -1
  189. package/dist/tools/fileOperations.js.map +1 -1
  190. package/dist/tools/fileWatcher.js +8 -2
  191. package/dist/tools/fileWatcher.js.map +1 -1
  192. package/dist/tools/fixAllLintErrors.js +10 -5
  193. package/dist/tools/fixAllLintErrors.js.map +1 -1
  194. package/dist/tools/formatDocument.js +10 -5
  195. package/dist/tools/formatDocument.js.map +1 -1
  196. package/dist/tools/getCodeCoverage.js +7 -3
  197. package/dist/tools/getCodeCoverage.js.map +1 -1
  198. package/dist/tools/handoffNote.js +2 -1
  199. package/dist/tools/handoffNote.js.map +1 -1
  200. package/dist/tools/headless/lspClient.js +3 -0
  201. package/dist/tools/headless/lspClient.js.map +1 -1
  202. package/dist/tools/lsp.js +17 -0
  203. package/dist/tools/lsp.js.map +1 -1
  204. package/dist/tools/openDiff.js +4 -1
  205. package/dist/tools/openDiff.js.map +1 -1
  206. package/dist/tools/openFile.js +4 -1
  207. package/dist/tools/openFile.js.map +1 -1
  208. package/dist/tools/organizeImports.js +5 -3
  209. package/dist/tools/organizeImports.js.map +1 -1
  210. package/dist/tools/previewEdit.js +7 -2
  211. package/dist/tools/previewEdit.js.map +1 -1
  212. package/dist/tools/recentTracesDigest.js +56 -11
  213. package/dist/tools/recentTracesDigest.js.map +1 -1
  214. package/dist/tools/refactorExtractFunction.js +4 -1
  215. package/dist/tools/refactorExtractFunction.js.map +1 -1
  216. package/dist/tools/refactorPreview.js +10 -2
  217. package/dist/tools/refactorPreview.js.map +1 -1
  218. package/dist/tools/replaceBlock.js +2 -1
  219. package/dist/tools/replaceBlock.js.map +1 -1
  220. package/dist/tools/searchAndReplace.js +2 -1
  221. package/dist/tools/searchAndReplace.js.map +1 -1
  222. package/dist/tools/spawnWorkspace.js +15 -7
  223. package/dist/tools/spawnWorkspace.js.map +1 -1
  224. package/dist/tools/testRunners/vitestJest.js +3 -1
  225. package/dist/tools/testRunners/vitestJest.js.map +1 -1
  226. package/dist/tools/transaction.js +4 -1
  227. package/dist/tools/transaction.js.map +1 -1
  228. package/dist/tools/utils.js +68 -8
  229. package/dist/tools/utils.js.map +1 -1
  230. package/dist/transport.d.ts +1 -1
  231. package/dist/transport.js +18 -4
  232. package/dist/transport.js.map +1 -1
  233. package/dist/winShim.d.ts +34 -0
  234. package/dist/winShim.js +94 -0
  235. package/dist/winShim.js.map +1 -0
  236. package/dist/writeFileAtomic.d.ts +23 -0
  237. package/dist/writeFileAtomic.js +94 -0
  238. package/dist/writeFileAtomic.js.map +1 -0
  239. package/package.json +17 -6
  240. package/scripts/postinstall.mjs +42 -2
  241. package/scripts/smoke/run-all.mjs +213 -0
  242. package/scripts/start-all.mjs +572 -0
  243. package/scripts/start-all.ps1 +209 -0
  244. package/scripts/start-all.sh +73 -17
  245. package/scripts/start-orchestrator.ps1 +158 -0
  246. package/scripts/start-remote.mjs +122 -0
  247. package/templates/automation-policies/recipe-authoring.json +1 -1
  248. package/templates/automation-policies/security-first.json +1 -1
  249. package/templates/automation-policies/strict-lint.json +1 -1
  250. package/templates/automation-policies/test-driven.json +1 -1
  251. package/templates/automation-policy.example.json +1 -1
  252. package/templates/co.patchwork-os.bridge.plist +1 -1
  253. package/templates/recipes/approval-queue-ui-test.yaml +1 -1
  254. package/templates/recipes/ctx-loop-test.yaml +1 -1
  255. package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
  256. package/dist/commands/marketplace.d.ts +0 -16
  257. package/dist/commands/marketplace.js +0 -32
  258. package/dist/commands/marketplace.js.map +0 -1
  259. package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
  260. package/dist/recipes/legacyRecipeCompat.js +0 -131
  261. package/dist/recipes/legacyRecipeCompat.js.map +0 -1
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ // Cross-platform smoke test runner — replaces run-all.sh.
3
+ // Works on Windows (PowerShell/cmd), macOS, and Linux.
4
+ import { execFileSync, spawn } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const BRIDGE = process.env.BRIDGE ?? "claude-ide-bridge";
12
+ const PORT = 37210;
13
+ const CAT2_PORT = 37211;
14
+
15
+ // Security: shell metacharacters that could enable command injection.
16
+ // On Windows `\` is the path separator (D:\…\bridge.cmd), not an injection
17
+ // vector. Same fix PR #527 applied to scripts/start-all.mjs.
18
+ const SHELL_METACHARACTERS =
19
+ process.platform === "win32"
20
+ ? /[;&|`$(){}[\]<>"'\n\r]/
21
+ : /[;&|`$(){}[\]<>"'\\\n\r]/;
22
+
23
+ /**
24
+ * Validate that a binary path is safe to execute.
25
+ * Prevents command injection by checking for shell metacharacters.
26
+ * @throws Error if path contains dangerous characters
27
+ */
28
+ function validateBinaryPath(binaryPath) {
29
+ if (!binaryPath || typeof binaryPath !== "string") {
30
+ throw new Error("Binary path is empty or invalid");
31
+ }
32
+ if (SHELL_METACHARACTERS.test(binaryPath)) {
33
+ throw new Error(
34
+ `Binary path contains shell metacharacters (potential injection): ${binaryPath}`,
35
+ );
36
+ }
37
+ }
38
+
39
+ // Validate BRIDGE path on startup
40
+ try {
41
+ validateBinaryPath(BRIDGE);
42
+ } catch (err) {
43
+ console.error(`ERROR: ${err.message}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ const TMPWS = fs.mkdtempSync(path.join(os.tmpdir(), "patchwork-smoke-ws-"));
48
+ const CLAUDE_CFG = fs.mkdtempSync(
49
+ path.join(os.tmpdir(), "patchwork-smoke-cfg-"),
50
+ );
51
+ fs.mkdirSync(path.join(CLAUDE_CFG, "ide"), { recursive: true });
52
+
53
+ process.env.CLAUDE_CONFIG_DIR = CLAUDE_CFG;
54
+
55
+ let bridgePid = null;
56
+ let cat2Pid = null;
57
+ let cat2Cfg = null;
58
+
59
+ function cleanup() {
60
+ for (const pid of [bridgePid, cat2Pid]) {
61
+ if (pid == null) continue;
62
+ try {
63
+ process.kill(pid);
64
+ } catch {
65
+ /* already gone */
66
+ }
67
+ }
68
+ for (const dir of [TMPWS, CLAUDE_CFG, cat2Cfg]) {
69
+ if (!dir) continue;
70
+ try {
71
+ fs.rmSync(dir, { recursive: true, force: true });
72
+ } catch {
73
+ /* best-effort */
74
+ }
75
+ }
76
+ }
77
+
78
+ process.on("exit", cleanup);
79
+ process.on("SIGINT", () => process.exit(1));
80
+ process.on("SIGTERM", () => process.exit(1));
81
+
82
+ // Windows GHA runners are noticeably slower at cold-starting node (signed-
83
+ // binary scan, AV hooks). 10s causes recurring "lock file not written" flakes
84
+ // on main. PR #538 doubled the bound in helpers.mjs for individual smoke
85
+ // scripts; this harness was missed. Match the doubled budget here too.
86
+ const DEFAULT_LOCK_TIMEOUT_MS = process.platform === "win32" ? 30_000 : 10_000;
87
+
88
+ function waitForLock(cfgDir, port, timeoutMs = DEFAULT_LOCK_TIMEOUT_MS) {
89
+ const lockPath = path.join(cfgDir, "ide", `${port}.lock`);
90
+ const deadline = Date.now() + timeoutMs;
91
+ while (!fs.existsSync(lockPath)) {
92
+ if (Date.now() > deadline) return false;
93
+ // busy-wait in 100ms increments — same as the bash script
94
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
95
+ }
96
+ return true;
97
+ }
98
+
99
+ // Spawn bridge for the given port and config dir. Captures stderr so a
100
+ // startup failure can be surfaced — `stdio: "ignore"` would swallow it
101
+ // and leave the operator staring at "lock file not written after 10s".
102
+ function startBridge(port, cfgDir, wsDir) {
103
+ // Security: BRIDGE path already validated on startup
104
+ const proc = spawn(BRIDGE, ["--port", String(port), "--workspace", wsDir], {
105
+ env: { ...process.env, CLAUDE_CONFIG_DIR: cfgDir },
106
+ stdio: ["ignore", "ignore", "pipe"],
107
+ // On Windows, npm global bins are .cmd wrappers that need shell:true
108
+ // Safe because BRIDGE path is validated for injection chars on startup
109
+ shell: process.platform === "win32",
110
+ });
111
+ proc.stderrBuf = "";
112
+ proc.stderr.on("data", (d) => {
113
+ proc.stderrBuf += d.toString();
114
+ });
115
+ return proc;
116
+ }
117
+
118
+ // ── Start main bridge ─────────────────────────────────────────────────────────
119
+ console.log(`Starting bridge on port ${PORT}...`);
120
+ const bridgeProc = startBridge(PORT, CLAUDE_CFG, TMPWS);
121
+ bridgePid = bridgeProc.pid;
122
+
123
+ if (!waitForLock(CLAUDE_CFG, PORT)) {
124
+ console.error(
125
+ `ERROR: bridge lock file not written after ${DEFAULT_LOCK_TIMEOUT_MS / 1000}s`,
126
+ );
127
+ console.error(
128
+ `Bridge stderr (last 4 KB):\n${(bridgeProc.stderrBuf || "(empty)").slice(-4096)}`,
129
+ );
130
+ process.exit(1);
131
+ }
132
+ // tiny extra buffer for WS listener to bind
133
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
134
+
135
+ const TOKEN = execFileSync(BRIDGE, ["print-token", "--port", String(PORT)], {
136
+ encoding: "utf-8",
137
+ shell: process.platform === "win32",
138
+ }).trim();
139
+
140
+ console.log(`Bridge ready. Token: ${TOKEN.slice(0, 8)}...\n`);
141
+
142
+ // ── Start CAT-2 bridge (separate instance — CAT-2 kills the bridge it tests) ─
143
+ cat2Cfg = fs.mkdtempSync(path.join(os.tmpdir(), "patchwork-smoke-cat2-"));
144
+ fs.mkdirSync(path.join(cat2Cfg, "ide"), { recursive: true });
145
+
146
+ const cat2Proc = startBridge(CAT2_PORT, cat2Cfg, TMPWS);
147
+ cat2Pid = cat2Proc.pid;
148
+ waitForLock(cat2Cfg, CAT2_PORT);
149
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
150
+
151
+ // ── Category runner ───────────────────────────────────────────────────────────
152
+ let pass = 0;
153
+ let fail = 0;
154
+ const failures = [];
155
+
156
+ function runCat(label, scriptFile, args = [], extraEnv = {}) {
157
+ try {
158
+ execFileSync(process.execPath, [scriptFile, ...args], {
159
+ env: { ...process.env, ...extraEnv },
160
+ stdio: "inherit",
161
+ });
162
+ console.log(`\x1b[32m[PASS]\x1b[0m ${label}`);
163
+ pass++;
164
+ } catch {
165
+ console.log(`\x1b[31m[FAIL]\x1b[0m ${label}`);
166
+ fail++;
167
+ failures.push(label);
168
+ }
169
+ }
170
+
171
+ const S = __dirname;
172
+ const P = String(PORT);
173
+ const T = TOKEN;
174
+
175
+ runCat(
176
+ "CAT-2 (lockfile)",
177
+ path.join(S, "cat2-lockfile.mjs"),
178
+ [String(CAT2_PORT), String(cat2Pid)],
179
+ { CLAUDE_CONFIG_DIR: cat2Cfg },
180
+ );
181
+ fs.rmSync(cat2Cfg, { recursive: true, force: true });
182
+ cat2Cfg = null;
183
+ cat2Pid = null;
184
+
185
+ runCat("CAT-3 (auth)", path.join(S, "cat3-auth.mjs"), [P, T]);
186
+ runCat("CAT-4 (tools)", path.join(S, "cat4-tools.mjs"));
187
+ runCat("CAT-5 (http)", path.join(S, "cat5-http.mjs"), [P, T]);
188
+ runCat("CAT-6 (oauth)", path.join(S, "cat6-oauth.mjs"));
189
+ runCat("CAT-7 (plugin)", path.join(S, "cat7-plugin.mjs"));
190
+ runCat("CAT-8 (ratelimit)", path.join(S, "cat8-ratelimit.mjs"), [P, T]);
191
+
192
+ // Give bridge 1s to reset after CAT-8 saturates the rate limiter
193
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 1000);
194
+
195
+ runCat("CAT-9 (prompts/res)", path.join(S, "cat9-prompts-resources.mjs"), [
196
+ P,
197
+ T,
198
+ ]);
199
+ runCat("CAT-10 (health)", path.join(S, "cat10-health.mjs"), [P, T]);
200
+ runCat("CAT-11 (shutdown)", path.join(S, "cat11-shutdown.mjs"));
201
+ runCat("CAT-12 (automation)", path.join(S, "cat12-automation.mjs"));
202
+
203
+ // ── Summary ───────────────────────────────────────────────────────────────────
204
+ const total = pass + fail;
205
+ console.log("\n═══════════════════════════════════");
206
+ if (fail === 0) {
207
+ console.log(`\x1b[32mALL PASS\x1b[0m (${pass}/${total} categories)`);
208
+ } else {
209
+ console.log(`\x1b[31mFAILURES: ${fail}/${total} categories\x1b[0m`);
210
+ for (const c of failures) console.log(` ✗ ${c}`);
211
+ }
212
+ console.log("═══════════════════════════════════");
213
+ process.exit(fail > 0 ? 1 : 0);