@raindrop-ai/claude-code 0.0.4 → 0.0.5
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 +4 -0
- package/dist/cli.js +400 -37
- package/dist/index.cjs +395 -25
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +387 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,10 @@ This saves your write key and configures Claude Code hooks. Every session will n
|
|
|
20
20
|
|
|
21
21
|
- Every prompt turn as a separate event, grouped by session
|
|
22
22
|
- Tool calls with inputs, outputs, and real durations
|
|
23
|
+
- Token usage per turn and per session (input, output, cache read, cache creation)
|
|
24
|
+
- Model name, service tier, and stop reason
|
|
25
|
+
- CLAUDE.md and rules file contents
|
|
26
|
+
- `--append-system-prompt` / `--append-system-prompt-file` content (best-effort)
|
|
23
27
|
- Subagent spawns and completions
|
|
24
28
|
- Permission denials and context compaction
|
|
25
29
|
- Nested trace view (tools under root, subagent tools under subagent)
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/event-mapper.ts
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
4
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync, unlinkSync, writeFileSync } from "fs";
|
|
5
|
+
import { execSync } from "child_process";
|
|
5
6
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6
7
|
import { tmpdir } from "os";
|
|
7
8
|
import { join } from "path";
|
|
@@ -653,7 +654,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
653
654
|
|
|
654
655
|
// src/package-info.ts
|
|
655
656
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
656
|
-
var PACKAGE_VERSION = "0.0.
|
|
657
|
+
var PACKAGE_VERSION = "0.0.5";
|
|
657
658
|
|
|
658
659
|
// src/shipper.ts
|
|
659
660
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -690,6 +691,138 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
690
691
|
}
|
|
691
692
|
};
|
|
692
693
|
|
|
694
|
+
// src/transcript.ts
|
|
695
|
+
import { existsSync, readFileSync } from "fs";
|
|
696
|
+
function parseTranscript(transcriptPath) {
|
|
697
|
+
var _a, _b, _c, _d, _e;
|
|
698
|
+
try {
|
|
699
|
+
if (!existsSync(transcriptPath)) return void 0;
|
|
700
|
+
const content = readFileSync(transcriptPath, "utf-8");
|
|
701
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
702
|
+
const summary = {
|
|
703
|
+
totalInputTokens: 0,
|
|
704
|
+
totalOutputTokens: 0,
|
|
705
|
+
totalCacheReadTokens: 0,
|
|
706
|
+
totalCacheCreationTokens: 0,
|
|
707
|
+
turnCount: 0,
|
|
708
|
+
toolsUsed: [],
|
|
709
|
+
hasThinking: false
|
|
710
|
+
};
|
|
711
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
712
|
+
let lastUsage;
|
|
713
|
+
for (const line of lines) {
|
|
714
|
+
let entry;
|
|
715
|
+
try {
|
|
716
|
+
entry = JSON.parse(line);
|
|
717
|
+
} catch (e) {
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (entry.type === "system" && entry.subtype === "turn_duration") {
|
|
721
|
+
const durationMs = entry["durationMs"];
|
|
722
|
+
if (typeof durationMs === "number") {
|
|
723
|
+
summary.totalDurationMs = ((_a = summary.totalDurationMs) != null ? _a : 0) + durationMs;
|
|
724
|
+
}
|
|
725
|
+
const version = entry["version"];
|
|
726
|
+
if (typeof version === "string") {
|
|
727
|
+
summary.codeVersion = version;
|
|
728
|
+
}
|
|
729
|
+
const branch = entry["gitBranch"];
|
|
730
|
+
if (typeof branch === "string") {
|
|
731
|
+
summary.gitBranch = branch;
|
|
732
|
+
}
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (entry.type !== "assistant") continue;
|
|
736
|
+
const msg = entry.message;
|
|
737
|
+
if (!msg) continue;
|
|
738
|
+
summary.turnCount++;
|
|
739
|
+
if (msg.model) {
|
|
740
|
+
summary.model = msg.model;
|
|
741
|
+
}
|
|
742
|
+
if (msg.stop_reason) {
|
|
743
|
+
summary.stopReason = msg.stop_reason;
|
|
744
|
+
}
|
|
745
|
+
const entryVersion = entry["version"];
|
|
746
|
+
if (typeof entryVersion === "string") {
|
|
747
|
+
summary.codeVersion = entryVersion;
|
|
748
|
+
}
|
|
749
|
+
const entryBranch = entry["gitBranch"];
|
|
750
|
+
if (typeof entryBranch === "string") {
|
|
751
|
+
summary.gitBranch = entryBranch;
|
|
752
|
+
}
|
|
753
|
+
if (msg.usage) {
|
|
754
|
+
const u = msg.usage;
|
|
755
|
+
summary.totalInputTokens += (_b = u.input_tokens) != null ? _b : 0;
|
|
756
|
+
summary.totalOutputTokens += (_c = u.output_tokens) != null ? _c : 0;
|
|
757
|
+
summary.totalCacheReadTokens += (_d = u.cache_read_input_tokens) != null ? _d : 0;
|
|
758
|
+
summary.totalCacheCreationTokens += (_e = u.cache_creation_input_tokens) != null ? _e : 0;
|
|
759
|
+
if (u.service_tier) {
|
|
760
|
+
summary.serviceTier = u.service_tier;
|
|
761
|
+
}
|
|
762
|
+
lastUsage = u;
|
|
763
|
+
}
|
|
764
|
+
if (Array.isArray(msg.content)) {
|
|
765
|
+
for (const block of msg.content) {
|
|
766
|
+
if (block.type === "tool_use" && block.name) {
|
|
767
|
+
toolNames.add(block.name);
|
|
768
|
+
}
|
|
769
|
+
if (block.type === "thinking") {
|
|
770
|
+
summary.hasThinking = true;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (lastUsage) {
|
|
776
|
+
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
777
|
+
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
778
|
+
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
779
|
+
}
|
|
780
|
+
summary.toolsUsed = [...toolNames].sort();
|
|
781
|
+
return summary;
|
|
782
|
+
} catch (e) {
|
|
783
|
+
return void 0;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function transcriptToProperties(summary) {
|
|
787
|
+
var _a, _b;
|
|
788
|
+
const props = {};
|
|
789
|
+
props["ai.usage.prompt_tokens"] = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
|
|
790
|
+
props["ai.usage.completion_tokens"] = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
|
|
791
|
+
if (summary.lastTurnCacheReadTokens !== void 0) {
|
|
792
|
+
props["ai.usage.cache_read_tokens"] = summary.lastTurnCacheReadTokens;
|
|
793
|
+
}
|
|
794
|
+
props["ai.usage.session_total.prompt_tokens"] = summary.totalInputTokens;
|
|
795
|
+
props["ai.usage.session_total.completion_tokens"] = summary.totalOutputTokens;
|
|
796
|
+
props["ai.usage.session_total.cache_read_tokens"] = summary.totalCacheReadTokens;
|
|
797
|
+
props["ai.usage.session_total.cache_creation_tokens"] = summary.totalCacheCreationTokens;
|
|
798
|
+
if (summary.model) {
|
|
799
|
+
props["ai.model"] = summary.model;
|
|
800
|
+
}
|
|
801
|
+
if (summary.serviceTier) {
|
|
802
|
+
props["ai.service_tier"] = summary.serviceTier;
|
|
803
|
+
}
|
|
804
|
+
if (summary.stopReason) {
|
|
805
|
+
props["ai.stop_reason"] = summary.stopReason;
|
|
806
|
+
}
|
|
807
|
+
if (summary.toolsUsed.length > 0) {
|
|
808
|
+
props["ai.tools_used"] = summary.toolsUsed.join(", ");
|
|
809
|
+
}
|
|
810
|
+
props["ai.turn_count"] = summary.turnCount;
|
|
811
|
+
if (summary.totalDurationMs !== void 0) {
|
|
812
|
+
props["ai.duration_ms"] = summary.totalDurationMs;
|
|
813
|
+
}
|
|
814
|
+
if (summary.codeVersion) {
|
|
815
|
+
props["claude_code.version"] = summary.codeVersion;
|
|
816
|
+
}
|
|
817
|
+
if (summary.gitBranch) {
|
|
818
|
+
props["git.branch"] = summary.gitBranch;
|
|
819
|
+
}
|
|
820
|
+
if (summary.hasThinking) {
|
|
821
|
+
props["ai.has_thinking"] = true;
|
|
822
|
+
}
|
|
823
|
+
return props;
|
|
824
|
+
}
|
|
825
|
+
|
|
693
826
|
// src/event-mapper.ts
|
|
694
827
|
var MAX_ATTR_LENGTH = 32768;
|
|
695
828
|
function truncate(value) {
|
|
@@ -734,8 +867,8 @@ function writeState(key, value) {
|
|
|
734
867
|
function readState(key) {
|
|
735
868
|
try {
|
|
736
869
|
const filePath = statePath(key);
|
|
737
|
-
if (!
|
|
738
|
-
return
|
|
870
|
+
if (!existsSync2(filePath)) return void 0;
|
|
871
|
+
return readFileSync2(filePath, "utf-8");
|
|
739
872
|
} catch (e) {
|
|
740
873
|
return void 0;
|
|
741
874
|
}
|
|
@@ -800,12 +933,15 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
800
933
|
cwd: payload.cwd,
|
|
801
934
|
permission_mode: payload.permission_mode,
|
|
802
935
|
plugin_version: PACKAGE_VERSION,
|
|
936
|
+
sdk: PACKAGE_NAME,
|
|
937
|
+
sdk_version: PACKAGE_VERSION,
|
|
803
938
|
...payload.agent_id ? { agent_id: payload.agent_id } : {},
|
|
804
939
|
...payload.agent_type ? { agent_type: payload.agent_type } : {}
|
|
805
940
|
};
|
|
806
941
|
switch (payload.hook_event_name) {
|
|
807
942
|
case "SessionStart":
|
|
808
943
|
writeState(`model_${payload.session_id}`, (_a = payload.model) != null ? _a : "");
|
|
944
|
+
tryCaptureAppendSystemPrompt(payload.session_id);
|
|
809
945
|
break;
|
|
810
946
|
case "UserPromptSubmit":
|
|
811
947
|
await handleUserPromptSubmit(payload, convoId, config, baseProperties, eventShipper, traceShipper);
|
|
@@ -820,10 +956,13 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
820
956
|
handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
821
957
|
break;
|
|
822
958
|
case "Stop":
|
|
823
|
-
await
|
|
959
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
|
|
824
960
|
break;
|
|
825
961
|
case "StopFailure":
|
|
826
|
-
await
|
|
962
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
|
|
963
|
+
error: payload.error,
|
|
964
|
+
error_details: payload.error_details
|
|
965
|
+
});
|
|
827
966
|
break;
|
|
828
967
|
case "SubagentStart":
|
|
829
968
|
handleSubagentStart(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
@@ -834,6 +973,9 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
834
973
|
case "PermissionDenied":
|
|
835
974
|
handlePermissionDenied(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
836
975
|
break;
|
|
976
|
+
case "InstructionsLoaded":
|
|
977
|
+
handleInstructionsLoaded(payload);
|
|
978
|
+
break;
|
|
837
979
|
case "PostCompact":
|
|
838
980
|
await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
839
981
|
break;
|
|
@@ -842,6 +984,8 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
842
984
|
deleteState(turnEventKey(payload.session_id));
|
|
843
985
|
deleteState(rootSpanKey(payload.session_id));
|
|
844
986
|
deleteState(`model_${payload.session_id}`);
|
|
987
|
+
deleteState(appendSystemPromptKey(payload.session_id));
|
|
988
|
+
cleanupInstructions(payload.session_id);
|
|
845
989
|
break;
|
|
846
990
|
default:
|
|
847
991
|
if (config.debug) {
|
|
@@ -999,25 +1143,243 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
|
|
|
999
1143
|
}
|
|
1000
1144
|
});
|
|
1001
1145
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1146
|
+
function handleInstructionsLoaded(payload) {
|
|
1147
|
+
var _a;
|
|
1148
|
+
if (!payload.file_path || !payload.session_id) return;
|
|
1149
|
+
try {
|
|
1150
|
+
if (existsSync2(payload.file_path)) {
|
|
1151
|
+
const content = readFileSync2(payload.file_path, "utf-8");
|
|
1152
|
+
const MAX_INSTRUCTIONS_LENGTH = 8192;
|
|
1153
|
+
const truncated = content.length > MAX_INSTRUCTIONS_LENGTH ? content.slice(0, MAX_INSTRUCTIONS_LENGTH) + "\n...[truncated]" : content;
|
|
1154
|
+
const fileKey = safeKey(payload.file_path);
|
|
1155
|
+
const header = `# ${(_a = payload.memory_type) != null ? _a : "instructions"}: ${payload.file_path}
|
|
1156
|
+
`;
|
|
1157
|
+
writeState(
|
|
1158
|
+
`instr_${payload.session_id}_${fileKey}`,
|
|
1159
|
+
header + truncated
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
} catch (e) {
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function gatherInstructions(sessionId) {
|
|
1166
|
+
try {
|
|
1167
|
+
if (!existsSync2(STATE_DIR)) return void 0;
|
|
1168
|
+
const prefix = safeKey(`instr_${sessionId}_`);
|
|
1169
|
+
const files = readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix));
|
|
1170
|
+
if (files.length === 0) return void 0;
|
|
1171
|
+
const parts = [];
|
|
1172
|
+
for (const file of files.sort()) {
|
|
1173
|
+
try {
|
|
1174
|
+
parts.push(readFileSync2(join(STATE_DIR, file), "utf-8"));
|
|
1175
|
+
} catch (e) {
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
|
|
1180
|
+
} catch (e) {
|
|
1181
|
+
return void 0;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
function cleanupInstructions(sessionId) {
|
|
1185
|
+
try {
|
|
1186
|
+
if (!existsSync2(STATE_DIR)) return;
|
|
1187
|
+
const prefix = safeKey(`instr_${sessionId}_`);
|
|
1188
|
+
for (const file of readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix))) {
|
|
1189
|
+
try {
|
|
1190
|
+
unlinkSync(join(STATE_DIR, file));
|
|
1191
|
+
} catch (e) {
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
} catch (e) {
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
var APPEND_FLAG = "--append-system-prompt";
|
|
1198
|
+
var APPEND_FILE_FLAG = "--append-system-prompt-file";
|
|
1199
|
+
var MAX_ANCESTOR_DEPTH = 15;
|
|
1200
|
+
var PS_TIMEOUT_MS = 3e3;
|
|
1201
|
+
function appendSystemPromptKey(sessionId) {
|
|
1202
|
+
return `append_sysprompt_${sessionId}`;
|
|
1203
|
+
}
|
|
1204
|
+
function tryCaptureAppendSystemPrompt(sessionId) {
|
|
1205
|
+
try {
|
|
1206
|
+
const content = readAncestorAppendSystemPrompt();
|
|
1207
|
+
if (content) {
|
|
1208
|
+
writeState(appendSystemPromptKey(sessionId), content);
|
|
1209
|
+
}
|
|
1210
|
+
} catch (e) {
|
|
1211
|
+
}
|
|
1010
1212
|
}
|
|
1011
|
-
|
|
1213
|
+
function readAncestorAppendSystemPrompt() {
|
|
1214
|
+
let pid;
|
|
1215
|
+
try {
|
|
1216
|
+
pid = process.ppid;
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
return void 0;
|
|
1219
|
+
}
|
|
1220
|
+
if (!pid || pid <= 1) return void 0;
|
|
1221
|
+
for (let depth = 0; depth < MAX_ANCESTOR_DEPTH && pid != null && pid > 1; depth++) {
|
|
1222
|
+
try {
|
|
1223
|
+
const args2 = getProcessArgs(pid);
|
|
1224
|
+
if (args2 && args2.length > 0) {
|
|
1225
|
+
const content = extractAppendSystemPrompt(args2);
|
|
1226
|
+
if (content) return content;
|
|
1227
|
+
}
|
|
1228
|
+
} catch (e) {
|
|
1229
|
+
}
|
|
1230
|
+
try {
|
|
1231
|
+
pid = getParentPid(pid);
|
|
1232
|
+
} catch (e) {
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return void 0;
|
|
1237
|
+
}
|
|
1238
|
+
function getProcessArgs(pid) {
|
|
1239
|
+
if (process.platform === "linux") {
|
|
1240
|
+
try {
|
|
1241
|
+
const raw = readFileSync2(`/proc/${pid}/cmdline`, "utf-8");
|
|
1242
|
+
const args2 = raw.split("\0").filter(Boolean);
|
|
1243
|
+
return args2.length > 0 ? args2 : void 0;
|
|
1244
|
+
} catch (e) {
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
try {
|
|
1248
|
+
const output = execSync(`ps -ww -o args= -p ${pid}`, {
|
|
1249
|
+
encoding: "utf-8",
|
|
1250
|
+
timeout: PS_TIMEOUT_MS,
|
|
1251
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1252
|
+
});
|
|
1253
|
+
const trimmed = output.trim();
|
|
1254
|
+
if (!trimmed) return void 0;
|
|
1255
|
+
return trimmed.split(/\s+/);
|
|
1256
|
+
} catch (e) {
|
|
1257
|
+
return void 0;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
function getParentPid(pid) {
|
|
1261
|
+
try {
|
|
1262
|
+
const output = execSync(`ps -o ppid= -p ${pid}`, {
|
|
1263
|
+
encoding: "utf-8",
|
|
1264
|
+
timeout: PS_TIMEOUT_MS,
|
|
1265
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1266
|
+
});
|
|
1267
|
+
const ppid = parseInt(output.trim(), 10);
|
|
1268
|
+
return Number.isFinite(ppid) && ppid > 0 ? ppid : void 0;
|
|
1269
|
+
} catch (e) {
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function extractAppendSystemPrompt(args2) {
|
|
1274
|
+
const parts = [];
|
|
1275
|
+
for (let i = 0; i < args2.length; i++) {
|
|
1276
|
+
const arg = args2[i];
|
|
1277
|
+
try {
|
|
1278
|
+
if (arg === APPEND_FILE_FLAG && i + 1 < args2.length) {
|
|
1279
|
+
const filePath = args2[i + 1];
|
|
1280
|
+
i++;
|
|
1281
|
+
try {
|
|
1282
|
+
if (existsSync2(filePath)) {
|
|
1283
|
+
parts.push(readFileSync2(filePath, "utf-8"));
|
|
1284
|
+
}
|
|
1285
|
+
} catch (e) {
|
|
1286
|
+
}
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
if (arg.startsWith(APPEND_FILE_FLAG + "=")) {
|
|
1290
|
+
const filePath = arg.slice(APPEND_FILE_FLAG.length + 1);
|
|
1291
|
+
try {
|
|
1292
|
+
if (filePath && existsSync2(filePath)) {
|
|
1293
|
+
parts.push(readFileSync2(filePath, "utf-8"));
|
|
1294
|
+
}
|
|
1295
|
+
} catch (e) {
|
|
1296
|
+
}
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
if (arg === APPEND_FLAG && i + 1 < args2.length) {
|
|
1300
|
+
const valueParts = [];
|
|
1301
|
+
for (let j = i + 1; j < args2.length; j++) {
|
|
1302
|
+
if (args2[j].startsWith("--")) break;
|
|
1303
|
+
valueParts.push(args2[j]);
|
|
1304
|
+
i = j;
|
|
1305
|
+
}
|
|
1306
|
+
if (valueParts.length > 0) {
|
|
1307
|
+
parts.push(valueParts.join(" "));
|
|
1308
|
+
}
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
if (arg.startsWith(APPEND_FLAG + "=") && !arg.startsWith(APPEND_FILE_FLAG)) {
|
|
1312
|
+
const value = arg.slice(APPEND_FLAG.length + 1);
|
|
1313
|
+
if (value) {
|
|
1314
|
+
parts.push(value);
|
|
1315
|
+
}
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
} catch (e) {
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
1323
|
+
}
|
|
1324
|
+
function readAppendSystemPrompt(sessionId) {
|
|
1325
|
+
try {
|
|
1326
|
+
return readState(appendSystemPromptKey(sessionId)) || void 0;
|
|
1327
|
+
} catch (e) {
|
|
1328
|
+
return void 0;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1332
|
+
var _a, _b;
|
|
1333
|
+
if (!payload.transcript_path) {
|
|
1334
|
+
return { summary: void 0, props: {} };
|
|
1335
|
+
}
|
|
1336
|
+
try {
|
|
1337
|
+
const summary = parseTranscript(payload.transcript_path);
|
|
1338
|
+
if (!summary) return { summary: void 0, props: {} };
|
|
1339
|
+
const props = transcriptToProperties(summary);
|
|
1340
|
+
const spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
|
|
1341
|
+
const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
|
|
1342
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1343
|
+
const parent = getParentContext(payload);
|
|
1344
|
+
const now = nowUnixNanoString();
|
|
1345
|
+
traceShipper.createSpan({
|
|
1346
|
+
name: summary.model,
|
|
1347
|
+
eventId,
|
|
1348
|
+
parent,
|
|
1349
|
+
startTimeUnixNano: now,
|
|
1350
|
+
endTimeUnixNano: now,
|
|
1351
|
+
attributes: [
|
|
1352
|
+
attrString("ai.operationId", "generateText"),
|
|
1353
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1354
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1355
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1356
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1357
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1358
|
+
]
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
return { summary, props };
|
|
1362
|
+
} catch (e) {
|
|
1363
|
+
return { summary: void 0, props: {} };
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1367
|
+
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1368
|
+
const instructions = gatherInstructions(payload.session_id);
|
|
1369
|
+
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1012
1370
|
await eventShipper.patch(eventId, {
|
|
1013
1371
|
isPending: false,
|
|
1014
1372
|
userId: config.userId,
|
|
1373
|
+
convoId: payload.session_id,
|
|
1015
1374
|
eventName: config.eventName,
|
|
1016
1375
|
output: payload.last_assistant_message,
|
|
1376
|
+
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1017
1377
|
properties: {
|
|
1018
1378
|
...properties,
|
|
1019
|
-
|
|
1020
|
-
|
|
1379
|
+
...transcriptProps,
|
|
1380
|
+
...instructions ? { system_instructions: truncate(instructions) } : {},
|
|
1381
|
+
...appendSysPrompt ? { append_system_prompt: truncate(appendSysPrompt) } : {},
|
|
1382
|
+
...extraProperties
|
|
1021
1383
|
}
|
|
1022
1384
|
});
|
|
1023
1385
|
}
|
|
@@ -1034,7 +1396,7 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
|
|
|
1034
1396
|
}
|
|
1035
1397
|
|
|
1036
1398
|
// src/config.ts
|
|
1037
|
-
import { existsSync as
|
|
1399
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1038
1400
|
import { homedir, userInfo } from "os";
|
|
1039
1401
|
import { dirname, join as join2 } from "path";
|
|
1040
1402
|
var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
|
|
@@ -1042,8 +1404,8 @@ function loadConfig() {
|
|
|
1042
1404
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
1043
1405
|
let file = {};
|
|
1044
1406
|
try {
|
|
1045
|
-
if (
|
|
1046
|
-
file = JSON.parse(
|
|
1407
|
+
if (existsSync3(CONFIG_PATH)) {
|
|
1408
|
+
file = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
|
|
1047
1409
|
}
|
|
1048
1410
|
} catch (e) {
|
|
1049
1411
|
}
|
|
@@ -1083,8 +1445,8 @@ function updateConfig(patch) {
|
|
|
1083
1445
|
mkdirSync2(dir, { recursive: true });
|
|
1084
1446
|
let existing = {};
|
|
1085
1447
|
try {
|
|
1086
|
-
if (
|
|
1087
|
-
existing = JSON.parse(
|
|
1448
|
+
if (existsSync3(CONFIG_PATH)) {
|
|
1449
|
+
existing = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
|
|
1088
1450
|
}
|
|
1089
1451
|
} catch (e) {
|
|
1090
1452
|
}
|
|
@@ -1092,7 +1454,7 @@ function updateConfig(patch) {
|
|
|
1092
1454
|
}
|
|
1093
1455
|
|
|
1094
1456
|
// src/local-debugger.ts
|
|
1095
|
-
import { existsSync as
|
|
1457
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1096
1458
|
import { tmpdir as tmpdir2 } from "os";
|
|
1097
1459
|
import { join as join3 } from "path";
|
|
1098
1460
|
var DEFAULT_PORT = 5899;
|
|
@@ -1103,8 +1465,8 @@ var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
|
|
|
1103
1465
|
var CACHE_TTL_MS = 5e3;
|
|
1104
1466
|
function readCache() {
|
|
1105
1467
|
try {
|
|
1106
|
-
if (!
|
|
1107
|
-
const data = JSON.parse(
|
|
1468
|
+
if (!existsSync4(CACHE_FILE)) return void 0;
|
|
1469
|
+
const data = JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
1108
1470
|
if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
|
|
1109
1471
|
return data;
|
|
1110
1472
|
} catch (e) {
|
|
@@ -1173,7 +1535,7 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
|
1173
1535
|
}
|
|
1174
1536
|
|
|
1175
1537
|
// src/hook-handler.ts
|
|
1176
|
-
import { existsSync as
|
|
1538
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
1177
1539
|
import { join as join4 } from "path";
|
|
1178
1540
|
import { tmpdir as tmpdir3 } from "os";
|
|
1179
1541
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -1182,8 +1544,8 @@ function getOtlpTraceId(sessionId) {
|
|
|
1182
1544
|
try {
|
|
1183
1545
|
const key = `rootspan_${sessionId}`.replace(/[^a-zA-Z0-9_\-]/g, "_");
|
|
1184
1546
|
const filePath = join4(STATE_DIR2, key);
|
|
1185
|
-
if (!
|
|
1186
|
-
const ctx = JSON.parse(
|
|
1547
|
+
if (!existsSync5(filePath)) return void 0;
|
|
1548
|
+
const ctx = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
1187
1549
|
if (!ctx.traceIdB64) return void 0;
|
|
1188
1550
|
return Buffer.from(ctx.traceIdB64, "base64").toString("hex");
|
|
1189
1551
|
} catch (e) {
|
|
@@ -1302,11 +1664,11 @@ async function handleHook() {
|
|
|
1302
1664
|
}
|
|
1303
1665
|
|
|
1304
1666
|
// src/setup.ts
|
|
1305
|
-
import { existsSync as
|
|
1667
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1306
1668
|
import { homedir as homedir2, userInfo as userInfo2 } from "os";
|
|
1307
1669
|
import { dirname as dirname2, join as join5, resolve } from "path";
|
|
1308
1670
|
import { createInterface } from "readline";
|
|
1309
|
-
import { execSync } from "child_process";
|
|
1671
|
+
import { execSync as execSync2 } from "child_process";
|
|
1310
1672
|
function getClaudeSettingsPath(scope, cwd) {
|
|
1311
1673
|
if (scope === "project") {
|
|
1312
1674
|
return resolve(cwd, ".claude", "settings.json");
|
|
@@ -1325,8 +1687,9 @@ var CORE_HOOK_EVENTS = [
|
|
|
1325
1687
|
"SessionEnd"
|
|
1326
1688
|
];
|
|
1327
1689
|
var VERSIONED_HOOK_EVENTS = [
|
|
1328
|
-
{ minVersion: "2.1.78", events: ["StopFailure"] },
|
|
1329
1690
|
{ minVersion: "2.1.76", events: ["PostCompact"] },
|
|
1691
|
+
{ minVersion: "2.1.78", events: ["StopFailure"] },
|
|
1692
|
+
{ minVersion: "2.1.78", events: ["InstructionsLoaded"] },
|
|
1330
1693
|
{ minVersion: "2.1.89", events: ["PermissionDenied"] }
|
|
1331
1694
|
];
|
|
1332
1695
|
function parseVersion(version) {
|
|
@@ -1345,7 +1708,7 @@ function isVersionAtLeast(current, required) {
|
|
|
1345
1708
|
}
|
|
1346
1709
|
function detectClaudeCodeVersion() {
|
|
1347
1710
|
try {
|
|
1348
|
-
const output =
|
|
1711
|
+
const output = execSync2("claude --version", {
|
|
1349
1712
|
encoding: "utf-8",
|
|
1350
1713
|
timeout: 5e3,
|
|
1351
1714
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -1424,8 +1787,8 @@ async function runSetup(args2) {
|
|
|
1424
1787
|
mkdirSync4(configDir, { recursive: true });
|
|
1425
1788
|
let existingConfig = {};
|
|
1426
1789
|
try {
|
|
1427
|
-
if (
|
|
1428
|
-
existingConfig = JSON.parse(
|
|
1790
|
+
if (existsSync6(configPath)) {
|
|
1791
|
+
existingConfig = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
1429
1792
|
}
|
|
1430
1793
|
} catch (e) {
|
|
1431
1794
|
}
|
|
@@ -1449,9 +1812,9 @@ async function runSetup(args2) {
|
|
|
1449
1812
|
const settingsDir = dirname2(settingsPath);
|
|
1450
1813
|
mkdirSync4(settingsDir, { recursive: true });
|
|
1451
1814
|
let settings = {};
|
|
1452
|
-
if (
|
|
1815
|
+
if (existsSync6(settingsPath)) {
|
|
1453
1816
|
try {
|
|
1454
|
-
settings = JSON.parse(
|
|
1817
|
+
settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
1455
1818
|
} catch (e) {
|
|
1456
1819
|
console.warn(` Warning: could not parse ${settingsPath}, creating fresh`);
|
|
1457
1820
|
settings = {};
|
|
@@ -1481,7 +1844,7 @@ async function runSetup(args2) {
|
|
|
1481
1844
|
console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
|
|
1482
1845
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1483
1846
|
try {
|
|
1484
|
-
|
|
1847
|
+
execSync2(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
|
|
1485
1848
|
} catch (e) {
|
|
1486
1849
|
console.log(`
|
|
1487
1850
|
Warning: 'raindrop-claude-code' is not in your PATH.
|