@dvmkit/sdk 0.2.0-rc.9 → 0.2.1-rc.9
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 +2 -0
- package/dist/{chunk-V7EVFLAK.js → chunk-2D75MNNG.js} +154 -523
- package/dist/{chunk-BIFLRKMO.js → chunk-2KGG5ORW.js} +1 -1
- package/dist/{chunk-5URG56JJ.js → chunk-A4CM2BD3.js} +18 -2
- package/dist/{chunk-FT6HTUM4.js → chunk-AJNFXR7J.js} +1 -1
- package/dist/{chunk-S3XAHZQY.js → chunk-COYKJRMM.js} +24 -3
- package/dist/{chunk-UP2F5RRT.js → chunk-D7YQYLLM.js} +1 -4
- package/dist/{chunk-OMIQMMME.js → chunk-FPOLJRBF.js} +3 -3
- package/dist/{chunk-QP53RWAD.js → chunk-JAHRF5X3.js} +1 -1
- package/dist/{chunk-PCUQZDZA.js → chunk-JNKQZVZX.js} +95 -321
- package/dist/{chunk-JDT5LCJC.js → chunk-MD3H3BF7.js} +1 -1
- package/dist/{chunk-KSQEFVFJ.js → chunk-MMCE7XSR.js} +1 -1
- package/dist/{chunk-MRAGS5VP.js → chunk-N5B66UOS.js} +4 -16
- package/dist/{chunk-2ATUAUAO.js → chunk-O6D72KYA.js} +2 -2
- package/dist/{chunk-KXCKFIJI.js → chunk-QX52F6IB.js} +1 -1
- package/dist/{chunk-PHHAYRQV.js → chunk-U5VNEALI.js} +23 -10
- package/dist/{chunk-JLXYOV4Y.js → chunk-V5ZRATJH.js} +1 -1
- package/dist/{chunk-6GRIKOFB.js → chunk-VA7GT6C6.js} +5 -16
- package/dist/{chunk-O2X2CCKH.js → chunk-VIVXB4CD.js} +1 -1
- package/dist/{chunk-L67WTZX2.js → chunk-XMDQOXHO.js} +10 -33
- package/dist/{chunk-QT4ONTST.js → chunk-XRLKGCAW.js} +1 -1
- package/dist/{chunk-KMZXTBLA.js → chunk-XZ7LW4S5.js} +0 -1
- package/dist/{chunk-XQXJKJ3P.js → chunk-YO56VCTQ.js} +0 -4
- package/dist/{credit-ledger-5ZEJRI46.js → credit-ledger-MHA652TV.js} +2 -2
- package/dist/{credit-menu-D4Gcdgc4.d.ts → credit-menu-kDA9RVzD.d.ts} +135 -532
- package/dist/{fx-B0SLBe5x.d.ts → fx-DhyT1Cjh.d.ts} +1 -1
- package/dist/index.d.ts +5 -17
- package/dist/internal/caller.d.ts +109 -698
- package/dist/internal/caller.js +10 -12
- package/dist/internal/server.d.ts +17 -42
- package/dist/internal/server.js +15 -15
- package/dist/{job-store-B2uZvga4.d.ts → job-store-D3CcZp_g.d.ts} +6 -10
- package/dist/{lightning-backend-CQBnQgsT.d.ts → lightning-backend-BhsyrBLa.d.ts} +1 -15
- package/dist/{memory-credit-ledger-ZOH6C3N4.js → memory-credit-ledger-BYBATRN3.js} +3 -3
- package/dist/{mpp-secret-state-WNAQQ6K4.js → mpp-secret-state-B5GCILVH.js} +1 -1
- package/dist/{mpp-setup-IOJBF7DB.js → mpp-setup-MPP3ST5X.js} +1 -1
- package/dist/{payout-reporter-5PIRYFVQ.js → payout-reporter-KEMUXEUO.js} +2 -2
- package/dist/{postgres-consumed-credential-store-ISRHBMOU.js → postgres-consumed-credential-store-6N2V5MJN.js} +1 -1
- package/dist/postgres-job-store-MZZZMS2B.js +7 -0
- package/dist/postgres-kv-store-DXGTKXLU.js +7 -0
- package/dist/{postgres-replay-store-IZFLTTAC.js → postgres-replay-store-ZF4JNZOW.js} +2 -2
- package/dist/{processed-payment-store-FIDI3RNH.js → processed-payment-store-GKMAWOM3.js} +2 -2
- package/dist/revenue-reporter-O75BGYFK.js +7 -0
- package/dist/server/index.d.ts +34 -140
- package/dist/server/index.js +52 -124
- package/dist/{step-cache-CXg7ziML.d.ts → step-cache-v2yhyhSZ.d.ts} +359 -1112
- package/dist/{tempo-charge-store-76TDAF34.js → tempo-charge-store-SDTUNTKA.js} +2 -2
- package/dist/{tempo-lifecycle-DXM7QXJQ.js → tempo-lifecycle-G5SB4HFC.js} +4 -4
- package/dist/{tempo-session-store-2JNOKJGX.js → tempo-session-store-WF3VZXNF.js} +2 -2
- package/dist/{tempo-wallet-O67H5M4N.js → tempo-wallet-ZFNUDFVO.js} +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/{usd-BNDg1715.d.ts → usd-Cu9G6xv2.d.ts} +1 -1
- package/dist/{x402-7S2EFINY.js → x402-CI6PSHNO.js} +4 -4
- package/package.json +2 -2
- package/dist/postgres-job-store-OGQ6IT4U.js +0 -7
- package/dist/postgres-kv-store-D5E2EZ24.js +0 -7
- package/dist/revenue-reporter-NNCNRY4C.js +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MemoryProcessedPaymentStore,
|
|
3
3
|
ProcessedPaymentReplayError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XRLKGCAW.js";
|
|
5
5
|
import {
|
|
6
6
|
createX402FacilitatorAuthHeaders,
|
|
7
7
|
createX402FacilitatorClient,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-BQ2NMWKE.js";
|
|
11
11
|
import {
|
|
12
12
|
MemoryCreditLedger
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-U5VNEALI.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-MD3H3BF7.js";
|
|
39
39
|
import {
|
|
40
40
|
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
41
41
|
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
toLockPubkey,
|
|
61
61
|
verifyCashuToken,
|
|
62
62
|
verifyChallenge
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-QX52F6IB.js";
|
|
64
64
|
import {
|
|
65
65
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
66
66
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
msatsToUsd,
|
|
84
84
|
msatsToUsdc,
|
|
85
85
|
usdcToMsats
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-A4CM2BD3.js";
|
|
87
87
|
import {
|
|
88
88
|
resolveRpcOverride
|
|
89
89
|
} from "./chunk-MKI6OVW4.js";
|
|
@@ -100,7 +100,7 @@ import {
|
|
|
100
100
|
pinDimension,
|
|
101
101
|
withSpan,
|
|
102
102
|
withTraceContext
|
|
103
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-YO56VCTQ.js";
|
|
104
104
|
import {
|
|
105
105
|
FxRateUnavailableError,
|
|
106
106
|
InvalidCurrencyError,
|
|
@@ -130,10 +130,10 @@ import {
|
|
|
130
130
|
isNonChannelBitcoinRail,
|
|
131
131
|
netOwedSats,
|
|
132
132
|
x402SettlementPending
|
|
133
|
-
} from "./chunk-
|
|
133
|
+
} from "./chunk-2D75MNNG.js";
|
|
134
134
|
import {
|
|
135
135
|
withSdkInitLock
|
|
136
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-COYKJRMM.js";
|
|
137
137
|
import {
|
|
138
138
|
withPostgresRecoveryContext
|
|
139
139
|
} from "./chunk-C3MTFLC6.js";
|
|
@@ -141,7 +141,7 @@ import {
|
|
|
141
141
|
advertisedMethods,
|
|
142
142
|
challengeMeta,
|
|
143
143
|
isTempoInsufficientFundsError
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-XZ7LW4S5.js";
|
|
145
145
|
|
|
146
146
|
// src/sdk/server/lock-pubkey-store.ts
|
|
147
147
|
async function initLockPubkeyTable(db) {
|
|
@@ -881,16 +881,13 @@ async function buildCreditMenu(args) {
|
|
|
881
881
|
ttl_ms: args.config.ttlSeconds * 1e3,
|
|
882
882
|
currency,
|
|
883
883
|
funding,
|
|
884
|
-
//
|
|
885
|
-
// sessions only would otherwise lose the whole block the moment the gate
|
|
886
|
-
// closed, taking its capability signal with it.
|
|
884
|
+
// Keep withheld Tempo capability discoverable even when no method is currently offered.
|
|
887
885
|
...(tempoMethods.length > 0 || tempoWithheld.length > 0) && {
|
|
888
886
|
tempo: {
|
|
889
887
|
methods: tempoMethods,
|
|
890
888
|
...tempoWithheld.length > 0 && { withheld: tempoWithheld }
|
|
891
889
|
}
|
|
892
890
|
},
|
|
893
|
-
// Tied to the rail's own presence, the way `lightning_min_micro` is below:
|
|
894
891
|
// a builder who overrides `paymentMethods` can drop x402 from `funding`
|
|
895
892
|
// while the rail stays wired, and terms for a rail the menu doesn't offer
|
|
896
893
|
// are the same misadvertisement in the other direction.
|
|
@@ -3651,7 +3648,7 @@ async function verifyUpfrontPayment(opts) {
|
|
|
3651
3648
|
x402Requirements
|
|
3652
3649
|
});
|
|
3653
3650
|
}
|
|
3654
|
-
const { verifyX402Payment } = await import("./x402-
|
|
3651
|
+
const { verifyX402Payment } = await import("./x402-CI6PSHNO.js");
|
|
3655
3652
|
const receipt = await verifyX402Payment(
|
|
3656
3653
|
x402Payment,
|
|
3657
3654
|
x402Config,
|
|
@@ -4585,7 +4582,7 @@ async function verifyIncomingPayment(body, opts, snapshot) {
|
|
|
4585
4582
|
snapshot
|
|
4586
4583
|
});
|
|
4587
4584
|
}
|
|
4588
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4585
|
+
const { verifyX402Payment } = await import("./x402-CI6PSHNO.js");
|
|
4589
4586
|
const receipt = await verifyX402Payment(
|
|
4590
4587
|
body.content.x402_payment,
|
|
4591
4588
|
opts.x402Config,
|
|
@@ -5002,7 +4999,7 @@ async function processIncomingPayment(job, body, opts) {
|
|
|
5002
4999
|
try {
|
|
5003
5000
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5004
5001
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5005
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5002
|
+
const { verifyX402Payment } = await import("./x402-CI6PSHNO.js");
|
|
5006
5003
|
const receipt = await verifyX402Payment(
|
|
5007
5004
|
body.content.x402_payment,
|
|
5008
5005
|
opts.x402Config,
|
|
@@ -6691,14 +6688,8 @@ var CREDIT_REQUEST_SCHEMA = z.object({
|
|
|
6691
6688
|
/** Client-generated idempotency key — the same id polls/collects one drain. */
|
|
6692
6689
|
drain_id: z.string().min(1),
|
|
6693
6690
|
/**
|
|
6694
|
-
*
|
|
6695
|
-
*
|
|
6696
|
-
* Deliberately a bare string rather than the `DrainMethod` enum, so a
|
|
6697
|
-
* rail this build does not carry is answered by `validateDrainTarget`
|
|
6698
|
-
* with a typed `drain_method_unsupported` naming the rails that do
|
|
6699
|
-
* work — not by a Zod parse failure that says only "invalid enum". The
|
|
6700
|
-
* caller of a removed rail (`lightning`) and the caller of a
|
|
6701
|
-
* typo deserve the same actionable refusal.
|
|
6691
|
+
* Required on a fresh drain, optional on poll. Keep this a string so an unsupported
|
|
6692
|
+
* rail receives its specific typed refusal before payout-shape validation.
|
|
6702
6693
|
*/
|
|
6703
6694
|
method: z.string().min(1).optional(),
|
|
6704
6695
|
/**
|
|
@@ -6824,17 +6815,12 @@ async function handleBalance(c, deps, args) {
|
|
|
6824
6815
|
const primary = pickPrimary(all, args.menu.currency);
|
|
6825
6816
|
return c.json({
|
|
6826
6817
|
op: "balance",
|
|
6827
|
-
//
|
|
6828
|
-
//
|
|
6818
|
+
// Keep expired credit records visible. Nominal expiry alone does not release ownership;
|
|
6819
|
+
// an expiry sweep can later zero the undrawn balance.
|
|
6829
6820
|
credits: all.map(
|
|
6830
6821
|
(credit) => creditBody(credit, pendingChannels.has(credit.tempoChannelId ?? ""))
|
|
6831
6822
|
),
|
|
6832
6823
|
...primary ? { credit: creditBody(primary, pendingChannels.has(primary.tempoChannelId ?? "")) } : {},
|
|
6833
|
-
// the funding menu the quote and 402 surfaces already advertise,
|
|
6834
|
-
// echoed here so one read answers "what do I hold, and what may I add".
|
|
6835
|
-
// Under the key `menu`, not `credit` — that one is the credit projection on
|
|
6836
|
-
// this route. Carried without the caller echo fields: `credits[]` above says
|
|
6837
|
-
// the same thing in more detail, so `deps.menu()` is called without a pubkey.
|
|
6838
6824
|
menu: args.menu
|
|
6839
6825
|
});
|
|
6840
6826
|
}
|
|
@@ -7113,11 +7099,8 @@ async function completeTempoDrain(c, deps, record, expectedChannelId, credit) {
|
|
|
7113
7099
|
resourcePath: c.req.path,
|
|
7114
7100
|
action: "close",
|
|
7115
7101
|
expectedChannelId,
|
|
7116
|
-
//
|
|
7117
|
-
//
|
|
7118
|
-
// broadcasts. Run above the call instead and a credential
|
|
7119
|
-
// signed for another channel touches the reservation on its way to a
|
|
7120
|
-
// refusal that was never going to close anything.
|
|
7102
|
+
// Reassert the intent only after the verifier binds the credential to this channel, and
|
|
7103
|
+
// before broadcast. A wrong-channel signature cannot create or cancel its close intent.
|
|
7121
7104
|
onBound: async () => {
|
|
7122
7105
|
await beginClose(expectedChannelId, record.drainId);
|
|
7123
7106
|
recoveryVersion = await deps.tempoSettlementReadiness?.captureRecoveryVersion();
|
|
@@ -7419,9 +7402,6 @@ async function handleX402ChannelDrain(c, deps, args) {
|
|
|
7419
7402
|
amount: "0",
|
|
7420
7403
|
operation: "refund",
|
|
7421
7404
|
effectId: `${args.credit.creditId}:${args.drainId}`,
|
|
7422
|
-
// The voucher the caller presents is what actually gets revoked and paid,
|
|
7423
|
-
// so the settlement layer refuses one signed for any other channel before
|
|
7424
|
-
// it touches anything.
|
|
7425
7405
|
expectedChannelId: channelId,
|
|
7426
7406
|
refundBoundNative,
|
|
7427
7407
|
commit: async (_funding, tx) => {
|
|
@@ -7640,12 +7620,9 @@ function drainBody(record) {
|
|
|
7640
7620
|
status: record.status,
|
|
7641
7621
|
amount_micro: record.amountMicro,
|
|
7642
7622
|
currency: record.currency,
|
|
7643
|
-
//
|
|
7644
|
-
//
|
|
7645
|
-
//
|
|
7646
|
-
// does not move with the market. Published beside the fiat one rather than
|
|
7647
|
-
// instead of it — the fiat figure is what the caller's balance was
|
|
7648
|
-
// denominated in, and both are things an agent relays.
|
|
7623
|
+
// Fixed in-kind Bitcoin refund obligation after the delivery reserve. It reflects the
|
|
7624
|
+
// funding lots remaining at request time; null means no whole-balance in-kind price was
|
|
7625
|
+
// available.
|
|
7649
7626
|
...record.owedSats !== null ? { owed_sats: record.owedSats } : {},
|
|
7650
7627
|
ledger_seq: record.ledgerSeq,
|
|
7651
7628
|
created_at: record.createdAt,
|
|
@@ -7659,17 +7636,7 @@ function drainCopy(record) {
|
|
|
7659
7636
|
case "pending":
|
|
7660
7637
|
if (record.method === "cashu") {
|
|
7661
7638
|
return {
|
|
7662
|
-
display: record.owedSats === null ? "The refund is approved and being prepared. The balance is already set aside for you." :
|
|
7663
|
-
// Named because it is the obligation, and because it is
|
|
7664
|
-
// fixed: a Bitcoin refund returns the share of the Bitcoin you
|
|
7665
|
-
// deposited that you never spent, so it does not move if the
|
|
7666
|
-
// price does before you collect it. The mint's own
|
|
7667
|
-
// redemption charge is named rather than glossed — it is a sat
|
|
7668
|
-
// or two at a fee-charging mint and nothing at a free one, and
|
|
7669
|
-
// a figure an agent relays to a human should not be a promise
|
|
7670
|
-
// that lands short.
|
|
7671
|
-
`The refund is approved and being prepared: ${record.owedSats} sats, the share of the Bitcoin you deposited that you haven't spent, less whatever the mint charges to hand it over. That figure won't move while you wait, whatever the price does.`
|
|
7672
|
-
),
|
|
7639
|
+
display: record.owedSats === null ? "The refund is approved and being prepared. The balance is already set aside for you." : `The refund is approved and being prepared: ${record.owedSats} sats, the share of the Bitcoin you deposited that you haven't spent, less whatever the mint charges to hand it over. That figure won't move while you wait, whatever the price does.`,
|
|
7673
7640
|
hint: "Poll this op with the same drain_id until a token appears. Preparation can be delayed (e.g. the mint is temporarily down) \u2014 the refund stays owed to you either way."
|
|
7674
7641
|
};
|
|
7675
7642
|
}
|
|
@@ -7795,11 +7762,6 @@ async function handleFund(c, deps, args) {
|
|
|
7795
7762
|
fundId: fund.fund_id,
|
|
7796
7763
|
amountMicro: fund.amount_micro,
|
|
7797
7764
|
menu,
|
|
7798
|
-
// Lightning and MPP name their rail in the signed body. Header-attached
|
|
7799
|
-
// Cashu and x402 are discriminated by `verifyUpfrontPayment`, which still
|
|
7800
|
-
// refuses their mismatches before money moves. Lightning must be checked
|
|
7801
|
-
// here because past this point the bolt11 is minted and payable; MPP is
|
|
7802
|
-
// checked here so discovery never advertises the wrong Tempo instrument.
|
|
7803
7765
|
rail: fund.method,
|
|
7804
7766
|
tempoIntent: fund.method === "tempo" ? fund.intent : void 0
|
|
7805
7767
|
});
|
|
@@ -8090,9 +8052,8 @@ function lightningInvoiceBlock(invoice) {
|
|
|
8090
8052
|
invoice: invoice.bolt11,
|
|
8091
8053
|
payment_hash: invoice.paymentHash,
|
|
8092
8054
|
amount_msats: invoice.amountMsats,
|
|
8093
|
-
//
|
|
8094
|
-
//
|
|
8095
|
-
// in seconds, and a bare `expires_at` invites a reader to assume that.
|
|
8055
|
+
// Wire timestamp is milliseconds; bolt11 lifetimes are conventionally expressed in
|
|
8056
|
+
// seconds.
|
|
8096
8057
|
expires_at_ms: invoice.expiresAt
|
|
8097
8058
|
}
|
|
8098
8059
|
};
|
|
@@ -8323,26 +8284,13 @@ var DRAIN_CONFLICT_COPY = {
|
|
|
8323
8284
|
retryable: true,
|
|
8324
8285
|
drainRegistered: false
|
|
8325
8286
|
},
|
|
8326
|
-
// Deliberately rail-neutral: `refundFloorEvidence` is one floor over both
|
|
8327
|
-
// channel rails, and two doors with two answers about one shortfall is the
|
|
8328
|
-
// drift consolidating it was meant to prevent. Tempo answers its
|
|
8329
|
-
// own through `tempoFloorRefusal`, which reaches the same conclusion a step
|
|
8330
|
-
// earlier and adds the one rail-specific fact — its channel is already closed.
|
|
8331
8287
|
refund_floor: {
|
|
8332
8288
|
display: "The refund reached the chain, but it paid less than the balance this service had recorded, so nothing was booked here. Your on-chain refund arrived; the difference has to be reconciled by the service operator.",
|
|
8333
8289
|
hint: ({ creditId, drainId }) => `Report drain ${drainId} on credit ${creditId} to the service operator \u2014 only they can reconcile the difference. Retrying this drain, or requesting a new drain_id, lands here again.`,
|
|
8334
|
-
// Terminal for the caller, unlike the two above: no amount of waiting or
|
|
8335
|
-
// re-signing changes what the chain already paid.
|
|
8336
8290
|
retryable: false,
|
|
8337
|
-
//
|
|
8338
|
-
//
|
|
8339
|
-
//
|
|
8340
|
-
// `effect_id`, which is the id `X402SettlementRepair.reconcile` books the
|
|
8341
|
-
// drain under — so the caller has to keep it. Dropping it mints a fresh one
|
|
8342
|
-
// that the wedged-channel gate refuses as `settlement_pending` anyway, and
|
|
8343
|
-
// re-presenting *this* id is that gate's own documented exemption. Tempo
|
|
8344
|
-
// reaches the same `true` one artifact earlier, off a `credit_drains` row
|
|
8345
|
-
// its close-intent reservation already committed.
|
|
8291
|
+
// The settlement evidence survives outside the rolled-back ledger transaction. Keep
|
|
8292
|
+
// drain_registered:true so the caller retains this effect ID for repair even when no
|
|
8293
|
+
// drain row committed.
|
|
8346
8294
|
drainRegistered: true
|
|
8347
8295
|
}
|
|
8348
8296
|
};
|
|
@@ -8388,8 +8336,6 @@ function drainLedgerErrorResponse(error, creditId, drainId) {
|
|
|
8388
8336
|
message: error.message,
|
|
8389
8337
|
display: copy?.display,
|
|
8390
8338
|
hint: copy?.hint({ creditId, drainId }),
|
|
8391
|
-
// An unclassified conflict keeps the duplicate-payout copy's own
|
|
8392
|
-
// `retryable`, and the id it was never told anything about.
|
|
8393
8339
|
retryable: copy?.retryable,
|
|
8394
8340
|
extra: { drain_registered: copy?.drainRegistered ?? false }
|
|
8395
8341
|
});
|
|
@@ -8440,8 +8386,8 @@ function creditBody(credit, reconciliationPending = false) {
|
|
|
8440
8386
|
remaining_micro: credit.availableMicro,
|
|
8441
8387
|
expiry_ms: credit.expiryMs,
|
|
8442
8388
|
expired: credit.expired,
|
|
8443
|
-
//
|
|
8444
|
-
//
|
|
8389
|
+
// Nominal expiry does not itself reclaim ownership; a committed sweep can remove undrawn
|
|
8390
|
+
// value.
|
|
8445
8391
|
drainable: credit.status === "active" && credit.availableMicro > 0,
|
|
8446
8392
|
...reconciliationPending && { reconciliation_pending: true },
|
|
8447
8393
|
ledger_seq: credit.lastLedgerSeq
|
|
@@ -8975,11 +8921,9 @@ var X402ExactSettlementServer = class {
|
|
|
8975
8921
|
}
|
|
8976
8922
|
return this.finalize(intent, args.btcUsdRate, args.commit);
|
|
8977
8923
|
}
|
|
8978
|
-
/** Read the operator queue. */
|
|
8979
8924
|
list(args) {
|
|
8980
8925
|
return this.opts.store.list(args);
|
|
8981
8926
|
}
|
|
8982
|
-
/** Read durable attempt evidence for one intent. */
|
|
8983
8927
|
attempts(dvmId, intentId) {
|
|
8984
8928
|
return this.opts.store.attempts(dvmId, intentId);
|
|
8985
8929
|
}
|
|
@@ -9756,20 +9700,10 @@ var ReceiptIssuer = class {
|
|
|
9756
9700
|
this.pubkey = receiptPubkeyFromSecret(opts.secret);
|
|
9757
9701
|
}
|
|
9758
9702
|
/**
|
|
9759
|
-
*
|
|
9760
|
-
*
|
|
9761
|
-
*
|
|
9762
|
-
*
|
|
9763
|
-
* sequence number and still attest an outcome. Whether feedback is
|
|
9764
|
-
* paid-only is the feedback layer's call, not this one's.
|
|
9765
|
-
*
|
|
9766
|
-
* Timing and attribution come from the committed terminal row, not signing
|
|
9767
|
-
* time or handler input. Legacy rows without those durable inputs omit the
|
|
9768
|
-
* optional blocks rather than inventing historical evidence.
|
|
9769
|
-
*
|
|
9770
|
-
* `credit` is the resolved draw block — present on every job
|
|
9771
|
-
* whose payment funded/drew the ledger, absent otherwise. Signature-
|
|
9772
|
-
* compatible either way: `canonicalize` omits absent keys.
|
|
9703
|
+
* seq is store-allocated; issuedAt is epoch seconds. Free jobs also consume a
|
|
9704
|
+
* sequence and attest their outcome. Timing/attribution comes from the committed
|
|
9705
|
+
* terminal row; legacy rows omit unknown historical fields. credit is present
|
|
9706
|
+
* only for a resolved ledger draw.
|
|
9773
9707
|
*/
|
|
9774
9708
|
issue(record, seq, issuedAt, credit) {
|
|
9775
9709
|
const unsigned = {
|
|
@@ -9880,7 +9814,6 @@ function resultHashFor(record) {
|
|
|
9880
9814
|
}
|
|
9881
9815
|
|
|
9882
9816
|
// src/lib/cashu/mint-info.ts
|
|
9883
|
-
var MINT_INFO_TTL_MS = 5 * 6e4;
|
|
9884
9817
|
async function getKeysetFinalExpiry(mintUrl, keysetId) {
|
|
9885
9818
|
try {
|
|
9886
9819
|
const wallet = await getWallet(mintUrl);
|
|
@@ -10324,11 +10257,9 @@ var JobManager = class {
|
|
|
10324
10257
|
get receiptsEnabled() {
|
|
10325
10258
|
return this.opts.receiptIssuer !== void 0 && isReceiptIssuingStore(this.jobStore);
|
|
10326
10259
|
}
|
|
10327
|
-
/** True when the named capability exists on this descriptor. */
|
|
10328
10260
|
hasCapability(name) {
|
|
10329
10261
|
return Object.prototype.hasOwnProperty.call(this.descriptor.capabilities, name);
|
|
10330
10262
|
}
|
|
10331
|
-
/** Names of every capability this descriptor exposes — used for error envelopes. */
|
|
10332
10263
|
capabilityNames() {
|
|
10333
10264
|
return Object.keys(this.descriptor.capabilities);
|
|
10334
10265
|
}
|
|
@@ -10925,7 +10856,6 @@ var JobManager = class {
|
|
|
10925
10856
|
job.messageAppenderTail = next;
|
|
10926
10857
|
};
|
|
10927
10858
|
}
|
|
10928
|
-
/** Start (or restart) the idle timer for a job. */
|
|
10929
10859
|
startIdleTimer(job) {
|
|
10930
10860
|
this.clearIdleTimer(job.id);
|
|
10931
10861
|
const timeoutMs = this.descriptor.idleTimeout * 1e3;
|
|
@@ -10934,7 +10864,6 @@ var JobManager = class {
|
|
|
10934
10864
|
}, timeoutMs);
|
|
10935
10865
|
this.idleTimers.set(job.id, timer);
|
|
10936
10866
|
}
|
|
10937
|
-
/** Clear the idle timer for a job. */
|
|
10938
10867
|
clearIdleTimer(id) {
|
|
10939
10868
|
const timer = this.idleTimers.get(id);
|
|
10940
10869
|
if (timer) {
|
|
@@ -12533,7 +12462,6 @@ var JobManager = class {
|
|
|
12533
12462
|
for (const record of records) await this.reportTerminalJob(record.id);
|
|
12534
12463
|
this.terminalReportRecoveryCursor = records.length === 100 ? records[records.length - 1].id : void 0;
|
|
12535
12464
|
}
|
|
12536
|
-
/** Handle job reaching terminal state (completed, failed, cancelled). */
|
|
12537
12465
|
async handleJobTerminal(job) {
|
|
12538
12466
|
this.clearIdleTimer(job.id);
|
|
12539
12467
|
if (await this.adoptDurableTerminal(job)) return;
|
|
@@ -12809,7 +12737,6 @@ var JobManager = class {
|
|
|
12809
12737
|
}, 6e4);
|
|
12810
12738
|
this.cleanupTimers.set(job.id, timer);
|
|
12811
12739
|
}
|
|
12812
|
-
/** Clear all timers. Called on server shutdown to allow clean exit. */
|
|
12813
12740
|
shutdown() {
|
|
12814
12741
|
for (const [id] of this.idleTimers) {
|
|
12815
12742
|
this.clearIdleTimer(id);
|
|
@@ -13054,7 +12981,6 @@ var MintHealthTracker = class {
|
|
|
13054
12981
|
isNutIncompatible(mintUrl) {
|
|
13055
12982
|
return this.state.get(normaliseMintUrl(mintUrl))?.nutIncompatible ?? false;
|
|
13056
12983
|
}
|
|
13057
|
-
/** Plain-object snapshot keyed by mint URL — for tests + observability. */
|
|
13058
12984
|
snapshot() {
|
|
13059
12985
|
return Object.fromEntries(Array.from(this.state.entries()).map(([k, v]) => [k, { ...v }]));
|
|
13060
12986
|
}
|
|
@@ -13112,12 +13038,7 @@ var MintHealthTracker = class {
|
|
|
13112
13038
|
);
|
|
13113
13039
|
}
|
|
13114
13040
|
}
|
|
13115
|
-
/**
|
|
13116
|
-
* One-shot structured summary emitted after the first probe tick. Replaces
|
|
13117
|
-
* the boot check's per-mint `cashu_mint_startup_health` lines and surfaces an
|
|
13118
|
-
* all-mints-unhealthy warning — the operator alert that the Fly restart-loop
|
|
13119
|
-
* used to be before boot stopped refusing to start.
|
|
13120
|
-
*/
|
|
13041
|
+
/** One boot summary, including an all-unhealthy warning. */
|
|
13121
13042
|
logBootHealth() {
|
|
13122
13043
|
const mints = Object.entries(this.snapshot()).map(([mintUrl, s]) => ({
|
|
13123
13044
|
mintUrl,
|
|
@@ -13248,7 +13169,6 @@ var PostgresX402ExactSettlementStore = class {
|
|
|
13248
13169
|
this.pool = pool;
|
|
13249
13170
|
}
|
|
13250
13171
|
pool;
|
|
13251
|
-
/** Create the exact settlement tables. */
|
|
13252
13172
|
async init() {
|
|
13253
13173
|
await withSdkInitLock(this.pool, async () => {
|
|
13254
13174
|
await this.pool.query(`
|
|
@@ -14194,20 +14114,11 @@ async function handlePendingDrains(c, opts) {
|
|
|
14194
14114
|
amount_micro: d.amountMicro,
|
|
14195
14115
|
amount_sats: amountSats,
|
|
14196
14116
|
amount_msats: amountSats === null ? null : amountSats * 1e3,
|
|
14197
|
-
// Whether this row's sats figure is the in-kind obligation or a live-rate
|
|
14198
|
-
// conversion. An operator settling a refund should be able to see which,
|
|
14199
|
-
// and the servicer's own logs carry it for the same reason.
|
|
14200
14117
|
sats_basis: amountSats === null ? null : d.owedSats === null ? "rate" : "in_kind",
|
|
14201
|
-
//
|
|
14202
|
-
//
|
|
14203
|
-
// `completeX402ChannelDrain` treats as its settlement floor,
|
|
14204
|
-
// so publishing it is what lets an operator settle from the row instead
|
|
14205
|
-
// of assuming their stablecoin happens to be six-decimal.
|
|
14118
|
+
// Rail-native debit, distinct from fiat amount_micro. This is the channel refund floor
|
|
14119
|
+
// and the size a manual payout must cover.
|
|
14206
14120
|
amount_native: d.drainedNative,
|
|
14207
14121
|
settlement,
|
|
14208
|
-
// Why this row is classified as it is, and the channel an operator would
|
|
14209
|
-
// look up to see the close land. `payout` cannot answer it: a
|
|
14210
|
-
// channel-backed mpp drain carries `{}`.
|
|
14211
14122
|
channel_id: d.channelId,
|
|
14212
14123
|
created_at: d.createdAt
|
|
14213
14124
|
};
|
|
@@ -14218,13 +14129,7 @@ async function handlePendingDrains(c, opts) {
|
|
|
14218
14129
|
acc[method] = pending.filter((d) => d.method === method).reduce((sum, d) => sum + d.amountMicro, 0);
|
|
14219
14130
|
return acc;
|
|
14220
14131
|
}, {}),
|
|
14221
|
-
// What no job will ever pay down: the operator's own worklist, in the one
|
|
14222
|
-
// denomination these rows have.
|
|
14223
14132
|
manual_micro: drains.filter((d) => d.settlement === "manual").reduce((sum, d) => sum + d.amount_micro, 0),
|
|
14224
|
-
// The rest of the stablecoin liability — owed, but by a channel close in
|
|
14225
|
-
// flight rather than by a person. Published beside `manual_micro` so the
|
|
14226
|
-
// difference between the two is visible rather than inferred from a
|
|
14227
|
-
// `by_method_micro` figure neither of them matches.
|
|
14228
14133
|
channel_micro: drains.filter((d) => d.settlement === "channel").reduce((sum, d) => sum + d.amount_micro, 0)
|
|
14229
14134
|
};
|
|
14230
14135
|
return c.json({ drains, liability, mints: opts.mints?.() ?? [] });
|
|
@@ -14253,17 +14158,21 @@ async function handleBitcoinLiability(c, opts) {
|
|
|
14253
14158
|
drain_liability_sats: drainSats,
|
|
14254
14159
|
drain_count: promised.length,
|
|
14255
14160
|
total_owed_sats: deposit.depositSats + drainSats,
|
|
14256
|
-
//
|
|
14257
|
-
//
|
|
14258
|
-
//
|
|
14259
|
-
// prices the whole balance off a live rate, so both are reported whole and
|
|
14260
|
-
// neither contributes to `deposit_liability_sats` — a sweep computed
|
|
14261
|
-
// against `total_owed_sats` alone is short by whatever they turn out to be
|
|
14262
|
-
// worth.
|
|
14161
|
+
// Uncovered or basisless balances require whole-balance live-rate pricing and do not
|
|
14162
|
+
// contribute to deposit_liability_sats. total_owed_sats alone cannot reserve their
|
|
14163
|
+
// eventual refunds.
|
|
14263
14164
|
uncovered_credits: deposit.uncoveredCredits,
|
|
14264
|
-
|
|
14165
|
+
uncovered_by_currency: deposit.uncoveredByCurrency.map((currency) => ({
|
|
14166
|
+
currency: currency.currency,
|
|
14167
|
+
micro: currency.micro,
|
|
14168
|
+
credits: currency.credits
|
|
14169
|
+
})),
|
|
14265
14170
|
unbacked_credits: deposit.unbackedCredits,
|
|
14266
|
-
|
|
14171
|
+
unbacked_by_currency: deposit.unbackedByCurrency.map((currency) => ({
|
|
14172
|
+
currency: currency.currency,
|
|
14173
|
+
micro: currency.micro,
|
|
14174
|
+
credits: currency.credits
|
|
14175
|
+
})),
|
|
14267
14176
|
unpriced_drains: drainsUnpriced
|
|
14268
14177
|
});
|
|
14269
14178
|
}
|
|
@@ -14701,9 +14610,6 @@ async function handleReconcileInvoice(c, opts) {
|
|
|
14701
14610
|
const result = await opts.ledger.reconcileBlockedInvoice({
|
|
14702
14611
|
paymentHash,
|
|
14703
14612
|
targetCreditId,
|
|
14704
|
-
// `fund`'s upsert OVERWRITES `expiry_ms`, so a bare `now + ttl` would
|
|
14705
|
-
// shorten a target credit with more life left on it — destroying
|
|
14706
|
-
// spendable value while purporting to add some.
|
|
14707
14613
|
expiryMs,
|
|
14708
14614
|
basis,
|
|
14709
14615
|
...settledAtMs === void 0 ? {} : { settledAtMs },
|
|
@@ -14724,9 +14630,6 @@ async function handleReconcileInvoice(c, opts) {
|
|
|
14724
14630
|
creditCurrency: invoice.currency,
|
|
14725
14631
|
fundedAt: settledAtMs ?? invoice.settledAt ?? nowMs,
|
|
14726
14632
|
expiryMs,
|
|
14727
|
-
// The settlements table types this deposit `repair` off the
|
|
14728
|
-
// flag: the sats landed at payment time and the
|
|
14729
|
-
// deposit is the one row they get.
|
|
14730
14633
|
repaired: true
|
|
14731
14634
|
}
|
|
14732
14635
|
}
|
|
@@ -14809,8 +14712,6 @@ async function handleWriteOffInvoice(c, opts) {
|
|
|
14809
14712
|
amount_micro: written.amountMicro,
|
|
14810
14713
|
currency: written.currency,
|
|
14811
14714
|
note: written.writeOffNote,
|
|
14812
|
-
// Mirrors the reconcile's `replayed`: the operator asked for a decision and
|
|
14813
|
-
// finds one already recorded, possibly with someone else's note on it.
|
|
14814
14715
|
replayed: !outcome.written
|
|
14815
14716
|
});
|
|
14816
14717
|
}
|
|
@@ -16880,12 +16781,12 @@ async function createDVMServer(descriptor, opts) {
|
|
|
16880
16781
|
const platformUrl = opts.platformUrl ?? envMaybe.DVMKIT_PLATFORM_URL;
|
|
16881
16782
|
const dvmId = opts.dvmId;
|
|
16882
16783
|
if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
|
|
16883
|
-
const { RevenueReporter } = await import("./revenue-reporter-
|
|
16784
|
+
const { RevenueReporter } = await import("./revenue-reporter-O75BGYFK.js");
|
|
16884
16785
|
const reporter = new RevenueReporter(opts.db, platformUrl, platformToken);
|
|
16885
16786
|
revenueReporter = reporter;
|
|
16886
16787
|
await reporter.init();
|
|
16887
16788
|
reporter.startRetryLoop();
|
|
16888
|
-
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-
|
|
16789
|
+
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-KEMUXEUO.js");
|
|
16889
16790
|
payoutReporter = new PayoutReporter({
|
|
16890
16791
|
dvmId,
|
|
16891
16792
|
db: opts.db,
|
|
@@ -17143,17 +17044,9 @@ var DVMServer = class {
|
|
|
17143
17044
|
mints: opts.mints,
|
|
17144
17045
|
jobStore: opts.jobStore,
|
|
17145
17046
|
devMode: opts.devMode,
|
|
17146
|
-
//
|
|
17147
|
-
//
|
|
17148
|
-
//
|
|
17149
|
-
// raw option meant a DVM that never set `paymentMethods` (no first-party
|
|
17150
|
-
// one does; the descriptor documents it as "derived from configured
|
|
17151
|
-
// rails") advertised both rails, took the upfront payment on either, and
|
|
17152
|
-
// then emitted a mid-job payment-request carrying no rail at all. The
|
|
17153
|
-
// caller's responder had nothing to sign and the job stalled at
|
|
17154
|
-
// needs-approval — reachable only from a rung that raises a real mid-job
|
|
17155
|
-
// ask, which nothing above L1 did. Facilitator availability is still
|
|
17156
|
-
// re-checked per ask in `context.ts`, so a static list is safe here.
|
|
17047
|
+
// Use the same derived rail list as discovery/upfront payment; otherwise
|
|
17048
|
+
// default-configured rails vanish from mid-job asks. Availability is rechecked
|
|
17049
|
+
// per ask.
|
|
17157
17050
|
paymentMethods: (opts.paymentMethods ?? this.defaultPaymentMethods()).filter(
|
|
17158
17051
|
(method) => method !== "x402" || this.x402CurrencySupported()
|
|
17159
17052
|
),
|
|
@@ -17411,12 +17304,8 @@ var DVMServer = class {
|
|
|
17411
17304
|
return state;
|
|
17412
17305
|
}
|
|
17413
17306
|
/**
|
|
17414
|
-
*
|
|
17415
|
-
*
|
|
17416
|
-
* upfront flow uses at lines 709-718 so accept-time rotation tolerance is
|
|
17417
|
-
* consistent across upfront and mid-job. Falls back to `undefined` when
|
|
17418
|
-
* accumulator mode isn't wired (the receive path then rejects with
|
|
17419
|
-
* `cashu_not_p2pk_locked`).
|
|
17307
|
+
* Mid-job and upfront receive paths use current plus grace-period retired keys.
|
|
17308
|
+
* Without accumulator mode this is undefined and the receive path refuses it.
|
|
17420
17309
|
*/
|
|
17421
17310
|
async resolveLockPubkeysForReceive() {
|
|
17422
17311
|
if (this.opts.disposableCashu && this.opts.lockPubkey) return [toLockPubkey(this.opts.lockPubkey)];
|
|
@@ -17431,12 +17320,8 @@ var DVMServer = class {
|
|
|
17431
17320
|
}
|
|
17432
17321
|
}
|
|
17433
17322
|
/**
|
|
17434
|
-
*
|
|
17435
|
-
*
|
|
17436
|
-
* cached state, and only as a final resort to the env-derived seed in a
|
|
17437
|
-
* single-entry array. The cache makes the degradation safe across a
|
|
17438
|
-
* rotation — without it the env seed advertises and accepts the retired
|
|
17439
|
-
* pubkey while the DB is unreachable.
|
|
17323
|
+
* Read Postgres first, then fall back to last-observed state, then the env seed.
|
|
17324
|
+
* Caching avoids reverting an observed rotation to the old seed during an outage.
|
|
17440
17325
|
*/
|
|
17441
17326
|
async lockPubkeyStateForRead(db, dvmId) {
|
|
17442
17327
|
try {
|
|
@@ -17448,22 +17333,9 @@ var DVMServer = class {
|
|
|
17448
17333
|
}
|
|
17449
17334
|
}
|
|
17450
17335
|
/**
|
|
17451
|
-
*
|
|
17452
|
-
*
|
|
17453
|
-
*
|
|
17454
|
-
* Routine settlement used to ride the per-DVM watcher machine's four-minute
|
|
17455
|
-
* poll, which is exactly what kept an idle DVM awake and billing. It cannot
|
|
17456
|
-
* move to a timer for the same reason — a timer on a machine that ought to
|
|
17457
|
-
* be suspended either does not fire or defeats the suspension. So it rides
|
|
17458
|
-
* requests instead, the way Lightning funding is credited: a DVM
|
|
17459
|
-
* with no traffic has nothing to settle on a 24-hour cadence anyway, and the
|
|
17460
|
-
* forced-close guarantee does not depend on this path at all — that is the
|
|
17461
|
-
* chain-event wake, which reaches a suspended DVM through Fly Proxy.
|
|
17462
|
-
*
|
|
17463
|
-
* Fire-and-forget and self-throttled: the request never waits on it, only
|
|
17464
|
-
* one runs at a time, and a failure is logged rather than surfaced. The
|
|
17465
|
-
* sweep re-checks each channel's own schedule, so the throttle here only
|
|
17466
|
-
* bounds how often we pay for the store scan.
|
|
17336
|
+
* Opportunistic traffic avoids a timer that keeps an idle machine awake. Forced
|
|
17337
|
+
* close servicing is independent. Fire-and-forget, one sweep at a time; failures
|
|
17338
|
+
* are logged. The throttle bounds scans, not each channel's settlement schedule.
|
|
17467
17339
|
*/
|
|
17468
17340
|
maybeSweepTempoSessions() {
|
|
17469
17341
|
const sweep = this.opts.mpp?.sweepTempoSessions;
|
|
@@ -17528,22 +17400,9 @@ var DVMServer = class {
|
|
|
17528
17400
|
]);
|
|
17529
17401
|
}
|
|
17530
17402
|
/**
|
|
17531
|
-
*
|
|
17532
|
-
*
|
|
17533
|
-
*
|
|
17534
|
-
*
|
|
17535
|
-
* Deliberately the same question the platform's post-deploy `/v1/info` probe
|
|
17536
|
-
* answers (`infoAdvertisesTempoSession`), from the same two facts: the credit
|
|
17537
|
-
* menu exists at all, and the configured MPP methods include `tempo/session`.
|
|
17538
|
-
* Credit-scoped because sessions are confined to `POST /v1/credit` — a
|
|
17539
|
-
* top-level session method with no credit menu is not a channel anyone can
|
|
17540
|
-
* open, so it is not a candidate the payee fallback should reach for.
|
|
17541
|
-
*
|
|
17542
|
-
* It reads `advertisedMethods` rather than {@link creditMppSplit}'s `offered`,
|
|
17543
|
-
* and never {@link tempoSessionOffered}: what the column records is
|
|
17544
|
-
* capability, not availability, so an observer blip must leave it alone. That
|
|
17545
|
-
* distinction is the whole of and answering it here from the gate
|
|
17546
|
-
* would reintroduce it on a one-minute cadence.
|
|
17403
|
+
* Report configured session capability independently of current availability.
|
|
17404
|
+
* A temporary observer outage must not remove the platform's watch signal.
|
|
17405
|
+
* Credit menu, auth and session method configuration still gate that capability.
|
|
17547
17406
|
*/
|
|
17548
17407
|
tempoSessionCapability() {
|
|
17549
17408
|
if (!this.opts.mpp) return false;
|
|
@@ -17561,25 +17420,11 @@ var DVMServer = class {
|
|
|
17561
17420
|
);
|
|
17562
17421
|
}
|
|
17563
17422
|
/**
|
|
17564
|
-
*
|
|
17565
|
-
*
|
|
17566
|
-
*
|
|
17567
|
-
*
|
|
17568
|
-
*
|
|
17569
|
-
* its own Tempo RPC and verifies chain, escrow, topic and local channel
|
|
17570
|
-
* ownership before touching money (see `handleTempoCloseEvent`). An
|
|
17571
|
-
* unverifiable locator can therefore do nothing, which is what lets the
|
|
17572
|
-
* platform wake a DVM without holding a signing key or a recoverable copy of
|
|
17573
|
-
* the DVM's bearer token.
|
|
17574
|
-
*
|
|
17575
|
-
* What an unauthenticated caller *can* do is spend the DVM's resources: each
|
|
17576
|
-
* POST is an unthrottled outbound `eth_getTransactionReceipt` against the
|
|
17577
|
-
* builder's own Tempo provider, billed to them, before anything about the
|
|
17578
|
-
* locator has been checked — the verification needs the receipt to happen at
|
|
17579
|
-
* all. So the cost of an open endpoint is metered RPC calls, not money: no
|
|
17580
|
-
* locator can move value except one naming a real close of a channel this
|
|
17581
|
-
* DVM owns, and settling that is what the routine cadence would do anyway.
|
|
17582
|
-
* Watch the provider's quota rather than the ledger.
|
|
17423
|
+
* Unauthenticated wake carrying only an event locator. After parsing, verify
|
|
17424
|
+
* the receipt from the DVM's own RPC, including chain, escrow, topic and local
|
|
17425
|
+
* channel ownership before settlement. The observer needs no spending secret.
|
|
17426
|
+
* Syntactically valid requests can still consume unthrottled provider RPC quota;
|
|
17427
|
+
* malformed locators return 400 before that read.
|
|
17583
17428
|
*/
|
|
17584
17429
|
registerTempoCloseEventRoute() {
|
|
17585
17430
|
const mpp = this.opts.mpp;
|
|
@@ -17640,16 +17485,8 @@ var DVMServer = class {
|
|
|
17640
17485
|
});
|
|
17641
17486
|
}
|
|
17642
17487
|
/**
|
|
17643
|
-
*
|
|
17644
|
-
*
|
|
17645
|
-
* field is independently sourced (opts wins per-field, env fills gaps);
|
|
17646
|
-
* all three of pubkey / attestation / signature must resolve to emit the
|
|
17647
|
-
* block — partial state is treated as absent rather than half-emitted.
|
|
17648
|
-
*
|
|
17649
|
-
* Env-var form: `DVMKIT_BUILDER_ATTESTATION` is a JSON string (Fly secrets
|
|
17650
|
-
* can't carry structured data), so it's parsed here. A malformed JSON
|
|
17651
|
-
* string drops the block rather than throwing, so a corrupted env var
|
|
17652
|
-
* can't take the DVM down.
|
|
17488
|
+
* Options override environment per field. Emit only when pubkey, attestation
|
|
17489
|
+
* and signature all exist; malformed attestation JSON omits the block.
|
|
17653
17490
|
*/
|
|
17654
17491
|
builderAttestationBlock() {
|
|
17655
17492
|
const builder = this.opts.builder;
|
|
@@ -18032,11 +17869,8 @@ var DVMServer = class {
|
|
|
18032
17869
|
return this.pricingCurrency;
|
|
18033
17870
|
}
|
|
18034
17871
|
/**
|
|
18035
|
-
*
|
|
18036
|
-
*
|
|
18037
|
-
* caller can see which rails return funds on failure before committing. The
|
|
18038
|
-
* `methods` shape stays identical to `/v1/info` and the platform isolate route
|
|
18039
|
-
* (current nested `payment.methods`); refundability rides alongside.
|
|
17872
|
+
* Advertise the same method list as info. Refundability describes the rail
|
|
17873
|
+
* itself; failed jobs release their pending credit draw separately.
|
|
18040
17874
|
*/
|
|
18041
17875
|
quotePaymentBlock() {
|
|
18042
17876
|
const methods = this.resolvePaymentMethods();
|
|
@@ -18149,12 +17983,8 @@ var DVMServer = class {
|
|
|
18149
17983
|
return c.json(info);
|
|
18150
17984
|
}
|
|
18151
17985
|
/**
|
|
18152
|
-
*
|
|
18153
|
-
*
|
|
18154
|
-
* `pricing` block — `pricing.quote.schema` for dynamic-priced capabilities,
|
|
18155
|
-
* `pricing.max` for static-priced ones. Capabilities without an `input`
|
|
18156
|
-
* schema or any pricing surface omit those fields entirely; free
|
|
18157
|
-
* capabilities (no `price`) omit `pricing` entirely.
|
|
17986
|
+
* Input-mode JSON Schema keeps defaulted fields optional on the advertised
|
|
17987
|
+
* wire contract. Capability names are the descriptor's keys.
|
|
18158
17988
|
*/
|
|
18159
17989
|
renderCapabilities() {
|
|
18160
17990
|
const out = {};
|
|
@@ -18355,11 +18185,8 @@ var DVMServer = class {
|
|
|
18355
18185
|
return c.json(res);
|
|
18356
18186
|
}
|
|
18357
18187
|
/**
|
|
18358
|
-
*
|
|
18359
|
-
*
|
|
18360
|
-
* path — cannot drift: a field added for one is present in the other by
|
|
18361
|
-
* construction, and both paths price against the same view of the caller's
|
|
18362
|
-
* credit.
|
|
18188
|
+
* Callers supply quote-schema-parsed data when a quote schema exists, alongside
|
|
18189
|
+
* raw input text and params. Quote and job pricing share these runtime helpers.
|
|
18363
18190
|
*/
|
|
18364
18191
|
async buildQuoteContext(args) {
|
|
18365
18192
|
const ctx = {
|
|
@@ -18381,16 +18208,8 @@ var DVMServer = class {
|
|
|
18381
18208
|
return ctx;
|
|
18382
18209
|
}
|
|
18383
18210
|
/**
|
|
18384
|
-
*
|
|
18385
|
-
*
|
|
18386
|
-
* structured 400 envelope agents can relay verbatim to a human caller.
|
|
18387
|
-
*
|
|
18388
|
-
* - **Missing** — `body.capability` is absent or empty. Surface as
|
|
18389
|
-
* `capability_required` with a hint pointing at `/v1/info` so the agent
|
|
18390
|
-
* can re-discover the capability set.
|
|
18391
|
-
* - **Unknown** — `body.capability` is set but doesn't match any
|
|
18392
|
-
* capability on this descriptor. Surface as `unknown_capability` listing
|
|
18393
|
-
* the known names so the agent can correct without a round-trip.
|
|
18211
|
+
* Missing or unknown capabilities return structured errors with the current
|
|
18212
|
+
* info-schema URL.
|
|
18394
18213
|
*/
|
|
18395
18214
|
requireCapabilityFromBody(c, capability) {
|
|
18396
18215
|
const known = this.jobManager.capabilityNames();
|
|
@@ -18494,22 +18313,9 @@ var DVMServer = class {
|
|
|
18494
18313
|
);
|
|
18495
18314
|
}
|
|
18496
18315
|
/**
|
|
18497
|
-
*
|
|
18498
|
-
*
|
|
18499
|
-
*
|
|
18500
|
-
* A capability schema may declare the signing-envelope fields required
|
|
18501
|
-
* (cast's `addEpisodeSchema`) or optional (scribe's `transcribeSchema`). On
|
|
18502
|
-
* the required shape an *unsigned* request fails the input parse naming
|
|
18503
|
-
* `pubkey` / `signature` — a signing failure, and answering it with "your
|
|
18504
|
-
* input is invalid: pubkey required" is the mistake in reverse. So a
|
|
18505
|
-
* parse whose issues are **only** envelope-side falls through to the auth
|
|
18506
|
-
* gate, which answers 401 `signature_invalid` with the remedy that works.
|
|
18507
|
-
*
|
|
18508
|
-
* Deferring is safe because the gate below re-parses the same schema over the
|
|
18509
|
-
* same bytes, so a caller that reaches it with an envelope-side failure is
|
|
18510
|
-
* rejected there for certain; both routes still carry a backstop for the
|
|
18511
|
-
* impossible case. It applies only to a DVM that declares `auth` — with no
|
|
18512
|
-
* gate below, the 400 is the only answer available.
|
|
18316
|
+
* On authenticated DVMs, envelope-only parse failures defer to auth
|
|
18317
|
+
* (401 signature_invalid). Builder input issues remain 400 invalid_input.
|
|
18318
|
+
* Auth reparses the same schema/raw input; a backstop refuses disagreement.
|
|
18513
18319
|
*/
|
|
18514
18320
|
deferToAuthGate(err) {
|
|
18515
18321
|
if (!this.descriptor.auth) return false;
|
|
@@ -18953,10 +18759,6 @@ var DVMServer = class {
|
|
|
18953
18759
|
priceMsats,
|
|
18954
18760
|
capabilityName,
|
|
18955
18761
|
requesterTokenHash,
|
|
18956
|
-
// everything the idempotent-replay path needs to recognise a
|
|
18957
|
-
// retry of THIS call and re-issue the token its lost response carried.
|
|
18958
|
-
// `jobId` is the pre-allocated id the payment's ledger draw
|
|
18959
|
-
// already committed under.
|
|
18960
18762
|
{
|
|
18961
18763
|
requesterToken: jobToken,
|
|
18962
18764
|
requestFingerprint: fingerprint,
|
|
@@ -19039,20 +18841,9 @@ var DVMServer = class {
|
|
|
19039
18841
|
});
|
|
19040
18842
|
}
|
|
19041
18843
|
/**
|
|
19042
|
-
*
|
|
19043
|
-
*
|
|
19044
|
-
*
|
|
19045
|
-
* but until now it was also useless: the caller got a 409 and the `job_id` /
|
|
19046
|
-
* `job_token` it paid for stayed unreachable forever.
|
|
19047
|
-
*
|
|
19048
|
-
* Returns the original job's submit response iff the retry proves it is the
|
|
19049
|
-
* original caller. Proof is reproducing the request, **not** possessing the
|
|
19050
|
-
* spent Cashu token: the token is a bearer secret, so gating on it alone
|
|
19051
|
-
* would hand the job (and its result) to anyone who lifted it. The legitimate
|
|
19052
|
-
* retry always has the body — the resume sweep re-POSTs it — while a
|
|
19053
|
-
* token thief does not. On a descriptor-auth DVM the caller pubkey must match
|
|
19054
|
-
* too. Anything short of that falls through to the 409 the caller has always
|
|
19055
|
-
* gotten.
|
|
18844
|
+
* Recover a lost paid response only for matching request fingerprint,
|
|
18845
|
+
* authenticated pubkey and requester identity. A spent bearer token alone
|
|
18846
|
+
* is not proof of request ownership.
|
|
19056
18847
|
*/
|
|
19057
18848
|
async recoverReplayedJob(c, opts) {
|
|
19058
18849
|
if (!opts.requestId) return void 0;
|
|
@@ -19076,16 +18867,9 @@ var DVMServer = class {
|
|
|
19076
18867
|
return buildSubmitResponse(c, prior, prior.requesterToken);
|
|
19077
18868
|
}
|
|
19078
18869
|
/**
|
|
19079
|
-
*
|
|
19080
|
-
*
|
|
19081
|
-
*
|
|
19082
|
-
* and re-issue the original response under the SAME proof-of-origin gates
|
|
19083
|
-
* as the header regime: reproduce the fingerprint, sign as the same caller
|
|
19084
|
-
* (explicit draws only exist on descriptor-auth DVMs), match requesterId.
|
|
19085
|
-
*
|
|
19086
|
-
* A recorded draw whose job row never landed (crash between the ledger
|
|
19087
|
-
* commit and the job persist) has nothing to hand back — refuse with the
|
|
19088
|
-
* 409 and log loudly; the pending hold is surfaced by `listPendingDraws`.
|
|
18870
|
+
* Resolve the recorded draw's job under the same fingerprint/pubkey/requester
|
|
18871
|
+
* checks as header recovery. If no job exists, the caller may still recover a
|
|
18872
|
+
* pending durable acceptance under its bound public request-id claim.
|
|
19089
18873
|
*/
|
|
19090
18874
|
async recoverDrawnJob(c, opts) {
|
|
19091
18875
|
let jobId = opts.jobId;
|
|
@@ -19195,12 +18979,9 @@ var DVMServer = class {
|
|
|
19195
18979
|
return payment;
|
|
19196
18980
|
}
|
|
19197
18981
|
/**
|
|
19198
|
-
* Recompute
|
|
19199
|
-
*
|
|
19200
|
-
*
|
|
19201
|
-
* satisfy `onQuote.schema` or the handler throws — failing closed is the
|
|
19202
|
-
* right default, since the alternative (running the job with
|
|
19203
|
-
* `requiredMsats=undefined`) lets unpaid jobs through.
|
|
18982
|
+
* Recompute dynamic upfront pricing from parsed job input, applying the quote
|
|
18983
|
+
* schema separately. Refuse invalid input/results, currency mismatch or missing
|
|
18984
|
+
* FX rather than running without a price (400/500/503 as classified below).
|
|
19204
18985
|
*/
|
|
19205
18986
|
async computeDynamicPrice(onQuote, parsedInput, body, callerPubkey) {
|
|
19206
18987
|
let data = parsedInput;
|
|
@@ -19977,16 +19758,9 @@ var PlatformTempoObserverHealth = class {
|
|
|
19977
19758
|
};
|
|
19978
19759
|
}
|
|
19979
19760
|
/**
|
|
19980
|
-
*
|
|
19981
|
-
*
|
|
19982
|
-
*
|
|
19983
|
-
* the current set of declarations. `host.ts` primes this client before any
|
|
19984
|
-
* mount exists, so at boot there is routinely an open probe carrying **no**
|
|
19985
|
-
* `tempo_session` at all — handing it back to the mount that just declared
|
|
19986
|
-
* one would leave a scale-to-zero DVM unstamped until traffic arrived, which
|
|
19987
|
-
* is exactly the container-restart case is about. Same shape with
|
|
19988
|
-
* two mounts: the second would join a probe whose URL already read the first
|
|
19989
|
-
* one's `false`.
|
|
19761
|
+
* De-duplicate probes only while their declaration epoch is current. A mount
|
|
19762
|
+
* declared during boot or another mount's probe must await a fresh probe so
|
|
19763
|
+
* the platform receives the updated capability, even before traffic arrives.
|
|
19990
19764
|
*/
|
|
19991
19765
|
async refresh() {
|
|
19992
19766
|
const open = this.inFlight;
|