opencode-sync-plugin 0.3.2 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +42 -2
  2. package/dist/index.js +94 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@ Sync your OpenCode sessions to the cloud. Search, share, and access your coding
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/opencode-sync-plugin.svg)](https://www.npmjs.com/package/opencode-sync-plugin)
6
6
 
7
- ## OpenSync Ecosystem
7
+ ## OpenSync Ecosystem link
8
8
 
9
9
  | Package | Description | Links |
10
10
  |---------|-------------|-------|
@@ -31,6 +31,44 @@ npm install
31
31
  npm run build
32
32
  ```
33
33
 
34
+ ## Upgrading
35
+
36
+ To upgrade to the latest version of the plugin:
37
+
38
+ **Using npm:**
39
+
40
+ ```bash
41
+ npm update -g opencode-sync-plugin
42
+ ```
43
+
44
+ **Or reinstall to get the latest:**
45
+
46
+ ```bash
47
+ npm install -g opencode-sync-plugin@latest
48
+ ```
49
+
50
+ **Clear the OpenCode plugin cache** (required for OpenCode to pick up the new version):
51
+
52
+ ```bash
53
+ rm -rf ~/.cache/opencode/node_modules/opencode-sync-plugin
54
+ ```
55
+
56
+ **Restart OpenCode** to load the updated plugin.
57
+
58
+ **Check your installed version:**
59
+
60
+ ```bash
61
+ opencode-sync version
62
+ ```
63
+
64
+ **Backfill existing sessions with proper titles** (if upgrading from v0.3.2 or earlier):
65
+
66
+ ```bash
67
+ opencode-sync sync --force
68
+ ```
69
+
70
+ This re-syncs all local sessions with accurate titles from OpenCode's local storage.
71
+
34
72
  ## Setup
35
73
 
36
74
  ### 1. Get your credentials
@@ -112,10 +150,12 @@ The plugin hooks into OpenCode events and syncs data automatically:
112
150
  |-------|--------|
113
151
  | `session.created` | Creates session record in cloud |
114
152
  | `session.updated` | Updates session metadata |
115
- | `session.idle` | Final sync with token counts and cost |
153
+ | `session.idle` | Final sync with accurate title, token counts, and cost |
116
154
  | `message.updated` | Syncs user and assistant messages |
117
155
  | `message.part.updated` | Syncs completed message parts |
118
156
 
157
+ On `session.idle`, the plugin queries OpenCode's SDK to get the accurate session title (generated after the first message exchange). This ensures sessions are stored with meaningful titles instead of "Untitled".
158
+
119
159
  Data is stored in your Convex deployment. You can view, search, and share sessions via the web UI.
120
160
 
121
161
  ## CLI Commands
package/dist/index.js CHANGED
@@ -3,7 +3,64 @@ import {
3
3
  } from "./chunk-J64QRI6W.js";
4
4
 
5
5
  // src/index.ts
6
+ import { existsSync, readFileSync, readdirSync } from "fs";
7
+ import { homedir } from "os";
8
+ import { join } from "path";
6
9
  var syncedSessions = /* @__PURE__ */ new Set();
10
+ function getLocalSessionData(sessionId) {
11
+ try {
12
+ const basePath = join(homedir(), ".local", "share", "opencode", "storage");
13
+ const sessionPath = join(basePath, "session");
14
+ const messagePath = join(basePath, "message", sessionId);
15
+ if (!existsSync(sessionPath)) return null;
16
+ let result = {};
17
+ const projectDirs = readdirSync(sessionPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
18
+ for (const projectDir of projectDirs) {
19
+ const sessionFile = join(sessionPath, projectDir, `${sessionId}.json`);
20
+ if (existsSync(sessionFile)) {
21
+ const content = readFileSync(sessionFile, "utf8");
22
+ const data = JSON.parse(content);
23
+ result.title = data.title;
24
+ result.slug = data.slug;
25
+ break;
26
+ }
27
+ }
28
+ if (existsSync(messagePath)) {
29
+ let totalPromptTokens = 0;
30
+ let totalCompletionTokens = 0;
31
+ let totalCost = 0;
32
+ const messageFiles = readdirSync(messagePath).filter(
33
+ (f) => f.endsWith(".json")
34
+ );
35
+ for (const msgFile of messageFiles) {
36
+ try {
37
+ const msgContent = readFileSync(join(messagePath, msgFile), "utf8");
38
+ const msgData = JSON.parse(msgContent);
39
+ if (msgData.tokens) {
40
+ totalPromptTokens += msgData.tokens.input || 0;
41
+ totalCompletionTokens += msgData.tokens.output || 0;
42
+ }
43
+ if (msgData.cost) {
44
+ totalCost += msgData.cost;
45
+ }
46
+ if (!result.model && msgData.modelID) {
47
+ result.model = msgData.modelID;
48
+ }
49
+ if (!result.provider && msgData.providerID) {
50
+ result.provider = msgData.providerID;
51
+ }
52
+ } catch {
53
+ }
54
+ }
55
+ result.promptTokens = totalPromptTokens;
56
+ result.completionTokens = totalCompletionTokens;
57
+ result.cost = totalCost;
58
+ }
59
+ return result;
60
+ } catch {
61
+ }
62
+ return null;
63
+ }
7
64
  var syncedMessages = /* @__PURE__ */ new Set();
8
65
  var messagePartsText = /* @__PURE__ */ new Map();
9
66
  var messageMetadata = /* @__PURE__ */ new Map();
@@ -55,7 +112,7 @@ function doSyncSession(session) {
55
112
  },
56
113
  body: JSON.stringify({
57
114
  externalId: session.id,
58
- title: session.title || "Untitled Session",
115
+ title: session.title || session.slug || "Untitled Session",
59
116
  projectPath,
60
117
  projectName: projectPath?.split("/").pop(),
61
118
  model: modelId,
@@ -132,7 +189,7 @@ function scheduleSyncMessage(messageId) {
132
189
  }, DEBOUNCE_MS);
133
190
  syncTimeouts.set(messageId, timeout);
134
191
  }
135
- var OpenCodeSyncPlugin = async () => {
192
+ var OpenCodeSyncPlugin = async ({ client }) => {
136
193
  return {
137
194
  event: async ({ event }) => {
138
195
  try {
@@ -144,6 +201,41 @@ var OpenCodeSyncPlugin = async () => {
144
201
  if (syncedSessions.has(sessionId)) return;
145
202
  syncedSessions.add(sessionId);
146
203
  }
204
+ if (event.type === "session.idle") {
205
+ const localData = getLocalSessionData(sessionId);
206
+ if (localData) {
207
+ doSyncSession({
208
+ ...props,
209
+ title: localData.title || props?.title,
210
+ slug: localData.slug || props?.slug,
211
+ modelID: localData.model || props?.modelID,
212
+ providerID: localData.provider || props?.providerID,
213
+ tokens: {
214
+ input: localData.promptTokens || 0,
215
+ output: localData.completionTokens || 0
216
+ },
217
+ cost: localData.cost || 0
218
+ });
219
+ return;
220
+ }
221
+ if (client) {
222
+ try {
223
+ const response = await client.session.get({
224
+ path: { id: sessionId }
225
+ });
226
+ const sessionData = response?.data || response;
227
+ if (sessionData?.title || sessionData?.slug) {
228
+ doSyncSession({
229
+ ...props,
230
+ title: sessionData.title,
231
+ slug: sessionData.slug
232
+ });
233
+ return;
234
+ }
235
+ } catch {
236
+ }
237
+ }
238
+ }
147
239
  doSyncSession(props);
148
240
  }
149
241
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sync-plugin",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "Sync your OpenCode sessions to the OpenSync dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",