@raindrop-ai/opencode-plugin 0.0.4 → 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 +380 -226
- package/dist/index.js +380 -226
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -34,86 +34,30 @@ function loadConfig(projectDirectory) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
42
|
-
type: "module",
|
|
43
|
-
main: "dist/index.cjs",
|
|
44
|
-
module: "dist/index.js",
|
|
45
|
-
types: "dist/index.d.ts",
|
|
46
|
-
exports: {
|
|
47
|
-
".": {
|
|
48
|
-
types: "./dist/index.d.ts",
|
|
49
|
-
import: "./dist/index.js",
|
|
50
|
-
require: "./dist/index.cjs"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
sideEffects: false,
|
|
54
|
-
files: [
|
|
55
|
-
"dist/**"
|
|
56
|
-
],
|
|
57
|
-
scripts: {
|
|
58
|
-
build: "tsup",
|
|
59
|
-
dev: "tsup --watch",
|
|
60
|
-
clean: "rm -rf dist",
|
|
61
|
-
test: "cd tests && yarn test"
|
|
62
|
-
},
|
|
63
|
-
peerDependencies: {
|
|
64
|
-
"@opencode-ai/plugin": ">=1.2.0",
|
|
65
|
-
"@opencode-ai/sdk": ">=1.2.0"
|
|
66
|
-
},
|
|
67
|
-
peerDependenciesMeta: {
|
|
68
|
-
"@opencode-ai/sdk": {
|
|
69
|
-
optional: true
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
devDependencies: {
|
|
73
|
-
"@opencode-ai/plugin": "^1.2.0",
|
|
74
|
-
"@opencode-ai/sdk": "^1.2.0",
|
|
75
|
-
"@types/node": "^20.11.17",
|
|
76
|
-
tsup: "^8.4.0",
|
|
77
|
-
typescript: "^5.3.3"
|
|
78
|
-
},
|
|
79
|
-
tsup: {
|
|
80
|
-
entry: [
|
|
81
|
-
"src/index.ts"
|
|
82
|
-
],
|
|
83
|
-
format: [
|
|
84
|
-
"cjs",
|
|
85
|
-
"esm"
|
|
86
|
-
],
|
|
87
|
-
dts: {
|
|
88
|
-
resolve: true
|
|
89
|
-
},
|
|
90
|
-
clean: true
|
|
91
|
-
},
|
|
92
|
-
publishConfig: {
|
|
93
|
-
access: "public"
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// src/shipper.ts
|
|
98
|
-
var PLUGIN_VERSION = package_default.version;
|
|
99
|
-
function getCryptoObj() {
|
|
100
|
-
return globalThis.crypto;
|
|
37
|
+
// ../core/dist/chunk-FTBZHS25.js
|
|
38
|
+
function getCrypto() {
|
|
39
|
+
const c = globalThis.crypto;
|
|
40
|
+
return c;
|
|
101
41
|
}
|
|
102
42
|
function randomBytes(length) {
|
|
103
|
-
const
|
|
43
|
+
const cryptoObj = getCrypto();
|
|
104
44
|
const out = new Uint8Array(length);
|
|
105
|
-
if (
|
|
106
|
-
|
|
45
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
46
|
+
cryptoObj.getRandomValues(out);
|
|
107
47
|
return out;
|
|
108
48
|
}
|
|
109
49
|
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
110
50
|
return out;
|
|
111
51
|
}
|
|
112
52
|
function base64Encode(bytes) {
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
53
|
+
const maybeBuffer = globalThis.Buffer;
|
|
54
|
+
if (maybeBuffer) {
|
|
55
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
56
|
+
}
|
|
115
57
|
let binary = "";
|
|
116
|
-
for (let i2 = 0; i2 < bytes.length; i2++)
|
|
58
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
59
|
+
binary += String.fromCharCode(bytes[i2]);
|
|
60
|
+
}
|
|
117
61
|
const btoaFn = globalThis.btoa;
|
|
118
62
|
if (typeof btoaFn === "function") return btoaFn(binary);
|
|
119
63
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -134,69 +78,9 @@ function base64Encode(bytes) {
|
|
|
134
78
|
}
|
|
135
79
|
return out;
|
|
136
80
|
}
|
|
137
|
-
var SERVICE_NAME = "raindrop.opencode-plugin";
|
|
138
|
-
var SpanStatusCode = {
|
|
139
|
-
ERROR: 2
|
|
140
|
-
};
|
|
141
81
|
function generateId() {
|
|
142
82
|
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
143
83
|
}
|
|
144
|
-
function createSpanIds(parent) {
|
|
145
|
-
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
146
|
-
const spanId = base64Encode(randomBytes(8));
|
|
147
|
-
return {
|
|
148
|
-
traceIdB64: traceId,
|
|
149
|
-
spanIdB64: spanId,
|
|
150
|
-
parentSpanIdB64: parent ? parent.spanIdB64 : void 0
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
function nowUnixNanoString() {
|
|
154
|
-
return Date.now().toString() + "000000";
|
|
155
|
-
}
|
|
156
|
-
function attrString(key, value) {
|
|
157
|
-
if (value === void 0) return void 0;
|
|
158
|
-
return { key, value: { stringValue: value } };
|
|
159
|
-
}
|
|
160
|
-
function attrInt(key, value) {
|
|
161
|
-
if (value === void 0) return void 0;
|
|
162
|
-
if (!Number.isFinite(value)) return void 0;
|
|
163
|
-
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
164
|
-
}
|
|
165
|
-
function buildOtlpSpan(args) {
|
|
166
|
-
const attrs = [
|
|
167
|
-
attrString("span.id", args.ids.spanIdB64),
|
|
168
|
-
attrString("span.parent.id", args.ids.parentSpanIdB64),
|
|
169
|
-
...args.attributes
|
|
170
|
-
].filter((x) => x !== void 0);
|
|
171
|
-
const span = {
|
|
172
|
-
traceId: args.ids.traceIdB64,
|
|
173
|
-
spanId: args.ids.spanIdB64,
|
|
174
|
-
name: args.name,
|
|
175
|
-
startTimeUnixNano: args.startTimeUnixNano,
|
|
176
|
-
endTimeUnixNano: args.endTimeUnixNano
|
|
177
|
-
};
|
|
178
|
-
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
179
|
-
if (attrs.length) span.attributes = attrs;
|
|
180
|
-
if (args.status) span.status = args.status;
|
|
181
|
-
return span;
|
|
182
|
-
}
|
|
183
|
-
function buildExportTraceServiceRequest(spans) {
|
|
184
|
-
return {
|
|
185
|
-
resourceSpans: [
|
|
186
|
-
{
|
|
187
|
-
resource: {
|
|
188
|
-
attributes: [{ key: "service.name", value: { stringValue: SERVICE_NAME } }]
|
|
189
|
-
},
|
|
190
|
-
scopeSpans: [
|
|
191
|
-
{
|
|
192
|
-
scope: { name: SERVICE_NAME, version: PLUGIN_VERSION },
|
|
193
|
-
spans
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
]
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
84
|
function wait(ms) {
|
|
201
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
202
86
|
}
|
|
@@ -209,7 +93,7 @@ function parseRetryAfter(headers) {
|
|
|
209
93
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
210
94
|
if (!value) return void 0;
|
|
211
95
|
const asNumber = Number(value);
|
|
212
|
-
if (!Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
96
|
+
if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
213
97
|
const asDate = new Date(value).getTime();
|
|
214
98
|
if (!Number.isNaN(asDate)) {
|
|
215
99
|
const delta = asDate - Date.now();
|
|
@@ -218,26 +102,29 @@ function parseRetryAfter(headers) {
|
|
|
218
102
|
return void 0;
|
|
219
103
|
}
|
|
220
104
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
221
|
-
if (previousError
|
|
105
|
+
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
222
106
|
const v = previousError.retryAfterMs;
|
|
223
107
|
if (typeof v === "number") return Math.max(0, v);
|
|
224
108
|
}
|
|
225
109
|
if (attemptNumber <= 1) return 0;
|
|
226
|
-
|
|
110
|
+
const base = 500;
|
|
111
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
112
|
+
return base * factor;
|
|
227
113
|
}
|
|
228
114
|
async function withRetry(operation, opName, opts) {
|
|
115
|
+
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
229
116
|
let lastError = void 0;
|
|
230
|
-
for (let
|
|
231
|
-
if (
|
|
232
|
-
const delay = getRetryDelayMs(
|
|
117
|
+
for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
|
|
118
|
+
if (attemptNumber > 1) {
|
|
119
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
233
120
|
if (opts.debug) {
|
|
234
121
|
console.warn(
|
|
235
|
-
|
|
122
|
+
`${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
|
|
236
123
|
);
|
|
237
124
|
}
|
|
238
125
|
if (delay > 0) await wait(delay);
|
|
239
126
|
} else if (opts.debug) {
|
|
240
|
-
console.log(
|
|
127
|
+
console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
|
|
241
128
|
}
|
|
242
129
|
try {
|
|
243
130
|
return await operation();
|
|
@@ -246,20 +133,26 @@ async function withRetry(operation, opName, opts) {
|
|
|
246
133
|
if (opts.debug) {
|
|
247
134
|
const msg = err instanceof Error ? err.message : String(err);
|
|
248
135
|
console.warn(
|
|
249
|
-
|
|
136
|
+
`${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
|
|
250
137
|
);
|
|
251
138
|
}
|
|
252
|
-
if (
|
|
139
|
+
if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
|
|
140
|
+
break;
|
|
141
|
+
if (attemptNumber === opts.maxAttempts) break;
|
|
253
142
|
}
|
|
254
143
|
}
|
|
255
144
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
256
145
|
}
|
|
257
146
|
async function postJson(url, body, headers, opts) {
|
|
147
|
+
const opName = `POST ${url}`;
|
|
258
148
|
await withRetry(
|
|
259
149
|
async () => {
|
|
260
150
|
const resp = await fetch(url, {
|
|
261
151
|
method: "POST",
|
|
262
|
-
headers: {
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
...headers
|
|
155
|
+
},
|
|
263
156
|
body: JSON.stringify(body)
|
|
264
157
|
});
|
|
265
158
|
if (!resp.ok) {
|
|
@@ -269,13 +162,71 @@ async function postJson(url, body, headers, opts) {
|
|
|
269
162
|
);
|
|
270
163
|
const retryAfterMs = parseRetryAfter(resp.headers);
|
|
271
164
|
if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
|
|
165
|
+
err.retryable = resp.status === 429 || resp.status >= 500;
|
|
272
166
|
throw err;
|
|
273
167
|
}
|
|
274
168
|
},
|
|
275
|
-
|
|
169
|
+
opName,
|
|
276
170
|
opts
|
|
277
171
|
);
|
|
278
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
|
+
}
|
|
279
230
|
function mergePatches(target, source) {
|
|
280
231
|
var _a, _b, _c, _d;
|
|
281
232
|
const out = { ...target, ...source };
|
|
@@ -293,34 +244,58 @@ var EventShipper = class {
|
|
|
293
244
|
this.sticky = /* @__PURE__ */ new Map();
|
|
294
245
|
this.timers = /* @__PURE__ */ new Map();
|
|
295
246
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
296
|
-
var _a, _b;
|
|
297
|
-
this.writeKey = opts.writeKey;
|
|
298
|
-
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;
|
|
299
251
|
this.debug = opts.debug;
|
|
300
|
-
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}` } : {};
|
|
301
273
|
}
|
|
302
274
|
async patch(eventId, patch) {
|
|
303
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
304
|
-
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;
|
|
305
278
|
if (this.debug) {
|
|
306
|
-
console.log(
|
|
279
|
+
console.log(`${this.prefix} queue patch`, {
|
|
307
280
|
eventId,
|
|
308
281
|
userId: patch.userId,
|
|
282
|
+
eventName: patch.eventName,
|
|
309
283
|
hasInput: typeof patch.input === "string" && patch.input.length > 0,
|
|
310
284
|
hasOutput: typeof patch.output === "string" && patch.output.length > 0,
|
|
285
|
+
attachments: (_b = (_a = patch.attachments) == null ? void 0 : _a.length) != null ? _b : 0,
|
|
311
286
|
isPending: patch.isPending
|
|
312
287
|
});
|
|
313
288
|
}
|
|
314
|
-
const sticky = (
|
|
315
|
-
const existing = (
|
|
289
|
+
const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
|
|
290
|
+
const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
|
|
316
291
|
const merged = mergePatches(existing, patch);
|
|
317
|
-
merged.isPending = (
|
|
292
|
+
merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
|
|
318
293
|
this.buffers.set(eventId, merged);
|
|
319
294
|
this.sticky.set(eventId, {
|
|
320
|
-
userId: (
|
|
321
|
-
convoId: (
|
|
322
|
-
eventName: (
|
|
323
|
-
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
|
|
324
299
|
});
|
|
325
300
|
const t = this.timers.get(eventId);
|
|
326
301
|
if (t) clearTimeout(t);
|
|
@@ -338,6 +313,7 @@ var EventShipper = class {
|
|
|
338
313
|
await this.patch(eventId, { ...patch, isPending: false });
|
|
339
314
|
}
|
|
340
315
|
async flush() {
|
|
316
|
+
if (!this.enabled) return;
|
|
341
317
|
const ids = [...this.buffers.keys()];
|
|
342
318
|
await Promise.all(ids.map((id) => this.flushOne(id)));
|
|
343
319
|
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
@@ -348,8 +324,70 @@ var EventShipper = class {
|
|
|
348
324
|
this.timers.clear();
|
|
349
325
|
await this.flush();
|
|
350
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
|
+
}
|
|
351
388
|
async flushOne(eventId) {
|
|
352
|
-
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;
|
|
353
391
|
const timer = this.timers.get(eventId);
|
|
354
392
|
if (timer) {
|
|
355
393
|
clearTimeout(timer);
|
|
@@ -359,105 +397,123 @@ var EventShipper = class {
|
|
|
359
397
|
this.buffers.delete(eventId);
|
|
360
398
|
if (!accumulated) return;
|
|
361
399
|
const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
|
|
362
|
-
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;
|
|
363
401
|
const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
|
|
364
402
|
if (!userId) {
|
|
365
403
|
if (this.debug) {
|
|
366
|
-
console.warn(
|
|
367
|
-
`[raindrop-ai/opencode-plugin] skipping track_partial for ${eventId}: missing userId`
|
|
368
|
-
);
|
|
404
|
+
console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
|
|
369
405
|
}
|
|
406
|
+
this.sticky.delete(eventId);
|
|
370
407
|
return;
|
|
371
408
|
}
|
|
409
|
+
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
372
410
|
const payload = {
|
|
373
411
|
event_id: eventId,
|
|
374
412
|
user_id: userId,
|
|
375
413
|
event: eventName,
|
|
376
|
-
timestamp: (
|
|
414
|
+
timestamp: (_f = accumulated.timestamp) != null ? _f : (/* @__PURE__ */ new Date()).toISOString(),
|
|
377
415
|
ai_data: {
|
|
378
416
|
input: accumulated.input,
|
|
379
417
|
output: accumulated.output,
|
|
380
418
|
model: accumulated.model,
|
|
381
|
-
convo_id: (
|
|
419
|
+
convo_id: (_g = accumulated.convoId) != null ? _g : sticky.convoId
|
|
382
420
|
},
|
|
383
421
|
properties: {
|
|
384
|
-
...
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
metadata: { jsRuntime: "node" }
|
|
388
|
-
}
|
|
422
|
+
...restProperties,
|
|
423
|
+
...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
|
|
424
|
+
$context: this.context
|
|
389
425
|
},
|
|
390
426
|
attachments: accumulated.attachments,
|
|
391
427
|
is_pending: ((_i = (_h = accumulated.isPending) != null ? _h : sticky.isPending) != null ? _i : true) !== false
|
|
392
428
|
};
|
|
393
429
|
const url = `${this.baseUrl}events/track_partial`;
|
|
394
430
|
if (this.debug) {
|
|
395
|
-
console.log(
|
|
431
|
+
console.log(`${this.prefix} sending track_partial`, {
|
|
396
432
|
eventId,
|
|
397
433
|
eventName,
|
|
398
434
|
userId,
|
|
399
|
-
isPending: payload
|
|
435
|
+
isPending: payload.is_pending,
|
|
400
436
|
inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
|
|
401
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 : [],
|
|
402
445
|
endpoint: url
|
|
403
446
|
});
|
|
404
447
|
}
|
|
405
|
-
const p = postJson(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
);
|
|
448
|
+
const p = postJson(url, payload, this.authHeaders(), {
|
|
449
|
+
maxAttempts: 3,
|
|
450
|
+
debug: this.debug,
|
|
451
|
+
sdkName: this.sdkName
|
|
452
|
+
});
|
|
411
453
|
this.inFlight.add(p);
|
|
412
454
|
try {
|
|
413
455
|
try {
|
|
414
456
|
await p;
|
|
415
457
|
if (this.debug) {
|
|
416
|
-
console.log(
|
|
458
|
+
console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
|
|
417
459
|
}
|
|
418
460
|
} catch (err) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
console.warn(
|
|
422
|
-
`[raindrop-ai/opencode-plugin] failed to send track_partial (dropping): ${msg}`
|
|
423
|
-
);
|
|
424
|
-
}
|
|
461
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
462
|
+
console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
|
|
425
463
|
}
|
|
426
464
|
} finally {
|
|
427
465
|
this.inFlight.delete(p);
|
|
428
466
|
}
|
|
429
|
-
|
|
467
|
+
const isPending = payload.is_pending;
|
|
468
|
+
if (!isPending) {
|
|
469
|
+
this.sticky.delete(eventId);
|
|
470
|
+
}
|
|
430
471
|
}
|
|
431
472
|
};
|
|
432
473
|
var TraceShipper = class {
|
|
433
474
|
constructor(opts) {
|
|
434
475
|
this.queue = [];
|
|
435
476
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
436
|
-
var _a, _b;
|
|
437
|
-
this.writeKey = opts.writeKey;
|
|
438
|
-
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;
|
|
439
481
|
this.debug = opts.debug;
|
|
440
|
-
this.
|
|
441
|
-
this.
|
|
442
|
-
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}` } : {};
|
|
443
496
|
}
|
|
444
497
|
startSpan(args) {
|
|
445
|
-
var _a;
|
|
498
|
+
var _a, _b;
|
|
446
499
|
const ids = createSpanIds(args.parent);
|
|
447
|
-
const started = nowUnixNanoString();
|
|
500
|
+
const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
448
501
|
const attrs = [
|
|
449
|
-
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
502
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
503
|
+
attrString("ai.operationId", args.operationId)
|
|
450
504
|
];
|
|
451
|
-
if ((
|
|
505
|
+
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
452
506
|
return { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
453
507
|
}
|
|
454
508
|
endSpan(span, extra) {
|
|
455
|
-
var _a;
|
|
509
|
+
var _a, _b;
|
|
456
510
|
if (span.endTimeUnixNano) return;
|
|
457
|
-
span.endTimeUnixNano = nowUnixNanoString();
|
|
458
|
-
if ((
|
|
459
|
-
|
|
460
|
-
|
|
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) {
|
|
461
517
|
const message = extra.error instanceof Error ? extra.error.message : String(extra.error);
|
|
462
518
|
status = { code: SpanStatusCode.ERROR, message };
|
|
463
519
|
}
|
|
@@ -471,10 +527,6 @@ var TraceShipper = class {
|
|
|
471
527
|
});
|
|
472
528
|
this.enqueue(otlp);
|
|
473
529
|
}
|
|
474
|
-
/**
|
|
475
|
-
* Create and enqueue a span with both start and end times (atomic insert).
|
|
476
|
-
* Used for tool spans and any span whose full duration is known at write time.
|
|
477
|
-
*/
|
|
478
530
|
createSpan(args) {
|
|
479
531
|
var _a;
|
|
480
532
|
const ids = createSpanIds(args.parent);
|
|
@@ -493,7 +545,18 @@ var TraceShipper = class {
|
|
|
493
545
|
this.enqueue(otlp);
|
|
494
546
|
}
|
|
495
547
|
enqueue(span) {
|
|
496
|
-
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
|
+
}
|
|
497
560
|
this.queue.push(span);
|
|
498
561
|
if (this.queue.length >= this.maxBatchSize) {
|
|
499
562
|
void this.flush().catch(() => {
|
|
@@ -509,42 +572,34 @@ var TraceShipper = class {
|
|
|
509
572
|
}
|
|
510
573
|
}
|
|
511
574
|
async flush() {
|
|
575
|
+
if (!this.enabled) return;
|
|
512
576
|
if (this.timer) {
|
|
513
577
|
clearTimeout(this.timer);
|
|
514
578
|
this.timer = void 0;
|
|
515
579
|
}
|
|
516
580
|
while (this.queue.length > 0) {
|
|
517
581
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
518
|
-
const body = buildExportTraceServiceRequest(batch);
|
|
582
|
+
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
519
583
|
const url = `${this.baseUrl}traces`;
|
|
520
|
-
const spanNames = batch.map((span) => span.name);
|
|
521
584
|
if (this.debug) {
|
|
522
|
-
console.log(
|
|
585
|
+
console.log(`${this.prefix} sending traces batch`, {
|
|
523
586
|
spans: batch.length,
|
|
524
|
-
endpoint: url
|
|
525
|
-
spanNames
|
|
587
|
+
endpoint: url
|
|
526
588
|
});
|
|
527
589
|
}
|
|
528
|
-
const p = postJson(
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
);
|
|
590
|
+
const p = postJson(url, body, this.authHeaders(), {
|
|
591
|
+
maxAttempts: 3,
|
|
592
|
+
debug: this.debug,
|
|
593
|
+
sdkName: this.sdkName
|
|
594
|
+
});
|
|
534
595
|
this.inFlight.add(p);
|
|
535
596
|
try {
|
|
536
597
|
try {
|
|
537
598
|
await p;
|
|
538
|
-
if (this.debug) {
|
|
539
|
-
console.log(`[raindrop-ai/opencode-plugin] sent ${batch.length} spans`, {
|
|
540
|
-
spanNames
|
|
541
|
-
});
|
|
542
|
-
}
|
|
599
|
+
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
543
600
|
} catch (err) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
console.warn(`[raindrop-ai/opencode-plugin] failed to send spans (dropping): ${msg}`);
|
|
547
|
-
}
|
|
601
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
602
|
+
console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
|
|
548
603
|
}
|
|
549
604
|
} finally {
|
|
550
605
|
this.inFlight.delete(p);
|
|
@@ -562,6 +617,105 @@ var TraceShipper = class {
|
|
|
562
617
|
}
|
|
563
618
|
};
|
|
564
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
|
+
|
|
565
719
|
// src/tracing-linkage-helpers.ts
|
|
566
720
|
function createSessionParentMapHelpers({
|
|
567
721
|
sessions: sessions2,
|
|
@@ -1238,12 +1392,12 @@ async function plugin(input) {
|
|
|
1238
1392
|
if (config.debug) {
|
|
1239
1393
|
appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
|
|
1240
1394
|
}
|
|
1241
|
-
const eventShipper = new
|
|
1395
|
+
const eventShipper = new EventShipper2({
|
|
1242
1396
|
writeKey: config.writeKey,
|
|
1243
1397
|
endpoint: config.endpoint,
|
|
1244
1398
|
debug: config.debug
|
|
1245
1399
|
});
|
|
1246
|
-
const traceShipper = new
|
|
1400
|
+
const traceShipper = new TraceShipper2({
|
|
1247
1401
|
writeKey: config.writeKey,
|
|
1248
1402
|
endpoint: config.endpoint,
|
|
1249
1403
|
debug: config.debug
|