opencode-mempalace 0.1.4 → 0.1.6

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.
@@ -0,0 +1,11 @@
1
+ export interface UpdateResult {
2
+ currentVersion: string | null;
3
+ latestVersion: string | null;
4
+ updated: boolean;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Check npm registry for a newer version and install it if available.
9
+ * @param runInstall - Callback to run `bun install` in a given directory. Returns true on success.
10
+ */
11
+ export declare function checkAndUpdate(runInstall: (cwd: string) => Promise<boolean>): Promise<UpdateResult>;
package/dist/index.js CHANGED
@@ -12334,6 +12334,180 @@ function tool(input) {
12334
12334
  return input;
12335
12335
  }
12336
12336
  tool.schema = exports_external;
12337
+ // src/auto-update.ts
12338
+ import * as fs from "fs";
12339
+ import * as path from "path";
12340
+ import * as os from "os";
12341
+ import * as crypto from "crypto";
12342
+ var PACKAGE_NAME = "opencode-mempalace";
12343
+ var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
12344
+ var NPM_FETCH_TIMEOUT = 5000;
12345
+ function getOpenCodeCacheDir() {
12346
+ if (process.platform === "win32") {
12347
+ const appdata = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
12348
+ return path.join(appdata, "opencode");
12349
+ }
12350
+ return path.join(process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache"), "opencode");
12351
+ }
12352
+ function getOpenCodeConfigDir() {
12353
+ if (process.platform === "win32") {
12354
+ const appdata = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
12355
+ return path.join(appdata, "opencode");
12356
+ }
12357
+ return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "opencode");
12358
+ }
12359
+ var CACHE_DIR = path.join(getOpenCodeCacheDir(), "packages");
12360
+ var CONFIG_DIR = getOpenCodeConfigDir();
12361
+ function getCachedVersion() {
12362
+ const locations = [
12363
+ path.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json"),
12364
+ path.join(CACHE_DIR, `${PACKAGE_NAME}@latest`, "node_modules", PACKAGE_NAME, "package.json"),
12365
+ path.join(CONFIG_DIR, "node_modules", PACKAGE_NAME, "package.json")
12366
+ ];
12367
+ for (const pkgPath of locations) {
12368
+ try {
12369
+ if (fs.existsSync(pkgPath)) {
12370
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
12371
+ if (pkg.version)
12372
+ return pkg.version;
12373
+ }
12374
+ } catch {}
12375
+ }
12376
+ return null;
12377
+ }
12378
+ async function getLatestVersion() {
12379
+ const controller = new AbortController;
12380
+ const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
12381
+ try {
12382
+ const response = await fetch(NPM_REGISTRY_URL, {
12383
+ signal: controller.signal,
12384
+ headers: { Accept: "application/json" }
12385
+ });
12386
+ if (!response.ok)
12387
+ return null;
12388
+ const data = await response.json();
12389
+ return data.latest ?? null;
12390
+ } catch {
12391
+ return null;
12392
+ } finally {
12393
+ clearTimeout(timeoutId);
12394
+ }
12395
+ }
12396
+ function stripTrailingCommas(json2) {
12397
+ return json2.replace(/,(\s*[}\]])/g, "$1");
12398
+ }
12399
+ function invalidatePackage() {
12400
+ const pkgDirs = [
12401
+ path.join(CONFIG_DIR, "node_modules", PACKAGE_NAME),
12402
+ path.join(CACHE_DIR, "node_modules", PACKAGE_NAME),
12403
+ path.join(CACHE_DIR, `${PACKAGE_NAME}@latest`, "node_modules", PACKAGE_NAME)
12404
+ ];
12405
+ let removed = false;
12406
+ for (const dir of pkgDirs) {
12407
+ try {
12408
+ if (fs.existsSync(dir)) {
12409
+ fs.rmSync(dir, { recursive: true, force: true });
12410
+ removed = true;
12411
+ }
12412
+ } catch {}
12413
+ }
12414
+ for (const baseDir of [CACHE_DIR, path.join(CACHE_DIR, `${PACKAGE_NAME}@latest`)]) {
12415
+ const textLock = path.join(baseDir, "bun.lock");
12416
+ const binaryLock = path.join(baseDir, "bun.lockb");
12417
+ if (fs.existsSync(textLock)) {
12418
+ try {
12419
+ const content = fs.readFileSync(textLock, "utf-8");
12420
+ const lock = JSON.parse(stripTrailingCommas(content));
12421
+ if (lock.packages?.[PACKAGE_NAME]) {
12422
+ delete lock.packages[PACKAGE_NAME];
12423
+ fs.writeFileSync(textLock, JSON.stringify(lock, null, 2));
12424
+ removed = true;
12425
+ }
12426
+ } catch {}
12427
+ } else if (fs.existsSync(binaryLock)) {
12428
+ try {
12429
+ fs.unlinkSync(binaryLock);
12430
+ removed = true;
12431
+ } catch {}
12432
+ }
12433
+ }
12434
+ return removed;
12435
+ }
12436
+ function syncCachePackageJson(pkgJsonPath) {
12437
+ try {
12438
+ if (!fs.existsSync(pkgJsonPath)) {
12439
+ fs.mkdirSync(path.dirname(pkgJsonPath), { recursive: true });
12440
+ fs.writeFileSync(pkgJsonPath, JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2));
12441
+ return true;
12442
+ }
12443
+ const content = fs.readFileSync(pkgJsonPath, "utf-8");
12444
+ const pkg = JSON.parse(content);
12445
+ if (!pkg.dependencies)
12446
+ pkg.dependencies = {};
12447
+ if (pkg.dependencies[PACKAGE_NAME] === "latest")
12448
+ return true;
12449
+ pkg.dependencies[PACKAGE_NAME] = "latest";
12450
+ const tmpPath = `${pkgJsonPath}.${crypto.randomUUID()}`;
12451
+ fs.writeFileSync(tmpPath, JSON.stringify(pkg, null, 2));
12452
+ fs.renameSync(tmpPath, pkgJsonPath);
12453
+ return true;
12454
+ } catch {
12455
+ return false;
12456
+ }
12457
+ }
12458
+ function resolveActiveWorkspace() {
12459
+ const configInstall = path.join(CONFIG_DIR, "node_modules", PACKAGE_NAME, "package.json");
12460
+ const cacheInstall = path.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
12461
+ if (fs.existsSync(configInstall))
12462
+ return CONFIG_DIR;
12463
+ if (fs.existsSync(cacheInstall))
12464
+ return CACHE_DIR;
12465
+ if (fs.existsSync(path.join(CACHE_DIR, "package.json")))
12466
+ return CACHE_DIR;
12467
+ return CONFIG_DIR;
12468
+ }
12469
+ async function checkAndUpdate(runInstall) {
12470
+ const currentVersion = getCachedVersion();
12471
+ const latestVersion = await getLatestVersion();
12472
+ if (!currentVersion || !latestVersion) {
12473
+ return { currentVersion, latestVersion, updated: false };
12474
+ }
12475
+ if (currentVersion === latestVersion) {
12476
+ return { currentVersion, latestVersion, updated: false };
12477
+ }
12478
+ try {
12479
+ syncCachePackageJson(path.join(CACHE_DIR, "package.json"));
12480
+ const atLatestDir = path.join(CACHE_DIR, `${PACKAGE_NAME}@latest`);
12481
+ if (fs.existsSync(atLatestDir)) {
12482
+ syncCachePackageJson(path.join(atLatestDir, "package.json"));
12483
+ }
12484
+ invalidatePackage();
12485
+ const activeWorkspace = resolveActiveWorkspace();
12486
+ const success2 = await runInstall(activeWorkspace);
12487
+ if (success2) {
12488
+ if (activeWorkspace !== CACHE_DIR) {
12489
+ await runInstall(CACHE_DIR).catch(() => {});
12490
+ }
12491
+ if (activeWorkspace !== atLatestDir && fs.existsSync(path.join(atLatestDir, "package.json"))) {
12492
+ await runInstall(atLatestDir).catch(() => {});
12493
+ }
12494
+ }
12495
+ return {
12496
+ currentVersion,
12497
+ latestVersion,
12498
+ updated: success2,
12499
+ error: success2 ? undefined : "bun install failed"
12500
+ };
12501
+ } catch (err) {
12502
+ return {
12503
+ currentVersion,
12504
+ latestVersion,
12505
+ updated: false,
12506
+ error: err instanceof Error ? err.message : String(err)
12507
+ };
12508
+ }
12509
+ }
12510
+
12337
12511
  // src/index.ts
