@raindrop-ai/claude-code 0.0.16 → 0.0.18

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 CHANGED
@@ -63,6 +63,18 @@ Replace the built-in signal categories with your own via `~/.config/raindrop/con
63
63
 
64
64
  Or via env var: `RAINDROP_SELF_DIAGNOSTICS='{"signals":{...}}'`
65
65
 
66
+ ## Projects
67
+
68
+ If your org has multiple projects, route telemetry to a specific one by setting its slug via the `RAINDROP_PROJECT_ID` env var or the `project_id` key in `~/.config/raindrop/config.json`:
69
+
70
+ ```json
71
+ {
72
+ "project_id": "support-prod"
73
+ }
74
+ ```
75
+
76
+ This sets the `X-Raindrop-Project-Id` header on every outbound request. Leave it unset (or use `"default"`) to use your org's default **Production** project — the existing behavior. Single-project orgs need nothing new.
77
+
66
78
  ## Payload size limits & bounded hooks
67
79
 
68
80
  Event input/output are capped at **1,000,000 characters per field** (span
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { randomUUID as randomUUID2 } from "crypto";
7
7
  import { tmpdir } from "os";
8
8
  import { join } from "path";
9
9
 
10
- // ../core/dist/chunk-SK6EJEO7.js
10
+ // ../core/dist/chunk-WKRW55KX.js
11
11
  function getCrypto() {
12
12
  const c = globalThis.crypto;
13
13
  return c;
@@ -51,6 +51,20 @@ function base64Encode(bytes) {
51
51
  }
52
52
  return out;
53
53
  }
54
+ function runWithTracingSuppressed(fn) {
55
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
56
+ if (typeof hook !== "function") return fn();
57
+ let started = false;
58
+ try {
59
+ return hook(() => {
60
+ started = true;
61
+ return fn();
62
+ });
63
+ } catch (err) {
64
+ if (started) throw err;
65
+ return fn();
66
+ }
67
+ }
54
68
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
55
69
  var MAX_RETRY_DELAY_MS = 3e4;
56
70
  function wait(ms) {
@@ -161,15 +175,17 @@ async function postJson(url, body, headers, opts) {
161
175
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
162
176
  await withRetry(
163
177
  async () => {
164
- const resp = await fetch(url, {
165
- method: "POST",
166
- headers: {
167
- "Content-Type": "application/json",
168
- ...headers
169
- },
170
- body: JSON.stringify(body),
171
- signal: AbortSignal.timeout(timeoutMs)
172
- });
178
+ const resp = await runWithTracingSuppressed(
179
+ () => fetch(url, {
180
+ method: "POST",
181
+ headers: {
182
+ "Content-Type": "application/json",
183
+ ...headers
184
+ },
185
+ body: JSON.stringify(body),
186
+ signal: AbortSignal.timeout(timeoutMs)
187
+ })
188
+ );
173
189
  if (!resp.ok) {
174
190
  const text = await resp.text().catch(() => "");
175
191
  const err = new Error(
@@ -1117,6 +1133,31 @@ var TraceShipper = class {
1117
1133
  // ../core/dist/index.node.js
1118
1134
  import { AsyncLocalStorage } from "async_hooks";
1119
1135
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1136
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1137
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1138
+ );
1139
+ function findOtelContextManager() {
1140
+ var _a;
1141
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1142
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1143
+ const api = globalThis[sym];
1144
+ const cm = api == null ? void 0 : api.context;
1145
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1146
+ return cm;
1147
+ }
1148
+ }
1149
+ return void 0;
1150
+ }
1151
+ function installTracingSuppressionHook() {
1152
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1153
+ const hook = (fn) => {
1154
+ const cm = findOtelContextManager();
1155
+ if (!cm) return fn();
1156
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1157
+ };
1158
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1159
+ }
1160
+ installTracingSuppressionHook();
1120
1161
 
1121
1162
  // src/package-info.ts
1122
1163
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
@@ -2188,7 +2229,9 @@ async function healthPing(baseUrl) {
2188
2229
  const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
2189
2230
  try {
2190
2231
  const signal = AbortSignal.timeout(HEALTH_TIMEOUT_MS);
2191
- const resp = await fetch(healthUrl, { signal });
2232
+ const resp = await runWithTracingSuppressed(
2233
+ () => fetch(healthUrl, { signal })
2234
+ );
2192
2235
  if (resp.ok) {
2193
2236
  const body = await resp.json();
2194
2237
  if (body.ok) return baseUrl;
@@ -2228,12 +2271,14 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
2228
2271
  if (debug) {
2229
2272
  console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
2230
2273
  }
2231
- void fetch(url, {
2232
- method: "POST",
2233
- headers: { "Content-Type": "application/json" },
2234
- body: JSON.stringify(payload),
2235
- signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2236
- }).catch(() => {
2274
+ void runWithTracingSuppressed(
2275
+ () => fetch(url, {
2276
+ method: "POST",
2277
+ headers: { "Content-Type": "application/json" },
2278
+ body: JSON.stringify(payload),
2279
+ signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2280
+ })
2281
+ ).catch(() => {
2237
2282
  });
2238
2283
  }
2239
2284
 
package/dist/index.cjs CHANGED
@@ -43,7 +43,7 @@ __export(src_exports, {
43
43
  });
44
44
  module.exports = __toCommonJS(src_exports);
45
45
 
46
- // ../core/dist/chunk-SK6EJEO7.js
46
+ // ../core/dist/chunk-WKRW55KX.js
47
47
  function getCrypto() {
48
48
  const c = globalThis.crypto;
49
49
  return c;
@@ -87,6 +87,20 @@ function base64Encode(bytes) {
87
87
  }
88
88
  return out;
89
89
  }
90
+ function runWithTracingSuppressed(fn) {
91
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
92
+ if (typeof hook !== "function") return fn();
93
+ let started = false;
94
+ try {
95
+ return hook(() => {
96
+ started = true;
97
+ return fn();
98
+ });
99
+ } catch (err) {
100
+ if (started) throw err;
101
+ return fn();
102
+ }
103
+ }
90
104
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
91
105
  var MAX_RETRY_DELAY_MS = 3e4;
92
106
  function wait(ms) {
@@ -197,15 +211,17 @@ async function postJson(url, body, headers, opts) {
197
211
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
198
212
  await withRetry(
199
213
  async () => {
200
- const resp = await fetch(url, {
201
- method: "POST",
202
- headers: {
203
- "Content-Type": "application/json",
204
- ...headers
205
- },
206
- body: JSON.stringify(body),
207
- signal: AbortSignal.timeout(timeoutMs)
208
- });
214
+ const resp = await runWithTracingSuppressed(
215
+ () => fetch(url, {
216
+ method: "POST",
217
+ headers: {
218
+ "Content-Type": "application/json",
219
+ ...headers
220
+ },
221
+ body: JSON.stringify(body),
222
+ signal: AbortSignal.timeout(timeoutMs)
223
+ })
224
+ );
209
225
  if (!resp.ok) {
210
226
  const text = await resp.text().catch(() => "");
211
227
  const err = new Error(
@@ -1153,6 +1169,31 @@ var TraceShipper = class {
1153
1169
  // ../core/dist/index.node.js
1154
1170
  var import_async_hooks = require("async_hooks");
1155
1171
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1172
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1173
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1174
+ );
1175
+ function findOtelContextManager() {
1176
+ var _a;
1177
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1178
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1179
+ const api = globalThis[sym];
1180
+ const cm = api == null ? void 0 : api.context;
1181
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1182
+ return cm;
1183
+ }
1184
+ }
1185
+ return void 0;
1186
+ }
1187
+ function installTracingSuppressionHook() {
1188
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1189
+ const hook = (fn) => {
1190
+ const cm = findOtelContextManager();
1191
+ if (!cm) return fn();
1192
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1193
+ };
1194
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1195
+ }
1196
+ installTracingSuppressionHook();
1156
1197
 
1157
1198
  // src/package-info.ts
1158
1199
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
@@ -2231,7 +2272,9 @@ async function healthPing(baseUrl) {
2231
2272
  const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
2232
2273
  try {
2233
2274
  const signal = AbortSignal.timeout(HEALTH_TIMEOUT_MS);
2234
- const resp = await fetch(healthUrl, { signal });
2275
+ const resp = await runWithTracingSuppressed(
2276
+ () => fetch(healthUrl, { signal })
2277
+ );
2235
2278
  if (resp.ok) {
2236
2279
  const body = await resp.json();
2237
2280
  if (body.ok) return baseUrl;
@@ -2271,12 +2314,14 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
2271
2314
  if (debug) {
2272
2315
  console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
2273
2316
  }
2274
- void fetch(url, {
2275
- method: "POST",
2276
- headers: { "Content-Type": "application/json" },
2277
- body: JSON.stringify(payload),
2278
- signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2279
- }).catch(() => {
2317
+ void runWithTracingSuppressed(
2318
+ () => fetch(url, {
2319
+ method: "POST",
2320
+ headers: { "Content-Type": "application/json" },
2321
+ body: JSON.stringify(payload),
2322
+ signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2323
+ })
2324
+ ).catch(() => {
2280
2325
  });
2281
2326
  }
2282
2327
 
package/dist/index.d.cts CHANGED
@@ -357,6 +357,42 @@ declare class TraceShipper$1 {
357
357
  shutdown(): Promise<void>;
358
358
  }
359
359
 
360
+ /**
361
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
362
+ *
363
+ * Why this exists
364
+ * ---------------
365
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
366
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
367
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
368
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
369
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
370
+ * very exporter that issued the request, so they get shipped right back to
371
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
372
+ * another fetch, they feed back on themselves). The result is a run list
373
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
374
+ * `.../live` entries that drown out the real agent turns — especially with
375
+ * sub-agents, where every sandbox runs its own instrumentation.
376
+ *
377
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
378
+ * around the request; instrumentations check `isTracingSuppressed` and return
379
+ * a no-op span instead of recording one. We do this through a hook stashed on
380
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
381
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
382
+ * hard-depends on them, and the hook is simply absent when they (and thus
383
+ * any instrumentation to suppress) are not installed; and
384
+ * - the browser bundle never pulls in `node:module`, mirroring how core
385
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
386
+ *
387
+ * When no hook is present the callback runs unchanged, so suppression is a
388
+ * best-effort no-op rather than a hard requirement.
389
+ */
390
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
391
+ type SuppressTracingHook = <T>(fn: () => T) => T;
392
+ declare global {
393
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
394
+ }
395
+
360
396
  type ParentSpanContext = {
361
397
  traceIdB64: string;
362
398
  spanIdB64: string;
package/dist/index.d.ts CHANGED
@@ -357,6 +357,42 @@ declare class TraceShipper$1 {
357
357
  shutdown(): Promise<void>;
358
358
  }
359
359
 
360
+ /**
361
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
362
+ *
363
+ * Why this exists
364
+ * ---------------
365
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
366
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
367
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
368
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
369
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
370
+ * very exporter that issued the request, so they get shipped right back to
371
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
372
+ * another fetch, they feed back on themselves). The result is a run list
373
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
374
+ * `.../live` entries that drown out the real agent turns — especially with
375
+ * sub-agents, where every sandbox runs its own instrumentation.
376
+ *
377
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
378
+ * around the request; instrumentations check `isTracingSuppressed` and return
379
+ * a no-op span instead of recording one. We do this through a hook stashed on
380
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
381
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
382
+ * hard-depends on them, and the hook is simply absent when they (and thus
383
+ * any instrumentation to suppress) are not installed; and
384
+ * - the browser bundle never pulls in `node:module`, mirroring how core
385
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
386
+ *
387
+ * When no hook is present the callback runs unchanged, so suppression is a
388
+ * best-effort no-op rather than a hard requirement.
389
+ */
390
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
391
+ type SuppressTracingHook = <T>(fn: () => T) => T;
392
+ declare global {
393
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
394
+ }
395
+
360
396
  type ParentSpanContext = {
361
397
  traceIdB64: string;
362
398
  spanIdB64: string;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // ../core/dist/chunk-SK6EJEO7.js
1
+ // ../core/dist/chunk-WKRW55KX.js
2
2
  function getCrypto() {
3
3
  const c = globalThis.crypto;
4
4
  return c;
@@ -42,6 +42,20 @@ function base64Encode(bytes) {
42
42
  }
43
43
  return out;
44
44
  }
45
+ function runWithTracingSuppressed(fn) {
46
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
47
+ if (typeof hook !== "function") return fn();
48
+ let started = false;
49
+ try {
50
+ return hook(() => {
51
+ started = true;
52
+ return fn();
53
+ });
54
+ } catch (err) {
55
+ if (started) throw err;
56
+ return fn();
57
+ }
58
+ }
45
59
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
46
60
  var MAX_RETRY_DELAY_MS = 3e4;
47
61
  function wait(ms) {
@@ -152,15 +166,17 @@ async function postJson(url, body, headers, opts) {
152
166
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
153
167
  await withRetry(
154
168
  async () => {
155
- const resp = await fetch(url, {
156
- method: "POST",
157
- headers: {
158
- "Content-Type": "application/json",
159
- ...headers
160
- },
161
- body: JSON.stringify(body),
162
- signal: AbortSignal.timeout(timeoutMs)
163
- });
169
+ const resp = await runWithTracingSuppressed(
170
+ () => fetch(url, {
171
+ method: "POST",
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ ...headers
175
+ },
176
+ body: JSON.stringify(body),
177
+ signal: AbortSignal.timeout(timeoutMs)
178
+ })
179
+ );
164
180
  if (!resp.ok) {
165
181
  const text = await resp.text().catch(() => "");
166
182
  const err = new Error(
@@ -1108,6 +1124,31 @@ var TraceShipper = class {
1108
1124
  // ../core/dist/index.node.js
1109
1125
  import { AsyncLocalStorage } from "async_hooks";
1110
1126
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1127
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1128
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1129
+ );
1130
+ function findOtelContextManager() {
1131
+ var _a;
1132
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1133
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1134
+ const api = globalThis[sym];
1135
+ const cm = api == null ? void 0 : api.context;
1136
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1137
+ return cm;
1138
+ }
1139
+ }
1140
+ return void 0;
1141
+ }
1142
+ function installTracingSuppressionHook() {
1143
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1144
+ const hook = (fn) => {
1145
+ const cm = findOtelContextManager();
1146
+ if (!cm) return fn();
1147
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1148
+ };
1149
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1150
+ }
1151
+ installTracingSuppressionHook();
1111
1152
 
1112
1153
  // src/package-info.ts
1113
1154
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
@@ -2186,7 +2227,9 @@ async function healthPing(baseUrl) {
2186
2227
  const healthUrl = baseUrl.replace(/\/v1\/$/, "/health");
2187
2228
  try {
2188
2229
  const signal = AbortSignal.timeout(HEALTH_TIMEOUT_MS);
2189
- const resp = await fetch(healthUrl, { signal });
2230
+ const resp = await runWithTracingSuppressed(
2231
+ () => fetch(healthUrl, { signal })
2232
+ );
2190
2233
  if (resp.ok) {
2191
2234
  const body = await resp.json();
2192
2235
  if (body.ok) return baseUrl;
@@ -2226,12 +2269,14 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
2226
2269
  if (debug) {
2227
2270
  console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
2228
2271
  }
2229
- void fetch(url, {
2230
- method: "POST",
2231
- headers: { "Content-Type": "application/json" },
2232
- body: JSON.stringify(payload),
2233
- signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2234
- }).catch(() => {
2272
+ void runWithTracingSuppressed(
2273
+ () => fetch(url, {
2274
+ method: "POST",
2275
+ headers: { "Content-Type": "application/json" },
2276
+ body: JSON.stringify(payload),
2277
+ signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2278
+ })
2279
+ ).catch(() => {
2235
2280
  });
2236
2281
  }
2237
2282
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/claude-code",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Raindrop observability for Claude Code CLI — automatic session, tool call, and prompt tracing via hooks",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.raindrop.ai/docs/integrations/claude-code/",
@@ -53,7 +53,7 @@
53
53
  "tsup": "^8.4.0",
54
54
  "typescript": "^5.3.3",
55
55
  "vitest": "^2.1.0",
56
- "@raindrop-ai/core": "0.0.4"
56
+ "@raindrop-ai/core": "0.0.5"
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public"