@melaya/runner 1.1.68 → 1.1.69
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/browserBridge.js +178 -0
- package/dist/browserFast.d.ts +144 -0
- package/dist/browserFast.js +363 -0
- package/dist/browserProvisioner.d.ts +39 -0
- package/dist/browserProvisioner.js +96 -0
- package/dist/cli.js +180 -66
- package/dist/computerHelperManifest.d.ts +5 -0
- package/dist/computerHelperManifest.js +9 -0
- package/dist/computerHostCli.d.ts +233 -0
- package/dist/computerHostCli.js +808 -0
- package/dist/computerRelay.d.ts +73 -0
- package/dist/computerRelay.js +280 -0
- package/dist/connection.d.ts +12 -2
- package/dist/connection.js +299 -82
- package/dist/detect.d.ts +40 -1
- package/dist/detect.js +194 -36
- package/dist/hostChannel.d.ts +122 -0
- package/dist/hostChannel.js +117 -0
- package/dist/hostFiles.d.ts +25 -0
- package/dist/hostFiles.js +90 -0
- package/dist/linkedinLogin.js +13 -17
- package/dist/lumaLogin.js +11 -17
- package/dist/processTree.d.ts +65 -0
- package/dist/processTree.js +237 -0
- package/dist/publishGate.js +14 -4
- package/dist/pythonEnv.js +6 -7
- package/dist/sharedVendor.js +3 -1
- package/dist/singleInstance.js +10 -1
- package/dist/startupCache.d.ts +22 -0
- package/dist/startupCache.js +45 -0
- package/dist/startupTimings.d.ts +34 -0
- package/dist/startupTimings.js +81 -0
- package/dist/uvBootstrap.js +32 -24
- package/dist/vaults.js +3 -1
- package/package.json +1 -1
package/dist/browserBridge.js
CHANGED
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
// POST /browser/act -> {ok, result, tree, generation} |
|
|
25
25
|
// {ok:false, error:{code, message}}
|
|
26
26
|
// POST /browser/run -> {ok, result, console[], tree}
|
|
27
|
+
// POST /browser/fast -> {ok, result: text} (browser_fast: the
|
|
28
|
+
// server's fast executor drives this run
|
|
29
|
+
// over a pump, see browserFast.ts)
|
|
27
30
|
//
|
|
28
31
|
// Every act is ACT-AND-OBSERVE: it returns a fresh snapshot so the model
|
|
29
32
|
// never acts twice on a stale world.
|
|
@@ -41,6 +44,7 @@ import { runSandboxedScript } from "./codeWorker.js";
|
|
|
41
44
|
import { classifyControlEffect, writeGatedInMode } from "./publishGate.js";
|
|
42
45
|
import { applyUploadResize, parseUploadResize, resolveDataUrlSource, resolveHandleSource, resolvePathSource, resolveUrlSource, UploadSourceError, } from "./uploadFetch.js";
|
|
43
46
|
import { unlink } from "node:fs/promises";
|
|
47
|
+
import { StableRefs, clampHitlMode, execFastCommand, postServerJson, runFastPump, translateStepRefs, } from "./browserFast.js";
|
|
44
48
|
// A wall-clock around a CDP command. Playwright's CDPSession.send has NO default
|
|
45
49
|
// timeout, so a wedged/busy renderer means the promise NEVER settles (not a
|
|
46
50
|
// rejection - no catch fires) and the lease op-chain queues every later op
|
|
@@ -812,6 +816,10 @@ export async function startBrowserBridge(opts) {
|
|
|
812
816
|
}
|
|
813
817
|
return rec;
|
|
814
818
|
}
|
|
819
|
+
// The last snapshot's rendered nodes per lease, for fast mode (browserFast.ts),
|
|
820
|
+
// which renders its own screen with stable refs from them.
|
|
821
|
+
const fastSnapItems = new WeakMap();
|
|
822
|
+
const fastStableRefs = new WeakMap();
|
|
815
823
|
async function captureSnapshot(rec, lease, scope) {
|
|
816
824
|
const page = lease.page;
|
|
817
825
|
const generation = sessions.beginSnapshot(lease);
|
|
@@ -874,10 +882,17 @@ export async function startBrowserBridge(opts) {
|
|
|
874
882
|
.sort((a, b) => a.order - b.order);
|
|
875
883
|
// Bind refs and render one line per node.
|
|
876
884
|
const lines = [];
|
|
885
|
+
const fastItems = [];
|
|
877
886
|
let refNo = 0;
|
|
878
887
|
for (const n of candidates) {
|
|
879
888
|
refNo += 1;
|
|
880
889
|
const ref = `@e${refNo}`;
|
|
890
|
+
fastItems.push({
|
|
891
|
+
ref, frameKey: n.frameKey, backendNodeId: n.backendNodeId, role: n.role, name: n.name,
|
|
892
|
+
aria: n.aria, domId: n.domId, href: n.href, click: n.click, edit: n.edit, covered: n.covered,
|
|
893
|
+
l: n.l, t: n.t, r: n.r, b: n.b,
|
|
894
|
+
...(n.edit && n.value ? { value: n.value } : {}),
|
|
895
|
+
});
|
|
881
896
|
sessions.bindRef(lease, {
|
|
882
897
|
ref,
|
|
883
898
|
targetRef: lease.ref,
|
|
@@ -895,6 +910,7 @@ export async function startBrowserBridge(opts) {
|
|
|
895
910
|
const href = n.href ? ` @${n.href.slice(0, 100)}` : "";
|
|
896
911
|
lines.push(`${ref} <${n.role}>${flags}${name}${aria}${domId}${href} <${Math.round(n.l)},${Math.round(n.t)},${Math.round(n.r)},${Math.round(n.b)}>`);
|
|
897
912
|
}
|
|
913
|
+
fastSnapItems.set(lease, { generation, items: fastItems });
|
|
898
914
|
const url = page.url();
|
|
899
915
|
const title = await page.title().catch(() => "");
|
|
900
916
|
const header = `url=${url} title=${title.slice(0, 120)} gen=${generation} nodes=${lines.length}/${total}`;
|
|
@@ -1048,7 +1064,19 @@ export async function startBrowserBridge(opts) {
|
|
|
1048
1064
|
}
|
|
1049
1065
|
}
|
|
1050
1066
|
catch { /* unknown -> leave uncovered (conservative) */ }
|
|
1067
|
+
// A field's value, for fast mode's check that typed text
|
|
1068
|
+
// landed. Never a secret field's (password, one-time code,
|
|
1069
|
+
// card number): those are left out entirely.
|
|
1070
|
+
let value = "";
|
|
1071
|
+
if (edit) {
|
|
1072
|
+
const type = attr(dom.doc, dom.nodeIdx, "type").toLowerCase();
|
|
1073
|
+
const ac = attr(dom.doc, dom.nodeIdx, "autocomplete").toLowerCase();
|
|
1074
|
+
const secret = type === "password" || /(^|\s)(current-password|new-password|one-time-code|cc-number|cc-csc)(\s|$)/.test(ac);
|
|
1075
|
+
if (!secret)
|
|
1076
|
+
value = String(n.value?.value ?? "").slice(0, 400);
|
|
1077
|
+
}
|
|
1051
1078
|
out.push({
|
|
1079
|
+
...(value ? { value } : {}),
|
|
1052
1080
|
frameKey,
|
|
1053
1081
|
backendNodeId: backendId,
|
|
1054
1082
|
role: role || "node",
|
|
@@ -1728,6 +1756,25 @@ export async function startBrowserBridge(opts) {
|
|
|
1728
1756
|
const value = String(args.value ?? "");
|
|
1729
1757
|
const cdp = await getCdp(rec, page);
|
|
1730
1758
|
const resolved = await cdpRace(cdp.send("DOM.resolveNode", { backendNodeId: binding.backendNodeId }), "DOM.resolveNode");
|
|
1759
|
+
if (args.match_label) {
|
|
1760
|
+
// Fast mode names an option by what the dropdown SHOWS (its text)
|
|
1761
|
+
// or by its value; pick the one that matches, and say so when none
|
|
1762
|
+
// does instead of setting a value the <select> does not have.
|
|
1763
|
+
const r = await cdpRace(cdp.send("Runtime.callFunctionOn", {
|
|
1764
|
+
objectId: resolved.object.objectId,
|
|
1765
|
+
functionDeclaration: "function(v){ var w = String(v).replace(/\\s+/g,' ').trim().toLowerCase(); var opts = this.options ? Array.prototype.slice.call(this.options) : [];" +
|
|
1766
|
+
" var hit = opts.find(function(o){ return String(o.value).trim().toLowerCase() === w; }) ||" +
|
|
1767
|
+
" opts.find(function(o){ return String(o.text).replace(/\\s+/g,' ').trim().toLowerCase() === w; });" +
|
|
1768
|
+
" if (!hit) return null; this.value = hit.value; this.dispatchEvent(new Event('input',{bubbles:true}));" +
|
|
1769
|
+
" this.dispatchEvent(new Event('change',{bubbles:true})); return hit.value; }",
|
|
1770
|
+
arguments: [{ value }],
|
|
1771
|
+
returnByValue: true,
|
|
1772
|
+
}), "Runtime.callFunctionOn");
|
|
1773
|
+
if (r.result?.value === null || r.result?.value === undefined) {
|
|
1774
|
+
throw new BridgeError("act_args_invalid", `the dropdown has no option "${value.slice(0, 80)}"`);
|
|
1775
|
+
}
|
|
1776
|
+
return { selected: String(r.result.value) };
|
|
1777
|
+
}
|
|
1731
1778
|
await cdpRace(cdp.send("Runtime.callFunctionOn", {
|
|
1732
1779
|
objectId: resolved.object.objectId,
|
|
1733
1780
|
functionDeclaration: `function(v){ this.value = v; this.dispatchEvent(new Event('input',{bubbles:true})); this.dispatchEvent(new Event('change',{bubbles:true})); }`,
|
|
@@ -2039,6 +2086,132 @@ export async function startBrowserBridge(opts) {
|
|
|
2039
2086
|
},
|
|
2040
2087
|
};
|
|
2041
2088
|
}
|
|
2089
|
+
// -- Fast mode (browser_fast) on this browser -----------------------------
|
|
2090
|
+
//
|
|
2091
|
+
// The server runs the fast executor (fastRun.ts) and sends this bridge one
|
|
2092
|
+
// command at a time over a pump the bridge itself opens (browserFast.ts).
|
|
2093
|
+
// Every command runs through performAct on THIS run's registration, so the
|
|
2094
|
+
// grant's effect ceiling and origin policy, the takeover pause and the
|
|
2095
|
+
// publish gate apply exactly as for the per-step tools.
|
|
2096
|
+
const FAST_CONTROL_TIMEOUT_MS = 195_000; // browser.py holds the call 200 s; the server answers by 170 s
|
|
2097
|
+
const FAST_PUMP_TIMEOUT_MS = 45_000; // the server long-polls 25 s
|
|
2098
|
+
function fastStable(lease) {
|
|
2099
|
+
let s = fastStableRefs.get(lease);
|
|
2100
|
+
if (!s) {
|
|
2101
|
+
s = new StableRefs();
|
|
2102
|
+
fastStableRefs.set(lease, s);
|
|
2103
|
+
}
|
|
2104
|
+
return s;
|
|
2105
|
+
}
|
|
2106
|
+
function currentBindings(lease) {
|
|
2107
|
+
return [...lease.refs.values()].filter((b) => b.snapshotGeneration === lease.snapshotGeneration && b.documentGeneration === lease.documentGeneration);
|
|
2108
|
+
}
|
|
2109
|
+
function fastOps(reg) {
|
|
2110
|
+
return {
|
|
2111
|
+
state: () => ({
|
|
2112
|
+
cancelled: reg.cancelled,
|
|
2113
|
+
expired: Date.now() / 1000 > reg.spec.grant.exp + 30,
|
|
2114
|
+
paused: isActPaused(reg),
|
|
2115
|
+
}),
|
|
2116
|
+
observe: async () => {
|
|
2117
|
+
const { rec, lease } = await getLease(reg);
|
|
2118
|
+
const snap = await captureSnapshot(rec, lease);
|
|
2119
|
+
const kept = fastSnapItems.get(lease);
|
|
2120
|
+
return {
|
|
2121
|
+
url: snap.url, title: snap.title, generation: snap.generation,
|
|
2122
|
+
documentGeneration: lease.documentGeneration,
|
|
2123
|
+
items: kept && kept.generation === snap.generation ? kept.items : [],
|
|
2124
|
+
};
|
|
2125
|
+
},
|
|
2126
|
+
bindings: async () => {
|
|
2127
|
+
const { lease } = await getLease(reg);
|
|
2128
|
+
return { documentGeneration: lease.documentGeneration, refs: currentBindings(lease) };
|
|
2129
|
+
},
|
|
2130
|
+
act: async (args) => {
|
|
2131
|
+
const result = await performAct(reg, args);
|
|
2132
|
+
if (args.kind !== "get_text") {
|
|
2133
|
+
// The same settle actAndObserve gives an act before the page is read.
|
|
2134
|
+
const { lease } = await getLease(reg);
|
|
2135
|
+
await lease.page.waitForLoadState("domcontentloaded", { timeout: 5_000 }).catch(() => { });
|
|
2136
|
+
await lease.page.waitForTimeout(ACT_SETTLE_MS);
|
|
2137
|
+
}
|
|
2138
|
+
return result;
|
|
2139
|
+
},
|
|
2140
|
+
gateSubmit: async (mode) => {
|
|
2141
|
+
const { lease } = await getLease(reg);
|
|
2142
|
+
// Exactly the gate performAct's press_key Enter raises (same digest),
|
|
2143
|
+
// so the approved card is the one that later lets the Enter through.
|
|
2144
|
+
await enforcePublishGate(reg, lease.page, "press_key", "focused field", "press_key:Enter", mode, "publish");
|
|
2145
|
+
},
|
|
2146
|
+
location: async () => {
|
|
2147
|
+
const { lease } = await getLease(reg);
|
|
2148
|
+
return { url: lease.page.url(), title: (await lease.page.title().catch(() => "")).slice(0, 200) };
|
|
2149
|
+
},
|
|
2150
|
+
wait: async (ms) => {
|
|
2151
|
+
const { lease } = await getLease(reg);
|
|
2152
|
+
await lease.page.waitForTimeout(Math.max(0, Math.min(5_000, ms)));
|
|
2153
|
+
},
|
|
2154
|
+
stableRefs: async () => fastStable((await getLease(reg)).lease),
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
async function handleFast(reg, payload) {
|
|
2158
|
+
const serverBase = String(reg.spec.serverBase || "").replace(/\/$/, "");
|
|
2159
|
+
const authHeader = String(reg.spec.runAuthHeader || "");
|
|
2160
|
+
if (!serverBase || !authHeader) {
|
|
2161
|
+
throw new BridgeError("source_unavailable", "fast mode needs this run's server connection, which it was registered without; use the per-step browser_* tools.");
|
|
2162
|
+
}
|
|
2163
|
+
const mode = clampHitlMode(reg.spec.hitlMode, payload["hitl_mode"]);
|
|
2164
|
+
const control = async (body) => {
|
|
2165
|
+
const { status, json } = await postServerJson(serverBase, "/api/v1/browser/runner/fast", authHeader, body, FAST_CONTROL_TIMEOUT_MS);
|
|
2166
|
+
if (status >= 200 && status < 300 && typeof json.result === "string")
|
|
2167
|
+
return json.result;
|
|
2168
|
+
const err = (json.error && typeof json.error === "object" ? json.error : {});
|
|
2169
|
+
if (status === 404 && !err.code) {
|
|
2170
|
+
throw new BridgeError("fast_unavailable", "this Melaya server does not run fast mode on the runner's browser yet; use the per-step browser_* tools (or browser_batch).");
|
|
2171
|
+
}
|
|
2172
|
+
throw new BridgeError(String(err.code || "fast_failed"), String(err.message || `fast mode failed (HTTP ${status})`).slice(0, 600));
|
|
2173
|
+
};
|
|
2174
|
+
const resumeId = typeof payload["fast_run_id"] === "string" ? String(payload["fast_run_id"]).trim() : "";
|
|
2175
|
+
if (resumeId)
|
|
2176
|
+
return control({ op: "resume", fast_run_id: resumeId, ...(payload["stop"] === true ? { stop: true } : {}) });
|
|
2177
|
+
const { run_id: _r, pipeline_name: _p, hitl_mode: _h, fast_run_id: _f, stop: _s, ...args } = payload;
|
|
2178
|
+
// Steps may quote the @eN of the caller's last browser_get_screen_tree:
|
|
2179
|
+
// turn them into stable refs while that snapshot is still the current one.
|
|
2180
|
+
try {
|
|
2181
|
+
const { lease } = await getLease(reg);
|
|
2182
|
+
const toStable = fastStable(lease).assign(lease.documentGeneration, currentBindings(lease));
|
|
2183
|
+
args.steps = translateStepRefs(args.steps, (r) => toStable.get(r) ?? null);
|
|
2184
|
+
}
|
|
2185
|
+
catch { /* no session yet: the run's own first read decides */ }
|
|
2186
|
+
const fastId = randomBytes(18).toString("base64url");
|
|
2187
|
+
const ops = fastOps(reg);
|
|
2188
|
+
void runFastPump({
|
|
2189
|
+
fastId,
|
|
2190
|
+
post: async (body) => {
|
|
2191
|
+
const { status, json } = await postServerJson(serverBase, "/api/v1/browser/runner/fast/pump", authHeader, body, FAST_PUMP_TIMEOUT_MS);
|
|
2192
|
+
// Refused or unknown: nothing more will come for this run.
|
|
2193
|
+
if (status === 401 || status === 403 || status === 404 || status === 410)
|
|
2194
|
+
return { end: true };
|
|
2195
|
+
if (status < 200 || status >= 300)
|
|
2196
|
+
throw new Error(`pump HTTP ${status}`);
|
|
2197
|
+
return json;
|
|
2198
|
+
},
|
|
2199
|
+
exec: (cmd) => execFastCommand(ops, cmd, mode),
|
|
2200
|
+
isCancelled: () => reg.cancelled,
|
|
2201
|
+
log,
|
|
2202
|
+
}).catch((e) => log(`[fast] pump stopped: ${e?.message || e}`));
|
|
2203
|
+
const grant = reg.spec.grant;
|
|
2204
|
+
return control({
|
|
2205
|
+
op: "start",
|
|
2206
|
+
fast_id: fastId,
|
|
2207
|
+
run_id: reg.spec.runId,
|
|
2208
|
+
session_id: grant.browserSession,
|
|
2209
|
+
jti: grant.jti,
|
|
2210
|
+
target_ref: grant.target.ref,
|
|
2211
|
+
hitl_mode: mode,
|
|
2212
|
+
args,
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2042
2215
|
// Post an (unchanged) heartbeat frame at least this often so the viewer's
|
|
2043
2216
|
// frame watchdog never trips on a STATIC page. Without it, a page that is not
|
|
2044
2217
|
// animating (the agent is reasoning, or the page is idle) produced ZERO frames
|
|
@@ -2330,6 +2503,11 @@ export async function startBrowserBridge(opts) {
|
|
|
2330
2503
|
const out = await actAndObserve(reg, payload);
|
|
2331
2504
|
return respond(200, out);
|
|
2332
2505
|
}
|
|
2506
|
+
case "/browser/fast": {
|
|
2507
|
+
// browser_fast (browser.py): the server's fast executor drives this
|
|
2508
|
+
// run's browser through a pump the bridge opens (handleFast).
|
|
2509
|
+
return respond(200, { ok: true, result: await handleFast(reg, payload) });
|
|
2510
|
+
}
|
|
2333
2511
|
case "/browser/run": {
|
|
2334
2512
|
if (!reg.spec.codeMode) {
|
|
2335
2513
|
return fail(403, "code_mode_disabled", "code mode is not enabled for this run (Phase 2 capability)");
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export declare const STABLE_REF_PREFIX = "r";
|
|
2
|
+
export interface RefBinding {
|
|
3
|
+
ref: string;
|
|
4
|
+
frameKey: string;
|
|
5
|
+
backendNodeId: number;
|
|
6
|
+
role: string;
|
|
7
|
+
name: string;
|
|
8
|
+
snapshotGeneration?: number;
|
|
9
|
+
documentGeneration?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class StableRefs {
|
|
12
|
+
private documentGeneration;
|
|
13
|
+
private next;
|
|
14
|
+
private byKey;
|
|
15
|
+
private byNum;
|
|
16
|
+
private static keyOf;
|
|
17
|
+
private sync;
|
|
18
|
+
/** Stable ref for every binding of the current snapshot (snapshot ref ->
|
|
19
|
+
* stable ref). Records each node's latest role and label. */
|
|
20
|
+
assign(documentGeneration: number, bindings: Iterable<RefBinding>): Map<string, string>;
|
|
21
|
+
/** Stable ref of a binding already numbered (no new number). */
|
|
22
|
+
stableOf(documentGeneration: number, b: {
|
|
23
|
+
frameKey: string;
|
|
24
|
+
backendNodeId: number;
|
|
25
|
+
}): string | null;
|
|
26
|
+
/** The current snapshot's ref for a stable ref, or null when the element is
|
|
27
|
+
* gone, the document changed, or the node no longer carries the role and
|
|
28
|
+
* label it was read with. Never a guess. */
|
|
29
|
+
resolve(stableRef: string, documentGeneration: number, current: Iterable<RefBinding>): string | null;
|
|
30
|
+
}
|
|
31
|
+
export declare function isStableRef(ref: unknown): boolean;
|
|
32
|
+
/** One rendered node of the bridge's last snapshot (captureSnapshot keeps
|
|
33
|
+
* them per lease for this module). */
|
|
34
|
+
export interface SnapItem extends RefBinding {
|
|
35
|
+
aria: string;
|
|
36
|
+
domId: string;
|
|
37
|
+
href: string;
|
|
38
|
+
click: boolean;
|
|
39
|
+
edit: boolean;
|
|
40
|
+
covered: boolean;
|
|
41
|
+
l: number;
|
|
42
|
+
t: number;
|
|
43
|
+
r: number;
|
|
44
|
+
b: number;
|
|
45
|
+
/** Editable fields only, never a secret field's. */
|
|
46
|
+
value?: string;
|
|
47
|
+
}
|
|
48
|
+
/** The screen in the line format the server's parseBrowserTree reads:
|
|
49
|
+
* `r@e12 <role> [click] [edit] [COVERED] "name" ="value" ~aria #id @href <l,t,r,b>`.
|
|
50
|
+
* A COVERED element (something else is painted over its centre) is not
|
|
51
|
+
* offered as clickable: a click there lands on whatever covers it. */
|
|
52
|
+
export declare function renderFastLines(items: SnapItem[], toStable: Map<string, string>): string[];
|
|
53
|
+
/** The LLM usually read the page with browser_get_screen_tree before planning,
|
|
54
|
+
* so its steps may quote that snapshot's @eN (a target's `ref`, a list's
|
|
55
|
+
* `in`). The run's first read takes a new snapshot, which would orphan them:
|
|
56
|
+
* translate them to stable refs now, while that snapshot is still current.
|
|
57
|
+
* A ref that no longer resolves is dropped; the step falls back to its label. */
|
|
58
|
+
export declare function translateStepRefs(steps: unknown, toStable: (snapshotRef: string) => string | null): unknown;
|
|
59
|
+
/** The run's autonomy: the registered (server-clamped) mode, which a caller
|
|
60
|
+
* holding the bridge token may TIGHTEN but never loosen (same rule as the
|
|
61
|
+
* bridge's publish gate). No registered mode: the caller's, default safe. */
|
|
62
|
+
export declare function clampHitlMode(specMode: unknown, bodyMode: unknown): "safe" | "payments_only" | "autonomous";
|
|
63
|
+
export interface FastObservation {
|
|
64
|
+
url: string;
|
|
65
|
+
title: string;
|
|
66
|
+
generation: number;
|
|
67
|
+
documentGeneration: number;
|
|
68
|
+
items: SnapItem[];
|
|
69
|
+
}
|
|
70
|
+
/** What the bridge gives a fast session. Every write goes through act, which
|
|
71
|
+
* is the bridge's performAct for this run's registration. */
|
|
72
|
+
export interface FastBridgeOps {
|
|
73
|
+
/** The run is torn down, its grant expired, or the user took over. */
|
|
74
|
+
state(): {
|
|
75
|
+
cancelled: boolean;
|
|
76
|
+
expired: boolean;
|
|
77
|
+
paused: boolean;
|
|
78
|
+
};
|
|
79
|
+
/** Take a snapshot (the bridge binds its @eN refs) and return its nodes. */
|
|
80
|
+
observe(): Promise<FastObservation>;
|
|
81
|
+
/** The current snapshot's bindings, for resolving a stable ref. */
|
|
82
|
+
bindings(): Promise<{
|
|
83
|
+
documentGeneration: number;
|
|
84
|
+
refs: RefBinding[];
|
|
85
|
+
}>;
|
|
86
|
+
/** One act through performAct. Throws {code, message} on refusal. */
|
|
87
|
+
act(args: Record<string, unknown>): Promise<unknown>;
|
|
88
|
+
/** Stage (or correlate) the approval a submitting Enter needs, BEFORE
|
|
89
|
+
* anything is typed. Throws approval_required while it is pending. */
|
|
90
|
+
gateSubmit(mode: string): Promise<void>;
|
|
91
|
+
/** URL and title of the controlled tab. */
|
|
92
|
+
location(): Promise<{
|
|
93
|
+
url: string;
|
|
94
|
+
title: string;
|
|
95
|
+
}>;
|
|
96
|
+
wait(ms: number): Promise<void>;
|
|
97
|
+
/** Stable refs of this lease (kept across fast runs, per document). */
|
|
98
|
+
stableRefs(): Promise<StableRefs>;
|
|
99
|
+
}
|
|
100
|
+
export interface FastCommand {
|
|
101
|
+
seq: number;
|
|
102
|
+
kind: string;
|
|
103
|
+
body: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
export type FastOutcome = {
|
|
106
|
+
ok: true;
|
|
107
|
+
data: Record<string, unknown>;
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
code: string;
|
|
111
|
+
message: string;
|
|
112
|
+
};
|
|
113
|
+
export declare class FastCommandError extends Error {
|
|
114
|
+
code: string;
|
|
115
|
+
constructor(code: string, message: string);
|
|
116
|
+
}
|
|
117
|
+
/** Run one command the server's executor sent, under this run's grant and
|
|
118
|
+
* autonomy (`mode`, clamped on this side: the server cannot loosen it). */
|
|
119
|
+
export declare function execFastCommand(ops: FastBridgeOps, cmd: FastCommand, mode: string): Promise<FastOutcome>;
|
|
120
|
+
export interface PumpReply {
|
|
121
|
+
command?: FastCommand;
|
|
122
|
+
end?: boolean;
|
|
123
|
+
idle?: boolean;
|
|
124
|
+
}
|
|
125
|
+
/** Pull the server executor's commands and run them, one at a time, until the
|
|
126
|
+
* server ends the run or this run is torn down. A command is executed once:
|
|
127
|
+
* its outcome is carried by the next poll, resent unchanged if that poll
|
|
128
|
+
* fails, and the server ignores an outcome it already has. Nothing is ever
|
|
129
|
+
* re-run here. */
|
|
130
|
+
export declare function runFastPump(o: {
|
|
131
|
+
fastId: string;
|
|
132
|
+
post: (body: Record<string, unknown>) => Promise<PumpReply>;
|
|
133
|
+
exec: (cmd: FastCommand) => Promise<FastOutcome>;
|
|
134
|
+
isCancelled: () => boolean;
|
|
135
|
+
log?: (msg: string) => void;
|
|
136
|
+
/** Consecutive failed polls before giving up (the server's own command
|
|
137
|
+
* deadline then reports the step as unknown). */
|
|
138
|
+
maxFailures?: number;
|
|
139
|
+
sleep?: (ms: number) => Promise<void>;
|
|
140
|
+
}): Promise<void>;
|
|
141
|
+
export declare function postServerJson(serverBase: string, path: string, authHeader: string, body: Record<string, unknown>, timeoutMs: number): Promise<{
|
|
142
|
+
status: number;
|
|
143
|
+
json: Record<string, unknown>;
|
|
144
|
+
}>;
|