@commandgarden/cli 1.4.1 → 2.0.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 (81) hide show
  1. package/dist/main.js +6 -12
  2. package/node_modules/@commandgarden/app/dist/client/assets/index-CThI0UCl.css +1 -0
  3. package/node_modules/@commandgarden/app/dist/client/assets/index-WANip_um.js +252 -0
  4. package/node_modules/@commandgarden/app/dist/client/index.html +2 -2
  5. package/node_modules/@commandgarden/app/dist/server/journal/journal.config.d.ts +25 -0
  6. package/node_modules/@commandgarden/app/dist/server/journal/journal.config.js +31 -0
  7. package/node_modules/@commandgarden/app/dist/server/journal/journal.service.d.ts +37 -0
  8. package/node_modules/@commandgarden/app/dist/server/journal/journal.service.js +142 -0
  9. package/node_modules/@commandgarden/app/dist/server/journal/journal.types.d.ts +91 -0
  10. package/node_modules/@commandgarden/app/dist/server/journal/journal.types.js +4 -0
  11. package/node_modules/@commandgarden/app/dist/server/journal/sources/git.source.d.ts +21 -0
  12. package/node_modules/@commandgarden/app/dist/server/journal/sources/git.source.js +72 -0
  13. package/node_modules/@commandgarden/app/dist/server/journal/sources/jira.source.d.ts +15 -0
  14. package/node_modules/@commandgarden/app/dist/server/journal/sources/jira.source.js +42 -0
  15. package/node_modules/@commandgarden/app/dist/server/journal/sources/meetings.source.d.ts +21 -0
  16. package/node_modules/@commandgarden/app/dist/server/journal/sources/meetings.source.js +25 -0
  17. package/node_modules/@commandgarden/app/dist/server/journal/sources/timetracking.source.d.ts +17 -0
  18. package/node_modules/@commandgarden/app/dist/server/journal/sources/timetracking.source.js +83 -0
  19. package/node_modules/@commandgarden/app/dist/server/routes/index.js +2 -0
  20. package/node_modules/@commandgarden/app/dist/server/routes/journal.d.ts +9 -0
  21. package/node_modules/@commandgarden/app/dist/server/routes/journal.js +56 -0
  22. package/node_modules/@commandgarden/chrome/dist/content-script.js +84 -2
  23. package/node_modules/@commandgarden/chrome/dist/content-script.js.map +2 -2
  24. package/node_modules/@commandgarden/chrome/dist/service-worker.js +73 -8
  25. package/node_modules/@commandgarden/chrome/dist/service-worker.js.map +3 -3
  26. package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.eval.js +143 -0
  27. package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.yaml +70 -0
  28. package/node_modules/@commandgarden/daemon/connectors/gcs-kb-content.yaml +58 -0
  29. package/node_modules/@commandgarden/daemon/connectors/gcs-kb-pages.yaml +47 -0
  30. package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.eval.js +115 -0
  31. package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.yaml +54 -0
  32. package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.eval.js +186 -0
  33. package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.yaml +41 -0
  34. package/node_modules/@commandgarden/daemon/connectors/socket-security-news.yaml +63 -0
  35. package/node_modules/@commandgarden/daemon/connectors/tokenmaster-client-trustedby.yaml +62 -0
  36. package/node_modules/@commandgarden/daemon/connectors/tokenmaster-clients-list.yaml +55 -0
  37. package/node_modules/@commandgarden/daemon/connectors/wiz-blog-security.eval.js +29 -0
  38. package/node_modules/@commandgarden/daemon/connectors/wiz-blog-security.yaml +44 -0
  39. package/node_modules/@commandgarden/daemon/dist/daemon-runner.d.ts +12 -0
  40. package/node_modules/@commandgarden/daemon/dist/daemon-runner.d.ts.map +1 -0
  41. package/node_modules/@commandgarden/daemon/dist/daemon-runner.js +65 -0
  42. package/node_modules/@commandgarden/daemon/dist/daemon-runner.js.map +1 -0
  43. package/node_modules/@commandgarden/daemon/dist/server.d.ts.map +1 -1
  44. package/node_modules/@commandgarden/daemon/dist/server.js +87 -13
  45. package/node_modules/@commandgarden/daemon/dist/server.js.map +1 -1
  46. package/node_modules/@commandgarden/daemon/dist/transforms.d.ts +3 -0
  47. package/node_modules/@commandgarden/daemon/dist/transforms.d.ts.map +1 -0
  48. package/node_modules/@commandgarden/daemon/dist/transforms.js +36 -0
  49. package/node_modules/@commandgarden/daemon/dist/transforms.js.map +1 -0
  50. package/node_modules/@commandgarden/daemon/package.json +4 -2
  51. package/node_modules/@commandgarden/shared/dist/connector.d.ts +175 -0
  52. package/node_modules/@commandgarden/shared/dist/connector.d.ts.map +1 -1
  53. package/node_modules/@commandgarden/shared/dist/connector.js +2 -0
  54. package/node_modules/@commandgarden/shared/dist/connector.js.map +1 -1
  55. package/node_modules/@commandgarden/shared/dist/expression/evaluator.d.ts +2 -0
  56. package/node_modules/@commandgarden/shared/dist/expression/evaluator.d.ts.map +1 -1
  57. package/node_modules/@commandgarden/shared/dist/expression/evaluator.js.map +1 -1
  58. package/node_modules/@commandgarden/shared/dist/expression/filters.d.ts.map +1 -1
  59. package/node_modules/@commandgarden/shared/dist/expression/filters.js +5 -0
  60. package/node_modules/@commandgarden/shared/dist/expression/filters.js.map +1 -1
  61. package/node_modules/@commandgarden/shared/dist/fan-out.d.ts +28 -0
  62. package/node_modules/@commandgarden/shared/dist/fan-out.d.ts.map +1 -0
  63. package/node_modules/@commandgarden/shared/dist/fan-out.js +55 -0
  64. package/node_modules/@commandgarden/shared/dist/fan-out.js.map +1 -0
  65. package/node_modules/@commandgarden/shared/dist/index.d.ts +2 -1
  66. package/node_modules/@commandgarden/shared/dist/index.d.ts.map +1 -1
  67. package/node_modules/@commandgarden/shared/dist/index.js +3 -1
  68. package/node_modules/@commandgarden/shared/dist/index.js.map +1 -1
  69. package/node_modules/@commandgarden/shared/dist/loader.d.ts.map +1 -1
  70. package/node_modules/@commandgarden/shared/dist/loader.js +17 -0
  71. package/node_modules/@commandgarden/shared/dist/loader.js.map +1 -1
  72. package/node_modules/@commandgarden/shared/dist/pipeline.d.ts +223 -1
  73. package/node_modules/@commandgarden/shared/dist/pipeline.d.ts.map +1 -1
  74. package/node_modules/@commandgarden/shared/dist/pipeline.js +58 -2
  75. package/node_modules/@commandgarden/shared/dist/pipeline.js.map +1 -1
  76. package/node_modules/@commandgarden/shared/dist/protocol.d.ts +1 -0
  77. package/node_modules/@commandgarden/shared/dist/protocol.d.ts.map +1 -1
  78. package/node_modules/@commandgarden/shared/dist/protocol.js.map +1 -1
  79. package/package.json +1 -1
  80. package/node_modules/@commandgarden/app/dist/client/assets/index-B-_w54e9.css +0 -1
  81. package/node_modules/@commandgarden/app/dist/client/assets/index-Bb1BOK1E.js +0 -252
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Journal Fastify route — POST /api/journal/generate.
3
+ * Reads journal config from user preferences (SQLite), instantiates
4
+ * sources with that config, and generates the weekly report.
5
+ */
6
+ import type { FastifyInstance } from 'fastify';
7
+ import type { DaemonClient } from '@commandgarden/shared';
8
+ import type { AppStore } from '../store.js';
9
+ export declare function journalRoutes(app: FastifyInstance, daemon: DaemonClient, store: AppStore): void;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Journal Fastify route — POST /api/journal/generate.
3
+ * Reads journal config from user preferences (SQLite), instantiates
4
+ * sources with that config, and generates the weekly report.
5
+ */
6
+ import { getJournalConfig } from '../journal/journal.config.js';
7
+ import { GitSource } from '../journal/sources/git.source.js';
8
+ import { JiraSource } from '../journal/sources/jira.source.js';
9
+ import { TimetrackingSource } from '../journal/sources/timetracking.source.js';
10
+ import { MeetingsSource } from '../journal/sources/meetings.source.js';
11
+ import { JournalService } from '../journal/journal.service.js';
12
+ export function journalRoutes(app, daemon, store) {
13
+ app.post('/api/journal/generate', async (req, reply) => {
14
+ const body = req.body;
15
+ // Validate weekStart is present and matches YYYY-MM-DD format
16
+ if (!body?.weekStart || !/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.test(body.weekStart)) {
17
+ return reply.code(400).send({
18
+ ok: false,
19
+ error: 'weekStart is required and must be YYYY-MM-DD format',
20
+ });
21
+ }
22
+ // Build config from user preferences (configured via GUI Config page)
23
+ const config = getJournalConfig(store);
24
+ // Check that required settings are configured
25
+ if (!config.azureDevOps.org || config.azureDevOps.repos.length === 0) {
26
+ return reply.code(400).send({
27
+ ok: false,
28
+ error: 'Journal not configured. Go to Configuration → Journal Settings and set your ADO org and repos.',
29
+ });
30
+ }
31
+ try {
32
+ const start = Date.now();
33
+ // Sources receive config from preferences — no env vars needed
34
+ const git = new GitSource(daemon, config);
35
+ const jira = new JiraSource(daemon);
36
+ const timetracking = new TimetrackingSource(daemon, config);
37
+ const meetings = new MeetingsSource(daemon);
38
+ const service = new JournalService(git, timetracking, meetings, jira);
39
+ const result = await service.generate({ weekStart: body.weekStart });
40
+ // Audit log (non-blocking — failure should not break the response)
41
+ const durationMs = Date.now() - start;
42
+ daemon.post('/api/audit/log', {
43
+ command: 'journal generate',
44
+ args: { weekStart: body.weekStart },
45
+ status: result.status,
46
+ durationMs,
47
+ }).catch(() => { });
48
+ return result;
49
+ }
50
+ catch (err) {
51
+ const message = err instanceof Error ? err.message : 'Unknown error';
52
+ console.error('[journal] generate error:', message);
53
+ return reply.code(500).send({ ok: false, error: message });
54
+ }
55
+ });
56
+ }
@@ -43,6 +43,61 @@
43
43
  return record;
