@phnx-labs/agents-cli 1.22.116 → 1.22.118

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 (254) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/browser.d.ts +52 -70
  4. package/dist/commands/browser.js +192 -3562
  5. package/dist/commands/daemon.d.ts +39 -2
  6. package/dist/commands/daemon.js +72 -73
  7. package/dist/commands/events.js +1 -1
  8. package/dist/commands/exec.d.ts +18 -0
  9. package/dist/commands/exec.js +104 -12
  10. package/dist/commands/repo.js +13 -49
  11. package/dist/commands/route.js +5 -5
  12. package/dist/commands/routines.js +39 -12
  13. package/dist/commands/sessions-render.d.ts +1 -1
  14. package/dist/commands/sessions-render.js +0 -1
  15. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  16. package/dist/commands/sessions.test-fixture.js +0 -37
  17. package/dist/commands/setup-browser.d.ts +17 -12
  18. package/dist/commands/setup-browser.js +52 -87
  19. package/dist/commands/setup-preferences.d.ts +0 -17
  20. package/dist/commands/setup-preferences.js +18 -49
  21. package/dist/commands/setup-term.d.ts +4 -3
  22. package/dist/commands/setup-term.js +5 -4
  23. package/dist/commands/setup.js +11 -11
  24. package/dist/commands/ssh.js +10 -1
  25. package/dist/commands/sync.js +6 -44
  26. package/dist/commands/teams.js +0 -1
  27. package/dist/commands/update.d.ts +19 -0
  28. package/dist/commands/update.js +34 -6
  29. package/dist/commands/view.d.ts +12 -1
  30. package/dist/commands/view.js +47 -2
  31. package/dist/index.js +107 -26
  32. package/dist/lib/account-capabilities.js +0 -2
  33. package/dist/lib/account-catalog.d.ts +38 -0
  34. package/dist/lib/account-catalog.js +110 -26
  35. package/dist/lib/account-provider-registry.js +1 -1
  36. package/dist/lib/account-registry.d.ts +1 -1
  37. package/dist/lib/account-registry.js +36 -2
  38. package/dist/lib/accounting/rotate.js +22 -8
  39. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  40. package/dist/lib/accounting/usage-ingest.js +115 -45
  41. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  42. package/dist/lib/accounting/usage-sync.js +227 -66
  43. package/dist/lib/accounting/usage.d.ts +16 -3
  44. package/dist/lib/accounting/usage.js +26 -8
  45. package/dist/lib/accounts/slots.d.ts +8 -0
  46. package/dist/lib/accounts/slots.js +17 -0
  47. package/dist/lib/add-dir.js +0 -1
  48. package/dist/lib/agent-cli-commands.js +0 -1
  49. package/dist/lib/agent-spec/agents.d.ts +6 -7
  50. package/dist/lib/agent-spec/agents.js +6 -60
  51. package/dist/lib/auth-health.d.ts +69 -1
  52. package/dist/lib/auth-health.js +133 -4
  53. package/dist/lib/browser/context.d.ts +82 -0
  54. package/dist/lib/browser/context.js +88 -0
  55. package/dist/lib/browser/paths.d.ts +25 -0
  56. package/dist/lib/browser/paths.js +51 -0
  57. package/dist/lib/browser/record.d.ts +38 -0
  58. package/dist/lib/browser/record.js +57 -0
  59. package/dist/lib/browser/sessions-list.js +1 -2
  60. package/dist/lib/browser-client.d.ts +158 -0
  61. package/dist/lib/browser-client.js +234 -0
  62. package/dist/lib/channels/owner-sink.d.ts +1 -18
  63. package/dist/lib/channels/owner-sink.js +29 -82
  64. package/dist/lib/channels/providers/rush.d.ts +7 -7
  65. package/dist/lib/channels/providers/rush.js +127 -57
  66. package/dist/lib/claude-statusline.d.ts +1 -1
  67. package/dist/lib/claude-statusline.js +15 -3
  68. package/dist/lib/commands.js +1 -1
  69. package/dist/lib/crabbox/lease.js +1 -1
  70. package/dist/lib/crabbox/runtimes.js +1 -3
  71. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  72. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  73. package/dist/lib/daemon/auth-sync-service.js +37 -43
  74. package/dist/lib/daemon/daemon.d.ts +25 -14
  75. package/dist/lib/daemon/daemon.js +92 -154
  76. package/dist/lib/daemon/runner.d.ts +2 -2
  77. package/dist/lib/daemon/runner.js +32 -9
  78. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  79. package/dist/lib/daemon/self-update-service.js +40 -36
  80. package/dist/lib/daemon/service.d.ts +12 -8
  81. package/dist/lib/daemon/supervisor.d.ts +65 -54
  82. package/dist/lib/daemon/supervisor.js +136 -171
  83. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  84. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  85. package/dist/lib/daemon/usage-sync-service.js +46 -78
  86. package/dist/lib/daemon-health.d.ts +22 -5
  87. package/dist/lib/daemon-health.js +47 -3
  88. package/dist/lib/daemon-services.d.ts +1 -1
  89. package/dist/lib/daemon-services.js +0 -10
  90. package/dist/lib/daemon-ticks.js +4 -2
  91. package/dist/lib/devices/doctor-findings.js +8 -11
  92. package/dist/lib/devices/harness-inventory.js +14 -5
  93. package/dist/lib/exec-account-home.js +5 -0
  94. package/dist/lib/exec.js +0 -11
  95. package/dist/lib/feed/events.js +0 -1
  96. package/dist/lib/feed/tool-activity.js +5 -2
  97. package/dist/lib/fleet-shared-state.d.ts +45 -0
  98. package/dist/lib/fleet-shared-state.js +78 -9
  99. package/dist/lib/fs-atomic.js +25 -0
  100. package/dist/lib/harness-auth-capabilities.js +0 -1
  101. package/dist/lib/helper-versions.js +1 -1
  102. package/dist/lib/hooks/cache.d.ts +7 -0
  103. package/dist/lib/hooks/cache.js +27 -2
  104. package/dist/lib/hooks/install.js +7 -1
  105. package/dist/lib/hosts/ready.d.ts +38 -8
  106. package/dist/lib/hosts/ready.js +62 -14
  107. package/dist/lib/hosts/remote-cmd.js +1 -0
  108. package/dist/lib/installations/active-check.d.ts +27 -0
  109. package/dist/lib/installations/active-check.js +61 -0
  110. package/dist/lib/installations/migrate.js +6 -3
  111. package/dist/lib/installations/shims.js +9 -7
  112. package/dist/lib/installations/update.js +21 -15
  113. package/dist/lib/installations/versions.js +12 -1
  114. package/dist/lib/manifest.js +1 -1
  115. package/dist/lib/models.js +0 -2
  116. package/dist/lib/open-url.js +28 -54
  117. package/dist/lib/placement.d.ts +2 -0
  118. package/dist/lib/placement.js +5 -0
  119. package/dist/lib/profiles.js +0 -2
  120. package/dist/lib/refresh.js +1 -1
  121. package/dist/lib/routine-readiness.d.ts +35 -0
  122. package/dist/lib/routine-readiness.js +74 -16
  123. package/dist/lib/rules/compile.js +6 -0
  124. package/dist/lib/sandbox.js +0 -2
  125. package/dist/lib/secrets-policy.d.ts +89 -11
  126. package/dist/lib/secrets-policy.js +175 -28
  127. package/dist/lib/session/db.d.ts +1 -1
  128. package/dist/lib/session/db.js +65 -18
  129. package/dist/lib/session/discover.js +1 -283
  130. package/dist/lib/session/mirror.d.ts +4 -3
  131. package/dist/lib/session/mirror.js +4 -3
  132. package/dist/lib/session/parse.d.ts +0 -2
  133. package/dist/lib/session/parse.js +1 -138
  134. package/dist/lib/session/throughput.d.ts +1 -3
  135. package/dist/lib/session/throughput.js +0 -20
  136. package/dist/lib/session/types.d.ts +2 -2
  137. package/dist/lib/session/types.js +2 -2
  138. package/dist/lib/sessions-client.d.ts +60 -1
  139. package/dist/lib/sessions-client.js +227 -4
  140. package/dist/lib/setup-tool-status.js +8 -12
  141. package/dist/lib/signin-badge.d.ts +3 -2
  142. package/dist/lib/signin-badge.js +6 -3
  143. package/dist/lib/smart-launch.js +4 -1
  144. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  147. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  148. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  149. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  150. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  151. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  152. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  153. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  154. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  155. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  156. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  157. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  158. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  159. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  160. package/dist/lib/staleness/writers/sources.js +5 -0
  161. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  162. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  163. package/dist/lib/state.d.ts +6 -4
  164. package/dist/lib/state.js +7 -17
  165. package/dist/lib/teams/parsers.d.ts +1 -1
  166. package/dist/lib/teams/parsers.js +1 -136
  167. package/dist/lib/teams/placement-probe.js +50 -15
  168. package/dist/lib/teams/scheduler.d.ts +7 -0
  169. package/dist/lib/types.d.ts +10 -1
  170. package/dist/lib/types.js +1 -1
  171. package/dist/lib/view-types.d.ts +32 -0
  172. package/dist/lib/watchdog/read.js +0 -1
  173. package/dist/lib/watchdog/rotate.js +1 -2
  174. package/dist/lib/watchdog/runner.js +2 -2
  175. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  176. package/dist/lib/watchdog/watchdogTail.js +0 -31
  177. package/package.json +1 -1
  178. package/scripts/postinstall.js +8 -8
  179. package/dist/browser.d.ts +0 -2
  180. package/dist/browser.js +0 -17
  181. package/dist/commands/browser-picker.d.ts +0 -18
  182. package/dist/commands/browser-picker.js +0 -97
  183. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  184. package/dist/lib/browser/arc-discovery.js +0 -197
  185. package/dist/lib/browser/arc-dom.d.ts +0 -14
  186. package/dist/lib/browser/arc-dom.js +0 -121
  187. package/dist/lib/browser/caller-identity.d.ts +0 -40
  188. package/dist/lib/browser/caller-identity.js +0 -175
  189. package/dist/lib/browser/cdp.d.ts +0 -51
  190. package/dist/lib/browser/cdp.js +0 -254
  191. package/dist/lib/browser/chrome.d.ts +0 -141
  192. package/dist/lib/browser/chrome.js +0 -704
  193. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  194. package/dist/lib/browser/chromium-discovery.js +0 -96
  195. package/dist/lib/browser/devices.d.ts +0 -34
  196. package/dist/lib/browser/devices.js +0 -61
  197. package/dist/lib/browser/domain-skills.d.ts +0 -71
  198. package/dist/lib/browser/domain-skills.js +0 -195
  199. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  200. package/dist/lib/browser/drivers/arc.js +0 -276
  201. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  202. package/dist/lib/browser/drivers/firefox.js +0 -377
  203. package/dist/lib/browser/drivers/local.d.ts +0 -55
  204. package/dist/lib/browser/drivers/local.js +0 -266
  205. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  206. package/dist/lib/browser/drivers/ssh.js +0 -467
  207. package/dist/lib/browser/editor.d.ts +0 -3
  208. package/dist/lib/browser/editor.js +0 -50
  209. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  210. package/dist/lib/browser/ffmpeg.js +0 -184
  211. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  212. package/dist/lib/browser/firefox-discovery.js +0 -162
  213. package/dist/lib/browser/har.d.ts +0 -85
  214. package/dist/lib/browser/har.js +0 -77
  215. package/dist/lib/browser/hygiene.d.ts +0 -97
  216. package/dist/lib/browser/hygiene.js +0 -153
  217. package/dist/lib/browser/index.d.ts +0 -5
  218. package/dist/lib/browser/index.js +0 -5
  219. package/dist/lib/browser/input.d.ts +0 -7
  220. package/dist/lib/browser/input.js +0 -92
  221. package/dist/lib/browser/ipc.d.ts +0 -204
  222. package/dist/lib/browser/ipc.js +0 -1453
  223. package/dist/lib/browser/login-detection.d.ts +0 -87
  224. package/dist/lib/browser/login-detection.js +0 -274
  225. package/dist/lib/browser/profiles.d.ts +0 -422
  226. package/dist/lib/browser/profiles.js +0 -1157
  227. package/dist/lib/browser/refs.d.ts +0 -89
  228. package/dist/lib/browser/refs.js +0 -191
  229. package/dist/lib/browser/registry.d.ts +0 -70
  230. package/dist/lib/browser/registry.js +0 -252
  231. package/dist/lib/browser/remote-control.d.ts +0 -59
  232. package/dist/lib/browser/remote-control.js +0 -85
  233. package/dist/lib/browser/resolve-target.d.ts +0 -66
  234. package/dist/lib/browser/resolve-target.js +0 -264
  235. package/dist/lib/browser/runtime-state.d.ts +0 -291
  236. package/dist/lib/browser/runtime-state.js +0 -584
  237. package/dist/lib/browser/secret-ref.d.ts +0 -10
  238. package/dist/lib/browser/secret-ref.js +0 -14
  239. package/dist/lib/browser/service.d.ts +0 -812
  240. package/dist/lib/browser/service.js +0 -4693
  241. package/dist/lib/browser/stream.d.ts +0 -17
  242. package/dist/lib/browser/stream.js +0 -72
  243. package/dist/lib/browser/task-index.d.ts +0 -78
  244. package/dist/lib/browser/task-index.js +0 -199
  245. package/dist/lib/browser/types.d.ts +0 -647
  246. package/dist/lib/browser/types.js +0 -61
  247. package/dist/lib/browser/upload.d.ts +0 -29
  248. package/dist/lib/browser/upload.js +0 -298
  249. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  250. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  251. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  252. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  253. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  254. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -1,87 +0,0 @@
