@raindrop-ai/opencode-plugin 0.0.18 → 0.1.0
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 +102 -15
- package/dist/index.d.cts +105 -3
- package/dist/index.d.ts +105 -3
- package/dist/index.js +102 -15
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
26
|
|
|
27
|
-
// ../core/dist/chunk-
|
|
27
|
+
// ../core/dist/chunk-QTUH3BHZ.js
|
|
28
28
|
function getCrypto() {
|
|
29
29
|
const c = globalThis.crypto;
|
|
30
30
|
return c;
|
|
@@ -39,6 +39,17 @@ function randomBytes(length) {
|
|
|
39
39
|
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
40
40
|
return out;
|
|
41
41
|
}
|
|
42
|
+
function randomUUID() {
|
|
43
|
+
const cryptoObj = getCrypto();
|
|
44
|
+
if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
|
|
45
|
+
return cryptoObj.randomUUID();
|
|
46
|
+
}
|
|
47
|
+
const b = randomBytes(16);
|
|
48
|
+
b[6] = b[6] & 15 | 64;
|
|
49
|
+
b[8] = b[8] & 63 | 128;
|
|
50
|
+
const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
51
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
52
|
+
}
|
|
42
53
|
function base64Encode(bytes) {
|
|
43
54
|
const maybeBuffer = globalThis.Buffer;
|
|
44
55
|
if (maybeBuffer) {
|
|
@@ -69,7 +80,7 @@ function base64Encode(bytes) {
|
|
|
69
80
|
return out;
|
|
70
81
|
}
|
|
71
82
|
function generateId() {
|
|
72
|
-
return
|
|
83
|
+
return randomUUID();
|
|
73
84
|
}
|
|
74
85
|
function runWithTracingSuppressed(fn) {
|
|
75
86
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
@@ -406,16 +417,52 @@ function projectIdHeaders(projectId) {
|
|
|
406
417
|
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
407
418
|
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
408
419
|
function mergePatches(target, source) {
|
|
409
|
-
var _a, _b, _c, _d;
|
|
420
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
410
421
|
const out = { ...target, ...source };
|
|
411
422
|
if (target.properties || source.properties) {
|
|
412
423
|
out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
|
|
413
424
|
}
|
|
425
|
+
if (target.usage || source.usage) {
|
|
426
|
+
out.usage = { ...(_c = target.usage) != null ? _c : {} };
|
|
427
|
+
if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
|
|
428
|
+
out.usage.promptTokens = source.usage.promptTokens;
|
|
429
|
+
}
|
|
430
|
+
if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
|
|
431
|
+
out.usage.completionTokens = source.usage.completionTokens;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (source.error === void 0) {
|
|
435
|
+
out.error = target.error;
|
|
436
|
+
}
|
|
437
|
+
if (target.featureFlags || source.featureFlags) {
|
|
438
|
+
out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
|
|
439
|
+
}
|
|
414
440
|
if (target.attachments || source.attachments) {
|
|
415
|
-
out.attachments = [...(
|
|
441
|
+
out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
|
|
416
442
|
}
|
|
417
443
|
return out;
|
|
418
444
|
}
|
|
445
|
+
function isErrorLike(value) {
|
|
446
|
+
return value !== null && typeof value === "object";
|
|
447
|
+
}
|
|
448
|
+
function eventTelemetryProperties(patch) {
|
|
449
|
+
var _a, _b;
|
|
450
|
+
const properties = {};
|
|
451
|
+
if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
|
|
452
|
+
properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
|
|
453
|
+
}
|
|
454
|
+
if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
|
|
455
|
+
properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
|
|
456
|
+
}
|
|
457
|
+
if (patch.error !== void 0 && patch.error !== null) {
|
|
458
|
+
const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
|
|
459
|
+
const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
|
|
460
|
+
const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
|
|
461
|
+
properties["error.type"] = errorType;
|
|
462
|
+
properties["error.message"] = errorMessage;
|
|
463
|
+
}
|
|
464
|
+
return properties;
|
|
465
|
+
}
|
|
419
466
|
var EventShipper = class {
|
|
420
467
|
constructor(opts) {
|
|
421
468
|
this.buffers = /* @__PURE__ */ new Map();
|
|
@@ -664,6 +711,11 @@ var EventShipper = class {
|
|
|
664
711
|
return;
|
|
665
712
|
}
|
|
666
713
|
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
714
|
+
let telemetryProperties = {};
|
|
715
|
+
try {
|
|
716
|
+
telemetryProperties = eventTelemetryProperties(accumulated);
|
|
717
|
+
} catch (e) {
|
|
718
|
+
}
|
|
667
719
|
const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
|
|
668
720
|
const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
|
|
669
721
|
const payload = {
|
|
@@ -673,15 +725,17 @@ var EventShipper = class {
|
|
|
673
725
|
timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
|
|
674
726
|
ai_data: {
|
|
675
727
|
input: accumulated.input,
|
|
676
|
-
output: accumulated.output,
|
|
728
|
+
output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
|
|
677
729
|
model: accumulated.model,
|
|
678
730
|
convo_id: convoId
|
|
679
731
|
},
|
|
680
732
|
properties: {
|
|
681
733
|
...restProperties,
|
|
734
|
+
...telemetryProperties,
|
|
682
735
|
...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
|
|
683
736
|
$context: this.context
|
|
684
737
|
},
|
|
738
|
+
...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
|
|
685
739
|
attachments: accumulated.attachments,
|
|
686
740
|
is_pending: isPending
|
|
687
741
|
};
|
|
@@ -835,6 +889,13 @@ function redactJsonAttributeValue(key, value) {
|
|
|
835
889
|
if (scrubbedJson === json) return void 0;
|
|
836
890
|
return { stringValue: scrubbedJson };
|
|
837
891
|
}
|
|
892
|
+
function identityAttrs(args) {
|
|
893
|
+
return [
|
|
894
|
+
attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
|
|
895
|
+
attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
|
|
896
|
+
attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
|
|
897
|
+
];
|
|
898
|
+
}
|
|
838
899
|
function applyOtelSpanAttributeLimit(limit) {
|
|
839
900
|
var _a, _b;
|
|
840
901
|
try {
|
|
@@ -959,6 +1020,7 @@ var TraceShipper = class {
|
|
|
959
1020
|
const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
960
1021
|
const attrs = [
|
|
961
1022
|
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
1023
|
+
...identityAttrs(args),
|
|
962
1024
|
attrString("ai.operationId", args.operationId)
|
|
963
1025
|
];
|
|
964
1026
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
@@ -1014,7 +1076,8 @@ var TraceShipper = class {
|
|
|
1014
1076
|
var _a;
|
|
1015
1077
|
const ids = createSpanIds(args.parent);
|
|
1016
1078
|
const attrs = [
|
|
1017
|
-
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
1079
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
1080
|
+
...identityAttrs(args)
|
|
1018
1081
|
];
|
|
1019
1082
|
if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
|
|
1020
1083
|
const otlp = buildOtlpSpan({
|
|
@@ -1233,7 +1296,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1233
1296
|
// package.json
|
|
1234
1297
|
var package_default = {
|
|
1235
1298
|
name: "@raindrop-ai/opencode-plugin",
|
|
1236
|
-
version: "0.0
|
|
1299
|
+
version: "0.1.0",
|
|
1237
1300
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1238
1301
|
type: "module",
|
|
1239
1302
|
main: "dist/index.js",
|
|
@@ -1618,7 +1681,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1618
1681
|
// event — session lifecycle + streaming parts
|
|
1619
1682
|
// ------------------------------------------------------------------
|
|
1620
1683
|
event: async ({ event }) => {
|
|
1621
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1684
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
1622
1685
|
try {
|
|
1623
1686
|
const props = event.properties;
|
|
1624
1687
|
const info = props["info"];
|
|
@@ -1890,7 +1953,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1890
1953
|
}
|
|
1891
1954
|
state.currentRootSpan = void 0;
|
|
1892
1955
|
const hasAssistantResponse = outputText.trim().length > 0;
|
|
1893
|
-
if (!hasAssistantResponse) {
|
|
1956
|
+
if (!hasAssistantResponse && !msgError) {
|
|
1894
1957
|
await traceShipper.flush();
|
|
1895
1958
|
state.currentEventId = void 0;
|
|
1896
1959
|
return;
|
|
@@ -1898,11 +1961,20 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1898
1961
|
if (!state.parentId) {
|
|
1899
1962
|
await eventShipper.finish(state.currentEventId, {
|
|
1900
1963
|
userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
|
|
1901
|
-
model:
|
|
1902
|
-
output: outputText,
|
|
1964
|
+
model: modelID,
|
|
1965
|
+
output: msgError ? void 0 : outputText,
|
|
1966
|
+
usage: {
|
|
1967
|
+
promptTokens: inputTokens,
|
|
1968
|
+
completionTokens: outputTokens
|
|
1969
|
+
},
|
|
1970
|
+
error: msgError ? {
|
|
1971
|
+
name: msgError.name,
|
|
1972
|
+
message: (_g = (_f = (_e = msgError.data) == null ? void 0 : _e.message) != null ? _f : msgError.name) != null ? _g : "OpenCode request failed"
|
|
1973
|
+
} : void 0,
|
|
1903
1974
|
properties: {
|
|
1904
1975
|
plugin_version: PLUGIN_VERSION,
|
|
1905
1976
|
message_id: messageId,
|
|
1977
|
+
"ai.provider": providerID,
|
|
1906
1978
|
...isCompaction ? { is_compaction: true } : {}
|
|
1907
1979
|
}
|
|
1908
1980
|
});
|
|
@@ -1942,7 +2014,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1942
2014
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
1943
2015
|
sessions.delete(String(sessionID));
|
|
1944
2016
|
} else if (event.type === "session.error") {
|
|
1945
|
-
const errorSessionID = (
|
|
2017
|
+
const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
|
|
1946
2018
|
if (errorSessionID === void 0 || errorSessionID === "")
|
|
1947
2019
|
throw new Error("session.error: missing sessionID");
|
|
1948
2020
|
const state = sessions.get(String(errorSessionID));
|
|
@@ -1950,7 +2022,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1950
2022
|
const errorObj = props["error"];
|
|
1951
2023
|
if (errorObj == null) throw new Error("session.error: missing error object");
|
|
1952
2024
|
const errorName = errorObj.name;
|
|
1953
|
-
const errorMessage = (
|
|
2025
|
+
const errorMessage = (_j = (_i = errorObj.data) == null ? void 0 : _i.message) != null ? _j : errorObj.name;
|
|
1954
2026
|
if (errorName == null && errorMessage == null)
|
|
1955
2027
|
throw new Error("session.error: error object must have name or data.message");
|
|
1956
2028
|
const errorStr = `${errorMessage != null ? errorMessage : errorName}
|
|
@@ -2010,7 +2082,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
2010
2082
|
promptMeta.properties = m["properties"];
|
|
2011
2083
|
}
|
|
2012
2084
|
} else if (part["type"] === "file") {
|
|
2013
|
-
const mimeType = part["
|
|
2085
|
+
const mimeType = part["mime"];
|
|
2014
2086
|
const filename = part["filename"];
|
|
2015
2087
|
const url = part["url"];
|
|
2016
2088
|
if (url && (mimeType == null ? void 0 : mimeType.startsWith("image/"))) {
|
|
@@ -2244,6 +2316,21 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
2244
2316
|
}
|
|
2245
2317
|
|
|
2246
2318
|
// src/index.ts
|
|
2319
|
+
function createRaindropHandle(eventShipper, traceShipper) {
|
|
2320
|
+
return {
|
|
2321
|
+
users: {
|
|
2322
|
+
async identify(users) {
|
|
2323
|
+
await eventShipper.identify(users);
|
|
2324
|
+
}
|
|
2325
|
+
},
|
|
2326
|
+
async flush() {
|
|
2327
|
+
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
2328
|
+
},
|
|
2329
|
+
async shutdown() {
|
|
2330
|
+
await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2247
2334
|
async function plugin(input) {
|
|
2248
2335
|
var _a;
|
|
2249
2336
|
const config = loadConfig(input.directory);
|
|
@@ -2289,5 +2376,5 @@ async function plugin(input) {
|
|
|
2289
2376
|
eventShipper,
|
|
2290
2377
|
traceShipper
|
|
2291
2378
|
);
|
|
2292
|
-
return hooks;
|
|
2379
|
+
return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper) };
|
|
2293
2380
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,107 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Hooks, PluginInput } from '@opencode-ai/plugin';
|
|
2
2
|
|
|
3
|
+
type IdentifyInput = {
|
|
4
|
+
userId: string;
|
|
5
|
+
traits?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run telemetry egress with OpenTelemetry tracing suppressed.
|
|
10
|
+
*
|
|
11
|
+
* Why this exists
|
|
12
|
+
* ---------------
|
|
13
|
+
* Raindrop integrations ship spans/events over HTTP with the global `fetch`
|
|
14
|
+
* (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
|
|
15
|
+
* fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
|
|
16
|
+
* every Eve agent installs — that instrumentation wraps *our own* telemetry
|
|
17
|
+
* POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
|
|
18
|
+
* very exporter that issued the request, so they get shipped right back to
|
|
19
|
+
* Raindrop and Workshop as standalone "runs" (and, because each export issues
|
|
20
|
+
* another fetch, they feed back on themselves). The result is a run list
|
|
21
|
+
* flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
|
|
22
|
+
* `.../live` entries that drown out the real agent turns — especially with
|
|
23
|
+
* sub-agents, where every sandbox runs its own instrumentation.
|
|
24
|
+
*
|
|
25
|
+
* The OTel-blessed fix is to mark the active context as "tracing suppressed"
|
|
26
|
+
* around the request; instrumentations check `isTracingSuppressed` and return
|
|
27
|
+
* a no-op span instead of recording one. We do this through a hook stashed on
|
|
28
|
+
* `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
|
|
29
|
+
* - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
|
|
30
|
+
* hard-depends on them, and the hook is simply absent when they (and thus
|
|
31
|
+
* any instrumentation to suppress) are not installed; and
|
|
32
|
+
* - the browser bundle never pulls in `node:module`, mirroring how core
|
|
33
|
+
* injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
|
|
34
|
+
*
|
|
35
|
+
* When no hook is present the callback runs unchanged, so suppression is a
|
|
36
|
+
* best-effort no-op rather than a hard requirement.
|
|
37
|
+
*/
|
|
38
|
+
/** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
|
|
39
|
+
type SuppressTracingHook = <T>(fn: () => T) => T;
|
|
40
|
+
declare global {
|
|
41
|
+
var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type ParentSpanContext = {
|
|
45
|
+
traceIdB64: string;
|
|
46
|
+
spanIdB64: string;
|
|
47
|
+
eventId: string;
|
|
48
|
+
};
|
|
49
|
+
interface ContextSpan {
|
|
50
|
+
readonly traceIdB64: string;
|
|
51
|
+
readonly spanIdB64: string;
|
|
52
|
+
readonly eventId: string;
|
|
53
|
+
log?(data: Record<string, unknown>): void;
|
|
54
|
+
}
|
|
55
|
+
interface AsyncLocalStorageLike<T> {
|
|
56
|
+
getStore(): T | undefined;
|
|
57
|
+
run<R>(store: T, callback: () => R): R;
|
|
58
|
+
enterWith?(store: T): void;
|
|
59
|
+
}
|
|
60
|
+
declare abstract class ContextManager {
|
|
61
|
+
abstract getParentSpanIds(): ParentSpanContext | undefined;
|
|
62
|
+
abstract runInContext<R>(span: ContextSpan, callback: () => R): R;
|
|
63
|
+
abstract getCurrentSpan(): ContextSpan | undefined;
|
|
64
|
+
abstract isReady(): boolean;
|
|
65
|
+
}
|
|
66
|
+
declare global {
|
|
67
|
+
var RAINDROP_CONTEXT_MANAGER: (new () => ContextManager) | undefined;
|
|
68
|
+
var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The Raindrop control handle exposed alongside the OpenCode hooks.
|
|
73
|
+
*
|
|
74
|
+
* The plugin's public surface is the framework `Hooks` object, which OpenCode
|
|
75
|
+
* consumes and whose keyspace it owns. Rather than pollute that keyspace with a
|
|
76
|
+
* top-level `users` (as the client-factory wrappers do), the identify surface
|
|
77
|
+
* lives under a namespaced `raindrop` handle — mirroring `@raindrop-ai/eve`'s
|
|
78
|
+
* `raindrop` handle precedent — so application/tool code can attach user traits
|
|
79
|
+
* without waiting for a session hook to fire.
|
|
80
|
+
*/
|
|
81
|
+
interface RaindropOpencodeHandle {
|
|
82
|
+
users: {
|
|
83
|
+
/**
|
|
84
|
+
* Attach traits to one or more users (posts to `users/identify`). Buffered
|
|
85
|
+
* in the shared EventShipper and delivered on the next {@link flush} / span
|
|
86
|
+
* flush, so pair a bare identify with `flush()` when there is no active
|
|
87
|
+
* OpenCode session driving delivery.
|
|
88
|
+
*/
|
|
89
|
+
identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
/** Drain the buffered event + trace shippers. */
|
|
92
|
+
flush(): Promise<void>;
|
|
93
|
+
/** Flush and tear down the shippers (also wired into session teardown). */
|
|
94
|
+
shutdown(): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The plugin's return value: the OpenCode {@link Hooks} plus the additive
|
|
98
|
+
* {@link RaindropOpencodeHandle}. `raindrop` is optional because the plugin
|
|
99
|
+
* returns bare `{}` (no handle) when tracing is disabled — no write key and no
|
|
100
|
+
* local Workshop destination.
|
|
101
|
+
*/
|
|
102
|
+
type RaindropOpencodeHooks = Hooks & {
|
|
103
|
+
raindrop?: RaindropOpencodeHandle;
|
|
104
|
+
};
|
|
3
105
|
/**
|
|
4
106
|
* Raindrop plugin for OpenCode.
|
|
5
107
|
*
|
|
@@ -12,6 +114,6 @@ import { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
|
12
114
|
*
|
|
13
115
|
* Optional config file: ~/.config/opencode/raindrop.json or .opencode/raindrop.json
|
|
14
116
|
*/
|
|
15
|
-
declare function plugin(input: PluginInput): Promise<
|
|
117
|
+
declare function plugin(input: PluginInput): Promise<RaindropOpencodeHooks>;
|
|
16
118
|
|
|
17
|
-
export { plugin as default };
|
|
119
|
+
export { type IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, plugin as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,107 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Hooks, PluginInput } from '@opencode-ai/plugin';
|
|
2
2
|
|
|
3
|
+
type IdentifyInput = {
|
|
4
|
+
userId: string;
|
|
5
|
+
traits?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run telemetry egress with OpenTelemetry tracing suppressed.
|
|
10
|
+
*
|
|
11
|
+
* Why this exists
|
|
12
|
+
* ---------------
|
|
13
|
+
* Raindrop integrations ship spans/events over HTTP with the global `fetch`
|
|
14
|
+
* (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
|
|
15
|
+
* fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
|
|
16
|
+
* every Eve agent installs — that instrumentation wraps *our own* telemetry
|
|
17
|
+
* POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
|
|
18
|
+
* very exporter that issued the request, so they get shipped right back to
|
|
19
|
+
* Raindrop and Workshop as standalone "runs" (and, because each export issues
|
|
20
|
+
* another fetch, they feed back on themselves). The result is a run list
|
|
21
|
+
* flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
|
|
22
|
+
* `.../live` entries that drown out the real agent turns — especially with
|
|
23
|
+
* sub-agents, where every sandbox runs its own instrumentation.
|
|
24
|
+
*
|
|
25
|
+
* The OTel-blessed fix is to mark the active context as "tracing suppressed"
|
|
26
|
+
* around the request; instrumentations check `isTracingSuppressed` and return
|
|
27
|
+
* a no-op span instead of recording one. We do this through a hook stashed on
|
|
28
|
+
* `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
|
|
29
|
+
* - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
|
|
30
|
+
* hard-depends on them, and the hook is simply absent when they (and thus
|
|
31
|
+
* any instrumentation to suppress) are not installed; and
|
|
32
|
+
* - the browser bundle never pulls in `node:module`, mirroring how core
|
|
33
|
+
* injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
|
|
34
|
+
*
|
|
35
|
+
* When no hook is present the callback runs unchanged, so suppression is a
|
|
36
|
+
* best-effort no-op rather than a hard requirement.
|
|
37
|
+
*/
|
|
38
|
+
/** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
|
|
39
|
+
type SuppressTracingHook = <T>(fn: () => T) => T;
|
|
40
|
+
declare global {
|
|
41
|
+
var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type ParentSpanContext = {
|
|
45
|
+
traceIdB64: string;
|
|
46
|
+
spanIdB64: string;
|
|
47
|
+
eventId: string;
|
|
48
|
+
};
|
|
49
|
+
interface ContextSpan {
|
|
50
|
+
readonly traceIdB64: string;
|
|
51
|
+
readonly spanIdB64: string;
|
|
52
|
+
readonly eventId: string;
|
|
53
|
+
log?(data: Record<string, unknown>): void;
|
|
54
|
+
}
|
|
55
|
+
interface AsyncLocalStorageLike<T> {
|
|
56
|
+
getStore(): T | undefined;
|
|
57
|
+
run<R>(store: T, callback: () => R): R;
|
|
58
|
+
enterWith?(store: T): void;
|
|
59
|
+
}
|
|
60
|
+
declare abstract class ContextManager {
|
|
61
|
+
abstract getParentSpanIds(): ParentSpanContext | undefined;
|
|
62
|
+
abstract runInContext<R>(span: ContextSpan, callback: () => R): R;
|
|
63
|
+
abstract getCurrentSpan(): ContextSpan | undefined;
|
|
64
|
+
abstract isReady(): boolean;
|
|
65
|
+
}
|
|
66
|
+
declare global {
|
|
67
|
+
var RAINDROP_CONTEXT_MANAGER: (new () => ContextManager) | undefined;
|
|
68
|
+
var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The Raindrop control handle exposed alongside the OpenCode hooks.
|
|
73
|
+
*
|
|
74
|
+
* The plugin's public surface is the framework `Hooks` object, which OpenCode
|
|
75
|
+
* consumes and whose keyspace it owns. Rather than pollute that keyspace with a
|
|
76
|
+
* top-level `users` (as the client-factory wrappers do), the identify surface
|
|
77
|
+
* lives under a namespaced `raindrop` handle — mirroring `@raindrop-ai/eve`'s
|
|
78
|
+
* `raindrop` handle precedent — so application/tool code can attach user traits
|
|
79
|
+
* without waiting for a session hook to fire.
|
|
80
|
+
*/
|
|
81
|
+
interface RaindropOpencodeHandle {
|
|
82
|
+
users: {
|
|
83
|
+
/**
|
|
84
|
+
* Attach traits to one or more users (posts to `users/identify`). Buffered
|
|
85
|
+
* in the shared EventShipper and delivered on the next {@link flush} / span
|
|
86
|
+
* flush, so pair a bare identify with `flush()` when there is no active
|
|
87
|
+
* OpenCode session driving delivery.
|
|
88
|
+
*/
|
|
89
|
+
identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
/** Drain the buffered event + trace shippers. */
|
|
92
|
+
flush(): Promise<void>;
|
|
93
|
+
/** Flush and tear down the shippers (also wired into session teardown). */
|
|
94
|
+
shutdown(): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The plugin's return value: the OpenCode {@link Hooks} plus the additive
|
|
98
|
+
* {@link RaindropOpencodeHandle}. `raindrop` is optional because the plugin
|
|
99
|
+
* returns bare `{}` (no handle) when tracing is disabled — no write key and no
|
|
100
|
+
* local Workshop destination.
|
|
101
|
+
*/
|
|
102
|
+
type RaindropOpencodeHooks = Hooks & {
|
|
103
|
+
raindrop?: RaindropOpencodeHandle;
|
|
104
|
+
};
|
|
3
105
|
/**
|
|
4
106
|
* Raindrop plugin for OpenCode.
|
|
5
107
|
*
|
|
@@ -12,6 +114,6 @@ import { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
|
12
114
|
*
|
|
13
115
|
* Optional config file: ~/.config/opencode/raindrop.json or .opencode/raindrop.json
|
|
14
116
|
*/
|
|
15
|
-
declare function plugin(input: PluginInput): Promise<
|
|
117
|
+
declare function plugin(input: PluginInput): Promise<RaindropOpencodeHooks>;
|
|
16
118
|
|
|
17
|
-
export { plugin as default };
|
|
119
|
+
export { type IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, plugin as default };
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
// ../core/dist/chunk-
|
|
8
|
+
// ../core/dist/chunk-QTUH3BHZ.js
|
|
9
9
|
function getCrypto() {
|
|
10
10
|
const c = globalThis.crypto;
|
|
11
11
|
return c;
|
|
@@ -20,6 +20,17 @@ function randomBytes(length) {
|
|
|
20
20
|
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
21
21
|
return out;
|
|
22
22
|
}
|
|
23
|
+
function randomUUID() {
|
|
24
|
+
const cryptoObj = getCrypto();
|
|
25
|
+
if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
|
|
26
|
+
return cryptoObj.randomUUID();
|
|
27
|
+
}
|
|
28
|
+
const b = randomBytes(16);
|
|
29
|
+
b[6] = b[6] & 15 | 64;
|
|
30
|
+
b[8] = b[8] & 63 | 128;
|
|
31
|
+
const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
32
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
33
|
+
}
|
|
23
34
|
function base64Encode(bytes) {
|
|
24
35
|
const maybeBuffer = globalThis.Buffer;
|
|
25
36
|
if (maybeBuffer) {
|
|
@@ -50,7 +61,7 @@ function base64Encode(bytes) {
|
|
|
50
61
|
return out;
|
|
51
62
|
}
|
|
52
63
|
function generateId() {
|
|
53
|
-
return
|
|
64
|
+
return randomUUID();
|
|
54
65
|
}
|
|
55
66
|
function runWithTracingSuppressed(fn) {
|
|
56
67
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
@@ -387,16 +398,52 @@ function projectIdHeaders(projectId) {
|
|
|
387
398
|
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
388
399
|
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
389
400
|
function mergePatches(target, source) {
|
|
390
|
-
var _a, _b, _c, _d;
|
|
401
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
391
402
|
const out = { ...target, ...source };
|
|
392
403
|
if (target.properties || source.properties) {
|
|
393
404
|
out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
|
|
394
405
|
}
|
|
406
|
+
if (target.usage || source.usage) {
|
|
407
|
+
out.usage = { ...(_c = target.usage) != null ? _c : {} };
|
|
408
|
+
if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
|
|
409
|
+
out.usage.promptTokens = source.usage.promptTokens;
|
|
410
|
+
}
|
|
411
|
+
if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
|
|
412
|
+
out.usage.completionTokens = source.usage.completionTokens;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (source.error === void 0) {
|
|
416
|
+
out.error = target.error;
|
|
417
|
+
}
|
|
418
|
+
if (target.featureFlags || source.featureFlags) {
|
|
419
|
+
out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
|
|
420
|
+
}
|
|
395
421
|
if (target.attachments || source.attachments) {
|
|
396
|
-
out.attachments = [...(
|
|
422
|
+
out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
|
|
397
423
|
}
|
|
398
424
|
return out;
|
|
399
425
|
}
|
|
426
|
+
function isErrorLike(value) {
|
|
427
|
+
return value !== null && typeof value === "object";
|
|
428
|
+
}
|
|
429
|
+
function eventTelemetryProperties(patch) {
|
|
430
|
+
var _a, _b;
|
|
431
|
+
const properties = {};
|
|
432
|
+
if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
|
|
433
|
+
properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
|
|
434
|
+
}
|
|
435
|
+
if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
|
|
436
|
+
properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
|
|
437
|
+
}
|
|
438
|
+
if (patch.error !== void 0 && patch.error !== null) {
|
|
439
|
+
const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
|
|
440
|
+
const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
|
|
441
|
+
const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
|
|
442
|
+
properties["error.type"] = errorType;
|
|
443
|
+
properties["error.message"] = errorMessage;
|
|
444
|
+
}
|
|
445
|
+
return properties;
|
|
446
|
+
}
|
|
400
447
|
var EventShipper = class {
|
|
401
448
|
constructor(opts) {
|
|
402
449
|
this.buffers = /* @__PURE__ */ new Map();
|
|
@@ -645,6 +692,11 @@ var EventShipper = class {
|
|
|
645
692
|
return;
|
|
646
693
|
}
|
|
647
694
|
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
695
|
+
let telemetryProperties = {};
|
|
696
|
+
try {
|
|
697
|
+
telemetryProperties = eventTelemetryProperties(accumulated);
|
|
698
|
+
} catch (e) {
|
|
699
|
+
}
|
|
648
700
|
const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
|
|
649
701
|
const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
|
|
650
702
|
const payload = {
|
|
@@ -654,15 +706,17 @@ var EventShipper = class {
|
|
|
654
706
|
timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
|
|
655
707
|
ai_data: {
|
|
656
708
|
input: accumulated.input,
|
|
657
|
-
output: accumulated.output,
|
|
709
|
+
output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
|
|
658
710
|
model: accumulated.model,
|
|
659
711
|
convo_id: convoId
|
|
660
712
|
},
|
|
661
713
|
properties: {
|
|
662
714
|
...restProperties,
|
|
715
|
+
...telemetryProperties,
|
|
663
716
|
...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
|
|
664
717
|
$context: this.context
|
|
665
718
|
},
|
|
719
|
+
...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
|
|
666
720
|
attachments: accumulated.attachments,
|
|
667
721
|
is_pending: isPending
|
|
668
722
|
};
|
|
@@ -816,6 +870,13 @@ function redactJsonAttributeValue(key, value) {
|
|
|
816
870
|
if (scrubbedJson === json) return void 0;
|
|
817
871
|
return { stringValue: scrubbedJson };
|
|
818
872
|
}
|
|
873
|
+
function identityAttrs(args) {
|
|
874
|
+
return [
|
|
875
|
+
attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
|
|
876
|
+
attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
|
|
877
|
+
attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
|
|
878
|
+
];
|
|
879
|
+
}
|
|
819
880
|
function applyOtelSpanAttributeLimit(limit) {
|
|
820
881
|
var _a, _b;
|
|
821
882
|
try {
|
|
@@ -940,6 +1001,7 @@ var TraceShipper = class {
|
|
|
940
1001
|
const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
|
|
941
1002
|
const attrs = [
|
|
942
1003
|
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
1004
|
+
...identityAttrs(args),
|
|
943
1005
|
attrString("ai.operationId", args.operationId)
|
|
944
1006
|
];
|
|
945
1007
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
@@ -995,7 +1057,8 @@ var TraceShipper = class {
|
|
|
995
1057
|
var _a;
|
|
996
1058
|
const ids = createSpanIds(args.parent);
|
|
997
1059
|
const attrs = [
|
|
998
|
-
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
1060
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
|
|
1061
|
+
...identityAttrs(args)
|
|
999
1062
|
];
|
|
1000
1063
|
if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
|
|
1001
1064
|
const otlp = buildOtlpSpan({
|
|
@@ -1214,7 +1277,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1214
1277
|
// package.json
|
|
1215
1278
|
var package_default = {
|
|
1216
1279
|
name: "@raindrop-ai/opencode-plugin",
|
|
1217
|
-
version: "0.0
|
|
1280
|
+
version: "0.1.0",
|
|
1218
1281
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1219
1282
|
type: "module",
|
|
1220
1283
|
main: "dist/index.js",
|
|
@@ -1599,7 +1662,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1599
1662
|
// event — session lifecycle + streaming parts
|
|
1600
1663
|
// ------------------------------------------------------------------
|
|
1601
1664
|
event: async ({ event }) => {
|
|
1602
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1665
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
1603
1666
|
try {
|
|
1604
1667
|
const props = event.properties;
|
|
1605
1668
|
const info = props["info"];
|
|
@@ -1871,7 +1934,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1871
1934
|
}
|
|
1872
1935
|
state.currentRootSpan = void 0;
|
|
1873
1936
|
const hasAssistantResponse = outputText.trim().length > 0;
|
|
1874
|
-
if (!hasAssistantResponse) {
|
|
1937
|
+
if (!hasAssistantResponse && !msgError) {
|
|
1875
1938
|
await traceShipper.flush();
|
|
1876
1939
|
state.currentEventId = void 0;
|
|
1877
1940
|
return;
|
|
@@ -1879,11 +1942,20 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1879
1942
|
if (!state.parentId) {
|
|
1880
1943
|
await eventShipper.finish(state.currentEventId, {
|
|
1881
1944
|
userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
|
|
1882
|
-
model:
|
|
1883
|
-
output: outputText,
|
|
1945
|
+
model: modelID,
|
|
1946
|
+
output: msgError ? void 0 : outputText,
|
|
1947
|
+
usage: {
|
|
1948
|
+
promptTokens: inputTokens,
|
|
1949
|
+
completionTokens: outputTokens
|
|
1950
|
+
},
|
|
1951
|
+
error: msgError ? {
|
|
1952
|
+
name: msgError.name,
|
|
1953
|
+
message: (_g = (_f = (_e = msgError.data) == null ? void 0 : _e.message) != null ? _f : msgError.name) != null ? _g : "OpenCode request failed"
|
|
1954
|
+
} : void 0,
|
|
1884
1955
|
properties: {
|
|
1885
1956
|
plugin_version: PLUGIN_VERSION,
|
|
1886
1957
|
message_id: messageId,
|
|
1958
|
+
"ai.provider": providerID,
|
|
1887
1959
|
...isCompaction ? { is_compaction: true } : {}
|
|
1888
1960
|
}
|
|
1889
1961
|
});
|
|
@@ -1923,7 +1995,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1923
1995
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
1924
1996
|
sessions.delete(String(sessionID));
|
|
1925
1997
|
} else if (event.type === "session.error") {
|
|
1926
|
-
const errorSessionID = (
|
|
1998
|
+
const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
|
|
1927
1999
|
if (errorSessionID === void 0 || errorSessionID === "")
|
|
1928
2000
|
throw new Error("session.error: missing sessionID");
|
|
1929
2001
|
const state = sessions.get(String(errorSessionID));
|
|
@@ -1931,7 +2003,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1931
2003
|
const errorObj = props["error"];
|
|
1932
2004
|
if (errorObj == null) throw new Error("session.error: missing error object");
|
|
1933
2005
|
const errorName = errorObj.name;
|
|
1934
|
-
const errorMessage = (
|
|
2006
|
+
const errorMessage = (_j = (_i = errorObj.data) == null ? void 0 : _i.message) != null ? _j : errorObj.name;
|
|
1935
2007
|
if (errorName == null && errorMessage == null)
|
|
1936
2008
|
throw new Error("session.error: error object must have name or data.message");
|
|
1937
2009
|
const errorStr = `${errorMessage != null ? errorMessage : errorName}
|
|
@@ -1991,7 +2063,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1991
2063
|
promptMeta.properties = m["properties"];
|
|
1992
2064
|
}
|
|
1993
2065
|
} else if (part["type"] === "file") {
|
|
1994
|
-
const mimeType = part["
|
|
2066
|
+
const mimeType = part["mime"];
|
|
1995
2067
|
const filename = part["filename"];
|
|
1996
2068
|
const url = part["url"];
|
|
1997
2069
|
if (url && (mimeType == null ? void 0 : mimeType.startsWith("image/"))) {
|
|
@@ -2225,6 +2297,21 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
2225
2297
|
}
|
|
2226
2298
|
|
|
2227
2299
|
// src/index.ts
|
|
2300
|
+
function createRaindropHandle(eventShipper, traceShipper) {
|
|
2301
|
+
return {
|
|
2302
|
+
users: {
|
|
2303
|
+
async identify(users) {
|
|
2304
|
+
await eventShipper.identify(users);
|
|
2305
|
+
}
|
|
2306
|
+
},
|
|
2307
|
+
async flush() {
|
|
2308
|
+
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
2309
|
+
},
|
|
2310
|
+
async shutdown() {
|
|
2311
|
+
await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
|
|
2312
|
+
}
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2228
2315
|
async function plugin(input) {
|
|
2229
2316
|
var _a;
|
|
2230
2317
|
const config = loadConfig(input.directory);
|
|
@@ -2270,7 +2357,7 @@ async function plugin(input) {
|
|
|
2270
2357
|
eventShipper,
|
|
2271
2358
|
traceShipper
|
|
2272
2359
|
);
|
|
2273
|
-
return hooks;
|
|
2360
|
+
return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper) };
|
|
2274
2361
|
}
|
|
2275
2362
|
export {
|
|
2276
2363
|
plugin as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/opencode-plugin",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@types/node": "^20.11.17",
|
|
38
38
|
"tsup": "^8.4.0",
|
|
39
39
|
"typescript": "^5.3.3",
|
|
40
|
-
"@raindrop-ai/core": "0.
|
|
40
|
+
"@raindrop-ai/core": "0.1.3"
|
|
41
41
|
},
|
|
42
42
|
"tsup": {
|
|
43
43
|
"entry": [
|