dsh-deepseek-balance-widget 2.3.4 → 2.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 (2) hide show
  1. package/lib/index.js +86 -20
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
2
- import { readFile, writeFile, stat, mkdir, copyFile } from "node:fs/promises";
2
+ import { readFile, writeFile, stat, mkdir, copyFile, readdir, realpath } from "node:fs/promises";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname, resolve } from "node:path";
5
5
  import { homedir } from "node:os";
@@ -541,31 +541,59 @@ async function fileExists(path) {
541
541
  }
542
542
 
543
543
  /**
544
- * Detect the package manager used by UPDATE_CWD.
544
+ * Detect the package manager used in a given project root.
545
+ * @param {string} cwd
545
546
  * @returns {Promise<"pnpm" | "npm">}
546
547
  */
547
- async function detectPackageManager() {
548
- const pnpmLock = resolve(UPDATE_CWD, "pnpm-lock.yaml");
549
- const pnpmLockYml = resolve(UPDATE_CWD, "pnpm-lock.yml");
548
+ async function detectPackageManager(cwd) {
549
+ const pnpmLock = resolve(cwd, "pnpm-lock.yaml");
550
+ const pnpmLockYml = resolve(cwd, "pnpm-lock.yml");
550
551
  if (await fileExists(pnpmLock) || await fileExists(pnpmLockYml)) return "pnpm";
551
552
  return "npm";
552
553
  }
553
554
 
554
555
  /**
555
- * Run the package-manager update command for this package in the discovered project root.
556
- * Uses shell mode on Windows so that .cmd scripts (npm.cmd / pnpm.cmd) can actually spawn.
556
+ * Collect every dsh profile root that currently contains this plugin in its
557
+ * node_modules. We never rely on a single reverse-derived path because dsh may
558
+ * load the plugin from a different profile than the one our module URL points
559
+ * at (desktop vs web profile, pnpm symlinks, custom-plugins, etc). The previous
560
+ * single-point UPDATE_CWD caused "update succeeded but version unchanged"
561
+ * failures on machines where dsh loaded the plugin from a different location
562
+ * than the one npm wrote to.
563
+ * @returns {Promise<string[]>}
564
+ */
565
+ async function getUpdateTargets() {
566
+ const roots = new Set();
567
+ const normalize = (p) => String(p).replace(/\\/g, "/");
568
+ // 1. Reverse-derive from PLUGIN_ROOT (handles symlink edge cases via realpath).
569
+ try {
570
+ const realRoot = normalize(await realpath(PLUGIN_ROOT));
571
+ const parts = realRoot.split("/");
572
+ const nmIndex = parts.lastIndexOf("node_modules");
573
+ if (nmIndex > 0) roots.add(parts.slice(0, nmIndex).join("/"));
574
+ } catch {}
575
+ // 2. Scan every dsh profile for a node_modules entry of this plugin.
576
+ try {
577
+ const profilesDir = resolve(homedir(), ".dsh", "profiles");
578
+ const entries = await readdir(profilesDir, { withFileTypes: true });
579
+ for (const entry of entries) {
580
+ if (!entry.isDirectory()) continue;
581
+ const profileRoot = normalize(resolve(profilesDir, entry.name));
582
+ const pluginDir = resolve(profileRoot, "node_modules", "dsh-deepseek-balance-widget");
583
+ if (await fileExists(pluginDir)) {
584
+ roots.add(profileRoot);
585
+ }
586
+ }
587
+ } catch {}
588
+ return [...roots];
589
+ }
590
+
591
+ /**
592
+ * Run the package-manager update command for this package in a single project root.
557
593
  * @returns {Promise<{ok:boolean, output:string, error?:string}>}
558
594
  */
559
- async function runPackageUpdate() {
560
- const versionInfo = await queryNpmVersion();
561
- if (!versionInfo.ok) {
562
- return { ok: false, error: versionInfo.error || "failed to query npm version" };
563
- }
564
- if (!versionInfo.updateAvailable) {
565
- return { ok: true, output: "already up to date", noOp: true };
566
- }
567
- const target = versionInfo.latest;
568
- const pm = await detectPackageManager();
595
+ async function runInstallOnce(cwd, target) {
596
+ const pm = await detectPackageManager(cwd);
569
597
  const isWin = process.platform === "win32";
570
598
  const cmd = isWin ? (pm === "pnpm" ? "pnpm" : "npm") : pm;
571
599
  const args = pm === "pnpm"
@@ -578,13 +606,13 @@ async function runPackageUpdate() {
578
606
  // Build a single command string and let cmd.exe parse it; hide the console window.
579
607
  const quotedArgs = args.map((a) => `"${a.replace(/"/g, '\\"')}"`).join(" ");
580
608
  child = spawn("cmd.exe", ["/d", "/s", "/c", `${cmd} ${quotedArgs}`], {
581
- cwd: UPDATE_CWD,
609
+ cwd,
582
610
  windowsHide: true,
583
611
  env: { ...process.env, NPM_CONFIG_FUND: "false", NPM_CONFIG_AUDIT: "false" }
584
612
  });
585
613
  } else {
586
614
  child = spawn(cmd, args, {
587
- cwd: UPDATE_CWD,
615
+ cwd,
588
616
  env: { ...process.env, NPM_CONFIG_FUND: "false", NPM_CONFIG_AUDIT: "false" }
589
617
  });
590
618
  }
@@ -604,7 +632,7 @@ async function runPackageUpdate() {
604
632
  // Verify the on-disk version actually changed so the user can't be misled into
605
633
  // restarting dsh when the install silently wrote to a different location.
606
634
  try {
607
- const installedPkgPath = resolve(UPDATE_CWD, "node_modules", "dsh-deepseek-balance-widget", "package.json");
635
+ const installedPkgPath = resolve(cwd, "node_modules", "dsh-deepseek-balance-widget", "package.json");
608
636
  const installedPkg = JSON.parse(await readFile(installedPkgPath, "utf8"));
609
637
  if (installedPkg.version !== target) {
610
638
  resolve({ ok: false, output, error: `installed version mismatch: expected ${target}, found ${installedPkg.version}` });
@@ -619,6 +647,44 @@ async function runPackageUpdate() {
619
647
  });
620
648
  }
621
649
 
650
+ /**
651
+ * Update the plugin in every dsh profile that currently has it installed.
652
+ * The first success wins; if at least one location lands on the target version
653
+ * the update is reported as successful.
654
+ * @returns {Promise<{ok:boolean, output:string, error?:string}>}
655
+ */
656
+ async function runPackageUpdate() {
657
+ const versionInfo = await queryNpmVersion();
658
+ if (!versionInfo.ok) {
659
+ return { ok: false, error: versionInfo.error || "failed to query npm version" };
660
+ }
661
+ if (!versionInfo.updateAvailable) {
662
+ return { ok: true, output: "already up to date", noOp: true };
663
+ }
664
+ const target = versionInfo.latest;
665
+ let targets = await getUpdateTargets();
666
+ if (targets.length === 0) {
667
+ // Fallback to the legacy single reverse-derived path if scanning found nothing.
668
+ targets = [UPDATE_CWD];
669
+ }
670
+ const lines = [];
671
+ let anySuccess = false;
672
+ for (const cwd of targets) {
673
+ const result = await runInstallOnce(cwd, target);
674
+ const label = cwd.replace(/\//g, "\\");
675
+ if (result.ok) {
676
+ anySuccess = true;
677
+ lines.push(`✓ ${label} -> ${target}`);
678
+ } else {
679
+ lines.push(`✗ ${label}: ${result.error || "failed"}`);
680
+ }
681
+ }
682
+ if (anySuccess) {
683
+ return { ok: true, output: lines.join("\n") };
684
+ }
685
+ return { ok: false, output: lines.join("\n"), error: "update failed in all candidate locations" };
686
+ }
687
+
622
688
  /**
623
689
  * Query the DeepSeek balance API for one key.
624
690
  * @param {string} apiKey - the resolved DeepSeek API key.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-deepseek-balance-widget",
3
- "version": "2.3.4",
3
+ "version": "2.3.5",
4
4
  "type": "module",
5
5
  "description": "Multi-provider AI balance widget for the dsh web sidebar: a live, auto-refreshing balance pill plus a detail popover listing DeepSeek and any added providers (MiMo etc.). Keys are stored per-machine in ~/.dsh/ai-balances.json and resolved from the local credential seam, never hardcoded.",
6
6
  "keywords": [