@qvac/harness 0.4.0 → 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 (299) hide show
  1. package/CHANGELOG.md +689 -141
  2. package/README.md +29 -7
  3. package/dist/harness.bundle +71 -50
  4. package/dist-lib/index.d.ts +36 -39
  5. package/dist-lib/index.js +279 -114
  6. package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
  7. package/dist-lib/lib/audio-transcode-bare.js +28 -18
  8. package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
  10. package/dist-lib/lib/capabilities/audio-resample.js +30 -18
  11. package/dist-lib/lib/capabilities/completion.d.ts +20 -19
  12. package/dist-lib/lib/capabilities/completion.js +193 -132
  13. package/dist-lib/lib/capabilities/download.d.ts +6 -6
  14. package/dist-lib/lib/capabilities/download.js +15 -8
  15. package/dist-lib/lib/capabilities/embed.js +2 -1
  16. package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
  17. package/dist-lib/lib/capabilities/enhance.js +7 -4
  18. package/dist-lib/lib/capabilities/invoke.js +7 -4
  19. package/dist-lib/lib/capabilities/kv-cache.js +2 -1
  20. package/dist-lib/lib/capabilities/preview-model-source.d.ts +9 -7
  21. package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
  22. package/dist-lib/lib/capabilities/speech-text.js +5 -4
  23. package/dist-lib/lib/capabilities/tool-selection.js +10 -9
  24. package/dist-lib/lib/capabilities/tool-turns.js +30 -16
  25. package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
  26. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  27. package/dist-lib/lib/capabilities/voice.js +22 -12
  28. package/dist-lib/lib/connect.d.ts +11 -32
  29. package/dist-lib/lib/connect.js +152 -112
  30. package/dist-lib/lib/context-window.js +25 -21
  31. package/dist-lib/lib/contract-types.d.ts +22 -15
  32. package/dist-lib/lib/diagram-repair.d.ts +4 -4
  33. package/dist-lib/lib/diagram-repair.js +6 -3
  34. package/dist-lib/lib/docx/index.d.ts +3 -2
  35. package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
  36. package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
  37. package/dist-lib/lib/hardware/capability-probe.js +20 -10
  38. package/dist-lib/lib/hardware/gpu-classify.js +6 -3
  39. package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
  40. package/dist-lib/lib/hardware/gpu-probe.js +4 -2
  41. package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
  42. package/dist-lib/lib/hardware/parsers.js +8 -4
  43. package/dist-lib/lib/helpers/buffer-burst.js +4 -2
  44. package/dist-lib/lib/helpers/control-tokens.js +4 -2
  45. package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
  46. package/dist-lib/lib/helpers/join-guard.js +4 -2
  47. package/dist-lib/lib/helpers/load-stall.js +4 -2
  48. package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
  49. package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
  50. package/dist-lib/lib/helpers/race-abort.js +2 -1
  51. package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
  52. package/dist-lib/lib/helpers/race-timeout.js +14 -0
  53. package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
  54. package/dist-lib/lib/helpers/run-outcome.js +36 -20
  55. package/dist-lib/lib/helpers/spawn-path.js +6 -3
  56. package/dist-lib/lib/helpers/text-coalescer.js +6 -3
  57. package/dist-lib/lib/helpers/thinking.js +38 -45
  58. package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
  59. package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
  60. package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
  61. package/dist-lib/lib/helpers/tool-intent.js +4 -2
  62. package/dist-lib/lib/helpers/tool-label.js +18 -9
  63. package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
  64. package/dist-lib/lib/hooks/built-in/egress.js +2 -1
  65. package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
  66. package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
  67. package/dist-lib/lib/hooks/pipeline.js +6 -3
  68. package/dist-lib/lib/hooks/run.js +12 -8
  69. package/dist-lib/lib/model-download-progress.js +10 -8
  70. package/dist-lib/lib/models/context.d.ts +1 -1
  71. package/dist-lib/lib/models/context.js +5 -1
  72. package/dist-lib/lib/models/custom-model-cache.d.ts +10 -1
  73. package/dist-lib/lib/models/custom-model-cache.js +43 -15
  74. package/dist-lib/lib/models/gguf-header.d.ts +3 -2
  75. package/dist-lib/lib/models/gguf-header.js +44 -22
  76. package/dist-lib/lib/models/idle-unload.d.ts +14 -0
  77. package/dist-lib/lib/models/idle-unload.js +51 -0
  78. package/dist-lib/lib/models/kv-cache.d.ts +3 -2
  79. package/dist-lib/lib/models/kv-cache.js +4 -3
  80. package/dist-lib/lib/models/llm-presets.d.ts +2 -2
  81. package/dist-lib/lib/models/llm-presets.js +4 -2
  82. package/dist-lib/lib/models/model-catalog-constants.js +3 -2
  83. package/dist-lib/lib/models/model-catalog.d.ts +13 -20
  84. package/dist-lib/lib/models/model-catalog.js +59 -31
  85. package/dist-lib/lib/models/model-delete.js +18 -8
  86. package/dist-lib/lib/models/model-handle.js +2 -1
  87. package/dist-lib/lib/models/model-list.d.ts +8 -3
  88. package/dist-lib/lib/models/model-list.js +11 -6
  89. package/dist-lib/lib/models/model-pool.d.ts +44 -10
  90. package/dist-lib/lib/models/model-pool.js +47 -42
  91. package/dist-lib/lib/models/model-source.js +10 -5
  92. package/dist-lib/lib/models/music-catalog.d.ts +2 -2
  93. package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
  94. package/dist-lib/lib/models/nmt-catalog.js +10 -5
  95. package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
  96. package/dist-lib/lib/models/opencl-cache.js +5 -3
  97. package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
  98. package/dist-lib/lib/models/sdk-faults.js +9 -6
  99. package/dist-lib/lib/models/shards.d.ts +11 -0
  100. package/dist-lib/lib/models/shards.js +15 -0
  101. package/dist-lib/lib/models/tool-dialect.js +10 -5
  102. package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
  103. package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
  104. package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
  105. package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
  106. package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
  107. package/dist-lib/lib/office/index.d.ts +3 -2
  108. package/dist-lib/lib/office/python.d.ts +3 -2
  109. package/dist-lib/lib/office/python.js +2 -1
  110. package/dist-lib/lib/prompt.d.ts +5 -4
  111. package/dist-lib/lib/prompt.js +8 -4
  112. package/dist-lib/lib/queue.js +10 -5
  113. package/dist-lib/lib/resolution-detector.js +21 -11
  114. package/dist-lib/lib/rpc-session.js +2 -1
  115. package/dist-lib/lib/sdk-loader.js +2 -1
  116. package/dist-lib/lib/sdk-storage.js +4 -2
  117. package/dist-lib/lib/serve.js +110 -66
  118. package/dist-lib/lib/spawn.js +7 -4
  119. package/dist-lib/lib/tmp-path.d.ts +7 -6
  120. package/dist-lib/lib/tool-call-display.d.ts +2 -2
  121. package/dist-lib/lib/tools/create-documents.js +18 -10
  122. package/dist-lib/lib/tools/edit-documents.js +23 -13
  123. package/dist-lib/lib/tools/edit-image.d.ts +2 -2
  124. package/dist-lib/lib/tools/edit-image.js +60 -36
  125. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
  126. package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
  127. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
  128. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
  129. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
  130. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
  131. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
  132. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
  133. package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
  134. package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
  135. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
  136. package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
  137. package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
  138. package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
  139. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
  140. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
  141. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
  142. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
  143. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
  144. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
  145. package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
  146. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
  147. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
  148. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
  149. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
  150. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
  151. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
  152. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
  153. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
  154. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
  155. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
  156. package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
  157. package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
  158. package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
  159. package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
  160. package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
  161. package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
  162. package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
  163. package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
  164. package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
  165. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
  166. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
  167. package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
  168. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
  169. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
  170. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
  171. package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
  172. package/dist-lib/lib/tools/exec/runner.js +5 -3
  173. package/dist-lib/lib/tools/exec/scope.js +52 -28
  174. package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
  175. package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
  176. package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
  177. package/dist-lib/lib/tools/exec/video-tools.js +2 -2
  178. package/dist-lib/lib/tools/exec.d.ts +6 -8
  179. package/dist-lib/lib/tools/exec.js +113 -54
  180. package/dist-lib/lib/tools/file-glob.d.ts +4 -5
  181. package/dist-lib/lib/tools/file-glob.js +16 -11
  182. package/dist-lib/lib/tools/fs/glob.js +13 -18
  183. package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
  184. package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
  185. package/dist-lib/lib/tools/fs/remote.js +13 -7
  186. package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
  187. package/dist-lib/lib/tools/fs/scope.js +20 -12
  188. package/dist-lib/lib/tools/fs/walk.js +6 -3
  189. package/dist-lib/lib/tools/fs/write.d.ts +3 -2
  190. package/dist-lib/lib/tools/generate-image.d.ts +11 -9
  191. package/dist-lib/lib/tools/generate-image.js +73 -37
  192. package/dist-lib/lib/tools/generate-music.d.ts +22 -11
  193. package/dist-lib/lib/tools/generate-music.js +79 -48
  194. package/dist-lib/lib/tools/grep.js +66 -41
  195. package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
  196. package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
  197. package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
  198. package/dist-lib/lib/tools/http-request.d.ts +1 -1
  199. package/dist-lib/lib/tools/http-request.js +33 -18
  200. package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
  201. package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
  202. package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
  203. package/dist-lib/lib/tools/mcp-call.js +37 -16
  204. package/dist-lib/lib/tools/operations/builders.js +10 -6
  205. package/dist-lib/lib/tools/operations/compile.js +20 -10
  206. package/dist-lib/lib/tools/operations/decoders.js +12 -6
  207. package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
  208. package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
  209. package/dist-lib/lib/tools/outbound-guard.js +14 -8
  210. package/dist-lib/lib/tools/private-ip.js +8 -7
  211. package/dist-lib/lib/tools/rag-search.js +14 -7
  212. package/dist-lib/lib/tools/read-files.js +23 -20
  213. package/dist-lib/lib/tools/skill.js +19 -10
  214. package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
  215. package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
  216. package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
  217. package/dist-lib/lib/tools/skills/bin-check.js +10 -3
  218. package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
  219. package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
  220. package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
  221. package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
  222. package/dist-lib/lib/tools/skills/catalog.js +33 -28
  223. package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
  224. package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
  225. package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
  226. package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
  227. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
  228. package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
  229. package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
  230. package/dist-lib/lib/tools/skills/credential-check.js +24 -12
  231. package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
  232. package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
  233. package/dist-lib/lib/tools/skills/credential-health.js +5 -3
  234. package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
  235. package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
  236. package/dist-lib/lib/tools/skills/mention.js +7 -6
  237. package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
  238. package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
  239. package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
  240. package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
  241. package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
  242. package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
  243. package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
  244. package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
  245. package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
  246. package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
  247. package/dist-lib/lib/tools/skills/skill-state.js +48 -29
  248. package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
  249. package/dist-lib/lib/tools/toolsets.js +2 -1
  250. package/dist-lib/lib/tools/translate.d.ts +4 -5
  251. package/dist-lib/lib/tools/translate.js +31 -14
  252. package/dist-lib/lib/tools/url-guard.js +2 -1
  253. package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
  254. package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
  255. package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
  256. package/dist-lib/lib/tools/weather-lookup.js +18 -12
  257. package/dist-lib/lib/tools/web-fetch.js +47 -24
  258. package/dist-lib/lib/tools/web-search.js +8 -3
  259. package/dist-lib/lib/validating-lookup.d.ts +4 -4
  260. package/dist-lib/lib/wav-encode.d.ts +1 -1
  261. package/dist-lib/lib/wav-encode.js +11 -10
  262. package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
  263. package/dist-lib/lib/web-fetch/extract.js +4 -2
  264. package/dist-lib/lib/web-fetch/images.js +8 -4
  265. package/dist-lib/lib/web-fetch/video.d.ts +2 -2
  266. package/dist-lib/lib/web-fetch/video.js +8 -4
  267. package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
  268. package/dist-lib/lib/web-search/engines/aol.js +1 -1
  269. package/dist-lib/lib/web-search/engines/bing.js +1 -1
  270. package/dist-lib/lib/web-search/engines/brave.js +4 -1
  271. package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
  272. package/dist-lib/lib/web-search/engines/google.js +7 -3
  273. package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
  274. package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
  275. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
  276. package/dist-lib/lib/web-search/fetch-html.js +13 -7
  277. package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
  278. package/dist-lib/lib/web-search/orchestrator.js +3 -3
  279. package/dist-lib/lib/wire.d.ts +3 -2
  280. package/dist-lib/lib/wire.js +23 -15
  281. package/dist-lib/schema/baseline.js +10 -5
  282. package/dist-lib/schema/build.js +2 -1
  283. package/dist-lib/schema/harness.d.ts +15 -2
  284. package/dist-lib/schema/harness.js +14 -2
  285. package/dist-lib/schema/release-tags.d.ts +3 -2
  286. package/dist-lib/schema/release-tags.js +6 -3
  287. package/dist-lib/spec/bind.js +3 -0
  288. package/dist-lib/spec/calls.js +1 -0
  289. package/dist-lib/spec/hrpc/hrpc.json +13 -0
  290. package/dist-lib/spec/hrpc/index.js +15 -3
  291. package/dist-lib/spec/hrpc/messages.js +39 -8
  292. package/dist-lib/spec/hyperschema/index.js +39 -8
  293. package/dist-lib/spec/hyperschema/schema.json +30 -1
  294. package/dist-lib/spec/hyperschema/types.d.ts +9 -1
  295. package/package.json +13 -14
  296. package/worker/harness-sidecar.mjs +15 -22
  297. package/worker/stow-entry.mjs +7 -4
  298. package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
  299. /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
