cdp-toolkit 1.8.0 → 1.9.1
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/CHANGELOG.md +237 -0
- package/CONTRACT.md +5 -1
- package/README.md +41 -8
- package/dist/cli.js +407 -121
- package/dist/index.js +377 -120
- package/dist/mcp.js +464 -166
- package/package.json +3 -2
- package/src/bidi/driver.ts +28 -0
- package/src/bidi-tools.ts +21 -0
- package/src/cdp/driver.ts +16 -1
- package/src/cdp/workers.ts +177 -0
- package/src/cli.ts +5 -1
- package/src/client.ts +52 -2
- package/src/driver.ts +27 -1
- package/src/leases-tools.ts +2 -2
- package/src/manifest.ts +61 -45
- package/src/mcp.ts +1 -1
- package/src/origins.ts +41 -1
- package/src/shared-tools.ts +104 -3
- package/src/tools/console.ts +24 -7
- package/src/tools/network.ts +21 -7
- package/src/tools/recorder.ts +110 -10
- package/src/types.ts +22 -0
- package/src/workers.ts +221 -0
package/src/manifest.ts
CHANGED
|
@@ -89,7 +89,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
89
89
|
},
|
|
90
90
|
"target": {
|
|
91
91
|
"type": "string",
|
|
92
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
92
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
95
|
"required": [
|
|
@@ -110,7 +110,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
110
110
|
},
|
|
111
111
|
"target": {
|
|
112
112
|
"type": "string",
|
|
113
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
113
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
116
|
"required": [
|
|
@@ -131,7 +131,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
131
131
|
},
|
|
132
132
|
"target": {
|
|
133
133
|
"type": "string",
|
|
134
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
134
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
135
135
|
},
|
|
136
136
|
"url": {
|
|
137
137
|
"type": "string",
|
|
@@ -181,7 +181,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
181
181
|
},
|
|
182
182
|
"target": {
|
|
183
183
|
"type": "string",
|
|
184
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
184
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
185
185
|
},
|
|
186
186
|
"text": {
|
|
187
187
|
"type": "string",
|
|
@@ -204,7 +204,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
"name": "evaluate_script",
|
|
207
|
-
"description": "Run arbitrary JavaScript in the target page's main-world context over raw CDP and return the evaluated value (returnByValue). With no 'args' the 'expression' is evaluated as a raw expression; when 'args' is provided 'expression' must be a function literal (arrow or classic) invoked on globalThis with the args passed positionally. A thrown exception surfaces as an error; non-serializable returns (DOM nodes, functions) come back as their CDP description string. Pass 'savePath' to write the evaluated value to a JSON file instead: the response then carries only {path,bytes,type,target} and the value itself never appears in it, in any form. That is the way to read a credential (a JWT or session token out of localStorage, for example) without putting the secret into the caller's transcript.",
|
|
207
|
+
"description": "Run arbitrary JavaScript in the target page's main-world context over raw CDP and return the evaluated value (returnByValue). With no 'args' the 'expression' is evaluated as a raw expression; when 'args' is provided 'expression' must be a function literal (arrow or classic) invoked on globalThis with the args passed positionally. A thrown exception surfaces as an error; non-serializable returns (DOM nodes, functions) come back as their CDP description string. Pass 'savePath' to write the evaluated value to a JSON file instead: the response then carries only {path,bytes,type,target} and the value itself never appears in it, in any form. That is the way to read a credential (a JWT or session token out of localStorage, for example) without putting the secret into the caller's transcript. This is the one tool that can EVALUATE inside an MV3 extension's background SERVICE WORKER rather than a page (list_network_requests/get_network_request/list_console_messages OBSERVE one): pass target 'worker:<extension-id>' (Chrome only) and read chrome.storage.local, chrome.runtime and the worker's own globals directly, with an idle-evicted worker started for you first (see 'wake').",
|
|
208
208
|
"inputSchema": {
|
|
209
209
|
"type": "object",
|
|
210
210
|
"properties": {
|
|
@@ -214,11 +214,15 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
214
214
|
},
|
|
215
215
|
"target": {
|
|
216
216
|
"type": "string",
|
|
217
|
-
"description": "Page selector: 'active' (or omit) -> first page-type target | '<32-hex targetId>' -> exact target by id | 'index:N' -> Nth page-type target (0-based) | 'url:<substring>' -> first page whose url contains substring | 'title:<substring>' -> first page whose title contains substring."
|
|
217
|
+
"description": "Page selector: 'active' (or omit) -> first page-type target | '<32-hex targetId>' -> exact target by id | 'index:N' -> Nth page-type target (0-based) | 'url:<substring>' -> first page whose url contains substring | 'title:<substring>' -> first page whose title contains substring | 'label:<name>' -> the page-type target with exactly this label (checked against the origin ledger and live leases) | 'worker:<substring>' -> a service/shared worker whose url contains the substring, which is how you reach an MV3 extension's background service worker (worker:<extension-id> or worker:background.js) and read chrome.storage.local from it. The worker: arm is CHROME-ONLY (Capability 'worker.targets') and is accepted by four tools only — evaluate_script, list_network_requests, get_network_request and list_console_messages — never as universal grammar; an MV3 worker that has been idle-evicted is started automatically first, see 'wake'."
|
|
218
|
+
},
|
|
219
|
+
"wake": {
|
|
220
|
+
"type": "boolean",
|
|
221
|
+
"description": "Only valid with a 'worker:<substring>' target, and rejected on a page target rather than ignored. Default true: an MV3 extension service worker is idle-evicted after seconds of inactivity and then exists in NO target listing, so a wake asks Chrome to start it (ServiceWorker.startWorker over a page session) and then re-reads the target list to confirm it actually came up. Pass false to fail fast instead of starting anything; the error then says the worker may simply be asleep."
|
|
218
222
|
},
|
|
219
223
|
"expression": {
|
|
220
224
|
"type": "string",
|
|
221
|
-
"description": "JavaScript to run. Evaluated as an expression when 'args' is omitted; must be a function literal (arrow or classic) whose parameters receive 'args' when 'args' is provided."
|
|
225
|
+
"description": "JavaScript to run. Evaluated as an expression when 'args' is omitted; must be a function literal (arrow or classic) whose parameters receive 'args' when 'args' is provided. REQUIRED — the code always goes in this key, never in 'function', 'code', 'js', 'script', 'fn' or 'body'; a call using one of those instead is rejected with an error naming the wrong key."
|
|
222
226
|
},
|
|
223
227
|
"awaitPromise": {
|
|
224
228
|
"type": "boolean",
|
|
@@ -251,7 +255,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
251
255
|
},
|
|
252
256
|
"target": {
|
|
253
257
|
"type": "string",
|
|
254
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
258
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
|
|
255
259
|
},
|
|
256
260
|
"domain": {
|
|
257
261
|
"type": "string",
|
|
@@ -282,7 +286,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
282
286
|
},
|
|
283
287
|
"target": {
|
|
284
288
|
"type": "string",
|
|
285
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
289
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
|
|
286
290
|
},
|
|
287
291
|
"name": {
|
|
288
292
|
"type": "string",
|
|
@@ -338,7 +342,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
338
342
|
},
|
|
339
343
|
"target": {
|
|
340
344
|
"type": "string",
|
|
341
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
345
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
|
|
342
346
|
},
|
|
343
347
|
"name": {
|
|
344
348
|
"type": "string",
|
|
@@ -373,7 +377,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
373
377
|
},
|
|
374
378
|
"target": {
|
|
375
379
|
"type": "string",
|
|
376
|
-
"description": "Target page selector: 'active' (default, = first page-type target) | 'index:N' (Nth page-type target, 0-based) | 'url:<substring>' | 'title:<substring>' | '<32-hex targetId>' (exact target by id)."
|
|
380
|
+
"description": "Target page selector: 'active' (default, = first page-type target) | 'index:N' (Nth page-type target, 0-based) | 'url:<substring>' | 'title:<substring>' | 'label:<name>' (exact label match, origin ledger or live lease) | '<32-hex targetId>' (exact target by id)."
|
|
377
381
|
},
|
|
378
382
|
"interactiveOnly": {
|
|
379
383
|
"type": "boolean",
|
|
@@ -396,7 +400,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
396
400
|
},
|
|
397
401
|
"target": {
|
|
398
402
|
"type": "string",
|
|
399
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
403
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
400
404
|
},
|
|
401
405
|
"uid": {
|
|
402
406
|
"type": "number",
|
|
@@ -449,7 +453,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
449
453
|
},
|
|
450
454
|
"target": {
|
|
451
455
|
"type": "string",
|
|
452
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
456
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
453
457
|
},
|
|
454
458
|
"uid": {
|
|
455
459
|
"type": "number",
|
|
@@ -476,7 +480,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
476
480
|
},
|
|
477
481
|
"target": {
|
|
478
482
|
"type": "string",
|
|
479
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
483
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
480
484
|
},
|
|
481
485
|
"from": {
|
|
482
486
|
"type": "object",
|
|
@@ -562,7 +566,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
562
566
|
},
|
|
563
567
|
"target": {
|
|
564
568
|
"type": "string",
|
|
565
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
569
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
566
570
|
},
|
|
567
571
|
"uid": {
|
|
568
572
|
"type": "number",
|
|
@@ -605,7 +609,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
605
609
|
},
|
|
606
610
|
"target": {
|
|
607
611
|
"type": "string",
|
|
608
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
612
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
609
613
|
},
|
|
610
614
|
"action": {
|
|
611
615
|
"type": "string",
|
|
@@ -671,7 +675,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
671
675
|
},
|
|
672
676
|
"target": {
|
|
673
677
|
"type": "string",
|
|
674
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'. A download is browser-scoped, so this names the tab for the lease check and for the echoed target, not which download is returned."
|
|
678
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'. A download is browser-scoped, so this names the tab for the lease check and for the echoed target, not which download is returned."
|
|
675
679
|
},
|
|
676
680
|
"arm": {
|
|
677
681
|
"type": "boolean",
|
|
@@ -698,7 +702,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
698
702
|
},
|
|
699
703
|
"target": {
|
|
700
704
|
"type": "string",
|
|
701
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'. Used for the lease check and, when 'origin' is omitted, as the source of the origin to grant for."
|
|
705
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'. Used for the lease check and, when 'origin' is omitted, as the source of the origin to grant for."
|
|
702
706
|
},
|
|
703
707
|
"permissions": {
|
|
704
708
|
"type": "array",
|
|
@@ -732,7 +736,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
732
736
|
},
|
|
733
737
|
"target": {
|
|
734
738
|
"type": "string",
|
|
735
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
739
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
736
740
|
},
|
|
737
741
|
"uid": {
|
|
738
742
|
"type": "number",
|
|
@@ -765,7 +769,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
765
769
|
},
|
|
766
770
|
"target": {
|
|
767
771
|
"type": "string",
|
|
768
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
772
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
769
773
|
},
|
|
770
774
|
"fields": {
|
|
771
775
|
"type": "array",
|
|
@@ -811,7 +815,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
811
815
|
},
|
|
812
816
|
"target": {
|
|
813
817
|
"type": "string",
|
|
814
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
818
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
815
819
|
},
|
|
816
820
|
"uid": {
|
|
817
821
|
"type": "number",
|
|
@@ -844,7 +848,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
844
848
|
},
|
|
845
849
|
"target": {
|
|
846
850
|
"type": "string",
|
|
847
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
851
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
848
852
|
},
|
|
849
853
|
"key": {
|
|
850
854
|
"type": "string",
|
|
@@ -876,7 +880,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
876
880
|
},
|
|
877
881
|
"target": {
|
|
878
882
|
"type": "string",
|
|
879
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
883
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
880
884
|
},
|
|
881
885
|
"uid": {
|
|
882
886
|
"type": "number",
|
|
@@ -915,7 +919,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
915
919
|
},
|
|
916
920
|
"target": {
|
|
917
921
|
"type": "string",
|
|
918
|
-
"description": "Target page selector. undefined or 'active' -> first page target; '<32-hex targetId>' -> exact target by id; 'index:N' -> Nth page target (0-based); 'url:<substring>' -> first page whose url contains substring; 'title:<substring>' -> first page whose title contains substring."
|
|
922
|
+
"description": "Target page selector. undefined or 'active' -> first page target; '<32-hex targetId>' -> exact target by id; 'index:N' -> Nth page target (0-based); 'url:<substring>' -> first page whose url contains substring; 'title:<substring>' -> first page whose title contains substring; 'label:<name>' -> the page with exactly this label (origin ledger or live lease)."
|
|
919
923
|
},
|
|
920
924
|
"format": {
|
|
921
925
|
"type": "string",
|
|
@@ -966,7 +970,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
966
970
|
},
|
|
967
971
|
"target": {
|
|
968
972
|
"type": "string",
|
|
969
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
973
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
970
974
|
},
|
|
971
975
|
"width": {
|
|
972
976
|
"type": "number",
|
|
@@ -1066,7 +1070,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1066
1070
|
},
|
|
1067
1071
|
"target": {
|
|
1068
1072
|
"type": "string",
|
|
1069
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1073
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1070
1074
|
},
|
|
1071
1075
|
"width": {
|
|
1072
1076
|
"type": "number",
|
|
@@ -1104,7 +1108,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1104
1108
|
},
|
|
1105
1109
|
"target": {
|
|
1106
1110
|
"type": "string",
|
|
1107
|
-
"description": "Target page selector: 'active' (or omitted) for the first page-type target | 'index:N' (0-based) | 'url:<substring>' | 'title:<substring>' | a 32-hex <targetId>."
|
|
1111
|
+
"description": "Target page selector: 'active' (or omitted) for the first page-type target | 'index:N' (0-based) | 'url:<substring>' | 'title:<substring>' | 'label:<name>' (exact label match, origin ledger or live lease) | a 32-hex <targetId>."
|
|
1108
1112
|
},
|
|
1109
1113
|
"accept": {
|
|
1110
1114
|
"type": "boolean",
|
|
@@ -1131,7 +1135,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1131
1135
|
},
|
|
1132
1136
|
{
|
|
1133
1137
|
"name": "list_console_messages",
|
|
1134
|
-
"description": "Read console output (logs, warnings, exceptions) captured for the target page. By default reads the target's existing shared buffer and returns parsed console entries (empty if no capture has run); with reload:true it reloads the page and records a fresh capture window (both console+network) so a network reload never wipes console history.",
|
|
1138
|
+
"description": "Read console output (logs, warnings, exceptions) captured for the target page. By default reads the target's existing shared buffer and returns parsed console entries (empty if no capture has run); with reload:true it reloads the page and records a fresh capture window (both console+network) so a network reload never wipes console history. SERVICE WORKERS (Chrome only, Capability 'worker.targets'): pass target 'worker:<extension-id>' to watch an MV3 extension's BACKGROUND SERVICE WORKER. The outbound request that worker makes to your backend is recorded from CDP's Network domain on the worker's own session, which observes what the code ACTUALLY SENT and so needs neither of the two tricks a caller reaches for first. Both of those fail for ordinary JavaScript reasons rather than service-worker ones: a value bound at module top level is not visible from evaluate_script's global scope (plain lexical scoping, exactly the same in a page), and assigning self.fetch cannot rebind a fetch reference the module already captured. A worker's console.log arrives as Runtime.consoleAPICalled on its own session. A worker capture has no reload to drive it: reload:true means 'listen for durationMs while the worker runs', so trigger the worker's work (another tool call, an extension message, an alarm) during the window; a worker woken by the call has already run its top-level code by the time the recorder attaches, so a request it makes at startup can be missed. SIDE EFFECT, deliberate and documented: the recording holds a CDP session on the worker, and that KEEPS THE WORKER ALIVE for as long as the capture runs — an MV3 worker is otherwise idle-evicted within seconds — and Chrome resumes evicting it once the capture stops.",
|
|
1135
1139
|
"inputSchema": {
|
|
1136
1140
|
"type": "object",
|
|
1137
1141
|
"properties": {
|
|
@@ -1141,15 +1145,19 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1141
1145
|
},
|
|
1142
1146
|
"target": {
|
|
1143
1147
|
"type": "string",
|
|
1144
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
1148
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>' | 'worker:<substring>' -> a service/shared worker whose url contains the substring, which is how you reach an MV3 extension's background service worker (worker:<extension-id> or worker:background.js). The worker: arm is CHROME-ONLY (Capability 'worker.targets'), is refused on Firefox, and a bare worker target id works too."
|
|
1145
1149
|
},
|
|
1146
1150
|
"reload": {
|
|
1147
1151
|
"type": "boolean",
|
|
1148
|
-
"description": "Record fresh by reloading the page and capturing for a window. Default false (read the existing buffer)."
|
|
1152
|
+
"description": "Record fresh by reloading the page and capturing for a window. Default false (read the existing buffer). With a worker: target there is nothing to reload: the window records for durationMs while the worker runs, and the session it holds keeps that worker from being idle-evicted until the capture stops."
|
|
1149
1153
|
},
|
|
1150
1154
|
"durationMs": {
|
|
1151
1155
|
"type": "number",
|
|
1152
1156
|
"description": "Capture window for reload mode, in milliseconds. Default 2500."
|
|
1157
|
+
},
|
|
1158
|
+
"wake": {
|
|
1159
|
+
"type": "boolean",
|
|
1160
|
+
"description": "Worker targets only, and only when a capture is being started (reload:true). Default true: an MV3 extension service worker is idle-evicted after seconds of inactivity and then exists in NO target listing, so the capture asks Chrome to start it (ServiceWorker.startWorker over a page session) and then re-reads the target list to confirm it actually came up. Pass false to fail fast instead of starting anything. Rejected rather than ignored on a page target, on a bare target id (Chrome re-mints a worker's target id when it restarts, so only the url-substring form can name a worker that is not running), and on a read-only call (the buffer is keyed by target id, so a woken worker's buffer would be empty)."
|
|
1153
1161
|
}
|
|
1154
1162
|
},
|
|
1155
1163
|
"required": [],
|
|
@@ -1168,7 +1176,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1168
1176
|
},
|
|
1169
1177
|
"target": {
|
|
1170
1178
|
"type": "string",
|
|
1171
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
1179
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
|
|
1172
1180
|
},
|
|
1173
1181
|
"index": {
|
|
1174
1182
|
"type": "number",
|
|
@@ -1181,7 +1189,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1181
1189
|
},
|
|
1182
1190
|
{
|
|
1183
1191
|
"name": "list_network_requests",
|
|
1184
|
-
"description": "Return correlated network request rows (one per requestId, with status/headers/state) for the target page. By default reads the target's existing buffer; with reload:true it reloads and records a fresh both-domains capture window. Use filterUrl to keep only requests whose URL contains a substring.",
|
|
1192
|
+
"description": "Return correlated network request rows (one per requestId, with status/headers/state) for the target page. By default reads the target's existing buffer; with reload:true it reloads and records a fresh both-domains capture window. Use filterUrl to keep only requests whose URL contains a substring. SERVICE WORKERS (Chrome only, Capability 'worker.targets'): pass target 'worker:<extension-id>' to watch an MV3 extension's BACKGROUND SERVICE WORKER. The outbound request that worker makes to your backend is recorded from CDP's Network domain on the worker's own session, which observes what the code ACTUALLY SENT and so needs neither of the two tricks a caller reaches for first. Both of those fail for ordinary JavaScript reasons rather than service-worker ones: a value bound at module top level is not visible from evaluate_script's global scope (plain lexical scoping, exactly the same in a page), and assigning self.fetch cannot rebind a fetch reference the module already captured. A worker capture has no reload to drive it: reload:true means 'listen for durationMs while the worker runs', so trigger the worker's work (another tool call, an extension message, an alarm) during the window; a worker woken by the call has already run its top-level code by the time the recorder attaches, so a request it makes at startup can be missed. SIDE EFFECT, deliberate and documented: the recording holds a CDP session on the worker, and that KEEPS THE WORKER ALIVE for as long as the capture runs — an MV3 worker is otherwise idle-evicted within seconds — and Chrome resumes evicting it once the capture stops.",
|
|
1185
1193
|
"inputSchema": {
|
|
1186
1194
|
"type": "object",
|
|
1187
1195
|
"properties": {
|
|
@@ -1191,11 +1199,11 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1191
1199
|
},
|
|
1192
1200
|
"target": {
|
|
1193
1201
|
"type": "string",
|
|
1194
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
1202
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>' | 'worker:<substring>' -> a service/shared worker whose url contains the substring, which is how you reach an MV3 extension's background service worker (worker:<extension-id> or worker:background.js). The worker: arm is CHROME-ONLY (Capability 'worker.targets'), is refused on Firefox, and a bare worker target id works too."
|
|
1195
1203
|
},
|
|
1196
1204
|
"reload": {
|
|
1197
1205
|
"type": "boolean",
|
|
1198
|
-
"description": "Record fresh by reloading the page and capturing for a window. Default false (read the existing buffer)."
|
|
1206
|
+
"description": "Record fresh by reloading the page and capturing for a window. Default false (read the existing buffer). With a worker: target there is nothing to reload: the window records for durationMs while the worker runs, and the session it holds keeps that worker from being idle-evicted until the capture stops."
|
|
1199
1207
|
},
|
|
1200
1208
|
"durationMs": {
|
|
1201
1209
|
"type": "number",
|
|
@@ -1204,6 +1212,10 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1204
1212
|
"filterUrl": {
|
|
1205
1213
|
"type": "string",
|
|
1206
1214
|
"description": "Only return requests whose URL contains this substring."
|
|
1215
|
+
},
|
|
1216
|
+
"wake": {
|
|
1217
|
+
"type": "boolean",
|
|
1218
|
+
"description": "Worker targets only, and only when a capture is being started (reload:true). Default true: an MV3 extension service worker is idle-evicted after seconds of inactivity and then exists in NO target listing, so the capture asks Chrome to start it (ServiceWorker.startWorker over a page session) and then re-reads the target list to confirm it actually came up. Pass false to fail fast instead of starting anything. Rejected rather than ignored on a page target, on a bare target id (Chrome re-mints a worker's target id when it restarts, so only the url-substring form can name a worker that is not running), and on a read-only call (the buffer is keyed by target id, so a woken worker's buffer would be empty)."
|
|
1207
1219
|
}
|
|
1208
1220
|
},
|
|
1209
1221
|
"required": [],
|
|
@@ -1212,7 +1224,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1212
1224
|
},
|
|
1213
1225
|
{
|
|
1214
1226
|
"name": "get_network_request",
|
|
1215
|
-
"description": "Return one network request (matched by exact requestId, else by url substring) including status/headers. Requires at least one of requestId or url (throws otherwise). With includeBody:true the body fetch drives a fresh reload
|
|
1227
|
+
"description": "Return one network request (matched by exact requestId, else by url substring) including status/headers. Requires at least one of requestId or url (throws otherwise). With includeBody:true the body fetch drives a fresh capture (a reload for a page, a listen window for a worker) and is matched by url ONLY (a fresh capture re-mints requestIds, so a carried-over requestId cannot fetch a body, so it returns metadata plus bodyUnavailableReason). SERVICE WORKERS (Chrome only, Capability 'worker.targets'): pass target 'worker:<extension-id>' to watch an MV3 extension's BACKGROUND SERVICE WORKER. The outbound request that worker makes to your backend is recorded from CDP's Network domain on the worker's own session, which observes what the code ACTUALLY SENT and so needs neither of the two tricks a caller reaches for first. Both of those fail for ordinary JavaScript reasons rather than service-worker ones: a value bound at module top level is not visible from evaluate_script's global scope (plain lexical scoping, exactly the same in a page), and assigning self.fetch cannot rebind a fetch reference the module already captured. A worker capture has no reload to drive it: reload:true means 'listen for durationMs while the worker runs', so trigger the worker's work (another tool call, an extension message, an alarm) during the window; a worker woken by the call has already run its top-level code by the time the recorder attaches, so a request it makes at startup can be missed. SIDE EFFECT, deliberate and documented: the recording holds a CDP session on the worker, and that KEEPS THE WORKER ALIVE for as long as the capture runs — an MV3 worker is otherwise idle-evicted within seconds — and Chrome resumes evicting it once the capture stops.",
|
|
1216
1228
|
"inputSchema": {
|
|
1217
1229
|
"type": "object",
|
|
1218
1230
|
"properties": {
|
|
@@ -1222,7 +1234,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1222
1234
|
},
|
|
1223
1235
|
"target": {
|
|
1224
1236
|
"type": "string",
|
|
1225
|
-
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
|
|
1237
|
+
"description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>' | 'worker:<substring>' -> a service/shared worker whose url contains the substring, which is how you reach an MV3 extension's background service worker (worker:<extension-id> or worker:background.js). The worker: arm is CHROME-ONLY (Capability 'worker.targets'), is refused on Firefox, and a bare worker target id works too."
|
|
1226
1238
|
},
|
|
1227
1239
|
"requestId": {
|
|
1228
1240
|
"type": "string",
|
|
@@ -1239,6 +1251,10 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1239
1251
|
"durationMs": {
|
|
1240
1252
|
"type": "number",
|
|
1241
1253
|
"description": "Capture window for the reload-driven body fetch, in milliseconds. Default 2500."
|
|
1254
|
+
},
|
|
1255
|
+
"wake": {
|
|
1256
|
+
"type": "boolean",
|
|
1257
|
+
"description": "Worker targets only, and only when a capture is being started (includeBody together with url). Default true: an MV3 extension service worker is idle-evicted after seconds of inactivity and then exists in NO target listing, so the capture asks Chrome to start it (ServiceWorker.startWorker over a page session) and then re-reads the target list to confirm it actually came up. Pass false to fail fast instead of starting anything. Rejected rather than ignored on a page target, on a bare target id (Chrome re-mints a worker's target id when it restarts, so only the url-substring form can name a worker that is not running), and on a read-only call (the buffer is keyed by target id, so a woken worker's buffer would be empty)."
|
|
1242
1258
|
}
|
|
1243
1259
|
},
|
|
1244
1260
|
"required": [],
|
|
@@ -1257,7 +1273,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1257
1273
|
},
|
|
1258
1274
|
"target": {
|
|
1259
1275
|
"type": "string",
|
|
1260
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1276
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1261
1277
|
},
|
|
1262
1278
|
"categories": {
|
|
1263
1279
|
"type": "array",
|
|
@@ -1283,7 +1299,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1283
1299
|
},
|
|
1284
1300
|
"target": {
|
|
1285
1301
|
"type": "string",
|
|
1286
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1302
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1287
1303
|
}
|
|
1288
1304
|
},
|
|
1289
1305
|
"required": [],
|
|
@@ -1321,7 +1337,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1321
1337
|
},
|
|
1322
1338
|
"target": {
|
|
1323
1339
|
"type": "string",
|
|
1324
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1340
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1325
1341
|
},
|
|
1326
1342
|
"durationMs": {
|
|
1327
1343
|
"type": "number",
|
|
@@ -1359,7 +1375,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1359
1375
|
},
|
|
1360
1376
|
"target": {
|
|
1361
1377
|
"type": "string",
|
|
1362
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1378
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1363
1379
|
},
|
|
1364
1380
|
"format": {
|
|
1365
1381
|
"type": "string",
|
|
@@ -1406,7 +1422,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1406
1422
|
},
|
|
1407
1423
|
"target": {
|
|
1408
1424
|
"type": "string",
|
|
1409
|
-
"description": "Which recording to stop: 'active' (default, valid only when exactly one recording is in flight) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1425
|
+
"description": "Which recording to stop: 'active' (default, valid only when exactly one recording is in flight) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1410
1426
|
},
|
|
1411
1427
|
"savePath": {
|
|
1412
1428
|
"type": "string",
|
|
@@ -1429,7 +1445,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1429
1445
|
},
|
|
1430
1446
|
"target": {
|
|
1431
1447
|
"type": "string",
|
|
1432
|
-
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
|
|
1448
|
+
"description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
|
|
1433
1449
|
},
|
|
1434
1450
|
"savePath": {
|
|
1435
1451
|
"type": "string",
|
|
@@ -1496,7 +1512,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1496
1512
|
},
|
|
1497
1513
|
"target": {
|
|
1498
1514
|
"type": "string",
|
|
1499
|
-
"description": "Target selector: active | index:N | url:<substr> | title:<substr> | <targetId>. Defaults to the active page."
|
|
1515
|
+
"description": "Target selector: active | index:N | url:<substr> | title:<substr> | label:<name> | <targetId>. Defaults to the active page."
|
|
1500
1516
|
},
|
|
1501
1517
|
"urlPattern": {
|
|
1502
1518
|
"type": "string",
|
|
@@ -1604,7 +1620,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1604
1620
|
"properties": {
|
|
1605
1621
|
"target": {
|
|
1606
1622
|
"type": "string",
|
|
1607
|
-
"description": "Take over an already-open tab, e.g. one the human has open, when asked to: pass any target selector — active | index:N | url:<substr> | title:<substr> | <targetId>. Resolved against the live page list only, so it NEVER opens a tab: a selector that matches nothing is an error, not a new blank tab. The tab is left open on release because the toolkit did not create it. Refused if another live process holds it, including a lease the gate acquired for that process automatically under CDP_REQUIRE_LEASE: this takes over unleased (human) tabs and never steals one from a live agent. Mutually exclusive with targetId."
|
|
1623
|
+
"description": "Take over an already-open tab, e.g. one the human has open, when asked to: pass any target selector — active | index:N | url:<substr> | title:<substr> | label:<name> | <targetId>. Resolved against the live page list only, so it NEVER opens a tab: a selector that matches nothing is an error, not a new blank tab. The tab is left open on release because the toolkit did not create it. Refused if another live process holds it, including a lease the gate acquired for that process automatically under CDP_REQUIRE_LEASE: this takes over unleased (human) tabs and never steals one from a live agent. Mutually exclusive with targetId."
|
|
1608
1624
|
},
|
|
1609
1625
|
"targetId": {
|
|
1610
1626
|
"type": "string",
|
|
@@ -1639,7 +1655,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1639
1655
|
},
|
|
1640
1656
|
"target": {
|
|
1641
1657
|
"type": "string",
|
|
1642
|
-
"description": "Target selector for a tab this process holds: active | index:N | url:<substr> | title:<substr> | <targetId>. Mutually exclusive with 'lease'. Refused if another process holds the tab."
|
|
1658
|
+
"description": "Target selector for a tab this process holds: active | index:N | url:<substr> | title:<substr> | label:<name> | <targetId>. Mutually exclusive with 'lease'. Refused if another process holds the tab."
|
|
1643
1659
|
},
|
|
1644
1660
|
"close": {
|
|
1645
1661
|
"type": "boolean",
|
package/src/mcp.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { toolAvailability } from "./capabilities.ts";
|
|
|
24
24
|
import { FIREFOX_TOOLS } from "./firefox-tools.ts";
|
|
25
25
|
import { leaseFromArgs, markLongLivedProcess, withLeaseScope } from "./leases.ts";
|
|
26
26
|
|
|
27
|
-
const VERSION = "1.
|
|
27
|
+
const VERSION = "1.9.1";
|
|
28
28
|
|
|
29
29
|
// --browser is read once at startup (MCP has no per-call notion of backend): explicit flag,
|
|
30
30
|
// else CDP_BROWSER env var, else "chrome" (zero behavior change for existing users/configs).
|
package/src/origins.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
import { mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
|
|
33
33
|
import { join } from "node:path";
|
|
34
34
|
import type { BrowserDriver, PageInfo } from "./driver.ts";
|
|
35
|
-
import { defaultLabel, leaseDir, type LeaseBackend } from "./leases.ts";
|
|
35
|
+
import { defaultLabel, leaseDir, listLeases, type LeaseBackend } from "./leases.ts";
|
|
36
36
|
|
|
37
37
|
/** What list_pages can say about where a tab came from. There is deliberately
|
|
38
38
|
* no "human": see the file header. */
|
|
@@ -264,3 +264,43 @@ export async function newTrackedPage(
|
|
|
264
264
|
await recordOrigin(backend, page.id, { label: opts.label });
|
|
265
265
|
return page;
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
/** What "label:<name>" resolution found, common to all three selector-grammar
|
|
269
|
+
* copies (shared-tools.ts's pickPage, client.ts's pickTarget, bidi/driver.ts's
|
|
270
|
+
* pickContext): 0, 1 or >1 live targetIds carrying the queried label, plus
|
|
271
|
+
* every label actually in use, for a miss error that teaches something. */
|
|
272
|
+
export interface LabelResolution {
|
|
273
|
+
matchIds: string[];
|
|
274
|
+
/** Deduped, sorted. Empty when no live target has any recorded label. */
|
|
275
|
+
knownLabels: string[];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* The shared core of "label:<name>" resolution: exact match against BOTH the
|
|
280
|
+
* origin ledger (agent-created tabs) and live lease records (covers a
|
|
281
|
+
* taken-over tab claimed with a label, which has no origin record — see
|
|
282
|
+
* leases-tools.ts's claimPage), scoped to `liveIds` so a label whose tab is
|
|
283
|
+
* gone neither matches nor pollutes the miss list.
|
|
284
|
+
*
|
|
285
|
+
* PURE OVER ITS INPUTS BEYOND THE TWO READS: no reap, no write, no lease gate.
|
|
286
|
+
* Each of the three callers formats its own error in its own idiom
|
|
287
|
+
* (SharedToolError, CdpError, DriverError), so this returns data, never
|
|
288
|
+
* throws — a caller decides what "matches.length !== 1" means to it.
|
|
289
|
+
*/
|
|
290
|
+
export async function resolveLiveLabel(backend: LeaseBackend, label: string, liveIds: readonly string[]): Promise<LabelResolution> {
|
|
291
|
+
const live = new Set(liveIds);
|
|
292
|
+
const [origins, leases] = await Promise.all([originIndex(backend), listLeases({ liveBackend: backend })]);
|
|
293
|
+
const knownLabels = new Set<string>();
|
|
294
|
+
const matchIds = new Set<string>();
|
|
295
|
+
for (const rec of origins.values()) {
|
|
296
|
+
if (rec.unreadable !== undefined || !live.has(rec.targetId)) continue;
|
|
297
|
+
knownLabels.add(rec.label);
|
|
298
|
+
if (rec.label === label) matchIds.add(rec.targetId);
|
|
299
|
+
}
|
|
300
|
+
for (const rec of leases) {
|
|
301
|
+
if (rec.backend !== backend || rec.unreadable !== undefined || !live.has(rec.targetId)) continue;
|
|
302
|
+
knownLabels.add(rec.label);
|
|
303
|
+
if (rec.label === label) matchIds.add(rec.targetId);
|
|
304
|
+
}
|
|
305
|
+
return { matchIds: [...matchIds], knownLabels: [...knownLabels].sort() };
|
|
306
|
+
}
|