enigma-memory 0.1.17 → 0.1.22

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 (290) hide show
  1. package/README.md +85 -27
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +4979 -3263
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/SIMULATION.md +14 -9
  74. package/deploy/docker-compose.local-production-simulation.yml +54 -12
  75. package/docs/benchmark-attestation-network.md +487 -487
  76. package/docs/benchmark-reproducibility.md +289 -289
  77. package/docs/blockchain-only-mechanisms.md +400 -400
  78. package/docs/browser-extension-install.md +8 -6
  79. package/docs/client-connectors.md +16 -12
  80. package/docs/demo-proof-network.md +275 -275
  81. package/docs/developer-ecosystem.md +15 -13
  82. package/docs/developer-proof-quickstart.md +325 -325
  83. package/docs/enigma-memory-ready-conformance.md +378 -376
  84. package/docs/install-anywhere.md +64 -30
  85. package/docs/installers-and-desktop.md +8 -7
  86. package/docs/memory-benchmarks.md +1 -1
  87. package/docs/memory-drive-health-model.md +690 -690
  88. package/docs/novelty-invention-candidates.md +161 -161
  89. package/docs/proof-network-build-notes.md +240 -240
  90. package/docs/proof-network-claim-boundaries.md +320 -318
  91. package/docs/proof-network.md +339 -339
  92. package/docs/sdk-api.md +324 -324
  93. package/docs/solana-proof-rail.md +453 -453
  94. package/examples/01-quickstart-agent/index.mjs +49 -0
  95. package/examples/01_agent_memory_quickstart.mjs +57 -0
  96. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  97. package/examples/02_cross_model_passport.mjs +64 -0
  98. package/examples/03-langchain-memory/index.mjs +41 -0
  99. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  100. package/examples/04-python-trading-agent/trader.py +49 -0
  101. package/examples/README.md +27 -0
  102. package/examples/ci/github-actions.yml +7 -2
  103. package/package.json +410 -278
  104. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  105. package/packages/connectors/src/index.js +196 -4
  106. package/packages/connectors/swarm-router.mjs +168 -0
  107. package/packages/core/src/index.js +249 -2
  108. package/packages/core/src/version.mjs +7 -0
  109. package/packages/dev-tools/package.json +19 -0
  110. package/packages/dev-tools/src/index.js +4 -0
  111. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  112. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  113. package/packages/dev-tools/src/vault-inspector.js +114 -0
  114. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  115. package/packages/developer-platform/src/access-credentials.js +341 -0
  116. package/packages/developer-platform/src/http.js +132 -0
  117. package/packages/developer-platform/src/index.js +4 -0
  118. package/packages/developer-platform/src/usage-http.js +60 -0
  119. package/packages/developer-platform/src/usage.js +295 -0
  120. package/packages/enclave-runtime/attestation.mjs +159 -0
  121. package/packages/enclave-runtime/index.mjs +47 -0
  122. package/packages/enclave-runtime/session-manager.mjs +253 -0
  123. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  124. package/packages/enigma-reflex/package.json +14 -0
  125. package/packages/enigma-reflex/src/index.js +204 -0
  126. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  127. package/packages/enigma-reflex/training/requirements.txt +8 -0
  128. package/packages/enigma-reflex/training/train.py +314 -0
  129. package/packages/enigma-weave/LICENSE +22 -0
  130. package/packages/enigma-weave/UPSTREAM.json +21 -0
  131. package/packages/enigma-weave/package.json +14 -0
  132. package/packages/enigma-weave/src/index.js +286 -0
  133. package/packages/hosted-cloud/src/index.js +80 -5
  134. package/packages/importers/src/index.js +432 -0
  135. package/packages/inference-runtime/src/browser.js +401 -0
  136. package/packages/inference-runtime/src/chat.js +265 -0
  137. package/packages/inference-runtime/src/code.js +407 -0
  138. package/packages/inference-runtime/src/contracts.js +162 -0
  139. package/packages/inference-runtime/src/http.js +232 -0
  140. package/packages/inference-runtime/src/image.js +186 -0
  141. package/packages/inference-runtime/src/index.js +10 -0
  142. package/packages/inference-runtime/src/model-router.js +320 -0
  143. package/packages/inference-runtime/src/platform.js +125 -0
  144. package/packages/inference-runtime/src/privacy.js +400 -0
  145. package/packages/inference-runtime/src/video.js +253 -0
  146. package/packages/mcp-server/README.md +22 -6
  147. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  148. package/packages/mcp-server/src/index.js +2498 -1185
  149. package/packages/mcp-server/src/oauth.js +561 -0
  150. package/packages/mcp-server/src/private-handoff.js +84 -0
  151. package/packages/mcp-server/src/remote-http.js +273 -0
  152. package/packages/mcp-server/src/remote-policy.js +72 -0
  153. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  154. package/packages/mesh/index.d.ts +283 -0
  155. package/packages/mesh/package.json +23 -0
  156. package/packages/mesh/src/crypto.js +189 -0
  157. package/packages/mesh/src/federation-packets.js +353 -0
  158. package/packages/mesh/src/gossip.js +311 -0
  159. package/packages/mesh/src/index.js +6 -0
  160. package/packages/mesh/src/protocol.js +255 -0
  161. package/packages/mesh/src/router.js +279 -0
  162. package/packages/mesh/src/transport.js +306 -0
  163. package/packages/passport/src/index.js +436 -7
  164. package/packages/private-economy/src/credits-http.js +100 -0
  165. package/packages/private-economy/src/credits.js +447 -0
  166. package/packages/private-economy/src/index.js +5 -0
  167. package/packages/private-economy/src/payments-http.js +120 -0
  168. package/packages/private-economy/src/payments.js +509 -0
  169. package/packages/private-economy/src/x402.js +346 -0
  170. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  171. package/packages/rag/index.d.ts +182 -0
  172. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  173. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  174. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  175. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  176. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  177. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  178. package/packages/rag/package.json +27 -0
  179. package/packages/rag/src/blinded-search.js +109 -0
  180. package/packages/rag/src/bm25.js +169 -0
  181. package/packages/rag/src/embeddings.js +459 -0
  182. package/packages/rag/src/hybrid.js +76 -0
  183. package/packages/rag/src/index.js +38 -0
  184. package/packages/rag/src/reranker.js +61 -0
  185. package/packages/rag/src/research.js +107 -0
  186. package/packages/rag/src/vector-store.js +430 -0
  187. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  188. package/packages/sdk/index.d.ts +760 -0
  189. package/packages/sdk/package.json +33 -0
  190. package/packages/sdk/python/README.md +24 -0
  191. package/packages/sdk/python/enigma_sdk.py +250 -0
  192. package/packages/sdk/python/pyproject.toml +34 -0
  193. package/packages/sdk/python/requirements.txt +1 -0
  194. package/packages/sdk/python/setup.py +20 -0
  195. package/packages/sdk/src/federation/capability-grant.js +389 -0
  196. package/packages/sdk/src/federation/federation-router.js +360 -0
  197. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  198. package/packages/sdk/src/federation/index.js +3 -0
  199. package/packages/sdk/src/index.js +1796 -0
  200. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  201. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  202. package/packages/sdk/src/intelligence/index.js +4 -0
  203. package/packages/sdk/src/intelligence/ontology.js +122 -0
  204. package/packages/sdk/src/intelligence/temporal.js +123 -0
  205. package/packages/sdk/src/market-client.js +142 -0
  206. package/packages/sdk/src/mesh-client.js +110 -0
  207. package/packages/sdk/src/middleware/index.js +3 -0
  208. package/packages/sdk/src/middleware/langchain.js +159 -0
  209. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  210. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  211. package/packages/sdk/src/rag-client.js +85 -0
  212. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  213. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  214. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  215. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  216. package/packages/snapcompact/index.d.ts +284 -0
  217. package/packages/snapcompact/package.json +25 -0
  218. package/packages/snapcompact/src/index.js +716 -0
  219. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  220. package/packages/terminal-console/animations.mjs +240 -0
  221. package/packages/terminal-console/auto-anchor.mjs +220 -0
  222. package/packages/terminal-console/banner.mjs +91 -0
  223. package/packages/terminal-console/commands.mjs +459 -0
  224. package/packages/terminal-console/delegation.mjs +152 -0
  225. package/packages/terminal-console/index.mjs +5 -0
  226. package/packages/terminal-console/outbox.mjs +143 -0
  227. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  228. package/packages/terminal-console/repl.mjs +136 -0
  229. package/packages/terminal-console/signer-store.mjs +130 -0
  230. package/packages/terminal-console/solana-rpc.mjs +214 -0
  231. package/packages/terminal-console/solana-transport.mjs +189 -0
  232. package/packages/terminal-tui/dashboard.mjs +214 -0
  233. package/packages/terminal-tui/index.mjs +28 -0
  234. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  235. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  236. package/packages/vault/index.d.ts +449 -0
  237. package/packages/vault/package.json +27 -0
  238. package/packages/vault/src/e2ee.mjs +393 -0
  239. package/packages/vault/src/enclave.js +481 -0
  240. package/packages/vault/src/erasure.js +207 -0
  241. package/packages/vault/src/index.js +1150 -125
  242. package/packages/vault/src/persistence.js +307 -0
  243. package/packages/vault/src/poseidon.js +354 -0
  244. package/packages/vault/src/receipt.js +459 -0
  245. package/scripts/benchmark-optical-context.mjs +166 -0
  246. package/scripts/bootstrap-enigma.mjs +502 -0
  247. package/scripts/build-edge-backend-workers.mjs +20 -5
  248. package/scripts/build-goal-completion-audit.mjs +72 -25
  249. package/scripts/build-hosted-api-key-lifecycle.mjs +292 -274
  250. package/scripts/build-hosted-customer-lifecycle.mjs +493 -476
  251. package/scripts/build-hosted-probe-worker.mjs +19 -4
  252. package/scripts/build-installer-assets.mjs +409 -389
  253. package/scripts/build-operator-evidence-starter.mjs +59 -1
  254. package/scripts/build-production-backend-env-kit.mjs +2 -0
  255. package/scripts/build-production-unblocker.mjs +4 -1
  256. package/scripts/build-proof-network-packet.mjs +213 -213
  257. package/scripts/check.mjs +25 -4
  258. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  259. package/scripts/install-enigma-local.mjs +18 -5
  260. package/scripts/release-audit.mjs +74 -115
  261. package/scripts/release-provenance.mjs +12 -2
  262. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  263. package/scripts/run-standard-memory-benchmarks.mjs +1354 -1352
  264. package/scripts/scan-secrets.mjs +178 -0
  265. package/scripts/simulate-production-env.mjs +71 -10
  266. package/scripts/validate-hosted-backend-live.mjs +112 -1
  267. package/specs/antibody-pack-v1.schema.json +95 -0
  268. package/specs/antigen-envelope-v1.schema.json +81 -0
  269. package/specs/boundary-manifest-v1.schema.json +35 -35
  270. package/specs/capsule-v1.schema.json +55 -55
  271. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  272. package/specs/claim-ledger-v1.schema.json +291 -0
  273. package/specs/context-passport-v1.schema.json +59 -0
  274. package/specs/deletion-tombstone-v1.schema.json +26 -26
  275. package/specs/evidence-packet-v1.schema.json +177 -0
  276. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  277. package/specs/immune-scan-report-v1.schema.json +112 -0
  278. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  279. package/specs/memory-atom-v1.schema.json +59 -0
  280. package/specs/memory-event-v1.schema.json +42 -42
  281. package/specs/passport-v1.schema.json +50 -50
  282. package/specs/proof-of-non-use-v1.schema.json +65 -0
  283. package/specs/quarantine-record-v1.schema.json +126 -0
  284. package/specs/receipt-v1.schema.json +61 -61
  285. package/specs/state-checkpoint-v1.schema.json +37 -37
  286. package/specs/trust-bundle-v1.schema.json +56 -56
  287. package/specs/trust-card-v1.schema.json +119 -0
  288. package/docs/proof-network-launch-plan.md +0 -421
  289. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  290. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -0,0 +1,447 @@
