@dvmkit/sdk 0.5.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-AFV77S4C.js +258 -0
- package/dist/chunk-BQJIFRAZ.js +36 -0
- package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
- package/dist/{chunk-TZ2WKCDD.js → chunk-HCLVYZUZ.js} +2 -2
- package/dist/{chunk-EYTHFFFU.js → chunk-HHBDBMEO.js} +159 -34
- package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
- package/dist/{chunk-Z4KLECAC.js → chunk-N5RZ5HVB.js} +1 -1
- package/dist/{chunk-TUA7PYH7.js → chunk-OQA7IHI4.js} +124 -124
- package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
- package/dist/{chunk-A4YFWZ7R.js → chunk-RCM3P2HE.js} +1 -1
- package/dist/content-retention-BTGSQRfP.d.ts +78 -0
- package/dist/{credit-menu-CAfqe8IY.d.ts → credit-menu-BAsrRu3z.d.ts} +98 -5
- package/dist/{fx-D9nsN3n4.d.ts → fx-BgDmyTdd.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/internal/caller.d.ts +16 -15
- package/dist/internal/caller.js +7 -5
- package/dist/internal/server.d.ts +8 -99
- package/dist/internal/server.js +12 -9
- package/dist/{job-store-Bx-VFRjg.d.ts → job-store-BVeF5_Jq.d.ts} +1 -1
- package/dist/{lightning-backend-BAx2Y8pP.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 +22 -9
- package/dist/{step-cache-QG_h5-3G.d.ts → step-cache-CPdH1hei.d.ts} +2 -2
- package/dist/testing/index.d.ts +9 -2
- package/dist/testing/index.js +6 -3
- package/dist/{usd-EwRqCPF-.d.ts → usd-AWlrwlM5.d.ts} +1 -1
- package/package.json +3 -2
- package/recipes/postgres/financial-export-v1.sql +137 -0
|
@@ -1,191 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ContentHoldError,
|
|
3
|
+
applyContentHoldMutation,
|
|
4
|
+
isTerminal
|
|
5
|
+
} from "./chunk-AFV77S4C.js";
|
|
6
|
+
import {
|
|
3
7
|
loggers
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QR2EF2NI.js";
|
|
5
9
|
import {
|
|
6
10
|
redactUrl
|
|
7
11
|
} from "./chunk-FUJ36YDV.js";
|
|
8
12
|
|
|
9
|
-
// src/sdk/server/job.ts
|
|
10
|
-
function toJobRecord(job) {
|
|
11
|
-
return {
|
|
12
|
-
id: job.id,
|
|
13
|
-
tags: job.tags,
|
|
14
|
-
capability: job.capability,
|
|
15
|
-
input: job.input,
|
|
16
|
-
params: job.params,
|
|
17
|
-
requesterId: job.requesterId,
|
|
18
|
-
requesterTokenHash: job.requesterTokenHash,
|
|
19
|
-
requesterToken: job.requesterToken,
|
|
20
|
-
requestFingerprint: job.requestFingerprint,
|
|
21
|
-
requesterPubkey: job.requesterPubkey,
|
|
22
|
-
requestId: job.requestId,
|
|
23
|
-
authRequestPath: job.authRequestPath,
|
|
24
|
-
status: job.status,
|
|
25
|
-
summary: job.summary,
|
|
26
|
-
timingKnown: job.timingKnown,
|
|
27
|
-
endedBy: job.endedBy,
|
|
28
|
-
terminalAtMs: job.terminalAtMs,
|
|
29
|
-
callerWaitMs: job.callerWaitMs,
|
|
30
|
-
awaitingInputAtMs: job.awaitingInputAtMs,
|
|
31
|
-
messages: job.messages,
|
|
32
|
-
seq: job.seq,
|
|
33
|
-
paidMsats: job.paidMsats,
|
|
34
|
-
paymentMint: job.paymentMint,
|
|
35
|
-
paymentRail: job.paymentRail,
|
|
36
|
-
paymentTxHash: job.paymentTxHash,
|
|
37
|
-
paymentTransactionHash: job.paymentTransactionHash,
|
|
38
|
-
nativeAmount: job.nativeAmount,
|
|
39
|
-
nativeAsset: job.nativeAsset,
|
|
40
|
-
cashuFlow: job.cashuFlow,
|
|
41
|
-
costAmountMicro: job.costAmountMicro,
|
|
42
|
-
costCurrency: job.costCurrency,
|
|
43
|
-
costRevision: job.costRevision,
|
|
44
|
-
creditId: job.creditId,
|
|
45
|
-
drawId: job.drawId,
|
|
46
|
-
fundingReceipt: job.fundingReceipt,
|
|
47
|
-
fundingCredit: job.fundingCredit,
|
|
48
|
-
receivedProofs: job.receivedProofs,
|
|
49
|
-
pendingPaymentMsats: job.pendingPaymentMsats,
|
|
50
|
-
pendingMppChallengeIds: job.pendingMppChallengeIds,
|
|
51
|
-
pendingX402Nonce: job.pendingX402Nonce,
|
|
52
|
-
pendingX402AmountUsdcMicro: job.pendingX402AmountUsdcMicro,
|
|
53
|
-
pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
|
|
54
|
-
pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency,
|
|
55
|
-
askedTopUpMicro: job.askedTopUpMicro,
|
|
56
|
-
askedTopUpCurrency: job.askedTopUpCurrency,
|
|
57
|
-
topUpCapUnenforcedReason: job.topUpCapUnenforcedReason,
|
|
58
|
-
requiredMsats: job.requiredMsats,
|
|
59
|
-
receipt: job.receipt,
|
|
60
|
-
stepCache: job.stepCache.serialize(),
|
|
61
|
-
state: job.state,
|
|
62
|
-
createdAt: job.createdAt,
|
|
63
|
-
lastActivityAt: job.lastActivityAt
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function fromJobRecord(record) {
|
|
67
|
-
return {
|
|
68
|
-
id: record.id,
|
|
69
|
-
tags: record.tags,
|
|
70
|
-
capability: record.capability,
|
|
71
|
-
input: record.input,
|
|
72
|
-
params: record.params,
|
|
73
|
-
requesterId: record.requesterId,
|
|
74
|
-
requesterTokenHash: record.requesterTokenHash,
|
|
75
|
-
status: record.status,
|
|
76
|
-
summary: record.summary,
|
|
77
|
-
timingKnown: record.timingKnown,
|
|
78
|
-
endedBy: record.endedBy,
|
|
79
|
-
terminalAtMs: record.terminalAtMs,
|
|
80
|
-
callerWaitMs: record.callerWaitMs,
|
|
81
|
-
awaitingInputAtMs: record.awaitingInputAtMs,
|
|
82
|
-
messages: [...record.messages],
|
|
83
|
-
seq: record.seq,
|
|
84
|
-
paidMsats: record.paidMsats,
|
|
85
|
-
paymentMint: record.paymentMint,
|
|
86
|
-
paymentRail: record.paymentRail,
|
|
87
|
-
paymentTxHash: record.paymentTxHash,
|
|
88
|
-
paymentTransactionHash: record.paymentTransactionHash,
|
|
89
|
-
nativeAmount: record.nativeAmount,
|
|
90
|
-
nativeAsset: record.nativeAsset,
|
|
91
|
-
cashuFlow: record.cashuFlow,
|
|
92
|
-
costAmountMicro: record.costAmountMicro,
|
|
93
|
-
costCurrency: record.costCurrency,
|
|
94
|
-
costRevision: record.costRevision,
|
|
95
|
-
creditId: record.creditId,
|
|
96
|
-
drawId: record.drawId,
|
|
97
|
-
fundingReceipt: record.fundingReceipt,
|
|
98
|
-
fundingCredit: record.fundingCredit,
|
|
99
|
-
receivedProofs: [...record.receivedProofs],
|
|
100
|
-
pendingPaymentMsats: record.pendingPaymentMsats,
|
|
101
|
-
pendingMppChallengeIds: record.pendingMppChallengeIds ? [...record.pendingMppChallengeIds] : void 0,
|
|
102
|
-
requesterToken: record.requesterToken,
|
|
103
|
-
requestFingerprint: record.requestFingerprint,
|
|
104
|
-
requesterPubkey: record.requesterPubkey,
|
|
105
|
-
requestId: record.requestId,
|
|
106
|
-
authRequestPath: record.authRequestPath,
|
|
107
|
-
pendingX402Nonce: record.pendingX402Nonce,
|
|
108
|
-
pendingX402AmountUsdcMicro: record.pendingX402AmountUsdcMicro,
|
|
109
|
-
pendingPaymentFiatMicro: record.pendingPaymentFiatMicro,
|
|
110
|
-
pendingPaymentFiatCurrency: record.pendingPaymentFiatCurrency,
|
|
111
|
-
askedTopUpMicro: record.askedTopUpMicro,
|
|
112
|
-
askedTopUpCurrency: record.askedTopUpCurrency,
|
|
113
|
-
topUpCapUnenforcedReason: record.topUpCapUnenforcedReason,
|
|
114
|
-
requiredMsats: record.requiredMsats,
|
|
115
|
-
receipt: record.receipt,
|
|
116
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
117
|
-
abort: new AbortController(),
|
|
118
|
-
messageAppender: void 0,
|
|
119
|
-
messageAppenderTail: void 0,
|
|
120
|
-
stepCache: StepCache.deserialize(record.stepCache),
|
|
121
|
-
state: record.state,
|
|
122
|
-
pendingPrompts: /* @__PURE__ */ new Map(),
|
|
123
|
-
pendingPayment: null,
|
|
124
|
-
replayHighSeq: null,
|
|
125
|
-
replayProviderSkip: 0,
|
|
126
|
-
replayPaymentIndex: 0,
|
|
127
|
-
createdAt: record.createdAt,
|
|
128
|
-
lastActivityAt: record.lastActivityAt
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function abortJob(job, reason) {
|
|
132
|
-
job.abort.abort(new JobCancelledError(reason));
|
|
133
|
-
}
|
|
134
|
-
var JobCancelledError = class extends Error {
|
|
135
|
-
constructor(message = "Job cancelled") {
|
|
136
|
-
super(message);
|
|
137
|
-
this.name = "JobCancelledError";
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
function providerMessage(job, type, content) {
|
|
141
|
-
if (job.replayProviderSkip > 0) {
|
|
142
|
-
job.replayProviderSkip--;
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const msg = localMessage(job, "provider", type, content);
|
|
146
|
-
if (type === "payment-request") {
|
|
147
|
-
const paymentRequest = content;
|
|
148
|
-
const pendingPaymentDelta = typeof paymentRequest.amount_msats === "number" ? paymentRequest.amount_msats : void 0;
|
|
149
|
-
const pendingMppChallengeIds = paymentRequest.tempo?.challenges?.map((challenge) => challenge.id).filter((id) => typeof id === "string");
|
|
150
|
-
job.messageAppender?.(msg, {
|
|
151
|
-
awaitingInput: job.status === "awaiting-input",
|
|
152
|
-
pendingPaymentDelta,
|
|
153
|
-
pendingMppChallengeIds,
|
|
154
|
-
pendingX402Nonce: typeof paymentRequest.x402?.nonce === "string" ? paymentRequest.x402.nonce : void 0,
|
|
155
|
-
pendingX402AmountUsdcMicro: typeof paymentRequest.x402?.required_usdc_micro === "string" ? paymentRequest.x402.required_usdc_micro : void 0,
|
|
156
|
-
// read off the job rather than the content — the fiat envelope
|
|
157
|
-
// is ledger state, not part of the wire shape, and `requestPayment` has
|
|
158
|
-
// already pinned it on the job by the time this fires (same ordering the
|
|
159
|
-
// `pendingPaymentMsats` comment above describes).
|
|
160
|
-
pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
|
|
161
|
-
pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency
|
|
162
|
-
});
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
job.messageAppender?.(msg, type === "prompt" ? { awaitingInput: job.status === "awaiting-input" } : void 0);
|
|
166
|
-
}
|
|
167
|
-
function localMessage(job, from, type, content) {
|
|
168
|
-
job.seq++;
|
|
169
|
-
const msg = {
|
|
170
|
-
seq: job.seq,
|
|
171
|
-
from,
|
|
172
|
-
timestamp: Math.floor(Date.now() / 1e3),
|
|
173
|
-
type,
|
|
174
|
-
content
|
|
175
|
-
};
|
|
176
|
-
job.messages.push(msg);
|
|
177
|
-
for (const listener of job.listeners) {
|
|
178
|
-
listener(msg);
|
|
179
|
-
}
|
|
180
|
-
return msg;
|
|
181
|
-
}
|
|
182
|
-
function isTerminal(status) {
|
|
183
|
-
return status === "completed" || status === "failed" || status === "cancelled";
|
|
184
|
-
}
|
|
185
|
-
function isYieldMessage(msg) {
|
|
186
|
-
return msg.type === "prompt" || msg.type === "payment-request" || msg.type === "working" || msg.type === "complete" || msg.type === "cancel";
|
|
187
|
-
}
|
|
188
|
-
|
|
189
13
|
// src/sdk/server/job-store.ts
|
|
190
14
|
function accumulateAskedTopUp(prior, ask) {
|
|
191
15
|
const poisoned = (reason) => ({
|
|
@@ -219,6 +43,7 @@ function isStaleJobReapable(store) {
|
|
|
219
43
|
|
|
220
44
|
// src/sdk/server/memory-job-store.ts
|
|
221
45
|
var MemoryJobStore = class {
|
|
46
|
+
contentHolds = /* @__PURE__ */ new Map();
|
|
222
47
|
data = /* @__PURE__ */ new Map();
|
|
223
48
|
messages = /* @__PURE__ */ new Map();
|
|
224
49
|
nextSeq = /* @__PURE__ */ new Map();
|
|
@@ -290,6 +115,7 @@ var MemoryJobStore = class {
|
|
|
290
115
|
}
|
|
291
116
|
const snapshot = structuredClone(record);
|
|
292
117
|
if (existing) snapshot.createdAt = existing.createdAt;
|
|
118
|
+
if (existing && isTerminal(existing.status)) snapshot.lastActivityAt = existing.lastActivityAt;
|
|
293
119
|
if (existing?.receipt) snapshot.receipt = existing.receipt;
|
|
294
120
|
snapshot.timingKnown = existing ? existing.timingKnown ?? false : record.timingKnown ?? true;
|
|
295
121
|
applyTiming(snapshot, existing, Date.now());
|
|
@@ -302,6 +128,7 @@ var MemoryJobStore = class {
|
|
|
302
128
|
return Promise.resolve();
|
|
303
129
|
}
|
|
304
130
|
delete(id) {
|
|
131
|
+
this.contentHolds.delete(id);
|
|
305
132
|
this.data.delete(id);
|
|
306
133
|
this.messages.delete(id);
|
|
307
134
|
this.nextSeq.delete(id);
|
|
@@ -317,7 +144,7 @@ var MemoryJobStore = class {
|
|
|
317
144
|
}
|
|
318
145
|
redactJob(args) {
|
|
319
146
|
const record = this.data.get(args.jobId);
|
|
320
|
-
if (!record || record.redactedAt !== void 0 || !isTerminal(record.status) || record.lastActivityAt !== args.expectedLastActivityAt) {
|
|
147
|
+
if (!record || record.redactedAt !== void 0 || !isTerminal(record.status) || this.contentHolds.get(args.jobId)?.holds.some((hold) => hold.cleared_at === null) || record.lastActivityAt !== args.expectedLastActivityAt) {
|
|
321
148
|
return Promise.resolve(false);
|
|
322
149
|
}
|
|
323
150
|
record.input = "";
|
|
@@ -340,6 +167,17 @@ var MemoryJobStore = class {
|
|
|
340
167
|
this.messages.delete(args.jobId);
|
|
341
168
|
return Promise.resolve(true);
|
|
342
169
|
}
|
|
170
|
+
getContentSnapshot(jobId) {
|
|
171
|
+
const job = this.data.get(jobId);
|
|
172
|
+
return Promise.resolve(job ? structuredClone({ job, history: this.contentHolds.get(jobId) ?? { holds: [], events: [] } }) : void 0);
|
|
173
|
+
}
|
|
174
|
+
mutateContentHold(jobId, mutation) {
|
|
175
|
+
const job = this.data.get(jobId);
|
|
176
|
+
if (!job) return Promise.reject(new ContentHoldError("job_not_found"));
|
|
177
|
+
const history = applyContentHoldMutation(job, this.contentHolds.get(jobId) ?? { holds: [], events: [] }, mutation);
|
|
178
|
+
this.contentHolds.set(jobId, history);
|
|
179
|
+
return Promise.resolve(structuredClone(history));
|
|
180
|
+
}
|
|
343
181
|
claimReceiptSeq(jobId) {
|
|
344
182
|
if (!this.data.has(jobId)) {
|
|
345
183
|
return Promise.reject(new Error(`claimReceiptSeq: job ${jobId} not found`));
|
|
@@ -394,7 +232,7 @@ var MemoryJobStore = class {
|
|
|
394
232
|
record.askedTopUpCurrency = asked.currency;
|
|
395
233
|
record.topUpCapUnenforcedReason = asked.reason;
|
|
396
234
|
}
|
|
397
|
-
record.lastActivityAt = Date.now();
|
|
235
|
+
if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
|
|
398
236
|
if (seq > record.seq) record.seq = seq;
|
|
399
237
|
this.notify(jobId, fullMessage);
|
|
400
238
|
return Promise.resolve(seq);
|
|
@@ -407,7 +245,7 @@ var MemoryJobStore = class {
|
|
|
407
245
|
const seq = this.allocateSeq(jobId);
|
|
408
246
|
const fullMessage = { ...message, seq };
|
|
409
247
|
this.messagesFor(jobId).push({ message: fullMessage, status: "pending-verification" });
|
|
410
|
-
record.lastActivityAt = Date.now();
|
|
248
|
+
if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
|
|
411
249
|
if (seq > record.seq) record.seq = seq;
|
|
412
250
|
this.fireNotifyOnly(jobId);
|
|
413
251
|
return Promise.resolve(seq);
|
|
@@ -456,7 +294,7 @@ var MemoryJobStore = class {
|
|
|
456
294
|
record.pendingX402AmountUsdcMicro = void 0;
|
|
457
295
|
}
|
|
458
296
|
}
|
|
459
|
-
record.lastActivityAt = Date.now();
|
|
297
|
+
if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
|
|
460
298
|
this.notify(jobId, stored.message);
|
|
461
299
|
return Promise.resolve({
|
|
462
300
|
counters: snapshotCounters(record),
|
|
@@ -521,7 +359,7 @@ var MemoryJobStore = class {
|
|
|
521
359
|
if (record?.status !== "awaiting-input") return Promise.resolve(false);
|
|
522
360
|
applyTiming(record, structuredClone(record), Date.now(), "processing");
|
|
523
361
|
record.status = "processing";
|
|
524
|
-
record.lastActivityAt = Date.now();
|
|
362
|
+
if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
|
|
525
363
|
return Promise.resolve(true);
|
|
526
364
|
}
|
|
527
365
|
async tryReactivationLock(jobId, fn) {
|
|
@@ -704,14 +542,6 @@ function mergeSignals(a, b) {
|
|
|
704
542
|
}
|
|
705
543
|
|
|
706
544
|
export {
|
|
707
|
-
toJobRecord,
|
|
708
|
-
fromJobRecord,
|
|
709
|
-
abortJob,
|
|
710
|
-
JobCancelledError,
|
|
711
|
-
providerMessage,
|
|
712
|
-
localMessage,
|
|
713
|
-
isTerminal,
|
|
714
|
-
isYieldMessage,
|
|
715
545
|
accumulateAskedTopUp,
|
|
716
546
|
UNCAPPABLE_ASK_TOTAL,
|
|
717
547
|
isJobRetentionStore,
|
|
@@ -953,6 +953,112 @@ function generateEphemeralReceiptKey() {
|
|
|
953
953
|
return { secret: bytesToHex(secretKey), pubkey: bytesToHex(publicKey) };
|
|
954
954
|
}
|
|
955
955
|
|
|
956
|
+
// src/lib/wallet-agent/keypair.ts
|
|
957
|
+
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
|
|
958
|
+
var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
959
|
+
function derivePubkeyFromPrivkey(privkey) {
|
|
960
|
+
if (!PRIVKEY_HEX_RE.test(privkey)) {
|
|
961
|
+
throw new DvmError(
|
|
962
|
+
"invalid_privkey",
|
|
963
|
+
"Privkey must be 64 hex characters (32 bytes).",
|
|
964
|
+
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
const bytes = hexToBytes2(privkey);
|
|
968
|
+
if (!secp256k12.utils.isValidSecretKey(bytes)) {
|
|
969
|
+
throw new DvmError(
|
|
970
|
+
"invalid_privkey",
|
|
971
|
+
"Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
|
|
972
|
+
"Generate a fresh key with: dvm wallet init"
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
const pub = secp256k12.getPublicKey(bytes);
|
|
976
|
+
return bytesToHex2(pub);
|
|
977
|
+
}
|
|
978
|
+
function bytesToHex2(bytes) {
|
|
979
|
+
return Buffer.from(bytes).toString("hex");
|
|
980
|
+
}
|
|
981
|
+
function hexToBytes2(hex) {
|
|
982
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/lib/wallet-agent/sign.ts
|
|
986
|
+
import { schnorr as schnorr2 } from "@noble/curves/secp256k1.js";
|
|
987
|
+
var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
|
|
988
|
+
var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
989
|
+
var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
|
|
990
|
+
function signChallenge(privkeyHex, challenge) {
|
|
991
|
+
if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
|
|
992
|
+
throw new DvmError(
|
|
993
|
+
"invalid_privkey",
|
|
994
|
+
"Privkey must be 64 hex characters (32 bytes).",
|
|
995
|
+
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
if (challenge.length !== 32) {
|
|
999
|
+
throw new DvmError(
|
|
1000
|
+
"invalid_challenge",
|
|
1001
|
+
`Challenge must be exactly 32 bytes; got ${challenge.length}.`,
|
|
1002
|
+
"Pass the SHA-256 digest output (32 bytes)."
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
const sig = schnorr2.sign(challenge, hexToBytes3(privkeyHex));
|
|
1006
|
+
return bytesToHex3(sig);
|
|
1007
|
+
}
|
|
1008
|
+
function verifyChallenge(pubkeyHex, challenge, sigHex) {
|
|
1009
|
+
if (!SIG_HEX_RE.test(sigHex)) return false;
|
|
1010
|
+
if (challenge.length !== 32) return false;
|
|
1011
|
+
let xonly;
|
|
1012
|
+
if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
|
|
1013
|
+
xonly = hexToBytes3(pubkeyHex.slice(2));
|
|
1014
|
+
} else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
|
|
1015
|
+
xonly = hexToBytes3(pubkeyHex);
|
|
1016
|
+
} else {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
try {
|
|
1020
|
+
return schnorr2.verify(hexToBytes3(sigHex), challenge, xonly);
|
|
1021
|
+
} catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function hexToBytes3(hex) {
|
|
1026
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1027
|
+
}
|
|
1028
|
+
function bytesToHex3(bytes) {
|
|
1029
|
+
return Buffer.from(bytes).toString("hex");
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// src/lib/cashu/admin-auth.ts
|
|
1033
|
+
import { randomBytes } from "crypto";
|
|
1034
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
1035
|
+
var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
|
|
1036
|
+
var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
|
|
1037
|
+
var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
|
|
1038
|
+
function buildAdminChallenge(args) {
|
|
1039
|
+
const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
|
|
1040
|
+
return sha2562(new TextEncoder().encode(msg));
|
|
1041
|
+
}
|
|
1042
|
+
function buildAdminAuthHeader(args) {
|
|
1043
|
+
const nowMs = args.now ? args.now() : Date.now();
|
|
1044
|
+
const unixSeconds = Math.floor(nowMs / 1e3);
|
|
1045
|
+
const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
|
|
1046
|
+
const nonceHex = Buffer.from(nonceBytes).toString("hex");
|
|
1047
|
+
const challenge = buildAdminChallenge({
|
|
1048
|
+
method: args.method,
|
|
1049
|
+
path: args.path,
|
|
1050
|
+
dvmId: args.dvmId,
|
|
1051
|
+
nonceHex,
|
|
1052
|
+
unixSeconds
|
|
1053
|
+
});
|
|
1054
|
+
const sigHex = signChallenge(args.privkeyHex, challenge);
|
|
1055
|
+
return {
|
|
1056
|
+
header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
|
|
1057
|
+
nonceHex,
|
|
1058
|
+
unixSeconds
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
956
1062
|
// src/lib/builder-identity.ts
|
|
957
1063
|
import {
|
|
958
1064
|
chmodSync as chmodSync3,
|
|
@@ -964,8 +1070,8 @@ import {
|
|
|
964
1070
|
unlinkSync as unlinkSync4,
|
|
965
1071
|
writeFileSync as writeFileSync5
|
|
966
1072
|
} from "fs";
|
|
967
|
-
import { schnorr as
|
|
968
|
-
import { sha256 as
|
|
1073
|
+
import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
|
|
1074
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
969
1075
|
|
|
970
1076
|
// src/lib/builder-store.ts
|
|
971
1077
|
import {
|
|
@@ -1070,8 +1176,8 @@ function migrateV1Store(parsed) {
|
|
|
1070
1176
|
|
|
1071
1177
|
// src/lib/builder-identity.ts
|
|
1072
1178
|
function generateIdentity() {
|
|
1073
|
-
const { secretKey, publicKey } =
|
|
1074
|
-
return { pubkey:
|
|
1179
|
+
const { secretKey, publicKey } = schnorr3.keygen();
|
|
1180
|
+
return { pubkey: bytesToHex4(publicKey), secret: bytesToHex4(secretKey) };
|
|
1075
1181
|
}
|
|
1076
1182
|
function loadIdentitySecret() {
|
|
1077
1183
|
if (!existsSync5(BUILDER_KEY_FILE)) return null;
|
|
@@ -1114,7 +1220,7 @@ function writeIdentity(keypair, opts = {}) {
|
|
|
1114
1220
|
setIdentityPubkey(keypair.pubkey);
|
|
1115
1221
|
}
|
|
1116
1222
|
function hashCapabilities(capabilities) {
|
|
1117
|
-
return
|
|
1223
|
+
return bytesToHex4(sha2563(canonicaliseForSigning(capabilities)));
|
|
1118
1224
|
}
|
|
1119
1225
|
function buildAttestation(args) {
|
|
1120
1226
|
return {
|
|
@@ -1127,14 +1233,14 @@ function buildAttestation(args) {
|
|
|
1127
1233
|
};
|
|
1128
1234
|
}
|
|
1129
1235
|
function signAttestation(payload, secretHex) {
|
|
1130
|
-
const secretBytes =
|
|
1236
|
+
const secretBytes = hexToBytes4(secretHex);
|
|
1131
1237
|
const canonicalBytes = canonicaliseForSigning(payload);
|
|
1132
|
-
return
|
|
1238
|
+
return bytesToHex4(schnorr3.sign(canonicalBytes, secretBytes));
|
|
1133
1239
|
}
|
|
1134
1240
|
function verifyAttestation(payload, signatureHex, pubkeyHex) {
|
|
1135
1241
|
try {
|
|
1136
1242
|
const canonicalBytes = canonicaliseForSigning(payload);
|
|
1137
|
-
return
|
|
1243
|
+
return schnorr3.verify(hexToBytes4(signatureHex), canonicalBytes, hexToBytes4(pubkeyHex));
|
|
1138
1244
|
} catch {
|
|
1139
1245
|
return false;
|
|
1140
1246
|
}
|
|
@@ -1157,12 +1263,12 @@ function warnAndTightenLooseKeyMode() {
|
|
|
1157
1263
|
} catch {
|
|
1158
1264
|
}
|
|
1159
1265
|
}
|
|
1160
|
-
function
|
|
1266
|
+
function bytesToHex4(bytes) {
|
|
1161
1267
|
let s = "";
|
|
1162
1268
|
for (const b of bytes) s += b.toString(16).padStart(2, "0");
|
|
1163
1269
|
return s;
|
|
1164
1270
|
}
|
|
1165
|
-
function
|
|
1271
|
+
function hexToBytes4(hex) {
|
|
1166
1272
|
if (hex.length % 2 !== 0) {
|
|
1167
1273
|
throw new Error(`hex string has odd length: ${String(hex.length)}`);
|
|
1168
1274
|
}
|
|
@@ -1462,112 +1568,6 @@ function parseTraceparent(header) {
|
|
|
1462
1568
|
return { traceId, parentSpanId, flags: parseInt(flagsHex, 16) };
|
|
1463
1569
|
}
|
|
1464
1570
|
|
|
1465
|
-
// src/lib/wallet-agent/keypair.ts
|
|
1466
|
-
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
|
|
1467
|
-
var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
1468
|
-
function derivePubkeyFromPrivkey(privkey) {
|
|
1469
|
-
if (!PRIVKEY_HEX_RE.test(privkey)) {
|
|
1470
|
-
throw new DvmError(
|
|
1471
|
-
"invalid_privkey",
|
|
1472
|
-
"Privkey must be 64 hex characters (32 bytes).",
|
|
1473
|
-
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
1474
|
-
);
|
|
1475
|
-
}
|
|
1476
|
-
const bytes = hexToBytes3(privkey);
|
|
1477
|
-
if (!secp256k12.utils.isValidSecretKey(bytes)) {
|
|
1478
|
-
throw new DvmError(
|
|
1479
|
-
"invalid_privkey",
|
|
1480
|
-
"Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
|
|
1481
|
-
"Generate a fresh key with: dvm wallet init"
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
const pub = secp256k12.getPublicKey(bytes);
|
|
1485
|
-
return bytesToHex3(pub);
|
|
1486
|
-
}
|
|
1487
|
-
function bytesToHex3(bytes) {
|
|
1488
|
-
return Buffer.from(bytes).toString("hex");
|
|
1489
|
-
}
|
|
1490
|
-
function hexToBytes3(hex) {
|
|
1491
|
-
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
// src/lib/wallet-agent/sign.ts
|
|
1495
|
-
import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
|
|
1496
|
-
var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
|
|
1497
|
-
var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
1498
|
-
var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
|
|
1499
|
-
function signChallenge(privkeyHex, challenge) {
|
|
1500
|
-
if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
|
|
1501
|
-
throw new DvmError(
|
|
1502
|
-
"invalid_privkey",
|
|
1503
|
-
"Privkey must be 64 hex characters (32 bytes).",
|
|
1504
|
-
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
if (challenge.length !== 32) {
|
|
1508
|
-
throw new DvmError(
|
|
1509
|
-
"invalid_challenge",
|
|
1510
|
-
`Challenge must be exactly 32 bytes; got ${challenge.length}.`,
|
|
1511
|
-
"Pass the SHA-256 digest output (32 bytes)."
|
|
1512
|
-
);
|
|
1513
|
-
}
|
|
1514
|
-
const sig = schnorr3.sign(challenge, hexToBytes4(privkeyHex));
|
|
1515
|
-
return bytesToHex4(sig);
|
|
1516
|
-
}
|
|
1517
|
-
function verifyChallenge(pubkeyHex, challenge, sigHex) {
|
|
1518
|
-
if (!SIG_HEX_RE.test(sigHex)) return false;
|
|
1519
|
-
if (challenge.length !== 32) return false;
|
|
1520
|
-
let xonly;
|
|
1521
|
-
if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
|
|
1522
|
-
xonly = hexToBytes4(pubkeyHex.slice(2));
|
|
1523
|
-
} else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
|
|
1524
|
-
xonly = hexToBytes4(pubkeyHex);
|
|
1525
|
-
} else {
|
|
1526
|
-
return false;
|
|
1527
|
-
}
|
|
1528
|
-
try {
|
|
1529
|
-
return schnorr3.verify(hexToBytes4(sigHex), challenge, xonly);
|
|
1530
|
-
} catch {
|
|
1531
|
-
return false;
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
function hexToBytes4(hex) {
|
|
1535
|
-
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1536
|
-
}
|
|
1537
|
-
function bytesToHex4(bytes) {
|
|
1538
|
-
return Buffer.from(bytes).toString("hex");
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
// src/lib/cashu/admin-auth.ts
|
|
1542
|
-
import { randomBytes } from "crypto";
|
|
1543
|
-
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
1544
|
-
var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
|
|
1545
|
-
var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
|
|
1546
|
-
var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
|
|
1547
|
-
function buildAdminChallenge(args) {
|
|
1548
|
-
const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
|
|
1549
|
-
return sha2563(new TextEncoder().encode(msg));
|
|
1550
|
-
}
|
|
1551
|
-
function buildAdminAuthHeader(args) {
|
|
1552
|
-
const nowMs = args.now ? args.now() : Date.now();
|
|
1553
|
-
const unixSeconds = Math.floor(nowMs / 1e3);
|
|
1554
|
-
const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
|
|
1555
|
-
const nonceHex = Buffer.from(nonceBytes).toString("hex");
|
|
1556
|
-
const challenge = buildAdminChallenge({
|
|
1557
|
-
method: args.method,
|
|
1558
|
-
path: args.path,
|
|
1559
|
-
dvmId: args.dvmId,
|
|
1560
|
-
nonceHex,
|
|
1561
|
-
unixSeconds
|
|
1562
|
-
});
|
|
1563
|
-
const sigHex = signChallenge(args.privkeyHex, challenge);
|
|
1564
|
-
return {
|
|
1565
|
-
header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
|
|
1566
|
-
nonceHex,
|
|
1567
|
-
unixSeconds
|
|
1568
|
-
};
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
1571
|
// src/lib/monotonic-clock.ts
|
|
1572
1572
|
function createMonotonicClock() {
|
|
1573
1573
|
let anchorWallMs = Date.now();
|
|
@@ -1662,6 +1662,14 @@ export {
|
|
|
1662
1662
|
receiptPubkeyFromSecret,
|
|
1663
1663
|
deriveReceiptKeypair,
|
|
1664
1664
|
generateEphemeralReceiptKey,
|
|
1665
|
+
PRIVKEY_HEX_RE,
|
|
1666
|
+
derivePubkeyFromPrivkey,
|
|
1667
|
+
verifyChallenge,
|
|
1668
|
+
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
1669
|
+
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
1670
|
+
ADMIN_STATE_UNAVAILABLE,
|
|
1671
|
+
buildAdminChallenge,
|
|
1672
|
+
buildAdminAuthHeader,
|
|
1665
1673
|
generateIdentity,
|
|
1666
1674
|
loadIdentitySecret,
|
|
1667
1675
|
writeIdentity,
|
|
@@ -1685,13 +1693,5 @@ export {
|
|
|
1685
1693
|
receiptFailureDetail,
|
|
1686
1694
|
isPublicRequestId,
|
|
1687
1695
|
parseTraceparent,
|
|
1688
|
-
PRIVKEY_HEX_RE,
|
|
1689
|
-
derivePubkeyFromPrivkey,
|
|
1690
|
-
verifyChallenge,
|
|
1691
|
-
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
1692
|
-
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
1693
|
-
ADMIN_STATE_UNAVAILABLE,
|
|
1694
|
-
buildAdminChallenge,
|
|
1695
|
-
buildAdminAuthHeader,
|
|
1696
1696
|
createMonotonicClock
|
|
1697
1697
|
};
|
|
@@ -10,39 +10,6 @@ import {
|
|
|
10
10
|
projectSpanRow
|
|
11
11
|
} from "./chunk-ETXDTUJO.js";
|
|
12
12
|
|
|
13
|
-
// src/sdk/step-cache.ts
|
|
14
|
-
var StepCache = class _StepCache {
|
|
15
|
-
cache = /* @__PURE__ */ new Map();
|
|
16
|
-
has(id) {
|
|
17
|
-
return this.cache.has(id);
|
|
18
|
-
}
|
|
19
|
-
get(id) {
|
|
20
|
-
return this.getRecord(id).value;
|
|
21
|
-
}
|
|
22
|
-
getRecord(id) {
|
|
23
|
-
const record = this.cache.get(id);
|
|
24
|
-
if (record === void 0) throw new Error(`StepCache: no cached result for step "${id}"`);
|
|
25
|
-
return record;
|
|
26
|
-
}
|
|
27
|
-
set(id, value, costs) {
|
|
28
|
-
this.cache.set(id, {
|
|
29
|
-
id,
|
|
30
|
-
value,
|
|
31
|
-
...costs !== void 0 && costs.length > 0 ? { costs: costs.map(({ amount, currency }) => ({ amount, currency })) } : {}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
serialize() {
|
|
35
|
-
return [...this.cache.values()];
|
|
36
|
-
}
|
|
37
|
-
static deserialize(records) {
|
|
38
|
-
const cache = new _StepCache();
|
|
39
|
-
for (const { id, value, costs } of records) {
|
|
40
|
-
cache.set(id, value, costs);
|
|
41
|
-
}
|
|
42
|
-
return cache;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
13
|
// src/sdk/logger.ts
|
|
47
14
|
function createConsoleLogger(_jobId) {
|
|
48
15
|
return {
|
|
@@ -373,15 +340,15 @@ async function withSpan(opts, fn) {
|
|
|
373
340
|
|
|
374
341
|
// src/observability/datasets.ts
|
|
375
342
|
var SPANS_DATASETS = [
|
|
376
|
-
{ name: "platform", retentionDays:
|
|
377
|
-
{ name: "cashu", retentionDays:
|
|
378
|
-
{ name: "lightning", retentionDays:
|
|
379
|
-
{ name: "stripe", retentionDays:
|
|
380
|
-
{ name: "x402", retentionDays:
|
|
381
|
-
{ name: "tempo", retentionDays:
|
|
382
|
-
{ name: "isolate", retentionDays:
|
|
383
|
-
{ name: "audit", retentionDays:
|
|
384
|
-
{ name: "cron", retentionDays:
|
|
343
|
+
{ name: "platform", retentionDays: 730 },
|
|
344
|
+
{ name: "cashu", retentionDays: 730 },
|
|
345
|
+
{ name: "lightning", retentionDays: 730 },
|
|
346
|
+
{ name: "stripe", retentionDays: 730 },
|
|
347
|
+
{ name: "x402", retentionDays: 730 },
|
|
348
|
+
{ name: "tempo", retentionDays: 730 },
|
|
349
|
+
{ name: "isolate", retentionDays: 730 },
|
|
350
|
+
{ name: "audit", retentionDays: 730 },
|
|
351
|
+
{ name: "cron", retentionDays: 730 }
|
|
385
352
|
];
|
|
386
353
|
var DATASET_NAMES = new Set(SPANS_DATASETS.map((d) => d.name));
|
|
387
354
|
|
|
@@ -485,7 +452,6 @@ function buildNoopLoggers() {
|
|
|
485
452
|
}
|
|
486
453
|
|
|
487
454
|
export {
|
|
488
|
-
StepCache,
|
|
489
455
|
createConsoleLogger,
|
|
490
456
|
createStdoutLogger,
|
|
491
457
|
createNoopLogger,
|