commandmate 0.10.4 → 0.11.1
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +265 -265
- package/.next/app-path-routes-manifest.json +25 -25
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +9 -9
- package/.next/react-loadable-manifest.json +4 -4
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page.js +1 -1
- package/.next/server/app/chat/page.js.nft.json +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page.js +2 -2
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page.js +1 -1
- package/.next/server/app/offline/page.js.nft.json +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page.js +1 -1
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +7 -7
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +25 -25
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3644.js +13 -0
- package/.next/server/chunks/5959.js +1 -1
- package/.next/server/chunks/646.js +2 -14
- package/.next/server/chunks/9067.js +1 -1
- package/.next/server/chunks/9355.js +13 -1
- package/.next/server/chunks/{9992.js → 9968.js} +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/4560-64b1461fd996127a.js +1 -0
- package/.next/static/chunks/{7347-a013a163a75030ec.js → 6488-d6e53d8da584e644.js} +1 -1
- package/.next/static/chunks/{8566-b8bbcfd837cc338b.js → 8566-9da32e46c9d3fd54.js} +1 -1
- package/.next/static/chunks/8676.c1ef7e2aac5f05a0.js +1 -0
- package/.next/static/chunks/{8965-872d8de254196cec.js → 8965-5ff40db68775eb01.js} +1 -1
- package/.next/static/chunks/app/chat/{page-5d20240f98c07cee.js → page-6f714339e12c3a16.js} +1 -1
- package/.next/static/chunks/app/layout-86574192bd3c0e6b.js +1 -0
- package/.next/static/chunks/app/more/{page-af1d2af13ea901d2.js → page-a496425bba427b48.js} +1 -1
- package/.next/static/chunks/app/{page-3bbe737ec910d1fb.js → page-15bfd76bcf65842d.js} +1 -1
- package/.next/static/chunks/app/repositories/{page-ace9134ac165a6de.js → page-d214522ab8792ce7.js} +1 -1
- package/.next/static/chunks/app/review/{page-331b80f4f58582bc.js → page-1dadf0b5d7129598.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-d694149a7e8649b6.js → page-96f007d986c5f327.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/{page-9ff4d065483cdfe9.js → page-6a45f3744da50d95.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3b07e3be354aae57.js +1 -0
- package/.next/static/chunks/{webpack-4fbc38b2e3abb013.js → webpack-25d4697671930f37.js} +1 -1
- package/.next/trace +10 -10
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +178 -1
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +6 -0
- package/dist/cli/types/index.d.ts +7 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/npx-runner.d.ts +88 -0
- package/dist/cli/utils/npx-runner.d.ts.map +1 -0
- package/dist/cli/utils/npx-runner.js +165 -0
- package/dist/lib/app-update/update-lock.d.ts +39 -0
- package/dist/lib/app-update/update-lock.d.ts.map +1 -0
- package/dist/lib/app-update/update-lock.js +116 -0
- package/package.json +1 -1
- package/.next/server/chunks/1104.js +0 -13
- package/.next/static/chunks/4560-2bf9cb6504cc4425.js +0 -1
- package/.next/static/chunks/8676.cc638529497c5a1e.js +0 -1
- package/.next/static/chunks/app/layout-fe5857360dfba2b9.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-932a52f8d11971f7.js +0 -1
- /package/.next/static/{uBCIYS1Hth7V4unroKqGu → WzRCDS2PLtMvg0Sjwlf0S}/_buildManifest.js +0 -0
- /package/.next/static/{uBCIYS1Hth7V4unroKqGu → WzRCDS2PLtMvg0Sjwlf0S}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAuBzE;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwP9E"}
|
|
@@ -28,11 +28,16 @@ const install_context_1 = require("../utils/install-context");
|
|
|
28
28
|
const env_setup_1 = require("../utils/env-setup");
|
|
29
29
|
const daemon_factory_1 = require("../utils/daemon-factory");
|
|
30
30
|
const npm_runner_1 = require("../utils/npm-runner");
|
|
31
|
+
const npx_runner_1 = require("../utils/npx-runner");
|
|
31
32
|
const semver_1 = require("../utils/semver");
|
|
32
33
|
const health_check_1 = require("../utils/health-check");
|
|
33
34
|
const worktree_servers_1 = require("../utils/worktree-servers");
|
|
34
35
|
const prompt_1 = require("../utils/prompt");
|
|
35
36
|
const api_client_1 = require("../utils/api-client");
|
|
37
|
+
// Relative import (not '@/'): the CLI build has no path alias / tsc-alias step.
|
|
38
|
+
// Only the abort paths of the npx relaunch touch this — success leaves the lock
|
|
39
|
+
// to expire, exactly like the global flow (Issue #1395 §5.2).
|
|
40
|
+
const update_lock_1 = require("../../lib/app-update/update-lock");
|
|
36
41
|
const logger = new logger_1.CLILogger();
|
|
37
42
|
/** npm package name (kept as a literal: never build it from a dynamic path) */
|
|
38
43
|
const PACKAGE_NAME = 'commandmate';
|
|
@@ -43,7 +48,7 @@ const PACKAGE_NAME = 'commandmate';
|
|
|
43
48
|
*/
|
|
44
49
|
async function updateCommand(options = {}) {
|
|
45
50
|
try {
|
|
46
|
-
// --- Step 0: npx gate (Issue #1319)
|
|
51
|
+
// --- Step 0: npx gate (Issue #1319 / #1395) -----------------------------
|
|
47
52
|
// `npx commandmate` runs from the npx cache, which isGlobalInstall() reports
|
|
48
53
|
// as global (Issue #1195). Without this gate step 1 lets a throwaway process
|
|
49
54
|
// rewrite the user's global install, and step 8 then fails its own check by
|
|
@@ -51,6 +56,14 @@ async function updateCommand(options = {}) {
|
|
|
51
56
|
// The gate covers --check too: "current version" under npx is whatever npx
|
|
52
57
|
// happened to cache, which says nothing about the user's install.
|
|
53
58
|
if ((0, install_context_1.isNpxExecution)()) {
|
|
59
|
+
// Issue #1395: the GUI update route spawns this with the hidden
|
|
60
|
+
// --relaunch-npx flag to actually update in place (fetch a fresh npx cache
|
|
61
|
+
// and relaunch the daemon from it). The bare user-facing `commandmate
|
|
62
|
+
// update` under npx stays the #1319 no-op (§6).
|
|
63
|
+
if (options.relaunchNpx) {
|
|
64
|
+
await runNpxSelfUpdate();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
54
67
|
printNpxGuidance();
|
|
55
68
|
process.exit(types_1.ExitCode.SUCCESS);
|
|
56
69
|
return;
|
|
@@ -244,6 +257,134 @@ async function updateCommand(options = {}) {
|
|
|
244
257
|
process.exit(types_1.ExitCode.UNEXPECTED_ERROR);
|
|
245
258
|
}
|
|
246
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* npx in-place self-update orchestrator (Issue #1395, design §2.1).
|
|
262
|
+
*
|
|
263
|
+
* Runs detached from the server it replaces (the GUI route spawns it with
|
|
264
|
+
* `detached + unref`), so stopping the old daemon does not kill this process.
|
|
265
|
+
* The sequence is fail-fast: the new version is fetched and verified BEFORE the
|
|
266
|
+
* old daemon is stopped, so a stale/failed fetch aborts with zero downtime.
|
|
267
|
+
*
|
|
268
|
+
* Lock policy (§5.2): the route already holds the update lock. Abort paths here
|
|
269
|
+
* release it so a retry is not blocked for 10 minutes; a successful relaunch
|
|
270
|
+
* leaves it to expire, exactly like the global flow (a competing retry must not
|
|
271
|
+
* stop the freshly-started daemon). npx env hygiene lives inside npx-runner.
|
|
272
|
+
*/
|
|
273
|
+
async function runNpxSelfUpdate() {
|
|
274
|
+
// .env must exist and be loaded before status/auth resolution so the health
|
|
275
|
+
// check URL and token resolve correctly (mirrors step 6).
|
|
276
|
+
const envPath = (0, env_setup_1.getEnvPath)();
|
|
277
|
+
if (!(0, fs_1.existsSync)(envPath)) {
|
|
278
|
+
logger.error('No .env configuration was found, so the server cannot be relaunched.');
|
|
279
|
+
logger.info('Nothing was changed.');
|
|
280
|
+
printNpxManualRelaunch();
|
|
281
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
282
|
+
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
(0, dotenv_1.config)({ path: envPath });
|
|
286
|
+
// Expected version = the registry's latest (honours the user's .npmrc).
|
|
287
|
+
const view = (0, npm_runner_1.viewLatestVersion)(PACKAGE_NAME);
|
|
288
|
+
if (!view.success || !view.version) {
|
|
289
|
+
logger.error(`Failed to query the npm registry: ${view.error ?? 'unknown error'}`);
|
|
290
|
+
logger.info('Nothing was changed. Check your network/npm configuration and try again.');
|
|
291
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
292
|
+
process.exit(types_1.ExitCode.UPDATE_FAILED);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const expected = view.version;
|
|
296
|
+
// --- Warmup + version verify BEFORE the stop (fail-fast, zero downtime) ----
|
|
297
|
+
logger.info(`Fetching ${PACKAGE_NAME}@latest via npx (this may take a minute)...`);
|
|
298
|
+
const warm = (0, npx_runner_1.warmNpxLatest)(PACKAGE_NAME);
|
|
299
|
+
if (!warm.success || !warm.version) {
|
|
300
|
+
logger.error(`Failed to fetch the latest version via npx: ${warm.error ?? 'unknown error'}`);
|
|
301
|
+
printNpxStaleGuidance();
|
|
302
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
303
|
+
process.exit(types_1.ExitCode.UPDATE_FAILED);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (stripV(warm.version) !== stripV(expected)) {
|
|
307
|
+
logger.error(`npx returned v${warm.version} but the registry latest is v${expected} (a stale npx cache is likely).`);
|
|
308
|
+
printNpxStaleGuidance();
|
|
309
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
310
|
+
process.exit(types_1.ExitCode.UPDATE_FAILED);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// --- Stop the current daemon (a brief outage starts here) ------------------
|
|
314
|
+
const daemonManager = (0, daemon_factory_1.getDaemonManagerFactory)().create();
|
|
315
|
+
const wasRunning = await daemonManager.isRunning();
|
|
316
|
+
if (wasRunning) {
|
|
317
|
+
logger.info('Stopping the current server...');
|
|
318
|
+
const stopped = await daemonManager.stop();
|
|
319
|
+
if (!stopped) {
|
|
320
|
+
logger.error('Failed to stop the server. Update aborted (nothing was changed).');
|
|
321
|
+
logger.info(`Run "${PACKAGE_NAME} stop --force" and try again.`);
|
|
322
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
323
|
+
process.exit(types_1.ExitCode.STOP_FAILED);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
logger.success('Server stopped');
|
|
327
|
+
}
|
|
328
|
+
// --- Relaunch from the freshly-cached version ------------------------------
|
|
329
|
+
logger.info(`Starting ${PACKAGE_NAME}@latest from the npx cache...`);
|
|
330
|
+
const relaunch = (0, npx_runner_1.spawnNpxDaemon)(PACKAGE_NAME);
|
|
331
|
+
if (!relaunch.success) {
|
|
332
|
+
logger.error(`Failed to start the new server: ${relaunch.error ?? 'unknown error'}`);
|
|
333
|
+
// The old server was stopped, so it is down: release the lock so the user
|
|
334
|
+
// can retry immediately.
|
|
335
|
+
printNpxManualRelaunch();
|
|
336
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
337
|
+
process.exit(types_1.ExitCode.START_FAILED);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
// (a) liveness is a precondition for the HTTP probe
|
|
341
|
+
if (!(await daemonManager.isRunning())) {
|
|
342
|
+
logger.error('The new server could not be detected after the relaunch.');
|
|
343
|
+
printNpxManualRelaunch();
|
|
344
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
345
|
+
process.exit(types_1.ExitCode.START_FAILED);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
// (b) base URL comes from getStatus(), never re-derived here (D-13)
|
|
349
|
+
const status = await daemonManager.getStatus();
|
|
350
|
+
const baseUrl = status?.url;
|
|
351
|
+
if (!baseUrl) {
|
|
352
|
+
logger.error('The server started but its address could not be confirmed.');
|
|
353
|
+
printNpxManualRelaunch();
|
|
354
|
+
(0, update_lock_1.releaseUpdateLock)();
|
|
355
|
+
process.exit(types_1.ExitCode.START_FAILED);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
// Post-start version verify (belt-and-suspenders, §4.2). A mismatch is a
|
|
359
|
+
// warning, not a rollback: the new daemon is up and the GUI will reload onto
|
|
360
|
+
// whatever it is now running.
|
|
361
|
+
if (status?.version && stripV(status.version) !== stripV(expected)) {
|
|
362
|
+
logger.warn(`The running server reports v${status.version}, but v${expected} was expected. ` +
|
|
363
|
+
`Verify with "${PACKAGE_NAME} status".`);
|
|
364
|
+
}
|
|
365
|
+
// (c) poll until readiness is proven
|
|
366
|
+
logger.info('Waiting for the server to become ready...');
|
|
367
|
+
const readiness = await (0, health_check_1.waitForReady)(baseUrl, { token: (0, api_client_1.resolveAuthToken)() });
|
|
368
|
+
if (readiness === 'ready') {
|
|
369
|
+
logger.success(`CommandMate v${status?.version ?? expected} is ready at ${baseUrl}`);
|
|
370
|
+
printNpxPostUpdateNotes();
|
|
371
|
+
process.exit(types_1.ExitCode.SUCCESS);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// (d) degrade: the server answers but readiness cannot be proven (auth/IP/TLS)
|
|
375
|
+
if (readiness === 'degraded') {
|
|
376
|
+
logger.warn('サーバは応答していますが、マイグレーション完了は確認できませんでした(認証有効時はトークンが必要です)。');
|
|
377
|
+
logger.info(`"${PACKAGE_NAME} status" で確認してください。`);
|
|
378
|
+
printNpxPostUpdateNotes();
|
|
379
|
+
process.exit(types_1.ExitCode.SUCCESS);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// (e) timeout: the relaunch itself succeeded and the new daemon is up, so the
|
|
383
|
+
// lock is left to expire (a retry must not stop the fresh server).
|
|
384
|
+
logger.error('The server started but did not answer in time.');
|
|
385
|
+
printNpxManualRelaunch();
|
|
386
|
+
process.exit(types_1.ExitCode.START_FAILED);
|
|
387
|
+
}
|
|
247
388
|
/**
|
|
248
389
|
* Read the version from the installed package.json.
|
|
249
390
|
* @throws if package.json cannot be read or has no version
|
|
@@ -295,6 +436,42 @@ function printNpxGuidance() {
|
|
|
295
436
|
console.log('(stop the server -> install -> restart).');
|
|
296
437
|
console.log('');
|
|
297
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Guidance when npx could not fetch the expected latest (Issue #1395).
|
|
441
|
+
* The old server is untouched, so this only tells the user how to retry.
|
|
442
|
+
*/
|
|
443
|
+
function printNpxStaleGuidance() {
|
|
444
|
+
console.log('');
|
|
445
|
+
console.log('The latest version could not be fetched cleanly (a stale npx cache is likely).');
|
|
446
|
+
console.log('Nothing was changed — the current server is still running.');
|
|
447
|
+
console.log('Try again in a moment, or clear the npx/npm cache first:');
|
|
448
|
+
console.log(` npm cache clean --force`);
|
|
449
|
+
console.log(` npx --yes --prefer-online ${PACKAGE_NAME}@latest`);
|
|
450
|
+
console.log('');
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Guidance when the relaunch could not be completed/confirmed (Issue #1395).
|
|
454
|
+
* Under npx there is no global install to roll back to; the recovery path is to
|
|
455
|
+
* relaunch with npx by hand. Config/db under ~/.commandmate are preserved.
|
|
456
|
+
*/
|
|
457
|
+
function printNpxManualRelaunch() {
|
|
458
|
+
console.log('');
|
|
459
|
+
console.log('Finish the update by relaunching the server manually:');
|
|
460
|
+
console.log(` npx ${PACKAGE_NAME}@latest`);
|
|
461
|
+
console.log('Your configuration and database under ~/.commandmate are preserved.');
|
|
462
|
+
console.log('');
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Completion notes for the npx relaunch (Issue #1395 §7.2).
|
|
466
|
+
* Startup options are not fully re-derived from CLI flags after a relaunch, so
|
|
467
|
+
* point the user at status rather than asserting anything was lost/kept.
|
|
468
|
+
*/
|
|
469
|
+
function printNpxPostUpdateNotes() {
|
|
470
|
+
console.log('');
|
|
471
|
+
console.log('Note: 再起動後は .env と引き継がれた設定で起動しています。');
|
|
472
|
+
console.log(` ${PACKAGE_NAME} status で稼働先(ポート等)を確認してください。`);
|
|
473
|
+
console.log('');
|
|
474
|
+
}
|
|
298
475
|
/**
|
|
299
476
|
* Manual upgrade steps for non-global (development checkout) installs.
|
|
300
477
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AA0D5C;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAkKtC"}
|
package/dist/cli/program.js
CHANGED
|
@@ -172,10 +172,16 @@ function buildProgram() {
|
|
|
172
172
|
.description('Update CommandMate to the latest version')
|
|
173
173
|
.option('--check', 'Only check for updates (no install, stop or restart)')
|
|
174
174
|
.option('-y, --yes', 'Skip the confirmation prompt (required for non-interactive use)')
|
|
175
|
+
// Issue #1395: hidden — the GUI update route uses this to relaunch an
|
|
176
|
+
// npx-launched server from a fresh npx cache. Hidden so the user-facing
|
|
177
|
+
// `commandmate update` under npx stays a no-op (§6). Fixed argv, no request
|
|
178
|
+
// input reaches it (§5).
|
|
179
|
+
.addOption(new commander_1.Option('--relaunch-npx', 'Internal: relaunch an npx server from a fresh cache').hideHelp())
|
|
175
180
|
.action(async (options) => {
|
|
176
181
|
await (0, update_1.updateCommand)({
|
|
177
182
|
check: options.check,
|
|
178
183
|
yes: options.yes,
|
|
184
|
+
relaunchNpx: options.relaunchNpx,
|
|
179
185
|
});
|
|
180
186
|
});
|
|
181
187
|
// Issue #264: issue/docs commands (addCommand pattern for subcommand support)
|
|
@@ -89,6 +89,13 @@ export interface UpdateOptions {
|
|
|
89
89
|
check?: boolean;
|
|
90
90
|
/** Skip the confirmation prompt (required for non-interactive execution, D-2) */
|
|
91
91
|
yes?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Hidden flag (Issue #1395): under npx, stop the current daemon and relaunch it
|
|
94
|
+
* from a freshly-fetched `npx commandmate@latest` cache instead of the no-op
|
|
95
|
+
* guidance. Set only by the GUI update route; the bare user-facing
|
|
96
|
+
* `commandmate update` under npx stays a no-op (§6).
|
|
97
|
+
*/
|
|
98
|
+
relaunchNpx?: boolean;
|
|
92
99
|
}
|
|
93
100
|
/**
|
|
94
101
|
* Daemon process status
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,oBAAY,QAAQ;IAClB,OAAO,IAAI;IACX,gBAAgB,IAAI;IACpB,YAAY,IAAI;IAChB,YAAY,IAAI;IAChB,WAAW,IAAI;IACf,8FAA8F;IAC9F,aAAa,IAAI;IACjB,gBAAgB,KAAK;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iFAAiF;IACjF,GAAG,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npx Execution Wrapper
|
|
3
|
+
* Issue #1395: GUI one-click self-update for npx-launched servers.
|
|
4
|
+
*
|
|
5
|
+
* The npx self-update fetches a fresh npx cache and relaunches the daemon from
|
|
6
|
+
* it (design §2.1), rather than mutating a global install (rejected Alt C). This
|
|
7
|
+
* module owns the two npx spawns and the env hygiene they need:
|
|
8
|
+
*
|
|
9
|
+
* - `warmNpxLatest` — force-download the latest and read back its version, so
|
|
10
|
+
* staleness can be caught *before* the old daemon is stopped (design §4.2).
|
|
11
|
+
* - `spawnNpxDaemon` — relaunch the server from the freshly-cached version.
|
|
12
|
+
* - `sanitizeNpxEnv` — strip the `npm_*` env a `npm run start` parent leaks, so
|
|
13
|
+
* it cannot skew npx's registry/cache/prefix resolution (design §7.3).
|
|
14
|
+
*
|
|
15
|
+
* MF-SEC-1 (mirrors npm-runner): every command uses spawnSync with array args,
|
|
16
|
+
* never a shell string. `packageName` is always a literal constant supplied by
|
|
17
|
+
* the caller (update.ts PACKAGE_NAME) — never built from request input (§5).
|
|
18
|
+
*
|
|
19
|
+
* @module npx-runner
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Upper bound for the warmup download.
|
|
23
|
+
*
|
|
24
|
+
* The warmup runs *before* the old daemon is stopped, so hitting this bound
|
|
25
|
+
* aborts with zero downtime (design §4.2). Kept comfortably under the banner's
|
|
26
|
+
* UPDATE_TIMEOUT_MS (5 min, UpdateNotificationBanner.tsx) so a stalled download
|
|
27
|
+
* aborts and logs before the GUI gives up (design §8.2 / §9-8).
|
|
28
|
+
*/
|
|
29
|
+
export declare const NPX_WARMUP_TIMEOUT_MS: number;
|
|
30
|
+
/** Result of the warmup + version query */
|
|
31
|
+
export interface NpxWarmResult {
|
|
32
|
+
/** Whether the warmup succeeded and a version was read */
|
|
33
|
+
success: boolean;
|
|
34
|
+
/** Version reported by `<pkg> --version` (only when success) */
|
|
35
|
+
version?: string;
|
|
36
|
+
/** Human readable failure reason (only when !success) */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Result of the daemon relaunch */
|
|
40
|
+
export interface NpxDaemonResult {
|
|
41
|
+
/** Whether the relaunch exited 0 */
|
|
42
|
+
success: boolean;
|
|
43
|
+
/** Exit status of the npx process (null when it never ran) */
|
|
44
|
+
status: number | null;
|
|
45
|
+
/** Human readable failure reason (only when !success) */
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build an env for an npx spawn with the leaked `npm_*` variables removed.
|
|
50
|
+
*
|
|
51
|
+
* A server started by `npm run start` carries `npm_config_*` / `npm_lifecycle_*`
|
|
52
|
+
* / `npm_package_*` in its env; those flow down the inherit chain into the npx
|
|
53
|
+
* call and npx reads `npm_config_*` as configuration, which would silently
|
|
54
|
+
* redirect its registry/cache/prefix (design §7.3). Everything else — notably
|
|
55
|
+
* `CM_*` (config continuity) and `PATH` (needed to locate npx) — passes through.
|
|
56
|
+
*
|
|
57
|
+
* @param env - Source env (defaults to the current process env)
|
|
58
|
+
* @returns A fresh env object with all `npm_*` keys removed
|
|
59
|
+
*/
|
|
60
|
+
export declare function sanitizeNpxEnv(env?: Record<string, string | undefined>): NodeJS.ProcessEnv;
|
|
61
|
+
/**
|
|
62
|
+
* Force-fetch the latest published version via npx and read back its version.
|
|
63
|
+
*
|
|
64
|
+
* `npx --yes <pkg>@latest --version` downloads the latest into the npx cache
|
|
65
|
+
* (`--yes` suppresses the install prompt) and prints the package version;
|
|
66
|
+
* commander's `.version()` short-circuits before any command runs, so this has
|
|
67
|
+
* no side effects and starts no server (design §2.1). Running it before the
|
|
68
|
+
* stop lets a stale/failed fetch abort with zero downtime (design §4.2).
|
|
69
|
+
*
|
|
70
|
+
* @param packageName - Package name (a literal constant, e.g. `commandmate`)
|
|
71
|
+
* @returns The fetched version, or a classified failure
|
|
72
|
+
*/
|
|
73
|
+
export declare function warmNpxLatest(packageName: string): NpxWarmResult;
|
|
74
|
+
/**
|
|
75
|
+
* Relaunch the server as a daemon from the freshly-cached npx version.
|
|
76
|
+
*
|
|
77
|
+
* `npx --yes <pkg>@latest start --daemon` runs the just-warmed package (so no
|
|
78
|
+
* new download) and spawns `npm run start` from the new cache dir; the npx
|
|
79
|
+
* process exits once the daemon is detached. `start --daemon` is used rather
|
|
80
|
+
* than the bare quickstart, which bails in a non-interactive process (design
|
|
81
|
+
* §2.1). The call blocks until npx exits, so the exit status is the relaunch
|
|
82
|
+
* outcome. Output is echoed so it lands in the update log.
|
|
83
|
+
*
|
|
84
|
+
* @param packageName - Package name (a literal constant, e.g. `commandmate`)
|
|
85
|
+
* @returns Success, or a classified failure carrying the exit status
|
|
86
|
+
*/
|
|
87
|
+
export declare function spawnNpxDaemon(packageName: string): NpxDaemonResult;
|
|
88
|
+
//# sourceMappingURL=npx-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npx-runner.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/npx-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,QAAgB,CAAC;AAEnD,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,CAAC,UAAU,CAWnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAoChE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAyCnE"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* npx Execution Wrapper
|
|
4
|
+
* Issue #1395: GUI one-click self-update for npx-launched servers.
|
|
5
|
+
*
|
|
6
|
+
* The npx self-update fetches a fresh npx cache and relaunches the daemon from
|
|
7
|
+
* it (design §2.1), rather than mutating a global install (rejected Alt C). This
|
|
8
|
+
* module owns the two npx spawns and the env hygiene they need:
|
|
9
|
+
*
|
|
10
|
+
* - `warmNpxLatest` — force-download the latest and read back its version, so
|
|
11
|
+
* staleness can be caught *before* the old daemon is stopped (design §4.2).
|
|
12
|
+
* - `spawnNpxDaemon` — relaunch the server from the freshly-cached version.
|
|
13
|
+
* - `sanitizeNpxEnv` — strip the `npm_*` env a `npm run start` parent leaks, so
|
|
14
|
+
* it cannot skew npx's registry/cache/prefix resolution (design §7.3).
|
|
15
|
+
*
|
|
16
|
+
* MF-SEC-1 (mirrors npm-runner): every command uses spawnSync with array args,
|
|
17
|
+
* never a shell string. `packageName` is always a literal constant supplied by
|
|
18
|
+
* the caller (update.ts PACKAGE_NAME) — never built from request input (§5).
|
|
19
|
+
*
|
|
20
|
+
* @module npx-runner
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.NPX_WARMUP_TIMEOUT_MS = void 0;
|
|
24
|
+
exports.sanitizeNpxEnv = sanitizeNpxEnv;
|
|
25
|
+
exports.warmNpxLatest = warmNpxLatest;
|
|
26
|
+
exports.spawnNpxDaemon = spawnNpxDaemon;
|
|
27
|
+
const child_process_1 = require("child_process");
|
|
28
|
+
/**
|
|
29
|
+
* Upper bound for the warmup download.
|
|
30
|
+
*
|
|
31
|
+
* The warmup runs *before* the old daemon is stopped, so hitting this bound
|
|
32
|
+
* aborts with zero downtime (design §4.2). Kept comfortably under the banner's
|
|
33
|
+
* UPDATE_TIMEOUT_MS (5 min, UpdateNotificationBanner.tsx) so a stalled download
|
|
34
|
+
* aborts and logs before the GUI gives up (design §8.2 / §9-8).
|
|
35
|
+
*/
|
|
36
|
+
exports.NPX_WARMUP_TIMEOUT_MS = 3 * 60 * 1000;
|
|
37
|
+
/**
|
|
38
|
+
* Build an env for an npx spawn with the leaked `npm_*` variables removed.
|
|
39
|
+
*
|
|
40
|
+
* A server started by `npm run start` carries `npm_config_*` / `npm_lifecycle_*`
|
|
41
|
+
* / `npm_package_*` in its env; those flow down the inherit chain into the npx
|
|
42
|
+
* call and npx reads `npm_config_*` as configuration, which would silently
|
|
43
|
+
* redirect its registry/cache/prefix (design §7.3). Everything else — notably
|
|
44
|
+
* `CM_*` (config continuity) and `PATH` (needed to locate npx) — passes through.
|
|
45
|
+
*
|
|
46
|
+
* @param env - Source env (defaults to the current process env)
|
|
47
|
+
* @returns A fresh env object with all `npm_*` keys removed
|
|
48
|
+
*/
|
|
49
|
+
function sanitizeNpxEnv(env = process.env) {
|
|
50
|
+
// Built as a plain record: the project augments ProcessEnv with a required
|
|
51
|
+
// NODE_ENV, so an empty ProcessEnv literal would not typecheck.
|
|
52
|
+
const clean = {};
|
|
53
|
+
for (const [key, value] of Object.entries(env)) {
|
|
54
|
+
if (/^npm_/i.test(key)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
clean[key] = value;
|
|
58
|
+
}
|
|
59
|
+
return clean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Force-fetch the latest published version via npx and read back its version.
|
|
63
|
+
*
|
|
64
|
+
* `npx --yes <pkg>@latest --version` downloads the latest into the npx cache
|
|
65
|
+
* (`--yes` suppresses the install prompt) and prints the package version;
|
|
66
|
+
* commander's `.version()` short-circuits before any command runs, so this has
|
|
67
|
+
* no side effects and starts no server (design §2.1). Running it before the
|
|
68
|
+
* stop lets a stale/failed fetch abort with zero downtime (design §4.2).
|
|
69
|
+
*
|
|
70
|
+
* @param packageName - Package name (a literal constant, e.g. `commandmate`)
|
|
71
|
+
* @returns The fetched version, or a classified failure
|
|
72
|
+
*/
|
|
73
|
+
function warmNpxLatest(packageName) {
|
|
74
|
+
const result = (0, child_process_1.spawnSync)('npx', ['--yes', `${packageName}@latest`, '--version'], {
|
|
75
|
+
encoding: 'utf-8',
|
|
76
|
+
timeout: exports.NPX_WARMUP_TIMEOUT_MS,
|
|
77
|
+
env: sanitizeNpxEnv(),
|
|
78
|
+
});
|
|
79
|
+
if (result.error) {
|
|
80
|
+
const errnoError = result.error;
|
|
81
|
+
if (errnoError.code === 'ENOENT') {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: 'npx command not found. Install Node.js/npm and try again.',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { success: false, error: errnoError.message };
|
|
88
|
+
}
|
|
89
|
+
if (result.status !== 0) {
|
|
90
|
+
const stderr = readStream(result.stderr);
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: stderr || `npx exited with status ${String(result.status)}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const version = parseVersion(readStream(result.stdout));
|
|
97
|
+
if (!version) {
|
|
98
|
+
return { success: false, error: 'npx did not report a version' };
|
|
99
|
+
}
|
|
100
|
+
return { success: true, version };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Relaunch the server as a daemon from the freshly-cached npx version.
|
|
104
|
+
*
|
|
105
|
+
* `npx --yes <pkg>@latest start --daemon` runs the just-warmed package (so no
|
|
106
|
+
* new download) and spawns `npm run start` from the new cache dir; the npx
|
|
107
|
+
* process exits once the daemon is detached. `start --daemon` is used rather
|
|
108
|
+
* than the bare quickstart, which bails in a non-interactive process (design
|
|
109
|
+
* §2.1). The call blocks until npx exits, so the exit status is the relaunch
|
|
110
|
+
* outcome. Output is echoed so it lands in the update log.
|
|
111
|
+
*
|
|
112
|
+
* @param packageName - Package name (a literal constant, e.g. `commandmate`)
|
|
113
|
+
* @returns Success, or a classified failure carrying the exit status
|
|
114
|
+
*/
|
|
115
|
+
function spawnNpxDaemon(packageName) {
|
|
116
|
+
const result = (0, child_process_1.spawnSync)('npx', ['--yes', `${packageName}@latest`, 'start', '--daemon'], {
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
env: sanitizeNpxEnv(),
|
|
119
|
+
});
|
|
120
|
+
const stdout = readStream(result.stdout);
|
|
121
|
+
const stderr = readStream(result.stderr);
|
|
122
|
+
if (stdout) {
|
|
123
|
+
console.log(stdout);
|
|
124
|
+
}
|
|
125
|
+
if (stderr) {
|
|
126
|
+
console.error(stderr);
|
|
127
|
+
}
|
|
128
|
+
if (result.error) {
|
|
129
|
+
const errnoError = result.error;
|
|
130
|
+
if (errnoError.code === 'ENOENT') {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
status: null,
|
|
134
|
+
error: 'npx command not found. Install Node.js/npm and try again.',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return { success: false, status: result.status, error: errnoError.message };
|
|
138
|
+
}
|
|
139
|
+
if (result.status !== 0) {
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
status: result.status,
|
|
143
|
+
error: stderr || `npx start exited with status ${String(result.status)}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { success: true, status: 0 };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Extract the first semver-looking token from `--version` output.
|
|
150
|
+
* Robust against npx warning noise that may share the stream.
|
|
151
|
+
*/
|
|
152
|
+
function parseVersion(output) {
|
|
153
|
+
const match = output.match(/\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\b/);
|
|
154
|
+
return match ? match[1] : null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Read a spawnSync stream as a trimmed string.
|
|
158
|
+
* `encoding: 'utf-8'` yields strings, but mocks/edge cases may yield null.
|
|
159
|
+
*/
|
|
160
|
+
function readStream(stream) {
|
|
161
|
+
if (typeof stream === 'string') {
|
|
162
|
+
return stream.trim();
|
|
163
|
+
}
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update Lock
|
|
3
|
+
* Issue #1198: guard `POST /api/app/update` against concurrent self-updates.
|
|
4
|
+
*
|
|
5
|
+
* Design constraints:
|
|
6
|
+
* - Acquisition is a single O_EXCL create (mirrors PidManager.writePid), so two
|
|
7
|
+
* concurrent requests cannot both win.
|
|
8
|
+
* - A successful update restarts the server, so nothing survives to release the
|
|
9
|
+
* lock. Timeout expiry is therefore the primary release path, and
|
|
10
|
+
* releaseUpdateLock() exists only for the spawn-failed case.
|
|
11
|
+
*
|
|
12
|
+
* @module lib/app-update/update-lock
|
|
13
|
+
*/
|
|
14
|
+
/** Lock file name inside the config directory */
|
|
15
|
+
export declare const UPDATE_LOCK_FILENAME = "update.lock";
|
|
16
|
+
/**
|
|
17
|
+
* Age after which a lock is considered abandoned.
|
|
18
|
+
* `commandmate update` takes tens of seconds (npm install dominates); 10 minutes
|
|
19
|
+
* is well clear of that while still recovering from a killed update process.
|
|
20
|
+
*/
|
|
21
|
+
export declare const UPDATE_LOCK_TIMEOUT_MS: number;
|
|
22
|
+
/**
|
|
23
|
+
* Absolute path of the update lock file.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getUpdateLockPath(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Take the update lock.
|
|
28
|
+
*
|
|
29
|
+
* @param now - Current epoch ms (injectable for tests)
|
|
30
|
+
* @returns true when the caller now holds the lock, false when an update is in progress
|
|
31
|
+
*/
|
|
32
|
+
export declare function acquireUpdateLock(now?: number): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Drop the update lock.
|
|
35
|
+
* Only meaningful when the update never started (spawn failure): a started
|
|
36
|
+
* update replaces this process, so the lock outlives it until it expires.
|
|
37
|
+
*/
|
|
38
|
+
export declare function releaseUpdateLock(): void;
|
|
39
|
+
//# sourceMappingURL=update-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-lock.d.ts","sourceRoot":"","sources":["../../../src/lib/app-update/update-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,iDAAiD;AACjD,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAiB,CAAC;AAUrD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AA0CD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,OAAO,CAoBnE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
|