@vostride/agent-qa-plugin-kernel 0.3.1 → 0.3.3
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.
package/README.md
CHANGED
|
@@ -40,10 +40,10 @@ A test runs there with `use.browser.remote: kernel` in the config, a suite or th
|
|
|
40
40
|
|
|
41
41
|
Options belong to the `plugins` entry and describe the account.
|
|
42
42
|
|
|
43
|
-
| Option | Default | Meaning
|
|
44
|
-
| -------- | -------------------------- |
|
|
45
|
-
| `apiKey` | required | The Kernel API key, a credential. Write it as a `{{secret:<name>}}` reference.
|
|
46
|
-
| `apiUrl` | `https://api.onkernel.com` | Kernel's API. It must use `https://` unless it points at this machine.
|
|
43
|
+
| Option | Default | Meaning |
|
|
44
|
+
| -------- | -------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
45
|
+
| `apiKey` | required | The Kernel API key, a credential. Write it as a `{{secret:<name>}}` reference. |
|
|
46
|
+
| `apiUrl` | `https://api.onkernel.com` | Kernel's API. It must use `https://` with no user or password, unless it points at this machine. |
|
|
47
47
|
|
|
48
48
|
The key is sent only as the `Authorization: Bearer` header. It never appears in a message, a log or
|
|
49
49
|
an answer.
|
package/dist/main.js
CHANGED
|
@@ -1,73 +1,22 @@
|
|
|
1
|
-
import { PluginError, credential, definePlugin, z } from "@vostride/agent-qa-plugin-kit";
|
|
1
|
+
import { PluginError, checkEndpoints, createVendorApi, credential, definePlugin, z } from "@vostride/agent-qa-plugin-kit";
|
|
2
2
|
import { setTimeout } from "node:timers/promises";
|
|
3
3
|
// src/kernel-api.ts
|
|
4
|
+
const KERNEL_STATUS_KINDS = {
|
|
5
|
+
403: "auth",
|
|
6
|
+
529: "capacity"
|
|
7
|
+
};
|
|
4
8
|
const KernelBrowserSchema = z.object({
|
|
5
9
|
session_id: z.string().min(1),
|
|
6
10
|
cdp_ws_url: z.string().min(1),
|
|
7
11
|
browser_live_view_url: z.string().min(1).optional()
|
|
8
12
|
});
|
|
9
|
-
function kernelFailureKind(status) {
|
|
10
|
-
if (status === void 0) return "unreachable";
|
|
11
|
-
if (status === 401 || status === 403) return "auth";
|
|
12
|
-
if (status === 429 || status === 529) return "capacity";
|
|
13
|
-
return "refused";
|
|
14
|
-
}
|
|
15
|
-
const MAX_REASON_LENGTH = 200;
|
|
16
|
-
function oneLine(text) {
|
|
17
|
-
const line = text.replace(/\s+/g, " ").trim();
|
|
18
|
-
return line.length > MAX_REASON_LENGTH ? `${line.slice(0, MAX_REASON_LENGTH)}…` : line;
|
|
19
|
-
}
|
|
20
|
-
async function vendorReason(response) {
|
|
21
|
-
const text = await response.text().catch(() => "");
|
|
22
|
-
try {
|
|
23
|
-
const body = JSON.parse(text);
|
|
24
|
-
if (typeof body === "object" && body !== null && "message" in body) {
|
|
25
|
-
const { message } = body;
|
|
26
|
-
if (typeof message === "string" && message.trim() !== "") return `: ${oneLine(message)}`;
|
|
27
|
-
}
|
|
28
|
-
} catch {}
|
|
29
|
-
return "";
|
|
30
|
-
}
|
|
31
|
-
async function statusFailure(response, request) {
|
|
32
|
-
const retryAfter = response.headers.get("retry-after");
|
|
33
|
-
const wait = response.status === 429 && retryAfter !== null ? `, retry after ${retryAfter} s` : "";
|
|
34
|
-
const reason = await vendorReason(response);
|
|
35
|
-
return new PluginError(`Kernel answered ${response.status} to ${request}${reason}${wait}`, { kind: kernelFailureKind(response.status) });
|
|
36
|
-
}
|
|
37
|
-
async function jsonOf(response) {
|
|
38
|
-
const text = await response.text();
|
|
39
|
-
try {
|
|
40
|
-
return JSON.parse(text);
|
|
41
|
-
} catch {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function causeText(error) {
|
|
46
|
-
if (!(error instanceof Error)) return String(error);
|
|
47
|
-
const cause = error.cause;
|
|
48
|
-
if (cause instanceof Error) return oneLine("code" in cause && typeof cause.code === "string" ? cause.code : cause.message);
|
|
49
|
-
return oneLine(error.message);
|
|
50
|
-
}
|
|
51
13
|
function createKernelApi(input) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
method,
|
|
59
|
-
headers: {
|
|
60
|
-
...init.headers,
|
|
61
|
-
authorization
|
|
62
|
-
},
|
|
63
|
-
...init.body === void 0 ? {} : { body: init.body },
|
|
64
|
-
...init.signal === void 0 ? {} : { signal: init.signal }
|
|
65
|
-
});
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (init.signal?.aborted === true) throw error;
|
|
68
|
-
throw new PluginError(`Kernel could not be reached at ${url.origin}: ${causeText(error)}`, { kind: "unreachable" });
|
|
69
|
-
}
|
|
70
|
-
};
|
|
14
|
+
const api = createVendorApi({
|
|
15
|
+
vendor: "Kernel",
|
|
16
|
+
baseUrl: input.apiUrl,
|
|
17
|
+
headers: { authorization: `Bearer ${input.apiKey}` },
|
|
18
|
+
kinds: KERNEL_STATUS_KINDS
|
|
19
|
+
});
|
|
71
20
|
const jsonHeaders = {
|
|
72
21
|
"content-type": "application/json",
|
|
73
22
|
accept: "application/json"
|
|
@@ -75,24 +24,24 @@ function createKernelApi(input) {
|
|
|
75
24
|
const poolPath = (pool, action = "") => `browser_pools/${encodeURIComponent(pool)}${action === "" ? "" : `/${action}`}`;
|
|
76
25
|
return {
|
|
77
26
|
async createBrowser(body, signal) {
|
|
78
|
-
const response = await send("POST", "browsers", {
|
|
27
|
+
const response = await api.send("POST", "browsers", {
|
|
79
28
|
headers: jsonHeaders,
|
|
80
29
|
body: JSON.stringify(body),
|
|
81
30
|
signal
|
|
82
31
|
});
|
|
83
|
-
if (!response.ok) throw await
|
|
84
|
-
return
|
|
32
|
+
if (!response.ok) throw await api.failure(response, "POST /browsers");
|
|
33
|
+
return api.json(response);
|
|
85
34
|
},
|
|
86
35
|
async deleteBrowser(id, signal) {
|
|
87
|
-
const response = await send("DELETE", `browsers/${encodeURIComponent(id)}`, { signal });
|
|
36
|
+
const response = await api.send("DELETE", `browsers/${encodeURIComponent(id)}`, { signal });
|
|
88
37
|
if (response.ok || response.status === 404) {
|
|
89
38
|
await response.body?.cancel();
|
|
90
39
|
return;
|
|
91
40
|
}
|
|
92
|
-
throw await
|
|
41
|
+
throw await api.failure(response, "DELETE /browsers/{id}");
|
|
93
42
|
},
|
|
94
43
|
async acquireFromPool(pool, body, signal) {
|
|
95
|
-
const response = await send("POST", poolPath(pool, "acquire"), {
|
|
44
|
+
const response = await api.send("POST", poolPath(pool, "acquire"), {
|
|
96
45
|
headers: jsonHeaders,
|
|
97
46
|
body: JSON.stringify(body),
|
|
98
47
|
signal
|
|
@@ -101,14 +50,14 @@ function createKernelApi(input) {
|
|
|
101
50
|
await response.body?.cancel();
|
|
102
51
|
return { acquired: false };
|
|
103
52
|
}
|
|
104
|
-
if (!response.ok) throw await
|
|
53
|
+
if (!response.ok) throw await api.failure(response, "POST /browser_pools/{pool}/acquire");
|
|
105
54
|
return {
|
|
106
55
|
acquired: true,
|
|
107
|
-
browser: await
|
|
56
|
+
browser: await api.json(response)
|
|
108
57
|
};
|
|
109
58
|
},
|
|
110
59
|
async releaseToPool(pool, sessionId, reuse, signal) {
|
|
111
|
-
const response = await send("POST", poolPath(pool, "release"), {
|
|
60
|
+
const response = await api.send("POST", poolPath(pool, "release"), {
|
|
112
61
|
headers: jsonHeaders,
|
|
113
62
|
body: JSON.stringify({
|
|
114
63
|
session_id: sessionId,
|
|
@@ -120,26 +69,26 @@ function createKernelApi(input) {
|
|
|
120
69
|
await response.body?.cancel();
|
|
121
70
|
return;
|
|
122
71
|
}
|
|
123
|
-
throw await
|
|
72
|
+
throw await api.failure(response, "POST /browser_pools/{pool}/release");
|
|
124
73
|
},
|
|
125
74
|
async listBrowsers(limit, signal) {
|
|
126
|
-
const response = await send("GET", `browsers?limit=${String(limit)}`, {
|
|
75
|
+
const response = await api.send("GET", `browsers?limit=${String(limit)}`, {
|
|
127
76
|
headers: { accept: "application/json" },
|
|
128
77
|
signal
|
|
129
78
|
});
|
|
130
|
-
if (!response.ok) throw await
|
|
79
|
+
if (!response.ok) throw await api.failure(response, "GET /browsers");
|
|
131
80
|
await response.body?.cancel();
|
|
132
81
|
},
|
|
133
82
|
async orgLimits(signal) {
|
|
134
|
-
const response = await send("GET", "org/limits", {
|
|
83
|
+
const response = await api.send("GET", "org/limits", {
|
|
135
84
|
headers: { accept: "application/json" },
|
|
136
85
|
signal
|
|
137
86
|
});
|
|
138
|
-
if (!response.ok) throw await
|
|
139
|
-
return
|
|
87
|
+
if (!response.ok) throw await api.failure(response, "GET /org/limits");
|
|
88
|
+
return api.json(response);
|
|
140
89
|
},
|
|
141
90
|
async getPool(pool, signal) {
|
|
142
|
-
const response = await send("GET", poolPath(pool), {
|
|
91
|
+
const response = await api.send("GET", poolPath(pool), {
|
|
143
92
|
headers: { accept: "application/json" },
|
|
144
93
|
signal
|
|
145
94
|
});
|
|
@@ -147,7 +96,7 @@ function createKernelApi(input) {
|
|
|
147
96
|
await response.body?.cancel();
|
|
148
97
|
return false;
|
|
149
98
|
}
|
|
150
|
-
if (!response.ok) throw await
|
|
99
|
+
if (!response.ok) throw await api.failure(response, "GET /browser_pools/{pool}");
|
|
151
100
|
await response.body?.cancel();
|
|
152
101
|
return true;
|
|
153
102
|
}
|
|
@@ -156,7 +105,6 @@ function createKernelApi(input) {
|
|
|
156
105
|
// src/plugin.ts
|
|
157
106
|
const KERNEL_API_URL = "https://api.onkernel.com";
|
|
158
107
|
const POOL_POLL_FLOOR_MS = 1e3;
|
|
159
|
-
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "[::1]"]);
|
|
160
108
|
const options = z.strictObject({
|
|
161
109
|
apiKey: credential("The Kernel API key"),
|
|
162
110
|
apiUrl: z.url().default(KERNEL_API_URL).describe("Kernel's API, https:// unless it points at this machine")
|
|
@@ -176,13 +124,6 @@ const settings = z.strictObject({
|
|
|
176
124
|
profile: z.union([z.string().min(1), z.strictObject({ id: z.string().min(1) })]).optional().describe("A Kernel profile loaded read-only, by name or as { id }; needs context: reuse"),
|
|
177
125
|
extra: z.record(z.string(), z.unknown()).optional().describe("Fields merged last into the create or pool acquire request, overriding the rest")
|
|
178
126
|
});
|
|
179
|
-
function apiUrlAllowed(value) {
|
|
180
|
-
const url = new URL(value);
|
|
181
|
-
if (url.username !== "" || url.password !== "") return false;
|
|
182
|
-
if (url.protocol === "https:") return true;
|
|
183
|
-
if (url.protocol !== "http:") return false;
|
|
184
|
-
return LOOPBACK_HOSTS.has(url.hostname) || /^127(?:\.\d{1,3}){3}$/.test(url.hostname);
|
|
185
|
-
}
|
|
186
127
|
const CREATE_ONLY_SETTINGS = [
|
|
187
128
|
"stealth",
|
|
188
129
|
"region",
|
|
@@ -301,10 +242,10 @@ var plugin_default = definePlugin({
|
|
|
301
242
|
serves: ["cdp"],
|
|
302
243
|
skill: "./skill",
|
|
303
244
|
initialize({ options }) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
});
|
|
245
|
+
checkEndpoints(options, [{
|
|
246
|
+
path: "apiUrl",
|
|
247
|
+
scheme: "https"
|
|
248
|
+
}]);
|
|
308
249
|
},
|
|
309
250
|
async check({ options, remotes }, ctx) {
|
|
310
251
|
const api = createKernelApi(options);
|
|
@@ -369,4 +310,4 @@ var plugin_default = definePlugin({
|
|
|
369
310
|
});
|
|
370
311
|
export { plugin_default as t };
|
|
371
312
|
|
|
372
|
-
//# sourceMappingURL=plugin-
|
|
313
|
+
//# sourceMappingURL=plugin-Bh7Aks1i.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-Bh7Aks1i.js","names":["delay"],"sources":["../src/kernel-api.ts","../src/plugin.ts"],"sourcesContent":["import {\n createVendorApi,\n vendorFailureKind,\n z,\n type VendorStatusKinds,\n} from '@vostride/agent-qa-plugin-kit'\n\ntype KernelFailureKind = ReturnType<typeof vendorFailureKind>\n\nconst KERNEL_STATUS_KINDS: VendorStatusKinds = { 403: 'auth', 529: 'capacity' }\n\nexport const KernelBrowserSchema = z.object({\n session_id: z.string().min(1),\n cdp_ws_url: z.string().min(1),\n browser_live_view_url: z.string().min(1).optional(),\n})\n\ninterface KernelApiInput {\n apiUrl: string\n apiKey: string\n}\n\nexport type PoolAcquireAnswer = { acquired: true; browser: unknown } | { acquired: false }\n\nexport interface KernelApi {\n createBrowser(body: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n deleteBrowser(id: string, signal?: AbortSignal): Promise<void>\n acquireFromPool(\n pool: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<PoolAcquireAnswer>\n releaseToPool(pool: string, sessionId: string, reuse: boolean, signal?: AbortSignal): Promise<void>\n listBrowsers(limit: number, signal?: AbortSignal): Promise<void>\n orgLimits(signal?: AbortSignal): Promise<unknown>\n getPool(pool: string, signal?: AbortSignal): Promise<boolean>\n}\n\nexport function kernelFailureKind(status: number | undefined): KernelFailureKind {\n return vendorFailureKind(status, KERNEL_STATUS_KINDS)\n}\n\nexport function createKernelApi(input: KernelApiInput): KernelApi {\n const api = createVendorApi({\n vendor: 'Kernel',\n baseUrl: input.apiUrl,\n headers: { authorization: `Bearer ${input.apiKey}` },\n kinds: KERNEL_STATUS_KINDS,\n })\n\n const jsonHeaders = { 'content-type': 'application/json', accept: 'application/json' }\n const poolPath = (pool: string, action = '') =>\n `browser_pools/${encodeURIComponent(pool)}${action === '' ? '' : `/${action}`}`\n\n return {\n async createBrowser(body, signal) {\n const response = await api.send('POST', 'browsers', {\n headers: jsonHeaders,\n body: JSON.stringify(body),\n signal,\n })\n if (!response.ok) throw await api.failure(response, 'POST /browsers')\n return api.json(response)\n },\n\n async deleteBrowser(id, signal) {\n const response = await api.send('DELETE', `browsers/${encodeURIComponent(id)}`, { signal })\n if (response.ok || response.status === 404) {\n await response.body?.cancel()\n return\n }\n throw await api.failure(response, 'DELETE /browsers/{id}')\n },\n\n async acquireFromPool(pool, body, signal) {\n const response = await api.send('POST', poolPath(pool, 'acquire'), {\n headers: jsonHeaders,\n body: JSON.stringify(body),\n signal,\n })\n if (response.status === 204) {\n await response.body?.cancel()\n return { acquired: false }\n }\n if (!response.ok) throw await api.failure(response, 'POST /browser_pools/{pool}/acquire')\n return { acquired: true, browser: await api.json(response) }\n },\n\n async releaseToPool(pool, sessionId, reuse, signal) {\n const response = await api.send('POST', poolPath(pool, 'release'), {\n headers: jsonHeaders,\n body: JSON.stringify({ session_id: sessionId, reuse }),\n signal,\n })\n if (response.ok || response.status === 404) {\n await response.body?.cancel()\n return\n }\n throw await api.failure(response, 'POST /browser_pools/{pool}/release')\n },\n\n async listBrowsers(limit, signal) {\n const response = await api.send('GET', `browsers?limit=${String(limit)}`, {\n headers: { accept: 'application/json' },\n signal,\n })\n if (!response.ok) throw await api.failure(response, 'GET /browsers')\n await response.body?.cancel()\n },\n\n async orgLimits(signal) {\n const response = await api.send('GET', 'org/limits', {\n headers: { accept: 'application/json' },\n signal,\n })\n if (!response.ok) throw await api.failure(response, 'GET /org/limits')\n return api.json(response)\n },\n\n async getPool(pool, signal) {\n const response = await api.send('GET', poolPath(pool), {\n headers: { accept: 'application/json' },\n signal,\n })\n if (response.status === 404) {\n await response.body?.cancel()\n return false\n }\n if (!response.ok) throw await api.failure(response, 'GET /browser_pools/{pool}')\n await response.body?.cancel()\n return true\n },\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport {\n checkEndpoints,\n credential,\n definePlugin,\n PluginError,\n z,\n type AcquireInput,\n type PluginCallContext,\n} from '@vostride/agent-qa-plugin-kit'\nimport {\n createKernelApi,\n KernelBrowserSchema,\n type KernelApi,\n type PoolAcquireAnswer,\n} from './kernel-api.js'\n\nconst KERNEL_API_URL = 'https://api.onkernel.com'\n// Kernel answers 204 at once when the acquire timeout is short, so a poll waits at least this long.\nconst POOL_POLL_FLOOR_MS = 1_000\n\nconst options = z.strictObject({\n apiKey: credential('The Kernel API key'),\n apiUrl: z\n .url()\n .default(KERNEL_API_URL)\n .describe(\"Kernel's API, https:// unless it points at this machine\"),\n})\n\nconst settings = z.strictObject({\n headless: z\n .boolean()\n .default(true)\n .describe('Run a headless browser; Kernel offers a live view only for a headful one'),\n timeoutSeconds: z\n .int()\n .min(10)\n .max(259_200)\n .default(60)\n .describe('Seconds with no connection before Kernel deletes the browser'),\n stealth: z.boolean().optional().describe(\"Kernel's stealth mode\"),\n region: z.string().min(1).optional().describe('The Kernel region, for example eu-west'),\n proxy: z.record(z.string(), z.unknown()).optional().describe(\"Kernel's proxy, sent as given\"),\n viewport: z\n .strictObject({\n width: z.int().positive(),\n height: z.int().positive(),\n refreshRate: z.int().positive().optional(),\n })\n .optional()\n .describe('The browser window size'),\n pool: z\n .string()\n .min(1)\n .optional()\n .describe('A Kernel browser pool, by id or name, to acquire from instead of creating a browser'),\n profile: z\n .union([z.string().min(1), z.strictObject({ id: z.string().min(1) })])\n .optional()\n .describe('A Kernel profile loaded read-only, by name or as { id }; needs context: reuse'),\n extra: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Fields merged last into the create or pool acquire request, overriding the rest'),\n})\n\ntype KernelSettings = z.output<typeof settings>\ntype Session = AcquireInput<unknown, unknown>['session']\n\nconst CREATE_ONLY_SETTINGS = ['stealth', 'region', 'proxy', 'viewport'] as const\n\nfunction profileBody(input: KernelSettings, session: Session) {\n const { profile } = input\n if (profile === undefined) return undefined\n if (session.platform !== 'web' || session.context !== 'reuse') {\n throw new PluginError(\n 'profile loads only into a reused context; set context: reuse on the remote.',\n { kind: 'refused', path: 'settings.profile' },\n )\n }\n const named = typeof profile === 'string' ? { name: profile } : { id: profile.id }\n return { ...named, save_changes: false }\n}\n\nfunction createBody(input: KernelSettings, profile: object | undefined): Record<string, unknown> {\n const { viewport } = input\n return {\n headless: input.headless,\n timeout_seconds: input.timeoutSeconds,\n ...(input.stealth === undefined ? {} : { stealth: input.stealth }),\n ...(input.region === undefined ? {} : { region: input.region }),\n ...(input.proxy === undefined ? {} : { proxy: input.proxy }),\n ...(viewport === undefined\n ? {}\n : {\n viewport: {\n width: viewport.width,\n height: viewport.height,\n ...(viewport.refreshRate === undefined ? {} : { refresh_rate: viewport.refreshRate }),\n },\n }),\n ...(profile === undefined ? {} : { profile }),\n ...input.extra,\n }\n}\n\nconst HandleSchema = z.object({\n sessionId: z.string().min(1),\n pool: z.string().min(1).optional(),\n reuse: z.boolean().optional(),\n})\ntype KernelHandle = z.output<typeof HandleSchema>\n\nfunction sessionIdOf(answer: unknown): string | undefined {\n if (typeof answer !== 'object' || answer === null || !('session_id' in answer)) return undefined\n const id = answer.session_id\n return typeof id === 'string' && id !== '' ? id : undefined\n}\n\nfunction cdpAnswer(answer: unknown, place: Omit<KernelHandle, 'sessionId'>) {\n const sessionId = sessionIdOf(answer)\n const handle = sessionId === undefined ? undefined : { sessionId, ...place }\n const browser = KernelBrowserSchema.safeParse(answer)\n if (!browser.success) {\n throw new PluginError('Kernel answered a browser without a session_id and a cdp_ws_url.', {\n kind: 'refused',\n ...(handle === undefined ? {} : { handle }),\n })\n }\n const { cdp_ws_url: url, browser_live_view_url: liveView } = browser.data\n return {\n handle: { sessionId: browser.data.session_id, ...place },\n protocol: 'cdp' as const,\n url,\n mask: liveView === undefined ? [url] : [url, liveView],\n }\n}\n\nasync function acquireFromPool(\n api: KernelApi,\n pool: string,\n body: Record<string, unknown>,\n reuse: boolean,\n ctx: PluginCallContext,\n) {\n const stopped = () => new PluginError(`stopped waiting for pool ${pool}`, { kind: 'timeout' })\n // Unlike a create, a wait that ends in 204 holds no browser, so the cancel stops the wait.\n for (;;) {\n const began = Date.now()\n let answer: PoolAcquireAnswer\n try {\n answer = await api.acquireFromPool(pool, body, ctx.signal)\n } catch (error) {\n if (ctx.signal.aborted) throw stopped()\n throw error\n }\n if (answer.acquired) return cdpAnswer(answer.browser, { pool, reuse })\n if (ctx.signal.aborted) throw stopped()\n ctx.progress({ message: `waiting for pool ${pool}` })\n const rest = POOL_POLL_FLOOR_MS - (Date.now() - began)\n if (rest > 0) await delay(rest, undefined, { signal: ctx.signal }).catch(() => {})\n if (ctx.signal.aborted) throw stopped()\n }\n}\n\nconst OrgLimitsSchema = z.object({\n concurrent_sessions_available: z.int().nonnegative(),\n concurrent_sessions_used: z.int().nonnegative(),\n})\n\nfunction refusedKey(error: unknown): boolean {\n return error instanceof PluginError && error.kind === 'auth'\n}\n\nfunction failureLine(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nasync function limitsLine(api: KernelApi, signal: AbortSignal): Promise<string> {\n let answer: unknown\n try {\n answer = await api.orgLimits(signal)\n } catch (error) {\n if (refusedKey(error)) return 'organization limits not readable with this key'\n return `organization limits not readable: ${failureLine(error)}`\n }\n const limits = OrgLimitsSchema.safeParse(answer)\n if (!limits.success) return 'organization limits not readable: Kernel answered another shape'\n const { concurrent_sessions_available: free, concurrent_sessions_used: used } = limits.data\n return `${String(free)} of ${String(free + used)} concurrent sessions free`\n}\n\nexport default definePlugin({\n options,\n settings,\n serves: ['cdp'],\n skill: './skill',\n\n initialize({ options }) {\n checkEndpoints(options, [{ path: 'apiUrl', scheme: 'https' }])\n },\n\n async check({ options, remotes }, ctx) {\n const api = createKernelApi(options)\n try {\n await api.listBrowsers(1, ctx.signal)\n } catch (error) {\n const line = refusedKey(error) ? 'the API key was refused' : failureLine(error)\n return { state: 'warn', lines: [line] }\n }\n const lines = ['the API key was accepted', await limitsLine(api, ctx.signal)]\n let state: 'ok' | 'warn' = 'ok'\n const pools = new Set(remotes.flatMap(({ settings }) => settings.pool ?? []))\n for (const pool of pools) {\n let found: boolean\n try {\n found = await api.getPool(pool, ctx.signal)\n } catch (error) {\n state = 'warn'\n lines.push(`pool ${pool} not readable: ${failureLine(error)}`)\n continue\n }\n if (!found) state = 'warn'\n lines.push(found ? `pool ${pool} found` : `pool ${pool} not found`)\n }\n return { state, lines }\n },\n\n async acquire({ options, settings, session }, ctx) {\n if (session.platform !== 'web' || session.engine !== 'chromium') {\n const what = session.platform === 'web' ? session.engine : session.platform\n throw new PluginError(`Kernel runs Chromium web sessions only, not ${what}.`, {\n kind: 'refused',\n })\n }\n const profile = profileBody(settings, session)\n const api = createKernelApi(options)\n if (settings.pool !== undefined) {\n const fromCreate = CREATE_ONLY_SETTINGS.find((name) => settings[name] !== undefined)\n if (fromCreate !== undefined) {\n throw new PluginError(\n `${fromCreate} comes from the pool's own configuration; remove it from a remote with pool.`,\n { kind: 'refused', path: `settings.${fromCreate}` },\n )\n }\n const body = { ...(profile === undefined ? {} : { profile }), ...settings.extra }\n const reuse = session.context !== 'reuse'\n return acquireFromPool(api, settings.pool, body, reuse, ctx)\n }\n // The create is not cancelled: once Kernel has a browser, the handle must come back to be\n // deleted, and the kit releases a handle that arrives after a cancel was answered.\n return cdpAnswer(await api.createBrowser(createBody(settings, profile)), {})\n },\n\n async release({ options, handle }, ctx) {\n const parsed = HandleSchema.safeParse(handle)\n if (!parsed.success) {\n throw new PluginError('A Kernel handle names its browser by sessionId.', { kind: 'refused' })\n }\n const { sessionId, pool, reuse } = parsed.data\n const api = createKernelApi(options)\n if (pool === undefined) await api.deleteBrowser(sessionId, ctx.signal)\n else await api.releaseToPool(pool, sessionId, reuse ?? false, ctx.signal)\n },\n})\n"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as default } from "./plugin-
|
|
1
|
+
export { t as default } from "./plugin-Bh7Aks1i.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vostride/agent-qa-plugin-kernel",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "agent-qa plugin for Kernel: creates a Kernel browser before a test connects and deletes it after.",
|
|
6
6
|
"keywords": [
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
"clean": "rm -rf dist .turbo"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@vostride/agent-qa-plugin-kit": "0.3.
|
|
45
|
+
"@vostride/agent-qa-plugin-kit": "0.3.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "~24.19.2",
|
|
49
|
-
"@vostride/agent-qa-arch": "0.3.
|
|
49
|
+
"@vostride/agent-qa-arch": "0.3.3",
|
|
50
50
|
"tsdown": "~0.23.4",
|
|
51
51
|
"typescript": "~7.0.2",
|
|
52
52
|
"vitest": "~5.0.5"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-C2raYjWg.js","names":["delay"],"sources":["../src/kernel-api.ts","../src/plugin.ts"],"sourcesContent":["import { PluginError, z } from '@vostride/agent-qa-plugin-kit'\n\ntype KernelFailureKind = 'auth' | 'capacity' | 'refused' | 'unreachable'\n\nexport const KernelBrowserSchema = z.object({\n session_id: z.string().min(1),\n cdp_ws_url: z.string().min(1),\n browser_live_view_url: z.string().min(1).optional(),\n})\n\ninterface KernelApiInput {\n apiUrl: string\n apiKey: string\n}\n\nexport type PoolAcquireAnswer = { acquired: true; browser: unknown } | { acquired: false }\n\nexport interface KernelApi {\n createBrowser(body: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n deleteBrowser(id: string, signal?: AbortSignal): Promise<void>\n acquireFromPool(\n pool: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<PoolAcquireAnswer>\n releaseToPool(pool: string, sessionId: string, reuse: boolean, signal?: AbortSignal): Promise<void>\n listBrowsers(limit: number, signal?: AbortSignal): Promise<void>\n orgLimits(signal?: AbortSignal): Promise<unknown>\n getPool(pool: string, signal?: AbortSignal): Promise<boolean>\n}\n\nexport function kernelFailureKind(status: number | undefined): KernelFailureKind {\n if (status === undefined) return 'unreachable'\n if (status === 401 || status === 403) return 'auth'\n if (status === 429 || status === 529) return 'capacity'\n return 'refused'\n}\n\ninterface SendInit {\n headers?: Record<string, string>\n body?: string\n signal?: AbortSignal | undefined\n}\n\nconst MAX_REASON_LENGTH = 200\n\nfunction oneLine(text: string): string {\n const line = text.replace(/\\s+/g, ' ').trim()\n return line.length > MAX_REASON_LENGTH ? `${line.slice(0, MAX_REASON_LENGTH)}…` : line\n}\n\nasync function vendorReason(response: Response): Promise<string> {\n const text = await response.text().catch(() => '')\n try {\n const body: unknown = JSON.parse(text)\n if (typeof body === 'object' && body !== null && 'message' in body) {\n const { message } = body\n if (typeof message === 'string' && message.trim() !== '') return `: ${oneLine(message)}`\n }\n } catch {\n // a body that is not JSON carries no reason worth showing\n }\n return ''\n}\n\nasync function statusFailure(response: Response, request: string): Promise<PluginError> {\n const retryAfter = response.headers.get('retry-after')\n const wait = response.status === 429 && retryAfter !== null ? `, retry after ${retryAfter} s` : ''\n const reason = await vendorReason(response)\n return new PluginError(`Kernel answered ${response.status} to ${request}${reason}${wait}`, {\n kind: kernelFailureKind(response.status),\n })\n}\n\nasync function jsonOf(response: Response): Promise<unknown> {\n const text = await response.text()\n try {\n return JSON.parse(text) as unknown\n } catch {\n return undefined\n }\n}\n\nfunction causeText(error: unknown): string {\n if (!(error instanceof Error)) return String(error)\n const cause = error.cause\n if (cause instanceof Error) {\n const code = 'code' in cause && typeof cause.code === 'string' ? cause.code : cause.message\n return oneLine(code)\n }\n return oneLine(error.message)\n}\n\nexport function createKernelApi(input: KernelApiInput): KernelApi {\n const base = input.apiUrl.endsWith('/') ? input.apiUrl : `${input.apiUrl}/`\n const authorization = `Bearer ${input.apiKey}`\n\n const send = async (method: string, path: string, init: SendInit): Promise<Response> => {\n const url = new URL(path, base)\n try {\n return await fetch(url, {\n method,\n headers: { ...init.headers, authorization },\n ...(init.body === undefined ? {} : { body: init.body }),\n ...(init.signal === undefined ? {} : { signal: init.signal }),\n })\n } catch (error) {\n if (init.signal?.aborted === true) throw error\n throw new PluginError(`Kernel could not be reached at ${url.origin}: ${causeText(error)}`, {\n kind: 'unreachable',\n })\n }\n }\n\n const jsonHeaders = { 'content-type': 'application/json', accept: 'application/json' }\n const poolPath = (pool: string, action = '') =>\n `browser_pools/${encodeURIComponent(pool)}${action === '' ? '' : `/${action}`}`\n\n return {\n async createBrowser(body, signal) {\n const response = await send('POST', 'browsers', {\n headers: jsonHeaders,\n body: JSON.stringify(body),\n signal,\n })\n if (!response.ok) throw await statusFailure(response, 'POST /browsers')\n return jsonOf(response)\n },\n\n async deleteBrowser(id, signal) {\n const response = await send('DELETE', `browsers/${encodeURIComponent(id)}`, { signal })\n if (response.ok || response.status === 404) {\n await response.body?.cancel()\n return\n }\n throw await statusFailure(response, 'DELETE /browsers/{id}')\n },\n\n async acquireFromPool(pool, body, signal) {\n const response = await send('POST', poolPath(pool, 'acquire'), {\n headers: jsonHeaders,\n body: JSON.stringify(body),\n signal,\n })\n if (response.status === 204) {\n await response.body?.cancel()\n return { acquired: false }\n }\n if (!response.ok) throw await statusFailure(response, 'POST /browser_pools/{pool}/acquire')\n return { acquired: true, browser: await jsonOf(response) }\n },\n\n async releaseToPool(pool, sessionId, reuse, signal) {\n const response = await send('POST', poolPath(pool, 'release'), {\n headers: jsonHeaders,\n body: JSON.stringify({ session_id: sessionId, reuse }),\n signal,\n })\n if (response.ok || response.status === 404) {\n await response.body?.cancel()\n return\n }\n throw await statusFailure(response, 'POST /browser_pools/{pool}/release')\n },\n\n async listBrowsers(limit, signal) {\n const response = await send('GET', `browsers?limit=${String(limit)}`, {\n headers: { accept: 'application/json' },\n signal,\n })\n if (!response.ok) throw await statusFailure(response, 'GET /browsers')\n await response.body?.cancel()\n },\n\n async orgLimits(signal) {\n const response = await send('GET', 'org/limits', {\n headers: { accept: 'application/json' },\n signal,\n })\n if (!response.ok) throw await statusFailure(response, 'GET /org/limits')\n return jsonOf(response)\n },\n\n async getPool(pool, signal) {\n const response = await send('GET', poolPath(pool), {\n headers: { accept: 'application/json' },\n signal,\n })\n if (response.status === 404) {\n await response.body?.cancel()\n return false\n }\n if (!response.ok) throw await statusFailure(response, 'GET /browser_pools/{pool}')\n await response.body?.cancel()\n return true\n },\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport {\n credential,\n definePlugin,\n PluginError,\n z,\n type AcquireInput,\n type PluginCallContext,\n} from '@vostride/agent-qa-plugin-kit'\nimport {\n createKernelApi,\n KernelBrowserSchema,\n type KernelApi,\n type PoolAcquireAnswer,\n} from './kernel-api.js'\n\nconst KERNEL_API_URL = 'https://api.onkernel.com'\n// Kernel answers 204 at once when the acquire timeout is short, so a poll waits at least this long.\nconst POOL_POLL_FLOOR_MS = 1_000\nconst LOOPBACK_HOSTS = new Set(['localhost', '[::1]'])\n\nconst options = z.strictObject({\n apiKey: credential('The Kernel API key'),\n apiUrl: z\n .url()\n .default(KERNEL_API_URL)\n .describe(\"Kernel's API, https:// unless it points at this machine\"),\n})\n\nconst settings = z.strictObject({\n headless: z\n .boolean()\n .default(true)\n .describe('Run a headless browser; Kernel offers a live view only for a headful one'),\n timeoutSeconds: z\n .int()\n .min(10)\n .max(259_200)\n .default(60)\n .describe('Seconds with no connection before Kernel deletes the browser'),\n stealth: z.boolean().optional().describe(\"Kernel's stealth mode\"),\n region: z.string().min(1).optional().describe('The Kernel region, for example eu-west'),\n proxy: z.record(z.string(), z.unknown()).optional().describe(\"Kernel's proxy, sent as given\"),\n viewport: z\n .strictObject({\n width: z.int().positive(),\n height: z.int().positive(),\n refreshRate: z.int().positive().optional(),\n })\n .optional()\n .describe('The browser window size'),\n pool: z\n .string()\n .min(1)\n .optional()\n .describe('A Kernel browser pool, by id or name, to acquire from instead of creating a browser'),\n profile: z\n .union([z.string().min(1), z.strictObject({ id: z.string().min(1) })])\n .optional()\n .describe('A Kernel profile loaded read-only, by name or as { id }; needs context: reuse'),\n extra: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Fields merged last into the create or pool acquire request, overriding the rest'),\n})\n\ntype KernelSettings = z.output<typeof settings>\ntype Session = AcquireInput<unknown, unknown>['session']\n\nfunction apiUrlAllowed(value: string): boolean {\n const url = new URL(value)\n if (url.username !== '' || url.password !== '') return false\n if (url.protocol === 'https:') return true\n if (url.protocol !== 'http:') return false\n return LOOPBACK_HOSTS.has(url.hostname) || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname)\n}\n\nconst CREATE_ONLY_SETTINGS = ['stealth', 'region', 'proxy', 'viewport'] as const\n\nfunction profileBody(input: KernelSettings, session: Session) {\n const { profile } = input\n if (profile === undefined) return undefined\n if (session.platform !== 'web' || session.context !== 'reuse') {\n throw new PluginError(\n 'profile loads only into a reused context; set context: reuse on the remote.',\n { kind: 'refused', path: 'settings.profile' },\n )\n }\n const named = typeof profile === 'string' ? { name: profile } : { id: profile.id }\n return { ...named, save_changes: false }\n}\n\nfunction createBody(input: KernelSettings, profile: object | undefined): Record<string, unknown> {\n const { viewport } = input\n return {\n headless: input.headless,\n timeout_seconds: input.timeoutSeconds,\n ...(input.stealth === undefined ? {} : { stealth: input.stealth }),\n ...(input.region === undefined ? {} : { region: input.region }),\n ...(input.proxy === undefined ? {} : { proxy: input.proxy }),\n ...(viewport === undefined\n ? {}\n : {\n viewport: {\n width: viewport.width,\n height: viewport.height,\n ...(viewport.refreshRate === undefined ? {} : { refresh_rate: viewport.refreshRate }),\n },\n }),\n ...(profile === undefined ? {} : { profile }),\n ...input.extra,\n }\n}\n\nconst HandleSchema = z.object({\n sessionId: z.string().min(1),\n pool: z.string().min(1).optional(),\n reuse: z.boolean().optional(),\n})\ntype KernelHandle = z.output<typeof HandleSchema>\n\nfunction sessionIdOf(answer: unknown): string | undefined {\n if (typeof answer !== 'object' || answer === null || !('session_id' in answer)) return undefined\n const id = answer.session_id\n return typeof id === 'string' && id !== '' ? id : undefined\n}\n\nfunction cdpAnswer(answer: unknown, place: Omit<KernelHandle, 'sessionId'>) {\n const sessionId = sessionIdOf(answer)\n const handle = sessionId === undefined ? undefined : { sessionId, ...place }\n const browser = KernelBrowserSchema.safeParse(answer)\n if (!browser.success) {\n throw new PluginError('Kernel answered a browser without a session_id and a cdp_ws_url.', {\n kind: 'refused',\n ...(handle === undefined ? {} : { handle }),\n })\n }\n const { cdp_ws_url: url, browser_live_view_url: liveView } = browser.data\n return {\n handle: { sessionId: browser.data.session_id, ...place },\n protocol: 'cdp' as const,\n url,\n mask: liveView === undefined ? [url] : [url, liveView],\n }\n}\n\nasync function acquireFromPool(\n api: KernelApi,\n pool: string,\n body: Record<string, unknown>,\n reuse: boolean,\n ctx: PluginCallContext,\n) {\n const stopped = () => new PluginError(`stopped waiting for pool ${pool}`, { kind: 'timeout' })\n // Unlike a create, a wait that ends in 204 holds no browser, so the cancel stops the wait.\n for (;;) {\n const began = Date.now()\n let answer: PoolAcquireAnswer\n try {\n answer = await api.acquireFromPool(pool, body, ctx.signal)\n } catch (error) {\n if (ctx.signal.aborted) throw stopped()\n throw error\n }\n if (answer.acquired) return cdpAnswer(answer.browser, { pool, reuse })\n if (ctx.signal.aborted) throw stopped()\n ctx.progress({ message: `waiting for pool ${pool}` })\n const rest = POOL_POLL_FLOOR_MS - (Date.now() - began)\n if (rest > 0) await delay(rest, undefined, { signal: ctx.signal }).catch(() => {})\n if (ctx.signal.aborted) throw stopped()\n }\n}\n\nconst OrgLimitsSchema = z.object({\n concurrent_sessions_available: z.int().nonnegative(),\n concurrent_sessions_used: z.int().nonnegative(),\n})\n\nfunction refusedKey(error: unknown): boolean {\n return error instanceof PluginError && error.kind === 'auth'\n}\n\nfunction failureLine(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nasync function limitsLine(api: KernelApi, signal: AbortSignal): Promise<string> {\n let answer: unknown\n try {\n answer = await api.orgLimits(signal)\n } catch (error) {\n if (refusedKey(error)) return 'organization limits not readable with this key'\n return `organization limits not readable: ${failureLine(error)}`\n }\n const limits = OrgLimitsSchema.safeParse(answer)\n if (!limits.success) return 'organization limits not readable: Kernel answered another shape'\n const { concurrent_sessions_available: free, concurrent_sessions_used: used } = limits.data\n return `${String(free)} of ${String(free + used)} concurrent sessions free`\n}\n\nexport default definePlugin({\n options,\n settings,\n serves: ['cdp'],\n skill: './skill',\n\n initialize({ options }) {\n if (!apiUrlAllowed(options.apiUrl)) {\n throw new PluginError('apiUrl must use https:// unless it points at this machine.', {\n kind: 'refused',\n path: 'apiUrl',\n })\n }\n },\n\n async check({ options, remotes }, ctx) {\n const api = createKernelApi(options)\n try {\n await api.listBrowsers(1, ctx.signal)\n } catch (error) {\n const line = refusedKey(error) ? 'the API key was refused' : failureLine(error)\n return { state: 'warn', lines: [line] }\n }\n const lines = ['the API key was accepted', await limitsLine(api, ctx.signal)]\n let state: 'ok' | 'warn' = 'ok'\n const pools = new Set(remotes.flatMap(({ settings }) => settings.pool ?? []))\n for (const pool of pools) {\n let found: boolean\n try {\n found = await api.getPool(pool, ctx.signal)\n } catch (error) {\n state = 'warn'\n lines.push(`pool ${pool} not readable: ${failureLine(error)}`)\n continue\n }\n if (!found) state = 'warn'\n lines.push(found ? `pool ${pool} found` : `pool ${pool} not found`)\n }\n return { state, lines }\n },\n\n async acquire({ options, settings, session }, ctx) {\n if (session.platform !== 'web' || session.engine !== 'chromium') {\n const what = session.platform === 'web' ? session.engine : session.platform\n throw new PluginError(`Kernel runs Chromium web sessions only, not ${what}.`, {\n kind: 'refused',\n })\n }\n const profile = profileBody(settings, session)\n const api = createKernelApi(options)\n if (settings.pool !== undefined) {\n const fromCreate = CREATE_ONLY_SETTINGS.find((name) => settings[name] !== undefined)\n if (fromCreate !== undefined) {\n throw new PluginError(\n `${fromCreate} comes from the pool's own configuration; remove it from a remote with pool.`,\n { kind: 'refused', path: `settings.${fromCreate}` },\n )\n }\n const body = { ...(profile === undefined ? {} : { profile }), ...settings.extra }\n const reuse = session.context !== 'reuse'\n return acquireFromPool(api, settings.pool, body, reuse, ctx)\n }\n // The create is not cancelled: once Kernel has a browser, the handle must come back to be\n // deleted, and the kit releases a handle that arrives after a cancel was answered.\n return cdpAnswer(await api.createBrowser(createBody(settings, profile)), {})\n },\n\n async release({ options, handle }, ctx) {\n const parsed = HandleSchema.safeParse(handle)\n if (!parsed.success) {\n throw new PluginError('A Kernel handle names its browser by sessionId.', { kind: 'refused' })\n }\n const { sessionId, pool, reuse } = parsed.data\n const api = createKernelApi(options)\n if (pool === undefined) await api.deleteBrowser(sessionId, ctx.signal)\n else await api.releaseToPool(pool, sessionId, reuse ?? false, ctx.signal)\n },\n})\n"],"mappings":"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"}
|