@dvmkit/sdk 0.1.3-rc.7 → 0.1.5-rc.7
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.
- package/README.md +116 -9
- package/dist/{chunk-ANFX5HEG.js → chunk-2UUXIIOC.js} +43 -13
- package/dist/{chunk-C6JHBLMW.js → chunk-BIP6G74V.js} +1 -1
- package/dist/{chunk-EPNDZ5DH.js → chunk-CEOAHV2I.js} +0 -30
- package/dist/{chunk-TSWKITGR.js → chunk-CGKZDODG.js} +8 -1
- package/dist/{chunk-YDIYXGYL.js → chunk-E4EVGPDX.js} +16 -9
- package/dist/{chunk-NFRM5QYP.js → chunk-EVBK675R.js} +5 -0
- package/dist/{chunk-DBCLBYHP.js → chunk-KVEHHC7W.js} +11 -1
- package/dist/{chunk-2K7E3N2D.js → chunk-KXCKFIJI.js} +77 -14
- package/dist/{chunk-3ZHMQCYP.js → chunk-NJFOV36R.js} +663 -235
- package/dist/{chunk-5GFED3GJ.js → chunk-NPIW5VR5.js} +40 -6
- package/dist/{chunk-LLXV32HA.js → chunk-SSSZUVWM.js} +96 -3
- package/dist/{chunk-7AKBC4PW.js → chunk-TQWGQCNV.js} +1 -1
- package/dist/{chunk-4B56DEEV.js → chunk-U6M3ATSG.js} +7 -1
- package/dist/{credit-menu-C7zAJElJ.d.ts → credit-menu-B-e3vZGo.d.ts} +136 -21
- package/dist/{fx-BF_SG2i0.d.ts → fx-CGRJE8rm.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/internal/caller.d.ts +54 -22
- package/dist/internal/caller.js +171 -38
- package/dist/internal/index.js +1 -1
- package/dist/internal/server.d.ts +8 -8
- package/dist/internal/server.js +7 -7
- package/dist/{job-store-Bn23V3QU.d.ts → job-store-Cnlv9pOx.d.ts} +16 -1
- package/dist/{lightning-backend-C04nH94l.d.ts → lightning-backend-KvQM0YHi.d.ts} +1 -1
- package/dist/{memory-credit-ledger-OP24Z2KO.js → memory-credit-ledger-MNUOTQO5.js} +1 -1
- package/dist/{mpp-setup-MOBWGTWJ.js → mpp-setup-SPBOF5AM.js} +11 -1
- package/dist/{postgres-job-store-TAONYLIF.js → postgres-job-store-3RAXMNSY.js} +1 -1
- package/dist/{revenue-reporter-XXSU5KVB.js → revenue-reporter-ASZ7SHHH.js} +1 -1
- package/dist/server/index.d.ts +26 -13
- package/dist/server/index.js +219 -55
- package/dist/{step-cache-3cT4Shk0.d.ts → step-cache-CwM_Q8rK.d.ts} +149 -3
- package/dist/{tempo-lifecycle-SQL3KLEZ.js → tempo-lifecycle-DFIXQ54Q.js} +3 -3
- package/dist/{tempo-wallet-QOLEIPCV.js → tempo-wallet-4QKSV65O.js} +10 -2
- package/dist/testing/index.d.ts +13 -3
- package/dist/testing/index.js +31 -2
- package/dist/{usd-BnuXoFl5.d.ts → usd-nYZSb7KQ.d.ts} +1 -1
- package/dist/{x402-T2C5MX3T.js → x402-5H27DCBE.js} +2 -2
- package/package.json +6 -4
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-BQ2NMWKE.js";
|
|
11
11
|
import {
|
|
12
12
|
MemoryCreditLedger
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KVEHHC7W.js";
|
|
14
14
|
import {
|
|
15
15
|
SIGNED_ENVELOPE_FIELDS,
|
|
16
16
|
SignedRequestError,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
localMessage,
|
|
36
36
|
providerMessage,
|
|
37
37
|
toJobRecord
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-U6M3ATSG.js";
|
|
39
39
|
import {
|
|
40
40
|
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
41
41
|
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
createMonotonicClock,
|
|
50
50
|
getWallet,
|
|
51
51
|
isPublicRequestId,
|
|
52
|
+
isTestMintUrl,
|
|
52
53
|
isX402SettlementReconciliationReason,
|
|
53
54
|
normaliseMintUrl,
|
|
54
55
|
parseTraceparent,
|
|
@@ -59,7 +60,7 @@ import {
|
|
|
59
60
|
toLockPubkey,
|
|
60
61
|
verifyCashuToken,
|
|
61
62
|
verifyChallenge
|
|
62
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-KXCKFIJI.js";
|
|
63
64
|
import {
|
|
64
65
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
65
66
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -99,7 +100,7 @@ import {
|
|
|
99
100
|
pinDimension,
|
|
100
101
|
withSpan,
|
|
101
102
|
withTraceContext
|
|
102
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-E4EVGPDX.js";
|
|
103
104
|
import {
|
|
104
105
|
FxRateUnavailableError,
|
|
105
106
|
InvalidCurrencyError,
|
|
@@ -140,7 +141,7 @@ import {
|
|
|
140
141
|
advertisedMethods,
|
|
141
142
|
challengeMeta,
|
|
142
143
|
isTempoInsufficientFundsError
|
|
143
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-NPIW5VR5.js";
|
|
144
145
|
|
|
145
146
|
// src/sdk/server/lock-pubkey-store.ts
|
|
146
147
|
async function initLockPubkeyTable(db) {
|
|
@@ -3074,7 +3075,8 @@ import {
|
|
|
3074
3075
|
Amount as Amount2,
|
|
3075
3076
|
CheckStateEnum,
|
|
3076
3077
|
getP2PKExpectedWitnessPubkeys,
|
|
3077
|
-
getTagInt
|
|
3078
|
+
getTagInt,
|
|
3079
|
+
verifyDleqIfPresent
|
|
3078
3080
|
} from "@cashu/cashu-ts";
|
|
3079
3081
|
function valueCashuFunding(wallet, proofs) {
|
|
3080
3082
|
let grossSats = Amount2.zero();
|
|
@@ -3091,6 +3093,9 @@ function valueCashuFunding(wallet, proofs) {
|
|
|
3091
3093
|
};
|
|
3092
3094
|
}
|
|
3093
3095
|
async function acceptAccumulatorPayment(opts) {
|
|
3096
|
+
if (opts.disposable && !opts.mints.every(isTestMintUrl)) {
|
|
3097
|
+
throw new Error("Disposable Cashu accepts only known test or loopback mints.");
|
|
3098
|
+
}
|
|
3094
3099
|
if (!opts.requestId) {
|
|
3095
3100
|
return {
|
|
3096
3101
|
kind: "rejected",
|
|
@@ -3115,6 +3120,16 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3115
3120
|
const walletProvider = opts.walletProvider ?? getWallet;
|
|
3116
3121
|
const wallet = await walletProvider(mintUrl);
|
|
3117
3122
|
proofs = wallet.decodeToken(opts.cashuToken).proofs;
|
|
3123
|
+
if (meta.unit !== "sat" || proofs.length === 0) {
|
|
3124
|
+
throw new Error("Cashu payment requires nonempty sat proofs.");
|
|
3125
|
+
}
|
|
3126
|
+
for (const proof of proofs) {
|
|
3127
|
+
if (!proof.dleq || !verifyDleqIfPresent(proof, wallet.getKeyset(proof.id))) {
|
|
3128
|
+
throw new Error(
|
|
3129
|
+
"Cashu payment requires valid mint-provided NUT-12 DLEQ evidence for every proof. Request a fresh token from a compatible mint."
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3118
3133
|
valuation = valueCashuFunding(wallet, proofs);
|
|
3119
3134
|
} catch (err) {
|
|
3120
3135
|
return {
|
|
@@ -3149,6 +3164,9 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3149
3164
|
};
|
|
3150
3165
|
}
|
|
3151
3166
|
const allowedNormalised = allowed.map(toLockPubkey);
|
|
3167
|
+
if (opts.disposable && (allowedNormalised.length !== 1 || (getTagInt(p.secret, "n_sigs") ?? 1) !== 1)) {
|
|
3168
|
+
return { kind: "rejected", code: "not_p2pk_locked", message: "Disposable Cashu requires an exclusive single-signature P2PK lock." };
|
|
3169
|
+
}
|
|
3152
3170
|
const match = accepted.find((pk) => allowedNormalised.includes(pk));
|
|
3153
3171
|
if (!match) {
|
|
3154
3172
|
return {
|
|
@@ -3185,6 +3203,9 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3185
3203
|
message: "One or more proofs are pending at the mint \u2014 retry shortly."
|
|
3186
3204
|
};
|
|
3187
3205
|
}
|
|
3206
|
+
if (states.some((state) => state.state !== CheckStateEnum.UNSPENT)) {
|
|
3207
|
+
return { kind: "rejected", code: "mint_unreachable", message: "Mint returned an unknown proof state." };
|
|
3208
|
+
}
|
|
3188
3209
|
} catch (err) {
|
|
3189
3210
|
return {
|
|
3190
3211
|
kind: "rejected",
|
|
@@ -3202,7 +3223,13 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3202
3223
|
tExpireMsByIndex
|
|
3203
3224
|
};
|
|
3204
3225
|
try {
|
|
3205
|
-
if (opts.
|
|
3226
|
+
if (opts.disposable) {
|
|
3227
|
+
const account = opts.withDisposableLedger;
|
|
3228
|
+
if (!account) throw new Error("Disposable Cashu requires atomic credit accounting.");
|
|
3229
|
+
await opts.disposable.commit(opts.requestId, proofs, (ledger) => account(ledger, acceptedValue));
|
|
3230
|
+
} else if (!opts.db) {
|
|
3231
|
+
throw new Error("Cashu accumulator requires a storage backend.");
|
|
3232
|
+
} else if (opts.withCommitTx) {
|
|
3206
3233
|
const withCommitTx = opts.withCommitTx;
|
|
3207
3234
|
const client = await opts.db.connect();
|
|
3208
3235
|
try {
|
|
@@ -3254,7 +3281,7 @@ function parseNut11LocktimeMs(secret) {
|
|
|
3254
3281
|
var UPFRONT_CHALLENGE_TTL_MS = 5 * 60 * 1e3;
|
|
3255
3282
|
var CONSUMED_CREDENTIAL_TTL_MS = 2 * UPFRONT_CHALLENGE_TTL_MS;
|
|
3256
3283
|
function devModeSkipsPaymentVerification(opts) {
|
|
3257
|
-
return Boolean(opts.devMode) && !opts.mints;
|
|
3284
|
+
return Boolean(opts.devMode) && !opts.mints && !opts.x402 && !opts.mpp && !opts.paymentMethods?.some((method) => method === "cashu" || method === "x402" || method === "tempo");
|
|
3258
3285
|
}
|
|
3259
3286
|
var PAYMENT_PROOF_KEYS = ["cashu_token", "x402_payment", "tempo_credential"];
|
|
3260
3287
|
function hasPaymentProof(content) {
|
|
@@ -3457,7 +3484,7 @@ async function verifyUpfrontPayment(opts) {
|
|
|
3457
3484
|
}
|
|
3458
3485
|
};
|
|
3459
3486
|
}
|
|
3460
|
-
if (opts.cashuMode === "p2pk-accumulator" && opts.accumulatorDb && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
3487
|
+
if (opts.cashuMode === "p2pk-accumulator" && (opts.accumulatorDb || opts.disposableCashu) && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
3461
3488
|
return verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx);
|
|
3462
3489
|
}
|
|
3463
3490
|
return {
|
|
@@ -3605,7 +3632,7 @@ async function verifyUpfrontPayment(opts) {
|
|
|
3605
3632
|
x402Requirements
|
|
3606
3633
|
});
|
|
3607
3634
|
}
|
|
3608
|
-
const { verifyX402Payment } = await import("./x402-
|
|
3635
|
+
const { verifyX402Payment } = await import("./x402-5H27DCBE.js");
|
|
3609
3636
|
const receipt = await verifyX402Payment(
|
|
3610
3637
|
x402Payment,
|
|
3611
3638
|
x402Config,
|
|
@@ -4454,7 +4481,7 @@ function buildPaymentErrorResponse(body, challenges, x402Requirements, status, x
|
|
|
4454
4481
|
async function verifyIncomingPayment(body, opts, snapshot) {
|
|
4455
4482
|
if (body.type !== "payment") return {};
|
|
4456
4483
|
if (body.content.cashu_token) {
|
|
4457
|
-
if (opts.cashuMode === "p2pk-accumulator" && opts.accumulatorDb && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
4484
|
+
if (opts.cashuMode === "p2pk-accumulator" && (opts.accumulatorDb || opts.disposableCashu) && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
4458
4485
|
const mints = opts.mints ?? [];
|
|
4459
4486
|
if (mints.length === 0) {
|
|
4460
4487
|
return {
|
|
@@ -4472,6 +4499,7 @@ async function verifyIncomingPayment(body, opts, snapshot) {
|
|
|
4472
4499
|
{
|
|
4473
4500
|
cashuToken: body.content.cashu_token,
|
|
4474
4501
|
accumulatorDb: opts.accumulatorDb,
|
|
4502
|
+
disposableCashu: opts.disposableCashu,
|
|
4475
4503
|
dvmId: opts.dvmId,
|
|
4476
4504
|
lockPubkeys: opts.lockPubkeys,
|
|
4477
4505
|
requestId,
|
|
@@ -4533,7 +4561,7 @@ async function verifyIncomingPayment(body, opts, snapshot) {
|
|
|
4533
4561
|
snapshot
|
|
4534
4562
|
});
|
|
4535
4563
|
}
|
|
4536
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4564
|
+
const { verifyX402Payment } = await import("./x402-5H27DCBE.js");
|
|
4537
4565
|
const receipt = await verifyX402Payment(
|
|
4538
4566
|
body.content.x402_payment,
|
|
4539
4567
|
opts.x402Config,
|
|
@@ -4897,7 +4925,7 @@ function applyPaymentInfoToJob(job, info) {
|
|
|
4897
4925
|
async function processIncomingPayment(job, body, opts) {
|
|
4898
4926
|
if (body.type !== "payment") return null;
|
|
4899
4927
|
if (body.content.cashu_token) {
|
|
4900
|
-
if (opts.cashuMode === "p2pk-accumulator" && opts.accumulatorDb && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
4928
|
+
if (opts.cashuMode === "p2pk-accumulator" && (opts.accumulatorDb || opts.disposableCashu) && opts.dvmId && opts.lockPubkeys && opts.lockPubkeys.length > 0) {
|
|
4901
4929
|
const mints = opts.mints ?? [];
|
|
4902
4930
|
if (mints.length === 0) {
|
|
4903
4931
|
return {
|
|
@@ -4912,6 +4940,7 @@ async function processIncomingPayment(job, body, opts) {
|
|
|
4912
4940
|
{
|
|
4913
4941
|
cashuToken: body.content.cashu_token,
|
|
4914
4942
|
accumulatorDb: opts.accumulatorDb,
|
|
4943
|
+
disposableCashu: opts.disposableCashu,
|
|
4915
4944
|
dvmId: opts.dvmId,
|
|
4916
4945
|
lockPubkeys: opts.lockPubkeys,
|
|
4917
4946
|
requestId,
|
|
@@ -4946,7 +4975,7 @@ async function processIncomingPayment(job, body, opts) {
|
|
|
4946
4975
|
try {
|
|
4947
4976
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
4948
4977
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
4949
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4978
|
+
const { verifyX402Payment } = await import("./x402-5H27DCBE.js");
|
|
4950
4979
|
const receipt = await verifyX402Payment(
|
|
4951
4980
|
body.content.x402_payment,
|
|
4952
4981
|
opts.x402Config,
|
|
@@ -5066,7 +5095,7 @@ function creditPendingPayment(job) {
|
|
|
5066
5095
|
}
|
|
5067
5096
|
async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
5068
5097
|
const { cashuToken, accumulatorDb, dvmId, lockPubkeys, mintHealthTracker } = opts;
|
|
5069
|
-
if (!cashuToken || !accumulatorDb || !dvmId || !lockPubkeys || lockPubkeys.length === 0) {
|
|
5098
|
+
if (!cashuToken || !accumulatorDb && !opts.disposableCashu || !dvmId || !lockPubkeys || lockPubkeys.length === 0) {
|
|
5070
5099
|
return {
|
|
5071
5100
|
paidMsats: 0,
|
|
5072
5101
|
receivedProofs: [],
|
|
@@ -5145,6 +5174,17 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5145
5174
|
requestId: opts.requestId,
|
|
5146
5175
|
mints,
|
|
5147
5176
|
db: accumulatorDb,
|
|
5177
|
+
disposable: opts.disposableCashu,
|
|
5178
|
+
withDisposableLedger: ledgerCtx && requestId ? async (ledger, accepted) => {
|
|
5179
|
+
credited = await fundAndDraw({ ...ledgerCtx, ledger }, {
|
|
5180
|
+
rail: "cashu",
|
|
5181
|
+
paymentId: requestId,
|
|
5182
|
+
paidMsats: accepted.paidMsats,
|
|
5183
|
+
grossPaidMsats: accepted.grossMsats,
|
|
5184
|
+
cashuInputFeeMsats: accepted.inputFeeMsats,
|
|
5185
|
+
mint: tokenMint
|
|
5186
|
+
});
|
|
5187
|
+
} : void 0,
|
|
5148
5188
|
dvmId,
|
|
5149
5189
|
lockPubkeys,
|
|
5150
5190
|
beforeCommit,
|
|
@@ -6360,8 +6400,8 @@ function authErrorBody(err, opts = {}) {
|
|
|
6360
6400
|
hint: "Regenerate the request with the current timestamp + a fresh nonce; sign with secp256k1+BIP-340 Schnorr over the canonical JSON form of the input."
|
|
6361
6401
|
};
|
|
6362
6402
|
}
|
|
6363
|
-
function infoSchemaUrl(requestUrl) {
|
|
6364
|
-
return `${new URL(requestUrl).origin}/v1/info`;
|
|
6403
|
+
function infoSchemaUrl(requestUrl, publicOrigin) {
|
|
6404
|
+
return `${publicOrigin ?? new URL(requestUrl).origin}/v1/info`;
|
|
6365
6405
|
}
|
|
6366
6406
|
function callerInputIssues(issues) {
|
|
6367
6407
|
if (!issues) return [];
|
|
@@ -9118,6 +9158,7 @@ function isRecord2(value) {
|
|
|
9118
9158
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
9119
9159
|
|
|
9120
9160
|
// src/sdk/server/context.ts
|
|
9161
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
9121
9162
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
9122
9163
|
|
|
9123
9164
|
// src/sdk/server/instrumented-kv.ts
|
|
@@ -9160,6 +9201,7 @@ function instrumentKVStore(inner) {
|
|
|
9160
9201
|
}
|
|
9161
9202
|
|
|
9162
9203
|
// src/sdk/server/context.ts
|
|
9204
|
+
var stepCostScope = new AsyncLocalStorage();
|
|
9163
9205
|
function buildContext(job, opts) {
|
|
9164
9206
|
const log = createConsoleLogger(job.id);
|
|
9165
9207
|
const instrumentedStore = instrumentKVStore(opts.store);
|
|
@@ -9180,6 +9222,45 @@ function buildContext(job, opts) {
|
|
|
9180
9222
|
let creditedToHandlerMsats = 0;
|
|
9181
9223
|
let creditedToHandlerMicro = 0;
|
|
9182
9224
|
let poolBasis = false;
|
|
9225
|
+
let declaredCostAmount = 0;
|
|
9226
|
+
let declaredCostCurrency;
|
|
9227
|
+
let declaredCostRevision = 0;
|
|
9228
|
+
let costPoisoned = false;
|
|
9229
|
+
const stepCostOwner = {};
|
|
9230
|
+
const declareCost = (declared) => {
|
|
9231
|
+
if (terminated("cost")) return;
|
|
9232
|
+
const currency = validateCurrency(declared.currency);
|
|
9233
|
+
const total = declaredCostAmount + declared.amount;
|
|
9234
|
+
const micro = Math.round(total * 1e6);
|
|
9235
|
+
if (!Number.isFinite(declared.amount) || declared.amount < 0 || !Number.isSafeInteger(micro)) {
|
|
9236
|
+
throw new RangeError(
|
|
9237
|
+
`ctx.cost amounts must each be a finite, non-negative number of currency units, and must total no more than ${Math.floor(Number.MAX_SAFE_INTEGER / 1e6)}; got ${String(declared.amount)}`
|
|
9238
|
+
);
|
|
9239
|
+
}
|
|
9240
|
+
declaredCostRevision += 1;
|
|
9241
|
+
job.costRevision = declaredCostRevision;
|
|
9242
|
+
const replayable = { amount: declared.amount, currency };
|
|
9243
|
+
const scope = stepCostScope.getStore();
|
|
9244
|
+
if (scope?.owner === stepCostOwner) {
|
|
9245
|
+
for (const frame of scope.frames) frame.push(replayable);
|
|
9246
|
+
}
|
|
9247
|
+
if (declaredCostCurrency !== void 0 && declaredCostCurrency !== currency) {
|
|
9248
|
+
costPoisoned = true;
|
|
9249
|
+
log.error("ctx.cost() currency switched mid-job \u2014 this job reports no cost", {
|
|
9250
|
+
declared: currency,
|
|
9251
|
+
pinned: declaredCostCurrency
|
|
9252
|
+
});
|
|
9253
|
+
}
|
|
9254
|
+
declaredCostCurrency ??= currency;
|
|
9255
|
+
if (costPoisoned) {
|
|
9256
|
+
job.costAmountMicro = void 0;
|
|
9257
|
+
job.costCurrency = void 0;
|
|
9258
|
+
return;
|
|
9259
|
+
}
|
|
9260
|
+
declaredCostAmount = total;
|
|
9261
|
+
job.costAmountMicro = micro;
|
|
9262
|
+
job.costCurrency = currency;
|
|
9263
|
+
};
|
|
9183
9264
|
return {
|
|
9184
9265
|
jobId: job.id,
|
|
9185
9266
|
tags: job.tags,
|
|
@@ -9572,10 +9653,21 @@ function buildContext(job, opts) {
|
|
|
9572
9653
|
providerMessage(job, "cancel", { reason: error });
|
|
9573
9654
|
runTerminal();
|
|
9574
9655
|
},
|
|
9656
|
+
cost: declareCost,
|
|
9575
9657
|
step: async (id, fn) => {
|
|
9576
|
-
if (job.stepCache.has(id))
|
|
9577
|
-
|
|
9578
|
-
|
|
9658
|
+
if (job.stepCache.has(id)) {
|
|
9659
|
+
const record = job.stepCache.getRecord(id);
|
|
9660
|
+
for (const declared of record.costs ?? []) declareCost(declared);
|
|
9661
|
+
return record.value;
|
|
9662
|
+
}
|
|
9663
|
+
const costs = [];
|
|
9664
|
+
const scope = stepCostScope.getStore();
|
|
9665
|
+
const parentFrames = scope?.owner === stepCostOwner ? scope.frames : [];
|
|
9666
|
+
const result = await stepCostScope.run(
|
|
9667
|
+
{ owner: stepCostOwner, frames: [...parentFrames, costs] },
|
|
9668
|
+
fn
|
|
9669
|
+
);
|
|
9670
|
+
job.stepCache.set(id, result, costs);
|
|
9579
9671
|
return result;
|
|
9580
9672
|
},
|
|
9581
9673
|
state: job.state,
|
|
@@ -9941,6 +10033,9 @@ async function reapExpiredKeysetRows(opts, db) {
|
|
|
9941
10033
|
// src/sdk/server/job-manager.ts
|
|
9942
10034
|
var STALE_JOB_WATCHDOG_HEADROOM_MS = 9e4;
|
|
9943
10035
|
var DEFAULT_PROCESSING_WATCHDOG_MS = 5 * 6e4;
|
|
10036
|
+
var TERMINAL_COST_RECOVERY_BATCH = 100;
|
|
10037
|
+
var TERMINAL_COST_RECOVERY_MAX_PAGES = 10;
|
|
10038
|
+
var TERMINAL_COST_RECOVERY_INTERVAL_MS = 3e4;
|
|
9944
10039
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
9945
10040
|
var DEFAULT_REACTIVATION_CLAIM_GRACE_MS = 150;
|
|
9946
10041
|
var REACTIVATION_CLAIM_POLL_MS = 25;
|
|
@@ -9959,7 +10054,12 @@ var ORPHAN_SWEEP_CLAIMS = /* @__PURE__ */ new WeakSet();
|
|
|
9959
10054
|
var ATTACH_RAILS = ["cashu", "x402", "tempo"];
|
|
9960
10055
|
var CREDIT_INBOUND_ATTEMPTS = 3;
|
|
9961
10056
|
var CREDIT_INBOUND_BACKOFF_MS = 50;
|
|
10057
|
+
var TERMINAL_COST_HANDOFF_RETRY_INITIAL_MS = 100;
|
|
10058
|
+
var TERMINAL_COST_HANDOFF_RETRY_MAX_MS = 3e4;
|
|
9962
10059
|
var CREDIT_INBOUND_READ_ATTEMPTS = 2;
|
|
10060
|
+
function isTerminalCostRecoveryStore(store) {
|
|
10061
|
+
return "saveForDvm" in store && "listUnreportedTerminalCosts" in store && "markTerminalCostReported" in store;
|
|
10062
|
+
}
|
|
9963
10063
|
var JobManager = class {
|
|
9964
10064
|
descriptor;
|
|
9965
10065
|
opts;
|
|
@@ -9967,6 +10067,8 @@ var JobManager = class {
|
|
|
9967
10067
|
jobStore;
|
|
9968
10068
|
idleTimers = /* @__PURE__ */ new Map();
|
|
9969
10069
|
cleanupTimers = /* @__PURE__ */ new Map();
|
|
10070
|
+
/** Cost revisions held only by a terminal handler owner until its durable merge succeeds. */
|
|
10071
|
+
terminalCostHandoffRetryTimers = /* @__PURE__ */ new Map();
|
|
9970
10072
|
/** Per-job NOTIFY unsubscribe handles for the durable-status watch (internal-review). */
|
|
9971
10073
|
statusWatchers = /* @__PURE__ */ new Map();
|
|
9972
10074
|
/** Job ids whose durable-status re-read is in flight — coalesces notify storms (internal-review). */
|
|
@@ -9990,6 +10092,7 @@ var JobManager = class {
|
|
|
9990
10092
|
staleSweepTimer;
|
|
9991
10093
|
heartbeatTimer;
|
|
9992
10094
|
orphanDrawSweepTimer;
|
|
10095
|
+
terminalCostRecoveryTimer;
|
|
9993
10096
|
jobRetentionSweepTimer;
|
|
9994
10097
|
jobRetentionBootTimer;
|
|
9995
10098
|
/** This manager holds {@link ORPHAN_SWEEP_CLAIMS} for its ledger. */
|
|
@@ -10007,6 +10110,8 @@ var JobManager = class {
|
|
|
10007
10110
|
monotonicNowMs = createMonotonicClock();
|
|
10008
10111
|
sweepInFlight = false;
|
|
10009
10112
|
orphanSweepInFlight = false;
|
|
10113
|
+
terminalCostRecoveryInFlight = false;
|
|
10114
|
+
terminalCostRecoveryResumeAfter;
|
|
10010
10115
|
jobRetentionSweepInFlight = false;
|
|
10011
10116
|
heartbeatInFlight = false;
|
|
10012
10117
|
/**
|
|
@@ -10132,6 +10237,19 @@ var JobManager = class {
|
|
|
10132
10237
|
}
|
|
10133
10238
|
}
|
|
10134
10239
|
}
|
|
10240
|
+
if (opts.dvmId && opts.onJobCost && isTerminalCostRecoveryStore(this.jobStore)) {
|
|
10241
|
+
this.terminalCostRecoveryTimer = setInterval(() => {
|
|
10242
|
+
void this.recoverTerminalCosts().catch((err) => {
|
|
10243
|
+
console.error(
|
|
10244
|
+
JSON.stringify({
|
|
10245
|
+
level: "terminal_cost_recovery_failed",
|
|
10246
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10247
|
+
})
|
|
10248
|
+
);
|
|
10249
|
+
});
|
|
10250
|
+
}, TERMINAL_COST_RECOVERY_INTERVAL_MS);
|
|
10251
|
+
this.terminalCostRecoveryTimer.unref();
|
|
10252
|
+
}
|
|
10135
10253
|
}
|
|
10136
10254
|
/** Active in-memory jobs. */
|
|
10137
10255
|
get activeJobs() {
|
|
@@ -10431,7 +10549,11 @@ var JobManager = class {
|
|
|
10431
10549
|
record.seq = Math.max(record.seq, highSeq);
|
|
10432
10550
|
}
|
|
10433
10551
|
}
|
|
10434
|
-
|
|
10552
|
+
if (this.opts.dvmId && isTerminalCostRecoveryStore(this.jobStore)) {
|
|
10553
|
+
await this.jobStore.saveForDvm(record, this.opts.dvmId);
|
|
10554
|
+
} else {
|
|
10555
|
+
await this.jobStore.save(record);
|
|
10556
|
+
}
|
|
10435
10557
|
}
|
|
10436
10558
|
/**
|
|
10437
10559
|
* Cross-machine terminal guard (internal-review). `buildContext`'s guard reads the
|
|
@@ -10472,6 +10594,7 @@ var JobManager = class {
|
|
|
10472
10594
|
job.pendingPrompts.clear();
|
|
10473
10595
|
job.pendingPayment = null;
|
|
10474
10596
|
abortJob(job, reason);
|
|
10597
|
+
const terminalCostPersisted = durable.status === "completed" ? true : await this.tryPersistAdoptedTerminalCost(job, 1);
|
|
10475
10598
|
if (durable.status !== "completed") {
|
|
10476
10599
|
localMessage(job, "provider", "cancel", { reason });
|
|
10477
10600
|
}
|
|
@@ -10486,8 +10609,78 @@ var JobManager = class {
|
|
|
10486
10609
|
this.clearIdleTimer(job.id);
|
|
10487
10610
|
void this.issueReceiptForStoredJob(job.id);
|
|
10488
10611
|
this.scheduleCleanup(job);
|
|
10612
|
+
if (durable.status !== "completed" && terminalCostPersisted) {
|
|
10613
|
+
await this.reportJobCost(job);
|
|
10614
|
+
}
|
|
10489
10615
|
return true;
|
|
10490
10616
|
}
|
|
10617
|
+
/**
|
|
10618
|
+
* Persist an adopted terminal cost, retaining the in-memory revision for a
|
|
10619
|
+
* later retry when the durable store is transiently unavailable.
|
|
10620
|
+
*/
|
|
10621
|
+
async tryPersistAdoptedTerminalCost(job, attempt) {
|
|
10622
|
+
try {
|
|
10623
|
+
await this.persistAdoptedTerminalCost(job);
|
|
10624
|
+
return true;
|
|
10625
|
+
} catch (err) {
|
|
10626
|
+
this.scheduleTerminalCostHandoffRetry(job, attempt, err);
|
|
10627
|
+
return false;
|
|
10628
|
+
}
|
|
10629
|
+
}
|
|
10630
|
+
/** Retry outside the status watcher, which correctly stops once the job is terminal. */
|
|
10631
|
+
scheduleTerminalCostHandoffRetry(job, attempt, err) {
|
|
10632
|
+
if (this.terminalCostHandoffRetryTimers.has(job.id)) return;
|
|
10633
|
+
const delayMs = Math.min(
|
|
10634
|
+
TERMINAL_COST_HANDOFF_RETRY_INITIAL_MS * 2 ** Math.min(attempt - 1, 8),
|
|
10635
|
+
TERMINAL_COST_HANDOFF_RETRY_MAX_MS
|
|
10636
|
+
);
|
|
10637
|
+
console.error(
|
|
10638
|
+
JSON.stringify({
|
|
10639
|
+
level: "terminal_cost_handoff_retry_scheduled",
|
|
10640
|
+
jobId: job.id,
|
|
10641
|
+
attempt,
|
|
10642
|
+
delayMs,
|
|
10643
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10644
|
+
})
|
|
10645
|
+
);
|
|
10646
|
+
const timer = setTimeout(() => {
|
|
10647
|
+
this.terminalCostHandoffRetryTimers.delete(job.id);
|
|
10648
|
+
void this.retryTerminalCostHandoff(job, attempt + 1);
|
|
10649
|
+
}, delayMs);
|
|
10650
|
+
timer.unref();
|
|
10651
|
+
this.terminalCostHandoffRetryTimers.set(job.id, timer);
|
|
10652
|
+
}
|
|
10653
|
+
async retryTerminalCostHandoff(job, attempt) {
|
|
10654
|
+
if (!await this.tryPersistAdoptedTerminalCost(job, attempt)) return;
|
|
10655
|
+
await this.reportJobCost(job);
|
|
10656
|
+
}
|
|
10657
|
+
/** Durably merge a terminal handler owner's latest cost before reporting it. */
|
|
10658
|
+
async persistAdoptedTerminalCost(job) {
|
|
10659
|
+
const dvmId = this.opts.dvmId;
|
|
10660
|
+
const localRevision = job.costRevision;
|
|
10661
|
+
if (localRevision === void 0 || !dvmId || !isTerminalCostRecoveryStore(this.jobStore)) {
|
|
10662
|
+
return;
|
|
10663
|
+
}
|
|
10664
|
+
const durable = await this.jobStore.get(job.id);
|
|
10665
|
+
if (!durable || durable.status !== job.status) {
|
|
10666
|
+
throw new Error(`Terminal cost merge lost durable job ${job.id}`);
|
|
10667
|
+
}
|
|
10668
|
+
if ((durable.costRevision ?? -1) > localRevision) {
|
|
10669
|
+
job.costAmountMicro = durable.costAmountMicro;
|
|
10670
|
+
job.costCurrency = durable.costCurrency;
|
|
10671
|
+
job.costRevision = durable.costRevision;
|
|
10672
|
+
return;
|
|
10673
|
+
}
|
|
10674
|
+
await this.jobStore.saveForDvm(
|
|
10675
|
+
{
|
|
10676
|
+
...durable,
|
|
10677
|
+
costAmountMicro: job.costAmountMicro,
|
|
10678
|
+
costCurrency: job.costCurrency,
|
|
10679
|
+
costRevision: localRevision
|
|
10680
|
+
},
|
|
10681
|
+
dvmId
|
|
10682
|
+
);
|
|
10683
|
+
}
|
|
10491
10684
|
/**
|
|
10492
10685
|
* Watch the durable status of a locally-active job (internal-review).
|
|
10493
10686
|
*
|
|
@@ -10763,7 +10956,7 @@ var JobManager = class {
|
|
|
10763
10956
|
);
|
|
10764
10957
|
if (claimed) {
|
|
10765
10958
|
swept++;
|
|
10766
|
-
await this.
|
|
10959
|
+
await this.finalizeStoredTerminal(record.id);
|
|
10767
10960
|
console.warn(
|
|
10768
10961
|
JSON.stringify({
|
|
10769
10962
|
level: awaiting ? "stale_job_cancelled" : "stale_job_failed",
|
|
@@ -11543,6 +11736,7 @@ var JobManager = class {
|
|
|
11543
11736
|
resourcePath: `/v1/job/${job.id}/messages`,
|
|
11544
11737
|
cashuMode: this.opts.cashuMode,
|
|
11545
11738
|
accumulatorDb: this.opts.cashuMode === "p2pk-accumulator" ? this.opts.db : void 0,
|
|
11739
|
+
disposableCashu: this.opts.disposableCashu,
|
|
11546
11740
|
dvmId: this.opts.dvmId,
|
|
11547
11741
|
lockPubkeys: extra?.lockPubkeys,
|
|
11548
11742
|
mintHealthTracker: extra?.mintHealthTracker,
|
|
@@ -11981,8 +12175,25 @@ var JobManager = class {
|
|
|
11981
12175
|
* `context.ts`). The gap is visible to callers — the `seq` series skips a
|
|
11982
12176
|
* number — which is precisely the completeness signal receipts exist for.
|
|
11983
12177
|
*/
|
|
12178
|
+
async observePaidCompletion(record) {
|
|
12179
|
+
const callback = this.opts.onPaidJobCompleted;
|
|
12180
|
+
if (!callback || record.status !== "completed" || !record.paymentRail || !record.creditId || !record.drawId || !this.opts.creditLedger) return;
|
|
12181
|
+
const draw = await this.opts.creditLedger.getDraw({ creditId: record.creditId, drawId: record.drawId }).catch(() => void 0);
|
|
12182
|
+
const rail = draw?.railValue.rail;
|
|
12183
|
+
const currency = draw?.railValue.creditCurrency;
|
|
12184
|
+
if (draw?.status !== "settled" || draw.amountMicro <= 0 || !currency || !rail || !isFundingRail(rail)) return;
|
|
12185
|
+
const testFunds = rail === "lightning" ? null : this.opts.paymentMode?.rails[rail].testFunds ?? null;
|
|
12186
|
+
try {
|
|
12187
|
+
await callback({ jobId: record.id, rail, amountMicro: draw.amountMicro, currency, testFunds });
|
|
12188
|
+
} catch (error) {
|
|
12189
|
+
console.error(`[${record.id}] Paid completion observer failed:`, error);
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
11984
12192
|
async issueReceipt(record) {
|
|
11985
|
-
if (record.receipt)
|
|
12193
|
+
if (record.receipt) {
|
|
12194
|
+
await this.observePaidCompletion(record);
|
|
12195
|
+
return record.receipt;
|
|
12196
|
+
}
|
|
11986
12197
|
if (!isReceiptableStatus(record.status)) return void 0;
|
|
11987
12198
|
let credit;
|
|
11988
12199
|
try {
|
|
@@ -12002,13 +12213,18 @@ var JobManager = class {
|
|
|
12002
12213
|
return void 0;
|
|
12003
12214
|
}
|
|
12004
12215
|
const issuer = this.opts.receiptIssuer;
|
|
12005
|
-
if (!issuer)
|
|
12216
|
+
if (!issuer) {
|
|
12217
|
+
await this.observePaidCompletion(record);
|
|
12218
|
+
return void 0;
|
|
12219
|
+
}
|
|
12006
12220
|
if (!isReceiptIssuingStore(this.jobStore)) return void 0;
|
|
12007
12221
|
const store = this.jobStore;
|
|
12008
12222
|
try {
|
|
12009
12223
|
const seq = await store.claimReceiptSeq(record.id);
|
|
12010
12224
|
const receipt = issuer.issue(record, seq, void 0, credit);
|
|
12011
|
-
|
|
12225
|
+
const saved = await store.saveReceipt(record.id, receipt) ?? receipt;
|
|
12226
|
+
await this.observePaidCompletion(record);
|
|
12227
|
+
return saved;
|
|
12012
12228
|
} catch (err) {
|
|
12013
12229
|
console.error(
|
|
12014
12230
|
JSON.stringify({
|
|
@@ -12135,6 +12351,7 @@ var JobManager = class {
|
|
|
12135
12351
|
async finalizeTerminal(job) {
|
|
12136
12352
|
await this.attachReceipt(job);
|
|
12137
12353
|
await this.persistJob(job).catch(() => void 0);
|
|
12354
|
+
await this.reportJobCost(job);
|
|
12138
12355
|
}
|
|
12139
12356
|
/**
|
|
12140
12357
|
* Issue a receipt for a job this process doesn't hold in `activeJobs` — a
|
|
@@ -12151,13 +12368,117 @@ var JobManager = class {
|
|
|
12151
12368
|
if (active && receipt) active.receipt = receipt;
|
|
12152
12369
|
return receipt;
|
|
12153
12370
|
}
|
|
12371
|
+
/**
|
|
12372
|
+
* Finish accounting for a terminal written directly to the store — the
|
|
12373
|
+
* stale reaper and a cancel handled on a different machine (internal-review).
|
|
12374
|
+
*/
|
|
12375
|
+
async finalizeStoredTerminal(jobId) {
|
|
12376
|
+
const receipt = await this.issueReceiptForStoredJob(jobId);
|
|
12377
|
+
const record = await this.jobStore.get(jobId).catch(() => void 0);
|
|
12378
|
+
if (record && isTerminal(record.status)) await this.reportJobCost(record);
|
|
12379
|
+
return receipt;
|
|
12380
|
+
}
|
|
12154
12381
|
/** Handle job reaching terminal state (completed, failed, cancelled). */
|
|
12155
12382
|
async handleJobTerminal(job) {
|
|
12156
12383
|
this.clearIdleTimer(job.id);
|
|
12157
12384
|
await this.attachReceipt(job);
|
|
12158
|
-
await this.bookRevenue(job);
|
|
12385
|
+
const booked = await this.bookRevenue(job);
|
|
12386
|
+
if (!booked) await this.reportJobCost(job);
|
|
12159
12387
|
this.scheduleCleanup(job);
|
|
12160
12388
|
}
|
|
12389
|
+
/**
|
|
12390
|
+
* Re-enqueue terminal cost-only rows left between the job commit and outbox
|
|
12391
|
+
* insert. The marker advances only after `onJobCost` resolves, which for the
|
|
12392
|
+
* SDK reporter means the local durable insert has committed; a crash after
|
|
12393
|
+
* that insert but before the marker can duplicate a payload, but the
|
|
12394
|
+
* revisioned platform contract makes that replay harmless.
|
|
12395
|
+
*/
|
|
12396
|
+
async recoverTerminalCosts() {
|
|
12397
|
+
const store = this.jobStore;
|
|
12398
|
+
const dvmId = this.opts.dvmId;
|
|
12399
|
+
if (!dvmId || !this.opts.onJobCost || !isTerminalCostRecoveryStore(store)) {
|
|
12400
|
+
return { examined: 0, queued: 0, complete: true };
|
|
12401
|
+
}
|
|
12402
|
+
if (this.terminalCostRecoveryInFlight) {
|
|
12403
|
+
return { examined: 0, queued: 0, complete: false };
|
|
12404
|
+
}
|
|
12405
|
+
this.terminalCostRecoveryInFlight = true;
|
|
12406
|
+
let examined = 0;
|
|
12407
|
+
let queued = 0;
|
|
12408
|
+
let cursor = this.terminalCostRecoveryResumeAfter;
|
|
12409
|
+
try {
|
|
12410
|
+
for (let page = 0; page < TERMINAL_COST_RECOVERY_MAX_PAGES; page++) {
|
|
12411
|
+
const candidates = await store.listUnreportedTerminalCosts({
|
|
12412
|
+
dvmId,
|
|
12413
|
+
limit: TERMINAL_COST_RECOVERY_BATCH,
|
|
12414
|
+
...cursor ? { afterJobId: cursor } : {}
|
|
12415
|
+
});
|
|
12416
|
+
for (const record of candidates) {
|
|
12417
|
+
examined++;
|
|
12418
|
+
cursor = record.id;
|
|
12419
|
+
if (await this.reportJobCost(record)) queued++;
|
|
12420
|
+
}
|
|
12421
|
+
if (candidates.length < TERMINAL_COST_RECOVERY_BATCH) {
|
|
12422
|
+
this.terminalCostRecoveryResumeAfter = void 0;
|
|
12423
|
+
if (examined > 0) {
|
|
12424
|
+
console.log(
|
|
12425
|
+
JSON.stringify({
|
|
12426
|
+
level: "info",
|
|
12427
|
+
event: "terminal_cost_recovery_complete",
|
|
12428
|
+
dvmId,
|
|
12429
|
+
examined,
|
|
12430
|
+
queued
|
|
12431
|
+
})
|
|
12432
|
+
);
|
|
12433
|
+
}
|
|
12434
|
+
return { examined, queued, complete: true };
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
this.terminalCostRecoveryResumeAfter = cursor;
|
|
12438
|
+
console.warn(
|
|
12439
|
+
JSON.stringify({
|
|
12440
|
+
level: "terminal_cost_recovery_truncated",
|
|
12441
|
+
dvmId,
|
|
12442
|
+
examined,
|
|
12443
|
+
queued,
|
|
12444
|
+
resumeAfterJobId: cursor
|
|
12445
|
+
})
|
|
12446
|
+
);
|
|
12447
|
+
return { examined, queued, complete: false };
|
|
12448
|
+
} finally {
|
|
12449
|
+
this.terminalCostRecoveryInFlight = false;
|
|
12450
|
+
}
|
|
12451
|
+
}
|
|
12452
|
+
/** Report the latest declared-cost state only when no revenue event carried it. */
|
|
12453
|
+
async reportJobCost(job) {
|
|
12454
|
+
const onJobCost = this.opts.onJobCost;
|
|
12455
|
+
if (!onJobCost || job.status !== "completed" && job.status !== "failed" && job.status !== "cancelled") {
|
|
12456
|
+
return false;
|
|
12457
|
+
}
|
|
12458
|
+
const costRevision = job.costRevision ?? (job.costAmountMicro !== void 0 && job.costCurrency !== void 0 ? 0 : void 0);
|
|
12459
|
+
if (costRevision === void 0) return false;
|
|
12460
|
+
const payload = {
|
|
12461
|
+
dvmId: this.opts.dvmId ?? "",
|
|
12462
|
+
jobId: job.id,
|
|
12463
|
+
...job.capability ? { capability: job.capability } : {},
|
|
12464
|
+
outcome: job.status,
|
|
12465
|
+
costRevision,
|
|
12466
|
+
cost: job.costAmountMicro !== void 0 && job.costCurrency !== void 0 ? { amountMicro: job.costAmountMicro, currency: job.costCurrency } : null
|
|
12467
|
+
};
|
|
12468
|
+
try {
|
|
12469
|
+
await onJobCost(payload);
|
|
12470
|
+
if (isTerminalCostRecoveryStore(this.jobStore)) {
|
|
12471
|
+
const marked = await this.jobStore.markTerminalCostReported(job.id, costRevision);
|
|
12472
|
+
if (!marked) {
|
|
12473
|
+
return false;
|
|
12474
|
+
}
|
|
12475
|
+
}
|
|
12476
|
+
} catch (err) {
|
|
12477
|
+
console.error(`[${job.id}] Job cost reporting failed:`, err);
|
|
12478
|
+
return false;
|
|
12479
|
+
}
|
|
12480
|
+
return true;
|
|
12481
|
+
}
|
|
12161
12482
|
/**
|
|
12162
12483
|
* Report a completed paid job's revenue (internal-review, re-keyed by internal-review).
|
|
12163
12484
|
* Fire-and-forget — the `RevenueReporter` owns persistence and retry.
|
|
@@ -12296,6 +12617,9 @@ var JobManager = class {
|
|
|
12296
12617
|
onJobCompleted({
|
|
12297
12618
|
dvmId: this.opts.dvmId ?? "",
|
|
12298
12619
|
jobId: job.id,
|
|
12620
|
+
// Legacy Postgres rows surface a missing capability as `""`. Keep
|
|
12621
|
+
// that absence distinct from every real capability name on the wire.
|
|
12622
|
+
...job.capability ? { capability: job.capability } : {},
|
|
12299
12623
|
paidMsats: bookedMsats,
|
|
12300
12624
|
paymentMint: job.paymentMint,
|
|
12301
12625
|
rail: job.paymentRail,
|
|
@@ -12303,6 +12627,11 @@ var JobManager = class {
|
|
|
12303
12627
|
nativeAmount: job.nativeAmount,
|
|
12304
12628
|
nativeAsset: job.nativeAsset,
|
|
12305
12629
|
cashuFlow: job.cashuFlow,
|
|
12630
|
+
// Absent unless the handler declared one, and absent is a fact: the
|
|
12631
|
+
// platform shows "no costs reported" rather than a zero cost and a
|
|
12632
|
+
// 100% margin (internal-review). Both halves or neither — a bare amount
|
|
12633
|
+
// in an unnamed currency is not a cost.
|
|
12634
|
+
...job.costAmountMicro !== void 0 && job.costCurrency !== void 0 ? { cost: { amountMicro: job.costAmountMicro, currency: job.costCurrency } } : {},
|
|
12306
12635
|
...credit
|
|
12307
12636
|
})
|
|
12308
12637
|
).catch((err) => {
|
|
@@ -12329,6 +12658,10 @@ var JobManager = class {
|
|
|
12329
12658
|
clearTimeout(timer);
|
|
12330
12659
|
this.cleanupTimers.delete(id);
|
|
12331
12660
|
}
|
|
12661
|
+
for (const [id, timer] of this.terminalCostHandoffRetryTimers) {
|
|
12662
|
+
clearTimeout(timer);
|
|
12663
|
+
this.terminalCostHandoffRetryTimers.delete(id);
|
|
12664
|
+
}
|
|
12332
12665
|
for (const id of [...this.statusWatchers.keys()]) {
|
|
12333
12666
|
this.unwatchDurableStatus(id);
|
|
12334
12667
|
}
|
|
@@ -12348,6 +12681,10 @@ var JobManager = class {
|
|
|
12348
12681
|
clearInterval(this.orphanDrawSweepTimer);
|
|
12349
12682
|
this.orphanDrawSweepTimer = void 0;
|
|
12350
12683
|
}
|
|
12684
|
+
if (this.terminalCostRecoveryTimer) {
|
|
12685
|
+
clearInterval(this.terminalCostRecoveryTimer);
|
|
12686
|
+
this.terminalCostRecoveryTimer = void 0;
|
|
12687
|
+
}
|
|
12351
12688
|
if (this.jobRetentionBootTimer) {
|
|
12352
12689
|
clearTimeout(this.jobRetentionBootTimer);
|
|
12353
12690
|
this.jobRetentionBootTimer = void 0;
|
|
@@ -13380,7 +13717,7 @@ var X402FacilitatorHealth = class {
|
|
|
13380
13717
|
facilitator: support.facilitator,
|
|
13381
13718
|
network: support.network,
|
|
13382
13719
|
reason: support.reason,
|
|
13383
|
-
message: "x402
|
|
13720
|
+
message: "Exact x402 is unavailable because the configured facilitator advertises no supported exact generation on this network. Configured batch settlement is evaluated separately; other rails are unaffected."
|
|
13384
13721
|
})
|
|
13385
13722
|
);
|
|
13386
13723
|
} else if (!support.exact.v1 || !support.exact.v2 || this.config.batchSettlement && !support.batchSettlement) {
|
|
@@ -15293,6 +15630,23 @@ function parseSerializedDleq(raw) {
|
|
|
15293
15630
|
return { e: dleq.e, s: dleq.s, ...dleq.r !== void 0 ? { r: dleq.r } : {} };
|
|
15294
15631
|
}
|
|
15295
15632
|
|
|
15633
|
+
// src/sdk/server/caller-facing-url.ts
|
|
15634
|
+
function resolvePublicOrigin(value) {
|
|
15635
|
+
if (!value) return void 0;
|
|
15636
|
+
try {
|
|
15637
|
+
const url = new URL(value);
|
|
15638
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
15639
|
+
return url.origin;
|
|
15640
|
+
} catch {
|
|
15641
|
+
return void 0;
|
|
15642
|
+
}
|
|
15643
|
+
}
|
|
15644
|
+
function callerFacingRequestUrl(requestUrl, publicOrigin) {
|
|
15645
|
+
if (!publicOrigin) return requestUrl;
|
|
15646
|
+
const request = new URL(requestUrl);
|
|
15647
|
+
return `${publicOrigin}${request.pathname}${request.search}`;
|
|
15648
|
+
}
|
|
15649
|
+
|
|
15296
15650
|
// src/sdk/server/credit-expiry-sweep.ts
|
|
15297
15651
|
function startCreditExpirySweep(ledger, opts = {}) {
|
|
15298
15652
|
const sweepExpiredCredits = ledger.sweepExpiredCredits?.bind(ledger);
|
|
@@ -16308,194 +16662,236 @@ function revenueReporterBannerState(s) {
|
|
|
16308
16662
|
return s.devMode ? "(dev \u2014 not reporting)" : "misconfigured";
|
|
16309
16663
|
}
|
|
16310
16664
|
async function createDVMServer(descriptor, opts) {
|
|
16311
|
-
if (process.env.DVMKIT_CASHU_MINTS && opts.cashuMode === void 0) {
|
|
16312
|
-
console.warn(
|
|
16313
|
-
JSON.stringify({
|
|
16314
|
-
level: "cashu_opts_env_ignored",
|
|
16315
|
-
message: "DVMKIT_CASHU_MINTS is set in env but cashuMode opt is undefined \u2014 cashu is NOT active. Pass cashuMode, lockPubkey, and db via createDVMHost to enable it."
|
|
16316
|
-
})
|
|
16317
|
-
);
|
|
16318
|
-
}
|
|
16319
|
-
const envMaybe = opts.env;
|
|
16320
|
-
const platformToken = opts.platformToken ?? envMaybe.DVMKIT_PLATFORM_TOKEN;
|
|
16321
|
-
const platformUrl = opts.platformUrl ?? envMaybe.DVMKIT_PLATFORM_URL;
|
|
16322
|
-
const dvmId = opts.dvmId;
|
|
16323
16665
|
let revenueReporter;
|
|
16324
16666
|
let payoutReporter;
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16667
|
+
let unsubscribeTempoSettlementReadiness;
|
|
16668
|
+
let creditExpirySweep;
|
|
16669
|
+
let x402ChannelStorage;
|
|
16670
|
+
let x402BatchSettlement;
|
|
16671
|
+
let server;
|
|
16672
|
+
let shutdownPromise;
|
|
16673
|
+
const shutdown = () => shutdownPromise ??= (async () => {
|
|
16674
|
+
const errors = [];
|
|
16675
|
+
const capture = async (operation) => {
|
|
16676
|
+
try {
|
|
16677
|
+
await operation();
|
|
16678
|
+
} catch (error) {
|
|
16679
|
+
errors.push(error);
|
|
16680
|
+
}
|
|
16681
|
+
};
|
|
16682
|
+
let settlementStopped;
|
|
16683
|
+
if (server) {
|
|
16684
|
+
try {
|
|
16685
|
+
settlementStopped = server.shutdown();
|
|
16686
|
+
} catch (error) {
|
|
16687
|
+
errors.push(error);
|
|
16688
|
+
}
|
|
16689
|
+
} else if (x402BatchSettlement) {
|
|
16690
|
+
try {
|
|
16691
|
+
settlementStopped = x402BatchSettlement.stop();
|
|
16692
|
+
} catch (error) {
|
|
16693
|
+
errors.push(error);
|
|
16694
|
+
}
|
|
16695
|
+
}
|
|
16696
|
+
await capture(() => payoutReporter?.stop());
|
|
16697
|
+
await capture(() => revenueReporter?.stop());
|
|
16698
|
+
await capture(() => creditExpirySweep?.stop());
|
|
16699
|
+
await capture(() => unsubscribeTempoSettlementReadiness?.());
|
|
16700
|
+
if (settlementStopped) await capture(() => settlementStopped);
|
|
16701
|
+
if (x402ChannelStorage) {
|
|
16702
|
+
await capture(() => x402ChannelStorage?.close());
|
|
16703
|
+
}
|
|
16704
|
+
if (errors.length === 1) throw errors[0];
|
|
16705
|
+
if (errors.length > 1) {
|
|
16706
|
+
throw new AggregateError(errors, `${String(errors.length)} DVM shutdown operations failed`);
|
|
16707
|
+
}
|
|
16708
|
+
})();
|
|
16709
|
+
try {
|
|
16710
|
+
if (process.env.DVMKIT_CASHU_MINTS && opts.cashuMode === void 0) {
|
|
16711
|
+
console.warn(
|
|
16712
|
+
JSON.stringify({
|
|
16713
|
+
level: "cashu_opts_env_ignored",
|
|
16714
|
+
message: "DVMKIT_CASHU_MINTS is set in env but cashuMode opt is undefined \u2014 cashu is NOT active. Pass cashuMode, lockPubkey, and db via createDVMHost to enable it."
|
|
16715
|
+
})
|
|
16716
|
+
);
|
|
16717
|
+
}
|
|
16718
|
+
const envMaybe = opts.env;
|
|
16719
|
+
const platformToken = opts.platformToken ?? envMaybe.DVMKIT_PLATFORM_TOKEN;
|
|
16720
|
+
const platformUrl = opts.platformUrl ?? envMaybe.DVMKIT_PLATFORM_URL;
|
|
16721
|
+
const dvmId = opts.dvmId;
|
|
16722
|
+
if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
|
|
16723
|
+
const { RevenueReporter } = await import("./revenue-reporter-ASZ7SHHH.js");
|
|
16724
|
+
const reporter = new RevenueReporter(opts.db, platformUrl, platformToken);
|
|
16725
|
+
revenueReporter = reporter;
|
|
16726
|
+
await reporter.init();
|
|
16727
|
+
reporter.startRetryLoop();
|
|
16728
|
+
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-RG6XNGPI.js");
|
|
16729
|
+
payoutReporter = new PayoutReporter({
|
|
16730
|
+
dvmId,
|
|
16731
|
+
db: opts.db,
|
|
16732
|
+
transport: reporter,
|
|
16733
|
+
// The same condition the admin routes and the accumulator monitor mount on.
|
|
16734
|
+
cashu: opts.cashuMode === "p2pk-accumulator" && !!opts.lockPubkey,
|
|
16735
|
+
...typeof opts.db.connect === "function" ? {} : { batchStore: new MemoryX402BatchStore() }
|
|
16736
|
+
});
|
|
16737
|
+
await payoutReporter.init();
|
|
16738
|
+
payoutReporter.start();
|
|
16739
|
+
}
|
|
16740
|
+
assertRevenueReporterReady({
|
|
16741
|
+
failFast: opts.failFast ?? false,
|
|
16742
|
+
devMode: opts.devMode,
|
|
16743
|
+
platformToken,
|
|
16744
|
+
platformUrl,
|
|
16333
16745
|
dvmId,
|
|
16334
|
-
|
|
16335
|
-
transport: reporter,
|
|
16336
|
-
// The same condition the admin routes and the accumulator monitor mount on.
|
|
16337
|
-
cashu: opts.cashuMode === "p2pk-accumulator" && !!opts.lockPubkey,
|
|
16338
|
-
...typeof opts.db.connect === "function" ? {} : { batchStore: new MemoryX402BatchStore() }
|
|
16746
|
+
hasPgPool: !!opts.db
|
|
16339
16747
|
});
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16344
|
-
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
dvmId
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
token: observation.token,
|
|
16373
|
-
operation: observation.operation,
|
|
16374
|
-
errorClass: observation.errorClass,
|
|
16375
|
-
checkedAt: observation.checkedAt,
|
|
16376
|
-
...observation.balanceMicro !== void 0 ? { balanceMicro: observation.balanceMicro.toString() } : {},
|
|
16377
|
-
...observation.channelId ? { channelId: observation.channelId } : {},
|
|
16378
|
-
...observation.creditId ? { creditId: observation.creditId } : {},
|
|
16379
|
-
...observation.drainId ? { drainId: observation.drainId } : {}
|
|
16380
|
-
}) : void 0
|
|
16381
|
-
});
|
|
16382
|
-
opts.tempoSettlementReadiness?.start();
|
|
16383
|
-
const creditLedger = opts.creditLedger ?? new MemoryCreditLedger();
|
|
16384
|
-
if (revenueReporter && dvmId) {
|
|
16385
|
-
creditLedger.useCreditExpiryReleaseOutbox?.({
|
|
16386
|
-
dvmId,
|
|
16387
|
-
enqueue: revenueReporter.enqueueCreditExpiryRelease.bind(revenueReporter)
|
|
16748
|
+
const activeRevenueReporter = revenueReporter;
|
|
16749
|
+
const effectiveOnJobCompleted = opts.onJobCompleted ?? (activeRevenueReporter ? (info) => activeRevenueReporter.report(info) : void 0);
|
|
16750
|
+
const effectiveOnJobCost = opts.onJobCost ?? (activeRevenueReporter ? (info) => activeRevenueReporter.reportCost(info) : void 0);
|
|
16751
|
+
const effectiveOnPaidJobDeath = opts.onPaidJobDeath ?? (activeRevenueReporter ? (info) => activeRevenueReporter.reportPaidJobDeath(info) : void 0);
|
|
16752
|
+
const effectiveOnRevenueSkippedNoRail = opts.onRevenueSkippedNoRail ?? (activeRevenueReporter ? (info) => activeRevenueReporter.reportRevenueSkippedNoRail(info) : void 0);
|
|
16753
|
+
const enqueueCreditDeposit = !opts.onCreditFunded && activeRevenueReporter ? activeRevenueReporter.enqueueDeposit.bind(activeRevenueReporter) : void 0;
|
|
16754
|
+
const enqueueCreditDrain = activeRevenueReporter ? activeRevenueReporter.enqueueCreditDrain.bind(activeRevenueReporter) : void 0;
|
|
16755
|
+
const enqueueCreditDrawRelease = activeRevenueReporter ? activeRevenueReporter.enqueueCreditDrawRelease.bind(activeRevenueReporter) : void 0;
|
|
16756
|
+
unsubscribeTempoSettlementReadiness = opts.tempoSettlementReadiness?.subscribe({
|
|
16757
|
+
onBalance: (observation) => activeRevenueReporter && dvmId ? activeRevenueReporter.reportTempoSettlementBalance({
|
|
16758
|
+
dvmId,
|
|
16759
|
+
address: observation.address,
|
|
16760
|
+
network: observation.network,
|
|
16761
|
+
token: observation.token,
|
|
16762
|
+
balanceMicro: observation.balanceMicro.toString(),
|
|
16763
|
+
lowBalanceMicro: observation.lowBalanceMicro.toString(),
|
|
16764
|
+
checkedAt: observation.checkedAt,
|
|
16765
|
+
ready: observation.ready
|
|
16766
|
+
}) : void 0,
|
|
16767
|
+
onFailure: (observation) => activeRevenueReporter && dvmId ? activeRevenueReporter.reportTempoSettlementFailure({
|
|
16768
|
+
dvmId,
|
|
16769
|
+
address: observation.address,
|
|
16770
|
+
network: observation.network,
|
|
16771
|
+
token: observation.token,
|
|
16772
|
+
operation: observation.operation,
|
|
16773
|
+
errorClass: observation.errorClass,
|
|
16774
|
+
checkedAt: observation.checkedAt,
|
|
16775
|
+
...observation.balanceMicro !== void 0 ? { balanceMicro: observation.balanceMicro.toString() } : {},
|
|
16776
|
+
...observation.channelId ? { channelId: observation.channelId } : {},
|
|
16777
|
+
...observation.creditId ? { creditId: observation.creditId } : {},
|
|
16778
|
+
...observation.drainId ? { drainId: observation.drainId } : {}
|
|
16779
|
+
}) : void 0
|
|
16388
16780
|
});
|
|
16389
|
-
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
const selfRelayEnabled = !!x402BatchConfig?.selfRelay;
|
|
16396
|
-
const effectiveX402BatchConfig = x402BatchConfig?.selfRelay ? {
|
|
16397
|
-
...x402BatchConfig,
|
|
16398
|
-
selfRelay: {
|
|
16399
|
-
...x402BatchConfig.selfRelay,
|
|
16400
|
-
onBalance: async (observation) => {
|
|
16401
|
-
if (revenueReporter && dvmId) {
|
|
16402
|
-
await revenueReporter.reportX402SelfRelayGasBalance({
|
|
16403
|
-
dvmId,
|
|
16404
|
-
address: observation.address,
|
|
16405
|
-
network: observation.network,
|
|
16406
|
-
balanceWei: observation.balanceWei.toString(),
|
|
16407
|
-
lowBalanceWei: observation.lowBalanceWei.toString(),
|
|
16408
|
-
checkedAt: observation.checkedAt
|
|
16409
|
-
});
|
|
16410
|
-
}
|
|
16411
|
-
await x402BatchConfig.selfRelay?.onBalance?.(observation);
|
|
16412
|
-
},
|
|
16413
|
-
onRpcHealth: async (observation) => {
|
|
16414
|
-
if (revenueReporter && dvmId) {
|
|
16415
|
-
await revenueReporter.reportX402SelfRelayRpcHealth({
|
|
16416
|
-
dvmId,
|
|
16417
|
-
...observation
|
|
16418
|
-
});
|
|
16419
|
-
}
|
|
16420
|
-
await x402BatchConfig.selfRelay?.onRpcHealth?.(observation);
|
|
16421
|
-
}
|
|
16422
|
-
}
|
|
16423
|
-
} : x402BatchConfig;
|
|
16424
|
-
const x402ChannelStorage = opts.db && x402Config && effectiveX402BatchConfig && !effectiveX402BatchConfig.storage ? new PostgresX402ChannelStorage(opts.db, {
|
|
16425
|
-
terminalizeUnbackedCredit: (evidence, tx) => creditLedger.terminalizeX402Credit(evidence, tx)
|
|
16426
|
-
}) : void 0;
|
|
16427
|
-
if (x402ChannelStorage) {
|
|
16428
|
-
await x402ChannelStorage.init();
|
|
16429
|
-
creditLedger.useX402SettlementGate?.(x402ChannelStorage);
|
|
16430
|
-
}
|
|
16431
|
-
const x402BatchSettlementFactory = x402Config && effectiveX402BatchConfig ? () => createX402BatchSettlementServer({
|
|
16432
|
-
channelStorage: x402ChannelStorage,
|
|
16433
|
-
network: x402Config.network,
|
|
16434
|
-
payTo: x402Config.payTo,
|
|
16435
|
-
facilitator: x402Config.facilitator,
|
|
16436
|
-
...x402Config.facilitatorAuth ? { facilitatorAuth: x402Config.facilitatorAuth } : {},
|
|
16437
|
-
config: effectiveX402BatchConfig,
|
|
16438
|
-
db: opts.db,
|
|
16439
|
-
allowInMemory: opts.devMode,
|
|
16440
|
-
earnedNative: (channelId) => creditLedger.earnedNativeForX402Channel(channelId),
|
|
16441
|
-
supportedResponse: selfRelayEnabled ? void 0 : opts.x402FacilitatorHealth?.supportedResponse?.(),
|
|
16442
|
-
payout: payoutReporter?.x402Observer()
|
|
16443
|
-
}) : void 0;
|
|
16444
|
-
const x402BatchSettlement = x402BatchSettlementFactory && (bootX402Support?.batchSettlement || selfRelayEnabled) ? await x402BatchSettlementFactory() : void 0;
|
|
16445
|
-
const x402ExactStore = x402Config && opts.db && typeof opts.db.connect === "function" ? new PostgresX402ExactSettlementStore(opts.db) : void 0;
|
|
16446
|
-
await x402ExactStore?.init();
|
|
16447
|
-
const x402ExactEvidence = x402Config && x402ExactStore ? await createX402SettlementEvidenceReader({
|
|
16448
|
-
network: x402Config.network,
|
|
16449
|
-
...x402BatchConfig?.rpcUrl ? { rpcUrl: x402BatchConfig.rpcUrl } : {}
|
|
16450
|
-
}) : void 0;
|
|
16451
|
-
const x402ExactSettlement = x402Config && x402ExactStore ? new X402ExactSettlementServer({
|
|
16452
|
-
store: x402ExactStore,
|
|
16453
|
-
evidence: x402ExactEvidence,
|
|
16454
|
-
repair: async (effect, receipt, tx) => {
|
|
16455
|
-
if (!receipt.txHash) throw new Error("settled exact x402 intent has no transaction");
|
|
16456
|
-
await repairX402ExactSettlementEffect({
|
|
16457
|
-
effect,
|
|
16458
|
-
paymentId: receipt.txHash,
|
|
16459
|
-
ledger: creditLedger,
|
|
16460
|
-
processedPayments,
|
|
16461
|
-
enqueueCreditDeposit,
|
|
16462
|
-
tx
|
|
16781
|
+
opts.tempoSettlementReadiness?.start();
|
|
16782
|
+
const creditLedger = opts.creditLedger ?? new MemoryCreditLedger();
|
|
16783
|
+
if (activeRevenueReporter && dvmId) {
|
|
16784
|
+
creditLedger.useCreditExpiryReleaseOutbox?.({
|
|
16785
|
+
dvmId,
|
|
16786
|
+
enqueue: activeRevenueReporter.enqueueCreditExpiryRelease.bind(activeRevenueReporter)
|
|
16463
16787
|
});
|
|
16464
16788
|
}
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
|
|
16476
|
-
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16789
|
+
creditExpirySweep = startCreditExpirySweep(creditLedger, opts.creditExpirySweep);
|
|
16790
|
+
const processedPayments = opts.processedPayments ?? new MemoryProcessedPaymentStore();
|
|
16791
|
+
const bootX402Support = opts.x402 ? opts.x402FacilitatorHealth?.support() ?? configuredX402FacilitatorSupport(opts.x402) : void 0;
|
|
16792
|
+
const x402Config = opts.x402;
|
|
16793
|
+
const x402BatchConfig = x402Config?.batchSettlement;
|
|
16794
|
+
const selfRelayEnabled = !!x402BatchConfig?.selfRelay;
|
|
16795
|
+
const effectiveX402BatchConfig = x402BatchConfig?.selfRelay ? {
|
|
16796
|
+
...x402BatchConfig,
|
|
16797
|
+
selfRelay: {
|
|
16798
|
+
...x402BatchConfig.selfRelay,
|
|
16799
|
+
onBalance: async (observation) => {
|
|
16800
|
+
if (activeRevenueReporter && dvmId) {
|
|
16801
|
+
await activeRevenueReporter.reportX402SelfRelayGasBalance({
|
|
16802
|
+
dvmId,
|
|
16803
|
+
address: observation.address,
|
|
16804
|
+
network: observation.network,
|
|
16805
|
+
balanceWei: observation.balanceWei.toString(),
|
|
16806
|
+
lowBalanceWei: observation.lowBalanceWei.toString(),
|
|
16807
|
+
checkedAt: observation.checkedAt
|
|
16808
|
+
});
|
|
16809
|
+
}
|
|
16810
|
+
await x402BatchConfig.selfRelay?.onBalance?.(observation);
|
|
16811
|
+
},
|
|
16812
|
+
onRpcHealth: async (observation) => {
|
|
16813
|
+
if (activeRevenueReporter && dvmId) {
|
|
16814
|
+
await activeRevenueReporter.reportX402SelfRelayRpcHealth({
|
|
16815
|
+
dvmId,
|
|
16816
|
+
...observation
|
|
16817
|
+
});
|
|
16818
|
+
}
|
|
16819
|
+
await x402BatchConfig.selfRelay?.onRpcHealth?.(observation);
|
|
16820
|
+
}
|
|
16493
16821
|
}
|
|
16494
|
-
}
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
|
|
16498
|
-
|
|
16822
|
+
} : x402BatchConfig;
|
|
16823
|
+
x402ChannelStorage = opts.db && x402Config && effectiveX402BatchConfig && !effectiveX402BatchConfig.storage ? new PostgresX402ChannelStorage(opts.db, {
|
|
16824
|
+
terminalizeUnbackedCredit: (evidence, tx) => creditLedger.terminalizeX402Credit(evidence, tx)
|
|
16825
|
+
}) : void 0;
|
|
16826
|
+
if (x402ChannelStorage) {
|
|
16827
|
+
await x402ChannelStorage.init();
|
|
16828
|
+
creditLedger.useX402SettlementGate?.(x402ChannelStorage);
|
|
16829
|
+
}
|
|
16830
|
+
const x402BatchSettlementFactory = x402Config && effectiveX402BatchConfig ? () => createX402BatchSettlementServer({
|
|
16831
|
+
channelStorage: x402ChannelStorage,
|
|
16832
|
+
network: x402Config.network,
|
|
16833
|
+
payTo: x402Config.payTo,
|
|
16834
|
+
facilitator: x402Config.facilitator,
|
|
16835
|
+
...x402Config.facilitatorAuth ? { facilitatorAuth: x402Config.facilitatorAuth } : {},
|
|
16836
|
+
config: effectiveX402BatchConfig,
|
|
16837
|
+
db: opts.db,
|
|
16838
|
+
allowInMemory: opts.devMode,
|
|
16839
|
+
earnedNative: (channelId) => creditLedger.earnedNativeForX402Channel(channelId),
|
|
16840
|
+
supportedResponse: selfRelayEnabled ? void 0 : opts.x402FacilitatorHealth?.supportedResponse?.(),
|
|
16841
|
+
payout: payoutReporter?.x402Observer()
|
|
16842
|
+
}) : void 0;
|
|
16843
|
+
x402BatchSettlement = x402BatchSettlementFactory && (bootX402Support?.batchSettlement || selfRelayEnabled) ? await x402BatchSettlementFactory() : void 0;
|
|
16844
|
+
const x402ExactStore = x402Config && opts.db && typeof opts.db.connect === "function" ? new PostgresX402ExactSettlementStore(opts.db) : void 0;
|
|
16845
|
+
await x402ExactStore?.init();
|
|
16846
|
+
const x402ExactEvidence = x402Config && x402ExactStore ? await createX402SettlementEvidenceReader({
|
|
16847
|
+
network: x402Config.network,
|
|
16848
|
+
...x402BatchConfig?.rpcUrl ? { rpcUrl: x402BatchConfig.rpcUrl } : {}
|
|
16849
|
+
}) : void 0;
|
|
16850
|
+
const x402ExactSettlement = x402Config && x402ExactStore ? new X402ExactSettlementServer({
|
|
16851
|
+
store: x402ExactStore,
|
|
16852
|
+
evidence: x402ExactEvidence,
|
|
16853
|
+
repair: async (effect, receipt, tx) => {
|
|
16854
|
+
if (!receipt.txHash) throw new Error("settled exact x402 intent has no transaction");
|
|
16855
|
+
await repairX402ExactSettlementEffect({
|
|
16856
|
+
effect,
|
|
16857
|
+
paymentId: receipt.txHash,
|
|
16858
|
+
ledger: creditLedger,
|
|
16859
|
+
processedPayments,
|
|
16860
|
+
enqueueCreditDeposit,
|
|
16861
|
+
tx
|
|
16862
|
+
});
|
|
16863
|
+
}
|
|
16864
|
+
}) : void 0;
|
|
16865
|
+
server = new DVMServer(descriptor, {
|
|
16866
|
+
...opts,
|
|
16867
|
+
creditLedger,
|
|
16868
|
+
processedPayments,
|
|
16869
|
+
onJobCompleted: effectiveOnJobCompleted,
|
|
16870
|
+
onJobCost: effectiveOnJobCost,
|
|
16871
|
+
onPaidJobDeath: effectiveOnPaidJobDeath,
|
|
16872
|
+
onRevenueSkippedNoRail: effectiveOnRevenueSkippedNoRail,
|
|
16873
|
+
onCreditFunded: opts.onCreditFunded,
|
|
16874
|
+
enqueueCreditDeposit,
|
|
16875
|
+
enqueueCreditDrain,
|
|
16876
|
+
enqueueCreditDrawRelease,
|
|
16877
|
+
x402BatchSettlement,
|
|
16878
|
+
x402BatchSettlementFactory,
|
|
16879
|
+
x402ExactSettlement,
|
|
16880
|
+
payoutReporter
|
|
16881
|
+
});
|
|
16882
|
+
await server.jobManager.recoverTerminalCosts();
|
|
16883
|
+
return {
|
|
16884
|
+
app: server.app,
|
|
16885
|
+
authAudience: server.authAudience,
|
|
16886
|
+
shutdown,
|
|
16887
|
+
jobManager: server.jobManager,
|
|
16888
|
+
revenueReporterActive: !!activeRevenueReporter,
|
|
16889
|
+
payoutReporter
|
|
16890
|
+
};
|
|
16891
|
+
} catch (initializationError) {
|
|
16892
|
+
await shutdown().catch(() => void 0);
|
|
16893
|
+
throw initializationError;
|
|
16894
|
+
}
|
|
16499
16895
|
}
|
|
16500
16896
|
function unknownRouteNotFound(c) {
|
|
16501
16897
|
c.set("unknownRoute", true);
|
|
@@ -16507,6 +16903,8 @@ var DVMServer = class {
|
|
|
16507
16903
|
mintHealthTracker;
|
|
16508
16904
|
descriptor;
|
|
16509
16905
|
opts;
|
|
16906
|
+
/** Validated `DVMKIT_PUBLIC_URL` origin, or request-derived URLs when absent. */
|
|
16907
|
+
publicOrigin;
|
|
16510
16908
|
consumedCredentialStore;
|
|
16511
16909
|
adminCashuNonceStore;
|
|
16512
16910
|
ownsMintHealthTracker;
|
|
@@ -16550,6 +16948,7 @@ var DVMServer = class {
|
|
|
16550
16948
|
this.descriptor = descriptor;
|
|
16551
16949
|
this.pricingCurrency = descriptor.currency ?? "usd";
|
|
16552
16950
|
this.opts = opts;
|
|
16951
|
+
this.publicOrigin = resolvePublicOrigin(opts.env.DVMKIT_PUBLIC_URL);
|
|
16553
16952
|
this.authAudience = this.resolveAuthAudience();
|
|
16554
16953
|
this.x402BatchSettlement = opts.x402BatchSettlement;
|
|
16555
16954
|
this.x402ExactSettlement = opts.x402ExactSettlement;
|
|
@@ -16568,7 +16967,6 @@ var DVMServer = class {
|
|
|
16568
16967
|
this.ownsMintHealthTracker = false;
|
|
16569
16968
|
} else if (opts.mints && opts.mints.length > 0) {
|
|
16570
16969
|
this.mintHealthTracker = new MintHealthTracker({ mints: opts.mints });
|
|
16571
|
-
this.mintHealthTracker.start();
|
|
16572
16970
|
this.ownsMintHealthTracker = true;
|
|
16573
16971
|
} else {
|
|
16574
16972
|
this.ownsMintHealthTracker = false;
|
|
@@ -16581,12 +16979,6 @@ var DVMServer = class {
|
|
|
16581
16979
|
this.creditLedger = opts.creditLedger ?? new MemoryCreditLedger();
|
|
16582
16980
|
this.opts.mpp?.useTempoCreditLedger?.(this.creditLedger);
|
|
16583
16981
|
this.processedPayments = opts.processedPayments ?? new MemoryProcessedPaymentStore();
|
|
16584
|
-
if (this.opts.tempoObserverHealth?.declareSessionCapability) {
|
|
16585
|
-
this.undeclareSessionCapability = this.opts.tempoObserverHealth.declareSessionCapability(
|
|
16586
|
-
() => this.tempoSessionCapability()
|
|
16587
|
-
);
|
|
16588
|
-
void this.opts.tempoObserverHealth.refresh?.();
|
|
16589
|
-
}
|
|
16590
16982
|
this.jobManager = new JobManager(descriptor, {
|
|
16591
16983
|
receiptIssuer: this.receiptIssuer,
|
|
16592
16984
|
creditLedger: this.creditLedger,
|
|
@@ -16615,6 +17007,7 @@ var DVMServer = class {
|
|
|
16615
17007
|
mpp: opts.mpp,
|
|
16616
17008
|
cashuMode: opts.cashuMode,
|
|
16617
17009
|
lockPubkey: opts.lockPubkey,
|
|
17010
|
+
disposableCashu: opts.disposableCashu,
|
|
16618
17011
|
db: opts.db,
|
|
16619
17012
|
dvmId: opts.dvmId,
|
|
16620
17013
|
authAudience: this.authAudience,
|
|
@@ -16631,6 +17024,9 @@ var DVMServer = class {
|
|
|
16631
17024
|
pricingCurrency: this.pricingCurrency,
|
|
16632
17025
|
onCreditFunded: opts.onCreditFunded,
|
|
16633
17026
|
onJobCompleted: opts.onJobCompleted,
|
|
17027
|
+
onPaidJobCompleted: opts.onPaidJobCompleted,
|
|
17028
|
+
paymentMode: opts.paymentMode,
|
|
17029
|
+
onJobCost: opts.onJobCost,
|
|
16634
17030
|
onPaidJobDeath: opts.onPaidJobDeath,
|
|
16635
17031
|
onRevenueSkippedNoRail: opts.onRevenueSkippedNoRail,
|
|
16636
17032
|
onJobRetentionSweepFailed: opts.onJobRetentionSweepFailed,
|
|
@@ -16638,19 +17034,44 @@ var DVMServer = class {
|
|
|
16638
17034
|
reactivationClaimGraceMs: opts.reactivationClaimGraceMs
|
|
16639
17035
|
});
|
|
16640
17036
|
this.app = new Hono();
|
|
16641
|
-
|
|
17037
|
+
try {
|
|
17038
|
+
this.registerRoutes();
|
|
17039
|
+
if (this.ownsMintHealthTracker) this.mintHealthTracker?.start();
|
|
17040
|
+
if (this.opts.tempoObserverHealth?.declareSessionCapability) {
|
|
17041
|
+
this.undeclareSessionCapability = this.opts.tempoObserverHealth.declareSessionCapability(
|
|
17042
|
+
() => this.tempoSessionCapability()
|
|
17043
|
+
);
|
|
17044
|
+
void this.opts.tempoObserverHealth.refresh?.();
|
|
17045
|
+
}
|
|
17046
|
+
} catch (error) {
|
|
17047
|
+
try {
|
|
17048
|
+
this.jobManager.shutdown();
|
|
17049
|
+
} catch {
|
|
17050
|
+
}
|
|
17051
|
+
try {
|
|
17052
|
+
if (this.ownsMintHealthTracker) this.mintHealthTracker?.stop();
|
|
17053
|
+
} catch {
|
|
17054
|
+
}
|
|
17055
|
+
try {
|
|
17056
|
+
this.undeclareSessionCapability?.();
|
|
17057
|
+
} catch {
|
|
17058
|
+
}
|
|
17059
|
+
this.undeclareSessionCapability = void 0;
|
|
17060
|
+
throw error;
|
|
17061
|
+
}
|
|
16642
17062
|
}
|
|
16643
17063
|
/**
|
|
16644
17064
|
* Tear down this mount. Every leg is synchronous except the batch-settlement
|
|
16645
17065
|
* server's own `stop()`; the returned promise settles once that has finished,
|
|
16646
17066
|
* so a caller holding a resource behind it — the settlement pool the spend
|
|
16647
17067
|
* gate's store owns (internal-review) — can sequence its close instead of pulling it
|
|
16648
|
-
* out from under an in-flight stop.
|
|
17068
|
+
* out from under an in-flight stop. A rejection remains observable so the app
|
|
17069
|
+
* and host teardown layers can finish later cleanup before surfacing it.
|
|
16649
17070
|
*/
|
|
16650
17071
|
shutdown() {
|
|
16651
17072
|
this.jobManager.shutdown();
|
|
16652
17073
|
const settlement = this.x402BatchSettlementInit ?? Promise.resolve(this.x402BatchSettlement);
|
|
16653
|
-
const stopped = settlement.then((s) => s?.stop())
|
|
17074
|
+
const stopped = settlement.then((s) => s?.stop());
|
|
16654
17075
|
if (this.ownsMintHealthTracker) this.mintHealthTracker?.stop();
|
|
16655
17076
|
this.undeclareSessionCapability?.();
|
|
16656
17077
|
this.undeclareSessionCapability = void 0;
|
|
@@ -16847,6 +17268,7 @@ var DVMServer = class {
|
|
|
16847
17268
|
* `cashu_not_p2pk_locked`).
|
|
16848
17269
|
*/
|
|
16849
17270
|
async resolveLockPubkeysForReceive() {
|
|
17271
|
+
if (this.opts.disposableCashu && this.opts.lockPubkey) return [toLockPubkey(this.opts.lockPubkey)];
|
|
16850
17272
|
if (this.opts.cashuMode !== "p2pk-accumulator" || !this.opts.db || !this.opts.dvmId || !this.opts.lockPubkey) {
|
|
16851
17273
|
return void 0;
|
|
16852
17274
|
}
|
|
@@ -17165,7 +17587,7 @@ var DVMServer = class {
|
|
|
17165
17587
|
defaultPaymentMethods() {
|
|
17166
17588
|
const methods = [];
|
|
17167
17589
|
if (this.opts.mints?.length) {
|
|
17168
|
-
const accumulatorReady = this.opts.cashuMode === "p2pk-accumulator" && !!this.opts.lockPubkey && !!this.opts.db;
|
|
17590
|
+
const accumulatorReady = this.opts.cashuMode === "p2pk-accumulator" && !!this.opts.lockPubkey && (!!this.opts.db || !!this.opts.disposableCashu);
|
|
17169
17591
|
if (accumulatorReady) methods.push("cashu");
|
|
17170
17592
|
}
|
|
17171
17593
|
if (this.opts.x402 && this.x402CurrencySupported()) methods.push("x402");
|
|
@@ -17350,7 +17772,7 @@ var DVMServer = class {
|
|
|
17350
17772
|
const authHeader = c.req.header("Authorization");
|
|
17351
17773
|
const { Credential } = await import("mppx");
|
|
17352
17774
|
const mppCredential = authHeader ? Credential.extractPaymentScheme(authHeader) ?? void 0 : void 0;
|
|
17353
|
-
let lockPubkeys;
|
|
17775
|
+
let lockPubkeys = this.opts.disposableCashu && this.opts.lockPubkey ? [toLockPubkey(this.opts.lockPubkey)] : void 0;
|
|
17354
17776
|
if (this.opts.cashuMode === "p2pk-accumulator" && this.opts.db && this.opts.dvmId && this.opts.lockPubkey) {
|
|
17355
17777
|
const active = await this.lockPubkeyStateForRead(this.opts.db, this.opts.dvmId);
|
|
17356
17778
|
lockPubkeys = activePubkeySet(active);
|
|
@@ -17368,7 +17790,7 @@ var DVMServer = class {
|
|
|
17368
17790
|
x402ExactSettlement: this.x402ExactSettlement,
|
|
17369
17791
|
x402ExactVersions: x402Support?.exact,
|
|
17370
17792
|
x402BatchSettlement,
|
|
17371
|
-
x402Resource: c.req.url,
|
|
17793
|
+
x402Resource: callerFacingRequestUrl(c.req.url, this.publicOrigin),
|
|
17372
17794
|
mppCredential,
|
|
17373
17795
|
mpp: this.opts.mpp,
|
|
17374
17796
|
resourcePath: c.req.path,
|
|
@@ -17384,6 +17806,7 @@ var DVMServer = class {
|
|
|
17384
17806
|
consumedCredentialStore: this.consumedCredentialStore,
|
|
17385
17807
|
cashuMode: this.opts.cashuMode,
|
|
17386
17808
|
accumulatorDb: this.opts.cashuMode === "p2pk-accumulator" ? this.opts.db : void 0,
|
|
17809
|
+
disposableCashu: this.opts.disposableCashu,
|
|
17387
17810
|
dvmId: this.opts.dvmId,
|
|
17388
17811
|
lockPubkeys,
|
|
17389
17812
|
requestId: c.req.header("X-Cashu-Request-Id"),
|
|
@@ -17570,6 +17993,7 @@ var DVMServer = class {
|
|
|
17570
17993
|
...this.opts.mpp.tempoConfiguration && {
|
|
17571
17994
|
recipient: this.opts.mpp.tempoConfiguration.recipient,
|
|
17572
17995
|
payment_token: this.opts.mpp.tempoConfiguration.paymentToken,
|
|
17996
|
+
chain_id: this.opts.mpp.tempoConfiguration.chainId,
|
|
17573
17997
|
...this.opts.mpp.tempoConfiguration.operator && {
|
|
17574
17998
|
operator: this.opts.mpp.tempoConfiguration.operator
|
|
17575
17999
|
},
|
|
@@ -17698,7 +18122,8 @@ var DVMServer = class {
|
|
|
17698
18122
|
500
|
|
17699
18123
|
);
|
|
17700
18124
|
}
|
|
17701
|
-
if (err instanceof SignedRequestError)
|
|
18125
|
+
if (err instanceof SignedRequestError)
|
|
18126
|
+
return buildAuthErrorResponse(c, err, this.publicOrigin);
|
|
17702
18127
|
throw err;
|
|
17703
18128
|
}
|
|
17704
18129
|
}
|
|
@@ -17723,7 +18148,7 @@ var DVMServer = class {
|
|
|
17723
18148
|
{ onError: () => ({ "error.code": "invalid_quote_data" }) }
|
|
17724
18149
|
);
|
|
17725
18150
|
} catch (err) {
|
|
17726
|
-
const schemaUrl = infoSchemaUrl(c.req.url);
|
|
18151
|
+
const schemaUrl = infoSchemaUrl(c.req.url, this.publicOrigin);
|
|
17727
18152
|
const { message, hint } = err instanceof z2.ZodError ? humanizeZodError(err, { schemaUrl }) : {
|
|
17728
18153
|
message: "Quote-input validation failed",
|
|
17729
18154
|
hint: `Inspect ${schemaUrl} for the expected input schema.`
|
|
@@ -17837,7 +18262,7 @@ var DVMServer = class {
|
|
|
17837
18262
|
*/
|
|
17838
18263
|
requireCapabilityFromBody(c, capability) {
|
|
17839
18264
|
const known = this.jobManager.capabilityNames();
|
|
17840
|
-
const infoUrl =
|
|
18265
|
+
const infoUrl = infoSchemaUrl(c.req.url, this.publicOrigin);
|
|
17841
18266
|
if (typeof capability !== "string" || capability.length === 0) {
|
|
17842
18267
|
return {
|
|
17843
18268
|
error: c.json(
|
|
@@ -17878,7 +18303,7 @@ var DVMServer = class {
|
|
|
17878
18303
|
try {
|
|
17879
18304
|
return { body: await c.req.json() };
|
|
17880
18305
|
} catch {
|
|
17881
|
-
const infoUrl =
|
|
18306
|
+
const infoUrl = infoSchemaUrl(c.req.url, this.publicOrigin);
|
|
17882
18307
|
return {
|
|
17883
18308
|
error: c.json(
|
|
17884
18309
|
{
|
|
@@ -17913,7 +18338,7 @@ var DVMServer = class {
|
|
|
17913
18338
|
* relayed to a human don't change with the status.
|
|
17914
18339
|
*/
|
|
17915
18340
|
invalidInputResponse(c, err) {
|
|
17916
|
-
const schemaUrl = infoSchemaUrl(c.req.url);
|
|
18341
|
+
const schemaUrl = infoSchemaUrl(c.req.url, this.publicOrigin);
|
|
17917
18342
|
const issues = zodIssuesOf(err);
|
|
17918
18343
|
if (issues) {
|
|
17919
18344
|
const inputIssues = callerInputIssues(issues);
|
|
@@ -18027,7 +18452,8 @@ var DVMServer = class {
|
|
|
18027
18452
|
500
|
|
18028
18453
|
);
|
|
18029
18454
|
}
|
|
18030
|
-
if (error instanceof SignedRequestError)
|
|
18455
|
+
if (error instanceof SignedRequestError)
|
|
18456
|
+
return buildAuthErrorResponse(c, error, this.publicOrigin);
|
|
18031
18457
|
throw error;
|
|
18032
18458
|
}
|
|
18033
18459
|
const signedRequestId = requestIdFromEnvelope(body.data);
|
|
@@ -18081,6 +18507,7 @@ var DVMServer = class {
|
|
|
18081
18507
|
const capabilityResult = this.requireCapabilityFromBody(c, body.capability);
|
|
18082
18508
|
if ("error" in capabilityResult) return capabilityResult.error;
|
|
18083
18509
|
const capabilityName = capabilityResult.name;
|
|
18510
|
+
pinDimension("capability", capabilityName);
|
|
18084
18511
|
const cap = this.descriptor.capabilities[capabilityName];
|
|
18085
18512
|
let parsedInput;
|
|
18086
18513
|
let deferredInputError;
|
|
@@ -18145,7 +18572,8 @@ var DVMServer = class {
|
|
|
18145
18572
|
500
|
|
18146
18573
|
);
|
|
18147
18574
|
}
|
|
18148
|
-
if (err instanceof SignedRequestError)
|
|
18575
|
+
if (err instanceof SignedRequestError)
|
|
18576
|
+
return buildAuthErrorResponse(c, err, this.publicOrigin);
|
|
18149
18577
|
throw err;
|
|
18150
18578
|
}
|
|
18151
18579
|
}
|
|
@@ -18378,7 +18806,7 @@ var DVMServer = class {
|
|
|
18378
18806
|
} catch (err) {
|
|
18379
18807
|
if (err instanceof SignedRequestError && err.sub_reason === "replay_detected" && exactAdmission?.replayed) {
|
|
18380
18808
|
} else if (err instanceof SignedRequestError) {
|
|
18381
|
-
return buildAuthErrorResponse(c, err);
|
|
18809
|
+
return buildAuthErrorResponse(c, err, this.publicOrigin);
|
|
18382
18810
|
} else {
|
|
18383
18811
|
throw err;
|
|
18384
18812
|
}
|
|
@@ -18936,8 +19364,7 @@ var DVMServer = class {
|
|
|
18936
19364
|
summary,
|
|
18937
19365
|
lastActivityAt: Date.now()
|
|
18938
19366
|
});
|
|
18939
|
-
const
|
|
18940
|
-
const infoUrl = `${baseUrl.origin}/v1/info`;
|
|
19367
|
+
const infoUrl = infoSchemaUrl(c.req.url, this.publicOrigin);
|
|
18941
19368
|
return c.json(
|
|
18942
19369
|
{
|
|
18943
19370
|
error: "unknown_capability",
|
|
@@ -18960,7 +19387,7 @@ var DVMServer = class {
|
|
|
18960
19387
|
summary: "Persisted auth envelope failed signature re-verification on reactivation",
|
|
18961
19388
|
lastActivityAt: Date.now()
|
|
18962
19389
|
}).catch(() => void 0);
|
|
18963
|
-
return buildAuthErrorResponse(c, err);
|
|
19390
|
+
return buildAuthErrorResponse(c, err, this.publicOrigin);
|
|
18964
19391
|
}
|
|
18965
19392
|
if (err instanceof ReactivationAuthSchemaError) {
|
|
18966
19393
|
return c.json(
|
|
@@ -19077,13 +19504,13 @@ var DVMServer = class {
|
|
|
19077
19504
|
if (isStreamableJobStore(store)) {
|
|
19078
19505
|
const cancelled = await store.cancelJob(id, reason ?? "Job cancelled");
|
|
19079
19506
|
if (!cancelled) return this.jobNotActiveResponse(c);
|
|
19080
|
-
await jm.
|
|
19507
|
+
await jm.finalizeStoredTerminal(id);
|
|
19081
19508
|
return c.json({ status: "cancelled" });
|
|
19082
19509
|
}
|
|
19083
19510
|
record.status = "cancelled";
|
|
19084
19511
|
record.lastActivityAt = Date.now();
|
|
19085
19512
|
await jm.store.save(record);
|
|
19086
|
-
await jm.
|
|
19513
|
+
await jm.finalizeStoredTerminal(id);
|
|
19087
19514
|
return c.json({ status: "cancelled" });
|
|
19088
19515
|
}
|
|
19089
19516
|
if (!isJobOwner(c, job)) return c.json({ error: "Job not found" }, 404);
|
|
@@ -19350,8 +19777,8 @@ var PaymentValidationError = class extends Error {
|
|
|
19350
19777
|
}
|
|
19351
19778
|
detail;
|
|
19352
19779
|
};
|
|
19353
|
-
function buildAuthErrorResponse(c, err) {
|
|
19354
|
-
return c.json(authErrorBody(err, { schemaUrl: infoSchemaUrl(c.req.url) }), 401);
|
|
19780
|
+
function buildAuthErrorResponse(c, err, publicOrigin) {
|
|
19781
|
+
return c.json(authErrorBody(err, { schemaUrl: infoSchemaUrl(c.req.url, publicOrigin) }), 401);
|
|
19355
19782
|
}
|
|
19356
19783
|
function validateMessageContent(type, content, devMode) {
|
|
19357
19784
|
if (type === "response") {
|
|
@@ -19545,6 +19972,7 @@ export {
|
|
|
19545
19972
|
pruneRetired,
|
|
19546
19973
|
DEFAULT_LOCK_PUBKEY_GRACE_SECONDS,
|
|
19547
19974
|
activePubkeySet,
|
|
19975
|
+
AccumulatorReplayError,
|
|
19548
19976
|
initWalletAccumulatorTable,
|
|
19549
19977
|
insertAccumulatorRows,
|
|
19550
19978
|
clearAccumulatorForDvm,
|