@@ -3,16 +3,18 @@ import { transferProgress } from '../model-download-progress.js';
3
3
  import ResourcePuddle from 'resource-puddle';
4
4
  import { ModelHandle } from './model-handle.js';
5
5
  import { defaultContextSize } from './context.js';
6
+ import { customContextLength } from './model-catalog.js';
6
7
  import { adrenoSafeKvCache } from './kv-cache.js';
7
8
  import { DEFAULT_EMBED_BATCH_SIZE } from './embed-config.js';
8
9
  import { hlog } from '../log.js';
9
10
  import { RUNAWAY_STOP_SEQUENCES } from '../helpers/control-tokens.js';
11
+ import { raceTimeout } from '../helpers/race-timeout.js';
10
12
  import { resolveModelSrc } from './model-source.js';
11
13
  export function createLoadTracker() {
12
14
  const inflight = new Set();
13
15
  let draining = null;
14
16
  const cancel = (sdk, requestId) => {
15
- void sdk.cancel({ requestId }).catch(() => { });
17
+ void sdk.cancel({ requestId }).catch(noop);
16
18
  };
17
19
  return {
18
20
  get draining() {
@@ -20,8 +22,9 @@ export function createLoadTracker() {
20
22
  },
21
23
  track(pending) {
22
24
  const { requestId } = pending;
23
- if (!requestId)
25
+ if (!requestId) {
24
26
  return pending;
27
+ }
25
28
  if (draining) {
26
29
  cancel(draining, requestId);
27
30
  return pending;
@@ -33,8 +36,9 @@ export function createLoadTracker() {
33
36
  },
34
37
  drain(sdk) {
35
38
  draining = sdk;
36
- for (const requestId of inflight)
39
+ for (const requestId of inflight) {
37
40
  cancel(sdk, requestId);
41
+ }
38
42
  inflight.clear();
39
43
  },
40
44
  undrain() {
@@ -60,6 +64,8 @@ export function modelKey(model, opts = {}) {
60
64
  opts.projectionModel ?? ''
61
65
  ].join('\0');
62
66
  }
67
+ class ModelPuddle extends ResourcePuddle {
68
+ }
63
69
  function entryOf(pool, key) {
64
70
  return pool.resources.get(key);
65
71
  }
@@ -79,8 +85,9 @@ export function releaseModel(pool, key) {
79
85
  void pending.then(() => releaseSettled(pool, key), () => releaseSettled(pool, key));
80
86
  }
81
87
  function releaseSettled(pool, key) {
82
- if (!pool.destroyed)
88
+ if (!pool.destroyed) {
83
89
  pool.release(key);
90
+ }
84
91
  }
85
92
  export function loadedModels(pool) {
86
93
  const out = [];
@@ -95,8 +102,9 @@ export async function leaseResidentModel(pool, wanted = () => true) {
95
102
  for (const entry of pool.resources.values()) {
96
103
  const key = entry.id;
97
104
  const pending = entry.value.peek();
98
- if (!pending || entry.value.loading || !wanted(key.split('\0')[0]))
105
+ if (!pending || entry.value.loading || !wanted(key.split('\0')[0])) {
99
106
  continue;
107
+ }
100
108
  pool.open(key);
101
109
  const loaded = await pending;
102
110
  return { model: loaded.model, modelId: loaded.modelId, release: () => releaseModel(pool, key) };
@@ -115,7 +123,7 @@ export async function evictIdle(pool) {
115
123
  await pool.gc();
116
124
  }
117
125
  }
118
- export async function awaitModelCapacity(pool, cap, deadlineMs, tickMs = 250, signal) {
126
+ export async function awaitModelCapacity(pool, { cap, deadlineMs, tickMs = 250, signal }) {
119
127
  const roomed = () => loadedModels(pool).length < cap;
120
128
  let done = false;
121
129
  let timer = null;
@@ -131,8 +139,9 @@ export async function awaitModelCapacity(pool, cap, deadlineMs, tickMs = 250, si
131
139
  if (done || signal?.aborted)
132
140
  return false;
133
141
  await new Promise((resolve) => setTimeout(resolve, tickMs));
134
- if (done || signal?.aborted)
142
+ if (done || signal?.aborted) {
135
143
  return false;
144
+ }
136
145
  await evictIdle(pool);
137
146
  }
138
147
  return true;
@@ -142,31 +151,23 @@ export async function awaitModelCapacity(pool, cap, deadlineMs, tickMs = 250, si
142
151
  }
143
152
  finally {
144
153
  done = true;
145
- if (timer !== null)
154
+ if (timer !== null) {
146
155
  clearTimeout(timer);
156
+ }
147
157
  }
148
158
  }
149
- export async function evictModelWithin(pool, key, deadlineMs, tickMs = 250) {
150
- let timer = null;
151
- const expired = new Promise((resolve) => {
152
- timer = setTimeout(() => resolve(false), deadlineMs);
153
- });
159
+ export async function evictModelWithin(pool, key, { deadlineMs, tickMs = 250 }) {
154
160
  const evicting = (async () => {
155
161
  while (isResident(pool, key)) {
156
162
  await evictIdle(pool);
157
- if (!isResident(pool, key))
163
+ if (!isResident(pool, key)) {
158
164
  break;
165
+ }
159
166
  await new Promise((resolve) => setTimeout(resolve, tickMs));
160
167
  }
161
168
  return true;
162
169
  })();
163
- try {
164
- return await Promise.race([evicting, expired]);
165
- }
166
- finally {
167
- if (timer !== null)
168
- clearTimeout(timer);
169
- }
170
+ return raceTimeout(evicting, { ms: deadlineMs, fallback: false });
170
171
  }
171
172
  function llmVerbosity() {
172
173
  const raw = Number(process.env.QVAC_LLM_VERBOSITY ?? 0);
@@ -174,8 +175,9 @@ function llmVerbosity() {
174
175
  }
175
176
  export function progressState(model, progress) {
176
177
  const transfer = transferProgress(progress);
177
- if (!transfer)
178
+ if (!transfer) {
178
179
  return { status: 'loading', model };
180
+ }
179
181
  const { percentage, downloaded, total } = transfer;
180
182
  return percentage < 100
181
183
  ? { ...progress, status: 'downloading', model, percentage, downloaded, total }
@@ -188,14 +190,11 @@ function monotonicMs() {
188
190
  }
189
191
  export function loadClockGuard(model, sdk, { now = () => Date.now(), monoMs = monotonicMs, sampleMs = CLOCK_SAMPLE_MS, jumpMs = CLOCK_JUMP_MS } = {}) {
190
192
  let timer = null;
191
- let fail;
193
+ const { promise: jumped, reject: fail } = Promise.withResolvers();
192
194
  let done = false;
193
195
  let abandoned = false;
194
196
  let lastWall = now();
195
197
  let lastMono = monoMs();
196
- const jumped = new Promise((_resolve, reject) => {
197
- fail = reject;
198
- });
199
198
  const arm = () => {
200
199
  timer = setTimeout(sample, sampleMs);
201
200
  timer.unref?.();
@@ -221,32 +220,36 @@ export function loadClockGuard(model, sdk, { now = () => Date.now(), monoMs = mo
221
220
  }
222
221
  catch (err) {
223
222
  if (abandoned && pending.requestId) {
224
- void sdk.cancel?.({ requestId: pending.requestId }).catch(() => { });
223
+ void sdk.cancel?.({ requestId: pending.requestId }).catch(noop);
225
224
  }
226
225
  throw err;
227
226
  }
228
227
  finally {
229
228
  done = true;
230
- if (timer !== null)
229
+ if (timer !== null) {
231
230
  clearTimeout(timer);
231
+ }
232
232
  }
233
233
  };
234
234
  return { race };
235
235
  }
236
- export function createModelPool(loadSdk, notify, deviceConfig = {}, loads = createLoadTracker()) {
236
+ export function createModelPool(loadSdk, notify, { deviceConfig = {}, loads = createLoadTracker() } = {}) {
237
237
  const load = async (model, opts) => {
238
238
  const sdk = await loadSdk();
239
239
  notify({ status: 'loading', model });
240
240
  const modelSrc = await resolveModelSrc(sdk, model);
241
241
  const resolved = resolveLoadOptions(opts, deviceConfig);
242
- const ctxSize = resolved.ctxSize || defaultContextSize(modelSrc);
242
+ const ctxSize = resolved.ctxSize || defaultContextSize(modelSrc, customContextLength(model));
243
243
  const projectionModelSrc = resolved.projectionModel
244
244
  ? await resolveModelSrc(sdk, resolved.projectionModel)
245
245
  : undefined;
246
246
  const mainGpu = deviceConfig.device !== 'cpu' && deviceConfig.mainGpu
247
247
  ? await deviceConfig.mainGpu()
248
248
  : undefined;
249
- const kvCache = adrenoSafeKvCache(deviceConfig.device, resolved.kvCacheK, resolved.kvCacheV);
249
+ const kvCache = adrenoSafeKvCache(deviceConfig.device, {
250
+ k: resolved.kvCacheK,
251
+ v: resolved.kvCacheV
252
+ });
250
253
  const openclCache = deviceConfig.device !== 'cpu' && deviceConfig.openclCacheDir
251
254
  ? deviceConfig.openclCacheDir()
252
255
  : undefined;
@@ -258,10 +261,10 @@ export function createModelPool(loadSdk, notify, deviceConfig = {}, loads = crea
258
261
  'cache-type-k': kvCache.k,
259
262
  'cache-type-v': kvCache.v,
260
263
  projectionModelSrc,
261
- ...(deviceConfig.device ? { device: deviceConfig.device } : {}),
262
- ...(deviceConfig.gpuLayers === undefined ? {} : { gpu_layers: deviceConfig.gpuLayers }),
263
- ...(mainGpu === undefined ? {} : { 'main-gpu': mainGpu }),
264
- ...(openclCache ? { openclCacheDir: openclCache } : {})
264
+ device: deviceConfig.device,
265
+ gpu_layers: deviceConfig.gpuLayers,
266
+ 'main-gpu': mainGpu,
267
+ openclCacheDir: openclCache || undefined
265
268
  };
266
269
  const { projectionModelSrc: projection, ...loggedConfig } = modelConfig;
267
270
  hlog('llm load config', { model, projection: Boolean(projection), ...loggedConfig });
@@ -276,13 +279,13 @@ export function createModelPool(loadSdk, notify, deviceConfig = {}, loads = crea
276
279
  notify({ status: 'ready', model });
277
280
  return { model, modelId, ctxSize };
278
281
  };
279
- return new ResourcePuddle({
282
+ return new ModelPuddle({
280
283
  open: (key, model = key, opts = {}) => new ModelHandle({ load: () => load(model, opts) }),
281
284
  gc: unloadOnGc(loadSdk, notify),
282
285
  ...NO_IDLE_GC
283
286
  });
284
287
  }
285
- export function createEmbedPool(loadSdk, notify, faults, batchSize = DEFAULT_EMBED_BATCH_SIZE, openclCacheDir, loads = createLoadTracker()) {
288
+ export function createEmbedPool(loadSdk, notify, { faults, batchSize = DEFAULT_EMBED_BATCH_SIZE, openclCacheDir, loads = createLoadTracker() }) {
286
289
  const load = async (model) => {
287
290
  const sdk = await loadSdk();
288
291
  notify({ status: 'loading', model });
@@ -295,20 +298,20 @@ export function createEmbedPool(loadSdk, notify, faults, batchSize = DEFAULT_EMB
295
298
  batchSize,
296
299
  gpuLayers: faults.gpuLayers(),
297
300
  device: faults.device,
298
- ...(openclCache ? { openclCacheDir: openclCache } : {})
301
+ openclCacheDir: openclCache || undefined
299
302
  },
300
303
  onProgress: (progress) => notify(progressState(model, progress))
301
304
  })));
302
305
  notify({ status: 'ready', model });
303
306
  return { model, modelId };
304
307
  };
305
- return new ResourcePuddle({
308
+ return new ModelPuddle({
306
309
  open: (key, model = key) => new ModelHandle({ load: () => load(model) }),
307
310
  gc: unloadOnGc(loadSdk, notify),
308
311
  ...NO_IDLE_GC
309
312
  });
310
313
  }
311
- export function createVoicePool(loadSdk, notify, modelType, addon, modelConfig, openclCacheDir, loads = createLoadTracker()) {
314
+ export function createVoicePool(loadSdk, notify, { modelType, addon, modelConfig, openclCacheDir, loads = createLoadTracker() }) {
312
315
  const wantsGpu = modelConfig?.['useGPU'] === true;
313
316
  let gpuRefused = false;
314
317
  const load = async (model) => {
@@ -335,7 +338,7 @@ export function createVoicePool(loadSdk, notify, modelType, addon, modelConfig,
335
338
  notify({ status: 'ready', model });
336
339
  return { model, modelId };
337
340
  };
338
- return new ResourcePuddle({
341
+ return new ModelPuddle({
339
342
  open: (key, model = key) => new ModelHandle({ load: () => load(model) }),
340
343
  gc: unloadOnGc(loadSdk, notify),
341
344
  ...NO_IDLE_GC
@@ -346,8 +349,9 @@ function unloadOnGc(loadSdk, notify) {
346
349
  const sdk = await loadSdk();
347
350
  for (const handle of handles) {
348
351
  const pending = handle.peek();
349
- if (pending === null)
352
+ if (pending === null) {
350
353
  continue;
354
+ }
351
355
  let loaded;
352
356
  try {
353
357
  loaded = await pending;
@@ -367,3 +371,4 @@ function unloadOnGc(loadSdk, notify) {
367
371
  }
368
372
  };
369
373
  }
374
+ function noop() { }
@@ -1,21 +1,26 @@
1
1
  import { audioGenModelByName, curatedModelAsset, customModelSrc } from './model-catalog.js';
2
2
  export async function resolveModelSrc(sdk, model, addon = 'llm') {
3
- if (/^(\/|~|\.|file:|https?:|registry:|pear:|hyper:)/.test(model))
3
+ if (/^(\/|~|\.|file:|https?:|registry:|pear:|hyper:)/.test(model)) {
4
4
  return model;
5
+ }
5
6
  const custom = customModelSrc(model, addon);
6
- if (custom)
7
+ if (custom) {
7
8
  return custom;
9
+ }
8
10
  if (addon === 'audiogen') {
9
11
  const audio = audioGenModelByName(model);
10
- if (!audio)
12
+ if (!audio) {
11
13
  throw new Error(`audiogen model not found: ${model}`);
14
+ }
12
15
  return audio.src;
13
16
  }
14
17
  const asset = curatedModelAsset(model, addon);
15
- if (asset)
18
+ if (asset) {
16
19
  return asset;
20
+ }
17
21
  const [entry] = await sdk.modelRegistrySearch({ filter: model, addon });
18
- if (!entry)
22
+ if (!entry) {
19
23
  throw new Error(`model not found in registry: ${model} (addon=${addon})`);
24
+ }
20
25
  return { ...entry, src: `registry://${entry.registrySource}/${entry.registryPath}` };
21
26
  }
@@ -2,11 +2,11 @@ export declare const MUSIC_MODEL_CHOICES: readonly ['turbo', 'turbo-hq', 'sft'];
2
2
  export type MusicModelChoice = (typeof MUSIC_MODEL_CHOICES)[number];
3
3
  export declare const GENERATE_MUSIC_TOOL = "generate_music";
4
4
  export declare const DEFAULT_MUSIC_MODEL_CHOICE: MusicModelChoice;
5
- export type MusicChoiceEntry = {
5
+ export interface MusicChoiceEntry {
6
6
  readonly choice: MusicModelChoice;
7
7
  readonly label: string;
8
8
  readonly sizeBytesApprox: number;
9
9
  readonly requiresGpu: boolean;
10
10
  readonly notes: string;
11
- };
11
+ }
12
12
  export declare const MUSIC_CHOICE_CATALOG: ReadonlyArray<MusicChoiceEntry>;
@@ -28,7 +28,8 @@ export interface SlmPlan {
28
28
  model: string;
29
29
  }
30
30
  export type TranslationPlan = NmtPlan | SlmPlan;
31
- export declare function sameLanguage(from: string, to: string): boolean;
32
- export declare function translationPlanFor(requestedFrom: string, requestedTo: string): TranslationPlan | {
31
+ export interface TranslationRefusal {
33
32
  error: string;
34
- };
33
+ }
34
+ export declare function sameLanguage(from: string, to: string): boolean;
35
+ export declare function translationPlanFor(requestedFrom: string, requestedTo: string): TranslationPlan | TranslationRefusal;
@@ -115,8 +115,9 @@ function canonical(code) {
115
115
  export function translationPlanFor(requestedFrom, requestedTo) {
116
116
  const from = requestedFrom.toLowerCase();
117
117
  const to = requestedTo.toLowerCase();
118
- if (sameLanguage(from, to))
118
+ if (sameLanguage(from, to)) {
119
119
  return { error: `${from} is already the target language` };
120
+ }
120
121
  const slmFrom = slmLanguage(from);
121
122
  const slmTo = slmLanguage(to);
122
123
  if (slmFrom || slmTo) {
@@ -146,16 +147,20 @@ export function translationPlanFor(requestedFrom, requestedTo) {
146
147
  };
147
148
  }
148
149
  if (to === ENGLISH) {
149
- if (!TO_ENGLISH.includes(from))
150
+ if (!TO_ENGLISH.includes(from)) {
150
151
  return { error: `cannot translate from ${from}` };
152
+ }
151
153
  return { route: 'nmt', from, to, model: modelFilter(`${from}${ENGLISH}`) };
152
154
  }
153
- if (!FROM_ENGLISH.includes(to))
155
+ if (!FROM_ENGLISH.includes(to)) {
154
156
  return { error: `cannot translate to ${to}` };
155
- if (from === ENGLISH)
157
+ }
158
+ if (from === ENGLISH) {
156
159
  return { route: 'nmt', from, to, model: modelFilter(`${ENGLISH}${to}`) };
157
- if (!TO_ENGLISH.includes(from))
160
+ }
161
+ if (!TO_ENGLISH.includes(from)) {
158
162
  return { error: `cannot translate from ${from}` };
163
+ }
159
164
  return {
160
165
  route: 'nmt',
161
166
  from,
@@ -2,4 +2,9 @@ export declare const OPENCL_CACHE_MAX_AGE_MS: number;
2
2
  export declare function openclEngineKey(sdkVersion?: string): string;
3
3
  export declare function openclCacheRoot(env?: Record<string, string | undefined>, hostPlatform?: string): string | undefined;
4
4
  export declare function openclCacheDir(env?: Record<string, string | undefined>, hostPlatform?: string, sdkVersion?: string): string | undefined;
5
- export declare function prepareOpenclCache(env?: Record<string, string | undefined>, hostPlatform?: string, sdkVersion?: string, now?: number): void;
5
+ export interface PrepareOpenclCacheOptions {
6
+ hostPlatform?: string;
7
+ sdkVersion?: string | undefined;
8
+ now?: number;
9
+ }
10
+ export declare function prepareOpenclCache(env?: Record<string, string | undefined>, { hostPlatform, sdkVersion, now }?: PrepareOpenclCacheOptions): void;
@@ -13,18 +13,20 @@ export function openclCacheRoot(env = process.env, hostPlatform = platform()) {
13
13
  if (hostPlatform !== 'android')
14
14
  return undefined;
15
15
  const dir = env[OPENCL_CACHE_DIR_ENV];
16
- if (!dir?.startsWith('/'))
16
+ if (!dir?.startsWith('/')) {
17
17
  return undefined;
18
+ }
18
19
  return dir.split('/').includes('..') ? undefined : dir;
19
20
  }
20
21
  export function openclCacheDir(env = process.env, hostPlatform = platform(), sdkVersion) {
21
22
  const root = openclCacheRoot(env, hostPlatform);
22
23
  return root === undefined ? undefined : path.join(root, openclEngineKey(sdkVersion));
23
24
  }
24
- export function prepareOpenclCache(env = process.env, hostPlatform = platform(), sdkVersion, now = Date.now()) {
25
+ export function prepareOpenclCache(env = process.env, { hostPlatform = platform(), sdkVersion, now = Date.now() } = {}) {
25
26
  const root = openclCacheRoot(env, hostPlatform);
26
- if (root === undefined)
27
+ if (root === undefined) {
27
28
  return;
29
+ }
28
30
  const key = openclEngineKey(sdkVersion);
29
31
  try {
30
32
  fs.mkdirSync(path.join(root, key), { recursive: true });
@@ -3,19 +3,22 @@ export declare function isSdkGpuDeviceLostError(err: unknown): boolean;
3
3
  export declare function isSdkModelWedgedError(err: unknown): boolean;
4
4
  export declare function isContextOverflowError(err: unknown): boolean;
5
5
  export type GpuFaultDecision = 'reload' | 'fallback';
6
- export declare function createGpuFaultEscalation(): {
6
+ interface GpuFaultEscalation {
7
7
  recordFault(onGpu: boolean): GpuFaultDecision;
8
8
  reset(): void;
9
- };
10
- export type GpuFaultState = ReturnType<typeof createGpuFaultState>;
11
- export declare function createGpuFaultState({ device, gpuLayers }?: {
12
- device?: EmbedDevice;
13
- gpuLayers?: number;
14
- }): {
9
+ }
10
+ export declare function createGpuFaultEscalation(): GpuFaultEscalation;
11
+ export interface GpuFaultState {
15
12
  readonly device: EmbedDevice;
16
13
  readonly generation: number;
17
14
  gpuLayers(): number;
18
15
  key(model: string): string;
19
16
  recordFault(bornAt: number): void;
20
17
  reset(): void;
21
- };
18
+ }
19
+ export interface GpuFaultStateOptions {
20
+ device?: EmbedDevice;
21
+ gpuLayers?: number;
22
+ }
23
+ export declare function createGpuFaultState({ device, gpuLayers }?: GpuFaultStateOptions): GpuFaultState;
24
+ export {};
@@ -1,23 +1,25 @@
1
1
  export function isSdkGpuDeviceLostError(err) {
2
2
  if (typeof err !== 'object' || err === null)
3
3
  return false;
4
- const message = err.message;
5
- if (typeof message !== 'string')
4
+ const message = 'message' in err ? err.message : undefined;
5
+ if (typeof message !== 'string') {
6
6
  return false;
7
+ }
7
8
  return /ErrorDeviceLost/i.test(message) || /VK_ERROR_DEVICE_LOST/i.test(message);
8
9
  }
9
10
  export function isSdkModelWedgedError(err) {
10
11
  if (typeof err !== 'object' || err === null)
11
12
  return false;
12
- const message = err.message;
13
- if (typeof message !== 'string')
13
+ const message = 'message' in err ? err.message : undefined;
14
+ if (typeof message !== 'string') {
14
15
  return false;
16
+ }
15
17
  return /failed to (decode next token|eval chunk)/i.test(message);
16
18
  }
17
19
  export function isContextOverflowError(err) {
18
20
  if (typeof err !== 'object' || err === null)
19
21
  return false;
20
- return err.name === 'CONTEXT_OVERFLOW';
22
+ return 'name' in err && err.name === 'CONTEXT_OVERFLOW';
21
23
  }
22
24
  export function createGpuFaultEscalation() {
23
25
  let count = 0;
@@ -56,8 +58,9 @@ export function createGpuFaultState({ device, gpuLayers } = {}) {
56
58
  return;
57
59
  lastFaulted = bornAt;
58
60
  generation += 1;
59
- if (escalation.recordFault(current === 'gpu') === 'fallback')
61
+ if (escalation.recordFault(current === 'gpu') === 'fallback') {
60
62
  current = 'cpu';
63
+ }
61
64
  },
62
65
  reset() {
63
66
  escalation.reset();
@@ -0,0 +1,11 @@
1
+ export interface Shard {
2
+ readonly url: string;
3
+ readonly filename: string;
4
+ }
5
+ export interface ShardSet {
6
+ readonly base: string;
7
+ readonly baseUrl: string;
8
+ readonly extension: string;
9
+ readonly shards: ReadonlyArray<Shard>;
10
+ }
11
+ export declare function shardSet(src: string): ShardSet | null;
@@ -0,0 +1,15 @@
1
+ const SHARD = /^(.+)-(\d{5})-of-(\d{5})(\.\w+)$/;
2
+ export function shardSet(src) {
3
+ const parts = src.split('/');
4
+ const match = SHARD.exec(parts[parts.length - 1]?.split('?')[0] ?? '');
5
+ if (!match) {
6
+ return null;
7
+ }
8
+ const [, base, , total, extension] = match;
9
+ const baseUrl = parts.slice(0, -1).join('/');
10
+ const shards = Array.from({ length: Number(total) }, (_, i) => {
11
+ const filename = `${base}-${String(i + 1).padStart(5, '0')}-of-${total}${extension}`;
12
+ return { url: `${baseUrl}/${filename}`, filename };
13
+ });
14
+ return { base, baseUrl, extension, shards };
15
+ }
@@ -5,15 +5,20 @@ export function detectToolDialect(src, name) {
5
5
  .filter(Boolean)
6
6
  .pop() ?? '';
7
7
  const tag = `${(name ?? '').toLowerCase()}|${basename}`;
8
- if (/qwen3[._-]?[56](?![a-z0-9])/.test(tag))
8
+ if (/qwen3[._-]?[56](?![a-z0-9])/.test(tag)) {
9
9
  return 'qwen35';
10
- if (/gemma[-_]?4(?=[^a-z0-9]|$)/.test(tag))
10
+ }
11
+ if (/gemma[-_]?4(?=[^a-z0-9]|$)/.test(tag)) {
11
12
  return 'gemma4';
12
- if (/gpt[_-]?oss/.test(tag))
13
+ }
14
+ if (/gpt[_-]?oss/.test(tag)) {
13
15
  return 'harmony';
14
- if (/deepseek[-_. ]?v(?:4|3[._-]?2)(?![0-9])/.test(tag))
16
+ }
17
+ if (/deepseek[-_. ]?v(?:4|3[._-]?2)(?![0-9])/.test(tag)) {
15
18
  return 'dsml';
16
- if (/lfm[_-]?\d/.test(tag))
19
+ }
20
+ if (/lfm[_-]?\d/.test(tag)) {
17
21
  return 'pythonic';
22
+ }
18
23
  return undefined;
19
24
  }
@@ -1,15 +1,15 @@
1
1
  import { type BareLookupAll } from './validating-lookup.ts';
2
2
  import { type NoRedirectFetchInit, type NoRedirectFetchResponse } from './no-redirect-fetch-impl.ts';
3
3
  export type { NoRedirectFetchResponse };
4
- export type ConnectionAgent = {
4
+ export interface ConnectionAgent {
5
5
  createConnection(opts: {
6
6
  readonly host?: string;
7
7
  }): unknown;
8
- };
8
+ }
9
9
  export type AgentCtor<A extends ConnectionAgent> = new (opts: object) => A;
10
10
  export declare class BlockedHostError extends Error {
11
11
  readonly code = "EBLOCKEDHOST";
12
12
  constructor(host: string);
13
13
  }
14
14
  export declare function makeValidatingAgent<A extends ConnectionAgent>(Agent: AgentCtor<A>, lookup: BareLookupAll): A;
15
- export default function noRedirectFetch(url: URL, init?: NoRedirectFetchInit): Promise<NoRedirectFetchResponse>;
15
+ export declare function noRedirectFetch(url: URL, init?: NoRedirectFetchInit): Promise<NoRedirectFetchResponse>;
@@ -35,7 +35,7 @@ const rawAllLookup = (hostname, opts, cb) => {
35
35
  const validatingLookup = makeValidatingLookup(rawAllLookup);
36
36
  const httpAgent = makeValidatingAgent(HttpAgent, validatingLookup);
37
37
  const httpsAgent = makeValidatingAgent(HttpsAgent, validatingLookup);
38
- export default function noRedirectFetch(url, init = {}) {
38
+ export function noRedirectFetch(url, init = {}) {
39
39
  const request = (target, opts, onresponse) => {
40
40
  const common = { method: readMethod(opts.method), headers: opts.headers };
41
41
  const forward = (res) => onresponse(res);
@@ -48,8 +48,9 @@ export function runNoRedirectFetch(request, url, init = {}) {
48
48
  if (init.maxBytes !== undefined && received > init.maxBytes) {
49
49
  if (init.truncate) {
50
50
  const allowed = chunk.byteLength - (received - init.maxBytes);
51
- if (allowed > 0)
51
+ if (allowed > 0) {
52
52
  chunks.push(chunk.subarray(0, allowed));
53
+ }
53
54
  respond();
54
55
  req.destroy();
55
56
  return;
@@ -72,12 +73,14 @@ export function runNoRedirectFetch(request, url, init = {}) {
72
73
  req.destroy(err);
73
74
  reject(err);
74
75
  };
75
- if (init.signal.aborted)
76
+ if (init.signal.aborted) {
76
77
  return abort();
78
+ }
77
79
  init.signal.addEventListener('abort', abort);
78
80
  }
79
- if (init.body !== undefined)
81
+ if (init.body !== undefined) {
80
82
  req.write(init.body);
83
+ }
81
84
  req.end();
82
85
  });
83
86
  }
@@ -1,4 +1,4 @@
1
1
  import * as http from 'node:http';
2
2
  import { type NoRedirectFetchInit, type NoRedirectFetchResponse } from './no-redirect-fetch-impl.ts';
3
- export default function noRedirectFetch(url: URL, init?: NoRedirectFetchInit): Promise<NoRedirectFetchResponse>;
3
+ export declare function noRedirectFetch(url: URL, init?: NoRedirectFetchInit): Promise<NoRedirectFetchResponse>;
4
4
  export declare function createLookup(validatedAddresses: readonly string[] | undefined): NonNullable<http.RequestOptions['lookup']>;
@@ -1,9 +1,10 @@
1
1
  import * as dns from 'node:dns';
2
2
  import * as http from 'node:http';
3
3
  import * as https from 'node:https';
4
+ import { isRecord } from './guards.js';
4
5
  import { isPrivateOrBlocked } from './tools/private-ip.js';
5
6
  import { runNoRedirectFetch } from './no-redirect-fetch-impl.js';
6
- export default function noRedirectFetch(url, init = {}) {
7
+ export function noRedirectFetch(url, init = {}) {
7
8
  const request = (target, options, onresponse) => {
8
9
  const common = {
9
10
  method: options.method,
@@ -17,22 +18,21 @@ export default function noRedirectFetch(url, init = {}) {
17
18
  }
18
19
  export function createLookup(validatedAddresses) {
19
20
  return (hostname, options, callback) => {
20
- const wantsAll = typeof options === 'object' && options !== null && options.all === true;
21
- const cb = callback;
21
+ const wantsAll = isRecord(options) && options.all === true;
22
22
  const fail = () => {
23
- cb(new Error(`Requests to "${hostname}" resolve to a blocked address.`), '', 0);
23
+ callback(new Error(`Requests to "${hostname}" resolve to a blocked address.`), '', 0);
24
24
  };
25
25
  const respond = (records) => {
26
- if (records.length === 0 || records.some((record) => isPrivateOrBlocked(record.address))) {
26
+ const [first] = records;
27
+ if (!first || records.some((record) => isPrivateOrBlocked(record.address))) {
27
28
  fail();
28
29
  return;
29
30
  }
30
31
  if (wantsAll) {
31
- cb(null, records);
32
+ callback(null, records);
32
33
  return;
33
34
  }
34
- const [first] = records;
35
- cb(null, first.address, first.family);
35
+ callback(null, first.address, first.family);
36
36
  };
37
37
  if (validatedAddresses !== undefined) {
38
38
  respond(validatedAddresses.map((address) => ({
@@ -43,7 +43,7 @@ export function createLookup(validatedAddresses) {
43
43
  }
44
44
  dns.lookup(hostname, { all: true }, (err, records) => {
45
45
  if (err) {
46
- cb(err, '', 0);
46
+ callback(err, '', 0);
47
47
  return;
48
48
  }
49
49
  respond(records);