@flrande/browserctl 0.5.0 → 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 -57
  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 -253
  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 -1436
  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,103 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
-
3
- import { stopDaemon } from "./daemon-client";
4
- import { EXIT_CODES, runCli } from "./main";
5
- import { reserveLoopbackPort } from "./test-port";
6
-
7
- const TEST_SESSION_ID = "session:e2e";
8
- let testDaemonPort = 0;
9
-
10
- function createIoCapture() {
11
- const state = {
12
- stdout: "",
13
- stderr: ""
14
- };
15
-
16
- return {
17
- state,
18
- io: {
19
- stdout: {
20
- write(content: string) {
21
- state.stdout += content;
22
- }
23
- },
24
- stderr: {
25
- write(content: string) {
26
- state.stderr += content;
27
- }
28
- }
29
- }
30
- };
31
- }
32
-
33
- function parseJsonLine(state: { stdout: string }): Record<string, unknown> {
34
- return JSON.parse(state.stdout.trim()) as Record<string, unknown>;
35
- }
36
-
37
- beforeEach(async () => {
38
- testDaemonPort = await reserveLoopbackPort();
39
- process.env.BROWSERCTL_DAEMON_PORT = String(testDaemonPort);
40
- process.env.BROWSERD_MANAGED_LOCAL_ENABLED = "false";
41
- process.env.BROWSERD_DEFAULT_DRIVER = "managed";
42
- process.env.BROWSERD_CHROME_RELAY_MODE = "cdp";
43
- await stopDaemon(testDaemonPort);
44
- });
45
-
46
- afterEach(async () => {
47
- await stopDaemon(testDaemonPort);
48
- delete process.env.BROWSERCTL_DAEMON_PORT;
49
- delete process.env.BROWSERD_MANAGED_LOCAL_ENABLED;
50
- delete process.env.BROWSERD_DEFAULT_DRIVER;
51
- delete process.env.BROWSERD_CHROME_RELAY_MODE;
52
- });
53
-
54
- describe("browserctl e2e", () => {
55
- it("reuses daemon state across multiple commands in the same session", async () => {
56
- const startCapture = createIoCapture();
57
- const startExitCode = await runCli(["daemon-start", "--json"], startCapture.io);
58
-
59
- expect(startExitCode).toBe(EXIT_CODES.OK);
60
- const startPayload = parseJsonLine(startCapture.state);
61
- expect(startPayload.ok).toBe(true);
62
- const startData = startPayload.data as Record<string, unknown>;
63
- expect(startData.running).toBe(true);
64
- expect(startData.port).toBe(testDaemonPort);
65
-
66
- const openCapture = createIoCapture();
67
- const openExitCode = await runCli(
68
- ["tab-open", "--session", TEST_SESSION_ID, "https://example.com", "--json"],
69
- openCapture.io
70
- );
71
-
72
- expect(openExitCode).toBe(EXIT_CODES.OK);
73
- const openPayload = parseJsonLine(openCapture.state);
74
- expect(openPayload.ok).toBe(true);
75
- const openData = openPayload.data as Record<string, unknown>;
76
- expect(openData.driver).toBe("managed");
77
- const targetId = openData.targetId;
78
- expect(typeof targetId).toBe("string");
79
-
80
- const listCapture = createIoCapture();
81
- const listExitCode = await runCli(["tabs", "--session", TEST_SESSION_ID, "--json"], listCapture.io);
82
-
83
- expect(listExitCode).toBe(EXIT_CODES.OK);
84
- const listPayload = parseJsonLine(listCapture.state);
85
- expect(listPayload.ok).toBe(true);
86
- const listData = listPayload.data as { tabs: string[] };
87
- expect(listData.tabs).toContain(targetId);
88
-
89
- const consoleCapture = createIoCapture();
90
- const consoleExitCode = await runCli(
91
- ["console-list", "--session", TEST_SESSION_ID, String(targetId), "--json"],
92
- consoleCapture.io
93
- );
94
- expect(consoleExitCode).toBe(EXIT_CODES.OK);
95
- const consolePayload = parseJsonLine(consoleCapture.state);
96
- expect(consolePayload.ok).toBe(true);
97
- expect(consolePayload.data).toMatchObject({
98
- driver: "managed",
99
- targetId,
100
- entries: []
101
- });
102
- }, 20_000);
103
- });
@@ -1,461 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- import * as a11ySnapshotCommand from "./commands/a11y-snapshot";
4
- import * as consoleListCommand from "./commands/console-list";
5
- import * as cookieClearCommand from "./commands/cookie-clear";
6
- import * as cookieGetCommand from "./commands/cookie-get";
7
- import * as cookieSetCommand from "./commands/cookie-set";
8
- import * as daemonClient from "./daemon-client";
9
- import * as dialogArmCommand from "./commands/dialog-arm";
10
- import * as domQueryAllCommand from "./commands/dom-query-all";
11
- import * as domQueryCommand from "./commands/dom-query";
12
- import * as downloadTriggerCommand from "./commands/download-trigger";
13
- import * as downloadWaitCommand from "./commands/download-wait";
14
- import * as elementScreenshotCommand from "./commands/element-screenshot";
15
- import * as frameListCommand from "./commands/frame-list";
16
- import * as frameSnapshotCommand from "./commands/frame-snapshot";
17
- import * as harExportCommand from "./commands/har-export";
18
- import * as networkListCommand from "./commands/network-list";
19
- import * as networkWaitForCommand from "./commands/network-wait-for";
20
- import * as profileListCommand from "./commands/profile-list";
21
- import * as profileUseCommand from "./commands/profile-use";
22
- import * as responseBodyCommand from "./commands/response-body";
23
- import * as sessionDropCommand from "./commands/session-drop";
24
- import * as sessionListCommand from "./commands/session-list";
25
- import * as snapshotCommand from "./commands/snapshot";
26
- import * as storageGetCommand from "./commands/storage-get";
27
- import * as storageSetCommand from "./commands/storage-set";
28
- import * as tabCloseCommand from "./commands/tab-close";
29
- import * as tabFocusCommand from "./commands/tab-focus";
30
- import * as tabOpenCommand from "./commands/tab-open";
31
- import * as traceGetCommand from "./commands/trace-get";
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";
36
- import { EXIT_CODES, runCli } from "./main";
37
-
38
- function createIoCapture() {
39
- const state = {
40
- stdout: "",
41
- stderr: ""
42
- };
43
-
44
- return {
45
- state,
46
- io: {
47
- stdout: {
48
- write(content: string) {
49
- state.stdout += content;
50
- }
51
- },
52
- stderr: {
53
- write(content: string) {
54
- state.stderr += content;
55
- }
56
- }
57
- }
58
- };
59
- }
60
-
61
- const ORIGINAL_BROWSERCTL_AUTH_TOKEN = process.env.BROWSERCTL_AUTH_TOKEN;
62
- const ORIGINAL_BROWSERCTL_BROWSER = process.env.BROWSERCTL_BROWSER;
63
-
64
- type DispatchCase = {
65
- command: string;
66
- commandArgs: string[];
67
- moduleRef: Record<string, unknown>;
68
- handlerName: string;
69
- };
70
-
71
- type MemoryDispatchCase = {
72
- command: string;
73
- commandArgs: string[];
74
- expectedTool: string;
75
- expectedArgs: Record<string, unknown>;
76
- };
77
-
78
- const COMMAND_DISPATCH_CASES: DispatchCase[] = [
79
- {
80
- command: "a11y-snapshot",
81
- commandArgs: ["target:1"],
82
- moduleRef: a11ySnapshotCommand as unknown as Record<string, unknown>,
83
- handlerName: "runA11ySnapshotCommand"
84
- },
85
- {
86
- command: "console-list",
87
- commandArgs: ["target:1"],
88
- moduleRef: consoleListCommand as unknown as Record<string, unknown>,
89
- handlerName: "runConsoleListCommand"
90
- },
91
- {
92
- command: "cookie-clear",
93
- commandArgs: ["target:1"],
94
- moduleRef: cookieClearCommand as unknown as Record<string, unknown>,
95
- handlerName: "runCookieClearCommand"
96
- },
97
- {
98
- command: "cookie-get",
99
- commandArgs: ["target:1"],
100
- moduleRef: cookieGetCommand as unknown as Record<string, unknown>,
101
- handlerName: "runCookieGetCommand"
102
- },
103
- {
104
- command: "cookie-set",
105
- commandArgs: ["target:1", "sid", "abc"],
106
- moduleRef: cookieSetCommand as unknown as Record<string, unknown>,
107
- handlerName: "runCookieSetCommand"
108
- },
109
- {
110
- command: "dialog-arm",
111
- commandArgs: ["target:1"],
112
- moduleRef: dialogArmCommand as unknown as Record<string, unknown>,
113
- handlerName: "runDialogArmCommand"
114
- },
115
- {
116
- command: "dom-query",
117
- commandArgs: ["target:1", "#root"],
118
- moduleRef: domQueryCommand as unknown as Record<string, unknown>,
119
- handlerName: "runDomQueryCommand"
120
- },
121
- {
122
- command: "dom-query-all",
123
- commandArgs: ["target:1", ".item"],
124
- moduleRef: domQueryAllCommand as unknown as Record<string, unknown>,
125
- handlerName: "runDomQueryAllCommand"
126
- },
127
- {
128
- command: "download-trigger",
129
- commandArgs: ["target:1"],
130
- moduleRef: downloadTriggerCommand as unknown as Record<string, unknown>,
131
- handlerName: "runDownloadTriggerCommand"
132
- },
133
- {
134
- command: "download-wait",
135
- commandArgs: ["target:1"],
136
- moduleRef: downloadWaitCommand as unknown as Record<string, unknown>,
137
- handlerName: "runDownloadWaitCommand"
138
- },
139
- {
140
- command: "element-screenshot",
141
- commandArgs: ["target:1", "#hero"],
142
- moduleRef: elementScreenshotCommand as unknown as Record<string, unknown>,
143
- handlerName: "runElementScreenshotCommand"
144
- },
145
- {
146
- command: "frame-list",
147
- commandArgs: ["target:1"],
148
- moduleRef: frameListCommand as unknown as Record<string, unknown>,
149
- handlerName: "runFrameListCommand"
150
- },
151
- {
152
- command: "frame-snapshot",
153
- commandArgs: ["target:1", "frame:0"],
154
- moduleRef: frameSnapshotCommand as unknown as Record<string, unknown>,
155
- handlerName: "runFrameSnapshotCommand"
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
- },
169
- {
170
- command: "network-wait-for",
171
- commandArgs: ["target:1", "/api", "1000"],
172
- moduleRef: networkWaitForCommand as unknown as Record<string, unknown>,
173
- handlerName: "runNetworkWaitForCommand"
174
- },
175
- {
176
- command: "profile-list",
177
- commandArgs: [],
178
- moduleRef: profileListCommand as unknown as Record<string, unknown>,
179
- handlerName: "runProfileListCommand"
180
- },
181
- {
182
- command: "profile-use",
183
- commandArgs: ["managed"],
184
- moduleRef: profileUseCommand as unknown as Record<string, unknown>,
185
- handlerName: "runProfileUseCommand"
186
- },
187
- {
188
- command: "response-body",
189
- commandArgs: ["target:1", "request:1"],
190
- moduleRef: responseBodyCommand as unknown as Record<string, unknown>,
191
- handlerName: "runResponseBodyCommand"
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
- },
205
- {
206
- command: "snapshot",
207
- commandArgs: ["target:1"],
208
- moduleRef: snapshotCommand as unknown as Record<string, unknown>,
209
- handlerName: "runSnapshotCommand"
210
- },
211
- {
212
- command: "storage-get",
213
- commandArgs: ["target:1", "local", "theme"],
214
- moduleRef: storageGetCommand as unknown as Record<string, unknown>,
215
- handlerName: "runStorageGetCommand"
216
- },
217
- {
218
- command: "storage-set",
219
- commandArgs: ["target:1", "local", "theme", "dark"],
220
- moduleRef: storageSetCommand as unknown as Record<string, unknown>,
221
- handlerName: "runStorageSetCommand"
222
- },
223
- {
224
- command: "tab-close",
225
- commandArgs: ["target:1"],
226
- moduleRef: tabCloseCommand as unknown as Record<string, unknown>,
227
- handlerName: "runTabCloseCommand"
228
- },
229
- {
230
- command: "tab-focus",
231
- commandArgs: ["target:1"],
232
- moduleRef: tabFocusCommand as unknown as Record<string, unknown>,
233
- handlerName: "runTabFocusCommand"
234
- },
235
- {
236
- command: "tab-open",
237
- commandArgs: ["https://example.com"],
238
- moduleRef: tabOpenCommand as unknown as Record<string, unknown>,
239
- handlerName: "runTabOpenCommand"
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
- },
265
- {
266
- command: "upload-arm",
267
- commandArgs: ["target:1", "upload.txt"],
268
- moduleRef: uploadArmCommand as unknown as Record<string, unknown>,
269
- handlerName: "runUploadArmCommand"
270
- }
271
- ];
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
-
372
- afterEach(() => {
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;
390
- });
391
-
392
- describe("cli dispatch matrix", () => {
393
- it.each(COMMAND_DISPATCH_CASES)(
394
- "dispatches $command to $handlerName",
395
- async ({ command, commandArgs, moduleRef, handlerName }) => {
396
- const handler = moduleRef[handlerName] as ((args: string[]) => Promise<unknown>) | undefined;
397
- if (typeof handler !== "function") {
398
- throw new Error(`Expected ${handlerName} to be a function.`);
399
- }
400
-
401
- const handlerSpy = vi.spyOn(moduleRef as Record<string, (...args: unknown[]) => unknown>, handlerName)
402
- .mockResolvedValue({ ok: true });
403
- const { io, state } = createIoCapture();
404
-
405
- const exitCode = await runCli([command, ...commandArgs], io);
406
-
407
- expect(exitCode).toBe(EXIT_CODES.OK);
408
- expect(state.stderr).toBe("");
409
- expect(state.stdout).toMatch(new RegExp(`^${command}: `));
410
- expect(handlerSpy).toHaveBeenCalledTimes(1);
411
- expect(handlerSpy).toHaveBeenCalledWith(commandArgs);
412
- }
413
- );
414
-
415
- it("dispatches daemon-stop through daemon client", async () => {
416
- const stopSpy = vi.spyOn(daemonClient, "stopDaemon").mockResolvedValue({
417
- stopped: true,
418
- port: 41337,
419
- pid: 12345
420
- });
421
- const { io, state } = createIoCapture();
422
-
423
- const exitCode = await runCli(["daemon-stop", "--json"], io);
424
-
425
- expect(exitCode).toBe(EXIT_CODES.OK);
426
- expect(stopSpy).toHaveBeenCalledTimes(1);
427
- expect(JSON.parse(state.stdout)).toEqual({
428
- ok: true,
429
- command: "daemon-stop",
430
- data: {
431
- stopped: true,
432
- port: 41337,
433
- pid: 12345
434
- }
435
- });
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
- );
461
- });