@qqbrowser/openclaw-qbot 0.10.13 → 0.10.16
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 +289 -237
- package/skills/qqbrowser-skill/SKILL.md +241 -233
- 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,345 @@ 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`, `variable`, `start`, `on_error`, 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
|
-
|
|
85
|
-
|
|
86
|
-
Based on the task recording, you (the AI) must:
|
|
87
|
-
|
|
88
|
-
1. **Keep all actions and indices exactly as recorded** — do NOT change `action` names or `index` values
|
|
89
|
-
2. **Identify parameterizable values** — values that a user would likely want to change on each run:
|
|
90
|
-
- Text inputs (`browser_input_text` → the `text` field)
|
|
91
|
-
- URLs (`browser_go_to_url` → the `url` field, only if the URL varies per use)
|
|
92
|
-
- File paths in upload actions
|
|
93
|
-
- Any user-specific data (names, emails, content, etc.)
|
|
94
|
-
3. **Define parameters** with descriptive names, descriptions, and whether they are required
|
|
95
|
-
4. **Replace ONLY literal values** with `{{param_name}}` placeholders in the steps (only the `text`, `url`, or file path fields — NEVER replace `action` or `index`)
|
|
96
|
-
5. **Add meaningful descriptions** to each step
|
|
97
|
-
6. **Set appropriate metadata** (name, description, url)
|
|
105
|
+
Fields: `count`(string/number, supports `{{param}}`), `variable`(loop var name), `start`(default 1, 1-based for `nth`), `on_error`(optional, see below).
|
|
98
106
|
|
|
99
|
-
|
|
100
|
-
- Recording `"text": "我的搜索词"` → Playbook `"text": "{{search_keyword}}"`
|
|
101
|
-
- Everything else (action names, index values, key names) stays **exactly** the same.
|
|
107
|
+
### `on_error` — Dynamic List Handling
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Generate a JSON file conforming to this exact format and save it to the playbooks directory:
|
|
109
|
+
When the list length is **unknown or dynamic** (e.g., "claim all free games", "process all search results"), use `on_error: "break"`:
|
|
106
110
|
|
|
107
111
|
```json
|
|
108
112
|
{
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
]
|
|
113
|
+
"action": "loop",
|
|
114
|
+
"params": { "count": "100", "variable": "i", "start": 1, "on_error": "break" },
|
|
115
|
+
"description": "处理所有列表项(自动检测结束)",
|
|
116
|
+
"steps": [ ... ]
|
|
168
117
|
}
|
|
169
118
|
```
|
|
170
119
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Use `
|
|
194
|
-
|
|
195
|
-
|
|
120
|
+
| `on_error` | Behavior |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `"fail"` (default) | Sub-step failure → entire replay fails |
|
|
123
|
+
| `"break"` | Sub-step failure → **exit loop gracefully**, replay continues as success |
|
|
124
|
+
|
|
125
|
+
**When to use `on_error: "break"`:**
|
|
126
|
+
- List length is unknown at script generation time
|
|
127
|
+
- Task is "process ALL items" (not a fixed count)
|
|
128
|
+
- Set `count` to a large upper bound (e.g., 100), loop breaks when `find_and_act` can't find the Nth element
|
|
129
|
+
|
|
130
|
+
### Key Rules for Loop Steps
|
|
131
|
+
|
|
132
|
+
1. **Use `browser_find_and_act` with `by: "css"` + `nth: "{{i}}"`** inside loops — NOT `browser_click_element` with hardcoded index.
|
|
133
|
+
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.
|
|
134
|
+
3. **`value` must be a list-level CSS selector** matching ALL items (e.g., `.ContentItem h2 a`), NOT specific text content.
|
|
135
|
+
4. **CSS selector MUST come from actual DOM**, with this priority:
|
|
136
|
+
- **Best**: Extract from `__list_probe__` in recording (see Step 2b)
|
|
137
|
+
- **Good**: Use `by: "css"` selector already in recording's `find_and_act`
|
|
138
|
+
- **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)
|
|
139
|
+
- **NEVER**: Fabricate or guess. If all above fail, set `metadata.requires_ai: true`.
|
|
140
|
+
5. **`{{i}}` is 1-based** (start=1), aligning with `nth` (nth=1 = first element).
|
|
141
|
+
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`.**
|
|
142
|
+
7. **Use `browser_go_back` ONLY** if iteration stays in the **same tab** (no tab ops in recording).
|
|
143
|
+
8. **Include `browser_wait`** between iterations for stability.
|
|
144
|
+
|
|
145
|
+
### Decision Tables
|
|
146
|
+
|
|
147
|
+
**Loop vs Single JS Extraction:**
|
|
148
|
+
|
|
149
|
+
| Scenario | Method |
|
|
150
|
+
|----------|--------|
|
|
151
|
+
| List page data only | Single `browser_eval_content_js` |
|
|
152
|
+
| Need detail page content | `loop` (navigate in/out per item) |
|
|
153
|
+
| Pagination + extract | `loop` |
|
|
154
|
+
| Fixed different form fields | Linear steps |
|
|
155
|
+
| Process ALL items (unknown count) | `loop` with `on_error: "break"` + large `count` |
|
|
156
|
+
|
|
157
|
+
**`go_back` vs `tab_close` + `tab_switch`:**
|
|
158
|
+
|
|
159
|
+
| Scenario | Method |
|
|
160
|
+
|----------|--------|
|
|
161
|
+
| Link opens in **same tab** | `browser_go_back` |
|
|
162
|
+
| Link opens in **new tab** | `browser_tab_close` + `browser_tab_switch` |
|
|
163
|
+
| Unsure | Prefer `tab_close` + `tab_switch` (safer) |
|
|
164
|
+
|
|
165
|
+
## Tab Management
|
|
166
|
+
|
|
167
|
+
Playbooks **MUST use semantic tab references** — physical `tabId` changes every session.
|
|
168
|
+
|
|
169
|
+
| Reference | Meaning |
|
|
170
|
+
|-----------|---------|
|
|
171
|
+
| `"origin"` | Tab active when replay started (always available) |
|
|
172
|
+
| `"current"` | Currently active tab (dynamic) |
|
|
173
|
+
| `"latest"` | Most recently opened tab (after any `tab_open`) |
|
|
174
|
+
| Custom alias | Named via `"as"` param in `tab_open` |
|
|
175
|
+
|
|
176
|
+
**Tab action examples:**
|
|
177
|
+
```json
|
|
178
|
+
{ "action": "browser_tab_open", "params": { "url": "https://example.com", "as": "detail" } }
|
|
179
|
+
{ "action": "browser_tab_switch", "params": { "tab": "origin" } }
|
|
180
|
+
{ "action": "browser_tab_close", "params": { "tab": "current" } }
|
|
181
|
+
```
|
|
196
182
|
|
|
197
|
-
|
|
183
|
+
**Rules:**
|
|
184
|
+
1. NEVER use physical `tabId` — always semantic references.
|
|
185
|
+
2. `browser_go_back` does NOT work across tabs — use `tab_close` + `tab_switch`.
|
|
186
|
+
3. After `tab_close`, explicitly `tab_switch` — don't assume Chrome's active tab.
|
|
198
187
|
|
|
199
|
-
|
|
188
|
+
## Workflow
|
|
200
189
|
|
|
201
190
|
```bash
|
|
202
|
-
# 1. Get
|
|
191
|
+
# 1. Get recording
|
|
203
192
|
qqbrowser-skill task_latest
|
|
204
193
|
|
|
205
|
-
# 2. AI analyzes
|
|
194
|
+
# 2. AI analyzes → generates playbook:
|
|
195
|
+
# 2a: Filter excluded actions (snapshot, screenshot, tab_list, etc.)
|
|
196
|
+
# 2b: Extract CSS selector from __list_probe__ (see below)
|
|
197
|
+
# 2c: Detect loop patterns (repeated sequences → loop, using selector from 2b)
|
|
198
|
+
# 2d: Detect tab patterns (tabId → semantic refs). NEVER silently drop tab ops.
|
|
199
|
+
# 2e: Cross-check loop+tab — if loop iteration has tab ops, use tab_switch/close NOT go_back
|
|
200
|
+
# 2f: Identify parameterizable values → {{param}}
|
|
201
|
+
# 2g: Add descriptions, metadata, settings
|
|
202
|
+
|
|
203
|
+
# 3. Save
|
|
204
|
+
mkdir -p ~/.qqbrowser-skill/playbooks
|
|
205
|
+
# write_to_file tool
|
|
206
|
+
|
|
207
|
+
# 4. Run
|
|
208
|
+
# ⚠️ browser_replay can take UP TO 10 MINUTES for complex playbooks with loops.
|
|
209
|
+
# You MUST wait patiently for the command to finish. Do NOT assume it has timed out.
|
|
210
|
+
qqbrowser-skill browser_replay --script ~/.qqbrowser-skill/playbooks/<name>.json \
|
|
211
|
+
--variables '{"param1": "value1"}'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **⚠️ 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
215
|
|
|
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"
|
|
216
|
+
### Step 2b: Extract CSS Selector from Recording
|
|
212
217
|
|
|
213
|
-
|
|
218
|
+
**Primary source**: Look for `browser_eval_content_js` calls whose output contains `__list_probe__: true`.
|
|
214
219
|
```
|
|
220
|
+
Recording data example:
|
|
221
|
+
browser_eval_content_js → output: {"__list_probe__": true, "selector": ".SearchResult-Card .ContentItem h2 a", "count": 10, "samples": ["文章A", "文章B", "文章C"]}
|
|
222
|
+
```
|
|
223
|
+
Extract the `selector` value — this is the **verified CSS selector** from actual DOM, use it directly in the loop's `find_and_act`.
|
|
215
224
|
|
|
216
|
-
|
|
225
|
+
**Fallback** (if no `__list_probe__` in recording):
|
|
226
|
+
1. Check if recording's `find_and_act` already uses `by: "css"` — use that selector directly
|
|
227
|
+
2. If recording only has `by: "text"` or `click_element(index)`, the selector is unknown
|
|
228
|
+
3. Use `browser_get_info --type list_selector --index <any_list_item_index>` to auto-detect the selector
|
|
229
|
+
4. If `list_selector` also fails, run `browser_eval_content_js` with a probe script to discover the selector
|
|
230
|
+
5. **NEVER** guess or fabricate selectors
|
|
217
231
|
|
|
218
|
-
|
|
219
|
-
# Linux / macOS
|
|
220
|
-
ls ~/.qqbrowser-skill/playbooks/
|
|
232
|
+
### Step 2c: Loop Pattern Detection
|
|
221
233
|
|
|
222
|
-
|
|
223
|
-
|
|
234
|
+
```
|
|
235
|
+
1. Group consecutive steps into sequences
|
|
236
|
+
2. If 2+ adjacent sequences share the SAME action pattern → convert to `loop`
|
|
237
|
+
3. What varies between iterations:
|
|
238
|
+
- `nth` increments → use `{{i}}`
|
|
239
|
+
- Different index values → replace with `find_and_act` + `nth: "{{i}}"`
|
|
240
|
+
- Different text/title in `find_and_act(by="text")` → convert to `by: "css"` + `nth: "{{i}}"`
|
|
241
|
+
4. Use the CSS selector from Step 2b (from __list_probe__ or fallback probe)
|
|
242
|
+
5. Convert `browser_click_element(index=...)` → `find_and_act(by="css", nth="{{i}}")`
|
|
243
|
+
6. Count source: user-mentioned quantity → `{{count}}` param; fixed → literal
|
|
224
244
|
```
|
|
225
245
|
|
|
226
|
-
|
|
246
|
+
**Recording patterns → playbook conversion:**
|
|
227
247
|
|
|
228
|
-
|
|
229
|
-
|
|
248
|
+
| Recording Pattern | Conversion |
|
|
249
|
+
|---|---|
|
|
250
|
+
| Repeated sequences with different text targets | `loop` + `by: "css"` + `nth: "{{i}}"` |
|
|
251
|
+
| Repeated sequences with different `index` values | `loop` + `by: "css"` + `nth: "{{i}}"` |
|
|
252
|
+
| `find_and_act(by="text", value="标题A")` → ... → `find_and_act(by="text", value="标题B")` | **MUST** convert `by: "text"` → `by: "css"` + `nth: "{{i}}"` |
|
|
253
|
+
|
|
254
|
+
### Step 2d: Tab Pattern Detection
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
1. `browser_tab_open` → discard physical tabId, add semantic `"as"` alias
|
|
258
|
+
2. `browser_tab_switch(tabId=N)` → replace with "origin"/"latest"/custom alias
|
|
259
|
+
3. `browser_tab_close(tabId=N)` → replace with semantic ref
|
|
260
|
+
4. `browser_go_back` after new tab → replace with tab_close("current") + tab_switch("origin")
|
|
230
261
|
```
|
|
231
262
|
|
|
232
|
-
|
|
263
|
+
## Complete Example: Loop + New Tab
|
|
233
264
|
|
|
234
|
-
|
|
265
|
+
This example demonstrates ALL key conversions (text→css, tabId→semantic, linear→loop):
|
|
235
266
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
267
|
+
Recording (raw):
|
|
268
|
+
```
|
|
269
|
+
eval_content_js → {"__list_probe__": true, "selector": ".ContentItem h2 a", "count": 10, "samples": ["文章标题A","文章标题B","文章标题C"]}
|
|
270
|
+
find_and_act(by="text", value="文章标题A", action="click")
|
|
271
|
+
click_element(index="197_3i4c_uv7n") ← opened new tab
|
|
272
|
+
tab_switch(tabId=1412776911)
|
|
273
|
+
eval_content_js(script="...")
|
|
274
|
+
tab_close(tabId=1412776911)
|
|
275
|
+
find_and_act(by="text", value="文章标题B", action="click")
|
|
276
|
+
click_element(index="206_90r2_rddo")
|
|
277
|
+
tab_switch(tabId=1412776913)
|
|
278
|
+
eval_content_js(script="...")
|
|
279
|
+
tab_close(tabId=1412776913)
|
|
280
|
+
```
|
|
239
281
|
|
|
240
|
-
|
|
241
|
-
|
|
282
|
+
Playbook generator extracts selector `.ContentItem h2 a` from the `__list_probe__` line.
|
|
283
|
+
|
|
284
|
+
Playbook (generated):
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"version": "1.0",
|
|
288
|
+
"metadata": {
|
|
289
|
+
"name": "搜索并提取文章(新Tab场景)",
|
|
290
|
+
"description": "搜索主题,逐个在新Tab打开文章提取内容",
|
|
291
|
+
"created_at": "2026-06-02T15:00:00Z",
|
|
292
|
+
"requires_ai": false
|
|
293
|
+
},
|
|
294
|
+
"params": {
|
|
295
|
+
"topic": { "description": "搜索关键词", "required": true },
|
|
296
|
+
"count": { "description": "提取数量", "required": false, "default": "3" }
|
|
297
|
+
},
|
|
298
|
+
"settings": { "default_delay_ms": 2000, "default_retry_count": 1, "step_timeout_ms": 30000 },
|
|
299
|
+
"steps": [
|
|
300
|
+
{ "action": "browser_go_to_url", "params": { "url": "https://example.com/search?q={{topic}}" }, "description": "搜索主题" },
|
|
301
|
+
{ "action": "browser_wait", "params": { "seconds": 3 }, "description": "等待搜索结果" },
|
|
302
|
+
{
|
|
303
|
+
"action": "loop",
|
|
304
|
+
"params": { "count": "{{count}}", "variable": "i", "start": 1 },
|
|
305
|
+
"description": "逐个在新Tab打开文章并提取",
|
|
306
|
+
"steps": [
|
|
307
|
+
{ "action": "browser_find_and_act", "params": { "by": "css", "value": ".ContentItem h2 a", "nth": "{{i}}", "action": "click" }, "description": "点击第{{i}}篇文章" },
|
|
308
|
+
{ "action": "browser_tab_switch", "params": { "tab": "latest" }, "description": "切到文章Tab" },
|
|
309
|
+
{ "action": "browser_wait", "params": { "seconds": 2 }, "description": "等待加载" },
|
|
310
|
+
{ "action": "browser_eval_content_js", "params": { "script": "JSON.stringify({title:document.querySelector('h1')?.textContent?.trim(),content:document.querySelector('.RichText')?.textContent?.substring(0,2000)})" }, "description": "提取内容" },
|
|
311
|
+
{ "action": "browser_tab_close", "params": { "tab": "current" }, "description": "关闭文章Tab" },
|
|
312
|
+
{ "action": "browser_tab_switch", "params": { "tab": "origin" }, "description": "切回列表页" },
|
|
313
|
+
{ "action": "browser_wait", "params": { "seconds": 1 }, "description": "等待恢复" }
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
242
318
|
```
|
|
243
319
|
|
|
244
|
-
|
|
320
|
+
**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
321
|
|
|
246
|
-
|
|
322
|
+
## `requires_ai` Tasks
|
|
247
323
|
|
|
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
|
|
324
|
+
Set `metadata.requires_ai: true` when recording contains AI-dependent steps (summarization, comparison, conditional decisions). Include only replayable portions as steps.
|
|
254
325
|
|
|
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
|
|
326
|
+
## `browser_find_and_act` Usage
|
|
260
327
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
- Be descriptive: `notes_title` not `title`, `search_query` not `q`
|
|
264
|
-
- Group related params with common prefix: `login_username`, `login_password`
|
|
328
|
+
For single-element targeting (NOT in loops): `by: "text"` or `by: "label"` with known stable text.
|
|
329
|
+
For list iteration (in loops): `by: "css"` + `nth: "{{i}}"` with list-level selector.
|
|
265
330
|
|
|
266
|
-
##
|
|
331
|
+
## `browser_eval_content_js` Usage
|
|
267
332
|
|
|
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
|
-
```
|
|
333
|
+
Primary data extraction method. Supports `{{param}}` in script for selectors, counts, keywords.
|
|
334
|
+
Selector priority: `id` > `data-*` > ARIA > semantic class > structural path. NEVER use dynamic/hashed classes.
|
|
335
|
+
|
|
336
|
+
## Common Mistakes
|
|
281
337
|
|
|
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 |
|
|
338
|
+
| Mistake | Fix |
|
|
339
|
+
|---------|-----|
|
|
340
|
+
| Including `browser_snapshot` etc. in steps | Filter out — AI-decision only |
|
|
341
|
+
| Fabricating steps without `task_latest` | Always base on actual recording |
|
|
342
|
+
| Parameterizing `index` or `action` | Only parameterize value fields (`text`, `url`, `script`) |
|
|
343
|
+
| Using `parameters` instead of `params` | Schema key is `params` |
|
|
344
|
+
| Expanding N repetitions as linear steps | Use `loop` with `{{count}}` |
|
|
345
|
+
| Using `click_element` + hardcoded index in loops | Use `find_and_act` + `nth: "{{i}}"` |
|
|
346
|
+
| Using `by: "text"`/`"label"` with `nth` in loops | `nth` requires `by: "css"` |
|
|
347
|
+
| Keeping `by: "text"` + specific content in loops | Recording's `find_and_act(text, "标题")` MUST convert to `by: "css"` + `nth` in loop |
|
|
348
|
+
| Fabricating CSS selectors | Must come from `__list_probe__` in recording, or existing `find_and_act(css)`, or a live probe — never guess |
|
|
349
|
+
| Using search keyword as `value` in loop | `value` is CSS selector (`.ContentItem h2 a`), not the keyword |
|
|
350
|
+
| `find_and_act(text)` + `click_element(index)` for same target | Merge into single `find_and_act(css, nth)` in loop |
|
|
351
|
+
| Hardcoding physical `tabId` | Use semantic refs: `"origin"`, `"current"`, `"latest"`, custom alias |
|
|
352
|
+
| `browser_go_back` after new tab opened | Use `tab_close` + `tab_switch` — new tab has no history |
|
|
353
|
+
| Silently dropping tab ops from recording | If recording has tab ops, playbook MUST include tab management |
|
|
354
|
+
| Loop with `go_back` when recording had tab ops | Check recording — if tab_switch/close exists, use tab management not go_back |
|