mobygate 0.3.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -4,6 +4,32 @@ All notable changes to mobygate are documented here. Format loosely follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version numbers are
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.4.0] — 2026-04-19
8
+
9
+ ### Added
10
+
11
+ - **`mobygate update` command** — upgrades to the latest version and
12
+ restarts the service in one step. Detects the install mode:
13
+ - `npm install -g` install → `npm install -g mobygate@latest`
14
+ - git clone → `git pull --ff-only` + `npm install`
15
+ After the update lands, reloads the managed service (launchd /
16
+ Task Scheduler / systemd) so the new code is actually running.
17
+ Aliased as `mobygate upgrade`. Exits early with a friendly message
18
+ if you're already on the latest version.
19
+
20
+ ### Fixed
21
+
22
+ - `installed_version` in `~/.mobygate/state.json` was hardcoded to
23
+ `"0.1.0"` for every install regardless of the actual version. Now
24
+ read from `package.json` so `mobygate version` and the dashboard
25
+ don't lie about what's running. Also records `install_mode`
26
+ (`git` | `npm` | `unknown`) in the state file.
27
+ - `mobygate version` output now also shows install mode + path, so
28
+ you can tell at a glance whether a machine is running the git
29
+ clone or the npm install.
30
+ - Three duplicated `readFileSync(package.json)` call sites
31
+ collapsed into one `readPackage()` helper in bin/mobygate.js.
32
+
7
33
  ## [0.3.0] — 2026-04-19
8
34
 
9
35
  Shippable on npm. Sessions survive restarts. Logs live in a canonical
package/bin/mobygate.js CHANGED
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
22
- import { resolve, dirname, join } from 'path';
22
+ import { resolve, dirname, join, sep } from 'path';
23
23
  import { fileURLToPath } from 'url';
24
24
  import { spawnSync } from 'child_process';
25
25
  import { createInterface } from 'readline';
@@ -44,6 +44,21 @@ const REPO_ROOT = resolve(dirname(__filename), '..');
44
44
  const SERVER_LABEL = 'ai.mobygate.server';
45
45
  const AUTH_LABEL = 'ai.mobygate.auth-refresh';
46
46
 
47
+ /** Read this package's metadata once. Version is the single source of truth
48
+ * for banners, state.json, and version comparisons — no more hardcoded
49
+ * strings drifting from package.json. */
50
+ function readPackage() {
51
+ return JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8'));
52
+ }
53
+
54
+ /** Detect how mobygate is installed. Matters for `update` routing —
55
+ * git clones pull; npm-globals `npm install -g`. */
56
+ function detectInstallMode() {
57
+ if (existsSync(join(REPO_ROOT, '.git'))) return 'git';
58
+ if (REPO_ROOT.includes(`${sep}node_modules${sep}mobygate`)) return 'npm';
59
+ return 'unknown';
60
+ }
61
+
47
62
  // ---------- tiny helpers ----------
48
63
 
