@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.cjs
CHANGED
|
@@ -49,30 +49,34 @@ function loadConfig(projectDirectory) {
|
|
|
49
49
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
|
|
50
50
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
51
51
|
eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
|
|
52
|
-
userId: merged.user_id,
|
|
53
52
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
|
|
54
53
|
};
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
//
|
|
58
|
-
function
|
|
59
|
-
|
|
56
|
+
// ../core/dist/chunk-FTBZHS25.js
|
|
57
|
+
function getCrypto() {
|
|
58
|
+
const c = globalThis.crypto;
|
|
59
|
+
return c;
|
|
60
60
|
}
|
|
61
61
|
function randomBytes(length) {
|
|
62
|
-
const
|
|
62
|
+
const cryptoObj = getCrypto();
|
|
63
63
|
const out = new Uint8Array(length);
|
|
64
|
-
if (
|
|
65
|
-
|
|
64
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
65
|
+
cryptoObj.getRandomValues(out);
|
|
66
66
|
return out;
|
|
67
67
|
}
|
|
68
68
|
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
69
69
|
return out;
|
|
70
70
|
}
|
|
71
71
|
function base64Encode(bytes) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
72
|
+
const maybeBuffer = globalThis.Buffer;
|
|
73
|
+
if (maybeBuffer) {
|
|
74
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
75
|
+
}
|
|
74
76
|
let binary = "";
|
|
75
|
-
for (let i2 = 0; i2 < bytes.length; i2++)
|
|
77
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
78
|
+
binary += String.fromCharCode(bytes[i2]);
|
|
79
|
+
}
|
|
76
80
|
const btoaFn = globalThis.btoa;
|
|
77
81
|
if (typeof btoaFn === "function") return btoaFn(binary);
|
|
78
82
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -93,70 +97,9 @@ function base64Encode(bytes) {
|
|
|
93
97
|
}
|
|
94
98
|
return out;
|
|
95
99
|
}
|
|
96
|
-
var SERVICE_NAME = "raindrop.opencode-plugin";
|
|
97
|
-
var PLUGIN_VERSION = "0.1.0";
|
|
98
|
-
var SpanStatusCode = {
|
|
99
|
-
ERROR: 2
|
|
100
|
-
};
|
|
101
100
|
function generateId() {
|
|
102
101
|
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
103
102
|
}
|
|
104
|
-
function createSpanIds(parent) {
|
|
105
|
-
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
106
|
-
const spanId = base64Encode(randomBytes(8));
|
|
107
|
-
return {
|
|
108
|
-
traceIdB64: traceId,
|
|
109
|
-
spanIdB64: spanId,
|
|
110
|
-
parentSpanIdB64: parent ? parent.spanIdB64 : void 0
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
function nowUnixNanoString() {
|
|
114
|
-
return String(Date.now() * 1e6);
|
|
115
|
-
}
|
|
116
|
-
function attrString(key, value) {
|
|
117
|
-
if (value === void 0) return void 0;
|
|
118
|
-
return { key, value: { stringValue: value } };
|
|
119
|
-
}
|
|
120
|
-
function attrInt(key, value) {
|
|
121
|
-
if (value === void 0) return void 0;
|
|
122
|
-
if (!Number.isFinite(value)) return void 0;
|
|
123
|
-
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
124
|
-
}
|
|
125
|
-
function buildOtlpSpan(args) {
|
|
126
|
-
const attrs = [
|
|
127
|
-
attrString("span.id", args.ids.spanIdB64),
|
|
128
|
-
attrString("span.parent.id", args.ids.parentSpanIdB64),
|
|
129
|
-
...args.attributes
|
|
130
|
-
].filter((x) => x !== void 0);
|
|
131
|
-
const span = {
|
|
132
|
-
traceId: args.ids.traceIdB64,
|
|
133
|
-
spanId: args.ids.spanIdB64,
|
|
134
|
-
name: args.name,
|
|
135
|
-
startTimeUnixNano: args.startTimeUnixNano,
|
|
136
|
-
endTimeUnixNano: args.endTimeUnixNano
|
|
137
|
-
};
|
|
138
|
-
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
139
|
-
if (attrs.length) span.attributes = attrs;
|
|
140
|
-
if (args.status) span.status = args.status;
|
|
141
|
-
return span;
|
|
142
|
-
}
|
|
143
|
-
function buildExportTraceServiceRequest(spans) {
|
|
144
|
-
return {
|
|
145
|
-
resourceSpans: [
|
|
146
|
-
{
|
|
147
|
-
resource: {
|
|
148
|
-
attributes: [{ key: "service.name", value: { stringValue: SERVICE_NAME } }]
|
|
149
|
-
},
|
|
150
|
-
scopeSpans: [
|
|
151
|
-
{
|
|
152
|
-
scope: { name: SERVICE_NAME, version: PLUGIN_VERSION },
|
|
153
|
-
spans
|
|
154
|
-
}
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
]
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
103
|
function wait(ms) {
|
|
161
104
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
162
105
|
}
|
|
@@ -169,7 +112,7 @@ function parseRetryAfter(headers) {
|
|
|
169
112
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
170
113
|
if (!value) return void 0;
|
|
171
114
|
const asNumber = Number(value);
|
|
172
|
-
if (!Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
115
|
+
if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
173
116
|
const asDate = new Date(value).getTime();
|
|
174
117
|
if (!Number.isNaN(asDate)) {
|
|
175
118
|
const delta = asDate - Date.now();
|
|
@@ -178,26 +121,29 @@ function parseRetryAfter(headers) {
|
|
|
178
121
|
return void 0;
|
|
179
122
|
}
|
|
180
123
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
181
|
-
if (previousError
|
|
124
|
+
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
182
125
|
const v = previousError.retryAfterMs;
|
|
183
126
|
if (typeof v === "number") return Math.max(0, v);
|
|
184
127
|
}
|
|
185
128
|
if (attemptNumber <= 1) return 0;
|
|
186
|
-
|
|
129
|
+
const base = 500;
|
|
130
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
131
|
+
return base * factor;
|
|
187
132
|
}
|
|
188
133
|
async function withRetry(operation, opName, opts) {
|
|
134
|
+
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
189
135
|
let lastError = void 0;
|
|
190
|
-
for (let
|
|
191
|
-
if (
|
|
192
|
-
const delay = getRetryDelayMs(
|
|
136
|
+
for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
|
|
137
|
+
if (attemptNumber > 1) {
|
|
138
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
193
139
|
if (opts.debug) {
|
|
194
140
|
console.warn(
|
|
195
|
-
|
|
141
|
+
`${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
|
|
196
142
|
);
|
|
197
143
|
}
|
|
198
144
|
if (delay > 0) await wait(delay);
|
|
199
145
|
} else if (opts.debug) {
|
|
200
|
-
console.log(
|
|
146
|
+
console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
|
|
201
147
|
}
|
|
202
148
|
try {
|
|
203
149
|
return await operation();
|
|
@@ -206,20 +152,26 @@ async function withRetry(operation, opName, opts) {
|
|
|
206
152
|
if (opts.debug) {
|
|
207
153
|
const msg = err instanceof Error ? err.message : String(err);
|
|
208
154
|
console.warn(
|
|
209
|
-
|
|
155
|
+
`${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
|
|
210
156
|
);
|
|
211
157
|
}
|
|
212
|
-
if (
|
|
158
|
+
if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
|
|
159
|
+
break;
|
|
160
|
+
if (attemptNumber === opts.maxAttempts) break;
|
|
213
161
|
}
|
|
214
162
|
}
|
|
215
163
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
216
164
|
}
|
|
217
165
|
async function postJson(url, body, headers, opts) {
|
|
166
|
+
const opName = `POST ${url}`;
|
|
218
167
|
await withRetry(
|
|
219
168
|
async () => {
|
|
220
169
|
const resp = await fetch(url, {
|
|
221
170
|
method: "POST",
|
|
222
|
-
headers: {
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
...headers
|
|
174
|
+
},
|
|
223
175
|
body: JSON.stringify(body)
|
|
224
176
|
});
|
|
225
177
|
if (!resp.ok) {
|
|
@@ -229,13 +181,71 @@ async function postJson(url, body, headers, opts) {
|
|
|
229
181
|
);
|
|
230
182
|
const retryAfterMs = parseRetryAfter(resp.headers);
|
|
231
183
|
if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
|
|
184
|
+
err.retryable = resp.status === 429 || resp.status >= 500;
|
|
232
185
|
throw err;
|
|
233
186
|
}
|
|
234
187
|
},
|
|
235
|
-
|
|
188
|
+
opName,
|
|
236
189
|
opts
|
|
237
190
|
);
|
|
238
191
|
}
|
|
192
|
+
var SpanStatusCode = {
|
|
193
|
+
UNSET: 0,
|
|
194
|
+
OK: 1,
|
|
195
|
+
ERROR: 2
|
|
196
|
+
};
|
|
197
|
+
function createSpanIds(parent) {
|
|
198
|
+
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
199
|
+
const spanId = base64Encode(randomBytes(8));
|
|
200
|
+
return {
|
|
201
|
+
traceIdB64: traceId,
|
|
202
|
+
spanIdB64: spanId,
|
|
203
|
+
parentSpanIdB64: parent ? parent.spanIdB64 : void 0
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function nowUnixNanoString() {
|
|
207
|
+
return Date.now().toString() + "000000";
|
|
208
|
+
}
|
|
209
|
+
function attrString(key, value) {
|
|
210
|
+
if (value === void 0) return void 0;
|
|
211
|
+
return { key, value: { stringValue: value } };
|
|
212
|
+
}
|
|
213
|
+
function attrInt(key, value) {
|
|
214
|
+
if (value === void 0) return void 0;
|
|
215
|
+
if (!Number.isFinite(value)) return void 0;
|
|
216
|
+
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
217
|
+
}
|
|
218
|
+
function buildOtlpSpan(args) {
|
|
219
|
+
const attrs = args.attributes.filter((x) => x !== void 0);
|
|
220
|
+
const span = {
|
|
221
|
+
traceId: args.ids.traceIdB64,
|
|
222
|
+
spanId: args.ids.spanIdB64,
|
|
223
|
+
name: args.name,
|
|
224
|
+
startTimeUnixNano: args.startTimeUnixNano,
|
|
225
|
+
endTimeUnixNano: args.endTimeUnixNano
|
|
226
|
+
};
|
|
227
|
+
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
228
|
+
if (attrs.length) span.attributes = attrs;
|
|
229
|
+
if (args.status) span.status = args.status;
|
|
230
|
+
return span;
|
|
231
|
+
}
|
|
232
|
+
function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", serviceVersion = "0.0.0") {
|
|
233
|
+
return {
|
|
234
|
+
resourceSpans: [
|
|
235
|
+
{
|
|
236
|
+
resource: {
|
|
237
|
+
attributes: [{ key: "service.name", value: { stringValue: serviceName } }]
|
|
238
|
+
},
|
|
239
|
+
scopeSpans: [
|
|
240
|
+
{
|
|
241
|
+
scope: { name: serviceName, version: serviceVersion },
|
|
242
|
+
spans
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
239
249
|
function mergePatches(target, source) {
|
|
240
250
|
var _a, _b, _c, _d;
|
|
241
251
|
const out = { ...target, ...source };
|
|
@@ -253,34 +263,58 @@ var EventShipper = class {
|
|
|
253
263
|
this.sticky = /* @__PURE__ */ new Map();
|
|
254
264
|
this.timers = /* @__PURE__ */ new Map();
|
|
255
265
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
256
|
-
var _a, _b;
|
|
257
|
-
this.writeKey = opts.writeKey;
|
|
258
|
-
this.baseUrl = (
|
|
266
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
267
|
+
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
268
|
+
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
269
|
+
this.enabled = opts.enabled !== false;
|
|
259
270
|
this.debug = opts.debug;
|
|
260
|
-
this.partialFlushMs = (
|
|
271
|
+
this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
|
|
272
|
+
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
273
|
+
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
274
|
+
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
275
|
+
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
276
|
+
this.context = {
|
|
277
|
+
library: {
|
|
278
|
+
name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
|
|
279
|
+
version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
|
|
280
|
+
},
|
|
281
|
+
metadata: {
|
|
282
|
+
jsRuntime: isNode ? "node" : "web",
|
|
283
|
+
...isNode ? { nodeVersion: process.version } : {}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
isDebugEnabled() {
|
|
288
|
+
return this.debug;
|
|
289
|
+
}
|
|
290
|
+
authHeaders() {
|
|
291
|
+
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
261
292
|
}
|
|
262
293
|
async patch(eventId, patch) {
|
|
263
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
264
|
-
if (!
|
|
294
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
295
|
+
if (!this.enabled) return;
|
|
296
|
+
if (!eventId || !eventId.trim()) return;
|
|
265
297
|
if (this.debug) {
|
|
266
|
-
console.log(
|
|
298
|
+
console.log(`${this.prefix} queue patch`, {
|
|
267
299
|
eventId,
|
|
268
300
|
userId: patch.userId,
|
|
301
|
+
eventName: patch.eventName,
|
|
269
302
|
hasInput: typeof patch.input === "string" && patch.input.length > 0,
|
|
270
303
|
hasOutput: typeof patch.output === "string" && patch.output.length > 0,
|
|
304
|
+
attachments: (_b = (_a = patch.attachments) == null ? void 0 : _a.length) != null ? _b : 0,
|
|
271
305
|
isPending: patch.isPending
|
|
272
306
|
});
|
|
273
307
|
}
|
|
274
|
-
const sticky = (
|
|
275
|
-
const existing = (
|
|
308
|
+
const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
|
|
309
|
+
const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
|
|
276
310
|
const merged = mergePatches(existing, patch);
|
|
277
|
-
merged.isPending = (
|
|
311
|
+
merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
|
|
278
312
|
this.buffers.set(eventId, merged);
|
|
279
313
|
this.sticky.set(eventId, {
|
|
280
|
-
userId: (
|
|
281
|
-
convoId: (
|
|
282
|
-
eventName: (
|
|
283
|
-
isPending: (
|
|
314
|
+
userId: (_h = merged.userId) != null ? _h : sticky.userId,
|
|
315
|
+
convoId: (_i = merged.convoId) != null ? _i : sticky.convoId,
|
|
316
|
+
eventName: (_j = merged.eventName) != null ? _j : sticky.eventName,
|
|
317
|
+
isPending: (_k = merged.isPending) != null ? _k : sticky.isPending
|
|
284
318
|
});
|
|
285
319
|
const t = this.timers.get(eventId);
|
|
286
320
|
if (t) clearTimeout(t);
|
|
@@ -298,6 +332,7 @@ var EventShipper = class {
|
|
|
298
332
|
await this.patch(eventId, { ...patch, isPending: false });
|
|
299
333
|
}
|
|
300
334
|
async flush() {
|
|
335
|
+
if (!this.enabled) return;
|
|
301
336
|
const ids = [...this.buffers.keys()];
|
|
302
337
|
await Promise.all(ids.map((id) => this.flushOne(id)));
|
|
303
338
|
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
@@ -308,8 +343,70 @@ var EventShipper = class {
|
|
|
308
343
|
this.timers.clear();
|
|
309
344
|
await this.flush();
|
|
310
345
|
}
|
|
346
|
+
async trackSignal(signal) {
|
|
347
|
+
var _a, _b;
|
|
348
|
+
if (!this.enabled) return;
|
|
349
|
+
const body = [
|
|
350
|
+
{
|
|
351
|
+
event_id: signal.eventId,
|
|
352
|
+
signal_name: signal.name,
|
|
353
|
+
signal_type: (_a = signal.type) != null ? _a : "default",
|
|
354
|
+
timestamp: signal.timestamp,
|
|
355
|
+
sentiment: signal.sentiment,
|
|
356
|
+
attachment_id: signal.attachmentId,
|
|
357
|
+
properties: {
|
|
358
|
+
...(_b = signal.properties) != null ? _b : {},
|
|
359
|
+
...signal.comment ? { comment: signal.comment } : {},
|
|
360
|
+
...signal.after ? { after: signal.after } : {}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
];
|
|
364
|
+
const url = `${this.baseUrl}signals/track`;
|
|
365
|
+
try {
|
|
366
|
+
await postJson(url, body, this.authHeaders(), {
|
|
367
|
+
maxAttempts: 3,
|
|
368
|
+
debug: this.debug,
|
|
369
|
+
sdkName: this.sdkName
|
|
370
|
+
});
|
|
371
|
+
} catch (err) {
|
|
372
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
373
|
+
console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async identify(users) {
|
|
377
|
+
if (!this.enabled) return;
|
|
378
|
+
const list = Array.isArray(users) ? users : [users];
|
|
379
|
+
const body = list.filter((user) => {
|
|
380
|
+
if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
|
|
381
|
+
if (this.debug) {
|
|
382
|
+
console.warn(`${this.prefix} skipping identify: missing userId`);
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
}).map((user) => {
|
|
388
|
+
var _a;
|
|
389
|
+
return {
|
|
390
|
+
user_id: user.userId,
|
|
391
|
+
traits: (_a = user.traits) != null ? _a : {}
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
if (body.length === 0) return;
|
|
395
|
+
const url = `${this.baseUrl}users/identify`;
|
|
396
|
+
try {
|
|
397
|
+
await postJson(url, body, this.authHeaders(), {
|
|
398
|
+
maxAttempts: 3,
|
|
399
|
+
debug: this.debug,
|
|
400
|
+
sdkName: this.sdkName
|
|
401
|
+
});
|
|
402
|
+
} catch (err) {
|
|
403
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
404
|
+
console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
311
407
|
async flushOne(eventId) {
|
|
312
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
408
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
409
|
+
if (!this.enabled) return;
|
|
313
410
|
const timer = this.timers.get(eventId);
|
|
314
411
|
if (timer) {
|
|
315
412
|
clearTimeout(timer);
|
|
@@ -319,105 +416,123 @@ var EventShipper = class {
|
|
|
319
416
|
this.buffers.delete(eventId);
|
|
320
417
|
if (!accumulated) return;
|
|
321
418
|
const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
|
|
322
|
-
const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c :
|
|
419
|
+
const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
|
|
323
420
|
const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
|
|
324
421
|
if (!userId) {
|
|
325
422
|
if (this.debug) {
|
|
326
|
-
console.warn(
|
|
327
|
-
`[raindrop-ai/opencode-plugin] skipping track_partial for ${eventId}: missing userId`
|
|
328
|
-
);
|
|
423
|
+
console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
|
|
329
424
|
}
|
|
425
|
+
this.sticky.delete(eventId);
|
|
330
426
|
return;
|
|
331
427
|
}
|
|
428
|
+
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
332
429
|
const payload = {
|
|
333
430
|
event_id: eventId,
|
|
334
431
|
user_id: userId,
|
|
335
432
|
event: eventName,
|
|
336
|
-
timestamp: (
|
|
433
|
+
timestamp: (_f = accumulated.timestamp) != null ? _f : (/* @__PURE__ */ new Date()).toISOString(),
|
|
337
434
|
ai_data: {
|
|
338
435
|
input: accumulated.input,
|
|
339
436
|
output: accumulated.output,
|
|
340
437
|
model: accumulated.model,
|
|
341
|
-
convo_id: (
|
|
438
|
+
convo_id: (_g = accumulated.convoId) != null ? _g : sticky.convoId
|
|
342
439
|
},
|
|
343
440
|
properties: {
|
|
344
|
-
...
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
metadata: { jsRuntime: "node" }
|
|
348
|
-
}
|
|
441
|
+
...restProperties,
|
|
442
|
+
...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
|
|
443
|
+
$context: this.context
|
|
349
444
|
},
|
|
350
445
|
attachments: accumulated.attachments,
|
|
351
446
|
is_pending: ((_i = (_h = accumulated.isPending) != null ? _h : sticky.isPending) != null ? _i : true) !== false
|
|
352
447
|
};
|
|
353
448
|
const url = `${this.baseUrl}events/track_partial`;
|
|
354
449
|
if (this.debug) {
|
|
355
|
-
console.log(
|
|
450
|
+
console.log(`${this.prefix} sending track_partial`, {
|
|
356
451
|
eventId,
|
|
357
452
|
eventName,
|
|
358
453
|
userId,
|
|
359
|
-
isPending: payload
|
|
454
|
+
isPending: payload.is_pending,
|
|
360
455
|
inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
|
|
361
456
|
outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
|
|
457
|
+
attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
|
|
458
|
+
attachmentKinds: (_m = (_l = accumulated.attachments) == null ? void 0 : _l.map((a) => ({
|
|
459
|
+
type: a.type,
|
|
460
|
+
role: a.role,
|
|
461
|
+
name: a.name,
|
|
462
|
+
valuePreview: a.value.slice(0, 60)
|
|
463
|
+
}))) != null ? _m : [],
|
|
362
464
|
endpoint: url
|
|
363
465
|
});
|
|
364
466
|
}
|
|
365
|
-
const p = postJson(
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
);
|
|
467
|
+
const p = postJson(url, payload, this.authHeaders(), {
|
|
468
|
+
maxAttempts: 3,
|
|
469
|
+
debug: this.debug,
|
|
470
|
+
sdkName: this.sdkName
|
|
471
|
+
});
|
|
371
472
|
this.inFlight.add(p);
|
|
372
473
|
try {
|
|
373
474
|
try {
|
|
374
475
|
await p;
|
|
375
476
|
if (this.debug) {
|
|
376
|
-
console.log(
|
|
477
|
+
console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
|
|
377
478
|
}
|
|
378
479
|
} catch (err) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.warn(
|
|
382
|
-
`[raindrop-ai/opencode-plugin] failed to send track_partial (dropping): ${msg}`
|
|
383
|
-
);
|
|
384
|
-
}
|
|
480
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
481
|
+
console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
|
|
385
482
|
}
|
|
386
483
|
} finally {
|
|
387
484
|
this.inFlight.delete(p);
|
|
388
485
|
}
|
|
389
|
-
|
|
486
|
+
const isPending = payload.is_pending;
|
|
487
|
+
if (!isPending) {
|
|
488
|
+
this.sticky.delete(eventId);
|
|
489
|
+
}
|
|
390
490
|
}
|
|
391
491
|
};
|
|
392
492
|
var TraceShipper = class {
|
|
393
493
|
constructor(opts) {
|
|
394
494
|
this.queue = [];
|
|
395
495
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
396
|
-
var _a, _b;
|
|
397
|
-
this.writeKey = opts.writeKey;
|
|
398
|
-
this.baseUrl = (
|
|
496
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
497
|
+
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
498
|
+
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
499
|
+
this.enabled = opts.enabled !== false;
|
|
399
500
|
this.debug = opts.debug;
|
|
400
|
-
this.
|
|
401
|
-
this.
|
|
402
|
-
this.
|
|
501
|
+
this.debugSpans = opts.debugSpans === true;
|
|
502
|
+
this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
|
|
503
|
+
this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
|
|
504
|
+
this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
|
|
505
|
+
this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
|
|
506
|
+
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
507
|
+
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
508
|
+
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
509
|
+
}
|
|
510
|
+
isDebugEnabled() {
|
|
511
|
+
return this.debug;
|
|
512
|
+
}
|
|
513
|
+
authHeaders() {
|
|
514
|
+
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
403
515
|
}
|
|
404
516
|
startSpan(args) {
|
|
405
|
-
var _a;
|
|
517
|
+
var _a, _b;
|
|
406
518
|
const ids = createSpanIds(args.parent);
|
|
407
|
-
const started = nowUnixNanoString();
|
|
519
|
+
const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
408
520
|
const attrs = [
|
|
409
|
-
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
521
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
522
|
+
attrString("ai.operationId", args.operationId)
|
|
410
523
|
];
|
|
411
|
-
if ((
|
|
524
|
+
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
412
525
|
return { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
413
526
|
}
|
|
414
527
|
endSpan(span, extra) {
|
|
415
|
-
var _a;
|
|
528
|
+
var _a, _b;
|
|
416
529
|
if (span.endTimeUnixNano) return;
|
|
417
|
-
span.endTimeUnixNano = nowUnixNanoString();
|
|
418
|
-
if ((
|
|
419
|
-
|
|
420
|
-
|
|
530
|
+
span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
531
|
+
if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
|
|
532
|
+
span.attributes.push(...extra.attributes);
|
|
533
|
+
}
|
|
534
|
+
let status = extra == null ? void 0 : extra.status;
|
|
535
|
+
if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
|
|
421
536
|
const message = extra.error instanceof Error ? extra.error.message : String(extra.error);
|
|
422
537
|
status = { code: SpanStatusCode.ERROR, message };
|
|
423
538
|
}
|
|
@@ -431,10 +546,6 @@ var TraceShipper = class {
|
|
|
431
546
|
});
|
|
432
547
|
this.enqueue(otlp);
|
|
433
548
|
}
|
|
434
|
-
/**
|
|
435
|
-
* Create and enqueue a span with both start and end times (atomic insert).
|
|
436
|
-
* Used for tool spans and any span whose full duration is known at write time.
|
|
437
|
-
*/
|
|
438
549
|
createSpan(args) {
|
|
439
550
|
var _a;
|
|
440
551
|
const ids = createSpanIds(args.parent);
|
|
@@ -453,7 +564,18 @@ var TraceShipper = class {
|
|
|
453
564
|
this.enqueue(otlp);
|
|
454
565
|
}
|
|
455
566
|
enqueue(span) {
|
|
456
|
-
if (this.
|
|
567
|
+
if (!this.enabled) return;
|
|
568
|
+
if (this.debugSpans) {
|
|
569
|
+
const short = (s) => s ? s.slice(-8) : "none";
|
|
570
|
+
console.log(
|
|
571
|
+
`${this.prefix}[span] name=${span.name} trace=${short(span.traceId)} span=${short(span.spanId)} parent=${short(
|
|
572
|
+
span.parentSpanId
|
|
573
|
+
)}`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
577
|
+
this.queue.shift();
|
|
578
|
+
}
|
|
457
579
|
this.queue.push(span);
|
|
458
580
|
if (this.queue.length >= this.maxBatchSize) {
|
|
459
581
|
void this.flush().catch(() => {
|
|
@@ -469,42 +591,34 @@ var TraceShipper = class {
|
|
|
469
591
|
}
|
|
470
592
|
}
|
|
471
593
|
async flush() {
|
|
594
|
+
if (!this.enabled) return;
|
|
472
595
|
if (this.timer) {
|
|
473
596
|
clearTimeout(this.timer);
|
|
474
597
|
this.timer = void 0;
|
|
475
598
|
}
|
|
476
599
|
while (this.queue.length > 0) {
|
|
477
600
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
478
|
-
const body = buildExportTraceServiceRequest(batch);
|
|
601
|
+
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
479
602
|
const url = `${this.baseUrl}traces`;
|
|
480
|
-
const spanNames = batch.map((span) => span.name);
|
|
481
603
|
if (this.debug) {
|
|
482
|
-
console.log(
|
|
604
|
+
console.log(`${this.prefix} sending traces batch`, {
|
|
483
605
|
spans: batch.length,
|
|
484
|
-
endpoint: url
|
|
485
|
-
spanNames
|
|
606
|
+
endpoint: url
|
|
486
607
|
});
|
|
487
608
|
}
|
|
488
|
-
const p = postJson(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
);
|
|
609
|
+
const p = postJson(url, body, this.authHeaders(), {
|
|
610
|
+
maxAttempts: 3,
|
|
611
|
+
debug: this.debug,
|
|
612
|
+
sdkName: this.sdkName
|
|
613
|
+
});
|
|
494
614
|
this.inFlight.add(p);
|
|
495
615
|
try {
|
|
496
616
|
try {
|
|
497
617
|
await p;
|
|
498
|
-
if (this.debug) {
|
|
499
|
-
console.log(`[raindrop-ai/opencode-plugin] sent ${batch.length} spans`, {
|
|
500
|
-
spanNames
|
|
501
|
-
});
|
|
502
|
-
}
|
|
618
|
+
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
503
619
|
} catch (err) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
console.warn(`[raindrop-ai/opencode-plugin] failed to send spans (dropping): ${msg}`);
|
|
507
|
-
}
|
|
620
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
621
|
+
console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
|
|
508
622
|
}
|
|
509
623
|
} finally {
|
|
510
624
|
this.inFlight.delete(p);
|
|
@@ -522,6 +636,105 @@ var TraceShipper = class {
|
|
|
522
636
|
}
|
|
523
637
|
};
|
|
524
638
|
|
|
639
|
+
// package.json
|
|
640
|
+
var package_default = {
|
|
641
|
+
name: "@raindrop-ai/opencode-plugin",
|
|
642
|
+
version: "0.0.5",
|
|
643
|
+
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
644
|
+
type: "module",
|
|
645
|
+
main: "dist/index.cjs",
|
|
646
|
+
module: "dist/index.js",
|
|
647
|
+
types: "dist/index.d.ts",
|
|
648
|
+
exports: {
|
|
649
|
+
".": {
|
|
650
|
+
types: "./dist/index.d.ts",
|
|
651
|
+
import: "./dist/index.js",
|
|
652
|
+
require: "./dist/index.cjs"
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
sideEffects: false,
|
|
656
|
+
files: [
|
|
657
|
+
"dist/**"
|
|
658
|
+
],
|
|
659
|
+
scripts: {
|
|
660
|
+
build: "tsup",
|
|
661
|
+
dev: "tsup --watch",
|
|
662
|
+
clean: "rm -rf dist",
|
|
663
|
+
test: "cd tests && yarn test"
|
|
664
|
+
},
|
|
665
|
+
peerDependencies: {
|
|
666
|
+
"@opencode-ai/plugin": ">=1.2.0",
|
|
667
|
+
"@opencode-ai/sdk": ">=1.2.0"
|
|
668
|
+
},
|
|
669
|
+
peerDependenciesMeta: {
|
|
670
|
+
"@opencode-ai/sdk": {
|
|
671
|
+
optional: true
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
devDependencies: {
|
|
675
|
+
"@raindrop-ai/core": "*",
|
|
676
|
+
"@opencode-ai/plugin": "^1.2.0",
|
|
677
|
+
"@opencode-ai/sdk": "^1.2.0",
|
|
678
|
+
"@types/node": "^20.11.17",
|
|
679
|
+
tsup: "^8.4.0",
|
|
680
|
+
typescript: "^5.3.3"
|
|
681
|
+
},
|
|
682
|
+
tsup: {
|
|
683
|
+
entry: [
|
|
684
|
+
"src/index.ts"
|
|
685
|
+
],
|
|
686
|
+
format: [
|
|
687
|
+
"cjs",
|
|
688
|
+
"esm"
|
|
689
|
+
],
|
|
690
|
+
dts: {
|
|
691
|
+
resolve: true
|
|
692
|
+
},
|
|
693
|
+
clean: true,
|
|
694
|
+
noExternal: [
|
|
695
|
+
"@raindrop-ai/core"
|
|
696
|
+
]
|
|
697
|
+
},
|
|
698
|
+
publishConfig: {
|
|
699
|
+
access: "public"
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/shipper.ts
|
|
704
|
+
var EventShipper2 = class extends EventShipper {
|
|
705
|
+
constructor(opts) {
|
|
706
|
+
var _a, _b, _c, _d;
|
|
707
|
+
super({
|
|
708
|
+
...opts,
|
|
709
|
+
sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
|
|
710
|
+
libraryName: (_b = opts.libraryName) != null ? _b : package_default.name,
|
|
711
|
+
libraryVersion: (_c = opts.libraryVersion) != null ? _c : package_default.version,
|
|
712
|
+
defaultEventName: (_d = opts.defaultEventName) != null ? _d : "opencode_session"
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
var TraceShipper2 = class extends TraceShipper {
|
|
717
|
+
constructor(opts) {
|
|
718
|
+
var _a, _b, _c;
|
|
719
|
+
super({
|
|
720
|
+
...opts,
|
|
721
|
+
sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
|
|
722
|
+
serviceName: (_b = opts.serviceName) != null ? _b : "raindrop.opencode-plugin",
|
|
723
|
+
serviceVersion: (_c = opts.serviceVersion) != null ? _c : package_default.version
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
enqueue(span) {
|
|
727
|
+
var _a;
|
|
728
|
+
const attrs = (_a = span.attributes) != null ? _a : [];
|
|
729
|
+
attrs.unshift(
|
|
730
|
+
{ key: "span.id", value: { stringValue: span.spanId } },
|
|
731
|
+
...span.parentSpanId ? [{ key: "span.parent.id", value: { stringValue: span.parentSpanId } }] : []
|
|
732
|
+
);
|
|
733
|
+
span.attributes = attrs;
|
|
734
|
+
super.enqueue(span);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
|
|
525
738
|
// src/tracing-linkage-helpers.ts
|
|
526
739
|
function createSessionParentMapHelpers({
|
|
527
740
|
sessions: sessions2,
|
|
@@ -633,8 +846,8 @@ function getHostname() {
|
|
|
633
846
|
}
|
|
634
847
|
function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
635
848
|
function log(msg, data) {
|
|
636
|
-
|
|
637
|
-
const prefix = `[raindrop-ai/opencode-plugin] [
|
|
849
|
+
if (!config.debug) return;
|
|
850
|
+
const prefix = `[raindrop-ai/opencode-plugin] [info] ${msg}`;
|
|
638
851
|
if (data !== void 0) {
|
|
639
852
|
console.log(prefix, data);
|
|
640
853
|
return;
|
|
@@ -805,7 +1018,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
805
1018
|
attributes: llmAttrs
|
|
806
1019
|
});
|
|
807
1020
|
if (typeof timeObj["created"] === "number") {
|
|
808
|
-
llmSpan.startTimeUnixNano = String(timeObj["created"]
|
|
1021
|
+
llmSpan.startTimeUnixNano = String(Math.floor(timeObj["created"])) + "000000";
|
|
809
1022
|
}
|
|
810
1023
|
traceShipper.endSpan(llmSpan, { error: errorForSpan });
|
|
811
1024
|
await traceShipper.flush();
|
|
@@ -842,7 +1055,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
842
1055
|
attributes: llmAttrs
|
|
843
1056
|
});
|
|
844
1057
|
if (typeof timeObj["created"] === "number") {
|
|
845
|
-
finalLlmSpan.startTimeUnixNano = String(timeObj["created"]
|
|
1058
|
+
finalLlmSpan.startTimeUnixNano = String(Math.floor(timeObj["created"])) + "000000";
|
|
846
1059
|
}
|
|
847
1060
|
traceShipper.endSpan(finalLlmSpan, { error: errorForSpan });
|
|
848
1061
|
traceShipper.endSpan(rootSpan, {
|
|
@@ -902,7 +1115,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
902
1115
|
if (state.currentEventId) {
|
|
903
1116
|
state.currentEventId = void 0;
|
|
904
1117
|
}
|
|
905
|
-
await traceShipper.flush();
|
|
1118
|
+
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
906
1119
|
if (state.parentId) {
|
|
907
1120
|
cleanupSessionParentMap(String(sessionID));
|
|
908
1121
|
sessions.delete(String(sessionID));
|
|
@@ -1099,6 +1312,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1099
1312
|
});
|
|
1100
1313
|
}
|
|
1101
1314
|
} catch (err) {
|
|
1315
|
+
console.log(
|
|
1316
|
+
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.before hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1317
|
+
);
|
|
1102
1318
|
}
|
|
1103
1319
|
},
|
|
1104
1320
|
// ------------------------------------------------------------------
|
|
@@ -1170,6 +1386,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1170
1386
|
});
|
|
1171
1387
|
}
|
|
1172
1388
|
} catch (err) {
|
|
1389
|
+
console.log(
|
|
1390
|
+
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1391
|
+
);
|
|
1173
1392
|
}
|
|
1174
1393
|
}
|
|
1175
1394
|
};
|
|
@@ -1192,12 +1411,12 @@ async function plugin(input) {
|
|
|
1192
1411
|
if (config.debug) {
|
|
1193
1412
|
appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
|
|
1194
1413
|
}
|
|
1195
|
-
const eventShipper = new
|
|
1414
|
+
const eventShipper = new EventShipper2({
|
|
1196
1415
|
writeKey: config.writeKey,
|
|
1197
1416
|
endpoint: config.endpoint,
|
|
1198
1417
|
debug: config.debug
|
|
1199
1418
|
});
|
|
1200
|
-
const traceShipper = new
|
|
1419
|
+
const traceShipper = new TraceShipper2({
|
|
1201
1420
|
writeKey: config.writeKey,
|
|
1202
1421
|
endpoint: config.endpoint,
|
|
1203
1422
|
debug: config.debug
|