@xopcai/xopc 0.0.264 → 0.0.266
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/assets/auth-CknJ00ae.js +5844 -0
- package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +113 -0
- package/dist/browser-ext/dist/background.js +200 -68
- package/dist/browser-ext/dist/sidepanel.html +14 -0
- package/dist/browser-ext/dist/sidepanel.js +18832 -0
- package/dist/browser-ext/manifest.json +20 -20
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-BYBCpt7_.js → activity-store-Bhyy6ENz.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D20aBHPJ.js → agent-avatar-dicebear-Qrp5ALCF.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZvMqNrU.js → agent-defaults-settings-panel-DwinST48.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-6foBELwt.js → agents-Dlr0YyNt.js} +1 -1
- package/dist/gateway/static/root/assets/{api-YbtjDbuG.js → api-B2y7ZjZk.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-4ps1RrDU.js → app-management-settings-panel-bLNvz93c.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C7nBHnkG.js → appearance-settings-D5qgIepF.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-B_g-rL5b.js → apps-page-BQVlXhIb.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DfKK1iJO.js → archive-plugin-BEA7VAA4.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BOU1pdnQ.js → attachment-load-B1RyF3uu.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BePzexAH.js → automations-page-BfH0FdKa.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-7-m5pBEZ.js → automations-workspace-C37AlD3E.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-Cly8XDyt.js → binary-plugins-EU884ZXi.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DzSu62js.js → block-editor-Bjr3naHb.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-8bHj7Fv2.js → browser-automation-inputs-s5I78Iko.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CzL7Aj2k.js → browser-automations-page-CDrYrNGy.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-Dum3mTiz.js → browser-settings-page-DOmUkj2k.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ci6qVS-O.js → capabilities-settings-panel-slt1o04n.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-BOGEZnSv.js → channels-settings-oHTTX6Jy.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-BR8oQ_eP.js → channels-status-swr-_OsWr_vS.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BF9ZXa5Z.js → chat-terminal-dock-DEYQdv6n.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-DBvEvupa.js → connectors-page-olrbxHIT.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-expression-BszT_gjK.js → cron-expression-DFrbDWpP.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-D2MKBJ0G.js → date-picker-CYriYKpH.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-DqigBx-G.js → dependency-picker-XY3qUErs.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-CapspBGG.js → desktop-pet-Dsc__ynA.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DMPk4CaK.js → desktop-pet-settings-EpuX3C08.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CXLsDtrW.js → extension-debug-page-B_NeLV4K.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-B2j87ROw.js → extension-page-D3JC6mLB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-t1BApw-u.js → extension-settings-page-CXTQt1lp.js} +1 -1
- package/dist/gateway/static/root/assets/{files-BArWTfr7.js → files-Be5viEFY.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-BFSesvAC.js → gateway-config-swr-OObCK8jM.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BFAecoGy.js → gateway-settings-yeYm_UPE.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-D1giYgI-.js → gateway-startup-retry-D_TxKN_A.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CyD9zDcU.js → heartbeat-settings-coh1LFsg.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-uItZMiBE.js → home-page-CAgoBZqp.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-BeB-OPqO.js → image-generation-api-fPU9yGsw.js} +1 -1
- package/dist/gateway/static/root/assets/index-Cn__9Un2.js +74 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Q_CM-ZCs.js → keyboard-shortcuts-settings-Cu1bI3Bg.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-DaiBWfOt.js → local-app-workbench-page-C9s2lFaI.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BALMNCSp.js → local-apps-page-CJ03Umjr.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-RJHYFoq9.js → locale-store-DFQX9kTl.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-C-TS7oFg.js → logs-page-CY_DQKJ2.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-BajkJeYl.js → management-settings-YPEemeMo.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-BxoQJqHx.js → markdown-view-D-5jHRWg.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C_8Hl6KG.js → media-plugins-BjT6jlrA.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-Cf1h5WXB.js → messages-Dj8QuS6e.js} +2 -2
- package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsWIZrZW.js → mobile-pairing-wizard-A8eLtV6v.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-CCbPg_GP.js → note-detail-panel-DHT2QksJ.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-BdxxcUKa.js → notes-page-BRFQiivM.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-BMYQef5D.js → page-tabs-DNUwulnl.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CKZ-H5hJ.js → preview-open-alternatives-B33KsKkJ.js} +1 -1
- package/dist/gateway/static/root/assets/{product-delivery-CV4pDOtG.js → product-delivery-CCC4FJ3Y.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-zXJIptXs.js → product-open-page-CIGLPFm7.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-DRhhxhZB.js → project-detail-page-CItEFVt9.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-Wt7gF3Sx.js → projects-page-Bmy-c_lL.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-BSfFaxVN.js → registry-api-CsZLatKo.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-B6de5Zrb.js → remote-access-hub-H6B1cA2V.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DcUB5h1f.js → runtime-tools-settings-panel-BsCovC39.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CquWhe7r.js → schema-form-NsPb_Vam.js} +1 -1
- package/dist/gateway/static/root/assets/{schemas-BHS7bo06.js → schemas-BaskVM5g.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-BNWO2EVF.js → sessions-page-CyYLSXk_.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-3T4rPk4A.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DwswAefc.js → settings-form-section-BzPIPe1-.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CoAVmjjs.js → settings-page-4idpMhsw.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-C9EIcWtH.js → setup-status-panel-D2ds_TTD.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DWwy_4zh.js → share-preview-page-Djo3VTaC.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CIUvq27-.js → shares-settings-fhRzNfRp.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Ct8tUju_.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-CqsgVtVh.js → skills-page-riM7FytZ.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-B-N73248.js → skills-page.utils-B3G8LBb-.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BndjXuBZ.js → system-settings-panel-CW91jjnB.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-CG6Ha9t7.js → task-detail-page-BBXKvl0k.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C5ssn64t.js → theme-store-zRT6vaZc.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-BxZdcp3h.js → tunnel-api-DILER0Yb.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-mj9CUuz1.js → use-autosave-GxnP8vLO.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-DKeHMXI4.js → user-model-page-I1H4XRfA.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CzxVMYlr.js → voice-api-key-field-CI3sXyl4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-COA0V1cK.js → voice-config-api-DpJEu2RQ.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-DMiPK8dp.js → work-discovery-overlay-DZC9Exn3.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C82OXCJK.js → workflow-run-setup-panel-CaBaQER2.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-HLCUV3rQ.js → workflows-page-C-_Dxihw.js} +1 -1
- package/dist/gateway/static/root/index.html +12 -12
- package/dist/package.js +1 -1
- package/dist/packages/browser-control-contract/src/index.js +14 -0
- package/dist/packages/endpoint-tools-protocol/src/index.js +2 -1
- package/dist/packages/gateway-contract/src/browser-page-context.js +43 -0
- package/dist/packages/gateway-contract/src/browser-tab-binding.js +29 -0
- package/dist/packages/gateway-contract/src/device-pairing.js +18 -2
- package/dist/packages/realtime-protocol/src/index.js +1 -0
- package/dist/packages/realtime-protocol/src/voice-audio.js +70 -9
- package/dist/packages/realtime-protocol/src/voice.js +65 -17
- package/dist/src/agent/agent-manager.js +4 -4
- package/dist/src/agent/background-review/run-background-review.js +1 -1
- package/dist/src/agent/embedded/model-runtime.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
- package/dist/src/agent/service.js +2 -2
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/source-context/browser-page.d.ts +3 -0
- package/dist/src/agent/source-context/browser-page.js +39 -0
- package/dist/src/agent/source-context/injector.js +9 -2
- package/dist/src/agent/source-context/types.d.ts +7 -1
- package/dist/src/agent/source-context/types.js +4 -1
- package/dist/src/agent/tasks/task-judge-service.js +3 -3
- package/dist/src/agent/tools/factory.js +9 -1
- package/dist/src/ai-assist/text-assist.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/oauth/xopc-cloud.js +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +2 -2
- package/dist/src/browser/automations/schema.d.ts +1 -1
- package/dist/src/browser/automations/service.d.ts +1 -1
- package/dist/src/browser/native-messaging-host.d.ts +15 -0
- package/dist/src/browser/native-messaging-host.js +105 -0
- package/dist/src/browser/providers/browser-ext-install.d.ts +17 -1
- package/dist/src/browser/providers/browser-ext-install.js +82 -16
- package/dist/src/browser/providers/extension.d.ts +4 -0
- package/dist/src/browser/providers/extension.js +104 -38
- package/dist/src/browser/runtime/browser-runtime.d.ts +2 -1
- package/dist/src/browser/runtime/browser-runtime.js +6 -2
- package/dist/src/cli/commands/browser.js +13 -3
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/endpoint-tools/runtime.js +2 -0
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +3 -2
- package/dist/src/gateway/file-path-classifier.js +1 -1
- package/dist/src/gateway/hono/app.js +19 -3
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +4 -1
- package/dist/src/gateway/hono/middleware/auth.js +12 -1
- package/dist/src/gateway/hono/routes/browser-install.js +1 -1
- package/dist/src/gateway/hono/routes/browser.js +106 -7
- package/dist/src/gateway/hono/routes/devices.js +36 -3
- package/dist/src/gateway/hono/routes/endpoint-tools.js +15 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/session-input-handler.d.ts +20 -2
- package/dist/src/gateway/hono/routes/session-input-handler.js +18 -0
- package/dist/src/gateway/hono/routes/sessions.js +12 -6
- package/dist/src/gateway/hono/routes/voice.js +5 -4
- package/dist/src/gateway/security/gateway-scopes.d.ts +1 -0
- package/dist/src/gateway/security/gateway-scopes.js +10 -1
- package/dist/src/gateway/security/origin-check.d.ts +1 -0
- package/dist/src/gateway/security/origin-check.js +5 -1
- package/dist/src/gateway/service/agent-runner.js +2 -1
- package/dist/src/gateway/service/config-coordinator.js +1 -1
- package/dist/src/gateway/service/session-input-coordinator.d.ts +1 -0
- package/dist/src/gateway/service/session-input-coordinator.js +5 -4
- package/dist/src/gateway/service.js +22 -21
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/register-runtime-providers.js +1 -1
- package/dist/src/realtime/runtime.js +2 -1
- package/dist/src/session/client-history.d.ts +4 -1
- package/dist/src/session/client-history.js +6 -3
- package/dist/src/storage/sqlite/browser-tab-binding-repository.d.ts +7 -0
- package/dist/src/storage/sqlite/browser-tab-binding-repository.js +60 -0
- package/dist/src/storage/sqlite/device-access-repository.d.ts +3 -1
- package/dist/src/storage/sqlite/device-access-repository.js +4 -3
- package/dist/src/storage/sqlite/device-pairing-approval.js +6 -4
- package/dist/src/storage/sqlite/device-pairing-repository.d.ts +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/migrations/155_browser_extension.sql +120 -0
- package/dist/src/storage/sqlite/migrations/156_browser_tab_bindings.sql +14 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/voice/realtime/agentBroker.d.ts +54 -0
- package/dist/src/voice/realtime/agentBroker.js +116 -0
- package/dist/src/voice/realtime/agentEngine.js +62 -15
- package/dist/src/voice/realtime/engine.d.ts +2 -1
- package/dist/src/voice/realtime/omniEngine.js +36 -7
- package/dist/src/voice/realtime/pcmFrameBuffer.d.ts +6 -0
- package/dist/src/voice/realtime/pcmFrameBuffer.js +29 -0
- package/dist/src/voice/realtime/runtime.js +97 -21
- package/dist/src/voice/realtime/runtime.types.d.ts +5 -21
- package/dist/src/voice/realtime/turnPolicy.d.ts +36 -0
- package/dist/src/voice/realtime/turnPolicy.js +134 -0
- package/dist/src/work-discovery/service.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +1 -1
- package/package.json +2 -2
- package/dist/browser-ext/dist/content.js +0 -191
- package/dist/browser-ext/dist/popup.js +0 -32
- package/dist/browser-ext/popup.html +0 -37
- package/dist/gateway/static/root/assets/index-DQtjs2x3.js +0 -74
- package/dist/gateway/static/root/assets/settings-advanced-gate-DK1yTYE4.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DfE3zFVw.js +0 -1
- package/dist/src/voice/realtime/conversationTurn.d.ts +0 -20
- package/dist/src/voice/realtime/conversationTurn.js +0 -83
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light dark;
|
|
3
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
4
|
+
background: #f8fafc;
|
|
5
|
+
color: #0f172a;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
html, body, #root { height: 100%; margin: 0; }
|
|
10
|
+
button, input, textarea { font: inherit; }
|
|
11
|
+
button { cursor: pointer; }
|
|
12
|
+
|
|
13
|
+
.shell { min-height: 100%; display: flex; flex-direction: column; background: #fff; }
|
|
14
|
+
.header { height: 56px; padding: 0 16px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid #e2e8f0; }
|
|
15
|
+
.brand { font-size: 17px; font-weight: 650; flex: 1; }
|
|
16
|
+
.status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: #64748b; }
|
|
17
|
+
.status-dot { width: 8px; height: 8px; border-radius: 999px; background: #94a3b8; }
|
|
18
|
+
.status-dot.online { background: #22c55e; }
|
|
19
|
+
.status-dot.error { background: #ef4444; }
|
|
20
|
+
.center { flex: 1; display: grid; place-items: center; padding: 24px; }
|
|
21
|
+
.card { width: min(100%, 420px); border: 1px solid #e2e8f0; border-radius: 16px; padding: 20px; background: #fff; }
|
|
22
|
+
.card h1 { margin: 0 0 8px; font-size: 20px; }
|
|
23
|
+
.muted { color: #64748b; line-height: 1.5; font-size: 13px; }
|
|
24
|
+
.field { display: grid; gap: 6px; margin-top: 14px; font-size: 13px; }
|
|
25
|
+
.field input, .field textarea { width: 100%; border: 1px solid #cbd5e1; border-radius: 9px; padding: 10px 12px; background: transparent; }
|
|
26
|
+
.primary { border: 0; border-radius: 9px; background: #2563eb; color: #fff; padding: 10px 14px; font-weight: 600; }
|
|
27
|
+
.secondary { border: 1px solid #cbd5e1; border-radius: 9px; background: transparent; padding: 8px 12px; }
|
|
28
|
+
.actions { display: flex; gap: 8px; margin-top: 16px; }
|
|
29
|
+
.local-connect { width: 100%; margin-top: 12px; }
|
|
30
|
+
.pairing-divider { display: flex; align-items: center; gap: 8px; margin-top: 16px; color: #94a3b8; font-size: 11px; }
|
|
31
|
+
.pairing-divider::before, .pairing-divider::after { content: ""; height: 1px; flex: 1; background: #e2e8f0; }
|
|
32
|
+
.error-text { color: #dc2626; font-size: 13px; margin-top: 10px; }
|
|
33
|
+
.approval-code { font: 700 28px ui-monospace, monospace; letter-spacing: 5px; margin: 18px 0; }
|
|
34
|
+
.disconnect-link { position: absolute; top: 17px; right: 96px; border: 0; background: transparent; color: #64748b; font-size: 11px; }
|
|
35
|
+
.chat { min-height: 0; flex: 1; display: flex; flex-direction: column; position: relative; }
|
|
36
|
+
.chat-toolbar { height: 52px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; padding: 0 12px; gap: 8px; }
|
|
37
|
+
.session-trigger { flex: 1; display: flex; justify-content: center; gap: 7px; border: 0; background: transparent; font-weight: 600; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
38
|
+
.icon-button { width: 32px; height: 32px; border-radius: 8px; border: 1px solid #e2e8f0; background: transparent; }
|
|
39
|
+
.session-menu { position: absolute; z-index: 5; top: 108px; left: 12px; right: 12px; max-height: min(60vh, 500px); border: 1px solid #cbd5e1; border-radius: 12px; background: #fff; padding: 10px; box-shadow: 0 14px 40px rgb(15 23 42 / 14%); }
|
|
40
|
+
.session-menu > input { width: 100%; border: 0; border-bottom: 1px solid #e2e8f0; padding: 9px; outline: none; background: transparent; }
|
|
41
|
+
.session-list { overflow: auto; max-height: 380px; padding-top: 5px; }
|
|
42
|
+
.session-list button { width: 100%; border: 0; background: transparent; border-radius: 7px; display: flex; justify-content: space-between; gap: 12px; padding: 9px; text-align: left; }
|
|
43
|
+
.session-list button:hover { background: #f1f5f9; }
|
|
44
|
+
.session-list span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
45
|
+
.session-list small { color: #94a3b8; }
|
|
46
|
+
.messages { flex: 1; overflow-y: auto; padding: 18px 16px 150px; }
|
|
47
|
+
.empty-chat { height: 100%; display: grid; place-content: center; text-align: center; color: #64748b; }
|
|
48
|
+
.mark { font-size: 40px; color: #94a3b8; }
|
|
49
|
+
.message { margin: 0 0 20px; line-height: 1.55; }
|
|
50
|
+
.message.user { margin-left: 15%; background: #f1f5f9; border-radius: 14px; padding: 10px 12px; }
|
|
51
|
+
.message-role { font-size: 11px; font-weight: 700; color: #64748b; margin-bottom: 4px; text-transform: uppercase; letter-spacing: .04em; }
|
|
52
|
+
.message-content { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
53
|
+
.source-badge { display: inline-block; max-width: 100%; margin-bottom: 6px; border: 1px solid #bfdbfe; border-radius: 999px; padding: 3px 8px; color: #1d4ed8; font-size: 11px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
54
|
+
.caret { display: inline-block; width: 6px; height: 1em; margin-left: 2px; vertical-align: -2px; background: #2563eb; animation: blink 1s steps(1) infinite; }
|
|
55
|
+
@keyframes blink { 50% { opacity: 0; } }
|
|
56
|
+
.composer { position: absolute; left: 12px; right: 12px; bottom: 12px; border: 1px solid #cbd5e1; border-radius: 16px; background: #fff; padding: 10px; box-shadow: 0 8px 30px rgb(15 23 42 / 10%); }
|
|
57
|
+
.composer textarea { width: 100%; resize: none; border: 0; outline: none; background: transparent; }
|
|
58
|
+
.tab-mention-menu { position: absolute; z-index: 6; left: 0; right: 0; bottom: calc(100% + 8px); max-height: min(45vh, 320px); overflow-y: auto; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff; padding: 5px; box-shadow: 0 14px 40px rgb(15 23 42 / 14%); }
|
|
59
|
+
.tab-mention-menu button { width: 100%; display: grid; gap: 2px; border: 0; border-radius: 8px; padding: 8px 10px; background: transparent; color: inherit; text-align: left; }
|
|
60
|
+
.tab-mention-menu button:hover, .tab-mention-menu button.selected { background: #eff6ff; }
|
|
61
|
+
.tab-mention-menu span, .tab-mention-menu small { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
62
|
+
.tab-mention-menu span { font-size: 12px; font-weight: 600; }
|
|
63
|
+
.tab-mention-menu small, .tab-mention-empty { color: #64748b; font-size: 11px; }
|
|
64
|
+
.tab-mention-empty { padding: 10px; }
|
|
65
|
+
.composer-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; margin-top: 5px; }
|
|
66
|
+
.composer-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
|
|
67
|
+
.composer-tools button { border: 0; background: transparent; padding: 3px; color: #475569; font-size: 11px; }
|
|
68
|
+
.context-chip { display: flex; align-items: center; gap: 5px; margin-bottom: 7px; border: 1px solid #bfdbfe; border-radius: 8px; padding: 6px 7px; color: #1e40af; background: #eff6ff; font-size: 11px; }
|
|
69
|
+
.context-chip span { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
70
|
+
.context-chip button { border: 0; background: transparent; color: inherit; padding: 2px 4px; }
|
|
71
|
+
.context-chip.stale { border-color: #fbbf24; background: #fffbeb; color: #92400e; }
|
|
72
|
+
.attachment-chip { display: flex; align-items: center; gap: 5px; margin-bottom: 7px; border: 1px solid #cbd5e1; border-radius: 8px; padding: 6px 7px; color: #475569; background: #f8fafc; font-size: 11px; }
|
|
73
|
+
.attachment-chip span { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
74
|
+
.attachment-chip button { border: 0; background: transparent; color: inherit; }
|
|
75
|
+
.file-input { display: none; }
|
|
76
|
+
.connection-label { color: #94a3b8; font-size: 11px; }
|
|
77
|
+
.model-controls { display: flex; align-items: center; gap: 4px; margin-left: auto; }
|
|
78
|
+
.model-controls select { min-width: 0; max-width: 120px; border: 0; background: transparent; color: #64748b; font-size: 11px; }
|
|
79
|
+
.send-button, .stop-button { border: 0; border-radius: 999px; min-width: 34px; height: 34px; background: #2563eb; color: #fff; font-weight: 700; }
|
|
80
|
+
.send-button:disabled { background: #94a3b8; cursor: default; }
|
|
81
|
+
.stop-button { padding: 0 13px; }
|
|
82
|
+
.composer-error { color: #dc2626; font-size: 12px; margin-bottom: 6px; }
|
|
83
|
+
.clarification { margin: 0 0 10px; border: 1px solid #bfdbfe; border-radius: 11px; padding: 10px; background: #eff6ff; }
|
|
84
|
+
.clarification strong { display: block; font-size: 12px; color: #1d4ed8; }
|
|
85
|
+
.clarification p { margin: 6px 0 8px; font-size: 13px; line-height: 1.45; }
|
|
86
|
+
.clarification > button, .clarification-actions button, .clarification-answer button { margin: 0 6px 6px 0; border: 1px solid #93c5fd; border-radius: 7px; padding: 5px 8px; background: #fff; color: #1e3a8a; font-size: 12px; }
|
|
87
|
+
.clarification-answer { display: flex; gap: 6px; }
|
|
88
|
+
.clarification-answer input { min-width: 0; flex: 1; border: 1px solid #93c5fd; border-radius: 7px; padding: 6px 8px; background: #fff; }
|
|
89
|
+
.clarification-actions { margin-top: 3px; }
|
|
90
|
+
.clarification-actions button { border-color: transparent; background: transparent; color: #64748b; }
|
|
91
|
+
|
|
92
|
+
@media (prefers-color-scheme: dark) {
|
|
93
|
+
:root { background: #0f172a; color: #e2e8f0; }
|
|
94
|
+
.shell, .card { background: #111827; }
|
|
95
|
+
.header, .card { border-color: #334155; }
|
|
96
|
+
.field input, .field textarea, .secondary { border-color: #475569; color: #e2e8f0; }
|
|
97
|
+
.muted, .status { color: #94a3b8; }
|
|
98
|
+
.chat-toolbar { border-color: #334155; }
|
|
99
|
+
.session-menu, .composer, .tab-mention-menu { background: #111827; border-color: #475569; }
|
|
100
|
+
.session-menu > input { border-color: #334155; color: #e2e8f0; }
|
|
101
|
+
.session-list button { color: #e2e8f0; }
|
|
102
|
+
.session-list button:hover, .message.user { background: #1e293b; }
|
|
103
|
+
.tab-mention-menu button:hover, .tab-mention-menu button.selected { background: #1e293b; }
|
|
104
|
+
.clarification { border-color: #1e40af; background: #172554; }
|
|
105
|
+
.clarification strong { color: #93c5fd; }
|
|
106
|
+
.clarification > button, .clarification-answer button { border-color: #1d4ed8; background: #1e3a8a; color: #dbeafe; }
|
|
107
|
+
.clarification-answer input { border-color: #1d4ed8; background: #0f172a; color: #e2e8f0; }
|
|
108
|
+
.source-badge { border-color: #1e40af; color: #93c5fd; }
|
|
109
|
+
.composer-tools button { color: #94a3b8; }
|
|
110
|
+
.context-chip { border-color: #1e40af; background: #172554; color: #bfdbfe; }
|
|
111
|
+
.context-chip.stale { border-color: #92400e; background: #451a03; color: #fde68a; }
|
|
112
|
+
.attachment-chip { border-color: #475569; background: #1e293b; color: #cbd5e1; }
|
|
113
|
+
}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import { T as TAB_BINDING_PREFIX, d as captureTabPage, P as PENDING_CONTEXT_KEY, r as readProfile, u as signBrowserBridgeChallenge } from './assets/auth-CknJ00ae.js';
|
|
2
|
+
|
|
3
|
+
const BROWSER_EXTENSION_PROTOCOL_VERSION = 4;
|
|
4
|
+
function browserWireAuthenticationPayload(input) {
|
|
5
|
+
return [
|
|
6
|
+
"xopc-browser-extension-v4",
|
|
7
|
+
input.connectionId,
|
|
8
|
+
input.challenge,
|
|
9
|
+
String(input.issuedAt),
|
|
10
|
+
input.principalId,
|
|
11
|
+
input.extensionId,
|
|
12
|
+
input.extensionVersion
|
|
13
|
+
].join("\n");
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
const XOPC_EXT_PORT = 19820;
|
|
4
17
|
const XOPC_EXT_HOST = "127.0.0.1";
|
|
@@ -116,16 +129,6 @@ chrome.debugger.onDetach.addListener((source) => {
|
|
|
116
129
|
if (source.tabId) attached.delete(source.tabId);
|
|
117
130
|
});
|
|
118
131
|
|
|
119
|
-
async function sendContentMessage(tabId, message) {
|
|
120
|
-
try {
|
|
121
|
-
await chrome.tabs.sendMessage(tabId, message);
|
|
122
|
-
} catch {
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async function hideOverlayOnTab(tabId) {
|
|
126
|
-
await sendContentMessage(tabId, { type: "content/hide-overlay" });
|
|
127
|
-
}
|
|
128
|
-
|
|
129
132
|
const log$1 = createLogger("SessionManager");
|
|
130
133
|
const XOPC_TAB_GROUP_TITLE = "xopc";
|
|
131
134
|
const automationSessions = /* @__PURE__ */ new Map();
|
|
@@ -262,9 +265,6 @@ async function closeSession(workspace = DEFAULT_WORKSPACE) {
|
|
|
262
265
|
if (!session) return;
|
|
263
266
|
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
264
267
|
const tabs = await chrome.tabs.query({ groupId: session.groupId }).catch(() => []);
|
|
265
|
-
for (const t of tabs) {
|
|
266
|
-
if (t.id != null) await hideOverlayOnTab(t.id);
|
|
267
|
-
}
|
|
268
268
|
for (const t of tabs) {
|
|
269
269
|
if (t.id != null) await chrome.tabs.remove(t.id).catch(() => {
|
|
270
270
|
});
|
|
@@ -272,18 +272,58 @@ async function closeSession(workspace = DEFAULT_WORKSPACE) {
|
|
|
272
272
|
automationSessions.delete(workspace);
|
|
273
273
|
log$1.info("Closed automation session", { workspace });
|
|
274
274
|
}
|
|
275
|
-
async function closeAllSessions() {
|
|
276
|
-
for (const workspace of [...automationSessions.keys()]) {
|
|
277
|
-
await closeSession(workspace);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
275
|
|
|
281
276
|
const observations = /* @__PURE__ */ new Map();
|
|
277
|
+
async function markerDigest(tabId) {
|
|
278
|
+
const marker = await evaluate(tabId, `({ url: location.href, timeOrigin: performance.timeOrigin })`);
|
|
279
|
+
const url = new URL(marker.url);
|
|
280
|
+
url.username = "";
|
|
281
|
+
url.password = "";
|
|
282
|
+
url.hash = "";
|
|
283
|
+
const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${url.toString()}
|
|
284
|
+
${marker.timeOrigin}`));
|
|
285
|
+
return [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
286
|
+
}
|
|
287
|
+
async function resolveAttachedTarget(input) {
|
|
288
|
+
if (input.target?.kind !== "attached_tab") return void 0;
|
|
289
|
+
const key = `${TAB_BINDING_PREFIX}${input.target.bindingId}`;
|
|
290
|
+
const stored = await chrome.storage.session.get(key);
|
|
291
|
+
const binding = stored[key];
|
|
292
|
+
if (!binding || binding.id !== input.target.bindingId || binding.expiresAt <= Date.now()) {
|
|
293
|
+
return fail("SESSION_NOT_FOUND", "The attached tab binding is unavailable or expired.");
|
|
294
|
+
}
|
|
295
|
+
if (input.action === "navigate" || input.action === "tabs" || input.action === "sequence") {
|
|
296
|
+
return fail("INVALID_INPUT", `${input.action} is unavailable for an attached tab.`);
|
|
297
|
+
}
|
|
298
|
+
const needsAct = !["observe", "wait", "close"].includes(input.action);
|
|
299
|
+
if (needsAct && binding.mode !== "act") {
|
|
300
|
+
return fail("APPROVAL_REQUIRED", "This tab is attached in read-only mode. Enable Control tab in the side panel.");
|
|
301
|
+
}
|
|
302
|
+
const tabId = Number(binding.tabId);
|
|
303
|
+
const tab = Number.isInteger(tabId) ? await chrome.tabs.get(tabId).catch(() => null) : null;
|
|
304
|
+
if (!tab || String(tab.windowId) !== binding.windowId || !tab.url) {
|
|
305
|
+
return fail("SESSION_NOT_FOUND", "The attached tab is no longer available.");
|
|
306
|
+
}
|
|
307
|
+
let origin;
|
|
308
|
+
try {
|
|
309
|
+
origin = new URL(tab.url).origin;
|
|
310
|
+
} catch {
|
|
311
|
+
return fail("UNSUPPORTED_PAGE", "The attached tab URL is not supported.");
|
|
312
|
+
}
|
|
313
|
+
if (origin !== binding.urlOrigin) {
|
|
314
|
+
return fail("STALE_OBSERVATION", "The attached tab navigated to another site. Bind it again.");
|
|
315
|
+
}
|
|
316
|
+
if (await markerDigest(tabId) !== binding.documentId) {
|
|
317
|
+
return fail("STALE_OBSERVATION", "The attached tab document changed. Bind it again.");
|
|
318
|
+
}
|
|
319
|
+
return { binding, tabId, stateKey: `attached:${binding.id}` };
|
|
320
|
+
}
|
|
282
321
|
async function executeBrowserCommand(command) {
|
|
283
322
|
const startedAt = Date.now();
|
|
284
323
|
if (command.protocolVersion !== BROWSER_EXTENSION_PROTOCOL_VERSION) {
|
|
285
324
|
return {
|
|
286
325
|
id: command.id,
|
|
326
|
+
connectionId: command.connectionId,
|
|
287
327
|
result: fail(
|
|
288
328
|
"DRIVER_UNAVAILABLE",
|
|
289
329
|
`Browser protocol mismatch. Expected ${BROWSER_EXTENSION_PROTOCOL_VERSION}; reload the xopc extension.`
|
|
@@ -292,10 +332,11 @@ async function executeBrowserCommand(command) {
|
|
|
292
332
|
}
|
|
293
333
|
try {
|
|
294
334
|
const result = await execute(command.input, command.timeoutMs, command.visualFallback, startedAt);
|
|
295
|
-
return { id: command.id, result };
|
|
335
|
+
return { id: command.id, connectionId: command.connectionId, result };
|
|
296
336
|
} catch (error) {
|
|
297
337
|
return {
|
|
298
338
|
id: command.id,
|
|
339
|
+
connectionId: command.connectionId,
|
|
299
340
|
result: {
|
|
300
341
|
ok: false,
|
|
301
342
|
error: {
|
|
@@ -309,31 +350,34 @@ async function executeBrowserCommand(command) {
|
|
|
309
350
|
async function execute(input, timeoutMs, visualFallback, startedAt) {
|
|
310
351
|
const sessionId = input.sessionId;
|
|
311
352
|
if (!sessionId) return fail("INVALID_INPUT", "Extension actions require a session id.");
|
|
353
|
+
const attached = await resolveAttachedTarget(input);
|
|
354
|
+
if (attached && !("binding" in attached)) return attached;
|
|
355
|
+
const stateKey = attached?.stateKey ?? sessionId;
|
|
312
356
|
if (input.action === "close") {
|
|
313
|
-
await closeSession(sessionId);
|
|
314
|
-
observations.delete(
|
|
357
|
+
if (!attached) await closeSession(sessionId);
|
|
358
|
+
observations.delete(stateKey);
|
|
315
359
|
return success("close", "read", startedAt);
|
|
316
360
|
}
|
|
317
361
|
if (input.action === "observe") {
|
|
318
362
|
const mode = input.visual === "always" ? "always" : input.visual === "never" || !visualFallback ? "never" : "auto";
|
|
319
|
-
const observation2 = await observe(sessionId, mode);
|
|
363
|
+
const observation2 = await observe(sessionId, mode, attached);
|
|
320
364
|
return success("observe", "read", startedAt, observation2);
|
|
321
365
|
}
|
|
322
366
|
if (input.action === "navigate") {
|
|
323
|
-
const tabId2 = await getActiveTabId(sessionId);
|
|
367
|
+
const tabId2 = attached?.tabId ?? await getActiveTabId(sessionId);
|
|
324
368
|
await chrome.tabs.update(tabId2, { url: input.url });
|
|
325
369
|
await waitForTabLoad(tabId2, timeoutMs);
|
|
326
|
-
resetWindowIdleTimer(sessionId);
|
|
327
|
-
const observation2 = await observe(sessionId, visualFallback ? "auto" : "never");
|
|
370
|
+
if (!attached) resetWindowIdleTimer(sessionId);
|
|
371
|
+
const observation2 = await observe(sessionId, visualFallback ? "auto" : "never", attached);
|
|
328
372
|
return verified(input, observation2, startedAt, "read");
|
|
329
373
|
}
|
|
330
374
|
if (input.action === "tabs") return tabs(sessionId, input, startedAt, timeoutMs);
|
|
331
375
|
if (input.action === "sequence") return fail("INVALID_INPUT", "Sequences are expanded by the browser runtime.");
|
|
332
|
-
const current = observations.get(
|
|
376
|
+
const current = observations.get(stateKey);
|
|
333
377
|
if (!current || current.revision !== input.revision) {
|
|
334
|
-
return fail("STALE_OBSERVATION", "The page changed. Observe it again.", await observe(sessionId, "never"));
|
|
378
|
+
return fail("STALE_OBSERVATION", "The page changed. Observe it again.", await observe(sessionId, "never", attached));
|
|
335
379
|
}
|
|
336
|
-
const tabId = await getActiveTabId(sessionId);
|
|
380
|
+
const tabId = attached?.tabId ?? await getActiveTabId(sessionId);
|
|
337
381
|
const ref = "ref" in input ? input.ref : void 0;
|
|
338
382
|
if (ref && !current.nodes.some((node) => node.ref === ref)) {
|
|
339
383
|
return fail("TARGET_NOT_FOUND", `Target ${ref} was not found.`);
|
|
@@ -388,11 +432,11 @@ async function execute(input, timeoutMs, visualFallback, startedAt) {
|
|
|
388
432
|
await sendCommand(tabId, "DOM.setFileInputFiles", { nodeId: query.nodeIds[0], files: input.paths });
|
|
389
433
|
}
|
|
390
434
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
391
|
-
const observation = await observe(sessionId, visualFallback ? "auto" : "never");
|
|
435
|
+
const observation = await observe(sessionId, visualFallback ? "auto" : "never", attached);
|
|
392
436
|
return verified(input, observation, startedAt, "draft");
|
|
393
437
|
}
|
|
394
|
-
async function observe(sessionId, visual) {
|
|
395
|
-
const tabId = await getActiveTabId(sessionId);
|
|
438
|
+
async function observe(sessionId, visual, attached) {
|
|
439
|
+
const tabId = attached?.tabId ?? await getActiveTabId(sessionId);
|
|
396
440
|
const tab = await chrome.tabs.get(tabId);
|
|
397
441
|
const raw = await evaluate(tabId, `(() => {
|
|
398
442
|
const selector = 'a[href],button,input:not([type="hidden"]),textarea,select,summary,[contenteditable="true"],[role="button"],[role="link"],[role="checkbox"],[role="radio"],[role="combobox"],[role="menuitem"],[role="option"],[role="tab"],[tabindex]:not([tabindex="-1"])';
|
|
@@ -428,10 +472,16 @@ async function observe(sessionId, visual) {
|
|
|
428
472
|
}
|
|
429
473
|
return { marker: location.href + '|' + performance.timeOrigin, nodes, hasVisualSurface: !!document.querySelector('canvas,embed[type="application/pdf"],object[type="application/pdf"]') };
|
|
430
474
|
})()`);
|
|
431
|
-
const
|
|
432
|
-
const
|
|
475
|
+
const stateKey = attached?.stateKey ?? sessionId;
|
|
476
|
+
const prior = observations.get(stateKey);
|
|
477
|
+
const state = prior?.documentMarker === raw.marker ? { ...prior, revision: prior.revision + 1, nodes: raw.nodes } : {
|
|
478
|
+
documentMarker: raw.marker,
|
|
479
|
+
documentId: attached && !prior ? attached.binding.documentId : crypto.randomUUID(),
|
|
480
|
+
revision: 1,
|
|
481
|
+
nodes: raw.nodes
|
|
482
|
+
};
|
|
433
483
|
const changes = diff(prior?.documentMarker === raw.marker ? prior.nodes : [], raw.nodes);
|
|
434
|
-
observations.set(
|
|
484
|
+
observations.set(stateKey, state);
|
|
435
485
|
const capture = visual === "always" || visual === "auto" && (raw.nodes.length === 0 || raw.hasVisualSurface);
|
|
436
486
|
const image = capture ? await captureScreenshot(tabId, { format: "jpeg", quality: 70}) : void 0;
|
|
437
487
|
return {
|
|
@@ -541,45 +591,92 @@ let reconnectTimer = null;
|
|
|
541
591
|
let keepAliveTimer = null;
|
|
542
592
|
let reconnectDelay = WS_RECONNECT_BASE_DELAY;
|
|
543
593
|
let intentionalClose = false;
|
|
544
|
-
|
|
594
|
+
let authenticatedConnectionId = null;
|
|
595
|
+
async function authenticate(socket, challenge) {
|
|
596
|
+
if (challenge.protocolVersion !== BROWSER_EXTENSION_PROTOCOL_VERSION) {
|
|
597
|
+
socket.close(4406, "Browser protocol mismatch");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const profile = await readProfile();
|
|
601
|
+
if (!profile) {
|
|
602
|
+
socket.close(4401, "Browser is not paired");
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const extensionVersion = chrome.runtime.getManifest().version;
|
|
606
|
+
const unsigned = {
|
|
607
|
+
connectionId: challenge.connectionId,
|
|
608
|
+
challenge: challenge.challenge,
|
|
609
|
+
issuedAt: challenge.issuedAt,
|
|
610
|
+
principalId: profile.deviceId,
|
|
611
|
+
extensionId: chrome.runtime.id,
|
|
612
|
+
extensionVersion
|
|
613
|
+
};
|
|
614
|
+
const message = {
|
|
615
|
+
type: "authenticate",
|
|
616
|
+
protocolVersion: BROWSER_EXTENSION_PROTOCOL_VERSION,
|
|
617
|
+
connectionId: challenge.connectionId,
|
|
618
|
+
principalId: profile.deviceId,
|
|
619
|
+
extensionId: chrome.runtime.id,
|
|
620
|
+
extensionVersion,
|
|
621
|
+
signature: await signBrowserBridgeChallenge(browserWireAuthenticationPayload(unsigned))
|
|
622
|
+
};
|
|
623
|
+
socket.send(JSON.stringify(message));
|
|
624
|
+
authenticatedConnectionId = challenge.connectionId;
|
|
625
|
+
sendStatus(profile.deviceId);
|
|
626
|
+
startKeepAlive();
|
|
627
|
+
}
|
|
628
|
+
async function connect() {
|
|
629
|
+
if (!await readProfile()) return;
|
|
545
630
|
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
|
|
546
631
|
intentionalClose = false;
|
|
632
|
+
authenticatedConnectionId = null;
|
|
547
633
|
const socket = new WebSocket(XOPC_EXT_WS_URL);
|
|
548
634
|
ws = socket;
|
|
549
635
|
socket.onopen = () => {
|
|
550
|
-
if (ws !== socket)
|
|
551
|
-
socket.close();
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
reconnectDelay = WS_RECONNECT_BASE_DELAY;
|
|
555
|
-
sendStatus();
|
|
556
|
-
startKeepAlive();
|
|
557
|
-
log.info("Connected to xopc daemon");
|
|
636
|
+
if (ws !== socket) socket.close();
|
|
558
637
|
};
|
|
559
638
|
socket.onmessage = async (event) => {
|
|
560
639
|
if (ws !== socket) return;
|
|
561
640
|
try {
|
|
562
|
-
const
|
|
563
|
-
|
|
641
|
+
const value = JSON.parse(String(event.data));
|
|
642
|
+
if (value.type === "auth_challenge") {
|
|
643
|
+
await authenticate(socket, value);
|
|
644
|
+
reconnectDelay = WS_RECONNECT_BASE_DELAY;
|
|
645
|
+
log.info("Authenticated with xopc browser bridge");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (!authenticatedConnectionId || value.connectionId !== authenticatedConnectionId) {
|
|
649
|
+
throw new Error("Browser command arrived before authentication or for a stale connection");
|
|
650
|
+
}
|
|
651
|
+
const result = await executeBrowserCommand(value);
|
|
564
652
|
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(result));
|
|
565
653
|
} catch (error) {
|
|
566
|
-
log.error("Failed to process browser
|
|
654
|
+
log.error("Failed to process browser bridge message", {
|
|
655
|
+
error: error instanceof Error ? error.message : String(error)
|
|
656
|
+
});
|
|
567
657
|
}
|
|
568
658
|
};
|
|
569
659
|
socket.onclose = (event) => {
|
|
570
660
|
if (ws !== socket) return;
|
|
571
661
|
stopKeepAlive();
|
|
572
662
|
ws = null;
|
|
573
|
-
|
|
574
|
-
|
|
663
|
+
authenticatedConnectionId = null;
|
|
664
|
+
log.warn("Disconnected from xopc browser bridge", {
|
|
665
|
+
code: event.code,
|
|
666
|
+
reason: event.reason,
|
|
667
|
+
wasClean: event.wasClean
|
|
668
|
+
});
|
|
669
|
+
if (!intentionalClose && event.code !== 4401) scheduleReconnect();
|
|
575
670
|
};
|
|
576
671
|
}
|
|
577
|
-
function sendStatus() {
|
|
578
|
-
if (ws?.readyState !== WebSocket.OPEN) return;
|
|
672
|
+
function sendStatus(principalId) {
|
|
673
|
+
if (ws?.readyState !== WebSocket.OPEN || !authenticatedConnectionId) return;
|
|
579
674
|
const status = {
|
|
580
675
|
type: "status",
|
|
581
676
|
protocolVersion: BROWSER_EXTENSION_PROTOCOL_VERSION,
|
|
582
677
|
extensionVersion: chrome.runtime.getManifest().version,
|
|
678
|
+
connectionId: authenticatedConnectionId,
|
|
679
|
+
principalId,
|
|
583
680
|
connected: true,
|
|
584
681
|
sessionCount: automationSessions.size
|
|
585
682
|
};
|
|
@@ -588,11 +685,15 @@ function sendStatus() {
|
|
|
588
685
|
function startKeepAlive() {
|
|
589
686
|
stopKeepAlive();
|
|
590
687
|
keepAliveTimer = setInterval(() => {
|
|
591
|
-
if (ws?.readyState !== WebSocket.OPEN) {
|
|
688
|
+
if (ws?.readyState !== WebSocket.OPEN || !authenticatedConnectionId) {
|
|
592
689
|
stopKeepAlive();
|
|
593
690
|
return;
|
|
594
691
|
}
|
|
595
|
-
const message = {
|
|
692
|
+
const message = {
|
|
693
|
+
type: "keepalive",
|
|
694
|
+
connectionId: authenticatedConnectionId,
|
|
695
|
+
timestamp: Date.now()
|
|
696
|
+
};
|
|
596
697
|
ws.send(JSON.stringify(message));
|
|
597
698
|
}, WS_KEEPALIVE_INTERVAL);
|
|
598
699
|
}
|
|
@@ -605,7 +706,7 @@ function scheduleReconnect() {
|
|
|
605
706
|
reconnectTimer = setTimeout(() => {
|
|
606
707
|
reconnectTimer = null;
|
|
607
708
|
reconnectDelay = Math.min(reconnectDelay * 1.5, WS_RECONNECT_MAX_DELAY);
|
|
608
|
-
connect();
|
|
709
|
+
void connect();
|
|
609
710
|
}, reconnectDelay);
|
|
610
711
|
}
|
|
611
712
|
function disconnect() {
|
|
@@ -615,26 +716,57 @@ function disconnect() {
|
|
|
615
716
|
reconnectTimer = null;
|
|
616
717
|
ws?.close();
|
|
617
718
|
ws = null;
|
|
719
|
+
authenticatedConnectionId = null;
|
|
618
720
|
}
|
|
619
721
|
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
620
|
-
if (message.type === "
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
sendResponse({ ok: true });
|
|
624
|
-
} else if (message.type === "popup/disconnect") {
|
|
722
|
+
if (message.type === "browser/get-status") {
|
|
723
|
+
sendResponse({ connected: Boolean(authenticatedConnectionId), url: XOPC_EXT_WS_URL });
|
|
724
|
+
} else if (message.type === "browser/reconnect") {
|
|
625
725
|
disconnect();
|
|
726
|
+
intentionalClose = false;
|
|
727
|
+
void connect();
|
|
626
728
|
sendResponse({ ok: true });
|
|
627
|
-
} else
|
|
729
|
+
} else {
|
|
730
|
+
sendResponse({ ok: false, error: `Unknown message: ${message.type}` });
|
|
731
|
+
}
|
|
628
732
|
return true;
|
|
629
733
|
});
|
|
630
|
-
chrome.
|
|
631
|
-
|
|
734
|
+
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
|
735
|
+
if (info.menuItemId !== "xopc-ask-selection" || !tab?.id) return;
|
|
736
|
+
const tabId = tab.id;
|
|
737
|
+
void captureTabPage(tabId, "selection").then(async (context) => {
|
|
738
|
+
await chrome.storage.session.set({
|
|
739
|
+
[PENDING_CONTEXT_KEY]: { context, tabId, source: "current_selection" }
|
|
740
|
+
});
|
|
741
|
+
await chrome.sidePanel.open({ tabId });
|
|
742
|
+
}).catch((error) => log.warn("Could not attach selected page text", {
|
|
743
|
+
error: error instanceof Error ? error.message : String(error)
|
|
744
|
+
}));
|
|
745
|
+
});
|
|
746
|
+
chrome.runtime.onInstalled.addListener(() => {
|
|
747
|
+
void chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
|
|
748
|
+
chrome.contextMenus.removeAll(() => {
|
|
749
|
+
chrome.contextMenus.create({
|
|
750
|
+
id: "xopc-ask-selection",
|
|
751
|
+
title: "Ask xopc about “%s”",
|
|
752
|
+
contexts: ["selection"]
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
void connect();
|
|
756
|
+
});
|
|
757
|
+
chrome.runtime.onStartup.addListener(() => {
|
|
758
|
+
void connect();
|
|
759
|
+
});
|
|
760
|
+
chrome.storage.onChanged.addListener((changes, area) => {
|
|
761
|
+
if (area === "local" && changes["xopc.browser.profile"]) {
|
|
762
|
+
disconnect();
|
|
763
|
+
intentionalClose = false;
|
|
764
|
+
void connect();
|
|
765
|
+
}
|
|
766
|
+
});
|
|
632
767
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
633
|
-
if (alarm.name === WS_WATCHDOG_ALARM && !intentionalClose) connect();
|
|
768
|
+
if (alarm.name === WS_WATCHDOG_ALARM && !intentionalClose) void connect();
|
|
634
769
|
});
|
|
635
770
|
void chrome.alarms.create(WS_WATCHDOG_ALARM, { periodInMinutes: WS_WATCHDOG_PERIOD_MINUTES });
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
disconnect();
|
|
639
|
-
void closeAllSessions();
|
|
640
|
-
});
|
|
771
|
+
void chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
|
|
772
|
+
void connect();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>xopc</title>
|
|
7
|
+
<script type="module" crossorigin src="/sidepanel.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/assets/auth-CknJ00ae.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/sidepanel-C69P3v9u.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|