1
- /**
2
- * Best-effort login-state detection for browser profiles.
3
- *
4
- * Reads a profile's Chromium cookie store (presence only — never decrypts the
5
- * Keychain-encrypted values) to tell which login-gated services have a live
6
- * session in that profile. Powers the `agents browser start` guardrail (warn
7
- * when an agent opens a logged-out profile for a login-gated URL) and the
8
- * `agents browser profiles logins` view.
9
- *
10
- * Everything here is advisory: any failure (missing DB, locked file, unknown
11
- * schema) degrades to "no known session" and NEVER throws to callers — browser
12
- * start must not slow down or break because cookie inspection hiccuped.
13
- */
14
- /** A login-gated service signature: host substrings + the auth-cookie names
15
- * whose presence indicates a live session, plus optional login metadata. */
16
- interface ServiceSignature {
17
- hosts: string[];
18
- cookies: string[];
19
- /**
20
- * Where an interactive login for this service starts, and the bundle key
21
- * PREFIX its credentials live under. By convention a profile's `--secrets`
22
- * bundle holds `<PREFIX>_USERNAME` / `<PREFIX>_PASSWORD` (see
23
- * `credKeysForService`). The prefix is explicit — never string-munged from the
24
- * service id — so e.g. `x` → `X`, `google` → `GOOGLE`.
25
- */
26
- login?: {
27
- loginUrl: string;
28
- keyPrefix: string;
29
- };
30
- }
31
- /**
32
- * Known login-gated services. Presence of ANY listed cookie on a matching host
33
- * (unexpired, or a session cookie) = logged in. Deliberately conservative: a
34
- * service absent from this map simply never triggers a warning — we do not
35
- * guess. Each cookie name here is the definitive authenticated-session token for
36
- * that service (e.g. LinkedIn's `li_at` is only set for a signed-in member;
37
- * visitor cookies like `bcookie`/`JSESSIONID` are NOT auth). Services whose
38
- * session is not reliably expressed as a recognizable cookie (e.g. Attio, whose
39
- * only same-site cookies are third-party analytics) are deliberately omitted so
40
- * we never emit a false "logged out" warning. Extend as needed.
41
- */
42
- export declare const AUTH_SIGNATURES: Record<string, ServiceSignature>;
43
- /**
44
- * The bundle keys a profile's `--secrets` bundle should hold to log into
45
- * `service`: `<PREFIX>_USERNAME` / `<PREFIX>_PASSWORD`. Null for services with no
46
- * login metadata. Feed a resolved value to the page with
47
- * `agents browser type <ref> --secret <bundle>/<KEY>` (never printed).
48
- */
49
- export declare function credKeysForService(service: string): {
50
- user: string;
51
- pass: string;
52
- } | null;
53
- /** The interactive login URL for a service, or null. */
54
- export declare function loginUrlForService(service: string): string | null;
55
- interface CookieRow {
56
- host_key: string;
57
- name: string;
58
- }
59
- /**
60
- * Pure core: given a profile's (already expiry-filtered) cookie rows, return the
61
- * services with a live session. Exported for direct unit testing without disk.
62
- */
63
- export declare function detectServices(rows: CookieRow[]): string[];
64
- /** Map a URL to a known login-gated service key, or null. */
65
- export declare function serviceForUrl(url: string): string | null;
66
- /**
67
- * Map a profile's saved logins to service → account username (identity, not
68
- * session). Best-effort; `{}` if `Login Data` is unreadable. The first non-empty
69
- * username per service wins.
70
- */
71
- export declare function accountsForProfile(profileName: string): Promise<Record<string, string>>;
72
- /**
73
- * For each service with a LIVE session in the profile (cookie-gated), the
74
- * account it is signed in as (from saved logins). Intersecting with live
75
- * sessions means a stale saved-login for a service you've since logged out of
76
- * never shows.
77
- */
78
- export declare function loginsWithAccountsForProfile(profileName: string): Promise<Array<{
79
- service: string;
80
- username?: string;
81
- }>>;
82
- /** Services with a live session in the given profile (best-effort; [] if none
83
- * detected or the cookie store is unreadable). */
84
- export declare function loginsForProfile(profileName: string): Promise<string[]>;
85
- /** Profile names that have a live session for the given service. */
86
- export declare function profilesLoggedInto(service: string): Promise<string[]>;
87
- export {};
@@ -1,274 +0,0 @@
1
- /**
2
- * Best-effort login-state detection for browser profiles.
3
- *
4
- * Reads a profile's Chromium cookie store (presence only — never decrypts the
5
- * Keychain-encrypted values) to tell which login-gated services have a live
6
- * session in that profile. Powers the `agents browser start` guardrail (warn
7
- * when an agent opens a logged-out profile for a login-gated URL) and the
8
- * `agents browser profiles logins` view.
9
- *
10
- * Everything here is advisory: any failure (missing DB, locked file, unknown
11
- * schema) degrades to "no known session" and NEVER throws to callers — browser
12
- * start must not slow down or break because cookie inspection hiccuped.
13
- */
14
- import * as fs from 'fs';
15
- import * as os from 'os';
16
- import * as path from 'path';
17
- import Database from '../sqlite.js';
18
- import { getBrowserRuntimeDir, listProfiles } from './profiles.js';
19
- /**
20
- * Known login-gated services. Presence of ANY listed cookie on a matching host
21
- * (unexpired, or a session cookie) = logged in. Deliberately conservative: a
22
- * service absent from this map simply never triggers a warning — we do not
23
- * guess. Each cookie name here is the definitive authenticated-session token for
24
- * that service (e.g. LinkedIn's `li_at` is only set for a signed-in member;
25
- * visitor cookies like `bcookie`/`JSESSIONID` are NOT auth). Services whose
26
- * session is not reliably expressed as a recognizable cookie (e.g. Attio, whose
27
- * only same-site cookies are third-party analytics) are deliberately omitted so
28
- * we never emit a false "logged out" warning. Extend as needed.
29
- */
30
- export const AUTH_SIGNATURES = {
31
- linkedin: {
32
- hosts: ['linkedin.com'],
33
- cookies: ['li_at'],
34
- login: { loginUrl: 'https://www.linkedin.com/login', keyPrefix: 'LINKEDIN' },
35
- },
36
- google: {
37
- hosts: ['google.com'],
38
- cookies: ['SID', 'SAPISID', '__Secure-1PSID', '__Secure-3PSID'],
39
- login: { loginUrl: 'https://accounts.google.com/', keyPrefix: 'GOOGLE' },
40
- },
41
- x: {
42
- hosts: ['x.com', 'twitter.com'],
43
- cookies: ['auth_token'],
44
- login: { loginUrl: 'https://x.com/login', keyPrefix: 'X' },
45
- },
46
- reddit: {
47
- hosts: ['reddit.com'],
48
- cookies: ['reddit_session', 'token_v2'],
49
- login: { loginUrl: 'https://www.reddit.com/login', keyPrefix: 'REDDIT' },
50
- },
51
- github: {
52
- hosts: ['github.com'],
53
- cookies: ['user_session'],
54
- login: { loginUrl: 'https://github.com/login', keyPrefix: 'GITHUB' },
55
- },
56
- };
57
- /**
58
- * The bundle keys a profile's `--secrets` bundle should hold to log into
59
- * `service`: `<PREFIX>_USERNAME` / `<PREFIX>_PASSWORD`. Null for services with no
60
- * login metadata. Feed a resolved value to the page with
61
- * `agents browser type <ref> --secret <bundle>/<KEY>` (never printed).
62
- */
63
- export function credKeysForService(service) {
64
- const login = AUTH_SIGNATURES[service]?.login;
65
- if (!login)
66
- return null;
67
- return { user: `${login.keyPrefix}_USERNAME`, pass: `${login.keyPrefix}_PASSWORD` };
68
- }
69
- /** The interactive login URL for a service, or null. */
70
- export function loginUrlForService(service) {
71
- return AUTH_SIGNATURES[service]?.login?.loginUrl ?? null;
72
- }
73
- /**
74
- * Current time as microseconds since 1601-01-01 (Chromium's `expires_utc`
75
- * epoch), as a BigInt. Chromium expiries routinely exceed 2^53, so this is
76
- * bound into SQL as a BigInt and the comparison happens IN SQLite — the huge
77
- * integer never marshals back to JS (node:sqlite throws RangeError if it does).
78
- * 11644473600000 = ms between 1601-01-01 and 1970-01-01.
79
- */
80
- function chromeNowMicrosBigInt() {
81
- return (BigInt(Date.now()) + 11644473600000n) * 1000n;
82
- }
83
- function hostMatches(hostKey, host) {
84
- return hostKey === host || hostKey === '.' + host || hostKey.endsWith('.' + host);
85
- }
86
- /**
87
- * Pure core: given a profile's (already expiry-filtered) cookie rows, return the
88
- * services with a live session. Exported for direct unit testing without disk.
89
- */
90
- export function detectServices(rows) {
91
- const services = [];
92
- for (const [service, sig] of Object.entries(AUTH_SIGNATURES)) {
93
- const found = rows.some((r) => sig.cookies.includes(r.name) && sig.hosts.some((h) => hostMatches(r.host_key, h)));
94
- if (found)
95
- services.push(service);
96
- }
97
- return services;
98
- }
99
- /** Map a URL to a known login-gated service key, or null. */
100
- export function serviceForUrl(url) {
101
- let host;
102
- try {
103
- host = new URL(url).hostname;
104
- }
105
- catch {
106
- return null;
107
- }
108
- for (const [service, sig] of Object.entries(AUTH_SIGNATURES)) {
109
- if (sig.hosts.some((h) => host === h || host.endsWith('.' + h)))
110
- return service;
111
- }
112
- return null;
113
- }
114
- /**
115
- * Locate candidate Chromium DBs for a profile at the given relative paths. The
116
- * live runtime dir is keyed by the composite `<profile>@<endpoint>` name, so we
117
- * scan the browser cache root for `<profile>` and `<profile>@*` dirs and return
118
- * every matching DB found, most-recently-modified first.
119
- */
120
- function profileDbCandidates(profileName, relPaths) {
121
- const root = getBrowserRuntimeDir();
122
- let entries;
123
- try {
124
- entries = fs.readdirSync(root);
125
- }
126
- catch {
127
- return [];
128
- }
129
- const dirs = entries.filter((e) => e === profileName || e.startsWith(profileName + '@'));
130
- const found = [];
131
- for (const d of dirs) {
132
- for (const rel of relPaths) {
133
- const p = path.join(root, d, ...rel);
134
- try {
135
- found.push({ p, mtime: fs.statSync(p).mtimeMs });
136
- }
137
- catch {
138
- /* not present */
139
- }
140
- }
141
- }
142
- return found.sort((a, b) => b.mtime - a.mtime).map((f) => f.p);
143
- }
144
- function cookieDbCandidates(profileName) {
145
- return profileDbCandidates(profileName, [
146
- ['chrome-data', 'Default', 'Cookies'],
147
- ['chrome-data', 'Default', 'Network', 'Cookies'],
148
- ]);
149
- }
150
- function loginDbCandidates(profileName) {
151
- return profileDbCandidates(profileName, [['chrome-data', 'Default', 'Login Data']]);
152
- }
153
- /**
154
- * Open a Chromium SQLite store safely: copy the DB (and any WAL/SHM sidecars) to
155
- * a temp dir first so a running browser's lock never blocks us and we never
156
- * touch the live file, run `fn`, then clean up. Returns `fallback` on any error.
157
- */
158
- function withCopiedDb(dbPath, fn, fallback) {
159
- let tmpDir = null;
160
- try {
161
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agents-browserdb-'));
162
- const tmpDb = path.join(tmpDir, 'db');
163
- fs.copyFileSync(dbPath, tmpDb);
164
- for (const suffix of ['-wal', '-shm']) {
165
- if (fs.existsSync(dbPath + suffix))
166
- fs.copyFileSync(dbPath + suffix, tmpDb + suffix);
167
- }
168
- const db = new Database(tmpDb);
169
- try {
170
- return fn(db);
171
- }
172
- finally {
173
- db.close();
174
- }
175
- }
176
- catch {
177
- return fallback;
178
- }
179
- finally {
180
- if (tmpDir) {
181
- try {
182
- fs.rmSync(tmpDir, { recursive: true, force: true });
183
- }
184
- catch {
185
- /* temp cleanup best-effort */
186
- }
187
- }
188
- }
189
- }
190
- /** Read (expiry-filtered) cookie rows from a Chromium cookie DB. [] on failure. */
191
- function readCookieRows(dbPath) {
192
- return withCopiedDb(dbPath, (db) => {
193
- // Filter expiry in SQL (session cookies have expires_utc = 0; otherwise it
194
- // must be in the future) and select only text columns, so the huge
195
- // microsecond integer never crosses into JS.
196
- const rows = db
197
- .prepare('SELECT host_key, name FROM cookies WHERE expires_utc = 0 OR expires_utc > ?')
198
- .all(chromeNowMicrosBigInt());
199
- return rows.map((r) => ({
200
- host_key: String(r.host_key ?? ''),
201
- name: String(r.name ?? ''),
202
- }));
203
- }, []);
204
- }
205
- /** Read saved-login rows from a Chromium `Login Data` DB. Skips
206
- * user-blacklisted origins ("never save"). Selects only text columns —
207
- * `password_value` (encrypted) is never touched. [] on failure. */
208
- function readLoginRows(dbPath) {
209
- return withCopiedDb(dbPath, (db) => {
210
- const rows = db
211
- .prepare('SELECT origin_url, username_value, signon_realm FROM logins WHERE blacklisted_by_user = 0')
212
- .all();
213
- return rows.map((r) => ({
214
- origin_url: String(r.origin_url ?? ''),
215
- username_value: String(r.username_value ?? ''),
216
- signon_realm: String(r.signon_realm ?? ''),
217
- }));
218
- }, []);
219
- }
220
- /**
221
- * Map a profile's saved logins to service → account username (identity, not
222
- * session). Best-effort; `{}` if `Login Data` is unreadable. The first non-empty
223
- * username per service wins.
224
- */
225
- export async function accountsForProfile(profileName) {
226
- const candidates = loginDbCandidates(profileName);
227
- if (candidates.length === 0)
228
- return {};
229
- const rows = readLoginRows(candidates[0]);
230
- const out = {};
231
- for (const r of rows) {
232
- if (!r.username_value)
233
- continue;
234
- const service = serviceForUrl(r.origin_url) ?? serviceForUrl(r.signon_realm);
235
- if (service && !out[service])
236
- out[service] = r.username_value;
237
- }
238
- return out;
239
- }
240
- /**
241
- * For each service with a LIVE session in the profile (cookie-gated), the
242
- * account it is signed in as (from saved logins). Intersecting with live
243
- * sessions means a stale saved-login for a service you've since logged out of
244
- * never shows.
245
- */
246
- export async function loginsWithAccountsForProfile(profileName) {
247
- const active = await loginsForProfile(profileName);
248
- if (active.length === 0)
249
- return [];
250
- const accounts = await accountsForProfile(profileName);
251
- return active.map((service) => ({ service, username: accounts[service] }));
252
- }
253
- /** Services with a live session in the given profile (best-effort; [] if none
254
- * detected or the cookie store is unreadable). */
255
- export async function loginsForProfile(profileName) {
256
- const candidates = cookieDbCandidates(profileName);
257
- if (candidates.length === 0)
258
- return [];
259
- const rows = readCookieRows(candidates[0]);
260
- if (rows.length === 0)
261
- return [];
262
- return detectServices(rows);
263
- }
264
- /** Profile names that have a live session for the given service. */
265
- export async function profilesLoggedInto(service) {
266
- const profiles = await listProfiles();
267
- const out = [];
268
- for (const p of profiles) {
269
- const logins = await loginsForProfile(p.name);
270
- if (logins.includes(service))
271
- out.push(p.name);
272
- }
273
- return out;
274
- }