@arbidocs/blocks 0.5.126 → 0.5.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/{ConnectedCallDock-733O5M36.cjs → ConnectedCallDock-XJOYVVAH.cjs} +5 -5
- package/dist/{ConnectedCallDock-733O5M36.cjs.map → ConnectedCallDock-XJOYVVAH.cjs.map} +1 -1
- package/dist/{ConnectedCallRoom-TAUGSRGV.cjs → ConnectedCallRoom-CUDYAPQK.cjs} +5 -5
- package/dist/{ConnectedCallRoom-TAUGSRGV.cjs.map → ConnectedCallRoom-CUDYAPQK.cjs.map} +1 -1
- package/dist/{ConnectedNativeDocxComparison-HDE522YC.cjs → ConnectedNativeDocxComparison-JUF2TI2P.cjs} +9 -9
- package/dist/{ConnectedNativeDocxComparison-HDE522YC.cjs.map → ConnectedNativeDocxComparison-JUF2TI2P.cjs.map} +1 -1
- package/dist/ConnectedPresentation-SI3L77J4.cjs +1 -1
- package/dist/{ConnectedSlidesTab-JSPU6EY5.cjs → ConnectedSlidesTab-E76OVY34.cjs} +3 -3
- package/dist/{ConnectedSlidesTab-JSPU6EY5.cjs.map → ConnectedSlidesTab-E76OVY34.cjs.map} +1 -1
- package/dist/{ConnectedStreamedWordArtifact-4FHPOAFZ.cjs → ConnectedStreamedWordArtifact-Q64DRRQD.cjs} +9 -9
- package/dist/{ConnectedStreamedWordArtifact-4FHPOAFZ.cjs.map → ConnectedStreamedWordArtifact-Q64DRRQD.cjs.map} +1 -1
- package/dist/{DocumentReviewCard-BDNXP3D4.cjs → DocumentReviewCard-M53XFLI2.cjs} +4 -4
- package/dist/{DocumentReviewCard-BDNXP3D4.cjs.map → DocumentReviewCard-M53XFLI2.cjs.map} +1 -1
- package/dist/{FilesViewerDialog-WQRNSKWM.cjs → FilesViewerDialog-V6FSRQTP.cjs} +8 -8
- package/dist/{FilesViewerDialog-WQRNSKWM.cjs.map → FilesViewerDialog-V6FSRQTP.cjs.map} +1 -1
- package/dist/{NativeDocxEditor-SUDULUN3.cjs → NativeDocxEditor-2PTEK6ND.cjs} +6 -6
- package/dist/{NativeDocxEditor-SUDULUN3.cjs.map → NativeDocxEditor-2PTEK6ND.cjs.map} +1 -1
- package/dist/WorkspaceTableView-3XYAV4BV.cjs +1 -1
- package/dist/agents/index.cjs +1 -1
- package/dist/ai-IEIR7SC2.cjs +1 -1
- package/dist/calls/index.cjs +2 -2
- package/dist/{chunk-Q6N5D3R5.cjs → chunk-23I5EUZA.cjs} +45 -15
- package/dist/chunk-23I5EUZA.cjs.map +1 -0
- package/dist/{chunk-OHHKBURG.cjs → chunk-2SU37RZA.cjs} +378 -11
- package/dist/chunk-2SU37RZA.cjs.map +1 -0
- package/dist/{chunk-6B4CCDDB.cjs → chunk-ESVRVZ53.cjs} +5 -5
- package/dist/{chunk-6B4CCDDB.cjs.map → chunk-ESVRVZ53.cjs.map} +1 -1
- package/dist/{chunk-KA3F6TIH.cjs → chunk-IWHP7FAB.cjs} +12 -5
- package/dist/chunk-IWHP7FAB.cjs.map +1 -0
- package/dist/{chunk-FB3JXSQT.cjs → chunk-JVLOIIA7.cjs} +8 -8
- package/dist/{chunk-FB3JXSQT.cjs.map → chunk-JVLOIIA7.cjs.map} +1 -1
- package/dist/{chunk-FGOJBOZX.cjs → chunk-M6WVIGYP.cjs} +4 -4
- package/dist/{chunk-FGOJBOZX.cjs.map → chunk-M6WVIGYP.cjs.map} +1 -1
- package/dist/{chunk-I4X72WUT.cjs → chunk-MMSHIIKG.cjs} +6 -6
- package/dist/{chunk-I4X72WUT.cjs.map → chunk-MMSHIIKG.cjs.map} +1 -1
- package/dist/{chunk-LXM5QXII.cjs → chunk-MXBXZXXQ.cjs} +3 -3
- package/dist/{chunk-LXM5QXII.cjs.map → chunk-MXBXZXXQ.cjs.map} +1 -1
- package/dist/{chunk-463QM764.cjs → chunk-NG2EE7HR.cjs} +3 -3
- package/dist/{chunk-463QM764.cjs.map → chunk-NG2EE7HR.cjs.map} +1 -1
- package/dist/chunk-NTDQQLDD.cjs +18 -0
- package/dist/{chunk-OALQGO5C.cjs.map → chunk-NTDQQLDD.cjs.map} +1 -1
- package/dist/{chunk-XNB27POJ.cjs → chunk-Q6IKXWPG.cjs} +4 -4
- package/dist/{chunk-XNB27POJ.cjs.map → chunk-Q6IKXWPG.cjs.map} +1 -1
- package/dist/{chunk-LOB2ATS5.cjs → chunk-VLQCLQAS.cjs} +5 -5
- package/dist/{chunk-LOB2ATS5.cjs.map → chunk-VLQCLQAS.cjs.map} +1 -1
- package/dist/{chunk-4KEQ62SL.cjs → chunk-YP5EBGLU.cjs} +6 -6
- package/dist/{chunk-4KEQ62SL.cjs.map → chunk-YP5EBGLU.cjs.map} +1 -1
- package/dist/{chunk-CMPHJRVV.cjs → chunk-ZYGAXD3N.cjs} +3 -3
- package/dist/{chunk-CMPHJRVV.cjs.map → chunk-ZYGAXD3N.cjs.map} +1 -1
- package/dist/connect/docx.cjs +13 -13
- package/dist/connect/index.cjs +233 -287
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/viewer.cjs +6 -6
- package/dist/connection/remote-control/ConnectedRemoteControlConsent.js +6 -3
- package/dist/connection/remote-control/ConnectedRemoteControlConsent.js.map +2 -2
- package/dist/connection/remote-control/ConnectedRemoteControlIndicator.js +12 -2
- package/dist/connection/remote-control/ConnectedRemoteControlIndicator.js.map +2 -2
- package/dist/connection/remote-control/ConnectedRemoteControlSwitch.js +1 -1
- package/dist/connection/remote-control/ConnectedRemoteControlSwitch.js.map +1 -1
- package/dist/connection/remote-control/browser-diagnostics.d.ts +7 -0
- package/dist/connection/remote-control/browser-diagnostics.js +109 -0
- package/dist/connection/remote-control/browser-diagnostics.js.map +7 -0
- package/dist/connection/remote-control/remoteControl.d.ts +9 -1
- package/dist/connection/remote-control/remoteControl.js +130 -10
- package/dist/connection/remote-control/remoteControl.js.map +2 -2
- package/dist/connection/remote-control/screen-sharing.d.ts +11 -0
- package/dist/connection/remote-control/screen-sharing.js +78 -0
- package/dist/connection/remote-control/screen-sharing.js.map +7 -0
- package/dist/connection/report-problem/screen-capture.d.ts +1 -0
- package/dist/connection/report-problem/screen-capture.js +2 -1
- package/dist/connection/report-problem/screen-capture.js.map +2 -2
- package/dist/docx/NativeDocxEditor.js +14 -12
- package/dist/docx/NativeDocxEditor.js.map +2 -2
- package/dist/docx/index.cjs +38 -38
- package/dist/docx/useVersionAutosave.d.ts +10 -0
- package/dist/docx/useVersionAutosave.js +36 -0
- package/dist/docx/useVersionAutosave.js.map +7 -0
- package/dist/embed/index.cjs +9 -9
- package/dist/facts/index.cjs +43 -43
- package/dist/index.cjs +97 -97
- package/dist/settings/index.cjs +1 -1
- package/dist/shell/RemoteControlPill.d.ts +2 -1
- package/dist/shell/RemoteControlPill.js +10 -3
- package/dist/shell/RemoteControlPill.js.map +2 -2
- package/dist/shell/index.cjs +21 -21
- package/dist/studio/index.cjs +1 -1
- package/dist/workflow-canvas/index.cjs +12 -12
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +5 -5
- package/dist/chunk-KA3F6TIH.cjs.map +0 -1
- package/dist/chunk-OALQGO5C.cjs +0 -18
- package/dist/chunk-OHHKBURG.cjs.map +0 -1
- package/dist/chunk-Q6N5D3R5.cjs.map +0 -1
package/dist/connect/viewer.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunkJVLOIIA7_cjs = require('../chunk-JVLOIIA7.cjs');
|
|
5
5
|
require('../chunk-JITYC4OS.cjs');
|
|
6
6
|
require('../chunk-WVZLAAIZ.cjs');
|
|
7
7
|
require('../chunk-X7ALF4MP.cjs');
|
|
@@ -17,7 +17,7 @@ require('../chunk-HYUKPHCT.cjs');
|
|
|
17
17
|
require('../chunk-ALK3PUUB.cjs');
|
|
18
18
|
require('../chunk-223D4727.cjs');
|
|
19
19
|
require('../chunk-SNUBQMIS.cjs');
|
|
20
|
-
require('../chunk-
|
|
20
|
+
require('../chunk-VLQCLQAS.cjs');
|
|
21
21
|
require('../chunk-4ONPEVTP.cjs');
|
|
22
22
|
require('../chunk-V3KLUWQQ.cjs');
|
|
23
23
|
require('../chunk-P5AQYC4D.cjs');
|
|
@@ -30,7 +30,7 @@ require('../chunk-DTMBAFIL.cjs');
|
|
|
30
30
|
require('../chunk-ZESMRGDV.cjs');
|
|
31
31
|
require('../chunk-VCEX7COE.cjs');
|
|
32
32
|
require('../chunk-34HQO4ZQ.cjs');
|
|
33
|
-
require('../chunk-
|
|
33
|
+
require('../chunk-IWHP7FAB.cjs');
|
|
34
34
|
require('../chunk-K4763GD5.cjs');
|
|
35
35
|
require('../chunk-2RLA6QO2.cjs');
|
|
36
36
|
require('../chunk-MZZ3SBQL.cjs');
|
|
@@ -43,7 +43,7 @@ require('../chunk-SK6HQ46Q.cjs');
|
|
|
43
43
|
require('../chunk-MNIQDXM6.cjs');
|
|
44
44
|
require('../chunk-E3Y7RI3J.cjs');
|
|
45
45
|
require('../chunk-TG2GM7WJ.cjs');
|
|
46
|
-
require('../chunk-
|
|
46
|
+
require('../chunk-ZYGAXD3N.cjs');
|
|
47
47
|
require('../chunk-OEJQFPWO.cjs');
|
|
48
48
|
require('../chunk-OYXWQTHF.cjs');
|
|
49
49
|
require('../chunk-S237ORM3.cjs');
|
|
@@ -63,12 +63,12 @@ require('../chunk-VG24E4JJ.cjs');
|
|
|
63
63
|
require('../chunk-U2FFG2B3.cjs');
|
|
64
64
|
require('../chunk-KKYNTWDU.cjs');
|
|
65
65
|
require('../chunk-M74FWYNL.cjs');
|
|
66
|
+
require('../chunk-VZOOGA47.cjs');
|
|
66
67
|
require('../chunk-HQDR43JC.cjs');
|
|
67
68
|
require('../chunk-JR2YSBD5.cjs');
|
|
68
69
|
require('../chunk-3YZKDCWY.cjs');
|
|
69
70
|
require('../chunk-SJSGSHIT.cjs');
|
|
70
71
|
require('../chunk-ZW6FPIFC.cjs');
|
|
71
|
-
require('../chunk-VZOOGA47.cjs');
|
|
72
72
|
require('../chunk-CT3SLXB7.cjs');
|
|
73
73
|
require('../chunk-DEXUL3T6.cjs');
|
|
74
74
|
require('../chunk-AFQTQC7A.cjs');
|
|
@@ -94,7 +94,7 @@ require('../chunk-PLIF5CUV.cjs');
|
|
|
94
94
|
|
|
95
95
|
Object.defineProperty(exports, "ConnectedDocumentViewer", {
|
|
96
96
|
enumerable: true,
|
|
97
|
-
get: function () { return
|
|
97
|
+
get: function () { return chunkJVLOIIA7_cjs.ConnectedDocumentViewer; }
|
|
98
98
|
});
|
|
99
99
|
Object.defineProperty(exports, "ConnectedDocumentToolsProvider", {
|
|
100
100
|
enumerable: true,
|
|
@@ -18,6 +18,8 @@ import { useGuidedRuns } from "../../guided/useGuidedRuns.js";
|
|
|
18
18
|
import { useRemoteControlActions } from "./remoteControl.js";
|
|
19
19
|
function RunPlan({ request }) {
|
|
20
20
|
const { sequences } = useGuidedRuns();
|
|
21
|
+
if (request.screenRequestId)
|
|
22
|
+
return /* @__PURE__ */ jsx("p", { "data-testid": "remote-control-consent-plan", children: request.screenQuestion });
|
|
21
23
|
const steps = request.steps ?? [];
|
|
22
24
|
if (steps.length === 0) {
|
|
23
25
|
const label = request.sequenceId ? sequences[request.sequenceId]?.label : void 0;
|
|
@@ -75,7 +77,7 @@ function ConsentPrompt() {
|
|
|
75
77
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
76
78
|
/* @__PURE__ */ jsx("span", { className: "font-semibold", "data-testid": "remote-control-consent-agent-name", children: pending.agentName }),
|
|
77
79
|
" ",
|
|
78
|
-
"wants to control your screen"
|
|
80
|
+
pending.screenRequestId ? "wants to view and control your screen" : "wants to control your screen"
|
|
79
81
|
] })
|
|
80
82
|
] }),
|
|
81
83
|
/* @__PURE__ */ jsxs(DialogDescription, { className: "sr-only", children: [
|
|
@@ -86,6 +88,7 @@ function ConsentPrompt() {
|
|
|
86
88
|
/* @__PURE__ */ jsxs(ModalSection, { className: "space-y-3 text-sm text-muted-foreground", children: [
|
|
87
89
|
/* @__PURE__ */ jsx("p", { children: "Remote control lets this assistant click and type in the app on your behalf \u2014 you'll see the cursor move and click as it works." }),
|
|
88
90
|
/* @__PURE__ */ jsx("p", { children: "You can turn it off at any time, and it is off by default at the start of every session." }),
|
|
91
|
+
pending.screenRequestId && /* @__PURE__ */ jsx("p", { children: "Choose a tab or window to share. While sharing, the assistant can inspect fresh images, recent ARBI console errors and failed requests, and click or type inside ARBI. Stop sharing ends viewing and control together. No audio or recording is captured." }),
|
|
89
92
|
/* @__PURE__ */ jsx(RunPlan, { request: pending }),
|
|
90
93
|
pending.agentExtId && /* @__PURE__ */ jsxs("p", { className: "text-xs", children: [
|
|
91
94
|
"Requesting agent:",
|
|
@@ -107,7 +110,7 @@ function ConsentPrompt() {
|
|
|
107
110
|
className: "w-full",
|
|
108
111
|
onClick: () => remote.grantAndRun({ forSession: true }),
|
|
109
112
|
"data-testid": "remote-control-consent-allow-session",
|
|
110
|
-
children: "Allow for this session"
|
|
113
|
+
children: pending.screenRequestId ? "Start sharing" : "Allow for this session"
|
|
111
114
|
}
|
|
112
115
|
),
|
|
113
116
|
/* @__PURE__ */ jsx(
|
|
@@ -118,7 +121,7 @@ function ConsentPrompt() {
|
|
|
118
121
|
className: "w-full",
|
|
119
122
|
onClick: () => remote.grantAndRun({ forSession: false }),
|
|
120
123
|
"data-testid": "remote-control-consent-allow-once",
|
|
121
|
-
children: "Just this once"
|
|
124
|
+
children: pending.screenRequestId ? "Share one image" : "Just this once"
|
|
122
125
|
}
|
|
123
126
|
),
|
|
124
127
|
/* @__PURE__ */ jsx(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/remote-control/ConnectedRemoteControlConsent.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * ConnectedRemoteControlConsent \u2014 the consent gate for backend-driven remote control.\n *\n * Opens whenever an `agt-\u2026` agent has requested to drive the UI over the websocket and remote\n * control is not already enabled this session (the request sits in the session's remote-control\n * `pending`). It names the requesting agent explicitly (GUARANTEE #3) so the user knows which of\n * their assistants is asking, and offers three choices:\n * - Allow for this session (primary) \u2014 enables it until the session ends.\n * - Just this once \u2014 runs the one requested action, stays off afterwards.\n * - Deny (cancel) \u2014 runs nothing.\n * It says what will run (the run's name, or the inline steps' notes and targets), and opens on\n * \"Just this once\", so Enter never grants the whole session. Nothing shows while the site has not\n * turned remote control on (`guided.remoteControl`).\n *\n * Mounted once by `ConnectedAppRuntime`. Its state lives in the session, so no props.\n */\n\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Button,\n} from '@arbidocs/react/ui'\nimport { useRef } from 'react'\nimport { useRemoteControl, type RemoteControlRequest } from '@arbidocs/react'\nimport { MousePointerClick } from 'lucide-react'\nimport { ModalSection } from '../../settings/ModalScaffold'\nimport { REMOTE_CONTROL_SURFACE, useMountedSurface } from '../../guided/surfaces'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\n/** What the request will run: the registered run's name, or its inline steps. */\nfunction RunPlan({ request }: { request: RemoteControlRequest }) {\n const { sequences } = useGuidedRuns()\n const steps = request.steps ?? []\n if (steps.length === 0) {\n const label = request.sequenceId ? sequences[request.sequenceId]?.label : undefined\n return (\n <p data-testid=\"remote-control-consent-plan\">\n It will run:{' '}\n <span className=\"font-medium text-foreground\">{label ?? request.sequenceId}</span>\n </p>\n )\n }\n // Every step, however many (the list scrolls): nothing runs that the user was not shown.\n return (\n <div data-testid=\"remote-control-consent-plan\">\n <p>\n It will ({steps.length} {steps.length === 1 ? 'step' : 'steps'}):\n </p>\n <ol className=\"mt-1 max-h-40 list-decimal space-y-0.5 overflow-y-auto pl-5 text-xs\">\n {steps.map((step, i) => (\n <li key={i}>\n {step.do === 'type' ? 'Type into' : 'Click'}{' '}\n <span className=\"break-all font-mono text-foreground\">{step.target}</span>\n {step.note ? <> \u2014 {step.note}</> : null}\n </li>\n ))}\n </ol>\n </div>\n )\n}\n\nexport function ConnectedRemoteControlConsent(): React.ReactElement | null {\n return useGuidedRuns().remoteControl ? <ConsentPrompt /> : null\n}\n\nfunction ConsentPrompt(): React.ReactElement | null {\n const pending = useRemoteControl((s) => s.pending)\n const remote = useRemoteControlActions()\n const onceRef = useRef<HTMLButtonElement>(null)\n // A remote request is refused while no prompt is on screen to ask.\n useMountedSurface('consent')\n\n // Closing the dialog by any means (Escape, overlay, X) is a denial.\n const handleOpenChange = (next: boolean): void => {\n if (!next) remote.deny()\n }\n\n if (!pending) return null\n\n return (\n <Dialog open={pending !== null} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-md\"\n data-testid=\"remote-control-consent-modal\"\n {...{ [REMOTE_CONTROL_SURFACE]: '' }}\n // The safest choice has the focus: Enter runs it once, never grants the session.\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n onceRef.current?.focus()\n }}\n >\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <MousePointerClick className=\"h-5 w-5 text-primary\" />\n <span>\n <span className=\"font-semibold\" data-testid=\"remote-control-consent-agent-name\">\n {pending.agentName}\n </span>{' '}\n wants to control your screen\n </span>\n </DialogTitle>\n <DialogDescription className=\"sr-only\">\n {pending.agentName} is requesting permission to click and type in the app on your\n behalf. Choose whether to allow it for this session, just this once, or deny.\n </DialogDescription>\n </DialogHeader>\n\n <ModalSection className=\"space-y-3 text-sm text-muted-foreground\">\n <p>\n Remote control lets this assistant click and type in the app on your behalf \u2014\n you'll see the cursor move and click as it works.\n </p>\n <p>\n You can turn it off at any time, and it is off by default at the start of every session.\n </p>\n <RunPlan request={pending} />\n {pending.agentExtId && (\n <p className=\"text-xs\">\n Requesting agent:{' '}\n <span\n className=\"font-mono text-foreground\"\n data-testid=\"remote-control-consent-agent-id\"\n >\n {pending.agentExtId}\n </span>\n </p>\n )}\n </ModalSection>\n\n <DialogFooter className=\"flex-col gap-2 sm:flex-col sm:space-x-0\">\n <Button\n className=\"w-full\"\n onClick={() => remote.grantAndRun({ forSession: true })}\n data-testid=\"remote-control-consent-allow-session\"\n >\n Allow for this session\n </Button>\n <Button\n ref={onceRef}\n variant=\"outline\"\n className=\"w-full\"\n onClick={() => remote.grantAndRun({ forSession: false })}\n data-testid=\"remote-control-consent-allow-once\"\n >\n Just this once\n </Button>\n <Button\n variant=\"ghost\"\n className=\"w-full\"\n onClick={() => remote.deny()}\n data-testid=\"remote-control-consent-deny\"\n >\n Deny\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * ConnectedRemoteControlConsent \u2014 the consent gate for backend-driven remote control.\n *\n * Opens whenever an `agt-\u2026` agent has requested to drive the UI over the websocket and remote\n * control is not already enabled this session (the request sits in the session's remote-control\n * `pending`). It names the requesting agent explicitly (GUARANTEE #3) so the user knows which of\n * their assistants is asking, and offers three choices:\n * - Allow for this session (primary) \u2014 enables it until the session ends.\n * - Just this once \u2014 runs the one requested action, stays off afterwards.\n * - Deny (cancel) \u2014 runs nothing.\n * It says what will run (the run's name, or the inline steps' notes and targets), and opens on\n * \"Just this once\", so Enter never grants the whole session. Nothing shows while the site has not\n * turned remote control on (`guided.remoteControl`).\n *\n * Mounted once by `ConnectedAppRuntime`. Its state lives in the session, so no props.\n */\n\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Button,\n} from '@arbidocs/react/ui'\nimport { useRef } from 'react'\nimport { useRemoteControl, type RemoteControlRequest } from '@arbidocs/react'\nimport { MousePointerClick } from 'lucide-react'\nimport { ModalSection } from '../../settings/ModalScaffold'\nimport { REMOTE_CONTROL_SURFACE, useMountedSurface } from '../../guided/surfaces'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\n/** What the request will run: the registered run's name, or its inline steps. */\nfunction RunPlan({ request }: { request: RemoteControlRequest }) {\n const { sequences } = useGuidedRuns()\n if (request.screenRequestId)\n return <p data-testid=\"remote-control-consent-plan\">{request.screenQuestion}</p>\n const steps = request.steps ?? []\n if (steps.length === 0) {\n const label = request.sequenceId ? sequences[request.sequenceId]?.label : undefined\n return (\n <p data-testid=\"remote-control-consent-plan\">\n It will run:{' '}\n <span className=\"font-medium text-foreground\">{label ?? request.sequenceId}</span>\n </p>\n )\n }\n // Every step, however many (the list scrolls): nothing runs that the user was not shown.\n return (\n <div data-testid=\"remote-control-consent-plan\">\n <p>\n It will ({steps.length} {steps.length === 1 ? 'step' : 'steps'}):\n </p>\n <ol className=\"mt-1 max-h-40 list-decimal space-y-0.5 overflow-y-auto pl-5 text-xs\">\n {steps.map((step, i) => (\n <li key={i}>\n {step.do === 'type' ? 'Type into' : 'Click'}{' '}\n <span className=\"break-all font-mono text-foreground\">{step.target}</span>\n {step.note ? <> \u2014 {step.note}</> : null}\n </li>\n ))}\n </ol>\n </div>\n )\n}\n\nexport function ConnectedRemoteControlConsent(): React.ReactElement | null {\n return useGuidedRuns().remoteControl ? <ConsentPrompt /> : null\n}\n\nfunction ConsentPrompt(): React.ReactElement | null {\n const pending = useRemoteControl((s) => s.pending)\n const remote = useRemoteControlActions()\n const onceRef = useRef<HTMLButtonElement>(null)\n // A remote request is refused while no prompt is on screen to ask.\n useMountedSurface('consent')\n\n // Closing the dialog by any means (Escape, overlay, X) is a denial.\n const handleOpenChange = (next: boolean): void => {\n if (!next) remote.deny()\n }\n\n if (!pending) return null\n\n return (\n <Dialog open={pending !== null} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-md\"\n data-testid=\"remote-control-consent-modal\"\n {...{ [REMOTE_CONTROL_SURFACE]: '' }}\n // The safest choice has the focus: Enter runs it once, never grants the session.\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n onceRef.current?.focus()\n }}\n >\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <MousePointerClick className=\"h-5 w-5 text-primary\" />\n <span>\n <span className=\"font-semibold\" data-testid=\"remote-control-consent-agent-name\">\n {pending.agentName}\n </span>{' '}\n {pending.screenRequestId\n ? 'wants to view and control your screen'\n : 'wants to control your screen'}\n </span>\n </DialogTitle>\n <DialogDescription className=\"sr-only\">\n {pending.agentName} is requesting permission to click and type in the app on your\n behalf. Choose whether to allow it for this session, just this once, or deny.\n </DialogDescription>\n </DialogHeader>\n\n <ModalSection className=\"space-y-3 text-sm text-muted-foreground\">\n <p>\n Remote control lets this assistant click and type in the app on your behalf \u2014\n you'll see the cursor move and click as it works.\n </p>\n <p>\n You can turn it off at any time, and it is off by default at the start of every session.\n </p>\n {pending.screenRequestId && (\n <p>\n Choose a tab or window to share. While sharing, the assistant can inspect fresh\n images, recent ARBI console errors and failed requests, and click or type inside ARBI.\n Stop sharing ends viewing and control together. No audio or recording is captured.\n </p>\n )}\n <RunPlan request={pending} />\n {pending.agentExtId && (\n <p className=\"text-xs\">\n Requesting agent:{' '}\n <span\n className=\"font-mono text-foreground\"\n data-testid=\"remote-control-consent-agent-id\"\n >\n {pending.agentExtId}\n </span>\n </p>\n )}\n </ModalSection>\n\n <DialogFooter className=\"flex-col gap-2 sm:flex-col sm:space-x-0\">\n <Button\n className=\"w-full\"\n onClick={() => remote.grantAndRun({ forSession: true })}\n data-testid=\"remote-control-consent-allow-session\"\n >\n {pending.screenRequestId ? 'Start sharing' : 'Allow for this session'}\n </Button>\n <Button\n ref={onceRef}\n variant=\"outline\"\n className=\"w-full\"\n onClick={() => remote.grantAndRun({ forSession: false })}\n data-testid=\"remote-control-consent-allow-once\"\n >\n {pending.screenRequestId ? 'Share one image' : 'Just this once'}\n </Button>\n <Button\n variant=\"ghost\"\n className=\"w-full\"\n onClick={() => remote.deny()}\n data-testid=\"remote-control-consent-deny\"\n >\n Deny\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,12 +9,22 @@ function ConnectedRemoteControlIndicator() {
|
|
|
9
9
|
}
|
|
10
10
|
function Indicator() {
|
|
11
11
|
const enabled = useRemoteControl((s) => s.enabled);
|
|
12
|
+
const sharing = useRemoteControl((s) => !!s.sharing);
|
|
12
13
|
const controllingAgent = useRemoteControl((s) => s.controllingAgent);
|
|
13
14
|
const remote = useRemoteControlActions();
|
|
14
15
|
if (!enabled && !controllingAgent) return null;
|
|
15
16
|
const isActive = controllingAgent !== null;
|
|
16
|
-
const label = isActive ? `${controllingAgent.name} is controlling your screen` : "Remote control on";
|
|
17
|
-
return /* @__PURE__ */ jsx(
|
|
17
|
+
const label = sharing ? controllingAgent ? `${controllingAgent.name} \xB7 screen sharing and control on` : "Sharing screen \xB7 remote control on" : isActive ? `${controllingAgent.name} is controlling your screen` : "Remote control on";
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
RemoteControlPill,
|
|
20
|
+
{
|
|
21
|
+
isActive: isActive || sharing,
|
|
22
|
+
label,
|
|
23
|
+
disableLabel: sharing ? "Stop sharing" : void 0,
|
|
24
|
+
showDisableLabel: sharing,
|
|
25
|
+
onDisable: () => remote.disable()
|
|
26
|
+
}
|
|
27
|
+
);
|
|
18
28
|
}
|
|
19
29
|
export {
|
|
20
30
|
ConnectedRemoteControlIndicator
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/remote-control/ConnectedRemoteControlIndicator.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * ConnectedRemoteControlIndicator: the persistent remote-control pill (`RemoteControlPill`) on the\n * session's state. Shown while remote control is enabled this session (or a run is executing);\n * labels WHO is driving while a websocket-driven run executes, and always offers an immediate\n * disable (GUARANTEE #5). Hidden entirely when remote control is off and no run is active, and\n * while the site has not turned remote control on (`guided.remoteControl`).\n */\n\nimport { useRemoteControl } from '@arbidocs/react'\nimport { RemoteControlPill } from '../../shell/RemoteControlPill'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\nexport function ConnectedRemoteControlIndicator(): React.ReactElement | null {\n return useGuidedRuns().remoteControl ? <Indicator /> : null\n}\n\nfunction Indicator(): React.ReactElement | null {\n const enabled = useRemoteControl((s) => s.enabled)\n const controllingAgent = useRemoteControl((s) => s.controllingAgent)\n const remote = useRemoteControlActions()\n\n if (!enabled && !controllingAgent) return null\n\n const isActive = controllingAgent !== null\n const label =
|
|
5
|
-
"mappings": ";AAcyC;AANzC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AAEjC,SAAS,kCAA6D;AAC3E,SAAO,cAAc,EAAE,gBAAgB,oBAAC,aAAU,IAAK;AACzD;AAEA,SAAS,YAAuC;AAC9C,QAAM,UAAU,iBAAiB,CAAC,MAAM,EAAE,OAAO;AACjD,QAAM,mBAAmB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB;AACnE,QAAM,SAAS,wBAAwB;AAEvC,MAAI,CAAC,WAAW,CAAC,iBAAkB,QAAO;AAE1C,QAAM,WAAW,qBAAqB;AACtC,QAAM,QAAQ,
|
|
4
|
+
"sourcesContent": ["/**\n * ConnectedRemoteControlIndicator: the persistent remote-control pill (`RemoteControlPill`) on the\n * session's state. Shown while remote control is enabled this session (or a run is executing);\n * labels WHO is driving while a websocket-driven run executes, and always offers an immediate\n * disable (GUARANTEE #5). Hidden entirely when remote control is off and no run is active, and\n * while the site has not turned remote control on (`guided.remoteControl`).\n */\n\nimport { useRemoteControl } from '@arbidocs/react'\nimport { RemoteControlPill } from '../../shell/RemoteControlPill'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\nexport function ConnectedRemoteControlIndicator(): React.ReactElement | null {\n return useGuidedRuns().remoteControl ? <Indicator /> : null\n}\n\nfunction Indicator(): React.ReactElement | null {\n const enabled = useRemoteControl((s) => s.enabled)\n const sharing = useRemoteControl((s) => !!s.sharing)\n const controllingAgent = useRemoteControl((s) => s.controllingAgent)\n const remote = useRemoteControlActions()\n\n if (!enabled && !controllingAgent) return null\n\n const isActive = controllingAgent !== null\n const label = sharing\n ? controllingAgent\n ? `${controllingAgent.name} \u00B7 screen sharing and control on`\n : 'Sharing screen \u00B7 remote control on'\n : isActive\n ? `${controllingAgent.name} is controlling your screen`\n : 'Remote control on'\n\n return (\n <RemoteControlPill\n isActive={isActive || sharing}\n label={label}\n disableLabel={sharing ? 'Stop sharing' : undefined}\n showDisableLabel={sharing}\n onDisable={() => remote.disable()}\n />\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAcyC;AANzC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AAEjC,SAAS,kCAA6D;AAC3E,SAAO,cAAc,EAAE,gBAAgB,oBAAC,aAAU,IAAK;AACzD;AAEA,SAAS,YAAuC;AAC9C,QAAM,UAAU,iBAAiB,CAAC,MAAM,EAAE,OAAO;AACjD,QAAM,UAAU,iBAAiB,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO;AACnD,QAAM,mBAAmB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB;AACnE,QAAM,SAAS,wBAAwB;AAEvC,MAAI,CAAC,WAAW,CAAC,iBAAkB,QAAO;AAE1C,QAAM,WAAW,qBAAqB;AACtC,QAAM,QAAQ,UACV,mBACE,GAAG,iBAAiB,IAAI,wCACxB,0CACF,WACE,GAAG,iBAAiB,IAAI,gCACxB;AAEN,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,YAAY;AAAA,MACtB;AAAA,MACA,cAAc,UAAU,iBAAiB;AAAA,MACzC,kBAAkB;AAAA,MAClB,WAAW,MAAM,OAAO,QAAQ;AAAA;AAAA,EAClC;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -18,7 +18,7 @@ function RemoteControlSwitch() {
|
|
|
18
18
|
label: "Remote control",
|
|
19
19
|
labelFor: "remoteControlEnabled",
|
|
20
20
|
...{ [REMOTE_CONTROL_SURFACE]: "" },
|
|
21
|
-
description: "Let an assistant
|
|
21
|
+
description: "Let an assistant help in the app. Screen sharing asks you to choose a tab or window; turning this off stops both sharing and control. Off by default every session.",
|
|
22
22
|
children: /* @__PURE__ */ jsx(
|
|
23
23
|
Switch,
|
|
24
24
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/remote-control/ConnectedRemoteControlSwitch.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * The remote-control switch of Settings \u2192 Privacy. Letting an assistant click and type in the app\n * is a privacy-adjacent permission, so it is switched there rather than from the account menu.\n * Off by default every session. Not shown while the site has not turned remote control on\n * (`guided.remoteControl`).\n */\nimport { useRemoteControl } from '@arbidocs/react'\nimport { Switch } from '@arbidocs/react/ui'\nimport { SettingsRow } from '../../settings/SettingsPanel'\nimport { REMOTE_CONTROL_SURFACE } from '../../guided/surfaces'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\nexport function ConnectedRemoteControlSwitch() {\n return useGuidedRuns().remoteControl ? <RemoteControlSwitch /> : null\n}\n\nfunction RemoteControlSwitch() {\n const enabled = useRemoteControl((state) => state.enabled)\n const remote = useRemoteControlActions()\n return (\n <SettingsRow\n label=\"Remote control\"\n labelFor=\"remoteControlEnabled\"\n // The whole row, its label too, is remote control's own: a remote run never acts on it.\n {...{ [REMOTE_CONTROL_SURFACE]: '' }}\n description=\"Let an assistant
|
|
4
|
+
"sourcesContent": ["/**\n * The remote-control switch of Settings \u2192 Privacy. Letting an assistant click and type in the app\n * is a privacy-adjacent permission, so it is switched there rather than from the account menu.\n * Off by default every session. Not shown while the site has not turned remote control on\n * (`guided.remoteControl`).\n */\nimport { useRemoteControl } from '@arbidocs/react'\nimport { Switch } from '@arbidocs/react/ui'\nimport { SettingsRow } from '../../settings/SettingsPanel'\nimport { REMOTE_CONTROL_SURFACE } from '../../guided/surfaces'\nimport { useGuidedRuns } from '../../guided/useGuidedRuns'\nimport { useRemoteControlActions } from './remoteControl'\n\nexport function ConnectedRemoteControlSwitch() {\n return useGuidedRuns().remoteControl ? <RemoteControlSwitch /> : null\n}\n\nfunction RemoteControlSwitch() {\n const enabled = useRemoteControl((state) => state.enabled)\n const remote = useRemoteControlActions()\n return (\n <SettingsRow\n label=\"Remote control\"\n labelFor=\"remoteControlEnabled\"\n // The whole row, its label too, is remote control's own: a remote run never acts on it.\n {...{ [REMOTE_CONTROL_SURFACE]: '' }}\n description=\"Let an assistant help in the app. Screen sharing asks you to choose a tab or window; turning this off stops both sharing and control. Off by default every session.\"\n >\n <Switch\n id=\"remoteControlEnabled\"\n checked={enabled}\n onCheckedChange={(value) => (value ? remote.enable() : remote.disable())}\n data-testid=\"toggle-remote-control\"\n {...{ [REMOTE_CONTROL_SURFACE]: '' }}\n />\n </SettingsRow>\n )\n}\n"],
|
|
5
5
|
"mappings": ";AAcyC;AARzC,SAAS,wBAAwB;AACjC,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,8BAA8B;AACvC,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AAEjC,SAAS,+BAA+B;AAC7C,SAAO,cAAc,EAAE,gBAAgB,oBAAC,uBAAoB,IAAK;AACnE;AAEA,SAAS,sBAAsB;AAC7B,QAAM,UAAU,iBAAiB,CAAC,UAAU,MAAM,OAAO;AACzD,QAAM,SAAS,wBAAwB;AACvC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,UAAS;AAAA,MAER,GAAG,EAAE,CAAC,sBAAsB,GAAG,GAAG;AAAA,MACnC,aAAY;AAAA,MAEZ;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,SAAS;AAAA,UACT,iBAAiB,CAAC,UAAW,QAAQ,OAAO,OAAO,IAAI,OAAO,QAAQ;AAAA,UACtE,eAAY;AAAA,UACX,GAAG,EAAE,CAAC,sBAAsB,GAAG,GAAG;AAAA;AAAA,MACrC;AAAA;AAAA,EACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Session-only browser evidence, collected only while the user is sharing. No bodies, headers or storage. */
|
|
2
|
+
export declare function collectBrowserDiagnostics(): {
|
|
3
|
+
snapshot: () => string;
|
|
4
|
+
stop: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function safeUrl(value: string): string;
|
|
7
|
+
export declare function sanitizeDiagnostic(value: string): string;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { formatErrorReport } from "../../settings/error-report.js";
|
|
3
|
+
function collectBrowserDiagnostics() {
|
|
4
|
+
const lines = [];
|
|
5
|
+
const add = (message) => {
|
|
6
|
+
lines.push(`${(/* @__PURE__ */ new Date()).toISOString()} ${sanitizeDiagnostic(message).slice(0, 1800)}`);
|
|
7
|
+
if (lines.length > 30) lines.shift();
|
|
8
|
+
};
|
|
9
|
+
const stringify = (value) => {
|
|
10
|
+
if (value instanceof Error)
|
|
11
|
+
return formatErrorReport({ error: value, url: safeUrl(location.href) });
|
|
12
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
13
|
+
return String(value);
|
|
14
|
+
return "[object omitted]";
|
|
15
|
+
};
|
|
16
|
+
const originalError = console.error;
|
|
17
|
+
const originalWarn = console.warn;
|
|
18
|
+
const error = (...args) => {
|
|
19
|
+
add(`error: ${args.map(stringify).join(" ")}`);
|
|
20
|
+
originalError.apply(console, args);
|
|
21
|
+
};
|
|
22
|
+
const warn = (...args) => {
|
|
23
|
+
add(`warning: ${args.map(stringify).join(" ")}`);
|
|
24
|
+
originalWarn.apply(console, args);
|
|
25
|
+
};
|
|
26
|
+
console.error = error;
|
|
27
|
+
console.warn = warn;
|
|
28
|
+
const onError = (event) => add(`uncaught: ${stringify(event.error ?? event.message)}`);
|
|
29
|
+
const onRejection = (event) => add(`unhandled rejection: ${stringify(event.reason)}`);
|
|
30
|
+
window.addEventListener("error", onError);
|
|
31
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
32
|
+
const originalFetch = window.fetch;
|
|
33
|
+
const wrappedFetch = async (...args) => {
|
|
34
|
+
const input = args[0];
|
|
35
|
+
const url = safeUrl(
|
|
36
|
+
typeof input === "string" ? input : input instanceof URL ? input.href : input.url
|
|
37
|
+
);
|
|
38
|
+
try {
|
|
39
|
+
const response = await originalFetch.apply(window, args);
|
|
40
|
+
if (!response.ok) add(`HTTP ${response.status}: ${url}`);
|
|
41
|
+
return response;
|
|
42
|
+
} catch (failure) {
|
|
43
|
+
add(`request failed: ${url}`);
|
|
44
|
+
throw failure;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
window.fetch = wrappedFetch;
|
|
48
|
+
let resources;
|
|
49
|
+
if (typeof PerformanceObserver !== "undefined") {
|
|
50
|
+
try {
|
|
51
|
+
resources = new PerformanceObserver((list) => {
|
|
52
|
+
for (const entry of list.getEntries()) {
|
|
53
|
+
const resource = entry;
|
|
54
|
+
if ("responseStatus" in resource && Number(resource.responseStatus) >= 400)
|
|
55
|
+
add(`HTTP ${resource.responseStatus}: ${safeUrl(resource.name)}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
resources.observe({ type: "resource" });
|
|
59
|
+
} catch {
|
|
60
|
+
resources = void 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
snapshot: () => [
|
|
65
|
+
`ARBI tab: ${safeUrl(location.href)}`,
|
|
66
|
+
`Viewport: ${innerWidth} x ${innerHeight}; DPR ${devicePixelRatio}`,
|
|
67
|
+
"Visible ARBI controls (selectors for control_ui):",
|
|
68
|
+
...Array.from(
|
|
69
|
+
document.querySelectorAll(
|
|
70
|
+
'button[data-testid],a[data-testid],input[data-testid]:not([type="password"]),select[data-testid],[role="button"][data-testid]'
|
|
71
|
+
)
|
|
72
|
+
).filter(
|
|
73
|
+
(el) => el.getBoundingClientRect().width > 0 && !el.closest("[data-remote-control-surface]")
|
|
74
|
+
).slice(0, 60).map(
|
|
75
|
+
(el) => `[data-testid=${JSON.stringify(el.dataset.testid)}] ${sanitizeDiagnostic(el.getAttribute("aria-label") ?? el.getAttribute("title") ?? el.textContent ?? "").slice(0, 80)}`
|
|
76
|
+
),
|
|
77
|
+
...lines
|
|
78
|
+
].join("\n").slice(-18e3),
|
|
79
|
+
stop: () => {
|
|
80
|
+
if (console.error === error) console.error = originalError;
|
|
81
|
+
if (console.warn === warn) console.warn = originalWarn;
|
|
82
|
+
if (window.fetch === wrappedFetch) window.fetch = originalFetch;
|
|
83
|
+
window.removeEventListener("error", onError);
|
|
84
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
85
|
+
resources?.disconnect();
|
|
86
|
+
lines.length = 0;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function safeUrl(value) {
|
|
91
|
+
try {
|
|
92
|
+
const url = new URL(value, location.href);
|
|
93
|
+
return `${url.origin}${url.pathname}`;
|
|
94
|
+
} catch {
|
|
95
|
+
return "[invalid URL]";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function sanitizeDiagnostic(value) {
|
|
99
|
+
return value.replace(/https?:\/\/[^\s<>"']+/g, (url) => safeUrl(url)).replace(/\bBearer\s+[^\s,;]+/gi, "Bearer [redacted]").replace(
|
|
100
|
+
/\b(token|password|secret|authorization|api[_-]?key)\s*[:=]\s*[^\s,;]+/gi,
|
|
101
|
+
"$1=[redacted]"
|
|
102
|
+
).replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted token]");
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
collectBrowserDiagnostics,
|
|
106
|
+
safeUrl,
|
|
107
|
+
sanitizeDiagnostic
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=browser-diagnostics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/connection/remote-control/browser-diagnostics.ts"],
|
|
4
|
+
"sourcesContent": ["import { formatErrorReport } from '../../settings/error-report'\n\n/** Session-only browser evidence, collected only while the user is sharing. No bodies, headers or storage. */\nexport function collectBrowserDiagnostics() {\n const lines: string[] = []\n const add = (message: string) => {\n lines.push(`${new Date().toISOString()} ${sanitizeDiagnostic(message).slice(0, 1800)}`)\n if (lines.length > 30) lines.shift()\n }\n const stringify = (value: unknown): string => {\n if (value instanceof Error)\n return formatErrorReport({ error: value, url: safeUrl(location.href) })\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')\n return String(value)\n return '[object omitted]'\n }\n const originalError = console.error\n const originalWarn = console.warn\n const error = (...args: unknown[]) => {\n add(`error: ${args.map(stringify).join(' ')}`)\n originalError.apply(console, args)\n }\n const warn = (...args: unknown[]) => {\n add(`warning: ${args.map(stringify).join(' ')}`)\n originalWarn.apply(console, args)\n }\n console.error = error\n console.warn = warn\n const onError = (event: ErrorEvent) => add(`uncaught: ${stringify(event.error ?? event.message)}`)\n const onRejection = (event: PromiseRejectionEvent) =>\n add(`unhandled rejection: ${stringify(event.reason)}`)\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n const originalFetch = window.fetch\n const wrappedFetch: typeof fetch = async (...args) => {\n const input = args[0]\n const url = safeUrl(\n typeof input === 'string' ? input : input instanceof URL ? input.href : input.url\n )\n try {\n const response = await originalFetch.apply(window, args)\n if (!response.ok) add(`HTTP ${response.status}: ${url}`)\n return response\n } catch (failure) {\n add(`request failed: ${url}`)\n throw failure\n }\n }\n window.fetch = wrappedFetch\n // Resource timing also catches failures made through fetch references captured before sharing.\n let resources: PerformanceObserver | undefined\n if (typeof PerformanceObserver !== 'undefined') {\n try {\n resources = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const resource = entry as PerformanceResourceTiming\n if ('responseStatus' in resource && Number(resource.responseStatus) >= 400)\n add(`HTTP ${resource.responseStatus}: ${safeUrl(resource.name)}`)\n }\n })\n resources.observe({ type: 'resource' })\n } catch {\n resources = undefined\n }\n }\n return {\n snapshot: () =>\n [\n `ARBI tab: ${safeUrl(location.href)}`,\n `Viewport: ${innerWidth} x ${innerHeight}; DPR ${devicePixelRatio}`,\n 'Visible ARBI controls (selectors for control_ui):',\n ...Array.from(\n document.querySelectorAll<HTMLElement>(\n 'button[data-testid],a[data-testid],input[data-testid]:not([type=\"password\"]),select[data-testid],[role=\"button\"][data-testid]'\n )\n )\n .filter(\n (el) =>\n el.getBoundingClientRect().width > 0 && !el.closest('[data-remote-control-surface]')\n )\n .slice(0, 60)\n .map(\n (el) =>\n `[data-testid=${JSON.stringify(el.dataset.testid)}] ${sanitizeDiagnostic(el.getAttribute('aria-label') ?? el.getAttribute('title') ?? el.textContent ?? '').slice(0, 80)}`\n ),\n ...lines,\n ]\n .join('\\n')\n .slice(-18_000),\n stop: () => {\n if (console.error === error) console.error = originalError\n if (console.warn === warn) console.warn = originalWarn\n if (window.fetch === wrappedFetch) window.fetch = originalFetch\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n resources?.disconnect()\n lines.length = 0\n },\n }\n}\n\nexport function safeUrl(value: string): string {\n try {\n const url = new URL(value, location.href)\n return `${url.origin}${url.pathname}`\n } catch {\n return '[invalid URL]'\n }\n}\nexport function sanitizeDiagnostic(value: string): string {\n return value\n .replace(/https?:\\/\\/[^\\s<>\"']+/g, (url) => safeUrl(url))\n .replace(/\\bBearer\\s+[^\\s,;]+/gi, 'Bearer [redacted]')\n .replace(\n /\\b(token|password|secret|authorization|api[_-]?key)\\s*[:=]\\s*[^\\s,;]+/gi,\n '$1=[redacted]'\n )\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g, '[redacted token]')\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -21,8 +21,16 @@ export interface RemoteControl {
|
|
|
21
21
|
/** Turn remote control ON directly — the user's own explicit choice. Runs nothing by itself. */
|
|
22
22
|
enable(): void;
|
|
23
23
|
}
|
|
24
|
+
export interface ScreenTransport {
|
|
25
|
+
reply(requestId: string, result: {
|
|
26
|
+
status: 'captured' | 'denied' | 'stopped' | 'unavailable' | 'action_finished';
|
|
27
|
+
screenshot_png?: string;
|
|
28
|
+
diagnostics?: string;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
diagnose?(): Promise<string>;
|
|
31
|
+
}
|
|
24
32
|
/** Remote control on a session's state, running the site's guided runs. */
|
|
25
|
-
export declare function createRemoteControl(session: RemoteControlSession, guided: Pick<GuidedRuns, 'isSequenceId' | 'run' | 'remoteControl'
|
|
33
|
+
export declare function createRemoteControl(session: RemoteControlSession, guided: Pick<GuidedRuns, 'isSequenceId' | 'run' | 'remoteControl'>, screenTransport?: ScreenTransport): RemoteControl;
|
|
26
34
|
/** Remote control on the host's session and guided runs. */
|
|
27
35
|
export declare function useRemoteControlActions(): RemoteControl;
|
|
28
36
|
/**
|
|
@@ -7,25 +7,115 @@ import {
|
|
|
7
7
|
toRemoteControlRequest,
|
|
8
8
|
useArbiHost,
|
|
9
9
|
useArbiSession,
|
|
10
|
-
useArbiSessionStore
|
|
10
|
+
useArbiSessionStore,
|
|
11
|
+
useArbiSessionState
|
|
11
12
|
} from "@arbidocs/react";
|
|
12
13
|
import { cancelGuidedRun } from "../../guided/runner.js";
|
|
13
14
|
import { useGuidedRuns } from "../../guided/useGuidedRuns.js";
|
|
14
15
|
import { isSurfaceMounted } from "../../guided/surfaces.js";
|
|
15
|
-
|
|
16
|
+
import { createScreenSharing } from "./screen-sharing.js";
|
|
17
|
+
const screenRuntimes = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
function screenRuntime(session) {
|
|
19
|
+
let runtime = screenRuntimes.get(session);
|
|
20
|
+
if (!runtime) {
|
|
21
|
+
runtime = { sharing: createScreenSharing(), generation: 0, setup: "" };
|
|
22
|
+
screenRuntimes.set(session, runtime);
|
|
23
|
+
}
|
|
24
|
+
return runtime;
|
|
25
|
+
}
|
|
26
|
+
function stopScreen(session) {
|
|
27
|
+
const runtime = screenRuntimes.get(session);
|
|
28
|
+
if (!runtime) return;
|
|
29
|
+
runtime.generation++;
|
|
30
|
+
runtime.sharing.stop();
|
|
31
|
+
runtime.setup = "";
|
|
32
|
+
const actions = remoteControlActions(session);
|
|
33
|
+
if (remoteControlStateOf(session.getState()).sharing) actions.setSharing(false);
|
|
34
|
+
}
|
|
35
|
+
function createRemoteControl(session, guided, screenTransport) {
|
|
16
36
|
const actions = remoteControlActions(session);
|
|
17
37
|
const state = () => remoteControlStateOf(session.getState());
|
|
38
|
+
const runtime = screenRuntime(session);
|
|
39
|
+
const reply = async (req, status) => {
|
|
40
|
+
if ((req.screenRequestId || req.actionRequestId) && screenTransport) {
|
|
41
|
+
await screenTransport.reply(req.screenRequestId || req.actionRequestId, { status }).catch(() => {
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const disable = () => {
|
|
46
|
+
const pending = state().pending;
|
|
47
|
+
if (pending) void reply(pending, "stopped");
|
|
48
|
+
stopScreen(session);
|
|
49
|
+
actions.setEnabled(false);
|
|
50
|
+
cancelGuidedRun();
|
|
51
|
+
};
|
|
52
|
+
const inspect = async (req, keepSharing) => {
|
|
53
|
+
if (!req.screenRequestId || !screenTransport) return;
|
|
54
|
+
const workspace = session.getState().workspaceId;
|
|
55
|
+
if (workspace !== req.workspaceExtId) {
|
|
56
|
+
await reply(req, "unavailable");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const token = runtime.generation;
|
|
60
|
+
const current = () => token === runtime.generation && session.getState().workspaceId === workspace;
|
|
61
|
+
actions.setControllingAgent({ name: req.agentName, extId: req.agentExtId });
|
|
62
|
+
try {
|
|
63
|
+
if (!runtime.sharing.active) {
|
|
64
|
+
await runtime.sharing.start(disable);
|
|
65
|
+
if (!current()) {
|
|
66
|
+
runtime.sharing.stop();
|
|
67
|
+
await reply(req, "stopped");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
actions.setSharing(true);
|
|
71
|
+
if (screenTransport.diagnose)
|
|
72
|
+
runtime.setup = await screenTransport.diagnose().catch(() => "Setup checks unavailable.");
|
|
73
|
+
}
|
|
74
|
+
if (!current()) {
|
|
75
|
+
await reply(req, "stopped");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const frame = await runtime.sharing.frame();
|
|
79
|
+
if (!current()) {
|
|
80
|
+
await reply(req, "stopped");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await screenTransport.reply(req.screenRequestId, {
|
|
84
|
+
status: "captured",
|
|
85
|
+
...frame,
|
|
86
|
+
diagnostics: `${frame.diagnostics}
|
|
87
|
+
${runtime.setup}`.slice(0, 24e3)
|
|
88
|
+
});
|
|
89
|
+
} catch {
|
|
90
|
+
await reply(req, current() ? "unavailable" : "stopped");
|
|
91
|
+
if (current()) disable();
|
|
92
|
+
} finally {
|
|
93
|
+
if (current()) {
|
|
94
|
+
actions.setControllingAgent(null);
|
|
95
|
+
if (!keepSharing) disable();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
18
99
|
const resolveRun = (req) => {
|
|
19
100
|
if (req.steps && req.steps.length > 0) return req.steps;
|
|
20
101
|
if (req.sequenceId && guided.isSequenceId(req.sequenceId)) return req.sequenceId;
|
|
21
102
|
return null;
|
|
22
103
|
};
|
|
23
|
-
const runRemoteRequest = async (req) => {
|
|
104
|
+
const runRemoteRequest = async (req, keepSharing = state().enabled) => {
|
|
105
|
+
if (req.screenRequestId) {
|
|
106
|
+
await inspect(req, keepSharing);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
24
109
|
const run = resolveRun(req);
|
|
25
110
|
if (!run) return;
|
|
111
|
+
const token = runtime.generation;
|
|
26
112
|
actions.setControllingAgent({ name: req.agentName, extId: req.agentExtId });
|
|
27
113
|
try {
|
|
28
114
|
await guided.run(run, { remote: true });
|
|
115
|
+
if (req.actionRequestId)
|
|
116
|
+
await reply(req, token === runtime.generation ? "action_finished" : "stopped");
|
|
117
|
+
} catch {
|
|
118
|
+
await reply(req, "unavailable");
|
|
29
119
|
} finally {
|
|
30
120
|
const current = state().controllingAgent;
|
|
31
121
|
if (current && current.name === req.agentName && current.extId === req.agentExtId) {
|
|
@@ -45,31 +135,49 @@ function createRemoteControl(session, guided) {
|
|
|
45
135
|
}
|
|
46
136
|
const req = toRemoteControlRequest(msg, guided.isSequenceId);
|
|
47
137
|
if (!req) return;
|
|
48
|
-
if (
|
|
138
|
+
if ((req.screenRequestId || req.actionRequestId) && (!screenTransport || session.getState().workspaceId !== req.workspaceExtId)) {
|
|
139
|
+
void reply(req, "unavailable");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (state().enabled && (!req.screenRequestId || runtime.sharing.active)) {
|
|
49
143
|
logger.info("[RemoteControl] Enabled \u2014 running requested action from", req.agentName);
|
|
50
144
|
void runRemoteRequest(req);
|
|
51
145
|
return;
|
|
52
146
|
}
|
|
53
147
|
logger.info("[RemoteControl] Consent required \u2014 prompting for", req.agentName);
|
|
148
|
+
if (state().pending) void reply(state().pending, "denied");
|
|
54
149
|
actions.request(req);
|
|
55
150
|
},
|
|
56
151
|
grantAndRun(opts) {
|
|
57
152
|
const pending = state().pending;
|
|
153
|
+
const keepSharing = opts.forSession || state().enabled;
|
|
58
154
|
actions.grant(opts);
|
|
59
|
-
if (pending) void runRemoteRequest(pending);
|
|
155
|
+
if (pending) void runRemoteRequest(pending, keepSharing);
|
|
60
156
|
},
|
|
61
|
-
deny: () =>
|
|
62
|
-
|
|
63
|
-
actions.
|
|
64
|
-
|
|
157
|
+
deny: () => {
|
|
158
|
+
const pending = state().pending;
|
|
159
|
+
actions.deny();
|
|
160
|
+
if (pending) void reply(pending, "denied");
|
|
65
161
|
},
|
|
162
|
+
disable,
|
|
66
163
|
enable: () => actions.setEnabled(true)
|
|
67
164
|
};
|
|
68
165
|
}
|
|
69
166
|
function useRemoteControlActions() {
|
|
70
167
|
const session = useArbiSessionStore();
|
|
71
168
|
const guided = useGuidedRuns();
|
|
72
|
-
|
|
169
|
+
const { arbi } = useArbiHost();
|
|
170
|
+
return useMemo(
|
|
171
|
+
() => createRemoteControl(
|
|
172
|
+
session,
|
|
173
|
+
guided,
|
|
174
|
+
arbi ? {
|
|
175
|
+
reply: (id, result) => arbi.remoteControl.replyScreen(id, result),
|
|
176
|
+
diagnose: async () => JSON.stringify(await arbi.diagnose())
|
|
177
|
+
} : void 0
|
|
178
|
+
),
|
|
179
|
+
[session, guided, arbi]
|
|
180
|
+
);
|
|
73
181
|
}
|
|
74
182
|
function useRemoteControlSessionReset() {
|
|
75
183
|
const session = useArbiSessionStore();
|
|
@@ -77,6 +185,7 @@ function useRemoteControlSessionReset() {
|
|
|
77
185
|
const userId = useArbiSession().user?.external_id ?? null;
|
|
78
186
|
useEffect(() => {
|
|
79
187
|
const reset = () => {
|
|
188
|
+
stopScreen(session);
|
|
80
189
|
remoteControlActions(session).reset();
|
|
81
190
|
cancelGuidedRun();
|
|
82
191
|
};
|
|
@@ -92,6 +201,7 @@ function useRemoteControlSessionReset() {
|
|
|
92
201
|
useEffect(() => {
|
|
93
202
|
if (lastUser.current === userId) return;
|
|
94
203
|
lastUser.current = userId;
|
|
204
|
+
stopScreen(session);
|
|
95
205
|
remoteControlActions(session).reset();
|
|
96
206
|
cancelGuidedRun();
|
|
97
207
|
}, [userId, session]);
|
|
@@ -100,6 +210,16 @@ function useRemoteControlHandlers() {
|
|
|
100
210
|
useRemoteControlSessionReset();
|
|
101
211
|
const remote = useRemoteControlActions();
|
|
102
212
|
const { remoteControl } = useGuidedRuns();
|
|
213
|
+
const session = useArbiSessionStore();
|
|
214
|
+
const workspaceId = useArbiSessionState((s) => s.workspaceId);
|
|
215
|
+
useEffect(
|
|
216
|
+
() => () => {
|
|
217
|
+
stopScreen(session);
|
|
218
|
+
remoteControlActions(session).setEnabled(false);
|
|
219
|
+
cancelGuidedRun();
|
|
220
|
+
},
|
|
221
|
+
[session, workspaceId]
|
|
222
|
+
);
|
|
103
223
|
return useMemo(
|
|
104
224
|
() => remoteControl ? {
|
|
105
225
|
remote_control: (message) => remote.handleMessage(message)
|