@xbrowser/cli 0.16.0 → 1.0.2

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 (52) hide show
  1. package/README.md +17 -26
  2. package/dist/{browser-R7B255ML.js → browser-GITRHHFO.js} +4 -1
  3. package/dist/{browser-GWBH6OJK.js → browser-R56O3CW6.js} +3 -1
  4. package/dist/{browser-I2HJZ7IP.js → browser-ZJOZB5CR.js} +4 -2
  5. package/dist/cdp-driver-BE3FOMRN.js +2803 -0
  6. package/dist/cdp-driver-TOPYJIFL.js +47 -0
  7. package/dist/chunk-2SVQTI2O.js +2794 -0
  8. package/dist/{chunk-KDYXFLAC.js → chunk-ACFE6PKF.js} +1015 -121
  9. package/dist/chunk-BBMRDUYQ.js +260 -0
  10. package/dist/chunk-CAFNSGYM.js +4834 -0
  11. package/dist/{chunk-DTJRVA76.js → chunk-ETCO4SNK.js} +2 -2
  12. package/dist/{chunk-RS6YYWTK.js → chunk-JPA2ZT2R.js} +140 -72
  13. package/dist/chunk-JPHCY4TC.js +260 -0
  14. package/dist/chunk-KFQGP6VL.js +33 -0
  15. package/dist/{chunk-ITKPSIP7.js → chunk-MDAPTB7C.js} +6 -25
  16. package/dist/chunk-OZKD3W4X.js +417 -0
  17. package/dist/chunk-PPG4D2EW.js +2796 -0
  18. package/dist/{chunk-ATFTAKMN.js → chunk-Q4IGYTKR.js} +39 -7
  19. package/dist/{chunk-F3ZWFCJJ.js → chunk-QIK2I3VQ.js} +141 -72
  20. package/dist/chunk-WJRE55TN.js +83 -0
  21. package/dist/cli.js +2358 -1086
  22. package/dist/{convert-4DUWZIKH.js → convert-LB3GJTLR.js} +4 -2
  23. package/dist/{convert-EKQVHKB4.js → convert-R3XXYKC6.js} +2 -2
  24. package/dist/{daemon-client-GX2UYIW4.js → daemon-client-DRCUMNHK.js} +45 -72
  25. package/dist/{daemon-client-XWSSQBEA.js → daemon-client-UZZEHHIV.js} +8 -1
  26. package/dist/daemon-main.js +3067 -1688
  27. package/dist/{extract-JUOQQX4V.js → extract-2ZFW2MX7.js} +1 -1
  28. package/dist/{extract-EGRXZSSK.js → extract-BSYBM4MR.js} +2 -0
  29. package/dist/{filter-OLAE26HN.js → filter-KCFO4RSV.js} +2 -0
  30. package/dist/{filter-VID2GGZ7.js → filter-T7DSZ2X7.js} +1 -1
  31. package/dist/{human-interaction-W753RVJB.js → human-interaction-UKAS5ZXV.js} +2 -2
  32. package/dist/index.d.ts +745 -148
  33. package/dist/index.js +3488 -1719
  34. package/dist/launcher-QUJ4M2VS.js +19 -0
  35. package/dist/launcher-YARP45UY.js +19 -0
  36. package/dist/{network-store-YAF5OIBH.js → network-store-XGZ25FFC.js} +1 -0
  37. package/dist/{network-store-BN6QEZ7R.js → network-store-YVDNUREI.js} +1 -1
  38. package/dist/{parse-action-dsl-T3DYC33D.js → parse-action-dsl-UM333TL2.js} +1 -1
  39. package/dist/{proxy-WKGUCH2C.js → proxy-LV4BJ5RC.js} +1 -1
  40. package/dist/session-recorder-RTDGURIJ.js +8 -0
  41. package/dist/session-recorder-YI7YYM36.js +7 -0
  42. package/dist/session-replayer-GLTUICSD.js +276 -0
  43. package/dist/site-knowledge-SYC6VCDB.js +23 -0
  44. package/package.json +6 -6
  45. package/dist/chunk-2ONMTDLK.js +0 -2050
  46. package/dist/daemon-client-3IJD6X4B.js +0 -59
  47. package/dist/network-store-2S5HATEV.js +0 -194
  48. package/dist/parse-action-dsl-DRSPBALP.js +0 -72
  49. package/dist/screenshot-CWAWMXVA.js +0 -28
  50. package/dist/screenshot-MB6R7RSS.js +0 -26
  51. package/dist/session-recorder-ILSSV2UC.js +0 -6
  52. package/dist/session-recorder-XET3DNML.js +0 -7
