harnery 0.37.0 → 0.38.0

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 (184) hide show
  1. package/dist/commander.d.ts +9 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +7 -2
  4. package/dist/commands/admission.d.ts +21 -0
  5. package/dist/commands/admission.d.ts.map +1 -0
  6. package/dist/commands/admission.js +565 -0
  7. package/dist/commands/agents.d.ts +7 -0
  8. package/dist/commands/agents.d.ts.map +1 -1
  9. package/dist/commands/agents.js +61 -1
  10. package/dist/commands/artifacts.d.ts.map +1 -1
  11. package/dist/commands/artifacts.js +48 -2
  12. package/dist/commands/browse-ai.d.ts +2 -2
  13. package/dist/commands/browse-ai.d.ts.map +1 -1
  14. package/dist/commands/browse-ai.js +6 -4
  15. package/dist/commands/browse.d.ts.map +1 -1
  16. package/dist/commands/browse.js +349 -21
  17. package/dist/commands/fetch.js +1 -0
  18. package/dist/commands/qa-record.d.ts +144 -0
  19. package/dist/commands/qa-record.d.ts.map +1 -0
  20. package/dist/commands/qa-record.js +0 -0
  21. package/dist/commands/qa-run.d.ts +7 -3
  22. package/dist/commands/qa-run.d.ts.map +1 -1
  23. package/dist/commands/qa-run.js +268 -13
  24. package/dist/commands/qa-status.d.ts +71 -0
  25. package/dist/commands/qa-status.d.ts.map +1 -0
  26. package/dist/commands/qa-status.js +490 -0
  27. package/dist/commands/qa-verify.d.ts +40 -0
  28. package/dist/commands/qa-verify.d.ts.map +1 -0
  29. package/dist/commands/qa-verify.js +180 -0
  30. package/dist/commands/review-pack.d.ts +4 -0
  31. package/dist/commands/review-pack.d.ts.map +1 -0
  32. package/dist/commands/review-pack.js +1001 -0
  33. package/dist/core/agents/qa-signal.d.ts +111 -0
  34. package/dist/core/agents/qa-signal.d.ts.map +1 -0
  35. package/dist/core/agents/qa-signal.js +231 -0
  36. package/dist/core/agents/session-name-display.d.ts +20 -5
  37. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  38. package/dist/core/agents/session-name-display.js +67 -7
  39. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  40. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  41. package/dist/core/agents/state/heartbeat-writer.d.ts +11 -0
  42. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  43. package/dist/core/agents/state/heartbeat-writer.js +17 -0
  44. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  45. package/dist/core/agents/state/live-coordination-view.js +1 -0
  46. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  47. package/dist/core/artifacts/constants.d.ts +1 -1
  48. package/dist/core/artifacts/constants.js +1 -1
  49. package/dist/core/artifacts/index.d.ts +57 -6
  50. package/dist/core/artifacts/index.d.ts.map +1 -1
  51. package/dist/core/artifacts/index.js +265 -10
  52. package/dist/core/config.d.ts +8 -0
  53. package/dist/core/config.d.ts.map +1 -1
  54. package/dist/core/config.js +16 -0
  55. package/dist/core/diagnostics/bundle.d.ts +16 -0
  56. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  57. package/dist/core/diagnostics/bundle.js +101 -7
  58. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  59. package/dist/core/events/v3/bootstrap.js +10 -0
  60. package/dist/core/events/v3/coordination-view.d.ts +3 -0
  61. package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
  62. package/dist/core/events/v3/coordination-view.js +69 -8
  63. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  64. package/dist/core/events/v3/producers/intake.js +9 -2
  65. package/dist/core/events/v3/producers/recorder.d.ts +23 -0
  66. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  67. package/dist/core/events/v3/producers/recorder.js +264 -18
  68. package/dist/core/hooks/cli.js +120 -27
  69. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  70. package/dist/core/hooks/resolve/transcript.js +10 -3
  71. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  72. package/dist/core/hooks/session-name-presence.js +4 -1
  73. package/dist/core/qa-artifacts.d.ts +20 -0
  74. package/dist/core/qa-artifacts.d.ts.map +1 -0
  75. package/dist/core/qa-artifacts.js +110 -0
  76. package/dist/core/resources/contract.d.ts +6 -0
  77. package/dist/core/resources/contract.d.ts.map +1 -1
  78. package/dist/core/resources/sampler.d.ts +7 -0
  79. package/dist/core/resources/sampler.d.ts.map +1 -1
  80. package/dist/core/resources/sampler.js +106 -5
  81. package/dist/lib/admission.d.ts +71 -0
  82. package/dist/lib/admission.d.ts.map +1 -0
  83. package/dist/lib/admission.js +264 -0
  84. package/dist/lib/agent-browser/client.d.ts +1 -1
  85. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  86. package/dist/lib/agent-browser/client.js +1 -5
  87. package/dist/lib/browser/capture-fidelity.d.ts +39 -0
  88. package/dist/lib/browser/capture-fidelity.d.ts.map +1 -0
  89. package/dist/lib/browser/capture-fidelity.js +84 -0
  90. package/dist/lib/browser/client.d.ts +41 -1
  91. package/dist/lib/browser/client.d.ts.map +1 -1
  92. package/dist/lib/browser/client.js +167 -9
  93. package/dist/lib/browser/critique.d.ts +38 -1
  94. package/dist/lib/browser/critique.d.ts.map +1 -1
  95. package/dist/lib/browser/critique.js +34 -6
  96. package/dist/lib/browser/index.d.ts +4 -2
  97. package/dist/lib/browser/index.d.ts.map +1 -1
  98. package/dist/lib/browser/index.js +2 -0
  99. package/dist/lib/browser/page-review-judge.d.ts +64 -0
  100. package/dist/lib/browser/page-review-judge.d.ts.map +1 -0
  101. package/dist/lib/browser/page-review-judge.js +270 -0
  102. package/dist/lib/browser/page-review-pack.d.ts +613 -0
  103. package/dist/lib/browser/page-review-pack.d.ts.map +1 -0
  104. package/dist/lib/browser/page-review-pack.js +1751 -0
  105. package/dist/lib/browser/qa-run-contracts.d.ts +214 -10
  106. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  107. package/dist/lib/browser/qa-run-contracts.js +136 -1
  108. package/dist/lib/browser/qa-run.d.ts +100 -10
  109. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  110. package/dist/lib/browser/qa-run.js +768 -169
  111. package/dist/lib/browser/request-diagnostics.d.ts +13 -0
  112. package/dist/lib/browser/request-diagnostics.d.ts.map +1 -0
  113. package/dist/lib/browser/request-diagnostics.js +18 -0
  114. package/dist/lib/browser/tiling.d.ts +19 -0
  115. package/dist/lib/browser/tiling.d.ts.map +1 -1
  116. package/dist/lib/browser/tiling.js +28 -0
  117. package/dist/lib/cookies/client.d.ts +9 -0
  118. package/dist/lib/cookies/client.d.ts.map +1 -1
  119. package/dist/lib/cookies/client.js +197 -44
  120. package/dist/lib/cookies/extra.d.ts +18 -0
  121. package/dist/lib/cookies/extra.d.ts.map +1 -0
  122. package/dist/lib/cookies/extra.js +14 -0
  123. package/dist/lib/cookies/index.d.ts +2 -1
  124. package/dist/lib/cookies/index.d.ts.map +1 -1
  125. package/dist/lib/cookies/index.js +2 -1
  126. package/dist/lib/durable-job.d.ts +124 -0
  127. package/dist/lib/durable-job.d.ts.map +1 -0
  128. package/dist/lib/durable-job.js +296 -0
  129. package/dist/lib/http/client.d.ts +7 -1
  130. package/dist/lib/http/client.d.ts.map +1 -1
  131. package/dist/lib/http/client.js +2 -0
  132. package/dist/lib/instructions/templates.d.ts.map +1 -1
  133. package/dist/lib/instructions/templates.js +5 -2
  134. package/package.json +8 -2
  135. package/src/commander.ts +50 -2
  136. package/src/commands/admission.ts +699 -0
  137. package/src/commands/agents.ts +87 -1
  138. package/src/commands/artifacts.ts +97 -21
  139. package/src/commands/browse-ai.ts +10 -5
  140. package/src/commands/browse.ts +481 -20
  141. package/src/commands/fetch.ts +1 -0
  142. package/src/commands/qa-record.ts +682 -0
  143. package/src/commands/qa-run.ts +335 -16
  144. package/src/commands/qa-status.ts +608 -0
  145. package/src/commands/qa-verify.ts +238 -0
  146. package/src/commands/review-pack.ts +1281 -0
  147. package/src/core/agents/qa-signal.ts +261 -0
  148. package/src/core/agents/session-name-display.ts +78 -7
  149. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  150. package/src/core/agents/state/heartbeat-writer.ts +23 -0
  151. package/src/core/agents/state/live-coordination-view.ts +1 -0
  152. package/src/core/agents/state/live-coordination-writer.ts +5 -0
  153. package/src/core/artifacts/constants.ts +1 -1
  154. package/src/core/artifacts/index.ts +370 -21
  155. package/src/core/config.ts +23 -0
  156. package/src/core/diagnostics/bundle.ts +119 -11
  157. package/src/core/events/v3/bootstrap.ts +10 -0
  158. package/src/core/events/v3/coordination-view.ts +100 -11
  159. package/src/core/events/v3/producers/intake.ts +9 -2
  160. package/src/core/events/v3/producers/recorder.ts +312 -18
  161. package/src/core/hooks/cli.ts +140 -32
  162. package/src/core/hooks/resolve/transcript.ts +10 -3
  163. package/src/core/hooks/session-name-presence.ts +6 -1
  164. package/src/core/qa-artifacts.ts +126 -0
  165. package/src/core/resources/contract.ts +7 -0
  166. package/src/core/resources/sampler.ts +138 -6
  167. package/src/lib/admission.ts +347 -0
  168. package/src/lib/agent-browser/client.ts +2 -10
  169. package/src/lib/browser/capture-fidelity.ts +98 -0
  170. package/src/lib/browser/client.ts +206 -10
  171. package/src/lib/browser/critique.ts +62 -7
  172. package/src/lib/browser/index.ts +36 -0
  173. package/src/lib/browser/page-review-judge.ts +360 -0
  174. package/src/lib/browser/page-review-pack.ts +2384 -0
  175. package/src/lib/browser/qa-run-contracts.ts +366 -3
  176. package/src/lib/browser/qa-run.ts +868 -190
  177. package/src/lib/browser/request-diagnostics.ts +27 -0
  178. package/src/lib/browser/tiling.ts +32 -0
  179. package/src/lib/cookies/client.ts +228 -42
  180. package/src/lib/cookies/extra.ts +28 -0
  181. package/src/lib/cookies/index.ts +2 -0
  182. package/src/lib/durable-job.ts +407 -0
  183. package/src/lib/http/client.ts +13 -1
  184. package/src/lib/instructions/templates.ts +5 -2
