@phnx-labs/agents-cli 1.22.117 → 1.22.119

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.
Files changed (271) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/accounts.js +7 -4
  4. package/dist/commands/browser.d.ts +52 -70
  5. package/dist/commands/browser.js +192 -3562
  6. package/dist/commands/daemon.d.ts +39 -2
  7. package/dist/commands/daemon.js +72 -73
  8. package/dist/commands/events.js +1 -1
  9. package/dist/commands/exec.js +53 -1
  10. package/dist/commands/projects.js +92 -7
  11. package/dist/commands/repo.js +13 -49
  12. package/dist/commands/route.js +5 -5
  13. package/dist/commands/routines.js +39 -12
  14. package/dist/commands/sessions-render.d.ts +1 -1
  15. package/dist/commands/sessions-render.js +0 -1
  16. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  17. package/dist/commands/sessions.test-fixture.js +0 -37
  18. package/dist/commands/setup-browser.d.ts +17 -12
  19. package/dist/commands/setup-browser.js +52 -87
  20. package/dist/commands/setup-preferences.d.ts +0 -17
  21. package/dist/commands/setup-preferences.js +18 -49
  22. package/dist/commands/setup.js +9 -9
  23. package/dist/commands/ssh.js +10 -1
  24. package/dist/commands/sync.js +6 -44
  25. package/dist/commands/teams.js +0 -1
  26. package/dist/commands/update.d.ts +19 -0
  27. package/dist/commands/update.js +34 -6
  28. package/dist/commands/view.d.ts +12 -1
  29. package/dist/commands/view.js +47 -2
  30. package/dist/index.js +107 -26
  31. package/dist/lib/account-capabilities.js +0 -2
  32. package/dist/lib/account-catalog.d.ts +38 -0
  33. package/dist/lib/account-catalog.js +110 -26
  34. package/dist/lib/account-provider-registry.js +1 -1
  35. package/dist/lib/account-registry.d.ts +1 -1
  36. package/dist/lib/account-registry.js +36 -2
  37. package/dist/lib/accounting/rotate.js +22 -8
  38. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  39. package/dist/lib/accounting/usage-ingest.js +115 -45
  40. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  41. package/dist/lib/accounting/usage-sync.js +227 -66
  42. package/dist/lib/accounting/usage.d.ts +16 -3
  43. package/dist/lib/accounting/usage.js +26 -8
  44. package/dist/lib/accounts/add.d.ts +3 -1
  45. package/dist/lib/accounts/add.js +24 -13
  46. package/dist/lib/accounts/slots.d.ts +8 -0
  47. package/dist/lib/accounts/slots.js +17 -0
  48. package/dist/lib/add-dir.js +0 -1
  49. package/dist/lib/agent-cli-commands.js +0 -1
  50. package/dist/lib/agent-spec/agents.d.ts +6 -7
  51. package/dist/lib/agent-spec/agents.js +7 -60
  52. package/dist/lib/auth-health.d.ts +69 -1
  53. package/dist/lib/auth-health.js +133 -4
  54. package/dist/lib/browser/context.d.ts +82 -0
  55. package/dist/lib/browser/context.js +88 -0
  56. package/dist/lib/browser/paths.d.ts +25 -0
  57. package/dist/lib/browser/paths.js +51 -0
  58. package/dist/lib/browser/record.d.ts +38 -0
  59. package/dist/lib/browser/record.js +57 -0
  60. package/dist/lib/browser/sessions-list.js +1 -2
  61. package/dist/lib/browser-client.d.ts +158 -0
  62. package/dist/lib/browser-client.js +234 -0
  63. package/dist/lib/channels/owner-sink.d.ts +1 -18
  64. package/dist/lib/channels/owner-sink.js +29 -82
  65. package/dist/lib/channels/providers/rush.d.ts +7 -7
  66. package/dist/lib/channels/providers/rush.js +127 -57
  67. package/dist/lib/claude-account-token.d.ts +7 -1
  68. package/dist/lib/claude-account-token.js +26 -4
  69. package/dist/lib/claude-statusline.d.ts +1 -1
  70. package/dist/lib/claude-statusline.js +15 -3
  71. package/dist/lib/commands.js +1 -1
  72. package/dist/lib/crabbox/lease.js +1 -1
  73. package/dist/lib/crabbox/runtimes.js +1 -3
  74. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  75. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  76. package/dist/lib/daemon/auth-sync-service.js +37 -43
  77. package/dist/lib/daemon/daemon.d.ts +25 -14
  78. package/dist/lib/daemon/daemon.js +92 -154
  79. package/dist/lib/daemon/runner.d.ts +2 -2
  80. package/dist/lib/daemon/runner.js +32 -9
  81. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  82. package/dist/lib/daemon/self-update-service.js +40 -36
  83. package/dist/lib/daemon/service.d.ts +12 -8
  84. package/dist/lib/daemon/supervisor.d.ts +65 -54
  85. package/dist/lib/daemon/supervisor.js +136 -171
  86. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  87. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  88. package/dist/lib/daemon/usage-sync-service.js +46 -78
  89. package/dist/lib/daemon-health.d.ts +22 -5
  90. package/dist/lib/daemon-health.js +47 -3
  91. package/dist/lib/daemon-services.d.ts +1 -1
  92. package/dist/lib/daemon-services.js +0 -10
  93. package/dist/lib/daemon-ticks.js +4 -2
  94. package/dist/lib/devices/doctor-findings.js +8 -11
  95. package/dist/lib/devices/harness-inventory.js +14 -5
  96. package/dist/lib/exec-account-home.js +5 -0
  97. package/dist/lib/exec.js +10 -13
  98. package/dist/lib/feed/events.js +0 -1
  99. package/dist/lib/feed/tool-activity.js +5 -2
  100. package/dist/lib/fleet-shared-state.d.ts +45 -0
  101. package/dist/lib/fleet-shared-state.js +78 -9
  102. package/dist/lib/fs-atomic.js +25 -0
  103. package/dist/lib/github/pr-mergeable.d.ts +4 -2
  104. package/dist/lib/github/pr-mergeable.js +5 -3
  105. package/dist/lib/github/project-prs.d.ts +58 -3
  106. package/dist/lib/github/project-prs.js +205 -7
  107. package/dist/lib/harness/adapters/cursor.js +14 -4
  108. package/dist/lib/harness-auth-capabilities.d.ts +4 -3
  109. package/dist/lib/harness-auth-capabilities.js +1 -2
  110. package/dist/lib/helper-versions.js +1 -1
  111. package/dist/lib/hooks/cache.d.ts +7 -0
  112. package/dist/lib/hooks/cache.js +27 -2
  113. package/dist/lib/hooks/install.js +7 -1
  114. package/dist/lib/hosts/ready.d.ts +38 -8
  115. package/dist/lib/hosts/ready.js +62 -14
  116. package/dist/lib/installations/active-check.d.ts +27 -0
  117. package/dist/lib/installations/active-check.js +61 -0
  118. package/dist/lib/installations/migrate.js +6 -3
  119. package/dist/lib/installations/shims.d.ts +1 -1
  120. package/dist/lib/installations/shims.js +32 -12
  121. package/dist/lib/installations/store.d.ts +9 -0
  122. package/dist/lib/installations/store.js +24 -0
  123. package/dist/lib/installations/update.js +21 -15
  124. package/dist/lib/installations/versions.js +15 -3
  125. package/dist/lib/manifest.js +1 -1
  126. package/dist/lib/models.js +0 -2
  127. package/dist/lib/open-url.js +28 -54
  128. package/dist/lib/profiles.js +0 -2
  129. package/dist/lib/projects.d.ts +16 -0
  130. package/dist/lib/projects.js +31 -0
  131. package/dist/lib/refresh.js +1 -1
  132. package/dist/lib/routine-readiness.d.ts +35 -0
  133. package/dist/lib/routine-readiness.js +74 -16
  134. package/dist/lib/rules/compile.js +6 -0
  135. package/dist/lib/sandbox.js +0 -2
  136. package/dist/lib/secrets-policy.d.ts +89 -11
  137. package/dist/lib/secrets-policy.js +175 -28
  138. package/dist/lib/session/active.d.ts +17 -6
  139. package/dist/lib/session/active.js +105 -49
  140. package/dist/lib/session/db.d.ts +1 -1
  141. package/dist/lib/session/db.js +65 -18
  142. package/dist/lib/session/discover.js +1 -283
  143. package/dist/lib/session/mirror.d.ts +4 -3
  144. package/dist/lib/session/mirror.js +4 -3
  145. package/dist/lib/session/parse.d.ts +0 -2
  146. package/dist/lib/session/parse.js +1 -138
  147. package/dist/lib/session/pid-registry.d.ts +6 -3
  148. package/dist/lib/session/pid-registry.js +18 -8
  149. package/dist/lib/session/remote/watch.d.ts +1 -1
  150. package/dist/lib/session/remote/watch.js +7 -3
  151. package/dist/lib/session/throughput.d.ts +1 -3
  152. package/dist/lib/session/throughput.js +0 -20
  153. package/dist/lib/session/types.d.ts +2 -2
  154. package/dist/lib/session/types.js +2 -2
  155. package/dist/lib/sessions-client.d.ts +60 -1
  156. package/dist/lib/sessions-client.js +227 -4
  157. package/dist/lib/setup-tool-status.js +5 -10
  158. package/dist/lib/signin-badge.d.ts +3 -2
  159. package/dist/lib/signin-badge.js +6 -3
  160. package/dist/lib/smart-launch.js +4 -1
  161. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  162. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  163. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  164. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  165. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  166. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  167. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  168. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  169. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  170. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  171. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  172. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  173. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  174. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  175. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  176. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  177. package/dist/lib/staleness/writers/sources.js +5 -0
  178. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  179. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  180. package/dist/lib/state.d.ts +6 -4
  181. package/dist/lib/state.js +7 -17
  182. package/dist/lib/teams/parsers.d.ts +1 -1
  183. package/dist/lib/teams/parsers.js +1 -136
  184. package/dist/lib/teams/placement-probe.js +50 -15
  185. package/dist/lib/teams/scheduler.d.ts +7 -0
  186. package/dist/lib/types.d.ts +16 -1
  187. package/dist/lib/types.js +1 -1
  188. package/dist/lib/view-types.d.ts +32 -0
  189. package/dist/lib/watchdog/read.js +0 -1
  190. package/dist/lib/watchdog/rotate.js +1 -2
  191. package/dist/lib/watchdog/runner.js +2 -2
  192. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  193. package/dist/lib/watchdog/watchdogTail.js +0 -31
  194. package/package.json +1 -1
  195. package/scripts/postinstall.js +8 -8
  196. package/dist/browser.d.ts +0 -2
  197. package/dist/browser.js +0 -17
  198. package/dist/commands/browser-picker.d.ts +0 -18
  199. package/dist/commands/browser-picker.js +0 -97
  200. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  201. package/dist/lib/browser/arc-discovery.js +0 -197
  202. package/dist/lib/browser/arc-dom.d.ts +0 -14
  203. package/dist/lib/browser/arc-dom.js +0 -121
  204. package/dist/lib/browser/caller-identity.d.ts +0 -40
  205. package/dist/lib/browser/caller-identity.js +0 -175
  206. package/dist/lib/browser/cdp.d.ts +0 -51
  207. package/dist/lib/browser/cdp.js +0 -254
  208. package/dist/lib/browser/chrome.d.ts +0 -141
  209. package/dist/lib/browser/chrome.js +0 -704
  210. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  211. package/dist/lib/browser/chromium-discovery.js +0 -96
  212. package/dist/lib/browser/devices.d.ts +0 -34
  213. package/dist/lib/browser/devices.js +0 -61
  214. package/dist/lib/browser/domain-skills.d.ts +0 -71
  215. package/dist/lib/browser/domain-skills.js +0 -195
  216. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  217. package/dist/lib/browser/drivers/arc.js +0 -276
  218. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  219. package/dist/lib/browser/drivers/firefox.js +0 -377
  220. package/dist/lib/browser/drivers/local.d.ts +0 -55
  221. package/dist/lib/browser/drivers/local.js +0 -266
  222. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  223. package/dist/lib/browser/drivers/ssh.js +0 -467
  224. package/dist/lib/browser/editor.d.ts +0 -3
  225. package/dist/lib/browser/editor.js +0 -50
  226. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  227. package/dist/lib/browser/ffmpeg.js +0 -184
  228. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  229. package/dist/lib/browser/firefox-discovery.js +0 -162
  230. package/dist/lib/browser/har.d.ts +0 -85
  231. package/dist/lib/browser/har.js +0 -77
  232. package/dist/lib/browser/hygiene.d.ts +0 -97
  233. package/dist/lib/browser/hygiene.js +0 -153
  234. package/dist/lib/browser/index.d.ts +0 -5
  235. package/dist/lib/browser/index.js +0 -5
  236. package/dist/lib/browser/input.d.ts +0 -7
  237. package/dist/lib/browser/input.js +0 -92
  238. package/dist/lib/browser/ipc.d.ts +0 -204
  239. package/dist/lib/browser/ipc.js +0 -1453
  240. package/dist/lib/browser/login-detection.d.ts +0 -87
  241. package/dist/lib/browser/login-detection.js +0 -274
  242. package/dist/lib/browser/profiles.d.ts +0 -422
  243. package/dist/lib/browser/profiles.js +0 -1157
  244. package/dist/lib/browser/refs.d.ts +0 -89
  245. package/dist/lib/browser/refs.js +0 -191
  246. package/dist/lib/browser/registry.d.ts +0 -70
  247. package/dist/lib/browser/registry.js +0 -252
  248. package/dist/lib/browser/remote-control.d.ts +0 -59
  249. package/dist/lib/browser/remote-control.js +0 -85
  250. package/dist/lib/browser/resolve-target.d.ts +0 -66
  251. package/dist/lib/browser/resolve-target.js +0 -264
  252. package/dist/lib/browser/runtime-state.d.ts +0 -291
  253. package/dist/lib/browser/runtime-state.js +0 -584
  254. package/dist/lib/browser/secret-ref.d.ts +0 -10
  255. package/dist/lib/browser/secret-ref.js +0 -14
  256. package/dist/lib/browser/service.d.ts +0 -812
  257. package/dist/lib/browser/service.js +0 -4693
  258. package/dist/lib/browser/stream.d.ts +0 -17
  259. package/dist/lib/browser/stream.js +0 -72
  260. package/dist/lib/browser/task-index.d.ts +0 -78
  261. package/dist/lib/browser/task-index.js +0 -199
  262. package/dist/lib/browser/types.d.ts +0 -647
  263. package/dist/lib/browser/types.js +0 -61
  264. package/dist/lib/browser/upload.d.ts +0 -29
  265. package/dist/lib/browser/upload.js +0 -298
  266. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  267. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  268. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  269. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  270. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  271. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -1,89 +0,0 @@
