@flrande/browserctl 0.4.0-dev.15.1 → 0.5.0-dev.19.1

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 (52) hide show
  1. package/apps/browserctl/src/commands/act.test.ts +71 -0
  2. package/apps/browserctl/src/commands/act.ts +45 -1
  3. package/apps/browserctl/src/commands/command-wrappers.test.ts +302 -0
  4. package/apps/browserctl/src/commands/console-list.test.ts +102 -0
  5. package/apps/browserctl/src/commands/console-list.ts +89 -1
  6. package/apps/browserctl/src/commands/har-export.test.ts +112 -0
  7. package/apps/browserctl/src/commands/har-export.ts +120 -0
  8. package/apps/browserctl/src/commands/memory-delete.ts +20 -0
  9. package/apps/browserctl/src/commands/memory-inspect.ts +20 -0
  10. package/apps/browserctl/src/commands/memory-list.ts +90 -0
  11. package/apps/browserctl/src/commands/memory-mode-set.ts +29 -0
  12. package/apps/browserctl/src/commands/memory-purge.ts +16 -0
  13. package/apps/browserctl/src/commands/memory-resolve.ts +56 -0
  14. package/apps/browserctl/src/commands/memory-status.ts +16 -0
  15. package/apps/browserctl/src/commands/memory-ttl-set.ts +28 -0
  16. package/apps/browserctl/src/commands/memory-upsert.ts +142 -0
  17. package/apps/browserctl/src/commands/network-list.test.ts +110 -0
  18. package/apps/browserctl/src/commands/network-list.ts +112 -0
  19. package/apps/browserctl/src/commands/session-drop.test.ts +36 -0
  20. package/apps/browserctl/src/commands/session-drop.ts +16 -0
  21. package/apps/browserctl/src/commands/session-list.test.ts +81 -0
  22. package/apps/browserctl/src/commands/session-list.ts +70 -0
  23. package/apps/browserctl/src/commands/trace-get.test.ts +61 -0
  24. package/apps/browserctl/src/commands/trace-get.ts +62 -0
  25. package/apps/browserctl/src/commands/wait-element.test.ts +80 -0
  26. package/apps/browserctl/src/commands/wait-element.ts +76 -0
  27. package/apps/browserctl/src/commands/wait-text.test.ts +110 -0
  28. package/apps/browserctl/src/commands/wait-text.ts +93 -0
  29. package/apps/browserctl/src/commands/wait-url.test.ts +80 -0
  30. package/apps/browserctl/src/commands/wait-url.ts +76 -0
  31. package/apps/browserctl/src/main.dispatch.test.ts +206 -1
  32. package/apps/browserctl/src/main.test.ts +30 -0
  33. package/apps/browserctl/src/main.ts +246 -4
  34. package/apps/browserd/src/container.ts +1603 -48
  35. package/apps/browserd/src/main.test.ts +538 -1
  36. package/apps/browserd/src/tool-matrix.test.ts +492 -3
  37. package/package.json +5 -1
  38. package/packages/core/src/driver.ts +1 -1
  39. package/packages/core/src/index.ts +1 -0
  40. package/packages/core/src/navigation-memory.test.ts +259 -0
  41. package/packages/core/src/navigation-memory.ts +360 -0
  42. package/packages/core/src/session-store.test.ts +33 -0
  43. package/packages/core/src/session-store.ts +111 -6
  44. package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +112 -2
  45. package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +233 -10
  46. package/packages/driver-managed/src/managed-driver.test.ts +124 -0
  47. package/packages/driver-managed/src/managed-driver.ts +233 -17
  48. package/packages/driver-managed/src/managed-local-driver.test.ts +104 -2
  49. package/packages/driver-managed/src/managed-local-driver.ts +232 -10
  50. package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +112 -2
  51. package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +232 -10
  52. package/packages/transport-mcp-stdio/src/tool-map.ts +18 -1
@@ -1,4 +1,4 @@
1
- import { afterEach, describe, expect, it, vi } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
 
3
3
  import * as a11ySnapshotCommand from "./commands/a11y-snapshot";
4
4
  import * as consoleListCommand from "./commands/console-list";
@@ -14,17 +14,25 @@ import * as downloadWaitCommand from "./commands/download-wait";
14
14
  import * as elementScreenshotCommand from "./commands/element-screenshot";