@@ -0,0 +1,33 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ export {
29
+ __require,
30
+ __esm,
31
+ __export,
32
+ __toCommonJS
33
+ };
@@ -276,35 +276,17 @@ var WebhookNotifier = class {
276
276
  };
277
277
 
278
278
  // src/config.ts
279
- import { existsSync, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
280
- import { join } from "path";
281
279
  import { homedir, tmpdir } from "os";
282
-
283
- // src/utils/json-file.ts
284
- import { readFileSync, writeFileSync } from "fs";
285
- function readJsonFile(filePath, defaultValue) {
286
- try {
287
- const content = readFileSync(filePath, "utf-8");
288
- return JSON.parse(content);
289
- } catch {
290
- return defaultValue;
291
- }
292
- }
293
-
294
- // src/config.ts
295
- function getConfigFile() {
296
- return join(homedir() || tmpdir(), ".xbrowser", "config.json");
280
+ import { join } from "path";
281
+ import { loadConfig as coreLoadConfig, saveConfig as coreSaveConfig } from "@dyyz1993/xcli-core";
282
+ function getConfigSource() {
283
+ return { configDir: join(homedir() || tmpdir(), ".xbrowser") };
297
284
  }
298
285
  function loadConfig() {
299
- const configFile = getConfigFile();
300
- if (!existsSync(configFile)) return {};
301
- return readJsonFile(configFile, {});
286
+ return coreLoadConfig(getConfigSource());
302
287
  }
303
288
  function saveConfig(config) {
304
- const dir = join(homedir() || tmpdir(), ".xbrowser");
305
- const configFile = getConfigFile();
306
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
307
- writeFileSync2(configFile, JSON.stringify(config, null, 2), "utf-8");
289
+ coreSaveConfig(getConfigSource(), config);
308
290
  }
309
291
  function getConfigValue(key) {
310
292
  return loadConfig()[key];
@@ -510,7 +492,6 @@ var HumanInteractionManager = class {
510
492
  };
511
493
 
512
494
  export {
513
- readJsonFile,
514
495
  ScreencastCapturer,
515
496
  CaptchaDetector,
516
497
  WebhookNotifier,
@@ -0,0 +1,417 @@
1
+ import {
2
+ __esm,
3
+ __export,
4
+ __require
5
+ } from "./chunk-KFQGP6VL.js";
6
+
7
+ // src/recorder/site-knowledge.ts
8
+ var site_knowledge_exports = {};
9
+ __export(site_knowledge_exports, {
10
+ addKnownIssue: () => addKnownIssue,
11
+ getKnowledgeDir: () => getKnowledgeDir,
12
+ getKnowledgePath: () => getKnowledgePath,
13
+ listSiteKnowledge: () => listSiteKnowledge,
14
+ readSiteKnowledge: () => readSiteKnowledge,
15
+ readSiteKnowledgeMarkdown: () => readSiteKnowledgeMarkdown,
16
+ toMarkdown: () => toMarkdown,
17
+ updateSiteKnowledge: () => updateSiteKnowledge
18
+ });
19
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
20
+ import { join } from "path";
21
+ import { homedir } from "os";
22
+ function getKnowledgeDir() {
23
+ return join(homedir(), ".xbrowser", "knowledge");
24
+ }
25
+ function getKnowledgePath(domain, ext) {
26
+ return join(getKnowledgeDir(), `${domain}.${ext}`);
27
+ }
28
+ function extractDomain(url) {
29
+ try {
30
+ const u = new URL(url);
31
+ return u.hostname.replace(/^www\./, "");
32
+ } catch {
33
+ return "unknown";
34
+ }
35
+ }
36
+ function normalizePath(url) {
37
+ try {
38
+ const u = new URL(url);
39
+ return u.pathname;
40
+ } catch {
41
+ return url;
42
+ }
43
+ }
44
+ function extractSelectors(actions, pageUrl) {
45
+ const seen = /* @__PURE__ */ new Map();
46
+ const now = (/* @__PURE__ */ new Date()).toISOString();
47
+ for (const action of actions) {
48
+ if (normalizePath(action.url) !== normalizePath(pageUrl)) continue;
49
+ const el = action.element;
50
+ if (!el || !el.selector) continue;
51
+ const key = el.selector;
52
+ const existing = seen.get(key);
53
+ if (existing) {
54
+ existing.timesSeen++;
55
+ existing.lastSeen = now;
56
+ } else {
57
+ const description = buildDescription(el, action);
58
+ seen.set(key, {
59
+ selector: key,
60
+ tag: el.tag || "unknown",
61
+ description,
62
+ actionType: action.type,
63
+ role: el.role,
64
+ text: el.text?.substring(0, 60),
65
+ confidence: el.confidence || "medium",
66
+ lastSeen: now,
67
+ timesSeen: 1,
68
+ status: "active"
69
+ });
70
+ }
71
+ }
72
+ return Array.from(seen.values()).sort((a, b) => b.timesSeen - a.timesSeen);
73
+ }
74
+ function buildDescription(el, action) {
75
+ const parts = [];
76
+ if (el.text) parts.push(`"${el.text}"`);
77
+ if (el.placeholder) parts.push(`placeholder="${el.placeholder}"`);
78
+ if (el.ariaLabel) parts.push(`aria-label="${el.ariaLabel}"`);
79
+ if (el.role) parts.push(`role=${el.role}`);
80
+ if (el.type) parts.push(`type=${el.type}`);
81
+ const actionDesc = {
82
+ click: "clicked",
83
+ input: `filled with "${action.value?.substring(0, 30)}"`,
84
+ change: "changed",
85
+ submit: "submitted form",
86
+ dblclick: "double-clicked",
87
+ contextmenu: "right-clicked",
88
+ hover: "hovered over",
89
+ focus: "focused"
90
+ };
91
+ const verb = actionDesc[action.type] || action.type;
92
+ const base = parts.length > 0 ? parts.join(", ") : el.tag || "element";
93
+ return `${base} \u2014 ${verb}`;
94
+ }
95
+ function extractForms(actions, pageUrl) {
96
+ const forms = /* @__PURE__ */ new Map();
97
+ for (const action of actions) {
98
+ if (normalizePath(action.url) !== normalizePath(pageUrl)) continue;
99
+ const el = action.element;
100
+ if (!el) continue;
101
+ if (el.tag === "input" || el.tag === "textarea" || el.tag === "select") {
102
+ const formKey = "main";
103
+ const form = forms.get(formKey) || {
104
+ name: "Main Form",
105
+ action: pageUrl,
106
+ fields: []
107
+ };
108
+ if (!form.fields.some((f) => f.selector === el.selector)) {
109
+ form.fields.push({
110
+ selector: el.selector || "",
111
+ tag: el.tag,
112
+ label: el.ariaLabel || el.placeholder || el.text || el.selector || "",
113
+ inputType: el.type || el.tag,
114
+ placeholder: el.placeholder
115
+ });
116
+ }
117
+ forms.set(formKey, form);
118
+ }
119
+ if (action.type === "submit" || action.type === "click" && el.tag === "button" && el.text) {
120
+ const form = forms.get("main");
121
+ if (form && !form.submitSelector) {
122
+ form.submitSelector = el.selector;
123
+ }
124
+ }
125
+ }
126
+ return Array.from(forms.values());
127
+ }
128
+ function extractNavLinks(actions) {
129
+ const links = [];
130
+ const seen = /* @__PURE__ */ new Set();
131
+ for (const action of actions) {
132
+ if (action.type !== "click" && action.type !== "navigation") continue;
133
+ const el = action.element;
134
+ if (!el || el.tag !== "a" || !el.text) continue;
135
+ const href = el.href || action.url;
136
+ if (!href || seen.has(href)) continue;
137
+ seen.add(href);
138
+ links.push({
139
+ text: el.text.substring(0, 40),
140
+ href,
141
+ selector: el.selector || ""
142
+ });
143
+ }
144
+ return links;
145
+ }
146
+ function extractApiEndpoints(network, existingEndpoints) {
147
+ const endpoints = {};
148
+ const now = (/* @__PURE__ */ new Date()).toISOString();
149
+ if (existingEndpoints) {
150
+ for (const [key, ep] of Object.entries(existingEndpoints)) {
151
+ endpoints[key] = ep;
152
+ }
153
+ }
154
+ for (const entry of network) {
155
+ if (!entry.url.includes("/api/") && !entry.url.includes("/v1/") && !entry.url.includes("/v2/") && entry.contentType && !entry.contentType.includes("json") && !entry.contentType.includes("text/")) continue;
156
+ if (["image", "stylesheet", "font", "manifest"].includes(entry.resourceType)) continue;
157
+ let path;
158
+ try {
159
+ path = new URL(entry.url).pathname;
160
+ } catch {
161
+ continue;
162
+ }
163
+ const method = entry.method;
164
+ const key = `${method} ${path}`;
165
+ if (endpoints[key]) {
166
+ endpoints[key].timesSeen++;
167
+ endpoints[key].lastSeen = now;
168
+ } else {
169
+ let params = [];
170
+ if (entry.requestBody && typeof entry.requestBody === "object") {
171
+ params = Object.keys(entry.requestBody).slice(0, 10);
172
+ }
173
+ let responseFields = [];
174
+ if (entry.responseBody && typeof entry.responseBody === "object") {
175
+ const resp = entry.responseBody;
176
+ responseFields = Object.keys(resp).slice(0, 10);
177
+ if (resp.data && typeof resp.data === "object") {
178
+ const dataKeys = Object.keys(resp.data).slice(0, 10);
179
+ responseFields = [...responseFields, ...dataKeys.map((k) => `data.${k}`)];
180
+ }
181
+ }
182
+ endpoints[key] = {
183
+ method,
184
+ url: entry.url.substring(0, 200),
185
+ path,
186
+ params,
187
+ responseFields,
188
+ lastSeen: now,
189
+ timesSeen: 1
190
+ };
191
+ }
192
+ }
193
+ return endpoints;
194
+ }
195
+ function mergePages(existing, newPages) {
196
+ const merged = { ...existing };
197
+ const now = (/* @__PURE__ */ new Date()).toISOString();
198
+ for (const [path, newPage] of Object.entries(newPages)) {
199
+ if (merged[path]) {
200
+ const old = merged[path];
201
+ const selectorMap = /* @__PURE__ */ new Map();
202
+ for (const sel of old.selectors) selectorMap.set(sel.selector, sel);
203
+ for (const sel of newPage.selectors) {
204
+ const existing2 = selectorMap.get(sel.selector);
205
+ if (existing2) {
206
+ existing2.timesSeen += sel.timesSeen;
207
+ existing2.lastSeen = now;
208
+ existing2.status = "active";
209
+ } else {
210
+ selectorMap.set(sel.selector, sel);
211
+ }
212
+ }
213
+ const newSelectorSet = new Set(newPage.selectors.map((s) => s.selector));
214
+ for (const sel of selectorMap.values()) {
215
+ if (!newSelectorSet.has(sel.selector) && sel.timesSeen > 0) {
216
+ }
217
+ }
218
+ merged[path] = {
219
+ ...newPage,
220
+ selectors: Array.from(selectorMap.values()).sort((a, b) => b.timesSeen - a.timesSeen),
221
+ forms: newPage.forms.length > 0 ? newPage.forms : old.forms,
222
+ navigationLinks: [.../* @__PURE__ */ new Set([...old.navigationLinks, ...newPage.navigationLinks])].slice(0, 50),
223
+ lastVisited: now
224
+ };
225
+ } else {
226
+ merged[path] = newPage;
227
+ }
228
+ }
229
+ return merged;
230
+ }
231
+ function updateSiteKnowledge(data) {
232
+ const domain = extractDomain(data.startUrl);
233
+ const jsonPath = getKnowledgePath(domain, "json");
234
+ let existing = null;
235
+ if (existsSync(jsonPath)) {
236
+ try {
237
+ existing = JSON.parse(readFileSync(jsonPath, "utf-8"));
238
+ } catch {
239
+ existing = null;
240
+ }
241
+ }
242
+ const pageMap = /* @__PURE__ */ new Map();
243
+ for (const action of data.actions) {
244
+ const path = normalizePath(action.url);
245
+ if (!pageMap.has(path)) pageMap.set(path, []);
246
+ pageMap.get(path).push(action);
247
+ }
248
+ const newPages = {};
249
+ for (const [path, actions] of pageMap) {
250
+ const fullUrl = actions[0]?.url || data.startUrl;
251
+ newPages[path] = {
252
+ url: fullUrl,
253
+ title: actions[0]?.pageTitle || "",
254
+ selectors: extractSelectors(data.actions, fullUrl),
255
+ forms: extractForms(data.actions, fullUrl),
256
+ navigationLinks: extractNavLinks(actions),
257
+ lastVisited: (/* @__PURE__ */ new Date()).toISOString()
258
+ };
259
+ }
260
+ const apiEndpoints = extractApiEndpoints(data.network, existing?.apiEndpoints);
261
+ const pages = existing ? mergePages(existing.pages, newPages) : newPages;
262
+ const knowledge = {
263
+ domain,
264
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
265
+ recordingCount: (existing?.recordingCount || 0) + 1,
266
+ pages,
267
+ apiEndpoints,
268
+ knownIssues: existing?.knownIssues || [],
269
+ generatedBy: "xbrowser-recorder"
270
+ };
271
+ mkdirSync(getKnowledgeDir(), { recursive: true });
272
+ writeFileSync(jsonPath, JSON.stringify(knowledge, null, 2), "utf-8");
273
+ writeFileSync(getKnowledgePath(domain, "md"), toMarkdown(knowledge), "utf-8");
274
+ return knowledge;
275
+ }
276
+ function toMarkdown(kb) {
277
+ const lines = [];
278
+ lines.push(`# Site Knowledge: ${kb.domain}`);
279
+ lines.push("");
280
+ lines.push("> **Auto-generated by xbrowser recorder. This document is for LLM consumption.**");
281
+ lines.push("> Use these selectors when writing automation scripts for this site.");
282
+ lines.push(`> Updated: ${kb.lastUpdated} | Recordings: ${kb.recordingCount}`);
283
+ lines.push("");
284
+ lines.push("## Pages");
285
+ lines.push("");
286
+ for (const page of Object.values(kb.pages)) {
287
+ lines.push(`### ${page.url}`);
288
+ lines.push(`- **Path**: ${normalizePath(page.url)}`);
289
+ if (page.title) lines.push(`- **Title**: ${page.title}`);
290
+ lines.push(`- **Last Visited**: ${page.lastVisited}`);
291
+ lines.push("");
292
+ if (page.selectors.length > 0) {
293
+ lines.push("#### Selectors");
294
+ lines.push("");
295
+ lines.push("| Selector | Tag | Action | Description | Confidence | Seen |");
296
+ lines.push("|----------|-----|--------|-------------|------------|------|");
297
+ for (const sel of page.selectors) {
298
+ const status = sel.status === "deprecated" ? " \u26A0\uFE0FDEPRECATED" : "";
299
+ lines.push(
300
+ `| \`${sel.selector}\` | ${sel.tag} | ${sel.actionType} | ${sel.description} | ${sel.confidence} | ${sel.timesSeen}x${status} |`
301
+ );
302
+ }
303
+ lines.push("");
304
+ }
305
+ if (page.forms.length > 0) {
306
+ lines.push("#### Forms");
307
+ lines.push("");
308
+ for (const form of page.forms) {
309
+ lines.push(`- **${form.name}** (${form.action})`);
310
+ for (const field of form.fields) {
311
+ const parts = [field.tag, field.inputType];
312
+ if (field.placeholder) parts.push(`placeholder="${field.placeholder}"`);
313
+ lines.push(` - \`${field.selector}\` \u2192 ${field.label} (${parts.join(", ")})`);
314
+ }
315
+ if (form.submitSelector) {
316
+ lines.push(` - Submit: \`${form.submitSelector}\``);
317
+ }
318
+ }
319
+ lines.push("");
320
+ }
321
+ if (page.navigationLinks.length > 0) {
322
+ lines.push("#### Navigation Links");
323
+ lines.push("");
324
+ for (const link of page.navigationLinks.slice(0, 20)) {
325
+ lines.push(`- [${link.text}](${link.href}) \u2192 \`${link.selector}\``);
326
+ }
327
+ lines.push("");
328
+ }
329
+ }
330
+ const endpoints = Object.values(kb.apiEndpoints);
331
+ if (endpoints.length > 0) {
332
+ lines.push("## API Endpoints");
333
+ lines.push("");
334
+ lines.push("| Method | Path | Params | Response Fields | Frequency |");
335
+ lines.push("|--------|------|--------|-----------------|-----------|");
336
+ for (const ep of endpoints.sort((a, b) => b.timesSeen - a.timesSeen)) {
337
+ const params = ep.params.length > 0 ? ep.params.join(", ") : "-";
338
+ const respFields = ep.responseFields.length > 0 ? ep.responseFields.slice(0, 5).join(", ") : "-";
339
+ lines.push(`| ${ep.method} | ${ep.path} | ${params} | ${respFields} | ${ep.timesSeen}x |`);
340
+ }
341
+ lines.push("");
342
+ }
343
+ if (kb.knownIssues.length > 0) {
344
+ lines.push("## Known Issues");
345
+ lines.push("");
346
+ for (const issue of kb.knownIssues) {
347
+ lines.push(`- ${issue}`);
348
+ }
349
+ lines.push("");
350
+ }
351
+ lines.push("---");
352
+ lines.push("");
353
+ lines.push("## How to Use This Document");
354
+ lines.push("");
355
+ lines.push("When writing automation scripts for this site:");
356
+ lines.push("1. Use the selectors from the **Selectors** tables above");
357
+ lines.push("2. Prefer selectors with **high confidence** and **higher Seen count**");
358
+ lines.push("3. For form filling, follow the **Forms** structure");
359
+ lines.push("4. For API interactions, reference the **API Endpoints** table");
360
+ lines.push("5. If a selector fails, it may be **deprecated** \u2014 check for alternative selectors");
361
+ lines.push("");
362
+ return lines.join("\n");
363
+ }
364
+ function readSiteKnowledge(domain) {
365
+ const path = getKnowledgePath(domain, "json");
366
+ if (!existsSync(path)) return null;
367
+ try {
368
+ return JSON.parse(readFileSync(path, "utf-8"));
369
+ } catch {
370
+ return null;
371
+ }
372
+ }
373
+ function readSiteKnowledgeMarkdown(domain) {
374
+ const path = getKnowledgePath(domain, "md");
375
+ if (!existsSync(path)) return null;
376
+ try {
377
+ return readFileSync(path, "utf-8");
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+ function listSiteKnowledge() {
383
+ const dir = getKnowledgeDir();
384
+ if (!existsSync(dir)) return [];
385
+ try {
386
+ const files = __require("fs").readdirSync(dir);
387
+ return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
388
+ } catch {
389
+ return [];
390
+ }
391
+ }
392
+ function addKnownIssue(domain, issue) {
393
+ const kb = readSiteKnowledge(domain);
394
+ if (!kb) return;
395
+ const dated = `[${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}] ${issue}`;
396
+ kb.knownIssues.push(dated);
397
+ kb.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
398
+ writeFileSync(getKnowledgePath(domain, "json"), JSON.stringify(kb, null, 2), "utf-8");
399
+ writeFileSync(getKnowledgePath(domain, "md"), toMarkdown(kb), "utf-8");
400
+ }
401
+ var init_site_knowledge = __esm({
402
+ "src/recorder/site-knowledge.ts"() {
403
+ }
404
+ });
405
+
406
+ export {
407
+ getKnowledgeDir,
408
+ getKnowledgePath,
409
+ updateSiteKnowledge,
410
+ toMarkdown,
411
+ readSiteKnowledge,
412
+ readSiteKnowledgeMarkdown,
413
+ listSiteKnowledge,
414
+ addKnownIssue,
415
+ site_knowledge_exports,
416
+ init_site_knowledge
417
+ };