@conto_finance/sdk 0.7.1 → 0.8.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 +137 -12
- package/dist/index.d.mts +382 -18
- package/dist/index.d.ts +382 -18
- package/dist/index.js +779 -123
- package/dist/index.mjs +777 -123
- package/dist/link-wallet.d.mts +11 -2
- package/dist/link-wallet.d.ts +11 -2
- package/dist/link-wallet.js +41 -4
- package/dist/link-wallet.mjs +41 -4
- package/package.json +2 -2
package/dist/link-wallet.d.mts
CHANGED
|
@@ -49,6 +49,8 @@ interface LinkSpendRequest {
|
|
|
49
49
|
actionResolution?: string;
|
|
50
50
|
/** Set with requires_action when Link gives the person a page to finish the step. */
|
|
51
51
|
actionUrl?: string;
|
|
52
|
+
/** The metadata sent with the request, when Link returns it. */
|
|
53
|
+
metadata?: Record<string, string>;
|
|
52
54
|
}
|
|
53
55
|
/**
|
|
54
56
|
* The single-use virtual card Link issued for an approved request. Live card
|
|
@@ -85,7 +87,10 @@ interface LinkWallet {
|
|
|
85
87
|
waitForApproval(id: string, options?: LinkWaitOptions): Promise<LinkSpendRequest>;
|
|
86
88
|
/** Read the card of an approved request. */
|
|
87
89
|
retrieveCard(id: string, options?: LinkCallOptions): Promise<LinkCardCredential>;
|
|
88
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Cancel a request, or confirm Link already closed it. Throws when its card
|
|
92
|
+
* may still work or may have been used, so keep the hold.
|
|
93
|
+
*/
|
|
89
94
|
cancel(id: string, options?: LinkCallOptions): Promise<void>;
|
|
90
95
|
}
|
|
91
96
|
type LinkWalletErrorReason = 'not_installed' | 'not_logged_in' | 'not_found' | 'not_approved' | 'invalid_input' | 'invalid_response' | 'timed_out' | 'aborted' | 'cli_failed';
|
|
@@ -98,7 +103,11 @@ declare class LinkWalletError extends Error {
|
|
|
98
103
|
readonly reason: LinkWalletErrorReason;
|
|
99
104
|
/** The error code link-cli reported, when it reported a safe one. */
|
|
100
105
|
readonly linkCode?: string;
|
|
101
|
-
|
|
106
|
+
/** The open Link request with the same details that blocks this one. */
|
|
107
|
+
readonly duplicateOf?: string;
|
|
108
|
+
/** The checkpoint Link has on that request, as Link reported it. */
|
|
109
|
+
readonly duplicateCheckpoint?: string;
|
|
110
|
+
constructor(reason: LinkWalletErrorReason, message: string, linkCode?: string, duplicateOf?: string, duplicateCheckpoint?: string);
|
|
102
111
|
}
|
|
103
112
|
interface LinkCliWalletOptions {
|
|
104
113
|
/** The link-cli executable. Defaults to `link-cli` on PATH. */
|
package/dist/link-wallet.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ interface LinkSpendRequest {
|
|
|
49
49
|
actionResolution?: string;
|
|
50
50
|
/** Set with requires_action when Link gives the person a page to finish the step. */
|
|
51
51
|
actionUrl?: string;
|
|
52
|
+
/** The metadata sent with the request, when Link returns it. */
|
|
53
|
+
metadata?: Record<string, string>;
|
|
52
54
|
}
|
|
53
55
|
/**
|
|
54
56
|
* The single-use virtual card Link issued for an approved request. Live card
|
|
@@ -85,7 +87,10 @@ interface LinkWallet {
|
|
|
85
87
|
waitForApproval(id: string, options?: LinkWaitOptions): Promise<LinkSpendRequest>;
|
|
86
88
|
/** Read the card of an approved request. */
|
|
87
89
|
retrieveCard(id: string, options?: LinkCallOptions): Promise<LinkCardCredential>;
|
|
88
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Cancel a request, or confirm Link already closed it. Throws when its card
|
|
92
|
+
* may still work or may have been used, so keep the hold.
|
|
93
|
+
*/
|
|
89
94
|
cancel(id: string, options?: LinkCallOptions): Promise<void>;
|
|
90
95
|
}
|
|
91
96
|
type LinkWalletErrorReason = 'not_installed' | 'not_logged_in' | 'not_found' | 'not_approved' | 'invalid_input' | 'invalid_response' | 'timed_out' | 'aborted' | 'cli_failed';
|
|
@@ -98,7 +103,11 @@ declare class LinkWalletError extends Error {
|
|
|
98
103
|
readonly reason: LinkWalletErrorReason;
|
|
99
104
|
/** The error code link-cli reported, when it reported a safe one. */
|
|
100
105
|
readonly linkCode?: string;
|
|
101
|
-
|
|
106
|
+
/** The open Link request with the same details that blocks this one. */
|
|
107
|
+
readonly duplicateOf?: string;
|
|
108
|
+
/** The checkpoint Link has on that request, as Link reported it. */
|
|
109
|
+
readonly duplicateCheckpoint?: string;
|
|
110
|
+
constructor(reason: LinkWalletErrorReason, message: string, linkCode?: string, duplicateOf?: string, duplicateCheckpoint?: string);
|
|
102
111
|
}
|
|
103
112
|
interface LinkCliWalletOptions {
|
|
104
113
|
/** The link-cli executable. Defaults to `link-cli` on PATH. */
|
package/dist/link-wallet.js
CHANGED
|
@@ -69,12 +69,14 @@ function linkTime(value) {
|
|
|
69
69
|
|
|
70
70
|
// src/link-wallet.ts
|
|
71
71
|
var LinkWalletError = class extends Error {
|
|
72
|
-
constructor(reason, message, linkCode) {
|
|
72
|
+
constructor(reason, message, linkCode, duplicateOf, duplicateCheckpoint) {
|
|
73
73
|
super(message);
|
|
74
74
|
this.name = "LinkWalletError";
|
|
75
75
|
this.reason = reason;
|
|
76
76
|
this.code = reason === "not_installed" || reason === "not_logged_in" ? "LINK_WALLET_UNAVAILABLE" : "LINK_WALLET_FAILED";
|
|
77
77
|
if (linkCode) this.linkCode = linkCode;
|
|
78
|
+
if (duplicateOf) this.duplicateOf = duplicateOf;
|
|
79
|
+
if (duplicateCheckpoint) this.duplicateCheckpoint = duplicateCheckpoint;
|
|
78
80
|
}
|
|
79
81
|
};
|
|
80
82
|
var LINK_REQUEST_ID = /^lsrq_[A-Za-z0-9]{1,64}$/;
|
|
@@ -91,6 +93,7 @@ var KNOWN_STATUSES = /* @__PURE__ */ new Set([
|
|
|
91
93
|
"canceled",
|
|
92
94
|
"failed"
|
|
93
95
|
]);
|
|
96
|
+
var CLOSED_STATUSES = /* @__PURE__ */ new Set(["denied", "expired", "canceled", "failed"]);
|
|
94
97
|
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
95
98
|
var COMMAND_TIMEOUT_MS = 6e4;
|
|
96
99
|
function record(value) {
|
|
@@ -132,6 +135,11 @@ function parseSpendRequest(value) {
|
|
|
132
135
|
const amount = data.amount;
|
|
133
136
|
const approvalUrl = safeLinkApprovalUrl(data.approval_url);
|
|
134
137
|
const approvalExpiresAt = linkTime(data.approval_window_expires_at);
|
|
138
|
+
const metadata = Object.fromEntries(
|
|
139
|
+
Object.entries(record(data.metadata)).filter(
|
|
140
|
+
(entry) => typeof entry[1] === "string"
|
|
141
|
+
)
|
|
142
|
+
);
|
|
135
143
|
return {
|
|
136
144
|
id,
|
|
137
145
|
status,
|
|
@@ -141,7 +149,8 @@ function parseSpendRequest(value) {
|
|
|
141
149
|
...approvalUrl ? { approvalUrl } : {},
|
|
142
150
|
...approvalExpiresAt ? { approvalExpiresAt } : {},
|
|
143
151
|
...text(nextAction.resolution) ? { actionResolution: text(nextAction.resolution) } : {},
|
|
144
|
-
...httpsUrl(nextAction.action_url) ? { actionUrl: httpsUrl(nextAction.action_url) } : {}
|
|
152
|
+
...httpsUrl(nextAction.action_url) ? { actionUrl: httpsUrl(nextAction.action_url) } : {},
|
|
153
|
+
...Object.keys(metadata).length ? { metadata } : {}
|
|
145
154
|
};
|
|
146
155
|
}
|
|
147
156
|
function invalidCard() {
|
|
@@ -342,9 +351,28 @@ function linkCliWallet(options = {}) {
|
|
|
342
351
|
const existing = typeof message === "string" ? /matching spend request already exists: (lsrq_[A-Za-z0-9]{1,64})/.exec(message)?.[1] : void 0;
|
|
343
352
|
if (existing) {
|
|
344
353
|
const current = await readOnce(existing, call);
|
|
345
|
-
|
|
354
|
+
const owner = current.metadata?.conto_checkpoint;
|
|
355
|
+
const mine = input.metadata?.conto_checkpoint;
|
|
356
|
+
if (owner && owner !== mine) {
|
|
357
|
+
throw new LinkWalletError(
|
|
358
|
+
"invalid_input",
|
|
359
|
+
"Link has an open request for another purchase with the same details.",
|
|
360
|
+
"duplicate_for_other_purchase",
|
|
361
|
+
existing,
|
|
362
|
+
owner
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
if (owner === mine && current.amountMinor === input.amountMinor && (current.merchantUrl === void 0 || new URL(current.merchantUrl).origin === new URL(input.merchantUrl).origin)) {
|
|
346
366
|
return current;
|
|
347
367
|
}
|
|
368
|
+
if (!owner && mine) {
|
|
369
|
+
throw new LinkWalletError(
|
|
370
|
+
"cli_failed",
|
|
371
|
+
"Link has an open request with the same details and no checkpoint on it.",
|
|
372
|
+
"duplicate_without_checkpoint",
|
|
373
|
+
existing
|
|
374
|
+
);
|
|
375
|
+
}
|
|
348
376
|
}
|
|
349
377
|
throw failureFromOutput(result.value);
|
|
350
378
|
}
|
|
@@ -410,7 +438,16 @@ function linkCliWallet(options = {}) {
|
|
|
410
438
|
},
|
|
411
439
|
async cancel(id, call) {
|
|
412
440
|
requireRequestId(id);
|
|
413
|
-
|
|
441
|
+
try {
|
|
442
|
+
await runOrThrow(["spend-request", "cancel", id], call);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
const status = await readOnce(id, call).then(
|
|
445
|
+
(request) => request.status,
|
|
446
|
+
() => void 0
|
|
447
|
+
);
|
|
448
|
+
if (status !== void 0 && CLOSED_STATUSES.has(status)) return;
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
414
451
|
}
|
|
415
452
|
};
|
|
416
453
|
}
|
package/dist/link-wallet.mjs
CHANGED
|
@@ -11,12 +11,14 @@ import { chmod, mkdtemp, open, rm } from "fs/promises";
|
|
|
11
11
|
import { tmpdir } from "os";
|
|
12
12
|
import path from "path";
|
|
13
13
|
var LinkWalletError = class extends Error {
|
|
14
|
-
constructor(reason, message, linkCode) {
|
|
14
|
+
constructor(reason, message, linkCode, duplicateOf, duplicateCheckpoint) {
|
|
15
15
|
super(message);
|
|
16
16
|
this.name = "LinkWalletError";
|
|
17
17
|
this.reason = reason;
|
|
18
18
|
this.code = reason === "not_installed" || reason === "not_logged_in" ? "LINK_WALLET_UNAVAILABLE" : "LINK_WALLET_FAILED";
|
|
19
19
|
if (linkCode) this.linkCode = linkCode;
|
|
20
|
+
if (duplicateOf) this.duplicateOf = duplicateOf;
|
|
21
|
+
if (duplicateCheckpoint) this.duplicateCheckpoint = duplicateCheckpoint;
|
|
20
22
|
}
|
|
21
23
|
};
|
|
22
24
|
var LINK_REQUEST_ID = /^lsrq_[A-Za-z0-9]{1,64}$/;
|
|
@@ -33,6 +35,7 @@ var KNOWN_STATUSES = /* @__PURE__ */ new Set([
|
|
|
33
35
|
"canceled",
|
|
34
36
|
"failed"
|
|
35
37
|
]);
|
|
38
|
+
var CLOSED_STATUSES = /* @__PURE__ */ new Set(["denied", "expired", "canceled", "failed"]);
|
|
36
39
|
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
37
40
|
var COMMAND_TIMEOUT_MS = 6e4;
|
|
38
41
|
function record(value) {
|
|
@@ -74,6 +77,11 @@ function parseSpendRequest(value) {
|
|
|
74
77
|
const amount = data.amount;
|
|
75
78
|
const approvalUrl = safeLinkApprovalUrl(data.approval_url);
|
|
76
79
|
const approvalExpiresAt = linkTime(data.approval_window_expires_at);
|
|
80
|
+
const metadata = Object.fromEntries(
|
|
81
|
+
Object.entries(record(data.metadata)).filter(
|
|
82
|
+
(entry) => typeof entry[1] === "string"
|
|
83
|
+
)
|
|
84
|
+
);
|
|
77
85
|
return {
|
|
78
86
|
id,
|
|
79
87
|
status,
|
|
@@ -83,7 +91,8 @@ function parseSpendRequest(value) {
|
|
|
83
91
|
...approvalUrl ? { approvalUrl } : {},
|
|
84
92
|
...approvalExpiresAt ? { approvalExpiresAt } : {},
|
|
85
93
|
...text(nextAction.resolution) ? { actionResolution: text(nextAction.resolution) } : {},
|
|
86
|
-
...httpsUrl(nextAction.action_url) ? { actionUrl: httpsUrl(nextAction.action_url) } : {}
|
|
94
|
+
...httpsUrl(nextAction.action_url) ? { actionUrl: httpsUrl(nextAction.action_url) } : {},
|
|
95
|
+
...Object.keys(metadata).length ? { metadata } : {}
|
|
87
96
|
};
|
|
88
97
|
}
|
|
89
98
|
function invalidCard() {
|
|
@@ -284,9 +293,28 @@ function linkCliWallet(options = {}) {
|
|
|
284
293
|
const existing = typeof message === "string" ? /matching spend request already exists: (lsrq_[A-Za-z0-9]{1,64})/.exec(message)?.[1] : void 0;
|
|
285
294
|
if (existing) {
|
|
286
295
|
const current = await readOnce(existing, call);
|
|
287
|
-
|
|
296
|
+
const owner = current.metadata?.conto_checkpoint;
|
|
297
|
+
const mine = input.metadata?.conto_checkpoint;
|
|
298
|
+
if (owner && owner !== mine) {
|
|
299
|
+
throw new LinkWalletError(
|
|
300
|
+
"invalid_input",
|
|
301
|
+
"Link has an open request for another purchase with the same details.",
|
|
302
|
+
"duplicate_for_other_purchase",
|
|
303
|
+
existing,
|
|
304
|
+
owner
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (owner === mine && current.amountMinor === input.amountMinor && (current.merchantUrl === void 0 || new URL(current.merchantUrl).origin === new URL(input.merchantUrl).origin)) {
|
|
288
308
|
return current;
|
|
289
309
|
}
|
|
310
|
+
if (!owner && mine) {
|
|
311
|
+
throw new LinkWalletError(
|
|
312
|
+
"cli_failed",
|
|
313
|
+
"Link has an open request with the same details and no checkpoint on it.",
|
|
314
|
+
"duplicate_without_checkpoint",
|
|
315
|
+
existing
|
|
316
|
+
);
|
|
317
|
+
}
|
|
290
318
|
}
|
|
291
319
|
throw failureFromOutput(result.value);
|
|
292
320
|
}
|
|
@@ -352,7 +380,16 @@ function linkCliWallet(options = {}) {
|
|
|
352
380
|
},
|
|
353
381
|
async cancel(id, call) {
|
|
354
382
|
requireRequestId(id);
|
|
355
|
-
|
|
383
|
+
try {
|
|
384
|
+
await runOrThrow(["spend-request", "cancel", id], call);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const status = await readOnce(id, call).then(
|
|
387
|
+
(request) => request.status,
|
|
388
|
+
() => void 0
|
|
389
|
+
);
|
|
390
|
+
if (status !== void 0 && CLOSED_STATUSES.has(status)) return;
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
356
393
|
}
|
|
357
394
|
};
|
|
358
395
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conto_finance/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Conto SDK for AI agent payment authorization",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dev": "tsup --watch",
|
|
47
47
|
"lint": "tsc --noEmit",
|
|
48
48
|
"test": "npm run lint && npm run test:contract && npm run test:consumer",
|
|
49
|
-
"test:contract": "npm run build && node --test test/contract.test.mjs test/playwright.test.mjs test/browserbase.test.mjs test/checkout-verifier.test.mjs test/developer-experience.test.mjs test/checkout.test.mjs test/link-wallet.test.mjs test/purchases.test.mjs test/allowances.test.mjs test/purchase-watch-lifecycle.test.mjs ../../examples/link-wallet-agent/verify.test.mjs",
|
|
49
|
+
"test:contract": "npm run build && node --test test/contract.test.mjs test/playwright.test.mjs test/browserbase.test.mjs test/checkout-verifier.test.mjs test/developer-experience.test.mjs test/checkout.test.mjs test/link-wallet.test.mjs test/purchases.test.mjs test/allowances.test.mjs test/purchase-watch-lifecycle.test.mjs test/spending.test.mjs ../../examples/link-wallet-agent/verify.test.mjs",
|
|
50
50
|
"test:consumer": "npm run build && node --test test/consumer.test.mjs",
|
|
51
51
|
"verify:release": "node scripts/verify-release.mjs",
|
|
52
52
|
"prepublishOnly": "npm run verify:release && npm run lint && npm run test:contract && npm run test:consumer"
|