@qvac/harness 0.3.3 → 0.5.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 (301) hide show
  1. package/CHANGELOG.md +700 -126
  2. package/README.md +156 -0
  3. package/dist/harness.bundle +71 -103
  4. package/dist/prebuilds-manifest.json +0 -8
  5. package/dist-lib/index.d.ts +42 -40
  6. package/dist-lib/index.js +307 -117
  7. package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
  8. package/dist-lib/lib/audio-transcode-bare.js +28 -18
  9. package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
  10. package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
  11. package/dist-lib/lib/capabilities/audio-resample.js +30 -18
  12. package/dist-lib/lib/capabilities/completion.d.ts +20 -19
  13. package/dist-lib/lib/capabilities/completion.js +194 -135
  14. package/dist-lib/lib/capabilities/download.d.ts +6 -6
  15. package/dist-lib/lib/capabilities/download.js +15 -8
  16. package/dist-lib/lib/capabilities/embed.js +2 -1
  17. package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
  18. package/dist-lib/lib/capabilities/enhance.js +7 -4
  19. package/dist-lib/lib/capabilities/invoke.js +7 -4
  20. package/dist-lib/lib/capabilities/kv-cache.js +2 -1
  21. package/dist-lib/lib/capabilities/preview-model-source.d.ts +69 -0
  22. package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
  23. package/dist-lib/lib/capabilities/speech-text.js +5 -4
  24. package/dist-lib/lib/capabilities/tool-selection.js +10 -9
  25. package/dist-lib/lib/capabilities/tool-turns.js +30 -16
  26. package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
  27. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  28. package/dist-lib/lib/capabilities/voice.js +22 -12
  29. package/dist-lib/lib/connect.d.ts +15 -37
  30. package/dist-lib/lib/connect.js +161 -112
  31. package/dist-lib/lib/context-window.js +25 -21
  32. package/dist-lib/lib/contract-types.d.ts +26 -20
  33. package/dist-lib/lib/diagram-repair.d.ts +4 -4
  34. package/dist-lib/lib/diagram-repair.js +6 -3
  35. package/dist-lib/lib/docx/index.d.ts +3 -2
  36. package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
  37. package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
  38. package/dist-lib/lib/hardware/capability-probe.js +20 -10
  39. package/dist-lib/lib/hardware/gpu-classify.js +6 -3
  40. package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
  41. package/dist-lib/lib/hardware/gpu-probe.js +4 -2
  42. package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
  43. package/dist-lib/lib/hardware/parsers.js +8 -4
  44. package/dist-lib/lib/helpers/buffer-burst.js +4 -2
  45. package/dist-lib/lib/helpers/control-tokens.js +4 -2
  46. package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
  47. package/dist-lib/lib/helpers/join-guard.js +4 -2
  48. package/dist-lib/lib/helpers/load-stall.js +4 -2
  49. package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
  50. package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
  51. package/dist-lib/lib/helpers/race-abort.js +2 -1
  52. package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
  53. package/dist-lib/lib/helpers/race-timeout.js +14 -0
  54. package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
  55. package/dist-lib/lib/helpers/run-outcome.js +36 -20
  56. package/dist-lib/lib/helpers/spawn-path.js +6 -3
  57. package/dist-lib/lib/helpers/text-coalescer.js +6 -3
  58. package/dist-lib/lib/helpers/thinking.js +38 -45
  59. package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
  60. package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
  61. package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
  62. package/dist-lib/lib/helpers/tool-intent.js +4 -2
  63. package/dist-lib/lib/helpers/tool-label.js +18 -9
  64. package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
  65. package/dist-lib/lib/hooks/built-in/egress.js +2 -1
  66. package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
  67. package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
  68. package/dist-lib/lib/hooks/pipeline.js +6 -3
  69. package/dist-lib/lib/hooks/run.js +12 -8
  70. package/dist-lib/lib/model-download-progress.js +10 -8
  71. package/dist-lib/lib/models/context.d.ts +1 -1
  72. package/dist-lib/lib/models/context.js +5 -1
  73. package/dist-lib/lib/models/custom-model-cache.d.ts +13 -0
  74. package/dist-lib/lib/models/custom-model-cache.js +70 -0
  75. package/dist-lib/lib/models/gguf-header.d.ts +31 -0
  76. package/dist-lib/lib/models/gguf-header.js +230 -0
  77. package/dist-lib/lib/models/idle-unload.d.ts +14 -0
  78. package/dist-lib/lib/models/idle-unload.js +51 -0
  79. package/dist-lib/lib/models/kv-cache.d.ts +3 -2
  80. package/dist-lib/lib/models/kv-cache.js +4 -3
  81. package/dist-lib/lib/models/llm-presets.d.ts +2 -2
  82. package/dist-lib/lib/models/llm-presets.js +4 -2
  83. package/dist-lib/lib/models/model-catalog-constants.js +3 -2
  84. package/dist-lib/lib/models/model-catalog.d.ts +19 -21
  85. package/dist-lib/lib/models/model-catalog.js +66 -33
  86. package/dist-lib/lib/models/model-delete.d.ts +4 -1
  87. package/dist-lib/lib/models/model-delete.js +30 -15
  88. package/dist-lib/lib/models/model-handle.js +2 -1
  89. package/dist-lib/lib/models/model-list.d.ts +14 -3
  90. package/dist-lib/lib/models/model-list.js +33 -6
  91. package/dist-lib/lib/models/model-pool.d.ts +44 -10
  92. package/dist-lib/lib/models/model-pool.js +47 -42
  93. package/dist-lib/lib/models/model-source.js +11 -6
  94. package/dist-lib/lib/models/music-catalog.d.ts +2 -2
  95. package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
  96. package/dist-lib/lib/models/nmt-catalog.js +10 -5
  97. package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
  98. package/dist-lib/lib/models/opencl-cache.js +5 -3
  99. package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
  100. package/dist-lib/lib/models/sdk-faults.js +9 -6
  101. package/dist-lib/lib/models/shards.d.ts +11 -0
  102. package/dist-lib/lib/models/shards.js +15 -0
  103. package/dist-lib/lib/models/tool-dialect.js +10 -5
  104. package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
  105. package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
  106. package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
  107. package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
  108. package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
  109. package/dist-lib/lib/office/index.d.ts +3 -2
  110. package/dist-lib/lib/office/python.d.ts +3 -2
  111. package/dist-lib/lib/office/python.js +2 -1
  112. package/dist-lib/lib/prompt.d.ts +5 -4
  113. package/dist-lib/lib/prompt.js +8 -4
  114. package/dist-lib/lib/queue.js +10 -5
  115. package/dist-lib/lib/resolution-detector.js +21 -11
  116. package/dist-lib/lib/rpc-session.js +2 -1
  117. package/dist-lib/lib/sdk-loader.js +2 -1
  118. package/dist-lib/lib/sdk-storage.js +4 -2
  119. package/dist-lib/lib/serve.js +128 -68
  120. package/dist-lib/lib/spawn.js +7 -4
  121. package/dist-lib/lib/tmp-path.d.ts +7 -6
  122. package/dist-lib/lib/tool-call-display.d.ts +2 -2
  123. package/dist-lib/lib/tools/create-documents.js +18 -10
  124. package/dist-lib/lib/tools/edit-documents.js +23 -13
  125. package/dist-lib/lib/tools/edit-image.d.ts +2 -2
  126. package/dist-lib/lib/tools/edit-image.js +60 -36
  127. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
  128. package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
  129. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
  130. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
  131. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
  132. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
  133. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
  134. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
  135. package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
  136. package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
  137. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
  138. package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
  139. package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
  140. package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
  141. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
  142. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
  143. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
  144. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
  145. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
  146. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
  147. package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
  148. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
  149. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
  150. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
  151. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
  152. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
  153. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
  154. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
  155. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
  156. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
  157. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
  158. package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
  159. package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
  160. package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
  161. package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
  162. package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
  163. package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
  164. package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
  165. package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
  166. package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
  167. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
  168. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
  169. package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
  170. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
  171. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
  172. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
  173. package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
  174. package/dist-lib/lib/tools/exec/runner.js +5 -3
  175. package/dist-lib/lib/tools/exec/scope.js +52 -28
  176. package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
  177. package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
  178. package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
  179. package/dist-lib/lib/tools/exec/video-tools.js +2 -2
  180. package/dist-lib/lib/tools/exec.d.ts +6 -8
  181. package/dist-lib/lib/tools/exec.js +113 -54
  182. package/dist-lib/lib/tools/file-glob.d.ts +4 -5
  183. package/dist-lib/lib/tools/file-glob.js +16 -11
  184. package/dist-lib/lib/tools/fs/glob.js +13 -18
  185. package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
  186. package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
  187. package/dist-lib/lib/tools/fs/remote.js +13 -7
  188. package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
  189. package/dist-lib/lib/tools/fs/scope.js +20 -12
  190. package/dist-lib/lib/tools/fs/walk.js +6 -3
  191. package/dist-lib/lib/tools/fs/write.d.ts +3 -2
  192. package/dist-lib/lib/tools/generate-image.d.ts +11 -9
  193. package/dist-lib/lib/tools/generate-image.js +73 -37
  194. package/dist-lib/lib/tools/generate-music.d.ts +22 -11
  195. package/dist-lib/lib/tools/generate-music.js +79 -48
  196. package/dist-lib/lib/tools/grep.js +66 -41
  197. package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
  198. package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
  199. package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
  200. package/dist-lib/lib/tools/http-request.d.ts +1 -1
  201. package/dist-lib/lib/tools/http-request.js +33 -18
  202. package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
  203. package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
  204. package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
  205. package/dist-lib/lib/tools/mcp-call.js +37 -16
  206. package/dist-lib/lib/tools/operations/builders.js +10 -6
  207. package/dist-lib/lib/tools/operations/compile.js +20 -10
  208. package/dist-lib/lib/tools/operations/decoders.js +12 -6
  209. package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
  210. package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
  211. package/dist-lib/lib/tools/outbound-guard.js +14 -8
  212. package/dist-lib/lib/tools/private-ip.js +8 -7
  213. package/dist-lib/lib/tools/rag-search.js +14 -7
  214. package/dist-lib/lib/tools/read-files.js +23 -20
  215. package/dist-lib/lib/tools/skill.js +19 -10
  216. package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
  217. package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
  218. package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
  219. package/dist-lib/lib/tools/skills/bin-check.js +10 -3
  220. package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
  221. package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
  222. package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
  223. package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
  224. package/dist-lib/lib/tools/skills/catalog.js +33 -28
  225. package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
  226. package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
  227. package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
  228. package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
  229. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
  230. package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
  231. package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
  232. package/dist-lib/lib/tools/skills/credential-check.js +24 -12
  233. package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
  234. package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
  235. package/dist-lib/lib/tools/skills/credential-health.js +5 -3
  236. package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
  237. package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
  238. package/dist-lib/lib/tools/skills/mention.js +7 -6
  239. package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
  240. package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
  241. package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
  242. package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
  243. package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
  244. package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
  245. package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
  246. package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
  247. package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
  248. package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
  249. package/dist-lib/lib/tools/skills/skill-state.js +48 -29
  250. package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
  251. package/dist-lib/lib/tools/toolsets.js +2 -1
  252. package/dist-lib/lib/tools/translate.d.ts +4 -5
  253. package/dist-lib/lib/tools/translate.js +31 -14
  254. package/dist-lib/lib/tools/url-guard.js +2 -1
  255. package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
  256. package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
  257. package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
  258. package/dist-lib/lib/tools/weather-lookup.js +18 -12
  259. package/dist-lib/lib/tools/web-fetch.js +47 -24
  260. package/dist-lib/lib/tools/web-search.js +8 -3
  261. package/dist-lib/lib/validating-lookup.d.ts +4 -4
  262. package/dist-lib/lib/wav-encode.d.ts +1 -1
  263. package/dist-lib/lib/wav-encode.js +11 -10
  264. package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
  265. package/dist-lib/lib/web-fetch/extract.js +4 -2
  266. package/dist-lib/lib/web-fetch/images.js +8 -4
  267. package/dist-lib/lib/web-fetch/video.d.ts +2 -2
  268. package/dist-lib/lib/web-fetch/video.js +8 -4
  269. package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
  270. package/dist-lib/lib/web-search/engines/aol.js +1 -1
  271. package/dist-lib/lib/web-search/engines/bing.js +1 -1
  272. package/dist-lib/lib/web-search/engines/brave.js +4 -1
  273. package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
  274. package/dist-lib/lib/web-search/engines/google.js +7 -3
  275. package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
  276. package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
  277. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
  278. package/dist-lib/lib/web-search/fetch-html.js +13 -7
  279. package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
  280. package/dist-lib/lib/web-search/orchestrator.js +3 -3
  281. package/dist-lib/lib/wire.d.ts +3 -2
  282. package/dist-lib/lib/wire.js +23 -15
  283. package/dist-lib/schema/baseline.js +10 -5
  284. package/dist-lib/schema/build.js +2 -1
  285. package/dist-lib/schema/harness.d.ts +15 -2
  286. package/dist-lib/schema/harness.js +55 -2
  287. package/dist-lib/schema/release-tags.d.ts +3 -2
  288. package/dist-lib/schema/release-tags.js +6 -3
  289. package/dist-lib/spec/bind.js +6 -0
  290. package/dist-lib/spec/calls.js +2 -0
  291. package/dist-lib/spec/hrpc/hrpc.json +26 -0
  292. package/dist-lib/spec/hrpc/index.js +27 -3
  293. package/dist-lib/spec/hrpc/messages.js +207 -8
  294. package/dist-lib/spec/hyperschema/index.js +207 -8
  295. package/dist-lib/spec/hyperschema/schema.json +165 -1
  296. package/dist-lib/spec/hyperschema/types.d.ts +42 -1
  297. package/package.json +13 -14
  298. package/worker/harness-sidecar.mjs +16 -22
  299. package/worker/stow-entry.mjs +7 -4
  300. package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
  301. /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import type { ExecResult } from '../../runner.ts';
