@aliyunrds/ctxdb 1.0.7 → 1.0.8-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.internal.md +59 -0
- package/README.md +162 -12
- package/dist/{chunk-X2BX6LR6.js → chunk-3B7J5VUT.js} +1 -1
- package/dist/{chunk-S5W4FQ7M.js → chunk-KYW76CL7.js} +970 -771
- package/dist/{chunk-R2QS6ACP.js → chunk-LTAO6USK.js} +1 -1
- package/dist/{chunk-DPLMBHLU.js → chunk-NF5KFV2P.js} +5 -5
- package/dist/{chunk-QVXDI77N.js → chunk-NFAPLK4C.js} +1 -1
- package/dist/chunk-R67JELM7.js +20 -0
- package/dist/{chunk-DC5NWGDW.js → chunk-SGRMPSZU.js} +2 -2
- package/dist/{chunk-NFTGG6WK.js → chunk-TYW6ABNS.js} +2 -2
- package/dist/{chunk-Z6OHSEF2.js → chunk-ULAWTBBC.js} +34 -16
- package/dist/chunk-VIG4SYLU.js +8 -0
- package/dist/cli/main.js +1526 -73
- package/dist/hooks/hermes-post-llm-call.js +8 -6
- package/dist/hooks/hermes-pre-llm-call.js +11 -9
- package/dist/hooks/pre-tool-use.js +2 -1
- package/dist/hooks/session-start.js +10 -8
- package/dist/hooks/stop.js +8 -6
- package/dist/hooks/user-prompt-submit.js +10 -8
- package/dist/opencode/index.js +58 -2
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +4 -4
- package/dist/setup/skills/contextdb-memory/SKILL.md +4 -4
- package/dist/workers/version-check.js +3 -1
- package/package.json +8 -2
- /package/dist/{chunk-UEKR2Z3S.js → chunk-5ML4VSXN.js} +0 -0
package/dist/cli/main.js
CHANGED
|
@@ -16,13 +16,17 @@ import {
|
|
|
16
16
|
uploadFile,
|
|
17
17
|
uploadText,
|
|
18
18
|
validateLocalFile
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-NFAPLK4C.js";
|
|
20
20
|
import {
|
|
21
|
+
AGENT_CHOICES,
|
|
22
|
+
CtxdbError,
|
|
21
23
|
DEFAULT_BASE_URL,
|
|
22
24
|
DEFAULT_USER_ID,
|
|
23
25
|
HttpClient,
|
|
26
|
+
PACKAGE_NAME,
|
|
24
27
|
PACKAGE_VERSION,
|
|
25
28
|
SUPPORTED_AGENTS,
|
|
29
|
+
SUPPORTED_AGENT_CHOICES,
|
|
26
30
|
agentFromEnv,
|
|
27
31
|
agentHomeDir,
|
|
28
32
|
agentHomeDirs,
|
|
@@ -39,12 +43,16 @@ import {
|
|
|
39
43
|
save,
|
|
40
44
|
updateConfiguredDebug,
|
|
41
45
|
writeInstalledPkgVersion
|
|
42
|
-
} from "../chunk-
|
|
46
|
+
} from "../chunk-KYW76CL7.js";
|
|
43
47
|
import {
|
|
44
48
|
beginUpdateNotification,
|
|
45
49
|
completeUpdateNotification,
|
|
46
50
|
runSelfUpdate
|
|
47
|
-
} from "../chunk-
|
|
51
|
+
} from "../chunk-ULAWTBBC.js";
|
|
52
|
+
import {
|
|
53
|
+
DISTRIBUTION_MANIFEST
|
|
54
|
+
} from "../chunk-R67JELM7.js";
|
|
55
|
+
import "../chunk-VIG4SYLU.js";
|
|
48
56
|
|
|
49
57
|
// src/cli/util.ts
|
|
50
58
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -55,6 +63,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
55
63
|
// setup / init
|
|
56
64
|
"no-install-skill",
|
|
57
65
|
"no-validate",
|
|
66
|
+
"no-browser",
|
|
67
|
+
"device-code",
|
|
58
68
|
// update / upgrade
|
|
59
69
|
"self-update",
|
|
60
70
|
"no-self-update",
|
|
@@ -78,8 +88,34 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
78
88
|
// memory search / kb search (three-tier projection)
|
|
79
89
|
"raw",
|
|
80
90
|
// kb search / memory search --knowledge (W3 compaction bypass)
|
|
81
|
-
"full"
|
|
91
|
+
"full",
|
|
82
92
|
// debug recall full-content human output
|
|
93
|
+
// promotion
|
|
94
|
+
"is-default",
|
|
95
|
+
// promotion template create / update
|
|
96
|
+
"clear-default-kb",
|
|
97
|
+
// promotion settings update (clears with an empty string)
|
|
98
|
+
// review
|
|
99
|
+
"default",
|
|
100
|
+
// review template create/update --default
|
|
101
|
+
"no-default",
|
|
102
|
+
// review template update --no-default
|
|
103
|
+
"review-enabled",
|
|
104
|
+
// review settings update
|
|
105
|
+
"no-review-enabled",
|
|
106
|
+
// review settings update
|
|
107
|
+
"rewrite-enabled",
|
|
108
|
+
// review settings update
|
|
109
|
+
"no-rewrite-enabled",
|
|
110
|
+
// review settings update
|
|
111
|
+
"conflict-detection-enabled",
|
|
112
|
+
// review settings update
|
|
113
|
+
"no-conflict-detection-enabled",
|
|
114
|
+
// review settings update
|
|
115
|
+
"rewrite-auto-ingest-enabled",
|
|
116
|
+
// review settings update
|
|
117
|
+
"no-rewrite-auto-ingest-enabled"
|
|
118
|
+
// review settings update
|
|
83
119
|
]);
|
|
84
120
|
function parseArgs(argv, booleanFlags = BOOLEAN_FLAGS) {
|
|
85
121
|
const positional = [];
|
|
@@ -306,7 +342,9 @@ function fail(message, code = 1) {
|
|
|
306
342
|
}
|
|
307
343
|
|
|
308
344
|
// src/cli/main.ts
|
|
345
|
+
import { realpathSync } from "fs";
|
|
309
346
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
347
|
+
import { basename as basename2, resolve as resolve2 } from "path";
|
|
310
348
|
|
|
311
349
|
// src/setup/installer.ts
|
|
312
350
|
import {
|
|
@@ -327,8 +365,9 @@ import { join, dirname, resolve } from "path";
|
|
|
327
365
|
import { fileURLToPath } from "url";
|
|
328
366
|
import { execFileSync } from "child_process";
|
|
329
367
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
330
|
-
var
|
|
331
|
-
var
|
|
368
|
+
var PACKAGE_UNINSTALL_TARGETS = DISTRIBUTION_MANIFEST.id === "internal" ? DISTRIBUTION_MANIFEST.packageName : `${PACKAGE_NAME} @aliyunrds/ctxdb-shared`;
|
|
369
|
+
var UNINSTALL_NPM_HINT = `To also remove the npm binaries: \`npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS}\``;
|
|
370
|
+
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.)`;
|
|
332
371
|
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.";
|
|
333
372
|
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.";
|
|
334
373
|
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";
|
|
@@ -349,6 +388,8 @@ function skillInstallRoot(agent) {
|
|
|
349
388
|
return join(homedir(), ".config", "opencode", "skills");
|
|
350
389
|
case "hermes":
|
|
351
390
|
return join(homedir(), ".hermes", "skills");
|
|
391
|
+
case "workbuddy":
|
|
392
|
+
return join(homedir(), ".workbuddy", "skills");
|
|
352
393
|
}
|
|
353
394
|
}
|
|
354
395
|
var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
|
|
@@ -369,6 +410,8 @@ function hookConfigPath(agent) {
|
|
|
369
410
|
return join(homedir(), ".codex", "hooks.json");
|
|
370
411
|
case "claude":
|
|
371
412
|
return join(homedir(), ".claude", "settings.json");
|
|
413
|
+
case "workbuddy":
|
|
414
|
+
return join(homedir(), ".workbuddy", "settings.json");
|
|
372
415
|
case "opencode":
|
|
373
416
|
case "hermes":
|
|
374
417
|
return null;
|
|
@@ -424,6 +467,7 @@ async function runSetup(options) {
|
|
|
424
467
|
existingAgentHomes = inspectAgentHomes(agent).existing;
|
|
425
468
|
}
|
|
426
469
|
const cfg = load({ agent });
|
|
470
|
+
const persistedCredentialConfig = options.persistCredential === false ? load({ agent, env: {}, managedCredentials: false }) : null;
|
|
427
471
|
if (options.apiKey) cfg.apiKey = options.apiKey;
|
|
428
472
|
if (options.baseUrl) cfg.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
429
473
|
else if (!cfg.baseUrl) cfg.baseUrl = DEFAULT_BASE_URL;
|
|
@@ -436,19 +480,38 @@ async function runSetup(options) {
|
|
|
436
480
|
});
|
|
437
481
|
return { ok: false, steps };
|
|
438
482
|
}
|
|
439
|
-
|
|
483
|
+
const omitApiKey = options.persistCredential === false;
|
|
484
|
+
const persistedCfg = omitApiKey ? {
|
|
485
|
+
...cfg,
|
|
486
|
+
apiKey: null,
|
|
487
|
+
baseUrl: persistedCredentialConfig?.baseUrl ?? DEFAULT_BASE_URL
|
|
488
|
+
} : cfg;
|
|
489
|
+
save(persistedCfg, void 0, { agent, omitApiKey });
|
|
440
490
|
steps.push({
|
|
441
491
|
step: "write-config",
|
|
442
492
|
ok: true,
|
|
443
493
|
detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
|
|
444
494
|
});
|
|
445
|
-
if (isBuiltinAgent(agent)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
495
|
+
if (isBuiltinAgent(agent)) {
|
|
496
|
+
const hasDefault = configuredAgents().includes("default");
|
|
497
|
+
if (!hasDefault) {
|
|
498
|
+
save(persistedCfg, void 0, { agent: "default", omitApiKey });
|
|
499
|
+
steps.push({
|
|
500
|
+
step: "write-default-config",
|
|
501
|
+
ok: true,
|
|
502
|
+
detail: `default: copied from ${agent}`
|
|
503
|
+
});
|
|
504
|
+
} else if (omitApiKey) {
|
|
505
|
+
const persistedDefault = load({
|
|
506
|
+
agent: "default",
|
|
507
|
+
env: {},
|
|
508
|
+
managedCredentials: false
|
|
509
|
+
});
|
|
510
|
+
save(persistedDefault, void 0, {
|
|
511
|
+
agent: "default",
|
|
512
|
+
omitApiKey: true
|
|
513
|
+
});
|
|
514
|
+
}
|
|
452
515
|
}
|
|
453
516
|
if (installSkill && isBuiltinAgent(agent) && existingAgentHomes.includes(agentHomeDir(agent))) {
|
|
454
517
|
const installRoot = skillInstallRoot(agent);
|
|
@@ -758,6 +821,7 @@ async function runSetupAll(options = {}, dependencies = {}) {
|
|
|
758
821
|
apiKey,
|
|
759
822
|
baseUrl: options.baseUrl ?? existing.baseUrl,
|
|
760
823
|
userId: options.userId ?? existing.userId,
|
|
824
|
+
persistCredential: options.persistCredential,
|
|
761
825
|
installSkill: options.installSkill,
|
|
762
826
|
validate: options.validate
|
|
763
827
|
}));
|
|
@@ -1308,9 +1372,13 @@ function stripCtxdbHooks(agent) {
|
|
|
1308
1372
|
if (!path) return 0;
|
|
1309
1373
|
return stripCtxdbHooksFromFile(path);
|
|
1310
1374
|
}
|
|
1375
|
+
var CTXDB_PACKAGE_NAMES = [
|
|
1376
|
+
"@aliyunrds/ctxdb",
|
|
1377
|
+
"@ali/ctxdb-internal"
|
|
1378
|
+
];
|
|
1311
1379
|
function locateHookPaths() {
|
|
1312
1380
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1313
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1381
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1314
1382
|
if (!pkgRoot) return null;
|
|
1315
1383
|
const preferSrc = here.includes("/src/") || here.includes("\\src\\");
|
|
1316
1384
|
if (preferSrc) {
|
|
@@ -1344,7 +1412,7 @@ function hookPathsFromDir(dir, ext) {
|
|
|
1344
1412
|
}
|
|
1345
1413
|
function locateSkillDir(variant) {
|
|
1346
1414
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1347
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1415
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1348
1416
|
if (!pkgRoot) return null;
|
|
1349
1417
|
const preferSrc = here.includes("/src/") || here.includes("\\src\\");
|
|
1350
1418
|
if (preferSrc) {
|
|
@@ -1358,13 +1426,14 @@ function locateSkillDir(variant) {
|
|
|
1358
1426
|
return null;
|
|
1359
1427
|
}
|
|
1360
1428
|
function walkUpToPackageJson(start, expectedName) {
|
|
1429
|
+
const expectedNames = Array.isArray(expectedName) ? expectedName : [expectedName];
|
|
1361
1430
|
let cur = start;
|
|
1362
1431
|
for (let i = 0; i < 8; i++) {
|
|
1363
1432
|
const p = join(cur, "package.json");
|
|
1364
1433
|
if (existsSync(p)) {
|
|
1365
1434
|
try {
|
|
1366
1435
|
const pkg = JSON.parse(readFileSync(p, "utf-8"));
|
|
1367
|
-
if (pkg.name
|
|
1436
|
+
if (expectedNames.includes(pkg.name)) return cur;
|
|
1368
1437
|
} catch {
|
|
1369
1438
|
}
|
|
1370
1439
|
}
|
|
@@ -1403,7 +1472,8 @@ var SKILL_INSTALL_TARGETS = {
|
|
|
1403
1472
|
claude: join(homedir(), ".claude", "skills"),
|
|
1404
1473
|
opencode: join(homedir(), ".config", "opencode", "skills"),
|
|
1405
1474
|
openclaw: join(homedir(), ".openclaw", "skills"),
|
|
1406
|
-
hermes: join(homedir(), ".hermes", "skills")
|
|
1475
|
+
hermes: join(homedir(), ".hermes", "skills"),
|
|
1476
|
+
workbuddy: join(homedir(), ".workbuddy", "skills")
|
|
1407
1477
|
};
|
|
1408
1478
|
function skillInstallTargetPath(target) {
|
|
1409
1479
|
return SKILL_INSTALL_TARGETS[target] ?? null;
|
|
@@ -1467,7 +1537,7 @@ function opencodeBundlePath() {
|
|
|
1467
1537
|
}
|
|
1468
1538
|
function resolveOpencodePluginEntry() {
|
|
1469
1539
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
1470
|
-
const pkgRoot = walkUpToPackageJson(here,
|
|
1540
|
+
const pkgRoot = walkUpToPackageJson(here, CTXDB_PACKAGE_NAMES);
|
|
1471
1541
|
if (!pkgRoot) return null;
|
|
1472
1542
|
const candidates = [
|
|
1473
1543
|
join(pkgRoot, "dist", "opencode", "index.js"),
|
|
@@ -1508,7 +1578,7 @@ var LEGACY_MARKER_VALUES = ["@aliyunrds/ctxdb-qoder"];
|
|
|
1508
1578
|
var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
|
|
1509
1579
|
function appendOne(hooks, event, command, agent) {
|
|
1510
1580
|
if (!Array.isArray(hooks[event])) hooks[event] = [];
|
|
1511
|
-
const useExecForm = agent === "claude";
|
|
1581
|
+
const useExecForm = agent === "claude" || agent === "workbuddy";
|
|
1512
1582
|
const cmdRunner = agent === "qoder";
|
|
1513
1583
|
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}`;
|
|
1514
1584
|
const dup = hooks[event].some(
|
|
@@ -1997,6 +2067,7 @@ async function runUpgrade(options = {}) {
|
|
|
1997
2067
|
for (const agent of agents) {
|
|
1998
2068
|
results[agent] = await runSetup({
|
|
1999
2069
|
agent,
|
|
2070
|
+
persistCredential: options.persistCredential,
|
|
2000
2071
|
// Updating local package artifacts must not depend on service
|
|
2001
2072
|
// connectivity. Users can run `ctxdb ping` separately.
|
|
2002
2073
|
validate: false
|
|
@@ -2010,7 +2081,7 @@ async function runUpgrade(options = {}) {
|
|
|
2010
2081
|
var STATUS_HELP = `ctxdb status \u2014 inspect configuration and connectivity
|
|
2011
2082
|
|
|
2012
2083
|
USAGE
|
|
2013
|
-
ctxdb status [--agent
|
|
2084
|
+
ctxdb status [--agent <${AGENT_CHOICES}> | --all] [--json]
|
|
2014
2085
|
|
|
2015
2086
|
FLAGS
|
|
2016
2087
|
--agent <name> Inspect exactly one Agent profile
|
|
@@ -2136,7 +2207,7 @@ async function ping(args) {
|
|
|
2136
2207
|
var HELP = `ctxdb setup \u2014 configure an agent
|
|
2137
2208
|
|
|
2138
2209
|
USAGE
|
|
2139
|
-
ctxdb setup [--agent
|
|
2210
|
+
ctxdb setup [--agent <${SUPPORTED_AGENT_CHOICES}> | --all]
|
|
2140
2211
|
[--api-key=K] [--base-url=URL] [--user-id=ID]
|
|
2141
2212
|
[--no-install-skill] [--no-validate] [--json]
|
|
2142
2213
|
|
|
@@ -2157,7 +2228,7 @@ BEHAVIOR
|
|
|
2157
2228
|
any running Hermes CLI/gateway process to load the hooks.
|
|
2158
2229
|
|
|
2159
2230
|
FLAGS
|
|
2160
|
-
--agent <name> Target agent (
|
|
2231
|
+
--agent <name> Target agent (${SUPPORTED_AGENT_CHOICES})
|
|
2161
2232
|
--all Target every setup-compatible detected Agent
|
|
2162
2233
|
--api-key <key> API key (required on first setup)
|
|
2163
2234
|
--base-url <url> Server URL (default: https://context-database.aliyuncs.com)
|
|
@@ -2200,20 +2271,49 @@ function parseAgent(args) {
|
|
|
2200
2271
|
}
|
|
2201
2272
|
async function setup(args) {
|
|
2202
2273
|
if (args.flags.help || args.flags.h) {
|
|
2203
|
-
process.stdout.write(
|
|
2274
|
+
process.stdout.write(
|
|
2275
|
+
DISTRIBUTION_MANIFEST.capabilities.managedCredentials ? HELP.replace(
|
|
2276
|
+
" [--api-key=K] [--base-url=URL] [--user-id=ID]",
|
|
2277
|
+
" [--user-id=ID]"
|
|
2278
|
+
).replace(
|
|
2279
|
+
" Rerun for a new Agent, to repair integration files, or\n with --api-key <new-key> to replace that Agent's key.\n",
|
|
2280
|
+
" Rerun after ctxdb login to add or repair Agent integration files.\n"
|
|
2281
|
+
).replace(
|
|
2282
|
+
" --api-key <key> API key (required on first setup)\n",
|
|
2283
|
+
""
|
|
2284
|
+
).replace(
|
|
2285
|
+
" --base-url <url> Server URL (default: https://context-database.aliyuncs.com)\n",
|
|
2286
|
+
""
|
|
2287
|
+
) : HELP
|
|
2288
|
+
);
|
|
2204
2289
|
return 0;
|
|
2205
2290
|
}
|
|
2206
2291
|
const json = !!args.flags.json;
|
|
2207
2292
|
const all = args.flags.all === true;
|
|
2293
|
+
const explicitApiKey = typeof args.flags["api-key"] === "string" ? args.flags["api-key"] : void 0;
|
|
2294
|
+
if (DISTRIBUTION_MANIFEST.capabilities.managedCredentials && explicitApiKey) {
|
|
2295
|
+
process.stderr.write(
|
|
2296
|
+
"--api-key is not supported by this distribution; use ctxdb login so the credential is stored in credentials.json\n"
|
|
2297
|
+
);
|
|
2298
|
+
return 2;
|
|
2299
|
+
}
|
|
2300
|
+
if (DISTRIBUTION_MANIFEST.capabilities.managedCredentials && typeof args.flags["base-url"] === "string") {
|
|
2301
|
+
process.stderr.write(
|
|
2302
|
+
"--base-url is not supported by this distribution; use ctxdb login --server so the DATA URL is supplied by the login service\n"
|
|
2303
|
+
);
|
|
2304
|
+
return 2;
|
|
2305
|
+
}
|
|
2306
|
+
const persistCredential = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials;
|
|
2208
2307
|
if (all && args.flags.agent !== void 0) {
|
|
2209
2308
|
process.stderr.write("cannot use --all and --agent together\n");
|
|
2210
2309
|
return 2;
|
|
2211
2310
|
}
|
|
2212
2311
|
if (all) {
|
|
2213
2312
|
const result2 = await runSetupAll({
|
|
2214
|
-
apiKey:
|
|
2313
|
+
apiKey: explicitApiKey,
|
|
2215
2314
|
baseUrl: typeof args.flags["base-url"] === "string" ? args.flags["base-url"] : void 0,
|
|
2216
2315
|
userId: typeof args.flags["user-id"] === "string" ? args.flags["user-id"] : void 0,
|
|
2316
|
+
persistCredential,
|
|
2217
2317
|
installSkill: !args.flags["no-install-skill"],
|
|
2218
2318
|
validate: !args.flags["no-validate"]
|
|
2219
2319
|
});
|
|
@@ -2237,15 +2337,16 @@ ${highlightInlineCode(hint)}
|
|
|
2237
2337
|
const agent = parseAgent(args);
|
|
2238
2338
|
if (agent === "default" && !json && !configuredAgents().includes("default")) {
|
|
2239
2339
|
process.stderr.write(
|
|
2240
|
-
`hint: creating CLI-only "default" agent (no hooks/skills). Use --agent
|
|
2340
|
+
`hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <${SUPPORTED_AGENT_CHOICES}> for full setup.
|
|
2241
2341
|
`
|
|
2242
2342
|
);
|
|
2243
2343
|
}
|
|
2244
2344
|
const result = await runSetup({
|
|
2245
2345
|
agent,
|
|
2246
|
-
apiKey:
|
|
2346
|
+
apiKey: explicitApiKey,
|
|
2247
2347
|
baseUrl: typeof args.flags["base-url"] === "string" ? args.flags["base-url"] : void 0,
|
|
2248
2348
|
userId: typeof args.flags["user-id"] === "string" ? args.flags["user-id"] : void 0,
|
|
2349
|
+
persistCredential,
|
|
2249
2350
|
installSkill: !args.flags["no-install-skill"],
|
|
2250
2351
|
validate: !args.flags["no-validate"]
|
|
2251
2352
|
});
|
|
@@ -2264,14 +2365,15 @@ ${highlightInlineCode(h)}
|
|
|
2264
2365
|
}
|
|
2265
2366
|
|
|
2266
2367
|
// src/cli/teardown.ts
|
|
2368
|
+
var PACKAGE_UNINSTALL_TARGETS2 = DISTRIBUTION_MANIFEST.id === "internal" ? DISTRIBUTION_MANIFEST.packageName : `${PACKAGE_NAME} @aliyunrds/ctxdb-shared`;
|
|
2267
2369
|
var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
|
|
2268
2370
|
|
|
2269
2371
|
USAGE
|
|
2270
|
-
ctxdb uninstall [--agent
|
|
2372
|
+
ctxdb uninstall [--agent <${SUPPORTED_AGENT_CHOICES}>] [--purge-config] [--purge-logs] [--purge-all] [--json]
|
|
2271
2373
|
|
|
2272
2374
|
DEFAULT BEHAVIOR (no --agent, no purge flags)
|
|
2273
|
-
Strips ctxdb hook entries and skill directories for ALL agents
|
|
2274
|
-
|
|
2375
|
+
Strips ctxdb hook entries and skill directories for ALL agents
|
|
2376
|
+
(${SUPPORTED_AGENTS.join(", ")}). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
|
|
2275
2377
|
so you can re-run \`ctxdb setup\` later without losing configuration.
|
|
2276
2378
|
|
|
2277
2379
|
WITH --agent <name>
|
|
@@ -2279,7 +2381,7 @@ WITH --agent <name>
|
|
|
2279
2381
|
Other agents remain untouched.
|
|
2280
2382
|
|
|
2281
2383
|
FLAGS
|
|
2282
|
-
--agent <name> Target a single agent (
|
|
2384
|
+
--agent <name> Target a single agent (${SUPPORTED_AGENT_CHOICES})
|
|
2283
2385
|
--purge-config Also delete ~/.ctxdb/ctxdb.json
|
|
2284
2386
|
--purge-logs Also delete ~/.ctxdb/logs/
|
|
2285
2387
|
--purge-all Delete everything under ~/.ctxdb/
|
|
@@ -2287,7 +2389,7 @@ FLAGS
|
|
|
2287
2389
|
|
|
2288
2390
|
NOTE
|
|
2289
2391
|
Does not run \`npm uninstall\`. After uninstall completes, remove the
|
|
2290
|
-
npm package separately: npm uninstall -g
|
|
2392
|
+
npm package separately: npm uninstall -g ${PACKAGE_UNINSTALL_TARGETS2}
|
|
2291
2393
|
`;
|
|
2292
2394
|
function uninstall(args) {
|
|
2293
2395
|
if (args.flags.help || args.flags.h) {
|
|
@@ -2328,25 +2430,29 @@ ${h}
|
|
|
2328
2430
|
}
|
|
2329
2431
|
|
|
2330
2432
|
// src/cli/upgrade.ts
|
|
2433
|
+
var UPDATE_PACKAGE_NAME = DISTRIBUTION_MANIFEST.packageName || PACKAGE_NAME;
|
|
2434
|
+
var UPDATE_REGISTRY_ARG = DISTRIBUTION_MANIFEST.packageRegistry ? ` --registry ${DISTRIBUTION_MANIFEST.packageRegistry}` : "";
|
|
2331
2435
|
var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
|
|
2332
2436
|
|
|
2333
2437
|
USAGE
|
|
2334
|
-
ctxdb upgrade [--agent
|
|
2438
|
+
ctxdb upgrade [--agent <${SUPPORTED_AGENT_CHOICES}>] [--no-self-update] [--json]
|
|
2335
2439
|
|
|
2336
2440
|
BEHAVIOR
|
|
2337
|
-
Pulls the latest
|
|
2441
|
+
Pulls the latest ${UPDATE_PACKAGE_NAME} package from npm, then re-runs setup
|
|
2338
2442
|
for configured agents using their existing credentials.
|
|
2339
2443
|
|
|
2340
2444
|
Without --agent: sets up all configured built-in agents.
|
|
2341
2445
|
With --agent: sets up only the specified agent.
|
|
2342
2446
|
|
|
2343
2447
|
FLAGS
|
|
2344
|
-
--agent <name> Target a single agent (
|
|
2448
|
+
--agent <name> Target a single agent (${SUPPORTED_AGENT_CHOICES})
|
|
2345
2449
|
--self-update Pull latest package from npm (enabled by default)
|
|
2346
2450
|
--no-self-update Skip the npm package update and only re-run setup
|
|
2347
2451
|
--json Machine-readable JSON output
|
|
2348
2452
|
`;
|
|
2349
|
-
var MANUAL_UPDATE_HINT =
|
|
2453
|
+
var MANUAL_UPDATE_HINT = `Automatic update failed. Please run manually:
|
|
2454
|
+
npm install -g ${UPDATE_PACKAGE_NAME}${UPDATE_REGISTRY_ARG}
|
|
2455
|
+
ctxdb setup`;
|
|
2350
2456
|
function printUpdateFailure(error, json) {
|
|
2351
2457
|
if (json) {
|
|
2352
2458
|
process.stdout.write(JSON.stringify({ ok: false, error, hint: MANUAL_UPDATE_HINT }) + "\n");
|
|
@@ -2392,7 +2498,10 @@ async function upgrade(args) {
|
|
|
2392
2498
|
process.stderr.write("nothing to upgrade for default agent (no hooks or skills)\n");
|
|
2393
2499
|
return 0;
|
|
2394
2500
|
}
|
|
2395
|
-
const result = await runUpgrade({
|
|
2501
|
+
const result = await runUpgrade({
|
|
2502
|
+
agent: agentFlag,
|
|
2503
|
+
persistCredential: !DISTRIBUTION_MANIFEST.capabilities.managedCredentials
|
|
2504
|
+
});
|
|
2396
2505
|
if (result.ok) {
|
|
2397
2506
|
try {
|
|
2398
2507
|
writeInstalledPkgVersion(PACKAGE_VERSION);
|
|
@@ -2421,6 +2530,7 @@ ${h}
|
|
|
2421
2530
|
|
|
2422
2531
|
// src/cli/skill-cli.ts
|
|
2423
2532
|
var SUPPORTED_TARGETS = Object.keys(SKILL_INSTALL_TARGETS);
|
|
2533
|
+
var SUPPORTED_TARGET_CHOICES = SUPPORTED_TARGETS.join("|");
|
|
2424
2534
|
var HELP4 = `ctxdb skill install \u2014 install skill files to an agent's skill directory
|
|
2425
2535
|
|
|
2426
2536
|
USAGE
|
|
@@ -2993,6 +3103,1195 @@ async function debugRecall(args) {
|
|
|
2993
3103
|
return 0;
|
|
2994
3104
|
}
|
|
2995
3105
|
|
|
3106
|
+
// src/cli/promotion-cli.ts
|
|
3107
|
+
var GENERATE_USAGE = "usage: ctxdb memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--agent=<name>] [--json]";
|
|
3108
|
+
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]";
|
|
3109
|
+
var GET_USAGE = "usage: ctxdb memory promotion get <promotion-id> [--raw] [--agent=<name>] [--json]";
|
|
3110
|
+
var APPROVE_USAGE = "usage: ctxdb memory promotion approve --promotion-id=<id> --knowledge-base-id=<kb-id> [--agent=<name>] [--json]";
|
|
3111
|
+
var REJECT_USAGE = "usage: ctxdb memory promotion reject --promotion-id=<id> [--reason=<text>] [--agent=<name>] [--json]";
|
|
3112
|
+
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]";
|
|
3113
|
+
var BATCH_DELETE_USAGE = "usage: ctxdb memory promotion batch-delete --ids=<id1,id2,...> [--agent=<name>] [--json]";
|
|
3114
|
+
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]`;
|
|
3115
|
+
var TEMPLATE_CREATE_USAGE = `usage: ctxdb memory promotion template create --name=<name> --sections-json='[{"title":"...","guidance":"...","level":1}]' [--is-default] [--agent=<name>] [--json]`;
|
|
3116
|
+
var TEMPLATE_GET_USAGE = "usage: ctxdb memory promotion template get <template-id> [--agent=<name>] [--json]";
|
|
3117
|
+
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]`;
|
|
3118
|
+
var TEMPLATE_DELETE_USAGE = "usage: ctxdb memory promotion template delete <template-id> [--agent=<name>] [--json]";
|
|
3119
|
+
var TEMPLATE_SET_DEFAULT_USAGE = "usage: ctxdb memory promotion template set-default <template-id> [--agent=<name>] [--json]";
|
|
3120
|
+
var TEMPLATE_CLEAR_DEFAULT_USAGE = "usage: ctxdb memory promotion template clear-default [--agent=<name>] [--json]";
|
|
3121
|
+
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]";
|
|
3122
|
+
var PROMOTION_STATUSES = [
|
|
3123
|
+
"generating",
|
|
3124
|
+
"pending_review",
|
|
3125
|
+
"approving",
|
|
3126
|
+
"approved",
|
|
3127
|
+
"rejected",
|
|
3128
|
+
"generation_failed"
|
|
3129
|
+
];
|
|
3130
|
+
var BATCH_ACTIONS = ["approve", "reject"];
|
|
3131
|
+
var BATCH_MAX_ITEMS = 50;
|
|
3132
|
+
var TEMPLATE_SECTIONS_MAX = 15;
|
|
3133
|
+
var PROMOTION_LIST_FIELDS = [
|
|
3134
|
+
"promotion_id",
|
|
3135
|
+
"topic",
|
|
3136
|
+
"title",
|
|
3137
|
+
"status",
|
|
3138
|
+
"source_type",
|
|
3139
|
+
"quality_score",
|
|
3140
|
+
"auto_ingested",
|
|
3141
|
+
"target_kb_id",
|
|
3142
|
+
"updated_at"
|
|
3143
|
+
];
|
|
3144
|
+
var PROMOTION_DETAIL_FIELDS = [
|
|
3145
|
+
"promotion_id",
|
|
3146
|
+
"topic",
|
|
3147
|
+
"title",
|
|
3148
|
+
"content",
|
|
3149
|
+
"whats_new",
|
|
3150
|
+
"status",
|
|
3151
|
+
"source_type",
|
|
3152
|
+
"quality_score",
|
|
3153
|
+
"auto_ingested",
|
|
3154
|
+
"doubts",
|
|
3155
|
+
"doubt_resolutions",
|
|
3156
|
+
"memory_count",
|
|
3157
|
+
"rejection_reason",
|
|
3158
|
+
"target_kb_id",
|
|
3159
|
+
"target_document_id",
|
|
3160
|
+
"last_error",
|
|
3161
|
+
"reviewed_by",
|
|
3162
|
+
"reviewed_at",
|
|
3163
|
+
"created_at",
|
|
3164
|
+
"updated_at"
|
|
3165
|
+
];
|
|
3166
|
+
async function promotionGenerate(args) {
|
|
3167
|
+
const topic = args.positional[0];
|
|
3168
|
+
if (!topic || topic.trim() === "") fail(GENERATE_USAGE, 2);
|
|
3169
|
+
const ctx = buildContext(args);
|
|
3170
|
+
const body = { topic };
|
|
3171
|
+
const kbId = stringFlag(args, "knowledge-base-id");
|
|
3172
|
+
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
3173
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/generate", body);
|
|
3174
|
+
emit(args, resp, "memory promotion generate", (data) => data);
|
|
3175
|
+
return 0;
|
|
3176
|
+
}
|
|
3177
|
+
async function promotionList(args) {
|
|
3178
|
+
const params = {};
|
|
3179
|
+
const status2 = stringFlag(args, "status");
|
|
3180
|
+
if (status2 !== void 0) {
|
|
3181
|
+
if (!PROMOTION_STATUSES.includes(status2)) {
|
|
3182
|
+
fail(`invalid --status: ${status2} (expected one of ${PROMOTION_STATUSES.join("|")})
|
|
3183
|
+
${LIST_USAGE}`, 2);
|
|
3184
|
+
}
|
|
3185
|
+
params.status = status2;
|
|
3186
|
+
}
|
|
3187
|
+
const page = intFlag(args, "page", LIST_USAGE);
|
|
3188
|
+
if (page !== void 0) params.page = page;
|
|
3189
|
+
const pageSize = intFlag(args, "page-size", LIST_USAGE);
|
|
3190
|
+
if (pageSize !== void 0) params.page_size = pageSize;
|
|
3191
|
+
const sortBy = stringFlag(args, "sort-by");
|
|
3192
|
+
if (sortBy !== void 0) {
|
|
3193
|
+
if (sortBy !== "quality_score") {
|
|
3194
|
+
fail(`invalid --sort-by: ${sortBy} (expected quality_score)
|
|
3195
|
+
${LIST_USAGE}`, 2);
|
|
3196
|
+
}
|
|
3197
|
+
params.sort_by = sortBy;
|
|
3198
|
+
}
|
|
3199
|
+
const sortOrder = stringFlag(args, "sort-order");
|
|
3200
|
+
if (sortOrder !== void 0) {
|
|
3201
|
+
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
3202
|
+
fail(`invalid --sort-order: ${sortOrder} (expected asc|desc)
|
|
3203
|
+
${LIST_USAGE}`, 2);
|
|
3204
|
+
}
|
|
3205
|
+
params.sort_order = sortOrder;
|
|
3206
|
+
}
|
|
3207
|
+
const ctx = buildContext(args);
|
|
3208
|
+
const resp = await ctx.client.get("/v1/memory_promotions", params);
|
|
3209
|
+
emit(args, resp, "memory promotion list", compactList);
|
|
3210
|
+
return 0;
|
|
3211
|
+
}
|
|
3212
|
+
async function promotionGet(args) {
|
|
3213
|
+
const promotionId = args.positional[0];
|
|
3214
|
+
if (!promotionId || promotionId.trim() === "") fail(GET_USAGE, 2);
|
|
3215
|
+
const ctx = buildContext(args);
|
|
3216
|
+
const resp = await ctx.client.get("/v1/memory_promotions/detail", {
|
|
3217
|
+
promotion_id: promotionId
|
|
3218
|
+
});
|
|
3219
|
+
emit(args, resp, "memory promotion get", compactDetail);
|
|
3220
|
+
return 0;
|
|
3221
|
+
}
|
|
3222
|
+
async function promotionApprove(args) {
|
|
3223
|
+
const promotionId = requireIdFlag(args, "promotion-id", APPROVE_USAGE);
|
|
3224
|
+
const kbId = requireIdFlag(args, "knowledge-base-id", APPROVE_USAGE);
|
|
3225
|
+
const ctx = buildContext(args);
|
|
3226
|
+
const body = {
|
|
3227
|
+
promotion_id: promotionId,
|
|
3228
|
+
knowledge_base_id: kbId
|
|
3229
|
+
};
|
|
3230
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/approve", body);
|
|
3231
|
+
emit(args, resp, "memory promotion approve", (data) => data);
|
|
3232
|
+
return 0;
|
|
3233
|
+
}
|
|
3234
|
+
async function promotionReject(args) {
|
|
3235
|
+
const promotionId = requireIdFlag(args, "promotion-id", REJECT_USAGE);
|
|
3236
|
+
const ctx = buildContext(args);
|
|
3237
|
+
const body = { promotion_id: promotionId };
|
|
3238
|
+
const reason = stringFlag(args, "reason");
|
|
3239
|
+
if (reason !== void 0) body.reason = reason;
|
|
3240
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/reject", body);
|
|
3241
|
+
emit(args, resp, "memory promotion reject", (data) => data);
|
|
3242
|
+
return 0;
|
|
3243
|
+
}
|
|
3244
|
+
async function promotionBatchAction(args) {
|
|
3245
|
+
const action = stringFlag(args, "action");
|
|
3246
|
+
if (action === void 0 || !BATCH_ACTIONS.includes(action)) {
|
|
3247
|
+
fail(`--action is required and must be one of ${BATCH_ACTIONS.join("|")}
|
|
3248
|
+
${BATCH_ACTION_USAGE}`, 2);
|
|
3249
|
+
}
|
|
3250
|
+
const ids = parseIdsFlag(args, BATCH_ACTION_USAGE);
|
|
3251
|
+
const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag(args, "knowledge-base-id");
|
|
3252
|
+
const ctx = buildContext(args);
|
|
3253
|
+
const body = { action, ids };
|
|
3254
|
+
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
3255
|
+
const reason = stringFlag(args, "reason");
|
|
3256
|
+
if (reason !== void 0) body.reason = reason;
|
|
3257
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/batch", body);
|
|
3258
|
+
const data = emit(args, resp, "memory promotion batch-action", (data2) => data2);
|
|
3259
|
+
return batchExitCode(data);
|
|
3260
|
+
}
|
|
3261
|
+
async function promotionBatchDelete(args) {
|
|
3262
|
+
const ids = parseIdsFlag(args, BATCH_DELETE_USAGE);
|
|
3263
|
+
const ctx = buildContext(args);
|
|
3264
|
+
const resp = await ctx.client.deleteJson(
|
|
3265
|
+
"/v1/memory_promotions/batch",
|
|
3266
|
+
{ ids }
|
|
3267
|
+
);
|
|
3268
|
+
const data = emit(args, resp, "memory promotion batch-delete", (data2) => data2);
|
|
3269
|
+
return batchExitCode(data);
|
|
3270
|
+
}
|
|
3271
|
+
async function promotionResolveDoubts(args) {
|
|
3272
|
+
const promotionId = requireIdFlag(args, "promotion-id", RESOLVE_DOUBTS_USAGE);
|
|
3273
|
+
const resolutionsRaw = args.flags["resolutions-json"];
|
|
3274
|
+
if (typeof resolutionsRaw !== "string" || resolutionsRaw.trim() === "") {
|
|
3275
|
+
fail(`--resolutions-json is required
|
|
3276
|
+
${RESOLVE_DOUBTS_USAGE}`, 2);
|
|
3277
|
+
}
|
|
3278
|
+
const resolutions = parseResolutions(resolutionsRaw, RESOLVE_DOUBTS_USAGE);
|
|
3279
|
+
const ctx = buildContext(args);
|
|
3280
|
+
const body = {
|
|
3281
|
+
promotion_id: promotionId,
|
|
3282
|
+
doubt_resolutions: resolutions
|
|
3283
|
+
};
|
|
3284
|
+
const title = stringFlag(args, "title");
|
|
3285
|
+
if (title !== void 0) body.title = title;
|
|
3286
|
+
const content = stringFlag(args, "content");
|
|
3287
|
+
if (content !== void 0) body.content = content;
|
|
3288
|
+
const resp = await ctx.client.postJson(
|
|
3289
|
+
"/v1/memory_promotions/resolve_doubts",
|
|
3290
|
+
body
|
|
3291
|
+
);
|
|
3292
|
+
emit(args, resp, "memory promotion resolve-doubts", (data) => data);
|
|
3293
|
+
return 0;
|
|
3294
|
+
}
|
|
3295
|
+
async function promotionTemplateList(args) {
|
|
3296
|
+
const ctx = buildContext(args);
|
|
3297
|
+
const resp = await ctx.client.get("/v1/memory_promotions/templates");
|
|
3298
|
+
emit(args, resp, "memory promotion template list", compactTemplateList);
|
|
3299
|
+
return 0;
|
|
3300
|
+
}
|
|
3301
|
+
async function promotionTemplateCreate(args) {
|
|
3302
|
+
const body = buildTemplateUpsertBody(args, TEMPLATE_CREATE_USAGE);
|
|
3303
|
+
const ctx = buildContext(args);
|
|
3304
|
+
const resp = await ctx.client.postJson("/v1/memory_promotions/templates", body);
|
|
3305
|
+
emit(args, resp, "memory promotion template create", (data) => data);
|
|
3306
|
+
return 0;
|
|
3307
|
+
}
|
|
3308
|
+
async function promotionTemplateGet(args) {
|
|
3309
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_GET_USAGE);
|
|
3310
|
+
const ctx = buildContext(args);
|
|
3311
|
+
const resp = await ctx.client.get(
|
|
3312
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`
|
|
3313
|
+
);
|
|
3314
|
+
emit(args, resp, "memory promotion template get", (data) => data);
|
|
3315
|
+
return 0;
|
|
3316
|
+
}
|
|
3317
|
+
async function promotionTemplateUpdate(args) {
|
|
3318
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_UPDATE_USAGE);
|
|
3319
|
+
const body = buildTemplateUpsertBody(args, TEMPLATE_UPDATE_USAGE);
|
|
3320
|
+
const ctx = buildContext(args);
|
|
3321
|
+
const resp = await ctx.client.putJson(
|
|
3322
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`,
|
|
3323
|
+
body
|
|
3324
|
+
);
|
|
3325
|
+
emit(args, resp, "memory promotion template update", (data) => data);
|
|
3326
|
+
return 0;
|
|
3327
|
+
}
|
|
3328
|
+
async function promotionTemplateDelete(args) {
|
|
3329
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_DELETE_USAGE);
|
|
3330
|
+
const ctx = buildContext(args);
|
|
3331
|
+
const resp = await ctx.client.delete(
|
|
3332
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}`
|
|
3333
|
+
);
|
|
3334
|
+
emit(args, resp, "memory promotion template delete", (data) => data);
|
|
3335
|
+
return 0;
|
|
3336
|
+
}
|
|
3337
|
+
async function promotionTemplateSetDefault(args) {
|
|
3338
|
+
const templateId = requireTemplateIdPositional(args, TEMPLATE_SET_DEFAULT_USAGE);
|
|
3339
|
+
const ctx = buildContext(args);
|
|
3340
|
+
const resp = await ctx.client.postJson(
|
|
3341
|
+
`/v1/memory_promotions/templates/${encodeURIComponent(templateId)}/set_default`
|
|
3342
|
+
);
|
|
3343
|
+
emit(args, resp, "memory promotion template set-default", (data) => data);
|
|
3344
|
+
return 0;
|
|
3345
|
+
}
|
|
3346
|
+
async function promotionTemplateClearDefault(args) {
|
|
3347
|
+
if (args.positional.length > 0) fail(TEMPLATE_CLEAR_DEFAULT_USAGE, 2);
|
|
3348
|
+
const ctx = buildContext(args);
|
|
3349
|
+
const resp = await ctx.client.delete("/v1/memory_promotions/templates/default");
|
|
3350
|
+
emit(args, resp, "memory promotion template clear-default", (data) => data);
|
|
3351
|
+
return 0;
|
|
3352
|
+
}
|
|
3353
|
+
async function promotionSettingsGet(args) {
|
|
3354
|
+
const ctx = buildContext(args);
|
|
3355
|
+
const resp = await ctx.client.get("/v1/memory_promotions/settings");
|
|
3356
|
+
emit(args, resp, "memory promotion settings get", (data) => data);
|
|
3357
|
+
return 0;
|
|
3358
|
+
}
|
|
3359
|
+
async function promotionSettingsUpdate(args) {
|
|
3360
|
+
const body = {};
|
|
3361
|
+
const enabled = stringFlag(args, "auto-ingest-enabled");
|
|
3362
|
+
if (enabled !== void 0) {
|
|
3363
|
+
if (enabled !== "true" && enabled !== "false") {
|
|
3364
|
+
fail(`invalid --auto-ingest-enabled: ${enabled} (expected true|false)
|
|
3365
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3366
|
+
}
|
|
3367
|
+
body.auto_ingest_enabled = enabled === "true";
|
|
3368
|
+
}
|
|
3369
|
+
const threshold = stringFlag(args, "auto-ingest-threshold");
|
|
3370
|
+
if (threshold !== void 0) {
|
|
3371
|
+
const n = Number(threshold);
|
|
3372
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
3373
|
+
fail(`invalid --auto-ingest-threshold: ${threshold} (expected a number in [0,1])
|
|
3374
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3375
|
+
}
|
|
3376
|
+
body.auto_ingest_threshold = n;
|
|
3377
|
+
}
|
|
3378
|
+
const defaultKbId = stringFlag(args, "default-kb-id");
|
|
3379
|
+
const clearDefaultKb = args.flags["clear-default-kb"] === true;
|
|
3380
|
+
if (defaultKbId !== void 0 && clearDefaultKb) {
|
|
3381
|
+
fail(`cannot use --default-kb-id and --clear-default-kb together
|
|
3382
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3383
|
+
}
|
|
3384
|
+
if (defaultKbId !== void 0) body.default_kb_id = defaultKbId;
|
|
3385
|
+
if (clearDefaultKb) body.default_kb_id = "";
|
|
3386
|
+
if (Object.keys(body).length === 0) {
|
|
3387
|
+
fail(`at least one setting flag is required
|
|
3388
|
+
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
3389
|
+
}
|
|
3390
|
+
const ctx = buildContext(args);
|
|
3391
|
+
const resp = await ctx.client.putJson("/v1/memory_promotions/settings", body);
|
|
3392
|
+
emit(args, resp, "memory promotion settings update", (data) => data);
|
|
3393
|
+
return 0;
|
|
3394
|
+
}
|
|
3395
|
+
function stringFlag(args, name) {
|
|
3396
|
+
const value = args.flags[name];
|
|
3397
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3398
|
+
}
|
|
3399
|
+
function requireIdFlag(args, name, usage) {
|
|
3400
|
+
const value = stringFlag(args, name);
|
|
3401
|
+
if (value === void 0) fail(`--${name} is required
|
|
3402
|
+
${usage}`, 2);
|
|
3403
|
+
return value;
|
|
3404
|
+
}
|
|
3405
|
+
function requireTemplateIdPositional(args, usage) {
|
|
3406
|
+
const templateId = args.positional[0];
|
|
3407
|
+
if (!templateId || templateId.trim() === "") fail(usage, 2);
|
|
3408
|
+
return templateId;
|
|
3409
|
+
}
|
|
3410
|
+
function intFlag(args, name, usage) {
|
|
3411
|
+
const raw = stringFlag(args, name);
|
|
3412
|
+
if (raw === void 0) return void 0;
|
|
3413
|
+
const n = Number(raw);
|
|
3414
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
3415
|
+
fail(`invalid --${name}: ${raw} (expected a positive integer)
|
|
3416
|
+
${usage}`, 2);
|
|
3417
|
+
}
|
|
3418
|
+
return n;
|
|
3419
|
+
}
|
|
3420
|
+
function parseIdsFlag(args, usage) {
|
|
3421
|
+
const raw = stringFlag(args, "ids");
|
|
3422
|
+
if (raw === void 0) fail(`--ids is required (comma-separated)
|
|
3423
|
+
${usage}`, 2);
|
|
3424
|
+
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3425
|
+
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3426
|
+
${usage}`, 2);
|
|
3427
|
+
if (ids.length > BATCH_MAX_ITEMS) {
|
|
3428
|
+
fail(`--ids accepts at most ${BATCH_MAX_ITEMS} items per request (got ${ids.length})
|
|
3429
|
+
${usage}`, 2);
|
|
3430
|
+
}
|
|
3431
|
+
return ids;
|
|
3432
|
+
}
|
|
3433
|
+
function parseResolutions(raw, usage) {
|
|
3434
|
+
let parsed;
|
|
3435
|
+
try {
|
|
3436
|
+
parsed = JSON.parse(raw);
|
|
3437
|
+
} catch {
|
|
3438
|
+
fail(`--resolutions-json is not valid JSON
|
|
3439
|
+
${usage}`, 2);
|
|
3440
|
+
}
|
|
3441
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3442
|
+
fail(`--resolutions-json must be a non-empty array
|
|
3443
|
+
${usage}`, 2);
|
|
3444
|
+
}
|
|
3445
|
+
return parsed.map((entry, i) => {
|
|
3446
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3447
|
+
fail(`--resolutions-json[${i}] must be an object
|
|
3448
|
+
${usage}`, 2);
|
|
3449
|
+
}
|
|
3450
|
+
const e = entry;
|
|
3451
|
+
if (typeof e.index !== "number" || !Number.isInteger(e.index) || e.index < 0) {
|
|
3452
|
+
fail(`--resolutions-json[${i}].index must be a non-negative integer
|
|
3453
|
+
${usage}`, 2);
|
|
3454
|
+
}
|
|
3455
|
+
if (typeof e.choice !== "string" || e.choice.trim() === "") {
|
|
3456
|
+
fail(`--resolutions-json[${i}].choice must be a non-empty string
|
|
3457
|
+
${usage}`, 2);
|
|
3458
|
+
}
|
|
3459
|
+
const out = { index: e.index, choice: e.choice };
|
|
3460
|
+
if (e.comment !== void 0 && e.comment !== null) {
|
|
3461
|
+
if (typeof e.comment !== "string") {
|
|
3462
|
+
fail(`--resolutions-json[${i}].comment must be a string
|
|
3463
|
+
${usage}`, 2);
|
|
3464
|
+
}
|
|
3465
|
+
out.comment = e.comment;
|
|
3466
|
+
}
|
|
3467
|
+
return out;
|
|
3468
|
+
});
|
|
3469
|
+
}
|
|
3470
|
+
function buildTemplateUpsertBody(args, usage) {
|
|
3471
|
+
const name = stringFlag(args, "name");
|
|
3472
|
+
if (name === void 0) fail(`--name is required
|
|
3473
|
+
${usage}`, 2);
|
|
3474
|
+
const sectionsRaw = args.flags["sections-json"];
|
|
3475
|
+
if (typeof sectionsRaw !== "string" || sectionsRaw.trim() === "") {
|
|
3476
|
+
fail(`--sections-json is required
|
|
3477
|
+
${usage}`, 2);
|
|
3478
|
+
}
|
|
3479
|
+
const sections = parseSections(sectionsRaw, usage);
|
|
3480
|
+
const body = { name, sections };
|
|
3481
|
+
if (args.flags["is-default"] === true) body.is_default = true;
|
|
3482
|
+
return body;
|
|
3483
|
+
}
|
|
3484
|
+
function parseSections(raw, usage) {
|
|
3485
|
+
let parsed;
|
|
3486
|
+
try {
|
|
3487
|
+
parsed = JSON.parse(raw);
|
|
3488
|
+
} catch {
|
|
3489
|
+
fail(`--sections-json is not valid JSON
|
|
3490
|
+
${usage}`, 2);
|
|
3491
|
+
}
|
|
3492
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3493
|
+
fail(`--sections-json must be a non-empty array
|
|
3494
|
+
${usage}`, 2);
|
|
3495
|
+
}
|
|
3496
|
+
if (parsed.length > TEMPLATE_SECTIONS_MAX) {
|
|
3497
|
+
fail(`--sections-json accepts at most ${TEMPLATE_SECTIONS_MAX} sections (got ${parsed.length})
|
|
3498
|
+
${usage}`, 2);
|
|
3499
|
+
}
|
|
3500
|
+
return parsed.map((entry, i) => {
|
|
3501
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3502
|
+
fail(`--sections-json[${i}] must be an object
|
|
3503
|
+
${usage}`, 2);
|
|
3504
|
+
}
|
|
3505
|
+
const e = entry;
|
|
3506
|
+
if (typeof e.title !== "string" || e.title.trim() === "") {
|
|
3507
|
+
fail(`--sections-json[${i}].title must be a non-empty string
|
|
3508
|
+
${usage}`, 2);
|
|
3509
|
+
}
|
|
3510
|
+
if (/[\r\n]/.test(e.title.trim())) {
|
|
3511
|
+
fail(`--sections-json[${i}].title must not contain line breaks
|
|
3512
|
+
${usage}`, 2);
|
|
3513
|
+
}
|
|
3514
|
+
if (typeof e.guidance !== "string" || e.guidance.trim() === "") {
|
|
3515
|
+
fail(`--sections-json[${i}].guidance must be a non-empty string
|
|
3516
|
+
${usage}`, 2);
|
|
3517
|
+
}
|
|
3518
|
+
const out = { title: e.title, guidance: e.guidance };
|
|
3519
|
+
if (e.level !== void 0 && e.level !== null) {
|
|
3520
|
+
if (typeof e.level !== "number" || !Number.isInteger(e.level) || e.level < 1 || e.level > 3) {
|
|
3521
|
+
fail(`--sections-json[${i}].level must be an integer in 1-3
|
|
3522
|
+
${usage}`, 2);
|
|
3523
|
+
}
|
|
3524
|
+
out.level = e.level;
|
|
3525
|
+
}
|
|
3526
|
+
return out;
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
function unwrapBox(response, operation) {
|
|
3530
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
3531
|
+
throw new CtxdbError(`${operation} returned an invalid response`);
|
|
3532
|
+
}
|
|
3533
|
+
const box = response;
|
|
3534
|
+
const errorCode = box.errorCode;
|
|
3535
|
+
const failed = typeof errorCode === "number" && errorCode !== 0 || typeof errorCode === "string" && errorCode.trim() !== "" && errorCode.trim() !== "0";
|
|
3536
|
+
if (failed) {
|
|
3537
|
+
const message = typeof box.errorMessage === "string" && box.errorMessage ? box.errorMessage : `errorCode=${box.errorCode}`;
|
|
3538
|
+
throw new CtxdbError(`${operation} failed: ${message}`);
|
|
3539
|
+
}
|
|
3540
|
+
return Object.hasOwn(box, "data") ? box.data : response;
|
|
3541
|
+
}
|
|
3542
|
+
function emit(args, response, operation, compact) {
|
|
3543
|
+
const data = unwrapBox(response, operation);
|
|
3544
|
+
const out = args.flags.raw ? response : compact(data);
|
|
3545
|
+
printResult(out, !!args.flags.json);
|
|
3546
|
+
return data;
|
|
3547
|
+
}
|
|
3548
|
+
function batchExitCode(data) {
|
|
3549
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return 0;
|
|
3550
|
+
const failed = data.failed;
|
|
3551
|
+
return typeof failed === "number" && failed > 0 ? 1 : 0;
|
|
3552
|
+
}
|
|
3553
|
+
function pickFields(source, fields) {
|
|
3554
|
+
const out = {};
|
|
3555
|
+
for (const key of fields) {
|
|
3556
|
+
const value = source[key];
|
|
3557
|
+
if (value !== void 0 && value !== null) out[key] = value;
|
|
3558
|
+
}
|
|
3559
|
+
return out;
|
|
3560
|
+
}
|
|
3561
|
+
function compactList(data) {
|
|
3562
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
3563
|
+
const d = data;
|
|
3564
|
+
const rows = Array.isArray(d.promotions) ? d.promotions : [];
|
|
3565
|
+
const out = {
|
|
3566
|
+
promotions: rows.filter((p) => p && typeof p === "object" && !Array.isArray(p)).map((p) => pickFields(p, PROMOTION_LIST_FIELDS))
|
|
3567
|
+
};
|
|
3568
|
+
for (const key of ["total", "page", "page_size"]) {
|
|
3569
|
+
if (typeof d[key] === "number") out[key] = d[key];
|
|
3570
|
+
}
|
|
3571
|
+
return out;
|
|
3572
|
+
}
|
|
3573
|
+
function compactDetail(data) {
|
|
3574
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
3575
|
+
return pickFields(data, PROMOTION_DETAIL_FIELDS);
|
|
3576
|
+
}
|
|
3577
|
+
function compactTemplateList(data) {
|
|
3578
|
+
if (!Array.isArray(data)) return data;
|
|
3579
|
+
return data.filter((t) => t && typeof t === "object" && !Array.isArray(t)).map((t) => {
|
|
3580
|
+
const template = t;
|
|
3581
|
+
const out = pickFields(template, [
|
|
3582
|
+
"id",
|
|
3583
|
+
"name",
|
|
3584
|
+
"is_default",
|
|
3585
|
+
"updatedAt"
|
|
3586
|
+
]);
|
|
3587
|
+
if (Array.isArray(template.sections)) {
|
|
3588
|
+
out.sections = template.sections.filter((s) => s && typeof s === "object" && !Array.isArray(s)).map(
|
|
3589
|
+
(s) => pickFields(s, ["level", "title"])
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
return out;
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
// src/cli/review-cli.ts
|
|
3597
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3598
|
+
|
|
3599
|
+
// src/lib/review.ts
|
|
3600
|
+
function unwrapReviewData(response, operation) {
|
|
3601
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
3602
|
+
throw new CtxdbError(`${operation} returned an invalid response`);
|
|
3603
|
+
}
|
|
3604
|
+
const box = response;
|
|
3605
|
+
const errorCode = box.errorCode;
|
|
3606
|
+
const failed = typeof errorCode === "number" && errorCode !== 0 || typeof errorCode === "string" && errorCode.trim() !== "" && errorCode.trim() !== "0";
|
|
3607
|
+
if (failed) {
|
|
3608
|
+
const message = typeof box.errorMessage === "string" && box.errorMessage ? box.errorMessage : `errorCode=${errorCode}`;
|
|
3609
|
+
throw new CtxdbError(`${operation} failed: ${message}`);
|
|
3610
|
+
}
|
|
3611
|
+
return box.data ?? null;
|
|
3612
|
+
}
|
|
3613
|
+
function asRecord(value) {
|
|
3614
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3615
|
+
}
|
|
3616
|
+
function pickString(record, field) {
|
|
3617
|
+
const value = record[field];
|
|
3618
|
+
return typeof value === "string" ? value : "";
|
|
3619
|
+
}
|
|
3620
|
+
function pickNumber(record, field) {
|
|
3621
|
+
const value = record[field];
|
|
3622
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3623
|
+
}
|
|
3624
|
+
function pickBoolean(record, field) {
|
|
3625
|
+
const value = record[field];
|
|
3626
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3627
|
+
}
|
|
3628
|
+
function parseEmbeddedJson(value) {
|
|
3629
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
3630
|
+
try {
|
|
3631
|
+
return JSON.parse(value);
|
|
3632
|
+
} catch {
|
|
3633
|
+
return void 0;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
function countArrayEntries(value) {
|
|
3637
|
+
return Array.isArray(value) ? value.length : void 0;
|
|
3638
|
+
}
|
|
3639
|
+
function readReviewListData(data) {
|
|
3640
|
+
const record = asRecord(data);
|
|
3641
|
+
return {
|
|
3642
|
+
reviews: record && Array.isArray(record.reviews) ? record.reviews : [],
|
|
3643
|
+
total: record ? pickNumber(record, "total") : void 0,
|
|
3644
|
+
page: record ? pickNumber(record, "page") : void 0,
|
|
3645
|
+
page_size: record ? pickNumber(record, "page_size") : void 0
|
|
3646
|
+
};
|
|
3647
|
+
}
|
|
3648
|
+
function minimalReviewListItem(raw) {
|
|
3649
|
+
const r = asRecord(raw) ?? {};
|
|
3650
|
+
const out = {
|
|
3651
|
+
review_id: pickString(r, "review_id"),
|
|
3652
|
+
document_name: pickString(r, "document_name"),
|
|
3653
|
+
decision: pickString(r, "decision"),
|
|
3654
|
+
review_status: pickString(r, "review_status")
|
|
3655
|
+
};
|
|
3656
|
+
const score = pickNumber(r, "confidence_score");
|
|
3657
|
+
if (score !== void 0) out.confidence_score = score;
|
|
3658
|
+
const canApproveReview = pickBoolean(r, "can_approve_review");
|
|
3659
|
+
if (canApproveReview !== void 0) out.can_approve_review = canApproveReview;
|
|
3660
|
+
const createdAt = pickString(r, "created_at");
|
|
3661
|
+
if (createdAt) out.created_at = createdAt;
|
|
3662
|
+
return out;
|
|
3663
|
+
}
|
|
3664
|
+
function compactReviewListItem(raw) {
|
|
3665
|
+
const r = asRecord(raw) ?? {};
|
|
3666
|
+
const out = {
|
|
3667
|
+
...minimalReviewListItem(raw),
|
|
3668
|
+
document_id: pickString(r, "document_id"),
|
|
3669
|
+
knowledge_base_id: pickString(r, "knowledge_base_id")
|
|
3670
|
+
};
|
|
3671
|
+
const hasConflicts = pickBoolean(r, "has_conflicts");
|
|
3672
|
+
if (hasConflicts !== void 0) out.has_conflicts = hasConflicts;
|
|
3673
|
+
const hasContradictions = pickBoolean(r, "has_contradictions");
|
|
3674
|
+
if (hasContradictions !== void 0) out.has_contradictions = hasContradictions;
|
|
3675
|
+
const rewriteNeeded = pickBoolean(r, "rewrite_needed");
|
|
3676
|
+
if (rewriteNeeded !== void 0) out.rewrite_needed = rewriteNeeded;
|
|
3677
|
+
const humanAction = pickString(r, "human_action");
|
|
3678
|
+
if (humanAction) out.human_action = humanAction;
|
|
3679
|
+
return out;
|
|
3680
|
+
}
|
|
3681
|
+
function projectReviewList(data, projectItem) {
|
|
3682
|
+
const { reviews, total, page, page_size } = readReviewListData(data);
|
|
3683
|
+
const out = { reviews: reviews.map(projectItem) };
|
|
3684
|
+
if (total !== void 0) out.total = total;
|
|
3685
|
+
if (page !== void 0) out.page = page;
|
|
3686
|
+
if (page_size !== void 0) out.page_size = page_size;
|
|
3687
|
+
return out;
|
|
3688
|
+
}
|
|
3689
|
+
function minimalReviewListResponse(data) {
|
|
3690
|
+
return projectReviewList(data, minimalReviewListItem);
|
|
3691
|
+
}
|
|
3692
|
+
function compactReviewListResponse(data) {
|
|
3693
|
+
return projectReviewList(data, compactReviewListItem);
|
|
3694
|
+
}
|
|
3695
|
+
function minimalReviewDetail(raw) {
|
|
3696
|
+
const r = asRecord(raw);
|
|
3697
|
+
if (!r) return {};
|
|
3698
|
+
const out = {
|
|
3699
|
+
review_id: pickString(r, "review_id"),
|
|
3700
|
+
document_name: pickString(r, "document_name"),
|
|
3701
|
+
decision: pickString(r, "decision"),
|
|
3702
|
+
review_status: pickString(r, "review_status")
|
|
3703
|
+
};
|
|
3704
|
+
for (const field of [
|
|
3705
|
+
"document_id",
|
|
3706
|
+
"knowledge_base_id",
|
|
3707
|
+
"decision_reason",
|
|
3708
|
+
"rewrite_strategy",
|
|
3709
|
+
"human_action",
|
|
3710
|
+
"created_at"
|
|
3711
|
+
]) {
|
|
3712
|
+
const value = pickString(r, field);
|
|
3713
|
+
if (value) out[field] = value;
|
|
3714
|
+
}
|
|
3715
|
+
for (const field of [
|
|
3716
|
+
"confidence_score",
|
|
3717
|
+
"logic_score",
|
|
3718
|
+
"noise_score",
|
|
3719
|
+
"contradiction_score"
|
|
3720
|
+
]) {
|
|
3721
|
+
const value = pickNumber(r, field);
|
|
3722
|
+
if (value !== void 0) out[field] = value;
|
|
3723
|
+
}
|
|
3724
|
+
for (const field of ["has_contradictions", "rewrite_needed"]) {
|
|
3725
|
+
const value = pickBoolean(r, field);
|
|
3726
|
+
if (value !== void 0) out[field] = value;
|
|
3727
|
+
}
|
|
3728
|
+
const conflictCount = countArrayEntries(parseEmbeddedJson(r.conflicts));
|
|
3729
|
+
if (conflictCount !== void 0) out.conflict_count = conflictCount;
|
|
3730
|
+
const originalContent = asRecord(r.original_content);
|
|
3731
|
+
if (originalContent) {
|
|
3732
|
+
const contentPage = pickNumber(originalContent, "page");
|
|
3733
|
+
if (contentPage !== void 0) out.content_page = contentPage;
|
|
3734
|
+
const totalPages = pickNumber(originalContent, "total_pages");
|
|
3735
|
+
if (totalPages !== void 0) out.content_total_pages = totalPages;
|
|
3736
|
+
const totalChars = pickNumber(originalContent, "total_chars");
|
|
3737
|
+
if (totalChars !== void 0) out.content_total_chars = totalChars;
|
|
3738
|
+
}
|
|
3739
|
+
return out;
|
|
3740
|
+
}
|
|
3741
|
+
var DETAIL_JSON_STRING_FIELDS = [
|
|
3742
|
+
"contradictions",
|
|
3743
|
+
"conflicts",
|
|
3744
|
+
"quality_details",
|
|
3745
|
+
"original_contradictions",
|
|
3746
|
+
"rewrite_info_check",
|
|
3747
|
+
"human_conflict_resolutions"
|
|
3748
|
+
];
|
|
3749
|
+
function compactReviewDetail(raw) {
|
|
3750
|
+
const r = asRecord(raw);
|
|
3751
|
+
if (!r) return {};
|
|
3752
|
+
const out = { ...r };
|
|
3753
|
+
for (const field of DETAIL_JSON_STRING_FIELDS) {
|
|
3754
|
+
const parsed = parseEmbeddedJson(r[field]);
|
|
3755
|
+
if (parsed !== void 0) out[field] = parsed;
|
|
3756
|
+
}
|
|
3757
|
+
return out;
|
|
3758
|
+
}
|
|
3759
|
+
function minimalReviewTemplate(raw) {
|
|
3760
|
+
const r = asRecord(raw) ?? {};
|
|
3761
|
+
return {
|
|
3762
|
+
id: pickString(r, "id"),
|
|
3763
|
+
name: pickString(r, "name"),
|
|
3764
|
+
is_default: pickBoolean(r, "is_default") ?? false,
|
|
3765
|
+
knowledge_base_id: pickString(r, "knowledge_base_id"),
|
|
3766
|
+
section_count: Array.isArray(r.sections) ? r.sections.length : 0
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
function compactReviewTemplate(raw) {
|
|
3770
|
+
return asRecord(raw) ?? {};
|
|
3771
|
+
}
|
|
3772
|
+
function minimalReviewTemplateListResponse(data) {
|
|
3773
|
+
const items = Array.isArray(data) ? data : [];
|
|
3774
|
+
const templates = items.map(minimalReviewTemplate);
|
|
3775
|
+
return { templates, count: templates.length };
|
|
3776
|
+
}
|
|
3777
|
+
function minimalReviewTemplateDetail(raw) {
|
|
3778
|
+
const r = asRecord(raw);
|
|
3779
|
+
if (!r) return {};
|
|
3780
|
+
const out = { ...r };
|
|
3781
|
+
delete out.content;
|
|
3782
|
+
return out;
|
|
3783
|
+
}
|
|
3784
|
+
var REVIEW_ACTIONS = [
|
|
3785
|
+
"confirm",
|
|
3786
|
+
"force_accept",
|
|
3787
|
+
"accept_rewrite",
|
|
3788
|
+
"reject",
|
|
3789
|
+
"reject_reupload"
|
|
3790
|
+
];
|
|
3791
|
+
var REVIEW_BATCH_ACTIONS = ["confirm", "reject"];
|
|
3792
|
+
var CONFLICT_RESOLUTIONS = [
|
|
3793
|
+
"use_new",
|
|
3794
|
+
"keep_old",
|
|
3795
|
+
"merge",
|
|
3796
|
+
"annotate_divergence"
|
|
3797
|
+
];
|
|
3798
|
+
var REVIEW_STATUS_FILTERS = ["pending", "reviewed", "rejected"];
|
|
3799
|
+
var BATCH_MAX_ITEMS2 = 50;
|
|
3800
|
+
var TEMPLATE_MAX_SECTIONS = 15;
|
|
3801
|
+
var TEMPLATE_MAX_NAME_LENGTH = 128;
|
|
3802
|
+
function reviewBatchExitCode(data) {
|
|
3803
|
+
const failed = asRecord(data)?.failed;
|
|
3804
|
+
return typeof failed === "number" && failed > 0 ? 1 : 0;
|
|
3805
|
+
}
|
|
3806
|
+
function parseIdList(raw) {
|
|
3807
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3808
|
+
}
|
|
3809
|
+
function parseConflictResolutions(raw) {
|
|
3810
|
+
let parsed;
|
|
3811
|
+
try {
|
|
3812
|
+
parsed = JSON.parse(raw);
|
|
3813
|
+
} catch {
|
|
3814
|
+
throw new Error(
|
|
3815
|
+
'resolutions must be a JSON array: [{"conflict_id": "...", "resolution": "use_new|keep_old|merge|annotate_divergence"}]'
|
|
3816
|
+
);
|
|
3817
|
+
}
|
|
3818
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3819
|
+
throw new Error("resolutions must be a non-empty JSON array");
|
|
3820
|
+
}
|
|
3821
|
+
return parsed.map((entry, index) => {
|
|
3822
|
+
const record = asRecord(entry);
|
|
3823
|
+
const conflictId = record ? record.conflict_id : void 0;
|
|
3824
|
+
const resolution = record ? record.resolution : void 0;
|
|
3825
|
+
if (typeof conflictId !== "string" || conflictId.trim() === "") {
|
|
3826
|
+
throw new Error(`resolutions[${index}].conflict_id must be a non-empty string`);
|
|
3827
|
+
}
|
|
3828
|
+
if (typeof resolution !== "string" || !CONFLICT_RESOLUTIONS.includes(resolution)) {
|
|
3829
|
+
throw new Error(
|
|
3830
|
+
`resolutions[${index}].resolution must be one of ${CONFLICT_RESOLUTIONS.join("|")}`
|
|
3831
|
+
);
|
|
3832
|
+
}
|
|
3833
|
+
return { conflict_id: conflictId.trim(), resolution };
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
function parseTemplateSections(raw) {
|
|
3837
|
+
let parsed;
|
|
3838
|
+
try {
|
|
3839
|
+
parsed = JSON.parse(raw);
|
|
3840
|
+
} catch {
|
|
3841
|
+
throw new Error(
|
|
3842
|
+
'sections must be a JSON array: [{"level": 1, "title": "...", "guidance": "..."}]'
|
|
3843
|
+
);
|
|
3844
|
+
}
|
|
3845
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3846
|
+
throw new Error("sections must be a non-empty JSON array");
|
|
3847
|
+
}
|
|
3848
|
+
if (parsed.length > TEMPLATE_MAX_SECTIONS) {
|
|
3849
|
+
throw new Error(`sections must have at most ${TEMPLATE_MAX_SECTIONS} entries`);
|
|
3850
|
+
}
|
|
3851
|
+
return parsed.map((entry, index) => {
|
|
3852
|
+
const record = asRecord(entry);
|
|
3853
|
+
const title = record ? record.title : void 0;
|
|
3854
|
+
const guidance = record ? record.guidance : void 0;
|
|
3855
|
+
if (typeof title !== "string" || title.trim() === "") {
|
|
3856
|
+
throw new Error(`sections[${index}].title must be a non-empty string`);
|
|
3857
|
+
}
|
|
3858
|
+
if (title.includes("\n")) {
|
|
3859
|
+
throw new Error(`sections[${index}].title must be a single line`);
|
|
3860
|
+
}
|
|
3861
|
+
if (typeof guidance !== "string" || guidance.trim() === "") {
|
|
3862
|
+
throw new Error(`sections[${index}].guidance must be a non-empty string`);
|
|
3863
|
+
}
|
|
3864
|
+
const out = { title, guidance };
|
|
3865
|
+
if (record && record.level !== void 0) {
|
|
3866
|
+
const level = record.level;
|
|
3867
|
+
if (typeof level !== "number" || !Number.isInteger(level) || level < 1 || level > 3) {
|
|
3868
|
+
throw new Error(`sections[${index}].level must be an integer between 1 and 3`);
|
|
3869
|
+
}
|
|
3870
|
+
out.level = level;
|
|
3871
|
+
}
|
|
3872
|
+
return out;
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// src/cli/review-cli.ts
|
|
3877
|
+
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]";
|
|
3878
|
+
var REVIEW_GET_USAGE = "usage: ctxdb kb review get <review-id> [--content-page=N] [--verbose] [--raw] [--json]";
|
|
3879
|
+
var REVIEW_ACTION_USAGE = `usage: ctxdb kb review action <review-id> --action=<${REVIEW_ACTIONS.join("|")}>
|
|
3880
|
+
[--edited-content=<text> | --edited-content-file=<path>]
|
|
3881
|
+
[--conflict-resolutions=<json>] [--verbose] [--raw] [--json]`;
|
|
3882
|
+
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("|") + '"}]';
|
|
3883
|
+
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)`;
|
|
3884
|
+
var REVIEW_BATCH_DELETE_USAGE = `usage: ctxdb kb review batch-delete --ids=<id1,id2,...> [--json] (max ${BATCH_MAX_ITEMS2} ids)`;
|
|
3885
|
+
var REVIEW_DELETE_USAGE = "usage: ctxdb kb review delete <review-id> [--json]";
|
|
3886
|
+
var TEMPLATE_LIST_USAGE = "usage: ctxdb kb review template list <knowledge-base-id> [--verbose] [--raw] [--json]";
|
|
3887
|
+
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)';
|
|
3888
|
+
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]";
|
|
3889
|
+
var TEMPLATE_DELETE_USAGE2 = "usage: ctxdb kb review template delete <knowledge-base-id> <template-id> [--json]";
|
|
3890
|
+
var TEMPLATE_GET_USAGE2 = "usage: ctxdb kb review template get <knowledge-base-id> <template-id> [--verbose] [--raw] [--json]";
|
|
3891
|
+
var TEMPLATE_SET_DEFAULT_USAGE2 = "usage: ctxdb kb review template set-default <knowledge-base-id> <template-id> [--verbose] [--raw] [--json]";
|
|
3892
|
+
var TEMPLATE_CLEAR_DEFAULT_USAGE2 = "usage: ctxdb kb review template clear-default <knowledge-base-id> [--json]";
|
|
3893
|
+
var SETTINGS_GET_USAGE = "usage: ctxdb kb review settings get <knowledge-base-id> [--json]";
|
|
3894
|
+
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]";
|
|
3895
|
+
function stringValue(args, name) {
|
|
3896
|
+
const value = args.flags[name];
|
|
3897
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3898
|
+
}
|
|
3899
|
+
function requireId(args, flagName, positionalIndex, usage) {
|
|
3900
|
+
const positional = args.positional[positionalIndex]?.trim();
|
|
3901
|
+
const value = positional !== void 0 && positional !== "" ? positional : stringValue(args, flagName);
|
|
3902
|
+
if (value === void 0) fail(usage, 2);
|
|
3903
|
+
return value.trim();
|
|
3904
|
+
}
|
|
3905
|
+
function requireEnumFlag(args, flagName, allowed, usage) {
|
|
3906
|
+
const value = stringValue(args, flagName);
|
|
3907
|
+
if (value === void 0) fail(usage, 2);
|
|
3908
|
+
if (!allowed.includes(value)) {
|
|
3909
|
+
fail(`${flagName} must be one of ${allowed.join("|")}
|
|
3910
|
+
${usage}`, 2);
|
|
3911
|
+
}
|
|
3912
|
+
return value;
|
|
3913
|
+
}
|
|
3914
|
+
function positiveIntFlag(args, flagName, usage) {
|
|
3915
|
+
const value = args.flags[flagName];
|
|
3916
|
+
if (value === void 0) return void 0;
|
|
3917
|
+
if (typeof value !== "string") fail(usage, 2);
|
|
3918
|
+
const parsed = Number(value);
|
|
3919
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
3920
|
+
fail(`${flagName} must be a positive integer
|
|
3921
|
+
${usage}`, 2);
|
|
3922
|
+
}
|
|
3923
|
+
return parsed;
|
|
3924
|
+
}
|
|
3925
|
+
function readPayloadInput(args, inlineFlag, fileFlag, usage) {
|
|
3926
|
+
const inline = stringValue(args, inlineFlag);
|
|
3927
|
+
const filePath = stringValue(args, fileFlag);
|
|
3928
|
+
if (inline !== void 0 && filePath !== void 0) {
|
|
3929
|
+
fail(`cannot use --${inlineFlag} and --${fileFlag} together
|
|
3930
|
+
${usage}`, 2);
|
|
3931
|
+
}
|
|
3932
|
+
if (inline !== void 0) return inline;
|
|
3933
|
+
if (filePath !== void 0) {
|
|
3934
|
+
try {
|
|
3935
|
+
return readFileSync2(filePath, "utf-8");
|
|
3936
|
+
} catch (error) {
|
|
3937
|
+
fail(
|
|
3938
|
+
`cannot read --${fileFlag} ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
3939
|
+
2
|
|
3940
|
+
);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
fail(usage, 2);
|
|
3944
|
+
}
|
|
3945
|
+
function parseIdsFlag2(args, usage) {
|
|
3946
|
+
const raw = stringValue(args, "ids");
|
|
3947
|
+
if (raw === void 0) fail(usage, 2);
|
|
3948
|
+
const ids = parseIdList(raw);
|
|
3949
|
+
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3950
|
+
${usage}`, 2);
|
|
3951
|
+
if (ids.length > BATCH_MAX_ITEMS2) {
|
|
3952
|
+
fail(`--ids accepts at most ${BATCH_MAX_ITEMS2} ids per request
|
|
3953
|
+
${usage}`, 2);
|
|
3954
|
+
}
|
|
3955
|
+
return ids;
|
|
3956
|
+
}
|
|
3957
|
+
function project(args, response, operation, minimal, compact) {
|
|
3958
|
+
const data = unwrapReviewData(response, operation);
|
|
3959
|
+
if (args.flags.raw) return response;
|
|
3960
|
+
if (args.flags.verbose) return compact(data);
|
|
3961
|
+
return minimal(data);
|
|
3962
|
+
}
|
|
3963
|
+
async function reviewList(args) {
|
|
3964
|
+
const params = {};
|
|
3965
|
+
const knowledgeBaseId = stringValue(args, "knowledge-base-id");
|
|
3966
|
+
if (knowledgeBaseId !== void 0) params.knowledge_base_id = knowledgeBaseId;
|
|
3967
|
+
const decision = stringValue(args, "decision");
|
|
3968
|
+
if (decision !== void 0) params.decision = decision;
|
|
3969
|
+
const reviewStatus = stringValue(args, "review-status");
|
|
3970
|
+
if (reviewStatus !== void 0) {
|
|
3971
|
+
if (!REVIEW_STATUS_FILTERS.includes(reviewStatus)) {
|
|
3972
|
+
fail(`review-status must be one of ${REVIEW_STATUS_FILTERS.join("|")}
|
|
3973
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3974
|
+
}
|
|
3975
|
+
params.review_status = reviewStatus;
|
|
3976
|
+
}
|
|
3977
|
+
const page = positiveIntFlag(args, "page", REVIEW_LIST_USAGE);
|
|
3978
|
+
if (page !== void 0) params.page = page;
|
|
3979
|
+
const pageSize = positiveIntFlag(args, "page-size", REVIEW_LIST_USAGE);
|
|
3980
|
+
if (pageSize !== void 0) params.page_size = pageSize;
|
|
3981
|
+
const sortBy = stringValue(args, "sort-by");
|
|
3982
|
+
if (sortBy !== void 0) {
|
|
3983
|
+
if (sortBy !== "confidence_score") {
|
|
3984
|
+
fail(`sort-by only supports confidence_score
|
|
3985
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3986
|
+
}
|
|
3987
|
+
params.sort_by = sortBy;
|
|
3988
|
+
}
|
|
3989
|
+
const sortOrder = stringValue(args, "sort-order");
|
|
3990
|
+
if (sortOrder !== void 0) {
|
|
3991
|
+
if (sortBy === void 0) {
|
|
3992
|
+
fail(`sort-order requires sort-by
|
|
3993
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3994
|
+
}
|
|
3995
|
+
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
3996
|
+
fail(`sort-order must be asc or desc
|
|
3997
|
+
${REVIEW_LIST_USAGE}`, 2);
|
|
3998
|
+
}
|
|
3999
|
+
params.sort_order = sortOrder;
|
|
4000
|
+
}
|
|
4001
|
+
const ctx = buildContext(args);
|
|
4002
|
+
const resp = await ctx.client.get("/v1/knowledge/reviews", params);
|
|
4003
|
+
const out = project(
|
|
4004
|
+
args,
|
|
4005
|
+
resp,
|
|
4006
|
+
"review list",
|
|
4007
|
+
minimalReviewListResponse,
|
|
4008
|
+
compactReviewListResponse
|
|
4009
|
+
);
|
|
4010
|
+
printResult(out, !!args.flags.json);
|
|
4011
|
+
return 0;
|
|
4012
|
+
}
|
|
4013
|
+
async function reviewGet(args) {
|
|
4014
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_GET_USAGE);
|
|
4015
|
+
const contentPage = positiveIntFlag(args, "content-page", REVIEW_GET_USAGE);
|
|
4016
|
+
const params = { review_id: reviewId };
|
|
4017
|
+
if (contentPage !== void 0) params.content_page = contentPage;
|
|
4018
|
+
const ctx = buildContext(args);
|
|
4019
|
+
const resp = await ctx.client.get("/v1/knowledge/reviews/detail", params);
|
|
4020
|
+
const out = project(args, resp, "review get", minimalReviewDetail, compactReviewDetail);
|
|
4021
|
+
printResult(out, !!args.flags.json);
|
|
4022
|
+
return 0;
|
|
4023
|
+
}
|
|
4024
|
+
async function reviewAction(args) {
|
|
4025
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_ACTION_USAGE);
|
|
4026
|
+
const action = requireEnumFlag(args, "action", REVIEW_ACTIONS, REVIEW_ACTION_USAGE);
|
|
4027
|
+
const body = { review_id: reviewId, action };
|
|
4028
|
+
const editedContent = stringValue(args, "edited-content");
|
|
4029
|
+
const editedContentFile = stringValue(args, "edited-content-file");
|
|
4030
|
+
if (editedContent !== void 0 && editedContentFile !== void 0) {
|
|
4031
|
+
fail(`cannot use --edited-content and --edited-content-file together
|
|
4032
|
+
${REVIEW_ACTION_USAGE}`, 2);
|
|
4033
|
+
}
|
|
4034
|
+
if (editedContent !== void 0) body.edited_content = editedContent;
|
|
4035
|
+
else if (editedContentFile !== void 0) {
|
|
4036
|
+
try {
|
|
4037
|
+
body.edited_content = readFileSync2(editedContentFile, "utf-8");
|
|
4038
|
+
} catch (error) {
|
|
4039
|
+
fail(
|
|
4040
|
+
`cannot read --edited-content-file ${editedContentFile}: ${error instanceof Error ? error.message : String(error)}`,
|
|
4041
|
+
2
|
|
4042
|
+
);
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
const conflictResolutionsRaw = stringValue(args, "conflict-resolutions");
|
|
4046
|
+
if (conflictResolutionsRaw !== void 0) {
|
|
4047
|
+
try {
|
|
4048
|
+
body.conflict_resolutions = parseConflictResolutions(conflictResolutionsRaw);
|
|
4049
|
+
} catch (error) {
|
|
4050
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4051
|
+
${REVIEW_ACTION_USAGE}`, 2);
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
const ctx = buildContext(args);
|
|
4055
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/action", body);
|
|
4056
|
+
const out = project(args, resp, "review action", minimalReviewDetail, compactReviewDetail);
|
|
4057
|
+
printResult(out, !!args.flags.json);
|
|
4058
|
+
return 0;
|
|
4059
|
+
}
|
|
4060
|
+
async function reviewResolveConflicts(args) {
|
|
4061
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_RESOLVE_USAGE);
|
|
4062
|
+
const raw = readPayloadInput(args, "resolutions", "resolutions-file", REVIEW_RESOLVE_USAGE);
|
|
4063
|
+
let resolutions;
|
|
4064
|
+
try {
|
|
4065
|
+
resolutions = parseConflictResolutions(raw);
|
|
4066
|
+
} catch (error) {
|
|
4067
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4068
|
+
${REVIEW_RESOLVE_USAGE}`, 2);
|
|
4069
|
+
}
|
|
4070
|
+
const ctx = buildContext(args);
|
|
4071
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/resolve-conflicts", {
|
|
4072
|
+
review_id: reviewId,
|
|
4073
|
+
resolutions
|
|
4074
|
+
});
|
|
4075
|
+
const out = project(args, resp, "review resolve-conflicts", minimalReviewDetail, compactReviewDetail);
|
|
4076
|
+
printResult(out, !!args.flags.json);
|
|
4077
|
+
return 0;
|
|
4078
|
+
}
|
|
4079
|
+
async function reviewBatchAction(args) {
|
|
4080
|
+
const action = requireEnumFlag(args, "action", REVIEW_BATCH_ACTIONS, REVIEW_BATCH_ACTION_USAGE);
|
|
4081
|
+
const ids = parseIdsFlag2(args, REVIEW_BATCH_ACTION_USAGE);
|
|
4082
|
+
const ctx = buildContext(args);
|
|
4083
|
+
const resp = await ctx.client.postJson("/v1/knowledge/reviews/batch", { action, ids });
|
|
4084
|
+
const data = unwrapReviewData(resp, "review batch-action");
|
|
4085
|
+
printResult(data, !!args.flags.json);
|
|
4086
|
+
return reviewBatchExitCode(data);
|
|
4087
|
+
}
|
|
4088
|
+
async function reviewBatchDelete(args) {
|
|
4089
|
+
const ids = parseIdsFlag2(args, REVIEW_BATCH_DELETE_USAGE);
|
|
4090
|
+
const ctx = buildContext(args);
|
|
4091
|
+
const resp = await ctx.client.deleteJson("/v1/knowledge/reviews/batch", { ids });
|
|
4092
|
+
const data = unwrapReviewData(resp, "review batch-delete");
|
|
4093
|
+
printResult(data, !!args.flags.json);
|
|
4094
|
+
return reviewBatchExitCode(data);
|
|
4095
|
+
}
|
|
4096
|
+
async function reviewDelete(args) {
|
|
4097
|
+
const reviewId = requireId(args, "review-id", 0, REVIEW_DELETE_USAGE);
|
|
4098
|
+
const ctx = buildContext(args);
|
|
4099
|
+
const resp = await ctx.client.delete("/v1/knowledge/reviews", { review_id: reviewId });
|
|
4100
|
+
printResult(unwrapReviewData(resp, "review delete"), !!args.flags.json);
|
|
4101
|
+
return 0;
|
|
4102
|
+
}
|
|
4103
|
+
async function reviewTemplateList(args) {
|
|
4104
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_LIST_USAGE);
|
|
4105
|
+
const ctx = buildContext(args);
|
|
4106
|
+
const resp = await ctx.client.get("/v1/knowledge/review_templates", {
|
|
4107
|
+
knowledge_base_id: kbId
|
|
4108
|
+
});
|
|
4109
|
+
const data = unwrapReviewData(resp, "review template list");
|
|
4110
|
+
if (args.flags.raw) {
|
|
4111
|
+
printResult(resp, !!args.flags.json);
|
|
4112
|
+
return 0;
|
|
4113
|
+
}
|
|
4114
|
+
const out = args.flags.verbose ? Array.isArray(data) ? data.map(compactReviewTemplate) : [] : minimalReviewTemplateListResponse(data);
|
|
4115
|
+
printResult(out, !!args.flags.json);
|
|
4116
|
+
return 0;
|
|
4117
|
+
}
|
|
4118
|
+
function buildTemplateUpsertBody2(args, usage, requireNameAndSections) {
|
|
4119
|
+
const body = {};
|
|
4120
|
+
const name = stringValue(args, "name");
|
|
4121
|
+
if (name !== void 0) {
|
|
4122
|
+
if (name.length > TEMPLATE_MAX_NAME_LENGTH) {
|
|
4123
|
+
fail(`--name must be at most ${TEMPLATE_MAX_NAME_LENGTH} characters
|
|
4124
|
+
${usage}`, 2);
|
|
4125
|
+
}
|
|
4126
|
+
body.name = name;
|
|
4127
|
+
}
|
|
4128
|
+
const hasSectionsInput = Object.hasOwn(args.flags, "sections") || Object.hasOwn(args.flags, "sections-file");
|
|
4129
|
+
if (hasSectionsInput) {
|
|
4130
|
+
const raw = readPayloadInput(args, "sections", "sections-file", usage);
|
|
4131
|
+
try {
|
|
4132
|
+
body.sections = parseTemplateSections(raw);
|
|
4133
|
+
} catch (error) {
|
|
4134
|
+
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4135
|
+
${usage}`, 2);
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
if (requireNameAndSections && (body.name === void 0 || body.sections === void 0)) {
|
|
4139
|
+
fail(usage, 2);
|
|
4140
|
+
}
|
|
4141
|
+
return body;
|
|
4142
|
+
}
|
|
4143
|
+
function projectTemplateResult(args, resp, operation) {
|
|
4144
|
+
const data = unwrapReviewData(resp, operation);
|
|
4145
|
+
if (args.flags.raw) return resp;
|
|
4146
|
+
if (data === null) return { ok: true };
|
|
4147
|
+
if (args.flags.verbose) return compactReviewTemplate(data);
|
|
4148
|
+
return minimalReviewTemplateDetail(data);
|
|
4149
|
+
}
|
|
4150
|
+
async function reviewTemplateCreate(args) {
|
|
4151
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_CREATE_USAGE2);
|
|
4152
|
+
const body = buildTemplateUpsertBody2(args, TEMPLATE_CREATE_USAGE2, true);
|
|
4153
|
+
if (args.flags.default) body.is_default = true;
|
|
4154
|
+
const ctx = buildContext(args);
|
|
4155
|
+
const resp = await ctx.client.postJson("/v1/knowledge/review_templates", body, {
|
|
4156
|
+
knowledge_base_id: kbId
|
|
4157
|
+
});
|
|
4158
|
+
printResult(projectTemplateResult(args, resp, "review template create"), !!args.flags.json);
|
|
4159
|
+
return 0;
|
|
4160
|
+
}
|
|
4161
|
+
async function reviewTemplateUpdate(args) {
|
|
4162
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_UPDATE_USAGE2);
|
|
4163
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_UPDATE_USAGE2);
|
|
4164
|
+
const body = buildTemplateUpsertBody2(args, TEMPLATE_UPDATE_USAGE2, false);
|
|
4165
|
+
const setDefault = args.flags.default !== void 0;
|
|
4166
|
+
const clearDefault = args.flags["no-default"] !== void 0;
|
|
4167
|
+
if (setDefault && clearDefault) {
|
|
4168
|
+
fail(`cannot use --default and --no-default together
|
|
4169
|
+
${TEMPLATE_UPDATE_USAGE2}`, 2);
|
|
4170
|
+
}
|
|
4171
|
+
if (setDefault) body.is_default = true;
|
|
4172
|
+
else if (clearDefault) body.is_default = false;
|
|
4173
|
+
if (Object.keys(body).length === 0) {
|
|
4174
|
+
fail(`nothing to update: provide --name, --sections/--sections-file or --default/--no-default
|
|
4175
|
+
${TEMPLATE_UPDATE_USAGE2}`, 2);
|
|
4176
|
+
}
|
|
4177
|
+
const ctx = buildContext(args);
|
|
4178
|
+
const resp = await ctx.client.putJson("/v1/knowledge/review_templates", body, {
|
|
4179
|
+
knowledge_base_id: kbId,
|
|
4180
|
+
template_id: templateId
|
|
4181
|
+
});
|
|
4182
|
+
printResult(projectTemplateResult(args, resp, "review template update"), !!args.flags.json);
|
|
4183
|
+
return 0;
|
|
4184
|
+
}
|
|
4185
|
+
async function reviewTemplateDelete(args) {
|
|
4186
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_DELETE_USAGE2);
|
|
4187
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_DELETE_USAGE2);
|
|
4188
|
+
const ctx = buildContext(args);
|
|
4189
|
+
const resp = await ctx.client.delete("/v1/knowledge/review_templates", {
|
|
4190
|
+
knowledge_base_id: kbId,
|
|
4191
|
+
template_id: templateId
|
|
4192
|
+
});
|
|
4193
|
+
printResult(unwrapReviewData(resp, "review template delete"), !!args.flags.json);
|
|
4194
|
+
return 0;
|
|
4195
|
+
}
|
|
4196
|
+
async function reviewTemplateGet(args) {
|
|
4197
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_GET_USAGE2);
|
|
4198
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_GET_USAGE2);
|
|
4199
|
+
const ctx = buildContext(args);
|
|
4200
|
+
const resp = await ctx.client.get("/v1/knowledge/review_templates/detail", {
|
|
4201
|
+
knowledge_base_id: kbId,
|
|
4202
|
+
template_id: templateId
|
|
4203
|
+
});
|
|
4204
|
+
const data = unwrapReviewData(resp, "review template get");
|
|
4205
|
+
if (args.flags.raw) {
|
|
4206
|
+
printResult(resp, !!args.flags.json);
|
|
4207
|
+
return 0;
|
|
4208
|
+
}
|
|
4209
|
+
if (data === null) {
|
|
4210
|
+
printResult({ found: false, template_id: templateId }, !!args.flags.json);
|
|
4211
|
+
return 0;
|
|
4212
|
+
}
|
|
4213
|
+
const out = args.flags.verbose ? compactReviewTemplate(data) : minimalReviewTemplateDetail(data);
|
|
4214
|
+
printResult(out, !!args.flags.json);
|
|
4215
|
+
return 0;
|
|
4216
|
+
}
|
|
4217
|
+
async function reviewTemplateSetDefault(args) {
|
|
4218
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_SET_DEFAULT_USAGE2);
|
|
4219
|
+
const templateId = requireId(args, "template-id", 1, TEMPLATE_SET_DEFAULT_USAGE2);
|
|
4220
|
+
const ctx = buildContext(args);
|
|
4221
|
+
const resp = await ctx.client.postJson(
|
|
4222
|
+
"/v1/knowledge/review_templates/set_default",
|
|
4223
|
+
{},
|
|
4224
|
+
{ knowledge_base_id: kbId, template_id: templateId }
|
|
4225
|
+
);
|
|
4226
|
+
printResult(projectTemplateResult(args, resp, "review template set-default"), !!args.flags.json);
|
|
4227
|
+
return 0;
|
|
4228
|
+
}
|
|
4229
|
+
async function reviewTemplateClearDefault(args) {
|
|
4230
|
+
const kbId = requireId(args, "kb-id", 0, TEMPLATE_CLEAR_DEFAULT_USAGE2);
|
|
4231
|
+
const ctx = buildContext(args);
|
|
4232
|
+
const resp = await ctx.client.delete("/v1/knowledge/review_templates/default", {
|
|
4233
|
+
knowledge_base_id: kbId
|
|
4234
|
+
});
|
|
4235
|
+
printResult(unwrapReviewData(resp, "review template clear-default"), !!args.flags.json);
|
|
4236
|
+
return 0;
|
|
4237
|
+
}
|
|
4238
|
+
async function reviewSettingsGet(args) {
|
|
4239
|
+
const kbId = requireId(args, "kb-id", 0, SETTINGS_GET_USAGE);
|
|
4240
|
+
const ctx = buildContext(args);
|
|
4241
|
+
const resp = await ctx.client.get("/v1/knowledge/review_settings", {
|
|
4242
|
+
knowledge_base_id: kbId
|
|
4243
|
+
});
|
|
4244
|
+
printResult(unwrapReviewData(resp, "review settings get"), !!args.flags.json);
|
|
4245
|
+
return 0;
|
|
4246
|
+
}
|
|
4247
|
+
var SETTINGS_BOOL_FLAG_FIELDS = [
|
|
4248
|
+
["review-enabled", "review_enabled"],
|
|
4249
|
+
["rewrite-enabled", "rewrite_enabled"],
|
|
4250
|
+
["conflict-detection-enabled", "conflict_detection_enabled"],
|
|
4251
|
+
["rewrite-auto-ingest-enabled", "rewrite_auto_ingest_enabled"]
|
|
4252
|
+
];
|
|
4253
|
+
var SETTINGS_NUMBER_FLAG_FIELDS = [
|
|
4254
|
+
["high-threshold", "high_threshold", false],
|
|
4255
|
+
["logic-rewrite-threshold", "logic_rewrite_threshold", false],
|
|
4256
|
+
["noise-rewrite-threshold", "noise_rewrite_threshold", false],
|
|
4257
|
+
["max-rewrite-iterations", "max_rewrite_iterations", true]
|
|
4258
|
+
];
|
|
4259
|
+
async function reviewSettingsUpdate(args) {
|
|
4260
|
+
const kbId = requireId(args, "kb-id", 0, SETTINGS_UPDATE_USAGE2);
|
|
4261
|
+
const body = {};
|
|
4262
|
+
for (const [flag, field] of SETTINGS_BOOL_FLAG_FIELDS) {
|
|
4263
|
+
const on = args.flags[flag] !== void 0;
|
|
4264
|
+
const off = args.flags[`no-${flag}`] !== void 0;
|
|
4265
|
+
if (on && off) {
|
|
4266
|
+
fail(`cannot use --${flag} and --no-${flag} together
|
|
4267
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4268
|
+
}
|
|
4269
|
+
if (on) body[field] = true;
|
|
4270
|
+
else if (off) body[field] = false;
|
|
4271
|
+
}
|
|
4272
|
+
for (const [flag, field, integer] of SETTINGS_NUMBER_FLAG_FIELDS) {
|
|
4273
|
+
const value = args.flags[flag];
|
|
4274
|
+
if (value === void 0) continue;
|
|
4275
|
+
if (typeof value !== "string") fail(SETTINGS_UPDATE_USAGE2, 2);
|
|
4276
|
+
const parsed = Number(value);
|
|
4277
|
+
if (!Number.isFinite(parsed) || integer && !Number.isInteger(parsed)) {
|
|
4278
|
+
fail(`--${flag} must be ${integer ? "an integer" : "a number"}
|
|
4279
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4280
|
+
}
|
|
4281
|
+
body[field] = parsed;
|
|
4282
|
+
}
|
|
4283
|
+
if (Object.keys(body).length === 0) {
|
|
4284
|
+
fail(`nothing to update: provide at least one settings field
|
|
4285
|
+
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
4286
|
+
}
|
|
4287
|
+
body.knowledge_base_id = kbId;
|
|
4288
|
+
const ctx = buildContext(args);
|
|
4289
|
+
const resp = await ctx.client.patchJson("/v1/knowledge/review_settings", body);
|
|
4290
|
+
const data = unwrapReviewData(resp, "review settings update");
|
|
4291
|
+
printResult(data ?? { ok: true }, !!args.flags.json);
|
|
4292
|
+
return 0;
|
|
4293
|
+
}
|
|
4294
|
+
|
|
2996
4295
|
// src/cli/main.ts
|
|
2997
4296
|
var HELP5 = `ctxdb v${PACKAGE_VERSION} \u2014 RDS ContextDatabase CLI (multi-agent)
|
|
2998
4297
|
|
|
@@ -3000,18 +4299,20 @@ USAGE
|
|
|
3000
4299
|
ctxdb <command> [args] [--flags]
|
|
3001
4300
|
|
|
3002
4301
|
COMMANDS
|
|
3003
|
-
setup [--agent
|
|
4302
|
+
setup [--agent <${SUPPORTED_AGENT_CHOICES}> | --all]
|
|
3004
4303
|
[--api-key=K] [--base-url=URL] [--user-id=ID]
|
|
3005
4304
|
[--no-install-skill] [--no-validate] [--json]
|
|
3006
4305
|
Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
|
|
3007
4306
|
Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
|
|
3008
4307
|
QoderWork \u2192 writes agents.qoderwork config + ~/.qoderwork/settings.json hooks + skill
|
|
4308
|
+
QwenWork \u2192 writes agents.qwenwork config + ~/.qwenwork/settings.json hooks + skill
|
|
3009
4309
|
Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
|
|
3010
4310
|
Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
|
|
3011
4311
|
OpenCode \u2192 writes agents.opencode config + ~/.config/opencode/plugins/ctxdb.ts shim + skill
|
|
3012
4312
|
Hermes \u2192 writes agents.hermes config + ~/.hermes/config.yaml pre/post_llm_call hooks + skill
|
|
3013
4313
|
Approve pre_llm_call + post_llm_call with \`hermes --accept-hooks chat\`
|
|
3014
4314
|
(exit chat, rerun setup, then restart any running Hermes CLI/gateway)
|
|
4315
|
+
WorkBuddy \u2192 writes agents.workbuddy config + ~/.workbuddy/settings.json hooks + skill
|
|
3015
4316
|
--all \u2192 detects and configures every setup-compatible built-in Agent
|
|
3016
4317
|
status [--agent <name> | --all] [--json]
|
|
3017
4318
|
Inspect configuration, connectivity, and integration health.
|
|
@@ -3047,6 +4348,42 @@ COMMANDS
|
|
|
3047
4348
|
memory update <memory-id> --text=<new-text> [--agent=<name>]
|
|
3048
4349
|
memory delete <memory-id> | --all [--agent=<name>]
|
|
3049
4350
|
|
|
4351
|
+
memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--agent=<name>]
|
|
4352
|
+
memory promotion list [--status=<generating|pending_review|approving|approved|rejected|generation_failed>]
|
|
4353
|
+
[--page=N] [--page-size=N] [--sort-by=quality_score]
|
|
4354
|
+
[--sort-order=asc|desc] [--raw] [--agent=<name>]
|
|
4355
|
+
memory promotion get <promotion-id> [--raw] [--agent=<name>]
|
|
4356
|
+
memory promotion approve --promotion-id=<id> --knowledge-base-id=<kb-id> [--agent=<name>]
|
|
4357
|
+
memory promotion reject --promotion-id=<id> [--reason=<text>] [--agent=<name>]
|
|
4358
|
+
memory promotion batch-action --action=<approve|reject> --ids=<id1,id2,...>
|
|
4359
|
+
[--knowledge-base-id=<kb-id> (required for approve)] [--reason=<text>] [--agent=<name>]
|
|
4360
|
+
memory promotion batch-delete --ids=<id1,id2,...> [--agent=<name>]
|
|
4361
|
+
memory promotion resolve-doubts --promotion-id=<id>
|
|
4362
|
+
--resolutions-json='[{"index":0,"choice":"<label>","comment":"..."}]'
|
|
4363
|
+
[--title=<title>] [--content=<markdown>] [--agent=<name>]
|
|
4364
|
+
memory promotion template list [--raw] [--agent=<name>]
|
|
4365
|
+
memory promotion template create --name=<name>
|
|
4366
|
+
--sections-json='[{"title":"...","guidance":"...","level":1}]'
|
|
4367
|
+
[--is-default] [--agent=<name>]
|
|
4368
|
+
memory promotion template get <template-id> [--agent=<name>]
|
|
4369
|
+
memory promotion template update <template-id> --name=<name> --sections-json='[...]'
|
|
4370
|
+
[--is-default] [--agent=<name>]
|
|
4371
|
+
memory promotion template delete <template-id> [--agent=<name>]
|
|
4372
|
+
memory promotion template set-default <template-id> [--agent=<name>]
|
|
4373
|
+
memory promotion template clear-default [--agent=<name>]
|
|
4374
|
+
memory promotion settings get [--agent=<name>]
|
|
4375
|
+
memory promotion settings update [--auto-ingest-enabled=<true|false>]
|
|
4376
|
+
[--auto-ingest-threshold=<0..1>]
|
|
4377
|
+
[--default-kb-id=<kb-id> | --clear-default-kb] [--agent=<name>]
|
|
4378
|
+
Memory promotion (\u8BB0\u5FC6\u6210\u6587): generate, review,
|
|
4379
|
+
approve/reject promotion tasks; manage doc
|
|
4380
|
+
templates and auto-ingest settings. Batch
|
|
4381
|
+
requests accept at most 50 ids.
|
|
4382
|
+
Default output is a compact agent-friendly
|
|
4383
|
+
view; --raw returns the server envelope
|
|
4384
|
+
verbatim.
|
|
4385
|
+
|
|
4386
|
+
|
|
3050
4387
|
kb create <kb-name> [--description=<desc>] [--agent=<name>]
|
|
3051
4388
|
kb upload-text <kb-name> <doc-name> --text=<body> [--agent=<name>]
|
|
3052
4389
|
[--file-path=<server-logical-path>] [--wait|--no-wait]
|
|
@@ -3073,8 +4410,58 @@ COMMANDS
|
|
|
3073
4410
|
--verbose: compact view adds doc_name/kb_id/
|
|
3074
4411
|
doc_id?/tags? for citation.
|
|
3075
4412
|
--raw: server response verbatim (curl parity).
|
|
4413
|
+
kb review list [--knowledge-base-id=<id>] [--decision=<d>]
|
|
4414
|
+
[--review-status=<pending|reviewed|rejected>]
|
|
4415
|
+
[--page=N] [--page-size=N] [--sort-by=confidence_score]
|
|
4416
|
+
[--sort-order=asc|desc] [--verbose] [--raw]
|
|
4417
|
+
List knowledge review records (newest first
|
|
4418
|
+
by default; --sort-by sorts by quality score).
|
|
4419
|
+
kb review get <review-id> [--content-page=N] [--verbose] [--raw]
|
|
4420
|
+
Review detail: scores, decision, conflicts
|
|
4421
|
+
and paginated original content.
|
|
4422
|
+
kb review action <review-id> --action=<confirm|force_accept|accept_rewrite|reject|reject_reupload>
|
|
4423
|
+
[--edited-content=<text> | --edited-content-file=<path>]
|
|
4424
|
+
[--conflict-resolutions=<json>] [--verbose] [--raw]
|
|
4425
|
+
Perform one human review action.
|
|
4426
|
+
kb review resolve-conflicts <review-id>
|
|
4427
|
+
(--resolutions=<json> | --resolutions-file=<path>) [--verbose] [--raw]
|
|
4428
|
+
Resolve conflicts one by one. resolutions:
|
|
4429
|
+
[{"conflict_id": "...", "resolution":
|
|
4430
|
+
"use_new|keep_old|merge|annotate_divergence"}]
|
|
4431
|
+
kb review batch-action --action=<confirm|reject> --ids=<id1,id2,...>
|
|
4432
|
+
Batch confirm/reject up to 50 reviews.
|
|
4433
|
+
kb review batch-delete --ids=<id1,id2,...>
|
|
4434
|
+
Batch delete up to 50 reviewed/rejected records.
|
|
4435
|
+
kb review delete <review-id> Delete one reviewed/rejected record.
|
|
4436
|
+
kb review template list <kb-id> [--verbose] [--raw]
|
|
4437
|
+
kb review template create <kb-id> --name=<name>
|
|
4438
|
+
(--sections=<json> | --sections-file=<path>) [--default]
|
|
4439
|
+
kb review template update <kb-id> <template-id> [--name=<name>]
|
|
4440
|
+
[--sections=<json> | --sections-file=<path>]
|
|
4441
|
+
[--default | --no-default]
|
|
4442
|
+
kb review template delete <kb-id> <template-id>
|
|
4443
|
+
kb review template get <kb-id> <template-id> [--verbose] [--raw]
|
|
4444
|
+
kb review template set-default <kb-id> <template-id>
|
|
4445
|
+
kb review template clear-default <kb-id>
|
|
4446
|
+
Manage KB review-rewrite templates
|
|
4447
|
+
(max 5 per KB). sections JSON:
|
|
4448
|
+
[{"level": 1, "title": "...", "guidance": "..."}]
|
|
4449
|
+
kb review settings get <kb-id> [--json]
|
|
4450
|
+
kb review settings update <kb-id> [--review-enabled | --no-review-enabled]
|
|
4451
|
+
[--high-threshold=F] [--logic-rewrite-threshold=F]
|
|
4452
|
+
[--noise-rewrite-threshold=F] [--max-rewrite-iterations=N]
|
|
4453
|
+
[--rewrite-enabled | --no-rewrite-enabled]
|
|
4454
|
+
[--conflict-detection-enabled | --no-conflict-detection-enabled]
|
|
4455
|
+
[--rewrite-auto-ingest-enabled | --no-rewrite-auto-ingest-enabled]
|
|
4456
|
+
Per-KB knowledge-review enablement, thresholds,
|
|
4457
|
+
and rewrite/auto-ingest switches.
|
|
4458
|
+
kb review list/detail/template default to the
|
|
4459
|
+
minimal agent view; --verbose adds fields,
|
|
4460
|
+
--raw ships the server envelope verbatim.
|
|
4461
|
+
All kb review commands accept --json.
|
|
4462
|
+
|
|
3076
4463
|
|
|
3077
|
-
skill install --target
|
|
4464
|
+
skill install --target <${SUPPORTED_TARGET_CHOICES}> [--json]
|
|
3078
4465
|
skill install --path <dir> [--json]
|
|
3079
4466
|
Install skill files (contextdb-memory,
|
|
3080
4467
|
contextdb-knowledge) to an agent's skill directory.
|
|
@@ -3096,7 +4483,7 @@ HOOK ENV VARS:
|
|
|
3096
4483
|
CTXDB_SKIP_HOOKS=TRUE Hook/plugin entrypoints exit immediately.
|
|
3097
4484
|
Direct ctxdb CLI commands are unchanged.
|
|
3098
4485
|
|
|
3099
|
-
CONFIG: ~/.ctxdb/ctxdb.json (agents.default /
|
|
4486
|
+
CONFIG: ~/.ctxdb/ctxdb.json (agents.default / ${SUPPORTED_AGENTS.map((agent) => `agents.${agent}`).join(" / ")})
|
|
3100
4487
|
LOGS: ~/.ctxdb/logs/ctxdb.log, ~/.ctxdb/logs/recall-trace.jsonl
|
|
3101
4488
|
`;
|
|
3102
4489
|
var ROUTES = {
|
|
@@ -3121,7 +4508,30 @@ var ROUTES = {
|
|
|
3121
4508
|
list: memoryList,
|
|
3122
4509
|
get: memoryGet,
|
|
3123
4510
|
update: memoryUpdate,
|
|
3124
|
-
delete: memoryDelete
|
|
4511
|
+
delete: memoryDelete,
|
|
4512
|
+
promotion: {
|
|
4513
|
+
generate: promotionGenerate,
|
|
4514
|
+
list: promotionList,
|
|
4515
|
+
get: promotionGet,
|
|
4516
|
+
approve: promotionApprove,
|
|
4517
|
+
reject: promotionReject,
|
|
4518
|
+
"batch-action": promotionBatchAction,
|
|
4519
|
+
"batch-delete": promotionBatchDelete,
|
|
4520
|
+
"resolve-doubts": promotionResolveDoubts,
|
|
4521
|
+
template: {
|
|
4522
|
+
list: promotionTemplateList,
|
|
4523
|
+
create: promotionTemplateCreate,
|
|
4524
|
+
get: promotionTemplateGet,
|
|
4525
|
+
update: promotionTemplateUpdate,
|
|
4526
|
+
delete: promotionTemplateDelete,
|
|
4527
|
+
"set-default": promotionTemplateSetDefault,
|
|
4528
|
+
"clear-default": promotionTemplateClearDefault
|
|
4529
|
+
},
|
|
4530
|
+
settings: {
|
|
4531
|
+
get: promotionSettingsGet,
|
|
4532
|
+
update: promotionSettingsUpdate
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
3125
4535
|
},
|
|
3126
4536
|
kb: {
|
|
3127
4537
|
create: kbCreate,
|
|
@@ -3132,12 +4542,34 @@ var ROUTES = {
|
|
|
3132
4542
|
list: kbList,
|
|
3133
4543
|
"documents-list": kbDocumentsList,
|
|
3134
4544
|
"document-get": kbDocumentGet,
|
|
3135
|
-
search: kbSearch
|
|
4545
|
+
search: kbSearch,
|
|
4546
|
+
review: {
|
|
4547
|
+
list: reviewList,
|
|
4548
|
+
get: reviewGet,
|
|
4549
|
+
action: reviewAction,
|
|
4550
|
+
"resolve-conflicts": reviewResolveConflicts,
|
|
4551
|
+
"batch-action": reviewBatchAction,
|
|
4552
|
+
"batch-delete": reviewBatchDelete,
|
|
4553
|
+
delete: reviewDelete,
|
|
4554
|
+
template: {
|
|
4555
|
+
list: reviewTemplateList,
|
|
4556
|
+
create: reviewTemplateCreate,
|
|
4557
|
+
update: reviewTemplateUpdate,
|
|
4558
|
+
delete: reviewTemplateDelete,
|
|
4559
|
+
get: reviewTemplateGet,
|
|
4560
|
+
"set-default": reviewTemplateSetDefault,
|
|
4561
|
+
"clear-default": reviewTemplateClearDefault
|
|
4562
|
+
},
|
|
4563
|
+
settings: {
|
|
4564
|
+
get: reviewSettingsGet,
|
|
4565
|
+
update: reviewSettingsUpdate
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
3136
4568
|
}
|
|
3137
4569
|
};
|
|
3138
|
-
async function dispatch(argv) {
|
|
4570
|
+
async function dispatch(argv, routes = ROUTES, help = HELP5) {
|
|
3139
4571
|
if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
|
|
3140
|
-
process.stdout.write(
|
|
4572
|
+
process.stdout.write(help);
|
|
3141
4573
|
return 0;
|
|
3142
4574
|
}
|
|
3143
4575
|
if (argv[0] === "-v" || argv[0] === "--version") {
|
|
@@ -3146,61 +4578,82 @@ async function dispatch(argv) {
|
|
|
3146
4578
|
return 0;
|
|
3147
4579
|
}
|
|
3148
4580
|
const [first, ...rest] = argv;
|
|
3149
|
-
const route =
|
|
4581
|
+
const route = routes[first];
|
|
3150
4582
|
if (!route) {
|
|
3151
4583
|
process.stderr.write(`unknown command: ${first}
|
|
3152
4584
|
`);
|
|
3153
|
-
process.stderr.write(
|
|
4585
|
+
process.stderr.write(help);
|
|
3154
4586
|
return 2;
|
|
3155
4587
|
}
|
|
3156
4588
|
if (typeof route === "function") {
|
|
3157
4589
|
return Promise.resolve(route(parseArgs(rest)));
|
|
3158
4590
|
}
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
`
|
|
3163
|
-
|
|
4591
|
+
return dispatchGroup([first], route, rest);
|
|
4592
|
+
}
|
|
4593
|
+
async function dispatchGroup(path, group, argv) {
|
|
4594
|
+
const usage = `usage: ctxdb ${path.join(" ")} <${Object.keys(group).join("|")}> ...
|
|
4595
|
+
`;
|
|
4596
|
+
if (argv.length === 0) {
|
|
4597
|
+
process.stderr.write(usage);
|
|
3164
4598
|
return 2;
|
|
3165
4599
|
}
|
|
3166
|
-
if (
|
|
3167
|
-
process.stdout.write(
|
|
3168
|
-
`usage: ctxdb ${first} <${Object.keys(route).join("|")}> ...
|
|
3169
|
-
`
|
|
3170
|
-
);
|
|
4600
|
+
if (argv[0] === "-h" || argv[0] === "--help") {
|
|
4601
|
+
process.stdout.write(usage);
|
|
3171
4602
|
return 0;
|
|
3172
4603
|
}
|
|
3173
|
-
const [sub, ...subRest] =
|
|
3174
|
-
const handler =
|
|
4604
|
+
const [sub, ...subRest] = argv;
|
|
4605
|
+
const handler = group[sub];
|
|
3175
4606
|
if (!handler) {
|
|
3176
|
-
process.stderr.write(`unknown subcommand: ${
|
|
4607
|
+
process.stderr.write(`unknown subcommand: ${[...path, sub].join(" ")}
|
|
3177
4608
|
`);
|
|
3178
4609
|
return 2;
|
|
3179
4610
|
}
|
|
3180
|
-
|
|
4611
|
+
if (typeof handler === "function") {
|
|
4612
|
+
return Promise.resolve(handler(parseArgs(subRest)));
|
|
4613
|
+
}
|
|
4614
|
+
return dispatchGroup([...path, sub], handler, subRest);
|
|
3181
4615
|
}
|
|
3182
|
-
async function main(argv = process.argv.slice(2)) {
|
|
4616
|
+
async function main(argv = process.argv.slice(2), routes = ROUTES, help = HELP5) {
|
|
3183
4617
|
const notification = beginUpdateNotification(argv, {
|
|
3184
4618
|
currentVersion: PACKAGE_VERSION,
|
|
3185
4619
|
workerPath: fileURLToPath2(new URL("../workers/version-check.js", import.meta.url))
|
|
3186
4620
|
});
|
|
3187
|
-
const code = await dispatch(argv);
|
|
4621
|
+
const code = await dispatch(argv, routes, help);
|
|
3188
4622
|
completeUpdateNotification(notification, code);
|
|
3189
4623
|
return code;
|
|
3190
4624
|
}
|
|
3191
|
-
|
|
3192
|
-
(
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
4625
|
+
function runMain(routes = ROUTES, help = HELP5) {
|
|
4626
|
+
main(process.argv.slice(2), routes, help).then(
|
|
4627
|
+
(code) => {
|
|
4628
|
+
if (typeof code === "number") process.exitCode = code;
|
|
4629
|
+
},
|
|
4630
|
+
(err) => {
|
|
4631
|
+
process.stderr.write(`error: ${err?.message ?? err}
|
|
3197
4632
|
`);
|
|
3198
|
-
|
|
3199
|
-
|
|
4633
|
+
if (process.env.RDS_CTXDB_DEBUG === "1" && err?.stack) {
|
|
4634
|
+
process.stderr.write(err.stack + "\n");
|
|
4635
|
+
}
|
|
4636
|
+
process.exitCode = 1;
|
|
3200
4637
|
}
|
|
3201
|
-
|
|
4638
|
+
);
|
|
4639
|
+
}
|
|
4640
|
+
var directEntry = (() => {
|
|
4641
|
+
try {
|
|
4642
|
+
const modulePath = resolve2(fileURLToPath2(import.meta.url));
|
|
4643
|
+
const argvPath = resolve2(process.argv[1] ?? "");
|
|
4644
|
+
return modulePath === argvPath || realpathSync(modulePath) === realpathSync(argvPath);
|
|
4645
|
+
} catch {
|
|
4646
|
+
return false;
|
|
3202
4647
|
}
|
|
3203
|
-
);
|
|
4648
|
+
})();
|
|
4649
|
+
var directEntryName = basename2(fileURLToPath2(import.meta.url));
|
|
4650
|
+
if (directEntry && (directEntryName === "main.ts" || directEntryName === "main.js")) {
|
|
4651
|
+
runMain();
|
|
4652
|
+
}
|
|
3204
4653
|
export {
|
|
3205
|
-
|
|
4654
|
+
HELP5 as HELP,
|
|
4655
|
+
ROUTES,
|
|
4656
|
+
dispatch,
|
|
4657
|
+
main,
|
|
4658
|
+
runMain
|
|
3206
4659
|
};
|