humanish 0.97.0 → 0.98.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/AGENTS.md +86 -79
- package/CONTRIBUTING.md +7 -2
- package/README.md +7 -1
- package/dist/actor-contract.d.ts +35 -1
- package/dist/actor-contract.js +38 -0
- package/dist/actor-contract.js.map +1 -1
- package/dist/adapter-extension.js +1 -0
- package/dist/adapter-extension.js.map +1 -1
- package/dist/browser-control-client.d.ts +14 -0
- package/dist/browser-control-client.js +134 -0
- package/dist/browser-control-client.js.map +1 -0
- package/dist/browser-control-dispatcher.d.ts +14 -0
- package/dist/browser-control-dispatcher.js +109 -0
- package/dist/browser-control-dispatcher.js.map +1 -0
- package/dist/browser-control-protocol.d.ts +371 -0
- package/dist/browser-control-protocol.js +155 -0
- package/dist/browser-control-protocol.js.map +1 -0
- package/dist/browser-control-transport.d.ts +24 -0
- package/dist/browser-control-transport.js +156 -0
- package/dist/browser-control-transport.js.map +1 -0
- package/dist/comms-lease-store.d.ts +1 -0
- package/dist/comms-lease-store.js +9 -3
- package/dist/comms-lease-store.js.map +1 -1
- package/dist/computer-use-actor.d.ts +2 -2
- package/dist/computer-use-actor.js +6 -1
- package/dist/computer-use-actor.js.map +1 -1
- package/dist/computer-use.d.ts +23 -1
- package/dist/computer-use.js +253 -70
- package/dist/computer-use.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +6 -1
- package/dist/cua-actor-lab.js +55 -15
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/cua-executor-error.d.ts +31 -0
- package/dist/cua-executor-error.js +48 -0
- package/dist/cua-executor-error.js.map +1 -0
- package/dist/cua-provider-error.d.ts +12 -0
- package/dist/cua-provider-error.js +28 -0
- package/dist/cua-provider-error.js.map +1 -0
- package/dist/desktop-session.js +3 -1
- package/dist/desktop-session.js.map +1 -1
- package/dist/doctor-lab.d.ts +2 -0
- package/dist/doctor-lab.js +25 -7
- package/dist/doctor-lab.js.map +1 -1
- package/dist/e2b-desktop-executor.d.ts +1 -24
- package/dist/e2b-desktop-executor.js +2 -127
- package/dist/e2b-desktop-executor.js.map +1 -1
- package/dist/e2b-terminal-lab.js +1 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/frame-signature.d.ts +24 -0
- package/dist/frame-signature.js +128 -0
- package/dist/frame-signature.js.map +1 -0
- package/dist/guest-bootstrap.d.ts +43 -0
- package/dist/guest-bootstrap.js +240 -0
- package/dist/guest-bootstrap.js.map +1 -0
- package/dist/guest-browser-tools.d.ts +8 -0
- package/dist/guest-browser-tools.js +66 -0
- package/dist/guest-browser-tools.js.map +1 -0
- package/dist/guest-chromium-text.d.ts +27 -0
- package/dist/guest-chromium-text.js +281 -0
- package/dist/guest-chromium-text.js.map +1 -0
- package/dist/guest-desktop-executor.d.ts +22 -0
- package/dist/guest-desktop-executor.js +177 -0
- package/dist/guest-desktop-executor.js.map +1 -0
- package/dist/guest-desktop-native.d.ts +14 -0
- package/dist/guest-desktop-native.js +131 -0
- package/dist/guest-desktop-native.js.map +1 -0
- package/dist/guest-runtime-desktop.d.ts +35 -0
- package/dist/guest-runtime-desktop.js +231 -0
- package/dist/guest-runtime-desktop.js.map +1 -0
- package/dist/guest-runtime-main.d.ts +1 -0
- package/dist/guest-runtime-main.js +31 -0
- package/dist/guest-runtime-main.js.map +1 -0
- package/dist/guest-runtime-revision.d.ts +1 -0
- package/dist/guest-runtime-revision.js +3 -0
- package/dist/guest-runtime-revision.js.map +1 -0
- package/dist/guest-runtime.d.ts +25 -0
- package/dist/guest-runtime.js +96 -0
- package/dist/guest-runtime.js.map +1 -0
- package/dist/lab-config.js +10 -3
- package/dist/lab-config.js.map +1 -1
- package/dist/lab-engine.js +6 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/lab-summary.d.ts +2 -0
- package/dist/lab-summary.js +5 -0
- package/dist/lab-summary.js.map +1 -1
- package/dist/local-agent-cli.js +1 -1
- package/dist/local-agent-cli.js.map +1 -1
- package/dist/local-firecracker-desktop.d.ts +13 -0
- package/dist/local-firecracker-desktop.js +150 -0
- package/dist/local-firecracker-desktop.js.map +1 -0
- package/dist/local-firecracker-study.d.ts +9 -0
- package/dist/local-firecracker-study.js +93 -0
- package/dist/local-firecracker-study.js.map +1 -0
- package/dist/local-runtime-config.d.ts +6 -0
- package/dist/local-runtime-config.js +56 -0
- package/dist/local-runtime-config.js.map +1 -0
- package/dist/local-runtime-release.d.ts +3 -0
- package/dist/local-runtime-release.js +8 -0
- package/dist/local-runtime-release.js.map +1 -0
- package/dist/local-runtime.d.ts +25 -0
- package/dist/local-runtime.js +113 -0
- package/dist/local-runtime.js.map +1 -0
- package/dist/observer-app.html +4 -4
- package/dist/pricing.d.ts +22 -1
- package/dist/pricing.js +22 -0
- package/dist/pricing.js.map +1 -1
- package/dist/program.js +26 -0
- package/dist/program.js.map +1 -1
- package/dist/restricted-codex-participant-policy.d.ts +39 -0
- package/dist/restricted-codex-participant-policy.js +69 -0
- package/dist/restricted-codex-participant-policy.js.map +1 -0
- package/dist/restricted-codex-participant-run.d.ts +20 -0
- package/dist/restricted-codex-participant-run.js +78 -0
- package/dist/restricted-codex-participant-run.js.map +1 -0
- package/dist/restricted-codex-participant.d.ts +14 -0
- package/dist/restricted-codex-participant.js +178 -0
- package/dist/restricted-codex-participant.js.map +1 -0
- package/dist/restricted-codex-session.d.ts +2 -2
- package/dist/restricted-codex-session.js +11 -17
- package/dist/restricted-codex-session.js.map +1 -1
- package/dist/run-detail.js +4 -2
- package/dist/run-detail.js.map +1 -1
- package/dist/run.d.ts +12 -5
- package/dist/run.js +17 -1
- package/dist/run.js.map +1 -1
- package/dist/shared-world-lab.js +2 -2
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/study-analysis-job.d.ts +2 -2
- package/dist/study-analysis-validation.d.ts +14 -14
- package/dist/study-costs.js +6 -0
- package/dist/study-costs.js.map +1 -1
- package/dist/tui-app.js +37 -37
- package/docs/architecture/browser-control.md +117 -0
- package/docs/architecture/guest-desktop.md +87 -0
- package/docs/architecture/local-browser-runtime.md +100 -0
- package/docs/architecture/restricted-codex-analysis.md +4 -2
- package/docs/architecture/runtime-broker-core.md +30 -0
- package/docs/contracts/schemas.md +1 -1
- package/docs/goals/current.md +24 -8
- package/docs/product/open-source-install-experience.md +7 -0
- package/docs/ramp/README.md +24 -13
- package/docs/release/0.98.0-local-browser-studies.md +31 -0
- package/package.json +4 -2
- package/skills/humanish/SKILL.md +23 -3
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { validateBrowserControlIdentity, sameBrowserControlIdentity } from "./browser-control-protocol.js";
|
|
2
|
+
import { CuaExecutorError } from "./cua-executor-error.js";
|
|
3
|
+
import { createBrowserControlClient } from "./browser-control-client.js";
|
|
4
|
+
export const GUEST_BOOTSTRAP_VERSION = 1;
|
|
5
|
+
export const GUEST_BOOTSTRAP_LIMITS = Object.freeze({ bytes: 1024, admissionMs: 5000, readyMs: 35_000, connectMs: 3000, port: 5251 });
|
|
6
|
+
const refused = () => new CuaExecutorError("protocol_mismatch", "not_dispatched");
|
|
7
|
+
function canonical(identity, ready) {
|
|
8
|
+
const { generation, challenge, runtimeRevision } = validateBrowserControlIdentity(identity);
|
|
9
|
+
const fixed = { generation, challenge, runtimeRevision };
|
|
10
|
+
return JSON.stringify(ready ? { version: 1, ready: true, identity: fixed } : { version: 1, identity: fixed });
|
|
11
|
+
}
|
|
12
|
+
export function encodeGuestBootstrap(identity, ready = false) {
|
|
13
|
+
const bytes = Buffer.from(canonical(identity, ready));
|
|
14
|
+
if (bytes.length > GUEST_BOOTSTRAP_LIMITS.bytes)
|
|
15
|
+
throw refused();
|
|
16
|
+
const frame = Buffer.alloc(4 + bytes.length);
|
|
17
|
+
frame.writeUInt32BE(bytes.length);
|
|
18
|
+
bytes.copy(frame, 4);
|
|
19
|
+
return frame;
|
|
20
|
+
}
|
|
21
|
+
export function parseGuestBootstrap(bytes, revision, ready = false) {
|
|
22
|
+
if (!bytes.length || bytes.length > GUEST_BOOTSTRAP_LIMITS.bytes || bytes.some(byte => byte > 127))
|
|
23
|
+
throw refused();
|
|
24
|
+
try {
|
|
25
|
+
const value = JSON.parse(bytes.toString("ascii"));
|
|
26
|
+
if (typeof value !== "object" || value === null || !("identity" in value))
|
|
27
|
+
throw refused();
|
|
28
|
+
const identity = validateBrowserControlIdentity(value.identity);
|
|
29
|
+
if (identity.runtimeRevision !== revision || canonical(identity, ready) !== bytes.toString("ascii"))
|
|
30
|
+
throw refused();
|
|
31
|
+
return identity;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw refused();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Own the first frame and continue refusing early bytes until synchronous handoff. */
|
|
38
|
+
export class GuestBootstrapReader {
|
|
39
|
+
stream;
|
|
40
|
+
revision;
|
|
41
|
+
signal;
|
|
42
|
+
ready;
|
|
43
|
+
onFailure;
|
|
44
|
+
identity;
|
|
45
|
+
resolve;
|
|
46
|
+
reject;
|
|
47
|
+
header = Buffer.alloc(4);
|
|
48
|
+
headerUsed = 0;
|
|
49
|
+
body;
|
|
50
|
+
used = 0;
|
|
51
|
+
admitted = false;
|
|
52
|
+
terminal = false;
|
|
53
|
+
timer;
|
|
54
|
+
constructor(stream, revision, signal, ready = false, onFailure = () => { }) {
|
|
55
|
+
this.stream = stream;
|
|
56
|
+
this.revision = revision;
|
|
57
|
+
this.signal = signal;
|
|
58
|
+
this.ready = ready;
|
|
59
|
+
this.onFailure = onFailure;
|
|
60
|
+
this.identity = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; });
|
|
61
|
+
// A later factory rejection must not leave the admission rejection unconsumed.
|
|
62
|
+
void this.identity.catch(() => { });
|
|
63
|
+
this.timer = setTimeout(() => this.fail("deadline_exceeded"), ready ? GUEST_BOOTSTRAP_LIMITS.readyMs : GUEST_BOOTSTRAP_LIMITS.admissionMs);
|
|
64
|
+
stream.on("error", this.error);
|
|
65
|
+
stream.on("close", this.end);
|
|
66
|
+
stream.on("end", this.end);
|
|
67
|
+
stream.on("data", this.data);
|
|
68
|
+
signal.addEventListener("abort", this.abort, { once: true });
|
|
69
|
+
if (signal.aborted || stream.destroyed || stream.readableEnded || stream.writableEnded || stream.readableObjectMode || stream.writableObjectMode)
|
|
70
|
+
this.abort();
|
|
71
|
+
else
|
|
72
|
+
stream.resume();
|
|
73
|
+
}
|
|
74
|
+
data = (value) => {
|
|
75
|
+
if (this.terminal)
|
|
76
|
+
return;
|
|
77
|
+
if (!Buffer.isBuffer(value) || this.admitted) {
|
|
78
|
+
this.fail();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let offset = 0;
|
|
82
|
+
if (!this.body) {
|
|
83
|
+
const count = Math.min(4 - this.headerUsed, value.length);
|
|
84
|
+
value.copy(this.header, this.headerUsed, 0, count);
|
|
85
|
+
this.headerUsed += count;
|
|
86
|
+
offset += count;
|
|
87
|
+
if (this.headerUsed < 4)
|
|
88
|
+
return;
|
|
89
|
+
const length = this.header.readUInt32BE(0);
|
|
90
|
+
if (!length || length > GUEST_BOOTSTRAP_LIMITS.bytes) {
|
|
91
|
+
this.fail();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.body = Buffer.alloc(length);
|
|
95
|
+
}
|
|
96
|
+
const count = Math.min(value.length - offset, this.body.length - this.used);
|
|
97
|
+
value.copy(this.body, this.used, offset, offset + count);
|
|
98
|
+
this.used += count;
|
|
99
|
+
offset += count;
|
|
100
|
+
if (offset !== value.length) {
|
|
101
|
+
this.fail();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (this.used === this.body.length) {
|
|
105
|
+
try {
|
|
106
|
+
const identity = parseGuestBootstrap(this.body, this.revision, this.ready);
|
|
107
|
+
this.admitted = true;
|
|
108
|
+
clearTimeout(this.timer);
|
|
109
|
+
this.resolve(identity);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
this.fail();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
error = () => this.fail("transport_failed");
|
|
117
|
+
end = () => this.fail("transport_failed");
|
|
118
|
+
abort = () => this.fail("session_revoked");
|
|
119
|
+
release() {
|
|
120
|
+
clearTimeout(this.timer);
|
|
121
|
+
this.stream.off("data", this.data);
|
|
122
|
+
this.stream.off("end", this.end);
|
|
123
|
+
this.stream.off("close", this.end);
|
|
124
|
+
this.signal.removeEventListener("abort", this.abort);
|
|
125
|
+
// Retain a safe listener until queued native error/close events have drained.
|
|
126
|
+
const stream = this.stream, error = this.error;
|
|
127
|
+
const off = () => { stream.off("error", error); stream.off("close", off); };
|
|
128
|
+
stream.once("close", off);
|
|
129
|
+
if (stream.closed)
|
|
130
|
+
setImmediate(off);
|
|
131
|
+
}
|
|
132
|
+
fail(code = "protocol_mismatch") {
|
|
133
|
+
if (this.terminal)
|
|
134
|
+
return;
|
|
135
|
+
this.terminal = true;
|
|
136
|
+
this.release();
|
|
137
|
+
this.reject(new CuaExecutorError(code, "not_dispatched"));
|
|
138
|
+
this.stream.destroy();
|
|
139
|
+
this.onFailure();
|
|
140
|
+
}
|
|
141
|
+
/** Must be followed synchronously by installing the sole next stream owner. */
|
|
142
|
+
handoff() {
|
|
143
|
+
if (this.terminal || !this.admitted || this.signal.aborted || this.stream.destroyed || this.stream.readableLength !== 0)
|
|
144
|
+
throw refused();
|
|
145
|
+
this.stream.pause();
|
|
146
|
+
this.terminal = true;
|
|
147
|
+
this.release();
|
|
148
|
+
}
|
|
149
|
+
close() { this.fail("session_revoked"); }
|
|
150
|
+
}
|
|
151
|
+
/** Fixed preface on an already acquired Firecracker stream; no path discovery/retry. */
|
|
152
|
+
export async function connectGuestBootstrap(stream, identity, signal) {
|
|
153
|
+
validateBrowserControlIdentity(identity);
|
|
154
|
+
await new Promise((resolve, reject) => {
|
|
155
|
+
let bytes = Buffer.alloc(0), done = false;
|
|
156
|
+
const timer = setTimeout(() => finish(new CuaExecutorError("deadline_exceeded", "not_dispatched")), GUEST_BOOTSTRAP_LIMITS.connectMs);
|
|
157
|
+
const finish = (error) => {
|
|
158
|
+
if (done)
|
|
159
|
+
return;
|
|
160
|
+
done = true;
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
stream.pause();
|
|
163
|
+
stream.off("data", data);
|
|
164
|
+
stream.off("end", end);
|
|
165
|
+
stream.off("close", end);
|
|
166
|
+
signal.removeEventListener("abort", abort);
|
|
167
|
+
const release = () => { stream.off("error", end); stream.off("close", release); };
|
|
168
|
+
stream.once("close", release);
|
|
169
|
+
if (stream.closed)
|
|
170
|
+
setImmediate(release);
|
|
171
|
+
if (error) {
|
|
172
|
+
stream.destroy();
|
|
173
|
+
reject(error);
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
resolve();
|
|
177
|
+
};
|
|
178
|
+
const end = () => {
|
|
179
|
+
if (done)
|
|
180
|
+
stream.destroy();
|
|
181
|
+
finish(new CuaExecutorError("transport_failed", "not_dispatched"));
|
|
182
|
+
};
|
|
183
|
+
const abort = () => finish(new CuaExecutorError("session_revoked", "not_dispatched"));
|
|
184
|
+
const data = (chunk) => {
|
|
185
|
+
if (!Buffer.isBuffer(chunk) || bytes.length + chunk.length > 64 + 1028) {
|
|
186
|
+
finish(refused());
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
bytes = Buffer.concat([bytes, chunk]);
|
|
190
|
+
const newline = bytes.indexOf(10);
|
|
191
|
+
if (newline < 0) {
|
|
192
|
+
if (bytes.length > 64)
|
|
193
|
+
finish(refused());
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const line = bytes.subarray(0, newline + 1).toString("ascii");
|
|
197
|
+
if (newline >= 64 || bytes.subarray(0, newline + 1).some(byte => byte > 127)
|
|
198
|
+
|| !/^OK [1-9][0-9]{0,9}\n$/.test(line) || Number(line.slice(3)) > 0xffffffff) {
|
|
199
|
+
finish(refused());
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const tail = bytes.subarray(newline + 1);
|
|
203
|
+
if (tail.length) {
|
|
204
|
+
stream.pause();
|
|
205
|
+
stream.unshift(tail);
|
|
206
|
+
}
|
|
207
|
+
finish();
|
|
208
|
+
};
|
|
209
|
+
stream.on("error", end);
|
|
210
|
+
stream.on("end", end);
|
|
211
|
+
stream.on("close", end);
|
|
212
|
+
stream.on("data", data);
|
|
213
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
214
|
+
if (signal.aborted || stream.destroyed)
|
|
215
|
+
abort();
|
|
216
|
+
else {
|
|
217
|
+
stream.resume();
|
|
218
|
+
stream.write("CONNECT 5251\n", error => { if (error)
|
|
219
|
+
end(); });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
const reader = new GuestBootstrapReader(stream, identity.runtimeRevision, signal, true);
|
|
223
|
+
try {
|
|
224
|
+
stream.write(encodeGuestBootstrap(identity), error => { if (error)
|
|
225
|
+
reader.close(); });
|
|
226
|
+
const actual = await reader.identity;
|
|
227
|
+
if (!sameBrowserControlIdentity(actual, identity))
|
|
228
|
+
throw refused();
|
|
229
|
+
reader.handoff();
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
reader.close();
|
|
233
|
+
stream.destroy();
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
const client = createBrowserControlClient({ transport: stream, identity });
|
|
237
|
+
stream.resume();
|
|
238
|
+
return client;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=guest-bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-bootstrap.js","sourceRoot":"","sources":["../src/guest-bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GuestDesktopTools } from "./guest-desktop-executor.js";
|
|
2
|
+
import type { GuestDesktopNativeTools } from "./guest-desktop-native.js";
|
|
3
|
+
interface BrowserTextPort extends Pick<GuestDesktopTools, "prepareText"> {
|
|
4
|
+
assertReady(signal: AbortSignal): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/** Owner-only composition for a single headed Chromium page. Never an actor API. */
|
|
7
|
+
export declare function createGuestBrowserTools(native: GuestDesktopNativeTools, content: BrowserTextPort): GuestDesktopTools;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { BROWSER_CONTROL_LIMITS } from "./browser-control-protocol.js";
|
|
2
|
+
import { CuaExecutorError, isCuaExecutorError } from "./cua-executor-error.js";
|
|
3
|
+
/** Owner-only composition for a single headed Chromium page. Never an actor API. */
|
|
4
|
+
export function createGuestBrowserTools(native, content) {
|
|
5
|
+
let addressBarArmed = false;
|
|
6
|
+
let generation = 0;
|
|
7
|
+
return {
|
|
8
|
+
capture: signal => native.capture(signal),
|
|
9
|
+
async input(args, signal) {
|
|
10
|
+
addressBarArmed = false;
|
|
11
|
+
const inputGeneration = ++generation;
|
|
12
|
+
await native.input(args, signal);
|
|
13
|
+
if (signal.aborted || inputGeneration !== generation)
|
|
14
|
+
throw new CuaExecutorError("session_revoked", "outcome_uncertain");
|
|
15
|
+
// Positive navigation intent, not an inference from document.hasFocus().
|
|
16
|
+
addressBarArmed = args.length === 3 && args[0] === "key" && args[1] === "--clearmodifiers" && args[2] === "ctrl+l";
|
|
17
|
+
},
|
|
18
|
+
async prepareText(text, signal) {
|
|
19
|
+
const navigation = addressBarArmed;
|
|
20
|
+
addressBarArmed = false;
|
|
21
|
+
if (!navigation)
|
|
22
|
+
return content.prepareText(text, signal);
|
|
23
|
+
// Other browser chrome, IME composition, and Unicode omnibox text need
|
|
24
|
+
// separate qualification. Do not silently switch routes or transliterate.
|
|
25
|
+
if (!/^[\x20-\x7e]+$/.test(text) || Buffer.byteLength(text) > BROWSER_CONTROL_LIMITS.textBytes) {
|
|
26
|
+
throw new CuaExecutorError("action_rejected", "not_dispatched");
|
|
27
|
+
}
|
|
28
|
+
const preparedGeneration = generation;
|
|
29
|
+
let used = false;
|
|
30
|
+
function check() {
|
|
31
|
+
if (signal.aborted)
|
|
32
|
+
throw new CuaExecutorError("session_revoked", "not_dispatched");
|
|
33
|
+
if (used || preparedGeneration !== generation)
|
|
34
|
+
throw new CuaExecutorError("action_rejected", "not_dispatched");
|
|
35
|
+
}
|
|
36
|
+
check();
|
|
37
|
+
await content.assertReady(signal);
|
|
38
|
+
check();
|
|
39
|
+
return {
|
|
40
|
+
async paste() {
|
|
41
|
+
check();
|
|
42
|
+
await content.assertReady(signal);
|
|
43
|
+
check();
|
|
44
|
+
used = true;
|
|
45
|
+
// Re-establish the explicitly requested destination immediately before
|
|
46
|
+
// native typing; the previous page may have changed its own focus.
|
|
47
|
+
try {
|
|
48
|
+
await native.input(["key", "--clearmodifiers", "ctrl+l"], signal);
|
|
49
|
+
if (signal.aborted)
|
|
50
|
+
throw new CuaExecutorError("session_revoked", "outcome_uncertain");
|
|
51
|
+
await content.assertReady(signal);
|
|
52
|
+
if (signal.aborted || preparedGeneration !== generation)
|
|
53
|
+
throw new CuaExecutorError("session_revoked", "outcome_uncertain");
|
|
54
|
+
await native.typeAscii(text, signal);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Ctrl+L itself is input. No content fallback after that dispatch.
|
|
58
|
+
throw new CuaExecutorError(isCuaExecutorError(error) ? error.code : "execution_failed", "outcome_uncertain");
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
async close() { used = true; }
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=guest-browser-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-browser-tools.js","sourceRoot":"","sources":["../src/guest-browser-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
export interface PreparedGuestChromiumText {
|
|
3
|
+
paste(): Promise<void>;
|
|
4
|
+
close(): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface GuestChromiumText {
|
|
7
|
+
/** Owner/window readiness only: deliberately permits a focused browser toolbar. */
|
|
8
|
+
assertReady(signal: AbortSignal): Promise<void>;
|
|
9
|
+
prepareText(text: string, signal: AbortSignal): Promise<PreparedGuestChromiumText>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A finite text port for one owner-acquired Chromium page in a one-page context.
|
|
14
|
+
* It neither selects/activates targets nor handles browser chrome, frames or
|
|
15
|
+
* dialogs. An observed dialog poisons this port; the owner must handle it and
|
|
16
|
+
* construct a new port. A dialog already open can make preparation time out.
|
|
17
|
+
*
|
|
18
|
+
* Focus checks and insertText are separate protocol calls: page code can change
|
|
19
|
+
* focus between them. This is not atomic element binding or proof an app applied
|
|
20
|
+
* text. A lost post-send acknowledgement is uncertain and terminal for this port.
|
|
21
|
+
* The owner remains responsible for browser/session teardown.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createGuestChromiumText(options: {
|
|
24
|
+
context: BrowserContext;
|
|
25
|
+
page: Page;
|
|
26
|
+
assertFocusedWindow: (signal: AbortSignal) => Promise<void>;
|
|
27
|
+
}): GuestChromiumText;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { CuaExecutorError, isCuaExecutorError } from "./cua-executor-error.js";
|
|
3
|
+
const DEADLINE_MS = 5_000;
|
|
4
|
+
// These expressions execute only in this port's isolated world. Actor text is
|
|
5
|
+
// never interpolated into JavaScript, a selector, a world name, or a CDP method.
|
|
6
|
+
const PREPARE = `(() => {
|
|
7
|
+
const active = () => Object.getOwnPropertyDescriptor(Document.prototype, "activeElement").get.call(document);
|
|
8
|
+
const element = active();
|
|
9
|
+
const editable = () => {
|
|
10
|
+
if (!Document.prototype.hasFocus.call(document) || active() !== element ||
|
|
11
|
+
!element || !element.isConnected || element.ownerDocument !== document ||
|
|
12
|
+
element.localName === "iframe" || element.localName === "frame") return false;
|
|
13
|
+
if (element instanceof HTMLTextAreaElement) return !element.disabled && !element.readOnly;
|
|
14
|
+
if (element instanceof HTMLInputElement) return !element.disabled && !element.readOnly &&
|
|
15
|
+
["text", "search", "tel", "url", "email", "password", "number"].includes(element.type);
|
|
16
|
+
return element instanceof HTMLElement && element.isContentEditable;
|
|
17
|
+
};
|
|
18
|
+
if (!editable()) return false;
|
|
19
|
+
Object.defineProperty(globalThis, "__humanish_text_preparation__", {
|
|
20
|
+
configurable: true, value: Object.freeze({ editable })
|
|
21
|
+
});
|
|
22
|
+
return true;
|
|
23
|
+
})()`;
|
|
24
|
+
const RECHECK = `(() => {
|
|
25
|
+
const state = globalThis.__humanish_text_preparation__;
|
|
26
|
+
return !!state && state.editable() === true;
|
|
27
|
+
})()`;
|
|
28
|
+
/**
|
|
29
|
+
* A finite text port for one owner-acquired Chromium page in a one-page context.
|
|
30
|
+
* It neither selects/activates targets nor handles browser chrome, frames or
|
|
31
|
+
* dialogs. An observed dialog poisons this port; the owner must handle it and
|
|
32
|
+
* construct a new port. A dialog already open can make preparation time out.
|
|
33
|
+
*
|
|
34
|
+
* Focus checks and insertText are separate protocol calls: page code can change
|
|
35
|
+
* focus between them. This is not atomic element binding or proof an app applied
|
|
36
|
+
* text. A lost post-send acknowledgement is uncertain and terminal for this port.
|
|
37
|
+
* The owner remains responsible for browser/session teardown.
|
|
38
|
+
*/
|
|
39
|
+
export function createGuestChromiumText(options) {
|
|
40
|
+
const { context, page, assertFocusedWindow } = options;
|
|
41
|
+
const worldName = `humanish-text-${randomUUID()}`;
|
|
42
|
+
let generation = 0;
|
|
43
|
+
let closed = false;
|
|
44
|
+
let unusable = false;
|
|
45
|
+
let dialogSeen = false;
|
|
46
|
+
let busy = false;
|
|
47
|
+
let closing;
|
|
48
|
+
let activeDisposal;
|
|
49
|
+
const interruptions = new Set();
|
|
50
|
+
const sessions = new Set();
|
|
51
|
+
const detachments = new WeakMap();
|
|
52
|
+
function invalidate() {
|
|
53
|
+
generation++;
|
|
54
|
+
for (const interrupt of interruptions)
|
|
55
|
+
interrupt("session_revoked");
|
|
56
|
+
}
|
|
57
|
+
function dialog() { dialogSeen = true; invalidate(); }
|
|
58
|
+
function targetClosed() { unusable = true; invalidate(); }
|
|
59
|
+
page.on("framenavigated", invalidate);
|
|
60
|
+
page.on("frameattached", invalidate);
|
|
61
|
+
page.on("framedetached", invalidate);
|
|
62
|
+
page.on("dialog", dialog);
|
|
63
|
+
page.on("close", targetClosed);
|
|
64
|
+
context.on("page", invalidate);
|
|
65
|
+
context.on("close", targetClosed);
|
|
66
|
+
function scope(expected) {
|
|
67
|
+
if (closed || unusable)
|
|
68
|
+
throw new CuaExecutorError("executor_closed", "not_dispatched");
|
|
69
|
+
if (generation !== expected)
|
|
70
|
+
throw new CuaExecutorError("session_revoked", "not_dispatched");
|
|
71
|
+
const pages = context.pages();
|
|
72
|
+
if (dialogSeen || page.isClosed() || page.context() !== context || pages.length !== 1 || pages[0] !== page)
|
|
73
|
+
throw new CuaExecutorError("action_rejected", "not_dispatched");
|
|
74
|
+
}
|
|
75
|
+
async function run(signal, expected, body) {
|
|
76
|
+
let dispatched = false;
|
|
77
|
+
let stopped;
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
let rejectStop;
|
|
80
|
+
const stop = new Promise((_resolve, reject) => { rejectStop = reject; });
|
|
81
|
+
// An event can interrupt before the first awaited step; consume that rejection.
|
|
82
|
+
void stop.catch(() => { });
|
|
83
|
+
const interrupt = (code) => {
|
|
84
|
+
if (stopped)
|
|
85
|
+
return;
|
|
86
|
+
stopped = code;
|
|
87
|
+
controller.abort();
|
|
88
|
+
rejectStop(new CuaExecutorError(code, dispatched ? "outcome_uncertain" : "not_dispatched"));
|
|
89
|
+
};
|
|
90
|
+
const abort = () => interrupt("cancelled");
|
|
91
|
+
const timer = setTimeout(() => interrupt("deadline_exceeded"), DEADLINE_MS);
|
|
92
|
+
interruptions.add(interrupt);
|
|
93
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
94
|
+
const check = () => {
|
|
95
|
+
if (signal.aborted)
|
|
96
|
+
abort();
|
|
97
|
+
if (stopped)
|
|
98
|
+
throw new CuaExecutorError(stopped, dispatched ? "outcome_uncertain" : "not_dispatched");
|
|
99
|
+
scope(expected);
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
check();
|
|
103
|
+
return await body({
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
check,
|
|
106
|
+
async step(call, dispatch = false) {
|
|
107
|
+
check();
|
|
108
|
+
if (dispatch)
|
|
109
|
+
dispatched = true;
|
|
110
|
+
const value = await Promise.race([call(), stop]);
|
|
111
|
+
check();
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
controller.abort();
|
|
118
|
+
if (dispatched)
|
|
119
|
+
unusable = true;
|
|
120
|
+
const code = stopped ?? (isCuaExecutorError(error) ? error.code : "transport_failed");
|
|
121
|
+
throw new CuaExecutorError(code, dispatched ? "outcome_uncertain" : "not_dispatched");
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
signal.removeEventListener("abort", abort);
|
|
126
|
+
interruptions.delete(interrupt);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function detach(session) {
|
|
130
|
+
const prior = detachments.get(session);
|
|
131
|
+
if (prior)
|
|
132
|
+
return prior;
|
|
133
|
+
const result = (async () => {
|
|
134
|
+
let timer;
|
|
135
|
+
try {
|
|
136
|
+
await Promise.race([
|
|
137
|
+
session.detach(),
|
|
138
|
+
new Promise((_resolve, reject) => {
|
|
139
|
+
timer = setTimeout(() => reject(new CuaExecutorError("deadline_exceeded", "not_dispatched")), DEADLINE_MS);
|
|
140
|
+
})
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
unusable = true;
|
|
145
|
+
throw new CuaExecutorError("transport_failed", "not_dispatched");
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
sessions.delete(session);
|
|
150
|
+
}
|
|
151
|
+
})();
|
|
152
|
+
detachments.set(session, result);
|
|
153
|
+
// Late acquisition after cancellation also uses this consumed, bounded path.
|
|
154
|
+
void result.catch(() => { });
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
async function ready(op) {
|
|
158
|
+
await op.step(() => assertFocusedWindow(op.signal));
|
|
159
|
+
}
|
|
160
|
+
async function probe(op, session, contextId, expression) {
|
|
161
|
+
const result = await op.step(() => session.send("Runtime.evaluate", {
|
|
162
|
+
expression, contextId, returnByValue: true, awaitPromise: false,
|
|
163
|
+
userGesture: false, includeCommandLineAPI: false, silent: true
|
|
164
|
+
}));
|
|
165
|
+
if (result.exceptionDetails || result.result.type !== "boolean" || result.result.value !== true)
|
|
166
|
+
throw new CuaExecutorError("action_rejected", "not_dispatched");
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
async assertReady(signal) {
|
|
170
|
+
await run(signal, generation, ready);
|
|
171
|
+
},
|
|
172
|
+
async prepareText(text, signal) {
|
|
173
|
+
if (typeof text !== "string" || !text || text.includes("\0") || Buffer.byteLength(text, "utf8") > 65536 ||
|
|
174
|
+
Buffer.from(text, "utf8").toString("utf8") !== text)
|
|
175
|
+
throw new CuaExecutorError("invalid_request", "not_dispatched");
|
|
176
|
+
scope(generation);
|
|
177
|
+
if (busy)
|
|
178
|
+
throw new CuaExecutorError("executor_busy", "not_dispatched");
|
|
179
|
+
busy = true;
|
|
180
|
+
const expected = generation;
|
|
181
|
+
const lifetime = new AbortController();
|
|
182
|
+
const abortLifetime = () => lifetime.abort();
|
|
183
|
+
signal.addEventListener("abort", abortLifetime, { once: true });
|
|
184
|
+
if (signal.aborted)
|
|
185
|
+
abortLifetime();
|
|
186
|
+
let session;
|
|
187
|
+
let contextId;
|
|
188
|
+
let used = false;
|
|
189
|
+
let disposed = false;
|
|
190
|
+
let dispatchedText = false;
|
|
191
|
+
let disposal;
|
|
192
|
+
const dispose = () => {
|
|
193
|
+
if (!disposal) {
|
|
194
|
+
disposed = true;
|
|
195
|
+
lifetime.abort();
|
|
196
|
+
signal.removeEventListener("abort", abortLifetime);
|
|
197
|
+
disposal = (async () => {
|
|
198
|
+
try {
|
|
199
|
+
if (session)
|
|
200
|
+
await detach(session);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw new CuaExecutorError(isCuaExecutorError(error) ? error.code : "transport_failed", dispatchedText ? "outcome_uncertain" : "not_dispatched");
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
busy = false;
|
|
207
|
+
if (activeDisposal === dispose)
|
|
208
|
+
activeDisposal = undefined;
|
|
209
|
+
}
|
|
210
|
+
})();
|
|
211
|
+
}
|
|
212
|
+
return disposal;
|
|
213
|
+
};
|
|
214
|
+
activeDisposal = dispose;
|
|
215
|
+
try {
|
|
216
|
+
await run(lifetime.signal, expected, async (op) => {
|
|
217
|
+
await ready(op);
|
|
218
|
+
session = await op.step(() => context.newCDPSession(page).then(acquired => {
|
|
219
|
+
sessions.add(acquired);
|
|
220
|
+
if (op.signal.aborted || closed)
|
|
221
|
+
void detach(acquired).catch(() => { });
|
|
222
|
+
return acquired;
|
|
223
|
+
}));
|
|
224
|
+
const tree = await op.step(() => session.send("Page.getFrameTree"));
|
|
225
|
+
const frameId = tree.frameTree?.frame?.id;
|
|
226
|
+
if (typeof frameId !== "string" || !frameId || tree.frameTree.frame.parentId)
|
|
227
|
+
throw new CuaExecutorError("invalid_response", "not_dispatched");
|
|
228
|
+
const world = await op.step(() => session.send("Page.createIsolatedWorld", { frameId, worldName }));
|
|
229
|
+
contextId = world.executionContextId;
|
|
230
|
+
if (!Number.isSafeInteger(contextId) || contextId <= 0)
|
|
231
|
+
throw new CuaExecutorError("invalid_response", "not_dispatched");
|
|
232
|
+
await probe(op, session, contextId, PREPARE);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
await dispose().catch(() => { });
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
async paste() {
|
|
241
|
+
if (used || disposed)
|
|
242
|
+
throw new CuaExecutorError("action_rejected", "not_dispatched");
|
|
243
|
+
used = true; // A failed attempt cannot be retried through this handle.
|
|
244
|
+
await run(lifetime.signal, expected, async (op) => {
|
|
245
|
+
await ready(op);
|
|
246
|
+
await probe(op, session, contextId, RECHECK);
|
|
247
|
+
await ready(op);
|
|
248
|
+
op.check();
|
|
249
|
+
await op.step(() => {
|
|
250
|
+
dispatchedText = true;
|
|
251
|
+
return session.send("Input.insertText", { text });
|
|
252
|
+
}, true);
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
close: dispose
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
close() {
|
|
259
|
+
if (!closing) {
|
|
260
|
+
closed = true;
|
|
261
|
+
for (const interrupt of interruptions)
|
|
262
|
+
interrupt("executor_closed");
|
|
263
|
+
page.off("framenavigated", invalidate);
|
|
264
|
+
page.off("frameattached", invalidate);
|
|
265
|
+
page.off("framedetached", invalidate);
|
|
266
|
+
page.off("dialog", dialog);
|
|
267
|
+
page.off("close", targetClosed);
|
|
268
|
+
context.off("page", invalidate);
|
|
269
|
+
context.off("close", targetClosed);
|
|
270
|
+
const active = activeDisposal?.();
|
|
271
|
+
closing = Promise.allSettled([...(active ? [active] : []), ...[...sessions].map(detach)]).then(results => {
|
|
272
|
+
const failure = results.find(result => result.status === "rejected");
|
|
273
|
+
if (failure?.status === "rejected")
|
|
274
|
+
throw failure.reason;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return closing;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=guest-chromium-text.js.map
|