@quickchatai/pi-quickreview 1.2.0 → 1.3.2

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 (231) hide show
  1. package/README.md +79 -27
  2. package/assets/config.example.toml +25 -7
  3. package/assets/prompts/onboarding.md +8 -7
  4. package/assets/schemas/review-result.v1.json +60 -0
  5. package/dist/extensions/quickreview.d.ts +17 -5
  6. package/dist/extensions/quickreview.d.ts.map +1 -1
  7. package/dist/extensions/quickreview.js +79 -26
  8. package/dist/extensions/quickreview.js.map +1 -1
  9. package/dist/src/engine/auth.d.ts +4 -0
  10. package/dist/src/engine/auth.d.ts.map +1 -1
  11. package/dist/src/engine/auth.js +11 -2
  12. package/dist/src/engine/auth.js.map +1 -1
  13. package/dist/src/engine/lens/activity.d.ts +45 -0
  14. package/dist/src/engine/lens/activity.d.ts.map +1 -0
  15. package/dist/src/engine/lens/activity.js +118 -0
  16. package/dist/src/engine/lens/activity.js.map +1 -0
  17. package/dist/src/engine/lens/runlens.d.ts +7 -0
  18. package/dist/src/engine/lens/runlens.d.ts.map +1 -1
  19. package/dist/src/engine/lens/runlens.js +30 -1
  20. package/dist/src/engine/lens/runlens.js.map +1 -1
  21. package/dist/src/engine/lens/tool.d.ts +7 -0
  22. package/dist/src/engine/lens/tool.d.ts.map +1 -1
  23. package/dist/src/engine/lens/tool.js +101 -18
  24. package/dist/src/engine/lens/tool.js.map +1 -1
  25. package/dist/src/engine/lens/wave.d.ts +6 -0
  26. package/dist/src/engine/lens/wave.d.ts.map +1 -1
  27. package/dist/src/engine/lens/wave.js +16 -0
  28. package/dist/src/engine/lens/wave.js.map +1 -1
  29. package/dist/src/homes.js +1 -1
  30. package/dist/src/homes.js.map +1 -1
  31. package/dist/src/live/events.d.ts +3 -0
  32. package/dist/src/live/events.d.ts.map +1 -1
  33. package/dist/src/live/events.js.map +1 -1
  34. package/dist/src/live/lensline.d.ts +20 -0
  35. package/dist/src/live/lensline.d.ts.map +1 -0
  36. package/dist/src/live/lensline.js +70 -0
  37. package/dist/src/live/lensline.js.map +1 -0
  38. package/dist/src/live/projection.d.ts +55 -0
  39. package/dist/src/live/projection.d.ts.map +1 -0
  40. package/dist/src/live/projection.js +465 -0
  41. package/dist/src/live/projection.js.map +1 -0
  42. package/dist/src/live/read.d.ts +24 -0
  43. package/dist/src/live/read.d.ts.map +1 -0
  44. package/dist/src/live/read.js +60 -0
  45. package/dist/src/live/read.js.map +1 -0
  46. package/dist/src/live/writer.d.ts +3 -0
  47. package/dist/src/live/writer.d.ts.map +1 -1
  48. package/dist/src/live/writer.js +6 -1
  49. package/dist/src/live/writer.js.map +1 -1
  50. package/dist/src/localreview.d.ts +18 -8
  51. package/dist/src/localreview.d.ts.map +1 -1
  52. package/dist/src/localreview.js +103 -43
  53. package/dist/src/localreview.js.map +1 -1
  54. package/dist/src/modelroute.d.ts +4 -0
  55. package/dist/src/modelroute.d.ts.map +1 -1
  56. package/dist/src/modelroute.js +21 -0
  57. package/dist/src/modelroute.js.map +1 -1
  58. package/dist/src/plugin/args.d.ts +38 -7
  59. package/dist/src/plugin/args.d.ts.map +1 -1
  60. package/dist/src/plugin/args.js +134 -37
  61. package/dist/src/plugin/args.js.map +1 -1
  62. package/dist/src/plugin/index.d.ts.map +1 -1
  63. package/dist/src/plugin/index.js +5 -1
  64. package/dist/src/plugin/index.js.map +1 -1
  65. package/dist/src/plugin/login.d.ts +8 -4
  66. package/dist/src/plugin/login.d.ts.map +1 -1
  67. package/dist/src/plugin/login.js +25 -32
  68. package/dist/src/plugin/login.js.map +1 -1
  69. package/dist/src/plugin/messages.d.ts +7 -0
  70. package/dist/src/plugin/messages.d.ts.map +1 -0
  71. package/dist/src/plugin/messages.js +13 -0
  72. package/dist/src/plugin/messages.js.map +1 -0
  73. package/dist/src/plugin/peers.d.ts +17 -0
  74. package/dist/src/plugin/peers.d.ts.map +1 -0
  75. package/dist/src/plugin/peers.js +55 -0
  76. package/dist/src/plugin/peers.js.map +1 -0
  77. package/dist/src/plugin/review.d.ts +52 -6
  78. package/dist/src/plugin/review.d.ts.map +1 -1
  79. package/dist/src/plugin/review.js +261 -95
  80. package/dist/src/plugin/review.js.map +1 -1
  81. package/dist/src/plugin/scan.d.ts +80 -44
  82. package/dist/src/plugin/scan.d.ts.map +1 -1
  83. package/dist/src/plugin/scan.js +450 -233
  84. package/dist/src/plugin/scan.js.map +1 -1
  85. package/dist/src/plugin/scancases.d.ts +33 -0
  86. package/dist/src/plugin/scancases.d.ts.map +1 -0
  87. package/dist/src/plugin/scancases.js +153 -0
  88. package/dist/src/plugin/scancases.js.map +1 -0
  89. package/dist/src/plugin/settings.d.ts +18 -0
  90. package/dist/src/plugin/settings.d.ts.map +1 -0
  91. package/dist/src/plugin/settings.js +47 -0
  92. package/dist/src/plugin/settings.js.map +1 -0
  93. package/dist/src/plugin/setup.d.ts +35 -12
  94. package/dist/src/plugin/setup.d.ts.map +1 -1
  95. package/dist/src/plugin/setup.js +258 -118
  96. package/dist/src/plugin/setup.js.map +1 -1
  97. package/dist/src/plugin/versiongate.d.ts +3 -0
  98. package/dist/src/plugin/versiongate.d.ts.map +1 -1
  99. package/dist/src/plugin/versiongate.js +9 -4
  100. package/dist/src/plugin/versiongate.js.map +1 -1
  101. package/dist/src/profileoverrides.d.ts +3 -0
  102. package/dist/src/profileoverrides.d.ts.map +1 -1
  103. package/dist/src/profileoverrides.js +9 -2
  104. package/dist/src/profileoverrides.js.map +1 -1
  105. package/dist/src/reviewapi.d.ts +22 -31
  106. package/dist/src/reviewapi.d.ts.map +1 -1
  107. package/dist/src/reviewapi.js +90 -66
  108. package/dist/src/reviewapi.js.map +1 -1
  109. package/dist/src/reviewdest.d.ts +1 -1
  110. package/dist/src/reviewdest.d.ts.map +1 -1
  111. package/dist/src/reviewdest.js +4 -3
  112. package/dist/src/reviewdest.js.map +1 -1
  113. package/dist/src/reviewtarget.d.ts +36 -0
  114. package/dist/src/reviewtarget.d.ts.map +1 -0
  115. package/dist/src/reviewtarget.js +6 -0
  116. package/dist/src/reviewtarget.js.map +1 -0
  117. package/dist/src/round-progress.d.ts +5 -0
  118. package/dist/src/round-progress.d.ts.map +1 -1
  119. package/dist/src/round-progress.js.map +1 -1
  120. package/dist/src/round-stage-deadlines.d.ts +26 -1
  121. package/dist/src/round-stage-deadlines.d.ts.map +1 -1
  122. package/dist/src/round-stage-deadlines.js +45 -2
  123. package/dist/src/round-stage-deadlines.js.map +1 -1
  124. package/dist/src/savedpost.d.ts +65 -0
  125. package/dist/src/savedpost.d.ts.map +1 -0
  126. package/dist/src/savedpost.js +131 -0
  127. package/dist/src/savedpost.js.map +1 -0
  128. package/dist/src/scan/caseread.d.ts +84 -0
  129. package/dist/src/scan/caseread.d.ts.map +1 -0
  130. package/dist/src/scan/caseread.js +263 -0
  131. package/dist/src/scan/caseread.js.map +1 -0
  132. package/dist/src/scan/channelfs.d.ts +6 -0
  133. package/dist/src/scan/channelfs.d.ts.map +1 -0
  134. package/dist/src/scan/channelfs.js +27 -0
  135. package/dist/src/scan/channelfs.js.map +1 -0
  136. package/dist/src/scan/cutrender.d.ts +13 -0
  137. package/dist/src/scan/cutrender.d.ts.map +1 -0
  138. package/dist/src/scan/cutrender.js +51 -0
  139. package/dist/src/scan/cutrender.js.map +1 -0
  140. package/dist/src/scan/facts.d.ts +72 -0
  141. package/dist/src/scan/facts.d.ts.map +1 -0
  142. package/dist/src/scan/facts.js +593 -0
  143. package/dist/src/scan/facts.js.map +1 -0
  144. package/dist/src/scan/findings.d.ts +320 -0
  145. package/dist/src/scan/findings.d.ts.map +1 -0
  146. package/dist/src/scan/findings.js +948 -0
  147. package/dist/src/scan/findings.js.map +1 -0
  148. package/dist/src/scan/ledger.d.ts +116 -0
  149. package/dist/src/scan/ledger.d.ts.map +1 -0
  150. package/dist/src/scan/ledger.js +207 -0
  151. package/dist/src/scan/ledger.js.map +1 -0
  152. package/dist/src/scan/presentation.d.ts +19 -0
  153. package/dist/src/scan/presentation.d.ts.map +1 -0
  154. package/dist/src/scan/presentation.js +9 -0
  155. package/dist/src/scan/presentation.js.map +1 -0
  156. package/dist/src/scan/scan.d.ts +354 -0
  157. package/dist/src/scan/scan.d.ts.map +1 -0
  158. package/dist/src/scan/scan.js +725 -0
  159. package/dist/src/scan/scan.js.map +1 -0
  160. package/dist/src/scan/scanagents.d.ts +23 -0
  161. package/dist/src/scan/scanagents.d.ts.map +1 -0
  162. package/dist/src/scan/scanagents.js +99 -0
  163. package/dist/src/scan/scanagents.js.map +1 -0
  164. package/dist/src/scan/scanassemble.d.ts +65 -0
  165. package/dist/src/scan/scanassemble.d.ts.map +1 -0
  166. package/dist/src/scan/scanassemble.js +93 -0
  167. package/dist/src/scan/scanassemble.js.map +1 -0
  168. package/dist/src/scan/scanfs.d.ts +26 -0
  169. package/dist/src/scan/scanfs.d.ts.map +1 -0
  170. package/dist/src/scan/scanfs.js +229 -0
  171. package/dist/src/scan/scanfs.js.map +1 -0
  172. package/dist/src/scan/scanlease.d.ts +50 -0
  173. package/dist/src/scan/scanlease.d.ts.map +1 -0
  174. package/dist/src/scan/scanlease.js +251 -0
  175. package/dist/src/scan/scanlease.js.map +1 -0
  176. package/dist/src/scan/scanleaves.d.ts +55 -0
  177. package/dist/src/scan/scanleaves.d.ts.map +1 -0
  178. package/dist/src/scan/scanleaves.js +258 -0
  179. package/dist/src/scan/scanleaves.js.map +1 -0
  180. package/dist/src/scan/scanports.d.ts +79 -0
  181. package/dist/src/scan/scanports.d.ts.map +1 -0
  182. package/dist/src/scan/scanports.js +8 -0
  183. package/dist/src/scan/scanports.js.map +1 -0
  184. package/dist/src/scan/scanrender.d.ts +128 -0
  185. package/dist/src/scan/scanrender.d.ts.map +1 -0
  186. package/dist/src/scan/scanrender.js +527 -0
  187. package/dist/src/scan/scanrender.js.map +1 -0
  188. package/dist/src/scan/scanreport.d.ts +126 -0
  189. package/dist/src/scan/scanreport.d.ts.map +1 -0
  190. package/dist/src/scan/scanreport.js +289 -0
  191. package/dist/src/scan/scanreport.js.map +1 -0
  192. package/dist/src/scan/scanrun.d.ts +472 -0
  193. package/dist/src/scan/scanrun.d.ts.map +1 -0
  194. package/dist/src/scan/scanrun.js +1733 -0
  195. package/dist/src/scan/scanrun.js.map +1 -0
  196. package/dist/src/scan/scantree.d.ts +21 -0
  197. package/dist/src/scan/scantree.d.ts.map +1 -0
  198. package/dist/src/scan/scantree.js +297 -0
  199. package/dist/src/scan/scantree.js.map +1 -0
  200. package/dist/src/scan/scanverbs.d.ts +208 -0
  201. package/dist/src/scan/scanverbs.d.ts.map +1 -0
  202. package/dist/src/scan/scanverbs.js +689 -0
  203. package/dist/src/scan/scanverbs.js.map +1 -0
  204. package/dist/src/scan/stage2.d.ts +221 -0
  205. package/dist/src/scan/stage2.d.ts.map +1 -0
  206. package/dist/src/scan/stage2.js +617 -0
  207. package/dist/src/scan/stage2.js.map +1 -0
  208. package/dist/src/scan/stage2request.d.ts +125 -0
  209. package/dist/src/scan/stage2request.d.ts.map +1 -0
  210. package/dist/src/scan/stage2request.js +259 -0
  211. package/dist/src/scan/stage2request.js.map +1 -0
  212. package/dist/src/scan/textsniff.d.ts +14 -0
  213. package/dist/src/scan/textsniff.d.ts.map +1 -0
  214. package/dist/src/scan/textsniff.js +88 -0
  215. package/dist/src/scan/textsniff.js.map +1 -0
  216. package/dist/src/scan/unitcut.d.ts +135 -0
  217. package/dist/src/scan/unitcut.d.ts.map +1 -0
  218. package/dist/src/scan/unitcut.js +437 -0
  219. package/dist/src/scan/unitcut.js.map +1 -0
  220. package/dist/src/scan/unitcutstore.d.ts +155 -0
  221. package/dist/src/scan/unitcutstore.d.ts.map +1 -0
  222. package/dist/src/scan/unitcutstore.js +381 -0
  223. package/dist/src/scan/unitcutstore.js.map +1 -0
  224. package/dist/src/setupchecklist.d.ts +240 -0
  225. package/dist/src/setupchecklist.d.ts.map +1 -0
  226. package/dist/src/setupchecklist.js +407 -0
  227. package/dist/src/setupchecklist.js.map +1 -0
  228. package/package.json +7 -3
  229. package/skills/qcr-review/SKILL.md +24 -0
  230. package/skills/qcr-scan/SKILL.md +23 -0
  231. package/skills/qcr-setup/SKILL.md +18 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanfs.js","sourceRoot":"","sources":["../../../src/scan/scanfs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ export declare const DRIVER_DIR = "scan/driver";
