@aliyunrds/ctxdb 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{chunk-HAUTENYD.js → chunk-6S5RJYBC.js} +20 -77
- package/dist/{chunk-7NOXAU2X.js → chunk-LIC44DR6.js} +9 -2
- package/dist/chunk-Q2EEP4CE.js +69 -0
- package/dist/{chunk-QSSNPN3M.js → chunk-S45GOYUU.js} +46 -12
- package/dist/cli/main.js +417 -164
- package/dist/hooks/session-start.js +10 -3
- package/dist/hooks/stop.js +100 -21
- package/dist/hooks/user-prompt-submit.js +14 -5
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +161 -0
- package/dist/setup/skills/contextdb-memory/SKILL.md +142 -0
- package/package.json +2 -2
- package/dist/setup/skills/cli-only/SKILL.md +0 -116
- package/dist/setup/skills/hooks-driven/SKILL.md +0 -144
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @aliyunrds/ctxdb
|
|
2
2
|
|
|
3
|
-
Unified access layer for RDS ContextDatabase. One `ctxdb` CLI (memory + KB ops), one `setup
|
|
3
|
+
Unified access layer for RDS ContextDatabase. One `ctxdb` CLI (memory + KB ops), one `setup` installer (`--agent <…>` for hooks/skills, bare for CLI-only), per-agent SKILL.md — supports multiple code agents from a single package.
|
|
4
4
|
|
|
5
5
|
| Agent | Hooks | Skill install path | Skill style |
|
|
6
6
|
|---|---|---|---|
|
|
@@ -59,7 +59,7 @@ Implementation note: qoder/Claude consume the JSON `hookSpecificOutput.additiona
|
|
|
59
59
|
|
|
60
60
|
See `ctxdb --help`.
|
|
61
61
|
|
|
62
|
-
`setup`
|
|
62
|
+
`setup` without `--agent` writes `agents.default` in `~/.ctxdb/ctxdb.json` (CLI-only, no hooks or skills); with `--agent <qoder|codex|claude>` it writes agent config and installs hooks + skills for that harness. Memory and KB commands also accept `--agent <name>` so agents use their own config section; when omitted, `CTXDB_AGENT` env wins, otherwise `agents.default` is used. `uninstall` without `--agent` loops every supported agent; with `--agent <name>` it targets just that one. `teardown` is an alias for `uninstall`.
|
|
63
63
|
|
|
64
64
|
`memory add … --no-infer` stores the text verbatim (skips server-side LLM
|
|
65
65
|
fact-extraction). Use it when the user explicitly asks for a verbatim
|
|
@@ -100,7 +100,7 @@ Lives at `~/.ctxdb/ctxdb.json` (co-located with logs at `~/.ctxdb/logs/`). Schem
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
Agent selection is driven by `--agent <qoder|codex|claude>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `
|
|
103
|
+
Agent selection is driven by `--agent <qoder|codex|claude|default>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `default`. There is no `default_agent` field — older installs that have one written get it dropped on the next save.
|
|
104
104
|
|
|
105
105
|
Field reference:
|
|
106
106
|
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
NotFoundError
|
|
4
|
-
} from "./chunk-QSSNPN3M.js";
|
|
5
2
|
|
|
6
3
|
// src/lib/kb.ts
|
|
7
4
|
import { readFileSync, existsSync, statSync } from "fs";
|
|
@@ -12,16 +9,6 @@ var KB_COLLECTION = "/v1/knowledge/knowledge_bases";
|
|
|
12
9
|
var DOCUMENTS = "/v1/knowledge/documents";
|
|
13
10
|
var FILES = "/v1/knowledge/files";
|
|
14
11
|
var DOCUMENT_DETAIL = "/v1/knowledge/documents/detail";
|
|
15
|
-
async function tryNewThenLegacy(newPath, newCall, legacyCall) {
|
|
16
|
-
try {
|
|
17
|
-
return await newCall();
|
|
18
|
-
} catch (err) {
|
|
19
|
-
if (err instanceof NotFoundError && err.path === newPath) {
|
|
20
|
-
return legacyCall();
|
|
21
|
-
}
|
|
22
|
-
throw err;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
12
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
26
13
|
var DEFAULT_INGEST_TIMEOUT_MS = 3e4;
|
|
27
14
|
var DEFAULT_FILE_INGEST_TIMEOUT_MS = 6e4;
|
|
@@ -48,40 +35,20 @@ async function listKnowledgeBases(client) {
|
|
|
48
35
|
}
|
|
49
36
|
return [];
|
|
50
37
|
}
|
|
51
|
-
async function findKb(client, kbNameOrId) {
|
|
52
|
-
for (const kb of await listKnowledgeBases(client)) {
|
|
53
|
-
if (!kb || typeof kb !== "object") continue;
|
|
54
|
-
if (kb.name === kbNameOrId || kb.id === kbNameOrId) return kb;
|
|
55
|
-
}
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
38
|
async function createKb(client, kbName, description = "") {
|
|
59
39
|
return client.postJson(KB_COLLECTION, { name: kbName, description: description || "" });
|
|
60
40
|
}
|
|
61
|
-
async function
|
|
62
|
-
const existing = await findKb(client, kbName);
|
|
63
|
-
if (existing) return { kb: existing, created: false };
|
|
64
|
-
const created = await createKb(client, kbName, description);
|
|
65
|
-
return { kb: created, created: true };
|
|
66
|
-
}
|
|
67
|
-
async function uploadText(client, kbId, docName, text, mimeType = "text/plain", filePath) {
|
|
41
|
+
async function uploadText(client, kbName, docName, text, mimeType = "text/plain", filePath) {
|
|
68
42
|
const body = {
|
|
69
|
-
|
|
43
|
+
knowledge_base_name: kbName,
|
|
70
44
|
name: docName,
|
|
71
45
|
text,
|
|
72
46
|
mime_type: mimeType
|
|
73
47
|
};
|
|
74
48
|
if (filePath !== void 0 && filePath !== "") body.file_path = filePath;
|
|
75
|
-
return
|
|
76
|
-
DOCUMENTS,
|
|
77
|
-
() => client.postJson(DOCUMENTS, body),
|
|
78
|
-
() => {
|
|
79
|
-
const { knowledge_base_id: _drop, ...legacyBody } = body;
|
|
80
|
-
return client.postJson(`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents`, legacyBody);
|
|
81
|
-
}
|
|
82
|
-
);
|
|
49
|
+
return client.postJson(DOCUMENTS, body);
|
|
83
50
|
}
|
|
84
|
-
async function uploadFile(client,
|
|
51
|
+
async function uploadFile(client, kbName, localPath, options = {}) {
|
|
85
52
|
const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
86
53
|
const expanded = expandHome(localPath);
|
|
87
54
|
if (!existsSync(expanded)) throw new Error(`file not found: ${expanded}`);
|
|
@@ -97,30 +64,17 @@ async function uploadFile(client, kbId, localPath, options = {}) {
|
|
|
97
64
|
const content = readFileSync(expanded);
|
|
98
65
|
const mime = guessMime(expanded);
|
|
99
66
|
const fields = {
|
|
100
|
-
|
|
67
|
+
knowledge_base_name: kbName,
|
|
101
68
|
name: docName
|
|
102
69
|
};
|
|
103
70
|
if (options.filePath !== void 0 && options.filePath !== "") {
|
|
104
71
|
fields.file_path = options.filePath;
|
|
105
72
|
}
|
|
106
|
-
|
|
107
|
-
return tryNewThenLegacy(
|
|
73
|
+
return client.postMultipart(
|
|
108
74
|
FILES,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{ file: { filename, content, mimeType: mime } },
|
|
113
|
-
uploadOpts
|
|
114
|
-
),
|
|
115
|
-
() => {
|
|
116
|
-
const { knowledge_base_id: _drop, ...legacyFields } = fields;
|
|
117
|
-
return client.postMultipart(
|
|
118
|
-
`${KB_COLLECTION}/${encodeURIComponent(kbId)}/files`,
|
|
119
|
-
legacyFields,
|
|
120
|
-
{ file: { filename, content, mimeType: mime } },
|
|
121
|
-
uploadOpts
|
|
122
|
-
);
|
|
123
|
-
}
|
|
75
|
+
fields,
|
|
76
|
+
{ file: { filename, content, mimeType: mime } },
|
|
77
|
+
{ timeoutMs: options.timeoutMs }
|
|
124
78
|
);
|
|
125
79
|
}
|
|
126
80
|
function guessMime(path) {
|
|
@@ -128,24 +82,14 @@ function guessMime(path) {
|
|
|
128
82
|
if (ext in MIME_BY_EXT) return MIME_BY_EXT[ext];
|
|
129
83
|
return "application/octet-stream";
|
|
130
84
|
}
|
|
131
|
-
async function getDocument(client,
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
document_id: docId
|
|
137
|
-
}),
|
|
138
|
-
() => client.get(
|
|
139
|
-
`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents/${encodeURIComponent(docId)}`
|
|
140
|
-
)
|
|
141
|
-
);
|
|
85
|
+
async function getDocument(client, kbName, docId) {
|
|
86
|
+
return client.get(DOCUMENT_DETAIL, {
|
|
87
|
+
knowledge_base_name: kbName,
|
|
88
|
+
document_id: docId
|
|
89
|
+
});
|
|
142
90
|
}
|
|
143
|
-
async function listDocuments(client,
|
|
144
|
-
const resp = await
|
|
145
|
-
DOCUMENTS,
|
|
146
|
-
() => client.get(DOCUMENTS, { knowledge_base_id: kbId }),
|
|
147
|
-
() => client.get(`${KB_COLLECTION}/${encodeURIComponent(kbId)}/documents`)
|
|
148
|
-
);
|
|
91
|
+
async function listDocuments(client, kbName) {
|
|
92
|
+
const resp = await client.get(DOCUMENTS, { knowledge_base_name: kbName });
|
|
149
93
|
if (Array.isArray(resp)) return resp;
|
|
150
94
|
if (resp && typeof resp === "object") {
|
|
151
95
|
const o = resp;
|
|
@@ -153,13 +97,13 @@ async function listDocuments(client, kbId) {
|
|
|
153
97
|
}
|
|
154
98
|
return [];
|
|
155
99
|
}
|
|
156
|
-
async function pollIngest(client,
|
|
100
|
+
async function pollIngest(client, kbName, docId, options = {}) {
|
|
157
101
|
const timeoutMs = options.timeoutMs ?? DEFAULT_INGEST_TIMEOUT_MS;
|
|
158
102
|
const intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
159
103
|
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
160
104
|
const now = options.now ?? (() => performance.now());
|
|
161
105
|
const deadline = now() + timeoutMs;
|
|
162
|
-
let doc = await getDocument(client,
|
|
106
|
+
let doc = await getDocument(client, kbName, docId);
|
|
163
107
|
let timedOut = false;
|
|
164
108
|
while (ingestInFlight(doc)) {
|
|
165
109
|
if (now() >= deadline) {
|
|
@@ -168,7 +112,7 @@ async function pollIngest(client, kbId, docId, options = {}) {
|
|
|
168
112
|
}
|
|
169
113
|
await sleep(intervalMs);
|
|
170
114
|
try {
|
|
171
|
-
doc = await getDocument(client,
|
|
115
|
+
doc = await getDocument(client, kbName, docId);
|
|
172
116
|
} catch {
|
|
173
117
|
break;
|
|
174
118
|
}
|
|
@@ -241,8 +185,7 @@ export {
|
|
|
241
185
|
DEFAULT_INGEST_TIMEOUT_MS,
|
|
242
186
|
DEFAULT_FILE_INGEST_TIMEOUT_MS,
|
|
243
187
|
listKnowledgeBases,
|
|
244
|
-
|
|
245
|
-
findOrCreateKb,
|
|
188
|
+
createKb,
|
|
246
189
|
uploadText,
|
|
247
190
|
uploadFile,
|
|
248
191
|
getDocument,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
isConnectionError,
|
|
4
|
+
resetCircuit,
|
|
5
|
+
tripCircuit
|
|
6
|
+
} from "./chunk-Q2EEP4CE.js";
|
|
2
7
|
import {
|
|
3
8
|
CtxdbError
|
|
4
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-S45GOYUU.js";
|
|
5
10
|
|
|
6
11
|
// src/lib/recall-orchestrator.ts
|
|
7
12
|
import {
|
|
@@ -83,7 +88,7 @@ var EMPTY = {
|
|
|
83
88
|
memoryCount: 0,
|
|
84
89
|
knowledgeChunkCount: 0
|
|
85
90
|
};
|
|
86
|
-
async function recallTurn(prompt, cfg, client) {
|
|
91
|
+
async function recallTurn(prompt, cfg, client, agent = "default") {
|
|
87
92
|
if (!prompt.trim()) return { ...EMPTY, reason: "empty_prompt" };
|
|
88
93
|
if (!cfg.apiKey || !cfg.baseUrl) {
|
|
89
94
|
return { ...EMPTY, reason: "config_incomplete" };
|
|
@@ -101,8 +106,10 @@ async function recallTurn(prompt, cfg, client) {
|
|
|
101
106
|
let resp;
|
|
102
107
|
try {
|
|
103
108
|
resp = await client.postJson("/v3/memories/search/", body);
|
|
109
|
+
resetCircuit(agent);
|
|
104
110
|
} catch (err) {
|
|
105
111
|
const msg = err instanceof CtxdbError ? err.message : String(err);
|
|
112
|
+
if (isConnectionError(err)) tripCircuit(agent, cfg.baseUrl, msg);
|
|
106
113
|
return { ...EMPTY, reason: `http_error: ${msg}` };
|
|
107
114
|
}
|
|
108
115
|
if (!resp || typeof resp !== "object") {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
configDir
|
|
4
|
+
} from "./chunk-S45GOYUU.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/circuit.ts
|
|
7
|
+
import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
var CIRCUIT_DIR_NAME = "circuit";
|
|
10
|
+
var COOLDOWN_MS = 6e4;
|
|
11
|
+
function circuitDir() {
|
|
12
|
+
const dir = join(configDir(), CIRCUIT_DIR_NAME);
|
|
13
|
+
try {
|
|
14
|
+
mkdirSync(dir, { recursive: true });
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
return dir;
|
|
18
|
+
}
|
|
19
|
+
function circuitPath(agent) {
|
|
20
|
+
return join(circuitDir(), `${agent}.circuit`);
|
|
21
|
+
}
|
|
22
|
+
function isCircuitOpen(agent, baseUrl) {
|
|
23
|
+
const own = checkFile(circuitPath(agent), baseUrl);
|
|
24
|
+
if (own) return true;
|
|
25
|
+
try {
|
|
26
|
+
for (const f of readdirSync(circuitDir())) {
|
|
27
|
+
if (!f.endsWith(".circuit") || f === `${agent}.circuit`) continue;
|
|
28
|
+
if (checkFile(join(circuitDir(), f), baseUrl)) return true;
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function checkFile(path, baseUrl) {
|
|
35
|
+
try {
|
|
36
|
+
const st = statSync(path);
|
|
37
|
+
if (Date.now() - st.mtimeMs > COOLDOWN_MS) return false;
|
|
38
|
+
const line = readFileSync(path, "utf-8").split("\n")[0] || "";
|
|
39
|
+
return line.startsWith(baseUrl);
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function tripCircuit(agent, baseUrl, reason) {
|
|
45
|
+
try {
|
|
46
|
+
writeFileSync(circuitPath(agent), `${baseUrl}|${reason}|${(/* @__PURE__ */ new Date()).toISOString()}
|
|
47
|
+
`);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function resetCircuit(agent) {
|
|
52
|
+
try {
|
|
53
|
+
unlinkSync(circuitPath(agent));
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isConnectionError(err) {
|
|
58
|
+
if (!err || typeof err !== "object") return false;
|
|
59
|
+
if (err.name === "CtxdbHttpError") return false;
|
|
60
|
+
const msg = err.message ?? "";
|
|
61
|
+
return /timeout|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|network error/i.test(msg);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
isCircuitOpen,
|
|
66
|
+
tripCircuit,
|
|
67
|
+
resetCircuit,
|
|
68
|
+
isConnectionError
|
|
69
|
+
};
|
|
@@ -16,7 +16,14 @@ function isDebug() {
|
|
|
16
16
|
}
|
|
17
17
|
function debug(tag, msg, data) {
|
|
18
18
|
if (_enabled !== true) return;
|
|
19
|
-
const
|
|
19
|
+
const now = /* @__PURE__ */ new Date();
|
|
20
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
21
|
+
const ms = String(now.getMilliseconds()).padStart(3, "0");
|
|
22
|
+
const tz = -now.getTimezoneOffset();
|
|
23
|
+
const tzSign = tz >= 0 ? "+" : "-";
|
|
24
|
+
const tzH = pad(Math.floor(Math.abs(tz) / 60));
|
|
25
|
+
const tzM = pad(Math.abs(tz) % 60);
|
|
26
|
+
const ts = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}.${ms}${tzSign}${tzH}:${tzM}`;
|
|
20
27
|
let line = `${ts} [${tag}] ${msg}`;
|
|
21
28
|
if (data !== void 0) {
|
|
22
29
|
const s = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
@@ -251,9 +258,13 @@ function extractErrorDetail(text, fallback) {
|
|
|
251
258
|
import { homedir as homedir2 } from "os";
|
|
252
259
|
import { join as join3 } from "path";
|
|
253
260
|
var SUPPORTED_AGENTS = ["qoder", "codex", "claude"];
|
|
254
|
-
function
|
|
261
|
+
function isBuiltinAgent(v) {
|
|
255
262
|
return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
|
|
256
263
|
}
|
|
264
|
+
function isAgentSlug(v) {
|
|
265
|
+
return v === "default" || isBuiltinAgent(v);
|
|
266
|
+
}
|
|
267
|
+
var isAgent = isAgentSlug;
|
|
257
268
|
function agentHomeDir(agent) {
|
|
258
269
|
switch (agent) {
|
|
259
270
|
case "qoder":
|
|
@@ -265,17 +276,17 @@ function agentHomeDir(agent) {
|
|
|
265
276
|
}
|
|
266
277
|
}
|
|
267
278
|
function agentFromEnv(env = process.env) {
|
|
268
|
-
return
|
|
279
|
+
return isAgentSlug(env.CTXDB_AGENT) ? env.CTXDB_AGENT : "default";
|
|
269
280
|
}
|
|
270
281
|
function agentFromArgvWithFallback(argv = process.argv.slice(2), env = process.env) {
|
|
271
282
|
for (let i = 0; i < argv.length; i++) {
|
|
272
283
|
const tok = argv[i];
|
|
273
|
-
if (tok === "--agent" &&
|
|
284
|
+
if (tok === "--agent" && isAgentSlug(argv[i + 1])) {
|
|
274
285
|
return { agent: argv[i + 1], fellBack: false };
|
|
275
286
|
}
|
|
276
287
|
if (tok.startsWith("--agent=")) {
|
|
277
288
|
const raw = tok.slice("--agent=".length);
|
|
278
|
-
if (
|
|
289
|
+
if (isAgentSlug(raw)) return { agent: raw, fellBack: false };
|
|
279
290
|
}
|
|
280
291
|
}
|
|
281
292
|
return { agent: agentFromEnv(env), fellBack: true };
|
|
@@ -289,6 +300,9 @@ function defaultConfigPath() {
|
|
|
289
300
|
return join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
290
301
|
}
|
|
291
302
|
var DEFAULT_CONFIG_PATH = join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
303
|
+
function configDir() {
|
|
304
|
+
return join4(homedir3(), ".ctxdb");
|
|
305
|
+
}
|
|
292
306
|
var DEFAULT_BASE_URL = "https://context-database.aliyuncs.com";
|
|
293
307
|
var DEFAULT_USER_ID = "default";
|
|
294
308
|
var DEFAULT_TOP_K = 5;
|
|
@@ -297,8 +311,8 @@ var DEFAULT_KNOWLEDGE_TOP_K = 6;
|
|
|
297
311
|
function isComplete(cfg) {
|
|
298
312
|
return Boolean(cfg.apiKey && cfg.baseUrl);
|
|
299
313
|
}
|
|
300
|
-
function resolveConfigAgent(options = {}) {
|
|
301
|
-
if (
|
|
314
|
+
function resolveConfigAgent(options = {}, preloadedRaw) {
|
|
315
|
+
if (isAgentSlug(options.agent)) return options.agent;
|
|
302
316
|
return agentFromEnv(options.env);
|
|
303
317
|
}
|
|
304
318
|
function coerceInt(v, fallback) {
|
|
@@ -365,8 +379,8 @@ function applyEnv(cfg, env) {
|
|
|
365
379
|
function load(options = {}) {
|
|
366
380
|
const path = options.path ?? defaultConfigPath();
|
|
367
381
|
const env = options.env ?? process.env;
|
|
368
|
-
const agent = resolveConfigAgent({ agent: options.agent, env });
|
|
369
382
|
const raw = readRaw(path);
|
|
383
|
+
const agent = resolveConfigAgent({ ...options, path, env }, raw);
|
|
370
384
|
if (!isV2Schema(raw)) {
|
|
371
385
|
try {
|
|
372
386
|
process.stderr.write(
|
|
@@ -377,7 +391,18 @@ function load(options = {}) {
|
|
|
377
391
|
}
|
|
378
392
|
return applyEnv(configFromDisk({}), env);
|
|
379
393
|
}
|
|
380
|
-
|
|
394
|
+
const agentRaw = agentRawFromFile(raw, agent);
|
|
395
|
+
if (agent !== "default") {
|
|
396
|
+
const defaultRaw = agentRawFromFile(raw, "default");
|
|
397
|
+
if (Object.keys(defaultRaw).length > 0) {
|
|
398
|
+
for (const [k, v] of Object.entries(defaultRaw)) {
|
|
399
|
+
if (!(k in agentRaw)) {
|
|
400
|
+
agentRaw[k] = v;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return applyEnv(configFromDisk(agentRaw), env);
|
|
381
406
|
}
|
|
382
407
|
function configToDisk(cfg) {
|
|
383
408
|
return {
|
|
@@ -446,23 +471,32 @@ function configuredAgents(path) {
|
|
|
446
471
|
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object") return [];
|
|
447
472
|
return Object.keys(raw.agents).filter(isAgent);
|
|
448
473
|
}
|
|
474
|
+
function writeInstalledPkgVersion(version, path) {
|
|
475
|
+
const target = path ?? defaultConfigPath();
|
|
476
|
+
const raw = readRaw(target);
|
|
477
|
+
const updated = { ...raw, installed_pkg_version: version };
|
|
478
|
+
mkdirSync2(dirname3(target), { recursive: true });
|
|
479
|
+
writeFileSync(target, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
480
|
+
}
|
|
449
481
|
|
|
450
482
|
export {
|
|
451
483
|
setDebug,
|
|
452
484
|
debug,
|
|
453
485
|
CtxdbError,
|
|
454
|
-
NotFoundError,
|
|
455
486
|
HttpClient,
|
|
456
487
|
SUPPORTED_AGENTS,
|
|
457
|
-
|
|
488
|
+
isBuiltinAgent,
|
|
489
|
+
isAgentSlug,
|
|
458
490
|
agentHomeDir,
|
|
459
491
|
agentFromEnv,
|
|
460
492
|
agentFromArgvWithFallback,
|
|
493
|
+
configDir,
|
|
461
494
|
DEFAULT_BASE_URL,
|
|
462
495
|
DEFAULT_USER_ID,
|
|
463
496
|
isComplete,
|
|
464
497
|
load,
|
|
465
498
|
removeAgent,
|
|
466
499
|
save,
|
|
467
|
-
configuredAgents
|
|
500
|
+
configuredAgents,
|
|
501
|
+
writeInstalledPkgVersion
|
|
468
502
|
};
|