@aliyunrds/ctxdb 1.0.8-beta.2 → 1.0.8-beta.4
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 +47 -10
- package/dist/{chunk-X75B57M2.js → chunk-5DGRNP55.js} +1 -1
- package/dist/{chunk-R67JELM7.js → chunk-5ITT5GSP.js} +3 -2
- package/dist/{chunk-5ZSZMN2T.js → chunk-6MEWI73Y.js} +2 -2
- package/dist/{chunk-LYHGJOHM.js → chunk-76AXXLVE.js} +4 -3
- package/dist/{chunk-4FCZ2TZM.js → chunk-O6MKQ2I3.js} +1 -1
- package/dist/{chunk-ULAWTBBC.js → chunk-OEZM2ZYA.js} +1 -1
- package/dist/{chunk-VIG4SYLU.js → chunk-PI2SUS3M.js} +1 -1
- package/dist/{chunk-DMS5YHIK.js → chunk-SVO6H62S.js} +69 -28
- package/dist/{chunk-H33NUAHP.js → chunk-WJTV7JNR.js} +13 -6
- package/dist/{chunk-CULTDVI2.js → chunk-ZW7YXNJ6.js} +1 -1
- package/dist/cli/main.js +303 -81
- package/dist/hooks/hermes-post-llm-call.js +9 -7
- package/dist/hooks/hermes-pre-llm-call.js +12 -11
- package/dist/hooks/pre-tool-use.js +1 -1
- package/dist/hooks/session-start.js +11 -10
- package/dist/hooks/stop.js +9 -7
- package/dist/hooks/user-prompt-submit.js +11 -10
- package/dist/opencode/index.js +630 -41
- package/dist/workers/version-check.js +3 -3
- package/package.json +2 -2
package/dist/cli/main.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
uploadFile,
|
|
17
17
|
uploadText,
|
|
18
18
|
validateLocalFile
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-ZW7YXNJ6.js";
|
|
20
20
|
import {
|
|
21
21
|
AGENT_CHOICES,
|
|
22
22
|
CtxdbError,
|
|
@@ -32,27 +32,30 @@ import {
|
|
|
32
32
|
agentHomeDirs,
|
|
33
33
|
agentPlatformSupport,
|
|
34
34
|
configuredAgents,
|
|
35
|
+
defaultConfigPath,
|
|
35
36
|
detectInstalledAgents,
|
|
36
37
|
hasConfiguredAgent,
|
|
37
38
|
inspectAgentHomes,
|
|
38
39
|
isAgentSlug,
|
|
39
40
|
isBuiltinAgent,
|
|
40
|
-
|
|
41
|
+
isDataApiReady,
|
|
41
42
|
load,
|
|
42
43
|
removeAgent,
|
|
44
|
+
resolveDataApiCredential,
|
|
43
45
|
save,
|
|
46
|
+
secureAtomicWrite,
|
|
44
47
|
updateConfiguredDebug,
|
|
45
48
|
writeInstalledPkgVersion
|
|
46
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-SVO6H62S.js";
|
|
47
50
|
import {
|
|
48
51
|
beginUpdateNotification,
|
|
49
52
|
completeUpdateNotification,
|
|
50
53
|
runSelfUpdate
|
|
51
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-OEZM2ZYA.js";
|
|
52
55
|
import {
|
|
53
56
|
DISTRIBUTION_MANIFEST
|
|
54
|
-
} from "../chunk-
|
|
55
|
-
import "../chunk-
|
|
57
|
+
} from "../chunk-5ITT5GSP.js";
|
|
58
|
+
import "../chunk-PI2SUS3M.js";
|
|
56
59
|
|
|
57
60
|
// src/cli/util.ts
|
|
58
61
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -275,6 +278,8 @@ function formatStatusResult(r) {
|
|
|
275
278
|
["base_url", r.base_url],
|
|
276
279
|
["user_id", r.user_id],
|
|
277
280
|
["api_key", r.api_key_set ? "set" : "not set"],
|
|
281
|
+
["access_token", r.access_token_set ? "set" : "not set"],
|
|
282
|
+
["credential_type", r.credential_type ?? "none"],
|
|
278
283
|
["auto_capture", r.auto_capture],
|
|
279
284
|
["auto_recall", r.auto_recall],
|
|
280
285
|
["top_k", r.top_k],
|
|
@@ -323,15 +328,16 @@ function agentFromFlags(flags) {
|
|
|
323
328
|
function buildContext(args) {
|
|
324
329
|
const agent = agentFromFlags(args?.flags ?? {});
|
|
325
330
|
const cfg = load({ agent });
|
|
326
|
-
|
|
327
|
-
|
|
331
|
+
const credential = resolveDataApiCredential(cfg);
|
|
332
|
+
if (!cfg.baseUrl || !credential) {
|
|
333
|
+
const hint = agent === "default" ? "run `ctxdb setup --api-key=<key> --base-url=<url>` or set CTXDB_ACCESS_TOKEN / CTXDB_API_KEY / CTXDB_BASE_URL env vars" : `run \`ctxdb setup --agent ${agent}\` or set CTXDB_ACCESS_TOKEN / CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID env vars`;
|
|
328
334
|
process.stderr.write(`config incomplete for agent ${agent}: ${hint}
|
|
329
335
|
`);
|
|
330
336
|
process.exit(2);
|
|
331
337
|
}
|
|
332
338
|
const client = new HttpClient({
|
|
333
339
|
baseUrl: cfg.baseUrl,
|
|
334
|
-
|
|
340
|
+
credential
|
|
335
341
|
});
|
|
336
342
|
return { cfg, client, agent };
|
|
337
343
|
}
|
|
@@ -468,11 +474,14 @@ async function runSetup(options) {
|
|
|
468
474
|
}
|
|
469
475
|
const cfg = load({ agent });
|
|
470
476
|
const persistedCredentialConfig = options.persistCredential === false ? load({ agent, env: {}, managedCredentials: false }) : null;
|
|
471
|
-
if (options.apiKey)
|
|
477
|
+
if (options.apiKey) {
|
|
478
|
+
cfg.apiKey = options.apiKey;
|
|
479
|
+
cfg.oauthCredential = void 0;
|
|
480
|
+
}
|
|
472
481
|
if (options.baseUrl) cfg.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
473
482
|
else if (!cfg.baseUrl) cfg.baseUrl = DEFAULT_BASE_URL;
|
|
474
483
|
cfg.userId = options.userId ?? cfg.userId ?? DEFAULT_USER_ID;
|
|
475
|
-
if (!cfg
|
|
484
|
+
if (!isDataApiReady(cfg)) {
|
|
476
485
|
steps.push({
|
|
477
486
|
step: "write-config",
|
|
478
487
|
ok: false,
|
|
@@ -492,7 +501,7 @@ async function runSetup(options) {
|
|
|
492
501
|
ok: true,
|
|
493
502
|
detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
|
|
494
503
|
});
|
|
495
|
-
if (isBuiltinAgent(agent)) {
|
|
504
|
+
if (isBuiltinAgent(agent) && !cfg.oauthCredential) {
|
|
496
505
|
const hasDefault = configuredAgents().includes("default");
|
|
497
506
|
if (!hasDefault) {
|
|
498
507
|
save(persistedCfg, void 0, { agent: "default", omitApiKey });
|
|
@@ -716,7 +725,7 @@ async function runSetup(options) {
|
|
|
716
725
|
}
|
|
717
726
|
}
|
|
718
727
|
if (validate) {
|
|
719
|
-
if (!cfg
|
|
728
|
+
if (!isDataApiReady(cfg)) {
|
|
720
729
|
steps.push({
|
|
721
730
|
step: "validate-ping",
|
|
722
731
|
ok: false,
|
|
@@ -724,7 +733,10 @@ async function runSetup(options) {
|
|
|
724
733
|
});
|
|
725
734
|
} else {
|
|
726
735
|
try {
|
|
727
|
-
const client = new HttpClient({
|
|
736
|
+
const client = new HttpClient({
|
|
737
|
+
baseUrl: cfg.baseUrl,
|
|
738
|
+
credential: resolveDataApiCredential(cfg)
|
|
739
|
+
});
|
|
728
740
|
await client.get("/v1/ping/");
|
|
729
741
|
steps.push({ step: "validate-ping", ok: true });
|
|
730
742
|
} catch (err) {
|
|
@@ -811,8 +823,8 @@ async function runSetupAll(options = {}, dependencies = {}) {
|
|
|
811
823
|
}
|
|
812
824
|
for (const agent of detection.selected) {
|
|
813
825
|
const existing = configSnapshot.get(agent);
|
|
814
|
-
const apiKey = options.apiKey ?? existing.apiKey;
|
|
815
|
-
if (!apiKey) {
|
|
826
|
+
const apiKey = options.apiKey ?? (existing.oauthCredential ? void 0 : existing.apiKey ?? void 0);
|
|
827
|
+
if (!apiKey && !isDataApiReady(existing)) {
|
|
816
828
|
agents[agent] = missingApiKeyResult(agent);
|
|
817
829
|
continue;
|
|
818
830
|
}
|
|
@@ -2090,12 +2102,13 @@ FLAGS
|
|
|
2090
2102
|
`;
|
|
2091
2103
|
async function inspectStatus(agent) {
|
|
2092
2104
|
const cfg = load({ agent });
|
|
2093
|
-
const
|
|
2105
|
+
const credential = resolveDataApiCredential(cfg);
|
|
2106
|
+
const complete = Boolean(cfg.baseUrl && credential);
|
|
2094
2107
|
let connected = false;
|
|
2095
2108
|
let pingError = null;
|
|
2096
|
-
if (complete) {
|
|
2109
|
+
if (complete && credential) {
|
|
2097
2110
|
try {
|
|
2098
|
-
const client = new HttpClient({ baseUrl: cfg.baseUrl,
|
|
2111
|
+
const client = new HttpClient({ baseUrl: cfg.baseUrl, credential });
|
|
2099
2112
|
await client.get("/v1/ping/");
|
|
2100
2113
|
connected = true;
|
|
2101
2114
|
} catch (err) {
|
|
@@ -2110,6 +2123,8 @@ async function inspectStatus(agent) {
|
|
|
2110
2123
|
base_url: cfg.baseUrl,
|
|
2111
2124
|
user_id: cfg.userId,
|
|
2112
2125
|
api_key_set: Boolean(cfg.apiKey),
|
|
2126
|
+
access_token_set: Boolean(process.env.CTXDB_ACCESS_TOKEN?.trim()),
|
|
2127
|
+
credential_type: credential?.type ?? null,
|
|
2113
2128
|
auto_capture: cfg.autoCapture,
|
|
2114
2129
|
auto_recall: cfg.autoRecall,
|
|
2115
2130
|
top_k: cfg.topK,
|
|
@@ -2192,17 +2207,217 @@ ${hint}
|
|
|
2192
2207
|
async function ping(args) {
|
|
2193
2208
|
const agent = agentFromFlags(args.flags);
|
|
2194
2209
|
const cfg = load({ agent });
|
|
2195
|
-
|
|
2210
|
+
const credential = resolveDataApiCredential(cfg);
|
|
2211
|
+
if (!cfg.baseUrl || !credential) {
|
|
2196
2212
|
fail(
|
|
2197
|
-
`config incomplete for agent ${agent} \u2014 run \`ctxdb setup --agent ${agent}\` or set CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID`
|
|
2213
|
+
`config incomplete for agent ${agent} \u2014 run \`ctxdb setup --agent ${agent}\` or set CTXDB_ACCESS_TOKEN / CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID`
|
|
2198
2214
|
);
|
|
2199
2215
|
}
|
|
2200
|
-
const client = new HttpClient({ baseUrl: cfg.baseUrl,
|
|
2216
|
+
const client = new HttpClient({ baseUrl: cfg.baseUrl, credential });
|
|
2201
2217
|
const resp = await client.get("/v1/ping/");
|
|
2202
2218
|
printResult(resp, !!args.flags.json);
|
|
2203
2219
|
return 0;
|
|
2204
2220
|
}
|
|
2205
2221
|
|
|
2222
|
+
// src/cli/device-auth-cli.ts
|
|
2223
|
+
import { dirname as dirname3, join as join2 } from "path";
|
|
2224
|
+
import { CoreDeviceClient, normalizeCoreOrigin as normalizeCoreOrigin2, OAuthSessionStore } from "@aliyunrds/ctxdb-shared";
|
|
2225
|
+
|
|
2226
|
+
// src/lib/oauth-login-config.ts
|
|
2227
|
+
import { closeSync, existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2228
|
+
import { dirname as dirname2 } from "path";
|
|
2229
|
+
import { setTimeout as delay } from "timers/promises";
|
|
2230
|
+
import { normalizeCoreOrigin, oauthProfile } from "@aliyunrds/ctxdb-shared";
|
|
2231
|
+
function read(path) {
|
|
2232
|
+
if (!existsSync2(path)) return { version: 2, agents: {} };
|
|
2233
|
+
let raw;
|
|
2234
|
+
try {
|
|
2235
|
+
raw = JSON.parse(readFileSync2(path, "utf8"));
|
|
2236
|
+
} catch {
|
|
2237
|
+
throw new Error("Invalid ctxdb configuration; login did not overwrite it.");
|
|
2238
|
+
}
|
|
2239
|
+
if (!raw || raw.version !== 2 || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
2240
|
+
throw new Error("Unsupported ctxdb configuration; login did not overwrite it.");
|
|
2241
|
+
}
|
|
2242
|
+
return raw;
|
|
2243
|
+
}
|
|
2244
|
+
function selectedOAuthProfile(path, agent) {
|
|
2245
|
+
const section = read(path).agents[agent];
|
|
2246
|
+
return oauthProfile(section?.oauth_credential_ref, path, agent, section?.base_url);
|
|
2247
|
+
}
|
|
2248
|
+
async function commitOAuthProfile(path, agent, reference, baseUrl) {
|
|
2249
|
+
oauthProfile(reference, path, agent, baseUrl);
|
|
2250
|
+
mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
|
|
2251
|
+
const lock = path + ".oauth-config.lock", deadline = Date.now() + 1e4;
|
|
2252
|
+
let fd;
|
|
2253
|
+
while (true) {
|
|
2254
|
+
try {
|
|
2255
|
+
fd = openSync(lock, "wx", 384);
|
|
2256
|
+
break;
|
|
2257
|
+
} catch (error) {
|
|
2258
|
+
if (error.code !== "EEXIST") throw new Error("Cannot lock OAuth profile configuration.");
|
|
2259
|
+
if (Date.now() >= deadline) throw new Error("OAuth profile configuration is locked; recover a crashed login's config lock before retrying.");
|
|
2260
|
+
await delay(25);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
try {
|
|
2264
|
+
const raw = read(path), section = raw.agents[agent] ?? {};
|
|
2265
|
+
if (typeof section !== "object" || Array.isArray(section)) throw new Error("Invalid agent profile; login did not overwrite it.");
|
|
2266
|
+
const previous = oauthProfile(section.oauth_credential_ref, path, agent, section.base_url);
|
|
2267
|
+
raw.agents[agent] = { ...section, oauth_credential_ref: reference, base_url: normalizeCoreOrigin(baseUrl) };
|
|
2268
|
+
delete raw.agents[agent].api_key;
|
|
2269
|
+
secureAtomicWrite(path, JSON.stringify(raw, null, 2) + "\n");
|
|
2270
|
+
return previous;
|
|
2271
|
+
} finally {
|
|
2272
|
+
closeSync(fd);
|
|
2273
|
+
unlinkSync2(lock);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
function consoleAuthorizationBase(path, core) {
|
|
2277
|
+
if (!existsSync2(path)) return void 0;
|
|
2278
|
+
try {
|
|
2279
|
+
const raw = JSON.parse(readFileSync2(path, "utf8"));
|
|
2280
|
+
if (raw.version !== 1 || !Array.isArray(raw.environments) || raw.environments.length > 32) throw new Error();
|
|
2281
|
+
const matches = raw.environments.filter((entry) => normalizeCoreOrigin(entry.core_url) === core);
|
|
2282
|
+
if (matches.length > 1) throw new Error();
|
|
2283
|
+
if (!matches.length) return void 0;
|
|
2284
|
+
const url = new URL(matches[0].authorization_url);
|
|
2285
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error();
|
|
2286
|
+
return url.toString();
|
|
2287
|
+
} catch {
|
|
2288
|
+
throw new Error("Invalid local device-environments.json. No authorization request was created.");
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// src/cli/device-auth-cli.ts
|
|
2293
|
+
var usage = "ctxdb login [--agent <name>] [--server <core-origin>] [--device-code] [--no-browser] [--json]\n";
|
|
2294
|
+
function createPublicAuthCommands(dependencies = {}) {
|
|
2295
|
+
const env = () => dependencies.env ?? process.env;
|
|
2296
|
+
const stdout = dependencies.stdout ?? ((s) => {
|
|
2297
|
+
process.stdout.write(s);
|
|
2298
|
+
});
|
|
2299
|
+
const stderr = dependencies.stderr ?? ((s) => {
|
|
2300
|
+
process.stderr.write(s);
|
|
2301
|
+
});
|
|
2302
|
+
const client = dependencies.client ?? ((url) => new CoreDeviceClient(url));
|
|
2303
|
+
const output = (value, json) => stdout(json ? JSON.stringify(value) + "\n" : String(value) + "\n");
|
|
2304
|
+
function options(args, login = false) {
|
|
2305
|
+
const allowed = ["agent", "json", "help", "h", ...login ? ["server", "device-code", "no-browser"] : []];
|
|
2306
|
+
if (args.positional.length || Object.keys(args.flags).some((k) => !allowed.includes(k))) throw new Error("Unsupported Device login option.");
|
|
2307
|
+
for (const flag of ["json", "help", "h", "device-code", "no-browser"]) {
|
|
2308
|
+
if (args.flags[flag] !== void 0 && args.flags[flag] !== true) throw new Error("Invalid boolean Device login option.");
|
|
2309
|
+
}
|
|
2310
|
+
const agent = args.flags.agent ?? agentFromEnv(env());
|
|
2311
|
+
if (!isAgentSlug(agent)) throw new Error("Invalid agent profile.");
|
|
2312
|
+
const path = defaultConfigPath(env());
|
|
2313
|
+
return { agent, path, json: args.flags.json === true };
|
|
2314
|
+
}
|
|
2315
|
+
async function revoke(profile) {
|
|
2316
|
+
return new OAuthSessionStore(profile.directory).logout(profile.reference, profile.profile, client(profile.baseUrl));
|
|
2317
|
+
}
|
|
2318
|
+
return {
|
|
2319
|
+
async login(args) {
|
|
2320
|
+
if (args.flags.help || args.flags.h) {
|
|
2321
|
+
stdout(usage);
|
|
2322
|
+
return 0;
|
|
2323
|
+
}
|
|
2324
|
+
const { agent, path, json } = options(args, true);
|
|
2325
|
+
if (["CTXDB_API_KEY", "CTXDB_ACCESS_TOKEN", "CTXDB_BASE_URL"].some((k) => env()[k])) {
|
|
2326
|
+
throw new Error("Unset CTXDB_API_KEY, CTXDB_ACCESS_TOKEN and CTXDB_BASE_URL before managed login; use --server to select Core.");
|
|
2327
|
+
}
|
|
2328
|
+
if (args.flags.server !== void 0 && typeof args.flags.server !== "string") throw new Error("--server requires a Core origin.");
|
|
2329
|
+
const previous = selectedOAuthProfile(path, agent);
|
|
2330
|
+
const baseUrl = normalizeCoreOrigin2(args.flags.server ?? previous?.baseUrl ?? DEFAULT_BASE_URL);
|
|
2331
|
+
const consoleBase = consoleAuthorizationBase(join2(dirname3(path), "device-environments.json"), baseUrl);
|
|
2332
|
+
const connection = client(baseUrl), controller = new AbortController();
|
|
2333
|
+
const interrupt = () => controller.abort();
|
|
2334
|
+
process.once("SIGINT", interrupt);
|
|
2335
|
+
try {
|
|
2336
|
+
const session = await connection.login({ signal: controller.signal, onPrompt: (prompt) => {
|
|
2337
|
+
const url = consoleBase ? new URL(consoleBase) : void 0;
|
|
2338
|
+
url?.searchParams.set("user_code", prompt.userCode);
|
|
2339
|
+
const safe = {
|
|
2340
|
+
event: "device_authorization",
|
|
2341
|
+
user_code: prompt.userCode,
|
|
2342
|
+
request_id: prompt.requestId,
|
|
2343
|
+
expires_in: prompt.expiresIn,
|
|
2344
|
+
...url ? { authorization_url: url.toString() } : {}
|
|
2345
|
+
};
|
|
2346
|
+
stderr(json ? JSON.stringify(safe) + "\n" : `User code: ${prompt.userCode}
|
|
2347
|
+
${url ? url.toString() : "Console URL is not configured. Complete authorization with the independent POP test client."}
|
|
2348
|
+
`);
|
|
2349
|
+
} });
|
|
2350
|
+
const store = new OAuthSessionStore(join2(dirname3(path), "oauth-credentials"));
|
|
2351
|
+
const reference = store.saveLogin(agent, session);
|
|
2352
|
+
let replaced;
|
|
2353
|
+
try {
|
|
2354
|
+
replaced = await commitOAuthProfile(path, agent, reference, baseUrl);
|
|
2355
|
+
} catch (error) {
|
|
2356
|
+
await store.logout(reference, agent, connection);
|
|
2357
|
+
throw error;
|
|
2358
|
+
}
|
|
2359
|
+
if (replaced) {
|
|
2360
|
+
try {
|
|
2361
|
+
if (!(await revoke(replaced)).remoteRevoked) stderr("New login saved; old session could not be revoked remotely.\n");
|
|
2362
|
+
} catch {
|
|
2363
|
+
stderr("New login saved; old credential cleanup needs attention.\n");
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
output(json ? {
|
|
2367
|
+
ok: true,
|
|
2368
|
+
agent,
|
|
2369
|
+
workspace_id: session.workspaceId,
|
|
2370
|
+
member_id: session.memberId,
|
|
2371
|
+
scope: session.scope,
|
|
2372
|
+
client_id: session.clientId,
|
|
2373
|
+
account_display: session.accountDisplay,
|
|
2374
|
+
authorization_method: "device-code"
|
|
2375
|
+
} : `Logged in for ${agent}: Workspace ${session.workspaceId}, Member ${session.memberId}, scope ${session.scope}. Run ctxdb setup --agent ${agent} separately.`, json);
|
|
2376
|
+
return 0;
|
|
2377
|
+
} finally {
|
|
2378
|
+
process.removeListener("SIGINT", interrupt);
|
|
2379
|
+
}
|
|
2380
|
+
},
|
|
2381
|
+
async status(args) {
|
|
2382
|
+
if (args.flags.help || args.flags.h) {
|
|
2383
|
+
stdout("ctxdb auth status [--agent <name>] [--json]\n");
|
|
2384
|
+
return 0;
|
|
2385
|
+
}
|
|
2386
|
+
const { agent, path, json } = options(args);
|
|
2387
|
+
const profile = selectedOAuthProfile(path, agent);
|
|
2388
|
+
const state = profile ? new OAuthSessionStore(profile.directory).status(profile.reference, agent) : { state: "missing" };
|
|
2389
|
+
const overridden = Boolean(env().CTXDB_ACCESS_TOKEN || env().CTXDB_API_KEY);
|
|
2390
|
+
const ok = state.state === "ready";
|
|
2391
|
+
output(json ? { ok, agent, ...state, environment_credential_override: overridden } : `${agent}: ${state.state}${overridden ? " (environment credential takes precedence for DATA requests)" : ""}`, json);
|
|
2392
|
+
return ok ? 0 : 1;
|
|
2393
|
+
},
|
|
2394
|
+
async logout(args) {
|
|
2395
|
+
if (args.flags.help || args.flags.h) {
|
|
2396
|
+
stdout("ctxdb logout [--agent <name>] [--json]\n");
|
|
2397
|
+
return 0;
|
|
2398
|
+
}
|
|
2399
|
+
const { agent, path, json } = options(args);
|
|
2400
|
+
const profile = selectedOAuthProfile(path, agent);
|
|
2401
|
+
if (!profile) {
|
|
2402
|
+
output(json ? { ok: true, agent, local_cleared: true, remote_revoked: false, status: "no_managed_session" } : `${agent}: no managed session.`, json);
|
|
2403
|
+
return 0;
|
|
2404
|
+
}
|
|
2405
|
+
const store = new OAuthSessionStore(profile.directory);
|
|
2406
|
+
if (store.status(profile.reference, agent).state === "logged_out") {
|
|
2407
|
+
output(json ? { ok: true, agent, local_cleared: true, remote_revoked: false, status: "already_logged_out" } : `${agent}: already logged out locally.`, json);
|
|
2408
|
+
return 0;
|
|
2409
|
+
}
|
|
2410
|
+
const result = await revoke(profile);
|
|
2411
|
+
output(json ? { ok: result.remoteRevoked, agent, local_cleared: true, remote_revoked: result.remoteRevoked } : `${agent}: local credential cleared; ${result.remoteRevoked ? "remote revoke completed" : "remote revoke failed or could not be confirmed"}.`, json);
|
|
2412
|
+
return result.remoteRevoked ? 0 : 1;
|
|
2413
|
+
}
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
var commands = createPublicAuthCommands();
|
|
2417
|
+
var deviceLogin = commands.login;
|
|
2418
|
+
var deviceAuthStatus = commands.status;
|
|
2419
|
+
var deviceLogout = commands.logout;
|
|
2420
|
+
|
|
2206
2421
|
// src/cli/setup-cli.ts
|
|
2207
2422
|
var HELP = `ctxdb setup \u2014 configure an agent
|
|
2208
2423
|
|
|
@@ -2781,12 +2996,12 @@ function parseFileUpdate(args) {
|
|
|
2781
2996
|
}
|
|
2782
2997
|
fail(UPDATE_FILE_USAGE, 2);
|
|
2783
2998
|
}
|
|
2784
|
-
function shouldWaitForIngest(args,
|
|
2999
|
+
function shouldWaitForIngest(args, usage2) {
|
|
2785
3000
|
const waitRequested = args.flags.wait !== void 0;
|
|
2786
3001
|
const noWaitRequested = args.flags["no-wait"] !== void 0;
|
|
2787
3002
|
if (waitRequested && noWaitRequested) {
|
|
2788
3003
|
fail(`cannot use --wait and --no-wait together
|
|
2789
|
-
${
|
|
3004
|
+
${usage2}`, 2);
|
|
2790
3005
|
}
|
|
2791
3006
|
return waitRequested;
|
|
2792
3007
|
}
|
|
@@ -3396,130 +3611,130 @@ function stringFlag(args, name) {
|
|
|
3396
3611
|
const value = args.flags[name];
|
|
3397
3612
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3398
3613
|
}
|
|
3399
|
-
function requireIdFlag(args, name,
|
|
3614
|
+
function requireIdFlag(args, name, usage2) {
|
|
3400
3615
|
const value = stringFlag(args, name);
|
|
3401
3616
|
if (value === void 0) fail(`--${name} is required
|
|
3402
|
-
${
|
|
3617
|
+
${usage2}`, 2);
|
|
3403
3618
|
return value;
|
|
3404
3619
|
}
|
|
3405
|
-
function requireTemplateIdPositional(args,
|
|
3620
|
+
function requireTemplateIdPositional(args, usage2) {
|
|
3406
3621
|
const templateId = args.positional[0];
|
|
3407
|
-
if (!templateId || templateId.trim() === "") fail(
|
|
3622
|
+
if (!templateId || templateId.trim() === "") fail(usage2, 2);
|
|
3408
3623
|
return templateId;
|
|
3409
3624
|
}
|
|
3410
|
-
function intFlag(args, name,
|
|
3625
|
+
function intFlag(args, name, usage2) {
|
|
3411
3626
|
const raw = stringFlag(args, name);
|
|
3412
3627
|
if (raw === void 0) return void 0;
|
|
3413
3628
|
const n = Number(raw);
|
|
3414
3629
|
if (!Number.isInteger(n) || n < 1) {
|
|
3415
3630
|
fail(`invalid --${name}: ${raw} (expected a positive integer)
|
|
3416
|
-
${
|
|
3631
|
+
${usage2}`, 2);
|
|
3417
3632
|
}
|
|
3418
3633
|
return n;
|
|
3419
3634
|
}
|
|
3420
|
-
function parseIdsFlag(args,
|
|
3635
|
+
function parseIdsFlag(args, usage2) {
|
|
3421
3636
|
const raw = stringFlag(args, "ids");
|
|
3422
3637
|
if (raw === void 0) fail(`--ids is required (comma-separated)
|
|
3423
|
-
${
|
|
3638
|
+
${usage2}`, 2);
|
|
3424
3639
|
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3425
3640
|
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3426
|
-
${
|
|
3641
|
+
${usage2}`, 2);
|
|
3427
3642
|
if (ids.length > BATCH_MAX_ITEMS) {
|
|
3428
3643
|
fail(`--ids accepts at most ${BATCH_MAX_ITEMS} items per request (got ${ids.length})
|
|
3429
|
-
${
|
|
3644
|
+
${usage2}`, 2);
|
|
3430
3645
|
}
|
|
3431
3646
|
return ids;
|
|
3432
3647
|
}
|
|
3433
|
-
function parseResolutions(raw,
|
|
3648
|
+
function parseResolutions(raw, usage2) {
|
|
3434
3649
|
let parsed;
|
|
3435
3650
|
try {
|
|
3436
3651
|
parsed = JSON.parse(raw);
|
|
3437
3652
|
} catch {
|
|
3438
3653
|
fail(`--resolutions-json is not valid JSON
|
|
3439
|
-
${
|
|
3654
|
+
${usage2}`, 2);
|
|
3440
3655
|
}
|
|
3441
3656
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3442
3657
|
fail(`--resolutions-json must be a non-empty array
|
|
3443
|
-
${
|
|
3658
|
+
${usage2}`, 2);
|
|
3444
3659
|
}
|
|
3445
3660
|
return parsed.map((entry, i) => {
|
|
3446
3661
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3447
3662
|
fail(`--resolutions-json[${i}] must be an object
|
|
3448
|
-
${
|
|
3663
|
+
${usage2}`, 2);
|
|
3449
3664
|
}
|
|
3450
3665
|
const e = entry;
|
|
3451
3666
|
if (typeof e.index !== "number" || !Number.isInteger(e.index) || e.index < 0) {
|
|
3452
3667
|
fail(`--resolutions-json[${i}].index must be a non-negative integer
|
|
3453
|
-
${
|
|
3668
|
+
${usage2}`, 2);
|
|
3454
3669
|
}
|
|
3455
3670
|
if (typeof e.choice !== "string" || e.choice.trim() === "") {
|
|
3456
3671
|
fail(`--resolutions-json[${i}].choice must be a non-empty string
|
|
3457
|
-
${
|
|
3672
|
+
${usage2}`, 2);
|
|
3458
3673
|
}
|
|
3459
3674
|
const out = { index: e.index, choice: e.choice };
|
|
3460
3675
|
if (e.comment !== void 0 && e.comment !== null) {
|
|
3461
3676
|
if (typeof e.comment !== "string") {
|
|
3462
3677
|
fail(`--resolutions-json[${i}].comment must be a string
|
|
3463
|
-
${
|
|
3678
|
+
${usage2}`, 2);
|
|
3464
3679
|
}
|
|
3465
3680
|
out.comment = e.comment;
|
|
3466
3681
|
}
|
|
3467
3682
|
return out;
|
|
3468
3683
|
});
|
|
3469
3684
|
}
|
|
3470
|
-
function buildTemplateUpsertBody(args,
|
|
3685
|
+
function buildTemplateUpsertBody(args, usage2) {
|
|
3471
3686
|
const name = stringFlag(args, "name");
|
|
3472
3687
|
if (name === void 0) fail(`--name is required
|
|
3473
|
-
${
|
|
3688
|
+
${usage2}`, 2);
|
|
3474
3689
|
const sectionsRaw = args.flags["sections-json"];
|
|
3475
3690
|
if (typeof sectionsRaw !== "string" || sectionsRaw.trim() === "") {
|
|
3476
3691
|
fail(`--sections-json is required
|
|
3477
|
-
${
|
|
3692
|
+
${usage2}`, 2);
|
|
3478
3693
|
}
|
|
3479
|
-
const sections = parseSections(sectionsRaw,
|
|
3694
|
+
const sections = parseSections(sectionsRaw, usage2);
|
|
3480
3695
|
const body = { name, sections };
|
|
3481
3696
|
if (args.flags["is-default"] === true) body.is_default = true;
|
|
3482
3697
|
return body;
|
|
3483
3698
|
}
|
|
3484
|
-
function parseSections(raw,
|
|
3699
|
+
function parseSections(raw, usage2) {
|
|
3485
3700
|
let parsed;
|
|
3486
3701
|
try {
|
|
3487
3702
|
parsed = JSON.parse(raw);
|
|
3488
3703
|
} catch {
|
|
3489
3704
|
fail(`--sections-json is not valid JSON
|
|
3490
|
-
${
|
|
3705
|
+
${usage2}`, 2);
|
|
3491
3706
|
}
|
|
3492
3707
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
3493
3708
|
fail(`--sections-json must be a non-empty array
|
|
3494
|
-
${
|
|
3709
|
+
${usage2}`, 2);
|
|
3495
3710
|
}
|
|
3496
3711
|
if (parsed.length > TEMPLATE_SECTIONS_MAX) {
|
|
3497
3712
|
fail(`--sections-json accepts at most ${TEMPLATE_SECTIONS_MAX} sections (got ${parsed.length})
|
|
3498
|
-
${
|
|
3713
|
+
${usage2}`, 2);
|
|
3499
3714
|
}
|
|
3500
3715
|
return parsed.map((entry, i) => {
|
|
3501
3716
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3502
3717
|
fail(`--sections-json[${i}] must be an object
|
|
3503
|
-
${
|
|
3718
|
+
${usage2}`, 2);
|
|
3504
3719
|
}
|
|
3505
3720
|
const e = entry;
|
|
3506
3721
|
if (typeof e.title !== "string" || e.title.trim() === "") {
|
|
3507
3722
|
fail(`--sections-json[${i}].title must be a non-empty string
|
|
3508
|
-
${
|
|
3723
|
+
${usage2}`, 2);
|
|
3509
3724
|
}
|
|
3510
3725
|
if (/[\r\n]/.test(e.title.trim())) {
|
|
3511
3726
|
fail(`--sections-json[${i}].title must not contain line breaks
|
|
3512
|
-
${
|
|
3727
|
+
${usage2}`, 2);
|
|
3513
3728
|
}
|
|
3514
3729
|
if (typeof e.guidance !== "string" || e.guidance.trim() === "") {
|
|
3515
3730
|
fail(`--sections-json[${i}].guidance must be a non-empty string
|
|
3516
|
-
${
|
|
3731
|
+
${usage2}`, 2);
|
|
3517
3732
|
}
|
|
3518
3733
|
const out = { title: e.title, guidance: e.guidance };
|
|
3519
3734
|
if (e.level !== void 0 && e.level !== null) {
|
|
3520
3735
|
if (typeof e.level !== "number" || !Number.isInteger(e.level) || e.level < 1 || e.level > 3) {
|
|
3521
3736
|
fail(`--sections-json[${i}].level must be an integer in 1-3
|
|
3522
|
-
${
|
|
3737
|
+
${usage2}`, 2);
|
|
3523
3738
|
}
|
|
3524
3739
|
out.level = e.level;
|
|
3525
3740
|
}
|
|
@@ -3594,7 +3809,7 @@ function compactTemplateList(data) {
|
|
|
3594
3809
|
}
|
|
3595
3810
|
|
|
3596
3811
|
// src/cli/review-cli.ts
|
|
3597
|
-
import { readFileSync as
|
|
3812
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3598
3813
|
|
|
3599
3814
|
// src/lib/review.ts
|
|
3600
3815
|
function unwrapReviewData(response, operation) {
|
|
@@ -3896,43 +4111,43 @@ function stringValue(args, name) {
|
|
|
3896
4111
|
const value = args.flags[name];
|
|
3897
4112
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3898
4113
|
}
|
|
3899
|
-
function requireId(args, flagName, positionalIndex,
|
|
4114
|
+
function requireId(args, flagName, positionalIndex, usage2) {
|
|
3900
4115
|
const positional = args.positional[positionalIndex]?.trim();
|
|
3901
4116
|
const value = positional !== void 0 && positional !== "" ? positional : stringValue(args, flagName);
|
|
3902
|
-
if (value === void 0) fail(
|
|
4117
|
+
if (value === void 0) fail(usage2, 2);
|
|
3903
4118
|
return value.trim();
|
|
3904
4119
|
}
|
|
3905
|
-
function requireEnumFlag(args, flagName, allowed,
|
|
4120
|
+
function requireEnumFlag(args, flagName, allowed, usage2) {
|
|
3906
4121
|
const value = stringValue(args, flagName);
|
|
3907
|
-
if (value === void 0) fail(
|
|
4122
|
+
if (value === void 0) fail(usage2, 2);
|
|
3908
4123
|
if (!allowed.includes(value)) {
|
|
3909
4124
|
fail(`${flagName} must be one of ${allowed.join("|")}
|
|
3910
|
-
${
|
|
4125
|
+
${usage2}`, 2);
|
|
3911
4126
|
}
|
|
3912
4127
|
return value;
|
|
3913
4128
|
}
|
|
3914
|
-
function positiveIntFlag(args, flagName,
|
|
4129
|
+
function positiveIntFlag(args, flagName, usage2) {
|
|
3915
4130
|
const value = args.flags[flagName];
|
|
3916
4131
|
if (value === void 0) return void 0;
|
|
3917
|
-
if (typeof value !== "string") fail(
|
|
4132
|
+
if (typeof value !== "string") fail(usage2, 2);
|
|
3918
4133
|
const parsed = Number(value);
|
|
3919
4134
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
3920
4135
|
fail(`${flagName} must be a positive integer
|
|
3921
|
-
${
|
|
4136
|
+
${usage2}`, 2);
|
|
3922
4137
|
}
|
|
3923
4138
|
return parsed;
|
|
3924
4139
|
}
|
|
3925
|
-
function readPayloadInput(args, inlineFlag, fileFlag,
|
|
4140
|
+
function readPayloadInput(args, inlineFlag, fileFlag, usage2) {
|
|
3926
4141
|
const inline = stringValue(args, inlineFlag);
|
|
3927
4142
|
const filePath = stringValue(args, fileFlag);
|
|
3928
4143
|
if (inline !== void 0 && filePath !== void 0) {
|
|
3929
4144
|
fail(`cannot use --${inlineFlag} and --${fileFlag} together
|
|
3930
|
-
${
|
|
4145
|
+
${usage2}`, 2);
|
|
3931
4146
|
}
|
|
3932
4147
|
if (inline !== void 0) return inline;
|
|
3933
4148
|
if (filePath !== void 0) {
|
|
3934
4149
|
try {
|
|
3935
|
-
return
|
|
4150
|
+
return readFileSync3(filePath, "utf-8");
|
|
3936
4151
|
} catch (error) {
|
|
3937
4152
|
fail(
|
|
3938
4153
|
`cannot read --${fileFlag} ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3940,17 +4155,17 @@ ${usage}`, 2);
|
|
|
3940
4155
|
);
|
|
3941
4156
|
}
|
|
3942
4157
|
}
|
|
3943
|
-
fail(
|
|
4158
|
+
fail(usage2, 2);
|
|
3944
4159
|
}
|
|
3945
|
-
function parseIdsFlag2(args,
|
|
4160
|
+
function parseIdsFlag2(args, usage2) {
|
|
3946
4161
|
const raw = stringValue(args, "ids");
|
|
3947
|
-
if (raw === void 0) fail(
|
|
4162
|
+
if (raw === void 0) fail(usage2, 2);
|
|
3948
4163
|
const ids = parseIdList(raw);
|
|
3949
4164
|
if (ids.length === 0) fail(`--ids must contain at least one id
|
|
3950
|
-
${
|
|
4165
|
+
${usage2}`, 2);
|
|
3951
4166
|
if (ids.length > BATCH_MAX_ITEMS2) {
|
|
3952
4167
|
fail(`--ids accepts at most ${BATCH_MAX_ITEMS2} ids per request
|
|
3953
|
-
${
|
|
4168
|
+
${usage2}`, 2);
|
|
3954
4169
|
}
|
|
3955
4170
|
return ids;
|
|
3956
4171
|
}
|
|
@@ -4034,7 +4249,7 @@ ${REVIEW_ACTION_USAGE}`, 2);
|
|
|
4034
4249
|
if (editedContent !== void 0) body.edited_content = editedContent;
|
|
4035
4250
|
else if (editedContentFile !== void 0) {
|
|
4036
4251
|
try {
|
|
4037
|
-
body.edited_content =
|
|
4252
|
+
body.edited_content = readFileSync3(editedContentFile, "utf-8");
|
|
4038
4253
|
} catch (error) {
|
|
4039
4254
|
fail(
|
|
4040
4255
|
`cannot read --edited-content-file ${editedContentFile}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -4115,28 +4330,28 @@ async function reviewTemplateList(args) {
|
|
|
4115
4330
|
printResult(out, !!args.flags.json);
|
|
4116
4331
|
return 0;
|
|
4117
4332
|
}
|
|
4118
|
-
function buildTemplateUpsertBody2(args,
|
|
4333
|
+
function buildTemplateUpsertBody2(args, usage2, requireNameAndSections) {
|
|
4119
4334
|
const body = {};
|
|
4120
4335
|
const name = stringValue(args, "name");
|
|
4121
4336
|
if (name !== void 0) {
|
|
4122
4337
|
if (name.length > TEMPLATE_MAX_NAME_LENGTH) {
|
|
4123
4338
|
fail(`--name must be at most ${TEMPLATE_MAX_NAME_LENGTH} characters
|
|
4124
|
-
${
|
|
4339
|
+
${usage2}`, 2);
|
|
4125
4340
|
}
|
|
4126
4341
|
body.name = name;
|
|
4127
4342
|
}
|
|
4128
4343
|
const hasSectionsInput = Object.hasOwn(args.flags, "sections") || Object.hasOwn(args.flags, "sections-file");
|
|
4129
4344
|
if (hasSectionsInput) {
|
|
4130
|
-
const raw = readPayloadInput(args, "sections", "sections-file",
|
|
4345
|
+
const raw = readPayloadInput(args, "sections", "sections-file", usage2);
|
|
4131
4346
|
try {
|
|
4132
4347
|
body.sections = parseTemplateSections(raw);
|
|
4133
4348
|
} catch (error) {
|
|
4134
4349
|
fail(`${error instanceof Error ? error.message : String(error)}
|
|
4135
|
-
${
|
|
4350
|
+
${usage2}`, 2);
|
|
4136
4351
|
}
|
|
4137
4352
|
}
|
|
4138
4353
|
if (requireNameAndSections && (body.name === void 0 || body.sections === void 0)) {
|
|
4139
|
-
fail(
|
|
4354
|
+
fail(usage2, 2);
|
|
4140
4355
|
}
|
|
4141
4356
|
return body;
|
|
4142
4357
|
}
|
|
@@ -4299,7 +4514,12 @@ USAGE
|
|
|
4299
4514
|
ctxdb <command> [args] [--flags]
|
|
4300
4515
|
|
|
4301
4516
|
COMMANDS
|
|
4302
|
-
|
|
4517
|
+
${DISTRIBUTION_MANIFEST.capabilities.deviceFlow ? ` login [--agent <name>] [--scope read_only|read_write|default] [--server <core-origin>]
|
|
4518
|
+
[--device-code] [--no-browser] [--json]
|
|
4519
|
+
Device Flow login only; run setup separately.
|
|
4520
|
+
auth status [--agent <name>] [--json]
|
|
4521
|
+
logout [--agent <name>] [--json]
|
|
4522
|
+
` : ""} setup [--agent <${SUPPORTED_AGENT_CHOICES}> | --all]
|
|
4303
4523
|
[--api-key=K] [--base-url=URL] [--user-id=ID]
|
|
4304
4524
|
[--no-install-skill] [--no-validate] [--json]
|
|
4305
4525
|
Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
|
|
@@ -4474,6 +4694,7 @@ AGENT RESOLUTION (when --agent is omitted):
|
|
|
4474
4694
|
ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
|
|
4475
4695
|
CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
|
|
4476
4696
|
CTXDB_AGENT_ID CTXDB_APP_ID
|
|
4697
|
+
CTXDB_ACCESS_TOKEN Runtime-only Data API Bearer credential; takes precedence over API key
|
|
4477
4698
|
|
|
4478
4699
|
UPDATE CHECK ENV VARS:
|
|
4479
4700
|
CTXDB_DISABLE_UPDATE_CHECK=1
|
|
@@ -4487,6 +4708,7 @@ CONFIG: ~/.ctxdb/ctxdb.json (agents.default / ${SUPPORTED_AGENTS.map((agent) =>
|
|
|
4487
4708
|
LOGS: ~/.ctxdb/logs/ctxdb.log, ~/.ctxdb/logs/recall-trace.jsonl
|
|
4488
4709
|
`;
|
|
4489
4710
|
var ROUTES = {
|
|
4711
|
+
...DISTRIBUTION_MANIFEST.capabilities.deviceFlow ? { login: deviceLogin, logout: deviceLogout, auth: { status: deviceAuthStatus } } : {},
|
|
4490
4712
|
setup,
|
|
4491
4713
|
status,
|
|
4492
4714
|
ping,
|
|
@@ -4591,14 +4813,14 @@ async function dispatch(argv, routes = ROUTES, help = HELP5) {
|
|
|
4591
4813
|
return dispatchGroup([first], route, rest);
|
|
4592
4814
|
}
|
|
4593
4815
|
async function dispatchGroup(path, group, argv) {
|
|
4594
|
-
const
|
|
4816
|
+
const usage2 = `usage: ctxdb ${path.join(" ")} <${Object.keys(group).join("|")}> ...
|
|
4595
4817
|
`;
|
|
4596
4818
|
if (argv.length === 0) {
|
|
4597
|
-
process.stderr.write(
|
|
4819
|
+
process.stderr.write(usage2);
|
|
4598
4820
|
return 2;
|
|
4599
4821
|
}
|
|
4600
4822
|
if (argv[0] === "-h" || argv[0] === "--help") {
|
|
4601
|
-
process.stdout.write(
|
|
4823
|
+
process.stdout.write(usage2);
|
|
4602
4824
|
return 0;
|
|
4603
4825
|
}
|
|
4604
4826
|
const [sub, ...subRest] = argv;
|