@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/chunk-2U7G6UXE.js
DELETED
|
@@ -1,1205 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
DISTRIBUTION_MANIFEST
|
|
4
|
-
} from "./chunk-5ITT5GSP.js";
|
|
5
|
-
|
|
6
|
-
// src/lib/credentials.ts
|
|
7
|
-
import { selectDataApiCredential } from "@aliyunrds/ctxdb-shared";
|
|
8
|
-
function apiKeyCredential(apiKey) {
|
|
9
|
-
return { type: "api-key", value: apiKey };
|
|
10
|
-
}
|
|
11
|
-
var resolveDataApiCredential = selectDataApiCredential;
|
|
12
|
-
function isDataApiReady(cfg, env = process.env) {
|
|
13
|
-
return Boolean(cfg.baseUrl && resolveDataApiCredential(cfg, env));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// src/lib/logger.ts
|
|
17
|
-
import { appendFileSync, mkdirSync } from "fs";
|
|
18
|
-
import { homedir } from "os";
|
|
19
|
-
import { dirname, join } from "path";
|
|
20
|
-
function logPath() {
|
|
21
|
-
return join(homedir(), ".ctxdb", "logs", "ctxdb.log");
|
|
22
|
-
}
|
|
23
|
-
var _enabled = null;
|
|
24
|
-
function setDebug(enabled) {
|
|
25
|
-
_enabled = enabled;
|
|
26
|
-
}
|
|
27
|
-
function isDebug() {
|
|
28
|
-
return _enabled === true;
|
|
29
|
-
}
|
|
30
|
-
function debug(tag, msg, data) {
|
|
31
|
-
if (_enabled !== true) return;
|
|
32
|
-
const now = /* @__PURE__ */ new Date();
|
|
33
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
34
|
-
const ms = String(now.getMilliseconds()).padStart(3, "0");
|
|
35
|
-
const tz = -now.getTimezoneOffset();
|
|
36
|
-
const tzSign = tz >= 0 ? "+" : "-";
|
|
37
|
-
const tzH = pad(Math.floor(Math.abs(tz) / 60));
|
|
38
|
-
const tzM = pad(Math.abs(tz) % 60);
|
|
39
|
-
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}`;
|
|
40
|
-
let line = `${ts} [${tag}] ${msg}`;
|
|
41
|
-
if (data !== void 0) {
|
|
42
|
-
const s = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
43
|
-
line += `
|
|
44
|
-
${s}`;
|
|
45
|
-
}
|
|
46
|
-
line += "\n";
|
|
47
|
-
const p = logPath();
|
|
48
|
-
try {
|
|
49
|
-
mkdirSync(dirname(p), { recursive: true });
|
|
50
|
-
appendFileSync(p, line, "utf-8");
|
|
51
|
-
} catch {
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// src/lib/package-version.ts
|
|
56
|
-
import { readFileSync } from "fs";
|
|
57
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
58
|
-
import { fileURLToPath } from "url";
|
|
59
|
-
var PACKAGE_NAMES = /* @__PURE__ */ new Set([
|
|
60
|
-
"@aliyunrds/ctxdb",
|
|
61
|
-
"@ali/ctxdb-internal"
|
|
62
|
-
]);
|
|
63
|
-
function findPackageIdentity() {
|
|
64
|
-
let dir = dirname2(fileURLToPath(import.meta.url));
|
|
65
|
-
for (let i = 0; i < 5; i++) {
|
|
66
|
-
try {
|
|
67
|
-
const pkg = JSON.parse(readFileSync(join2(dir, "package.json"), "utf-8"));
|
|
68
|
-
if (PACKAGE_NAMES.has(pkg.name) && typeof pkg.version === "string") {
|
|
69
|
-
return { name: pkg.name, version: pkg.version };
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
}
|
|
73
|
-
dir = dirname2(dir);
|
|
74
|
-
}
|
|
75
|
-
return { name: "@aliyunrds/ctxdb", version: "0.0.0" };
|
|
76
|
-
}
|
|
77
|
-
var PACKAGE_IDENTITY = findPackageIdentity();
|
|
78
|
-
var PACKAGE_NAME = PACKAGE_IDENTITY.name;
|
|
79
|
-
var PACKAGE_VERSION = PACKAGE_IDENTITY.version;
|
|
80
|
-
|
|
81
|
-
// src/lib/http-client.ts
|
|
82
|
-
import { createRequestAuthorizationProvider } from "@aliyunrds/ctxdb-shared";
|
|
83
|
-
import "@aliyunrds/ctxdb-shared";
|
|
84
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
85
|
-
var CtxdbError = class extends Error {
|
|
86
|
-
constructor(message) {
|
|
87
|
-
super(message);
|
|
88
|
-
this.name = "CtxdbError";
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
var AuthError = class extends CtxdbError {
|
|
92
|
-
status = 401;
|
|
93
|
-
errorCode;
|
|
94
|
-
errorMessage;
|
|
95
|
-
data;
|
|
96
|
-
responseBody;
|
|
97
|
-
constructor(message = "Unauthorized (HTTP 401) \u2014 check Data API credential", fields = {}) {
|
|
98
|
-
super(message);
|
|
99
|
-
this.name = "AuthError";
|
|
100
|
-
this.errorCode = fields.errorCode;
|
|
101
|
-
this.errorMessage = fields.errorMessage;
|
|
102
|
-
this.data = fields.data;
|
|
103
|
-
this.responseBody = fields.responseBody;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
var NotFoundError = class extends CtxdbError {
|
|
107
|
-
status = 404;
|
|
108
|
-
path;
|
|
109
|
-
errorCode;
|
|
110
|
-
errorMessage;
|
|
111
|
-
data;
|
|
112
|
-
responseBody;
|
|
113
|
-
constructor(path, fields = {}) {
|
|
114
|
-
super(`Not found: ${path}`);
|
|
115
|
-
this.name = "NotFoundError";
|
|
116
|
-
this.path = path;
|
|
117
|
-
this.errorCode = fields.errorCode;
|
|
118
|
-
this.errorMessage = fields.errorMessage;
|
|
119
|
-
this.data = fields.data;
|
|
120
|
-
this.responseBody = fields.responseBody;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
var APIError = class extends CtxdbError {
|
|
124
|
-
status = 400;
|
|
125
|
-
path;
|
|
126
|
-
detail;
|
|
127
|
-
errorCode;
|
|
128
|
-
errorMessage;
|
|
129
|
-
data;
|
|
130
|
-
responseBody;
|
|
131
|
-
constructor(path, detail, fields = {}) {
|
|
132
|
-
super(`API error at ${path}: ${detail}`);
|
|
133
|
-
this.name = "APIError";
|
|
134
|
-
this.path = path;
|
|
135
|
-
this.detail = detail;
|
|
136
|
-
this.errorCode = fields.errorCode;
|
|
137
|
-
this.errorMessage = fields.errorMessage;
|
|
138
|
-
this.data = fields.data;
|
|
139
|
-
this.responseBody = fields.responseBody;
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
var CtxdbHttpError = class extends CtxdbError {
|
|
143
|
-
status;
|
|
144
|
-
detail;
|
|
145
|
-
errorCode;
|
|
146
|
-
errorMessage;
|
|
147
|
-
data;
|
|
148
|
-
responseBody;
|
|
149
|
-
constructor(status, detail, fields = {}) {
|
|
150
|
-
super(`HTTP ${status}: ${detail}`);
|
|
151
|
-
this.name = "CtxdbHttpError";
|
|
152
|
-
this.status = status;
|
|
153
|
-
this.detail = detail;
|
|
154
|
-
this.errorCode = fields.errorCode;
|
|
155
|
-
this.errorMessage = fields.errorMessage;
|
|
156
|
-
this.data = fields.data;
|
|
157
|
-
this.responseBody = fields.responseBody;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
var HttpClient = class {
|
|
161
|
-
baseUrl;
|
|
162
|
-
credential;
|
|
163
|
-
apiKey;
|
|
164
|
-
timeoutMs;
|
|
165
|
-
userAgent;
|
|
166
|
-
extraHeaders;
|
|
167
|
-
fetchImpl;
|
|
168
|
-
authorizationProvider;
|
|
169
|
-
constructor(opts) {
|
|
170
|
-
const credential = opts.credential ?? (opts.apiKey ? apiKeyCredential(opts.apiKey) : null);
|
|
171
|
-
if (!credential && !opts.authorizationProvider) {
|
|
172
|
-
throw new CtxdbError("DATA API credential is required");
|
|
173
|
-
}
|
|
174
|
-
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
175
|
-
this.credential = credential;
|
|
176
|
-
this.apiKey = opts.apiKey ?? "";
|
|
177
|
-
this.authorizationProvider = opts.authorizationProvider ?? createRequestAuthorizationProvider(credential, this.baseUrl);
|
|
178
|
-
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
179
|
-
this.userAgent = opts.userAgent ?? `ctxdb-cli/${PACKAGE_VERSION}`;
|
|
180
|
-
this.extraHeaders = { ...opts.extraHeaders ?? {} };
|
|
181
|
-
const f = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
182
|
-
this.fetchImpl = f;
|
|
183
|
-
}
|
|
184
|
-
async buildHeaders(authorization, contentType, requestHeaders = {}) {
|
|
185
|
-
const h = {
|
|
186
|
-
Authorization: `${authorization.scheme} ${authorization.value}`,
|
|
187
|
-
"User-Agent": this.userAgent,
|
|
188
|
-
Connection: "close",
|
|
189
|
-
...this.extraHeaders,
|
|
190
|
-
...requestHeaders
|
|
191
|
-
};
|
|
192
|
-
if (authorization.scheme === "Bearer") {
|
|
193
|
-
for (const key of Object.keys(h)) if (key.toLowerCase() === "authorization") delete h[key];
|
|
194
|
-
h.Authorization = `Bearer ${authorization.value}`;
|
|
195
|
-
}
|
|
196
|
-
if (contentType) h["Content-Type"] = contentType;
|
|
197
|
-
return h;
|
|
198
|
-
}
|
|
199
|
-
async doRequest(method, path, init = {}) {
|
|
200
|
-
const effectiveTimeout = init.timeoutMs ?? this.timeoutMs, started = Date.now();
|
|
201
|
-
let authorization;
|
|
202
|
-
if (this.authorizationProvider) {
|
|
203
|
-
authorization = await this.authorizationProvider.resolve({ timeoutMs: effectiveTimeout });
|
|
204
|
-
} else {
|
|
205
|
-
const credential = this.credential;
|
|
206
|
-
if (!credential || credential.type === "oauth-session") throw new CtxdbError("DATA API credential is required");
|
|
207
|
-
authorization = {
|
|
208
|
-
scheme: credential.type === "access-token" ? "Bearer" : "Token",
|
|
209
|
-
value: credential.value,
|
|
210
|
-
baseUrl: this.baseUrl,
|
|
211
|
-
expiresAt: null
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
const safeText = (value) => {
|
|
215
|
-
const text = String(value);
|
|
216
|
-
return text.split(authorization.value).join("[REDACTED]");
|
|
217
|
-
};
|
|
218
|
-
let url = `${authorization.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
219
|
-
if (init.params) {
|
|
220
|
-
const qs = new URLSearchParams();
|
|
221
|
-
for (const [k, v] of Object.entries(init.params)) {
|
|
222
|
-
if (v !== void 0 && v !== null) qs.append(k, String(v));
|
|
223
|
-
}
|
|
224
|
-
const s = qs.toString();
|
|
225
|
-
if (s) url = `${url}?${s}`;
|
|
226
|
-
}
|
|
227
|
-
const dbg = isDebug();
|
|
228
|
-
let t0 = 0;
|
|
229
|
-
if (dbg) {
|
|
230
|
-
t0 = Date.now();
|
|
231
|
-
debug("http", `\u2192 ${method} ${url} (timeout=${effectiveTimeout}ms)`);
|
|
232
|
-
}
|
|
233
|
-
const controller = new AbortController();
|
|
234
|
-
const timer = setTimeout(() => controller.abort(), Math.max(1, effectiveTimeout - (Date.now() - started)));
|
|
235
|
-
try {
|
|
236
|
-
let resp;
|
|
237
|
-
try {
|
|
238
|
-
resp = await this.fetchImpl(url, {
|
|
239
|
-
method,
|
|
240
|
-
headers: await this.buildHeaders(
|
|
241
|
-
authorization,
|
|
242
|
-
init.contentType,
|
|
243
|
-
init.headers
|
|
244
|
-
),
|
|
245
|
-
body: init.body,
|
|
246
|
-
signal: controller.signal,
|
|
247
|
-
...authorization.scheme === "Bearer" ? { redirect: "error" } : {}
|
|
248
|
-
});
|
|
249
|
-
} catch (err) {
|
|
250
|
-
if (err?.name === "AbortError") {
|
|
251
|
-
if (dbg) debug("http", `\u2717 ${method} ${path} timeout after ${Date.now() - t0}ms`);
|
|
252
|
-
throw new CtxdbError(`request timeout after ${effectiveTimeout}ms: ${url}`);
|
|
253
|
-
}
|
|
254
|
-
if (dbg) debug("http", `\u2717 ${method} ${path} network error after ${Date.now() - t0}ms: ${safeText(err?.message ?? err)}`);
|
|
255
|
-
throw new CtxdbError(`network error contacting ${url}: ${safeText(err?.message ?? err)}`);
|
|
256
|
-
}
|
|
257
|
-
if (resp.status === 204) {
|
|
258
|
-
if (dbg) debug("http", `\u2190 ${method} ${path} 204 (${Date.now() - t0}ms)`);
|
|
259
|
-
return {};
|
|
260
|
-
}
|
|
261
|
-
let text;
|
|
262
|
-
try {
|
|
263
|
-
const bodyText = await resp.text();
|
|
264
|
-
text = authorization.scheme === "Bearer" || !resp.ok ? safeText(bodyText) : bodyText;
|
|
265
|
-
} catch (err) {
|
|
266
|
-
if (err?.name === "AbortError") {
|
|
267
|
-
if (dbg) debug("http", `\u2717 ${method} ${path} body-read timeout after ${Date.now() - t0}ms`);
|
|
268
|
-
throw new CtxdbError(`request timeout after ${effectiveTimeout}ms (body read): ${url}`);
|
|
269
|
-
}
|
|
270
|
-
if (dbg) debug("http", `\u2717 ${method} ${path} body-read error after ${Date.now() - t0}ms: ${safeText(err?.message ?? err)}`);
|
|
271
|
-
throw new CtxdbError(`network error reading body from ${url}: ${safeText(err?.message ?? err)}`);
|
|
272
|
-
}
|
|
273
|
-
if (!resp.ok) {
|
|
274
|
-
const parsedError = parseErrorResponse(text, `HTTP ${resp.status}`);
|
|
275
|
-
if (dbg) debug("http", `\u2190 ${method} ${path} ${resp.status} (${Date.now() - t0}ms) ${parsedError.detail}`);
|
|
276
|
-
if (resp.status === 401) {
|
|
277
|
-
throw new AuthError(void 0, parsedError);
|
|
278
|
-
}
|
|
279
|
-
if (resp.status === 404) {
|
|
280
|
-
throw new NotFoundError(path, parsedError);
|
|
281
|
-
}
|
|
282
|
-
if (resp.status === 400) {
|
|
283
|
-
throw new APIError(path, parsedError.detail, parsedError);
|
|
284
|
-
}
|
|
285
|
-
throw new CtxdbHttpError(resp.status, parsedError.detail, parsedError);
|
|
286
|
-
}
|
|
287
|
-
if (dbg) debug("http", `\u2190 ${method} ${path} ${resp.status} (${Date.now() - t0}ms) body=${text.length}B`);
|
|
288
|
-
if (!text) return {};
|
|
289
|
-
return maybeJson(text);
|
|
290
|
-
} finally {
|
|
291
|
-
clearTimeout(timer);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// ---------- Convenience verbs ----------
|
|
295
|
-
get(path, params) {
|
|
296
|
-
return this.doRequest("GET", path, { params });
|
|
297
|
-
}
|
|
298
|
-
postJson(path, body, params, options = {}) {
|
|
299
|
-
return this.doRequest("POST", path, {
|
|
300
|
-
body: JSON.stringify(body ?? {}),
|
|
301
|
-
contentType: "application/json",
|
|
302
|
-
params,
|
|
303
|
-
timeoutMs: options.timeoutMs,
|
|
304
|
-
headers: options.headers
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
putJson(path, body, params, options = {}) {
|
|
308
|
-
return this.doRequest("PUT", path, {
|
|
309
|
-
body: JSON.stringify(body),
|
|
310
|
-
contentType: "application/json",
|
|
311
|
-
params,
|
|
312
|
-
timeoutMs: options.timeoutMs,
|
|
313
|
-
headers: options.headers
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
patchJson(path, body, params, options = {}) {
|
|
317
|
-
return this.doRequest("PATCH", path, {
|
|
318
|
-
body: JSON.stringify(body),
|
|
319
|
-
contentType: "application/json",
|
|
320
|
-
params,
|
|
321
|
-
timeoutMs: options.timeoutMs,
|
|
322
|
-
headers: options.headers
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
delete(path, params) {
|
|
326
|
-
return this.doRequest("DELETE", path, { params });
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* DELETE with a JSON body. ContextDB batch deletes use the
|
|
330
|
-
* `DELETE /v1/{resource}/batch` shape with an `{"ids": [...]}` body
|
|
331
|
-
* (server contract) — the plain `delete()` above only supports query
|
|
332
|
-
* params.
|
|
333
|
-
*/
|
|
334
|
-
deleteJson(path, body, params, options = {}) {
|
|
335
|
-
return this.doRequest("DELETE", path, {
|
|
336
|
-
body: JSON.stringify(body),
|
|
337
|
-
contentType: "application/json",
|
|
338
|
-
params,
|
|
339
|
-
timeoutMs: options.timeoutMs,
|
|
340
|
-
headers: options.headers
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* POST multipart/form-data using runtime-native FormData.
|
|
345
|
-
*
|
|
346
|
-
* `fields` are appended as text values; `files` are appended as Blob
|
|
347
|
-
* with a filename. We intentionally do NOT pass a `Content-Type`
|
|
348
|
-
* header — when fetch sees a FormData body it sets the multipart
|
|
349
|
-
* boundary itself.
|
|
350
|
-
*/
|
|
351
|
-
postMultipart(path, fields = {}, files = {}, options = {}) {
|
|
352
|
-
const fd = buildMultipartForm(fields, files);
|
|
353
|
-
return this.doRequest("POST", path, {
|
|
354
|
-
body: fd,
|
|
355
|
-
params: options.params,
|
|
356
|
-
timeoutMs: options.timeoutMs,
|
|
357
|
-
headers: options.headers
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
/** PUT multipart/form-data with the same runtime-owned boundary as POST. */
|
|
361
|
-
putMultipart(path, fields = {}, files = {}, options = {}) {
|
|
362
|
-
const fd = buildMultipartForm(fields, files);
|
|
363
|
-
return this.doRequest("PUT", path, {
|
|
364
|
-
body: fd,
|
|
365
|
-
params: options.params,
|
|
366
|
-
timeoutMs: options.timeoutMs,
|
|
367
|
-
headers: options.headers
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
function buildMultipartForm(fields, files) {
|
|
372
|
-
const fd = new FormData();
|
|
373
|
-
for (const [name, value] of Object.entries(fields)) {
|
|
374
|
-
fd.append(name, value);
|
|
375
|
-
}
|
|
376
|
-
for (const [name, part] of Object.entries(files)) {
|
|
377
|
-
const blob = part.content instanceof Blob ? part.content : new Blob([part.content], { type: part.mimeType });
|
|
378
|
-
fd.append(name, blob, part.filename);
|
|
379
|
-
}
|
|
380
|
-
return fd;
|
|
381
|
-
}
|
|
382
|
-
function maybeJson(text) {
|
|
383
|
-
try {
|
|
384
|
-
return JSON.parse(text);
|
|
385
|
-
} catch {
|
|
386
|
-
return text;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
function parseErrorResponse(text, fallback) {
|
|
390
|
-
if (!text) return { detail: fallback };
|
|
391
|
-
let parsed;
|
|
392
|
-
try {
|
|
393
|
-
parsed = JSON.parse(text);
|
|
394
|
-
} catch {
|
|
395
|
-
return { detail: text || fallback, responseBody: text || void 0 };
|
|
396
|
-
}
|
|
397
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
398
|
-
const obj = parsed;
|
|
399
|
-
let detail = "";
|
|
400
|
-
for (const k of ["errorMessage", "detail", "message", "error"]) {
|
|
401
|
-
const v = obj[k];
|
|
402
|
-
if (typeof v === "string" && v) {
|
|
403
|
-
detail = v;
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
const rawCode = obj.errorCode;
|
|
408
|
-
const numericCode = typeof rawCode === "number" ? rawCode : typeof rawCode === "string" && rawCode.trim() !== "" ? Number(rawCode) : void 0;
|
|
409
|
-
const errorCode = typeof numericCode === "number" && Number.isFinite(numericCode) ? numericCode : void 0;
|
|
410
|
-
const errorMessage = typeof obj.errorMessage === "string" && obj.errorMessage ? obj.errorMessage : void 0;
|
|
411
|
-
return {
|
|
412
|
-
detail: detail || JSON.stringify(parsed),
|
|
413
|
-
errorCode,
|
|
414
|
-
errorMessage,
|
|
415
|
-
data: Object.hasOwn(obj, "data") ? obj.data : void 0,
|
|
416
|
-
responseBody: parsed
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
return { detail: String(parsed), responseBody: parsed };
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// src/lib/agents.ts
|
|
423
|
-
import { homedir as homedir2 } from "os";
|
|
424
|
-
import { delimiter, join as join3, sep } from "path";
|
|
425
|
-
import { accessSync, constants, existsSync, statSync } from "fs";
|
|
426
|
-
var SUPPORTED_AGENTS = ["qoder", "qoderwork", "qwenwork", "codex", "claude", "opencode", "hermes", "workbuddy"];
|
|
427
|
-
var SUPPORTED_AGENT_CHOICES = SUPPORTED_AGENTS.join("|");
|
|
428
|
-
var AGENT_CHOICES = [...SUPPORTED_AGENTS, "default"].join("|");
|
|
429
|
-
function isBuiltinAgent(v) {
|
|
430
|
-
return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
|
|
431
|
-
}
|
|
432
|
-
function isAgentSlug(v) {
|
|
433
|
-
return v === "default" || isBuiltinAgent(v);
|
|
434
|
-
}
|
|
435
|
-
var isAgent = isAgentSlug;
|
|
436
|
-
function agentHomeDir(agent, home = homedir2()) {
|
|
437
|
-
switch (agent) {
|
|
438
|
-
case "qoder":
|
|
439
|
-
return join3(home, ".qoder");
|
|
440
|
-
case "qoderwork":
|
|
441
|
-
return join3(home, ".qoderwork");
|
|
442
|
-
case "qwenwork":
|
|
443
|
-
return join3(home, ".qwenwork");
|
|
444
|
-
case "codex":
|
|
445
|
-
return join3(home, ".codex");
|
|
446
|
-
case "claude":
|
|
447
|
-
return join3(home, ".claude");
|
|
448
|
-
case "opencode":
|
|
449
|
-
return join3(home, ".config", "opencode");
|
|
450
|
-
case "hermes":
|
|
451
|
-
return join3(home, ".hermes");
|
|
452
|
-
case "workbuddy":
|
|
453
|
-
return join3(home, ".workbuddy-ai");
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
var AGENT_VARIANT_HOMES = {
|
|
457
|
-
qoder: [".qoder-cn"],
|
|
458
|
-
qoderwork: [".qoderworkcn"],
|
|
459
|
-
qwenwork: [".qwenworkcn"],
|
|
460
|
-
workbuddy: [".workbuddy"]
|
|
461
|
-
};
|
|
462
|
-
function agentHomeDirs(agent, home = homedir2()) {
|
|
463
|
-
const primary = agentHomeDir(agent, home);
|
|
464
|
-
const variants = (AGENT_VARIANT_HOMES[agent] ?? []).map(
|
|
465
|
-
(name) => join3(home, name)
|
|
466
|
-
);
|
|
467
|
-
return [primary, ...variants];
|
|
468
|
-
}
|
|
469
|
-
function agentPlatformSupport(agent, platform = process.platform) {
|
|
470
|
-
if (agent === "hermes" && platform === "win32") {
|
|
471
|
-
return {
|
|
472
|
-
supported: false,
|
|
473
|
-
detail: "Hermes integration currently supports macOS/Linux only; Windows Hermes is not yet supported."
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
return { supported: true, detail: `${agent} is supported on ${platform}.` };
|
|
477
|
-
}
|
|
478
|
-
function inspectAgentHomes(agent, options = {}) {
|
|
479
|
-
const exists = options.exists ?? existsSyncAdapter;
|
|
480
|
-
const candidates = agentHomeDirs(agent, options.home);
|
|
481
|
-
const existing = candidates.filter(exists);
|
|
482
|
-
return { exists: existing.length > 0, existing, candidates };
|
|
483
|
-
}
|
|
484
|
-
function existsSyncAdapter(path) {
|
|
485
|
-
try {
|
|
486
|
-
return existsSync(path);
|
|
487
|
-
} catch {
|
|
488
|
-
return false;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
var EXECUTABLE_AGENT_NAMES = {
|
|
492
|
-
opencode: "opencode",
|
|
493
|
-
hermes: "hermes"
|
|
494
|
-
};
|
|
495
|
-
function isDirectoryAdapter(path) {
|
|
496
|
-
try {
|
|
497
|
-
return statSync(path).isDirectory();
|
|
498
|
-
} catch {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
function isExecutableAdapter(path, platform) {
|
|
503
|
-
try {
|
|
504
|
-
if (!statSync(path).isFile()) return false;
|
|
505
|
-
if (platform !== "win32") accessSync(path, constants.X_OK);
|
|
506
|
-
return true;
|
|
507
|
-
} catch {
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
function envValue(env, name, platform) {
|
|
512
|
-
if (env[name] !== void 0) return env[name];
|
|
513
|
-
if (platform !== "win32") return void 0;
|
|
514
|
-
const entry = Object.entries(env).find(([key]) => key.toLowerCase() === name.toLowerCase());
|
|
515
|
-
return entry?.[1];
|
|
516
|
-
}
|
|
517
|
-
function resolveAgentExecutable(command, options = {}) {
|
|
518
|
-
const platform = options.platform ?? process.platform;
|
|
519
|
-
const env = options.env ?? process.env;
|
|
520
|
-
const isExecutable = options.isExecutable ?? isExecutableAdapter;
|
|
521
|
-
const pathValue = envValue(env, "PATH", platform) ?? "";
|
|
522
|
-
const pathSeparator = platform === "win32" ? ";" : delimiter;
|
|
523
|
-
const pathEntries = pathValue.split(pathSeparator).map((entry) => entry.trim().replace(/^"(.*)"$/, "$1")).filter(Boolean);
|
|
524
|
-
const suffixes = platform === "win32" ? (envValue(env, "PATHEXT", platform) ?? ".COM;.EXE;.BAT;.CMD").split(";").map((suffix) => suffix.trim()).filter(Boolean) : [""];
|
|
525
|
-
for (const directory of pathEntries) {
|
|
526
|
-
for (const suffix of suffixes) {
|
|
527
|
-
const candidate = join3(directory, `${command}${suffix}`);
|
|
528
|
-
if (isExecutable(candidate, platform)) return candidate;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
return void 0;
|
|
532
|
-
}
|
|
533
|
-
function displayHomePath(home, path) {
|
|
534
|
-
if (path === home) return "~";
|
|
535
|
-
return path.startsWith(`${home}${sep}`) ? `~${path.slice(home.length)}` : path;
|
|
536
|
-
}
|
|
537
|
-
function detectInstalledAgents(options = {}) {
|
|
538
|
-
const home = options.home ?? homedir2();
|
|
539
|
-
const platform = options.platform ?? process.platform;
|
|
540
|
-
const env = options.env ?? process.env;
|
|
541
|
-
const isDirectory = options.isDirectory ?? isDirectoryAdapter;
|
|
542
|
-
const observations = [];
|
|
543
|
-
for (const agent of SUPPORTED_AGENTS) {
|
|
544
|
-
const evidence = [];
|
|
545
|
-
for (const candidate of agentHomeDirs(agent, home)) {
|
|
546
|
-
if (isDirectory(candidate)) {
|
|
547
|
-
evidence.push({ kind: "home", value: displayHomePath(home, candidate) });
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
const executableName = EXECUTABLE_AGENT_NAMES[agent];
|
|
551
|
-
if (executableName) {
|
|
552
|
-
const executable = resolveAgentExecutable(executableName, {
|
|
553
|
-
platform,
|
|
554
|
-
env,
|
|
555
|
-
isExecutable: options.isExecutable
|
|
556
|
-
});
|
|
557
|
-
if (executable) {
|
|
558
|
-
evidence.push({ kind: "executable", value: executableName });
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (evidence.length === 0) {
|
|
562
|
-
observations.push({
|
|
563
|
-
agent,
|
|
564
|
-
selected: false,
|
|
565
|
-
reason: "not-detected",
|
|
566
|
-
evidence
|
|
567
|
-
});
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
const support = agentPlatformSupport(agent, platform);
|
|
571
|
-
if (!support.supported) {
|
|
572
|
-
observations.push({
|
|
573
|
-
agent,
|
|
574
|
-
selected: false,
|
|
575
|
-
reason: "unsupported-platform",
|
|
576
|
-
evidence,
|
|
577
|
-
detail: support.detail
|
|
578
|
-
});
|
|
579
|
-
continue;
|
|
580
|
-
}
|
|
581
|
-
observations.push({ agent, selected: true, reason: "detected", evidence });
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
selected: observations.filter((item) => item.selected).map((item) => item.agent),
|
|
585
|
-
observations
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function agentFromEnv(env = process.env) {
|
|
589
|
-
return isAgentSlug(env.CTXDB_AGENT) ? env.CTXDB_AGENT : "default";
|
|
590
|
-
}
|
|
591
|
-
function agentFromArgvWithFallback(argv = process.argv.slice(2), env = process.env) {
|
|
592
|
-
for (let i = 0; i < argv.length; i++) {
|
|
593
|
-
const tok = argv[i];
|
|
594
|
-
if (tok === "--agent" && isAgentSlug(argv[i + 1])) {
|
|
595
|
-
return { agent: argv[i + 1], fellBack: false };
|
|
596
|
-
}
|
|
597
|
-
if (tok.startsWith("--agent=")) {
|
|
598
|
-
const raw = tok.slice("--agent=".length);
|
|
599
|
-
if (isAgentSlug(raw)) return { agent: raw, fellBack: false };
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return { agent: agentFromEnv(env), fellBack: true };
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// src/lib/config.ts
|
|
606
|
-
import { publicAuthenticationFromConfig } from "@aliyunrds/ctxdb-shared";
|
|
607
|
-
import { readFileSync as readFileSync2, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
608
|
-
import { homedir as homedir3 } from "os";
|
|
609
|
-
import { dirname as dirname4, join as join4 } from "path";
|
|
610
|
-
|
|
611
|
-
// src/lib/secure-file.ts
|
|
612
|
-
import {
|
|
613
|
-
chmodSync,
|
|
614
|
-
closeSync,
|
|
615
|
-
copyFileSync,
|
|
616
|
-
existsSync as existsSync2,
|
|
617
|
-
fsyncSync,
|
|
618
|
-
mkdirSync as mkdirSync2,
|
|
619
|
-
openSync,
|
|
620
|
-
renameSync,
|
|
621
|
-
unlinkSync,
|
|
622
|
-
writeFileSync
|
|
623
|
-
} from "fs";
|
|
624
|
-
import { randomBytes } from "crypto";
|
|
625
|
-
import { dirname as dirname3 } from "path";
|
|
626
|
-
var nodeSecureFileSystem = {
|
|
627
|
-
exists: existsSync2,
|
|
628
|
-
mkdir: (path, options) => {
|
|
629
|
-
mkdirSync2(path, options);
|
|
630
|
-
},
|
|
631
|
-
chmod: chmodSync,
|
|
632
|
-
open: openSync,
|
|
633
|
-
write: (fd, data) => {
|
|
634
|
-
writeFileSync(fd, data);
|
|
635
|
-
},
|
|
636
|
-
fsync: fsyncSync,
|
|
637
|
-
close: closeSync,
|
|
638
|
-
copy: copyFileSync,
|
|
639
|
-
rename: renameSync,
|
|
640
|
-
unlink: unlinkSync
|
|
641
|
-
};
|
|
642
|
-
function configBackupPath(target) {
|
|
643
|
-
return `${target}.bak`;
|
|
644
|
-
}
|
|
645
|
-
function secureAtomicWrite(target, content, options = {}) {
|
|
646
|
-
const fs = options.fs ?? nodeSecureFileSystem;
|
|
647
|
-
const platform = options.platform ?? process.platform;
|
|
648
|
-
const posix = platform !== "win32";
|
|
649
|
-
const dir = dirname3(target);
|
|
650
|
-
const backup = configBackupPath(target);
|
|
651
|
-
const suffix = options.tempSuffix ?? `${process.pid}.${randomBytes(6).toString("hex")}`;
|
|
652
|
-
const temp = `${target}.${suffix}.tmp`;
|
|
653
|
-
const hadTarget = fs.exists(target);
|
|
654
|
-
fs.mkdir(dir, { recursive: true, mode: 448 });
|
|
655
|
-
if (posix) {
|
|
656
|
-
fs.chmod(dir, 448);
|
|
657
|
-
if (hadTarget) fs.chmod(target, 384);
|
|
658
|
-
}
|
|
659
|
-
let fd = null;
|
|
660
|
-
let tempExists = false;
|
|
661
|
-
try {
|
|
662
|
-
fd = fs.open(temp, "wx", 384);
|
|
663
|
-
tempExists = true;
|
|
664
|
-
fs.write(fd, Buffer.from(content, "utf-8"));
|
|
665
|
-
fs.fsync(fd);
|
|
666
|
-
fs.close(fd);
|
|
667
|
-
fd = null;
|
|
668
|
-
if (posix) fs.chmod(temp, 384);
|
|
669
|
-
if (hadTarget && options.backup !== false) {
|
|
670
|
-
if (platform === "win32") {
|
|
671
|
-
if (fs.exists(backup)) fs.unlink(backup);
|
|
672
|
-
fs.rename(target, backup);
|
|
673
|
-
try {
|
|
674
|
-
fs.rename(temp, target);
|
|
675
|
-
tempExists = false;
|
|
676
|
-
} catch (error) {
|
|
677
|
-
fs.rename(backup, target);
|
|
678
|
-
throw error;
|
|
679
|
-
}
|
|
680
|
-
} else {
|
|
681
|
-
fs.copy(target, backup);
|
|
682
|
-
fs.chmod(backup, 384);
|
|
683
|
-
fs.rename(temp, target);
|
|
684
|
-
tempExists = false;
|
|
685
|
-
}
|
|
686
|
-
} else {
|
|
687
|
-
fs.rename(temp, target);
|
|
688
|
-
tempExists = false;
|
|
689
|
-
}
|
|
690
|
-
} finally {
|
|
691
|
-
if (fd !== null) {
|
|
692
|
-
try {
|
|
693
|
-
fs.close(fd);
|
|
694
|
-
} catch {
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
if (tempExists && fs.exists(temp)) {
|
|
698
|
-
try {
|
|
699
|
-
fs.unlink(temp);
|
|
700
|
-
} catch {
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// src/lib/secrets.ts
|
|
707
|
-
import { createHmac, randomBytes as randomBytes2 } from "crypto";
|
|
708
|
-
var PROCESS_FINGERPRINT_KEY = randomBytes2(32);
|
|
709
|
-
function maskApiKey(apiKey) {
|
|
710
|
-
if (!apiKey) return "";
|
|
711
|
-
const payload = apiKey.startsWith("ctxdb-") ? apiKey.slice("ctxdb-".length) : null;
|
|
712
|
-
if (payload && payload.length > 12) return `ctxdb-xxxxxx${payload.slice(-6)}`;
|
|
713
|
-
if (payload !== null) return "x".repeat(apiKey.length);
|
|
714
|
-
if (apiKey.length <= 12) return "x".repeat(apiKey.length);
|
|
715
|
-
return `xxxxxx${apiKey.slice(-6)}`;
|
|
716
|
-
}
|
|
717
|
-
function credentialFingerprint(apiKey, processKey = PROCESS_FINGERPRINT_KEY) {
|
|
718
|
-
if (!apiKey) return "";
|
|
719
|
-
return createHmac("sha256", processKey).update(apiKey).digest("hex").slice(0, 12);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// src/credentials/local-credential-provider.ts
|
|
723
|
-
import { LocalCredentialProvider, defaultCredentialsPath, readActiveCredentialSync, withAuthorizationLock, internalCandidate } from "@aliyunrds/ctxdb-shared";
|
|
724
|
-
|
|
725
|
-
// src/lib/config.ts
|
|
726
|
-
import {
|
|
727
|
-
resolveDebugPolicy,
|
|
728
|
-
oauthProfile,
|
|
729
|
-
validateLoginChoice,
|
|
730
|
-
authSnapshot,
|
|
731
|
-
assertAuthSnapshot,
|
|
732
|
-
withAuthConfigLock
|
|
733
|
-
} from "@aliyunrds/ctxdb-shared";
|
|
734
|
-
function defaultConfigPath(env = process.env) {
|
|
735
|
-
return env.CTXDB_CONFIG_PATH || join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
736
|
-
}
|
|
737
|
-
var DEFAULT_CONFIG_PATH = join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
738
|
-
function configDir() {
|
|
739
|
-
return join4(homedir3(), ".ctxdb");
|
|
740
|
-
}
|
|
741
|
-
var DEFAULT_BASE_URL = "https://api.cn-hangzhou.agentcontext.aliyuncs.com";
|
|
742
|
-
var DEFAULT_USER_ID = "default";
|
|
743
|
-
var DEFAULT_TOP_K = 5;
|
|
744
|
-
var DEFAULT_THRESHOLD = 0.4;
|
|
745
|
-
var DEFAULT_KNOWLEDGE_TOP_K = 6;
|
|
746
|
-
var DEFAULT_KB_CATALOG_INJECTION = "session_start";
|
|
747
|
-
function resolveConfigAgent(options = {}, preloadedRaw) {
|
|
748
|
-
if (isAgentSlug(options.agent)) return options.agent;
|
|
749
|
-
return agentFromEnv(options.env);
|
|
750
|
-
}
|
|
751
|
-
function coerceInt(v, fallback) {
|
|
752
|
-
if (v === null || v === void 0 || v === "") return fallback;
|
|
753
|
-
const n = typeof v === "number" ? v : Number(v);
|
|
754
|
-
return Number.isFinite(n) ? Math.trunc(n) : fallback;
|
|
755
|
-
}
|
|
756
|
-
function coerceFloat(v, fallback) {
|
|
757
|
-
if (v === null || v === void 0 || v === "") return fallback;
|
|
758
|
-
const n = typeof v === "number" ? v : Number(v);
|
|
759
|
-
return Number.isFinite(n) ? n : fallback;
|
|
760
|
-
}
|
|
761
|
-
function coerceBool(v, fallback) {
|
|
762
|
-
if (typeof v === "boolean") return v;
|
|
763
|
-
if (v === void 0 || v === null) return fallback;
|
|
764
|
-
return Boolean(v);
|
|
765
|
-
}
|
|
766
|
-
function coerceKbCatalogInjection(v) {
|
|
767
|
-
if (v === "session_start" || v === "user_prompt_submit" || v === "off") {
|
|
768
|
-
return v;
|
|
769
|
-
}
|
|
770
|
-
return DEFAULT_KB_CATALOG_INJECTION;
|
|
771
|
-
}
|
|
772
|
-
function readRaw(path) {
|
|
773
|
-
if (!existsSync3(path)) return {};
|
|
774
|
-
try {
|
|
775
|
-
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
776
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
777
|
-
return parsed;
|
|
778
|
-
}
|
|
779
|
-
} catch {
|
|
780
|
-
}
|
|
781
|
-
return {};
|
|
782
|
-
}
|
|
783
|
-
function asRawObject(value) {
|
|
784
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
785
|
-
}
|
|
786
|
-
function explicitAgentRaw(raw, agent) {
|
|
787
|
-
const agents = asRawObject(raw.agents);
|
|
788
|
-
return agents ? asRawObject(agents[agent]) : null;
|
|
789
|
-
}
|
|
790
|
-
function explicitString(raw, key) {
|
|
791
|
-
const value = raw?.[key];
|
|
792
|
-
return typeof value === "string" && value ? value : null;
|
|
793
|
-
}
|
|
794
|
-
function inspectConfig(options = {}) {
|
|
795
|
-
const path = options.path ?? defaultConfigPath();
|
|
796
|
-
const env = options.env ?? process.env;
|
|
797
|
-
if (!existsSync3(path)) {
|
|
798
|
-
return {
|
|
799
|
-
path,
|
|
800
|
-
state: "missing",
|
|
801
|
-
profiles: diagnosticProfiles({}, env),
|
|
802
|
-
unknownProfiles: [],
|
|
803
|
-
safeMessage: null
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
let parsed;
|
|
807
|
-
try {
|
|
808
|
-
parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
809
|
-
} catch {
|
|
810
|
-
return {
|
|
811
|
-
path,
|
|
812
|
-
state: "malformed-json",
|
|
813
|
-
profiles: diagnosticProfiles({}, env),
|
|
814
|
-
unknownProfiles: [],
|
|
815
|
-
safeMessage: "Configuration contains malformed JSON and requires confirmation before repair."
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
const raw = asRawObject(parsed);
|
|
819
|
-
if (!raw || !isV2Schema(raw)) {
|
|
820
|
-
return {
|
|
821
|
-
path,
|
|
822
|
-
state: "unsupported-schema",
|
|
823
|
-
profiles: diagnosticProfiles({}, env),
|
|
824
|
-
unknownProfiles: [],
|
|
825
|
-
safeMessage: "Configuration schema is unsupported and requires confirmation before repair."
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
const agents = asRawObject(raw.agents) ?? {};
|
|
829
|
-
const known = /* @__PURE__ */ new Set(["default", ...SUPPORTED_AGENTS]);
|
|
830
|
-
return {
|
|
831
|
-
path,
|
|
832
|
-
state: "valid-v2",
|
|
833
|
-
profiles: diagnosticProfiles(raw, env),
|
|
834
|
-
unknownProfiles: Object.keys(agents).filter((name) => !known.has(name)),
|
|
835
|
-
safeMessage: null
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
function diagnosticProfiles(raw, env) {
|
|
839
|
-
const profiles = ["default", ...SUPPORTED_AGENTS];
|
|
840
|
-
return profiles.map((agent) => {
|
|
841
|
-
const explicit = explicitAgentRaw(raw, agent);
|
|
842
|
-
const inherited = agent === "default" ? null : explicitAgentRaw(raw, "default");
|
|
843
|
-
const explicitKey = explicitString(explicit, "api_key");
|
|
844
|
-
const inheritedKey = explicitString(inherited, "api_key");
|
|
845
|
-
const apiKey = env.CTXDB_API_KEY || explicitKey || inheritedKey;
|
|
846
|
-
const credentialSource = env.CTXDB_API_KEY ? "environment" : explicitKey ? "profile" : inheritedKey ? "default" : "none";
|
|
847
|
-
const credentialProfile = credentialSource === "profile" ? agent : credentialSource === "default" ? "default" : null;
|
|
848
|
-
const explicitBase = explicitString(explicit, "base_url");
|
|
849
|
-
const inheritedBase = explicitString(inherited, "base_url");
|
|
850
|
-
const baseUrl = (env.CTXDB_BASE_URL || explicitBase || inheritedBase || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
851
|
-
const baseUrlSource = env.CTXDB_BASE_URL ? "environment" : explicitBase ? "profile" : inheritedBase ? "default" : "built-in";
|
|
852
|
-
const explicitUser = explicitString(explicit, "user_id");
|
|
853
|
-
const inheritedUser = explicitString(inherited, "user_id");
|
|
854
|
-
const userId = env.CTXDB_USER_ID || explicitUser || inheritedUser || DEFAULT_USER_ID;
|
|
855
|
-
const userIdSource = env.CTXDB_USER_ID ? "environment" : explicitUser ? "profile" : inheritedUser ? "default" : "built-in";
|
|
856
|
-
return {
|
|
857
|
-
agent,
|
|
858
|
-
explicitProfile: explicit !== null,
|
|
859
|
-
explicitFields: explicit ? Object.keys(explicit).sort() : [],
|
|
860
|
-
credential: {
|
|
861
|
-
source: credentialSource,
|
|
862
|
-
profile: credentialProfile,
|
|
863
|
-
masked: apiKey ? maskApiKey(apiKey) : null,
|
|
864
|
-
fingerprint: apiKey ? credentialFingerprint(apiKey) : null
|
|
865
|
-
},
|
|
866
|
-
effective: {
|
|
867
|
-
baseUrl,
|
|
868
|
-
baseUrlSource,
|
|
869
|
-
userId,
|
|
870
|
-
userIdSource,
|
|
871
|
-
complete: Boolean(apiKey && baseUrl)
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
function agentRawFromFile(raw, agent) {
|
|
877
|
-
const agents = raw.agents;
|
|
878
|
-
if (agents && typeof agents === "object" && !Array.isArray(agents)) {
|
|
879
|
-
const candidate = agents[agent];
|
|
880
|
-
if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
|
|
881
|
-
return candidate;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
return {};
|
|
885
|
-
}
|
|
886
|
-
function isV2Schema(raw) {
|
|
887
|
-
if (Object.keys(raw).length === 0) return true;
|
|
888
|
-
return raw.version === 2 && raw.agents !== null && typeof raw.agents === "object" && !Array.isArray(raw.agents);
|
|
889
|
-
}
|
|
890
|
-
function configFromDisk(raw) {
|
|
891
|
-
const debugConfigured = coerceBool(raw.debug, false);
|
|
892
|
-
return applyDebugPolicy({
|
|
893
|
-
apiKey: typeof raw.api_key === "string" && raw.api_key ? raw.api_key : null,
|
|
894
|
-
baseUrl: typeof raw.base_url === "string" && raw.base_url ? String(raw.base_url).replace(/\/+$/, "") : DEFAULT_BASE_URL,
|
|
895
|
-
userId: typeof raw.user_id === "string" && raw.user_id ? raw.user_id : DEFAULT_USER_ID,
|
|
896
|
-
agentId: typeof raw.agent_id === "string" && raw.agent_id ? raw.agent_id : null,
|
|
897
|
-
appId: typeof raw.app_id === "string" && raw.app_id ? raw.app_id : null,
|
|
898
|
-
autoCapture: coerceBool(raw.auto_capture, true),
|
|
899
|
-
autoRecall: coerceBool(raw.auto_recall, true),
|
|
900
|
-
warmupRecall: coerceBool(raw.warmup_recall, false),
|
|
901
|
-
recallKnowledge: coerceBool(raw.recall_knowledge, false),
|
|
902
|
-
topK: coerceInt(raw.top_k, DEFAULT_TOP_K),
|
|
903
|
-
threshold: coerceFloat(raw.threshold, DEFAULT_THRESHOLD),
|
|
904
|
-
knowledgeTopK: coerceInt(raw.knowledge_top_k, DEFAULT_KNOWLEDGE_TOP_K),
|
|
905
|
-
debug: debugConfigured,
|
|
906
|
-
debugConfigured,
|
|
907
|
-
debugForced: false,
|
|
908
|
-
debugReason: null,
|
|
909
|
-
kbCatalogInjection: coerceKbCatalogInjection(raw.kb_catalog_injection)
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
function applyDebugPolicy(cfg) {
|
|
913
|
-
const policy = resolveDebugPolicy(cfg.debugConfigured, cfg.baseUrl);
|
|
914
|
-
cfg.debug = policy.debug;
|
|
915
|
-
cfg.debugConfigured = policy.debugConfigured;
|
|
916
|
-
cfg.debugForced = policy.debugForced;
|
|
917
|
-
cfg.debugReason = policy.debugReason;
|
|
918
|
-
return cfg;
|
|
919
|
-
}
|
|
920
|
-
function applyEnv(cfg, env) {
|
|
921
|
-
if (env.CTXDB_API_KEY) cfg.apiKey = env.CTXDB_API_KEY;
|
|
922
|
-
if (env.CTXDB_BASE_URL) cfg.baseUrl = env.CTXDB_BASE_URL.replace(/\/+$/, "");
|
|
923
|
-
if (env.CTXDB_USER_ID) cfg.userId = env.CTXDB_USER_ID;
|
|
924
|
-
if (env.CTXDB_AGENT_ID) cfg.agentId = env.CTXDB_AGENT_ID;
|
|
925
|
-
if (env.CTXDB_APP_ID) cfg.appId = env.CTXDB_APP_ID;
|
|
926
|
-
return applyDebugPolicy(cfg);
|
|
927
|
-
}
|
|
928
|
-
function load(options = {}) {
|
|
929
|
-
const env = options.env ?? process.env;
|
|
930
|
-
const path = options.path ?? defaultConfigPath(env);
|
|
931
|
-
const raw = readRaw(path);
|
|
932
|
-
const agent = resolveConfigAgent({ ...options, path, env }, raw);
|
|
933
|
-
if (!isV2Schema(raw)) {
|
|
934
|
-
try {
|
|
935
|
-
process.stderr.write(
|
|
936
|
-
`[ctxdb] config schema invalid at ${path}, treating as first-time install
|
|
937
|
-
`
|
|
938
|
-
);
|
|
939
|
-
} catch {
|
|
940
|
-
}
|
|
941
|
-
const cfg2 = configFromDisk({});
|
|
942
|
-
const managed2 = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials || options.managedCredentials === false ? void 0 : readActiveCredentialSync(
|
|
943
|
-
options.credentialsPath ?? join4(dirname4(path), "credentials.json")
|
|
944
|
-
);
|
|
945
|
-
if (managed2) {
|
|
946
|
-
cfg2.apiKey = managed2.payload.apiKey;
|
|
947
|
-
cfg2.baseUrl = managed2.metadata.baseUrl;
|
|
948
|
-
}
|
|
949
|
-
applyEnv(cfg2, env);
|
|
950
|
-
if (managed2) cfg2.authContext = {
|
|
951
|
-
configPath: path,
|
|
952
|
-
agent,
|
|
953
|
-
baseUrl: cfg2.baseUrl,
|
|
954
|
-
inheritDefaultKey: true,
|
|
955
|
-
managedCredentials: true,
|
|
956
|
-
credentialsPath: options.credentialsPath,
|
|
957
|
-
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
958
|
-
};
|
|
959
|
-
return cfg2;
|
|
960
|
-
}
|
|
961
|
-
const explicit = agentRawFromFile(raw, agent);
|
|
962
|
-
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) validateLoginChoice(explicit);
|
|
963
|
-
const agentRaw = { ...explicit };
|
|
964
|
-
if (agent !== "default") {
|
|
965
|
-
const defaultRaw = agentRawFromFile(raw, "default");
|
|
966
|
-
if (Object.keys(defaultRaw).length > 0) {
|
|
967
|
-
for (const [k, v] of Object.entries(defaultRaw)) {
|
|
968
|
-
if (k !== "credential_ref" && k !== "oauth_credential_ref" && k !== "access_credential" && !(k in agentRaw)) {
|
|
969
|
-
agentRaw[k] = v;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
const cfg = configFromDisk(agentRaw);
|
|
975
|
-
const managed = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials || options.managedCredentials === false ? void 0 : readActiveCredentialSync(
|
|
976
|
-
options.credentialsPath ?? join4(dirname4(path), "credentials.json")
|
|
977
|
-
);
|
|
978
|
-
if (managed) {
|
|
979
|
-
cfg.apiKey = managed.payload.apiKey;
|
|
980
|
-
cfg.baseUrl = managed.metadata.baseUrl;
|
|
981
|
-
}
|
|
982
|
-
const environmentCredential = !!(env.CTXDB_API_KEY || env.CTXDB_ACCESS_TOKEN?.trim());
|
|
983
|
-
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
984
|
-
cfg.credentialRef = explicit.credential_ref;
|
|
985
|
-
const stored = environmentCredential ? void 0 : publicAuthenticationFromConfig({ ...raw, version: 2, agents: raw.agents ?? {} }, path, agent, env.CTXDB_BASE_URL);
|
|
986
|
-
if (stored) {
|
|
987
|
-
cfg.baseUrl = stored.baseUrl;
|
|
988
|
-
cfg.apiKey = stored.apiKey ?? null;
|
|
989
|
-
cfg.oauthCredential = stored.oauthCredential;
|
|
990
|
-
} else if (cfg.credentialRef) cfg.apiKey = null;
|
|
991
|
-
if (environmentCredential && explicit.oauth_credential_ref) cfg.oauthCredential = oauthProfile(explicit.oauth_credential_ref, path, agent, cfg.baseUrl);
|
|
992
|
-
}
|
|
993
|
-
const persistedApiKey = cfg.apiKey;
|
|
994
|
-
applyEnv(cfg, env);
|
|
995
|
-
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
996
|
-
const authRaw = { ...raw, version: 2, agents: raw.agents ?? {} };
|
|
997
|
-
if (Object.hasOwn(explicit, "access_credential")) {
|
|
998
|
-
cfg.accessCredential = explicit.access_credential;
|
|
999
|
-
if (!env.CTXDB_API_KEY) cfg.apiKey = null;
|
|
1000
|
-
}
|
|
1001
|
-
if (env.CTXDB_API_KEY) {
|
|
1002
|
-
cfg.environmentApiKey = env.CTXDB_API_KEY;
|
|
1003
|
-
cfg.persistedApiKey = cfg.accessCredential !== void 0 || cfg.oauthCredential ? null : persistedApiKey;
|
|
1004
|
-
}
|
|
1005
|
-
cfg.authSnapshot = authSnapshot(authRaw, agent);
|
|
1006
|
-
cfg.authAgent = agent;
|
|
1007
|
-
cfg.authContext = {
|
|
1008
|
-
configPath: path,
|
|
1009
|
-
agent,
|
|
1010
|
-
baseUrl: cfg.baseUrl,
|
|
1011
|
-
inheritDefaultKey: true,
|
|
1012
|
-
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
if (managed) cfg.authContext = {
|
|
1016
|
-
configPath: path,
|
|
1017
|
-
agent,
|
|
1018
|
-
baseUrl: cfg.baseUrl,
|
|
1019
|
-
inheritDefaultKey: true,
|
|
1020
|
-
managedCredentials: true,
|
|
1021
|
-
credentialsPath: options.credentialsPath,
|
|
1022
|
-
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
1023
|
-
};
|
|
1024
|
-
return cfg;
|
|
1025
|
-
}
|
|
1026
|
-
function configToDisk(cfg) {
|
|
1027
|
-
return {
|
|
1028
|
-
...cfg.credentialRef ? { credential_ref: cfg.credentialRef } : {},
|
|
1029
|
-
...cfg.accessCredential !== void 0 ? { access_credential: cfg.accessCredential } : cfg.oauthCredential ? { oauth_credential_ref: cfg.oauthCredential.reference } : {},
|
|
1030
|
-
api_key: cfg.credentialRef || cfg.accessCredential !== void 0 || cfg.oauthCredential ? null : cfg.environmentApiKey && cfg.apiKey === cfg.environmentApiKey ? cfg.persistedApiKey ?? null : cfg.apiKey,
|
|
1031
|
-
...!cfg.credentialRef && cfg.accessCredential === void 0 && !cfg.oauthCredential && !cfg.authContext?.managedCredentials ? { base_url: cfg.baseUrl } : {},
|
|
1032
|
-
user_id: cfg.userId,
|
|
1033
|
-
agent_id: cfg.agentId,
|
|
1034
|
-
app_id: cfg.appId,
|
|
1035
|
-
auto_capture: cfg.autoCapture,
|
|
1036
|
-
auto_recall: cfg.autoRecall,
|
|
1037
|
-
warmup_recall: cfg.warmupRecall,
|
|
1038
|
-
recall_knowledge: cfg.recallKnowledge,
|
|
1039
|
-
top_k: cfg.topK,
|
|
1040
|
-
threshold: cfg.threshold,
|
|
1041
|
-
knowledge_top_k: cfg.knowledgeTopK,
|
|
1042
|
-
debug: cfg.debugConfigured,
|
|
1043
|
-
kb_catalog_injection: cfg.kbCatalogInjection
|
|
1044
|
-
};
|
|
1045
|
-
}
|
|
1046
|
-
function removeAgent(agent, path, options = {}) {
|
|
1047
|
-
const target = path ?? defaultConfigPath();
|
|
1048
|
-
return withAuthConfigLock(target, () => {
|
|
1049
|
-
if (!existsSync3(target)) {
|
|
1050
|
-
return { removed: false, remainingAgents: [], fileDeleted: false };
|
|
1051
|
-
}
|
|
1052
|
-
const raw = readRaw(target);
|
|
1053
|
-
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1054
|
-
return { removed: false, remainingAgents: [], fileDeleted: false };
|
|
1055
|
-
}
|
|
1056
|
-
const agents = { ...raw.agents };
|
|
1057
|
-
if (!(agent in agents)) {
|
|
1058
|
-
return {
|
|
1059
|
-
removed: false,
|
|
1060
|
-
remainingAgents: Object.keys(agents),
|
|
1061
|
-
fileDeleted: false
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
delete agents[agent];
|
|
1065
|
-
const remaining = Object.keys(agents);
|
|
1066
|
-
if (remaining.length === 0 && !options.keepEmptyShell && !Object.keys(asRawObject(raw.logins) ?? {}).length) {
|
|
1067
|
-
try {
|
|
1068
|
-
unlinkSync2(target);
|
|
1069
|
-
return { removed: true, remainingAgents: [], fileDeleted: true };
|
|
1070
|
-
} catch {
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
const onDisk = { ...raw, version: 2, agents };
|
|
1074
|
-
delete onDisk.default_agent;
|
|
1075
|
-
secureAtomicWrite(target, JSON.stringify(onDisk, null, 2) + "\n");
|
|
1076
|
-
return { removed: true, remainingAgents: remaining, fileDeleted: false };
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
function save(cfg, path, options = {}) {
|
|
1080
|
-
const target = path ?? defaultConfigPath();
|
|
1081
|
-
const agent = resolveConfigAgent(options);
|
|
1082
|
-
withAuthConfigLock(target, () => {
|
|
1083
|
-
const raw = readRaw(target);
|
|
1084
|
-
const validRaw = isV2Schema(raw) ? raw : {};
|
|
1085
|
-
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
1086
|
-
const authRaw = { ...validRaw, version: 2, agents: validRaw.agents ?? {} };
|
|
1087
|
-
if (cfg.authSnapshot && cfg.authAgent === agent) assertAuthSnapshot(authRaw, agent, cfg.authSnapshot);
|
|
1088
|
-
}
|
|
1089
|
-
const existingAgents = validRaw.agents && typeof validRaw.agents === "object" && !Array.isArray(validRaw.agents) ? { ...validRaw.agents } : {};
|
|
1090
|
-
const serialized = configToDisk(cfg);
|
|
1091
|
-
if (options.omitApiKey) delete serialized.api_key;
|
|
1092
|
-
const previous = asRawObject(existingAgents[agent]) ?? {};
|
|
1093
|
-
const next = { ...previous, ...serialized };
|
|
1094
|
-
if (cfg.credentialRef) {
|
|
1095
|
-
delete next.access_credential;
|
|
1096
|
-
delete next.oauth_credential_ref;
|
|
1097
|
-
delete next.api_key;
|
|
1098
|
-
} else if (cfg.accessCredential !== void 0) {
|
|
1099
|
-
delete next.credential_ref;
|
|
1100
|
-
delete next.oauth_credential_ref;
|
|
1101
|
-
delete next.api_key;
|
|
1102
|
-
} else if (cfg.oauthCredential) {
|
|
1103
|
-
delete next.access_credential;
|
|
1104
|
-
next.api_key = null;
|
|
1105
|
-
} else {
|
|
1106
|
-
delete next.access_credential;
|
|
1107
|
-
delete next.oauth_credential_ref;
|
|
1108
|
-
}
|
|
1109
|
-
if (options.omitApiKey) {
|
|
1110
|
-
delete next.api_key;
|
|
1111
|
-
for (const field of ["access_credential", "credential_ref", "oauth_credential_ref"]) {
|
|
1112
|
-
if (Object.hasOwn(previous, field)) next[field] = previous[field];
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
if (next.credential_ref || Object.hasOwn(next, "access_credential") || next.oauth_credential_ref || cfg.authContext?.managedCredentials) delete next.base_url;
|
|
1116
|
-
const onDisk = {
|
|
1117
|
-
...validRaw,
|
|
1118
|
-
version: 2,
|
|
1119
|
-
agents: {
|
|
1120
|
-
...existingAgents,
|
|
1121
|
-
[agent]: next
|
|
1122
|
-
}
|
|
1123
|
-
};
|
|
1124
|
-
delete onDisk.default_agent;
|
|
1125
|
-
secureAtomicWrite(target, JSON.stringify(onDisk, null, 2) + "\n");
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
function configuredAgents(path) {
|
|
1129
|
-
const target = path ?? defaultConfigPath();
|
|
1130
|
-
const raw = readRaw(target);
|
|
1131
|
-
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object") return [];
|
|
1132
|
-
return Object.keys(raw.agents).filter(isAgent);
|
|
1133
|
-
}
|
|
1134
|
-
function hasConfiguredAgent(agent, path) {
|
|
1135
|
-
const raw = readRaw(path ?? defaultConfigPath());
|
|
1136
|
-
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
const profile = raw.agents[agent];
|
|
1140
|
-
return Boolean(
|
|
1141
|
-
profile && typeof profile === "object" && !Array.isArray(profile)
|
|
1142
|
-
);
|
|
1143
|
-
}
|
|
1144
|
-
function updateConfiguredDebug(agent, configured, path) {
|
|
1145
|
-
const target = path ?? defaultConfigPath();
|
|
1146
|
-
return withAuthConfigLock(target, () => {
|
|
1147
|
-
const raw = readRaw(target);
|
|
1148
|
-
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1149
|
-
return false;
|
|
1150
|
-
}
|
|
1151
|
-
const agents = { ...raw.agents };
|
|
1152
|
-
const profile = agents[agent];
|
|
1153
|
-
if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
|
|
1154
|
-
return false;
|
|
1155
|
-
}
|
|
1156
|
-
agents[agent] = { ...profile, debug: configured };
|
|
1157
|
-
secureAtomicWrite(
|
|
1158
|
-
target,
|
|
1159
|
-
JSON.stringify({ ...raw, agents }, null, 2) + "\n"
|
|
1160
|
-
);
|
|
1161
|
-
return true;
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
function writeInstalledPkgVersion(version, path) {
|
|
1165
|
-
const target = path ?? defaultConfigPath();
|
|
1166
|
-
const raw = readRaw(target);
|
|
1167
|
-
const updated = { ...raw, installed_pkg_version: version };
|
|
1168
|
-
secureAtomicWrite(target, JSON.stringify(updated, null, 2) + "\n");
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
export {
|
|
1172
|
-
resolveDataApiCredential,
|
|
1173
|
-
isDataApiReady,
|
|
1174
|
-
setDebug,
|
|
1175
|
-
isDebug,
|
|
1176
|
-
debug,
|
|
1177
|
-
PACKAGE_NAME,
|
|
1178
|
-
PACKAGE_VERSION,
|
|
1179
|
-
CtxdbError,
|
|
1180
|
-
HttpClient,
|
|
1181
|
-
SUPPORTED_AGENTS,
|
|
1182
|
-
SUPPORTED_AGENT_CHOICES,
|
|
1183
|
-
AGENT_CHOICES,
|
|
1184
|
-
isBuiltinAgent,
|
|
1185
|
-
isAgentSlug,
|
|
1186
|
-
agentHomeDir,
|
|
1187
|
-
agentHomeDirs,
|
|
1188
|
-
agentPlatformSupport,
|
|
1189
|
-
inspectAgentHomes,
|
|
1190
|
-
detectInstalledAgents,
|
|
1191
|
-
agentFromEnv,
|
|
1192
|
-
agentFromArgvWithFallback,
|
|
1193
|
-
defaultConfigPath,
|
|
1194
|
-
configDir,
|
|
1195
|
-
DEFAULT_BASE_URL,
|
|
1196
|
-
DEFAULT_USER_ID,
|
|
1197
|
-
inspectConfig,
|
|
1198
|
-
load,
|
|
1199
|
-
removeAgent,
|
|
1200
|
-
save,
|
|
1201
|
-
configuredAgents,
|
|
1202
|
-
hasConfiguredAgent,
|
|
1203
|
-
updateConfiguredDebug,
|
|
1204
|
-
writeInstalledPkgVersion
|
|
1205
|
-
};
|