@hraness/sponge 0.2.5 → 0.3.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 (227) hide show
  1. package/README.md +23 -10
  2. package/bin/sponge.js +8 -7
  3. package/dist/contracts/index.js +24 -6
  4. package/dist/contracts/validate.js +106 -29
  5. package/dist/migrations/008.d.ts +3 -0
  6. package/dist/migrations/008.js +5 -0
  7. package/dist/migrations/009.d.ts +3 -0
  8. package/dist/migrations/009.js +8 -0
  9. package/dist/migrations/010.d.ts +3 -0
  10. package/dist/migrations/010.js +11 -0
  11. package/dist/migrations/index.d.ts +15 -0
  12. package/dist/migrations/index.js +39 -0
  13. package/dist/src/acquisition/capture.js +87 -28
  14. package/dist/src/acquisition/cleanup.d.ts +0 -4
  15. package/dist/src/acquisition/cleanup.js +3 -5
  16. package/dist/src/acquisition/errors.js +17 -2
  17. package/dist/src/acquisition/files.d.ts +0 -1
  18. package/dist/src/acquisition/files.js +14 -11
  19. package/dist/src/acquisition/index.d.ts +2 -3
  20. package/dist/src/acquisition/index.js +1 -2
  21. package/dist/src/acquisition/lock.js +1 -1
  22. package/dist/src/acquisition/normalize.js +57 -18
  23. package/dist/src/acquisition/pins.js +22 -4
  24. package/dist/src/acquisition/process.js +67 -29
  25. package/dist/src/acquisition/runtime.d.ts +5 -7
  26. package/dist/src/acquisition/runtime.js +129 -68
  27. package/dist/src/acquisition/verify-helper.js +10 -3
  28. package/dist/src/annotations/quote.js +40 -12
  29. package/dist/src/annotations/store.d.ts +0 -1
  30. package/dist/src/annotations/store.js +66 -14
  31. package/dist/src/app/article.d.ts +2 -1
  32. package/dist/src/app/article.js +66 -70
  33. package/dist/src/app/bundle-files.d.ts +0 -4
  34. package/dist/src/app/bundle-files.js +11 -14
  35. package/dist/src/app/markdown.js +18 -6
  36. package/dist/src/app/source.d.ts +0 -4
  37. package/dist/src/app/source.js +18 -14
  38. package/dist/src/cli/arguments.d.ts +2 -8
  39. package/dist/src/cli/arguments.js +67 -80
  40. package/dist/src/cli/commands.d.ts +42 -0
  41. package/dist/src/cli/commands.js +795 -0
  42. package/dist/src/cli/config.js +12 -9
  43. package/dist/src/cli/digest.d.ts +1 -1
  44. package/dist/src/cli/digest.js +39 -17
  45. package/dist/src/cli/import.js +25 -4
  46. package/dist/src/cli/indexing.d.ts +2 -0
  47. package/dist/src/cli/indexing.js +26 -9
  48. package/dist/src/cli/main.js +47 -386
  49. package/dist/src/cli/portability.d.ts +0 -2
  50. package/dist/src/cli/portability.js +13 -8
  51. package/dist/src/cli/queue.js +12 -8
  52. package/dist/src/cli/runtimes.d.ts +1 -3
  53. package/dist/src/cli/runtimes.js +13 -6
  54. package/dist/src/cli/terminal.js +1 -1
  55. package/dist/src/cli/worker.d.ts +7 -0
  56. package/dist/src/cli/worker.js +54 -15
  57. package/dist/src/derivations/kinds.d.ts +34 -0
  58. package/dist/src/derivations/kinds.js +14 -0
  59. package/dist/src/derivations/types.d.ts +48 -0
  60. package/dist/src/derivations/types.js +1 -0
  61. package/dist/src/digest/derivation.d.ts +25 -0
  62. package/dist/src/digest/derivation.js +124 -0
  63. package/dist/src/digest/editorial.d.ts +10 -46
  64. package/dist/src/digest/editorial.js +73 -59
  65. package/dist/src/digest/endpoint.js +63 -27
  66. package/dist/src/digest/pipeline.d.ts +53 -32
  67. package/dist/src/digest/pipeline.js +357 -125
  68. package/dist/src/digest/schema.d.ts +44 -17
  69. package/dist/src/digest/schema.js +132 -44
  70. package/dist/src/errors.d.ts +46 -0
  71. package/dist/src/errors.js +117 -0
  72. package/dist/src/identity/query-policy.d.ts +0 -5
  73. package/dist/src/identity/query-policy.js +61 -9
  74. package/dist/src/identity/uri.d.ts +6 -5
  75. package/dist/src/identity/uri.js +37 -18
  76. package/dist/src/identity/visibility.d.ts +11 -1
  77. package/dist/src/identity/visibility.js +81 -17
  78. package/dist/src/importers/apply.js +104 -36
  79. package/dist/src/importers/custody.js +27 -9
  80. package/dist/src/importers/inspect.js +125 -38
  81. package/dist/src/importers/jungle-digest.d.ts +31 -24
  82. package/dist/src/importers/jungle-digest.js +217 -115
  83. package/dist/src/importers/jungle-store.d.ts +20 -11
  84. package/dist/src/importers/jungle-store.js +136 -41
  85. package/dist/src/importers/jungle.d.ts +32 -16
  86. package/dist/src/importers/jungle.js +145 -32
  87. package/dist/src/importers/manifest.js +57 -21
  88. package/dist/src/importers/verify-helper.js +18 -4
  89. package/dist/src/indexing/image.js +29 -10
  90. package/dist/src/indexing/index.d.ts +37 -6
  91. package/dist/src/indexing/index.js +360 -124
  92. package/dist/src/indexing/jobs.js +156 -47
  93. package/dist/src/indexing/prune-run.d.ts +2 -0
  94. package/dist/src/indexing/prune-run.js +69 -26
  95. package/dist/src/indexing/prune.d.ts +12 -3
  96. package/dist/src/indexing/prune.js +85 -35
  97. package/dist/src/indexing/runner.d.ts +2 -1
  98. package/dist/src/indexing/runner.js +83 -40
  99. package/dist/src/indexing/upgrade.d.ts +9 -0
  100. package/dist/src/indexing/upgrade.js +79 -0
  101. package/dist/src/inference/asr/bridge.js +6 -2
  102. package/dist/src/inference/asr/protocol.js +109 -19
  103. package/dist/src/inference/asr/runtime.js +58 -21
  104. package/dist/src/inference/asr/windows.js +9 -5
  105. package/dist/src/inference/embedding/index.d.ts +2 -6
  106. package/dist/src/inference/embedding/index.js +89 -29
  107. package/dist/src/ingest/attempt.d.ts +29 -0
  108. package/dist/src/ingest/attempt.js +55 -0
  109. package/dist/src/media/capture-helper.js +7 -2
  110. package/dist/src/media/capture.d.ts +2 -1
  111. package/dist/src/media/capture.js +124 -43
  112. package/dist/src/media/handler.d.ts +4 -2
  113. package/dist/src/media/handler.js +88 -70
  114. package/dist/src/media/transcribe.js +63 -21
  115. package/dist/src/metadata/article.js +49 -17
  116. package/dist/src/models/artifacts.d.ts +10 -8
  117. package/dist/src/models/artifacts.js +237 -106
  118. package/dist/src/models/catalog.d.ts +2 -5
  119. package/dist/src/models/catalog.js +94 -25
  120. package/dist/src/models/download.js +55 -15
  121. package/dist/src/models/runtime.d.ts +0 -2
  122. package/dist/src/models/runtime.js +69 -33
  123. package/dist/src/pdf/capture-helper.js +3 -1
  124. package/dist/src/pdf/capture.d.ts +2 -1
  125. package/dist/src/pdf/capture.js +95 -27
  126. package/dist/src/pdf/errors.js +12 -1
  127. package/dist/src/pdf/handler.d.ts +2 -1
  128. package/dist/src/pdf/handler.js +70 -71
  129. package/dist/src/pdf/index.d.ts +1 -3
  130. package/dist/src/pdf/index.js +0 -2
  131. package/dist/src/pdf/input.js +12 -7
  132. package/dist/src/pdf/normalize.js +80 -21
  133. package/dist/src/pdf/source-identity.js +3 -2
  134. package/dist/src/pdf/source.js +12 -8
  135. package/dist/src/pdf/verify-helper.js +15 -3
  136. package/dist/src/portability/backup.d.ts +2 -9
  137. package/dist/src/portability/backup.js +108 -22
  138. package/dist/src/portability/database.js +51 -17
  139. package/dist/src/portability/export.js +35 -9
  140. package/dist/src/portability/files.d.ts +1 -4
  141. package/dist/src/portability/files.js +127 -67
  142. package/dist/src/portability/gc.js +65 -22
  143. package/dist/src/publish/component.js +1 -1
  144. package/dist/src/publish/feed.d.ts +2 -2
  145. package/dist/src/publish/feed.js +75 -29
  146. package/dist/src/publish/identity.d.ts +5 -1
  147. package/dist/src/publish/identity.js +31 -11
  148. package/dist/src/publish/index.d.ts +2 -3
  149. package/dist/src/publish/index.js +34 -21
  150. package/dist/src/publish/projection.d.ts +2 -9
  151. package/dist/src/publish/projection.js +21 -14
  152. package/dist/src/search/extension.js +3 -1
  153. package/dist/src/search/hybrid.d.ts +3 -8
  154. package/dist/src/search/hybrid.js +45 -11
  155. package/dist/src/search/lexical.js +18 -4
  156. package/dist/src/search/query.js +56 -25
  157. package/dist/src/search/vector.js +37 -10
  158. package/dist/src/service/launch-agent.d.ts +5 -3
  159. package/dist/src/service/launch-agent.js +272 -83
  160. package/dist/src/service/lifetime.js +47 -14
  161. package/dist/src/service/loop.d.ts +3 -3
  162. package/dist/src/service/loop.js +47 -19
  163. package/dist/src/store/annotations.d.ts +32 -0
  164. package/dist/src/store/annotations.js +44 -0
  165. package/dist/src/store/backups.d.ts +18 -0
  166. package/dist/src/store/backups.js +110 -0
  167. package/dist/src/store/blobs.d.ts +24 -0
  168. package/dist/src/store/blobs.js +139 -0
  169. package/dist/src/store/changes.d.ts +5 -12
  170. package/dist/src/store/changes.js +108 -56
  171. package/dist/src/store/context.d.ts +15 -0
  172. package/dist/src/store/context.js +19 -0
  173. package/dist/src/store/derivations.d.ts +42 -0
  174. package/dist/src/store/derivations.js +104 -0
  175. package/dist/src/store/files.d.ts +0 -2
  176. package/dist/src/store/files.js +19 -24
  177. package/dist/src/store/ids.d.ts +30 -0
  178. package/dist/src/store/ids.js +34 -0
  179. package/dist/src/store/imports.d.ts +26 -0
  180. package/dist/src/store/imports.js +57 -0
  181. package/dist/src/store/index.d.ts +3 -186
  182. package/dist/src/store/index.js +2 -809
  183. package/dist/src/store/lease.d.ts +20 -0
  184. package/dist/src/store/lease.js +62 -0
  185. package/dist/src/store/library.d.ts +75 -0
  186. package/dist/src/store/library.js +174 -0
  187. package/dist/src/store/open.d.ts +7 -0
  188. package/dist/src/store/open.js +183 -0
  189. package/dist/src/store/queue.d.ts +65 -0
  190. package/dist/src/store/queue.js +327 -0
  191. package/dist/src/store/revisions.d.ts +12 -0
  192. package/dist/src/store/revisions.js +189 -0
  193. package/dist/src/store/search-index.d.ts +63 -0
  194. package/dist/src/store/search-index.js +190 -0
  195. package/dist/src/store/sync-ready.d.ts +6 -0
  196. package/dist/src/store/sync-ready.js +121 -0
  197. package/dist/src/store/types.d.ts +1 -5
  198. package/dist/src/store/types.js +1 -8
  199. package/dist/src/util/canonical.d.ts +2 -0
  200. package/dist/src/util/canonical.js +17 -0
  201. package/dist/src/util/exact.d.ts +2 -0
  202. package/dist/src/util/exact.js +9 -0
  203. package/dist/src/util/fsync.d.ts +4 -0
  204. package/dist/src/util/fsync.js +22 -0
  205. package/dist/src/util/hash.d.ts +4 -0
  206. package/dist/src/util/hash.js +8 -0
  207. package/dist/src/worker/basic.d.ts +0 -1
  208. package/dist/src/worker/basic.js +23 -7
  209. package/dist/src/worker/custody.d.ts +4 -1
  210. package/dist/src/worker/custody.js +111 -38
  211. package/dist/src/worker/engine.d.ts +2 -1
  212. package/dist/src/worker/engine.js +42 -20
  213. package/dist/src/worker/identity.js +13 -5
  214. package/dist/src/worker/journal.d.ts +1 -1
  215. package/dist/src/worker/journal.js +54 -24
  216. package/dist/src/worker/lock.d.ts +18 -0
  217. package/dist/src/worker/lock.js +196 -0
  218. package/dist/src/worker/runtime-gc.d.ts +4 -3
  219. package/dist/src/worker/runtime-gc.js +68 -41
  220. package/dist/src/worker/supervisor-child.js +19 -6
  221. package/dist/src/worker/supervisor.js +41 -8
  222. package/native/runtime-manifest.json +4 -4
  223. package/package.json +10 -2
  224. package/dist/src/digest/store.d.ts +0 -57
  225. package/dist/src/digest/store.js +0 -77
  226. package/dist/src/store/foundation.d.ts +0 -7
  227. package/dist/src/store/foundation.js +0 -7