12338
12512
  var DEFAULT_MCP_COMMAND = [
12339
12513
  "bun",
@@ -12357,11 +12531,29 @@ On session start, load mempalace context by calling these tools directly (do NOT
12357
12531
  Call both in parallel. If either fails (ChromaDB not running), skip silently and continue.
12358
12532
  Use the results to inform your responses \u2014 do not announce or summarize them unless the user asks.
12359
12533
  Proceed with the user's request immediately.`;
12360
- var mempalacePlugin = async (_input, options) => {
12534
+ var mempalacePlugin = async (input, options) => {
12361
12535
  const opts = options ?? {};
12362
12536
  const mcpCommand = opts.mcpCommand ?? DEFAULT_MCP_COMMAND;
12363
12537
  const sessionsSeen = new Set;
12364
12538
  const diaryWritten = new Set;
12539
+ let updateResult = null;
12540
+ if (!opts.disableAutoUpdate) {
12541
+ checkAndUpdate(async (cwd) => {
12542
+ try {
12543
+ const result = await input.$`bun install`.cwd(cwd).quiet().nothrow();
12544
+ return result.exitCode === 0;
12545
+ } catch {
12546
+ return false;
12547
+ }
12548
+ }).then((result) => {
12549
+ updateResult = result;
12550
+ if (result.updated) {
12551
+ console.log(`[opencode-mempalace] Auto-updated: ${result.currentVersion} \u2192 ${result.latestVersion}. Restart to apply.`);
12552
+ } else if (result.error) {
12553
+ console.log(`[opencode-mempalace] Update available: ${result.currentVersion} \u2192 ${result.latestVersion} (install failed: ${result.error})`);
12554
+ }
12555
+ }).catch(() => {});
12556
+ }
12365
12557
  return {
12366
12558
  config: opts.disableMcp ? undefined : async (config2) => {
12367
12559
  if (!config2.mcp)
@@ -12374,23 +12566,28 @@ var mempalacePlugin = async (_input, options) => {
12374
12566
  };
12375
12567
  }
12376
12568
  },
12377
- "experimental.chat.system.transform": opts.disableProtocol ? undefined : async (_input2, output) => {
12378
- output.system.push(PALACE_PROTOCOL);
12569
+ "experimental.chat.system.transform": async (_input, output) => {
12570
+ if (!opts.disableProtocol) {
12571
+ output.system.push(PALACE_PROTOCOL);
12572
+ }
12573
+ if (updateResult?.updated) {
12574
+ output.system.push(`[opencode-mempalace] Updated ${updateResult.currentVersion} \u2192 ${updateResult.latestVersion}. Restart OpenCode to apply.`);
12575
+ }
12379
12576
  },
12380
- "tool.execute.after": async (input, _output) => {
12381
- if (input.tool === "mcp_mempalace_mempalace_diary_write") {
12382
- diaryWritten.add(input.sessionID);
12577
+ "tool.execute.after": async (input2, _output) => {
12578
+ if (input2.tool === "mcp_mempalace_mempalace_diary_write") {
12579
+ diaryWritten.add(input2.sessionID);
12383
12580
  }
12384
12581
  },
12385
- "experimental.session.compacting": async (input, output) => {
12386
- if (!diaryWritten.has(input.sessionID)) {
12582
+ "experimental.session.compacting": async (input2, output) => {
12583
+ if (!diaryWritten.has(input2.sessionID)) {
12387
12584
  output.context.push("\u26A0\uFE0F MEMPALACE: No diary entry written yet for this session. Call mcp_mempalace_mempalace_diary_write with a session summary BEFORE important context is lost to compaction.");
12388
12585
  }
12389
12586
  },
12390
- "chat.message": opts.disableAutoLoad ? undefined : async (input, output) => {
12391
- if (sessionsSeen.has(input.sessionID))
12587
+ "chat.message": opts.disableAutoLoad ? undefined : async (input2, output) => {
12588
+ if (sessionsSeen.has(input2.sessionID))
12392
12589
  return;
12393
- sessionsSeen.add(input.sessionID);
12590
+ sessionsSeen.add(input2.sessionID);
12394
12591
  const firstTextPart = output.parts.find((p) => p.type === "text");
12395
12592
  if (firstTextPart && "text" in firstTextPart) {
12396
12593
  firstTextPart.text = `${SESSION_START_INSTRUCTION}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mempalace",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "OpenCode plugin for MemPalace memory system — auto-registers MCP server, injects memory protocol into system prompt, and loads context on session start",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",