@framers/agentos-ext-browser-automation 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attach/AttachController.d.ts +183 -0
- package/dist/attach/AttachController.d.ts.map +1 -0
- package/dist/attach/AttachController.js +283 -0
- package/dist/attach/AttachController.js.map +1 -0
- package/dist/attach/__tests__/AttachController.test.d.ts +2 -0
- package/dist/attach/__tests__/AttachController.test.d.ts.map +1 -0
- package/dist/attach/__tests__/AttachController.test.js +278 -0
- package/dist/attach/__tests__/AttachController.test.js.map +1 -0
- package/dist/attach/__tests__/attach-primitives.test.d.ts +2 -0
- package/dist/attach/__tests__/attach-primitives.test.d.ts.map +1 -0
- package/dist/attach/__tests__/attach-primitives.test.js +158 -0
- package/dist/attach/__tests__/attach-primitives.test.js.map +1 -0
- package/dist/attach/__tests__/jxa-args.test.d.ts +2 -0
- package/dist/attach/__tests__/jxa-args.test.d.ts.map +1 -0
- package/dist/attach/__tests__/jxa-args.test.js +37 -0
- package/dist/attach/__tests__/jxa-args.test.js.map +1 -0
- package/dist/attach/__tests__/pack-registration.test.d.ts +2 -0
- package/dist/attach/__tests__/pack-registration.test.d.ts.map +1 -0
- package/dist/attach/__tests__/pack-registration.test.js +34 -0
- package/dist/attach/__tests__/pack-registration.test.js.map +1 -0
- package/dist/attach/__tests__/tools.test.d.ts +2 -0
- package/dist/attach/__tests__/tools.test.d.ts.map +1 -0
- package/dist/attach/__tests__/tools.test.js +155 -0
- package/dist/attach/__tests__/tools.test.js.map +1 -0
- package/dist/attach/backends/__tests__/raw-cdp.test.d.ts +2 -0
- package/dist/attach/backends/__tests__/raw-cdp.test.d.ts.map +1 -0
- package/dist/attach/backends/__tests__/raw-cdp.test.js +133 -0
- package/dist/attach/backends/__tests__/raw-cdp.test.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 +188 -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 +216 -0
- package/dist/attach/backends/raw-cdp.js.map +1 -0
- package/dist/attach/daemon/__tests__/client.test.d.ts +2 -0
- package/dist/attach/daemon/__tests__/client.test.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/client.test.js +58 -0
- package/dist/attach/daemon/__tests__/client.test.js.map +1 -0
- package/dist/attach/daemon/__tests__/daemon-lock.test.d.ts +2 -0
- package/dist/attach/daemon/__tests__/daemon-lock.test.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/daemon-lock.test.js +72 -0
- package/dist/attach/daemon/__tests__/daemon-lock.test.js.map +1 -0
- package/dist/attach/daemon/__tests__/daemon-loop.integration.test.d.ts +2 -0
- package/dist/attach/daemon/__tests__/daemon-loop.integration.test.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/daemon-loop.integration.test.js +102 -0
- package/dist/attach/daemon/__tests__/daemon-loop.integration.test.js.map +1 -0
- package/dist/attach/daemon/__tests__/harness.d.ts +44 -0
- package/dist/attach/daemon/__tests__/harness.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/harness.js +96 -0
- package/dist/attach/daemon/__tests__/harness.js.map +1 -0
- package/dist/attach/daemon/__tests__/protocol.test.d.ts +2 -0
- package/dist/attach/daemon/__tests__/protocol.test.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/protocol.test.js +71 -0
- package/dist/attach/daemon/__tests__/protocol.test.js.map +1 -0
- package/dist/attach/daemon/__tests__/runner.test.d.ts +2 -0
- package/dist/attach/daemon/__tests__/runner.test.d.ts.map +1 -0
- package/dist/attach/daemon/__tests__/runner.test.js +67 -0
- package/dist/attach/daemon/__tests__/runner.test.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 +87 -0
- package/dist/attach/daemon/daemon-lock.js.map +1 -0
- package/dist/attach/daemon/daemon-main.d.ts +6 -0
- package/dist/attach/daemon/daemon-main.d.ts.map +1 -0
- package/dist/attach/daemon/daemon-main.js +80 -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 +264 -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 +30 -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 +108 -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 +60 -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 +89 -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 +50 -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 +72 -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 +92 -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 +30 -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 +78 -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 +195 -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 +59 -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 +34 -0
- package/dist/attach.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/package.json +22 -7
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HEARTBEAT_STALE_MS = exports.STALE_FILE_MS = exports.DEFAULT_PID_FILE = exports.DEFAULT_IPC_DIR = void 0;
|
|
4
|
+
exports.resolveIpcDir = resolveIpcDir;
|
|
5
|
+
exports.newCmdId = newCmdId;
|
|
6
|
+
exports.cmdPath = cmdPath;
|
|
7
|
+
exports.respPath = respPath;
|
|
8
|
+
exports.statusPath = statusPath;
|
|
9
|
+
exports.writeAtomicJson = writeAtomicJson;
|
|
10
|
+
exports.readJson = readJson;
|
|
11
|
+
exports.listPendingCmds = listPendingCmds;
|
|
12
|
+
exports.gcStale = gcStale;
|
|
13
|
+
/**
|
|
14
|
+
* @fileoverview File-queue IPC protocol for the attach daemon.
|
|
15
|
+
*
|
|
16
|
+
* One file per command (`cmd-<id>.json`), one per response (`resp-<id>.json`),
|
|
17
|
+
* plus an atomically maintained `status.json` heartbeat. All writes are
|
|
18
|
+
* write-then-rename so a poller can never observe a torn body. Ids are STRINGS:
|
|
19
|
+
* numeric ids at `time_ns` scale exceed JS 2^53 integer precision and fail the
|
|
20
|
+
* round-trip match (proven by the reference driver).
|
|
21
|
+
*
|
|
22
|
+
* Only the daemon process ever touches the network; every consumer of this
|
|
23
|
+
* module reads and writes local files, so no caller can trigger the per-process
|
|
24
|
+
* macOS Local Network prompt.
|
|
25
|
+
*
|
|
26
|
+
* @module browser-automation/attach/daemon/protocol
|
|
27
|
+
*/
|
|
28
|
+
const node_fs_1 = require("node:fs");
|
|
29
|
+
const node_os_1 = require("node:os");
|
|
30
|
+
const node_path_1 = require("node:path");
|
|
31
|
+
const node_crypto_1 = require("node:crypto");
|
|
32
|
+
/** Default IPC directory (created 0700). */
|
|
33
|
+
exports.DEFAULT_IPC_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.wunderland', 'attach', 'ipc');
|
|
34
|
+
/** Default daemon pidfile. */
|
|
35
|
+
exports.DEFAULT_PID_FILE = (0, node_path_1.join)((0, node_os_1.homedir)(), '.wunderland', 'attach', 'daemon.pid');
|
|
36
|
+
/** cmd/resp files older than this are garbage-collected. */
|
|
37
|
+
exports.STALE_FILE_MS = 10 * 60000;
|
|
38
|
+
/** status.json heartbeats older than this mark the daemon stale. */
|
|
39
|
+
exports.HEARTBEAT_STALE_MS = 30000;
|
|
40
|
+
/** Resolve the IPC dir from an explicit override or env, ensuring it exists 0700. */
|
|
41
|
+
function resolveIpcDir(override, env = process.env) {
|
|
42
|
+
const dir = override?.trim() || env.WUNDERLAND_ATTACH_IPC_DIR?.trim() || exports.DEFAULT_IPC_DIR;
|
|
43
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
44
|
+
return dir;
|
|
45
|
+
}
|
|
46
|
+
/** New string command id: `<client>-<epochMs>-<random8>`. */
|
|
47
|
+
function newCmdId(client) {
|
|
48
|
+
return `${client}-${Date.now()}-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
49
|
+
}
|
|
50
|
+
/** Path of a command file. */
|
|
51
|
+
function cmdPath(dir, id) {
|
|
52
|
+
return (0, node_path_1.join)(dir, `cmd-${id}.json`);
|
|
53
|
+
}
|
|
54
|
+
/** Path of a response file. */
|
|
55
|
+
function respPath(dir, id) {
|
|
56
|
+
return (0, node_path_1.join)(dir, `resp-${id}.json`);
|
|
57
|
+
}
|
|
58
|
+
/** Path of the daemon heartbeat file. */
|
|
59
|
+
function statusPath(dir) {
|
|
60
|
+
return (0, node_path_1.join)(dir, 'status.json');
|
|
61
|
+
}
|
|
62
|
+
/** Write JSON via tmp-then-rename so readers never see a torn body. */
|
|
63
|
+
function writeAtomicJson(file, value) {
|
|
64
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
65
|
+
(0, node_fs_1.writeFileSync)(tmp, JSON.stringify(value), { mode: 0o600 });
|
|
66
|
+
(0, node_fs_1.renameSync)(tmp, file);
|
|
67
|
+
}
|
|
68
|
+
/** Read+parse JSON; undefined when missing or torn. */
|
|
69
|
+
function readJson(file) {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Pending command files, oldest first (mtime, then name for determinism). */
|
|
78
|
+
function listPendingCmds(dir) {
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const name of (0, node_fs_1.readdirSync)(dir)) {
|
|
81
|
+
if (!name.startsWith('cmd-') || !name.endsWith('.json'))
|
|
82
|
+
continue;
|
|
83
|
+
const path = (0, node_path_1.join)(dir, name);
|
|
84
|
+
try {
|
|
85
|
+
out.push({ id: name.slice(4, -5), path, mtimeMs: (0, node_fs_1.statSync)(path).mtimeMs });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* raced with GC or the consumer */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out.sort((a, b) => a.mtimeMs - b.mtimeMs || (a.id < b.id ? -1 : 1));
|
|
92
|
+
}
|
|
93
|
+
/** Delete cmd/resp files older than {@link STALE_FILE_MS} (pass `now` = Infinity to sweep all). */
|
|
94
|
+
function gcStale(dir, now = Date.now()) {
|
|
95
|
+
for (const name of (0, node_fs_1.readdirSync)(dir)) {
|
|
96
|
+
if (!/^(cmd|resp)-.*\.json$/.test(name))
|
|
97
|
+
continue;
|
|
98
|
+
const path = (0, node_path_1.join)(dir, name);
|
|
99
|
+
try {
|
|
100
|
+
if (now - (0, node_fs_1.statSync)(path).mtimeMs > exports.STALE_FILE_MS)
|
|
101
|
+
(0, node_fs_1.unlinkSync)(path);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* raced with the consumer */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/attach/daemon/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Declarative attach runs: a parsed steps object, no control flow.
|
|
3
|
+
*
|
|
4
|
+
* Allowed ops: goto | read | extract | wait. NO eval — the JS lane
|
|
5
|
+
* (`attach run script.mjs`) covers everything needing logic. The runner
|
|
6
|
+
* claims before the first step and releases in a finally block, so a failed
|
|
7
|
+
* run never strands the session until the ttl. Consumes a parsed JS object;
|
|
8
|
+
* YAML parsing happens in the wunderland CLI (`yaml` is a wunderland dep).
|
|
9
|
+
*
|
|
10
|
+
* @module browser-automation/attach/daemon/runner
|
|
11
|
+
*/
|
|
12
|
+
import type { AttachDaemonClient } from './client.js';
|
|
13
|
+
/** One declarative step. */
|
|
14
|
+
export type AttachRunStep = {
|
|
15
|
+
op: 'goto';
|
|
16
|
+
url: string;
|
|
17
|
+
settle?: number;
|
|
18
|
+
out?: string;
|
|
19
|
+
} | {
|
|
20
|
+
op: 'read';
|
|
21
|
+
selector?: string;
|
|
22
|
+
maxChars?: number;
|
|
23
|
+
out?: string;
|
|
24
|
+
} | {
|
|
25
|
+
op: 'extract';
|
|
26
|
+
fields?: Record<string, string>;
|
|
27
|
+
out?: string;
|
|
28
|
+
} | {
|
|
29
|
+
op: 'wait';
|
|
30
|
+
ms: number;
|
|
31
|
+
out?: string;
|
|
32
|
+
};
|
|
33
|
+
/** A parsed declarative plan. */
|
|
34
|
+
export interface AttachRunPlan {
|
|
35
|
+
name: string;
|
|
36
|
+
claim?: {
|
|
37
|
+
ttlMs?: number;
|
|
38
|
+
};
|
|
39
|
+
steps: AttachRunStep[];
|
|
40
|
+
}
|
|
41
|
+
/** Run result: per-step records plus keyed captures. */
|
|
42
|
+
export interface AttachRunResult {
|
|
43
|
+
name: string;
|
|
44
|
+
startedAt: number;
|
|
45
|
+
ok: boolean;
|
|
46
|
+
failedStep?: number;
|
|
47
|
+
error?: {
|
|
48
|
+
code?: string;
|
|
49
|
+
message: string;
|
|
50
|
+
};
|
|
51
|
+
steps: Array<{
|
|
52
|
+
op: string;
|
|
53
|
+
ok: boolean;
|
|
54
|
+
out?: string;
|
|
55
|
+
}>;
|
|
56
|
+
results: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Execute `plan` against a live daemon via `client`. Validates the op
|
|
60
|
+
* whitelist up front (an eval step is a hard error, not a skipped step),
|
|
61
|
+
* aborts on the first failing step, and always releases the claim.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runAttachScript(plan: AttachRunPlan, client: AttachDaemonClient): Promise<AttachRunResult>;
|
|
64
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/attach/daemon/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,4BAA4B;AAC5B,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAID;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CAyC/G"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runAttachScript = runAttachScript;
|
|
4
|
+
const ALLOWED = new Set(['goto', 'read', 'extract', 'wait']);
|
|
5
|
+
/**
|
|
6
|
+
* Execute `plan` against a live daemon via `client`. Validates the op
|
|
7
|
+
* whitelist up front (an eval step is a hard error, not a skipped step),
|
|
8
|
+
* aborts on the first failing step, and always releases the claim.
|
|
9
|
+
*/
|
|
10
|
+
async function runAttachScript(plan, client) {
|
|
11
|
+
if (!plan || !Array.isArray(plan.steps))
|
|
12
|
+
throw new Error('attach run plan needs a steps array');
|
|
13
|
+
for (const [i, s] of plan.steps.entries()) {
|
|
14
|
+
const opName = s.op ?? '(missing)';
|
|
15
|
+
if (!ALLOWED.has(opName)) {
|
|
16
|
+
throw new Error(`step ${i}: ${opName} is not a declarative op (eval is not a declarative op; use the JS lane)`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const result = {
|
|
20
|
+
name: plan.name ?? 'attach-run',
|
|
21
|
+
startedAt: Date.now(),
|
|
22
|
+
ok: true,
|
|
23
|
+
steps: [],
|
|
24
|
+
results: {},
|
|
25
|
+
};
|
|
26
|
+
await client.claim(plan.claim?.ttlMs);
|
|
27
|
+
try {
|
|
28
|
+
for (const [i, step] of plan.steps.entries()) {
|
|
29
|
+
try {
|
|
30
|
+
let data;
|
|
31
|
+
if (step.op === 'goto')
|
|
32
|
+
data = await client.goto(step.url, { settle: step.settle });
|
|
33
|
+
else if (step.op === 'read')
|
|
34
|
+
data = await client.read({ selector: step.selector, maxChars: step.maxChars });
|
|
35
|
+
else if (step.op === 'extract')
|
|
36
|
+
data = await client.extract(step.fields);
|
|
37
|
+
else
|
|
38
|
+
await new Promise((r) => setTimeout(r, step.ms));
|
|
39
|
+
result.steps.push({ op: step.op, ok: true, out: step.out });
|
|
40
|
+
if (step.out !== undefined)
|
|
41
|
+
result.results[step.out] = data;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const e = err;
|
|
45
|
+
result.ok = false;
|
|
46
|
+
result.failedStep = i;
|
|
47
|
+
result.error = { code: e.code, message: e.message ?? String(err) };
|
|
48
|
+
result.steps.push({ op: step.op, ok: false, out: step.out });
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
await client.release().catch(() => {
|
|
56
|
+
/* claim may have expired or the run never claimed successfully */
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/attach/daemon/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AttachSurface } from '../AttachController.js';
|
|
2
|
+
/** Options for {@link DaemonAttachSurface}. */
|
|
3
|
+
export interface DaemonAttachSurfaceOptions {
|
|
4
|
+
ipcDir?: string;
|
|
5
|
+
/** Spawn the daemon entry ONCE if none is alive (default false). Never retries. */
|
|
6
|
+
autostart?: boolean;
|
|
7
|
+
/** Env for an autostarted daemon (defaults to process.env). */
|
|
8
|
+
env?: Record<string, string | undefined>;
|
|
9
|
+
}
|
|
10
|
+
/** See fileoverview. */
|
|
11
|
+
export declare class DaemonAttachSurface implements AttachSurface {
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly opts;
|
|
14
|
+
private autostartAttempted;
|
|
15
|
+
constructor(opts?: DaemonAttachSurfaceOptions);
|
|
16
|
+
/** Synchronous snapshot from status.json (tools call status() sync). */
|
|
17
|
+
status(): unknown;
|
|
18
|
+
/** Acquire driving rights; optionally autostart the daemon exactly once. */
|
|
19
|
+
claim(): Promise<unknown>;
|
|
20
|
+
/** Navigate the agent tab (policy enforced daemon-side). */
|
|
21
|
+
goto(url: string): Promise<string>;
|
|
22
|
+
/** Read visible text from the agent tab. */
|
|
23
|
+
read(selector?: string, maxChars?: number): Promise<string>;
|
|
24
|
+
/** Give up driving rights; the daemon keeps its held session. */
|
|
25
|
+
detach(): Promise<void>;
|
|
26
|
+
pause(): void;
|
|
27
|
+
resume(): void;
|
|
28
|
+
setDryRun(on: boolean): void;
|
|
29
|
+
private spawnDaemonOnce;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../../src/attach/daemon/surface.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAID,wBAAwB;AACxB,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAS;gBAEvB,IAAI,GAAE,0BAA+B;IAKjD,wEAAwE;IACxE,MAAM,IAAI,OAAO;IAKjB,4EAA4E;IACtE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ/B,4DAA4D;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxC,4CAA4C;IACtC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjE,iEAAiE;IAC3D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAM7B,KAAK,IAAI,IAAI;IAMb,MAAM,IAAI,IAAI;IAMd,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;YAMd,eAAe;CAmB9B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DaemonAttachSurface = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Daemon-backed implementation of the tool-facing AttachSurface.
|
|
6
|
+
*
|
|
7
|
+
* `browser_attach_*` tools built over this class drive the persistent daemon's
|
|
8
|
+
* held session instead of opening their own transport, so a mission process
|
|
9
|
+
* never touches the network (no per-process macOS prompt). `detach()` maps to
|
|
10
|
+
* the daemon `release` op: the agent gives up driving rights; the daemon keeps
|
|
11
|
+
* its session. No eval member exists here by contract.
|
|
12
|
+
*
|
|
13
|
+
* @module browser-automation/attach/daemon/surface
|
|
14
|
+
*/
|
|
15
|
+
const node_child_process_1 = require("node:child_process");
|
|
16
|
+
const node_url_1 = require("node:url");
|
|
17
|
+
const errors_js_1 = require("../errors.js");
|
|
18
|
+
const client_js_1 = require("./client.js");
|
|
19
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
20
|
+
/** See fileoverview. */
|
|
21
|
+
class DaemonAttachSurface {
|
|
22
|
+
constructor(opts = {}) {
|
|
23
|
+
this.autostartAttempted = false;
|
|
24
|
+
this.opts = opts;
|
|
25
|
+
this.client = new client_js_1.AttachDaemonClient({ ipcDir: opts.ipcDir, client: 'agent-tools' });
|
|
26
|
+
}
|
|
27
|
+
/** Synchronous snapshot from status.json (tools call status() sync). */
|
|
28
|
+
status() {
|
|
29
|
+
const s = this.client.statusFile();
|
|
30
|
+
return s ?? { state: 'unavailable', hint: 'run `wunderland attach start`' };
|
|
31
|
+
}
|
|
32
|
+
/** Acquire driving rights; optionally autostart the daemon exactly once. */
|
|
33
|
+
async claim() {
|
|
34
|
+
if (!this.client.daemonLooksAlive() && this.opts.autostart && !this.autostartAttempted) {
|
|
35
|
+
this.autostartAttempted = true; // exactly one attempt per process, NEVER a retry loop
|
|
36
|
+
await this.spawnDaemonOnce();
|
|
37
|
+
}
|
|
38
|
+
return this.client.claim();
|
|
39
|
+
}
|
|
40
|
+
/** Navigate the agent tab (policy enforced daemon-side). */
|
|
41
|
+
async goto(url) {
|
|
42
|
+
return (await this.client.goto(url)).url;
|
|
43
|
+
}
|
|
44
|
+
/** Read visible text from the agent tab. */
|
|
45
|
+
async read(selector, maxChars) {
|
|
46
|
+
return (await this.client.read({ selector, maxChars })).text;
|
|
47
|
+
}
|
|
48
|
+
/** Give up driving rights; the daemon keeps its held session. */
|
|
49
|
+
async detach() {
|
|
50
|
+
await this.client.release();
|
|
51
|
+
}
|
|
52
|
+
// Control ops are fire-and-forget over the queue; tools return the file
|
|
53
|
+
// snapshot, which reflects the change within one daemon tick.
|
|
54
|
+
pause() {
|
|
55
|
+
void this.client.control('pause').catch(() => {
|
|
56
|
+
/* surfaced on the next driving op */
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
resume() {
|
|
60
|
+
void this.client.control('resume').catch(() => {
|
|
61
|
+
/* surfaced on the next driving op */
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
setDryRun(on) {
|
|
65
|
+
void this.client.control(on ? 'dry_run_on' : 'dry_run_off').catch(() => {
|
|
66
|
+
/* surfaced on the next driving op */
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async spawnDaemonOnce() {
|
|
70
|
+
const entry = (0, node_url_1.fileURLToPath)(new URL('./daemon-main.js', import.meta.url));
|
|
71
|
+
(0, node_child_process_1.spawn)(process.execPath, [entry], {
|
|
72
|
+
detached: true,
|
|
73
|
+
stdio: 'ignore',
|
|
74
|
+
env: { ...(this.opts.env ?? process.env) },
|
|
75
|
+
}).unref();
|
|
76
|
+
for (let i = 0; i < 50; i++) {
|
|
77
|
+
// wait up to ~20s for connected
|
|
78
|
+
await sleep(400);
|
|
79
|
+
const s = this.client.statusFile();
|
|
80
|
+
if (s?.state === 'connected')
|
|
81
|
+
return;
|
|
82
|
+
if (s?.state === 'failed')
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
throw new errors_js_1.AttachError('DAEMON_UNAVAILABLE', `autostart did not reach connected: ${this.client.statusFile()?.lastError ?? 'no status'}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.DaemonAttachSurface = DaemonAttachSurface;
|
|
89
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","sourceRoot":"","sources":["../../../src/attach/daemon/surface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Parser for Chrome's `DevToolsActivePort` discovery file.
|
|
3
|
+
*
|
|
4
|
+
* When Chrome runs with remote debugging enabled it writes a two-line file at
|
|
5
|
+
* the root of its user-data directory:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* 9222
|
|
9
|
+
* /devtools/browser/07797f92-4fc3-432a-91dd-0e982e1a3bc0
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Line 1 is the listening port, line 2 the browser-target WebSocket path. The
|
|
13
|
+
* GUID rotates every browser boot, so callers MUST re-read this file per
|
|
14
|
+
* connection attempt — a cached ws URL goes stale the moment Chrome restarts.
|
|
15
|
+
*
|
|
16
|
+
* @module browser-automation/attach/devtools-port
|
|
17
|
+
*/
|
|
18
|
+
/** Parsed contents of a `DevToolsActivePort` file. */
|
|
19
|
+
export interface DevToolsEndpoint {
|
|
20
|
+
/** TCP port the DevTools server listens on (loopback only). */
|
|
21
|
+
port: number;
|
|
22
|
+
/** Browser-target WebSocket path, e.g. `/devtools/browser/<guid>`. */
|
|
23
|
+
wsPath: string;
|
|
24
|
+
/** Full loopback WebSocket URL (`ws://localhost:…` — the host Chrome's DNS-rebinding guard accepts). */
|
|
25
|
+
wsUrl: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse the raw text of a `DevToolsActivePort` file.
|
|
29
|
+
*
|
|
30
|
+
* @param text Raw file contents (two newline-separated lines).
|
|
31
|
+
* @returns The parsed endpoint.
|
|
32
|
+
* @throws {Error} When the file is malformed: fewer than two non-empty lines,
|
|
33
|
+
* a non-numeric/out-of-range port, or a ws path that is not a
|
|
34
|
+
* `/devtools/browser/…` browser-target path.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseDevToolsActivePort(text: string): DevToolsEndpoint;
|
|
37
|
+
//# sourceMappingURL=devtools-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-port.d.ts","sourceRoot":"","sources":["../../src/attach/devtools-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAmBtE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Parser for Chrome's `DevToolsActivePort` discovery file.
|
|
4
|
+
*
|
|
5
|
+
* When Chrome runs with remote debugging enabled it writes a two-line file at
|
|
6
|
+
* the root of its user-data directory:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* 9222
|
|
10
|
+
* /devtools/browser/07797f92-4fc3-432a-91dd-0e982e1a3bc0
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Line 1 is the listening port, line 2 the browser-target WebSocket path. The
|
|
14
|
+
* GUID rotates every browser boot, so callers MUST re-read this file per
|
|
15
|
+
* connection attempt — a cached ws URL goes stale the moment Chrome restarts.
|
|
16
|
+
*
|
|
17
|
+
* @module browser-automation/attach/devtools-port
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.parseDevToolsActivePort = parseDevToolsActivePort;
|
|
21
|
+
/**
|
|
22
|
+
* Parse the raw text of a `DevToolsActivePort` file.
|
|
23
|
+
*
|
|
24
|
+
* @param text Raw file contents (two newline-separated lines).
|
|
25
|
+
* @returns The parsed endpoint.
|
|
26
|
+
* @throws {Error} When the file is malformed: fewer than two non-empty lines,
|
|
27
|
+
* a non-numeric/out-of-range port, or a ws path that is not a
|
|
28
|
+
* `/devtools/browser/…` browser-target path.
|
|
29
|
+
*/
|
|
30
|
+
function parseDevToolsActivePort(text) {
|
|
31
|
+
const lines = String(text)
|
|
32
|
+
.split('\n')
|
|
33
|
+
.map((l) => l.trim())
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
if (lines.length < 2) {
|
|
36
|
+
throw new Error('DevToolsActivePort malformed: expected "<port>\\n<wsPath>"');
|
|
37
|
+
}
|
|
38
|
+
const port = Number(lines[0]);
|
|
39
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
40
|
+
throw new Error(`DevToolsActivePort malformed: invalid port "${lines[0]}"`);
|
|
41
|
+
}
|
|
42
|
+
const wsPath = lines[1];
|
|
43
|
+
if (!wsPath.startsWith('/devtools/browser/')) {
|
|
44
|
+
throw new Error(`DevToolsActivePort malformed: unexpected ws path "${wsPath}"`);
|
|
45
|
+
}
|
|
46
|
+
// Host MUST be `localhost`: Chrome's DNS-rebinding guard 403s the literal
|
|
47
|
+
// `127.0.0.1` form on the ws upgrade (proven live 2026-07-21).
|
|
48
|
+
return { port, wsPath, wsUrl: `ws://localhost:${port}${wsPath}` };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=devtools-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-port.js","sourceRoot":"","sources":["../../src/attach/devtools-port.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AAqBH,0DAmBC;AA5BD;;;;;;;;GAQG;AACH,SAAgB,uBAAuB,CAAC,IAAY;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC;SACvB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,+CAA+C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,qDAAqD,MAAM,GAAG,CAAC,CAAC;IAClF,CAAC;IACD,0EAA0E;IAC1E,+DAA+D;IAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,IAAI,GAAG,MAAM,EAAE,EAAE,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Structured errors + diagnostic redaction for the attach lane.
|
|
3
|
+
*
|
|
4
|
+
* Attach operations run inside the user's AUTHENTICATED browser, so raw error
|
|
5
|
+
* text can carry session URLs, tokens, or account identifiers. Everything that
|
|
6
|
+
* leaves the attach layer is (a) mapped to a stable machine-readable code and
|
|
7
|
+
* (b) redacted of credential-class substrings before it can reach a report or
|
|
8
|
+
* a model prompt (Codex spec review F13/F14).
|
|
9
|
+
*
|
|
10
|
+
* @module browser-automation/attach/errors
|
|
11
|
+
*/
|
|
12
|
+
/** Stable failure codes for every attach-lane operation. */
|
|
13
|
+
export type AttachErrorCode = 'CDP_TIMEOUT' | 'CDP_UNAVAILABLE' | 'JS_DISABLED' | 'ACCESSIBILITY_DENIED' | 'PROFILE_MISMATCH' | 'TAB_CLOSED' | 'NAV_TIMEOUT' | 'POLICY_BLOCKED' | 'LEASE_DENIED' | 'STALE_PORT_FILE' | 'DAEMON_UNAVAILABLE' | 'DAEMON_RUNNING' | 'NOT_CLAIMANT' | 'UNSUPPORTED_OP' | 'UNKNOWN';
|
|
14
|
+
/** Structured attach error surfaced to tools/callers. */
|
|
15
|
+
export interface StructuredAttachError {
|
|
16
|
+
code: AttachErrorCode;
|
|
17
|
+
/** Redacted, human-readable diagnostic. Never verbatim page/session text. */
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
/** Error subclass carrying a structured code through throw sites. */
|
|
21
|
+
export declare class AttachError extends Error {
|
|
22
|
+
readonly code: AttachErrorCode;
|
|
23
|
+
constructor(code: AttachErrorCode, message: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Redact credential-class substrings from a diagnostic string.
|
|
27
|
+
*
|
|
28
|
+
* @param text Raw diagnostic text.
|
|
29
|
+
* @returns The text with tokens/keys/secrets masked.
|
|
30
|
+
*/
|
|
31
|
+
export declare function redactDiagnostic(text: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Map any thrown value to a {@link StructuredAttachError} with a redacted
|
|
34
|
+
* message. An {@link AttachError} keeps its explicit code; everything else is
|
|
35
|
+
* classified by message pattern, defaulting to `UNKNOWN`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function toStructuredError(err: unknown): StructuredAttachError;
|
|
38
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/attach/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,sBAAsB,GACtB,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,iBAAiB,GACjB,oBAAoB,GACpB,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,SAAS,CAAC;AAEd,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,eAAe,CAAC;IACtB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qEAAqE;AACrE,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBACnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM;CAKnD;AASD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,qBAAqB,CASrE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Structured errors + diagnostic redaction for the attach lane.
|
|
4
|
+
*
|
|
5
|
+
* Attach operations run inside the user's AUTHENTICATED browser, so raw error
|
|
6
|
+
* text can carry session URLs, tokens, or account identifiers. Everything that
|
|
7
|
+
* leaves the attach layer is (a) mapped to a stable machine-readable code and
|
|
8
|
+
* (b) redacted of credential-class substrings before it can reach a report or
|
|
9
|
+
* a model prompt (Codex spec review F13/F14).
|
|
10
|
+
*
|
|
11
|
+
* @module browser-automation/attach/errors
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.AttachError = void 0;
|
|
15
|
+
exports.redactDiagnostic = redactDiagnostic;
|
|
16
|
+
exports.toStructuredError = toStructuredError;
|
|
17
|
+
/** Error subclass carrying a structured code through throw sites. */
|
|
18
|
+
class AttachError extends Error {
|
|
19
|
+
constructor(code, message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'AttachError';
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.AttachError = AttachError;
|
|
26
|
+
const CREDENTIAL_PATTERNS = [
|
|
27
|
+
[/(token|secret|password|passwd|apikey|api_key|auth|bearer|session|cookie)(["']?\s*[:=]\s*["']?)[^\s"'&;]+/gi, '$1$2[redacted]'],
|
|
28
|
+
[/eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}/g, '[redacted-jwt]'],
|
|
29
|
+
[/\b(sk|pk|rk|ak)-[A-Za-z0-9_-]{12,}\b/g, '[redacted-key]'],
|
|
30
|
+
[/[?&](code|state|id_token|access_token|refresh_token)=[^\s&"']+/gi, '?[redacted-param]'],
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Redact credential-class substrings from a diagnostic string.
|
|
34
|
+
*
|
|
35
|
+
* @param text Raw diagnostic text.
|
|
36
|
+
* @returns The text with tokens/keys/secrets masked.
|
|
37
|
+
*/
|
|
38
|
+
function redactDiagnostic(text) {
|
|
39
|
+
let out = String(text ?? '');
|
|
40
|
+
for (const [re, rep] of CREDENTIAL_PATTERNS)
|
|
41
|
+
out = out.replace(re, rep);
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
const CODE_MATCHERS = [
|
|
45
|
+
[/timeout.*connectovercdp|connectovercdp.*timeout|cdp.*timeout/i, 'CDP_TIMEOUT'],
|
|
46
|
+
[/econnrefused|ws error|socket hang up|cdp.*unavailable/i, 'CDP_UNAVAILABLE'],
|
|
47
|
+
[/javascript through applescript is turned off/i, 'JS_DISABLED'],
|
|
48
|
+
[/not authorized to send apple events|accessibility/i, 'ACCESSIBILITY_DENIED'],
|
|
49
|
+
[/profile.*mismatch|wrong profile/i, 'PROFILE_MISMATCH'],
|
|
50
|
+
[/tab .* not found|window .* not found|target closed/i, 'TAB_CLOSED'],
|
|
51
|
+
[/navigation.*timeout|nav.*timeout|net::err_timed_out/i, 'NAV_TIMEOUT'],
|
|
52
|
+
[/policy|blocked|not allowed/i, 'POLICY_BLOCKED'],
|
|
53
|
+
[/lease/i, 'LEASE_DENIED'],
|
|
54
|
+
[/devtoolsactiveport.*malformed|stale.*port/i, 'STALE_PORT_FILE'],
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Map any thrown value to a {@link StructuredAttachError} with a redacted
|
|
58
|
+
* message. An {@link AttachError} keeps its explicit code; everything else is
|
|
59
|
+
* classified by message pattern, defaulting to `UNKNOWN`.
|
|
60
|
+
*/
|
|
61
|
+
function toStructuredError(err) {
|
|
62
|
+
if (err instanceof AttachError) {
|
|
63
|
+
return { code: err.code, message: redactDiagnostic(err.message) };
|
|
64
|
+
}
|
|
65
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
66
|
+
for (const [re, code] of CODE_MATCHERS) {
|
|
67
|
+
if (re.test(msg))
|
|
68
|
+
return { code, message: redactDiagnostic(msg) };
|
|
69
|
+
}
|
|
70
|
+
return { code: 'UNKNOWN', message: redactDiagnostic(msg) };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/attach/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAkDH,4CAIC;AAoBD,8CASC;AAxDD,qEAAqE;AACrE,MAAa,WAAY,SAAQ,KAAK;IAEpC,YAAY,IAAqB,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAPD,kCAOC;AAED,MAAM,mBAAmB,GAA4B;IACnD,CAAC,4GAA4G,EAAE,gBAAgB,CAAC;IAChI,CAAC,6DAA6D,EAAE,gBAAgB,CAAC;IACjF,CAAC,uCAAuC,EAAE,gBAAgB,CAAC;IAC3D,CAAC,kEAAkE,EAAE,mBAAmB,CAAC;CAC1F,CAAC;AAEF;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,IAAI,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7B,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,mBAAmB;QAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACxE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,aAAa,GAAqC;IACtD,CAAC,+DAA+D,EAAE,aAAa,CAAC;IAChF,CAAC,wDAAwD,EAAE,iBAAiB,CAAC;IAC7E,CAAC,+CAA+C,EAAE,aAAa,CAAC;IAChE,CAAC,oDAAoD,EAAE,sBAAsB,CAAC;IAC9E,CAAC,kCAAkC,EAAE,kBAAkB,CAAC;IACxD,CAAC,qDAAqD,EAAE,YAAY,CAAC;IACrE,CAAC,sDAAsD,EAAE,aAAa,CAAC;IACvE,CAAC,6BAA6B,EAAE,gBAAgB,CAAC;IACjD,CAAC,QAAQ,EAAE,cAAc,CAAC;IAC1B,CAAC,4CAA4C,EAAE,iBAAiB,CAAC;CAClE,CAAC;AAEF;;;;GAIG;AACH,SAAgB,iBAAiB,CAAC,GAAY;IAC5C,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;IACpE,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,aAAa,EAAE,CAAC;QACvC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** On-disk lease record. */
|
|
2
|
+
export interface AttachLease {
|
|
3
|
+
/** PID of the holding process. */
|
|
4
|
+
pid: number;
|
|
5
|
+
/** Random per-lease nonce every operation must present. */
|
|
6
|
+
nonce: string;
|
|
7
|
+
/** Epoch ms when the lease was acquired. */
|
|
8
|
+
acquiredAt: number;
|
|
9
|
+
/** Epoch ms after which the lease is reclaimable regardless of liveness. */
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
}
|
|
12
|
+
/** Options for {@link acquireLease}. */
|
|
13
|
+
export interface LeaseOptions {
|
|
14
|
+
/** Lease file path. */
|
|
15
|
+
file: string;
|
|
16
|
+
/** TTL in ms (default 10 minutes). */
|
|
17
|
+
ttlMs?: number;
|
|
18
|
+
/** Injectable clock for tests. */
|
|
19
|
+
now?: () => number;
|
|
20
|
+
/** Injectable liveness probe for tests (default: `process.kill(pid, 0)`). */
|
|
21
|
+
isPidAlive?: (pid: number) => boolean;
|
|
22
|
+
/** Acquiring process id (default `process.pid`). */
|
|
23
|
+
pid?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Acquire the exclusive attach lease.
|
|
27
|
+
*
|
|
28
|
+
* @returns The acquired lease (persist the nonce; ops require it).
|
|
29
|
+
* @throws {AttachError} `LEASE_DENIED` when a live, unexpired lease exists.
|
|
30
|
+
*/
|
|
31
|
+
export declare function acquireLease(opts: LeaseOptions): AttachLease;
|
|
32
|
+
/**
|
|
33
|
+
* Assert `nonce` matches the current lease.
|
|
34
|
+
* @throws {AttachError} `LEASE_DENIED` on missing lease or nonce mismatch.
|
|
35
|
+
*/
|
|
36
|
+
export declare function requireLease(file: string, nonce: string): AttachLease;
|
|
37
|
+
/**
|
|
38
|
+
* Release the lease. Only the holder (matching nonce) may release; releasing a
|
|
39
|
+
* missing lease is a no-op.
|
|
40
|
+
* @throws {AttachError} `LEASE_DENIED` when the nonce does not match.
|
|
41
|
+
*/
|
|
42
|
+
export declare function releaseLease(file: string, nonce: string): void;
|
|
43
|
+
//# sourceMappingURL=lease.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../src/attach/lease.ts"],"names":[],"mappings":"AAkBA,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAqBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAsB5D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAMrE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO9D"}
|