@@ -4,35 +4,79 @@ import { fileURLToPath } from 'node:url';
4
4
  import { release } from 'node:os';
5
5
  import { asrIdentity } from '../inference/asr/protocol.js';
6
6
  import { embeddingIdentity } from '../inference/embedding/index.js';
7
- export const nativeAssetNames = { 'asr-helper': 'sponge-speech-macos26-arm64', 'embedding-helper': 'sponge-embedding-macos26-arm64', 'embedding-library': 'libCLiteRTLM_mac.dylib', notices: 'native-NOTICES.txt' };
8
- export class NativeCatalogError extends Error {
9
- code;
10
- constructor(code) {
11
- super(code);
12
- this.code = code;
13
- }
14
- }
15
- const fail = () => { throw new NativeCatalogError('integrity'); };
16
- function object(value, keys) { if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).sort().join(',') !== keys.sort().join(','))
17
- fail(); return value; }
7
+ import { exactKeys } from '../util/exact.js';
8
+ import { SpongeError } from '../errors.js';
9
+ export const nativeAssetNames = {
10
+ 'asr-helper': 'sponge-speech-macos26-arm64',
11
+ 'embedding-helper': 'sponge-embedding-macos26-arm64',
12
+ 'embedding-library': 'libCLiteRTLM_mac.dylib',
13
+ notices: 'native-NOTICES.txt',
14
+ };
15
+ const fail = () => {
16
+ throw new SpongeError('integrity');
17
+ };
18
+ const object = (value, keys) => exactKeys(value, keys, fail);
18
19
  const sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/u.test(v);
