@opencode-trace/plugin 0.0.3

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.
Files changed (46) hide show
  1. package/README.md +124 -0
  2. package/dist/integration.test.d.ts +2 -0
  3. package/dist/integration.test.d.ts.map +1 -0
  4. package/dist/integration.test.js +76 -0
  5. package/dist/integration.test.js.map +1 -0
  6. package/dist/plugin-instance.d.ts +29 -0
  7. package/dist/plugin-instance.d.ts.map +1 -0
  8. package/dist/plugin-instance.js +219 -0
  9. package/dist/plugin-instance.js.map +1 -0
  10. package/dist/plugin-instance.test.d.ts +2 -0
  11. package/dist/plugin-instance.test.d.ts.map +1 -0
  12. package/dist/plugin-instance.test.js +135 -0
  13. package/dist/plugin-instance.test.js.map +1 -0
  14. package/dist/redact.d.ts +2 -0
  15. package/dist/redact.d.ts.map +1 -0
  16. package/dist/redact.js +40 -0
  17. package/dist/redact.js.map +1 -0
  18. package/dist/redact.test.d.ts +2 -0
  19. package/dist/redact.test.d.ts.map +1 -0
  20. package/dist/redact.test.js +77 -0
  21. package/dist/redact.test.js.map +1 -0
  22. package/dist/state-queue.d.ts +14 -0
  23. package/dist/state-queue.d.ts.map +1 -0
  24. package/dist/state-queue.js +44 -0
  25. package/dist/state-queue.js.map +1 -0
  26. package/dist/state-queue.test.d.ts +2 -0
  27. package/dist/state-queue.test.d.ts.map +1 -0
  28. package/dist/state-queue.test.js +99 -0
  29. package/dist/state-queue.test.js.map +1 -0
  30. package/dist/trace.d.ts +32 -0
  31. package/dist/trace.d.ts.map +1 -0
  32. package/dist/trace.js +100 -0
  33. package/dist/trace.js.map +1 -0
  34. package/dist/trace.test.d.ts +2 -0
  35. package/dist/trace.test.d.ts.map +1 -0
  36. package/dist/trace.test.js +359 -0
  37. package/dist/trace.test.js.map +1 -0
  38. package/dist/write-queue.d.ts +14 -0
  39. package/dist/write-queue.d.ts.map +1 -0
  40. package/dist/write-queue.js +62 -0
  41. package/dist/write-queue.js.map +1 -0
  42. package/dist/write-queue.test.d.ts +2 -0
  43. package/dist/write-queue.test.d.ts.map +1 -0
  44. package/dist/write-queue.test.js +92 -0
  45. package/dist/write-queue.test.js.map +1 -0
  46. package/package.json +48 -0
