cdp-toolkit 1.6.0 → 1.7.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/CHANGELOG.md +155 -0
- package/CONTRACT.md +7 -2
- package/README.md +57 -13
- package/dist/cli.js +184 -62
- package/dist/index.js +184 -62
- package/dist/mcp.js +245 -112
- package/package.json +1 -1
- package/src/leases-tools.ts +146 -25
- package/src/leases.ts +83 -3
- package/src/manifest.ts +59 -49
- package/src/mcp.ts +5 -2
- package/src/reap.ts +114 -0
- package/src/shared-tools.ts +39 -7
package/src/manifest.ts
CHANGED
|
@@ -25,13 +25,13 @@ export interface ToolSpec {
|
|
|
25
25
|
export const MANIFEST: ToolSpec[] = [
|
|
26
26
|
{
|
|
27
27
|
"name": "list_pages",
|
|
28
|
-
"description": "Enumerate browser page targets via the CDP browser endpoint (GET /json/list). By default returns only page-type tabs; set 'all' to also include workers and background pages. Each entry carries the targetId used as a target selector elsewhere, plus an 'origin' field recording where the tab came from: 'agent' means this toolkit created it (the entry then also carries the creating 'label' and 'createdAt'), and this stays true after the creating agent releases its lease or dies, which is what makes a stray agent tab findable later. 'unknown' means there is no creation record. It never says 'human': the toolkit cannot prove a person opened a tab, so 'unknown' is the honest word for a tab it did not create, one opened before the toolkit ran, or one whose record could not be written. A tab whose record exists but could not be read reports origin 'unknown' plus 'originUnreadable', so a broken record is never mistaken for no record.",
|
|
28
|
+
"description": "Enumerate browser page targets via the CDP browser endpoint (GET /json/list). By default returns only page-type tabs; set 'all' to also include workers and background pages. Each entry carries the targetId used as a target selector elsewhere, plus an 'origin' field recording where the tab came from: 'agent' means this toolkit created it (the entry then also carries the creating 'label' and 'createdAt'), and this stays true after the creating agent releases its lease or dies, which is what makes a stray agent tab findable later. 'unknown' means there is no creation record. It never says 'human': the toolkit cannot prove a person opened a tab, so 'unknown' is the honest word for a tab it did not create, one opened before the toolkit ran, or one whose record could not be written. A tab whose record exists but could not be read reports origin 'unknown' plus 'originUnreadable', so a broken record is never mistaken for no record. Under CDP_REQUIRE_LEASE this call also reaps: a tab this toolkit created whose lease is stale because the owning process died or the TTL elapsed is closed, and the closed tabs are reported in an additive 'reaped' array ({targetId,label,reason}) that is present only when something was actually closed. A tab with no lease is never reaped, so tabs opened by a caller that never uses leases are untouched.",
|
|
29
29
|
"inputSchema": {
|
|
30
30
|
"type": "object",
|
|
31
31
|
"properties": {
|
|
32
32
|
"lease": {
|
|
33
33
|
"type": "string",
|
|
34
|
-
"description": "Opaque lease token from claim_page.
|
|
34
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
35
35
|
},
|
|
36
36
|
"all": {
|
|
37
37
|
"type": "boolean",
|
|
@@ -50,7 +50,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
50
50
|
"properties": {
|
|
51
51
|
"lease": {
|
|
52
52
|
"type": "string",
|
|
53
|
-
"description": "Opaque lease token from claim_page.
|
|
53
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
54
54
|
},
|
|
55
55
|
"url": {
|
|
56
56
|
"type": "string",
|
|
@@ -58,7 +58,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
58
58
|
},
|
|
59
59
|
"claim": {
|
|
60
60
|
"type": "boolean",
|
|
61
|
-
"description": "Claim the new tab atomically as part of creating it and return a lease token alongside {targetId,url}. Default false, which is byte-identical to the pre-1.2 behavior."
|
|
61
|
+
"description": "Claim the new tab atomically as part of creating it and return a lease token alongside {targetId,url}. Default false, which is byte-identical to the pre-1.2 behavior. Under CDP_REQUIRE_LEASE the new tab is claimed and a lease returned even without claim:true, because a tab nobody holds is a tab nobody may drive; passing claim:true additionally makes the lease an explicit one, which requires its token on every later call even from this same process."
|
|
62
62
|
},
|
|
63
63
|
"label": {
|
|
64
64
|
"type": "string",
|
|
@@ -81,7 +81,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
81
81
|
"properties": {
|
|
82
82
|
"lease": {
|
|
83
83
|
"type": "string",
|
|
84
|
-
"description": "Opaque lease token from claim_page.
|
|
84
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
85
85
|
},
|
|
86
86
|
"target": {
|
|
87
87
|
"type": "string",
|
|
@@ -102,7 +102,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
102
102
|
"properties": {
|
|
103
103
|
"lease": {
|
|
104
104
|
"type": "string",
|
|
105
|
-
"description": "Opaque lease token from claim_page.
|
|
105
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
106
106
|
},
|
|
107
107
|
"target": {
|
|
108
108
|
"type": "string",
|
|
@@ -123,7 +123,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
123
123
|
"properties": {
|
|
124
124
|
"lease": {
|
|
125
125
|
"type": "string",
|
|
126
|
-
"description": "Opaque lease token from claim_page.
|
|
126
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
127
127
|
},
|
|
128
128
|
"target": {
|
|
129
129
|
"type": "string",
|
|
@@ -165,7 +165,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
165
165
|
"properties": {
|
|
166
166
|
"lease": {
|
|
167
167
|
"type": "string",
|
|
168
|
-
"description": "Opaque lease token from claim_page.
|
|
168
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
169
169
|
},
|
|
170
170
|
"target": {
|
|
171
171
|
"type": "string",
|
|
@@ -198,7 +198,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
198
198
|
"properties": {
|
|
199
199
|
"lease": {
|
|
200
200
|
"type": "string",
|
|
201
|
-
"description": "Opaque lease token from claim_page.
|
|
201
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
202
202
|
},
|
|
203
203
|
"target": {
|
|
204
204
|
"type": "string",
|
|
@@ -235,7 +235,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
235
235
|
"properties": {
|
|
236
236
|
"lease": {
|
|
237
237
|
"type": "string",
|
|
238
|
-
"description": "Opaque lease token from claim_page.
|
|
238
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
239
239
|
},
|
|
240
240
|
"target": {
|
|
241
241
|
"type": "string",
|
|
@@ -266,7 +266,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
266
266
|
"properties": {
|
|
267
267
|
"lease": {
|
|
268
268
|
"type": "string",
|
|
269
|
-
"description": "Opaque lease token from claim_page.
|
|
269
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
270
270
|
},
|
|
271
271
|
"target": {
|
|
272
272
|
"type": "string",
|
|
@@ -322,7 +322,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
322
322
|
"properties": {
|
|
323
323
|
"lease": {
|
|
324
324
|
"type": "string",
|
|
325
|
-
"description": "Opaque lease token from claim_page.
|
|
325
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
326
326
|
},
|
|
327
327
|
"target": {
|
|
328
328
|
"type": "string",
|
|
@@ -357,7 +357,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
357
357
|
"properties": {
|
|
358
358
|
"lease": {
|
|
359
359
|
"type": "string",
|
|
360
|
-
"description": "Opaque lease token from claim_page.
|
|
360
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
361
361
|
},
|
|
362
362
|
"target": {
|
|
363
363
|
"type": "string",
|
|
@@ -380,7 +380,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
380
380
|
"properties": {
|
|
381
381
|
"lease": {
|
|
382
382
|
"type": "string",
|
|
383
|
-
"description": "Opaque lease token from claim_page.
|
|
383
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
384
384
|
},
|
|
385
385
|
"target": {
|
|
386
386
|
"type": "string",
|
|
@@ -420,7 +420,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
420
420
|
"properties": {
|
|
421
421
|
"lease": {
|
|
422
422
|
"type": "string",
|
|
423
|
-
"description": "Opaque lease token from claim_page.
|
|
423
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
424
424
|
},
|
|
425
425
|
"target": {
|
|
426
426
|
"type": "string",
|
|
@@ -447,7 +447,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
447
447
|
"properties": {
|
|
448
448
|
"lease": {
|
|
449
449
|
"type": "string",
|
|
450
|
-
"description": "Opaque lease token from claim_page.
|
|
450
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
451
451
|
},
|
|
452
452
|
"target": {
|
|
453
453
|
"type": "string",
|
|
@@ -499,7 +499,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
499
499
|
"properties": {
|
|
500
500
|
"lease": {
|
|
501
501
|
"type": "string",
|
|
502
|
-
"description": "Opaque lease token from claim_page.
|
|
502
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
503
503
|
},
|
|
504
504
|
"target": {
|
|
505
505
|
"type": "string",
|
|
@@ -532,7 +532,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
532
532
|
"properties": {
|
|
533
533
|
"lease": {
|
|
534
534
|
"type": "string",
|
|
535
|
-
"description": "Opaque lease token from claim_page.
|
|
535
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
536
536
|
},
|
|
537
537
|
"target": {
|
|
538
538
|
"type": "string",
|
|
@@ -578,7 +578,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
578
578
|
"properties": {
|
|
579
579
|
"lease": {
|
|
580
580
|
"type": "string",
|
|
581
|
-
"description": "Opaque lease token from claim_page.
|
|
581
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
582
582
|
},
|
|
583
583
|
"target": {
|
|
584
584
|
"type": "string",
|
|
@@ -611,7 +611,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
611
611
|
"properties": {
|
|
612
612
|
"lease": {
|
|
613
613
|
"type": "string",
|
|
614
|
-
"description": "Opaque lease token from claim_page.
|
|
614
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
615
615
|
},
|
|
616
616
|
"target": {
|
|
617
617
|
"type": "string",
|
|
@@ -643,7 +643,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
643
643
|
"properties": {
|
|
644
644
|
"lease": {
|
|
645
645
|
"type": "string",
|
|
646
|
-
"description": "Opaque lease token from claim_page.
|
|
646
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
647
647
|
},
|
|
648
648
|
"target": {
|
|
649
649
|
"type": "string",
|
|
@@ -682,7 +682,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
682
682
|
"properties": {
|
|
683
683
|
"lease": {
|
|
684
684
|
"type": "string",
|
|
685
|
-
"description": "Opaque lease token from claim_page.
|
|
685
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
686
686
|
},
|
|
687
687
|
"target": {
|
|
688
688
|
"type": "string",
|
|
@@ -733,7 +733,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
733
733
|
"properties": {
|
|
734
734
|
"lease": {
|
|
735
735
|
"type": "string",
|
|
736
|
-
"description": "Opaque lease token from claim_page.
|
|
736
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
737
737
|
},
|
|
738
738
|
"target": {
|
|
739
739
|
"type": "string",
|
|
@@ -833,7 +833,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
833
833
|
"properties": {
|
|
834
834
|
"lease": {
|
|
835
835
|
"type": "string",
|
|
836
|
-
"description": "Opaque lease token from claim_page.
|
|
836
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
837
837
|
},
|
|
838
838
|
"target": {
|
|
839
839
|
"type": "string",
|
|
@@ -871,7 +871,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
871
871
|
"properties": {
|
|
872
872
|
"lease": {
|
|
873
873
|
"type": "string",
|
|
874
|
-
"description": "Opaque lease token from claim_page.
|
|
874
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
875
875
|
},
|
|
876
876
|
"target": {
|
|
877
877
|
"type": "string",
|
|
@@ -908,7 +908,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
908
908
|
"properties": {
|
|
909
909
|
"lease": {
|
|
910
910
|
"type": "string",
|
|
911
|
-
"description": "Opaque lease token from claim_page.
|
|
911
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
912
912
|
},
|
|
913
913
|
"target": {
|
|
914
914
|
"type": "string",
|
|
@@ -935,7 +935,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
935
935
|
"properties": {
|
|
936
936
|
"lease": {
|
|
937
937
|
"type": "string",
|
|
938
|
-
"description": "Opaque lease token from claim_page.
|
|
938
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
939
939
|
},
|
|
940
940
|
"target": {
|
|
941
941
|
"type": "string",
|
|
@@ -958,7 +958,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
958
958
|
"properties": {
|
|
959
959
|
"lease": {
|
|
960
960
|
"type": "string",
|
|
961
|
-
"description": "Opaque lease token from claim_page.
|
|
961
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
962
962
|
},
|
|
963
963
|
"target": {
|
|
964
964
|
"type": "string",
|
|
@@ -989,7 +989,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
989
989
|
"properties": {
|
|
990
990
|
"lease": {
|
|
991
991
|
"type": "string",
|
|
992
|
-
"description": "Opaque lease token from claim_page.
|
|
992
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
993
993
|
},
|
|
994
994
|
"target": {
|
|
995
995
|
"type": "string",
|
|
@@ -1024,7 +1024,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1024
1024
|
"properties": {
|
|
1025
1025
|
"lease": {
|
|
1026
1026
|
"type": "string",
|
|
1027
|
-
"description": "Opaque lease token from claim_page.
|
|
1027
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1028
1028
|
},
|
|
1029
1029
|
"target": {
|
|
1030
1030
|
"type": "string",
|
|
@@ -1050,7 +1050,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1050
1050
|
"properties": {
|
|
1051
1051
|
"lease": {
|
|
1052
1052
|
"type": "string",
|
|
1053
|
-
"description": "Opaque lease token from claim_page.
|
|
1053
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1054
1054
|
},
|
|
1055
1055
|
"target": {
|
|
1056
1056
|
"type": "string",
|
|
@@ -1069,7 +1069,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1069
1069
|
"properties": {
|
|
1070
1070
|
"lease": {
|
|
1071
1071
|
"type": "string",
|
|
1072
|
-
"description": "Opaque lease token from claim_page.
|
|
1072
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1073
1073
|
},
|
|
1074
1074
|
"tracePath": {
|
|
1075
1075
|
"type": "string",
|
|
@@ -1088,7 +1088,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1088
1088
|
"properties": {
|
|
1089
1089
|
"lease": {
|
|
1090
1090
|
"type": "string",
|
|
1091
|
-
"description": "Opaque lease token from claim_page.
|
|
1091
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1092
1092
|
},
|
|
1093
1093
|
"target": {
|
|
1094
1094
|
"type": "string",
|
|
@@ -1126,7 +1126,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1126
1126
|
"properties": {
|
|
1127
1127
|
"lease": {
|
|
1128
1128
|
"type": "string",
|
|
1129
|
-
"description": "Opaque lease token from claim_page.
|
|
1129
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1130
1130
|
},
|
|
1131
1131
|
"target": {
|
|
1132
1132
|
"type": "string",
|
|
@@ -1173,7 +1173,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1173
1173
|
"properties": {
|
|
1174
1174
|
"lease": {
|
|
1175
1175
|
"type": "string",
|
|
1176
|
-
"description": "Opaque lease token from claim_page.
|
|
1176
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1177
1177
|
},
|
|
1178
1178
|
"target": {
|
|
1179
1179
|
"type": "string",
|
|
@@ -1196,7 +1196,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1196
1196
|
"properties": {
|
|
1197
1197
|
"lease": {
|
|
1198
1198
|
"type": "string",
|
|
1199
|
-
"description": "Opaque lease token from claim_page.
|
|
1199
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1200
1200
|
},
|
|
1201
1201
|
"target": {
|
|
1202
1202
|
"type": "string",
|
|
@@ -1219,7 +1219,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1219
1219
|
"properties": {
|
|
1220
1220
|
"lease": {
|
|
1221
1221
|
"type": "string",
|
|
1222
|
-
"description": "Opaque lease token from claim_page.
|
|
1222
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1223
1223
|
},
|
|
1224
1224
|
"url": {
|
|
1225
1225
|
"type": "string",
|
|
@@ -1263,7 +1263,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1263
1263
|
"properties": {
|
|
1264
1264
|
"lease": {
|
|
1265
1265
|
"type": "string",
|
|
1266
|
-
"description": "Opaque lease token from claim_page.
|
|
1266
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1267
1267
|
},
|
|
1268
1268
|
"target": {
|
|
1269
1269
|
"type": "string",
|
|
@@ -1333,7 +1333,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1333
1333
|
"properties": {
|
|
1334
1334
|
"lease": {
|
|
1335
1335
|
"type": "string",
|
|
1336
|
-
"description": "Opaque lease token from claim_page.
|
|
1336
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1337
1337
|
},
|
|
1338
1338
|
"target": {
|
|
1339
1339
|
"type": "string",
|
|
@@ -1352,7 +1352,7 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1352
1352
|
"properties": {
|
|
1353
1353
|
"lease": {
|
|
1354
1354
|
"type": "string",
|
|
1355
|
-
"description": "Opaque lease token from claim_page.
|
|
1355
|
+
"description": "Opaque lease token from claim_page. Omit it for a tab this process already holds: under CDP_REQUIRE_LEASE the gate acquires a lease automatically and one acquired that way passes for any later call from the same process. Required when the tab is held by ANOTHER process, and required for a tab claimed explicitly via claim_page or new_page{claim:true}, which always demands its own token. Without CDP_REQUIRE_LEASE an unleased tab needs no token at all."
|
|
1356
1356
|
},
|
|
1357
1357
|
"target": {
|
|
1358
1358
|
"type": "string",
|
|
@@ -1369,17 +1369,21 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1369
1369
|
},
|
|
1370
1370
|
{
|
|
1371
1371
|
"name": "claim_page",
|
|
1372
|
-
"description": "Take exclusive ownership of a browser tab and return an opaque lease token.
|
|
1372
|
+
"description": "Take exclusive ownership of a browser tab and return an opaque lease token. Two modes. FRESH TAB: with no target and no targetId, opens a new tab (optionally at url) and claims it, so 'give me my own tab' is one call. TAKEOVER: with target, claims a tab that is already open — this is how you work in a tab the human already has open when they ask you to, instead of opening your own — resolving any target selector against the live page list and never creating anything. The answer's 'opened' flag says which happened (true only when this call created the tab), and that same creation record is what release_page consults, so a tab you took over is left open when you release it. Any later tool call against a leased tab must carry the matching token in its 'lease' argument or it is refused, naming the holder. The lease is reclaimable once its owning process dies, its ttlMs elapses without use, or its tab is closed. Refused from the CLI: a CLI invocation is one process per call, so its lease would be reclaimable immediately.",
|
|
1373
1373
|
"inputSchema": {
|
|
1374
1374
|
"type": "object",
|
|
1375
1375
|
"properties": {
|
|
1376
|
+
"target": {
|
|
1377
|
+
"type": "string",
|
|
1378
|
+
"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."
|
|
1379
|
+
},
|
|
1376
1380
|
"targetId": {
|
|
1377
1381
|
"type": "string",
|
|
1378
|
-
"description": "Claim this already-open page target instead of opening a new tab. Must be an exact target id, not a selector."
|
|
1382
|
+
"description": "Claim this already-open page target instead of opening a new tab. Must be an exact target id, not a selector. Kept for back-compat: prefer 'target', which accepts an exact id too plus every other selector form. Mutually exclusive with target."
|
|
1379
1383
|
},
|
|
1380
1384
|
"url": {
|
|
1381
1385
|
"type": "string",
|
|
1382
|
-
"description": "When opening a fresh tab, navigate it here. Ignored when targetId is given."
|
|
1386
|
+
"description": "When opening a fresh tab, navigate it here. Ignored when target or targetId is given, since both of those claim a tab that is already open."
|
|
1383
1387
|
},
|
|
1384
1388
|
"label": {
|
|
1385
1389
|
"type": "string",
|
|
@@ -1396,24 +1400,30 @@ export const MANIFEST: ToolSpec[] = [
|
|
|
1396
1400
|
},
|
|
1397
1401
|
{
|
|
1398
1402
|
"name": "release_page",
|
|
1399
|
-
"description": "
|
|
1403
|
+
"description": "Give a lease back, and close the tab if this toolkit opened it. Takes exactly one of 'lease' (the token claim_page returned) or 'target' (a selector for a tab this process holds, which is how you release a lease the gate acquired for you automatically, since that path never hands you a token). A tab with a creation record from this toolkit is closed; a tab that was already open and merely claimed is released and left alone. Override either way with 'close'. Idempotent: an already-released, reclaimed, or expired lease reports released:false, and a release that did not happen never closes anything, because by then the tab may belong to another agent. Answers {released, closed, targetId}.",
|
|
1400
1404
|
"inputSchema": {
|
|
1401
1405
|
"type": "object",
|
|
1402
1406
|
"properties": {
|
|
1403
1407
|
"lease": {
|
|
1404
1408
|
"type": "string",
|
|
1405
|
-
"description": "The opaque token claim_page (or new_page with claim:true) returned."
|
|
1409
|
+
"description": "The opaque token claim_page (or new_page with claim:true) returned. Mutually exclusive with 'target'. Under CDP_REQUIRE_LEASE a lease the gate acquired for you automatically never produced a token in the first place, so give 'target' instead."
|
|
1410
|
+
},
|
|
1411
|
+
"target": {
|
|
1412
|
+
"type": "string",
|
|
1413
|
+
"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."
|
|
1414
|
+
},
|
|
1415
|
+
"close": {
|
|
1416
|
+
"type": "boolean",
|
|
1417
|
+
"description": "Force the close decision instead of letting the creation ledger decide. true closes even a tab this toolkit did not open; false keeps an agent-created tab open. Omit for the default, which closes only tabs this toolkit created."
|
|
1406
1418
|
}
|
|
1407
1419
|
},
|
|
1408
|
-
"required": [
|
|
1409
|
-
"lease"
|
|
1410
|
-
],
|
|
1420
|
+
"required": [],
|
|
1411
1421
|
"additionalProperties": false
|
|
1412
1422
|
}
|
|
1413
1423
|
},
|
|
1414
1424
|
{
|
|
1415
1425
|
"name": "list_leases",
|
|
1416
|
-
"description": "Enumerate every active lease for diagnosis: backend, target id, agent label, pid, createdAt, lastUsedAt, ttlMs, whether the owning process is still alive, and whether the lease is reclaimable and why. Requires no token. Never returns the lease nonce, so this cannot be used to forge a token. A row for a lease file that could not be read or parsed instead carries `unreadable` (the errno, or \"unparseable\"), with label, pid, and the timestamp fields as zero placeholders and stale always false, since stale is what marks a lease free to take and an unreadable one must never read that way.",
|
|
1426
|
+
"description": "Enumerate every active lease for diagnosis: backend, target id, agent label, pid, createdAt, lastUsedAt, ttlMs, whether the owning process is still alive, and whether the lease is reclaimable and why. Requires no token. Never returns the lease nonce, so this cannot be used to forge a token. A row for a lease file that could not be read or parsed instead carries `unreadable` (the errno, or \"unparseable\"), with label, pid, and the timestamp fields as zero placeholders and stale always false, since stale is what marks a lease free to take and an unreadable one must never read that way. Under CDP_REQUIRE_LEASE this call reaps abandoned agent tabs exactly as list_pages does, reporting them in an additive 'reaped' array and omitting their rows from the listing.",
|
|
1417
1427
|
"inputSchema": {
|
|
1418
1428
|
"type": "object",
|
|
1419
1429
|
"properties": {},
|
package/src/mcp.ts
CHANGED
|
@@ -21,9 +21,9 @@ import { MANIFEST } from "./manifest.ts";
|
|
|
21
21
|
import { resolveBrowserKind, stripBrowserFlag, getOrCreateFirefoxSession, disposeFirefoxSession } from "./backend.ts";
|
|
22
22
|
import { toolAvailability } from "./capabilities.ts";
|
|
23
23
|
import { FIREFOX_TOOLS } from "./firefox-tools.ts";
|
|
24
|
-
import { leaseFromArgs, withLeaseScope } from "./leases.ts";
|
|
24
|
+
import { leaseFromArgs, markLongLivedProcess, withLeaseScope } from "./leases.ts";
|
|
25
25
|
|
|
26
|
-
const VERSION = "1.
|
|
26
|
+
const VERSION = "1.7.0";
|
|
27
27
|
|
|
28
28
|
// --browser is read once at startup (MCP has no per-call notion of backend): explicit flag,
|
|
29
29
|
// else CDP_BROWSER env var, else "chrome" (zero behavior change for existing users/configs).
|
|
@@ -100,6 +100,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
auditCoverage();
|
|
103
|
+
// This process is the long-lived MCP server, which is what makes strict mode
|
|
104
|
+
// (CDP_REQUIRE_LEASE) safe to honor here and unsafe in cli.ts. See requireLease.
|
|
105
|
+
markLongLivedProcess();
|
|
103
106
|
await server.connect(new StdioServerTransport());
|
|
104
107
|
console.error(`[cdp-toolkit] MCP server v${VERSION} ready, browser=${BROWSER}, ${AVAILABILITY.available.length} tools, CDP_BASE=${BASE}`);
|
|
105
108
|
|
package/src/reap.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closing tabs whose agent died, without a background sweeper.
|
|
3
|
+
*
|
|
4
|
+
* WHY REAP AT ALL. release_page closes a tab an agent gives back, but an agent
|
|
5
|
+
* that crashes, is killed, or simply stops calling never gives anything back.
|
|
6
|
+
* Its tab stays open forever with a lease file nobody will ever release. That
|
|
7
|
+
* is the failure this module exists for, and it is the common one: a timeout is
|
|
8
|
+
* a more likely end to an agent than a clean shutdown.
|
|
9
|
+
*
|
|
10
|
+
* WHY REAP-ON-READ AND NOT A SWEEPER. Same reasoning as origins.ts's ledger: a
|
|
11
|
+
* background sweeper is a second lifetime to reason about and a process that
|
|
12
|
+
* has to be running. list_pages and list_leases already hold the browser's
|
|
13
|
+
* target list and the lease directory at the moment they run, so doing it there
|
|
14
|
+
* costs one extra pass over data already in hand and needs nothing scheduled.
|
|
15
|
+
*
|
|
16
|
+
* WHY THE SELECTION IS A PURE FUNCTION. Every mistake in this module closes a
|
|
17
|
+
* tab someone wanted. A pure selector over plain data is testable across the
|
|
18
|
+
* whole matrix of {provenance} x {lease state} x {liveness} with no browser and
|
|
19
|
+
* no filesystem, which is the only way to be confident about a destructive
|
|
20
|
+
* operation. The impure wrapper below does nothing but call it and act.
|
|
21
|
+
*/
|
|
22
|
+
import type { BrowserDriver } from "./driver.ts";
|
|
23
|
+
import { listLeases, releaseLeaseFor, type LeaseBackend, type LeaseSummary } from "./leases.ts";
|
|
24
|
+
import { originIndex, type OriginSummary } from "./origins.ts";
|
|
25
|
+
|
|
26
|
+
export interface ReapedTab {
|
|
27
|
+
targetId: string;
|
|
28
|
+
label: string;
|
|
29
|
+
/** Why it was reaped. Deliberately narrower than LeaseStaleReason: see below. */
|
|
30
|
+
reason: "dead-pid" | "expired";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ReapInput {
|
|
34
|
+
backend: LeaseBackend;
|
|
35
|
+
/**
|
|
36
|
+
* Ids from the PAGE-ONLY listing, never from an `all:true` listing. A worker,
|
|
37
|
+
* iframe or background page can carry a lease (pickPage's bare-id branch
|
|
38
|
+
* resolves non-page targets), and "close the stale tab" is meaningless for
|
|
39
|
+
* one. This is a different set from the one shared-tools' reapSet feeds to the
|
|
40
|
+
* origin ledger, which deliberately uses the unfiltered listing so a live but
|
|
41
|
+
* filtered-out target does not lose its provenance record. Two reaps, two
|
|
42
|
+
* questions, two id sets: do not merge them.
|
|
43
|
+
*/
|
|
44
|
+
livePageIds: readonly string[];
|
|
45
|
+
origins: ReadonlyMap<string, OriginSummary>;
|
|
46
|
+
leases: readonly LeaseSummary[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Select the tabs that may be closed. PURE: no I/O, no driver, no clock.
|
|
51
|
+
*
|
|
52
|
+
* All four conditions must hold, and condition 4 is the load-bearing one:
|
|
53
|
+
*
|
|
54
|
+
* 1. the lease row is for this backend and READABLE. An `unreadable` row is
|
|
55
|
+
* never reaped for the same reason listLeases reports it as stale:false -
|
|
56
|
+
* we cannot see who owns it, and a guess in the destructive direction is
|
|
57
|
+
* the one guess that cannot be undone.
|
|
58
|
+
* 2. it is stale for `dead-pid` or `expired`. NOT `target-gone`, which means
|
|
59
|
+
* the tab is already closed and there is nothing to do, and obviously not
|
|
60
|
+
* `false`.
|
|
61
|
+
* 3. the target is actually still open, per livePageIds.
|
|
62
|
+
* 4. the toolkit OPENED it. An agent-created tab with NO lease is deliberately
|
|
63
|
+
* never reaped: that is exactly what new_page produces for a user who never
|
|
64
|
+
* touches leases, and closing those would be a data-loss bug rather than a
|
|
65
|
+
* cleanup. Only a tab that was claimed and then abandoned qualifies.
|
|
66
|
+
*/
|
|
67
|
+
export function staleAgentTabs(input: ReapInput): ReapedTab[] {
|
|
68
|
+
const live = new Set(input.livePageIds);
|
|
69
|
+
const out: ReapedTab[] = [];
|
|
70
|
+
for (const row of input.leases) {
|
|
71
|
+
if (row.backend !== input.backend) continue;
|
|
72
|
+
if (row.unreadable !== undefined) continue;
|
|
73
|
+
if (row.stale !== "dead-pid" && row.stale !== "expired") continue;
|
|
74
|
+
if (!live.has(row.targetId)) continue;
|
|
75
|
+
const origin = input.origins.get(row.targetId);
|
|
76
|
+
if (origin === undefined || origin.unreadable !== undefined) continue;
|
|
77
|
+
out.push({ targetId: row.targetId, label: row.label, reason: row.stale });
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Select, close, and drop the leases. Returns ONLY the tabs actually closed.
|
|
84
|
+
*
|
|
85
|
+
* A close that fails is not reported, and that asymmetry is deliberate: the
|
|
86
|
+
* caller filters its listing by this array, so reporting a tab we failed to
|
|
87
|
+
* close would hide a tab that is still open. Failing quietly leaves the tab
|
|
88
|
+
* visible and the lease in place, and the next read tries again.
|
|
89
|
+
*
|
|
90
|
+
* The lease file is unlinked; the ORIGIN record is not. listOrigins already
|
|
91
|
+
* reaps records for targets the browser no longer has, so the record disappears
|
|
92
|
+
* on the next listing. One lifetime rule for origin records, not three.
|
|
93
|
+
*/
|
|
94
|
+
export async function reapStaleAgentTabs(driver: BrowserDriver, backend: LeaseBackend): Promise<ReapedTab[]> {
|
|
95
|
+
// The PAGE-ONLY listing: see ReapInput.livePageIds.
|
|
96
|
+
const livePageIds = (await driver.listPages()).map((p) => p.id);
|
|
97
|
+
const [origins, leases] = await Promise.all([
|
|
98
|
+
// undefined liveIds = reap nothing from the ledger here. Reaping origin
|
|
99
|
+
// records is list_pages' job and it uses a DIFFERENT id set (the unfiltered
|
|
100
|
+
// listing); doing it here too would delete records for targets that are
|
|
101
|
+
// live but filtered out of the page-only view.
|
|
102
|
+
originIndex(backend, undefined),
|
|
103
|
+
listLeases({ liveIds: livePageIds, liveBackend: backend }).catch(() => [] as LeaseSummary[]),
|
|
104
|
+
]);
|
|
105
|
+
const candidates = staleAgentTabs({ backend, livePageIds, origins, leases });
|
|
106
|
+
const closed: ReapedTab[] = [];
|
|
107
|
+
for (const c of candidates) {
|
|
108
|
+
const res = await driver.closePage(c.targetId).catch(() => ({ success: false }));
|
|
109
|
+
if (res.success !== true) continue;
|
|
110
|
+
await releaseLeaseFor(backend, c.targetId);
|
|
111
|
+
closed.push(c);
|
|
112
|
+
}
|
|
113
|
+
return closed;
|
|
114
|
+
}
|