@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,183 +0,0 @@
1
- import { once } from "node:events";
2
- import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
3
- import type { AddressInfo } from "node:net";
4
-
5
- import { describe, expect, it } from "vitest";
6
-
7
- import { createManagedDriver } from "./index";
8
-
9
- describe("createManagedDriver", () => {
10
- it("returns status with managed kind", async () => {
11
- const driver = createManagedDriver();
12
- const status = await driver.status();
13
-
14
- expect(status.kind).toBe("managed");
15
- });
16
-
17
- it("openTab creates a target and listTabs reflects it", async () => {
18
- const driver = createManagedDriver();
19
-
20
- expect(await driver.listTabs()).toEqual([]);
21
-
22
- const firstTarget = await driver.openTab("https://example.com/a");
23
- const secondTarget = await driver.openTab("https://example.com/b");
24
-
25
- expect(firstTarget).not.toBe(secondTarget);
26
- expect(await driver.listTabs()).toEqual([firstTarget, secondTarget]);
27
- });
28
-
29
- it("closeTab removes the target from listTabs", async () => {
30
- const driver = createManagedDriver();
31
- const firstTarget = await driver.openTab("https://example.com/a");
32
- const secondTarget = await driver.openTab("https://example.com/b");
33
-
34
- await driver.closeTab(firstTarget);
35
-
36
- expect(await driver.listTabs()).toEqual([secondTarget]);
37
- });
38
-
39
- it("isolates tabs by profile", async () => {
40
- const driver = createManagedDriver();
41
- const alphaProfile = "profile:alpha";
42
- const betaProfile = "profile:beta";
43
-
44
- const alphaTarget = await driver.openTab("https://example.com/alpha", alphaProfile);
45
- const betaTarget = await driver.openTab("https://example.com/beta", betaProfile);
46
-
47
- expect(await driver.listTabs(alphaProfile)).toEqual([alphaTarget]);
48
- expect(await driver.listTabs(betaProfile)).toEqual([betaTarget]);
49
- expect(await driver.listTabs(alphaProfile)).not.toContain(betaTarget);
50
- expect(await driver.listTabs(betaProfile)).not.toContain(alphaTarget);
51
- });
52
-
53
- it("focusTab uses targetId within the selected profile", async () => {
54
- const driver = createManagedDriver();
55
- const profile = "profile:alpha";
56
- const target = await driver.openTab("https://example.com/alpha", profile);
57
-
58
- await expect(driver.focusTab(target, profile)).resolves.toBeUndefined();
59
- await expect(driver.focusTab(target, "profile:beta")).rejects.toThrowError(
60
- `Unknown targetId: ${target} (profile: profile:beta)`
61
- );
62
- });
63
-
64
- it("tracks local URL/html state for navigate/login/download flows", async () => {
65
- function sendHtml(response: ServerResponse, route: string): void {
66
- response.statusCode = 200;
67
- response.setHeader("content-type", "text/html; charset=utf-8");
68
- response.end(`<!doctype html><h1>Mock</h1><p>Route: ${route}</p>`);
69
- }
70
-
71
- const server = createServer((request: IncomingMessage, response: ServerResponse) => {
72
- if (request.url === "/app" && request.method === "GET") {
73
- sendHtml(response, "/app");
74
- return;
75
- }
76
-
77
- if (request.url === "/app/cart" && request.method === "GET") {
78
- sendHtml(response, "/app/cart");
79
- return;
80
- }
81
-
82
- if (request.url === "/api/login" && request.method === "POST") {
83
- response.statusCode = 200;
84
- response.setHeader("set-cookie", "mock_session=test; Path=/; HttpOnly");
85
- response.setHeader("content-type", "application/json; charset=utf-8");
86
- response.end(JSON.stringify({ ok: true }));
87
- return;
88
- }
89
-
90
- if (request.url === "/download/orders.csv" && request.method === "GET") {
91
- response.statusCode = 200;
92
- response.setHeader("content-type", "text/csv; charset=utf-8");
93
- response.end("order_id,total\n1,42\n");
94
- return;
95
- }
96
-
97
- response.statusCode = 404;
98
- response.end("not-found");
99
- });
100
-
101
- server.listen(0, "127.0.0.1");
102
- await once(server, "listening");
103
-
104
- const address = server.address();
105
- if (address === null || typeof address === "string") {
106
- throw new Error("Expected server address info to be available.");
107
- }
108
-
109
- const baseUrl = `http://127.0.0.1:${(address as AddressInfo).port}`;
110
- const driver = createManagedDriver();
111
-
112
- try {
113
- const targetId = await driver.openTab(`${baseUrl}/app`);
114
- const initialSnapshot = await driver.snapshot(targetId);
115
- expect(initialSnapshot).toMatchObject({
116
- hasTarget: true,
117
- url: `${baseUrl}/app`
118
- });
119
- expect(initialSnapshot).toMatchObject({
120
- html: expect.stringContaining("Route: /app")
121
- });
122
-
123
- const loginResult = await driver.act(
124
- {
125
- type: "login",
126
- payload: { path: "/api/login" }
127
- },
128
- targetId
129
- );
130
- expect(loginResult).toMatchObject({
131
- actionType: "login",
132
- targetKnown: true,
133
- ok: true,
134
- executed: true
135
- });
136
-
137
- const navigateResult = await driver.act(
138
- {
139
- type: "navigate",
140
- payload: { path: "/app/cart" }
141
- },
142
- targetId
143
- );
144
- expect(navigateResult).toMatchObject({
145
- actionType: "navigate",
146
- targetKnown: true,
147
- ok: true,
148
- executed: true
149
- });
150
-
151
- const postNavigateSnapshot = await driver.snapshot(targetId);
152
- expect(postNavigateSnapshot).toMatchObject({
153
- hasTarget: true,
154
- url: `${baseUrl}/app/cart`
155
- });
156
- expect(postNavigateSnapshot).toMatchObject({
157
- html: expect.stringContaining("Route: /app/cart")
158
- });
159
-
160
- const prepareDownloadResult = await driver.act(
161
- {
162
- type: "prepare-download",
163
- payload: { path: "/download/orders.csv" }
164
- },
165
- targetId
166
- );
167
- expect(prepareDownloadResult).toMatchObject({
168
- actionType: "prepare-download",
169
- targetKnown: true,
170
- ok: true,
171
- executed: true
172
- });
173
-
174
- await expect(
175
- driver.waitDownload(targetId, undefined, "C:\\downloads\\orders.csv")
176
- ).resolves.toEqual({
177
- path: "C:\\downloads\\orders.csv"
178
- });
179
- } finally {
180
- server.close();
181
- }
182
- });
183
- });
@@ -1,341 +0,0 @@
1
- import type { BrowserDriver } from "../../core/src/driver";
2
- import type { ProfileId, TargetId } from "../../core/src/types";
3
-
4
- export type ManagedDriverStatus = {
5
- kind: "managed";
6
- connected: true;
7
- };
8
-
9
- const DEFAULT_PROFILE_ID: ProfileId = "profile:managed:default";
10
-
11
- type ManagedTab = {
12
- url: string;
13
- html: string;
14
- cookieHeader?: string;
15
- pendingDownloadUrl?: string;
16
- };
17
-
18
- type ManagedProfileState = {
19
- nextTargetNumber: number;
20
- tabs: Map<TargetId, ManagedTab>;
21
- tabOrder: TargetId[];
22
- focusedTargetId?: TargetId;
23
- };
24
-
25
- function resolveProfileId(profile?: ProfileId): ProfileId {
26
- return profile ?? DEFAULT_PROFILE_ID;
27
- }
28
-
29
- function createTargetId(profileId: ProfileId, targetNumber: number): TargetId {
30
- return `target:managed:${profileId}:${targetNumber}`;
31
- }
32
-
33
- function createProfileState(): ManagedProfileState {
34
- return {
35
- nextTargetNumber: 1,
36
- tabs: new Map<TargetId, ManagedTab>(),
37
- tabOrder: []
38
- };
39
- }
40
-
41
- function isObjectRecord(value: unknown): value is Record<string, unknown> {
42
- return typeof value === "object" && value !== null && !Array.isArray(value);
43
- }
44
-
45
- function parseCookieHeader(response: Response): string | undefined {
46
- const rawSetCookie = response.headers.get("set-cookie");
47
- if (rawSetCookie === null) {
48
- return undefined;
49
- }
50
-
51
- const [cookieSegment] = rawSetCookie.split(";");
52
- const cookieHeader = cookieSegment.trim();
53
- return cookieHeader.length > 0 ? cookieHeader : undefined;
54
- }
55
-
56
- function readPayloadString(
57
- payload: Record<string, unknown>,
58
- fieldName: string
59
- ): string | undefined {
60
- const value = payload[fieldName];
61
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
62
- }
63
-
64
- async function requestTab(
65
- tab: ManagedTab,
66
- url: string,
67
- init: RequestInit
68
- ): Promise<void> {
69
- const headers = new Headers(init.headers ?? {});
70
- if (tab.cookieHeader !== undefined && !headers.has("cookie")) {
71
- headers.set("cookie", tab.cookieHeader);
72
- }
73
-
74
- const response = await fetch(url, {
75
- ...init,
76
- headers
77
- });
78
-
79
- const cookieHeader = parseCookieHeader(response);
80
- if (cookieHeader !== undefined) {
81
- tab.cookieHeader = cookieHeader;
82
- }
83
-
84
- const body = await response.text();
85
- if (!response.ok) {
86
- throw new Error(`${init.method ?? "GET"} ${url} failed with ${response.status}`);
87
- }
88
-
89
- tab.url = response.url;
90
- tab.html = body;
91
- }
92
-
93
- export function createManagedDriver(): BrowserDriver<ManagedDriverStatus> {
94
- const profileStates = new Map<ProfileId, ManagedProfileState>();
95
-
96
- function getOrCreateProfileState(profileId: ProfileId): ManagedProfileState {
97
- const existingState = profileStates.get(profileId);
98
- if (existingState !== undefined) {
99
- return existingState;
100
- }
101
-
102
- const createdState = createProfileState();
103
- profileStates.set(profileId, createdState);
104
- return createdState;
105
- }
106
-
107
- function requireTargetInProfile(profileId: ProfileId, targetId: TargetId): ManagedProfileState {
108
- const profileState = profileStates.get(profileId);
109
- if (profileState === undefined || !profileState.tabs.has(targetId)) {
110
- throw new Error(`Unknown targetId: ${targetId} (profile: ${profileId})`);
111
- }
112
-
113
- return profileState;
114
- }
115
-
116
- return {
117
- status: async () => ({ kind: "managed", connected: true }),
118
- listProfiles: async () => {
119
- const knownProfiles = new Set<ProfileId>([DEFAULT_PROFILE_ID]);
120
- for (const profileId of profileStates.keys()) {
121
- knownProfiles.add(profileId);
122
- }
123
-
124
- return Array.from(knownProfiles).sort();
125
- },
126
- listTabs: async (profile) => {
127
- const profileId = resolveProfileId(profile);
128
- const profileState = profileStates.get(profileId);
129
- return profileState === undefined ? [] : [...profileState.tabOrder];
130
- },
131
- openTab: async (url, profile) => {
132
- const profileId = resolveProfileId(profile);
133
- const profileState = getOrCreateProfileState(profileId);
134
- const targetId = createTargetId(profileId, profileState.nextTargetNumber);
135
-
136
- profileState.nextTargetNumber += 1;
137
- const tab: ManagedTab = {
138
- url,
139
- html: ""
140
- };
141
- profileState.tabs.set(targetId, tab);
142
- profileState.tabOrder.push(targetId);
143
- if (profileState.focusedTargetId === undefined) {
144
- profileState.focusedTargetId = targetId;
145
- }
146
-
147
- try {
148
- await requestTab(tab, url, { method: "GET" });
149
- } catch {
150
- // Keep managed driver deterministic even when URL is unreachable.
151
- }
152
-
153
- return targetId;
154
- },
155
- focusTab: async (targetId, profile) => {
156
- const profileId = resolveProfileId(profile);
157
- const profileState = requireTargetInProfile(profileId, targetId);
158
- profileState.focusedTargetId = targetId;
159
- },
160
- closeTab: async (targetId, profile) => {
161
- const profileId = resolveProfileId(profile);
162
- const profileState = requireTargetInProfile(profileId, targetId);
163
-
164
- profileState.tabs.delete(targetId);
165
- profileState.tabOrder = profileState.tabOrder.filter((existingTargetId) => existingTargetId !== targetId);
166
- if (profileState.focusedTargetId === targetId) {
167
- profileState.focusedTargetId = profileState.tabOrder[0];
168
- }
169
- },
170
- snapshot: async (targetId, profile) => {
171
- const profileId = resolveProfileId(profile);
172
- const tab = profileStates.get(profileId)?.tabs.get(targetId);
173
- if (tab === undefined) {
174
- return {
175
- kind: "managed",
176
- profile: profileId,
177
- targetId,
178
- hasTarget: false
179
- };
180
- }
181
-
182
- return {
183
- kind: "managed",
184
- profile: profileId,
185
- targetId,
186
- hasTarget: true,
187
- url: tab.url,
188
- html: tab.html
189
- };
190
- },
191
- act: async (action, targetId, profile) => {
192
- const profileId = resolveProfileId(profile);
193
- const tab = profileStates.get(profileId)?.tabs.get(targetId);
194
- if (tab === undefined) {
195
- return {
196
- actionType: action.type,
197
- profile: profileId,
198
- targetId,
199
- targetKnown: false,
200
- ok: false,
201
- executed: false
202
- };
203
- }
204
-
205
- const payload = isObjectRecord(action.payload) ? action.payload : {};
206
- if (action.type === "navigate" || action.type === "goto") {
207
- const nextUrlValue = readPayloadString(payload, "url") ?? readPayloadString(payload, "path");
208
- if (nextUrlValue === undefined) {
209
- return {
210
- actionType: action.type,
211
- profile: profileId,
212
- targetId,
213
- targetKnown: true,
214
- ok: false,
215
- executed: true,
216
- error: "action.payload.url or action.payload.path is required for navigate action."
217
- };
218
- }
219
-
220
- const nextUrl = new URL(nextUrlValue, tab.url).toString();
221
- try {
222
- await requestTab(tab, nextUrl, { method: "GET" });
223
- } catch (error) {
224
- return {
225
- actionType: action.type,
226
- profile: profileId,
227
- targetId,
228
- targetKnown: true,
229
- ok: false,
230
- executed: true,
231
- error: error instanceof Error ? error.message : String(error)
232
- };
233
- }
234
-
235
- return {
236
- actionType: action.type,
237
- profile: profileId,
238
- targetId,
239
- targetKnown: true,
240
- ok: true,
241
- executed: true,
242
- data: {
243
- url: tab.url
244
- }
245
- };
246
- }
247
-
248
- if (action.type === "login") {
249
- const loginPath = readPayloadString(payload, "path") ?? "/api/login";
250
- const loginUrl = new URL(loginPath, tab.url).toString();
251
- try {
252
- await requestTab(tab, loginUrl, { method: "POST" });
253
- } catch (error) {
254
- return {
255
- actionType: action.type,
256
- profile: profileId,
257
- targetId,
258
- targetKnown: true,
259
- ok: false,
260
- executed: true,
261
- error: error instanceof Error ? error.message : String(error)
262
- };
263
- }
264
-
265
- return {
266
- actionType: action.type,
267
- profile: profileId,
268
- targetId,
269
- targetKnown: true,
270
- ok: true,
271
- executed: true
272
- };
273
- }
274
-
275
- if (action.type === "prepare-download") {
276
- const downloadPath = readPayloadString(payload, "path");
277
- if (downloadPath === undefined) {
278
- return {
279
- actionType: action.type,
280
- profile: profileId,
281
- targetId,
282
- targetKnown: true,
283
- ok: false,
284
- executed: true,
285
- error: "action.payload.path is required for prepare-download action."
286
- };
287
- }
288
-
289
- tab.pendingDownloadUrl = new URL(downloadPath, tab.url).toString();
290
- return {
291
- actionType: action.type,
292
- profile: profileId,
293
- targetId,
294
- targetKnown: true,
295
- ok: true,
296
- executed: true,
297
- data: {
298
- url: tab.pendingDownloadUrl
299
- }
300
- };
301
- }
302
-
303
- return {
304
- actionType: action.type,
305
- profile: profileId,
306
- targetId,
307
- targetKnown: true,
308
- ok: true,
309
- executed: false
310
- };
311
- },
312
- armUpload: async () => {},
313
- armDialog: async () => {},
314
- waitDownload: async (targetId, profile, path) => {
315
- const profileId = resolveProfileId(profile);
316
- const tab = profileStates.get(profileId)?.tabs.get(targetId);
317
- if (tab === undefined) {
318
- throw new Error(`Unknown targetId: ${targetId} (profile: ${profileId})`);
319
- }
320
-
321
- if (tab.pendingDownloadUrl !== undefined) {
322
- const headers = new Headers();
323
- if (tab.cookieHeader !== undefined) {
324
- headers.set("cookie", tab.cookieHeader);
325
- }
326
-
327
- try {
328
- await fetch(tab.pendingDownloadUrl, {
329
- method: "GET",
330
- headers
331
- });
332
- } finally {
333
- tab.pendingDownloadUrl = undefined;
334
- }
335
- }
336
-
337
- return { path: path ?? "managed-download.bin" };
338
- },
339
- triggerDownload: async () => {}
340
- };
341
- }