@flrande/browserctl 0.5.0-dev.22.1 → 0.6.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.
Files changed (136) hide show
  1. package/dist/client.d.ts +34 -0
  2. package/dist/client.js +138 -0
  3. package/dist/commandRegistry.d.ts +16 -0
  4. package/dist/commandRegistry.js +21 -0
  5. package/dist/help.d.ts +4 -0
  6. package/dist/help.js +24 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.js +23 -0
  9. package/dist/runCli.d.ts +5 -0
  10. package/dist/runCli.js +170 -0
  11. package/package.json +32 -59
  12. package/INSTALL-CN.md +0 -92
  13. package/INSTALL.md +0 -92
  14. package/LICENSE +0 -21
  15. package/README-CN.md +0 -69
  16. package/README.md +0 -69
  17. package/apps/browserctl/src/commands/a11y-snapshot.ts +0 -20
  18. package/apps/browserctl/src/commands/act.test.ts +0 -71
  19. package/apps/browserctl/src/commands/act.ts +0 -64
  20. package/apps/browserctl/src/commands/command-wrappers.test.ts +0 -688
  21. package/apps/browserctl/src/commands/common.test.ts +0 -87
  22. package/apps/browserctl/src/commands/common.ts +0 -191
  23. package/apps/browserctl/src/commands/console-list.test.ts +0 -102
  24. package/apps/browserctl/src/commands/console-list.ts +0 -108
  25. package/apps/browserctl/src/commands/cookie-clear.ts +0 -18
  26. package/apps/browserctl/src/commands/cookie-get.ts +0 -18
  27. package/apps/browserctl/src/commands/cookie-set.ts +0 -22
  28. package/apps/browserctl/src/commands/dialog-arm.ts +0 -20
  29. package/apps/browserctl/src/commands/dom-query-all.ts +0 -18
  30. package/apps/browserctl/src/commands/dom-query.ts +0 -18
  31. package/apps/browserctl/src/commands/download-trigger.ts +0 -22
  32. package/apps/browserctl/src/commands/download-wait.test.ts +0 -67
  33. package/apps/browserctl/src/commands/download-wait.ts +0 -27
  34. package/apps/browserctl/src/commands/element-screenshot.ts +0 -20
  35. package/apps/browserctl/src/commands/frame-list.ts +0 -16
  36. package/apps/browserctl/src/commands/frame-snapshot.ts +0 -18
  37. package/apps/browserctl/src/commands/har-export.test.ts +0 -112
  38. package/apps/browserctl/src/commands/har-export.ts +0 -120
  39. package/apps/browserctl/src/commands/memory-delete.ts +0 -20
  40. package/apps/browserctl/src/commands/memory-inspect.ts +0 -20
  41. package/apps/browserctl/src/commands/memory-list.ts +0 -90
  42. package/apps/browserctl/src/commands/memory-mode-set.ts +0 -29
  43. package/apps/browserctl/src/commands/memory-purge.ts +0 -16
  44. package/apps/browserctl/src/commands/memory-resolve.ts +0 -56
  45. package/apps/browserctl/src/commands/memory-status.ts +0 -16
  46. package/apps/browserctl/src/commands/memory-ttl-set.ts +0 -28
  47. package/apps/browserctl/src/commands/memory-upsert.ts +0 -142
  48. package/apps/browserctl/src/commands/network-list.test.ts +0 -110
  49. package/apps/browserctl/src/commands/network-list.ts +0 -112
  50. package/apps/browserctl/src/commands/network-wait-for.test.ts +0 -90
  51. package/apps/browserctl/src/commands/network-wait-for.ts +0 -100
  52. package/apps/browserctl/src/commands/profile-list.ts +0 -16
  53. package/apps/browserctl/src/commands/profile-use.ts +0 -18
  54. package/apps/browserctl/src/commands/response-body.ts +0 -24
  55. package/apps/browserctl/src/commands/screenshot.ts +0 -16
  56. package/apps/browserctl/src/commands/session-drop.test.ts +0 -36
  57. package/apps/browserctl/src/commands/session-drop.ts +0 -16
  58. package/apps/browserctl/src/commands/session-list.test.ts +0 -81
  59. package/apps/browserctl/src/commands/session-list.ts +0 -70
  60. package/apps/browserctl/src/commands/snapshot.ts +0 -16
  61. package/apps/browserctl/src/commands/status.ts +0 -10
  62. package/apps/browserctl/src/commands/storage-get.ts +0 -20
  63. package/apps/browserctl/src/commands/storage-set.ts +0 -22
  64. package/apps/browserctl/src/commands/tab-close.ts +0 -20
  65. package/apps/browserctl/src/commands/tab-focus.ts +0 -20
  66. package/apps/browserctl/src/commands/tab-open.ts +0 -19
  67. package/apps/browserctl/src/commands/tabs.ts +0 -13
  68. package/apps/browserctl/src/commands/trace-get.test.ts +0 -61
  69. package/apps/browserctl/src/commands/trace-get.ts +0 -62
  70. package/apps/browserctl/src/commands/upload-arm.ts +0 -26
  71. package/apps/browserctl/src/commands/wait-element.test.ts +0 -80
  72. package/apps/browserctl/src/commands/wait-element.ts +0 -76
  73. package/apps/browserctl/src/commands/wait-text.test.ts +0 -110
  74. package/apps/browserctl/src/commands/wait-text.ts +0 -93
  75. package/apps/browserctl/src/commands/wait-url.test.ts +0 -80
  76. package/apps/browserctl/src/commands/wait-url.ts +0 -76
  77. package/apps/browserctl/src/daemon-client.test.ts +0 -512
  78. package/apps/browserctl/src/daemon-client.ts +0 -632
  79. package/apps/browserctl/src/e2e.test.ts +0 -103
  80. package/apps/browserctl/src/main.dispatch.test.ts +0 -461
  81. package/apps/browserctl/src/main.test.ts +0 -334
  82. package/apps/browserctl/src/main.ts +0 -957
  83. package/apps/browserctl/src/smoke.e2e.test.ts +0 -97
  84. package/apps/browserctl/src/test-port.ts +0 -26
  85. package/apps/browserd/src/bootstrap.ts +0 -432
  86. package/apps/browserd/src/chrome-relay-extension-bridge.test.ts +0 -250
  87. package/apps/browserd/src/chrome-relay-extension-bridge.ts +0 -506
  88. package/apps/browserd/src/container.ts +0 -3088
  89. package/apps/browserd/src/main.test.ts +0 -1522
  90. package/apps/browserd/src/main.ts +0 -7
  91. package/apps/browserd/src/test-port.ts +0 -26
  92. package/apps/browserd/src/tool-matrix.test.ts +0 -887
  93. package/bin/browserctl.cjs +0 -21
  94. package/bin/browserd.cjs +0 -21
  95. package/extensions/chrome-relay/README-CN.md +0 -39
  96. package/extensions/chrome-relay/README.md +0 -39
  97. package/extensions/chrome-relay/background.js +0 -1687
  98. package/extensions/chrome-relay/manifest.json +0 -15
  99. package/extensions/chrome-relay/popup.html +0 -369
  100. package/extensions/chrome-relay/popup.js +0 -972
  101. package/packages/core/src/bootstrap.test.ts +0 -10
  102. package/packages/core/src/driver-registry.test.ts +0 -45
  103. package/packages/core/src/driver-registry.ts +0 -22
  104. package/packages/core/src/driver.ts +0 -47
  105. package/packages/core/src/index.ts +0 -6
  106. package/packages/core/src/navigation-memory.test.ts +0 -259
  107. package/packages/core/src/navigation-memory.ts +0 -360
  108. package/packages/core/src/ref-cache.test.ts +0 -61
  109. package/packages/core/src/ref-cache.ts +0 -28
  110. package/packages/core/src/session-store.test.ts +0 -82
  111. package/packages/core/src/session-store.ts +0 -138
  112. package/packages/core/src/types.ts +0 -9
  113. package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +0 -744
  114. package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +0 -2429
  115. package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.test.ts +0 -264
  116. package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.ts +0 -521
  117. package/packages/driver-chrome-relay/src/index.ts +0 -26
  118. package/packages/driver-managed/src/index.ts +0 -22
  119. package/packages/driver-managed/src/managed-driver.test.ts +0 -183
  120. package/packages/driver-managed/src/managed-driver.ts +0 -341
  121. package/packages/driver-managed/src/managed-local-driver.test.ts +0 -608
  122. package/packages/driver-managed/src/managed-local-driver.ts +0 -2243
  123. package/packages/driver-remote-cdp/src/index.ts +0 -19
  124. package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +0 -727
  125. package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +0 -2264
  126. package/packages/protocol/src/envelope.test.ts +0 -25
  127. package/packages/protocol/src/envelope.ts +0 -31
  128. package/packages/protocol/src/errors.test.ts +0 -17
  129. package/packages/protocol/src/errors.ts +0 -11
  130. package/packages/protocol/src/index.ts +0 -3
  131. package/packages/protocol/src/tools.ts +0 -3
  132. package/packages/transport-mcp-stdio/src/index.ts +0 -3
  133. package/packages/transport-mcp-stdio/src/sdk-server.ts +0 -139
  134. package/packages/transport-mcp-stdio/src/server.test.ts +0 -281
  135. package/packages/transport-mcp-stdio/src/server.ts +0 -183
  136. package/packages/transport-mcp-stdio/src/tool-map.ts +0 -84
