@knowzai/cli 0.1.0 → 0.2.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 (321) hide show
  1. package/assets/portable/.env.example +3 -0
  2. package/assets/portable/docker-compose.yml +18 -0
  3. package/assets/portable/releases.json +22 -0
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/base-command.d.ts +21 -2
  6. package/dist/base-command.d.ts.map +1 -1
  7. package/dist/base-command.js +47 -4
  8. package/dist/base-command.js.map +1 -1
  9. package/dist/commands/ask.d.ts.map +1 -1
  10. package/dist/commands/ask.js +5 -1
  11. package/dist/commands/ask.js.map +1 -1
  12. package/dist/commands/auth/status.d.ts +1 -1
  13. package/dist/commands/auth/status.d.ts.map +1 -1
  14. package/dist/commands/auth/status.js +4 -0
  15. package/dist/commands/auth/status.js.map +1 -1
  16. package/dist/commands/chat.d.ts +5 -0
  17. package/dist/commands/chat.d.ts.map +1 -1
  18. package/dist/commands/chat.js +32 -13
  19. package/dist/commands/chat.js.map +1 -1
  20. package/dist/commands/config/get.d.ts.map +1 -1
  21. package/dist/commands/config/get.js +3 -1
  22. package/dist/commands/config/get.js.map +1 -1
  23. package/dist/commands/config/list.d.ts.map +1 -1
  24. package/dist/commands/config/list.js +12 -4
  25. package/dist/commands/config/list.js.map +1 -1
  26. package/dist/commands/doctor.js +1 -1
  27. package/dist/commands/doctor.js.map +1 -1
  28. package/dist/commands/export.d.ts +27 -0
  29. package/dist/commands/export.d.ts.map +1 -0
  30. package/dist/commands/export.js +155 -0
  31. package/dist/commands/export.js.map +1 -0
  32. package/dist/commands/import.d.ts +27 -0
  33. package/dist/commands/import.d.ts.map +1 -0
  34. package/dist/commands/import.js +124 -0
  35. package/dist/commands/import.js.map +1 -0
  36. package/dist/commands/index/scan.d.ts.map +1 -1
  37. package/dist/commands/index/scan.js +24 -5
  38. package/dist/commands/index/scan.js.map +1 -1
  39. package/dist/commands/knowledge/amend.d.ts +1 -0
  40. package/dist/commands/knowledge/amend.d.ts.map +1 -1
  41. package/dist/commands/knowledge/amend.js +11 -2
  42. package/dist/commands/knowledge/amend.js.map +1 -1
  43. package/dist/commands/knowledge/attach.d.ts +19 -0
  44. package/dist/commands/knowledge/attach.d.ts.map +1 -0
  45. package/dist/commands/knowledge/attach.js +51 -0
  46. package/dist/commands/knowledge/attach.js.map +1 -0
  47. package/dist/commands/knowledge/create.d.ts +9 -1
  48. package/dist/commands/knowledge/create.d.ts.map +1 -1
  49. package/dist/commands/knowledge/create.js +26 -4
  50. package/dist/commands/knowledge/create.js.map +1 -1
  51. package/dist/commands/knowledge/update.d.ts +1 -0
  52. package/dist/commands/knowledge/update.d.ts.map +1 -1
  53. package/dist/commands/knowledge/update.js +4 -1
  54. package/dist/commands/knowledge/update.js.map +1 -1
  55. package/dist/commands/login.d.ts +19 -3
  56. package/dist/commands/login.d.ts.map +1 -1
  57. package/dist/commands/login.js +113 -64
  58. package/dist/commands/login.js.map +1 -1
  59. package/dist/commands/logout.d.ts +2 -0
  60. package/dist/commands/logout.d.ts.map +1 -1
  61. package/dist/commands/logout.js +35 -1
  62. package/dist/commands/logout.js.map +1 -1
  63. package/dist/commands/mcp/serve.d.ts +16 -0
  64. package/dist/commands/mcp/serve.d.ts.map +1 -0
  65. package/dist/commands/mcp/serve.js +50 -0
  66. package/dist/commands/mcp/serve.js.map +1 -0
  67. package/dist/commands/menu.d.ts.map +1 -1
  68. package/dist/commands/menu.js +39 -3
  69. package/dist/commands/menu.js.map +1 -1
  70. package/dist/commands/peer/accept-fingerprint.d.ts +17 -0
  71. package/dist/commands/peer/accept-fingerprint.d.ts.map +1 -0
  72. package/dist/commands/peer/accept-fingerprint.js +35 -0
  73. package/dist/commands/peer/accept-fingerprint.js.map +1 -0
  74. package/dist/commands/peer/add.d.ts +22 -0
  75. package/dist/commands/peer/add.d.ts.map +1 -0
  76. package/dist/commands/peer/add.js +44 -0
  77. package/dist/commands/peer/add.js.map +1 -0
  78. package/dist/commands/peer/cancel.d.ts +13 -0
  79. package/dist/commands/peer/cancel.d.ts.map +1 -0
  80. package/dist/commands/peer/cancel.js +20 -0
  81. package/dist/commands/peer/cancel.js.map +1 -0
  82. package/dist/commands/peer/link.d.ts +16 -0
  83. package/dist/commands/peer/link.d.ts.map +1 -0
  84. package/dist/commands/peer/link.js +32 -0
  85. package/dist/commands/peer/link.js.map +1 -0
  86. package/dist/commands/peer/links.d.ts +12 -0
  87. package/dist/commands/peer/links.d.ts.map +1 -0
  88. package/dist/commands/peer/links.js +25 -0
  89. package/dist/commands/peer/links.js.map +1 -0
  90. package/dist/commands/peer/list.d.ts +10 -0
  91. package/dist/commands/peer/list.d.ts.map +1 -0
  92. package/dist/commands/peer/list.js +22 -0
  93. package/dist/commands/peer/list.js.map +1 -0
  94. package/dist/commands/peer/pull.d.ts +19 -0
  95. package/dist/commands/peer/pull.d.ts.map +1 -0
  96. package/dist/commands/peer/pull.js +30 -0
  97. package/dist/commands/peer/pull.js.map +1 -0
  98. package/dist/commands/peer/push.d.ts +19 -0
  99. package/dist/commands/peer/push.d.ts.map +1 -0
  100. package/dist/commands/peer/push.js +30 -0
  101. package/dist/commands/peer/push.js.map +1 -0
  102. package/dist/commands/peer/remove.d.ts +12 -0
  103. package/dist/commands/peer/remove.d.ts.map +1 -0
  104. package/dist/commands/peer/remove.js +19 -0
  105. package/dist/commands/peer/remove.js.map +1 -0
  106. package/dist/commands/peer/rotate-key.d.ts +14 -0
  107. package/dist/commands/peer/rotate-key.d.ts.map +1 -0
  108. package/dist/commands/peer/rotate-key.js +26 -0
  109. package/dist/commands/peer/rotate-key.js.map +1 -0
  110. package/dist/commands/peer/run.d.ts +13 -0
  111. package/dist/commands/peer/run.d.ts.map +1 -0
  112. package/dist/commands/peer/run.js +31 -0
  113. package/dist/commands/peer/run.js.map +1 -0
  114. package/dist/commands/peer/runs.d.ts +15 -0
  115. package/dist/commands/peer/runs.d.ts.map +1 -0
  116. package/dist/commands/peer/runs.js +32 -0
  117. package/dist/commands/peer/runs.js.map +1 -0
  118. package/dist/commands/peer/test.d.ts +13 -0
  119. package/dist/commands/peer/test.d.ts.map +1 -0
  120. package/dist/commands/peer/test.js +30 -0
  121. package/dist/commands/peer/test.js.map +1 -0
  122. package/dist/commands/peer/unlink.d.ts +13 -0
  123. package/dist/commands/peer/unlink.d.ts.map +1 -0
  124. package/dist/commands/peer/unlink.js +20 -0
  125. package/dist/commands/peer/unlink.js.map +1 -0
  126. package/dist/commands/pg/status.d.ts.map +1 -1
  127. package/dist/commands/pg/status.js +6 -3
  128. package/dist/commands/pg/status.js.map +1 -1
  129. package/dist/commands/pg/up.d.ts.map +1 -1
  130. package/dist/commands/pg/up.js +9 -4
  131. package/dist/commands/pg/up.js.map +1 -1
  132. package/dist/commands/platform/status.d.ts.map +1 -1
  133. package/dist/commands/platform/status.js +4 -4
  134. package/dist/commands/platform/status.js.map +1 -1
  135. package/dist/commands/profile/purge.d.ts +20 -0
  136. package/dist/commands/profile/purge.d.ts.map +1 -0
  137. package/dist/commands/profile/purge.js +43 -0
  138. package/dist/commands/profile/purge.js.map +1 -0
  139. package/dist/commands/repo/context.d.ts.map +1 -1
  140. package/dist/commands/repo/context.js +3 -1
  141. package/dist/commands/repo/context.js.map +1 -1
  142. package/dist/commands/repo/graph.d.ts +3 -6
  143. package/dist/commands/repo/graph.d.ts.map +1 -1
  144. package/dist/commands/repo/graph.js +24 -40
  145. package/dist/commands/repo/graph.js.map +1 -1
  146. package/dist/commands/repo/index/index.d.ts +1 -0
  147. package/dist/commands/repo/index/index.d.ts.map +1 -1
  148. package/dist/commands/repo/index/index.js +100 -12
  149. package/dist/commands/repo/index/index.js.map +1 -1
  150. package/dist/commands/repo/search.d.ts.map +1 -1
  151. package/dist/commands/repo/search.js +2 -0
  152. package/dist/commands/repo/search.js.map +1 -1
  153. package/dist/commands/repo/setup.d.ts +9 -2
  154. package/dist/commands/repo/setup.d.ts.map +1 -1
  155. package/dist/commands/repo/setup.js +84 -8
  156. package/dist/commands/repo/setup.js.map +1 -1
  157. package/dist/commands/status.d.ts +4 -0
  158. package/dist/commands/status.d.ts.map +1 -1
  159. package/dist/commands/status.js +21 -3
  160. package/dist/commands/status.js.map +1 -1
  161. package/dist/commands/transfer.d.ts +30 -0
  162. package/dist/commands/transfer.d.ts.map +1 -0
  163. package/dist/commands/transfer.js +140 -0
  164. package/dist/commands/transfer.js.map +1 -0
  165. package/dist/commands/tui.js +1 -1
  166. package/dist/commands/tui.js.map +1 -1
  167. package/dist/commands/up.d.ts +5 -2
  168. package/dist/commands/up.d.ts.map +1 -1
  169. package/dist/commands/up.js +41 -12
  170. package/dist/commands/up.js.map +1 -1
  171. package/dist/lib/attach.d.ts +48 -0
  172. package/dist/lib/attach.d.ts.map +1 -0
  173. package/dist/lib/attach.js +91 -0
  174. package/dist/lib/attach.js.map +1 -0
  175. package/dist/lib/chat-render.d.ts +39 -0
  176. package/dist/lib/chat-render.d.ts.map +1 -0
  177. package/dist/lib/chat-render.js +77 -0
  178. package/dist/lib/chat-render.js.map +1 -0
  179. package/dist/lib/cmek-guard.d.ts +52 -0
  180. package/dist/lib/cmek-guard.d.ts.map +1 -0
  181. package/dist/lib/cmek-guard.js +73 -0
  182. package/dist/lib/cmek-guard.js.map +1 -0
  183. package/dist/lib/content.d.ts +7 -0
  184. package/dist/lib/content.d.ts.map +1 -1
  185. package/dist/lib/content.js +9 -2
  186. package/dist/lib/content.js.map +1 -1
  187. package/dist/lib/credential-purge.d.ts +52 -0
  188. package/dist/lib/credential-purge.d.ts.map +1 -0
  189. package/dist/lib/credential-purge.js +37 -0
  190. package/dist/lib/credential-purge.js.map +1 -0
  191. package/dist/lib/ingest.d.ts.map +1 -1
  192. package/dist/lib/ingest.js +7 -3
  193. package/dist/lib/ingest.js.map +1 -1
  194. package/dist/lib/local-mcp-server.d.ts +82 -0
  195. package/dist/lib/local-mcp-server.d.ts.map +1 -0
  196. package/dist/lib/local-mcp-server.js +252 -0
  197. package/dist/lib/local-mcp-server.js.map +1 -0
  198. package/dist/lib/local-mcp-stdio.d.ts +8 -0
  199. package/dist/lib/local-mcp-stdio.d.ts.map +1 -0
  200. package/dist/lib/local-mcp-stdio.js +50 -0
  201. package/dist/lib/local-mcp-stdio.js.map +1 -0
  202. package/dist/lib/login-guided.d.ts +20 -0
  203. package/dist/lib/login-guided.d.ts.map +1 -0
  204. package/dist/lib/login-guided.js +89 -0
  205. package/dist/lib/login-guided.js.map +1 -0
  206. package/dist/lib/login.d.ts +58 -12
  207. package/dist/lib/login.d.ts.map +1 -1
  208. package/dist/lib/login.js +170 -12
  209. package/dist/lib/login.js.map +1 -1
  210. package/dist/lib/onboarding.d.ts +56 -0
  211. package/dist/lib/onboarding.d.ts.map +1 -0
  212. package/dist/lib/onboarding.js +67 -0
  213. package/dist/lib/onboarding.js.map +1 -0
  214. package/dist/lib/peer-command.d.ts +51 -0
  215. package/dist/lib/peer-command.d.ts.map +1 -0
  216. package/dist/lib/peer-command.js +120 -0
  217. package/dist/lib/peer-command.js.map +1 -0
  218. package/dist/lib/peer-support.d.ts +67 -0
  219. package/dist/lib/peer-support.d.ts.map +1 -0
  220. package/dist/lib/peer-support.js +154 -0
  221. package/dist/lib/peer-support.js.map +1 -0
  222. package/dist/lib/platform-frontdoor-runtime.d.ts.map +1 -1
  223. package/dist/lib/platform-frontdoor-runtime.js +4 -0
  224. package/dist/lib/platform-frontdoor-runtime.js.map +1 -1
  225. package/dist/lib/platform-frontdoor.d.ts +18 -2
  226. package/dist/lib/platform-frontdoor.d.ts.map +1 -1
  227. package/dist/lib/platform-frontdoor.js +148 -9
  228. package/dist/lib/platform-frontdoor.js.map +1 -1
  229. package/dist/lib/platform-lifecycle.d.ts +7 -0
  230. package/dist/lib/platform-lifecycle.d.ts.map +1 -1
  231. package/dist/lib/platform-lifecycle.js +14 -3
  232. package/dist/lib/platform-lifecycle.js.map +1 -1
  233. package/dist/lib/platform-onboarding.d.ts +1 -0
  234. package/dist/lib/platform-onboarding.d.ts.map +1 -1
  235. package/dist/lib/platform-onboarding.js +13 -2
  236. package/dist/lib/platform-onboarding.js.map +1 -1
  237. package/dist/lib/platform-profile-purge.d.ts +1 -1
  238. package/dist/lib/platform-profile-purge.d.ts.map +1 -1
  239. package/dist/lib/platform-profile-purge.js +2 -1
  240. package/dist/lib/platform-profile-purge.js.map +1 -1
  241. package/dist/lib/platform-provider-prompt.d.ts +4 -0
  242. package/dist/lib/platform-provider-prompt.d.ts.map +1 -1
  243. package/dist/lib/platform-provider-prompt.js +4 -0
  244. package/dist/lib/platform-provider-prompt.js.map +1 -1
  245. package/dist/lib/platform-release-manifest.d.ts +14 -0
  246. package/dist/lib/platform-release-manifest.d.ts.map +1 -1
  247. package/dist/lib/platform-release-manifest.js +24 -0
  248. package/dist/lib/platform-release-manifest.js.map +1 -1
  249. package/dist/lib/portability-shared.d.ts +49 -0
  250. package/dist/lib/portability-shared.d.ts.map +1 -0
  251. package/dist/lib/portability-shared.js +220 -0
  252. package/dist/lib/portability-shared.js.map +1 -0
  253. package/dist/lib/project-mcp-config.d.ts +14 -0
  254. package/dist/lib/project-mcp-config.d.ts.map +1 -0
  255. package/dist/lib/project-mcp-config.js +78 -0
  256. package/dist/lib/project-mcp-config.js.map +1 -0
  257. package/dist/lib/release-package-graph.d.ts +154 -0
  258. package/dist/lib/release-package-graph.d.ts.map +1 -0
  259. package/dist/lib/release-package-graph.js +249 -0
  260. package/dist/lib/release-package-graph.js.map +1 -0
  261. package/dist/lib/repo-binding.d.ts +7 -2
  262. package/dist/lib/repo-binding.d.ts.map +1 -1
  263. package/dist/lib/repo-binding.js +131 -18
  264. package/dist/lib/repo-binding.js.map +1 -1
  265. package/dist/lib/repo-command-support.d.ts +3 -1
  266. package/dist/lib/repo-command-support.d.ts.map +1 -1
  267. package/dist/lib/repo-command-support.js.map +1 -1
  268. package/dist/lib/repo-local-mcp-backend.d.ts +65 -0
  269. package/dist/lib/repo-local-mcp-backend.d.ts.map +1 -0
  270. package/dist/lib/repo-local-mcp-backend.js +288 -0
  271. package/dist/lib/repo-local-mcp-backend.js.map +1 -0
  272. package/dist/lib/repo-local-mcp-runtime.d.ts +17 -0
  273. package/dist/lib/repo-local-mcp-runtime.d.ts.map +1 -0
  274. package/dist/lib/repo-local-mcp-runtime.js +136 -0
  275. package/dist/lib/repo-local-mcp-runtime.js.map +1 -0
  276. package/dist/lib/repo-scope-migration.d.ts +4 -0
  277. package/dist/lib/repo-scope-migration.d.ts.map +1 -0
  278. package/dist/lib/repo-scope-migration.js +55 -0
  279. package/dist/lib/repo-scope-migration.js.map +1 -0
  280. package/dist/lib/repo-scope.d.ts +9 -0
  281. package/dist/lib/repo-scope.d.ts.map +1 -0
  282. package/dist/lib/repo-scope.js +15 -0
  283. package/dist/lib/repo-scope.js.map +1 -0
  284. package/dist/lib/safe-file.d.ts +141 -0
  285. package/dist/lib/safe-file.d.ts.map +1 -0
  286. package/dist/lib/safe-file.js +296 -0
  287. package/dist/lib/safe-file.js.map +1 -0
  288. package/dist/lib/setup-console.d.ts +1 -1
  289. package/dist/lib/setup-console.d.ts.map +1 -1
  290. package/dist/lib/setup-console.js +3 -1
  291. package/dist/lib/setup-console.js.map +1 -1
  292. package/dist/lib/setup-page.d.ts.map +1 -1
  293. package/dist/lib/setup-page.js +18 -6
  294. package/dist/lib/setup-page.js.map +1 -1
  295. package/dist/lib/setup-plan.d.ts +2 -1
  296. package/dist/lib/setup-plan.d.ts.map +1 -1
  297. package/dist/lib/setup-plan.js +9 -2
  298. package/dist/lib/setup-plan.js.map +1 -1
  299. package/dist/lib/setup-server.d.ts.map +1 -1
  300. package/dist/lib/setup-server.js +13 -3
  301. package/dist/lib/setup-server.js.map +1 -1
  302. package/dist/lib/sso-login.d.ts +42 -0
  303. package/dist/lib/sso-login.d.ts.map +1 -0
  304. package/dist/lib/sso-login.js +135 -0
  305. package/dist/lib/sso-login.js.map +1 -0
  306. package/dist/lib/transfer.d.ts +80 -0
  307. package/dist/lib/transfer.d.ts.map +1 -0
  308. package/dist/lib/transfer.js +173 -0
  309. package/dist/lib/transfer.js.map +1 -0
  310. package/dist/lib/tui.d.ts +0 -6
  311. package/dist/lib/tui.d.ts.map +1 -1
  312. package/dist/lib/tui.js +6 -1
  313. package/dist/lib/tui.js.map +1 -1
  314. package/dist/lib/ui.js +1 -1
  315. package/dist/lib/ui.js.map +1 -1
  316. package/dist/menu/run.d.ts +7 -0
  317. package/dist/menu/run.d.ts.map +1 -1
  318. package/dist/menu/run.js +38 -2
  319. package/dist/menu/run.js.map +1 -1
  320. package/oclif.manifest.json +3400 -1361
  321. package/package.json +15 -10