@@ -0,0 +1,13 @@
1
+ export interface FailedRequestCandidate {
2
+ url: string;
3
+ method: string;
4
+ failure: string;
5
+ resourceType: string;
6
+ }
7
+ /**
8
+ * Next.js may start a speculative React Server Component fetch and cancel it
9
+ * once the destination is no longer needed. Chromium reports that deliberate
10
+ * cancellation as net::ERR_ABORTED even though the rendered page is healthy.
11
+ */
12
+ export declare function isBenignNextRscAbort(request: FailedRequestCandidate): boolean;
13
+ //# sourceMappingURL=request-diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/request-diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAc7E"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Next.js may start a speculative React Server Component fetch and cancel it
3
+ * once the destination is no longer needed. Chromium reports that deliberate
4
+ * cancellation as net::ERR_ABORTED even though the rendered page is healthy.
5
+ */
6
+ export function isBenignNextRscAbort(request) {
7
+ if (request.failure !== "net::ERR_ABORTED" ||
8
+ request.method !== "GET" ||
9
+ request.resourceType !== "fetch") {
10
+ return false;
11
+ }
12
+ try {
13
+ return new URL(request.url).searchParams.has("_rsc");
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
@@ -68,4 +68,23 @@ export declare function bandOversizedRect(rect: {
68
68
  width: number;
69
69
  height: number;
70
70
  }>;
71
+ /**
72
+ * Indices of bands past the kept prefix (`allBands[0..keptCount)`) that
73
+ * intersect any of `rects`. A tile cap keeps the top of a tall page and drops
74
+ * the rest; a deterministic gate that already found a defect below the cap
75
+ * still deserves a tile, so the caller cuts these bands as well and labels
76
+ * them as hit bands. Pure: the result is deduplicated, ascending, and holds
77
+ * at most `maxExtra` entries. Rects and bands share one pixel space.
78
+ */
79
+ export declare function bandsCoveringRects(allBands: ReadonlyArray<{
80
+ x: number;
81
+ y: number;
82
+ width: number;
83
+ height: number;
84
+ }>, keptCount: number, rects: ReadonlyArray<{
85
+ x: number;
86
+ y: number;
87
+ width: number;
88
+ height: number;
89
+ }>, maxExtra?: number): number[];
71
90
  //# sourceMappingURL=tiling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiling.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/tiling.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;IACvC,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,6DAA6D;AAC7D,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CActF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EAAE,EACnB,IAAI,EAAE,WAAW,GAChB;IACD,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CA+CA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5E,KAAK,EAAE,UAAU,EAAE,EACnB,IAAI,EAAE,WAAW,GAChB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAU/E"}
1
+ {"version":3,"file":"tiling.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/tiling.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;IACvC,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,6DAA6D;AAC7D,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CActF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EAAE,EACnB,IAAI,EAAE,WAAW,GAChB;IACD,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CA+CA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5E,KAAK,EAAE,UAAU,EAAE,EACnB,IAAI,EAAE,WAAW,GAChB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAU/E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,aAAa,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAChF,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,aAAa,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7E,QAAQ,SAAK,GACZ,MAAM,EAAE,CAiBV"}
@@ -116,3 +116,31 @@ export function bandOversizedRect(rect, atoms, opts) {
116
116
  height: r.height,
117
117
  }));
118
118
  }
