@datalayer/agent-runtimes 1.3.114 → 1.5.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/dist-embed/chunks/ChatView-CaBuPTyG.js +32 -0
- package/dist-embed/chunks/{DeckComponent-CuRWiARr.js → DeckComponent-cRJJMGgt.js} +1 -1
- package/dist-embed/chunks/{DocumentView-BYU2HREd.js → DocumentView-CiqN9ze6.js} +1 -1
- package/dist-embed/chunks/{EphemeralDocument-BjFSuQKH.js → EphemeralDocument-BjJfn7J-.js} +1 -1
- package/dist-embed/chunks/{ExcalidrawComponent-CFLnMPnf.js → ExcalidrawComponent-BtNXWUN8.js} +1 -1
- package/dist-embed/chunks/{ExcalidrawModal-CB9d5Vpk.js → ExcalidrawModal-C6SEKFlD.js} +1 -1
- package/dist-embed/chunks/{JupyterLexicalExtension-CYnZrelh.js → JupyterLexicalExtension-BuRLImy5.js} +5 -5
- package/dist-embed/chunks/{NotebookToolSurfaces-D62q-lbb.js → NotebookToolSurfaces-ktcGRm7d.js} +1 -1
- package/dist-embed/chunks/{Output-2KxoKu5F.js → Output-BscOPLqE.js} +10 -10
- package/dist-embed/chunks/{YouTubePlugin-CjODyxZ2.js → YouTubePlugin-xBjNr2Iu.js} +1 -1
- package/dist-embed/chunks/{ar-SA-G6X2FPQ2-C-zAYgA-.js → ar-SA-G6X2FPQ2-BB82iKLz.js} +1 -1
- package/dist-embed/chunks/{az-AZ-76LH7QW2-BHq5js6U.js → az-AZ-76LH7QW2-DsMRKXlw.js} +1 -1
- package/dist-embed/chunks/{bg-BG-XCXSNQG7-BTcNNcTc.js → bg-BG-XCXSNQG7-43Y1kPxa.js} +1 -1
- package/dist-embed/chunks/{bn-BD-2XOGV67Q-DXRk27u1.js → bn-BD-2XOGV67Q-CLqPJBll.js} +1 -1
- package/dist-embed/chunks/{ca-ES-6MX7JW3Y-D-0J9rqp.js → ca-ES-6MX7JW3Y-CDFGSEh3.js} +1 -1
- package/dist-embed/chunks/{cs-CZ-2BRQDIVT-CgN3I-57.js → cs-CZ-2BRQDIVT-ByNB8eZd.js} +1 -1
- package/dist-embed/chunks/{da-DK-5WZEPLOC-CHQloSoX.js → da-DK-5WZEPLOC-B7MrVwy8.js} +1 -1
- package/dist-embed/chunks/{de-DE-XR44H4JA-C-Hq84pe.js → de-DE-XR44H4JA-CN1cdk94.js} +1 -1
- package/dist-embed/chunks/{el-GR-BZB4AONW-MTzGcSsP.js → el-GR-BZB4AONW-DnaGhWD1.js} +1 -1
- package/dist-embed/chunks/{es-ES-U4NZUMDT-Thj-4Rwn.js → es-ES-U4NZUMDT-DE1vZzDE.js} +1 -1
- package/dist-embed/chunks/{eu-ES-A7QVB2H4-BEEyNQo-.js → eu-ES-A7QVB2H4-DmHB0zpm.js} +1 -1
- package/dist-embed/chunks/{fa-IR-HGAKTJCU-CtUGmNBZ.js → fa-IR-HGAKTJCU-CNzE8tJj.js} +1 -1
- package/dist-embed/chunks/{fi-FI-Z5N7JZ37-6GMgpkSu.js → fi-FI-Z5N7JZ37-Ca2vTFQC.js} +1 -1
- package/dist-embed/chunks/{fr-FR-RHASNOE6-BakNCbxj.js → fr-FR-RHASNOE6-B0yWFX3_.js} +1 -1
- package/dist-embed/chunks/{gl-ES-HMX3MZ6V-Cwkb9OPk.js → gl-ES-HMX3MZ6V-2_3FMVJq.js} +1 -1
- package/dist-embed/chunks/{he-IL-6SHJWFNN-BaMmvCkr.js → he-IL-6SHJWFNN-BW1lby00.js} +1 -1
- package/dist-embed/chunks/{hi-IN-IWLTKZ5I-1aWewhep.js → hi-IN-IWLTKZ5I-DTixfSwB.js} +1 -1
- package/dist-embed/chunks/{hu-HU-A5ZG7DT2-BIHI23BQ.js → hu-HU-A5ZG7DT2-DOfYwLWP.js} +1 -1
- package/dist-embed/chunks/{id-ID-SAP4L64H-CYpgmx8X.js → id-ID-SAP4L64H-Cwtdtkvg.js} +1 -1
- package/dist-embed/chunks/{index-BhSMzuKq.js → index--qMT5k1q.js} +1 -1
- package/dist-embed/chunks/{index-b1yzCiTq.js → index-B-UP9DlM.js} +1 -1
- package/dist-embed/chunks/{index-DMKsCn-y.js → index-BAwsCLGT.js} +1 -1
- package/dist-embed/chunks/{index-BUhqZVtg.js → index-BITOMOWN.js} +1 -1
- package/dist-embed/chunks/{index-1wTrAXI1.js → index-B_9UD0bt.js} +2 -2
- package/dist-embed/chunks/{index-DtlUttQj.js → index-BgFbP5P0.js} +1 -1
- package/dist-embed/chunks/{index-CC-Ht79i.js → index-CMUTS_sA.js} +1 -1
- package/dist-embed/chunks/{index-B7nmHsEo.js → index-Cgf7tlW3.js} +1 -1
- package/dist-embed/chunks/{index-CsZ2HycN.js → index-CrNfUmIU.js} +1 -1
- package/dist-embed/chunks/{index-DIOcQUrz.js → index-CzuLxFBK.js} +1 -1
- package/dist-embed/chunks/{index-EmZm7_bH.js → index-DEn5R18M.js} +1 -1
- package/dist-embed/chunks/{index-DeVZ-nmF.js → index-DFEBhySA.js} +1 -1
- package/dist-embed/chunks/{index-CMYyiALV.js → index-DYkwi0aX.js} +1 -1
- package/dist-embed/chunks/{index-Fzz2KjFM.js → index-DvpdAlVH.js} +1 -1
- package/dist-embed/chunks/{index-W7TVoWP5.js → index-Dyh5xopp.js} +1 -1
- package/dist-embed/chunks/{index-en9Yf1Yz.js → index-Jt4bWLoz.js} +1 -1
- package/dist-embed/chunks/{index-BbucLMre.js → index-S-sjhoD3.js} +1 -1
- package/dist-embed/chunks/{index-D18sf241.js → index-TRLGqZaF.js} +1 -1
- package/dist-embed/chunks/{index-ZZnjmFwm.js → index-YcqCoWJl.js} +1 -1
- package/dist-embed/chunks/{index-BFLtEBuP.js → index-sQy5cmWo.js} +1 -1
- package/dist-embed/chunks/{it-IT-JPQ66NNP-CLcqplyX.js → it-IT-JPQ66NNP-C3xZ61-6.js} +1 -1
- package/dist-embed/chunks/{ja-JP-DBVTYXUO-u8Qe6tR7.js → ja-JP-DBVTYXUO-CeTmglEb.js} +1 -1
- package/dist-embed/chunks/{kaa-6HZHGXH3-BG2kx50I.js → kaa-6HZHGXH3-D6Jr6B6Y.js} +1 -1
- package/dist-embed/chunks/{kab-KAB-ZGHBKWFO-CSYUpRxd.js → kab-KAB-ZGHBKWFO-pEXBqeuG.js} +1 -1
- package/dist-embed/chunks/{kk-KZ-P5N5QNE5-CpBMgHAz.js → kk-KZ-P5N5QNE5-BRJunmHm.js} +1 -1
- package/dist-embed/chunks/{km-KH-HSX4SM5Z-CoMUUQMb.js → km-KH-HSX4SM5Z--GJgT9rN.js} +1 -1
- package/dist-embed/chunks/{ko-KR-MTYHY66A-DwxY8CGI.js → ko-KR-MTYHY66A-Bjnez4qR.js} +1 -1
- package/dist-embed/chunks/{ku-TR-6OUDTVRD-UoNYUfm0.js → ku-TR-6OUDTVRD-DgOdYJrS.js} +1 -1
- package/dist-embed/chunks/{lt-LT-XHIRWOB4-Dr4uXaEj.js → lt-LT-XHIRWOB4-BMwhrtjM.js} +1 -1
- package/dist-embed/chunks/{lv-LV-5QDEKY6T-CNC56J-_.js → lv-LV-5QDEKY6T-0pxW7YE0.js} +1 -1
- package/dist-embed/chunks/{mr-IN-CRQNXWMA-BKWa9nzZ.js → mr-IN-CRQNXWMA-tndp8BXC.js} +1 -1
- package/dist-embed/chunks/{my-MM-5M5IBNSE-bwAzOWhV.js → my-MM-5M5IBNSE-sispDhO4.js} +1 -1
- package/dist-embed/chunks/{nb-NO-T6EIAALU-CkpsChor.js → nb-NO-T6EIAALU-CjBurOuS.js} +1 -1
- package/dist-embed/chunks/{nl-NL-IS3SIHDZ-COVk4rlb.js → nl-NL-IS3SIHDZ-B58EVmr3.js} +1 -1
- package/dist-embed/chunks/{nn-NO-6E72VCQL-DlyFF-C1.js → nn-NO-6E72VCQL-D1p7LZSq.js} +1 -1
- package/dist-embed/chunks/{oc-FR-POXYY2M6-CFDosV2C.js → oc-FR-POXYY2M6-C1M8sddP.js} +1 -1
- package/dist-embed/chunks/{pa-IN-N4M65BXN-BKRB3O0U.js → pa-IN-N4M65BXN-D8ESzASl.js} +1 -1
- package/dist-embed/chunks/{percentages-BXMCSKIN-D7pnqUsZ.js → percentages-BXMCSKIN-BihMTsFR.js} +3 -3
- package/dist-embed/chunks/{pl-PL-T2D74RX3-C5r2Tds7.js → pl-PL-T2D74RX3-DQGe6LHC.js} +1 -1
- package/dist-embed/chunks/{pt-BR-5N22H2LF-CDnT98bW.js → pt-BR-5N22H2LF-CvXBgZQs.js} +1 -1
- package/dist-embed/chunks/{pt-PT-UZXXM6DQ-C6c4SPk1.js → pt-PT-UZXXM6DQ-BMnnlIUJ.js} +1 -1
- package/dist-embed/chunks/{ro-RO-JPDTUUEW-CW6pbzEZ.js → ro-RO-JPDTUUEW-DZ-9UUH7.js} +1 -1
- package/dist-embed/chunks/{ru-RU-B4JR7IUQ-CbaAJG0w.js → ru-RU-B4JR7IUQ-JlRBwjob.js} +1 -1
- package/dist-embed/chunks/{si-LK-N5RQ5JYF-BZkUjpXF.js → si-LK-N5RQ5JYF-Bx8kHnkd.js} +1 -1
- package/dist-embed/chunks/{sk-SK-C5VTKIMK-DsfWgpPU.js → sk-SK-C5VTKIMK-xsfEfbZb.js} +1 -1
- package/dist-embed/chunks/{sl-SI-NN7IZMDC-Kx0FqxKU.js → sl-SI-NN7IZMDC-SQYUECnr.js} +1 -1
- package/dist-embed/chunks/{subset-shared.chunk-DM3l9OuH.js → subset-shared.chunk-Ch6j8nKC.js} +1 -1
- package/dist-embed/chunks/{subset-worker.chunk-4w4yibm3.js → subset-worker.chunk-pJjck5pR.js} +1 -1
- package/dist-embed/chunks/{sv-SE-XGPEYMSR-DY0ryO-L.js → sv-SE-XGPEYMSR-BxcjSHE2.js} +1 -1
- package/dist-embed/chunks/{ta-IN-2NMHFXQM-CWpIs0i0.js → ta-IN-2NMHFXQM-rItSkDUS.js} +1 -1
- package/dist-embed/chunks/{th-TH-HPSO5L25-Cwkcfb3u.js → th-TH-HPSO5L25-B7UzESFj.js} +1 -1
- package/dist-embed/chunks/{tr-TR-DEFEU3FU-BqJZZl-Y.js → tr-TR-DEFEU3FU-Ci6oNfEX.js} +1 -1
- package/dist-embed/chunks/{uk-UA-QMV73CPH-Dg213mG4.js → uk-UA-QMV73CPH-C9csqvO6.js} +1 -1
- package/dist-embed/chunks/{vi-VN-M7AON7JQ-DY94NKxF.js → vi-VN-M7AON7JQ-DRAj2kZj.js} +1 -1
- package/dist-embed/chunks/{zh-CN-LNUGB5OW-B6I9A7sA.js → zh-CN-LNUGB5OW-DLOyW8hH.js} +1 -1
- package/dist-embed/chunks/{zh-HK-E62DVLB3-D3oqgDOt.js → zh-HK-E62DVLB3-DwC6_2Ws.js} +1 -1
- package/dist-embed/chunks/{zh-TW-RAJ6MFWO-CYwgY0Jo.js → zh-TW-RAJ6MFWO-DEMugIwa.js} +1 -1
- package/dist-embed/datalayer-app-main.js +156 -156
- package/lib/apps/apps/appspec.d.ts +13 -0
- package/lib/apps/apps/appspec.js +93 -0
- package/lib/apps/apps/checks.d.ts +1 -1
- package/lib/apps/apps/checks.js +2 -2
- package/lib/apps/apps/customComponents.js +1 -1
- package/lib/apps/core/canvasBlocks.d.ts +3 -3
- package/lib/apps/core/canvasParts.d.ts +6 -4
- package/lib/apps/core/index.d.ts +8 -0
- package/lib/apps/embed/AppEmbed.js +13 -20
- package/lib/apps/embed/embedSession.d.ts +8 -4
- package/lib/apps/embed/embedSession.js +16 -9
- package/lib/apps/embed/hostBridge.d.ts +47 -13
- package/lib/apps/embed/hostBridge.js +135 -18
- package/lib/apps/index.d.ts +1 -1
- package/lib/apps/index.js +1 -1
- package/lib/apps/plugins/a2ui-surface/AnswerShows.d.ts +21 -0
- package/lib/apps/plugins/a2ui-surface/AnswerShows.js +29 -0
- package/lib/apps/plugins/a2ui-surface/SurfaceToolResult.js +4 -7
- package/lib/apps/plugins/a2ui-surface/index.d.ts +2 -1
- package/lib/apps/plugins/a2ui-surface/index.js +10 -1
- package/lib/apps/plugins/a2ui-surface/toolResult.d.ts +20 -11
- package/lib/apps/plugins/a2ui-surface/toolResult.js +27 -11
- package/lib/apps/plugins/app-components/index.d.ts +1 -1
- package/lib/apps/plugins/app-components/index.js +2 -2
- package/lib/apps/plugins/app-elements/AppElements.d.ts +3 -3
- package/lib/apps/plugins/app-elements/AppElements.js +5 -20
- package/lib/apps/plugins/app-elements/QuestionView.d.ts +30 -0
- package/lib/apps/plugins/app-elements/QuestionView.js +70 -0
- package/lib/apps/plugins/app-elements/index.d.ts +7 -4
- package/lib/apps/plugins/app-elements/index.js +14 -5
- package/lib/apps/plugins/app-page/appPageModel.d.ts +6 -8
- package/lib/apps/plugins/app-page/appPageModel.js +0 -2
- package/lib/apps/plugins/app-page/pageRun.d.ts +2 -2
- package/lib/apps/plugins/app-page/pageRun.js +3 -3
- package/lib/apps/plugins/canvas-blocks/index.d.ts +13 -13
- package/lib/apps/plugins/canvas-blocks/index.js +24 -24
- package/lib/apps/plugins/canvas-parts/index.d.ts +7 -1
- package/lib/apps/plugins/canvas-parts/index.js +36 -0
- package/lib/apps/plugins/chat/ChatView.js +29 -3
- package/lib/chat/base/ChatBase.js +264 -43
- package/lib/chat/base/loopElement.d.ts +16 -17
- package/lib/chat/base/loopElement.js +11 -27
- package/lib/chat/base/loopStep.d.ts +15 -15
- package/lib/chat/base/loopStep.js +10 -13
- package/lib/chat/base/loopWindow.d.ts +15 -20
- package/lib/chat/base/loopWindow.js +26 -27
- package/lib/chat/base/uiFiles.d.ts +29 -0
- package/lib/chat/base/uiFiles.js +69 -0
- package/lib/chat/base/uiOps.d.ts +96 -0
- package/lib/chat/base/uiOps.js +251 -0
- package/lib/chat/messages/ChatMessageList.d.ts +13 -1
- package/lib/chat/messages/ChatMessageList.js +4 -4
- package/lib/components/a2ui/datalayer/Chart.d.ts +1 -1
- package/lib/components/a2ui/datalayer/schema.js +1 -1
- package/lib/components/teams/AnswerSurfaces.d.ts +3 -2
- package/lib/components/teams/AnswerSurfaces.js +7 -7
- package/lib/components/teams/useA2ATeam.d.ts +1 -1
- package/lib/config/AgentConfiguration.d.ts +1 -1
- package/lib/examples/ProtocolSignedRequestsExample.d.ts +10 -0
- package/lib/examples/ProtocolSignedRequestsExample.js +41 -0
- package/lib/examples/ProtocolUcpCheckoutExample.d.ts +10 -0
- package/lib/examples/ProtocolUcpCheckoutExample.js +59 -0
- package/lib/examples/example-selector.js +2 -0
- package/lib/examples/exampleGroups.d.ts +1 -1
- package/lib/examples/exampleGroups.js +4 -0
- package/lib/examples/index.d.ts +2 -0
- package/lib/examples/index.js +2 -0
- package/lib/examples/protocols/signedRequestsDemo.d.ts +27 -0
- package/lib/examples/protocols/signedRequestsDemo.js +59 -0
- package/lib/examples/protocols/ucpCheckoutDemo.d.ts +36 -0
- package/lib/examples/protocols/ucpCheckoutDemo.js +68 -0
- package/lib/protocols/AGUIAdapter.d.ts +7 -0
- package/lib/protocols/AGUIAdapter.js +43 -3
- package/lib/protocols/a2a/index.js +1 -0
- package/lib/protocols/agentic-commerce/index.js +1 -0
- package/lib/protocols/core.d.ts +6 -0
- package/lib/protocols/mcp/index.js +1 -0
- package/lib/protocols/pact/index.js +1 -0
- package/lib/protocols/ucp/index.js +1 -0
- package/lib/protocols/ucp/merchant.d.ts +15 -0
- package/lib/protocols/ucp/merchant.js +257 -0
- package/lib/protocols/web-bot-auth/index.js +1 -0
- package/lib/specs/agents/agents.js +338 -169
- package/lib/specs/apps.js +134 -6
- package/lib/specs/appspecSchema.js +59 -0
- package/lib/specs/cogs.js +6 -3
- package/lib/specs/index.d.ts +2 -1
- package/lib/specs/index.js +2 -1
- package/lib/specs/protocols.d.ts +24 -0
- package/lib/specs/protocols.js +197 -0
- package/lib/specs/ui.d.ts +21 -0
- package/lib/specs/{uiPlugins.js → ui.js} +14 -14
- package/lib/types/agentspecs.d.ts +69 -5
- package/lib/types/messages.d.ts +7 -0
- package/lib/types/protocol.d.ts +15 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_agents.py +43 -8
- package/scripts/codegen/generate_protocols.py +207 -0
- package/scripts/codegen/generate_sandbox_agents.py +1 -1
- package/scripts/codegen/{generate_ui_plugins.py → generate_ui.py} +40 -40
- package/scripts/gen_workers.py +2 -2
- package/dist-embed/chunks/ChatView-D41Nx5rC.js +0 -32
- package/lib/chat/base/loopMessage.d.ts +0 -37
- package/lib/chat/base/loopMessage.js +0 -37
- package/lib/specs/uiPlugins.d.ts +0 -21
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* *Example Store*, the local UCP test merchant, as a `fetch` (AGENT_PROTOCOLS
|
|
7
|
+
* EX-04, EX-10): the same catalogue, totals, email gate, idempotency and
|
|
8
|
+
* test payment as `agent_runtimes.protocols.ucp.merchant.ExampleStore`, so a
|
|
9
|
+
* page or a test buys from it with no network. A test merchant only: it keeps
|
|
10
|
+
* nothing beyond the page, and refuses any payment but its test handler's.
|
|
11
|
+
*
|
|
12
|
+
* @module protocols/ucp/merchant
|
|
13
|
+
*/
|
|
14
|
+
import { CHECKOUT_CAPABILITY, SHOPPING_SERVICE, UCP_VERSION } from './index';
|
|
15
|
+
/** The store's products, as the Python store lists them. */
|
|
16
|
+
export const EXAMPLE_STORE_CATALOG = {
|
|
17
|
+
tea_sencha: { title: 'Sencha green tea, 100 g', price: 1250 },
|
|
18
|
+
tea_assam: { title: 'Assam black tea, 250 g', price: 1800 },
|
|
19
|
+
mug: { title: 'Stoneware mug', price: 2400 },
|
|
20
|
+
};
|
|
21
|
+
/** The one payment handler it takes, and the prefix of its test tokens. */
|
|
22
|
+
export const EXAMPLE_STORE_HANDLER_ID = 'test_pay_1';
|
|
23
|
+
const TEST_TOKEN_PREFIX = 'tok_test_';
|
|
24
|
+
const json = (body, status = 200) => new Response(JSON.stringify(body), {
|
|
25
|
+
status,
|
|
26
|
+
headers: { 'content-type': 'application/json' },
|
|
27
|
+
});
|
|
28
|
+
const refuse = (status, code, content) => json({
|
|
29
|
+
ucp: { version: UCP_VERSION, status: 'error' },
|
|
30
|
+
messages: [{ type: 'error', code, content, severity: 'recoverable' }],
|
|
31
|
+
}, status);
|
|
32
|
+
const meta = () => ({
|
|
33
|
+
version: UCP_VERSION,
|
|
34
|
+
status: 'success',
|
|
35
|
+
capabilities: { [CHECKOUT_CAPABILITY]: [{ version: UCP_VERSION }] },
|
|
36
|
+
payment_handlers: {
|
|
37
|
+
'com.example.test_pay': [
|
|
38
|
+
{
|
|
39
|
+
id: EXAMPLE_STORE_HANDLER_ID,
|
|
40
|
+
version: UCP_VERSION,
|
|
41
|
+
available_instruments: [{ type: 'card' }],
|
|
42
|
+
config: {},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
async function fingerprint(path, body) {
|
|
48
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(path + body));
|
|
49
|
+
return Array.from(new Uint8Array(digest), b => b.toString(16).padStart(2, '0')).join('');
|
|
50
|
+
}
|
|
51
|
+
/** The store: its `fetch`, and the orders it took. */
|
|
52
|
+
export function exampleStore(base = 'https://store.example') {
|
|
53
|
+
const checkouts = new Map();
|
|
54
|
+
const replies = new Map();
|
|
55
|
+
const orders = [];
|
|
56
|
+
const render = (checkout) => {
|
|
57
|
+
let subtotal = 0;
|
|
58
|
+
const lines = checkout.lines.map((line, index) => {
|
|
59
|
+
const product = EXAMPLE_STORE_CATALOG[line.id];
|
|
60
|
+
const amount = product.price * line.quantity;
|
|
61
|
+
subtotal += amount;
|
|
62
|
+
return {
|
|
63
|
+
id: `li_${index + 1}`,
|
|
64
|
+
item: { id: line.id, title: product.title, price: product.price },
|
|
65
|
+
quantity: line.quantity,
|
|
66
|
+
totals: [
|
|
67
|
+
{ type: 'subtotal', amount },
|
|
68
|
+
{ type: 'total', amount },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const tax = Math.round(subtotal * 0.08);
|
|
73
|
+
const messages = [];
|
|
74
|
+
if (['incomplete', 'ready_for_complete'].includes(checkout.status)) {
|
|
75
|
+
checkout.status = checkout.buyer?.email
|
|
76
|
+
? 'ready_for_complete'
|
|
77
|
+
: 'incomplete';
|
|
78
|
+
if (!checkout.buyer?.email) {
|
|
79
|
+
messages.push({
|
|
80
|
+
type: 'error',
|
|
81
|
+
code: 'missing',
|
|
82
|
+
path: '$.buyer.email',
|
|
83
|
+
content: 'Buyer email is required',
|
|
84
|
+
severity: 'recoverable',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
ucp: meta(),
|
|
90
|
+
id: checkout.id,
|
|
91
|
+
status: checkout.status,
|
|
92
|
+
currency: 'USD',
|
|
93
|
+
line_items: lines,
|
|
94
|
+
totals: [
|
|
95
|
+
{ type: 'subtotal', amount: subtotal },
|
|
96
|
+
{ type: 'tax', amount: tax },
|
|
97
|
+
{ type: 'total', amount: subtotal + tax },
|
|
98
|
+
],
|
|
99
|
+
links: [{ type: 'terms_of_service', url: `${base}/terms` }],
|
|
100
|
+
expires_at: checkout.expiresAt,
|
|
101
|
+
...(messages.length ? { messages } : {}),
|
|
102
|
+
...(checkout.buyer?.email ? { buyer: checkout.buyer } : {}),
|
|
103
|
+
...(checkout.order ? { order: checkout.order } : {}),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
const linesOf = (body) => {
|
|
107
|
+
const lines = [];
|
|
108
|
+
for (const line of body.line_items ?? []) {
|
|
109
|
+
const id = line?.item?.id;
|
|
110
|
+
if (!(id in EXAMPLE_STORE_CATALOG)) {
|
|
111
|
+
return `No product ${id}`;
|
|
112
|
+
}
|
|
113
|
+
lines.push({ id, quantity: Number(line.quantity ?? 1) });
|
|
114
|
+
}
|
|
115
|
+
return lines;
|
|
116
|
+
};
|
|
117
|
+
const handle = (method, path, body) => {
|
|
118
|
+
if (method === 'POST' && path === '/checkout-sessions') {
|
|
119
|
+
const lines = linesOf(body);
|
|
120
|
+
if (typeof lines === 'string') {
|
|
121
|
+
return { status: 404, body: { messages: [{ content: lines }] } };
|
|
122
|
+
}
|
|
123
|
+
const checkout = {
|
|
124
|
+
id: `chk_${crypto.randomUUID().slice(0, 12)}`,
|
|
125
|
+
status: 'incomplete',
|
|
126
|
+
lines,
|
|
127
|
+
buyer: body.buyer ?? {},
|
|
128
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
129
|
+
};
|
|
130
|
+
checkouts.set(checkout.id, checkout);
|
|
131
|
+
return { status: 201, body: render(checkout) };
|
|
132
|
+
}
|
|
133
|
+
const [, , id, action] = path.split('/');
|
|
134
|
+
const checkout = checkouts.get(id);
|
|
135
|
+
if (!checkout) {
|
|
136
|
+
return { status: 404, body: { messages: [{ content: 'No checkout' }] } };
|
|
137
|
+
}
|
|
138
|
+
if (method === 'PUT' && !action) {
|
|
139
|
+
if (!['incomplete', 'ready_for_complete'].includes(checkout.status)) {
|
|
140
|
+
return { status: 409, body: render(checkout) };
|
|
141
|
+
}
|
|
142
|
+
if (body.buyer) {
|
|
143
|
+
checkout.buyer = body.buyer;
|
|
144
|
+
}
|
|
145
|
+
if (body.line_items) {
|
|
146
|
+
const lines = linesOf(body);
|
|
147
|
+
if (typeof lines === 'string') {
|
|
148
|
+
return { status: 404, body: { messages: [{ content: lines }] } };
|
|
149
|
+
}
|
|
150
|
+
checkout.lines = lines;
|
|
151
|
+
}
|
|
152
|
+
return { status: 200, body: render(checkout) };
|
|
153
|
+
}
|
|
154
|
+
if (method === 'POST' && action === 'complete') {
|
|
155
|
+
if (checkout.status !== 'ready_for_complete') {
|
|
156
|
+
return { status: 200, body: render(checkout) };
|
|
157
|
+
}
|
|
158
|
+
const [instrument] = body.payment?.instruments ?? [{}];
|
|
159
|
+
const token = String(instrument?.credential?.token ?? '');
|
|
160
|
+
if (instrument?.handler_id !== EXAMPLE_STORE_HANDLER_ID ||
|
|
161
|
+
!token.startsWith(TEST_TOKEN_PREFIX)) {
|
|
162
|
+
return {
|
|
163
|
+
status: 402,
|
|
164
|
+
body: {
|
|
165
|
+
ucp: { version: UCP_VERSION, status: 'error' },
|
|
166
|
+
messages: [
|
|
167
|
+
{
|
|
168
|
+
type: 'error',
|
|
169
|
+
code: 'payment_declined',
|
|
170
|
+
content: 'The payment was declined',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const order = {
|
|
177
|
+
id: `ord_${crypto.randomUUID().slice(0, 10)}`,
|
|
178
|
+
permalink_url: `${base}/orders/${checkout.id}`,
|
|
179
|
+
};
|
|
180
|
+
checkout.status = 'completed';
|
|
181
|
+
checkout.order = order;
|
|
182
|
+
orders.push({ order: order.id, checkout: checkout.id });
|
|
183
|
+
return { status: 200, body: render(checkout) };
|
|
184
|
+
}
|
|
185
|
+
if (method === 'POST' && action === 'cancel') {
|
|
186
|
+
if (checkout.status !== 'completed') {
|
|
187
|
+
checkout.status = 'canceled';
|
|
188
|
+
}
|
|
189
|
+
return { status: 200, body: render(checkout) };
|
|
190
|
+
}
|
|
191
|
+
if (method === 'GET' && !action) {
|
|
192
|
+
return { status: 200, body: render(checkout) };
|
|
193
|
+
}
|
|
194
|
+
return { status: 405, body: {} };
|
|
195
|
+
};
|
|
196
|
+
const fetchImpl = (async (input, init) => {
|
|
197
|
+
const url = new URL(String(input));
|
|
198
|
+
if (url.pathname === '/.well-known/ucp') {
|
|
199
|
+
return json({
|
|
200
|
+
ucp: {
|
|
201
|
+
version: UCP_VERSION,
|
|
202
|
+
services: {
|
|
203
|
+
[SHOPPING_SERVICE]: [
|
|
204
|
+
{
|
|
205
|
+
version: UCP_VERSION,
|
|
206
|
+
transport: 'rest',
|
|
207
|
+
endpoint: `${base}/ucp/v1`,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
capabilities: { [CHECKOUT_CAPABILITY]: [{ version: UCP_VERSION }] },
|
|
212
|
+
payment_handlers: meta().payment_handlers,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const headers = new Headers(init?.headers);
|
|
217
|
+
const agent = headers.get('UCP-Agent') ?? '';
|
|
218
|
+
if (!/^profile="https:\/\/[^"]+"$/.test(agent)) {
|
|
219
|
+
return refuse(400, 'invalid_ucp_agent', 'UCP-Agent names its profile over https');
|
|
220
|
+
}
|
|
221
|
+
if (!headers.get('Request-Id')) {
|
|
222
|
+
return refuse(400, 'missing_request_id', 'Request-Id is required');
|
|
223
|
+
}
|
|
224
|
+
const method = (init?.method ?? 'GET').toUpperCase();
|
|
225
|
+
const path = url.pathname.replace(/^\/ucp\/v1/, '');
|
|
226
|
+
const raw = typeof init?.body === 'string' ? init.body : '';
|
|
227
|
+
const body = raw ? JSON.parse(raw) : {};
|
|
228
|
+
if (path === '/catalog/search') {
|
|
229
|
+
const query = String(body.query ?? '').toLowerCase();
|
|
230
|
+
return json({
|
|
231
|
+
ucp: { version: UCP_VERSION },
|
|
232
|
+
products: Object.entries(EXAMPLE_STORE_CATALOG)
|
|
233
|
+
.filter(([, product]) => product.title.toLowerCase().includes(query))
|
|
234
|
+
.map(([id, product]) => ({ id, title: product.title })),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
if (method === 'GET') {
|
|
238
|
+
const answer = handle(method, path, body);
|
|
239
|
+
return json(answer.body, answer.status);
|
|
240
|
+
}
|
|
241
|
+
const key = headers.get('Idempotency-Key');
|
|
242
|
+
if (!key) {
|
|
243
|
+
return refuse(400, 'missing_idempotency_key', 'Idempotency-Key is required');
|
|
244
|
+
}
|
|
245
|
+
const seen = await fingerprint(url.pathname, raw);
|
|
246
|
+
const replied = replies.get(key);
|
|
247
|
+
if (replied) {
|
|
248
|
+
return replied.seen === seen
|
|
249
|
+
? json(replied.body, replied.status)
|
|
250
|
+
: refuse(409, 'idempotency_conflict', 'the key was used for another request');
|
|
251
|
+
}
|
|
252
|
+
const answer = handle(method, path, body);
|
|
253
|
+
replies.set(key, { seen, ...answer });
|
|
254
|
+
return json(answer.body, answer.status);
|
|
255
|
+
});
|
|
256
|
+
return { fetch: fetchImpl, orders };
|
|
257
|
+
}
|
|
@@ -324,6 +324,7 @@ export const WebBotAuthPlugin = definePlugin({
|
|
|
324
324
|
status: 'implemented',
|
|
325
325
|
specification: 'https://datatracker.ietf.org/doc/draft-ietf-webbotauth-httpsig-protocol/',
|
|
326
326
|
plugin: WEB_BOT_AUTH_PLUGIN,
|
|
327
|
+
catalogue: 'web-bot-auth',
|
|
327
328
|
points: WEB_BOT_AUTH_POINTS.map(point => point.id),
|
|
328
329
|
}, { id: 'web_bot_auth', order: DEFAULT_ORDER }),
|
|
329
330
|
contribution(WebBotAuthDirectoryDiscoveries, directoryDiscovery(), {
|