@qqbrowser/openclaw-qbot 0.10.13 → 0.10.15
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/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +6 -6
- package/node_modules/@aws-sdk/client-bedrock-runtime/dist-cjs/index.js +1 -0
- package/node_modules/@aws-sdk/client-bedrock-runtime/dist-es/models/enums.js +1 -0
- package/node_modules/@aws-sdk/client-bedrock-runtime/package.json +13 -13
- package/node_modules/@aws-sdk/core/package.json +6 -6
- package/node_modules/@aws-sdk/credential-provider-env/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-http/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +13 -13
- package/node_modules/@aws-sdk/credential-provider-login/package.json +6 -6
- package/node_modules/@aws-sdk/credential-provider-node/dist-cjs/index.js +12 -1
- package/node_modules/@aws-sdk/credential-provider-node/dist-es/runtime/memoize-chain.js +12 -1
- package/node_modules/@aws-sdk/credential-provider-node/package.json +11 -11
- package/node_modules/@aws-sdk/credential-provider-process/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +6 -6
- package/node_modules/@aws-sdk/eventstream-handler-node/package.json +4 -4
- package/node_modules/@aws-sdk/middleware-eventstream/package.json +4 -4
- package/node_modules/@aws-sdk/middleware-websocket/package.json +7 -7
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/package.json +8 -8
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +4 -4
- package/node_modules/@aws-sdk/token-providers/package.json +6 -6
- package/node_modules/@aws-sdk/types/package.json +2 -2
- package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
- package/node_modules/@clack/core/dist/index.mjs +7 -7
- package/node_modules/@clack/core/package.json +2 -1
- package/node_modules/@clack/prompts/dist/index.mjs +38 -38
- package/node_modules/@clack/prompts/package.json +2 -2
- package/node_modules/@nodable/entities/package.json +1 -1
- package/node_modules/@nodable/entities/src/EntityDecoder.js +1 -1
- package/node_modules/@nodable/entities/src/entities.js +0 -18
- package/node_modules/@slack/bolt/dist/App.js +8 -12
- package/node_modules/@slack/bolt/dist/context/index.js +7 -7
- package/node_modules/@slack/bolt/dist/index.js +16 -16
- package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.js +2 -0
- package/node_modules/@slack/bolt/dist/receivers/ExpressReceiver.js +8 -1
- package/node_modules/@slack/bolt/dist/receivers/HTTPReceiver.js +4 -2
- package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.js +1 -1
- package/node_modules/@slack/bolt/dist/receivers/verify-request.js +3 -0
- package/node_modules/@slack/bolt/dist/receivers/verify-signing-secret.js +12 -0
- package/node_modules/@slack/bolt/dist/types/actions/index.js +1 -1
- package/node_modules/@slack/bolt/dist/types/index.js +3 -3
- package/node_modules/@slack/bolt/package.json +4 -4
- package/node_modules/@smithy/core/dist-cjs/index.js +3 -4
- package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +3 -11
- package/node_modules/@smithy/core/dist-cjs/submodules/config/index.browser.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/config/index.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/config/index.native.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.browser.js +9 -40
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.js +9 -40
- package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +12 -142
- package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.browser.js +39 -22
- package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.js +39 -22
- package/node_modules/@smithy/core/dist-cjs/submodules/schema/index.js +7 -9
- package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.browser.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.native.js +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/transport/index.js +184 -0
- package/node_modules/@smithy/core/dist-es/index.js +6 -6
- package/node_modules/@smithy/core/dist-es/submodules/client/index.js +2 -2
- package/node_modules/@smithy/core/dist-es/submodules/config/config-resolver/regionConfig/checkRegion.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/index.browser.js +2 -2
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/index.js +2 -2
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/middleware-endpoint/adaptors/toEndpointV1.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/middleware-endpoint/resolveEndpointConfig.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/lib/index.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/protocols/HttpBindingProtocol.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/protocols/HttpProtocol.js +1 -2
- package/node_modules/@smithy/core/dist-es/submodules/protocols/RpcProtocol.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/protocols/index.js +5 -5
- package/node_modules/@smithy/core/dist-es/submodules/protocols/middleware-content-length/contentLengthMiddleware.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/protocols/requestBuilder.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/configurations.js +19 -6
- package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/retryMiddleware.js +4 -5
- package/node_modules/@smithy/core/dist-es/submodules/retry/service-error-classification/constants.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/ConfiguredRetryStrategy.js +4 -5
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/DefaultRetryToken.js +3 -0
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/StandardRetryStrategy.js +9 -5
- package/node_modules/@smithy/core/dist-es/submodules/schema/middleware/schemaDeserializationMiddleware.js +3 -4
- package/node_modules/@smithy/core/dist-es/submodules/schema/middleware/schemaSerializationMiddleware.js +1 -2
- package/node_modules/@smithy/core/dist-es/submodules/serde/middleware-serde/deserializerMiddleware.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/transport/index.js +9 -0
- package/node_modules/@smithy/core/dist-es/submodules/{protocols/url-parser → transport}/parseUrl.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/{endpoints → transport}/toEndpointV1.js +1 -1
- package/node_modules/@smithy/core/package.json +20 -11
- package/node_modules/@smithy/core/transport.js +5 -0
- package/node_modules/@smithy/credential-provider-imds/dist-cjs/index.js +14 -13
- package/node_modules/@smithy/credential-provider-imds/dist-es/fromContainerMetadata.js +14 -13
- package/node_modules/@smithy/credential-provider-imds/package.json +3 -3
- package/node_modules/@smithy/fetch-http-handler/package.json +4 -4
- package/node_modules/@smithy/node-http-handler/package.json +4 -4
- package/node_modules/@smithy/signature-v4/package.json +3 -3
- package/node_modules/@smithy/types/package.json +1 -1
- package/node_modules/eventsource-parser/dist/index.cjs +21 -10
- package/node_modules/eventsource-parser/dist/index.d.cts +33 -10
- package/node_modules/eventsource-parser/dist/index.js +21 -10
- package/node_modules/eventsource-parser/dist/stream.cjs +4 -3
- package/node_modules/eventsource-parser/dist/stream.d.cts +16 -3
- package/node_modules/eventsource-parser/dist/stream.js +4 -3
- package/node_modules/eventsource-parser/package.json +8 -8
- package/node_modules/hasown/package.json +4 -5
- package/node_modules/lru-cache/package.json +1 -1
- package/node_modules/protobufjs/dist/light/protobuf.js +7 -5
- package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/minimal/protobuf.js +3 -3
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/protobuf.js +7 -5
- package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
- package/node_modules/protobufjs/package.json +1 -1
- package/node_modules/protobufjs/src/converter.js +4 -2
- package/node_modules/protobufjs/src/roots.js +1 -1
- package/node_modules/thread-stream/.claude/settings.local.json +15 -0
- package/node_modules/thread-stream/CLAUDE.md +64 -0
- package/node_modules/thread-stream/index.js +41 -13
- package/node_modules/thread-stream/lib/indexes.js +3 -1
- package/node_modules/thread-stream/lib/worker.js +20 -8
- package/node_modules/thread-stream/package.json +1 -1
- package/node_modules/undici/lib/global.js +10 -1
- package/node_modules/undici/package.json +1 -1
- package/package.json +1 -1
- package/skills/qqbrowser-playbook/SKILL.md +262 -234
- package/skills/qqbrowser-skill/SKILL.md +330 -234
- package/node_modules/@smithy/core/dist-cjs/getSmithyContext.js +0 -6
- package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/getHttpAuthSchemeEndpointRuleSetPlugin.js +0 -21
- package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/getHttpAuthSchemePlugin.js +0 -21
- package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/httpAuthSchemeMiddleware.js +0 -46
- package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/index.js +0 -6
- package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/resolveAuthOptions.js +0 -24
- package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/getHttpSigningMiddleware.js +0 -19
- package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/httpSigningMiddleware.js +0 -27
- package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/index.js +0 -5
- package/node_modules/@smithy/core/dist-cjs/normalizeProvider.js +0 -10
- package/node_modules/@smithy/core/dist-cjs/pagination/createPaginator.js +0 -44
- package/node_modules/@smithy/core/dist-cjs/request-builder/requestBuilder.js +0 -5
- package/node_modules/@smithy/core/dist-cjs/setFeature.js +0 -14
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/DefaultIdentityProviderConfig.js +0 -18
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/httpApiKeyAuth.js +0 -38
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/httpBearerAuth.js +0 -15
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/index.js +0 -6
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/noAuth.js +0 -9
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/index.js +0 -6
- package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/memoizeIdentityProvider.js +0 -61
- package/node_modules/@smithy/core/dist-es/request-builder/requestBuilder.js +0 -1
- package/node_modules/@smithy/core/dist-es/submodules/client/util-middleware/getSmithyContext.js +0 -2
- package/node_modules/@smithy/core/dist-es/submodules/event-streams/eventstream-codec/TestVectors.fixture.js +0 -146
- package/node_modules/@smithy/core/dist-es/submodules/event-streams/eventstream-codec/vectorTypes.fixture.js +0 -1
- package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/index.js +0 -2
- package/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +0 -21
- package/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +0 -88
- package/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +0 -21
- package/node_modules/@smithy/signature-v4/dist-es/suite.fixture.js +0 -399
- /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/getHttpAuthSchemeEndpointRuleSetPlugin.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/getHttpAuthSchemePlugin.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/httpAuthSchemeMiddleware.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/index.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/resolveAuthOptions.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/getHttpSigningMiddleware.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/httpSigningMiddleware.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/index.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{pagination → legacy-root-exports/pagination}/createPaginator.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/DefaultIdentityProviderConfig.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/httpApiKeyAuth.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/httpBearerAuth.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/index.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/noAuth.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/index.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/memoizeIdentityProvider.js +0 -0
- /package/node_modules/@smithy/core/dist-es/{getSmithyContext.js → submodules/transport/getSmithyContext.js} +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/httpRequest.js +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/httpResponse.js +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{endpoints/util-endpoints/lib → transport}/isValidHostLabel.js +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/isValidHostname.js +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{client/util-middleware → transport}/normalizeProvider.js +0 -0
- /package/node_modules/@smithy/core/dist-es/submodules/{protocols/querystring-parser → transport}/parseQueryString.js +0 -0
|
@@ -10,293 +10,321 @@ permissions:
|
|
|
10
10
|
|
|
11
11
|
# qqbrowser-playbook
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Convert raw `qqbrowser-skill` task recordings into reusable, parameterized replay scripts.
|
|
14
|
+
|
|
15
|
+
## Critical Rules
|
|
16
|
+
|
|
17
|
+
1. A playbook **MUST** be generated from actual task recording (`task_latest`). NEVER fabricate steps.
|
|
18
|
+
2. Only use actions from `task_latest` — copy action names and index values **verbatim**.
|
|
19
|
+
3. Only replace **user data** (`text`, `url`, file path) with `{{param_name}}`. NEVER replace `action` or `index`.
|
|
20
|
+
4. Save to `~/.qqbrowser-skill/playbooks/<kebab-case-name>.json`. Create dir first: `mkdir -p ~/.qqbrowser-skill/playbooks`.
|
|
21
|
+
5. JSON structure: `version` + `metadata` + `params` + `settings` + `steps`. Key is `params` not `parameters`.
|
|
22
|
+
|
|
23
|
+
## Valid Actions
|
|
24
|
+
|
|
25
|
+
### ✅ Allowed (replayable)
|
|
26
|
+
|
|
27
|
+
| Action | Parameterizable Fields |
|
|
28
|
+
|--------|----------------------|
|
|
29
|
+
| `browser_go_to_url` | `url` |
|
|
30
|
+
| `browser_click_element` / `browser_dblclick_element` / `browser_focus_element` | — |
|
|
31
|
+
| `browser_input_text` / `browser_keyboard_op` | `text` |
|
|
32
|
+
| `browser_keypress` / `browser_keydown` / `browser_keyup` | — |
|
|
33
|
+
| `browser_select_dropdown_option` | `text` |
|
|
34
|
+
| `browser_check_op` | — |
|
|
35
|
+
| `browser_scroll_down` / `browser_scroll_up` / `browser_scroll_to_top` / `browser_scroll_to_bottom` / `browser_scroll_by` / `browser_scroll_into_view` | — |
|
|
36
|
+
| `browser_scroll_to_text` | `text` |
|
|
37
|
+
| `browser_wait` | — |
|
|
38
|
+
| `browser_go_back` | — |
|
|
39
|
+
| `browser_dialog` | `text` |
|
|
40
|
+
| `browser_tab_open` | `url`, `as` (alias). Use semantic refs, NOT physical `tabId` |
|
|
41
|
+
| `browser_tab_switch` / `browser_tab_close` | `tab` (`"origin"` / `"current"` / `"latest"` / custom alias) |
|
|
42
|
+
| `browser_download_file` / `browser_download_url` | — |
|
|
43
|
+
| `browser_eval_content_js` | `script` |
|
|
44
|
+
| `browser_find_and_act` | `value`, `actionValue`, `name`, `nth` |
|
|
45
|
+
| `loop` | `count`, nested `steps` |
|
|
46
|
+
|
|
47
|
+
### ❌ Excluded (filter out from recording)
|
|
48
|
+
|
|
49
|
+
`browser_snapshot`, `browser_screenshot`, `browser_get_info`, `browser_check_state`, `browser_get_dropdown_options`, `browser_tab_list`, `task_begin`/`task_end`/`task_latest`, `browser_done`
|
|
50
|
+
|
|
51
|
+
## Playbook JSON Format
|
|
14
52
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Playbooks are stored in the `playbooks` subdirectory under the qqbrowser-skill data directory (`DEFAULT_DATA_DIR/playbooks/`). The data directory follows the same convention as `qqbrowser-skill`:
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"version": "1.0",
|
|
56
|
+
"metadata": {
|
|
57
|
+
"name": "小红书发布笔记",
|
|
58
|
+
"description": "自动打开小红书并发布一篇笔记",
|
|
59
|
+
"created_at": "2026-05-21T16:59:00Z",
|
|
60
|
+
"url": "https://www.xiaohongshu.com/explore",
|
|
61
|
+
"requires_ai": false
|
|
62
|
+
},
|
|
63
|
+
"params": {
|
|
64
|
+
"notes_title": { "description": "笔记标题", "required": true },
|
|
65
|
+
"notes_content": { "description": "笔记正文内容", "required": true }
|
|
66
|
+
},
|
|
67
|
+
"settings": { "default_delay_ms": 1000, "default_retry_count": 1, "step_timeout_ms": 30000 },
|
|
68
|
+
"steps": [
|
|
69
|
+
{ "action": "browser_go_to_url", "params": { "url": "https://www.xiaohongshu.com/explore" }, "description": "打开小红书探索页" },
|
|
70
|
+
{ "action": "browser_input_text", "params": { "index": "6_2vl4_txwg", "text": "{{notes_title}}" }, "description": "输入笔记标题" },
|
|
71
|
+
{ "action": "browser_click_element", "params": { "index": "19_oxgq_ttmr" }, "description": "点击发布" }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
38
75
|
|
|
39
|
-
|
|
40
|
-
|----------|-------------------|
|
|
41
|
-
| Linux / macOS | `~/.qqbrowser-skill/playbooks/` |
|
|
42
|
-
| Windows | `%LOCALAPPDATA%/qqbrowser-skill/playbooks/` |
|
|
76
|
+
**Fields**: `version`(always "1.0"), `metadata.name`, `metadata.description`, `metadata.created_at`(ISO8601) are required. `metadata.url`, `metadata.requires_ai`(default false) are optional. Each param needs `description`(string) and `required`(bool), optional `default`. Steps need `action`, `params`, `description`; optional `condition: {"param":"xx"}` for conditional execution.
|
|
43
77
|
|
|
44
|
-
|
|
78
|
+
## Parameter Extraction
|
|
45
79
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
80
|
+
**Parameterize**: user text, emails, usernames, quantities, file paths, user-specific URL segments.
|
|
81
|
+
**Never parameterize**: `index`, `action`, fixed workflow URLs, fixed UI buttons, `settings`.
|
|
82
|
+
**Naming**: `snake_case`, descriptive (`notes_title` not `title`).
|
|
49
83
|
|
|
50
|
-
|
|
51
|
-
$PLAYBOOKS_DIR = "$env:LOCALAPPDATA/qqbrowser-skill/playbooks"
|
|
52
|
-
```
|
|
84
|
+
## Conditional Steps
|
|
53
85
|
|
|
54
|
-
|
|
86
|
+
Add `condition: {"param": "cc"}` to ALL steps dependent on an optional param. If param absent, step is skipped.
|
|
55
87
|
|
|
56
|
-
##
|
|
88
|
+
## Loop Steps
|
|
57
89
|
|
|
58
|
-
|
|
90
|
+
Use `loop` when recording shows **2+ repetitions** of the same action sequence differing only in target element, and count is user-controlled.
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
qqbrowser-skill task_latest
|
|
62
|
-
```
|
|
92
|
+
**Do NOT use loop** for fixed form fields or when a single `browser_eval_content_js` can extract all data at once.
|
|
63
93
|
|
|
64
|
-
|
|
94
|
+
### Loop JSON
|
|
65
95
|
|
|
66
96
|
```json
|
|
67
97
|
{
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
{"skill_name": "browser_click_element", "params": {"index": "154_17q6_1w8c"}},
|
|
73
|
-
{"skill_name": "browser_click_element", "params": {"index": "7_ek3h_2b3r"}},
|
|
74
|
-
{"skill_name": "browser_click_element", "params": {"index": "10_4bq5_u01g"}},
|
|
75
|
-
{"skill_name": "browser_input_text", "params": {"index": "6_2vl4_txwg", "text": "我的笔记标题"}},
|
|
76
|
-
{"skill_name": "browser_input_text", "params": {"index": "4_ku44_rrpt", "text": "这是笔记的正文内容..."}},
|
|
77
|
-
{"skill_name": "browser_click_element", "params": {"index": "19_oxgq_ttmr"}}
|
|
78
|
-
],
|
|
79
|
-
"started_at": "2026-05-21T16:59:00Z",
|
|
80
|
-
"ended_at": "2026-05-21T17:01:30Z"
|
|
98
|
+
"action": "loop",
|
|
99
|
+
"params": { "count": "{{count}}", "variable": "i", "start": 1 },
|
|
100
|
+
"description": "逐个访问前N篇文章",
|
|
101
|
+
"steps": [ ... ]
|
|
81
102
|
}
|
|
82
103
|
```
|
|
83
104
|
|
|
84
|
-
|
|
105
|
+
Fields: `count`(string/number, supports `{{param}}`), `variable`(loop var name), `start`(default 1, 1-based for `nth`).
|
|
85
106
|
|
|
86
|
-
|
|
107
|
+
### Key Rules for Loop Steps
|
|
87
108
|
|
|
88
|
-
1. **
|
|
89
|
-
2.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
5.
|
|
97
|
-
6. **
|
|
109
|
+
1. **Use `browser_find_and_act` with `by: "css"` + `nth: "{{i}}"`** inside loops — NOT `browser_click_element` with hardcoded index.
|
|
110
|
+
2. **`by` MUST be `"css"` when `nth` is used** — NEVER `by: "text"` or `by: "label"` with `nth`. Text/label is for single unique elements, not list iteration.
|
|
111
|
+
3. **`value` must be a list-level CSS selector** matching ALL items (e.g., `.ContentItem h2 a`), NOT specific text content.
|
|
112
|
+
4. **CSS selector MUST come from actual DOM**, with this priority:
|
|
113
|
+
- **Best**: Extract from `__list_probe__` in recording (see Step 2b)
|
|
114
|
+
- **Good**: Use `by: "css"` selector already in recording's `find_and_act`
|
|
115
|
+
- **Fallback**: Use `browser_get_info --type list_selector --index <id>` to auto-detect, or run `browser_eval_content_js` probe to discover (before generating playbook)
|
|
116
|
+
- **NEVER**: Fabricate or guess. If all above fail, set `metadata.requires_ai: true`.
|
|
117
|
+
5. **`{{i}}` is 1-based** (start=1), aligning with `nth` (nth=1 = first element).
|
|
118
|
+
6. **Check recording for tab operations in each iteration** — if recording has `tab_switch`/`tab_close`, loop body MUST use `tab_switch("latest")` → ... → `tab_close("current")` → `tab_switch("origin")`. **NEVER silently convert tab-based recording to `go_back`.**
|
|
119
|
+
7. **Use `browser_go_back` ONLY** if iteration stays in the **same tab** (no tab ops in recording).
|
|
120
|
+
8. **Include `browser_wait`** between iterations for stability.
|
|
98
121
|
|
|
99
|
-
|
|
100
|
-
- Recording `"text": "我的搜索词"` → Playbook `"text": "{{search_keyword}}"`
|
|
101
|
-
- Everything else (action names, index values, key names) stays **exactly** the same.
|
|
122
|
+
### Decision Tables
|
|
102
123
|
|
|
103
|
-
|
|
124
|
+
**Loop vs Single JS Extraction:**
|
|
104
125
|
|
|
105
|
-
|
|
126
|
+
| Scenario | Method |
|
|
127
|
+
|----------|--------|
|
|
128
|
+
| List page data only | Single `browser_eval_content_js` |
|
|
129
|
+
| Need detail page content | `loop` (navigate in/out per item) |
|
|
130
|
+
| Pagination + extract | `loop` |
|
|
131
|
+
| Fixed different form fields | Linear steps |
|
|
106
132
|
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
"version": "1.0",
|
|
110
|
-
"metadata": {
|
|
111
|
-
"name": "小红书发布长文笔记",
|
|
112
|
-
"description": "自动打开小红书,发布一篇长文笔记并暂存离开",
|
|
113
|
-
"created_at": "2026-05-21T16:59:00Z",
|
|
114
|
-
"url": "https://www.xiaohongshu.com/explore"
|
|
115
|
-
},
|
|
116
|
-
"params": {
|
|
117
|
-
"notes_title": {
|
|
118
|
-
"description": "笔记标题",
|
|
119
|
-
"required": true
|
|
120
|
-
},
|
|
121
|
-
"notes_content": {
|
|
122
|
-
"description": "笔记正文内容",
|
|
123
|
-
"required": true
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"settings": {
|
|
127
|
-
"default_delay_ms": 1000,
|
|
128
|
-
"default_retry_count": 1,
|
|
129
|
-
"step_timeout_ms": 30000
|
|
130
|
-
},
|
|
131
|
-
"steps": [
|
|
132
|
-
{
|
|
133
|
-
"action": "browser_go_to_url",
|
|
134
|
-
"params": { "url": "https://www.xiaohongshu.com/explore" },
|
|
135
|
-
"description": "打开小红书探索页"
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"action": "browser_click_element",
|
|
139
|
-
"params": { "index": "154_17q6_1w8c" },
|
|
140
|
-
"description": "点击发布按钮"
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"action": "browser_click_element",
|
|
144
|
-
"params": { "index": "7_ek3h_2b3r" },
|
|
145
|
-
"description": "选择写长文"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"action": "browser_click_element",
|
|
149
|
-
"params": { "index": "10_4bq5_u01g" },
|
|
150
|
-
"description": "点击新的创作"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"action": "browser_input_text",
|
|
154
|
-
"params": { "index": "6_2vl4_txwg", "text": "{{notes_title}}" },
|
|
155
|
-
"description": "输入笔记标题"
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
"action": "browser_input_text",
|
|
159
|
-
"params": { "index": "4_ku44_rrpt", "text": "{{notes_content}}" },
|
|
160
|
-
"description": "输入笔记正文"
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
"action": "browser_click_element",
|
|
164
|
-
"params": { "index": "19_oxgq_ttmr" },
|
|
165
|
-
"description": "点击暂存离开"
|
|
166
|
-
}
|
|
167
|
-
]
|
|
168
|
-
}
|
|
169
|
-
```
|
|
133
|
+
**`go_back` vs `tab_close` + `tab_switch`:**
|
|
170
134
|
|
|
171
|
-
|
|
135
|
+
| Scenario | Method |
|
|
136
|
+
|----------|--------|
|
|
137
|
+
| Link opens in **same tab** | `browser_go_back` |
|
|
138
|
+
| Link opens in **new tab** | `browser_tab_close` + `browser_tab_switch` |
|
|
139
|
+
| Unsure | Prefer `tab_close` + `tab_switch` (safer) |
|
|
172
140
|
|
|
173
|
-
|
|
174
|
-
|-------|------|----------|-------------|
|
|
175
|
-
| `version` | string | Yes | Always `"1.0"` |
|
|
176
|
-
| `metadata.name` | string | Yes | Human-readable name of the playbook |
|
|
177
|
-
| `metadata.description` | string | Yes | What this playbook does |
|
|
178
|
-
| `metadata.created_at` | string | Yes | ISO 8601 timestamp of creation |
|
|
179
|
-
| `metadata.url` | string | No | The starting URL of the automation |
|
|
180
|
-
| `params` | object | No | Parameter definitions (omit if no parameters needed) |
|
|
181
|
-
| `params.<key>.description` | string | Yes | What this parameter is for |
|
|
182
|
-
| `params.<key>.required` | boolean | Yes | Whether the parameter must be provided |
|
|
183
|
-
| `params.<key>.default` | string | No | Default value if not provided |
|
|
184
|
-
| `settings.default_delay_ms` | number | Yes | Delay between steps in milliseconds |
|
|
185
|
-
| `settings.default_retry_count` | number | Yes | Number of retries on step failure |
|
|
186
|
-
| `settings.step_timeout_ms` | number | Yes | Timeout for each step |
|
|
187
|
-
| `steps[].action` | string | Yes | The browser action (must have `browser_` prefix) |
|
|
188
|
-
| `steps[].params` | object | Yes | Parameters for the action |
|
|
189
|
-
| `steps[].description` | string | Yes | Human-readable step description |
|
|
141
|
+
## Tab Management
|
|
190
142
|
|
|
191
|
-
|
|
143
|
+
Playbooks **MUST use semantic tab references** — physical `tabId` changes every session.
|
|
192
144
|
|
|
193
|
-
|
|
145
|
+
| Reference | Meaning |
|
|
146
|
+
|-----------|---------|
|
|
147
|
+
| `"origin"` | Tab active when replay started (always available) |
|
|
148
|
+
| `"current"` | Currently active tab (dynamic) |
|
|
149
|
+
| `"latest"` | Most recently opened tab (after any `tab_open`) |
|
|
150
|
+
| Custom alias | Named via `"as"` param in `tab_open` |
|
|
194
151
|
|
|
195
|
-
|
|
152
|
+
**Tab action examples:**
|
|
153
|
+
```json
|
|
154
|
+
{ "action": "browser_tab_open", "params": { "url": "https://example.com", "as": "detail" } }
|
|
155
|
+
{ "action": "browser_tab_switch", "params": { "tab": "origin" } }
|
|
156
|
+
{ "action": "browser_tab_close", "params": { "tab": "current" } }
|
|
157
|
+
```
|
|
196
158
|
|
|
197
|
-
|
|
159
|
+
**Rules:**
|
|
160
|
+
1. NEVER use physical `tabId` — always semantic references.
|
|
161
|
+
2. `browser_go_back` does NOT work across tabs — use `tab_close` + `tab_switch`.
|
|
162
|
+
3. After `tab_close`, explicitly `tab_switch` — don't assume Chrome's active tab.
|
|
198
163
|
|
|
199
|
-
|
|
164
|
+
## Workflow
|
|
200
165
|
|
|
201
166
|
```bash
|
|
202
|
-
# 1. Get
|
|
167
|
+
# 1. Get recording
|
|
203
168
|
qqbrowser-skill task_latest
|
|
204
169
|
|
|
205
|
-
# 2. AI analyzes
|
|
170
|
+
# 2. AI analyzes → generates playbook:
|
|
171
|
+
# 2a: Filter excluded actions (snapshot, screenshot, tab_list, etc.)
|
|
172
|
+
# 2b: Extract CSS selector from __list_probe__ (see below)
|
|
173
|
+
# 2c: Detect loop patterns (repeated sequences → loop, using selector from 2b)
|
|
174
|
+
# 2d: Detect tab patterns (tabId → semantic refs). NEVER silently drop tab ops.
|
|
175
|
+
# 2e: Cross-check loop+tab — if loop iteration has tab ops, use tab_switch/close NOT go_back
|
|
176
|
+
# 2f: Identify parameterizable values → {{param}}
|
|
177
|
+
# 2g: Add descriptions, metadata, settings
|
|
178
|
+
|
|
179
|
+
# 3. Save
|
|
180
|
+
mkdir -p ~/.qqbrowser-skill/playbooks
|
|
181
|
+
# write_to_file tool
|
|
182
|
+
|
|
183
|
+
# 4. Run
|
|
184
|
+
# ⚠️ browser_replay can take UP TO 10 MINUTES for complex playbooks with loops.
|
|
185
|
+
# You MUST wait patiently for the command to finish. Do NOT assume it has timed out.
|
|
186
|
+
qqbrowser-skill browser_replay --script ~/.qqbrowser-skill/playbooks/<name>.json \
|
|
187
|
+
--variables '{"param1": "value1"}'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> **⚠️ IMPORTANT**: `browser_replay` executes all playbook steps sequentially in the browser. Depending on step count, delays, and network conditions, **execution can take up to 10 minutes**. Wait for the command to return a result before proceeding — do NOT interrupt or retry prematurely.
|
|
206
191
|
|
|
207
|
-
|
|
208
|
-
# Linux / macOS:
|
|
209
|
-
mkdir -p "$HOME/.qqbrowser-skill/playbooks"
|
|
210
|
-
# Windows (PowerShell):
|
|
211
|
-
New-Item -ItemType Directory -Force -Path "$env:LOCALAPPDATA/qqbrowser-skill/playbooks"
|
|
192
|
+
### Step 2b: Extract CSS Selector from Recording
|
|
212
193
|
|
|
213
|
-
|
|
194
|
+
**Primary source**: Look for `browser_eval_content_js` calls whose output contains `__list_probe__: true`.
|
|
195
|
+
```
|
|
196
|
+
Recording data example:
|
|
197
|
+
browser_eval_content_js → output: {"__list_probe__": true, "selector": ".SearchResult-Card .ContentItem h2 a", "count": 10, "samples": ["文章A", "文章B", "文章C"]}
|
|
214
198
|
```
|
|
199
|
+
Extract the `selector` value — this is the **verified CSS selector** from actual DOM, use it directly in the loop's `find_and_act`.
|
|
215
200
|
|
|
216
|
-
|
|
201
|
+
**Fallback** (if no `__list_probe__` in recording):
|
|
202
|
+
1. Check if recording's `find_and_act` already uses `by: "css"` — use that selector directly
|
|
203
|
+
2. If recording only has `by: "text"` or `click_element(index)`, the selector is unknown
|
|
204
|
+
3. Use `browser_get_info --type list_selector --index <any_list_item_index>` to auto-detect the selector
|
|
205
|
+
4. If `list_selector` also fails, run `browser_eval_content_js` with a probe script to discover the selector
|
|
206
|
+
5. **NEVER** guess or fabricate selectors
|
|
217
207
|
|
|
218
|
-
|
|
219
|
-
# Linux / macOS
|
|
220
|
-
ls ~/.qqbrowser-skill/playbooks/
|
|
208
|
+
### Step 2c: Loop Pattern Detection
|
|
221
209
|
|
|
222
|
-
|
|
223
|
-
|
|
210
|
+
```
|
|
211
|
+
1. Group consecutive steps into sequences
|
|
212
|
+
2. If 2+ adjacent sequences share the SAME action pattern → convert to `loop`
|
|
213
|
+
3. What varies between iterations:
|
|
214
|
+
- `nth` increments → use `{{i}}`
|
|
215
|
+
- Different index values → replace with `find_and_act` + `nth: "{{i}}"`
|
|
216
|
+
- Different text/title in `find_and_act(by="text")` → convert to `by: "css"` + `nth: "{{i}}"`
|
|
217
|
+
4. Use the CSS selector from Step 2b (from __list_probe__ or fallback probe)
|
|
218
|
+
5. Convert `browser_click_element(index=...)` → `find_and_act(by="css", nth="{{i}}")`
|
|
219
|
+
6. Count source: user-mentioned quantity → `{{count}}` param; fixed → literal
|
|
224
220
|
```
|
|
225
221
|
|
|
226
|
-
|
|
222
|
+
**Recording patterns → playbook conversion:**
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
| Recording Pattern | Conversion |
|
|
225
|
+
|---|---|
|
|
226
|
+
| Repeated sequences with different text targets | `loop` + `by: "css"` + `nth: "{{i}}"` |
|
|
227
|
+
| Repeated sequences with different `index` values | `loop` + `by: "css"` + `nth: "{{i}}"` |
|
|
228
|
+
| `find_and_act(by="text", value="标题A")` → ... → `find_and_act(by="text", value="标题B")` | **MUST** convert `by: "text"` → `by: "css"` + `nth: "{{i}}"` |
|
|
229
|
+
|
|
230
|
+
### Step 2d: Tab Pattern Detection
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
1. `browser_tab_open` → discard physical tabId, add semantic `"as"` alias
|
|
234
|
+
2. `browser_tab_switch(tabId=N)` → replace with "origin"/"latest"/custom alias
|
|
235
|
+
3. `browser_tab_close(tabId=N)` → replace with semantic ref
|
|
236
|
+
4. `browser_go_back` after new tab → replace with tab_close("current") + tab_switch("origin")
|
|
230
237
|
```
|
|
231
238
|
|
|
232
|
-
|
|
239
|
+
## Complete Example: Loop + New Tab
|
|
233
240
|
|
|
234
|
-
|
|
241
|
+
This example demonstrates ALL key conversions (text→css, tabId→semantic, linear→loop):
|
|
235
242
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
Recording (raw):
|
|
244
|
+
```
|
|
245
|
+
eval_content_js → {"__list_probe__": true, "selector": ".ContentItem h2 a", "count": 10, "samples": ["文章标题A","文章标题B","文章标题C"]}
|
|
246
|
+
find_and_act(by="text", value="文章标题A", action="click")
|
|
247
|
+
click_element(index="197_3i4c_uv7n") ← opened new tab
|
|
248
|
+
tab_switch(tabId=1412776911)
|
|
249
|
+
eval_content_js(script="...")
|
|
250
|
+
tab_close(tabId=1412776911)
|
|
251
|
+
find_and_act(by="text", value="文章标题B", action="click")
|
|
252
|
+
click_element(index="206_90r2_rddo")
|
|
253
|
+
tab_switch(tabId=1412776913)
|
|
254
|
+
eval_content_js(script="...")
|
|
255
|
+
tab_close(tabId=1412776913)
|
|
256
|
+
```
|
|
239
257
|
|
|
240
|
-
|
|
241
|
-
|
|
258
|
+
Playbook generator extracts selector `.ContentItem h2 a` from the `__list_probe__` line.
|
|
259
|
+
|
|
260
|
+
Playbook (generated):
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"version": "1.0",
|
|
264
|
+
"metadata": {
|
|
265
|
+
"name": "搜索并提取文章(新Tab场景)",
|
|
266
|
+
"description": "搜索主题,逐个在新Tab打开文章提取内容",
|
|
267
|
+
"created_at": "2026-06-02T15:00:00Z",
|
|
268
|
+
"requires_ai": false
|
|
269
|
+
},
|
|
270
|
+
"params": {
|
|
271
|
+
"topic": { "description": "搜索关键词", "required": true },
|
|
272
|
+
"count": { "description": "提取数量", "required": false, "default": "3" }
|
|
273
|
+
},
|
|
274
|
+
"settings": { "default_delay_ms": 2000, "default_retry_count": 1, "step_timeout_ms": 30000 },
|
|
275
|
+
"steps": [
|
|
276
|
+
{ "action": "browser_go_to_url", "params": { "url": "https://example.com/search?q={{topic}}" }, "description": "搜索主题" },
|
|
277
|
+
{ "action": "browser_wait", "params": { "seconds": 3 }, "description": "等待搜索结果" },
|
|
278
|
+
{
|
|
279
|
+
"action": "loop",
|
|
280
|
+
"params": { "count": "{{count}}", "variable": "i", "start": 1 },
|
|
281
|
+
"description": "逐个在新Tab打开文章并提取",
|
|
282
|
+
"steps": [
|
|
283
|
+
{ "action": "browser_find_and_act", "params": { "by": "css", "value": ".ContentItem h2 a", "nth": "{{i}}", "action": "click" }, "description": "点击第{{i}}篇文章" },
|
|
284
|
+
{ "action": "browser_tab_switch", "params": { "tab": "latest" }, "description": "切到文章Tab" },
|
|
285
|
+
{ "action": "browser_wait", "params": { "seconds": 2 }, "description": "等待加载" },
|
|
286
|
+
{ "action": "browser_eval_content_js", "params": { "script": "JSON.stringify({title:document.querySelector('h1')?.textContent?.trim(),content:document.querySelector('.RichText')?.textContent?.substring(0,2000)})" }, "description": "提取内容" },
|
|
287
|
+
{ "action": "browser_tab_close", "params": { "tab": "current" }, "description": "关闭文章Tab" },
|
|
288
|
+
{ "action": "browser_tab_switch", "params": { "tab": "origin" }, "description": "切回列表页" },
|
|
289
|
+
{ "action": "browser_wait", "params": { "seconds": 1 }, "description": "等待恢复" }
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
242
294
|
```
|
|
243
295
|
|
|
244
|
-
|
|
296
|
+
**Key conversions:** `by:"text"`+title → `by:"css"`+`nth:"{{i}}"` | `click_element(index)` → merged into `find_and_act` | `tabId:N` → `"latest"`/`"current"`/`"origin"` | 2× linear → `loop`
|
|
245
297
|
|
|
246
|
-
|
|
298
|
+
## `requires_ai` Tasks
|
|
247
299
|
|
|
248
|
-
|
|
249
|
-
- **User-generated text content**: titles, messages, descriptions, comments, posts
|
|
250
|
-
- **User-specific data**: usernames, email addresses, phone numbers, passwords
|
|
251
|
-
- **Search queries**: any text entered into search boxes
|
|
252
|
-
- **File paths**: paths to files being uploaded
|
|
253
|
-
- **Variable URLs**: URLs that contain user-specific identifiers or change per use
|
|
300
|
+
Set `metadata.requires_ai: true` when recording contains AI-dependent steps (summarization, comparison, conditional decisions). Include only replayable portions as steps.
|
|
254
301
|
|
|
255
|
-
|
|
256
|
-
- **Fixed navigation URLs**: the starting URL of the workflow (e.g., `https://www.xiaohongshu.com/explore`)
|
|
257
|
-
- **Element indices**: the `index` field in click/input actions (these are structural, not data)
|
|
258
|
-
- **UI interaction steps**: clicks on fixed buttons like "Submit", "Next", "Save"
|
|
259
|
-
- **Fixed option selections**: dropdown choices that are always the same for this workflow
|
|
302
|
+
## `browser_find_and_act` Usage
|
|
260
303
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
- Be descriptive: `notes_title` not `title`, `search_query` not `q`
|
|
264
|
-
- Group related params with common prefix: `login_username`, `login_password`
|
|
304
|
+
For single-element targeting (NOT in loops): `by: "text"` or `by: "label"` with known stable text.
|
|
305
|
+
For list iteration (in loops): `by: "css"` + `nth: "{{i}}"` with list-level selector.
|
|
265
306
|
|
|
266
|
-
##
|
|
307
|
+
## `browser_eval_content_js` Usage
|
|
267
308
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
1. Run: qqbrowser-skill task_latest
|
|
273
|
-
2. Analyze the returned commands
|
|
274
|
-
3. Identify that browser_input_text steps contain user data that should be parameterized
|
|
275
|
-
4. Generate the playbook JSON with params and {{placeholders}}
|
|
276
|
-
5. Determine the correct playbooks directory for the current platform
|
|
277
|
-
6. Ensure the playbooks directory exists (mkdir -p)
|
|
278
|
-
7. Write the JSON file to <playbooks_dir>/<name>.json
|
|
279
|
-
8. Report to the user: playbook name, parameters defined, file path, and how to run it
|
|
280
|
-
```
|
|
309
|
+
Primary data extraction method. Supports `{{param}}` in script for selectors, counts, keywords.
|
|
310
|
+
Selector priority: `id` > `data-*` > ARIA > semantic class > structural path. NEVER use dynamic/hashed classes.
|
|
311
|
+
|
|
312
|
+
## Common Mistakes
|
|
281
313
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
|
293
|
-
|
|
294
|
-
|
|
|
295
|
-
|
|
|
296
|
-
|
|
|
297
|
-
|
|
|
298
|
-
|
|
|
299
|
-
| Saving to wrong directory (e.g. `~/.qbotclaw/workspace/`) | Playbooks have a designated storage path | Always save to `~/.qqbrowser-skill/playbooks/` |
|
|
300
|
-
| Using flat JSON format (top-level `name`, `parameters`, `steps`) | The schema requires nested structure | Must use `metadata.name`, `params`, `settings`, `steps` structure |
|
|
301
|
-
| Using `parameters` key instead of `params` | The schema key is `params` | Use `params` with `description` and `required` fields |
|
|
302
|
-
| Using `write content=` shell command to save | May corrupt JSON formatting | Use `write_to_file` tool with proper path |
|
|
314
|
+
| Mistake | Fix |
|
|
315
|
+
|---------|-----|
|
|
316
|
+
| Including `browser_snapshot` etc. in steps | Filter out — AI-decision only |
|
|
317
|
+
| Fabricating steps without `task_latest` | Always base on actual recording |
|
|
318
|
+
| Parameterizing `index` or `action` | Only parameterize value fields (`text`, `url`, `script`) |
|
|
319
|
+
| Using `parameters` instead of `params` | Schema key is `params` |
|
|
320
|
+
| Expanding N repetitions as linear steps | Use `loop` with `{{count}}` |
|
|
321
|
+
| Using `click_element` + hardcoded index in loops | Use `find_and_act` + `nth: "{{i}}"` |
|
|
322
|
+
| Using `by: "text"`/`"label"` with `nth` in loops | `nth` requires `by: "css"` |
|
|
323
|
+
| Keeping `by: "text"` + specific content in loops | Recording's `find_and_act(text, "标题")` MUST convert to `by: "css"` + `nth` in loop |
|
|
324
|
+
| Fabricating CSS selectors | Must come from `__list_probe__` in recording, or existing `find_and_act(css)`, or a live probe — never guess |
|
|
325
|
+
| Using search keyword as `value` in loop | `value` is CSS selector (`.ContentItem h2 a`), not the keyword |
|
|
326
|
+
| `find_and_act(text)` + `click_element(index)` for same target | Merge into single `find_and_act(css, nth)` in loop |
|
|
327
|
+
| Hardcoding physical `tabId` | Use semantic refs: `"origin"`, `"current"`, `"latest"`, custom alias |
|
|
328
|
+
| `browser_go_back` after new tab opened | Use `tab_close` + `tab_switch` — new tab has no history |
|
|
329
|
+
| Silently dropping tab ops from recording | If recording has tab ops, playbook MUST include tab management |
|
|
330
|
+
| Loop with `go_back` when recording had tab ops | Check recording — if tab_switch/close exists, use tab management not go_back |
|