agents 0.30.1 → 0.32.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/README.md +2 -1
- package/dist/{agent-routing-CDbpVjYq.d.ts → agent-routing-vVMA7m8Y.d.ts} +69 -608
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-C8DH8Yii.d.ts → agent-tools-gGXXkf1z.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/agent-tools.js +0 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/{ai-iXw0uOGj.d.ts → ai-CDn3Abax.d.ts} +30 -4
- package/dist/{ai-sdk-DRPh2rRa.js → ai-sdk-Kon40i0q.js} +2 -36
- package/dist/ai-sdk-Kon40i0q.js.map +1 -0
- package/dist/browser/ai.d.ts +1 -1
- package/dist/browser/ai.js +16 -3
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/index.d.ts +64 -16
- package/dist/browser/index.js +4 -4
- package/dist/browser/tools/ai-sdk.d.ts +7 -5
- package/dist/browser/tools/ai-sdk.js +4 -4
- package/dist/browser/tools/ai-sdk.js.map +1 -1
- package/dist/browser/tools/pi.d.ts +9 -15
- package/dist/browser/tools/pi.js +6 -14
- package/dist/browser/tools/pi.js.map +1 -1
- package/dist/browser/tools/tanstack-ai.d.ts +48 -25
- package/dist/browser/tools/tanstack-ai.js +8 -4
- package/dist/browser/tools/tanstack-ai.js.map +1 -1
- package/dist/{browser-DccPw_wW.js → browser-1eUbPvC2.js} +413 -14
- package/dist/browser-1eUbPvC2.js.map +1 -0
- package/dist/{browser-DlXhSAdf.d.ts → browser-D9RNpJuT.d.ts} +254 -20
- package/dist/browser-tool-BhjCcNVE.d.ts +71 -0
- package/dist/browser-tool-DRan7Lk6.js +497 -0
- package/dist/browser-tool-DRan7Lk6.js.map +1 -0
- package/dist/capability-id-B3J8Nok_.js +13 -0
- package/dist/capability-id-B3J8Nok_.js.map +1 -0
- package/dist/catalog-BpOejOsE.js +239 -0
- package/dist/catalog-BpOejOsE.js.map +1 -0
- package/dist/catalog-BpiVV1OF.d.ts +173 -0
- package/dist/chat/index.d.ts +2 -2
- package/dist/chat-sdk/index.d.ts +3 -3
- package/dist/chat-sdk/index.js +1 -1
- package/dist/client-4OlMIJx-.js.map +1 -1
- package/dist/{client-BnYbNT3z.js → client-DMWujQJ1.js} +68 -244
- package/dist/client-DMWujQJ1.js.map +1 -0
- package/dist/{client-C27CtRZr.d.ts → client-eSfgYvd3.d.ts} +9 -7
- package/dist/client.d.ts +1 -1
- package/dist/{cloudflare-Dzvc7V2N.js → cloudflare-CUUm_9q9.js} +26 -4
- package/dist/cloudflare-CUUm_9q9.js.map +1 -0
- package/dist/{connector-DAXBOIIi.js → connector-CM-uuxKM.js} +96 -8
- package/dist/connector-CM-uuxKM.js.map +1 -0
- package/dist/{connector-20DJ6p4k.d.ts → connector-DZ71EjE-.d.ts} +109 -9
- package/dist/{do-oauth-client-provider-DfsUcpc3.d.ts → do-oauth-client-provider-DtNqv7oj.d.ts} +26 -2
- package/dist/{index-YSKgfgg9.d.ts → emitter-BJZtCVjQ.d.ts} +6 -140
- package/dist/emitter-DPOVcCln.js +15 -0
- package/dist/emitter-DPOVcCln.js.map +1 -0
- package/dist/experimental/channels/web/index.js +1 -1
- package/dist/experimental/webmcp.js +1 -1
- package/dist/{handler-stateless-Bly_nSH3.js → handler-stateless-B5hU3dxK.js} +35 -2
- package/dist/handler-stateless-B5hU3dxK.js.map +1 -0
- package/dist/{handler-stateless-DxYpJ_XF.d.ts → handler-stateless-C90kD5_R.d.ts} +3 -47
- package/dist/harness/container/index.d.ts +15 -15
- package/dist/harness/container/index.js +0 -1
- package/dist/harness/container/index.js.map +1 -1
- package/dist/harness/container/runtime/index.js +2 -2
- package/dist/harness/opencode/index.js +0 -1
- package/dist/harness/opencode/index.js.map +1 -1
- package/dist/harness/pi/index.d.ts +15 -7
- package/dist/harness/pi/index.js +74 -151
- package/dist/harness/pi/index.js.map +1 -1
- package/dist/harness/think/index.d.ts +2 -1
- package/dist/{images-xaDv2Fqe.js → images-CfuRCjwq.js} +1 -5
- package/dist/images-CfuRCjwq.js.map +1 -0
- package/dist/{index-DCxk3OSJ.d.ts → index-8k4qsX1T.d.ts} +2 -28
- package/dist/index.d.ts +15 -16
- package/dist/index.js +3 -3
- package/dist/internal-hosts-Kfp5A392.d.ts +230 -0
- package/dist/internal-kmw70xwR.js.map +1 -1
- package/dist/invoker-BC1SZ3q1.js +26 -0
- package/dist/invoker-BC1SZ3q1.js.map +1 -0
- package/dist/invoker-DoLFPJZV.d.ts +21 -0
- package/dist/markdown-BZ-VLtA1.js +86 -0
- package/dist/markdown-BZ-VLtA1.js.map +1 -0
- package/dist/mcp/client/do-oauth-client-provider.d.ts +4 -2
- package/dist/mcp/client/do-oauth-client-provider.js +15 -1
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/client/index.d.ts +15 -1
- package/dist/mcp/client/index.js +2 -2
- package/dist/mcp/client/x402.d.ts +7 -5
- package/dist/mcp/client/x402.js +34 -1
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +561 -27
- package/dist/mcp/index.js +180 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server/index.d.ts +1 -1
- package/dist/mcp/server/index.js +1 -1
- package/dist/models/ai-sdk/index.d.ts +2 -2
- package/dist/models/ai-sdk/index.js +1 -1
- package/dist/models/opencode/index.d.ts +2 -2
- package/dist/models/opencode/index.js +1 -1
- package/dist/models/pi-ai/index.d.ts +3 -28
- package/dist/models/pi-ai/index.js +160 -18
- package/dist/models/pi-ai/index.js.map +1 -1
- package/dist/observability/ai/index.js +1 -2
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/observability/index.d.ts +170 -9
- package/dist/observability/index.js +386 -11
- package/dist/observability/index.js.map +1 -1
- package/dist/protocol-BEgrJYC5.d.ts +29 -29
- package/dist/react.d.ts +2 -2
- package/dist/resource-B59OV2a2.js +325 -0
- package/dist/resource-B59OV2a2.js.map +1 -0
- package/dist/routing/index.d.ts +1 -1
- package/dist/script-CFDLTk5b.d.ts +91 -0
- package/dist/script-workspace-BpdAYq1R.d.ts +41 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/{session-store-yvwq1f9x.js → session-7CDgVE0z.js} +455 -33
- package/dist/session-7CDgVE0z.js.map +1 -0
- package/dist/{session-store-_IK6dV-w.d.ts → session-store-B7Ca-u1t.d.ts} +8 -1
- package/dist/settings-Dk2il2Hf.js.map +1 -1
- package/dist/skill-DRBcIBjC.d.ts +221 -0
- package/dist/skills/index.d.ts +275 -36
- package/dist/skills/index.js +265 -1534
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/runner.d.ts +37 -0
- package/dist/skills/runner.js +912 -0
- package/dist/skills/runner.js.map +1 -0
- package/dist/skills/tools/ai-sdk.d.ts +36 -0
- package/dist/skills/tools/ai-sdk.js +94 -0
- package/dist/skills/tools/ai-sdk.js.map +1 -0
- package/dist/source-9RQzlEhM.d.ts +112 -0
- package/dist/{src-DfLzdwlK.js → src-DoGNJ4Ud.js} +21 -6
- package/dist/src-DoGNJ4Ud.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tool-CTp72Jb_.d.ts +10 -10
- package/dist/tool-CxKsvxjm.js.map +1 -1
- package/dist/tool-DkT_DJBm.js.map +1 -1
- package/dist/tool-DoVberpN.d.ts +22 -22
- package/dist/tool-helpers-BI7ifO2S.js +255 -0
- package/dist/tool-helpers-BI7ifO2S.js.map +1 -0
- package/dist/{transport-CPh7YGiw.d.ts → transport-5moD2pW5.d.ts} +1 -43
- package/dist/types-D2naO3rA.d.ts +49 -0
- package/dist/vite.d.ts +2 -2
- package/dist/vite.js +10 -258
- package/dist/vite.js.map +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-CyoeosdO.js → websockets-C6SJtWCJ.js} +1 -2
- package/dist/{websockets-CyoeosdO.js.map → websockets-C6SJtWCJ.js.map} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +0 -1
- package/dist/workflows.js.map +1 -1
- package/docs/browse-the-web.md +247 -413
- package/docs/harnesses/pi.md +36 -30
- package/docs/index.md +4 -4
- package/docs/mcp-client.md +25 -1
- package/docs/mcp-servers.md +26 -7
- package/docs/models-pi-ai.md +9 -11
- package/docs/models.md +1 -8
- package/docs/observability.md +5 -3
- package/docs/securing-mcp-servers.md +113 -302
- package/package.json +31 -17
- package/dist/ai-sdk-DRPh2rRa.js.map +0 -1
- package/dist/browser-DccPw_wW.js.map +0 -1
- package/dist/browser-tool-B1mhifin.js +0 -616
- package/dist/browser-tool-B1mhifin.js.map +0 -1
- package/dist/browser-tool-Cxjk5sgG.d.ts +0 -92
- package/dist/client-BnYbNT3z.js.map +0 -1
- package/dist/cloudflare-Dzvc7V2N.js.map +0 -1
- package/dist/connector-DAXBOIIi.js.map +0 -1
- package/dist/handler-stateless-Bly_nSH3.js.map +0 -1
- package/dist/images-xaDv2Fqe.js.map +0 -1
- package/dist/index-iv9FTXX9.d.ts +0 -311
- package/dist/invoker-CG0_p_Wq.d.ts +0 -20
- package/dist/invoker-CHMnoxIA.js +0 -57
- package/dist/invoker-CHMnoxIA.js.map +0 -1
- package/dist/session-store-yvwq1f9x.js.map +0 -1
- package/dist/skills/compile.d.ts +0 -40
- package/dist/skills/compile.js +0 -65
- package/dist/skills/compile.js.map +0 -1
- package/dist/src-DfLzdwlK.js.map +0 -1
- package/dist/tool-helpers-O5BDVEAY.js +0 -58
- package/dist/tool-helpers-O5BDVEAY.js.map +0 -1
- package/skills-module.d.ts +0 -22
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { f as SkillResourceKind, n as Skill } from "./skill-DRBcIBjC.js";
|
|
2
|
+
|
|
3
|
+
//#region src/skills/script.d.ts
|
|
4
|
+
/** A request to run one bundled skill script. */
|
|
5
|
+
interface SkillScriptRequest {
|
|
6
|
+
/** The skill that owns the script. */
|
|
7
|
+
readonly skill: Skill;
|
|
8
|
+
/** Script path relative to the skill, under `scripts/`. */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/** Script source. */
|
|
11
|
+
readonly source: string;
|
|
12
|
+
/** JSON input from the model. */
|
|
13
|
+
readonly input: unknown;
|
|
14
|
+
/** Other files bundled with the skill, mounted for the script. */
|
|
15
|
+
readonly resources?: readonly SkillScriptFile[];
|
|
16
|
+
}
|
|
17
|
+
/** A bundled file made available to a running script. */
|
|
18
|
+
interface SkillScriptFile {
|
|
19
|
+
/** Path relative to the skill directory. */
|
|
20
|
+
readonly path: string;
|
|
21
|
+
/** UTF-8 text, or base64 when `encoding` is `"base64"`. */
|
|
22
|
+
readonly content: string;
|
|
23
|
+
/** Defaults to `"text"`. */
|
|
24
|
+
readonly encoding?: "text" | "base64";
|
|
25
|
+
/** Role of the file in the skill. */
|
|
26
|
+
readonly kind?: SkillResourceKind;
|
|
27
|
+
}
|
|
28
|
+
/** Runs skill scripts. Enables Think's `run_skill_script` tool. */
|
|
29
|
+
interface SkillScriptRunner {
|
|
30
|
+
/**
|
|
31
|
+
* Run a script.
|
|
32
|
+
*
|
|
33
|
+
* @param request - The script and its skill.
|
|
34
|
+
* @returns The script's result.
|
|
35
|
+
* @throws When the script fails; the tool reports the message to the model.
|
|
36
|
+
*/
|
|
37
|
+
run(request: SkillScriptRequest): Promise<unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** Skill metadata passed to running scripts. */
|
|
40
|
+
interface SkillScriptContext {
|
|
41
|
+
/** The skill that owns the script. */
|
|
42
|
+
readonly skill: {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly description: string;
|
|
45
|
+
readonly license?: string;
|
|
46
|
+
readonly compatibility?: string;
|
|
47
|
+
readonly metadata: Readonly<Record<string, string>>;
|
|
48
|
+
readonly allowedTools: readonly string[];
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The `ctx` object passed as the second argument to function-style JS
|
|
53
|
+
* skill scripts (`export default async function run(input, ctx)`).
|
|
54
|
+
*
|
|
55
|
+
* Capabilities are gated by the runner: `workspace` throws unless workspace
|
|
56
|
+
* access is enabled, and `tools` only resolves tools the runner was given.
|
|
57
|
+
*/
|
|
58
|
+
interface SkillRunContext {
|
|
59
|
+
/** Metadata for the skill that owns this script. */
|
|
60
|
+
skill: SkillScriptContext["skill"];
|
|
61
|
+
/** Text bundled resources by relative path (e.g. `references/style-guide.md`). */
|
|
62
|
+
files: Record<string, string>;
|
|
63
|
+
/** Workspace access, gated by the runner's `workspace` permission. */
|
|
64
|
+
workspace: {
|
|
65
|
+
readFile(path: string): Promise<string | null>;
|
|
66
|
+
listFiles(path?: string): Promise<unknown>;
|
|
67
|
+
glob(pattern: string): Promise<unknown>;
|
|
68
|
+
stat(path: string): Promise<{
|
|
69
|
+
type: string;
|
|
70
|
+
size: number;
|
|
71
|
+
} | null>;
|
|
72
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
73
|
+
};
|
|
74
|
+
/** Explicitly granted tools: `tools.call(name, input)` or `tools.<name>(input)`. */
|
|
75
|
+
tools: {
|
|
76
|
+
call(name: string, input?: unknown): Promise<unknown>;
|
|
77
|
+
} & Record<string, (input?: unknown) => Promise<unknown>>;
|
|
78
|
+
/** Scratch artifacts returned to the model as `outputFiles`. */
|
|
79
|
+
output: {
|
|
80
|
+
writeFile(name: string, content: string): Promise<void>;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export {
|
|
85
|
+
SkillScriptRunner as a,
|
|
86
|
+
SkillScriptRequest as i,
|
|
87
|
+
SkillScriptContext as n,
|
|
88
|
+
SkillScriptFile as r,
|
|
89
|
+
SkillRunContext as t
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=script-CFDLTk5b.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/skills/script-workspace.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The Workspace a skill script reaches through `ctx.workspace`: a
|
|
4
|
+
* `Workspace` from `@cloudflare/computer`, or anything with the same `fs`
|
|
5
|
+
* methods.
|
|
6
|
+
*/
|
|
7
|
+
interface SkillScriptWorkspace {
|
|
8
|
+
readonly fs: {
|
|
9
|
+
stat(path: string): Promise<{
|
|
10
|
+
size: number;
|
|
11
|
+
isDirectory: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
readFile(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
14
|
+
writeFile(path: string, content: Uint8Array): Promise<void>;
|
|
15
|
+
mkdir(
|
|
16
|
+
path: string,
|
|
17
|
+
options?: {
|
|
18
|
+
recursive?: boolean;
|
|
19
|
+
}
|
|
20
|
+
): Promise<void>;
|
|
21
|
+
readdir(path: string): Promise<
|
|
22
|
+
Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
size: number;
|
|
25
|
+
isDirectory: boolean;
|
|
26
|
+
}>
|
|
27
|
+
>;
|
|
28
|
+
find(
|
|
29
|
+
directory: string,
|
|
30
|
+
pattern?: string
|
|
31
|
+
): Promise<
|
|
32
|
+
Array<{
|
|
33
|
+
path: string;
|
|
34
|
+
type: "file" | "dir";
|
|
35
|
+
}>
|
|
36
|
+
>;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { SkillScriptWorkspace as t };
|
|
41
|
+
//# sourceMappingURL=script-workspace-BpdAYq1R.d.ts.map
|
package/dist/serializable.d.ts
CHANGED
|
@@ -303,19 +303,29 @@ function isUnimplementedBindingMethod(error, method) {
|
|
|
303
303
|
* `acquire()`, as in fully local dev.
|
|
304
304
|
*/
|
|
305
305
|
async function acquireBrowserSession(browser, options = {}) {
|
|
306
|
+
const { outboundByHost, ...httpOptions } = options;
|
|
306
307
|
if (browser.acquire) try {
|
|
307
308
|
const { sessionId } = await browser.acquire({
|
|
308
309
|
keepAlive: options.keepAliveMs,
|
|
309
310
|
recording: options.recording,
|
|
310
|
-
guardrails: options.guardrails
|
|
311
|
+
guardrails: options.guardrails,
|
|
312
|
+
...outboundByHost ? { outboundByHost } : {}
|
|
311
313
|
});
|
|
312
314
|
return { sessionId };
|
|
313
315
|
} catch (error) {
|
|
314
316
|
if (!isUnimplementedBindingMethod(error, "acquire")) throw error;
|
|
315
317
|
}
|
|
316
|
-
|
|
318
|
+
if (outboundByHost) warnInternalHostsUnavailable(Object.keys(outboundByHost));
|
|
319
|
+
const { sessionId } = await createBrowserSession(browser, httpOptions);
|
|
317
320
|
return { sessionId };
|
|
318
321
|
}
|
|
322
|
+
let warnedInternalHostsUnavailable = false;
|
|
323
|
+
/** Say once per isolate that internal hostnames need a deployed Worker. */
|
|
324
|
+
function warnInternalHostsUnavailable(hosts) {
|
|
325
|
+
if (warnedInternalHostsUnavailable) return;
|
|
326
|
+
warnedInternalHostsUnavailable = true;
|
|
327
|
+
console.warn(`[agents/browser] Internal hostnames (${hosts.join(", ")}) only work once deployed: this Browser Run binding has no acquire(), as in local dev, so the browser starts without them.`);
|
|
328
|
+
}
|
|
319
329
|
/**
|
|
320
330
|
* The HTTP status in a typed binding method's error. Browser Run's binding
|
|
321
331
|
* errors carry it only in the message, e.g. `Not Found (status=404)`.
|
|
@@ -409,35 +419,6 @@ async function connectBrowserSession(browser, sessionId, options) {
|
|
|
409
419
|
});
|
|
410
420
|
}
|
|
411
421
|
//#endregion
|
|
412
|
-
//#region src/browser/connector-validation.ts
|
|
413
|
-
function formatToolValidationError(connector, tool, errors) {
|
|
414
|
-
const specific = errors.filter((error) => error.keyword !== "properties");
|
|
415
|
-
return `Invalid arguments for ${connector}.${tool}${(specific.length > 0 ? specific : errors).map((error) => {
|
|
416
|
-
const location = error.instanceLocation.replace(/^#\/?/, "").replaceAll("~1", "/").replaceAll("~0", "~").replaceAll("/", ".");
|
|
417
|
-
return `${location ? ` at ${location}` : ""}: ${error.error}`;
|
|
418
|
-
}).join(";")}`;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Validate a connector tool's arguments against its input schema before it
|
|
422
|
-
* runs, so the model gets a precise error instead of a failure deep inside
|
|
423
|
-
* browser work. An omitted argument counts as `{}`: argumentless tools are
|
|
424
|
-
* documented as `cdp.spec()`.
|
|
425
|
-
*/
|
|
426
|
-
function validateConnectorArgs(connector, name, tool) {
|
|
427
|
-
if (!tool.inputSchema) return tool;
|
|
428
|
-
const schema = tool.inputSchema;
|
|
429
|
-
const validator = new Validator(schema, "7", false);
|
|
430
|
-
return {
|
|
431
|
-
...tool,
|
|
432
|
-
execute: async (args, ctx) => {
|
|
433
|
-
const input = args === void 0 ? {} : args;
|
|
434
|
-
const result = validator.validate(input);
|
|
435
|
-
if (!result.valid) throw new Error(formatToolValidationError(connector, name, result.errors));
|
|
436
|
-
return await tool.execute(input, ctx);
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
//#endregion
|
|
441
422
|
//#region src/browser/live-view.ts
|
|
442
423
|
/**
|
|
443
424
|
* Browser Run mints `devtoolsFrontendUrl`s (the Live View links) that are
|
|
@@ -654,6 +635,447 @@ var _queues = { _: /* @__PURE__ */ new WeakMap() };
|
|
|
654
635
|
*/
|
|
655
636
|
const DEFAULT_SWEEP_IDLE_MS = 600 * 1e3;
|
|
656
637
|
//#endregion
|
|
657
|
-
|
|
638
|
+
//#region src/browser/session.ts
|
|
639
|
+
/**
|
|
640
|
+
* `BrowserSession`: one run's connection to a persistent `Browser`, with the
|
|
641
|
+
* rules that make CDP usable from model-written code.
|
|
642
|
+
*
|
|
643
|
+
* The model never manages CDP sessions. `sessionId: "active"` addresses the
|
|
644
|
+
* tab the scope last worked in, `attachToTarget` returns a handle that keeps
|
|
645
|
+
* working in later runs, and common protocol mistakes come back as
|
|
646
|
+
* instructions. When the session closes it saves the scope's active tab and
|
|
647
|
+
* reports what happened to the browser: a restart, a tab closed by someone
|
|
648
|
+
* else, a tab shared with another scope, tabs the page opened.
|
|
649
|
+
*
|
|
650
|
+
* `browserTool` opens one per run.
|
|
651
|
+
*/
|
|
652
|
+
/** `sessionId` value that addresses the tab the agent is working in. */
|
|
653
|
+
const ACTIVE_PAGE_SESSION = "active";
|
|
654
|
+
/**
|
|
655
|
+
* `attachToTarget` returns `{ sessionId: "target:<targetId>" }`, a stable
|
|
656
|
+
* handle rather than a connection-scoped CDP session id, so it keeps working
|
|
657
|
+
* in later runs. `send` resolves it on the live socket.
|
|
658
|
+
*/
|
|
659
|
+
const ATTACH_HANDLE_PREFIX = "target:";
|
|
660
|
+
/** Chrome's JSON-RPC code for a CDP session id it doesn't know. */
|
|
661
|
+
const CDP_SESSION_NOT_FOUND = -32001;
|
|
662
|
+
/**
|
|
663
|
+
* Screenshots are kept whole in the chat message for the UI, and a stored
|
|
664
|
+
* message must fit SQLite's 2 MB row (see `ROW_MAX_BYTES` in chat/sanitize.ts),
|
|
665
|
+
* or the image is compacted away. Failing early lets the model retake it
|
|
666
|
+
* smaller. Other results are cut to a preview before they're stored.
|
|
667
|
+
*
|
|
668
|
+
* @internal The tools also refuse to keep a returned screenshot over it.
|
|
669
|
+
*/
|
|
670
|
+
const DEFAULT_MAX_RESULT_BYTES = 1e6;
|
|
671
|
+
/** `#liveParams`: nothing to detach. */
|
|
672
|
+
const DETACHED = Symbol("detached");
|
|
673
|
+
const MANAGED_BROWSER = "this browser is managed for you and persists between runs. To close a tab, send Target.closeTarget with its targetId.";
|
|
674
|
+
const HOST_ONLY = "it is for the host, not your code.";
|
|
675
|
+
const NO_TUNNELING = "send each command with browser.cdp instead, passing sessionId to reach a tab.";
|
|
676
|
+
/**
|
|
677
|
+
* Commands model code can't send, and why.
|
|
678
|
+
*
|
|
679
|
+
* - Ending the browser: the host owns its lifetime, so the model can't close
|
|
680
|
+
* it out from under the agent (and lose its logins).
|
|
681
|
+
* - Live View and handoff: a Live View link controls the whole browser, so it
|
|
682
|
+
* goes to a trusted person through `Browser.liveView()`, never to the model.
|
|
683
|
+
* - Tunnels: these carry raw protocol messages past this check.
|
|
684
|
+
*/
|
|
685
|
+
const REFUSED_COMMANDS = /* @__PURE__ */ new Map([
|
|
686
|
+
["Browser.close", MANAGED_BROWSER],
|
|
687
|
+
["Browser.crash", MANAGED_BROWSER],
|
|
688
|
+
["Browser.crashGpuProcess", MANAGED_BROWSER],
|
|
689
|
+
["Cloudflare.getLiveView", HOST_ONLY],
|
|
690
|
+
["Cloudflare.handoff", HOST_ONLY],
|
|
691
|
+
["Target.sendMessageToTarget", NO_TUNNELING],
|
|
692
|
+
["Target.exposeDevToolsProtocol", NO_TUNNELING]
|
|
693
|
+
]);
|
|
694
|
+
const SEND_SCHEMA = {
|
|
695
|
+
type: "object",
|
|
696
|
+
properties: {
|
|
697
|
+
method: {
|
|
698
|
+
type: "string",
|
|
699
|
+
minLength: 1
|
|
700
|
+
},
|
|
701
|
+
params: { type: "object" },
|
|
702
|
+
sessionId: { type: "string" },
|
|
703
|
+
timeoutMs: {
|
|
704
|
+
type: "number",
|
|
705
|
+
exclusiveMinimum: 0
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
required: ["method"]
|
|
709
|
+
};
|
|
710
|
+
const ATTACH_SCHEMA = {
|
|
711
|
+
type: "object",
|
|
712
|
+
properties: {
|
|
713
|
+
targetId: {
|
|
714
|
+
type: "string",
|
|
715
|
+
minLength: 1
|
|
716
|
+
},
|
|
717
|
+
timeoutMs: {
|
|
718
|
+
type: "number",
|
|
719
|
+
exclusiveMinimum: 0
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
required: ["targetId"]
|
|
723
|
+
};
|
|
724
|
+
const DEBUG_LOG_SCHEMA = {
|
|
725
|
+
type: "object",
|
|
726
|
+
properties: { limit: {
|
|
727
|
+
type: "number",
|
|
728
|
+
minimum: 1
|
|
729
|
+
} }
|
|
730
|
+
};
|
|
731
|
+
var _browser = /* @__PURE__ */ new WeakMap();
|
|
732
|
+
var _scope = /* @__PURE__ */ new WeakMap();
|
|
733
|
+
var _maxResultBytes = /* @__PURE__ */ new WeakMap();
|
|
734
|
+
var _state = /* @__PURE__ */ new WeakMap();
|
|
735
|
+
var _connecting = /* @__PURE__ */ new WeakMap();
|
|
736
|
+
var _closed = /* @__PURE__ */ new WeakMap();
|
|
737
|
+
var _BrowserSession_brand = /* @__PURE__ */ new WeakSet();
|
|
738
|
+
/**
|
|
739
|
+
* One run's connection to a persistent browser, for one scope.
|
|
740
|
+
*
|
|
741
|
+
* It connects on its first call and stays connected until {@link close}.
|
|
742
|
+
* Concurrent first calls share one connect. Every method takes one options
|
|
743
|
+
* object, validated before anything touches the browser, because the input
|
|
744
|
+
* usually comes from model-written code.
|
|
745
|
+
*
|
|
746
|
+
* Create one per run with `browser.session({ scope })`, and close it when
|
|
747
|
+
* the run ends. The browser itself stays open.
|
|
748
|
+
*
|
|
749
|
+
* @experimental The API surface may change before stabilizing.
|
|
750
|
+
*/
|
|
751
|
+
var BrowserSession = class {
|
|
752
|
+
constructor(browser, options = {}) {
|
|
753
|
+
_classPrivateMethodInitSpec(this, _BrowserSession_brand);
|
|
754
|
+
_classPrivateFieldInitSpec(this, _browser, void 0);
|
|
755
|
+
_classPrivateFieldInitSpec(this, _scope, void 0);
|
|
756
|
+
_classPrivateFieldInitSpec(this, _maxResultBytes, void 0);
|
|
757
|
+
_classPrivateFieldInitSpec(this, _state, void 0);
|
|
758
|
+
_classPrivateFieldInitSpec(this, _connecting, void 0);
|
|
759
|
+
_classPrivateFieldInitSpec(this, _closed, false);
|
|
760
|
+
_classPrivateFieldSet2(_browser, this, browser);
|
|
761
|
+
_classPrivateFieldSet2(_scope, this, options.scope);
|
|
762
|
+
_classPrivateFieldSet2(_maxResultBytes, this, options.maxResultBytes ?? 1e6);
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Send a CDP command and resolve with its result, without the JSON-RPC
|
|
766
|
+
* envelope. `sessionId: "active"` runs it in the scope's tab.
|
|
767
|
+
*/
|
|
768
|
+
async send(input) {
|
|
769
|
+
const { method, params, sessionId, timeoutMs } = validate("browser.cdp", SEND_SCHEMA, input);
|
|
770
|
+
const refused = REFUSED_COMMANDS.get(method);
|
|
771
|
+
if (refused) throw new Error(`${method} is not available: ${refused}`);
|
|
772
|
+
const state = await _assertClassBrand(_BrowserSession_brand, this, _connect).call(this);
|
|
773
|
+
const live = await _assertClassBrand(_BrowserSession_brand, this, _resolveSessionId).call(this, state, sessionId);
|
|
774
|
+
const liveParams = _assertClassBrand(_BrowserSession_brand, this, _liveParams).call(this, state, method, params);
|
|
775
|
+
if (liveParams === DETACHED) return {};
|
|
776
|
+
let result;
|
|
777
|
+
try {
|
|
778
|
+
result = await state.connected.cdp.send(method, liveParams, {
|
|
779
|
+
sessionId: live,
|
|
780
|
+
timeoutMs
|
|
781
|
+
});
|
|
782
|
+
} catch (error) {
|
|
783
|
+
throw await _assertClassBrand(_BrowserSession_brand, this, _teach).call(this, state, error, method, sessionId);
|
|
784
|
+
}
|
|
785
|
+
_assertClassBrand(_BrowserSession_brand, this, _observe).call(this, state, method, liveParams, result);
|
|
786
|
+
if (method === "Page.captureScreenshot") _assertClassBrand(_BrowserSession_brand, this, _assertFits).call(this, result);
|
|
787
|
+
return result;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Make an open tab the scope's active tab, and resolve with a `sessionId`
|
|
791
|
+
* handle for it that keeps working in later runs.
|
|
792
|
+
*/
|
|
793
|
+
async attachToTarget(input) {
|
|
794
|
+
const { targetId, timeoutMs } = validate("browser.attachToTarget", ATTACH_SCHEMA, input);
|
|
795
|
+
const state = await _assertClassBrand(_BrowserSession_brand, this, _connect).call(this);
|
|
796
|
+
const target = targetId;
|
|
797
|
+
await _assertClassBrand(_BrowserSession_brand, this, _attach).call(this, state, target, timeoutMs);
|
|
798
|
+
_assertClassBrand(_BrowserSession_brand, this, _setActive).call(this, state, target);
|
|
799
|
+
return { sessionId: attachHandle(target) };
|
|
800
|
+
}
|
|
801
|
+
/** The Chrome DevTools Protocol description the browser serves. */
|
|
802
|
+
async spec() {
|
|
803
|
+
return (await _assertClassBrand(_BrowserSession_brand, this, _connect).call(this)).connected.spec();
|
|
804
|
+
}
|
|
805
|
+
/** Recent CDP traffic on this session's connection, newest last. */
|
|
806
|
+
async getDebugLog(input) {
|
|
807
|
+
const { limit } = validate("browser.getDebugLog", DEBUG_LOG_SCHEMA, input);
|
|
808
|
+
return (await _assertClassBrand(_BrowserSession_brand, this, _connect).call(this)).connected.cdp.getDebugLog(limit);
|
|
809
|
+
}
|
|
810
|
+
/** Forget the CDP traffic recorded so far. */
|
|
811
|
+
async clearDebugLog() {
|
|
812
|
+
(await _assertClassBrand(_BrowserSession_brand, this, _connect).call(this)).connected.cdp.clearDebugLog();
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* End the session: save the scope's active tab, work out what happened to
|
|
817
|
+
* the browser, and close the connection. The browser stays open.
|
|
818
|
+
*
|
|
819
|
+
* Resolves with the report, or `undefined` when the session never
|
|
820
|
+
* connected. Never rejects: failures to read the browser's state are
|
|
821
|
+
* logged and leave the report incomplete. Later calls resolve with
|
|
822
|
+
* `undefined`, and other methods reject.
|
|
823
|
+
*/
|
|
824
|
+
async close() {
|
|
825
|
+
if (_classPrivateFieldGet2(_closed, this)) return void 0;
|
|
826
|
+
_classPrivateFieldSet2(_closed, this, true);
|
|
827
|
+
let state;
|
|
828
|
+
try {
|
|
829
|
+
state = _classPrivateFieldGet2(_state, this) ?? await _classPrivateFieldGet2(_connecting, this);
|
|
830
|
+
} catch {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
if (!state) return void 0;
|
|
834
|
+
_classPrivateFieldSet2(_state, this, void 0);
|
|
835
|
+
try {
|
|
836
|
+
const open = await _assertClassBrand(_BrowserSession_brand, this, _recordNewTabs).call(this, state);
|
|
837
|
+
const active = await _assertClassBrand(_BrowserSession_brand, this, _saveActiveTarget).call(this, state, open);
|
|
838
|
+
if (active && state.activeResolved) await _assertClassBrand(_BrowserSession_brand, this, _recordSharedTab).call(this, state, active);
|
|
839
|
+
} finally {
|
|
840
|
+
state.connected.cdp.disconnect();
|
|
841
|
+
}
|
|
842
|
+
return state.report;
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
/** Connect once. Concurrent first calls share one connect. */
|
|
846
|
+
function _connect() {
|
|
847
|
+
if (_classPrivateFieldGet2(_closed, this)) return Promise.reject(/* @__PURE__ */ new Error("This browser session is closed."));
|
|
848
|
+
if (_classPrivateFieldGet2(_state, this)) return Promise.resolve(_classPrivateFieldGet2(_state, this));
|
|
849
|
+
_classPrivateFieldGet2(_connecting, this) ?? _classPrivateFieldSet2(_connecting, this, _assertClassBrand(_BrowserSession_brand, this, _open).call(this).finally(() => {
|
|
850
|
+
_classPrivateFieldSet2(_connecting, this, void 0);
|
|
851
|
+
}));
|
|
852
|
+
return _classPrivateFieldGet2(_connecting, this);
|
|
853
|
+
}
|
|
854
|
+
async function _open() {
|
|
855
|
+
const connected = await _classPrivateFieldGet2(_browser, this).connect(_classPrivateFieldGet2(_scope, this) === void 0 ? void 0 : { scope: _classPrivateFieldGet2(_scope, this) });
|
|
856
|
+
let initialPages;
|
|
857
|
+
try {
|
|
858
|
+
initialPages = await _assertClassBrand(_BrowserSession_brand, this, _pages).call(this, connected.cdp);
|
|
859
|
+
} catch (error) {
|
|
860
|
+
connected.cdp.disconnect();
|
|
861
|
+
throw error;
|
|
862
|
+
}
|
|
863
|
+
const state = {
|
|
864
|
+
connected,
|
|
865
|
+
attached: /* @__PURE__ */ new Map(),
|
|
866
|
+
workedIn: /* @__PURE__ */ new Set(),
|
|
867
|
+
initialPages: new Set(initialPages.map((page) => page.targetId)),
|
|
868
|
+
activeResolved: false,
|
|
869
|
+
closed: /* @__PURE__ */ new Set(),
|
|
870
|
+
report: {
|
|
871
|
+
restarted: connected.restarted,
|
|
872
|
+
newTabs: [],
|
|
873
|
+
tabClosed: false,
|
|
874
|
+
tabShared: false
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
if (_classPrivateFieldGet2(_closed, this)) return state;
|
|
878
|
+
_classPrivateFieldSet2(_state, this, state);
|
|
879
|
+
return state;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* A screenshot over the limit is too big to keep in the chat message
|
|
883
|
+
* (see `DEFAULT_MAX_RESULT_BYTES`), so fail with how to take a smaller one.
|
|
884
|
+
*/
|
|
885
|
+
function _assertFits(result) {
|
|
886
|
+
const size = JSON.stringify(result ?? null).length;
|
|
887
|
+
if (size <= _classPrivateFieldGet2(_maxResultBytes, this)) return;
|
|
888
|
+
const megabytes = (size / 1e6).toFixed(1);
|
|
889
|
+
const limit = (_classPrivateFieldGet2(_maxResultBytes, this) / 1e6).toFixed(0);
|
|
890
|
+
throw new Error(`The screenshot result is ${megabytes} MB, over the ${limit} MB limit on a send result. Capture just the viewport (no captureBeyondViewport or clip larger than the window), or pass format: "jpeg" with quality: 60.`);
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Add tabs the page opened during this session to the report. Returns the
|
|
894
|
+
* open tabs, or `undefined` when the browser couldn't be asked.
|
|
895
|
+
*/
|
|
896
|
+
async function _recordNewTabs(state) {
|
|
897
|
+
let pages;
|
|
898
|
+
try {
|
|
899
|
+
pages = await _assertClassBrand(_BrowserSession_brand, this, _pages).call(this, state.connected.cdp);
|
|
900
|
+
} catch (error) {
|
|
901
|
+
_assertClassBrand(_BrowserSession_brand, this, _warn).call(this, "list the open tabs", error);
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const opened = /* @__PURE__ */ new Set();
|
|
905
|
+
let grew = true;
|
|
906
|
+
while (grew) {
|
|
907
|
+
grew = false;
|
|
908
|
+
for (const page of pages) {
|
|
909
|
+
const opener = page.openerId;
|
|
910
|
+
if (!opener || opened.has(page.targetId) || state.initialPages.has(page.targetId)) continue;
|
|
911
|
+
if (state.workedIn.has(opener) || opened.has(opener)) {
|
|
912
|
+
opened.add(page.targetId);
|
|
913
|
+
grew = true;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
state.report.newTabs = pages.filter((page) => opened.has(page.targetId)).map(({ targetId, url, title }) => ({
|
|
918
|
+
targetId,
|
|
919
|
+
url,
|
|
920
|
+
title
|
|
921
|
+
}));
|
|
922
|
+
return new Set(pages.map((page) => page.targetId));
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Save the tab this session settled on, forgetting one that has since
|
|
926
|
+
* closed, and return it. Without the list of open tabs, save the choice
|
|
927
|
+
* unchecked: the next session checks the stored tab is still open before
|
|
928
|
+
* using it.
|
|
929
|
+
*/
|
|
930
|
+
async function _saveActiveTarget(state, open) {
|
|
931
|
+
const stored = state.connected.activeTargetId;
|
|
932
|
+
if (!state.activeResolved && stored && open && !open.has(stored) && !state.closed.has(stored)) state.report.tabClosed = true;
|
|
933
|
+
const candidate = state.activeResolved ? state.activeTargetId : stored;
|
|
934
|
+
const active = candidate && (!open || open.has(candidate)) ? candidate : void 0;
|
|
935
|
+
if (active === stored) return active;
|
|
936
|
+
try {
|
|
937
|
+
await state.connected.setActiveTarget(active);
|
|
938
|
+
} catch (error) {
|
|
939
|
+
_assertClassBrand(_BrowserSession_brand, this, _warn).call(this, "save the active tab", error);
|
|
940
|
+
}
|
|
941
|
+
return active;
|
|
942
|
+
}
|
|
943
|
+
/** Report when another scope is working in this session's tab too. */
|
|
944
|
+
async function _recordSharedTab(state, active) {
|
|
945
|
+
try {
|
|
946
|
+
if ((await state.connected.targetsInOtherScopes()).has(active)) state.report.tabShared = true;
|
|
947
|
+
} catch (error) {
|
|
948
|
+
_assertClassBrand(_BrowserSession_brand, this, _warn).call(this, "check whether the active tab is shared", error);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function _warn(action, error) {
|
|
952
|
+
console.warn(`[agents/browser] Failed to ${action} for browser "${_classPrivateFieldGet2(_browser, this).name}"`, error);
|
|
953
|
+
}
|
|
954
|
+
async function _pages(cdp) {
|
|
955
|
+
return ((await cdp.send("Target.getTargets"))?.targetInfos ?? []).filter((info) => info.type === "page");
|
|
956
|
+
}
|
|
957
|
+
async function _resolveSessionId(state, sessionId) {
|
|
958
|
+
if (sessionId === "active") return _assertClassBrand(_BrowserSession_brand, this, _attach).call(this, state, await _assertClassBrand(_BrowserSession_brand, this, _activeTarget).call(this, state));
|
|
959
|
+
if (isAttachHandle(sessionId)) return _assertClassBrand(_BrowserSession_brand, this, _attach).call(this, state, targetOfHandle(sessionId));
|
|
960
|
+
return sessionId;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Decide which tab `"active"` means, once per session: the scope's stored
|
|
964
|
+
* tab if it is still open, else a new blank tab. Never a tab the scope
|
|
965
|
+
* didn't open, even a blank one: it may be another scope's, or a popup.
|
|
966
|
+
*/
|
|
967
|
+
async function _activeTarget(state) {
|
|
968
|
+
if (state.activeTargetId) return state.activeTargetId;
|
|
969
|
+
const { connected } = state;
|
|
970
|
+
const pages = await _assertClassBrand(_BrowserSession_brand, this, _pages).call(this, connected.cdp);
|
|
971
|
+
const stored = connected.activeTargetId;
|
|
972
|
+
let targetId = pages.find((page) => page.targetId === stored)?.targetId;
|
|
973
|
+
if (!targetId && stored && !state.closed.has(stored)) state.report.tabClosed = true;
|
|
974
|
+
if (!targetId) targetId = (await connected.cdp.send("Target.createTarget", { url: "about:blank" })).targetId;
|
|
975
|
+
_assertClassBrand(_BrowserSession_brand, this, _setActive).call(this, state, targetId);
|
|
976
|
+
return targetId;
|
|
977
|
+
}
|
|
978
|
+
function _setActive(state, targetId) {
|
|
979
|
+
state.activeTargetId = targetId;
|
|
980
|
+
state.activeResolved = true;
|
|
981
|
+
}
|
|
982
|
+
async function _attach(state, targetId, timeoutMs) {
|
|
983
|
+
const existing = state.attached.get(targetId);
|
|
984
|
+
if (existing) return existing;
|
|
985
|
+
const live = await state.connected.cdp.attachToTarget(targetId, { timeoutMs });
|
|
986
|
+
state.attached.set(targetId, live);
|
|
987
|
+
state.workedIn.add(targetId);
|
|
988
|
+
return live;
|
|
989
|
+
}
|
|
990
|
+
/** Track tab changes the model makes through raw Target commands. */
|
|
991
|
+
function _observe(state, method, params, result) {
|
|
992
|
+
const targetId = typeof params?.targetId === "string" ? params.targetId : void 0;
|
|
993
|
+
if (method === "Target.createTarget") {
|
|
994
|
+
const created = result?.targetId;
|
|
995
|
+
if (typeof created === "string") _assertClassBrand(_BrowserSession_brand, this, _setActive).call(this, state, created);
|
|
996
|
+
} else if (method === "Target.attachToTarget" && targetId) {
|
|
997
|
+
_assertClassBrand(_BrowserSession_brand, this, _setActive).call(this, state, targetId);
|
|
998
|
+
state.workedIn.add(targetId);
|
|
999
|
+
} else if (method === "Target.closeTarget" && targetId) {
|
|
1000
|
+
state.attached.delete(targetId);
|
|
1001
|
+
state.closed.add(targetId);
|
|
1002
|
+
if (state.activeTargetId === targetId) state.activeTargetId = void 0;
|
|
1003
|
+
} else if (method === "Target.detachFromTarget") {
|
|
1004
|
+
for (const [target, live] of state.attached) if (target === targetId || live === params?.sessionId) state.attached.delete(target);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Swap a handle in `Target.detachFromTarget`'s `sessionId` param for the
|
|
1009
|
+
* live CDP session id. Returns {@link DETACHED} when the handle's tab isn't
|
|
1010
|
+
* attached on this socket, so there is nothing to detach.
|
|
1011
|
+
*/
|
|
1012
|
+
function _liveParams(state, method, params) {
|
|
1013
|
+
const handle = params?.sessionId;
|
|
1014
|
+
if (method !== "Target.detachFromTarget" || typeof handle !== "string") return params;
|
|
1015
|
+
if (!isAttachHandle(handle)) return params;
|
|
1016
|
+
const live = state.attached.get(targetOfHandle(handle));
|
|
1017
|
+
return live ? {
|
|
1018
|
+
...params,
|
|
1019
|
+
sessionId: live
|
|
1020
|
+
} : DETACHED;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Turn the common protocol mistakes into instructions: an expired CDP
|
|
1024
|
+
* session id, an event sent as a command, and a page-scoped command
|
|
1025
|
+
* without a session.
|
|
1026
|
+
*/
|
|
1027
|
+
async function _teach(state, error, method, sessionId) {
|
|
1028
|
+
if (!(error instanceof CdpProtocolError)) return error;
|
|
1029
|
+
if (error.code === CDP_SESSION_NOT_FOUND && sessionId && sessionId !== "active" && !isAttachHandle(sessionId)) return /* @__PURE__ */ new Error(`${error.message}. CDP session ids don't carry over between runs. Use sessionId: "active" for the current tab, or the handle from browser.attachToTarget({ targetId }) for another tab.`);
|
|
1030
|
+
if (error.code !== -32601) return error;
|
|
1031
|
+
if (await _assertClassBrand(_BrowserSession_brand, this, _isEvent).call(this, state, method)) return /* @__PURE__ */ new Error(`${error.message}. '${method}' is a CDP *event*, not a command, so it cannot be sent. To wait for page state, poll instead (e.g. Runtime.evaluate of document.readyState until "complete").`);
|
|
1032
|
+
if (!sessionId) return /* @__PURE__ */ new Error(`${error.message}. '${method}' is page-scoped: pass sessionId: "active" to run it in the current tab: browser.cdp({ method: "${method}", params, sessionId: "active" }).`);
|
|
1033
|
+
return error;
|
|
1034
|
+
}
|
|
1035
|
+
async function _isEvent(state, method) {
|
|
1036
|
+
try {
|
|
1037
|
+
const spec = await state.connected.spec();
|
|
1038
|
+
const domain = method.split(".")[0];
|
|
1039
|
+
return spec.domains.some((d) => d.name === domain && d.events.some((e) => e.event === method));
|
|
1040
|
+
} catch {
|
|
1041
|
+
return false;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
function attachHandle(targetId) {
|
|
1045
|
+
return `${ATTACH_HANDLE_PREFIX}${targetId}`;
|
|
1046
|
+
}
|
|
1047
|
+
function isAttachHandle(sessionId) {
|
|
1048
|
+
return sessionId?.startsWith(ATTACH_HANDLE_PREFIX) ?? false;
|
|
1049
|
+
}
|
|
1050
|
+
function targetOfHandle(handle) {
|
|
1051
|
+
return handle.slice(7);
|
|
1052
|
+
}
|
|
1053
|
+
const validators = /* @__PURE__ */ new WeakMap();
|
|
1054
|
+
/**
|
|
1055
|
+
* Check a method's input against its schema before it touches the browser,
|
|
1056
|
+
* so the model gets a precise error. An omitted input counts as `{}`.
|
|
1057
|
+
*
|
|
1058
|
+
* @internal Also used by `browser-functions.ts`.
|
|
1059
|
+
*/
|
|
1060
|
+
function validate(name, schema, input) {
|
|
1061
|
+
let validator = validators.get(schema);
|
|
1062
|
+
if (!validator) {
|
|
1063
|
+
validator = new Validator(schema, "7", false);
|
|
1064
|
+
validators.set(schema, validator);
|
|
1065
|
+
}
|
|
1066
|
+
const value = input ?? {};
|
|
1067
|
+
const result = validator.validate(value);
|
|
1068
|
+
if (!result.valid) throw new Error(formatValidationError(name, result.errors));
|
|
1069
|
+
return value;
|
|
1070
|
+
}
|
|
1071
|
+
function formatValidationError(name, errors) {
|
|
1072
|
+
const specific = errors.filter((error) => error.keyword !== "properties");
|
|
1073
|
+
return `Invalid arguments for ${name}${(specific.length > 0 ? specific : errors).map((error) => {
|
|
1074
|
+
const location = error.instanceLocation.replace(/^#\/?/, "").replaceAll("~1", "/").replaceAll("~0", "~").replaceAll("/", ".");
|
|
1075
|
+
return `${location ? ` at ${location}` : ""}: ${error.error}`;
|
|
1076
|
+
}).join(";")}`;
|
|
1077
|
+
}
|
|
1078
|
+
//#endregion
|
|
1079
|
+
export { CdpProtocolError as C, CdpConnection as S, connectUrl as T, deleteBrowserSession as _, DEFAULT_SWEEP_IDLE_MS as a, listBrowserTargets as b, LIVE_VIEW_URL_TTL_MS as c, BrowserRenderingError as d, acquireBrowserSession as f, createBrowserSession as g, connectBrowserSession as h, validate as i, applyLiveViewMode as l, connectBrowser as m, BrowserSession as n, DurableBrowserSessionStore as o, closeBrowserSession as p, DEFAULT_MAX_RESULT_BYTES as r, loadCdpSpec as s, ACTIVE_PAGE_SESSION as t, createLiveView as u, getBrowserRecording as v, CdpSession as w, CDP_METHOD_NOT_FOUND as x, isMissingBrowserSession as y };
|
|
658
1080
|
|
|
659
|
-
//# sourceMappingURL=session-
|
|
1081
|
+
//# sourceMappingURL=session-7CDgVE0z.js.map
|