19
- function version(v) { return typeof v === 'string' && /^(0|[1-9]\d{0,15})\.(0|[1-9]\d{0,15})\.(0|[1-9]\d{0,15})$/u.test(v) && v.split('.').every(n => Number.isSafeInteger(Number(n))); }
20
+ function version(v) {
21
+ return (typeof v === 'string' &&
22
+ /^(0|[1-9]\d{0,15})\.(0|[1-9]\d{0,15})\.(0|[1-9]\d{0,15})$/u.test(v) &&
23
+ v.split('.').every((n) => Number.isSafeInteger(Number(n))));
24
+ }
20
25
  /** Package-embedded release hashes are authority; local probe helper hashes never enter this catalog. */
21
26
  export function parseNativeCatalog(value, expectedVersion) {
22
- const r = object(value, ['schemaVersion', 'packageVersion', 'sourceCommit', 'platform', 'architecture', 'minimumMacOS', 'artifacts', 'build']);
23
- if (r.schemaVersion !== 1 || !version(expectedVersion) || r.packageVersion !== expectedVersion || typeof r.sourceCommit !== 'string' || !/^[a-f0-9]{40}$/u.test(r.sourceCommit) || r.platform !== 'darwin' || r.architecture !== 'arm64' || r.minimumMacOS !== '26.0' || !Array.isArray(r.artifacts) || r.artifacts.length !== 4)
27
+ const r = object(value, [
28
+ 'schemaVersion',
29
+ 'packageVersion',
30
+ 'sourceCommit',
31
+ 'platform',
32
+ 'architecture',
33
+ 'minimumMacOS',
34
+ 'artifacts',
35
+ 'build',
36
+ ]);
37
+ if (r.schemaVersion !== 1 ||
38
+ !version(expectedVersion) ||
39
+ r.packageVersion !== expectedVersion ||
40
+ typeof r.sourceCommit !== 'string' ||
41
+ !/^[a-f0-9]{40}$/u.test(r.sourceCommit) ||
42
+ r.platform !== 'darwin' ||
43
+ r.architecture !== 'arm64' ||
44
+ r.minimumMacOS !== '26.0' ||
45
+ !Array.isArray(r.artifacts) ||
46
+ r.artifacts.length !== 4)
24
47
  fail();
25
48
  const roles = new Set();
26
- const artifacts = r.artifacts.map((value) => { const a = object(value, ['role', 'name', 'bytes', 'sha256']); if (typeof a.role !== 'string' || !Object.hasOwn(nativeAssetNames, a.role) || roles.has(a.role) || a.name !== nativeAssetNames[a.role] || typeof a.bytes !== 'number' || !Number.isSafeInteger(a.bytes) || a.bytes < 1 || a.bytes > (a.role === 'embedding-library' ? 160 * 1024 ** 2 : a.role === 'notices' ? 1024 ** 2 : 32 * 1024 ** 2) || !sha(a.sha256))
27
- fail(); roles.add(a.role); if (a.role === 'embedding-library' && a.sha256 !== embeddingIdentity.librarySha256)
28
- fail(); return a; });
49
+ const artifacts = r.artifacts.map((value) => {
50
+ const a = object(value, ['role', 'name', 'bytes', 'sha256']);
51
+ if (typeof a.role !== 'string' ||
52
+ !Object.hasOwn(nativeAssetNames, a.role) ||
53
+ roles.has(a.role) ||
54
+ a.name !== nativeAssetNames[a.role] ||
55
+ typeof a.bytes !== 'number' ||
56
+ !Number.isSafeInteger(a.bytes) ||
57
+ a.bytes < 1 ||
58
+ a.bytes >
59
+ (a.role === 'embedding-library' ? 160 * 1024 ** 2 : a.role === 'notices' ? 1024 ** 2 : 32 * 1024 ** 2) ||
60
+ !sha(a.sha256))
61
+ fail();
62
+ roles.add(a.role);
63
+ if (a.role === 'embedding-library' && a.sha256 !== embeddingIdentity.librarySha256)
64
+ fail();
65
+ return a;
66
+ });
29
67
  const build = object(r.build, ['asrSourceSha256', 'embeddingSourceSha256', 'swiftVersion', 'clangVersion']);
30
- if (!sha(build.asrSourceSha256) || !sha(build.embeddingSourceSha256) || ['swiftVersion', 'clangVersion'].some(k => typeof build[k] !== 'string' || !build[k] || Buffer.byteLength(String(build[k])) > 2048))
68
+ if (!sha(build.asrSourceSha256) ||
69
+ !sha(build.embeddingSourceSha256) ||
70
+ ['swiftVersion', 'clangVersion'].some((k) => typeof build[k] !== 'string' || !build[k] || Buffer.byteLength(String(build[k])) > 2048))
31
71
  fail();
32
72
  return { ...r, artifacts, build };
33
73
  }
