@framers/agentos-ext-browser-automation 0.1.0 → 0.2.1
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 +80 -0
- package/dist/BrowserService.js +9 -45
- package/dist/BrowserService.js.map +1 -1
- package/dist/attach/AttachController.d.ts +183 -0
- package/dist/attach/AttachController.d.ts.map +1 -0
- package/dist/attach/AttachController.js +286 -0
- package/dist/attach/AttachController.js.map +1 -0
- package/dist/attach/backends/jxa.d.ts +39 -0
- package/dist/attach/backends/jxa.d.ts.map +1 -0
- package/dist/attach/backends/jxa.js +155 -0
- package/dist/attach/backends/jxa.js.map +1 -0
- package/dist/attach/backends/raw-cdp.d.ts +53 -0
- package/dist/attach/backends/raw-cdp.d.ts.map +1 -0
- package/dist/attach/backends/raw-cdp.js +213 -0
- package/dist/attach/backends/raw-cdp.js.map +1 -0
- package/dist/attach/daemon/client.d.ts +64 -0
- package/dist/attach/daemon/client.d.ts.map +1 -0
- package/dist/attach/daemon/client.js +120 -0
- package/dist/attach/daemon/client.js.map +1 -0
- package/dist/attach/daemon/daemon-lock.d.ts +19 -0
- package/dist/attach/daemon/daemon-lock.d.ts.map +1 -0
- package/dist/attach/daemon/daemon-lock.js +84 -0
- package/dist/attach/daemon/daemon-lock.js.map +1 -0
- package/dist/attach/daemon/daemon-main.d.ts +17 -0
- package/dist/attach/daemon/daemon-main.d.ts.map +1 -0
- package/dist/attach/daemon/daemon-main.js +99 -0
- package/dist/attach/daemon/daemon-main.js.map +1 -0
- package/dist/attach/daemon/daemon.d.ts +52 -0
- package/dist/attach/daemon/daemon.d.ts.map +1 -0
- package/dist/attach/daemon/daemon.js +273 -0
- package/dist/attach/daemon/daemon.js.map +1 -0
- package/dist/attach/daemon/index.d.ts +12 -0
- package/dist/attach/daemon/index.d.ts.map +1 -0
- package/dist/attach/daemon/index.js +12 -0
- package/dist/attach/daemon/index.js.map +1 -0
- package/dist/attach/daemon/protocol.d.ts +69 -0
- package/dist/attach/daemon/protocol.d.ts.map +1 -0
- package/dist/attach/daemon/protocol.js +96 -0
- package/dist/attach/daemon/protocol.js.map +1 -0
- package/dist/attach/daemon/runner.d.ts +64 -0
- package/dist/attach/daemon/runner.d.ts.map +1 -0
- package/dist/attach/daemon/runner.js +57 -0
- package/dist/attach/daemon/runner.js.map +1 -0
- package/dist/attach/daemon/surface.d.ts +31 -0
- package/dist/attach/daemon/surface.d.ts.map +1 -0
- package/dist/attach/daemon/surface.js +87 -0
- package/dist/attach/daemon/surface.js.map +1 -0
- package/dist/attach/devtools-port.d.ts +37 -0
- package/dist/attach/devtools-port.d.ts.map +1 -0
- package/dist/attach/devtools-port.js +47 -0
- package/dist/attach/devtools-port.js.map +1 -0
- package/dist/attach/errors.d.ts +38 -0
- package/dist/attach/errors.d.ts.map +1 -0
- package/dist/attach/errors.js +67 -0
- package/dist/attach/errors.js.map +1 -0
- package/dist/attach/lease.d.ts +43 -0
- package/dist/attach/lease.d.ts.map +1 -0
- package/dist/attach/lease.js +87 -0
- package/dist/attach/lease.js.map +1 -0
- package/dist/attach/singleton-lock.d.ts +21 -0
- package/dist/attach/singleton-lock.d.ts.map +1 -0
- package/dist/attach/singleton-lock.js +27 -0
- package/dist/attach/singleton-lock.js.map +1 -0
- package/dist/attach/state.d.ts +31 -0
- package/dist/attach/state.d.ts.map +1 -0
- package/dist/attach/state.js +71 -0
- package/dist/attach/state.js.map +1 -0
- package/dist/attach/tools.d.ts +208 -0
- package/dist/attach/tools.d.ts.map +1 -0
- package/dist/attach/tools.js +191 -0
- package/dist/attach/tools.js.map +1 -0
- package/dist/attach/url-policy.d.ts +41 -0
- package/dist/attach/url-policy.d.ts.map +1 -0
- package/dist/attach/url-policy.js +55 -0
- package/dist/attach/url-policy.js.map +1 -0
- package/dist/attach.d.ts +18 -0
- package/dist/attach.d.ts.map +1 -0
- package/dist/attach.js +18 -0
- package/dist/attach.js.map +1 -0
- package/dist/captcha/CaptchaSolver.js +4 -7
- package/dist/captcha/CaptchaSolver.js.map +1 -1
- package/dist/captcha/ICaptchaProvider.js +1 -2
- package/dist/captcha/ICaptchaProvider.js.map +1 -1
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -65
- package/dist/index.js.map +1 -1
- package/dist/proxy/IProxyProvider.js +1 -2
- package/dist/proxy/IProxyProvider.js.map +1 -1
- package/dist/proxy/ProxyManager.js +7 -10
- package/dist/proxy/ProxyManager.js.map +1 -1
- package/dist/tools/ClickTool.js +17 -20
- package/dist/tools/ClickTool.js.map +1 -1
- package/dist/tools/EvaluateTool.js +16 -19
- package/dist/tools/EvaluateTool.js.map +1 -1
- package/dist/tools/ExtractTool.js +23 -26
- package/dist/tools/ExtractTool.js.map +1 -1
- package/dist/tools/FillTool.js +17 -20
- package/dist/tools/FillTool.js.map +1 -1
- package/dist/tools/NavigateTool.js +31 -34
- package/dist/tools/NavigateTool.js.map +1 -1
- package/dist/tools/PageSnapshotTool.js +13 -16
- package/dist/tools/PageSnapshotTool.js.map +1 -1
- package/dist/tools/ScreenshotTool.js +16 -19
- package/dist/tools/ScreenshotTool.js.map +1 -1
- package/dist/tools/ScrollTool.js +17 -20
- package/dist/tools/ScrollTool.js.map +1 -1
- package/dist/tools/SessionTool.js +24 -27
- package/dist/tools/SessionTool.js.map +1 -1
- package/dist/tools/WaitTool.js +22 -25
- package/dist/tools/WaitTool.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +23 -7
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AttachController — the one object allowed to touch an attached
|
|
3
|
+
* (user-owned, already-running) browser.
|
|
4
|
+
*
|
|
5
|
+
* Non-negotiable contract, enforced here rather than by convention:
|
|
6
|
+
*
|
|
7
|
+
* 1. ATTACH-ONLY. The controller can never launch a browser. When the target
|
|
8
|
+
* is missing or fails identity verification it ABORTS with a structured
|
|
9
|
+
* error — it must not "helpfully" spawn Chrome (Codex spec review F9).
|
|
10
|
+
* 2. NON-DESTRUCTIVE DETACH. `detach()` tears down only OUR transport
|
|
11
|
+
* connection. The backend interface deliberately has no close/quit surface,
|
|
12
|
+
* so no code path can call `Browser.close()`/`Page.close()` on the user's
|
|
13
|
+
* browser (Codex F10 — wunderland's `BrowserSession.disconnect()` calls
|
|
14
|
+
* `browser.close()` and is exactly the hazard this replaces).
|
|
15
|
+
* 3. MARKER-TAB BOUND. Every operation targets the single agent tab the
|
|
16
|
+
* controller claimed; other tabs/windows are out of reach by construction.
|
|
17
|
+
* 4. LEASED. All operations require the cross-process lease nonce acquired at
|
|
18
|
+
* `claim()` (F3), and run under the no-replay state machine with deadlines
|
|
19
|
+
* (F14) plus the deny-by-default URL policy (F12).
|
|
20
|
+
*
|
|
21
|
+
* @module browser-automation/attach/AttachController
|
|
22
|
+
*/
|
|
23
|
+
import { AttachError, toStructuredError } from './errors.js';
|
|
24
|
+
import { acquireLease, releaseLease, requireLease } from './lease.js';
|
|
25
|
+
import { AttachStateMachine, withDeadline } from './state.js';
|
|
26
|
+
import { isNavigationAllowed, revalidateRedirect } from './url-policy.js';
|
|
27
|
+
/**
|
|
28
|
+
* Orchestrates one attach session end to end. One controller instance = one
|
|
29
|
+
* lease = one agent tab.
|
|
30
|
+
*/
|
|
31
|
+
export class AttachController {
|
|
32
|
+
backend;
|
|
33
|
+
opts;
|
|
34
|
+
machine = new AttachStateMachine();
|
|
35
|
+
lease;
|
|
36
|
+
tab;
|
|
37
|
+
lastError;
|
|
38
|
+
paused = false;
|
|
39
|
+
dryRun;
|
|
40
|
+
navigations = 0;
|
|
41
|
+
lastUrl;
|
|
42
|
+
constructor(opts) {
|
|
43
|
+
this.backend = opts.backend;
|
|
44
|
+
this.opts = opts;
|
|
45
|
+
this.dryRun = !!opts.dryRun;
|
|
46
|
+
}
|
|
47
|
+
get deadline() {
|
|
48
|
+
return this.opts.deadlineMs ?? 45_000;
|
|
49
|
+
}
|
|
50
|
+
/** Non-throwing status snapshot. */
|
|
51
|
+
status() {
|
|
52
|
+
return {
|
|
53
|
+
transport: this.lease ? this.backend.kind : 'none',
|
|
54
|
+
state: this.machine.state,
|
|
55
|
+
leaseHeld: !!this.lease,
|
|
56
|
+
agentTab: this.tab,
|
|
57
|
+
lastError: this.lastError,
|
|
58
|
+
paused: this.paused,
|
|
59
|
+
dryRun: this.dryRun,
|
|
60
|
+
navigations: this.navigations,
|
|
61
|
+
lastUrl: this.lastUrl,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Pause the session — navigation/read refuse until {@link resume}. */
|
|
65
|
+
pause() {
|
|
66
|
+
this.paused = true;
|
|
67
|
+
}
|
|
68
|
+
/** Resume a paused session. */
|
|
69
|
+
resume() {
|
|
70
|
+
this.paused = false;
|
|
71
|
+
}
|
|
72
|
+
/** Toggle dry-run at runtime (simulate navigation/reads, don't perform them). */
|
|
73
|
+
setDryRun(on) {
|
|
74
|
+
this.dryRun = on;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Acquire the lease, open the transport, verify profile identity, and claim
|
|
78
|
+
* the marker agent tab. Aborts (never launches) on any verification failure.
|
|
79
|
+
*/
|
|
80
|
+
async claim() {
|
|
81
|
+
this.machine.to('connecting');
|
|
82
|
+
try {
|
|
83
|
+
this.lease = acquireLease({ file: this.opts.leaseFile });
|
|
84
|
+
await withDeadline(this.backend.connect(), this.deadline, 'connect');
|
|
85
|
+
const identity = await withDeadline(this.backend.probeIdentity(), this.deadline, 'probeIdentity');
|
|
86
|
+
if (!identity.includes(this.opts.expectedIdentity)) {
|
|
87
|
+
throw new AttachError('PROFILE_MISMATCH', `attached browser did not present the expected profile identity (looked for "${this.opts.expectedIdentity}")`);
|
|
88
|
+
}
|
|
89
|
+
this.tab = await withDeadline(this.backend.claimAgentTab(), this.deadline, 'claimAgentTab');
|
|
90
|
+
this.machine.to('ready');
|
|
91
|
+
return this.status();
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.lastError = toStructuredError(err);
|
|
95
|
+
this.machine.to('failed');
|
|
96
|
+
await this.safeReleaseOnFailure();
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Navigate the agent tab (policy-checked, HITL-gated, redirect-revalidated). */
|
|
101
|
+
async goto(url) {
|
|
102
|
+
this.requireReady();
|
|
103
|
+
const verdict = isNavigationAllowed(url, this.opts.urlPolicy);
|
|
104
|
+
if (!verdict.allowed) {
|
|
105
|
+
throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" rejected: ${verdict.reason}`);
|
|
106
|
+
}
|
|
107
|
+
// HITL gate: after the policy passes, an approver may still block the page.
|
|
108
|
+
if (this.opts.onNavigate) {
|
|
109
|
+
const decision = await this.opts.onNavigate(url);
|
|
110
|
+
const approved = typeof decision === 'boolean' ? decision : decision.approved;
|
|
111
|
+
if (!approved) {
|
|
112
|
+
const reason = typeof decision === 'object' && decision.reason ? decision.reason : 'operator denied';
|
|
113
|
+
throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" denied by approver: ${reason}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
requireLease(this.opts.leaseFile, this.lease.nonce);
|
|
117
|
+
// Dry-run: report the URL as the landed target without touching the browser.
|
|
118
|
+
if (this.dryRun) {
|
|
119
|
+
this.navigations += 1;
|
|
120
|
+
this.lastUrl = url;
|
|
121
|
+
return url;
|
|
122
|
+
}
|
|
123
|
+
this.machine.to('navigating');
|
|
124
|
+
try {
|
|
125
|
+
const landed = await withDeadline(this.backend.gotoTab(this.tab, url), this.deadline, 'goto');
|
|
126
|
+
this.navigations += 1;
|
|
127
|
+
this.lastUrl = landed;
|
|
128
|
+
const landedVerdict = revalidateRedirect(landed, this.opts.urlPolicy);
|
|
129
|
+
if (!landedVerdict.allowed) {
|
|
130
|
+
this.machine.to('ready');
|
|
131
|
+
throw new AttachError('POLICY_BLOCKED', `redirect landed on a blocked target: ${landedVerdict.reason}`);
|
|
132
|
+
}
|
|
133
|
+
this.machine.to('ready');
|
|
134
|
+
return landed;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (this.machine.state === 'navigating')
|
|
138
|
+
this.machine.to('failed');
|
|
139
|
+
this.lastError = toStructuredError(err);
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Read text from the agent tab (size-capped by the backend). */
|
|
144
|
+
async read(selector, maxChars) {
|
|
145
|
+
this.requireReady();
|
|
146
|
+
requireLease(this.opts.leaseFile, this.lease.nonce);
|
|
147
|
+
if (this.dryRun)
|
|
148
|
+
return '[dry-run] read skipped';
|
|
149
|
+
this.machine.to('reading');
|
|
150
|
+
try {
|
|
151
|
+
const text = await withDeadline(this.backend.readTab(this.tab, selector, maxChars), this.deadline, 'read');
|
|
152
|
+
this.machine.to('ready');
|
|
153
|
+
return text;
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (this.machine.state === 'reading')
|
|
157
|
+
this.machine.to('failed');
|
|
158
|
+
this.lastError = toStructuredError(err);
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Fixed, parameterized extraction expression: the selector map is data, never code. */
|
|
163
|
+
static extractExpression(fields) {
|
|
164
|
+
const spec = JSON.stringify(fields ?? {});
|
|
165
|
+
return `(() => {
|
|
166
|
+
const fields = ${spec};
|
|
167
|
+
const grab = (sel) => [...document.querySelectorAll(sel)].map((e) => (e.textContent || '').trim()).filter(Boolean);
|
|
168
|
+
if (Object.keys(fields).length) {
|
|
169
|
+
const out = {};
|
|
170
|
+
for (const [name, sel] of Object.entries(fields)) out[name] = grab(sel).slice(0, 200);
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
url: location.href,
|
|
175
|
+
title: document.title,
|
|
176
|
+
heading: (document.querySelector('h1,h2') || {}).textContent?.trim() || '',
|
|
177
|
+
links: [...document.querySelectorAll('a[href]')].map((a) => ({ href: a.href, text: (a.textContent || '').trim().slice(0, 60) })).slice(0, 500),
|
|
178
|
+
text: (document.body.innerText || '').replace(/\\s+/g, ' ').slice(0, 4000),
|
|
179
|
+
};
|
|
180
|
+
})()`;
|
|
181
|
+
}
|
|
182
|
+
/** The backend's optional eval primitive, or a structured refusal. */
|
|
183
|
+
requireEval() {
|
|
184
|
+
const fn = this.backend.evalInTab?.bind(this.backend);
|
|
185
|
+
if (!fn) {
|
|
186
|
+
throw new AttachError('UNSUPPORTED_OP', `${this.backend.kind} backend has no page-evaluation capability`);
|
|
187
|
+
}
|
|
188
|
+
return fn;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Structured extraction over a fixed, parameterized expression: for each
|
|
192
|
+
* `name: cssSelector` pair, trimmed textContent lists; with no fields, a
|
|
193
|
+
* default page summary (url/title/heading/links/text). No caller-supplied
|
|
194
|
+
* code ever reaches the page — selectors are data.
|
|
195
|
+
*/
|
|
196
|
+
async extract(fields) {
|
|
197
|
+
this.requireReady();
|
|
198
|
+
const evalFn = this.requireEval();
|
|
199
|
+
requireLease(this.opts.leaseFile, this.lease.nonce);
|
|
200
|
+
if (this.dryRun)
|
|
201
|
+
return { dryRun: true };
|
|
202
|
+
this.machine.to('reading');
|
|
203
|
+
try {
|
|
204
|
+
const out = await withDeadline(evalFn(this.tab, AttachController.extractExpression(fields)), this.deadline, 'extract');
|
|
205
|
+
this.machine.to('ready');
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
if (this.machine.state === 'reading')
|
|
210
|
+
this.machine.to('failed');
|
|
211
|
+
this.lastError = toStructuredError(err);
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Arbitrary page evaluation. USER LANE ONLY: reachable through the daemon
|
|
217
|
+
* protocol's `eval` op and the JS client; no agent tool maps to it and the
|
|
218
|
+
* tool-facing surface has no eval member.
|
|
219
|
+
*/
|
|
220
|
+
async evaluate(expression, timeoutMs) {
|
|
221
|
+
this.requireReady();
|
|
222
|
+
const evalFn = this.requireEval();
|
|
223
|
+
requireLease(this.opts.leaseFile, this.lease.nonce);
|
|
224
|
+
if (this.dryRun)
|
|
225
|
+
return '[dry-run] evaluate skipped';
|
|
226
|
+
this.machine.to('reading');
|
|
227
|
+
try {
|
|
228
|
+
const out = await withDeadline(evalFn(this.tab, expression, timeoutMs), Math.max(this.deadline, (timeoutMs ?? 0) + 5_000), 'evaluate');
|
|
229
|
+
this.machine.to('ready');
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
if (this.machine.state === 'reading')
|
|
234
|
+
this.machine.to('failed');
|
|
235
|
+
this.lastError = toStructuredError(err);
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Release the session: park is the CALLER's responsibility (navigate to
|
|
241
|
+
* about:blank first if desired), then this closes only our transport and
|
|
242
|
+
* releases the lease. There is no code path to close browser/tabs.
|
|
243
|
+
*/
|
|
244
|
+
async detach() {
|
|
245
|
+
try {
|
|
246
|
+
await this.backend.disconnectTransport();
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
if (this.lease)
|
|
250
|
+
releaseLease(this.opts.leaseFile, this.lease.nonce);
|
|
251
|
+
this.lease = undefined;
|
|
252
|
+
this.tab = undefined;
|
|
253
|
+
if (this.machine.state !== 'disconnected') {
|
|
254
|
+
if (this.machine.state !== 'failed')
|
|
255
|
+
this.machine.to('failed');
|
|
256
|
+
this.machine.to('disconnected');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
requireReady() {
|
|
261
|
+
if (this.paused) {
|
|
262
|
+
throw new AttachError('UNKNOWN', 'attach session is paused — call resume() before further operations');
|
|
263
|
+
}
|
|
264
|
+
if (this.machine.state !== 'ready' || !this.tab || !this.lease) {
|
|
265
|
+
throw new AttachError('UNKNOWN', `attach session not ready (state=${this.machine.state}) — call claim() first`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async safeReleaseOnFailure() {
|
|
269
|
+
try {
|
|
270
|
+
await this.backend.disconnectTransport();
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
/* transport already dead */
|
|
274
|
+
}
|
|
275
|
+
if (this.lease) {
|
|
276
|
+
try {
|
|
277
|
+
releaseLease(this.opts.leaseFile, this.lease.nonce);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
/* foreign lease — leave it */
|
|
281
|
+
}
|
|
282
|
+
this.lease = undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=AttachController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttachController.js","sourceRoot":"","sources":["../../src/attach/AttachController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AttachBackend } from '../AttachController.js';
|
|
2
|
+
/** Options for {@link JxaBackend}. */
|
|
3
|
+
export interface JxaBackendOptions {
|
|
4
|
+
/** Chrome profile root (defaults to the macOS default profile root). */
|
|
5
|
+
profileRoot?: string;
|
|
6
|
+
/** Identity probe URL (a page whose title reveals the signed-in account). */
|
|
7
|
+
identityProbeUrl?: string;
|
|
8
|
+
/** Per-exec timeout ms (default 40s). */
|
|
9
|
+
execTimeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the exact osascript argv for a driver invocation. Pure + exported for
|
|
13
|
+
* unit tests: arguments pass as argv (never shell-interpolated). The driver
|
|
14
|
+
* receives `profileRoot` as its first arg, then the command and its args.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildJxaArgs(script: string, profileRoot: string, cmd: string, args: string[]): string[];
|
|
17
|
+
/** The embedded JXA driver source (written to a temp file at first use). */
|
|
18
|
+
export declare const JXA_DRIVER_SOURCE = "ObjC.import('Foundation');\nfunction chromePid(root) {\n const dest = $.NSFileManager.defaultManager.destinationOfSymbolicLinkAtPathError(root + '/SingletonLock', null);\n if (!dest || !dest.js) throw new Error('SingletonLock unreadable');\n return parseInt(dest.js.match(/-(\\d+)$/)[1], 10);\n}\nfunction run(argv) {\n const root = argv[0], cmd = argv[1];\n const c = Application(chromePid(root));\n c.includeStandardAdditions = false;\n const MARKER = '__agentos_attach_tab_v1__';\n if (cmd === 'claim') {\n for (const w of c.windows()) {\n for (const t of w.tabs()) {\n if (t.url() === 'about:blank') {\n try { c.execute(t, { javascript: 'window.name=' + JSON.stringify(MARKER) }); } catch (e) {}\n return w.id() + ' ' + t.id();\n }\n }\n }\n throw new Error('NO_BLANK_TAB: open a fresh tab (about:blank) in the target profile first');\n }\n const wid = argv[2], tid = argv[3];\n const w = c.windows().find((x) => String(x.id()) === String(wid));\n if (!w) throw new Error('window ' + wid + ' not found');\n const t = w.tabs().find((x) => String(x.id()) === String(tid));\n if (!t) throw new Error('tab ' + tid + ' not found in window ' + wid);\n if (cmd === 'goto') {\n t.url = argv[4];\n for (let i = 0; i < 80; i++) { delay(0.5); try { if (t.loading() === false) break; } catch (e) { break; } }\n delay(1);\n return t.url();\n }\n if (cmd === 'url') return t.url() + '\\n' + t.title();\n if (cmd === 'read') {\n const sel = argv[4] || 'body';\n const max = parseInt(argv[5] || '6000', 10);\n const js = '(document.querySelector(' + JSON.stringify(sel) + ')||document.body).innerText.slice(0,' + max + ')';\n const r = c.execute(t, { javascript: js });\n return typeof r === 'string' ? r : JSON.stringify(r);\n }\n throw new Error('unknown cmd ' + cmd);\n}\n";
|
|
19
|
+
/** macOS JXA transport backend. */
|
|
20
|
+
export declare class JxaBackend implements AttachBackend {
|
|
21
|
+
readonly kind: "jxa";
|
|
22
|
+
private readonly profileRoot;
|
|
23
|
+
private readonly identityProbeUrl;
|
|
24
|
+
private readonly execTimeoutMs;
|
|
25
|
+
private driverPath?;
|
|
26
|
+
private claimed?;
|
|
27
|
+
constructor(opts?: JxaBackendOptions);
|
|
28
|
+
/** Verify the SingletonLock resolves (Chrome is running) — no side effects. */
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
/** Nothing persistent to tear down: each op is a discrete osascript exec. */
|
|
31
|
+
disconnectTransport(): Promise<void>;
|
|
32
|
+
private driver;
|
|
33
|
+
private exec;
|
|
34
|
+
claimAgentTab(): Promise<string>;
|
|
35
|
+
probeIdentity(): Promise<string>;
|
|
36
|
+
gotoTab(tab: string, url: string): Promise<string>;
|
|
37
|
+
readTab(tab: string, selector?: string, maxChars?: number): Promise<string>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=jxa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jxa.d.ts","sourceRoot":"","sources":["../../../src/attach/backends/jxa.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAEvG;AAED,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,00DA2C7B,CAAC;AAEF,mCAAmC;AACnC,qBAAa,UAAW,YAAW,aAAa;IAC9C,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAC,CAA+B;gBAEnC,IAAI,GAAE,iBAAsB;IAMxC,+EAA+E;IACzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B,6EAA6E;IACvE,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;YAI5B,MAAM;YAWN,IAAI;IAeZ,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAQhC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAQhC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAIlF"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview JXA (macOS) attach backend.
|
|
3
|
+
*
|
|
4
|
+
* Drives the user's RUNNING Chrome through `osascript -l JavaScript`,
|
|
5
|
+
* addressing the application BY PID resolved from the profile root's
|
|
6
|
+
* `SingletonLock` symlink — bundle-name addressing is ambiguous when more than
|
|
7
|
+
* one Chrome instance runs (e.g. a devtools-mcp cache instance) and MUST NOT
|
|
8
|
+
* be used. Navigation and reads work on background tabs; nothing here focuses,
|
|
9
|
+
* creates windows beyond the agent tab, or closes anything.
|
|
10
|
+
*
|
|
11
|
+
* The embedded driver mirrors the field-proven `vca-jxa.js` contract:
|
|
12
|
+
* claim (marker-binds an about:blank tab), goto (poll `loading`), url, and a
|
|
13
|
+
* selector-scoped innerText read. Raw arbitrary page JS is deliberately NOT
|
|
14
|
+
* exposed (Codex spec review F2).
|
|
15
|
+
*
|
|
16
|
+
* @module browser-automation/attach/backends/jxa
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from 'node:child_process';
|
|
19
|
+
import { promisify } from 'node:util';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { readlinkSync } from 'node:fs';
|
|
23
|
+
import { pidFromSingletonLock } from '../singleton-lock.js';
|
|
24
|
+
import { AttachError } from '../errors.js';
|
|
25
|
+
const pExecFile = promisify(execFile);
|
|
26
|
+
/**
|
|
27
|
+
* Build the exact osascript argv for a driver invocation. Pure + exported for
|
|
28
|
+
* unit tests: arguments pass as argv (never shell-interpolated). The driver
|
|
29
|
+
* receives `profileRoot` as its first arg, then the command and its args.
|
|
30
|
+
*/
|
|
31
|
+
export function buildJxaArgs(script, profileRoot, cmd, args) {
|
|
32
|
+
return ['-l', 'JavaScript', script, profileRoot, cmd, ...args];
|
|
33
|
+
}
|
|
34
|
+
/** The embedded JXA driver source (written to a temp file at first use). */
|
|
35
|
+
export const JXA_DRIVER_SOURCE = `ObjC.import('Foundation');
|
|
36
|
+
function chromePid(root) {
|
|
37
|
+
const dest = $.NSFileManager.defaultManager.destinationOfSymbolicLinkAtPathError(root + '/SingletonLock', null);
|
|
38
|
+
if (!dest || !dest.js) throw new Error('SingletonLock unreadable');
|
|
39
|
+
return parseInt(dest.js.match(/-(\\d+)$/)[1], 10);
|
|
40
|
+
}
|
|
41
|
+
function run(argv) {
|
|
42
|
+
const root = argv[0], cmd = argv[1];
|
|
43
|
+
const c = Application(chromePid(root));
|
|
44
|
+
c.includeStandardAdditions = false;
|
|
45
|
+
const MARKER = '__agentos_attach_tab_v1__';
|
|
46
|
+
if (cmd === 'claim') {
|
|
47
|
+
for (const w of c.windows()) {
|
|
48
|
+
for (const t of w.tabs()) {
|
|
49
|
+
if (t.url() === 'about:blank') {
|
|
50
|
+
try { c.execute(t, { javascript: 'window.name=' + JSON.stringify(MARKER) }); } catch (e) {}
|
|
51
|
+
return w.id() + ' ' + t.id();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw new Error('NO_BLANK_TAB: open a fresh tab (about:blank) in the target profile first');
|
|
56
|
+
}
|
|
57
|
+
const wid = argv[2], tid = argv[3];
|
|
58
|
+
const w = c.windows().find((x) => String(x.id()) === String(wid));
|
|
59
|
+
if (!w) throw new Error('window ' + wid + ' not found');
|
|
60
|
+
const t = w.tabs().find((x) => String(x.id()) === String(tid));
|
|
61
|
+
if (!t) throw new Error('tab ' + tid + ' not found in window ' + wid);
|
|
62
|
+
if (cmd === 'goto') {
|
|
63
|
+
t.url = argv[4];
|
|
64
|
+
for (let i = 0; i < 80; i++) { delay(0.5); try { if (t.loading() === false) break; } catch (e) { break; } }
|
|
65
|
+
delay(1);
|
|
66
|
+
return t.url();
|
|
67
|
+
}
|
|
68
|
+
if (cmd === 'url') return t.url() + '\\n' + t.title();
|
|
69
|
+
if (cmd === 'read') {
|
|
70
|
+
const sel = argv[4] || 'body';
|
|
71
|
+
const max = parseInt(argv[5] || '6000', 10);
|
|
72
|
+
const js = '(document.querySelector(' + JSON.stringify(sel) + ')||document.body).innerText.slice(0,' + max + ')';
|
|
73
|
+
const r = c.execute(t, { javascript: js });
|
|
74
|
+
return typeof r === 'string' ? r : JSON.stringify(r);
|
|
75
|
+
}
|
|
76
|
+
throw new Error('unknown cmd ' + cmd);
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
/** macOS JXA transport backend. */
|
|
80
|
+
export class JxaBackend {
|
|
81
|
+
kind = 'jxa';
|
|
82
|
+
profileRoot;
|
|
83
|
+
identityProbeUrl;
|
|
84
|
+
execTimeoutMs;
|
|
85
|
+
driverPath;
|
|
86
|
+
claimed;
|
|
87
|
+
constructor(opts = {}) {
|
|
88
|
+
this.profileRoot = opts.profileRoot ?? join(homedir(), 'Library/Application Support/Google/Chrome');
|
|
89
|
+
this.identityProbeUrl = opts.identityProbeUrl ?? 'https://mail.google.com/mail/u/0/';
|
|
90
|
+
this.execTimeoutMs = opts.execTimeoutMs ?? 40_000;
|
|
91
|
+
}
|
|
92
|
+
/** Verify the SingletonLock resolves (Chrome is running) — no side effects. */
|
|
93
|
+
async connect() {
|
|
94
|
+
let target;
|
|
95
|
+
try {
|
|
96
|
+
target = readlinkSync(join(this.profileRoot, 'SingletonLock'));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
throw new AttachError('CDP_UNAVAILABLE', 'no running Chrome found for the target profile root (SingletonLock absent)');
|
|
100
|
+
}
|
|
101
|
+
pidFromSingletonLock(target); // throws on malformed
|
|
102
|
+
}
|
|
103
|
+
/** Nothing persistent to tear down: each op is a discrete osascript exec. */
|
|
104
|
+
async disconnectTransport() {
|
|
105
|
+
this.claimed = undefined;
|
|
106
|
+
}
|
|
107
|
+
async driver() {
|
|
108
|
+
if (this.driverPath)
|
|
109
|
+
return this.driverPath;
|
|
110
|
+
const { mkdtempSync, writeFileSync } = await import('node:fs');
|
|
111
|
+
const { tmpdir } = await import('node:os');
|
|
112
|
+
const dir = mkdtempSync(join(tmpdir(), 'attach-jxa-'));
|
|
113
|
+
const file = join(dir, 'driver.js');
|
|
114
|
+
writeFileSync(file, JXA_DRIVER_SOURCE);
|
|
115
|
+
this.driverPath = file;
|
|
116
|
+
return file;
|
|
117
|
+
}
|
|
118
|
+
async exec(cmd, args) {
|
|
119
|
+
const script = await this.driver();
|
|
120
|
+
const argv = buildJxaArgs(script, this.profileRoot, cmd, args);
|
|
121
|
+
try {
|
|
122
|
+
const { stdout } = await pExecFile('osascript', argv, { timeout: this.execTimeoutMs });
|
|
123
|
+
return stdout.trim();
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
127
|
+
throw new AttachError(/turned off/i.test(msg) ? 'JS_DISABLED' : /not authorized/i.test(msg) ? 'ACCESSIBILITY_DENIED' : 'UNKNOWN', msg);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async claimAgentTab() {
|
|
131
|
+
const out = await this.exec('claim', []);
|
|
132
|
+
const [wid, tid] = out.split(/\s+/);
|
|
133
|
+
if (!wid || !tid)
|
|
134
|
+
throw new AttachError('TAB_CLOSED', `claim returned malformed ids: "${out}"`);
|
|
135
|
+
this.claimed = { wid, tid };
|
|
136
|
+
return `${wid} ${tid}`;
|
|
137
|
+
}
|
|
138
|
+
async probeIdentity() {
|
|
139
|
+
// Claim first if needed so the probe never touches a user tab.
|
|
140
|
+
if (!this.claimed)
|
|
141
|
+
await this.claimAgentTab();
|
|
142
|
+
const { wid, tid } = this.claimed;
|
|
143
|
+
await this.exec('goto', [wid, tid, this.identityProbeUrl]);
|
|
144
|
+
return this.exec('url', [wid, tid]);
|
|
145
|
+
}
|
|
146
|
+
async gotoTab(tab, url) {
|
|
147
|
+
const [wid, tid] = tab.split(/\s+/);
|
|
148
|
+
return this.exec('goto', [wid, tid, url]);
|
|
149
|
+
}
|
|
150
|
+
async readTab(tab, selector, maxChars) {
|
|
151
|
+
const [wid, tid] = tab.split(/\s+/);
|
|
152
|
+
return this.exec('read', [wid, tid, selector ?? 'body', String(maxChars ?? 6000)]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=jxa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jxa.js","sourceRoot":"","sources":["../../../src/attach/backends/jxa.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AttachBackend } from '../AttachController.js';
|
|
2
|
+
/** Options for {@link RawCdpBackend}. */
|
|
3
|
+
export interface RawCdpBackendOptions {
|
|
4
|
+
/** Chrome profile root holding `DevToolsActivePort` (default: macOS default profile root). */
|
|
5
|
+
profileRoot?: string;
|
|
6
|
+
/** Identity probe: substring matched against page-target urls (default `mail.google.com`). */
|
|
7
|
+
identityProbeUrl?: string;
|
|
8
|
+
/** Post-navigation settle delay ms (default 1800; the daemon sets 0 and settles per op). */
|
|
9
|
+
settleMs?: number;
|
|
10
|
+
/** Per-CDP-command timeout ms (default 20000). */
|
|
11
|
+
commandTimeoutMs?: number;
|
|
12
|
+
/** ws open timeout ms (default 15000). */
|
|
13
|
+
connectTimeoutMs?: number;
|
|
14
|
+
/** Eval result size cap in serialized chars (default 512 * 1024). */
|
|
15
|
+
evalMaxChars?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Raw single-target CDP backend (see fileoverview for the contract).
|
|
19
|
+
*
|
|
20
|
+
* Never sends `Browser.close`; `Target.closeTarget` is only ever issued for
|
|
21
|
+
* the tab this instance created.
|
|
22
|
+
*/
|
|
23
|
+
export declare class RawCdpBackend implements AttachBackend {
|
|
24
|
+
readonly kind: "cdp";
|
|
25
|
+
private readonly opts;
|
|
26
|
+
private ws;
|
|
27
|
+
private nextId;
|
|
28
|
+
private readonly pending;
|
|
29
|
+
private sessionId;
|
|
30
|
+
private createdTargetId;
|
|
31
|
+
/** Invoked once when the transport drops unexpectedly (daemon schedules backoff reconnect). */
|
|
32
|
+
onUnexpectedClose: ((err: Error) => void) | null;
|
|
33
|
+
constructor(options?: RawCdpBackendOptions);
|
|
34
|
+
/** Open the ws to the browser endpoint. Creates/focuses NOTHING. Idempotent while connected. */
|
|
35
|
+
connect(): Promise<void>;
|
|
36
|
+
/** Close ONLY our created tab (if any), then the ws. Never `Browser.close`. */
|
|
37
|
+
disconnectTransport(): Promise<void>;
|
|
38
|
+
/** Create the agent tab, attach flattened to it alone, enable Page/Runtime on that session only. */
|
|
39
|
+
claimAgentTab(): Promise<string>;
|
|
40
|
+
/** Metadata-only identity read: matching page targets' titles/urls. No attach, no navigation. */
|
|
41
|
+
probeIdentity(): Promise<string>;
|
|
42
|
+
/** Navigate the agent tab, settle, return the landed URL. */
|
|
43
|
+
gotoTab(tab: string, url: string): Promise<string>;
|
|
44
|
+
/** innerText read, optional selector scope, capped. */
|
|
45
|
+
readTab(tab: string, selector?: string, maxChars?: number): Promise<string>;
|
|
46
|
+
/** Runtime.evaluate in the agent tab (optional backend capability used by extract/evaluate). */
|
|
47
|
+
evalInTab(tab: string, expression: string, timeoutMs?: number): Promise<unknown>;
|
|
48
|
+
private requireTab;
|
|
49
|
+
private onMessage;
|
|
50
|
+
private failAllPending;
|
|
51
|
+
private send;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=raw-cdp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-cdp.d.ts","sourceRoot":"","sources":["../../../src/attach/backends/raw-cdp.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAWD;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,eAAe,CAAuB;IAC9C,+FAA+F;IAC/F,iBAAiB,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;gBAE5C,OAAO,GAAE,oBAAyB;IAW9C,gGAAgG;IAC1F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqD9B,+EAA+E;IACzE,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB1C,oGAAoG;IAC9F,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAatC,iGAAiG;IAC3F,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAUtC,6DAA6D;IACvD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOxD,uDAAuD;IACjD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ/E,gGAAgG;IAC1F,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBtF,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,IAAI;CAoBb"}
|