@oneshot-agent/sdk 0.19.0 → 0.19.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 +84 -0
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +193 -237
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,7 +48,8 @@ var swap_1 = require("./swap");
|
|
|
48
48
|
Object.defineProperty(exports, "getSwapQuote", { enumerable: true, get: function () { return swap_1.getSwapQuote; } });
|
|
49
49
|
Object.defineProperty(exports, "executeSwap", { enumerable: true, get: function () { return swap_1.executeSwap; } });
|
|
50
50
|
__exportStar(require("./errors"), exports);
|
|
51
|
-
|
|
51
|
+
// Keep in sync with package.json `version`. Guarded by version.test.ts.
|
|
52
|
+
const SDK_VERSION = '0.19.1';
|
|
52
53
|
// ============================================================================
|
|
53
54
|
// Environment Configuration
|
|
54
55
|
// ============================================================================
|
|
@@ -192,9 +193,7 @@ class OneShot {
|
|
|
192
193
|
// (e.g. a future API revision without the header check) or skipped (cap
|
|
193
194
|
// un-set so the server returned a 200 with the quote and the caller still
|
|
194
195
|
// wants the local guard to apply).
|
|
195
|
-
|
|
196
|
-
throw new errors_1.OneShotError(`Quote $${quote.total_cost} exceeds maxCost $${options.maxCost}`);
|
|
197
|
-
}
|
|
196
|
+
this.assertWithinMaxCost(quote.total_cost, options.maxCost);
|
|
198
197
|
const resolvedFromAddress = fromAddress ?? quote.from_address;
|
|
199
198
|
const payload = {
|
|
200
199
|
// Server replays the locked address from the quote when from_address
|
|
@@ -311,14 +310,11 @@ class OneShot {
|
|
|
311
310
|
return this.tool('research/interactions', { ...options });
|
|
312
311
|
}
|
|
313
312
|
async inboxList(options = {}) {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (options.include_body)
|
|
320
|
-
params.set('include_body', 'true');
|
|
321
|
-
const qs = params.toString();
|
|
313
|
+
const qs = this.buildQuery({
|
|
314
|
+
since: options.since || undefined,
|
|
315
|
+
limit: options.limit || undefined,
|
|
316
|
+
include_body: options.include_body ? 'true' : undefined,
|
|
317
|
+
});
|
|
322
318
|
const response = await fetch(`${this.baseUrl}/v1/tools/inbox${qs ? `?${qs}` : ''}`, {
|
|
323
319
|
headers: this.headers()
|
|
324
320
|
});
|
|
@@ -351,28 +347,17 @@ class OneShot {
|
|
|
351
347
|
variant_id: options.variant_id
|
|
352
348
|
};
|
|
353
349
|
// Commerce quotes can take up to 90s due to Rye API polling
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
366
|
-
payTo: quoteData.payment_request.recipient,
|
|
367
|
-
amount: quoteData.payment_request.amount,
|
|
368
|
-
currency: 'USD',
|
|
369
|
-
facilitator_url: this.baseUrl,
|
|
370
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
371
|
-
};
|
|
372
|
-
this.checkAbortBeforePayment(options.signal);
|
|
373
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/tools/commerce/buy', payload, quoteData.context.quote_id, options.signal);
|
|
374
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
375
|
-
const buyResp = await this.makeRequest('/v1/tools/commerce/buy', payload, auth, quoteData.context.quote_id, options.signal, 60000);
|
|
350
|
+
const { execResp: buyResp } = await this.runQuoteToPay({
|
|
351
|
+
endpoint: '/v1/tools/commerce/buy',
|
|
352
|
+
payload,
|
|
353
|
+
signal: options.signal,
|
|
354
|
+
maxCost: options.maxCost,
|
|
355
|
+
quoteTimeoutMs: 120000,
|
|
356
|
+
execTimeoutMs: 60000,
|
|
357
|
+
expectMsg: 'Expected 402 for quote',
|
|
358
|
+
totalOf: (ctx) => ctx.total,
|
|
359
|
+
onQuote: (ctx) => this.log(`Commerce quote: $${ctx.total} for "${ctx.product_title}"`),
|
|
360
|
+
});
|
|
376
361
|
if (buyResp.status !== 202) {
|
|
377
362
|
throw new errors_1.ToolError('Commerce buy failed', buyResp.status, await buyResp.text());
|
|
378
363
|
}
|
|
@@ -424,9 +409,6 @@ class OneShot {
|
|
|
424
409
|
const payload = {
|
|
425
410
|
objective: options.objective,
|
|
426
411
|
target_number: options.target_number,
|
|
427
|
-
signal: options.signal,
|
|
428
|
-
onStatusUpdate: options.onStatusUpdate,
|
|
429
|
-
wait: options.wait
|
|
430
412
|
};
|
|
431
413
|
if (options.caller_persona)
|
|
432
414
|
payload.caller_persona = options.caller_persona;
|
|
@@ -434,40 +416,25 @@ class OneShot {
|
|
|
434
416
|
payload.context = options.context;
|
|
435
417
|
if (options.max_duration_minutes)
|
|
436
418
|
payload.max_duration_minutes = options.max_duration_minutes;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
throw new errors_1.OneShotError(`Quote $${quoteData.context.total} exceeds maxCost $${options.maxCost}`);
|
|
457
|
-
}
|
|
458
|
-
const paymentInfo = {
|
|
459
|
-
protocol: 'x402',
|
|
460
|
-
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
461
|
-
payTo: quoteData.payment_request.recipient,
|
|
462
|
-
amount: quoteData.payment_request.amount,
|
|
463
|
-
currency: 'USD',
|
|
464
|
-
facilitator_url: this.baseUrl,
|
|
465
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
466
|
-
};
|
|
467
|
-
this.checkAbortBeforePayment(options.signal);
|
|
468
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/tools/voice/call', payload, quoteData.context.quote_id, options.signal);
|
|
469
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
470
|
-
const callResp = await this.makeRequest('/v1/tools/voice/call', payload, auth, quoteData.context.quote_id, options.signal);
|
|
419
|
+
const { execResp: callResp } = await this.runQuoteToPay({
|
|
420
|
+
endpoint: '/v1/tools/voice/call',
|
|
421
|
+
payload,
|
|
422
|
+
signal: options.signal,
|
|
423
|
+
maxCost: options.maxCost,
|
|
424
|
+
expectMsg: 'Expected 402 for quote',
|
|
425
|
+
totalOf: (ctx) => ctx.total,
|
|
426
|
+
onQuote: (ctx) => this.log(`Voice quote: $${ctx.total} for ${ctx.estimated_duration_minutes}min call`),
|
|
427
|
+
on400: async (resp) => {
|
|
428
|
+
const errorData = await resp.json();
|
|
429
|
+
if (errorData.error === 'content_blocked') {
|
|
430
|
+
throw new errors_1.ContentBlockedError(errorData.message, errorData.categories || []);
|
|
431
|
+
}
|
|
432
|
+
if (errorData.error === 'emergency_number_blocked') {
|
|
433
|
+
throw new errors_1.EmergencyNumberError(errorData.message, errorData.blocked_number || '');
|
|
434
|
+
}
|
|
435
|
+
throw new errors_1.ValidationError(errorData.message || 'Invalid request', 'request');
|
|
436
|
+
},
|
|
437
|
+
});
|
|
471
438
|
if (callResp.status !== 202) {
|
|
472
439
|
throw new errors_1.ToolError('Voice call initiation failed', callResp.status, await callResp.text());
|
|
473
440
|
}
|
|
@@ -510,44 +477,27 @@ class OneShot {
|
|
|
510
477
|
const payload = {
|
|
511
478
|
message: options.message,
|
|
512
479
|
to_number: options.to_number,
|
|
513
|
-
signal: options.signal,
|
|
514
|
-
onStatusUpdate: options.onStatusUpdate,
|
|
515
|
-
wait: options.wait
|
|
516
480
|
};
|
|
517
481
|
// SMS uses quote-to-pay flow (402 -> payment -> 202)
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
const paymentInfo = {
|
|
539
|
-
protocol: 'x402',
|
|
540
|
-
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
541
|
-
payTo: quoteData.payment_request.recipient,
|
|
542
|
-
amount: quoteData.payment_request.amount,
|
|
543
|
-
currency: 'USD',
|
|
544
|
-
facilitator_url: this.baseUrl,
|
|
545
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
546
|
-
};
|
|
547
|
-
this.checkAbortBeforePayment(options.signal);
|
|
548
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/tools/sms/send', payload, quoteData.context.quote_id, options.signal);
|
|
549
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
550
|
-
const sendResp = await this.makeRequest('/v1/tools/sms/send', payload, auth, quoteData.context.quote_id, options.signal);
|
|
482
|
+
const { execResp: sendResp } = await this.runQuoteToPay({
|
|
483
|
+
endpoint: '/v1/tools/sms/send',
|
|
484
|
+
payload,
|
|
485
|
+
signal: options.signal,
|
|
486
|
+
maxCost: options.maxCost,
|
|
487
|
+
expectMsg: 'Expected 402 for quote',
|
|
488
|
+
totalOf: (ctx) => ctx.total,
|
|
489
|
+
onQuote: (ctx) => this.log(`SMS quote: $${ctx.total} for ${ctx.segment_count} segment(s) to ${recipientCount} recipient(s)`),
|
|
490
|
+
on400: async (resp) => {
|
|
491
|
+
const errorData = await resp.json();
|
|
492
|
+
if (errorData.error === 'content_blocked') {
|
|
493
|
+
throw new errors_1.ContentBlockedError(errorData.message, errorData.categories || []);
|
|
494
|
+
}
|
|
495
|
+
if (errorData.error === 'emergency_number_blocked') {
|
|
496
|
+
throw new errors_1.EmergencyNumberError(errorData.message, errorData.blocked_number || '');
|
|
497
|
+
}
|
|
498
|
+
throw new errors_1.ValidationError(errorData.message || 'Invalid request', 'request');
|
|
499
|
+
},
|
|
500
|
+
});
|
|
551
501
|
if (sendResp.status !== 202) {
|
|
552
502
|
throw new errors_1.ToolError('SMS send failed', sendResp.status, await sendResp.text());
|
|
553
503
|
}
|
|
@@ -584,9 +534,6 @@ class OneShot {
|
|
|
584
534
|
const payload = {
|
|
585
535
|
type: options.type ?? 'saas',
|
|
586
536
|
product: options.product,
|
|
587
|
-
signal: options.signal,
|
|
588
|
-
onStatusUpdate: options.onStatusUpdate,
|
|
589
|
-
wait: options.wait
|
|
590
537
|
};
|
|
591
538
|
if (options.source_url)
|
|
592
539
|
payload.source_url = options.source_url;
|
|
@@ -602,34 +549,24 @@ class OneShot {
|
|
|
602
549
|
payload.domain = options.domain;
|
|
603
550
|
if (options.build_id)
|
|
604
551
|
payload.build_id = options.build_id;
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
payTo: quoteData.payment_request.recipient,
|
|
624
|
-
amount: quoteData.payment_request.amount,
|
|
625
|
-
currency: 'USD',
|
|
626
|
-
facilitator_url: this.baseUrl,
|
|
627
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
628
|
-
};
|
|
629
|
-
this.checkAbortBeforePayment(options.signal);
|
|
630
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/tools/build', payload, quoteData.context.quote_id, options.signal);
|
|
631
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
632
|
-
const buildResp = await this.makeRequest('/v1/tools/build', payload, auth, quoteData.context.quote_id, options.signal);
|
|
552
|
+
const { execResp: buildResp } = await this.runQuoteToPay({
|
|
553
|
+
endpoint: '/v1/tools/build',
|
|
554
|
+
payload,
|
|
555
|
+
signal: options.signal,
|
|
556
|
+
maxCost: options.maxCost,
|
|
557
|
+
// Build analysis can be slow server-side; bound the quote leg client-side.
|
|
558
|
+
quoteTimeoutMs: 120000,
|
|
559
|
+
expectMsg: 'Expected 402 for quote',
|
|
560
|
+
totalOf: (ctx) => ctx.pricing.total,
|
|
561
|
+
onQuote: (ctx) => {
|
|
562
|
+
this.log(`Build quote: $${ctx.pricing.total} for "${ctx.product_name}"`);
|
|
563
|
+
this.log(`Type: ${ctx.analysis.inferred_type}, Sections: ${ctx.analysis.estimated_sections}`);
|
|
564
|
+
},
|
|
565
|
+
on400: async (resp) => {
|
|
566
|
+
const errorData = await resp.json();
|
|
567
|
+
throw new errors_1.ValidationError(errorData.message || 'Invalid request', 'request');
|
|
568
|
+
},
|
|
569
|
+
});
|
|
633
570
|
if (buildResp.status !== 202) {
|
|
634
571
|
throw new errors_1.ToolError('Build initiation failed', buildResp.status, await buildResp.text());
|
|
635
572
|
}
|
|
@@ -662,9 +599,6 @@ class OneShot {
|
|
|
662
599
|
}
|
|
663
600
|
const payload = {
|
|
664
601
|
task: options.task,
|
|
665
|
-
signal: options.signal,
|
|
666
|
-
onStatusUpdate: options.onStatusUpdate,
|
|
667
|
-
wait: options.wait
|
|
668
602
|
};
|
|
669
603
|
if (options.output_schema)
|
|
670
604
|
payload.output_schema = options.output_schema;
|
|
@@ -680,33 +614,21 @@ class OneShot {
|
|
|
680
614
|
payload.secrets = options.secrets;
|
|
681
615
|
if (options.max_steps)
|
|
682
616
|
payload.max_steps = options.max_steps;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
protocol: 'x402',
|
|
699
|
-
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
700
|
-
payTo: quoteData.payment_request.recipient,
|
|
701
|
-
amount: quoteData.payment_request.amount,
|
|
702
|
-
currency: 'USD',
|
|
703
|
-
facilitator_url: this.baseUrl,
|
|
704
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
705
|
-
};
|
|
706
|
-
this.checkAbortBeforePayment(options.signal);
|
|
707
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/tools/browser', payload, quoteData.context.quote_id, options.signal);
|
|
708
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
709
|
-
const execResp = await this.makeRequest('/v1/tools/browser', payload, auth, quoteData.context.quote_id, options.signal);
|
|
617
|
+
const { execResp } = await this.runQuoteToPay({
|
|
618
|
+
endpoint: '/v1/tools/browser',
|
|
619
|
+
payload,
|
|
620
|
+
signal: options.signal,
|
|
621
|
+
maxCost: options.maxCost,
|
|
622
|
+
// Browser analysis can be slow server-side; bound the quote leg client-side.
|
|
623
|
+
quoteTimeoutMs: 120000,
|
|
624
|
+
expectMsg: 'Expected 402 for quote',
|
|
625
|
+
totalOf: (ctx) => ctx.estimated_cost,
|
|
626
|
+
onQuote: (ctx) => this.log(`Browser quote: $${ctx.estimated_cost} for ~${ctx.estimated_steps} steps`),
|
|
627
|
+
on400: async (resp) => {
|
|
628
|
+
const errorData = await resp.json();
|
|
629
|
+
throw new errors_1.ValidationError(errorData.message || 'Invalid request', 'request');
|
|
630
|
+
},
|
|
631
|
+
});
|
|
710
632
|
if (execResp.status !== 202) {
|
|
711
633
|
throw new errors_1.ToolError('Browser task initiation failed', execResp.status, await execResp.text());
|
|
712
634
|
}
|
|
@@ -730,7 +652,7 @@ class OneShot {
|
|
|
730
652
|
this.validate(name, 'name');
|
|
731
653
|
const response = await fetch(`${this.baseUrl}/v1/tools/browser/profiles`, {
|
|
732
654
|
method: 'POST',
|
|
733
|
-
headers:
|
|
655
|
+
headers: this.jsonHeaders(),
|
|
734
656
|
body: JSON.stringify({ name }),
|
|
735
657
|
});
|
|
736
658
|
if (!response.ok) {
|
|
@@ -811,14 +733,11 @@ class OneShot {
|
|
|
811
733
|
* ```
|
|
812
734
|
*/
|
|
813
735
|
async smsInboxList(options = {}) {
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if (options.from)
|
|
820
|
-
params.set('from', options.from);
|
|
821
|
-
const qs = params.toString();
|
|
736
|
+
const qs = this.buildQuery({
|
|
737
|
+
since: options.since || undefined,
|
|
738
|
+
limit: options.limit || undefined,
|
|
739
|
+
from: options.from || undefined,
|
|
740
|
+
});
|
|
822
741
|
const response = await fetch(`${this.baseUrl}/v1/tools/sms/inbox${qs ? `?${qs}` : ''}`, {
|
|
823
742
|
headers: this.headers()
|
|
824
743
|
});
|
|
@@ -862,12 +781,10 @@ class OneShot {
|
|
|
862
781
|
* ```
|
|
863
782
|
*/
|
|
864
783
|
async notifications(options = {}) {
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
params.set('limit', String(options.limit));
|
|
870
|
-
const qs = params.toString();
|
|
784
|
+
const qs = this.buildQuery({
|
|
785
|
+
unread: options.unread ? 'true' : undefined,
|
|
786
|
+
limit: options.limit || undefined,
|
|
787
|
+
});
|
|
871
788
|
const response = await fetch(`${this.baseUrl}/v1/tools/notifications${qs ? `?${qs}` : ''}`, {
|
|
872
789
|
headers: this.headers()
|
|
873
790
|
});
|
|
@@ -959,36 +876,24 @@ class OneShot {
|
|
|
959
876
|
payload.soul_service_slug = options.soul_service_slug;
|
|
960
877
|
if (options.schedule)
|
|
961
878
|
payload.schedule = options.schedule;
|
|
962
|
-
//
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
protocol: 'x402',
|
|
981
|
-
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
982
|
-
payTo: quoteData.payment_request.recipient,
|
|
983
|
-
amount: quoteData.payment_request.amount,
|
|
984
|
-
currency: 'USD',
|
|
985
|
-
facilitator_url: this.baseUrl,
|
|
986
|
-
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
987
|
-
};
|
|
988
|
-
this.checkAbortBeforePayment(options.signal);
|
|
989
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, '/v1/compute', payload, quoteData.context.quote_id, options.signal);
|
|
990
|
-
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
991
|
-
const createResp = await this.makeRequest('/v1/compute', payload, auth, quoteData.context.quote_id, options.signal);
|
|
879
|
+
// Compute returns the goal directly on 202 — no job polling, unlike the
|
|
880
|
+
// other paid tools.
|
|
881
|
+
const { execResp: createResp } = await this.runQuoteToPay({
|
|
882
|
+
endpoint: '/v1/compute',
|
|
883
|
+
payload,
|
|
884
|
+
signal: options.signal,
|
|
885
|
+
maxCost: options.maxCost,
|
|
886
|
+
expectMsg: 'Expected 402 for compute quote',
|
|
887
|
+
totalOf: (ctx) => ctx.total_budget,
|
|
888
|
+
onQuote: (ctx) => this.log(`Compute quote: $${ctx.total_budget} — ${ctx.objective_summary}`),
|
|
889
|
+
on400: async (resp) => {
|
|
890
|
+
const errorData = await resp.json();
|
|
891
|
+
if (errorData.error === 'content_blocked') {
|
|
892
|
+
throw new errors_1.ContentBlockedError(errorData.message, []);
|
|
893
|
+
}
|
|
894
|
+
throw new errors_1.ValidationError(errorData.message || 'Invalid request', 'request');
|
|
895
|
+
},
|
|
896
|
+
});
|
|
992
897
|
if (createResp.status !== 202) {
|
|
993
898
|
throw new errors_1.ToolError('Compute goal creation failed', createResp.status, await createResp.text());
|
|
994
899
|
}
|
|
@@ -1075,7 +980,7 @@ class OneShot {
|
|
|
1075
980
|
this.validate(goalId, 'goalId');
|
|
1076
981
|
const response = await fetch(`${this.baseUrl}/v1/compute/${goalId}/cancel`, {
|
|
1077
982
|
method: 'POST',
|
|
1078
|
-
headers:
|
|
983
|
+
headers: this.jsonHeaders(),
|
|
1079
984
|
body: JSON.stringify({ reason })
|
|
1080
985
|
});
|
|
1081
986
|
if (response.status === 404) {
|
|
@@ -1104,7 +1009,7 @@ class OneShot {
|
|
|
1104
1009
|
this.validate(input.task_id, 'task_id');
|
|
1105
1010
|
const response = await fetch(`${this.baseUrl}/v1/compute/${goalId}/respond`, {
|
|
1106
1011
|
method: 'POST',
|
|
1107
|
-
headers:
|
|
1012
|
+
headers: this.jsonHeaders(),
|
|
1108
1013
|
body: JSON.stringify(input)
|
|
1109
1014
|
});
|
|
1110
1015
|
if (response.status === 404) {
|
|
@@ -1128,7 +1033,7 @@ class OneShot {
|
|
|
1128
1033
|
this.validate(goalId, 'goalId');
|
|
1129
1034
|
const response = await fetch(`${this.baseUrl}/v1/compute/${goalId}/pause`, {
|
|
1130
1035
|
method: 'POST',
|
|
1131
|
-
headers:
|
|
1036
|
+
headers: this.jsonHeaders(),
|
|
1132
1037
|
body: JSON.stringify({ reason })
|
|
1133
1038
|
});
|
|
1134
1039
|
if (!response.ok) {
|
|
@@ -1150,7 +1055,7 @@ class OneShot {
|
|
|
1150
1055
|
this.validate(goalId, 'goalId');
|
|
1151
1056
|
const response = await fetch(`${this.baseUrl}/v1/compute/${goalId}/resume`, {
|
|
1152
1057
|
method: 'POST',
|
|
1153
|
-
headers:
|
|
1058
|
+
headers: this.jsonHeaders(),
|
|
1154
1059
|
body: JSON.stringify({})
|
|
1155
1060
|
});
|
|
1156
1061
|
if (!response.ok) {
|
|
@@ -1219,10 +1124,7 @@ class OneShot {
|
|
|
1219
1124
|
* ```
|
|
1220
1125
|
*/
|
|
1221
1126
|
async spendBreakdown(options) {
|
|
1222
|
-
const
|
|
1223
|
-
if (options?.period)
|
|
1224
|
-
params.set('period', String(options.period));
|
|
1225
|
-
const qs = params.toString();
|
|
1127
|
+
const qs = this.buildQuery({ period: options?.period || undefined });
|
|
1226
1128
|
const response = await fetch(`${this.baseUrl}/v1/analytics/spend/breakdown${qs ? `?${qs}` : ''}`, {
|
|
1227
1129
|
headers: this.headers()
|
|
1228
1130
|
});
|
|
@@ -1241,10 +1143,7 @@ class OneShot {
|
|
|
1241
1143
|
* ```
|
|
1242
1144
|
*/
|
|
1243
1145
|
async rocs(options) {
|
|
1244
|
-
const
|
|
1245
|
-
if (options?.period)
|
|
1246
|
-
params.set('period', String(options.period));
|
|
1247
|
-
const qs = params.toString();
|
|
1146
|
+
const qs = this.buildQuery({ period: options?.period || undefined });
|
|
1248
1147
|
const response = await fetch(`${this.baseUrl}/v1/analytics/rocs${qs ? `?${qs}` : ''}`, {
|
|
1249
1148
|
headers: this.headers()
|
|
1250
1149
|
});
|
|
@@ -1265,14 +1164,11 @@ class OneShot {
|
|
|
1265
1164
|
* ```
|
|
1266
1165
|
*/
|
|
1267
1166
|
async receiptsList(options) {
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (options?.limit)
|
|
1274
|
-
params.set('limit', String(options.limit));
|
|
1275
|
-
const qs = params.toString();
|
|
1167
|
+
const qs = this.buildQuery({
|
|
1168
|
+
period: options?.period || undefined,
|
|
1169
|
+
category: options?.category || undefined,
|
|
1170
|
+
limit: options?.limit || undefined,
|
|
1171
|
+
});
|
|
1276
1172
|
const response = await fetch(`${this.baseUrl}/v1/analytics/receipts${qs ? `?${qs}` : ''}`, {
|
|
1277
1173
|
headers: this.headers()
|
|
1278
1174
|
});
|
|
@@ -1324,6 +1220,26 @@ class OneShot {
|
|
|
1324
1220
|
'X-OneShot-SDK-Version': SDK_VERSION
|
|
1325
1221
|
};
|
|
1326
1222
|
}
|
|
1223
|
+
/** Auth headers plus Content-Type for JSON-body requests. */
|
|
1224
|
+
jsonHeaders() {
|
|
1225
|
+
return { 'Content-Type': 'application/json', ...this.headers() };
|
|
1226
|
+
}
|
|
1227
|
+
/** Build a query string, skipping null/undefined values. Pass falsy-but-valid
|
|
1228
|
+
* values (0, '') as undefined at the call site to match prior `if (x)` guards. */
|
|
1229
|
+
buildQuery(params) {
|
|
1230
|
+
const qs = new URLSearchParams();
|
|
1231
|
+
for (const [k, v] of Object.entries(params)) {
|
|
1232
|
+
if (v != null)
|
|
1233
|
+
qs.set(k, String(v));
|
|
1234
|
+
}
|
|
1235
|
+
return qs.toString();
|
|
1236
|
+
}
|
|
1237
|
+
/** Local fast-fail guard: throw when a quote total exceeds the caller's cap. */
|
|
1238
|
+
assertWithinMaxCost(total, maxCost) {
|
|
1239
|
+
if (maxCost && parseFloat(total) > maxCost) {
|
|
1240
|
+
throw new errors_1.OneShotError(`Quote $${total} exceeds maxCost $${maxCost}`);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1327
1243
|
/**
|
|
1328
1244
|
* Header that asks the API to reject the request when the computed quote
|
|
1329
1245
|
* exceeds the caller-supplied cap (commit 8f328a7). The SDK still does its
|
|
@@ -1416,15 +1332,55 @@ class OneShot {
|
|
|
1416
1332
|
}
|
|
1417
1333
|
return (result.data ?? result);
|
|
1418
1334
|
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Shared x402 quote-to-pay flow for paid tools. Fetches the 402 quote, runs
|
|
1337
|
+
* any per-tool 400 handling, enforces maxCost, signs the payment auth, and
|
|
1338
|
+
* POSTs the paid request. Returns the parsed quote context plus the raw paid
|
|
1339
|
+
* Response — the caller owns the post-202 handling (poll a job vs. return the
|
|
1340
|
+
* body directly), since that differs per tool.
|
|
1341
|
+
*/
|
|
1342
|
+
async runQuoteToPay(cfg) {
|
|
1343
|
+
const quoteResp = await this.makeRequest(cfg.endpoint, cfg.payload, undefined, undefined, cfg.signal, cfg.quoteTimeoutMs, this.maxCostHeader(cfg.maxCost));
|
|
1344
|
+
if (quoteResp.status === 400 && cfg.on400) {
|
|
1345
|
+
await cfg.on400(quoteResp);
|
|
1346
|
+
}
|
|
1347
|
+
if (quoteResp.status !== 402) {
|
|
1348
|
+
throw new errors_1.ToolError(cfg.expectMsg, quoteResp.status, await quoteResp.text());
|
|
1349
|
+
}
|
|
1350
|
+
const quoteData = await quoteResp.json();
|
|
1351
|
+
cfg.onQuote(quoteData.context);
|
|
1352
|
+
this.assertWithinMaxCost(cfg.totalOf(quoteData.context), cfg.maxCost);
|
|
1353
|
+
const paymentInfo = {
|
|
1354
|
+
protocol: 'x402',
|
|
1355
|
+
network: `eip155:${quoteData.payment_request.chain_id}`,
|
|
1356
|
+
payTo: quoteData.payment_request.recipient,
|
|
1357
|
+
amount: quoteData.payment_request.amount,
|
|
1358
|
+
currency: 'USD',
|
|
1359
|
+
facilitator_url: this.baseUrl,
|
|
1360
|
+
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
1361
|
+
};
|
|
1362
|
+
this.checkAbortBeforePayment(cfg.signal);
|
|
1363
|
+
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal);
|
|
1364
|
+
const auth = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
1365
|
+
const execResp = await this.makeRequest(cfg.endpoint, cfg.payload, auth, quoteData.context.quote_id, cfg.signal, cfg.execTimeoutMs);
|
|
1366
|
+
return { context: quoteData.context, execResp };
|
|
1367
|
+
}
|
|
1419
1368
|
async pollJob(requestId, timeoutSec, signal, onStatusUpdate, phoneOpts) {
|
|
1420
|
-
// Try WebSocket push first, fall back to HTTP polling
|
|
1369
|
+
// Try WebSocket push first, fall back to HTTP polling. Both phases share a
|
|
1370
|
+
// single deadline so the combined wait never exceeds the caller's timeout —
|
|
1371
|
+
// previously the WS cap (≤180s) plus a fresh full HTTP timeout could total
|
|
1372
|
+
// up to ~1.6× timeoutSec (e.g. ~480s for a 300s request).
|
|
1373
|
+
const deadline = timeoutSec != null ? Date.now() + timeoutSec * 1000 : undefined;
|
|
1421
1374
|
let result;
|
|
1422
1375
|
try {
|
|
1423
1376
|
result = await this.waitViaWebSocket(requestId, timeoutSec, signal, onStatusUpdate);
|
|
1424
1377
|
}
|
|
1425
1378
|
catch {
|
|
1426
1379
|
this.log('WebSocket unavailable, falling back to HTTP polling');
|
|
1427
|
-
|
|
1380
|
+
const remainingSec = deadline != null
|
|
1381
|
+
? Math.max(1, Math.ceil((deadline - Date.now()) / 1000))
|
|
1382
|
+
: undefined;
|
|
1383
|
+
result = await this.pollJobHttp(requestId, remainingSec, signal, onStatusUpdate);
|
|
1428
1384
|
}
|
|
1429
1385
|
// Optional second phase: keep polling for the async phone-reveal webhook.
|
|
1430
1386
|
// Only kicks in when the caller explicitly opts in AND the result still
|
|
@@ -1601,7 +1557,7 @@ class OneShot {
|
|
|
1601
1557
|
else if (msg.status === 'failed') {
|
|
1602
1558
|
settle(() => {
|
|
1603
1559
|
cleanup();
|
|
1604
|
-
reject(new errors_1.JobError(`Job failed: ${msg.error ?? 'Unknown'}`, requestId, String(msg.error ?? 'Unknown')));
|
|
1560
|
+
reject(new errors_1.JobError(`Job failed: ${msg.error ?? 'Unknown'}`, requestId, String(msg.error ?? 'Unknown'), msg.error_code));
|
|
1605
1561
|
});
|
|
1606
1562
|
}
|
|
1607
1563
|
else {
|
|
@@ -1657,7 +1613,7 @@ class OneShot {
|
|
|
1657
1613
|
return result;
|
|
1658
1614
|
}
|
|
1659
1615
|
if (job.status === 'failed') {
|
|
1660
|
-
throw new errors_1.JobError(`Job failed: ${job.error ?? 'Unknown'}`, requestId, String(job.error ?? 'Unknown'));
|
|
1616
|
+
throw new errors_1.JobError(`Job failed: ${job.error ?? 'Unknown'}`, requestId, String(job.error ?? 'Unknown'), job.error_code);
|
|
1661
1617
|
}
|
|
1662
1618
|
onStatusUpdate?.(job.status, requestId);
|
|
1663
1619
|
retries = 0;
|