15
15
  import * as frameListCommand from "./commands/frame-list";
16
16
  import * as frameSnapshotCommand from "./commands/frame-snapshot";
17
+ import * as harExportCommand from "./commands/har-export";
18
+ import * as networkListCommand from "./commands/network-list";
17
19
  import * as networkWaitForCommand from "./commands/network-wait-for";
18
20
  import * as profileListCommand from "./commands/profile-list";
19
21
  import * as profileUseCommand from "./commands/profile-use";
20
22
  import * as responseBodyCommand from "./commands/response-body";
23
+ import * as sessionDropCommand from "./commands/session-drop";
24
+ import * as sessionListCommand from "./commands/session-list";
21
25
  import * as snapshotCommand from "./commands/snapshot";
22
26
  import * as storageGetCommand from "./commands/storage-get";
23
27
  import * as storageSetCommand from "./commands/storage-set";
24
28
  import * as tabCloseCommand from "./commands/tab-close";
25
29
  import * as tabFocusCommand from "./commands/tab-focus";
26
30
  import * as tabOpenCommand from "./commands/tab-open";
31
+ import * as traceGetCommand from "./commands/trace-get";
27
32
  import * as uploadArmCommand from "./commands/upload-arm";
33
+ import * as waitElementCommand from "./commands/wait-element";
34
+ import * as waitTextCommand from "./commands/wait-text";
35
+ import * as waitUrlCommand from "./commands/wait-url";
28
36
  import { EXIT_CODES, runCli } from "./main";
29
37
 
30
38
  function createIoCapture() {
@@ -50,6 +58,9 @@ function createIoCapture() {
50
58
  };
51
59
  }
52
60
 