@@ -1,957 +0,0 @@
1
- import { pathToFileURL } from "node:url";
2
-
3
- import { runActCommand } from "./commands/act";
4
- import { runA11ySnapshotCommand } from "./commands/a11y-snapshot";
5
- import { runCookieClearCommand } from "./commands/cookie-clear";
6
- import { runCookieGetCommand } from "./commands/cookie-get";
7
- import { runCookieSetCommand } from "./commands/cookie-set";
8
- import { runConsoleListCommand } from "./commands/console-list";
9
- import { runDialogArmCommand } from "./commands/dialog-arm";
10
- import { runDomQueryAllCommand } from "./commands/dom-query-all";
11
- import { runDomQueryCommand } from "./commands/dom-query";
12
- import { runDownloadTriggerCommand } from "./commands/download-trigger";
13
- import { runDownloadWaitCommand } from "./commands/download-wait";
14
- import { runElementScreenshotCommand } from "./commands/element-screenshot";
15
- import { runFrameListCommand } from "./commands/frame-list";
16
- import { runFrameSnapshotCommand } from "./commands/frame-snapshot";
17
- import { runHarExportCommand } from "./commands/har-export";
18
- import { runMemoryDeleteCommand } from "./commands/memory-delete";
19
- import { runMemoryInspectCommand } from "./commands/memory-inspect";
20
- import { runMemoryListCommand } from "./commands/memory-list";
21
- import { runMemoryModeSetCommand } from "./commands/memory-mode-set";
22
- import { runMemoryPurgeCommand } from "./commands/memory-purge";
23
- import { runMemoryResolveCommand } from "./commands/memory-resolve";
24
- import { runMemoryStatusCommand } from "./commands/memory-status";
25
- import { runMemoryTtlSetCommand } from "./commands/memory-ttl-set";
26
- import { runMemoryUpsertCommand } from "./commands/memory-upsert";
27
- import { runNetworkListCommand } from "./commands/network-list";
28
- import { runNetworkWaitForCommand } from "./commands/network-wait-for";
29
- import { parseCommandContext } from "./commands/common";
30
- import { runProfileListCommand } from "./commands/profile-list";
31
- import { runProfileUseCommand } from "./commands/profile-use";
32
- import { runResponseBodyCommand } from "./commands/response-body";
33
- import { runScreenshotCommand } from "./commands/screenshot";
34
- import { runSessionDropCommand } from "./commands/session-drop";
35
- import { runSessionListCommand } from "./commands/session-list";
36
- import { runSnapshotCommand } from "./commands/snapshot";
37
- import { runStatusCommand } from "./commands/status";
38
- import { runStorageGetCommand } from "./commands/storage-get";
39
- import { runStorageSetCommand } from "./commands/storage-set";
40
- import { runTabCloseCommand } from "./commands/tab-close";
41
- import { runTabFocusCommand } from "./commands/tab-focus";
42
- import { runTabOpenCommand } from "./commands/tab-open";
43
- import { runTabsCommand } from "./commands/tabs";
44
- import { runTraceGetCommand } from "./commands/trace-get";
45
- import { runUploadArmCommand } from "./commands/upload-arm";
46
- import { runWaitElementCommand } from "./commands/wait-element";
47
- import { runWaitTextCommand } from "./commands/wait-text";
48
- import { runWaitUrlCommand } from "./commands/wait-url";
49
- import { ensureDaemonRunning, getDaemonStatus, stopDaemon } from "./daemon-client";
50
-
51
- export const EXIT_CODES = {
52
- OK: 0,
53
- COMMAND_ERROR: 1,
54
- INVALID_ARGS: 2
55
- } as const;
56
-
57
- export type CommandName =
58
- | "status"
59
- | "tabs"
60
- | "profile-list"
61
- | "profile-use"
62
- | "tab-open"
63
- | "tab-focus"
64
- | "tab-close"
65
- | "snapshot"
66
- | "screenshot"
67
- | "dom-query"
68
- | "dom-query-all"
69
- | "element-screenshot"
70
- | "a11y-snapshot"
71
- | "wait-element"
72
- | "wait-text"
73
- | "wait-url"
74
- | "network-list"
75
- | "har-export"
76
- | "network-wait-for"
77
- | "cookie-get"
78
- | "cookie-set"
79
- | "cookie-clear"
80
- | "storage-get"
81
- | "storage-set"
82
- | "frame-list"
83
- | "frame-snapshot"
84
- | "act"
85
- | "upload-arm"
86
- | "dialog-arm"
87
- | "download-trigger"
88
- | "download-wait"
89
- | "console-list"
90
- | "response-body"
91
- | "session-list"
92
- | "session-drop"
93
- | "trace-get"
94
- | "memory-status"
95
- | "memory-resolve"
96
- | "memory-upsert"
97
- | "memory-list"
98
- | "memory-inspect"
99
- | "memory-delete"
100
- | "memory-purge"
101
- | "memory-mode-set"
102
- | "memory-ttl-set"
103
- | "daemon-status"
104
- | "daemon-start"
105
- | "daemon-stop";
106
-
107
- export interface ParsedArgs {
108
- command: CommandName | null;
109
- commandArgs: string[];
110
- json: boolean;
111
- helpTopic?: HelpTopic;
112
- error?: string;
113
- }
114
-
115
- export type HelpTopic = "all" | CommandName;
116
-
117
- const VALID_COMMANDS: ReadonlySet<CommandName> = new Set([
118
- "status",
119
- "tabs",
120
- "profile-list",
121
- "profile-use",
122
- "tab-open",
123
- "tab-focus",
124
- "tab-close",
125
- "snapshot",
126
- "screenshot",
127
- "dom-query",
128
- "dom-query-all",
129
- "element-screenshot",
130
- "a11y-snapshot",
131
- "wait-element",
132
- "wait-text",
133
- "wait-url",
134
- "network-list",
135
- "har-export",
136
- "network-wait-for",
137
- "cookie-get",
138
- "cookie-set",
139
- "cookie-clear",
140
- "storage-get",
141
- "storage-set",
142
- "frame-list",
143
- "frame-snapshot",
144
- "act",
145
- "upload-arm",
146
- "dialog-arm",
147
- "download-trigger",
148
- "download-wait",
149
- "console-list",
150
- "response-body",
151
- "session-list",
152
- "session-drop",
153
- "trace-get",
154
- "memory-status",
155
- "memory-resolve",
156
- "memory-upsert",
157
- "memory-list",
158
- "memory-inspect",
159
- "memory-delete",
160
- "memory-purge",
161
- "memory-mode-set",
162
- "memory-ttl-set",
163
- "daemon-status",
164
- "daemon-start",
165
- "daemon-stop"
166
- ]);
167
-
168
- interface CommandHelpEntry {
169
- usage: string;
170
- description: string;
171
- }
172
-
173
- const COMMAND_HELP: Readonly<Record<CommandName, CommandHelpEntry>> = {
174
- status: {
175
- usage: "status",
176
- description: "Query daemon and active profile readiness."
177
- },
178
- tabs: {
179
- usage: "tabs",
180
- description: "List tabs for the current session."
181
- },
182
- "profile-list": {
183
- usage: "profile-list",
184
- description: "List available driver profiles."
185
- },
186
- "profile-use": {
187
- usage: "profile-use <driverKey>",
188
- description: "Bind session to a driver profile."
189
- },
190
- "tab-open": {
191
- usage: "tab-open <url>",
192
- description: "Open a new tab with the given URL."
193
- },
194
- "tab-focus": {
195
- usage: "tab-focus <targetId>",
196
- description: "Activate an existing tab target."
197
- },
198
- "tab-close": {
199
- usage: "tab-close <targetId>",
200
- description: "Close a tab target."
201
- },
202
- snapshot: {
203
- usage: "snapshot <targetId>",
204
- description: "Get structured DOM snapshot for a tab."
205
- },
206
- screenshot: {
207
- usage: "screenshot <targetId>",
208
- description: "Capture full-page screenshot for a tab."
209
- },
210
- "dom-query": {
211
- usage: "dom-query <targetId> <selector>",
212
- description: "Get first matching element details."
213
- },
214
- "dom-query-all": {
215
- usage: "dom-query-all <targetId> <selector>",
216
- description: "List all matching elements."
217
- },
218
- "element-screenshot": {
219
- usage: "element-screenshot <targetId> <selector>",
220
- description: "Capture screenshot for one element."
221
- },
222
- "a11y-snapshot": {
223
- usage: "a11y-snapshot <targetId> [selector]",
224
- description: "Get accessibility tree snapshot."
225
- },
226
- "wait-element": {
227
- usage: "wait-element <targetId> <selector> [--timeout-ms <ms>] [--poll-ms <ms>]",
228
- description: "Wait until selector appears in the page."
229
- },
230
- "wait-text": {
231
- usage: "wait-text <targetId> <text> [--selector <selector>] [--timeout-ms <ms>] [--poll-ms <ms>]",
232
- description: "Wait until expected text appears."
233
- },
234
- "wait-url": {
235
- usage: "wait-url <targetId> <urlPattern> [--timeout-ms <ms>] [--poll-ms <ms>]",
236
- description: "Wait until current URL matches a pattern."
237
- },
238
- "network-list": {
239
- usage:
240
- "network-list <targetId> [--url-contains <text>] [--method <METHOD>] [--status <CODE>] [--since <ISO>] [--limit <n>]",
241
- description: "List captured network requests with filters."
242
- },
243
- "har-export": {
244
- usage:
245
- "har-export <targetId> [--include-bodies] [--url-contains <text>] [--method <METHOD>] [--status <CODE>] [--since <ISO>] [--limit <n>]",
246
- description: "Export captured network requests as HAR."
247
- },
248
- "network-wait-for": {
249
- usage:
250
- "network-wait-for <targetId> <urlPattern> [--method <METHOD>] [--status <CODE>] [--timeout-ms <ms>] [--poll-ms <ms>]",
251
- description: "Wait until a matching network response appears."
252
- },
253
- "cookie-get": {
254
- usage: "cookie-get <targetId> [name]",
255
- description: "Read cookie(s) from page context."
256
- },
257
- "cookie-set": {
258
- usage: "cookie-set <targetId> <name> <value> [url]",
259
- description: "Set one cookie in page context."
260
- },
261
- "cookie-clear": {
262
- usage: "cookie-clear <targetId> [name]",
263
- description: "Clear one or all cookies."
264
- },
265
- "storage-get": {
266
- usage: "storage-get <targetId> <local|session> <key>",
267
- description: "Read a storage value."
268
- },
269
- "storage-set": {
270
- usage: "storage-set <targetId> <local|session> <key> <value>",
271
- description: "Write a storage value."
272
- },
273
- "frame-list": {
274
- usage: "frame-list <targetId>",
275
- description: "List frames for a tab."
276
- },
277
- "frame-snapshot": {
278
- usage: "frame-snapshot <targetId> <frameId>",
279
- description: "Snapshot one frame subtree."
280
- },
281
- act: {
282
- usage: "act <actionType> <targetId> [--payload-json <json>]",
283
- description: "Execute high-level action against a tab."
284
- },
285
- "upload-arm": {
286
- usage: "upload-arm <targetId> <file1> [file2 ...]",
287
- description: "Prepare file chooser upload paths."
288
- },
289
- "dialog-arm": {
290
- usage: "dialog-arm <targetId>",
291
- description: "Prepare next JS dialog interception."
292
- },
293
- "download-trigger": {
294
- usage: "download-trigger <targetId>",
295
- description: "Trigger download capture mode."
296
- },
297
- "download-wait": {
298
- usage: "download-wait <targetId> [path]",
299
- description: "Wait for next download and persist file."
300
- },
301
- "console-list": {
302
- usage: "console-list <targetId> [--type <type>] [--contains <text>] [--since <ISO>] [--limit <n>]",
303
- description: "List collected console events."
304
- },
305
- "response-body": {
306
- usage: "response-body <targetId> <requestId>",
307
- description: "Read response body for a captured request."
308
- },
309
- "session-list": {
310
- usage: "session-list [--tenant <tenant>] [--limit <n>]",
311
- description: "List active sessions for governance and multi-tenant inspection."
312
- },
313
- "session-drop": {
314
- usage: "session-drop <sessionId>",
315
- description: "Drop one session state by id."
316
- },
317
- "trace-get": {
318
- usage: "trace-get [--limit <n>]",
319
- description: "Read task-level trace steps and key responses for current session."
320
- },
321
- "memory-status": {
322
- usage: "memory-status",
323
- description: "Read navigation memory runtime status and counters."
324
- },
325
- "memory-resolve": {
326
- usage: "memory-resolve <domain> <intentKey> [--profile-id <profileId>]",
327
- description: "Resolve a memory entry for a domain/intent key."
328
- },
329
- "memory-upsert": {
330
- usage:
331
- "memory-upsert <domain> <intentKey> --signals-json <json> [--profile-id <profileId>] [--confidence <n>] [--confirmed [true|false]]",
332
- description: "Create or update one memory entry."
333
- },
334
- "memory-list": {
335
- usage: "memory-list [--domain <domain>] [--profile-id <profileId>] [--intent-key <intentKey>]",
336
- description: "List navigation memory entries with optional filters."
337
- },
338
- "memory-inspect": {
339
- usage: "memory-inspect <id>",
340
- description: "Inspect one memory entry by id."
341
- },
342
- "memory-delete": {
343
- usage: "memory-delete <id>",
344
- description: "Delete one memory entry by id."
345
- },
346
- "memory-purge": {
347
- usage: "memory-purge",
348
- description: "Purge expired memory entries."
349
- },
350
- "memory-mode-set": {
351
- usage: "memory-mode-set <mode>",
352
- description: "Set navigation memory mode (off/ask/auto)."
353
- },
354
- "memory-ttl-set": {
355
- usage: "memory-ttl-set <ttlDays>",
356
- description: "Set navigation memory retention in days."
357
- },
358
- "daemon-status": {
359
- usage: "daemon-status",
360
- description: "Inspect local daemon process status."
361
- },
362
- "daemon-start": {
363
- usage: "daemon-start [--browser <chromium|chrome|edge>]",
364
- description: "Start daemon if needed and report runtime."
365
- },
366
- "daemon-stop": {
367
- usage: "daemon-stop",
368
- description: "Stop local daemon process."
369
- }
370
- };
371
-
372
- const COMMAND_GROUPS: ReadonlyArray<{ title: string; commands: readonly CommandName[] }> = [
373
- {
374
- title: "Daemon lifecycle",
375
- commands: ["daemon-start", "daemon-status", "daemon-stop"]
376
- },
377
- {
378
- title: "Driver/session",
379
- commands: ["status", "profile-list", "profile-use", "tabs", "session-list", "session-drop"]
380
- },
381
- {
382
- title: "Navigation memory",
383
- commands: [
384
- "memory-status",
385
- "memory-resolve",
386
- "memory-upsert",
387
- "memory-list",
388
- "memory-inspect",
389
- "memory-delete",
390
- "memory-purge",
391
- "memory-mode-set",
392
- "memory-ttl-set"
393
- ]
394
- },
395
- {
396
- title: "Tab/page",
397
- commands: ["tab-open", "tab-focus", "tab-close", "snapshot", "screenshot"]
398
- },
399
- {
400
- title: "Structured reads",
401
- commands: [
402
- "dom-query",
403
- "dom-query-all",
404
- "element-screenshot",
405
- "a11y-snapshot",
406
- "wait-element",
407
- "wait-text",
408
- "wait-url",
409
- "network-list",
410
- "har-export",
411
- "network-wait-for",
412
- "cookie-get",
413
- "cookie-set",
414
- "cookie-clear",
415
- "storage-get",
416
- "storage-set",
417
- "frame-list",
418
- "frame-snapshot",
419
- "console-list",
420
- "response-body",
421
- "trace-get"
422
- ]
423
- },
424
- {
425
- title: "Action/file flow",
426
- commands: ["act", "upload-arm", "dialog-arm", "download-trigger", "download-wait"]
427
- }
428
- ];
429
-
430
- const CHROME_RELAY_FAILURE_GUIDANCE = [
431
- "chrome-relay 连接失败,请先完成以下检查:",
432
- "1) 扩展 relay 方案:设置 BROWSERD_CHROME_RELAY_MODE=extension。",
433
- "2) 在 chrome://extensions 或 edge://extensions 中加载 extensions/chrome-relay(Load unpacked)。",
434
- "3) 在扩展弹窗里确认 Bridge URL 与 BROWSERD_CHROME_RELAY_URL 一致(默认 ws://127.0.0.1:9223/bridge)。",
435
- "4) 如果你走 CDP relay 方案,确认浏览器已启动远程调试端口(例如 --remote-debugging-port=9223)。",
436
- "5) 确认 BROWSERD_CHROME_RELAY_URL 指向可访问地址(默认 http://127.0.0.1:9223)。"
437
- ].join("\n");
438
-
439
- function isHelpFlag(value: string): boolean {
440
- return value === "--help" || value === "-h";
441
- }
442
-
443
- function formatGeneralHelp(): string {
444
- const lines = [
445
- "browserctl - BrowserCtl command line client",
446
- "",
447
- "Usage:",
448
- " browserctl [--json] <command> [command-options] [positionals]",
449
- " browserctl help [command]",
450
- " browserctl <command> --help",
451
- "",
452
- "Global options:",
453
- " --json Print success/error envelopes as JSON.",
454
- " --help, -h Show general help or command help.",
455
- "",
456
- "Context options (browser commands):",
457
- " --session <id> Session namespace (default: cli:local).",
458
- " --profile <driverKey> Driver override.",
459
- " --token <authToken> Request auth token override.",
460
- " --browser <preset> Daemon startup browser preset (daemon-start only).",
461
- "",
462
- "Commands:"
463
- ];
464
-
465
- for (const group of COMMAND_GROUPS) {
466
- lines.push(` ${group.title}:`);
467
- for (const command of group.commands) {
468
- const entry = COMMAND_HELP[command];
469
- lines.push(` ${entry.usage}`);
470
- lines.push(` ${entry.description}`);
471
- }
472
- }
473
-
474
- lines.push("");
475
- lines.push("Run \"browserctl help <command>\" for command-specific usage.");
476
-
477
- return `${lines.join("\n")}\n`;
478
- }
479
-
480
- function formatCommandHelp(command: CommandName): string {
481
- const entry = COMMAND_HELP[command];
482
- const lines = [`Command: ${command}`, "", "Usage:", ` browserctl ${entry.usage}`, "", entry.description];
483
- const isDaemonCommand = command.startsWith("daemon-");
484
-
485
- if (!isDaemonCommand) {
486
- lines.push("");
487
- lines.push("Context options:");
488
- lines.push(" --session <id> Session namespace (default: cli:local).");
489
- lines.push(" --profile <driverKey> Driver override.");
490
- lines.push(" --token <authToken> Request auth token override.");
491
- if (command === "act") {
492
- lines.push("");
493
- lines.push("Command options:");
494
- lines.push(" --payload-json <json> JSON object forwarded as action.payload.");
495
- } else if (command === "wait-element" || command === "wait-url") {
496
- lines.push("");
497
- lines.push("Command options:");
498
- lines.push(" --timeout-ms <ms> Maximum wait time (positive integer).");
499
- lines.push(" --poll-ms <ms> Poll interval (positive integer).");
500
- } else if (command === "wait-text") {
501
- lines.push("");
502
- lines.push("Command options:");
503
- lines.push(" --selector <selector> Limit matching to selector results.");
504
- lines.push(" --timeout-ms <ms> Maximum wait time (positive integer).");
505
- lines.push(" --poll-ms <ms> Poll interval (positive integer).");
506
- } else if (command === "network-list") {
507
- lines.push("");
508
- lines.push("Command options:");
509
- lines.push(" --url-contains <text> URL substring filter.");
510
- lines.push(" --method <METHOD> HTTP method filter.");
511
- lines.push(" --status <CODE> HTTP status filter.");
512
- lines.push(" --since <ISO> Timestamp lower bound.");
513
- lines.push(" --limit <n> Max number of entries.");
514
- } else if (command === "console-list") {
515
- lines.push("");
516
- lines.push("Command options:");
517
- lines.push(" --type <type> Console type filter (log/error/warn...).");
518
- lines.push(" --contains <text> Message substring filter.");
519
- lines.push(" --since <ISO> Timestamp lower bound.");
520
- lines.push(" --limit <n> Max number of entries.");
521
- } else if (command === "har-export") {
522
- lines.push("");
523
- lines.push("Command options:");
524
- lines.push(" --include-bodies Include response bodies when available.");
525
- lines.push(" --url-contains <text> URL substring filter.");
526
- lines.push(" --method <METHOD> HTTP method filter.");
527
- lines.push(" --status <CODE> HTTP status filter.");
528
- lines.push(" --since <ISO> Timestamp lower bound.");
529
- lines.push(" --limit <n> Max number of entries.");
530
- } else if (command === "trace-get") {
531
- lines.push("");
532
- lines.push("Command options:");
533
- lines.push(" --limit <n> Max number of records per trace section.");
534
- } else if (command === "session-list") {
535
- lines.push("");
536
- lines.push("Command options:");
537
- lines.push(" --tenant <tenant> Tenant filter.");
538
- lines.push(" --limit <n> Max number of sessions.");
539
- } else if (command === "memory-resolve") {
540
- lines.push("");
541
- lines.push("Command options:");
542
- lines.push(" --profile-id <profileId> Override memory scope profile id.");
543
- } else if (command === "memory-upsert") {
544
- lines.push("");
545
- lines.push("Command options:");
546
- lines.push(" --signals-json <json> Required JSON array of memory signals.");
547
- lines.push(" --profile-id <profileId> Override memory scope profile id.");
548
- lines.push(" --confidence <n> Optional confidence score.");
549
- lines.push(" --confirmed [true|false] Optional confirmation flag.");
550
- } else if (command === "memory-list") {
551
- lines.push("");
552
- lines.push("Command options:");
553
- lines.push(" --domain <domain> Optional domain filter.");
554
- lines.push(" --profile-id <profileId> Optional profile id filter.");
555
- lines.push(" --intent-key <intentKey> Optional intent key filter.");
556
- }
557
- } else if (command === "daemon-start") {
558
- lines.push("");
559
- lines.push("Command options:");
560
- lines.push(" --browser <chromium|chrome|edge> managed-local browser preset.");
561
- lines.push(" --token <authToken> Request auth token override.");
562
- } else if (command === "daemon-status") {
563
- lines.push("");
564
- lines.push("Command options:");
565
- lines.push(" --token <authToken> Request auth token override.");
566
- }
567
-
568
- lines.push("");
569
- lines.push("Global options:");
570
- lines.push(" --json");
571
- lines.push(" --help, -h");
572
-
573
- return `${lines.join("\n")}\n`;
574
- }
575
-
576
- function formatHelp(topic: HelpTopic): string {
577
- if (topic === "all") {
578
- return formatGeneralHelp();
579
- }
580
-
581
- return formatCommandHelp(topic);
582
- }
583
-
584
- export function parseArgs(argv: string[]): ParsedArgs {
585
- const positional: string[] = [];
586
- let json = false;
587
- let helpRequested = false;
588
-
589
- for (const value of argv) {
590
- if (value === "--json") {
591
- json = true;
592
- continue;
593
- }
594
-
595
- if (isHelpFlag(value)) {
596
- helpRequested = true;
597
- continue;
598
- }
599
-
600
- positional.push(value);
601
- }
602
-
603
- const [commandToken, ...commandArgs] = positional;
604
- if (commandToken === undefined) {
605
- if (helpRequested) {
606
- return {
607
- command: null,
608
- commandArgs: [],
609
- json,
610
- helpTopic: "all"
611
- };
612
- }
613
-
614
- return {
615
- command: null,
616
- commandArgs: [],
617
- json,
618
- error: "Missing command."
619
- };
620
- }
621
-
622
- if (commandToken === "help") {
623
- if (commandArgs.length === 0) {
624
- return {
625
- command: null,
626
- commandArgs: [],
627
- json,
628
- helpTopic: "all"
629
- };
630
- }
631
-
632
- if (commandArgs.length > 1) {
633
- return {
634
- command: null,
635
- commandArgs: [],
636
- json,
637
- error: "Too many arguments for help."
638
- };
639
- }
640
-
641
- const topic = commandArgs[0];
642
- if (!VALID_COMMANDS.has(topic as CommandName)) {
643
- return {
644
- command: null,
645
- commandArgs: [],
646
- json,
647
- error: `Unknown help topic: ${topic}`
648
- };
649
- }
650
-
651
- return {
652
- command: null,
653
- commandArgs: [],
654
- json,
655
- helpTopic: topic as CommandName
656
- };
657
- }
658
-
659
- if (helpRequested) {
660
- if (!VALID_COMMANDS.has(commandToken as CommandName)) {
661
- return {
662
- command: null,
663
- commandArgs: [],
664
- json,
665
- error: `Unknown command: ${commandToken}`
666
- };
667
- }
668
-
669
- return {
670
- command: null,
671
- commandArgs: [],
672
- json,
673
- helpTopic: commandToken as CommandName
674
- };
675
- }
676
-
677
- if (!VALID_COMMANDS.has(commandToken as CommandName)) {
678
- return {
679
- command: null,
680
- commandArgs: [],
681
- json,
682
- error: `Unknown command: ${commandToken}`
683
- };
684
- }
685
-
686
- return {
687
- command: commandToken as CommandName,
688
- commandArgs,
689
- json
690
- };
691
- }
692
-
693
- function tryResolveProfile(commandArgs: string[]): string | undefined {
694
- try {
695
- return parseCommandContext(commandArgs).profile;
696
- } catch {
697
- return undefined;
698
- }
699
- }
700
-
701
- function isConnectionFailureMessage(message: string): boolean {
702
- return /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|Timed out/i.test(message);
703
- }
704
-
705
- function shouldAttachChromeRelayGuidance(commandArgs: string[], message: string): boolean {
706
- const lowerMessage = message.toLowerCase();
707
- if (lowerMessage.includes("chrome relay extension is not connected")) {
708
- return true;
709
- }
710
-
711
- if (!isConnectionFailureMessage(message)) {
712
- return false;
713
- }
714
-
715
- const profile = tryResolveProfile(commandArgs);
716
- if (profile === "chrome-relay") {
717
- return true;
718
- }
719
-
720
- return (
721
- lowerMessage.includes("chrome-relay") ||
722
- lowerMessage.includes("connectovercdp") ||
723
- lowerMessage.includes("/json/version") ||
724
- lowerMessage.includes("9223")
725
- );
726
- }
727
-
728
- function enrichCommandErrorMessage(commandArgs: string[], message: string): string {
729
- if (!shouldAttachChromeRelayGuidance(commandArgs, message)) {
730
- return message;
731
- }
732
-
733
- return `${message}\n\n${CHROME_RELAY_FAILURE_GUIDANCE}`;
734
- }
735
-
736
- interface WritableLike {
737
- write(content: string): void;
738
- }
739
-
740
- export interface CliIo {
741
- stdout: WritableLike;
742
- stderr: WritableLike;
743
- }
744
-
745
- function formatHumanOutput(command: CommandName, data: unknown): string {
746
- return `${command}: ${JSON.stringify(data)}\n`;
747
- }
748
-
749
- function writeError(io: CliIo, message: string, json: boolean, exitCode: number): void {
750
- if (json) {
751
- io.stderr.write(
752
- `${JSON.stringify({
753
- ok: false,
754
- error: {
755
- message,
756
- exitCode
757
- }
758
- })}\n`
759
- );
760
- return;
761
- }
762
-
763
- io.stderr.write(`${message}\n`);
764
- }
765
-
766
- async function executeCommand(command: CommandName, commandArgs: string[]): Promise<unknown> {
767
- switch (command) {
768
- case "status":
769
- return await runStatusCommand(commandArgs);
770
- case "tabs":
771
- return await runTabsCommand(commandArgs);
772
- case "profile-list":
773
- return await runProfileListCommand(commandArgs);
774
- case "profile-use":
775
- return await runProfileUseCommand(commandArgs);
776
- case "tab-open":
777
- return await runTabOpenCommand(commandArgs);
778
- case "tab-focus":
779
- return await runTabFocusCommand(commandArgs);
780
- case "tab-close":
781
- return await runTabCloseCommand(commandArgs);
782
- case "snapshot":
783
- return await runSnapshotCommand(commandArgs);
784
- case "screenshot":
785
- return await runScreenshotCommand(commandArgs);
786
- case "dom-query":
787
- return await runDomQueryCommand(commandArgs);
788
- case "dom-query-all":
789
- return await runDomQueryAllCommand(commandArgs);
790
- case "element-screenshot":
791
- return await runElementScreenshotCommand(commandArgs);
792
- case "a11y-snapshot":
793
- return await runA11ySnapshotCommand(commandArgs);
794
- case "wait-element":
795
- return await runWaitElementCommand(commandArgs);
796
- case "wait-text":
797
- return await runWaitTextCommand(commandArgs);
798
- case "wait-url":
799
- return await runWaitUrlCommand(commandArgs);
800
- case "network-list":
801
- return await runNetworkListCommand(commandArgs);
802
- case "har-export":
803
- return await runHarExportCommand(commandArgs);
804
- case "network-wait-for":
805
- return await runNetworkWaitForCommand(commandArgs);
806
- case "cookie-get":
807
- return await runCookieGetCommand(commandArgs);
808
- case "cookie-set":
809
- return await runCookieSetCommand(commandArgs);
810
- case "cookie-clear":
811
- return await runCookieClearCommand(commandArgs);
812
- case "storage-get":
813
- return await runStorageGetCommand(commandArgs);
814
- case "storage-set":
815
- return await runStorageSetCommand(commandArgs);
816
- case "frame-list":
817
- return await runFrameListCommand(commandArgs);
818
- case "frame-snapshot":
819
- return await runFrameSnapshotCommand(commandArgs);
820
- case "act":
821
- return await runActCommand(commandArgs);
822
- case "upload-arm":
823
- return await runUploadArmCommand(commandArgs);
824
- case "dialog-arm":
825
- return await runDialogArmCommand(commandArgs);
826
- case "download-trigger":
827
- return await runDownloadTriggerCommand(commandArgs);
828
- case "download-wait":
829
- return await runDownloadWaitCommand(commandArgs);
830
- case "console-list":
831
- return await runConsoleListCommand(commandArgs);
832
- case "response-body":
833
- return await runResponseBodyCommand(commandArgs);
834
- case "session-list":
835
- return await runSessionListCommand(commandArgs);
836
- case "session-drop":
837
- return await runSessionDropCommand(commandArgs);
838
- case "trace-get":
839
- return await runTraceGetCommand(commandArgs);
840
- case "memory-status":
841
- return await runMemoryStatusCommand(commandArgs);
842
- case "memory-resolve":
843
- return await runMemoryResolveCommand(commandArgs);
844
- case "memory-upsert":
845
- return await runMemoryUpsertCommand(commandArgs);
846
- case "memory-list":
847
- return await runMemoryListCommand(commandArgs);
848
- case "memory-inspect":
849
- return await runMemoryInspectCommand(commandArgs);
850
- case "memory-delete":
851
- return await runMemoryDeleteCommand(commandArgs);
852
- case "memory-purge":
853
- return await runMemoryPurgeCommand(commandArgs);
854
- case "memory-mode-set":
855
- return await runMemoryModeSetCommand(commandArgs);
856
- case "memory-ttl-set":
857
- return await runMemoryTtlSetCommand(commandArgs);
858
- case "daemon-status": {
859
- const context = parseCommandContext(commandArgs);
860
- return await getDaemonStatus({
861
- authToken: context.authToken
862
- });
863
- }
864
- case "daemon-start": {
865
- const context = parseCommandContext(commandArgs);
866
- return await ensureDaemonRunning({
867
- authToken: context.authToken,
868
- startup: context.daemonStartup
869
- });
870
- }
871
- case "daemon-stop":
872
- return stopDaemon();
873
- default:
874
- return neverCommand(command);
875
- }
876
- }
877
-
878
- function neverCommand(command: never): never {
879
- throw new Error(`Unsupported command: ${command}`);
880
- }
881
-
882
- export async function runCli(
883
- argv: string[],
884
- io: CliIo = {
885
- stdout: process.stdout,
886
- stderr: process.stderr
887
- }
888
- ): Promise<number> {
889
- const parsedArgs = parseArgs(argv);
890
- if (parsedArgs.error !== undefined) {
891
- writeError(io, parsedArgs.error, parsedArgs.json, EXIT_CODES.INVALID_ARGS);
892
- return EXIT_CODES.INVALID_ARGS;
893
- }
894
-
895
- if (parsedArgs.helpTopic !== undefined) {
896
- const helpText = formatHelp(parsedArgs.helpTopic);
897
- if (parsedArgs.json) {
898
- io.stdout.write(
899
- `${JSON.stringify({
900
- ok: true,
901
- command: "help",
902
- data: {
903
- topic: parsedArgs.helpTopic,
904
- text: helpText.trimEnd()
905
- }
906
- })}\n`
907
- );
908
- } else {
909
- io.stdout.write(helpText);
910
- }
911
-
912
- return EXIT_CODES.OK;
913
- }
914
-
915
- try {
916
- const data = await executeCommand(parsedArgs.command, parsedArgs.commandArgs);
917
-
918
- if (parsedArgs.json) {
919
- io.stdout.write(
920
- `${JSON.stringify({
921
- ok: true,
922
- command: parsedArgs.command,
923
- data
924
- })}\n`
925
- );
926
- } else {
927
- io.stdout.write(formatHumanOutput(parsedArgs.command, data));
928
- }
929
-
930
- return EXIT_CODES.OK;
931
- } catch (error) {
932
- const message = error instanceof Error ? error.message : "Unknown command failure.";
933
- writeError(
934
- io,
935
- enrichCommandErrorMessage(parsedArgs.commandArgs, message),
936
- parsedArgs.json,
937
- EXIT_CODES.COMMAND_ERROR
938
- );
939
- return EXIT_CODES.COMMAND_ERROR;
940
- }
941
- }
942
-
943
- export async function main(argv: string[] = process.argv.slice(2)): Promise<number> {
944
- return await runCli(argv);
945
- }
946
-
947
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
948
- void main()
949
- .then((exitCode) => {
950
- process.exitCode = exitCode;
951
- })
952
- .catch((error) => {
953
- const message = error instanceof Error ? error.message : "Unexpected CLI failure.";
954
- process.stderr.write(`${message}\n`);
955
- process.exitCode = EXIT_CODES.COMMAND_ERROR;
956
- });
957
- }