@xopcai/xopc 0.0.88 → 0.0.90
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 +8 -1
- package/README.zh-CN.md +8 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-cPvvYLXo.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-Bk1_P5FJ.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-CZoeQwHz.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DIsl75Y3.js → channels-status-swr-BrtH2VzC.js} +1 -1
- package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +1 -0
- package/dist/gateway/static/root/assets/cron-api-CyqbgfHM.js +1 -0
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +2 -0
- package/dist/gateway/static/root/assets/cron-page-BpLdiQN8.js +1 -0
- package/dist/gateway/static/root/assets/dist-BpAiK86n.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BVJohZoZ.js → extension-debug-page-D6Ak0STa.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BT2tmElC.js → extension-page-Q0P3d6DW.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BSS47c2j.js → extension-settings-page-CL55LwU_.js} +1 -1
- package/dist/gateway/static/root/assets/eye-DAfL1U7M.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-BaFNUtkE.js → fetch-Dqa9iTWl.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-QwYEq6Hz.js → field-primitives-HUR6JElP.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BVSidEDJ.js → heartbeat-config-api-DusckjUX.js} +1 -1
- package/dist/gateway/static/root/assets/{index-qNrVJp-y.js → index-BYcGfwcE.js} +97 -97
- package/dist/gateway/static/root/assets/index-V7MQ7834.css +1 -0
- package/dist/gateway/static/root/assets/{logs-page-DDonPVLn.js → logs-page-_HcZ2fgK.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-iezSMjho.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-B8N3A3Zo.js → settings-form-section-a0qGVOlr.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-C9_nYQwM.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-Q7KqkO-u.js → share-preview-page-DExl7CJy.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-BlgGD93t.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-BbRc5ugR.js → theme-store-C0Ehmdo5.js} +1 -1
- package/dist/gateway/static/root/assets/url-fxyYANfA.js +3 -0
- package/dist/gateway/static/root/assets/{utils-CxDGduqK.js → utils-DRQryzdn.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-D0viACE2.js +1 -0
- package/dist/gateway/static/root/assets/workflow-page.utils-DnG8JBhV.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-BvMobnJP.js +27 -0
- package/dist/gateway/static/root/index.html +7 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +2 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/service.js +2 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +3 -1
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +20 -18
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
- package/dist/src/agent/tools/cronjob-tool.d.ts +6 -0
- package/dist/src/agent/tools/cronjob-tool.js +76 -10
- package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
- package/dist/src/agent/tools/edit.d.ts +5 -1
- package/dist/src/agent/tools/edit.js +7 -5
- package/dist/src/agent/tools/edit.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -0
- package/dist/src/agent/tools/factory.js +4 -25
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.d.ts +6 -28
- package/dist/src/agent/tools/workflow-tool.js +60 -260
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/tools/write.d.ts +5 -1
- package/dist/src/agent/tools/write.js +7 -5
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/agent/workflow/agent-progress.js +2 -0
- package/dist/src/agent/workflow/agent-progress.js.map +1 -1
- package/dist/src/agent/workflow/builtins/client-proposal.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/client-proposal.js +155 -0
- package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.js +150 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -0
- package/dist/src/agent/workflow/builtins/content-draft.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/content-draft.js +146 -0
- package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.js +137 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -0
- package/dist/src/agent/workflow/builtins/decision-compare.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/decision-compare.js +173 -0
- package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.js +148 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +10 -1
- package/dist/src/agent/workflow/builtins/index.js +46 -1
- package/dist/src/agent/workflow/builtins/index.js.map +1 -1
- package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/meeting-prep.js +144 -0
- package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -0
- package/dist/src/agent/workflow/builtins/offer-design.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/offer-design.js +161 -0
- package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -0
- package/dist/src/agent/workflow/builtins/weekly-review.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/weekly-review.js +131 -0
- package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -0
- package/dist/src/agent/workflow/step-labels.js +2 -2
- package/dist/src/agent/workflow/step-labels.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.js +3 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +4 -0
- package/dist/src/agent/workflow/workflow-child-tools.d.ts +4 -0
- package/dist/src/agent/workflow/workflow-child-tools.js +21 -0
- package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -0
- package/dist/src/auth/credentials.d.ts +14 -2
- package/dist/src/auth/credentials.js +38 -13
- package/dist/src/auth/credentials.js.map +1 -1
- package/dist/src/auth/oauth/types.d.ts +16 -0
- package/dist/src/chat-commands/agent-edit.d.ts +4 -0
- package/dist/src/chat-commands/agent-edit.js +136 -0
- package/dist/src/chat-commands/agent-edit.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/bin.js +2 -0
- package/dist/src/cli/bin.js.map +1 -1
- package/dist/src/cli/commands/auth.js +6 -0
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/cron.js +42 -3
- package/dist/src/cli/commands/cron.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +79 -56
- package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +6 -0
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/update.js +86 -79
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/commands/agents.config.d.ts +3 -2
- package/dist/src/commands/agents.config.js +5 -2
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-typed-models.d.ts +2 -7
- package/dist/src/config/agent-typed-models.js +3 -14
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/localized-text.d.ts +6 -0
- package/dist/src/config/localized-text.js +42 -0
- package/dist/src/config/localized-text.js.map +1 -0
- package/dist/src/config/models-json.d.ts +6 -6
- package/dist/src/config/schema.d.ts +6 -21
- package/dist/src/config/schema.js +4 -4
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.d.ts +4 -0
- package/dist/src/cron/executor.js +169 -5
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/job-content.js +2 -1
- package/dist/src/cron/job-content.js.map +1 -1
- package/dist/src/cron/types.d.ts +28 -1
- package/dist/src/cron/validation.d.ts +80 -0
- package/dist/src/cron/validation.js +30 -4
- package/dist/src/cron/validation.js.map +1 -1
- package/dist/src/cron/workflow-run-completion.d.ts +23 -0
- package/dist/src/cron/workflow-run-completion.js +72 -0
- package/dist/src/cron/workflow-run-completion.js.map +1 -0
- package/dist/src/extensions/update.d.ts +51 -0
- package/dist/src/extensions/update.js +260 -0
- package/dist/src/extensions/update.js.map +1 -0
- package/dist/src/gateway/agents-admin.d.ts +15 -8
- package/dist/src/gateway/agents-admin.js +77 -28
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/gateway-workflow-host.types.d.ts +17 -0
- package/dist/src/gateway/gateway-workflow-host.types.js +1 -0
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -0
- package/dist/src/gateway/hono/lib/config-payload.js +2 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +2 -0
- package/dist/src/gateway/hono/middleware/auth.js +12 -7
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/oauth-async.js +40 -15
- package/dist/src/gateway/hono/oauth-async.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +31 -6
- package/dist/src/gateway/hono/oauth.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +55 -12
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +1 -1
- package/dist/src/gateway/hono/routes/models.js +11 -5
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.js +55 -107
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +72 -191
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/server.js +2 -0
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +5 -0
- package/dist/src/gateway/service.js +24 -3
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/brew.d.ts +4 -0
- package/dist/src/infra/brew.js +20 -0
- package/dist/src/infra/brew.js.map +1 -0
- package/dist/src/infra/package-json.d.ts +2 -0
- package/dist/src/infra/package-json.js +23 -0
- package/dist/src/infra/package-json.js.map +1 -0
- package/dist/src/infra/package-update-steps.d.ts +35 -0
- package/dist/src/infra/package-update-steps.js +304 -0
- package/dist/src/infra/package-update-steps.js.map +1 -0
- package/dist/src/infra/path-env.d.ts +11 -0
- package/dist/src/infra/path-env.js +90 -0
- package/dist/src/infra/path-env.js.map +1 -0
- package/dist/src/infra/path-prepend.d.ts +7 -0
- package/dist/src/infra/path-prepend.js +44 -0
- package/dist/src/infra/path-prepend.js.map +1 -0
- package/dist/src/infra/stable-node-path.d.ts +2 -0
- package/dist/src/infra/stable-node-path.js +28 -0
- package/dist/src/infra/stable-node-path.js.map +1 -0
- package/dist/src/infra/update-global.d.ts +30 -23
- package/dist/src/infra/update-global.js +113 -64
- package/dist/src/infra/update-global.js.map +1 -1
- package/dist/src/infra/update-log.d.ts +1 -0
- package/dist/src/infra/update-log.js +12 -0
- package/dist/src/infra/update-log.js.map +1 -0
- package/dist/src/infra/update-restart.d.ts +20 -0
- package/dist/src/infra/update-restart.js +165 -0
- package/dist/src/infra/update-restart.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +89 -1
- package/dist/src/infra/update-runner.js +604 -173
- package/dist/src/infra/update-runner.js.map +1 -1
- package/dist/src/infra/update-startup.d.ts +3 -0
- package/dist/src/infra/update-startup.js +8 -4
- package/dist/src/infra/update-startup.js.map +1 -1
- package/dist/src/providers/index.d.ts +8 -0
- package/dist/src/providers/index.js +51 -12
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/routing/resolve-route.d.ts +3 -1
- package/dist/src/routing/resolve-route.js.map +1 -1
- package/dist/src/session/store.d.ts +5 -3
- package/dist/src/session/store.js +66 -20
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/share/site-share-config.d.ts +3 -2
- package/dist/src/share/site-share-config.js.map +1 -1
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/workflows/domain/command.d.ts +2 -1
- package/dist/src/workflows/domain/definition-utils.d.ts +14 -0
- package/dist/src/workflows/domain/definition-utils.js +50 -0
- package/dist/src/workflows/domain/definition-utils.js.map +1 -0
- package/dist/src/workflows/domain/event.d.ts +3 -0
- package/dist/src/workflows/domain/index.d.ts +2 -0
- package/dist/src/workflows/domain/index.js +3 -1
- package/dist/src/workflows/domain/run.d.ts +60 -0
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/domain/validation.d.ts +19 -0
- package/dist/src/workflows/domain/validation.js +66 -0
- package/dist/src/workflows/domain/validation.js.map +1 -0
- package/dist/src/workflows/engine/projector.js +17 -0
- package/dist/src/workflows/engine/projector.js.map +1 -1
- package/dist/src/workflows/engine/workflow-engine.d.ts +2 -1
- package/dist/src/workflows/engine/workflow-engine.js +128 -0
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/index.d.ts +4 -0
- package/dist/src/workflows/index.js +9 -2
- package/dist/src/workflows/service/run-view-to-snapshot.d.ts +4 -0
- package/dist/src/workflows/service/run-view-to-snapshot.js +63 -0
- package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -0
- package/dist/src/workflows/service/workflow-run-service.d.ts +37 -0
- package/dist/src/workflows/service/workflow-run-service.js +282 -0
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -0
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +47 -0
- package/dist/src/workflows/service/workflow-run-service.types.js +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +29 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +177 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -0
- package/dist/src/workflows/service/workflow-session-key.d.ts +3 -0
- package/dist/src/workflows/service/workflow-session-key.js +21 -0
- package/dist/src/workflows/service/workflow-session-key.js.map +1 -0
- package/dist/src/workflows/store/run-store.js +1 -0
- package/dist/src/workflows/store/run-store.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-CRxETUZx.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-wKWf3l57.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-DDbqVNkx.js +0 -1
- package/dist/gateway/static/root/assets/copy-SxMW6Xpc.js +0 -1
- package/dist/gateway/static/root/assets/cron-api-N9hvuRrn.js +0 -1
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-DueM3rBz.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-tlNGNxhP.js +0 -1
- package/dist/gateway/static/root/assets/dist-CJwfHYvT.js +0 -1
- package/dist/gateway/static/root/assets/index-CqZzHNEg.css +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DKt-Wmib.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-DcJjvvw4.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-DuJ4BTO3.js +0 -2
- package/dist/gateway/static/root/assets/url-D6jvVYIA.js +0 -7
- package/dist/gateway/static/root/assets/voice-api-key-field-CTyHz7L_.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-GacJ41Fv.js +0 -27
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { resolveExecPathBinPrepend } from "./path-env.js";
|
|
2
|
+
import { readPackageVersion } from "./package-json.js";
|
|
3
|
+
import { applyPathPrepend } from "./path-prepend.js";
|
|
1
4
|
import { createDefaultCommandRunner } from "./run-command.js";
|
|
2
5
|
import fs from "node:fs";
|
|
3
6
|
import fs$1 from "node:fs/promises";
|
|
4
7
|
import path from "node:path";
|
|
5
8
|
//#region src/infra/update-global.ts
|
|
6
9
|
const XOPC_PACKAGE_NAME = "@xopcai/xopc";
|
|
10
|
+
const GLOBAL_RENAME_PREFIX = ".";
|
|
7
11
|
const COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT = "0";
|
|
8
12
|
const NPM_GLOBAL_INSTALL_QUIET_FLAGS = [
|
|
9
13
|
"--no-fund",
|
|
@@ -12,7 +16,6 @@ const NPM_GLOBAL_INSTALL_QUIET_FLAGS = [
|
|
|
12
16
|
];
|
|
13
17
|
const NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = ["--omit=optional", ...NPM_GLOBAL_INSTALL_QUIET_FLAGS];
|
|
14
18
|
const GLOBAL_DETECT_TIMEOUT_MS = 15e3;
|
|
15
|
-
const GLOBAL_INSTALL_TIMEOUT_MS = 2700 * 1e3;
|
|
16
19
|
async function pathExists(targetPath) {
|
|
17
20
|
try {
|
|
18
21
|
await fs$1.access(targetPath);
|
|
@@ -48,9 +51,16 @@ function resolvePreferredNpmCommand(pkgRoot) {
|
|
|
48
51
|
const candidate = process.platform === "win32" ? path.join(prefix, "npm.cmd") : path.join(prefix, "bin", "npm");
|
|
49
52
|
return fs.existsSync(candidate) ? candidate : null;
|
|
50
53
|
}
|
|
54
|
+
function resolveNpmFromExecPath() {
|
|
55
|
+
const execPath = process.execPath?.trim();
|
|
56
|
+
if (!execPath) return null;
|
|
57
|
+
const binDir = path.dirname(execPath);
|
|
58
|
+
const candidate = process.platform === "win32" ? path.join(binDir, "npm.cmd") : path.join(binDir, "npm");
|
|
59
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
60
|
+
}
|
|
51
61
|
function resolvePreferredGlobalManagerCommand(manager, pkgRoot) {
|
|
52
62
|
if (manager !== "npm") return manager;
|
|
53
|
-
return resolvePreferredNpmCommand(pkgRoot) ?? manager;
|
|
63
|
+
return resolvePreferredNpmCommand(pkgRoot) ?? resolveNpmFromExecPath() ?? manager;
|
|
54
64
|
}
|
|
55
65
|
function resolveGlobalInstallCommand(manager, pkgRoot) {
|
|
56
66
|
return {
|
|
@@ -61,6 +71,40 @@ function resolveGlobalInstallCommand(manager, pkgRoot) {
|
|
|
61
71
|
function normalizeGlobalInstallCommand(managerOrCommand, pkgRoot) {
|
|
62
72
|
return typeof managerOrCommand === "string" ? resolveGlobalInstallCommand(managerOrCommand, pkgRoot) : managerOrCommand;
|
|
63
73
|
}
|
|
74
|
+
function resolveNpmGlobalPrefixLayoutFromGlobalRoot(globalRoot) {
|
|
75
|
+
const trimmed = globalRoot?.trim();
|
|
76
|
+
if (!trimmed) return null;
|
|
77
|
+
const normalized = path.resolve(trimmed);
|
|
78
|
+
if (path.basename(normalized) !== "node_modules") return null;
|
|
79
|
+
const parentDir = path.dirname(normalized);
|
|
80
|
+
if (path.basename(parentDir) === "lib") {
|
|
81
|
+
const prefix = path.dirname(parentDir);
|
|
82
|
+
return {
|
|
83
|
+
prefix,
|
|
84
|
+
globalRoot: normalized,
|
|
85
|
+
binDir: path.join(prefix, "bin")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (process.platform === "win32") return {
|
|
89
|
+
prefix: parentDir,
|
|
90
|
+
globalRoot: normalized,
|
|
91
|
+
binDir: parentDir
|
|
92
|
+
};
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function resolveNpmGlobalPrefixLayoutFromPrefix(prefix) {
|
|
96
|
+
const resolvedPrefix = path.resolve(prefix);
|
|
97
|
+
if (process.platform === "win32") return {
|
|
98
|
+
prefix: resolvedPrefix,
|
|
99
|
+
globalRoot: path.join(resolvedPrefix, "node_modules"),
|
|
100
|
+
binDir: resolvedPrefix
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
prefix: resolvedPrefix,
|
|
104
|
+
globalRoot: path.join(resolvedPrefix, "lib", "node_modules"),
|
|
105
|
+
binDir: path.join(resolvedPrefix, "bin")
|
|
106
|
+
};
|
|
107
|
+
}
|
|
64
108
|
function applyWindowsPackageInstallEnv(env) {
|
|
65
109
|
if (process.platform !== "win32") return;
|
|
66
110
|
env.NPM_CONFIG_UPDATE_NOTIFIER = "false";
|
|
@@ -71,10 +115,12 @@ function applyCorepackDownloadPromptEnv(env) {
|
|
|
71
115
|
if (!env.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim()) env.COREPACK_ENABLE_DOWNLOAD_PROMPT = COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT;
|
|
72
116
|
}
|
|
73
117
|
async function createGlobalInstallEnv(env) {
|
|
118
|
+
const pathPrepend = resolveExecPathBinPrepend();
|
|
74
119
|
const sourceEnv = env ?? process.env;
|
|
75
120
|
const hasCorepack = Boolean(sourceEnv.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim());
|
|
76
|
-
if (process.platform !== "win32" && hasCorepack) return env;
|
|
121
|
+
if (process.platform !== "win32" && hasCorepack && pathPrepend.length === 0) return env;
|
|
77
122
|
const merged = Object.fromEntries(Object.entries(sourceEnv).filter(([, value]) => value != null).map(([key, value]) => [key, String(value)]));
|
|
123
|
+
applyPathPrepend(merged, pathPrepend);
|
|
78
124
|
applyWindowsPackageInstallEnv(merged);
|
|
79
125
|
applyCorepackDownloadPromptEnv(merged);
|
|
80
126
|
return merged;
|
|
@@ -84,6 +130,20 @@ function resolveGlobalInstallSpec(params) {
|
|
|
84
130
|
if (override) return override;
|
|
85
131
|
return `${XOPC_PACKAGE_NAME}@${params.version}`;
|
|
86
132
|
}
|
|
133
|
+
function resolveExpectedInstalledVersionFromSpec(spec) {
|
|
134
|
+
const normalized = spec.trim();
|
|
135
|
+
if (!normalized.startsWith(`@xopcai/xopc@`)) return null;
|
|
136
|
+
const rawVersion = normalized.slice(13).trim();
|
|
137
|
+
if (!rawVersion || rawVersion.includes("/") || rawVersion.includes(":") || rawVersion.includes("#") || /^(latest|beta|next|dev)$/i.test(rawVersion)) return null;
|
|
138
|
+
return rawVersion;
|
|
139
|
+
}
|
|
140
|
+
async function collectInstalledGlobalPackageErrors(params) {
|
|
141
|
+
const errors = [];
|
|
142
|
+
const installedVersion = await readPackageVersion(params.packageRoot);
|
|
143
|
+
if (params.expectedVersion && installedVersion !== params.expectedVersion) errors.push(`expected installed version ${params.expectedVersion}, found ${installedVersion ?? "<missing>"}`);
|
|
144
|
+
if (!installedVersion) errors.push("missing package.json version");
|
|
145
|
+
return errors;
|
|
146
|
+
}
|
|
87
147
|
async function resolveGlobalRoot(managerOrCommand, runCommand, timeoutMs, pkgRoot) {
|
|
88
148
|
const res = await runCommand([
|
|
89
149
|
normalizeGlobalInstallCommand(managerOrCommand, pkgRoot).command,
|
|
@@ -93,7 +153,21 @@ async function resolveGlobalRoot(managerOrCommand, runCommand, timeoutMs, pkgRoo
|
|
|
93
153
|
if (!res || res.code !== 0) return null;
|
|
94
154
|
return res.stdout.trim() || null;
|
|
95
155
|
}
|
|
96
|
-
function
|
|
156
|
+
async function resolveGlobalPackageRoot(managerOrCommand, runCommand, timeoutMs, pkgRoot) {
|
|
157
|
+
const root = await resolveGlobalRoot(managerOrCommand, runCommand, timeoutMs, pkgRoot);
|
|
158
|
+
if (!root) return null;
|
|
159
|
+
return joinGlobalPackagePath(root);
|
|
160
|
+
}
|
|
161
|
+
async function resolveGlobalInstallTarget(params) {
|
|
162
|
+
const command = normalizeGlobalInstallCommand(params.manager, params.pkgRoot);
|
|
163
|
+
const globalRoot = await resolveGlobalRoot(command, params.runCommand, params.timeoutMs, params.pkgRoot);
|
|
164
|
+
return {
|
|
165
|
+
...command,
|
|
166
|
+
globalRoot,
|
|
167
|
+
packageRoot: globalRoot ? joinGlobalPackagePath(globalRoot) : null
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function globalInstallArgs(managerOrCommand, spec, pkgRoot, installPrefix) {
|
|
97
171
|
const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);
|
|
98
172
|
if (resolved.manager === "pnpm") return [
|
|
99
173
|
resolved.command,
|
|
@@ -105,27 +179,55 @@ function globalInstallArgs(managerOrCommand, spec, pkgRoot) {
|
|
|
105
179
|
resolved.command,
|
|
106
180
|
"install",
|
|
107
181
|
"-g",
|
|
182
|
+
...installPrefix ? ["--prefix", installPrefix] : [],
|
|
108
183
|
spec,
|
|
109
184
|
...NPM_GLOBAL_INSTALL_QUIET_FLAGS
|
|
110
185
|
];
|
|
111
186
|
}
|
|
112
|
-
function globalInstallFallbackArgs(managerOrCommand, spec, pkgRoot) {
|
|
187
|
+
function globalInstallFallbackArgs(managerOrCommand, spec, pkgRoot, installPrefix) {
|
|
113
188
|
const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);
|
|
114
189
|
if (resolved.manager !== "npm") return null;
|
|
115
190
|
return [
|
|
116
191
|
resolved.command,
|
|
117
192
|
"install",
|
|
118
193
|
"-g",
|
|
194
|
+
...installPrefix ? ["--prefix", installPrefix] : [],
|
|
119
195
|
spec,
|
|
120
196
|
...NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS
|
|
121
197
|
];
|
|
122
198
|
}
|
|
199
|
+
async function cleanupGlobalRenameDirs(params) {
|
|
200
|
+
const removed = [];
|
|
201
|
+
const root = params.globalRoot.trim();
|
|
202
|
+
const name = params.packageName.trim();
|
|
203
|
+
if (!root || !name) return { removed };
|
|
204
|
+
const prefix = `${GLOBAL_RENAME_PREFIX}${name}-`;
|
|
205
|
+
let entries = [];
|
|
206
|
+
try {
|
|
207
|
+
entries = await fs$1.readdir(root);
|
|
208
|
+
} catch {
|
|
209
|
+
return { removed };
|
|
210
|
+
}
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
if (!entry.startsWith(prefix)) continue;
|
|
213
|
+
const target = path.join(root, entry);
|
|
214
|
+
try {
|
|
215
|
+
if (!(await fs$1.lstat(target)).isDirectory()) continue;
|
|
216
|
+
await fs$1.rm(target, {
|
|
217
|
+
recursive: true,
|
|
218
|
+
force: true
|
|
219
|
+
});
|
|
220
|
+
removed.push(entry);
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
return { removed };
|
|
224
|
+
}
|
|
123
225
|
async function detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs) {
|
|
124
226
|
const pkgReal = await tryRealpath(pkgRoot);
|
|
125
|
-
|
|
227
|
+
const candidates = [{
|
|
126
228
|
manager: "npm",
|
|
127
229
|
argv: [
|
|
128
|
-
"npm",
|
|
230
|
+
resolvePreferredGlobalManagerCommand("npm", pkgRoot),
|
|
129
231
|
"root",
|
|
130
232
|
"-g"
|
|
131
233
|
]
|
|
@@ -136,7 +238,8 @@ async function detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs)
|
|
|
136
238
|
"root",
|
|
137
239
|
"-g"
|
|
138
240
|
]
|
|
139
|
-
}]
|
|
241
|
+
}];
|
|
242
|
+
for (const { manager, argv } of candidates) {
|
|
140
243
|
const res = await runCommand(argv, { timeoutMs }).catch(() => null);
|
|
141
244
|
if (!res || res.code !== 0) continue;
|
|
142
245
|
const globalRoot = res.stdout.trim();
|
|
@@ -144,7 +247,7 @@ async function detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs)
|
|
|
144
247
|
const expectedReal = await tryRealpath(joinGlobalPackagePath(await tryRealpath(globalRoot)));
|
|
145
248
|
if (path.resolve(expectedReal) === path.resolve(pkgReal)) return manager;
|
|
146
249
|
}
|
|
147
|
-
if (resolvePreferredNpmCommand(pkgRoot)) return "npm";
|
|
250
|
+
if (resolvePreferredNpmCommand(pkgRoot) || resolveNpmFromExecPath()) return "npm";
|
|
148
251
|
return null;
|
|
149
252
|
}
|
|
150
253
|
async function detectGlobalInstallManagerByPresence(runCommand, timeoutMs) {
|
|
@@ -164,61 +267,7 @@ async function resolveGlobalManager(params) {
|
|
|
164
267
|
}
|
|
165
268
|
return await detectGlobalInstallManagerByPresence(runCommand, timeoutMs) ?? "npm";
|
|
166
269
|
}
|
|
167
|
-
function tailOutput(text, max = 4e3) {
|
|
168
|
-
const t = text.trim();
|
|
169
|
-
if (t.length <= max) return t;
|
|
170
|
-
return t.slice(-max);
|
|
171
|
-
}
|
|
172
|
-
async function runGlobalPackageInstall(params) {
|
|
173
|
-
const timeoutMs = params.timeoutMs ?? GLOBAL_INSTALL_TIMEOUT_MS;
|
|
174
|
-
const runCommand = createDefaultCommandRunner();
|
|
175
|
-
const installEnv = await createGlobalInstallEnv();
|
|
176
|
-
const runStep = async (argv) => {
|
|
177
|
-
const result = await runCommand(argv, {
|
|
178
|
-
timeoutMs,
|
|
179
|
-
env: installEnv
|
|
180
|
-
});
|
|
181
|
-
if (params.echoToTerminal) {
|
|
182
|
-
if (result.stdout) process.stdout.write(result.stdout);
|
|
183
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
184
|
-
}
|
|
185
|
-
return result;
|
|
186
|
-
};
|
|
187
|
-
const primary = await runStep(globalInstallArgs(params.manager, params.spec, params.pkgRoot));
|
|
188
|
-
if (primary.code === 0) return {
|
|
189
|
-
exitCode: 0,
|
|
190
|
-
stdout: primary.stdout,
|
|
191
|
-
stderr: primary.stderr,
|
|
192
|
-
packageManager: params.manager,
|
|
193
|
-
usedFallback: false
|
|
194
|
-
};
|
|
195
|
-
const fallbackArgv = globalInstallFallbackArgs(params.manager, params.spec, params.pkgRoot);
|
|
196
|
-
if (!fallbackArgv) return {
|
|
197
|
-
exitCode: primary.code ?? 1,
|
|
198
|
-
stdout: primary.stdout,
|
|
199
|
-
stderr: primary.stderr,
|
|
200
|
-
packageManager: params.manager,
|
|
201
|
-
usedFallback: false
|
|
202
|
-
};
|
|
203
|
-
const fallback = await runStep(fallbackArgv);
|
|
204
|
-
return {
|
|
205
|
-
exitCode: fallback.code ?? 1,
|
|
206
|
-
stdout: [primary.stdout, fallback.stdout].filter(Boolean).join("\n"),
|
|
207
|
-
stderr: [primary.stderr, fallback.stderr].filter(Boolean).join("\n"),
|
|
208
|
-
packageManager: params.manager,
|
|
209
|
-
usedFallback: true
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
function formatGlobalInstallFailure(params) {
|
|
213
|
-
const tail = tailOutput(params.stderr);
|
|
214
|
-
return [
|
|
215
|
-
`Global install via ${params.packageManager} failed (exit ${params.exitCode}).`,
|
|
216
|
-
params.usedFallback ? "Retried with --omit=optional." : null,
|
|
217
|
-
`Try manually: ${params.packageManager} install -g ${params.spec}`,
|
|
218
|
-
tail ? `Install output:\n${tail}` : null
|
|
219
|
-
].filter(Boolean).join("\n");
|
|
220
|
-
}
|
|
221
270
|
//#endregion
|
|
222
|
-
export { XOPC_PACKAGE_NAME, createGlobalInstallEnv, detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot,
|
|
271
|
+
export { XOPC_PACKAGE_NAME, cleanupGlobalRenameDirs, collectInstalledGlobalPackageErrors, createGlobalInstallEnv, detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot, globalInstallArgs, globalInstallFallbackArgs, joinGlobalPackagePath, resolveExpectedInstalledVersionFromSpec, resolveGlobalInstallCommand, resolveGlobalInstallSpec, resolveGlobalInstallTarget, resolveGlobalManager, resolveGlobalPackageRoot, resolveGlobalRoot, resolveNpmGlobalPrefixLayoutFromGlobalRoot, resolveNpmGlobalPrefixLayoutFromPrefix };
|
|
223
272
|
|
|
224
273
|
//# sourceMappingURL=update-global.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-global.js","names":["fs","fsSync"],"sources":["../../../src/infra/update-global.ts"],"sourcesContent":["// src/infra/update-global.ts — global npm/pnpm install detection and commands (OpenClaw-aligned)\n\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport {\n createDefaultCommandRunner,\n type CommandRunner,\n type CommandRunResult,\n} from './run-command.js';\n\nexport const XOPC_PACKAGE_NAME = '@xopcai/xopc';\n\nexport type GlobalInstallManager = 'npm' | 'pnpm';\n\nexport type ResolvedGlobalInstallCommand = {\n manager: GlobalInstallManager;\n command: string;\n};\n\nconst COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT = '0';\nconst NPM_GLOBAL_INSTALL_QUIET_FLAGS = ['--no-fund', '--no-audit', '--loglevel=error'] as const;\nconst NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = [\n '--omit=optional',\n ...NPM_GLOBAL_INSTALL_QUIET_FLAGS,\n] as const;\n\nconst GLOBAL_DETECT_TIMEOUT_MS = 15_000;\nconst GLOBAL_INSTALL_TIMEOUT_MS = 45 * 60 * 1000;\n\nexport type GlobalInstallRunResult = {\n exitCode: number;\n stdout: string;\n stderr: string;\n packageManager: GlobalInstallManager;\n usedFallback: boolean;\n};\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function tryRealpath(targetPath: string): Promise<string> {\n try {\n return await fs.realpath(targetPath);\n } catch {\n return path.resolve(targetPath);\n }\n}\n\nexport function joinGlobalPackagePath(globalRoot: string): string {\n return path.join(globalRoot, XOPC_PACKAGE_NAME);\n}\n\nfunction inferNpmPrefixFromPackageRoot(pkgRoot?: string | null): string | null {\n const trimmed = pkgRoot?.trim();\n if (!trimmed) return null;\n const normalized = path.resolve(trimmed);\n const nodeModulesDir = path.dirname(normalized);\n if (path.basename(nodeModulesDir) !== 'node_modules') return null;\n const parentDir = path.dirname(nodeModulesDir);\n if (path.basename(parentDir) === 'lib') {\n return path.dirname(parentDir);\n }\n if (process.platform === 'win32' && path.basename(parentDir).toLowerCase() === 'npm') {\n return parentDir;\n }\n return null;\n}\n\nfunction resolvePreferredNpmCommand(pkgRoot?: string | null): string | null {\n const prefix = inferNpmPrefixFromPackageRoot(pkgRoot);\n if (!prefix) return null;\n const candidate =\n process.platform === 'win32' ? path.join(prefix, 'npm.cmd') : path.join(prefix, 'bin', 'npm');\n return fsSync.existsSync(candidate) ? candidate : null;\n}\n\nfunction resolvePreferredGlobalManagerCommand(\n manager: GlobalInstallManager,\n pkgRoot?: string | null,\n): string {\n if (manager !== 'npm') return manager;\n return resolvePreferredNpmCommand(pkgRoot) ?? manager;\n}\n\nexport function resolveGlobalInstallCommand(\n manager: GlobalInstallManager,\n pkgRoot?: string | null,\n): ResolvedGlobalInstallCommand {\n return {\n manager,\n command: resolvePreferredGlobalManagerCommand(manager, pkgRoot),\n };\n}\n\nfunction normalizeGlobalInstallCommand(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n pkgRoot?: string | null,\n): ResolvedGlobalInstallCommand {\n return typeof managerOrCommand === 'string'\n ? resolveGlobalInstallCommand(managerOrCommand, pkgRoot)\n : managerOrCommand;\n}\n\nfunction applyWindowsPackageInstallEnv(env: Record<string, string>): void {\n if (process.platform !== 'win32') return;\n env.NPM_CONFIG_UPDATE_NOTIFIER = 'false';\n env.NPM_CONFIG_FUND = 'false';\n env.NPM_CONFIG_AUDIT = 'false';\n}\n\nfunction applyCorepackDownloadPromptEnv(env: Record<string, string>): void {\n if (!env.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim()) {\n env.COREPACK_ENABLE_DOWNLOAD_PROMPT = COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT;\n }\n}\n\nexport async function createGlobalInstallEnv(\n env?: NodeJS.ProcessEnv,\n): Promise<NodeJS.ProcessEnv | undefined> {\n const sourceEnv = env ?? process.env;\n const hasCorepack = Boolean(sourceEnv.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim());\n if (process.platform !== 'win32' && hasCorepack) {\n return env;\n }\n const merged = Object.fromEntries(\n Object.entries(sourceEnv)\n .filter(([, value]) => value != null)\n .map(([key, value]) => [key, String(value)]),\n ) as Record<string, string>;\n applyWindowsPackageInstallEnv(merged);\n applyCorepackDownloadPromptEnv(merged);\n return merged;\n}\n\nexport function resolveGlobalInstallSpec(params: {\n version: string;\n env?: NodeJS.ProcessEnv;\n}): string {\n const override =\n params.env?.XOPC_UPDATE_PACKAGE_SPEC?.trim() || process.env.XOPC_UPDATE_PACKAGE_SPEC?.trim();\n if (override) return override;\n return `${XOPC_PACKAGE_NAME}@${params.version}`;\n}\n\nexport async function resolveGlobalRoot(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n runCommand: CommandRunner,\n timeoutMs: number,\n pkgRoot?: string | null,\n): Promise<string | null> {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n const argv = [resolved.command, 'root', '-g'];\n const res = await runCommand(argv, { timeoutMs }).catch(() => null);\n if (!res || res.code !== 0) return null;\n const root = res.stdout.trim();\n return root || null;\n}\n\nexport function globalInstallArgs(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n spec: string,\n pkgRoot?: string | null,\n): string[] {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n if (resolved.manager === 'pnpm') {\n return [resolved.command, 'add', '-g', spec];\n }\n return [resolved.command, 'install', '-g', spec, ...NPM_GLOBAL_INSTALL_QUIET_FLAGS];\n}\n\nexport function globalInstallFallbackArgs(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n spec: string,\n pkgRoot?: string | null,\n): string[] | null {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n if (resolved.manager !== 'npm') return null;\n return [resolved.command, 'install', '-g', spec, ...NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS];\n}\n\nexport async function detectGlobalInstallManagerForRoot(\n runCommand: CommandRunner,\n pkgRoot: string,\n timeoutMs: number,\n): Promise<GlobalInstallManager | null> {\n const pkgReal = await tryRealpath(pkgRoot);\n\n const candidates: Array<{ manager: GlobalInstallManager; argv: string[] }> = [\n { manager: 'npm', argv: ['npm', 'root', '-g'] },\n { manager: 'pnpm', argv: ['pnpm', 'root', '-g'] },\n ];\n\n for (const { manager, argv } of candidates) {\n const res = await runCommand(argv, { timeoutMs }).catch(() => null);\n if (!res || res.code !== 0) continue;\n const globalRoot = res.stdout.trim();\n if (!globalRoot) continue;\n const globalReal = await tryRealpath(globalRoot);\n const expected = joinGlobalPackagePath(globalReal);\n const expectedReal = await tryRealpath(expected);\n if (path.resolve(expectedReal) === path.resolve(pkgReal)) {\n return manager;\n }\n }\n\n if (resolvePreferredNpmCommand(pkgRoot)) {\n return 'npm';\n }\n\n return null;\n}\n\nexport async function detectGlobalInstallManagerByPresence(\n runCommand: CommandRunner,\n timeoutMs: number,\n): Promise<GlobalInstallManager | null> {\n for (const manager of ['npm', 'pnpm'] as const) {\n const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);\n if (!root) continue;\n if (await pathExists(joinGlobalPackagePath(root))) {\n return manager;\n }\n }\n return null;\n}\n\nexport async function resolveGlobalManager(params: {\n root: string | null;\n timeoutMs?: number;\n}): Promise<GlobalInstallManager> {\n const runCommand = createDefaultCommandRunner();\n const timeoutMs = params.timeoutMs ?? GLOBAL_DETECT_TIMEOUT_MS;\n\n if (params.root) {\n const detected = await detectGlobalInstallManagerForRoot(runCommand, params.root, timeoutMs);\n if (detected) return detected;\n }\n\n const byPresence = await detectGlobalInstallManagerByPresence(runCommand, timeoutMs);\n return byPresence ?? 'npm';\n}\n\nfunction tailOutput(text: string, max = 4000): string {\n const t = text.trim();\n if (t.length <= max) return t;\n return t.slice(-max);\n}\n\nexport async function runGlobalPackageInstall(params: {\n manager: GlobalInstallManager;\n spec: string;\n pkgRoot: string | null;\n timeoutMs?: number;\n /** Echo install output to the parent process (CLI interactive). */\n echoToTerminal?: boolean;\n}): Promise<GlobalInstallRunResult> {\n const timeoutMs = params.timeoutMs ?? GLOBAL_INSTALL_TIMEOUT_MS;\n const runCommand = createDefaultCommandRunner();\n const installEnv = await createGlobalInstallEnv();\n\n const runStep = async (argv: string[]): Promise<CommandRunResult> => {\n const result = await runCommand(argv, { timeoutMs, env: installEnv });\n if (params.echoToTerminal) {\n if (result.stdout) process.stdout.write(result.stdout);\n if (result.stderr) process.stderr.write(result.stderr);\n }\n return result;\n };\n\n const primaryArgv = globalInstallArgs(params.manager, params.spec, params.pkgRoot);\n const primary = await runStep(primaryArgv);\n if (primary.code === 0) {\n return {\n exitCode: 0,\n stdout: primary.stdout,\n stderr: primary.stderr,\n packageManager: params.manager,\n usedFallback: false,\n };\n }\n\n const fallbackArgv = globalInstallFallbackArgs(params.manager, params.spec, params.pkgRoot);\n if (!fallbackArgv) {\n return {\n exitCode: primary.code ?? 1,\n stdout: primary.stdout,\n stderr: primary.stderr,\n packageManager: params.manager,\n usedFallback: false,\n };\n }\n\n const fallback = await runStep(fallbackArgv);\n return {\n exitCode: fallback.code ?? 1,\n stdout: [primary.stdout, fallback.stdout].filter(Boolean).join('\\n'),\n stderr: [primary.stderr, fallback.stderr].filter(Boolean).join('\\n'),\n packageManager: params.manager,\n usedFallback: true,\n };\n}\n\nexport function formatGlobalInstallFailure(params: {\n packageManager: GlobalInstallManager;\n spec: string;\n exitCode: number;\n stderr: string;\n usedFallback: boolean;\n}): string {\n const tail = tailOutput(params.stderr);\n const parts = [\n `Global install via ${params.packageManager} failed (exit ${params.exitCode}).`,\n params.usedFallback ? 'Retried with --omit=optional.' : null,\n `Try manually: ${params.packageManager} install -g ${params.spec}`,\n tail ? `Install output:\\n${tail}` : null,\n ].filter(Boolean);\n return parts.join('\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-global.js","names":["fs","fsSync"],"sources":["../../../src/infra/update-global.ts"],"sourcesContent":["// src/infra/update-global.ts — global npm/pnpm install detection and commands (OpenClaw-aligned)\n\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { applyPathPrepend } from './path-prepend.js';\nimport { resolveExecPathBinPrepend } from './path-env.js';\nimport { readPackageVersion } from './package-json.js';\nimport { createDefaultCommandRunner, type CommandRunner } from './run-command.js';\n\nexport const XOPC_PACKAGE_NAME = '@xopcai/xopc';\n\nexport type GlobalInstallManager = 'npm' | 'pnpm';\n\nexport type ResolvedGlobalInstallCommand = {\n manager: GlobalInstallManager;\n command: string;\n};\n\nexport type ResolvedGlobalInstallTarget = ResolvedGlobalInstallCommand & {\n globalRoot: string | null;\n packageRoot: string | null;\n};\n\nexport type NpmGlobalPrefixLayout = {\n prefix: string;\n globalRoot: string;\n binDir: string;\n};\n\nconst GLOBAL_RENAME_PREFIX = '.';\nconst COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT = '0';\nconst NPM_GLOBAL_INSTALL_QUIET_FLAGS = ['--no-fund', '--no-audit', '--loglevel=error'] as const;\nconst NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = [\n '--omit=optional',\n ...NPM_GLOBAL_INSTALL_QUIET_FLAGS,\n] as const;\n\nconst GLOBAL_DETECT_TIMEOUT_MS = 15_000;\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function tryRealpath(targetPath: string): Promise<string> {\n try {\n return await fs.realpath(targetPath);\n } catch {\n return path.resolve(targetPath);\n }\n}\n\nexport function joinGlobalPackagePath(globalRoot: string): string {\n return path.join(globalRoot, XOPC_PACKAGE_NAME);\n}\n\nfunction inferNpmPrefixFromPackageRoot(pkgRoot?: string | null): string | null {\n const trimmed = pkgRoot?.trim();\n if (!trimmed) return null;\n const normalized = path.resolve(trimmed);\n const nodeModulesDir = path.dirname(normalized);\n if (path.basename(nodeModulesDir) !== 'node_modules') return null;\n const parentDir = path.dirname(nodeModulesDir);\n if (path.basename(parentDir) === 'lib') {\n return path.dirname(parentDir);\n }\n if (process.platform === 'win32' && path.basename(parentDir).toLowerCase() === 'npm') {\n return parentDir;\n }\n return null;\n}\n\nfunction resolvePreferredNpmCommand(pkgRoot?: string | null): string | null {\n const prefix = inferNpmPrefixFromPackageRoot(pkgRoot);\n if (!prefix) return null;\n const candidate =\n process.platform === 'win32' ? path.join(prefix, 'npm.cmd') : path.join(prefix, 'bin', 'npm');\n return fsSync.existsSync(candidate) ? candidate : null;\n}\n\nfunction resolveNpmFromExecPath(): string | null {\n const execPath = process.execPath?.trim();\n if (!execPath) return null;\n const binDir = path.dirname(execPath);\n const candidate =\n process.platform === 'win32' ? path.join(binDir, 'npm.cmd') : path.join(binDir, 'npm');\n return fsSync.existsSync(candidate) ? candidate : null;\n}\n\nfunction resolvePreferredGlobalManagerCommand(\n manager: GlobalInstallManager,\n pkgRoot?: string | null,\n): string {\n if (manager !== 'npm') return manager;\n return resolvePreferredNpmCommand(pkgRoot) ?? resolveNpmFromExecPath() ?? manager;\n}\n\nexport function resolveGlobalInstallCommand(\n manager: GlobalInstallManager,\n pkgRoot?: string | null,\n): ResolvedGlobalInstallCommand {\n return {\n manager,\n command: resolvePreferredGlobalManagerCommand(manager, pkgRoot),\n };\n}\n\nfunction normalizeGlobalInstallCommand(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n pkgRoot?: string | null,\n): ResolvedGlobalInstallCommand {\n return typeof managerOrCommand === 'string'\n ? resolveGlobalInstallCommand(managerOrCommand, pkgRoot)\n : managerOrCommand;\n}\n\nexport function resolveNpmGlobalPrefixLayoutFromGlobalRoot(\n globalRoot?: string | null,\n): NpmGlobalPrefixLayout | null {\n const trimmed = globalRoot?.trim();\n if (!trimmed) return null;\n const normalized = path.resolve(trimmed);\n if (path.basename(normalized) !== 'node_modules') return null;\n const parentDir = path.dirname(normalized);\n if (path.basename(parentDir) === 'lib') {\n const prefix = path.dirname(parentDir);\n return {\n prefix,\n globalRoot: normalized,\n binDir: path.join(prefix, 'bin'),\n };\n }\n if (process.platform === 'win32') {\n return {\n prefix: parentDir,\n globalRoot: normalized,\n binDir: parentDir,\n };\n }\n return null;\n}\n\nexport function resolveNpmGlobalPrefixLayoutFromPrefix(prefix: string): NpmGlobalPrefixLayout {\n const resolvedPrefix = path.resolve(prefix);\n if (process.platform === 'win32') {\n return {\n prefix: resolvedPrefix,\n globalRoot: path.join(resolvedPrefix, 'node_modules'),\n binDir: resolvedPrefix,\n };\n }\n return {\n prefix: resolvedPrefix,\n globalRoot: path.join(resolvedPrefix, 'lib', 'node_modules'),\n binDir: path.join(resolvedPrefix, 'bin'),\n };\n}\n\nfunction applyWindowsPackageInstallEnv(env: Record<string, string>): void {\n if (process.platform !== 'win32') return;\n env.NPM_CONFIG_UPDATE_NOTIFIER = 'false';\n env.NPM_CONFIG_FUND = 'false';\n env.NPM_CONFIG_AUDIT = 'false';\n}\n\nfunction applyCorepackDownloadPromptEnv(env: Record<string, string>): void {\n if (!env.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim()) {\n env.COREPACK_ENABLE_DOWNLOAD_PROMPT = COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT;\n }\n}\n\nexport async function createGlobalInstallEnv(\n env?: NodeJS.ProcessEnv,\n): Promise<NodeJS.ProcessEnv | undefined> {\n const pathPrepend = resolveExecPathBinPrepend();\n const sourceEnv = env ?? process.env;\n const hasCorepack = Boolean(sourceEnv.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim());\n if (process.platform !== 'win32' && hasCorepack && pathPrepend.length === 0) {\n return env;\n }\n const merged = Object.fromEntries(\n Object.entries(sourceEnv)\n .filter(([, value]) => value != null)\n .map(([key, value]) => [key, String(value)]),\n ) as Record<string, string>;\n applyPathPrepend(merged, pathPrepend);\n applyWindowsPackageInstallEnv(merged);\n applyCorepackDownloadPromptEnv(merged);\n return merged;\n}\n\nexport function resolveGlobalInstallSpec(params: {\n version: string;\n env?: NodeJS.ProcessEnv;\n}): string {\n const override =\n params.env?.XOPC_UPDATE_PACKAGE_SPEC?.trim() || process.env.XOPC_UPDATE_PACKAGE_SPEC?.trim();\n if (override) return override;\n return `${XOPC_PACKAGE_NAME}@${params.version}`;\n}\n\nexport function resolveExpectedInstalledVersionFromSpec(spec: string): string | null {\n const normalized = spec.trim();\n if (!normalized.startsWith(`${XOPC_PACKAGE_NAME}@`)) return null;\n const rawVersion = normalized.slice(XOPC_PACKAGE_NAME.length + 1).trim();\n if (\n !rawVersion ||\n rawVersion.includes('/') ||\n rawVersion.includes(':') ||\n rawVersion.includes('#') ||\n /^(latest|beta|next|dev)$/i.test(rawVersion)\n ) {\n return null;\n }\n return rawVersion;\n}\n\nexport async function collectInstalledGlobalPackageErrors(params: {\n packageRoot: string;\n expectedVersion?: string | null;\n}): Promise<string[]> {\n const errors: string[] = [];\n const installedVersion = await readPackageVersion(params.packageRoot);\n if (params.expectedVersion && installedVersion !== params.expectedVersion) {\n errors.push(\n `expected installed version ${params.expectedVersion}, found ${installedVersion ?? '<missing>'}`,\n );\n }\n if (!installedVersion) {\n errors.push('missing package.json version');\n }\n return errors;\n}\n\nexport async function resolveGlobalRoot(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n runCommand: CommandRunner,\n timeoutMs: number,\n pkgRoot?: string | null,\n): Promise<string | null> {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n const argv = [resolved.command, 'root', '-g'];\n const res = await runCommand(argv, { timeoutMs }).catch(() => null);\n if (!res || res.code !== 0) return null;\n const root = res.stdout.trim();\n return root || null;\n}\n\nexport async function resolveGlobalPackageRoot(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n runCommand: CommandRunner,\n timeoutMs: number,\n pkgRoot?: string | null,\n): Promise<string | null> {\n const root = await resolveGlobalRoot(managerOrCommand, runCommand, timeoutMs, pkgRoot);\n if (!root) return null;\n return joinGlobalPackagePath(root);\n}\n\nexport async function resolveGlobalInstallTarget(params: {\n manager: GlobalInstallManager | ResolvedGlobalInstallCommand;\n runCommand: CommandRunner;\n timeoutMs: number;\n pkgRoot?: string | null;\n}): Promise<ResolvedGlobalInstallTarget> {\n const command = normalizeGlobalInstallCommand(params.manager, params.pkgRoot);\n const globalRoot = await resolveGlobalRoot(\n command,\n params.runCommand,\n params.timeoutMs,\n params.pkgRoot,\n );\n return {\n ...command,\n globalRoot,\n packageRoot: globalRoot ? joinGlobalPackagePath(globalRoot) : null,\n };\n}\n\nexport function globalInstallArgs(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n spec: string,\n pkgRoot?: string | null,\n installPrefix?: string | null,\n): string[] {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n if (resolved.manager === 'pnpm') {\n return [resolved.command, 'add', '-g', spec];\n }\n return [\n resolved.command,\n 'install',\n '-g',\n ...(installPrefix ? ['--prefix', installPrefix] : []),\n spec,\n ...NPM_GLOBAL_INSTALL_QUIET_FLAGS,\n ];\n}\n\nexport function globalInstallFallbackArgs(\n managerOrCommand: GlobalInstallManager | ResolvedGlobalInstallCommand,\n spec: string,\n pkgRoot?: string | null,\n installPrefix?: string | null,\n): string[] | null {\n const resolved = normalizeGlobalInstallCommand(managerOrCommand, pkgRoot);\n if (resolved.manager !== 'npm') return null;\n return [\n resolved.command,\n 'install',\n '-g',\n ...(installPrefix ? ['--prefix', installPrefix] : []),\n spec,\n ...NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS,\n ];\n}\n\nexport async function cleanupGlobalRenameDirs(params: {\n globalRoot: string;\n packageName: string;\n}): Promise<{ removed: string[] }> {\n const removed: string[] = [];\n const root = params.globalRoot.trim();\n const name = params.packageName.trim();\n if (!root || !name) return { removed };\n const prefix = `${GLOBAL_RENAME_PREFIX}${name}-`;\n let entries: string[] = [];\n try {\n entries = await fs.readdir(root);\n } catch {\n return { removed };\n }\n for (const entry of entries) {\n if (!entry.startsWith(prefix)) continue;\n const target = path.join(root, entry);\n try {\n const stat = await fs.lstat(target);\n if (!stat.isDirectory()) continue;\n await fs.rm(target, { recursive: true, force: true });\n removed.push(entry);\n } catch {\n // ignore\n }\n }\n return { removed };\n}\n\nexport async function detectGlobalInstallManagerForRoot(\n runCommand: CommandRunner,\n pkgRoot: string,\n timeoutMs: number,\n): Promise<GlobalInstallManager | null> {\n const pkgReal = await tryRealpath(pkgRoot);\n const npmCommand = resolvePreferredGlobalManagerCommand('npm', pkgRoot);\n const candidates: Array<{ manager: GlobalInstallManager; argv: string[] }> = [\n { manager: 'npm', argv: [npmCommand, 'root', '-g'] },\n { manager: 'pnpm', argv: ['pnpm', 'root', '-g'] },\n ];\n\n for (const { manager, argv } of candidates) {\n const res = await runCommand(argv, { timeoutMs }).catch(() => null);\n if (!res || res.code !== 0) continue;\n const globalRoot = res.stdout.trim();\n if (!globalRoot) continue;\n const globalReal = await tryRealpath(globalRoot);\n const expected = joinGlobalPackagePath(globalReal);\n const expectedReal = await tryRealpath(expected);\n if (path.resolve(expectedReal) === path.resolve(pkgReal)) {\n return manager;\n }\n }\n\n if (resolvePreferredNpmCommand(pkgRoot) || resolveNpmFromExecPath()) {\n return 'npm';\n }\n\n return null;\n}\n\nexport async function detectGlobalInstallManagerByPresence(\n runCommand: CommandRunner,\n timeoutMs: number,\n): Promise<GlobalInstallManager | null> {\n for (const manager of ['npm', 'pnpm'] as const) {\n const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);\n if (!root) continue;\n if (await pathExists(joinGlobalPackagePath(root))) {\n return manager;\n }\n }\n return null;\n}\n\nexport async function resolveGlobalManager(params: {\n root: string | null;\n timeoutMs?: number;\n}): Promise<GlobalInstallManager> {\n const runCommand = createDefaultCommandRunner();\n const timeoutMs = params.timeoutMs ?? GLOBAL_DETECT_TIMEOUT_MS;\n\n if (params.root) {\n const detected = await detectGlobalInstallManagerForRoot(runCommand, params.root, timeoutMs);\n if (detected) return detected;\n }\n\n const byPresence = await detectGlobalInstallManagerByPresence(runCommand, timeoutMs);\n return byPresence ?? 'npm';\n}\n"],"mappings":";;;;;;;;AAWA,MAAa,oBAAoB;AAoBjC,MAAM,uBAAuB;AAC7B,MAAM,0CAA0C;AAChD,MAAM,iCAAiC;CAAC;CAAa;CAAc;CAAmB;AACtF,MAAM,yCAAyC,CAC7C,mBACA,GAAG,+BACJ;AAED,MAAM,2BAA2B;AAEjC,eAAe,WAAW,YAAsC;AAC9D,KAAI;AACF,QAAMA,KAAG,OAAO,WAAW;AAC3B,SAAO;SACD;AACN,SAAO;;;AAIX,eAAe,YAAY,YAAqC;AAC9D,KAAI;AACF,SAAO,MAAMA,KAAG,SAAS,WAAW;SAC9B;AACN,SAAO,KAAK,QAAQ,WAAW;;;AAInC,SAAgB,sBAAsB,YAA4B;AAChE,QAAO,KAAK,KAAK,YAAY,kBAAkB;;AAGjD,SAAS,8BAA8B,SAAwC;CAC7E,MAAM,UAAU,SAAS,MAAM;AAC/B,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,aAAa,KAAK,QAAQ,QAAQ;CACxC,MAAM,iBAAiB,KAAK,QAAQ,WAAW;AAC/C,KAAI,KAAK,SAAS,eAAe,KAAK,eAAgB,QAAO;CAC7D,MAAM,YAAY,KAAK,QAAQ,eAAe;AAC9C,KAAI,KAAK,SAAS,UAAU,KAAK,MAC/B,QAAO,KAAK,QAAQ,UAAU;AAEhC,KAAI,QAAQ,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,aAAa,KAAK,MAC7E,QAAO;AAET,QAAO;;AAGT,SAAS,2BAA2B,SAAwC;CAC1E,MAAM,SAAS,8BAA8B,QAAQ;AACrD,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,YACJ,QAAQ,aAAa,UAAU,KAAK,KAAK,QAAQ,UAAU,GAAG,KAAK,KAAK,QAAQ,OAAO,MAAM;AAC/F,QAAOC,GAAO,WAAW,UAAU,GAAG,YAAY;;AAGpD,SAAS,yBAAwC;CAC/C,MAAM,WAAW,QAAQ,UAAU,MAAM;AACzC,KAAI,CAAC,SAAU,QAAO;CACtB,MAAM,SAAS,KAAK,QAAQ,SAAS;CACrC,MAAM,YACJ,QAAQ,aAAa,UAAU,KAAK,KAAK,QAAQ,UAAU,GAAG,KAAK,KAAK,QAAQ,MAAM;AACxF,QAAOA,GAAO,WAAW,UAAU,GAAG,YAAY;;AAGpD,SAAS,qCACP,SACA,SACQ;AACR,KAAI,YAAY,MAAO,QAAO;AAC9B,QAAO,2BAA2B,QAAQ,IAAI,wBAAwB,IAAI;;AAG5E,SAAgB,4BACd,SACA,SAC8B;AAC9B,QAAO;EACL;EACA,SAAS,qCAAqC,SAAS,QAAQ;EAChE;;AAGH,SAAS,8BACP,kBACA,SAC8B;AAC9B,QAAO,OAAO,qBAAqB,WAC/B,4BAA4B,kBAAkB,QAAQ,GACtD;;AAGN,SAAgB,2CACd,YAC8B;CAC9B,MAAM,UAAU,YAAY,MAAM;AAClC,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,aAAa,KAAK,QAAQ,QAAQ;AACxC,KAAI,KAAK,SAAS,WAAW,KAAK,eAAgB,QAAO;CACzD,MAAM,YAAY,KAAK,QAAQ,WAAW;AAC1C,KAAI,KAAK,SAAS,UAAU,KAAK,OAAO;EACtC,MAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,SAAO;GACL;GACA,YAAY;GACZ,QAAQ,KAAK,KAAK,QAAQ,MAAM;GACjC;;AAEH,KAAI,QAAQ,aAAa,QACvB,QAAO;EACL,QAAQ;EACR,YAAY;EACZ,QAAQ;EACT;AAEH,QAAO;;AAGT,SAAgB,uCAAuC,QAAuC;CAC5F,MAAM,iBAAiB,KAAK,QAAQ,OAAO;AAC3C,KAAI,QAAQ,aAAa,QACvB,QAAO;EACL,QAAQ;EACR,YAAY,KAAK,KAAK,gBAAgB,eAAe;EACrD,QAAQ;EACT;AAEH,QAAO;EACL,QAAQ;EACR,YAAY,KAAK,KAAK,gBAAgB,OAAO,eAAe;EAC5D,QAAQ,KAAK,KAAK,gBAAgB,MAAM;EACzC;;AAGH,SAAS,8BAA8B,KAAmC;AACxE,KAAI,QAAQ,aAAa,QAAS;AAClC,KAAI,6BAA6B;AACjC,KAAI,kBAAkB;AACtB,KAAI,mBAAmB;;AAGzB,SAAS,+BAA+B,KAAmC;AACzE,KAAI,CAAC,IAAI,iCAAiC,MAAM,CAC9C,KAAI,kCAAkC;;AAI1C,eAAsB,uBACpB,KACwC;CACxC,MAAM,cAAc,2BAA2B;CAC/C,MAAM,YAAY,OAAO,QAAQ;CACjC,MAAM,cAAc,QAAQ,UAAU,iCAAiC,MAAM,CAAC;AAC9E,KAAI,QAAQ,aAAa,WAAW,eAAe,YAAY,WAAW,EACxE,QAAO;CAET,MAAM,SAAS,OAAO,YACpB,OAAO,QAAQ,UAAU,CACtB,QAAQ,GAAG,WAAW,SAAS,KAAK,CACpC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,OAAO,MAAM,CAAC,CAAC,CAC/C;AACD,kBAAiB,QAAQ,YAAY;AACrC,+BAA8B,OAAO;AACrC,gCAA+B,OAAO;AACtC,QAAO;;AAGT,SAAgB,yBAAyB,QAG9B;CACT,MAAM,WACJ,OAAO,KAAK,0BAA0B,MAAM,IAAI,QAAQ,IAAI,0BAA0B,MAAM;AAC9F,KAAI,SAAU,QAAO;AACrB,QAAO,GAAG,kBAAkB,GAAG,OAAO;;AAGxC,SAAgB,wCAAwC,MAA6B;CACnF,MAAM,aAAa,KAAK,MAAM;AAC9B,KAAI,CAAC,WAAW,WAAW,gBAAwB,CAAE,QAAO;CAC5D,MAAM,aAAa,WAAW,MAAM,GAA6B,CAAC,MAAM;AACxE,KACE,CAAC,cACD,WAAW,SAAS,IAAI,IACxB,WAAW,SAAS,IAAI,IACxB,WAAW,SAAS,IAAI,IACxB,4BAA4B,KAAK,WAAW,CAE5C,QAAO;AAET,QAAO;;AAGT,eAAsB,oCAAoC,QAGpC;CACpB,MAAM,SAAmB,EAAE;CAC3B,MAAM,mBAAmB,MAAM,mBAAmB,OAAO,YAAY;AACrE,KAAI,OAAO,mBAAmB,qBAAqB,OAAO,gBACxD,QAAO,KACL,8BAA8B,OAAO,gBAAgB,UAAU,oBAAoB,cACpF;AAEH,KAAI,CAAC,iBACH,QAAO,KAAK,+BAA+B;AAE7C,QAAO;;AAGT,eAAsB,kBACpB,kBACA,YACA,WACA,SACwB;CAGxB,MAAM,MAAM,MAAM,WAAW;EAFZ,8BAA8B,kBAAkB,QAC3C,CAAC;EAAS;EAAQ;EACP,EAAE,EAAE,WAAW,CAAC,CAAC,YAAY,KAAK;AACnE,KAAI,CAAC,OAAO,IAAI,SAAS,EAAG,QAAO;AAEnC,QADa,IAAI,OAAO,MACb,IAAI;;AAGjB,eAAsB,yBACpB,kBACA,YACA,WACA,SACwB;CACxB,MAAM,OAAO,MAAM,kBAAkB,kBAAkB,YAAY,WAAW,QAAQ;AACtF,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,sBAAsB,KAAK;;AAGpC,eAAsB,2BAA2B,QAKR;CACvC,MAAM,UAAU,8BAA8B,OAAO,SAAS,OAAO,QAAQ;CAC7E,MAAM,aAAa,MAAM,kBACvB,SACA,OAAO,YACP,OAAO,WACP,OAAO,QACR;AACD,QAAO;EACL,GAAG;EACH;EACA,aAAa,aAAa,sBAAsB,WAAW,GAAG;EAC/D;;AAGH,SAAgB,kBACd,kBACA,MACA,SACA,eACU;CACV,MAAM,WAAW,8BAA8B,kBAAkB,QAAQ;AACzE,KAAI,SAAS,YAAY,OACvB,QAAO;EAAC,SAAS;EAAS;EAAO;EAAM;EAAK;AAE9C,QAAO;EACL,SAAS;EACT;EACA;EACA,GAAI,gBAAgB,CAAC,YAAY,cAAc,GAAG,EAAE;EACpD;EACA,GAAG;EACJ;;AAGH,SAAgB,0BACd,kBACA,MACA,SACA,eACiB;CACjB,MAAM,WAAW,8BAA8B,kBAAkB,QAAQ;AACzE,KAAI,SAAS,YAAY,MAAO,QAAO;AACvC,QAAO;EACL,SAAS;EACT;EACA;EACA,GAAI,gBAAgB,CAAC,YAAY,cAAc,GAAG,EAAE;EACpD;EACA,GAAG;EACJ;;AAGH,eAAsB,wBAAwB,QAGX;CACjC,MAAM,UAAoB,EAAE;CAC5B,MAAM,OAAO,OAAO,WAAW,MAAM;CACrC,MAAM,OAAO,OAAO,YAAY,MAAM;AACtC,KAAI,CAAC,QAAQ,CAAC,KAAM,QAAO,EAAE,SAAS;CACtC,MAAM,SAAS,GAAG,uBAAuB,KAAK;CAC9C,IAAI,UAAoB,EAAE;AAC1B,KAAI;AACF,YAAU,MAAMD,KAAG,QAAQ,KAAK;SAC1B;AACN,SAAO,EAAE,SAAS;;AAEpB,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,MAAM,WAAW,OAAO,CAAE;EAC/B,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM;AACrC,MAAI;AAEF,OAAI,EAAC,MADcA,KAAG,MAAM,OAAO,EACzB,aAAa,CAAE;AACzB,SAAMA,KAAG,GAAG,QAAQ;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AACrD,WAAQ,KAAK,MAAM;UACb;;AAIV,QAAO,EAAE,SAAS;;AAGpB,eAAsB,kCACpB,YACA,SACA,WACsC;CACtC,MAAM,UAAU,MAAM,YAAY,QAAQ;CAE1C,MAAM,aAAuE,CAC3E;EAAE,SAAS;EAAO,MAAM;GAFP,qCAAqC,OAAO,QAE1B;GAAE;GAAQ;GAAK;EAAE,EACpD;EAAE,SAAS;EAAQ,MAAM;GAAC;GAAQ;GAAQ;GAAK;EAAE,CAClD;AAED,MAAK,MAAM,EAAE,SAAS,UAAU,YAAY;EAC1C,MAAM,MAAM,MAAM,WAAW,MAAM,EAAE,WAAW,CAAC,CAAC,YAAY,KAAK;AACnE,MAAI,CAAC,OAAO,IAAI,SAAS,EAAG;EAC5B,MAAM,aAAa,IAAI,OAAO,MAAM;AACpC,MAAI,CAAC,WAAY;EAGjB,MAAM,eAAe,MAAM,YADV,sBAAsB,MADd,YAAY,WAAW,CAED,CAAC;AAChD,MAAI,KAAK,QAAQ,aAAa,KAAK,KAAK,QAAQ,QAAQ,CACtD,QAAO;;AAIX,KAAI,2BAA2B,QAAQ,IAAI,wBAAwB,CACjE,QAAO;AAGT,QAAO;;AAGT,eAAsB,qCACpB,YACA,WACsC;AACtC,MAAK,MAAM,WAAW,CAAC,OAAO,OAAO,EAAW;EAC9C,MAAM,OAAO,MAAM,kBAAkB,SAAS,YAAY,UAAU;AACpE,MAAI,CAAC,KAAM;AACX,MAAI,MAAM,WAAW,sBAAsB,KAAK,CAAC,CAC/C,QAAO;;AAGX,QAAO;;AAGT,eAAsB,qBAAqB,QAGT;CAChC,MAAM,aAAa,4BAA4B;CAC/C,MAAM,YAAY,OAAO,aAAa;AAEtC,KAAI,OAAO,MAAM;EACf,MAAM,WAAW,MAAM,kCAAkC,YAAY,OAAO,MAAM,UAAU;AAC5F,MAAI,SAAU,QAAO;;AAIvB,QAAO,MADkB,qCAAqC,YAAY,UAAU,IAC/D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function trimLogTail(text: string | null | undefined, maxChars: number): string | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/infra/update-log.ts
|
|
2
|
+
function trimLogTail(text, maxChars) {
|
|
3
|
+
if (!text) return null;
|
|
4
|
+
const trimmed = text.trim();
|
|
5
|
+
if (!trimmed) return null;
|
|
6
|
+
if (trimmed.length <= maxChars) return trimmed;
|
|
7
|
+
return trimmed.slice(-maxChars);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { trimLogTail };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=update-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-log.js","names":[],"sources":["../../../src/infra/update-log.ts"],"sourcesContent":["export function trimLogTail(text: string | null | undefined, maxChars: number): string | null {\n if (!text) return null;\n const trimmed = text.trim();\n if (!trimmed) return null;\n if (trimmed.length <= maxChars) return trimmed;\n return trimmed.slice(-maxChars);\n}\n"],"mappings":";AAAA,SAAgB,YAAY,MAAiC,UAAiC;AAC5F,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,UAAU,KAAK,MAAM;AAC3B,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,QAAQ,UAAU,SAAU,QAAO;AACvC,QAAO,QAAQ,MAAM,CAAC,SAAS"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway restart after xopc update — daemon, unmanaged, or in-process respawn.
|
|
3
|
+
*/
|
|
4
|
+
export type UpdateRestartResult = {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
mode: 'in-process' | 'daemon' | 'unmanaged' | 'skipped' | 'disabled' | 'failed';
|
|
7
|
+
message?: string;
|
|
8
|
+
};
|
|
9
|
+
export type InProcessRestartTrigger = () => {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
mode?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function isRunningInsideGatewayService(env?: Record<string, string | undefined>): boolean;
|
|
15
|
+
export declare function maybeRestartGatewayAfterUpdate(params: {
|
|
16
|
+
shouldRestart?: boolean;
|
|
17
|
+
expectedVersion?: string;
|
|
18
|
+
configPath?: string;
|
|
19
|
+
triggerInProcessRestart?: InProcessRestartTrigger;
|
|
20
|
+
}): Promise<UpdateRestartResult>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { init_paths, resolveConfigPath } from "../config/paths.js";
|
|
4
|
+
import { init_loader, loadConfig } from "../config/loader.js";
|
|
5
|
+
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
6
|
+
import { resolveGatewayService } from "../daemon/service.js";
|
|
7
|
+
import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "./restart.js";
|
|
8
|
+
import { findVerifiedGatewayListenerPidsOnPortSync, signalVerifiedGatewayPidSync } from "./gateway-processes.js";
|
|
9
|
+
//#region src/infra/update-restart.ts
|
|
10
|
+
init_loader();
|
|
11
|
+
init_paths();
|
|
12
|
+
init_logger();
|
|
13
|
+
const log = createLogger("UpdateRestart");
|
|
14
|
+
const DEFAULT_HEALTH_ATTEMPTS = 120;
|
|
15
|
+
const DEFAULT_HEALTH_DELAY_MS = 500;
|
|
16
|
+
function isRunningInsideGatewayService(env = process.env) {
|
|
17
|
+
return env.XOPC_SERVICE_MARKER?.trim() === "1";
|
|
18
|
+
}
|
|
19
|
+
function resolveGatewayPort(configPath) {
|
|
20
|
+
const config = loadConfig(configPath ?? resolveConfigPath());
|
|
21
|
+
return typeof config.gateway?.port === "number" ? config.gateway.port : 18790;
|
|
22
|
+
}
|
|
23
|
+
function parsePortFromArgs(programArguments) {
|
|
24
|
+
if (!programArguments?.length) return null;
|
|
25
|
+
for (let i = 0; i < programArguments.length; i += 1) {
|
|
26
|
+
const arg = programArguments[i];
|
|
27
|
+
if (arg === "--port") {
|
|
28
|
+
const parsed = parseInt(String(programArguments[i + 1]), 10);
|
|
29
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
30
|
+
}
|
|
31
|
+
if (arg?.startsWith("--port=")) {
|
|
32
|
+
const parsed = parseInt(arg.split("=", 2)[1] ?? "", 10);
|
|
33
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
async function resolveRestartPort(service) {
|
|
39
|
+
return parsePortFromArgs((await service.readCommand(process.env).catch(() => null))?.programArguments) ?? resolveGatewayPort();
|
|
40
|
+
}
|
|
41
|
+
async function waitForGatewayHealth(port, expectedVersion) {
|
|
42
|
+
for (let attempt = 0; attempt < DEFAULT_HEALTH_ATTEMPTS; attempt += 1) try {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
45
|
+
const response = await fetch(`http://127.0.0.1:${port}/api/health`, {
|
|
46
|
+
method: "GET",
|
|
47
|
+
signal: controller.signal
|
|
48
|
+
});
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (expectedVersion) {
|
|
55
|
+
const body = await response.json();
|
|
56
|
+
if (body.version && body.version !== expectedVersion) {
|
|
57
|
+
await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
} catch {
|
|
63
|
+
await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
async function restartUnmanagedGateway(port, configPath) {
|
|
68
|
+
if (!isRestartEnabled(loadConfig(configPath ?? resolveConfigPath()))) return {
|
|
69
|
+
ok: false,
|
|
70
|
+
mode: "disabled",
|
|
71
|
+
message: "Gateway restart is disabled in config (commands.restart=false). Restart manually: xopc gateway restart"
|
|
72
|
+
};
|
|
73
|
+
const pids = findVerifiedGatewayListenerPidsOnPortSync(port).filter((pid) => Number.isFinite(pid) && pid > 0);
|
|
74
|
+
if (pids.length === 0) return {
|
|
75
|
+
ok: false,
|
|
76
|
+
mode: "failed",
|
|
77
|
+
message: `No gateway listener found on port ${port}.`
|
|
78
|
+
};
|
|
79
|
+
if (pids.length > 1) return {
|
|
80
|
+
ok: false,
|
|
81
|
+
mode: "failed",
|
|
82
|
+
message: `Multiple gateway processes on port ${port}; use "xopc gateway status" before retrying.`
|
|
83
|
+
};
|
|
84
|
+
const targetPid = pids[0];
|
|
85
|
+
if (process.platform === "win32") {
|
|
86
|
+
writeGatewayRestartIntentSync({ targetPid });
|
|
87
|
+
signalVerifiedGatewayPidSync(targetPid, "SIGTERM");
|
|
88
|
+
} else {
|
|
89
|
+
authorizeGatewaySigusr1Restart();
|
|
90
|
+
signalVerifiedGatewayPidSync(targetPid, "SIGUSR1");
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
mode: "unmanaged",
|
|
95
|
+
message: `Gateway restart signal sent to process ${targetPid} on port ${port}.`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function restartDaemonGateway(service, port, expectedVersion) {
|
|
99
|
+
try {
|
|
100
|
+
if (!await service.isLoaded({ env: process.env })) return restartUnmanagedGateway(port);
|
|
101
|
+
await service.restart({
|
|
102
|
+
env: process.env,
|
|
103
|
+
stdout: process.stdout
|
|
104
|
+
});
|
|
105
|
+
if (!await waitForGatewayHealth(port, expectedVersion)) {
|
|
106
|
+
log.warn({
|
|
107
|
+
port,
|
|
108
|
+
expectedVersion
|
|
109
|
+
}, "Gateway restart completed but health check timed out");
|
|
110
|
+
return {
|
|
111
|
+
ok: true,
|
|
112
|
+
mode: "daemon",
|
|
113
|
+
message: "Gateway service restarted; health check timed out (gateway may still be starting)."
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
mode: "daemon",
|
|
119
|
+
message: "Gateway service restarted successfully."
|
|
120
|
+
};
|
|
121
|
+
} catch (err) {
|
|
122
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
123
|
+
log.warn({
|
|
124
|
+
err,
|
|
125
|
+
port
|
|
126
|
+
}, `Daemon restart failed: ${message}`);
|
|
127
|
+
return {
|
|
128
|
+
ok: false,
|
|
129
|
+
mode: "failed",
|
|
130
|
+
message
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function maybeRestartGatewayAfterUpdate(params) {
|
|
135
|
+
if (params.shouldRestart === false) return {
|
|
136
|
+
ok: true,
|
|
137
|
+
mode: "skipped",
|
|
138
|
+
message: "Restart skipped (--no-restart)."
|
|
139
|
+
};
|
|
140
|
+
if (!isRestartEnabled(loadConfig(params.configPath ?? resolveConfigPath()))) return {
|
|
141
|
+
ok: false,
|
|
142
|
+
mode: "disabled",
|
|
143
|
+
message: "Gateway restart is disabled (commands.restart=false). Restart manually: xopc gateway restart"
|
|
144
|
+
};
|
|
145
|
+
if (isRunningInsideGatewayService() && params.triggerInProcessRestart) {
|
|
146
|
+
const result = params.triggerInProcessRestart();
|
|
147
|
+
if (!result.ok) return {
|
|
148
|
+
ok: false,
|
|
149
|
+
mode: "failed",
|
|
150
|
+
message: result.message ?? "In-process gateway restart failed."
|
|
151
|
+
};
|
|
152
|
+
log.info({ mode: result.mode }, "Scheduled in-process gateway restart after update");
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
mode: "in-process",
|
|
156
|
+
message: result.message ?? "Gateway restart scheduled."
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const service = await resolveGatewayService();
|
|
160
|
+
return restartDaemonGateway(service, await resolveRestartPort(service), params.expectedVersion);
|
|
161
|
+
}
|
|
162
|
+
//#endregion
|
|
163
|
+
export { isRunningInsideGatewayService, maybeRestartGatewayAfterUpdate };
|
|
164
|
+
|
|
165
|
+
//# sourceMappingURL=update-restart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-restart.js","names":[],"sources":["../../../src/infra/update-restart.ts"],"sourcesContent":["/**\n * Gateway restart after xopc update — daemon, unmanaged, or in-process respawn.\n */\n\nimport { loadConfig } from '../config/loader.js';\nimport { resolveConfigPath } from '../config/paths.js';\nimport { isRestartEnabled } from '../config/commands.flags.js';\nimport { resolveGatewayService } from '../daemon/service.js';\nimport type { GatewayService } from '../daemon/types.js';\nimport {\n findVerifiedGatewayListenerPidsOnPortSync,\n signalVerifiedGatewayPidSync,\n} from './gateway-processes.js';\nimport { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from './restart.js';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('UpdateRestart');\n\nconst DEFAULT_HEALTH_ATTEMPTS = 120;\nconst DEFAULT_HEALTH_DELAY_MS = 500;\n\nexport type UpdateRestartResult = {\n ok: boolean;\n mode: 'in-process' | 'daemon' | 'unmanaged' | 'skipped' | 'disabled' | 'failed';\n message?: string;\n};\n\nexport type InProcessRestartTrigger = () => { ok: boolean; mode?: string; message?: string };\n\nexport function isRunningInsideGatewayService(\n env: Record<string, string | undefined> = process.env,\n): boolean {\n return env.XOPC_SERVICE_MARKER?.trim() === '1';\n}\n\nfunction resolveGatewayPort(configPath?: string): number {\n const config = loadConfig(configPath ?? resolveConfigPath());\n return typeof config.gateway?.port === 'number' ? config.gateway.port : 18790;\n}\n\nfunction parsePortFromArgs(programArguments: string[] | undefined): number | null {\n if (!programArguments?.length) {\n return null;\n }\n for (let i = 0; i < programArguments.length; i += 1) {\n const arg = programArguments[i];\n if (arg === '--port') {\n const parsed = parseInt(String(programArguments[i + 1]), 10);\n if (Number.isFinite(parsed) && parsed > 0) {\n return parsed;\n }\n }\n if (arg?.startsWith('--port=')) {\n const parsed = parseInt(arg.split('=', 2)[1] ?? '', 10);\n if (Number.isFinite(parsed) && parsed > 0) {\n return parsed;\n }\n }\n }\n return null;\n}\n\nasync function resolveRestartPort(service: GatewayService): Promise<number> {\n const command = await service.readCommand(process.env).catch(() => null);\n return parsePortFromArgs(command?.programArguments) ?? resolveGatewayPort();\n}\n\nasync function waitForGatewayHealth(port: number, expectedVersion?: string): Promise<boolean> {\n for (let attempt = 0; attempt < DEFAULT_HEALTH_ATTEMPTS; attempt += 1) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 1500);\n const response = await fetch(`http://127.0.0.1:${port}/api/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n clearTimeout(timeout);\n if (!response.ok) {\n await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));\n continue;\n }\n if (expectedVersion) {\n const body = (await response.json()) as { version?: string };\n if (body.version && body.version !== expectedVersion) {\n await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));\n continue;\n }\n }\n return true;\n } catch {\n await new Promise((r) => setTimeout(r, DEFAULT_HEALTH_DELAY_MS));\n }\n }\n return false;\n}\n\nasync function restartUnmanagedGateway(port: number, configPath?: string): Promise<UpdateRestartResult> {\n const config = loadConfig(configPath ?? resolveConfigPath());\n if (!isRestartEnabled(config)) {\n return {\n ok: false,\n mode: 'disabled',\n message:\n 'Gateway restart is disabled in config (commands.restart=false). Restart manually: xopc gateway restart',\n };\n }\n\n const pids = findVerifiedGatewayListenerPidsOnPortSync(port).filter(\n (pid): pid is number => Number.isFinite(pid) && pid > 0,\n );\n if (pids.length === 0) {\n return {\n ok: false,\n mode: 'failed',\n message: `No gateway listener found on port ${port}.`,\n };\n }\n if (pids.length > 1) {\n return {\n ok: false,\n mode: 'failed',\n message: `Multiple gateway processes on port ${port}; use \"xopc gateway status\" before retrying.`,\n };\n }\n\n const targetPid = pids[0];\n if (process.platform === 'win32') {\n writeGatewayRestartIntentSync({ targetPid });\n signalVerifiedGatewayPidSync(targetPid, 'SIGTERM');\n } else {\n authorizeGatewaySigusr1Restart();\n signalVerifiedGatewayPidSync(targetPid, 'SIGUSR1');\n }\n\n return {\n ok: true,\n mode: 'unmanaged',\n message: `Gateway restart signal sent to process ${targetPid} on port ${port}.`,\n };\n}\n\nasync function restartDaemonGateway(\n service: GatewayService,\n port: number,\n expectedVersion?: string,\n): Promise<UpdateRestartResult> {\n try {\n const loaded = await service.isLoaded({ env: process.env });\n if (!loaded) {\n return restartUnmanagedGateway(port);\n }\n await service.restart({ env: process.env, stdout: process.stdout });\n const healthy = await waitForGatewayHealth(port, expectedVersion);\n if (!healthy) {\n log.warn({ port, expectedVersion }, 'Gateway restart completed but health check timed out');\n return {\n ok: true,\n mode: 'daemon',\n message: 'Gateway service restarted; health check timed out (gateway may still be starting).',\n };\n }\n return {\n ok: true,\n mode: 'daemon',\n message: 'Gateway service restarted successfully.',\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log.warn({ err, port }, `Daemon restart failed: ${message}`);\n return { ok: false, mode: 'failed', message };\n }\n}\n\nexport async function maybeRestartGatewayAfterUpdate(params: {\n shouldRestart?: boolean;\n expectedVersion?: string;\n configPath?: string;\n triggerInProcessRestart?: InProcessRestartTrigger;\n}): Promise<UpdateRestartResult> {\n if (params.shouldRestart === false) {\n return { ok: true, mode: 'skipped', message: 'Restart skipped (--no-restart).' };\n }\n\n const config = loadConfig(params.configPath ?? resolveConfigPath());\n if (!isRestartEnabled(config)) {\n return {\n ok: false,\n mode: 'disabled',\n message:\n 'Gateway restart is disabled (commands.restart=false). Restart manually: xopc gateway restart',\n };\n }\n\n if (isRunningInsideGatewayService() && params.triggerInProcessRestart) {\n const result = params.triggerInProcessRestart();\n if (!result.ok) {\n return {\n ok: false,\n mode: 'failed',\n message: result.message ?? 'In-process gateway restart failed.',\n };\n }\n log.info({ mode: result.mode }, 'Scheduled in-process gateway restart after update');\n return {\n ok: true,\n mode: 'in-process',\n message: result.message ?? 'Gateway restart scheduled.',\n };\n }\n\n const service = await resolveGatewayService();\n const port = await resolveRestartPort(service);\n return restartDaemonGateway(service, port, params.expectedVersion);\n}\n"],"mappings":"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"}
|