49
64
  const c = {
@@ -80,7 +95,7 @@ async function confirm(q, def = true) {
80
95
  // ---------- commands ----------
81
96
 
82
97
  async function cmdInit() {
83
- const pkg = JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8'));
98
+ const pkg = readPackage();
84
99
  print(banner({ version: pkg.version }));
85
100
  section('mobygate init');
86
101
  print(c.dim(`Platform: ${PLATFORM} Install path: ${REPO_ROOT}`));
@@ -210,7 +225,8 @@ async function cmdInit() {
210
225
 
211
226
  writeState({
212
227
  install_path: REPO_ROOT,
213
- installed_version: '0.1.0',
228
+ installed_version: pkg.version,
229
+ install_mode: detectInstallMode(),
214
230
  installed_at: new Date().toISOString(),
215
231
  platform: PLATFORM,
216
232
  node_bin: nodeBin,
@@ -284,7 +300,7 @@ function cmdRestart() {
284
300
  }
285
301
 
286
302
  async function cmdStatus() {
287
- const pkg = JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8'));
303
+ const pkg = readPackage();
288
304
  print(compactBanner({ version: pkg.version }));
289
305
  section('mobygate status');
290
306
  const config = loadConfig();
@@ -388,8 +404,70 @@ async function cmdUninstall() {
388
404
  }
389
405
 
390
406
  function cmdVersion() {
391
- const pkg = JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8'));
392
- print(`mobygate v${pkg.version}`);
407
+ const pkg = readPackage();
408
+ print(`mobygate v${pkg.version} · ${detectInstallMode()} install at ${REPO_ROOT}`);
409
+ }
410
+
411
+ /**
412
+ * Upgrade mobygate to the latest version from its source of truth:
413
+ * - `npm` mode → `npm install -g mobygate@latest`
414
+ * - `git` mode → `git pull` + `npm install`
415
+ * After the update lands, restart the service so the running process
416
+ * picks up the new code.
417
+ */
418
+ async function cmdUpdate() {
419
+ const pkg = readPackage();
420
+ const mode = detectInstallMode();
421
+ section('mobygate update');
422
+ print(c.dim(`Current: v${pkg.version} · ${mode} install at ${REPO_ROOT}`));
423
+
424
+ // ---- Look up latest published version on npm
425
+ info('Checking npm for the latest release...');
426
+ const view = spawnSync('npm', ['view', 'mobygate', 'version'], { encoding: 'utf8', timeout: 10_000 });
427
+ if (view.status !== 0) {
428
+ return die(`Couldn't reach npm registry: ${view.stderr?.trim() || view.error?.message || 'unknown'}`);
429
+ }
430
+ const latest = view.stdout.trim();
431
+ print(c.dim(`Latest: v${latest}`));
432
+ if (latest === pkg.version) {
433
+ return ok(`Already on v${latest}. Nothing to do.`);
434
+ }
435
+ print('');
436
+
437
+ // ---- Perform the upgrade
438
+ if (mode === 'npm') {
439
+ info(`Running \`npm install -g mobygate@latest\`...`);
440
+ const r = spawnSync('npm', ['install', '-g', 'mobygate@latest'], { stdio: 'inherit' });
441
+ if (r.status !== 0) return die('npm install failed. See output above.');
442
+ ok(`Installed mobygate@${latest}`);
443
+ } else if (mode === 'git') {
444
+ info(`Running \`git pull\` in ${REPO_ROOT}...`);
445
+ const pull = spawnSync('git', ['-C', REPO_ROOT, 'pull', '--ff-only'], { stdio: 'inherit' });
446
+ if (pull.status !== 0) return die('git pull failed. Resolve conflicts and retry.');
447
+ info(`Running \`npm install\`...`);
448
+ const install = spawnSync('npm', ['install'], { cwd: REPO_ROOT, stdio: 'inherit' });
449
+ if (install.status !== 0) return die('npm install failed. See output above.');
450
+ ok(`Pulled and installed. See git log for what changed.`);
451
+ } else {
452
+ return die(`Install mode is "${mode}" — can't auto-update. Reinstall via npm or git.`);
453
+ }
454
+
455
+ // ---- Restart the managed service so the new code is running
456
+ section('Restart');
457
+ info('Restarting service so the new build is running...');
458
+ if (IS_MAC) {
459
+ const p = plistPathForLabel(SERVER_LABEL);
460
+ launchctlUnload(p); launchctlLoad(p);
461
+ ok(`Reloaded ${SERVER_LABEL}`);
462
+ } else if (IS_WIN) {
463
+ stopWindowsTask(WIN_LABELS.server); startWindowsTask(WIN_LABELS.server);
464
+ ok(`Restarted ${WIN_LABELS.server}`);
465
+ } else if (IS_LINUX) {
466
+ stopLinuxUnit(LINUX_UNITS.server); startLinuxUnit(LINUX_UNITS.server);
467
+ ok(`Restarted ${LINUX_UNITS.server}`);
468
+ }
469
+ print('');
470
+ info(`Tip: if the install-layout changed (new service file, new paths), run \`mobygate init\` to re-install the service definitions.`);
393
471
  }
394
472
 
395
473
  function usage() {
@@ -397,6 +475,7 @@ function usage() {
397
475
 
398
476
  Usage:
399
477
  mobygate init Interactive setup: config, services, smoke test
478
+ mobygate update Upgrade to the latest version + restart service
400
479
  mobygate start Start the proxy service
401
480
  mobygate stop Stop the proxy service
402
481
  mobygate restart Stop + start
@@ -404,7 +483,7 @@ Usage:
404
483
  mobygate logs Tail the server log
405
484
  mobygate auth Show auth status + run a refresh probe
406
485
  mobygate uninstall Remove installed services
407
- mobygate version Print version
486
+ mobygate version Print version + install mode + path
408
487
 
409
488
  Config: ~/.mobygate/config.yaml (env vars override)
410
489
  Repo: ${REPO_ROOT}
@@ -416,6 +495,8 @@ Repo: ${REPO_ROOT}
416
495
  const cmd = process.argv[2];
417
496
  const COMMANDS = {
418
497
  init: cmdInit,
498
+ update: cmdUpdate,
499
+ upgrade: cmdUpdate,
419
500
  start: cmdStart,
420
501
  stop: cmdStop,
421
502
  restart: cmdRestart,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobygate",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "OpenAI-compatible local proxy for Claude Max. The Möbius-strip gateway: OpenAI shape in, Claude Max out.",
5
5
  "type": "module",
6
6
  "main": "server.js",