@vellumai/assistant 0.4.22 → 0.4.23
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/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +55 -44
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -75
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/ipc-snapshot.test.ts +0 -54
- package/src/__tests__/resolve-guardian-trust-class.test.ts +61 -0
- package/src/__tests__/session-init.benchmark.test.ts +0 -4
- package/src/daemon/daemon-control.ts +3 -0
- package/src/daemon/handlers/browser.ts +2 -48
- package/src/daemon/ipc-contract/browser.ts +4 -74
- package/src/daemon/ipc-contract/surfaces.ts +51 -48
- package/src/daemon/ipc-contract-inventory.json +0 -7
- package/src/daemon/session-agent-loop.ts +2 -1
- package/src/daemon/session-tool-setup.ts +27 -13
- package/src/memory/migrations/102-alter-table-columns.ts +254 -37
- package/src/memory/schema.ts +1227 -1035
- package/src/tools/browser/browser-execution.ts +314 -331
- package/src/tools/browser/browser-handoff.ts +11 -37
- package/src/tools/browser/browser-manager.ts +271 -264
- package/src/tools/browser/browser-screencast.ts +19 -75
package/package.json
CHANGED
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
* bun run ipc:check-swift-drift # check for drift
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import * as fs from
|
|
16
|
-
import * as path from
|
|
15
|
+
import * as fs from "fs";
|
|
16
|
+
import * as path from "path";
|
|
17
17
|
|
|
18
|
-
import { extractInventory } from
|
|
18
|
+
import { extractInventory } from "../../src/daemon/ipc-contract-inventory.js";
|
|
19
19
|
|
|
20
|
-
const ROOT = path.resolve(import.meta.dirname ?? __dirname,
|
|
21
|
-
const CONTRACT_PATH = path.join(ROOT,
|
|
20
|
+
const ROOT = path.resolve(import.meta.dirname ?? __dirname, "../..");
|
|
21
|
+
const CONTRACT_PATH = path.join(ROOT, "src/daemon/ipc-contract.ts");
|
|
22
22
|
const SWIFT_PATH = path.resolve(
|
|
23
23
|
ROOT,
|
|
24
|
-
|
|
24
|
+
"../clients/shared/IPC/IPCMessages.swift",
|
|
25
25
|
);
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -31,37 +31,35 @@ const SWIFT_PATH = path.resolve(
|
|
|
31
31
|
*/
|
|
32
32
|
const SWIFT_OMIT_ALLOWLIST = new Set<string>([
|
|
33
33
|
// Server-internal events not surfaced to macOS client
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
"context_compacted",
|
|
35
|
+
"memory_recalled",
|
|
36
|
+
"model_info",
|
|
37
|
+
"secret_detected",
|
|
38
|
+
"sessions_clear_response",
|
|
39
|
+
"usage_response",
|
|
40
|
+
"usage_update",
|
|
41
41
|
// Gallery and cloud sharing — not yet consumed by the macOS client
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
"gallery_install_response",
|
|
43
|
+
"gallery_list_response",
|
|
44
|
+
"share_app_cloud_response",
|
|
45
45
|
// Page publishing — not yet consumed by the macOS client
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
"publish_page_response",
|
|
47
|
+
"unpublish_page_response",
|
|
48
48
|
// Heartbeat alerts — not yet consumed by the macOS client
|
|
49
|
-
|
|
50
|
-
// Browser handoff — not yet consumed by the macOS client
|
|
51
|
-
'browser_handoff_request',
|
|
49
|
+
"heartbeat_alert",
|
|
52
50
|
// Guardian verification — daemon-internal for Telegram channel setup
|
|
53
|
-
|
|
51
|
+
"guardian_verification_response",
|
|
54
52
|
// Ingress invite/member management — not yet consumed by the macOS client
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
"ingress_invite_response",
|
|
54
|
+
"ingress_member_response",
|
|
57
55
|
// Inbox escalation — not yet consumed by the macOS client
|
|
58
|
-
|
|
56
|
+
"assistant_inbox_escalation_response",
|
|
59
57
|
// Work item messages — not yet consumed by the macOS client
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
"work_item_get_response",
|
|
59
|
+
"work_item_run_task_response",
|
|
60
|
+
"work_item_status_changed",
|
|
61
|
+
"work_item_update_response",
|
|
62
|
+
"work_items_list_response",
|
|
65
63
|
]);
|
|
66
64
|
|
|
67
65
|
/**
|
|
@@ -72,7 +70,7 @@ const SWIFT_OMIT_ALLOWLIST = new Set<string>([
|
|
|
72
70
|
const INVENTORY_UNEXTRACTABLE = new Set<string>([
|
|
73
71
|
// UiSurfaceShow is a union of UiSurfaceShowCard | UiSurfaceShowForm | ...
|
|
74
72
|
// The shared wire type 'ui_surface_show' comes from UiSurfaceShowBase.
|
|
75
|
-
|
|
73
|
+
"ui_surface_show",
|
|
76
74
|
]);
|
|
77
75
|
|
|
78
76
|
/**
|
|
@@ -82,9 +80,9 @@ const INVENTORY_UNEXTRACTABLE = new Set<string>([
|
|
|
82
80
|
*/
|
|
83
81
|
const SWIFT_AHEAD_ALLOWLIST = new Set<string>([
|
|
84
82
|
// Defined in Swift LayoutConfig.swift ahead of daemon implementation
|
|
85
|
-
|
|
83
|
+
"ui_layout_config",
|
|
86
84
|
// Defined in Swift HTTPDaemonClient ahead of daemon token rotation endpoint
|
|
87
|
-
|
|
85
|
+
"token_rotated",
|
|
88
86
|
]);
|
|
89
87
|
|
|
90
88
|
// --- Extract Swift decode cases ---
|
|
@@ -97,9 +95,13 @@ function extractSwiftDecodeCases(swiftSource: string): Set<string> {
|
|
|
97
95
|
let match: RegExpExecArray | null;
|
|
98
96
|
|
|
99
97
|
// Only scan inside the ServerMessage init(from decoder:) block
|
|
100
|
-
const decoderStart = swiftSource.indexOf(
|
|
98
|
+
const decoderStart = swiftSource.indexOf(
|
|
99
|
+
"public init(from decoder: Decoder) throws",
|
|
100
|
+
);
|
|
101
101
|
if (decoderStart === -1) {
|
|
102
|
-
throw new Error(
|
|
102
|
+
throw new Error(
|
|
103
|
+
"Could not find ServerMessage decoder in IPCMessages.swift",
|
|
104
|
+
);
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
const decoderSection = swiftSource.slice(decoderStart);
|
|
@@ -120,7 +122,7 @@ const contractServerTypes = new Set([
|
|
|
120
122
|
...INVENTORY_UNEXTRACTABLE,
|
|
121
123
|
]);
|
|
122
124
|
|
|
123
|
-
const swiftSource = fs.readFileSync(SWIFT_PATH,
|
|
125
|
+
const swiftSource = fs.readFileSync(SWIFT_PATH, "utf-8");
|
|
124
126
|
const swiftDecodeCases = extractSwiftDecodeCases(swiftSource);
|
|
125
127
|
|
|
126
128
|
const diffs: string[] = [];
|
|
@@ -134,7 +136,10 @@ for (const wireType of contractServerTypes) {
|
|
|
134
136
|
|
|
135
137
|
// Types decoded in Swift but not in contract
|
|
136
138
|
for (const wireType of swiftDecodeCases) {
|
|
137
|
-
if (
|
|
139
|
+
if (
|
|
140
|
+
!contractServerTypes.has(wireType) &&
|
|
141
|
+
!SWIFT_AHEAD_ALLOWLIST.has(wireType)
|
|
142
|
+
) {
|
|
138
143
|
diffs.push(` - Swift decodes "${wireType}" but it is not in the contract`);
|
|
139
144
|
}
|
|
140
145
|
}
|
|
@@ -142,30 +147,36 @@ for (const wireType of swiftDecodeCases) {
|
|
|
142
147
|
// Stale allowlist entries
|
|
143
148
|
for (const wireType of SWIFT_OMIT_ALLOWLIST) {
|
|
144
149
|
if (!contractServerTypes.has(wireType)) {
|
|
145
|
-
diffs.push(
|
|
150
|
+
diffs.push(
|
|
151
|
+
` ? Omit-allowlist entry "${wireType}" is not in the contract (stale?)`,
|
|
152
|
+
);
|
|
146
153
|
}
|
|
147
154
|
}
|
|
148
155
|
for (const wireType of INVENTORY_UNEXTRACTABLE) {
|
|
149
156
|
if (!swiftDecodeCases.has(wireType)) {
|
|
150
|
-
diffs.push(
|
|
157
|
+
diffs.push(
|
|
158
|
+
` ? Unextractable entry "${wireType}" is not decoded in Swift (stale?)`,
|
|
159
|
+
);
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
for (const wireType of SWIFT_AHEAD_ALLOWLIST) {
|
|
154
163
|
if (contractServerTypes.has(wireType)) {
|
|
155
|
-
diffs.push(
|
|
164
|
+
diffs.push(
|
|
165
|
+
` ? Ahead-allowlist entry "${wireType}" is now in the contract (remove from allowlist)`,
|
|
166
|
+
);
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
if (diffs.length > 0) {
|
|
160
|
-
console.error(
|
|
171
|
+
console.error("IPC Swift decoder drift detected:\n");
|
|
161
172
|
for (const line of diffs) {
|
|
162
173
|
console.error(line);
|
|
163
174
|
}
|
|
164
175
|
console.error(
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
"\nFix: update IPCMessages.swift decode cases, the contract, or the",
|
|
177
|
+
"allowlist in check-swift-decoder-drift.ts.",
|
|
167
178
|
);
|
|
168
179
|
process.exit(1);
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
console.log(
|
|
182
|
+
console.log("IPC Swift decoder is in sync with the contract.");
|
|
@@ -776,46 +776,6 @@ exports[`IPC message snapshots ClientMessage types browser_cdp_response serializ
|
|
|
776
776
|
}
|
|
777
777
|
`;
|
|
778
778
|
|
|
779
|
-
exports[`IPC message snapshots ClientMessage types browser_user_click serializes to expected JSON 1`] = `
|
|
780
|
-
{
|
|
781
|
-
"sessionId": "test-session",
|
|
782
|
-
"surfaceId": "test-surface",
|
|
783
|
-
"type": "browser_user_click",
|
|
784
|
-
"x": 100,
|
|
785
|
-
"y": 200,
|
|
786
|
-
}
|
|
787
|
-
`;
|
|
788
|
-
|
|
789
|
-
exports[`IPC message snapshots ClientMessage types browser_user_scroll serializes to expected JSON 1`] = `
|
|
790
|
-
{
|
|
791
|
-
"deltaX": 0,
|
|
792
|
-
"deltaY": -100,
|
|
793
|
-
"sessionId": "test-session",
|
|
794
|
-
"surfaceId": "test-surface",
|
|
795
|
-
"type": "browser_user_scroll",
|
|
796
|
-
"x": 100,
|
|
797
|
-
"y": 200,
|
|
798
|
-
}
|
|
799
|
-
`;
|
|
800
|
-
|
|
801
|
-
exports[`IPC message snapshots ClientMessage types browser_user_keypress serializes to expected JSON 1`] = `
|
|
802
|
-
{
|
|
803
|
-
"key": "Enter",
|
|
804
|
-
"sessionId": "test-session",
|
|
805
|
-
"surfaceId": "test-surface",
|
|
806
|
-
"type": "browser_user_keypress",
|
|
807
|
-
}
|
|
808
|
-
`;
|
|
809
|
-
|
|
810
|
-
exports[`IPC message snapshots ClientMessage types browser_interactive_mode serializes to expected JSON 1`] = `
|
|
811
|
-
{
|
|
812
|
-
"enabled": true,
|
|
813
|
-
"sessionId": "test-session",
|
|
814
|
-
"surfaceId": "test-surface",
|
|
815
|
-
"type": "browser_interactive_mode",
|
|
816
|
-
}
|
|
817
|
-
`;
|
|
818
|
-
|
|
819
779
|
exports[`IPC message snapshots ClientMessage types work_items_list serializes to expected JSON 1`] = `
|
|
820
780
|
{
|
|
821
781
|
"status": "queued",
|
|
@@ -2437,22 +2397,6 @@ exports[`IPC message snapshots ServerMessage types app_files_changed serializes
|
|
|
2437
2397
|
}
|
|
2438
2398
|
`;
|
|
2439
2399
|
|
|
2440
|
-
exports[`IPC message snapshots ServerMessage types browser_frame serializes to expected JSON 1`] = `
|
|
2441
|
-
{
|
|
2442
|
-
"frame": "base64-jpeg-data",
|
|
2443
|
-
"metadata": {
|
|
2444
|
-
"offsetTop": 0,
|
|
2445
|
-
"pageScaleFactor": 1,
|
|
2446
|
-
"scrollOffsetX": 0,
|
|
2447
|
-
"scrollOffsetY": 0,
|
|
2448
|
-
"timestamp": 1700000000,
|
|
2449
|
-
},
|
|
2450
|
-
"sessionId": "sess-001",
|
|
2451
|
-
"surfaceId": "surface-001",
|
|
2452
|
-
"type": "browser_frame",
|
|
2453
|
-
}
|
|
2454
|
-
`;
|
|
2455
|
-
|
|
2456
2400
|
exports[`IPC message snapshots ServerMessage types diagnostics_export_response serializes to expected JSON 1`] = `
|
|
2457
2401
|
{
|
|
2458
2402
|
"filePath": "/tmp/diagnostics-conv-001.zip",
|
|
@@ -2518,25 +2462,6 @@ exports[`IPC message snapshots ServerMessage types browser_cdp_request serialize
|
|
|
2518
2462
|
}
|
|
2519
2463
|
`;
|
|
2520
2464
|
|
|
2521
|
-
exports[`IPC message snapshots ServerMessage types browser_interactive_mode_changed serializes to expected JSON 1`] = `
|
|
2522
|
-
{
|
|
2523
|
-
"enabled": true,
|
|
2524
|
-
"sessionId": "test-session",
|
|
2525
|
-
"surfaceId": "test-surface",
|
|
2526
|
-
"type": "browser_interactive_mode_changed",
|
|
2527
|
-
}
|
|
2528
|
-
`;
|
|
2529
|
-
|
|
2530
|
-
exports[`IPC message snapshots ServerMessage types browser_handoff_request serializes to expected JSON 1`] = `
|
|
2531
|
-
{
|
|
2532
|
-
"message": "Login required",
|
|
2533
|
-
"reason": "auth",
|
|
2534
|
-
"sessionId": "test-session",
|
|
2535
|
-
"surfaceId": "test-surface",
|
|
2536
|
-
"type": "browser_handoff_request",
|
|
2537
|
-
}
|
|
2538
|
-
`;
|
|
2539
|
-
|
|
2540
2465
|
exports[`IPC message snapshots ServerMessage types document_editor_show serializes to expected JSON 1`] = `
|
|
2541
2466
|
{
|
|
2542
2467
|
"initialContent": "# Hello World",
|
|
@@ -64,10 +64,6 @@ mock.module("../tools/browser/browser-screencast.js", () => ({
|
|
|
64
64
|
stopBrowserScreencast: async () => {},
|
|
65
65
|
stopAllScreencasts: async () => {},
|
|
66
66
|
ensureScreencast: async () => {},
|
|
67
|
-
updateBrowserStatus: () => {},
|
|
68
|
-
updatePagesList: async () => {},
|
|
69
|
-
getElementBounds: async () => null,
|
|
70
|
-
updateHighlights: () => {},
|
|
71
67
|
}));
|
|
72
68
|
|
|
73
69
|
import {
|
|
@@ -481,34 +481,6 @@ const clientMessages: Record<ClientMessageType, ClientMessage> = {
|
|
|
481
481
|
sessionId: "test-session",
|
|
482
482
|
success: true,
|
|
483
483
|
},
|
|
484
|
-
browser_user_click: {
|
|
485
|
-
type: "browser_user_click",
|
|
486
|
-
sessionId: "test-session",
|
|
487
|
-
surfaceId: "test-surface",
|
|
488
|
-
x: 100,
|
|
489
|
-
y: 200,
|
|
490
|
-
},
|
|
491
|
-
browser_user_scroll: {
|
|
492
|
-
type: "browser_user_scroll",
|
|
493
|
-
sessionId: "test-session",
|
|
494
|
-
surfaceId: "test-surface",
|
|
495
|
-
deltaX: 0,
|
|
496
|
-
deltaY: -100,
|
|
497
|
-
x: 100,
|
|
498
|
-
y: 200,
|
|
499
|
-
},
|
|
500
|
-
browser_user_keypress: {
|
|
501
|
-
type: "browser_user_keypress",
|
|
502
|
-
sessionId: "test-session",
|
|
503
|
-
surfaceId: "test-surface",
|
|
504
|
-
key: "Enter",
|
|
505
|
-
},
|
|
506
|
-
browser_interactive_mode: {
|
|
507
|
-
type: "browser_interactive_mode",
|
|
508
|
-
sessionId: "test-session",
|
|
509
|
-
surfaceId: "test-surface",
|
|
510
|
-
enabled: true,
|
|
511
|
-
},
|
|
512
484
|
work_items_list: {
|
|
513
485
|
type: "work_items_list",
|
|
514
486
|
status: "queued",
|
|
@@ -1590,19 +1562,6 @@ const serverMessages: Record<ServerMessageType, ServerMessage> = {
|
|
|
1590
1562
|
type: "app_files_changed",
|
|
1591
1563
|
appId: "app-001",
|
|
1592
1564
|
},
|
|
1593
|
-
browser_frame: {
|
|
1594
|
-
type: "browser_frame",
|
|
1595
|
-
sessionId: "sess-001",
|
|
1596
|
-
surfaceId: "surface-001",
|
|
1597
|
-
frame: "base64-jpeg-data",
|
|
1598
|
-
metadata: {
|
|
1599
|
-
offsetTop: 0,
|
|
1600
|
-
pageScaleFactor: 1,
|
|
1601
|
-
scrollOffsetX: 0,
|
|
1602
|
-
scrollOffsetY: 0,
|
|
1603
|
-
timestamp: 1700000000,
|
|
1604
|
-
},
|
|
1605
|
-
},
|
|
1606
1565
|
diagnostics_export_response: {
|
|
1607
1566
|
type: "diagnostics_export_response",
|
|
1608
1567
|
success: true,
|
|
@@ -1637,19 +1596,6 @@ const serverMessages: Record<ServerMessageType, ServerMessage> = {
|
|
|
1637
1596
|
type: "browser_cdp_request",
|
|
1638
1597
|
sessionId: "test-session",
|
|
1639
1598
|
},
|
|
1640
|
-
browser_interactive_mode_changed: {
|
|
1641
|
-
type: "browser_interactive_mode_changed",
|
|
1642
|
-
sessionId: "test-session",
|
|
1643
|
-
surfaceId: "test-surface",
|
|
1644
|
-
enabled: true,
|
|
1645
|
-
},
|
|
1646
|
-
browser_handoff_request: {
|
|
1647
|
-
type: "browser_handoff_request",
|
|
1648
|
-
sessionId: "test-session",
|
|
1649
|
-
surfaceId: "test-surface",
|
|
1650
|
-
reason: "auth" as const,
|
|
1651
|
-
message: "Login required",
|
|
1652
|
-
},
|
|
1653
1599
|
document_editor_show: {
|
|
1654
1600
|
type: "document_editor_show",
|
|
1655
1601
|
sessionId: "sess-001",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { GuardianRuntimeContext } from "../daemon/session-runtime-assembly.js";
|
|
4
|
+
|
|
5
|
+
// ── Module mocks ─────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
let fakeHttpAuthDisabled = false;
|
|
8
|
+
|
|
9
|
+
mock.module("../config/env.js", () => ({
|
|
10
|
+
isHttpAuthDisabled: () => fakeHttpAuthDisabled,
|
|
11
|
+
hasUngatedHttpAuthDisabled: () => false,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
// ── Real imports (after mocks) ───────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
import { resolveGuardianTrustClass } from "../daemon/session-tool-setup.js";
|
|
17
|
+
|
|
18
|
+
afterAll(() => {
|
|
19
|
+
mock.restore();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
describe("resolveGuardianTrustClass", () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
fakeHttpAuthDisabled = false;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("returns guardian context trust class when auth is enabled", () => {
|
|
30
|
+
const ctx: Pick<GuardianRuntimeContext, "trustClass"> = {
|
|
31
|
+
trustClass: "trusted_contact",
|
|
32
|
+
};
|
|
33
|
+
expect(resolveGuardianTrustClass(ctx as GuardianRuntimeContext)).toBe(
|
|
34
|
+
"trusted_contact",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("defaults to guardian when no guardian context and auth is enabled", () => {
|
|
39
|
+
expect(resolveGuardianTrustClass(undefined)).toBe("guardian");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("forces guardian when HTTP auth is disabled, regardless of context trust class", () => {
|
|
43
|
+
fakeHttpAuthDisabled = true;
|
|
44
|
+
const ctx: Pick<GuardianRuntimeContext, "trustClass"> = {
|
|
45
|
+
trustClass: "trusted_contact",
|
|
46
|
+
};
|
|
47
|
+
expect(resolveGuardianTrustClass(ctx as GuardianRuntimeContext)).toBe(
|
|
48
|
+
"guardian",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("forces guardian for unknown trust class when HTTP auth is disabled", () => {
|
|
53
|
+
fakeHttpAuthDisabled = true;
|
|
54
|
+
const ctx: Pick<GuardianRuntimeContext, "trustClass"> = {
|
|
55
|
+
trustClass: "unknown",
|
|
56
|
+
};
|
|
57
|
+
expect(resolveGuardianTrustClass(ctx as GuardianRuntimeContext)).toBe(
|
|
58
|
+
"guardian",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -277,11 +277,7 @@ mock.module("../tools/browser/browser-screencast.js", () => ({
|
|
|
277
277
|
registerSessionSender: () => {},
|
|
278
278
|
unregisterSessionSender: () => {},
|
|
279
279
|
ensureScreencast: () => Promise.resolve(),
|
|
280
|
-
updateBrowserStatus: () => {},
|
|
281
|
-
updatePagesList: () => Promise.resolve(),
|
|
282
280
|
stopBrowserScreencast: () => Promise.resolve(),
|
|
283
|
-
getElementBounds: () => Promise.resolve(null),
|
|
284
|
-
updateHighlights: () => {},
|
|
285
281
|
stopAllScreencasts: () => Promise.resolve(),
|
|
286
282
|
isScreencastActive: () => false,
|
|
287
283
|
getSender: () => undefined,
|
|
@@ -251,6 +251,9 @@ function releaseStartupLock(): void {
|
|
|
251
251
|
try { unlinkSync(getStartupLockPath()); } catch { /* already removed */ }
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// NOTE: startDaemon() is the assistant-side daemon lifecycle manager.
|
|
255
|
+
// It should eventually converge with cli/src/lib/local.ts::startLocalDaemon
|
|
256
|
+
// which is the CLI-side equivalent.
|
|
254
257
|
export async function startDaemon(): Promise<{
|
|
255
258
|
pid: number;
|
|
256
259
|
alreadyRunning: boolean;
|
|
@@ -1,54 +1,8 @@
|
|
|
1
|
-
import { browserManager
|
|
2
|
-
import { defineHandlers
|
|
1
|
+
import { browserManager } from "../../tools/browser/browser-manager.js";
|
|
2
|
+
import { defineHandlers } from "./shared.js";
|
|
3
3
|
|
|
4
4
|
export const browserHandlers = defineHandlers({
|
|
5
5
|
browser_cdp_response: (msg) => {
|
|
6
6
|
browserManager.resolveCDPResponse(msg.sessionId, msg.success, msg.declined);
|
|
7
7
|
},
|
|
8
|
-
|
|
9
|
-
browser_user_click: async (msg) => {
|
|
10
|
-
try {
|
|
11
|
-
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
12
|
-
const viewport = await page.evaluate('(() => ({ vw: window.innerWidth, vh: window.innerHeight }))()') as { vw: number; vh: number };
|
|
13
|
-
const scale = Math.min(SCREENCAST_WIDTH / viewport.vw, SCREENCAST_HEIGHT / viewport.vh);
|
|
14
|
-
const pageX = msg.x / scale;
|
|
15
|
-
const pageY = msg.y / scale;
|
|
16
|
-
const options: Record<string, unknown> = {};
|
|
17
|
-
if (msg.button === 'right') options.button = 'right';
|
|
18
|
-
if (msg.doubleClick) options.clickCount = 2;
|
|
19
|
-
await page.mouse.click(pageX, pageY, options);
|
|
20
|
-
} catch (err) {
|
|
21
|
-
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user click');
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
browser_user_scroll: async (msg) => {
|
|
26
|
-
try {
|
|
27
|
-
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
28
|
-
await page.mouse.wheel(msg.deltaX, msg.deltaY);
|
|
29
|
-
} catch (err) {
|
|
30
|
-
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user scroll');
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
browser_user_keypress: async (msg) => {
|
|
35
|
-
try {
|
|
36
|
-
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
37
|
-
const combo = msg.modifiers?.length ? [...msg.modifiers, msg.key].join('+') : msg.key;
|
|
38
|
-
await page.keyboard.press(combo);
|
|
39
|
-
} catch (err) {
|
|
40
|
-
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user keypress');
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
browser_interactive_mode: (msg, socket, ctx) => {
|
|
45
|
-
log.info({ sessionId: msg.sessionId, enabled: msg.enabled }, 'Interactive mode toggled');
|
|
46
|
-
browserManager.setInteractiveMode(msg.sessionId, msg.enabled);
|
|
47
|
-
ctx.send(socket, {
|
|
48
|
-
type: 'browser_interactive_mode_changed',
|
|
49
|
-
sessionId: msg.sessionId,
|
|
50
|
-
surfaceId: msg.surfaceId,
|
|
51
|
-
enabled: msg.enabled,
|
|
52
|
-
});
|
|
53
|
-
},
|
|
54
8
|
});
|
|
@@ -1,89 +1,19 @@
|
|
|
1
1
|
// Browser interaction types.
|
|
2
2
|
|
|
3
|
-
export interface BrowserFrame {
|
|
4
|
-
type: 'browser_frame';
|
|
5
|
-
sessionId: string;
|
|
6
|
-
surfaceId: string;
|
|
7
|
-
frame: string; // base64 JPEG
|
|
8
|
-
metadata?: { offsetTop: number; pageScaleFactor: number; scrollOffsetX: number; scrollOffsetY: number; timestamp: number };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
3
|
export interface BrowserCDPRequest {
|
|
12
|
-
type:
|
|
4
|
+
type: "browser_cdp_request";
|
|
13
5
|
sessionId: string;
|
|
14
6
|
}
|
|
15
7
|
|
|
16
8
|
export interface BrowserCDPResponse {
|
|
17
|
-
type:
|
|
9
|
+
type: "browser_cdp_response";
|
|
18
10
|
sessionId: string;
|
|
19
11
|
success: boolean;
|
|
20
12
|
declined?: boolean;
|
|
21
13
|
}
|
|
22
14
|
|
|
23
|
-
export interface BrowserUserClick {
|
|
24
|
-
type: 'browser_user_click';
|
|
25
|
-
sessionId: string;
|
|
26
|
-
surfaceId: string;
|
|
27
|
-
x: number;
|
|
28
|
-
y: number;
|
|
29
|
-
button?: 'left' | 'right';
|
|
30
|
-
doubleClick?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface BrowserUserScroll {
|
|
34
|
-
type: 'browser_user_scroll';
|
|
35
|
-
sessionId: string;
|
|
36
|
-
surfaceId: string;
|
|
37
|
-
deltaX: number;
|
|
38
|
-
deltaY: number;
|
|
39
|
-
x: number;
|
|
40
|
-
y: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface BrowserUserKeypress {
|
|
44
|
-
type: 'browser_user_keypress';
|
|
45
|
-
sessionId: string;
|
|
46
|
-
surfaceId: string;
|
|
47
|
-
key: string;
|
|
48
|
-
modifiers?: string[];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface BrowserInteractiveMode {
|
|
52
|
-
type: 'browser_interactive_mode';
|
|
53
|
-
sessionId: string;
|
|
54
|
-
surfaceId: string;
|
|
55
|
-
enabled: boolean;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface BrowserInteractiveModeChanged {
|
|
59
|
-
type: 'browser_interactive_mode_changed';
|
|
60
|
-
sessionId: string;
|
|
61
|
-
surfaceId: string;
|
|
62
|
-
enabled: boolean;
|
|
63
|
-
reason?: string;
|
|
64
|
-
message?: string;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface BrowserHandoffRequest {
|
|
68
|
-
type: 'browser_handoff_request';
|
|
69
|
-
sessionId: string;
|
|
70
|
-
surfaceId: string;
|
|
71
|
-
reason: 'auth' | 'checkout' | 'captcha' | 'custom';
|
|
72
|
-
message: string;
|
|
73
|
-
bringToFront?: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
15
|
// --- Domain-level union aliases (consumed by the barrel file) ---
|
|
77
16
|
|
|
78
|
-
export type _BrowserClientMessages =
|
|
79
|
-
| BrowserCDPResponse
|
|
80
|
-
| BrowserUserClick
|
|
81
|
-
| BrowserUserScroll
|
|
82
|
-
| BrowserUserKeypress
|
|
83
|
-
| BrowserInteractiveMode;
|
|
17
|
+
export type _BrowserClientMessages = BrowserCDPResponse;
|
|
84
18
|
|
|
85
|
-
export type _BrowserServerMessages =
|
|
86
|
-
| BrowserFrame
|
|
87
|
-
| BrowserCDPRequest
|
|
88
|
-
| BrowserInteractiveModeChanged
|
|
89
|
-
| BrowserHandoffRequest;
|
|
19
|
+
export type _BrowserServerMessages = BrowserCDPRequest;
|