2
- export type PyodideLike = {
2
+ export interface PyodideLike {
3
3
  runPython(code: string, options?: {
4
4
  filename?: string;
5
5
  }): unknown;
@@ -42,7 +42,7 @@ export type PyodideLike = {
42
42
  unlink(path: string): void;
43
43
  rmdir(path: string): void;
44
44
  };
45
- };
45
+ }
46
46
  export type LoadPyodideFn = () => Promise<PyodideLike>;
47
47
  export type PythonRunResult = ExecResult & {
48
48
  files?: ReadonlyMap<string, Uint8Array>;
@@ -53,7 +53,7 @@ export type PythonRunResult = ExecResult & {
53
53
  }[];
54
54
  undeclared?: readonly string[];
55
55
  };
56
- export type RunSpec = {
56
+ export interface RunSpec {
57
57
  readonly code: string;
58
58
  readonly packages?: readonly string[] | undefined;
59
59
  readonly inputs?: readonly {
@@ -66,15 +66,16 @@ export type RunSpec = {
66
66
  readonly installTimeoutMs: number;
67
67
  readonly network?: boolean | undefined;
68
68
  readonly hosts?: readonly string[] | undefined;
69
- };
70
- export type RunOutcome = {
69
+ }
70
+ export interface RunOutcome {
71
71
  readonly result: PythonRunResult;
72
72
  readonly evict: boolean;
73
- };
73
+ }
74
74
  export declare function cap(text: string, limit: number): string;
75
75
  export declare function isPyodideFatal(error: unknown): boolean;
76
76
  export declare function repairDoubleEscapedSource(code: string, message: string): string | null;
77
- export declare function performRun(py: PyodideLike, spec: RunSpec, opts?: {
77
+ export interface PerformRunOptions {
78
78
  readonly isCancelled?: () => boolean;
79
79
  readonly askConnect?: ((host: string) => Promise<boolean>) | undefined;
80
- }): Promise<RunOutcome>;
80
+ }
81
+ export declare function performRun(py: PyodideLike, spec: RunSpec, opts?: PerformRunOptions): Promise<RunOutcome>;
@@ -1,3 +1,4 @@
1
+ import { errorMessage } from '../../../../guards.js';
1
2
  import { AbortedError } from '../../../../helpers/race-abort.js';
2
3
  import { hlog } from '../../../../log.js';
3
4
  import { TRUNCATED_OUTPUT_SUFFIX } from '../../runner.js';
@@ -13,8 +14,9 @@ export function cap(text, limit) {
13
14
  export function isPyodideFatal(error) {
14
15
  if (typeof error !== 'object' || error === null)
15
16
  return false;
16
- if ('pyodide_fatal_error' in error && error.pyodide_fatal_error === true)
17
+ if ('pyodide_fatal_error' in error && error.pyodide_fatal_error === true) {
17
18
  return true;
19
+ }
18
20
  return error instanceof Error && error.message.includes('and can no longer be used');
19
21
  }
20
22
  const DOUBLE_ESCAPED = /\\[nt"']/;
@@ -24,8 +26,9 @@ function unescapeSource(code) {
24
26
  return '\n';
25
27
  if (char === 't')
26
28
  return '\t';
27
- if (char === 'r')
29
+ if (char === 'r') {
28
30
  return '\r';
31
+ }
29
32
  return char;
30
33
  });
31
34
  }
@@ -34,8 +37,9 @@ export function repairDoubleEscapedSource(code, message) {
34
37
  return null;
35
38
  if (code.includes('\n'))
36
39
  return null;
37
- if (!DOUBLE_ESCAPED.test(code))
40
+ if (!DOUBLE_ESCAPED.test(code)) {
38
41
  return null;
42
+ }
39
43
  const repaired = unescapeSource(code);
40
44
  return repaired === code ? null : repaired;
41
45
  }
@@ -60,7 +64,7 @@ async function enter(py, code, access) {
60
64
  setConnectPolicy(null);
61
65
  }
62
66
  }
63
- async function attempt(py, code, output, maxOutputChars, access) {
67
+ async function attempt(py, code, { output, maxOutputChars, access }) {
64
68
  try {
65
69
  await enter(py, code, access);
66
70
  return {
@@ -74,7 +78,7 @@ async function attempt(py, code, output, maxOutputChars, access) {
74
78
  };
75
79
  }
76
80
  catch (error) {
77
- const message = error instanceof Error ? error.message : String(error);
81
+ const message = errorMessage(error);
78
82
  return {
79
83
  result: {
80
84
  stdout: cap(output.stdout, maxOutputChars),
@@ -86,16 +90,19 @@ async function attempt(py, code, output, maxOutputChars, access) {
86
90
  };
87
91
  }
88
92
  }
89
- async function runOnce(py, code, output, maxOutputChars, access) {
90
- const first = await attempt(py, code, output, maxOutputChars, access);
91
- if (first.error === null)
93
+ async function runOnce(py, code, opts) {
94
+ const first = await attempt(py, code, opts);
95
+ if (first.error === null) {
92
96
  return first;
97
+ }
93
98
  const repaired = repairDoubleEscapedSource(code, first.error);
94
- if (repaired === null)
99
+ if (repaired === null) {
95
100
  return first;
96
- const retry = await attempt(py, repaired, output, maxOutputChars, access);
97
- if (retry.error !== null && retry.error.includes('SyntaxError'))
101
+ }
102
+ const retry = await attempt(py, repaired, opts);
103
+ if (retry.error !== null && retry.error.includes('SyntaxError')) {
98
104
  return first;
105
+ }
99
106
  return retry;
100
107
  }
101
108
  const FIT_DECKS = `
@@ -168,16 +175,18 @@ class InstallTimeoutError extends Error {
168
175
  }
169
176
  function installSource(py, packages) {
170
177
  const plan = planVendoredInstall(packages);
171
- if (!plan)
178
+ if (!plan) {
172
179
  return `import micropip\nawait micropip.install(${JSON.stringify(packages)})`;
180
+ }
173
181
  const specs = [...stageVendoredWheels(py.FS, plan), ...plan.packages];
174
182
  hlog('python: installing from vendored wheels', { specs });
175
183
  return `import micropip\nawait micropip.install(${JSON.stringify(specs)}, deps=False)`;
176
184
  }
177
- async function installPackages(py, packages, timeoutMs, isCancelled) {
185
+ async function installPackages(py, packages, { timeoutMs, isCancelled }) {
178
186
  await py.loadPackage('micropip');
179
- if (isCancelled())
187
+ if (isCancelled()) {
180
188
  throw new AbortedError();
189
+ }
181
190
  const install = py.runPythonAsync(installSource(py, packages));
182
191
  let timer;
183
192
  const bound = new Promise((_, reject) => {
@@ -187,8 +196,9 @@ async function installPackages(py, packages, timeoutMs, isCancelled) {
187
196
  await Promise.race([install, bound]);
188
197
  }
189
198
  finally {
190
- if (timer)
199
+ if (timer) {
191
200
  clearTimeout(timer);
201
+ }
192
202
  }
193
203
  }
194
204
  const DELIVERABLE = /\.(pptx|docx|xlsx|pdf|png|jpe?g|gif|webp|csv|json|txt|md|zip|mp4|webm|m4a|mp3)$/i;
@@ -204,15 +214,16 @@ function newDeliverables(before, after) {
204
214
  return [...after].filter((name) => !before.has(name) && DELIVERABLE.test(name));
205
215
  }
206
216
  function fitDecks(py, outputs) {
207
- if (!outputs.some((name) => name.toLowerCase().endsWith('.pptx')))
217
+ if (!outputs.some((name) => name.toLowerCase().endsWith('.pptx'))) {
208
218
  return false;
219
+ }
209
220
  try {
210
221
  py.runPython(`${FIT_DECKS}\n_qvac_fit_decks(${JSON.stringify(outputs)})`);
211
222
  return false;
212
223
  }
213
224
  catch (error) {
214
225
  hlog('python: deck fit skipped', {
215
- err: error instanceof Error ? error.message : String(error)
226
+ err: errorMessage(error)
216
227
  });
217
228
  return isPyodideFatal(error);
218
229
  }
@@ -225,20 +236,25 @@ export async function performRun(py, spec, opts = {}) {
225
236
  py.setStdout({ batched: (chunk) => (output.stdout += chunk + '\n') });
226
237
  py.setStderr({ batched: (chunk) => (output.stderr += chunk + '\n') });
227
238
  if (spec.packages?.length) {
228
- if (isCancelled())
239
+ if (isCancelled()) {
229
240
  throw new AbortedError();
241
+ }
230
242
  hlog('python: installing packages', {
231
243
  packages: [...spec.packages],
232
244
  timeoutMs: spec.installTimeoutMs
233
245
  });
234
246
  try {
235
- await installPackages(py, spec.packages, spec.installTimeoutMs, isCancelled);
247
+ await installPackages(py, spec.packages, {
248
+ timeoutMs: spec.installTimeoutMs,
249
+ isCancelled
250
+ });
236
251
  hlog('python: packages ready', { packages: [...spec.packages] });
237
252
  }
238
253
  catch (error) {
239
- if (error instanceof AbortedError)
254
+ if (error instanceof AbortedError) {
240
255
  throw error;
241
- const message = error instanceof Error ? error.message : String(error);
256
+ }
257
+ const message = errorMessage(error);
242
258
  const timedOut = error instanceof InstallTimeoutError;
243
259
  const evict = timedOut || isPyodideFatal(error);
244
260
  hlog(timedOut ? 'python: package install timed out' : 'python: package install failed', {
@@ -254,14 +270,20 @@ export async function performRun(py, spec, opts = {}) {
254
270
  output.stdout = '';
255
271
  output.stderr = '';
256
272
  }
257
- if (isCancelled())
273
+ if (isCancelled()) {
258
274
  throw new AbortedError();
275
+ }
259
276
  if (!spec.inputs?.length && !spec.outputs?.length && !spec.workdir) {
260
277
  const cwd = py.FS.cwd();
261
278
  const before = listFiles(py, cwd);
262
- const { result, fatal } = await runOnce(py, spec.code, output, spec.maxOutputChars, access);
263
- if (result.exitCode !== 0)
279
+ const { result, fatal } = await runOnce(py, spec.code, {
280
+ output,
281
+ maxOutputChars: spec.maxOutputChars,
282
+ access
283
+ });
284
+ if (result.exitCode !== 0) {
264
285
  return { result, evict: fatal };
286
+ }
265
287
  const undeclared = newDeliverables(before, listFiles(py, cwd));
266
288
  return { result: undeclared.length > 0 ? { ...result, undeclared } : result, evict: false };
267
289
  }
@@ -270,12 +292,18 @@ export async function performRun(py, spec, opts = {}) {
270
292
  py.FS.chdir(scratch.cwd);
271
293
  stageInputFiles(py.FS, scratch.cwd, spec.inputs ?? []);
272
294
  const before = listFiles(py, scratch.cwd);
273
- const { result, fatal } = await runOnce(py, spec.code, output, spec.maxOutputChars, access);
295
+ const { result, fatal } = await runOnce(py, spec.code, {
296
+ output,
297
+ maxOutputChars: spec.maxOutputChars,
298
+ access
299
+ });
274
300
  let evict = fatal;
275
- if (result.exitCode === 0)
301
+ if (result.exitCode === 0) {
276
302
  evict = fitDecks(py, spec.outputs ?? []);
277
- if (evict)
303
+ }
304
+ if (evict) {
278
305
  return { result: { ...result, missing: spec.outputs ?? [] }, evict };
306
+ }
279
307
  const outputs = collectOutputs(py.FS, scratch.cwd, spec.outputs ?? []);
280
308
  const declared = new Set(spec.outputs ?? []);
281
309
  const undeclared = result.exitCode === 0
@@ -287,7 +315,7 @@ export async function performRun(py, spec, opts = {}) {
287
315
  files: outputs.files,
288
316
  missing: outputs.missing,
289
317
  rejected: outputs.rejected,
290
- ...(undeclared.length > 0 && { undeclared })
318
+ undeclared: undeclared.length > 0 ? undeclared : undefined
291
319
  },
292
320
  evict: false
293
321
  };
@@ -17,28 +17,26 @@ declare const ManifestSchema: z.ZodObject<{
17
17
  }, z.core.$strip>>;
18
18
  }, z.core.$strip>;
19
19
  export type WheelManifest = z.infer<typeof ManifestSchema>;
20
- export type VendoredInstall = {
20
+ export interface VendoredInstall {
21
21
  readonly wheels: readonly string[];
22
22
  readonly packages: readonly string[];
23
- };
24
- type WheelFs = {
23
+ }
24
+ interface WheelFs {
25
25
  mkdirTree(path: string): void;
26
26
  writeFile(path: string, data: Uint8Array): void;
27
27
  analyzePath(path: string): {
28
28
  exists: boolean;
29
29
  };
30
- };
30
+ }
31
31
  export declare function wheelsDir(): string | undefined;
32
32
  export declare function vendoredManifest(dir?: string | undefined): WheelManifest | null;
33
33
  export declare function normalizeDistribution(name: string): string;
34
- export declare function vendoredSpec(distribution: string, opts?: {
34
+ export interface VendoredOptions {
35
35
  readonly dir?: string | undefined;
36
36
  readonly manifest?: WheelManifest | null;
37
- }): string | null;
38
- export declare function planVendoredInstall(specs: readonly string[], opts?: {
39
- readonly dir?: string | undefined;
40
- readonly manifest?: WheelManifest | null;
41
- }): VendoredInstall | null;
37
+ }
38
+ export declare function vendoredSpec(distribution: string, opts?: VendoredOptions): string | null;
39
+ export declare function planVendoredInstall(specs: readonly string[], opts?: VendoredOptions): VendoredInstall | null;
42
40
  export declare function stageVendoredWheels(target: WheelFs, install: VendoredInstall): string[];
43
41
  export declare function seedPackageCache(cacheDir: string, dir?: string | undefined): void;
44
42
  export {};
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import process from 'process';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { z } from 'zod';
5
+ import { errorMessage } from '../../../../guards.js';
5
6
  import { hlog } from '../../../../log.js';
6
7
  const ManifestSchema = z.object({
7
8
  pyodide: z.string(),
@@ -16,12 +17,14 @@ const ManifestSchema = z.object({
16
17
  const STAGE_DIR = '/qvac-wheels';
17
18
  export function wheelsDir() {
18
19
  const fromHost = process.env.QVAC_PYTHON_WHEELS_DIR;
19
- if (fromHost)
20
+ if (fromHost) {
20
21
  return fromHost;
22
+ }
21
23
  const root = new URL('../../../../../', import.meta.url);
22
24
  const source = new URL(root.pathname.endsWith('/dist-lib/') ? '../wheels' : 'wheels', root);
23
- if (source.href.includes('.bundle/'))
25
+ if (source.href.includes('.bundle/')) {
24
26
  return undefined;
27
+ }
25
28
  return fileURLToPath(source.href);
26
29
  }
27
30
  export function vendoredManifest(dir = wheelsDir()) {
@@ -38,7 +41,7 @@ export function vendoredManifest(dir = wheelsDir()) {
38
41
  catch (error) {
39
42
  hlog('python: vendored wheel manifest unusable', {
40
43
  file,
41
- err: error instanceof Error ? error.message : String(error)
44
+ err: errorMessage(error)
42
45
  });
43
46
  return null;
44
47
  }
@@ -58,23 +61,26 @@ export function vendoredSpec(distribution, opts = {}) {
58
61
  const dir = opts.dir ?? wheelsDir();
59
62
  const manifest = opts.manifest === undefined ? vendoredManifest(dir) : opts.manifest;
60
63
  const entry = manifest?.packages[name];
61
- if (!entry)
64
+ if (!entry) {
62
65
  return null;
66
+ }
63
67
  const spec = entry.version ? `${name}==${entry.version}` : name;
64
68
  return planVendoredInstall([spec], { dir, manifest }) ? spec : null;
65
69
  }
66
70
  export function planVendoredInstall(specs, opts = {}) {
67
71
  const dir = opts.dir ?? wheelsDir();
68
72
  const manifest = opts.manifest === undefined ? vendoredManifest(dir) : opts.manifest;
69
- if (!manifest || !dir || specs.length === 0)
73
+ if (!manifest || !dir || specs.length === 0) {
70
74
  return null;
75
+ }
71
76
  const wheels = new Set();
72
77
  const packages = new Set();
73
78
  for (const spec of specs) {
74
79
  const { name, version } = parseSpec(spec);
75
80
  const entry = manifest.packages[name];
76
- if (!entry || entry.version !== version)
81
+ if (!entry || entry.version !== version) {
77
82
  return null;
83
+ }
78
84
  for (const wheel of entry.pypi) {
79
85
  if (!fs.existsSync(`${dir}/${wheel}`)) {
80
86
  hlog('python: vendored wheel missing, installing from PyPI instead', { wheel });
@@ -82,14 +88,16 @@ export function planVendoredInstall(specs, opts = {}) {
82
88
  }
83
89
  wheels.add(`${dir}/${wheel}`);
84
90
  }
85
- for (const dependency of entry.pyodide)
91
+ for (const dependency of entry.pyodide) {
86
92
  packages.add(dependency);
93
+ }
87
94
  }
88
95
  return { wheels: [...wheels], packages: [...packages] };
89
96
  }
90
97
  export function stageVendoredWheels(target, install) {
91
- if (install.wheels.length > 0)
98
+ if (install.wheels.length > 0) {
92
99
  target.mkdirTree(STAGE_DIR);
100
+ }
93
101
  return install.wheels.map((wheel) => {
94
102
  const staged = `${STAGE_DIR}/${wheel.slice(wheel.lastIndexOf('/') + 1)}`;
95
103
  if (!target.analyzePath(staged).exists) {
@@ -100,18 +108,21 @@ export function stageVendoredWheels(target, install) {
100
108
  }
101
109
  export function seedPackageCache(cacheDir, dir = wheelsDir()) {
102
110
  const manifest = vendoredManifest(dir);
103
- if (!manifest || !dir)
111
+ if (!manifest || !dir) {
104
112
  return;
113
+ }
105
114
  fs.mkdirSync(cacheDir, { recursive: true });
106
115
  let seeded = 0;
107
116
  for (const [file, entry] of Object.entries(manifest.files)) {
108
- if (entry.channel !== 'pyodide' || fs.existsSync(`${cacheDir}/${file}`))
117
+ if (entry.channel !== 'pyodide' || fs.existsSync(`${cacheDir}/${file}`)) {
109
118
  continue;
119
+ }
110
120
  if (!fs.existsSync(`${dir}/${file}`))
111
121
  continue;
112
122
  fs.copyFileSync(`${dir}/${file}`, `${cacheDir}/${file}`);
113
123
  seeded++;
114
124
  }
115
- if (seeded > 0)
125
+ if (seeded > 0) {
116
126
  hlog('python: seeded package cache', { cacheDir, wheels: seeded });
127
+ }
117
128
  }
@@ -1,8 +1,9 @@
1
1
  import { type AbortSignal as BareAbortSignal } from 'bare-abort-controller';
2
2
  import type { PythonRunResult, RunSpec } from '../run-session.ts';
3
- export declare function runPythonInWorker(spec: RunSpec, opts: {
3
+ export interface RunSpecOptions {
4
4
  readonly timeoutMs: number;
5
5
  readonly signal?: BareAbortSignal | undefined;
6
6
  readonly askConnect?: ((host: string) => Promise<boolean>) | undefined;
7
- }): Promise<PythonRunResult>;
7
+ }
8
+ export declare function runPythonInWorker(spec: RunSpec, opts: RunSpecOptions): Promise<PythonRunResult>;
8
9
  export declare function closePythonWorker(): Promise<void>;
@@ -20,17 +20,20 @@ function workerEntry() {
20
20
  async function readMessage(link) {
21
21
  for (;;) {
22
22
  const message = asWorkerMessage(await link.read());
23
- if (message?.type !== 'log')
23
+ if (message?.type !== 'log') {
24
24
  return message;
25
+ }
25
26
  hlog(`python worker: ${message.message}`);
26
27
  }
27
28
  }
28
29
  async function awaitReady(link) {
29
30
  const message = await readMessage(link);
30
- if (message?.type === 'ready')
31
+ if (message?.type === 'ready') {
31
32
  return;
32
- if (message?.type === 'boot-error')
33
+ }
34
+ if (message?.type === 'boot-error') {
33
35
  throw new Error(`python worker: ${message.message}`);
36
+ }
34
37
  throw new Error('python worker sent an unexpected first message');
35
38
  }
36
39
  function boot() {
@@ -44,28 +47,33 @@ function boot() {
44
47
  nextId: 1
45
48
  };
46
49
  worker.ready.catch(() => {
47
- if (active === worker)
50
+ if (active === worker) {
48
51
  active = null;
52
+ }
49
53
  link.kill();
50
54
  });
51
55
  return worker;
52
56
  }
53
57
  function destroy(worker) {
54
- if (active === worker)
58
+ if (active === worker) {
55
59
  active = null;
60
+ }
56
61
  worker.link.kill();
57
62
  }
58
63
  async function drainAndReap(worker) {
59
- if (active === worker)
64
+ if (active === worker) {
60
65
  active = null;
66
+ }
61
67
  const closed = readMessage(worker.link).then(() => 'message', () => 'closed');
62
68
  const timer = grace(SHUTDOWN_GRACE_MS);
63
69
  try {
64
70
  const settled = await Promise.race([closed, timer.expired]);
65
- if (settled === 'closed')
71
+ if (settled === 'closed') {
66
72
  worker.link.reap();
67
- else
73
+ }
74
+ else {
68
75
  worker.link.kill();
76
+ }
69
77
  }
70
78
  finally {
71
79
  timer.clear();
@@ -79,12 +87,14 @@ async function resultFor(worker, id, askConnect) {
79
87
  message = await readMessage(worker.link);
80
88
  }
81
89
  if (message?.type === 'result') {
82
- if (message.id !== id)
90
+ if (message.id !== id) {
83
91
  throw new Error('python worker answered a different run');
92
+ }
84
93
  return { result: fromWireResult(message.result), evict: message.evict };
85
94
  }
86
- if (message?.type === 'boot-error')
95
+ if (message?.type === 'boot-error') {
87
96
  throw new Error(`python worker: ${message.message}`);
97
+ }
88
98
  throw new Error('python worker sent an unexpected message');
89
99
  }
90
100
  function grace(ms) {
@@ -129,8 +139,9 @@ export async function runPythonInWorker(spec, opts) {
129
139
  const ask = opts.askConnect;
130
140
  const outcome = (async () => {
131
141
  await worker.ready;
132
- if (cancelled)
142
+ if (cancelled) {
133
143
  throw new AbortedError();
144
+ }
134
145
  sent = true;
135
146
  await worker.link.write({ type: 'run', id, spec });
136
147
  return resultFor(worker, id, ask && ((host) => raceAbort(ask(host), stop.signal)));
@@ -139,38 +150,40 @@ export async function runPythonInWorker(spec, opts) {
139
150
  const timeout = new Promise((resolve) => {
140
151
  timer = setTimeout(() => resolve('timeout'), opts.timeoutMs);
141
152
  });
153
+ const cancel = async () => {
154
+ cancelled = true;
155
+ stop.abort(undefined);
156
+ if (sent) {
157
+ await interrupt(worker, outcome);
158
+ }
159
+ else {
160
+ outcome.catch(noop);
161
+ }
162
+ };
142
163
  let first;
143
164
  try {
144
165
  first = await raceAbort(Promise.race([outcome, timeout]), opts.signal);
145
166
  }
146
167
  catch (error) {
147
168
  if (error instanceof AbortedError) {
148
- cancelled = true;
149
- stop.abort(undefined);
150
- if (sent)
151
- await interrupt(worker, outcome);
152
- else
153
- outcome.catch(() => { });
169
+ await cancel();
154
170
  throw error;
155
171
  }
156
172
  destroy(worker);
157
173
  throw error;
158
174
  }
159
175
  finally {
160
- if (timer)
176
+ if (timer) {
161
177
  clearTimeout(timer);
178
+ }
162
179
  }
163
180
  if (first === 'timeout') {
164
- cancelled = true;
165
- stop.abort(undefined);
166
- if (sent)
167
- await interrupt(worker, outcome);
168
- else
169
- outcome.catch(() => { });
181
+ await cancel();
170
182
  throw new Error(`Command timed out after ${opts.timeoutMs}ms`);
171
183
  }
172
- if (first.evict)
184
+ if (first.evict) {
173
185
  await drainAndReap(worker);
186
+ }
174
187
  return first.result;
175
188
  }
176
189
  finally {
@@ -179,8 +192,9 @@ export async function runPythonInWorker(spec, opts) {
179
192
  }
180
193
  export async function closePythonWorker() {
181
194
  const worker = active;
182
- if (!worker)
195
+ if (!worker) {
183
196
  return;
197
+ }
184
198
  active = null;
185
199
  const timer = grace(SHUTDOWN_GRACE_MS);
186
200
  try {
@@ -199,3 +213,4 @@ export async function closePythonWorker() {
199
213
  timer.clear();
200
214
  }
201
215
  }
216
+ function noop() { }