@dvmkit/sdk 0.4.0-rc.1 → 0.6.0-rc.1
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 +100 -0
- package/dist/{chunk-TXX4GOUB.js → chunk-7ZUKL4IV.js} +1 -1
- package/dist/chunk-AFV77S4C.js +258 -0
- package/dist/{chunk-2VGWYG4P.js → chunk-B2IROPQF.js} +446 -81
- package/dist/chunk-BQJIFRAZ.js +36 -0
- package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
- package/dist/{chunk-74PUEZJ4.js → chunk-GFY5FRX3.js} +1 -1
- package/dist/{chunk-O3QDFPKM.js → chunk-HCLVYZUZ.js} +2 -2
- package/dist/{chunk-K2YX2VRA.js → chunk-HHBDBMEO.js} +170 -43
- package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
- package/dist/{chunk-W2EQGDF5.js → chunk-LE74UEV7.js} +49 -8
- package/dist/{chunk-CE7XEHYC.js → chunk-N5RZ5HVB.js} +1 -1
- package/dist/chunk-OFVV37XC.js +57 -0
- package/dist/{chunk-5LR6P7WN.js → chunk-OQA7IHI4.js} +133 -213
- package/dist/{chunk-3MHE725Z.js → chunk-PAIM66YA.js} +90 -1
- package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
- package/dist/{chunk-KXZUCCEY.js → chunk-RCM3P2HE.js} +5 -56
- package/dist/{chunk-R4DQZFAL.js → chunk-UOCZXO2Z.js} +3 -3
- package/dist/content-retention-BTGSQRfP.d.ts +78 -0
- package/dist/{credit-menu-B8_p_vUz.d.ts → credit-menu-BAsrRu3z.d.ts} +98 -5
- package/dist/{fx-DuaYHvVn.d.ts → fx-BgDmyTdd.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -3
- package/dist/internal/caller.d.ts +90 -22
- package/dist/internal/caller.js +40 -40
- package/dist/internal/index.js +1 -1
- package/dist/internal/server.d.ts +8 -99
- package/dist/internal/server.js +15 -11
- package/dist/{job-store-D6Rjj5Oi.d.ts → job-store-BVeF5_Jq.d.ts} +1 -1
- package/dist/{lightning-backend-BaeCPmx7.d.ts → lightning-backend-40fIzz1W.d.ts} +1 -1
- package/dist/{postgres-consumed-credential-store-CUQJKRSL.js → postgres-consumed-credential-store-BUVCAPLS.js} +13 -0
- package/dist/{postgres-job-store-VLXLWXUQ.js → postgres-job-store-VHLQBJZC.js} +3 -1
- package/dist/server/index.d.ts +28 -10
- package/dist/server/index.js +24 -10
- package/dist/{step-cache-Bi43A0JN.d.ts → step-cache-CPdH1hei.d.ts} +3 -3
- package/dist/{tempo-lifecycle-OTHV757Y.js → tempo-lifecycle-ZBBLFBMH.js} +5 -4
- package/dist/{tempo-wallet-32JWQZKQ.js → tempo-wallet-B5XXV3YH.js} +1 -1
- package/dist/testing/index.d.ts +9 -2
- package/dist/testing/index.js +6 -3
- package/dist/{usd-AZPVq4eU.d.ts → usd-AWlrwlM5.d.ts} +1 -1
- package/dist/{x402-CEDHGZDI.js → x402-R6HPBY5D.js} +5 -4
- package/package.json +3 -2
- package/recipes/postgres/financial-export-v1.sql +137 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/sdk/step-cache.ts
|
|
2
|
+
var StepCache = class _StepCache {
|
|
3
|
+
cache = /* @__PURE__ */ new Map();
|
|
4
|
+
has(id) {
|
|
5
|
+
return this.cache.has(id);
|
|
6
|
+
}
|
|
7
|
+
get(id) {
|
|
8
|
+
return this.getRecord(id).value;
|
|
9
|
+
}
|
|
10
|
+
getRecord(id) {
|
|
11
|
+
const record = this.cache.get(id);
|
|
12
|
+
if (record === void 0) throw new Error(`StepCache: no cached result for step "${id}"`);
|
|
13
|
+
return record;
|
|
14
|
+
}
|
|
15
|
+
set(id, value, costs) {
|
|
16
|
+
this.cache.set(id, {
|
|
17
|
+
id,
|
|
18
|
+
value,
|
|
19
|
+
...costs !== void 0 && costs.length > 0 ? { costs: costs.map(({ amount, currency }) => ({ amount, currency })) } : {}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
serialize() {
|
|
23
|
+
return [...this.cache.values()];
|
|
24
|
+
}
|
|
25
|
+
static deserialize(records) {
|
|
26
|
+
const cache = new _StepCache();
|
|
27
|
+
for (const { id, value, costs } of records) {
|
|
28
|
+
cache.set(id, value, costs);
|
|
29
|
+
}
|
|
30
|
+
return cache;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
StepCache
|
|
36
|
+
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContentHoldError,
|
|
3
|
+
applyContentHoldMutation
|
|
4
|
+
} from "./chunk-AFV77S4C.js";
|
|
1
5
|
import {
|
|
2
6
|
withSdkInitLock
|
|
3
7
|
} from "./chunk-COYKJRMM.js";
|
|
@@ -228,6 +232,18 @@ var PostgresJobStore = class {
|
|
|
228
232
|
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS receipt JSONB;
|
|
229
233
|
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS receipt_seq BIGINT;
|
|
230
234
|
ALTER TABLE jobs ADD COLUMN IF NOT EXISTS redacted_at BIGINT;
|
|
235
|
+
CREATE TABLE IF NOT EXISTS job_content_holds (
|
|
236
|
+
id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
|
|
237
|
+
data JSONB NOT NULL
|
|
238
|
+
);
|
|
239
|
+
CREATE INDEX IF NOT EXISTS job_content_holds_job_idx ON job_content_holds(job_id);
|
|
240
|
+
CREATE TABLE IF NOT EXISTS job_content_hold_events (
|
|
241
|
+
event_position BIGSERIAL UNIQUE,
|
|
242
|
+
id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
|
|
243
|
+
data JSONB NOT NULL
|
|
244
|
+
);
|
|
245
|
+
CREATE INDEX IF NOT EXISTS job_content_hold_events_job_idx ON job_content_hold_events(job_id);
|
|
246
|
+
|
|
231
247
|
-- credit-ledger linkage. Written at submission alongside the
|
|
232
248
|
-- payment fields; the terminal funnel settles/releases the draw and the
|
|
233
249
|
-- receipt countersigns the ReceiptCredit block from it.
|
|
@@ -536,7 +552,7 @@ var PostgresJobStore = class {
|
|
|
536
552
|
required_msats = EXCLUDED.required_msats,
|
|
537
553
|
step_cache = EXCLUDED.step_cache,
|
|
538
554
|
state = EXCLUDED.state,
|
|
539
|
-
last_activity_at = EXCLUDED.last_activity_at
|
|
555
|
+
last_activity_at = CASE WHEN jobs.status IN ('completed', 'failed', 'cancelled') THEN jobs.last_activity_at ELSE EXCLUDED.last_activity_at END
|
|
540
556
|
-- terminal is terminal. On a multi-machine DVM a DELETE can
|
|
541
557
|
-- land on a machine that isn't running the handler, which cancels the
|
|
542
558
|
-- job in the store only; the handler's machine never learns of it and
|
|
@@ -737,6 +753,7 @@ var PostgresJobStore = class {
|
|
|
737
753
|
const client = await this.pool.connect();
|
|
738
754
|
try {
|
|
739
755
|
await client.query("BEGIN");
|
|
756
|
+
await client.query("SELECT id FROM jobs WHERE id = $1 FOR UPDATE", [args.jobId]);
|
|
740
757
|
const { rowCount } = await client.query(
|
|
741
758
|
`UPDATE jobs SET
|
|
742
759
|
input = '',
|
|
@@ -760,6 +777,7 @@ var PostgresJobStore = class {
|
|
|
760
777
|
AND last_activity_at = $2
|
|
761
778
|
AND redacted_at IS NULL
|
|
762
779
|
AND status IN ('completed', 'failed', 'cancelled')
|
|
780
|
+
AND NOT EXISTS (SELECT 1 FROM job_content_holds h WHERE h.job_id=jobs.id AND h.data->'cleared_at' = 'null'::jsonb)
|
|
763
781
|
AND ($4::boolean = false OR NOT EXISTS (
|
|
764
782
|
SELECT 1 FROM job_messages m WHERE m.job_id=jobs.id
|
|
765
783
|
AND m.data->>'type'='payment'
|
|
@@ -781,6 +799,38 @@ var PostgresJobStore = class {
|
|
|
781
799
|
client.release();
|
|
782
800
|
}
|
|
783
801
|
}
|
|
802
|
+
async getContentSnapshot(jobId, dvmId) {
|
|
803
|
+
const { rows } = await this.pool.query(
|
|
804
|
+
`SELECT j.*, COALESCE((SELECT jsonb_agg(h.data ORDER BY h.data->>'created_at', h.id) FROM job_content_holds h WHERE h.job_id=j.id), '[]'::jsonb) AS content_holds,
|
|
805
|
+
COALESCE((SELECT jsonb_agg(e.data ORDER BY e.event_position) FROM job_content_hold_events e WHERE e.job_id=j.id), '[]'::jsonb) AS content_hold_events
|
|
806
|
+
FROM jobs j WHERE j.id=$1 AND ($2::text IS NULL OR j.dvm_id IS NULL OR j.dvm_id=$2)`,
|
|
807
|
+
[jobId, dvmId ?? null]
|
|
808
|
+
);
|
|
809
|
+
if (!rows[0]) return void 0;
|
|
810
|
+
return { job: rowToRecord(rows[0]), history: { holds: rows[0].content_holds, events: rows[0].content_hold_events } };
|
|
811
|
+
}
|
|
812
|
+
async mutateContentHold(jobId, mutation, dvmId) {
|
|
813
|
+
const client = await this.pool.connect();
|
|
814
|
+
try {
|
|
815
|
+
await client.query("BEGIN");
|
|
816
|
+
const { rows } = await client.query("SELECT * FROM jobs WHERE id=$1 AND ($2::text IS NULL OR dvm_id IS NULL OR dvm_id=$2) FOR UPDATE", [jobId, dvmId ?? null]);
|
|
817
|
+
if (!rows[0]) throw new ContentHoldError("job_not_found");
|
|
818
|
+
const holds = await client.query("SELECT data FROM job_content_holds WHERE job_id=$1 ORDER BY data->>'created_at', id", [jobId]);
|
|
819
|
+
const events = await client.query("SELECT data FROM job_content_hold_events WHERE job_id=$1 ORDER BY event_position", [jobId]);
|
|
820
|
+
const history = applyContentHoldMutation(rowToRecord(rows[0]), { holds: holds.rows.map((row) => row.data), events: events.rows.map((row) => row.data) }, mutation);
|
|
821
|
+
const event = history.events.at(-1);
|
|
822
|
+
const hold = history.holds.find((item) => item.id === event.hold_id);
|
|
823
|
+
await client.query(`INSERT INTO job_content_holds(id,job_id,data) VALUES($1,$2,$3) ON CONFLICT(id) DO UPDATE SET data=EXCLUDED.data`, [hold.id, jobId, JSON.stringify(hold)]);
|
|
824
|
+
await client.query("INSERT INTO job_content_hold_events(id,job_id,data) VALUES($1,$2,$3)", [event.id, jobId, JSON.stringify(event)]);
|
|
825
|
+
await client.query("COMMIT");
|
|
826
|
+
return history;
|
|
827
|
+
} catch (error) {
|
|
828
|
+
await client.query("ROLLBACK");
|
|
829
|
+
throw error;
|
|
830
|
+
} finally {
|
|
831
|
+
client.release();
|
|
832
|
+
}
|
|
833
|
+
}
|
|
784
834
|
async claimReceiptSeq(jobId) {
|
|
785
835
|
const client = await this.pool.connect();
|
|
786
836
|
try {
|
|
@@ -828,7 +878,7 @@ var PostgresJobStore = class {
|
|
|
828
878
|
try {
|
|
829
879
|
await client.query("BEGIN");
|
|
830
880
|
const { rows } = await client.query(
|
|
831
|
-
`UPDATE jobs SET next_seq = next_seq + 1, last_activity_at = $2,
|
|
881
|
+
`UPDATE jobs SET next_seq = next_seq + 1, last_activity_at = CASE WHEN status IN ('completed','failed','cancelled') THEN last_activity_at ELSE $2 END,
|
|
832
882
|
awaiting_input_at_ms = CASE WHEN $3::boolean AND status NOT IN ('completed', 'failed', 'cancelled')
|
|
833
883
|
THEN COALESCE(awaiting_input_at_ms, $2) ELSE awaiting_input_at_ms END,
|
|
834
884
|
status = CASE WHEN $3::boolean AND status NOT IN ('completed', 'failed', 'cancelled')
|
|
@@ -925,7 +975,7 @@ var PostgresJobStore = class {
|
|
|
925
975
|
await client.query("BEGIN");
|
|
926
976
|
if (message.type === "payment") await this.hostingCleanupFence?.assertNewIntent(client);
|
|
927
977
|
const { rows } = await client.query(
|
|
928
|
-
`UPDATE jobs SET next_seq = next_seq + 1, last_activity_at = $2
|
|
978
|
+
`UPDATE jobs SET next_seq = next_seq + 1, last_activity_at = CASE WHEN status IN ('completed','failed','cancelled') THEN last_activity_at ELSE $2 END
|
|
929
979
|
WHERE id = $1 AND redacted_at IS NULL
|
|
930
980
|
RETURNING (next_seq - 1) AS seq`,
|
|
931
981
|
[jobId, Date.now()]
|
|
@@ -1024,7 +1074,7 @@ var PostgresJobStore = class {
|
|
|
1024
1074
|
cashu_flow = COALESCE($9, cashu_flow),
|
|
1025
1075
|
pending_x402_nonce = CASE WHEN $11::boolean THEN NULL ELSE pending_x402_nonce END,
|
|
1026
1076
|
pending_x402_amount_usdc_micro = CASE WHEN $11::boolean THEN NULL ELSE pending_x402_amount_usdc_micro END,
|
|
1027
|
-
last_activity_at = $12
|
|
1077
|
+
last_activity_at = CASE WHEN status IN ('completed','failed','cancelled') THEN last_activity_at ELSE $12 END
|
|
1028
1078
|
WHERE id = $1
|
|
1029
1079
|
RETURNING status, paid_msats, pending_payment_msats, credit_id, draw_id`,
|
|
1030
1080
|
params
|
|
@@ -103,7 +103,7 @@ async function generateTempoKey(opts = {}) {
|
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
if (existing && opts.force) {
|
|
106
|
-
const { assertTempoWalletIdle } = await import("./tempo-lifecycle-
|
|
106
|
+
const { assertTempoWalletIdle } = await import("./tempo-lifecycle-ZBBLFBMH.js");
|
|
107
107
|
await assertTempoWalletIdle("replace");
|
|
108
108
|
}
|
|
109
109
|
const { privateKeyToAccount, generatePrivateKey } = await import("viem/accounts");
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
DEFAULT_CREDIT_TTL_SECONDS,
|
|
5
5
|
DEFAULT_JOB_RETENTION_DAYS,
|
|
6
6
|
isZodSchema
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RCM3P2HE.js";
|
|
8
8
|
import {
|
|
9
9
|
InvalidCurrencyError,
|
|
10
10
|
validateCurrency
|
|
@@ -47,7 +47,7 @@ function configureDVM(config) {
|
|
|
47
47
|
}
|
|
48
48
|
function resolveJobRetentionDays(raw) {
|
|
49
49
|
const days = raw ?? DEFAULT_JOB_RETENTION_DAYS;
|
|
50
|
-
if (!Number.isSafeInteger(days) || days < 0) {
|
|
50
|
+
if (!Number.isSafeInteger(days) || days < 0 || days > Math.floor((Number.MAX_SAFE_INTEGER - Date.now()) / 864e5)) {
|
|
51
51
|
throw new Error(
|
|
52
52
|
`configureDVM: jobRetentionDays must be a non-negative whole number of days; received ${JSON.stringify(raw)}`
|
|
53
53
|
);
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-FYRR2UEZ.js";
|
|
15
15
|
import {
|
|
16
16
|
PostgresJobStore
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-EQLA4FFY.js";
|
|
18
18
|
import {
|
|
19
19
|
parseStoreValue
|
|
20
20
|
} from "./chunk-YG7G4DPZ.js";
|
|
@@ -25,24 +25,30 @@ import {
|
|
|
25
25
|
humanizeZodIssues,
|
|
26
26
|
signedRequestInput,
|
|
27
27
|
zodIssuesOf
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-N5RZ5HVB.js";
|
|
29
29
|
import {
|
|
30
|
-
JobCancelledError,
|
|
31
30
|
MemoryJobStore,
|
|
32
31
|
UNCAPPABLE_ASK_TOTAL,
|
|
33
|
-
abortJob,
|
|
34
32
|
accumulateAskedTopUp,
|
|
35
33
|
createInstrumentedFetch,
|
|
36
|
-
fromJobRecord,
|
|
37
34
|
isJobRetentionStore,
|
|
38
35
|
isReceiptIssuingStore,
|
|
39
|
-
isStreamableJobStore
|
|
36
|
+
isStreamableJobStore
|
|
37
|
+
} from "./chunk-IV6Q7NRW.js";
|
|
38
|
+
import {
|
|
39
|
+
ContentHoldError,
|
|
40
|
+
JobCancelledError,
|
|
41
|
+
abortJob,
|
|
42
|
+
baselineContentExpiry,
|
|
43
|
+
contentMetadata,
|
|
44
|
+
fromJobRecord,
|
|
45
|
+
isContentHoldStore,
|
|
40
46
|
isTerminal,
|
|
41
47
|
isYieldMessage,
|
|
42
48
|
localMessage,
|
|
43
49
|
providerMessage,
|
|
44
50
|
toJobRecord
|
|
45
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-AFV77S4C.js";
|
|
46
52
|
import {
|
|
47
53
|
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
48
54
|
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
@@ -52,7 +58,6 @@ import {
|
|
|
52
58
|
attestedDvmIdentity,
|
|
53
59
|
buildAdminChallenge,
|
|
54
60
|
checkMintHealth,
|
|
55
|
-
computeResultHash,
|
|
56
61
|
createMonotonicClock,
|
|
57
62
|
getWallet,
|
|
58
63
|
isPublicRequestId,
|
|
@@ -61,13 +66,10 @@ import {
|
|
|
61
66
|
normaliseMintUrl,
|
|
62
67
|
parseTraceparent,
|
|
63
68
|
receiptPubkeyFromSecret,
|
|
64
|
-
signDrainReceipt,
|
|
65
|
-
signFundingReceipt,
|
|
66
|
-
signReceipt,
|
|
67
69
|
toLockPubkey,
|
|
68
70
|
verifyCashuToken,
|
|
69
71
|
verifyChallenge
|
|
70
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-OQA7IHI4.js";
|
|
71
73
|
import {
|
|
72
74
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
73
75
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -85,28 +87,36 @@ import {
|
|
|
85
87
|
} from "./chunk-IWQHUSMO.js";
|
|
86
88
|
import {
|
|
87
89
|
IMPLICIT_CREDIT_TTL_MS,
|
|
90
|
+
computeResultHash,
|
|
88
91
|
formatStaleness,
|
|
89
92
|
msatsToUsd,
|
|
90
93
|
msatsToUsdc,
|
|
94
|
+
signDrainReceipt,
|
|
95
|
+
signFundingReceipt,
|
|
96
|
+
signReceipt,
|
|
91
97
|
usdcToMsats
|
|
92
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-PAIM66YA.js";
|
|
93
99
|
import {
|
|
94
100
|
resolveRpcOverride
|
|
95
101
|
} from "./chunk-CZULM42S.js";
|
|
96
102
|
import {
|
|
97
103
|
RAIL_REFUNDABLE,
|
|
98
104
|
canonicalRequestPath,
|
|
99
|
-
canonicalize,
|
|
100
105
|
isZodSchema
|
|
101
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-RCM3P2HE.js";
|
|
107
|
+
import {
|
|
108
|
+
canonicalize
|
|
109
|
+
} from "./chunk-OFVV37XC.js";
|
|
102
110
|
import {
|
|
103
|
-
StepCache,
|
|
104
111
|
createConsoleLogger,
|
|
105
112
|
loggers,
|
|
106
113
|
pinDimension,
|
|
107
114
|
withSpan,
|
|
108
115
|
withTraceContext
|
|
109
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-QR2EF2NI.js";
|
|
117
|
+
import {
|
|
118
|
+
StepCache
|
|
119
|
+
} from "./chunk-BQJIFRAZ.js";
|
|
110
120
|
import {
|
|
111
121
|
FxRateUnavailableError,
|
|
112
122
|
InvalidCurrencyError,
|
|
@@ -766,6 +776,15 @@ var MemoryConsumedCredentialStore = class {
|
|
|
766
776
|
}
|
|
767
777
|
return Promise.resolve(true);
|
|
768
778
|
}
|
|
779
|
+
consume(realm, challengeId, ttlSeconds) {
|
|
780
|
+
const key = `${realm}:${challengeId}`;
|
|
781
|
+
const now = Date.now();
|
|
782
|
+
if ((this.data.get(key) ?? 0) > now) return Promise.resolve(false);
|
|
783
|
+
for (const [entry, expiry] of this.data) if (expiry <= now) this.data.delete(entry);
|
|
784
|
+
if (this.data.size >= this.maxEntries) return Promise.reject(new Error("Nonce store capacity exhausted"));
|
|
785
|
+
this.data.set(key, now + ttlSeconds * 1e3);
|
|
786
|
+
return Promise.resolve(true);
|
|
787
|
+
}
|
|
769
788
|
mark(realm, challengeId, ttlSeconds) {
|
|
770
789
|
const key = `${realm}:${challengeId}`;
|
|
771
790
|
if (this.data.has(key)) this.data.delete(key);
|
|
@@ -3777,7 +3796,7 @@ async function verifyUpfrontPayment(opts) {
|
|
|
3777
3796
|
x402Requirements
|
|
3778
3797
|
});
|
|
3779
3798
|
}
|
|
3780
|
-
const { verifyX402Payment } = await import("./x402-
|
|
3799
|
+
const { verifyX402Payment } = await import("./x402-R6HPBY5D.js");
|
|
3781
3800
|
const receipt = await verifyX402Payment(
|
|
3782
3801
|
x402Payment,
|
|
3783
3802
|
x402Config,
|
|
@@ -4752,7 +4771,7 @@ async function verifyIncomingPayment(body, opts, snapshot) {
|
|
|
4752
4771
|
snapshot
|
|
4753
4772
|
});
|
|
4754
4773
|
}
|
|
4755
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4774
|
+
const { verifyX402Payment } = await import("./x402-R6HPBY5D.js");
|
|
4756
4775
|
const receipt = await verifyX402Payment(
|
|
4757
4776
|
body.content.x402_payment,
|
|
4758
4777
|
opts.x402Config,
|
|
@@ -5177,7 +5196,7 @@ async function processIncomingPayment(job, body, opts) {
|
|
|
5177
5196
|
try {
|
|
5178
5197
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5179
5198
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5180
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5199
|
+
const { verifyX402Payment } = await import("./x402-R6HPBY5D.js");
|
|
5181
5200
|
const receipt = await verifyX402Payment(
|
|
5182
5201
|
body.content.x402_payment,
|
|
5183
5202
|
opts.x402Config,
|
|
@@ -8705,10 +8724,14 @@ async function verifyAdminAuth(opts) {
|
|
|
8705
8724
|
nonceHex,
|
|
8706
8725
|
unixSeconds: unixStr
|
|
8707
8726
|
});
|
|
8708
|
-
const matched = opts.acceptedPubkeys.
|
|
8727
|
+
const matched = opts.acceptedPubkeys.find((pk) => verifyChallenge(pk, digest, sigHex));
|
|
8709
8728
|
if (!matched) return { ok: false, reason: "bad_signature" };
|
|
8710
|
-
|
|
8711
|
-
|
|
8729
|
+
if (opts.nonceStore.consume) {
|
|
8730
|
+
if (!await opts.nonceStore.consume(NONCE_REALM, nonceHex, NONCE_TTL_SECONDS)) return { ok: false, reason: "replay" };
|
|
8731
|
+
} else {
|
|
8732
|
+
await opts.nonceStore.mark(NONCE_REALM, nonceHex, NONCE_TTL_SECONDS);
|
|
8733
|
+
}
|
|
8734
|
+
return { ok: true, actor: matched };
|
|
8712
8735
|
}
|
|
8713
8736
|
function installAdminCashuRoutes(app, opts) {
|
|
8714
8737
|
app.get("/admin/cashu/pending-batches", (c) => handlePendingBatches(c, opts));
|
|
@@ -8973,6 +8996,69 @@ async function handleRotateLockPubkey(c, opts) {
|
|
|
8973
8996
|
});
|
|
8974
8997
|
}
|
|
8975
8998
|
|
|
8999
|
+
// src/sdk/server/admin-content.ts
|
|
9000
|
+
function installAdminContentRoutes(app, opts) {
|
|
9001
|
+
app.get("/admin/jobs/:jobId/content-holds", (c) => handle(c, opts));
|
|
9002
|
+
app.post("/admin/jobs/:jobId/content-holds", (c) => handle(c, opts, "placed"));
|
|
9003
|
+
app.post("/admin/jobs/:jobId/content-holds/:holdId/renew", (c) => handle(c, opts, "renewed"));
|
|
9004
|
+
app.post("/admin/jobs/:jobId/content-holds/:holdId/clear", (c) => handle(c, opts, "cleared"));
|
|
9005
|
+
}
|
|
9006
|
+
async function handle(c, opts, action) {
|
|
9007
|
+
const loaded = await loadAdminLockPubkeyState(c, opts);
|
|
9008
|
+
if ("response" in loaded) return loaded.response;
|
|
9009
|
+
const auth = await verifyAdminAuth({
|
|
9010
|
+
authHeader: c.req.header("Authorization"),
|
|
9011
|
+
method: c.req.method,
|
|
9012
|
+
path: c.req.path,
|
|
9013
|
+
dvmId: opts.dvmId,
|
|
9014
|
+
acceptedPubkeys: [loaded.state.current, ...loaded.state.retired.map((item) => item.pubkey)],
|
|
9015
|
+
nonceStore: opts.nonceStore,
|
|
9016
|
+
now: opts.now
|
|
9017
|
+
});
|
|
9018
|
+
if (!auth.ok) return c.json({ error: "unauthorized", reason: auth.reason }, 401);
|
|
9019
|
+
if (!isContentHoldStore(opts.store) || !opts.nonceStore.consume) {
|
|
9020
|
+
return c.json({ error: "content_holds_unavailable", message: "This service requires a content-hold store and atomic admin nonce store." }, 503);
|
|
9021
|
+
}
|
|
9022
|
+
const jobId = c.req.param("jobId") ?? "";
|
|
9023
|
+
const at = opts.now?.() ?? Date.now();
|
|
9024
|
+
try {
|
|
9025
|
+
if (action) {
|
|
9026
|
+
let body;
|
|
9027
|
+
try {
|
|
9028
|
+
body = await c.req.json();
|
|
9029
|
+
} catch {
|
|
9030
|
+
return c.json({ error: "invalid_body", message: "Send a JSON object." }, 400);
|
|
9031
|
+
}
|
|
9032
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return c.json({ error: "invalid_body" }, 400);
|
|
9033
|
+
const allowed = action === "placed" ? ["purpose", "reason", "review_due_at"] : action === "renewed" ? ["reason", "review_due_at"] : ["reason"];
|
|
9034
|
+
if (Object.keys(body).some((key) => !allowed.includes(key))) return c.json({ error: "invalid_body", message: "Only purpose, reason and review_due_at are accepted as applicable; identity and times are server-derived." }, 400);
|
|
9035
|
+
if (typeof body.reason !== "string" || body.reason.trim().length === 0 || body.reason.length > 1e3) return c.json({ error: "invalid_reason", message: "reason must contain 1..1000 characters." }, 400);
|
|
9036
|
+
if (action !== "cleared" && (!Number.isSafeInteger(body.review_due_at) || body.review_due_at <= at)) return c.json({ error: "invalid_review_due_at", message: "review_due_at must be a future epoch millisecond integer." }, 400);
|
|
9037
|
+
const base2 = { reason: body.reason.trim(), actor: auth.actor, at };
|
|
9038
|
+
let mutation;
|
|
9039
|
+
if (action === "placed") {
|
|
9040
|
+
if (body.purpose !== "support" && body.purpose !== "dispute") return c.json({ error: "invalid_purpose" }, 400);
|
|
9041
|
+
mutation = { ...base2, action, purpose: body.purpose, review_due_at: body.review_due_at };
|
|
9042
|
+
} else if (action === "renewed") {
|
|
9043
|
+
mutation = { ...base2, action, hold_id: c.req.param("holdId") ?? "", review_due_at: body.review_due_at };
|
|
9044
|
+
} else mutation = { ...base2, action, hold_id: c.req.param("holdId") ?? "" };
|
|
9045
|
+
await opts.store.mutateContentHold(jobId, mutation, opts.dvmId);
|
|
9046
|
+
}
|
|
9047
|
+
const snapshot = await opts.store.getContentSnapshot(jobId, opts.dvmId);
|
|
9048
|
+
if (!snapshot) return c.json({ error: "job_not_found" }, 404);
|
|
9049
|
+
return c.json({
|
|
9050
|
+
job_id: jobId,
|
|
9051
|
+
...contentMetadata(snapshot.job, opts.retentionDays, snapshot.history.holds),
|
|
9052
|
+
baseline_content_expires_at: baselineContentExpiry(snapshot.job, opts.retentionDays),
|
|
9053
|
+
holds: snapshot.history.holds.map((hold) => ({ ...hold, review_overdue: hold.cleared_at === null && hold.review_due_at <= at })),
|
|
9054
|
+
events: snapshot.history.events
|
|
9055
|
+
}, action === "placed" ? 201 : 200);
|
|
9056
|
+
} catch (error) {
|
|
9057
|
+
if (error instanceof ContentHoldError) return c.json({ error: error.code }, error.code === "job_not_found" ? 404 : 409);
|
|
9058
|
+
throw error;
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
|
|
8976
9062
|
// src/sdk/server/x402-exact-settlement.ts
|
|
8977
9063
|
import { createHash as createHash4 } from "crypto";
|
|
8978
9064
|
var X402ExactSettlementServer = class {
|
|
@@ -19942,6 +20028,13 @@ var DVMServer = class {
|
|
|
19942
20028
|
...payoutReporter ? { onMelted: payoutReporter.meltHook() } : {}
|
|
19943
20029
|
});
|
|
19944
20030
|
}
|
|
20031
|
+
installAdminContentRoutes(this.app, {
|
|
20032
|
+
dvmId,
|
|
20033
|
+
store: this.jobManager.store,
|
|
20034
|
+
retentionDays: this.descriptor.jobRetentionDays,
|
|
20035
|
+
getLockPubkeyState: () => this.loadActiveLockPubkeyState(db, dvmId),
|
|
20036
|
+
nonceStore: this.adminCashuNonceStore
|
|
20037
|
+
});
|
|
19945
20038
|
installAdminCreditRoutes(this.app, {
|
|
19946
20039
|
db,
|
|
19947
20040
|
dvmId,
|
|
@@ -20248,12 +20341,12 @@ var DVMServer = class {
|
|
|
20248
20341
|
ownerDisplayBlock() {
|
|
20249
20342
|
const owner = this.opts.owner;
|
|
20250
20343
|
const env = this.opts.env;
|
|
20251
|
-
const
|
|
20252
|
-
if (!
|
|
20344
|
+
const handle2 = owner?.handle ?? env.DVMKIT_OWNER_HANDLE;
|
|
20345
|
+
if (!handle2) return null;
|
|
20253
20346
|
const type = owner?.type ?? env.DVMKIT_OWNER_TYPE ?? "builder";
|
|
20254
20347
|
const displayName = owner?.displayName ?? env.DVMKIT_OWNER_DISPLAY_NAME ?? null;
|
|
20255
20348
|
const avatarUrl = owner?.avatarUrl ?? env.DVMKIT_OWNER_AVATAR_URL ?? null;
|
|
20256
|
-
return { handle, display_name: displayName, avatar_url: avatarUrl, type };
|
|
20349
|
+
return { handle: handle2, display_name: displayName, avatar_url: avatarUrl, type };
|
|
20257
20350
|
}
|
|
20258
20351
|
defaultPaymentMethods() {
|
|
20259
20352
|
const methods = [];
|
|
@@ -21138,7 +21231,7 @@ var DVMServer = class {
|
|
|
21138
21231
|
if (prior?.requestId !== requestId || prior.requesterPubkey !== authPubkey || prior.requestFingerprint !== fingerprint || prior.requesterId !== c.get("requesterId")) {
|
|
21139
21232
|
return requestResultNotFound(c, requestId);
|
|
21140
21233
|
}
|
|
21141
|
-
return buildRequestResultResponse(c, requestId, prior);
|
|
21234
|
+
return buildRequestResultResponse(c, requestId, prior, await this.contentFields(prior));
|
|
21142
21235
|
}
|
|
21143
21236
|
async handleCreateJob(c, resumeOnly = false) {
|
|
21144
21237
|
if (!resumeOnly) this.maybeSweepTempoSessions();
|
|
@@ -21268,7 +21361,7 @@ var DVMServer = class {
|
|
|
21268
21361
|
const prior = priorId ? await this.jobManager.store.get(priorId) : void 0;
|
|
21269
21362
|
if (prior && prior.requestFingerprint === fingerprint && prior.requesterId === c.get("requesterId") && (!this.descriptor.auth || prior.requesterPubkey === authPubkey)) {
|
|
21270
21363
|
await this.jobManager.ensureReceipt(prior);
|
|
21271
|
-
return
|
|
21364
|
+
return await this.submitResponse(c, prior, prior.requesterToken);
|
|
21272
21365
|
}
|
|
21273
21366
|
}
|
|
21274
21367
|
if (!resumeOnly && this.opts.hostingControlInstance && creditEnvelope && authPubkey) {
|
|
@@ -21310,7 +21403,7 @@ var DVMServer = class {
|
|
|
21310
21403
|
if (publicRequestId && authPubkey && recoveryStore) {
|
|
21311
21404
|
const prior = await recoveryStore.findJobByRequestId(publicRequestId, authPubkey);
|
|
21312
21405
|
if (prior) {
|
|
21313
|
-
return resumeOnly ?
|
|
21406
|
+
return resumeOnly ? await this.submitResponse(c, prior, prior.requesterToken) : requestIdExists(c, publicRequestId);
|
|
21314
21407
|
}
|
|
21315
21408
|
}
|
|
21316
21409
|
let jobId = publicRequestClaimResult?.jobId ?? this.jobManager.allocateJobId();
|
|
@@ -21526,7 +21619,7 @@ var DVMServer = class {
|
|
|
21526
21619
|
exactAdmission.intentId
|
|
21527
21620
|
);
|
|
21528
21621
|
await this.jobManager.ensureReceipt(prior);
|
|
21529
|
-
return
|
|
21622
|
+
return await this.submitResponse(c, prior, prior.requesterToken);
|
|
21530
21623
|
}
|
|
21531
21624
|
}
|
|
21532
21625
|
if (this.descriptor.auth && authEnvelope) {
|
|
@@ -21587,7 +21680,7 @@ var DVMServer = class {
|
|
|
21587
21680
|
await jm.finalizeStoredTerminal(jobId);
|
|
21588
21681
|
}
|
|
21589
21682
|
const terminal = await store.get(jobId);
|
|
21590
|
-
if (terminal) return
|
|
21683
|
+
if (terminal) return await this.submitResponse(c, terminal, jobToken);
|
|
21591
21684
|
}
|
|
21592
21685
|
if (exactAdmission) {
|
|
21593
21686
|
await this.x402ExactSettlement?.completeAdmission(
|
|
@@ -21602,7 +21695,7 @@ var DVMServer = class {
|
|
|
21602
21695
|
setTimeout(resolve, 0);
|
|
21603
21696
|
});
|
|
21604
21697
|
await awaitTerminalWork(job);
|
|
21605
|
-
let response =
|
|
21698
|
+
let response = await this.submitResponse(c, job, jobToken);
|
|
21606
21699
|
if (payment.x402SettleResponseHeader) {
|
|
21607
21700
|
response = new Response(response.body, response);
|
|
21608
21701
|
response.headers.set("PAYMENT-RESPONSE", payment.x402SettleResponseHeader);
|
|
@@ -21690,7 +21783,7 @@ var DVMServer = class {
|
|
|
21690
21783
|
return void 0;
|
|
21691
21784
|
}
|
|
21692
21785
|
await this.jobManager.ensureReceipt(prior);
|
|
21693
|
-
return
|
|
21786
|
+
return await this.submitResponse(c, prior, prior.requesterToken);
|
|
21694
21787
|
}
|
|
21695
21788
|
/**
|
|
21696
21789
|
* Resolve the recorded draw's job under the same fingerprint/pubkey/requester
|
|
@@ -21735,7 +21828,7 @@ var DVMServer = class {
|
|
|
21735
21828
|
return void 0;
|
|
21736
21829
|
}
|
|
21737
21830
|
await this.jobManager.ensureReceipt(prior);
|
|
21738
|
-
return
|
|
21831
|
+
return await this.submitResponse(c, prior, prior.requesterToken);
|
|
21739
21832
|
}
|
|
21740
21833
|
/**
|
|
21741
21834
|
* Resume the gap between a durable x402/Tempo marker+draw commit and the
|
|
@@ -21884,6 +21977,23 @@ var DVMServer = class {
|
|
|
21884
21977
|
};
|
|
21885
21978
|
}
|
|
21886
21979
|
}
|
|
21980
|
+
async submitResponse(c, job, jobToken) {
|
|
21981
|
+
const snapshot = { ...job, messages: [...job.messages] };
|
|
21982
|
+
return buildSubmitResponse(c, snapshot, jobToken, await this.contentFields(snapshot));
|
|
21983
|
+
}
|
|
21984
|
+
async contentFields(job) {
|
|
21985
|
+
const store = this.jobManager.store;
|
|
21986
|
+
const responseStatus = job.status;
|
|
21987
|
+
if (isContentHoldStore(store)) {
|
|
21988
|
+
const snapshot = await store.getContentSnapshot(job.id);
|
|
21989
|
+
if (snapshot) return { ...contentMetadata({ ...snapshot.job, status: responseStatus }, this.descriptor.jobRetentionDays, snapshot.history.holds) };
|
|
21990
|
+
}
|
|
21991
|
+
return isContentHoldStore(store) ? { ...contentMetadata(job, this.descriptor.jobRetentionDays) } : {};
|
|
21992
|
+
}
|
|
21993
|
+
async messageFields(job, messages) {
|
|
21994
|
+
const fields = await this.contentFields(job);
|
|
21995
|
+
return { messages: fields.content_available === false ? [] : messages, ...fields };
|
|
21996
|
+
}
|
|
21887
21997
|
async handleGetJob(c) {
|
|
21888
21998
|
if (!jobCredential.presents(c.req)) return c.json(jobCredential.body(), 401);
|
|
21889
21999
|
const id = c.req.param("id") ?? "";
|
|
@@ -21892,7 +22002,8 @@ var DVMServer = class {
|
|
|
21892
22002
|
if (!isJobOwner(c, job)) return c.json({ error: "Job not found" }, 404);
|
|
21893
22003
|
await this.jobManager.ensureReceipt(job);
|
|
21894
22004
|
const res2 = { job_id: job.id, status: job.status };
|
|
21895
|
-
Object.assign(res2, terminalResponseFields(job));
|
|
22005
|
+
Object.assign(res2, terminalResponseFields(job), await this.contentFields(job));
|
|
22006
|
+
removeUnavailableContent(res2);
|
|
21896
22007
|
return c.json(res2);
|
|
21897
22008
|
}
|
|
21898
22009
|
const record = await this.jobManager.store.get(id);
|
|
@@ -21900,7 +22011,8 @@ var DVMServer = class {
|
|
|
21900
22011
|
if (!isJobOwner(c, record)) return c.json({ error: "Job not found" }, 404);
|
|
21901
22012
|
await this.jobManager.ensureReceipt(record);
|
|
21902
22013
|
const res = { job_id: record.id, status: record.status };
|
|
21903
|
-
Object.assign(res, terminalResponseFields(record));
|
|
22014
|
+
Object.assign(res, terminalResponseFields(record), await this.contentFields(record));
|
|
22015
|
+
removeUnavailableContent(res);
|
|
21904
22016
|
return c.json(res);
|
|
21905
22017
|
}
|
|
21906
22018
|
async handleGetMessages(c) {
|
|
@@ -21971,7 +22083,9 @@ var DVMServer = class {
|
|
|
21971
22083
|
});
|
|
21972
22084
|
});
|
|
21973
22085
|
}
|
|
22086
|
+
const fields = await this.contentFields(record);
|
|
21974
22087
|
return streamSSE(c, async (stream) => {
|
|
22088
|
+
if (fields.content_available === false) return;
|
|
21975
22089
|
for (const msg of filtered) {
|
|
21976
22090
|
await stream.writeSSE({ data: JSON.stringify(msg), event: "message" });
|
|
21977
22091
|
}
|
|
@@ -21979,13 +22093,15 @@ var DVMServer = class {
|
|
|
21979
22093
|
}
|
|
21980
22094
|
if (isStreamableJobStore(store) && !isTerminal(record.status)) {
|
|
21981
22095
|
const msgs = await store.getMessages(id, after);
|
|
21982
|
-
if (msgs.length > 0) return c.json(
|
|
22096
|
+
if (msgs.length > 0) return c.json(await this.messageFields(record, msgs));
|
|
21983
22097
|
}
|
|
21984
|
-
return c.json(
|
|
22098
|
+
return c.json(await this.messageFields(record, filtered));
|
|
21985
22099
|
}
|
|
21986
22100
|
if (!isJobOwner(c, job)) return c.json({ error: "Job not found" }, 404);
|
|
21987
22101
|
if (accept.includes("text/event-stream")) {
|
|
22102
|
+
const fields = await this.contentFields(job);
|
|
21988
22103
|
return streamSSE(c, async (stream) => {
|
|
22104
|
+
if (fields.content_available === false) return;
|
|
21989
22105
|
const existing = job.messages.filter((m) => m.seq > after);
|
|
21990
22106
|
let yielded = false;
|
|
21991
22107
|
for (const msg of existing) {
|
|
@@ -22014,7 +22130,7 @@ var DVMServer = class {
|
|
|
22014
22130
|
});
|
|
22015
22131
|
});
|
|
22016
22132
|
}
|
|
22017
|
-
return c.json(
|
|
22133
|
+
return c.json(await this.messageFields(job, job.messages.filter((m) => m.seq > after)));
|
|
22018
22134
|
}
|
|
22019
22135
|
jobNotActiveResponse(c) {
|
|
22020
22136
|
return c.json(
|
|
@@ -22258,12 +22374,13 @@ function timingSafeEqualHex(a, b) {
|
|
|
22258
22374
|
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
22259
22375
|
return diff === 0;
|
|
22260
22376
|
}
|
|
22261
|
-
function buildSubmitResponse(c, job, jobToken) {
|
|
22377
|
+
function buildSubmitResponse(c, job, jobToken, contentFields) {
|
|
22262
22378
|
const res = { job_id: job.id };
|
|
22263
22379
|
if (jobToken) res.job_token = jobToken;
|
|
22264
22380
|
if (job.status === "awaiting-input") res.status = "awaiting-input";
|
|
22265
22381
|
else if (job.status === "completed") res.status = "completed";
|
|
22266
|
-
Object.assign(res, terminalResponseFields(job));
|
|
22382
|
+
Object.assign(res, terminalResponseFields(job), contentFields);
|
|
22383
|
+
removeUnavailableContent(res);
|
|
22267
22384
|
return c.json(res, job.status === "completed" ? 200 : 202);
|
|
22268
22385
|
}
|
|
22269
22386
|
function requestRecoveryStore(store) {
|
|
@@ -22319,7 +22436,7 @@ function requestNotAccepted(c, requestId) {
|
|
|
22319
22436
|
409
|
|
22320
22437
|
);
|
|
22321
22438
|
}
|
|
22322
|
-
function buildRequestResultResponse(c, requestId, job) {
|
|
22439
|
+
function buildRequestResultResponse(c, requestId, job, contentFields) {
|
|
22323
22440
|
const status = job.status === "working" ? "processing" : job.status;
|
|
22324
22441
|
const res = {
|
|
22325
22442
|
request_id: requestId,
|
|
@@ -22328,7 +22445,8 @@ function buildRequestResultResponse(c, requestId, job) {
|
|
|
22328
22445
|
};
|
|
22329
22446
|
if (job.requesterToken) res.job_token = job.requesterToken;
|
|
22330
22447
|
if (job.paymentTransactionHash) res.transaction_hash = job.paymentTransactionHash;
|
|
22331
|
-
Object.assign(res, terminalResponseFields(job));
|
|
22448
|
+
Object.assign(res, terminalResponseFields(job), contentFields);
|
|
22449
|
+
removeUnavailableContent(res);
|
|
22332
22450
|
return c.json(res, isTerminal(job.status) ? 200 : 202);
|
|
22333
22451
|
}
|
|
22334
22452
|
function requestResultNotFound(c, requestId) {
|
|
@@ -22356,6 +22474,14 @@ async function awaitTerminalWork(job) {
|
|
|
22356
22474
|
]);
|
|
22357
22475
|
clearTimeout(timer);
|
|
22358
22476
|
}
|
|
22477
|
+
function removeUnavailableContent(fields) {
|
|
22478
|
+
if (fields.content_available === false) {
|
|
22479
|
+
delete fields.summary;
|
|
22480
|
+
fields.has_artifacts = false;
|
|
22481
|
+
delete fields.artifact_count;
|
|
22482
|
+
delete fields.payload_in;
|
|
22483
|
+
}
|
|
22484
|
+
}
|
|
22359
22485
|
function terminalResponseFields(job) {
|
|
22360
22486
|
const fields = {};
|
|
22361
22487
|
if (job.status === "completed") {
|
|
@@ -22748,6 +22874,7 @@ export {
|
|
|
22748
22874
|
handleCreditRequest,
|
|
22749
22875
|
installAdminCashuRoutes,
|
|
22750
22876
|
createAdminCashuNonceStore,
|
|
22877
|
+
installAdminContentRoutes,
|
|
22751
22878
|
X402ExactSettlementServer,
|
|
22752
22879
|
X402ExactSettlementEvidenceMissingError,
|
|
22753
22880
|
HostedSettlementReportObligations,
|