opencode-replay 1.0.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/dist/index.js +164 -4
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # opencode-replay
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/opencode-replay.svg)](https://www.npmjs.com/package/opencode-replay)
4
+ [![npm downloads](https://img.shields.io/npm/dm/opencode-replay.svg)](https://www.npmjs.com/package/opencode-replay)
5
+
3
6
  A CLI tool that generates static HTML transcripts from [OpenCode](https://github.com/sst/opencode) sessions, enabling browsing, searching, and sharing of AI-assisted coding conversations.
4
7
 
8
+ **[Live Demo](https://ramtinj95.github.io/opencode-replay/)** - See example session transcripts
9
+
5
10
  ## Why?
6
11
 
7
12
  OpenCode stores session data in `~/.local/share/opencode/storage/` as JSON files, but this data isn't easily browsable or shareable. `opencode-replay` transforms these sessions into clean, searchable, static HTML pages.
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  // src/index.ts
5
5
  import { parseArgs } from "util";
6
- import { resolve as resolve2, join as join4 } from "path";
6
+ import { resolve as resolve2, join as join5 } from "path";
7
7
  import { readdir as readdir2 } from "fs/promises";
8
8
 
9
9
  // src/storage/reader.ts
@@ -2224,6 +2224,161 @@ function openBrowser(url) {
2224
2224
  }
2225
2225
  }
2226
2226
 
2227
+ // src/utils/update-notifier.ts
2228
+ import { homedir as homedir2 } from "os";
2229
+ import { join as join4 } from "path";
2230
+ import { mkdir as mkdir2 } from "fs/promises";
2231
+ var PACKAGE_NAME = "opencode-replay";
2232
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
2233
+ function detectPackageManager() {
2234
+ const modulePath = import.meta.dir;
2235
+ if (modulePath.includes(".bun") || modulePath.includes("bun/install")) {
2236
+ return "bun";
2237
+ }
2238
+ if (process.env.BUN_INSTALL) {
2239
+ return "bun";
2240
+ }
2241
+ if (typeof Bun !== "undefined") {
2242
+ const bunInstallPath = join4(homedir2(), ".bun");
2243
+ try {
2244
+ if (Bun.file(bunInstallPath).size) {
2245
+ return "bun";
2246
+ }
2247
+ } catch {}
2248
+ }
2249
+ return "npm";
2250
+ }
2251
+ function getCachePath() {
2252
+ const cacheDir = process.env.XDG_CACHE_HOME || join4(homedir2(), ".cache");
2253
+ return join4(cacheDir, "opencode-replay", "update-check.json");
2254
+ }
2255
+ async function readCache() {
2256
+ try {
2257
+ const cachePath = getCachePath();
2258
+ const file = Bun.file(cachePath);
2259
+ if (await file.exists()) {
2260
+ return await file.json();
2261
+ }
2262
+ } catch {}
2263
+ return null;
2264
+ }
2265
+ async function writeCache(data) {
2266
+ try {
2267
+ const cachePath = getCachePath();
2268
+ await mkdir2(join4(cachePath, ".."), { recursive: true });
2269
+ await Bun.write(cachePath, JSON.stringify(data));
2270
+ } catch {}
2271
+ }
2272
+ async function fetchLatestVersion() {
2273
+ try {
2274
+ const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
2275
+ headers: {
2276
+ Accept: "application/json"
2277
+ },
2278
+ signal: AbortSignal.timeout(3000)
2279
+ });
2280
+ if (!response.ok) {
2281
+ return null;
2282
+ }
2283
+ const data = await response.json();
2284
+ return data.version || null;
2285
+ } catch {
2286
+ return null;
2287
+ }
2288
+ }
2289
+ function isNewerVersion(current, latest) {
2290
+ const currentParts = current.split(".").map(Number);
2291
+ const latestParts = latest.split(".").map(Number);
2292
+ for (let i = 0;i < 3; i++) {
2293
+ const c = currentParts[i] || 0;
2294
+ const l = latestParts[i] || 0;
2295
+ if (l > c)
2296
+ return true;
2297
+ if (l < c)
2298
+ return false;
2299
+ }
2300
+ return false;
2301
+ }
2302
+ async function checkForUpdates(currentVersion) {
2303
+ try {
2304
+ const cache = await readCache();
2305
+ const now = Date.now();
2306
+ if (cache && now - cache.lastCheck < CHECK_INTERVAL_MS) {
2307
+ if (cache.latestVersion && isNewerVersion(currentVersion, cache.latestVersion)) {
2308
+ const pm = detectPackageManager();
2309
+ return {
2310
+ current: currentVersion,
2311
+ latest: cache.latestVersion,
2312
+ packageManager: pm,
2313
+ updateCommand: pm === "bun" ? `bun update -g ${PACKAGE_NAME}` : `npm update -g ${PACKAGE_NAME}`
2314
+ };
2315
+ }
2316
+ return null;
2317
+ }
2318
+ const latestVersion = await fetchLatestVersion();
2319
+ await writeCache({
2320
+ lastCheck: now,
2321
+ latestVersion
2322
+ });
2323
+ if (latestVersion && isNewerVersion(currentVersion, latestVersion)) {
2324
+ const pm = detectPackageManager();
2325
+ return {
2326
+ current: currentVersion,
2327
+ latest: latestVersion,
2328
+ packageManager: pm,
2329
+ updateCommand: pm === "bun" ? `bun update -g ${PACKAGE_NAME}` : `npm update -g ${PACKAGE_NAME}`
2330
+ };
2331
+ }
2332
+ return null;
2333
+ } catch {
2334
+ return null;
2335
+ }
2336
+ }
2337
+ function formatUpdateNotification(info) {
2338
+ const reset = "\x1B[0m";
2339
+ const dim = "\x1B[2m";
2340
+ const bold = "\x1B[1m";
2341
+ const yellow = "\x1B[33m";
2342
+ const cyan = "\x1B[36m";
2343
+ const green = "\x1B[32m";
2344
+ const topLeft = "\u250C";
2345
+ const topRight = "\u2510";
2346
+ const bottomLeft = "\u2514";
2347
+ const bottomRight = "\u2518";
2348
+ const horizontal = "\u2500";
2349
+ const vertical = "\u2502";
2350
+ const line1 = `Update available: ${dim}${info.current}${reset} \u2192 ${green}${bold}${info.latest}${reset}`;
2351
+ const line2 = `Run: ${cyan}${info.updateCommand}${reset}`;
2352
+ const contentWidth = Math.max(`Update available: ${info.current} \u2192 ${info.latest}`.length, `Run: ${info.updateCommand}`.length);
2353
+ const boxWidth = contentWidth + 4;
2354
+ const topBorder = `${yellow}${topLeft}${horizontal.repeat(boxWidth)}${topRight}${reset}`;
2355
+ const bottomBorder = `${yellow}${bottomLeft}${horizontal.repeat(boxWidth)}${bottomRight}${reset}`;
2356
+ const padLine = (line, visibleLength) => {
2357
+ const padding = boxWidth - visibleLength - 2;
2358
+ return `${yellow}${vertical}${reset} ${line}${" ".repeat(padding)} ${yellow}${vertical}${reset}`;
2359
+ };
2360
+ const paddedLine1 = padLine(line1, `Update available: ${info.current} \u2192 ${info.latest}`.length);
2361
+ const paddedLine2 = padLine(line2, `Run: ${info.updateCommand}`.length);
2362
+ return `
2363
+ ${topBorder}
2364
+ ${paddedLine1}
2365
+ ${paddedLine2}
2366
+ ${bottomBorder}
2367
+ `;
2368
+ }
2369
+ async function notifyIfUpdateAvailable(currentVersion) {
2370
+ if (!process.stdout.isTTY) {
2371
+ return;
2372
+ }
2373
+ if (process.env.NO_COLOR) {
2374
+ return;
2375
+ }
2376
+ const updateInfo = await checkForUpdates(currentVersion);
2377
+ if (updateInfo) {
2378
+ console.error(formatUpdateNotification(updateInfo));
2379
+ }
2380
+ }
2381
+
2227
2382
  // src/index.ts
2228
2383
  var colors = {
2229
2384
  reset: "\x1B[0m",
@@ -2429,9 +2584,10 @@ Examples:
2429
2584
  `);
2430
2585
  process.exit(0);
2431
2586
  }
2587
+ var pkg = await Bun.file(resolve2(import.meta.dir, "..", "package.json")).json();
2588
+ var currentVersion = pkg.version;
2432
2589
  if (values.version) {
2433
- const pkg = await Bun.file(resolve2(import.meta.dir, "..", "package.json")).json();
2434
- console.log(pkg.version);
2590
+ console.log(currentVersion);
2435
2591
  process.exit(0);
2436
2592
  }
2437
2593
  var storagePath = values.storage ?? getDefaultStoragePath();
@@ -2447,7 +2603,7 @@ if (isNaN(port) || port < 1 || port > 65535) {
2447
2603
  }
2448
2604
  try {
2449
2605
  await readdir2(storagePath);
2450
- const projectDir = join4(storagePath, "project");
2606
+ const projectDir = join5(storagePath, "project");
2451
2607
  try {
2452
2608
  await readdir2(projectDir);
2453
2609
  } catch {
@@ -2548,6 +2704,7 @@ if (values["no-generate"]) {
2548
2704
  }
2549
2705
  }
2550
2706
  if (values.serve) {
2707
+ await notifyIfUpdateAvailable(currentVersion);
2551
2708
  await serve({
2552
2709
  directory: resolve2(outputDir),
2553
2710
  port,
@@ -2557,6 +2714,9 @@ if (values.serve) {
2557
2714
  const indexPath = resolve2(outputDir, "index.html");
2558
2715
  openInBrowser(indexPath);
2559
2716
  }
2717
+ if (!values.serve) {
2718
+ await notifyIfUpdateAvailable(currentVersion);
2719
+ }
2560
2720
  function openInBrowser(target) {
2561
2721
  const platform = process.platform;
2562
2722
  if (platform === "darwin") {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/package.json",
2
3
  "name": "opencode-replay",
3
- "version": "1.0.0",
4
+ "version": "1.0.2",
4
5
  "description": "Generate static HTML transcripts from OpenCode sessions",
5
6
  "type": "module",
6
7
  "bin": {