@raindrop-ai/claude-code 0.0.3 → 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 +433 -46
- package/dist/index.cjs +423 -33
- package/dist/index.d.cts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +415 -28
- package/package.json +1 -1
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
|
}
|
|
@@ -796,15 +929,19 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
796
929
|
var _a;
|
|
797
930
|
const convoId = payload.session_id;
|
|
798
931
|
const baseProperties = {
|
|
932
|
+
...config.customProperties,
|
|
799
933
|
cwd: payload.cwd,
|
|
800
934
|
permission_mode: payload.permission_mode,
|
|
801
935
|
plugin_version: PACKAGE_VERSION,
|
|
936
|
+
sdk: PACKAGE_NAME,
|
|
937
|
+
sdk_version: PACKAGE_VERSION,
|
|
802
938
|
...payload.agent_id ? { agent_id: payload.agent_id } : {},
|
|
803
939
|
...payload.agent_type ? { agent_type: payload.agent_type } : {}
|
|
804
940
|
};
|
|
805
941
|
switch (payload.hook_event_name) {
|
|
806
942
|
case "SessionStart":
|
|
807
943
|
writeState(`model_${payload.session_id}`, (_a = payload.model) != null ? _a : "");
|
|
944
|
+
tryCaptureAppendSystemPrompt(payload.session_id);
|
|
808
945
|
break;
|
|
809
946
|
case "UserPromptSubmit":
|
|
810
947
|
await handleUserPromptSubmit(payload, convoId, config, baseProperties, eventShipper, traceShipper);
|
|
@@ -819,10 +956,13 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
819
956
|
handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
820
957
|
break;
|
|
821
958
|
case "Stop":
|
|
822
|
-
await
|
|
959
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
|
|
823
960
|
break;
|
|
824
961
|
case "StopFailure":
|
|
825
|
-
await
|
|
962
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
|
|
963
|
+
error: payload.error,
|
|
964
|
+
error_details: payload.error_details
|
|
965
|
+
});
|
|
826
966
|
break;
|
|
827
967
|
case "SubagentStart":
|
|
828
968
|
handleSubagentStart(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
@@ -833,6 +973,9 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
833
973
|
case "PermissionDenied":
|
|
834
974
|
handlePermissionDenied(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
835
975
|
break;
|
|
976
|
+
case "InstructionsLoaded":
|
|
977
|
+
handleInstructionsLoaded(payload);
|
|
978
|
+
break;
|
|
836
979
|
case "PostCompact":
|
|
837
980
|
await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
838
981
|
break;
|
|
@@ -841,6 +984,8 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
841
984
|
deleteState(turnEventKey(payload.session_id));
|
|
842
985
|
deleteState(rootSpanKey(payload.session_id));
|
|
843
986
|
deleteState(`model_${payload.session_id}`);
|
|
987
|
+
deleteState(appendSystemPromptKey(payload.session_id));
|
|
988
|
+
cleanupInstructions(payload.session_id);
|
|
844
989
|
break;
|
|
845
990
|
default:
|
|
846
991
|
if (config.debug) {
|
|
@@ -870,7 +1015,7 @@ async function handleUserPromptSubmit(payload, convoId, config, properties, even
|
|
|
870
1015
|
isPending: true,
|
|
871
1016
|
userId: config.userId,
|
|
872
1017
|
convoId,
|
|
873
|
-
eventName:
|
|
1018
|
+
eventName: config.eventName,
|
|
874
1019
|
input: payload.prompt,
|
|
875
1020
|
model,
|
|
876
1021
|
properties
|
|
@@ -990,7 +1135,7 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
|
|
|
990
1135
|
isPending: true,
|
|
991
1136
|
userId: config.userId,
|
|
992
1137
|
convoId: payload.session_id,
|
|
993
|
-
eventName:
|
|
1138
|
+
eventName: config.eventName,
|
|
994
1139
|
properties: {
|
|
995
1140
|
...properties,
|
|
996
1141
|
compaction_trigger: payload.trigger,
|
|
@@ -998,27 +1143,251 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
|
|
|
998
1143
|
}
|
|
999
1144
|
});
|
|
1000
1145
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
+
}
|
|
1212
|
+
}
|
|
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
|
+
}
|
|
1007
1259
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
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);
|
|
1370
|
+
await eventShipper.patch(eventId, {
|
|
1371
|
+
isPending: false,
|
|
1010
1372
|
userId: config.userId,
|
|
1373
|
+
convoId: payload.session_id,
|
|
1374
|
+
eventName: config.eventName,
|
|
1011
1375
|
output: payload.last_assistant_message,
|
|
1376
|
+
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1012
1377
|
properties: {
|
|
1013
1378
|
...properties,
|
|
1014
|
-
|
|
1015
|
-
|
|
1379
|
+
...transcriptProps,
|
|
1380
|
+
...instructions ? { system_instructions: truncate(instructions) } : {},
|
|
1381
|
+
...appendSysPrompt ? { append_system_prompt: truncate(appendSysPrompt) } : {},
|
|
1382
|
+
...extraProperties
|
|
1016
1383
|
}
|
|
1017
1384
|
});
|
|
1018
1385
|
}
|
|
1019
1386
|
async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
|
|
1020
|
-
await eventShipper.
|
|
1387
|
+
await eventShipper.patch(eventId, {
|
|
1388
|
+
isPending: false,
|
|
1021
1389
|
userId: config.userId,
|
|
1390
|
+
eventName: config.eventName,
|
|
1022
1391
|
properties: {
|
|
1023
1392
|
...properties,
|
|
1024
1393
|
session_end_reason: payload.reason
|
|
@@ -1027,16 +1396,16 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
|
|
|
1027
1396
|
}
|
|
1028
1397
|
|
|
1029
1398
|
// src/config.ts
|
|
1030
|
-
import { existsSync as
|
|
1399
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1031
1400
|
import { homedir, userInfo } from "os";
|
|
1032
1401
|
import { dirname, join as join2 } from "path";
|
|
1033
1402
|
var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
|
|
1034
1403
|
function loadConfig() {
|
|
1035
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1404
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
1036
1405
|
let file = {};
|
|
1037
1406
|
try {
|
|
1038
|
-
if (
|
|
1039
|
-
file = JSON.parse(
|
|
1407
|
+
if (existsSync3(CONFIG_PATH)) {
|
|
1408
|
+
file = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
|
|
1040
1409
|
}
|
|
1041
1410
|
} catch (e) {
|
|
1042
1411
|
}
|
|
@@ -1047,12 +1416,25 @@ function loadConfig() {
|
|
|
1047
1416
|
return "unknown";
|
|
1048
1417
|
}
|
|
1049
1418
|
})();
|
|
1419
|
+
let customProperties = (_a = file.custom_properties) != null ? _a : {};
|
|
1420
|
+
const envProps = process.env["RAINDROP_PROPERTIES"];
|
|
1421
|
+
if (envProps) {
|
|
1422
|
+
try {
|
|
1423
|
+
const parsed = JSON.parse(envProps);
|
|
1424
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1425
|
+
customProperties = { ...customProperties, ...parsed };
|
|
1426
|
+
}
|
|
1427
|
+
} catch (e) {
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1050
1430
|
return {
|
|
1051
|
-
writeKey: (
|
|
1052
|
-
endpoint: (
|
|
1053
|
-
userId: (
|
|
1054
|
-
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (
|
|
1055
|
-
enabled: (
|
|
1431
|
+
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
1432
|
+
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
1433
|
+
userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
|
|
1434
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
|
|
1435
|
+
enabled: (_i = file.enabled) != null ? _i : true,
|
|
1436
|
+
eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
|
|
1437
|
+
customProperties
|
|
1056
1438
|
};
|
|
1057
1439
|
}
|
|
1058
1440
|
function getConfigPath() {
|
|
@@ -1063,8 +1445,8 @@ function updateConfig(patch) {
|
|
|
1063
1445
|
mkdirSync2(dir, { recursive: true });
|
|
1064
1446
|
let existing = {};
|
|
1065
1447
|
try {
|
|
1066
|
-
if (
|
|
1067
|
-
existing = JSON.parse(
|
|
1448
|
+
if (existsSync3(CONFIG_PATH)) {
|
|
1449
|
+
existing = JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
|
|
1068
1450
|
}
|
|
1069
1451
|
} catch (e) {
|
|
1070
1452
|
}
|
|
@@ -1072,7 +1454,7 @@ function updateConfig(patch) {
|
|
|
1072
1454
|
}
|
|
1073
1455
|
|
|
1074
1456
|
// src/local-debugger.ts
|
|
1075
|
-
import { existsSync as
|
|
1457
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1076
1458
|
import { tmpdir as tmpdir2 } from "os";
|
|
1077
1459
|
import { join as join3 } from "path";
|
|
1078
1460
|
var DEFAULT_PORT = 5899;
|
|
@@ -1083,8 +1465,8 @@ var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
|
|
|
1083
1465
|
var CACHE_TTL_MS = 5e3;
|
|
1084
1466
|
function readCache() {
|
|
1085
1467
|
try {
|
|
1086
|
-
if (!
|
|
1087
|
-
const data = JSON.parse(
|
|
1468
|
+
if (!existsSync4(CACHE_FILE)) return void 0;
|
|
1469
|
+
const data = JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
1088
1470
|
if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
|
|
1089
1471
|
return data;
|
|
1090
1472
|
} catch (e) {
|
|
@@ -1153,7 +1535,7 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
|
1153
1535
|
}
|
|
1154
1536
|
|
|
1155
1537
|
// src/hook-handler.ts
|
|
1156
|
-
import { existsSync as
|
|
1538
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
1157
1539
|
import { join as join4 } from "path";
|
|
1158
1540
|
import { tmpdir as tmpdir3 } from "os";
|
|
1159
1541
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -1162,8 +1544,8 @@ function getOtlpTraceId(sessionId) {
|
|
|
1162
1544
|
try {
|
|
1163
1545
|
const key = `rootspan_${sessionId}`.replace(/[^a-zA-Z0-9_\-]/g, "_");
|
|
1164
1546
|
const filePath = join4(STATE_DIR2, key);
|
|
1165
|
-
if (!
|
|
1166
|
-
const ctx = JSON.parse(
|
|
1547
|
+
if (!existsSync5(filePath)) return void 0;
|
|
1548
|
+
const ctx = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
1167
1549
|
if (!ctx.traceIdB64) return void 0;
|
|
1168
1550
|
return Buffer.from(ctx.traceIdB64, "base64").toString("hex");
|
|
1169
1551
|
} catch (e) {
|
|
@@ -1223,7 +1605,9 @@ async function handleHook() {
|
|
|
1223
1605
|
}
|
|
1224
1606
|
const mapperConfig = {
|
|
1225
1607
|
userId: config.userId,
|
|
1226
|
-
debug: config.debug
|
|
1608
|
+
debug: config.debug,
|
|
1609
|
+
eventName: config.eventName,
|
|
1610
|
+
customProperties: config.customProperties
|
|
1227
1611
|
};
|
|
1228
1612
|
const eventShipper = new EventShipper2({
|
|
1229
1613
|
writeKey: config.writeKey,
|
|
@@ -1280,11 +1664,11 @@ async function handleHook() {
|
|
|
1280
1664
|
}
|
|
1281
1665
|
|
|
1282
1666
|
// src/setup.ts
|
|
1283
|
-
import { existsSync as
|
|
1667
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1284
1668
|
import { homedir as homedir2, userInfo as userInfo2 } from "os";
|
|
1285
1669
|
import { dirname as dirname2, join as join5, resolve } from "path";
|
|
1286
1670
|
import { createInterface } from "readline";
|
|
1287
|
-
import { execSync } from "child_process";
|
|
1671
|
+
import { execSync as execSync2 } from "child_process";
|
|
1288
1672
|
function getClaudeSettingsPath(scope, cwd) {
|
|
1289
1673
|
if (scope === "project") {
|
|
1290
1674
|
return resolve(cwd, ".claude", "settings.json");
|
|
@@ -1303,8 +1687,9 @@ var CORE_HOOK_EVENTS = [
|
|
|
1303
1687
|
"SessionEnd"
|
|
1304
1688
|
];
|
|
1305
1689
|
var VERSIONED_HOOK_EVENTS = [
|
|
1306
|
-
{ minVersion: "2.1.78", events: ["StopFailure"] },
|
|
1307
1690
|
{ minVersion: "2.1.76", events: ["PostCompact"] },
|
|
1691
|
+
{ minVersion: "2.1.78", events: ["StopFailure"] },
|
|
1692
|
+
{ minVersion: "2.1.78", events: ["InstructionsLoaded"] },
|
|
1308
1693
|
{ minVersion: "2.1.89", events: ["PermissionDenied"] }
|
|
1309
1694
|
];
|
|
1310
1695
|
function parseVersion(version) {
|
|
@@ -1323,7 +1708,7 @@ function isVersionAtLeast(current, required) {
|
|
|
1323
1708
|
}
|
|
1324
1709
|
function detectClaudeCodeVersion() {
|
|
1325
1710
|
try {
|
|
1326
|
-
const output =
|
|
1711
|
+
const output = execSync2("claude --version", {
|
|
1327
1712
|
encoding: "utf-8",
|
|
1328
1713
|
timeout: 5e3,
|
|
1329
1714
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -1402,8 +1787,8 @@ async function runSetup(args2) {
|
|
|
1402
1787
|
mkdirSync4(configDir, { recursive: true });
|
|
1403
1788
|
let existingConfig = {};
|
|
1404
1789
|
try {
|
|
1405
|
-
if (
|
|
1406
|
-
existingConfig = JSON.parse(
|
|
1790
|
+
if (existsSync6(configPath)) {
|
|
1791
|
+
existingConfig = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
1407
1792
|
}
|
|
1408
1793
|
} catch (e) {
|
|
1409
1794
|
}
|
|
@@ -1427,9 +1812,9 @@ async function runSetup(args2) {
|
|
|
1427
1812
|
const settingsDir = dirname2(settingsPath);
|
|
1428
1813
|
mkdirSync4(settingsDir, { recursive: true });
|
|
1429
1814
|
let settings = {};
|
|
1430
|
-
if (
|
|
1815
|
+
if (existsSync6(settingsPath)) {
|
|
1431
1816
|
try {
|
|
1432
|
-
settings = JSON.parse(
|
|
1817
|
+
settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
1433
1818
|
} catch (e) {
|
|
1434
1819
|
console.warn(` Warning: could not parse ${settingsPath}, creating fresh`);
|
|
1435
1820
|
settings = {};
|
|
@@ -1459,7 +1844,7 @@ async function runSetup(args2) {
|
|
|
1459
1844
|
console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
|
|
1460
1845
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1461
1846
|
try {
|
|
1462
|
-
|
|
1847
|
+
execSync2(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
|
|
1463
1848
|
} catch (e) {
|
|
1464
1849
|
console.log(`
|
|
1465
1850
|
Warning: 'raindrop-claude-code' is not in your PATH.
|
|
@@ -1569,6 +1954,8 @@ async function main() {
|
|
|
1569
1954
|
Environment:
|
|
1570
1955
|
RAINDROP_WRITE_KEY API write key (alternative to --write-key or config file)
|
|
1571
1956
|
RAINDROP_USER_ID User ID override
|
|
1957
|
+
RAINDROP_EVENT_NAME Custom event name (default: "claude_code_session")
|
|
1958
|
+
RAINDROP_PROPERTIES JSON object merged into every event's properties
|
|
1572
1959
|
RAINDROP_API_URL Custom API endpoint
|
|
1573
1960
|
RAINDROP_LOCAL_DEBUGGER Local debugger URL (auto-detected if running on :5899)
|
|
1574
1961
|
RAINDROP_DEBUG Set to "true" for verbose logging
|