34
- export function nativePlatformSupported(platform = process.platform, architecture = process.arch, kernel = release()) { return platform === 'darwin' && architecture === 'arm64' && /^\d+\./u.test(kernel) && Number(kernel.split('.')[0]) >= 25; }
35
- export function nativeCatalogPath() { return fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? '../../native/runtime-manifest.json' : '../../../native/runtime-manifest.json', import.meta.url)); }
74
+ export function nativePlatformSupported(platform = process.platform, architecture = process.arch, kernel = release()) {
75
+ return (platform === 'darwin' && architecture === 'arm64' && /^\d+\./u.test(kernel) && Number(kernel.split('.')[0]) >= 25);
76
+ }
77
+ export function nativeCatalogPath() {
78
+ return fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? '../../native/runtime-manifest.json' : '../../../native/runtime-manifest.json', import.meta.url));
79
+ }
36
80
  export async function loadNativeCatalog(expectedVersion, path = nativeCatalogPath()) {
37
81
  let file;
38
82
  try {
@@ -40,8 +84,8 @@ export async function loadNativeCatalog(expectedVersion, path = nativeCatalogPat
40
84
  }
41
85
  catch (error) {
42
86
  if (error.code === 'ENOENT')
43
- throw new NativeCatalogError('runtime-missing');
44
- throw new NativeCatalogError('integrity');
87
+ throw new SpongeError('runtime-missing');
88
+ throw new SpongeError('integrity');
45
89
  }
46
90
  try {
47
91
  const stat = await file.stat();
@@ -65,7 +109,32 @@ export function nativeModelPackage(catalog, kind) {
65
109
  if (kind !== 'asr' && kind !== 'embedding')
66
110
  fail();
67
111
  const roles = kind === 'asr' ? ['asr-helper', 'notices'] : ['embedding-helper', 'embedding-library', 'notices'];
68
- const artifacts = roles.map(role => { const a = catalog.artifacts.find(a => a.role === role); return { name: role === 'asr-helper' ? 'sponge-speech' : role === 'embedding-helper' ? 'sponge-embedding' : a.name, url: `https://github.com/hraness/sponge/releases/download/v${catalog.packageVersion}/${a.name}`, sha256: a.sha256, bytes: a.bytes, ...(role.endsWith('helper') ? { executable: true } : {}) }; });
69
- artifacts.push(kind === 'asr' ? { name: 'whistle.cact', url: 'https://huggingface.co/Cactus-Compute/whistle/resolve/ca5287601bef25af26dcf2e1b2bdc0843a7c19e5/whistle.cact', sha256: asrIdentity.modelSha256, bytes: asrIdentity.modelBytes } : { name: 'embeddinggemma-2-text-vision-440m.litertlm', url: 'https://huggingface.co/litert-community/embeddinggemma-2-text-vision-440m-litert-lm/resolve/e301f74d5551b0c2641bd5cb4652a76239d5c5f8/embeddinggemma-2-text-vision-440m.litertlm', sha256: embeddingIdentity.modelSha256, bytes: 387710976 });
70
- return { id: `sponge-${kind}-${catalog.packageVersion}-macos26-arm64`, license: 'Sponge helpers: proprietary; Needle, Whistle, LiteRT-LM and model: Apache-2.0; see native-NOTICES.txt', artifacts };
112
+ const artifacts = roles.map((role) => {
113
+ const a = catalog.artifacts.find((a) => a.role === role);
114
+ return {
115
+ name: role === 'asr-helper' ? 'sponge-speech' : role === 'embedding-helper' ? 'sponge-embedding' : a.name,
116
+ url: `https://github.com/hraness/sponge/releases/download/v${catalog.packageVersion}/${a.name}`,
117
+ sha256: a.sha256,
118
+ bytes: a.bytes,
119
+ ...(role.endsWith('helper') ? { executable: true } : {}),
120
+ };
121
+ });
122
+ artifacts.push(kind === 'asr'
123
+ ? {
124
+ name: 'whistle.cact',
125
+ url: 'https://huggingface.co/Cactus-Compute/whistle/resolve/ca5287601bef25af26dcf2e1b2bdc0843a7c19e5/whistle.cact',
126
+ sha256: asrIdentity.modelSha256,
127
+ bytes: asrIdentity.modelBytes,
128
+ }
129
+ : {
130
+ name: 'embeddinggemma-2-text-vision-440m.litertlm',
131
+ url: 'https://huggingface.co/litert-community/embeddinggemma-2-text-vision-440m-litert-lm/resolve/e301f74d5551b0c2641bd5cb4652a76239d5c5f8/embeddinggemma-2-text-vision-440m.litertlm',
132
+ sha256: embeddingIdentity.modelSha256,
133
+ bytes: 387710976,
134
+ });
135
+ return {
136
+ id: `sponge-${kind}-${catalog.packageVersion}-macos26-arm64`,
137
+ license: 'Sponge helpers: proprietary; Needle, Whistle, LiteRT-LM and model: Apache-2.0; see native-NOTICES.txt',
138
+ artifacts,
139
+ };
71
140
  }
@@ -1,14 +1,22 @@
1
- import { ModelSetupError } from './artifacts.js';
1
+ import { SpongeError } from '../errors.js';
2
2
  const api = 'https://api.github.com/repos/hraness/sponge';
3
3
  const nativeUrl = /^https:\/\/github\.com\/hraness\/sponge\/releases\/download\/(v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*))\/([A-Za-z0-9_.-]+)$/;
4
- const cdnHosts = new Set(['release-assets.githubusercontent.com', 'objects.githubusercontent.com', 'github-releases.githubusercontent.com']);
4
+ const cdnHosts = new Set([
5
+ 'release-assets.githubusercontent.com',
6
+ 'objects.githubusercontent.com',
7
+ 'github-releases.githubusercontent.com',
8
+ ]);
5
9
  const redirectCodes = new Set([301, 302, 303, 307, 308]);
6
10
  export const nativeDownloadAuthGuidance = 'Set GH_TOKEN or GITHUB_TOKEN with Contents: read access to hraness/sponge, then retry setup. Credentials are never saved.';
7
- function fail(code) { throw new ModelSetupError(code); }
8
- async function discard(response) { try {
9
- await response.body?.cancel();
11
+ function fail(code) {
12
+ throw new SpongeError(code);
13
+ }
14
+ async function discard(response) {
15
+ try {
16
+ await response.body?.cancel();
17
+ }
18
+ catch { }
10
19
  }
11
- catch { } }
12
20
  async function boundedJson(response) {
13
21
  if (!response.body)
14
22
  fail('integrity');
@@ -27,7 +35,7 @@ async function boundedJson(response) {
27
35
  return value;
28
36
  }
29
37
  catch (error) {
30
- if (error instanceof ModelSetupError)
38
+ if (error instanceof SpongeError)
31
39
  throw error;
32
40
  fail('integrity');
33
41
  }
@@ -43,7 +51,7 @@ function credential(env) {
43
51
  * Manual CDN redirects always discard auth; returned bytes still pass package hashes.
44
52
  * Public model downloads never read credentials or receive authorization headers. */
45
53
  export function nativeArtifactFetch(manifest, options = {}) {
46
- const transport = options.fetch ?? globalThis.fetch, env = options.env ?? process.env, artifacts = new Map(manifest.artifacts.map(a => [a.url, a]));
54
+ const transport = options.fetch ?? globalThis.fetch, env = options.env ?? process.env, artifacts = new Map(manifest.artifacts.map((a) => [a.url, a]));
47
55
  const request = async (url, signal, headers) => {
48
56
  if (signal?.aborted)
49
57
  fail('cancelled');
@@ -57,7 +65,11 @@ export function nativeArtifactFetch(manifest, options = {}) {
57
65
  }
58
66
  };
59
67
  const download = async (artifact, tag, name, signal) => {
60
- const token = credential(env), auth = { Authorization: `Bearer ${token}`, 'X-GitHub-Api-Version': '2022-11-28', Accept: 'application/vnd.github+json' };
68
+ const token = credential(env), auth = {
69
+ Authorization: `Bearer ${token}`,
70
+ 'X-GitHub-Api-Version': '2022-11-28',
71
+ Accept: 'application/vnd.github+json',
72
+ };
61
73
  const metadataResponse = await request(`${api}/releases/tags/${tag}`, signal, auth);
62
74
  if (!metadataResponse.ok) {
63
75
  await discard(metadataResponse);
@@ -66,13 +78,28 @@ export function nativeArtifactFetch(manifest, options = {}) {
66
78
  fail('unavailable');
67
79
  }
68
80
  const release = await boundedJson(metadataResponse);
69
- if (release.tag_name !== tag || release.immutable !== true || release.draft !== false || release.prerelease !== false || !Number.isSafeInteger(release.id) || Number(release.id) <= 0 || release.url !== `${api}/releases/${release.id}` || release.html_url !== `https://github.com/hraness/sponge/releases/tag/${tag}` || !Array.isArray(release.assets) || release.assets.length > 1000)
81
+ if (release.tag_name !== tag ||
82
+ release.immutable !== true ||
83
+ release.draft !== false ||
84
+ release.prerelease !== false ||
85
+ !Number.isSafeInteger(release.id) ||
86
+ Number(release.id) <= 0 ||
87
+ release.url !== `${api}/releases/${release.id}` ||
88
+ release.html_url !== `https://github.com/hraness/sponge/releases/tag/${tag}` ||
89
+ !Array.isArray(release.assets) ||
90
+ release.assets.length > 1000)
70
91
  fail('integrity');
71
- const matches = release.assets.filter(a => a && typeof a === 'object' && a.name === name);
92
+ const matches = release.assets.filter((a) => a && typeof a === 'object' && a.name === name);
72
93
  if (matches.length !== 1)
73
94
  fail('integrity');
74
95
  const asset = matches[0];
75
- if (!Number.isSafeInteger(asset.id) || asset.id <= 0 || asset.state !== 'uploaded' || asset.size !== artifact.bytes || asset.digest !== `sha256:${artifact.sha256}` || asset.url !== `${api}/releases/assets/${asset.id}` || asset.browser_download_url !== artifact.url)
96
+ if (!Number.isSafeInteger(asset.id) ||
97
+ asset.id <= 0 ||
98
+ asset.state !== 'uploaded' ||
99
+ asset.size !== artifact.bytes ||
100
+ asset.digest !== `sha256:${artifact.sha256}` ||
101
+ asset.url !== `${api}/releases/assets/${asset.id}` ||
102
+ asset.browser_download_url !== artifact.url)
76
103
  fail('integrity');
77
104
  let response = await request(asset.url, signal, { ...auth, Accept: 'application/octet-stream' });
78
105
  for (let redirects = 0; redirectCodes.has(response.status); redirects++) {
@@ -87,7 +114,12 @@ export function nativeArtifactFetch(manifest, options = {}) {
87
114
  catch {
88
115
  fail('integrity');
89
116
  }
90
- if (next.protocol !== 'https:' || next.username || next.password || next.port || next.hash || !cdnHosts.has(next.hostname))
117
+ if (next.protocol !== 'https:' ||
118
+ next.username ||
119
+ next.password ||
120
+ next.port ||
121
+ next.hash ||
122
+ !cdnHosts.has(next.hostname))
91
123
  fail('integrity');
92
124
  response = await request(next.href, signal); // No credential or GitHub API headers cross this boundary.
93
125
  }
@@ -101,7 +133,7 @@ export function nativeArtifactFetch(manifest, options = {}) {
101
133
  };
102
134
  return (async (input, init) => {
103
135
  const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url, artifact = artifacts.get(url), signal = init?.signal ?? undefined;
104
- if (!artifact || init?.headers || init?.body || init?.method && init.method !== 'GET')
136
+ if (!artifact || init?.headers || init?.body || (init?.method && init.method !== 'GET'))
105
137
  fail('integrity');
106
138
  if (signal?.aborted)
107
139
  fail('cancelled');
@@ -133,7 +165,15 @@ export function nativeArtifactFetch(manifest, options = {}) {
133
165
  catch {
134
166
  fail('integrity');
135
167
  }
136
- if (next.protocol !== 'https:' || next.username || next.password || next.port || next.hash || !(next.hostname === 'huggingface.co' || next.hostname.endsWith('.huggingface.co') || next.hostname === 'hf.co' || next.hostname.endsWith('.hf.co')))
168
+ if (next.protocol !== 'https:' ||
169
+ next.username ||
170
+ next.password ||
171
+ next.port ||
172
+ next.hash ||
173
+ !(next.hostname === 'huggingface.co' ||
174
+ next.hostname.endsWith('.huggingface.co') ||
175
+ next.hostname === 'hf.co' ||
176
+ next.hostname.endsWith('.hf.co')))
137
177
  fail('integrity');
138
178
  currentUrl = next.href;
139
179
  response = await request(currentUrl, signal);
@@ -14,8 +14,6 @@ export declare function nativeSetupPlan(kind: NativeKind, version: string): Prom
14
14
  export declare function setupNativeRuntime(cacheRoot: string, catalog: NativeCatalog, kind: NativeKind, options?: {
15
15
  signal?: AbortSignal;
16
16
  }): Promise<AsrRuntime | EmbeddingRuntime>;
17
- export declare function inspectAsrRuntime(cacheRoot: string, catalog: NativeCatalog): Promise<AsrRuntime>;
18
- export declare function inspectEmbeddingRuntime(cacheRoot: string, catalog: NativeCatalog): Promise<EmbeddingRuntime>;
19
17
  export declare function provisionNative(kind: 'asr', cacheRoot: string, version: string, signal?: AbortSignal): Promise<AsrRuntime>;
20
18
  export declare function provisionNative(kind: 'embedding', cacheRoot: string, version: string, signal?: AbortSignal): Promise<EmbeddingRuntime>;
21
19
  export declare function provisionNative(kind: NativeKind, cacheRoot: string, version: string, signal?: AbortSignal): Promise<AsrRuntime | EmbeddingRuntime>;
@@ -2,36 +2,72 @@ import { mkdir } from 'node:fs/promises';
2
2
  import { dirname } from 'node:path';
3
3
  import { privateRoot } from '../acquisition/files.js';
4
4
  import { nativeArtifactFetch } from './download.js';
5
- import { inspectModelPackage, provisionModelPackage, recoverModelPackage, modelSetupPlan, ModelSetupError } from './artifacts.js';
6
- import { nativePlatformSupported, nativeModelPackage, NativeCatalogError, loadNativeCatalog } from './catalog.js';
7
- export async function nativeSetupPlan(kind, version) { const catalog = await loadNativeCatalog(version); return { ...modelSetupPlan(nativeModelPackage(catalog, kind)), supported: nativePlatformSupported(), minimumMacOS: '26.0' }; }
8
- function supported() { if (!nativePlatformSupported())
9
- throw new NativeCatalogError('unsupported'); }
10
- function asr(c, p) { return { helper: p['sponge-speech'], helperSha256: c.artifacts.find(a => a.role === 'asr-helper').sha256, model: p['whistle.cact'] }; }
11
- function embedding(c, p) { return { helper: p['sponge-embedding'], helperSha256: c.artifacts.find(a => a.role === 'embedding-helper').sha256, library: p['libCLiteRTLM_mac.dylib'], model: p['embeddinggemma-2-text-vision-440m.litertlm'] }; }
12
- export async function setupNativeRuntime(cacheRoot, catalog, kind, options = {}) { supported(); if (options.signal?.aborted)
13
- throw new ModelSetupError('cancelled'); const manifest = nativeModelPackage(catalog, kind); await privateRoot(dirname(cacheRoot)); try {
14
- await mkdir(cacheRoot, { mode: 0o700 });
15
- }
16
- catch (error) {
17
- if (error.code !== 'EEXIST')
18
- throw error;
19
- } await privateRoot(cacheRoot); await recoverModelPackage(cacheRoot, manifest); const paths = await provisionModelPackage(cacheRoot, manifest, { ...options, fetch: nativeArtifactFetch(manifest) }); return kind === 'asr' ? asr(catalog, paths) : embedding(catalog, paths); }
20
- export async function inspectAsrRuntime(cacheRoot, catalog) { supported(); try {
21
- return asr(catalog, await inspectModelPackage(cacheRoot, nativeModelPackage(catalog, 'asr')));
22
- }
23
- catch (error) {
24
- if (error.code === 'ENOENT')
25
- throw new NativeCatalogError('runtime-missing');
26
- throw error;
27
- } }
28
- export async function inspectEmbeddingRuntime(cacheRoot, catalog) { supported(); try {
29
- return embedding(catalog, await inspectModelPackage(cacheRoot, nativeModelPackage(catalog, 'embedding')));
30
- }
31
- catch (error) {
32
- if (error.code === 'ENOENT')
33
- throw new NativeCatalogError('runtime-missing');
34
- throw error;
35
- } }
36
- export async function provisionNative(kind, cacheRoot, version, signal) { return setupNativeRuntime(cacheRoot, await loadNativeCatalog(version), kind, { signal }); }
37
- export async function inspectNative(kind, cacheRoot, version) { const catalog = await loadNativeCatalog(version); return kind === 'asr' ? inspectAsrRuntime(cacheRoot, catalog) : inspectEmbeddingRuntime(cacheRoot, catalog); }
5
+ import { inspectInstalledModelPackage, provisionModelPackage, recoverModelPackage, modelSetupPlan, } from './artifacts.js';
6
+ import { nativePlatformSupported, nativeModelPackage, loadNativeCatalog, } from './catalog.js';
7
+ import { SpongeError } from '../errors.js';
8
+ export async function nativeSetupPlan(kind, version) {
9
+ const catalog = await loadNativeCatalog(version);
10
+ return {
11
+ ...modelSetupPlan(nativeModelPackage(catalog, kind)),
12
+ supported: nativePlatformSupported(),
13
+ minimumMacOS: '26.0',
14
+ };
15
+ }
16
+ function supported() {
17
+ if (!nativePlatformSupported())
18
+ throw new SpongeError('unsupported');
19
+ }
20
+ function asr(c, p) {
21
+ return {
22
+ helper: p['sponge-speech'],
23
+ helperSha256: c.artifacts.find((a) => a.role === 'asr-helper').sha256,
24
+ model: p['whistle.cact'],
25
+ };
26
+ }
27
+ function embedding(c, p) {
28
+ return {
29
+ helper: p['sponge-embedding'],
30
+ helperSha256: c.artifacts.find((a) => a.role === 'embedding-helper').sha256,
31
+ library: p['libCLiteRTLM_mac.dylib'],
32
+ model: p['embeddinggemma-2-text-vision-440m.litertlm'],
33
+ };
34
+ }
35
+ export async function setupNativeRuntime(cacheRoot, catalog, kind, options = {}) {
36
+ supported();
37
+ if (options.signal?.aborted)
38
+ throw new SpongeError('cancelled');
39
+ const manifest = nativeModelPackage(catalog, kind);
40
+ await privateRoot(dirname(cacheRoot));
41
+ try {
42
+ await mkdir(cacheRoot, { mode: 0o700 });
43
+ }
44
+ catch (error) {
45
+ if (error.code !== 'EEXIST')
46
+ throw error;
47
+ }
48
+ await privateRoot(cacheRoot);
49
+ await recoverModelPackage(cacheRoot, manifest);
50
+ const paths = await provisionModelPackage(cacheRoot, manifest, { ...options, fetch: nativeArtifactFetch(manifest) });
51
+ return kind === 'asr' ? asr(catalog, paths) : embedding(catalog, paths);
52
+ }
53
+ async function installed(cacheRoot, catalog, kind) {
54
+ const paths = await inspectInstalledModelPackage(cacheRoot, nativeModelPackage(catalog, kind));
55
+ if (!paths)
56
+ throw new SpongeError('runtime-missing');
57
+ return paths;
58
+ }
59
+ async function inspectAsrRuntime(cacheRoot, catalog) {
60
+ supported();
61
+ return asr(catalog, await installed(cacheRoot, catalog, 'asr'));
62
+ }
63
+ async function inspectEmbeddingRuntime(cacheRoot, catalog) {
64
+ supported();
65
+ return embedding(catalog, await installed(cacheRoot, catalog, 'embedding'));
66
+ }
67
+ export async function provisionNative(kind, cacheRoot, version, signal) {
68
+ return setupNativeRuntime(cacheRoot, await loadNativeCatalog(version), kind, { signal });
69
+ }
70
+ export async function inspectNative(kind, cacheRoot, version) {
71
+ const catalog = await loadNativeCatalog(version);
72
+ return kind === 'asr' ? inspectAsrRuntime(cacheRoot, catalog) : inspectEmbeddingRuntime(cacheRoot, catalog);
73
+ }
@@ -3,7 +3,9 @@ const runtime = process.argv[2], args = process.argv.slice(3);
3
3
  try {
4
4
  const { runPdfAcquisition } = await import(import.meta.resolve('@hraness/ghostget/pdf-acquisition', `${runtime}/package.json`));
5
5
  const { runPdfToolCommand } = await import(import.meta.resolve('@hraness/wordcell/pdf', `${runtime}/package.json`));
6
- process.exitCode = await runPdfAcquisition(args, process.env, { stdout: (value) => process.stdout.write(value), stderr: (value) => process.stderr.write(value) }, { runTool: (specification) => runPdfToolCommand({ ...specification, processGroup: 'inherit' }) });
6
+ process.exitCode = await runPdfAcquisition(args, process.env, { stdout: (value) => process.stdout.write(value), stderr: (value) => process.stderr.write(value) }, {
7
+ runTool: (specification) => runPdfToolCommand({ ...specification, processGroup: 'inherit' }),
8
+ });
7
9
  }
8
10
  catch {
9
11
  process.stdout.write(JSON.stringify({ ok: false, code: 'unknown' }));
@@ -1,7 +1,7 @@
1
1
  import type { CaptureResult, ErrorEnvelope } from '../../contracts/index.js';
2
2
  import { runOwned } from '../acquisition/process.js';
3
3
  import { type CaptureRuntime } from '../acquisition/runtime.js';
4
- export type CaptureConnection = {
4
+ type CaptureConnection = {
5
5
  kind: 'account';
6
6
  stateHome: string;
7
7
  accountId: string;
@@ -27,3 +27,4 @@ export type CaptureOutcome = {
27
27
  dispose: () => Promise<void>;
28
28
  } | ErrorEnvelope;
29
29
  export declare function capturePdf(request: PdfCaptureRequest): Promise<CaptureOutcome>;
30
+ export {};
@@ -4,13 +4,14 @@ import { fileURLToPath } from 'node:url';
4
4
  import { limits } from '../../contracts/index.js';
5
5
  import { privateRoot, physicalBytes } from '../acquisition/files.js';
6
6
  import { runOwned, isolatedEnvironment } from '../acquisition/process.js';
7
- import { inspectCaptureRuntime, CaptureSetupError } from '../acquisition/runtime.js';
8
- import { settleCaptureRoot, CaptureCleanupError } from '../acquisition/cleanup.js';
7
+ import { inspectCaptureRuntime } from '../acquisition/runtime.js';
8
+ import { settleCaptureRoot } from '../acquisition/cleanup.js';
9
9
  import { failure } from '../acquisition/errors.js';
10
10
  import { object } from '../acquisition/normalize.js';
11
11
  import { normalizePdfBundle } from './normalize.js';
12
12
  import { copyPdfInput } from './input.js';
13
13
  import { pdfFailure } from './errors.js';
14
+ import { SpongeError, publicCode, codeOf } from '../errors.js';
14
15
  export async function capturePdf(request) {
15
16
  const { requestId, signal } = request;
16
17
  if (!/^[a-zA-Z0-9_-]{1,128}$/.test(requestId))
@@ -18,7 +19,10 @@ export async function capturePdf(request) {
18
19
  let url;
19
20
  try {
20
21
  url = new URL(request.sourceUrl);
21
- if (!['http:', 'https:', 'file:'].includes(url.protocol) || url.username || url.password || request.sourceUrl.length > 16384)
22
+ if (!['http:', 'https:', 'file:'].includes(url.protocol) ||
23
+ url.username ||
24
+ url.password ||
25
+ request.sourceUrl.length > 16384)
22
26
  throw Error();
23
27
  }
24
28
  catch {
@@ -31,7 +35,7 @@ export async function capturePdf(request) {
31
35
  runtime = await inspectCaptureRuntime(request.runtime.root, request.runtime.pins);
32
36
  }
33
37
  catch (error) {
34
- return failure(requestId, error instanceof CaptureSetupError ? error.code : 'runtime-missing', 'setup');
38
+ return failure(requestId, publicCode(error, 'runtime-missing'), 'setup');
35
39
  }
36
40
  let base;
37
41
  try {
@@ -51,7 +55,27 @@ export async function capturePdf(request) {
51
55
  if (request.connection?.kind === 'cdp' || request.allowPrivateNetwork)
52
56
  return failure(requestId, 'unsupported');
53
57
  const captureHelper = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? './capture-helper.ts' : './capture-helper.js', import.meta.url));
54
- const args = [captureHelper, runtime.root, request.sourceUrl, '--slug', 'document', '--timeout-ms', String(deadline), '--max-pdf-bytes', String(limits.pdfBytes), '--max-pages', '500', '--max-images', String(limits.imagesPerItem), '--max-asset-bytes', String(limits.imageBytes), '--max-total-asset-bytes', String(500 * 1024 ** 2), '--quiet', '--json'];
58
+ const args = [
59
+ captureHelper,
60
+ runtime.root,
61
+ request.sourceUrl,
62
+ '--slug',
63
+ 'document',
64
+ '--timeout-ms',
65
+ String(deadline),
66
+ '--max-pdf-bytes',
67
+ String(limits.pdfBytes),
68
+ '--max-pages',
69
+ '500',
70
+ '--max-images',
71
+ String(limits.imagesPerItem),
72
+ '--max-asset-bytes',
73
+ String(limits.imageBytes),
74
+ '--max-total-asset-bytes',
75
+ String(500 * 1024 ** 2),
76
+ '--quiet',
77
+ '--json',
78
+ ];
55
79
  let selectedState;
56
80
  if (request.connection?.kind === 'account') {
57
81
  if (!/^[a-zA-Z0-9_.-]{1,128}$/.test(request.connection.accountId))
@@ -73,32 +97,45 @@ export async function capturePdf(request) {
73
97
  if (signal?.aborted)
74
98
  abort();
75
99
  let cleanup = 'settled', keep = false, quota = false, checking = false;
76
- const monitor = setInterval(async () => { if (checking)
77
- return; checking = true; try {
78
- if (await physicalBytes(root) > limits.stagingBytes) {
100
+ const monitor = setInterval(async () => {
101
+ if (checking)
102
+ return;
103
+ checking = true;
104
+ try {
105
+ if ((await physicalBytes(root)) > limits.stagingBytes) {
106
+ quota = true;
107
+ controller.abort();
108
+ }
109
+ }
110
+ catch {
79
111
  quota = true;
80
112
  controller.abort();
81
113
  }
82
- }
83
- catch {
84
- quota = true;
85
- controller.abort();
86
- }
87
- finally {
88
- checking = false;
89
- } }, 100);
114
+ finally {
115
+ checking = false;
116
+ }
117
+ }, 100);
90
118
  const started = Date.now();
91
119
  try {
92
120
  await mkdir(home, { mode: 0o700 });
93
121
  await mkdir(output, { mode: 0o700 });
94
- await writeFile(join(root, 'ownership.json'), JSON.stringify({ schemaVersion: 1, requestId, state: 'acquiring' }), { mode: 0o600 });
122
+ await writeFile(join(root, 'ownership.json'), JSON.stringify({ schemaVersion: 1, requestId, state: 'acquiring' }), {
123
+ mode: 0o600,
124
+ });
95
125
  let sourceHash;
96
126
  if (url.protocol === 'file:') {
97
127
  sourceHash = await copyPdfInput(fileURLToPath(url), join(root, 'input.pdf'), controller.signal);
98
128
  args[2] = join(root, 'input.pdf');
99
129
  }
100
130
  cleanup = 'pending';
101
- const result = await (request.runProcess ?? runOwned)(runtime.bun, args, { cwd: root, env, signal: controller.signal, deadlineMs: Math.max(1, deadline - (Date.now() - started)), maxOutputBytes: limits.metadataBytes, graceMs: 5000 });
131
+ const result = await (request.runProcess ?? runOwned)(runtime.bun, args, {
132
+ cwd: root,
133
+ env,
134
+ signal: controller.signal,
135
+ deadlineMs: Math.max(1, deadline - (Date.now() - started)),
136
+ maxOutputBytes: limits.metadataBytes,
137
+ graceMs: 5000,
138
+ });
102
139
  cleanup = result.cleanup;
103
140
  let summary;
104
141
  try {
@@ -119,11 +156,19 @@ export async function capturePdf(request) {
119
156
  if (result.code !== 0 || object(summary).ok !== true)
120
157
  return upstream;
121
158
  const outputDirectory = object(summary).outputDirectory;
122
- if (typeof outputDirectory !== 'string' || !outputDirectory.startsWith(output + '/') || await realpath(outputDirectory) !== outputDirectory)
159
+ if (typeof outputDirectory !== 'string' ||
160
+ !outputDirectory.startsWith(output + '/') ||
161
+ (await realpath(outputDirectory)) !== outputDirectory)
123
162
  return failure(requestId, 'integrity', 'verification');
124
163
  const helper = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? './verify-helper.ts' : './verify-helper.js', import.meta.url));
125
164
  cleanup = 'pending';
126
- const checked = await (request.runProcess ?? runOwned)(runtime.bun, [helper, runtime.root, outputDirectory, request.sourceUrl], { cwd: root, env, signal: controller.signal, deadlineMs: Math.max(1, deadline - (Date.now() - started)), maxOutputBytes: limits.helperFrameBytes });
165
+ const checked = await (request.runProcess ?? runOwned)(runtime.bun, [helper, runtime.root, outputDirectory, request.sourceUrl], {
166
+ cwd: root,
167
+ env,
168
+ signal: controller.signal,
169
+ deadlineMs: Math.max(1, deadline - (Date.now() - started)),
170
+ maxOutputBytes: limits.helperFrameBytes,
171
+ });
127
172
  cleanup = checked.cleanup;
128
173
  if (cleanup !== 'settled')
129
174
  return failure(requestId, 'cleanup-required', 'cleanup');
@@ -131,26 +176,49 @@ export async function capturePdf(request) {
131
176
  return failure(requestId, checked.stopped);
132
177
  if (checked.code !== 0)
133
178
  return failure(requestId, 'integrity', 'verification');
134
- const capture = await normalizePdfBundle({ bundleRoot: outputDirectory, inspection: JSON.parse(checked.stdout), summary, requestId, sourceUrl: request.sourceUrl, wordcellVersion: runtime.pins.wordcell.version, ...(sourceHash ? { sourceHash } : {}) });
179
+ const capture = await normalizePdfBundle({
180
+ bundleRoot: outputDirectory,
181
+ inspection: JSON.parse(checked.stdout),
182
+ summary,
183
+ requestId,
184
+ sourceUrl: request.sourceUrl,
185
+ wordcellVersion: runtime.pins.wordcell.version,
186
+ ...(sourceHash ? { sourceHash } : {}),
187
+ });
135
188
  if (controller.signal.aborted)
136
189
  return failure(requestId, quota ? 'limit-exceeded' : 'cancelled');
137
- await writeFile(join(root, 'ownership.json'), JSON.stringify({ schemaVersion: 1, requestId, state: 'verified' }), { mode: 0o600 });
190
+ await writeFile(join(root, 'ownership.json'), JSON.stringify({ schemaVersion: 1, requestId, state: 'verified' }), {
191
+ mode: 0o600,
192
+ });
138
193
  keep = true;
139
- return { ok: true, capture, bundleRoot: outputDirectory, dispose: async () => { if (await settleCaptureRoot(root, requestId) === 'pending')
140
- throw new CaptureCleanupError(); } };
194
+ return {
195
+ ok: true,
196
+ capture,
197
+ bundleRoot: outputDirectory,
198
+ dispose: async () => {
199
+ if ((await settleCaptureRoot(root, requestId)) === 'pending')
200
+ throw new SpongeError('cleanup-required');
201
+ },
202
+ };
141
203
  }
142
204
  catch (error) {
143
205
  if (cleanup !== 'settled')
144
206
  return failure(requestId, 'cleanup-required', 'cleanup');
145
- const code = (error instanceof Error && ['integrity', 'limit-exceeded', 'schema-newer', 'invalid-input', 'cancelled'].includes(error.message) ? error.message : 'unclassified');
207
+ const raw = codeOf(error), code = raw === 'integrity' ||
208
+ raw === 'limit-exceeded' ||
209
+ raw === 'schema-newer' ||
210
+ raw === 'invalid-input' ||
211
+ raw === 'cancelled'
212
+ ? raw
213
+ : 'unclassified';
146
214
  return failure(requestId, code, 'verification');
147
215
  }
148
216
  finally {
149
217
  clearInterval(monitor);
150
218
  while (checking)
151
- await new Promise(r => setTimeout(r, 5));
219
+ await new Promise((r) => setTimeout(r, 5));
152
220
  signal?.removeEventListener('abort', abort);
153
- if (!keep && cleanup === 'settled' && await settleCaptureRoot(root, requestId) === 'pending')
221
+ if (!keep && cleanup === 'settled' && (await settleCaptureRoot(root, requestId)) === 'pending')
154
222
  return failure(requestId, 'cleanup-required', 'cleanup');
155
223
  }
156
224
  }