codex-chatgpt-control 0.5.1-alpha.2 → 0.5.1-alpha.3
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/CHANGELOG.md +15 -0
- package/README.md +27 -0
- package/contracts/v1/fixtures/backend-capabilities.json +40 -1
- package/contracts/v1/fixtures/backend-compatibility.json +21 -0
- package/contracts/v1/fixtures/backend-version.json +6 -1
- package/contracts/v1/fixtures/operation-action-prepared-event.json +42 -0
- package/contracts/v1/fixtures/operation-action.json +13 -0
- package/contracts/v1/fixtures/operation-artifact-receipt.json +14 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-intent-event.json +24 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-receipt-event.json +26 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-state.json +178 -0
- package/contracts/v1/fixtures/operation-blocker.json +10 -0
- package/contracts/v1/fixtures/operation-collect-request.json +17 -0
- package/contracts/v1/fixtures/operation-collect-result.json +42 -0
- package/contracts/v1/fixtures/operation-control-receipt.json +13 -0
- package/contracts/v1/fixtures/operation-control-request.json +18 -0
- package/contracts/v1/fixtures/operation-control-result.json +31 -0
- package/contracts/v1/fixtures/operation-event.json +18 -0
- package/contracts/v1/fixtures/operation-handle.json +10 -0
- package/contracts/v1/fixtures/operation-inspect-request.json +13 -0
- package/contracts/v1/fixtures/operation-inspect-result.json +205 -0
- package/contracts/v1/fixtures/operation-ownership-baseline-event.json +34 -0
- package/contracts/v1/fixtures/operation-receipt.json +32 -0
- package/contracts/v1/fixtures/operation-recovery-decision.json +7 -0
- package/contracts/v1/fixtures/operation-recovery-observation.json +12 -0
- package/contracts/v1/fixtures/operation-request.json +33 -0
- package/contracts/v1/fixtures/operation-state.json +144 -0
- package/contracts/v1/fixtures/operation-submission-witness-event.json +20 -0
- package/contracts/v1/fixtures/operation-submission-witness.json +11 -0
- package/contracts/v1/fixtures/operation-submit-result.json +16 -0
- package/contracts/v1/fixtures/operation-target-established-event.json +21 -0
- package/contracts/v1/manifest.json +155 -1
- package/contracts/v1/parity-suite.json +96 -1
- package/contracts/v1/schemas/backend-compatibility.schema.json +65 -0
- package/contracts/v1/schemas/backend-request.schema.json +8 -2
- package/contracts/v1/schemas/capabilities.schema.json +81 -1
- package/contracts/v1/schemas/manifest.schema.json +57 -0
- package/contracts/v1/schemas/operation-action.schema.json +155 -0
- package/contracts/v1/schemas/operation-artifact-receipt.schema.json +89 -0
- package/contracts/v1/schemas/operation-blocker.schema.json +70 -0
- package/contracts/v1/schemas/operation-collect-request.schema.json +46 -0
- package/contracts/v1/schemas/operation-collect-result.schema.json +74 -0
- package/contracts/v1/schemas/operation-control-receipt.schema.json +108 -0
- package/contracts/v1/schemas/operation-control-request.schema.json +111 -0
- package/contracts/v1/schemas/operation-control-result.schema.json +112 -0
- package/contracts/v1/schemas/operation-event.schema.json +756 -0
- package/contracts/v1/schemas/operation-handle.schema.json +55 -0
- package/contracts/v1/schemas/operation-inspect-request.schema.json +42 -0
- package/contracts/v1/schemas/operation-inspect-result.schema.json +1662 -0
- package/contracts/v1/schemas/operation-receipt.schema.json +153 -0
- package/contracts/v1/schemas/operation-recovery.schema.json +314 -0
- package/contracts/v1/schemas/operation-request.schema.json +158 -0
- package/contracts/v1/schemas/operation-state.schema.json +691 -0
- package/contracts/v1/schemas/operation-submission-witness.schema.json +48 -0
- package/contracts/v1/schemas/operation-submit-result.schema.json +94 -0
- package/contracts/v1/surface-drift-policy.json +50 -0
- package/contracts/v1/vectors/operation-request-digest-v1.json +43 -0
- package/dist/codex-chatgpt-control-backend.mjs +43897 -9094
- package/dist/codex-chatgpt-control.bundle.mjs +46360 -10262
- package/dist/src/backend/client.d.ts +103 -2
- package/dist/src/backend/client.js +1436 -97
- package/dist/src/backend/compatibility.d.ts +12 -0
- package/dist/src/backend/compatibility.js +208 -0
- package/dist/src/backend/protocol.d.ts +84 -1
- package/dist/src/backend/protocol.js +31 -5
- package/dist/src/backend/runtime-identity.d.ts +10 -0
- package/dist/src/backend/runtime-identity.js +141 -0
- package/dist/src/backend/session.d.ts +10 -2
- package/dist/src/backend/session.js +478 -7
- package/dist/src/backend/stdio-server.d.ts +4 -1
- package/dist/src/backend/stdio-server.js +240 -28
- package/dist/src/browser/active-composer-file-input.d.ts +9 -0
- package/dist/src/browser/active-composer-file-input.js +33 -0
- package/dist/src/browser/attach.d.ts +4 -1
- package/dist/src/browser/attach.js +136 -28
- package/dist/src/browser/downloads.d.ts +2 -0
- package/dist/src/client.d.ts +38 -0
- package/dist/src/client.js +1222 -59
- package/dist/src/commands/configuration.js +13 -7
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +26 -3
- package/dist/src/commands/experience.js +35 -0
- package/dist/src/commands/files.js +4 -27
- package/dist/src/commands/modes.js +49 -17
- package/dist/src/commands/power-discovery.d.ts +130 -0
- package/dist/src/commands/power-discovery.js +627 -0
- package/dist/src/commands/project-sources.js +325 -31
- package/dist/src/commands/sequence.js +59 -32
- package/dist/src/commands/session.js +7 -2
- package/dist/src/commands/work.js +31 -0
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +9 -0
- package/dist/src/operations/artifact-output.d.ts +69 -0
- package/dist/src/operations/artifact-output.js +1748 -0
- package/dist/src/operations/artifact-stream.d.ts +19 -0
- package/dist/src/operations/artifact-stream.js +35 -0
- package/dist/src/operations/artifact-transfer.d.ts +149 -0
- package/dist/src/operations/artifact-transfer.js +1312 -0
- package/dist/src/operations/browser-adapter.d.ts +161 -0
- package/dist/src/operations/browser-adapter.js +2023 -0
- package/dist/src/operations/browser-observation.d.ts +110 -0
- package/dist/src/operations/browser-observation.js +1208 -0
- package/dist/src/operations/browser-target.d.ts +103 -0
- package/dist/src/operations/browser-target.js +489 -0
- package/dist/src/operations/canonical.d.ts +4 -0
- package/dist/src/operations/canonical.js +412 -0
- package/dist/src/operations/chatgpt-runtime.d.ts +72 -0
- package/dist/src/operations/chatgpt-runtime.js +1150 -0
- package/dist/src/operations/client.d.ts +138 -0
- package/dist/src/operations/client.js +1143 -0
- package/dist/src/operations/collector.d.ts +195 -0
- package/dist/src/operations/collector.js +1039 -0
- package/dist/src/operations/control.d.ts +372 -0
- package/dist/src/operations/control.js +1498 -0
- package/dist/src/operations/file-identity.d.ts +33 -0
- package/dist/src/operations/file-identity.js +153 -0
- package/dist/src/operations/handle.d.ts +19 -0
- package/dist/src/operations/handle.js +727 -0
- package/dist/src/operations/index.d.ts +22 -0
- package/dist/src/operations/index.js +22 -0
- package/dist/src/operations/journal.d.ts +142 -0
- package/dist/src/operations/journal.js +1931 -0
- package/dist/src/operations/production-attachments.d.ts +116 -0
- package/dist/src/operations/production-attachments.js +1018 -0
- package/dist/src/operations/production-chatgpt-artifacts.d.ts +74 -0
- package/dist/src/operations/production-chatgpt-artifacts.js +1146 -0
- package/dist/src/operations/production-chatgpt-attachments.d.ts +71 -0
- package/dist/src/operations/production-chatgpt-attachments.js +1563 -0
- package/dist/src/operations/production-configuration.d.ts +42 -0
- package/dist/src/operations/production-configuration.js +1363 -0
- package/dist/src/operations/production-primitives.d.ts +53 -0
- package/dist/src/operations/production-primitives.js +1073 -0
- package/dist/src/operations/production-work-steer.d.ts +203 -0
- package/dist/src/operations/production-work-steer.js +1218 -0
- package/dist/src/operations/recovery.d.ts +80 -0
- package/dist/src/operations/recovery.js +174 -0
- package/dist/src/operations/runtime-adapter.d.ts +118 -0
- package/dist/src/operations/runtime-adapter.js +786 -0
- package/dist/src/operations/send-once.d.ts +224 -0
- package/dist/src/operations/send-once.js +1081 -0
- package/dist/src/operations/service.d.ts +281 -0
- package/dist/src/operations/service.js +2810 -0
- package/dist/src/operations/staging.d.ts +136 -0
- package/dist/src/operations/staging.js +630 -0
- package/dist/src/operations/state-machine.d.ts +29 -0
- package/dist/src/operations/state-machine.js +2047 -0
- package/dist/src/operations/submission.d.ts +423 -0
- package/dist/src/operations/submission.js +1676 -0
- package/dist/src/operations/turn-ownership.d.ts +202 -0
- package/dist/src/operations/turn-ownership.js +700 -0
- package/dist/src/operations/types.d.ts +454 -0
- package/dist/src/operations/types.js +20 -0
- package/dist/src/operations/wire-requests.d.ts +21 -0
- package/dist/src/operations/wire-requests.js +396 -0
- package/dist/src/operations/wire-results.d.ts +97 -0
- package/dist/src/operations/wire-results.js +818 -0
- package/dist/src/runner/responses.d.ts +3 -1
- package/dist/src/runner/responses.js +19 -1
- package/dist/src/runner/result.js +227 -55
- package/dist/src/runner/types.d.ts +12 -0
- package/dist/src/runtime/command-routing.d.ts +149 -0
- package/dist/src/runtime/command-routing.js +431 -0
- package/dist/src/runtime/coordinated-browser.d.ts +24 -0
- package/dist/src/runtime/coordinated-browser.js +315 -0
- package/dist/src/runtime/coordinated-page.d.ts +39 -0
- package/dist/src/runtime/coordinated-page.js +634 -0
- package/dist/src/runtime/operation-context.d.ts +142 -0
- package/dist/src/runtime/operation-context.js +410 -0
- package/dist/src/runtime/runtime-session.d.ts +95 -0
- package/dist/src/runtime/runtime-session.js +314 -0
- package/dist/src/runtime/tab-coordinator.d.ts +200 -0
- package/dist/src/runtime/tab-coordinator.js +1200 -0
- package/dist/src/runtime/value-boundaries.d.ts +18 -0
- package/dist/src/runtime/value-boundaries.js +49 -0
- package/dist/src/safety/untrusted-output.js +2 -1
- package/dist/src/scripts/backend-server.js +4 -1
- package/dist/src/scripts/live-smoke/harness.js +79 -7
- package/dist/src/scripts/live-smoke/scenarios.d.ts +28 -0
- package/dist/src/scripts/live-smoke/scenarios.js +110 -18
- package/dist/src/scripts/live-smoke/types.d.ts +3 -0
- package/dist/src/scripts/release-canary-module.js +17 -4
- package/dist/src/types.d.ts +38 -1
- package/package.json +1 -1
- package/references/2026-08-16-transactional-operations.md +459 -0
- package/references/agents-runner.md +34 -0
- package/references/backend-protocol.md +73 -0
- package/references/python-parity.md +72 -0
- package/references/responses-adapter.md +19 -0
- package/references/streaming.md +6 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createBrowserResourceKey, getProcessTabCoordinator } from "./tab-coordinator.js";
|
|
2
|
+
import { createCoordinatedPage, unwrapCoordinatedPage } from "./coordinated-page.js";
|
|
3
|
+
const MAX_PROTO_DEPTH = 12;
|
|
4
|
+
const MAX_CACHED_BROWSER_AFFINITIES = 256;
|
|
5
|
+
export const MAX_BROWSER_TAB_CANDIDATES = 256;
|
|
6
|
+
const UNKNOWN_BROWSER_ID = "codex-process-browser";
|
|
7
|
+
const DEFAULT_BACKEND_SESSION_ID = "legacy-runtime";
|
|
8
|
+
export class CoordinatedBrowserError extends Error {
|
|
9
|
+
code = "coordinated_browser_invalid";
|
|
10
|
+
constructor(message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.name = "CoordinatedBrowserError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const browserResourceCache = new WeakMap();
|
|
16
|
+
const browserWrappers = new WeakMap();
|
|
17
|
+
const rawBrowsers = new WeakMap();
|
|
18
|
+
function isObjectLike(value) {
|
|
19
|
+
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
20
|
+
}
|
|
21
|
+
function invalid(message, cause) {
|
|
22
|
+
throw new CoordinatedBrowserError(message, cause === undefined ? undefined : { cause });
|
|
23
|
+
}
|
|
24
|
+
function readDataMember(value, key, label) {
|
|
25
|
+
let current = value;
|
|
26
|
+
for (let depth = 0; current !== null && depth < MAX_PROTO_DEPTH; depth += 1) {
|
|
27
|
+
let descriptor;
|
|
28
|
+
try {
|
|
29
|
+
descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return invalid(`Cannot inspect ${label}: provider descriptor access failed`, error);
|
|
33
|
+
}
|
|
34
|
+
if (descriptor !== undefined) {
|
|
35
|
+
if (!("value" in descriptor))
|
|
36
|
+
return invalid(`Cannot use ${label}: accessor-backed provider members are not supported`);
|
|
37
|
+
return descriptor.value;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const prototype = Object.getPrototypeOf(current);
|
|
41
|
+
current = isObjectLike(prototype) ? prototype : null;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return invalid(`Cannot inspect ${label}: provider prototype access failed`, error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (current !== null)
|
|
48
|
+
return invalid(`Cannot inspect ${label}: provider prototype depth exceeded`);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
function optionalCallable(value, key, label) {
|
|
52
|
+
const member = readDataMember(value, key, label);
|
|
53
|
+
if (member === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (typeof member !== "function")
|
|
56
|
+
return invalid(`${label} is not callable`);
|
|
57
|
+
return member;
|
|
58
|
+
}
|
|
59
|
+
function normalizeOwner(owner) {
|
|
60
|
+
const ownerRecord = owner === undefined ? undefined : (isObjectLike(owner) ? owner : invalid("coordinated browser owner metadata is invalid"));
|
|
61
|
+
const backendSessionIdValue = ownerRecord === undefined
|
|
62
|
+
? DEFAULT_BACKEND_SESSION_ID
|
|
63
|
+
: readDataMember(ownerRecord, "backendSessionId", "owner.backendSessionId");
|
|
64
|
+
if (typeof backendSessionIdValue !== "string" || backendSessionIdValue.trim().length === 0 || /[\u0000-\u001f\u007f]/u.test(backendSessionIdValue)) {
|
|
65
|
+
return invalid("coordinated browser owner metadata is invalid");
|
|
66
|
+
}
|
|
67
|
+
const ownerIdValue = ownerRecord === undefined ? undefined : readDataMember(ownerRecord, "ownerId", "owner.ownerId");
|
|
68
|
+
const operationIdValue = ownerRecord === undefined ? undefined : readDataMember(ownerRecord, "operationId", "owner.operationId");
|
|
69
|
+
if ((ownerIdValue !== undefined && (typeof ownerIdValue !== "string" || ownerIdValue.trim().length === 0 || /[\u0000-\u001f\u007f]/u.test(ownerIdValue)))
|
|
70
|
+
|| (operationIdValue !== undefined && (typeof operationIdValue !== "string" || operationIdValue.trim().length === 0 || /[\u0000-\u001f\u007f]/u.test(operationIdValue)))) {
|
|
71
|
+
return invalid("coordinated browser owner metadata is invalid");
|
|
72
|
+
}
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
backendSessionId: backendSessionIdValue.trim(),
|
|
75
|
+
...(ownerIdValue === undefined ? {} : { ownerId: ownerIdValue.trim() }),
|
|
76
|
+
...(operationIdValue === undefined ? {} : { operationId: operationIdValue.trim() })
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function normalizeOptions(options) {
|
|
80
|
+
const value = options === undefined
|
|
81
|
+
? undefined
|
|
82
|
+
: (isObjectLike(options) ? options : invalid("coordinated browser options are invalid"));
|
|
83
|
+
const coordinatorValue = value === undefined
|
|
84
|
+
? undefined
|
|
85
|
+
: readDataMember(value, "coordinator", "options.coordinator");
|
|
86
|
+
const ownerValue = value === undefined
|
|
87
|
+
? undefined
|
|
88
|
+
: readDataMember(value, "owner", "options.owner");
|
|
89
|
+
const coordinator = coordinatorValue ?? getProcessTabCoordinator();
|
|
90
|
+
if (!isObjectLike(coordinator))
|
|
91
|
+
return invalid("coordinated browser coordinator is invalid");
|
|
92
|
+
if (typeof readDataMember(coordinator, "withBrowserAcquisition", "coordinator.withBrowserAcquisition") !== "function"
|
|
93
|
+
|| typeof readDataMember(coordinator, "withTabTransaction", "coordinator.withTabTransaction") !== "function") {
|
|
94
|
+
return invalid("coordinated browser coordinator is missing transaction methods");
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({ coordinator: coordinator, owner: normalizeOwner(ownerValue) });
|
|
97
|
+
}
|
|
98
|
+
function stableBrowserResource(browser) {
|
|
99
|
+
if (browser !== undefined && isObjectLike(browser)) {
|
|
100
|
+
const existing = browserResourceCache.get(browser);
|
|
101
|
+
if (existing !== undefined)
|
|
102
|
+
return existing;
|
|
103
|
+
let browserName;
|
|
104
|
+
try {
|
|
105
|
+
browserName = readDataMember(browser, "name", "browser.name");
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
browserName = undefined;
|
|
109
|
+
}
|
|
110
|
+
const normalizedName = typeof browserName === "string"
|
|
111
|
+
&& browserName.trim().length > 0
|
|
112
|
+
&& browserName.trim().length <= 128
|
|
113
|
+
&& !/[\u0000-\u001f\u007f]/u.test(browserName)
|
|
114
|
+
? browserName.trim().slice(0, 128)
|
|
115
|
+
: undefined;
|
|
116
|
+
// Names are provider-level identities, not tab labels. Unknown/current
|
|
117
|
+
// Codex bridge objects all deliberately share one conservative fallback.
|
|
118
|
+
const browserId = normalizedName === undefined
|
|
119
|
+
? UNKNOWN_BROWSER_ID
|
|
120
|
+
: `codex-${normalizedName}`;
|
|
121
|
+
const key = createBrowserResourceKey("codex", browserId);
|
|
122
|
+
browserResourceCache.set(browser, key);
|
|
123
|
+
return key;
|
|
124
|
+
}
|
|
125
|
+
return createBrowserResourceKey("codex", UNKNOWN_BROWSER_ID);
|
|
126
|
+
}
|
|
127
|
+
export function coordinatedBrowserResource(browser) {
|
|
128
|
+
return { kind: "browser", key: stableBrowserResource(browser) };
|
|
129
|
+
}
|
|
130
|
+
function invoke(fn, receiver, args) {
|
|
131
|
+
try {
|
|
132
|
+
return Reflect.apply(fn, receiver, args);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function absorbRejection(promise) {
|
|
139
|
+
void promise.catch(() => undefined);
|
|
140
|
+
return promise;
|
|
141
|
+
}
|
|
142
|
+
function route(state, priority, label, callback) {
|
|
143
|
+
const requestOptions = { owner: state.options.owner, priority, label };
|
|
144
|
+
const pending = state.options.coordinator.withBrowserAcquisition(state.browserResource, requestOptions, () => callback());
|
|
145
|
+
return absorbRejection(pending);
|
|
146
|
+
}
|
|
147
|
+
function pageFor(state, value) {
|
|
148
|
+
if (!isObjectLike(value))
|
|
149
|
+
return invalid("browser provider returned an invalid page object");
|
|
150
|
+
const rawPage = unwrapCoordinatedPage(value);
|
|
151
|
+
return createCoordinatedPage(rawPage, {
|
|
152
|
+
coordinator: state.options.coordinator,
|
|
153
|
+
resource: { kind: "browser", key: state.browserResource },
|
|
154
|
+
owner: state.options.owner
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function pageResult(state, value) {
|
|
158
|
+
if (isObjectLike(value))
|
|
159
|
+
return pageFor(state, value);
|
|
160
|
+
return invalid("browser provider returned an invalid page object");
|
|
161
|
+
}
|
|
162
|
+
function callAndMap(state, receiver, method, args, priority, label, mapper) {
|
|
163
|
+
const pending = route(state, priority, label, async () => mapper(await invoke(method, receiver, args)));
|
|
164
|
+
return absorbRejection(pending);
|
|
165
|
+
}
|
|
166
|
+
function boundedTabCandidates(value, label) {
|
|
167
|
+
if (!Array.isArray(value))
|
|
168
|
+
return invalid(`${label} returned an invalid tab list`);
|
|
169
|
+
if (value.length > MAX_BROWSER_TAB_CANDIDATES) {
|
|
170
|
+
return invalid(`${label} exceeded the bounded tab candidate limit`);
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
function wrapUser(rawUser, state) {
|
|
175
|
+
const openTabs = optionalCallable(rawUser, "openTabs", "browser.user.openTabs");
|
|
176
|
+
const claimTab = optionalCallable(rawUser, "claimTab", "browser.user.claimTab");
|
|
177
|
+
return {
|
|
178
|
+
...(openTabs === undefined ? {} : {
|
|
179
|
+
openTabs: () => callAndMap(state, rawUser, openTabs, [], "read", "browser.user.openTabs", value => boundedTabCandidates(value, "browser.user.openTabs"))
|
|
180
|
+
}),
|
|
181
|
+
...(claimTab === undefined ? {} : {
|
|
182
|
+
claimTab: (tab) => callAndMap(state, rawUser, claimTab, [tab], "mutation", "browser.user.claimTab", value => pageResult(state, value))
|
|
183
|
+
})
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function wrapTabs(rawTabs, state) {
|
|
187
|
+
const selected = optionalCallable(rawTabs, "selected", "browser.tabs.selected");
|
|
188
|
+
const list = optionalCallable(rawTabs, "list", "browser.tabs.list");
|
|
189
|
+
const get = optionalCallable(rawTabs, "get", "browser.tabs.get");
|
|
190
|
+
const create = optionalCallable(rawTabs, "create", "browser.tabs.create");
|
|
191
|
+
const newer = optionalCallable(rawTabs, "new", "browser.tabs.new");
|
|
192
|
+
const finalize = optionalCallable(rawTabs, "finalize", "browser.tabs.finalize");
|
|
193
|
+
return {
|
|
194
|
+
...(selected === undefined ? {} : {
|
|
195
|
+
selected: () => callAndMap(state, rawTabs, selected, [], "read", "browser.tabs.selected", value => value === undefined ? undefined : pageFor(state, value))
|
|
196
|
+
}),
|
|
197
|
+
...(list === undefined ? {} : {
|
|
198
|
+
list: () => callAndMap(state, rawTabs, list, [], "read", "browser.tabs.list", value => boundedTabCandidates(value, "browser.tabs.list").map(item => pageFor(state, item)))
|
|
199
|
+
}),
|
|
200
|
+
...(get === undefined ? {} : {
|
|
201
|
+
get: (id) => callAndMap(state, rawTabs, get, [id], "read", "browser.tabs.get", value => pageFor(state, value))
|
|
202
|
+
}),
|
|
203
|
+
...(create === undefined ? {} : {
|
|
204
|
+
create: (url) => callAndMap(state, rawTabs, create, [url], "mutation", "browser.tabs.create", value => pageFor(state, value))
|
|
205
|
+
}),
|
|
206
|
+
...(newer === undefined ? {} : {
|
|
207
|
+
new: (url) => callAndMap(state, rawTabs, newer, url === undefined ? [] : [url], "mutation", "browser.tabs.new", value => pageFor(state, value))
|
|
208
|
+
}),
|
|
209
|
+
...(finalize === undefined ? {} : {
|
|
210
|
+
finalize: (options) => route(state, "control", "browser.tabs.finalize", () => invoke(finalize, rawTabs, [options]))
|
|
211
|
+
})
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function makeBrowserState(rawBrowser, options) {
|
|
215
|
+
return Object.freeze({ rawBrowser, browserResource: stableBrowserResource(rawBrowser), options });
|
|
216
|
+
}
|
|
217
|
+
function buildBrowser(state) {
|
|
218
|
+
const rawBrowser = state.rawBrowser;
|
|
219
|
+
const name = readDataMember(rawBrowser, "name", "browser.name");
|
|
220
|
+
const rawUser = readDataMember(rawBrowser, "user", "browser.user");
|
|
221
|
+
const rawTabs = readDataMember(rawBrowser, "tabs", "browser.tabs");
|
|
222
|
+
const newPage = optionalCallable(rawBrowser, "newPage", "browser.newPage");
|
|
223
|
+
const wrapper = {
|
|
224
|
+
...(typeof name === "string" ? { name } : {}),
|
|
225
|
+
...(rawUser === undefined ? {} : {
|
|
226
|
+
user: wrapUser(isObjectLike(rawUser) ? rawUser : invalid("browser.user must be an object"), state)
|
|
227
|
+
}),
|
|
228
|
+
...(rawTabs === undefined ? {} : {
|
|
229
|
+
tabs: wrapTabs(isObjectLike(rawTabs) ? rawTabs : invalid("browser.tabs must be an object"), state)
|
|
230
|
+
}),
|
|
231
|
+
...(newPage === undefined ? {} : {
|
|
232
|
+
newPage: () => callAndMap(state, rawBrowser, newPage, [], "mutation", "browser.newPage", value => pageFor(state, value))
|
|
233
|
+
})
|
|
234
|
+
};
|
|
235
|
+
rawBrowsers.set(wrapper, state.rawBrowser);
|
|
236
|
+
return wrapper;
|
|
237
|
+
}
|
|
238
|
+
function browserCache(rawBrowser, coordinator) {
|
|
239
|
+
let byCoordinator = browserWrappers.get(rawBrowser);
|
|
240
|
+
if (byCoordinator === undefined) {
|
|
241
|
+
byCoordinator = new WeakMap();
|
|
242
|
+
browserWrappers.set(rawBrowser, byCoordinator);
|
|
243
|
+
}
|
|
244
|
+
let byOwner = byCoordinator.get(coordinator);
|
|
245
|
+
if (byOwner === undefined) {
|
|
246
|
+
byOwner = new Map();
|
|
247
|
+
byCoordinator.set(coordinator, byOwner);
|
|
248
|
+
}
|
|
249
|
+
return byOwner;
|
|
250
|
+
}
|
|
251
|
+
function cacheBrowser(cache, affinity, browser) {
|
|
252
|
+
// Owner affinity includes operationId. Touching on a hit and evicting the
|
|
253
|
+
// oldest entry keeps a long-lived runtime from retaining one strong wrapper
|
|
254
|
+
// and its provider graph per operation forever.
|
|
255
|
+
cache.delete(affinity);
|
|
256
|
+
cache.set(affinity, browser);
|
|
257
|
+
while (cache.size > MAX_CACHED_BROWSER_AFFINITIES) {
|
|
258
|
+
const oldest = cache.keys().next().value;
|
|
259
|
+
if (oldest === undefined)
|
|
260
|
+
break;
|
|
261
|
+
cache.delete(oldest);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function ownerCacheKey(owner) {
|
|
265
|
+
return [owner.backendSessionId, owner.ownerId ?? "", owner.operationId ?? ""]
|
|
266
|
+
.map(value => encodeURIComponent(value))
|
|
267
|
+
.join(":");
|
|
268
|
+
}
|
|
269
|
+
/** Wrap a browser using one browser-wide actor; no per-tab capability is inferred. */
|
|
270
|
+
export function createCoordinatedBrowser(browser, options) {
|
|
271
|
+
if (!isObjectLike(browser))
|
|
272
|
+
return invalid("browser must be a provider BrowserLike object");
|
|
273
|
+
const rawBrowser = (rawBrowsers.get(browser) ?? browser);
|
|
274
|
+
const normalized = normalizeOptions(options);
|
|
275
|
+
const cache = browserCache(rawBrowser, normalized.coordinator);
|
|
276
|
+
const key = ownerCacheKey(normalized.owner);
|
|
277
|
+
const existing = cache.get(key);
|
|
278
|
+
if (existing !== undefined) {
|
|
279
|
+
cacheBrowser(cache, key, existing);
|
|
280
|
+
return existing;
|
|
281
|
+
}
|
|
282
|
+
const wrapper = buildBrowser(makeBrowserState(rawBrowser, normalized));
|
|
283
|
+
cacheBrowser(cache, key, wrapper);
|
|
284
|
+
return wrapper;
|
|
285
|
+
}
|
|
286
|
+
/** Wrap one initial/captured page with the browser-wide legacy actor. */
|
|
287
|
+
export function createCoordinatedPageForBrowser(page, browser, options) {
|
|
288
|
+
const normalized = normalizeOptions(options);
|
|
289
|
+
const rawBrowser = browser === undefined ? undefined : (rawBrowsers.get(browser) ?? browser);
|
|
290
|
+
const resource = { kind: "browser", key: stableBrowserResource(rawBrowser) };
|
|
291
|
+
return createCoordinatedPage(unwrapCoordinatedPage(page), {
|
|
292
|
+
coordinator: normalized.coordinator,
|
|
293
|
+
resource,
|
|
294
|
+
owner: normalized.owner
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
/** Make a fresh RuntimeEnv snapshot with only browser/page values coordinated. */
|
|
298
|
+
export function coordinateRuntimeEnv(env, options) {
|
|
299
|
+
const normalized = normalizeOptions(options);
|
|
300
|
+
const browser = env.browser === undefined ? undefined : createCoordinatedBrowser(env.browser, normalized);
|
|
301
|
+
const page = env.page === undefined ? undefined : createCoordinatedPageForBrowser(env.page, browser, normalized);
|
|
302
|
+
return {
|
|
303
|
+
...(env.agent === undefined ? {} : { agent: env.agent }),
|
|
304
|
+
...(browser === undefined ? {} : { browser }),
|
|
305
|
+
...(page === undefined ? {} : { page }),
|
|
306
|
+
...(env.clipboard === undefined ? {} : { clipboard: env.clipboard }),
|
|
307
|
+
...(env.now === undefined ? {} : { now: env.now }),
|
|
308
|
+
...(env.expectedTabId === undefined ? {} : { expectedTabId: env.expectedTabId })
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
export function unwrapCoordinatedBrowser(browser) {
|
|
312
|
+
if (!isObjectLike(browser))
|
|
313
|
+
return browser;
|
|
314
|
+
return rawBrowsers.get(browser) ?? browser;
|
|
315
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { PageLike } from "../types.js";
|
|
2
|
+
import { type BrowserResourceKey, type CoordinatorOwner, type CoordinatorPriority, type ProcessTabCoordinator, type TabResourceKey } from "./tab-coordinator.js";
|
|
3
|
+
/** The two coordinator resources understood by the page facade. */
|
|
4
|
+
export type CoordinatedPageResource = Readonly<{
|
|
5
|
+
kind: "tab";
|
|
6
|
+
key: TabResourceKey;
|
|
7
|
+
}> | Readonly<{
|
|
8
|
+
kind: "browser";
|
|
9
|
+
key: BrowserResourceKey;
|
|
10
|
+
}>;
|
|
11
|
+
export type CoordinatedPageOptions = Readonly<{
|
|
12
|
+
coordinator: ProcessTabCoordinator;
|
|
13
|
+
resource: CoordinatedPageResource;
|
|
14
|
+
owner: CoordinatorOwner;
|
|
15
|
+
/** Optional default deadline for one browser transaction. */
|
|
16
|
+
defaultTimeoutMs?: number;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* The priority mapping is deliberately public. It makes a call site review
|
|
20
|
+
*able without having to infer scheduler intent from a method name.
|
|
21
|
+
*/
|
|
22
|
+
export declare const COORDINATED_PAGE_PRIORITIES: Readonly<Record<string, CoordinatorPriority>>;
|
|
23
|
+
export declare class CoordinatedPageError extends Error {
|
|
24
|
+
readonly code = "coordinated_page_invalid";
|
|
25
|
+
constructor(message: string, options?: {
|
|
26
|
+
cause?: unknown;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** Return the registration fence for a coordinated waitForEvent promise. */
|
|
30
|
+
export declare function coordinatedEventRegistrationBarrier(value: unknown): Promise<void> | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Wrap one provider page. The raw page is never enumerated or copied; only
|
|
33
|
+
* the documented PageLike members are read through bounded data descriptors.
|
|
34
|
+
* Locator construction remains synchronous, while each locator action is a
|
|
35
|
+
* separate short coordinator transaction.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createCoordinatedPage(page: PageLike, options: CoordinatedPageOptions): PageLike;
|
|
38
|
+
/** Explicit identity seam for integrations that need the underlying provider page. */
|
|
39
|
+
export declare function unwrapCoordinatedPage<T extends PageLike>(page: T): PageLike;
|