44
44
  });
45
45
  }
46
+ function extractHtml(selector) {
47
+ const el = document.querySelector(selector);
48
+ if (!el) throw new Error(`Element "${selector}" not found`);
49
+ return el.innerHTML;
50
+ }
51
+ function extractTree(rootSelector, group, leaf, pathSeparator) {
52
+ const root = document.querySelector(rootSelector);
53
+ if (!root) throw new Error(`Root "${rootSelector}" not found`);
54
+ function extractField(el, spec) {
55
+ if (spec === "textContent") return el.textContent?.trim().replace(/\s+/g, " ") ?? "";
56
+ if (spec === "href") return el.getAttribute("href") ?? "";
57
+ if (spec.startsWith("attr:")) return el.getAttribute(spec.slice(5)) ?? "";
58
+ return el.textContent?.trim() ?? "";
59
+ }
60
+ function walk(container, ancestors, depth) {
61
+ const results = [];
62
+ for (const child of Array.from(container.children)) {
63
+ if (child.matches(leaf.match)) {
64
+ const row = {};
65
+ for (const [name, spec] of Object.entries(leaf.fields)) {
66
+ row[name] = extractField(child, spec);
67
+ }
68
+ const title = row.title || child.textContent?.trim() || "";
69
+ const section = ancestors.length > 0 ? ancestors[0] : title;
70
+ const pathParts = [...ancestors, title];
71
+ row.section = section;
72
+ row.path = pathParts.join(pathSeparator);
73
+ row.depth = depth;
74
+ results.push(row);
75
+ } else if (child.matches(group.match)) {
76
+ const titleEl = child.querySelector(group.title);
77
+ const groupTitle = titleEl?.textContent?.trim().replace(/\s+/g, " ") ?? "";
78
+ const childContainer = child.querySelector(group.children);
79
+ if (childContainer) {
80
+ results.push(...walk(childContainer, [...ancestors, groupTitle], depth + 1));
81
+ }
82
+ } else {
83
+ results.push(...walk(child, ancestors, depth));
84
+ }
85
+ }
86
+ return results;
87
+ }
88
+ return walk(root, [], 0);
89
+ }
90
+ async function clickAll(selector, pause, maxRounds, settle) {
91
+ for (let round = 0; round < maxRounds; round++) {
92
+ const elements = document.querySelectorAll(selector);
93
+ if (elements.length === 0) break;
94
+ for (const el of Array.from(elements)) {
95
+ el.click();
96
+ if (pause > 0) await new Promise((r) => setTimeout(r, pause));
97
+ }
98
+ }
99
+ if (settle > 0) await new Promise((r) => setTimeout(r, settle));
100
+ }
46
101
  async function clickElement(selector) {
47
102
  const el = document.querySelector(selector);
48
103
  if (!el) throw new Error(`Element "${selector}" not found`);
@@ -86,19 +141,46 @@
86
141
  return void 0;
87
142
  case "extract":
88
143
  return extractData(p.selector, p.fields);
144
+ case "extract_html":
145
+ return extractHtml(p.selector);
146
+ case "extract_tree":
147
+ return extractTree(
148
+ p.root,
149
+ p.group,
150
+ p.leaf,
151
+ p.pathSeparator ?? " / "
152
+ );
89
153
  case "click":
90
154
  await clickElement(p.selector);
91
155
  return void 0;
156
+ case "click_all":
157
+ await clickAll(
158
+ p.selector,
159
+ p.pause ?? 200,
160
+ p.maxRounds ?? 10,
161
+ p.settle ?? 300
162
+ );
163
+ return void 0;
92
164
  case "type":
93
165
  await typeIntoElement(p.selector, p.value);
94
166
  return void 0;
95
- case "fetch":
96
- return fetchFromPage(
167
+ case "fetch": {
168
+ const result = await fetchFromPage(
97
169
  p.url,
98
170
  p.method,
99
171
  p.headers,
100
172
  p.body
101
173
  );
174
+ if (p.dataPath && result != null && typeof result === "object" && !Array.isArray(result)) {
175
+ let nested = result;
176
+ for (const key of p.dataPath.split(".")) {
177
+ if (nested == null || typeof nested !== "object") return [];
178
+ nested = nested[key];
179
+ }
180
+ return Array.isArray(nested) ? nested : [];
181
+ }
182
+ return result;
183
+ }
102
184
  default:
103
185
  throw new Error(`Unknown action: ${req.action}`);
104
186
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/messages.ts", "../src/content/dom-executor.ts", "../src/content/content-script.ts"],
4
- "sourcesContent": ["// src/messages.ts\nconst randomUUID = () => crypto.randomUUID();\n\nexport type DomAction = 'wait' | 'extract' | 'click' | 'type' | 'fetch';\n\nexport interface DomRequest {\n id: string;\n action: DomAction;\n params: Record<string, unknown>;\n}\n\nexport interface DomResponse {\n requestId: string;\n ok: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport function createDomRequest(action: DomAction, params: Record<string, unknown>): DomRequest {\n return { id: randomUUID(), action, params };\n}\n\nexport function createDomResponse(\n requestId: string, ok: boolean, data?: unknown, error?: string,\n): DomResponse {\n return { requestId, ok, data, error };\n}\n\nexport function isDomRequest(value: unknown): value is DomRequest {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.id === 'string' && typeof obj.action === 'string' && typeof obj.params === 'object';\n}\n\nexport function isDomResponse(value: unknown): value is DomResponse {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.requestId === 'string' && typeof obj.ok === 'boolean';\n}\n", "// src/content/dom-executor.ts\n\nexport function waitForSelector(selector: string, timeout: number): Promise<void> {\n return new Promise((resolve, reject) => {\n if (document.querySelector(selector)) { resolve(); return; }\n const interval = 100;\n let elapsed = 0;\n const timer = setInterval(() => {\n if (document.querySelector(selector)) { clearInterval(timer); resolve(); return; }\n elapsed += interval;\n if (elapsed >= timeout) {\n clearInterval(timer);\n reject(new Error(`Selector \"${selector}\" timed out after ${timeout}ms`));\n }\n }, interval);\n });\n}\n\nexport function extractData(\n rowSelector: string,\n fields: Record<string, string>,\n): Record<string, string>[] {\n const rows = document.querySelectorAll(rowSelector);\n return Array.from(rows).map(row => {\n const record: Record<string, string> = {};\n for (const [name, selector] of Object.entries(fields)) {\n record[name] = row.querySelector(selector)?.textContent?.trim() ?? '';\n }\n return record;\n });\n}\n\nexport async function clickElement(selector: string): Promise<void> {\n const el = document.querySelector(selector);\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n (el as HTMLElement).click();\n}\n\nexport async function typeIntoElement(selector: string, value: string): Promise<void> {\n const el = document.querySelector(selector) as HTMLInputElement | null;\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n el.focus();\n el.value = value;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nexport async function fetchFromPage(\n url: string, method = 'GET', headers?: Record<string, string>, body?: string,\n): Promise<unknown> {\n const resp = await fetch(url, {\n method, headers, body, credentials: 'include',\n });\n const contentType = resp.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) return resp.json();\n return resp.text();\n}\n", "// src/content/content-script.ts\nimport { isDomRequest, createDomResponse, type DomRequest } from '../messages.js';\nimport { waitForSelector, extractData, clickElement, typeIntoElement, fetchFromPage } from './dom-executor.js';\n\nchrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {\n if (!isDomRequest(message)) return false;\n const req = message as DomRequest;\n handleRequest(req).then(\n (data) => sendResponse(createDomResponse(req.id, true, data)),\n (err) => sendResponse(createDomResponse(req.id, false, undefined, err.message)),\n );\n return true; // Keep channel open for async response\n});\n\nasync function handleRequest(req: DomRequest): Promise<unknown> {\n const p = req.params;\n switch (req.action) {\n case 'wait':\n await waitForSelector(p.selector as string, (p.timeout as number) ?? 10000);\n return undefined;\n case 'extract':\n return extractData(p.selector as string, p.fields as Record<string, string>);\n case 'click':\n await clickElement(p.selector as string);\n return undefined;\n case 'type':\n await typeIntoElement(p.selector as string, p.value as string);\n return undefined;\n case 'fetch':\n return fetchFromPage(\n p.url as string, p.method as string,\n p.headers as Record<string, string>, p.body as string,\n );\n default:\n throw new Error(`Unknown action: ${req.action}`);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// src/messages.ts\nconst randomUUID = () => crypto.randomUUID();\n\nexport type DomAction = 'wait' | 'extract' | 'extract_tree' | 'extract_html' | 'click' | 'click_all' | 'type' | 'fetch';\n\nexport interface DomRequest {\n id: string;\n action: DomAction;\n params: Record<string, unknown>;\n}\n\nexport interface DomResponse {\n requestId: string;\n ok: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport function createDomRequest(action: DomAction, params: Record<string, unknown>): DomRequest {\n return { id: randomUUID(), action, params };\n}\n\nexport function createDomResponse(\n requestId: string, ok: boolean, data?: unknown, error?: string,\n): DomResponse {\n return { requestId, ok, data, error };\n}\n\nexport function isDomRequest(value: unknown): value is DomRequest {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.id === 'string' && typeof obj.action === 'string' && typeof obj.params === 'object';\n}\n\nexport function isDomResponse(value: unknown): value is DomResponse {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.requestId === 'string' && typeof obj.ok === 'boolean';\n}\n", "// src/content/dom-executor.ts\n\nexport function waitForSelector(selector: string, timeout: number): Promise<void> {\n return new Promise((resolve, reject) => {\n if (document.querySelector(selector)) { resolve(); return; }\n const interval = 100;\n let elapsed = 0;\n const timer = setInterval(() => {\n if (document.querySelector(selector)) { clearInterval(timer); resolve(); return; }\n elapsed += interval;\n if (elapsed >= timeout) {\n clearInterval(timer);\n reject(new Error(`Selector \"${selector}\" timed out after ${timeout}ms`));\n }\n }, interval);\n });\n}\n\nexport function extractData(\n rowSelector: string,\n fields: Record<string, string>,\n): Record<string, string>[] {\n const rows = document.querySelectorAll(rowSelector);\n return Array.from(rows).map(row => {\n const record: Record<string, string> = {};\n for (const [name, selector] of Object.entries(fields)) {\n record[name] = row.querySelector(selector)?.textContent?.trim() ?? '';\n }\n return record;\n });\n}\n\nexport function extractHtml(selector: string): string {\n const el = document.querySelector(selector);\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n return el.innerHTML;\n}\n\nexport function extractTree(\n rootSelector: string,\n group: { match: string; title: string; children: string },\n leaf: { match: string; fields: Record<string, string> },\n pathSeparator: string,\n): Record<string, unknown>[] {\n const root = document.querySelector(rootSelector);\n if (!root) throw new Error(`Root \"${rootSelector}\" not found`);\n\n function extractField(el: Element, spec: string): string {\n if (spec === 'textContent') return el.textContent?.trim().replace(/\\s+/g, ' ') ?? '';\n if (spec === 'href') return el.getAttribute('href') ?? '';\n if (spec.startsWith('attr:')) return el.getAttribute(spec.slice(5)) ?? '';\n return el.textContent?.trim() ?? '';\n }\n\n function walk(container: Element, ancestors: string[], depth: number): Record<string, unknown>[] {\n const results: Record<string, unknown>[] = [];\n for (const child of Array.from(container.children)) {\n if (child.matches(leaf.match)) {\n const row: Record<string, unknown> = {};\n for (const [name, spec] of Object.entries(leaf.fields)) {\n row[name] = extractField(child, spec);\n }\n const title = (row.title as string) || child.textContent?.trim() || '';\n const section = ancestors.length > 0 ? ancestors[0] : title;\n const pathParts = [...ancestors, title];\n row.section = section;\n row.path = pathParts.join(pathSeparator);\n row.depth = depth;\n results.push(row);\n } else if (child.matches(group.match)) {\n const titleEl = child.querySelector(group.title);\n const groupTitle = titleEl?.textContent?.trim().replace(/\\s+/g, ' ') ?? '';\n const childContainer = child.querySelector(group.children);\n if (childContainer) {\n results.push(...walk(childContainer, [...ancestors, groupTitle], depth + 1));\n }\n } else {\n results.push(...walk(child, ancestors, depth));\n }\n }\n return results;\n }\n\n return walk(root, [], 0);\n}\n\nexport async function clickAll(\n selector: string, pause: number, maxRounds: number, settle: number,\n): Promise<void> {\n for (let round = 0; round < maxRounds; round++) {\n const elements = document.querySelectorAll(selector);\n if (elements.length === 0) break;\n for (const el of Array.from(elements)) {\n (el as HTMLElement).click();\n if (pause > 0) await new Promise(r => setTimeout(r, pause));\n }\n }\n if (settle > 0) await new Promise(r => setTimeout(r, settle));\n}\n\nexport async function clickElement(selector: string): Promise<void> {\n const el = document.querySelector(selector);\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n (el as HTMLElement).click();\n}\n\nexport async function typeIntoElement(selector: string, value: string): Promise<void> {\n const el = document.querySelector(selector) as HTMLInputElement | null;\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n el.focus();\n el.value = value;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nexport async function fetchFromPage(\n url: string, method = 'GET', headers?: Record<string, string>, body?: string,\n): Promise<unknown> {\n const resp = await fetch(url, {\n method, headers, body, credentials: 'include',\n });\n const contentType = resp.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) return resp.json();\n return resp.text();\n}\n", "// src/content/content-script.ts\nimport { isDomRequest, createDomResponse, type DomRequest } from '../messages.js';\nimport { waitForSelector, extractData, extractHtml, extractTree, clickElement, clickAll, typeIntoElement, fetchFromPage } from './dom-executor.js';\n\nchrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {\n if (!isDomRequest(message)) return false;\n const req = message as DomRequest;\n handleRequest(req).then(\n (data) => sendResponse(createDomResponse(req.id, true, data)),\n (err) => sendResponse(createDomResponse(req.id, false, undefined, err.message)),\n );\n return true; // Keep channel open for async response\n});\n\nasync function handleRequest(req: DomRequest): Promise<unknown> {\n const p = req.params;\n switch (req.action) {\n case 'wait':\n await waitForSelector(p.selector as string, (p.timeout as number) ?? 10000);\n return undefined;\n case 'extract':\n return extractData(p.selector as string, p.fields as Record<string, string>);\n case 'extract_html':\n return extractHtml(p.selector as string);\n case 'extract_tree':\n return extractTree(\n p.root as string,\n p.group as { match: string; title: string; children: string },\n p.leaf as { match: string; fields: Record<string, string> },\n (p.pathSeparator as string) ?? ' / ',\n );\n case 'click':\n await clickElement(p.selector as string);\n return undefined;\n case 'click_all':\n await clickAll(\n p.selector as string,\n (p.pause as number) ?? 200,\n (p.maxRounds as number) ?? 10,\n (p.settle as number) ?? 300,\n );\n return undefined;\n case 'type':\n await typeIntoElement(p.selector as string, p.value as string);\n return undefined;\n case 'fetch': {\n const result = await fetchFromPage(\n p.url as string, p.method as string,\n p.headers as Record<string, string>, p.body as string,\n );\n if (p.dataPath && result != null && typeof result === 'object' && !Array.isArray(result)) {\n let nested: unknown = result;\n for (const key of (p.dataPath as string).split('.')) {\n if (nested == null || typeof nested !== 'object') return [];\n nested = (nested as Record<string, unknown>)[key];\n }\n return Array.isArray(nested) ? nested : [];\n }\n return result;\n }\n default:\n throw new Error(`Unknown action: ${req.action}`);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4227,7 +4227,10 @@ var STEP_CAPABILITY_MAP = {
4227
4227
  navigate: "navigate",
4228
4228
  wait: "navigate",
4229
4229
  extract: "dom_read",
4230
+ extract_tree: "dom_read",
4231
+ extract_html: "dom_read",
4230
4232
  click: "dom_write",
4233
+ click_all: "dom_write",
4231
4234
  type: "dom_write",
4232
4235
  intercept: "intercept_response",
4233
4236
  cookie: "cookie_read",
@@ -4235,6 +4238,7 @@ var STEP_CAPABILITY_MAP = {
4235
4238
  map: null,
4236
4239
  filter: null,
4237
4240
  set: null,
4241
+ transform: null,
4238
4242
  js_evaluate: "js_evaluate"
4239
4243
  };
4240
4244
  var navigateStepSchema = external_exports.object({
@@ -4277,7 +4281,8 @@ var fetchStepSchema = external_exports.object({
4277
4281
  method: external_exports.string().optional(),
4278
4282
  headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
4279
4283
  body: external_exports.string().optional(),
4280
- as: external_exports.string().optional()
4284
+ as: external_exports.string().optional(),
4285
+ dataPath: external_exports.string().optional()
4281
4286
  });
4282
4287
  var mapStepSchema = external_exports.object({
4283
4288
  step: external_exports.literal("map"),
@@ -4294,6 +4299,39 @@ var setStepSchema = external_exports.object({
4294
4299
  name: external_exports.string(),
4295
4300
  value: external_exports.string()
4296
4301
  });
4302
+ var extractHtmlStepSchema = external_exports.object({
4303
+ step: external_exports.literal("extract_html"),
4304
+ selector: external_exports.string(),
4305
+ as: external_exports.string()
4306
+ });
4307
+ var transformStepSchema = external_exports.object({
4308
+ step: external_exports.literal("transform"),
4309
+ type: external_exports.string(),
4310
+ input: external_exports.string(),
4311
+ as: external_exports.string(),
4312
+ options: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
4313
+ });
4314
+ var extractTreeStepSchema = external_exports.object({
4315
+ step: external_exports.literal("extract_tree"),
4316
+ root: external_exports.string(),
4317
+ group: external_exports.object({
4318
+ match: external_exports.string(),
4319
+ title: external_exports.string(),
4320
+ children: external_exports.string()
4321
+ }),
4322
+ leaf: external_exports.object({
4323
+ match: external_exports.string(),
4324
+ fields: external_exports.record(external_exports.string(), external_exports.string())
4325
+ }),
4326
+ pathSeparator: external_exports.string().optional()
4327
+ });
4328
+ var clickAllStepSchema = external_exports.object({
4329
+ step: external_exports.literal("click_all"),
4330
+ selector: external_exports.string(),
4331
+ pause: external_exports.number().positive().optional(),
4332
+ maxRounds: external_exports.number().positive().optional(),
4333
+ settle: external_exports.number().nonnegative().optional()
4334
+ });
4297
4335
  var jsEvaluateStepSchema = external_exports.object({
4298
4336
  step: external_exports.literal("js_evaluate"),
4299
4337
  code: external_exports.string().optional(),
@@ -4312,6 +4350,10 @@ var pipelineStepSchema = external_exports.discriminatedUnion("step", [
4312
4350
  mapStepSchema,
4313
4351
  filterStepSchema,
4314
4352
  setStepSchema,
4353
+ extractHtmlStepSchema,
4354
+ transformStepSchema,
4355
+ extractTreeStepSchema,
4356
+ clickAllStepSchema,
4315
4357
  jsEvaluateStepSchema
4316
4358
  ]);
4317
4359
 
@@ -4323,7 +4365,8 @@ var connectorArgSchema = external_exports.object({
4323
4365
  required: external_exports.boolean().default(false),
4324
4366
  default: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]).optional(),
4325
4367
  help: external_exports.string().optional(),
4326
- pattern: external_exports.string().optional()
4368
+ pattern: external_exports.string().optional(),
4369
+ enum: external_exports.array(external_exports.string()).optional()
4327
4370
  });
4328
4371
  var connectorColumnSchema = external_exports.object({
4329
4372
  name: external_exports.string(),
@@ -4335,6 +4378,7 @@ var connectorSchema = external_exports.object({
4335
4378
  version: external_exports.string(),
4336
4379
  description: external_exports.string().optional(),
4337
4380
  access: external_exports.enum(["read", "write"]).default("read"),
4381
+ vars: external_exports.record(external_exports.unknown()).optional(),
4338
4382
  domains: external_exports.array(external_exports.string()).min(1),
4339
4383
  capabilities: external_exports.array(capabilityEnum).min(1),
4340
4384
  args: external_exports.array(connectorArgSchema).optional().default([]),
@@ -6776,7 +6820,12 @@ var BUILT_IN_FILTERS = {
6776
6820
  number: (value) => Number(value),
6777
6821
  trim: (value) => String(value).trim(),
6778
6822
  upper: (value) => String(value).toUpperCase(),
6779
- lower: (value) => String(value).toLowerCase()
6823
+ lower: (value) => String(value).toLowerCase(),
6824
+ lookup: (value, map2) => {
6825
+ if (map2 == null || typeof map2 !== "object")
6826
+ return void 0;
6827
+ return map2[String(value)];
6828
+ }
6780
6829
  };
6781
6830
 
6782
6831
  // ../shared/dist/expression/evaluator.js
@@ -6832,8 +6881,9 @@ var PipelineContext = class {
6832
6881
  cookies = {};
6833
6882
  data = [];
6834
6883
  args;
6835
- constructor(args) {
6884
+ constructor(args, initialVars) {
6836
6885
  this.args = args;
6886
+ if (initialVars) Object.assign(this.vars, initialVars);
6837
6887
  }
6838
6888
  setVar(name, value) {
6839
6889
  this.vars[name] = value;
@@ -6841,6 +6891,9 @@ var PipelineContext = class {
6841
6891
  getVar(name) {
6842
6892
  return this.vars[name];
6843
6893
  }
6894
+ getVars() {
6895
+ return { ...this.vars };
6896
+ }
6844
6897
  setCookies(cookies) {
6845
6898
  Object.assign(this.cookies, cookies);
6846
6899
  }
@@ -6861,7 +6914,8 @@ var PipelineContext = class {
6861
6914
  const rowCtx = {
6862
6915
  args: this.args,
6863
6916
  vars: { ...this.vars, row },
6864
- cookies: this.cookies
6917
+ cookies: this.cookies,
6918
+ row
6865
6919
  };
6866
6920
  mapped[key] = interpolate(expr, rowCtx);
6867
6921
  }
@@ -6917,7 +6971,7 @@ var PipelineRunner = class {
6917
6971
  }
6918
6972
  }
6919
6973
  async run(connector, args) {
6920
- const ctx = new PipelineContext(args);
6974
+ const ctx = new PipelineContext(args, connector.vars);
6921
6975
  let tabId = -1;
6922
6976
  const stepSummaries = [];
6923
6977
  try {
@@ -6935,6 +6989,7 @@ var PipelineRunner = class {
6935
6989
  }
6936
6990
  case "wait":
6937
6991
  case "click":
6992
+ case "click_all":
6938
6993
  await this.adapter.executeInContent(tabId, step);
6939
6994
  break;
6940
6995
  case "type":
@@ -6948,6 +7003,16 @@ var PipelineRunner = class {
6948
7003
  ctx.setData(data);
6949
7004
  break;
6950
7005
  }
7006
+ case "extract_tree": {
7007
+ const treeData = await this.adapter.executeInContent(tabId, step);
7008
+ ctx.setData(treeData);
7009
+ break;
7010
+ }
7011
+ case "extract_html": {
7012
+ const html = await this.adapter.executeInContent(tabId, step);
7013
+ if (step.as) ctx.setVar(step.as, html);
7014
+ break;
7015
+ }
6951
7016
  case "fetch": {
6952
7017
  const fetchStep = {
6953
7018
  ...step,
@@ -7014,9 +7079,9 @@ var PipelineRunner = class {
7014
7079
  throw err;
7015
7080
  }
7016
7081
  }
7017
- return { id: "", ok: true, data: ctx.getData(), steps: stepSummaries };
7082
+ return { id: "", ok: true, data: ctx.getData(), vars: ctx.getVars(), steps: stepSummaries };
7018
7083
  } catch (err) {
7019
- return { id: "", ok: false, data: [], error: err instanceof Error ? err.message : String(err), steps: stepSummaries };
7084
+ return { id: "", ok: false, data: [], vars: {}, error: err instanceof Error ? err.message : String(err), steps: stepSummaries };
7020
7085
  }
7021
7086
  }
7022
7087
  };