From 4e28966fd0fea11979b1435f8fb9850be42d45e4 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Wed, 24 Jun 2026 16:46:03 -0500 Subject: [PATCH 1/2] feat(gateway): honor MOSAIC_GATEWAY_SKIP_NPM_INSTALL to skip registry reinstall Lets dev/offline installs keep an already-present global @mosaicstack/gateway (e.g. a build-from-source `install.sh --dev`) instead of overwriting it with the registry @latest build during `mosaic wizard` / `mosaic gateway install`. Refs #675 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RMoEx7hfdFGjUiCHuN1RRi --- packages/mosaic/src/stages/gateway-config.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/mosaic/src/stages/gateway-config.ts b/packages/mosaic/src/stages/gateway-config.ts index b6abde3..38bd85d 100644 --- a/packages/mosaic/src/stages/gateway-config.ts +++ b/packages/mosaic/src/stages/gateway-config.ts @@ -294,7 +294,12 @@ export async function gatewayConfigStage( } // Install the gateway npm package on first install or after failure. - if (!opts.skipInstall && !daemonRunning) { + // MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 forces a skip even without opts.skipInstall: + // used by dev/offline installs where @mosaicstack/gateway is already present + // globally (e.g. a build-from-source `install.sh --dev`) and must not be + // overwritten by the registry @latest build. + const skipNpmInstall = opts.skipInstall || process.env['MOSAIC_GATEWAY_SKIP_NPM_INSTALL'] === '1'; + if (!skipNpmInstall && !daemonRunning) { installGatewayPackage(); } -- 2.49.1 From 4b0137325be6357c65b168ce9b97c9fc98b4e0ef Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 9 Jul 2026 15:20:47 -0500 Subject: [PATCH 2/2] test(wizard): cover MOSAIC_GATEWAY_SKIP_NPM_INSTALL gate in gateway-config stage Two cases: fresh install without skipInstall calls installGatewayPackage exactly once (baseline), and MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 suppresses the registry install so a build-from-source gateway (install.sh --dev) is not overwritten by @latest. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RMoEx7hfdFGjUiCHuN1RRi --- .../mosaic/src/stages/gateway-config.spec.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/mosaic/src/stages/gateway-config.spec.ts b/packages/mosaic/src/stages/gateway-config.spec.ts index 52554ac..befc5d2 100644 --- a/packages/mosaic/src/stages/gateway-config.spec.ts +++ b/packages/mosaic/src/stages/gateway-config.spec.ts @@ -136,6 +136,7 @@ describe('gatewayConfigStage', () => { delete process.env['MOSAIC_STORAGE_TIER']; delete process.env['MOSAIC_DATABASE_URL']; delete process.env['MOSAIC_VALKEY_URL']; + delete process.env['MOSAIC_GATEWAY_SKIP_NPM_INSTALL']; }); afterEach(() => { @@ -167,6 +168,36 @@ describe('gatewayConfigStage', () => { expect(state.gateway?.regeneratedConfig).toBe(true); }); + it('installs the gateway package on fresh install when skipInstall is not set', async () => { + const p = buildPrompter(); + const state = makeState('/home/user/.config/mosaic'); + + const result = await gatewayConfigStage(p, state, { + host: 'localhost', + defaultPort: 14242, + skipInstall: false, + }); + + expect(result.ready).toBe(true); + expect(daemonState.installPkgCalled).toBe(1); + }); + + it('honors MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 and skips the registry install (dev/offline installs)', async () => { + process.env['MOSAIC_GATEWAY_SKIP_NPM_INSTALL'] = '1'; + const p = buildPrompter(); + const state = makeState('/home/user/.config/mosaic'); + + const result = await gatewayConfigStage(p, state, { + host: 'localhost', + defaultPort: 14242, + skipInstall: false, + }); + + // The source-built global gateway must NOT be overwritten by @latest. + expect(result.ready).toBe(true); + expect(daemonState.installPkgCalled).toBe(0); + }); + it('does not ask for a gateway API key when provider setup was completed with no key', async () => { delete process.env['MOSAIC_ASSUME_YES']; const originalIsTTY = process.stdin.isTTY; -- 2.49.1