@raindrop-ai/opencode-plugin 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +175 -85
- package/dist/index.d.cts +2 -34
- package/dist/index.d.ts +2 -34
- package/dist/index.js +175 -85
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -47,7 +47,7 @@ function loadConfig(projectDirectory) {
|
|
|
47
47
|
}
|
|
48
48
|
return {
|
|
49
49
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
|
|
50
|
-
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai",
|
|
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
52
|
userId: merged.user_id,
|
|
53
53
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
|
|
@@ -100,6 +100,9 @@ var SpanStatusCode = {
|
|
|
100
100
|
OK: 1,
|
|
101
101
|
ERROR: 2
|
|
102
102
|
};
|
|
103
|
+
function generateId() {
|
|
104
|
+
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
105
|
+
}
|
|
103
106
|
function createSpanIds(parent) {
|
|
104
107
|
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
105
108
|
const spanId = base64Encode(randomBytes(8));
|
|
@@ -428,6 +431,27 @@ var TraceShipper = class {
|
|
|
428
431
|
});
|
|
429
432
|
this.enqueue(otlp);
|
|
430
433
|
}
|
|
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
|
+
createSpan(args) {
|
|
439
|
+
var _a;
|
|
440
|
+
const ids = createSpanIds(args.parent);
|
|
441
|
+
const attrs = [
|
|
442
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
443
|
+
];
|
|
444
|
+
if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
|
|
445
|
+
const otlp = buildOtlpSpan({
|
|
446
|
+
ids,
|
|
447
|
+
name: args.name,
|
|
448
|
+
startTimeUnixNano: args.startTimeUnixNano,
|
|
449
|
+
endTimeUnixNano: args.endTimeUnixNano,
|
|
450
|
+
attributes: attrs,
|
|
451
|
+
status: args.status
|
|
452
|
+
});
|
|
453
|
+
this.enqueue(otlp);
|
|
454
|
+
}
|
|
431
455
|
enqueue(span) {
|
|
432
456
|
if (this.queue.length >= this.maxQueueSize) this.queue.shift();
|
|
433
457
|
this.queue.push(span);
|
|
@@ -526,6 +550,8 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
526
550
|
})) == null ? void 0 : _a.catch) == null ? void 0 : _b.call(_a, () => {
|
|
527
551
|
});
|
|
528
552
|
}
|
|
553
|
+
const hostname = getHostname();
|
|
554
|
+
const os = process.platform;
|
|
529
555
|
log("Raindrop tracing hooks registered");
|
|
530
556
|
return {
|
|
531
557
|
// ------------------------------------------------------------------
|
|
@@ -545,21 +571,9 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
545
571
|
return;
|
|
546
572
|
}
|
|
547
573
|
const userId = (_a = config.userId) != null ? _a : sessionID;
|
|
548
|
-
const hostname = getHostname();
|
|
549
|
-
const os = process.platform;
|
|
550
|
-
const rootSpan = traceShipper.startSpan({
|
|
551
|
-
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
552
|
-
eventId: sessionID,
|
|
553
|
-
attributes: [
|
|
554
|
-
attrString("workspace", worktree),
|
|
555
|
-
attrString("directory", directory),
|
|
556
|
-
attrString("hostname", hostname),
|
|
557
|
-
attrString("os", os)
|
|
558
|
-
]
|
|
559
|
-
});
|
|
560
574
|
const state = {
|
|
561
|
-
|
|
562
|
-
|
|
575
|
+
sessionId: sessionID,
|
|
576
|
+
userId,
|
|
563
577
|
turnNumber: 0,
|
|
564
578
|
toolCount: 0,
|
|
565
579
|
startTime: Date.now(),
|
|
@@ -569,26 +583,22 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
569
583
|
reasoningParts: /* @__PURE__ */ new Map(),
|
|
570
584
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
571
585
|
toolStartTimes: /* @__PURE__ */ new Map(),
|
|
572
|
-
|
|
586
|
+
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
573
587
|
toolCallMessageIds: /* @__PURE__ */ new Map(),
|
|
574
588
|
processedMessages: /* @__PURE__ */ new Set(),
|
|
575
589
|
attachments: []
|
|
576
590
|
};
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
hostname,
|
|
587
|
-
os,
|
|
588
|
-
turn_count: 0,
|
|
589
|
-
tool_count: 0
|
|
590
|
-
}
|
|
591
|
+
state.currentRootSpan = traceShipper.startSpan({
|
|
592
|
+
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
593
|
+
eventId: sessionID,
|
|
594
|
+
attributes: [
|
|
595
|
+
attrString("workspace", worktree),
|
|
596
|
+
attrString("directory", directory),
|
|
597
|
+
attrString("hostname", hostname),
|
|
598
|
+
attrString("os", os)
|
|
599
|
+
]
|
|
591
600
|
});
|
|
601
|
+
sessions.set(sessionID, state);
|
|
592
602
|
log("Session created", { sessionID, userId });
|
|
593
603
|
} else if (event.type === "message.part.updated") {
|
|
594
604
|
const part = props["part"];
|
|
@@ -632,7 +642,7 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
632
642
|
const time = msgInfo["time"];
|
|
633
643
|
if (!msgSessionID || !messageId || !(time == null ? void 0 : time["completed"])) return;
|
|
634
644
|
const state = sessions.get(msgSessionID);
|
|
635
|
-
if (!state || !state.currentTurnSpan) return;
|
|
645
|
+
if (!state || !state.currentEventId || !state.currentTurnSpan) return;
|
|
636
646
|
if (state.processedMessages.has(messageId)) return;
|
|
637
647
|
state.processedMessages.add(messageId);
|
|
638
648
|
const tokens = msgInfo["tokens"];
|
|
@@ -657,7 +667,7 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
657
667
|
const llmSpan = traceShipper.startSpan({
|
|
658
668
|
name: modelName,
|
|
659
669
|
parent: state.currentTurnSpan.ids,
|
|
660
|
-
eventId: state.
|
|
670
|
+
eventId: state.currentEventId,
|
|
661
671
|
attributes: llmAttrs
|
|
662
672
|
});
|
|
663
673
|
if (typeof time["created"] === "number") {
|
|
@@ -668,11 +678,17 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
668
678
|
|
|
669
679
|
type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
670
680
|
traceShipper.endSpan(llmSpan, { error: errorForSpan });
|
|
671
|
-
await eventShipper.
|
|
681
|
+
await eventShipper.finish(state.currentEventId, {
|
|
682
|
+
userId: state.userId,
|
|
672
683
|
model: modelName,
|
|
673
|
-
output: outputText || void 0
|
|
684
|
+
output: outputText || void 0,
|
|
685
|
+
properties: {
|
|
686
|
+
turn_count: state.turnNumber,
|
|
687
|
+
tool_count: state.toolCount
|
|
688
|
+
}
|
|
674
689
|
});
|
|
675
|
-
|
|
690
|
+
await traceShipper.flush();
|
|
691
|
+
log("LLM span created, turn/root close on session.idle", {
|
|
676
692
|
messageId,
|
|
677
693
|
modelName,
|
|
678
694
|
inputTokens,
|
|
@@ -687,14 +703,23 @@ type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
|
687
703
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
688
704
|
state.currentTurnSpan = void 0;
|
|
689
705
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
706
|
+
if (state.currentRootSpan) {
|
|
707
|
+
traceShipper.endSpan(state.currentRootSpan);
|
|
708
|
+
state.currentRootSpan = void 0;
|
|
709
|
+
}
|
|
710
|
+
if (state.currentEventId) {
|
|
711
|
+
await eventShipper.finish(state.currentEventId, {
|
|
712
|
+
userId: state.userId,
|
|
713
|
+
output: state.currentOutput || void 0,
|
|
714
|
+
properties: {
|
|
715
|
+
turn_count: state.turnNumber,
|
|
716
|
+
tool_count: state.toolCount
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
state.currentEventId = void 0;
|
|
720
|
+
}
|
|
721
|
+
await traceShipper.flush();
|
|
722
|
+
log("Session idle \u2014 turn and root closed", { sessionID });
|
|
698
723
|
} else if (event.type === "session.deleted") {
|
|
699
724
|
if (!sessionID) return;
|
|
700
725
|
const state = sessions.get(String(sessionID));
|
|
@@ -703,15 +728,22 @@ type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
|
703
728
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
704
729
|
state.currentTurnSpan = void 0;
|
|
705
730
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
731
|
+
if (state.currentRootSpan) {
|
|
732
|
+
traceShipper.endSpan(state.currentRootSpan);
|
|
733
|
+
state.currentRootSpan = void 0;
|
|
734
|
+
}
|
|
735
|
+
if (state.currentEventId) {
|
|
736
|
+
await eventShipper.finish(state.currentEventId, {
|
|
737
|
+
userId: state.userId,
|
|
738
|
+
output: state.currentOutput || void 0,
|
|
739
|
+
model: state.model,
|
|
740
|
+
properties: {
|
|
741
|
+
turn_count: state.turnNumber,
|
|
742
|
+
tool_count: state.toolCount
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
state.currentEventId = void 0;
|
|
746
|
+
}
|
|
715
747
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
716
748
|
sessions.delete(String(sessionID));
|
|
717
749
|
log("Session deleted \u2014 flushed", { sessionID });
|
|
@@ -730,14 +762,21 @@ type: ${errorName}`;
|
|
|
730
762
|
traceShipper.endSpan(state.currentTurnSpan, { error: errorStr });
|
|
731
763
|
state.currentTurnSpan = void 0;
|
|
732
764
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
765
|
+
if (state.currentRootSpan) {
|
|
766
|
+
traceShipper.endSpan(state.currentRootSpan, { error: errorStr });
|
|
767
|
+
state.currentRootSpan = void 0;
|
|
768
|
+
}
|
|
769
|
+
if (state.currentEventId) {
|
|
770
|
+
await eventShipper.finish(state.currentEventId, {
|
|
771
|
+
userId: state.userId,
|
|
772
|
+
properties: {
|
|
773
|
+
error: errorStr,
|
|
774
|
+
turn_count: state.turnNumber,
|
|
775
|
+
tool_count: state.toolCount
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
state.currentEventId = void 0;
|
|
779
|
+
}
|
|
741
780
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
742
781
|
sessions.delete(String(errorSessionID));
|
|
743
782
|
log("Session error handled", { errorSessionID, errorName });
|
|
@@ -756,21 +795,53 @@ type: ${errorName}`;
|
|
|
756
795
|
}
|
|
757
796
|
},
|
|
758
797
|
// ------------------------------------------------------------------
|
|
759
|
-
// chat.message — user sent a message; start a new turn
|
|
798
|
+
// chat.message — user sent a message; start a new turn with a fresh event ID
|
|
760
799
|
// ------------------------------------------------------------------
|
|
761
800
|
"chat.message": async (messageInput, output) => {
|
|
762
|
-
var _a;
|
|
801
|
+
var _a, _b;
|
|
763
802
|
try {
|
|
764
803
|
const { sessionID } = messageInput;
|
|
765
|
-
|
|
766
|
-
if (!state)
|
|
804
|
+
let state = sessions.get(sessionID);
|
|
805
|
+
if (!state) {
|
|
806
|
+
state = {
|
|
807
|
+
sessionId: sessionID,
|
|
808
|
+
userId: (_a = config.userId) != null ? _a : sessionID,
|
|
809
|
+
turnNumber: 0,
|
|
810
|
+
toolCount: 0,
|
|
811
|
+
startTime: Date.now(),
|
|
812
|
+
currentInput: "",
|
|
813
|
+
currentOutput: "",
|
|
814
|
+
outputParts: /* @__PURE__ */ new Map(),
|
|
815
|
+
reasoningParts: /* @__PURE__ */ new Map(),
|
|
816
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
817
|
+
toolStartTimes: /* @__PURE__ */ new Map(),
|
|
818
|
+
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
819
|
+
toolCallMessageIds: /* @__PURE__ */ new Map(),
|
|
820
|
+
processedMessages: /* @__PURE__ */ new Set(),
|
|
821
|
+
attachments: []
|
|
822
|
+
};
|
|
823
|
+
sessions.set(sessionID, state);
|
|
824
|
+
}
|
|
767
825
|
if (state.currentTurnSpan) {
|
|
768
826
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
769
827
|
state.currentTurnSpan = void 0;
|
|
770
828
|
}
|
|
829
|
+
if (!state.currentRootSpan) {
|
|
830
|
+
state.currentRootSpan = traceShipper.startSpan({
|
|
831
|
+
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
832
|
+
eventId: sessionID,
|
|
833
|
+
attributes: [
|
|
834
|
+
attrString("workspace", worktree),
|
|
835
|
+
attrString("directory", directory),
|
|
836
|
+
attrString("hostname", hostname),
|
|
837
|
+
attrString("os", os)
|
|
838
|
+
]
|
|
839
|
+
});
|
|
840
|
+
}
|
|
771
841
|
state.turnNumber++;
|
|
772
842
|
state.currentOutput = "";
|
|
773
|
-
|
|
843
|
+
state.currentEventId = generateId();
|
|
844
|
+
const parts = (_b = output == null ? void 0 : output.parts) != null ? _b : [];
|
|
774
845
|
const textParts = [];
|
|
775
846
|
const newAttachments = [];
|
|
776
847
|
for (const part of parts) {
|
|
@@ -790,17 +861,29 @@ type: ${errorName}`;
|
|
|
790
861
|
if (newAttachments.length > 0) state.attachments.push(...newAttachments);
|
|
791
862
|
state.currentTurnSpan = traceShipper.startSpan({
|
|
792
863
|
name: `Turn ${state.turnNumber}`,
|
|
793
|
-
parent: state.
|
|
794
|
-
eventId: state.
|
|
864
|
+
parent: state.currentRootSpan.ids,
|
|
865
|
+
eventId: state.currentEventId,
|
|
795
866
|
attributes: [attrInt("turn.number", state.turnNumber)]
|
|
796
867
|
});
|
|
797
|
-
await eventShipper.patch(state.
|
|
868
|
+
await eventShipper.patch(state.currentEventId, {
|
|
869
|
+
isPending: true,
|
|
870
|
+
userId: state.userId,
|
|
871
|
+
convoId: state.sessionId,
|
|
872
|
+
eventName: config.eventName,
|
|
798
873
|
input: userText || void 0,
|
|
799
874
|
attachments: newAttachments.length > 0 ? newAttachments : void 0,
|
|
800
|
-
properties: {
|
|
875
|
+
properties: {
|
|
876
|
+
workspace: worktree,
|
|
877
|
+
directory,
|
|
878
|
+
hostname,
|
|
879
|
+
os,
|
|
880
|
+
turn_count: state.turnNumber,
|
|
881
|
+
tool_count: state.toolCount
|
|
882
|
+
}
|
|
801
883
|
});
|
|
802
884
|
log("Chat message \u2014 new turn started", {
|
|
803
885
|
sessionID,
|
|
886
|
+
eventId: state.currentEventId,
|
|
804
887
|
turnNumber: state.turnNumber,
|
|
805
888
|
inputLength: userText.length,
|
|
806
889
|
attachments: newAttachments.length
|
|
@@ -810,50 +893,57 @@ type: ${errorName}`;
|
|
|
810
893
|
}
|
|
811
894
|
},
|
|
812
895
|
// ------------------------------------------------------------------
|
|
813
|
-
// tool.execute.before — record start time
|
|
896
|
+
// tool.execute.before — record start time only (span created atomically in after)
|
|
814
897
|
// ------------------------------------------------------------------
|
|
815
898
|
"tool.execute.before": async (toolInput, _output) => {
|
|
816
899
|
try {
|
|
817
900
|
const { tool, sessionID, callID } = toolInput;
|
|
818
901
|
const state = sessions.get(sessionID);
|
|
819
|
-
if (!state || !state.currentTurnSpan) return;
|
|
902
|
+
if (!state || !state.currentTurnSpan || !state.currentEventId) return;
|
|
820
903
|
state.toolStartTimes.set(callID, Date.now());
|
|
821
|
-
|
|
822
|
-
|
|
904
|
+
state.toolSpanStarts.set(callID, {
|
|
905
|
+
startTimeUnixNano: nowUnixNanoString(),
|
|
823
906
|
parent: state.currentTurnSpan.ids,
|
|
824
|
-
eventId: state.
|
|
825
|
-
|
|
907
|
+
eventId: state.currentEventId,
|
|
908
|
+
name: tool
|
|
826
909
|
});
|
|
827
|
-
state.toolSpans.set(callID, toolSpan);
|
|
828
910
|
log("Tool execute before", { tool, sessionID, callID });
|
|
829
911
|
} catch (err) {
|
|
830
912
|
log("Error in tool.execute.before hook", { error: String(err) });
|
|
831
913
|
}
|
|
832
914
|
},
|
|
833
915
|
// ------------------------------------------------------------------
|
|
834
|
-
// tool.execute.after —
|
|
916
|
+
// tool.execute.after — create tool span atomically with both start and end times
|
|
835
917
|
// ------------------------------------------------------------------
|
|
836
918
|
"tool.execute.after": async (toolInput, result) => {
|
|
837
919
|
var _a;
|
|
838
920
|
try {
|
|
839
921
|
const { tool, sessionID, callID } = toolInput;
|
|
840
922
|
const state = sessions.get(sessionID);
|
|
841
|
-
if (!state) return;
|
|
923
|
+
if (!state || !state.currentEventId) return;
|
|
842
924
|
state.toolCount++;
|
|
843
925
|
state.toolStartTimes.delete(callID);
|
|
844
926
|
state.toolCallMessageIds.delete(callID);
|
|
845
|
-
const
|
|
846
|
-
state.
|
|
847
|
-
if (
|
|
927
|
+
const startInfo = state.toolSpanStarts.get(callID);
|
|
928
|
+
state.toolSpanStarts.delete(callID);
|
|
929
|
+
if (startInfo) {
|
|
930
|
+
const endTimeUnixNano = nowUnixNanoString();
|
|
848
931
|
const outputPreview = typeof result.output === "string" ? result.output.slice(0, 200) : JSON.stringify((_a = result.output) != null ? _a : "").slice(0, 200);
|
|
849
|
-
traceShipper.
|
|
932
|
+
traceShipper.createSpan({
|
|
933
|
+
name: startInfo.name,
|
|
934
|
+
parent: startInfo.parent,
|
|
935
|
+
eventId: startInfo.eventId,
|
|
936
|
+
startTimeUnixNano: startInfo.startTimeUnixNano,
|
|
937
|
+
endTimeUnixNano,
|
|
850
938
|
attributes: [
|
|
939
|
+
attrString("tool.name", tool),
|
|
851
940
|
attrString("tool.title", result.title),
|
|
852
941
|
attrString("tool.output_preview", outputPreview)
|
|
853
942
|
]
|
|
854
943
|
});
|
|
855
944
|
}
|
|
856
|
-
await eventShipper.patch(state.
|
|
945
|
+
await eventShipper.patch(state.currentEventId, {
|
|
946
|
+
userId: state.userId,
|
|
857
947
|
properties: { tool_count: state.toolCount }
|
|
858
948
|
});
|
|
859
949
|
log("Tool execute after", { tool, sessionID, callID });
|
|
@@ -885,7 +975,7 @@ async function plugin(input) {
|
|
|
885
975
|
"warn",
|
|
886
976
|
"RAINDROP_WRITE_KEY not set \u2014 Raindrop tracing disabled. Set it in your environment or in ~/.config/opencode/raindrop.json."
|
|
887
977
|
);
|
|
888
|
-
return {
|
|
978
|
+
return {};
|
|
889
979
|
}
|
|
890
980
|
if (config.debug) {
|
|
891
981
|
appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
|
|
@@ -909,5 +999,5 @@ async function plugin(input) {
|
|
|
909
999
|
eventShipper,
|
|
910
1000
|
traceShipper
|
|
911
1001
|
);
|
|
912
|
-
return
|
|
1002
|
+
return hooks;
|
|
913
1003
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginInput } from '@opencode-ai/plugin';
|
|
1
|
+
import { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Raindrop plugin for OpenCode.
|
|
@@ -12,38 +12,6 @@ import { PluginInput } from '@opencode-ai/plugin';
|
|
|
12
12
|
*
|
|
13
13
|
* Optional config file: ~/.config/opencode/raindrop.json or .opencode/raindrop.json
|
|
14
14
|
*/
|
|
15
|
-
declare function plugin(input: PluginInput): Promise<
|
|
16
|
-
hooks: {};
|
|
17
|
-
} | {
|
|
18
|
-
hooks: {
|
|
19
|
-
event: ({ event, }: {
|
|
20
|
-
event: {
|
|
21
|
-
type: string;
|
|
22
|
-
properties: Record<string, unknown>;
|
|
23
|
-
};
|
|
24
|
-
}) => Promise<void>;
|
|
25
|
-
"chat.message": (messageInput: {
|
|
26
|
-
sessionID: string;
|
|
27
|
-
agent?: unknown;
|
|
28
|
-
model?: unknown;
|
|
29
|
-
}, output: {
|
|
30
|
-
parts?: Array<Record<string, unknown>>;
|
|
31
|
-
} | undefined) => Promise<void>;
|
|
32
|
-
"tool.execute.before": (toolInput: {
|
|
33
|
-
tool: string;
|
|
34
|
-
sessionID: string;
|
|
35
|
-
callID: string;
|
|
36
|
-
}, _output: unknown) => Promise<void>;
|
|
37
|
-
"tool.execute.after": (toolInput: {
|
|
38
|
-
tool: string;
|
|
39
|
-
sessionID: string;
|
|
40
|
-
callID: string;
|
|
41
|
-
}, result: {
|
|
42
|
-
output?: unknown;
|
|
43
|
-
metadata?: unknown;
|
|
44
|
-
title?: string;
|
|
45
|
-
}) => Promise<void>;
|
|
46
|
-
};
|
|
47
|
-
}>;
|
|
15
|
+
declare function plugin(input: PluginInput): Promise<Hooks>;
|
|
48
16
|
|
|
49
17
|
export { plugin as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginInput } from '@opencode-ai/plugin';
|
|
1
|
+
import { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Raindrop plugin for OpenCode.
|
|
@@ -12,38 +12,6 @@ import { PluginInput } from '@opencode-ai/plugin';
|
|
|
12
12
|
*
|
|
13
13
|
* Optional config file: ~/.config/opencode/raindrop.json or .opencode/raindrop.json
|
|
14
14
|
*/
|
|
15
|
-
declare function plugin(input: PluginInput): Promise<
|
|
16
|
-
hooks: {};
|
|
17
|
-
} | {
|
|
18
|
-
hooks: {
|
|
19
|
-
event: ({ event, }: {
|
|
20
|
-
event: {
|
|
21
|
-
type: string;
|
|
22
|
-
properties: Record<string, unknown>;
|
|
23
|
-
};
|
|
24
|
-
}) => Promise<void>;
|
|
25
|
-
"chat.message": (messageInput: {
|
|
26
|
-
sessionID: string;
|
|
27
|
-
agent?: unknown;
|
|
28
|
-
model?: unknown;
|
|
29
|
-
}, output: {
|
|
30
|
-
parts?: Array<Record<string, unknown>>;
|
|
31
|
-
} | undefined) => Promise<void>;
|
|
32
|
-
"tool.execute.before": (toolInput: {
|
|
33
|
-
tool: string;
|
|
34
|
-
sessionID: string;
|
|
35
|
-
callID: string;
|
|
36
|
-
}, _output: unknown) => Promise<void>;
|
|
37
|
-
"tool.execute.after": (toolInput: {
|
|
38
|
-
tool: string;
|
|
39
|
-
sessionID: string;
|
|
40
|
-
callID: string;
|
|
41
|
-
}, result: {
|
|
42
|
-
output?: unknown;
|
|
43
|
-
metadata?: unknown;
|
|
44
|
-
title?: string;
|
|
45
|
-
}) => Promise<void>;
|
|
46
|
-
};
|
|
47
|
-
}>;
|
|
15
|
+
declare function plugin(input: PluginInput): Promise<Hooks>;
|
|
48
16
|
|
|
49
17
|
export { plugin as default };
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ function loadConfig(projectDirectory) {
|
|
|
28
28
|
}
|
|
29
29
|
return {
|
|
30
30
|
writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
|
|
31
|
-
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai",
|
|
31
|
+
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
32
32
|
eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
|
|
33
33
|
userId: merged.user_id,
|
|
34
34
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
|
|
@@ -81,6 +81,9 @@ var SpanStatusCode = {
|
|
|
81
81
|
OK: 1,
|
|
82
82
|
ERROR: 2
|
|
83
83
|
};
|
|
84
|
+
function generateId() {
|
|
85
|
+
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
86
|
+
}
|
|
84
87
|
function createSpanIds(parent) {
|
|
85
88
|
const traceId = parent ? parent.traceIdB64 : base64Encode(randomBytes(16));
|
|
86
89
|
const spanId = base64Encode(randomBytes(8));
|
|
@@ -409,6 +412,27 @@ var TraceShipper = class {
|
|
|
409
412
|
});
|
|
410
413
|
this.enqueue(otlp);
|
|
411
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Create and enqueue a span with both start and end times (atomic insert).
|
|
417
|
+
* Used for tool spans and any span whose full duration is known at write time.
|
|
418
|
+
*/
|
|
419
|
+
createSpan(args) {
|
|
420
|
+
var _a;
|
|
421
|
+
const ids = createSpanIds(args.parent);
|
|
422
|
+
const attrs = [
|
|
423
|
+
attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
|
|
424
|
+
];
|
|
425
|
+
if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
|
|
426
|
+
const otlp = buildOtlpSpan({
|
|
427
|
+
ids,
|
|
428
|
+
name: args.name,
|
|
429
|
+
startTimeUnixNano: args.startTimeUnixNano,
|
|
430
|
+
endTimeUnixNano: args.endTimeUnixNano,
|
|
431
|
+
attributes: attrs,
|
|
432
|
+
status: args.status
|
|
433
|
+
});
|
|
434
|
+
this.enqueue(otlp);
|
|
435
|
+
}
|
|
412
436
|
enqueue(span) {
|
|
413
437
|
if (this.queue.length >= this.maxQueueSize) this.queue.shift();
|
|
414
438
|
this.queue.push(span);
|
|
@@ -507,6 +531,8 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
507
531
|
})) == null ? void 0 : _a.catch) == null ? void 0 : _b.call(_a, () => {
|
|
508
532
|
});
|
|
509
533
|
}
|
|
534
|
+
const hostname = getHostname();
|
|
535
|
+
const os = process.platform;
|
|
510
536
|
log("Raindrop tracing hooks registered");
|
|
511
537
|
return {
|
|
512
538
|
// ------------------------------------------------------------------
|
|
@@ -526,21 +552,9 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
526
552
|
return;
|
|
527
553
|
}
|
|
528
554
|
const userId = (_a = config.userId) != null ? _a : sessionID;
|
|
529
|
-
const hostname = getHostname();
|
|
530
|
-
const os = process.platform;
|
|
531
|
-
const rootSpan = traceShipper.startSpan({
|
|
532
|
-
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
533
|
-
eventId: sessionID,
|
|
534
|
-
attributes: [
|
|
535
|
-
attrString("workspace", worktree),
|
|
536
|
-
attrString("directory", directory),
|
|
537
|
-
attrString("hostname", hostname),
|
|
538
|
-
attrString("os", os)
|
|
539
|
-
]
|
|
540
|
-
});
|
|
541
555
|
const state = {
|
|
542
|
-
|
|
543
|
-
|
|
556
|
+
sessionId: sessionID,
|
|
557
|
+
userId,
|
|
544
558
|
turnNumber: 0,
|
|
545
559
|
toolCount: 0,
|
|
546
560
|
startTime: Date.now(),
|
|
@@ -550,26 +564,22 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
550
564
|
reasoningParts: /* @__PURE__ */ new Map(),
|
|
551
565
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
552
566
|
toolStartTimes: /* @__PURE__ */ new Map(),
|
|
553
|
-
|
|
567
|
+
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
554
568
|
toolCallMessageIds: /* @__PURE__ */ new Map(),
|
|
555
569
|
processedMessages: /* @__PURE__ */ new Set(),
|
|
556
570
|
attachments: []
|
|
557
571
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
hostname,
|
|
568
|
-
os,
|
|
569
|
-
turn_count: 0,
|
|
570
|
-
tool_count: 0
|
|
571
|
-
}
|
|
572
|
+
state.currentRootSpan = traceShipper.startSpan({
|
|
573
|
+
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
574
|
+
eventId: sessionID,
|
|
575
|
+
attributes: [
|
|
576
|
+
attrString("workspace", worktree),
|
|
577
|
+
attrString("directory", directory),
|
|
578
|
+
attrString("hostname", hostname),
|
|
579
|
+
attrString("os", os)
|
|
580
|
+
]
|
|
572
581
|
});
|
|
582
|
+
sessions.set(sessionID, state);
|
|
573
583
|
log("Session created", { sessionID, userId });
|
|
574
584
|
} else if (event.type === "message.part.updated") {
|
|
575
585
|
const part = props["part"];
|
|
@@ -613,7 +623,7 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
613
623
|
const time = msgInfo["time"];
|
|
614
624
|
if (!msgSessionID || !messageId || !(time == null ? void 0 : time["completed"])) return;
|
|
615
625
|
const state = sessions.get(msgSessionID);
|
|
616
|
-
if (!state || !state.currentTurnSpan) return;
|
|
626
|
+
if (!state || !state.currentEventId || !state.currentTurnSpan) return;
|
|
617
627
|
if (state.processedMessages.has(messageId)) return;
|
|
618
628
|
state.processedMessages.add(messageId);
|
|
619
629
|
const tokens = msgInfo["tokens"];
|
|
@@ -638,7 +648,7 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
638
648
|
const llmSpan = traceShipper.startSpan({
|
|
639
649
|
name: modelName,
|
|
640
650
|
parent: state.currentTurnSpan.ids,
|
|
641
|
-
eventId: state.
|
|
651
|
+
eventId: state.currentEventId,
|
|
642
652
|
attributes: llmAttrs
|
|
643
653
|
});
|
|
644
654
|
if (typeof time["created"] === "number") {
|
|
@@ -649,11 +659,17 @@ function createHooks(client, config, worktree, directory, eventShipper, traceShi
|
|
|
649
659
|
|
|
650
660
|
type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
651
661
|
traceShipper.endSpan(llmSpan, { error: errorForSpan });
|
|
652
|
-
await eventShipper.
|
|
662
|
+
await eventShipper.finish(state.currentEventId, {
|
|
663
|
+
userId: state.userId,
|
|
653
664
|
model: modelName,
|
|
654
|
-
output: outputText || void 0
|
|
665
|
+
output: outputText || void 0,
|
|
666
|
+
properties: {
|
|
667
|
+
turn_count: state.turnNumber,
|
|
668
|
+
tool_count: state.toolCount
|
|
669
|
+
}
|
|
655
670
|
});
|
|
656
|
-
|
|
671
|
+
await traceShipper.flush();
|
|
672
|
+
log("LLM span created, turn/root close on session.idle", {
|
|
657
673
|
messageId,
|
|
658
674
|
modelName,
|
|
659
675
|
inputTokens,
|
|
@@ -668,14 +684,23 @@ type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
|
668
684
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
669
685
|
state.currentTurnSpan = void 0;
|
|
670
686
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
687
|
+
if (state.currentRootSpan) {
|
|
688
|
+
traceShipper.endSpan(state.currentRootSpan);
|
|
689
|
+
state.currentRootSpan = void 0;
|
|
690
|
+
}
|
|
691
|
+
if (state.currentEventId) {
|
|
692
|
+
await eventShipper.finish(state.currentEventId, {
|
|
693
|
+
userId: state.userId,
|
|
694
|
+
output: state.currentOutput || void 0,
|
|
695
|
+
properties: {
|
|
696
|
+
turn_count: state.turnNumber,
|
|
697
|
+
tool_count: state.toolCount
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
state.currentEventId = void 0;
|
|
701
|
+
}
|
|
702
|
+
await traceShipper.flush();
|
|
703
|
+
log("Session idle \u2014 turn and root closed", { sessionID });
|
|
679
704
|
} else if (event.type === "session.deleted") {
|
|
680
705
|
if (!sessionID) return;
|
|
681
706
|
const state = sessions.get(String(sessionID));
|
|
@@ -684,15 +709,22 @@ type: ${(_f = msgError.name) != null ? _f : "UnknownError"}` : void 0;
|
|
|
684
709
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
685
710
|
state.currentTurnSpan = void 0;
|
|
686
711
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
712
|
+
if (state.currentRootSpan) {
|
|
713
|
+
traceShipper.endSpan(state.currentRootSpan);
|
|
714
|
+
state.currentRootSpan = void 0;
|
|
715
|
+
}
|
|
716
|
+
if (state.currentEventId) {
|
|
717
|
+
await eventShipper.finish(state.currentEventId, {
|
|
718
|
+
userId: state.userId,
|
|
719
|
+
output: state.currentOutput || void 0,
|
|
720
|
+
model: state.model,
|
|
721
|
+
properties: {
|
|
722
|
+
turn_count: state.turnNumber,
|
|
723
|
+
tool_count: state.toolCount
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
state.currentEventId = void 0;
|
|
727
|
+
}
|
|
696
728
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
697
729
|
sessions.delete(String(sessionID));
|
|
698
730
|
log("Session deleted \u2014 flushed", { sessionID });
|
|
@@ -711,14 +743,21 @@ type: ${errorName}`;
|
|
|
711
743
|
traceShipper.endSpan(state.currentTurnSpan, { error: errorStr });
|
|
712
744
|
state.currentTurnSpan = void 0;
|
|
713
745
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
746
|
+
if (state.currentRootSpan) {
|
|
747
|
+
traceShipper.endSpan(state.currentRootSpan, { error: errorStr });
|
|
748
|
+
state.currentRootSpan = void 0;
|
|
749
|
+
}
|
|
750
|
+
if (state.currentEventId) {
|
|
751
|
+
await eventShipper.finish(state.currentEventId, {
|
|
752
|
+
userId: state.userId,
|
|
753
|
+
properties: {
|
|
754
|
+
error: errorStr,
|
|
755
|
+
turn_count: state.turnNumber,
|
|
756
|
+
tool_count: state.toolCount
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
state.currentEventId = void 0;
|
|
760
|
+
}
|
|
722
761
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
723
762
|
sessions.delete(String(errorSessionID));
|
|
724
763
|
log("Session error handled", { errorSessionID, errorName });
|
|
@@ -737,21 +776,53 @@ type: ${errorName}`;
|
|
|
737
776
|
}
|
|
738
777
|
},
|
|
739
778
|
// ------------------------------------------------------------------
|
|
740
|
-
// chat.message — user sent a message; start a new turn
|
|
779
|
+
// chat.message — user sent a message; start a new turn with a fresh event ID
|
|
741
780
|
// ------------------------------------------------------------------
|
|
742
781
|
"chat.message": async (messageInput, output) => {
|
|
743
|
-
var _a;
|
|
782
|
+
var _a, _b;
|
|
744
783
|
try {
|
|
745
784
|
const { sessionID } = messageInput;
|
|
746
|
-
|
|
747
|
-
if (!state)
|
|
785
|
+
let state = sessions.get(sessionID);
|
|
786
|
+
if (!state) {
|
|
787
|
+
state = {
|
|
788
|
+
sessionId: sessionID,
|
|
789
|
+
userId: (_a = config.userId) != null ? _a : sessionID,
|
|
790
|
+
turnNumber: 0,
|
|
791
|
+
toolCount: 0,
|
|
792
|
+
startTime: Date.now(),
|
|
793
|
+
currentInput: "",
|
|
794
|
+
currentOutput: "",
|
|
795
|
+
outputParts: /* @__PURE__ */ new Map(),
|
|
796
|
+
reasoningParts: /* @__PURE__ */ new Map(),
|
|
797
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
798
|
+
toolStartTimes: /* @__PURE__ */ new Map(),
|
|
799
|
+
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
800
|
+
toolCallMessageIds: /* @__PURE__ */ new Map(),
|
|
801
|
+
processedMessages: /* @__PURE__ */ new Set(),
|
|
802
|
+
attachments: []
|
|
803
|
+
};
|
|
804
|
+
sessions.set(sessionID, state);
|
|
805
|
+
}
|
|
748
806
|
if (state.currentTurnSpan) {
|
|
749
807
|
traceShipper.endSpan(state.currentTurnSpan);
|
|
750
808
|
state.currentTurnSpan = void 0;
|
|
751
809
|
}
|
|
810
|
+
if (!state.currentRootSpan) {
|
|
811
|
+
state.currentRootSpan = traceShipper.startSpan({
|
|
812
|
+
name: `OpenCode: ${getProjectName(worktree)}`,
|
|
813
|
+
eventId: sessionID,
|
|
814
|
+
attributes: [
|
|
815
|
+
attrString("workspace", worktree),
|
|
816
|
+
attrString("directory", directory),
|
|
817
|
+
attrString("hostname", hostname),
|
|
818
|
+
attrString("os", os)
|
|
819
|
+
]
|
|
820
|
+
});
|
|
821
|
+
}
|
|
752
822
|
state.turnNumber++;
|
|
753
823
|
state.currentOutput = "";
|
|
754
|
-
|
|
824
|
+
state.currentEventId = generateId();
|
|
825
|
+
const parts = (_b = output == null ? void 0 : output.parts) != null ? _b : [];
|
|
755
826
|
const textParts = [];
|
|
756
827
|
const newAttachments = [];
|
|
757
828
|
for (const part of parts) {
|
|
@@ -771,17 +842,29 @@ type: ${errorName}`;
|
|
|
771
842
|
if (newAttachments.length > 0) state.attachments.push(...newAttachments);
|
|
772
843
|
state.currentTurnSpan = traceShipper.startSpan({
|
|
773
844
|
name: `Turn ${state.turnNumber}`,
|
|
774
|
-
parent: state.
|
|
775
|
-
eventId: state.
|
|
845
|
+
parent: state.currentRootSpan.ids,
|
|
846
|
+
eventId: state.currentEventId,
|
|
776
847
|
attributes: [attrInt("turn.number", state.turnNumber)]
|
|
777
848
|
});
|
|
778
|
-
await eventShipper.patch(state.
|
|
849
|
+
await eventShipper.patch(state.currentEventId, {
|
|
850
|
+
isPending: true,
|
|
851
|
+
userId: state.userId,
|
|
852
|
+
convoId: state.sessionId,
|
|
853
|
+
eventName: config.eventName,
|
|
779
854
|
input: userText || void 0,
|
|
780
855
|
attachments: newAttachments.length > 0 ? newAttachments : void 0,
|
|
781
|
-
properties: {
|
|
856
|
+
properties: {
|
|
857
|
+
workspace: worktree,
|
|
858
|
+
directory,
|
|
859
|
+
hostname,
|
|
860
|
+
os,
|
|
861
|
+
turn_count: state.turnNumber,
|
|
862
|
+
tool_count: state.toolCount
|
|
863
|
+
}
|
|
782
864
|
});
|
|
783
865
|
log("Chat message \u2014 new turn started", {
|
|
784
866
|
sessionID,
|
|
867
|
+
eventId: state.currentEventId,
|
|
785
868
|
turnNumber: state.turnNumber,
|
|
786
869
|
inputLength: userText.length,
|
|
787
870
|
attachments: newAttachments.length
|
|
@@ -791,50 +874,57 @@ type: ${errorName}`;
|
|
|
791
874
|
}
|
|
792
875
|
},
|
|
793
876
|
// ------------------------------------------------------------------
|
|
794
|
-
// tool.execute.before — record start time
|
|
877
|
+
// tool.execute.before — record start time only (span created atomically in after)
|
|
795
878
|
// ------------------------------------------------------------------
|
|
796
879
|
"tool.execute.before": async (toolInput, _output) => {
|
|
797
880
|
try {
|
|
798
881
|
const { tool, sessionID, callID } = toolInput;
|
|
799
882
|
const state = sessions.get(sessionID);
|
|
800
|
-
if (!state || !state.currentTurnSpan) return;
|
|
883
|
+
if (!state || !state.currentTurnSpan || !state.currentEventId) return;
|
|
801
884
|
state.toolStartTimes.set(callID, Date.now());
|
|
802
|
-
|
|
803
|
-
|
|
885
|
+
state.toolSpanStarts.set(callID, {
|
|
886
|
+
startTimeUnixNano: nowUnixNanoString(),
|
|
804
887
|
parent: state.currentTurnSpan.ids,
|
|
805
|
-
eventId: state.
|
|
806
|
-
|
|
888
|
+
eventId: state.currentEventId,
|
|
889
|
+
name: tool
|
|
807
890
|
});
|
|
808
|
-
state.toolSpans.set(callID, toolSpan);
|
|
809
891
|
log("Tool execute before", { tool, sessionID, callID });
|
|
810
892
|
} catch (err) {
|
|
811
893
|
log("Error in tool.execute.before hook", { error: String(err) });
|
|
812
894
|
}
|
|
813
895
|
},
|
|
814
896
|
// ------------------------------------------------------------------
|
|
815
|
-
// tool.execute.after —
|
|
897
|
+
// tool.execute.after — create tool span atomically with both start and end times
|
|
816
898
|
// ------------------------------------------------------------------
|
|
817
899
|
"tool.execute.after": async (toolInput, result) => {
|
|
818
900
|
var _a;
|
|
819
901
|
try {
|
|
820
902
|
const { tool, sessionID, callID } = toolInput;
|
|
821
903
|
const state = sessions.get(sessionID);
|
|
822
|
-
if (!state) return;
|
|
904
|
+
if (!state || !state.currentEventId) return;
|
|
823
905
|
state.toolCount++;
|
|
824
906
|
state.toolStartTimes.delete(callID);
|
|
825
907
|
state.toolCallMessageIds.delete(callID);
|
|
826
|
-
const
|
|
827
|
-
state.
|
|
828
|
-
if (
|
|
908
|
+
const startInfo = state.toolSpanStarts.get(callID);
|
|
909
|
+
state.toolSpanStarts.delete(callID);
|
|
910
|
+
if (startInfo) {
|
|
911
|
+
const endTimeUnixNano = nowUnixNanoString();
|
|
829
912
|
const outputPreview = typeof result.output === "string" ? result.output.slice(0, 200) : JSON.stringify((_a = result.output) != null ? _a : "").slice(0, 200);
|
|
830
|
-
traceShipper.
|
|
913
|
+
traceShipper.createSpan({
|
|
914
|
+
name: startInfo.name,
|
|
915
|
+
parent: startInfo.parent,
|
|
916
|
+
eventId: startInfo.eventId,
|
|
917
|
+
startTimeUnixNano: startInfo.startTimeUnixNano,
|
|
918
|
+
endTimeUnixNano,
|
|
831
919
|
attributes: [
|
|
920
|
+
attrString("tool.name", tool),
|
|
832
921
|
attrString("tool.title", result.title),
|
|
833
922
|
attrString("tool.output_preview", outputPreview)
|
|
834
923
|
]
|
|
835
924
|
});
|
|
836
925
|
}
|
|
837
|
-
await eventShipper.patch(state.
|
|
926
|
+
await eventShipper.patch(state.currentEventId, {
|
|
927
|
+
userId: state.userId,
|
|
838
928
|
properties: { tool_count: state.toolCount }
|
|
839
929
|
});
|
|
840
930
|
log("Tool execute after", { tool, sessionID, callID });
|
|
@@ -866,7 +956,7 @@ async function plugin(input) {
|
|
|
866
956
|
"warn",
|
|
867
957
|
"RAINDROP_WRITE_KEY not set \u2014 Raindrop tracing disabled. Set it in your environment or in ~/.config/opencode/raindrop.json."
|
|
868
958
|
);
|
|
869
|
-
return {
|
|
959
|
+
return {};
|
|
870
960
|
}
|
|
871
961
|
if (config.debug) {
|
|
872
962
|
appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
|
|
@@ -890,7 +980,7 @@ async function plugin(input) {
|
|
|
890
980
|
eventShipper,
|
|
891
981
|
traceShipper
|
|
892
982
|
);
|
|
893
|
-
return
|
|
983
|
+
return hooks;
|
|
894
984
|
}
|
|
895
985
|
export {
|
|
896
986
|
plugin as default
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/opencode-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.
|
|
7
|
-
"module": "dist/index.
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.
|
|
13
|
-
"require": "./dist/index.
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|