@raindrop-ai/claude-code 0.0.1 → 0.0.3
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/dist/cli.js +350 -49
- package/dist/index.cjs +165 -7
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +166 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
|
|
9
|
-
// ../core/dist/chunk-
|
|
9
|
+
// ../core/dist/chunk-4UCYIEH4.js
|
|
10
10
|
function getCrypto() {
|
|
11
11
|
const c = globalThis.crypto;
|
|
12
12
|
return c;
|
|
@@ -442,11 +442,28 @@ var EventShipper = class {
|
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
444
|
};
|
|
445
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
446
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
447
|
+
var _a, _b, _c;
|
|
448
|
+
const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
|
|
449
|
+
return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
|
|
450
|
+
}
|
|
451
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
452
|
+
var _a;
|
|
453
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
454
|
+
if (!baseUrl) return;
|
|
455
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
456
|
+
maxAttempts: 1,
|
|
457
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
458
|
+
sdkName: options.sdkName
|
|
459
|
+
}).catch(() => {
|
|
460
|
+
});
|
|
461
|
+
}
|
|
445
462
|
var TraceShipper = class {
|
|
446
463
|
constructor(opts) {
|
|
447
464
|
this.queue = [];
|
|
448
465
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
449
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
466
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
450
467
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
451
468
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
452
469
|
this.enabled = opts.enabled !== false;
|
|
@@ -459,6 +476,13 @@ var TraceShipper = class {
|
|
|
459
476
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
460
477
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
461
478
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
479
|
+
const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
|
|
480
|
+
if (localDebugger) {
|
|
481
|
+
this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
|
|
482
|
+
if (this.debug) {
|
|
483
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
462
486
|
}
|
|
463
487
|
isDebugEnabled() {
|
|
464
488
|
return this.debug;
|
|
@@ -475,7 +499,25 @@ var TraceShipper = class {
|
|
|
475
499
|
attrString("ai.operationId", args2.operationId)
|
|
476
500
|
];
|
|
477
501
|
if ((_b = args2.attributes) == null ? void 0 : _b.length) attrs.push(...args2.attributes);
|
|
478
|
-
|
|
502
|
+
const span = { ids, name: args2.name, startTimeUnixNano: started, attributes: attrs };
|
|
503
|
+
if (this.localDebuggerUrl) {
|
|
504
|
+
const openSpan = buildOtlpSpan({
|
|
505
|
+
ids: span.ids,
|
|
506
|
+
name: span.name,
|
|
507
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
508
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
509
|
+
// placeholder — will be updated on endSpan
|
|
510
|
+
attributes: span.attributes,
|
|
511
|
+
status: { code: SpanStatusCode.UNSET }
|
|
512
|
+
});
|
|
513
|
+
const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
|
|
514
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
515
|
+
baseUrl: this.localDebuggerUrl,
|
|
516
|
+
debug: false,
|
|
517
|
+
sdkName: this.sdkName
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
return span;
|
|
479
521
|
}
|
|
480
522
|
endSpan(span, extra) {
|
|
481
523
|
var _a, _b;
|
|
@@ -498,6 +540,14 @@ var TraceShipper = class {
|
|
|
498
540
|
status
|
|
499
541
|
});
|
|
500
542
|
this.enqueue(otlp);
|
|
543
|
+
if (this.localDebuggerUrl) {
|
|
544
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
545
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
546
|
+
baseUrl: this.localDebuggerUrl,
|
|
547
|
+
debug: false,
|
|
548
|
+
sdkName: this.sdkName
|
|
549
|
+
});
|
|
550
|
+
}
|
|
501
551
|
}
|
|
502
552
|
createSpan(args2) {
|
|
503
553
|
var _a;
|
|
@@ -515,6 +565,14 @@ var TraceShipper = class {
|
|
|
515
565
|
status: args2.status
|
|
516
566
|
});
|
|
517
567
|
this.enqueue(otlp);
|
|
568
|
+
if (this.localDebuggerUrl) {
|
|
569
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
570
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
571
|
+
baseUrl: this.localDebuggerUrl,
|
|
572
|
+
debug: false,
|
|
573
|
+
sdkName: this.sdkName
|
|
574
|
+
});
|
|
575
|
+
}
|
|
518
576
|
}
|
|
519
577
|
enqueue(span) {
|
|
520
578
|
if (!this.enabled) return;
|
|
@@ -969,12 +1027,12 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
|
|
|
969
1027
|
}
|
|
970
1028
|
|
|
971
1029
|
// src/config.ts
|
|
972
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
1030
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
973
1031
|
import { homedir, userInfo } from "os";
|
|
974
|
-
import { join as join2 } from "path";
|
|
1032
|
+
import { dirname, join as join2 } from "path";
|
|
975
1033
|
var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
|
|
976
1034
|
function loadConfig() {
|
|
977
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1035
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
978
1036
|
let file = {};
|
|
979
1037
|
try {
|
|
980
1038
|
if (existsSync2(CONFIG_PATH)) {
|
|
@@ -993,15 +1051,125 @@ function loadConfig() {
|
|
|
993
1051
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : file.write_key) != null ? _b : "",
|
|
994
1052
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : file.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
995
1053
|
userId: (_f = (_e = process.env["RAINDROP_USER_ID"]) != null ? _e : file.user_id) != null ? _f : systemUser,
|
|
996
|
-
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false
|
|
1054
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false,
|
|
1055
|
+
enabled: (_h = file.enabled) != null ? _h : true
|
|
997
1056
|
};
|
|
998
1057
|
}
|
|
999
1058
|
function getConfigPath() {
|
|
1000
1059
|
return CONFIG_PATH;
|
|
1001
1060
|
}
|
|
1061
|
+
function updateConfig(patch) {
|
|
1062
|
+
const dir = dirname(CONFIG_PATH);
|
|
1063
|
+
mkdirSync2(dir, { recursive: true });
|
|
1064
|
+
let existing = {};
|
|
1065
|
+
try {
|
|
1066
|
+
if (existsSync2(CONFIG_PATH)) {
|
|
1067
|
+
existing = JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
|
|
1068
|
+
}
|
|
1069
|
+
} catch (e) {
|
|
1070
|
+
}
|
|
1071
|
+
writeFileSync2(CONFIG_PATH, JSON.stringify({ ...existing, ...patch }, null, 2) + "\n", "utf-8");
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/local-debugger.ts
|
|
1075
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1076
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1077
|
+
import { join as join3 } from "path";
|
|
1078
|
+
var DEFAULT_PORT = 5899;
|
|
1079
|
+
var DEFAULT_URL = `http://localhost:${DEFAULT_PORT}/v1/`;
|
|
1080
|
+
var HEALTH_TIMEOUT_MS = 300;
|
|
1081
|
+
var CACHE_DIR = join3(tmpdir2(), "raindrop-claude-code");
|
|
1082
|
+
var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
|
|
1083
|
+
var CACHE_TTL_MS = 5e3;
|
|
1084
|
+
function readCache() {
|
|
1085
|
+
try {
|
|
1086
|
+
if (!existsSync3(CACHE_FILE)) return void 0;
|
|
1087
|
+
const data = JSON.parse(readFileSync3(CACHE_FILE, "utf-8"));
|
|
1088
|
+
if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
|
|
1089
|
+
return data;
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
return void 0;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function writeCache(url) {
|
|
1095
|
+
try {
|
|
1096
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
1097
|
+
writeFileSync3(CACHE_FILE, JSON.stringify({ url, ts: Date.now() }), "utf-8");
|
|
1098
|
+
} catch (e) {
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
async function healthPing(baseUrl) {
|
|
1102
|
+
const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
|
|
1103
|
+
try {
|
|
1104
|
+
const controller = new AbortController();
|
|
1105
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
1106
|
+
const resp = await fetch(healthUrl, { signal: controller.signal });
|
|
1107
|
+
clearTimeout(timeout);
|
|
1108
|
+
if (resp.ok) {
|
|
1109
|
+
const body = await resp.json();
|
|
1110
|
+
if (body.ok) return baseUrl;
|
|
1111
|
+
}
|
|
1112
|
+
} catch (e) {
|
|
1113
|
+
}
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
async function detectLocalDebugger(debug) {
|
|
1117
|
+
var _a;
|
|
1118
|
+
const envUrl = resolveLocalDebuggerBaseUrl();
|
|
1119
|
+
if (envUrl) {
|
|
1120
|
+
if (debug) {
|
|
1121
|
+
console.log(`[raindrop-ai/claude-code] Local debugger configured via ${LOCAL_DEBUGGER_ENV_VAR}: ${envUrl}`);
|
|
1122
|
+
}
|
|
1123
|
+
return { url: envUrl, autoDetected: false };
|
|
1124
|
+
}
|
|
1125
|
+
const cached = readCache();
|
|
1126
|
+
if (cached) {
|
|
1127
|
+
if (debug) {
|
|
1128
|
+
console.log(`[raindrop-ai/claude-code] Local debugger cache hit: ${(_a = cached.url) != null ? _a : "not running"}`);
|
|
1129
|
+
}
|
|
1130
|
+
return { url: cached.url, autoDetected: cached.url !== null };
|
|
1131
|
+
}
|
|
1132
|
+
if (debug) {
|
|
1133
|
+
console.log(`[raindrop-ai/claude-code] Probing local debugger at localhost:${DEFAULT_PORT}...`);
|
|
1134
|
+
}
|
|
1135
|
+
const detected = await healthPing(DEFAULT_URL);
|
|
1136
|
+
writeCache(detected);
|
|
1137
|
+
if (detected && debug) {
|
|
1138
|
+
console.log(`[raindrop-ai/claude-code] Local debugger auto-detected at ${detected}`);
|
|
1139
|
+
}
|
|
1140
|
+
return { url: detected, autoDetected: detected !== null };
|
|
1141
|
+
}
|
|
1142
|
+
function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
1143
|
+
const url = `${baseUrl}events/track_partial`;
|
|
1144
|
+
if (debug) {
|
|
1145
|
+
console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
|
|
1146
|
+
}
|
|
1147
|
+
void fetch(url, {
|
|
1148
|
+
method: "POST",
|
|
1149
|
+
headers: { "Content-Type": "application/json" },
|
|
1150
|
+
body: JSON.stringify(payload)
|
|
1151
|
+
}).catch(() => {
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1002
1154
|
|
|
1003
1155
|
// src/hook-handler.ts
|
|
1156
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
1157
|
+
import { join as join4 } from "path";
|
|
1158
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
1004
1159
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
1160
|
+
var STATE_DIR2 = join4(tmpdir3(), "raindrop-claude-code");
|
|
1161
|
+
function getOtlpTraceId(sessionId) {
|
|
1162
|
+
try {
|
|
1163
|
+
const key = `rootspan_${sessionId}`.replace(/[^a-zA-Z0-9_\-]/g, "_");
|
|
1164
|
+
const filePath = join4(STATE_DIR2, key);
|
|
1165
|
+
if (!existsSync4(filePath)) return void 0;
|
|
1166
|
+
const ctx = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
1167
|
+
if (!ctx.traceIdB64) return void 0;
|
|
1168
|
+
return Buffer.from(ctx.traceIdB64, "base64").toString("hex");
|
|
1169
|
+
} catch (e) {
|
|
1170
|
+
return void 0;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1005
1173
|
function readStdin() {
|
|
1006
1174
|
return new Promise((resolve2) => {
|
|
1007
1175
|
if (process.stdin.isTTY) {
|
|
@@ -1041,7 +1209,16 @@ async function handleHook() {
|
|
|
1041
1209
|
return;
|
|
1042
1210
|
}
|
|
1043
1211
|
const config = loadConfig();
|
|
1044
|
-
if (!config.
|
|
1212
|
+
if (!config.enabled) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
const debugger_ = await detectLocalDebugger(config.debug);
|
|
1216
|
+
if (debugger_.url && debugger_.autoDetected) {
|
|
1217
|
+
process.env[LOCAL_DEBUGGER_ENV_VAR] = debugger_.url;
|
|
1218
|
+
}
|
|
1219
|
+
const hasCloudKey = Boolean(config.writeKey);
|
|
1220
|
+
const hasDebugger = Boolean(debugger_.url);
|
|
1221
|
+
if (!hasCloudKey && !hasDebugger) {
|
|
1045
1222
|
return;
|
|
1046
1223
|
}
|
|
1047
1224
|
const mapperConfig = {
|
|
@@ -1051,13 +1228,44 @@ async function handleHook() {
|
|
|
1051
1228
|
const eventShipper = new EventShipper2({
|
|
1052
1229
|
writeKey: config.writeKey,
|
|
1053
1230
|
endpoint: config.endpoint,
|
|
1054
|
-
debug: config.debug
|
|
1231
|
+
debug: config.debug,
|
|
1232
|
+
// Only ship to the cloud when we have an API key. When only the local
|
|
1233
|
+
// debugger is present the shipper still needs to be instantiated so the
|
|
1234
|
+
// event mapper can call patch/finish, but cloud POSTs are skipped.
|
|
1235
|
+
enabled: hasCloudKey
|
|
1055
1236
|
});
|
|
1056
1237
|
const traceShipper = new TraceShipper2({
|
|
1057
1238
|
writeKey: config.writeKey,
|
|
1058
1239
|
endpoint: config.endpoint,
|
|
1059
|
-
debug: config.debug
|
|
1240
|
+
debug: config.debug,
|
|
1241
|
+
enabled: hasCloudKey
|
|
1060
1242
|
});
|
|
1243
|
+
if (debugger_.url) {
|
|
1244
|
+
const debuggerUrl = debugger_.url;
|
|
1245
|
+
const origPatch = eventShipper.patch.bind(eventShipper);
|
|
1246
|
+
eventShipper.patch = async (eventId, patch) => {
|
|
1247
|
+
var _a, _b, _c, _d;
|
|
1248
|
+
const traceId = (_a = getOtlpTraceId(payload.session_id)) != null ? _a : payload.session_id;
|
|
1249
|
+
mirrorEventToLocalDebugger(debuggerUrl, {
|
|
1250
|
+
event_id: eventId,
|
|
1251
|
+
user_id: (_b = patch.userId) != null ? _b : config.userId,
|
|
1252
|
+
event: (_c = patch.eventName) != null ? _c : "claude_code_session",
|
|
1253
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1254
|
+
ai_data: {
|
|
1255
|
+
input: patch.input,
|
|
1256
|
+
output: patch.output,
|
|
1257
|
+
model: patch.model,
|
|
1258
|
+
convo_id: patch.convoId
|
|
1259
|
+
},
|
|
1260
|
+
properties: {
|
|
1261
|
+
...patch.properties,
|
|
1262
|
+
...payload.session_id ? { trace_id: traceId } : {}
|
|
1263
|
+
},
|
|
1264
|
+
is_pending: (_d = patch.isPending) != null ? _d : true
|
|
1265
|
+
}, config.debug);
|
|
1266
|
+
await origPatch(eventId, patch);
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1061
1269
|
try {
|
|
1062
1270
|
await mapHookToRaindrop(payload, mapperConfig, eventShipper, traceShipper);
|
|
1063
1271
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
@@ -1072,17 +1280,18 @@ async function handleHook() {
|
|
|
1072
1280
|
}
|
|
1073
1281
|
|
|
1074
1282
|
// src/setup.ts
|
|
1075
|
-
import { existsSync as
|
|
1283
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
1076
1284
|
import { homedir as homedir2, userInfo as userInfo2 } from "os";
|
|
1077
|
-
import { dirname, join as
|
|
1285
|
+
import { dirname as dirname2, join as join5, resolve } from "path";
|
|
1078
1286
|
import { createInterface } from "readline";
|
|
1287
|
+
import { execSync } from "child_process";
|
|
1079
1288
|
function getClaudeSettingsPath(scope, cwd) {
|
|
1080
1289
|
if (scope === "project") {
|
|
1081
1290
|
return resolve(cwd, ".claude", "settings.json");
|
|
1082
1291
|
}
|
|
1083
|
-
return
|
|
1292
|
+
return join5(homedir2(), ".claude", "settings.json");
|
|
1084
1293
|
}
|
|
1085
|
-
var
|
|
1294
|
+
var CORE_HOOK_EVENTS = [
|
|
1086
1295
|
"SessionStart",
|
|
1087
1296
|
"UserPromptSubmit",
|
|
1088
1297
|
"PreToolUse",
|
|
@@ -1090,15 +1299,58 @@ var HOOK_EVENTS = [
|
|
|
1090
1299
|
"PostToolUseFailure",
|
|
1091
1300
|
"SubagentStart",
|
|
1092
1301
|
"SubagentStop",
|
|
1093
|
-
"PermissionDenied",
|
|
1094
|
-
"PostCompact",
|
|
1095
1302
|
"Stop",
|
|
1096
|
-
"StopFailure",
|
|
1097
1303
|
"SessionEnd"
|
|
1098
1304
|
];
|
|
1099
|
-
|
|
1305
|
+
var VERSIONED_HOOK_EVENTS = [
|
|
1306
|
+
{ minVersion: "2.1.78", events: ["StopFailure"] },
|
|
1307
|
+
{ minVersion: "2.1.76", events: ["PostCompact"] },
|
|
1308
|
+
{ minVersion: "2.1.89", events: ["PermissionDenied"] }
|
|
1309
|
+
];
|
|
1310
|
+
function parseVersion(version) {
|
|
1311
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
1312
|
+
if (!match) return void 0;
|
|
1313
|
+
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];
|
|
1314
|
+
}
|
|
1315
|
+
function isVersionAtLeast(current, required) {
|
|
1316
|
+
const req = parseVersion(required);
|
|
1317
|
+
if (!req) return false;
|
|
1318
|
+
for (let i = 0; i < 3; i++) {
|
|
1319
|
+
if (current[i] > req[i]) return true;
|
|
1320
|
+
if (current[i] < req[i]) return false;
|
|
1321
|
+
}
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
function detectClaudeCodeVersion() {
|
|
1325
|
+
try {
|
|
1326
|
+
const output = execSync("claude --version", {
|
|
1327
|
+
encoding: "utf-8",
|
|
1328
|
+
timeout: 5e3,
|
|
1329
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1330
|
+
}).trim();
|
|
1331
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
1332
|
+
return match ? match[1] : void 0;
|
|
1333
|
+
} catch (e) {
|
|
1334
|
+
return void 0;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
function getHookEventsForVersion(version) {
|
|
1338
|
+
const events = [...CORE_HOOK_EVENTS];
|
|
1339
|
+
if (!version) {
|
|
1340
|
+
return events;
|
|
1341
|
+
}
|
|
1342
|
+
const parsed = parseVersion(version);
|
|
1343
|
+
if (!parsed) return events;
|
|
1344
|
+
for (const { minVersion, events: versionedEvents } of VERSIONED_HOOK_EVENTS) {
|
|
1345
|
+
if (isVersionAtLeast(parsed, minVersion)) {
|
|
1346
|
+
events.push(...versionedEvents);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return events;
|
|
1350
|
+
}
|
|
1351
|
+
function makeHookConfig(hookEvents) {
|
|
1100
1352
|
const hooks = {};
|
|
1101
|
-
for (const event of
|
|
1353
|
+
for (const event of hookEvents) {
|
|
1102
1354
|
hooks[event] = [
|
|
1103
1355
|
{
|
|
1104
1356
|
hooks: [
|
|
@@ -1129,10 +1381,10 @@ async function runSetup(args2) {
|
|
|
1129
1381
|
const scopeLabel = scope === "project" ? "project" : "global";
|
|
1130
1382
|
console.log("\n Raindrop \xD7 Claude Code \u2014 Setup\n");
|
|
1131
1383
|
let writeKey = (_c = (_b = args2.writeKey) != null ? _b : process.env["RAINDROP_WRITE_KEY"]) != null ? _c : "";
|
|
1132
|
-
if (!writeKey) {
|
|
1384
|
+
if (!writeKey && !args2.localOnly) {
|
|
1133
1385
|
writeKey = await prompt(" Enter your Raindrop write key: ");
|
|
1134
1386
|
}
|
|
1135
|
-
if (!writeKey) {
|
|
1387
|
+
if (!writeKey && !args2.localOnly) {
|
|
1136
1388
|
console.error("\n Error: write key is required. Get it from https://app.raindrop.ai\n");
|
|
1137
1389
|
process.exit(1);
|
|
1138
1390
|
}
|
|
@@ -1144,37 +1396,47 @@ async function runSetup(args2) {
|
|
|
1144
1396
|
userId = "unknown";
|
|
1145
1397
|
}
|
|
1146
1398
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1399
|
+
if (writeKey) {
|
|
1400
|
+
const configPath = getConfigPath();
|
|
1401
|
+
const configDir = dirname2(configPath);
|
|
1402
|
+
mkdirSync4(configDir, { recursive: true });
|
|
1403
|
+
let existingConfig = {};
|
|
1404
|
+
try {
|
|
1405
|
+
if (existsSync5(configPath)) {
|
|
1406
|
+
existingConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
1407
|
+
}
|
|
1408
|
+
} catch (e) {
|
|
1154
1409
|
}
|
|
1155
|
-
|
|
1410
|
+
const raindropConfig = {
|
|
1411
|
+
...existingConfig,
|
|
1412
|
+
write_key: writeKey,
|
|
1413
|
+
user_id: userId
|
|
1414
|
+
};
|
|
1415
|
+
writeFileSync4(configPath, JSON.stringify(raindropConfig, null, 2) + "\n", "utf-8");
|
|
1416
|
+
console.log(` Saved Raindrop config to ${configPath}`);
|
|
1417
|
+
}
|
|
1418
|
+
const ccVersion = detectClaudeCodeVersion();
|
|
1419
|
+
const hookEvents = getHookEventsForVersion(ccVersion);
|
|
1420
|
+
if (ccVersion) {
|
|
1421
|
+
console.log(` Detected Claude Code v${ccVersion}`);
|
|
1422
|
+
} else {
|
|
1423
|
+
console.log(` Could not detect Claude Code version \u2014 registering core hooks only.`);
|
|
1424
|
+
console.log(` Run setup again after installing Claude Code to enable all hooks.`);
|
|
1156
1425
|
}
|
|
1157
|
-
const raindropConfig = {
|
|
1158
|
-
...existingConfig,
|
|
1159
|
-
write_key: writeKey,
|
|
1160
|
-
user_id: userId
|
|
1161
|
-
};
|
|
1162
|
-
writeFileSync2(configPath, JSON.stringify(raindropConfig, null, 2) + "\n", "utf-8");
|
|
1163
|
-
console.log(` Saved Raindrop config to ${configPath}`);
|
|
1164
1426
|
const settingsPath = getClaudeSettingsPath(scope, process.cwd());
|
|
1165
|
-
const settingsDir =
|
|
1166
|
-
|
|
1427
|
+
const settingsDir = dirname2(settingsPath);
|
|
1428
|
+
mkdirSync4(settingsDir, { recursive: true });
|
|
1167
1429
|
let settings = {};
|
|
1168
|
-
if (
|
|
1430
|
+
if (existsSync5(settingsPath)) {
|
|
1169
1431
|
try {
|
|
1170
|
-
settings = JSON.parse(
|
|
1432
|
+
settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
|
|
1171
1433
|
} catch (e) {
|
|
1172
1434
|
console.warn(` Warning: could not parse ${settingsPath}, creating fresh`);
|
|
1173
1435
|
settings = {};
|
|
1174
1436
|
}
|
|
1175
1437
|
}
|
|
1176
1438
|
const existingHooks = (_e = settings["hooks"]) != null ? _e : {};
|
|
1177
|
-
const newHooks = makeHookConfig();
|
|
1439
|
+
const newHooks = makeHookConfig(hookEvents);
|
|
1178
1440
|
for (const [event, hookGroups] of Object.entries(newHooks)) {
|
|
1179
1441
|
const existing = existingHooks[event];
|
|
1180
1442
|
if (!existing || !Array.isArray(existing)) {
|
|
@@ -1193,9 +1455,8 @@ async function runSetup(args2) {
|
|
|
1193
1455
|
}
|
|
1194
1456
|
}
|
|
1195
1457
|
settings["hooks"] = existingHooks;
|
|
1196
|
-
|
|
1458
|
+
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
1197
1459
|
console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
|
|
1198
|
-
const { execSync } = await import("child_process");
|
|
1199
1460
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1200
1461
|
try {
|
|
1201
1462
|
execSync(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
|
|
@@ -1207,7 +1468,8 @@ async function runSetup(args2) {
|
|
|
1207
1468
|
npm install -g @raindrop-ai/claude-code
|
|
1208
1469
|
`);
|
|
1209
1470
|
}
|
|
1210
|
-
|
|
1471
|
+
if (writeKey) {
|
|
1472
|
+
console.log(`
|
|
1211
1473
|
Done! Claude Code will now send telemetry to Raindrop.
|
|
1212
1474
|
Scope: ${scopeLabel}
|
|
1213
1475
|
User ID: ${userId}
|
|
@@ -1215,6 +1477,13 @@ async function runSetup(args2) {
|
|
|
1215
1477
|
To verify, start a Claude Code session and check your Raindrop dashboard.
|
|
1216
1478
|
Set RAINDROP_DEBUG=true for verbose logging.
|
|
1217
1479
|
`);
|
|
1480
|
+
} else {
|
|
1481
|
+
console.log(`
|
|
1482
|
+
Done! Hooks installed (${scopeLabel}).
|
|
1483
|
+
No write key configured \u2014 events will only be sent to the local debugger.
|
|
1484
|
+
Start it with: npx @raindrop-ai/local-debugger
|
|
1485
|
+
`);
|
|
1486
|
+
}
|
|
1218
1487
|
}
|
|
1219
1488
|
|
|
1220
1489
|
// src/cli.ts
|
|
@@ -1233,7 +1502,8 @@ async function main() {
|
|
|
1233
1502
|
await runSetup({
|
|
1234
1503
|
writeKey: parseFlag("write-key"),
|
|
1235
1504
|
userId: parseFlag("user-id"),
|
|
1236
|
-
scope
|
|
1505
|
+
scope,
|
|
1506
|
+
localOnly: args.includes("--local-only")
|
|
1237
1507
|
});
|
|
1238
1508
|
break;
|
|
1239
1509
|
}
|
|
@@ -1241,6 +1511,32 @@ async function main() {
|
|
|
1241
1511
|
await handleHook();
|
|
1242
1512
|
break;
|
|
1243
1513
|
}
|
|
1514
|
+
case "status": {
|
|
1515
|
+
const config = loadConfig();
|
|
1516
|
+
const result = await detectLocalDebugger(config.debug);
|
|
1517
|
+
console.log("");
|
|
1518
|
+
if (result.url) {
|
|
1519
|
+
console.log(` Local debugger: \x1B[32m\u25CF connected\x1B[0m`);
|
|
1520
|
+
console.log(` URL: ${result.url}`);
|
|
1521
|
+
console.log(` Source: ${result.autoDetected ? "auto-detected" : "RAINDROP_LOCAL_DEBUGGER env var"}`);
|
|
1522
|
+
} else {
|
|
1523
|
+
console.log(` Local debugger: \x1B[2m\u25CB not detected\x1B[0m`);
|
|
1524
|
+
console.log(` Start it with: npx @dawn/local-debugger`);
|
|
1525
|
+
}
|
|
1526
|
+
console.log(` Hooks: ${config.enabled ? "\x1B[32menabled\x1B[0m" : "\x1B[2mdisabled\x1B[0m"}`);
|
|
1527
|
+
console.log("");
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
case "enable": {
|
|
1531
|
+
updateConfig({ enabled: true });
|
|
1532
|
+
console.log(" Raindrop hooks enabled.");
|
|
1533
|
+
break;
|
|
1534
|
+
}
|
|
1535
|
+
case "disable": {
|
|
1536
|
+
updateConfig({ enabled: false });
|
|
1537
|
+
console.log(" Raindrop hooks disabled.");
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1244
1540
|
case "version":
|
|
1245
1541
|
case "--version":
|
|
1246
1542
|
case "-v": {
|
|
@@ -1261,16 +1557,21 @@ async function main() {
|
|
|
1261
1557
|
--write-key=KEY Raindrop write key (or set RAINDROP_WRITE_KEY)
|
|
1262
1558
|
--user-id=ID User identifier (defaults to system username)
|
|
1263
1559
|
--scope=SCOPE "user" (global, default) or "project" (.claude/ in cwd)
|
|
1560
|
+
--local-only Install hooks without a write key (local debugger only)
|
|
1264
1561
|
|
|
1265
1562
|
hook Handle a Claude Code hook event (reads JSON from stdin)
|
|
1563
|
+
status Check local debugger connectivity
|
|
1564
|
+
enable Enable Raindrop hooks
|
|
1565
|
+
disable Disable Raindrop hooks
|
|
1266
1566
|
version Print version
|
|
1267
1567
|
help Show this help message
|
|
1268
1568
|
|
|
1269
1569
|
Environment:
|
|
1270
|
-
RAINDROP_WRITE_KEY
|
|
1271
|
-
RAINDROP_USER_ID
|
|
1272
|
-
RAINDROP_API_URL
|
|
1273
|
-
|
|
1570
|
+
RAINDROP_WRITE_KEY API write key (alternative to --write-key or config file)
|
|
1571
|
+
RAINDROP_USER_ID User ID override
|
|
1572
|
+
RAINDROP_API_URL Custom API endpoint
|
|
1573
|
+
RAINDROP_LOCAL_DEBUGGER Local debugger URL (auto-detected if running on :5899)
|
|
1574
|
+
RAINDROP_DEBUG Set to "true" for verbose logging
|
|
1274
1575
|
`);
|
|
1275
1576
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
1276
1577
|
process.exit(0);
|
package/dist/index.cjs
CHANGED
|
@@ -24,13 +24,16 @@ __export(src_exports, {
|
|
|
24
24
|
PACKAGE_NAME: () => PACKAGE_NAME,
|
|
25
25
|
PACKAGE_VERSION: () => PACKAGE_VERSION,
|
|
26
26
|
TraceShipper: () => TraceShipper2,
|
|
27
|
+
detectLocalDebugger: () => detectLocalDebugger,
|
|
27
28
|
getConfigPath: () => getConfigPath,
|
|
28
29
|
loadConfig: () => loadConfig,
|
|
29
|
-
mapHookToRaindrop: () => mapHookToRaindrop
|
|
30
|
+
mapHookToRaindrop: () => mapHookToRaindrop,
|
|
31
|
+
mirrorEventToLocalDebugger: () => mirrorEventToLocalDebugger,
|
|
32
|
+
updateConfig: () => updateConfig
|
|
30
33
|
});
|
|
31
34
|
module.exports = __toCommonJS(src_exports);
|
|
32
35
|
|
|
33
|
-
// ../core/dist/chunk-
|
|
36
|
+
// ../core/dist/chunk-4UCYIEH4.js
|
|
34
37
|
function getCrypto() {
|
|
35
38
|
const c = globalThis.crypto;
|
|
36
39
|
return c;
|
|
@@ -466,11 +469,28 @@ var EventShipper = class {
|
|
|
466
469
|
}
|
|
467
470
|
}
|
|
468
471
|
};
|
|
472
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
473
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
474
|
+
var _a, _b, _c;
|
|
475
|
+
const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
|
|
476
|
+
return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
|
|
477
|
+
}
|
|
478
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
479
|
+
var _a;
|
|
480
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
481
|
+
if (!baseUrl) return;
|
|
482
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
483
|
+
maxAttempts: 1,
|
|
484
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
485
|
+
sdkName: options.sdkName
|
|
486
|
+
}).catch(() => {
|
|
487
|
+
});
|
|
488
|
+
}
|
|
469
489
|
var TraceShipper = class {
|
|
470
490
|
constructor(opts) {
|
|
471
491
|
this.queue = [];
|
|
472
492
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
473
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
493
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
474
494
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
475
495
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
476
496
|
this.enabled = opts.enabled !== false;
|
|
@@ -483,6 +503,13 @@ var TraceShipper = class {
|
|
|
483
503
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
484
504
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
485
505
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
506
|
+
const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
|
|
507
|
+
if (localDebugger) {
|
|
508
|
+
this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
|
|
509
|
+
if (this.debug) {
|
|
510
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
486
513
|
}
|
|
487
514
|
isDebugEnabled() {
|
|
488
515
|
return this.debug;
|
|
@@ -499,7 +526,25 @@ var TraceShipper = class {
|
|
|
499
526
|
attrString("ai.operationId", args.operationId)
|
|
500
527
|
];
|
|
501
528
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
502
|
-
|
|
529
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
530
|
+
if (this.localDebuggerUrl) {
|
|
531
|
+
const openSpan = buildOtlpSpan({
|
|
532
|
+
ids: span.ids,
|
|
533
|
+
name: span.name,
|
|
534
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
535
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
536
|
+
// placeholder — will be updated on endSpan
|
|
537
|
+
attributes: span.attributes,
|
|
538
|
+
status: { code: SpanStatusCode.UNSET }
|
|
539
|
+
});
|
|
540
|
+
const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
|
|
541
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
542
|
+
baseUrl: this.localDebuggerUrl,
|
|
543
|
+
debug: false,
|
|
544
|
+
sdkName: this.sdkName
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return span;
|
|
503
548
|
}
|
|
504
549
|
endSpan(span, extra) {
|
|
505
550
|
var _a, _b;
|
|
@@ -522,6 +567,14 @@ var TraceShipper = class {
|
|
|
522
567
|
status
|
|
523
568
|
});
|
|
524
569
|
this.enqueue(otlp);
|
|
570
|
+
if (this.localDebuggerUrl) {
|
|
571
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
572
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
573
|
+
baseUrl: this.localDebuggerUrl,
|
|
574
|
+
debug: false,
|
|
575
|
+
sdkName: this.sdkName
|
|
576
|
+
});
|
|
577
|
+
}
|
|
525
578
|
}
|
|
526
579
|
createSpan(args) {
|
|
527
580
|
var _a;
|
|
@@ -539,6 +592,14 @@ var TraceShipper = class {
|
|
|
539
592
|
status: args.status
|
|
540
593
|
});
|
|
541
594
|
this.enqueue(otlp);
|
|
595
|
+
if (this.localDebuggerUrl) {
|
|
596
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
597
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
598
|
+
baseUrl: this.localDebuggerUrl,
|
|
599
|
+
debug: false,
|
|
600
|
+
sdkName: this.sdkName
|
|
601
|
+
});
|
|
602
|
+
}
|
|
542
603
|
}
|
|
543
604
|
enqueue(span) {
|
|
544
605
|
if (!this.enabled) return;
|
|
@@ -662,7 +723,7 @@ var import_node_os = require("os");
|
|
|
662
723
|
var import_node_path = require("path");
|
|
663
724
|
var CONFIG_PATH = (0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "raindrop", "config.json");
|
|
664
725
|
function loadConfig() {
|
|
665
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
726
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
666
727
|
let file = {};
|
|
667
728
|
try {
|
|
668
729
|
if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
|
|
@@ -681,12 +742,25 @@ function loadConfig() {
|
|
|
681
742
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : file.write_key) != null ? _b : "",
|
|
682
743
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : file.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
683
744
|
userId: (_f = (_e = process.env["RAINDROP_USER_ID"]) != null ? _e : file.user_id) != null ? _f : systemUser,
|
|
684
|
-
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false
|
|
745
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false,
|
|
746
|
+
enabled: (_h = file.enabled) != null ? _h : true
|
|
685
747
|
};
|
|
686
748
|
}
|
|
687
749
|
function getConfigPath() {
|
|
688
750
|
return CONFIG_PATH;
|
|
689
751
|
}
|
|
752
|
+
function updateConfig(patch) {
|
|
753
|
+
const dir = (0, import_node_path.dirname)(CONFIG_PATH);
|
|
754
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
755
|
+
let existing = {};
|
|
756
|
+
try {
|
|
757
|
+
if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
|
|
758
|
+
existing = JSON.parse((0, import_node_fs.readFileSync)(CONFIG_PATH, "utf-8"));
|
|
759
|
+
}
|
|
760
|
+
} catch (e) {
|
|
761
|
+
}
|
|
762
|
+
(0, import_node_fs.writeFileSync)(CONFIG_PATH, JSON.stringify({ ...existing, ...patch }, null, 2) + "\n", "utf-8");
|
|
763
|
+
}
|
|
690
764
|
|
|
691
765
|
// src/event-mapper.ts
|
|
692
766
|
var import_node_fs2 = require("fs");
|
|
@@ -1027,13 +1101,97 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
|
|
|
1027
1101
|
}
|
|
1028
1102
|
});
|
|
1029
1103
|
}
|
|
1104
|
+
|
|
1105
|
+
// src/local-debugger.ts
|
|
1106
|
+
var import_node_fs3 = require("fs");
|
|
1107
|
+
var import_node_os3 = require("os");
|
|
1108
|
+
var import_node_path3 = require("path");
|
|
1109
|
+
var DEFAULT_PORT = 5899;
|
|
1110
|
+
var DEFAULT_URL = `http://localhost:${DEFAULT_PORT}/v1/`;
|
|
1111
|
+
var HEALTH_TIMEOUT_MS = 300;
|
|
1112
|
+
var CACHE_DIR = (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "raindrop-claude-code");
|
|
1113
|
+
var CACHE_FILE = (0, import_node_path3.join)(CACHE_DIR, "debugger_cache");
|
|
1114
|
+
var CACHE_TTL_MS = 5e3;
|
|
1115
|
+
function readCache() {
|
|
1116
|
+
try {
|
|
1117
|
+
if (!(0, import_node_fs3.existsSync)(CACHE_FILE)) return void 0;
|
|
1118
|
+
const data = JSON.parse((0, import_node_fs3.readFileSync)(CACHE_FILE, "utf-8"));
|
|
1119
|
+
if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
|
|
1120
|
+
return data;
|
|
1121
|
+
} catch (e) {
|
|
1122
|
+
return void 0;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
function writeCache(url) {
|
|
1126
|
+
try {
|
|
1127
|
+
(0, import_node_fs3.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
1128
|
+
(0, import_node_fs3.writeFileSync)(CACHE_FILE, JSON.stringify({ url, ts: Date.now() }), "utf-8");
|
|
1129
|
+
} catch (e) {
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async function healthPing(baseUrl) {
|
|
1133
|
+
const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
|
|
1134
|
+
try {
|
|
1135
|
+
const controller = new AbortController();
|
|
1136
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
1137
|
+
const resp = await fetch(healthUrl, { signal: controller.signal });
|
|
1138
|
+
clearTimeout(timeout);
|
|
1139
|
+
if (resp.ok) {
|
|
1140
|
+
const body = await resp.json();
|
|
1141
|
+
if (body.ok) return baseUrl;
|
|
1142
|
+
}
|
|
1143
|
+
} catch (e) {
|
|
1144
|
+
}
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
async function detectLocalDebugger(debug) {
|
|
1148
|
+
var _a;
|
|
1149
|
+
const envUrl = resolveLocalDebuggerBaseUrl();
|
|
1150
|
+
if (envUrl) {
|
|
1151
|
+
if (debug) {
|
|
1152
|
+
console.log(`[raindrop-ai/claude-code] Local debugger configured via ${LOCAL_DEBUGGER_ENV_VAR}: ${envUrl}`);
|
|
1153
|
+
}
|
|
1154
|
+
return { url: envUrl, autoDetected: false };
|
|
1155
|
+
}
|
|
1156
|
+
const cached = readCache();
|
|
1157
|
+
if (cached) {
|
|
1158
|
+
if (debug) {
|
|
1159
|
+
console.log(`[raindrop-ai/claude-code] Local debugger cache hit: ${(_a = cached.url) != null ? _a : "not running"}`);
|
|
1160
|
+
}
|
|
1161
|
+
return { url: cached.url, autoDetected: cached.url !== null };
|
|
1162
|
+
}
|
|
1163
|
+
if (debug) {
|
|
1164
|
+
console.log(`[raindrop-ai/claude-code] Probing local debugger at localhost:${DEFAULT_PORT}...`);
|
|
1165
|
+
}
|
|
1166
|
+
const detected = await healthPing(DEFAULT_URL);
|
|
1167
|
+
writeCache(detected);
|
|
1168
|
+
if (detected && debug) {
|
|
1169
|
+
console.log(`[raindrop-ai/claude-code] Local debugger auto-detected at ${detected}`);
|
|
1170
|
+
}
|
|
1171
|
+
return { url: detected, autoDetected: detected !== null };
|
|
1172
|
+
}
|
|
1173
|
+
function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
1174
|
+
const url = `${baseUrl}events/track_partial`;
|
|
1175
|
+
if (debug) {
|
|
1176
|
+
console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
|
|
1177
|
+
}
|
|
1178
|
+
void fetch(url, {
|
|
1179
|
+
method: "POST",
|
|
1180
|
+
headers: { "Content-Type": "application/json" },
|
|
1181
|
+
body: JSON.stringify(payload)
|
|
1182
|
+
}).catch(() => {
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1030
1185
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1031
1186
|
0 && (module.exports = {
|
|
1032
1187
|
EventShipper,
|
|
1033
1188
|
PACKAGE_NAME,
|
|
1034
1189
|
PACKAGE_VERSION,
|
|
1035
1190
|
TraceShipper,
|
|
1191
|
+
detectLocalDebugger,
|
|
1036
1192
|
getConfigPath,
|
|
1037
1193
|
loadConfig,
|
|
1038
|
-
mapHookToRaindrop
|
|
1194
|
+
mapHookToRaindrop,
|
|
1195
|
+
mirrorEventToLocalDebugger,
|
|
1196
|
+
updateConfig
|
|
1039
1197
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -147,6 +147,8 @@ declare class TraceShipper$1 {
|
|
|
147
147
|
private queue;
|
|
148
148
|
private timer;
|
|
149
149
|
private inFlight;
|
|
150
|
+
/** URL of the local debugger (from RAINDROP_LOCAL_DEBUGGER env var). */
|
|
151
|
+
private localDebuggerUrl;
|
|
150
152
|
constructor(opts: TraceShipperOptions);
|
|
151
153
|
isDebugEnabled(): boolean;
|
|
152
154
|
private authHeaders;
|
|
@@ -219,11 +221,19 @@ declare class TraceShipper extends TraceShipper$1 {
|
|
|
219
221
|
enqueue(span: OtlpSpan): void;
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
interface ConfigFile {
|
|
225
|
+
write_key?: string;
|
|
226
|
+
api_url?: string;
|
|
227
|
+
user_id?: string;
|
|
228
|
+
debug?: boolean;
|
|
229
|
+
enabled?: boolean;
|
|
230
|
+
}
|
|
222
231
|
interface RaindropConfig {
|
|
223
232
|
writeKey: string;
|
|
224
233
|
endpoint: string;
|
|
225
234
|
userId: string;
|
|
226
235
|
debug: boolean;
|
|
236
|
+
enabled: boolean;
|
|
227
237
|
}
|
|
228
238
|
/**
|
|
229
239
|
* Load config with precedence (low -> high):
|
|
@@ -233,6 +243,7 @@ interface RaindropConfig {
|
|
|
233
243
|
*/
|
|
234
244
|
declare function loadConfig(): RaindropConfig;
|
|
235
245
|
declare function getConfigPath(): string;
|
|
246
|
+
declare function updateConfig(patch: Partial<ConfigFile>): void;
|
|
236
247
|
|
|
237
248
|
type SetupScope = "user" | "project";
|
|
238
249
|
|
|
@@ -270,4 +281,25 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
270
281
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
271
282
|
declare const PACKAGE_VERSION = "0.0.1";
|
|
272
283
|
|
|
273
|
-
|
|
284
|
+
interface LocalDebuggerResult {
|
|
285
|
+
/** The resolved base URL (e.g. "http://localhost:5899/v1/"), or null if not detected. */
|
|
286
|
+
url: string | null;
|
|
287
|
+
/** Whether the debugger was auto-detected (vs explicitly configured via env var). */
|
|
288
|
+
autoDetected: boolean;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Detect whether the local debugger is available.
|
|
292
|
+
*
|
|
293
|
+
* Resolution order:
|
|
294
|
+
* 1. RAINDROP_LOCAL_DEBUGGER env var — use directly (no health check, trust the user)
|
|
295
|
+
* 2. Cached probe result (within TTL)
|
|
296
|
+
* 3. Probe http://localhost:5899/health with a short timeout
|
|
297
|
+
*/
|
|
298
|
+
declare function detectLocalDebugger(debug: boolean): Promise<LocalDebuggerResult>;
|
|
299
|
+
/**
|
|
300
|
+
* Mirror an event payload to the local debugger's track_partial endpoint.
|
|
301
|
+
* Fire-and-forget — errors are silently swallowed.
|
|
302
|
+
*/
|
|
303
|
+
declare function mirrorEventToLocalDebugger(baseUrl: string, payload: Record<string, unknown>, debug: boolean): void;
|
|
304
|
+
|
|
305
|
+
export { EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SetupScope, TraceShipper, detectLocalDebugger, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, updateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -147,6 +147,8 @@ declare class TraceShipper$1 {
|
|
|
147
147
|
private queue;
|
|
148
148
|
private timer;
|
|
149
149
|
private inFlight;
|
|
150
|
+
/** URL of the local debugger (from RAINDROP_LOCAL_DEBUGGER env var). */
|
|
151
|
+
private localDebuggerUrl;
|
|
150
152
|
constructor(opts: TraceShipperOptions);
|
|
151
153
|
isDebugEnabled(): boolean;
|
|
152
154
|
private authHeaders;
|
|
@@ -219,11 +221,19 @@ declare class TraceShipper extends TraceShipper$1 {
|
|
|
219
221
|
enqueue(span: OtlpSpan): void;
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
interface ConfigFile {
|
|
225
|
+
write_key?: string;
|
|
226
|
+
api_url?: string;
|
|
227
|
+
user_id?: string;
|
|
228
|
+
debug?: boolean;
|
|
229
|
+
enabled?: boolean;
|
|
230
|
+
}
|
|
222
231
|
interface RaindropConfig {
|
|
223
232
|
writeKey: string;
|
|
224
233
|
endpoint: string;
|
|
225
234
|
userId: string;
|
|
226
235
|
debug: boolean;
|
|
236
|
+
enabled: boolean;
|
|
227
237
|
}
|
|
228
238
|
/**
|
|
229
239
|
* Load config with precedence (low -> high):
|
|
@@ -233,6 +243,7 @@ interface RaindropConfig {
|
|
|
233
243
|
*/
|
|
234
244
|
declare function loadConfig(): RaindropConfig;
|
|
235
245
|
declare function getConfigPath(): string;
|
|
246
|
+
declare function updateConfig(patch: Partial<ConfigFile>): void;
|
|
236
247
|
|
|
237
248
|
type SetupScope = "user" | "project";
|
|
238
249
|
|
|
@@ -270,4 +281,25 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
270
281
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
271
282
|
declare const PACKAGE_VERSION = "0.0.1";
|
|
272
283
|
|
|
273
|
-
|
|
284
|
+
interface LocalDebuggerResult {
|
|
285
|
+
/** The resolved base URL (e.g. "http://localhost:5899/v1/"), or null if not detected. */
|
|
286
|
+
url: string | null;
|
|
287
|
+
/** Whether the debugger was auto-detected (vs explicitly configured via env var). */
|
|
288
|
+
autoDetected: boolean;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Detect whether the local debugger is available.
|
|
292
|
+
*
|
|
293
|
+
* Resolution order:
|
|
294
|
+
* 1. RAINDROP_LOCAL_DEBUGGER env var — use directly (no health check, trust the user)
|
|
295
|
+
* 2. Cached probe result (within TTL)
|
|
296
|
+
* 3. Probe http://localhost:5899/health with a short timeout
|
|
297
|
+
*/
|
|
298
|
+
declare function detectLocalDebugger(debug: boolean): Promise<LocalDebuggerResult>;
|
|
299
|
+
/**
|
|
300
|
+
* Mirror an event payload to the local debugger's track_partial endpoint.
|
|
301
|
+
* Fire-and-forget — errors are silently swallowed.
|
|
302
|
+
*/
|
|
303
|
+
declare function mirrorEventToLocalDebugger(baseUrl: string, payload: Record<string, unknown>, debug: boolean): void;
|
|
304
|
+
|
|
305
|
+
export { EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SetupScope, TraceShipper, detectLocalDebugger, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, updateConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../core/dist/chunk-
|
|
1
|
+
// ../core/dist/chunk-4UCYIEH4.js
|
|
2
2
|
function getCrypto() {
|
|
3
3
|
const c = globalThis.crypto;
|
|
4
4
|
return c;
|
|
@@ -434,11 +434,28 @@ var EventShipper = class {
|
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
};
|
|
437
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
438
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
439
|
+
var _a, _b, _c;
|
|
440
|
+
const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
|
|
441
|
+
return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
|
|
442
|
+
}
|
|
443
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
444
|
+
var _a;
|
|
445
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
446
|
+
if (!baseUrl) return;
|
|
447
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
448
|
+
maxAttempts: 1,
|
|
449
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
450
|
+
sdkName: options.sdkName
|
|
451
|
+
}).catch(() => {
|
|
452
|
+
});
|
|
453
|
+
}
|
|
437
454
|
var TraceShipper = class {
|
|
438
455
|
constructor(opts) {
|
|
439
456
|
this.queue = [];
|
|
440
457
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
441
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
458
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
442
459
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
443
460
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
444
461
|
this.enabled = opts.enabled !== false;
|
|
@@ -451,6 +468,13 @@ var TraceShipper = class {
|
|
|
451
468
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
452
469
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
453
470
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
471
|
+
const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
|
|
472
|
+
if (localDebugger) {
|
|
473
|
+
this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
|
|
474
|
+
if (this.debug) {
|
|
475
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
454
478
|
}
|
|
455
479
|
isDebugEnabled() {
|
|
456
480
|
return this.debug;
|
|
@@ -467,7 +491,25 @@ var TraceShipper = class {
|
|
|
467
491
|
attrString("ai.operationId", args.operationId)
|
|
468
492
|
];
|
|
469
493
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
470
|
-
|
|
494
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
495
|
+
if (this.localDebuggerUrl) {
|
|
496
|
+
const openSpan = buildOtlpSpan({
|
|
497
|
+
ids: span.ids,
|
|
498
|
+
name: span.name,
|
|
499
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
500
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
501
|
+
// placeholder — will be updated on endSpan
|
|
502
|
+
attributes: span.attributes,
|
|
503
|
+
status: { code: SpanStatusCode.UNSET }
|
|
504
|
+
});
|
|
505
|
+
const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
|
|
506
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
507
|
+
baseUrl: this.localDebuggerUrl,
|
|
508
|
+
debug: false,
|
|
509
|
+
sdkName: this.sdkName
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
return span;
|
|
471
513
|
}
|
|
472
514
|
endSpan(span, extra) {
|
|
473
515
|
var _a, _b;
|
|
@@ -490,6 +532,14 @@ var TraceShipper = class {
|
|
|
490
532
|
status
|
|
491
533
|
});
|
|
492
534
|
this.enqueue(otlp);
|
|
535
|
+
if (this.localDebuggerUrl) {
|
|
536
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
537
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
538
|
+
baseUrl: this.localDebuggerUrl,
|
|
539
|
+
debug: false,
|
|
540
|
+
sdkName: this.sdkName
|
|
541
|
+
});
|
|
542
|
+
}
|
|
493
543
|
}
|
|
494
544
|
createSpan(args) {
|
|
495
545
|
var _a;
|
|
@@ -507,6 +557,14 @@ var TraceShipper = class {
|
|
|
507
557
|
status: args.status
|
|
508
558
|
});
|
|
509
559
|
this.enqueue(otlp);
|
|
560
|
+
if (this.localDebuggerUrl) {
|
|
561
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
562
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
563
|
+
baseUrl: this.localDebuggerUrl,
|
|
564
|
+
debug: false,
|
|
565
|
+
sdkName: this.sdkName
|
|
566
|
+
});
|
|
567
|
+
}
|
|
510
568
|
}
|
|
511
569
|
enqueue(span) {
|
|
512
570
|
if (!this.enabled) return;
|
|
@@ -625,12 +683,12 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
625
683
|
};
|
|
626
684
|
|
|
627
685
|
// src/config.ts
|
|
628
|
-
import { existsSync, readFileSync } from "fs";
|
|
686
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
629
687
|
import { homedir, userInfo } from "os";
|
|
630
|
-
import { join } from "path";
|
|
688
|
+
import { dirname, join } from "path";
|
|
631
689
|
var CONFIG_PATH = join(homedir(), ".config", "raindrop", "config.json");
|
|
632
690
|
function loadConfig() {
|
|
633
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
691
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
634
692
|
let file = {};
|
|
635
693
|
try {
|
|
636
694
|
if (existsSync(CONFIG_PATH)) {
|
|
@@ -649,15 +707,28 @@ function loadConfig() {
|
|
|
649
707
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : file.write_key) != null ? _b : "",
|
|
650
708
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : file.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
651
709
|
userId: (_f = (_e = process.env["RAINDROP_USER_ID"]) != null ? _e : file.user_id) != null ? _f : systemUser,
|
|
652
|
-
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false
|
|
710
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = file.debug) != null ? _g : false,
|
|
711
|
+
enabled: (_h = file.enabled) != null ? _h : true
|
|
653
712
|
};
|
|
654
713
|
}
|
|
655
714
|
function getConfigPath() {
|
|
656
715
|
return CONFIG_PATH;
|
|
657
716
|
}
|
|
717
|
+
function updateConfig(patch) {
|
|
718
|
+
const dir = dirname(CONFIG_PATH);
|
|
719
|
+
mkdirSync(dir, { recursive: true });
|
|
720
|
+
let existing = {};
|
|
721
|
+
try {
|
|
722
|
+
if (existsSync(CONFIG_PATH)) {
|
|
723
|
+
existing = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
724
|
+
}
|
|
725
|
+
} catch (e) {
|
|
726
|
+
}
|
|
727
|
+
writeFileSync(CONFIG_PATH, JSON.stringify({ ...existing, ...patch }, null, 2) + "\n", "utf-8");
|
|
728
|
+
}
|
|
658
729
|
|
|
659
730
|
// src/event-mapper.ts
|
|
660
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
731
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
661
732
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
662
733
|
import { tmpdir } from "os";
|
|
663
734
|
import { join as join2 } from "path";
|
|
@@ -679,7 +750,7 @@ function safeStringify(value) {
|
|
|
679
750
|
var STATE_DIR = join2(tmpdir(), "raindrop-claude-code");
|
|
680
751
|
function ensureStateDir() {
|
|
681
752
|
try {
|
|
682
|
-
|
|
753
|
+
mkdirSync2(STATE_DIR, { recursive: true });
|
|
683
754
|
} catch (e) {
|
|
684
755
|
}
|
|
685
756
|
}
|
|
@@ -697,7 +768,7 @@ function statePath(key) {
|
|
|
697
768
|
function writeState(key, value) {
|
|
698
769
|
try {
|
|
699
770
|
ensureStateDir();
|
|
700
|
-
|
|
771
|
+
writeFileSync2(statePath(key), value, "utf-8");
|
|
701
772
|
} catch (e) {
|
|
702
773
|
}
|
|
703
774
|
}
|
|
@@ -995,12 +1066,96 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
|
|
|
995
1066
|
}
|
|
996
1067
|
});
|
|
997
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
// src/local-debugger.ts
|
|
1071
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1072
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1073
|
+
import { join as join3 } from "path";
|
|
1074
|
+
var DEFAULT_PORT = 5899;
|
|
1075
|
+
var DEFAULT_URL = `http://localhost:${DEFAULT_PORT}/v1/`;
|
|
1076
|
+
var HEALTH_TIMEOUT_MS = 300;
|
|
1077
|
+
var CACHE_DIR = join3(tmpdir2(), "raindrop-claude-code");
|
|
1078
|
+
var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
|
|
1079
|
+
var CACHE_TTL_MS = 5e3;
|
|
1080
|
+
function readCache() {
|
|
1081
|
+
try {
|
|
1082
|
+
if (!existsSync3(CACHE_FILE)) return void 0;
|
|
1083
|
+
const data = JSON.parse(readFileSync3(CACHE_FILE, "utf-8"));
|
|
1084
|
+
if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
|
|
1085
|
+
return data;
|
|
1086
|
+
} catch (e) {
|
|
1087
|
+
return void 0;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
function writeCache(url) {
|
|
1091
|
+
try {
|
|
1092
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
1093
|
+
writeFileSync3(CACHE_FILE, JSON.stringify({ url, ts: Date.now() }), "utf-8");
|
|
1094
|
+
} catch (e) {
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
async function healthPing(baseUrl) {
|
|
1098
|
+
const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
|
|
1099
|
+
try {
|
|
1100
|
+
const controller = new AbortController();
|
|
1101
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
1102
|
+
const resp = await fetch(healthUrl, { signal: controller.signal });
|
|
1103
|
+
clearTimeout(timeout);
|
|
1104
|
+
if (resp.ok) {
|
|
1105
|
+
const body = await resp.json();
|
|
1106
|
+
if (body.ok) return baseUrl;
|
|
1107
|
+
}
|
|
1108
|
+
} catch (e) {
|
|
1109
|
+
}
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
async function detectLocalDebugger(debug) {
|
|
1113
|
+
var _a;
|
|
1114
|
+
const envUrl = resolveLocalDebuggerBaseUrl();
|
|
1115
|
+
if (envUrl) {
|
|
1116
|
+
if (debug) {
|
|
1117
|
+
console.log(`[raindrop-ai/claude-code] Local debugger configured via ${LOCAL_DEBUGGER_ENV_VAR}: ${envUrl}`);
|
|
1118
|
+
}
|
|
1119
|
+
return { url: envUrl, autoDetected: false };
|
|
1120
|
+
}
|
|
1121
|
+
const cached = readCache();
|
|
1122
|
+
if (cached) {
|
|
1123
|
+
if (debug) {
|
|
1124
|
+
console.log(`[raindrop-ai/claude-code] Local debugger cache hit: ${(_a = cached.url) != null ? _a : "not running"}`);
|
|
1125
|
+
}
|
|
1126
|
+
return { url: cached.url, autoDetected: cached.url !== null };
|
|
1127
|
+
}
|
|
1128
|
+
if (debug) {
|
|
1129
|
+
console.log(`[raindrop-ai/claude-code] Probing local debugger at localhost:${DEFAULT_PORT}...`);
|
|
1130
|
+
}
|
|
1131
|
+
const detected = await healthPing(DEFAULT_URL);
|
|
1132
|
+
writeCache(detected);
|
|
1133
|
+
if (detected && debug) {
|
|
1134
|
+
console.log(`[raindrop-ai/claude-code] Local debugger auto-detected at ${detected}`);
|
|
1135
|
+
}
|
|
1136
|
+
return { url: detected, autoDetected: detected !== null };
|
|
1137
|
+
}
|
|
1138
|
+
function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
1139
|
+
const url = `${baseUrl}events/track_partial`;
|
|
1140
|
+
if (debug) {
|
|
1141
|
+
console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
|
|
1142
|
+
}
|
|
1143
|
+
void fetch(url, {
|
|
1144
|
+
method: "POST",
|
|
1145
|
+
headers: { "Content-Type": "application/json" },
|
|
1146
|
+
body: JSON.stringify(payload)
|
|
1147
|
+
}).catch(() => {
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
998
1150
|
export {
|
|
999
1151
|
EventShipper2 as EventShipper,
|
|
1000
1152
|
PACKAGE_NAME,
|
|
1001
1153
|
PACKAGE_VERSION,
|
|
1002
1154
|
TraceShipper2 as TraceShipper,
|
|
1155
|
+
detectLocalDebugger,
|
|
1003
1156
|
getConfigPath,
|
|
1004
1157
|
loadConfig,
|
|
1005
|
-
mapHookToRaindrop
|
|
1158
|
+
mapHookToRaindrop,
|
|
1159
|
+
mirrorEventToLocalDebugger,
|
|
1160
|
+
updateConfig
|
|
1006
1161
|
};
|
package/package.json
CHANGED