@aliyunrds/ctxdb 1.0.2 → 1.0.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.
@@ -0,0 +1,462 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/self-update.ts
4
+ import { spawnSync } from "child_process";
5
+ import { fileURLToPath } from "url";
6
+ import { dirname } from "path";
7
+ import semver from "semver";
8
+ var PACKAGE_NAME = "@aliyunrds/ctxdb";
9
+ var NPM_VIEW_TIMEOUT_MS = 1e4;
10
+ function npmCommand(platform = process.platform) {
11
+ return platform === "win32" ? "npm.cmd" : "npm";
12
+ }
13
+ function ctxdbCommand(platform = process.platform) {
14
+ return platform === "win32" ? "ctxdb.cmd" : "ctxdb";
15
+ }
16
+ function detectInstallMethod() {
17
+ const dir = dirname(fileURLToPath(import.meta.url));
18
+ return dir.includes("/node_modules/") || dir.includes("\\node_modules\\") ? "npm" : "unknown";
19
+ }
20
+ function checkLatestVersion(currentVersion, options = {}) {
21
+ const current = semver.valid(currentVersion);
22
+ if (!current) {
23
+ return {
24
+ latest: null,
25
+ current: currentVersion,
26
+ isNewer: false,
27
+ error: `invalid current package version: ${currentVersion}`
28
+ };
29
+ }
30
+ const platform = options.platform ?? process.platform;
31
+ const run = options.spawnSyncFn ?? spawnSync;
32
+ const result = run(npmCommand(platform), ["view", PACKAGE_NAME, "version"], {
33
+ encoding: "utf-8",
34
+ timeout: options.timeoutMs ?? NPM_VIEW_TIMEOUT_MS,
35
+ stdio: ["ignore", "pipe", "pipe"],
36
+ shell: platform === "win32"
37
+ });
38
+ if (result.status !== 0 || !result.stdout?.trim()) {
39
+ return {
40
+ latest: null,
41
+ current: currentVersion,
42
+ isNewer: false,
43
+ error: spawnFailureMessage(result, "npm view")
44
+ };
45
+ }
46
+ const latest = semver.valid(result.stdout.trim());
47
+ if (!latest) {
48
+ return {
49
+ latest: null,
50
+ current,
51
+ isNewer: false,
52
+ error: `npm view returned an invalid version: ${result.stdout.trim()}`
53
+ };
54
+ }
55
+ return {
56
+ latest,
57
+ current,
58
+ isNewer: isNewerVersion(latest, current)
59
+ };
60
+ }
61
+ function spawnFailureMessage(result, commandLabel) {
62
+ const stderr = result.stderr?.toString().trim();
63
+ if (stderr) return stderr;
64
+ if (result.error) return `${commandLabel} failed: ${result.error.message}`;
65
+ if (result.signal) return `${commandLabel} terminated by signal ${result.signal}`;
66
+ return `${commandLabel} exited with code ${result.status}`;
67
+ }
68
+ function isNewerVersion(candidate, current) {
69
+ const candidateVersion = semver.valid(candidate);
70
+ const currentVersion = semver.valid(current);
71
+ return Boolean(
72
+ candidateVersion && currentVersion && semver.gt(candidateVersion, currentVersion)
73
+ );
74
+ }
75
+ function runSelfUpdate(currentVersion, passthroughArgs = [], options = {}) {
76
+ const method = options.installMethod ?? detectInstallMethod();
77
+ if (method !== "npm") {
78
+ return {
79
+ ok: false,
80
+ updated: false,
81
+ fromVersion: currentVersion,
82
+ error: "ctxdb was not installed via npm. Update manually with your package manager."
83
+ };
84
+ }
85
+ const check = checkLatestVersion(currentVersion);
86
+ if (check.error) {
87
+ return {
88
+ ok: false,
89
+ updated: false,
90
+ fromVersion: currentVersion,
91
+ error: `npm view failed: ${check.error}`
92
+ };
93
+ }
94
+ if (!check.isNewer) {
95
+ process.stderr.write(
96
+ `ctxdb: package already up to date (current v${currentVersion}, latest v${check.latest})
97
+ `
98
+ );
99
+ return { ok: true, updated: false, fromVersion: currentVersion };
100
+ }
101
+ process.stderr.write(`ctxdb: updating ${PACKAGE_NAME} v${currentVersion} \u2192 v${check.latest}...
102
+ `);
103
+ const install = spawnSync(npmCommand(), ["install", "-g", `${PACKAGE_NAME}@${check.latest}`], {
104
+ stdio: "inherit",
105
+ encoding: "utf-8",
106
+ shell: process.platform === "win32"
107
+ });
108
+ if (install.status !== 0) {
109
+ return {
110
+ ok: false,
111
+ updated: false,
112
+ fromVersion: currentVersion,
113
+ error: spawnFailureMessage(install, "npm install -g")
114
+ };
115
+ }
116
+ process.stderr.write(`ctxdb: package updated to v${check.latest}, running setup...
117
+ `);
118
+ const setupArgs = ["update", "--no-self-update", ...passthroughArgs];
119
+ const refresh = spawnSync(ctxdbCommand(), setupArgs, {
120
+ stdio: "inherit",
121
+ encoding: "utf-8",
122
+ shell: process.platform === "win32"
123
+ });
124
+ return {
125
+ ok: refresh.status === 0,
126
+ updated: true,
127
+ fromVersion: currentVersion,
128
+ toVersion: check.latest,
129
+ error: refresh.status !== 0 ? spawnFailureMessage(refresh, "ctxdb setup") : void 0,
130
+ failureHandled: refresh.status !== null && refresh.status !== 0
131
+ };
132
+ }
133
+
134
+ // src/lib/update-notifier.ts
135
+ import { randomUUID } from "crypto";
136
+ import { spawn } from "child_process";
137
+ import {
138
+ mkdirSync,
139
+ readFileSync,
140
+ renameSync,
141
+ rmSync,
142
+ statSync,
143
+ unlinkSync,
144
+ writeFileSync
145
+ } from "fs";
146
+ import { homedir } from "os";
147
+ import { dirname as dirname2, join } from "path";
148
+ import semver2 from "semver";
149
+ var VERSION_CHECK_SCHEMA = 1;
150
+ var SUCCESS_TTL_MS = 24 * 60 * 60 * 1e3;
151
+ var FAILURE_BACKOFF_BASE_MS = 60 * 60 * 1e3;
152
+ var FAILURE_BACKOFF_MAX_MS = 6 * 60 * 60 * 1e3;
153
+ var NOTIFICATION_THROTTLE_MS = 24 * 60 * 60 * 1e3;
154
+ var LOCK_STALE_AFTER_MS = 10 * 60 * 1e3;
155
+ function versionCheckPaths(home = homedir()) {
156
+ const cacheDir = join(home, ".ctxdb", "cache");
157
+ return {
158
+ cacheDir,
159
+ statePath: join(cacheDir, "version-check.json"),
160
+ lockPath: join(cacheDir, "version-check.lock")
161
+ };
162
+ }
163
+ function createInitialVersionCheckState(currentVersion) {
164
+ return {
165
+ schemaVersion: VERSION_CHECK_SCHEMA,
166
+ currentVersion,
167
+ latestVersion: null,
168
+ lastSuccessAt: null,
169
+ lastFailureAt: null,
170
+ nextCheckAt: 0,
171
+ consecutiveFailures: 0,
172
+ lastNotifiedAt: null,
173
+ lastNotifiedVersion: null
174
+ };
175
+ }
176
+ function isNullableTimestamp(value) {
177
+ return value === null || typeof value === "number" && Number.isFinite(value) && value >= 0;
178
+ }
179
+ function isVersionCheckState(value) {
180
+ if (!value || typeof value !== "object") return false;
181
+ const state = value;
182
+ return state.schemaVersion === VERSION_CHECK_SCHEMA && typeof state.currentVersion === "string" && (state.latestVersion === null || typeof state.latestVersion === "string") && isNullableTimestamp(state.lastSuccessAt) && isNullableTimestamp(state.lastFailureAt) && typeof state.nextCheckAt === "number" && Number.isFinite(state.nextCheckAt) && state.nextCheckAt >= 0 && Number.isInteger(state.consecutiveFailures) && state.consecutiveFailures >= 0 && isNullableTimestamp(state.lastNotifiedAt) && (state.lastNotifiedVersion === null || typeof state.lastNotifiedVersion === "string");
183
+ }
184
+ function readVersionCheckState(statePath) {
185
+ try {
186
+ const parsed = JSON.parse(readFileSync(statePath, "utf8"));
187
+ return isVersionCheckState(parsed) ? parsed : null;
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+ function writeVersionCheckState(statePath, state) {
193
+ const cacheDir = dirname2(statePath);
194
+ const temporaryPath = `${statePath}.${process.pid}.${randomUUID()}.tmp`;
195
+ try {
196
+ mkdirSync(cacheDir, { recursive: true, mode: 448 });
197
+ writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
198
+ `, {
199
+ encoding: "utf8",
200
+ mode: 384,
201
+ flag: "wx"
202
+ });
203
+ renameSync(temporaryPath, statePath);
204
+ return true;
205
+ } catch {
206
+ try {
207
+ rmSync(temporaryPath, { force: true });
208
+ } catch {
209
+ }
210
+ return false;
211
+ }
212
+ }
213
+ function stateForCurrentVersion(state, currentVersion) {
214
+ return state?.currentVersion === currentVersion ? state : createInitialVersionCheckState(currentVersion);
215
+ }
216
+ function isVersionCheckDue(state, now) {
217
+ return now >= state.nextCheckAt;
218
+ }
219
+ function failureBackoffMs(consecutiveFailures) {
220
+ const exponent = Math.max(0, consecutiveFailures - 1);
221
+ return Math.min(
222
+ FAILURE_BACKOFF_BASE_MS * 2 ** exponent,
223
+ FAILURE_BACKOFF_MAX_MS
224
+ );
225
+ }
226
+ function recordVersionCheckSuccess(paths, currentVersion, latestVersion, now = Date.now()) {
227
+ if (!semver2.valid(currentVersion) || !semver2.valid(latestVersion)) return false;
228
+ const state = stateForCurrentVersion(
229
+ readVersionCheckState(paths.statePath),
230
+ currentVersion
231
+ );
232
+ return writeVersionCheckState(paths.statePath, {
233
+ ...state,
234
+ latestVersion,
235
+ lastSuccessAt: now,
236
+ lastFailureAt: null,
237
+ nextCheckAt: now + SUCCESS_TTL_MS,
238
+ consecutiveFailures: 0
239
+ });
240
+ }
241
+ function recordVersionCheckFailure(paths, currentVersion, now = Date.now()) {
242
+ const state = stateForCurrentVersion(
243
+ readVersionCheckState(paths.statePath),
244
+ currentVersion
245
+ );
246
+ const consecutiveFailures = state.consecutiveFailures + 1;
247
+ return writeVersionCheckState(paths.statePath, {
248
+ ...state,
249
+ lastFailureAt: now,
250
+ nextCheckAt: now + failureBackoffMs(consecutiveFailures),
251
+ consecutiveFailures
252
+ });
253
+ }
254
+ function readLock(lockPath) {
255
+ try {
256
+ const parsed = JSON.parse(readFileSync(lockPath, "utf8"));
257
+ if (!parsed || typeof parsed !== "object") return null;
258
+ const lock = parsed;
259
+ if (typeof lock.token !== "string" || typeof lock.createdAt !== "number" || !Number.isFinite(lock.createdAt)) {
260
+ return null;
261
+ }
262
+ return { token: lock.token, createdAt: lock.createdAt };
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+ function lockCreatedAt(lockPath) {
268
+ const lock = readLock(lockPath);
269
+ if (lock) return lock.createdAt;
270
+ try {
271
+ return statSync(lockPath).mtimeMs;
272
+ } catch {
273
+ return null;
274
+ }
275
+ }
276
+ function acquireVersionCheckLock(paths, now = Date.now(), token = randomUUID()) {
277
+ try {
278
+ mkdirSync(paths.cacheDir, { recursive: true, mode: 448 });
279
+ } catch {
280
+ return null;
281
+ }
282
+ for (let attempt = 0; attempt < 2; attempt++) {
283
+ try {
284
+ const lock = { token, createdAt: now };
285
+ writeFileSync(paths.lockPath, `${JSON.stringify(lock)}
286
+ `, {
287
+ encoding: "utf8",
288
+ mode: 384,
289
+ flag: "wx"
290
+ });
291
+ return lock;
292
+ } catch (error) {
293
+ if (error.code !== "EEXIST") return null;
294
+ const createdAt = lockCreatedAt(paths.lockPath);
295
+ if (createdAt === null || now - createdAt < LOCK_STALE_AFTER_MS) return null;
296
+ try {
297
+ unlinkSync(paths.lockPath);
298
+ } catch {
299
+ return null;
300
+ }
301
+ }
302
+ }
303
+ return null;
304
+ }
305
+ function releaseVersionCheckLock(paths, token) {
306
+ const lock = readLock(paths.lockPath);
307
+ if (!lock || lock.token !== token) return false;
308
+ try {
309
+ unlinkSync(paths.lockPath);
310
+ return true;
311
+ } catch {
312
+ return false;
313
+ }
314
+ }
315
+ function getUpdateHintCandidate(state, now = Date.now()) {
316
+ const current = semver2.valid(state.currentVersion);
317
+ const latest = state.latestVersion ? semver2.valid(state.latestVersion) : null;
318
+ if (!current || !latest || !semver2.gt(latest, current)) return null;
319
+ if (state.lastNotifiedVersion === latest && state.lastNotifiedAt !== null && now - state.lastNotifiedAt < NOTIFICATION_THROTTLE_MS) {
320
+ return null;
321
+ }
322
+ return {
323
+ currentVersion: current,
324
+ latestVersion: latest,
325
+ message: `ctxdb: update available v${current} \u2192 v${latest}; run \`ctxdb update\`.
326
+ `
327
+ };
328
+ }
329
+ function markVersionNotified(paths, candidate, now = Date.now()) {
330
+ const state = stateForCurrentVersion(
331
+ readVersionCheckState(paths.statePath),
332
+ candidate.currentVersion
333
+ );
334
+ if (state.latestVersion === null) return false;
335
+ return writeVersionCheckState(paths.statePath, {
336
+ ...state,
337
+ lastNotifiedAt: now,
338
+ lastNotifiedVersion: candidate.latestVersion
339
+ });
340
+ }
341
+ function scheduleVersionCheck(options) {
342
+ const now = options.now ?? Date.now();
343
+ if (!semver2.valid(options.currentVersion)) return false;
344
+ const state = stateForCurrentVersion(
345
+ readVersionCheckState(options.paths.statePath),
346
+ options.currentVersion
347
+ );
348
+ if (!isVersionCheckDue(state, now)) return false;
349
+ const lock = acquireVersionCheckLock(options.paths, now);
350
+ if (!lock) return false;
351
+ const handleLaunchFailure = () => {
352
+ recordVersionCheckFailure(options.paths, options.currentVersion, now);
353
+ releaseVersionCheckLock(options.paths, lock.token);
354
+ };
355
+ try {
356
+ const spawnFn = options.spawnFn ?? spawn;
357
+ const child = spawnFn(
358
+ options.execPath ?? process.execPath,
359
+ [
360
+ options.workerPath,
361
+ `--current-version=${options.currentVersion}`,
362
+ `--state-path=${options.paths.statePath}`,
363
+ `--lock-path=${options.paths.lockPath}`,
364
+ `--lock-token=${lock.token}`
365
+ ],
366
+ {
367
+ detached: true,
368
+ stdio: "ignore",
369
+ windowsHide: true
370
+ }
371
+ );
372
+ child.once("error", handleLaunchFailure);
373
+ child.unref();
374
+ return true;
375
+ } catch {
376
+ handleLaunchFailure();
377
+ return false;
378
+ }
379
+ }
380
+ function isCiEnvironment(env = process.env) {
381
+ const ci = env.CI?.trim().toLowerCase();
382
+ if (ci && ci !== "0" && ci !== "false") return true;
383
+ return [
384
+ "CONTINUOUS_INTEGRATION",
385
+ "GITHUB_ACTIONS",
386
+ "GITLAB_CI",
387
+ "TF_BUILD",
388
+ "JENKINS_URL",
389
+ "BUILDKITE",
390
+ "CIRCLECI",
391
+ "TEAMCITY_VERSION"
392
+ ].some((name) => Boolean(env[name]));
393
+ }
394
+ function shouldUseUpdateNotifier(argv, options = {}) {
395
+ const env = options.env ?? process.env;
396
+ if (options.stderrIsTTY !== true) return false;
397
+ if (options.installMethod !== "npm") return false;
398
+ if (env.CTXDB_DISABLE_UPDATE_CHECK === "1") return false;
399
+ if (isCiEnvironment(env)) return false;
400
+ if (argv.some((argument) => argument === "--json" || argument.startsWith("--json="))) {
401
+ return false;
402
+ }
403
+ const command = argv[0];
404
+ return command !== "update" && command !== "upgrade";
405
+ }
406
+ function beginUpdateNotification(argv, options) {
407
+ try {
408
+ const installMethod = options.installMethod ?? detectInstallMethod();
409
+ if (!shouldUseUpdateNotifier(argv, {
410
+ env: options.env ?? process.env,
411
+ stderrIsTTY: options.stderrIsTTY ?? process.stderr.isTTY,
412
+ installMethod
413
+ })) {
414
+ return null;
415
+ }
416
+ const now = options.now ?? Date.now();
417
+ const paths = options.paths ?? versionCheckPaths(options.home);
418
+ const state = stateForCurrentVersion(
419
+ readVersionCheckState(paths.statePath),
420
+ options.currentVersion
421
+ );
422
+ const candidate = getUpdateHintCandidate(state, now);
423
+ scheduleVersionCheck({
424
+ paths,
425
+ currentVersion: options.currentVersion,
426
+ workerPath: options.workerPath,
427
+ now,
428
+ execPath: options.execPath,
429
+ spawnFn: options.spawnFn
430
+ });
431
+ return { paths, candidate };
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ function completeUpdateNotification(session, exitCode, options = {}) {
437
+ if (!session?.candidate || exitCode !== 0) return false;
438
+ try {
439
+ if (!markVersionNotified(
440
+ session.paths,
441
+ session.candidate,
442
+ options.now ?? Date.now()
443
+ )) {
444
+ return false;
445
+ }
446
+ const writeStderr = options.writeStderr ?? ((message) => process.stderr.write(message));
447
+ writeStderr(session.candidate.message);
448
+ return true;
449
+ } catch {
450
+ return false;
451
+ }
452
+ }
453
+
454
+ export {
455
+ checkLatestVersion,
456
+ runSelfUpdate,
457
+ recordVersionCheckSuccess,
458
+ recordVersionCheckFailure,
459
+ releaseVersionCheckLock,
460
+ beginUpdateNotification,
461
+ completeUpdateNotification
462
+ };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-EUQ3OFCQ.js";
5
+ } from "./chunk-Q4JYST7K.js";
6
6
 
7
7
  // src/lib/user-prompt-submit-compose.ts
8
8
  async function composeUserPromptSubmit(cfg, agent, client, prompt) {
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-EUQ3OFCQ.js";
5
+ } from "./chunk-Q4JYST7K.js";
6
6
  import {
7
7
  debug
8
- } from "./chunk-6FZL67GH.js";
8
+ } from "./chunk-JFTKYEVN.js";
9
9
 
10
10
  // src/lib/warmup-recall.ts
11
11
  import { execSync } from "child_process";