abelworkflow 1.2.3 → 1.3.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 (63) hide show
  1. package/README.md +26 -25
  2. package/extensions/gpt-responses-compat.ts +166 -27
  3. package/lib/cli/main.mjs +18 -86
  4. package/lib/cli/pi.mjs +43 -0
  5. package/lib/cli/prompts.mjs +3 -3
  6. package/lib/installer/assets.mjs +35 -31
  7. package/lib/installer/install.mjs +29 -9
  8. package/lib/installer/links.mjs +97 -84
  9. package/lib/paths.mjs +0 -2
  10. package/lib/providers/claude.mjs +14 -3
  11. package/lib/providers/codex.mjs +14 -9
  12. package/lib/providers/pi.mjs +61 -68
  13. package/lib/providers/skills.mjs +22 -22
  14. package/lib/providers/url.mjs +32 -1
  15. package/lib/templates/codex/agents/default.toml +16 -54
  16. package/lib/templates/codex/agents/explorer.toml +25 -52
  17. package/lib/templates/codex/agents/planner.toml +23 -77
  18. package/lib/templates/codex/agents/reviewer.toml +16 -62
  19. package/lib/templates/codex/agents/worker.toml +30 -64
  20. package/lib/templates/codex/config-base.toml +0 -6
  21. package/lib/templates/{workflow/gitignore.template → gitignore.template} +0 -3
  22. package/lib/tools/cli-installer.mjs +40 -12
  23. package/package.json +13 -18
  24. package/lib/templates/workflow/AGENTS.md +0 -50
  25. package/lib/templates/workflow/commands/abel-design.md +0 -175
  26. package/lib/templates/workflow/commands/abel-diagnose.md +0 -63
  27. package/lib/templates/workflow/commands/abel-implement.md +0 -170
  28. package/lib/templates/workflow/commands/abel-init.md +0 -25
  29. package/skills/dev-browser/SKILL.md +0 -281
  30. package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
  31. package/skills/dev-browser/dist/scripts/start.js +0 -90
  32. package/skills/dev-browser/dist/src/client.d.ts +0 -92
  33. package/skills/dev-browser/dist/src/client.js +0 -310
  34. package/skills/dev-browser/dist/src/entrypoint.d.ts +0 -29
  35. package/skills/dev-browser/dist/src/entrypoint.js +0 -113
  36. package/skills/dev-browser/dist/src/index.d.ts +0 -3
  37. package/skills/dev-browser/dist/src/index.js +0 -1
  38. package/skills/dev-browser/dist/src/page-api.d.ts +0 -24
  39. package/skills/dev-browser/dist/src/page-api.js +0 -103
  40. package/skills/dev-browser/dist/src/relay.d.ts +0 -26
  41. package/skills/dev-browser/dist/src/relay.js +0 -567
  42. package/skills/dev-browser/dist/src/runtime.d.ts +0 -34
  43. package/skills/dev-browser/dist/src/runtime.js +0 -44
  44. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -22
  45. package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -868
  46. package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -13
  47. package/skills/dev-browser/dist/src/snapshot/index.js +0 -13
  48. package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -12
  49. package/skills/dev-browser/dist/src/snapshot/inject.js +0 -12
  50. package/skills/dev-browser/dist/src/standalone.d.ts +0 -31
  51. package/skills/dev-browser/dist/src/standalone.js +0 -173
  52. package/skills/dev-browser/dist/src/startup.d.ts +0 -46
  53. package/skills/dev-browser/dist/src/startup.js +0 -77
  54. package/skills/dev-browser/dist/src/target-registry.d.ts +0 -28
  55. package/skills/dev-browser/dist/src/target-registry.js +0 -134
  56. package/skills/dev-browser/dist/src/types.d.ts +0 -26
  57. package/skills/dev-browser/dist/src/types.js +0 -1
  58. package/skills/dev-browser/package-lock.json +0 -1545
  59. package/skills/dev-browser/package.json +0 -35
  60. package/skills/dev-browser/references/scraping.md +0 -144
  61. package/skills/git-commit/SKILL.md +0 -124
  62. package/skills/time/SKILL.md +0 -119
  63. package/skills/time/scripts/time_cli.py +0 -143
