qz-browser-agent 0.1.3 → 0.1.5
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 +54 -25
- package/package.json +5 -2
- package/src/args.mjs +57 -16
- package/src/bin.mjs +2 -2
- package/src/catalog.json +73 -10
- package/src/cli-resources.mjs +2 -2
- package/src/cli.mjs +135 -120
- package/src/client.mjs +61 -19
- package/src/commands.mjs +45 -10
- package/src/connect-page.mjs +89 -0
- package/src/connection.mjs +145 -0
- package/src/daemon.mjs +180 -31
- package/src/drive-guide.mjs +38 -0
- package/src/drive-timeout.mjs +21 -0
- package/src/open-browser.mjs +40 -11
- package/src/qz-drive-SKILL.md +165 -0
- package/src/qz-logo-day.png +0 -0
- package/src/qz-logo.png +0 -0
- package/src/script-call.mjs +40 -0
- package/src/server.mjs +25 -8
- package/src/state.mjs +81 -29
- package/src/task-stream.mjs +4 -4
- package/src/terminal.mjs +68 -18
- package/src/tools.mjs +148 -30
package/README.md
CHANGED
|
@@ -2,54 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
An open-source browser agent for humans and coding agents.
|
|
4
4
|
|
|
5
|
-
qz is a local-first extension for Chrome and Edge.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
qz is a local-first extension for Chrome and Edge. This CLI and its MCP endpoint let coding agents
|
|
6
|
+
such as Claude Code, Codex, and pi drive your own browser, with your existing logins, or hand qz a
|
|
7
|
+
whole web task. Driving the browser needs no model configured in qz: your agent writes a short
|
|
8
|
+
script, and qz returns a compact result and trace that keep its token use low. People can watch
|
|
9
|
+
and step in from the side panel. Everything runs locally.
|
|
10
10
|
|
|
11
11
|
[Website](https://alibaba.github.io/qz-browser-agent/) ·
|
|
12
12
|
[GitHub](https://github.com/alibaba/qz-browser-agent) ·
|
|
13
13
|
[Chrome Web Store](https://chromewebstore.google.com/detail/dknbdgkgoinlgjpfebapkklbmjjgdblm) ·
|
|
14
14
|
[Full CLI and MCP reference](https://github.com/alibaba/qz-browser-agent/blob/main/docs/cli.md)
|
|
15
15
|
|
|
16
|
-

|
|
17
|
-
|
|
18
16
|
## Quick start
|
|
19
17
|
|
|
20
18
|
Requires Node.js 22 or later.
|
|
21
19
|
|
|
22
20
|
1. **Install the extension.** Click **Add to Chrome** on the
|
|
23
|
-
[Chrome Web Store](https://chromewebstore.google.com/detail/dknbdgkgoinlgjpfebapkklbmjjgdblm)
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
[Chrome Web Store](https://chromewebstore.google.com/detail/dknbdgkgoinlgjpfebapkklbmjjgdblm).
|
|
22
|
+
2. **Install the CLI.** Or use `npx qz-browser-agent@latest` in place of `qz`.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --global qz-browser-agent@latest
|
|
26
|
+
```
|
|
27
|
+
3. **Drive the browser.** No model setup is needed in qz.
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
|
-
|
|
30
|
+
qz session create --title "Read example page"
|
|
31
|
+
qz script --session SESSION_ID - <<'JS'
|
|
32
|
+
const page = qz.tab(await qz.openTab({ url: "https://example.com" }));
|
|
33
|
+
return await page.read({ limit: 2000 });
|
|
34
|
+
JS
|
|
29
35
|
```
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
The first command connects the browser automatically. To teach your agent the full workflow,
|
|
38
|
+
install the qz Skill with `npx skills add alibaba/qz-browser-agent -g`, or have it read `qz skill`.
|
|
39
|
+
4. **Or hand qz the whole task.** Choose a provider and model in the side panel's **Settings**, then:
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
|
-
|
|
42
|
+
qz create "Find this year's Nobel Prize winners in every category and put them in a table with source links." --stream
|
|
35
43
|
```
|
|
36
44
|
|
|
37
45
|
`--stream` shows progress, any questions qz needs you to answer, and the final result.
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
`npx qz-browser-agent@latest`:
|
|
47
|
+
## Drive the browser
|
|
41
48
|
|
|
42
49
|
```bash
|
|
43
|
-
|
|
44
|
-
qz --
|
|
50
|
+
qz skill
|
|
51
|
+
qz session create --title "Read example page" --json
|
|
52
|
+
qz script --session SESSION_ID task.js --args '{"query":"example"}'
|
|
53
|
+
qz session show SESSION_ID
|
|
54
|
+
qz session cancel SESSION_ID
|
|
45
55
|
```
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
`session create` starts no model. With `--json`, `result.qz_skills` lists the Skills the new session
|
|
58
|
+
can use right away, and `qz.call("load_skill",{name})` makes another Skill's tools callable at once.
|
|
59
|
+
|
|
60
|
+
Scripts run outside pages and use the `qz` SDK. Bind `qz.tab(tabRef)` for page operations,
|
|
61
|
+
use `page.locator(query)` for element actions and waits, and `page.evaluate(code,args?)` for DOM access.
|
|
62
|
+
Discover additional capabilities with `qz.listTools()` or `qz.listTools({names:["list_images"]})`,
|
|
63
|
+
then invoke them through `qz.call(name,args)`. stdout is JSON `{ok,result,trace}`: execution status, the returned business value,
|
|
64
|
+
and ordered error and `trace()`/`console` messages without levels. Failures put `ToolError` in `result`; `undefined`
|
|
65
|
+
returns become `null`.
|
|
66
|
+
|
|
67
|
+
Browser operations prepare their connection automatically: they reuse the first responding
|
|
68
|
+
online browser, or open the connection page once in the system default browser. When qz does not respond
|
|
69
|
+
there, the page links to the Chrome Web Store and shows its URL with a copy button for a browser that has qz.
|
|
70
|
+
Select an existing profile with --client NAME|UUID; it never falls back to another profile. An incompatible
|
|
71
|
+
local service is restarted once before browser operations. Complete the extension’s connection
|
|
72
|
+
prompt when needed.
|
|
49
73
|
|
|
50
74
|
## Connect an MCP client
|
|
51
75
|
|
|
52
|
-
|
|
76
|
+
Print the MCP URL; this starts the local service if needed:
|
|
53
77
|
|
|
54
78
|
```bash
|
|
55
79
|
npx qz-browser-agent@latest mcp
|
|
@@ -80,15 +104,17 @@ claude mcp add --transport http qz http://127.0.0.1:18765/mcp
|
|
|
80
104
|
}
|
|
81
105
|
```
|
|
82
106
|
|
|
83
|
-
CLI and MCP share the same browser connection. The MCP
|
|
107
|
+
CLI and MCP share the same browser connection. The default MCP catalog is the direct-drive set;
|
|
108
|
+
`qz mcp --profile full` prints the URL for the task and diagnostic catalog. The MCP tool list is in the
|
|
84
109
|
[reference](https://github.com/alibaba/qz-browser-agent/blob/main/docs/cli.md#task-and-resource-operations).
|
|
85
110
|
|
|
86
111
|
## Common commands
|
|
87
112
|
|
|
88
113
|
| Command | What it does |
|
|
89
114
|
| --- | --- |
|
|
90
|
-
| `qz connect` |
|
|
115
|
+
| `qz connect uri` | Print a URL to open in a chosen browser profile |
|
|
91
116
|
| `qz status` | Check the connection and service address |
|
|
117
|
+
| `qz cleanup` | Stop registered local services and remove runtime files and logs |
|
|
92
118
|
| `qz browsers` | List paired browsers, or `forget` an offline one |
|
|
93
119
|
| `qz create "PROMPT"` | Start a task; add `--stream` or `--wait` to follow it |
|
|
94
120
|
| `qz send TASK_ID "MESSAGE"` | Send a follow-up, or `--steer` the current run |
|
|
@@ -102,7 +128,6 @@ CLI and MCP share the same browser connection. The MCP tool list is in the
|
|
|
102
128
|
| `qz skills` | Find, read, or export Skills |
|
|
103
129
|
| `qz settings` | Read or update settings |
|
|
104
130
|
| `qz automations` | List or run automations |
|
|
105
|
-
| `qz stop` | Stop the local service and disconnect all browsers |
|
|
106
131
|
|
|
107
132
|
With several browser profiles connected, choose one with `--client NAME|UUID` (or `-c`).
|
|
108
133
|
Run `qz COMMAND --help` for options. Interactive terminals show readable text; pipes receive JSON.
|
|
@@ -116,8 +141,12 @@ Timeout or Ctrl-C ends observation while the browser task continues; execute the
|
|
|
116
141
|
## Privacy and local access
|
|
117
142
|
|
|
118
143
|
The service accepts connections only on your computer, and ordinary websites cannot use its MCP
|
|
119
|
-
endpoint. Local programs can control connected qz tasks while the service is running.
|
|
120
|
-
|
|
144
|
+
endpoint. Local programs can control connected qz tasks while the service is running. Disconnect
|
|
145
|
+
a profile in its qz connection settings; task cancellation remains a separate operation.
|
|
146
|
+
Run `qz cleanup` to stop local services across installations and remove their runtime files and logs.
|
|
147
|
+
It preserves browser registrations, tabs, tasks and saved data; the next browser command starts its
|
|
148
|
+
service automatically. Active CLI requests and direct-drive scripts are interrupted; side-panel tasks
|
|
149
|
+
continue. `QZ_STATE_DIR` selects a different runtime directory when set.
|
|
121
150
|
Connection settings are stored in `~/.qz-browser-agent/run/`, and pairing credentials never
|
|
122
151
|
leave your computer.
|
|
123
152
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qz-browser-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Local browser tools and MCP access for the qz extension",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": { "node": ">=22" },
|
|
7
7
|
"bin": { "qz": "src/bin.mjs" },
|
|
8
|
-
"files": ["src/*.mjs", "src/catalog.json", "README.md", "LICENSE"],
|
|
8
|
+
"files": ["src/*.mjs", "src/catalog.json", "src/qz-drive-SKILL.md", "src/qz-logo.png", "src/qz-logo-day.png", "README.md", "LICENSE"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"prepack": "cp ../../skills/qz-browser-agent/SKILL.md src/qz-drive-SKILL.md && cp ../../src/icons/icon128.png src/qz-logo.png && cp ../../src/icons/logo-day.png src/qz-logo-day.png"
|
|
11
|
+
},
|
|
9
12
|
"license": "Apache-2.0",
|
|
10
13
|
"homepage": "https://alibaba.github.io/qz-browser-agent/",
|
|
11
14
|
"bugs": { "url": "https://github.com/alibaba/qz-browser-agent/issues" },
|
package/src/args.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import { QzError } from "./client.mjs";
|
|
3
3
|
import { browserName } from "./browser-registry.mjs";
|
|
4
|
-
import { COMMANDS, commandOptions } from "./commands.mjs";
|
|
4
|
+
import { COMMANDS, DRIVE_SESSION_REQUIRED, commandOptions } from "./commands.mjs";
|
|
5
5
|
|
|
6
6
|
function invalid(message) {
|
|
7
|
-
throw new QzError("
|
|
7
|
+
throw new QzError("INVALID_ARGUMENTS", message, {
|
|
8
|
+
hint: "Run qz --help, or npm run qz -- --help, to check the command and its arguments.",
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function requireDriveSession(command, options) {
|
|
13
|
+
if (!String(options.session || "").trim()) invalid(`qz ${command} requires --session <id>. ${DRIVE_SESSION_REQUIRED}`);
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
function helpTopic(parts) {
|
|
@@ -14,20 +20,29 @@ function helpTopic(parts) {
|
|
|
14
20
|
invalid("Use qz help COMMAND.");
|
|
15
21
|
}
|
|
16
22
|
|
|
23
|
+
const argumentOptions = {
|
|
24
|
+
session: { type: "string", short: "s" }, timeout: { type: "string" },
|
|
25
|
+
title: { type: "string", short: "t" }, client: { type: "string", short: "c" },
|
|
26
|
+
"wait-id": { type: "string" }, format: { type: "string" }, query: { type: "string" },
|
|
27
|
+
start: { type: "string" }, limit: { type: "string" }, scope: { type: "string" }, filename: { type: "string" },
|
|
28
|
+
parent: { type: "string" }, port: { type: "string" }, name: { type: "string" },
|
|
29
|
+
wait: { type: "boolean" }, stream: { type: "boolean" }, steer: { type: "boolean" },
|
|
30
|
+
json: { type: "boolean" }, plain: { type: "boolean" }, "no-color": { type: "boolean" },
|
|
31
|
+
lang: { type: "string" }, profile: { type: "string" }, args: { type: "string" },
|
|
32
|
+
keep: { type: "string", multiple: true },
|
|
33
|
+
help: { type: "boolean", short: "h" }, version: { type: "boolean", short: "v" },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Identify the command for syntax-error presentation while consuming option values normally. */
|
|
37
|
+
export function commandNameFromArgs(argv) {
|
|
38
|
+
return parseArgs({ args: argv, allowPositionals: true, strict: false, options: argumentOptions }).positionals[0] || "help";
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
/** Parse syntax only. A prompt or args value of "-" is read from stdin by the caller. */
|
|
18
42
|
export function parseCommand(argv) {
|
|
19
43
|
let parsed;
|
|
20
44
|
try {
|
|
21
|
-
parsed = parseArgs({ args: argv, allowPositionals: true, strict: true, tokens: true, options:
|
|
22
|
-
session: { type: "string", short: "s" }, timeout: { type: "string" },
|
|
23
|
-
title: { type: "string", short: "t" }, client: { type: "string", short: "c" },
|
|
24
|
-
"wait-id": { type: "string" }, format: { type: "string" }, query: { type: "string" },
|
|
25
|
-
start: { type: "string" }, limit: { type: "string" }, scope: { type: "string" }, filename: { type: "string" },
|
|
26
|
-
parent: { type: "string" }, port: { type: "string" }, name: { type: "string" }, "no-open": { type: "boolean" },
|
|
27
|
-
wait: { type: "boolean" }, stream: { type: "boolean" }, steer: { type: "boolean" },
|
|
28
|
-
json: { type: "boolean" }, plain: { type: "boolean" }, "no-color": { type: "boolean" },
|
|
29
|
-
help: { type: "boolean", short: "h" }, version: { type: "boolean", short: "v" },
|
|
30
|
-
} });
|
|
45
|
+
parsed = parseArgs({ args: argv, allowPositionals: true, strict: true, tokens: true, options: argumentOptions });
|
|
31
46
|
} catch (error) { invalid(error.message); }
|
|
32
47
|
|
|
33
48
|
const options = { ...parsed.values };
|
|
@@ -35,7 +50,7 @@ export function parseCommand(argv) {
|
|
|
35
50
|
for (const token of parsed.tokens) {
|
|
36
51
|
if (token.kind !== "option" || token.value === undefined) continue;
|
|
37
52
|
const name = token.name;
|
|
38
|
-
if (seen.has(name)) invalid(`Provide --${name} only once.`);
|
|
53
|
+
if (seen.has(name) && !argumentOptions[name].multiple) invalid(`Provide --${name} only once.`);
|
|
39
54
|
seen.add(name);
|
|
40
55
|
}
|
|
41
56
|
if (options.json && options.plain) invalid("Choose --json or --plain.");
|
|
@@ -57,11 +72,15 @@ export function parseCommand(argv) {
|
|
|
57
72
|
for (const name of ["session", "client", "wait-id", "parent"]) {
|
|
58
73
|
if (options[name] !== undefined && !options[name].trim()) invalid(`--${name} requires a non-empty ID.`);
|
|
59
74
|
}
|
|
75
|
+
if (options.keep !== undefined) {
|
|
76
|
+
options.keep = options.keep.map(value => value.trim());
|
|
77
|
+
if (options.keep.some(value => !value)) invalid("--keep requires a non-empty tabRef.");
|
|
78
|
+
}
|
|
60
79
|
if (options.name !== undefined) {
|
|
61
80
|
try { options.name = browserName(options.name); } catch (error) { invalid(error.message); }
|
|
62
81
|
}
|
|
63
|
-
if (options.client !== undefined &&
|
|
64
|
-
invalid("Use --client to select an existing browser, or --name
|
|
82
|
+
if (options.client !== undefined && options.name !== undefined) {
|
|
83
|
+
invalid("Use --client to select an existing browser, or --name to name a new profile.");
|
|
65
84
|
}
|
|
66
85
|
if (options.port !== undefined) {
|
|
67
86
|
const port = Number(options.port);
|
|
@@ -70,6 +89,8 @@ export function parseCommand(argv) {
|
|
|
70
89
|
}
|
|
71
90
|
if (options.filename !== undefined && !options.filename.trim()) invalid("--filename requires a non-empty name.");
|
|
72
91
|
if (options.scope !== undefined && !["all", "global", "site", "resident"].includes(options.scope)) invalid("--scope must be all, global, site, or resident.");
|
|
92
|
+
if (options.profile !== undefined && !["full", "drive"].includes(options.profile)) invalid("--profile must be full or drive.");
|
|
93
|
+
if (options.lang !== undefined && !options.lang.trim()) invalid("--lang requires a value.");
|
|
73
94
|
if (options.client !== undefined) {
|
|
74
95
|
options["client-id"] = options.client;
|
|
75
96
|
delete options.client;
|
|
@@ -90,7 +111,10 @@ export function parseCommand(argv) {
|
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
let command = originalCommand, action, id;
|
|
93
|
-
if (command === "
|
|
114
|
+
if (command === "connect") {
|
|
115
|
+
if (words.length !== 1 || words[0] !== "uri") invalid("Use qz connect uri to print a browser connection URL.");
|
|
116
|
+
action = "uri";
|
|
117
|
+
} else if (command === "browsers") {
|
|
94
118
|
action = words[0] || "list";
|
|
95
119
|
if (action === "list" ? words.length > 1 : action !== "forget" || words.length !== 2 || !words[1].trim()) invalid("Use qz browsers [list], or qz browsers forget NAME|UUID.");
|
|
96
120
|
id = words[1];
|
|
@@ -141,6 +165,23 @@ export function parseCommand(argv) {
|
|
|
141
165
|
id = words[0];
|
|
142
166
|
if (command === "submit") options.args = words[1];
|
|
143
167
|
if (command === "watch") options.wait = true;
|
|
168
|
+
} else if (command === "script") {
|
|
169
|
+
if (words.length > 1) invalid("Use qz script --session ID [FILE|-] [--args JSON].");
|
|
170
|
+
requireDriveSession("script", options);
|
|
171
|
+
options.file = words[0] ?? "-";
|
|
172
|
+
if (options.args !== undefined) {
|
|
173
|
+
try { options.args = JSON.parse(options.args); } catch { invalid("--args must be valid JSON."); }
|
|
174
|
+
}
|
|
175
|
+
} else if (command === "session") {
|
|
176
|
+
action = words[0] || "";
|
|
177
|
+
const count = ["create", "list"].includes(action) ? 1 : 2;
|
|
178
|
+
if (!Object.hasOwn(COMMANDS.session.actions, action) || words.length !== count || words.some(word => !word.trim())) {
|
|
179
|
+
invalid("Use qz session create --title TITLE, qz session list, qz session show ID, or qz session cancel ID.");
|
|
180
|
+
}
|
|
181
|
+
if (action === "create" && !String(options.title || "").trim()) invalid("qz session create requires --title TITLE. Choose a readable task title.");
|
|
182
|
+
id = words[1];
|
|
183
|
+
} else if (command === "skill") {
|
|
184
|
+
if (words.length) invalid("Use qz skill.");
|
|
144
185
|
} else if (command === "tasks") {
|
|
145
186
|
if (words.length) invalid("Use qz tasks to list tasks, qz create PROMPT, qz show ID, or qz cancel ID.");
|
|
146
187
|
action = "list";
|
package/src/bin.mjs
CHANGED
|
@@ -9,11 +9,11 @@ if (isEntry) {
|
|
|
9
9
|
const argv = process.argv.slice(2);
|
|
10
10
|
if (Number(process.versions.node.split(".")[0]) < 22) {
|
|
11
11
|
const error = {
|
|
12
|
-
code: "
|
|
12
|
+
code: "UNSUPPORTED_NODE_VERSION",
|
|
13
13
|
message: `qz requires Node.js 22 or later. Current version: ${process.versions.node}.`,
|
|
14
14
|
hint: "Install Node.js 22 or later, then run qz again.",
|
|
15
15
|
};
|
|
16
|
-
if (argv.includes("--json")) process.stdout.write(`${JSON.stringify(
|
|
16
|
+
if (argv.includes("--json")) process.stdout.write(`${JSON.stringify(error)}\n`);
|
|
17
17
|
else process.stderr.write(`${error.message}\n${error.hint}\n`);
|
|
18
18
|
process.exitCode = 1;
|
|
19
19
|
} else {
|
package/src/catalog.json
CHANGED
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
"properties": {
|
|
54
54
|
"limit": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Limit"},
|
|
55
55
|
"excludeSessionId": {"default": "", "title": "Excludesessionid", "type": "string"},
|
|
56
|
+
"scope": {"type": "string", "description": "session returns one session. Omit it to list tasks."},
|
|
57
|
+
"sessionId": {"type": "string", "description": "Session to return when scope is session."},
|
|
56
58
|
"timeoutSeconds": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Timeoutseconds"},
|
|
57
59
|
"clientId": {"default": "", "title": "Clientid", "type": "string", "description": "Browser name or UUID. Required when multiple browsers are online. Offline or unknown selections never fall back to another browser."}
|
|
58
60
|
},
|
|
@@ -228,7 +230,7 @@
|
|
|
228
230
|
},
|
|
229
231
|
{
|
|
230
232
|
"name": "qingzhou_get_session_snapshot",
|
|
231
|
-
"description": "Read a task snapshot: session, runtime, current input, transcript, recent messages, artifacts, and optional trace/tools. Pending input includes field IDs, option values, instructions, and responseTemplate for answering directly. Tool transcript argsState/argsGenerating distinguish generating arguments from final empty arguments.",
|
|
233
|
+
"description": "Read a task snapshot: session, runtime, current input, transcript, recent messages, artifacts, cumulative usage, and optional trace/tools. usage sums recorded model calls (inputTokens, outputTokens, cachedTokens, costUsd, modelCalls) plus toolCalls, screenshots, and contextCompressions. Pending input includes field IDs, option values, instructions, and responseTemplate for answering directly. Tool transcript argsState/argsGenerating distinguish generating arguments from final empty arguments.",
|
|
232
234
|
"inputSchema": {
|
|
233
235
|
"properties": {
|
|
234
236
|
"sessionId": {"default": "default", "title": "Sessionid", "type": "string"},
|
|
@@ -264,11 +266,13 @@
|
|
|
264
266
|
},
|
|
265
267
|
{
|
|
266
268
|
"name": "qingzhou_stop_session",
|
|
267
|
-
"description": "
|
|
269
|
+
"description": "结束任务或直驱会话。直驱会话结束后不可再执行工具,也无需再调用 close_tab;默认自动关闭该会话创建的标签页,并保留用户原有或已交回给用户的标签页。keepTabRefs 指定要交还用户的会话标签,keepTabs:true 保留全部标签页。End a task or direct-drive session, closing its created tabs by default while preserving user tabs. Pass keepTabRefs to return selected session tabs to the user, or keepTabs:true to retain all tabs.",
|
|
268
270
|
"inputSchema": {
|
|
269
271
|
"properties": {
|
|
270
272
|
"sessionId": {"title": "Sessionid", "type": "string"},
|
|
271
273
|
"cascade": {"default": true, "title": "Cascade", "type": "boolean"},
|
|
274
|
+
"keepTabs": {"title": "Keeptabs", "type": "boolean"},
|
|
275
|
+
"keepTabRefs": {"type": "array", "items": {"anyOf": [{"type": "integer"}, {"type": "string"}]}, "description": "Session tabRef values to keep after ending the session. Validated before stopping."},
|
|
272
276
|
"timeoutSeconds": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Timeoutseconds"},
|
|
273
277
|
"clientId": {"default": "", "title": "Clientid", "type": "string", "description": "Browser name or UUID. Required when multiple browsers are online. Offline or unknown selections never fall back to another browser."}
|
|
274
278
|
},
|
|
@@ -279,10 +283,10 @@
|
|
|
279
283
|
},
|
|
280
284
|
{
|
|
281
285
|
"name": "qingzhou_list_tools",
|
|
282
|
-
"description": "
|
|
286
|
+
"description": "List default direct-drive tool signatures; sessionId is optional. Query non-default or loaded Skill tools by exact name with qingzhou_get_tool (pass sessionId for loaded Skills). For full schemas, call tools.list_tools({names:[...]}) inside run_script. Never creates a session.",
|
|
283
287
|
"inputSchema": {
|
|
284
288
|
"properties": {
|
|
285
|
-
"sessionId": {"
|
|
289
|
+
"sessionId": {"title": "Sessionid", "type": "string"},
|
|
286
290
|
"timeoutSeconds": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Timeoutseconds"},
|
|
287
291
|
"clientId": {"default": "", "title": "Clientid", "type": "string", "description": "Browser name or UUID. Required when multiple browsers are online. Offline or unknown selections never fall back to another browser."}
|
|
288
292
|
},
|
|
@@ -337,20 +341,79 @@
|
|
|
337
341
|
"type": "object"
|
|
338
342
|
}
|
|
339
343
|
},
|
|
344
|
+
{
|
|
345
|
+
"name": "qingzhou_list_drive_sessions",
|
|
346
|
+
"description": "List direct-drive sessions as {sessionId,title,status} observations.",
|
|
347
|
+
"inputSchema": {
|
|
348
|
+
"properties": {
|
|
349
|
+
"timeoutSeconds": {"type": ["number", "null"], "default": null},
|
|
350
|
+
"clientId": {"type": "string", "default": "", "description": "Browser name or UUID."}
|
|
351
|
+
},
|
|
352
|
+
"type": "object"
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"name": "qingzhou_get_drive_session",
|
|
357
|
+
"description": "Read one direct-drive session as {sessionId,title,status}.",
|
|
358
|
+
"inputSchema": {
|
|
359
|
+
"properties": {
|
|
360
|
+
"sessionId": {"type": "string", "minLength": 1},
|
|
361
|
+
"timeoutSeconds": {"type": ["number", "null"], "default": null},
|
|
362
|
+
"clientId": {"type": "string", "default": "", "description": "Browser name or UUID."}
|
|
363
|
+
},
|
|
364
|
+
"required": ["sessionId"],
|
|
365
|
+
"type": "object"
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"name": "qingzhou_create_drive_session",
|
|
370
|
+
"description": "Create one direct-drive session with a readable title and return sessionId, title, status, and qz_skills (summaries of the user workflows available to the session). Pass that id on every tool execution. This does not start a model.",
|
|
371
|
+
"inputSchema": {
|
|
372
|
+
"required": ["title"],
|
|
373
|
+
"properties": {
|
|
374
|
+
"title": {"type": "string", "minLength": 1, "pattern": "\\S", "description": "Readable task title."},
|
|
375
|
+
"timeoutSeconds": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Timeoutseconds"},
|
|
376
|
+
"clientId": {"default": "", "title": "Clientid", "type": "string", "description": "Browser name or UUID. Required when multiple browsers are online. Offline or unknown selections never fall back to another browser."}
|
|
377
|
+
},
|
|
378
|
+
"title": "qingzhou_create_drive_sessionArguments",
|
|
379
|
+
"type": "object"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
340
382
|
{
|
|
341
383
|
"name": "qingzhou_execute_tool",
|
|
342
|
-
"description": "
|
|
384
|
+
"description": "在已连接的浏览器里执行现有工具。先调用 qingzhou_create_drive_session 创建会话,再传入该 sessionId(不会隐式创建会话)。单次调用传 name 与 args;已知参数的并发或顺序批次传 calls(每项含 id、name、args,id 仅用于关联结果,不支持跨项插值);多步逻辑与条件编排可调用 name:\"run_script\"(页面外执行,支持顶层 await/return 与 tools.*,页面内 DOM 函数用 evaluate)。页面工具显式传 tabRef。逐项检查 isError,批次与脚本均非事务;run_script 的 result 为 {ok,result,trace} 回执,trace 按序记录报错与显式 trace()/console 输出。Execute browser tools in an explicitly created session (pass sessionId from qingzhou_create_drive_session). Supply name/args for one call, calls ([{id,name,args}]) for a static batch, or name:\"run_script\" for outer JS orchestration (use evaluate for in-page DOM functions). Page tools require an explicit tabRef. Check isError on each call; batches and scripts are not transactions. run_script returns {ok,result,trace}, where trace lists errors and explicit trace()/console diagnostics in order.",
|
|
343
385
|
"inputSchema": {
|
|
344
386
|
"properties": {
|
|
387
|
+
"scriptApiVersion": {"type": "integer", "minimum": 1, "description": "Script API protocol version; must match the extension before executing the request."},
|
|
345
388
|
"name": {"title": "Name", "type": "string"},
|
|
346
389
|
"args": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "default": null, "title": "Args"},
|
|
347
|
-
"
|
|
348
|
-
|
|
390
|
+
"calls": {
|
|
391
|
+
"type": "array",
|
|
392
|
+
"items": {
|
|
393
|
+
"type": "object",
|
|
394
|
+
"additionalProperties": false,
|
|
395
|
+
"required": ["id", "name"],
|
|
396
|
+
"properties": {
|
|
397
|
+
"id": {"type": "string"},
|
|
398
|
+
"name": {"type": "string"},
|
|
399
|
+
"args": {"type": "object", "additionalProperties": true}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
"limits": {
|
|
404
|
+
"type": "object",
|
|
405
|
+
"additionalProperties": false,
|
|
406
|
+
"properties": {
|
|
407
|
+
"timeoutSeconds": {"type": "number"},
|
|
408
|
+
"maxCalls": {"type": "number"}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
"sessionId": {"title": "Sessionid", "type": "string"},
|
|
349
412
|
"wait": {"default": true, "title": "Wait", "type": "boolean"},
|
|
350
413
|
"timeoutSeconds": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "title": "Timeoutseconds"},
|
|
351
414
|
"clientId": {"default": "", "title": "Clientid", "type": "string", "description": "Browser name or UUID. Required when multiple browsers are online. Offline or unknown selections never fall back to another browser."}
|
|
352
415
|
},
|
|
353
|
-
"required": ["
|
|
416
|
+
"required": ["sessionId"],
|
|
354
417
|
"title": "qingzhou_execute_toolArguments",
|
|
355
418
|
"type": "object"
|
|
356
419
|
}
|
|
@@ -372,12 +435,12 @@
|
|
|
372
435
|
},
|
|
373
436
|
{
|
|
374
437
|
"name": "qingzhou_get_tool",
|
|
375
|
-
"description": "Read
|
|
438
|
+
"description": "Read a tool by exact name without a session. Pass sessionId for a loaded Skill tool. Direct drive returns a compact signature and description; this call never creates a session.",
|
|
376
439
|
"inputSchema": {
|
|
377
440
|
"type": "object",
|
|
378
441
|
"properties": {
|
|
379
442
|
"name": {"type": "string", "minLength": 1},
|
|
380
|
-
"sessionId": {"type": "string"
|
|
443
|
+
"sessionId": {"type": "string"},
|
|
381
444
|
"timeoutSeconds": {"type": ["number", "null"], "default": null},
|
|
382
445
|
"clientId": {"type": "string", "default": ""}
|
|
383
446
|
},
|
package/src/cli-resources.mjs
CHANGED
|
@@ -11,9 +11,9 @@ export function settingsPatch(id, value, json = {}) {
|
|
|
11
11
|
try { parsed = JSON.parse(value); } catch { /* An unquoted CLI value is a string. */ }
|
|
12
12
|
config = { [id]: parsed };
|
|
13
13
|
}
|
|
14
|
-
if (!record(config) || !Object.keys(config).length) throw new QzError("
|
|
14
|
+
if (!record(config) || !Object.keys(config).length) throw new QzError("INVALID_ARGUMENTS", "Settings must be a non-empty JSON object.");
|
|
15
15
|
if (Object.keys(config).some(key => !key.trim() || ["__proto__", "prototype", "constructor"].includes(key))) {
|
|
16
|
-
throw new QzError("
|
|
16
|
+
throw new QzError("INVALID_ARGUMENTS", "Settings contain an invalid field name.");
|
|
17
17
|
}
|
|
18
18
|
return config;
|
|
19
19
|
}
|