@raindrop-ai/claude-code 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js 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-U5HUTMR5.js
10
+ // ../core/dist/chunk-SK6EJEO7.js
11
11
  function getCrypto() {
12
12
  const c = globalThis.crypto;
13
13
  return c;
@@ -348,6 +348,25 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
348
348
  }).catch(() => {
349
349
  });
350
350
  }
351
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
352
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
353
+ function isValidProjectIdSlug(value) {
354
+ return PROJECT_ID_SLUG_PATTERN.test(value);
355
+ }
356
+ function normalizeProjectId(raw, opts) {
357
+ if (typeof raw !== "string") return void 0;
358
+ const trimmed = raw.trim();
359
+ if (!trimmed) return void 0;
360
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
361
+ console.warn(
362
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
363
+ );
364
+ }
365
+ return trimmed;
366
+ }
367
+ function projectIdHeaders(projectId) {
368
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
369
+ }
351
370
  var SHUTDOWN_DEADLINE_MS = 1e4;
352
371
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
353
372
  function mergePatches(target, source) {
@@ -382,6 +401,10 @@ var EventShipper = class {
382
401
  if (this.debug && this.localDebuggerUrl) {
383
402
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
384
403
  }
404
+ this.projectId = normalizeProjectId(opts.projectId, {
405
+ debug: this.debug,
406
+ prefix: this.prefix
407
+ });
385
408
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
386
409
  this.context = {
387
410
  library: {
@@ -400,6 +423,9 @@ var EventShipper = class {
400
423
  authHeaders() {
401
424
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
402
425
  }
426
+ requestHeaders() {
427
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
428
+ }
403
429
  /**
404
430
  * Build the retry/timeout options for one POST, honoring the shutdown
405
431
  * deadline. Returns `null` when the shutdown drain window is exhausted —
@@ -529,7 +555,7 @@ var EventShipper = class {
529
555
  return;
530
556
  }
531
557
  try {
532
- await postJson(url, body, this.authHeaders(), opts);
558
+ await postJson(url, body, this.requestHeaders(), opts);
533
559
  } catch (err) {
534
560
  const msg = err instanceof Error ? err.message : String(err);
535
561
  rateLimitedLog(
@@ -565,7 +591,7 @@ var EventShipper = class {
565
591
  return;
566
592
  }
567
593
  try {
568
- await postJson(url, body, this.authHeaders(), opts);
594
+ await postJson(url, body, this.requestHeaders(), opts);
569
595
  } catch (err) {
570
596
  const msg = err instanceof Error ? err.message : String(err);
571
597
  rateLimitedLog(
@@ -661,7 +687,7 @@ var EventShipper = class {
661
687
  if (!isPending) this.sticky.delete(eventId);
662
688
  return;
663
689
  }
664
- const p = postJson(url, payload, this.authHeaders(), opts);
690
+ const p = postJson(url, payload, this.requestHeaders(), opts);
665
691
  this.inFlight.add(p);
666
692
  try {
667
693
  try {
@@ -808,6 +834,10 @@ var TraceShipper = class {
808
834
  if (this.debug && this.localDebuggerUrl) {
809
835
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
810
836
  }
837
+ this.projectId = normalizeProjectId(opts.projectId, {
838
+ debug: this.debug,
839
+ prefix: this.prefix
840
+ });
811
841
  this.transformSpanHook = opts.transformSpan;
812
842
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
813
843
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
@@ -884,6 +914,9 @@ var TraceShipper = class {
884
914
  authHeaders() {
885
915
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
886
916
  }
917
+ requestHeaders() {
918
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
919
+ }
887
920
  startSpan(args2) {
888
921
  var _a, _b;
889
922
  const ids = createSpanIds(args2.parent);
@@ -1015,7 +1048,7 @@ var TraceShipper = class {
1015
1048
  endpoint: url
1016
1049
  });
1017
1050
  }
1018
- const p = postJson(url, body, this.authHeaders(), opts);
1051
+ const p = postJson(url, body, this.requestHeaders(), opts);
1019
1052
  this.inFlight.add(p);
1020
1053
  try {
1021
1054
  try {
@@ -2051,7 +2084,7 @@ import { homedir, userInfo } from "os";
2051
2084
  import { dirname, join as join2 } from "path";
2052
2085
  var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
2053
2086
  function loadConfig() {
2054
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2087
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2055
2088
  let file = {};
2056
2089
  try {
2057
2090
  if (existsSync3(CONFIG_PATH)) {
@@ -2091,9 +2124,10 @@ function loadConfig() {
2091
2124
  return {
2092
2125
  writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
2093
2126
  endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
2094
- userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
2095
- convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
2096
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
2127
+ projectId: (_f = process.env["RAINDROP_PROJECT_ID"]) != null ? _f : file.project_id,
2128
+ userId: (_h = (_g = process.env["RAINDROP_USER_ID"]) != null ? _g : file.user_id) != null ? _h : systemUser,
2129
+ convoId: ((_i = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _i.trim()) || void 0,
2130
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_j = file.debug) != null ? _j : false,
2097
2131
  enabled: (() => {
2098
2132
  var _a2;
2099
2133
  const env = process.env["RAINDROP_ENABLED"];
@@ -2101,7 +2135,7 @@ function loadConfig() {
2101
2135
  const lower = env.toLowerCase();
2102
2136
  return lower !== "false" && env !== "0";
2103
2137
  })(),
2104
- eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
2138
+ eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
2105
2139
  customProperties,
2106
2140
  selfDiagnostics
2107
2141
  };
@@ -2313,6 +2347,7 @@ async function handleHook() {
2313
2347
  writeKey: config.writeKey,
2314
2348
  endpoint: config.endpoint,
2315
2349
  debug: config.debug,
2350
+ projectId: config.projectId,
2316
2351
  // Only ship to the cloud when we have an API key. When only the local
2317
2352
  // debugger is present the shipper still needs to be instantiated so the
2318
2353
  // event mapper can call patch/finish, but cloud POSTs are skipped.
@@ -2322,6 +2357,7 @@ async function handleHook() {
2322
2357
  writeKey: config.writeKey,
2323
2358
  endpoint: config.endpoint,
2324
2359
  debug: config.debug,
2360
+ projectId: config.projectId,
2325
2361
  enabled: hasCloudKey
2326
2362
  });
2327
2363
  if (debugger_.url) {
@@ -2840,6 +2876,7 @@ async function executeTool(args2) {
2840
2876
  writeKey: config.writeKey,
2841
2877
  endpoint: config.endpoint,
2842
2878
  debug: false,
2879
+ projectId: config.projectId,
2843
2880
  enabled: true
2844
2881
  });
2845
2882
  try {
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-U5HUTMR5.js
46
+ // ../core/dist/chunk-SK6EJEO7.js
47
47
  function getCrypto() {
48
48
  const c = globalThis.crypto;
49
49
  return c;
@@ -384,6 +384,25 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
384
384
  }).catch(() => {
385
385
  });
386
386
  }
387
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
388
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
389
+ function isValidProjectIdSlug(value) {
390
+ return PROJECT_ID_SLUG_PATTERN.test(value);
391
+ }
392
+ function normalizeProjectId(raw, opts) {
393
+ if (typeof raw !== "string") return void 0;
394
+ const trimmed = raw.trim();
395
+ if (!trimmed) return void 0;
396
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
397
+ console.warn(
398
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
399
+ );
400
+ }
401
+ return trimmed;
402
+ }
403
+ function projectIdHeaders(projectId) {
404
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
405
+ }
387
406
  var SHUTDOWN_DEADLINE_MS = 1e4;
388
407
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
389
408
  function mergePatches(target, source) {
@@ -418,6 +437,10 @@ var EventShipper = class {
418
437
  if (this.debug && this.localDebuggerUrl) {
419
438
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
420
439
  }
440
+ this.projectId = normalizeProjectId(opts.projectId, {
441
+ debug: this.debug,
442
+ prefix: this.prefix
443
+ });
421
444
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
422
445
  this.context = {
423
446
  library: {
@@ -436,6 +459,9 @@ var EventShipper = class {
436
459
  authHeaders() {
437
460
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
438
461
  }
462
+ requestHeaders() {
463
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
464
+ }
439
465
  /**
440
466
  * Build the retry/timeout options for one POST, honoring the shutdown
441
467
  * deadline. Returns `null` when the shutdown drain window is exhausted —
@@ -565,7 +591,7 @@ var EventShipper = class {
565
591
  return;
566
592
  }
567
593
  try {
568
- await postJson(url, body, this.authHeaders(), opts);
594
+ await postJson(url, body, this.requestHeaders(), opts);
569
595
  } catch (err) {
570
596
  const msg = err instanceof Error ? err.message : String(err);
571
597
  rateLimitedLog(
@@ -601,7 +627,7 @@ var EventShipper = class {
601
627
  return;
602
628
  }
603
629
  try {
604
- await postJson(url, body, this.authHeaders(), opts);
630
+ await postJson(url, body, this.requestHeaders(), opts);
605
631
  } catch (err) {
606
632
  const msg = err instanceof Error ? err.message : String(err);
607
633
  rateLimitedLog(
@@ -697,7 +723,7 @@ var EventShipper = class {
697
723
  if (!isPending) this.sticky.delete(eventId);
698
724
  return;
699
725
  }
700
- const p = postJson(url, payload, this.authHeaders(), opts);
726
+ const p = postJson(url, payload, this.requestHeaders(), opts);
701
727
  this.inFlight.add(p);
702
728
  try {
703
729
  try {
@@ -844,6 +870,10 @@ var TraceShipper = class {
844
870
  if (this.debug && this.localDebuggerUrl) {
845
871
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
846
872
  }
873
+ this.projectId = normalizeProjectId(opts.projectId, {
874
+ debug: this.debug,
875
+ prefix: this.prefix
876
+ });
847
877
  this.transformSpanHook = opts.transformSpan;
848
878
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
849
879
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
@@ -920,6 +950,9 @@ var TraceShipper = class {
920
950
  authHeaders() {
921
951
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
922
952
  }
953
+ requestHeaders() {
954
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
955
+ }
923
956
  startSpan(args) {
924
957
  var _a, _b;
925
958
  const ids = createSpanIds(args.parent);
@@ -1051,7 +1084,7 @@ var TraceShipper = class {
1051
1084
  endpoint: url
1052
1085
  });
1053
1086
  }
1054
- const p = postJson(url, body, this.authHeaders(), opts);
1087
+ const p = postJson(url, body, this.requestHeaders(), opts);
1055
1088
  this.inFlight.add(p);
1056
1089
  try {
1057
1090
  try {
@@ -1166,7 +1199,7 @@ var import_node_os = require("os");
1166
1199
  var import_node_path = require("path");
1167
1200
  var CONFIG_PATH = (0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "raindrop", "config.json");
1168
1201
  function loadConfig() {
1169
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1202
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1170
1203
  let file = {};
1171
1204
  try {
1172
1205
  if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
@@ -1206,9 +1239,10 @@ function loadConfig() {
1206
1239
  return {
1207
1240
  writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
1208
1241
  endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
1209
- userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
1210
- convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
1211
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
1242
+ projectId: (_f = process.env["RAINDROP_PROJECT_ID"]) != null ? _f : file.project_id,
1243
+ userId: (_h = (_g = process.env["RAINDROP_USER_ID"]) != null ? _g : file.user_id) != null ? _h : systemUser,
1244
+ convoId: ((_i = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _i.trim()) || void 0,
1245
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_j = file.debug) != null ? _j : false,
1212
1246
  enabled: (() => {
1213
1247
  var _a2;
1214
1248
  const env = process.env["RAINDROP_ENABLED"];
@@ -1216,7 +1250,7 @@ function loadConfig() {
1216
1250
  const lower = env.toLowerCase();
1217
1251
  return lower !== "false" && env !== "0";
1218
1252
  })(),
1219
- eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
1253
+ eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
1220
1254
  customProperties,
1221
1255
  selfDiagnostics
1222
1256
  };
@@ -2382,6 +2416,7 @@ async function executeTool(args) {
2382
2416
  writeKey: config.writeKey,
2383
2417
  endpoint: config.endpoint,
2384
2418
  debug: false,
2419
+ projectId: config.projectId,
2385
2420
  enabled: true
2386
2421
  });
2387
2422
  try {
package/dist/index.d.cts CHANGED
@@ -83,6 +83,13 @@ type EventShipperOptions = {
83
83
  * Pass `null` to opt out of all mirroring (including auto-detect).
84
84
  */
85
85
  localDebuggerUrl?: string | null;
86
+ /**
87
+ * Optional project slug. When set, every outbound cloud request includes an
88
+ * `X-Raindrop-Project-Id: <projectId>` header. Empty / whitespace-only
89
+ * values are ignored. Slug format is validated on construction but never
90
+ * throws — the backend returns 400 on invalid values.
91
+ */
92
+ projectId?: string;
86
93
  /**
87
94
  * Per-field character cap applied to event input/output BEFORE buffering
88
95
  * or serialization, so oversized payloads cost the cap — not the payload —
@@ -101,6 +108,7 @@ declare class EventShipper$1 {
101
108
  private sdkName;
102
109
  private prefix;
103
110
  private defaultEventName;
111
+ private projectId;
104
112
  private context;
105
113
  private buffers;
106
114
  private sticky;
@@ -124,6 +132,7 @@ declare class EventShipper$1 {
124
132
  constructor(opts: EventShipperOptions);
125
133
  isDebugEnabled(): boolean;
126
134
  private authHeaders;
135
+ private requestHeaders;
127
136
  /**
128
137
  * Build the retry/timeout options for one POST, honoring the shutdown
129
138
  * deadline. Returns `null` when the shutdown drain window is exhausted —
@@ -195,6 +204,13 @@ type TraceShipperOptions = {
195
204
  * Pass `null` to opt out of all mirroring (including auto-detect).
196
205
  */
197
206
  localDebuggerUrl?: string | null;
207
+ /**
208
+ * Optional project slug. When set, every OTLP trace export includes an
209
+ * `X-Raindrop-Project-Id: <projectId>` header. Empty / whitespace-only
210
+ * values are ignored. Slug format is validated on construction but never
211
+ * throws — the backend returns 400 on invalid values.
212
+ */
213
+ projectId?: string;
198
214
  /**
199
215
  * Per-span hook that fires for every OTLP span right before the span is
200
216
  * shipped (both to the Raindrop API and to a local debugger). Lets callers
@@ -252,6 +268,7 @@ declare class TraceShipper$1 {
252
268
  private flushIntervalMs;
253
269
  private maxBatchSize;
254
270
  private maxQueueSize;
271
+ private projectId;
255
272
  private queue;
256
273
  private timer;
257
274
  private inFlight;
@@ -302,6 +319,7 @@ declare class TraceShipper$1 {
302
319
  private redactSpan;
303
320
  isDebugEnabled(): boolean;
304
321
  private authHeaders;
322
+ private requestHeaders;
305
323
  startSpan(args: {
306
324
  name: string;
307
325
  parent?: {
@@ -386,6 +404,7 @@ interface SelfDiagnosticsConfig {
386
404
  interface ConfigFile {
387
405
  write_key?: string;
388
406
  api_url?: string;
407
+ project_id?: string;
389
408
  user_id?: string;
390
409
  debug?: boolean;
391
410
  enabled?: boolean;
@@ -396,6 +415,13 @@ interface ConfigFile {
396
415
  interface RaindropConfig {
397
416
  writeKey: string;
398
417
  endpoint: string;
418
+ /**
419
+ * Optional Raindrop project slug. When set, every outbound cloud request
420
+ * carries an `X-Raindrop-Project-Id` header. Unset → no header (the project
421
+ * resolves to `default` server-side; byte-identical to prior behavior).
422
+ * Sourced from `RAINDROP_PROJECT_ID` or the `project_id` config-file key.
423
+ */
424
+ projectId?: string;
399
425
  userId: string;
400
426
  convoId?: string;
401
427
  debug: boolean;
package/dist/index.d.ts CHANGED
@@ -83,6 +83,13 @@ type EventShipperOptions = {
83
83
  * Pass `null` to opt out of all mirroring (including auto-detect).
84
84
  */
85
85
  localDebuggerUrl?: string | null;
86
+ /**
87
+ * Optional project slug. When set, every outbound cloud request includes an
88
+ * `X-Raindrop-Project-Id: <projectId>` header. Empty / whitespace-only
89
+ * values are ignored. Slug format is validated on construction but never
90
+ * throws — the backend returns 400 on invalid values.
91
+ */
92
+ projectId?: string;
86
93
  /**
87
94
  * Per-field character cap applied to event input/output BEFORE buffering
88
95
  * or serialization, so oversized payloads cost the cap — not the payload —
@@ -101,6 +108,7 @@ declare class EventShipper$1 {
101
108
  private sdkName;
102
109
  private prefix;
103
110
  private defaultEventName;
111
+ private projectId;
104
112
  private context;
105
113
  private buffers;
106
114
  private sticky;
@@ -124,6 +132,7 @@ declare class EventShipper$1 {
124
132
  constructor(opts: EventShipperOptions);
125
133
  isDebugEnabled(): boolean;
126
134
  private authHeaders;
135
+ private requestHeaders;
127
136
  /**
128
137
  * Build the retry/timeout options for one POST, honoring the shutdown
129
138
  * deadline. Returns `null` when the shutdown drain window is exhausted —
@@ -195,6 +204,13 @@ type TraceShipperOptions = {
195
204
  * Pass `null` to opt out of all mirroring (including auto-detect).
196
205
  */
197
206
  localDebuggerUrl?: string | null;
207
+ /**
208
+ * Optional project slug. When set, every OTLP trace export includes an
209
+ * `X-Raindrop-Project-Id: <projectId>` header. Empty / whitespace-only
210
+ * values are ignored. Slug format is validated on construction but never
211
+ * throws — the backend returns 400 on invalid values.
212
+ */
213
+ projectId?: string;
198
214
  /**
199
215
  * Per-span hook that fires for every OTLP span right before the span is
200
216
  * shipped (both to the Raindrop API and to a local debugger). Lets callers
@@ -252,6 +268,7 @@ declare class TraceShipper$1 {
252
268
  private flushIntervalMs;
253
269
  private maxBatchSize;
254
270
  private maxQueueSize;
271
+ private projectId;
255
272
  private queue;
256
273
  private timer;
257
274
  private inFlight;
@@ -302,6 +319,7 @@ declare class TraceShipper$1 {
302
319
  private redactSpan;
303
320
  isDebugEnabled(): boolean;
304
321
  private authHeaders;
322
+ private requestHeaders;
305
323
  startSpan(args: {
306
324
  name: string;
307
325
  parent?: {
@@ -386,6 +404,7 @@ interface SelfDiagnosticsConfig {
386
404
  interface ConfigFile {
387
405
  write_key?: string;
388
406
  api_url?: string;
407
+ project_id?: string;
389
408
  user_id?: string;
390
409
  debug?: boolean;
391
410
  enabled?: boolean;
@@ -396,6 +415,13 @@ interface ConfigFile {
396
415
  interface RaindropConfig {
397
416
  writeKey: string;
398
417
  endpoint: string;
418
+ /**
419
+ * Optional Raindrop project slug. When set, every outbound cloud request
420
+ * carries an `X-Raindrop-Project-Id` header. Unset → no header (the project
421
+ * resolves to `default` server-side; byte-identical to prior behavior).
422
+ * Sourced from `RAINDROP_PROJECT_ID` or the `project_id` config-file key.
423
+ */
424
+ projectId?: string;
399
425
  userId: string;
400
426
  convoId?: string;
401
427
  debug: boolean;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // ../core/dist/chunk-U5HUTMR5.js
1
+ // ../core/dist/chunk-SK6EJEO7.js
2
2
  function getCrypto() {
3
3
  const c = globalThis.crypto;
4
4
  return c;
@@ -339,6 +339,25 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
339
339
  }).catch(() => {
340
340
  });
341
341
  }
342
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
343
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
344
+ function isValidProjectIdSlug(value) {
345
+ return PROJECT_ID_SLUG_PATTERN.test(value);
346
+ }
347
+ function normalizeProjectId(raw, opts) {
348
+ if (typeof raw !== "string") return void 0;
349
+ const trimmed = raw.trim();
350
+ if (!trimmed) return void 0;
351
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
352
+ console.warn(
353
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
354
+ );
355
+ }
356
+ return trimmed;
357
+ }
358
+ function projectIdHeaders(projectId) {
359
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
360
+ }
342
361
  var SHUTDOWN_DEADLINE_MS = 1e4;
343
362
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
344
363
  function mergePatches(target, source) {
@@ -373,6 +392,10 @@ var EventShipper = class {
373
392
  if (this.debug && this.localDebuggerUrl) {
374
393
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
375
394
  }
395
+ this.projectId = normalizeProjectId(opts.projectId, {
396
+ debug: this.debug,
397
+ prefix: this.prefix
398
+ });
376
399
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
377
400
  this.context = {
378
401
  library: {
@@ -391,6 +414,9 @@ var EventShipper = class {
391
414
  authHeaders() {
392
415
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
393
416
  }
417
+ requestHeaders() {
418
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
419
+ }
394
420
  /**
395
421
  * Build the retry/timeout options for one POST, honoring the shutdown
396
422
  * deadline. Returns `null` when the shutdown drain window is exhausted —
@@ -520,7 +546,7 @@ var EventShipper = class {
520
546
  return;
521
547
  }
522
548
  try {
523
- await postJson(url, body, this.authHeaders(), opts);
549
+ await postJson(url, body, this.requestHeaders(), opts);
524
550
  } catch (err) {
525
551
  const msg = err instanceof Error ? err.message : String(err);
526
552
  rateLimitedLog(
@@ -556,7 +582,7 @@ var EventShipper = class {
556
582
  return;
557
583
  }
558
584
  try {
559
- await postJson(url, body, this.authHeaders(), opts);
585
+ await postJson(url, body, this.requestHeaders(), opts);
560
586
  } catch (err) {
561
587
  const msg = err instanceof Error ? err.message : String(err);
562
588
  rateLimitedLog(
@@ -652,7 +678,7 @@ var EventShipper = class {
652
678
  if (!isPending) this.sticky.delete(eventId);
653
679
  return;
654
680
  }
655
- const p = postJson(url, payload, this.authHeaders(), opts);
681
+ const p = postJson(url, payload, this.requestHeaders(), opts);
656
682
  this.inFlight.add(p);
657
683
  try {
658
684
  try {
@@ -799,6 +825,10 @@ var TraceShipper = class {
799
825
  if (this.debug && this.localDebuggerUrl) {
800
826
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
801
827
  }
828
+ this.projectId = normalizeProjectId(opts.projectId, {
829
+ debug: this.debug,
830
+ prefix: this.prefix
831
+ });
802
832
  this.transformSpanHook = opts.transformSpan;
803
833
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
804
834
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
@@ -875,6 +905,9 @@ var TraceShipper = class {
875
905
  authHeaders() {
876
906
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
877
907
  }
908
+ requestHeaders() {
909
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
910
+ }
878
911
  startSpan(args) {
879
912
  var _a, _b;
880
913
  const ids = createSpanIds(args.parent);
@@ -1006,7 +1039,7 @@ var TraceShipper = class {
1006
1039
  endpoint: url
1007
1040
  });
1008
1041
  }
1009
- const p = postJson(url, body, this.authHeaders(), opts);
1042
+ const p = postJson(url, body, this.requestHeaders(), opts);
1010
1043
  this.inFlight.add(p);
1011
1044
  try {
1012
1045
  try {
@@ -1121,7 +1154,7 @@ import { homedir, userInfo } from "os";
1121
1154
  import { dirname, join } from "path";
1122
1155
  var CONFIG_PATH = join(homedir(), ".config", "raindrop", "config.json");
1123
1156
  function loadConfig() {
1124
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1157
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1125
1158
  let file = {};
1126
1159
  try {
1127
1160
  if (existsSync(CONFIG_PATH)) {
@@ -1161,9 +1194,10 @@ function loadConfig() {
1161
1194
  return {
1162
1195
  writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
1163
1196
  endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
1164
- userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
1165
- convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
1166
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
1197
+ projectId: (_f = process.env["RAINDROP_PROJECT_ID"]) != null ? _f : file.project_id,
1198
+ userId: (_h = (_g = process.env["RAINDROP_USER_ID"]) != null ? _g : file.user_id) != null ? _h : systemUser,
1199
+ convoId: ((_i = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _i.trim()) || void 0,
1200
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_j = file.debug) != null ? _j : false,
1167
1201
  enabled: (() => {
1168
1202
  var _a2;
1169
1203
  const env = process.env["RAINDROP_ENABLED"];
@@ -1171,7 +1205,7 @@ function loadConfig() {
1171
1205
  const lower = env.toLowerCase();
1172
1206
  return lower !== "false" && env !== "0";
1173
1207
  })(),
1174
- eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
1208
+ eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
1175
1209
  customProperties,
1176
1210
  selfDiagnostics
1177
1211
  };
@@ -2337,6 +2371,7 @@ async function executeTool(args) {
2337
2371
  writeKey: config.writeKey,
2338
2372
  endpoint: config.endpoint,
2339
2373
  debug: false,
2374
+ projectId: config.projectId,
2340
2375
  enabled: true
2341
2376
  });
2342
2377
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/claude-code",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "Raindrop observability for Claude Code CLI — automatic session, tool call, and prompt tracing via hooks",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -58,7 +58,7 @@
58
58
  "tsup": "^8.4.0",
59
59
  "typescript": "^5.3.3",
60
60
  "vitest": "^2.1.0",
61
- "@raindrop-ai/core": "0.0.3"
61
+ "@raindrop-ai/core": "0.0.4"
62
62
  },
63
63
  "publishConfig": {
64
64
  "access": "public"