@aliyunrds/ctxdb 1.0.9-beta.1 → 1.0.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -2
- package/dist/{chunk-U4DRH4NZ.js → chunk-5HPYYOPH.js} +2 -3
- package/dist/chunk-LASAMQQY.js +259 -0
- package/dist/{chunk-3MEBQDGU.js → chunk-QUPLZ7KF.js} +8 -8
- package/dist/{chunk-FD3IPF6R.js → chunk-SG4LQTOF.js} +4 -4
- package/dist/{chunk-T5ZJEPR4.js → chunk-SZ3AZDMU.js} +5 -5
- package/dist/chunk-TZFDXKLJ.js +3278 -0
- package/dist/{chunk-V7IZ2UYJ.js → chunk-VMBCSFGF.js} +4 -4
- package/dist/{chunk-7TPYIT63.js → chunk-XWJAZAMN.js} +3 -3
- package/dist/cli/main.js +1189 -212
- package/dist/hooks/hermes-post-llm-call.js +25 -6
- package/dist/hooks/hermes-pre-llm-call.js +46 -13
- package/dist/hooks/session-end.js +35 -0
- package/dist/hooks/session-start.js +20 -10
- package/dist/hooks/stop.js +15 -5
- package/dist/hooks/user-prompt-submit.js +20 -9
- package/dist/opencode/index.js +22792 -1663
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +9 -0
- package/dist/setup/skills/contextdb-skills/SKILL.md +100 -0
- package/dist/workers/version-check.js +3 -3
- package/package.json +11 -2
- package/dist/chunk-2ROMQSPA.js +0 -569
- package/dist/chunk-2U7G6UXE.js +0 -1205
package/dist/cli/main.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
beginUpdateNotification,
|
|
4
|
+
completeUpdateNotification,
|
|
5
|
+
runSelfUpdate
|
|
6
|
+
} from "../chunk-QUPLZ7KF.js";
|
|
2
7
|
import {
|
|
3
8
|
DEFAULT_FILE_INGEST_TIMEOUT_MS,
|
|
4
9
|
DEFAULT_FILE_UPLOAD_TIMEOUT_MS,
|
|
@@ -31,13 +36,14 @@ import {
|
|
|
31
36
|
uploadFile,
|
|
32
37
|
uploadText,
|
|
33
38
|
validateLocalFile
|
|
34
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-SZ3AZDMU.js";
|
|
35
40
|
import {
|
|
36
41
|
AGENT_CHOICES,
|
|
37
42
|
CtxdbError,
|
|
38
43
|
DEFAULT_BASE_URL,
|
|
39
44
|
DEFAULT_USER_ID,
|
|
40
45
|
HttpClient,
|
|
46
|
+
MAX_ZIP,
|
|
41
47
|
PACKAGE_NAME,
|
|
42
48
|
PACKAGE_VERSION,
|
|
43
49
|
SUPPORTED_AGENTS,
|
|
@@ -46,36 +52,61 @@ import {
|
|
|
46
52
|
agentHomeDir,
|
|
47
53
|
agentHomeDirs,
|
|
48
54
|
agentPlatformSupport,
|
|
55
|
+
autoCollectAllowed,
|
|
56
|
+
bindSession,
|
|
57
|
+
bindingPath,
|
|
58
|
+
businessEvents,
|
|
59
|
+
catalogContext,
|
|
60
|
+
configFileName,
|
|
49
61
|
configuredAgents,
|
|
50
62
|
defaultConfigPath,
|
|
51
63
|
detectInstalledAgents,
|
|
64
|
+
expireSessionEvidence,
|
|
65
|
+
getBinding,
|
|
52
66
|
hasConfiguredAgent,
|
|
53
67
|
inspectAgentHomes,
|
|
54
68
|
inspectConfig,
|
|
69
|
+
inspectZip,
|
|
55
70
|
isAgentSlug,
|
|
56
71
|
isBuiltinAgent,
|
|
57
72
|
isDataApiReady,
|
|
58
73
|
load,
|
|
74
|
+
loadSkill,
|
|
75
|
+
privatePath,
|
|
76
|
+
publishPackage,
|
|
77
|
+
readPackage,
|
|
78
|
+
readPrivate,
|
|
79
|
+
readSpool,
|
|
80
|
+
readState,
|
|
81
|
+
refreshManifest,
|
|
59
82
|
removeAgent,
|
|
83
|
+
requireUuid,
|
|
60
84
|
resolveDataApiCredential,
|
|
85
|
+
resumeSubmission,
|
|
86
|
+
retryFeedback,
|
|
61
87
|
save,
|
|
88
|
+
sha256,
|
|
89
|
+
skillRoot,
|
|
90
|
+
spoolDirectory,
|
|
91
|
+
threshold,
|
|
92
|
+
unbox,
|
|
93
|
+
updateAutoSkillEvolution,
|
|
62
94
|
updateConfiguredDebug,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} from "../chunk-3MEBQDGU.js";
|
|
95
|
+
validateEvidence,
|
|
96
|
+
verifiedClient,
|
|
97
|
+
withStateLock,
|
|
98
|
+
writeInstalledPkgVersion,
|
|
99
|
+
writeState
|
|
100
|
+
} from "../chunk-TZFDXKLJ.js";
|
|
70
101
|
import {
|
|
71
102
|
DISTRIBUTION_MANIFEST
|
|
72
103
|
} from "../chunk-5ITT5GSP.js";
|
|
73
104
|
import "../chunk-PI2SUS3M.js";
|
|
74
105
|
|
|
75
106
|
// src/cli/main.ts
|
|
76
|
-
import { realpathSync } from "fs";
|
|
77
|
-
import { basename as basename3, resolve as
|
|
78
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
107
|
+
import { realpathSync } from "node:fs";
|
|
108
|
+
import { basename as basename3, resolve as resolve4 } from "node:path";
|
|
109
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
79
110
|
|
|
80
111
|
// src/cli/util.ts
|
|
81
112
|
var REPEATABLE_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -100,6 +131,13 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
100
131
|
"purge-config",
|
|
101
132
|
"purge-logs",
|
|
102
133
|
"purge-all",
|
|
134
|
+
// skills
|
|
135
|
+
"local",
|
|
136
|
+
"editable",
|
|
137
|
+
"remote",
|
|
138
|
+
"resume",
|
|
139
|
+
"apply",
|
|
140
|
+
"cache",
|
|
103
141
|
// memory
|
|
104
142
|
"no-infer",
|
|
105
143
|
// memory add
|
|
@@ -155,10 +193,10 @@ function parseArgs(argv, booleanFlags = BOOLEAN_FLAGS) {
|
|
|
155
193
|
const positional2 = [];
|
|
156
194
|
const flags = {};
|
|
157
195
|
const repeatedFlags = {};
|
|
158
|
-
const setFlag = (name,
|
|
159
|
-
flags[name] =
|
|
196
|
+
const setFlag = (name, value2) => {
|
|
197
|
+
flags[name] = value2;
|
|
160
198
|
if (REPEATABLE_FLAGS.has(name)) {
|
|
161
|
-
(repeatedFlags[name] ??= []).push(
|
|
199
|
+
(repeatedFlags[name] ??= []).push(value2);
|
|
162
200
|
}
|
|
163
201
|
};
|
|
164
202
|
let separatorSeen = false;
|
|
@@ -231,8 +269,8 @@ function isUpgradeResult(v) {
|
|
|
231
269
|
}
|
|
232
270
|
function isMultiAgentResult(v) {
|
|
233
271
|
if (typeof v !== "object" || v === null) return false;
|
|
234
|
-
const
|
|
235
|
-
return
|
|
272
|
+
const value2 = v;
|
|
273
|
+
return value2.mode === "all" && (value2.operation === "setup" || value2.operation === "status") && typeof value2.agents === "object" && value2.agents !== null && Array.isArray(value2.skipped) && typeof value2.summary === "object" && value2.summary !== null;
|
|
236
274
|
}
|
|
237
275
|
function isStatusResult(v) {
|
|
238
276
|
if (typeof v !== "object" || v === null) return false;
|
|
@@ -257,6 +295,7 @@ function formatUpgradeResult(r) {
|
|
|
257
295
|
for (const [name, result] of Object.entries(r.agents)) {
|
|
258
296
|
const icon = result.ok ? CHECK : CROSS;
|
|
259
297
|
lines.push(` ${icon} ${BOLD}${name}${RESET}`);
|
|
298
|
+
if (result.hook_activation) lines.push(` ${highlightInlineCode(result.hook_activation.next_action)}`);
|
|
260
299
|
for (const s of result.steps) {
|
|
261
300
|
const sIcon = s.ok ? CHECK : CROSS;
|
|
262
301
|
const detail = s.detail ? ` ${dim(s.detail)}` : "";
|
|
@@ -293,6 +332,7 @@ function formatMultiAgentResult(r) {
|
|
|
293
332
|
if (result.hooks_node !== void 0) {
|
|
294
333
|
lines.push(` ${DIM}hooks${RESET} ${result.hooks_node}`);
|
|
295
334
|
}
|
|
335
|
+
if (result.hook_activation) lines.push(` ${highlightInlineCode(result.hook_activation.next_action)}`);
|
|
296
336
|
if (result.ping_error) {
|
|
297
337
|
lines.push(` ${DIM}ping_error${RESET} ${result.ping_error}`);
|
|
298
338
|
}
|
|
@@ -316,6 +356,7 @@ function formatStatusResult(r) {
|
|
|
316
356
|
["base_url", r.base_url],
|
|
317
357
|
["user_id", r.user_id],
|
|
318
358
|
["api_key", r.api_key_set ? "set" : "not set"],
|
|
359
|
+
["auto_skill_evolution", r.auto_skill_evolution],
|
|
319
360
|
["access_token", r.access_token_set ? "set" : "not set"],
|
|
320
361
|
["credential_type", r.credential_type ?? "none"],
|
|
321
362
|
["auto_capture", r.auto_capture],
|
|
@@ -334,6 +375,12 @@ function formatStatusResult(r) {
|
|
|
334
375
|
display.push(["hooks_node", r.hooks_node]);
|
|
335
376
|
display.push(["hooks_node_ok", r.hooks_node_ok]);
|
|
336
377
|
}
|
|
378
|
+
if (r.hook_activation) {
|
|
379
|
+
const activation = r.hook_activation;
|
|
380
|
+
display.push(["hook_trust", activation.trust], ["runtime_ready", "\u5F85\u786E\u8BA4"], ["hook_config", activation.config_path]);
|
|
381
|
+
lines.push(`${BOLD}${highlightInlineCode(String(activation.next_action))}${RESET}
|
|
382
|
+
`);
|
|
383
|
+
}
|
|
337
384
|
if (r.ping_error) display.push(["ping_error", r.ping_error]);
|
|
338
385
|
const maxKey = Math.max(...display.map(([k]) => k.length));
|
|
339
386
|
for (const [k, v] of display) {
|
|
@@ -342,19 +389,19 @@ function formatStatusResult(r) {
|
|
|
342
389
|
}
|
|
343
390
|
return lines.join("\n");
|
|
344
391
|
}
|
|
345
|
-
function printResult(
|
|
392
|
+
function printResult(value2, json, command) {
|
|
346
393
|
if (json) {
|
|
347
|
-
process.stdout.write(JSON.stringify(
|
|
348
|
-
} else if (isMultiAgentResult(
|
|
349
|
-
process.stdout.write(formatMultiAgentResult(
|
|
350
|
-
} else if (isUpgradeResult(
|
|
351
|
-
process.stdout.write(formatUpgradeResult(
|
|
352
|
-
} else if (isSetupResult(
|
|
353
|
-
process.stdout.write(formatSetupResult(
|
|
354
|
-
} else if (isStatusResult(
|
|
355
|
-
process.stdout.write(formatStatusResult(
|
|
394
|
+
process.stdout.write(JSON.stringify(value2, null, 2) + "\n");
|
|
395
|
+
} else if (isMultiAgentResult(value2)) {
|
|
396
|
+
process.stdout.write(formatMultiAgentResult(value2) + "\n");
|
|
397
|
+
} else if (isUpgradeResult(value2)) {
|
|
398
|
+
process.stdout.write(formatUpgradeResult(value2) + "\n");
|
|
399
|
+
} else if (isSetupResult(value2)) {
|
|
400
|
+
process.stdout.write(formatSetupResult(value2, command ?? "setup") + "\n");
|
|
401
|
+
} else if (isStatusResult(value2)) {
|
|
402
|
+
process.stdout.write(formatStatusResult(value2) + "\n");
|
|
356
403
|
} else {
|
|
357
|
-
process.stdout.write(summarize(
|
|
404
|
+
process.stdout.write(summarize(value2) + "\n");
|
|
358
405
|
}
|
|
359
406
|
}
|
|
360
407
|
function agentFromFlags(flags) {
|
|
@@ -395,11 +442,11 @@ import {
|
|
|
395
442
|
rmdirSync,
|
|
396
443
|
unlinkSync,
|
|
397
444
|
statSync
|
|
398
|
-
} from "fs";
|
|
399
|
-
import { homedir } from "os";
|
|
400
|
-
import { join, dirname, resolve } from "path";
|
|
401
|
-
import { fileURLToPath } from "url";
|
|
402
|
-
import { execFileSync } from "child_process";
|
|
445
|
+
} from "node:fs";
|
|
446
|
+
import { homedir } from "node:os";
|
|
447
|
+
import { join, dirname, resolve } from "node:path";
|
|
448
|
+
import { fileURLToPath } from "node:url";
|
|
449
|
+
import { execFileSync } from "node:child_process";
|
|
403
450
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
404
451
|
import { setupAuthentication, oauthProfileStatus, validateDataBaseUrl } from "@aliyunrds/ctxdb-shared";
|
|
405
452
|
|
|
@@ -440,7 +487,7 @@ function skillInstallRoot(agent) {
|
|
|
440
487
|
return join(homedir(), ".workbuddy-ai", "skills");
|
|
441
488
|
}
|
|
442
489
|
}
|
|
443
|
-
var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
|
|
490
|
+
var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge", "contextdb-skills"];
|
|
444
491
|
var LEGACY_SKILL_DIRS = ["ctxdb"];
|
|
445
492
|
function agentSupportsHooks(agent) {
|
|
446
493
|
if (agent === "opencode" || agent === "hermes") return false;
|
|
@@ -470,7 +517,23 @@ var LEGACY_QODER_SKILL_DIRS = [
|
|
|
470
517
|
"rds-ctxdb-qoder",
|
|
471
518
|
"qoder-ctxdb"
|
|
472
519
|
];
|
|
473
|
-
var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart"];
|
|
520
|
+
var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart", "SessionEnd"];
|
|
521
|
+
var CODEX_HOOKS_TRUST_HINT = "\u5F85\u786E\u8BA4\uFF1ACodex Hook \u4FE1\u4EFB\u5C1A\u672A\u6838\u5B9E\uFF0C\u5B89\u88C5\u6210\u529F\u548C\u8FDE\u63A5\u6210\u529F\u4E0D\u4EE3\u8868\u81EA\u52A8\u63D0\u70BC\u5DF2\u5C31\u7EEA\u3002CLI \u8F93\u5165 `/hooks`\uFF1B\u684C\u9762\u7248\u8FDB\u5165 \u8BBE\u7F6E \u2192 `Hooks\uFF08\u94A9\u5B50\uFF09` \u2192 `\u7528\u6237\u914D\u7F6E`\uFF0C\u4EC5\u68C0\u67E5\u5E76\u4FE1\u4EFB `@aliyunrds/ctxdb` \u7684 `SessionStart`\u3001`UserPromptSubmit`\u3001`Stop`\u3001`SessionEnd`\u3002\u91CD\u542F\u5BA2\u6237\u7AEF\u540E\u65B0\u5EFA\u4F1A\u8BDD\u5E76\u53D1\u9001\u666E\u901A\u6D88\u606F\uFF0C\u518D\u8FD0\u884C `ctxdb skill status --agent codex --json` \u6838\u5BF9\u8BE5\u4F1A\u8BDD\u7684\u91C7\u96C6\u8BB0\u5F55\u3002";
|
|
522
|
+
function codexHookActivation() {
|
|
523
|
+
return {
|
|
524
|
+
trust: "unverified",
|
|
525
|
+
runtime_ready: null,
|
|
526
|
+
config_path: hookConfigPath("codex"),
|
|
527
|
+
required_events: [...HOOK_EVENTS],
|
|
528
|
+
next_action: CODEX_HOOKS_TRUST_HINT
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function codexHookDefinitions() {
|
|
532
|
+
const file = hookConfigPath("codex");
|
|
533
|
+
if (!existsSync(file)) return "[]";
|
|
534
|
+
const data = JSON.parse(readFileSync(file, "utf8"));
|
|
535
|
+
return JSON.stringify(Object.entries(data.hooks ?? {}).sort(([a], [b]) => a.localeCompare(b)).flatMap(([event, entries]) => Array.isArray(entries) ? entries.filter(entryIsCtxdb).map((entry) => [event, entry]) : []));
|
|
536
|
+
}
|
|
474
537
|
var MIN_NODE_MAJOR = 20;
|
|
475
538
|
function parseNodeMajor(version) {
|
|
476
539
|
const match = /^v?(\d+)/.exec(version.trim());
|
|
@@ -501,6 +564,7 @@ async function runSetup(options) {
|
|
|
501
564
|
return { ok: false, steps };
|
|
502
565
|
}
|
|
503
566
|
const agent = options.agent;
|
|
567
|
+
let codexDefinitionsChanged;
|
|
504
568
|
const installSkill = options.installSkill !== false;
|
|
505
569
|
const validate = options.validate !== false;
|
|
506
570
|
const nodeVersionCheck = checkCurrentNodeVersion();
|
|
@@ -629,6 +693,7 @@ async function runSetup(options) {
|
|
|
629
693
|
if (!nodeCheck.ok) return { ok: false, steps };
|
|
630
694
|
if (primaryHomeExists) {
|
|
631
695
|
try {
|
|
696
|
+
const before = agent === "codex" ? codexHookDefinitions() : void 0;
|
|
632
697
|
backupSettingsJson(agent);
|
|
633
698
|
steps.push({ step: "backup-settings", ok: true });
|
|
634
699
|
const removed = stripCtxdbHooks(agent);
|
|
@@ -640,6 +705,7 @@ async function runSetup(options) {
|
|
|
640
705
|
});
|
|
641
706
|
}
|
|
642
707
|
appendHooks(agent, hookPaths);
|
|
708
|
+
if (agent === "codex") codexDefinitionsChanged = before !== codexHookDefinitions();
|
|
643
709
|
steps.push({ step: "append-hooks", ok: true });
|
|
644
710
|
} catch (err) {
|
|
645
711
|
steps.push({
|
|
@@ -654,6 +720,7 @@ async function runSetup(options) {
|
|
|
654
720
|
chmodSync(hookPaths.userPromptSubmit, 493);
|
|
655
721
|
chmodSync(hookPaths.stop, 493);
|
|
656
722
|
chmodSync(hookPaths.sessionStart, 493);
|
|
723
|
+
if (hookPaths.sessionEnd) chmodSync(hookPaths.sessionEnd, 493);
|
|
657
724
|
if (hookPaths.preToolUse && existsSync(hookPaths.preToolUse)) {
|
|
658
725
|
chmodSync(hookPaths.preToolUse, 493);
|
|
659
726
|
}
|
|
@@ -816,11 +883,14 @@ async function runSetup(options) {
|
|
|
816
883
|
}
|
|
817
884
|
const failedSteps = steps.filter((s) => !s.ok);
|
|
818
885
|
const ok = failedSteps.length === 0;
|
|
819
|
-
const actionRequired = agent === "hermes" && failedSteps.length === 1 && failedSteps[0]?.step === "check-hermes-hook-allowlist" ? "hermes-hook-allowlist" : void 0;
|
|
886
|
+
const actionRequired = agent === "hermes" && failedSteps.length === 1 && failedSteps[0]?.step === "check-hermes-hook-allowlist" ? "hermes-hook-allowlist" : agent === "codex" && failedSteps.length === 0 && codexDefinitionsChanged !== void 0 ? "review-codex-hook-trust" : void 0;
|
|
820
887
|
const hints = ok ? [UNINSTALL_ORDER_HINT] : [];
|
|
821
|
-
if (actionRequired) {
|
|
888
|
+
if (actionRequired === "hermes-hook-allowlist") {
|
|
822
889
|
hints.unshift(HERMES_ALLOWLIST_ACTION_HINT, HERMES_WEBUI_GATEWAY_HINT);
|
|
823
890
|
}
|
|
891
|
+
if (actionRequired === "review-codex-hook-trust") {
|
|
892
|
+
hints.unshift(CODEX_HOOKS_TRUST_HINT, codexDefinitionsChanged ? "ContextDB Hook \u5B9A\u4E49\u5DF2\u65B0\u589E\u6216\u53D8\u66F4\uFF1BCodex \u6309\u5B9A\u4E49\u54C8\u5E0C\u8BB0\u5F55\u4FE1\u4EFB\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u6388\u6743\u3002" : "ContextDB Hook \u5B9A\u4E49\u672A\u53D8\u5316\uFF1B\u8FD9\u4E0D\u8BC1\u660E\u5DF2\u6388\u6743\uFF0C\u4E5F\u4E0D\u4F1A\u4FEE\u6539\u5BBF\u4E3B\u7684\u4FE1\u4EFB\u8BB0\u5F55\u3002");
|
|
893
|
+
}
|
|
824
894
|
if (ok && agent === "opencode") {
|
|
825
895
|
hints.unshift(
|
|
826
896
|
`OpenCode plugin shim: ${opencodeShimPath()}`,
|
|
@@ -838,6 +908,7 @@ async function runSetup(options) {
|
|
|
838
908
|
ok,
|
|
839
909
|
steps,
|
|
840
910
|
actionRequired,
|
|
911
|
+
...actionRequired === "review-codex-hook-trust" ? { hook_activation: { ...codexHookActivation(), definitions_changed: codexDefinitionsChanged } } : {},
|
|
841
912
|
hints: hints.length > 0 ? hints : void 0
|
|
842
913
|
};
|
|
843
914
|
}
|
|
@@ -1500,6 +1571,7 @@ function hookPathsFromDir(dir, ext) {
|
|
|
1500
1571
|
userPromptSubmit: ups,
|
|
1501
1572
|
stop: stp,
|
|
1502
1573
|
sessionStart: ss,
|
|
1574
|
+
sessionEnd: existsSync(join(dir, `session-end.${ext}`)) ? join(dir, `session-end.${ext}`) : void 0,
|
|
1503
1575
|
preToolUse: existsSync(ptu) ? ptu : void 0,
|
|
1504
1576
|
hermesPreLlmCall: existsSync(hpl) ? hpl : void 0,
|
|
1505
1577
|
hermesPostLlmCall: existsSync(hpost) ? hpost : void 0
|
|
@@ -1616,6 +1688,7 @@ function appendHooksToFile(path, agent, hookPaths) {
|
|
|
1616
1688
|
appendOne(data.hooks, "UserPromptSubmit", hookPaths.userPromptSubmit, agent);
|
|
1617
1689
|
appendOne(data.hooks, "Stop", hookPaths.stop, agent);
|
|
1618
1690
|
appendOne(data.hooks, "SessionStart", hookPaths.sessionStart, agent);
|
|
1691
|
+
if ((agent === "codex" || agent === "claude" || agent === "workbuddy") && hookPaths.sessionEnd) appendOne(data.hooks, "SessionEnd", hookPaths.sessionEnd, agent);
|
|
1619
1692
|
mkdirSync(dirname(path), { recursive: true });
|
|
1620
1693
|
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
1621
1694
|
}
|
|
@@ -1684,7 +1757,7 @@ function appendOne(hooks, event, command, agent) {
|
|
|
1684
1757
|
)
|
|
1685
1758
|
);
|
|
1686
1759
|
if (dup) return;
|
|
1687
|
-
const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: 60 };
|
|
1760
|
+
const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: event === "SessionEnd" ? 3 : 60 };
|
|
1688
1761
|
const entry = {
|
|
1689
1762
|
hooks: [inner],
|
|
1690
1763
|
[ENTRY_MARKER_KEY]: ENTRY_MARKER_VALUE,
|
|
@@ -1753,8 +1826,8 @@ function hermesHooksMap(data) {
|
|
|
1753
1826
|
}
|
|
1754
1827
|
return data.hooks;
|
|
1755
1828
|
}
|
|
1756
|
-
function quoteHermesCommandPart(
|
|
1757
|
-
return `'${
|
|
1829
|
+
function quoteHermesCommandPart(value2) {
|
|
1830
|
+
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
1758
1831
|
}
|
|
1759
1832
|
function hermesHookCommand(scriptPath, nodePath) {
|
|
1760
1833
|
return `${quoteHermesCommandPart(nodePath)} ${quoteHermesCommandPart(scriptPath)} --agent=hermes`;
|
|
@@ -1960,11 +2033,11 @@ function inspectCodexHooksFeature(rawContent) {
|
|
|
1960
2033
|
};
|
|
1961
2034
|
}
|
|
1962
2035
|
if (/^features\.hooks\s*=/.test(stripped)) {
|
|
1963
|
-
const
|
|
1964
|
-
if (
|
|
2036
|
+
const value2 = stripped.split("=", 2)[1]?.trim();
|
|
2037
|
+
if (value2 === "true") return { kind: "already-enabled" };
|
|
1965
2038
|
return {
|
|
1966
2039
|
kind: "ambiguous",
|
|
1967
|
-
reason: `features.hooks already set to '${
|
|
2040
|
+
reason: `features.hooks already set to '${value2}' via dotted-key \u2014 please set it to true manually`
|
|
1968
2041
|
};
|
|
1969
2042
|
}
|
|
1970
2043
|
}
|
|
@@ -1989,12 +2062,12 @@ function inspectCodexHooksFeature(rawContent) {
|
|
|
1989
2062
|
if (!inFeatures) continue;
|
|
1990
2063
|
const kv = stripped.match(/^hooks\s*=\s*(.+)$/);
|
|
1991
2064
|
if (kv) {
|
|
1992
|
-
const
|
|
1993
|
-
if (
|
|
1994
|
-
if (
|
|
2065
|
+
const value2 = kv[1].trim();
|
|
2066
|
+
if (value2 === "true") return { kind: "already-enabled" };
|
|
2067
|
+
if (value2 === "false") return { kind: "features-with-hooks-false" };
|
|
1995
2068
|
return {
|
|
1996
2069
|
kind: "ambiguous",
|
|
1997
|
-
reason: `[features].hooks already set to '${
|
|
2070
|
+
reason: `[features].hooks already set to '${value2}' \u2014 please set it to true manually`
|
|
1998
2071
|
};
|
|
1999
2072
|
}
|
|
2000
2073
|
}
|
|
@@ -2210,10 +2283,6 @@ FLAGS
|
|
|
2210
2283
|
--no-validate Skip connectivity ping after setup
|
|
2211
2284
|
--json Machine-readable JSON output
|
|
2212
2285
|
`;
|
|
2213
|
-
var CODEX_HOOKS_TRUST_REMINDER = [
|
|
2214
|
-
"codex hooks \u914D\u7F6E\u540E\uFF0C\u9700\u8981\u91CD\u542F Codex \u5BF9\u8BDD\uFF0C\u5728\u5F39\u51FA\u7684 hooks \u786E\u8BA4\u754C\u9762\u9009\u62E9`\u63A5\u6536\u6240\u6709 hooks(\u94A9\u5B50)`\u9009\u9879\u3002",
|
|
2215
|
-
"\u5982\u679C\u4F7F\u7528\u7684\u662F Codex App \u684C\u9762\u7248\uFF0C\u64CD\u4F5C\u8DEF\u5F84\u662F\uFF1A\u5DE6\u4E0B\u89D2\u70B9\u51FB\u7528\u6237\u540D \u2192 \u70B9\u51FB\u8BBE\u7F6E \u2192 \u70B9\u51FB\u4FA7\u8FB9\u680F\u7684 `Hooks\uFF08\u94A9\u5B50\uFF09`\u2192 \u70B9\u51FB`\u7528\u6237\u914D\u7F6E`\uFF0C\u4FE1\u4EFB `UserPromptSubmit`\u3001`SessionStart` \u548C `Stop` \u91CC\u7684 `aliyunrds/ctxdb` \u94A9\u5B50\u3002"
|
|
2216
|
-
].join("\n");
|
|
2217
2286
|
var FULL_APP_RESTART_REMINDERS = {
|
|
2218
2287
|
qoder: "Next action: \u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u65B0\u542F\u52A8 Qoder\uFF08\u4E0D\u662F\u53EA\u5173\u95ED\u5F53\u524D\u5BF9\u8BDD\uFF09\uFF0C\u4F7F\u65B0\u7684 hooks \u548C skill \u751F\u6548\u3002",
|
|
2219
2288
|
qoderwork: "Next action: \u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u65B0\u542F\u52A8 QoderWork\uFF08\u4E0D\u662F\u53EA\u5173\u95ED\u5F53\u524D\u5BF9\u8BDD\uFF09\uFF0C\u4F7F\u65B0\u7684 hooks \u548C skill \u751F\u6548\u3002",
|
|
@@ -2221,7 +2290,7 @@ var FULL_APP_RESTART_REMINDERS = {
|
|
|
2221
2290
|
};
|
|
2222
2291
|
function postSetupReminders(agent) {
|
|
2223
2292
|
const reminder = FULL_APP_RESTART_REMINDERS[agent];
|
|
2224
|
-
return [...reminder ? [reminder] : [], ...agent === "codex" ? [
|
|
2293
|
+
return [...reminder ? [reminder] : [], ...agent === "codex" ? [CODEX_HOOKS_TRUST_HINT] : []];
|
|
2225
2294
|
}
|
|
2226
2295
|
function printPostSetupReminder(agent) {
|
|
2227
2296
|
for (const reminder of postSetupReminders(agent)) process.stderr.write(`
|
|
@@ -2387,6 +2456,8 @@ async function inspectStatus(agent) {
|
|
|
2387
2456
|
base_url: cfg.baseUrl,
|
|
2388
2457
|
user_id: cfg.userId,
|
|
2389
2458
|
api_key_set: Boolean(cfg.apiKey),
|
|
2459
|
+
auto_skill_evolution: cfg.autoSkillEvolution,
|
|
2460
|
+
...agent === "codex" ? { hook_activation: codexHookActivation() } : {},
|
|
2390
2461
|
access_token_set: credential?.type === "access-token",
|
|
2391
2462
|
credential_type: credential?.type ?? null,
|
|
2392
2463
|
auto_capture: cfg.autoCapture,
|
|
@@ -2665,7 +2736,7 @@ FLAGS
|
|
|
2665
2736
|
--json Machine-readable JSON output
|
|
2666
2737
|
|
|
2667
2738
|
Note: This command only installs skill files (contextdb, contextdb-memory,
|
|
2668
|
-
contextdb-knowledge). It does not write config or install hooks.
|
|
2739
|
+
contextdb-knowledge, contextdb-skills). It does not write config or install hooks.
|
|
2669
2740
|
To configure server connection, run: ctxdb setup --api-key=<key> --base-url=<url>
|
|
2670
2741
|
`;
|
|
2671
2742
|
function skillInstall(args) {
|
|
@@ -2719,6 +2790,603 @@ function skillInstall(args) {
|
|
|
2719
2790
|
return ok ? 0 : 1;
|
|
2720
2791
|
}
|
|
2721
2792
|
|
|
2793
|
+
// src/skills/session-selector.ts
|
|
2794
|
+
import { homedir as homedir2 } from "node:os";
|
|
2795
|
+
import { join as join3, win32 } from "node:path";
|
|
2796
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2797
|
+
|
|
2798
|
+
// src/skills/status.ts
|
|
2799
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, lstatSync } from "node:fs";
|
|
2800
|
+
import "node:path";
|
|
2801
|
+
function statusBindings(root, agent) {
|
|
2802
|
+
const dir = privatePath(root, "sessions", "bindings");
|
|
2803
|
+
if (!existsSync2(dir)) return [];
|
|
2804
|
+
return readdirSync2(dir).filter((name) => /^[a-f0-9-]{36}\.json$/.test(name)).map((name) => getBinding(name.slice(0, -5), root)).filter((bound) => !agent || bound.agent === agent).sort((a, b) => Number(b.active === true) - Number(a.active === true));
|
|
2805
|
+
}
|
|
2806
|
+
function localSkillStatus(bound, root) {
|
|
2807
|
+
const spool = readSpool(bound, root), business = businessEvents(spool);
|
|
2808
|
+
const calls = new Set(business.filter((e) => e.type === "tool_call").map((e) => e.tool_call_id));
|
|
2809
|
+
const results = new Set(spool.events.filter((e) => e.type === "tool_result").map((e) => e.tool_call_id));
|
|
2810
|
+
const unpaired = spool.events.filter((e) => e.type === "tool_call" && !results.has(e.tool_call_id)).map((e) => e.tool_call_id);
|
|
2811
|
+
const path = (...parts) => privatePath(root, ...parts);
|
|
2812
|
+
const skills = path("skills", bound.scope_key), evolution = path("skill-evolution", bound.scope_key);
|
|
2813
|
+
const directory = path(spoolDirectory(bound));
|
|
2814
|
+
const list = (dir) => existsSync2(dir) ? readdirSync2(dir) : [];
|
|
2815
|
+
const dispositions = list(path(directory, "dispositions")).filter((n) => /^\d+-\d+\.json$/.test(n)).map((name) => {
|
|
2816
|
+
const file = path(directory, "dispositions", name);
|
|
2817
|
+
const d = readState(root, file);
|
|
2818
|
+
return {
|
|
2819
|
+
path: file,
|
|
2820
|
+
status: d.status,
|
|
2821
|
+
reason: d.reason,
|
|
2822
|
+
from_sequence: d.from_sequence,
|
|
2823
|
+
through_sequence: d.through_sequence,
|
|
2824
|
+
event_count: d.event_count,
|
|
2825
|
+
buffer_bytes: d.buffer_bytes,
|
|
2826
|
+
created_at: d.created_at
|
|
2827
|
+
};
|
|
2828
|
+
});
|
|
2829
|
+
const publications = [], evaluations = [];
|
|
2830
|
+
for (const name of list(path(evolution, "artifacts"))) {
|
|
2831
|
+
if (/^upload-[a-f0-9-]{36}$/.test(name)) {
|
|
2832
|
+
const file = path(evolution, "artifacts", name, "publication.json");
|
|
2833
|
+
const p = readState(root, file);
|
|
2834
|
+
if (p.binding_id !== bound.id) continue;
|
|
2835
|
+
publications.push({
|
|
2836
|
+
evaluation_id: p.evaluation_id,
|
|
2837
|
+
request_id: p.input.client_request_id,
|
|
2838
|
+
source: p.input.source,
|
|
2839
|
+
result: p.result,
|
|
2840
|
+
verified: p.verified ?? false,
|
|
2841
|
+
manifest_refreshed: p.manifest_refreshed,
|
|
2842
|
+
active_advanced: p.active_advanced,
|
|
2843
|
+
receipt_path: file,
|
|
2844
|
+
bundle_path: path(evolution, "artifacts", name, "bundle.zip")
|
|
2845
|
+
});
|
|
2846
|
+
} else if (/^evaluation-[a-f0-9-]{36}\.json$/.test(name)) {
|
|
2847
|
+
const file = path(evolution, "artifacts", name), e = readState(root, file);
|
|
2848
|
+
if (e.binding_id === bound.id) evaluations.push({ request_id: e.evidence.client_request_id, result: e.result, path: file });
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
const handoffs = list(path(directory, "handoffs")).filter((n) => /^[a-zA-Z0-9_-]{1,128}\.json$/.test(n)).map((name) => {
|
|
2852
|
+
const file = path(directory, "handoffs", name), h = readState(root, file);
|
|
2853
|
+
const related = publications.filter((p) => p.evaluation_id === h.event_id);
|
|
2854
|
+
return {
|
|
2855
|
+
event_id: h.event_id,
|
|
2856
|
+
delivery: h.delivery,
|
|
2857
|
+
recorded_at: h.recorded_at,
|
|
2858
|
+
completion: related.some((p) => p.verified) ? "published" : related.length ? "publication_unconfirmed" : "unconfirmed",
|
|
2859
|
+
automatic_redelivery: false,
|
|
2860
|
+
path: file
|
|
2861
|
+
};
|
|
2862
|
+
});
|
|
2863
|
+
const loaded = (bound.loaded_versions ?? []).map((ref) => ({
|
|
2864
|
+
...ref,
|
|
2865
|
+
path: path(skills, "cache", ref.skill_id, ref.version_id, "bundle", "SKILL.md")
|
|
2866
|
+
}));
|
|
2867
|
+
const manifestPath = path(skills, "manifest.json");
|
|
2868
|
+
const snapshot = existsSync2(manifestPath) ? readState(root, manifestPath) : void 0;
|
|
2869
|
+
const drafts = list(path(skills, "drafts")).flatMap((name) => {
|
|
2870
|
+
if (!lstatSync(path(skills, "drafts", name)).isDirectory()) return [];
|
|
2871
|
+
const direct = path(skills, "drafts", name, "SKILL.md"), bundle = path(skills, "drafts", name, "bundle", "SKILL.md");
|
|
2872
|
+
return [direct, bundle].filter((file) => existsSync2(file));
|
|
2873
|
+
});
|
|
2874
|
+
const submissions = list(path(directory, "submissions")).filter((n) => /^[a-f0-9-]{36}\.json$/.test(n)).map((name) => {
|
|
2875
|
+
const file = path(directory, "submissions", name);
|
|
2876
|
+
const bytes = readPrivate(root, file, 65536), e = JSON.parse(bytes.toString("utf8"));
|
|
2877
|
+
return {
|
|
2878
|
+
path: file,
|
|
2879
|
+
request_id: e.client_request_id,
|
|
2880
|
+
trigger: e.trigger,
|
|
2881
|
+
request_bytes: bytes.length,
|
|
2882
|
+
distinct_tool_calls: new Set(e.events.filter((event) => event.type === "tool_call").map((event) => event.tool_call_id)).size,
|
|
2883
|
+
event_count: e.events.length,
|
|
2884
|
+
goal_characters: Array.from(e.goal ?? "").length,
|
|
2885
|
+
max_summary_characters: Math.max(0, ...e.events.map((event) => Array.from(event.summary ?? "").length))
|
|
2886
|
+
};
|
|
2887
|
+
});
|
|
2888
|
+
const trigger = threshold(spool);
|
|
2889
|
+
const waiting = bound.active === false ? "inactive_session" : spool.maintenance?.active ? "agent_maintenance" : spool.current ? "evaluation_in_progress_or_awaiting_delivery" : !autoCollectAllowed(bound, root) ? "automatic_capture_disabled" : !trigger ? "below_threshold" : "awaiting_complete_host_checkpoint";
|
|
2890
|
+
return {
|
|
2891
|
+
binding_id: bound.id,
|
|
2892
|
+
session_id: bound.session_id,
|
|
2893
|
+
agent: bound.agent,
|
|
2894
|
+
endpoint: bound.endpoint,
|
|
2895
|
+
auth_scope: bound.auth_scope,
|
|
2896
|
+
scope_key: bound.scope_key,
|
|
2897
|
+
active: bound.active,
|
|
2898
|
+
verified: bound.verified,
|
|
2899
|
+
auto_evolution_allowed: bound.auto_evolution_allowed,
|
|
2900
|
+
current: spool.current,
|
|
2901
|
+
error: spool.error,
|
|
2902
|
+
maintenance: spool.maintenance,
|
|
2903
|
+
buffered_events: spool.events.length,
|
|
2904
|
+
buffer_bytes: spool.buffer_bytes,
|
|
2905
|
+
buffer: {
|
|
2906
|
+
business_events: business.length,
|
|
2907
|
+
distinct_tool_calls: calls.size,
|
|
2908
|
+
bytes: spool.buffer_bytes,
|
|
2909
|
+
threshold_reached: !!trigger,
|
|
2910
|
+
trigger,
|
|
2911
|
+
tool_call_threshold: 10,
|
|
2912
|
+
bytes_threshold: 40960,
|
|
2913
|
+
max_request_bytes: 65536,
|
|
2914
|
+
max_events: 200,
|
|
2915
|
+
max_summary_characters: 2e3,
|
|
2916
|
+
unpaired_call_ids: unpaired,
|
|
2917
|
+
has_final_answer_hash: !!spool.last_answer_hash,
|
|
2918
|
+
waiting_reason: waiting
|
|
2919
|
+
},
|
|
2920
|
+
...bound.agent === "codex" ? { hook_activation: {
|
|
2921
|
+
trust: "unverified",
|
|
2922
|
+
runtime_ready: null,
|
|
2923
|
+
binding_record: "present",
|
|
2924
|
+
transcript_checkpoint: spool.cursor > 0 ? "present" : "not_observed",
|
|
2925
|
+
evidence_capture: spool.events.length > 0 || submissions.some((s) => s.event_count > 0) || dispositions.some((d) => Number(d.event_count) > 0) ? "present" : "not_observed",
|
|
2926
|
+
evaluation_submission: spool.current?.event_id ? "accepted" : submissions.length ? "prepared" : "not_observed",
|
|
2927
|
+
last_checkpoint_at: spool.updated_at ?? null,
|
|
2928
|
+
note: "Local records are not Codex trust receipts. A binding can be created by CLI; historical capture or submission does not prove current hooks are trusted. Match this session to your new conversation. Missing records can mean no event yet, an unloaded hook, missing trust, or execution failure."
|
|
2929
|
+
} } : {},
|
|
2930
|
+
state_exists: existsSync2(path(directory, "state.json")),
|
|
2931
|
+
cursor: spool.cursor,
|
|
2932
|
+
submitted_through_sequence: spool.submitted_through_sequence,
|
|
2933
|
+
processed_through_sequence: spool.processed_through_sequence,
|
|
2934
|
+
updated_at: spool.updated_at,
|
|
2935
|
+
dispositions,
|
|
2936
|
+
submissions,
|
|
2937
|
+
publications,
|
|
2938
|
+
evaluations,
|
|
2939
|
+
handoffs,
|
|
2940
|
+
scope_drafts: drafts,
|
|
2941
|
+
loaded_versions: loaded,
|
|
2942
|
+
manifest: {
|
|
2943
|
+
path: manifestPath,
|
|
2944
|
+
exists: !!snapshot,
|
|
2945
|
+
status: bound.manifest_status,
|
|
2946
|
+
fetched_at: snapshot?.fetched_at,
|
|
2947
|
+
skills: snapshot?.manifest.skills ?? []
|
|
2948
|
+
},
|
|
2949
|
+
paths: {
|
|
2950
|
+
binding: path(bindingPath(bound)),
|
|
2951
|
+
state: path(directory, "state.json"),
|
|
2952
|
+
skills,
|
|
2953
|
+
drafts: path(skills, "drafts"),
|
|
2954
|
+
cache: path(skills, "cache"),
|
|
2955
|
+
evolution,
|
|
2956
|
+
submissions: path(directory, "submissions"),
|
|
2957
|
+
dispositions: path(directory, "dispositions"),
|
|
2958
|
+
current_submission: spool.current ? path(directory, "submissions", `${spool.current.request_id}.json`) : null
|
|
2959
|
+
},
|
|
2960
|
+
notes: [
|
|
2961
|
+
"Buffer counts only unsubmitted evidence and clears after accepted submission; submissions retain the submitted call/event/byte counts.",
|
|
2962
|
+
"Inactive sessions may be host initialization sessions; match session_id to the actual conversation before interpreting counters.",
|
|
2963
|
+
"A delivered handoff is not completion. Unconfirmed includes ignored, interrupted or permission-blocked work; absence of a receipt cannot identify the reason. No automatic redelivery.",
|
|
2964
|
+
"Drafts and manifest are scope-wide; draft ownership is not inferred from directory existence.",
|
|
2965
|
+
"Loaded versions may come from publication verification or manual Load; they do not prove business reuse.",
|
|
2966
|
+
"Local current is a checkpoint snapshot; use --remote for server status. A final-answer hash alone does not certify a complete boundary."
|
|
2967
|
+
]
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
// src/skills/session-selector.ts
|
|
2972
|
+
function qoderWorkDatabase(platform2 = process.platform, home = homedir2(), appData = process.env.APPDATA) {
|
|
2973
|
+
if (platform2 === "darwin") return join3(home, "Library", "Application Support", "QoderWork", "data", "agents.db");
|
|
2974
|
+
if (platform2 === "win32") return win32.join(appData || win32.join(home, "AppData", "Roaming"), "QoderWork", "data", "agents.db");
|
|
2975
|
+
throw new Error("qoderwork_chat_lookup_platform_unsupported_use_binding_id");
|
|
2976
|
+
}
|
|
2977
|
+
async function selectSkillSessions(id, agent, root) {
|
|
2978
|
+
if (!id.trim() || id.length > 256) throw new Error("invalid_session_id");
|
|
2979
|
+
const bindings = statusBindings(root, agent);
|
|
2980
|
+
const direct = bindings.filter((b) => b.session_id === id);
|
|
2981
|
+
if (direct.length) return { bindings: direct, host_chat_id: void 0, unbound_session_ids: [] };
|
|
2982
|
+
if (agent !== "qoderwork") throw new Error("session_binding_not_found");
|
|
2983
|
+
const database = qoderWorkDatabase();
|
|
2984
|
+
if (!existsSync3(database)) throw new Error("qoderwork_database_not_found");
|
|
2985
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
2986
|
+
if (major < 22 || major === 22 && minor < 13 || major === 23 && minor < 4) {
|
|
2987
|
+
throw new Error("qoderwork_chat_lookup_requires_node_22_13_or_newer_use_binding_id");
|
|
2988
|
+
}
|
|
2989
|
+
let sqlite;
|
|
2990
|
+
try {
|
|
2991
|
+
sqlite = await import("node:sqlite");
|
|
2992
|
+
} catch {
|
|
2993
|
+
throw new Error("qoderwork_chat_lookup_requires_node_22_13_or_newer_use_binding_id");
|
|
2994
|
+
}
|
|
2995
|
+
let rows;
|
|
2996
|
+
try {
|
|
2997
|
+
const db = new sqlite.DatabaseSync(database, { readOnly: true });
|
|
2998
|
+
try {
|
|
2999
|
+
rows = db.prepare("SELECT s.session_id FROM chats c LEFT JOIN sub_chats s ON s.chat_id=c.id WHERE c.id=? ORDER BY s.session_id").all(id);
|
|
3000
|
+
if (rows.some((r) => !(r.session_id === null || typeof r.session_id === "string"))) throw new Error();
|
|
3001
|
+
} finally {
|
|
3002
|
+
db.close();
|
|
3003
|
+
}
|
|
3004
|
+
} catch {
|
|
3005
|
+
throw new Error("qoderwork_database_unreadable_or_schema_unsupported_use_binding_id");
|
|
3006
|
+
}
|
|
3007
|
+
if (!rows.length) throw new Error("qoderwork_chat_not_found");
|
|
3008
|
+
const ids = [...new Set(rows.map((r) => r.session_id).filter((s) => !!s))];
|
|
3009
|
+
if (!ids.length) throw new Error("qoderwork_session_not_started");
|
|
3010
|
+
const matched = bindings.filter((b) => ids.includes(b.session_id));
|
|
3011
|
+
const unbound = ids.filter((id2) => !matched.some((b) => b.session_id === id2));
|
|
3012
|
+
return { bindings: matched, host_chat_id: id, unbound_session_ids: unbound };
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
// src/skills/evaluation.ts
|
|
3016
|
+
import { randomUUID } from "node:crypto";
|
|
3017
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
3018
|
+
import { join as join4 } from "node:path";
|
|
3019
|
+
async function submitManualEvaluation(binding2, client, input, source, requestId, root = skillRoot()) {
|
|
3020
|
+
const { client_request_id: ignored, ...rest } = input;
|
|
3021
|
+
const body = { ...rest, submission_mode: "manual", trigger: "manual" };
|
|
3022
|
+
const artifacts = join4("skill-evolution", binding2.scope_key, "artifacts");
|
|
3023
|
+
const index = join4(artifacts, `manual-${sha256(JSON.stringify([source, body]))}.json`);
|
|
3024
|
+
const id = requestId ? requireUuid(requestId) : await withStateLock(root, index, async () => {
|
|
3025
|
+
if (existsSync4(privatePath(root, index))) return requireUuid(readState(root, index).request_id);
|
|
3026
|
+
const request_id = randomUUID();
|
|
3027
|
+
writeState(root, index, { request_id });
|
|
3028
|
+
return request_id;
|
|
3029
|
+
});
|
|
3030
|
+
const evidence = { ...body, client_request_id: id };
|
|
3031
|
+
validateEvidence(evidence);
|
|
3032
|
+
const path = join4(artifacts, `evaluation-${id}.json`);
|
|
3033
|
+
return withStateLock(root, path, async () => {
|
|
3034
|
+
let state;
|
|
3035
|
+
if (existsSync4(privatePath(root, path))) {
|
|
3036
|
+
state = readState(root, path);
|
|
3037
|
+
if (JSON.stringify(state.evidence) !== JSON.stringify(evidence)) throw new Error("idempotency_payload_mismatch");
|
|
3038
|
+
} else {
|
|
3039
|
+
state = { binding_id: binding2.id, evidence };
|
|
3040
|
+
writeState(root, path, state);
|
|
3041
|
+
}
|
|
3042
|
+
if (!state.result) {
|
|
3043
|
+
const result = unbox(await client.postJson("/v1/skill-evolution/evaluations", evidence));
|
|
3044
|
+
if (!result || !/^[a-zA-Z0-9_-]{1,128}$/.test(result.event_id) || typeof result.status !== "string") throw new Error("invalid_evaluation_response");
|
|
3045
|
+
state.result = result;
|
|
3046
|
+
writeState(root, path, state);
|
|
3047
|
+
}
|
|
3048
|
+
return state.result;
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
// src/cli/skill-storage-cli.ts
|
|
3053
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3054
|
+
import { join as join5, resolve as resolve2 } from "node:path";
|
|
3055
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, rmSync as rmSync2 } from "node:fs";
|
|
3056
|
+
function value(args, name) {
|
|
3057
|
+
const item = args.flags[name];
|
|
3058
|
+
if (item === true) throw new Error(`--${name} requires a value`);
|
|
3059
|
+
return item;
|
|
3060
|
+
}
|
|
3061
|
+
async function binding(args) {
|
|
3062
|
+
const id = value(args, "session");
|
|
3063
|
+
if (id) return getBinding(id);
|
|
3064
|
+
if (process.env.CODEX_THREAD_ID || process.env.CODEX_SESSION_ID || process.env.CLAUDE_CODE_ENTRYPOINT || process.env.CTXDB_SKILL_REQUIRE_SESSION === "1") throw new Error("session_binding_required");
|
|
3065
|
+
return bindSession(agentFromFlags(args.flags), `terminal-${randomUUID2()}`, skillRoot(), 3e4);
|
|
3066
|
+
}
|
|
3067
|
+
var skillBind = async (args) => {
|
|
3068
|
+
const session = value(args, "session-id");
|
|
3069
|
+
if (!session) throw new Error("--session-id is required");
|
|
3070
|
+
const result = await bindSession(agentFromFlags(args.flags), session, skillRoot(), 3e4);
|
|
3071
|
+
const client = await verifiedClient(result);
|
|
3072
|
+
await refreshManifest(result, client);
|
|
3073
|
+
printResult({ binding_id: result.id, auth_scope: result.auth_scope }, !!args.flags.json);
|
|
3074
|
+
return 0;
|
|
3075
|
+
};
|
|
3076
|
+
var skillManifest = async (args) => {
|
|
3077
|
+
const bound = await binding(args), client = await verifiedClient(bound);
|
|
3078
|
+
const snapshot = await refreshManifest(bound, client);
|
|
3079
|
+
printResult({ ...snapshot, catalog_context: catalogContext(bound) }, !!args.flags.json);
|
|
3080
|
+
return 0;
|
|
3081
|
+
};
|
|
3082
|
+
var skillList = async (args) => {
|
|
3083
|
+
const bound = await binding(args), client = await verifiedClient(bound);
|
|
3084
|
+
const snapshot = await refreshManifest(bound, client);
|
|
3085
|
+
const result = {
|
|
3086
|
+
fetched_at: snapshot.fetched_at,
|
|
3087
|
+
auth_scope: snapshot.manifest.auth_scope,
|
|
3088
|
+
skills: [...snapshot.manifest.skills].sort((left, right) => left.display_name.localeCompare(right.display_name) || left.skill_id.localeCompare(right.skill_id))
|
|
3089
|
+
};
|
|
3090
|
+
if (args.flags.json) printResult(result, true);
|
|
3091
|
+
else if (result.skills.length === 0) process.stdout.write("No active Skills.\n");
|
|
3092
|
+
else process.stdout.write(`${result.skills.length} active Skill${result.skills.length === 1 ? "" : "s"}:
|
|
3093
|
+
${result.skills.map((entry) => `- ${entry.display_name} (${entry.category})
|
|
3094
|
+
skill_id: ${entry.skill_id}
|
|
3095
|
+
active_version_id: ${entry.active_version_id}
|
|
3096
|
+
updated_at: ${entry.updated_at}`).join("\n")}
|
|
3097
|
+
`);
|
|
3098
|
+
return 0;
|
|
3099
|
+
};
|
|
3100
|
+
async function effectiveSkillConfig(args) {
|
|
3101
|
+
const bound = await binding(args), client = await verifiedClient(bound);
|
|
3102
|
+
const remote = unbox(await client.get("/v1/skill-evolution/config"));
|
|
3103
|
+
const localEnabled = load({ agent: bound.agent, path: join5(skillRoot(), configFileName()) }).autoSkillEvolution !== false;
|
|
3104
|
+
return {
|
|
3105
|
+
...remote,
|
|
3106
|
+
local_auto_evolution_enabled: localEnabled,
|
|
3107
|
+
effective_auto_evolution_enabled: localEnabled && remote.auto_evolution_enabled === true
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3110
|
+
function localConfigAgent(args, requireExplicit = false) {
|
|
3111
|
+
const session = value(args, "session");
|
|
3112
|
+
if (session) {
|
|
3113
|
+
if (requireExplicit) throw new Error("--session cannot be used for local config changes");
|
|
3114
|
+
const bound = getBinding(session);
|
|
3115
|
+
const requested = args.flags.agent === void 0 ? void 0 : agentFromFlags(args.flags);
|
|
3116
|
+
if (requested && requested !== bound.agent) throw new Error("session_agent_mismatch");
|
|
3117
|
+
return bound.agent;
|
|
3118
|
+
}
|
|
3119
|
+
if (requireExplicit && typeof args.flags.agent !== "string") throw new Error("--agent is required for local config changes");
|
|
3120
|
+
return agentFromFlags(args.flags);
|
|
3121
|
+
}
|
|
3122
|
+
function localAutoEvolution(agent) {
|
|
3123
|
+
return load({ agent, path: join5(skillRoot(), configFileName()) }).autoSkillEvolution !== false;
|
|
3124
|
+
}
|
|
3125
|
+
var skillConfig = async (args) => {
|
|
3126
|
+
const [action, key, rawValue, ...extra] = args.positional;
|
|
3127
|
+
if (!action || action === "get" && !key) {
|
|
3128
|
+
if (rawValue || extra.length) throw new Error("invalid_skill_config_command");
|
|
3129
|
+
printResult(await effectiveSkillConfig(args), !!args.flags.json);
|
|
3130
|
+
return 0;
|
|
3131
|
+
}
|
|
3132
|
+
if (!["get", "set", "unset"].includes(action) || key !== "auto-skill-evolution" || extra.length) {
|
|
3133
|
+
throw new Error("invalid_skill_config_command");
|
|
3134
|
+
}
|
|
3135
|
+
if (action === "get") {
|
|
3136
|
+
if (rawValue) throw new Error("invalid_skill_config_command");
|
|
3137
|
+
const agent2 = localConfigAgent(args);
|
|
3138
|
+
printResult({ key, agent: agent2, value: localAutoEvolution(agent2), source: "local" }, !!args.flags.json);
|
|
3139
|
+
return 0;
|
|
3140
|
+
}
|
|
3141
|
+
const agent = localConfigAgent(args, true);
|
|
3142
|
+
let enabled;
|
|
3143
|
+
if (action === "set") {
|
|
3144
|
+
if (rawValue !== "on" && rawValue !== "off") throw new Error("auto-skill-evolution value must be on or off");
|
|
3145
|
+
enabled = rawValue === "on";
|
|
3146
|
+
} else if (rawValue) throw new Error("invalid_skill_config_command");
|
|
3147
|
+
if (!updateAutoSkillEvolution(agent, enabled, join5(skillRoot(), configFileName()))) throw new Error("agent_config_not_found");
|
|
3148
|
+
printResult({ key, agent, value: localAutoEvolution(agent), source: enabled === void 0 ? "default" : "local" }, !!args.flags.json);
|
|
3149
|
+
return 0;
|
|
3150
|
+
};
|
|
3151
|
+
var skillSearch = async (args) => {
|
|
3152
|
+
const query = args.positional.join(" ");
|
|
3153
|
+
const topK = Number(value(args, "top-k") ?? 5);
|
|
3154
|
+
if (!query || Array.from(query).length > 512 || !Number.isInteger(topK) || topK < 1 || topK > 10) throw new Error("invalid_search");
|
|
3155
|
+
if (args.flags.local) throw new Error("local_search_removed_use_manifest_or_remote_search");
|
|
3156
|
+
const bound = await binding(args);
|
|
3157
|
+
const result = unbox(await (await verifiedClient(bound)).get("/v1/skills/search", { query, top_k: topK }));
|
|
3158
|
+
printResult(result, !!args.flags.json);
|
|
3159
|
+
return 0;
|
|
3160
|
+
};
|
|
3161
|
+
var skillLoad = async (args) => {
|
|
3162
|
+
const id = value(args, "skill-id");
|
|
3163
|
+
if (!id) throw new Error("--skill-id is required");
|
|
3164
|
+
const bound = await binding(args), client = await verifiedClient(bound);
|
|
3165
|
+
printResult(await loadSkill(bound, client, id, value(args, "version-id"), !!args.flags.editable), !!args.flags.json);
|
|
3166
|
+
return 0;
|
|
3167
|
+
};
|
|
3168
|
+
var skillDelete = (args) => skillDisable(args, true);
|
|
3169
|
+
var skillDisable = async (args, deleteHistory = false) => {
|
|
3170
|
+
const id = value(args, "skill-id");
|
|
3171
|
+
if (!id) throw new Error("--skill-id is required");
|
|
3172
|
+
requireUuid(id);
|
|
3173
|
+
const bound = await binding(args);
|
|
3174
|
+
if (readSpool(bound).maintenance?.active) throw new Error("management_requires_user_instruction");
|
|
3175
|
+
const client = await verifiedClient(bound);
|
|
3176
|
+
const result = unbox(await client.delete("/v1/skills", {
|
|
3177
|
+
skill_id: id,
|
|
3178
|
+
...deleteHistory ? { delete_history: true } : {}
|
|
3179
|
+
}));
|
|
3180
|
+
if (result.skill_id !== id || result.deleted !== true) throw new Error("invalid_disable_response");
|
|
3181
|
+
await refreshManifest(bound, client);
|
|
3182
|
+
printResult(result, !!args.flags.json);
|
|
3183
|
+
return 0;
|
|
3184
|
+
};
|
|
3185
|
+
var skillRollback = async (args) => {
|
|
3186
|
+
const id = value(args, "skill-id"), version = value(args, "version-id"), base = value(args, "base-version-id");
|
|
3187
|
+
if (!id || !version || !base) throw new Error("rollback_requires_skill_version_and_base");
|
|
3188
|
+
[id, version, base].forEach(requireUuid);
|
|
3189
|
+
const reason = value(args, "revoke-reason");
|
|
3190
|
+
if (reason !== void 0 && (!reason.trim() || reason.length > 1e3)) throw new Error("invalid_revoke_reason");
|
|
3191
|
+
const bound = await binding(args);
|
|
3192
|
+
if (readSpool(bound).maintenance?.active) throw new Error("management_requires_user_instruction");
|
|
3193
|
+
const client = await verifiedClient(bound);
|
|
3194
|
+
const result = unbox(await client.postJson("/v1/skills/rollback", {
|
|
3195
|
+
skill_id: id,
|
|
3196
|
+
version_id: version,
|
|
3197
|
+
base_version_id: base,
|
|
3198
|
+
...reason ? { revoke_reason: reason } : {}
|
|
3199
|
+
}));
|
|
3200
|
+
if (result.skill_id !== id || result.version_id !== version || result.action !== "rollback") throw new Error("invalid_rollback_response");
|
|
3201
|
+
await refreshManifest(bound, client);
|
|
3202
|
+
printResult(result, !!args.flags.json);
|
|
3203
|
+
return 0;
|
|
3204
|
+
};
|
|
3205
|
+
var skillValidate = async (args) => {
|
|
3206
|
+
const path = args.positional[0];
|
|
3207
|
+
if (!path) throw new Error("package path is required");
|
|
3208
|
+
const pkg = await readPackage(resolve2(path));
|
|
3209
|
+
printResult({
|
|
3210
|
+
valid: true,
|
|
3211
|
+
validation_scope: "static_package",
|
|
3212
|
+
scripts_executed: false,
|
|
3213
|
+
bundle_hash: pkg.hash,
|
|
3214
|
+
bundle_size_bytes: pkg.zip.length,
|
|
3215
|
+
file_count: pkg.files.size,
|
|
3216
|
+
name: pkg.metadata.name,
|
|
3217
|
+
version: pkg.metadata.version
|
|
3218
|
+
}, !!args.flags.json);
|
|
3219
|
+
return 0;
|
|
3220
|
+
};
|
|
3221
|
+
var skillUpload = async (args) => {
|
|
3222
|
+
const path = args.positional[0];
|
|
3223
|
+
if (!path) throw new Error("package path is required");
|
|
3224
|
+
const skillId = value(args, "skill-id"), base = value(args, "base-version-id");
|
|
3225
|
+
if (!!skillId !== !!base) throw new Error("update_requires_skill_and_base_version");
|
|
3226
|
+
if (skillId) {
|
|
3227
|
+
requireUuid(skillId);
|
|
3228
|
+
requireUuid(base);
|
|
3229
|
+
}
|
|
3230
|
+
const pkg = await readPackage(resolve2(path));
|
|
3231
|
+
const bound = await binding(args), client = await verifiedClient(bound), root = skillRoot();
|
|
3232
|
+
const source = value(args, "source") ?? "import";
|
|
3233
|
+
if (source !== "import" && source !== "evolution") throw new Error("invalid_upload_source");
|
|
3234
|
+
if (readSpool(bound, root).maintenance?.active && source !== "evolution") throw new Error("maintenance_upload_requires_evolution_source");
|
|
3235
|
+
const body = {
|
|
3236
|
+
operation: skillId ? "update" : "create",
|
|
3237
|
+
bundle_hash: pkg.hash,
|
|
3238
|
+
bundle_size_bytes: pkg.zip.length,
|
|
3239
|
+
source,
|
|
3240
|
+
...skillId ? { skill_id: skillId, base_version_id: base } : {}
|
|
3241
|
+
};
|
|
3242
|
+
const index = join5("skill-evolution", bound.scope_key, "artifacts", `draft-${sha256(resolve2(path))}.json`);
|
|
3243
|
+
const result = await withStateLock(root, index, async () => {
|
|
3244
|
+
const previous = existsSync5(privatePath(root, index)) ? readState(root, index) : void 0;
|
|
3245
|
+
const same = previous && JSON.stringify(previous.body) === JSON.stringify(body);
|
|
3246
|
+
if (previous && !same) {
|
|
3247
|
+
const publicationPath = join5("skill-evolution", bound.scope_key, "artifacts", `upload-${previous.request_id}`, "publication.json");
|
|
3248
|
+
if (existsSync5(privatePath(root, publicationPath)) && !readState(root, publicationPath).result)
|
|
3249
|
+
throw new Error("previous_upload_unresolved_run_status_resume_before_changing_draft");
|
|
3250
|
+
}
|
|
3251
|
+
const requested = value(args, "request-id");
|
|
3252
|
+
if (same && requested && requested !== previous.request_id) throw new Error("retry_requires_original_request_id");
|
|
3253
|
+
const requestId = same ? previous.request_id : requested ?? randomUUID2();
|
|
3254
|
+
requireUuid(requestId);
|
|
3255
|
+
const receipt = join5("skill-evolution", bound.scope_key, "artifacts", `upload-${requestId}`, "publication.json");
|
|
3256
|
+
if (existsSync5(privatePath(root, receipt))) {
|
|
3257
|
+
const original = readState(root, receipt).input;
|
|
3258
|
+
if (JSON.stringify(original) !== JSON.stringify({ ...body, client_request_id: requestId })) throw new Error("idempotency_payload_mismatch");
|
|
3259
|
+
}
|
|
3260
|
+
writeState(root, index, { request_id: requestId, body });
|
|
3261
|
+
return publishPackage(bound, client, pkg, { ...body, client_request_id: requestId }, root);
|
|
3262
|
+
});
|
|
3263
|
+
printResult(result, !!args.flags.json);
|
|
3264
|
+
return 0;
|
|
3265
|
+
};
|
|
3266
|
+
var skillEvaluate = async (args) => {
|
|
3267
|
+
const file = args.positional[0];
|
|
3268
|
+
if (!file) throw new Error("evidence JSON path is required");
|
|
3269
|
+
const root = skillRoot(), bytes = readPrivate(resolve2(file, ".."), resolve2(file), 65536);
|
|
3270
|
+
const evidence = JSON.parse(bytes.toString("utf8"));
|
|
3271
|
+
if (!evidence || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error("invalid_evidence");
|
|
3272
|
+
const bound = await binding(args), client = await verifiedClient(bound);
|
|
3273
|
+
const result = await submitManualEvaluation(bound, client, evidence, resolve2(file), value(args, "request-id"), root);
|
|
3274
|
+
printResult(result, !!args.flags.json);
|
|
3275
|
+
return 0;
|
|
3276
|
+
};
|
|
3277
|
+
var skillStatus = async (args) => {
|
|
3278
|
+
const root = skillRoot();
|
|
3279
|
+
let selected = value(args, "session");
|
|
3280
|
+
const sessionId = value(args, "session-id");
|
|
3281
|
+
if (sessionId !== void 0 && selected) throw new Error("session_and_session_id_are_mutually_exclusive");
|
|
3282
|
+
if (sessionId !== void 0 && !value(args, "agent")) throw new Error("--session-id requires --agent");
|
|
3283
|
+
const selection = sessionId !== void 0 ? await selectSkillSessions(sessionId, agentFromFlags(args.flags), root) : void 0;
|
|
3284
|
+
if (selection && (args.flags.remote || args.flags.resume || args.flags["event-id"])) {
|
|
3285
|
+
if (selection.bindings.length !== 1 || selection.unbound_session_ids.length) throw new Error("session_id_requires_unique_binding_use_session");
|
|
3286
|
+
selected = selection.bindings[0].id;
|
|
3287
|
+
args = { ...args, flags: { ...args.flags, session: selected } };
|
|
3288
|
+
}
|
|
3289
|
+
const event = value(args, "event-id");
|
|
3290
|
+
if (args.flags.remote && args.flags.resume) throw new Error("remote_and_resume_are_mutually_exclusive");
|
|
3291
|
+
if (!args.flags.resume && !event) {
|
|
3292
|
+
if (args.flags.remote && !selected) throw new Error("--remote requires --session");
|
|
3293
|
+
const bounds = selection?.bindings ?? (selected ? [getBinding(selected, root)] : statusBindings(root, value(args, "agent")));
|
|
3294
|
+
const sessions = bounds.map((bound2) => ({ ...localSkillStatus(bound2, root), ...selection?.host_chat_id ? { host_chat_id: selection.host_chat_id } : {} }));
|
|
3295
|
+
if (args.flags.remote) {
|
|
3296
|
+
const id = sessions[0]?.current?.event_id;
|
|
3297
|
+
if (id) Object.assign(sessions[0], { evaluation: unbox(await (await verifiedClient(bounds[0])).get("/v1/skill-evolution/evaluations", { event_id: id })) });
|
|
3298
|
+
else if (sessions[0]) Object.assign(sessions[0], { remote_status: "no_event_id" });
|
|
3299
|
+
}
|
|
3300
|
+
const result = { auth_scope: selected ? bounds[0].auth_scope : void 0, sessions, ...selection ? { requested_session_id: sessionId, unbound_session_ids: selection.unbound_session_ids } : {} };
|
|
3301
|
+
if (args.flags.json) printResult(result, true);
|
|
3302
|
+
else {
|
|
3303
|
+
if (!sessions.length) console.log("No Skill sessions found. Start a bound Agent conversation first.");
|
|
3304
|
+
if (selection?.host_chat_id) console.log(`QoderWork chat: ${selection.host_chat_id}`);
|
|
3305
|
+
for (const id of selection?.unbound_session_ids ?? []) console.log(`No ContextDB binding observed for session: ${id}`);
|
|
3306
|
+
for (const s of sessions) {
|
|
3307
|
+
console.log(`${s.binding_id} ${s.agent} session=${s.session_id}`);
|
|
3308
|
+
console.log(` Session: ${s.active === false ? "inactive" : s.active === true ? "active" : "unknown"}`);
|
|
3309
|
+
console.log(` Workspace: ${s.auth_scope.workspace_id} Member: ${s.auth_scope.member_id}`);
|
|
3310
|
+
console.log(` Pending buffer (clears after submission): ${s.buffer.distinct_tool_calls}/10 calls, ${s.buffer.bytes}/40960 bytes, ${s.buffered_events} events; ${s.buffer.waiting_reason}`);
|
|
3311
|
+
console.log(` Evaluate: ${s.current?.event_id ?? "no_event_id"}; request=${s.current?.request_id ?? "none"}; delivery=${s.current?.delivery ?? "none"}`);
|
|
3312
|
+
if ("evaluation" in s) console.log(` Server: ${JSON.stringify(s.evaluation)}`);
|
|
3313
|
+
for (const h of s.handoffs) console.log(` Handoff: ${h.event_id}; delivery=${h.delivery}; completion=${h.completion}; automatic_redelivery=false`);
|
|
3314
|
+
if (s.error) console.log(` Error: ${s.error}`);
|
|
3315
|
+
for (const d of s.dispositions) console.log(` Not evaluated: ${d.reason}; events=${d.event_count}, bytes=${d.buffer_bytes}; ${d.path}`);
|
|
3316
|
+
for (const p of s.publications) console.log(` Publication: ${p.request_id}; verified=${p.verified}; ${JSON.stringify(p.result ?? null)}; ${p.receipt_path}`);
|
|
3317
|
+
console.log(` Drafts (scope-wide): ${s.paths.drafts}`);
|
|
3318
|
+
for (const file of s.scope_drafts) console.log(` Scope draft: ${file}`);
|
|
3319
|
+
for (const e of s.submissions) console.log(` Frozen request: ${e.request_id}; ${e.distinct_tool_calls} calls, ${e.request_bytes} bytes, ${e.event_count} events; ${e.path}`);
|
|
3320
|
+
console.log(` Manifest: ${s.manifest.path} (${s.manifest.status})`);
|
|
3321
|
+
for (const v of s.loaded_versions) console.log(` Loaded: ${v.skill_id}@${v.version_id}; ${v.path}`);
|
|
3322
|
+
console.log(` State: ${s.paths.state}`);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
return 0;
|
|
3326
|
+
}
|
|
3327
|
+
if (args.flags.resume && !selected && !event) throw new Error("--resume requires --session");
|
|
3328
|
+
const bound = await binding(args);
|
|
3329
|
+
if (event) {
|
|
3330
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(event)) throw new Error("invalid_event_id");
|
|
3331
|
+
printResult(unbox(await (await verifiedClient(bound)).get("/v1/skill-evolution/evaluations", { event_id: event })), !!args.flags.json);
|
|
3332
|
+
return 0;
|
|
3333
|
+
}
|
|
3334
|
+
const artifacts = join5("skill-evolution", bound.scope_key, "artifacts");
|
|
3335
|
+
if (existsSync5(privatePath(root, artifacts))) for (const name of readdirSync3(privatePath(root, artifacts))) {
|
|
3336
|
+
if (/^evaluation-[a-f0-9-]{36}\.json$/.test(name)) {
|
|
3337
|
+
const state = readState(root, join5(artifacts, name));
|
|
3338
|
+
if (state.binding_id === bound.id && !state.result) await submitManualEvaluation(
|
|
3339
|
+
bound,
|
|
3340
|
+
await verifiedClient(bound),
|
|
3341
|
+
state.evidence,
|
|
3342
|
+
"",
|
|
3343
|
+
state.evidence.client_request_id,
|
|
3344
|
+
root
|
|
3345
|
+
);
|
|
3346
|
+
} else if (/^upload-[a-f0-9-]{36}$/.test(name)) {
|
|
3347
|
+
const dir = join5(artifacts, name), state = readState(root, join5(dir, "publication.json"));
|
|
3348
|
+
if (state.binding_id !== bound.id || state.verified) continue;
|
|
3349
|
+
const client = await verifiedClient(bound);
|
|
3350
|
+
const pkg = await inspectZip(readPrivate(root, join5(dir, "bundle.zip"), MAX_ZIP), state.input.bundle_hash, state.input.bundle_size_bytes);
|
|
3351
|
+
await publishPackage(bound, client, pkg, state.input, root);
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
await expireSessionEvidence(bound, root);
|
|
3355
|
+
await resumeSubmission(bound, root);
|
|
3356
|
+
return skillStatus({ positional: [], flags: { session: bound.id, ...args.flags.json ? { json: true } : {} } });
|
|
3357
|
+
};
|
|
3358
|
+
var skillPurge = async (args) => {
|
|
3359
|
+
const bound = await binding(args), root = skillRoot();
|
|
3360
|
+
if (args.flags.cache) {
|
|
3361
|
+
const cache = join5("skills", bound.scope_key, "cache");
|
|
3362
|
+
const eligible2 = existsSync5(privatePath(root, cache)) ? [cache] : [];
|
|
3363
|
+
if (args.flags.apply) await withStateLock(root, cache, async () => {
|
|
3364
|
+
rmSync2(privatePath(root, cache), { recursive: true, force: true });
|
|
3365
|
+
});
|
|
3366
|
+
printResult({ applied: !!args.flags.apply, eligible: eligible2, prerequisite: "Stop related sessions and transfers before --apply; drafts are preserved." }, !!args.flags.json);
|
|
3367
|
+
return 0;
|
|
3368
|
+
}
|
|
3369
|
+
const artifacts = join5("skill-evolution", bound.scope_key, "artifacts"), eligible = [];
|
|
3370
|
+
if (existsSync5(privatePath(root, artifacts))) for (const name of readdirSync3(privatePath(root, artifacts))) {
|
|
3371
|
+
if (!/^upload-[a-f0-9-]{36}$/.test(name)) continue;
|
|
3372
|
+
const dir = join5(artifacts, name), state = readState(root, join5(dir, "publication.json"));
|
|
3373
|
+
if (state.verified && existsSync5(privatePath(root, dir, "bundle.zip"))) eligible.push(name);
|
|
3374
|
+
}
|
|
3375
|
+
if (args.flags.apply) for (const name of eligible) await withStateLock(root, join5(artifacts, name), async () => {
|
|
3376
|
+
if (readState(root, join5(artifacts, name, "publication.json")).verified)
|
|
3377
|
+
rmSync2(privatePath(root, artifacts, name, "bundle.zip"), { force: true });
|
|
3378
|
+
});
|
|
3379
|
+
printResult({ applied: !!args.flags.apply, scope_key: bound.scope_key, eligible }, !!args.flags.json);
|
|
3380
|
+
return 0;
|
|
3381
|
+
};
|
|
3382
|
+
var skillRetryFeedback = async (args) => {
|
|
3383
|
+
const id = value(args, "session"), event = value(args, "event-id");
|
|
3384
|
+
if (!id || !event) throw new Error("retry-feedback requires --session and --event-id");
|
|
3385
|
+
await retryFeedback(getBinding(id), event);
|
|
3386
|
+
printResult({ event_id: event, queued_for_next_user_prompt: true, published: false }, !!args.flags.json);
|
|
3387
|
+
return 0;
|
|
3388
|
+
};
|
|
3389
|
+
|
|
2722
3390
|
// src/cli/debug-cli.ts
|
|
2723
3391
|
import {
|
|
2724
3392
|
readRecallTraces
|
|
@@ -2880,12 +3548,12 @@ async function debugRecall(args) {
|
|
|
2880
3548
|
import {
|
|
2881
3549
|
accessSync,
|
|
2882
3550
|
constants as fsConstants,
|
|
2883
|
-
existsSync as
|
|
3551
|
+
existsSync as existsSync6,
|
|
2884
3552
|
openAsBlob,
|
|
2885
3553
|
statSync as statSync2
|
|
2886
|
-
} from "fs";
|
|
2887
|
-
import { homedir as
|
|
2888
|
-
import { basename, extname, resolve as
|
|
3554
|
+
} from "node:fs";
|
|
3555
|
+
import { homedir as homedir3 } from "node:os";
|
|
3556
|
+
import { basename, extname, resolve as resolve3 } from "node:path";
|
|
2889
3557
|
|
|
2890
3558
|
// src/lib/review.ts
|
|
2891
3559
|
function unwrapReviewData(response, operation) {
|
|
@@ -2901,31 +3569,31 @@ function unwrapReviewData(response, operation) {
|
|
|
2901
3569
|
}
|
|
2902
3570
|
return box.data ?? null;
|
|
2903
3571
|
}
|
|
2904
|
-
function asRecord(
|
|
2905
|
-
return
|
|
3572
|
+
function asRecord(value2) {
|
|
3573
|
+
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
2906
3574
|
}
|
|
2907
3575
|
function pickString(record, field) {
|
|
2908
|
-
const
|
|
2909
|
-
return typeof
|
|
3576
|
+
const value2 = record[field];
|
|
3577
|
+
return typeof value2 === "string" ? value2 : "";
|
|
2910
3578
|
}
|
|
2911
3579
|
function pickNumber(record, field) {
|
|
2912
|
-
const
|
|
2913
|
-
return typeof
|
|
3580
|
+
const value2 = record[field];
|
|
3581
|
+
return typeof value2 === "number" && Number.isFinite(value2) ? value2 : void 0;
|
|
2914
3582
|
}
|
|
2915
3583
|
function pickBoolean(record, field) {
|
|
2916
|
-
const
|
|
2917
|
-
return typeof
|
|
3584
|
+
const value2 = record[field];
|
|
3585
|
+
return typeof value2 === "boolean" ? value2 : void 0;
|
|
2918
3586
|
}
|
|
2919
|
-
function parseEmbeddedJson(
|
|
2920
|
-
if (typeof
|
|
3587
|
+
function parseEmbeddedJson(value2) {
|
|
3588
|
+
if (typeof value2 !== "string" || value2.trim() === "") return void 0;
|
|
2921
3589
|
try {
|
|
2922
|
-
return JSON.parse(
|
|
3590
|
+
return JSON.parse(value2);
|
|
2923
3591
|
} catch {
|
|
2924
3592
|
return void 0;
|
|
2925
3593
|
}
|
|
2926
3594
|
}
|
|
2927
|
-
function countArrayEntries(
|
|
2928
|
-
return Array.isArray(
|
|
3595
|
+
function countArrayEntries(value2) {
|
|
3596
|
+
return Array.isArray(value2) ? value2.length : void 0;
|
|
2929
3597
|
}
|
|
2930
3598
|
function readReviewListData(data) {
|
|
2931
3599
|
const record = asRecord(data);
|
|
@@ -3000,8 +3668,8 @@ function minimalReviewDetail(raw) {
|
|
|
3000
3668
|
"human_action",
|
|
3001
3669
|
"created_at"
|
|
3002
3670
|
]) {
|
|
3003
|
-
const
|
|
3004
|
-
if (
|
|
3671
|
+
const value2 = pickString(r, field);
|
|
3672
|
+
if (value2) out[field] = value2;
|
|
3005
3673
|
}
|
|
3006
3674
|
for (const field of [
|
|
3007
3675
|
"confidence_score",
|
|
@@ -3009,12 +3677,12 @@ function minimalReviewDetail(raw) {
|
|
|
3009
3677
|
"noise_score",
|
|
3010
3678
|
"contradiction_score"
|
|
3011
3679
|
]) {
|
|
3012
|
-
const
|
|
3013
|
-
if (
|
|
3680
|
+
const value2 = pickNumber(r, field);
|
|
3681
|
+
if (value2 !== void 0) out[field] = value2;
|
|
3014
3682
|
}
|
|
3015
3683
|
for (const field of ["has_contradictions", "rewrite_needed"]) {
|
|
3016
|
-
const
|
|
3017
|
-
if (
|
|
3684
|
+
const value2 = pickBoolean(r, field);
|
|
3685
|
+
if (value2 !== void 0) out[field] = value2;
|
|
3018
3686
|
}
|
|
3019
3687
|
const conflictCount = countArrayEntries(parseEmbeddedJson(r.conflicts));
|
|
3020
3688
|
if (conflictCount !== void 0) out.conflict_count = conflictCount;
|
|
@@ -3174,7 +3842,7 @@ async function memoryAdd(args) {
|
|
|
3174
3842
|
fail(`memory add cannot use --image with --no-infer; image memory requires inference
|
|
3175
3843
|
${usage2}`, 2);
|
|
3176
3844
|
}
|
|
3177
|
-
const images = imageFlags.length > 0 ? await Promise.all(imageFlags.map((
|
|
3845
|
+
const images = imageFlags.length > 0 ? await Promise.all(imageFlags.map((value2) => loadImagePart(value2, usage2))) : [];
|
|
3178
3846
|
const ctx = buildContext(args);
|
|
3179
3847
|
const userId = args.flags["user-id"] ?? ctx.cfg.userId;
|
|
3180
3848
|
const metadata = parseMetadataFlags(args.flags["metadata"]);
|
|
@@ -3225,12 +3893,12 @@ async function memorySearch(args) {
|
|
|
3225
3893
|
const ctx = buildContext(args);
|
|
3226
3894
|
const userId = args.flags["user-id"] ?? ctx.cfg.userId;
|
|
3227
3895
|
const topK = numFlag(args.flags["top-k"], ctx.cfg.topK);
|
|
3228
|
-
const
|
|
3896
|
+
const threshold2 = numFlag(args.flags["threshold"], ctx.cfg.threshold);
|
|
3229
3897
|
const body = {
|
|
3230
3898
|
query,
|
|
3231
3899
|
user_id: userId,
|
|
3232
3900
|
top_k: topK,
|
|
3233
|
-
threshold
|
|
3901
|
+
threshold: threshold2
|
|
3234
3902
|
};
|
|
3235
3903
|
if (args.flags.knowledge) {
|
|
3236
3904
|
body.knowledge = { enable: true, top_k: ctx.cfg.knowledgeTopK };
|
|
@@ -3352,7 +4020,7 @@ async function memoryEntityCreate(args) {
|
|
|
3352
4020
|
};
|
|
3353
4021
|
addOptionalEntityFields(body, args);
|
|
3354
4022
|
const ctx = buildContext(args);
|
|
3355
|
-
if (![body.user_id, body.agent_id, body.app_id, body.run_id].some((
|
|
4023
|
+
if (![body.user_id, body.agent_id, body.app_id, body.run_id].some((value2) => typeof value2 === "string" && value2.trim() !== "")) {
|
|
3356
4024
|
body.user_id = ctx.cfg.userId;
|
|
3357
4025
|
}
|
|
3358
4026
|
const resp = await ctx.client.postJson("/v1/memories/entities", body);
|
|
@@ -3417,18 +4085,18 @@ function parseMetadataFlags(raw) {
|
|
|
3417
4085
|
}
|
|
3418
4086
|
return out;
|
|
3419
4087
|
}
|
|
3420
|
-
function stringFlag(
|
|
3421
|
-
return typeof
|
|
4088
|
+
function stringFlag(value2) {
|
|
4089
|
+
return typeof value2 === "string" ? value2 : void 0;
|
|
3422
4090
|
}
|
|
3423
|
-
function csvFlag(
|
|
3424
|
-
return typeof
|
|
4091
|
+
function csvFlag(value2) {
|
|
4092
|
+
return typeof value2 === "string" ? value2.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
3425
4093
|
}
|
|
3426
|
-
function jsonStringArray(
|
|
3427
|
-
if (
|
|
3428
|
-
if (typeof
|
|
4094
|
+
function jsonStringArray(value2, name) {
|
|
4095
|
+
if (value2 === void 0) return [];
|
|
4096
|
+
if (typeof value2 !== "string") fail(`--${name} must be a JSON array of strings`, 2);
|
|
3429
4097
|
let parsed;
|
|
3430
4098
|
try {
|
|
3431
|
-
parsed = JSON.parse(
|
|
4099
|
+
parsed = JSON.parse(value2);
|
|
3432
4100
|
} catch {
|
|
3433
4101
|
fail(`--${name} must be a JSON array of strings`, 2);
|
|
3434
4102
|
}
|
|
@@ -3439,8 +4107,8 @@ function jsonStringArray(value, name) {
|
|
|
3439
4107
|
}
|
|
3440
4108
|
function addOptionalScopeFields(body, args) {
|
|
3441
4109
|
for (const [flag, field] of [["user-id", "user_id"], ["agent-id", "agent_id"], ["app-id", "app_id"], ["run-id", "run_id"]]) {
|
|
3442
|
-
const
|
|
3443
|
-
if (
|
|
4110
|
+
const value2 = stringFlag(args.flags[flag]);
|
|
4111
|
+
if (value2 !== void 0) body[field] = value2;
|
|
3444
4112
|
}
|
|
3445
4113
|
}
|
|
3446
4114
|
function addOptionalEntityFields(body, args) {
|
|
@@ -3452,8 +4120,8 @@ function addOptionalEntityFields(body, args) {
|
|
|
3452
4120
|
function repeatedFlagValues(args, name) {
|
|
3453
4121
|
const repeated = args.repeatedFlags?.[name];
|
|
3454
4122
|
if (repeated) return repeated;
|
|
3455
|
-
const
|
|
3456
|
-
return
|
|
4123
|
+
const value2 = args.flags[name];
|
|
4124
|
+
return value2 === void 0 ? [] : [value2];
|
|
3457
4125
|
}
|
|
3458
4126
|
async function loadImagePart(rawPath, usage2) {
|
|
3459
4127
|
if (typeof rawPath !== "string" || rawPath.trim() === "") {
|
|
@@ -3461,7 +4129,7 @@ async function loadImagePart(rawPath, usage2) {
|
|
|
3461
4129
|
${usage2}`, 2);
|
|
3462
4130
|
}
|
|
3463
4131
|
const path = expandLocalPath(rawPath);
|
|
3464
|
-
if (!
|
|
4132
|
+
if (!existsSync6(path)) fail(`image file not found: ${path}`, 2);
|
|
3465
4133
|
const stat = statSync2(path);
|
|
3466
4134
|
if (!stat.isFile()) fail(`image path is not a file: ${path}`, 2);
|
|
3467
4135
|
try {
|
|
@@ -3482,9 +4150,9 @@ ${usage2}`, 2);
|
|
|
3482
4150
|
}
|
|
3483
4151
|
function expandLocalPath(path) {
|
|
3484
4152
|
if (path === "~" || path.startsWith("~/")) {
|
|
3485
|
-
return
|
|
4153
|
+
return resolve3(homedir3(), path === "~" ? "" : path.slice(2));
|
|
3486
4154
|
}
|
|
3487
|
-
return
|
|
4155
|
+
return resolve3(path);
|
|
3488
4156
|
}
|
|
3489
4157
|
function imageMimeType(path) {
|
|
3490
4158
|
switch (extname(path).toLowerCase()) {
|
|
@@ -3501,7 +4169,7 @@ function imageMimeType(path) {
|
|
|
3501
4169
|
}
|
|
3502
4170
|
|
|
3503
4171
|
// src/cli/kb-cli.ts
|
|
3504
|
-
import { basename as basename2 } from "path";
|
|
4172
|
+
import { basename as basename2 } from "node:path";
|
|
3505
4173
|
var UPLOAD_TEXT_USAGE = "usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--file-path=<server-logical-path>] [--review-enabled|--no-review-enabled] [--wait|--no-wait]";
|
|
3506
4174
|
var UPLOAD_FILE_USAGE = "usage: ctxdb kb upload-file <kb-name> <local-path> [--doc-name=...] [--file-path=<server-logical-path>] [--review-enabled|--no-review-enabled] [--wait|--no-wait]";
|
|
3507
4175
|
var UPDATE_TEXT_USAGE = "usage: ctxdb kb update-text <document-id> --text=<body> [--wait|--no-wait]\n or: ctxdb kb update-text --kb-name=<kb-name> --doc-name=<logical-name> --text=<body> [--file-path=<server-logical-path>] [--wait|--no-wait]";
|
|
@@ -3521,17 +4189,17 @@ var ENTITY_CHUNKS_USAGE = "usage: ctxdb kb entity-chunks --kb=<kb-id> --entity-i
|
|
|
3521
4189
|
var RELATIONS_USAGE = "usage: ctxdb kb relations [--kb=<kb-id>] [--entity-id=<id> | --entity-ids=<a,b>]\n [--relation-type=<t>] [--page=N] [--page-size=N]\n [--max-nodes=N] [--max-edges=N] [--verbose] [--raw] [--json]";
|
|
3522
4190
|
var UPDATE_LOCATOR_FLAGS = ["kb-name", "doc-name", "file-path"];
|
|
3523
4191
|
function nonEmptyFlag(args, name) {
|
|
3524
|
-
const
|
|
3525
|
-
return typeof
|
|
4192
|
+
const value2 = args.flags[name];
|
|
4193
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
|
|
3526
4194
|
}
|
|
3527
4195
|
function optionalNonEmptyFlag(args, name, usage2) {
|
|
3528
4196
|
if (!Object.hasOwn(args.flags, name)) return void 0;
|
|
3529
|
-
const
|
|
3530
|
-
if (
|
|
4197
|
+
const value2 = nonEmptyFlag(args, name);
|
|
4198
|
+
if (value2 === void 0) {
|
|
3531
4199
|
fail(`--${name} must have a non-empty value
|
|
3532
4200
|
${usage2}`, 2);
|
|
3533
4201
|
}
|
|
3534
|
-
return
|
|
4202
|
+
return value2;
|
|
3535
4203
|
}
|
|
3536
4204
|
function hasAnyLocatorFlag(args) {
|
|
3537
4205
|
return UPDATE_LOCATOR_FLAGS.some((name) => Object.hasOwn(args.flags, name));
|
|
@@ -3603,10 +4271,10 @@ function parseKbLocator(args, usage2) {
|
|
|
3603
4271
|
function optionalNonNegativeInteger(args, name, usage2) {
|
|
3604
4272
|
const raw = args.flags[name];
|
|
3605
4273
|
if (raw === void 0) return void 0;
|
|
3606
|
-
const
|
|
3607
|
-
if (!Number.isInteger(
|
|
4274
|
+
const value2 = typeof raw === "string" ? Number(raw) : Number.NaN;
|
|
4275
|
+
if (!Number.isInteger(value2) || value2 < 0) fail(`--${name} must be a non-negative integer
|
|
3608
4276
|
${usage2}`, 2);
|
|
3609
|
-
return
|
|
4277
|
+
return value2;
|
|
3610
4278
|
}
|
|
3611
4279
|
function requiredIds(args, usage2) {
|
|
3612
4280
|
const raw = args.flags.ids;
|
|
@@ -3916,10 +4584,10 @@ function limitGraphRelations(result, maxNodes, maxEdges) {
|
|
|
3916
4584
|
if (!Array.isArray(graph.relations)) return result;
|
|
3917
4585
|
const nodeIds = /* @__PURE__ */ new Set();
|
|
3918
4586
|
const relations = [];
|
|
3919
|
-
for (const
|
|
4587
|
+
for (const value2 of graph.relations) {
|
|
3920
4588
|
if (maxEdges !== void 0 && relations.length >= maxEdges) break;
|
|
3921
|
-
if (!
|
|
3922
|
-
const relation =
|
|
4589
|
+
if (!value2 || typeof value2 !== "object") continue;
|
|
4590
|
+
const relation = value2;
|
|
3923
4591
|
const source = relation.source_entity_id;
|
|
3924
4592
|
const target = relation.target_entity_id;
|
|
3925
4593
|
if (typeof source !== "string" || typeof target !== "string") continue;
|
|
@@ -3927,7 +4595,7 @@ function limitGraphRelations(result, maxNodes, maxEdges) {
|
|
|
3927
4595
|
nextNodes.add(source);
|
|
3928
4596
|
nextNodes.add(target);
|
|
3929
4597
|
if (maxNodes !== void 0 && nextNodes.size > maxNodes) continue;
|
|
3930
|
-
relations.push(
|
|
4598
|
+
relations.push(value2);
|
|
3931
4599
|
nodeIds.add(source);
|
|
3932
4600
|
nodeIds.add(target);
|
|
3933
4601
|
}
|
|
@@ -3987,10 +4655,10 @@ async function kbSearch(args) {
|
|
|
3987
4655
|
return 0;
|
|
3988
4656
|
}
|
|
3989
4657
|
function positiveIntFlag(args, flagName, usage2) {
|
|
3990
|
-
const
|
|
3991
|
-
if (
|
|
3992
|
-
if (typeof
|
|
3993
|
-
const parsed = Number(
|
|
4658
|
+
const value2 = args.flags[flagName];
|
|
4659
|
+
if (value2 === void 0) return void 0;
|
|
4660
|
+
if (typeof value2 !== "string") fail(usage2, 2);
|
|
4661
|
+
const parsed = Number(value2);
|
|
3994
4662
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
3995
4663
|
fail(`${flagName} must be a positive integer
|
|
3996
4664
|
${usage2}`, 2);
|
|
@@ -4109,11 +4777,11 @@ async function kbAcl(operation, args) {
|
|
|
4109
4777
|
throw new Error(`exactly one ${operation === "delete" ? "ACL" : "KB"} ID is required`);
|
|
4110
4778
|
}
|
|
4111
4779
|
const allowed = /* @__PURE__ */ new Set([...FLAGS[operation], "agent", "json", "raw"]);
|
|
4112
|
-
for (const [flag,
|
|
4780
|
+
for (const [flag, value2] of Object.entries(args.flags)) {
|
|
4113
4781
|
if (!allowed.has(flag)) throw new Error(`unknown --${flag}`);
|
|
4114
4782
|
if (flag === "agent" || flag === "json" || flag === "raw") continue;
|
|
4115
|
-
if (typeof
|
|
4116
|
-
values[flag.replaceAll("-", "_")] =
|
|
4783
|
+
if (typeof value2 !== "string" || !value2.trim()) throw new Error(`--${flag} requires a value`);
|
|
4784
|
+
values[flag.replaceAll("-", "_")] = value2.trim();
|
|
4117
4785
|
}
|
|
4118
4786
|
for (const [field, choices] of Object.entries({
|
|
4119
4787
|
principal_type: ["member", "group"],
|
|
@@ -4164,6 +4832,139 @@ var kbAclSubjects = (args) => kbAcl("subjects", args);
|
|
|
4164
4832
|
var kbAclSet = (args) => kbAcl("set", args);
|
|
4165
4833
|
var kbAclDelete = (args) => kbAcl("delete", args);
|
|
4166
4834
|
|
|
4835
|
+
// src/cli/kb-graph-cli.ts
|
|
4836
|
+
var GRAPH_MUTATION_USAGE = {
|
|
4837
|
+
"entity-create": "ctxdb kb entity-create --kb=<kb-id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
|
|
4838
|
+
"entity-update": "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
|
|
4839
|
+
"entity-delete": "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes",
|
|
4840
|
+
"relation-create": "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<id> --target-entity-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
|
|
4841
|
+
"relation-update": "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
|
|
4842
|
+
"relation-delete": "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes"
|
|
4843
|
+
};
|
|
4844
|
+
var ENTITY_FIELDS = ["name", "type", "description", "aliases-json"];
|
|
4845
|
+
var RELATION_FIELDS = ["relation-type", "description", "keywords-json", "weight"];
|
|
4846
|
+
function stringFlag2(args, name, allowEmpty = false) {
|
|
4847
|
+
const value2 = args.flags[name];
|
|
4848
|
+
if (typeof value2 !== "string" || !allowEmpty && !value2.trim()) {
|
|
4849
|
+
throw new Error(`--${name} requires ${allowEmpty ? "a string value (use --description='' to clear)" : "a non-empty value"}`);
|
|
4850
|
+
}
|
|
4851
|
+
return value2;
|
|
4852
|
+
}
|
|
4853
|
+
function stringArrayFlag(args, name) {
|
|
4854
|
+
const text = stringFlag2(args, name);
|
|
4855
|
+
let value2;
|
|
4856
|
+
try {
|
|
4857
|
+
value2 = JSON.parse(text);
|
|
4858
|
+
} catch {
|
|
4859
|
+
throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
|
|
4860
|
+
}
|
|
4861
|
+
if (!Array.isArray(value2) || !value2.every((item) => typeof item === "string")) {
|
|
4862
|
+
throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
|
|
4863
|
+
}
|
|
4864
|
+
return value2;
|
|
4865
|
+
}
|
|
4866
|
+
function parseMutation(args, resource, action) {
|
|
4867
|
+
const idFlag = `${resource}-id`;
|
|
4868
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
4869
|
+
"kb",
|
|
4870
|
+
"agent",
|
|
4871
|
+
"json",
|
|
4872
|
+
"raw",
|
|
4873
|
+
...action === "delete" ? [idFlag, "yes"] : [
|
|
4874
|
+
...resource === "entity" ? ENTITY_FIELDS : RELATION_FIELDS,
|
|
4875
|
+
...action === "update" ? [idFlag] : resource === "relation" ? ["source-entity-id", "target-entity-id"] : []
|
|
4876
|
+
]
|
|
4877
|
+
]);
|
|
4878
|
+
if (args.positional.length) throw new Error("use explicit ID flags; positional locators are not supported");
|
|
4879
|
+
for (const [flag, value2] of Object.entries(args.flags)) {
|
|
4880
|
+
if (!allowed.has(flag)) throw new Error(`unknown --${flag}`);
|
|
4881
|
+
if (["yes", "json", "raw"].includes(flag) && value2 !== true) {
|
|
4882
|
+
throw new Error(`--${flag} is a boolean switch; do not supply a value`);
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
if (args.flags.agent !== void 0) stringFlag2(args, "agent");
|
|
4886
|
+
const knowledge_base_id = stringFlag2(args, "kb").trim();
|
|
4887
|
+
const locator = action === "create" ? {} : { [`${resource}_id`]: stringFlag2(args, idFlag).trim() };
|
|
4888
|
+
if (action === "delete") {
|
|
4889
|
+
if (args.flags.yes !== true) {
|
|
4890
|
+
throw new Error(`deletion is irreversible${resource === "entity" ? " and deletes all incoming/outgoing relations" : ""}; pass --yes to confirm`);
|
|
4891
|
+
}
|
|
4892
|
+
return { knowledge_base_id, ...locator };
|
|
4893
|
+
}
|
|
4894
|
+
if (resource === "entity") {
|
|
4895
|
+
return {
|
|
4896
|
+
knowledge_base_id,
|
|
4897
|
+
...locator,
|
|
4898
|
+
entity_name: stringFlag2(args, "name"),
|
|
4899
|
+
entity_type: stringFlag2(args, "type"),
|
|
4900
|
+
description: stringFlag2(args, "description", true),
|
|
4901
|
+
aliases: stringArrayFlag(args, "aliases-json")
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4904
|
+
const weight = Number(stringFlag2(args, "weight"));
|
|
4905
|
+
if (!Number.isFinite(weight)) throw new Error("--weight must be a finite number");
|
|
4906
|
+
return {
|
|
4907
|
+
knowledge_base_id,
|
|
4908
|
+
...locator,
|
|
4909
|
+
...action === "create" ? {
|
|
4910
|
+
source_entity_id: stringFlag2(args, "source-entity-id").trim(),
|
|
4911
|
+
target_entity_id: stringFlag2(args, "target-entity-id").trim()
|
|
4912
|
+
} : {},
|
|
4913
|
+
relation_type: stringFlag2(args, "relation-type"),
|
|
4914
|
+
description: stringFlag2(args, "description", true),
|
|
4915
|
+
keywords: stringArrayFlag(args, "keywords-json"),
|
|
4916
|
+
weight
|
|
4917
|
+
};
|
|
4918
|
+
}
|
|
4919
|
+
async function mutateGraph(args, resource, action) {
|
|
4920
|
+
const command = `${resource}-${action}`;
|
|
4921
|
+
const usage2 = `usage: ${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]
|
|
4922
|
+
`;
|
|
4923
|
+
if (args.flags.help || args.flags.h) {
|
|
4924
|
+
process.stdout.write(usage2);
|
|
4925
|
+
return 0;
|
|
4926
|
+
}
|
|
4927
|
+
let payload;
|
|
4928
|
+
try {
|
|
4929
|
+
payload = parseMutation(args, resource, action);
|
|
4930
|
+
} catch (error) {
|
|
4931
|
+
process.stderr.write(`${error.message}
|
|
4932
|
+
${usage2}`);
|
|
4933
|
+
return 2;
|
|
4934
|
+
}
|
|
4935
|
+
const { client } = buildContext(args);
|
|
4936
|
+
const path = resource === "entity" ? "/v1/knowledge/entities" : "/v1/knowledge/entity-relations";
|
|
4937
|
+
const response = action === "create" ? await client.postJson(path, payload) : action === "update" ? await client.putJson(path, payload) : await client.delete(path, payload);
|
|
4938
|
+
let data;
|
|
4939
|
+
try {
|
|
4940
|
+
data = unwrapReviewData(response, `kb ${command}`);
|
|
4941
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
4942
|
+
throw new CtxdbError(`kb ${command} returned no mutation result`);
|
|
4943
|
+
}
|
|
4944
|
+
const result = data;
|
|
4945
|
+
if (typeof result[`${resource}_id`] !== "string" || !result[`${resource}_id`] || result.knowledge_base_id !== payload.knowledge_base_id || action !== "create" && result[`${resource}_id`] !== payload[`${resource}_id`]) {
|
|
4946
|
+
throw new CtxdbError(`kb ${command} returned an invalid mutation locator`);
|
|
4947
|
+
}
|
|
4948
|
+
if (action === "delete") {
|
|
4949
|
+
if (typeof result.deleted !== "boolean") throw new CtxdbError(`kb ${command} returned no deletion status`);
|
|
4950
|
+
if (resource === "entity" && ["deleted_entity_count", "deleted_relation_count"].some(
|
|
4951
|
+
(key) => !Number.isSafeInteger(result[key]) || result[key] < 0
|
|
4952
|
+
)) throw new CtxdbError(`kb ${command} returned invalid deletion counts`);
|
|
4953
|
+
}
|
|
4954
|
+
} catch (error) {
|
|
4955
|
+
const requestId = response?.requestId;
|
|
4956
|
+
throw new CtxdbError(`${error.message}${typeof requestId === "string" ? ` (RequestId: ${requestId})` : ""}`);
|
|
4957
|
+
}
|
|
4958
|
+
printResult(args.flags.raw ? response : data, !!args.flags.json);
|
|
4959
|
+
return action === "delete" && data.deleted !== true ? 1 : 0;
|
|
4960
|
+
}
|
|
4961
|
+
var kbEntityCreate = (args) => mutateGraph(args, "entity", "create");
|
|
4962
|
+
var kbEntityUpdate = (args) => mutateGraph(args, "entity", "update");
|
|
4963
|
+
var kbEntityDelete = (args) => mutateGraph(args, "entity", "delete");
|
|
4964
|
+
var kbRelationCreate = (args) => mutateGraph(args, "relation", "create");
|
|
4965
|
+
var kbRelationUpdate = (args) => mutateGraph(args, "relation", "update");
|
|
4966
|
+
var kbRelationDelete = (args) => mutateGraph(args, "relation", "delete");
|
|
4967
|
+
|
|
4167
4968
|
// src/cli/promotion-cli.ts
|
|
4168
4969
|
var GENERATE_USAGE = "usage: ctxdb memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--wait|--no-wait] [--agent=<name>] [--json]";
|
|
4169
4970
|
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]";
|
|
@@ -4244,7 +5045,7 @@ ${GENERATE_USAGE}`, 2);
|
|
|
4244
5045
|
if (!topic || topic.trim() === "") fail(GENERATE_USAGE, 2);
|
|
4245
5046
|
const ctx = buildContext(args);
|
|
4246
5047
|
const body = { topic };
|
|
4247
|
-
const kbId =
|
|
5048
|
+
const kbId = stringFlag3(args, "knowledge-base-id");
|
|
4248
5049
|
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
4249
5050
|
const resp = await ctx.client.postJson("/v1/memory_promotions/generate", body);
|
|
4250
5051
|
let data = unwrapBox(resp, "memory promotion generate");
|
|
@@ -4260,7 +5061,7 @@ ${GENERATE_USAGE}`, 2);
|
|
|
4260
5061
|
}) + "\n");
|
|
4261
5062
|
const deadline = Date.now() + 3e5;
|
|
4262
5063
|
while (data.status === "generating" && Date.now() < deadline) {
|
|
4263
|
-
await new Promise((
|
|
5064
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1500));
|
|
4264
5065
|
data = unwrapBox(
|
|
4265
5066
|
await ctx.client.get("/v1/memory_promotions/detail", { promotion_id: promotionId }),
|
|
4266
5067
|
"memory promotion get"
|
|
@@ -4277,7 +5078,7 @@ ${GENERATE_USAGE}`, 2);
|
|
|
4277
5078
|
async function promotionList(args) {
|
|
4278
5079
|
if (showHelp(args, LIST_USAGE)) return 0;
|
|
4279
5080
|
const params = {};
|
|
4280
|
-
const status2 =
|
|
5081
|
+
const status2 = stringFlag3(args, "status");
|
|
4281
5082
|
if (status2 !== void 0) {
|
|
4282
5083
|
if (!PROMOTION_STATUSES.includes(status2)) {
|
|
4283
5084
|
fail(`invalid --status: ${status2} (expected one of ${PROMOTION_STATUSES.join("|")})
|
|
@@ -4289,7 +5090,7 @@ ${LIST_USAGE}`, 2);
|
|
|
4289
5090
|
if (page !== void 0) params.page = page;
|
|
4290
5091
|
const pageSize = intFlag(args, "page-size", LIST_USAGE);
|
|
4291
5092
|
if (pageSize !== void 0) params.page_size = pageSize;
|
|
4292
|
-
const sortBy =
|
|
5093
|
+
const sortBy = stringFlag3(args, "sort-by");
|
|
4293
5094
|
if (sortBy !== void 0) {
|
|
4294
5095
|
if (sortBy !== "quality_score") {
|
|
4295
5096
|
fail(`invalid --sort-by: ${sortBy} (expected quality_score)
|
|
@@ -4297,7 +5098,7 @@ ${LIST_USAGE}`, 2);
|
|
|
4297
5098
|
}
|
|
4298
5099
|
params.sort_by = sortBy;
|
|
4299
5100
|
}
|
|
4300
|
-
const sortOrder =
|
|
5101
|
+
const sortOrder = stringFlag3(args, "sort-order");
|
|
4301
5102
|
if (sortOrder !== void 0) {
|
|
4302
5103
|
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
4303
5104
|
fail(`invalid --sort-order: ${sortOrder} (expected asc|desc)
|
|
@@ -4339,7 +5140,7 @@ async function promotionReject(args) {
|
|
|
4339
5140
|
const promotionId = requireIdFlag(args, "promotion-id", REJECT_USAGE);
|
|
4340
5141
|
const ctx = buildContext(args);
|
|
4341
5142
|
const body = { promotion_id: promotionId };
|
|
4342
|
-
const reason =
|
|
5143
|
+
const reason = stringFlag3(args, "reason");
|
|
4343
5144
|
if (reason !== void 0) body.reason = reason;
|
|
4344
5145
|
const resp = await ctx.client.postJson("/v1/memory_promotions/reject", body);
|
|
4345
5146
|
emit(args, resp, "memory promotion reject", (data) => data);
|
|
@@ -4347,17 +5148,17 @@ async function promotionReject(args) {
|
|
|
4347
5148
|
}
|
|
4348
5149
|
async function promotionBatchAction(args) {
|
|
4349
5150
|
if (showHelp(args, BATCH_ACTION_USAGE)) return 0;
|
|
4350
|
-
const action =
|
|
5151
|
+
const action = stringFlag3(args, "action");
|
|
4351
5152
|
if (action === void 0 || !BATCH_ACTIONS.includes(action)) {
|
|
4352
5153
|
fail(`--action is required and must be one of ${BATCH_ACTIONS.join("|")}
|
|
4353
5154
|
${BATCH_ACTION_USAGE}`, 2);
|
|
4354
5155
|
}
|
|
4355
5156
|
const ids = parseIdsFlag(args, BATCH_ACTION_USAGE);
|
|
4356
|
-
const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) :
|
|
5157
|
+
const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag3(args, "knowledge-base-id");
|
|
4357
5158
|
const ctx = buildContext(args);
|
|
4358
5159
|
const body = { action, ids };
|
|
4359
5160
|
if (kbId !== void 0) body.knowledge_base_id = kbId;
|
|
4360
|
-
const reason =
|
|
5161
|
+
const reason = stringFlag3(args, "reason");
|
|
4361
5162
|
if (reason !== void 0) body.reason = reason;
|
|
4362
5163
|
const resp = await ctx.client.postJson("/v1/memory_promotions/batch", body);
|
|
4363
5164
|
const data = emit(args, resp, "memory promotion batch-action", (data2) => data2);
|
|
@@ -4383,8 +5184,8 @@ async function promotionResolveDoubts(args) {
|
|
|
4383
5184
|
${RESOLVE_DOUBTS_USAGE}`, 2);
|
|
4384
5185
|
}
|
|
4385
5186
|
const resolutions = parseResolutions(resolutionsRaw, RESOLVE_DOUBTS_USAGE);
|
|
4386
|
-
const title =
|
|
4387
|
-
const content =
|
|
5187
|
+
const title = stringFlag3(args, "title");
|
|
5188
|
+
const content = stringFlag3(args, "content");
|
|
4388
5189
|
if (resolutions.length === 0 && title === void 0 && content === void 0) {
|
|
4389
5190
|
fail(`provide a resolution or a non-empty --title/--content edit
|
|
4390
5191
|
${RESOLVE_DOUBTS_USAGE}`, 2);
|
|
@@ -4478,7 +5279,7 @@ async function promotionSettingsGet(args) {
|
|
|
4478
5279
|
async function promotionSettingsUpdate(args) {
|
|
4479
5280
|
if (showHelp(args, SETTINGS_UPDATE_USAGE)) return 0;
|
|
4480
5281
|
const body = {};
|
|
4481
|
-
const enabled =
|
|
5282
|
+
const enabled = stringFlag3(args, "auto-ingest-enabled");
|
|
4482
5283
|
if (enabled !== void 0) {
|
|
4483
5284
|
if (enabled !== "true" && enabled !== "false") {
|
|
4484
5285
|
fail(`invalid --auto-ingest-enabled: ${enabled} (expected true|false)
|
|
@@ -4486,16 +5287,16 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
|
|
|
4486
5287
|
}
|
|
4487
5288
|
body.auto_ingest_enabled = enabled === "true";
|
|
4488
5289
|
}
|
|
4489
|
-
const
|
|
4490
|
-
if (
|
|
4491
|
-
const n = Number(
|
|
5290
|
+
const threshold2 = stringFlag3(args, "auto-ingest-threshold");
|
|
5291
|
+
if (threshold2 !== void 0) {
|
|
5292
|
+
const n = Number(threshold2);
|
|
4492
5293
|
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
4493
|
-
fail(`invalid --auto-ingest-threshold: ${
|
|
5294
|
+
fail(`invalid --auto-ingest-threshold: ${threshold2} (expected a number in [0,1])
|
|
4494
5295
|
${SETTINGS_UPDATE_USAGE}`, 2);
|
|
4495
5296
|
}
|
|
4496
5297
|
body.auto_ingest_threshold = n;
|
|
4497
5298
|
}
|
|
4498
|
-
const defaultKbId =
|
|
5299
|
+
const defaultKbId = stringFlag3(args, "default-kb-id");
|
|
4499
5300
|
const clearDefaultKb = args.flags["clear-default-kb"] === true;
|
|
4500
5301
|
if (defaultKbId !== void 0 && clearDefaultKb) {
|
|
4501
5302
|
fail(`cannot use --default-kb-id and --clear-default-kb together
|
|
@@ -4512,15 +5313,15 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
|
|
|
4512
5313
|
emit(args, resp, "memory promotion settings update", (data) => data);
|
|
4513
5314
|
return 0;
|
|
4514
5315
|
}
|
|
4515
|
-
function
|
|
4516
|
-
const
|
|
4517
|
-
return typeof
|
|
5316
|
+
function stringFlag3(args, name) {
|
|
5317
|
+
const value2 = args.flags[name];
|
|
5318
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
|
|
4518
5319
|
}
|
|
4519
5320
|
function requireIdFlag(args, name, usage2) {
|
|
4520
|
-
const
|
|
4521
|
-
if (
|
|
5321
|
+
const value2 = stringFlag3(args, name);
|
|
5322
|
+
if (value2 === void 0) fail(`--${name} is required
|
|
4522
5323
|
${usage2}`, 2);
|
|
4523
|
-
return
|
|
5324
|
+
return value2;
|
|
4524
5325
|
}
|
|
4525
5326
|
function requireTemplateIdPositional(args, usage2) {
|
|
4526
5327
|
const templateId = args.positional[0];
|
|
@@ -4528,7 +5329,7 @@ function requireTemplateIdPositional(args, usage2) {
|
|
|
4528
5329
|
return templateId;
|
|
4529
5330
|
}
|
|
4530
5331
|
function intFlag(args, name, usage2) {
|
|
4531
|
-
const raw =
|
|
5332
|
+
const raw = stringFlag3(args, name);
|
|
4532
5333
|
if (raw === void 0) return void 0;
|
|
4533
5334
|
const n = Number(raw);
|
|
4534
5335
|
if (!Number.isInteger(n) || n < 1) {
|
|
@@ -4538,7 +5339,7 @@ ${usage2}`, 2);
|
|
|
4538
5339
|
return n;
|
|
4539
5340
|
}
|
|
4540
5341
|
function parseIdsFlag(args, usage2) {
|
|
4541
|
-
const raw =
|
|
5342
|
+
const raw = stringFlag3(args, "ids");
|
|
4542
5343
|
if (raw === void 0) fail(`--ids is required (comma-separated)
|
|
4543
5344
|
${usage2}`, 2);
|
|
4544
5345
|
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -4588,7 +5389,7 @@ ${usage2}`, 2);
|
|
|
4588
5389
|
});
|
|
4589
5390
|
}
|
|
4590
5391
|
function buildTemplateUpsertBody(args, usage2) {
|
|
4591
|
-
const name =
|
|
5392
|
+
const name = stringFlag3(args, "name");
|
|
4592
5393
|
if (name === void 0) fail(`--name is required
|
|
4593
5394
|
${usage2}`, 2);
|
|
4594
5395
|
const sectionsRaw = args.flags["sections-json"];
|
|
@@ -4673,8 +5474,8 @@ function batchExitCode(data) {
|
|
|
4673
5474
|
function pickFields(source, fields) {
|
|
4674
5475
|
const out = {};
|
|
4675
5476
|
for (const key of fields) {
|
|
4676
|
-
const
|
|
4677
|
-
if (
|
|
5477
|
+
const value2 = source[key];
|
|
5478
|
+
if (value2 !== void 0 && value2 !== null) out[key] = value2;
|
|
4678
5479
|
}
|
|
4679
5480
|
return out;
|
|
4680
5481
|
}
|
|
@@ -4714,7 +5515,7 @@ function compactTemplateList(data) {
|
|
|
4714
5515
|
}
|
|
4715
5516
|
|
|
4716
5517
|
// src/cli/review-cli.ts
|
|
4717
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
5518
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
4718
5519
|
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]";
|
|
4719
5520
|
var REVIEW_GET_USAGE = "usage: ctxdb kb review get <review-id> [--content-page=N] [--verbose] [--raw] [--json]";
|
|
4720
5521
|
var REVIEW_ACTION_USAGE = `usage: ctxdb kb review action <review-id> --action=<${REVIEW_ACTIONS.join("|")}>
|
|
@@ -4734,29 +5535,29 @@ var TEMPLATE_CLEAR_DEFAULT_USAGE2 = "usage: ctxdb kb review template clear-defau
|
|
|
4734
5535
|
var SETTINGS_GET_USAGE2 = "usage: ctxdb kb review settings get <knowledge-base-id> [--json]";
|
|
4735
5536
|
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]";
|
|
4736
5537
|
function stringValue(args, name) {
|
|
4737
|
-
const
|
|
4738
|
-
return typeof
|
|
5538
|
+
const value2 = args.flags[name];
|
|
5539
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
|
|
4739
5540
|
}
|
|
4740
5541
|
function requireId(args, flagName, positionalIndex, usage2) {
|
|
4741
5542
|
const positional2 = args.positional[positionalIndex]?.trim();
|
|
4742
|
-
const
|
|
4743
|
-
if (
|
|
4744
|
-
return
|
|
5543
|
+
const value2 = positional2 !== void 0 && positional2 !== "" ? positional2 : stringValue(args, flagName);
|
|
5544
|
+
if (value2 === void 0) fail(usage2, 2);
|
|
5545
|
+
return value2.trim();
|
|
4745
5546
|
}
|
|
4746
5547
|
function requireEnumFlag(args, flagName, allowed, usage2) {
|
|
4747
|
-
const
|
|
4748
|
-
if (
|
|
4749
|
-
if (!allowed.includes(
|
|
5548
|
+
const value2 = stringValue(args, flagName);
|
|
5549
|
+
if (value2 === void 0) fail(usage2, 2);
|
|
5550
|
+
if (!allowed.includes(value2)) {
|
|
4750
5551
|
fail(`${flagName} must be one of ${allowed.join("|")}
|
|
4751
5552
|
${usage2}`, 2);
|
|
4752
5553
|
}
|
|
4753
|
-
return
|
|
5554
|
+
return value2;
|
|
4754
5555
|
}
|
|
4755
5556
|
function positiveIntFlag2(args, flagName, usage2) {
|
|
4756
|
-
const
|
|
4757
|
-
if (
|
|
4758
|
-
if (typeof
|
|
4759
|
-
const parsed = Number(
|
|
5557
|
+
const value2 = args.flags[flagName];
|
|
5558
|
+
if (value2 === void 0) return void 0;
|
|
5559
|
+
if (typeof value2 !== "string") fail(usage2, 2);
|
|
5560
|
+
const parsed = Number(value2);
|
|
4760
5561
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
4761
5562
|
fail(`${flagName} must be a positive integer
|
|
4762
5563
|
${usage2}`, 2);
|
|
@@ -5111,10 +5912,10 @@ ${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
|
5111
5912
|
else if (off) body[field] = false;
|
|
5112
5913
|
}
|
|
5113
5914
|
for (const [flag, field, integer] of SETTINGS_NUMBER_FLAG_FIELDS) {
|
|
5114
|
-
const
|
|
5115
|
-
if (
|
|
5116
|
-
if (typeof
|
|
5117
|
-
const parsed = Number(
|
|
5915
|
+
const value2 = args.flags[flag];
|
|
5916
|
+
if (value2 === void 0) continue;
|
|
5917
|
+
if (typeof value2 !== "string") fail(SETTINGS_UPDATE_USAGE2, 2);
|
|
5918
|
+
const parsed = Number(value2);
|
|
5118
5919
|
if (!Number.isFinite(parsed) || integer && !Number.isInteger(parsed)) {
|
|
5119
5920
|
fail(`--${flag} must be ${integer ? "an integer" : "a number"}
|
|
5120
5921
|
${SETTINGS_UPDATE_USAGE2}`, 2);
|
|
@@ -5169,20 +5970,20 @@ function outputs(...extra) {
|
|
|
5169
5970
|
...extra
|
|
5170
5971
|
];
|
|
5171
5972
|
}
|
|
5172
|
-
function details(
|
|
5973
|
+
function details(value2) {
|
|
5173
5974
|
return {
|
|
5174
|
-
usage:
|
|
5175
|
-
positionals:
|
|
5176
|
-
options:
|
|
5177
|
-
constraints:
|
|
5178
|
-
outputModes:
|
|
5179
|
-
warnings:
|
|
5180
|
-
examples:
|
|
5181
|
-
notes:
|
|
5975
|
+
usage: value2.usage,
|
|
5976
|
+
positionals: value2.positionals ?? [],
|
|
5977
|
+
options: value2.options ?? [],
|
|
5978
|
+
constraints: value2.constraints ?? [],
|
|
5979
|
+
outputModes: value2.outputModes ?? outputs(),
|
|
5980
|
+
warnings: value2.warnings ?? [],
|
|
5981
|
+
examples: value2.examples,
|
|
5982
|
+
notes: value2.notes ?? []
|
|
5182
5983
|
};
|
|
5183
5984
|
}
|
|
5184
|
-
function leaf(name, summary, handler,
|
|
5185
|
-
return { kind: "command", name, aliases, summary, handler, ...
|
|
5985
|
+
function leaf(name, summary, handler, value2, aliases = []) {
|
|
5986
|
+
return { kind: "command", name, aliases, summary, handler, ...value2 };
|
|
5186
5987
|
}
|
|
5187
5988
|
function group(name, summary, children, aliases = []) {
|
|
5188
5989
|
return { kind: "group", name, aliases, summary, children };
|
|
@@ -5239,12 +6040,14 @@ function publicSetupSpec(managedCredentials) {
|
|
|
5239
6040
|
notes: managedCredentials ? [
|
|
5240
6041
|
"Without --agent, setup writes the CLI-only default profile and installs no Hooks or Skills.",
|
|
5241
6042
|
"Run ctxdb login before setup to create managed credentials.",
|
|
6043
|
+
"Codex setup does not grant hook trust: review only ContextDB hooks with /hooks or App Settings > Hooks. JSON includes the pending review action.",
|
|
5242
6044
|
"Hermes requires approval of pre_llm_call and post_llm_call with: hermes --accept-hooks chat"
|
|
5243
6045
|
] : [
|
|
5244
6046
|
"Without --agent, setup writes the CLI-only default profile and installs no Hooks or Skills.",
|
|
5245
6047
|
"Ordinary setup preserves existing authentication; a profile with no authentication may bind the default login.",
|
|
5246
6048
|
"Explicitly unbound, invalid, or conflicting authentication is not automatically replaced; use --login or --api-key to switch.",
|
|
5247
6049
|
"A shared login keeps its Core origin; --base-url cannot redirect its tokens to another origin.",
|
|
6050
|
+
"Codex setup does not grant hook trust: review only ContextDB hooks with /hooks or App Settings > Hooks. JSON includes the pending review action.",
|
|
5248
6051
|
"Hermes requires approval of pre_llm_call and post_llm_call with: hermes --accept-hooks chat"
|
|
5249
6052
|
],
|
|
5250
6053
|
examples: managedCredentials ? [
|
|
@@ -5403,7 +6206,116 @@ var debugGroup = group("debug", "Control and inspect local recall traces.", [
|
|
|
5403
6206
|
})
|
|
5404
6207
|
)
|
|
5405
6208
|
]);
|
|
5406
|
-
var
|
|
6209
|
+
var SKILL_SCOPE_OPTIONS = [
|
|
6210
|
+
option("--session", "Use an existing Skill session binding.", { valueName: "binding-id" }),
|
|
6211
|
+
AGENT_OPTION,
|
|
6212
|
+
JSON_OPTION
|
|
6213
|
+
];
|
|
6214
|
+
var SKILL_ID_OPTION = option("--skill-id", "Skill UUID.", { valueName: "id", required: true });
|
|
6215
|
+
var skillGroup = group("skill", "Install, discover, publish, and manage ContextDB Skills.", [
|
|
6216
|
+
leaf("bind", "Bind a session to the current member and refresh its Skill manifest.", skillBind, details({
|
|
6217
|
+
usage: ["ctxdb skill bind --session-id=<id> [--agent=<name>] [--json]"],
|
|
6218
|
+
options: [option("--session-id", "Agent session identifier.", { valueName: "id", required: true }), AGENT_OPTION, JSON_OPTION],
|
|
6219
|
+
examples: [{ command: "ctxdb skill bind --session-id=<session-id> --agent=codex --json" }]
|
|
6220
|
+
})),
|
|
6221
|
+
leaf("manifest", "Refresh the visible Skill manifest for this subject.", skillManifest, details({
|
|
6222
|
+
usage: ["ctxdb skill manifest [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6223
|
+
options: SKILL_SCOPE_OPTIONS,
|
|
6224
|
+
examples: [{ command: "ctxdb skill manifest --session=<binding-id> --json" }]
|
|
6225
|
+
}), ["sync"]),
|
|
6226
|
+
leaf("list", "List active Skills visible to the current subject.", skillList, details({
|
|
6227
|
+
usage: ["ctxdb skill list [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6228
|
+
options: SKILL_SCOPE_OPTIONS,
|
|
6229
|
+
notes: ["Refreshes the authoritative remote manifest; it does not fall back to stale cached permissions."],
|
|
6230
|
+
examples: [{ command: "ctxdb skill list --agent=qwenwork --json" }]
|
|
6231
|
+
})),
|
|
6232
|
+
leaf("config", "Read effective Skill configuration or change an Agent-local setting.", skillConfig, details({
|
|
6233
|
+
usage: [
|
|
6234
|
+
"ctxdb skill config [get [auto-skill-evolution]] [--session=<binding-id>] [--agent=<name>] [--json]",
|
|
6235
|
+
"ctxdb skill config set auto-skill-evolution <on|off> --agent=<name> [--json]",
|
|
6236
|
+
"ctxdb skill config unset auto-skill-evolution --agent=<name> [--json]"
|
|
6237
|
+
],
|
|
6238
|
+
positionals: [
|
|
6239
|
+
positional("action", "Optional operation: get, set, or unset.", false),
|
|
6240
|
+
positional("key", "Agent-local configuration key.", false),
|
|
6241
|
+
positional("value", "Configuration value; auto-skill-evolution accepts on or off.", false)
|
|
6242
|
+
],
|
|
6243
|
+
options: SKILL_SCOPE_OPTIONS,
|
|
6244
|
+
constraints: ["set and unset require --agent and cannot be combined with --session."],
|
|
6245
|
+
notes: ["Only auto-skill-evolution is locally writable. Service policy and thresholds remain read-only."],
|
|
6246
|
+
examples: [
|
|
6247
|
+
{ command: "ctxdb skill config --session=<binding-id> --json" },
|
|
6248
|
+
{ command: "ctxdb skill config set auto-skill-evolution off --agent=qwenwork --json" }
|
|
6249
|
+
]
|
|
6250
|
+
})),
|
|
6251
|
+
leaf("search", "Search remote Skills visible to the current subject.", skillSearch, details({
|
|
6252
|
+
usage: ["ctxdb skill search <query> [--top-k=<n>] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6253
|
+
positionals: [positional("query", "Search text, at most 512 Unicode code points.")],
|
|
6254
|
+
options: [option("--top-k", "Number of results, from 1 to 10.", { valueName: "n", type: "integer", default: 5 }), ...SKILL_SCOPE_OPTIONS],
|
|
6255
|
+
examples: [{ command: "ctxdb skill search 'validate JSONL' --top-k=5 --json" }]
|
|
6256
|
+
})),
|
|
6257
|
+
leaf("load", "Download and verify a Skill version, optionally creating an editable copy.", skillLoad, details({
|
|
6258
|
+
usage: ["ctxdb skill load --skill-id=<id> [--version-id=<id>] [--editable] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6259
|
+
options: [SKILL_ID_OPTION, option("--version-id", "Exact version UUID; omit to select the active version.", { valueName: "id" }), option("--editable", "Create an editable draft from the verified package.", { type: "boolean" }), ...SKILL_SCOPE_OPTIONS],
|
|
6260
|
+
examples: [{ command: "ctxdb skill load --skill-id=<skill-id> --version-id=<version-id> --json" }]
|
|
6261
|
+
})),
|
|
6262
|
+
leaf("disable", "Disable a private Skill while retaining its history.", skillDisable, details({
|
|
6263
|
+
usage: ["ctxdb skill disable --skill-id=<id> [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6264
|
+
options: [SKILL_ID_OPTION, ...SKILL_SCOPE_OPTIONS],
|
|
6265
|
+
warnings: ["Requires an explicit user instruction; unavailable during automatic maintenance."],
|
|
6266
|
+
examples: [{ command: "ctxdb skill disable --skill-id=<skill-id> --json" }]
|
|
6267
|
+
})),
|
|
6268
|
+
leaf("delete", "Delete a private Skill and clean up its history and packages.", skillDelete, details({
|
|
6269
|
+
usage: ["ctxdb skill delete --skill-id=<id> [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6270
|
+
options: [SKILL_ID_OPTION, ...SKILL_SCOPE_OPTIONS],
|
|
6271
|
+
warnings: ["Deletes historical packages. Requires an explicit user instruction; unavailable during automatic maintenance."],
|
|
6272
|
+
examples: [{ command: "ctxdb skill delete --skill-id=<skill-id> --json" }]
|
|
6273
|
+
})),
|
|
6274
|
+
leaf("rollback", "Activate a retained version using the expected current version.", skillRollback, details({
|
|
6275
|
+
usage: ["ctxdb skill rollback --skill-id=<id> --version-id=<target> --base-version-id=<current> [--revoke-reason=<reason>] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6276
|
+
options: [SKILL_ID_OPTION, option("--version-id", "Target version UUID.", { valueName: "target", required: true }), option("--base-version-id", "Expected active version UUID.", { valueName: "current", required: true }), option("--revoke-reason", "Optional nonblank reason, at most 1000 UTF-16 code units.", { valueName: "reason" }), ...SKILL_SCOPE_OPTIONS],
|
|
6277
|
+
warnings: ["Requires an explicit user instruction; unavailable during automatic maintenance."],
|
|
6278
|
+
examples: [{ command: "ctxdb skill rollback --skill-id=<skill-id> --version-id=<target> --base-version-id=<current> --json" }]
|
|
6279
|
+
})),
|
|
6280
|
+
leaf("validate", "Check local Skill package structure without executing scripts.", skillValidate, details({
|
|
6281
|
+
usage: ["ctxdb skill validate <path> [--json]"],
|
|
6282
|
+
positionals: [positional("path", "Skill package directory or ZIP path.", true, "path")],
|
|
6283
|
+
options: [JSON_OPTION],
|
|
6284
|
+
notes: ["Reports validation_scope=static_package and scripts_executed=false; does not establish semantic correctness."],
|
|
6285
|
+
examples: [{ command: "ctxdb skill validate ./my-skill --json" }]
|
|
6286
|
+
})),
|
|
6287
|
+
leaf("upload", "Publish a new Skill or update an existing Skill with Base validation.", skillUpload, details({
|
|
6288
|
+
usage: ["ctxdb skill upload <path> [--source=<import|evolution>] [--skill-id=<id> --base-version-id=<id>] [--request-id=<id>] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6289
|
+
positionals: [positional("path", "Skill package directory or ZIP path.", true, "path")],
|
|
6290
|
+
options: [option("--source", "Publication source.", { valueName: "source", choices: ["import", "evolution"], default: "import" }), option("--skill-id", "Existing Skill UUID for an update.", { valueName: "id" }), option("--base-version-id", "Expected active version UUID for an update.", { valueName: "id" }), option("--request-id", "Idempotency UUID; retries retain the original identifier.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
|
|
6291
|
+
constraints: ["Supply --skill-id and --base-version-id together, or omit both to create a Skill."],
|
|
6292
|
+
notes: ["Resolve unfinished uploads with skill status --resume before changing the draft."],
|
|
6293
|
+
examples: [{ command: "ctxdb skill upload ./my-skill --source=import --json" }]
|
|
6294
|
+
})),
|
|
6295
|
+
leaf("evaluate", "Submit an evidence JSON file for Skill evaluation.", skillEvaluate, details({
|
|
6296
|
+
usage: ["ctxdb skill evaluate <evidence.json> [--request-id=<id>] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6297
|
+
positionals: [positional("evidence.json", "Evidence JSON object file, at most 65536 bytes.", true, "path")],
|
|
6298
|
+
options: [option("--request-id", "Idempotency UUID for this evaluation.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
|
|
6299
|
+
examples: [{ command: "ctxdb skill evaluate ./evidence.json --session=<binding-id> --json" }]
|
|
6300
|
+
})),
|
|
6301
|
+
leaf("status", "Inspect Skill evaluation and publication state or resume unfinished work.", skillStatus, details({
|
|
6302
|
+
usage: ["ctxdb skill status [--remote | --resume] [--event-id=<id>] [--session=<binding-id> | --session-id=<agent-session-id>] [--agent=<name>] [--json]"],
|
|
6303
|
+
options: [option("--session-id", "Agent session ID; QoderWork on macOS/Windows also accepts its UI chat ID. Requires --agent; mutually exclusive with --session. Multiple matches require an explicit Binding ID for remote/recovery operations.", { valueName: "id" }), option("--remote", "Query the selected session current server event without resuming work.", { type: "boolean" }), option("--resume", "Resume unfinished evaluations and publications; --session restricts recovery to that binding.", { type: "boolean" }), option("--event-id", "Query one server evaluation event.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
|
|
6304
|
+
notes: ["Default is local read-only status; without a session selector lists bindings, optionally filtered by --agent. --session filters precisely.", "--remote and --resume require --session or a uniquely resolved --session-id and cannot be combined. With --event-id, queries that event only; --resume has no effect.", "--session-id resolves existing bindings only. QoderWork UI lookup requires Node 22.13+, 23.4+, or 24+ and reads its local SQLite database read-only; missing bindings are listed separately. It never creates a binding.", "Drafts are scope-wide. Local Load records do not prove Agent business reuse."],
|
|
6305
|
+
examples: [{ command: "ctxdb skill status --resume --session=<binding-id> --json" }]
|
|
6306
|
+
})),
|
|
6307
|
+
leaf("retry-feedback", "Explicitly re-deliver a retained evaluation on the next user prompt.", skillRetryFeedback, details({
|
|
6308
|
+
usage: ["ctxdb skill retry-feedback --session=<binding-id> --event-id=<id> [--json]"],
|
|
6309
|
+
options: [option("--event-id", "Retained evaluation ID.", { valueName: "id", required: true }), ...SKILL_SCOPE_OPTIONS],
|
|
6310
|
+
notes: ["Use only after explicit user authorization; stop related Agent activity first. Does not author, upload, or re-submit evidence.", "Refuses pending evaluations and existing or uncertain publications. Check status and permissions first; resume frozen uploads separately."],
|
|
6311
|
+
examples: [{ command: "ctxdb skill retry-feedback --session=<binding-id> --event-id=<id> --json" }]
|
|
6312
|
+
})),
|
|
6313
|
+
leaf("purge", "Preview or remove verified publication payloads or the Skill cache.", skillPurge, details({
|
|
6314
|
+
usage: ["ctxdb skill purge [--cache] [--apply] [--session=<binding-id>] [--agent=<name>] [--json]"],
|
|
6315
|
+
options: [option("--cache", "Select the downloaded Skill cache instead of verified upload payloads.", { type: "boolean" }), option("--apply", "Perform deletion; otherwise only preview eligible data.", { type: "boolean" }), ...SKILL_SCOPE_OPTIONS],
|
|
6316
|
+
warnings: ["Stop related sessions and transfers before applying cache deletion. Drafts and unfinished upload payloads are preserved."],
|
|
6317
|
+
examples: [{ command: "ctxdb skill purge --json" }, { command: "ctxdb skill purge --cache --apply --session=<binding-id> --json" }]
|
|
6318
|
+
})),
|
|
5407
6319
|
leaf(
|
|
5408
6320
|
"install",
|
|
5409
6321
|
"Install ContextDB Skills to a built-in Agent or custom directory.",
|
|
@@ -6013,6 +6925,70 @@ var kbAclGroup = group("acl", "Manage direct knowledge-base grants and inspect e
|
|
|
6013
6925
|
examples: [{ command: "ctxdb kb acl delete <acl-id> --json" }]
|
|
6014
6926
|
}))
|
|
6015
6927
|
]);
|
|
6928
|
+
var graphKbOption = option("--kb", "Knowledge-base ID (not a name).", { valueName: "kb-id", required: true });
|
|
6929
|
+
var graphEntityIdOption = option("--entity-id", "Exact entity ID within the knowledge base.", { valueName: "id", required: true });
|
|
6930
|
+
var graphRelationIdOption = option("--relation-id", "Exact relation ID within the knowledge base.", { valueName: "id", required: true });
|
|
6931
|
+
var graphEntityFields = [
|
|
6932
|
+
option("--name", "Complete entity name.", { valueName: "name", required: true }),
|
|
6933
|
+
option("--type", "Complete entity type.", { valueName: "type", required: true }),
|
|
6934
|
+
option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
|
|
6935
|
+
option("--aliases-json", "Complete string array; use '[]' for no aliases.", { valueName: "json-array", type: "json", required: true })
|
|
6936
|
+
];
|
|
6937
|
+
var graphRelationFields = [
|
|
6938
|
+
option("--relation-type", "Complete relation type.", { valueName: "type", required: true }),
|
|
6939
|
+
option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
|
|
6940
|
+
option("--keywords-json", "Complete string array; use '[]' for no keywords.", { valueName: "json-array", type: "json", required: true }),
|
|
6941
|
+
option("--weight", "Relation weight between 0 and 1; range checked by the server.", { valueName: "number", type: "number", required: true })
|
|
6942
|
+
];
|
|
6943
|
+
var graphMutationOutput = [AGENT_OPTION, RAW_OPTION, JSON_OPTION];
|
|
6944
|
+
var graphDeleteConfirmation = option("--yes", "Explicitly confirm irreversible deletion; required in terminals and scripts.", { type: "boolean", required: true });
|
|
6945
|
+
var graphMutationUsage = (command) => [`${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]`];
|
|
6946
|
+
var graphMutationCommands = [
|
|
6947
|
+
leaf("entity-create", "Create one knowledge-graph entity.", kbEntityCreate, details({
|
|
6948
|
+
usage: graphMutationUsage("entity-create"),
|
|
6949
|
+
options: [graphKbOption, ...graphEntityFields, ...graphMutationOutput],
|
|
6950
|
+
notes: ["Returns the server-assigned entity ID and editable fields. This operates on the KB graph, independently of memory entities."],
|
|
6951
|
+
examples: [{ command: "ctxdb kb entity-create --kb=<kb-id> --name=ContextDB --type=Product --description='Context database' --aliases-json='[]' --json" }]
|
|
6952
|
+
})),
|
|
6953
|
+
leaf("entity-update", "Replace the editable fields of one knowledge-graph entity.", kbEntityUpdate, details({
|
|
6954
|
+
usage: graphMutationUsage("entity-update"),
|
|
6955
|
+
options: [graphKbOption, graphEntityIdOption, ...graphEntityFields, ...graphMutationOutput],
|
|
6956
|
+
warnings: ["Full replacement: read entity-get first and supply all editable fields. Empty description/aliases explicitly clear them.", "Renaming preserves the entity ID and updates incident relation names. The server rejects renames exceeding its incident-relation limit."],
|
|
6957
|
+
examples: [{ command: "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=ContextDB --type=Product --description='' --aliases-json='[]' --json" }]
|
|
6958
|
+
})),
|
|
6959
|
+
leaf("entity-delete", "Irreversibly delete an entity and all incoming/outgoing relations.", kbEntityDelete, details({
|
|
6960
|
+
usage: graphMutationUsage("entity-delete"),
|
|
6961
|
+
options: [graphKbOption, graphEntityIdOption, graphDeleteConfirmation, ...graphMutationOutput],
|
|
6962
|
+
warnings: ["Irreversible: deletes the selected entity and every incident relation. No delete request is sent without --yes."],
|
|
6963
|
+
notes: ["Only exact IDs are accepted; names and bulk deletion are unsupported.", "Returns deleted, deleted_entity_count and deleted_relation_count from the server. A false deleted result exits 1."],
|
|
6964
|
+
examples: [{ command: "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --json" }]
|
|
6965
|
+
})),
|
|
6966
|
+
leaf("relation-create", "Create one relation between knowledge-graph entity IDs.", kbRelationCreate, details({
|
|
6967
|
+
usage: graphMutationUsage("relation-create"),
|
|
6968
|
+
options: [
|
|
6969
|
+
graphKbOption,
|
|
6970
|
+
option("--source-entity-id", "Existing source entity ID.", { valueName: "id", required: true }),
|
|
6971
|
+
option("--target-entity-id", "Existing target entity ID.", { valueName: "id", required: true }),
|
|
6972
|
+
...graphRelationFields,
|
|
6973
|
+
...graphMutationOutput
|
|
6974
|
+
],
|
|
6975
|
+
examples: [{ command: "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<a> --target-entity-id=<b> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
|
|
6976
|
+
})),
|
|
6977
|
+
leaf("relation-update", "Replace the editable fields of one knowledge-graph relation.", kbRelationUpdate, details({
|
|
6978
|
+
usage: graphMutationUsage("relation-update"),
|
|
6979
|
+
options: [graphKbOption, graphRelationIdOption, ...graphRelationFields, ...graphMutationOutput],
|
|
6980
|
+
warnings: ["Full replacement: read relations --verbose first and supply all editable fields. Empty description/keywords explicitly clear them."],
|
|
6981
|
+
constraints: ["Relation ID and source/target entity IDs cannot be changed."],
|
|
6982
|
+
examples: [{ command: "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
|
|
6983
|
+
})),
|
|
6984
|
+
leaf("relation-delete", "Irreversibly delete one knowledge-graph relation.", kbRelationDelete, details({
|
|
6985
|
+
usage: graphMutationUsage("relation-delete"),
|
|
6986
|
+
options: [graphKbOption, graphRelationIdOption, graphDeleteConfirmation, ...graphMutationOutput],
|
|
6987
|
+
warnings: ["Irreversible: deletes only this relation; both endpoint entities remain. No delete request is sent without --yes."],
|
|
6988
|
+
notes: ["Only exact relation IDs are accepted. Returns deleted from the server; a false result exits 1. The relation API does not return deletion counts."],
|
|
6989
|
+
examples: [{ command: "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --json" }]
|
|
6990
|
+
}))
|
|
6991
|
+
];
|
|
6016
6992
|
var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph, and review.", [
|
|
6017
6993
|
leaf("create", "Create a knowledge base.", kbCreate, details({
|
|
6018
6994
|
usage: ["ctxdb kb create <kb-name> [--description=<text>] [--graph-enabled|--no-graph-enabled] [--review-enabled|--no-review-enabled] [--agent=<name>] [--json]"],
|
|
@@ -6277,6 +7253,7 @@ var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph,
|
|
|
6277
7253
|
notes: ["--entity-id matches either endpoint; --entity-ids requires both endpoints in the supplied set."],
|
|
6278
7254
|
examples: [{ command: "ctxdb kb relations --kb=<kb-id> --entity-id=<id> --verbose --json" }]
|
|
6279
7255
|
})),
|
|
7256
|
+
...graphMutationCommands,
|
|
6280
7257
|
reviewGroup
|
|
6281
7258
|
]);
|
|
6282
7259
|
function createCommandTree(options = {}) {
|
|
@@ -6339,8 +7316,8 @@ function findChild(parent, name) {
|
|
|
6339
7316
|
|
|
6340
7317
|
// src/cli/device-auth-cli.ts
|
|
6341
7318
|
import { AuthorizationService, publicAuthorizationTarget, oauthSessionCandidate, oauthSessionFromCredential } from "@aliyunrds/ctxdb-shared";
|
|
6342
|
-
import { dirname as dirname2, join as
|
|
6343
|
-
import { createInterface } from "readline/promises";
|
|
7319
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
7320
|
+
import { createInterface } from "node:readline/promises";
|
|
6344
7321
|
import {
|
|
6345
7322
|
CoreDeviceClient,
|
|
6346
7323
|
normalizeCoreOrigin as normalizeCoreOrigin2,
|
|
@@ -6361,12 +7338,12 @@ import {
|
|
|
6361
7338
|
} from "@aliyunrds/ctxdb-shared";
|
|
6362
7339
|
|
|
6363
7340
|
// src/lib/oauth-login-config.ts
|
|
6364
|
-
import { existsSync as
|
|
7341
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3 } from "node:fs";
|
|
6365
7342
|
import { isOfficialProductionUrl, normalizeCoreOrigin } from "@aliyunrds/ctxdb-shared";
|
|
6366
7343
|
var PRODUCTION_AUTHORIZATION_URL = "https://rdsnext.console.aliyun.com/contextDbCliAuthorization/cn-hangzhou";
|
|
6367
|
-
function authorizationPage(
|
|
6368
|
-
if (typeof
|
|
6369
|
-
const url = new URL(
|
|
7344
|
+
function authorizationPage(value2) {
|
|
7345
|
+
if (typeof value2 !== "string" || !value2.trim()) throw new Error();
|
|
7346
|
+
const url = new URL(value2);
|
|
6370
7347
|
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error();
|
|
6371
7348
|
return url.toString();
|
|
6372
7349
|
}
|
|
@@ -6379,7 +7356,7 @@ function consoleAuthorizationBase(path, core, loginUrl) {
|
|
|
6379
7356
|
}
|
|
6380
7357
|
}
|
|
6381
7358
|
const fallback = isOfficialProductionUrl(core) ? PRODUCTION_AUTHORIZATION_URL : void 0;
|
|
6382
|
-
if (!
|
|
7359
|
+
if (!existsSync7(path)) return fallback;
|
|
6383
7360
|
try {
|
|
6384
7361
|
const raw = JSON.parse(readFileSync3(path, "utf8"));
|
|
6385
7362
|
if (raw.version !== 1 || !Array.isArray(raw.environments) || raw.environments.length > 32) throw new Error();
|
|
@@ -6393,8 +7370,8 @@ function consoleAuthorizationBase(path, core, loginUrl) {
|
|
|
6393
7370
|
}
|
|
6394
7371
|
|
|
6395
7372
|
// src/lib/open-browser.ts
|
|
6396
|
-
import { spawn } from "child_process";
|
|
6397
|
-
import { platform } from "os";
|
|
7373
|
+
import { spawn } from "node:child_process";
|
|
7374
|
+
import { platform } from "node:os";
|
|
6398
7375
|
function openBrowser(url) {
|
|
6399
7376
|
try {
|
|
6400
7377
|
const parsed = new URL(url);
|
|
@@ -6404,19 +7381,19 @@ function openBrowser(url) {
|
|
|
6404
7381
|
}
|
|
6405
7382
|
const os = platform();
|
|
6406
7383
|
const command = os === "darwin" ? { file: "open", args: [url] } : os === "win32" ? { file: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] } : { file: "xdg-open", args: [url] };
|
|
6407
|
-
return new Promise((
|
|
7384
|
+
return new Promise((resolve5) => {
|
|
6408
7385
|
try {
|
|
6409
7386
|
const child = spawn(command.file, command.args, { detached: true, stdio: "ignore", windowsHide: true, shell: false });
|
|
6410
|
-
const timer = setTimeout(() =>
|
|
7387
|
+
const timer = setTimeout(() => resolve5(true), 2e3);
|
|
6411
7388
|
const finish = (opened) => {
|
|
6412
7389
|
clearTimeout(timer);
|
|
6413
|
-
|
|
7390
|
+
resolve5(opened);
|
|
6414
7391
|
};
|
|
6415
7392
|
child.once("error", () => finish(false));
|
|
6416
7393
|
child.once("exit", (code) => finish(code === 0));
|
|
6417
7394
|
child.unref();
|
|
6418
7395
|
} catch {
|
|
6419
|
-
|
|
7396
|
+
resolve5(false);
|
|
6420
7397
|
}
|
|
6421
7398
|
});
|
|
6422
7399
|
}
|
|
@@ -6433,7 +7410,7 @@ function createPublicAuthCommands(dependencies = {}) {
|
|
|
6433
7410
|
});
|
|
6434
7411
|
const client = dependencies.client ?? ((url) => new CoreDeviceClient(url));
|
|
6435
7412
|
const launchBrowser = dependencies.openBrowser ?? openBrowser;
|
|
6436
|
-
const output = (
|
|
7413
|
+
const output = (value2, json) => stdout(json ? JSON.stringify(value2) + "\n" : String(value2) + "\n");
|
|
6437
7414
|
function options(args, command) {
|
|
6438
7415
|
const allowed = ["agent", "json", "help", "h", ...command === "login" ? ["base-url", "login-url", "server", "device-code", "no-browser", "login", "yes"] : command === "logout" ? ["login", "yes"] : command === "status" ? ["all"] : []];
|
|
6439
7416
|
if (args.positional.length || Object.keys(args.flags).some((k) => !allowed.includes(k))) throw new Error("Unsupported authentication option.");
|
|
@@ -6479,7 +7456,7 @@ function createPublicAuthCommands(dependencies = {}) {
|
|
|
6479
7456
|
const pending = beginLogin(path, agent, name);
|
|
6480
7457
|
await confirmImpact(name ? pending.users[0] ?? agent : agent, pending.users, args, "Reauthorizing this login");
|
|
6481
7458
|
const baseUrl = normalizeCoreOrigin2(explicitBase ?? DEFAULT_BASE_URL);
|
|
6482
|
-
const consoleBase = consoleAuthorizationBase(
|
|
7459
|
+
const consoleBase = consoleAuthorizationBase(join6(dirname2(path), "device-environments.json"), baseUrl, args.flags["login-url"]);
|
|
6483
7460
|
const connection = client(baseUrl), controller = new AbortController();
|
|
6484
7461
|
let browserOpening = Promise.resolve();
|
|
6485
7462
|
const interrupt = () => controller.abort();
|
|
@@ -6788,23 +7765,23 @@ function renderRows(rows, indent = " ") {
|
|
|
6788
7765
|
return rows.map(([label, description]) => `${indent}${label.padEnd(width)} ${description}
|
|
6789
7766
|
`).join("");
|
|
6790
7767
|
}
|
|
6791
|
-
function optionLabel(
|
|
6792
|
-
const valuePart =
|
|
7768
|
+
function optionLabel(value2) {
|
|
7769
|
+
const valuePart = value2.valueName ? ` <${value2.valueName}>` : "";
|
|
6793
7770
|
const annotations = [
|
|
6794
|
-
|
|
6795
|
-
|
|
7771
|
+
value2.required ? "required" : "",
|
|
7772
|
+
value2.repeatable ? "repeatable" : ""
|
|
6796
7773
|
].filter(Boolean);
|
|
6797
|
-
return `${
|
|
7774
|
+
return `${value2.name}${valuePart}${annotations.length ? ` (${annotations.join("; ")})` : ""}`;
|
|
6798
7775
|
}
|
|
6799
|
-
function optionDescription(
|
|
7776
|
+
function optionDescription(value2) {
|
|
6800
7777
|
const details2 = [
|
|
6801
|
-
|
|
6802
|
-
|
|
7778
|
+
value2.default !== void 0 ? `Default: ${String(value2.default)}.` : "",
|
|
7779
|
+
value2.choices?.length ? `Choices: ${value2.choices.join("|")}.` : ""
|
|
6803
7780
|
].filter(Boolean);
|
|
6804
|
-
return `${
|
|
7781
|
+
return `${value2.description}${details2.length ? ` ${details2.join(" ")}` : ""}`;
|
|
6805
7782
|
}
|
|
6806
|
-
function positionalLabel(
|
|
6807
|
-
return `${
|
|
7783
|
+
function positionalLabel(value2) {
|
|
7784
|
+
return `${value2.name} <${value2.type}> (${value2.required ? "required" : "optional"})`;
|
|
6808
7785
|
}
|
|
6809
7786
|
function renderExamples(examples) {
|
|
6810
7787
|
return examples.map(
|
|
@@ -6854,7 +7831,7 @@ function renderListSection(title, values) {
|
|
|
6854
7831
|
if (values.length === 0) return "";
|
|
6855
7832
|
return `
|
|
6856
7833
|
${title}
|
|
6857
|
-
${values.map((
|
|
7834
|
+
${values.map((value2) => ` ${value2}
|
|
6858
7835
|
`).join("")}`;
|
|
6859
7836
|
}
|
|
6860
7837
|
function renderLeaf(leaf2, path) {
|
|
@@ -6864,11 +7841,11 @@ USAGE
|
|
|
6864
7841
|
${leaf2.usage.map((usage2) => ` ${usage2}
|
|
6865
7842
|
`).join("")}
|
|
6866
7843
|
ARGUMENTS
|
|
6867
|
-
${renderRows(leaf2.positionals.map((
|
|
7844
|
+
${renderRows(leaf2.positionals.map((value2) => [positionalLabel(value2), value2.description]))}
|
|
6868
7845
|
OPTIONS
|
|
6869
|
-
${renderRows(leaf2.options.map((
|
|
7846
|
+
${renderRows(leaf2.options.map((value2) => [optionLabel(value2), optionDescription(value2)]))}` + renderListSection("CONSTRAINTS", leaf2.constraints) + `
|
|
6870
7847
|
OUTPUT
|
|
6871
|
-
${renderRows(leaf2.outputModes.map((
|
|
7848
|
+
${renderRows(leaf2.outputModes.map((value2) => [value2.name, value2.description]))}` + renderListSection("NOTES", leaf2.notes) + renderListSection("WARNINGS", leaf2.warnings) + `
|
|
6872
7849
|
EXAMPLES
|
|
6873
7850
|
${renderExamples(leaf2.examples)}`;
|
|
6874
7851
|
}
|
|
@@ -6900,15 +7877,15 @@ function serializeNode(command, path, recursive) {
|
|
|
6900
7877
|
return {
|
|
6901
7878
|
...base,
|
|
6902
7879
|
usage: [...serializable.usage],
|
|
6903
|
-
positionals: serializable.positionals.map((
|
|
6904
|
-
options: serializable.options.map((
|
|
6905
|
-
...
|
|
6906
|
-
...
|
|
7880
|
+
positionals: serializable.positionals.map((value2) => ({ ...value2 })),
|
|
7881
|
+
options: serializable.options.map((value2) => ({
|
|
7882
|
+
...value2,
|
|
7883
|
+
...value2.choices ? { choices: [...value2.choices] } : {}
|
|
6907
7884
|
})),
|
|
6908
7885
|
constraints: [...serializable.constraints],
|
|
6909
|
-
outputModes: serializable.outputModes.map((
|
|
7886
|
+
outputModes: serializable.outputModes.map((value2) => ({ ...value2 })),
|
|
6910
7887
|
warnings: [...serializable.warnings],
|
|
6911
|
-
examples: serializable.examples.map((
|
|
7888
|
+
examples: serializable.examples.map((value2) => ({ ...value2 })),
|
|
6912
7889
|
notes: [...serializable.notes]
|
|
6913
7890
|
};
|
|
6914
7891
|
}
|
|
@@ -6967,8 +7944,8 @@ function resolvePath(root, tokens) {
|
|
|
6967
7944
|
}
|
|
6968
7945
|
return { node, path, consumed, parent };
|
|
6969
7946
|
}
|
|
6970
|
-
function isUnknown(
|
|
6971
|
-
return "group" in
|
|
7947
|
+
function isUnknown(value2) {
|
|
7948
|
+
return "group" in value2;
|
|
6972
7949
|
}
|
|
6973
7950
|
function writeUnknown(unknown, root, version) {
|
|
6974
7951
|
const scope = unknown.path.length === 1 ? "command" : "subcommand";
|
|
@@ -7097,8 +8074,8 @@ function runMain(root = COMMAND_TREE) {
|
|
|
7097
8074
|
}
|
|
7098
8075
|
var directEntry = (() => {
|
|
7099
8076
|
try {
|
|
7100
|
-
const modulePath =
|
|
7101
|
-
const argvPath =
|
|
8077
|
+
const modulePath = resolve4(fileURLToPath2(import.meta.url));
|
|
8078
|
+
const argvPath = resolve4(process.argv[1] ?? "");
|
|
7102
8079
|
return modulePath === argvPath || realpathSync(modulePath) === realpathSync(argvPath);
|
|
7103
8080
|
} catch {
|
|
7104
8081
|
return false;
|