@raindrop-ai/claude-code 0.0.13 → 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/index.d.cts CHANGED
@@ -35,7 +35,6 @@ type SpanIds = {
35
35
  spanIdB64: string;
36
36
  parentSpanIdB64?: string;
37
37
  };
38
-
39
38
  type Attachment = {
40
39
  type: string;
41
40
  role: string;
@@ -79,6 +78,26 @@ type EventShipperOptions = {
79
78
  libraryName?: string;
80
79
  libraryVersion?: string;
81
80
  defaultEventName?: string;
81
+ /**
82
+ * Explicit Workshop / local debugger URL. Wins over env vars + auto-detect.
83
+ * Pass `null` to opt out of all mirroring (including auto-detect).
84
+ */
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;
93
+ /**
94
+ * Per-field character cap applied to event input/output BEFORE buffering
95
+ * or serialization, so oversized payloads cost the cap — not the payload —
96
+ * on the calling code path. Truncated fields end with
97
+ * `...[truncated by raindrop]` and never exceed the cap, marker included.
98
+ * Defaults to 1,000,000 (matching the Python SDK).
99
+ */
100
+ maxTextFieldChars?: number;
82
101
  };
83
102
  declare class EventShipper$1 {
84
103
  private baseUrl;
@@ -89,14 +108,46 @@ declare class EventShipper$1 {
89
108
  private sdkName;
90
109
  private prefix;
91
110
  private defaultEventName;
111
+ private projectId;
92
112
  private context;
93
113
  private buffers;
94
114
  private sticky;
95
115
  private timers;
96
116
  private inFlight;
117
+ private maxTextFieldCharsOpt;
118
+ /**
119
+ * Epoch ms deadline while `shutdown()` is draining; undefined otherwise.
120
+ * Checked before every POST issued during the final flush.
121
+ */
122
+ private shutdownDeadlineAt;
123
+ /**
124
+ * Set once `shutdown()` begins and never cleared. Sends issued after the
125
+ * drain window (stragglers, or flush work the deadline abandoned
126
+ * mid-drain) run as a single short attempt instead of regaining the full
127
+ * retry schedule.
128
+ */
129
+ private hasShutdown;
130
+ /** URL of the local debugger / Workshop daemon, when one is reachable. */
131
+ private localDebuggerUrl;
97
132
  constructor(opts: EventShipperOptions);
98
133
  isDebugEnabled(): boolean;
99
134
  private authHeaders;
135
+ private requestHeaders;
136
+ /**
137
+ * Build the retry/timeout options for one POST, honoring the shutdown
138
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
139
+ * the caller must drop the payload (with a rate-limited warning) instead
140
+ * of issuing a request that could outlive process exit.
141
+ *
142
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
143
+ * path is mid-drain takes effect immediately: no further retries, and the
144
+ * per-attempt timeout is clamped to the remaining window. After
145
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
146
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
147
+ * run as a single short attempt rather than regaining the full retry
148
+ * schedule.
149
+ */
150
+ private requestOpts;
100
151
  patch(eventId: string, patch: Patch): Promise<void>;
101
152
  finish(eventId: string, patch: {
102
153
  output?: string;
@@ -108,8 +159,26 @@ declare class EventShipper$1 {
108
159
  shutdown(): Promise<void>;
109
160
  trackSignal(signal: SignalInput): Promise<void>;
110
161
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
162
+ private warnShutdownDrop;
111
163
  private flushOne;
112
164
  }
165
+ /**
166
+ * Hook fired per OTLP span right before the span is shipped (to the Raindrop
167
+ * API and to a local debugger). Lets callers inspect, rewrite, or drop the
168
+ * entire span — not just individual attributes — which is more flexible than
169
+ * an attribute-level hook (you can rename attributes, add new ones, drop the
170
+ * span outright, etc.).
171
+ *
172
+ * Return values:
173
+ * - `undefined` or the same span: ship the span unchanged.
174
+ * - a new `OtlpSpan`: ship the returned span in place of the original.
175
+ * - `null`: drop the span entirely from every ship path.
176
+ *
177
+ * The hook runs on the hot path — keep it synchronous and side-effect-free.
178
+ * If the hook throws, the span is dropped (fail-closed) so a buggy hook can
179
+ * never accidentally ship raw, un-redacted spans.
180
+ */
181
+ type TransformSpanHook = (span: OtlpSpan) => OtlpSpan | null | undefined;
113
182
 
114
183
  type InternalSpan = {
115
184
  ids: SpanIds;
@@ -130,6 +199,61 @@ type TraceShipperOptions = {
130
199
  sdkName?: string;
131
200
  serviceName?: string;
132
201
  serviceVersion?: string;
202
+ /**
203
+ * Explicit Workshop / local debugger URL. Wins over env vars + auto-detect.
204
+ * Pass `null` to opt out of all mirroring (including auto-detect).
205
+ */
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;
214
+ /**
215
+ * Per-span hook that fires for every OTLP span right before the span is
216
+ * shipped (both to the Raindrop API and to a local debugger). Lets callers
217
+ * inspect, rewrite, or drop entire spans — rename attributes, add new ones,
218
+ * scrub additional secret-shaped values inside `ai.prompt.messages` /
219
+ * `ai.toolCall.args`, etc.
220
+ *
221
+ * Return values:
222
+ * - `undefined` or the same span reference: ship the span unchanged.
223
+ * - a new `OtlpSpan`: ship the returned span in place of the original.
224
+ * - `null`: drop the span entirely from every ship path.
225
+ *
226
+ * The hook runs BEFORE the default redactor (which is the always-on floor
227
+ * for documented BYOK secrets). The default redactor still runs on the
228
+ * post-transform span unless `disableDefaultRedaction` is set, so even if
229
+ * a custom transform overlooks a secret-shaped attribute, the floor catches
230
+ * it.
231
+ *
232
+ * The hook runs on the hot path — keep it synchronous and side-effect-free.
233
+ * If the hook itself throws, the span is dropped (fail-closed) so a buggy
234
+ * hook can never accidentally ship raw, un-redacted spans.
235
+ */
236
+ transformSpan?: TransformSpanHook;
237
+ /**
238
+ * Disable the built-in default span transformer (which scrubs documented
239
+ * secret-shaped properties — `apiKey`, `secretAccessKey`, `privateKey`,
240
+ * etc. — inside `ai.request.providerOptions` and
241
+ * `ai.response.providerMetadata`).
242
+ *
243
+ * Default: `false` (i.e. default redaction is on). Setting this to `true`
244
+ * disables the floor entirely; provide a custom `transformSpan` if you
245
+ * still want some redaction in that case.
246
+ */
247
+ disableDefaultRedaction?: boolean;
248
+ /**
249
+ * Per-attribute character cap applied to every span attribute string value
250
+ * right before the span enters a ship path, so a multi-MB prompt/tool
251
+ * payload can never make the batch `JSON.stringify` (which runs on the
252
+ * event loop) cost seconds. Truncated values end with
253
+ * `...[truncated by raindrop]` and never exceed the cap, marker included.
254
+ * Defaults to 1,000,000 (matching the Python SDK).
255
+ */
256
+ maxTextFieldChars?: number;
133
257
  };
134
258
  declare class TraceShipper$1 {
135
259
  private baseUrl;
@@ -144,14 +268,58 @@ declare class TraceShipper$1 {
144
268
  private flushIntervalMs;
145
269
  private maxBatchSize;
146
270
  private maxQueueSize;
271
+ private projectId;
147
272
  private queue;
148
273
  private timer;
149
274
  private inFlight;
150
- /** URL of the local debugger (from RAINDROP_LOCAL_DEBUGGER env var). */
275
+ /** URL of the local debugger / Workshop daemon, when one is reachable. */
151
276
  private localDebuggerUrl;
277
+ private transformSpanHook;
278
+ private disableDefaultRedaction;
279
+ private maxTextFieldCharsOpt;
280
+ /**
281
+ * Epoch ms deadline while `shutdown()` is draining; undefined otherwise.
282
+ * Checked before every batch POST issued during the final flush.
283
+ */
284
+ private shutdownDeadlineAt;
285
+ /**
286
+ * Set once `shutdown()` begins and never cleared. Sends issued after the
287
+ * drain window (stragglers, or flush work the deadline abandoned
288
+ * mid-drain) run as a single short attempt instead of regaining the full
289
+ * retry schedule.
290
+ */
291
+ private hasShutdown;
152
292
  constructor(opts: TraceShipperOptions);
293
+ /**
294
+ * Cap every string attribute value on the span. O(#attributes) length
295
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
296
+ * pipeline so the default secret-scrub still sees parseable JSON in
297
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
298
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
299
+ * in the surviving prefix).
300
+ *
301
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
302
+ * for span content, matching the Python SDK and the OTel SDK convention.
303
+ */
304
+ private capSpanAttributes;
305
+ /**
306
+ * Apply the user `transformSpan` hook (if any) followed by the default
307
+ * redactor (unless disabled). Returns either the (possibly new) span to
308
+ * ship, or `null` to drop the span entirely.
309
+ *
310
+ * Ordering: user hook runs first so callers can rewrite the span freely
311
+ * (rename attrs, add new ones, scrub things the default doesn't know
312
+ * about). The default redactor then runs on whatever the user produced,
313
+ * acting as the always-on floor for documented BYOK secrets. If the user
314
+ * sets `disableDefaultRedaction: true`, the floor is skipped.
315
+ *
316
+ * Fail-closed: if the user hook throws, the span is dropped — a buggy
317
+ * hook can never accidentally ship raw, un-redacted spans.
318
+ */
319
+ private redactSpan;
153
320
  isDebugEnabled(): boolean;
154
321
  private authHeaders;
322
+ private requestHeaders;
155
323
  startSpan(args: {
156
324
  name: string;
157
325
  parent?: {
@@ -163,6 +331,7 @@ declare class TraceShipper$1 {
163
331
  attributes?: Array<OtlpKeyValue | undefined>;
164
332
  startTimeUnixNano?: string;
165
333
  }): InternalSpan;
334
+ private mirrorToLocalDebugger;
166
335
  endSpan(span: InternalSpan, extra?: {
167
336
  attributes?: InternalSpan["attributes"];
168
337
  error?: unknown;
@@ -183,6 +352,8 @@ declare class TraceShipper$1 {
183
352
  }): void;
184
353
  enqueue(span: OtlpSpan): void;
185
354
  flush(): Promise<void>;
355
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
356
+ private requestOpts;
186
357
  shutdown(): Promise<void>;
187
358
  }
188
359
 
@@ -233,6 +404,7 @@ interface SelfDiagnosticsConfig {
233
404
  interface ConfigFile {
234
405
  write_key?: string;
235
406
  api_url?: string;
407
+ project_id?: string;
236
408
  user_id?: string;
237
409
  debug?: boolean;
238
410
  enabled?: boolean;
@@ -243,6 +415,13 @@ interface ConfigFile {
243
415
  interface RaindropConfig {
244
416
  writeKey: string;
245
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;
246
425
  userId: string;
247
426
  convoId?: string;
248
427
  debug: boolean;
package/dist/index.d.ts CHANGED
@@ -35,7 +35,6 @@ type SpanIds = {
35
35
  spanIdB64: string;
36
36
  parentSpanIdB64?: string;
37
37
  };
38
-
39
38
  type Attachment = {
40
39
  type: string;
41
40
  role: string;
@@ -79,6 +78,26 @@ type EventShipperOptions = {
79
78
  libraryName?: string;
80
79
  libraryVersion?: string;
81
80
  defaultEventName?: string;
81
+ /**
82
+ * Explicit Workshop / local debugger URL. Wins over env vars + auto-detect.
83
+ * Pass `null` to opt out of all mirroring (including auto-detect).
84
+ */
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;
93
+ /**
94
+ * Per-field character cap applied to event input/output BEFORE buffering
95
+ * or serialization, so oversized payloads cost the cap — not the payload —
96
+ * on the calling code path. Truncated fields end with
97
+ * `...[truncated by raindrop]` and never exceed the cap, marker included.
98
+ * Defaults to 1,000,000 (matching the Python SDK).
99
+ */
100
+ maxTextFieldChars?: number;
82
101
  };
83
102
  declare class EventShipper$1 {
84
103
  private baseUrl;
@@ -89,14 +108,46 @@ declare class EventShipper$1 {
89
108
  private sdkName;
90
109
  private prefix;
91
110
  private defaultEventName;
111
+ private projectId;
92
112
  private context;
93
113
  private buffers;
94
114
  private sticky;
95
115
  private timers;
96
116
  private inFlight;
117
+ private maxTextFieldCharsOpt;
118
+ /**
119
+ * Epoch ms deadline while `shutdown()` is draining; undefined otherwise.
120
+ * Checked before every POST issued during the final flush.
121
+ */
122
+ private shutdownDeadlineAt;
123
+ /**
124
+ * Set once `shutdown()` begins and never cleared. Sends issued after the
125
+ * drain window (stragglers, or flush work the deadline abandoned
126
+ * mid-drain) run as a single short attempt instead of regaining the full
127
+ * retry schedule.
128
+ */
129
+ private hasShutdown;
130
+ /** URL of the local debugger / Workshop daemon, when one is reachable. */
131
+ private localDebuggerUrl;
97
132
  constructor(opts: EventShipperOptions);
98
133
  isDebugEnabled(): boolean;
99
134
  private authHeaders;
135
+ private requestHeaders;
136
+ /**
137
+ * Build the retry/timeout options for one POST, honoring the shutdown
138
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
139
+ * the caller must drop the payload (with a rate-limited warning) instead
140
+ * of issuing a request that could outlive process exit.
141
+ *
142
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
143
+ * path is mid-drain takes effect immediately: no further retries, and the
144
+ * per-attempt timeout is clamped to the remaining window. After
145
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
146
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
147
+ * run as a single short attempt rather than regaining the full retry
148
+ * schedule.
149
+ */
150
+ private requestOpts;
100
151
  patch(eventId: string, patch: Patch): Promise<void>;
101
152
  finish(eventId: string, patch: {
102
153
  output?: string;
@@ -108,8 +159,26 @@ declare class EventShipper$1 {
108
159
  shutdown(): Promise<void>;
109
160
  trackSignal(signal: SignalInput): Promise<void>;
110
161
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
162
+ private warnShutdownDrop;
111
163
  private flushOne;
112
164
  }
165
+ /**
166
+ * Hook fired per OTLP span right before the span is shipped (to the Raindrop
167
+ * API and to a local debugger). Lets callers inspect, rewrite, or drop the
168
+ * entire span — not just individual attributes — which is more flexible than
169
+ * an attribute-level hook (you can rename attributes, add new ones, drop the
170
+ * span outright, etc.).
171
+ *
172
+ * Return values:
173
+ * - `undefined` or the same span: ship the span unchanged.
174
+ * - a new `OtlpSpan`: ship the returned span in place of the original.
175
+ * - `null`: drop the span entirely from every ship path.
176
+ *
177
+ * The hook runs on the hot path — keep it synchronous and side-effect-free.
178
+ * If the hook throws, the span is dropped (fail-closed) so a buggy hook can
179
+ * never accidentally ship raw, un-redacted spans.
180
+ */
181
+ type TransformSpanHook = (span: OtlpSpan) => OtlpSpan | null | undefined;
113
182
 
114
183
  type InternalSpan = {
115
184
  ids: SpanIds;
@@ -130,6 +199,61 @@ type TraceShipperOptions = {
130
199
  sdkName?: string;
131
200
  serviceName?: string;
132
201
  serviceVersion?: string;
202
+ /**
203
+ * Explicit Workshop / local debugger URL. Wins over env vars + auto-detect.
204
+ * Pass `null` to opt out of all mirroring (including auto-detect).
205
+ */
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;
214
+ /**
215
+ * Per-span hook that fires for every OTLP span right before the span is
216
+ * shipped (both to the Raindrop API and to a local debugger). Lets callers
217
+ * inspect, rewrite, or drop entire spans — rename attributes, add new ones,
218
+ * scrub additional secret-shaped values inside `ai.prompt.messages` /
219
+ * `ai.toolCall.args`, etc.
220
+ *
221
+ * Return values:
222
+ * - `undefined` or the same span reference: ship the span unchanged.
223
+ * - a new `OtlpSpan`: ship the returned span in place of the original.
224
+ * - `null`: drop the span entirely from every ship path.
225
+ *
226
+ * The hook runs BEFORE the default redactor (which is the always-on floor
227
+ * for documented BYOK secrets). The default redactor still runs on the
228
+ * post-transform span unless `disableDefaultRedaction` is set, so even if
229
+ * a custom transform overlooks a secret-shaped attribute, the floor catches
230
+ * it.
231
+ *
232
+ * The hook runs on the hot path — keep it synchronous and side-effect-free.
233
+ * If the hook itself throws, the span is dropped (fail-closed) so a buggy
234
+ * hook can never accidentally ship raw, un-redacted spans.
235
+ */
236
+ transformSpan?: TransformSpanHook;
237
+ /**
238
+ * Disable the built-in default span transformer (which scrubs documented
239
+ * secret-shaped properties — `apiKey`, `secretAccessKey`, `privateKey`,
240
+ * etc. — inside `ai.request.providerOptions` and
241
+ * `ai.response.providerMetadata`).
242
+ *
243
+ * Default: `false` (i.e. default redaction is on). Setting this to `true`
244
+ * disables the floor entirely; provide a custom `transformSpan` if you
245
+ * still want some redaction in that case.
246
+ */
247
+ disableDefaultRedaction?: boolean;
248
+ /**
249
+ * Per-attribute character cap applied to every span attribute string value
250
+ * right before the span enters a ship path, so a multi-MB prompt/tool
251
+ * payload can never make the batch `JSON.stringify` (which runs on the
252
+ * event loop) cost seconds. Truncated values end with
253
+ * `...[truncated by raindrop]` and never exceed the cap, marker included.
254
+ * Defaults to 1,000,000 (matching the Python SDK).
255
+ */
256
+ maxTextFieldChars?: number;
133
257
  };
134
258
  declare class TraceShipper$1 {
135
259
  private baseUrl;
@@ -144,14 +268,58 @@ declare class TraceShipper$1 {
144
268
  private flushIntervalMs;
145
269
  private maxBatchSize;
146
270
  private maxQueueSize;
271
+ private projectId;
147
272
  private queue;
148
273
  private timer;
149
274
  private inFlight;
150
- /** URL of the local debugger (from RAINDROP_LOCAL_DEBUGGER env var). */
275
+ /** URL of the local debugger / Workshop daemon, when one is reachable. */
151
276
  private localDebuggerUrl;
277
+ private transformSpanHook;
278
+ private disableDefaultRedaction;
279
+ private maxTextFieldCharsOpt;
280
+ /**
281
+ * Epoch ms deadline while `shutdown()` is draining; undefined otherwise.
282
+ * Checked before every batch POST issued during the final flush.
283
+ */
284
+ private shutdownDeadlineAt;
285
+ /**
286
+ * Set once `shutdown()` begins and never cleared. Sends issued after the
287
+ * drain window (stragglers, or flush work the deadline abandoned
288
+ * mid-drain) run as a single short attempt instead of regaining the full
289
+ * retry schedule.
290
+ */
291
+ private hasShutdown;
152
292
  constructor(opts: TraceShipperOptions);
293
+ /**
294
+ * Cap every string attribute value on the span. O(#attributes) length
295
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
296
+ * pipeline so the default secret-scrub still sees parseable JSON in
297
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
298
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
299
+ * in the surviving prefix).
300
+ *
301
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
302
+ * for span content, matching the Python SDK and the OTel SDK convention.
303
+ */
304
+ private capSpanAttributes;
305
+ /**
306
+ * Apply the user `transformSpan` hook (if any) followed by the default
307
+ * redactor (unless disabled). Returns either the (possibly new) span to
308
+ * ship, or `null` to drop the span entirely.
309
+ *
310
+ * Ordering: user hook runs first so callers can rewrite the span freely
311
+ * (rename attrs, add new ones, scrub things the default doesn't know
312
+ * about). The default redactor then runs on whatever the user produced,
313
+ * acting as the always-on floor for documented BYOK secrets. If the user
314
+ * sets `disableDefaultRedaction: true`, the floor is skipped.
315
+ *
316
+ * Fail-closed: if the user hook throws, the span is dropped — a buggy
317
+ * hook can never accidentally ship raw, un-redacted spans.
318
+ */
319
+ private redactSpan;
153
320
  isDebugEnabled(): boolean;
154
321
  private authHeaders;
322
+ private requestHeaders;
155
323
  startSpan(args: {
156
324
  name: string;
157
325
  parent?: {
@@ -163,6 +331,7 @@ declare class TraceShipper$1 {
163
331
  attributes?: Array<OtlpKeyValue | undefined>;
164
332
  startTimeUnixNano?: string;
165
333
  }): InternalSpan;
334
+ private mirrorToLocalDebugger;
166
335
  endSpan(span: InternalSpan, extra?: {
167
336
  attributes?: InternalSpan["attributes"];
168
337
  error?: unknown;
@@ -183,6 +352,8 @@ declare class TraceShipper$1 {
183
352
  }): void;
184
353
  enqueue(span: OtlpSpan): void;
185
354
  flush(): Promise<void>;
355
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
356
+ private requestOpts;
186
357
  shutdown(): Promise<void>;
187
358
  }
188
359
 
@@ -233,6 +404,7 @@ interface SelfDiagnosticsConfig {
233
404
  interface ConfigFile {
234
405
  write_key?: string;
235
406
  api_url?: string;
407
+ project_id?: string;
236
408
  user_id?: string;
237
409
  debug?: boolean;
238
410
  enabled?: boolean;
@@ -243,6 +415,13 @@ interface ConfigFile {
243
415
  interface RaindropConfig {
244
416
  writeKey: string;
245
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;
246
425
  userId: string;
247
426
  convoId?: string;
248
427
  debug: boolean;