119
+ /**
120
+ * Indices of bands past the kept prefix (`allBands[0..keptCount)`) that
121
+ * intersect any of `rects`. A tile cap keeps the top of a tall page and drops
122
+ * the rest; a deterministic gate that already found a defect below the cap
123
+ * still deserves a tile, so the caller cuts these bands as well and labels
124
+ * them as hit bands. Pure: the result is deduplicated, ascending, and holds
125
+ * at most `maxExtra` entries. Rects and bands share one pixel space.
126
+ */
127
+ export function bandsCoveringRects(allBands, keptCount, rects, maxExtra = 50) {
128
+ const out = [];
129
+ if (rects.length === 0 || maxExtra <= 0)
130
+ return out;
131
+ for (let index = Math.max(0, keptCount); index < allBands.length; index++) {
132
+ if (out.length >= maxExtra)
133
+ break;
134
+ const band = allBands[index];
135
+ if (!band)
136
+ continue;
137
+ const hit = rects.some((rect) => {
138
+ const x1 = rect.x + Math.max(rect.width, 1);
139
+ const y1 = rect.y + Math.max(rect.height, 1);
140
+ return (band.x < x1 && band.x + band.width > rect.x && band.y < y1 && band.y + band.height > rect.y);
141
+ });
142
+ if (hit)
143
+ out.push(index);
144
+ }
145
+ return out;
146
+ }
@@ -52,6 +52,10 @@ export interface InfoResult {
52
52
  exportedAt?: string;
53
53
  exportedFrom?: string;
54
54
  }