61
+ const ORIGINAL_BROWSERCTL_AUTH_TOKEN = process.env.BROWSERCTL_AUTH_TOKEN;
62
+ const ORIGINAL_BROWSERCTL_BROWSER = process.env.BROWSERCTL_BROWSER;
63
+
53
64
  type DispatchCase = {
54
65
  command: string;
55
66
  commandArgs: string[];
@@ -57,6 +68,13 @@ type DispatchCase = {
57
68
  handlerName: string;
58
69
  };
59
70
 
71
+ type MemoryDispatchCase = {
72
+ command: string;
73
+ commandArgs: string[];
74
+ expectedTool: string;
75
+ expectedArgs: Record<string, unknown>;
76
+ };
77
+
60
78
  const COMMAND_DISPATCH_CASES: DispatchCase[] = [
61
79
  {
62
80
  command: "a11y-snapshot",
@@ -136,6 +154,18 @@ const COMMAND_DISPATCH_CASES: DispatchCase[] = [
136
154
  moduleRef: frameSnapshotCommand as unknown as Record<string, unknown>,
137
155
  handlerName: "runFrameSnapshotCommand"
138
156
  },
157
+ {
158
+ command: "har-export",
159
+ commandArgs: ["target:1"],
160
+ moduleRef: harExportCommand as unknown as Record<string, unknown>,
161
+ handlerName: "runHarExportCommand"
162
+ },
163
+ {
164
+ command: "network-list",
165
+ commandArgs: ["target:1"],
166
+ moduleRef: networkListCommand as unknown as Record<string, unknown>,
167
+ handlerName: "runNetworkListCommand"
168
+ },
139
169
  {
140
170
  command: "network-wait-for",
141
171
  commandArgs: ["target:1", "/api", "1000"],
@@ -160,6 +190,18 @@ const COMMAND_DISPATCH_CASES: DispatchCase[] = [
160
190
  moduleRef: responseBodyCommand as unknown as Record<string, unknown>,
161
191
  handlerName: "runResponseBodyCommand"
162
192
  },
193
+ {
194
+ command: "session-drop",
195
+ commandArgs: ["tenant-a:job-1"],
196
+ moduleRef: sessionDropCommand as unknown as Record<string, unknown>,
197
+ handlerName: "runSessionDropCommand"
198
+ },
199
+ {
200
+ command: "session-list",
201
+ commandArgs: ["--tenant", "ops", "--limit", "10"],
202
+ moduleRef: sessionListCommand as unknown as Record<string, unknown>,
203
+ handlerName: "runSessionListCommand"
204
+ },
163
205
  {
164
206
  command: "snapshot",
165
207
  commandArgs: ["target:1"],
@@ -196,6 +238,30 @@ const COMMAND_DISPATCH_CASES: DispatchCase[] = [
196
238
  moduleRef: tabOpenCommand as unknown as Record<string, unknown>,
197
239
  handlerName: "runTabOpenCommand"
198
240
  },
241
+ {
242
+ command: "trace-get",
243
+ commandArgs: ["--limit", "10"],
244
+ moduleRef: traceGetCommand as unknown as Record<string, unknown>,
245
+ handlerName: "runTraceGetCommand"
246
+ },
247
+ {
248
+ command: "wait-element",
249
+ commandArgs: ["target:1", "#root", "1500"],
250
+ moduleRef: waitElementCommand as unknown as Record<string, unknown>,
251
+ handlerName: "runWaitElementCommand"
252
+ },
253
+ {
254
+ command: "wait-text",
255
+ commandArgs: ["target:1", "ready"],
256
+ moduleRef: waitTextCommand as unknown as Record<string, unknown>,
257
+ handlerName: "runWaitTextCommand"
258
+ },
259
+ {
260
+ command: "wait-url",
261
+ commandArgs: ["target:1", "/dashboard"],
262
+ moduleRef: waitUrlCommand as unknown as Record<string, unknown>,
263
+ handlerName: "runWaitUrlCommand"
264
+ },
199
265
  {
200
266
  command: "upload-arm",
201
267
  commandArgs: ["target:1", "upload.txt"],
@@ -204,8 +270,123 @@ const COMMAND_DISPATCH_CASES: DispatchCase[] = [
204
270
  }
205
271
  ];
206
272
 
273
+ const MEMORY_DISPATCH_CASES: MemoryDispatchCase[] = [
274
+ {
275
+ command: "memory-status",
276
+ commandArgs: [],
277
+ expectedTool: "browser.memory.status",
278
+ expectedArgs: {
279
+ sessionId: "cli:local"
280
+ }
281
+ },
282
+ {
283
+ command: "memory-mode-set",
284
+ commandArgs: ["ask"],
285
+ expectedTool: "browser.memory.mode.set",
286
+ expectedArgs: {
287
+ sessionId: "cli:local",
288
+ mode: "ask"
289
+ }
290
+ },
291
+ {
292
+ command: "memory-resolve",
293
+ commandArgs: ["forum.example", "open_profile"],
294
+ expectedTool: "browser.memory.resolve",
295
+ expectedArgs: {
296
+ sessionId: "cli:local",
297
+ domain: "forum.example",
298
+ profileId: "default",
299
+ intentKey: "open_profile"
300
+ }
301
+ },
302
+ {
303
+ command: "memory-upsert",
304
+ commandArgs: [
305
+ "forum.example",
306
+ "open_profile",
307
+ "--signals-json",
308
+ "[{\"kind\":\"urlPattern\",\"value\":\"/profile\"}]",
309
+ "--confidence",
310
+ "0.9",
311
+ "--confirmed"
312
+ ],
313
+ expectedTool: "browser.memory.upsert",
314
+ expectedArgs: {
315
+ sessionId: "cli:local",
316
+ domain: "forum.example",
317
+ profileId: "default",
318
+ intentKey: "open_profile",
319
+ signals: [{ kind: "urlPattern", value: "/profile" }],
320
+ confidence: 0.9,
321
+ confirmed: true
322
+ }
323
+ },
324
+ {
325
+ command: "memory-list",
326
+ commandArgs: ["--domain", "forum.example", "--profile-id", "managed", "--intent-key", "open_profile"],
327
+ expectedTool: "browser.memory.list",
328
+ expectedArgs: {
329
+ sessionId: "cli:local",
330
+ domain: "forum.example",
331
+ profileId: "managed",
332
+ intentKey: "open_profile"
333
+ }
334
+ },
335
+ {
336
+ command: "memory-inspect",
337
+ commandArgs: ["memory:1"],
338
+ expectedTool: "browser.memory.inspect",
339
+ expectedArgs: {
340
+ sessionId: "cli:local",
341
+ id: "memory:1"
342
+ }
343
+ },
344
+ {
345
+ command: "memory-delete",
346
+ commandArgs: ["memory:1"],
347
+ expectedTool: "browser.memory.delete",
348
+ expectedArgs: {
349
+ sessionId: "cli:local",
350
+ id: "memory:1"
351
+ }
352
+ },
353
+ {
354
+ command: "memory-purge",
355
+ commandArgs: [],
356
+ expectedTool: "browser.memory.purge",
357
+ expectedArgs: {
358
+ sessionId: "cli:local"
359
+ }
360
+ },
361
+ {
362
+ command: "memory-ttl-set",
363
+ commandArgs: ["7"],
364
+ expectedTool: "browser.memory.ttl.set",
365
+ expectedArgs: {
366
+ sessionId: "cli:local",
367
+ ttlDays: 7
368
+ }
369
+ }
370
+ ];
371
+
207
372
  afterEach(() => {
208
373
  vi.restoreAllMocks();
374
+ if (ORIGINAL_BROWSERCTL_AUTH_TOKEN === undefined) {
375
+ delete process.env.BROWSERCTL_AUTH_TOKEN;
376
+ } else {
377
+ process.env.BROWSERCTL_AUTH_TOKEN = ORIGINAL_BROWSERCTL_AUTH_TOKEN;
378
+ }
379
+
380
+ if (ORIGINAL_BROWSERCTL_BROWSER === undefined) {
381
+ delete process.env.BROWSERCTL_BROWSER;
382
+ } else {
383
+ process.env.BROWSERCTL_BROWSER = ORIGINAL_BROWSERCTL_BROWSER;
384
+ }
385
+ });
386
+
387
+ beforeEach(() => {
388
+ delete process.env.BROWSERCTL_AUTH_TOKEN;
389
+ delete process.env.BROWSERCTL_BROWSER;
209
390
  });
210
391
 
211
392
  describe("cli dispatch matrix", () => {
@@ -253,4 +434,28 @@ describe("cli dispatch matrix", () => {
253
434
  }
254
435
  });
255
436
  });
437
+
438
+ it.each(MEMORY_DISPATCH_CASES)(
439
+ "dispatches $command to $expectedTool",
440
+ async ({ command, commandArgs, expectedTool, expectedArgs }) => {
441
+ const callDaemonToolSpy = vi.spyOn(daemonClient, "callDaemonTool").mockResolvedValue({
442
+ ok: true
443
+ });
444
+ const { io, state } = createIoCapture();
445
+
446
+ const exitCode = await runCli([command, ...commandArgs, "--json"], io);
447
+
448
+ expect(exitCode).toBe(EXIT_CODES.OK);
449
+ expect(state.stderr).toBe("");
450
+ expect(callDaemonToolSpy).toHaveBeenCalledTimes(1);
451
+ expect(callDaemonToolSpy).toHaveBeenCalledWith(expectedTool, expectedArgs);
452
+ expect(JSON.parse(state.stdout)).toEqual({
453
+ ok: true,
454
+ command,
455
+ data: {
456
+ ok: true
457
+ }
458
+ });
459
+ }
460
+ );
256
461
  });
@@ -39,6 +39,22 @@ describe("cli", () => {
39
39
  expect(parseArgs(["status"]).command).toBe("status");
40
40
  });
41
41
 
42
+ it("treats removed template command as unknown", () => {
43
+ expect(parseArgs(["template-run"]).error).toBe("Unknown command: template-run");
44
+ });
45
+
46
+ it("parses memory-status command", () => {
47
+ expect(parseArgs(["memory-status"]).command).toBe("memory-status");
48
+ });
49
+
50
+ it("parses memory-mode-set command", () => {
51
+ expect(parseArgs(["memory-mode-set", "ask"]).command).toBe("memory-mode-set");
52
+ });
53
+
54
+ it("parses memory-resolve command", () => {
55
+ expect(parseArgs(["memory-resolve", "forum.example", "open_profile"]).command).toBe("memory-resolve");
56
+ });
57
+
42
58
  it("preserves unknown flags and '--' separator as command args", () => {
43
59
  expect(parseArgs(["status", "--profile", "default", "--", "--raw"])).toEqual({
44
60
  command: "status",
@@ -122,6 +138,9 @@ describe("cli", () => {
122
138
  expect(state.stderr).toBe("");
123
139
  expect(state.stdout).toContain("Usage:");
124
140
  expect(state.stdout).toContain("browserctl help [command]");
141
+ expect(state.stdout).toContain("session-list [--tenant <tenant>] [--limit <n>]");
142
+ expect(state.stdout).toContain("memory-status");
143
+ expect(state.stdout).toContain("memory-mode-set <mode>");
125
144
  });
126
145
 
127
146
  it("writes JSON help output when --json is provided", async () => {
@@ -165,6 +184,17 @@ describe("cli", () => {
165
184
  expect(state.stdout).toContain("browserctl tab-open <url>");
166
185
  });
167
186
 
187
+ it("shows payload option in act command help", async () => {
188
+ const { io, state } = createIoCapture();
189
+
190
+ const exitCode = await runCli(["help", "act"], io);
191
+
192
+ expect(exitCode).toBe(EXIT_CODES.OK);
193
+ expect(state.stderr).toBe("");
194
+ expect(state.stdout).toContain("browserctl act <actionType> <targetId> [--payload-json <json>]");
195
+ expect(state.stdout).toContain("--payload-json <json>");
196
+ });
197
+
168
198
  it("returns INVALID_ARGS for unknown help topic", async () => {
169
199
  const { io, state } = createIoCapture();
170
200
 
@@ -14,12 +14,25 @@ import { runDownloadWaitCommand } from "./commands/download-wait";
14
14
  import { runElementScreenshotCommand } from "./commands/element-screenshot";
15
15
  import { runFrameListCommand } from "./commands/frame-list";
16
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";
17
28
  import { runNetworkWaitForCommand } from "./commands/network-wait-for";
18
29
  import { parseCommandContext } from "./commands/common";
19
30
  import { runProfileListCommand } from "./commands/profile-list";
20
31
  import { runProfileUseCommand } from "./commands/profile-use";
21
32
  import { runResponseBodyCommand } from "./commands/response-body";
22
33
  import { runScreenshotCommand } from "./commands/screenshot";
34
+ import { runSessionDropCommand } from "./commands/session-drop";
35
+ import { runSessionListCommand } from "./commands/session-list";
23
36
  import { runSnapshotCommand } from "./commands/snapshot";
24
37
  import { runStatusCommand } from "./commands/status";
25
38
  import { runStorageGetCommand } from "./commands/storage-get";
@@ -28,7 +41,11 @@ import { runTabCloseCommand } from "./commands/tab-close";
28
41
  import { runTabFocusCommand } from "./commands/tab-focus";
29
42
  import { runTabOpenCommand } from "./commands/tab-open";
30
43
  import { runTabsCommand } from "./commands/tabs";
44
+ import { runTraceGetCommand } from "./commands/trace-get";
31
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";
32
49
  import { ensureDaemonRunning, getDaemonStatus, stopDaemon } from "./daemon-client";
33
50
 
34
51
  export const EXIT_CODES = {
@@ -51,6 +68,11 @@ export type CommandName =
51
68
  | "dom-query-all"
52
69
  | "element-screenshot"
53
70
  | "a11y-snapshot"
71
+ | "wait-element"
72
+ | "wait-text"
73
+ | "wait-url"
74
+ | "network-list"
75
+ | "har-export"
54
76
  | "network-wait-for"
55
77
  | "cookie-get"
56
78
  | "cookie-set"
@@ -66,6 +88,18 @@ export type CommandName =
66
88
  | "download-wait"
67
89
  | "console-list"
68
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"
69
103
  | "daemon-status"
70
104
  | "daemon-start"
71
105
  | "daemon-stop";
@@ -94,6 +128,11 @@ const VALID_COMMANDS: ReadonlySet<CommandName> = new Set([
94
128
  "dom-query-all",
95
129
  "element-screenshot",
96
130
  "a11y-snapshot",
131
+ "wait-element",
132
+ "wait-text",
133
+ "wait-url",
134
+ "network-list",
135
+ "har-export",
97
136
  "network-wait-for",
98
137
  "cookie-get",
99
138
  "cookie-set",
@@ -109,6 +148,18 @@ const VALID_COMMANDS: ReadonlySet<CommandName> = new Set([
109
148
  "download-wait",
110
149
  "console-list",
111
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",
112
163
  "daemon-status",
113
164
  "daemon-start",
114
165
  "daemon-stop"
@@ -172,6 +223,28 @@ const COMMAND_HELP: Readonly<Record<CommandName, CommandHelpEntry>> = {
172
223
  usage: "a11y-snapshot <targetId> [selector]",
173
224
  description: "Get accessibility tree snapshot."
174
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
+ },
175
248
  "network-wait-for": {
176
249
  usage:
177
250
  "network-wait-for <targetId> <urlPattern> [--method <METHOD>] [--status <CODE>] [--timeout-ms <ms>] [--poll-ms <ms>]",
@@ -206,7 +279,7 @@ const COMMAND_HELP: Readonly<Record<CommandName, CommandHelpEntry>> = {
206
279
  description: "Snapshot one frame subtree."
207
280
  },
208
281
  act: {
209
- usage: "act <actionType> <targetId>",
282
+ usage: "act <actionType> <targetId> [--payload-json <json>]",
210
283
  description: "Execute high-level action against a tab."
211
284
  },
212
285
  "upload-arm": {
@@ -226,13 +299,62 @@ const COMMAND_HELP: Readonly<Record<CommandName, CommandHelpEntry>> = {
226
299
  description: "Wait for next download and persist file."
227
300
  },
228
301
  "console-list": {
229
- usage: "console-list <targetId>",
302
+ usage: "console-list <targetId> [--type <type>] [--contains <text>] [--since <ISO>] [--limit <n>]",
230
303
  description: "List collected console events."
231
304
  },
232
305
  "response-body": {
233
306
  usage: "response-body <targetId> <requestId>",
234
307
  description: "Read response body for a captured request."
235
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
+ },
236
358
  "daemon-status": {
237
359
  usage: "daemon-status",
238
360
  description: "Inspect local daemon process status."
@@ -254,7 +376,21 @@ const COMMAND_GROUPS: ReadonlyArray<{ title: string; commands: readonly CommandN
254
376
  },
255
377
  {
256
378
  title: "Driver/session",
257
- commands: ["status", "profile-list", "profile-use", "tabs"]
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
+ ]
258
394
  },
259
395
  {
260
396
  title: "Tab/page",
@@ -267,6 +403,11 @@ const COMMAND_GROUPS: ReadonlyArray<{ title: string; commands: readonly CommandN
267
403
  "dom-query-all",
268
404
  "element-screenshot",
269
405
  "a11y-snapshot",
406
+ "wait-element",
407
+ "wait-text",
408
+ "wait-url",
409
+ "network-list",
410
+ "har-export",
270
411
  "network-wait-for",
271
412
  "cookie-get",
272
413
  "cookie-set",
@@ -276,7 +417,8 @@ const COMMAND_GROUPS: ReadonlyArray<{ title: string; commands: readonly CommandN
276
417
  "frame-list",
277
418
  "frame-snapshot",
278
419
  "console-list",
279
- "response-body"
420
+ "response-body",
421
+ "trace-get"
280
422
  ]
281
423
  },
282
424
  {
@@ -346,6 +488,72 @@ function formatCommandHelp(command: CommandName): string {
346
488
  lines.push(" --session <id> Session namespace (default: cli:local).");
347
489
  lines.push(" --profile <driverKey> Driver override.");
348
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
+ }
349
557
  } else if (command === "daemon-start") {
350
558
  lines.push("");
351
559
  lines.push("Command options:");
@@ -583,6 +791,16 @@ async function executeCommand(command: CommandName, commandArgs: string[]): Prom
583
791
  return await runElementScreenshotCommand(commandArgs);
584
792
  case "a11y-snapshot":
585
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);
586
804
  case "network-wait-for":
587
805
  return await runNetworkWaitForCommand(commandArgs);
588
806
  case "cookie-get":
@@ -613,6 +831,30 @@ async function executeCommand(command: CommandName, commandArgs: string[]): Prom
613
831
  return await runConsoleListCommand(commandArgs);
614
832
  case "response-body":
615
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);
616
858
  case "daemon-status": {
617
859
  const context = parseCommandContext(commandArgs);
618
860
  return await getDaemonStatus({