opencode-sync-plugin 0.2.7 → 0.2.9
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/README.md +5 -1
- package/dist/chunk-FYT43SUA.js +99 -0
- package/dist/cli.js +204 -42
- package/dist/config.d.ts +7 -1
- package/dist/config.js +13 -3
- package/dist/index.js +32 -11
- package/package.json +1 -1
- package/dist/chunk-JPPDGYOB.js +0 -43
package/README.md
CHANGED
|
@@ -117,10 +117,14 @@ Data is stored in your Convex deployment. You can view, search, and share sessio
|
|
|
117
117
|
| `opencode-sync login` | Configure with Convex URL and API Key |
|
|
118
118
|
| `opencode-sync verify` | Verify credentials and OpenCode config |
|
|
119
119
|
| `opencode-sync sync` | Test connectivity and create a test session |
|
|
120
|
-
| `opencode-sync sync --
|
|
120
|
+
| `opencode-sync sync --new` | Sync only new sessions (uses local tracking) |
|
|
121
|
+
| `opencode-sync sync --all` | Sync all sessions (queries backend, skips existing) |
|
|
122
|
+
| `opencode-sync sync --force` | Clear tracking and resync all sessions |
|
|
121
123
|
| `opencode-sync logout` | Clear stored credentials |
|
|
122
124
|
| `opencode-sync status` | Show authentication status |
|
|
123
125
|
| `opencode-sync config` | Show current configuration |
|
|
126
|
+
| `opencode-sync version` | Show installed version |
|
|
127
|
+
| `opencode-sync help` | Show help message |
|
|
124
128
|
|
|
125
129
|
## Configuration storage
|
|
126
130
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
var CONFIG_DIR = join(homedir(), ".opensync");
|
|
6
|
+
var CONFIG_FILE = join(CONFIG_DIR, "credentials.json");
|
|
7
|
+
var SYNCED_SESSIONS_FILE = join(CONFIG_DIR, "synced-sessions.json");
|
|
8
|
+
function getConfig() {
|
|
9
|
+
try {
|
|
10
|
+
if (!existsSync(CONFIG_FILE)) return null;
|
|
11
|
+
const content = readFileSync(CONFIG_FILE, "utf8");
|
|
12
|
+
const config = JSON.parse(content);
|
|
13
|
+
if (!config || !config.convexUrl) return null;
|
|
14
|
+
return config;
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error("Error reading config:", e);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function setConfig(cfg) {
|
|
21
|
+
try {
|
|
22
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
23
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.error("Error saving config:", e);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function clearConfig() {
|
|
31
|
+
try {
|
|
32
|
+
if (existsSync(CONFIG_FILE)) {
|
|
33
|
+
writeFileSync(CONFIG_FILE, "{}", "utf8");
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error("Error clearing config:", e);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function isLoggingEnabled() {
|
|
40
|
+
const config = getConfig();
|
|
41
|
+
return config?.logging === true;
|
|
42
|
+
}
|
|
43
|
+
function setLogging(enabled) {
|
|
44
|
+
const config = getConfig();
|
|
45
|
+
if (config) {
|
|
46
|
+
setConfig({ ...config, logging: enabled });
|
|
47
|
+
} else {
|
|
48
|
+
console.error("No config found. Please run 'opencode-sync login' first.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getSyncedSessions() {
|
|
52
|
+
try {
|
|
53
|
+
if (!existsSync(SYNCED_SESSIONS_FILE)) return /* @__PURE__ */ new Set();
|
|
54
|
+
const content = readFileSync(SYNCED_SESSIONS_FILE, "utf8");
|
|
55
|
+
const data = JSON.parse(content);
|
|
56
|
+
return new Set(Array.isArray(data.sessionIds) ? data.sessionIds : []);
|
|
57
|
+
} catch {
|
|
58
|
+
return /* @__PURE__ */ new Set();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function addSyncedSessions(sessionIds) {
|
|
62
|
+
try {
|
|
63
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
64
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
const existing = getSyncedSessions();
|
|
67
|
+
for (const id of sessionIds) {
|
|
68
|
+
existing.add(id);
|
|
69
|
+
}
|
|
70
|
+
const data = { sessionIds: Array.from(existing), lastUpdated: Date.now() };
|
|
71
|
+
writeFileSync(SYNCED_SESSIONS_FILE, JSON.stringify(data, null, 2), "utf8");
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error("Error saving synced sessions:", e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function clearSyncedSessions() {
|
|
77
|
+
try {
|
|
78
|
+
if (existsSync(SYNCED_SESSIONS_FILE)) {
|
|
79
|
+
writeFileSync(
|
|
80
|
+
SYNCED_SESSIONS_FILE,
|
|
81
|
+
JSON.stringify({ sessionIds: [], lastUpdated: Date.now() }),
|
|
82
|
+
"utf8"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error("Error clearing synced sessions:", e);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
getConfig,
|
|
92
|
+
setConfig,
|
|
93
|
+
clearConfig,
|
|
94
|
+
isLoggingEnabled,
|
|
95
|
+
setLogging,
|
|
96
|
+
getSyncedSessions,
|
|
97
|
+
addSyncedSessions,
|
|
98
|
+
clearSyncedSessions
|
|
99
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
addSyncedSessions,
|
|
3
4
|
clearConfig,
|
|
5
|
+
clearSyncedSessions,
|
|
4
6
|
getConfig,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
getSyncedSessions,
|
|
8
|
+
setConfig,
|
|
9
|
+
setLogging
|
|
10
|
+
} from "./chunk-FYT43SUA.js";
|
|
7
11
|
|
|
8
12
|
// src/cli.ts
|
|
9
13
|
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
@@ -39,7 +43,7 @@ async function main() {
|
|
|
39
43
|
status();
|
|
40
44
|
break;
|
|
41
45
|
case "config":
|
|
42
|
-
|
|
46
|
+
handleConfig();
|
|
43
47
|
break;
|
|
44
48
|
case "sync":
|
|
45
49
|
await sync();
|
|
@@ -60,16 +64,22 @@ async function main() {
|
|
|
60
64
|
}
|
|
61
65
|
async function login() {
|
|
62
66
|
console.log("\n OpenSync Login\n");
|
|
63
|
-
const convexUrl = await prompt(
|
|
67
|
+
const convexUrl = await prompt(
|
|
68
|
+
"Convex URL (e.g., https://your-project.convex.cloud): "
|
|
69
|
+
);
|
|
64
70
|
if (!convexUrl) {
|
|
65
71
|
console.error("Convex URL is required");
|
|
66
72
|
process.exit(1);
|
|
67
73
|
}
|
|
68
74
|
if (!convexUrl.includes(".convex.cloud") && !convexUrl.includes(".convex.site")) {
|
|
69
|
-
console.error(
|
|
75
|
+
console.error(
|
|
76
|
+
"Invalid Convex URL. Should end with .convex.cloud or .convex.site"
|
|
77
|
+
);
|
|
70
78
|
process.exit(1);
|
|
71
79
|
}
|
|
72
|
-
const apiKey = await prompt(
|
|
80
|
+
const apiKey = await prompt(
|
|
81
|
+
"API Key (from Settings page, starts with osk_): "
|
|
82
|
+
);
|
|
73
83
|
if (!apiKey) {
|
|
74
84
|
console.error("API Key is required");
|
|
75
85
|
process.exit(1);
|
|
@@ -99,8 +109,12 @@ async function login() {
|
|
|
99
109
|
}' > ~/.config/opencode/opencode.json`);
|
|
100
110
|
console.log("\n Then verify your setup:\n");
|
|
101
111
|
console.log(" opencode-sync verify\n");
|
|
102
|
-
console.log(
|
|
103
|
-
|
|
112
|
+
console.log(
|
|
113
|
+
" Note: If you have existing opencode.json settings, manually add"
|
|
114
|
+
);
|
|
115
|
+
console.log(
|
|
116
|
+
' "plugin": ["opencode-sync-plugin"] to preserve your config.\n'
|
|
117
|
+
);
|
|
104
118
|
} catch (e) {
|
|
105
119
|
console.error("\nFailed to connect to OpenSync backend.");
|
|
106
120
|
console.error("Please verify your Convex URL is correct.");
|
|
@@ -122,10 +136,18 @@ function verify() {
|
|
|
122
136
|
} else {
|
|
123
137
|
console.log(" Credentials: OK");
|
|
124
138
|
console.log(" Convex URL:", config.convexUrl);
|
|
125
|
-
console.log(
|
|
139
|
+
console.log(
|
|
140
|
+
" API Key:",
|
|
141
|
+
config.apiKey.slice(0, 8) + "..." + config.apiKey.slice(-4)
|
|
142
|
+
);
|
|
126
143
|
console.log();
|
|
127
144
|
}
|
|
128
|
-
const opencodeConfigPath = join(
|
|
145
|
+
const opencodeConfigPath = join(
|
|
146
|
+
homedir(),
|
|
147
|
+
".config",
|
|
148
|
+
"opencode",
|
|
149
|
+
"opencode.json"
|
|
150
|
+
);
|
|
129
151
|
const projectConfigPath = join(process.cwd(), "opencode.json");
|
|
130
152
|
let configFound = false;
|
|
131
153
|
let configPath = "";
|
|
@@ -166,9 +188,13 @@ function verify() {
|
|
|
166
188
|
console.log();
|
|
167
189
|
}
|
|
168
190
|
if (hasErrors) {
|
|
169
|
-
console.log(
|
|
191
|
+
console.log(
|
|
192
|
+
" Setup incomplete. Fix the issues above and run verify again.\n"
|
|
193
|
+
);
|
|
170
194
|
} else {
|
|
171
|
-
console.log(
|
|
195
|
+
console.log(
|
|
196
|
+
" Ready! Start OpenCode and the plugin will load automatically.\n"
|
|
197
|
+
);
|
|
172
198
|
}
|
|
173
199
|
}
|
|
174
200
|
function status() {
|
|
@@ -187,9 +213,30 @@ function status() {
|
|
|
187
213
|
}
|
|
188
214
|
console.log(" Status: Configured\n");
|
|
189
215
|
console.log(" Convex URL:", config.convexUrl);
|
|
190
|
-
console.log(
|
|
216
|
+
console.log(
|
|
217
|
+
" API Key:",
|
|
218
|
+
config.apiKey.slice(0, 8) + "..." + config.apiKey.slice(-4)
|
|
219
|
+
);
|
|
191
220
|
console.log();
|
|
192
221
|
}
|
|
222
|
+
function handleConfig() {
|
|
223
|
+
const loggingArg = args.find((a) => a.startsWith("--logging="));
|
|
224
|
+
if (loggingArg) {
|
|
225
|
+
const value = loggingArg.split("=")[1]?.toLowerCase();
|
|
226
|
+
if (value === "true" || value === "1" || value === "on") {
|
|
227
|
+
setLogging(true);
|
|
228
|
+
console.log("\n Logging enabled.\n");
|
|
229
|
+
} else if (value === "false" || value === "0" || value === "off") {
|
|
230
|
+
setLogging(false);
|
|
231
|
+
console.log("\n Logging disabled.\n");
|
|
232
|
+
} else {
|
|
233
|
+
console.log("\n Invalid value for --logging. Use true or false.\n");
|
|
234
|
+
console.log(" Example: opencode-sync config --logging=true\n");
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
showConfig();
|
|
239
|
+
}
|
|
193
240
|
function showConfig() {
|
|
194
241
|
const config = getConfig();
|
|
195
242
|
console.log("\n OpenSync Config\n");
|
|
@@ -199,11 +246,43 @@ function showConfig() {
|
|
|
199
246
|
return;
|
|
200
247
|
}
|
|
201
248
|
console.log(" Convex URL:", config.convexUrl);
|
|
202
|
-
console.log(
|
|
249
|
+
console.log(
|
|
250
|
+
" API Key:",
|
|
251
|
+
config.apiKey ? config.apiKey.slice(0, 8) + "..." + config.apiKey.slice(-4) : "Not set"
|
|
252
|
+
);
|
|
253
|
+
console.log(" Logging:", config.logging ? "enabled" : "disabled");
|
|
203
254
|
console.log();
|
|
204
255
|
}
|
|
256
|
+
function getMessageTextContent(partBasePath, messageId) {
|
|
257
|
+
const messagePartPath = join(partBasePath, messageId);
|
|
258
|
+
if (!existsSync(messagePartPath)) {
|
|
259
|
+
return "";
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const partFiles = readdirSync(messagePartPath).filter(
|
|
263
|
+
(f) => f.endsWith(".json")
|
|
264
|
+
);
|
|
265
|
+
let textContent = "";
|
|
266
|
+
for (const partFile of partFiles) {
|
|
267
|
+
try {
|
|
268
|
+
const partData = JSON.parse(
|
|
269
|
+
readFileSync(join(messagePartPath, partFile), "utf8")
|
|
270
|
+
);
|
|
271
|
+
if (partData.type === "text" && partData.text) {
|
|
272
|
+
textContent += partData.text;
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return textContent;
|
|
278
|
+
} catch {
|
|
279
|
+
return "";
|
|
280
|
+
}
|
|
281
|
+
}
|
|
205
282
|
async function sync() {
|
|
206
283
|
const syncAll = args.includes("--all");
|
|
284
|
+
const syncNew = args.includes("--new");
|
|
285
|
+
const syncForce = args.includes("--force");
|
|
207
286
|
const config = getConfig();
|
|
208
287
|
if (!config || !config.apiKey || !config.convexUrl) {
|
|
209
288
|
console.log("\n Status: Not configured\n");
|
|
@@ -211,8 +290,13 @@ async function sync() {
|
|
|
211
290
|
return;
|
|
212
291
|
}
|
|
213
292
|
const siteUrl = config.convexUrl.replace(".convex.cloud", ".convex.site");
|
|
214
|
-
if (
|
|
215
|
-
|
|
293
|
+
if (syncForce) {
|
|
294
|
+
clearSyncedSessions();
|
|
295
|
+
await syncAllSessions(siteUrl, config.apiKey, "force");
|
|
296
|
+
} else if (syncAll) {
|
|
297
|
+
await syncAllSessions(siteUrl, config.apiKey, "all");
|
|
298
|
+
} else if (syncNew) {
|
|
299
|
+
await syncAllSessions(siteUrl, config.apiKey, "new");
|
|
216
300
|
} else {
|
|
217
301
|
await syncConnectivityTest(siteUrl, config.apiKey);
|
|
218
302
|
}
|
|
@@ -277,11 +361,37 @@ async function syncConnectivityTest(siteUrl, apiKey) {
|
|
|
277
361
|
console.log();
|
|
278
362
|
}
|
|
279
363
|
}
|
|
280
|
-
async function
|
|
281
|
-
|
|
282
|
-
|
|
364
|
+
async function fetchBackendSessionIds(siteUrl, apiKey) {
|
|
365
|
+
try {
|
|
366
|
+
const res = await fetch(`${siteUrl}/sync/sessions/list`, {
|
|
367
|
+
method: "GET",
|
|
368
|
+
headers: {
|
|
369
|
+
Authorization: `Bearer ${apiKey}`
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
if (res.ok) {
|
|
373
|
+
const data = await res.json();
|
|
374
|
+
return new Set(Array.isArray(data.sessionIds) ? data.sessionIds : []);
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
return /* @__PURE__ */ new Set();
|
|
379
|
+
}
|
|
380
|
+
async function syncAllSessions(siteUrl, apiKey, mode) {
|
|
381
|
+
const modeLabel = mode === "force" ? "Force Syncing" : mode === "new" ? "Syncing New" : "Syncing All";
|
|
382
|
+
console.log(`
|
|
383
|
+
OpenSync: ${modeLabel} Local Sessions
|
|
384
|
+
`);
|
|
385
|
+
const opencodePath = join(
|
|
386
|
+
homedir(),
|
|
387
|
+
".local",
|
|
388
|
+
"share",
|
|
389
|
+
"opencode",
|
|
390
|
+
"storage"
|
|
391
|
+
);
|
|
283
392
|
const sessionPath = join(opencodePath, "session");
|
|
284
393
|
const messagePath = join(opencodePath, "message");
|
|
394
|
+
const partPath = join(opencodePath, "part");
|
|
285
395
|
if (!existsSync(sessionPath)) {
|
|
286
396
|
console.log(" No OpenCode sessions found.");
|
|
287
397
|
console.log(" Expected path:", sessionPath);
|
|
@@ -293,7 +403,9 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
293
403
|
const projectDirs = readdirSync(sessionPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
294
404
|
for (const projectDir of projectDirs) {
|
|
295
405
|
const projectSessionPath = join(sessionPath, projectDir);
|
|
296
|
-
const sessionFiles = readdirSync(projectSessionPath).filter(
|
|
406
|
+
const sessionFiles = readdirSync(projectSessionPath).filter(
|
|
407
|
+
(f) => f.endsWith(".json")
|
|
408
|
+
);
|
|
297
409
|
for (const file of sessionFiles) {
|
|
298
410
|
try {
|
|
299
411
|
const content = readFileSync(join(projectSessionPath, file), "utf8");
|
|
@@ -306,20 +418,49 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
306
418
|
}
|
|
307
419
|
}
|
|
308
420
|
} catch (e) {
|
|
309
|
-
console.log(
|
|
421
|
+
console.log(
|
|
422
|
+
" Error reading sessions:",
|
|
423
|
+
e instanceof Error ? e.message : String(e)
|
|
424
|
+
);
|
|
310
425
|
return;
|
|
311
426
|
}
|
|
312
|
-
console.log(` Found ${sessions.length} sessions
|
|
313
|
-
`);
|
|
427
|
+
console.log(` Found ${sessions.length} local sessions`);
|
|
314
428
|
if (sessions.length === 0) {
|
|
315
429
|
return;
|
|
316
430
|
}
|
|
317
|
-
let
|
|
431
|
+
let alreadySynced = /* @__PURE__ */ new Set();
|
|
432
|
+
if (mode === "all") {
|
|
433
|
+
console.log(" Checking backend for existing sessions...");
|
|
434
|
+
alreadySynced = await fetchBackendSessionIds(siteUrl, apiKey);
|
|
435
|
+
if (alreadySynced.size > 0) {
|
|
436
|
+
console.log(` Found ${alreadySynced.size} already synced on backend`);
|
|
437
|
+
}
|
|
438
|
+
} else if (mode === "new") {
|
|
439
|
+
alreadySynced = getSyncedSessions();
|
|
440
|
+
if (alreadySynced.size > 0) {
|
|
441
|
+
console.log(` Found ${alreadySynced.size} in local tracking file`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const sessionsToSync = sessions.filter((s) => !alreadySynced.has(s.data.id));
|
|
445
|
+
const skippedCount = sessions.length - sessionsToSync.length;
|
|
446
|
+
if (skippedCount > 0) {
|
|
447
|
+
console.log(` Skipping ${skippedCount} already synced sessions`);
|
|
448
|
+
}
|
|
449
|
+
console.log(` Will sync ${sessionsToSync.length} sessions
|
|
450
|
+
`);
|
|
451
|
+
if (sessionsToSync.length === 0) {
|
|
452
|
+
console.log(" All sessions already synced. Use --force to resync.\n");
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
let syncedSessionCount = 0;
|
|
318
456
|
let syncedMessages = 0;
|
|
319
457
|
let failedSessions = 0;
|
|
320
|
-
|
|
458
|
+
const newlySyncedIds = [];
|
|
459
|
+
for (const session of sessionsToSync) {
|
|
321
460
|
const { data } = session;
|
|
322
|
-
process.stdout.write(
|
|
461
|
+
process.stdout.write(
|
|
462
|
+
` Syncing: ${data.title || data.slug || data.id}... `
|
|
463
|
+
);
|
|
323
464
|
let totalPromptTokens = 0;
|
|
324
465
|
let totalCompletionTokens = 0;
|
|
325
466
|
let totalCost = 0;
|
|
@@ -329,10 +470,15 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
329
470
|
const messages = [];
|
|
330
471
|
if (existsSync(sessionMessagePath)) {
|
|
331
472
|
try {
|
|
332
|
-
const messageFiles = readdirSync(sessionMessagePath).filter(
|
|
473
|
+
const messageFiles = readdirSync(sessionMessagePath).filter(
|
|
474
|
+
(f) => f.endsWith(".json")
|
|
475
|
+
);
|
|
333
476
|
for (const msgFile of messageFiles) {
|
|
334
477
|
try {
|
|
335
|
-
const msgContent = readFileSync(
|
|
478
|
+
const msgContent = readFileSync(
|
|
479
|
+
join(sessionMessagePath, msgFile),
|
|
480
|
+
"utf8"
|
|
481
|
+
);
|
|
336
482
|
const msgData = JSON.parse(msgContent);
|
|
337
483
|
if (msgData.id && msgData.sessionID === data.id) {
|
|
338
484
|
messages.push(msgData);
|
|
@@ -380,10 +526,12 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
380
526
|
failedSessions++;
|
|
381
527
|
continue;
|
|
382
528
|
}
|
|
383
|
-
|
|
529
|
+
syncedSessionCount++;
|
|
530
|
+
newlySyncedIds.push(data.id);
|
|
384
531
|
let msgCount = 0;
|
|
385
532
|
for (const msg of messages) {
|
|
386
533
|
try {
|
|
534
|
+
const textContent = getMessageTextContent(partPath, msg.id);
|
|
387
535
|
const msgRes = await fetch(`${siteUrl}/sync/message`, {
|
|
388
536
|
method: "POST",
|
|
389
537
|
headers: {
|
|
@@ -394,8 +542,7 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
394
542
|
sessionExternalId: data.id,
|
|
395
543
|
externalId: msg.id,
|
|
396
544
|
role: msg.role,
|
|
397
|
-
textContent
|
|
398
|
-
// We don't have content in the message metadata files
|
|
545
|
+
textContent,
|
|
399
546
|
model: msg.modelID,
|
|
400
547
|
promptTokens: msg.tokens?.input,
|
|
401
548
|
completionTokens: msg.tokens?.output,
|
|
@@ -415,10 +562,16 @@ async function syncAllSessions(siteUrl, apiKey) {
|
|
|
415
562
|
failedSessions++;
|
|
416
563
|
}
|
|
417
564
|
}
|
|
565
|
+
if (newlySyncedIds.length > 0) {
|
|
566
|
+
addSyncedSessions(newlySyncedIds);
|
|
567
|
+
}
|
|
418
568
|
console.log();
|
|
419
569
|
console.log(` Summary:`);
|
|
420
|
-
console.log(` Sessions synced: ${
|
|
570
|
+
console.log(` Sessions synced: ${syncedSessionCount}`);
|
|
421
571
|
console.log(` Messages synced: ${syncedMessages}`);
|
|
572
|
+
if (skippedCount > 0) {
|
|
573
|
+
console.log(` Skipped: ${skippedCount}`);
|
|
574
|
+
}
|
|
422
575
|
if (failedSessions > 0) {
|
|
423
576
|
console.log(` Failed: ${failedSessions}`);
|
|
424
577
|
}
|
|
@@ -433,15 +586,19 @@ function help() {
|
|
|
433
586
|
Usage: opencode-sync <command> [options]
|
|
434
587
|
|
|
435
588
|
Commands:
|
|
436
|
-
login
|
|
437
|
-
verify
|
|
438
|
-
sync
|
|
439
|
-
sync --
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
589
|
+
login Configure with Convex URL and API Key
|
|
590
|
+
verify Verify credentials and OpenCode config
|
|
591
|
+
sync Test connectivity and create a test session
|
|
592
|
+
sync --new Sync only sessions not in local tracking file
|
|
593
|
+
sync --all Sync all sessions (checks backend, skips existing)
|
|
594
|
+
sync --force Clear tracking and resync all sessions
|
|
595
|
+
logout Clear stored credentials
|
|
596
|
+
status Show current authentication status
|
|
597
|
+
config Show current configuration
|
|
598
|
+
config --logging=true Enable debug logging
|
|
599
|
+
config --logging=false Disable debug logging (default)
|
|
600
|
+
version Show version number
|
|
601
|
+
help Show this help message
|
|
445
602
|
|
|
446
603
|
Setup:
|
|
447
604
|
1. Go to your OpenSync dashboard Settings page
|
|
@@ -451,7 +608,12 @@ function help() {
|
|
|
451
608
|
5. Add plugin to opencode.json (see instructions after login)
|
|
452
609
|
6. Run: opencode-sync verify
|
|
453
610
|
7. Run: opencode-sync sync (to test connectivity)
|
|
454
|
-
8. Run: opencode-sync sync --
|
|
611
|
+
8. Run: opencode-sync sync --new (to sync new sessions only)
|
|
612
|
+
|
|
613
|
+
Sync Modes:
|
|
614
|
+
--new Fast: uses local tracking, skips previously synced
|
|
615
|
+
--all Accurate: queries backend, skips existing on server
|
|
616
|
+
--force Full: clears tracking and resyncs everything
|
|
455
617
|
`);
|
|
456
618
|
}
|
|
457
619
|
function prompt(question) {
|
package/dist/config.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
interface Config {
|
|
2
2
|
convexUrl: string;
|
|
3
3
|
apiKey: string;
|
|
4
|
+
logging?: boolean;
|
|
4
5
|
}
|
|
5
6
|
declare function getConfig(): Config | null;
|
|
6
7
|
declare function setConfig(cfg: Config): void;
|
|
7
8
|
declare function clearConfig(): void;
|
|
9
|
+
declare function isLoggingEnabled(): boolean;
|
|
10
|
+
declare function setLogging(enabled: boolean): void;
|
|
11
|
+
declare function getSyncedSessions(): Set<string>;
|
|
12
|
+
declare function addSyncedSessions(sessionIds: string[]): void;
|
|
13
|
+
declare function clearSyncedSessions(): void;
|
|
8
14
|
|
|
9
|
-
export { clearConfig, getConfig, setConfig };
|
|
15
|
+
export { addSyncedSessions, clearConfig, clearSyncedSessions, getConfig, getSyncedSessions, isLoggingEnabled, setConfig, setLogging };
|
package/dist/config.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
addSyncedSessions,
|
|
2
3
|
clearConfig,
|
|
4
|
+
clearSyncedSessions,
|
|
3
5
|
getConfig,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
getSyncedSessions,
|
|
7
|
+
isLoggingEnabled,
|
|
8
|
+
setConfig,
|
|
9
|
+
setLogging
|
|
10
|
+
} from "./chunk-FYT43SUA.js";
|
|
6
11
|
export {
|
|
12
|
+
addSyncedSessions,
|
|
7
13
|
clearConfig,
|
|
14
|
+
clearSyncedSessions,
|
|
8
15
|
getConfig,
|
|
9
|
-
|
|
16
|
+
getSyncedSessions,
|
|
17
|
+
isLoggingEnabled,
|
|
18
|
+
setConfig,
|
|
19
|
+
setLogging
|
|
10
20
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
getConfig
|
|
3
|
-
|
|
2
|
+
getConfig,
|
|
3
|
+
isLoggingEnabled
|
|
4
|
+
} from "./chunk-FYT43SUA.js";
|
|
4
5
|
|
|
5
6
|
// src/index.ts
|
|
7
|
+
function log(...args) {
|
|
8
|
+
if (isLoggingEnabled()) {
|
|
9
|
+
console.log(...args);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
6
12
|
var syncedSessions = /* @__PURE__ */ new Set();
|
|
7
13
|
var syncedMessages = /* @__PURE__ */ new Set();
|
|
8
14
|
var messagePartsText = /* @__PURE__ */ new Map();
|
|
@@ -48,7 +54,7 @@ function doSyncSession(session) {
|
|
|
48
54
|
return;
|
|
49
55
|
}
|
|
50
56
|
const url = config.convexUrl.replace(".convex.cloud", ".convex.site");
|
|
51
|
-
|
|
57
|
+
log("[opencode-sync] Syncing session:", session.id);
|
|
52
58
|
const projectPath = session.path?.cwd || session.cwd || session.directory;
|
|
53
59
|
const modelId = session.modelID || session.model?.modelID || session.model;
|
|
54
60
|
const providerId = session.providerID || session.model?.providerID || session.provider;
|
|
@@ -72,7 +78,9 @@ function doSyncSession(session) {
|
|
|
72
78
|
completionTokens,
|
|
73
79
|
cost
|
|
74
80
|
})
|
|
75
|
-
}).then((r) => r.json()).then((data) =>
|
|
81
|
+
}).then((r) => r.json()).then((data) => log("[opencode-sync] Session sync response:", data)).catch(
|
|
82
|
+
(err) => console.error("[opencode-sync] Session sync error:", err)
|
|
83
|
+
);
|
|
76
84
|
} catch (err) {
|
|
77
85
|
console.error("[opencode-sync] doSyncSession error:", err);
|
|
78
86
|
}
|
|
@@ -85,12 +93,12 @@ function doSyncMessage(sessionId, messageId, role, textContent, metadata) {
|
|
|
85
93
|
return;
|
|
86
94
|
}
|
|
87
95
|
if (!textContent || textContent.trim().length === 0) {
|
|
88
|
-
|
|
96
|
+
log("[opencode-sync] Skipping empty message:", messageId);
|
|
89
97
|
return;
|
|
90
98
|
}
|
|
91
99
|
const finalRole = role === "unknown" || !role ? inferRole(textContent) : role;
|
|
92
100
|
const url = config.convexUrl.replace(".convex.cloud", ".convex.site");
|
|
93
|
-
|
|
101
|
+
log(
|
|
94
102
|
"[opencode-sync] Syncing message:",
|
|
95
103
|
messageId,
|
|
96
104
|
"role:",
|
|
@@ -118,7 +126,9 @@ function doSyncMessage(sessionId, messageId, role, textContent, metadata) {
|
|
|
118
126
|
completionTokens: metadata?.tokens?.output,
|
|
119
127
|
durationMs
|
|
120
128
|
})
|
|
121
|
-
}).then((r) => r.json()).then((data) =>
|
|
129
|
+
}).then((r) => r.json()).then((data) => log("[opencode-sync] Message sync response:", data)).catch(
|
|
130
|
+
(err) => console.error("[opencode-sync] Message sync error:", err)
|
|
131
|
+
);
|
|
122
132
|
} catch (err) {
|
|
123
133
|
console.error("[opencode-sync] doSyncMessage error:", err);
|
|
124
134
|
}
|
|
@@ -131,7 +141,13 @@ function trySyncMessage(messageId) {
|
|
|
131
141
|
const textContent = textParts.join("");
|
|
132
142
|
if (!textContent.trim()) return;
|
|
133
143
|
syncedMessages.add(messageId);
|
|
134
|
-
doSyncMessage(
|
|
144
|
+
doSyncMessage(
|
|
145
|
+
metadata.sessionId,
|
|
146
|
+
messageId,
|
|
147
|
+
metadata.role,
|
|
148
|
+
textContent,
|
|
149
|
+
metadata.info
|
|
150
|
+
);
|
|
135
151
|
messagePartsText.delete(messageId);
|
|
136
152
|
messageMetadata.delete(messageId);
|
|
137
153
|
}
|
|
@@ -145,7 +161,7 @@ function scheduleSyncMessage(messageId) {
|
|
|
145
161
|
syncTimeouts.set(messageId, timeout);
|
|
146
162
|
}
|
|
147
163
|
var OpenCodeSyncPlugin = async (input) => {
|
|
148
|
-
|
|
164
|
+
log("[opencode-sync] Plugin initialized for project:", input.project?.id);
|
|
149
165
|
return {
|
|
150
166
|
event: async ({ event }) => {
|
|
151
167
|
try {
|
|
@@ -163,7 +179,12 @@ var OpenCodeSyncPlugin = async (input) => {
|
|
|
163
179
|
if (event.type === "message.updated") {
|
|
164
180
|
const info = props?.info;
|
|
165
181
|
if (info?.id && info?.sessionID && info?.role) {
|
|
166
|
-
|
|
182
|
+
log(
|
|
183
|
+
"[opencode-sync] Message metadata received:",
|
|
184
|
+
info.id,
|
|
185
|
+
"role:",
|
|
186
|
+
info.role
|
|
187
|
+
);
|
|
167
188
|
messageMetadata.set(info.id, {
|
|
168
189
|
role: info.role,
|
|
169
190
|
sessionId: info.sessionID,
|
|
@@ -179,7 +200,7 @@ var OpenCodeSyncPlugin = async (input) => {
|
|
|
179
200
|
if (part?.type === "text" && part?.messageID && part?.sessionID) {
|
|
180
201
|
const messageId = part.messageID;
|
|
181
202
|
const text = part.text || "";
|
|
182
|
-
|
|
203
|
+
log(
|
|
183
204
|
"[opencode-sync] Text part received for message:",
|
|
184
205
|
messageId,
|
|
185
206
|
"length:",
|
package/package.json
CHANGED
package/dist/chunk-JPPDGYOB.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// src/config.ts
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
var CONFIG_DIR = join(homedir(), ".opensync");
|
|
6
|
-
var CONFIG_FILE = join(CONFIG_DIR, "credentials.json");
|
|
7
|
-
function getConfig() {
|
|
8
|
-
try {
|
|
9
|
-
if (!existsSync(CONFIG_FILE)) return null;
|
|
10
|
-
const content = readFileSync(CONFIG_FILE, "utf8");
|
|
11
|
-
const config = JSON.parse(content);
|
|
12
|
-
if (!config || !config.convexUrl) return null;
|
|
13
|
-
return config;
|
|
14
|
-
} catch (e) {
|
|
15
|
-
console.error("Error reading config:", e);
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function setConfig(cfg) {
|
|
20
|
-
try {
|
|
21
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
22
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.error("Error saving config:", e);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function clearConfig() {
|
|
30
|
-
try {
|
|
31
|
-
if (existsSync(CONFIG_FILE)) {
|
|
32
|
-
writeFileSync(CONFIG_FILE, "{}", "utf8");
|
|
33
|
-
}
|
|
34
|
-
} catch (e) {
|
|
35
|
-
console.error("Error clearing config:", e);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
getConfig,
|
|
41
|
-
setConfig,
|
|
42
|
-
clearConfig
|
|
43
|
-
};
|