@xopcai/xopc 0.0.421 → 0.0.422
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/browser-ext/dist/background.js +184 -2
- package/dist/browser-ext/dist/sidepanel.js +9 -0
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-CKrylgib.js → activity-store-CvJ9rdwP.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Dv5Rda4y.js → agent-avatar-dicebear-CFP6g4w7.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-display-B1ycycx2.js → agent-avatar-display-BHBe88Gu.js} +2 -2
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-D285zy0S.js → agent-defaults-settings-panel-DlGRuKMv.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-D3yCqVan.js → agent-plugin-dialog-C_Dm-u6_.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DyAuR3rG.js → agents-BdVJHOiK.js} +3 -3
- package/dist/gateway/static/root/assets/{agents-admin-api-CASGzvkW.js → agents-admin-api-Do1Pza0R.js} +1 -1
- package/dist/gateway/static/root/assets/{api-Uk05lF9i.js → api-BQv5dLcH.js} +1 -1
- package/dist/gateway/static/root/assets/{api-C4LxuNzq.js → api-ONg37_fH.js} +1 -1
- package/dist/gateway/static/root/assets/app-link-1j40sJ-z.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CDnZNKHt.js → app-management-settings-panel-B4Wqhf4J.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CmCF7PZ5.js → appearance-settings-Dm1kRzKL.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BR9qoA2P.js → apps-page-D1MMk1P7.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-nuHYYVty.js → archive-plugin-p_yLkyaC.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-api-DtJfd1QM.js → automation-api-DQaWBR03.js} +1 -1
- package/dist/gateway/static/root/assets/automation-result-state-CO_243JM.js +1 -0
- package/dist/gateway/static/root/assets/automation-task-card-D2p9qlYE.js +3 -0
- package/dist/gateway/static/root/assets/{automations-page-DM4IdUJh.js → automations-page-Ba64H37d.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BkzC3oIM.js +9 -0
- package/dist/gateway/static/root/assets/{binary-plugins-CHCAr8jt.js → binary-plugins-B-6kyqtt.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-B0v8KxsO.js → block-editor-B5ysJ96n.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-DjUW7PEB.js → browser-automation-inputs-C2A3NIt3.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-Cf8AoQz1.js → browser-automations-page-CnbXFw1_.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-control-api-C6NZgaA1.js → browser-control-api-Aj_seKE9.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-DV2FNEDf.js → browser-settings-page-C7fxPq_z.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-CwdjWz2r.js → capabilities-BgtCwC_E.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-D0-HCZ5J.js → capabilities-DOTgaSgA.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-DwDkritH.js → capabilities-page-m3b9sLFG.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-mme16Fbp.js → capabilities-settings-panel-pYWZtPeU.js} +3 -3
- package/dist/gateway/static/root/assets/{channels-lKFLu8p-.js → channels-DGpRM6pj.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-l80cvSac.js → chat-agents-api-EvJ_Wvuc.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-chart-block-BwP5QXEk.js → chat-chart-block-BQDbHLcw.js} +1 -1
- package/dist/gateway/static/root/assets/chat-page-GF9NIuZN.js +72 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-D2bV9YUL.js → chat-terminal-dock-DHFTGoEp.js} +2 -2
- package/dist/gateway/static/root/assets/{command-palette-api-CtapG8ck.js → command-palette-api-DPA_yEuy.js} +1 -1
- package/dist/gateway/static/root/assets/{composer-payload-handoff-DYuhKZRC.js → composer-payload-handoff-BTjauSNp.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-DZtMuihL.js → computer-settings-page-BFSSQfcx.js} +1 -1
- package/dist/gateway/static/root/assets/{connected-providers-grid-BQH5k3iC.js → connected-providers-grid-DM2glQN3.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-DrvFdP3G.js → connector-service-page-CFvg58qa.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-api-BzkgVBYC.js → connectors-api-CbC6Yht5.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-BtRNVCFy.js → connectors-page-D7pipFHA.js} +1 -1
- package/dist/gateway/static/root/assets/date-picker-Dd3MbG0x.js +1 -0
- package/dist/gateway/static/root/assets/{dependency-picker-Dh8hPmsT.js → dependency-picker-DcsHg2ml.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-CFz8bqyI.js → desktop-pet-DldzQT2h.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-COaboNn1.js → desktop-pet-settings-CYdI4KxB.js} +1 -1
- package/dist/gateway/static/root/assets/{dev-view-store-D8XYHVqG.js → dev-view-store-JiJv585f.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-D8cLc41G.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Cjv7rdo3.js → directory-picker-path-field-8WOXv8zw.js} +1 -1
- package/dist/gateway/static/root/assets/{discussion-annotation-sync-YvlVw17j.js → discussion-annotation-sync-B-grSe0w.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CXx3m5-B.js → extension-debug-page-EEQkTNEu.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-CnOdNwbo.js → extension-iframe-host-vlZVeMCY.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-marketplace-api-BABmUY2s.js → extension-marketplace-api-_o2In_al.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C1nGr4DD.js → extension-page-CPrT111s.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-BA0Y2Rvs.js → extension-provider-B2Mf3FBu.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-QIPI8e7k.js → extension-settings-page-CznJEgWV.js} +1 -1
- package/dist/gateway/static/root/assets/{file-preview-surface-BsB7bnFH.js → file-preview-surface-7h8_fZul.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bt8w0Iio.js → gateway-config-swr-DbbNEOKj.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-CyCCNUxm.js → gateway-realtime-D20ZrAuM.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-C6sMzYDT.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-restart-Ty4AzDxa.js → gateway-restart-5ut-lhJm.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BvKq7vuz.js → gateway-settings-BCf_cibp.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-N3k-C7c2.js → gateway-startup-retry-DlHL0DpH.js} +1 -1
- package/dist/gateway/static/root/assets/{global-defaults-api-D8EB2vfY.js → global-defaults-api-CmODomcV.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-eRuAzi58.js → home-page-uz22TRB-.js} +2 -2
- package/dist/gateway/static/root/assets/{image-generation-api-C99Fsofm.js → image-generation-api-dzPnpUFu.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-Cp9RquVb.js → imports-page-ChBoNMyE.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSIg7V1A.js +11 -0
- package/dist/gateway/static/root/assets/index-Dv8mUlNN.css +1 -0
- package/dist/gateway/static/root/assets/{install-connector-dialog-CsoAidXp.js → install-connector-dialog-CNcdQDX0.js} +1 -1
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DFhOP2aD.js → keyboard-shortcuts-settings-CRbYq2_g.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-DbtU_A-l.js → local-app-workbench-page-04cX5TJu.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-rjhHfLpe.js → local-apps-page-fWM0FEhN.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-DHM4vWDO.js → local-session-drafts-ClrSe5mH.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-5Hf8J8-_.js → locale-store-VNjH18Dj.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-Dx_JIOpF.js → logs-page-6GanGLrm.js} +1 -1
- package/dist/gateway/static/root/assets/management-settings-BxD_lc4E.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-view-pzSRu7av.js → markdown-view-DLCBYWtx.js} +2 -2
- package/dist/gateway/static/root/assets/{media-plugins-3cJuHbO7.js → media-plugins-BvtNwKFb.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-uXxg5p54.js → messages-Btr7C0hF.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-ksLo-5B_.js → model-selector-DYUnp917.js} +1 -1
- package/dist/gateway/static/root/assets/{models-hub-cache-DhZng2nF.js → models-hub-cache-_qE7L-_H.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-B6RbjwJq.js → note-detail-panel-bjRZesmS.js} +3 -3
- package/dist/gateway/static/root/assets/note-image-lightbox-D7Jqb75C.js +1 -0
- package/dist/gateway/static/root/assets/{notes-api-DocbsBbT.js → notes-api-Bxa11X3a.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-B7889s6-.js → notes-page-D2QpJ3WZ.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-api-CCFIPbA0.js → oauth-api-DIE4UpP7.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-provider-connect-GxkF0vWJ.js → oauth-provider-connect-B95tiddN.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-CvWLVeRA.js → page-context-capture-button-C-QbbwmN.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-draft-CMRDNv4d.js → page-context-draft-BHmOfksL.js} +1 -1
- package/dist/gateway/static/root/assets/page-header-store-bPV8_tKL.js +1 -0
- package/dist/gateway/static/root/assets/{personal-avatar-CqNJ48Ge.js → personal-avatar-nXrmMfrV.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-BCwlYT3F.js +1 -0
- package/dist/gateway/static/root/assets/{personal-settings-panel-DAcl8DU4.js → personal-settings-panel-vkYmXK8h.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-dependencies-card-DLWblGj7.js → phone-dependencies-card-CZDAxTWv.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-model-picker-ugrT9GJu.js → phone-model-picker-4Snfs8Hq.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-settings-page-DIIt7JOE.js → phone-settings-page-D8HperfA.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-workspace-store-mawKT5kV.js → phone-workspace-store-Di9R9Ux5.js} +1 -1
- package/dist/gateway/static/root/assets/{plugin-details-dialog-BdueMT3q.js → plugin-details-dialog-DJsDu-_8.js} +1 -1
- package/dist/gateway/static/root/assets/{preference-select-fields-CmLZNwbm.js → preference-select-fields-C6abaM3k.js} +1 -1
- package/dist/gateway/static/root/assets/{product-automation-feedback-DYwhL37Y.js → product-automation-feedback-C74s9Mjz.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-DRyD9_Xq.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-B5G4X71t.js → product-open-page-AbDP5w1c.js} +1 -1
- package/dist/gateway/static/root/assets/product-preview-content-CktjvFbc.js +1 -0
- package/dist/gateway/static/root/assets/product-preview-modal-CzsqLwB4.js +2 -0
- package/dist/gateway/static/root/assets/{project-detail-page-CRJb69vP.js → project-detail-page-Cf3tggGP.js} +2 -2
- package/dist/gateway/static/root/assets/{project-understanding-D3w8cZEm.js → project-understanding-jmhFbHch.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-CkifzKjz.js → projects-page-jMgZiFnO.js} +1 -1
- package/dist/gateway/static/root/assets/{providers-api-CKFg2PZQ.js → providers-api-CJBYFKAK.js} +1 -1
- package/dist/gateway/static/root/assets/{recording-upload-ZkmRf0GR.js → recording-upload-Dl9a5yOV.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-B1Mh3bNo.js → registry-api-BwkbPUYu.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-Dw5rG-aA.js → remote-access-hub-CBQ_bOy9.js} +1 -1
- package/dist/gateway/static/root/assets/{resource-unavailable-BUWsH3fp.js → resource-unavailable-35wBZxmz.js} +1 -1
- package/dist/gateway/static/root/assets/route-preload-BcA6SPeC.js +2 -0
- package/dist/gateway/static/root/assets/{runtime-tools-api-BXvn5ImA.js → runtime-tools-api-BF28JDAO.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BALcypHp.js → runtime-tools-settings-panel-Bq2xQ2qd.js} +1 -1
- package/dist/gateway/static/root/assets/{service-recommendation-list-CpVUnz89.js → service-recommendation-list-CdaS4MPF.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-VquhIBHI.js → session-api-zJXYlRPt.js} +1 -1
- package/dist/gateway/static/root/assets/session-manager-wWxpzSo0.js +23 -0
- package/dist/gateway/static/root/assets/{sessions-page-DlvPkE5Y.js → sessions-page-BkjD_dUD.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BUMCEtA_.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-section-c9_iZ-46.js → settings-advanced-section-C84IghdL.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-mode-store-Dem6-TBe.js → settings-mode-store-CtQBJvbO.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B_wNfFQe.js → settings-page-eEIz9InP.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-header-C8a_oOyw.js → settings-page-header-CsPAYlqV.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CTSRWljG.js → settings-page-layout-lPLeJQoV.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BNZoqF40.js → setup-status-panel-BSU5WpaE.js} +1 -1
- package/dist/gateway/static/root/assets/{share-link-dialog-C5JTTxsy.js → share-link-dialog-NaM6jvIz.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-D-KxzXgm.js → share-preview-page-CHjZn27s.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-CztKbxdd.js → shares-api-B9rVcV1y.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CzR21j6e.js → shares-settings-5sB15V5v.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-B7kWl6LK.js → skill-api-D6zVfEuK.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-list-api-Cn07P6qZ.js → skill-list-api-SLmTBYF5.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-Bhl0u16_.js → skill-reload-api-UQkq2orI.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BmkuxHZe.js → skills-page-Ca0pYZ4i.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-Ce6zIcki.js → system-settings-panel-BARWbjRN.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-CysJhG3B.js → task-detail-page-roqAsGue.js} +2 -2
- package/dist/gateway/static/root/assets/{task-result-evidence-CGZKMnNo.js → task-result-evidence-Bb1F7nAM.js} +1 -1
- package/dist/gateway/static/root/assets/{terminal-panel-store-YwLUkuyV.js → terminal-panel-store-DYDeXXAg.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-DOJ9tu-j.js → theme-store-DbStcH89.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-BplBpTdL.js → tracing-settings-page-YfkNCxbP.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-B5-Ypvqa.js → tunnel-api-BK_ivyIb.js} +1 -1
- package/dist/gateway/static/root/assets/{understanding-activity-store-CmwtE8Kd.js → understanding-activity-store-p7Z7oq8z.js} +1 -1
- package/dist/gateway/static/root/assets/{understanding-refresh-store-9apSmAVL.js → understanding-refresh-store-D-U6rcwZ.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-cPbirpXX.js → usage-page-URFSiswS.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-B3p2QWaF.js → use-autosave-B7cbumro.js} +1 -1
- package/dist/gateway/static/root/assets/{use-composer-attachments-D-AEYC85.js → use-composer-attachments-DAtiwLsE.js} +1 -1
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-CO4SC-LX.js → use-file-preview-expanded-B8erpWsX.js} +2 -2
- package/dist/gateway/static/root/assets/{use-personal-profile-DocmhC9I.js → use-personal-profile-DDW29nm-.js} +1 -1
- package/dist/gateway/static/root/assets/{use-workspace-tree-VraTGKx-.js → use-workspace-tree-DCoTf22f.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-DuRALzhe.js → user-model-page-DSV5NkNj.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-DbSQHZZW.js → voice-api-key-field-DzYdJZN4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-D9iNJSvm.js → voice-config-api-wotvT6Hw.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-input-shortcut-events-hso9_VvT.js → voice-input-shortcut-events-BI6PuiIB.js} +1 -1
- package/dist/gateway/static/root/assets/voice-transcribe-api-DAK9eLRr.js +1 -0
- package/dist/gateway/static/root/assets/{work-discovery-overlay-fsL9RJqB.js → work-discovery-overlay-BzmB9Rub.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-page-DAQPKVDs.js → work-discovery-page-CmGjxUIT.js} +1 -1
- package/dist/gateway/static/root/assets/{working-directory-picker-modal-Ta73QrNw.js → working-directory-picker-modal-B-EHm52-.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-api-CgrcNyBc.js → workspace-api-uhiEMPpK.js} +2 -2
- package/dist/gateway/static/root/assets/workspace-editor-agent-store-DrPVyikA.js +1 -0
- package/dist/gateway/static/root/assets/{workspace-file-preview-dialog-B7uBuPtA.js → workspace-file-preview-dialog-BDm06MZm.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-open-location-menu-Bi2vRvaQ.js → workspace-open-location-menu-Bv83uoqu.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CCOOBG-7.js → xopc-cloud-account-card-Cd5GdpGL.js} +1 -1
- package/dist/gateway/static/root/index.html +84 -84
- package/dist/gateway/static/root/notification-sw.js +21 -7
- package/dist/package.js +1 -1
- package/dist/packages/endpoint-tools-protocol/src/context.js +115 -0
- package/dist/packages/endpoint-tools-protocol/src/index.js +4 -0
- package/dist/packages/endpoint-tools-protocol/src/location-sharing.js +51 -0
- package/dist/packages/gateway-contract/src/gateway-scopes.js +5 -1
- package/dist/packages/gateway-contract/src/index.js +2 -0
- package/dist/packages/gateway-contract/src/phone-workspace.js +6 -0
- package/dist/packages/gateway-contract/src/product-delivery.js +9 -3
- package/dist/src/agent/embedded/external-tool-discovery.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +2 -2
- package/dist/src/agent/embedded/run-turn.js +72 -9
- package/dist/src/agent/embedded/session-runner.js +2 -1
- package/dist/src/agent/embedded/types.d.ts +1 -1
- package/dist/src/agent/prompt/system-prompt.js +1 -0
- package/dist/src/agent/service/direct-turn-helpers.d.ts +0 -7
- package/dist/src/agent/service/direct-turn-helpers.js +3 -2
- package/dist/src/agent/service/process-direct-streaming.js +2 -1
- package/dist/src/agent/source-context/device-environment.d.ts +3 -0
- package/dist/src/agent/source-context/device-environment.js +38 -0
- package/dist/src/agent/source-context/injector.js +2 -1
- package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
- package/dist/src/agent/tool-manuals/phone.js +1 -1
- package/dist/src/agent/tools/automation-tool.js +3 -2
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/personal-read-tool.js +1 -1
- package/dist/src/agent/tools/phone-use-tool.js +46 -2
- package/dist/src/agent/tools/xopc-use-tool.js +1 -0
- package/dist/src/computer/runtime.d.ts +1 -1
- package/dist/src/config/schema.d.ts +1 -0
- package/dist/src/endpoint-tools/context-store.d.ts +29 -0
- package/dist/src/endpoint-tools/context-store.js +138 -0
- package/dist/src/endpoint-tools/location-sharing.d.ts +42 -0
- package/dist/src/endpoint-tools/location-sharing.js +203 -0
- package/dist/src/endpoint-tools/runtime.d.ts +4 -0
- package/dist/src/endpoint-tools/runtime.js +8 -0
- package/dist/src/endpoint-tools/turn-context.d.ts +2 -1
- package/dist/src/endpoint-tools/turn-context.js +4 -2
- package/dist/src/gateway/hono/middleware/auth.js +1 -1
- package/dist/src/gateway/hono/routes/devices.js +1 -0
- package/dist/src/gateway/hono/routes/endpoint-compatibility.js +4 -0
- package/dist/src/gateway/hono/routes/endpoint-environment.d.ts +3 -0
- package/dist/src/gateway/hono/routes/endpoint-environment.js +98 -0
- package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +16 -0
- package/dist/src/gateway/hono/routes/location-sharing.d.ts +3 -0
- package/dist/src/gateway/hono/routes/location-sharing.js +164 -0
- package/dist/src/gateway/hono/routes/phone.js +246 -34
- package/dist/src/gateway/hono/routes/session-command-handler.js +1 -1
- package/dist/src/gateway/hono/routes/session-input-handler.js +2 -1
- package/dist/src/gateway/hono/routes/tunnel.js +10 -0
- package/dist/src/gateway/security/gateway-scopes.js +6 -0
- package/dist/src/gateway/security/phone-access.d.ts +4 -0
- package/dist/src/gateway/security/phone-access.js +20 -0
- package/dist/src/gateway/service/agent-runner.js +3 -1
- package/dist/src/gateway/service/config-coordinator.d.ts +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +2 -1
- package/dist/src/gateway/service/session-input-coordinator.d.ts +3 -0
- package/dist/src/gateway/service/session-input-coordinator.js +50 -2
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +8 -0
- package/dist/src/gateway/session-reset-service.js +4 -0
- package/dist/src/notifications/planner.js +6 -6
- package/dist/src/phone/assistance-service.d.ts +6 -0
- package/dist/src/phone/assistance-service.js +32 -0
- package/dist/src/phone/assistance.d.ts +40 -0
- package/dist/src/phone/assistance.js +273 -0
- package/dist/src/phone/cloud-phones.d.ts +5 -0
- package/dist/src/phone/cloud-phones.js +14 -0
- package/dist/src/phone/contracts.d.ts +7 -1
- package/dist/src/phone/contracts.js +5 -0
- package/dist/src/phone/environment-adapter.d.ts +3 -0
- package/dist/src/phone/environment-adapter.js +11 -0
- package/dist/src/phone/location-baseline-store.d.ts +9 -0
- package/dist/src/phone/location-baseline-store.js +61 -0
- package/dist/src/phone/location-environment.d.ts +61 -0
- package/dist/src/phone/location-environment.js +157 -0
- package/dist/src/phone/model-adapter.js +16 -5
- package/dist/src/phone/runtime.d.ts +17 -2
- package/dist/src/phone/runtime.js +203 -20
- package/dist/src/phone/stream-gateway.d.ts +5 -0
- package/dist/src/phone/stream-gateway.js +85 -51
- package/dist/src/phone/wuying-location.d.ts +18 -0
- package/dist/src/phone/wuying-location.js +86 -0
- package/dist/src/storage/sqlite/device-access-repository.d.ts +4 -0
- package/dist/src/storage/sqlite/device-access-repository.js +18 -1
- package/dist/src/tunnel/broker-client.d.ts +8 -0
- package/dist/src/tunnel/broker-client.js +18 -1
- package/dist/src/voice/realtime/runtime.js +1 -1
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/app-link-CytZ5zkp.js +0 -1
- package/dist/gateway/static/root/assets/automation-display-BlGt8MAC.js +0 -2
- package/dist/gateway/static/root/assets/automation-task-card-Cp3RIw5d.js +0 -2
- package/dist/gateway/static/root/assets/automations-workspace-CXLu04I-.js +0 -9
- package/dist/gateway/static/root/assets/chat-page-CT3K7lFT.js +0 -71
- package/dist/gateway/static/root/assets/date-picker-ByTasrfl.js +0 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-BKMpLdIA.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-0gmDfPeP.js +0 -1
- package/dist/gateway/static/root/assets/index-6vVrWfwM.js +0 -11
- package/dist/gateway/static/root/assets/index-DDGK6klk.css +0 -1
- package/dist/gateway/static/root/assets/management-settings-CuSzixOw.js +0 -1
- package/dist/gateway/static/root/assets/note-image-lightbox-BCjvBqwy.js +0 -1
- package/dist/gateway/static/root/assets/page-header-store-reh1es-3.js +0 -1
- package/dist/gateway/static/root/assets/personal-page-D95FHaw9.js +0 -1
- package/dist/gateway/static/root/assets/product-delivery-BU_H84jL.js +0 -1
- package/dist/gateway/static/root/assets/product-preview-content-BUheyLqt.js +0 -1
- package/dist/gateway/static/root/assets/product-preview-modal-Cj_Zxu4G.js +0 -2
- package/dist/gateway/static/root/assets/route-preload-CRKbVwQ1.js +0 -2
- package/dist/gateway/static/root/assets/session-manager-DNMosCtW.js +0 -23
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ckc1aCLG.js +0 -1
- package/dist/gateway/static/root/assets/voice-transcribe-api-BqIs0jgv.js +0 -1
- package/dist/gateway/static/root/assets/workspace-editor-agent-store-Dw6wtadX.js +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type EndpointContextView } from '@xopcai/endpoint-tools-protocol';
|
|
2
|
+
import type { EndpointRegistry } from './registry.js';
|
|
3
|
+
export declare class EndpointContextError extends Error {
|
|
4
|
+
readonly code: 'CONTEXT_INVALID' | 'CONTEXT_CONFLICT' | 'SOURCE_UNAVAILABLE';
|
|
5
|
+
constructor(code: 'CONTEXT_INVALID' | 'CONTEXT_CONFLICT' | 'SOURCE_UNAVAILABLE', message: string);
|
|
6
|
+
}
|
|
7
|
+
/** Bounded, process-local observations. Heartbeats never refresh field timestamps. */
|
|
8
|
+
export declare class EndpointContextStore {
|
|
9
|
+
private readonly registry;
|
|
10
|
+
private readonly now;
|
|
11
|
+
private readonly snapshots;
|
|
12
|
+
private readonly unsubscribe;
|
|
13
|
+
constructor(registry: EndpointRegistry, now?: () => number);
|
|
14
|
+
private require;
|
|
15
|
+
connection(endpointId: string): {
|
|
16
|
+
connectionEpoch: string;
|
|
17
|
+
revision: number;
|
|
18
|
+
reportSequence: number;
|
|
19
|
+
heartbeatSequence: number;
|
|
20
|
+
};
|
|
21
|
+
report(endpointId: string, input: unknown): {
|
|
22
|
+
revision: number;
|
|
23
|
+
};
|
|
24
|
+
heartbeat(endpointId: string, input: unknown): {
|
|
25
|
+
revision: number;
|
|
26
|
+
};
|
|
27
|
+
view(endpointId: string): EndpointContextView | undefined;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ENDPOINT_CONTEXT_OFFLINE_MS, endpointContextHeartbeatSchema, endpointContextReportSchema } from "../../packages/endpoint-tools-protocol/src/context.js";
|
|
2
|
+
import { canonicalJson, init_src } from "../../packages/endpoint-tools-protocol/src/index.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
//#region src/endpoint-tools/context-store.ts
|
|
5
|
+
init_src();
|
|
6
|
+
var EndpointContextError = class extends Error {
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/** Bounded, process-local observations. Heartbeats never refresh field timestamps. */
|
|
13
|
+
var EndpointContextStore = class {
|
|
14
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
15
|
+
unsubscribe;
|
|
16
|
+
constructor(registry, now = Date.now) {
|
|
17
|
+
this.registry = registry;
|
|
18
|
+
this.now = now;
|
|
19
|
+
this.unsubscribe = registry.onChange((endpointId, reason) => {
|
|
20
|
+
this.snapshots.delete(endpointId);
|
|
21
|
+
const endpoint = registry.get(endpointId);
|
|
22
|
+
if (reason === "authorization" || !endpoint) return;
|
|
23
|
+
if (this.snapshots.size >= 256) return;
|
|
24
|
+
this.snapshots.set(endpointId, {
|
|
25
|
+
epoch: endpoint.connectionId,
|
|
26
|
+
revision: 0,
|
|
27
|
+
sequence: 0,
|
|
28
|
+
heartbeatSequence: 0,
|
|
29
|
+
lastSeenAt: 0,
|
|
30
|
+
availability: "background",
|
|
31
|
+
fields: /* @__PURE__ */ new Map(),
|
|
32
|
+
receipts: /* @__PURE__ */ new Map()
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
require(endpointId) {
|
|
37
|
+
const state = this.snapshots.get(endpointId);
|
|
38
|
+
if (!state || this.registry.get(endpointId)?.connectionId !== state.epoch) throw new EndpointContextError("SOURCE_UNAVAILABLE", "Context source is unavailable");
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
connection(endpointId) {
|
|
42
|
+
const state = this.require(endpointId);
|
|
43
|
+
return {
|
|
44
|
+
connectionEpoch: state.epoch,
|
|
45
|
+
revision: state.revision,
|
|
46
|
+
reportSequence: state.sequence,
|
|
47
|
+
heartbeatSequence: state.heartbeatSequence
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
report(endpointId, input) {
|
|
51
|
+
const parsed = endpointContextReportSchema.safeParse(input);
|
|
52
|
+
if (!parsed.success) throw new EndpointContextError("CONTEXT_INVALID", "Invalid endpoint context report");
|
|
53
|
+
const report = parsed.data;
|
|
54
|
+
const state = this.require(endpointId);
|
|
55
|
+
if (report.connectionEpoch !== state.epoch) throw new EndpointContextError("SOURCE_UNAVAILABLE", "Context connection changed");
|
|
56
|
+
const digest = createHash("sha256").update(canonicalJson(report)).digest("hex");
|
|
57
|
+
const receipt = state.receipts.get(report.reportId);
|
|
58
|
+
if (receipt) {
|
|
59
|
+
if (receipt.digest !== digest) throw new EndpointContextError("CONTEXT_CONFLICT", "Report identity was reused");
|
|
60
|
+
return { revision: receipt.revision };
|
|
61
|
+
}
|
|
62
|
+
if (report.sequence <= state.sequence || report.mode === "patch" && report.baseRevision !== state.revision) throw new EndpointContextError("CONTEXT_CONFLICT", "Context baseline or sequence changed; send a full report");
|
|
63
|
+
const now = this.now();
|
|
64
|
+
if (report.capturedAt > now + 6e4 || report.capturedAt < now - 3e4) throw new EndpointContextError("CONTEXT_INVALID", "Context sample is stale or has an invalid clock");
|
|
65
|
+
const fields = report.mode === "full" ? /* @__PURE__ */ new Map() : new Map(state.fields);
|
|
66
|
+
for (const key of report.cleared) fields.delete(key);
|
|
67
|
+
for (const [key, value] of Object.entries(report.values)) {
|
|
68
|
+
if (value === void 0) continue;
|
|
69
|
+
const field = key;
|
|
70
|
+
const prior = state.fields.get(field);
|
|
71
|
+
if (prior && report.capturedAt < prior.capturedAt) throw new EndpointContextError("CONTEXT_CONFLICT", "Context sample moved backwards");
|
|
72
|
+
if (prior && report.capturedAt === prior.capturedAt) {
|
|
73
|
+
if (canonicalJson(prior.value) !== canonicalJson(value)) throw new EndpointContextError("CONTEXT_CONFLICT", "Context sample identity changed");
|
|
74
|
+
fields.set(field, prior);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
fields.set(field, {
|
|
78
|
+
value: structuredClone(value),
|
|
79
|
+
capturedAt: report.capturedAt,
|
|
80
|
+
receivedAt: now,
|
|
81
|
+
validUntil: Math.min(report.capturedAt, now) + (field === "device.power" ? 3e4 : 3e5)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
state.fields = fields;
|
|
85
|
+
state.sequence = report.sequence;
|
|
86
|
+
state.revision += 1;
|
|
87
|
+
state.receipts.set(report.reportId, {
|
|
88
|
+
digest,
|
|
89
|
+
revision: state.revision
|
|
90
|
+
});
|
|
91
|
+
if (state.receipts.size > 64) state.receipts.delete(state.receipts.keys().next().value);
|
|
92
|
+
return { revision: state.revision };
|
|
93
|
+
}
|
|
94
|
+
heartbeat(endpointId, input) {
|
|
95
|
+
const parsed = endpointContextHeartbeatSchema.safeParse(input);
|
|
96
|
+
if (!parsed.success) throw new EndpointContextError("CONTEXT_INVALID", "Invalid context heartbeat");
|
|
97
|
+
const state = this.require(endpointId);
|
|
98
|
+
if (parsed.data.connectionEpoch !== state.epoch || parsed.data.sequence <= state.heartbeatSequence) throw new EndpointContextError("CONTEXT_CONFLICT", "Context heartbeat connection or sequence changed");
|
|
99
|
+
state.heartbeatSequence = parsed.data.sequence;
|
|
100
|
+
state.lastSeenAt = this.now();
|
|
101
|
+
state.availability = parsed.data.availability;
|
|
102
|
+
return { revision: state.revision };
|
|
103
|
+
}
|
|
104
|
+
view(endpointId) {
|
|
105
|
+
let state;
|
|
106
|
+
try {
|
|
107
|
+
state = this.require(endpointId);
|
|
108
|
+
} catch {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const now = this.now();
|
|
112
|
+
const availability = state.lastSeenAt === 0 || now - state.lastSeenAt >= 45e3 ? "offline" : this.registry.get(endpointId)?.kind !== "browser" && this.registry.get(endpointId)?.availability === "background" ? "background" : state.availability;
|
|
113
|
+
const fields = {};
|
|
114
|
+
for (const [key, reading] of state.fields) {
|
|
115
|
+
const status = availability !== "foreground" ? "unavailable" : reading.validUntil <= now ? "stale" : "fresh";
|
|
116
|
+
fields[key] = {
|
|
117
|
+
status,
|
|
118
|
+
capturedAt: reading.capturedAt,
|
|
119
|
+
validUntil: reading.validUntil,
|
|
120
|
+
...status === "fresh" ? { value: structuredClone(reading.value) } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
version: 1,
|
|
125
|
+
endpointId,
|
|
126
|
+
revision: state.revision,
|
|
127
|
+
availability,
|
|
128
|
+
sourceValidUntil: state.lastSeenAt + ENDPOINT_CONTEXT_OFFLINE_MS,
|
|
129
|
+
fields
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
close() {
|
|
133
|
+
this.unsubscribe();
|
|
134
|
+
this.snapshots.clear();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
//#endregion
|
|
138
|
+
export { EndpointContextError, EndpointContextStore };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type LocationSample, type LocationShareRequest, type LocationShareSummary } from '@xopcai/endpoint-tools-protocol';
|
|
2
|
+
import type { EndpointRegistry } from './registry.js';
|
|
3
|
+
import type { EndpointContextStore } from './context-store.js';
|
|
4
|
+
export declare class LocationSharingError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
constructor(code: string, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface LocationConsumer {
|
|
9
|
+
apply(input: {
|
|
10
|
+
shareId: string;
|
|
11
|
+
conversationId: string;
|
|
12
|
+
phoneId: string;
|
|
13
|
+
sample: LocationSample;
|
|
14
|
+
validUntil: number;
|
|
15
|
+
assertCurrent(): void;
|
|
16
|
+
}, signal: AbortSignal): Promise<void>;
|
|
17
|
+
clear(shareId: string, phoneId: string): Promise<boolean>;
|
|
18
|
+
state?(shareId: string, phoneId: string): LocationShareSummary['sync'];
|
|
19
|
+
}
|
|
20
|
+
/** Explicitly authorized coordinates remain transient and are never part of public context values. */
|
|
21
|
+
export declare class LocationSharingService {
|
|
22
|
+
private readonly registry;
|
|
23
|
+
private readonly contexts;
|
|
24
|
+
private readonly now;
|
|
25
|
+
private readonly shares;
|
|
26
|
+
private consumer?;
|
|
27
|
+
private readonly timer;
|
|
28
|
+
private readonly unsubscribe;
|
|
29
|
+
constructor(registry: EndpointRegistry, contexts: EndpointContextStore, now?: () => number);
|
|
30
|
+
setConsumer(consumer: LocationConsumer): void;
|
|
31
|
+
create(endpointId: string, input: LocationShareRequest): LocationShareSummary;
|
|
32
|
+
private usable;
|
|
33
|
+
sample(id: string, endpointId: string, input: unknown): Promise<LocationShareSummary>;
|
|
34
|
+
private require;
|
|
35
|
+
private summary;
|
|
36
|
+
list(endpointId: string, conversationId?: string): LocationShareSummary[];
|
|
37
|
+
revoke(id: string, endpointId: string): Promise<LocationShareSummary>;
|
|
38
|
+
private invalidate;
|
|
39
|
+
private clear;
|
|
40
|
+
sweep(): Promise<void>;
|
|
41
|
+
close(): Promise<void>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { locationShareRequestSchema, locationShareSampleSchema } from "../../packages/endpoint-tools-protocol/src/location-sharing.js";
|
|
2
|
+
import { init_src } from "../../packages/endpoint-tools-protocol/src/index.js";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
//#region src/endpoint-tools/location-sharing.ts
|
|
5
|
+
init_src();
|
|
6
|
+
var LocationSharingError = class extends Error {
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/** Explicitly authorized coordinates remain transient and are never part of public context values. */
|
|
13
|
+
var LocationSharingService = class {
|
|
14
|
+
shares = /* @__PURE__ */ new Map();
|
|
15
|
+
consumer;
|
|
16
|
+
timer;
|
|
17
|
+
unsubscribe;
|
|
18
|
+
constructor(registry, contexts, now = Date.now) {
|
|
19
|
+
this.registry = registry;
|
|
20
|
+
this.contexts = contexts;
|
|
21
|
+
this.now = now;
|
|
22
|
+
this.timer = setInterval(() => {
|
|
23
|
+
this.sweep();
|
|
24
|
+
}, 1e3);
|
|
25
|
+
this.timer.unref();
|
|
26
|
+
this.unsubscribe = registry.onChange((endpointId) => {
|
|
27
|
+
for (const share of this.shares.values()) if (share.summary.endpointId === endpointId) this.invalidate(share, "source_unavailable");
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
setConsumer(consumer) {
|
|
31
|
+
this.consumer = consumer;
|
|
32
|
+
}
|
|
33
|
+
create(endpointId, input) {
|
|
34
|
+
const request = locationShareRequestSchema.parse(input);
|
|
35
|
+
const endpoint = this.registry.get(endpointId);
|
|
36
|
+
if (!endpoint || this.contexts.view(endpointId)?.availability !== "foreground") throw new LocationSharingError("SOURCE_UNAVAILABLE", "Open the source device before sharing location");
|
|
37
|
+
for (const [id, share] of this.shares) if ([
|
|
38
|
+
"revoked",
|
|
39
|
+
"expired",
|
|
40
|
+
"source_unavailable"
|
|
41
|
+
].includes(share.summary.status) && share.summary.sync !== "cleanup_pending") this.shares.delete(id);
|
|
42
|
+
if (this.shares.size >= 64) throw new LocationSharingError("SHARING_CAPACITY", "Too many location shares");
|
|
43
|
+
if ([...this.shares.values()].some((share) => share.summary.endpointId === endpointId && share.summary.conversationId === request.conversationId && ![
|
|
44
|
+
"revoked",
|
|
45
|
+
"expired",
|
|
46
|
+
"source_unavailable"
|
|
47
|
+
].includes(share.summary.status))) throw new LocationSharingError("SHARING_EXISTS", "Stop the current location share first");
|
|
48
|
+
const summary = {
|
|
49
|
+
...request,
|
|
50
|
+
id: randomUUID(),
|
|
51
|
+
endpointId,
|
|
52
|
+
expiresAt: this.now() + request.durationMs,
|
|
53
|
+
status: "waiting",
|
|
54
|
+
sync: request.phoneId ? "pending" : "not_requested"
|
|
55
|
+
};
|
|
56
|
+
const share = {
|
|
57
|
+
summary,
|
|
58
|
+
epoch: endpoint.connectionId,
|
|
59
|
+
sequence: 0,
|
|
60
|
+
controller: new AbortController(),
|
|
61
|
+
operation: Promise.resolve()
|
|
62
|
+
};
|
|
63
|
+
this.shares.set(summary.id, share);
|
|
64
|
+
return { ...summary };
|
|
65
|
+
}
|
|
66
|
+
usable(share) {
|
|
67
|
+
return !share.controller.signal.aborted && share.summary.expiresAt > this.now() && this.registry.get(share.summary.endpointId)?.connectionId === share.epoch && this.contexts.view(share.summary.endpointId)?.availability === "foreground";
|
|
68
|
+
}
|
|
69
|
+
async sample(id, endpointId, input) {
|
|
70
|
+
const share = this.require(id, endpointId);
|
|
71
|
+
const parsed = locationShareSampleSchema.safeParse(input);
|
|
72
|
+
if (!parsed.success) throw new LocationSharingError("LOCATION_INVALID", "Invalid location sample");
|
|
73
|
+
if (!this.usable(share)) {
|
|
74
|
+
await this.invalidate(share, share.summary.expiresAt <= this.now() ? "expired" : "source_unavailable");
|
|
75
|
+
throw new LocationSharingError("GRANT_REVOKED", "Location sharing is no longer active");
|
|
76
|
+
}
|
|
77
|
+
const { sequence, sample } = parsed.data;
|
|
78
|
+
if (sequence <= share.sequence || share.summary.mode === "once" && share.sequence > 0) throw new LocationSharingError("LOCATION_REPLAY", "Location sample was already consumed");
|
|
79
|
+
if (sample.capturedAt < this.now() - 3e4 || sample.capturedAt > this.now() + 6e4 || share.sample && sample.capturedAt <= share.sample.capturedAt) throw new LocationSharingError("CONTEXT_STALE", "Acquire a fresh location sample");
|
|
80
|
+
if (share.summary.precision === "precise" && sample.precision !== "precise") throw new LocationSharingError("PRECISION_UNAVAILABLE", "Precise location is unavailable; choose approximate sharing");
|
|
81
|
+
const value = share.summary.precision === "approximate" || sample.precision === "approximate" ? {
|
|
82
|
+
...sample,
|
|
83
|
+
latitude: Math.round(sample.latitude * 50) / 50,
|
|
84
|
+
longitude: Math.round(sample.longitude * 50) / 50,
|
|
85
|
+
accuracyMeters: Math.max(3e3, sample.accuracyMeters),
|
|
86
|
+
precision: "approximate"
|
|
87
|
+
} : sample;
|
|
88
|
+
share.sequence = sequence;
|
|
89
|
+
share.sample = value;
|
|
90
|
+
share.sampleController?.abort();
|
|
91
|
+
share.sampleController = new AbortController();
|
|
92
|
+
const valueRef = randomUUID();
|
|
93
|
+
Object.assign(share.summary, {
|
|
94
|
+
status: "fresh",
|
|
95
|
+
capturedAt: value.capturedAt,
|
|
96
|
+
accuracyMeters: value.accuracyMeters,
|
|
97
|
+
validUntil: Math.min(value.capturedAt, this.now()) + 3e4,
|
|
98
|
+
valueRef
|
|
99
|
+
});
|
|
100
|
+
share.summary.validUntil = Math.min(share.summary.validUntil, share.summary.expiresAt);
|
|
101
|
+
if (share.summary.phoneId) {
|
|
102
|
+
share.summary.sync = "pending";
|
|
103
|
+
const consumer = this.consumer;
|
|
104
|
+
if (!consumer) throw new LocationSharingError("PHONE_ENV_UNSUPPORTED", "Phone location synchronization is unavailable");
|
|
105
|
+
const validUntil = share.summary.validUntil;
|
|
106
|
+
const signal = AbortSignal.any([
|
|
107
|
+
share.controller.signal,
|
|
108
|
+
share.sampleController.signal,
|
|
109
|
+
AbortSignal.timeout(Math.max(1, validUntil - this.now()))
|
|
110
|
+
]);
|
|
111
|
+
const assertCurrent = () => {
|
|
112
|
+
if (!this.usable(share) || share.summary.valueRef !== valueRef || validUntil <= this.now()) throw new LocationSharingError("CONTEXT_STALE", "Location authorization or sample expired");
|
|
113
|
+
};
|
|
114
|
+
share.operation = share.operation.catch(() => {}).then(async () => {
|
|
115
|
+
try {
|
|
116
|
+
assertCurrent();
|
|
117
|
+
await consumer.apply({
|
|
118
|
+
shareId: id,
|
|
119
|
+
conversationId: share.summary.conversationId,
|
|
120
|
+
phoneId: share.summary.phoneId,
|
|
121
|
+
sample: value,
|
|
122
|
+
validUntil,
|
|
123
|
+
assertCurrent
|
|
124
|
+
}, signal);
|
|
125
|
+
assertCurrent();
|
|
126
|
+
share.summary.sync = "applied";
|
|
127
|
+
} catch {
|
|
128
|
+
if (share.summary.valueRef === valueRef && share.summary.status === "fresh") share.summary.sync = "failed";
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
await share.operation;
|
|
132
|
+
}
|
|
133
|
+
return this.summary(share);
|
|
134
|
+
}
|
|
135
|
+
require(id, endpointId) {
|
|
136
|
+
const share = this.shares.get(id);
|
|
137
|
+
if (!share || share.summary.endpointId !== endpointId) throw new LocationSharingError("SHARE_NOT_FOUND", "Location share is unavailable");
|
|
138
|
+
return share;
|
|
139
|
+
}
|
|
140
|
+
summary(share) {
|
|
141
|
+
const result = { ...share.summary };
|
|
142
|
+
if (result.phoneId && ["applied", "cleanup_pending"].includes(result.sync)) result.sync = this.consumer?.state?.(result.id, result.phoneId) ?? result.sync;
|
|
143
|
+
if (!this.usable(share) || !share.sample || (result.validUntil ?? 0) <= this.now()) {
|
|
144
|
+
delete result.valueRef;
|
|
145
|
+
if (result.status === "fresh") result.status = "stale";
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
list(endpointId, conversationId) {
|
|
150
|
+
return [...this.shares.values()].filter((share) => share.summary.endpointId === endpointId && (!conversationId || share.summary.conversationId === conversationId)).map((share) => this.summary(share));
|
|
151
|
+
}
|
|
152
|
+
async revoke(id, endpointId) {
|
|
153
|
+
return this.invalidate(this.require(id, endpointId), "revoked");
|
|
154
|
+
}
|
|
155
|
+
async invalidate(share, status) {
|
|
156
|
+
share.controller.abort();
|
|
157
|
+
share.sampleController?.abort();
|
|
158
|
+
share.sample = void 0;
|
|
159
|
+
delete share.summary.valueRef;
|
|
160
|
+
if (![
|
|
161
|
+
"revoked",
|
|
162
|
+
"expired",
|
|
163
|
+
"source_unavailable"
|
|
164
|
+
].includes(share.summary.status)) share.summary.status = status;
|
|
165
|
+
await this.clear(share);
|
|
166
|
+
return this.summary(share);
|
|
167
|
+
}
|
|
168
|
+
async clear(share) {
|
|
169
|
+
if (!share.summary.phoneId || !this.consumer) return;
|
|
170
|
+
share.summary.sync = "cleanup_pending";
|
|
171
|
+
share.operation = share.operation.catch(() => {}).then(async () => {
|
|
172
|
+
try {
|
|
173
|
+
share.summary.sync = await this.consumer.clear(share.summary.id, share.summary.phoneId) ? "clean" : "cleanup_pending";
|
|
174
|
+
} catch {
|
|
175
|
+
share.summary.sync = "cleanup_pending";
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
await share.operation;
|
|
179
|
+
}
|
|
180
|
+
async sweep() {
|
|
181
|
+
const pending = [];
|
|
182
|
+
for (const share of this.shares.values()) {
|
|
183
|
+
if (share.summary.status === "waiting" && share.summary.expiresAt > this.now() && this.registry.get(share.summary.endpointId)?.connectionId === share.epoch) continue;
|
|
184
|
+
if (!share.controller.signal.aborted && !this.usable(share)) pending.push(this.invalidate(share, share.summary.expiresAt <= this.now() ? "expired" : "source_unavailable"));
|
|
185
|
+
else if (share.sample && (share.summary.validUntil ?? 0) <= this.now()) {
|
|
186
|
+
share.sampleController?.abort();
|
|
187
|
+
share.sample = void 0;
|
|
188
|
+
delete share.summary.valueRef;
|
|
189
|
+
share.summary.status = "stale";
|
|
190
|
+
pending.push(this.clear(share));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
await Promise.all(pending);
|
|
194
|
+
}
|
|
195
|
+
async close() {
|
|
196
|
+
clearInterval(this.timer);
|
|
197
|
+
this.unsubscribe();
|
|
198
|
+
await Promise.all([...this.shares.values()].map((share) => this.invalidate(share, "revoked")));
|
|
199
|
+
this.shares.clear();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
//#endregion
|
|
203
|
+
export { LocationSharingError, LocationSharingService };
|
|
@@ -5,8 +5,12 @@ import { EndpointBindingService } from './binding-service.js';
|
|
|
5
5
|
import { EndpointInvocationService, type EndpointInvocationAuditSink } from './invocation-service.js';
|
|
6
6
|
import { EndpointRegistry, type EndpointRegistration, type EndpointTransport } from './registry.js';
|
|
7
7
|
import { EndpointUploadService } from './upload-service.js';
|
|
8
|
+
import { EndpointContextStore } from './context-store.js';
|
|
9
|
+
import { LocationSharingService } from './location-sharing.js';
|
|
8
10
|
export declare class EndpointToolRuntime {
|
|
9
11
|
readonly registry: EndpointRegistry;
|
|
12
|
+
readonly contexts: EndpointContextStore;
|
|
13
|
+
readonly locations: LocationSharingService;
|
|
10
14
|
readonly grants: DeviceGrantService;
|
|
11
15
|
readonly invocations: EndpointInvocationService;
|
|
12
16
|
readonly uploads: EndpointUploadService;
|
|
@@ -11,10 +11,12 @@ import { EndpointToolPolicy } from "./policy.js";
|
|
|
11
11
|
import { EndpointInvocationService } from "./invocation-service.js";
|
|
12
12
|
import { EndpointAuthenticator } from "./auth.js";
|
|
13
13
|
import { EndpointBindingService } from "./binding-service.js";
|
|
14
|
+
import { EndpointContextStore } from "./context-store.js";
|
|
14
15
|
import { DeviceGrantService } from "./grant-service.js";
|
|
15
16
|
import { recordDeviceGrantEvent } from "../storage/sqlite/device-grant-audit-repository.js";
|
|
16
17
|
import { EndpointRegistry } from "./registry.js";
|
|
17
18
|
import { EndpointUploadService } from "./upload-service.js";
|
|
19
|
+
import { LocationSharingService } from "./location-sharing.js";
|
|
18
20
|
import { join } from "node:path";
|
|
19
21
|
//#region src/endpoint-tools/runtime.ts
|
|
20
22
|
init_paths();
|
|
@@ -23,6 +25,8 @@ const log = createLogger("EndpointTools");
|
|
|
23
25
|
const MAX_MESSAGE_CLOCK_SKEW_MS = 6e4;
|
|
24
26
|
var EndpointToolRuntime = class {
|
|
25
27
|
registry;
|
|
28
|
+
contexts;
|
|
29
|
+
locations;
|
|
26
30
|
grants;
|
|
27
31
|
invocations;
|
|
28
32
|
uploads;
|
|
@@ -32,6 +36,8 @@ var EndpointToolRuntime = class {
|
|
|
32
36
|
constructor(options = {}) {
|
|
33
37
|
const policy = new EndpointToolPolicy();
|
|
34
38
|
this.registry = new EndpointRegistry(policy);
|
|
39
|
+
this.contexts = new EndpointContextStore(this.registry);
|
|
40
|
+
this.locations = new LocationSharingService(this.registry, this.contexts);
|
|
35
41
|
this.grants = new DeviceGrantService(this.registry, Date.now, recordDeviceGrantEvent);
|
|
36
42
|
this.bindings = new EndpointBindingService(this.registry, {
|
|
37
43
|
get: getEndpointSessionBinding,
|
|
@@ -103,6 +109,8 @@ var EndpointToolRuntime = class {
|
|
|
103
109
|
this.grants.close();
|
|
104
110
|
this.invocations.close();
|
|
105
111
|
this.uploads.close();
|
|
112
|
+
this.contexts.close();
|
|
113
|
+
this.locations.close();
|
|
106
114
|
this.registry.closeAll();
|
|
107
115
|
}
|
|
108
116
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EndpointContext } from '@xopcai/gateway-contract';
|
|
2
|
+
import type { EndpointContextView, LocationShareSummary } from '@xopcai/endpoint-tools-protocol';
|
|
2
3
|
import type { AgentSourceContext } from '../agent/source-context/types.js';
|
|
3
4
|
import type { GatewayPrincipal } from '../gateway/security/gateway-principal.js';
|
|
4
5
|
import type { EndpointConnectionSnapshot, EndpointRegistry } from './registry.js';
|
|
5
6
|
export declare function endpointClaimBelongsToPrincipal(registry: EndpointRegistry, endpointId: string, principal: GatewayPrincipal): boolean;
|
|
6
|
-
export declare function deviceTurnSourceContext(endpoint: EndpointConnectionSnapshot, environment?: EndpointContext): AgentSourceContext;
|
|
7
|
+
export declare function deviceTurnSourceContext(endpoint: EndpointConnectionSnapshot, environment?: EndpointContext, taskContext?: EndpointContextView, locationShares?: LocationShareSummary[]): AgentSourceContext;
|
|
@@ -5,7 +5,7 @@ function endpointClaimBelongsToPrincipal(registry, endpointId, principal) {
|
|
|
5
5
|
const endpoint = registry.get(endpointId);
|
|
6
6
|
return Boolean(endpoint && (principal.kind === "owner" || principal.kind === "device" && endpoint.principalId === principal.deviceId));
|
|
7
7
|
}
|
|
8
|
-
function deviceTurnSourceContext(endpoint, environment) {
|
|
8
|
+
function deviceTurnSourceContext(endpoint, environment, taskContext, locationShares) {
|
|
9
9
|
const name = getEndpointDeviceSettings(endpoint.principalId)?.nickname ?? endpoint.displayName;
|
|
10
10
|
const snapshot = {
|
|
11
11
|
endpointId: endpoint.endpointId,
|
|
@@ -13,7 +13,9 @@ function deviceTurnSourceContext(endpoint, environment) {
|
|
|
13
13
|
name: name.slice(0, 80),
|
|
14
14
|
platform: endpoint.platform.slice(0, 40),
|
|
15
15
|
kind: endpoint.kind,
|
|
16
|
-
...environment ? { environment } : {}
|
|
16
|
+
...environment ? { environment } : {},
|
|
17
|
+
...taskContext ? { taskContext } : {},
|
|
18
|
+
...locationShares?.length ? { locationShares: locationShares.slice(-3) } : {}
|
|
17
19
|
};
|
|
18
20
|
const text = JSON.stringify(snapshot).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e");
|
|
19
21
|
return {
|
|
@@ -9,8 +9,8 @@ import { authenticateDeviceAccessToken, getDevice } from "../../../storage/sqlit
|
|
|
9
9
|
import { getOrCreateGatewayIdentity } from "../../../storage/sqlite/gateway-identity-repository.js";
|
|
10
10
|
import { safeEqualSecret } from "../../security/secret-equal.js";
|
|
11
11
|
import { resolveClientIpFromRequest } from "../../client-ip.js";
|
|
12
|
-
import { authenticateBrowserSession } from "../../../storage/sqlite/browser-session-repository.js";
|
|
13
12
|
import { PROOF_HEADER, acquireProofBodyBudget, consumeRequestNonce, parseRequestProof, proofDigest, requestProofMessage, verifyRequestProofSignature } from "../../security/device-request-proof.js";
|
|
13
|
+
import { authenticateBrowserSession } from "../../../storage/sqlite/browser-session-repository.js";
|
|
14
14
|
import { isChromeExtensionOrigin } from "../../security/origin-check.js";
|
|
15
15
|
import { browserCookieRequestAllowed, readBrowserSessionCookie, writeBrowserSessionCookie } from "../../security/browser-session.js";
|
|
16
16
|
import { authorizeTrustedProxy } from "../../trusted-proxy.js";
|
|
@@ -230,6 +230,7 @@ function registerDeviceRoutes(authenticated, deps) {
|
|
|
230
230
|
deps.service.endpointTools.registry.revokePrincipal(principal.deviceId);
|
|
231
231
|
deps.service.realtime.disconnectPrincipal(principal.deviceId);
|
|
232
232
|
deps.service.voiceRealtime.disconnectPrincipal(principal.deviceId);
|
|
233
|
+
deps.service.phoneStreams.disconnectPrincipal(principal.deviceId);
|
|
233
234
|
}
|
|
234
235
|
return c.json({
|
|
235
236
|
ok: true,
|
|
@@ -19,6 +19,10 @@ function registerEndpointCompatibilityRoutes(authenticated) {
|
|
|
19
19
|
capabilities: REALTIME_CAPABILITIES
|
|
20
20
|
},
|
|
21
21
|
turnDeviceContextV1: true,
|
|
22
|
+
endpointEnvironmentV1: true,
|
|
23
|
+
locationSharingV1: true,
|
|
24
|
+
mobilePhoneWorkspaceV1: true,
|
|
25
|
+
phoneMediaProtocolVersion: 1,
|
|
22
26
|
deviceStateToolsV1: true,
|
|
23
27
|
hardwareBridgeV1: true,
|
|
24
28
|
deviceLocationTasksV1: true,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { endpointTurnClaimSchema, init_src } from "../../../../packages/endpoint-tools-protocol/src/index.js";
|
|
2
|
+
import { EndpointContextError } from "../../../endpoint-tools/context-store.js";
|
|
3
|
+
import { endpointClaimBelongsToPrincipal } from "../../../endpoint-tools/turn-context.js";
|
|
4
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { bodyLimit } from "hono/body-limit";
|
|
7
|
+
//#region src/gateway/hono/routes/endpoint-environment.ts
|
|
8
|
+
init_src();
|
|
9
|
+
const root = "/api/user-context/environment";
|
|
10
|
+
const mutationSchema = z.strictObject({
|
|
11
|
+
origin: endpointTurnClaimSchema,
|
|
12
|
+
report: z.unknown().optional(),
|
|
13
|
+
heartbeat: z.unknown().optional()
|
|
14
|
+
});
|
|
15
|
+
function registerEndpointEnvironmentRoutes(app, deps) {
|
|
16
|
+
const runtime = deps.service.endpointTools;
|
|
17
|
+
app.get(root, (c) => {
|
|
18
|
+
const actor = getGatewayPrincipal(c);
|
|
19
|
+
c.header("Cache-Control", "no-store");
|
|
20
|
+
const snapshots = runtime.registry.list().filter((endpoint) => endpointClaimBelongsToPrincipal(runtime.registry, endpoint.endpointId, actor)).map((endpoint) => runtime.contexts.view(endpoint.endpointId)).filter(Boolean);
|
|
21
|
+
return c.json({
|
|
22
|
+
ok: true,
|
|
23
|
+
payload: snapshots
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
app.get(`${root}/endpoints/:endpointId`, (c) => {
|
|
27
|
+
c.header("Cache-Control", "no-store");
|
|
28
|
+
const id = c.req.param("endpointId");
|
|
29
|
+
if (!endpointClaimBelongsToPrincipal(runtime.registry, id, getGatewayPrincipal(c))) return c.json({
|
|
30
|
+
ok: false,
|
|
31
|
+
error: {
|
|
32
|
+
code: "FORBIDDEN",
|
|
33
|
+
message: "Context source is not accessible"
|
|
34
|
+
}
|
|
35
|
+
}, 403);
|
|
36
|
+
try {
|
|
37
|
+
return c.json({
|
|
38
|
+
ok: true,
|
|
39
|
+
payload: {
|
|
40
|
+
...runtime.contexts.connection(id),
|
|
41
|
+
context: runtime.contexts.view(id)
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
return c.json({
|
|
46
|
+
ok: false,
|
|
47
|
+
error: {
|
|
48
|
+
code: "SOURCE_UNAVAILABLE",
|
|
49
|
+
message: "Context source is unavailable"
|
|
50
|
+
}
|
|
51
|
+
}, 409);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
for (const action of ["reports", "heartbeats"]) app.post(`${root}/${action}`, bodyLimit({ maxSize: 16384 }), async (c) => {
|
|
55
|
+
c.header("Cache-Control", "no-store");
|
|
56
|
+
const parsed = mutationSchema.safeParse(await c.req.json().catch(() => null));
|
|
57
|
+
if (!parsed.success || (action === "reports" ? parsed.data.heartbeat !== void 0 || parsed.data.report === void 0 : parsed.data.report !== void 0 || parsed.data.heartbeat === void 0)) return c.json({
|
|
58
|
+
ok: false,
|
|
59
|
+
error: {
|
|
60
|
+
code: "CONTEXT_INVALID",
|
|
61
|
+
message: "Invalid context request"
|
|
62
|
+
}
|
|
63
|
+
}, 400);
|
|
64
|
+
const { origin } = parsed.data;
|
|
65
|
+
if (!endpointClaimBelongsToPrincipal(runtime.registry, origin.endpointId, getGatewayPrincipal(c))) return c.json({
|
|
66
|
+
ok: false,
|
|
67
|
+
error: {
|
|
68
|
+
code: "FORBIDDEN",
|
|
69
|
+
message: "Context source is not accessible"
|
|
70
|
+
}
|
|
71
|
+
}, 403);
|
|
72
|
+
if (!runtime.registry.verifyTurnClaim(origin.endpointId, origin.token)) return c.json({
|
|
73
|
+
ok: false,
|
|
74
|
+
error: {
|
|
75
|
+
code: "SOURCE_UNAVAILABLE",
|
|
76
|
+
message: "Context connection changed"
|
|
77
|
+
}
|
|
78
|
+
}, 409);
|
|
79
|
+
try {
|
|
80
|
+
const payload = action === "reports" ? runtime.contexts.report(origin.endpointId, parsed.data.report) : runtime.contexts.heartbeat(origin.endpointId, parsed.data.heartbeat);
|
|
81
|
+
return c.json({
|
|
82
|
+
ok: true,
|
|
83
|
+
payload
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (!(error instanceof EndpointContextError)) throw error;
|
|
87
|
+
return c.json({
|
|
88
|
+
ok: false,
|
|
89
|
+
error: {
|
|
90
|
+
code: error.code,
|
|
91
|
+
message: error.message
|
|
92
|
+
}
|
|
93
|
+
}, error.code === "CONTEXT_INVALID" ? 400 : 409);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { registerEndpointEnvironmentRoutes };
|
|
@@ -106,6 +106,7 @@ function registerEndpointToolRoutes(authenticated, deps) {
|
|
|
106
106
|
if (accessRevoked) {
|
|
107
107
|
deps.service.realtime.disconnectPrincipal(id);
|
|
108
108
|
deps.service.voiceRealtime.disconnectPrincipal(id);
|
|
109
|
+
deps.service.phoneStreams.disconnectPrincipal(id);
|
|
109
110
|
}
|
|
110
111
|
if (principalRevoked) {
|
|
111
112
|
for (const endpoint of deps.service.endpointTools.registry.list()) if (endpoint.principalId === id) deps.service.endpointTools.disconnect(endpoint.endpointId, "Device revoked");
|
|
@@ -3,6 +3,22 @@ function startsWithAny(path, prefixes) {
|
|
|
3
3
|
return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
|
|
4
4
|
}
|
|
5
5
|
const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
|
|
6
|
+
{
|
|
7
|
+
id: "location-sharing",
|
|
8
|
+
match: (path) => /^\/api\/user-context\/environment\/location-shares(?:\/[^/]+(?:\/samples)?)?$/.test(path),
|
|
9
|
+
load: async () => {
|
|
10
|
+
const { registerLocationSharingRoutes } = await import("./location-sharing.js");
|
|
11
|
+
return { register: registerLocationSharingRoutes };
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "endpoint-environment",
|
|
16
|
+
match: (path) => /^\/api\/user-context\/environment(?:\/(?:reports|heartbeats|endpoints\/[^/]+))?$/.test(path),
|
|
17
|
+
load: async () => {
|
|
18
|
+
const { registerEndpointEnvironmentRoutes } = await import("./endpoint-environment.js");
|
|
19
|
+
return { register: registerEndpointEnvironmentRoutes };
|
|
20
|
+
}
|
|
21
|
+
},
|
|
6
22
|
{
|
|
7
23
|
id: "phone",
|
|
8
24
|
match: (path) => startsWithAny(path, ["/api/phone"]),
|