raindrop-ai 0.1.1 → 0.1.2
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 +21 -0
- package/dist/{chunk-DP5ED5DR.mjs → chunk-UICWZEYM.mjs} +251 -48
- package/dist/index.d.mts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +358 -72
- package/dist/index.mjs +130 -25
- package/dist/tracing/index.js +229 -48
- package/dist/tracing/index.mjs +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
3
|
+
MAX_RETRY_DELAY_MS,
|
|
4
|
+
POST_SHUTDOWN_TIMEOUT_MS,
|
|
5
|
+
SHUTDOWN_DEADLINE_MS,
|
|
6
|
+
capText,
|
|
2
7
|
mirrorPartialEventToLocalDebugger,
|
|
3
8
|
package_default,
|
|
9
|
+
raceWithTimeout,
|
|
10
|
+
rateLimitedLog,
|
|
11
|
+
redactUrlForLog,
|
|
4
12
|
resolveLocalDebuggerBaseUrl,
|
|
13
|
+
resolveMaxTextFieldChars,
|
|
14
|
+
setDefaultMaxTextFieldChars,
|
|
15
|
+
stringifyBounded,
|
|
5
16
|
tracing
|
|
6
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UICWZEYM.mjs";
|
|
7
18
|
import {
|
|
8
19
|
__commonJS,
|
|
9
20
|
__toESM
|
|
@@ -9957,7 +9968,6 @@ var PartialClientAiTrack = ClientAiTrack.extend({
|
|
|
9957
9968
|
var AttachmentTypeSchema = z.enum(["code", "text", "image", "iframe"]);
|
|
9958
9969
|
|
|
9959
9970
|
// src/index.ts
|
|
9960
|
-
process.env.TRACELOOP_TELEMETRY = "false";
|
|
9961
9971
|
var MAX_INGEST_SIZE_BYTES = 1 * 1024 * 1024;
|
|
9962
9972
|
var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
|
|
9963
9973
|
var SELF_DIAGNOSTICS_SOURCE_DEFAULT = "agent_reporting_tool";
|
|
@@ -10082,12 +10092,12 @@ function parseRetryAfter(headers) {
|
|
|
10082
10092
|
}
|
|
10083
10093
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
10084
10094
|
if (previousError && typeof previousError.retryAfterMs === "number") {
|
|
10085
|
-
return Math.max(0, previousError.retryAfterMs);
|
|
10095
|
+
return Math.min(Math.max(0, previousError.retryAfterMs), MAX_RETRY_DELAY_MS);
|
|
10086
10096
|
}
|
|
10087
10097
|
if (attemptNumber <= 1) return 0;
|
|
10088
10098
|
const base = 500;
|
|
10089
10099
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
10090
|
-
return base * factor;
|
|
10100
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
10091
10101
|
}
|
|
10092
10102
|
async function withRetry(operation, opName, maxAttempts, debugLogs = false) {
|
|
10093
10103
|
var _a;
|
|
@@ -10126,7 +10136,16 @@ var Raindrop = class {
|
|
|
10126
10136
|
this.partialEventBuffer = /* @__PURE__ */ new Map();
|
|
10127
10137
|
this.partialEventTimeouts = /* @__PURE__ */ new Map();
|
|
10128
10138
|
this.inFlightRequests = /* @__PURE__ */ new Set();
|
|
10139
|
+
/**
|
|
10140
|
+
* Set once `close()` begins and never cleared. Sends issued after the
|
|
10141
|
+
* drain window (stragglers, or flush work the deadline abandoned
|
|
10142
|
+
* mid-drain) run as a single short attempt instead of regaining the full
|
|
10143
|
+
* retry schedule.
|
|
10144
|
+
*/
|
|
10145
|
+
this.hasShutdown = false;
|
|
10129
10146
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
10147
|
+
setDefaultMaxTextFieldChars(config.maxTextFieldChars);
|
|
10148
|
+
this.maxTextFieldChars = resolveMaxTextFieldChars(config.maxTextFieldChars);
|
|
10130
10149
|
this.writeKey = (_a = config.writeKey) != null ? _a : "";
|
|
10131
10150
|
this.localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
|
|
10132
10151
|
this.apiUrl = (_b = this.formatEndpoint(config.endpoint)) != null ? _b : `https://api.raindrop.ai/v1/`;
|
|
@@ -10349,14 +10368,16 @@ var Raindrop = class {
|
|
|
10349
10368
|
eventId,
|
|
10350
10369
|
userId,
|
|
10351
10370
|
model,
|
|
10352
|
-
input,
|
|
10353
|
-
output,
|
|
10371
|
+
input: rawInput,
|
|
10372
|
+
output: rawOutput,
|
|
10354
10373
|
convoId,
|
|
10355
10374
|
attachments,
|
|
10356
10375
|
properties,
|
|
10357
10376
|
timestamp,
|
|
10358
10377
|
...rest
|
|
10359
10378
|
} = e;
|
|
10379
|
+
const input = capText(rawInput, this.maxTextFieldChars);
|
|
10380
|
+
const output = capText(rawOutput, this.maxTextFieldChars);
|
|
10360
10381
|
const customEventId = eventId != null ? eventId : crypto.randomUUID();
|
|
10361
10382
|
const parsedAttachments = AttachmentSchema.array().safeParse(attachments != null ? attachments : []);
|
|
10362
10383
|
if (!parsedAttachments.success) {
|
|
@@ -10563,20 +10584,51 @@ var Raindrop = class {
|
|
|
10563
10584
|
void fetch(`${localUrl}${endpoint}`, {
|
|
10564
10585
|
method: "POST",
|
|
10565
10586
|
headers,
|
|
10566
|
-
body: JSON.stringify(body)
|
|
10587
|
+
body: JSON.stringify(body),
|
|
10588
|
+
// Bounded like the cloud paths: a black-holed Workshop URL must not
|
|
10589
|
+
// pin connections indefinitely (fire-and-forget still holds sockets).
|
|
10590
|
+
signal: AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS)
|
|
10567
10591
|
}).catch(() => {
|
|
10568
10592
|
});
|
|
10569
10593
|
} catch (e) {
|
|
10570
10594
|
}
|
|
10571
10595
|
}
|
|
10596
|
+
/**
|
|
10597
|
+
* Attempts/timeout budget for one POST, honoring the close() deadline.
|
|
10598
|
+
* Returns `null` when the shutdown budget is exhausted — the caller must
|
|
10599
|
+
* drop the payload instead of issuing a request that could outlive
|
|
10600
|
+
* process exit. Checked fresh on every send so a deadline that expires
|
|
10601
|
+
* mid-drain takes effect immediately.
|
|
10602
|
+
*/
|
|
10603
|
+
requestBudget() {
|
|
10604
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
10605
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
10606
|
+
if (remainingMs <= 0) return null;
|
|
10607
|
+
return { maxAttempts: 1, timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs) };
|
|
10608
|
+
}
|
|
10609
|
+
if (this.hasShutdown) {
|
|
10610
|
+
return { maxAttempts: 1, timeoutMs: POST_SHUTDOWN_TIMEOUT_MS };
|
|
10611
|
+
}
|
|
10612
|
+
return { maxAttempts: 3, timeoutMs: DEFAULT_REQUEST_TIMEOUT_MS };
|
|
10613
|
+
}
|
|
10614
|
+
warnShutdownDrop(what) {
|
|
10615
|
+
rateLimitedLog(
|
|
10616
|
+
"js-sdk.shutdown_deadline",
|
|
10617
|
+
() => console.warn(`[raindrop] shutdown flush deadline exceeded; dropping ${what}`)
|
|
10618
|
+
);
|
|
10619
|
+
}
|
|
10572
10620
|
async sendBatchToApi(events, endpoint) {
|
|
10573
|
-
var _a;
|
|
10574
10621
|
const body = events.map(({ type, ...event }) => event);
|
|
10575
10622
|
this.mirrorBatchToLocalDebugger(endpoint, body);
|
|
10576
10623
|
if (this.localOnly) {
|
|
10577
10624
|
return;
|
|
10578
10625
|
}
|
|
10579
|
-
const opName = `POST ${endpoint}`;
|
|
10626
|
+
const opName = `POST ${redactUrlForLog(this.apiUrl + endpoint)}`;
|
|
10627
|
+
const budget = this.requestBudget();
|
|
10628
|
+
if (!budget) {
|
|
10629
|
+
this.warnShutdownDrop(`${events.length} event(s) for ${endpoint}`);
|
|
10630
|
+
return;
|
|
10631
|
+
}
|
|
10580
10632
|
try {
|
|
10581
10633
|
const response = await withRetry(
|
|
10582
10634
|
async () => {
|
|
@@ -10586,7 +10638,11 @@ var Raindrop = class {
|
|
|
10586
10638
|
"Content-Type": "application/json",
|
|
10587
10639
|
Authorization: `Bearer ${this.writeKey}`
|
|
10588
10640
|
},
|
|
10589
|
-
body: JSON.stringify(body)
|
|
10641
|
+
body: JSON.stringify(body),
|
|
10642
|
+
// Bounded: a dead or black-holed endpoint must never pin this
|
|
10643
|
+
// request (and the flush/close paths awaiting it) until the OS
|
|
10644
|
+
// gives up.
|
|
10645
|
+
signal: AbortSignal.timeout(budget.timeoutMs)
|
|
10590
10646
|
});
|
|
10591
10647
|
if (!resp.ok) {
|
|
10592
10648
|
const text = await resp.text().catch(() => "");
|
|
@@ -10600,7 +10656,7 @@ var Raindrop = class {
|
|
|
10600
10656
|
return resp;
|
|
10601
10657
|
},
|
|
10602
10658
|
opName,
|
|
10603
|
-
|
|
10659
|
+
budget.maxAttempts,
|
|
10604
10660
|
this.debugLogs
|
|
10605
10661
|
);
|
|
10606
10662
|
if (this.debugLogs) {
|
|
@@ -10609,9 +10665,18 @@ var Raindrop = class {
|
|
|
10609
10665
|
return response;
|
|
10610
10666
|
} catch (error) {
|
|
10611
10667
|
if (error == null ? void 0 : error.response) {
|
|
10612
|
-
|
|
10668
|
+
rateLimitedLog(
|
|
10669
|
+
`js-sdk.batch_failed.${endpoint}`,
|
|
10670
|
+
() => console.error(`[raindrop] Error in response (${opName}): ` + error.response.data)
|
|
10671
|
+
);
|
|
10613
10672
|
} else {
|
|
10614
|
-
|
|
10673
|
+
rateLimitedLog(
|
|
10674
|
+
`js-sdk.batch_failed.${endpoint}`,
|
|
10675
|
+
() => {
|
|
10676
|
+
var _a;
|
|
10677
|
+
return console.error(`[raindrop] Error (${opName}): ` + ((_a = error == null ? void 0 : error.message) != null ? _a : String(error)));
|
|
10678
|
+
}
|
|
10679
|
+
);
|
|
10615
10680
|
}
|
|
10616
10681
|
}
|
|
10617
10682
|
}
|
|
@@ -10668,6 +10733,12 @@ var Raindrop = class {
|
|
|
10668
10733
|
console.warn("[raindrop] trackAiPartial requires an eventId.");
|
|
10669
10734
|
return Promise.resolve();
|
|
10670
10735
|
}
|
|
10736
|
+
if (typeof rest.input === "string") {
|
|
10737
|
+
rest.input = capText(rest.input, this.maxTextFieldChars);
|
|
10738
|
+
}
|
|
10739
|
+
if (typeof rest.output === "string") {
|
|
10740
|
+
rest.output = capText(rest.output, this.maxTextFieldChars);
|
|
10741
|
+
}
|
|
10671
10742
|
const existingEvent = this.partialEventBuffer.get(eventId) || {};
|
|
10672
10743
|
const mergedEvent = this.deepMergeObjects(
|
|
10673
10744
|
{ ...existingEvent },
|
|
@@ -10698,7 +10769,7 @@ var Raindrop = class {
|
|
|
10698
10769
|
this.partialEventTimeouts.set(eventId, newTimeout);
|
|
10699
10770
|
if (this.debugLogs) {
|
|
10700
10771
|
console.log(
|
|
10701
|
-
`[raindrop] Updated partial event buffer for eventId: ${eventId}. Timeout reset (
|
|
10772
|
+
`[raindrop] Updated partial event buffer for eventId: ${eventId}. Timeout reset (2s). Event: ${stringifyBounded(mergedEvent, 2e3)}`
|
|
10702
10773
|
);
|
|
10703
10774
|
}
|
|
10704
10775
|
return Promise.resolve();
|
|
@@ -10714,9 +10785,12 @@ var Raindrop = class {
|
|
|
10714
10785
|
try {
|
|
10715
10786
|
await flushPromise;
|
|
10716
10787
|
} catch (error) {
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10788
|
+
rateLimitedLog(
|
|
10789
|
+
"js-sdk.partial_flush_failed",
|
|
10790
|
+
() => console.error(
|
|
10791
|
+
`[raindrop] Failed to flush partial event ${eventId}:`,
|
|
10792
|
+
error instanceof Error ? error : String(error)
|
|
10793
|
+
)
|
|
10720
10794
|
);
|
|
10721
10795
|
} finally {
|
|
10722
10796
|
this.inFlightRequests.delete(flushPromise);
|
|
@@ -10784,7 +10858,7 @@ var Raindrop = class {
|
|
|
10784
10858
|
const parsed = PartialClientAiTrack.partial().safeParse(eventToSend);
|
|
10785
10859
|
if (!parsed.success) {
|
|
10786
10860
|
console.warn(
|
|
10787
|
-
`[raindrop] Invalid accumulated data for partial eventId ${eventId}. Event not sent. Errors: ${this.formatZodError(parsed.error)} Data: ${
|
|
10861
|
+
`[raindrop] Invalid accumulated data for partial eventId ${eventId}. Event not sent. Errors: ${this.formatZodError(parsed.error)} Data: ${stringifyBounded(eventToSend, 2e3)}`
|
|
10788
10862
|
);
|
|
10789
10863
|
return;
|
|
10790
10864
|
}
|
|
@@ -10808,7 +10882,6 @@ var Raindrop = class {
|
|
|
10808
10882
|
* @param event - The event data conforming to ClientAiTrack schema.
|
|
10809
10883
|
*/
|
|
10810
10884
|
async sendPartialEvent(event) {
|
|
10811
|
-
var _a;
|
|
10812
10885
|
const endpoint = "events/track_partial";
|
|
10813
10886
|
const opName = `POST ${endpoint}`;
|
|
10814
10887
|
mirrorPartialEventToLocalDebugger(event, {
|
|
@@ -10820,6 +10893,11 @@ var Raindrop = class {
|
|
|
10820
10893
|
if (this.localOnly) {
|
|
10821
10894
|
return;
|
|
10822
10895
|
}
|
|
10896
|
+
const budget = this.requestBudget();
|
|
10897
|
+
if (!budget) {
|
|
10898
|
+
this.warnShutdownDrop(`partial event ${event.event_id}`);
|
|
10899
|
+
return;
|
|
10900
|
+
}
|
|
10823
10901
|
try {
|
|
10824
10902
|
const response = await withRetry(
|
|
10825
10903
|
async () => {
|
|
@@ -10829,8 +10907,10 @@ var Raindrop = class {
|
|
|
10829
10907
|
"Content-Type": "application/json",
|
|
10830
10908
|
Authorization: `Bearer ${this.writeKey}`
|
|
10831
10909
|
},
|
|
10832
|
-
body: JSON.stringify(event)
|
|
10910
|
+
body: JSON.stringify(event),
|
|
10833
10911
|
// Send the single event object
|
|
10912
|
+
// Bounded: see sendBatchToApi.
|
|
10913
|
+
signal: AbortSignal.timeout(budget.timeoutMs)
|
|
10834
10914
|
});
|
|
10835
10915
|
if (!resp.ok) {
|
|
10836
10916
|
const errorBody = await resp.text().catch(() => "");
|
|
@@ -10844,7 +10924,7 @@ var Raindrop = class {
|
|
|
10844
10924
|
return resp;
|
|
10845
10925
|
},
|
|
10846
10926
|
opName,
|
|
10847
|
-
|
|
10927
|
+
budget.maxAttempts,
|
|
10848
10928
|
this.debugLogs
|
|
10849
10929
|
);
|
|
10850
10930
|
if (this.debugLogs) {
|
|
@@ -10854,8 +10934,14 @@ var Raindrop = class {
|
|
|
10854
10934
|
}
|
|
10855
10935
|
return response;
|
|
10856
10936
|
} catch (error) {
|
|
10857
|
-
|
|
10858
|
-
|
|
10937
|
+
rateLimitedLog(
|
|
10938
|
+
"js-sdk.partial_failed",
|
|
10939
|
+
() => {
|
|
10940
|
+
var _a;
|
|
10941
|
+
return console.error(
|
|
10942
|
+
`[raindrop] Failed to send partial event ${event.event_id} to ${endpoint}: ${(_a = error == null ? void 0 : error.message) != null ? _a : String(error)}`
|
|
10943
|
+
);
|
|
10944
|
+
}
|
|
10859
10945
|
);
|
|
10860
10946
|
throw error;
|
|
10861
10947
|
}
|
|
@@ -10874,7 +10960,21 @@ var Raindrop = class {
|
|
|
10874
10960
|
}
|
|
10875
10961
|
}
|
|
10876
10962
|
async close() {
|
|
10877
|
-
|
|
10963
|
+
this.hasShutdown = true;
|
|
10964
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
10965
|
+
try {
|
|
10966
|
+
await this.closeWithinDeadline();
|
|
10967
|
+
} finally {
|
|
10968
|
+
this.shutdownDeadlineAt = void 0;
|
|
10969
|
+
}
|
|
10970
|
+
}
|
|
10971
|
+
async closeWithinDeadline() {
|
|
10972
|
+
const remainingMs = () => {
|
|
10973
|
+
var _a;
|
|
10974
|
+
return Math.max(0, ((_a = this.shutdownDeadlineAt) != null ? _a : Date.now()) - Date.now());
|
|
10975
|
+
};
|
|
10976
|
+
const tracingSettled = await raceWithTimeout(this._tracing.close(), remainingMs());
|
|
10977
|
+
if (!tracingSettled) this.warnShutdownDrop("in-flight trace flush");
|
|
10878
10978
|
try {
|
|
10879
10979
|
await this.flush();
|
|
10880
10980
|
} catch (error) {
|
|
@@ -10903,7 +11003,12 @@ var Raindrop = class {
|
|
|
10903
11003
|
);
|
|
10904
11004
|
}
|
|
10905
11005
|
try {
|
|
10906
|
-
await
|
|
11006
|
+
const settled = await raceWithTimeout(
|
|
11007
|
+
Promise.allSettled(Array.from(this.inFlightRequests)).then(() => {
|
|
11008
|
+
}),
|
|
11009
|
+
remainingMs()
|
|
11010
|
+
);
|
|
11011
|
+
if (!settled) this.warnShutdownDrop("in-flight partial event request(s)");
|
|
10907
11012
|
} catch (e) {
|
|
10908
11013
|
console.error(`[raindrop] Error waiting for in-flight requests:`, e);
|
|
10909
11014
|
}
|