trawl-devices-agent 0.1.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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/auth.js +23 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/codegen.js +41 -0
  6. package/dist/codegen.js.map +1 -0
  7. package/dist/control.js +106 -0
  8. package/dist/control.js.map +1 -0
  9. package/dist/devices.js +69 -0
  10. package/dist/devices.js.map +1 -0
  11. package/dist/guide.js +14 -0
  12. package/dist/guide.js.map +1 -0
  13. package/dist/index.js +40 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/interpolate.js +20 -0
  16. package/dist/interpolate.js.map +1 -0
  17. package/dist/mask.js +19 -0
  18. package/dist/mask.js.map +1 -0
  19. package/dist/recorder.js +99 -0
  20. package/dist/recorder.js.map +1 -0
  21. package/dist/recorderInject.js +165 -0
  22. package/dist/recorderInject.js.map +1 -0
  23. package/dist/retention.js +21 -0
  24. package/dist/retention.js.map +1 -0
  25. package/dist/routes.js +140 -0
  26. package/dist/routes.js.map +1 -0
  27. package/dist/runner.js +373 -0
  28. package/dist/runner.js.map +1 -0
  29. package/dist/sandbox.js +80 -0
  30. package/dist/sandbox.js.map +1 -0
  31. package/dist/server.js +100 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/sessions.js +85 -0
  34. package/dist/sessions.js.map +1 -0
  35. package/dist/steps.js +27 -0
  36. package/dist/steps.js.map +1 -0
  37. package/dist/targets.js +68 -0
  38. package/dist/targets.js.map +1 -0
  39. package/dist/traffic.js +116 -0
  40. package/dist/traffic.js.map +1 -0
  41. package/dist/transform.js +110 -0
  42. package/dist/transform.js.map +1 -0
  43. package/dist/types.js +12 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/version.js +5 -0
  46. package/dist/version.js.map +1 -0
  47. package/dist/workspace.js +50 -0
  48. package/dist/workspace.js.map +1 -0
  49. package/package.json +50 -0
  50. package/skills/writing-device-scripts/SKILL.md +59 -0
