cdp-toolkit 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 also the one tool that can evaluate inside an MV3 extension's background SERVICE WORKER rather than a page: 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 this tool only; 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",
@@ -1141,7 +1145,7 @@ 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>'."
1145
1149
  },
1146
1150
  "reload": {
1147
1151
  "type": "boolean",
@@ -1168,7 +1172,7 @@ export const MANIFEST: ToolSpec[] = [
1168
1172
  },
1169
1173
  "target": {
1170
1174
  "type": "string",
1171
- "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
1175
+ "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
1172
1176
  },
1173
1177
  "index": {
1174
1178
  "type": "number",
@@ -1191,7 +1195,7 @@ export const MANIFEST: ToolSpec[] = [
1191
1195
  },
1192
1196
  "target": {
1193
1197
  "type": "string",
1194
- "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
1198
+ "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
1195
1199
  },
1196
1200
  "reload": {
1197
1201
  "type": "boolean",
@@ -1222,7 +1226,7 @@ export const MANIFEST: ToolSpec[] = [
1222
1226
  },
1223
1227
  "target": {
1224
1228
  "type": "string",
1225
- "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>'."
1229
+ "description": "Target page selector: 'active' (default) | '<targetId>' | 'index:N' | 'url:<substring>' | 'title:<substring>' | 'label:<name>'."
1226
1230
  },
1227
1231
  "requestId": {
1228
1232
  "type": "string",
@@ -1257,7 +1261,7 @@ export const MANIFEST: ToolSpec[] = [
1257
1261
  },
1258
1262
  "target": {
1259
1263
  "type": "string",
1260
- "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1264
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
1261
1265
  },
1262
1266
  "categories": {
1263
1267
  "type": "array",
@@ -1283,7 +1287,7 @@ export const MANIFEST: ToolSpec[] = [
1283
1287
  },
1284
1288
  "target": {
1285
1289
  "type": "string",
1286
- "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1290
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
1287
1291
  }
1288
1292
  },
1289
1293
  "required": [],
@@ -1321,7 +1325,7 @@ export const MANIFEST: ToolSpec[] = [
1321
1325
  },
1322
1326
  "target": {
1323
1327
  "type": "string",
1324
- "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1328
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
1325
1329
  },
1326
1330
  "durationMs": {
1327
1331
  "type": "number",
@@ -1359,7 +1363,7 @@ export const MANIFEST: ToolSpec[] = [
1359
1363
  },
1360
1364
  "target": {
1361
1365
  "type": "string",
1362
- "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1366
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
1363
1367
  },
1364
1368
  "format": {
1365
1369
  "type": "string",
@@ -1406,7 +1410,7 @@ export const MANIFEST: ToolSpec[] = [
1406
1410
  },
1407
1411
  "target": {
1408
1412
  "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>'."
1413
+ "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
1414
  },
1411
1415
  "savePath": {
1412
1416
  "type": "string",
@@ -1429,7 +1433,7 @@ export const MANIFEST: ToolSpec[] = [
1429
1433
  },
1430
1434
  "target": {
1431
1435
  "type": "string",
1432
- "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1436
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | 'label:<name>' | '<targetId>'."
1433
1437
  },
1434
1438
  "savePath": {
1435
1439
  "type": "string",
@@ -1496,7 +1500,7 @@ export const MANIFEST: ToolSpec[] = [
1496
1500
  },
1497
1501
  "target": {
1498
1502
  "type": "string",
1499
- "description": "Target selector: active | index:N | url:<substr> | title:<substr> | <targetId>. Defaults to the active page."
1503
+ "description": "Target selector: active | index:N | url:<substr> | title:<substr> | label:<name> | <targetId>. Defaults to the active page."
1500
1504
  },
1501
1505
  "urlPattern": {
1502
1506
  "type": "string",
@@ -1604,7 +1608,7 @@ export const MANIFEST: ToolSpec[] = [
1604
1608
  "properties": {
1605
1609
  "target": {
1606
1610
  "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."
1611
+ "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
1612
  },
1609
1613
  "targetId": {
1610
1614
  "type": "string",
@@ -1639,7 +1643,7 @@ export const MANIFEST: ToolSpec[] = [
1639
1643
  },
1640
1644
  "target": {
1641
1645
  "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."
1646
+ "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
1647
  },
1644
1648
  "close": {
1645
1649
  "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.8.0";
27
+ const VERSION = "1.9.0";
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
+ }
@@ -44,7 +44,8 @@ import {
44
44
  type LeaseBackend, type LeaseStaleReason, type LeaseToken,
45
45
  } from "./leases.ts";
46
46
  import { installBeacon, probeRendererActivity, rendererProbeSupported } from "./activity.ts";
47
- import { newTrackedPage, originIndex, type PageOrigin } from "./origins.ts";
47
+ import { newTrackedPage, originIndex, resolveLiveLabel, type PageOrigin } from "./origins.ts";
48
+ import { isWorkerSelector, WORKER_SELECTOR_PAGE_ONLY_MESSAGE, WORKER_SELECTOR_UNSUPPORTED_MESSAGE } from "./workers.ts";
48
49
  import { reapStaleAgentTabs, type ReapedTab } from "./reap.ts";
49
50
 
50
51
  const ARTIFACT_DIR = process.env.CDP_ARTIFACT_DIR ?? "/tmp/cdp-toolkit";
@@ -132,6 +133,21 @@ export async function pickPage(driver: BrowserDriver, pages: PageInfo[], selecto
132
133
  if (!p) throw new SharedToolError(`no page title containing '${needle}'`);
133
134
  return p;
134
135
  }
136
+ if (selector.startsWith("label:")) return pickByLabel(driver, pages, selector.slice(6));
137
+ if (isWorkerSelector(selector)) {
138
+ // THE PAGE-ONLY CONTRACT, STATED OUT LOUD. Every caller of pickPage
139
+ // (resolvePage -> close_page / select_page / release_page, claim_page's
140
+ // takeover) acts on a TAB, and the 1.9.0 "worker:" arm is deliberately NOT
141
+ // implemented here: it resolves against the page-only listing these callers
142
+ // pass, which by construction never contains a worker, so the arm could
143
+ // only ever miss. Refusing with the reason beats falling through to the
144
+ // bare-id lookup, whose "no target with id 'worker:foo'" reads as a typo.
145
+ // The arm lives in client.ts's pickTarget (Chrome's all-targets resolver),
146
+ // and only evaluate_script routes to it.
147
+ throw new SharedToolError(
148
+ backendOf(driver) === "firefox" ? WORKER_SELECTOR_UNSUPPORTED_MESSAGE : WORKER_SELECTOR_PAGE_ONLY_MESSAGE,
149
+ );
150
+ }
135
151
  const exactPage = pages.find((x) => x.id === selector);
136
152
  if (exactPage) return exactPage;
137
153
  const all = await driver.listPages({ all: true });
@@ -140,6 +156,35 @@ export async function pickPage(driver: BrowserDriver, pages: PageInfo[], selecto
140
156
  throw new SharedToolError(`no target with id '${selector}'`);
141
157
  }
142
158
 
159
+ /**
160
+ * "label:<name>" resolution for pickPage. The lookup itself (both stores,
161
+ * live-scoped) is shared with client.ts's pickTarget and bidi/driver.ts's
162
+ * pickContext via origins.ts's resolveLiveLabel, so the three copies of the
163
+ * selector grammar cannot drift on what counts as a hit, a miss or an
164
+ * ambiguity. Only the error wording is local, matching this file's
165
+ * SharedToolError idiom.
166
+ *
167
+ * Nothing is reaped here: this is a read, not a maintenance pass, and pickPage
168
+ * stays gate-free and side-effect-free over its inputs.
169
+ */
170
+ async function pickByLabel(driver: BrowserDriver, pages: PageInfo[], label: string): Promise<PageInfo> {
171
+ const { matchIds, knownLabels } = await resolveLiveLabel(backendOf(driver), label, pages.map((x) => x.id));
172
+ if (matchIds.length > 1) {
173
+ throw new SharedToolError(
174
+ `label 'label:${label}' matches more than one live target: ${matchIds.join(", ")}. Resolve by target id instead.`,
175
+ );
176
+ }
177
+ if (matchIds.length === 1) {
178
+ const p = pages.find((x) => x.id === matchIds[0]);
179
+ if (p) return p;
180
+ }
181
+ throw new SharedToolError(
182
+ knownLabels.length
183
+ ? `no live target with label '${label}' (labels currently in use: ${knownLabels.join(", ")})`
184
+ : `no live target with label '${label}' (no labels are currently assigned to any open target)`,
185
+ );
186
+ }
187
+
143
188
  /* -------------------------------- element locators -------------------------------- */
144
189
 
145
190
  function locatorOf(args: { uid?: DriverUid; selector?: string }): ElementLocator {
@@ -476,14 +521,70 @@ function sinkRequested(savePath?: string): savePath is string {
476
521
  return savePath !== undefined && savePath !== "";
477
522
  }
478
523
 
524
+ /** Keys a caller reaches for out of habit from other automation APIs, none of
525
+ * which this tool reads: the code always goes in 'expression'. Checked in
526
+ * this order so the error names one wrong key, deterministically, when a
527
+ * caller happens to pass more than one. */
528
+ const EXPRESSION_ALIAS_KEYS = ["function", "code", "js", "script", "fn", "body"] as const;
529
+
530
+ /**
531
+ * Turn a `worker:<substring>` target into a concrete target id, waking an
532
+ * idle-evicted MV3 service worker on the way when `wake` is not false.
533
+ *
534
+ * WHY THIS RETURNS AN ID RATHER THAN DRIVING THE WORKER ITSELF. Once resolved,
535
+ * a worker is just a target with its own webSocketDebuggerUrl, and a DIRECT
536
+ * connection to it evaluates exactly like a page does — `Runtime.evaluate` with
537
+ * `awaitPromise` returns real awaited values there (measured on Chrome 151;
538
+ * `chrome.storage.local.get(null)` comes back as an object, not undefined). So
539
+ * the worker rides the ordinary page() acquisition path from here, and every
540
+ * behavior around it — the args/function form, exception reporting, and above
541
+ * all savePath's no-value-in-the-response secret hygiene — is the SAME CODE,
542
+ * not a parallel copy that could quietly diverge on the leak-shaped edge.
543
+ *
544
+ * Exported for the unit tests, which need to reach the capability refusal
545
+ * without a browser.
546
+ */
547
+ export async function resolveWorkerSelectorFor(
548
+ driver: BrowserDriver,
549
+ selector: string,
550
+ wake: boolean,
551
+ ): Promise<PageInfo> {
552
+ if (!driver.capabilities.has("worker.targets") || typeof driver.resolveWorkerTarget !== "function") {
553
+ throw new SharedToolError(`evaluateScript: ${WORKER_SELECTOR_UNSUPPORTED_MESSAGE}`);
554
+ }
555
+ return driver.resolveWorkerTarget(selector, { wake });
556
+ }
557
+
479
558
  export async function evaluateScript(
480
559
  driver: BrowserDriver,
481
- args: { target?: TargetSelector; expression: string; awaitPromise?: boolean; args?: unknown[]; savePath?: string },
560
+ args: { target?: TargetSelector; expression: string; awaitPromise?: boolean; args?: unknown[]; savePath?: string; wake?: boolean },
482
561
  ): Promise<unknown> {
483
562
  if (typeof args.expression !== "string" || args.expression.length === 0) {
563
+ const wrongKey = EXPRESSION_ALIAS_KEYS.find((k) => (args as Record<string, unknown>)[k] !== undefined);
564
+ if (wrongKey !== undefined) {
565
+ throw new SharedToolError(
566
+ `evaluateScript: you passed '${wrongKey}'; the key is 'expression' — function literals go in 'expression', invoked with 'args'.`,
567
+ );
568
+ }
484
569
  throw new SharedToolError("evaluateScript: 'expression' must be a non-empty string");
485
570
  }
486
- return withPage(driver, args.target, async (page) => {
571
+ if (args.wake !== undefined && typeof args.wake !== "boolean") {
572
+ throw new SharedToolError("evaluateScript: 'wake' must be a boolean");
573
+ }
574
+ // `wake` is REFUSED on a page selector rather than ignored. Accepting it there
575
+ // would read as "this call will wake something", and the caller would never
576
+ // learn that the argument did nothing at all.
577
+ if (args.wake !== undefined && !isWorkerSelector(args.target)) {
578
+ throw new SharedToolError(
579
+ "evaluateScript: 'wake' only applies to a target of the form 'worker:<substring>'; a page target is never asleep.",
580
+ );
581
+ }
582
+ // Resolved to a bare target id BEFORE acquisition, so the page() call below is
583
+ // the same one every other selector makes.
584
+ const target = isWorkerSelector(args.target)
585
+ ? (await resolveWorkerSelectorFor(driver, args.target, args.wake ?? true)).id
586
+ : args.target;
587
+ return withPage(driver, target, async (page) => {
487
588
  const value = await page.evaluate(args.expression, { args: args.args, awaitPromise: args.awaitPromise ?? true });
488
589
  // No savePath: byte-identical to the pre-sink behavior, the value itself.
489
590
  // A thrown page-side exception never reaches here, so it still surfaces as
package/src/types.ts CHANGED
@@ -41,6 +41,26 @@ export interface CdpEvent<P = Record<string, unknown>> {
41
41
  * - "index:N" -> Nth page-type target (0-based)
42
42
  * - "url:<substring>" -> first page whose url contains substring
43
43
  * - "title:<substring>" -> first page whose title contains substring
44
+ * - "label:<name>" -> the page-type target whose label EXACTLY
45
+ * matches `name`, checked against both the origin ledger (agent-created
46
+ * tabs, see src/origins.ts) and live lease records (covers a taken-over
47
+ * tab claimed with a label, which has no origin record — see
48
+ * src/leases-tools.ts's claimPage). The resolved id must be present in
49
+ * the listing this call is resolving against; a label whose tab is gone
50
+ * falls through to the normal no-match error. A miss enumerates the
51
+ * labels that DO exist; two live targets sharing a label is an ambiguity
52
+ * error naming both ids, never a silent first-match. See pickPage in
53
+ * src/shared-tools.ts.
54
+ * - "worker:<substring>" -> a service_worker/shared_worker target whose
55
+ * URL contains the substring (an MV3 extension's background worker is
56
+ * `chrome-extension://<id>/<script>`, so both the extension id and the
57
+ * script name match). NOT UNIVERSAL, unlike every arm above it: resolved
58
+ * only by Chrome's pickTarget (this file's resolveTarget), accepted only by
59
+ * evaluate_script, refused with a capability error on Firefox and by the
60
+ * page-only resolvers. It reads the UNFILTERED target listing, because a
61
+ * worker is not a page target — and a worker that is idle-evicted is in no
62
+ * listing at all, which is what evaluate_script's `wake` exists for. See
63
+ * src/workers.ts and src/cdp/workers.ts.
44
64
  */
45
65
  export type TargetSelector = string | undefined;
46
66