55
+ export declare class CookieStoreParseError extends Error {
56
+ readonly path: string;
57
+ constructor(path: string, cause: unknown);
58
+ }
55
59
  export declare class CookieJar {
56
60
  readonly path: string;
57
61
  private readonly source;
@@ -63,6 +67,9 @@ export declare class CookieJar {
63
67
  * (constructor-provided source). Creates the parent directory if needed.
64
68
  */
65
69
  save(store: CookieStore): void;
70
+ private saveUnlocked;
71
+ private stamp;
72
+ private update;
66
73
  /**
67
74
  * Read + filter helper. Drops expired cookies. Optional domain filter
68
75
  * uses the same `domainMatches` logic Playwright/CDP do (leading dot
@@ -76,6 +83,8 @@ export declare class CookieJar {
76
83
  * Returns the saved store.
77
84
  */
78
85
  set(cookie: Cookie): CookieStore;
86
+ /** Merge cookies and origins in one interprocess-coordinated update. */
87
+ merge(cookies: Cookie[], origins?: OriginEntry[]): CookieStore;
79
88
  /**
80
89
  * Clear cookies. Pass `{ all: true }` to wipe everything (including
81
90
  * origins/localStorage), or `{ domain: "..." }` to drop matching cookies
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/client.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,139 @@
1
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { dirname } from "node:path";
1
+ import { randomUUID } from "node:crypto";
2
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ export class CookieStoreParseError extends Error {
5
+ path;
6
+ constructor(path, cause) {
7
+ const detail = cause instanceof Error ? cause.message : String(cause);
8
+ super(`Cookie-store parse failed at "${path}" after a stable read: ${detail}. ` +
9
+ "The original cookie-store file was left unchanged.", { cause });
10
+ this.name = "CookieStoreParseError";
11
+ this.path = path;
12
+ }
13
+ }
14
+ const LOCK_WAIT_MS = 10_000;
15
+ const LOCK_POLL_MS = 10;
16
+ const STABLE_READ_ATTEMPTS = 5;
17
+ const STABLE_READ_POLL_MS = 10;
18
+ const sleepBuffer = new Int32Array(new SharedArrayBuffer(4));
19
+ function sleepSync(ms) {
20
+ Atomics.wait(sleepBuffer, 0, 0, ms);
21
+ }
22
+ function isErrorCode(error, code) {
23
+ return error.code === code;
24
+ }
25
+ function snapshot(path) {
26
+ const stat = statSync(path);
27
+ return {
28
+ dev: stat.dev,
29
+ ino: stat.ino,
30
+ size: stat.size,
31
+ mtimeMs: stat.mtimeMs,
32
+ ctimeMs: stat.ctimeMs,
33
+ };
34
+ }
35
+ function sameSnapshot(left, right) {
36
+ return (left.dev === right.dev &&
37
+ left.ino === right.ino &&
38
+ left.size === right.size &&
39
+ left.mtimeMs === right.mtimeMs &&
40
+ left.ctimeMs === right.ctimeMs);
41
+ }
42
+ function readStableFile(path) {
43
+ for (let attempt = 1; attempt <= STABLE_READ_ATTEMPTS; attempt++) {
44
+ let before;
45
+ try {
46
+ before = snapshot(path);
47
+ }
48
+ catch (error) {
49
+ if (isErrorCode(error, "ENOENT"))
50
+ return undefined;
51
+ throw error;
52
+ }
53
+ try {
54
+ const raw = readFileSync(path, "utf8");
55
+ const after = snapshot(path);
56
+ if (sameSnapshot(before, after))
57
+ return raw;
58
+ }
59
+ catch (error) {
60
+ if (!isErrorCode(error, "ENOENT"))
61
+ throw error;
62
+ }
63
+ if (attempt < STABLE_READ_ATTEMPTS)
64
+ sleepSync(STABLE_READ_POLL_MS);
65
+ }
66
+ throw new Error(`Cookie-store read failed at "${path}": the file did not remain stable across ` +
67
+ `${STABLE_READ_ATTEMPTS} attempts. The original cookie-store file was left unchanged.`);
68
+ }
69
+ function atomicWrite(path, contents) {
70
+ const dir = dirname(path);
71
+ mkdirSync(dir, { recursive: true });
72
+ const temporary = join(dir, `.${basename(path)}.${process.pid}.${randomUUID().slice(0, 8)}.tmp`);
73
+ try {
74
+ writeFileSync(temporary, contents, { mode: 0o600 });
75
+ renameSync(temporary, path);
76
+ }
77
+ finally {
78
+ try {
79
+ unlinkSync(temporary);
80
+ }
81
+ catch {
82
+ // A successful rename already removed the temporary path. A failed
83
+ // cleanup must not hide the original write error.
84
+ }
85
+ }
86
+ }
87
+ function withStoreLock(path, operation) {
88
+ mkdirSync(dirname(path), { recursive: true });
89
+ const lockPath = `${path}.lock`;
90
+ const deadline = Date.now() + LOCK_WAIT_MS;
91
+ let lockFd;
92
+ while (lockFd === undefined) {
93
+ try {
94
+ lockFd = openSync(lockPath, "wx", 0o600);
95
+ try {
96
+ writeFileSync(lockFd, `${JSON.stringify({ pid: process.pid, acquiredAt: new Date().toISOString() })}\n`);
97
+ }
98
+ catch (error) {
99
+ closeSync(lockFd);
100
+ lockFd = undefined;
101
+ try {
102
+ unlinkSync(lockPath);
103
+ }
104
+ catch {
105
+ // Preserve the lock-write error.
106
+ }
107
+ throw error;
108
+ }
109
+ }
110
+ catch (error) {
111
+ if (!isErrorCode(error, "EEXIST")) {
112
+ if (lockFd !== undefined)
113
+ closeSync(lockFd);
114
+ throw error;
115
+ }
116
+ if (Date.now() >= deadline) {
117
+ throw new Error(`Cookie-store update timed out waiting for lock "${lockPath}" after ${LOCK_WAIT_MS}ms. ` +
118
+ "The cookie-store file was left unchanged.");
119
+ }
120
+ sleepSync(LOCK_POLL_MS);
121
+ }
122
+ }
123
+ closeSync(lockFd);
124
+ try {
125
+ return operation();
126
+ }
127
+ finally {
128
+ try {
129
+ unlinkSync(lockPath);
130
+ }
131
+ catch {
132
+ // The update has already committed. Do not replace its result with a
133
+ // cleanup error; a later writer will report the exact lock path.
134
+ }
135
+ }
136
+ }
3
137
  export class CookieJar {
4
138
  path;
5
139
  source;
@@ -11,11 +145,16 @@ export class CookieJar {
11
145
  return existsSync(this.path);
12
146
  }
13
147
  load() {
14
- if (!existsSync(this.path)) {
148
+ const raw = readStableFile(this.path);
149
+ if (raw === undefined)
15
150
  return { cookies: [], origins: [] };
151
+ let parsed;
152
+ try {
153
+ parsed = JSON.parse(raw);
154
+ }
155
+ catch (error) {
156
+ throw new CookieStoreParseError(this.path, error);
16
157
  }
17
- const raw = readFileSync(this.path, "utf-8");
18
- const parsed = JSON.parse(raw);
19
158
  return {
20
159
  cookies: parsed.cookies ?? [],
21
160
  origins: parsed.origins ?? [],
@@ -28,13 +167,26 @@ export class CookieJar {
28
167
  * (constructor-provided source). Creates the parent directory if needed.
29
168
  */
30
169
  save(store) {
31
- const stamped = {
170
+ withStoreLock(this.path, () => this.saveUnlocked(store));
171
+ }
172
+ saveUnlocked(store) {
173
+ const stamped = this.stamp(store);
174
+ atomicWrite(this.path, `${JSON.stringify(stamped, null, 2)}\n`);
175
+ return stamped;
176
+ }
177
+ stamp(store) {
178
+ return {
32
179
  ...store,
33
180
  exportedAt: new Date().toISOString(),
34
181
  exportedFrom: this.source,
35
182
  };
36
- mkdirSync(dirname(this.path), { recursive: true });
37
- writeFileSync(this.path, `${JSON.stringify(stamped, null, 2)}\n`);
183
+ }
184
+ update(operation) {
185
+ return withStoreLock(this.path, () => {
186
+ const updated = operation(this.load());
187
+ this.saveUnlocked(updated.store);
188
+ return updated.result;
189
+ });
38
190
  }
39
191
  /**
40
192
  * Read + filter helper. Drops expired cookies. Optional domain filter
@@ -54,10 +206,18 @@ export class CookieJar {
54
206
  * Returns the saved store.
55
207
  */
56
208
  set(cookie) {
57
- const store = this.load();
58
- const merged = mergeCookies(store, [cookie]);
59
- this.save(merged);
60
- return merged;
209
+ return this.merge([cookie]);
210
+ }
211
+ /** Merge cookies and origins in one interprocess-coordinated update. */
212
+ merge(cookies, origins = []) {
213
+ return this.update((store) => {
214
+ const merged = mergeCookies(store, cookies);
215
+ const byOrigin = new Map(store.origins.map((origin) => [origin.origin, origin]));
216
+ for (const origin of origins)
217
+ byOrigin.set(origin.origin, origin);
218
+ merged.origins = [...byOrigin.values()];
219
+ return { store: merged, result: merged };
220
+ });
61
221
  }
62
222
  /**
63
223
  * Clear cookies. Pass `{ all: true }` to wipe everything (including
@@ -68,26 +228,29 @@ export class CookieJar {
68
228
  if (!opts.domain && !opts.all) {
69
229
  throw new Error("Specify { domain } or { all: true }");
70
230
  }
71
- const store = this.load();
72
- const before = store.cookies.length;
73
- if (opts.all) {
74
- store.cookies = [];
75
- store.origins = [];
76
- }
77
- else if (opts.domain) {
78
- const dom = opts.domain;
79
- store.cookies = store.cookies.filter((c) => !domainMatches(c.domain, dom));
80
- store.origins = store.origins.filter((o) => {
81
- try {
82
- return !domainMatches(dom, new URL(o.origin).hostname);
83
- }
84
- catch {
85
- return true;
86
- }
87
- });
88
- }
89
- this.save(store);
90
- return { before, after: store.cookies.length };
231
+ return this.update((store) => {
232
+ const before = store.cookies.length;
233
+ if (opts.all) {
234
+ store.cookies = [];
235
+ store.origins = [];
236
+ }
237
+ else if (opts.domain) {
238
+ const dom = opts.domain;
239
+ store.cookies = store.cookies.filter((c) => !domainMatches(c.domain, dom));
240
+ store.origins = store.origins.filter((o) => {
241
+ try {
242
+ return !domainMatches(dom, new URL(o.origin).hostname);
243
+ }
244
+ catch {
245
+ return true;
246
+ }
247
+ });
248
+ }
249
+ return {
250
+ store,
251
+ result: { before, after: store.cookies.length },
252
+ };
253
+ });
91
254
  }
92
255
  /**
93
256
  * Build a `Cookie:` header value for a URL. Only cookies that match
@@ -123,16 +286,7 @@ export class CookieJar {
123
286
  this.save(store);
124
287
  }
125
288
  else {
126
- const store = this.load();
127
- const merged = mergeCookies(store, cookies);
128
- if (incoming.origins) {
129
- const seen = new Set(store.origins.map((o) => o.origin));
130
- for (const o of incoming.origins) {
131
- if (!seen.has(o.origin))
132
- merged.origins.push(o);
133
- }
134
- }
135
- this.save(merged);
289
+ this.merge(cookies, incoming.origins ?? []);
136
290
  }
137
291
  return { count: cookies.length };
138
292
  }
@@ -144,8 +298,7 @@ export class CookieJar {
144
298
  exportedAt: new Date().toISOString(),
145
299
  exportedFrom: `${this.source}-export`,
146
300
  };
147
- mkdirSync(dirname(filePath), { recursive: true });
148
- writeFileSync(filePath, `${JSON.stringify(stamped, null, 2)}\n`);
301
+ atomicWrite(filePath, `${JSON.stringify(stamped, null, 2)}\n`);
149
302
  return { count: stamped.cookies.length };
150
303
  }
151
304
  /** Stats for the `info` command. Stable shape so callers can JSON it. */
@@ -0,0 +1,18 @@
1
+ import type { Cookie, CookieJar } from "./client.js";
2
+ /**
3
+ * Host callback that can mint or refresh cookies for one request URL.
4
+ * Must stay synchronous: `fetch` and `browse` attach cookies before they
5
+ * leave this process, the same constraint as `extraHeaders`.
6
+ *
7
+ * Return cookies to add; do not mutate the jar. The caller persists them
8
+ * with `jar.set()` so `--no-cookies` (no jar) skips the callback entirely.
9
+ */
10
+ export type ExtraCookies = (url: string, jar: CookieJar) => Cookie[];
11
+ /**
12
+ * Run a host `extraCookies` callback and persist whatever it returns.
13
+ * No-op when the callback is omitted. Order matters: this must run
14
+ * before `jar.header()` or Playwright `addCookies`, because an already
15
+ * populated `Cookie` header cannot pick up extra cookies later.
16
+ */
17
+ export declare function applyExtraCookies(url: string, jar: CookieJar, extraCookies?: ExtraCookies): Cookie[];
18
+ //# sourceMappingURL=extra.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extra.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/extra.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,MAAM,EAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,SAAS,EACd,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,EAAE,CAKV"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Run a host `extraCookies` callback and persist whatever it returns.
3
+ * No-op when the callback is omitted. Order matters: this must run
4
+ * before `jar.header()` or Playwright `addCookies`, because an already
5
+ * populated `Cookie` header cannot pick up extra cookies later.
6
+ */
7
+ export function applyExtraCookies(url, jar, extraCookies) {
8
+ if (!extraCookies)
9
+ return [];
10
+ const cookies = extraCookies(url, jar);
11
+ for (const cookie of cookies)
12
+ jar.set(cookie);
13
+ return cookies;
14
+ }
@@ -1,2 +1,3 @@
1
- export { type Cookie, CookieJar, type CookieJarOptions, type CookieStore, domainMatches, type InfoResult, isExpired, matchingCookies, mergeCookies, type OriginEntry, pathMatches, } from "./client.js";
1
+ export { type Cookie, CookieJar, type CookieJarOptions, type CookieStore, CookieStoreParseError, domainMatches, type InfoResult, isExpired, matchingCookies, mergeCookies, type OriginEntry, pathMatches, } from "./client.js";
2
+ export { applyExtraCookies, type ExtraCookies } from "./extra.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,aAAa,EACb,KAAK,UAAU,EACf,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,WAAW,EAChB,WAAW,GACZ,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,qBAAqB,EACrB,aAAa,EACb,KAAK,UAAU,EACf,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,WAAW,EAChB,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -1 +1,2 @@
1
- export { CookieJar, domainMatches, isExpired, matchingCookies, mergeCookies, pathMatches, } from "./client.js";
1
+ export { CookieJar, CookieStoreParseError, domainMatches, isExpired, matchingCookies, mergeCookies, pathMatches, } from "./client.js";
2
+ export { applyExtraCookies } from "./extra.js";
@@ -0,0 +1,124 @@
1
+ export declare const DURABLE_JOB_SCHEMA_VERSION = 1;
2
+ /** How often the supervisor rewrites status.json while a job runs. */
3
+ export declare const DURABLE_JOB_HEARTBEAT_MS = 15000;
4
+ /** A running job whose heartbeat is older than this earns a staleness warning,
5
+ * but stays "running" while its PID is alive. */
6
+ export declare const DURABLE_JOB_HEARTBEAT_STALE_MS = 120000;
7
+ export declare const DURABLE_JOB_DOCUMENT_FILENAME = "job.json";
8
+ export declare const DURABLE_JOB_STATUS_FILENAME = "status.json";
9
+ export declare const DURABLE_JOB_LOG_FILENAME = "job.log";
10
+ /** States a supervisor writes. "completed" is the only terminal one. */
11
+ export type DurableJobState = "launching" | "queued" | "running" | "completed";
12
+ /** States a reader can observe. "dead" is derived, never written. */
13
+ export type DurableJobObservedState = DurableJobState | "dead";
14
+ export interface DurableJobDocument {
15
+ schema_version: number;
16
+ job_id: string;
17
+ /** Admission resource the job queues on. */
18
+ resource: string;
19
+ /** Concurrent holders allowed on that resource. */
20
+ capacity: number;
21
+ /** Human-readable description shown in listings. */
22
+ label: string;
23
+ /** Argument vector of the wrapped command; argv[0] is the executable. */
24
+ argv: string[];
25
+ /** Directory the wrapped command runs in. */
26
+ cwd: string;
27
+ created_at: string;
28
+ }
29
+ export interface DurableJobStatus {
30
+ schema_version: number;
31
+ job_id: string;
32
+ /** PID of the supervisor, not of the wrapped command. */
33
+ pid: number;
34
+ state: DurableJobState;
35
+ started_at: string;
36
+ updated_at: string;
37
+ /** Present while the supervisor waits for an admission slot. */
38
+ queue?: {
39
+ resource: string;
40
+ waiting_since: string;
41
+ };
42
+ exit_code?: number | null;
43
+ signal?: string | null;
44
+ }
45
+ export interface DurableJobReport {
46
+ state: DurableJobObservedState;
47
+ /** The job reached "completed"; exit_code and signal are final. */
48
+ terminal: boolean;
49
+ job_id: string | null;
50
+ pid: number | null;
51
+ resource: string | null;
52
+ label: string | null;
53
+ argv: string[] | null;
54
+ exit_code: number | null;
55
+ signal: string | null;
56
+ created_at: string | null;
57
+ started_at: string | null;
58
+ updated_at: string | null;
59
+ /** now minus status.updated_at; null without a parseable heartbeat. */
60
+ heartbeat_age_ms: number | null;
61
+ queue?: {
62
+ resource: string;
63
+ waiting_since: string;
64
+ };
65
+ job_dir: string;
66
+ log_path: string;
67
+ warnings: string[];
68
+ }
69
+ export interface DurableJobDeps {
70
+ /** PID liveness probe (injectable for tests). Default: process.kill(pid, 0). */
71
+ pidAlive?: (pid: number) => boolean;
72
+ /** Evaluation instant in epoch milliseconds. Default: Date.now(). */
73
+ now?: () => number;
74
+ }
75
+ export type DurableJobClassification = {
76
+ ok: true;
77
+ report: DurableJobReport;
78
+ } | {
79
+ ok: false;
80
+ error: string;
81
+ };
82
+ export interface DurableJobListEntry {
83
+ job_id: string;
84
+ job_dir: string;
85
+ created_at: string | null;
86
+ document: DurableJobDocument | null;
87
+ report: DurableJobReport | null;
88
+ /** Set when the directory exists but its status document is unusable. */
89
+ error?: string;
90
+ }
91
+ /** Directory holding every job record under a base directory. */
92
+ export declare function jobsRoot(base: string): string;
93
+ /** Directory of one job record. Does not create it. */
94
+ export declare function jobDirFor(base: string, jobId: string): string;
95
+ /** Create (idempotently) the directory for one job and return its path. */
96
+ export declare function createJobDir(base: string, jobId: string): string;
97
+ export declare function jobLogPath(jobDir: string): string;
98
+ /** Write the immutable job description. */
99
+ export declare function writeJobDocument(jobDir: string, document: DurableJobDocument): void;
100
+ /** Replace the status document atomically, so a reader never sees a torn file. */
101
+ export declare function writeJobStatus(jobDir: string, status: DurableJobStatus): void;
102
+ /** Read the job description, or null when it is missing or unusable. */
103
+ export declare function readJobDocument(jobDir: string): DurableJobDocument | null;
104
+ /** Read the status document, or null when it is missing or unusable. */
105
+ export declare function readJobStatus(jobDir: string): DurableJobStatus | null;
106
+ /**
107
+ * Classify one job directory, fail-closed. "completed" is terminal and carries
108
+ * the wrapped command's exit code. Any other written state is trusted only
109
+ * while the supervisor PID is alive; otherwise the job is "dead" — the record
110
+ * outlived the process that owned it.
111
+ */
112
+ export declare function classifyJob(jobDir: string, deps?: DurableJobDeps): DurableJobClassification;
113
+ /**
114
+ * Every job record under a base directory, newest first. Ordering keys on the
115
+ * description's created_at, falling back to the status document's started_at,
116
+ * then to the directory name, so a record missing its description still lists.
117
+ */
118
+ export declare function listJobs(base: string, deps?: DurableJobDeps): DurableJobListEntry[];
119
+ /** Describe a job's outcome as a process exit code: the wrapped command's own
120
+ * code, 1 when it died on a signal, 4 when the record is dead. */
121
+ export declare function jobExitCode(report: DurableJobReport): number;
122
+ /** Format a duration the way the status commands do. */
123
+ export declare function formatJobAge(ms: number): string;
124
+ //# sourceMappingURL=durable-job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-job.d.ts","sourceRoot":"","sources":["../../src/lib/durable-job.ts"],"names":[],"mappings":"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"}