1
- import type { CDPClient } from './cdp.js';
2
- export interface RefOpts {
3
- interactive?: boolean;
4
- limit?: number;
5
- compact?: boolean;
6
- }
7
- export interface RefNode {
8
- ref: number;
9
- role: string;
10
- name: string;
11
- attrs: string[];
12
- backendNodeId?: number;
13
- /** Stable selector used by non-CDP DOM backends such as native Arc. */
14
- selector?: string;
15
- editor?: string;
16
- }
17
- /**
18
- * Stable, navigation-surviving identity for a ref. Unlike {@link RefNode}, this
19
- * carries no `backendNodeId` (which goes stale the moment the DOM re-renders) —
20
- * only the accessible descriptor a fresh `getRefs` can be re-matched against.
21
- * Cached in per-task state so a later `click <ref>` can self-heal when the
22
- * integer ref has drifted (see {@link healRef}).
23
- */
24
- export interface RefDescriptor {
25
- ref: number;
26
- role: string;
27
- name: string;
28
- attrs: string[];
29
- /**
30
- * Best-effort CSS selector captured via DOM. Reserved for future
31
- * selector-based healing; matching today is role+name+attrs+position.
32
- */
33
- selector?: string;
34
- }
35
- /**
36
- * A cached ref listing for one tab: the stable descriptors plus the resolved
37
- * {@link RefOpts} the listing was built with. A later `click`/`type` MUST
38
- * rebuild its node map with these same opts, or the integer refs it looks up
39
- * are numbered against a *different* accessibility filter than the one the
40
- * user saw in `browser refs` — the drift that makes self-heal silently click
41
- * the wrong element on the second click.
42
- */
43
- export interface RefSnapshot {
44
- descriptors: RefDescriptor[];
45
- opts: {
46
- interactive: boolean;
47
- limit: number;
48
- };
49
- }
50
- /** Snapshot the stable descriptor for every ref in a freshly-built node map. */
51
- export declare function describeRefs(nodeMap: Map<number, RefNode>): RefDescriptor[];
52
- /**
53
- * Re-resolve a stale ref against a freshly-built node map by matching the
54
- * cached descriptor. Pure — no CDP, no DOM.
55
- *
56
- * The integer ref assigned by {@link getRefs} is positional: it shifts whenever
57
- * the accessibility tree changes (navigation, re-render, or even a different
58
- * `interactive` filter). The descriptor's (role, name) pair is the coarse
59
- * identity, but a page can carry duplicate (role, name) elements — two "Submit"
60
- * buttons, one action button per repeated list row. Matching on (role, name)
61
- * alone would heal to whichever duplicate the fresh scan happens to hit first,
62
- * which can be the wrong one. So among (role, name) matches we tie-break, in
63
- * priority order:
64
- *
65
- * 1. **DOM-backed** — a candidate with a `backendNodeId` is resolvable to
66
- * coordinates; one without is unclickable, so healing to it is useless.
67
- * 2. **Attribute similarity** — the cached `attrs` (disabled / checked /
68
- * selected / …) disambiguate otherwise-identical twins (the *disabled*
69
- * Submit vs the enabled one).
70
- * 3. **Positional proximity** — the fresh ref closest to the original cached
71
- * ref index wins, so a repeated list-row action heals to its nearest
72
- * positional twin rather than jumping across the page.
73
- *
74
- * Returns the new integer ref, or `null` when no node with the same role+name
75
- * exists in the fresh map (unhealable — the element is genuinely gone).
76
- */
77
- export declare function healRef(descriptor: Pick<RefDescriptor, 'ref' | 'role' | 'name' | 'attrs'>, freshNodeMap: Map<number, RefNode>): number | null;
78
- export declare function getRefs(cdp: CDPClient, sessionId: string, opts?: RefOpts): Promise<{
79
- refs: string;
80
- nodeMap: Map<number, RefNode>;
81
- opts: {
82
- interactive: boolean;
83
- limit: number;
84
- };
85
- }>;
86
- export declare function resolveRefToCoords(cdp: CDPClient, sessionId: string, nodeMap: Map<number, RefNode>, ref: number): Promise<{
87
- x: number;
88
- y: number;
89
- }>;
@@ -1,191 +0,0 @@
1
- import { truncate } from '../format.js';
2
- /** Snapshot the stable descriptor for every ref in a freshly-built node map. */
3
- export function describeRefs(nodeMap) {
4
- return Array.from(nodeMap.values()).map((n) => ({
5
- ref: n.ref,
6
- role: n.role,
7
- name: n.name,
8
- attrs: n.attrs.slice(),
9
- selector: n.selector,
10
- }));
11
- }
12
- /**
13
- * Re-resolve a stale ref against a freshly-built node map by matching the
14
- * cached descriptor. Pure — no CDP, no DOM.
15
- *
16
- * The integer ref assigned by {@link getRefs} is positional: it shifts whenever
17
- * the accessibility tree changes (navigation, re-render, or even a different
18
- * `interactive` filter). The descriptor's (role, name) pair is the coarse
19
- * identity, but a page can carry duplicate (role, name) elements — two "Submit"
20
- * buttons, one action button per repeated list row. Matching on (role, name)
21
- * alone would heal to whichever duplicate the fresh scan happens to hit first,
22
- * which can be the wrong one. So among (role, name) matches we tie-break, in
23
- * priority order:
24
- *
25
- * 1. **DOM-backed** — a candidate with a `backendNodeId` is resolvable to
26
- * coordinates; one without is unclickable, so healing to it is useless.
27
- * 2. **Attribute similarity** — the cached `attrs` (disabled / checked /
28
- * selected / …) disambiguate otherwise-identical twins (the *disabled*
29
- * Submit vs the enabled one).
30
- * 3. **Positional proximity** — the fresh ref closest to the original cached
31
- * ref index wins, so a repeated list-row action heals to its nearest
32
- * positional twin rather than jumping across the page.
33
- *
34
- * Returns the new integer ref, or `null` when no node with the same role+name
35
- * exists in the fresh map (unhealable — the element is genuinely gone).
36
- */
37
- export function healRef(descriptor, freshNodeMap) {
38
- const { ref, role, name, attrs } = descriptor;
39
- const cachedAttrs = new Set(attrs);
40
- // How well a candidate's attribute set matches the cached one: reward shared
41
- // attrs, penalize both extras the candidate carries and cached attrs it is
42
- // missing. Exact set equality scores highest; an all-attrs-empty comparison
43
- // scores 0 for every candidate (so the tie-break falls through to proximity).
44
- const attrScore = (node) => {
45
- let shared = 0;
46
- for (const a of node.attrs)
47
- if (cachedAttrs.has(a))
48
- shared += 1;
49
- const extra = node.attrs.length - shared;
50
- const missing = cachedAttrs.size - shared;
51
- return shared * 2 - extra - missing;
52
- };
53
- let best = null;
54
- let bestKey = null;
55
- for (const node of freshNodeMap.values()) {
56
- if (node.role !== role || node.name !== name)
57
- continue;
58
- // Lexicographic key, all "higher is better":
59
- // [ DOM-backed, attribute similarity, -distance-to-original-ref ].
60
- const key = [
61
- node.backendNodeId !== undefined || node.selector !== undefined ? 1 : 0,
62
- attrScore(node),
63
- -Math.abs(node.ref - ref),
64
- ];
65
- if (bestKey === null ||
66
- key[0] > bestKey[0] ||
67
- (key[0] === bestKey[0] && key[1] > bestKey[1]) ||
68
- (key[0] === bestKey[0] && key[1] === bestKey[1] && key[2] > bestKey[2])) {
69
- best = node;
70
- bestKey = key;
71
- }
72
- }
73
- return best ? best.ref : null;
74
- }
75
- const EDITOR_DETECT_FN = `(function() {
76
- let el = this;
77
- for (let i = 0; i < 5; i++) {
78
- if (!el || el === document.documentElement) break;
79
- if (el.hasAttribute && el.hasAttribute('data-lexical-editor')) return 'lexical';
80
- if (el.classList && el.classList.contains('ProseMirror')) return 'prosemirror';
81
- if (el.hasAttribute && el.hasAttribute('data-slate-editor')) return 'slate';
82
- if (el.classList && Array.from(el.classList).some(function(c) { return /^DraftEditor-/.test(c); })) return 'draft';
83
- if (el.classList && el.classList.contains('ql-editor')) return 'quill';
84
- if (el.classList && el.classList.contains('ck-editor__editable')) return 'ckeditor5';
85
- if (el.tagName === 'TRIX-EDITOR') return 'trix';
86
- el = el.parentElement;
87
- }
88
- return null;
89
- })`;
90
- async function detectEditorForNode(cdp, sessionId, backendNodeId) {
91
- const { object } = await cdp.send('DOM.resolveNode', { backendNodeId }, sessionId);
92
- if (!object.objectId)
93
- return undefined;
94
- const objectId = object.objectId;
95
- try {
96
- const { result } = await cdp.send('Runtime.callFunctionOn', { objectId, functionDeclaration: EDITOR_DETECT_FN, returnByValue: true }, sessionId);
97
- return result.value ?? undefined;
98
- }
99
- finally {
100
- await cdp.send('Runtime.releaseObject', { objectId }, sessionId);
101
- }
102
- }
103
- const INTERACTIVE_ROLES = new Set([
104
- 'button',
105
- 'link',
106
- 'textbox',
107
- 'checkbox',
108
- 'radio',
109
- 'combobox',
110
- 'listbox',
111
- 'option',
112
- 'menuitem',
113
- 'tab',
114
- 'slider',
115
- 'spinbutton',
116
- 'searchbox',
117
- 'switch',
118
- 'menuitemcheckbox',
119
- 'menuitemradio',
120
- 'treeitem',
121
- ]);
122
- export async function getRefs(cdp, sessionId, opts = {}) {
123
- const { interactive = true, limit = 500, compact = false } = opts;
124
- const { nodes } = (await cdp.send('Accessibility.getFullAXTree', {}, sessionId));
125
- const nodeMap = new Map();
126
- const lines = [];
127
- let refCounter = 1;
128
- for (const node of nodes) {
129
- if (refCounter > limit)
130
- break;
131
- const role = node.role?.value?.toLowerCase() || '';
132
- if (!role || role === 'none' || role === 'generic')
133
- continue;
134
- if (interactive && !INTERACTIVE_ROLES.has(role))
135
- continue;
136
- const name = node.name?.value || '';
137
- const attrs = [];
138
- for (const prop of node.properties || []) {
139
- const propName = prop.name;
140
- const propValue = prop.value?.value;
141
- if (propName === 'disabled' && propValue === true)
142
- attrs.push('disabled');
143
- if (propName === 'checked' && propValue === true)
144
- attrs.push('checked');
145
- if (propName === 'selected' && propValue === true)
146
- attrs.push('selected');
147
- if (propName === 'expanded' && propValue === true)
148
- attrs.push('expanded');
149
- if (propName === 'required' && propValue === true)
150
- attrs.push('required');
151
- if (propName === 'readonly' && propValue === true)
152
- attrs.push('readonly');
153
- if (propName === 'invalid' && propValue === true)
154
- attrs.push('invalid');
155
- }
156
- const ref = refCounter++;
157
- const refNode = {
158
- ref,
159
- role,
160
- name,
161
- attrs,
162
- backendNodeId: node.backendDOMNodeId,
163
- };
164
- if (role === 'textbox' && node.backendDOMNodeId) {
165
- const editor = await detectEditorForNode(cdp, sessionId, node.backendDOMNodeId);
166
- if (editor)
167
- refNode.editor = editor;
168
- }
169
- nodeMap.set(ref, refNode);
170
- const attrStr = attrs.length > 0 ? ` [${attrs.join('] [')}]` : '';
171
- const editorStr = refNode.editor ? ` [editor=${refNode.editor}]` : '';
172
- const nameStr = name ? ` "${truncate(name, 50)}"` : '';
173
- const line = compact
174
- ? `${role}${nameStr} [ref=${ref}]${attrStr}${editorStr}`
175
- : `- ${role}${nameStr} [ref=${ref}]${attrStr}${editorStr}`;
176
- lines.push(line);
177
- }
178
- return { refs: lines.join('\n'), nodeMap, opts: { interactive, limit } };
179
- }
180
- export async function resolveRefToCoords(cdp, sessionId, nodeMap, ref) {
181
- const node = nodeMap.get(ref);
182
- if (!node)
183
- throw new Error(`Ref ${ref} not found`);
184
- if (!node.backendNodeId)
185
- throw new Error(`Ref ${ref} has no DOM node`);
186
- const { model } = (await cdp.send('DOM.getBoxModel', { backendNodeId: node.backendNodeId }, sessionId));
187
- const [x1, y1, x2, y2, x3, y3, x4, y4] = model.content;
188
- const centerX = (x1 + x2 + x3 + x4) / 4;
189
- const centerY = (y1 + y2 + y3 + y4) / 4;
190
- return { x: centerX, y: centerY };
191
- }
@@ -1,70 +0,0 @@
1
- import type { BrowserProfileConfig } from '../types.js';
2
- export interface ProfileDeclaration {
3
- device: string;
4
- config: BrowserProfileConfig;
5
- }
6
- /**
7
- * The leftover central `browser:` tombstone from before per-device declarations.
8
- * Empty when none remain — and `agents sync` now drains it automatically via
9
- * {@link autoEvictCentralBrowserProfiles}, so on a synced box this is empty and
10
- * {@link profileRegistry} is the single source of truth. It survives only to
11
- * fold in migration and to explain an as-yet-unclaimed profile in a resolve
12
- * error (a profile hostable on a peer that has not synced yet), never as a
13
- * parallel store callers read for resolution.
14
- */
15
- export declare function centralBrowserProfiles(): Record<string, BrowserProfileConfig>;
16
- interface CentralClaimResult {
17
- claimed: string[];
18
- skipped: string[];
19
- }
20
- /**
21
- * Fold leftover central `browser:` entries into THIS device's declaration file.
22
- *
23
- * The EXPLICIT operator action (`agents browser profiles claim`), run on the
24
- * machine that actually owns the browser. It is never called implicitly from a
25
- * read, and that is the point: every device can read the central map, so an
26
- * implicit claim races — whichever box reads first claims the name, {@link
27
- * profileKind} then reports it `identity`, and the daemon tunnels to that box,
28
- * which for a fleet-wide `cdp://localhost:*` profile is a logged-out headless
29
- * browser wearing a credentialed browser's name. {@link autoEvictCentralBrowserProfiles}
30
- * closes that race for the automatic path (host-gated + non-throwing on sync);
31
- * this one is the manual, throw-on-conflict counterpart.
32
- *
33
- * `canHostHere` is supplied by the command layer so this module stays a leaf —
34
- * it must not import `isProfileLaunchableHere` (that would cycle through
35
- * chrome.ts → profiles.ts). Only profiles this machine can host are claimed; the
36
- * rest stay central, undeclared, and fail loudly on resolve.
37
- */
38
- export declare function migrateCentralBrowserProfiles(canHostHere: (config: BrowserProfileConfig) => boolean, name?: string): CentralClaimResult;
39
- /**
40
- * Automatic, self-draining counterpart to {@link migrateCentralBrowserProfiles}:
41
- * fold every lingering central `browser:` profile THIS machine can host into its
42
- * device doc and clear it from central, so the tombstone drains itself on
43
- * `agents sync` with no manual `agents browser profiles claim`.
44
- *
45
- * Safe to call implicitly, unlike a claim at registry-read time (which races —
46
- * see the `profileRegistry does not claim central declarations` guard), but the
47
- * CALLER owns one ordering invariant: invoke it AFTER the sync's repo pull. Two
48
- * boxes can both host the same profile (`canHostHere` is launchability, not
49
- * ownership), so a box acting on a PRE-pull view of central could re-claim a
50
- * profile a peer already drained and pushed — both writes land in different
51
- * device files with no git conflict, and the profile flips identity->fungible.
52
- * Running post-pull means central already reflects peers' drains, so the tombstone
53
- * is gone locally before this box looks. The selection is also computed INSIDE
54
- * the meta lock, and the path never throws — an unhostable profile or one that
55
- * conflicts with an existing local declaration is left central for an explicit
56
- * claim rather than wedging the sync. After it runs, {@link profileRegistry} is
57
- * the single source of truth: a claimed profile lives in the device doc alone,
58
- * never double-counted across the two stores.
59
- */
60
- export declare function autoEvictCentralBrowserProfiles(canHostHere: (config: BrowserProfileConfig) => boolean): CentralClaimResult;
61
- /**
62
- * Every profile any device declares, keyed by name to all declaring devices.
63
- * Reads every `devices/<name>/agents.yaml`; declarations never overwrite.
64
- */
65
- export declare function profileRegistry(): Map<string, ProfileDeclaration[]>;
66
- /** Devices declaring `name`, empty when nobody does. */
67
- export declare function declaringDevices(name: string): string[];
68
- /** Exactly one declaring device is identity-bearing; several are fungible. */
69
- export declare function profileKind(name: string): 'identity' | 'fungible' | null;
70
- export {};
@@ -1,252 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { isDeepStrictEqual } from 'node:util';
4
- import * as yaml from 'yaml';
5
- import { commitCentralConfigAfterWrite, getUserAgentsDir, readMeta, updateMeta, withMetaLock, writeMetaUnlocked } from '../state.js';
6
- function isRecord(value) {
7
- return !!value && typeof value === 'object' && !Array.isArray(value);
8
- }
9
- /**
10
- * The leftover central `browser:` tombstone from before per-device declarations.
11
- * Empty when none remain — and `agents sync` now drains it automatically via
12
- * {@link autoEvictCentralBrowserProfiles}, so on a synced box this is empty and
13
- * {@link profileRegistry} is the single source of truth. It survives only to
14
- * fold in migration and to explain an as-yet-unclaimed profile in a resolve
15
- * error (a profile hostable on a peer that has not synced yet), never as a
16
- * parallel store callers read for resolution.
17
- */
18
- export function centralBrowserProfiles() {
19
- const central = readMeta().browser;
20
- if (!central || Object.keys(central).length === 0)
21
- return {};
22
- return { ...central };
23
- }
24
- /**
25
- * Pick the central `browser:` entries this machine may fold into its device doc.
26
- *
27
- * A profile is claimable only when `canHostHere` accepts it — the endpoint or
28
- * binary resolves to THIS machine — so a `cdp://localhost:*` profile owned by a
29
- * peer is never claimed here (the logged-out-headless-browser bug this module
30
- * exists to prevent). A name already declared locally with an IDENTICAL config
31
- * is folded too, which just drops the redundant central copy. A name declared
32
- * locally with a DIFFERENT config is a genuine conflict: `onConflict: 'throw'`
33
- * (the explicit `claim` command) surfaces it; `onConflict: 'skip'` (automatic
34
- * eviction on sync) leaves it central so a stray duplicate can never wedge a
35
- * sync — an operator resolves it with an explicit claim.
36
- */
37
- function selectClaimableCentral(central, local, canHostHere, opts) {
38
- const toClaim = {};
39
- const skipped = [];
40
- for (const [profileName, config] of Object.entries(central)) {
41
- if (opts.name && profileName !== opts.name)
42
- continue;
43
- if (!canHostHere(config)) {
44
- skipped.push(profileName);
45
- continue;
46
- }
47
- const existing = local[profileName];
48
- if (existing && !isDeepStrictEqual(existing, config)) {
49
- if (opts.onConflict === 'throw') {
50
- throw new Error(`Cannot migrate browser profile "${profileName}": central agents.yaml and this device's ` +
51
- `agents.yaml declare different configurations. Resolve the duplicate before retrying.`);
52
- }
53
- skipped.push(profileName);
54
- continue;
55
- }
56
- toClaim[profileName] = config;
57
- }
58
- return { toClaim, skipped };
59
- }
60
- /**
61
- * Return `current` with `toClaim` moved out of the central `browser:` map and
62
- * into this device's `deviceBrowser` doc. The central key is dropped entirely
63
- * once drained, so the tombstone disappears rather than lingering as an empty
64
- * map. Pure — the caller applies it under the meta lock.
65
- */
66
- function buildEvictedMeta(current, toClaim) {
67
- const legacy = current;
68
- const remaining = {};
69
- for (const [profileName, config] of Object.entries(legacy.browser ?? {})) {
70
- if (!(profileName in toClaim))
71
- remaining[profileName] = config;
72
- }
73
- const { browser: _removed, ...withoutCentralBrowser } = legacy;
74
- return {
75
- ...withoutCentralBrowser,
76
- ...(Object.keys(remaining).length > 0 ? { browser: remaining } : {}),
77
- deviceBrowser: { ...current.deviceBrowser, ...toClaim },
78
- };
79
- }
80
- /**
81
- * Commit a claim computed by the EXPLICIT path (`migrateCentralBrowserProfiles`).
82
- * `toClaim` was selected from an unlocked snapshot — acceptable for that manual,
83
- * deliberate command; the automatic path instead selects INSIDE the lock (see
84
- * {@link autoEvictCentralBrowserProfiles}) so an unrelated concurrent write can
85
- * never be clobbered by a stale selection.
86
- */
87
- function commitCentralClaim(toClaim) {
88
- updateMeta((current) => buildEvictedMeta(current, toClaim));
89
- }
90
- /**
91
- * Fold leftover central `browser:` entries into THIS device's declaration file.
92
- *
93
- * The EXPLICIT operator action (`agents browser profiles claim`), run on the
94
- * machine that actually owns the browser. It is never called implicitly from a
95
- * read, and that is the point: every device can read the central map, so an
96
- * implicit claim races — whichever box reads first claims the name, {@link
97
- * profileKind} then reports it `identity`, and the daemon tunnels to that box,
98
- * which for a fleet-wide `cdp://localhost:*` profile is a logged-out headless
99
- * browser wearing a credentialed browser's name. {@link autoEvictCentralBrowserProfiles}
100
- * closes that race for the automatic path (host-gated + non-throwing on sync);
101
- * this one is the manual, throw-on-conflict counterpart.
102
- *
103
- * `canHostHere` is supplied by the command layer so this module stays a leaf —
104
- * it must not import `isProfileLaunchableHere` (that would cycle through
105
- * chrome.ts → profiles.ts). Only profiles this machine can host are claimed; the
106
- * rest stay central, undeclared, and fail loudly on resolve.
107
- */
108
- export function migrateCentralBrowserProfiles(canHostHere, name) {
109
- const meta = readMeta();
110
- const central = meta.browser;
111
- if (!central || Object.keys(central).length === 0) {
112
- if (name) {
113
- throw new Error(`No leftover central browser profile named "${name}".`);
114
- }
115
- return { claimed: [], skipped: [] };
116
- }
117
- const local = meta.deviceBrowser ?? {};
118
- if (name) {
119
- const config = central[name];
120
- if (!config) {
121
- throw new Error(local[name]
122
- ? `Browser profile "${name}" is already declared on this device.`
123
- : `No leftover central browser profile named "${name}".`);
124
- }
125
- if (!canHostHere(config)) {
126
- throw new Error(`Cannot claim browser profile "${name}": this machine cannot host it ` +
127
- `(its browser/binary isn't installed here). Run this command on the machine that has that browser.`);
128
- }
129
- }
130
- const { toClaim, skipped } = selectClaimableCentral(central, local, canHostHere, {
131
- name,
132
- onConflict: 'throw',
133
- });
134
- const claimed = Object.keys(toClaim).sort();
135
- skipped.sort();
136
- if (claimed.length === 0)
137
- return { claimed, skipped };
138
- commitCentralClaim(toClaim);
139
- return { claimed, skipped };
140
- }
141
- /**
142
- * Automatic, self-draining counterpart to {@link migrateCentralBrowserProfiles}:
143
- * fold every lingering central `browser:` profile THIS machine can host into its
144
- * device doc and clear it from central, so the tombstone drains itself on
145
- * `agents sync` with no manual `agents browser profiles claim`.
146
- *
147
- * Safe to call implicitly, unlike a claim at registry-read time (which races —
148
- * see the `profileRegistry does not claim central declarations` guard), but the
149
- * CALLER owns one ordering invariant: invoke it AFTER the sync's repo pull. Two
150
- * boxes can both host the same profile (`canHostHere` is launchability, not
151
- * ownership), so a box acting on a PRE-pull view of central could re-claim a
152
- * profile a peer already drained and pushed — both writes land in different
153
- * device files with no git conflict, and the profile flips identity->fungible.
154
- * Running post-pull means central already reflects peers' drains, so the tombstone
155
- * is gone locally before this box looks. The selection is also computed INSIDE
156
- * the meta lock, and the path never throws — an unhostable profile or one that
157
- * conflicts with an existing local declaration is left central for an explicit
158
- * claim rather than wedging the sync. After it runs, {@link profileRegistry} is
159
- * the single source of truth: a claimed profile lives in the device doc alone,
160
- * never double-counted across the two stores.
161
- */
162
- export function autoEvictCentralBrowserProfiles(canHostHere) {
163
- // Read fresh, select, and commit inside ONE meta-lock acquisition. Reading
164
- // central before the lock and committing after (the manual path's shape) leaves
165
- // a window where a concurrent write on THIS machine changes central/deviceBrowser
166
- // between snapshot and commit, so the stale selection would be merged over the
167
- // fresher value. Selecting from the state the lock just handed us closes that
168
- // window. We write ONLY when something is claimed, so a no-op sync never touches
169
- // any doc (writeMetaUnlocked would otherwise re-serialize the device doc every
170
- // run). This mirrors updateMeta's own body, minus the unconditional write.
171
- let centralChanged = false;
172
- const result = withMetaLock(() => {
173
- const meta = readMeta();
174
- const central = meta.browser;
175
- if (!central || Object.keys(central).length === 0)
176
- return { claimed: [], skipped: [] };
177
- const local = meta.deviceBrowser ?? {};
178
- const { toClaim, skipped } = selectClaimableCentral(central, local, canHostHere, {
179
- onConflict: 'skip',
180
- });
181
- const claimed = Object.keys(toClaim).sort();
182
- skipped.sort();
183
- if (claimed.length === 0)
184
- return { claimed, skipped };
185
- centralChanged = writeMetaUnlocked(buildEvictedMeta(meta, toClaim));
186
- return { claimed, skipped };
187
- });
188
- // Commit the central eviction AFTER the meta lock releases (the invariant:
189
- // every non-daemon central write commits after the lock, so agents.yaml is
190
- // never left dirty at rest to re-open the pull-trip window). Gated inside the
191
- // helper on centralChanged && !isDaemonProcess().
192
- commitCentralConfigAfterWrite(centralChanged);
193
- return result;
194
- }
195
- /**
196
- * Every profile any device declares, keyed by name to all declaring devices.
197
- * Reads every `devices/<name>/agents.yaml`; declarations never overwrite.
198
- */
199
- export function profileRegistry() {
200
- const registry = new Map();
201
- const devicesDir = path.join(getUserAgentsDir(), 'devices');
202
- if (!fs.existsSync(devicesDir))
203
- return registry;
204
- const devices = fs
205
- .readdirSync(devicesDir, { withFileTypes: true })
206
- .filter((entry) => entry.isDirectory())
207
- .map((entry) => entry.name)
208
- .sort();
209
- for (const device of devices) {
210
- const file = path.join(devicesDir, device, 'agents.yaml');
211
- if (!fs.existsSync(file))
212
- continue;
213
- let parsed;
214
- try {
215
- parsed = yaml.parse(fs.readFileSync(file, 'utf8'));
216
- }
217
- catch (error) {
218
- throw new Error(`Cannot read browser declarations from ${file}: ${error instanceof Error ? error.message : String(error)}`);
219
- }
220
- if (parsed == null)
221
- continue;
222
- if (!isRecord(parsed)) {
223
- throw new Error(`Cannot read browser declarations from ${file}: document root must be a map.`);
224
- }
225
- const browser = parsed.browser;
226
- if (browser == null)
227
- continue;
228
- if (!isRecord(browser)) {
229
- throw new Error(`Cannot read browser declarations from ${file}: browser must be a map.`);
230
- }
231
- for (const [name, config] of Object.entries(browser)) {
232
- if (!isRecord(config)) {
233
- throw new Error(`Cannot read browser declaration "${name}" from ${file}: profile must be a map.`);
234
- }
235
- const declarations = registry.get(name) ?? [];
236
- declarations.push({ device, config: config });
237
- registry.set(name, declarations);
238
- }
239
- }
240
- return registry;
241
- }
242
- /** Devices declaring `name`, empty when nobody does. */
243
- export function declaringDevices(name) {
244
- return (profileRegistry().get(name) ?? []).map((declaration) => declaration.device);
245
- }
246
- /** Exactly one declaring device is identity-bearing; several are fungible. */
247
- export function profileKind(name) {
248
- const count = profileRegistry().get(name)?.length ?? 0;
249
- if (count === 0)
250
- return null;
251
- return count === 1 ? 'identity' : 'fungible';
252
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Consent gate for driving THIS machine's browser from another fleet machine.
3
- *
4
- * `browser --device <device>` routes a browser command to `<device>` over SSH (the
5
- * fleet passthrough) and runs `agents browser ...` there. `agents ssh <device>
6
- * agents browser …` is the same remote drive through the ssh wrapper. Every such
7
- * remote invocation carries the {@link FLEET_REMOTE_ENV} marker, set at the fleet
8
- * dispatch site (`maybeRunOnHost` / `streamAgentsOnHost`) and on the ssh wrapper
9
- * (`buildSshInvocation` via `markFleetRemote`). A machine only accepts being driven
10
- * when its owner has opted in via `agents browser remote-control on` (the
11
- * device-scope `browser.remote-control` config key, never synced).
12
- *
13
- * Local invocations (no marker) are never gated — this only governs cross-machine
14
- * drives. Read-only queries are not gated.
15
- *
16
- * The authoritative gate is {@link assertRemoteControlAllowedForRequest}, called
17
- * inside the browser daemon at the top of `resolveOrCreateTask` — the one
18
- * chokepoint every task-scoped verb resolves through — plus `BrowserService.start`
19
- * for the task-less `browser start` command and `BrowserService.stopProfile` for
20
- * the task-less `browser stop --profile` command. It has to live there because ~18
21
- * page verbs (`navigate`, `click`, `screenshot`, `tab-add`, …) launch OR attach
22
- * to a browser implicitly, and gating only the `browser start` command left every
23
- * one of them ungated; gating only the create branch of `resolveOrCreateTask` left
24
- * the attach paths ungated (RUSH-3064); `stop --profile` is handled by `bindTask`'s
25
- * early return before `resolveOrCreateTask` ever runs, so it needed its own gate
26
- * call too (PHNX-3317). {@link assertRemoteControlAllowed} remains as a fast-fail
27
- * CLI-side check so a refused `start` never auto-creates a profile.
28
- */
29
- /** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation` and `markFleetRemote`. */
30
- export declare const FLEET_REMOTE_ENV = "AGENTS_FLEET_REMOTE";
31
- /** True when this process was dispatched to this machine by a fleet `--device` or `agents ssh … agents browser` run. */
32
- export declare function isFleetRemoteInvocation(env?: NodeJS.ProcessEnv): boolean;
33
- /**
34
- * Guard the browser-drive entry point. A fleet-remote invocation may only drive
35
- * this machine's browser when the owner opted in; otherwise throw a clear,
36
- * actionable error. A local invocation is never gated.
37
- *
38
- * `env` and `enabled` are injectable so the decision is unit-testable without a
39
- * real remote hop or a real config store.
40
- */
41
- export declare function assertRemoteControlAllowed(opts?: {
42
- env?: NodeJS.ProcessEnv;
43
- enabled?: boolean;
44
- }): void;
45
- /**
46
- * The daemon-side consent gate.
47
- *
48
- * Reads ONLY the per-request marker, never `process.env`: the browser daemon is
49
- * shared and long-lived, and it may have been auto-started by a fleet-remote CLI
50
- * that leaked `AGENTS_FLEET_REMOTE=1` into its environment permanently. Reading
51
- * the daemon's env would then refuse every subsequent LOCAL drive on this
52
- * machine until the daemon restarted.
53
- *
54
- * `actor` is likewise the caller's forwarded identity, not the daemon's.
55
- */
56
- export declare function assertRemoteControlAllowedForRequest(fleetRemote: boolean | undefined, opts?: {
57
- actor?: string;
58
- enabled?: boolean;
59
- }): void;