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
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Browser-side recorder. Injected via addInitScript, so it runs before page
3
+ * scripts on every navigation. It reports candidate targets; the Node side
4
+ * decides which one survives (see recorder.ts).
5
+ */
6
+ export const RECORDER_SOURCE = String.raw `
7
+ (() => {
8
+ if (window.__trawlRecorderInstalled) return;
9
+ window.__trawlRecorderInstalled = true;
10
+
11
+ const MARK = 'data-trawl-rec-el';
12
+ const state = { paused: false, mode: 'record', pendingFill: null };
13
+
14
+ const inOverlay = (el) => !!(el && el.closest && el.closest('[data-trawl-overlay]'));
15
+
16
+ const cssPath = (el) => {
17
+ const parts = [];
18
+ let node = el;
19
+ while (node && node.nodeType === 1 && parts.length < 6) {
20
+ let part = node.tagName.toLowerCase();
21
+ if (node.id) { parts.unshift('#' + CSS.escape(node.id)); break; }
22
+ const parent = node.parentElement;
23
+ if (parent) {
24
+ const sameTag = [...parent.children].filter((c) => c.tagName === node.tagName);
25
+ if (sameTag.length > 1) part += ':nth-of-type(' + (sameTag.indexOf(node) + 1) + ')';
26
+ }
27
+ parts.unshift(part);
28
+ node = node.parentElement;
29
+ }
30
+ return parts.join(' > ');
31
+ };
32
+
33
+ const implicitRole = (el) => {
34
+ const tag = el.tagName.toLowerCase();
35
+ if (el.getAttribute('role')) return el.getAttribute('role');
36
+ if (tag === 'button') return 'button';
37
+ if (tag === 'a' && el.hasAttribute('href')) return 'link';
38
+ if (tag === 'select') return 'combobox';
39
+ if (tag === 'textarea') return 'textbox';
40
+ if (tag === 'input') {
41
+ const type = (el.getAttribute('type') || 'text').toLowerCase();
42
+ if (type === 'checkbox') return 'checkbox';
43
+ if (type === 'radio') return 'radio';
44
+ if (type === 'submit' || type === 'button') return 'button';
45
+ return 'textbox';
46
+ }
47
+ return null;
48
+ };
49
+
50
+ const accessibleName = (el) => {
51
+ const aria = el.getAttribute('aria-label');
52
+ if (aria) return aria.trim();
53
+ if (el.labels && el.labels[0]) return el.labels[0].textContent.trim();
54
+ const text = (el.textContent || '').trim();
55
+ if (text && text.length <= 60) return text;
56
+ const value = el.getAttribute('value');
57
+ return value ? value.trim() : '';
58
+ };
59
+
60
+ const labelText = (el) => (el.labels && el.labels[0] ? el.labels[0].textContent.trim() : '');
61
+
62
+ /** Ordered candidate targets, best first. */
63
+ const candidates = (el) => {
64
+ const out = [];
65
+ const testId = el.getAttribute('data-testid') || el.getAttribute('data-test-id') || el.getAttribute('data-qa');
66
+ if (testId) out.push({ testId: testId });
67
+ const role = implicitRole(el);
68
+ const name = accessibleName(el);
69
+ if (role && name) out.push({ role: role, name: name });
70
+ const label = labelText(el);
71
+ if (label) out.push({ label: label });
72
+ const placeholder = el.getAttribute('placeholder');
73
+ if (placeholder) out.push({ placeholder: placeholder });
74
+ const own = (el.textContent || '').trim();
75
+ if (own && own.length <= 40) out.push({ text: own });
76
+ out.push({ css: cssPath(el) });
77
+ return out;
78
+ };
79
+
80
+ const emit = (action, el, args) => {
81
+ if (state.paused) return;
82
+ el.setAttribute(MARK, '1');
83
+ return window.__trawlRec({ action: action, candidates: candidates(el), args: args || [] })
84
+ .catch(() => {})
85
+ .then(() => el.removeAttribute(MARK));
86
+ };
87
+
88
+ const flushFill = () => {
89
+ const pending = state.pendingFill;
90
+ if (!pending) return;
91
+ state.pendingFill = null;
92
+ emit('fill', pending.el, [pending.value]);
93
+ };
94
+
95
+ document.addEventListener('click', (e) => {
96
+ const el = e.target;
97
+ if (!el || inOverlay(el)) return;
98
+ flushFill();
99
+ if (state.mode === 'assert') {
100
+ state.mode = 'record';
101
+ const text = (el.textContent || '').trim();
102
+ emit(text ? 'expectText' : 'expectVisible', el, text ? [text] : []);
103
+ return;
104
+ }
105
+ const tag = el.tagName ? el.tagName.toLowerCase() : '';
106
+ const type = (el.getAttribute && (el.getAttribute('type') || '')).toLowerCase();
107
+ if (tag === 'input' && (type === 'checkbox' || type === 'radio')) {
108
+ emit(el.checked ? 'check' : 'uncheck', el, []);
109
+ return;
110
+ }
111
+ emit('click', el, []);
112
+ }, true);
113
+
114
+ document.addEventListener('input', (e) => {
115
+ const el = e.target;
116
+ if (!el || inOverlay(el)) return;
117
+ if (el.tagName === 'SELECT') { emit('select', el, [el.value]); return; }
118
+ if (state.pendingFill && state.pendingFill.el !== el) flushFill();
119
+ state.pendingFill = { el: el, value: el.value };
120
+ }, true);
121
+
122
+ document.addEventListener('change', (e) => {
123
+ if (state.pendingFill && state.pendingFill.el === e.target) flushFill();
124
+ }, true);
125
+
126
+ document.addEventListener('keydown', (e) => {
127
+ if (inOverlay(e.target)) return;
128
+ if (e.key === 'Enter' || e.key === 'Escape') { flushFill(); emit('press', e.target, [e.key]); }
129
+ }, true);
130
+
131
+ window.addEventListener('beforeunload', flushFill);
132
+
133
+ window.__trawlRecorderControl = (command) => {
134
+ if (command === 'pause') state.paused = true;
135
+ if (command === 'resume') state.paused = false;
136
+ if (command === 'assert') state.mode = 'assert';
137
+ if (command === 'flush') flushFill();
138
+ return { paused: state.paused, mode: state.mode };
139
+ };
140
+
141
+ const overlay = document.createElement('div');
142
+ overlay.setAttribute('data-trawl-overlay', '1');
143
+ overlay.style.cssText =
144
+ 'position:fixed;z-index:2147483647;right:12px;bottom:12px;display:flex;gap:6px;' +
145
+ 'padding:8px;border-radius:8px;background:#111;color:#fff;font:12px system-ui;box-shadow:0 2px 12px rgba(0,0,0,.4)';
146
+ const button = (label, command) => {
147
+ const b = document.createElement('button');
148
+ b.textContent = label;
149
+ b.style.cssText = 'all:unset;cursor:pointer;padding:4px 8px;border-radius:4px;background:#333;color:#fff';
150
+ b.addEventListener('click', () => {
151
+ const next = window.__trawlRecorderControl(command === 'pause' && state.paused ? 'resume' : command);
152
+ if (command === 'pause') b.textContent = next.paused ? '▶' : '⏸';
153
+ }, true);
154
+ return b;
155
+ };
156
+ const install = () => {
157
+ if (!document.body || document.querySelector('[data-trawl-overlay]')) return;
158
+ overlay.append(button('⏸', 'pause'), button('✓ assert', 'assert'));
159
+ document.body.appendChild(overlay);
160
+ };
161
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);
162
+ else install();
163
+ })();
164
+ `;
165
+ //# sourceMappingURL=recorderInject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorderInject.js","sourceRoot":"","sources":["../src/recorderInject.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8JxC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { runsDir } from "./workspace.js";
4
+ /** Delete all but the `keep` most recent run folders. Returns what was removed. */
5
+ export async function pruneRuns(root, keep) {
6
+ const dir = runsDir(root);
7
+ let entries;
8
+ try {
9
+ entries = await fs.readdir(dir, { withFileTypes: true });
10
+ }
11
+ catch {
12
+ return [];
13
+ }
14
+ const dated = await Promise.all(entries
15
+ .filter((e) => e.isDirectory())
16
+ .map(async (e) => ({ name: e.name, mtime: (await fs.stat(path.join(dir, e.name))).mtimeMs })));
17
+ const doomed = dated.sort((a, b) => b.mtime - a.mtime).slice(keep);
18
+ await Promise.all(doomed.map((d) => fs.rm(path.join(dir, d.name), { recursive: true, force: true })));
19
+ return doomed.map((d) => d.name);
20
+ }
21
+ //# sourceMappingURL=retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../src/retention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,OAAO;SACJ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SAC9B,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAChG,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtG,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
package/dist/routes.js ADDED
@@ -0,0 +1,140 @@
1
+ import { SessionStore } from "./sessions.js";
2
+ import { Runner } from "./runner.js";
3
+ import { getDevice, loadDevices, saveDevice } from "./devices.js";
4
+ import { listScripts, readScript, writeScript } from "./workspace.js";
5
+ import { collect } from "./sandbox.js";
6
+ import { AgentError } from "./types.js";
7
+ import { RecorderStore } from "./recorder.js";
8
+ import { performAction, snapshot } from "./control.js";
9
+ import { readGuide } from "./guide.js";
10
+ export function buildRoutes(deps) {
11
+ const workspace = deps.workspace;
12
+ const sessions = deps.sessions ?? new SessionStore();
13
+ const trawlProxyPort = deps.trawlProxyPort ?? 8080;
14
+ const runner = deps.runner ?? new Runner({ sessions, workspace, trawlProxyPort });
15
+ const recorder = new RecorderStore({ sessions, workspace });
16
+ const body = (value) => {
17
+ if (!value || typeof value !== "object")
18
+ throw new AgentError("script", "a JSON body is required");
19
+ return value;
20
+ };
21
+ return [
22
+ {
23
+ method: "GET",
24
+ path: "/devices",
25
+ handler: async () => ({ devices: await loadDevices(workspace), sessions: sessions.list() }),
26
+ },
27
+ { method: "POST", path: "/devices", handler: async (_r, _p, b) => ({ device: await saveDevice(workspace, b) }) },
28
+ {
29
+ method: "POST",
30
+ path: "/sessions",
31
+ handler: async (_r, _p, b) => {
32
+ const { deviceId, headless } = body(b);
33
+ const device = await getDevice(workspace, deviceId);
34
+ return { session: await sessions.start(device, { trawlProxyPort, headless }) };
35
+ },
36
+ },
37
+ { method: "GET", path: "/sessions", handler: async () => ({ sessions: sessions.list() }) },
38
+ {
39
+ method: "DELETE",
40
+ path: "/sessions/:id",
41
+ handler: async (_r, p) => {
42
+ await sessions.stop(p.id);
43
+ return { ok: true };
44
+ },
45
+ },
46
+ { method: "GET", path: "/scripts", handler: async () => ({ scripts: await listScripts(workspace) }) },
47
+ {
48
+ method: "GET",
49
+ path: "/scripts/read",
50
+ handler: async (_r, p) => ({ code: await readScript(workspace, p.path) }),
51
+ },
52
+ {
53
+ method: "POST",
54
+ path: "/scripts/write",
55
+ handler: async (_r, _p, b) => {
56
+ const { path: rel, code } = body(b);
57
+ const validation = await collect(code);
58
+ if (validation.errors.length) {
59
+ throw new AgentError("script", `refusing to write: ${validation.errors.map((e) => e.message).join("; ")}`);
60
+ }
61
+ await writeScript(workspace, rel, code);
62
+ return { path: rel, steps: validation.steps.length };
63
+ },
64
+ },
65
+ {
66
+ method: "POST",
67
+ path: "/scripts/validate",
68
+ handler: async (_r, _p, b) => {
69
+ const { code, path: rel } = body(b);
70
+ const source = code ?? (await readScript(workspace, rel));
71
+ return collect(source);
72
+ },
73
+ },
74
+ {
75
+ method: "POST",
76
+ path: "/runs",
77
+ handler: async (_r, _p, b) => {
78
+ const input = body(b);
79
+ const code = input.code ?? (await readScript(workspace, input.path));
80
+ const device = await getDevice(workspace, input.deviceId);
81
+ return runner.start({
82
+ code,
83
+ scriptPath: input.path,
84
+ device,
85
+ sessionId: input.sessionId,
86
+ env: input.env ?? {},
87
+ secrets: input.secrets ?? {},
88
+ headless: input.headless,
89
+ });
90
+ },
91
+ },
92
+ { method: "GET", path: "/runs", handler: async (_r, p) => ({ runs: runner.list(Number(p.limit ?? 20)) }) },
93
+ {
94
+ method: "GET",
95
+ path: "/runs/:id",
96
+ handler: async (_r, p) => {
97
+ const report = runner.get(p.id);
98
+ if (!report)
99
+ throw new AgentError("agent", `unknown run: ${p.id}`);
100
+ return report;
101
+ },
102
+ },
103
+ { method: "DELETE", path: "/runs/:id", handler: async (_r, p) => ({ cancelled: runner.cancel(p.id) }) },
104
+ {
105
+ method: "POST",
106
+ path: "/record/start",
107
+ handler: async (_r, _p, b) => {
108
+ const { sessionId, deviceId, url } = body(b);
109
+ if (!sessionId && !deviceId)
110
+ throw new AgentError("script", "deviceId or sessionId is required");
111
+ const id = sessionId ?? (await sessions.start(await getDevice(workspace, deviceId), { trawlProxyPort })).sessionId;
112
+ return recorder.start(id, url);
113
+ },
114
+ },
115
+ { method: "GET", path: "/record/:id", handler: async (_r, p) => recorder.get(p.id) },
116
+ {
117
+ method: "POST",
118
+ path: "/record/:id/stop",
119
+ handler: async (_r, p, b) => recorder.stop(p.id, (b ?? {})),
120
+ },
121
+ {
122
+ method: "POST",
123
+ path: "/control/snapshot",
124
+ handler: async (_r, _p, b) => {
125
+ const { sessionId } = body(b);
126
+ return { nodes: await snapshot(sessions.get(sessionId).page) };
127
+ },
128
+ },
129
+ {
130
+ method: "POST",
131
+ path: "/control/do",
132
+ handler: async (_r, _p, b) => {
133
+ const { sessionId, ...action } = body(b);
134
+ return performAction(sessions.get(sessionId).page, action);
135
+ },
136
+ },
137
+ { method: "GET", path: "/guide", handler: async () => ({ guide: await readGuide() }) },
138
+ ];
139
+ }
140
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}