@raindrop-ai/claude-code 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1006 @@
1
+ // ../core/dist/chunk-H6VSZSLN.js
2
+ function getCrypto() {
3
+ const c = globalThis.crypto;
4
+ return c;
5
+ }
6
+ function randomBytes(length) {
7
+ const cryptoObj = getCrypto();
8
+ const out = new Uint8Array(length);
9
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
10
+ cryptoObj.getRandomValues(out);
11
+ return out;
12
+ }
13
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
14
+ return out;
15
+ }
16
+ function base64Encode(bytes) {
17
+ const maybeBuffer = globalThis.Buffer;
18
+ if (maybeBuffer) {
19
+ return maybeBuffer.from(bytes).toString("base64");
20
+ }
21
+ let binary = "";
22
+ for (let i2 = 0; i2 < bytes.length; i2++) {
23
+ binary += String.fromCharCode(bytes[i2]);
24
+ }
25
+ const btoaFn = globalThis.btoa;
26
+ if (typeof btoaFn === "function") return btoaFn(binary);
27
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28
+ let out = "";
29
+ let i = 0;
30
+ while (i < binary.length) {
31
+ const c1 = binary.charCodeAt(i++) & 255;
32
+ const c2 = i < binary.length ? binary.charCodeAt(i++) & 255 : NaN;
33
+ const c3 = i < binary.length ? binary.charCodeAt(i++) & 255 : NaN;
34
+ const e1 = c1 >> 2;
35
+ const e2 = (c1 & 3) << 4 | (Number.isNaN(c2) ? 0 : c2 >> 4);
36
+ const e3 = Number.isNaN(c2) ? 64 : (c2 & 15) << 2 | (Number.isNaN(c3) ? 0 : c3 >> 6);
37
+ const e4 = Number.isNaN(c3) ? 64 : c3 & 63;
38
+ out += alphabet.charAt(e1);
39
+ out += alphabet.charAt(e2);
40
+ out += e3 === 64 ? "=" : alphabet.charAt(e3);
41
+ out += e4 === 64 ? "=" : alphabet.charAt(e4);
42
+ }
43
+ return out;
44
+ }
45
+ function wait(ms) {
46
+ return new Promise((resolve) => setTimeout(resolve, ms));
47
+ }
48
+ function formatEndpoint(endpoint) {
49
+ if (!endpoint) return void 0;
50
+ return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
51
+ }
52
+ function parseRetryAfter(headers) {
53
+ var _a;
54
+ const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
55
+ if (!value) return void 0;
56
+ const asNumber = Number(value);
57
+ if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
58
+ const asDate = new Date(value).getTime();
59
+ if (!Number.isNaN(asDate)) {
60
+ const delta = asDate - Date.now();
61
+ return delta > 0 ? delta : 0;
62
+ }
63
+ return void 0;
64
+ }
65
+ function getRetryDelayMs(attemptNumber, previousError) {
66
+ if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
67
+ const v = previousError.retryAfterMs;
68
+ if (typeof v === "number") return Math.max(0, v);
69
+ }
70
+ if (attemptNumber <= 1) return 0;
71
+ const base = 500;
72
+ const factor = Math.pow(2, attemptNumber - 2);
73
+ return base * factor;
74
+ }
75
+ async function withRetry(operation, opName, opts) {
76
+ const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
77
+ let lastError = void 0;
78
+ for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
79
+ if (attemptNumber > 1) {
80
+ const delay = getRetryDelayMs(attemptNumber, lastError);
81
+ if (opts.debug) {
82
+ console.warn(
83
+ `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
84
+ );
85
+ }
86
+ if (delay > 0) await wait(delay);
87
+ } else if (opts.debug) {
88
+ console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
89
+ }
90
+ try {
91
+ return await operation();
92
+ } catch (err) {
93
+ lastError = err;
94
+ if (opts.debug) {
95
+ const msg = err instanceof Error ? err.message : String(err);
96
+ console.warn(
97
+ `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
98
+ );
99
+ }
100
+ if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
101
+ break;
102
+ if (attemptNumber === opts.maxAttempts) break;
103
+ }
104
+ }
105
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
106
+ }
107
+ async function postJson(url, body, headers, opts) {
108
+ const opName = `POST ${url}`;
109
+ await withRetry(
110
+ async () => {
111
+ const resp = await fetch(url, {
112
+ method: "POST",
113
+ headers: {
114
+ "Content-Type": "application/json",
115
+ ...headers
116
+ },
117
+ body: JSON.stringify(body)
118
+ });
119
+ if (!resp.ok) {
120
+ const text = await resp.text().catch(() => "");
121
+ const err = new Error(
122
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
123
+ );
124
+ const retryAfterMs = parseRetryAfter(resp.headers);
125
+ if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
126
+ err.retryable = resp.status === 429 || resp.status >= 500;
127
+ throw err;
128
+ }
129
+ },
130
+ opName,
131
+ opts
132
+ );
133
+ }
134
+ var SpanStatusCode = {
135
+ UNSET: 0,
136
+ OK: 1,
137
+ ERROR: 2
138
+ };
139
+ function createSpanIds(parent) {
140
+ const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
141
+ const spanId = base64Encode(randomBytes(8));
142
+ return {
143
+ traceIdB64: traceId,
144
+ spanIdB64: spanId,
145
+ parentSpanIdB64: parent ? parent.spanIdB64 : void 0
146
+ };
147
+ }
148
+ function nowUnixNanoString() {
149
+ return Date.now().toString() + "000000";
150
+ }
151
+ function attrString(key, value) {
152
+ if (value === void 0) return void 0;
153
+ return { key, value: { stringValue: value } };
154
+ }
155
+ function attrInt(key, value) {
156
+ if (value === void 0) return void 0;
157
+ if (!Number.isFinite(value)) return void 0;
158
+ return { key, value: { intValue: String(Math.trunc(value)) } };
159
+ }
160
+ function buildOtlpSpan(args) {
161
+ const attrs = args.attributes.filter((x) => x !== void 0);
162
+ const span = {
163
+ traceId: args.ids.traceIdB64,
164
+ spanId: args.ids.spanIdB64,
165
+ name: args.name,
166
+ startTimeUnixNano: args.startTimeUnixNano,
167
+ endTimeUnixNano: args.endTimeUnixNano
168
+ };
169
+ if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
170
+ if (attrs.length) span.attributes = attrs;
171
+ if (args.status) span.status = args.status;
172
+ return span;
173
+ }
174
+ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", serviceVersion = "0.0.0") {
175
+ return {
176
+ resourceSpans: [
177
+ {
178
+ resource: {
179
+ attributes: [{ key: "service.name", value: { stringValue: serviceName } }]
180
+ },
181
+ scopeSpans: [
182
+ {
183
+ scope: { name: serviceName, version: serviceVersion },
184
+ spans
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ };
190
+ }
191
+ function mergePatches(target, source) {
192
+ var _a, _b, _c, _d;
193
+ const out = { ...target, ...source };
194
+ if (target.properties || source.properties) {
195
+ out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
196
+ }
197
+ if (target.attachments || source.attachments) {
198
+ out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
199
+ }
200
+ return out;
201
+ }
202
+ var EventShipper = class {
203
+ constructor(opts) {
204
+ this.buffers = /* @__PURE__ */ new Map();
205
+ this.sticky = /* @__PURE__ */ new Map();
206
+ this.timers = /* @__PURE__ */ new Map();
207
+ this.inFlight = /* @__PURE__ */ new Set();
208
+ var _a, _b, _c, _d, _e, _f, _g;
209
+ this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
210
+ this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
211
+ this.enabled = opts.enabled !== false;
212
+ this.debug = opts.debug;
213
+ this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
214
+ this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
215
+ this.prefix = `[raindrop-ai/${this.sdkName}]`;
216
+ this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
217
+ const isNode = typeof process !== "undefined" && typeof process.version === "string";
218
+ this.context = {
219
+ library: {
220
+ name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
221
+ version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
222
+ },
223
+ metadata: {
224
+ jsRuntime: isNode ? "node" : "web",
225
+ ...isNode ? { nodeVersion: process.version } : {}
226
+ }
227
+ };
228
+ }
229
+ isDebugEnabled() {
230
+ return this.debug;
231
+ }
232
+ authHeaders() {
233
+ return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
234
+ }
235
+ async patch(eventId, patch) {
236
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
237
+ if (!this.enabled) return;
238
+ if (!eventId || !eventId.trim()) return;
239
+ if (this.debug) {
240
+ console.log(`${this.prefix} queue patch`, {
241
+ eventId,
242
+ userId: patch.userId,
243
+ convoId: patch.convoId,
244
+ eventName: patch.eventName,
245
+ hasInput: typeof patch.input === "string" && patch.input.length > 0,
246
+ hasOutput: typeof patch.output === "string" && patch.output.length > 0,
247
+ attachments: (_b = (_a = patch.attachments) == null ? void 0 : _a.length) != null ? _b : 0,
248
+ isPending: patch.isPending
249
+ });
250
+ }
251
+ const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
252
+ const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
253
+ const merged = mergePatches(existing, patch);
254
+ merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
255
+ this.buffers.set(eventId, merged);
256
+ this.sticky.set(eventId, {
257
+ userId: (_h = merged.userId) != null ? _h : sticky.userId,
258
+ convoId: (_i = merged.convoId) != null ? _i : sticky.convoId,
259
+ eventName: (_j = merged.eventName) != null ? _j : sticky.eventName,
260
+ isPending: (_k = merged.isPending) != null ? _k : sticky.isPending
261
+ });
262
+ const t = this.timers.get(eventId);
263
+ if (t) clearTimeout(t);
264
+ if (merged.isPending === false) {
265
+ await this.flushOne(eventId);
266
+ return;
267
+ }
268
+ const timeout = setTimeout(() => {
269
+ void this.flushOne(eventId).catch(() => {
270
+ });
271
+ }, this.partialFlushMs);
272
+ this.timers.set(eventId, timeout);
273
+ }
274
+ async finish(eventId, patch) {
275
+ await this.patch(eventId, { ...patch, isPending: false });
276
+ }
277
+ async flush() {
278
+ if (!this.enabled) return;
279
+ const ids = [...this.buffers.keys()];
280
+ await Promise.all(ids.map((id) => this.flushOne(id)));
281
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
282
+ })));
283
+ }
284
+ async shutdown() {
285
+ for (const t of this.timers.values()) clearTimeout(t);
286
+ this.timers.clear();
287
+ await this.flush();
288
+ }
289
+ async trackSignal(signal) {
290
+ var _a, _b;
291
+ if (!this.enabled) return;
292
+ const body = [
293
+ {
294
+ event_id: signal.eventId,
295
+ signal_name: signal.name,
296
+ signal_type: (_a = signal.type) != null ? _a : "default",
297
+ timestamp: signal.timestamp,
298
+ sentiment: signal.sentiment,
299
+ attachment_id: signal.attachmentId,
300
+ properties: {
301
+ ...(_b = signal.properties) != null ? _b : {},
302
+ ...signal.comment ? { comment: signal.comment } : {},
303
+ ...signal.after ? { after: signal.after } : {}
304
+ }
305
+ }
306
+ ];
307
+ const url = `${this.baseUrl}signals/track`;
308
+ try {
309
+ await postJson(url, body, this.authHeaders(), {
310
+ maxAttempts: 3,
311
+ debug: this.debug,
312
+ sdkName: this.sdkName
313
+ });
314
+ } catch (err) {
315
+ const msg = err instanceof Error ? err.message : String(err);
316
+ console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
317
+ }
318
+ }
319
+ async identify(users) {
320
+ if (!this.enabled) return;
321
+ const list = Array.isArray(users) ? users : [users];
322
+ const body = list.filter((user) => {
323
+ if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
324
+ if (this.debug) {
325
+ console.warn(`${this.prefix} skipping identify: missing userId`);
326
+ }
327
+ return false;
328
+ }
329
+ return true;
330
+ }).map((user) => {
331
+ var _a;
332
+ return {
333
+ user_id: user.userId,
334
+ traits: (_a = user.traits) != null ? _a : {}
335
+ };
336
+ });
337
+ if (body.length === 0) return;
338
+ const url = `${this.baseUrl}users/identify`;
339
+ try {
340
+ await postJson(url, body, this.authHeaders(), {
341
+ maxAttempts: 3,
342
+ debug: this.debug,
343
+ sdkName: this.sdkName
344
+ });
345
+ } catch (err) {
346
+ const msg = err instanceof Error ? err.message : String(err);
347
+ console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
348
+ }
349
+ }
350
+ async flushOne(eventId) {
351
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
352
+ if (!this.enabled) return;
353
+ const timer = this.timers.get(eventId);
354
+ if (timer) {
355
+ clearTimeout(timer);
356
+ this.timers.delete(eventId);
357
+ }
358
+ const accumulated = this.buffers.get(eventId);
359
+ this.buffers.delete(eventId);
360
+ if (!accumulated) return;
361
+ const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
362
+ const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
363
+ const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
364
+ if (!userId) {
365
+ if (this.debug) {
366
+ console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
367
+ }
368
+ this.sticky.delete(eventId);
369
+ return;
370
+ }
371
+ const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
372
+ const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
373
+ const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
374
+ const payload = {
375
+ event_id: eventId,
376
+ user_id: userId,
377
+ event: eventName,
378
+ timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
379
+ ai_data: {
380
+ input: accumulated.input,
381
+ output: accumulated.output,
382
+ model: accumulated.model,
383
+ convo_id: convoId
384
+ },
385
+ properties: {
386
+ ...restProperties,
387
+ ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
388
+ $context: this.context
389
+ },
390
+ attachments: accumulated.attachments,
391
+ is_pending: isPending
392
+ };
393
+ const url = `${this.baseUrl}events/track_partial`;
394
+ if (this.debug) {
395
+ console.log(`${this.prefix} sending track_partial`, {
396
+ eventId,
397
+ eventName,
398
+ userId,
399
+ convoId,
400
+ isPending,
401
+ inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
402
+ outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
403
+ attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
404
+ attachmentKinds: (_m = (_l = accumulated.attachments) == null ? void 0 : _l.map((a) => ({
405
+ type: a.type,
406
+ role: a.role,
407
+ name: a.name,
408
+ valuePreview: a.value.slice(0, 60)
409
+ }))) != null ? _m : [],
410
+ endpoint: url
411
+ });
412
+ }
413
+ const p = postJson(url, payload, this.authHeaders(), {
414
+ maxAttempts: 3,
415
+ debug: this.debug,
416
+ sdkName: this.sdkName
417
+ });
418
+ this.inFlight.add(p);
419
+ try {
420
+ try {
421
+ await p;
422
+ if (this.debug) {
423
+ console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
424
+ }
425
+ } catch (err) {
426
+ const msg = err instanceof Error ? err.message : String(err);
427
+ console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
428
+ }
429
+ } finally {
430
+ this.inFlight.delete(p);
431
+ }
432
+ if (!isPending) {
433
+ this.sticky.delete(eventId);
434
+ }
435
+ }
436
+ };
437
+ var TraceShipper = class {
438
+ constructor(opts) {
439
+ this.queue = [];
440
+ this.inFlight = /* @__PURE__ */ new Set();
441
+ var _a, _b, _c, _d, _e, _f, _g, _h;
442
+ this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
443
+ this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
444
+ this.enabled = opts.enabled !== false;
445
+ this.debug = opts.debug;
446
+ this.debugSpans = opts.debugSpans === true;
447
+ this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
448
+ this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
449
+ this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
450
+ this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
451
+ this.prefix = `[raindrop-ai/${this.sdkName}]`;
452
+ this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
453
+ this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
454
+ }
455
+ isDebugEnabled() {
456
+ return this.debug;
457
+ }
458
+ authHeaders() {
459
+ return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
460
+ }
461
+ startSpan(args) {
462
+ var _a, _b;
463
+ const ids = createSpanIds(args.parent);
464
+ const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
465
+ const attrs = [
466
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
467
+ attrString("ai.operationId", args.operationId)
468
+ ];
469
+ if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
470
+ return { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
471
+ }
472
+ endSpan(span, extra) {
473
+ var _a, _b;
474
+ if (span.endTimeUnixNano) return;
475
+ span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
476
+ if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
477
+ span.attributes.push(...extra.attributes);
478
+ }
479
+ let status = extra == null ? void 0 : extra.status;
480
+ if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
481
+ const message = extra.error instanceof Error ? extra.error.message : String(extra.error);
482
+ status = { code: SpanStatusCode.ERROR, message };
483
+ }
484
+ const otlp = buildOtlpSpan({
485
+ ids: span.ids,
486
+ name: span.name,
487
+ startTimeUnixNano: span.startTimeUnixNano,
488
+ endTimeUnixNano: span.endTimeUnixNano,
489
+ attributes: span.attributes,
490
+ status
491
+ });
492
+ this.enqueue(otlp);
493
+ }
494
+ createSpan(args) {
495
+ var _a;
496
+ const ids = createSpanIds(args.parent);
497
+ const attrs = [
498
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
499
+ ];
500
+ if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
501
+ const otlp = buildOtlpSpan({
502
+ ids,
503
+ name: args.name,
504
+ startTimeUnixNano: args.startTimeUnixNano,
505
+ endTimeUnixNano: args.endTimeUnixNano,
506
+ attributes: attrs,
507
+ status: args.status
508
+ });
509
+ this.enqueue(otlp);
510
+ }
511
+ enqueue(span) {
512
+ if (!this.enabled) return;
513
+ if (this.debugSpans) {
514
+ const short = (s) => s ? s.slice(-8) : "none";
515
+ console.log(
516
+ `${this.prefix}[span] name=${span.name} trace=${short(span.traceId)} span=${short(span.spanId)} parent=${short(
517
+ span.parentSpanId
518
+ )}`
519
+ );
520
+ }
521
+ if (this.queue.length >= this.maxQueueSize) {
522
+ this.queue.shift();
523
+ }
524
+ this.queue.push(span);
525
+ if (this.queue.length >= this.maxBatchSize) {
526
+ void this.flush().catch(() => {
527
+ });
528
+ return;
529
+ }
530
+ if (!this.timer) {
531
+ this.timer = setTimeout(() => {
532
+ this.timer = void 0;
533
+ void this.flush().catch(() => {
534
+ });
535
+ }, this.flushIntervalMs);
536
+ }
537
+ }
538
+ async flush() {
539
+ if (!this.enabled) return;
540
+ if (this.timer) {
541
+ clearTimeout(this.timer);
542
+ this.timer = void 0;
543
+ }
544
+ while (this.queue.length > 0) {
545
+ const batch = this.queue.splice(0, this.maxBatchSize);
546
+ const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
547
+ const url = `${this.baseUrl}traces`;
548
+ if (this.debug) {
549
+ console.log(`${this.prefix} sending traces batch`, {
550
+ spans: batch.length,
551
+ endpoint: url
552
+ });
553
+ }
554
+ const p = postJson(url, body, this.authHeaders(), {
555
+ maxAttempts: 3,
556
+ debug: this.debug,
557
+ sdkName: this.sdkName
558
+ });
559
+ this.inFlight.add(p);
560
+ try {
561
+ try {
562
+ await p;
563
+ if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
564
+ } catch (err) {
565
+ const msg = err instanceof Error ? err.message : String(err);
566
+ console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
567
+ }
568
+ } finally {
569
+ this.inFlight.delete(p);
570
+ }
571
+ }
572
+ }
573
+ async shutdown() {
574
+ if (this.timer) {
575
+ clearTimeout(this.timer);
576
+ this.timer = void 0;
577
+ }
578
+ await this.flush();
579
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
580
+ })));
581
+ }
582
+ };
583
+
584
+ // ../core/dist/index.node.js
585
+ import { AsyncLocalStorage } from "async_hooks";
586
+ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
587
+
588
+ // src/package-info.ts
589
+ var PACKAGE_NAME = "@raindrop-ai/claude-code";
590
+ var PACKAGE_VERSION = "0.0.1";
591
+
592
+ // src/shipper.ts
593
+ var EventShipper2 = class extends EventShipper {
594
+ constructor(opts) {
595
+ var _a, _b, _c, _d;
596
+ super({
597
+ ...opts,
598
+ sdkName: (_a = opts.sdkName) != null ? _a : "claude-code",
599
+ libraryName: (_b = opts.libraryName) != null ? _b : PACKAGE_NAME,
600
+ libraryVersion: (_c = opts.libraryVersion) != null ? _c : PACKAGE_VERSION,
601
+ defaultEventName: (_d = opts.defaultEventName) != null ? _d : "claude_code_session"
602
+ });
603
+ }
604
+ };
605
+ var TraceShipper2 = class extends TraceShipper {
606
+ constructor(opts) {
607
+ var _a, _b, _c;
608
+ super({
609
+ ...opts,
610
+ sdkName: (_a = opts.sdkName) != null ? _a : "claude-code",
611
+ serviceName: (_b = opts.serviceName) != null ? _b : "raindrop.claude-code",
612
+ serviceVersion: (_c = opts.serviceVersion) != null ? _c : PACKAGE_VERSION
613
+ });
614
+ }
615
+ enqueue(span) {
616
+ var _a;
617
+ const attrs = (_a = span.attributes) != null ? _a : [];
618
+ attrs.unshift(
619
+ { key: "span.id", value: { stringValue: span.spanId } },
620
+ ...span.parentSpanId ? [{ key: "span.parent.id", value: { stringValue: span.parentSpanId } }] : []
621
+ );
622
+ span.attributes = attrs;
623
+ super.enqueue(span);
624
+ }
625
+ };
626
+
627
+ // src/config.ts
628
+ import { existsSync, readFileSync } from "fs";
629
+ import { homedir, userInfo } from "os";
630
+ import { join } from "path";
631
+ var CONFIG_PATH = join(homedir(), ".config", "raindrop", "config.json");
632
+ function loadConfig() {
633
+ var _a, _b, _c, _d, _e, _f, _g;
634
+ let file = {};
635
+ try {
636
+ if (existsSync(CONFIG_PATH)) {
637
+ file = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
638
+ }
639
+ } catch (e) {
640
+ }
641
+ const systemUser = (() => {
642
+ try {
643
+ return userInfo().username;
644
+ } catch (e) {
645
+ return "unknown";
646
+ }
647
+ })();
648
+ return {
649
+ writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : file.write_key) != null ? _b : "",
650
+ endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : file.api_url) != null ? _d : "https://api.raindrop.ai/v1",
651
+ 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
653
+ };
654
+ }
655
+ function getConfigPath() {
656
+ return CONFIG_PATH;
657
+ }
658
+
659
+ // src/event-mapper.ts
660
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
661
+ import { randomUUID as randomUUID2 } from "crypto";
662
+ import { tmpdir } from "os";
663
+ import { join as join2 } from "path";
664
+ var MAX_ATTR_LENGTH = 32768;
665
+ function truncate(value) {
666
+ if (value === void 0) return void 0;
667
+ if (value.length <= MAX_ATTR_LENGTH) return value;
668
+ const suffix = "\n...[truncated]";
669
+ return value.slice(0, MAX_ATTR_LENGTH - suffix.length) + suffix;
670
+ }
671
+ function safeStringify(value) {
672
+ if (value === void 0 || value === null) return void 0;
673
+ try {
674
+ return truncate(JSON.stringify(value));
675
+ } catch (e) {
676
+ return truncate(String(value));
677
+ }
678
+ }
679
+ var STATE_DIR = join2(tmpdir(), "raindrop-claude-code");
680
+ function ensureStateDir() {
681
+ try {
682
+ mkdirSync(STATE_DIR, { recursive: true });
683
+ } catch (e) {
684
+ }
685
+ }
686
+ function safeKey(key) {
687
+ return key.replace(/[^a-zA-Z0-9_\-]/g, "_");
688
+ }
689
+ function statePath(key) {
690
+ const sanitized = safeKey(key);
691
+ const full = join2(STATE_DIR, sanitized);
692
+ if (!full.startsWith(STATE_DIR)) {
693
+ throw new Error("Path traversal detected");
694
+ }
695
+ return full;
696
+ }
697
+ function writeState(key, value) {
698
+ try {
699
+ ensureStateDir();
700
+ writeFileSync(statePath(key), value, "utf-8");
701
+ } catch (e) {
702
+ }
703
+ }
704
+ function readState(key) {
705
+ try {
706
+ const filePath = statePath(key);
707
+ if (!existsSync2(filePath)) return void 0;
708
+ return readFileSync2(filePath, "utf-8");
709
+ } catch (e) {
710
+ return void 0;
711
+ }
712
+ }
713
+ function deleteState(key) {
714
+ try {
715
+ unlinkSync(statePath(key));
716
+ } catch (e) {
717
+ }
718
+ }
719
+ function turnEventKey(sessionId) {
720
+ return `event_${sessionId}`;
721
+ }
722
+ function saveCurrentEventId(sessionId, eventId) {
723
+ writeState(turnEventKey(sessionId), eventId);
724
+ }
725
+ function getCurrentEventId(sessionId) {
726
+ const saved = readState(turnEventKey(sessionId));
727
+ if (saved) return saved;
728
+ return `cc_${sessionId}`;
729
+ }
730
+ function saveSpanContext(key, ctx) {
731
+ writeState(key, JSON.stringify(ctx));
732
+ }
733
+ function readSpanContext(key) {
734
+ const raw = readState(key);
735
+ if (!raw) return void 0;
736
+ try {
737
+ return JSON.parse(raw);
738
+ } catch (e) {
739
+ return void 0;
740
+ }
741
+ }
742
+ function rootSpanKey(sessionId) {
743
+ return `rootspan_${sessionId}`;
744
+ }
745
+ function subagentSpanKey(agentId) {
746
+ return `subagentspan_${agentId}`;
747
+ }
748
+ function getParentContext(payload) {
749
+ if (payload.agent_id) {
750
+ const sub = readSpanContext(subagentSpanKey(payload.agent_id));
751
+ if (sub) return sub;
752
+ }
753
+ return readSpanContext(rootSpanKey(payload.session_id));
754
+ }
755
+ function saveTimestamp(key) {
756
+ writeState(key, String(Date.now()));
757
+ }
758
+ function readTimestamp(key) {
759
+ const raw = readState(key);
760
+ deleteState(key);
761
+ if (!raw) return void 0;
762
+ const ts = parseInt(raw, 10);
763
+ return Number.isFinite(ts) ? ts : void 0;
764
+ }
765
+ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
766
+ var _a;
767
+ const convoId = payload.session_id;
768
+ const baseProperties = {
769
+ cwd: payload.cwd,
770
+ permission_mode: payload.permission_mode,
771
+ plugin_version: PACKAGE_VERSION,
772
+ ...payload.agent_id ? { agent_id: payload.agent_id } : {},
773
+ ...payload.agent_type ? { agent_type: payload.agent_type } : {}
774
+ };
775
+ switch (payload.hook_event_name) {
776
+ case "SessionStart":
777
+ writeState(`model_${payload.session_id}`, (_a = payload.model) != null ? _a : "");
778
+ break;
779
+ case "UserPromptSubmit":
780
+ await handleUserPromptSubmit(payload, convoId, config, baseProperties, eventShipper, traceShipper);
781
+ break;
782
+ case "PreToolUse":
783
+ handlePreToolUse(payload);
784
+ break;
785
+ case "PostToolUse":
786
+ handlePostToolUse(payload, getCurrentEventId(payload.session_id), traceShipper);
787
+ break;
788
+ case "PostToolUseFailure":
789
+ handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
790
+ break;
791
+ case "Stop":
792
+ await handleStop(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
793
+ break;
794
+ case "StopFailure":
795
+ await handleStopFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
796
+ break;
797
+ case "SubagentStart":
798
+ handleSubagentStart(payload, getCurrentEventId(payload.session_id), traceShipper);
799
+ break;
800
+ case "SubagentStop":
801
+ handleSubagentStop(payload, getCurrentEventId(payload.session_id), traceShipper);
802
+ break;
803
+ case "PermissionDenied":
804
+ handlePermissionDenied(payload, getCurrentEventId(payload.session_id), traceShipper);
805
+ break;
806
+ case "PostCompact":
807
+ await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
808
+ break;
809
+ case "SessionEnd":
810
+ await handleSessionEnd(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
811
+ deleteState(turnEventKey(payload.session_id));
812
+ deleteState(rootSpanKey(payload.session_id));
813
+ deleteState(`model_${payload.session_id}`);
814
+ break;
815
+ default:
816
+ if (config.debug) {
817
+ console.log(`[raindrop-ai/claude-code] ignoring unhandled event: ${payload.hook_event_name}`);
818
+ }
819
+ }
820
+ }
821
+ async function handleUserPromptSubmit(payload, convoId, config, properties, eventShipper, traceShipper) {
822
+ const eventId = `cc_${randomUUID2()}`;
823
+ saveCurrentEventId(payload.session_id, eventId);
824
+ const model = readState(`model_${payload.session_id}`) || void 0;
825
+ const rootSpan = traceShipper.startSpan({
826
+ name: model != null ? model : "claude-code",
827
+ eventId,
828
+ attributes: [
829
+ attrString("ai.operationId", "generateText"),
830
+ attrString("session.model", model),
831
+ attrString("cwd", payload.cwd)
832
+ ]
833
+ });
834
+ traceShipper.endSpan(rootSpan);
835
+ saveSpanContext(rootSpanKey(payload.session_id), {
836
+ traceIdB64: rootSpan.ids.traceIdB64,
837
+ spanIdB64: rootSpan.ids.spanIdB64
838
+ });
839
+ const patch = {
840
+ isPending: true,
841
+ userId: config.userId,
842
+ convoId,
843
+ eventName: "claude_code_session",
844
+ input: payload.prompt,
845
+ model,
846
+ properties
847
+ };
848
+ await eventShipper.patch(eventId, patch);
849
+ }
850
+ function handlePreToolUse(payload) {
851
+ if (payload.tool_use_id) {
852
+ saveTimestamp(`tool_${payload.tool_use_id}`);
853
+ }
854
+ }
855
+ function createToolSpan(payload, eventId, traceShipper, error) {
856
+ const endMs = Date.now();
857
+ const savedStartMs = payload.tool_use_id ? readTimestamp(`tool_${payload.tool_use_id}`) : void 0;
858
+ const startMs = savedStartMs && savedStartMs <= endMs ? savedStartMs : endMs;
859
+ const durationMs = endMs - startMs;
860
+ const startNano = String(startMs) + "000000";
861
+ const endNano = String(endMs) + "000000";
862
+ const parent = getParentContext(payload);
863
+ traceShipper.createSpan({
864
+ name: "ai.toolCall",
865
+ eventId,
866
+ parent,
867
+ startTimeUnixNano: startNano,
868
+ endTimeUnixNano: endNano,
869
+ attributes: [
870
+ attrString("ai.operationId", "ai.toolCall"),
871
+ attrString("ai.toolCall.name", payload.tool_name),
872
+ attrString("ai.toolCall.id", payload.tool_use_id),
873
+ attrString("ai.toolCall.args", safeStringify(payload.tool_input)),
874
+ ...payload.tool_response != null ? [attrString("ai.toolCall.result", safeStringify(payload.tool_response))] : [],
875
+ attrInt("traceloop.entity.duration_ms", durationMs)
876
+ ],
877
+ ...error ? { status: { code: 2, message: error } } : {}
878
+ });
879
+ }
880
+ function handlePostToolUse(payload, eventId, traceShipper) {
881
+ createToolSpan(payload, eventId, traceShipper);
882
+ }
883
+ function handlePostToolUseFailure(payload, eventId, traceShipper) {
884
+ var _a;
885
+ createToolSpan(payload, eventId, traceShipper, (_a = payload.error) != null ? _a : "Tool execution failed");
886
+ }
887
+ function handleSubagentStart(payload, eventId, traceShipper) {
888
+ if (payload.agent_id) {
889
+ saveTimestamp(`subagent_${payload.agent_id}`);
890
+ }
891
+ const parent = getParentContext(payload);
892
+ const span = traceShipper.startSpan({
893
+ name: "ai.subagent.start",
894
+ eventId,
895
+ parent,
896
+ attributes: [
897
+ attrString("ai.operationId", "ai.subagent"),
898
+ attrString("ai.subagent.id", payload.agent_id),
899
+ attrString("ai.subagent.type", payload.agent_type)
900
+ ]
901
+ });
902
+ traceShipper.endSpan(span);
903
+ if (payload.agent_id) {
904
+ saveSpanContext(subagentSpanKey(payload.agent_id), {
905
+ traceIdB64: span.ids.traceIdB64,
906
+ spanIdB64: span.ids.spanIdB64
907
+ });
908
+ }
909
+ }
910
+ function handleSubagentStop(payload, eventId, traceShipper) {
911
+ const endMs = Date.now();
912
+ const savedStartMs = payload.agent_id ? readTimestamp(`subagent_${payload.agent_id}`) : void 0;
913
+ const startMs = savedStartMs && savedStartMs <= endMs ? savedStartMs : endMs;
914
+ const durationMs = endMs - startMs;
915
+ const startNano = String(startMs) + "000000";
916
+ const endNano = String(endMs) + "000000";
917
+ const parent = readSpanContext(rootSpanKey(payload.session_id));
918
+ traceShipper.createSpan({
919
+ name: "ai.subagent",
920
+ eventId,
921
+ parent,
922
+ startTimeUnixNano: startNano,
923
+ endTimeUnixNano: endNano,
924
+ attributes: [
925
+ attrString("ai.operationId", "ai.subagent"),
926
+ attrString("ai.subagent.id", payload.agent_id),
927
+ attrString("ai.subagent.type", payload.agent_type),
928
+ attrString("ai.subagent.result", safeStringify(payload.last_assistant_message)),
929
+ attrInt("traceloop.entity.duration_ms", durationMs)
930
+ ]
931
+ });
932
+ if (payload.agent_id) {
933
+ deleteState(subagentSpanKey(payload.agent_id));
934
+ }
935
+ }
936
+ function handlePermissionDenied(payload, eventId, traceShipper) {
937
+ var _a;
938
+ const parent = getParentContext(payload);
939
+ traceShipper.createSpan({
940
+ name: "ai.permissionDenied",
941
+ eventId,
942
+ parent,
943
+ startTimeUnixNano: nowUnixNanoString(),
944
+ endTimeUnixNano: nowUnixNanoString(),
945
+ attributes: [
946
+ attrString("ai.operationId", "ai.permissionDenied"),
947
+ attrString("ai.toolCall.name", payload.tool_name),
948
+ attrString("ai.toolCall.id", payload.tool_use_id),
949
+ attrString("ai.toolCall.args", safeStringify(payload.tool_input)),
950
+ attrString("ai.permissionDenied.reason", payload.reason)
951
+ ],
952
+ status: {
953
+ code: 2,
954
+ message: (_a = payload.reason) != null ? _a : "Permission denied"
955
+ }
956
+ });
957
+ }
958
+ async function handlePostCompact(payload, eventId, config, properties, eventShipper) {
959
+ await eventShipper.patch(eventId, {
960
+ isPending: true,
961
+ userId: config.userId,
962
+ convoId: payload.session_id,
963
+ eventName: "claude_code_session",
964
+ properties: {
965
+ ...properties,
966
+ compaction_trigger: payload.trigger,
967
+ compact_summary: payload.compact_summary
968
+ }
969
+ });
970
+ }
971
+ async function handleStop(payload, eventId, config, properties, eventShipper) {
972
+ await eventShipper.finish(eventId, {
973
+ userId: config.userId,
974
+ output: payload.last_assistant_message,
975
+ properties
976
+ });
977
+ }
978
+ async function handleStopFailure(payload, eventId, config, properties, eventShipper) {
979
+ await eventShipper.finish(eventId, {
980
+ userId: config.userId,
981
+ output: payload.last_assistant_message,
982
+ properties: {
983
+ ...properties,
984
+ error: payload.error,
985
+ error_details: payload.error_details
986
+ }
987
+ });
988
+ }
989
+ async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
990
+ await eventShipper.finish(eventId, {
991
+ userId: config.userId,
992
+ properties: {
993
+ ...properties,
994
+ session_end_reason: payload.reason
995
+ }
996
+ });
997
+ }
998
+ export {
999
+ EventShipper2 as EventShipper,
1000
+ PACKAGE_NAME,
1001
+ PACKAGE_VERSION,
1002
+ TraceShipper2 as TraceShipper,
1003
+ getConfigPath,
1004
+ loadConfig,
1005
+ mapHookToRaindrop
1006
+ };