@aliyunrds/ctxdb 1.0.8-beta.4 → 1.0.8
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 +205 -32
- package/dist/{chunk-SVO6H62S.js → chunk-3FAQREIU.js} +285 -216
- package/dist/{chunk-OEZM2ZYA.js → chunk-3MEBQDGU.js} +5 -0
- package/dist/{chunk-WJTV7JNR.js → chunk-AAWG5SPO.js} +3 -3
- package/dist/{chunk-O6MKQ2I3.js → chunk-AH2QEUK4.js} +1 -1
- package/dist/{chunk-ZW7YXNJ6.js → chunk-LZD55CWE.js} +286 -11
- package/dist/{chunk-5DGRNP55.js → chunk-MWAFAK5M.js} +1 -1
- package/dist/{chunk-6MEWI73Y.js → chunk-RRDQCZJ4.js} +2 -2
- package/dist/{chunk-76AXXLVE.js → chunk-YTCXHO4F.js} +2 -2
- package/dist/cli/main.js +4151 -1926
- package/dist/hooks/hermes-post-llm-call.js +3 -3
- package/dist/hooks/hermes-pre-llm-call.js +6 -6
- package/dist/hooks/session-start.js +5 -5
- package/dist/hooks/stop.js +3 -3
- package/dist/hooks/user-prompt-submit.js +5 -5
- package/dist/opencode/index.js +725 -245
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +14 -3
- package/dist/setup/skills/contextdb-memory/SKILL.md +10 -3
- package/dist/workers/version-check.js +1 -1
- package/package.json +2 -2
|
@@ -4,16 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-5ITT5GSP.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/credentials.ts
|
|
7
|
+
import { selectDataApiCredential } from "@aliyunrds/ctxdb-shared";
|
|
7
8
|
function apiKeyCredential(apiKey) {
|
|
8
9
|
return { type: "api-key", value: apiKey };
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
const accessToken = env.CTXDB_ACCESS_TOKEN?.trim();
|
|
12
|
-
if (accessToken) return { type: "access-token", value: accessToken };
|
|
13
|
-
if (env.CTXDB_API_KEY) return apiKeyCredential(env.CTXDB_API_KEY);
|
|
14
|
-
if (cfg.oauthCredential) return { type: "oauth-session", profile: cfg.oauthCredential };
|
|
15
|
-
return cfg.apiKey ? apiKeyCredential(cfg.apiKey) : null;
|
|
16
|
-
}
|
|
11
|
+
var resolveDataApiCredential = selectDataApiCredential;
|
|
17
12
|
function isDataApiReady(cfg, env = process.env) {
|
|
18
13
|
return Boolean(cfg.baseUrl && resolveDataApiCredential(cfg, env));
|
|
19
14
|
}
|
|
@@ -84,7 +79,8 @@ var PACKAGE_NAME = PACKAGE_IDENTITY.name;
|
|
|
84
79
|
var PACKAGE_VERSION = PACKAGE_IDENTITY.version;
|
|
85
80
|
|
|
86
81
|
// src/lib/http-client.ts
|
|
87
|
-
import {
|
|
82
|
+
import { createRequestAuthorizationProvider } from "@aliyunrds/ctxdb-shared";
|
|
83
|
+
import "@aliyunrds/ctxdb-shared";
|
|
88
84
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
89
85
|
var CtxdbError = class extends Error {
|
|
90
86
|
constructor(message) {
|
|
@@ -178,7 +174,7 @@ var HttpClient = class {
|
|
|
178
174
|
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
179
175
|
this.credential = credential;
|
|
180
176
|
this.apiKey = opts.apiKey ?? "";
|
|
181
|
-
this.authorizationProvider = opts.authorizationProvider ?? (credential
|
|
177
|
+
this.authorizationProvider = opts.authorizationProvider ?? createRequestAuthorizationProvider(credential, this.baseUrl);
|
|
182
178
|
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
183
179
|
this.userAgent = opts.userAgent ?? `ctxdb-cli/${PACKAGE_VERSION}`;
|
|
184
180
|
this.extraHeaders = { ...opts.extraHeaders ?? {} };
|
|
@@ -201,9 +197,10 @@ var HttpClient = class {
|
|
|
201
197
|
return h;
|
|
202
198
|
}
|
|
203
199
|
async doRequest(method, path, init = {}) {
|
|
200
|
+
const effectiveTimeout = init.timeoutMs ?? this.timeoutMs, started = Date.now();
|
|
204
201
|
let authorization;
|
|
205
202
|
if (this.authorizationProvider) {
|
|
206
|
-
authorization = await this.authorizationProvider.resolve();
|
|
203
|
+
authorization = await this.authorizationProvider.resolve({ timeoutMs: effectiveTimeout });
|
|
207
204
|
} else {
|
|
208
205
|
const credential = this.credential;
|
|
209
206
|
if (!credential || credential.type === "oauth-session") throw new CtxdbError("DATA API credential is required");
|
|
@@ -216,7 +213,7 @@ var HttpClient = class {
|
|
|
216
213
|
}
|
|
217
214
|
const safeText = (value) => {
|
|
218
215
|
const text = String(value);
|
|
219
|
-
return
|
|
216
|
+
return text.split(authorization.value).join("[REDACTED]");
|
|
220
217
|
};
|
|
221
218
|
let url = `${authorization.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
222
219
|
if (init.params) {
|
|
@@ -227,7 +224,6 @@ var HttpClient = class {
|
|
|
227
224
|
const s = qs.toString();
|
|
228
225
|
if (s) url = `${url}?${s}`;
|
|
229
226
|
}
|
|
230
|
-
const effectiveTimeout = init.timeoutMs ?? this.timeoutMs;
|
|
231
227
|
const dbg = isDebug();
|
|
232
228
|
let t0 = 0;
|
|
233
229
|
if (dbg) {
|
|
@@ -235,7 +231,7 @@ var HttpClient = class {
|
|
|
235
231
|
debug("http", `\u2192 ${method} ${url} (timeout=${effectiveTimeout}ms)`);
|
|
236
232
|
}
|
|
237
233
|
const controller = new AbortController();
|
|
238
|
-
const timer = setTimeout(() => controller.abort(), effectiveTimeout);
|
|
234
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, effectiveTimeout - (Date.now() - started)));
|
|
239
235
|
try {
|
|
240
236
|
let resp;
|
|
241
237
|
try {
|
|
@@ -264,7 +260,8 @@ var HttpClient = class {
|
|
|
264
260
|
}
|
|
265
261
|
let text;
|
|
266
262
|
try {
|
|
267
|
-
|
|
263
|
+
const bodyText = await resp.text();
|
|
264
|
+
text = authorization.scheme === "Bearer" || !resp.ok ? safeText(bodyText) : bodyText;
|
|
268
265
|
} catch (err) {
|
|
269
266
|
if (err?.name === "AbortError") {
|
|
270
267
|
if (dbg) debug("http", `\u2717 ${method} ${path} body-read timeout after ${Date.now() - t0}ms`);
|
|
@@ -605,9 +602,10 @@ function agentFromArgvWithFallback(argv = process.argv.slice(2), env = process.e
|
|
|
605
602
|
}
|
|
606
603
|
|
|
607
604
|
// src/lib/config.ts
|
|
608
|
-
import {
|
|
609
|
-
import {
|
|
610
|
-
import {
|
|
605
|
+
import { publicAuthenticationFromConfig } from "@aliyunrds/ctxdb-shared";
|
|
606
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
607
|
+
import { homedir as homedir3 } from "os";
|
|
608
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
611
609
|
|
|
612
610
|
// src/lib/secure-file.ts
|
|
613
611
|
import {
|
|
@@ -707,146 +705,39 @@ function secureAtomicWrite(target, content, options = {}) {
|
|
|
707
705
|
// src/lib/secrets.ts
|
|
708
706
|
import { createHmac, randomBytes as randomBytes2 } from "crypto";
|
|
709
707
|
var PROCESS_FINGERPRINT_KEY = randomBytes2(32);
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
readFileSync as readFileSync2,
|
|
718
|
-
statSync as statSync2,
|
|
719
|
-
unlinkSync as unlinkSync2,
|
|
720
|
-
writeFileSync as writeFileSync2
|
|
721
|
-
} from "fs";
|
|
722
|
-
import { homedir as homedir3 } from "os";
|
|
723
|
-
import { dirname as dirname4, join as join4 } from "path";
|
|
724
|
-
import { setTimeout as delay } from "timers/promises";
|
|
725
|
-
|
|
726
|
-
// src/credentials/types.ts
|
|
727
|
-
var ACTIVE_CONTEXTDB_CREDENTIAL = "contextdb/active";
|
|
728
|
-
|
|
729
|
-
// src/credentials/local-credential-provider.ts
|
|
730
|
-
var DOCUMENT_VERSION = 1;
|
|
731
|
-
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
732
|
-
function defaultCredentialsPath() {
|
|
733
|
-
return join4(homedir3(), ".ctxdb", "credentials.json");
|
|
708
|
+
function maskApiKey(apiKey) {
|
|
709
|
+
if (!apiKey) return "";
|
|
710
|
+
const payload = apiKey.startsWith("ctxdb-") ? apiKey.slice("ctxdb-".length) : null;
|
|
711
|
+
if (payload && payload.length > 12) return `ctxdb-xxxxxx${payload.slice(-6)}`;
|
|
712
|
+
if (payload !== null) return "x".repeat(apiKey.length);
|
|
713
|
+
if (apiKey.length <= 12) return "x".repeat(apiKey.length);
|
|
714
|
+
return `xxxxxx${apiKey.slice(-6)}`;
|
|
734
715
|
}
|
|
735
|
-
function
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
function normalizeOrigin(value, field) {
|
|
739
|
-
if (typeof value !== "string" || !value) {
|
|
740
|
-
throw new Error(`credentials: ${field} must be a non-empty URL`);
|
|
741
|
-
}
|
|
742
|
-
let url;
|
|
743
|
-
try {
|
|
744
|
-
url = new URL(value);
|
|
745
|
-
} catch {
|
|
746
|
-
throw new Error(`credentials: ${field} must be a valid URL`);
|
|
747
|
-
}
|
|
748
|
-
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
|
|
749
|
-
throw new Error(`credentials: ${field} must be an HTTP(S) origin`);
|
|
750
|
-
}
|
|
751
|
-
return url.toString().replace(/\/$/, "");
|
|
752
|
-
}
|
|
753
|
-
function parseRecord(value) {
|
|
754
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
755
|
-
throw new Error("credentials: record must be an object");
|
|
756
|
-
}
|
|
757
|
-
const raw = value;
|
|
758
|
-
if (raw.kind !== "api-key") {
|
|
759
|
-
throw new Error(`credentials: unsupported record kind ${String(raw.kind)}`);
|
|
760
|
-
}
|
|
761
|
-
const payload = raw.payload;
|
|
762
|
-
const metadata = raw.metadata;
|
|
763
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
764
|
-
throw new Error("credentials: api-key payload must be an object");
|
|
765
|
-
}
|
|
766
|
-
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
767
|
-
throw new Error("credentials: api-key metadata must be an object");
|
|
768
|
-
}
|
|
769
|
-
const body = payload;
|
|
770
|
-
const meta = metadata;
|
|
771
|
-
if (typeof body.api_key !== "string" || !body.api_key) {
|
|
772
|
-
throw new Error("credentials: api_key must be a non-empty string");
|
|
773
|
-
}
|
|
774
|
-
if (meta.authorization_method !== "browser-loopback" && meta.authorization_method !== "device-code") {
|
|
775
|
-
throw new Error("credentials: authorization_method is invalid");
|
|
776
|
-
}
|
|
777
|
-
if (typeof meta.issued_at !== "string" || !Number.isFinite(Date.parse(meta.issued_at))) {
|
|
778
|
-
throw new Error("credentials: issued_at is invalid");
|
|
779
|
-
}
|
|
780
|
-
return {
|
|
781
|
-
kind: "api-key",
|
|
782
|
-
payload: {
|
|
783
|
-
apiKey: body.api_key,
|
|
784
|
-
baseUrl: normalizeOrigin(body.base_url, "base_url"),
|
|
785
|
-
loginServer: normalizeOrigin(body.login_server, "login_server")
|
|
786
|
-
},
|
|
787
|
-
metadata: {
|
|
788
|
-
authorizationMethod: meta.authorization_method,
|
|
789
|
-
issuedAt: meta.issued_at
|
|
790
|
-
}
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
function parseDocument(text) {
|
|
794
|
-
let raw;
|
|
795
|
-
try {
|
|
796
|
-
raw = JSON.parse(text);
|
|
797
|
-
} catch {
|
|
798
|
-
throw new Error("credentials: credentials.json is not valid JSON");
|
|
799
|
-
}
|
|
800
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
801
|
-
throw new Error("credentials: document must be an object");
|
|
802
|
-
}
|
|
803
|
-
const document = raw;
|
|
804
|
-
if (document.version !== DOCUMENT_VERSION) {
|
|
805
|
-
throw new Error(`credentials: unsupported document version ${String(document.version)}`);
|
|
806
|
-
}
|
|
807
|
-
if (!document.records || typeof document.records !== "object" || Array.isArray(document.records)) {
|
|
808
|
-
throw new Error("credentials: records must be an object");
|
|
809
|
-
}
|
|
810
|
-
const records = document.records;
|
|
811
|
-
const unknown = Object.keys(records).filter((key) => key !== ACTIVE_CONTEXTDB_CREDENTIAL);
|
|
812
|
-
if (unknown.length > 0) {
|
|
813
|
-
throw new Error(`credentials: unsupported record key ${unknown[0]}`);
|
|
814
|
-
}
|
|
815
|
-
const active = records[ACTIVE_CONTEXTDB_CREDENTIAL];
|
|
816
|
-
return {
|
|
817
|
-
version: DOCUMENT_VERSION,
|
|
818
|
-
records: active === void 0 ? {} : { [ACTIVE_CONTEXTDB_CREDENTIAL]: parseRecord(active) }
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
function assertOwnerOnly(path) {
|
|
822
|
-
if (process.platform === "win32" || !existsSync3(path)) return;
|
|
823
|
-
const mode = statSync2(path).mode & 511;
|
|
824
|
-
if ((mode & 63) !== 0) {
|
|
825
|
-
throw new Error(`credentials: ${path} must be owner-only (run chmod 600)`);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
function readDocument(path) {
|
|
829
|
-
if (!existsSync3(path)) return emptyDocument();
|
|
830
|
-
assertOwnerOnly(path);
|
|
831
|
-
return parseDocument(readFileSync2(path, "utf8"));
|
|
832
|
-
}
|
|
833
|
-
function readActiveCredentialSync(path = defaultCredentialsPath()) {
|
|
834
|
-
return readDocument(path).records[ACTIVE_CONTEXTDB_CREDENTIAL];
|
|
716
|
+
function credentialFingerprint(apiKey, processKey = PROCESS_FINGERPRINT_KEY) {
|
|
717
|
+
if (!apiKey) return "";
|
|
718
|
+
return createHmac("sha256", processKey).update(apiKey).digest("hex").slice(0, 12);
|
|
835
719
|
}
|
|
836
720
|
|
|
721
|
+
// src/credentials/local-credential-provider.ts
|
|
722
|
+
import { LocalCredentialProvider, defaultCredentialsPath, readActiveCredentialSync, withAuthorizationLock, internalCandidate } from "@aliyunrds/ctxdb-shared";
|
|
723
|
+
|
|
837
724
|
// src/lib/config.ts
|
|
838
725
|
import {
|
|
839
726
|
resolveDebugPolicy,
|
|
840
|
-
oauthProfile
|
|
727
|
+
oauthProfile,
|
|
728
|
+
validateLoginChoice,
|
|
729
|
+
authSnapshot,
|
|
730
|
+
assertAuthSnapshot,
|
|
731
|
+
withAuthConfigLock
|
|
841
732
|
} from "@aliyunrds/ctxdb-shared";
|
|
842
733
|
function defaultConfigPath(env = process.env) {
|
|
843
|
-
return env.CTXDB_CONFIG_PATH ||
|
|
734
|
+
return env.CTXDB_CONFIG_PATH || join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
844
735
|
}
|
|
845
|
-
var DEFAULT_CONFIG_PATH =
|
|
736
|
+
var DEFAULT_CONFIG_PATH = join4(homedir3(), ".ctxdb", "ctxdb.json");
|
|
846
737
|
function configDir() {
|
|
847
|
-
return
|
|
738
|
+
return join4(homedir3(), ".ctxdb");
|
|
848
739
|
}
|
|
849
|
-
var DEFAULT_BASE_URL = "https://
|
|
740
|
+
var DEFAULT_BASE_URL = "https://api.cn-hangzhou.agentcontext.aliyuncs.com";
|
|
850
741
|
var DEFAULT_USER_ID = "default";
|
|
851
742
|
var DEFAULT_TOP_K = 5;
|
|
852
743
|
var DEFAULT_THRESHOLD = 0.4;
|
|
@@ -878,9 +769,9 @@ function coerceKbCatalogInjection(v) {
|
|
|
878
769
|
return DEFAULT_KB_CATALOG_INJECTION;
|
|
879
770
|
}
|
|
880
771
|
function readRaw(path) {
|
|
881
|
-
if (!
|
|
772
|
+
if (!existsSync3(path)) return {};
|
|
882
773
|
try {
|
|
883
|
-
const parsed = JSON.parse(
|
|
774
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
884
775
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
885
776
|
return parsed;
|
|
886
777
|
}
|
|
@@ -888,6 +779,99 @@ function readRaw(path) {
|
|
|
888
779
|
}
|
|
889
780
|
return {};
|
|
890
781
|
}
|
|
782
|
+
function asRawObject(value) {
|
|
783
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
784
|
+
}
|
|
785
|
+
function explicitAgentRaw(raw, agent) {
|
|
786
|
+
const agents = asRawObject(raw.agents);
|
|
787
|
+
return agents ? asRawObject(agents[agent]) : null;
|
|
788
|
+
}
|
|
789
|
+
function explicitString(raw, key) {
|
|
790
|
+
const value = raw?.[key];
|
|
791
|
+
return typeof value === "string" && value ? value : null;
|
|
792
|
+
}
|
|
793
|
+
function inspectConfig(options = {}) {
|
|
794
|
+
const path = options.path ?? defaultConfigPath();
|
|
795
|
+
const env = options.env ?? process.env;
|
|
796
|
+
if (!existsSync3(path)) {
|
|
797
|
+
return {
|
|
798
|
+
path,
|
|
799
|
+
state: "missing",
|
|
800
|
+
profiles: diagnosticProfiles({}, env),
|
|
801
|
+
unknownProfiles: [],
|
|
802
|
+
safeMessage: null
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
let parsed;
|
|
806
|
+
try {
|
|
807
|
+
parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
808
|
+
} catch {
|
|
809
|
+
return {
|
|
810
|
+
path,
|
|
811
|
+
state: "malformed-json",
|
|
812
|
+
profiles: diagnosticProfiles({}, env),
|
|
813
|
+
unknownProfiles: [],
|
|
814
|
+
safeMessage: "Configuration contains malformed JSON and requires confirmation before repair."
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
const raw = asRawObject(parsed);
|
|
818
|
+
if (!raw || !isV2Schema(raw)) {
|
|
819
|
+
return {
|
|
820
|
+
path,
|
|
821
|
+
state: "unsupported-schema",
|
|
822
|
+
profiles: diagnosticProfiles({}, env),
|
|
823
|
+
unknownProfiles: [],
|
|
824
|
+
safeMessage: "Configuration schema is unsupported and requires confirmation before repair."
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
const agents = asRawObject(raw.agents) ?? {};
|
|
828
|
+
const known = /* @__PURE__ */ new Set(["default", ...SUPPORTED_AGENTS]);
|
|
829
|
+
return {
|
|
830
|
+
path,
|
|
831
|
+
state: "valid-v2",
|
|
832
|
+
profiles: diagnosticProfiles(raw, env),
|
|
833
|
+
unknownProfiles: Object.keys(agents).filter((name) => !known.has(name)),
|
|
834
|
+
safeMessage: null
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function diagnosticProfiles(raw, env) {
|
|
838
|
+
const profiles = ["default", ...SUPPORTED_AGENTS];
|
|
839
|
+
return profiles.map((agent) => {
|
|
840
|
+
const explicit = explicitAgentRaw(raw, agent);
|
|
841
|
+
const inherited = agent === "default" ? null : explicitAgentRaw(raw, "default");
|
|
842
|
+
const explicitKey = explicitString(explicit, "api_key");
|
|
843
|
+
const inheritedKey = explicitString(inherited, "api_key");
|
|
844
|
+
const apiKey = env.CTXDB_API_KEY || explicitKey || inheritedKey;
|
|
845
|
+
const credentialSource = env.CTXDB_API_KEY ? "environment" : explicitKey ? "profile" : inheritedKey ? "default" : "none";
|
|
846
|
+
const credentialProfile = credentialSource === "profile" ? agent : credentialSource === "default" ? "default" : null;
|
|
847
|
+
const explicitBase = explicitString(explicit, "base_url");
|
|
848
|
+
const inheritedBase = explicitString(inherited, "base_url");
|
|
849
|
+
const baseUrl = (env.CTXDB_BASE_URL || explicitBase || inheritedBase || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
850
|
+
const baseUrlSource = env.CTXDB_BASE_URL ? "environment" : explicitBase ? "profile" : inheritedBase ? "default" : "built-in";
|
|
851
|
+
const explicitUser = explicitString(explicit, "user_id");
|
|
852
|
+
const inheritedUser = explicitString(inherited, "user_id");
|
|
853
|
+
const userId = env.CTXDB_USER_ID || explicitUser || inheritedUser || DEFAULT_USER_ID;
|
|
854
|
+
const userIdSource = env.CTXDB_USER_ID ? "environment" : explicitUser ? "profile" : inheritedUser ? "default" : "built-in";
|
|
855
|
+
return {
|
|
856
|
+
agent,
|
|
857
|
+
explicitProfile: explicit !== null,
|
|
858
|
+
explicitFields: explicit ? Object.keys(explicit).sort() : [],
|
|
859
|
+
credential: {
|
|
860
|
+
source: credentialSource,
|
|
861
|
+
profile: credentialProfile,
|
|
862
|
+
masked: apiKey ? maskApiKey(apiKey) : null,
|
|
863
|
+
fingerprint: apiKey ? credentialFingerprint(apiKey) : null
|
|
864
|
+
},
|
|
865
|
+
effective: {
|
|
866
|
+
baseUrl,
|
|
867
|
+
baseUrlSource,
|
|
868
|
+
userId,
|
|
869
|
+
userIdSource,
|
|
870
|
+
complete: Boolean(apiKey && baseUrl)
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
});
|
|
874
|
+
}
|
|
891
875
|
function agentRawFromFile(raw, agent) {
|
|
892
876
|
const agents = raw.agents;
|
|
893
877
|
if (agents && typeof agents === "object" && !Array.isArray(agents)) {
|
|
@@ -955,20 +939,32 @@ function load(options = {}) {
|
|
|
955
939
|
}
|
|
956
940
|
const cfg2 = configFromDisk({});
|
|
957
941
|
const managed2 = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials || options.managedCredentials === false ? void 0 : readActiveCredentialSync(
|
|
958
|
-
options.credentialsPath ??
|
|
942
|
+
options.credentialsPath ?? join4(dirname4(path), "credentials.json")
|
|
959
943
|
);
|
|
960
944
|
if (managed2) {
|
|
961
945
|
cfg2.apiKey = managed2.payload.apiKey;
|
|
962
|
-
cfg2.baseUrl = managed2.
|
|
946
|
+
cfg2.baseUrl = managed2.metadata.baseUrl;
|
|
963
947
|
}
|
|
964
|
-
|
|
948
|
+
applyEnv(cfg2, env);
|
|
949
|
+
if (managed2) cfg2.authContext = {
|
|
950
|
+
configPath: path,
|
|
951
|
+
agent,
|
|
952
|
+
baseUrl: cfg2.baseUrl,
|
|
953
|
+
inheritDefaultKey: true,
|
|
954
|
+
managedCredentials: true,
|
|
955
|
+
credentialsPath: options.credentialsPath,
|
|
956
|
+
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
957
|
+
};
|
|
958
|
+
return cfg2;
|
|
965
959
|
}
|
|
966
|
-
const
|
|
960
|
+
const explicit = agentRawFromFile(raw, agent);
|
|
961
|
+
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) validateLoginChoice(explicit);
|
|
962
|
+
const agentRaw = { ...explicit };
|
|
967
963
|
if (agent !== "default") {
|
|
968
964
|
const defaultRaw = agentRawFromFile(raw, "default");
|
|
969
965
|
if (Object.keys(defaultRaw).length > 0) {
|
|
970
966
|
for (const [k, v] of Object.entries(defaultRaw)) {
|
|
971
|
-
if (k !== "oauth_credential_ref" && !(k in agentRaw)) {
|
|
967
|
+
if (k !== "credential_ref" && k !== "oauth_credential_ref" && k !== "access_credential" && !(k in agentRaw)) {
|
|
972
968
|
agentRaw[k] = v;
|
|
973
969
|
}
|
|
974
970
|
}
|
|
@@ -976,23 +972,62 @@ function load(options = {}) {
|
|
|
976
972
|
}
|
|
977
973
|
const cfg = configFromDisk(agentRaw);
|
|
978
974
|
const managed = !DISTRIBUTION_MANIFEST.capabilities.managedCredentials || options.managedCredentials === false ? void 0 : readActiveCredentialSync(
|
|
979
|
-
options.credentialsPath ??
|
|
975
|
+
options.credentialsPath ?? join4(dirname4(path), "credentials.json")
|
|
980
976
|
);
|
|
981
977
|
if (managed) {
|
|
982
978
|
cfg.apiKey = managed.payload.apiKey;
|
|
983
|
-
cfg.baseUrl = managed.
|
|
979
|
+
cfg.baseUrl = managed.metadata.baseUrl;
|
|
984
980
|
}
|
|
981
|
+
const environmentCredential = !!(env.CTXDB_API_KEY || env.CTXDB_ACCESS_TOKEN?.trim());
|
|
982
|
+
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
983
|
+
cfg.credentialRef = explicit.credential_ref;
|
|
984
|
+
const stored = environmentCredential ? void 0 : publicAuthenticationFromConfig({ ...raw, version: 2, agents: raw.agents ?? {} }, path, agent, env.CTXDB_BASE_URL);
|
|
985
|
+
if (stored) {
|
|
986
|
+
cfg.baseUrl = stored.baseUrl;
|
|
987
|
+
cfg.apiKey = stored.apiKey ?? null;
|
|
988
|
+
cfg.oauthCredential = stored.oauthCredential;
|
|
989
|
+
} else if (cfg.credentialRef) cfg.apiKey = null;
|
|
990
|
+
if (environmentCredential && explicit.oauth_credential_ref) cfg.oauthCredential = oauthProfile(explicit.oauth_credential_ref, path, agent, cfg.baseUrl);
|
|
991
|
+
}
|
|
992
|
+
const persistedApiKey = cfg.apiKey;
|
|
985
993
|
applyEnv(cfg, env);
|
|
986
994
|
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
987
|
-
|
|
995
|
+
const authRaw = { ...raw, version: 2, agents: raw.agents ?? {} };
|
|
996
|
+
if (Object.hasOwn(explicit, "access_credential")) {
|
|
997
|
+
cfg.accessCredential = explicit.access_credential;
|
|
998
|
+
if (!env.CTXDB_API_KEY) cfg.apiKey = null;
|
|
999
|
+
}
|
|
1000
|
+
if (env.CTXDB_API_KEY) {
|
|
1001
|
+
cfg.environmentApiKey = env.CTXDB_API_KEY;
|
|
1002
|
+
cfg.persistedApiKey = cfg.accessCredential !== void 0 || cfg.oauthCredential ? null : persistedApiKey;
|
|
1003
|
+
}
|
|
1004
|
+
cfg.authSnapshot = authSnapshot(authRaw, agent);
|
|
1005
|
+
cfg.authAgent = agent;
|
|
1006
|
+
cfg.authContext = {
|
|
1007
|
+
configPath: path,
|
|
1008
|
+
agent,
|
|
1009
|
+
baseUrl: cfg.baseUrl,
|
|
1010
|
+
inheritDefaultKey: true,
|
|
1011
|
+
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
1012
|
+
};
|
|
988
1013
|
}
|
|
1014
|
+
if (managed) cfg.authContext = {
|
|
1015
|
+
configPath: path,
|
|
1016
|
+
agent,
|
|
1017
|
+
baseUrl: cfg.baseUrl,
|
|
1018
|
+
inheritDefaultKey: true,
|
|
1019
|
+
managedCredentials: true,
|
|
1020
|
+
credentialsPath: options.credentialsPath,
|
|
1021
|
+
...env.CTXDB_BASE_URL ? { baseUrlOverride: env.CTXDB_BASE_URL } : {}
|
|
1022
|
+
};
|
|
989
1023
|
return cfg;
|
|
990
1024
|
}
|
|
991
1025
|
function configToDisk(cfg) {
|
|
992
1026
|
return {
|
|
993
|
-
...cfg.
|
|
994
|
-
|
|
995
|
-
|
|
1027
|
+
...cfg.credentialRef ? { credential_ref: cfg.credentialRef } : {},
|
|
1028
|
+
...cfg.accessCredential !== void 0 ? { access_credential: cfg.accessCredential } : cfg.oauthCredential ? { oauth_credential_ref: cfg.oauthCredential.reference } : {},
|
|
1029
|
+
api_key: cfg.credentialRef || cfg.accessCredential !== void 0 || cfg.oauthCredential ? null : cfg.environmentApiKey && cfg.apiKey === cfg.environmentApiKey ? cfg.persistedApiKey ?? null : cfg.apiKey,
|
|
1030
|
+
...!cfg.credentialRef && cfg.accessCredential === void 0 && !cfg.oauthCredential && !cfg.authContext?.managedCredentials ? { base_url: cfg.baseUrl } : {},
|
|
996
1031
|
user_id: cfg.userId,
|
|
997
1032
|
agent_id: cfg.agentId,
|
|
998
1033
|
app_id: cfg.appId,
|
|
@@ -1009,53 +1044,85 @@ function configToDisk(cfg) {
|
|
|
1009
1044
|
}
|
|
1010
1045
|
function removeAgent(agent, path, options = {}) {
|
|
1011
1046
|
const target = path ?? defaultConfigPath();
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const raw = readRaw(target);
|
|
1016
|
-
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1017
|
-
return { removed: false, remainingAgents: [], fileDeleted: false };
|
|
1018
|
-
}
|
|
1019
|
-
const agents = { ...raw.agents };
|
|
1020
|
-
if (!(agent in agents)) {
|
|
1021
|
-
return {
|
|
1022
|
-
removed: false,
|
|
1023
|
-
remainingAgents: Object.keys(agents),
|
|
1024
|
-
fileDeleted: false
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
delete agents[agent];
|
|
1028
|
-
const remaining = Object.keys(agents);
|
|
1029
|
-
if (remaining.length === 0 && !options.keepEmptyShell) {
|
|
1030
|
-
try {
|
|
1031
|
-
unlinkSync3(target);
|
|
1032
|
-
return { removed: true, remainingAgents: [], fileDeleted: true };
|
|
1033
|
-
} catch {
|
|
1047
|
+
return withAuthConfigLock(target, () => {
|
|
1048
|
+
if (!existsSync3(target)) {
|
|
1049
|
+
return { removed: false, remainingAgents: [], fileDeleted: false };
|
|
1034
1050
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1051
|
+
const raw = readRaw(target);
|
|
1052
|
+
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1053
|
+
return { removed: false, remainingAgents: [], fileDeleted: false };
|
|
1054
|
+
}
|
|
1055
|
+
const agents = { ...raw.agents };
|
|
1056
|
+
if (!(agent in agents)) {
|
|
1057
|
+
return {
|
|
1058
|
+
removed: false,
|
|
1059
|
+
remainingAgents: Object.keys(agents),
|
|
1060
|
+
fileDeleted: false
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
delete agents[agent];
|
|
1064
|
+
const remaining = Object.keys(agents);
|
|
1065
|
+
if (remaining.length === 0 && !options.keepEmptyShell && !Object.keys(asRawObject(raw.logins) ?? {}).length) {
|
|
1066
|
+
try {
|
|
1067
|
+
unlinkSync2(target);
|
|
1068
|
+
return { removed: true, remainingAgents: [], fileDeleted: true };
|
|
1069
|
+
} catch {
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
const onDisk = { ...raw, version: 2, agents };
|
|
1073
|
+
delete onDisk.default_agent;
|
|
1074
|
+
secureAtomicWrite(target, JSON.stringify(onDisk, null, 2) + "\n");
|
|
1075
|
+
return { removed: true, remainingAgents: remaining, fileDeleted: false };
|
|
1076
|
+
});
|
|
1040
1077
|
}
|
|
1041
1078
|
function save(cfg, path, options = {}) {
|
|
1042
1079
|
const target = path ?? defaultConfigPath();
|
|
1043
1080
|
const agent = resolveConfigAgent(options);
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
...validRaw,
|
|
1051
|
-
version: 2,
|
|
1052
|
-
agents: {
|
|
1053
|
-
...existingAgents,
|
|
1054
|
-
[agent]: serialized
|
|
1081
|
+
withAuthConfigLock(target, () => {
|
|
1082
|
+
const raw = readRaw(target);
|
|
1083
|
+
const validRaw = isV2Schema(raw) ? raw : {};
|
|
1084
|
+
if (DISTRIBUTION_MANIFEST.capabilities.deviceFlow) {
|
|
1085
|
+
const authRaw = { ...validRaw, version: 2, agents: validRaw.agents ?? {} };
|
|
1086
|
+
if (cfg.authSnapshot && cfg.authAgent === agent) assertAuthSnapshot(authRaw, agent, cfg.authSnapshot);
|
|
1055
1087
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1088
|
+
const existingAgents = validRaw.agents && typeof validRaw.agents === "object" && !Array.isArray(validRaw.agents) ? { ...validRaw.agents } : {};
|
|
1089
|
+
const serialized = configToDisk(cfg);
|
|
1090
|
+
if (options.omitApiKey) delete serialized.api_key;
|
|
1091
|
+
const previous = asRawObject(existingAgents[agent]) ?? {};
|
|
1092
|
+
const next = { ...previous, ...serialized };
|
|
1093
|
+
if (cfg.credentialRef) {
|
|
1094
|
+
delete next.access_credential;
|
|
1095
|
+
delete next.oauth_credential_ref;
|
|
1096
|
+
delete next.api_key;
|
|
1097
|
+
} else if (cfg.accessCredential !== void 0) {
|
|
1098
|
+
delete next.credential_ref;
|
|
1099
|
+
delete next.oauth_credential_ref;
|
|
1100
|
+
delete next.api_key;
|
|
1101
|
+
} else if (cfg.oauthCredential) {
|
|
1102
|
+
delete next.access_credential;
|
|
1103
|
+
next.api_key = null;
|
|
1104
|
+
} else {
|
|
1105
|
+
delete next.access_credential;
|
|
1106
|
+
delete next.oauth_credential_ref;
|
|
1107
|
+
}
|
|
1108
|
+
if (options.omitApiKey) {
|
|
1109
|
+
delete next.api_key;
|
|
1110
|
+
for (const field of ["access_credential", "credential_ref", "oauth_credential_ref"]) {
|
|
1111
|
+
if (Object.hasOwn(previous, field)) next[field] = previous[field];
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (next.credential_ref || Object.hasOwn(next, "access_credential") || next.oauth_credential_ref || cfg.authContext?.managedCredentials) delete next.base_url;
|
|
1115
|
+
const onDisk = {
|
|
1116
|
+
...validRaw,
|
|
1117
|
+
version: 2,
|
|
1118
|
+
agents: {
|
|
1119
|
+
...existingAgents,
|
|
1120
|
+
[agent]: next
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
delete onDisk.default_agent;
|
|
1124
|
+
secureAtomicWrite(target, JSON.stringify(onDisk, null, 2) + "\n");
|
|
1125
|
+
});
|
|
1059
1126
|
}
|
|
1060
1127
|
function configuredAgents(path) {
|
|
1061
1128
|
const target = path ?? defaultConfigPath();
|
|
@@ -1075,21 +1142,23 @@ function hasConfiguredAgent(agent, path) {
|
|
|
1075
1142
|
}
|
|
1076
1143
|
function updateConfiguredDebug(agent, configured, path) {
|
|
1077
1144
|
const target = path ?? defaultConfigPath();
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1145
|
+
return withAuthConfigLock(target, () => {
|
|
1146
|
+
const raw = readRaw(target);
|
|
1147
|
+
if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
const agents = { ...raw.agents };
|
|
1151
|
+
const profile = agents[agent];
|
|
1152
|
+
if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
agents[agent] = { ...profile, debug: configured };
|
|
1156
|
+
secureAtomicWrite(
|
|
1157
|
+
target,
|
|
1158
|
+
JSON.stringify({ ...raw, agents }, null, 2) + "\n"
|
|
1159
|
+
);
|
|
1160
|
+
return true;
|
|
1161
|
+
});
|
|
1093
1162
|
}
|
|
1094
1163
|
function writeInstalledPkgVersion(version, path) {
|
|
1095
1164
|
const target = path ?? defaultConfigPath();
|
|
@@ -1120,11 +1189,11 @@ export {
|
|
|
1120
1189
|
detectInstalledAgents,
|
|
1121
1190
|
agentFromEnv,
|
|
1122
1191
|
agentFromArgvWithFallback,
|
|
1123
|
-
secureAtomicWrite,
|
|
1124
1192
|
defaultConfigPath,
|
|
1125
1193
|
configDir,
|
|
1126
1194
|
DEFAULT_BASE_URL,
|
|
1127
1195
|
DEFAULT_USER_ID,
|
|
1196
|
+
inspectConfig,
|
|
1128
1197
|
load,
|
|
1129
1198
|
removeAgent,
|
|
1130
1199
|
save,
|
|
@@ -418,6 +418,11 @@ function isCiEnvironment(env = process.env) {
|
|
|
418
418
|
function shouldUseUpdateNotifier(argv, options = {}) {
|
|
419
419
|
const env = options.env ?? process.env;
|
|
420
420
|
if (options.installMethod !== "npm") return false;
|
|
421
|
+
if (argv.length === 0 || argv[0] === "help") return false;
|
|
422
|
+
for (const argument of argv) {
|
|
423
|
+
if (argument === "--") break;
|
|
424
|
+
if (argument === "-h" || argument === "--help") return false;
|
|
425
|
+
}
|
|
421
426
|
if (env.CTXDB_DISABLE_UPDATE_CHECK === "1") return false;
|
|
422
427
|
if (isCiEnvironment(env)) return false;
|
|
423
428
|
if (argv.some((argument) => argument === "--json" || argument.startsWith("--json="))) {
|