package/dist/runner.js ADDED
@@ -0,0 +1,373 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { randomBytes } from "node:crypto";
3
+ import path from "node:path";
4
+ import { AgentError } from "./types.js";
5
+ import { runInSandbox } from "./sandbox.js";
6
+ import { describeTarget, isRegExp, toLocator, toMatcher } from "./targets.js";
7
+ import { runDir } from "./workspace.js";
8
+ import { TrafficBuffer, describeMatcher, parseMatcher } from "./traffic.js";
9
+ import { interpolate } from "./interpolate.js";
10
+ import { makeMasker } from "./mask.js";
11
+ const DEFAULT_TIMEOUT = 15_000;
12
+ const MAX_SCRIPT_MS = 10 * 60_000;
13
+ export class Runner {
14
+ deps;
15
+ runs = new Map();
16
+ constructor(deps) {
17
+ this.deps = deps;
18
+ }
19
+ get(runId) {
20
+ return this.runs.get(runId)?.report;
21
+ }
22
+ list(limit = 20) {
23
+ return [...this.runs.values()]
24
+ .map((r) => r.report)
25
+ .sort((a, b) => b.startedAt - a.startedAt)
26
+ .slice(0, limit);
27
+ }
28
+ cancel(runId) {
29
+ const state = this.runs.get(runId);
30
+ if (!state || state.report.status !== "running")
31
+ return false;
32
+ state.cancelled = true;
33
+ return true;
34
+ }
35
+ /** Starts the run in the background and returns the initial report. */
36
+ async start(input) {
37
+ const runId = `r_${randomBytes(4).toString("hex")}`;
38
+ const report = {
39
+ runId,
40
+ script: input.scriptPath ?? null,
41
+ device: input.device.id,
42
+ status: "running",
43
+ startedAt: Date.now(),
44
+ durationMs: 0,
45
+ steps: [],
46
+ artifacts: { trace: null, video: null },
47
+ warnings: [],
48
+ };
49
+ const state = { report, cancelled: false };
50
+ this.runs.set(runId, state);
51
+ void this.execute(input, state).catch((err) => {
52
+ state.report.status = "error";
53
+ state.report.warnings.push(`runner crashed: ${err.message}`);
54
+ });
55
+ return report;
56
+ }
57
+ async execute(input, state) {
58
+ const { report } = state;
59
+ const dir = runDir(this.deps.workspace, report.runId);
60
+ await fs.mkdir(dir, { recursive: true });
61
+ const ownSession = !input.sessionId;
62
+ const session = input.sessionId
63
+ ? this.deps.sessions.get(input.sessionId)
64
+ : this.deps.sessions.get((await this.deps.sessions.start(input.device, {
65
+ trawlProxyPort: this.deps.trawlProxyPort,
66
+ headless: input.headless ?? input.device.headless,
67
+ videoDir: input.device.video ? dir : undefined,
68
+ })).sessionId);
69
+ this.deps.sessions.setState(session.sessionId, "running");
70
+ const page = session.page;
71
+ const traceOn = input.device.trace !== "off";
72
+ if (traceOn)
73
+ await session.context.tracing.start({ screenshots: true, snapshots: true });
74
+ let timeout = DEFAULT_TIMEOUT;
75
+ let baseUrl = "";
76
+ let current = null;
77
+ let currentName;
78
+ const traffic = new TrafficBuffer();
79
+ const detachTraffic = await TrafficBuffer.attach(session.context, report.runId, () => (current ? current.index : report.steps.length), traffic);
80
+ const beginStep = (action, args) => {
81
+ if (state.cancelled)
82
+ throw new AgentError("script", "run cancelled");
83
+ const step = {
84
+ index: report.steps.length,
85
+ action,
86
+ args,
87
+ ...(currentName ? { name: currentName } : {}),
88
+ status: "passed",
89
+ startedAt: Date.now(),
90
+ durationMs: 0,
91
+ flows: [],
92
+ };
93
+ report.steps.push(step);
94
+ current = step;
95
+ return step;
96
+ };
97
+ const endStep = (step) => {
98
+ step.durationMs = Date.now() - step.startedAt;
99
+ };
100
+ /** Wraps a step body: timing, failure classification, screenshot on failure. */
101
+ const tracked = (action, body) => async (...rawArgs) => {
102
+ const args = rawArgs.map((a) => interpolate(a, input.env));
103
+ const step = beginStep(action, args);
104
+ try {
105
+ const result = await body(...args);
106
+ endStep(step);
107
+ return result;
108
+ }
109
+ catch (err) {
110
+ const agentErr = err instanceof AgentError
111
+ ? err
112
+ : new AgentError(/Timeout .* exceeded/i.test(err.message) ? "timeout" : "script", err.message);
113
+ step.status = "failed";
114
+ step.error = { kind: agentErr.kind, message: agentErr.message, ...(agentErr.detail ?? {}) };
115
+ const shot = `step-${String(step.index).padStart(2, "0")}.png`;
116
+ await page.screenshot({ path: path.join(dir, shot) }).then(() => {
117
+ step.screenshot = shot;
118
+ }, () => {
119
+ report.warnings.push(`screenshot failed for step ${step.index}`);
120
+ });
121
+ endStep(step);
122
+ throw agentErr;
123
+ }
124
+ };
125
+ const locate = (target) => toLocator(page, target);
126
+ const url = (u) => (baseUrl && u.startsWith("/") ? baseUrl.replace(/\/$/, "") + u : u);
127
+ const assertThat = (ok, message, expected, actual) => {
128
+ if (!ok)
129
+ throw new AgentError("assertion", message, { expected, actual });
130
+ };
131
+ const scope = {
132
+ env: input.env,
133
+ secret: (name) => {
134
+ const value = input.secrets[name];
135
+ if (value === undefined)
136
+ throw new AgentError("script", `secret not provided: ${name}`);
137
+ return value;
138
+ },
139
+ device: tracked("device", async () => { }),
140
+ use: tracked("use", async (options) => {
141
+ if (options?.baseUrl)
142
+ baseUrl = options.baseUrl;
143
+ if (options?.timeout)
144
+ timeout = options.timeout;
145
+ }),
146
+ goto: tracked("goto", async (u) => {
147
+ await page.goto(url(u), { timeout });
148
+ }),
149
+ back: tracked("back", async () => {
150
+ await page.goBack({ timeout });
151
+ }),
152
+ forward: tracked("forward", async () => {
153
+ await page.goForward({ timeout });
154
+ }),
155
+ reload: tracked("reload", async () => {
156
+ await page.reload({ timeout });
157
+ }),
158
+ click: tracked("click", async (t) => {
159
+ await locate(t).click({ timeout });
160
+ }),
161
+ dblclick: tracked("dblclick", async (t) => {
162
+ await locate(t).dblclick({ timeout });
163
+ }),
164
+ fill: tracked("fill", async (t, v) => {
165
+ await locate(t).fill(v, { timeout });
166
+ }),
167
+ type: tracked("type", async (t, v) => {
168
+ await locate(t).pressSequentially(v, { timeout });
169
+ }),
170
+ press: tracked("press", async (t, key) => {
171
+ if (t)
172
+ await locate(t).press(key, { timeout });
173
+ else
174
+ await page.keyboard.press(key);
175
+ }),
176
+ check: tracked("check", async (t) => {
177
+ await locate(t).check({ timeout });
178
+ }),
179
+ uncheck: tracked("uncheck", async (t) => {
180
+ await locate(t).uncheck({ timeout });
181
+ }),
182
+ select: tracked("select", async (t, v) => {
183
+ await locate(t).selectOption(v, { timeout });
184
+ }),
185
+ hover: tracked("hover", async (t) => {
186
+ await locate(t).hover({ timeout });
187
+ }),
188
+ upload: tracked("upload", async (t, file) => {
189
+ await locate(t).setInputFiles(path.resolve(this.deps.workspace, file), { timeout });
190
+ }),
191
+ drag: tracked("drag", async (from, to) => {
192
+ await locate(from).dragTo(locate(to), { timeout });
193
+ }),
194
+ scrollTo: tracked("scrollTo", async (t) => {
195
+ await locate(t).scrollIntoViewIfNeeded({ timeout });
196
+ }),
197
+ waitFor: tracked("waitFor", async (t, stateName = "visible") => {
198
+ await locate(t).waitFor({ state: stateName, timeout });
199
+ }),
200
+ waitForUrl: tracked("waitForUrl", async (pattern) => {
201
+ await page.waitForURL(pattern, { timeout });
202
+ }),
203
+ sleep: tracked("sleep", async (ms) => {
204
+ await page.waitForTimeout(ms);
205
+ }),
206
+ expectVisible: tracked("expectVisible", async (t) => {
207
+ await locate(t).waitFor({ state: "visible", timeout });
208
+ }),
209
+ expectHidden: tracked("expectHidden", async (t) => {
210
+ await locate(t).waitFor({ state: "hidden", timeout });
211
+ }),
212
+ expectText: tracked("expectText", async (t, expected) => {
213
+ const actual = ((await locate(t).textContent({ timeout })) ?? "").trim();
214
+ const matcher = toMatcher(expected);
215
+ const ok = isRegExp(matcher) ? matcher.test(actual) : actual === matcher;
216
+ assertThat(ok, `text of ${describeTarget(t)} does not match`, String(matcher), actual);
217
+ }),
218
+ expectValue: tracked("expectValue", async (t, expected) => {
219
+ const actual = await locate(t).inputValue({ timeout });
220
+ assertThat(actual === expected, `value of ${describeTarget(t)} does not match`, expected, actual);
221
+ }),
222
+ expectUrl: tracked("expectUrl", async (pattern) => {
223
+ try {
224
+ await page.waitForURL(pattern, { timeout });
225
+ }
226
+ catch {
227
+ throw new AgentError("assertion", "url does not match", { expected: pattern, actual: page.url() });
228
+ }
229
+ }),
230
+ expectCount: tracked("expectCount", async (t, expected) => {
231
+ const actual = await locate(t).count();
232
+ assertThat(actual === expected, `count of ${describeTarget(t)} does not match`, String(expected), String(actual));
233
+ }),
234
+ expectAttr: tracked("expectAttr", async (t, name, expected) => {
235
+ const actual = await locate(t).getAttribute(name, { timeout });
236
+ assertThat(actual === expected, `attribute ${name} of ${describeTarget(t)} does not match`, expected, String(actual));
237
+ }),
238
+ expectRequest: tracked("expectRequest", async (matcher) => {
239
+ await traffic.consume(parseMatcher(matcher), timeout);
240
+ }),
241
+ waitForResponse: tracked("waitForResponse", async (matcher) => {
242
+ await traffic.consume(parseMatcher(matcher), timeout);
243
+ }),
244
+ expectResponse: tracked("expectResponse", async (matcher, options = {}) => {
245
+ const hit = await traffic.consume(parseMatcher(matcher), timeout);
246
+ if (options.status !== undefined && hit.status !== options.status) {
247
+ throw new AgentError("assertion", `status of ${describeMatcher(parseMatcher(matcher))} does not match`, {
248
+ expected: String(options.status),
249
+ actual: String(hit.status),
250
+ });
251
+ }
252
+ if (options.jsonPath) {
253
+ const text = (await hit.responseBody()) ?? "";
254
+ let parsed;
255
+ try {
256
+ parsed = JSON.parse(text);
257
+ }
258
+ catch {
259
+ throw new AgentError("assertion", "response body is not JSON", {
260
+ expected: "JSON",
261
+ actual: text.slice(0, 120),
262
+ });
263
+ }
264
+ for (const [pointer, expected] of Object.entries(options.jsonPath)) {
265
+ const actual = readJsonPath(parsed, pointer);
266
+ const matcherValue = toMatcher(expected);
267
+ const ok = isRegExp(matcherValue) ? matcherValue.test(String(actual)) : actual === matcherValue;
268
+ if (!ok) {
269
+ throw new AgentError("assertion", `${pointer} does not match`, {
270
+ expected: String(matcherValue),
271
+ actual: JSON.stringify(actual),
272
+ });
273
+ }
274
+ }
275
+ }
276
+ }),
277
+ expectNoRequest: tracked("expectNoRequest", async (matcher) => {
278
+ const since = current ? current.index : 0;
279
+ const hits = traffic.seenSince(since, parseMatcher(matcher));
280
+ if (hits.length) {
281
+ throw new AgentError("assertion", `unexpected request ${describeMatcher(parseMatcher(matcher))}`, {
282
+ expected: "no request",
283
+ actual: `${hits.length} request(s)`,
284
+ });
285
+ }
286
+ }),
287
+ getText: tracked("getText", async (t) => ((await locate(t).textContent({ timeout })) ?? "").trim()),
288
+ getValue: tracked("getValue", async (t) => locate(t).inputValue({ timeout })),
289
+ getAttr: tracked("getAttr", async (t, name) => locate(t).getAttribute(name, { timeout })),
290
+ getUrl: tracked("getUrl", async () => page.url()),
291
+ count: tracked("count", async (t) => locate(t).count()),
292
+ screenshot: tracked("screenshot", async (name) => {
293
+ const file = `${name ?? `shot-${report.steps.length}`}.png`;
294
+ await page.screenshot({ path: path.join(dir, file) });
295
+ }),
296
+ note: tracked("note", async () => { }),
297
+ step: async (name, fn) => {
298
+ const step = beginStep("step", [name]);
299
+ endStep(step);
300
+ const previous = currentName;
301
+ currentName = name;
302
+ try {
303
+ await fn?.();
304
+ }
305
+ finally {
306
+ currentName = previous;
307
+ }
308
+ },
309
+ };
310
+ // Kept out of `report` until the artifacts are on disk: a poller that sees
311
+ // a final status must be able to read the report and its screenshots.
312
+ let finalStatus = "running";
313
+ try {
314
+ await runInSandbox(input.code, scope, MAX_SCRIPT_MS);
315
+ finalStatus = "passed";
316
+ }
317
+ catch (err) {
318
+ const kind = err instanceof AgentError ? err.kind : "script";
319
+ finalStatus = kind === "assertion" || kind === "timeout" ? "failed" : "error";
320
+ if (!report.steps.some((s) => s.status === "failed")) {
321
+ // The script threw outside a step (e.g. a bare `throw`).
322
+ report.steps.push({
323
+ index: report.steps.length,
324
+ action: "<script>",
325
+ args: [],
326
+ status: "failed",
327
+ startedAt: Date.now(),
328
+ durationMs: 0,
329
+ flows: [],
330
+ error: { kind, message: err.message },
331
+ });
332
+ }
333
+ }
334
+ finally {
335
+ // Steps after a failure were never executed, so they are simply absent:
336
+ // guessing them would be a lie for any script that branches.
337
+ await detachTraffic();
338
+ for (const observed of traffic.all()) {
339
+ const step = report.steps[observed.step];
340
+ if (step)
341
+ step.flows.push({ method: observed.method, url: observed.url, status: observed.status });
342
+ }
343
+ if (traceOn) {
344
+ const keep = input.device.trace === "always" || finalStatus !== "passed";
345
+ const tracePath = path.join(dir, "trace.zip");
346
+ await session.context.tracing.stop(keep ? { path: tracePath } : {}).catch(() => { });
347
+ if (keep)
348
+ report.artifacts.trace = "trace.zip";
349
+ }
350
+ report.durationMs = Date.now() - report.startedAt;
351
+ if (ownSession)
352
+ await this.deps.sessions.stop(session.sessionId);
353
+ else
354
+ this.deps.sessions.setState(session.sessionId, "idle");
355
+ const mask = makeMasker(input.secrets);
356
+ const finished = mask({ ...report, status: finalStatus });
357
+ await fs.writeFile(path.join(dir, "report.json"), JSON.stringify(finished, null, 2), "utf8").catch(() => { });
358
+ state.report = finished;
359
+ }
360
+ }
361
+ }
362
+ /** Supports the `$.a.b[0]` subset — enough for response assertions. */
363
+ function readJsonPath(value, pointer) {
364
+ const parts = pointer.replace(/^\$\.?/, "").split(/[.[\]]+/).filter(Boolean);
365
+ let node = value;
366
+ for (const part of parts) {
367
+ if (node === null || node === undefined)
368
+ return undefined;
369
+ node = node[part];
370
+ }
371
+ return node;
372
+ }
373
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import vm from "node:vm";
2
+ import { AgentError } from "./types.js";
3
+ import { addAwaits, hasBranching, unknownCalls } from "./transform.js";
4
+ import { ALLOWED_GLOBALS, STEP_NAMES } from "./steps.js";
5
+ const MAX_COLLECTED_STEPS = 5000;
6
+ /** Execute `code` with `scope` as its only globals. Steps are awaited automatically. */
7
+ export async function runInSandbox(code, scope, timeoutMs) {
8
+ const transformed = addAwaits(code, STEP_NAMES);
9
+ const context = vm.createContext({ ...scope, console });
10
+ const wrapped = `(async () => {\n${transformed}\n})()`;
11
+ try {
12
+ await vm.runInContext(wrapped, context, { timeout: timeoutMs, displayErrors: true });
13
+ }
14
+ catch (err) {
15
+ if (err instanceof AgentError)
16
+ throw err;
17
+ const message = err.message;
18
+ if (/Script execution timed out/i.test(message)) {
19
+ throw new AgentError("timeout", `script exceeded ${timeoutMs}ms`);
20
+ }
21
+ throw new AgentError("script", message);
22
+ }
23
+ }
24
+ /**
25
+ * Run the script with no-op steps to learn its structure without a browser.
26
+ *
27
+ * Async because the transform awaits every step: the recorded steps only land
28
+ * once the microtask queue drains. A synchronous runaway loop is cut by the vm
29
+ * timeout, an asynchronous one by MAX_COLLECTED_STEPS.
30
+ */
31
+ export async function collect(code) {
32
+ const errors = [];
33
+ const steps = [];
34
+ let approximate = false;
35
+ try {
36
+ approximate = hasBranching(code);
37
+ for (const name of unknownCalls(code, STEP_NAMES, ALLOWED_GLOBALS)) {
38
+ errors.push({ kind: "unknown-step", message: `unknown step: ${name}` });
39
+ }
40
+ }
41
+ catch (err) {
42
+ return { steps, approximate: false, errors: [{ kind: "syntax", message: err.message }] };
43
+ }
44
+ if (errors.length)
45
+ return { steps, approximate, errors };
46
+ let currentName;
47
+ const record = (action) => async (...args) => {
48
+ if (steps.length >= MAX_COLLECTED_STEPS)
49
+ throw new AgentError("script", "too many steps");
50
+ steps.push({ index: steps.length, action, args, ...(currentName ? { name: currentName } : {}) });
51
+ return action.startsWith("get") ? "" : action === "count" ? 0 : undefined;
52
+ };
53
+ const scope = {};
54
+ for (const name of STEP_NAMES)
55
+ scope[name] = record(name);
56
+ scope.step = async (name, fn) => {
57
+ steps.push({ index: steps.length, action: "step", args: [name] });
58
+ const previous = currentName;
59
+ currentName = name;
60
+ try {
61
+ await fn?.();
62
+ }
63
+ finally {
64
+ currentName = previous;
65
+ }
66
+ };
67
+ scope.secret = (name) => `«${name}»`;
68
+ scope.env = new Proxy({}, { get: () => "" });
69
+ try {
70
+ const transformed = addAwaits(code, STEP_NAMES);
71
+ const context = vm.createContext(scope);
72
+ // The timeout only covers synchronous execution; the step cap covers the rest.
73
+ await vm.runInContext(`(async () => {\n${transformed}\n})()`, context, { timeout: 1000 });
74
+ }
75
+ catch (err) {
76
+ errors.push({ kind: "runtime", message: err.message });
77
+ }
78
+ return { steps, approximate, errors };
79
+ }
80
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
package/dist/server.js ADDED
@@ -0,0 +1,100 @@
1
+ import http from "node:http";
2
+ import { timingSafeEqual } from "node:crypto";
3
+ import { AgentError } from "./types.js";
4
+ import { AGENT_VERSION } from "./version.js";
5
+ export function json(res, status, body) {
6
+ const payload = JSON.stringify(body ?? null);
7
+ res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
8
+ res.end(payload);
9
+ }
10
+ const safeEqual = (a, b) => {
11
+ const ab = Buffer.from(a);
12
+ const bb = Buffer.from(b);
13
+ return ab.length === bb.length && timingSafeEqual(ab, bb);
14
+ };
15
+ /** Token + Host + Origin. Returns a status/message when the request must be refused. */
16
+ export function guard(req, token, port) {
17
+ const host = req.headers.host ?? "";
18
+ if (host !== `127.0.0.1:${port}` && host !== `localhost:${port}`) {
19
+ return { ok: false, status: 403, message: "unexpected Host header" };
20
+ }
21
+ if (req.headers.origin)
22
+ return { ok: false, status: 403, message: "cross-origin requests are not allowed" };
23
+ const auth = req.headers.authorization ?? "";
24
+ if (!auth.startsWith("Bearer ") || !safeEqual(auth.slice(7), token)) {
25
+ return { ok: false, status: 401, message: "invalid token" };
26
+ }
27
+ return { ok: true };
28
+ }
29
+ const matchRoute = (routes, method, pathname) => {
30
+ for (const route of routes) {
31
+ if (route.method !== method)
32
+ continue;
33
+ const want = route.path.split("/");
34
+ const got = pathname.split("/");
35
+ if (want.length !== got.length)
36
+ continue;
37
+ const params = {};
38
+ let ok = true;
39
+ for (let i = 0; i < want.length; i++) {
40
+ const w = want[i];
41
+ const g = got[i];
42
+ if (w.startsWith(":"))
43
+ params[w.slice(1)] = decodeURIComponent(g);
44
+ else if (w !== g) {
45
+ ok = false;
46
+ break;
47
+ }
48
+ }
49
+ if (ok)
50
+ return { route, params };
51
+ }
52
+ return null;
53
+ };
54
+ const readBody = async (req) => {
55
+ const chunks = [];
56
+ for await (const chunk of req)
57
+ chunks.push(chunk);
58
+ if (!chunks.length)
59
+ return undefined;
60
+ try {
61
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
62
+ }
63
+ catch {
64
+ throw new AgentError("script", "request body is not valid JSON");
65
+ }
66
+ };
67
+ export function createServer(opts) {
68
+ const mask = opts.mask ?? ((v) => v);
69
+ return http.createServer(async (req, res) => {
70
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${opts.getPort()}`);
71
+ const hostHeader = req.headers.host ?? "";
72
+ const hostOk = hostHeader === `127.0.0.1:${opts.getPort()}` || hostHeader === `localhost:${opts.getPort()}`;
73
+ // /health skips the token check so the UI can detect a running agent.
74
+ if (req.method === "GET" && url.pathname === "/health") {
75
+ if (!hostOk || req.headers.origin)
76
+ return json(res, 403, { error: { kind: "agent", message: "refused" } });
77
+ const authed = guard(req, opts.token, opts.getPort()).ok;
78
+ return json(res, 200, authed ? { ok: true, agent: AGENT_VERSION, ...(opts.health?.() ?? {}) } : { ok: true, agent: AGENT_VERSION });
79
+ }
80
+ const verdict = guard(req, opts.token, opts.getPort());
81
+ if (!verdict.ok)
82
+ return json(res, verdict.status, { error: { kind: "agent", message: verdict.message } });
83
+ const hit = matchRoute(opts.routes, req.method ?? "GET", url.pathname);
84
+ if (!hit) {
85
+ return json(res, 404, { error: { kind: "agent", message: `no route for ${req.method} ${url.pathname}` } });
86
+ }
87
+ try {
88
+ const body = await readBody(req);
89
+ const query = Object.fromEntries(url.searchParams.entries());
90
+ const result = await hit.route.handler(req, { ...query, ...hit.params }, body);
91
+ json(res, 200, mask(result));
92
+ }
93
+ catch (err) {
94
+ const kind = err instanceof AgentError ? err.kind : "agent";
95
+ const status = kind === "script" || kind === "assertion" ? 400 : kind === "device" ? 409 : 500;
96
+ json(res, status, { error: { kind, message: err.message } });
97
+ }
98
+ });
99
+ }
100
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}