@timefly/opencode-plugin 0.2.10 → 0.2.12

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/cli.js CHANGED
File without changes
@@ -1 +1 @@
1
- {"version":3,"file":"event-handlers.d.ts","sourceRoot":"","sources":["../src/event-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAiBvD,OAAO,EASN,KAAK,gBAAgB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG/D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAA;AAKxE,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAed,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC7B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CA0Dd,CAAA;AAED,eAAO,MAAM,wBAAwB,GACpC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqCd,CAAA;AAED,eAAO,MAAM,sBAAsB,GAClC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC9B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqBd,CAAA;AAED,eAAO,MAAM,qBAAqB,GACjC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,OAAO,gBAAgB,EACvB,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAkCd,CAAA"}
1
+ {"version":3,"file":"event-handlers.d.ts","sourceRoot":"","sources":["../src/event-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAiBvD,OAAO,EASN,KAAK,gBAAgB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG/D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAA;AAKxE,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAgBd,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC7B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CA0Dd,CAAA;AAED,eAAO,MAAM,wBAAwB,GACpC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqCd,CAAA;AAED,eAAO,MAAM,sBAAsB,GAClC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC9B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqBd,CAAA;AAED,eAAO,MAAM,qBAAqB,GACjC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,OAAO,gBAAgB,EACvB,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAkCd,CAAA"}
@@ -8,6 +8,7 @@ export const handleSessionCreated = (eventProperties, tracker, publish) => {
8
8
  return Promise.resolve();
9
9
  }
10
10
  tracker.recordSessionStart(sessionInfo.id);
11
+ tracker.recordSessionProject(sessionInfo.id, sessionInfo.projectID, sessionInfo.directory);
11
12
  return publish([
12
13
  {
13
14
  eventType: 'session_start',
@@ -32,6 +32,10 @@ export type SessionTiming = {
32
32
  aiGenerationMs: number;
33
33
  userWaitMs: number;
34
34
  };
35
+ export type SessionProjectContext = {
36
+ projectId: string;
37
+ directory: string;
38
+ };
35
39
  export type TurnTokenSnapshot = {
36
40
  contextTokens: number;
37
41
  cacheReadTokens: number;
@@ -49,6 +53,9 @@ export type EventTracker = {
49
53
  hasProcessedPart: (partId: string) => boolean;
50
54
  markPartProcessed: (partId: string) => void;
51
55
  recordSessionStart: (sessionId: string, startedAtMs?: number) => void;
56
+ recordSessionProject: (sessionId: string, projectId: string, directory: string) => void;
57
+ getSessionProjectContext: (sessionId: string) => SessionProjectContext | undefined;
58
+ getMachineHostname: () => string;
52
59
  recordSessionStats: (sessionId: string, delta: Partial<SessionStats>) => void;
53
60
  recordProviderConnection: (sessionId: string, providerId: string, providerSource: string) => void;
54
61
  recordCompactionPending: (sessionId: string, contextBeforeTokens: number) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"event-tracker.d.ts","sourceRoot":"","sources":["../src/event-tracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-tracker.d.ts","sourceRoot":"","sources":["../src/event-tracker.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import os from 'node:os';
1
2
  const emptySessionStats = () => ({
2
3
  billedInputTokens: 0,
3
4
  outputTokens: 0,
@@ -52,12 +53,18 @@ const mergeSessionStats = (currentStats, delta) => ({
52
53
  startedAtMs: currentStats.startedAtMs,
53
54
  aiGenerationMs: currentStats.aiGenerationMs + (delta.aiGenerationMs ?? 0)
54
55
  });
56
+ const readMachineHostname = () => {
57
+ const hostname = os.hostname().trim();
58
+ return hostname.length > 0 ? hostname : 'opencode';
59
+ };
55
60
  export const createEventTracker = () => {
56
61
  const processedMessageIds = new Set();
57
62
  const processedUserMessageIds = new Set();
58
63
  const processedPartIds = new Set();
59
64
  const sessionStatsById = new Map();
65
+ const sessionProjectById = new Map();
60
66
  const pendingCompactionContextBySession = new Map();
67
+ const machineHostname = readMachineHostname();
61
68
  return {
62
69
  hasProcessedMessage: (messageId) => processedMessageIds.has(messageId),
63
70
  markMessageProcessed: (messageId) => {
@@ -81,6 +88,19 @@ export const createEventTracker = () => {
81
88
  startedAtMs
82
89
  });
83
90
  },
91
+ recordSessionProject: (sessionId, projectId, directory) => {
92
+ const cleanProjectId = cleanIdentity(projectId);
93
+ const cleanDirectory = directory.trim();
94
+ if (!cleanProjectId && !cleanDirectory) {
95
+ return;
96
+ }
97
+ sessionProjectById.set(sessionId, {
98
+ projectId: cleanProjectId,
99
+ directory: cleanDirectory
100
+ });
101
+ },
102
+ getSessionProjectContext: (sessionId) => sessionProjectById.get(sessionId),
103
+ getMachineHostname: () => machineHostname,
84
104
  recordSessionStats: (sessionId, delta) => {
85
105
  const currentStats = sessionStatsById.get(sessionId) ?? emptySessionStats();
86
106
  sessionStatsById.set(sessionId, mergeSessionStats(currentStats, delta));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAgBjD,eAAO,MAAM,qBAAqB,EAAE,MAuEnC,CAAA;AAED,eAAe,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAgBjD,eAAO,MAAM,qBAAqB,EAAE,MAmFnC,CAAA;AAED,eAAe,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ const runHookSafely = (run) => Promise.resolve()
10
10
  .catch(() => undefined);
11
11
  export const timeflyOpenCodePlugin = ({ client }) => {
12
12
  const tracker = createEventTracker();
13
- const publisher = createEventPublisher(client, PLUGIN_VERSION);
13
+ const publisher = createEventPublisher(client, PLUGIN_VERSION, tracker);
14
14
  return client.app
15
15
  .log({
16
16
  body: {
@@ -43,8 +43,18 @@ export const timeflyOpenCodePlugin = ({ client }) => {
43
43
  ]);
44
44
  }),
45
45
  'tool.execute.before': (input) => runHookSafely(() => {
46
+ const toolArguments = typeof input === 'object' && input !== null && 'arguments' in input
47
+ ? input.arguments
48
+ : undefined;
46
49
  tracker.recordSessionStats(input.sessionID, { toolCallCount: 1 });
47
- return publisher.publish([mapToolCallInput(input)]);
50
+ return publisher.publish([
51
+ mapToolCallInput({
52
+ sessionID: input.sessionID,
53
+ tool: input.tool,
54
+ callID: input.callID,
55
+ toolArguments
56
+ })
57
+ ]);
48
58
  }),
49
59
  'tool.execute.after': (input, output) => runHookSafely(() => {
50
60
  tracker.recordToolOutputChars(input.sessionID, output.output.length);
@@ -0,0 +1,3 @@
1
+ export declare const inferLanguageFromFilePath: (filePath: string) => string;
2
+ export declare const extractFilePathFromToolInput: (toolName: string, toolInput: unknown) => string;
3
+ //# sourceMappingURL=language-inference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-inference.d.ts","sourceRoot":"","sources":["../src/language-inference.ts"],"names":[],"mappings":"AA6CA,eAAO,MAAM,yBAAyB,GAAI,UAAU,MAAM,KAAG,MAgB5D,CAAA;AAQD,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,EAAE,WAAW,OAAO,KAAG,MA0BnF,CAAA"}
@@ -0,0 +1,75 @@
1
+ const EXTENSION_LANGUAGE_MAP = {
2
+ cjs: 'javascript',
3
+ cts: 'typescript',
4
+ css: 'css',
5
+ go: 'go',
6
+ htm: 'html',
7
+ html: 'html',
8
+ java: 'java',
9
+ js: 'javascript',
10
+ json: 'json',
11
+ jsx: 'javascriptreact',
12
+ kt: 'kotlin',
13
+ kts: 'kotlin',
14
+ md: 'markdown',
15
+ mjs: 'javascript',
16
+ mts: 'typescript',
17
+ py: 'python',
18
+ rb: 'ruby',
19
+ rs: 'rust',
20
+ scss: 'scss',
21
+ sh: 'shell',
22
+ sql: 'sql',
23
+ svelte: 'svelte',
24
+ ts: 'typescript',
25
+ tsx: 'typescriptreact',
26
+ vue: 'vue',
27
+ yaml: 'yaml',
28
+ yml: 'yaml'
29
+ };
30
+ const FILE_PATH_TOOL_KEYS = ['path', 'file', 'file_path', 'filePath', 'target', 'uri', 'document'];
31
+ const isRecord = (value) => typeof value === 'object' && value !== null;
32
+ const cleanFilePath = (value) => {
33
+ const trimmedValue = value.trim();
34
+ if (!trimmedValue || trimmedValue === 'unknown') {
35
+ return '';
36
+ }
37
+ return trimmedValue.replace(/^file:\/\//i, '');
38
+ };
39
+ export const inferLanguageFromFilePath = (filePath) => {
40
+ const cleanedPath = cleanFilePath(filePath);
41
+ if (!cleanedPath) {
42
+ return '';
43
+ }
44
+ const normalizedPath = cleanedPath.replace(/\\/g, '/');
45
+ const fileName = normalizedPath.split('/').filter(Boolean).pop() || normalizedPath;
46
+ const extension = fileName.includes('.') ? fileName.split('.').pop()?.toLowerCase() || '' : '';
47
+ if (!extension) {
48
+ return '';
49
+ }
50
+ return EXTENSION_LANGUAGE_MAP[extension] || extension;
51
+ };
52
+ const readStringField = (record, fieldName) => {
53
+ const fieldValue = record[fieldName];
54
+ return typeof fieldValue === 'string' ? cleanFilePath(fieldValue) : '';
55
+ };
56
+ export const extractFilePathFromToolInput = (toolName, toolInput) => {
57
+ if (typeof toolInput === 'string') {
58
+ return cleanFilePath(toolInput);
59
+ }
60
+ if (!isRecord(toolInput)) {
61
+ return '';
62
+ }
63
+ const directPath = FILE_PATH_TOOL_KEYS.map((fieldName) => readStringField(toolInput, fieldName)).find(Boolean);
64
+ if (directPath) {
65
+ return directPath;
66
+ }
67
+ const nestedArguments = toolInput.arguments;
68
+ if (typeof nestedArguments === 'string') {
69
+ return cleanFilePath(nestedArguments);
70
+ }
71
+ if (!isRecord(nestedArguments)) {
72
+ return '';
73
+ }
74
+ return FILE_PATH_TOOL_KEYS.map((fieldName) => readStringField(nestedArguments, fieldName)).find(Boolean) || '';
75
+ };
@@ -1,10 +1,12 @@
1
1
  import type { CreateAiUsageEventInput } from '@timefly/ai-sdk';
2
+ import type { EventTracker } from './event-tracker.js';
2
3
  import type { OpenCodeAssistantMessage, OpenCodeCompactionPart, OpenCodeRetryPart, OpenCodeSessionInfo, OpenCodeStepFinishPart, OpenCodeUserMessage } from './opencode-readers.js';
3
4
  import type { SessionStats, SessionTiming, CompactionDelta } from './event-tracker.js';
4
5
  export type { OpenCodeAssistantMessage, OpenCodeCompactionPart, OpenCodeRetryPart, OpenCodeSessionInfo, OpenCodeStepFinishPart, OpenCodeUserMessage } from './opencode-readers.js';
5
6
  export type { OpenCodeTokenUsage } from './token-usage.js';
6
7
  export declare const readSessionIdOverride: (sessionId: string) => string;
7
8
  export declare const buildModelId: (providerId: string, modelId: string) => string;
9
+ export declare const attachSessionContext: (tracker: Pick<EventTracker, "getSessionProjectContext" | "getMachineHostname">, input: CreateAiUsageEventInput) => CreateAiUsageEventInput;
8
10
  export declare const mapSessionStartInput: (sessionInfo: OpenCodeSessionInfo) => Pick<CreateAiUsageEventInput, "sessionId" | "metadata">;
9
11
  export declare const mapSessionEndInput: (sessionId: string, sessionStats: SessionStats, sessionTiming: SessionTiming) => Pick<CreateAiUsageEventInput, "sessionId" | "metadata" | "durationMs">;
10
12
  export declare const mapLlmRequestInput: (input: {
@@ -30,6 +32,7 @@ export declare const mapToolCallInput: (input: {
30
32
  sessionID: string;
31
33
  tool: string;
32
34
  callID: string;
35
+ toolArguments?: unknown;
33
36
  }) => CreateAiUsageEventInput;
34
37
  export declare const mapToolResultInput: (input: {
35
38
  sessionID: string;
@@ -1 +1 @@
1
- {"version":3,"file":"map-opencode-event.d.ts","sourceRoot":"","sources":["../src/map-opencode-event.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"map-opencode-event.d.ts","sourceRoot":"","sources":["../src/map-opencode-event.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { extractFilePathFromToolInput, inferLanguageFromFilePath } from './language-inference.js';
1
2
  import { buildTokenMetadata, buildTokenMetrics } from './token-usage.js';
2
3
  export const readSessionIdOverride = (sessionId) => typeof process !== 'undefined' && process.env.TIMEFLY_OPENCODE_SESSION_ID
3
4
  ? process.env.TIMEFLY_OPENCODE_SESSION_ID
@@ -34,6 +35,19 @@ export const buildModelId = (providerId, modelId) => {
34
35
  const cleanModelId = cleanIdentity(modelId);
35
36
  return cleanProviderId && cleanModelId ? `${cleanProviderId}/${cleanModelId}` : cleanModelId;
36
37
  };
38
+ export const attachSessionContext = (tracker, input) => {
39
+ const sessionContext = tracker.getSessionProjectContext(input.sessionId);
40
+ return {
41
+ ...input,
42
+ metadata: cleanMetadata({
43
+ ...input.metadata,
44
+ hostname: tracker.getMachineHostname(),
45
+ platform: process.platform,
46
+ ...(sessionContext?.projectId ? { project_id: sessionContext.projectId } : {}),
47
+ ...(sessionContext?.directory ? { directory: sessionContext.directory } : {})
48
+ })
49
+ };
50
+ };
37
51
  export const mapSessionStartInput = (sessionInfo) => ({
38
52
  sessionId: readSessionIdOverride(sessionInfo.id),
39
53
  metadata: {
@@ -197,14 +211,23 @@ export const mapErrorInput = (sessionId, errorMetadata) => ({
197
211
  eventType: 'error',
198
212
  metadata: errorMetadata
199
213
  });
200
- export const mapToolCallInput = (input) => ({
201
- sessionId: readSessionIdOverride(input.sessionID),
202
- eventType: 'tool_call',
203
- toolName: input.tool,
204
- metadata: {
205
- call_id: input.callID
206
- }
207
- });
214
+ export const mapToolCallInput = (input) => {
215
+ const filePath = extractFilePathFromToolInput(input.tool, input.toolArguments);
216
+ return {
217
+ sessionId: readSessionIdOverride(input.sessionID),
218
+ eventType: 'tool_call',
219
+ toolName: input.tool,
220
+ metadata: cleanMetadata({
221
+ call_id: input.callID,
222
+ ...(filePath
223
+ ? {
224
+ file_path: filePath,
225
+ language: inferLanguageFromFilePath(filePath)
226
+ }
227
+ : {})
228
+ })
229
+ };
230
+ };
208
231
  export const mapToolResultInput = (input) => ({
209
232
  sessionId: readSessionIdOverride(input.sessionID),
210
233
  eventType: 'tool_result',
@@ -1,8 +1,9 @@
1
1
  import { type CreateAiUsageEventInput } from '@timefly/ai-sdk';
2
2
  import type { PluginInput } from '@opencode-ai/plugin';
3
+ import type { EventTracker } from './event-tracker.js';
3
4
  type EventPublisher = {
4
5
  publish: (events: CreateAiUsageEventInput[]) => Promise<void>;
5
6
  };
6
- export declare const createEventPublisher: (client: PluginInput["client"], sourceVersion: string) => EventPublisher;
7
+ export declare const createEventPublisher: (client: PluginInput["client"], sourceVersion: string, tracker: Pick<EventTracker, "getSessionProjectContext" | "getMachineHostname">) => EventPublisher;
7
8
  export {};
8
9
  //# sourceMappingURL=publish-events.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish-events.d.ts","sourceRoot":"","sources":["../src/publish-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,uBAAuB,EAAoB,MAAM,iBAAiB,CAAA;AAC1I,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAItD,KAAK,cAAc,GAAG;IACrB,OAAO,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7D,CAAA;AAkCD,eAAO,MAAM,oBAAoB,GAAI,QAAQ,WAAW,CAAC,QAAQ,CAAC,EAAE,eAAe,MAAM,KAAG,cAoB3F,CAAA"}
1
+ {"version":3,"file":"publish-events.d.ts","sourceRoot":"","sources":["../src/publish-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,uBAAuB,EAAoB,MAAM,iBAAiB,CAAA;AAC1I,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKtD,KAAK,cAAc,GAAG;IACrB,OAAO,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7D,CAAA;AA+BD,eAAO,MAAM,oBAAoB,GAChC,QAAQ,WAAW,CAAC,QAAQ,CAAC,EAC7B,eAAe,MAAM,EACrB,SAAS,IAAI,CAAC,YAAY,EAAE,0BAA0B,GAAG,oBAAoB,CAAC,KAC5E,cAsBF,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { createAiUsageEvent, createTimeFlyAiClient, isSyncFailure } from '@timefly/ai-sdk';
2
+ import { attachSessionContext } from './map-opencode-event.js';
2
3
  const PRICING_URL = 'https://timefly.dev/pricing';
3
4
  const buildSyncFailureMessage = (error) => {
4
5
  if (error.isSupporterRequired) {
@@ -24,8 +25,7 @@ const logPublishFailure = (client, error) => {
24
25
  .then(() => undefined)
25
26
  .catch(() => undefined);
26
27
  };
27
- const buildUsageEvents = (sourceVersion, inputs) => inputs.map((input) => createAiUsageEvent('opencode', sourceVersion, input));
28
- export const createEventPublisher = (client, sourceVersion) => {
28
+ export const createEventPublisher = (client, sourceVersion, tracker) => {
29
29
  const timeflyClient = createTimeFlyAiClient({
30
30
  source: 'opencode',
31
31
  sourceVersion
@@ -35,7 +35,9 @@ export const createEventPublisher = (client, sourceVersion) => {
35
35
  if (!events.length) {
36
36
  return Promise.resolve();
37
37
  }
38
- const usageEvents = buildUsageEvents(sourceVersion, events);
38
+ const usageEvents = events
39
+ .map((eventInput) => attachSessionContext(tracker, eventInput))
40
+ .map((eventInput) => createAiUsageEvent('opencode', sourceVersion, eventInput));
39
41
  return timeflyClient
40
42
  .recordEvents(usageEvents)
41
43
  .then(() => undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timefly/opencode-plugin",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "TimeFly telemetry plugin for OpenCode — sessions, tokens, models, and tools",
5
5
  "type": "module",
6
6
  "bin": "./dist/cli.js",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@opencode-ai/plugin": "^1.17.7",
60
- "@timefly/ai-sdk": "workspace:*",
60
+ "@timefly/ai-sdk": "0.2.3",
61
61
  "@types/bun": "^1.3.14",
62
62
  "@types/node": "^22.15.21",
63
63
  "typescript": "^5.9.3"