@@ -1,310 +0,0 @@
1
- import { chromium, } from "playwright";
2
- import { getSnapshotScript } from "./snapshot/browser-script.js";
3
- const IGNORED_REQUEST_PATTERNS = [
4
- "doubleclick.net",
5
- "googlesyndication.com",
6
- "googletagmanager.com",
7
- "google-analytics.com",
8
- "facebook.net",
9
- "connect.facebook.net",
10
- "analytics",
11
- "/ads/",
12
- "tracking",
13
- "pixel",
14
- "hotjar.com",
15
- "clarity.ms",
16
- "mixpanel.com",
17
- "segment.com",
18
- "newrelic.com",
19
- "nr-data.net",
20
- "/tracker/",
21
- "/collector/",
22
- "/beacon/",
23
- "/telemetry/",
24
- "/log/",
25
- "/events/",
26
- "/track.",
27
- "/metrics/",
28
- ];
29
- const NON_CRITICAL_RESOURCE_TYPES = ["img", "image", "icon", "font"];
30
- /**
31
- * Wait for a page to finish loading using document.readyState and Playwright request events.
32
- *
33
- * Uses browser-use's approach of:
34
- * - Checking document.readyState for 'complete'
35
- * - Monitoring pending network requests via Playwright
36
- * - Filtering out ads, tracking, and non-critical resources
37
- * - Graceful timeout handling (continues even if timeout reached)
38
- */
39
- export async function waitForPageLoad(page, options = {}) {
40
- const { timeout = 10000, pollInterval = 50, minimumWait = 100, waitForNetworkIdle = true, } = options;
41
- const startTime = Date.now();
42
- let lastState = null;
43
- const requests = new Map();
44
- const trackRequest = (request, startedAt = Date.now()) => {
45
- requests.set(request, {
46
- url: request.url(),
47
- resourceType: request.resourceType(),
48
- startedAt,
49
- });
50
- };
51
- const onRequest = (request) => trackRequest(request);
52
- const onRequestDone = (request) => requests.delete(request);
53
- page.on("request", onRequest);
54
- page.on("requestfinished", onRequestDone);
55
- page.on("requestfailed", onRequestDone);
56
- try {
57
- for (const request of await page.requests().catch(() => [])) {
58
- const timing = request.timing();
59
- if (request.failure() === null && timing.responseEnd === -1) {
60
- trackRequest(request, timing.startTime);
61
- }
62
- }
63
- // Wait minimum time first
64
- if (minimumWait > 0) {
65
- await new Promise((resolve) => setTimeout(resolve, minimumWait));
66
- }
67
- // Poll until ready or timeout
68
- while (Date.now() - startTime < timeout) {
69
- try {
70
- lastState = await getPageLoadState(page, requests);
71
- // Check if document is complete
72
- const documentReady = lastState.documentReadyState === "complete";
73
- // Check if network is idle (no pending critical requests)
74
- const networkIdle = !waitForNetworkIdle || lastState.pendingRequests.length === 0;
75
- if (documentReady && networkIdle) {
76
- return {
77
- success: true,
78
- readyState: lastState.documentReadyState,
79
- pendingRequests: lastState.pendingRequests.length,
80
- waitTimeMs: Date.now() - startTime,
81
- timedOut: false,
82
- };
83
- }
84
- }
85
- catch {
86
- // Page may be navigating, continue polling
87
- }
88
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
89
- }
90
- // Timeout reached - return current state
91
- return {
92
- success: false,
93
- readyState: lastState?.documentReadyState ?? "unknown",
94
- pendingRequests: getPendingRequests(requests).length,
95
- waitTimeMs: Date.now() - startTime,
96
- timedOut: true,
97
- };
98
- }
99
- finally {
100
- page.off("request", onRequest);
101
- page.off("requestfinished", onRequestDone);
102
- page.off("requestfailed", onRequestDone);
103
- }
104
- }
105
- async function getPageLoadState(page, requests) {
106
- const documentReadyState = await page.evaluate(() => {
107
- // Access browser globals via globalThis for TypeScript compatibility
108
- /* eslint-disable @typescript-eslint/no-explicit-any */
109
- const g = globalThis;
110
- /* eslint-enable @typescript-eslint/no-explicit-any */
111
- return g.document.readyState;
112
- });
113
- return { documentReadyState, pendingRequests: getPendingRequests(requests) };
114
- }
115
- function getPendingRequests(requests) {
116
- const now = Date.now();
117
- return [...requests.values()].flatMap(({ url, resourceType, startedAt }) => {
118
- const loadingDurationMs = now - startedAt;
119
- if (shouldIgnoreRequest(url, resourceType, loadingDurationMs))
120
- return [];
121
- return [{ url, resourceType, loadingDurationMs }];
122
- });
123
- }
124
- function shouldIgnoreRequest(url, resourceType, loadingDurationMs) {
125
- if (IGNORED_REQUEST_PATTERNS.some((pattern) => url.includes(pattern)))
126
- return true;
127
- if (url.startsWith("data:") || url.length > 500)
128
- return true;
129
- if (NON_CRITICAL_RESOURCE_TYPES.includes(resourceType) && loadingDurationMs > 3000) {
130
- return true;
131
- }
132
- return /\.(jpg|jpeg|png|gif|webp|svg|ico)(\?|$)/i.test(url) && loadingDurationMs > 3000;
133
- }
134
- export async function findPageByTargetId(browser, targetId) {
135
- for (const context of browser.contexts()) {
136
- for (const page of context.pages()) {
137
- let session;
138
- try {
139
- session = await context.newCDPSession(page);
140
- const { targetInfo } = await session.send("Target.getTargetInfo");
141
- if (targetInfo.targetId === targetId)
142
- return page;
143
- }
144
- catch (error) {
145
- const message = error instanceof Error ? error.message : String(error);
146
- if (!message.includes("Target closed") && !message.includes("Session closed")) {
147
- console.warn(`Unexpected error checking page target: ${message}`);
148
- }
149
- }
150
- finally {
151
- await session?.detach().catch(() => undefined);
152
- }
153
- }
154
- }
155
- return null;
156
- }
157
- export async function connect(serverUrl = "http://localhost:9222") {
158
- const normalizedServerUrl = serverUrl.replace(/\/+$/, "");
159
- let browser = null;
160
- let connectingPromise = null;
161
- async function ensureConnected() {
162
- // Return existing connection if still active
163
- if (browser && browser.isConnected()) {
164
- return browser;
165
- }
166
- // If already connecting, wait for that connection (prevents race condition)
167
- if (connectingPromise) {
168
- return connectingPromise;
169
- }
170
- // Start new connection with mutex
171
- connectingPromise = (async () => {
172
- try {
173
- const { wsEndpoint } = await fetchServerInfo(normalizedServerUrl);
174
- // Connect to the browser via CDP
175
- browser = await chromium.connectOverCDP(wsEndpoint);
176
- return browser;
177
- }
178
- finally {
179
- connectingPromise = null;
180
- }
181
- })();
182
- return connectingPromise;
183
- }
184
- // Helper to get a page by name (used by multiple methods)
185
- async function getPage(name, options) {
186
- // Request the page from server (creates if doesn't exist)
187
- const res = await fetch(`${normalizedServerUrl}/pages`, {
188
- method: "POST",
189
- headers: { "Content-Type": "application/json" },
190
- body: JSON.stringify({ name, viewport: options?.viewport }),
191
- });
192
- if (!res.ok) {
193
- throw new Error(`Failed to get page: ${await res.text()}`);
194
- }
195
- const pageInfo = (await res.json());
196
- const { targetId } = pageInfo;
197
- // Connect to browser
198
- const b = await ensureConnected();
199
- const page = await waitForExactPage(b, targetId);
200
- if (!page) {
201
- throw new Error(`Page "${name}" not found in browser contexts`);
202
- }
203
- return page;
204
- }
205
- return {
206
- page: getPage,
207
- async list() {
208
- const res = await fetch(`${normalizedServerUrl}/pages`);
209
- const data = (await res.json());
210
- return data.pages;
211
- },
212
- async close(name) {
213
- const res = await fetch(`${normalizedServerUrl}/pages/${encodeURIComponent(name)}`, {
214
- method: "DELETE",
215
- });
216
- if (!res.ok) {
217
- throw new Error(`Failed to close page: ${await res.text()}`);
218
- }
219
- },
220
- async disconnect() {
221
- // Just disconnect the CDP connection - pages persist on server
222
- if (browser) {
223
- await browser.close();
224
- browser = null;
225
- }
226
- },
227
- async getAISnapshot(name) {
228
- // Get the page
229
- const page = await getPage(name);
230
- // Inject the snapshot script and call getAISnapshot
231
- const snapshotScript = getSnapshotScript();
232
- const snapshot = await page.evaluate((script) => {
233
- // Inject script if not already present
234
- // Note: page.evaluate runs in browser context where window exists
235
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
- const w = globalThis;
237
- if (!w.__devBrowser_getAISnapshot) {
238
- // eslint-disable-next-line no-eval
239
- eval(script);
240
- }
241
- return w.__devBrowser_getAISnapshot();
242
- }, snapshotScript);
243
- return snapshot;
244
- },
245
- async selectSnapshotRef(name, ref) {
246
- // Get the page
247
- const page = await getPage(name);
248
- // Find the element using the stored refs
249
- const elementHandle = await page.evaluateHandle((refId) => {
250
- // Note: page.evaluateHandle runs in browser context where globalThis is the window
251
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
252
- const w = globalThis;
253
- const refs = w.__devBrowserRefs;
254
- if (!refs) {
255
- throw new Error("No snapshot refs found. Call getAISnapshot first.");
256
- }
257
- const element = refs[refId];
258
- if (!element) {
259
- throw new Error(`Ref "${refId}" not found. Available refs: ${Object.keys(refs).join(", ")}`);
260
- }
261
- return element;
262
- }, ref);
263
- // Check if we got an element
264
- const element = elementHandle.asElement();
265
- if (!element) {
266
- await elementHandle.dispose();
267
- return null;
268
- }
269
- return element;
270
- },
271
- async getServerInfo() {
272
- return fetchServerInfo(normalizedServerUrl);
273
- },
274
- };
275
- }
276
- async function fetchServerInfo(serverUrl) {
277
- const res = await fetch(serverUrl);
278
- if (!res.ok) {
279
- throw new Error(`Server returned ${res.status}: ${await res.text()}`);
280
- }
281
- return parseServerInfo(await res.json());
282
- }
283
- function parseServerInfo(value) {
284
- const info = typeof value === "object" && value !== null
285
- ? value
286
- : {};
287
- const mode = info.mode;
288
- if (mode !== "standalone" && mode !== "extension") {
289
- throw new Error(`Invalid dev-browser server mode: expected "standalone" or "extension", received ${JSON.stringify(mode)}`);
290
- }
291
- if (typeof info.wsEndpoint !== "string" || info.wsEndpoint.trim().length === 0) {
292
- throw new Error(`Invalid dev-browser wsEndpoint: expected a non-empty string, received ${JSON.stringify(info.wsEndpoint)}`);
293
- }
294
- return {
295
- wsEndpoint: info.wsEndpoint,
296
- mode,
297
- extensionConnected: info.extensionConnected,
298
- };
299
- }
300
- async function waitForExactPage(browser, targetId) {
301
- const deadline = Date.now() + 5000;
302
- do {
303
- const page = await findPageByTargetId(browser, targetId);
304
- if (page)
305
- return page;
306
- if (Date.now() >= deadline)
307
- return null;
308
- await new Promise((resolve) => setTimeout(resolve, 25));
309
- } while (true);
310
- }
@@ -1,29 +0,0 @@
1
- export type EntrypointMode = "standalone" | "extension";
2
- export interface EntrypointArgs {
3
- mode: EntrypointMode;
4
- host: string;
5
- port: number;
6
- cdpPort: number;
7
- headless: boolean;
8
- }
9
- export interface StandaloneReadinessInfo {
10
- mode: "standalone";
11
- host: string;
12
- port: number;
13
- wsEndpoint: string;
14
- }
15
- export interface ExtensionReadinessInfo {
16
- mode: "extension";
17
- host: string;
18
- port: number;
19
- wsEndpoint: string;
20
- }
21
- export type ReadinessInfo = StandaloneReadinessInfo | ExtensionReadinessInfo;
22
- export declare const DEFAULT_HOST = "localhost";
23
- export declare function formatHostForUrl(host: string): string;
24
- export declare function formatHttpUrl(host: string, port: number): string;
25
- export declare function formatWsUrl(host: string, port: number, path?: string): string;
26
- export declare function resolveHostForProbe(host: string): string;
27
- export declare function normalizeLoopbackHost(host: string): string;
28
- export declare function parseEntrypointArgs(argv: string[], env?: Record<string, string | undefined>): EntrypointArgs;
29
- export declare function formatReadinessLines(info: ReadinessInfo): string[];
@@ -1,113 +0,0 @@
1
- import { isIP } from "node:net";
2
- export const DEFAULT_HOST = "localhost";
3
- const DEFAULT_PORT = 9222;
4
- const DEFAULT_CDP_PORT = 9223;
5
- export function formatHostForUrl(host) {
6
- const normalizedHost = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
7
- return normalizedHost.includes(":") ? `[${normalizedHost}]` : normalizedHost;
8
- }
9
- export function formatHttpUrl(host, port) {
10
- return `http://${formatHostForUrl(host)}:${port}`;
11
- }
12
- export function formatWsUrl(host, port, path = "") {
13
- return `ws://${formatHostForUrl(host)}:${port}${path}`;
14
- }
15
- export function resolveHostForProbe(host) {
16
- return normalizeLoopbackHost(host);
17
- }
18
- export function normalizeLoopbackHost(host) {
19
- const normalized = host === "[::1]" ? "::1" : host.toLowerCase();
20
- if (normalized === "localhost" || normalized === "::1")
21
- return normalized;
22
- if (isIP(normalized) === 4 && normalized.split(".")[0] === "127")
23
- return normalized;
24
- throw new Error(`Invalid host: ${host}. Only loopback hosts are allowed`);
25
- }
26
- export function parseEntrypointArgs(argv, env = process.env) {
27
- let index = 0;
28
- let mode = "standalone";
29
- let host = env.HOST ?? DEFAULT_HOST;
30
- let port = env.PORT ?? String(DEFAULT_PORT);
31
- let portLabel = env.PORT === undefined ? "port" : "PORT";
32
- let cdpPort = String(DEFAULT_CDP_PORT);
33
- let headless = env.HEADLESS?.trim().toLowerCase() === "true";
34
- const first = argv[0];
35
- if (first && !first.startsWith("--")) {
36
- if (first !== "standalone" && first !== "extension") {
37
- throw new Error(`Unknown mode: ${first}`);
38
- }
39
- mode = first;
40
- index = 1;
41
- }
42
- while (index < argv.length) {
43
- const token = argv[index++];
44
- switch (token) {
45
- case "--headless":
46
- if (mode === "extension") {
47
- throw new Error("--headless is only valid in standalone mode");
48
- }
49
- headless = true;
50
- break;
51
- case "--host":
52
- host = readValue(argv, index - 1);
53
- index += 1;
54
- break;
55
- case "--port":
56
- port = readValue(argv, index - 1);
57
- portLabel = "port";
58
- index += 1;
59
- break;
60
- case "--cdp-port":
61
- if (mode === "extension") {
62
- throw new Error("--cdp-port is only valid in standalone mode");
63
- }
64
- cdpPort = readValue(argv, index - 1);
65
- index += 1;
66
- break;
67
- default:
68
- throw new Error(`Unknown argument: ${token}`);
69
- }
70
- }
71
- return {
72
- mode,
73
- host: normalizeLoopbackHost(host),
74
- port: parsePort(port, portLabel),
75
- cdpPort: parsePort(cdpPort, "cdpPort"),
76
- headless,
77
- };
78
- }
79
- export function formatReadinessLines(info) {
80
- if (info.mode === "standalone") {
81
- return [
82
- "Dev browser server started",
83
- ` HTTP: ${formatHttpUrl(info.host, info.port)}`,
84
- ` WebSocket: ${info.wsEndpoint}`,
85
- "",
86
- "Ready",
87
- ];
88
- }
89
- return [
90
- "CDP relay server started",
91
- ` HTTP: ${formatHttpUrl(info.host, info.port)}`,
92
- ` CDP endpoint: ${info.wsEndpoint}`,
93
- ` Extension endpoint: ${formatWsUrl(info.host, info.port, "/extension")}`,
94
- "",
95
- "Waiting for extension to connect...",
96
- ];
97
- }
98
- function readValue(argv, optionIndex) {
99
- const value = argv[optionIndex + 1];
100
- if (value === undefined || value.startsWith("--")) {
101
- throw new Error(`Missing value for ${argv[optionIndex]}`);
102
- }
103
- return value;
104
- }
105
- function parsePort(value, label) {
106
- if (!/^\d+$/.test(value))
107
- throw new Error(`Invalid ${label}: ${value}`);
108
- const port = Number(value);
109
- if (port < 1 || port > 65535) {
110
- throw new Error(`Invalid ${label}: ${value}`);
111
- }
112
- return port;
113
- }
@@ -1,3 +0,0 @@
1
- export { createStandaloneApp, createStandalonePageBackend, serve } from "./standalone.js";
2
- export type { DevBrowserServer } from "./standalone.js";
3
- export type { GetPageResponse, ListPagesResponse, ServeOptions, ServerInfoResponse, } from "./types.js";
@@ -1 +0,0 @@
1
- export { createStandaloneApp, createStandalonePageBackend, serve } from "./standalone.js";
@@ -1,24 +0,0 @@
1
- import { Hono } from "hono";
2
- export interface Viewport {
3
- width: number;
4
- height: number;
5
- }
6
- export interface PageDescriptor {
7
- name: string;
8
- targetId: string;
9
- }
10
- export interface PageBackend {
11
- list(): Promise<PageDescriptor[]>;
12
- getOrCreate(name: string, viewport?: Viewport): Promise<PageDescriptor>;
13
- close(name: string): Promise<boolean>;
14
- }
15
- type PageBackendStatus = 400 | 404 | 409 | 500 | 502 | 503 | 504;
16
- export declare class PageBackendError extends Error {
17
- readonly status: PageBackendStatus;
18
- constructor(status: PageBackendStatus, message: string);
19
- }
20
- export declare function createPageApi({ backend, wsEndpoint, }: {
21
- backend: PageBackend;
22
- wsEndpoint: string;
23
- }): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
24
- export {};
@@ -1,103 +0,0 @@
1
- import { Hono } from "hono";
2
- export class PageBackendError extends Error {
3
- status;
4
- constructor(status, message) {
5
- super(message);
6
- this.status = status;
7
- this.name = "PageBackendError";
8
- }
9
- }
10
- export function createPageApi({ backend, wsEndpoint, }) {
11
- const app = new Hono();
12
- app.get("/pages", async (c) => {
13
- try {
14
- const pages = await backend.list();
15
- return c.json({ pages: pages.map(({ name }) => name) });
16
- }
17
- catch (error) {
18
- return backendError(c, error);
19
- }
20
- });
21
- app.post("/pages", async (c) => {
22
- const originError = rejectBrowserMutation(c);
23
- if (originError)
24
- return originError;
25
- if (c.req.header("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
26
- return c.json({ error: "content-type must be application/json" }, 415);
27
- }
28
- let body;
29
- try {
30
- body = await c.req.json();
31
- }
32
- catch {
33
- return c.json({ error: "request body must be valid JSON" }, 400);
34
- }
35
- const validation = validatePageRequest(body);
36
- if (typeof validation === "string") {
37
- return c.json({ error: validation }, 400);
38
- }
39
- try {
40
- const page = await backend.getOrCreate(validation.name, validation.viewport);
41
- return c.json({ wsEndpoint, ...page });
42
- }
43
- catch (error) {
44
- return backendError(c, error);
45
- }
46
- });
47
- app.delete("/pages/:name", async (c) => {
48
- const originError = rejectBrowserMutation(c);
49
- if (originError)
50
- return originError;
51
- try {
52
- if (!(await backend.close(c.req.param("name")))) {
53
- return c.json({ error: "page not found" }, 404);
54
- }
55
- return c.json({ success: true });
56
- }
57
- catch (error) {
58
- return backendError(c, error);
59
- }
60
- });
61
- return app;
62
- }
63
- function rejectBrowserMutation(c) {
64
- if (c.req.header("origin") !== undefined) {
65
- return c.json({ error: "browser-origin requests are not allowed" }, 403);
66
- }
67
- return null;
68
- }
69
- function validatePageRequest(body) {
70
- if (!isRecord(body) || typeof body.name !== "string") {
71
- return "name is required and must be a string";
72
- }
73
- if (body.name.length === 0) {
74
- return "name cannot be empty";
75
- }
76
- if (body.name.length > 256) {
77
- return "name must be 256 characters or less";
78
- }
79
- if (body.viewport === undefined) {
80
- return { name: body.name };
81
- }
82
- if (!isRecord(body.viewport) ||
83
- !isPositiveInteger(body.viewport.width) ||
84
- !isPositiveInteger(body.viewport.height)) {
85
- return "viewport must have positive integer width and height";
86
- }
87
- return {
88
- name: body.name,
89
- viewport: { width: body.viewport.width, height: body.viewport.height },
90
- };
91
- }
92
- function isRecord(value) {
93
- return typeof value === "object" && value !== null && !Array.isArray(value);
94
- }
95
- function isPositiveInteger(value) {
96
- return typeof value === "number" && Number.isInteger(value) && value > 0;
97
- }
98
- function backendError(c, error) {
99
- if (error instanceof PageBackendError) {
100
- return c.json({ error: error.message }, error.status);
101
- }
102
- return c.json({ error: error instanceof Error ? error.message : "internal server error" }, 500);
103
- }
@@ -1,26 +0,0 @@
1
- import { type PageBackend } from "./page-api.js";
2
- import { createTargetRegistry } from "./target-registry.js";
3
- export interface RelayOptions {
4
- port?: number;
5
- host?: string;
6
- targetTimeoutMs?: number;
7
- }
8
- export interface RelayServer {
9
- wsEndpoint: string;
10
- port: number;
11
- stop(): Promise<void>;
12
- }
13
- export declare const WEBSOCKET_POLICY_VIOLATION_CODE = 1008;
14
- export declare const CDP_ORIGIN_POLICY_REASON = "CDP endpoint requires an originless client";
15
- export declare const EXTENSION_ORIGIN_POLICY_REASON = "Extension endpoint requires originless or chrome-extension client";
16
- export declare function isTrustedCdpOrigin(origin: string | undefined): boolean;
17
- export declare function isTrustedExtensionOrigin(origin: string | undefined): boolean;
18
- type TargetRegistry = ReturnType<typeof createTargetRegistry>;
19
- export declare function createExtensionPageBackend({ registry, isConnected, sendCommand, timeoutMs, }: {
20
- registry: TargetRegistry;
21
- isConnected: () => boolean;
22
- sendCommand: (method: string, params?: Record<string, unknown>) => Promise<unknown>;
23
- timeoutMs: number;
24
- }): PageBackend;
25
- export declare function serveRelay(options?: RelayOptions): Promise<RelayServer>;
26
- export {};