kangentic 0.35.0 → 0.36.0

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 CHANGED
@@ -10,7 +10,7 @@ One command to install and launch [Kangentic](https://github.com/Kangentic/kange
10
10
  npx kangentic
11
11
  ```
12
12
 
13
- This downloads the pre-built binary for your platform from GitHub Releases, installs it, and launches the app. After the first run, the app manages its own updates automatically (Windows and macOS).
13
+ This downloads the pre-built binary for your platform from GitHub Releases, installs it, and launches the app. After the first run, the app manages its own updates automatically on every platform.
14
14
 
15
15
  ### Open a specific project
16
16
 
@@ -26,7 +26,7 @@ npx kangentic /path/to/your/project
26
26
  - **Windows:** Runs NSIS installer silently to `%LOCALAPPDATA%\Programs\Kangentic\`
27
27
  - **macOS:** Extracts .zip to `~/Applications/Kangentic.app`
28
28
  - **Linux:** Installs .rpm on RPM-family systems (`sudo dnf install` on Fedora/RHEL,
29
- `sudo zypper install` on openSUSE, falling back to `sudo rpm -i`) or .deb elsewhere
29
+ `sudo zypper install` on openSUSE, falling back to `sudo rpm -Uvh`) or .deb elsewhere
30
30
  (`sudo apt install`, falling back to `sudo dpkg -i`); prompts for password
31
31
  4. Launches the app
32
32
 
@@ -36,7 +36,7 @@ After the initial install, you generally don't need to run `npx kangentic` again
36
36
 
37
37
  - **Windows:** electron-updater installs new versions silently on restart.
38
38
  - **macOS:** Built-in auto-updater downloads in the background and prompts to restart. (Requires code signing.)
39
- - **Linux:** No auto-update. Re-run `npx kangentic` or download from [GitHub Releases](https://github.com/Kangentic/kangentic/releases).
39
+ - **Linux:** The app downloads the new deb/rpm and installs it when you click "Restart to update", asking for your password once. If that fails (no polkit agent, for example), re-run `npx kangentic@latest` (the `@latest` matters: a cached launcher reinstalls its own version), then quit and reopen the app.
40
40
 
41
41
  ### Install a specific version
42
42
 
package/bin/kangentic.js CHANGED
@@ -201,6 +201,49 @@ function commandExists(command) {
201
201
  }
202
202
  }
203
203
 
204
+ /**
205
+ * Best-effort check for an already-running Kangentic on Linux.
206
+ *
207
+ * Only ever used to SKIP a redundant launch, never to gate the install or the
208
+ * advice printed after it. That asymmetry is deliberate: `pgrep -x` matches a
209
+ * 15-character-truncated comm name and is a heuristic, so a false negative must
210
+ * cost no more than one extra launch that focuses the existing window. Anything
211
+ * that made the user-facing message conditional on this would degrade a false
212
+ * negative straight back into silence.
213
+ */
214
+ function isAppRunning(platformInfo) {
215
+ if (platformInfo.platform !== 'linux') return false;
216
+ if (!commandExists('pgrep')) return false;
217
+ try {
218
+ // Scoped to THIS user where possible: an unscoped match would see another
219
+ // account's Kangentic on a shared machine and skip a launch the invoking
220
+ // user needs. A false positive costs a silent no-op, which is worse than
221
+ // the extra launch a false negative costs. `process.getuid` is POSIX-only,
222
+ // so it is guarded rather than assumed even on this Linux-only path.
223
+ const userScope = typeof process.getuid === 'function' ? ['-U', String(process.getuid())] : [];
224
+ execFileSync('pgrep', ['-x', ...userScope, 'kangentic'], { stdio: 'ignore' });
225
+ return true;
226
+ } catch {
227
+ // Exit 1 means no match; any other failure is equally "not confirmed".
228
+ return false;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Whether to tell the user to quit and reopen the app after installing.
234
+ *
235
+ * True only for a Linux UPGRADE. Windows and macOS relaunch into the new
236
+ * version from here, so there is nothing left to finish; and a first-time
237
+ * install on any platform has no older copy still open to replace.
238
+ *
239
+ * Deliberately NOT gated on isAppRunning(): within Linux this must hold
240
+ * whether or not the pgrep probe confirmed anything, so a false negative costs
241
+ * one redundant launch rather than restoring the silence it exists to fix.
242
+ */
243
+ function shouldAdviseReopen(platformInfo, wasInstalled) {
244
+ return platformInfo.platform === 'linux' && wasInstalled;
245
+ }
246
+
204
247
  function installLinux(artifactPath) {
205
248
  console.log('Installing Kangentic...');
206
249
  if (artifactPath.endsWith('.deb')) {
@@ -222,8 +265,17 @@ function installLinux(artifactPath) {
222
265
  console.log('Running: sudo zypper install (you may be prompted for your password)');
223
266
  execFileSync('sudo', ['zypper', '--non-interactive', 'install', artifactPath], { stdio: 'inherit' });
224
267
  } else {
225
- console.log('Running: sudo rpm -i (you may be prompted for your password)');
226
- execFileSync('sudo', ['rpm', '-i', artifactPath], { stdio: 'inherit' });
268
+ // -U, not -i: `rpm -i` fails with "package is already installed" on any
269
+ // version-to-version upgrade, so on a distro with neither dnf nor zypper
270
+ // a repeat `npx kangentic` could never actually update.
271
+ //
272
+ // --replacepkgs because the app now self-updates on Linux, so it can
273
+ // already be at the version this launcher is about to install while the
274
+ // launcher's own version marker still says otherwise. Without it that
275
+ // reinstall exits non-zero and main() bails before ever launching. dnf
276
+ // and zypper already no-op in that case; this makes the fallback match.
277
+ console.log('Running: sudo rpm -U (you may be prompted for your password)');
278
+ execFileSync('sudo', ['rpm', '-Uvh', '--replacepkgs', artifactPath], { stdio: 'inherit' });
227
279
  }
228
280
  }
229
281
  console.log('Installation complete.');
@@ -413,6 +465,16 @@ async function main() {
413
465
  process.exit(1);
414
466
  }
415
467
 
468
+ // Both sampled BEFORE installing, since the install itself changes the answers.
469
+ // wasInstalled separates an upgrade from a first-time install: there is
470
+ // nothing to "finish updating" when the app was never on the machine. This
471
+ // deliberately checks the binary's presence (fs.existsSync), not
472
+ // isInstalled(): isInstalled() also compares the version marker and would
473
+ // read false on exactly the upgrade this advice exists to announce, since
474
+ // the marker still names the OLD version at this point in main().
475
+ const wasInstalled = fs.existsSync(getInstallPath(platformInfo));
476
+ const wasRunning = isAppRunning(platformInfo);
477
+
416
478
  // Install
417
479
  try {
418
480
  install(platformInfo, artifactPath);
@@ -430,6 +492,21 @@ async function main() {
430
492
  // ignore cleanup errors
431
493
  }
432
494
 
495
+ // A running instance is the case the user cannot otherwise detect: the app
496
+ // enforces a single-instance lock, so a launch from here would exit(0) and
497
+ // merely focus the OLD, pre-upgrade window with nothing to show that
498
+ // anything happened.
499
+ if (shouldAdviseReopen(platformInfo, wasInstalled)) {
500
+ // Phrased as a conditional because this fires for every Linux upgrade, not
501
+ // only a confirmed-running one. When nothing is running the launch below
502
+ // opens the new version directly, and an unconditional "quit and reopen"
503
+ // would be telling the user to undo what just happened.
504
+ console.log('If Kangentic is already open, quit and reopen it to finish updating.');
505
+ }
506
+
507
+ // Only skip the launch when a running instance was positively confirmed.
508
+ if (wasRunning) return;
509
+
433
510
  // Launch
434
511
  launch(platformInfo, targetDir, dataDir, extraArgs);
435
512
  }
@@ -452,4 +529,6 @@ module.exports = {
452
529
  getPlatformInfo,
453
530
  getArtifactFilename,
454
531
  installLinux,
532
+ isAppRunning,
533
+ shouldAdviseReopen,
455
534
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kangentic",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Install and launch Kangentic -- visual agent orchestration for Claude Code",
5
5
  "bin": {
6
6
  "kangentic": "bin/kangentic.js"