2
+ /** How often a driving runner renews its lease. */
3
+ export declare const LEASE_RENEW_MS = 5000;
4
+ /** A lease not renewed for this long has lapsed. */
5
+ export declare const LEASE_TTL_MS = 30000;
6
+ export interface LeaseRecord {
7
+ holder: string;
8
+ /** The run it drives; null while a start is still planning. */
9
+ run: string | null;
10
+ pid: number;
11
+ host: string;
12
+ at: string;
13
+ }
14
+ /** The lease port a ScanRunner drives. */
15
+ export interface ScanLease {
16
+ /** This runner's holder id. */
17
+ readonly holder: string;
18
+ /** Take the lease for `run` — or renew it when this runner holds it
19
+ * already; false when another live holder has it, or another claim won. */
20
+ claim(run: string | null): boolean;
21
+ /** Renew the lease this runner holds; false when it holds none any more. */
22
+ renew(run: string | null): boolean;
23
+ /** Give the lease up — only when this runner still holds it. */
24
+ release(): void;
25
+ /** Why the last claim failed, when it was not another holder's live lease. */
26
+ readonly lastError: string | null;
27
+ /** The live lease of ANOTHER holder, or null. */
28
+ other(): LeaseRecord | null;
29
+ }
30
+ /** The current lease record under the state home `home`, live or not —
31
+ * null when there is none or it cannot be read. For a reader that holds
32
+ * none (the TUI), which judges it with `leaseIsLive`. */
33
+ export declare function currentLease(home: string): LeaseRecord | null;
34
+ export declare const leaseIsLive: (r: LeaseRecord, now?: Date) => boolean;
35
+ /** The live lease under the state home `home`, or null. */
36
+ export declare function liveLease(home: string, now?: Date): LeaseRecord | null;
37
+ /** The lease as generation files under the state home `home`. Total: a
38
+ * failed write is a failed claim or renewal, never a throw. */
39
+ export declare function fileLease(home: string, opts?: {
40
+ clock?: () => Date;
41
+ holder?: string;
42
+ /** Called between reading the lease and creating the next generation. */
43
+ beforeCreate?: () => void;
44
+ /** Called right after generation `gen` was created, before the check
45
+ * that no newer one appeared. */
46
+ afterCreate?: (gen: number) => void;
47
+ /** The hard link (`fs.linkSync`). */
48
+ link?: (existing: string, path: string) => void;
49
+ }): ScanLease;
50
+ //# sourceMappingURL=scanlease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanlease.d.ts","sourceRoot":"","sources":["../../../src/scan/scanlease.ts"],"names":[],"mappings":"AA8CA,eAAO,MAAM,UAAU,gBAAgB,CAAC;AACxC,mDAAmD;AACnD,eAAO,MAAM,cAAc,OAAQ,CAAC;AACpC,oDAAoD;AACpD,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;+EAC2E;IAC3E,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IACnC,4EAA4E;IAC5E,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IACnC,gEAAgE;IAChE,OAAO,IAAI,IAAI,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iDAAiD;IACjD,KAAK,IAAI,WAAW,GAAG,IAAI,CAAC;CAC7B;AAmED;;yDAEyD;AACzD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAE7D;AAED,eAAO,MAAM,WAAW,GAAI,GAAG,WAAW,EAAE,MAAK,IAAiB,KAAG,OAAyB,CAAC;AAE/F,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,WAAW,GAAG,IAAI,CAGlF;AAED;+DAC+D;AAC/D,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IACJ,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B;qCACiC;IACjC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,qCAAqC;IACrC,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,GACL,SAAS,CA2HX"}
@@ -0,0 +1,251 @@
1
+ // The scan driver lease: which process drives the scan in flight, across
2
+ // processes. A ScanRunner knows its OWN run (`active`); a run recorded as
3
+ // `running` that this runner does not drive is either interrupted or driven
4
+ // by another process sharing the state home — the companion, or a
5
+ // `qcr scan start` driver. The lease tells them apart: the driving runner
6
+ // holds it and renews it while it drives; a holder that stopped renewing
7
+ // (killed, its machine asleep) has let it lapse, and its run reads as
8
+ // interrupted again.
9
+ //
10
+ // The lease is a sequence of GENERATIONS, `scan/driver/<n>.json`, the highest
11
+ // one current. A claim creates the next generation with an exclusive create
12
+ // (the record is written aside and hard-linked into place, which fails when
13
+ // the name exists — or, on a filesystem that refuses hard links, an O_EXCL
14
+ // create), so of any number of processes claiming at once exactly one wins
15
+ // each generation; the winner then checks no newer generation appeared before
16
+ // it takes the lease. A holder renews only its own generation; once a newer
17
+ // one exists (its lease lapsed and was taken over) it no longer holds the
18
+ // lease, and renewal says so. An unreadable current generation (an O_EXCL
19
+ // create not yet written, or a corrupt file) counts as live until its file is
20
+ // as old as the lease's life, then as no lease. A claim prunes every
21
+ // generation but the one before its own, and temp files older than the
22
+ // lease's life.
23
+ //
24
+ // What the lease does NOT serialise: the records other processes may write
25
+ // while a run is driven elsewhere. `qcr scan match` / `dismiss` and the
26
+ // companion's triage rewrite `scan/findings.json`, and a driver's reconcile
27
+ // does too, each as a fresh read, a change and a write — between one
28
+ // process's read and its write another's write can be lost. The run record
29
+ // `scan/run.json` is written only by the lease holder; a holder that lost the
30
+ // lease stops its run and writes it no more (scanrun.ts).
31
+ //
32
+ // A leaf: node built-ins only, so the TUI's scan tab reads the lease too.
33
+ import { randomUUID } from "node:crypto";
34
+ import { linkSync, statSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
35
+ import { hostname } from "node:os";
36
+ import { join } from "node:path";
37
+ export const DRIVER_DIR = "scan/driver";
38
+ /** How often a driving runner renews its lease. */
39
+ export const LEASE_RENEW_MS = 5_000;
40
+ /** A lease not renewed for this long has lapsed. */
41
+ export const LEASE_TTL_MS = 30_000;
42
+ function parse(text) {
43
+ try {
44
+ const r = JSON.parse(text);
45
+ if (typeof r.holder !== "string" || typeof r.at !== "string")
46
+ return null;
47
+ return {
48
+ holder: r.holder,
49
+ run: typeof r.run === "string" ? r.run : null,
50
+ pid: typeof r.pid === "number" ? r.pid : 0,
51
+ host: typeof r.host === "string" ? r.host : "",
52
+ at: r.at,
53
+ };
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
59
+ const GEN = /^(\d+)\.json$/;
60
+ /** Has the file at `path` been written within the lease's life? Files carry
61
+ * the real clock's mtime, so this reads the real clock. */
62
+ function fresh(path) {
63
+ try {
64
+ return Date.now() - statSync(path).mtimeMs < LEASE_TTL_MS;
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ const errText = (err) => err instanceof Error
71
+ ? `${err.code ?? "error"}: ${err.message}`
72
+ : String(err);
73
+ /** Where a hard link is refused rather than the name taken. */
74
+ const NO_LINK = new Set(["EPERM", "ENOTSUP", "EOPNOTSUPP", "EXDEV"]);
75
+ /** The current generation and its record (null: unreadable), or null when
76
+ * there is no lease at all. */
77
+ function current(dir) {
78
+ let names;
79
+ try {
80
+ names = readdirSync(dir);
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ const gens = names.flatMap((n) => {
86
+ const m = GEN.exec(n);
87
+ return m ? [Number(m[1])] : [];
88
+ });
89
+ if (gens.length === 0)
90
+ return null;
91
+ const gen = Math.max(...gens);
92
+ let record = null;
93
+ try {
94
+ record = parse(readFileSync(join(dir, `${gen}.json`), "utf8"));
95
+ }
96
+ catch {
97
+ record = null;
98
+ }
99
+ return { gen, record };
100
+ }
101
+ const isLive = (r, now) => {
102
+ const at = Date.parse(r.at);
103
+ return Number.isFinite(at) && now.getTime() - at < LEASE_TTL_MS;
104
+ };
105
+ /** The current lease record under the state home `home`, live or not —
106
+ * null when there is none or it cannot be read. For a reader that holds
107
+ * none (the TUI), which judges it with `leaseIsLive`. */
108
+ export function currentLease(home) {
109
+ return current(join(home, DRIVER_DIR))?.record ?? null;
110
+ }
111
+ export const leaseIsLive = (r, now = new Date()) => isLive(r, now);
112
+ /** The live lease under the state home `home`, or null. */
113
+ export function liveLease(home, now = new Date()) {
114
+ const r = currentLease(home);
115
+ return r && isLive(r, now) ? r : null;
116
+ }
117
+ /** The lease as generation files under the state home `home`. Total: a
118
+ * failed write is a failed claim or renewal, never a throw. */
119
+ export function fileLease(home, opts = {}) {
120
+ const dir = join(home, DRIVER_DIR);
121
+ const clock = opts.clock ?? (() => new Date());
122
+ const holder = opts.holder ?? `${hostname()}:${process.pid}:${randomUUID().slice(0, 8)}`;
123
+ const link = opts.link ?? linkSync;
124
+ let mine = null;
125
+ let lastError = null;
126
+ const record = (run, at = clock()) => `${JSON.stringify({ holder, run, pid: process.pid, host: hostname(), at: at.toISOString() })}\n`;
127
+ // Only its holder rewrites a generation: tmp + rename.
128
+ const rewrite = (gen, text) => {
129
+ const tmp = join(dir, `.${gen}.${randomUUID()}.tmp`);
130
+ try {
131
+ writeFileSync(tmp, text);
132
+ renameSync(tmp, join(dir, `${gen}.json`));
133
+ return true;
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ };
139
+ const prune = (below) => {
140
+ let names;
141
+ try {
142
+ names = readdirSync(dir);
143
+ }
144
+ catch {
145
+ return;
146
+ }
147
+ for (const name of names) {
148
+ const m = GEN.exec(name);
149
+ const stale = (m !== null && Number(m[1]) < below) ||
150
+ (name.startsWith(".") && name.endsWith(".tmp") && !fresh(join(dir, name)));
151
+ if (!stale)
152
+ continue;
153
+ try {
154
+ unlinkSync(join(dir, name));
155
+ }
156
+ catch {
157
+ // Another claimer pruned it first.
158
+ }
159
+ }
160
+ };
161
+ /** Create generation `gen` exclusively; null, or why not. */
162
+ const create = (gen, text) => {
163
+ const path = join(dir, `${gen}.json`);
164
+ const tmp = join(dir, `.claim.${randomUUID()}.tmp`);
165
+ try {
166
+ mkdirSync(dir, { recursive: true });
167
+ writeFileSync(tmp, text);
168
+ try {
169
+ link(tmp, path);
170
+ return null;
171
+ }
172
+ catch (err) {
173
+ if (!NO_LINK.has(err.code ?? ""))
174
+ throw err;
175
+ }
176
+ writeFileSync(path, text, { flag: "wx" });
177
+ return null;
178
+ }
179
+ catch (err) {
180
+ return errText(err);
181
+ }
182
+ finally {
183
+ try {
184
+ unlinkSync(tmp);
185
+ }
186
+ catch {
187
+ // Never written.
188
+ }
189
+ }
190
+ };
191
+ const lease = {
192
+ holder,
193
+ get lastError() {
194
+ return lastError;
195
+ },
196
+ claim(run) {
197
+ lastError = null;
198
+ const cur = current(dir);
199
+ if (cur !== null && mine === cur.gen)
200
+ return lease.renew(run);
201
+ if (cur !== null && cur.record === null && fresh(join(dir, `${cur.gen}.json`))) {
202
+ lastError = `generation ${cur.gen} is unreadable and was written within the lease's life — it may still be being written`;
203
+ return false;
204
+ }
205
+ if (cur?.record && cur.record.holder !== holder && isLive(cur.record, clock()))
206
+ return false;
207
+ opts.beforeCreate?.();
208
+ const gen = (cur?.gen ?? 0) + 1;
209
+ const failed = create(gen, record(run));
210
+ if (failed !== null) {
211
+ lastError = `generation ${gen} could not be created (${failed})`;
212
+ return false;
213
+ }
214
+ opts.afterCreate?.(gen);
215
+ // Won the create; still the newest?
216
+ if (current(dir)?.gen !== gen) {
217
+ lastError = `a newer generation than ${gen} appeared as it was created`;
218
+ return false;
219
+ }
220
+ mine = gen;
221
+ prune(gen - 1);
222
+ return true;
223
+ },
224
+ renew(run) {
225
+ const cur = current(dir);
226
+ if (mine === null ||
227
+ cur === null ||
228
+ cur.gen !== mine ||
229
+ cur.record === null ||
230
+ !isLive(cur.record, clock())) {
231
+ mine = null;
232
+ return false;
233
+ }
234
+ return rewrite(mine, record(run));
235
+ },
236
+ release() {
237
+ const cur = current(dir);
238
+ if (mine !== null && cur !== null && cur.gen === mine)
239
+ rewrite(mine, record(null, new Date(0)));
240
+ mine = null;
241
+ },
242
+ other() {
243
+ const cur = current(dir);
244
+ if (cur === null || cur.record === null || cur.gen === mine)
245
+ return null;
246
+ return isLive(cur.record, clock()) ? cur.record : null;
247
+ },
248
+ };
249
+ return lease;
250
+ }
251
+ //# sourceMappingURL=scanlease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanlease.js","sourceRoot":"","sources":["../../../src/scan/scanlease.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
2
+ import type { CaseRunner } from "./scanports.js";
3
+ import type { Runtime, SessionSpec } from "../engine/port.js";
4
+ import type { SessionExtras } from "../engine/sdkruntime.js";
5
+ /** A leaf turn's hard wall clock. The login's own case budget
6
+ * (`SCAN_CASE_AUTH_BUDGET_MS`) is the usual bound and aborts first; this one
7
+ * only backs it where no login budget applies, and is kept past it so the
8
+ * budget's last-resort step (it ends the process) never races the abort. */
9
+ export declare const LEAF_TURN_TIMEOUT_MS: number;
10
+ /** The per-session pieces the runner hands whoever builds the runtime: the
11
+ * `extras` of `sdkRuntime` (packages/pi-plugin/src/engine/sdkruntime.ts). */
12
+ export type LeafExtras = (spec: SessionSpec) => SessionExtras;
13
+ export interface SdkCaseRunnerOptions {
14
+ /** The scan agent build: `<agent>.md` files. */
15
+ agentsDir: string;
16
+ /** The leaf's working directory: first-party ground, never the reviewed
17
+ * checkout (whose own pi config must not load). Also what a relative read is
18
+ * resolved against. */
19
+ cwd: string;
20
+ /** Where each case's event stream is kept; absent, none is. */
21
+ transcriptDir?: string;
22
+ /** The env the leaf's env is filtered from. */
23
+ env: NodeJS.ProcessEnv;
24
+ /** The user's home: a read's `~` expands against it. */
25
+ home: string;
26
+ /** The model of an agent that names none. */
27
+ defaultModel?: string;
28
+ /** The runtime this case runs on, built with the case's `extras`. Called once
29
+ * per case: a route that must re-read its login per case builds its host
30
+ * here. */
31
+ runtime: (extras: LeafExtras) => Runtime | Promise<Runtime>;
32
+ /** The guard extension. Omitted: bash-guard over the leaf env. */
33
+ guard?: ExtensionFactory;
34
+ /** Wall clock for a case's turn. Omitted: `LEAF_TURN_TIMEOUT_MS`. */
35
+ timeoutMs?: number;
36
+ }
37
+ /** Where a case's session JSONL goes: `<run>/lens-sessions/<case>/` beside the
38
+ * transcript `rel` names (`<run>/<case>.jsonl`). */
39
+ export declare function caseSessionDir(transcriptDir: string, rel: string): string;
40
+ /**
41
+ * Runs a case as one SDK session, the way `piCaseRunner` runs it as one child:
42
+ * the agent's model (its `:thinking` suffix as the session's thinking level),
43
+ * its tools, its body appended to pi's system prompt, the task as the one
44
+ * message. The answer is the last assistant message's text; the cost is pi's
45
+ * own per-message accounting summed (null when it reported none); `reads` is the
46
+ * `read`/`grep` calls that finished without an error, resolved against `cwd`.
47
+ * With `transcriptDir` the session's whole event stream is kept at
48
+ * `transcriptFile(spec.id)` under it, the format `pi --mode json` prints, and
49
+ * its session JSONL beside it (`caseSessionDir`). An
50
+ * abort stops the session (abort, then dispose after the stop grace). Every
51
+ * outcome resolves — a runtime that cannot be built or a session that cannot
52
+ * start is a failed case, never a rejection.
53
+ */
54
+ export declare function sdkCaseRunner(opts: SdkCaseRunnerOptions): CaseRunner;
55
+ //# sourceMappingURL=scanleaves.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanleaves.d.ts","sourceRoot":"","sources":["../../../src/scan/scanleaves.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAexE,OAAO,KAAK,EAAe,UAAU,EAAY,MAAM,gBAAgB,CAAC;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;4EAG4E;AAC5E,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAqB3E;6EAC6E;AAC7E,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,WAAW,KAAK,aAAa,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB;;2BAEuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;eAEW;IACX,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,kEAAkE;IAClE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoBD;oDACoD;AACpD,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,UAAU,CA6KpE"}
@@ -0,0 +1,258 @@
1
+ // The scan's leaf runner on the SDK: the `CaseRunner` port
2
+ // (scanports.ts) over the runtime port, the in-process twin of
3
+ // `piCaseRunner` (caserunner.ts). One case is one session on the
4
+ // runtime, with the same pieces the child process gets: the agent file's model,
5
+ // tools and body (appended to pi's own system prompt), bash-guard the one
6
+ // extension, no skill and no context file, a cwd that is first-party ground and
7
+ // never the reviewed checkout, and the allowlisted env. The outcome, the reads
8
+ // and the kept transcript are read off the same event stream by the same
9
+ // reader (`caseReadout`), so a case answers identically whichever host ran it.
10
+ //
11
+ // It builds no host: the runtime a case runs on is the caller's (`runtime`),
12
+ // so this module loads no SDK. Two hosts hand one in: the `qcr scan` driver,
13
+ // a disposable one-scan process (src/engine/caserunner-sdk.ts
14
+ // `scanSdkCases`), and the pi plugin's /qcr-scan, inside the person's own pi.
15
+ import { appendFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { basename, dirname, join } from "node:path";
18
+ import { bashGuardConfigFromEnv, createBashGuard } from "../../extensions/bash-guard.js";
19
+ import { CASE_STOP_GRACE_MS } from "../casebudget.js";
20
+ import { splitModelSpec } from "../reviewconfig.js";
21
+ import { AuthError, SCAN_CASE_AUTH_BUDGET_MS } from "../engine/auth.js";
22
+ import { TRANSCRIPT_RUNS_KEPT, caseReadout, leafEnv, parseAgentFile, pruneTranscripts, stderrExcerpt, transcriptFile, } from "./caseread.js";
23
+ import { LENS_SESSIONS_DIR } from "../engine/openable.js";
24
+ /** A leaf turn's hard wall clock. The login's own case budget
25
+ * (`SCAN_CASE_AUTH_BUDGET_MS`) is the usual bound and aborts first; this one
26
+ * only backs it where no login budget applies, and is kept past it so the
27
+ * budget's last-resort step (it ends the process) never races the abort. */
28
+ export const LEAF_TURN_TIMEOUT_MS = SCAN_CASE_AUTH_BUDGET_MS + 15 * 60_000;
29
+ /** What the model's bash may draw from the leaf env, beyond pi's defaults:
30
+ * certificates, proxies, XDG dirs and the temp dirs. Never a provider key. */
31
+ const LEAF_BASH_ENV_ALLOW = [
32
+ "TMP",
33
+ "TEMP",
34
+ "XDG_*",
35
+ "SSL_CERT_FILE",
36
+ "SSL_CERT_DIR",
37
+ "NODE_EXTRA_CA_CERTS",
38
+ "HTTP_PROXY",
39
+ "HTTPS_PROXY",
40
+ "NO_PROXY",
41
+ "ALL_PROXY",
42
+ "http_proxy",
43
+ "https_proxy",
44
+ "no_proxy",
45
+ "all_proxy",
46
+ ];
47
+ /** A transcript's path under `dir`, relative and absolute, with its run
48
+ * directory made (and the old runs pruned when it is new); null when it cannot
49
+ * be made. A transcript is evidence, never a reason to fail a case. */
50
+ function openTranscript(dir, id) {
51
+ try {
52
+ const rel = transcriptFile(id);
53
+ const at = join(dir, rel);
54
+ const runDir = rel.includes("/") ? rel.slice(0, rel.indexOf("/")) : null;
55
+ const fresh = runDir !== null && !existsSync(join(dir, runDir));
56
+ mkdirSync(dirname(at), { recursive: true });
57
+ if (fresh)
58
+ pruneTranscripts(dir, TRANSCRIPT_RUNS_KEPT, runDir);
59
+ writeFileSync(at, "", { mode: 0o600 });
60
+ return { rel, at };
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ /** Where a case's session JSONL goes: `<run>/lens-sessions/<case>/` beside the
67
+ * transcript `rel` names (`<run>/<case>.jsonl`). */
68
+ export function caseSessionDir(transcriptDir, rel) {
69
+ return join(transcriptDir, dirname(rel), LENS_SESSIONS_DIR, basename(rel, ".jsonl"));
70
+ }
71
+ /**
72
+ * Runs a case as one SDK session, the way `piCaseRunner` runs it as one child:
73
+ * the agent's model (its `:thinking` suffix as the session's thinking level),
74
+ * its tools, its body appended to pi's system prompt, the task as the one
75
+ * message. The answer is the last assistant message's text; the cost is pi's
76
+ * own per-message accounting summed (null when it reported none); `reads` is the
77
+ * `read`/`grep` calls that finished without an error, resolved against `cwd`.
78
+ * With `transcriptDir` the session's whole event stream is kept at
79
+ * `transcriptFile(spec.id)` under it, the format `pi --mode json` prints, and
80
+ * its session JSONL beside it (`caseSessionDir`). An
81
+ * abort stops the session (abort, then dispose after the stop grace). Every
82
+ * outcome resolves — a runtime that cannot be built or a session that cannot
83
+ * start is a failed case, never a rejection.
84
+ */
85
+ export function sdkCaseRunner(opts) {
86
+ return {
87
+ run: async (spec, signal, onCost) => {
88
+ if (!/^[a-z][a-z0-9-]*$/.test(spec.agent))
89
+ return {
90
+ ok: false,
91
+ reason: `not an agent name: ${JSON.stringify(spec.agent)}`,
92
+ cost_usd: 0,
93
+ };
94
+ let agent;
95
+ try {
96
+ agent = parseAgentFile(readFileSync(join(opts.agentsDir, `${spec.agent}.md`), "utf8"));
97
+ }
98
+ catch {
99
+ return { ok: false, reason: `no agent ${spec.agent} in ${opts.agentsDir}`, cost_usd: 0 };
100
+ }
101
+ if (agent === null || agent.name !== spec.agent)
102
+ return { ok: false, reason: `${spec.agent}.md is not an agent file`, cost_usd: 0 };
103
+ const modelSpec = agent.model ?? opts.defaultModel;
104
+ if (!modelSpec)
105
+ return { ok: false, reason: `${spec.agent} names no model and none is set`, cost_usd: 0 };
106
+ if (signal.aborted)
107
+ return { ok: false, reason: "stopped before it started", cost_usd: 0 };
108
+ let tmp;
109
+ try {
110
+ tmp = mkdtempSync(join(tmpdir(), "qcr-case-"));
111
+ }
112
+ catch (err) {
113
+ return { ok: false, reason: `no temp directory for the case: ${String(err)}`, cost_usd: 0 };
114
+ }
115
+ let session;
116
+ try {
117
+ const env = leafEnv(opts.env);
118
+ const { model, thinking } = splitModelSpec(modelSpec);
119
+ const guard = opts.guard ?? createBashGuard(bashGuardConfigFromEnv(env, opts.cwd));
120
+ const extras = () => ({
121
+ extensions: [guard],
122
+ appendSystemPrompt: [agent.systemPrompt],
123
+ bashEnv: { allow: LEAF_BASH_ENV_ALLOW },
124
+ ...(thinking !== null
125
+ ? { thinkingLevel: thinking }
126
+ : {}),
127
+ });
128
+ // The transcript: opened before the session, appended event by event,
129
+ // abandoned (and its name dropped from the outcome) at the first write
130
+ // that fails.
131
+ const opened = opts.transcriptDir !== undefined ? openTranscript(opts.transcriptDir, spec.id) : null;
132
+ const sessionSpec = {
133
+ cwd: opts.cwd,
134
+ env,
135
+ model,
136
+ ...(agent.tools.length > 0 ? { tools: agent.tools } : {}),
137
+ name: spec.id,
138
+ // The case's session JSONL beside its transcript, so the case
139
+ // opens in pi: `qcr open <run dir> --lens <case>`.
140
+ ...(opts.transcriptDir !== undefined && opened !== null
141
+ ? { sessionDir: caseSessionDir(opts.transcriptDir, opened.rel) }
142
+ : {}),
143
+ eventsFile: join(tmp, "events.jsonl"),
144
+ stderrFile: join(tmp, "stderr.txt"),
145
+ limits: { timeoutMs: opts.timeoutMs ?? LEAF_TURN_TIMEOUT_MS },
146
+ };
147
+ let transcript = opened?.rel;
148
+ let transcriptAt = opened?.at ?? null;
149
+ const keep = (event) => {
150
+ if (transcriptAt === null)
151
+ return;
152
+ try {
153
+ appendFileSync(transcriptAt, JSON.stringify(event) + "\n");
154
+ }
155
+ catch {
156
+ transcriptAt = null;
157
+ transcript = undefined;
158
+ }
159
+ };
160
+ const readout = caseReadout(opts.cwd, opts.home, onCost);
161
+ const finish = (o) => transcript === undefined ? o : { ...o, transcript };
162
+ try {
163
+ const runtime = await opts.runtime(extras);
164
+ session = await runtime.create(sessionSpec);
165
+ }
166
+ catch (err) {
167
+ const reason = err instanceof Error ? err.message : String(err);
168
+ // A refused login already says what to do; anything else is the host's.
169
+ return {
170
+ ok: false,
171
+ reason: err instanceof AuthError ? reason : `could not start the session: ${reason}`,
172
+ cost_usd: 0,
173
+ };
174
+ }
175
+ session.subscribe((event) => {
176
+ keep(event);
177
+ readout.feed(event);
178
+ });
179
+ let stopped = false;
180
+ let hardStop;
181
+ const gone = new Promise((resolve) => {
182
+ hardStop = resolve;
183
+ });
184
+ const onAbort = () => {
185
+ stopped = true;
186
+ const live = session;
187
+ void live?.abort().catch(() => { });
188
+ // The stop grace, then the session is released; a turn still not
189
+ // settled a grace later is given up on, so the case resolves.
190
+ setTimeout(() => void live?.dispose().catch(() => { }), CASE_STOP_GRACE_MS).unref();
191
+ setTimeout(() => hardStop?.({ ok: false, reason: "stopped", cost_usd: readout.spent() }), 2 * CASE_STOP_GRACE_MS).unref();
192
+ };
193
+ signal.addEventListener("abort", onAbort, { once: true });
194
+ if (signal.aborted) {
195
+ onAbort();
196
+ // Cancelled while the session was being created: no turn starts.
197
+ return finish({ ok: false, reason: "stopped", cost_usd: 0 });
198
+ }
199
+ const turn = (async () => {
200
+ let result;
201
+ try {
202
+ result = await session.prompt(`Task: ${spec.prompt}`);
203
+ }
204
+ catch (err) {
205
+ return {
206
+ ok: false,
207
+ reason: `the session failed: ${err instanceof Error ? err.message : String(err)}`,
208
+ cost_usd: readout.spent(),
209
+ };
210
+ }
211
+ // What a case that ended without an answer had spent: a failure costs
212
+ // money too, and a caller holding a budget must count it.
213
+ const cost_usd = readout.spent();
214
+ if (stopped)
215
+ return { ok: false, reason: "stopped", cost_usd };
216
+ if (result.timedOut)
217
+ return { ok: false, reason: "the case ran past its wall clock", cost_usd };
218
+ // The model's own reason leads: a provider error also ends the turn non-zero.
219
+ if (readout.failure !== null)
220
+ return { ok: false, reason: readout.failure, cost_usd };
221
+ if (result.code !== 0) {
222
+ let tail = "";
223
+ try {
224
+ tail = stderrExcerpt(readFileSync(sessionSpec.stderrFile, "utf8"));
225
+ }
226
+ catch {
227
+ // No stderr to quote.
228
+ }
229
+ return {
230
+ ok: false,
231
+ reason: `the session ended without a result${tail ? `: ${tail}` : ""}`,
232
+ cost_usd,
233
+ };
234
+ }
235
+ if (readout.final.trim() === "")
236
+ return { ok: false, reason: "the subagent gave no answer", cost_usd };
237
+ return { ok: true, output: readout.final, cost_usd, reads: readout.reads };
238
+ })();
239
+ try {
240
+ return finish(await Promise.race([turn, gone]));
241
+ }
242
+ finally {
243
+ signal.removeEventListener("abort", onAbort);
244
+ }
245
+ }
246
+ finally {
247
+ try {
248
+ await session?.dispose();
249
+ }
250
+ catch {
251
+ // Already released.
252
+ }
253
+ rmSync(tmp, { recursive: true, force: true });
254
+ }
255
+ },
256
+ };
257
+ }
258
+ //# sourceMappingURL=scanleaves.js.map