@oneshot-agent/sdk 0.33.0 → 0.35.0
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 +56 -7
- package/dist/bin/verify-receipt.d.ts +3 -0
- package/dist/bin/verify-receipt.d.ts.map +1 -0
- package/dist/bin/verify-receipt.js +20 -0
- package/dist/bin/verify-receipt.js.map +1 -0
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +60 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +217 -14
- package/dist/index.js.map +1 -1
- package/dist/receipt-verify-cli.d.ts +15 -0
- package/dist/receipt-verify-cli.d.ts.map +1 -0
- package/dist/receipt-verify-cli.js +85 -0
- package/dist/receipt-verify-cli.js.map +1 -0
- package/dist/receipt.d.ts +104 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +243 -0
- package/dist/receipt.js.map +1 -0
- package/dist/types.d.ts +356 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -1
package/dist/index.js
CHANGED
|
@@ -55,8 +55,32 @@ var swap_1 = require("./swap");
|
|
|
55
55
|
Object.defineProperty(exports, "getSwapQuote", { enumerable: true, get: function () { return swap_1.getSwapQuote; } });
|
|
56
56
|
Object.defineProperty(exports, "executeSwap", { enumerable: true, get: function () { return swap_1.executeSwap; } });
|
|
57
57
|
__exportStar(require("./errors"), exports);
|
|
58
|
+
// Receipt verification (`canonicalizeReceipt`, `verifyReceipt`,
|
|
59
|
+
// `receiptFromWire`, and their supporting types `SignableReceipt`,
|
|
60
|
+
// `ReceiptSignatureFields`, `ReceiptJwk`, `ReceiptJwks`,
|
|
61
|
+
// `ReceiptVerificationResult`, `ReceiptVerificationFailureReason`) is
|
|
62
|
+
// intentionally NOT re-exported here, values OR types. `./receipt`
|
|
63
|
+
// statically imports `node:crypto`, and this file is the SDK's main entry —
|
|
64
|
+
// re-exporting its values would give every consumer (including browser/edge
|
|
65
|
+
// bundles, which this file otherwise avoids requiring Node builtins for;
|
|
66
|
+
// see the deliberate `btoa` fallback below) a hard Node-only dependency
|
|
67
|
+
// with no way to opt out. Import from the `@oneshot-agent/sdk/receipt`
|
|
68
|
+
// subpath instead (see package.json `exports` and the README's
|
|
69
|
+
// "Verifying Receipts" section).
|
|
70
|
+
//
|
|
71
|
+
// Types alone erase at compile time and carry no runtime import, so a
|
|
72
|
+
// type-only re-export here would be runtime-safe — but `SignableReceipt`
|
|
73
|
+
// declares `providerCost` (libs/agent-sdk/src/receipt.ts), the field whose
|
|
74
|
+
// removal from the public SDK surface `tests/unit/sdk-provider-cost-removed
|
|
75
|
+
// .test.ts` guards. Re-exporting it from the main entry would put it back
|
|
76
|
+
// on the surface that guard exists to keep clean, just via the type system
|
|
77
|
+
// instead of a runtime value. `providerCost` must stay inside
|
|
78
|
+
// `SignableReceipt` itself (it's part of what the signer actually signs —
|
|
79
|
+
// see `canonicalizeReceipt`), so the fix is not exposing that type here,
|
|
80
|
+
// not removing the field. Import `SignableReceipt` from the `./receipt`
|
|
81
|
+
// subpath directly if you need it.
|
|
58
82
|
// Keep in sync with package.json `version`. Guarded by version.test.ts.
|
|
59
|
-
const SDK_VERSION = '0.
|
|
83
|
+
const SDK_VERSION = '0.35.0';
|
|
60
84
|
/** HTTP poll cadence while push is unconfirmed: fast first checks, settling at 2s. */
|
|
61
85
|
const HTTP_POLL_BACKOFF_MS = [300, 600, 1000, 2000];
|
|
62
86
|
/** HTTP poll cadence once the WebSocket has delivered for this request. */
|
|
@@ -216,6 +240,7 @@ class OneShot {
|
|
|
216
240
|
this._budgets = validateBudgetConfig(config.budgets);
|
|
217
241
|
this._alertEmail = config.alerts?.email;
|
|
218
242
|
this._defaultHeaders = { ...(config.defaultHeaders ?? {}) };
|
|
243
|
+
this._stagingKey = config.stagingKey;
|
|
219
244
|
this.rpcProvider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl ?? RPC_URL);
|
|
220
245
|
if (config.accessToken) {
|
|
221
246
|
// Option D: access-token session. Exclusive with every wallet option,
|
|
@@ -305,12 +330,19 @@ class OneShot {
|
|
|
305
330
|
// from_mailbox, omit from_address entirely so the server picks from
|
|
306
331
|
// the agent's domain pool. The chosen address comes back on the
|
|
307
332
|
// quote response (`quote.from_address`) and we replay it on /send.
|
|
308
|
-
// When the caller pins
|
|
309
|
-
// `${mailbox ?? 'agent'}@${
|
|
333
|
+
// When the caller pins from_domain (with or without from_mailbox), we
|
|
334
|
+
// build `${mailbox ?? 'agent'}@${from_domain}`. Pinning from_mailbox
|
|
335
|
+
// alone has no domain to attach it to — `oneshotagent.com` is OneShot's
|
|
336
|
+
// own domain and no agent can send from it — so that combination is a
|
|
337
|
+
// client-side validation error rather than a guaranteed domain_not_owned
|
|
338
|
+
// rejection from the server.
|
|
339
|
+
if (options.from_mailbox && !options.from_domain) {
|
|
340
|
+
throw new errors_2.ValidationError('from_mailbox was pinned without from_domain. Pass from_domain (a domain you own) as well, or omit both to rotate across your warmed domains.', 'from_domain');
|
|
341
|
+
}
|
|
310
342
|
const useRotation = !options.from_domain && !options.from_mailbox;
|
|
311
343
|
const fromAddress = useRotation
|
|
312
344
|
? undefined
|
|
313
|
-
: `${options.from_mailbox ?? 'agent'}@${options.from_domain
|
|
345
|
+
: `${options.from_mailbox ?? 'agent'}@${options.from_domain}`;
|
|
314
346
|
// `mailbox_provisioning_fee` (>0) means `from_address` is a new address that
|
|
315
347
|
// provisions a mailbox on first send — a one-time fee folded into `total_cost`.
|
|
316
348
|
const quote = await this.tool('email/quote', {
|
|
@@ -789,8 +821,8 @@ class OneShot {
|
|
|
789
821
|
if (options.task.length < 10) {
|
|
790
822
|
throw new errors_2.ValidationError('Task must be at least 10 characters', 'task');
|
|
791
823
|
}
|
|
792
|
-
if (options.max_steps !== undefined && (options.max_steps <
|
|
793
|
-
throw new errors_2.ValidationError('max_steps must be between
|
|
824
|
+
if (options.max_steps !== undefined && (!Number.isInteger(options.max_steps) || options.max_steps < 25 || options.max_steps > 100)) {
|
|
825
|
+
throw new errors_2.ValidationError('max_steps must be between 25 and 100', 'max_steps');
|
|
794
826
|
}
|
|
795
827
|
const payload = {
|
|
796
828
|
task: options.task,
|
|
@@ -805,8 +837,8 @@ class OneShot {
|
|
|
805
837
|
payload.session_id = options.session_id;
|
|
806
838
|
if (options.profile_id)
|
|
807
839
|
payload.profile_id = options.profile_id;
|
|
808
|
-
if (options.secrets)
|
|
809
|
-
|
|
840
|
+
if (options.secrets !== undefined)
|
|
841
|
+
throw new errors_2.ValidationError('secrets is unsupported; import cookies or storage_state with createBrowserProfile', 'secrets');
|
|
810
842
|
if (options.max_steps)
|
|
811
843
|
payload.max_steps = options.max_steps;
|
|
812
844
|
const { execResp } = await this.runQuoteToPay({
|
|
@@ -843,18 +875,42 @@ class OneShot {
|
|
|
843
875
|
* console.log(profile.id); // Use this in browser({ profile_id: ... })
|
|
844
876
|
* ```
|
|
845
877
|
*/
|
|
846
|
-
async createBrowserProfile(name) {
|
|
878
|
+
async createBrowserProfile(name, options = {}) {
|
|
847
879
|
this.validate(name, 'name');
|
|
880
|
+
if (options.cookies !== undefined && options.storage_state !== undefined)
|
|
881
|
+
throw new errors_2.ValidationError('Provide cookies or storage_state, not both', 'options');
|
|
848
882
|
const response = await fetch(`${this.baseUrl}/v1/tools/browser/profiles`, {
|
|
849
883
|
method: 'POST',
|
|
850
884
|
headers: { 'Content-Type': 'application/json', ...(await this.signedReadHeaders()) },
|
|
851
|
-
body: JSON.stringify({ name }),
|
|
885
|
+
body: JSON.stringify({ name, ...options }),
|
|
886
|
+
signal: AbortSignal.timeout(150000),
|
|
852
887
|
});
|
|
853
888
|
if (!response.ok) {
|
|
854
889
|
throw new errors_2.ToolError('Failed to create browser profile', response.status, await response.text());
|
|
855
890
|
}
|
|
856
891
|
return response.json();
|
|
857
892
|
}
|
|
893
|
+
/** Open a profile login session; wait for idle before entering credentials in live_url. */
|
|
894
|
+
async startBrowserProfileSetup(profileId, startUrl) {
|
|
895
|
+
return this.browserProfileSetup(profileId, 'start', { start_url: startUrl });
|
|
896
|
+
}
|
|
897
|
+
async getBrowserProfileSetup(profileId) {
|
|
898
|
+
return this.browserProfileSetup(profileId, 'status');
|
|
899
|
+
}
|
|
900
|
+
/** Call after login and 2FA. Saves state and inspects cookies before site navigation. */
|
|
901
|
+
async finishBrowserProfileSetup(profileId) {
|
|
902
|
+
return this.browserProfileSetup(profileId, 'finish');
|
|
903
|
+
}
|
|
904
|
+
async browserProfileSetup(profileId, action, body = {}) {
|
|
905
|
+
this.validate(profileId, 'profileId');
|
|
906
|
+
const response = await fetch(`${this.baseUrl}/v1/tools/browser/profiles/${encodeURIComponent(profileId)}/setup/${action}`, {
|
|
907
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', ...(await this.signedReadHeaders()) },
|
|
908
|
+
body: JSON.stringify(body), signal: AbortSignal.timeout(150000),
|
|
909
|
+
});
|
|
910
|
+
if (!response.ok)
|
|
911
|
+
throw new errors_2.ToolError('Browser profile setup failed', response.status, '');
|
|
912
|
+
return response.json();
|
|
913
|
+
}
|
|
858
914
|
/**
|
|
859
915
|
* List all browser profiles
|
|
860
916
|
*
|
|
@@ -894,6 +950,119 @@ class OneShot {
|
|
|
894
950
|
throw new errors_2.ToolError('Failed to delete browser profile', response.status, await response.text());
|
|
895
951
|
}
|
|
896
952
|
}
|
|
953
|
+
// ── LinkedIn — messaging through a human-connected account (issue #756) ──
|
|
954
|
+
//
|
|
955
|
+
// The human connects THEIR OWN LinkedIn account through a hosted login link
|
|
956
|
+
// and grants specific actions. Connection/reads are free (signed proof);
|
|
957
|
+
// sync, reply, profile view and react are paid fixed-price tools. Every
|
|
958
|
+
// write is idempotency-keyed automatically — a message LinkedIn accepted
|
|
959
|
+
// cannot be recalled.
|
|
960
|
+
async linkedinFree(method, path, opts) {
|
|
961
|
+
const url = new URL(`${this.baseUrl}/v1/tools/linkedin${path}`);
|
|
962
|
+
for (const [k, v] of Object.entries(opts.query ?? {}))
|
|
963
|
+
if (v !== undefined)
|
|
964
|
+
url.searchParams.set(k, v);
|
|
965
|
+
const response = await fetch(url.toString(), {
|
|
966
|
+
method,
|
|
967
|
+
headers: { 'Content-Type': 'application/json', ...(await this.signedReadHeaders(opts.scope ?? 'read')) },
|
|
968
|
+
...(opts.body !== undefined ? { body: JSON.stringify(opts.body) } : {}),
|
|
969
|
+
signal: AbortSignal.timeout(60000),
|
|
970
|
+
});
|
|
971
|
+
if (!response.ok)
|
|
972
|
+
throw new errors_2.ToolError(`LinkedIn ${opts.what} failed`, response.status, await response.text());
|
|
973
|
+
return response.json();
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Create a hosted LinkedIn login link. Show `url` to the human once; it
|
|
977
|
+
* expires in 30 minutes and must be opened top-level (never in an iframe).
|
|
978
|
+
* Poll `getLinkedInConnection(intent_id)` until `completed`.
|
|
979
|
+
*/
|
|
980
|
+
async linkedinConnect(options) {
|
|
981
|
+
if (!Array.isArray(options.requestedActions) || options.requestedActions.length === 0) {
|
|
982
|
+
throw new errors_2.ValidationError('requestedActions must list at least one action (read, reply, view_profile, react)', 'requestedActions');
|
|
983
|
+
}
|
|
984
|
+
return this.linkedinFree('POST', '/connect', {
|
|
985
|
+
scope: 'write', what: 'connect',
|
|
986
|
+
body: { requested_actions: options.requestedActions, success_redirect_url: options.successRedirectUrl, failure_redirect_url: options.failureRedirectUrl },
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
async getLinkedInConnection(intentId) {
|
|
990
|
+
this.validate(intentId, 'intentId');
|
|
991
|
+
return this.linkedinFree('GET', `/connect/${encodeURIComponent(intentId)}`, { what: 'connection status' });
|
|
992
|
+
}
|
|
993
|
+
async listLinkedInAccounts(options = {}) {
|
|
994
|
+
return this.linkedinFree('GET', '/accounts', { what: 'accounts', query: { include_revoked: options.includeRevoked ? 'true' : undefined } });
|
|
995
|
+
}
|
|
996
|
+
async getLinkedInAccount(accountId) {
|
|
997
|
+
this.validate(accountId, 'accountId');
|
|
998
|
+
return this.linkedinFree('GET', `/accounts/${encodeURIComponent(accountId)}`, { what: 'account' });
|
|
999
|
+
}
|
|
1000
|
+
/** Reconnect an existing account (keeps ownership; may narrow, never widen, the grant). */
|
|
1001
|
+
async reconnectLinkedInAccount(accountId, options = {}) {
|
|
1002
|
+
this.validate(accountId, 'accountId');
|
|
1003
|
+
return this.linkedinFree('POST', `/accounts/${encodeURIComponent(accountId)}/reconnect`, {
|
|
1004
|
+
scope: 'write', what: 'reconnect',
|
|
1005
|
+
body: { requested_actions: options.requestedActions, success_redirect_url: options.successRedirectUrl, failure_redirect_url: options.failureRedirectUrl },
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
/** End the grant, cancel queued writes, delete the upstream connection. */
|
|
1009
|
+
async revokeLinkedInAccount(accountId) {
|
|
1010
|
+
this.validate(accountId, 'accountId');
|
|
1011
|
+
return this.linkedinFree('DELETE', `/accounts/${encodeURIComponent(accountId)}`, { scope: 'write', what: 'revoke' });
|
|
1012
|
+
}
|
|
1013
|
+
async getLinkedInSync(accountId) {
|
|
1014
|
+
this.validate(accountId, 'accountId');
|
|
1015
|
+
return this.linkedinFree('GET', `/accounts/${encodeURIComponent(accountId)}/sync`, { what: 'sync status' });
|
|
1016
|
+
}
|
|
1017
|
+
async linkedinConversations(options) {
|
|
1018
|
+
this.validate(options.accountId, 'accountId');
|
|
1019
|
+
return this.linkedinFree('GET', `/accounts/${encodeURIComponent(options.accountId)}/conversations`, {
|
|
1020
|
+
what: 'conversations',
|
|
1021
|
+
query: { cursor: options.cursor, limit: options.limit?.toString(), since: options.since, unread: options.unread ? 'true' : undefined, archived: options.archived ? 'true' : undefined },
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
async linkedinMessages(options) {
|
|
1025
|
+
this.validate(options.accountId, 'accountId');
|
|
1026
|
+
return this.linkedinFree('GET', `/accounts/${encodeURIComponent(options.accountId)}/messages`, {
|
|
1027
|
+
what: 'messages',
|
|
1028
|
+
query: {
|
|
1029
|
+
conversation_id: options.conversationId, direction: options.direction, since: options.since, changed_since: options.changedSince,
|
|
1030
|
+
cursor: options.cursor, limit: options.limit?.toString(), include_deleted: options.includeDeleted ? 'true' : undefined,
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* One bounded, paid history-sync run (up to maxPages × 250 messages). A
|
|
1036
|
+
* run that hits the bound is paused with a cursor; the next `continue`
|
|
1037
|
+
* call resumes it. Check `coverage.complete` before buying another run.
|
|
1038
|
+
*/
|
|
1039
|
+
async linkedinSync(options) {
|
|
1040
|
+
this.validate(options.accountId, 'accountId');
|
|
1041
|
+
const { accountId, mode, maxPages, ...rest } = options;
|
|
1042
|
+
return this.tool('linkedin/sync', { ...rest, account_id: accountId, ...(mode ? { mode } : {}), ...(maxPages ? { max_pages: maxPages } : {}), timeout: options.timeout ?? 600 });
|
|
1043
|
+
}
|
|
1044
|
+
/** Send a reply in an existing conversation through the connected account. Paced; per-account daily cap. */
|
|
1045
|
+
async linkedinReply(options) {
|
|
1046
|
+
this.validate(options.accountId, 'accountId');
|
|
1047
|
+
this.validate(options.conversationId, 'conversationId');
|
|
1048
|
+
this.validate(options.text, 'text');
|
|
1049
|
+
const { accountId, conversationId, text, ...rest } = options;
|
|
1050
|
+
return this.tool('linkedin/reply', { ...rest, account_id: accountId, conversation_id: conversationId, text, timeout: options.timeout ?? 180 });
|
|
1051
|
+
}
|
|
1052
|
+
/** View a profile through the connected account. `notify: true` is visible to the target and cannot be undone. */
|
|
1053
|
+
async linkedinViewProfile(options) {
|
|
1054
|
+
this.validate(options.accountId, 'accountId');
|
|
1055
|
+
this.validate(options.identifier, 'identifier');
|
|
1056
|
+
const { accountId, identifier, notify, ...rest } = options;
|
|
1057
|
+
return this.tool('linkedin/profile-view', { ...rest, account_id: accountId, identifier, notify: !!notify, timeout: options.timeout ?? 180 });
|
|
1058
|
+
}
|
|
1059
|
+
/** React to a post through the connected account. */
|
|
1060
|
+
async linkedinReact(options) {
|
|
1061
|
+
this.validate(options.accountId, 'accountId');
|
|
1062
|
+
this.validate(options.postId, 'postId');
|
|
1063
|
+
const { accountId, postId, reactionType, ...rest } = options;
|
|
1064
|
+
return this.tool('linkedin/react', { ...rest, account_id: accountId, post_id: postId, reaction_type: reactionType ?? 'like', timeout: options.timeout ?? 180 });
|
|
1065
|
+
}
|
|
897
1066
|
/**
|
|
898
1067
|
* Update an existing website build
|
|
899
1068
|
*
|
|
@@ -1487,11 +1656,42 @@ class OneShot {
|
|
|
1487
1656
|
headers() {
|
|
1488
1657
|
return {
|
|
1489
1658
|
...this._defaultHeaders,
|
|
1659
|
+
...(this._stagingKey ? { 'X-Staging-Key': this._stagingKey } : {}),
|
|
1490
1660
|
'X-Agent-ID': this.provider.address,
|
|
1491
1661
|
'X-OneShot-SDK-Version': SDK_VERSION,
|
|
1492
1662
|
...(this._accessToken ? { Authorization: `Bearer ${this._accessToken}` } : {}),
|
|
1493
1663
|
};
|
|
1494
1664
|
}
|
|
1665
|
+
/** Submit a wallet-proven application. Approval is performed by the staging operator. */
|
|
1666
|
+
async applyForStaging(contact, purpose) {
|
|
1667
|
+
return this.stagingOnboarding('POST', '/applications', { contact, purpose });
|
|
1668
|
+
}
|
|
1669
|
+
async getStagingApplication() {
|
|
1670
|
+
return this.stagingOnboarding('GET', '/applications/me');
|
|
1671
|
+
}
|
|
1672
|
+
/** Rotates the wallet's staging key and installs it on this SDK instance. */
|
|
1673
|
+
async obtainStagingKey() {
|
|
1674
|
+
const result = await this.stagingOnboarding('POST', '/credential', {});
|
|
1675
|
+
this._stagingKey = result.staging_key;
|
|
1676
|
+
return result.staging_key;
|
|
1677
|
+
}
|
|
1678
|
+
async stagingOnboarding(method, path, body) {
|
|
1679
|
+
if (this._accessToken)
|
|
1680
|
+
throw new Error('Staging onboarding requires a wallet signer');
|
|
1681
|
+
const digest = ethers_1.ethers.sha256(ethers_1.ethers.toUtf8Bytes(JSON.stringify(body ?? {}))).slice(2);
|
|
1682
|
+
const headers = await this.signedReadHeaders(`staging:${method}:${path}:${digest}`);
|
|
1683
|
+
if (!headers['x-agent-proof'])
|
|
1684
|
+
throw new Error('Could not sign staging application');
|
|
1685
|
+
const response = await fetch(`${this.baseUrl}/v1/staging${path}`, {
|
|
1686
|
+
method, headers: { ...headers, 'Content-Type': 'application/json' },
|
|
1687
|
+
signal: AbortSignal.timeout(10000),
|
|
1688
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
1689
|
+
});
|
|
1690
|
+
const result = await response.json();
|
|
1691
|
+
if (!response.ok)
|
|
1692
|
+
throw new Error(result.error ?? `Staging request failed (${response.status})`);
|
|
1693
|
+
return result;
|
|
1694
|
+
}
|
|
1495
1695
|
/** True for access-token sessions: credits-only, never signs. */
|
|
1496
1696
|
get isAccessTokenSession() {
|
|
1497
1697
|
return this._accessToken !== undefined;
|
|
@@ -1798,6 +1998,7 @@ class OneShot {
|
|
|
1798
1998
|
response = await fetch(`${baseUrl}/v1/agents/me`, {
|
|
1799
1999
|
headers: {
|
|
1800
2000
|
...(config.defaultHeaders ?? {}),
|
|
2001
|
+
...(config.stagingKey ? { 'X-Staging-Key': config.stagingKey } : {}),
|
|
1801
2002
|
Authorization: `Bearer ${config.accessToken}`,
|
|
1802
2003
|
'X-OneShot-SDK-Version': SDK_VERSION,
|
|
1803
2004
|
},
|
|
@@ -1913,7 +2114,8 @@ class OneShot {
|
|
|
1913
2114
|
return this.readReliabilityJson('/v1/status', false, true);
|
|
1914
2115
|
}
|
|
1915
2116
|
async executeToolRequest(endpoint, options, quoteId) {
|
|
1916
|
-
|
|
2117
|
+
// LinkedIn writes act through a human's account and cannot be recalled: always keyed.
|
|
2118
|
+
const reliable = /(?:^|\/)(enrich\/(profile|email)|verify\/email|linkedin\/(reply|profile-view|react))$/.test(endpoint);
|
|
1917
2119
|
const key = options.idempotencyKey ?? (reliable ? ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(16)) : undefined);
|
|
1918
2120
|
if (options.totalTimeoutMs !== undefined && (!Number.isFinite(options.totalTimeoutMs) || options.totalTimeoutMs <= 0)) {
|
|
1919
2121
|
throw new errors_2.ValidationError('totalTimeoutMs must be positive', 'totalTimeoutMs');
|
|
@@ -2293,7 +2495,7 @@ class OneShot {
|
|
|
2293
2495
|
settle(() => {
|
|
2294
2496
|
cleanup();
|
|
2295
2497
|
wait.via = 'ws';
|
|
2296
|
-
reject(new errors_2.JobError(`Job failed: ${msg.error ?? 'Unknown'}`, requestId, String(msg.error ?? 'Unknown'), msg.error_code));
|
|
2498
|
+
reject(new errors_2.JobError(`Job failed: ${msg.error ?? 'Unknown'}`, requestId, String(msg.error ?? 'Unknown'), msg.error_code, msg.result));
|
|
2297
2499
|
});
|
|
2298
2500
|
}
|
|
2299
2501
|
else {
|
|
@@ -2384,7 +2586,7 @@ class OneShot {
|
|
|
2384
2586
|
if (job.status === 'failed') {
|
|
2385
2587
|
if (wait)
|
|
2386
2588
|
wait.via = 'http';
|
|
2387
|
-
throw new errors_2.JobError(`Job failed: ${job.error ?? 'Unknown'}`, requestId, String(job.error ?? 'Unknown'), job.error_code);
|
|
2589
|
+
throw new errors_2.JobError(`Job failed: ${job.error ?? 'Unknown'}`, requestId, String(job.error ?? 'Unknown'), job.error_code, job.result);
|
|
2388
2590
|
}
|
|
2389
2591
|
emit?.(String(job.status));
|
|
2390
2592
|
retries = 0;
|
|
@@ -2605,7 +2807,8 @@ class OneShot {
|
|
|
2605
2807
|
resp = await this.makeRequest(endpoint, data, signed.auth, quoteId, signal, timeoutMs, extraHeaders);
|
|
2606
2808
|
}
|
|
2607
2809
|
catch (err) {
|
|
2608
|
-
|
|
2810
|
+
// Keyed writes may already be queued server-side after a transport failure: keep the reservation.
|
|
2811
|
+
if (!/(enrich\/(profile|email)|verify\/email|physical-mail\/send|linkedin\/(reply|profile-view|react))$/.test(endpoint))
|
|
2609
2812
|
this.releaseUsdcReservation(signed.reservation);
|
|
2610
2813
|
throw err;
|
|
2611
2814
|
}
|