@qvac/harness 0.4.0 → 0.6.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 +740 -141
  2. package/README.md +75 -7
  3. package/dist/harness.bundle +72 -50
  4. package/dist-lib/index.d.ts +49 -39
  5. package/dist-lib/index.js +448 -161
  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 +13 -32
  29. package/dist-lib/lib/connect.js +170 -112
  30. package/dist-lib/lib/context-window.js +25 -21
  31. package/dist-lib/lib/contract-types.d.ts +35 -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 +15 -20
  84. package/dist-lib/lib/models/model-catalog.js +66 -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 +48 -10
  90. package/dist-lib/lib/models/model-pool.js +106 -45
  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 +122 -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 +39 -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 +9 -0
  288. package/dist-lib/spec/calls.js +3 -0
  289. package/dist-lib/spec/hrpc/hrpc.json +39 -0
  290. package/dist-lib/spec/hrpc/index.js +39 -3
  291. package/dist-lib/spec/hrpc/messages.js +105 -8
  292. package/dist-lib/spec/hyperschema/index.js +105 -8
  293. package/dist-lib/spec/hyperschema/schema.json +77 -1
  294. package/dist-lib/spec/hyperschema/types.d.ts +25 -1
  295. package/package.json +14 -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
@@ -28,12 +28,14 @@ export function musicPercentTick(onPercent) {
28
28
  let last = -1;
29
29
  return ({ stage, step, total }) => {
30
30
  const weight = STAGE_WEIGHT[stage];
31
- if (!weight)
31
+ if (!weight) {
32
32
  return;
33
+ }
33
34
  const frac = total > 0 ? Math.min(1, Math.max(0, step / total)) : 0;
34
35
  const percentage = Math.min(100, Math.round(weight.base + frac * weight.span));
35
- if (percentage <= last)
36
+ if (percentage <= last) {
36
37
  return;
38
+ }
37
39
  last = percentage;
38
40
  onPercent(percentage);
39
41
  };
@@ -83,16 +85,18 @@ export function createMusicRuntime({ loadSdk, onProgress, onBeforeLoad, loads =
83
85
  loadSdk,
84
86
  gpuRequested: useGpu,
85
87
  defaultSteps,
86
- ensureLoaded: async (sdk, signal, modelChoice, steps) => {
88
+ ensureLoaded: async (sdk, { signal, modelChoice, inferenceSteps: steps } = {}) => {
87
89
  const stages = stagesFor(modelChoice, base);
88
90
  const effectiveSteps = steps ?? baseSteps;
89
91
  const fingerprint = fingerprintOf(stages, effectiveSteps);
90
92
  await lock.lock();
91
93
  try {
92
- if (loading && residentFingerprint !== fingerprint)
94
+ if (loading && residentFingerprint !== fingerprint) {
93
95
  await doUnload();
94
- if (!loading && onBeforeLoad)
96
+ }
97
+ if (!loading && onBeforeLoad) {
95
98
  await onBeforeLoad(signal);
99
+ }
96
100
  residentFingerprint = fingerprint;
97
101
  loading ??= (async () => {
98
102
  onProgress?.(musicProgressName(stages.dit), {});
@@ -108,7 +112,7 @@ export function createMusicRuntime({ loadSdk, onProgress, onBeforeLoad, loads =
108
112
  ditModelSrc,
109
113
  vaeModelSrc,
110
114
  useGPU: useGpu,
111
- ...(effectiveSteps ? { inferenceSteps: effectiveSteps } : {})
115
+ inferenceSteps: effectiveSteps || undefined
112
116
  };
113
117
  const modelId = await loads.track(sdk.loadModel({
114
118
  modelType: 'audiogen',
@@ -136,41 +140,48 @@ export function createMusicRuntime({ loadSdk, onProgress, onBeforeLoad, loads =
136
140
  };
137
141
  }
138
142
  export const BUSY_ERROR = 'a music generation is already running; try again when it finishes';
139
- export async function runAudioGen(sdk, modelId, params, signal, onTick) {
140
- if (signal?.aborted)
143
+ export async function runAudioGen(sdk, { modelId, params, signal, onTick }) {
144
+ if (signal?.aborted) {
141
145
  return { error: 'music generation canceled' };
146
+ }
142
147
  let run;
143
148
  try {
144
149
  run = sdk.audioGen({ modelId, ...params });
145
150
  }
146
151
  catch (err) {
147
- if (signal?.aborted)
152
+ if (signal?.aborted) {
148
153
  return { error: 'music generation canceled' };
154
+ }
149
155
  throw err;
150
156
  }
151
157
  const onAbort = () => {
152
- void sdk.cancel?.({ requestId: run.requestId }).catch(() => { });
158
+ void sdk.cancel?.({ requestId: run.requestId }).catch(noop);
153
159
  };
154
160
  signal?.addEventListener('abort', onAbort, { once: true });
155
161
  let settled = false;
156
162
  try {
157
163
  const drained = (async () => {
158
- for await (const tick of run.progressStream)
159
- if (!settled)
164
+ for await (const tick of run.progressStream) {
165
+ if (!settled) {
160
166
  onTick?.(tick);
161
- })().catch(() => { });
167
+ }
168
+ }
169
+ })().catch(noop);
162
170
  const audio = await run.audio;
163
171
  await drained;
164
- if (signal?.aborted)
172
+ if (signal?.aborted) {
165
173
  return { error: 'music generation canceled' };
166
- if (!audio?.pcm?.length)
174
+ }
175
+ if (!audio?.pcm?.length) {
167
176
  return { error: 'the audiogen model produced no audio' };
168
- const stats = await run.stats.catch(() => undefined);
177
+ }
178
+ const stats = await run.stats.catch(noop);
169
179
  return stats ? { ...audio, stats } : audio;
170
180
  }
171
181
  catch (err) {
172
- if (signal?.aborted)
182
+ if (signal?.aborted) {
173
183
  return { error: 'music generation canceled' };
184
+ }
174
185
  throw err;
175
186
  }
176
187
  finally {
@@ -180,8 +191,9 @@ export async function runAudioGen(sdk, modelId, params, signal, onTick) {
180
191
  }
181
192
  export function audioDurationSeconds(audio) {
182
193
  const bytesPerSample = audio.bitsPerSample / 8;
183
- if (bytesPerSample <= 0 || audio.channels <= 0 || audio.sampleRate <= 0)
194
+ if (bytesPerSample <= 0 || audio.channels <= 0 || audio.sampleRate <= 0) {
184
195
  return 0;
196
+ }
185
197
  const samplesPerChannel = audio.pcm.byteLength / bytesPerSample / audio.channels;
186
198
  return Math.round(samplesPerChannel / audio.sampleRate);
187
199
  }
@@ -197,8 +209,9 @@ export function presentStats(stats) {
197
209
  'backendId'
198
210
  ]) {
199
211
  const value = stats[key];
200
- if (typeof value === 'number' && Number.isFinite(value))
212
+ if (typeof value === 'number' && Number.isFinite(value)) {
201
213
  kept[key] = value;
214
+ }
202
215
  }
203
216
  return Object.keys(kept).length > 0 ? kept : undefined;
204
217
  }
@@ -240,8 +253,9 @@ export function pcmToWav(audio) {
240
253
  const header = new ArrayBuffer(44);
241
254
  const view = new DataView(header);
242
255
  const ascii = (offset, value) => {
243
- for (let i = 0; i < value.length; i++)
256
+ for (let i = 0; i < value.length; i++) {
244
257
  view.setUint8(offset + i, value.charCodeAt(i));
258
+ }
245
259
  };
246
260
  ascii(0, 'RIFF');
247
261
  view.setUint32(4, 36 + pcm.byteLength, true);
@@ -263,8 +277,9 @@ export function pcmToWav(audio) {
263
277
  }
264
278
  function parseArgs(args, defaults, maxDuration) {
265
279
  const caption = typeof args.prompt === 'string' ? args.prompt.trim() : '';
266
- if (!caption)
280
+ if (!caption) {
267
281
  return { error: 'prompt must be a non-empty string' };
282
+ }
268
283
  if (caption.length > MAX_CAPTION_CHARS) {
269
284
  return { error: `prompt exceeds ${MAX_CAPTION_CHARS} characters` };
270
285
  }
@@ -285,16 +300,18 @@ function parseArgs(args, defaults, maxDuration) {
285
300
  }
286
301
  duration = Math.round(n);
287
302
  }
288
- if (duration !== undefined)
303
+ if (duration !== undefined) {
289
304
  duration = Math.min(duration, maxDuration);
305
+ }
290
306
  let seed;
291
307
  if (args.seed !== undefined && args.seed !== null) {
292
308
  seed = Number(args.seed);
293
309
  if (!Number.isInteger(seed) || seed < -1 || seed > MAX_SEED) {
294
310
  return { error: `seed must be an integer between -1 (random) and ${MAX_SEED}` };
295
311
  }
296
- if (seed === -1)
312
+ if (seed === -1) {
297
313
  seed = undefined;
314
+ }
298
315
  }
299
316
  let vocalLanguage;
300
317
  if (typeof args.vocalLanguage === 'string' && args.vocalLanguage.trim()) {
@@ -362,8 +379,9 @@ export function normalizeClamps(clamps) {
362
379
  }
363
380
  export function resolveMusicSettings(ctx) {
364
381
  const settings = ctx?.settings;
365
- if (!settings)
382
+ if (!settings) {
366
383
  return {};
384
+ }
367
385
  const out = {};
368
386
  if (typeof settings.modelChoice === 'string' && settings.modelChoice) {
369
387
  out.modelChoice = settings.modelChoice;
@@ -438,12 +456,15 @@ export function createGenerateMusicTool(runtime, clamps) {
438
456
  execute: async (args, ctx) => {
439
457
  const music = resolveMusicSettings(ctx);
440
458
  const parsed = parseArgs(args, { duration: settingsDuration(music.duration, cap.maxDuration) }, cap.maxDuration);
441
- if ('error' in parsed)
459
+ if ('error' in parsed) {
442
460
  return parsed;
443
- if (!ctx?.attach)
461
+ }
462
+ if (!ctx?.attach) {
444
463
  return { error: 'this device cannot persist generated audio' };
445
- if (!runtime.acquire())
464
+ }
465
+ if (!runtime.acquire()) {
446
466
  return { error: BUSY_ERROR, code: 'busy' };
467
+ }
447
468
  const report = ctx.progress;
448
469
  const onTick = report && musicPercentTick((percentage) => report({ percentage }));
449
470
  const capMax = stepsCap ?? MAX_STEPS;
@@ -453,25 +474,35 @@ export function createGenerateMusicTool(runtime, clamps) {
453
474
  stepsCap;
454
475
  try {
455
476
  const sdk = await runtime.loadSdk();
456
- const { modelId } = await runtime.ensureLoaded(sdk, ctx.signal, music.modelChoice, steps);
477
+ const { modelId } = await runtime.ensureLoaded(sdk, {
478
+ signal: ctx.signal,
479
+ modelChoice: music.modelChoice,
480
+ inferenceSteps: steps
481
+ });
457
482
  const hasVocals = parsed.lyrics !== undefined &&
458
483
  parsed.lyrics.trim().toLowerCase() !== INSTRUMENTAL.toLowerCase();
459
484
  const instrumental = !hasVocals;
460
485
  const vocalLanguage = hasVocals
461
486
  ? (parsed.vocalLanguage ?? DEFAULT_VOCAL_LANGUAGE)
462
487
  : undefined;
463
- const result = await runAudioGen(sdk, modelId, {
464
- caption: parsed.caption,
465
- lyrics: hasVocals ? parsed.lyrics : INSTRUMENTAL,
466
- ...(parsed.duration !== undefined ? { duration: parsed.duration } : {}),
467
- ...(parsed.seed !== undefined ? { seed: parsed.seed } : {}),
468
- ...(vocalLanguage ? { vocalLanguage } : {}),
469
- ...(parsed.bpm !== undefined ? { bpm: parsed.bpm } : {}),
470
- ...(parsed.keyscale ? { keyscale: parsed.keyscale } : {}),
471
- ...(parsed.timesignature ? { timesignature: parsed.timesignature } : {})
472
- }, ctx.signal, onTick);
473
- if ('error' in result)
488
+ const result = await runAudioGen(sdk, {
489
+ modelId,
490
+ params: {
491
+ caption: parsed.caption,
492
+ lyrics: hasVocals ? parsed.lyrics : INSTRUMENTAL,
493
+ duration: parsed.duration,
494
+ seed: parsed.seed,
495
+ vocalLanguage: vocalLanguage || undefined,
496
+ bpm: parsed.bpm,
497
+ keyscale: parsed.keyscale || undefined,
498
+ timesignature: parsed.timesignature || undefined
499
+ },
500
+ signal: ctx.signal,
501
+ onTick
502
+ });
503
+ if ('error' in result) {
474
504
  return result;
505
+ }
475
506
  const wav = pcmToWav(result);
476
507
  const record = await ctx.attach({
477
508
  fileName: musicFileName(parsed.title, parsed.caption),
@@ -497,20 +528,19 @@ export function createGenerateMusicTool(runtime, clamps) {
497
528
  sampleRate: result.sampleRate,
498
529
  channels: result.channels,
499
530
  instrumental,
500
- ...(instrumental && captionImpliesVocals(parsed.caption)
501
- ? {
502
- note: 'This track is instrumental: no lyrics were provided, so the model did not sing. ' +
503
- 'The prompt described vocals; to produce a sung version, call generate_music again ' +
504
- 'with lyrics ([verse]/[chorus] tags). Otherwise describe this track as instrumental, not vocal.'
505
- }
506
- : {}),
531
+ note: instrumental && captionImpliesVocals(parsed.caption)
532
+ ? 'This track is instrumental: no lyrics were provided, so the model did not sing. ' +
533
+ 'The prompt described vocals; to produce a sung version, call generate_music again ' +
534
+ 'with lyrics ([verse]/[chorus] tags). Otherwise describe this track as instrumental, not vocal.'
535
+ : undefined,
507
536
  hostNotice: notice,
508
- ...(stats ? { data: { stats } } : {})
537
+ data: stats ? { stats } : undefined
509
538
  };
510
539
  }
511
540
  catch (err) {
512
- if (err instanceof AbortedError)
541
+ if (err instanceof AbortedError) {
513
542
  return { error: 'music generation canceled' };
543
+ }
514
544
  throw err;
515
545
  }
516
546
  finally {
@@ -520,3 +550,4 @@ export function createGenerateMusicTool(runtime, clamps) {
520
550
  }
521
551
  };
522
552
  }
553
+ function noop() { }
@@ -1,3 +1,4 @@
1
+ import { errorMessage } from '../guards.js';
1
2
  import { hlog } from '../log.js';
2
3
  import { searchTargets } from './file-glob.js';
3
4
  import { readLines } from './fs/read.js';
@@ -8,6 +9,38 @@ export const DEFAULT_GREP_MATCHES = 200;
8
9
  export const MAX_GREP_MATCHES = 1000;
9
10
  const MAX_LINE_CHARS = 500;
10
11
  const BINARY_PROBE_CHARS = 1024;
12
+ async function scanFile(drive, key, { path, tests, room }) {
13
+ const hits = [];
14
+ let probed = 0;
15
+ let binary = false;
16
+ let index = 0;
17
+ try {
18
+ for await (const line of readLines(drive, key)) {
19
+ index++;
20
+ if (probed < BINARY_PROBE_CHARS && line.includes('\0')) {
21
+ binary = true;
22
+ break;
23
+ }
24
+ if (probed < BINARY_PROBE_CHARS) {
25
+ probed += line.length + 1;
26
+ }
27
+ if (!tests.some((test) => test(line))) {
28
+ continue;
29
+ }
30
+ hits.push({ file: path, line: index, text: line.slice(0, MAX_LINE_CHARS) });
31
+ if (hits.length >= room) {
32
+ return { hits, binary, end: 'capped' };
33
+ }
34
+ }
35
+ }
36
+ catch (error) {
37
+ if (isDriveReadTruncated(error)) {
38
+ return { hits, binary, end: 'cut' };
39
+ }
40
+ return { hits, binary, end: 'unreadable', error };
41
+ }
42
+ return { hits, binary, end: 'complete' };
43
+ }
11
44
  function stringArray(raw) {
12
45
  if (!Array.isArray(raw) || raw.length === 0)
13
46
  return null;
@@ -23,7 +56,7 @@ function compileQueries(queries, regex, caseSensitive) {
23
56
  });
24
57
  }
25
58
  catch (err) {
26
- return { error: `invalid regex: ${err.message}` };
59
+ return { error: `invalid regex: ${errorMessage(err)}` };
27
60
  }
28
61
  }
29
62
  return queries.map((q) => {
@@ -70,11 +103,13 @@ export function createGrepTool({ realpath = defaultRealpath, deniedRoots } = {})
70
103
  rootsRequired: true,
71
104
  validate(rawArgs) {
72
105
  const queries = stringArray(rawArgs.queries);
73
- if (!queries)
106
+ if (!queries) {
74
107
  return 'queries must be a non-empty array of strings';
108
+ }
75
109
  const compiled = compileQueries(queries, rawArgs.regex === true, rawArgs.caseSensitive === true);
76
- if ('error' in compiled)
110
+ if ('error' in compiled) {
77
111
  return compiled.error;
112
+ }
78
113
  const maxMatches = rawArgs.maxMatches;
79
114
  if (maxMatches !== undefined && maxMatches !== null) {
80
115
  const n = Number(maxMatches);
@@ -91,16 +126,19 @@ export function createGrepTool({ realpath = defaultRealpath, deniedRoots } = {})
91
126
  async execute(rawArgs, ctx) {
92
127
  const roots = ctx?.fsRoots ?? [];
93
128
  const queries = stringArray(rawArgs.queries);
94
- if (!queries)
129
+ if (!queries) {
95
130
  return { error: 'queries must be a non-empty array of strings' };
131
+ }
96
132
  const compiled = compileQueries(queries, rawArgs.regex === true, rawArgs.caseSensitive === true);
97
- if ('error' in compiled)
133
+ if ('error' in compiled) {
98
134
  return compiled;
135
+ }
99
136
  const maxMatches = rawArgs.maxMatches ? Number(rawArgs.maxMatches) : DEFAULT_GREP_MATCHES;
100
137
  const deny = await canonicalDenyRoots(deniedRoots, realpath);
101
- const scoped = await searchTargets(rawArgs.dir, roots, realpath, deny);
102
- if ('error' in scoped)
138
+ const scoped = await searchTargets(rawArgs.dir, roots, { realpath, deny });
139
+ if ('error' in scoped) {
103
140
  return scoped;
141
+ }
104
142
  const matches = [];
105
143
  let truncated = false;
106
144
  let skippedLinks = 0;
@@ -111,47 +149,34 @@ export function createGrepTool({ realpath = defaultRealpath, deniedRoots } = {})
111
149
  for (const file of walk.files) {
112
150
  if (isDeniedPath(file.path, deny))
113
151
  continue;
114
- const hits = [];
115
- let probed = 0;
116
- let binary = false;
117
- let index = 0;
118
- try {
119
- for await (const line of readLines(target.root.drive, file.key)) {
120
- index++;
121
- if (probed < BINARY_PROBE_CHARS) {
122
- if (line.includes('\0')) {
123
- binary = true;
124
- break;
125
- }
126
- probed += line.length + 1;
127
- }
128
- if (!compiled.some((test) => test(line)))
129
- continue;
130
- hits.push({ file: file.path, line: index, text: line.slice(0, MAX_LINE_CHARS) });
131
- if (matches.length + hits.length >= maxMatches) {
132
- matches.push(...hits);
133
- return { matches, truncated: true, ...(skippedLinks ? { skippedLinks } : {}) };
134
- }
135
- }
152
+ const scan = await scanFile(target.root.drive, file.key, {
153
+ path: file.path,
154
+ tests: compiled,
155
+ room: maxMatches - matches.length
156
+ });
157
+ if (scan.end === 'capped') {
158
+ matches.push(...scan.hits);
159
+ return { matches, truncated: true, skippedLinks: skippedLinks || undefined };
136
160
  }
137
- catch (err) {
138
- if (isDriveReadTruncated(err)) {
139
- if (!binary)
140
- matches.push(...hits);
141
- truncated = true;
142
- continue;
143
- }
144
- hlog('warn: grep skipped a file', { file: file.path, err: err.message });
161
+ if (scan.end === 'unreadable') {
162
+ hlog('warn: grep skipped a file', {
163
+ file: file.path,
164
+ err: errorMessage(scan.error)
165
+ });
145
166
  continue;
146
167
  }
147
- if (!binary)
148
- matches.push(...hits);
168
+ if (scan.end === 'cut') {
169
+ truncated = true;
170
+ }
171
+ if (!scan.binary) {
172
+ matches.push(...scan.hits);
173
+ }
149
174
  }
150
175
  }
151
176
  return {
152
177
  matches,
153
- ...(truncated ? { truncated } : {}),
154
- ...(skippedLinks ? { skippedLinks } : {})
178
+ truncated: truncated || undefined,
179
+ skippedLinks: skippedLinks || undefined
155
180
  };
156
181
  }
157
182
  };
@@ -1,11 +1,14 @@
1
+ import b4a from 'b4a';
2
+ import { isRecord } from '../../guards.js';
1
3
  const BASE64URL = /^[A-Za-z0-9_-]+={0,2}$/;
2
4
  const PRINTABLE_RATIO = 0.9;
3
5
  export function encodeDeclaredFields(body, encode) {
4
6
  if (!encode)
5
7
  return body;
6
8
  const parsed = asObject(body);
7
- if (!parsed)
9
+ if (!parsed) {
8
10
  return body;
11
+ }
9
12
  let next = parsed;
10
13
  for (const [path, encoding] of Object.entries(encode)) {
11
14
  if (encoding !== 'base64' && encoding !== 'base64url')
@@ -23,34 +26,39 @@ function asObject(body) {
23
26
  return null;
24
27
  }
25
28
  }
26
- if (typeof body !== 'object' || body === null || Array.isArray(body))
29
+ if (!isRecord(body) || Array.isArray(body)) {
27
30
  return null;
31
+ }
28
32
  return body;
29
33
  }
30
34
  function setAt(body, path, encoding) {
31
35
  const [head, ...rest] = path;
32
- if (head === undefined)
36
+ if (head === undefined) {
33
37
  return body;
38
+ }
34
39
  const current = body[head];
35
40
  if (rest.length) {
36
- if (typeof current !== 'object' || current === null)
41
+ if (!isRecord(current)) {
37
42
  return body;
43
+ }
38
44
  return { ...body, [head]: setAt(current, rest, encoding) };
39
45
  }
40
- if (typeof current !== 'string')
46
+ if (typeof current !== 'string') {
41
47
  return body;
48
+ }
42
49
  return { ...body, [head]: encodeValue(current, encoding) };
43
50
  }
44
51
  function encodeValue(value, encoding) {
45
52
  if (isAlreadyEncoded(value))
46
53
  return value;
47
- return Buffer.from(value, 'utf8').toString(encoding);
54
+ return b4a.toString(b4a.from(value, 'utf8'), encoding);
48
55
  }
49
56
  function isAlreadyEncoded(value) {
50
- if (!BASE64URL.test(value) && !/^[A-Za-z0-9+/]+={0,2}$/.test(value))
57
+ if (!BASE64URL.test(value) && !/^[A-Za-z0-9+/]+={0,2}$/.test(value)) {
51
58
  return false;
59
+ }
52
60
  try {
53
- const decoded = Buffer.from(value, 'base64').toString('utf8');
61
+ const decoded = b4a.toString(b4a.from(value, 'base64'), 'utf8');
54
62
  return decoded.length > 0 && isMostlyPrintable(decoded);
55
63
  }
56
64
  catch {
@@ -61,8 +69,9 @@ function isMostlyPrintable(text) {
61
69
  const chars = [...text];
62
70
  const printable = chars.filter((char) => {
63
71
  const code = char.codePointAt(0) ?? 0;
64
- if (code === 0xfffd)
72
+ if (code === 0xfffd) {
65
73
  return false;
74
+ }
66
75
  return code === 9 || code === 10 || code === 13 || (code >= 32 && code !== 127);
67
76
  }).length;
68
77
  return printable / chars.length >= PRINTABLE_RATIO;
@@ -1,3 +1,4 @@
1
+ import { isRecord } from '../../guards.js';
1
2
  const BLOCKED_HEADERS = new Set(['authorization', 'cookie', 'proxy-authorization']);
2
3
  export const MAX_RESPONSE_CHARS = 8192;
3
4
  export const TIMEOUT_MS = 10_000;
@@ -5,8 +6,9 @@ export function isRedirectStatus(status) {
5
6
  return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
6
7
  }
7
8
  export function parseObjectArg(v, name) {
8
- if (v === null || v === undefined)
9
+ if (v === null || v === undefined) {
9
10
  return { ok: true, value: undefined };
11
+ }
10
12
  let value = v;
11
13
  if (typeof v === 'string') {
12
14
  try {
@@ -16,20 +18,23 @@ export function parseObjectArg(v, name) {
16
18
  return { ok: false, error: `${name} must be an object` };
17
19
  }
18
20
  }
19
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
21
+ if (!isPlainRecord(value)) {
20
22
  return { ok: false, error: `${name} must be an object` };
21
23
  }
22
- return { ok: true, value: value };
24
+ return { ok: true, value };
23
25
  }
24
26
  const BODY_JSON_ERROR = 'body is not valid JSON — resend it as one complete JSON object';
25
27
  export function parseJsonBody(body, contentType) {
26
- if (typeof body !== 'string')
28
+ if (typeof body !== 'string') {
27
29
  return { ok: true, value: body };
28
- if (contentType !== undefined && !/json/i.test(contentType))
30
+ }
31
+ if (contentType !== undefined && !/json/i.test(contentType)) {
29
32
  return { ok: true, value: body };
33
+ }
30
34
  const text = body.trim();
31
- if (text[0] !== '{' && text[0] !== '[')
35
+ if (text[0] !== '{' && text[0] !== '[') {
32
36
  return { ok: true, value: body };
37
+ }
33
38
  let parsed;
34
39
  try {
35
40
  parsed = JSON.parse(text);
@@ -40,15 +45,16 @@ export function parseJsonBody(body, contentType) {
40
45
  return { ok: true, value: isPlainRecord(parsed) ? parsed : body };
41
46
  }
42
47
  function isPlainRecord(value) {
43
- return typeof value === 'object' && value !== null && !Array.isArray(value);
48
+ return isRecord(value) && !Array.isArray(value);
44
49
  }
45
50
  export function applyQuery(url, query) {
46
51
  for (const [key, value] of Object.entries(query)) {
47
52
  if (value === null || value === undefined)
48
53
  continue;
49
54
  if (Array.isArray(value)) {
50
- for (const item of value)
55
+ for (const item of value) {
51
56
  url.searchParams.append(key, String(item));
57
+ }
52
58
  }
53
59
  else {
54
60
  url.searchParams.append(key, String(value));
@@ -59,24 +65,27 @@ export function contentTypeOf(headers) {
59
65
  if (typeof headers !== 'object' || headers === null)
60
66
  return undefined;
61
67
  for (const [key, value] of Object.entries(headers)) {
62
- if (key.toLowerCase() === 'content-type')
68
+ if (key.toLowerCase() === 'content-type') {
63
69
  return String(value);
70
+ }
64
71
  }
65
72
  return undefined;
66
73
  }
67
74
  export function buildHeaders(headerArgs) {
68
75
  const headers = {};
69
76
  for (const [key, value] of Object.entries(headerArgs)) {
70
- if (!BLOCKED_HEADERS.has(key.toLowerCase()))
77
+ if (!BLOCKED_HEADERS.has(key.toLowerCase())) {
71
78
  headers[key] = String(value);
79
+ }
72
80
  }
73
81
  return headers;
74
82
  }
75
83
  export function encodeBody(body, headers) {
76
84
  if (body === null || body === undefined)
77
85
  return undefined;
78
- if (typeof body !== 'object')
86
+ if (typeof body !== 'object') {
79
87
  return String(body);
88
+ }
80
89
  if (!Object.keys(headers).some((h) => h.toLowerCase() === 'content-type')) {
81
90
  headers['Content-Type'] = 'application/json';
82
91
  }