@hraness/kb 0.17.1

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 (200) hide show
  1. package/DISCLOSURE +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +545 -0
  4. package/dist/agent-context.js +35 -0
  5. package/dist/agent-guide-audit.js +18 -0
  6. package/dist/attachments.js +19 -0
  7. package/dist/authoring.js +33 -0
  8. package/dist/benchmark.js +19 -0
  9. package/dist/capture.js +49 -0
  10. package/dist/cli.js +3646 -0
  11. package/dist/clip/acquire.js +57 -0
  12. package/dist/clip/args.js +21 -0
  13. package/dist/clip/bounded-byte-buffer.js +9 -0
  14. package/dist/clip/browser-profiles.js +17 -0
  15. package/dist/clip/bundle-reader.js +11 -0
  16. package/dist/clip/cli.js +27 -0
  17. package/dist/clip/cookies.js +21 -0
  18. package/dist/clip/defuddle-worker.js +84 -0
  19. package/dist/clip/doctor.js +32 -0
  20. package/dist/clip/jobs.js +784 -0
  21. package/dist/clip/network-proxy.js +9 -0
  22. package/dist/clip/network.js +28 -0
  23. package/dist/clip/persist.js +30 -0
  24. package/dist/clip/refresh.js +11 -0
  25. package/dist/clip/terminal.js +11 -0
  26. package/dist/evaluation-builder.js +12238 -0
  27. package/dist/evaluation-kb.js +26 -0
  28. package/dist/evaluation.js +35 -0
  29. package/dist/git.js +39 -0
  30. package/dist/graph.js +39 -0
  31. package/dist/index-01jj6rbv.js +1103 -0
  32. package/dist/index-06c9ctr6.js +738 -0
  33. package/dist/index-0kavxzqj.js +2480 -0
  34. package/dist/index-1gwbassd.js +743 -0
  35. package/dist/index-1n418kb9.js +827 -0
  36. package/dist/index-1vrd1rmn.js +56 -0
  37. package/dist/index-1xxnjn0d.js +171 -0
  38. package/dist/index-3rm7cz6h.js +373 -0
  39. package/dist/index-3v2z4f0q.js +328 -0
  40. package/dist/index-48pz4jpc.js +392 -0
  41. package/dist/index-4j3tt0c3.js +234 -0
  42. package/dist/index-5n05se68.js +307 -0
  43. package/dist/index-5vwpzb5a.js +764 -0
  44. package/dist/index-6g2pv9d2.js +370 -0
  45. package/dist/index-7fzc32gf.js +712 -0
  46. package/dist/index-84x0vjjp.js +432 -0
  47. package/dist/index-adx6khj5.js +488 -0
  48. package/dist/index-b88v3vtm.js +805 -0
  49. package/dist/index-cv6fh7z5.js +462 -0
  50. package/dist/index-cxfrakt7.js +1067 -0
  51. package/dist/index-d13v9ckt.js +150 -0
  52. package/dist/index-dycjbhw2.js +1943 -0
  53. package/dist/index-dyqwejk5.js +531 -0
  54. package/dist/index-e5fbsywq.js +658 -0
  55. package/dist/index-f984hw45.js +927 -0
  56. package/dist/index-gh719d91.js +63 -0
  57. package/dist/index-hgve9rh2.js +2046 -0
  58. package/dist/index-hya40gb2.js +474 -0
  59. package/dist/index-j4zgmzjr.js +152 -0
  60. package/dist/index-jsmvyyvf.js +1166 -0
  61. package/dist/index-kh0pwvdd.js +1974 -0
  62. package/dist/index-mqx4nd6v.js +119 -0
  63. package/dist/index-mxxxytys.js +1254 -0
  64. package/dist/index-n5dd7r0v.js +711 -0
  65. package/dist/index-npg9z1a4.js +431 -0
  66. package/dist/index-pj501bh1.js +140 -0
  67. package/dist/index-qry4vhxk.js +1041 -0
  68. package/dist/index-s2gw5aw9.js +199 -0
  69. package/dist/index-vvdj6kca.js +70 -0
  70. package/dist/index-w2zc0vwa.js +571 -0
  71. package/dist/index-x3fthpsc.js +585 -0
  72. package/dist/index-zr53sf63.js +44 -0
  73. package/dist/index-zxdy5pby.js +2728 -0
  74. package/dist/index-zzhgcwyt.js +966 -0
  75. package/dist/index.js +453 -0
  76. package/dist/navigation.js +13 -0
  77. package/dist/pdf.js +85 -0
  78. package/dist/percolate.js +24 -0
  79. package/dist/portfolio.js +77 -0
  80. package/dist/query.js +29 -0
  81. package/dist/repository-memory.js +60 -0
  82. package/dist/sdk.js +36 -0
  83. package/dist/search-rules.js +34 -0
  84. package/dist/search.js +22 -0
  85. package/dist/semantic.js +39 -0
  86. package/dist/source-inbox.js +17 -0
  87. package/dist/untrusted-content.js +32 -0
  88. package/dist/url-intelligence.js +74 -0
  89. package/dist/workflow.js +23 -0
  90. package/dist/workflows/decision-context.js +20 -0
  91. package/dist/workflows/explain-change.js +8 -0
  92. package/dist/workflows/index.js +28 -0
  93. package/dist/workflows/plan-radar.js +10 -0
  94. package/package.json +409 -0
  95. package/skills/kb/AGENTS.md +19 -0
  96. package/skills/kb/SKILL.md +82 -0
  97. package/skills/kb/agents/openai.yaml +4 -0
  98. package/skills/kb/references/pdf-review.md +51 -0
  99. package/skills/kb/references/percolate.md +144 -0
  100. package/skills/kb/references/plan-structure.md +105 -0
  101. package/skills/kb/references/plan.md +115 -0
  102. package/skills/kb/references/query.md +231 -0
  103. package/skills/kb/references/refresh.md +133 -0
  104. package/skills/kb/references/save-pdf.md +125 -0
  105. package/skills/kb/references/save-url.md +178 -0
  106. package/skills/kb/references/url-authentication.md +75 -0
  107. package/skills/kb/references/url-platforms.md +25 -0
  108. package/src/agent-context.ts +1241 -0
  109. package/src/agent-guide-audit.ts +750 -0
  110. package/src/attachments.ts +723 -0
  111. package/src/authoring.ts +1725 -0
  112. package/src/benchmark.ts +362 -0
  113. package/src/capture.ts +68 -0
  114. package/src/cli.ts +3660 -0
  115. package/src/clip/acquire.ts +1273 -0
  116. package/src/clip/archive-today.ts +402 -0
  117. package/src/clip/args.ts +423 -0
  118. package/src/clip/assets.ts +262 -0
  119. package/src/clip/bounded-byte-buffer.ts +75 -0
  120. package/src/clip/browser-profiles.ts +372 -0
  121. package/src/clip/bundle-reader.ts +608 -0
  122. package/src/clip/capture.ts +1054 -0
  123. package/src/clip/cli.ts +181 -0
  124. package/src/clip/cookies.ts +512 -0
  125. package/src/clip/doctor.ts +1110 -0
  126. package/src/clip/extract.ts +868 -0
  127. package/src/clip/jobs.ts +959 -0
  128. package/src/clip/lib.ts +861 -0
  129. package/src/clip/media.ts +1474 -0
  130. package/src/clip/metadata-search-tool/Cargo.lock +2498 -0
  131. package/src/clip/metadata-search-tool/Cargo.toml +18 -0
  132. package/src/clip/metadata-search-tool/runner.ts +685 -0
  133. package/src/clip/metadata-search-tool/src/main.rs +389 -0
  134. package/src/clip/metadata-search.ts +687 -0
  135. package/src/clip/network-proxy.ts +627 -0
  136. package/src/clip/network.ts +950 -0
  137. package/src/clip/package-root.ts +54 -0
  138. package/src/clip/persist.ts +1472 -0
  139. package/src/clip/platforms.ts +1824 -0
  140. package/src/clip/refresh.ts +197 -0
  141. package/src/clip/structured.ts +432 -0
  142. package/src/clip/terminal.ts +197 -0
  143. package/src/clip/url-intelligence.ts +581 -0
  144. package/src/clip/url-metadata-backfill.ts +616 -0
  145. package/src/clip/url-metadata-cli.ts +251 -0
  146. package/src/clip/url-metadata.ts +1397 -0
  147. package/src/evaluation-analysis-v2.ts +1440 -0
  148. package/src/evaluation-builder.ts +2539 -0
  149. package/src/evaluation-corpus-authoring.ts +2052 -0
  150. package/src/evaluation-evidence.ts +1147 -0
  151. package/src/evaluation-execution-v2.ts +426 -0
  152. package/src/evaluation-implementation.ts +131 -0
  153. package/src/evaluation-kb-closure.ts +1430 -0
  154. package/src/evaluation-kb-runner-v2.ts +735 -0
  155. package/src/evaluation-kb-v2.ts +2567 -0
  156. package/src/evaluation-kb.ts +1018 -0
  157. package/src/evaluation-measurement-v2.ts +1462 -0
  158. package/src/evaluation-packing-v2.ts +452 -0
  159. package/src/evaluation-redaction.ts +28 -0
  160. package/src/evaluation-v2.ts +3857 -0
  161. package/src/evaluation.ts +1220 -0
  162. package/src/git.ts +1276 -0
  163. package/src/graph.ts +1487 -0
  164. package/src/index.ts +20 -0
  165. package/src/init.ts +123 -0
  166. package/src/navigation.ts +277 -0
  167. package/src/note-lock.ts +529 -0
  168. package/src/pdf/args.ts +202 -0
  169. package/src/pdf/capture.ts +298 -0
  170. package/src/pdf/cli.ts +159 -0
  171. package/src/pdf/extract.ts +303 -0
  172. package/src/pdf/layout.ts +618 -0
  173. package/src/pdf/markdown.ts +154 -0
  174. package/src/pdf/model.ts +234 -0
  175. package/src/pdf/ocr.ts +190 -0
  176. package/src/pdf/persist.ts +314 -0
  177. package/src/pdf/source.ts +135 -0
  178. package/src/pdf/tools.ts +146 -0
  179. package/src/pdf.ts +92 -0
  180. package/src/percolate.ts +877 -0
  181. package/src/portfolio-audit.ts +574 -0
  182. package/src/portfolio-identity.ts +162 -0
  183. package/src/portfolio-registry.ts +473 -0
  184. package/src/portfolio.ts +765 -0
  185. package/src/query.ts +646 -0
  186. package/src/repository-memory.ts +1150 -0
  187. package/src/sdk.ts +1452 -0
  188. package/src/search-rules.ts +712 -0
  189. package/src/search.ts +710 -0
  190. package/src/semantic-runtime.ts +1139 -0
  191. package/src/semantic.ts +2480 -0
  192. package/src/source-inbox.ts +232 -0
  193. package/src/untrusted-content.ts +401 -0
  194. package/src/url-intelligence.ts +104 -0
  195. package/src/vault.ts +636 -0
  196. package/src/workflow.ts +653 -0
  197. package/src/workflows/decision-context.ts +81 -0
  198. package/src/workflows/explain-change.ts +73 -0
  199. package/src/workflows/index.ts +3 -0
  200. package/src/workflows/plan-radar.ts +101 -0
