@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.
- package/assets/portable/.env.example +3 -0
- package/assets/portable/docker-compose.yml +18 -0
- package/assets/portable/releases.json +22 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/base-command.d.ts +21 -2
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +47 -4
- package/dist/base-command.js.map +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +5 -1
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/auth/status.d.ts +1 -1
- package/dist/commands/auth/status.d.ts.map +1 -1
- package/dist/commands/auth/status.js +4 -0
- package/dist/commands/auth/status.js.map +1 -1
- package/dist/commands/chat.d.ts +5 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +32 -13
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config/get.d.ts.map +1 -1
- package/dist/commands/config/get.js +3 -1
- package/dist/commands/config/get.js.map +1 -1
- package/dist/commands/config/list.d.ts.map +1 -1
- package/dist/commands/config/list.js +12 -4
- package/dist/commands/config/list.js.map +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +27 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +155 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/import.d.ts +27 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +124 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/index/scan.d.ts.map +1 -1
- package/dist/commands/index/scan.js +24 -5
- package/dist/commands/index/scan.js.map +1 -1
- package/dist/commands/knowledge/amend.d.ts +1 -0
- package/dist/commands/knowledge/amend.d.ts.map +1 -1
- package/dist/commands/knowledge/amend.js +11 -2
- package/dist/commands/knowledge/amend.js.map +1 -1
- package/dist/commands/knowledge/attach.d.ts +19 -0
- package/dist/commands/knowledge/attach.d.ts.map +1 -0
- package/dist/commands/knowledge/attach.js +51 -0
- package/dist/commands/knowledge/attach.js.map +1 -0
- package/dist/commands/knowledge/create.d.ts +9 -1
- package/dist/commands/knowledge/create.d.ts.map +1 -1
- package/dist/commands/knowledge/create.js +26 -4
- package/dist/commands/knowledge/create.js.map +1 -1
- package/dist/commands/knowledge/update.d.ts +1 -0
- package/dist/commands/knowledge/update.d.ts.map +1 -1
- package/dist/commands/knowledge/update.js +4 -1
- package/dist/commands/knowledge/update.js.map +1 -1
- package/dist/commands/login.d.ts +19 -3
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +113 -64
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +35 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/mcp/serve.d.ts +16 -0
- package/dist/commands/mcp/serve.d.ts.map +1 -0
- package/dist/commands/mcp/serve.js +50 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/menu.d.ts.map +1 -1
- package/dist/commands/menu.js +39 -3
- package/dist/commands/menu.js.map +1 -1
- package/dist/commands/peer/accept-fingerprint.d.ts +17 -0
- package/dist/commands/peer/accept-fingerprint.d.ts.map +1 -0
- package/dist/commands/peer/accept-fingerprint.js +35 -0
- package/dist/commands/peer/accept-fingerprint.js.map +1 -0
- package/dist/commands/peer/add.d.ts +22 -0
- package/dist/commands/peer/add.d.ts.map +1 -0
- package/dist/commands/peer/add.js +44 -0
- package/dist/commands/peer/add.js.map +1 -0
- package/dist/commands/peer/cancel.d.ts +13 -0
- package/dist/commands/peer/cancel.d.ts.map +1 -0
- package/dist/commands/peer/cancel.js +20 -0
- package/dist/commands/peer/cancel.js.map +1 -0
- package/dist/commands/peer/link.d.ts +16 -0
- package/dist/commands/peer/link.d.ts.map +1 -0
- package/dist/commands/peer/link.js +32 -0
- package/dist/commands/peer/link.js.map +1 -0
- package/dist/commands/peer/links.d.ts +12 -0
- package/dist/commands/peer/links.d.ts.map +1 -0
- package/dist/commands/peer/links.js +25 -0
- package/dist/commands/peer/links.js.map +1 -0
- package/dist/commands/peer/list.d.ts +10 -0
- package/dist/commands/peer/list.d.ts.map +1 -0
- package/dist/commands/peer/list.js +22 -0
- package/dist/commands/peer/list.js.map +1 -0
- package/dist/commands/peer/pull.d.ts +19 -0
- package/dist/commands/peer/pull.d.ts.map +1 -0
- package/dist/commands/peer/pull.js +30 -0
- package/dist/commands/peer/pull.js.map +1 -0
- package/dist/commands/peer/push.d.ts +19 -0
- package/dist/commands/peer/push.d.ts.map +1 -0
- package/dist/commands/peer/push.js +30 -0
- package/dist/commands/peer/push.js.map +1 -0
- package/dist/commands/peer/remove.d.ts +12 -0
- package/dist/commands/peer/remove.d.ts.map +1 -0
- package/dist/commands/peer/remove.js +19 -0
- package/dist/commands/peer/remove.js.map +1 -0
- package/dist/commands/peer/rotate-key.d.ts +14 -0
- package/dist/commands/peer/rotate-key.d.ts.map +1 -0
- package/dist/commands/peer/rotate-key.js +26 -0
- package/dist/commands/peer/rotate-key.js.map +1 -0
- package/dist/commands/peer/run.d.ts +13 -0
- package/dist/commands/peer/run.d.ts.map +1 -0
- package/dist/commands/peer/run.js +31 -0
- package/dist/commands/peer/run.js.map +1 -0
- package/dist/commands/peer/runs.d.ts +15 -0
- package/dist/commands/peer/runs.d.ts.map +1 -0
- package/dist/commands/peer/runs.js +32 -0
- package/dist/commands/peer/runs.js.map +1 -0
- package/dist/commands/peer/test.d.ts +13 -0
- package/dist/commands/peer/test.d.ts.map +1 -0
- package/dist/commands/peer/test.js +30 -0
- package/dist/commands/peer/test.js.map +1 -0
- package/dist/commands/peer/unlink.d.ts +13 -0
- package/dist/commands/peer/unlink.d.ts.map +1 -0
- package/dist/commands/peer/unlink.js +20 -0
- package/dist/commands/peer/unlink.js.map +1 -0
- package/dist/commands/pg/status.d.ts.map +1 -1
- package/dist/commands/pg/status.js +6 -3
- package/dist/commands/pg/status.js.map +1 -1
- package/dist/commands/pg/up.d.ts.map +1 -1
- package/dist/commands/pg/up.js +9 -4
- package/dist/commands/pg/up.js.map +1 -1
- package/dist/commands/platform/status.d.ts.map +1 -1
- package/dist/commands/platform/status.js +4 -4
- package/dist/commands/platform/status.js.map +1 -1
- package/dist/commands/profile/purge.d.ts +20 -0
- package/dist/commands/profile/purge.d.ts.map +1 -0
- package/dist/commands/profile/purge.js +43 -0
- package/dist/commands/profile/purge.js.map +1 -0
- package/dist/commands/repo/context.d.ts.map +1 -1
- package/dist/commands/repo/context.js +3 -1
- package/dist/commands/repo/context.js.map +1 -1
- package/dist/commands/repo/graph.d.ts +3 -6
- package/dist/commands/repo/graph.d.ts.map +1 -1
- package/dist/commands/repo/graph.js +24 -40
- package/dist/commands/repo/graph.js.map +1 -1
- package/dist/commands/repo/index/index.d.ts +1 -0
- package/dist/commands/repo/index/index.d.ts.map +1 -1
- package/dist/commands/repo/index/index.js +100 -12
- package/dist/commands/repo/index/index.js.map +1 -1
- package/dist/commands/repo/search.d.ts.map +1 -1
- package/dist/commands/repo/search.js +2 -0
- package/dist/commands/repo/search.js.map +1 -1
- package/dist/commands/repo/setup.d.ts +9 -2
- package/dist/commands/repo/setup.d.ts.map +1 -1
- package/dist/commands/repo/setup.js +84 -8
- package/dist/commands/repo/setup.js.map +1 -1
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +21 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/transfer.d.ts +30 -0
- package/dist/commands/transfer.d.ts.map +1 -0
- package/dist/commands/transfer.js +140 -0
- package/dist/commands/transfer.js.map +1 -0
- package/dist/commands/tui.js +1 -1
- package/dist/commands/tui.js.map +1 -1
- package/dist/commands/up.d.ts +5 -2
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +41 -12
- package/dist/commands/up.js.map +1 -1
- package/dist/lib/attach.d.ts +48 -0
- package/dist/lib/attach.d.ts.map +1 -0
- package/dist/lib/attach.js +91 -0
- package/dist/lib/attach.js.map +1 -0
- package/dist/lib/chat-render.d.ts +39 -0
- package/dist/lib/chat-render.d.ts.map +1 -0
- package/dist/lib/chat-render.js +77 -0
- package/dist/lib/chat-render.js.map +1 -0
- package/dist/lib/cmek-guard.d.ts +52 -0
- package/dist/lib/cmek-guard.d.ts.map +1 -0
- package/dist/lib/cmek-guard.js +73 -0
- package/dist/lib/cmek-guard.js.map +1 -0
- package/dist/lib/content.d.ts +7 -0
- package/dist/lib/content.d.ts.map +1 -1
- package/dist/lib/content.js +9 -2
- package/dist/lib/content.js.map +1 -1
- package/dist/lib/credential-purge.d.ts +52 -0
- package/dist/lib/credential-purge.d.ts.map +1 -0
- package/dist/lib/credential-purge.js +37 -0
- package/dist/lib/credential-purge.js.map +1 -0
- package/dist/lib/ingest.d.ts.map +1 -1
- package/dist/lib/ingest.js +7 -3
- package/dist/lib/ingest.js.map +1 -1
- package/dist/lib/local-mcp-server.d.ts +82 -0
- package/dist/lib/local-mcp-server.d.ts.map +1 -0
- package/dist/lib/local-mcp-server.js +252 -0
- package/dist/lib/local-mcp-server.js.map +1 -0
- package/dist/lib/local-mcp-stdio.d.ts +8 -0
- package/dist/lib/local-mcp-stdio.d.ts.map +1 -0
- package/dist/lib/local-mcp-stdio.js +50 -0
- package/dist/lib/local-mcp-stdio.js.map +1 -0
- package/dist/lib/login-guided.d.ts +20 -0
- package/dist/lib/login-guided.d.ts.map +1 -0
- package/dist/lib/login-guided.js +89 -0
- package/dist/lib/login-guided.js.map +1 -0
- package/dist/lib/login.d.ts +58 -12
- package/dist/lib/login.d.ts.map +1 -1
- package/dist/lib/login.js +170 -12
- package/dist/lib/login.js.map +1 -1
- package/dist/lib/onboarding.d.ts +56 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +67 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/peer-command.d.ts +51 -0
- package/dist/lib/peer-command.d.ts.map +1 -0
- package/dist/lib/peer-command.js +120 -0
- package/dist/lib/peer-command.js.map +1 -0
- package/dist/lib/peer-support.d.ts +67 -0
- package/dist/lib/peer-support.d.ts.map +1 -0
- package/dist/lib/peer-support.js +154 -0
- package/dist/lib/peer-support.js.map +1 -0
- package/dist/lib/platform-frontdoor-runtime.d.ts.map +1 -1
- package/dist/lib/platform-frontdoor-runtime.js +4 -0
- package/dist/lib/platform-frontdoor-runtime.js.map +1 -1
- package/dist/lib/platform-frontdoor.d.ts +18 -2
- package/dist/lib/platform-frontdoor.d.ts.map +1 -1
- package/dist/lib/platform-frontdoor.js +148 -9
- package/dist/lib/platform-frontdoor.js.map +1 -1
- package/dist/lib/platform-lifecycle.d.ts +7 -0
- package/dist/lib/platform-lifecycle.d.ts.map +1 -1
- package/dist/lib/platform-lifecycle.js +14 -3
- package/dist/lib/platform-lifecycle.js.map +1 -1
- package/dist/lib/platform-onboarding.d.ts +1 -0
- package/dist/lib/platform-onboarding.d.ts.map +1 -1
- package/dist/lib/platform-onboarding.js +13 -2
- package/dist/lib/platform-onboarding.js.map +1 -1
- package/dist/lib/platform-profile-purge.d.ts +1 -1
- package/dist/lib/platform-profile-purge.d.ts.map +1 -1
- package/dist/lib/platform-profile-purge.js +2 -1
- package/dist/lib/platform-profile-purge.js.map +1 -1
- package/dist/lib/platform-provider-prompt.d.ts +4 -0
- package/dist/lib/platform-provider-prompt.d.ts.map +1 -1
- package/dist/lib/platform-provider-prompt.js +4 -0
- package/dist/lib/platform-provider-prompt.js.map +1 -1
- package/dist/lib/platform-release-manifest.d.ts +14 -0
- package/dist/lib/platform-release-manifest.d.ts.map +1 -1
- package/dist/lib/platform-release-manifest.js +24 -0
- package/dist/lib/platform-release-manifest.js.map +1 -1
- package/dist/lib/portability-shared.d.ts +49 -0
- package/dist/lib/portability-shared.d.ts.map +1 -0
- package/dist/lib/portability-shared.js +220 -0
- package/dist/lib/portability-shared.js.map +1 -0
- package/dist/lib/project-mcp-config.d.ts +14 -0
- package/dist/lib/project-mcp-config.d.ts.map +1 -0
- package/dist/lib/project-mcp-config.js +78 -0
- package/dist/lib/project-mcp-config.js.map +1 -0
- package/dist/lib/release-package-graph.d.ts +154 -0
- package/dist/lib/release-package-graph.d.ts.map +1 -0
- package/dist/lib/release-package-graph.js +249 -0
- package/dist/lib/release-package-graph.js.map +1 -0
- package/dist/lib/repo-binding.d.ts +7 -2
- package/dist/lib/repo-binding.d.ts.map +1 -1
- package/dist/lib/repo-binding.js +131 -18
- package/dist/lib/repo-binding.js.map +1 -1
- package/dist/lib/repo-command-support.d.ts +3 -1
- package/dist/lib/repo-command-support.d.ts.map +1 -1
- package/dist/lib/repo-command-support.js.map +1 -1
- package/dist/lib/repo-local-mcp-backend.d.ts +65 -0
- package/dist/lib/repo-local-mcp-backend.d.ts.map +1 -0
- package/dist/lib/repo-local-mcp-backend.js +288 -0
- package/dist/lib/repo-local-mcp-backend.js.map +1 -0
- package/dist/lib/repo-local-mcp-runtime.d.ts +17 -0
- package/dist/lib/repo-local-mcp-runtime.d.ts.map +1 -0
- package/dist/lib/repo-local-mcp-runtime.js +136 -0
- package/dist/lib/repo-local-mcp-runtime.js.map +1 -0
- package/dist/lib/repo-scope-migration.d.ts +4 -0
- package/dist/lib/repo-scope-migration.d.ts.map +1 -0
- package/dist/lib/repo-scope-migration.js +55 -0
- package/dist/lib/repo-scope-migration.js.map +1 -0
- package/dist/lib/repo-scope.d.ts +9 -0
- package/dist/lib/repo-scope.d.ts.map +1 -0
- package/dist/lib/repo-scope.js +15 -0
- package/dist/lib/repo-scope.js.map +1 -0
- package/dist/lib/safe-file.d.ts +141 -0
- package/dist/lib/safe-file.d.ts.map +1 -0
- package/dist/lib/safe-file.js +296 -0
- package/dist/lib/safe-file.js.map +1 -0
- package/dist/lib/setup-console.d.ts +1 -1
- package/dist/lib/setup-console.d.ts.map +1 -1
- package/dist/lib/setup-console.js +3 -1
- package/dist/lib/setup-console.js.map +1 -1
- package/dist/lib/setup-page.d.ts.map +1 -1
- package/dist/lib/setup-page.js +18 -6
- package/dist/lib/setup-page.js.map +1 -1
- package/dist/lib/setup-plan.d.ts +2 -1
- package/dist/lib/setup-plan.d.ts.map +1 -1
- package/dist/lib/setup-plan.js +9 -2
- package/dist/lib/setup-plan.js.map +1 -1
- package/dist/lib/setup-server.d.ts.map +1 -1
- package/dist/lib/setup-server.js +13 -3
- package/dist/lib/setup-server.js.map +1 -1
- package/dist/lib/sso-login.d.ts +42 -0
- package/dist/lib/sso-login.d.ts.map +1 -0
- package/dist/lib/sso-login.js +135 -0
- package/dist/lib/sso-login.js.map +1 -0
- package/dist/lib/transfer.d.ts +80 -0
- package/dist/lib/transfer.d.ts.map +1 -0
- package/dist/lib/transfer.js +173 -0
- package/dist/lib/transfer.js.map +1 -0
- package/dist/lib/tui.d.ts +0 -6
- package/dist/lib/tui.d.ts.map +1 -1
- package/dist/lib/tui.js +6 -1
- package/dist/lib/tui.js.map +1 -1
- package/dist/lib/ui.js +1 -1
- package/dist/lib/ui.js.map +1 -1
- package/dist/menu/run.d.ts +7 -0
- package/dist/menu/run.d.ts.map +1 -1
- package/dist/menu/run.js +38 -2
- package/dist/menu/run.js.map +1 -1
- package/oclif.manifest.json +3400 -1361
- 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"}
|
package/dist/lib/content.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/lib/content.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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)
|
package/dist/lib/content.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
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"}
|
package/dist/lib/ingest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/lib/ingest.js
CHANGED
|
@@ -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
|
-
|
|
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'),
|
package/dist/lib/ingest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"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"}
|