1
+ import { createHash, randomBytes, randomUUID, scrypt as scryptCallback, timingSafeEqual } from 'node:crypto';
2
+ import { mkdir, open, readFile, rename } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
5
+
6
+ export const ENIGMA_CREDITS_STORE_SCHEMA = 'enigma.private_credits_store.v1';
7
+ export const ENIGMA_CREDITS_VAULT_SCHEMA = 'enigma.private_credits_vault.v1';
8
+ export const MICROCREDITS_PER_CREDIT = 1_000_000;
9
+ const ACCESS_PATTERN = /^enigma_cv_([A-Za-z0-9_-]{12})_([A-Za-z0-9_-]{43})$/;
10
+ const RECOVERY_PATTERN = /^enigma_cr_([A-Za-z0-9_-]{16})_([A-Za-z0-9_-]{43})$/;
11
+ const scrypt = promisify(scryptCallback);
12
+
13
+ function requiredString(value, name, maximum = 512) {
14
+ if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${name} is required`);
15
+ const normalized = value.trim();
16
+ if (normalized.length > maximum) throw new TypeError(`${name} exceeds ${maximum} characters`);
17
+ return normalized;
18
+ }
19
+
20
+ function boundedInteger(value, name, minimum, maximum, fallback) {
21
+ const resolved = value === undefined ? fallback : value;
22
+ if (!Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) throw new TypeError(`${name} must be from ${minimum} through ${maximum}`);
23
+ return resolved;
24
+ }
25
+
26
+ function iso(clock) {
27
+ return new Date(clock()).toISOString();
28
+ }
29
+ function checkedAdd(left, right, name) {
30
+ if (!Number.isSafeInteger(left) || !Number.isSafeInteger(right)) throw new Error(`${name} contains a non-safe integer`);
31
+ const result = left + right;
32
+ if (!Number.isSafeInteger(result)) {
33
+ const error = new Error(`${name} exceeds the safe integer range`);
34
+ error.code = 'credits_overflow';
35
+ throw error;
36
+ }
37
+ return result;
38
+ }
39
+
40
+ function checkedSum(values, name) {
41
+ return values.reduce((sum, value) => checkedAdd(sum, value, name), 0);
42
+ }
43
+
44
+
45
+ function emptyState() {
46
+ return { schema: ENIGMA_CREDITS_STORE_SCHEMA, revision: 0, vaults: {}, recoveryIndex: {}, accessIndex: {} };
47
+ }
48
+
49
+ function publicVault(vault, now = Date.now()) {
50
+ const activeReservations = Object.values(vault.reservations).filter((reservation) => reservation.status === 'reserved' && Date.parse(reservation.expiresAt) > now);
51
+ const reservedMicrocredits = checkedSum(activeReservations.map((reservation) => reservation.amountMicrocredits), 'reserved microcredits');
52
+ return {
53
+ schema: ENIGMA_CREDITS_VAULT_SCHEMA,
54
+ id: vault.id,
55
+ balanceMicrocredits: vault.balanceMicrocredits,
56
+ reservedMicrocredits,
57
+ availableMicrocredits: checkedAdd(vault.balanceMicrocredits, -reservedMicrocredits, 'available microcredits'),
58
+ creditScale: MICROCREDITS_PER_CREDIT,
59
+ createdAt: vault.createdAt,
60
+ recoveredAt: vault.recoveredAt,
61
+ recoveryVersion: vault.recoveryVersion,
62
+ ledgerSequence: vault.ledgerSequence,
63
+ accountIdentityStored: false,
64
+ privacyBoundary: 'Accountless and pseudonymous. Network operators may still observe IP address, timing, and vault access patterns.',
65
+ paymentFundingBoundary: 'Funding is linkable: on-chain rails can reveal a wallet and Stripe can identify a customer. The separate payment ledger maps that funding event to this pseudonymous vault.',
66
+ };
67
+ }
68
+
69
+ async function derive(secret, salt) {
70
+ return Buffer.from(await scrypt(secret, Buffer.from(salt, 'base64url'), 32, { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 }));
71
+ }
72
+
73
+ function verifier() {
74
+ const locator = randomBytes(12).toString('base64url');
75
+ const secret = randomBytes(32).toString('base64url');
76
+ const salt = randomBytes(16).toString('base64url');
77
+ return { locator, secret, salt };
78
+ }
79
+
80
+ function accessCredential() {
81
+ const locator = randomBytes(9).toString('base64url');
82
+ const secret = randomBytes(32).toString('base64url');
83
+ const salt = randomBytes(16).toString('base64url');
84
+ return { locator, secret, salt };
85
+ }
86
+
87
+ function parseCredential(value, pattern) {
88
+ const match = pattern.exec(String(value || ''));
89
+ return match ? { locator: match[1], secret: match[2] } : null;
90
+ }
91
+
92
+ function createStoreAdapter(load, save, { clock, maxLedgerEntries }) {
93
+ function fingerprint(value) {
94
+ return createHash('sha256').update(JSON.stringify(value)).digest('base64url');
95
+ }
96
+
97
+ function idempotencyConflict() {
98
+ const error = new Error('idempotency key was already used with different parameters');
99
+ error.code = 'idempotency_conflict';
100
+ return error;
101
+ }
102
+
103
+ let queue = Promise.resolve();
104
+ const transaction = (operation) => {
105
+ const run = queue.then(async () => {
106
+ const state = await load();
107
+ const result = await operation(state);
108
+ for (const vault of Object.values(state.vaults)) {
109
+ if (vault.ledger.length > maxLedgerEntries) vault.ledger.splice(0, vault.ledger.length - maxLedgerEntries);
110
+ }
111
+ state.revision = checkedAdd(state.revision, 1, 'credits store revision');
112
+ await save(state);
113
+ return result;
114
+ });
115
+ queue = run.catch(() => undefined);
116
+ return run;
117
+ };
118
+ const view = async (operation) => {
119
+ await queue;
120
+ return operation(await load());
121
+ };
122
+ return Object.freeze({
123
+ async insert(vault) {
124
+ return transaction((state) => {
125
+ if (state.vaults[vault.id] || state.recoveryIndex[vault.recovery.locator] || state.accessIndex[vault.initialAccess.locator]) throw new Error('credits credential collision');
126
+ const stored = { ...structuredClone(vault), accessTokens: { [vault.initialAccess.locator]: structuredClone(vault.initialAccess) } };
127
+ delete stored.initialAccess;
128
+ state.vaults[stored.id] = stored;
129
+ state.recoveryIndex[stored.recovery.locator] = stored.id;
130
+ state.accessIndex[vault.initialAccess.locator] = stored.id;
131
+ return structuredClone(stored);
132
+ });
133
+ },
134
+ async recoveryByLocator(locator) {
135
+ return view((state) => {
136
+ const id = state.recoveryIndex[locator];
137
+ return id && state.vaults[id] ? structuredClone(state.vaults[id]) : null;
138
+ });
139
+ },
140
+ async rotateRecovery({ vaultId, expectedVersion, previousLocator, recovery, access }) {
141
+ return transaction((state) => {
142
+ const vault = state.vaults[vaultId];
143
+ if (!vault || vault.recoveryVersion !== expectedVersion || vault.recovery.locator !== previousLocator) return null;
144
+ delete state.recoveryIndex[vault.recovery.locator];
145
+ for (const [locator] of Object.entries(vault.accessTokens)) delete state.accessIndex[locator];
146
+ vault.accessTokens = { [access.locator]: structuredClone(access) };
147
+ vault.recovery = structuredClone(recovery);
148
+ vault.recoveryVersion = checkedAdd(vault.recoveryVersion, 1, 'recovery version');
149
+ vault.recoveredAt = iso(clock);
150
+ state.recoveryIndex[recovery.locator] = vault.id;
151
+ state.accessIndex[access.locator] = vault.id;
152
+ vault.ledgerSequence = checkedAdd(vault.ledgerSequence, 1, 'ledger sequence');
153
+ vault.ledger.push({ sequence: vault.ledgerSequence, action: 'recovered', at: vault.recoveredAt, amountMicrocredits: 0, reference: null });
154
+ return structuredClone(vault);
155
+ });
156
+ },
157
+ async accessByLocator(locator) {
158
+ return view((state) => {
159
+ const vaultId = state.accessIndex[locator];
160
+ const vault = vaultId ? state.vaults[vaultId] : null;
161
+ const access = vault?.accessTokens?.[locator];
162
+ return vault && access ? { vault: structuredClone(vault), access: structuredClone(access) } : null;
163
+ });
164
+ },
165
+ async touchAccess(vaultId, locator, at) {
166
+ return transaction((state) => {
167
+ const vault = state.vaults[vaultId];
168
+ const access = vault?.accessTokens?.[locator];
169
+ if (!access || access.revokedAt || Date.parse(access.expiresAt) <= clock()) return null;
170
+ access.lastUsedAt = at;
171
+ return structuredClone(vault);
172
+ });
173
+ },
174
+ async revokeAccess(vaultId, locator) {
175
+ return transaction((state) => {
176
+ const vault = state.vaults[vaultId];
177
+ const access = vault?.accessTokens?.[locator];
178
+ if (!access) return false;
179
+ access.revokedAt ||= iso(clock);
180
+ return true;
181
+ });
182
+ },
183
+ async getVault(vaultId) {
184
+ return view((state) => state.vaults[vaultId] ? structuredClone(state.vaults[vaultId]) : null);
185
+ },
186
+ async credit(vaultId, operation) {
187
+ return transaction((state) => {
188
+ const vault = state.vaults[vaultId];
189
+ if (!vault) return null;
190
+ const idempotencyKey = `credit:${operation.idempotencyKey}`;
191
+ const requestFingerprint = fingerprint({
192
+ amountMicrocredits: operation.amountMicrocredits,
193
+ reference: operation.reference,
194
+ });
195
+ const existing = vault.idempotency[idempotencyKey];
196
+ if (existing) {
197
+ if (existing.fingerprint !== requestFingerprint) throw idempotencyConflict();
198
+ return { vault: structuredClone(vault), ledger: structuredClone(existing.result), inserted: false };
199
+ }
200
+ vault.balanceMicrocredits = checkedAdd(vault.balanceMicrocredits, operation.amountMicrocredits, 'credits balance');
201
+ vault.ledgerSequence = checkedAdd(vault.ledgerSequence, 1, 'ledger sequence');
202
+ const ledger = {
203
+ sequence: vault.ledgerSequence,
204
+ action: 'credited',
205
+ at: iso(clock),
206
+ amountMicrocredits: operation.amountMicrocredits,
207
+ reference: operation.reference,
208
+ idempotencyKey: operation.idempotencyKey,
209
+ };
210
+ vault.ledger.push(ledger);
211
+ vault.idempotency[idempotencyKey] = { operation: 'credit', fingerprint: requestFingerprint, result: ledger };
212
+ return { vault: structuredClone(vault), ledger: structuredClone(ledger), inserted: true };
213
+ });
214
+ },
215
+ async reserve(vaultId, operation) {
216
+ return transaction((state) => {
217
+ const vault = state.vaults[vaultId];
218
+ if (!vault) return null;
219
+ const idempotencyKey = `reserve:${operation.idempotencyKey}`;
220
+ const requestFingerprint = fingerprint({
221
+ amountMicrocredits: operation.amountMicrocredits,
222
+ purpose: operation.purpose,
223
+ });
224
+ const existing = vault.idempotency[idempotencyKey];
225
+ if (existing) {
226
+ if (existing.fingerprint !== requestFingerprint) throw idempotencyConflict();
227
+ return { vault: structuredClone(vault), reservation: structuredClone(existing.result), inserted: false };
228
+ }
229
+ const now = clock();
230
+ const reserved = checkedSum(Object.values(vault.reservations)
231
+ .filter((reservation) => reservation.status === 'reserved' && Date.parse(reservation.expiresAt) > now)
232
+ .map((reservation) => reservation.amountMicrocredits), 'reserved microcredits');
233
+ if (checkedAdd(vault.balanceMicrocredits, -reserved, 'available microcredits') < operation.amountMicrocredits) return { insufficient: true, vault: structuredClone(vault) };
234
+ const reservation = {
235
+ id: randomUUID(),
236
+ amountMicrocredits: operation.amountMicrocredits,
237
+ purpose: operation.purpose,
238
+ status: 'reserved',
239
+ createdAt: iso(clock),
240
+ expiresAt: new Date(now + operation.ttlMs).toISOString(),
241
+ capturedAt: null,
242
+ releasedAt: null,
243
+ };
244
+ vault.reservations[reservation.id] = reservation;
245
+ vault.idempotency[idempotencyKey] = { operation: 'reserve', fingerprint: requestFingerprint, result: reservation };
246
+ return { vault: structuredClone(vault), reservation: structuredClone(reservation), inserted: true };
247
+ });
248
+ },
249
+ async settleReservation(vaultId, reservationId, action) {
250
+ return transaction((state) => {
251
+ const vault = state.vaults[vaultId];
252
+ const reservation = vault?.reservations?.[reservationId];
253
+ if (!vault || !reservation) return null;
254
+ if (reservation.status !== 'reserved') return { vault: structuredClone(vault), reservation: structuredClone(reservation), changed: false };
255
+ const at = iso(clock);
256
+ if (action === 'captured') {
257
+ if (Date.parse(reservation.expiresAt) <= clock()) throw new Error('credit reservation expired');
258
+ if (vault.balanceMicrocredits < reservation.amountMicrocredits) throw new Error('credits balance changed below reservation');
259
+ vault.balanceMicrocredits = checkedAdd(vault.balanceMicrocredits, -reservation.amountMicrocredits, 'credits balance');
260
+ reservation.capturedAt = at;
261
+ } else {
262
+ reservation.releasedAt = at;
263
+ }
264
+ reservation.status = action;
265
+ vault.ledgerSequence = checkedAdd(vault.ledgerSequence, 1, 'ledger sequence');
266
+ vault.ledger.push({ sequence: vault.ledgerSequence, action, at, amountMicrocredits: -reservation.amountMicrocredits, reference: reservation.id });
267
+ return { vault: structuredClone(vault), reservation: structuredClone(reservation), changed: true };
268
+ });
269
+ },
270
+ });
271
+ }
272
+
273
+ export function createEphemeralCreditsStore(options = {}) {
274
+ const clock = options.clock || Date.now;
275
+ const maxLedgerEntries = boundedInteger(options.maxLedgerEntries, 'maxLedgerEntries', 100, 1_000_000, 100_000);
276
+ let state = emptyState();
277
+ return createStoreAdapter(async () => structuredClone(state), async (next) => { state = structuredClone(next); }, { clock, maxLedgerEntries });
278
+ }
279
+
280
+ export function createFileCreditsStore(options = {}) {
281
+ const path = resolve(String(options.path || '.enigma/credits.json'));
282
+ const clock = options.clock || Date.now;
283
+ const maxLedgerEntries = boundedInteger(options.maxLedgerEntries, 'maxLedgerEntries', 100, 1_000_000, 100_000);
284
+ async function load() {
285
+ try {
286
+ const state = JSON.parse(await readFile(path, 'utf8'));
287
+ if (state?.schema !== ENIGMA_CREDITS_STORE_SCHEMA) throw new Error('unsupported private credits store schema');
288
+ return state;
289
+ } catch (error) {
290
+ if (error.code === 'ENOENT') return emptyState();
291
+ throw error;
292
+ }
293
+ }
294
+ async function save(state) {
295
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
296
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
297
+ const handle = await open(temporary, 'wx', 0o600);
298
+ try {
299
+ await handle.writeFile(JSON.stringify(state));
300
+ await handle.sync();
301
+ } finally {
302
+ await handle.close();
303
+ }
304
+ await rename(temporary, path);
305
+ }
306
+ return createStoreAdapter(load, save, { clock, maxLedgerEntries });
307
+ }
308
+
309
+ export function createPrivateCreditsService(options = {}) {
310
+ const store = options.store;
311
+ if (!store || typeof store.insert !== 'function' || typeof store.recoveryByLocator !== 'function') throw new TypeError('private credits store is required');
312
+ const clock = options.clock || Date.now;
313
+ const accessTtlMs = boundedInteger(options.accessTtlSeconds, 'accessTtlSeconds', 300, 24 * 60 * 60, 15 * 60) * 1_000;
314
+ const reservationTtlMs = boundedInteger(options.reservationTtlSeconds, 'reservationTtlSeconds', 30, 60 * 60, 5 * 60) * 1_000;
315
+
316
+ async function materializeRecovery(candidate) {
317
+ return {
318
+ locator: candidate.locator,
319
+ salt: candidate.salt,
320
+ digest: (await derive(candidate.secret, candidate.salt)).toString('base64url'),
321
+ };
322
+ }
323
+
324
+ async function materializeAccess(candidate) {
325
+ return {
326
+ locator: candidate.locator,
327
+ salt: candidate.salt,
328
+ digest: (await derive(candidate.secret, candidate.salt)).toString('base64url'),
329
+ createdAt: iso(clock),
330
+ expiresAt: new Date(clock() + accessTtlMs).toISOString(),
331
+ lastUsedAt: null,
332
+ revokedAt: null,
333
+ };
334
+ }
335
+
336
+ function secretResponse(vault, recovery, access) {
337
+ return {
338
+ vault: publicVault(vault, clock()),
339
+ recoveryCode: `enigma_cr_${recovery.locator}_${recovery.secret}`,
340
+ accessToken: `enigma_cv_${access.locator}_${access.secret}`,
341
+ accessExpiresAt: new Date(clock() + accessTtlMs).toISOString(),
342
+ secretsShownOnce: true,
343
+ };
344
+ }
345
+
346
+ async function authenticate(accessToken) {
347
+ const parsed = parseCredential(accessToken, ACCESS_PATTERN);
348
+ if (!parsed) return null;
349
+ const found = await store.accessByLocator(parsed.locator);
350
+ if (!found || found.access.revokedAt || Date.parse(found.access.expiresAt) <= clock()) return null;
351
+ const actual = await derive(parsed.secret, found.access.salt);
352
+ const expected = Buffer.from(found.access.digest, 'base64url');
353
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return null;
354
+ const vault = await store.touchAccess(found.vault.id, parsed.locator, iso(clock));
355
+ if (!vault) return null;
356
+ return Object.freeze({ vaultId: vault.id, accessLocator: parsed.locator, authentication: 'private_credits', vault: publicVault(vault, clock()) });
357
+ }
358
+
359
+ return Object.freeze({
360
+ async create() {
361
+ const recovery = verifier();
362
+ const access = accessCredential();
363
+ const createdAt = iso(clock);
364
+ const vault = {
365
+ id: randomUUID(),
366
+ recovery: await materializeRecovery(recovery),
367
+ recoveryVersion: 1,
368
+ initialAccess: await materializeAccess(access),
369
+ balanceMicrocredits: 0,
370
+ createdAt,
371
+ recoveredAt: null,
372
+ ledgerSequence: 0,
373
+ ledger: [],
374
+ reservations: {},
375
+ idempotency: {},
376
+ };
377
+ const stored = await store.insert(vault);
378
+ return secretResponse(stored, recovery, access);
379
+ },
380
+ async recover(recoveryCode) {
381
+ const parsed = parseCredential(recoveryCode, RECOVERY_PATTERN);
382
+ if (!parsed) return null;
383
+ const found = await store.recoveryByLocator(parsed.locator);
384
+ if (!found) return null;
385
+ const actual = await derive(parsed.secret, found.recovery.salt);
386
+ const expected = Buffer.from(found.recovery.digest, 'base64url');
387
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return null;
388
+ const nextRecovery = verifier();
389
+ const access = accessCredential();
390
+ const rotated = await store.rotateRecovery({
391
+ vaultId: found.id,
392
+ expectedVersion: found.recoveryVersion,
393
+ previousLocator: parsed.locator,
394
+ recovery: await materializeRecovery(nextRecovery),
395
+ access: await materializeAccess(access),
396
+ });
397
+ if (!rotated) return null;
398
+ return secretResponse(rotated, nextRecovery, access);
399
+ },
400
+ authenticate,
401
+ async status(accessToken) {
402
+ const principal = await authenticate(accessToken);
403
+ return principal?.vault || null;
404
+ },
405
+ async logout(accessToken) {
406
+ const principal = await authenticate(accessToken);
407
+ return principal ? store.revokeAccess(principal.vaultId, principal.accessLocator) : false;
408
+ },
409
+ async credit(vaultId, input = {}) {
410
+ const result = await store.credit(requiredString(vaultId, 'vaultId'), {
411
+ amountMicrocredits: boundedInteger(input.amountMicrocredits, 'amountMicrocredits', 1, Number.MAX_SAFE_INTEGER),
412
+ idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey'),
413
+ reference: input.reference ? requiredString(input.reference, 'reference', 1024) : null,
414
+ });
415
+ if (!result) throw new Error('credits vault not found');
416
+ return { vault: publicVault(result.vault, clock()), ledger: result.ledger, inserted: result.inserted };
417
+ },
418
+ async reserve(accessToken, input = {}) {
419
+ const principal = await authenticate(accessToken);
420
+ if (!principal) return null;
421
+ const result = await store.reserve(principal.vaultId, {
422
+ amountMicrocredits: boundedInteger(input.amountMicrocredits, 'amountMicrocredits', 1, Number.MAX_SAFE_INTEGER),
423
+ idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey'),
424
+ purpose: requiredString(input.purpose || 'inference', 'purpose', 200),
425
+ ttlMs: reservationTtlMs,
426
+ });
427
+ if (result?.insufficient) {
428
+ const error = new Error('insufficient private credits');
429
+ error.code = 'insufficient_credits';
430
+ throw error;
431
+ }
432
+ return result ? { vault: publicVault(result.vault, clock()), reservation: result.reservation, inserted: result.inserted } : null;
433
+ },
434
+ async capture(accessToken, reservationId) {
435
+ const principal = await authenticate(accessToken);
436
+ if (!principal) return null;
437
+ const result = await store.settleReservation(principal.vaultId, requiredString(reservationId, 'reservationId'), 'captured');
438
+ return result ? { vault: publicVault(result.vault, clock()), reservation: result.reservation, changed: result.changed } : null;
439
+ },
440
+ async release(accessToken, reservationId) {
441
+ const principal = await authenticate(accessToken);
442
+ if (!principal) return null;
443
+ const result = await store.settleReservation(principal.vaultId, requiredString(reservationId, 'reservationId'), 'released');
444
+ return result ? { vault: publicVault(result.vault, clock()), reservation: result.reservation, changed: result.changed } : null;
445
+ },
446
+ });
447
+ }
@@ -0,0 +1,5 @@
1
+ export * from './credits.js';
2
+ export * from './credits-http.js';
3
+ export * from './payments.js';
4
+ export * from './payments-http.js';
5
+ export * from './x402.js';
@@ -0,0 +1,120 @@
1
+ const MAX_BODY_BYTES = 1024 * 1024;
2
+
3
+ class PaymentHttpError extends Error {
4
+ constructor(status, code, message) {
5
+ super(message);
6
+ this.name = 'PaymentHttpError';
7
+ this.status = status;
8
+ this.code = code;
9
+ }
10
+ }
11
+
12
+ function json(response, status, payload) {
13
+ const body = JSON.stringify(payload);
14
+ response.statusCode = status;
15
+ response.setHeader('Cache-Control', 'no-store');
16
+ response.setHeader('Content-Type', 'application/json; charset=utf-8');
17
+ response.setHeader('Content-Length', Buffer.byteLength(body));
18
+ response.setHeader('Referrer-Policy', 'no-referrer');
19
+ response.setHeader('X-Content-Type-Options', 'nosniff');
20
+ response.end(body);
21
+ }
22
+
23
+ async function rawBody(request) {
24
+ const chunks = [];
25
+ let size = 0;
26
+ let tooLarge = false;
27
+ for await (const chunk of request) {
28
+ size += chunk.length;
29
+ if (size > MAX_BODY_BYTES) tooLarge = true;
30
+ else chunks.push(chunk);
31
+ }
32
+ if (tooLarge) throw new PaymentHttpError(413, 'payload_too_large', 'payment request exceeds 1 MiB');
33
+ return Buffer.concat(chunks, size);
34
+ }
35
+
36
+ async function jsonBody(request) {
37
+ const contentType = String(request.headers['content-type'] || '').split(';', 1)[0].trim().toLowerCase();
38
+ if (contentType !== 'application/json') throw new PaymentHttpError(415, 'unsupported_media_type', 'Content-Type must be application/json');
39
+ try {
40
+ const value = JSON.parse((await rawBody(request)).toString('utf8') || '{}');
41
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error();
42
+ return value;
43
+ } catch (error) {
44
+ if (error instanceof PaymentHttpError) throw error;
45
+ throw new PaymentHttpError(400, 'invalid_json', 'payment request must be a JSON object');
46
+ }
47
+ }
48
+
49
+ function creditsBearer(request) {
50
+ const value = String(request.headers.authorization || '');
51
+ return value.startsWith('Bearer enigma_cv_') ? value.slice(7) : null;
52
+ }
53
+
54
+ function rejectUnknown(input, allowed) {
55
+ const unknown = Object.keys(input).find((key) => !allowed.has(key));
56
+ if (unknown) throw new PaymentHttpError(400, 'invalid_request', `unknown field: ${unknown}`);
57
+ }
58
+
59
+ export function createPaymentHttpHandler(options = {}) {
60
+ const payments = options.payments;
61
+ if (!payments || typeof payments.quote !== 'function' || typeof payments.settleOnchain !== 'function') throw new TypeError('credit payment service is required');
62
+ const stripe = options.stripe || null;
63
+ const basePath = String(options.basePath || '/v1/payments').replace(/\/+$/, '');
64
+
65
+ return async function paymentHttpHandler(request, response) {
66
+ const url = new URL(request.url || '/', 'http://localhost');
67
+ if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return false;
68
+ try {
69
+ if (request.method === 'GET' && url.pathname === `${basePath}/packages`) {
70
+ json(response, 200, { schema: 'enigma.credit_packages.v1', packages: payments.listPackages() });
71
+ return true;
72
+ }
73
+ if (request.method === 'POST' && url.pathname === `${basePath}/stripe/webhook`) {
74
+ if (!stripe) throw new PaymentHttpError(503, 'stripe_unavailable', 'Stripe subscription rail is not configured');
75
+ const event = stripe.verifyWebhook(await rawBody(request), request.headers['stripe-signature']);
76
+ json(response, 200, await payments.processStripeWebhook(event));
77
+ return true;
78
+ }
79
+ const accessToken = creditsBearer(request);
80
+ if (!accessToken) throw new PaymentHttpError(401, 'credits_session_required', 'private credits access token is required');
81
+ if (request.method === 'POST' && url.pathname === `${basePath}/quote`) {
82
+ const input = await jsonBody(request);
83
+ rejectUnknown(input, new Set(['packageId', 'rail']));
84
+ json(response, 201, await payments.quote(accessToken, input));
85
+ return true;
86
+ }
87
+ if (request.method === 'POST' && url.pathname === `${basePath}/settle`) {
88
+ const input = await jsonBody(request);
89
+ rejectUnknown(input, new Set(['quoteId', 'signature']));
90
+ json(response, 200, await payments.settleOnchain(accessToken, input));
91
+ return true;
92
+ }
93
+ if (request.method === 'POST' && url.pathname === `${basePath}/subscription/checkout`) {
94
+ const successUrl = typeof options.successUrl === 'function' ? options.successUrl(request) : options.successUrl;
95
+ const cancelUrl = typeof options.cancelUrl === 'function' ? options.cancelUrl(request) : options.cancelUrl;
96
+ if (!successUrl || !cancelUrl) throw new PaymentHttpError(503, 'checkout_unavailable', 'subscription checkout return URLs are not configured');
97
+ const input = await jsonBody(request);
98
+ rejectUnknown(input, new Set(['quoteId']));
99
+ json(response, 201, await payments.createSubscriptionCheckout(accessToken, {
100
+ quoteId: input.quoteId,
101
+ successUrl,
102
+ cancelUrl,
103
+ }));
104
+ return true;
105
+ }
106
+ throw new PaymentHttpError(404, 'not_found', 'payment endpoint not found');
107
+ } catch (error) {
108
+ const known = error instanceof PaymentHttpError;
109
+ const message = error?.message || 'payment operation failed';
110
+ const status = known ? error.status
111
+ : /not found/i.test(message) ? 404
112
+ : /expired|already|different|invalid or expired/i.test(message) ? 409
113
+ : error instanceof TypeError ? 400 : 400;
114
+ const code = known ? error.code : status === 404 ? 'not_found' : status === 409 ? 'payment_conflict' : 'payment_failed';
115
+ if (status === 401) response.setHeader('WWW-Authenticate', 'Bearer');
116
+ json(response, status, { error: { code, message } });
117
+ return true;
118
+ }
119
+ };
120
+ }