@aliyunrds/ctxdb 1.0.6 → 1.0.8-beta.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/README.internal.md +59 -0
- package/README.md +210 -23
- package/dist/{chunk-BHRWAU3E.js → chunk-24CNB3XJ.js} +2 -2
- package/dist/{chunk-ARZX2RLR.js → chunk-AAZLOCVB.js} +246 -34
- package/dist/{chunk-7EIJB5LA.js → chunk-BBTXHFMD.js} +1 -1
- package/dist/{chunk-L4GVGVOP.js → chunk-CAXRYH6E.js} +235 -43
- package/dist/{chunk-6BB65W5W.js → chunk-HF2CWTRU.js} +2 -2
- package/dist/{chunk-B7JCQL4O.js → chunk-NJJBT52C.js} +1 -1
- package/dist/chunk-R67JELM7.js +20 -0
- package/dist/{chunk-Z6OHSEF2.js → chunk-ULAWTBBC.js} +34 -16
- package/dist/chunk-VIG4SYLU.js +8 -0
- package/dist/{chunk-VHNHVMCA.js → chunk-WSMZOFZQ.js} +3 -3
- package/dist/cli/main.js +1627 -62
- package/dist/hooks/hermes-post-llm-call.js +6 -4
- package/dist/hooks/hermes-pre-llm-call.js +9 -7
- package/dist/hooks/pre-tool-use.js +2 -1
- package/dist/hooks/session-start.js +8 -6
- package/dist/hooks/stop.js +6 -4
- package/dist/hooks/user-prompt-submit.js +8 -6
- package/dist/opencode/index.js +58 -2
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +46 -8
- package/dist/setup/skills/contextdb-memory/SKILL.md +1 -1
- package/dist/workers/version-check.js +3 -1
- package/package.json +6 -2
- /package/dist/{chunk-UEKR2Z3S.js → chunk-5ML4VSXN.js} +0 -0
package/dist/cli/main.js
CHANGED
|
@@ -10,13 +10,19 @@ import {
|
|
|
10
10
|
listKnowledgeBases,
|
|
11
11
|
minimalKbQueryResponse,
|
|
12
12
|
pollIngest,
|
|
13
|
+
pollIngestByKnowledgeBaseId,
|
|
14
|
+
updateFile,
|
|
15
|
+
updateText,
|
|
13
16
|
uploadFile,
|
|
14
|
-
uploadText
|
|
15
|
-
|
|
17
|
+
uploadText,
|
|
18
|
+
validateLocalFile
|
|
19
|
+
} from "../chunk-CAXRYH6E.js";
|
|
16
20
|
import {
|
|
21
|
+
CtxdbError,
|
|
17
22
|
DEFAULT_BASE_URL,
|
|
18
23
|
DEFAULT_USER_ID,
|
|
19
24
|
HttpClient,
|
|
25
|
+
PACKAGE_NAME,
|
|
20
26
|
PACKAGE_VERSION,
|
|
21
27
|
SUPPORTED_AGENTS,
|
|
22
28
|
agentFromEnv,
|
|
@@ -35,12 +41,16 @@ import {
|
|
|
35
41
|
save,
|
|
36
42
|
updateConfiguredDebug,
|
|
37
43
|
writeInstalledPkgVersion
|
|
38
|
-
} from "../chunk-
|
|
44
|
+
} from "../chunk-AAZLOCVB.js";
|
|
39
45
|
import {
|
|
40
46
|
beginUpdateNotification,
|
|
41
47
|
completeUpdateNotification,
|
|
42
48
|
runSelfUpdate
|
|
43
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-ULAWTBBC.js";
|
|
50
|
+
import {
|
|
51
|
+
DISTRIBUTION_MANIFEST
|
|
52
|
+
} from "../chunk-R67JELM7.js";
|
|
53
|
+
import "../chunk-VIG4SYLU.js";
|
|
44
54
|
|
|
45
55
|
// src/cli/util.ts
|
|
46
56
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -51,6 +61,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
51
61
|
// setup / init
|
|
52
62
|
"no-install-skill",
|
|
53
63
|
"no-validate",
|
|
64
|
+
"no-browser",
|
|
65
|
+
"device-code",
|
|
54
66
|
// update / upgrade
|
|
55
67
|
"self-update",
|
|
56
68
|
"no-self-update",
|
|
@@ -74,8 +86,34 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
74
86
|
// memory search / kb search (three-tier projection)
|
|
75
87
|
"raw",
|
|
76
88
|
// kb search / memory search --knowledge (W3 compaction bypass)
|
|
77
|
-
"full"
|
|
89
|
+
"full",
|
|
78
90
|
// debug recall full-content human output
|
|
91
|
+
// promotion
|
|
92
|
+
"is-default",
|
|
93
|
+
// promotion template create / update
|
|
94
|
+
"clear-default-kb",
|
|
95
|
+
// promotion settings update (clears with an empty string)
|
|
96
|
+
// review
|
|
97
|
+
"default",
|
|
98
|
+
// review template create/update --default
|
|
99
|
+
"no-default",
|
|
100
|
+
// review template update --no-default
|
|
101
|
+
"review-enabled",
|
|
102
|
+
// review settings update
|
|
103
|
+
"no-review-enabled",
|
|
104
|
+
// review settings update
|
|
105
|
+
"rewrite-enabled",
|
|
106
|
+
// review settings update
|
|
107
|
+
"no-rewrite-enabled",
|
|
108
|
+
// review settings update
|
|
109
|
+
"conflict-detection-enabled",
|
|
110
|
+
// review settings update
|
|
111
|
+
"no-conflict-detection-enabled",
|
|
112
|
+
// review settings update
|
|
113
|
+
"rewrite-auto-ingest-enabled",
|
|
114
|
+
// review settings update
|
|
115
|
+
"no-rewrite-auto-ingest-enabled"
|
|
116
|
+
// review settings update
|
|
79
117
|
]);
|
|
80
118
|
function parseArgs(argv, booleanFlags = BOOLEAN_FLAGS) {
|
|
81
119
|
const positional = [];
|
|
@@ -302,7 +340,9 @@ function fail(message, code = 1) {
|
|
|
302
340
|
}
|
|
303
341
|
|
|
304
342
|
// src/cli/main.ts
|
|
343
|
+
import { realpathSync } from "fs";
|
|
305
344
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
345
|
+
import { basename as basename2, resolve as resolve2 } from "path";
|
|
306
346
|
|
|
307
347
|
// src/setup/installer.ts
|
|
308
348
|
import {
|
|
@@ -323,8 +363,9 @@ import { join, dirname, resolve } from "path";
|
|
|
323
363
|
import { fileURLToPath } from "url";
|
|
324
364
|
import { execFileSync } from "child_process";
|
|
325
365
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
326
|
-
var
|
|
327
|
-
var
|
|
366
|
+
var PACKAGE_UNINSTALL_TARGETS = DISTRIBUTION_MANIFEST.id === "internal" ? DISTRIBUTION_MANIFEST.packageName : `${PACKAGE_NAME} @aliyunrds/ctxdb-shared`;
|
|
367
|
+
var UNINSTALL_NPM_HINT = `To also remove the npm binaries: \`npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS}\``;
|
|
368
|
+
var UNINSTALL_ORDER_HINT = `When you eventually want to uninstall: run \`ctxdb uninstall --purge-all\` FIRST, then \`npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS}\`. (npm 7+ removed uninstall lifecycle hooks, so the order matters \u2014 otherwise agent settings.json hooks + ~/.ctxdb/ residue stay behind.)`;
|
|
328
369
|
var HERMES_RESTART_HINT = "Restart any running Hermes CLI or gateway process to load the registered hooks; Hermes registers shell hooks only at process startup.";
|
|
329
370
|
var HERMES_ALLOWLIST_ACTION_HINT = "Next action [hermes-hook-allowlist]: run `hermes --accept-hooks chat`; after Hermes starts, exit the chat and rerun `ctxdb setup --agent hermes`, then restart any running Hermes CLI or gateway process.";
|
|
330
371
|
var HERMES_WEBUI_GATEWAY_HINT = "Using hermes-webui? Its default legacy backend does not load Hermes shell hooks. Set `HERMES_WEBUI_CHAT_BACKEND=gateway` to route chat through Hermes Gateway; if the gateway requires authentication, also set `HERMES_WEBUI_GATEWAY_API_KEY`, then restart hermes-webui. Reference: https://github.com/nesquena/hermes-webui/blob/master/docs/advanced-chat-setup.md#gateway-backed-browser-chat";
|
|
@@ -345,6 +386,8 @@ function skillInstallRoot(agent) {
|
|
|
345
386
|
return join(homedir(), ".config", "opencode", "skills");
|
|
346
387
|
case "hermes":
|
|
347
388
|
return join(homedir(), ".hermes", "skills");
|
|
389
|
+
case "workbuddy":
|
|
390
|
+
return join(homedir(), ".workbuddy", "skills");
|
|
348
391
|
}
|
|
349
392
|
}
|
|
350
393
|
var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
|
|
@@ -365,6 +408,8 @@ function hookConfigPath(agent) {
|
|
|
365
408
|
return join(homedir(), ".codex", "hooks.json");
|
|
366
409
|
case "claude":
|
|
367
410
|
return join(homedir(), ".claude", "settings.json");
|
|
411
|
+
case "workbuddy":
|
|
412
|
+
return join(homedir(), ".workbuddy", "settings.json");
|
|
368
413
|
case "opencode":
|
|
369
414
|
case "hermes":
|
|
370
415
|
return null;
|
|
@@ -420,6 +465,7 @@ async function runSetup(options) {
|
|
|
420
465
|
existingAgentHomes = inspectAgentHomes(agent).existing;
|
|
421
466
|
}
|
|
422
467
|
const cfg = load({ agent });
|
|
468
|
+
const persistedCredentialConfig = options.persistCredential === false ? load({ agent, env: {}, managedCredentials: false }) : null;
|
|
423
469
|
if (options.apiKey) cfg.apiKey = options.apiKey;
|
|
424
470
|
if (options.baseUrl) cfg.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
425
471
|
else if (!cfg.baseUrl) cfg.baseUrl = DEFAULT_BASE_URL;
|
|
@@ -432,19 +478,38 @@ async function runSetup(options) {
|
|
|
432
478
|
});
|
|
433
479
|
return { ok: false, steps };
|
|
434
480
|
}
|
|
435
|
-
|
|
481
|
+
const omitApiKey = options.persistCredential === false;
|
|
482
|
+
const persistedCfg = omitApiKey ? {
|
|
483
|
+
...cfg,
|
|
484
|
+
apiKey: null,
|
|
485
|
+
baseUrl: persistedCredentialConfig?.baseUrl ?? DEFAULT_BASE_URL
|
|
486
|
+
} : cfg;
|
|
487
|
+
save(persistedCfg, void 0, { agent, omitApiKey });
|
|
436
488
|
steps.push({
|
|
437
489
|
step: "write-config",
|
|
438
490
|
ok: true,
|
|
439
491
|
detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
|
|
440
492
|
});
|
|
441
|
-
if (isBuiltinAgent(agent)
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
493
|
+
if (isBuiltinAgent(agent)) {
|
|
494
|
+
const hasDefault = configuredAgents().includes("default");
|
|
495
|
+
if (!hasDefault) {
|
|
496
|
+
save(persistedCfg, void 0, { agent: "default", omitApiKey });
|
|
497
|
+
steps.push({
|
|
498
|
+
step: "write-default-config",
|
|
499
|
+
ok: true,
|
|
500
|
+
detail: `default: copied from ${agent}`
|
|
501
|
+
});
|
|
502
|
+
} else if (omitApiKey) {
|
|
503
|
+
const persistedDefault = load({
|
|
504
|
+
agent: "default",
|
|
505
|
+
env: {},
|
|
506
|
+
managedCredentials: false
|
|
507
|
+
});
|
|
508
|
+
save(persistedDefault, void 0, {
|
|
509
|
+
agent: "default",
|
|
510
|
+
omitApiKey: true
|
|
511
|
+
});
|
|
512
|
+
}
|
|
448
513
|
}
|
|
449
514
|
if (installSkill && isBuiltinAgent(agent) && existingAgentHomes.includes(agentHomeDir(agent))) {
|
|
450
515
|
const installRoot = skillInstallRoot(agent);
|
|
@@ -754,6 +819,7 @@ async function runSetupAll(options = {}, dependencies = {}) {
|
|
|
754
819
|
apiKey,
|
|
755
820
|
baseUrl: options.baseUrl ?? existing.baseUrl,
|
|
756
821
|
userId: options.userId ?? existing.userId,
|
|
822
|
+
persistCredential: options.persistCredential,
|
|
757
823
|
installSkill: options.installSkill,
|
|
758
824
|
validate: options.validate
|
|
759
825
|
}));
|
|
@@ -1304,9 +1370,13 @@ function stripCtxdbHooks(agent) {
|
|
|
1304
1370
|
if (!path) return 0;
|
|
1305
1371
|
return stripCtxdbHooksFromFile(path);
|
|
1306
1372
|
}
|
|
1373
|
+
var CTXDB_PACKAGE_NAMES = [
|
|
1374
|
+
"@aliyunrds/ctxdb",
|
|
1375
|
+
"@ali/ctxdb-internal"
|
|
1376
|
+
];
|
|
1307
1377
|
function locateHookPaths() {
|
|
1308
1378
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1309
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1379
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1310
1380
|
if (!pkgRoot) return null;
|
|
1311
1381
|
const preferSrc = here.includes("/src/") || here.includes("\\src\\");
|
|
1312
1382
|
if (preferSrc) {
|
|
@@ -1340,7 +1410,7 @@ function hookPathsFromDir(dir, ext) {
|
|
|
1340
1410
|
}
|
|
1341
1411
|
function locateSkillDir(variant) {
|
|
1342
1412
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1343
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1413
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1344
1414
|
if (!pkgRoot) return null;
|
|
1345
1415
|
const preferSrc = here.includes("/src/") || here.includes("\\src\\");
|
|
1346
1416
|
if (preferSrc) {
|
|
@@ -1354,13 +1424,14 @@ function locateSkillDir(variant) {
|
|
|
1354
1424
|
return null;
|
|
1355
1425
|
}
|
|
1356
1426
|
function walkUpToPackageJson(start, expectedName) {
|
|
1427
|
+
const expectedNames = Array.isArray(expectedName) ? expectedName : [expectedName];
|
|
1357
1428
|
let cur = start;
|
|
1358
1429
|
for (let i = 0; i < 8; i++) {
|
|
1359
1430
|
const p = join(cur, "package.json");
|
|
1360
1431
|
if (existsSync(p)) {
|
|
1361
1432
|
try {
|
|
1362
1433
|
const pkg = JSON.parse(readFileSync(p, "utf-8"));
|
|
1363
|
-
if (pkg.name
|
|
1434
|
+
if (expectedNames.includes(pkg.name)) return cur;
|
|
1364
1435
|
} catch {
|
|
1365
1436
|
}
|
|
1366
1437
|
}
|
|
@@ -1399,7 +1470,8 @@ var SKILL_INSTALL_TARGETS = {
|
|
|
1399
1470
|
claude: join(homedir(), ".claude", "skills"),
|
|
1400
1471
|
opencode: join(homedir(), ".config", "opencode", "skills"),
|
|
1401
1472
|
openclaw: join(homedir(), ".openclaw", "skills"),
|
|
1402
|
-
hermes: join(homedir(), ".hermes", "skills")
|
|
1473
|
+
hermes: join(homedir(), ".hermes", "skills"),
|
|
1474
|
+
workbuddy: join(homedir(), ".workbuddy", "skills")
|
|
1403
1475
|
};
|
|
1404
1476
|
function skillInstallTargetPath(target) {
|
|
1405
1477
|
return SKILL_INSTALL_TARGETS[target] ?? null;
|
|
@@ -1463,7 +1535,7 @@ function opencodeBundlePath() {
|
|
|
1463
1535
|
}
|
|
1464
1536
|
function resolveOpencodePluginEntry() {
|
|
1465
1537
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1466
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1538
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1467
1539
|
if (!pkgRoot) return null;
|
|
1468
1540
|
const candidates = [
|
|
1469
1541
|
join(pkgRoot, "dist", "opencode", "index.js"),
|
|
@@ -1504,7 +1576,7 @@ var LEGACY_MARKER_VALUES = ["@aliyunrds/ctxdb-qoder"];
|
|
|
1504
1576
|
var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
|
|
1505
1577
|
function appendOne(hooks, event, command, agent) {
|
|
1506
1578
|
if (!Array.isArray(hooks[event])) hooks[event] = [];
|
|
1507
|
-
const useExecForm = agent === "claude";
|
|
1579
|
+
const useExecForm = agent === "claude" || agent === "workbuddy";
|
|
1508
1580
|
const cmdRunner = agent === "qoder";
|
|
1509
1581
|
const shellCmd = process.platform === "win32" ? cmdRunner ? `node ${command.replace(/\\/g, "/")} --agent=${agent}` : `"${process.execPath.replace(/\\/g, "/")}" "${command.replace(/\\/g, "/")}" --agent=${agent}` : `${process.execPath} ${command} --agent=${agent}`;
|
|
1510
1582
|
const dup = hooks[event].some(
|
|
@@ -1993,6 +2065,7 @@ async function runUpgrade(options = {}) {
|
|
|
1993
2065
|
for (const agent of agents) {
|
|
1994
2066
|
results[agent] = await runSetup({
|
|
1995
2067
|
agent,
|
|
2068
|
+
persistCredential: options.persistCredential,
|
|
1996
2069
|
// Updating local package artifacts must not depend on service
|
|
1997
2070
|
// connectivity. Users can run `ctxdb ping` separately.
|
|
1998
2071
|
validate: false
|
|
@@ -2196,20 +2269,49 @@ function parseAgent(args) {
|
|
|
2196
2269
|
}
|
|
2197
2270
|
async function setup(args) {
|
|
2198
2271
|
if (args.flags.help || args.flags.h) {
|
|
2199
|
-
process.stdout.write(
|
|
2272
|
+
process.stdout.write(
|
|
2273
|
+
DISTRIBUTION_MANIFEST.capabilities.managedCredentials ? HELP.replace(
|
|
2274
|
+
" [--api-key=K] [--base-url=URL] [--user-id=ID]",
|
|
2275
|
+
" [--user-id=ID]"
|
|
2276
|
+
).replace(
|
|
2277
|
+
" Rerun for a new Agent, to repair integration files, or\n with --api-key <new-key> to replace that Agent's key.\n",
|
|
2278
|
+
" Rerun after ctxdb login to add or repair Agent integration files.\n"
|
|
2279
|
+
).replace(
|
|
2280
|
+
" --api-key <key> API key (required on first setup)\n",
|
|
2281
|
+
""
|
|
2282
|
+
).replace(
|
|
2283
|
+
" --base-url <url> Server URL (default: https://context-database.aliyuncs.com)\n",
|
|
2284
|
+
""
|
|
2285
|
+
) : HELP
|
|
2286
|
+
);
|
|
2200
2287
|
return 0;
|
|
2201
2288
|
}
|
|
2202
2289
|
const json = !!args.flags.json;
|
|
2203
2290
|
const all = args.flags.all === true;
|
|
2291
|
+
const explicitApiKey = typeof args.flags["api-key"] === "string" ? args.flags["api-key"] : void 0;
|
|
2292
|
+
if (DISTRIBUTION_MANIFEST.capabilities.managedCredentials && explicitApiKey) {
|
|
2293
|
+
process.stderr.write(
|
|
2294
|
+
"--api-key is not supported by this distribution; use ctxdb login so the credential is stored in credentials.json\n"
|
|
2295
|
+
);
|
|
2296
|
+
return 2;
|
|
2297
|
+
}
|
|
2298
|
+
if (DISTRIBUTION_MANIFEST.capabilities.managedCredentials && typeof args.flags["base-url"] === "string") {
|
|
2299
|
+
process.stderr.write(
|
|
2300
|
+
"--base-url is not supported by this distribution; use ctxdb login --server so the DATA URL is supplied by the login service\n"
|
|
2301
|
+
);
|
|
2302
|
+
return 2;
|
|
2303
|
+
}
|
|
2304
|
+
const persistCredential = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials;
|
|
2204
2305
|
if (all && args.flags.agent !== void 0) {
|
|
2205
2306
|
process.stderr.write("cannot use --all and --agent together\n");
|
|
2206
2307
|
return 2;
|
|
2207
2308
|
}
|
|
2208
2309
|
if (all) {
|
|
2209
2310
|
const result2 = await runSetupAll({
|
|
2210
|
-
apiKey:
|
|
2311
|
+
apiKey: explicitApiKey,
|
|
2211
2312
|
baseUrl: typeof args.flags["base-url"] === "string" ? args.flags["base-url"] : void 0,
|
|
2212
2313
|
userId: typeof args.flags["user-id"] === "string" ? args.flags["user-id"] : void 0,
|
|
2314
|
+
persistCredential,
|
|
2213
2315
|
installSkill: !args.flags["no-install-skill"],
|
|
2214
2316
|
validate: !args.flags["no-validate"]
|
|
2215
2317
|
});
|
|
@@ -2239,9 +2341,10 @@ ${highlightInlineCode(hint)}
|
|
|
2239
2341
|
}
|
|
2240
2342
|
const result = await runSetup({
|
|
2241
2343
|
agent,
|
|
2242
|
-
apiKey:
|
|
2344
|
+
apiKey: explicitApiKey,
|
|
2243
2345
|
baseUrl: typeof args.flags["base-url"] === "string" ? args.flags["base-url"] : void 0,
|
|
2244
2346
|
userId: typeof args.flags["user-id"] === "string" ? args.flags["user-id"] : void 0,
|
|
2347
|
+
persistCredential,
|
|
2245
2348
|
installSkill: !args.flags["no-install-skill"],
|
|
2246
2349
|
validate: !args.flags["no-validate"]
|
|
2247
2350
|
});
|
|
@@ -2260,6 +2363,7 @@ ${highlightInlineCode(h)}
|
|
|
2260
2363
|
}
|
|
2261
2364
|
|
|
2262
2365
|
// src/cli/teardown.ts
|
|
2366
|
+
var PACKAGE_UNINSTALL_TARGETS2 = DISTRIBUTION_MANIFEST.id === "internal" ? DISTRIBUTION_MANIFEST.packageName : `${PACKAGE_NAME} @aliyunrds/ctxdb-shared`;
|
|
2263
2367
|
var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
|
|
2264
2368
|
|
|
2265
2369
|
USAGE
|
|
@@ -2283,7 +2387,7 @@ FLAGS
|
|
|
2283
2387
|
|
|
2284
2388
|
NOTE
|
|
2285
2389
|
Does not run \`npm uninstall\`. After uninstall completes, remove the
|
|
2286
|
-
npm package separately: npm uninstall -g
|
|
2390
|
+
npm package separately: npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS2}
|
|
2287
2391
|
`;
|
|
2288
2392
|
function uninstall(args) {
|
|
2289
2393
|
if (args.flags.help || args.flags.h) {
|
|
@@ -2324,13 +2428,15 @@ ${h}
|
|
|
2324
2428
|
}
|
|
2325
2429
|
|
|
2326
2430
|
// src/cli/upgrade.ts
|
|
2431
|
+
var UPDATE_PACKAGE_NAME = DISTRIBUTION_MANIFEST.packageName || PACKAGE_NAME;
|
|
2432
|
+
var UPDATE_REGISTRY_ARG = DISTRIBUTION_MANIFEST.packageRegistry ? ` --registry ${DISTRIBUTION_MANIFEST.packageRegistry}` : "";
|
|
2327
2433
|
var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
|
|
2328
2434
|
|
|
2329
2435
|
USAGE
|
|
2330
2436
|
ctxdb upgrade [--agent <qoder|qoderwork|qwenwork|codex|claude|opencode|hermes>] [--no-self-update] [--json]
|
|
2331
2437
|
|
|
2332
2438
|
BEHAVIOR
|
|
2333
|
-
Pulls the latest
|
|
2439
|
+
Pulls the latest ${UPDATE_PACKAGE_NAME} package from npm, then re-runs setup
|
|
2334
2440
|
for configured agents using their existing credentials.
|
|
2335
2441
|
|
|
2336
2442
|
Without --agent: sets up all configured built-in agents.
|
|
@@ -2342,7 +2448,9 @@ FLAGS
|
|
|
2342
2448
|
--no-self-update Skip the npm package update and only re-run setup
|
|
2343
2449
|
--json Machine-readable JSON output
|
|
2344
2450
|
`;
|
|
2345
|
-
var MANUAL_UPDATE_HINT =
|
|
2451
|
+
var MANUAL_UPDATE_HINT = `Automatic update failed. Please run manually:
|
|
2452
|
+
npm install -g ${UPDATE_PACKAGE_NAME}${UPDATE_REGISTRY_ARG}
|
|
2453
|
+
ctxdb setup`;
|
|
2346
2454
|
function printUpdateFailure(error, json) {
|
|
2347
2455
|
if (json) {
|
|
2348
2456
|
process.stdout.write(JSON.stringify({ ok: false, error, hint: MANUAL_UPDATE_HINT }) + "\n");
|
|
@@ -2388,7 +2496,10 @@ async function upgrade(args) {
|
|
|
2388
2496
|
process.stderr.write("nothing to upgrade for default agent (no hooks or skills)\n");
|
|
2389
2497
|
return 0;
|
|
2390
2498
|
}
|
|
2391
|
-
const result = await runUpgrade({
|
|
2499
|
+
const result = await runUpgrade({
|
|
2500
|
+
agent: agentFlag,
|
|
2501
|
+
persistCredential: !DISTRIBUTION_MANIFEST.capabilities.managedCredentials
|
|
2502
|
+
});
|
|
2392
2503
|
if (result.ok) {
|
|
2393
2504
|
try {
|
|
2394
2505
|
writeInstalledPkgVersion(PACKAGE_VERSION);
|
|
@@ -2612,8 +2723,61 @@ function parseMetadataFlags(raw) {
|
|
|
2612
2723
|
}
|
|
2613
2724
|
|
|
2614
2725
|
// src/cli/kb-cli.ts
|
|
2726
|
+
import { basename } from "path";
|
|
2615
2727
|
var UPLOAD_TEXT_USAGE = "usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--file-path=<server-logical-path>] [--wait|--no-wait]";
|
|
2616
2728
|
var UPLOAD_FILE_USAGE = "usage: ctxdb kb upload-file <kb-name> <local-path> [--doc-name=...] [--file-path=<server-logical-path>] [--wait|--no-wait]";
|
|
2729
|
+
var UPDATE_TEXT_USAGE = "usage: ctxdb kb update-text <document-id> --text=<body> [--wait|--no-wait]\n or: ctxdb kb update-text --kb-name=<kb-name> --doc-name=<logical-name> --text=<body> [--file-path=<server-logical-path>] [--wait|--no-wait]";
|
|
2730
|
+
var UPDATE_FILE_USAGE = "usage: ctxdb kb update-file <document-id> <local-path> [--wait|--no-wait]\n or: ctxdb kb update-file <local-path> --kb-name=<kb-name> [--doc-name=<logical-name>] [--file-path=<server-logical-path>] [--wait|--no-wait]";
|
|
2731
|
+
var UPDATE_LOCATOR_FLAGS = ["kb-name", "doc-name", "file-path"];
|
|
2732
|
+
function nonEmptyFlag(args, name) {
|
|
2733
|
+
const value = args.flags[name];
|
|
2734
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
2735
|
+
}
|
|
2736
|
+
function hasAnyLocatorFlag(args) {
|
|
2737
|
+
return UPDATE_LOCATOR_FLAGS.some((name) => Object.hasOwn(args.flags, name));
|
|
2738
|
+
}
|
|
2739
|
+
function parseTextUpdateLocator(args) {
|
|
2740
|
+
if (args.positional.length === 1 && !hasAnyLocatorFlag(args)) {
|
|
2741
|
+
const documentId = args.positional[0];
|
|
2742
|
+
if (documentId) return { documentId };
|
|
2743
|
+
}
|
|
2744
|
+
if (args.positional.length === 0) {
|
|
2745
|
+
const kbName = nonEmptyFlag(args, "kb-name");
|
|
2746
|
+
const docName = nonEmptyFlag(args, "doc-name");
|
|
2747
|
+
const filePath = nonEmptyFlag(args, "file-path");
|
|
2748
|
+
const filePathValid = !Object.hasOwn(args.flags, "file-path") || !!filePath;
|
|
2749
|
+
if (kbName && docName && filePathValid) {
|
|
2750
|
+
return { kbName, docName, ...filePath ? { filePath } : {} };
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
fail(UPDATE_TEXT_USAGE, 2);
|
|
2754
|
+
}
|
|
2755
|
+
function parseFileUpdate(args) {
|
|
2756
|
+
if (args.positional.length === 2 && !hasAnyLocatorFlag(args)) {
|
|
2757
|
+
const [documentId, localPath] = args.positional;
|
|
2758
|
+
if (documentId && localPath) return { locator: { documentId }, localPath };
|
|
2759
|
+
}
|
|
2760
|
+
if (args.positional.length === 1) {
|
|
2761
|
+
const [localPath] = args.positional;
|
|
2762
|
+
const kbName = nonEmptyFlag(args, "kb-name");
|
|
2763
|
+
const docNameFlag = nonEmptyFlag(args, "doc-name");
|
|
2764
|
+
const filePath = nonEmptyFlag(args, "file-path");
|
|
2765
|
+
const optionalFlagsValid = ["doc-name", "file-path"].every(
|
|
2766
|
+
(name) => !Object.hasOwn(args.flags, name) || !!nonEmptyFlag(args, name)
|
|
2767
|
+
);
|
|
2768
|
+
if (localPath && kbName && optionalFlagsValid) {
|
|
2769
|
+
return {
|
|
2770
|
+
locator: {
|
|
2771
|
+
kbName,
|
|
2772
|
+
docName: docNameFlag ?? basename(localPath),
|
|
2773
|
+
...filePath ? { filePath } : {}
|
|
2774
|
+
},
|
|
2775
|
+
localPath
|
|
2776
|
+
};
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
fail(UPDATE_FILE_USAGE, 2);
|
|
2780
|
+
}
|
|
2617
2781
|
function shouldWaitForIngest(args, usage) {
|
|
2618
2782
|
const waitRequested = args.flags.wait !== void 0;
|
|
2619
2783
|
const noWaitRequested = args.flags["no-wait"] !== void 0;
|
|
@@ -2665,6 +2829,57 @@ async function kbUploadFile(args) {
|
|
|
2665
2829
|
printResult({ document: final }, !!args.flags.json);
|
|
2666
2830
|
return 0;
|
|
2667
2831
|
}
|
|
2832
|
+
async function kbUpdateText(args) {
|
|
2833
|
+
const shouldWait = shouldWaitForIngest(args, UPDATE_TEXT_USAGE);
|
|
2834
|
+
const text = args.flags.text;
|
|
2835
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
2836
|
+
fail(UPDATE_TEXT_USAGE, 2);
|
|
2837
|
+
}
|
|
2838
|
+
const locator = parseTextUpdateLocator(args);
|
|
2839
|
+
const ctx = buildContext(args);
|
|
2840
|
+
const doc = await updateText(ctx.client, locator, text);
|
|
2841
|
+
if (!shouldWait) {
|
|
2842
|
+
printResult({ document: doc }, !!args.flags.json);
|
|
2843
|
+
return 0;
|
|
2844
|
+
}
|
|
2845
|
+
const final = await pollIngestByKnowledgeBaseId(
|
|
2846
|
+
ctx.client,
|
|
2847
|
+
doc.knowledge_base_id,
|
|
2848
|
+
doc.id,
|
|
2849
|
+
{
|
|
2850
|
+
timeoutMs: DEFAULT_INGEST_TIMEOUT_MS
|
|
2851
|
+
}
|
|
2852
|
+
);
|
|
2853
|
+
printResult({ document: final }, !!args.flags.json);
|
|
2854
|
+
return 0;
|
|
2855
|
+
}
|
|
2856
|
+
async function kbUpdateFile(args) {
|
|
2857
|
+
const shouldWait = shouldWaitForIngest(args, UPDATE_FILE_USAGE);
|
|
2858
|
+
const { locator, localPath } = parseFileUpdate(args);
|
|
2859
|
+
try {
|
|
2860
|
+
validateLocalFile(localPath);
|
|
2861
|
+
} catch (error) {
|
|
2862
|
+
fail(error instanceof Error ? error.message : String(error), 2);
|
|
2863
|
+
}
|
|
2864
|
+
const ctx = buildContext(args);
|
|
2865
|
+
const doc = await updateFile(ctx.client, locator, localPath, {
|
|
2866
|
+
timeoutMs: DEFAULT_FILE_UPLOAD_TIMEOUT_MS
|
|
2867
|
+
});
|
|
2868
|
+
if (!shouldWait) {
|
|
2869
|
+
printResult({ document: doc }, !!args.flags.json);
|
|
2870
|
+
return 0;
|
|
2871
|
+
}
|
|
2872
|
+
const final = await pollIngestByKnowledgeBaseId(
|
|
2873
|
+
ctx.client,
|
|
2874
|
+
doc.knowledge_base_id,
|
|
2875
|
+
doc.id,
|
|
2876
|
+
{
|
|
2877
|
+
timeoutMs: DEFAULT_FILE_INGEST_TIMEOUT_MS
|
|
2878
|
+
}
|
|
2879
|
+
);
|
|
2880
|
+
printResult({ document: final }, !!args.flags.json);
|
|
2881
|
+
return 0;
|
|
2882
|
+
}
|
|
2668
2883
|
async function kbCreate(args) {
|
|
2669
2884
|
const kbName = args.positional[0];
|
|
2670
2885
|
if (!kbName) fail("usage: ctxdb kb create <kb-name> [--description=<desc>]");
|
|
@@ -2885,6 +3100,1195 @@ async function debugRecall(args) {
|
|
|
2885
3100
|
return 0;
|
|
2886
3101
|
}
|
|
2887
3102
|
|
|
3103
|
+
// src/cli/promotion-cli.ts
|
|
3104
|
+
var GENERATE_USAGE = "usage: ctxdb memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--agent=<name>] [--json]";
|
|
3105
|
+
var LIST_USAGE = "usage: ctxdb memory promotion list [--status=<generating|pending_review|approving|approved|rejected|generation_failed>] [--page=N] [--page-size=N] [--sort-by=quality_score] [--sort-order=asc|desc] [--raw] [--agent=<name>] [--json]";
|
|
3106
|
+
var GET_USAGE = "usage: ctxdb memory promotion get <promotion-id> [--raw] [--agent=<name>] [--json]";
|
|
3107
|
+
var APPROVE_USAGE = "usage: ctxdb memory promotion approve --promotion-id=<id> --knowledge-base-id=<kb-id> [--agent=<name>] [--json]";
|
|
3108
|
+
var REJECT_USAGE = "usage: ctxdb memory promotion reject --promotion-id=<id> [--reason=<text>] [--agent=<name>] [--json]";
|
|
3109
|
+
var BATCH_ACTION_USAGE = "usage: ctxdb memory promotion batch-action --action=<approve|reject> --ids=<id1,id2,...> [--knowledge-base-id=<kb-id> (required for approve)] [--reason=<text>] [--agent=<name>] [--json]";
|
|
3110
|
+
var BATCH_DELETE_USAGE = "usage: ctxdb memory promotion batch-delete --ids=<id1,id2,...> [--agent=<name>] [--json]";
|
|
3111
|
+
var RESOLVE_DOUBTS_USAGE = `usage: ctxdb memory promotion resolve-doubts --promotion-id=<id> --resolutions-json='[{"index":0,"choice":"<label-or-custom>","comment":"..."}]' [--title=<title>] [--content=<markdown>] [--agent=<name>] [--json]`;
|
|
3112
|
+
var TEMPLATE_CREATE_USAGE = `usage: ctxdb memory promotion template create --name=<name> --sections-json='[{"title":"...","guidance":"...","level":1}]' [--is-default] [--agent=<name>] [--json]`;
|
|
3113
|
+
var TEMPLATE_GET_USAGE = "usage: ctxdb memory promotion template get <template-id> [--agent=<name>] [--json]";
|
|
3114
|
+
var TEMPLATE_UPDATE_USAGE = `usage: ctxdb memory promotion template update <template-id> --name=<name> --sections-json='[{"title":"...","guidance":"...","level":1}]' [--is-default] [--agent=<name>] [--json]`;
|
|
3115
|
+
var TEMPLATE_DELETE_USAGE = "usage: ctxdb memory promotion template delete <template-id> [--agent=<name>] [--json]";
|
|
3116
|
+
var TEMPLATE_SET_DEFAULT_USAGE = "usage: ctxdb memory promotion template set-default <template-id> [--agent=<name>] [--json]";
|
|
3117
|
+
var TEMPLATE_CLEAR_DEFAULT_USAGE = "usage: ctxdb memory promotion template clear-default [--agent=<name>] [--json]";
|
|
3118
|
+
var SETTINGS_UPDATE_USAGE = "usage: ctxdb memory promotion settings update [--auto-ingest-enabled=<true|false>] [--auto-ingest-threshold=<0..1>] [--default-kb-id=<kb-id> | --clear-default-kb] [--agent=<name>] [--json]";
|
|
3119
|
+
var PROMOTION_STATUSES = [
|
|
3120
|
+
"generating",
|
|
3121
|
+
"pending_review",
|
|
3122
|
+
"approving",
|
|
3123
|
+
"approved",
|
|
3124
|
+
"rejected",
|
|
3125
|
+
"generation_failed"
|
|
3126
|
+
];
|
|
3127
|
+
var BATCH_ACTIONS = ["approve", "reject"];
|
|
3128
|
+
var BATCH_MAX_ITEMS = 50;
|
|
3129
|
+
var TEMPLATE_SECTIONS_MAX = 15;
|
|
3130
|
+
var PROMOTION_LIST_FIELDS = [
|
|
3131
|
+
"promotion_id",
|
|
3132
|
+
"topic",
|
|
3133
|
+
"title",
|
|
3134
|
+
"status",
|
|
3135
|
+
"source_type",
|
|
3136
|
+
"quality_score",
|
|
3137
|
+
"auto_ingested",
|
|
3138
|
+
"target_kb_id",
|
|
3139
|
+
"updated_at"
|
|
3140
|
+
];
|
|
3141
|
+
var PROMOTION_DETAIL_FIELDS = [
|
|
3142
|
+
"promotion_id",
|
|
3143
|
+
"topic",
|
|
3144
|
+
"title",
|
|
3145
|
+
"content",
|
|
3146
|
+
"whats_new",
|
|
3147
|
+
"status",
|
|
3148
|
+
"source_type",
|
|
3149
|
+
"quality_score",
|
|
3150
|
+
"auto_ingested",
|
|
3151
|
+
"doubts",
|
|
3152
|
+
"doubt_resolutions",
|
|
3153
|
+
"memory_count",
|
|
3154
|
+
"rejection_reason",
|
|
3155
|
+
"target_kb_id",
|
|
3156
|
+
"target_document_id",
|
|
3157
|
+
"last_error",
|
|
3158
|
+
"reviewed_by",
|
|
3159
|
+
"reviewed_at",
|
|
3160
|
+
"created_at",
|
|
3161
|
+
"updated_at"
|
|
3162
|
+
];
|
|
3163
|
+
async function promotionGenerate(args) {
|
|
3164
|
+
const topic = args.positional[0];
|
|
3165
|
+
if (!topic || topic.trim() === "") fail(GENERATE_USAGE, 2);
|
|
3166
|
+
const ctx = buildContext(args);
|
|
3167
|
+
const body = { topic };
|
|
3168
|
+
const kbId = stringFlag(args, "knowledge-base-id");
|
|
3169
|
+
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
3170
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/generate", body);
|
|
3171
|
+
emit(args, resp, "memory promotion generate", (data) => data);
|
|
3172
|
+
return 0;
|
|
3173
|
+
}
|
|
3174
|
+
async function promotionList(args) {
|
|
3175
|
+
const params = {};
|
|
3176
|
+
const status2 = stringFlag(args, "status");
|
|
3177
|
+
if (status2 !== void 0) {
|
|
3178
|
+
if (!PROMOTION_STATUSES.includes(status2)) {
|
|
3179
|
+
fail(`invalid --status: ${status2} (expected one of ${PROMOTION_STATUSES.join("|")})
|
|
3180
|
+
${LIST_USAGE}`, 2);
|
|
3181
|
+
}
|
|
3182
|
+
params.status = status2;
|
|
3183
|
+
}
|
|
3184
|
+
const page = intFlag(args, "page", LIST_USAGE);
|
|
3185
|
+
if (page !== void 0) params.page = page;
|
|
3186
|
+
const pageSize = intFlag(args, "page-size", LIST_USAGE);
|
|
3187
|
+
if (pageSize !== void 0) params.page_size = pageSize;
|
|
3188
|
+
const sortBy = stringFlag(args, "sort-by");
|
|
3189
|
+
if (sortBy !== void 0) {
|
|
3190
|
+
if (sortBy !== "quality_score") {
|
|
3191
|
+
fail(`invalid --sort-by: ${sortBy} (expected quality_score)
|
|
3192
|
+
${LIST_USAGE}`, 2);
|
|
3193
|
+
}
|
|
3194
|
+
params.sort_by = sortBy;
|
|
3195
|
+
}
|
|
3196
|
+
const sortOrder = stringFlag(args, "sort-order");
|
|
3197
|
+
if (sortOrder !== void 0) {
|
|
3198
|
+
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
3199
|
+
fail(`invalid --sort-order: ${sortOrder} (expected asc|desc)
|
|
3200
|
+
${LIST_USAGE}`, 2);
|
|
3201
|
+
}
|
|
3202
|
+
params.sort_order = sortOrder;
|
|
3203
|
+
}
|
|
3204
|
+
const ctx = buildContext(args);
|
|
3205
|
+
const resp = await ctx.client.get("/v1/memory_promotions", params);
|
|
3206
|
+
emit(args, resp, "memory promotion list", compactList);
|
|
3207
|
+
return 0;
|
|
3208
|
+
}
|
|
3209
|
+
async function promotionGet(args) {
|
|
3210
|
+
const promotionId = args.positional[0];
|
|
3211
|
+
if (!promotionId || promotionId.trim() === "") fail(GET_USAGE, 2);
|
|
3212
|
+
const ctx = buildContext(args);
|
|
3213
|
+
const resp = await ctx.client.get("/v1/memory_promotions/detail", {
|
|
3214
|
+
promotion_id: promotionId
|
|
3215
|
+
});
|
|
3216
|
+
emit(args, resp, "memory promotion get", compactDetail);
|
|
3217
|
+
return 0;
|
|
3218
|
+
}
|
|
3219
|
+
async function promotionApprove(args) {
|
|
3220
|
+
const promotionId = requireIdFlag(args, "promotion-id", APPROVE_USAGE);
|
|
3221
|
+
const kbId = requireIdFlag(args, "knowledge-base-id", APPROVE_USAGE);
|
|
3222
|
+
const ctx = buildContext(args);
|
|
3223
|
+
const body = {
|
|
3224
|
+
promotion_id: promotionId,
|
|
3225
|
+
knowledge_base_id: kbId
|
|
3226
|
+
};
|
|
3227
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/approve", body);
|
|
3228
|
+
emit(args, resp, "memory promotion approve", (data) => data);
|
|
3229
|
+
return 0;
|
|
3230
|
+
}
|
|
3231
|
+
async function promotionReject(args) {
|
|
3232
|
+
const promotionId = requireIdFlag(args, "promotion-id", REJECT_USAGE);
|
|
3233
|
+
const ctx = buildContext(args);
|
|
3234
|
+
const body = { promotion_id: promotionId };
|
|
3235
|
+
const reason = stringFlag(args, "reason");
|
|
3236
|
+
if (reason !== void 0) body.reason = reason;
|
|
3237
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/reject", body);
|
|
3238
|
+
emit(args, resp, "memory promotion reject", (data) => data);
|
|
3239
|
+
return 0;
|
|
3240
|
+
}
|
|
3241
|
+
async function promotionBatchAction(args) {
|
|
3242
|
+
const action = stringFlag(args, "action");
|
|
3243
|
+
if (action === void 0 || !BATCH_ACTIONS.includes(action)) {
|
|
3244
|
+
fail(`--action is required and must be one of ${BATCH_ACTIONS.join("|")}
|
|
3245
|
+
${BATCH_ACTION_USAGE}`, 2);
|
|
3246
|
+
}
|
|
3247
|
+
const ids = parseIdsFlag(args, BATCH_ACTION_USAGE);
|
|
3248
|
+
const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag(args, "knowledge-base-id");
|
|
3249
|
+
const ctx = buildContext(args);
|
|
3250
|
+
const body = { action, ids };
|
|
3251
|
+
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
3252
|
+
const reason = stringFlag(args, "reason");
|
|
3253
|
+
if (reason !== void 0) body.reason = reason;
|
|
3254
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/batch", body);
|
|
3255
|
+
const data = emit(args, resp, "memory promotion batch-action", (data2) => data2);
|
|
3256
|
+
return batchExitCode(data);
|
|
3257
|
+
}
|
|
3258
|
+
async function promotionBatchDelete(args) {
|
|
3259
|
+
const ids = parseIdsFlag(args, BATCH_DELETE_USAGE);
|
|
3260
|
+
const ctx = buildContext(args);
|
|
3261
|
+
const resp = await ctx.client.deleteJson(
|
|
3262
|
+
"/v1/memory_promotions/batch",
|
|
3263
|
+
{ ids }
|
|
3264
|
+
);
|
|
3265
|
+
const data = emit(args, resp, "memory promotion batch-delete", (data2) => data2);
|
|
3266
|
+
return batchExitCode(data);
|
|
3267
|
+
}
|
|
3268
|
+
async function promotionResolveDoubts(args) {
|
|
3269
|
+
const promotionId = requireIdFlag(args, "promotion-id", RESOLVE_DOUBTS_USAGE);
|
|
3270
|
+
const resolutionsRaw = args.flags["resolutions-json"];
|
|
3271
|
+
if (typeof resolutionsRaw !== "string" || resolutionsRaw.trim() === "") {
|
|
3272
|
+
fail(`--resolutions-json is required
|
|
3273
|
+
${RESOLVE_DOUBTS_USAGE}`, 2);
|
|
3274
|
+
}
|
|
3275
|
+
const resolutions = parseResolutions(resolutionsRaw, RESOLVE_DOUBTS_USAGE);
|
|
3276
|
+
const ctx = buildContext(args);
|
|
3277
|
+
const body = {
|
|
3278
|
+
promotion_id: promotionId,
|
|
3279
|
+
doubt_resolutions: resolutions
|
|
3280
|
+
};
|
|
3281
|
+
const title = stringFlag(args, "title");
|
|
3282
|
+
if (title !== void 0) body.title = title;
|
|
3283
|
+
const content = stringFlag(args, "content");
|
|
3284
|
+
if (content !== void 0) body.content = content;
|
|
3285
|
+
const resp = await ctx.client.postJson(
|
|
3286
|
+
"/v1/memory_promotions/resolve_doubts",
|
|
3287
|
+
body
|
|
3288
|
+
);
|
|
3289
|
+
emit(args, resp, "memory promotion resolve-doubts", (data) => data);
|
|
3290
|
+
return 0;
|
|
3291
|
+
}
|
|
3292
|
+
async function promotionTemplateList(args) {
|
|
3293
|
+
const ctx = buildContext(args);
|
|
3294
|
+
const resp = await ctx.client.get("/v1/memory_promotions/templates");
|
|
3295
|
+
emit(args, resp, "memory promotion template list", compactTemplateList);
|
|
3296
|
+
return 0;
|
|
3297
|
+
}
|
|
3298
|
+
async function promotionTemplateCreate(args) {
|
|
3299
|
+
const body = buildTemplateUpsertBody(args, TEMPLATE_CREATE_USAGE);
|
|
3300
|
+
const ctx = buildContext(args);
|
|
3301
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/templates", body);
|
|
3302
|
+
emit(args, resp, "memory promotion template create", (data) => data);
|
|
3303
|
+
return 0;
|
|
3304
|
+
}
|
|
3305
|
+
async function promotionTemplateGet(args) {
|
|
3306
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_GET_USAGE);
|
|
3307
|
+
const ctx = buildContext(args);
|
|
3308
|
+
const resp = await ctx.client.get(
|
|
3309
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`
|
|
3310
|
+
);
|
|
3311
|
+
emit(args, resp, "memory promotion template get", (data) => data);
|
|
3312
|
+
return 0;
|
|
3313
|
+
}
|
|
3314
|
+
async function promotionTemplateUpdate(args) {
|
|
3315
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_UPDATE_USAGE);
|
|
3316
|
+
const body = buildTemplateUpsertBody(args, TEMPLATE_UPDATE_USAGE);
|
|
3317
|
+
const ctx = buildContext(args);
|
|
3318
|
+
const resp = await ctx.client.putJson(
|
|
3319
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`,
|
|
3320
|
+
body
|
|
3321
|
+
);
|
|
3322
|
+
emit(args, resp, "memory promotion template update", (data) => data);
|
|
3323
|
+
return 0;
|
|
3324
|
+
}
|
|
3325
|
+
async function promotionTemplateDelete(args) {
|
|
3326
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_DELETE_USAGE);
|
|
3327
|
+
const ctx = buildContext(args);
|
|
3328
|
+
const resp = await ctx.client.delete(
|
|
3329
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`
|
|
3330
|
+
);
|
|
3331
|
+
emit(args, resp, "memory promotion template delete", (data) => data);
|
|
3332
|
+
return 0;
|
|
3333
|
+
}
|
|
3334
|
+
async function promotionTemplateSetDefault(args) {
|
|
3335
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_SET_DEFAULT_USAGE);
|
|
3336
|
+
const ctx = buildContext(args);
|
|
3337
|
+
const resp = await ctx.client.postJson(
|
|
3338
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}/set_default`
|
|
3339
|
+
);
|
|
3340
|
+
emit(args, resp, "memory promotion template set-default", (data) => data);
|
|
3341
|
+
return 0;
|
|
3342
|
+
}
|
|
3343
|
+
async function promotionTemplateClearDefault(args) {
|
|
3344
|
+
if (args.positional.length > 0) fail(TEMPLATE_CLEAR_DEFAULT_USAGE, 2);
|
|
3345
|
+
const ctx = buildContext(args);
|
|
3346
|
+
const resp = await ctx.client.delete("/v1/memory_promotions/templates/default");
|
|
3347
|
+
emit(args, resp, "memory promotion template clear-default", (data) => data);
|
|
3348
|
+
return 0;
|
|
3349
|
+
}
|
|
3350
|
+
async function promotionSettingsGet(args) {
|
|
3351
|
+
const ctx = buildContext(args);
|
|
3352
|
+
const resp = await ctx.client.get("/v1/memory_promotions/settings");
|
|
3353
|
+
emit(args, resp, "memory promotion settings get", (data) => data);
|
|
3354
|
+
return 0;
|
|
3355
|
+
}
|
|
3356
|
+
async function promotionSettingsUpdate(args) {
|
|
3357
|
+
const body = {};
|
|
3358
|
+
const enabled = stringFlag(args, "auto-ingest-enabled");
|
|
3359
|
+
if (enabled !== void 0) {
|
|
3360
|
+
if (enabled !== "true" && enabled !== "false") {
|
|
3361
|
+
fail(`invalid --auto-ingest-enabled: ${enabled} (expected true|false)
|
|
3362
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3363
|
+
}
|
|
3364
|
+
body.auto_ingest_enabled = enabled === "true";
|
|
3365
|
+
}
|
|
3366
|
+
const threshold = stringFlag(args, "auto-ingest-threshold");
|
|
3367
|
+
if (threshold !== void 0) {
|
|
3368
|
+
const n = Number(threshold);
|
|
3369
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
3370
|
+
fail(`invalid --auto-ingest-threshold: ${threshold} (expected a number in [0,1])
|
|
3371
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3372
|
+
}
|
|
3373
|
+
body.auto_ingest_threshold = n;
|
|
3374
|
+
}
|
|
3375
|
+
const defaultKbId = stringFlag(args, "default-kb-id");
|
|
3376
|
+
const clearDefaultKb = args.flags["clear-default-kb"] === true;
|
|
3377
|
+
if (defaultKbId !== void 0 && clearDefaultKb) {
|
|
3378
|
+
fail(`cannot use --default-kb-id and --clear-default-kb together
|
|
3379
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3380
|
+
}
|
|
3381
|
+
if (defaultKbId !== void 0) body.default_kb_id = defaultKbId;
|
|
3382
|
+
if (clearDefaultKb) body.default_kb_id = "";
|
|
3383
|
+
if (Object.keys(body).length === 0) {
|
|
3384
|
+
fail(`at least one setting flag is required
|
|
3385
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3386
|
+
}
|
|
3387
|
+
const ctx = buildContext(args);
|
|
3388
|
+
const resp = await ctx.client.putJson("/v1/memory_promotions/settings", body);
|
|
3389
|
+
emit(args, resp, "memory promotion settings update", (data) => data);
|
|
3390
|
+
return 0;
|
|
3391
|
+
}
|
|
3392
|
+
function stringFlag(args, name) {
|
|
3393
|
+
const value = args.flags[name];
|
|
3394
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3395
|
+
}
|
|
3396
|
+
function requireIdFlag(args, name, usage) {
|
|
3397
|
+
const value = stringFlag(args, name);
|
|
3398
|
+
if (value === void 0) fail(`--${name} is required
|
|
3399
|
+
${usage}`, 2);
|
|
3400
|
+
return value;
|
|
3401
|
+
}
|
|
3402
|
+
function requireTemplateIdPositional(args, usage) {
|
|
3403
|
+
const templateId = args.positional[0];
|
|
3404
|
+
if (!templateId || templateId.trim() === "") fail(usage, 2);
|
|
3405
|
+
return templateId;
|
|
3406
|
+
}
|
|
3407
|
+
function intFlag(args, name, usage) {
|
|
3408
|
+
const raw = stringFlag(args, name);
|
|
3409
|
+
if (raw === void 0) return void 0;
|
|
3410
|
+
const n = Number(raw);
|
|
3411
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
3412
|
+
fail(`invalid --${name}: ${raw} (expected a positive integer)
|
|
3413
|
+
${usage}`, 2);
|
|
3414
|
+
}
|
|
3415
|
+
return n;
|
|
3416
|
+
}
|
|
3417
|
+
function parseIdsFlag(args, usage) {
|
|
3418
|
+
const raw = stringFlag(args, "ids");
|
|
3419
|
+
if (raw === void 0) fail(`--ids is required (comma-separated)
|
|
3420
|
+
${usage}`, 2);
|
|
3421
|
+
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3422
|
+
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3423
|
+
${usage}`, 2);
|
|
3424
|
+
if (ids.length > BATCH_MAX_ITEMS) {
|
|
3425
|
+
fail(`--ids accepts at most ${BATCH_MAX_ITEMS} items per request (got ${ids.length})
|
|
3426
|
+
${usage}`, 2);
|
|
3427
|
+
}
|
|
3428
|
+
return ids;
|
|
3429
|
+
}
|
|
3430
|
+
function parseResolutions(raw, usage) {
|
|
3431
|
+
let parsed;
|
|
3432
|
+
try {
|
|
3433
|
+
parsed = JSON.parse(raw);
|
|
3434
|
+
} catch {
|
|
3435
|
+
fail(`--resolutions-json is not valid JSON
|
|
3436
|
+
${usage}`, 2);
|
|
3437
|
+
}
|
|
3438
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3439
|
+
fail(`--resolutions-json must be a non-empty array
|
|
3440
|
+
${usage}`, 2);
|
|
3441
|
+
}
|
|
3442
|
+
return parsed.map((entry, i) => {
|
|
3443
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3444
|
+
fail(`--resolutions-json[${i}] must be an object
|
|
3445
|
+
${usage}`, 2);
|
|
3446
|
+
}
|
|
3447
|
+
const e = entry;
|
|
3448
|
+
if (typeof e.index !== "number" || !Number.isInteger(e.index) || e.index < 0) {
|
|
3449
|
+
fail(`--resolutions-json[${i}].index must be a non-negative integer
|
|
3450
|
+
${usage}`, 2);
|
|
3451
|
+
}
|
|
3452
|
+
if (typeof e.choice !== "string" || e.choice.trim() === "") {
|
|
3453
|
+
fail(`--resolutions-json[${i}].choice must be a non-empty string
|
|
3454
|
+
${usage}`, 2);
|
|
3455
|
+
}
|
|
3456
|
+
const out = { index: e.index, choice: e.choice };
|
|
3457
|
+
if (e.comment !== void 0 && e.comment !== null) {
|
|
3458
|
+
if (typeof e.comment !== "string") {
|
|
3459
|
+
fail(`--resolutions-json[${i}].comment must be a string
|
|
3460
|
+
${usage}`, 2);
|
|
3461
|
+
}
|
|
3462
|
+
out.comment = e.comment;
|
|
3463
|
+
}
|
|
3464
|
+
return out;
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3467
|
+
function buildTemplateUpsertBody(args, usage) {
|
|
3468
|
+
const name = stringFlag(args, "name");
|
|
3469
|
+
if (name === void 0) fail(`--name is required
|
|
3470
|
+
${usage}`, 2);
|
|
3471
|
+
const sectionsRaw = args.flags["sections-json"];
|
|
3472
|
+
if (typeof sectionsRaw !== "string" || sectionsRaw.trim() === "") {
|
|
3473
|
+
fail(`--sections-json is required
|
|
3474
|
+
${usage}`, 2);
|
|
3475
|
+
}
|
|
3476
|
+
const sections = parseSections(sectionsRaw, usage);
|
|
3477
|
+
const body = { name, sections };
|
|
3478
|
+
if (args.flags["is-default"] === true) body.is_default = true;
|
|
3479
|
+
return body;
|
|
3480
|
+
}
|
|
3481
|
+
function parseSections(raw, usage) {
|
|
3482
|
+
let parsed;
|
|
3483
|
+
try {
|
|
3484
|
+
parsed = JSON.parse(raw);
|
|
3485
|
+
} catch {
|
|
3486
|
+
fail(`--sections-json is not valid JSON
|
|
3487
|
+
${usage}`, 2);
|
|
3488
|
+
}
|
|
3489
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3490
|
+
fail(`--sections-json must be a non-empty array
|
|
3491
|
+
${usage}`, 2);
|
|
3492
|
+
}
|
|
3493
|
+
if (parsed.length > TEMPLATE_SECTIONS_MAX) {
|
|
3494
|
+
fail(`--sections-json accepts at most ${TEMPLATE_SECTIONS_MAX} sections (got ${parsed.length})
|
|
3495
|
+
${usage}`, 2);
|
|
3496
|
+
}
|
|
3497
|
+
return parsed.map((entry, i) => {
|
|
3498
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3499
|
+
fail(`--sections-json[${i}] must be an object
|
|
3500
|
+
${usage}`, 2);
|
|
3501
|
+
}
|
|
3502
|
+
const e = entry;
|
|
3503
|
+
if (typeof e.title !== "string" || e.title.trim() === "") {
|
|
3504
|
+
fail(`--sections-json[${i}].title must be a non-empty string
|
|
3505
|
+
${usage}`, 2);
|
|
3506
|
+
}
|
|
3507
|
+
if (/[\r\n]/.test(e.title.trim())) {
|
|
3508
|
+
fail(`--sections-json[${i}].title must not contain line breaks
|
|
3509
|
+
${usage}`, 2);
|
|
3510
|
+
}
|
|
3511
|
+
if (typeof e.guidance !== "string" || e.guidance.trim() === "") {
|
|
3512
|
+
fail(`--sections-json[${i}].guidance must be a non-empty string
|
|
3513
|
+
${usage}`, 2);
|
|
3514
|
+
}
|
|
3515
|
+
const out = { title: e.title, guidance: e.guidance };
|
|
3516
|
+
if (e.level !== void 0 && e.level !== null) {
|
|
3517
|
+
if (typeof e.level !== "number" || !Number.isInteger(e.level) || e.level < 1 || e.level > 3) {
|
|
3518
|
+
fail(`--sections-json[${i}].level must be an integer in 1-3
|
|
3519
|
+
${usage}`, 2);
|
|
3520
|
+
}
|
|
3521
|
+
out.level = e.level;
|
|
3522
|
+
}
|
|
3523
|
+
return out;
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3526
|
+
function unwrapBox(response, operation) {
|
|
3527
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
3528
|
+
throw new CtxdbError(`${operation} returned an invalid response`);
|
|
3529
|
+
}
|
|
3530
|
+
const box = response;
|
|
3531
|
+
const errorCode = box.errorCode;
|
|
3532
|
+
const failed = typeof errorCode === "number" && errorCode !== 0 || typeof errorCode === "string" && errorCode.trim() !== "" && errorCode.trim() !== "0";
|
|
3533
|
+
if (failed) {
|
|
3534
|
+
const message = typeof box.errorMessage === "string" && box.errorMessage ? box.errorMessage : `errorCode=${box.errorCode}`;
|
|
3535
|
+
throw new CtxdbError(`${operation} failed: ${message}`);
|
|
3536
|
+
}
|
|
3537
|
+
return Object.hasOwn(box, "data") ? box.data : response;
|
|
3538
|
+
}
|
|
3539
|
+
function emit(args, response, operation, compact) {
|
|
3540
|
+
const data = unwrapBox(response, operation);
|
|
3541
|
+
const out = args.flags.raw ? response : compact(data);
|
|
3542
|
+
printResult(out, !!args.flags.json);
|
|
3543
|
+
return data;
|
|
3544
|
+
}
|
|
3545
|
+
function batchExitCode(data) {
|
|
3546
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return 0;
|
|
3547
|
+
const failed = data.failed;
|
|
3548
|
+
return typeof failed === "number" && failed > 0 ? 1 : 0;
|
|
3549
|
+
}
|
|
3550
|
+
function pickFields(source, fields) {
|
|
3551
|
+
const out = {};
|
|
3552
|
+
for (const key of fields) {
|
|
3553
|
+
const value = source[key];
|
|
3554
|
+
if (value !== void 0 && value !== null) out[key] = value;
|
|
3555
|
+
}
|
|
3556
|
+
return out;
|
|
3557
|
+
}
|
|
3558
|
+
function compactList(data) {
|
|
3559
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
3560
|
+
const d = data;
|
|
3561
|
+
const rows = Array.isArray(d.promotions) ? d.promotions : [];
|
|
3562
|
+
const out = {
|
|
3563
|
+
promotions: rows.filter((p) => p && typeof p === "object" && !Array.isArray(p)).map((p) => pickFields(p, PROMOTION_LIST_FIELDS))
|
|
3564
|
+
};
|
|
3565
|
+
for (const key of ["total", "page", "page_size"]) {
|
|
3566
|
+
if (typeof d[key] === "number") out[key] = d[key];
|
|
3567
|
+
}
|
|
3568
|
+
return out;
|
|
3569
|
+
}
|
|
3570
|
+
function compactDetail(data) {
|
|
3571
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
3572
|
+
return pickFields(data, PROMOTION_DETAIL_FIELDS);
|
|
3573
|
+
}
|
|
3574
|
+
function compactTemplateList(data) {
|
|
3575
|
+
if (!Array.isArray(data)) return data;
|
|
3576
|
+
return data.filter((t) => t && typeof t === "object" && !Array.isArray(t)).map((t) => {
|
|
3577
|
+
const template = t;
|
|
3578
|
+
const out = pickFields(template, [
|
|
3579
|
+
"id",
|
|
3580
|
+
"name",
|
|
3581
|
+
"is_default",
|
|
3582
|
+
"updatedAt"
|
|
3583
|
+
]);
|
|
3584
|
+
if (Array.isArray(template.sections)) {
|
|
3585
|
+
out.sections = template.sections.filter((s) => s && typeof s === "object" && !Array.isArray(s)).map(
|
|
3586
|
+
(s) => pickFields(s, ["level", "title"])
|
|
3587
|
+
);
|
|
3588
|
+
}
|
|
3589
|
+
return out;
|
|
3590
|
+
});
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
// src/cli/review-cli.ts
|
|
3594
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3595
|
+
|
|
3596
|
+
// src/lib/review.ts
|
|
3597
|
+
function unwrapReviewData(response, operation) {
|
|
3598
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
3599
|
+
throw new CtxdbError(`${operation} returned an invalid response`);
|
|
3600
|
+
}
|
|
3601
|
+
const box = response;
|
|
3602
|
+
const errorCode = box.errorCode;
|
|
3603
|
+
const failed = typeof errorCode === "number" && errorCode !== 0 || typeof errorCode === "string" && errorCode.trim() !== "" && errorCode.trim() !== "0";
|
|
3604
|
+
if (failed) {
|
|
3605
|
+
const message = typeof box.errorMessage === "string" && box.errorMessage ? box.errorMessage : `errorCode=${errorCode}`;
|
|
3606
|
+
throw new CtxdbError(`${operation} failed: ${message}`);
|
|
3607
|
+
}
|
|
3608
|
+
return box.data ?? null;
|
|
3609
|
+
}
|
|
3610
|
+
function asRecord(value) {
|
|
3611
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3612
|
+
}
|
|
3613
|
+
function pickString(record, field) {
|
|
3614
|
+
const value = record[field];
|
|
3615
|
+
return typeof value === "string" ? value : "";
|
|
3616
|
+
}
|
|
3617
|
+
function pickNumber(record, field) {
|
|
3618
|
+
const value = record[field];
|
|
3619
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3620
|
+
}
|
|
3621
|
+
function pickBoolean(record, field) {
|
|
3622
|
+
const value = record[field];
|
|
3623
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3624
|
+
}
|
|
3625
|
+
function parseEmbeddedJson(value) {
|
|
3626
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
3627
|
+
try {
|
|
3628
|
+
return JSON.parse(value);
|
|
3629
|
+
} catch {
|
|
3630
|
+
return void 0;
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
function countArrayEntries(value) {
|
|
3634
|
+
return Array.isArray(value) ? value.length : void 0;
|
|
3635
|
+
}
|
|
3636
|
+
function readReviewListData(data) {
|
|
3637
|
+
const record = asRecord(data);
|
|
3638
|
+
return {
|
|
3639
|
+
reviews: record && Array.isArray(record.reviews) ? record.reviews : [],
|
|
3640
|
+
total: record ? pickNumber(record, "total") : void 0,
|
|
3641
|
+
page: record ? pickNumber(record, "page") : void 0,
|
|
3642
|
+
page_size: record ? pickNumber(record, "page_size") : void 0
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3645
|
+
function minimalReviewListItem(raw) {
|
|
3646
|
+
const r = asRecord(raw) ?? {};
|
|
3647
|
+
const out = {
|
|
3648
|
+
review_id: pickString(r, "review_id"),
|
|
3649
|
+
document_name: pickString(r, "document_name"),
|
|
3650
|
+
decision: pickString(r, "decision"),
|
|
3651
|
+
review_status: pickString(r, "review_status")
|
|
3652
|
+
};
|
|
3653
|
+
const score = pickNumber(r, "confidence_score");
|
|
3654
|
+
if (score !== void 0) out.confidence_score = score;
|
|
3655
|
+
const canApproveReview = pickBoolean(r, "can_approve_review");
|
|
3656
|
+
if (canApproveReview !== void 0) out.can_approve_review = canApproveReview;
|
|
3657
|
+
const createdAt = pickString(r, "created_at");
|
|
3658
|
+
if (createdAt) out.created_at = createdAt;
|
|
3659
|
+
return out;
|
|
3660
|
+
}
|
|
3661
|
+
function compactReviewListItem(raw) {
|
|
3662
|
+
const r = asRecord(raw) ?? {};
|
|
3663
|
+
const out = {
|
|
3664
|
+
...minimalReviewListItem(raw),
|
|
3665
|
+
document_id: pickString(r, "document_id"),
|
|
3666
|
+
knowledge_base_id: pickString(r, "knowledge_base_id")
|
|
3667
|
+
};
|
|
3668
|
+
const hasConflicts = pickBoolean(r, "has_conflicts");
|
|
3669
|
+
if (hasConflicts !== void 0) out.has_conflicts = hasConflicts;
|
|
3670
|
+
const hasContradictions = pickBoolean(r, "has_contradictions");
|
|
3671
|
+
if (hasContradictions !== void 0) out.has_contradictions = hasContradictions;
|
|
3672
|
+
const rewriteNeeded = pickBoolean(r, "rewrite_needed");
|
|
3673
|
+
if (rewriteNeeded !== void 0) out.rewrite_needed = rewriteNeeded;
|
|
3674
|
+
const humanAction = pickString(r, "human_action");
|
|
3675
|
+
if (humanAction) out.human_action = humanAction;
|
|
3676
|
+
return out;
|
|
3677
|
+
}
|
|
3678
|
+
function projectReviewList(data, projectItem) {
|
|
3679
|
+
const { reviews, total, page, page_size } = readReviewListData(data);
|
|
3680
|
+
const out = { reviews: reviews.map(projectItem) };
|
|
3681
|
+
if (total !== void 0) out.total = total;
|
|
3682
|
+
if (page !== void 0) out.page = page;
|
|
3683
|
+
if (page_size !== void 0) out.page_size = page_size;
|
|
3684
|
+
return out;
|
|
3685
|
+
}
|
|
3686
|
+
function minimalReviewListResponse(data) {
|
|
3687
|
+
return projectReviewList(data, minimalReviewListItem);
|
|
3688
|
+
}
|
|
3689
|
+
function compactReviewListResponse(data) {
|
|
3690
|
+
return projectReviewList(data, compactReviewListItem);
|
|
3691
|
+
}
|
|
3692
|
+
function minimalReviewDetail(raw) {
|
|
3693
|
+
const r = asRecord(raw);
|
|
3694
|
+
if (!r) return {};
|
|
3695
|
+
const out = {
|
|
3696
|
+
review_id: pickString(r, "review_id"),
|
|
3697
|
+
document_name: pickString(r, "document_name"),
|
|
3698
|
+
decision: pickString(r, "decision"),
|
|
3699
|
+
review_status: pickString(r, "review_status")
|
|
3700
|
+
};
|
|
3701
|
+
for (const field of [
|
|
3702
|
+
"document_id",
|
|
3703
|
+
"knowledge_base_id",
|
|
3704
|
+
"decision_reason",
|
|
3705
|
+
"rewrite_strategy",
|
|
3706
|
+
"human_action",
|
|
3707
|
+
"created_at"
|
|
3708
|
+
]) {
|
|
3709
|
+
const value = pickString(r, field);
|
|
3710
|
+
if (value) out[field] = value;
|
|
3711
|
+
}
|
|
3712
|
+
for (const field of [
|
|
3713
|
+
"confidence_score",
|
|
3714
|
+
"logic_score",
|
|
3715
|
+
"noise_score",
|
|
3716
|
+
"contradiction_score"
|
|
3717
|
+
]) {
|
|
3718
|
+
const value = pickNumber(r, field);
|
|
3719
|
+
if (value !== void 0) out[field] = value;
|
|
3720
|
+
}
|
|
3721
|
+
for (const field of ["has_contradictions", "rewrite_needed"]) {
|
|
3722
|
+
const value = pickBoolean(r, field);
|
|
3723
|
+
if (value !== void 0) out[field] = value;
|
|
3724
|
+
}
|
|
3725
|
+
const conflictCount = countArrayEntries(parseEmbeddedJson(r.conflicts));
|
|
3726
|
+
if (conflictCount !== void 0) out.conflict_count = conflictCount;
|
|
3727
|
+
const originalContent = asRecord(r.original_content);
|
|
3728
|
+
if (originalContent) {
|
|
3729
|
+
const contentPage = pickNumber(originalContent, "page");
|
|
3730
|
+
if (contentPage !== void 0) out.content_page = contentPage;
|
|
3731
|
+
const totalPages = pickNumber(originalContent, "total_pages");
|
|
3732
|
+
if (totalPages !== void 0) out.content_total_pages = totalPages;
|
|
3733
|
+
const totalChars = pickNumber(originalContent, "total_chars");
|
|
3734
|
+
if (totalChars !== void 0) out.content_total_chars = totalChars;
|
|
3735
|
+
}
|
|
3736
|
+
return out;
|
|
3737
|
+
}
|
|
3738
|
+
var DETAIL_JSON_STRING_FIELDS = [
|
|
3739
|
+
"contradictions",
|
|
3740
|
+
"conflicts",
|
|
3741
|
+
"quality_details",
|
|
3742
|
+
"original_contradictions",
|
|
3743
|
+
"rewrite_info_check",
|
|
3744
|
+
"human_conflict_resolutions"
|
|
3745
|
+
];
|
|
3746
|
+
function compactReviewDetail(raw) {
|
|
3747
|
+
const r = asRecord(raw);
|
|
3748
|
+
if (!r) return {};
|
|
3749
|
+
const out = { ...r };
|
|
3750
|
+
for (const field of DETAIL_JSON_STRING_FIELDS) {
|
|
3751
|
+
const parsed = parseEmbeddedJson(r[field]);
|
|
3752
|
+
if (parsed !== void 0) out[field] = parsed;
|
|
3753
|
+
}
|
|
3754
|
+
return out;
|
|
3755
|
+
}
|
|
3756
|
+
function minimalReviewTemplate(raw) {
|
|
3757
|
+
const r = asRecord(raw) ?? {};
|
|
3758
|
+
return {
|
|
3759
|
+
id: pickString(r, "id"),
|
|
3760
|
+
name: pickString(r, "name"),
|
|
3761
|
+
is_default: pickBoolean(r, "is_default") ?? false,
|
|
3762
|
+
knowledge_base_id: pickString(r, "knowledge_base_id"),
|
|
3763
|
+
section_count: Array.isArray(r.sections) ? r.sections.length : 0
|
|
3764
|
+
};
|
|
3765
|
+
}
|
|
3766
|
+
function compactReviewTemplate(raw) {
|
|
3767
|
+
return asRecord(raw) ?? {};
|
|
3768
|
+
}
|
|
3769
|
+
function minimalReviewTemplateListResponse(data) {
|
|
3770
|
+
const items = Array.isArray(data) ? data : [];
|
|
3771
|
+
const templates = items.map(minimalReviewTemplate);
|
|
3772
|
+
return { templates, count: templates.length };
|
|
3773
|
+
}
|
|
3774
|
+
function minimalReviewTemplateDetail(raw) {
|
|
3775
|
+
const r = asRecord(raw);
|
|
3776
|
+
if (!r) return {};
|
|
3777
|
+
const out = { ...r };
|
|
3778
|
+
delete out.content;
|
|
3779
|
+
return out;
|
|
3780
|
+
}
|
|
3781
|
+
var REVIEW_ACTIONS = [
|
|
3782
|
+
"confirm",
|
|
3783
|
+
"force_accept",
|
|
3784
|
+
"accept_rewrite",
|
|
3785
|
+
"reject",
|
|
3786
|
+
"reject_reupload"
|
|
3787
|
+
];
|
|
3788
|
+
var REVIEW_BATCH_ACTIONS = ["confirm", "reject"];
|
|
3789
|
+
var CONFLICT_RESOLUTIONS = [
|
|
3790
|
+
"use_new",
|
|
3791
|
+
"keep_old",
|
|
3792
|
+
"merge",
|
|
3793
|
+
"annotate_divergence"
|
|
3794
|
+
];
|
|
3795
|
+
var REVIEW_STATUS_FILTERS = ["pending", "reviewed", "rejected"];
|
|
3796
|
+
var BATCH_MAX_ITEMS2 = 50;
|
|
3797
|
+
var TEMPLATE_MAX_SECTIONS = 15;
|
|
3798
|
+
var TEMPLATE_MAX_NAME_LENGTH = 128;
|
|
3799
|
+
function reviewBatchExitCode(data) {
|
|
3800
|
+
const failed = asRecord(data)?.failed;
|
|
3801
|
+
return typeof failed === "number" && failed > 0 ? 1 : 0;
|
|
3802
|
+
}
|
|
3803
|
+
function parseIdList(raw) {
|
|
3804
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3805
|
+
}
|
|
3806
|
+
function parseConflictResolutions(raw) {
|
|
3807
|
+
let parsed;
|
|
3808
|
+
try {
|
|
3809
|
+
parsed = JSON.parse(raw);
|
|
3810
|
+
} catch {
|
|
3811
|
+
throw new Error(
|
|
3812
|
+
'resolutions must be a JSON array: [{"conflict_id": "...", "resolution": "use_new|keep_old|merge|annotate_divergence"}]'
|
|
3813
|
+
);
|
|
3814
|
+
}
|
|
3815
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3816
|
+
throw new Error("resolutions must be a non-empty JSON array");
|
|
3817
|
+
}
|
|
3818
|
+
return parsed.map((entry, index) => {
|
|
3819
|
+
const record = asRecord(entry);
|
|
3820
|
+
const conflictId = record ? record.conflict_id : void 0;
|
|
3821
|
+
const resolution = record ? record.resolution : void 0;
|
|
3822
|
+
if (typeof conflictId !== "string" || conflictId.trim() === "") {
|
|
3823
|
+
throw new Error(`resolutions[${index}].conflict_id must be a non-empty string`);
|
|
3824
|
+
}
|
|
3825
|
+
if (typeof resolution !== "string" || !CONFLICT_RESOLUTIONS.includes(resolution)) {
|
|
3826
|
+
throw new Error(
|
|
3827
|
+
`resolutions[${index}].resolution must be one of ${CONFLICT_RESOLUTIONS.join("|")}`
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3830
|
+
return { conflict_id: conflictId.trim(), resolution };
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3833
|
+
function parseTemplateSections(raw) {
|
|
3834
|
+
let parsed;
|
|
3835
|
+
try {
|
|
3836
|
+
parsed = JSON.parse(raw);
|
|
3837
|
+
} catch {
|
|
3838
|
+
throw new Error(
|
|
3839
|
+
'sections must be a JSON array: [{"level": 1, "title": "...", "guidance": "..."}]'
|
|
3840
|
+
);
|
|
3841
|
+
}
|
|
3842
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3843
|
+
throw new Error("sections must be a non-empty JSON array");
|
|
3844
|
+
}
|
|
3845
|
+
if (parsed.length > TEMPLATE_MAX_SECTIONS) {
|
|
3846
|
+
throw new Error(`sections must have at most ${TEMPLATE_MAX_SECTIONS} entries`);
|
|
3847
|
+
}
|
|
3848
|
+
return parsed.map((entry, index) => {
|
|
3849
|
+
const record = asRecord(entry);
|
|
3850
|
+
const title = record ? record.title : void 0;
|
|
3851
|
+
const guidance = record ? record.guidance : void 0;
|
|
3852
|
+
if (typeof title !== "string" || title.trim() === "") {
|
|
3853
|
+
throw new Error(`sections[${index}].title must be a non-empty string`);
|
|
3854
|
+
}
|
|
3855
|
+
if (title.includes("\n")) {
|
|
3856
|
+
throw new Error(`sections[${index}].title must be a single line`);
|
|
3857
|
+
}
|
|
3858
|
+
if (typeof guidance !== "string" || guidance.trim() === "") {
|
|
3859
|
+
throw new Error(`sections[${index}].guidance must be a non-empty string`);
|
|
3860
|
+
}
|
|
3861
|
+
const out = { title, guidance };
|
|
3862
|
+
if (record && record.level !== void 0) {
|
|
3863
|
+
const level = record.level;
|
|
3864
|
+
if (typeof level !== "number" || !Number.isInteger(level) || level < 1 || level > 3) {
|
|
3865
|
+
throw new Error(`sections[${index}].level must be an integer between 1 and 3`);
|
|
3866
|
+
}
|
|
3867
|
+
out.level = level;
|
|
3868
|
+
}
|
|
3869
|
+
return out;
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
// src/cli/review-cli.ts
|
|
3874
|
+
var REVIEW_LIST_USAGE = "usage: ctxdb kb review list [--knowledge-base-id=<id>] [--decision=<d>]\n [--review-status=<pending|reviewed|rejected>]\n [--page=N] [--page-size=N] [--sort-by=confidence_score]\n [--sort-order=asc|desc] [--verbose] [--raw] [--json]";
|
|
3875
|
+
var REVIEW_GET_USAGE = "usage: ctxdb kb review get <review-id> [--content-page=N] [--verbose] [--raw] [--json]";
|
|
3876
|
+
var REVIEW_ACTION_USAGE = `usage: ctxdb kb review action <review-id> --action=<${REVIEW_ACTIONS.join("|")}>
|
|
3877
|
+
[--edited-content=<text> | --edited-content-file=<path>]
|
|
3878
|
+
[--conflict-resolutions=<json>] [--verbose] [--raw] [--json]`;
|
|
3879
|
+
var REVIEW_RESOLVE_USAGE = 'usage: ctxdb kb review resolve-conflicts <review-id>\n (--resolutions=<json> | --resolutions-file=<path>) [--verbose] [--raw] [--json]\n resolutions: [{"conflict_id": "...", "resolution": "' + CONFLICT_RESOLUTIONS.join("|") + '"}]';
|
|
3880
|
+
var REVIEW_BATCH_ACTION_USAGE = `usage: ctxdb kb review batch-action --action=<${REVIEW_BATCH_ACTIONS.join("|")}> --ids=<id1,id2,...> [--json] (max ${BATCH_MAX_ITEMS2} ids)`;
|
|
3881
|
+
var REVIEW_BATCH_DELETE_USAGE = `usage: ctxdb kb review batch-delete --ids=<id1,id2,...> [--json] (max ${BATCH_MAX_ITEMS2} ids)`;
|
|
3882
|
+
var REVIEW_DELETE_USAGE = "usage: ctxdb kb review delete <review-id> [--json]";
|
|
3883
|
+
var TEMPLATE_LIST_USAGE = "usage: ctxdb kb review template list <knowledge-base-id> [--verbose] [--raw] [--json]";
|
|
3884
|
+
var TEMPLATE_CREATE_USAGE2 = 'usage: ctxdb kb review template create <knowledge-base-id> --name=<name>\n (--sections=<json> | --sections-file=<path>) [--default] [--verbose] [--raw] [--json]\n sections: [{"level": 1, "title": "...", "guidance": "..."}] (max 15)';
|
|
3885
|
+
var TEMPLATE_UPDATE_USAGE2 = "usage: ctxdb kb review template update <knowledge-base-id> <template-id>\n [--name=<name>] [--sections=<json> | --sections-file=<path>]\n [--default | --no-default] [--verbose] [--raw] [--json]";
|
|
3886
|
+
var TEMPLATE_DELETE_USAGE2 = "usage: ctxdb kb review template delete <knowledge-base-id> <template-id> [--json]";
|
|
3887
|
+
var TEMPLATE_GET_USAGE2 = "usage: ctxdb kb review template get <knowledge-base-id> <template-id> [--verbose] [--raw] [--json]";
|
|
3888
|
+
var TEMPLATE_SET_DEFAULT_USAGE2 = "usage: ctxdb kb review template set-default <knowledge-base-id> <template-id> [--verbose] [--raw] [--json]";
|
|
3889
|
+
var TEMPLATE_CLEAR_DEFAULT_USAGE2 = "usage: ctxdb kb review template clear-default <knowledge-base-id> [--json]";
|
|
3890
|
+
var SETTINGS_GET_USAGE = "usage: ctxdb kb review settings get <knowledge-base-id> [--json]";
|
|
3891
|
+
var SETTINGS_UPDATE_USAGE2 = "usage: ctxdb kb review settings update <knowledge-base-id>\n [--review-enabled | --no-review-enabled]\n [--high-threshold=F] [--logic-rewrite-threshold=F]\n [--noise-rewrite-threshold=F] [--max-rewrite-iterations=N]\n [--rewrite-enabled | --no-rewrite-enabled]\n [--conflict-detection-enabled | --no-conflict-detection-enabled]\n [--rewrite-auto-ingest-enabled | --no-rewrite-auto-ingest-enabled] [--json]";
|
|
3892
|
+
function stringValue(args, name) {
|
|
3893
|
+
const value = args.flags[name];
|
|
3894
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3895
|
+
}
|
|
3896
|
+
function requireId(args, flagName, positionalIndex, usage) {
|
|
3897
|
+
const positional = args.positional[positionalIndex]?.trim();
|
|
3898
|
+
const value = positional !== void 0 && positional !== "" ? positional : stringValue(args, flagName);
|
|
3899
|
+
if (value === void 0) fail(usage, 2);
|
|
3900
|
+
return value.trim();
|
|
3901
|
+
}
|
|
3902
|
+
function requireEnumFlag(args, flagName, allowed, usage) {
|
|
3903
|
+
const value = stringValue(args, flagName);
|
|
3904
|
+
if (value === void 0) fail(usage, 2);
|
|
3905
|
+
if (!allowed.includes(value)) {
|
|
3906
|
+
fail(`${flagName} must be one of ${allowed.join("|")}
|
|
3907
|
+
${usage}`, 2);
|
|
3908
|
+
}
|
|
3909
|
+
return value;
|
|
3910
|
+
}
|
|
3911
|
+
function positiveIntFlag(args, flagName, usage) {
|
|
3912
|
+
const value = args.flags[flagName];
|
|
3913
|
+
if (value === void 0) return void 0;
|
|
3914
|
+
if (typeof value !== "string") fail(usage, 2);
|
|
3915
|
+
const parsed = Number(value);
|
|
3916
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
3917
|
+
fail(`${flagName} must be a positive integer
|
|
3918
|
+
${usage}`, 2);
|
|
3919
|
+
}
|
|
3920
|
+
return parsed;
|
|
3921
|
+
}
|
|
3922
|
+
function readPayloadInput(args, inlineFlag, fileFlag, usage) {
|
|
3923
|
+
const inline = stringValue(args, inlineFlag);
|
|
3924
|
+
const filePath = stringValue(args, fileFlag);
|
|
3925
|
+
if (inline !== void 0 && filePath !== void 0) {
|
|
3926
|
+
fail(`cannot use --${inlineFlag} and --${fileFlag} together
|
|
3927
|
+
${usage}`, 2);
|
|
3928
|
+
}
|
|
3929
|
+
if (inline !== void 0) return inline;
|
|
3930
|
+
if (filePath !== void 0) {
|
|
3931
|
+
try {
|
|
3932
|
+
return readFileSync2(filePath, "utf-8");
|
|
3933
|
+
} catch (error) {
|
|
3934
|
+
fail(
|
|
3935
|
+
`cannot read --${fileFlag} ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
3936
|
+
2
|
|
3937
|
+
);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
fail(usage, 2);
|
|
3941
|
+
}
|
|
3942
|
+
function parseIdsFlag2(args, usage) {
|
|
3943
|
+
const raw = stringValue(args, "ids");
|
|
3944
|
+
if (raw === void 0) fail(usage, 2);
|
|
3945
|
+
const ids = parseIdList(raw);
|
|
3946
|
+
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3947
|
+
${usage}`, 2);
|
|
3948
|
+
if (ids.length > BATCH_MAX_ITEMS2) {
|
|
3949
|
+
fail(`--ids accepts at most ${BATCH_MAX_ITEMS2} ids per request
|
|
3950
|
+
${usage}`, 2);
|
|
3951
|
+
}
|
|
3952
|
+
return ids;
|
|
3953
|
+
}
|
|
3954
|
+
function project(args, response, operation, minimal, compact) {
|
|
3955
|
+
const data = unwrapReviewData(response, operation);
|
|
3956
|
+
if (args.flags.raw) return response;
|
|
3957
|
+
if (args.flags.verbose) return compact(data);
|
|
3958
|
+
return minimal(data);
|
|
3959
|
+
}
|
|
3960
|
+
async function reviewList(args) {
|
|
3961
|
+
const params = {};
|
|
3962
|
+
const knowledgeBaseId = stringValue(args, "knowledge-base-id");
|
|
3963
|
+
if (knowledgeBaseId !== void 0) params.knowledge_base_id = knowledgeBaseId;
|
|
3964
|
+
const decision = stringValue(args, "decision");
|
|
3965
|
+
if (decision !== void 0) params.decision = decision;
|
|
3966
|
+
const reviewStatus = stringValue(args, "review-status");
|
|
3967
|
+
if (reviewStatus !== void 0) {
|
|
3968
|
+
if (!REVIEW_STATUS_FILTERS.includes(reviewStatus)) {
|
|
3969
|
+
fail(`review-status must be one of ${REVIEW_STATUS_FILTERS.join("|")}
|
|
3970
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3971
|
+
}
|
|
3972
|
+
params.review_status = reviewStatus;
|
|
3973
|
+
}
|
|
3974
|
+
const page = positiveIntFlag(args, "page", REVIEW_LIST_USAGE);
|
|
3975
|
+
if (page !== void 0) params.page = page;
|
|
3976
|
+
const pageSize = positiveIntFlag(args, "page-size", REVIEW_LIST_USAGE);
|
|
3977
|
+
if (pageSize !== void 0) params.page_size = pageSize;
|
|
3978
|
+
const sortBy = stringValue(args, "sort-by");
|
|
3979
|
+
if (sortBy !== void 0) {
|
|
3980
|
+
if (sortBy !== "confidence_score") {
|
|
3981
|
+
fail(`sort-by only supports confidence_score
|
|
3982
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3983
|
+
}
|
|
3984
|
+
params.sort_by = sortBy;
|
|
3985
|
+
}
|
|
3986
|
+
const sortOrder = stringValue(args, "sort-order");
|
|
3987
|
+
if (sortOrder !== void 0) {
|
|
3988
|
+
if (sortBy === void 0) {
|
|
3989
|
+
fail(`sort-order requires sort-by
|
|
3990
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3991
|
+
}
|
|
3992
|
+
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
3993
|
+
fail(`sort-order must be asc or desc
|
|
3994
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3995
|
+
}
|
|
3996
|
+
params.sort_order = sortOrder;
|
|
3997
|
+
}
|
|
3998
|
+
const ctx = buildContext(args);
|
|
3999
|
+
const resp = await ctx.client.get("/v1/knowledge/reviews", params);
|
|
4000
|
+
const out = project(
|
|
4001
|
+
args,
|
|
4002
|
+
resp,
|
|
4003
|
+
"review list",
|
|
4004
|
+
minimalReviewListResponse,
|
|
4005
|
+
compactReviewListResponse
|
|
4006
|
+
);
|
|
4007
|
+
printResult(out, !!args.flags.json);
|
|
4008
|
+
return 0;
|
|
4009
|
+
}
|
|
4010
|
+
async function reviewGet(args) {
|
|
4011
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_GET_USAGE);
|
|
4012
|
+
const contentPage = positiveIntFlag(args, "content-page", REVIEW_GET_USAGE);
|
|
4013
|
+
const params = { review_id: reviewId };
|
|
4014
|
+
if (contentPage !== void 0) params.content_page = contentPage;
|
|
4015
|
+
const ctx = buildContext(args);
|
|
4016
|
+
const resp = await ctx.client.get("/v1/knowledge/reviews/detail", params);
|
|
4017
|
+
const out = project(args, resp, "review get", minimalReviewDetail, compactReviewDetail);
|
|
4018
|
+
printResult(out, !!args.flags.json);
|
|
4019
|
+
return 0;
|
|
4020
|
+
}
|
|
4021
|
+
async function reviewAction(args) {
|
|
4022
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_ACTION_USAGE);
|
|
4023
|
+
const action = requireEnumFlag(args, "action", REVIEW_ACTIONS, REVIEW_ACTION_USAGE);
|
|
4024
|
+
const body = { review_id: reviewId, action };
|
|
4025
|
+
const editedContent = stringValue(args, "edited-content");
|
|
4026
|
+
const editedContentFile = stringValue(args, "edited-content-file");
|
|
4027
|
+
if (editedContent !== void 0 && editedContentFile !== void 0) {
|
|
4028
|
+
fail(`cannot use --edited-content and --edited-content-file together
|
|
4029
|
+
${REVIEW_ACTION_USAGE}`, 2);
|
|
4030
|
+
}
|
|
4031
|
+
if (editedContent !== void 0) body.edited_content = editedContent;
|
|
4032
|
+
else if (editedContentFile !== void 0) {
|
|
4033
|
+
try {
|
|
4034
|
+
body.edited_content = readFileSync2(editedContentFile, "utf-8");
|
|
4035
|
+
} catch (error) {
|
|
4036
|
+
fail(
|
|
4037
|
+
`cannot read --edited-content-file ${editedContentFile}: ${error instanceof Error ? error.message : String(error)}`,
|
|
4038
|
+
2
|
|
4039
|
+
);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
const conflictResolutionsRaw = stringValue(args, "conflict-resolutions");
|
|
4043
|
+
if (conflictResolutionsRaw !== void 0) {
|
|
4044
|
+
try {
|
|
4045
|
+
body.conflict_resolutions = parseConflictResolutions(conflictResolutionsRaw);
|
|
4046
|
+
} catch (error) {
|
|
4047
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4048
|
+
${REVIEW_ACTION_USAGE}`, 2);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
const ctx = buildContext(args);
|
|
4052
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/action", body);
|
|
4053
|
+
const out = project(args, resp, "review action", minimalReviewDetail, compactReviewDetail);
|
|
4054
|
+
printResult(out, !!args.flags.json);
|
|
4055
|
+
return 0;
|
|
4056
|
+
}
|
|
4057
|
+
async function reviewResolveConflicts(args) {
|
|
4058
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_RESOLVE_USAGE);
|
|
4059
|
+
const raw = readPayloadInput(args, "resolutions", "resolutions-file", REVIEW_RESOLVE_USAGE);
|
|
4060
|
+
let resolutions;
|
|
4061
|
+
try {
|
|
4062
|
+
resolutions = parseConflictResolutions(raw);
|
|
4063
|
+
} catch (error) {
|
|
4064
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4065
|
+
${REVIEW_RESOLVE_USAGE}`, 2);
|
|
4066
|
+
}
|
|
4067
|
+
const ctx = buildContext(args);
|
|
4068
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/resolve-conflicts", {
|
|
4069
|
+
review_id: reviewId,
|
|
4070
|
+
resolutions
|
|
4071
|
+
});
|
|
4072
|
+
const out = project(args, resp, "review resolve-conflicts", minimalReviewDetail, compactReviewDetail);
|
|
4073
|
+
printResult(out, !!args.flags.json);
|
|
4074
|
+
return 0;
|
|
4075
|
+
}
|
|
4076
|
+
async function reviewBatchAction(args) {
|
|
4077
|
+
const action = requireEnumFlag(args, "action", REVIEW_BATCH_ACTIONS, REVIEW_BATCH_ACTION_USAGE);
|
|
4078
|
+
const ids = parseIdsFlag2(args, REVIEW_BATCH_ACTION_USAGE);
|
|
4079
|
+
const ctx = buildContext(args);
|
|
4080
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/batch", { action, ids });
|
|
4081
|
+
const data = unwrapReviewData(resp, "review batch-action");
|
|
4082
|
+
printResult(data, !!args.flags.json);
|
|
4083
|
+
return reviewBatchExitCode(data);
|
|
4084
|
+
}
|
|
4085
|
+
async function reviewBatchDelete(args) {
|
|
4086
|
+
const ids = parseIdsFlag2(args, REVIEW_BATCH_DELETE_USAGE);
|
|
4087
|
+
const ctx = buildContext(args);
|
|
4088
|
+
const resp = await ctx.client.deleteJson("/v1/knowledge/reviews/batch", { ids });
|
|
4089
|
+
const data = unwrapReviewData(resp, "review batch-delete");
|
|
4090
|
+
printResult(data, !!args.flags.json);
|
|
4091
|
+
return reviewBatchExitCode(data);
|
|
4092
|
+
}
|
|
4093
|
+
async function reviewDelete(args) {
|
|
4094
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_DELETE_USAGE);
|
|
4095
|
+
const ctx = buildContext(args);
|
|
4096
|
+
const resp = await ctx.client.delete("/v1/knowledge/reviews", { review_id: reviewId });
|
|
4097
|
+
printResult(unwrapReviewData(resp, "review delete"), !!args.flags.json);
|
|
4098
|
+
return 0;
|
|
4099
|
+
}
|
|
4100
|
+
async function reviewTemplateList(args) {
|
|
4101
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_LIST_USAGE);
|
|
4102
|
+
const ctx = buildContext(args);
|
|
4103
|
+
const resp = await ctx.client.get("/v1/knowledge/review_templates", {
|
|
4104
|
+
knowledge_base_id: kbId
|
|
4105
|
+
});
|
|
4106
|
+
const data = unwrapReviewData(resp, "review template list");
|
|
4107
|
+
if (args.flags.raw) {
|
|
4108
|
+
printResult(resp, !!args.flags.json);
|
|
4109
|
+
return 0;
|
|
4110
|
+
}
|
|
4111
|
+
const out = args.flags.verbose ? Array.isArray(data) ? data.map(compactReviewTemplate) : [] : minimalReviewTemplateListResponse(data);
|
|
4112
|
+
printResult(out, !!args.flags.json);
|
|
4113
|
+
return 0;
|
|
4114
|
+
}
|
|
4115
|
+
function buildTemplateUpsertBody2(args, usage, requireNameAndSections) {
|
|
4116
|
+
const body = {};
|
|
4117
|
+
const name = stringValue(args, "name");
|
|
4118
|
+
if (name !== void 0) {
|
|
4119
|
+
if (name.length > TEMPLATE_MAX_NAME_LENGTH) {
|
|
4120
|
+
fail(`--name must be at most ${TEMPLATE_MAX_NAME_LENGTH} characters
|
|
4121
|
+
${usage}`, 2);
|
|
4122
|
+
}
|
|
4123
|
+
body.name = name;
|
|
4124
|
+
}
|
|
4125
|
+
const hasSectionsInput = Object.hasOwn(args.flags, "sections") || Object.hasOwn(args.flags, "sections-file");
|
|
4126
|
+
if (hasSectionsInput) {
|
|
4127
|
+
const raw = readPayloadInput(args, "sections", "sections-file", usage);
|
|
4128
|
+
try {
|
|
4129
|
+
body.sections = parseTemplateSections(raw);
|
|
4130
|
+
} catch (error) {
|
|
4131
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4132
|
+
${usage}`, 2);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
if (requireNameAndSections && (body.name === void 0 || body.sections === void 0)) {
|
|
4136
|
+
fail(usage, 2);
|
|
4137
|
+
}
|
|
4138
|
+
return body;
|
|
4139
|
+
}
|
|
4140
|
+
function projectTemplateResult(args, resp, operation) {
|
|
4141
|
+
const data = unwrapReviewData(resp, operation);
|
|
4142
|
+
if (args.flags.raw) return resp;
|
|
4143
|
+
if (data === null) return { ok: true };
|
|
4144
|
+
if (args.flags.verbose) return compactReviewTemplate(data);
|
|
4145
|
+
return minimalReviewTemplateDetail(data);
|
|
4146
|
+
}
|
|
4147
|
+
async function reviewTemplateCreate(args) {
|
|
4148
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_CREATE_USAGE2);
|
|
4149
|
+
const body = buildTemplateUpsertBody2(args, TEMPLATE_CREATE_USAGE2, true);
|
|
4150
|
+
if (args.flags.default) body.is_default = true;
|
|
4151
|
+
const ctx = buildContext(args);
|
|
4152
|
+
const resp = await ctx.client.postJson("/v1/knowledge/review_templates", body, {
|
|
4153
|
+
knowledge_base_id: kbId
|
|
4154
|
+
});
|
|
4155
|
+
printResult(projectTemplateResult(args, resp, "review template create"), !!args.flags.json);
|
|
4156
|
+
return 0;
|
|
4157
|
+
}
|
|
4158
|
+
async function reviewTemplateUpdate(args) {
|
|
4159
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_UPDATE_USAGE2);
|
|
4160
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_UPDATE_USAGE2);
|
|
4161
|
+
const body = buildTemplateUpsertBody2(args, TEMPLATE_UPDATE_USAGE2, false);
|
|
4162
|
+
const setDefault = args.flags.default !== void 0;
|
|
4163
|
+
const clearDefault = args.flags["no-default"] !== void 0;
|
|
4164
|
+
if (setDefault && clearDefault) {
|
|
4165
|
+
fail(`cannot use --default and --no-default together
|
|
4166
|
+
${TEMPLATE_UPDATE_USAGE2}`, 2);
|
|
4167
|
+
}
|
|
4168
|
+
if (setDefault) body.is_default = true;
|
|
4169
|
+
else if (clearDefault) body.is_default = false;
|
|
4170
|
+
if (Object.keys(body).length === 0) {
|
|
4171
|
+
fail(`nothing to update: provide --name, --sections/--sections-file or --default/--no-default
|
|
4172
|
+
${TEMPLATE_UPDATE_USAGE2}`, 2);
|
|
4173
|
+
}
|
|
4174
|
+
const ctx = buildContext(args);
|
|
4175
|
+
const resp = await ctx.client.putJson("/v1/knowledge/review_templates", body, {
|
|
4176
|
+
knowledge_base_id: kbId,
|
|
4177
|
+
template_id: templateId
|
|
4178
|
+
});
|
|
4179
|
+
printResult(projectTemplateResult(args, resp, "review template update"), !!args.flags.json);
|
|
4180
|
+
return 0;
|
|
4181
|
+
}
|
|
4182
|
+
async function reviewTemplateDelete(args) {
|
|
4183
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_DELETE_USAGE2);
|
|
4184
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_DELETE_USAGE2);
|
|
4185
|
+
const ctx = buildContext(args);
|
|
4186
|
+
const resp = await ctx.client.delete("/v1/knowledge/review_templates", {
|
|
4187
|
+
knowledge_base_id: kbId,
|
|
4188
|
+
template_id: templateId
|
|
4189
|
+
});
|
|
4190
|
+
printResult(unwrapReviewData(resp, "review template delete"), !!args.flags.json);
|
|
4191
|
+
return 0;
|
|
4192
|
+
}
|
|
4193
|
+
async function reviewTemplateGet(args) {
|
|
4194
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_GET_USAGE2);
|
|
4195
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_GET_USAGE2);
|
|
4196
|
+
const ctx = buildContext(args);
|
|
4197
|
+
const resp = await ctx.client.get("/v1/knowledge/review_templates/detail", {
|
|
4198
|
+
knowledge_base_id: kbId,
|
|
4199
|
+
template_id: templateId
|
|
4200
|
+
});
|
|
4201
|
+
const data = unwrapReviewData(resp, "review template get");
|
|
4202
|
+
if (args.flags.raw) {
|
|
4203
|
+
printResult(resp, !!args.flags.json);
|
|
4204
|
+
return 0;
|
|
4205
|
+
}
|
|
4206
|
+
if (data === null) {
|
|
4207
|
+
printResult({ found: false, template_id: templateId }, !!args.flags.json);
|
|
4208
|
+
return 0;
|
|
4209
|
+
}
|
|
4210
|
+
const out = args.flags.verbose ? compactReviewTemplate(data) : minimalReviewTemplateDetail(data);
|
|
4211
|
+
printResult(out, !!args.flags.json);
|
|
4212
|
+
return 0;
|
|
4213
|
+
}
|
|
4214
|
+
async function reviewTemplateSetDefault(args) {
|
|
4215
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_SET_DEFAULT_USAGE2);
|
|
4216
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_SET_DEFAULT_USAGE2);
|
|
4217
|
+
const ctx = buildContext(args);
|
|
4218
|
+
const resp = await ctx.client.postJson(
|
|
4219
|
+
"/v1/knowledge/review_templates/set_default",
|
|
4220
|
+
{},
|
|
4221
|
+
{ knowledge_base_id: kbId, template_id: templateId }
|
|
4222
|
+
);
|
|
4223
|
+
printResult(projectTemplateResult(args, resp, "review template set-default"), !!args.flags.json);
|
|
4224
|
+
return 0;
|
|
4225
|
+
}
|
|
4226
|
+
async function reviewTemplateClearDefault(args) {
|
|
4227
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_CLEAR_DEFAULT_USAGE2);
|
|
4228
|
+
const ctx = buildContext(args);
|
|
4229
|
+
const resp = await ctx.client.delete("/v1/knowledge/review_templates/default", {
|
|
4230
|
+
knowledge_base_id: kbId
|
|
4231
|
+
});
|
|
4232
|
+
printResult(unwrapReviewData(resp, "review template clear-default"), !!args.flags.json);
|
|
4233
|
+
return 0;
|
|
4234
|
+
}
|
|
4235
|
+
async function reviewSettingsGet(args) {
|
|
4236
|
+
const kbId = requireId(args, "kb-id", 0, SETTINGS_GET_USAGE);
|
|
4237
|
+
const ctx = buildContext(args);
|
|
4238
|
+
const resp = await ctx.client.get("/v1/knowledge/review_settings", {
|
|
4239
|
+
knowledge_base_id: kbId
|
|
4240
|
+
});
|
|
4241
|
+
printResult(unwrapReviewData(resp, "review settings get"), !!args.flags.json);
|
|
4242
|
+
return 0;
|
|
4243
|
+
}
|
|
4244
|
+
var SETTINGS_BOOL_FLAG_FIELDS = [
|
|
4245
|
+
["review-enabled", "review_enabled"],
|
|
4246
|
+
["rewrite-enabled", "rewrite_enabled"],
|
|
4247
|
+
["conflict-detection-enabled", "conflict_detection_enabled"],
|
|
4248
|
+
["rewrite-auto-ingest-enabled", "rewrite_auto_ingest_enabled"]
|
|
4249
|
+
];
|
|
4250
|
+
var SETTINGS_NUMBER_FLAG_FIELDS = [
|
|
4251
|
+
["high-threshold", "high_threshold", false],
|
|
4252
|
+
["logic-rewrite-threshold", "logic_rewrite_threshold", false],
|
|
4253
|
+
["noise-rewrite-threshold", "noise_rewrite_threshold", false],
|
|
4254
|
+
["max-rewrite-iterations", "max_rewrite_iterations", true]
|
|
4255
|
+
];
|
|
4256
|
+
async function reviewSettingsUpdate(args) {
|
|
4257
|
+
const kbId = requireId(args, "kb-id", 0, SETTINGS_UPDATE_USAGE2);
|
|
4258
|
+
const body = {};
|
|
4259
|
+
for (const [flag, field] of SETTINGS_BOOL_FLAG_FIELDS) {
|
|
4260
|
+
const on = args.flags[flag] !== void 0;
|
|
4261
|
+
const off = args.flags[`no-${flag}`] !== void 0;
|
|
4262
|
+
if (on && off) {
|
|
4263
|
+
fail(`cannot use --${flag} and --no-${flag} together
|
|
4264
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4265
|
+
}
|
|
4266
|
+
if (on) body[field] = true;
|
|
4267
|
+
else if (off) body[field] = false;
|
|
4268
|
+
}
|
|
4269
|
+
for (const [flag, field, integer] of SETTINGS_NUMBER_FLAG_FIELDS) {
|
|
4270
|
+
const value = args.flags[flag];
|
|
4271
|
+
if (value === void 0) continue;
|
|
4272
|
+
if (typeof value !== "string") fail(SETTINGS_UPDATE_USAGE2, 2);
|
|
4273
|
+
const parsed = Number(value);
|
|
4274
|
+
if (!Number.isFinite(parsed) || integer && !Number.isInteger(parsed)) {
|
|
4275
|
+
fail(`--${flag} must be ${integer ? "an integer" : "a number"}
|
|
4276
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4277
|
+
}
|
|
4278
|
+
body[field] = parsed;
|
|
4279
|
+
}
|
|
4280
|
+
if (Object.keys(body).length === 0) {
|
|
4281
|
+
fail(`nothing to update: provide at least one settings field
|
|
4282
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4283
|
+
}
|
|
4284
|
+
body.knowledge_base_id = kbId;
|
|
4285
|
+
const ctx = buildContext(args);
|
|
4286
|
+
const resp = await ctx.client.patchJson("/v1/knowledge/review_settings", body);
|
|
4287
|
+
const data = unwrapReviewData(resp, "review settings update");
|
|
4288
|
+
printResult(data ?? { ok: true }, !!args.flags.json);
|
|
4289
|
+
return 0;
|
|
4290
|
+
}
|
|
4291
|
+
|
|
2888
4292
|
// src/cli/main.ts
|
|
2889
4293
|
var HELP5 = `ctxdb v${PACKAGE_VERSION} \u2014 RDS ContextDatabase CLI (multi-agent)
|
|
2890
4294
|
|
|
@@ -2939,14 +4343,57 @@ COMMANDS
|
|
|
2939
4343
|
memory update <memory-id> --text=<new-text> [--agent=<name>]
|
|
2940
4344
|
memory delete <memory-id> | --all [--agent=<name>]
|
|
2941
4345
|
|
|
4346
|
+
memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--agent=<name>]
|
|
4347
|
+
memory promotion list [--status=<generating|pending_review|approving|approved|rejected|generation_failed>]
|
|
4348
|
+
[--page=N] [--page-size=N] [--sort-by=quality_score]
|
|
4349
|
+
[--sort-order=asc|desc] [--raw] [--agent=<name>]
|
|
4350
|
+
memory promotion get <promotion-id> [--raw] [--agent=<name>]
|
|
4351
|
+
memory promotion approve --promotion-id=<id> --knowledge-base-id=<kb-id> [--agent=<name>]
|
|
4352
|
+
memory promotion reject --promotion-id=<id> [--reason=<text>] [--agent=<name>]
|
|
4353
|
+
memory promotion batch-action --action=<approve|reject> --ids=<id1,id2,...>
|
|
4354
|
+
[--knowledge-base-id=<kb-id> (required for approve)] [--reason=<text>] [--agent=<name>]
|
|
4355
|
+
memory promotion batch-delete --ids=<id1,id2,...> [--agent=<name>]
|
|
4356
|
+
memory promotion resolve-doubts --promotion-id=<id>
|
|
4357
|
+
--resolutions-json='[{"index":0,"choice":"<label>","comment":"..."}]'
|
|
4358
|
+
[--title=<title>] [--content=<markdown>] [--agent=<name>]
|
|
4359
|
+
memory promotion template list [--raw] [--agent=<name>]
|
|
4360
|
+
memory promotion template create --name=<name>
|
|
4361
|
+
--sections-json='[{"title":"...","guidance":"...","level":1}]'
|
|
4362
|
+
[--is-default] [--agent=<name>]
|
|
4363
|
+
memory promotion template get <template-id> [--agent=<name>]
|
|
4364
|
+
memory promotion template update <template-id> --name=<name> --sections-json='[...]'
|
|
4365
|
+
[--is-default] [--agent=<name>]
|
|
4366
|
+
memory promotion template delete <template-id> [--agent=<name>]
|
|
4367
|
+
memory promotion template set-default <template-id> [--agent=<name>]
|
|
4368
|
+
memory promotion template clear-default [--agent=<name>]
|
|
4369
|
+
memory promotion settings get [--agent=<name>]
|
|
4370
|
+
memory promotion settings update [--auto-ingest-enabled=<true|false>]
|
|
4371
|
+
[--auto-ingest-threshold=<0..1>]
|
|
4372
|
+
[--default-kb-id=<kb-id> | --clear-default-kb] [--agent=<name>]
|
|
4373
|
+
Memory promotion (\u8BB0\u5FC6\u6210\u6587): generate, review,
|
|
4374
|
+
approve/reject promotion tasks; manage doc
|
|
4375
|
+
templates and auto-ingest settings. Batch
|
|
4376
|
+
requests accept at most 50 ids.
|
|
4377
|
+
Default output is a compact agent-friendly
|
|
4378
|
+
view; --raw returns the server envelope
|
|
4379
|
+
verbatim.
|
|
4380
|
+
|
|
4381
|
+
|
|
2942
4382
|
kb create <kb-name> [--description=<desc>] [--agent=<name>]
|
|
2943
4383
|
kb upload-text <kb-name> <doc-name> --text=<body> [--agent=<name>]
|
|
2944
4384
|
[--file-path=<server-logical-path>] [--wait|--no-wait]
|
|
2945
4385
|
kb upload-file <kb-name> <local-path> [--agent=<name>] [--doc-name=...]
|
|
2946
4386
|
[--file-path=<server-logical-path>] [--wait|--no-wait]
|
|
2947
|
-
|
|
4387
|
+
kb update-text <document-id> --text=<body> [--agent=<name>] [--wait|--no-wait]
|
|
4388
|
+
or: update-text --kb-name=<kb-name> --doc-name=<logical-name> --text=<body>
|
|
4389
|
+
[--file-path=<server-logical-path>] [--wait|--no-wait]
|
|
4390
|
+
kb update-file <document-id> <local-path> [--agent=<name>] [--wait|--no-wait]
|
|
4391
|
+
or: update-file <local-path> --kb-name=<kb-name> [--doc-name=<logical-name>]
|
|
4392
|
+
[--file-path=<server-logical-path>] [--wait|--no-wait]
|
|
4393
|
+
Uploads return immediately by default; updates do too.
|
|
2948
4394
|
--wait polls for terminal ingest status.
|
|
2949
4395
|
--no-wait is a compatibility alias for the default.
|
|
4396
|
+
Keep the returned ID for the next update.
|
|
2950
4397
|
kb list [--agent=<name>]
|
|
2951
4398
|
kb documents-list <kb-name> [--agent=<name>]
|
|
2952
4399
|
kb document-get <kb-name> <doc-id> [--agent=<name>]
|
|
@@ -2958,6 +4405,56 @@ COMMANDS
|
|
|
2958
4405
|
--verbose: compact view adds doc_name/kb_id/
|
|
2959
4406
|
doc_id?/tags? for citation.
|
|
2960
4407
|
--raw: server response verbatim (curl parity).
|
|
4408
|
+
kb review list [--knowledge-base-id=<id>] [--decision=<d>]
|
|
4409
|
+
[--review-status=<pending|reviewed|rejected>]
|
|
4410
|
+
[--page=N] [--page-size=N] [--sort-by=confidence_score]
|
|
4411
|
+
[--sort-order=asc|desc] [--verbose] [--raw]
|
|
4412
|
+
List knowledge review records (newest first
|
|
4413
|
+
by default; --sort-by sorts by quality score).
|
|
4414
|
+
kb review get <review-id> [--content-page=N] [--verbose] [--raw]
|
|
4415
|
+
Review detail: scores, decision, conflicts
|
|
4416
|
+
and paginated original content.
|
|
4417
|
+
kb review action <review-id> --action=<confirm|force_accept|accept_rewrite|reject|reject_reupload>
|
|
4418
|
+
[--edited-content=<text> | --edited-content-file=<path>]
|
|
4419
|
+
[--conflict-resolutions=<json>] [--verbose] [--raw]
|
|
4420
|
+
Perform one human review action.
|
|
4421
|
+
kb review resolve-conflicts <review-id>
|
|
4422
|
+
(--resolutions=<json> | --resolutions-file=<path>) [--verbose] [--raw]
|
|
4423
|
+
Resolve conflicts one by one. resolutions:
|
|
4424
|
+
[{"conflict_id": "...", "resolution":
|
|
4425
|
+
"use_new|keep_old|merge|annotate_divergence"}]
|
|
4426
|
+
kb review batch-action --action=<confirm|reject> --ids=<id1,id2,...>
|
|
4427
|
+
Batch confirm/reject up to 50 reviews.
|
|
4428
|
+
kb review batch-delete --ids=<id1,id2,...>
|
|
4429
|
+
Batch delete up to 50 reviewed/rejected records.
|
|
4430
|
+
kb review delete <review-id> Delete one reviewed/rejected record.
|
|
4431
|
+
kb review template list <kb-id> [--verbose] [--raw]
|
|
4432
|
+
kb review template create <kb-id> --name=<name>
|
|
4433
|
+
(--sections=<json> | --sections-file=<path>) [--default]
|
|
4434
|
+
kb review template update <kb-id> <template-id> [--name=<name>]
|
|
4435
|
+
[--sections=<json> | --sections-file=<path>]
|
|
4436
|
+
[--default | --no-default]
|
|
4437
|
+
kb review template delete <kb-id> <template-id>
|
|
4438
|
+
kb review template get <kb-id> <template-id> [--verbose] [--raw]
|
|
4439
|
+
kb review template set-default <kb-id> <template-id>
|
|
4440
|
+
kb review template clear-default <kb-id>
|
|
4441
|
+
Manage KB review-rewrite templates
|
|
4442
|
+
(max 5 per KB). sections JSON:
|
|
4443
|
+
[{"level": 1, "title": "...", "guidance": "..."}]
|
|
4444
|
+
kb review settings get <kb-id> [--json]
|
|
4445
|
+
kb review settings update <kb-id> [--review-enabled | --no-review-enabled]
|
|
4446
|
+
[--high-threshold=F] [--logic-rewrite-threshold=F]
|
|
4447
|
+
[--noise-rewrite-threshold=F] [--max-rewrite-iterations=N]
|
|
4448
|
+
[--rewrite-enabled | --no-rewrite-enabled]
|
|
4449
|
+
[--conflict-detection-enabled | --no-conflict-detection-enabled]
|
|
4450
|
+
[--rewrite-auto-ingest-enabled | --no-rewrite-auto-ingest-enabled]
|
|
4451
|
+
Per-KB knowledge-review enablement, thresholds,
|
|
4452
|
+
and rewrite/auto-ingest switches.
|
|
4453
|
+
kb review list/detail/template default to the
|
|
4454
|
+
minimal agent view; --verbose adds fields,
|
|
4455
|
+
--raw ships the server envelope verbatim.
|
|
4456
|
+
All kb review commands accept --json.
|
|
4457
|
+
|
|
2961
4458
|
|
|
2962
4459
|
skill install --target <qoder|qoderwork|qwenwork|codex|claude|opencode|openclaw|hermes> [--json]
|
|
2963
4460
|
skill install --path <dir> [--json]
|
|
@@ -3006,21 +4503,68 @@ var ROUTES = {
|
|
|
3006
4503
|
list: memoryList,
|
|
3007
4504
|
get: memoryGet,
|
|
3008
4505
|
update: memoryUpdate,
|
|
3009
|
-
delete: memoryDelete
|
|
4506
|
+
delete: memoryDelete,
|
|
4507
|
+
promotion: {
|
|
4508
|
+
generate: promotionGenerate,
|
|
4509
|
+
list: promotionList,
|
|
4510
|
+
get: promotionGet,
|
|
4511
|
+
approve: promotionApprove,
|
|
4512
|
+
reject: promotionReject,
|
|
4513
|
+
"batch-action": promotionBatchAction,
|
|
4514
|
+
"batch-delete": promotionBatchDelete,
|
|
4515
|
+
"resolve-doubts": promotionResolveDoubts,
|
|
4516
|
+
template: {
|
|
4517
|
+
list: promotionTemplateList,
|
|
4518
|
+
create: promotionTemplateCreate,
|
|
4519
|
+
get: promotionTemplateGet,
|
|
4520
|
+
update: promotionTemplateUpdate,
|
|
4521
|
+
delete: promotionTemplateDelete,
|
|
4522
|
+
"set-default": promotionTemplateSetDefault,
|
|
4523
|
+
"clear-default": promotionTemplateClearDefault
|
|
4524
|
+
},
|
|
4525
|
+
settings: {
|
|
4526
|
+
get: promotionSettingsGet,
|
|
4527
|
+
update: promotionSettingsUpdate
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
3010
4530
|
},
|
|
3011
4531
|
kb: {
|
|
3012
4532
|
create: kbCreate,
|
|
3013
4533
|
"upload-text": kbUploadText,
|
|
3014
4534
|
"upload-file": kbUploadFile,
|
|
4535
|
+
"update-text": kbUpdateText,
|
|
4536
|
+
"update-file": kbUpdateFile,
|
|
3015
4537
|
list: kbList,
|
|
3016
4538
|
"documents-list": kbDocumentsList,
|
|
3017
4539
|
"document-get": kbDocumentGet,
|
|
3018
|
-
search: kbSearch
|
|
4540
|
+
search: kbSearch,
|
|
4541
|
+
review: {
|
|
4542
|
+
list: reviewList,
|
|
4543
|
+
get: reviewGet,
|
|
4544
|
+
action: reviewAction,
|
|
4545
|
+
"resolve-conflicts": reviewResolveConflicts,
|
|
4546
|
+
"batch-action": reviewBatchAction,
|
|
4547
|
+
"batch-delete": reviewBatchDelete,
|
|
4548
|
+
delete: reviewDelete,
|
|
4549
|
+
template: {
|
|
4550
|
+
list: reviewTemplateList,
|
|
4551
|
+
create: reviewTemplateCreate,
|
|
4552
|
+
update: reviewTemplateUpdate,
|
|
4553
|
+
delete: reviewTemplateDelete,
|
|
4554
|
+
get: reviewTemplateGet,
|
|
4555
|
+
"set-default": reviewTemplateSetDefault,
|
|
4556
|
+
"clear-default": reviewTemplateClearDefault
|
|
4557
|
+
},
|
|
4558
|
+
settings: {
|
|
4559
|
+
get: reviewSettingsGet,
|
|
4560
|
+
update: reviewSettingsUpdate
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
3019
4563
|
}
|
|
3020
4564
|
};
|
|
3021
|
-
async function dispatch(argv) {
|
|
4565
|
+
async function dispatch(argv, routes = ROUTES, help = HELP5) {
|
|
3022
4566
|
if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
|
|
3023
|
-
process.stdout.write(
|
|
4567
|
+
process.stdout.write(help);
|
|
3024
4568
|
return 0;
|
|
3025
4569
|
}
|
|
3026
4570
|
if (argv[0] === "-v" || argv[0] === "--version") {
|
|
@@ -3029,61 +4573,82 @@ async function dispatch(argv) {
|
|
|
3029
4573
|
return 0;
|
|
3030
4574
|
}
|
|
3031
4575
|
const [first, ...rest] = argv;
|
|
3032
|
-
const route =
|
|
4576
|
+
const route = routes[first];
|
|
3033
4577
|
if (!route) {
|
|
3034
4578
|
process.stderr.write(`unknown command: ${first}
|
|
3035
4579
|
`);
|
|
3036
|
-
process.stderr.write(
|
|
4580
|
+
process.stderr.write(help);
|
|
3037
4581
|
return 2;
|
|
3038
4582
|
}
|
|
3039
4583
|
if (typeof route === "function") {
|
|
3040
4584
|
return Promise.resolve(route(parseArgs(rest)));
|
|
3041
4585
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
`
|
|
3046
|
-
|
|
4586
|
+
return dispatchGroup([first], route, rest);
|
|
4587
|
+
}
|
|
4588
|
+
async function dispatchGroup(path, group, argv) {
|
|
4589
|
+
const usage = `usage: ctxdb ${path.join(" ")} <${Object.keys(group).join("|")}> ...
|
|
4590
|
+
`;
|
|
4591
|
+
if (argv.length === 0) {
|
|
4592
|
+
process.stderr.write(usage);
|
|
3047
4593
|
return 2;
|
|
3048
4594
|
}
|
|
3049
|
-
if (
|
|
3050
|
-
process.stdout.write(
|
|
3051
|
-
`usage: ctxdb ${first} <${Object.keys(route).join("|")}> ...
|
|
3052
|
-
`
|
|
3053
|
-
);
|
|
4595
|
+
if (argv[0] === "-h" || argv[0] === "--help") {
|
|
4596
|
+
process.stdout.write(usage);
|
|
3054
4597
|
return 0;
|
|
3055
4598
|
}
|
|
3056
|
-
const [sub, ...subRest] =
|
|
3057
|
-
const handler =
|
|
4599
|
+
const [sub, ...subRest] = argv;
|
|
4600
|
+
const handler = group[sub];
|
|
3058
4601
|
if (!handler) {
|
|
3059
|
-
process.stderr.write(`unknown subcommand: ${
|
|
4602
|
+
process.stderr.write(`unknown subcommand: ${[...path, sub].join(" ")}
|
|
3060
4603
|
`);
|
|
3061
4604
|
return 2;
|
|
3062
4605
|
}
|
|
3063
|
-
|
|
4606
|
+
if (typeof handler === "function") {
|
|
4607
|
+
return Promise.resolve(handler(parseArgs(subRest)));
|
|
4608
|
+
}
|
|
4609
|
+
return dispatchGroup([...path, sub], handler, subRest);
|
|
3064
4610
|
}
|
|
3065
|
-
async function main(argv = process.argv.slice(2)) {
|
|
4611
|
+
async function main(argv = process.argv.slice(2), routes = ROUTES, help = HELP5) {
|
|
3066
4612
|
const notification = beginUpdateNotification(argv, {
|
|
3067
4613
|
currentVersion: PACKAGE_VERSION,
|
|
3068
4614
|
workerPath: fileURLToPath2(new URL("../workers/version-check.js", import.meta.url))
|
|
3069
4615
|
});
|
|
3070
|
-
const code = await dispatch(argv);
|
|
4616
|
+
const code = await dispatch(argv, routes, help);
|
|
3071
4617
|
completeUpdateNotification(notification, code);
|
|
3072
4618
|
return code;
|
|
3073
4619
|
}
|
|
3074
|
-
|
|
3075
|
-
(
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
4620
|
+
function runMain(routes = ROUTES, help = HELP5) {
|
|
4621
|
+
main(process.argv.slice(2), routes, help).then(
|
|
4622
|
+
(code) => {
|
|
4623
|
+
if (typeof code === "number") process.exitCode = code;
|
|
4624
|
+
},
|
|
4625
|
+
(err) => {
|
|
4626
|
+
process.stderr.write(`error: ${err?.message ?? err}
|
|
3080
4627
|
`);
|
|
3081
|
-
|
|
3082
|
-
|
|
4628
|
+
if (process.env.RDS_CTXDB_DEBUG === "1" && err?.stack) {
|
|
4629
|
+
process.stderr.write(err.stack + "\n");
|
|
4630
|
+
}
|
|
4631
|
+
process.exitCode = 1;
|
|
3083
4632
|
}
|
|
3084
|
-
|
|
4633
|
+
);
|
|
4634
|
+
}
|
|
4635
|
+
var directEntry = (() => {
|
|
4636
|
+
try {
|
|
4637
|
+
const modulePath = resolve2(fileURLToPath2(import.meta.url));
|
|
4638
|
+
const argvPath = resolve2(process.argv[1] ?? "");
|
|
4639
|
+
return modulePath === argvPath || realpathSync(modulePath) === realpathSync(argvPath);
|
|
4640
|
+
} catch {
|
|
4641
|
+
return false;
|
|
3085
4642
|
}
|
|
3086
|
-
);
|
|
4643
|
+
})();
|
|
4644
|
+
var directEntryName = basename2(fileURLToPath2(import.meta.url));
|
|
4645
|
+
if (directEntry && (directEntryName === "main.ts" || directEntryName === "main.js")) {
|
|
4646
|
+
runMain();
|
|
4647
|
+
}
|
|
3087
4648
|
export {
|
|
3088
|
-
|
|
4649
|
+
HELP5 as HELP,
|
|
4650
|
+
ROUTES,
|
|
4651
|
+
dispatch,
|
|
4652
|
+
main,
|
|
4653
|
+
runMain
|
|
3089
4654
|
};
|