claude-bridge-cli 2.1.0 → 2.2.1
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/lib/bridge.js +59 -3
- package/package.json +1 -1
package/lib/bridge.js
CHANGED
|
@@ -7,6 +7,10 @@ const path = require("node:path");
|
|
|
7
7
|
const crypto = require("node:crypto");
|
|
8
8
|
const os = require("node:os");
|
|
9
9
|
|
|
10
|
+
// Extension builds seen calling this bridge: {version: last_seen_epoch}.
|
|
11
|
+
// Diagnostics only — surfaced on /health (see the x-ext-version note below).
|
|
12
|
+
const EXT_SEEN = {};
|
|
13
|
+
|
|
10
14
|
const running = new Map();
|
|
11
15
|
|
|
12
16
|
function homeDir() {
|
|
@@ -1075,8 +1079,46 @@ function stopSession(sessionId) {
|
|
|
1075
1079
|
|
|
1076
1080
|
// ── HTTP server ──
|
|
1077
1081
|
|
|
1082
|
+
// ── Transcript-retention guard ───────────────────────────────────────────────
|
|
1083
|
+
// Claude Code's `cleanupPeriodDays` DEFAULTS to 30: transcripts older than 30
|
|
1084
|
+
// days are silently DELETED at every CLI startup. On 2026-08-31 this erased
|
|
1085
|
+
// two months-old sessions ("Keycloak-IDP", "IDP-Lucid") from a machine whose
|
|
1086
|
+
// settings had no override — the owner "didn't delete it"; the tool did.
|
|
1087
|
+
// Every bridge start therefore pins a long retention into ~/.claude/settings.json
|
|
1088
|
+
// (create-if-absent, other keys preserved, .bak written on first change).
|
|
1089
|
+
// Opt out with CLAUDE_BRIDGE_NO_RETENTION_PIN=1. NOTE: 0 is NOT a safe value
|
|
1090
|
+
// (older CLIs treated it as "disable transcript writes") — use a big number.
|
|
1091
|
+
function ensureTranscriptRetention() {
|
|
1092
|
+
if (process.env.CLAUDE_BRIDGE_NO_RETENTION_PIN === "1") return;
|
|
1093
|
+
try {
|
|
1094
|
+
const dir = path.join(homeDir(), ".claude");
|
|
1095
|
+
const file = path.join(dir, "settings.json");
|
|
1096
|
+
let settings = {};
|
|
1097
|
+
let existed = false;
|
|
1098
|
+
try {
|
|
1099
|
+
settings = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
1100
|
+
existed = true;
|
|
1101
|
+
} catch (_e) { /* absent or unparseable-empty — treat as new */ }
|
|
1102
|
+
if (typeof settings !== "object" || settings === null) settings = {};
|
|
1103
|
+
const cur = settings.cleanupPeriodDays;
|
|
1104
|
+
if (typeof cur === "number" && cur >= 3650) return; // already pinned
|
|
1105
|
+
if (existed) {
|
|
1106
|
+
try { fs.copyFileSync(file, file + ".bak-retention"); } catch (_e) {}
|
|
1107
|
+
}
|
|
1108
|
+
settings.cleanupPeriodDays = 3650;
|
|
1109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1110
|
+
fs.writeFileSync(file, JSON.stringify(settings, null, 2) + "\n");
|
|
1111
|
+
console.log(`[retention] pinned cleanupPeriodDays=3650 in ${file}`
|
|
1112
|
+
+ (typeof cur === "number" ? ` (was ${cur})` : " (was absent → default 30)"));
|
|
1113
|
+
} catch (e) {
|
|
1114
|
+
// Never block the bridge on this — but say so, silence here is the bug.
|
|
1115
|
+
console.error("[retention] could not pin cleanupPeriodDays:", e.message);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1078
1119
|
function startBridge(config) {
|
|
1079
1120
|
const dd = dataDir();
|
|
1121
|
+
ensureTranscriptRetention();
|
|
1080
1122
|
|
|
1081
1123
|
const server = http.createServer(async (req, res) => {
|
|
1082
1124
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -1095,12 +1137,26 @@ function startBridge(config) {
|
|
|
1095
1137
|
return;
|
|
1096
1138
|
}
|
|
1097
1139
|
|
|
1140
|
+
// Record the extension build making this call. An extension does not
|
|
1141
|
+
// auto-update, so without this "I still see the bug" and "the fix isn't
|
|
1142
|
+
// loaded yet" are indistinguishable from the host side. Diagnostics only.
|
|
1143
|
+
try {
|
|
1144
|
+
const ev = String(req.headers["x-ext-version"] || "").trim().slice(0, 32);
|
|
1145
|
+
if (ev) EXT_SEEN[ev] = Math.floor(Date.now() / 1000);
|
|
1146
|
+
} catch (_e) {}
|
|
1147
|
+
|
|
1098
1148
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
1099
1149
|
let m;
|
|
1100
1150
|
|
|
1101
1151
|
// Health
|
|
1102
1152
|
if (url.pathname === "/health") {
|
|
1103
|
-
send(200, {
|
|
1153
|
+
send(200, {
|
|
1154
|
+
ok: true,
|
|
1155
|
+
ext_versions_seen: Object.fromEntries(
|
|
1156
|
+
Object.entries(EXT_SEEN).sort((a, b) => b[1] - a[1]).slice(0, 8)
|
|
1157
|
+
),
|
|
1158
|
+
});
|
|
1159
|
+
return;
|
|
1104
1160
|
}
|
|
1105
1161
|
|
|
1106
1162
|
// Ask
|
|
@@ -1190,7 +1246,7 @@ function startBridge(config) {
|
|
|
1190
1246
|
const q = url.searchParams.get("q") || "";
|
|
1191
1247
|
const limit = parseInt(url.searchParams.get("limit") || "30", 10);
|
|
1192
1248
|
const results = searchSessions(q, limit);
|
|
1193
|
-
send(200, { results });
|
|
1249
|
+
send(200, { q, results, hits: results }); // `hits` = Python-bridge parity (the extension reads it)
|
|
1194
1250
|
return;
|
|
1195
1251
|
}
|
|
1196
1252
|
|
|
@@ -1500,4 +1556,4 @@ function readBody(req) {
|
|
|
1500
1556
|
});
|
|
1501
1557
|
}
|
|
1502
1558
|
|
|
1503
|
-
module.exports = { startBridge };
|
|
1559
|
+
module.exports = { startBridge, ensureTranscriptRetention };
|
package/package.json
CHANGED