@@ -0,0 +1,91 @@
1
+ import { closeSync } from 'node:fs';
2
+ import { basename } from 'node:path';
3
+ import { logger, exitCodeForError, ExitCode } from '@knowzai/core';
4
+ import { openSafeFile, assertSafeFiles, MAX_SAFE_UPLOAD_BYTES } from './safe-file.js';
5
+ /**
6
+ * Fail before any network call when a path is unreadable, and name every offender.
7
+ *
8
+ * H7 — this now delegates to the shared safe-file gate, so an attachment is held
9
+ * to the same denied-path/symlink/size policy as `knowledge create --file`,
10
+ * `ingest add`, and `index scan`. The size ceiling is the streaming one: the
11
+ * uploader never holds a whole attachment in memory.
12
+ */
13
+ export function assertReadableFiles(paths) {
14
+ assertSafeFiles(paths, { operation: 'attach', maxBytes: MAX_SAFE_UPLOAD_BYTES });
15
+ }
16
+ function renderProgress(label, p) {
17
+ const pct = p.totalBytes ? Math.round((p.bytesSent / p.totalBytes) * 100) : 100;
18
+ process.stderr.write(`\r${label} ${pct}% (chunk ${p.chunkIndex + 1}/${p.totalChunks})`);
19
+ }
20
+ function clearProgress() {
21
+ process.stderr.write('\r\u001b[K'); // erase the redraw line
22
+ }
23
+ /**
24
+ * Upload each file and link it to `knowledgeId`. Every file is independent: one
25
+ * failure does not stop the run, and a file whose bytes landed but whose link
26
+ * failed reports its fileRecordId so the upload is recoverable (R9).
27
+ *
28
+ * H7 (TOCTOU) — the batch pre-flight above runs once, before the first byte
29
+ * moves; a ten-file run can spend minutes between that check and the last
30
+ * upload. So each file is re-judged with `O_NOFOLLOW` + `fstat` immediately
31
+ * before it is uploaded, and — this is the part that closes the race rather
32
+ * than merely narrowing it — the APPROVED DESCRIPTOR is what gets handed to the
33
+ * uploader. Re-checking the path and then passing the path still left a window:
34
+ * `initialize` round-trips to the server before the first byte is read, and a
35
+ * path swapped for a symlink to a credential file during that round trip would
36
+ * have been re-opened and uploaded. Nothing downstream re-resolves the path now,
37
+ * so the file that was judged is the file whose bytes are sent.
38
+ */
39
+ export async function uploadAndAttach(client, knowledgeId, paths, opts = {}) {
40
+ const showProgress = opts.progress ?? Boolean(process.stderr.isTTY);
41
+ const results = [];
42
+ for (const [index, path] of paths.entries()) {
43
+ const label = `[${index + 1}/${paths.length}] ${basename(path)}`;
44
+ let fileRecordId;
45
+ try {
46
+ const approved = openSafeFile(path, { operation: 'attach', maxBytes: MAX_SAFE_UPLOAD_BYTES });
47
+ let uploadedId;
48
+ try {
49
+ uploadedId = (await client.files.upload({
50
+ path,
51
+ // The judged descriptor, not the path — see the note above.
52
+ fd: approved.fd,
53
+ contentType: opts.contentType,
54
+ // R3 — keeps the parent's AwaitingAttachments window open across the run.
55
+ parentKnowledgeId: knowledgeId,
56
+ onProgress: showProgress ? (p) => renderProgress(label, p) : undefined,
57
+ })).fileRecordId;
58
+ }
59
+ finally {
60
+ // We opened it, so we close it — the uploader only borrows.
61
+ closeSync(approved.fd);
62
+ }
63
+ fileRecordId = uploadedId;
64
+ await client.knowledge.attachFile(knowledgeId, fileRecordId);
65
+ if (showProgress)
66
+ clearProgress();
67
+ logger.info(`${label} attached (${fileRecordId})`);
68
+ results.push({ file: path, ok: true, fileRecordId });
69
+ }
70
+ catch (e) {
71
+ if (showProgress)
72
+ clearProgress();
73
+ const message = e instanceof Error ? e.message : String(e);
74
+ logger.error(fileRecordId
75
+ ? `${label} uploaded as ${fileRecordId} but could not be attached: ${message}`
76
+ : `${label} failed: ${message}`);
77
+ results.push({ file: path, ok: false, fileRecordId, error: message, exitCode: exitCodeForError(e) });
78
+ }
79
+ }
80
+ return results;
81
+ }
82
+ /**
83
+ * Exit non-zero when any file failed, without discarding the payload that names
84
+ * the recoverable fileRecordIds — `--json` still emits exactly one document (R9/R10).
85
+ */
86
+ export function applyAttachExitCode(results) {
87
+ const failed = results.find((r) => !r.ok);
88
+ if (failed)
89
+ process.exitCode = failed.exitCode ?? ExitCode.Api;
90
+ }
91
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/lib/attach.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface ChatSource {
2
+ knowledgeId?: string;
3
+ title: string;
4
+ relevance: number;
5
+ }
6
+ /**
7
+ * Collapse repeats of the same knowledge item.
8
+ *
9
+ * The server emits one `sources` entry per retrieved CHUNK, so a single item can
10
+ * appear many times over — a real answer has been observed listing the same GUID
11
+ * five times. Keyed on `knowledgeId`, falling back to the title when an entry
12
+ * carries no id. First-seen order is preserved (that is retrieval order, which is
13
+ * meaningful); only the relevance is upgraded when a later duplicate scores higher.
14
+ */
15
+ export declare function dedupeSources(sources: ChatSource[]): ChatSource[];
16
+ /**
17
+ * Render the source list: deduped, capped, and silent when there is nothing to
18
+ * show. Returns lines rather than printing so it stays testable and so callers
19
+ * decide the stream.
20
+ */
21
+ export declare function formatSourceLines(sources: ChatSource[], opts: {
22
+ limit: number;
23
+ showIds?: boolean;
24
+ }): string[];
25
+ export interface StatusLine {
26
+ update(message: string): void;
27
+ clear(): void;
28
+ }
29
+ /**
30
+ * A single self-overwriting progress line on stderr.
31
+ *
32
+ * The pipeline emits several near-duplicate status messages per turn ("Searching
33
+ * knowledge base…" then "Searching…"). Printing one line each leaves a stack of
34
+ * stale progress above the answer, so this redraws in place and erases itself
35
+ * before the answer is written. Off entirely when stderr is not a TTY — the same
36
+ * rule the upload progress follows, so redirected output stays clean.
37
+ */
38
+ export declare function createStatusLine(stream: NodeJS.WriteStream): StatusLine;
39
+ //# sourceMappingURL=chat-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-render.d.ts","sourceRoot":"","sources":["../../src/lib/chat-render.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CASjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,EAAE,EACrB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACzC,MAAM,EAAE,CAiBV;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,UAAU,CAmBvE"}
@@ -0,0 +1,77 @@
1
+ import chalk from 'chalk';
2
+ /** Erase the current line and return the cursor to its start. */
3
+ const ERASE_LINE = '\r\u001b[K';
4
+ /**
5
+ * Collapse repeats of the same knowledge item.
6
+ *
7
+ * The server emits one `sources` entry per retrieved CHUNK, so a single item can
8
+ * appear many times over — a real answer has been observed listing the same GUID
9
+ * five times. Keyed on `knowledgeId`, falling back to the title when an entry
10
+ * carries no id. First-seen order is preserved (that is retrieval order, which is
11
+ * meaningful); only the relevance is upgraded when a later duplicate scores higher.
12
+ */
13
+ export function dedupeSources(sources) {
14
+ const byKey = new Map();
15
+ for (const s of sources) {
16
+ const key = s.knowledgeId ?? `title:${s.title}`;
17
+ const seen = byKey.get(key);
18
+ if (!seen)
19
+ byKey.set(key, { ...s });
20
+ else if (s.relevance > seen.relevance)
21
+ seen.relevance = s.relevance;
22
+ }
23
+ return [...byKey.values()];
24
+ }
25
+ /**
26
+ * Render the source list: deduped, capped, and silent when there is nothing to
27
+ * show. Returns lines rather than printing so it stays testable and so callers
28
+ * decide the stream.
29
+ */
30
+ export function formatSourceLines(sources, opts) {
31
+ const unique = dedupeSources(sources);
32
+ if (!unique.length)
33
+ return [];
34
+ const showIds = opts.showIds ?? true;
35
+ const shown = unique.slice(0, opts.limit);
36
+ const lines = [chalk.dim('sources:')];
37
+ for (const s of shown) {
38
+ lines.push(showIds && s.knowledgeId
39
+ ? ` - ${s.title} ${chalk.dim(`(${s.knowledgeId})`)}`
40
+ : ` - ${s.title}`);
41
+ }
42
+ const withheld = unique.length - shown.length;
43
+ if (withheld > 0)
44
+ lines.push(chalk.dim(` … ${withheld} more — /sources for the full list`));
45
+ return lines;
46
+ }
47
+ /**
48
+ * A single self-overwriting progress line on stderr.
49
+ *
50
+ * The pipeline emits several near-duplicate status messages per turn ("Searching
51
+ * knowledge base…" then "Searching…"). Printing one line each leaves a stack of
52
+ * stale progress above the answer, so this redraws in place and erases itself
53
+ * before the answer is written. Off entirely when stderr is not a TTY — the same
54
+ * rule the upload progress follows, so redirected output stays clean.
55
+ */
56
+ export function createStatusLine(stream) {
57
+ const enabled = Boolean(stream.isTTY);
58
+ let drawn = false;
59
+ let last;
60
+ return {
61
+ update(message) {
62
+ if (!enabled || !message || message === last)
63
+ return;
64
+ stream.write(`${ERASE_LINE}${chalk.dim(` · ${message}`)}`);
65
+ drawn = true;
66
+ last = message;
67
+ },
68
+ clear() {
69
+ if (!enabled || !drawn)
70
+ return;
71
+ stream.write(ERASE_LINE);
72
+ drawn = false;
73
+ last = undefined;
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=chat-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-render.js","sourceRoot":"","sources":["../../src/lib/chat-render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,iEAAiE;AACjE,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAAqB;IACjD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;aAC/B,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;IACtE,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAqB,EACrB,IAA0C;IAE1C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAE9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CACR,OAAO,IAAI,CAAC,CAAC,WAAW;YACtB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,WAAW,GAAG,CAAC,EAAE;YACrD,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CACrB,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC9C,IAAI,QAAQ,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,QAAQ,oCAAoC,CAAC,CAAC,CAAC;IAC7F,OAAO,KAAK,CAAC;AACf,CAAC;AAOD;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA0B;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,IAAwB,CAAC;IAE7B,OAAO;QACL,MAAM,CAAC,OAAe;YACpB,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI;gBAAE,OAAO;YACrD,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YAC5D,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,GAAG,OAAO,CAAC;QACjB,CAAC;QACD,KAAK;YACH,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK;gBAAE,OAAO;YAC/B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACzB,KAAK,GAAG,KAAK,CAAC;YACd,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ import type { ContentEncryptor } from '@knowzai/local-store';
2
+ /**
3
+ * CLI_SecurityReleaseReadiness H4 — CMEK must hold on EVERY cloud write path,
4
+ * not just `knowz sync push`.
5
+ *
6
+ * Model-3 CMEK promises the cloud only ever stores ciphertext. Before this
7
+ * guard, `sync push`/`sync run`/`capture seal` honoured it, but three sibling
8
+ * paths wrote to the same API without an encryptor:
9
+ *
10
+ * - `knowledge create` / `knowledge update` call `client.knowledge.*` directly
11
+ * - `repo setup --sync` / `repo index --push` build a `SyncEngine` with no
12
+ * encryptor, so repository source is pushed as plaintext (and pulled content
13
+ * is stored locally as undecrypted ciphertext)
14
+ * - file attachments upload raw bytes, for which no envelope format exists
15
+ * - `knowledge amend` asks the CLOUD to rewrite stored content it cannot read
16
+ *
17
+ * The first two are fixed by threading the profile's encryptor through. The
18
+ * last two cannot be fixed by encrypting. For attachments: the chunked-upload protocol is a byte
19
+ * stream with a server-computed SHA-256, and there is no file-envelope contract
20
+ * to carry the CMEK header. Inventing one here would produce files the server,
21
+ * the web client, and every other consumer could not read. So it fails CLOSED —
22
+ * a refusal the user can see beats a silent plaintext upload under a feature
23
+ * whose entire promise is that plaintext never leaves the machine.
24
+ */
25
+ /** Encrypt outbound content when the profile has CMEK enabled. */
26
+ export declare function encryptOutboundContent(encryptor: ContentEncryptor | undefined, content: string | undefined): string | undefined;
27
+ /** Decrypt content fetched from the cloud when it carries a CMEK envelope. */
28
+ export declare function decryptInboundContent(encryptor: ContentEncryptor | undefined, content: string | undefined): string | undefined;
29
+ /**
30
+ * Fail closed on CLOUD-SIDE CONTENT MUTATION while CMEK is enabled.
31
+ *
32
+ * `knowledge create`/`update` are safe under CMEK because the CLI encrypts the
33
+ * content before it leaves the machine — the cloud only ever holds ciphertext.
34
+ * `knowledge amend` is a different shape: it ships a natural-language
35
+ * instruction that the SERVER applies to the stored content. Under CMEK the
36
+ * server holds ciphertext and has no KEK, so it cannot read what it is being
37
+ * asked to amend, let alone re-encrypt the result. There is no
38
+ * encrypted-amendment envelope, so the only outcomes available are a failed
39
+ * amendment or — worse — one that decrypts nothing, rewrites the record in
40
+ * plaintext, and quietly ends the ciphertext-only guarantee.
41
+ *
42
+ * So it refuses, for the same reason `assertNoRawUploadUnderCmek` does: a
43
+ * refusal the user can see beats a silent downgrade of the feature's entire
44
+ * promise.
45
+ */
46
+ export declare function assertNoCloudAmendUnderCmek(encryptor: ContentEncryptor | undefined, operation: string): void;
47
+ /**
48
+ * Fail closed on raw-byte cloud writes while CMEK is enabled. `operation` names
49
+ * the user-facing action so the refusal explains itself.
50
+ */
51
+ export declare function assertNoRawUploadUnderCmek(encryptor: ContentEncryptor | undefined, operation: string): void;
52
+ //# sourceMappingURL=cmek-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmek-guard.d.ts","sourceRoot":"","sources":["../../src/lib/cmek-guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,kEAAkE;AAClE,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,GAAG,SAAS,EACvC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,MAAM,GAAG,SAAS,CAGpB;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,gBAAgB,GAAG,SAAS,EACvC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,MAAM,GAAG,SAAS,CAGpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,gBAAgB,GAAG,SAAS,EACvC,SAAS,EAAE,MAAM,GAChB,IAAI,CASN;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,gBAAgB,GAAG,SAAS,EACvC,SAAS,EAAE,MAAM,GAChB,IAAI,CAQN"}
@@ -0,0 +1,73 @@
1
+ import { ExitCode, KnowzError } from '@knowzai/core';
2
+ /**
3
+ * CLI_SecurityReleaseReadiness H4 — CMEK must hold on EVERY cloud write path,
4
+ * not just `knowz sync push`.
5
+ *
6
+ * Model-3 CMEK promises the cloud only ever stores ciphertext. Before this
7
+ * guard, `sync push`/`sync run`/`capture seal` honoured it, but three sibling
8
+ * paths wrote to the same API without an encryptor:
9
+ *
10
+ * - `knowledge create` / `knowledge update` call `client.knowledge.*` directly
11
+ * - `repo setup --sync` / `repo index --push` build a `SyncEngine` with no
12
+ * encryptor, so repository source is pushed as plaintext (and pulled content
13
+ * is stored locally as undecrypted ciphertext)
14
+ * - file attachments upload raw bytes, for which no envelope format exists
15
+ * - `knowledge amend` asks the CLOUD to rewrite stored content it cannot read
16
+ *
17
+ * The first two are fixed by threading the profile's encryptor through. The
18
+ * last two cannot be fixed by encrypting. For attachments: the chunked-upload protocol is a byte
19
+ * stream with a server-computed SHA-256, and there is no file-envelope contract
20
+ * to carry the CMEK header. Inventing one here would produce files the server,
21
+ * the web client, and every other consumer could not read. So it fails CLOSED —
22
+ * a refusal the user can see beats a silent plaintext upload under a feature
23
+ * whose entire promise is that plaintext never leaves the machine.
24
+ */
25
+ /** Encrypt outbound content when the profile has CMEK enabled. */
26
+ export function encryptOutboundContent(encryptor, content) {
27
+ if (content == null || !encryptor)
28
+ return content;
29
+ return encryptor.encrypt(content);
30
+ }
31
+ /** Decrypt content fetched from the cloud when it carries a CMEK envelope. */
32
+ export function decryptInboundContent(encryptor, content) {
33
+ if (content == null || !encryptor)
34
+ return content;
35
+ return encryptor.isEncrypted(content) ? encryptor.decrypt(content) : content;
36
+ }
37
+ /**
38
+ * Fail closed on CLOUD-SIDE CONTENT MUTATION while CMEK is enabled.
39
+ *
40
+ * `knowledge create`/`update` are safe under CMEK because the CLI encrypts the
41
+ * content before it leaves the machine — the cloud only ever holds ciphertext.
42
+ * `knowledge amend` is a different shape: it ships a natural-language
43
+ * instruction that the SERVER applies to the stored content. Under CMEK the
44
+ * server holds ciphertext and has no KEK, so it cannot read what it is being
45
+ * asked to amend, let alone re-encrypt the result. There is no
46
+ * encrypted-amendment envelope, so the only outcomes available are a failed
47
+ * amendment or — worse — one that decrypts nothing, rewrites the record in
48
+ * plaintext, and quietly ends the ciphertext-only guarantee.
49
+ *
50
+ * So it refuses, for the same reason `assertNoRawUploadUnderCmek` does: a
51
+ * refusal the user can see beats a silent downgrade of the feature's entire
52
+ * promise.
53
+ */
54
+ export function assertNoCloudAmendUnderCmek(encryptor, operation) {
55
+ if (!encryptor)
56
+ return;
57
+ throw new KnowzError(`${operation} is blocked while CMEK is enabled for this profile: the amendment is applied by the cloud, ` +
58
+ 'which holds only ciphertext and no key, and there is no encrypted-amendment envelope format yet — ' +
59
+ 'so the amendment would either fail or store plaintext in the cloud. ' +
60
+ 'Edit the content locally and run `knowz knowledge update` instead, or run this on a profile without CMEK.', ExitCode.Usage);
61
+ }
62
+ /**
63
+ * Fail closed on raw-byte cloud writes while CMEK is enabled. `operation` names
64
+ * the user-facing action so the refusal explains itself.
65
+ */
66
+ export function assertNoRawUploadUnderCmek(encryptor, operation) {
67
+ if (!encryptor)
68
+ return;
69
+ throw new KnowzError(`${operation} is blocked while CMEK is enabled for this profile: file attachments are uploaded as raw bytes ` +
70
+ 'and there is no encrypted-file envelope format yet, so the upload would store plaintext in the cloud. ' +
71
+ 'Attach the content as knowledge text instead, or run this on a profile without CMEK.', ExitCode.Usage);
72
+ }
73
+ //# sourceMappingURL=cmek-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmek-guard.js","sourceRoot":"","sources":["../../src/lib/cmek-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,kEAAkE;AAClE,MAAM,UAAU,sBAAsB,CACpC,SAAuC,EACvC,OAA2B;IAE3B,IAAI,OAAO,IAAI,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,qBAAqB,CACnC,SAAuC,EACvC,OAA2B;IAE3B,IAAI,OAAO,IAAI,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAuC,EACvC,SAAiB;IAEjB,IAAI,CAAC,SAAS;QAAE,OAAO;IACvB,MAAM,IAAI,UAAU,CAClB,GAAG,SAAS,6FAA6F;QACvG,oGAAoG;QACpG,sEAAsE;QACtE,2GAA2G,EAC7G,QAAQ,CAAC,KAAK,CACf,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,SAAuC,EACvC,SAAiB;IAEjB,IAAI,CAAC,SAAS;QAAE,OAAO;IACvB,MAAM,IAAI,UAAU,CAClB,GAAG,SAAS,iGAAiG;QAC3G,wGAAwG;QACxG,sFAAsF,EACxF,QAAQ,CAAC,KAAK,CACf,CAAC;AACJ,CAAC"}
@@ -1,6 +1,13 @@
1
1
  /**
2
2
  * Resolve knowledge content from (in priority order): --content, --file, or
3
3
  * piped stdin. Returns undefined if none provided.
4
+ *
5
+ * H7 — `--file` is gated by the shared safe-file policy AT THE READ. oclif's
6
+ * `exists: true` only proves the path resolves; it says nothing about whether the
7
+ * file is a credential store, a symlink pointing at one, or large enough to
8
+ * exhaust memory when read whole. `readSafeFile` judges and consumes one
9
+ * `O_NOFOLLOW` descriptor, so the file that passed the policy is the file whose
10
+ * bytes become knowledge content.
4
11
  */
5
12
  export declare function resolveContentInput(opts: {
6
13
  content?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU9B"}
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU9B"}
@@ -1,13 +1,20 @@
1
- import { readFileSync } from 'node:fs';
1
+ import { readSafeFile } from './safe-file.js';
2
2
  /**
3
3
  * Resolve knowledge content from (in priority order): --content, --file, or
4
4
  * piped stdin. Returns undefined if none provided.
5
+ *
6
+ * H7 — `--file` is gated by the shared safe-file policy AT THE READ. oclif's
7
+ * `exists: true` only proves the path resolves; it says nothing about whether the
8
+ * file is a credential store, a symlink pointing at one, or large enough to
9
+ * exhaust memory when read whole. `readSafeFile` judges and consumes one
10
+ * `O_NOFOLLOW` descriptor, so the file that passed the policy is the file whose
11
+ * bytes become knowledge content.
5
12
  */
6
13
  export async function resolveContentInput(opts) {
7
14
  if (opts.content !== undefined)
8
15
  return opts.content;
9
16
  if (opts.file)
10
- return readFileSync(opts.file, 'utf8');
17
+ return readSafeFile(opts.file, { operation: '--file' }).content;
11
18
  if (!process.stdin.isTTY) {
12
19
  const chunks = [];
13
20
  for await (const chunk of process.stdin)
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAGzC;IACC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IACpD,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;YAAE,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAGzC;IACC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IACpD,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;IAC/E,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;YAAE,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import type { CredentialStore, KnowzConfig } from '@knowzai/core';
2
+ /**
3
+ * CLI_SecurityReleaseReadiness H6 — remove EVERY stored credential before the
4
+ * local configuration is deleted.
5
+ *
6
+ * Uninstall previously ran `platform destroy --yes --purge-profile`, which
7
+ * deletes exactly one profile's secret, and then `rm -rf ~/.knowz`. Every other
8
+ * profile's API key stayed in the OS keychain — and, because the profile index
9
+ * that makes keychain entries enumerable lives inside `~/.knowz`, deleting the
10
+ * config directory made those leftovers unreachable by the CLI: they can then
11
+ * only be found and removed by hand, in Keychain Access / Credential Manager.
12
+ *
13
+ * Ordering is therefore load-bearing: enumerate and delete credentials FIRST,
14
+ * local state SECOND. `purgeAllCredentials` returns what it removed so the
15
+ * caller can report it honestly rather than claiming a clean uninstall.
16
+ */
17
+ export interface CredentialPurgeResult {
18
+ /** Profiles whose credential was deleted (best-effort union of index + config). */
19
+ profiles: string[];
20
+ /** Which store the credentials lived in — surfaced so a failure is diagnosable. */
21
+ backend: string;
22
+ }
23
+ export interface CredentialPurgeDeps {
24
+ loadConfig: () => KnowzConfig;
25
+ saveConfig: (cfg: KnowzConfig) => void;
26
+ credentialStore: () => Promise<CredentialStore>;
27
+ }
28
+ /**
29
+ * Delete every credential the store knows about, then drop the profiles from
30
+ * config. Profiles are collected from BOTH the credential index and the config
31
+ * file: either one can have entries the other lost, and a purge that misses an
32
+ * orphan is exactly the failure this closes.
33
+ *
34
+ * FAILS CLOSED on an unreadable index. `listProfiles()` is the ONLY way to
35
+ * discover OS-keychain entries — the keychain cannot be enumerated, so the
36
+ * index inside `~/.knowz` is the map to them. If that read throws, the map is
37
+ * gone, and continuing would delete the local config (making the leftovers
38
+ * unreachable by the CLI forever) while reporting a clean uninstall. So the
39
+ * error propagates untouched and `credential-index.json` is left exactly as it
40
+ * was: it is the recovery artifact, and truncating it is the one irreversible
41
+ * move available here.
42
+ *
43
+ * Each credential is deleted EXACTLY ONCE. `deleteAll()` plus a per-profile
44
+ * pass was a double delete: `deleteAll()` iterates the same index, so every
45
+ * indexed profile was deleted twice, and the second attempt hit an entry the
46
+ * OS keychain no longer has. That only looked harmless because the union pass
47
+ * covers everything `deleteAll()` would have.
48
+ */
49
+ export declare function purgeAllCredentials(deps: CredentialPurgeDeps, opts?: {
50
+ removeProfiles?: boolean;
51
+ }): Promise<CredentialPurgeResult>;
52
+ //# sourceMappingURL=credential-purge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-purge.d.ts","sourceRoot":"","sources":["../../src/lib/credential-purge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,WAAW,CAAC;IAC9B,UAAU,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACjD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,mBAAmB,EACzB,IAAI,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,qBAAqB,CAAC,CAgBhC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Delete every credential the store knows about, then drop the profiles from
3
+ * config. Profiles are collected from BOTH the credential index and the config
4
+ * file: either one can have entries the other lost, and a purge that misses an
5
+ * orphan is exactly the failure this closes.
6
+ *
7
+ * FAILS CLOSED on an unreadable index. `listProfiles()` is the ONLY way to
8
+ * discover OS-keychain entries — the keychain cannot be enumerated, so the
9
+ * index inside `~/.knowz` is the map to them. If that read throws, the map is
10
+ * gone, and continuing would delete the local config (making the leftovers
11
+ * unreachable by the CLI forever) while reporting a clean uninstall. So the
12
+ * error propagates untouched and `credential-index.json` is left exactly as it
13
+ * was: it is the recovery artifact, and truncating it is the one irreversible
14
+ * move available here.
15
+ *
16
+ * Each credential is deleted EXACTLY ONCE. `deleteAll()` plus a per-profile
17
+ * pass was a double delete: `deleteAll()` iterates the same index, so every
18
+ * indexed profile was deleted twice, and the second attempt hit an entry the
19
+ * OS keychain no longer has. That only looked harmless because the union pass
20
+ * covers everything `deleteAll()` would have.
21
+ */
22
+ export async function purgeAllCredentials(deps, opts = {}) {
23
+ const store = await deps.credentialStore();
24
+ const cfg = deps.loadConfig();
25
+ const indexed = await store.listProfiles();
26
+ const profiles = [...new Set([...indexed, ...Object.keys(cfg.profiles ?? {})])].sort();
27
+ for (const profile of profiles)
28
+ await store.deleteSecret(profile);
29
+ if (opts.removeProfiles !== false) {
30
+ for (const profile of Object.keys(cfg.profiles ?? {}))
31
+ delete cfg.profiles[profile];
32
+ cfg.activeProfile = 'dev';
33
+ deps.saveConfig(cfg);
34
+ }
35
+ return { profiles, backend: store.backend() };
36
+ }
37
+ //# sourceMappingURL=credential-purge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-purge.js","sourceRoot":"","sources":["../../src/lib/credential-purge.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAyB,EACzB,OAAqC,EAAE;IAEvC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;IAE9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEvF,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,MAAM,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAElE,IAAI,IAAI,CAAC,cAAc,KAAK,KAAK,EAAE,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;YAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpF,GAAG,CAAC,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;AAChD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AAKtD,sDAAsD;AACtD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAKlD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,OAAO,CAAC,YAAY,CAAC,CAsBvB"}
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AAKtD,sDAAsD;AACtD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAKlD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,OAAO,CAAC,YAAY,CAAC,CA0BvB"}
@@ -1,6 +1,6 @@
1
- import { readFileSync, statSync } from 'node:fs';
2
1
  import { createHash } from 'node:crypto';
3
2
  import { grammarForPath } from '@knowzai/indexer';
3
+ import { readSafeFile } from './safe-file.js';
4
4
  const DOC_EXT = new Set(['md', 'markdown', 'txt', 'rst', 'adoc', 'text']);
5
5
  const NON_CODE_GRAMMARS = new Set(['json', 'yaml', 'yml', 'toml', 'html', 'css']);
6
6
  /** Infer a knowledge type from the file extension. */
@@ -18,7 +18,12 @@ export function inferType(path) {
18
18
  * it. The foundation for richer ingestion sources (email/web/watch) later.
19
19
  */
20
20
  export async function ingestFile(indexer, store, path, opts = {}) {
21
- const content = readFileSync(path, 'utf8');
21
+ // H7 an ingested file becomes a knowledge item that `sync push` ships to the
22
+ // cloud, so the same denied-path/symlink/size policy applies here as anywhere
23
+ // else the CLI reads a user-named file. The policy check and the read share
24
+ // one `O_NOFOLLOW` descriptor, and the recorded file state comes from that
25
+ // descriptor's `fstat` rather than a second, re-resolving `statSync`.
26
+ const { content, stat: st } = readSafeFile(path, { operation: 'ingest' });
22
27
  const res = await indexer.indexFile(path, content);
23
28
  const type = opts.type ?? inferType(path);
24
29
  res.item.type = type;
@@ -30,7 +35,6 @@ export async function ingestFile(indexer, store, path, opts = {}) {
30
35
  res.item.syncState = 'new';
31
36
  await store.upsertItem(res.item);
32
37
  await store.putChunks(res.item.id, res.chunks, res.vectors);
33
- const st = statSync(path);
34
38
  await store.setFileState({
35
39
  path,
36
40
  hash: createHash('sha256').update(content).digest('hex'),
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"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"}