@@ -0,0 +1,827 @@
1
+ // @bun
2
+ import {
3
+ findKbPackageRoot,
4
+ isolatedAgentBrowserEnvironment
5
+ } from "./index-qry4vhxk.js";
6
+ import {
7
+ BoundedByteBuffer
8
+ } from "./index-gh719d91.js";
9
+
10
+ // src/clip/doctor.ts
11
+ import { chmodSync, existsSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
12
+ import { homedir, tmpdir } from "os";
13
+ import { dirname, join, resolve } from "path";
14
+ var expectedBunVersion = "1.3.14";
15
+ var expectedQmdVersion = "2.5.3";
16
+ var expectedSqliteVecVersion = "0.1.9";
17
+ var expectedNodeLlamaCppVersion = "3.18.1";
18
+ var expectedBetterSqliteVersion = "12.10.0";
19
+ var homebrewSqliteLibraryPaths = [
20
+ "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib",
21
+ "/usr/local/opt/sqlite/lib/libsqlite3.dylib"
22
+ ];
23
+ var dependencyVersions = {
24
+ defuddle: "0.19.1",
25
+ "agent-browser": "0.32.3",
26
+ "@steipete/sweet-cookie": "0.4.2",
27
+ "@tobilu/qmd": expectedQmdVersion
28
+ };
29
+ var dependencyNames = [
30
+ "defuddle",
31
+ "agent-browser",
32
+ "@steipete/sweet-cookie",
33
+ "@tobilu/qmd"
34
+ ];
35
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36
+ async function readBoundedStream(stream, maxBytes) {
37
+ const reader = stream.getReader();
38
+ const bytes = new BoundedByteBuffer(maxBytes);
39
+ try {
40
+ for (;; ) {
41
+ const result = await reader.read();
42
+ if (result.done)
43
+ break;
44
+ if (!bytes.append(result.value))
45
+ throw new Error(`diagnostic output exceeded ${maxBytes} bytes`);
46
+ }
47
+ } finally {
48
+ reader.releaseLock();
49
+ }
50
+ return new TextDecoder().decode(bytes.toUint8Array());
51
+ }
52
+ var runDiagnosticCommand = async (specification) => {
53
+ const child = Bun.spawn([...specification.command], {
54
+ stdin: "ignore",
55
+ stdout: "pipe",
56
+ stderr: "pipe",
57
+ ...specification.cwd === undefined ? {} : { cwd: specification.cwd },
58
+ ...specification.environment === undefined ? {} : { env: specification.environment }
59
+ });
60
+ let timedOut = false;
61
+ let forceKillTimer = null;
62
+ const timer = setTimeout(() => {
63
+ timedOut = true;
64
+ child.kill("SIGTERM");
65
+ forceKillTimer = setTimeout(() => child.kill("SIGKILL"), 1000);
66
+ }, specification.timeoutMs);
67
+ try {
68
+ const [stdout, stderr, exitCode] = await Promise.all([
69
+ readBoundedStream(child.stdout, specification.maxOutputBytes),
70
+ readBoundedStream(child.stderr, specification.maxOutputBytes),
71
+ child.exited
72
+ ]);
73
+ if (timedOut)
74
+ throw new Error(`diagnostic command timed out after ${specification.timeoutMs}ms`);
75
+ return { stdout, stderr, exitCode };
76
+ } catch (error) {
77
+ child.kill("SIGKILL");
78
+ await child.exited;
79
+ throw error;
80
+ } finally {
81
+ clearTimeout(timer);
82
+ if (forceKillTimer !== null)
83
+ clearTimeout(forceKillTimer);
84
+ }
85
+ };
86
+ function parseJson(value) {
87
+ const lines = value.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
88
+ for (let index = lines.length - 1;index >= 0; index -= 1) {
89
+ const line = lines[index];
90
+ if (line === undefined || line[0] !== "{" && line[0] !== "[")
91
+ continue;
92
+ try {
93
+ return JSON.parse(line);
94
+ } catch {}
95
+ }
96
+ return null;
97
+ }
98
+ function readJsonRecord(path, readText) {
99
+ try {
100
+ const parsed = JSON.parse(readText(path));
101
+ return isRecord(parsed) ? parsed : null;
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+ function dependencyVersion(manifest, name) {
107
+ if (manifest === null)
108
+ return null;
109
+ for (const key of ["dependencies", "devDependencies"]) {
110
+ const section = manifest[key];
111
+ if (!isRecord(section))
112
+ continue;
113
+ const value = section[name];
114
+ if (typeof value === "string")
115
+ return value;
116
+ }
117
+ return null;
118
+ }
119
+ function packageDirectory(name) {
120
+ return name.startsWith("@") ? name.split("/") : [name];
121
+ }
122
+ function installedDependencyDirectory(name, packageRoot, readText) {
123
+ const segments = packageDirectory(name);
124
+ let directory = resolve(packageRoot);
125
+ for (let depth = 0;depth < 12; depth += 1) {
126
+ const candidate = join(directory, "node_modules", ...segments);
127
+ const manifest = readJsonRecord(join(candidate, "package.json"), readText);
128
+ if (manifest !== null)
129
+ return { directory: candidate, manifest };
130
+ const parent = dirname(directory);
131
+ if (parent === directory)
132
+ break;
133
+ directory = parent;
134
+ }
135
+ return null;
136
+ }
137
+ function installedPackageVersion(name, fromDirectory, readText) {
138
+ const installed = installedDependencyDirectory(name, fromDirectory, readText);
139
+ if (installed === null)
140
+ return null;
141
+ return {
142
+ directory: installed.directory,
143
+ version: typeof installed.manifest.version === "string" ? installed.manifest.version : null
144
+ };
145
+ }
146
+ function capabilityStatus(statuses) {
147
+ if (statuses.some((status) => status === "unavailable"))
148
+ return "unavailable";
149
+ return statuses.every((status) => status === "ready") ? "ready" : "partial";
150
+ }
151
+ function canonicalDirectory(path, realpath) {
152
+ try {
153
+ return realpath(path);
154
+ } catch {
155
+ return path;
156
+ }
157
+ }
158
+ function nativeSqliteVecPackage(platform, architecture) {
159
+ if (platform === "darwin" && (architecture === "arm64" || architecture === "x64")) {
160
+ return { name: `sqlite-vec-darwin-${architecture}`, binary: "vec0.dylib" };
161
+ }
162
+ if (platform === "linux" && (architecture === "arm64" || architecture === "x64")) {
163
+ return { name: `sqlite-vec-linux-${architecture}`, binary: "vec0.so" };
164
+ }
165
+ if (platform === "win32" && architecture === "x64") {
166
+ return { name: "sqlite-vec-windows-x64", binary: "vec0.dll" };
167
+ }
168
+ return null;
169
+ }
170
+ function nativeNodeLlamaCppPackage(platform, architecture) {
171
+ if (platform === "darwin" && architecture === "arm64") {
172
+ return {
173
+ name: "@node-llama-cpp/mac-arm64-metal",
174
+ binary: join("bins", "mac-arm64-metal", "llama-addon.node")
175
+ };
176
+ }
177
+ if (platform === "darwin" && architecture === "x64") {
178
+ return {
179
+ name: "@node-llama-cpp/mac-x64",
180
+ binary: join("bins", "mac-x64", "llama-addon.node")
181
+ };
182
+ }
183
+ if (platform === "linux" && architecture === "arm64") {
184
+ return {
185
+ name: "@node-llama-cpp/linux-arm64",
186
+ binary: join("bins", "linux-arm64", "llama-addon.node")
187
+ };
188
+ }
189
+ if (platform === "linux" && architecture === "arm") {
190
+ return {
191
+ name: "@node-llama-cpp/linux-armv7l",
192
+ binary: join("bins", "linux-armv7l", "llama-addon.node")
193
+ };
194
+ }
195
+ if (platform === "linux" && architecture === "x64") {
196
+ return {
197
+ name: "@node-llama-cpp/linux-x64",
198
+ binary: join("bins", "linux-x64", "llama-addon.node")
199
+ };
200
+ }
201
+ if (platform === "win32" && architecture === "arm64") {
202
+ return {
203
+ name: "@node-llama-cpp/win-arm64",
204
+ binary: join("bins", "win-arm64", "llama-addon.node")
205
+ };
206
+ }
207
+ if (platform === "win32" && architecture === "x64") {
208
+ return {
209
+ name: "@node-llama-cpp/win-x64",
210
+ binary: join("bins", "win-x64", "llama-addon.node")
211
+ };
212
+ }
213
+ return null;
214
+ }
215
+ function inspectSearchReadiness(dependencies, packageRoot, runtime, platform, architecture, currentBunVersion, exists, readText, realpath) {
216
+ const qmd = dependencies.find(({ name }) => name === "@tobilu/qmd");
217
+ const qmdStatus = qmd?.status ?? "unavailable";
218
+ const runtimeStatus = runtime === "bun" && currentBunVersion !== expectedBunVersion ? "partial" : "ready";
219
+ const installedQmdDirectory = installedDependencyDirectory("@tobilu/qmd", packageRoot, readText)?.directory;
220
+ const qmdDirectory = installedQmdDirectory === undefined ? packageRoot : canonicalDirectory(installedQmdDirectory, realpath);
221
+ let sqlite;
222
+ if (runtime === "bun" && platform === "darwin") {
223
+ const selectedLibraryPath = homebrewSqliteLibraryPaths.find((path) => exists(path)) ?? null;
224
+ sqlite = {
225
+ provider: "homebrew",
226
+ expectedVersion: null,
227
+ installedVersion: null,
228
+ searchedLibraryPaths: homebrewSqliteLibraryPaths,
229
+ selectedLibraryPath,
230
+ nativeBindingPresent: null,
231
+ status: selectedLibraryPath === null ? "unavailable" : "ready"
232
+ };
233
+ } else if (runtime === "bun") {
234
+ sqlite = {
235
+ provider: "bun-built-in",
236
+ expectedVersion: null,
237
+ installedVersion: null,
238
+ searchedLibraryPaths: [],
239
+ selectedLibraryPath: null,
240
+ nativeBindingPresent: null,
241
+ status: "ready"
242
+ };
243
+ } else {
244
+ const installed = installedPackageVersion("better-sqlite3", qmdDirectory, readText);
245
+ const nativeBindingPresent = installed === null ? false : exists(join(installed.directory, "build", "Release", "better_sqlite3.node"));
246
+ sqlite = {
247
+ provider: "better-sqlite3",
248
+ expectedVersion: expectedBetterSqliteVersion,
249
+ installedVersion: installed?.version ?? null,
250
+ searchedLibraryPaths: [],
251
+ selectedLibraryPath: null,
252
+ nativeBindingPresent,
253
+ status: installed === null ? "unavailable" : installed.version === expectedBetterSqliteVersion && nativeBindingPresent ? "ready" : "partial"
254
+ };
255
+ }
256
+ const sqliteVecPackage = installedPackageVersion("sqlite-vec", qmdDirectory, readText);
257
+ const nativeDefinition = nativeSqliteVecPackage(platform, architecture);
258
+ const nativePackage = nativeDefinition === null ? null : installedPackageVersion(nativeDefinition.name, qmdDirectory, readText);
259
+ const nativeBinaryPresent = nativeDefinition === null || nativePackage === null ? null : exists(join(nativePackage.directory, nativeDefinition.binary));
260
+ const sqliteVecStatus = nativeDefinition === null || sqliteVecPackage === null || nativePackage === null ? "unavailable" : sqliteVecPackage.version === expectedSqliteVecVersion && nativePackage.version === expectedSqliteVecVersion && nativeBinaryPresent === true ? "ready" : "partial";
261
+ const nodeLlamaCpp = installedPackageVersion("node-llama-cpp", qmdDirectory, readText);
262
+ const canonicalNodeLlamaCppDirectory = nodeLlamaCpp === null ? qmdDirectory : canonicalDirectory(nodeLlamaCpp.directory, realpath);
263
+ const nativeEmbeddingDefinition = nativeNodeLlamaCppPackage(platform, architecture);
264
+ const nativeEmbeddingPackage = nativeEmbeddingDefinition === null ? null : installedPackageVersion(nativeEmbeddingDefinition.name, canonicalNodeLlamaCppDirectory, readText);
265
+ const nativeEmbeddingBinaryPresent = nativeEmbeddingDefinition === null || nativeEmbeddingPackage === null ? null : exists(join(nativeEmbeddingPackage.directory, nativeEmbeddingDefinition.binary));
266
+ const embeddingRuntimeStatus = nodeLlamaCpp === null ? "unavailable" : nativeEmbeddingDefinition === null || nativeEmbeddingPackage === null ? "partial" : nodeLlamaCpp.version === expectedNodeLlamaCppVersion && nativeEmbeddingPackage.version === expectedNodeLlamaCppVersion && nativeEmbeddingBinaryPresent === true ? "ready" : "partial";
267
+ const keywordStatus = capabilityStatus([
268
+ qmdStatus,
269
+ runtimeStatus,
270
+ ...runtime === "node" ? [sqlite.status] : []
271
+ ]);
272
+ return {
273
+ runtime,
274
+ platform,
275
+ architecture,
276
+ keywordOnly: {
277
+ status: keywordStatus,
278
+ modelRequired: false
279
+ },
280
+ semanticPrerequisites: {
281
+ status: capabilityStatus([keywordStatus, sqlite.status, sqliteVecStatus, embeddingRuntimeStatus]),
282
+ sqlite,
283
+ sqliteVec: {
284
+ expectedVersion: expectedSqliteVecVersion,
285
+ installedVersion: sqliteVecPackage?.version ?? null,
286
+ nativePackageName: nativeDefinition?.name ?? null,
287
+ nativeInstalledVersion: nativePackage?.version ?? null,
288
+ nativeBinaryPresent,
289
+ status: sqliteVecStatus
290
+ },
291
+ embeddingRuntime: {
292
+ expectedVersion: expectedNodeLlamaCppVersion,
293
+ installedVersion: nodeLlamaCpp?.version ?? null,
294
+ nativePackageName: nativeEmbeddingDefinition?.name ?? null,
295
+ nativeInstalledVersion: nativeEmbeddingPackage?.version ?? null,
296
+ nativeBinaryPresent: nativeEmbeddingBinaryPresent,
297
+ status: embeddingRuntimeStatus
298
+ },
299
+ embeddingModel: {
300
+ cacheStatus: "not-inspected"
301
+ }
302
+ }
303
+ };
304
+ }
305
+ function reportDependency(name, packageRoot, rootManifest, readText) {
306
+ const expectedVersion = dependencyVersions[name];
307
+ const declaredVersion = dependencyVersion(rootManifest, name);
308
+ const installed = installedDependencyDirectory(name, packageRoot, readText);
309
+ const installedVersion = typeof installed?.manifest.version === "string" ? installed.manifest.version : null;
310
+ const status = installedVersion === expectedVersion && declaredVersion !== null ? "ready" : installedVersion === null ? "unavailable" : "partial";
311
+ return { name, expectedVersion, declaredVersion, installedVersion, status };
312
+ }
313
+ var browserDefinitions = [
314
+ {
315
+ name: "Google Chrome",
316
+ macApplications: ["Google Chrome"],
317
+ executableNames: {
318
+ darwin: ["google-chrome"],
319
+ linux: ["google-chrome", "google-chrome-stable"],
320
+ win32: ["chrome"]
321
+ },
322
+ linuxPaths: [
323
+ "/usr/bin/google-chrome",
324
+ "/usr/bin/google-chrome-stable",
325
+ "/usr/local/bin/google-chrome",
326
+ "/opt/google/chrome/google-chrome"
327
+ ]
328
+ },
329
+ {
330
+ name: "Chromium",
331
+ macApplications: ["Chromium"],
332
+ executableNames: {
333
+ darwin: ["chromium"],
334
+ linux: ["chromium", "chromium-browser"],
335
+ win32: ["chromium"]
336
+ },
337
+ linuxPaths: [
338
+ "/usr/bin/chromium",
339
+ "/usr/bin/chromium-browser",
340
+ "/usr/local/bin/chromium",
341
+ "/snap/bin/chromium"
342
+ ]
343
+ },
344
+ {
345
+ name: "Microsoft Edge",
346
+ macApplications: ["Microsoft Edge"],
347
+ executableNames: {
348
+ darwin: ["microsoft-edge"],
349
+ linux: ["microsoft-edge", "microsoft-edge-stable"],
350
+ win32: ["msedge"]
351
+ },
352
+ linuxPaths: [
353
+ "/usr/bin/microsoft-edge",
354
+ "/usr/bin/microsoft-edge-stable",
355
+ "/opt/microsoft/msedge/msedge"
356
+ ]
357
+ },
358
+ {
359
+ name: "Arc",
360
+ macApplications: ["Arc"],
361
+ executableNames: {},
362
+ linuxPaths: []
363
+ }
364
+ ];
365
+ function browserPaths(definition, platform, homeDirectory, which, exists) {
366
+ const candidates = [];
367
+ if (platform === "darwin") {
368
+ for (const applicationName of definition.macApplications) {
369
+ candidates.push(`/Applications/${applicationName}.app`, join(homeDirectory, "Applications", `${applicationName}.app`));
370
+ }
371
+ }
372
+ if (platform === "linux")
373
+ candidates.push(...definition.linuxPaths);
374
+ for (const executable of definition.executableNames[platform] ?? []) {
375
+ const path = which(executable);
376
+ if (path !== null)
377
+ candidates.push(path);
378
+ }
379
+ return [...new Set(candidates.filter((path) => exists(path)))];
380
+ }
381
+ function findExecutable(name, commonPaths, which, exists) {
382
+ const fromPath = which(name);
383
+ if (fromPath !== null && exists(fromPath))
384
+ return fromPath;
385
+ return commonPaths.find((path) => exists(path)) ?? null;
386
+ }
387
+ async function commandVersion(path, arguments_, run) {
388
+ if (path === null)
389
+ return null;
390
+ try {
391
+ const result = await run({ command: [path, ...arguments_], timeoutMs: 30000, maxOutputBytes: 128 * 1024 });
392
+ if (result.exitCode !== 0)
393
+ return null;
394
+ const firstLine = (result.stdout.trim() || result.stderr.trim()).split(/\r?\n/, 1)[0] ?? "";
395
+ return firstLine === "" ? null : firstLine;
396
+ } catch {
397
+ return null;
398
+ }
399
+ }
400
+ async function inspectAgentBrowser(agentBrowserDirectory, exists, run) {
401
+ if (agentBrowserDirectory === null)
402
+ return { deriveClient: false, profiles: [] };
403
+ const executable = join(agentBrowserDirectory, "bin", "agent-browser.js");
404
+ if (!exists(executable))
405
+ return { deriveClient: false, profiles: [] };
406
+ const directory = mkdtempSync(join(tmpdir(), "hraness-kb-doctor-"));
407
+ const socketRoot = process.platform === "win32" ? tmpdir() : "/tmp";
408
+ const socketDirectory = mkdtempSync(join(socketRoot, "jc-ab-doctor-"));
409
+ chmodSync(directory, 448);
410
+ chmodSync(socketDirectory, 448);
411
+ const configPath = join(directory, "agent-browser.config.json");
412
+ writeFileSync(configPath, `{}
413
+ `, { encoding: "utf8", flag: "wx", mode: 384 });
414
+ chmodSync(configPath, 384);
415
+ const base = [process.execPath, executable, "--config", configPath];
416
+ const isolatedCommand = {
417
+ cwd: directory,
418
+ environment: isolatedAgentBrowserEnvironment(process.env, socketDirectory),
419
+ timeoutMs: 20000,
420
+ maxOutputBytes: 2 * 1024 * 1024
421
+ };
422
+ let skillsResult;
423
+ let profilesResult;
424
+ try {
425
+ [skillsResult, profilesResult] = await Promise.all([
426
+ run({ ...isolatedCommand, command: [...base, "skills", "list", "--json"] }).catch(() => null),
427
+ run({ ...isolatedCommand, command: [...base, "profiles", "--json"] }).catch(() => null)
428
+ ]);
429
+ } finally {
430
+ rmSync(socketDirectory, { recursive: true, force: true });
431
+ rmSync(directory, { recursive: true, force: true });
432
+ }
433
+ let deriveClient = false;
434
+ if (skillsResult?.exitCode === 0) {
435
+ const parsed = parseJson(skillsResult.stdout);
436
+ if (isRecord(parsed) && parsed.success === true && Array.isArray(parsed.data)) {
437
+ deriveClient = parsed.data.some((entry) => isRecord(entry) && entry.name === "derive-client");
438
+ }
439
+ }
440
+ const profiles = new Set;
441
+ if (profilesResult?.exitCode === 0) {
442
+ const parsed = parseJson(profilesResult.stdout);
443
+ if (isRecord(parsed) && parsed.success === true && Array.isArray(parsed.data)) {
444
+ for (const entry of parsed.data) {
445
+ if (!isRecord(entry) || typeof entry.name !== "string")
446
+ continue;
447
+ const name = entry.name.trim();
448
+ if (name !== "")
449
+ profiles.add(name);
450
+ }
451
+ }
452
+ }
453
+ return { deriveClient, profiles: [...profiles].sort((left, right) => left.localeCompare(right)) };
454
+ }
455
+ async function inspectClipEnvironment(options = {}) {
456
+ const packageRoot = resolve(options.packageRoot ?? findKbPackageRoot());
457
+ const homeDirectory = options.homeDirectory ?? homedir();
458
+ const platform = options.platform ?? process.platform;
459
+ const architecture = options.architecture ?? process.arch;
460
+ const runtime = options.runtime ?? (process.versions.bun === undefined ? "node" : "bun");
461
+ const exists = options.exists ?? existsSync;
462
+ const readText = options.readText ?? ((path) => readFileSync(path, "utf8"));
463
+ const realpath = options.realpath ?? realpathSync;
464
+ const which = options.which ?? ((name) => typeof Bun === "undefined" ? null : Bun.which(name));
465
+ const run = options.run ?? runDiagnosticCommand;
466
+ const rootManifest = readJsonRecord(join(packageRoot, "package.json"), readText);
467
+ const dependencies = dependencyNames.map((name) => reportDependency(name, packageRoot, rootManifest, readText));
468
+ const agentBrowserDirectory = installedDependencyDirectory("agent-browser", packageRoot, readText)?.directory ?? null;
469
+ const browsers = browserDefinitions.map((definition) => {
470
+ const paths = browserPaths(definition, platform, homeDirectory, which, exists);
471
+ return { name: definition.name, paths, status: paths.length === 0 ? "unavailable" : "ready" };
472
+ });
473
+ const ytDlpPath = findExecutable("yt-dlp", [
474
+ join(homeDirectory, ".local", "bin", "yt-dlp"),
475
+ "/opt/homebrew/bin/yt-dlp",
476
+ "/usr/local/bin/yt-dlp"
477
+ ], which, exists);
478
+ const ffmpegPath = findExecutable("ffmpeg", [
479
+ "/opt/homebrew/bin/ffmpeg",
480
+ "/usr/local/bin/ffmpeg"
481
+ ], which, exists);
482
+ const pdfinfoPath = findExecutable("pdfinfo", [
483
+ "/opt/homebrew/bin/pdfinfo",
484
+ "/usr/local/bin/pdfinfo",
485
+ "/usr/bin/pdfinfo"
486
+ ], which, exists);
487
+ const pdftohtmlPath = findExecutable("pdftohtml", [
488
+ "/opt/homebrew/bin/pdftohtml",
489
+ "/usr/local/bin/pdftohtml",
490
+ "/usr/bin/pdftohtml"
491
+ ], which, exists);
492
+ const tesseractPath = findExecutable("tesseract", [
493
+ join(homeDirectory, ".local", "bin", "tesseract"),
494
+ "/opt/homebrew/bin/tesseract",
495
+ "/usr/local/bin/tesseract",
496
+ "/usr/bin/tesseract"
497
+ ], which, exists);
498
+ const [
499
+ agentBrowser,
500
+ ytDlpVersion,
501
+ ffmpegVersion,
502
+ pdfinfoVersion,
503
+ pdftohtmlVersion,
504
+ tesseractVersion
505
+ ] = await Promise.all([
506
+ inspectAgentBrowser(agentBrowserDirectory, exists, run),
507
+ commandVersion(ytDlpPath, ["--version"], run),
508
+ commandVersion(ffmpegPath, ["-version"], run),
509
+ commandVersion(pdfinfoPath, ["-v"], run),
510
+ commandVersion(pdftohtmlPath, ["-v"], run),
511
+ commandVersion(tesseractPath, ["--version"], run)
512
+ ]);
513
+ const tools = [
514
+ {
515
+ name: "yt-dlp",
516
+ path: ytDlpPath,
517
+ version: ytDlpVersion,
518
+ status: ytDlpPath === null ? "unavailable" : ytDlpVersion === null ? "partial" : "ready"
519
+ },
520
+ {
521
+ name: "ffmpeg",
522
+ path: ffmpegPath,
523
+ version: ffmpegVersion,
524
+ status: ffmpegPath === null ? "unavailable" : ffmpegVersion === null ? "partial" : "ready"
525
+ },
526
+ {
527
+ name: "pdfinfo",
528
+ path: pdfinfoPath,
529
+ version: pdfinfoVersion,
530
+ status: pdfinfoPath === null ? "unavailable" : pdfinfoVersion === null ? "partial" : "ready"
531
+ },
532
+ {
533
+ name: "pdftohtml",
534
+ path: pdftohtmlPath,
535
+ version: pdftohtmlVersion,
536
+ status: pdftohtmlPath === null ? "unavailable" : pdftohtmlVersion === null ? "partial" : "ready"
537
+ },
538
+ {
539
+ name: "tesseract",
540
+ path: tesseractPath,
541
+ version: tesseractVersion,
542
+ status: tesseractPath === null ? "unavailable" : tesseractVersion === null ? "partial" : "ready"
543
+ }
544
+ ];
545
+ const warnings = [];
546
+ const currentBunVersion = options.currentBunVersion ?? (typeof Bun === "undefined" ? "not running under Bun" : Bun.version);
547
+ const search = inspectSearchReadiness(dependencies, packageRoot, runtime, platform, architecture, currentBunVersion, exists, readText, realpath);
548
+ if (currentBunVersion !== expectedBunVersion) {
549
+ warnings.push(`Use Bun ${expectedBunVersion}; current runtime is ${currentBunVersion}.`);
550
+ }
551
+ for (const dependency of dependencies) {
552
+ if (dependency.status === "unavailable") {
553
+ warnings.push(`${dependency.name} ${dependency.expectedVersion} is not installed; reinstall @hraness/kb with Bun.`);
554
+ } else if (dependency.status === "partial") {
555
+ warnings.push(`${dependency.name} must resolve to ${dependency.expectedVersion} for this kb release.`);
556
+ }
557
+ }
558
+ if (!agentBrowser.deriveClient) {
559
+ warnings.push("agent-browser does not expose the derive-client skill; reinstall the pinned dependency before HAR-based client work.");
560
+ }
561
+ const renderedBrowserAvailable = browsers.some(({ name, status }) => (name === "Google Chrome" || name === "Chromium") && status === "ready");
562
+ if (!renderedBrowserAvailable) {
563
+ warnings.push("Install Google Chrome or Chromium for rendered capture; Microsoft Edge can also be used through an explicitly selected CDP session, while Arc remains an explicit cookie or CDP source.");
564
+ }
565
+ if (agentBrowser.profiles.length === 0) {
566
+ warnings.push("No discoverable Chrome profile names were reported; use --browser-live, --cdp, or an explicit profile path when needed.");
567
+ }
568
+ if (ytDlpPath === null) {
569
+ warnings.push("Install yt-dlp to capture default YouTube metadata, thumbnails, and transcripts, and to enable optional --media all downloads.");
570
+ }
571
+ if (ffmpegPath === null)
572
+ warnings.push("Install ffmpeg for yt-dlp formats that require audio/video merging or remuxing.");
573
+ if (pdfinfoPath === null || pdftohtmlPath === null) {
574
+ const missing = [
575
+ ...pdfinfoPath === null ? ["pdfinfo"] : [],
576
+ ...pdftohtmlPath === null ? ["pdftohtml"] : []
577
+ ];
578
+ warnings.push(`Install Poppler's ${missing.join(" and ")} command${missing.length === 1 ? "" : "s"}; kb pdf requires both pdfinfo and pdftohtml.`);
579
+ } else if (pdfinfoVersion === null || pdftohtmlVersion === null) {
580
+ warnings.push("Poppler was found, but a PDF tool version could not be verified; kb pdf can attempt ingestion with a degraded tool report.");
581
+ }
582
+ if (tesseractPath === null) {
583
+ warnings.push("Install Tesseract to transcribe text in scans and screenshots; kb pdf still preserves native text and images without OCR.");
584
+ } else if (tesseractVersion === null) {
585
+ warnings.push("Tesseract was found, but its version could not be verified; kb pdf can attempt OCR with a degraded tool report.");
586
+ }
587
+ if (search.keywordOnly.status !== "ready") {
588
+ warnings.push("QMD keyword-only search is not ready; exact Markdown search remains available.");
589
+ }
590
+ const keywordFallback = search.keywordOnly.status === "ready" ? " Keyword-only QMD search remains available." : "";
591
+ if (runtime === "bun" && platform === "darwin" && search.semanticPrerequisites.sqlite.status !== "ready") {
592
+ warnings.push(`Install Homebrew SQLite with \`brew install sqlite\`; semantic and hybrid vector retrieval are unavailable.${keywordFallback}`);
593
+ } else if (runtime === "node" && search.semanticPrerequisites.sqlite.status !== "ready") {
594
+ warnings.push(`Reinstall @hraness/kb with Node so better-sqlite3 ${expectedBetterSqliteVersion} and its native binding are available.${keywordFallback}`);
595
+ }
596
+ if (search.semanticPrerequisites.sqliteVec.status !== "ready") {
597
+ const nativePackage = search.semanticPrerequisites.sqliteVec.nativePackageName;
598
+ warnings.push(nativePackage === null ? `sqlite-vec ${expectedSqliteVecVersion} has no native package for ${platform}-${architecture}; semantic and hybrid vector retrieval are unavailable.${keywordFallback}` : `Reinstall @hraness/kb so sqlite-vec ${expectedSqliteVecVersion} and ${nativePackage} ${expectedSqliteVecVersion} are installed; semantic and hybrid vector retrieval are unavailable.${keywordFallback}`);
599
+ }
600
+ if (search.semanticPrerequisites.embeddingRuntime.status !== "ready") {
601
+ const nativePackage = search.semanticPrerequisites.embeddingRuntime.nativePackageName;
602
+ warnings.push(nativePackage === null ? `node-llama-cpp ${expectedNodeLlamaCppVersion} has no prebuilt package for ${platform}-${architecture}; semantic and hybrid vector retrieval require a compatible local embedding runtime.${keywordFallback}` : `Reinstall @hraness/kb with Bun so node-llama-cpp ${expectedNodeLlamaCppVersion} and ${nativePackage} ${expectedNodeLlamaCppVersion} with its native binary are installed; semantic and hybrid vector retrieval are not ready.${keywordFallback}`);
603
+ }
604
+ return {
605
+ schemaVersion: 2,
606
+ generatedAt: (options.now ?? (() => new Date))().toISOString(),
607
+ bun: {
608
+ expectedVersion: expectedBunVersion,
609
+ currentVersion: currentBunVersion,
610
+ status: currentBunVersion === expectedBunVersion ? "ready" : "partial"
611
+ },
612
+ dependencies,
613
+ deriveClient: {
614
+ available: agentBrowser.deriveClient,
615
+ status: agentBrowser.deriveClient ? "ready" : "unavailable"
616
+ },
617
+ browsers,
618
+ chromeProfileNames: agentBrowser.profiles,
619
+ tools,
620
+ search,
621
+ warnings
622
+ };
623
+ }
624
+ function versionSummary(report) {
625
+ const declared = report.declaredVersion ?? "not declared";
626
+ const installed = report.installedVersion ?? "not installed";
627
+ return `${report.name}: ${report.status} (declared ${declared}; installed ${installed}; expected ${report.expectedVersion})`;
628
+ }
629
+ function renderDoctorReport(report) {
630
+ const sqlite = report.search.semanticPrerequisites.sqlite;
631
+ const sqliteDetail = sqlite.provider === "homebrew" ? sqlite.selectedLibraryPath === null ? `not found (checked ${sqlite.searchedLibraryPaths.join(", ")})` : sqlite.selectedLibraryPath : sqlite.provider === "better-sqlite3" ? `${sqlite.installedVersion ?? "not installed"}; native binding ${sqlite.nativeBindingPresent === true ? "present" : "not found"}` : "provided by Bun";
632
+ const sqliteVec = report.search.semanticPrerequisites.sqliteVec;
633
+ const nativePackage = sqliteVec.nativePackageName === null ? `unsupported for ${report.search.platform}-${report.search.architecture}` : `${sqliteVec.nativePackageName} ${sqliteVec.nativeInstalledVersion ?? "not installed"}; native binary ${sqliteVec.nativeBinaryPresent === true ? "present" : "not found"}`;
634
+ const embeddingRuntime = report.search.semanticPrerequisites.embeddingRuntime;
635
+ const nativeEmbeddingRuntime = embeddingRuntime.nativePackageName === null ? `unsupported for ${report.search.platform}-${report.search.architecture}` : `${embeddingRuntime.nativePackageName} ${embeddingRuntime.nativeInstalledVersion ?? "not installed"}; native binary ${embeddingRuntime.nativeBinaryPresent === true ? "present" : "not found"}`;
636
+ const lines = [
637
+ "KB environment",
638
+ `Bun: ${report.bun.status} (${report.bun.currentVersion}; expected ${report.bun.expectedVersion})`,
639
+ ...report.dependencies.map(versionSummary),
640
+ `agent-browser derive-client: ${report.deriveClient.status}`,
641
+ ...report.browsers.map((browser) => `${browser.name}: ${browser.status}${browser.paths.length === 0 ? "" : ` (${browser.paths.join(", ")})`}`),
642
+ `Chrome profiles: ${report.chromeProfileNames.length === 0 ? "none discovered" : report.chromeProfileNames.join(", ")}`,
643
+ ...report.tools.map((tool) => `${tool.name}: ${tool.status}${tool.version === null ? "" : ` (${tool.version})`}${tool.path === null ? "" : ` at ${tool.path}`}`),
644
+ `QMD keyword-only search: ${report.search.keywordOnly.status} (embedding model not required)`,
645
+ `Semantic prerequisites: ${report.search.semanticPrerequisites.status}`,
646
+ `SQLite extension support: ${sqlite.status} (${sqlite.provider}: ${sqliteDetail})`,
647
+ `sqlite-vec: ${sqliteVec.status} (installed ${sqliteVec.installedVersion ?? "not installed"}; expected ${sqliteVec.expectedVersion}; ${nativePackage})`,
648
+ `Embedding runtime: ${embeddingRuntime.status} (node-llama-cpp ${embeddingRuntime.installedVersion ?? "not installed"}; expected ${embeddingRuntime.expectedVersion}; ${nativeEmbeddingRuntime})`,
649
+ "Embedding model cache: not inspected; first semantic use may download the configured local model",
650
+ "Cookie/keychain probe: not performed"
651
+ ];
652
+ if (report.warnings.length > 0) {
653
+ lines.push("", "Warnings:", ...report.warnings.map((warning) => `- ${warning}`));
654
+ }
655
+ return `${lines.join(`
656
+ `)}
657
+ `;
658
+ }
659
+ var adapterCapabilities = [
660
+ {
661
+ id: "generic",
662
+ platform: "Generic web",
663
+ preferredModes: ["HTTP + Defuddle", "rendered browser fallback", "saved HTML"],
664
+ page: "best-effort",
665
+ conversations: "best-effort",
666
+ media: "best-effort",
667
+ limitations: [
668
+ "JavaScript-only regions require a browser",
669
+ "visible conversation prose can be retained, but site-specific item trees are not inferred generically"
670
+ ]
671
+ },
672
+ {
673
+ id: "x",
674
+ platform: "X",
675
+ preferredModes: ["rendered Chrome profile/live session", "Defuddle X extractor"],
676
+ page: "best-effort",
677
+ conversations: "best-effort",
678
+ media: "best-effort",
679
+ limitations: ["Only posts and replies loaded into the rendered page are captured", "virtualized or unloaded replies remain partial", "private GraphQL clients are not invoked automatically"]
680
+ },
681
+ {
682
+ id: "substack",
683
+ platform: "Substack",
684
+ preferredModes: ["HTTP + Defuddle", "authorized rendered session for subscriber pages"],
685
+ page: "access-dependent",
686
+ conversations: "best-effort",
687
+ media: "best-effort",
688
+ limitations: ["Subscriber-only text is captured only when the selected session can already view it", "visible comments are retained as unstructured rendered context with conservative counts", "email/app-only or virtualized comments can be absent"]
689
+ },
690
+ {
691
+ id: "instagram",
692
+ platform: "Instagram",
693
+ preferredModes: ["authorized rendered session", "yt-dlp for accessible media"],
694
+ page: "best-effort",
695
+ conversations: "best-effort",
696
+ media: "best-effort",
697
+ limitations: ["Generic rendered context; no dedicated item adapter", "Login walls, virtualization, and lazy-loaded comments limit completeness", "private accounts require the user's authorized session"]
698
+ },
699
+ {
700
+ id: "linkedin",
701
+ platform: "LinkedIn",
702
+ preferredModes: ["authorized rendered session", "saved rendered HTML"],
703
+ page: "best-effort",
704
+ conversations: "best-effort",
705
+ media: "best-effort",
706
+ limitations: ["Generic rendered fallback; no dedicated adapter", "UI changes and collapsed comment branches can reduce completeness", "no automated private API derivation"]
707
+ },
708
+ {
709
+ id: null,
710
+ platform: "Signed-in pages",
711
+ preferredModes: ["current browser tab", "temporary browser-profile copy", "origin-filtered cookie HTTP fallback"],
712
+ page: "access-dependent",
713
+ conversations: "access-dependent",
714
+ media: "access-dependent",
715
+ limitations: ["Captures what the selected session renders", "virtualized or unloaded regions can remain partial"]
716
+ },
717
+ {
718
+ id: "hacker-news",
719
+ platform: "Hacker News",
720
+ preferredModes: ["official Firebase API", "rendered/Defuddle fallback"],
721
+ page: "complete",
722
+ conversations: "bounded",
723
+ media: "unsupported",
724
+ limitations: ["Comment count and depth obey CLI bounds", "deleted and dead items remain represented only as exposed by the service"]
725
+ },
726
+ {
727
+ id: "reddit",
728
+ platform: "Reddit",
729
+ preferredModes: ["best-effort public listing JSON", "rendered session", "Defuddle Reddit extractor"],
730
+ page: "best-effort",
731
+ conversations: "bounded",
732
+ media: "best-effort",
733
+ limitations: ["The unofficial JSON surface can be denied or changed and falls back automatically", "collapsed/deleted branches and configured item/depth bounds remain explicit"]
734
+ },
735
+ {
736
+ id: "facebook",
737
+ platform: "Facebook",
738
+ preferredModes: ["authorized rendered session", "yt-dlp for accessible media"],
739
+ page: "best-effort",
740
+ conversations: "best-effort",
741
+ media: "best-effort",
742
+ limitations: ["Generic rendered context; no dedicated item adapter", "Only visible, loaded content is captured", "private audiences require the user's authorized session"]
743
+ },
744
+ {
745
+ id: "tiktok",
746
+ platform: "TikTok",
747
+ preferredModes: ["rendered session", "yt-dlp for accessible media"],
748
+ page: "best-effort",
749
+ conversations: "best-effort",
750
+ media: "best-effort",
751
+ limitations: ["Generic rendered context; no dedicated thread adapter", "Regional/login gates, virtualization, and lazy-loaded comments can reduce completeness", "DRM and access controls are never bypassed"]
752
+ },
753
+ {
754
+ id: "bluesky",
755
+ platform: "Bluesky",
756
+ preferredModes: ["public AT Protocol thread API", "rendered/Defuddle fallback"],
757
+ page: "complete",
758
+ conversations: "bounded",
759
+ media: "best-effort",
760
+ limitations: ["Thread count and depth obey CLI bounds", "moderation labels and unavailable records are preserved only as exposed"]
761
+ },
762
+ {
763
+ id: "threads",
764
+ platform: "Threads",
765
+ preferredModes: ["current browser tab", "rendered browser profile", "saved rendered HTML"],
766
+ page: "best-effort",
767
+ conversations: "best-effort",
768
+ media: "best-effort",
769
+ limitations: ["Only rendered posts and replies are retained", "virtualized or unloaded replies remain partial"]
770
+ },
771
+ {
772
+ id: "whatsapp",
773
+ platform: "WhatsApp Web",
774
+ preferredModes: ["current signed-in browser tab", "rendered browser profile"],
775
+ page: "access-dependent",
776
+ conversations: "best-effort",
777
+ media: "best-effort",
778
+ limitations: ["Only the open, rendered conversation is retained", "older virtualized messages can remain partial"]
779
+ },
780
+ {
781
+ id: "youtube",
782
+ platform: "YouTube",
783
+ preferredModes: ["yt-dlp metadata + thumbnail + transcript", "HTTP + Defuddle", "rendered browser"],
784
+ page: "best-effort",
785
+ conversations: "best-effort",
786
+ media: "best-effort",
787
+ limitations: [
788
+ "Transcript capture depends on an available exact-language caption track",
789
+ "Only loaded comments are retained",
790
+ "Full audio/video download remains opt-in with --media all"
791
+ ]
792
+ },
793
+ {
794
+ id: "github",
795
+ platform: "GitHub issues, pull requests, and discussions",
796
+ preferredModes: ["Defuddle GitHub extractor", "current signed-in browser tab for private repositories"],
797
+ page: "best-effort",
798
+ conversations: "best-effort",
799
+ media: "best-effort",
800
+ limitations: ["Only loaded timeline and review items are retained", "collapsed or paginated history can remain partial"]
801
+ },
802
+ {
803
+ id: "discourse",
804
+ platform: "Discourse",
805
+ preferredModes: ["Defuddle Discourse extractor", "rendered browser fallback"],
806
+ page: "best-effort",
807
+ conversations: "best-effort",
808
+ media: "best-effort",
809
+ limitations: ["Only loaded topic posts are retained", "long or login-gated topics can remain partial"]
810
+ }
811
+ ];
812
+ function renderAdapterCapabilities(capabilities = adapterCapabilities) {
813
+ const lines = [
814
+ "Clip adapters",
815
+ "Statuses: complete, bounded, best-effort, access-dependent, unsupported.",
816
+ ""
817
+ ];
818
+ for (const capability of capabilities) {
819
+ lines.push(`${capability.platform}: page=${capability.page}, conversations=${capability.conversations}, media=${capability.media}`, ` modes: ${capability.preferredModes.join("; ")}`, ...capability.limitations.map((limitation) => ` - ${limitation}`));
820
+ }
821
+ lines.push("", "Use the current browser tab or a temporary copy of a signed-in browser profile for pages you already have open.", "Capture is ingestion-only: it reads rendered content and does not post, like, follow, send, or submit.");
822
+ return `${lines.join(`
823
+ `)}
824
+ `;
825
+ }
826
+
827
+ export { expectedBunVersion, expectedQmdVersion, expectedSqliteVecVersion, expectedNodeLlamaCppVersion, runDiagnosticCommand, inspectClipEnvironment, renderDoctorReport, adapterCapabilities, renderAdapterCapabilities };