@aliyunrds/ctxdb 0.0.4 → 0.0.7
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.md +17 -17
- package/dist/chunk-6S5RJYBC.js +198 -0
- package/dist/{chunk-7NOXAU2X.js → chunk-LIC44DR6.js} +9 -2
- package/dist/chunk-Q2EEP4CE.js +69 -0
- package/dist/{chunk-QSSNPN3M.js → chunk-S45GOYUU.js} +46 -12
- package/dist/cli/main.js +481 -448
- package/dist/hooks/session-start.js +10 -3
- package/dist/hooks/stop.js +100 -21
- package/dist/hooks/user-prompt-submit.js +42 -8
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +161 -0
- package/dist/setup/skills/contextdb-memory/SKILL.md +142 -0
- package/package.json +2 -2
- package/dist/setup/skills/cli-only/SKILL.md +0 -116
- package/dist/setup/skills/hooks-driven/SKILL.md +0 -144
package/dist/cli/main.js
CHANGED
|
@@ -1,29 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_FILE_INGEST_TIMEOUT_MS,
|
|
4
|
+
DEFAULT_INGEST_TIMEOUT_MS,
|
|
5
|
+
compactChunk,
|
|
6
|
+
compactKbQueryResponse,
|
|
7
|
+
createKb,
|
|
8
|
+
getDocument,
|
|
9
|
+
listDocuments,
|
|
10
|
+
listKnowledgeBases,
|
|
11
|
+
minimalChunk,
|
|
12
|
+
minimalKbQueryResponse,
|
|
13
|
+
pollIngest,
|
|
14
|
+
uploadFile,
|
|
15
|
+
uploadText
|
|
16
|
+
} from "../chunk-6S5RJYBC.js";
|
|
2
17
|
import {
|
|
3
18
|
DEFAULT_BASE_URL,
|
|
4
19
|
DEFAULT_USER_ID,
|
|
5
20
|
HttpClient,
|
|
6
|
-
NotFoundError,
|
|
7
21
|
SUPPORTED_AGENTS,
|
|
8
22
|
agentFromEnv,
|
|
9
23
|
agentHomeDir,
|
|
10
24
|
configuredAgents,
|
|
11
|
-
|
|
25
|
+
isAgentSlug,
|
|
26
|
+
isBuiltinAgent,
|
|
12
27
|
isComplete,
|
|
13
28
|
load,
|
|
14
29
|
removeAgent,
|
|
15
|
-
save
|
|
16
|
-
|
|
30
|
+
save,
|
|
31
|
+
writeInstalledPkgVersion
|
|
32
|
+
} from "../chunk-S45GOYUU.js";
|
|
17
33
|
|
|
18
34
|
// src/cli/util.ts
|
|
19
35
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
20
36
|
// universal
|
|
21
37
|
"json",
|
|
38
|
+
"help",
|
|
39
|
+
"h",
|
|
22
40
|
// setup / init
|
|
23
41
|
"no-install-skill",
|
|
24
42
|
"no-validate",
|
|
25
|
-
|
|
26
|
-
// teardown
|
|
43
|
+
// uninstall
|
|
27
44
|
"purge-config",
|
|
28
45
|
"purge-logs",
|
|
29
46
|
"purge-all",
|
|
@@ -108,9 +125,9 @@ function isStatusResult(v) {
|
|
|
108
125
|
if (typeof v !== "object" || v === null) return false;
|
|
109
126
|
return "agent" in v && "connected" in v;
|
|
110
127
|
}
|
|
111
|
-
function formatSetupResult(r) {
|
|
128
|
+
function formatSetupResult(r, command = "setup") {
|
|
112
129
|
const lines = [];
|
|
113
|
-
lines.push(`${r.ok ? CHECK : CROSS} ${BOLD}ctxdb
|
|
130
|
+
lines.push(`${r.ok ? CHECK : CROSS} ${BOLD}ctxdb ${command}${RESET} ${r.ok ? "completed" : "failed"}
|
|
114
131
|
`);
|
|
115
132
|
for (const s of r.steps) {
|
|
116
133
|
const icon = s.ok ? CHECK : CROSS;
|
|
@@ -160,13 +177,13 @@ function formatStatusResult(r) {
|
|
|
160
177
|
}
|
|
161
178
|
return lines.join("\n");
|
|
162
179
|
}
|
|
163
|
-
function printResult(value, json) {
|
|
180
|
+
function printResult(value, json, command) {
|
|
164
181
|
if (json) {
|
|
165
182
|
process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
166
183
|
} else if (isUpgradeResult(value)) {
|
|
167
184
|
process.stdout.write(formatUpgradeResult(value) + "\n");
|
|
168
185
|
} else if (isSetupResult(value)) {
|
|
169
|
-
process.stdout.write(formatSetupResult(value) + "\n");
|
|
186
|
+
process.stdout.write(formatSetupResult(value, command ?? "setup") + "\n");
|
|
170
187
|
} else if (isStatusResult(value)) {
|
|
171
188
|
process.stdout.write(formatStatusResult(value) + "\n");
|
|
172
189
|
} else {
|
|
@@ -176,17 +193,16 @@ function printResult(value, json) {
|
|
|
176
193
|
function agentFromFlags(flags) {
|
|
177
194
|
const raw = flags.agent;
|
|
178
195
|
if (raw === void 0) return agentFromEnv();
|
|
179
|
-
if (
|
|
180
|
-
fail(`unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`, 2);
|
|
196
|
+
if (isAgentSlug(raw)) return raw;
|
|
197
|
+
fail(`unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`, 2);
|
|
181
198
|
}
|
|
182
199
|
function buildContext(args) {
|
|
183
200
|
const agent = agentFromFlags(args?.flags ?? {});
|
|
184
201
|
const cfg = load({ agent });
|
|
185
202
|
if (!isComplete(cfg)) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
`
|
|
189
|
-
);
|
|
203
|
+
const hint = agent === "default" ? "run `ctxdb setup --api-key=<key> --base-url=<url>` or set CTXDB_API_KEY / CTXDB_BASE_URL env vars" : `run \`ctxdb setup --agent ${agent}\` or set CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID env vars`;
|
|
204
|
+
process.stderr.write(`config incomplete for agent ${agent}: ${hint}
|
|
205
|
+
`);
|
|
190
206
|
process.exit(2);
|
|
191
207
|
}
|
|
192
208
|
const client = new HttpClient({
|
|
@@ -200,7 +216,7 @@ function fail(message, code = 1) {
|
|
|
200
216
|
`);
|
|
201
217
|
process.exit(code);
|
|
202
218
|
}
|
|
203
|
-
var PACKAGE_VERSION = "0.0.
|
|
219
|
+
var PACKAGE_VERSION = "0.0.7";
|
|
204
220
|
|
|
205
221
|
// src/setup/installer.ts
|
|
206
222
|
import {
|
|
@@ -221,7 +237,7 @@ import { join, dirname } from "path";
|
|
|
221
237
|
import { fileURLToPath } from "url";
|
|
222
238
|
import { execFileSync } from "child_process";
|
|
223
239
|
var UNINSTALL_NPM_HINT = "To also remove the npm binaries: `npm uninstall -g @aliyunrds/ctxdb @aliyunrds/ctxdb-shared`";
|
|
224
|
-
var UNINSTALL_ORDER_HINT = "When you eventually want to uninstall: run `ctxdb
|
|
240
|
+
var UNINSTALL_ORDER_HINT = "When you eventually want to uninstall: run `ctxdb uninstall --purge-all` FIRST, then `npm uninstall -g @aliyunrds/ctxdb @aliyunrds/ctxdb-shared`. (npm 7+ removed uninstall lifecycle hooks, so the order matters \u2014 otherwise agent settings.json hooks + ~/.ctxdb/ residue stay behind.)";
|
|
225
241
|
function skillInstallRoot(agent) {
|
|
226
242
|
switch (agent) {
|
|
227
243
|
case "qoder":
|
|
@@ -232,15 +248,10 @@ function skillInstallRoot(agent) {
|
|
|
232
248
|
return join(homedir(), ".claude", "skills");
|
|
233
249
|
}
|
|
234
250
|
}
|
|
235
|
-
var
|
|
236
|
-
|
|
237
|
-
return agentSupportsHooks(agent) ? "hooks-driven" : "cli-only";
|
|
238
|
-
}
|
|
239
|
-
function skillInstallDir(agent) {
|
|
240
|
-
return join(skillInstallRoot(agent), SKILL_DIR_NAME);
|
|
241
|
-
}
|
|
251
|
+
var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
|
|
252
|
+
var LEGACY_SKILL_DIRS = ["ctxdb"];
|
|
242
253
|
function agentSupportsHooks(agent) {
|
|
243
|
-
return agent
|
|
254
|
+
return isBuiltinAgent(agent);
|
|
244
255
|
}
|
|
245
256
|
function hookConfigPath(agent) {
|
|
246
257
|
switch (agent) {
|
|
@@ -260,54 +271,48 @@ var LEGACY_QODER_SKILL_DIRS = [
|
|
|
260
271
|
var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart"];
|
|
261
272
|
async function runSetup(options) {
|
|
262
273
|
const steps = [];
|
|
263
|
-
if (!
|
|
274
|
+
if (!isAgentSlug(options.agent)) {
|
|
264
275
|
steps.push({
|
|
265
276
|
step: "validate-agent",
|
|
266
277
|
ok: false,
|
|
267
|
-
detail: `unknown --agent: ${String(options.agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`
|
|
278
|
+
detail: `unknown --agent: ${String(options.agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`
|
|
268
279
|
});
|
|
269
280
|
return { ok: false, steps };
|
|
270
281
|
}
|
|
271
282
|
const agent = options.agent;
|
|
272
283
|
const installSkill = options.installSkill !== false;
|
|
273
284
|
const validate = options.validate !== false;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
285
|
+
if (isBuiltinAgent(agent)) {
|
|
286
|
+
const homeCheck = checkAgentHome(agent);
|
|
287
|
+
steps.push(homeCheck);
|
|
288
|
+
if (!homeCheck.ok) return { ok: false, steps };
|
|
289
|
+
}
|
|
277
290
|
const cfg = load({ agent });
|
|
278
291
|
if (options.apiKey) cfg.apiKey = options.apiKey;
|
|
279
292
|
if (options.baseUrl) cfg.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
280
293
|
else if (!cfg.baseUrl) cfg.baseUrl = DEFAULT_BASE_URL;
|
|
281
294
|
cfg.userId = options.userId ?? cfg.userId ?? DEFAULT_USER_ID;
|
|
295
|
+
if (!cfg.apiKey) {
|
|
296
|
+
steps.push({
|
|
297
|
+
step: "write-config",
|
|
298
|
+
ok: false,
|
|
299
|
+
detail: `${agent}: --api-key is required (won't write config without a valid API key)`
|
|
300
|
+
});
|
|
301
|
+
return { ok: false, steps };
|
|
302
|
+
}
|
|
282
303
|
save(cfg, void 0, { agent });
|
|
283
304
|
steps.push({
|
|
284
305
|
step: "write-config",
|
|
285
306
|
ok: true,
|
|
286
307
|
detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
|
|
287
308
|
});
|
|
288
|
-
if (installSkill) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
copySkillDir(srcDir, dest, agent);
|
|
294
|
-
steps.push({ step: "install-skill", ok: true, detail: dest });
|
|
295
|
-
} else {
|
|
296
|
-
steps.push({
|
|
297
|
-
step: "install-skill",
|
|
298
|
-
ok: false,
|
|
299
|
-
detail: `skill source directory not found (${skillResourceDir(agent)})`
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
} catch (err) {
|
|
303
|
-
steps.push({
|
|
304
|
-
step: "install-skill",
|
|
305
|
-
ok: false,
|
|
306
|
-
detail: err?.message ?? String(err)
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
+
if (installSkill && isBuiltinAgent(agent)) {
|
|
310
|
+
const installRoot = skillInstallRoot(agent);
|
|
311
|
+
const skillResult = installSkillsTo(installRoot, agent);
|
|
312
|
+
steps.push(...skillResult.steps);
|
|
313
|
+
if (!skillResult.ok) return { ok: false, steps };
|
|
309
314
|
}
|
|
310
|
-
if (agentSupportsHooks(agent)) {
|
|
315
|
+
if (isBuiltinAgent(agent) && agentSupportsHooks(agent)) {
|
|
311
316
|
const hookPaths = locateHookPaths();
|
|
312
317
|
steps.push({
|
|
313
318
|
step: "locate-hooks",
|
|
@@ -390,15 +395,15 @@ async function runSetup(options) {
|
|
|
390
395
|
}
|
|
391
396
|
function runRemove(agent, options = {}) {
|
|
392
397
|
const steps = [];
|
|
393
|
-
if (!
|
|
398
|
+
if (!isAgentSlug(agent)) {
|
|
394
399
|
steps.push({
|
|
395
400
|
step: "validate-agent",
|
|
396
401
|
ok: false,
|
|
397
|
-
detail: `unknown --agent: ${String(agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`
|
|
402
|
+
detail: `unknown --agent: ${String(agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`
|
|
398
403
|
});
|
|
399
404
|
return { ok: false, steps };
|
|
400
405
|
}
|
|
401
|
-
if (agentSupportsHooks(agent)) {
|
|
406
|
+
if (isBuiltinAgent(agent) && agentSupportsHooks(agent)) {
|
|
402
407
|
const path = hookConfigPath(agent);
|
|
403
408
|
if (!existsSync(path)) {
|
|
404
409
|
steps.push({
|
|
@@ -435,12 +440,12 @@ function runRemove(agent, options = {}) {
|
|
|
435
440
|
return { ok: false, steps };
|
|
436
441
|
}
|
|
437
442
|
}
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
...
|
|
441
|
-
|
|
442
|
-
)
|
|
443
|
-
]
|
|
443
|
+
const root = skillInstallRoot(agent);
|
|
444
|
+
const dirs = [
|
|
445
|
+
...SKILL_DIRS.map((d) => join(root, d)),
|
|
446
|
+
...LEGACY_SKILL_DIRS.map((d) => join(root, d)),
|
|
447
|
+
...agent === "qoder" ? LEGACY_QODER_SKILL_DIRS.map((d) => join(root, d)) : []
|
|
448
|
+
];
|
|
444
449
|
for (const p of dirs) {
|
|
445
450
|
if (!existsSync(p)) continue;
|
|
446
451
|
try {
|
|
@@ -454,26 +459,6 @@ function runRemove(agent, options = {}) {
|
|
|
454
459
|
});
|
|
455
460
|
}
|
|
456
461
|
}
|
|
457
|
-
} else {
|
|
458
|
-
const dir = skillInstallDir(agent);
|
|
459
|
-
if (!existsSync(dir)) {
|
|
460
|
-
steps.push({
|
|
461
|
-
step: "skill-missing",
|
|
462
|
-
ok: true,
|
|
463
|
-
detail: `nothing to remove (${dir})`
|
|
464
|
-
});
|
|
465
|
-
} else {
|
|
466
|
-
try {
|
|
467
|
-
rmSync(dir, { recursive: true, force: true });
|
|
468
|
-
steps.push({ step: "remove-skill-dir", ok: true, detail: dir });
|
|
469
|
-
} catch (err) {
|
|
470
|
-
steps.push({
|
|
471
|
-
step: "remove-skill-dir",
|
|
472
|
-
ok: false,
|
|
473
|
-
detail: `${dir}: ${err?.message ?? String(err)}`
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
462
|
}
|
|
478
463
|
try {
|
|
479
464
|
const r = removeAgent(agent, void 0, {
|
|
@@ -530,6 +515,12 @@ function runTeardown(options = {}) {
|
|
|
530
515
|
steps.push({ ...s, step: `${agent}:${s.step}` });
|
|
531
516
|
}
|
|
532
517
|
}
|
|
518
|
+
const defaultSub = runRemove("default", { keepConfigShell });
|
|
519
|
+
if (defaultSub.steps.some((s) => s.step === "remove-config-section" && s.detail && !s.detail.includes("nothing to remove"))) {
|
|
520
|
+
for (const s of defaultSub.steps) {
|
|
521
|
+
steps.push({ ...s, step: `default:${s.step}` });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
533
524
|
if (purgeConfig) {
|
|
534
525
|
const cfgPath = join(homedir(), ".ctxdb", "ctxdb.json");
|
|
535
526
|
if (existsSync(cfgPath)) {
|
|
@@ -811,6 +802,44 @@ function copySkillDir(src, dest, agent) {
|
|
|
811
802
|
}
|
|
812
803
|
}
|
|
813
804
|
}
|
|
805
|
+
var SKILL_INSTALL_TARGETS = {
|
|
806
|
+
qoder: join(homedir(), ".qoder", "skills"),
|
|
807
|
+
codex: join(homedir(), ".codex", "skills"),
|
|
808
|
+
claude: join(homedir(), ".claude", "skills"),
|
|
809
|
+
openclaw: join(homedir(), ".openclaw", "skills"),
|
|
810
|
+
hermes: join(homedir(), ".hermes", "skills")
|
|
811
|
+
};
|
|
812
|
+
function skillInstallTargetPath(target) {
|
|
813
|
+
return SKILL_INSTALL_TARGETS[target] ?? null;
|
|
814
|
+
}
|
|
815
|
+
function installSkillsTo(targetRoot, agent = "default") {
|
|
816
|
+
const steps = [];
|
|
817
|
+
for (const skillName of SKILL_DIRS) {
|
|
818
|
+
try {
|
|
819
|
+
const srcDir = locateSkillDir(skillName);
|
|
820
|
+
if (srcDir) {
|
|
821
|
+
const dest = join(targetRoot, skillName);
|
|
822
|
+
copySkillDir(srcDir, dest, agent);
|
|
823
|
+
steps.push({ step: "install-skill", ok: true, detail: dest });
|
|
824
|
+
} else {
|
|
825
|
+
steps.push({
|
|
826
|
+
step: "install-skill",
|
|
827
|
+
ok: false,
|
|
828
|
+
detail: `skill source directory not found (${skillName})`
|
|
829
|
+
});
|
|
830
|
+
return { ok: false, steps };
|
|
831
|
+
}
|
|
832
|
+
} catch (err) {
|
|
833
|
+
steps.push({
|
|
834
|
+
step: "install-skill",
|
|
835
|
+
ok: false,
|
|
836
|
+
detail: err?.message ?? String(err)
|
|
837
|
+
});
|
|
838
|
+
return { ok: false, steps };
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return { ok: true, steps };
|
|
842
|
+
}
|
|
814
843
|
function appendHooks(agent, hookPaths) {
|
|
815
844
|
const path = hookConfigPath(agent);
|
|
816
845
|
if (!path) return;
|
|
@@ -1065,7 +1094,7 @@ function describeCodexFeatureMutation(status2, createdNew) {
|
|
|
1065
1094
|
}
|
|
1066
1095
|
}
|
|
1067
1096
|
function runUpgrade(options = {}) {
|
|
1068
|
-
const agents = options.agent ? [options.agent] : configuredAgents(options.configPath);
|
|
1097
|
+
const agents = options.agent ? [options.agent] : configuredAgents(options.configPath).filter(isBuiltinAgent);
|
|
1069
1098
|
if (agents.length === 0) {
|
|
1070
1099
|
return {
|
|
1071
1100
|
ok: false,
|
|
@@ -1097,24 +1126,19 @@ function upgradeOneAgent(agent) {
|
|
|
1097
1126
|
if (!homeCheck.ok) {
|
|
1098
1127
|
return { ok: true, steps, hints: [`${agent}: skipped \u2014 agent home not found`] };
|
|
1099
1128
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
step: "install-skill",
|
|
1109
|
-
ok: false,
|
|
1110
|
-
detail: "skill source directory not found"
|
|
1111
|
-
});
|
|
1112
|
-
return { ok: false, steps };
|
|
1129
|
+
const root = skillInstallRoot(agent);
|
|
1130
|
+
for (const legacy of LEGACY_SKILL_DIRS) {
|
|
1131
|
+
const legacyDir = join(root, legacy);
|
|
1132
|
+
if (!existsSync(legacyDir)) continue;
|
|
1133
|
+
try {
|
|
1134
|
+
rmSync(legacyDir, { recursive: true, force: true });
|
|
1135
|
+
steps.push({ step: "remove-legacy-skill", ok: true, detail: legacyDir });
|
|
1136
|
+
} catch {
|
|
1113
1137
|
}
|
|
1114
|
-
} catch (err) {
|
|
1115
|
-
steps.push({ step: "install-skill", ok: false, detail: err?.message ?? String(err) });
|
|
1116
|
-
return { ok: false, steps };
|
|
1117
1138
|
}
|
|
1139
|
+
const skillResult = installSkillsTo(root, agent);
|
|
1140
|
+
steps.push(...skillResult.steps);
|
|
1141
|
+
if (!skillResult.ok) return { ok: false, steps };
|
|
1118
1142
|
if (agentSupportsHooks(agent)) {
|
|
1119
1143
|
const hookPaths = locateHookPaths();
|
|
1120
1144
|
if (!hookPaths) {
|
|
@@ -1157,46 +1181,6 @@ function upgradeOneAgent(agent) {
|
|
|
1157
1181
|
}
|
|
1158
1182
|
|
|
1159
1183
|
// src/cli/top.ts
|
|
1160
|
-
import { homedir as homedir2 } from "os";
|
|
1161
|
-
import { join as join2 } from "path";
|
|
1162
|
-
async function init(args) {
|
|
1163
|
-
const agent = agentFromFlags(args.flags);
|
|
1164
|
-
const cfg = load({ agent });
|
|
1165
|
-
if (typeof args.flags["api-key"] === "string") cfg.apiKey = args.flags["api-key"];
|
|
1166
|
-
if (typeof args.flags["base-url"] === "string") {
|
|
1167
|
-
cfg.baseUrl = args.flags["base-url"].replace(/\/+$/, "");
|
|
1168
|
-
} else if (!cfg.baseUrl) {
|
|
1169
|
-
cfg.baseUrl = DEFAULT_BASE_URL;
|
|
1170
|
-
}
|
|
1171
|
-
const cliUserId = typeof args.flags["user-id"] === "string" ? args.flags["user-id"] : void 0;
|
|
1172
|
-
cfg.userId = cliUserId ?? cfg.userId ?? DEFAULT_USER_ID;
|
|
1173
|
-
save(cfg, void 0, { agent });
|
|
1174
|
-
let validated = false;
|
|
1175
|
-
let pingError = null;
|
|
1176
|
-
if (!args.flags["no-validate"] && cfg.apiKey) {
|
|
1177
|
-
try {
|
|
1178
|
-
const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey });
|
|
1179
|
-
await client.get("/v1/ping/");
|
|
1180
|
-
validated = true;
|
|
1181
|
-
} catch (err) {
|
|
1182
|
-
pingError = err?.message ?? String(err);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
printResult(
|
|
1186
|
-
{
|
|
1187
|
-
ok: true,
|
|
1188
|
-
config_path: join2(homedir2(), ".ctxdb", "ctxdb.json"),
|
|
1189
|
-
agent,
|
|
1190
|
-
api_key_set: Boolean(cfg.apiKey),
|
|
1191
|
-
base_url: cfg.baseUrl,
|
|
1192
|
-
user_id: cfg.userId,
|
|
1193
|
-
validated,
|
|
1194
|
-
ping_error: pingError
|
|
1195
|
-
},
|
|
1196
|
-
!!args.flags.json
|
|
1197
|
-
);
|
|
1198
|
-
return 0;
|
|
1199
|
-
}
|
|
1200
1184
|
async function status(args) {
|
|
1201
1185
|
const agent = agentFromFlags(args.flags);
|
|
1202
1186
|
const cfg = load({ agent });
|
|
@@ -1212,7 +1196,7 @@ async function status(args) {
|
|
|
1212
1196
|
pingError = err?.message ?? String(err);
|
|
1213
1197
|
}
|
|
1214
1198
|
}
|
|
1215
|
-
const hookHealth = checkHookNodePaths(agent);
|
|
1199
|
+
const hookHealth = isBuiltinAgent(agent) ? checkHookNodePaths(agent) : { installed: false, nodePaths: [], nodeOk: true, detail: "no hooks (default agent)" };
|
|
1216
1200
|
printResult(
|
|
1217
1201
|
{
|
|
1218
1202
|
ok: complete,
|
|
@@ -1250,34 +1234,50 @@ async function ping(args) {
|
|
|
1250
1234
|
}
|
|
1251
1235
|
|
|
1252
1236
|
// src/cli/setup-cli.ts
|
|
1237
|
+
var HELP = `ctxdb setup \u2014 configure an agent
|
|
1238
|
+
|
|
1239
|
+
USAGE
|
|
1240
|
+
ctxdb setup [--agent <qoder|codex|claude>]
|
|
1241
|
+
[--api-key=K] [--base-url=URL] [--user-id=ID]
|
|
1242
|
+
[--no-install-skill] [--no-validate] [--json]
|
|
1243
|
+
|
|
1244
|
+
BEHAVIOR
|
|
1245
|
+
Without --agent Writes agents.default in ~/.ctxdb/ctxdb.json (CLI-only,
|
|
1246
|
+
no hooks or skills installed).
|
|
1247
|
+
With --agent Writes agent config + installs hooks + skills for the
|
|
1248
|
+
specified agent harness.
|
|
1249
|
+
|
|
1250
|
+
FLAGS
|
|
1251
|
+
--agent <name> Target agent (qoder|codex|claude)
|
|
1252
|
+
--api-key <key> API key (required on first setup)
|
|
1253
|
+
--base-url <url> Server URL (default: https://context-database.aliyuncs.com)
|
|
1254
|
+
--user-id <id> User bucket (default: "default")
|
|
1255
|
+
--no-install-skill Skip skill directory installation
|
|
1256
|
+
--no-validate Skip connectivity ping after setup
|
|
1257
|
+
--json Machine-readable JSON output
|
|
1258
|
+
`;
|
|
1253
1259
|
function parseAgent(args) {
|
|
1254
1260
|
const raw = args.flags.agent;
|
|
1255
|
-
if (
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
function agentError() {
|
|
1261
|
+
if (raw === void 0) return "default";
|
|
1262
|
+
if (typeof raw === "string" && isAgentSlug(raw)) return raw;
|
|
1259
1263
|
process.stderr.write(
|
|
1260
|
-
`
|
|
1261
|
-
e.g. ctxdb setup --agent qoder --api-key=<key>
|
|
1264
|
+
`unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)
|
|
1262
1265
|
`
|
|
1263
1266
|
);
|
|
1267
|
+
process.exit(2);
|
|
1264
1268
|
}
|
|
1265
1269
|
async function setup(args) {
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
return 2;
|
|
1270
|
+
if (args.flags.help || args.flags.h) {
|
|
1271
|
+
process.stdout.write(HELP);
|
|
1272
|
+
return 0;
|
|
1270
1273
|
}
|
|
1274
|
+
const agent = parseAgent(args);
|
|
1271
1275
|
const json = !!args.flags.json;
|
|
1272
|
-
if (
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
${h}
|
|
1278
|
-
`);
|
|
1279
|
-
}
|
|
1280
|
-
return result2.ok ? 0 : 1;
|
|
1276
|
+
if (agent === "default" && !json && !configuredAgents().includes("default")) {
|
|
1277
|
+
process.stderr.write(
|
|
1278
|
+
`hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|codex> for full setup.
|
|
1279
|
+
`
|
|
1280
|
+
);
|
|
1281
1281
|
}
|
|
1282
1282
|
const result = await runSetup({
|
|
1283
1283
|
agent,
|
|
@@ -1297,14 +1297,61 @@ ${h}
|
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
1299
|
// src/cli/teardown.ts
|
|
1300
|
-
|
|
1300
|
+
var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
|
|
1301
|
+
|
|
1302
|
+
USAGE
|
|
1303
|
+
ctxdb uninstall [--agent <qoder|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
|
|
1304
|
+
|
|
1305
|
+
DEFAULT BEHAVIOR (no --agent, no purge flags)
|
|
1306
|
+
Strips ctxdb hook entries and skill directories for ALL agents (qoder,
|
|
1307
|
+
codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
|
|
1308
|
+
so you can re-run \`ctxdb setup\` later without losing configuration.
|
|
1309
|
+
|
|
1310
|
+
WITH --agent <name>
|
|
1311
|
+
Only removes hooks + skill + config section for the specified agent.
|
|
1312
|
+
Other agents remain untouched.
|
|
1313
|
+
|
|
1314
|
+
FLAGS
|
|
1315
|
+
--agent <name> Target a single agent (qoder|codex|claude)
|
|
1316
|
+
--purge-config Also delete ~/.ctxdb/ctxdb.json
|
|
1317
|
+
--purge-logs Also delete ~/.ctxdb/logs/
|
|
1318
|
+
--purge-all Delete everything under ~/.ctxdb/
|
|
1319
|
+
--json Machine-readable JSON output
|
|
1320
|
+
|
|
1321
|
+
NOTE
|
|
1322
|
+
Does not run \`npm uninstall\`. After uninstall completes, remove the
|
|
1323
|
+
npm package separately: npm uninstall -g @aliyunrds/ctxdb @aliyunrds/ctxdb-shared
|
|
1324
|
+
`;
|
|
1325
|
+
function uninstall(args) {
|
|
1326
|
+
if (args.flags.help || args.flags.h) {
|
|
1327
|
+
process.stdout.write(HELP2);
|
|
1328
|
+
return 0;
|
|
1329
|
+
}
|
|
1330
|
+
const json = !!args.flags.json;
|
|
1331
|
+
const agentFlag = args.flags.agent;
|
|
1332
|
+
if (agentFlag !== void 0) {
|
|
1333
|
+
if (!isAgentSlug(agentFlag)) {
|
|
1334
|
+
process.stderr.write(
|
|
1335
|
+
`unknown --agent: ${String(agentFlag)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)
|
|
1336
|
+
`
|
|
1337
|
+
);
|
|
1338
|
+
return 2;
|
|
1339
|
+
}
|
|
1340
|
+
const result2 = runRemove(agentFlag);
|
|
1341
|
+
printResult(result2, json, `uninstall --agent ${agentFlag}`);
|
|
1342
|
+
if (!json && result2.hints) {
|
|
1343
|
+
for (const h of result2.hints) process.stderr.write(`
|
|
1344
|
+
${h}
|
|
1345
|
+
`);
|
|
1346
|
+
}
|
|
1347
|
+
return result2.ok ? 0 : 1;
|
|
1348
|
+
}
|
|
1301
1349
|
const result = runTeardown({
|
|
1302
1350
|
purgeConfig: !!args.flags["purge-config"],
|
|
1303
1351
|
purgeLogs: !!args.flags["purge-logs"],
|
|
1304
1352
|
purgeAll: !!args.flags["purge-all"]
|
|
1305
1353
|
});
|
|
1306
|
-
|
|
1307
|
-
printResult(result, json);
|
|
1354
|
+
printResult(result, json, "uninstall");
|
|
1308
1355
|
if (!json && result.hints) {
|
|
1309
1356
|
for (const h of result.hints) process.stderr.write(`
|
|
1310
1357
|
${h}
|
|
@@ -1313,10 +1360,145 @@ ${h}
|
|
|
1313
1360
|
return result.ok ? 0 : 1;
|
|
1314
1361
|
}
|
|
1315
1362
|
|
|
1363
|
+
// src/lib/self-update.ts
|
|
1364
|
+
import { spawnSync } from "child_process";
|
|
1365
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1366
|
+
import { dirname as dirname2 } from "path";
|
|
1367
|
+
var PACKAGE_NAME = "@aliyunrds/ctxdb";
|
|
1368
|
+
var NPM_VIEW_TIMEOUT_MS = 1e4;
|
|
1369
|
+
function detectInstallMethod() {
|
|
1370
|
+
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
1371
|
+
return dir.includes("/node_modules/") ? "npm" : "unknown";
|
|
1372
|
+
}
|
|
1373
|
+
function checkLatestVersion(currentVersion) {
|
|
1374
|
+
const result = spawnSync("npm", ["view", PACKAGE_NAME, "version"], {
|
|
1375
|
+
encoding: "utf-8",
|
|
1376
|
+
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
1377
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1378
|
+
});
|
|
1379
|
+
if (result.status !== 0 || !result.stdout?.trim()) {
|
|
1380
|
+
return {
|
|
1381
|
+
latest: null,
|
|
1382
|
+
current: currentVersion,
|
|
1383
|
+
isNewer: false,
|
|
1384
|
+
error: result.stderr?.trim() || `npm view exited with code ${result.status}`
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
const latest = result.stdout.trim();
|
|
1388
|
+
return {
|
|
1389
|
+
latest,
|
|
1390
|
+
current: currentVersion,
|
|
1391
|
+
isNewer: isNewerVersion(latest, currentVersion)
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
function isNewerVersion(candidate, current) {
|
|
1395
|
+
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
1396
|
+
const [cMaj = 0, cMin = 0, cPat = 0] = parse(candidate);
|
|
1397
|
+
const [uMaj = 0, uMin = 0, uPat = 0] = parse(current);
|
|
1398
|
+
if (cMaj !== uMaj) return cMaj > uMaj;
|
|
1399
|
+
if (cMin !== uMin) return cMin > uMin;
|
|
1400
|
+
return cPat > uPat;
|
|
1401
|
+
}
|
|
1402
|
+
function runSelfUpdate(currentVersion, passthroughArgs = []) {
|
|
1403
|
+
const method = detectInstallMethod();
|
|
1404
|
+
if (method !== "npm") {
|
|
1405
|
+
return {
|
|
1406
|
+
ok: false,
|
|
1407
|
+
updated: false,
|
|
1408
|
+
fromVersion: currentVersion,
|
|
1409
|
+
error: "ctxdb was not installed via npm. Update manually with your package manager."
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
const check = checkLatestVersion(currentVersion);
|
|
1413
|
+
if (check.error) {
|
|
1414
|
+
process.stderr.write(`ctxdb: npm view failed (${check.error}), skipping self-update
|
|
1415
|
+
`);
|
|
1416
|
+
return { ok: true, updated: false, fromVersion: currentVersion };
|
|
1417
|
+
}
|
|
1418
|
+
if (!check.isNewer) {
|
|
1419
|
+
process.stderr.write(`ctxdb: package already up to date (v${currentVersion})
|
|
1420
|
+
`);
|
|
1421
|
+
return { ok: true, updated: false, fromVersion: currentVersion };
|
|
1422
|
+
}
|
|
1423
|
+
process.stderr.write(`ctxdb: updating ${PACKAGE_NAME} v${currentVersion} \u2192 v${check.latest}...
|
|
1424
|
+
`);
|
|
1425
|
+
const install = spawnSync("npm", ["install", "-g", `${PACKAGE_NAME}@${check.latest}`], {
|
|
1426
|
+
stdio: "inherit",
|
|
1427
|
+
encoding: "utf-8"
|
|
1428
|
+
});
|
|
1429
|
+
if (install.status !== 0) {
|
|
1430
|
+
return {
|
|
1431
|
+
ok: false,
|
|
1432
|
+
updated: false,
|
|
1433
|
+
fromVersion: currentVersion,
|
|
1434
|
+
error: `npm install -g failed with exit code ${install.status}`
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
process.stderr.write(`ctxdb: package updated to v${check.latest}, refreshing skills/hooks...
|
|
1438
|
+
`);
|
|
1439
|
+
const upgradeArgs = ["upgrade", ...passthroughArgs];
|
|
1440
|
+
const refresh = spawnSync("ctxdb", upgradeArgs, {
|
|
1441
|
+
stdio: "inherit",
|
|
1442
|
+
encoding: "utf-8"
|
|
1443
|
+
});
|
|
1444
|
+
return {
|
|
1445
|
+
ok: refresh.status === 0,
|
|
1446
|
+
updated: true,
|
|
1447
|
+
fromVersion: currentVersion,
|
|
1448
|
+
toVersion: check.latest,
|
|
1449
|
+
error: refresh.status !== 0 ? `ctxdb upgrade exited with code ${refresh.status}` : void 0
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1316
1453
|
// src/cli/upgrade.ts
|
|
1454
|
+
var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
|
|
1455
|
+
|
|
1456
|
+
USAGE
|
|
1457
|
+
ctxdb upgrade [--agent <qoder|codex|claude>] [--self-update] [--json]
|
|
1458
|
+
|
|
1459
|
+
BEHAVIOR
|
|
1460
|
+
Refreshes skill files and hook entries for configured agents without
|
|
1461
|
+
touching credentials. Designed for the post-npm-update path.
|
|
1462
|
+
|
|
1463
|
+
Without --agent: upgrades all configured built-in agents.
|
|
1464
|
+
With --agent: upgrades only the specified agent.
|
|
1465
|
+
|
|
1466
|
+
FLAGS
|
|
1467
|
+
--agent <name> Target a single agent (qoder|codex|claude)
|
|
1468
|
+
--self-update Also pull latest package from npm before upgrading
|
|
1469
|
+
--json Machine-readable JSON output
|
|
1470
|
+
`;
|
|
1317
1471
|
function upgrade(args) {
|
|
1472
|
+
if (args.flags.help || args.flags.h) {
|
|
1473
|
+
process.stdout.write(HELP3);
|
|
1474
|
+
return 0;
|
|
1475
|
+
}
|
|
1476
|
+
const selfUpdate = !!args.flags["self-update"];
|
|
1477
|
+
if (selfUpdate) {
|
|
1478
|
+
const passthrough = [];
|
|
1479
|
+
const agentFlag2 = args.flags.agent;
|
|
1480
|
+
if (agentFlag2) passthrough.push("--agent", agentFlag2);
|
|
1481
|
+
if (args.flags.json) passthrough.push("--json");
|
|
1482
|
+
const result2 = runSelfUpdate(PACKAGE_VERSION, passthrough);
|
|
1483
|
+
if (!result2.ok) {
|
|
1484
|
+
if (args.flags.json) {
|
|
1485
|
+
process.stdout.write(JSON.stringify({ ok: false, error: result2.error }) + "\n");
|
|
1486
|
+
} else {
|
|
1487
|
+
process.stderr.write(`ctxdb: self-update failed: ${result2.error}
|
|
1488
|
+
`);
|
|
1489
|
+
}
|
|
1490
|
+
return 1;
|
|
1491
|
+
}
|
|
1492
|
+
if (result2.updated) {
|
|
1493
|
+
return 0;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1318
1496
|
const agentFlag = args.flags.agent;
|
|
1319
|
-
if (agentFlag
|
|
1497
|
+
if (agentFlag === "default") {
|
|
1498
|
+
process.stderr.write("nothing to upgrade for default agent (no hooks or skills)\n");
|
|
1499
|
+
return 0;
|
|
1500
|
+
}
|
|
1501
|
+
if (agentFlag && !isBuiltinAgent(agentFlag)) {
|
|
1320
1502
|
process.stderr.write(
|
|
1321
1503
|
`unknown --agent: ${agentFlag} (expected one of ${SUPPORTED_AGENTS.join(" / ")})
|
|
1322
1504
|
`
|
|
@@ -1324,6 +1506,12 @@ function upgrade(args) {
|
|
|
1324
1506
|
return 2;
|
|
1325
1507
|
}
|
|
1326
1508
|
const result = runUpgrade({ agent: agentFlag });
|
|
1509
|
+
if (result.ok) {
|
|
1510
|
+
try {
|
|
1511
|
+
writeInstalledPkgVersion(PACKAGE_VERSION);
|
|
1512
|
+
} catch {
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1327
1515
|
const json = !!args.flags.json;
|
|
1328
1516
|
printResult(result, json);
|
|
1329
1517
|
if (!json && result.hints) {
|
|
@@ -1334,238 +1522,69 @@ ${h}
|
|
|
1334
1522
|
return result.ok ? 0 : 1;
|
|
1335
1523
|
}
|
|
1336
1524
|
|
|
1337
|
-
// src/
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
".txt": "text/plain",
|
|
1362
|
-
".md": "text/markdown",
|
|
1363
|
-
".markdown": "text/markdown",
|
|
1364
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1365
|
-
".doc": "application/msword",
|
|
1366
|
-
".html": "text/html",
|
|
1367
|
-
".htm": "text/html",
|
|
1368
|
-
".json": "application/json",
|
|
1369
|
-
".csv": "text/csv",
|
|
1370
|
-
".xml": "application/xml",
|
|
1371
|
-
".rtf": "application/rtf"
|
|
1372
|
-
};
|
|
1373
|
-
async function listKnowledgeBases(client) {
|
|
1374
|
-
const resp = await client.get(KB_COLLECTION);
|
|
1375
|
-
if (Array.isArray(resp)) return resp;
|
|
1376
|
-
if (resp && typeof resp === "object") {
|
|
1377
|
-
const o = resp;
|
|
1378
|
-
return o.knowledge_bases ?? o.results ?? [];
|
|
1525
|
+
// src/cli/skill-cli.ts
|
|
1526
|
+
var SUPPORTED_TARGETS = Object.keys(SKILL_INSTALL_TARGETS);
|
|
1527
|
+
var HELP4 = `ctxdb skill install \u2014 install skill files to an agent's skill directory
|
|
1528
|
+
|
|
1529
|
+
USAGE
|
|
1530
|
+
ctxdb skill install --target <name>
|
|
1531
|
+
ctxdb skill install --path <dir>
|
|
1532
|
+
|
|
1533
|
+
TARGETS
|
|
1534
|
+
${SUPPORTED_TARGETS.join(", ")}
|
|
1535
|
+
|
|
1536
|
+
FLAGS
|
|
1537
|
+
--target <name> Install to a built-in agent's skill directory
|
|
1538
|
+
--path <dir> Install to a custom directory
|
|
1539
|
+
--json Machine-readable JSON output
|
|
1540
|
+
|
|
1541
|
+
Note: This command only installs skill files (contextdb, contextdb-memory,
|
|
1542
|
+
contextdb-knowledge). It does not write config or install hooks.
|
|
1543
|
+
To configure server connection, run: ctxdb setup --api-key=<key> --base-url=<url>
|
|
1544
|
+
`;
|
|
1545
|
+
function skillInstall(args) {
|
|
1546
|
+
if (args.flags.help || args.flags.h) {
|
|
1547
|
+
process.stdout.write(HELP4);
|
|
1548
|
+
return 0;
|
|
1379
1549
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (kb.name === kbNameOrId || kb.id === kbNameOrId) return kb;
|
|
1550
|
+
const json = !!args.flags.json;
|
|
1551
|
+
const target = args.flags.target;
|
|
1552
|
+
const customPath = args.flags.path;
|
|
1553
|
+
if (target && customPath) {
|
|
1554
|
+
fail("--target and --path are mutually exclusive", 2);
|
|
1386
1555
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
return client.postJson(KB_COLLECTION, { name: kbName, description: description || "" });
|
|
1391
|
-
}
|
|
1392
|
-
async function findOrCreateKb(client, kbName, description = "") {
|
|
1393
|
-
const existing = await findKb(client, kbName);
|
|
1394
|
-
if (existing) return { kb: existing, created: false };
|
|
1395
|
-
const created = await createKb(client, kbName, description);
|
|
1396
|
-
return { kb: created, created: true };
|
|
1397
|
-
}
|
|
1398
|
-
async function uploadText(client, kbId, docName, text, mimeType = "text/plain", filePath) {
|
|
1399
|
-
const body = {
|
|
1400
|
-
knowledge_base_id: kbId,
|
|
1401
|
-
name: docName,
|
|
1402
|
-
text,
|
|
1403
|
-
mime_type: mimeType
|
|
1404
|
-
};
|
|
1405
|
-
if (filePath !== void 0 && filePath !== "") body.file_path = filePath;
|
|
1406
|
-
return tryNewThenLegacy(
|
|
1407
|
-
DOCUMENTS,
|
|
1408
|
-
() => client.postJson(DOCUMENTS, body),
|
|
1409
|
-
() => {
|
|
1410
|
-
const { knowledge_base_id: _drop, ...legacyBody } = body;
|
|
1411
|
-
return client.postJson(`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents`, legacyBody);
|
|
1412
|
-
}
|
|
1413
|
-
);
|
|
1414
|
-
}
|
|
1415
|
-
async function uploadFile(client, kbId, localPath, options = {}) {
|
|
1416
|
-
const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
1417
|
-
const expanded = expandHome(localPath);
|
|
1418
|
-
if (!existsSync2(expanded)) throw new Error(`file not found: ${expanded}`);
|
|
1419
|
-
const stat = statSync2(expanded);
|
|
1420
|
-
if (!stat.isFile()) throw new Error(`not a file: ${expanded}`);
|
|
1421
|
-
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
1422
|
-
throw new Error(
|
|
1423
|
-
`file too large (${(stat.size / 1024 / 1024).toFixed(1)} MB), maximum is 100 MB`
|
|
1424
|
-
);
|
|
1556
|
+
if (!target && !customPath) {
|
|
1557
|
+
process.stderr.write(HELP4);
|
|
1558
|
+
return 2;
|
|
1425
1559
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
fields.file_path = options.filePath;
|
|
1436
|
-
}
|
|
1437
|
-
const uploadOpts = { timeoutMs: options.timeoutMs };
|
|
1438
|
-
return tryNewThenLegacy(
|
|
1439
|
-
FILES,
|
|
1440
|
-
() => client.postMultipart(
|
|
1441
|
-
FILES,
|
|
1442
|
-
fields,
|
|
1443
|
-
{ file: { filename, content, mimeType: mime } },
|
|
1444
|
-
uploadOpts
|
|
1445
|
-
),
|
|
1446
|
-
() => {
|
|
1447
|
-
const { knowledge_base_id: _drop, ...legacyFields } = fields;
|
|
1448
|
-
return client.postMultipart(
|
|
1449
|
-
`${KB_COLLECTION}/${encodeURIComponent(kbId)}/files`,
|
|
1450
|
-
legacyFields,
|
|
1451
|
-
{ file: { filename, content, mimeType: mime } },
|
|
1452
|
-
uploadOpts
|
|
1560
|
+
let targetRoot;
|
|
1561
|
+
if (customPath) {
|
|
1562
|
+
targetRoot = customPath;
|
|
1563
|
+
} else {
|
|
1564
|
+
const resolved = skillInstallTargetPath(target);
|
|
1565
|
+
if (!resolved) {
|
|
1566
|
+
fail(
|
|
1567
|
+
`unknown --target: ${target} (supported: ${SUPPORTED_TARGETS.join(", ")})`,
|
|
1568
|
+
2
|
|
1453
1569
|
);
|
|
1454
1570
|
}
|
|
1455
|
-
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
DOCUMENT_DETAIL,
|
|
1465
|
-
() => client.get(DOCUMENT_DETAIL, {
|
|
1466
|
-
knowledge_base_id: kbId,
|
|
1467
|
-
document_id: docId
|
|
1468
|
-
}),
|
|
1469
|
-
() => client.get(
|
|
1470
|
-
`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents/${encodeURIComponent(docId)}`
|
|
1471
|
-
)
|
|
1472
|
-
);
|
|
1473
|
-
}
|
|
1474
|
-
async function listDocuments(client, kbId) {
|
|
1475
|
-
const resp = await tryNewThenLegacy(
|
|
1476
|
-
DOCUMENTS,
|
|
1477
|
-
() => client.get(DOCUMENTS, { knowledge_base_id: kbId }),
|
|
1478
|
-
() => client.get(`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents`)
|
|
1479
|
-
);
|
|
1480
|
-
if (Array.isArray(resp)) return resp;
|
|
1481
|
-
if (resp && typeof resp === "object") {
|
|
1482
|
-
const o = resp;
|
|
1483
|
-
return o.documents ?? o.results ?? [];
|
|
1484
|
-
}
|
|
1485
|
-
return [];
|
|
1486
|
-
}
|
|
1487
|
-
async function pollIngest(client, kbId, docId, options = {}) {
|
|
1488
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_INGEST_TIMEOUT_MS;
|
|
1489
|
-
const intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
1490
|
-
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
1491
|
-
const now = options.now ?? (() => performance.now());
|
|
1492
|
-
const deadline = now() + timeoutMs;
|
|
1493
|
-
let doc = await getDocument(client, kbId, docId);
|
|
1494
|
-
let timedOut = false;
|
|
1495
|
-
while (ingestInFlight(doc)) {
|
|
1496
|
-
if (now() >= deadline) {
|
|
1497
|
-
timedOut = true;
|
|
1498
|
-
break;
|
|
1499
|
-
}
|
|
1500
|
-
await sleep(intervalMs);
|
|
1501
|
-
try {
|
|
1502
|
-
doc = await getDocument(client, kbId, docId);
|
|
1503
|
-
} catch {
|
|
1504
|
-
break;
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
if (doc && typeof doc === "object") {
|
|
1508
|
-
return { ...doc, _pollingTimedOut: timedOut };
|
|
1509
|
-
}
|
|
1510
|
-
return { _pollingTimedOut: timedOut };
|
|
1511
|
-
}
|
|
1512
|
-
function ingestInFlight(doc) {
|
|
1513
|
-
if (!doc || typeof doc !== "object") return false;
|
|
1514
|
-
const status2 = doc.ingest_status;
|
|
1515
|
-
return status2 === "processing" || status2 === "pending" || status2 === "in_progress";
|
|
1516
|
-
}
|
|
1517
|
-
function expandHome(p) {
|
|
1518
|
-
if (p.startsWith("~/") || p === "~") {
|
|
1519
|
-
return resolve(homedir3(), p.slice(2));
|
|
1520
|
-
}
|
|
1521
|
-
return resolve(p);
|
|
1522
|
-
}
|
|
1523
|
-
function compactChunk(raw) {
|
|
1524
|
-
const c = raw ?? {};
|
|
1525
|
-
const content = c.content_with_weight ?? c.content ?? c.chunk ?? c.text ?? "";
|
|
1526
|
-
const doc_name = c.docnm_kwd ?? c.doc_name ?? c.docnm ?? "";
|
|
1527
|
-
const kbRaw = c.kb_id ?? c.dataset_id;
|
|
1528
|
-
const kb_id = Array.isArray(kbRaw) ? typeof kbRaw[0] === "string" ? kbRaw[0] : "" : typeof kbRaw === "string" ? kbRaw : "";
|
|
1529
|
-
const docIdRaw = c.doc_id;
|
|
1530
|
-
const scoreRaw = c.similarity ?? c.rerank_score;
|
|
1531
|
-
const tagsRaw = c.tag_kwd;
|
|
1532
|
-
const out = {
|
|
1533
|
-
content,
|
|
1534
|
-
doc_name,
|
|
1535
|
-
kb_id,
|
|
1536
|
-
score: typeof scoreRaw === "number" ? scoreRaw : 0
|
|
1571
|
+
targetRoot = resolved;
|
|
1572
|
+
}
|
|
1573
|
+
const result = installSkillsTo(targetRoot);
|
|
1574
|
+
const ok = result.ok;
|
|
1575
|
+
const output = {
|
|
1576
|
+
ok,
|
|
1577
|
+
target: target ?? null,
|
|
1578
|
+
path: targetRoot,
|
|
1579
|
+
steps: result.steps
|
|
1537
1580
|
};
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1581
|
+
printResult(output, json);
|
|
1582
|
+
if (ok && !json) {
|
|
1583
|
+
process.stderr.write(
|
|
1584
|
+
"\nNote: This command only installs skill files.\nTo configure server connection, run:\n ctxdb setup --api-key=<key> --base-url=<url>\n"
|
|
1585
|
+
);
|
|
1543
1586
|
}
|
|
1544
|
-
return
|
|
1545
|
-
}
|
|
1546
|
-
function compactKbQueryResponse(raw) {
|
|
1547
|
-
if (!raw || typeof raw !== "object") return { chunks: [] };
|
|
1548
|
-
const r = raw;
|
|
1549
|
-
const chunksIn = Array.isArray(r.chunks) ? r.chunks : [];
|
|
1550
|
-
const out = {
|
|
1551
|
-
chunks: chunksIn.map(compactChunk)
|
|
1552
|
-
};
|
|
1553
|
-
if (typeof r.total === "number") out.total = r.total;
|
|
1554
|
-
return out;
|
|
1555
|
-
}
|
|
1556
|
-
function minimalChunk(raw) {
|
|
1557
|
-
const c = compactChunk(raw);
|
|
1558
|
-
return { content: c.content, score: c.score };
|
|
1559
|
-
}
|
|
1560
|
-
function minimalKbQueryResponse(raw) {
|
|
1561
|
-
if (!raw || typeof raw !== "object") return { chunks: [] };
|
|
1562
|
-
const r = raw;
|
|
1563
|
-
const chunksIn = Array.isArray(r.chunks) ? r.chunks : [];
|
|
1564
|
-
const out = {
|
|
1565
|
-
chunks: chunksIn.map(minimalChunk)
|
|
1566
|
-
};
|
|
1567
|
-
if (typeof r.total === "number") out.total = r.total;
|
|
1568
|
-
return out;
|
|
1587
|
+
return ok ? 0 : 1;
|
|
1569
1588
|
}
|
|
1570
1589
|
|
|
1571
1590
|
// src/cli/memory.ts
|
|
@@ -1698,25 +1717,20 @@ async function kbUploadText(args) {
|
|
|
1698
1717
|
const text = args.flags["text"];
|
|
1699
1718
|
if (!kbName || !docName || typeof text !== "string") {
|
|
1700
1719
|
fail(
|
|
1701
|
-
"usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--
|
|
1720
|
+
"usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--file-path=<server-logical-path>] [--no-wait]"
|
|
1702
1721
|
);
|
|
1703
1722
|
}
|
|
1704
1723
|
const ctx = buildContext(args);
|
|
1705
|
-
const description = args.flags["kb-description"] ?? "";
|
|
1706
1724
|
const filePath = typeof args.flags["file-path"] === "string" ? args.flags["file-path"] : void 0;
|
|
1707
|
-
const
|
|
1708
|
-
const doc = await uploadText(ctx.client, kb.id, docName, text, "text/plain", filePath);
|
|
1725
|
+
const doc = await uploadText(ctx.client, kbName, docName, text, "text/plain", filePath);
|
|
1709
1726
|
if (args.flags["no-wait"]) {
|
|
1710
|
-
printResult({
|
|
1727
|
+
printResult({ document: doc }, !!args.flags.json);
|
|
1711
1728
|
return 0;
|
|
1712
1729
|
}
|
|
1713
|
-
const final = await pollIngest(ctx.client,
|
|
1730
|
+
const final = await pollIngest(ctx.client, kbName, doc.id, {
|
|
1714
1731
|
timeoutMs: DEFAULT_INGEST_TIMEOUT_MS
|
|
1715
1732
|
});
|
|
1716
|
-
printResult(
|
|
1717
|
-
{ kb, kb_created: created, document: final },
|
|
1718
|
-
!!args.flags.json
|
|
1719
|
-
);
|
|
1733
|
+
printResult({ document: final }, !!args.flags.json);
|
|
1720
1734
|
return 0;
|
|
1721
1735
|
}
|
|
1722
1736
|
async function kbUploadFile(args) {
|
|
@@ -1727,23 +1741,28 @@ async function kbUploadFile(args) {
|
|
|
1727
1741
|
);
|
|
1728
1742
|
}
|
|
1729
1743
|
const ctx = buildContext(args);
|
|
1730
|
-
const
|
|
1731
|
-
const doc = await uploadFile(ctx.client, kb.id, localPath, {
|
|
1744
|
+
const doc = await uploadFile(ctx.client, kbName, localPath, {
|
|
1732
1745
|
docName: typeof args.flags["doc-name"] === "string" ? args.flags["doc-name"] : void 0,
|
|
1733
1746
|
filePath: typeof args.flags["file-path"] === "string" ? args.flags["file-path"] : void 0,
|
|
1734
1747
|
timeoutMs: DEFAULT_FILE_INGEST_TIMEOUT_MS
|
|
1735
1748
|
});
|
|
1736
1749
|
if (args.flags["no-wait"]) {
|
|
1737
|
-
printResult({
|
|
1750
|
+
printResult({ document: doc }, !!args.flags.json);
|
|
1738
1751
|
return 0;
|
|
1739
1752
|
}
|
|
1740
|
-
const final = await pollIngest(ctx.client,
|
|
1753
|
+
const final = await pollIngest(ctx.client, kbName, doc.id, {
|
|
1741
1754
|
timeoutMs: DEFAULT_FILE_INGEST_TIMEOUT_MS
|
|
1742
1755
|
});
|
|
1743
|
-
printResult(
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1756
|
+
printResult({ document: final }, !!args.flags.json);
|
|
1757
|
+
return 0;
|
|
1758
|
+
}
|
|
1759
|
+
async function kbCreate(args) {
|
|
1760
|
+
const kbName = args.positional[0];
|
|
1761
|
+
if (!kbName) fail("usage: ctxdb kb create <kb-name> [--description=<desc>]");
|
|
1762
|
+
const ctx = buildContext(args);
|
|
1763
|
+
const description = typeof args.flags["description"] === "string" ? args.flags["description"] : "";
|
|
1764
|
+
const kb = await createKb(ctx.client, kbName, description);
|
|
1765
|
+
printResult(kb, !!args.flags.json);
|
|
1747
1766
|
return 0;
|
|
1748
1767
|
}
|
|
1749
1768
|
async function kbList(args) {
|
|
@@ -1753,28 +1772,24 @@ async function kbList(args) {
|
|
|
1753
1772
|
return 0;
|
|
1754
1773
|
}
|
|
1755
1774
|
async function kbDocumentsList(args) {
|
|
1756
|
-
const
|
|
1757
|
-
if (!
|
|
1775
|
+
const kbName = args.positional[0];
|
|
1776
|
+
if (!kbName) fail("usage: ctxdb kb documents-list <kb-name>");
|
|
1758
1777
|
const ctx = buildContext(args);
|
|
1759
|
-
const
|
|
1760
|
-
if (!kb) fail(`KB not found: ${kbNameOrId}`);
|
|
1761
|
-
const docs = await listDocuments(ctx.client, kb.id);
|
|
1778
|
+
const docs = await listDocuments(ctx.client, kbName);
|
|
1762
1779
|
printResult(
|
|
1763
|
-
{
|
|
1780
|
+
{ documents: docs, count: docs.length },
|
|
1764
1781
|
!!args.flags.json
|
|
1765
1782
|
);
|
|
1766
1783
|
return 0;
|
|
1767
1784
|
}
|
|
1768
1785
|
async function kbDocumentGet(args) {
|
|
1769
|
-
const [
|
|
1770
|
-
if (!
|
|
1771
|
-
fail("usage: ctxdb kb document-get <kb-name
|
|
1786
|
+
const [kbName, docId] = args.positional;
|
|
1787
|
+
if (!kbName || !docId) {
|
|
1788
|
+
fail("usage: ctxdb kb document-get <kb-name> <doc-id>");
|
|
1772
1789
|
}
|
|
1773
1790
|
const ctx = buildContext(args);
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
const doc = await getDocument(ctx.client, kb.id, docId);
|
|
1777
|
-
printResult({ kb, document: doc }, !!args.flags.json);
|
|
1791
|
+
const doc = await getDocument(ctx.client, kbName, docId);
|
|
1792
|
+
printResult({ document: doc }, !!args.flags.json);
|
|
1778
1793
|
return 0;
|
|
1779
1794
|
}
|
|
1780
1795
|
async function kbSearch(args) {
|
|
@@ -1785,16 +1800,10 @@ async function kbSearch(args) {
|
|
|
1785
1800
|
);
|
|
1786
1801
|
}
|
|
1787
1802
|
const ctx = buildContext(args);
|
|
1788
|
-
const
|
|
1789
|
-
const datasetIds = [];
|
|
1790
|
-
for (const name of kbList2) {
|
|
1791
|
-
const kb = await findKb(ctx.client, name);
|
|
1792
|
-
if (!kb) fail(`KB not found: ${name}`);
|
|
1793
|
-
datasetIds.push(kb.id);
|
|
1794
|
-
}
|
|
1803
|
+
const kbNames = (typeof args.flags.kb === "string" ? args.flags.kb : "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
1795
1804
|
const body = {
|
|
1796
1805
|
question: query,
|
|
1797
|
-
|
|
1806
|
+
knowledge_base_names: kbNames
|
|
1798
1807
|
};
|
|
1799
1808
|
if (typeof args.flags["top-k"] === "string") {
|
|
1800
1809
|
const n = Number(args.flags["top-k"]);
|
|
@@ -1811,31 +1820,32 @@ async function kbSearch(args) {
|
|
|
1811
1820
|
}
|
|
1812
1821
|
|
|
1813
1822
|
// src/cli/main.ts
|
|
1814
|
-
var
|
|
1823
|
+
var HELP5 = `ctxdb v${PACKAGE_VERSION} \u2014 RDS ContextDatabase CLI (multi-agent)
|
|
1815
1824
|
|
|
1816
1825
|
USAGE
|
|
1817
1826
|
ctxdb <command> [args] [--flags]
|
|
1818
1827
|
|
|
1819
1828
|
COMMANDS
|
|
1820
|
-
|
|
1821
|
-
status [--agent <qoder|codex|claude>] [--json]
|
|
1822
|
-
ping [--agent <qoder|codex|claude>] [--json]
|
|
1823
|
-
setup --agent <qoder|codex|claude>
|
|
1829
|
+
setup [--agent <qoder|codex|claude>]
|
|
1824
1830
|
[--api-key=K] [--base-url=URL] [--user-id=ID]
|
|
1825
|
-
[--no-install-skill] [--no-validate] [--json]
|
|
1831
|
+
[--no-install-skill] [--no-validate] [--json]
|
|
1832
|
+
Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
|
|
1826
1833
|
Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
|
|
1827
1834
|
Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
|
|
1828
1835
|
Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
|
|
1829
|
-
|
|
1830
|
-
|
|
1836
|
+
status [--agent <name>] [--json]
|
|
1837
|
+
ping [--agent <name>] [--json]
|
|
1838
|
+
uninstall [--agent <name>] [--purge-config] [--purge-logs] [--purge-all] [--json]
|
|
1839
|
+
teardown (alias for uninstall)
|
|
1840
|
+
Without --agent: removes hooks + skills for every agent.
|
|
1841
|
+
With --agent: removes only that agent's hooks + skill + config section.
|
|
1831
1842
|
--purge-config also deletes ~/.ctxdb/ctxdb.json.
|
|
1832
1843
|
--purge-logs also deletes ~/.ctxdb/logs/.
|
|
1833
1844
|
--purge-all deletes everything under ~/.ctxdb/
|
|
1834
1845
|
(does not run npm uninstall).
|
|
1835
|
-
upgrade [--agent <name>] [--json]
|
|
1836
|
-
Refresh skills + hooks
|
|
1837
|
-
|
|
1838
|
-
Does not modify credentials.
|
|
1846
|
+
upgrade|update [--agent <name>] [--self-update] [--json]
|
|
1847
|
+
Refresh skills + hooks. --self-update also
|
|
1848
|
+
pulls latest package from npm.
|
|
1839
1849
|
|
|
1840
1850
|
memory add <text> [--agent=<name>] [--user-id=...] [--metadata=K1=V1,K2=V2] [--no-infer]
|
|
1841
1851
|
memory search <query> [--agent=<name>] [--top-k=10] [--threshold=0.4] [--knowledge]
|
|
@@ -1849,13 +1859,14 @@ COMMANDS
|
|
|
1849
1859
|
memory update <memory-id> --text=<new-text> [--agent=<name>]
|
|
1850
1860
|
memory delete <memory-id> | --all [--agent=<name>]
|
|
1851
1861
|
|
|
1862
|
+
kb create <kb-name> [--description=<desc>] [--agent=<name>]
|
|
1852
1863
|
kb upload-text <kb-name> <doc-name> --text=<body> [--agent=<name>]
|
|
1853
|
-
[--
|
|
1864
|
+
[--file-path=<server-logical-path>] [--no-wait]
|
|
1854
1865
|
kb upload-file <kb-name> <local-path> [--agent=<name>] [--doc-name=...]
|
|
1855
1866
|
[--file-path=<server-logical-path>] [--no-wait]
|
|
1856
1867
|
kb list [--agent=<name>]
|
|
1857
|
-
kb documents-list <kb-name
|
|
1858
|
-
kb document-get <kb-name
|
|
1868
|
+
kb documents-list <kb-name> [--agent=<name>]
|
|
1869
|
+
kb document-get <kb-name> <doc-id> [--agent=<name>]
|
|
1859
1870
|
kb search <query> [--agent=<name>] [--kb=name1,name2] [--top-k=N] [--threshold=F]
|
|
1860
1871
|
[--verbose] [--raw]
|
|
1861
1872
|
Standalone KB recall (independent of memory).
|
|
@@ -1864,19 +1875,33 @@ COMMANDS
|
|
|
1864
1875
|
doc_id?/tags? for citation.
|
|
1865
1876
|
--raw: server response verbatim (curl parity).
|
|
1866
1877
|
|
|
1878
|
+
skill install --target <qoder|codex|claude|openclaw|hermes> [--json]
|
|
1879
|
+
skill install --path <dir> [--json]
|
|
1880
|
+
Install skill files (contextdb-memory,
|
|
1881
|
+
contextdb-knowledge) to an agent's skill directory.
|
|
1882
|
+
Does not write config or install hooks.
|
|
1883
|
+
|
|
1884
|
+
AGENT RESOLUTION (when --agent is omitted):
|
|
1885
|
+
1. CTXDB_AGENT env var
|
|
1886
|
+
2. agents.default section in ~/.ctxdb/ctxdb.json
|
|
1887
|
+
|
|
1867
1888
|
ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
|
|
1868
1889
|
CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
|
|
1869
1890
|
|
|
1870
|
-
CONFIG: ~/.ctxdb/ctxdb.json (agents.qoder / agents.codex / agents.claude)
|
|
1891
|
+
CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.codex / agents.claude)
|
|
1871
1892
|
LOGS: ~/.ctxdb/logs/ctxdb.log
|
|
1872
1893
|
`;
|
|
1873
1894
|
var ROUTES = {
|
|
1874
|
-
|
|
1895
|
+
setup,
|
|
1875
1896
|
status,
|
|
1876
1897
|
ping,
|
|
1877
|
-
|
|
1878
|
-
teardown,
|
|
1898
|
+
uninstall,
|
|
1899
|
+
teardown: uninstall,
|
|
1879
1900
|
upgrade,
|
|
1901
|
+
update: upgrade,
|
|
1902
|
+
skill: {
|
|
1903
|
+
install: skillInstall
|
|
1904
|
+
},
|
|
1880
1905
|
memory: {
|
|
1881
1906
|
add: memoryAdd,
|
|
1882
1907
|
search: memorySearch,
|
|
@@ -1886,6 +1911,7 @@ var ROUTES = {
|
|
|
1886
1911
|
delete: memoryDelete
|
|
1887
1912
|
},
|
|
1888
1913
|
kb: {
|
|
1914
|
+
create: kbCreate,
|
|
1889
1915
|
"upload-text": kbUploadText,
|
|
1890
1916
|
"upload-file": kbUploadFile,
|
|
1891
1917
|
list: kbList,
|
|
@@ -1896,7 +1922,7 @@ var ROUTES = {
|
|
|
1896
1922
|
};
|
|
1897
1923
|
async function main(argv = process.argv.slice(2)) {
|
|
1898
1924
|
if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
|
|
1899
|
-
process.stdout.write(
|
|
1925
|
+
process.stdout.write(HELP5);
|
|
1900
1926
|
return 0;
|
|
1901
1927
|
}
|
|
1902
1928
|
if (argv[0] === "-v" || argv[0] === "--version") {
|
|
@@ -1909,7 +1935,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1909
1935
|
if (!route) {
|
|
1910
1936
|
process.stderr.write(`unknown command: ${first}
|
|
1911
1937
|
`);
|
|
1912
|
-
process.stderr.write(
|
|
1938
|
+
process.stderr.write(HELP5);
|
|
1913
1939
|
return 2;
|
|
1914
1940
|
}
|
|
1915
1941
|
if (typeof route === "function") {
|
|
@@ -1922,6 +1948,13 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1922
1948
|
);
|
|
1923
1949
|
return 2;
|
|
1924
1950
|
}
|
|
1951
|
+
if (rest[0] === "-h" || rest[0] === "--help") {
|
|
1952
|
+
process.stdout.write(
|
|
1953
|
+
`usage: ctxdb ${first} <${Object.keys(route).join("|")}> ...
|
|
1954
|
+
`
|
|
1955
|
+
);
|
|
1956
|
+
return 0;
|
|
1957
|
+
}
|
|
1925
1958
|
const [sub, ...subRest] = rest;
|
|
1926
1959
|
const handler = route[sub];
|
|
1927
1960
|
if (!handler) {
|