@@ -0,0 +1,359 @@
1
+ import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import entrypoint, { _resetForTesting } from "./trace.js";
6
+ async function waitForFile(filePath, timeoutMs = 5000) {
7
+ const startTime = Date.now();
8
+ while (true) {
9
+ if (existsSync(filePath)) {
10
+ try {
11
+ const content = readFileSync(filePath, "utf-8");
12
+ if (content && content.length > 0) {
13
+ JSON.parse(content);
14
+ return;
15
+ }
16
+ }
17
+ catch {
18
+ }
19
+ }
20
+ if (Date.now() - startTime > timeoutMs) {
21
+ throw new Error(`Timeout waiting for valid file ${filePath} after ${timeoutMs}ms`);
22
+ }
23
+ await new Promise(r => setTimeout(r, 10));
24
+ }
25
+ }
26
+ let testDir;
27
+ beforeEach(() => {
28
+ testDir = mkdtempSync(join(tmpdir(), "plugin-test-"));
29
+ vi.stubEnv("HOME", testDir);
30
+ });
31
+ afterEach(() => {
32
+ rmSync(testDir, { recursive: true, force: true });
33
+ vi.unstubAllEnvs();
34
+ });
35
+ describe("Plugin - Hooks 返回值", () => {
36
+ test("plugin 返回包含 event hook", async () => {
37
+ const hooks = await entrypoint.server({
38
+ client: {},
39
+ project: {},
40
+ directory: testDir,
41
+ worktree: testDir,
42
+ experimental_workspace: { register: vi.fn() },
43
+ serverUrl: new URL("http://localhost"),
44
+ $: {},
45
+ });
46
+ expect(hooks.event).toBeDefined();
47
+ });
48
+ test("plugin 返回包含 tool.execute.after hook", async () => {
49
+ const hooks = await entrypoint.server({
50
+ client: {},
51
+ project: {},
52
+ directory: testDir,
53
+ worktree: testDir,
54
+ experimental_workspace: { register: vi.fn() },
55
+ serverUrl: new URL("http://localhost"),
56
+ $: {},
57
+ });
58
+ expect(hooks["tool.execute.after"]).toBeDefined();
59
+ });
60
+ });
61
+ describe("Plugin - event hook 处理 session.created", () => {
62
+ test("event hook 处理 session.created 更新元数据", async () => {
63
+ const hooks = await entrypoint.server({
64
+ client: {},
65
+ project: {},
66
+ directory: testDir,
67
+ worktree: testDir,
68
+ experimental_workspace: { register: vi.fn() },
69
+ serverUrl: new URL("http://localhost"),
70
+ $: {},
71
+ });
72
+ const sessionId = "test-session-123";
73
+ await hooks.event({
74
+ event: {
75
+ type: "session.created",
76
+ properties: {
77
+ info: {
78
+ id: sessionId,
79
+ projectID: "test-project",
80
+ directory: testDir,
81
+ title: "Test Session Title",
82
+ version: "1.0",
83
+ time: { created: Date.now(), updated: Date.now() },
84
+ },
85
+ },
86
+ },
87
+ });
88
+ const traceDir = join(testDir, ".opencode-trace");
89
+ const sessionDir = join(traceDir, sessionId);
90
+ expect(existsSync(sessionDir)).toBe(true);
91
+ const dbPath = join(traceDir, "state.db");
92
+ expect(existsSync(dbPath)).toBe(true);
93
+ const metaPath = join(sessionDir, "metadata.json");
94
+ expect(existsSync(metaPath)).toBe(true);
95
+ const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
96
+ expect(meta.folderPath).toBe(testDir);
97
+ });
98
+ });
99
+ describe("Stream request detection", () => {
100
+ test("should detect stream request from body", () => {
101
+ const init = {
102
+ body: JSON.stringify({ stream: true, messages: [] }),
103
+ };
104
+ const isStream = JSON.parse(init.body ?? "{}").stream === true;
105
+ expect(isStream).toBe(true);
106
+ });
107
+ test("should detect non-stream request", () => {
108
+ const init = {
109
+ body: JSON.stringify({ stream: false, messages: [] }),
110
+ };
111
+ const isStream = JSON.parse(init.body ?? "{}").stream === true;
112
+ expect(isStream).toBe(false);
113
+ });
114
+ test("should handle missing stream field", () => {
115
+ const init = {
116
+ body: JSON.stringify({ messages: [] }),
117
+ };
118
+ const isStream = JSON.parse(init.body ?? "{}").stream === true;
119
+ expect(isStream).toBe(false);
120
+ });
121
+ test("should handle invalid JSON body", () => {
122
+ const init = {
123
+ body: "not valid json",
124
+ };
125
+ let isStream = false;
126
+ try {
127
+ isStream = JSON.parse(init.body ?? "{}")?.stream === true;
128
+ }
129
+ catch {
130
+ isStream = false;
131
+ }
132
+ expect(isStream).toBe(false);
133
+ });
134
+ });
135
+ describe("Latency metadata in TraceRecord", () => {
136
+ test("TraceRecord interface should have latency fields", () => {
137
+ const record = {
138
+ requestSentAt: 1234567.89,
139
+ firstTokenAt: 1234570.12,
140
+ lastTokenAt: 1234590.34,
141
+ };
142
+ expect(record.requestSentAt).toBe(1234567.89);
143
+ expect(record.firstTokenAt).toBe(1234570.12);
144
+ expect(record.lastTokenAt).toBe(1234590.34);
145
+ });
146
+ });
147
+ describe("tracedFetch stream integration", () => {
148
+ test("should apply TransformStream wrapper and record latency metadata", async () => {
149
+ _resetForTesting();
150
+ const mockFetch = vi.fn();
151
+ const originalFetch = globalThis.fetch;
152
+ globalThis.fetch = mockFetch;
153
+ const chunks = ["data: {\"content\": \"Hello\"}\n", "data: {\"content\": \"World\"}\n", "data: [DONE]\n"];
154
+ const encoder = new TextEncoder();
155
+ const streamChunks = chunks.map(c => encoder.encode(c));
156
+ const mockStream = new ReadableStream({
157
+ start(controller) {
158
+ for (const chunk of streamChunks) {
159
+ controller.enqueue(chunk);
160
+ }
161
+ controller.close();
162
+ },
163
+ });
164
+ mockFetch.mockResolvedValueOnce(new Response(mockStream, {
165
+ status: 200,
166
+ headers: { "content-type": "text/event-stream" },
167
+ }));
168
+ const hooks = await entrypoint.server({
169
+ client: {},
170
+ project: {},
171
+ directory: testDir,
172
+ worktree: testDir,
173
+ experimental_workspace: { register: vi.fn() },
174
+ serverUrl: new URL("http://localhost"),
175
+ $: {},
176
+ });
177
+ const sessionId = "stream-test-session";
178
+ await hooks.event({
179
+ event: {
180
+ type: "session.created",
181
+ properties: {
182
+ info: {
183
+ id: sessionId,
184
+ projectID: "test-project",
185
+ directory: testDir,
186
+ title: "Stream Test Session",
187
+ version: "1.0",
188
+ time: { created: Date.now(), updated: Date.now() },
189
+ },
190
+ },
191
+ },
192
+ });
193
+ const streamRequest = new Request("https://api.example.com/v1/chat/completions", {
194
+ method: "POST",
195
+ headers: {
196
+ "content-type": "application/json",
197
+ "x-opencode-session": sessionId,
198
+ },
199
+ body: JSON.stringify({ stream: true, messages: [{ role: "user", content: "test" }] }),
200
+ });
201
+ const response = await globalThis.fetch(streamRequest);
202
+ expect(response.__latencyMeta).toBeDefined();
203
+ expect(response.__latencyMeta.requestSentAt).toBeDefined();
204
+ expect(response.__latencyMeta.firstTokenAt).toBeNull();
205
+ expect(response.__latencyMeta.lastTokenAt).toBeNull();
206
+ const reader = response.body.getReader();
207
+ const receivedChunks = [];
208
+ while (true) {
209
+ const { done, value } = await reader.read();
210
+ if (done)
211
+ break;
212
+ receivedChunks.push(value);
213
+ }
214
+ expect(receivedChunks.length).toBe(3);
215
+ expect(response.__latencyMeta.firstTokenAt).not.toBeNull();
216
+ expect(response.__latencyMeta.lastTokenAt).not.toBeNull();
217
+ const traceDir = join(testDir, ".opencode-trace");
218
+ const sessionDir = join(traceDir, sessionId);
219
+ const recordFile = join(sessionDir, "1.json");
220
+ await waitForFile(recordFile, 5000);
221
+ if (existsSync(recordFile)) {
222
+ const record = JSON.parse(readFileSync(recordFile, "utf-8"));
223
+ expect(record.requestSentAt).toBeDefined();
224
+ expect(record.firstTokenAt).toBeDefined();
225
+ expect(record.lastTokenAt).toBeDefined();
226
+ }
227
+ globalThis.fetch = originalFetch;
228
+ });
229
+ test("should not wrap non-stream responses", async () => {
230
+ _resetForTesting();
231
+ const mockFetch = vi.fn();
232
+ const originalFetch = globalThis.fetch;
233
+ globalThis.fetch = mockFetch;
234
+ mockFetch.mockResolvedValueOnce(new Response(JSON.stringify({ result: "ok" }), {
235
+ status: 200,
236
+ headers: { "content-type": "application/json" },
237
+ }));
238
+ const hooks = await entrypoint.server({
239
+ client: {},
240
+ project: {},
241
+ directory: testDir,
242
+ worktree: testDir,
243
+ experimental_workspace: { register: vi.fn() },
244
+ serverUrl: new URL("http://localhost"),
245
+ $: {},
246
+ });
247
+ const sessionId = "non-stream-test-session";
248
+ await hooks.event({
249
+ event: {
250
+ type: "session.created",
251
+ properties: {
252
+ info: {
253
+ id: sessionId,
254
+ projectID: "test-project",
255
+ directory: testDir,
256
+ title: "Non-Stream Test",
257
+ version: "1.0",
258
+ time: { created: Date.now(), updated: Date.now() },
259
+ },
260
+ },
261
+ },
262
+ });
263
+ const request = new Request("https://api.example.com/v1/chat/completions", {
264
+ method: "POST",
265
+ headers: {
266
+ "content-type": "application/json",
267
+ "x-opencode-session": sessionId,
268
+ },
269
+ body: JSON.stringify({ stream: false, messages: [{ role: "user", content: "test" }] }),
270
+ });
271
+ const response = await globalThis.fetch(request);
272
+ expect(response.__latencyMeta).toBeUndefined();
273
+ globalThis.fetch = originalFetch;
274
+ });
275
+ });
276
+ describe("Plugin - tool.execute.after hook 处理 Task 工具", () => {
277
+ test("tool.execute.after hook 处理 Task 工具记录 sub session", async () => {
278
+ const hooks = await entrypoint.server({
279
+ client: {},
280
+ project: {},
281
+ directory: testDir,
282
+ worktree: testDir,
283
+ experimental_workspace: { register: vi.fn() },
284
+ serverUrl: new URL("http://localhost"),
285
+ $: {},
286
+ });
287
+ const parentSessionId = "parent-session-123";
288
+ const subSessionId = "sub-session-456";
289
+ await hooks.event({
290
+ event: {
291
+ type: "session.created",
292
+ properties: {
293
+ info: {
294
+ id: parentSessionId,
295
+ projectID: "test-project",
296
+ directory: testDir,
297
+ title: "Parent Session",
298
+ version: "1.0",
299
+ time: { created: Date.now(), updated: Date.now() },
300
+ },
301
+ },
302
+ },
303
+ });
304
+ await hooks["tool.execute.after"]({
305
+ tool: "task",
306
+ sessionID: parentSessionId,
307
+ callID: "call-123",
308
+ args: { description: "test", prompt: "test" },
309
+ }, {
310
+ title: "Task completed",
311
+ output: "success",
312
+ metadata: { session_id: subSessionId },
313
+ });
314
+ const traceDir = join(testDir, ".opencode-trace");
315
+ const dbPath = join(traceDir, "state.db");
316
+ expect(existsSync(dbPath)).toBe(true);
317
+ });
318
+ test("tool.execute.after hook 对非 Task 工具不记录 sub session", async () => {
319
+ const hooks = await entrypoint.server({
320
+ client: {},
321
+ project: {},
322
+ directory: testDir,
323
+ worktree: testDir,
324
+ experimental_workspace: { register: vi.fn() },
325
+ serverUrl: new URL("http://localhost"),
326
+ $: {},
327
+ });
328
+ const sessionId = "test-session-123";
329
+ await hooks.event({
330
+ event: {
331
+ type: "session.created",
332
+ properties: {
333
+ info: {
334
+ id: sessionId,
335
+ projectID: "test-project",
336
+ directory: testDir,
337
+ title: "Test Session",
338
+ version: "1.0",
339
+ time: { created: Date.now(), updated: Date.now() },
340
+ },
341
+ },
342
+ },
343
+ });
344
+ await hooks["tool.execute.after"]({
345
+ tool: "bash",
346
+ sessionID: sessionId,
347
+ callID: "call-123",
348
+ args: { command: "ls" },
349
+ }, {
350
+ title: "Command executed",
351
+ output: "file1.txt\nfile2.txt",
352
+ metadata: {},
353
+ });
354
+ const traceDir = join(testDir, ".opencode-trace");
355
+ const dbPath = join(traceDir, "state.db");
356
+ expect(existsSync(dbPath)).toBe(true);
357
+ });
358
+ });
359
+ //# sourceMappingURL=trace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.test.js","sourceRoot":"","sources":["../src/trace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { TraceRecord } from "./trace.js";
2
+ export declare class AsyncWriteQueue {
3
+ private queue;
4
+ private writing;
5
+ private traceDir;
6
+ private batchSize;
7
+ constructor(traceDir: string, batchSize?: number);
8
+ enqueue(session: string, seq: number, record: TraceRecord): void;
9
+ private processQueue;
10
+ flush(): Promise<void>;
11
+ private writeBatch;
12
+ private writeFallback;
13
+ }
14
+ //# sourceMappingURL=write-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-queue.d.ts","sourceRoot":"","sources":["../src/write-queue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAoE;IACjF,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;gBAEd,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAE,MAAW;IAKpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;YAOlD,YAAY;IAapB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAMd,UAAU;YAYV,aAAa;CAiB5B"}
@@ -0,0 +1,62 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { logger } from "@opencode-trace/core";
4
+ export class AsyncWriteQueue {
5
+ queue = [];
6
+ writing = false;
7
+ traceDir;
8
+ batchSize;
9
+ constructor(traceDir, batchSize = 10) {
10
+ this.traceDir = traceDir;
11
+ this.batchSize = batchSize;
12
+ }
13
+ enqueue(session, seq, record) {
14
+ this.queue.push({ session, seq, record });
15
+ if (!this.writing) {
16
+ this.processQueue();
17
+ }
18
+ }
19
+ async processQueue() {
20
+ this.writing = true;
21
+ while (this.queue.length > 0) {
22
+ const batch = this.queue.splice(0, this.batchSize);
23
+ await this.writeBatch(batch);
24
+ }
25
+ this.writing = false;
26
+ if (this.queue.length > 0 && !this.writing) {
27
+ this.processQueue();
28
+ }
29
+ }
30
+ async flush() {
31
+ while (this.writing || this.queue.length > 0) {
32
+ await new Promise(resolve => setTimeout(resolve, 10));
33
+ }
34
+ }
35
+ async writeBatch(items) {
36
+ for (const { session, seq, record } of items) {
37
+ try {
38
+ const sessionDir = join(this.traceDir, session);
39
+ await fs.mkdir(sessionDir, { recursive: true });
40
+ await fs.writeFile(join(sessionDir, `${seq}.json`), JSON.stringify(record, null, 2));
41
+ }
42
+ catch (err) {
43
+ await this.writeFallback(session, seq, record, err);
44
+ }
45
+ }
46
+ }
47
+ async writeFallback(session, seq, record, err) {
48
+ const fallbackDir = join(this.traceDir, "fallback");
49
+ await fs.mkdir(fallbackDir, { recursive: true });
50
+ const filename = `${session}-${seq}-${Date.now()}.json`;
51
+ await fs.writeFile(join(fallbackDir, filename), JSON.stringify({
52
+ record,
53
+ error: {
54
+ name: err.name,
55
+ message: err.message,
56
+ stack: err.stack
57
+ }
58
+ }, null, 2));
59
+ logger.error("Write failed, saved to fallback", { filename, error: err.message });
60
+ }
61
+ }
62
+ //# sourceMappingURL=write-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-queue.js","sourceRoot":"","sources":["../src/write-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=write-queue.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-queue.test.d.ts","sourceRoot":"","sources":["../src/write-queue.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
+ import { AsyncWriteQueue } from "./write-queue.js";
3
+ import { mkdtempSync, rmSync, existsSync, readFileSync, promises as fs } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ describe("AsyncWriteQueue", () => {
7
+ let tempDir;
8
+ let queue;
9
+ beforeEach(() => {
10
+ tempDir = mkdtempSync(join(tmpdir(), "write-queue-test-"));
11
+ queue = new AsyncWriteQueue(tempDir);
12
+ });
13
+ afterEach(() => {
14
+ rmSync(tempDir, { recursive: true, force: true });
15
+ });
16
+ test("enqueue writes JSON file to session directory", async () => {
17
+ const record = {
18
+ id: 1,
19
+ purpose: "test",
20
+ requestAt: "2026-05-07T00:00:00Z",
21
+ responseAt: "2026-05-07T00:00:01Z",
22
+ request: { method: "GET", url: "https://example.com", headers: {}, body: null },
23
+ response: { status: 200, statusText: "OK", headers: {}, body: null },
24
+ error: null,
25
+ };
26
+ queue.enqueue("session-1", 1, record);
27
+ await queue.flush();
28
+ const filePath = join(tempDir, "session-1", "1.json");
29
+ expect(existsSync(filePath)).toBe(true);
30
+ const content = JSON.parse(readFileSync(filePath, "utf-8"));
31
+ expect(content).toEqual(record);
32
+ });
33
+ test("enqueue processes items in batches of 10", async () => {
34
+ const records = Array.from({ length: 25 }, (_, i) => ({
35
+ id: i + 1,
36
+ purpose: `test-${i}`,
37
+ requestAt: `2026-05-07T00:00:${i.toString().padStart(2, "0")}Z`,
38
+ responseAt: `2026-05-07T00:00:${(i + 1).toString().padStart(2, "0")}Z`,
39
+ request: { method: "GET", url: `https://example.com/${i}`, headers: {}, body: null },
40
+ response: { status: 200, statusText: "OK", headers: {}, body: null },
41
+ error: null,
42
+ }));
43
+ for (let i = 0; i < 25; i++) {
44
+ queue.enqueue("batch-test", i + 1, records[i]);
45
+ }
46
+ await queue.flush();
47
+ for (let i = 1; i <= 25; i++) {
48
+ const filePath = join(tempDir, "batch-test", `${i}.json`);
49
+ expect(existsSync(filePath)).toBe(true);
50
+ }
51
+ });
52
+ test.skipIf(process.platform === "win32")("enqueue writes to fallback directory when primary write fails", async () => {
53
+ const record = {
54
+ id: 1,
55
+ purpose: "fallback-test",
56
+ requestAt: "2026-05-07T00:00:00Z",
57
+ responseAt: "2026-05-07T00:00:01Z",
58
+ request: { method: "GET", url: "https://example.com", headers: {}, body: null },
59
+ response: { status: 200, statusText: "OK", headers: {}, body: null },
60
+ error: null,
61
+ };
62
+ const sessionDir = join(tempDir, "readonly-session");
63
+ await fs.mkdir(sessionDir, { recursive: true });
64
+ await fs.chmod(sessionDir, 0o000);
65
+ queue.enqueue("readonly-session", 1, record);
66
+ await queue.flush();
67
+ await fs.chmod(sessionDir, 0o755);
68
+ const fallbackDir = join(tempDir, "fallback");
69
+ const files = await fs.readdir(fallbackDir);
70
+ expect(files.length).toBeGreaterThan(0);
71
+ });
72
+ test("flush waits for queue to drain", async () => {
73
+ const records = Array.from({ length: 15 }, (_, i) => ({
74
+ id: i + 1,
75
+ purpose: `flush-test-${i}`,
76
+ requestAt: "2026-05-07T00:00:00Z",
77
+ responseAt: "2026-05-07T00:00:01Z",
78
+ request: { method: "GET", url: "https://example.com", headers: {}, body: null },
79
+ response: { status: 200, statusText: "OK", headers: {}, body: null },
80
+ error: null,
81
+ }));
82
+ for (let i = 0; i < 15; i++) {
83
+ queue.enqueue("flush-test", i + 1, records[i]);
84
+ }
85
+ await queue.flush();
86
+ for (let i = 1; i <= 15; i++) {
87
+ const filePath = join(tempDir, "flush-test", `${i}.json`);
88
+ expect(existsSync(filePath)).toBe(true);
89
+ }
90
+ });
91
+ });
92
+ //# sourceMappingURL=write-queue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-queue.test.js","sourceRoot":"","sources":["../src/write-queue.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,OAAe,CAAC;IACpB,IAAI,KAAsB,CAAC;IAE3B,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC3D,KAAK,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,sBAAsB;YACjC,UAAU,EAAE,sBAAsB;YAClC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YAC/E,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpE,KAAK,EAAE,IAAI;SACZ,CAAC;QAEF,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAEtC,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACtD,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5D,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,EAAE,EAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAClD,EAAE,EAAE,CAAC,GAAG,CAAC;YACT,OAAO,EAAE,QAAQ,CAAC,EAAE;YACpB,SAAS,EAAE,oBAAoB,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG;YAC/D,UAAU,EAAE,oBAAoB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG;YACtE,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpF,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpE,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC,CAAC;QAEJ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QACpH,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE,sBAAsB;YACjC,UAAU,EAAE,sBAAsB;YAClC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YAC/E,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpE,KAAK,EAAE,IAAI;SACZ,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACrD,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAElC,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAE7C,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QAEpB,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAElC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,EAAE,EAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAClD,EAAE,EAAE,CAAC,GAAG,CAAC;YACT,OAAO,EAAE,cAAc,CAAC,EAAE;YAC1B,SAAS,EAAE,sBAAsB;YACjC,UAAU,EAAE,sBAAsB;YAClC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YAC/E,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpE,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC,CAAC;QAEJ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@opencode-trace/plugin",
3
+ "version": "0.0.3",
4
+ "type": "module",
5
+ "description": "OpenCode plugin for trace recording",
6
+ "main": "./dist/trace.js",
7
+ "types": "./dist/trace.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/trace.js",
11
+ "types": "./dist/trace.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist/",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest watch",
22
+ "clean": "node ../../scripts/clean.mjs"
23
+ },
24
+ "dependencies": {
25
+ "@opencode-trace/core": "0.0.3",
26
+ "@opencode-ai/plugin": "^1.14.22",
27
+ "@opencode-ai/sdk": "^1.14.41"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.3.0",
31
+ "vitest": "^4.1.5"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/aixmoyu/opencode-trace.git",
40
+ "directory": "packages/plugin"
41
+ },
42
+ "keywords": [
43
+ "opencode",
44
+ "trace",
45
+ "plugin"
46
+ ],
47
+ "license": "MIT"
48
+ }