@raindrop-ai/opencode-plugin 0.0.3 → 0.0.5
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.cjs +391 -172
- package/dist/index.js +391 -172
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -30,30 +30,34 @@ function loadConfig(projectDirectory) {
|
|
|
30
30
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
|
|
31
31
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
32
32
|
eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
|
|
33
|
-
userId: merged.user_id,
|
|
34
33
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
|
|
35
34
|
};
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
//
|
|
39
|
-
function
|
|
40
|
-
|
|
37
|
+
// ../core/dist/chunk-FTBZHS25.js
|
|
38
|
+
function getCrypto() {
|
|
39
|
+
const c = globalThis.crypto;
|
|
40
|
+
return c;
|
|
41
41
|
}
|
|
42
42
|
function randomBytes(length) {
|
|
43
|
-
const
|
|
43
|
+
const cryptoObj = getCrypto();
|
|
44
44
|
const out = new Uint8Array(length);
|
|
45
|
-
if (
|
|
46
|
-
|
|
45
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
46
|
+
cryptoObj.getRandomValues(out);
|
|
47
47
|
return out;
|
|
48
48
|
}
|
|
49
49
|
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
50
50
|
return out;
|
|
51
51
|
}
|
|
52
52
|
function base64Encode(bytes) {
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
53
|
+
const maybeBuffer = globalThis.Buffer;
|
|
54
|
+
if (maybeBuffer) {
|
|
55
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
56
|
+
}
|
|
55
57
|
let binary = "";
|
|
56
|
-
for (let i2 = 0; i2 < bytes.length; i2++)
|
|
58
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
59
|
+
binary += String.fromCharCode(bytes[i2]);
|
|
60
|
+
}
|
|
57
61
|
const btoaFn = globalThis.btoa;
|
|
58
62
|
if (typeof btoaFn === "function") return btoaFn(binary);
|
|
59
63
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -74,70 +78,9 @@ function base64Encode(bytes) {
|
|
|
74
78
|
}
|
|
75
79
|
return out;
|
|
76
80
|
}
|
|
77
|
-
var SERVICE_NAME = "raindrop.opencode-plugin";
|
|
78
|
-
var PLUGIN_VERSION = "0.1.0";
|
|
79
|
-
var SpanStatusCode = {
|
|
80
|
-
ERROR: 2
|
|
81
|
-
};
|
|
82
81
|
function generateId() {
|
|
83
82
|
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
84
83
|
}
|
|
85
|
-
function createSpanIds(parent) {
|
|
86
|
-
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
87
|
-
const spanId = base64Encode(randomBytes(8));
|
|
88
|
-
return {
|
|
89
|
-
traceIdB64: traceId,
|
|
90
|
-
spanIdB64: spanId,
|
|
91
|
-
parentSpanIdB64: parent ? parent.spanIdB64 : void 0
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function nowUnixNanoString() {
|
|
95
|
-
return String(Date.now() * 1e6);
|
|
96
|
-
}
|
|
97
|
-
function attrString(key, value) {
|
|
98
|
-
if (value === void 0) return void 0;
|
|
99
|
-
return { key, value: { stringValue: value } };
|
|
100
|
-
}
|
|
101
|
-
function attrInt(key, value) {
|
|
102
|
-
if (value === void 0) return void 0;
|
|
103
|
-
if (!Number.isFinite(value)) return void 0;
|
|
104
|
-
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
105
|
-
}
|
|
106
|
-
function buildOtlpSpan(args) {
|
|
107
|
-
const attrs = [
|
|
108
|
-
attrString("span.id", args.ids.spanIdB64),
|
|
109
|
-
attrString("span.parent.id", args.ids.parentSpanIdB64),
|
|
110
|
-
...args.attributes
|
|
111
|
-
].filter((x) => x !== void 0);
|
|
112
|
-
const span = {
|
|
113
|
-
traceId: args.ids.traceIdB64,
|
|
114
|
-
spanId: args.ids.spanIdB64,
|
|
115
|
-
name: args.name,
|
|
116
|
-
startTimeUnixNano: args.startTimeUnixNano,
|
|
117
|
-
endTimeUnixNano: args.endTimeUnixNano
|
|
118
|
-
};
|
|
119
|
-
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
120
|
-
if (attrs.length) span.attributes = attrs;
|
|
121
|
-
if (args.status) span.status = args.status;
|
|
122
|
-
return span;
|
|
123
|
-
}
|
|
124
|
-
function buildExportTraceServiceRequest(spans) {
|
|
125
|
-
return {
|
|
126
|
-
resourceSpans: [
|
|
127
|
-
{
|
|
128
|
-
resource: {
|
|
129
|
-
attributes: [{ key: "service.name", value: { stringValue: SERVICE_NAME } }]
|
|
130
|
-
},
|
|
131
|
-
scopeSpans: [
|
|
132
|
-
{
|
|
133
|
-
scope: { name: SERVICE_NAME, version: PLUGIN_VERSION },
|
|
134
|
-
spans
|
|
135
|
-
}
|
|
136
|
-
]
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
84
|
function wait(ms) {
|
|
142
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
143
86
|
}
|
|
@@ -150,7 +93,7 @@ function parseRetryAfter(headers) {
|
|
|
150
93
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
151
94
|
if (!value) return void 0;
|
|
152
95
|
const asNumber = Number(value);
|
|
153
|
-
if (!Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
96
|
+
if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
154
97
|
const asDate = new Date(value).getTime();
|
|
155
98
|
if (!Number.isNaN(asDate)) {
|
|
156
99
|
const delta = asDate - Date.now();
|
|
@@ -159,26 +102,29 @@ function parseRetryAfter(headers) {
|
|
|
159
102
|
return void 0;
|
|
160
103
|
}
|
|
161
104
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
162
|
-
if (previousError
|
|
105
|
+
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
163
106
|
const v = previousError.retryAfterMs;
|
|
164
107
|
if (typeof v === "number") return Math.max(0, v);
|
|
165
108
|
}
|
|
166
109
|
if (attemptNumber <= 1) return 0;
|
|
167
|
-
|
|
110
|
+
const base = 500;
|
|
111
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
112
|
+
return base * factor;
|
|
168
113
|
}
|
|
169
114
|
async function withRetry(operation, opName, opts) {
|
|
115
|
+
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
170
116
|
let lastError = void 0;
|
|
171
|
-
for (let
|
|
172
|
-
if (
|
|
173
|
-
const delay = getRetryDelayMs(
|
|
117
|
+
for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
|
|
118
|
+
if (attemptNumber > 1) {
|
|
119
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
174
120
|
if (opts.debug) {
|
|
175
121
|
console.warn(
|
|
176
|
-
|
|
122
|
+
`${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
|
|
177
123
|
);
|
|
178
124
|
}
|
|
179
125
|
if (delay > 0) await wait(delay);
|
|
180
126
|
} else if (opts.debug) {
|
|
181
|
-
console.log(
|
|
127
|
+
console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
|
|
182
128
|
}
|
|
183
129
|
try {
|
|
184
130
|
return await operation();
|
|
@@ -187,20 +133,26 @@ async function withRetry(operation, opName, opts) {
|
|
|
187
133
|
if (opts.debug) {
|
|
188
134
|
const msg = err instanceof Error ? err.message : String(err);
|
|
189
135
|
console.warn(
|
|
190
|
-
|
|
136
|
+
`${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
|
|
191
137
|
);
|
|
192
138
|
}
|
|
193
|
-
if (
|
|
139
|
+
if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
|
|
140
|
+
break;
|
|
141
|
+
if (attemptNumber === opts.maxAttempts) break;
|
|
194
142
|
}
|
|
195
143
|
}
|
|
196
144
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
197
145
|
}
|
|
198
146
|
async function postJson(url, body, headers, opts) {
|
|
147
|
+
const opName = `POST ${url}`;
|
|
199
148
|
await withRetry(
|
|
200
149
|
async () => {
|
|
201
150
|
const resp = await fetch(url, {
|
|
202
151
|
method: "POST",
|
|
203
|
-
headers: {
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
...headers
|
|
155
|
+
},
|
|
204
156
|
body: JSON.stringify(body)
|
|
205
157
|
});
|
|
206
158
|
if (!resp.ok) {
|
|
@@ -210,13 +162,71 @@ async function postJson(url, body, headers, opts) {
|
|
|
210
162
|
);
|
|
211
163
|
const retryAfterMs = parseRetryAfter(resp.headers);
|
|
212
164
|
if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
|
|
165
|
+
err.retryable = resp.status === 429 || resp.status >= 500;
|
|
213
166
|
throw err;
|
|
214
167
|
}
|
|
215
168
|
},
|
|
216
|
-
|
|
169
|
+
opName,
|
|
217
170
|
opts
|
|
218
171
|
);
|
|
219
172
|
}
|
|
173
|
+
var SpanStatusCode = {
|
|
174
|
+
UNSET: 0,
|
|
175
|
+
OK: 1,
|
|
176
|
+
ERROR: 2
|
|
177
|
+
};
|
|
178
|
+
function createSpanIds(parent) {
|
|
179
|
+
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
180
|
+
const spanId = base64Encode(randomBytes(8));
|
|
181
|
+
return {
|
|
182
|
+
traceIdB64: traceId,
|
|
183
|
+
spanIdB64: spanId,
|
|
184
|
+
parentSpanIdB64: parent ? parent.spanIdB64 : void 0
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function nowUnixNanoString() {
|
|
188
|
+
return Date.now().toString() + "000000";
|
|
189
|
+
}
|
|
190
|
+
function attrString(key, value) {
|
|
191
|
+
if (value === void 0) return void 0;
|
|
192
|
+
return { key, value: { stringValue: value } };
|
|
193
|
+
}
|
|
194
|
+
function attrInt(key, value) {
|
|
195
|
+
if (value === void 0) return void 0;
|
|
196
|
+
if (!Number.isFinite(value)) return void 0;
|
|
197
|
+
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
198
|
+
}
|
|
199
|
+
function buildOtlpSpan(args) {
|
|
200
|
+
const attrs = args.attributes.filter((x) => x !== void 0);
|
|
201
|
+
const span = {
|
|
202
|
+
traceId: args.ids.traceIdB64,
|
|
203
|
+
spanId: args.ids.spanIdB64,
|
|
204
|
+
name: args.name,
|
|
205
|
+
startTimeUnixNano: args.startTimeUnixNano,
|
|
206
|
+
endTimeUnixNano: args.endTimeUnixNano
|
|
207
|
+
};
|
|
208
|
+
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
209
|
+
if (attrs.length) span.attributes = attrs;
|
|
210
|
+
if (args.status) span.status = args.status;
|
|
211
|
+
return span;
|
|
212
|
+
}
|
|
213
|
+
function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", serviceVersion = "0.0.0") {
|
|
214
|
+
return {
|
|
215
|
+
resourceSpans: [
|
|
216
|
+
{
|
|
217
|
+
resource: {
|
|
218
|
+
attributes: [{ key: "service.name", value: { stringValue: serviceName } }]
|
|
219
|
+
},
|
|
220
|
+
scopeSpans: [
|
|
221
|
+
{
|
|
222
|
+
scope: { name: serviceName, version: serviceVersion },
|
|
223
|
+
spans
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
220
230
|
function mergePatches(target, source) {
|
|
221
231
|
var _a, _b, _c, _d;
|
|
222
232
|
const out = { ...target, ...source };
|
|
@@ -234,34 +244,58 @@ var EventShipper = class {
|
|
|
234
244
|
this.sticky = /* @__PURE__ */ new Map();
|
|
235
245
|
this.timers = /* @__PURE__ */ new Map();
|
|
236
246
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
237
|
-
var _a, _b;
|
|
238
|
-
this.writeKey = opts.writeKey;
|
|
239
|
-
this.baseUrl = (
|
|
247
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
248
|
+
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
249
|
+
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
250
|
+
this.enabled = opts.enabled !== false;
|
|
240
251
|
this.debug = opts.debug;
|
|
241
|
-
this.partialFlushMs = (
|
|
252
|
+
this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
|
|
253
|
+
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
254
|
+
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
255
|
+
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
256
|
+
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
257
|
+
this.context = {
|
|
258
|
+
library: {
|
|
259
|
+
name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
|
|
260
|
+
version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
|
|
261
|
+
},
|
|
262
|
+
metadata: {
|
|
263
|
+
jsRuntime: isNode ? "node" : "web",
|
|
264
|
+
...isNode ? { nodeVersion: process.version } : {}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
isDebugEnabled() {
|
|
269
|
+
return this.debug;
|
|
270
|
+
}
|
|
271
|
+
authHeaders() {
|
|
272
|
+
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
242
273
|
}
|
|
243
274
|
async patch(eventId, patch) {
|
|
244
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
245
|
-
if (!
|
|
275
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
276
|
+
if (!this.enabled) return;
|
|
277
|
+
if (!eventId || !eventId.trim()) return;
|
|
246
278
|
if (this.debug) {
|
|
247
|
-
console.log(
|
|
279
|
+
console.log(`${this.prefix} queue patch`, {
|
|
248
280
|
eventId,
|
|
249
281
|
userId: patch.userId,
|
|
282
|
+
eventName: patch.eventName,
|
|
250
283
|
hasInput: typeof patch.input === "string" && patch.input.length > 0,
|
|
251
284
|
hasOutput: typeof patch.output === "string" && patch.output.length > 0,
|
|
285
|
+
attachments: (_b = (_a = patch.attachments) == null ? void 0 : _a.length) != null ? _b : 0,
|
|
252
286
|
isPending: patch.isPending
|
|
253
287
|
});
|
|
254
288
|
}
|
|
255
|
-
const sticky = (
|
|
256
|
-
const existing = (
|
|
289
|
+
const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
|
|
290
|
+
const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
|
|
257
291
|
const merged = mergePatches(existing, patch);
|
|
258
|
-
merged.isPending = (
|
|
292
|
+
merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
|
|
259
293
|
this.buffers.set(eventId, merged);
|
|
260
294
|
this.sticky.set(eventId, {
|
|
261
|
-
userId: (
|
|
262
|
-
convoId: (
|
|
263
|
-
eventName: (
|
|
264
|
-
isPending: (
|
|
295
|
+
userId: (_h = merged.userId) != null ? _h : sticky.userId,
|
|
296
|
+
convoId: (_i = merged.convoId) != null ? _i : sticky.convoId,
|
|
297
|
+
eventName: (_j = merged.eventName) != null ? _j : sticky.eventName,
|
|
298
|
+
isPending: (_k = merged.isPending) != null ? _k : sticky.isPending
|
|
265
299
|
});
|
|
266
300
|
const t = this.timers.get(eventId);
|
|
267
301
|
if (t) clearTimeout(t);
|
|
@@ -279,6 +313,7 @@ var EventShipper = class {
|
|
|
279
313
|
await this.patch(eventId, { ...patch, isPending: false });
|
|
280
314
|
}
|
|
281
315
|
async flush() {
|
|
316
|
+
if (!this.enabled) return;
|
|
282
317
|
const ids = [...this.buffers.keys()];
|
|
283
318
|
await Promise.all(ids.map((id) => this.flushOne(id)));
|
|
284
319
|
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
@@ -289,8 +324,70 @@ var EventShipper = class {
|
|
|
289
324
|
this.timers.clear();
|
|
290
325
|
await this.flush();
|
|
291
326
|
}
|
|
327
|
+
async trackSignal(signal) {
|
|
328
|
+
var _a, _b;
|
|
329
|
+
if (!this.enabled) return;
|
|
330
|
+
const body = [
|
|
331
|
+
{
|
|
332
|
+
event_id: signal.eventId,
|
|
333
|
+
signal_name: signal.name,
|
|
334
|
+
signal_type: (_a = signal.type) != null ? _a : "default",
|
|
335
|
+
timestamp: signal.timestamp,
|
|
336
|
+
sentiment: signal.sentiment,
|
|
337
|
+
attachment_id: signal.attachmentId,
|
|
338
|
+
properties: {
|
|
339
|
+
...(_b = signal.properties) != null ? _b : {},
|
|
340
|
+
...signal.comment ? { comment: signal.comment } : {},
|
|
341
|
+
...signal.after ? { after: signal.after } : {}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
const url = `${this.baseUrl}signals/track`;
|
|
346
|
+
try {
|
|
347
|
+
await postJson(url, body, this.authHeaders(), {
|
|
348
|
+
maxAttempts: 3,
|
|
349
|
+
debug: this.debug,
|
|
350
|
+
sdkName: this.sdkName
|
|
351
|
+
});
|
|
352
|
+
} catch (err) {
|
|
353
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
354
|
+
console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async identify(users) {
|
|
358
|
+
if (!this.enabled) return;
|
|
359
|
+
const list = Array.isArray(users) ? users : [users];
|
|
360
|
+
const body = list.filter((user) => {
|
|
361
|
+
if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
|
|
362
|
+
if (this.debug) {
|
|
363
|
+
console.warn(`${this.prefix} skipping identify: missing userId`);
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return true;
|
|
368
|
+
}).map((user) => {
|
|
369
|
+
var _a;
|
|
370
|
+
return {
|
|
371
|
+
user_id: user.userId,
|
|
372
|
+
traits: (_a = user.traits) != null ? _a : {}
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
if (body.length === 0) return;
|
|
376
|
+
const url = `${this.baseUrl}users/identify`;
|
|
377
|
+
try {
|
|
378
|
+
await postJson(url, body, this.authHeaders(), {
|
|
379
|
+
maxAttempts: 3,
|
|
380
|
+
debug: this.debug,
|
|
381
|
+
sdkName: this.sdkName
|
|
382
|
+
});
|
|
383
|
+
} catch (err) {
|
|
384
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
385
|
+
console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
292
388
|
async flushOne(eventId) {
|
|
293
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
389
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
390
|
+
if (!this.enabled) return;
|
|
294
391
|
const timer = this.timers.get(eventId);
|
|
295
392
|
if (timer) {
|
|
296
393
|
clearTimeout(timer);
|
|
@@ -300,105 +397,123 @@ var EventShipper = class {
|
|
|
300
397
|
this.buffers.delete(eventId);
|
|
301
398
|
if (!accumulated) return;
|
|
302
399
|
const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
|
|
303
|
-
const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c :
|
|
400
|
+
const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
|
|
304
401
|
const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
|
|
305
402
|
if (!userId) {
|
|
306
403
|
if (this.debug) {
|
|
307
|
-
console.warn(
|
|
308
|
-
`[raindrop-ai/opencode-plugin] skipping track_partial for ${eventId}: missing userId`
|
|
309
|
-
);
|
|
404
|
+
console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
|
|
310
405
|
}
|
|
406
|
+
this.sticky.delete(eventId);
|
|
311
407
|
return;
|
|
312
408
|
}
|
|
409
|
+
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
313
410
|
const payload = {
|
|
314
411
|
event_id: eventId,
|
|
315
412
|
user_id: userId,
|
|
316
413
|
event: eventName,
|
|
317
|
-
timestamp: (
|
|
414
|
+
timestamp: (_f = accumulated.timestamp) != null ? _f : (/* @__PURE__ */ new Date()).toISOString(),
|
|
318
415
|
ai_data: {
|
|
319
416
|
input: accumulated.input,
|
|
320
417
|
output: accumulated.output,
|
|
321
418
|
model: accumulated.model,
|
|
322
|
-
convo_id: (
|
|
419
|
+
convo_id: (_g = accumulated.convoId) != null ? _g : sticky.convoId
|
|
323
420
|
},
|
|
324
421
|
properties: {
|
|
325
|
-
...
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
metadata: { jsRuntime: "node" }
|
|
329
|
-
}
|
|
422
|
+
...restProperties,
|
|
423
|
+
...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
|
|
424
|
+
$context: this.context
|
|
330
425
|
},
|
|
331
426
|
attachments: accumulated.attachments,
|
|
332
427
|
is_pending: ((_i = (_h = accumulated.isPending) != null ? _h : sticky.isPending) != null ? _i : true) !== false
|
|
333
428
|
};
|
|
334
429
|
const url = `${this.baseUrl}events/track_partial`;
|
|
335
430
|
if (this.debug) {
|
|
336
|
-
console.log(
|
|
431
|
+
console.log(`${this.prefix} sending track_partial`, {
|
|
337
432
|
eventId,
|
|
338
433
|
eventName,
|
|
339
434
|
userId,
|
|
340
|
-
isPending: payload
|
|
435
|
+
isPending: payload.is_pending,
|
|
341
436
|
inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
|
|
342
437
|
outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
|
|
438
|
+
attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
|
|
439
|
+
attachmentKinds: (_m = (_l = accumulated.attachments) == null ? void 0 : _l.map((a) => ({
|
|
440
|
+
type: a.type,
|
|
441
|
+
role: a.role,
|
|
442
|
+
name: a.name,
|
|
443
|
+
valuePreview: a.value.slice(0, 60)
|
|
444
|
+
}))) != null ? _m : [],
|
|
343
445
|
endpoint: url
|
|
344
446
|
});
|
|
345
447
|
}
|
|
346
|
-
const p = postJson(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
);
|
|
448
|
+
const p = postJson(url, payload, this.authHeaders(), {
|
|
449
|
+
maxAttempts: 3,
|
|
450
|
+
debug: this.debug,
|
|
451
|
+
sdkName: this.sdkName
|
|
452
|
+
});
|
|
352
453
|
this.inFlight.add(p);
|
|
353
454
|
try {
|
|
354
455
|
try {
|
|
355
456
|
await p;
|
|
356
457
|
if (this.debug) {
|
|
357
|
-
console.log(
|
|
458
|
+
console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
|
|
358
459
|
}
|
|
359
460
|
} catch (err) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
console.warn(
|
|
363
|
-
`[raindrop-ai/opencode-plugin] failed to send track_partial (dropping): ${msg}`
|
|
364
|
-
);
|
|
365
|
-
}
|
|
461
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
462
|
+
console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
|
|
366
463
|
}
|
|
367
464
|
} finally {
|
|
368
465
|
this.inFlight.delete(p);
|
|
369
466
|
}
|
|
370
|
-
|
|
467
|
+
const isPending = payload.is_pending;
|
|
468
|
+
if (!isPending) {
|
|
469
|
+
this.sticky.delete(eventId);
|
|
470
|
+
}
|
|
371
471
|
}
|
|
372
472
|
};
|
|
373
473
|
var TraceShipper = class {
|
|
374
474
|
constructor(opts) {
|
|
375
475
|
this.queue = [];
|
|
376
476
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
377
|
-
var _a, _b;
|
|
378
|
-
this.writeKey = opts.writeKey;
|
|
379
|
-
this.baseUrl = (
|
|
477
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
478
|
+
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
479
|
+
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
480
|
+
this.enabled = opts.enabled !== false;
|
|
380
481
|
this.debug = opts.debug;
|
|
381
|
-
this.
|
|
382
|
-
this.
|
|
383
|
-
this.
|
|
482
|
+
this.debugSpans = opts.debugSpans === true;
|
|
483
|
+
this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
|
|
484
|
+
this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
|
|
485
|
+
this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
|
|
486
|
+
this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
|
|
487
|
+
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
488
|
+
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
489
|
+
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
490
|
+
}
|
|
491
|
+
isDebugEnabled() {
|
|
492
|
+
return this.debug;
|
|
493
|
+
}
|
|
494
|
+
authHeaders() {
|
|
495
|
+
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
384
496
|
}
|
|
385
497
|
startSpan(args) {
|
|
386
|
-
var _a;
|
|
498
|
+
var _a, _b;
|
|
387
499
|
const ids = createSpanIds(args.parent);
|
|
388
|
-
const started = nowUnixNanoString();
|
|
500
|
+
const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
389
501
|
const attrs = [
|
|
390
|
-
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
502
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
503
|
+
attrString("ai.operationId", args.operationId)
|
|
391
504
|
];
|
|
392
|
-
if ((
|
|
505
|
+
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
393
506
|
return { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
394
507
|
}
|
|
395
508
|
endSpan(span, extra) {
|
|
396
|
-
var _a;
|
|
509
|
+
var _a, _b;
|
|
397
510
|
if (span.endTimeUnixNano) return;
|
|
398
|
-
span.endTimeUnixNano = nowUnixNanoString();
|
|
399
|
-
if ((
|
|
400
|
-
|
|
401
|
-
|
|
511
|
+
span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
512
|
+
if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
|
|
513
|
+
span.attributes.push(...extra.attributes);
|
|
514
|
+
}
|
|
515
|
+
let status = extra == null ? void 0 : extra.status;
|
|
516
|
+
if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
|
|
402
517
|
const message = extra.error instanceof Error ? extra.error.message : String(extra.error);
|
|
403
518
|
status = { code: SpanStatusCode.ERROR, message };
|
|
404
519
|
}
|
|
@@ -412,10 +527,6 @@ var TraceShipper = class {
|
|
|
412
527
|
});
|
|
413
528
|
this.enqueue(otlp);
|
|
414
529
|
}
|
|
415
|
-
/**
|
|
416
|
-
* Create and enqueue a span with both start and end times (atomic insert).
|
|
417
|
-
* Used for tool spans and any span whose full duration is known at write time.
|
|
418
|
-
*/
|
|
419
530
|
createSpan(args) {
|
|
420
531
|
var _a;
|
|
421
532
|
const ids = createSpanIds(args.parent);
|
|
@@ -434,7 +545,18 @@ var TraceShipper = class {
|
|
|
434
545
|
this.enqueue(otlp);
|
|
435
546
|
}
|
|
436
547
|
enqueue(span) {
|
|
437
|
-
if (this.
|
|
548
|
+
if (!this.enabled) return;
|
|
549
|
+
if (this.debugSpans) {
|
|
550
|
+
const short = (s) => s ? s.slice(-8) : "none";
|
|
551
|
+
console.log(
|
|
552
|
+
`${this.prefix}[span] name=${span.name} trace=${short(span.traceId)} span=${short(span.spanId)} parent=${short(
|
|
553
|
+
span.parentSpanId
|
|
554
|
+
)}`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
558
|
+
this.queue.shift();
|
|
559
|
+
}
|
|
438
560
|
this.queue.push(span);
|
|
439
561
|
if (this.queue.length >= this.maxBatchSize) {
|
|
440
562
|
void this.flush().catch(() => {
|
|
@@ -450,42 +572,34 @@ var TraceShipper = class {
|
|
|
450
572
|
}
|
|
451
573
|
}
|
|
452
574
|
async flush() {
|
|
575
|
+
if (!this.enabled) return;
|
|
453
576
|
if (this.timer) {
|
|
454
577
|
clearTimeout(this.timer);
|
|
455
578
|
this.timer = void 0;
|
|
456
579
|
}
|
|
457
580
|
while (this.queue.length > 0) {
|
|
458
581
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
459
|
-
const body = buildExportTraceServiceRequest(batch);
|
|
582
|
+
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
460
583
|
const url = `${this.baseUrl}traces`;
|
|
461
|
-
const spanNames = batch.map((span) => span.name);
|
|
462
584
|
if (this.debug) {
|
|
463
|
-
console.log(
|
|
585
|
+
console.log(`${this.prefix} sending traces batch`, {
|
|
464
586
|
spans: batch.length,
|
|
465
|
-
endpoint: url
|
|
466
|
-
spanNames
|
|
587
|
+
endpoint: url
|
|
467
588
|
});
|
|
468
589
|
}
|
|
469
|
-
const p = postJson(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
);
|
|
590
|
+
const p = postJson(url, body, this.authHeaders(), {
|
|
591
|
+
maxAttempts: 3,
|
|
592
|
+
debug: this.debug,
|
|
593
|
+
sdkName: this.sdkName
|
|
594
|
+
});
|
|
475
595
|
this.inFlight.add(p);
|
|
476
596
|
try {
|
|
477
597
|
try {
|
|
478
598
|
await p;
|
|
479
|
-
if (this.debug) {
|
|
480
|
-
console.log(`[raindrop-ai/opencode-plugin] sent ${batch.length} spans`, {
|
|
481
|
-
spanNames
|
|
482
|
-
});
|
|
483
|
-
}
|
|
599
|
+
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
484
600
|
} catch (err) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
console.warn(`[raindrop-ai/opencode-plugin] failed to send spans (dropping): ${msg}`);
|
|
488
|
-
}
|
|
601
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
602
|
+
console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
|
|
489
603
|
}
|
|
490
604
|
} finally {
|
|
491
605
|
this.inFlight.delete(p);
|
|
@@ -503,6 +617,105 @@ var TraceShipper = class {
|
|
|
503
617
|
}
|
|
504
618
|
};
|
|
505
619
|
|
|
620
|
+
// package.json
|
|
621
|
+
var package_default = {
|
|
622
|
+
name: "@raindrop-ai/opencode-plugin",
|
|
623
|
+
version: "0.0.5",
|
|
624
|
+
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
625
|
+
type: "module",
|
|
626
|
+
main: "dist/index.cjs",
|
|
627
|
+
module: "dist/index.js",
|
|
628
|
+
types: "dist/index.d.ts",
|
|
629
|
+
exports: {
|
|
630
|
+
".": {
|
|
631
|
+
types: "./dist/index.d.ts",
|
|
632
|
+
import: "./dist/index.js",
|
|
633
|
+
require: "./dist/index.cjs"
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
sideEffects: false,
|
|
637
|
+
files: [
|
|
638
|
+
"dist/**"
|
|
639
|
+
],
|
|
640
|
+
scripts: {
|
|
641
|
+
build: "tsup",
|
|
642
|
+
dev: "tsup --watch",
|
|
643
|
+
clean: "rm -rf dist",
|
|
644
|
+
test: "cd tests && yarn test"
|
|
645
|
+
},
|
|
646
|
+
peerDependencies: {
|
|
647
|
+
"@opencode-ai/plugin": ">=1.2.0",
|
|
648
|
+
"@opencode-ai/sdk": ">=1.2.0"
|
|
649
|
+
},
|
|
650
|
+
peerDependenciesMeta: {
|
|
651
|
+
"@opencode-ai/sdk": {
|
|
652
|
+
optional: true
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
devDependencies: {
|
|
656
|
+
"@raindrop-ai/core": "*",
|
|
657
|
+
"@opencode-ai/plugin": "^1.2.0",
|
|
658
|
+
"@opencode-ai/sdk": "^1.2.0",
|
|
659
|
+
"@types/node": "^20.11.17",
|
|
660
|
+
tsup: "^8.4.0",
|
|
661
|
+
typescript: "^5.3.3"
|
|
662
|
+
},
|
|
663
|
+
tsup: {
|
|
664
|
+
entry: [
|
|
665
|
+
"src/index.ts"
|
|
666
|
+
],
|
|
667
|
+
format: [
|
|
668
|
+
"cjs",
|
|
669
|
+
"esm"
|
|
670
|
+
],
|
|
671
|
+
dts: {
|
|
672
|
+
resolve: true
|
|
673
|
+
},
|
|
674
|
+
clean: true,
|
|
675
|
+
noExternal: [
|
|
676
|
+
"@raindrop-ai/core"
|
|
677
|
+
]
|
|
678
|
+
},
|
|
679
|
+
publishConfig: {
|
|
680
|
+
access: "public"
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// src/shipper.ts
|
|
685
|
+
var EventShipper2 = class extends EventShipper {
|
|
686
|
+
constructor(opts) {
|
|
687
|
+
var _a, _b, _c, _d;
|
|
688
|
+
super({
|
|
689
|
+
...opts,
|
|
690
|
+
sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
|
|
691
|
+
libraryName: (_b = opts.libraryName) != null ? _b : package_default.name,
|
|
692
|
+
libraryVersion: (_c = opts.libraryVersion) != null ? _c : package_default.version,
|
|
693
|
+
defaultEventName: (_d = opts.defaultEventName) != null ? _d : "opencode_session"
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var TraceShipper2 = class extends TraceShipper {
|
|
698
|
+
constructor(opts) {
|
|
699
|
+
var _a, _b, _c;
|
|
700
|
+
super({
|
|
701
|
+
...opts,
|
|
702
|
+
sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
|
|
703
|
+
serviceName: (_b = opts.serviceName) != null ? _b : "raindrop.opencode-plugin",
|
|
704
|
+
serviceVersion: (_c = opts.serviceVersion) != null ? _c : package_default.version
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
enqueue(span) {
|
|
708
|
+
var _a;
|
|
709
|
+
const attrs = (_a = span.attributes) != null ? _a : [];
|
|
710
|
+
attrs.unshift(
|
|
711
|
+
{ key: "span.id", value: { stringValue: span.spanId } },
|
|
712
|
+
...span.parentSpanId ? [{ key: "span.parent.id", value: { stringValue: span.parentSpanId } }] : []
|
|
713
|
+
);
|
|
714
|
+
span.attributes = attrs;
|
|
715
|
+
super.enqueue(span);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
506
719
|
// src/tracing-linkage-helpers.ts
|
|
507
720
|
function createSessionParentMapHelpers({
|
|
508
721
|
sessions: sessions2,
|
|
@@ -614,8 +827,8 @@ function getHostname() {
|
|
|
614
827
|
}
|
|
615
828
|
function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
616
829
|
function log(msg, data) {
|
|
617
|
-
|
|
618
|
-
const prefix = `[raindrop-ai/opencode-plugin] [
|
|
830
|
+
if (!config.debug) return;
|
|
831
|
+
const prefix = `[raindrop-ai/opencode-plugin] [info] ${msg}`;
|
|
619
832
|
if (data !== void 0) {
|
|
620
833
|
console.log(prefix, data);
|
|
621
834
|
return;
|
|
@@ -786,7 +999,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
786
999
|
attributes: llmAttrs
|
|
787
1000
|
});
|
|
788
1001
|
if (typeof timeObj["created"] === "number") {
|
|
789
|
-
llmSpan.startTimeUnixNano = String(timeObj["created"]
|
|
1002
|
+
llmSpan.startTimeUnixNano = String(Math.floor(timeObj["created"])) + "000000";
|
|
790
1003
|
}
|
|
791
1004
|
traceShipper.endSpan(llmSpan, { error: errorForSpan });
|
|
792
1005
|
await traceShipper.flush();
|
|
@@ -823,7 +1036,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
823
1036
|
attributes: llmAttrs
|
|
824
1037
|
});
|
|
825
1038
|
if (typeof timeObj["created"] === "number") {
|
|
826
|
-
finalLlmSpan.startTimeUnixNano = String(timeObj["created"]
|
|
1039
|
+
finalLlmSpan.startTimeUnixNano = String(Math.floor(timeObj["created"])) + "000000";
|
|
827
1040
|
}
|
|
828
1041
|
traceShipper.endSpan(finalLlmSpan, { error: errorForSpan });
|
|
829
1042
|
traceShipper.endSpan(rootSpan, {
|
|
@@ -883,7 +1096,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
883
1096
|
if (state.currentEventId) {
|
|
884
1097
|
state.currentEventId = void 0;
|
|
885
1098
|
}
|
|
886
|
-
await traceShipper.flush();
|
|
1099
|
+
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
887
1100
|
if (state.parentId) {
|
|
888
1101
|
cleanupSessionParentMap(String(sessionID));
|
|
889
1102
|
sessions.delete(String(sessionID));
|
|
@@ -1080,6 +1293,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1080
1293
|
});
|
|
1081
1294
|
}
|
|
1082
1295
|
} catch (err) {
|
|
1296
|
+
console.log(
|
|
1297
|
+
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.before hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1298
|
+
);
|
|
1083
1299
|
}
|
|
1084
1300
|
},
|
|
1085
1301
|
// ------------------------------------------------------------------
|
|
@@ -1151,6 +1367,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1151
1367
|
});
|
|
1152
1368
|
}
|
|
1153
1369
|
} catch (err) {
|
|
1370
|
+
console.log(
|
|
1371
|
+
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1372
|
+
);
|
|
1154
1373
|
}
|
|
1155
1374
|
}
|
|
1156
1375
|
};
|
|
@@ -1173,12 +1392,12 @@ async function plugin(input) {
|
|
|
1173
1392
|
if (config.debug) {
|
|
1174
1393
|
appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
|
|
1175
1394
|
}
|
|
1176
|
-
const eventShipper = new
|
|
1395
|
+
const eventShipper = new EventShipper2({
|
|
1177
1396
|
writeKey: config.writeKey,
|
|
1178
1397
|
endpoint: config.endpoint,
|
|
1179
1398
|
debug: config.debug
|
|
1180
1399
|
});
|
|
1181
|
-
const traceShipper = new
|
|
1400
|
+
const traceShipper = new TraceShipper2({
|
|
1182
1401
|
writeKey: config.writeKey,
|
|
1183
1402
|
endpoint: config.endpoint,
|
|
1184
1403
|
debug: config.debug
|