denchclaw 2.0.0 → 2.0.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/apps/web/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/apps/web/.next/standalone/apps/web/.next/app-build-manifest.json +102 -102
- package/apps/web/.next/standalone/apps/web/.next/app-path-routes-manifest.json +34 -34
- package/apps/web/.next/standalone/apps/web/.next/build-manifest.json +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found.rsc +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/active/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stop/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stream/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/subagents/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/[jobId]/runs/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/[sessionId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/search-transcript/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/memories/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/switch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/[sessionId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/messages/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/assets/[...path]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/context/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/copy/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/introspect/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/query/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/delete/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/init/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/list/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/mkdir/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/move/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/display-field/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/[id]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/bulk-delete/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/options/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/[fieldId]/enum-rename/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/[fieldId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/reorder/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/views/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/open-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/path-info/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/query/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/raw-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/rename/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/reports/execute/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/search-index/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/suggest-files/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/switch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/thumbnail/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/tree/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/upload/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/virtual-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/watch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/write-binary/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/index.html +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/index.rsc +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace.rsc +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app-paths-manifest.json +34 -34
- package/apps/web/.next/standalone/apps/web/.next/server/functions-config-manifest.json +19 -19
- package/apps/web/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/apps/web/.next/standalone/package.json +2 -2
- package/dist/{cli-name-DUQ-cavN.js → cli-name-8WJ6gVD5.js} +36 -3
- package/dist/entry.js +2 -21
- package/dist/{register.bootstrap-D9YhQgaK.js → program-DhxRjAOe.js} +926 -91
- package/dist/{run-main-buUOQk0k.js → run-main-oHgYgpCR.js} +5 -6
- package/package.json +2 -2
- package/dist/links-BTx7dmFj.js +0 -57
- package/dist/program-DahL9wBm.js +0 -195
- package/dist/theme-uCBEEejb.js +0 -36
- /package/apps/web/.next/standalone/apps/web/.next/static/{oOwR1DKioMELtFQgLgScE → InPdqFM6GAVmsiSfDz98A}/_buildManifest.js +0 -0
- /package/apps/web/.next/standalone/apps/web/.next/static/{oOwR1DKioMELtFQgLgScE → InPdqFM6GAVmsiSfDz98A}/_ssgManifest.js +0 -0
- /package/apps/web/.next/static/{oOwR1DKioMELtFQgLgScE → InPdqFM6GAVmsiSfDz98A}/_buildManifest.js +0 -0
- /package/apps/web/.next/static/{oOwR1DKioMELtFQgLgScE → InPdqFM6GAVmsiSfDz98A}/_ssgManifest.js +0 -0
|
@@ -1,18 +1,517 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { c as hasFlag, d as applyCliProfileEnv, f as expandHomePrefix, i as defaultRuntime, p as resolveRequiredHomeDir, s as getPrimaryCommand, t as isTruthyEnvValue, u as hasRootVersionAlias } from "./entry.js";
|
|
2
|
+
import { a as hasEmittedCliBanner, c as theme, i as formatCliBannerLine, n as resolveCliName, o as VERSION, s as isRich, t as replaceCliName } from "./cli-name-8WJ6gVD5.js";
|
|
3
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
5
4
|
import process$1 from "node:process";
|
|
6
5
|
import os from "node:os";
|
|
7
6
|
import path from "node:path";
|
|
8
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
9
|
-
import {
|
|
7
|
+
import fs, { copyFileSync, cpSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { Command } from "commander";
|
|
10
9
|
import { confirm, isCancel, spinner } from "@clack/prompts";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { createConnection } from "node:net";
|
|
12
12
|
|
|
13
|
+
//#region src/infra/ports-lsof.ts
|
|
14
|
+
const LSOF_CANDIDATES = process.platform === "darwin" ? ["/usr/sbin/lsof", "/usr/bin/lsof"] : ["/usr/bin/lsof", "/usr/sbin/lsof"];
|
|
15
|
+
function resolveLsofCommandSync() {
|
|
16
|
+
for (const candidate of LSOF_CANDIDATES) try {
|
|
17
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
18
|
+
return candidate;
|
|
19
|
+
} catch {}
|
|
20
|
+
return "lsof";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/utils.ts
|
|
25
|
+
/**
|
|
26
|
+
* Escapes special regex characters in a string so it can be used in a RegExp constructor.
|
|
27
|
+
*/
|
|
28
|
+
function escapeRegExp(value) {
|
|
29
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
|
+
}
|
|
31
|
+
function sleep$1(ms) {
|
|
32
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
33
|
+
}
|
|
34
|
+
function resolveUserPath(input) {
|
|
35
|
+
const trimmed = input.trim();
|
|
36
|
+
if (!trimmed) return trimmed;
|
|
37
|
+
if (trimmed.startsWith("~")) {
|
|
38
|
+
const expanded = expandHomePrefix(trimmed, {
|
|
39
|
+
home: resolveRequiredHomeDir(process.env, os.homedir),
|
|
40
|
+
env: process.env,
|
|
41
|
+
homedir: os.homedir
|
|
42
|
+
});
|
|
43
|
+
return path.resolve(expanded);
|
|
44
|
+
}
|
|
45
|
+
return path.resolve(trimmed);
|
|
46
|
+
}
|
|
47
|
+
function resolveConfigDir(env = process.env, homedir = os.homedir) {
|
|
48
|
+
const override = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim();
|
|
49
|
+
if (override) return resolveUserPath(override);
|
|
50
|
+
const newDir = path.join(resolveRequiredHomeDir(env, homedir), ".openclaw");
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(newDir)) return newDir;
|
|
53
|
+
} catch {}
|
|
54
|
+
return newDir;
|
|
55
|
+
}
|
|
56
|
+
function formatTerminalLink(label, url, opts) {
|
|
57
|
+
const esc = "\x1B";
|
|
58
|
+
const safeLabel = label.replaceAll(esc, "");
|
|
59
|
+
const safeUrl = url.replaceAll(esc, "");
|
|
60
|
+
if (!(opts?.force === true ? true : opts?.force === false ? false : Boolean(process.stdout.isTTY))) return opts?.fallback ?? `${safeLabel} (${safeUrl})`;
|
|
61
|
+
return `\u001b]8;;${safeUrl}\u0007${safeLabel}\u001b]8;;\u0007`;
|
|
62
|
+
}
|
|
63
|
+
const CONFIG_DIR = resolveConfigDir();
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/cli/ports.ts
|
|
67
|
+
function parseLsofOutput(output) {
|
|
68
|
+
const lines = output.split(/\r?\n/).filter(Boolean);
|
|
69
|
+
const results = [];
|
|
70
|
+
let current = {};
|
|
71
|
+
for (const line of lines) if (line.startsWith("p")) {
|
|
72
|
+
if (current.pid) results.push(current);
|
|
73
|
+
current = { pid: Number.parseInt(line.slice(1), 10) };
|
|
74
|
+
} else if (line.startsWith("c")) current.command = line.slice(1);
|
|
75
|
+
if (current.pid) results.push(current);
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
function listPortListeners(port) {
|
|
79
|
+
try {
|
|
80
|
+
return parseLsofOutput(execFileSync(resolveLsofCommandSync(), [
|
|
81
|
+
"-nP",
|
|
82
|
+
`-iTCP:${port}`,
|
|
83
|
+
"-sTCP:LISTEN",
|
|
84
|
+
"-FpFc"
|
|
85
|
+
], { encoding: "utf-8" }));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
const status = err.status;
|
|
88
|
+
if (err.code === "ENOENT") throw new Error("lsof not found; required for --force", { cause: err });
|
|
89
|
+
if (status === 1) return [];
|
|
90
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/terminal/links.ts
|
|
96
|
+
const DOCS_ROOT = "https://docs.openclaw.ai";
|
|
97
|
+
function formatDocsLink(path, label, opts) {
|
|
98
|
+
const trimmed = path.trim();
|
|
99
|
+
const url = trimmed.startsWith("http") ? trimmed : `${DOCS_ROOT}${trimmed.startsWith("/") ? trimmed : `/${trimmed}`}`;
|
|
100
|
+
return formatTerminalLink(label ?? url, url, {
|
|
101
|
+
fallback: opts?.fallback ?? url,
|
|
102
|
+
force: opts?.force
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
13
107
|
//#region src/terminal/prompt-style.ts
|
|
14
108
|
const stylePromptMessage = (message) => isRich() ? theme.accent(message) : message;
|
|
15
109
|
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/cli/web-runtime.ts
|
|
112
|
+
const DEFAULT_WEB_APP_PORT = 3100;
|
|
113
|
+
const WEB_RUNTIME_DIRNAME = "web-runtime";
|
|
114
|
+
const WEB_RUNTIME_APP_DIRNAME = "app";
|
|
115
|
+
const WEB_RUNTIME_MANIFEST_FILENAME = "manifest.json";
|
|
116
|
+
const WEB_RUNTIME_PROCESS_FILENAME = "process.json";
|
|
117
|
+
const WEB_APP_PROBE_ATTEMPTS = 20;
|
|
118
|
+
const WEB_APP_PROBE_DELAY_MS = 750;
|
|
119
|
+
const WEB_APP_PROBE_TIMEOUT_MS = 1500;
|
|
120
|
+
const LEGACY_STANDALONE_SEGMENT = "/apps/web/.next/standalone/apps/web";
|
|
121
|
+
function normalizePathForMatch(value) {
|
|
122
|
+
return value.replaceAll("\\", "/").toLowerCase();
|
|
123
|
+
}
|
|
124
|
+
function isPathWithin(parentDir, candidatePath) {
|
|
125
|
+
const relative = path.relative(path.resolve(parentDir), path.resolve(candidatePath));
|
|
126
|
+
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
127
|
+
}
|
|
128
|
+
function parseOptionalPositiveInt(value) {
|
|
129
|
+
if (value === void 0) return;
|
|
130
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value), 10);
|
|
131
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return;
|
|
132
|
+
return parsed;
|
|
133
|
+
}
|
|
134
|
+
function ensureParentDir(filePath) {
|
|
135
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
function readJsonFile(filePath) {
|
|
138
|
+
if (!existsSync(filePath)) return null;
|
|
139
|
+
try {
|
|
140
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function writeJsonFile(filePath, value) {
|
|
146
|
+
ensureParentDir(filePath);
|
|
147
|
+
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
148
|
+
}
|
|
149
|
+
function isProcessAlive(pid) {
|
|
150
|
+
try {
|
|
151
|
+
process$1.kill(pid, 0);
|
|
152
|
+
return true;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return error.code !== "ESRCH";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function terminatePidWithEscalation(pid) {
|
|
158
|
+
try {
|
|
159
|
+
process$1.kill(pid, "SIGTERM");
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error.code === "ESRCH") return;
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
for (let i = 0; i < 8; i += 1) {
|
|
165
|
+
if (!isProcessAlive(pid)) return;
|
|
166
|
+
await sleep$1(100);
|
|
167
|
+
}
|
|
168
|
+
if (!isProcessAlive(pid)) return;
|
|
169
|
+
try {
|
|
170
|
+
process$1.kill(pid, "SIGKILL");
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error.code === "ESRCH") return;
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
for (let i = 0; i < 8; i += 1) {
|
|
176
|
+
if (!isProcessAlive(pid)) return;
|
|
177
|
+
await sleep$1(100);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function resolveCliPackageRoot() {
|
|
181
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
182
|
+
for (let i = 0; i < 5; i += 1) {
|
|
183
|
+
if (existsSync(path.join(dir, "package.json"))) return dir;
|
|
184
|
+
dir = path.dirname(dir);
|
|
185
|
+
}
|
|
186
|
+
return process$1.cwd();
|
|
187
|
+
}
|
|
188
|
+
function resolveProfileStateDir(profile, env = process$1.env) {
|
|
189
|
+
const home = resolveRequiredHomeDir(env, os.homedir);
|
|
190
|
+
return path.join(home, ".openclaw-dench");
|
|
191
|
+
}
|
|
192
|
+
function resolveManagedWebRuntimeDir(stateDir) {
|
|
193
|
+
return path.join(stateDir, WEB_RUNTIME_DIRNAME);
|
|
194
|
+
}
|
|
195
|
+
function resolveManagedWebRuntimeAppDir(stateDir) {
|
|
196
|
+
return path.join(resolveManagedWebRuntimeDir(stateDir), WEB_RUNTIME_APP_DIRNAME);
|
|
197
|
+
}
|
|
198
|
+
function resolveManagedWebRuntimeServerPath(stateDir) {
|
|
199
|
+
return path.join(resolveManagedWebRuntimeAppDir(stateDir), "server.js");
|
|
200
|
+
}
|
|
201
|
+
function resolveManagedWebRuntimeManifestPath(stateDir) {
|
|
202
|
+
return path.join(resolveManagedWebRuntimeDir(stateDir), WEB_RUNTIME_MANIFEST_FILENAME);
|
|
203
|
+
}
|
|
204
|
+
function resolveManagedWebRuntimeProcessPath(stateDir) {
|
|
205
|
+
return path.join(resolveManagedWebRuntimeDir(stateDir), WEB_RUNTIME_PROCESS_FILENAME);
|
|
206
|
+
}
|
|
207
|
+
function resolvePackagedStandaloneServerPath(packageRoot) {
|
|
208
|
+
return path.join(packageRoot, "apps/web/.next/standalone/apps/web/server.js");
|
|
209
|
+
}
|
|
210
|
+
function resolvePackagedStandaloneAppDir(packageRoot) {
|
|
211
|
+
return path.dirname(resolvePackagedStandaloneServerPath(packageRoot));
|
|
212
|
+
}
|
|
213
|
+
function readManagedWebRuntimeManifest(stateDir) {
|
|
214
|
+
return readJsonFile(resolveManagedWebRuntimeManifestPath(stateDir));
|
|
215
|
+
}
|
|
216
|
+
function readManagedWebRuntimeProcess(stateDir) {
|
|
217
|
+
return readJsonFile(resolveManagedWebRuntimeProcessPath(stateDir));
|
|
218
|
+
}
|
|
219
|
+
function writeManagedWebRuntimeManifest(stateDir, manifest) {
|
|
220
|
+
writeJsonFile(resolveManagedWebRuntimeManifestPath(stateDir), manifest);
|
|
221
|
+
return manifest;
|
|
222
|
+
}
|
|
223
|
+
function writeManagedWebRuntimeProcess(stateDir, processMeta) {
|
|
224
|
+
writeJsonFile(resolveManagedWebRuntimeProcessPath(stateDir), processMeta);
|
|
225
|
+
}
|
|
226
|
+
function clearManagedWebRuntimeProcess(stateDir) {
|
|
227
|
+
rmSync(resolveManagedWebRuntimeProcessPath(stateDir), { force: true });
|
|
228
|
+
}
|
|
229
|
+
function updateManifestLastPort(stateDir, webPort, gatewayPort) {
|
|
230
|
+
const manifest = readManagedWebRuntimeManifest(stateDir);
|
|
231
|
+
if (!manifest) return null;
|
|
232
|
+
return writeManagedWebRuntimeManifest(stateDir, {
|
|
233
|
+
...manifest,
|
|
234
|
+
lastPort: webPort,
|
|
235
|
+
lastGatewayPort: gatewayPort
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function evaluateWebProfilesPayload(payload) {
|
|
239
|
+
if (!payload || typeof payload !== "object") return {
|
|
240
|
+
ok: false,
|
|
241
|
+
reason: "response payload is not an object"
|
|
242
|
+
};
|
|
243
|
+
const data = payload;
|
|
244
|
+
if (!(Array.isArray(data.profiles) ? data.profiles : Array.isArray(data.workspaces) ? data.workspaces : void 0)) return {
|
|
245
|
+
ok: false,
|
|
246
|
+
reason: "response payload missing profiles/workspaces array"
|
|
247
|
+
};
|
|
248
|
+
if ((data.activeProfile === null || typeof data.activeProfile === "string" ? data.activeProfile : data.activeWorkspace === null || typeof data.activeWorkspace === "string" ? data.activeWorkspace : void 0) === void 0) return {
|
|
249
|
+
ok: false,
|
|
250
|
+
reason: "response payload missing active profile/workspace field"
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
ok: true,
|
|
254
|
+
reason: "profiles payload shape is valid"
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async function probeWebRuntime(port) {
|
|
258
|
+
const controller = new AbortController();
|
|
259
|
+
const timer = setTimeout(() => controller.abort(), WEB_APP_PROBE_TIMEOUT_MS);
|
|
260
|
+
try {
|
|
261
|
+
const response = await fetch(`http://127.0.0.1:${port}/api/profiles`, {
|
|
262
|
+
method: "GET",
|
|
263
|
+
signal: controller.signal,
|
|
264
|
+
redirect: "manual"
|
|
265
|
+
});
|
|
266
|
+
if (response.status < 200 || response.status >= 400) return {
|
|
267
|
+
ok: false,
|
|
268
|
+
status: response.status,
|
|
269
|
+
reason: `/api/profiles returned status ${response.status}`
|
|
270
|
+
};
|
|
271
|
+
const evaluation = evaluateWebProfilesPayload(await response.json().catch(() => null));
|
|
272
|
+
return {
|
|
273
|
+
ok: evaluation.ok,
|
|
274
|
+
status: response.status,
|
|
275
|
+
reason: evaluation.reason
|
|
276
|
+
};
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
reason: (error instanceof Error ? error.message : String(error)) || "probe failed"
|
|
281
|
+
};
|
|
282
|
+
} finally {
|
|
283
|
+
clearTimeout(timer);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function waitForWebRuntime(port) {
|
|
287
|
+
let lastResult = {
|
|
288
|
+
ok: false,
|
|
289
|
+
reason: "web runtime did not respond"
|
|
290
|
+
};
|
|
291
|
+
for (let attempt = 0; attempt < WEB_APP_PROBE_ATTEMPTS; attempt += 1) {
|
|
292
|
+
const result = await probeWebRuntime(port);
|
|
293
|
+
if (result.ok) return result;
|
|
294
|
+
lastResult = result;
|
|
295
|
+
await sleep$1(WEB_APP_PROBE_DELAY_MS);
|
|
296
|
+
}
|
|
297
|
+
return lastResult;
|
|
298
|
+
}
|
|
299
|
+
function classifyWebPortListener(params) {
|
|
300
|
+
if (!params.cwd) return "foreign";
|
|
301
|
+
if (isPathWithin(params.managedRuntimeAppDir, params.cwd)) return "managed";
|
|
302
|
+
if (normalizePathForMatch(params.cwd).includes(LEGACY_STANDALONE_SEGMENT)) return "legacy-standalone";
|
|
303
|
+
return "foreign";
|
|
304
|
+
}
|
|
305
|
+
function parseSemverMajor(version) {
|
|
306
|
+
if (!version) return null;
|
|
307
|
+
const match = version.trim().match(/^v?(\d+)(?:\.\d+)?(?:\.\d+)?(?:[-+].*)?$/u);
|
|
308
|
+
if (!match) return null;
|
|
309
|
+
const major = Number.parseInt(match[1], 10);
|
|
310
|
+
if (!Number.isFinite(major)) return null;
|
|
311
|
+
return major;
|
|
312
|
+
}
|
|
313
|
+
function evaluateMajorVersionTransition(params) {
|
|
314
|
+
const previousMajor = parseSemverMajor(params.previousVersion);
|
|
315
|
+
const currentMajor = parseSemverMajor(params.currentVersion);
|
|
316
|
+
return {
|
|
317
|
+
previousMajor,
|
|
318
|
+
currentMajor,
|
|
319
|
+
isMajorTransition: previousMajor !== null && currentMajor !== null && previousMajor !== currentMajor
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function resolveProcessCwd(pid) {
|
|
323
|
+
try {
|
|
324
|
+
const output = execFileSync(resolveLsofCommandSync(), [
|
|
325
|
+
"-nP",
|
|
326
|
+
"-a",
|
|
327
|
+
"-p",
|
|
328
|
+
String(pid),
|
|
329
|
+
"-d",
|
|
330
|
+
"cwd",
|
|
331
|
+
"-Fn"
|
|
332
|
+
], { encoding: "utf-8" });
|
|
333
|
+
for (const line of output.split(/\r?\n/)) if (line.startsWith("n")) {
|
|
334
|
+
const cwd = line.slice(1).trim();
|
|
335
|
+
if (cwd.length > 0) return cwd;
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
} catch {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function inspectWebPortListeners(port, stateDir) {
|
|
343
|
+
if (process$1.env.VITEST === "true" && process$1.env.OPENCLAW_TEST_REAL_PORTS !== "1") return [];
|
|
344
|
+
const listeners = listPortListeners(port);
|
|
345
|
+
const managedRuntimeAppDir = resolveManagedWebRuntimeAppDir(stateDir);
|
|
346
|
+
return listeners.map((listener) => {
|
|
347
|
+
const cwd = resolveProcessCwd(listener.pid);
|
|
348
|
+
const ownership = classifyWebPortListener({
|
|
349
|
+
cwd,
|
|
350
|
+
managedRuntimeAppDir
|
|
351
|
+
});
|
|
352
|
+
return {
|
|
353
|
+
...listener,
|
|
354
|
+
cwd,
|
|
355
|
+
ownership
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function readLastKnownWebPort(stateDir) {
|
|
360
|
+
const processPort = parseOptionalPositiveInt(readManagedWebRuntimeProcess(stateDir)?.port);
|
|
361
|
+
if (processPort) return processPort;
|
|
362
|
+
const manifestPort = parseOptionalPositiveInt(readManagedWebRuntimeManifest(stateDir)?.lastPort);
|
|
363
|
+
if (manifestPort) return manifestPort;
|
|
364
|
+
return DEFAULT_WEB_APP_PORT;
|
|
365
|
+
}
|
|
366
|
+
function installManagedWebRuntime(params) {
|
|
367
|
+
const runtimeDir = resolveManagedWebRuntimeDir(params.stateDir);
|
|
368
|
+
const runtimeAppDir = resolveManagedWebRuntimeAppDir(params.stateDir);
|
|
369
|
+
const runtimeServerPath = resolveManagedWebRuntimeServerPath(params.stateDir);
|
|
370
|
+
const sourceStandaloneServer = resolvePackagedStandaloneServerPath(params.packageRoot);
|
|
371
|
+
const sourceAppDir = resolvePackagedStandaloneAppDir(params.packageRoot);
|
|
372
|
+
if (!existsSync(sourceStandaloneServer)) return {
|
|
373
|
+
installed: false,
|
|
374
|
+
runtimeDir,
|
|
375
|
+
runtimeAppDir,
|
|
376
|
+
runtimeServerPath,
|
|
377
|
+
reason: "standalone-missing"
|
|
378
|
+
};
|
|
379
|
+
mkdirSync(runtimeDir, { recursive: true });
|
|
380
|
+
rmSync(runtimeAppDir, {
|
|
381
|
+
recursive: true,
|
|
382
|
+
force: true
|
|
383
|
+
});
|
|
384
|
+
cpSync(sourceAppDir, runtimeAppDir, {
|
|
385
|
+
recursive: true,
|
|
386
|
+
force: true
|
|
387
|
+
});
|
|
388
|
+
const manifest = {
|
|
389
|
+
schemaVersion: 1,
|
|
390
|
+
deployedDenchVersion: params.denchVersion,
|
|
391
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
392
|
+
sourceStandaloneServer,
|
|
393
|
+
...typeof params.webPort === "number" ? { lastPort: params.webPort } : {},
|
|
394
|
+
...typeof params.gatewayPort === "number" ? { lastGatewayPort: params.gatewayPort } : {}
|
|
395
|
+
};
|
|
396
|
+
writeManagedWebRuntimeManifest(params.stateDir, manifest);
|
|
397
|
+
return {
|
|
398
|
+
installed: true,
|
|
399
|
+
runtimeDir,
|
|
400
|
+
runtimeAppDir,
|
|
401
|
+
runtimeServerPath,
|
|
402
|
+
manifest
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
async function stopManagedWebRuntime(params) {
|
|
406
|
+
const listeners = inspectWebPortListeners(params.port, params.stateDir);
|
|
407
|
+
const includeLegacyStandalone = params.includeLegacyStandalone ?? true;
|
|
408
|
+
const stoppable = listeners.filter((listener) => listener.ownership === "managed" || includeLegacyStandalone && listener.ownership === "legacy-standalone");
|
|
409
|
+
const skippedForeign = listeners.filter((listener) => listener.ownership === "foreign");
|
|
410
|
+
const uniquePids = [...new Set(stoppable.map((listener) => listener.pid))];
|
|
411
|
+
const stoppedPids = [];
|
|
412
|
+
for (const pid of uniquePids) {
|
|
413
|
+
await terminatePidWithEscalation(pid);
|
|
414
|
+
if (!isProcessAlive(pid)) stoppedPids.push(pid);
|
|
415
|
+
}
|
|
416
|
+
const processMeta = readManagedWebRuntimeProcess(params.stateDir);
|
|
417
|
+
if (processMeta?.port === params.port && stoppedPids.includes(processMeta.pid)) clearManagedWebRuntimeProcess(params.stateDir);
|
|
418
|
+
if (inspectWebPortListeners(params.port, params.stateDir).filter((listener) => listener.ownership === "managed").length === 0) clearManagedWebRuntimeProcess(params.stateDir);
|
|
419
|
+
return {
|
|
420
|
+
port: params.port,
|
|
421
|
+
stoppedPids,
|
|
422
|
+
skippedForeignPids: [...new Set(skippedForeign.map((listener) => listener.pid))]
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function startManagedWebRuntime(params) {
|
|
426
|
+
const runtimeServerPath = resolveManagedWebRuntimeServerPath(params.stateDir);
|
|
427
|
+
if (!existsSync(runtimeServerPath)) return {
|
|
428
|
+
started: false,
|
|
429
|
+
runtimeServerPath,
|
|
430
|
+
reason: "runtime-missing"
|
|
431
|
+
};
|
|
432
|
+
const logsDir = path.join(params.stateDir, "logs");
|
|
433
|
+
mkdirSync(logsDir, { recursive: true });
|
|
434
|
+
const outFd = openSync(path.join(logsDir, "web-app.log"), "a");
|
|
435
|
+
const errFd = openSync(path.join(logsDir, "web-app.err.log"), "a");
|
|
436
|
+
const child = spawn(process$1.execPath, [runtimeServerPath], {
|
|
437
|
+
cwd: path.dirname(runtimeServerPath),
|
|
438
|
+
detached: true,
|
|
439
|
+
stdio: [
|
|
440
|
+
"ignore",
|
|
441
|
+
outFd,
|
|
442
|
+
errFd
|
|
443
|
+
],
|
|
444
|
+
env: {
|
|
445
|
+
...process$1.env,
|
|
446
|
+
...params.env,
|
|
447
|
+
PORT: String(params.port),
|
|
448
|
+
HOSTNAME: "127.0.0.1",
|
|
449
|
+
OPENCLAW_GATEWAY_PORT: String(params.gatewayPort)
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
child.unref();
|
|
453
|
+
writeManagedWebRuntimeProcess(params.stateDir, {
|
|
454
|
+
pid: child.pid ?? -1,
|
|
455
|
+
port: params.port,
|
|
456
|
+
gatewayPort: params.gatewayPort,
|
|
457
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
458
|
+
runtimeAppDir: path.dirname(runtimeServerPath)
|
|
459
|
+
});
|
|
460
|
+
updateManifestLastPort(params.stateDir, params.port, params.gatewayPort);
|
|
461
|
+
return {
|
|
462
|
+
started: true,
|
|
463
|
+
pid: child.pid ?? -1,
|
|
464
|
+
runtimeServerPath
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
async function ensureManagedWebRuntime(params) {
|
|
468
|
+
if (!installManagedWebRuntime({
|
|
469
|
+
stateDir: params.stateDir,
|
|
470
|
+
packageRoot: params.packageRoot,
|
|
471
|
+
denchVersion: params.denchVersion,
|
|
472
|
+
webPort: params.port,
|
|
473
|
+
gatewayPort: params.gatewayPort
|
|
474
|
+
}).installed) return {
|
|
475
|
+
ready: false,
|
|
476
|
+
reason: "standalone web build is missing from package"
|
|
477
|
+
};
|
|
478
|
+
await stopManagedWebRuntime({
|
|
479
|
+
stateDir: params.stateDir,
|
|
480
|
+
port: params.port,
|
|
481
|
+
includeLegacyStandalone: true
|
|
482
|
+
});
|
|
483
|
+
const foreign = inspectWebPortListeners(params.port, params.stateDir).filter((listener) => listener.ownership === "foreign");
|
|
484
|
+
if (foreign.length > 0) {
|
|
485
|
+
const detail = foreign.map((listener) => `${listener.pid}${listener.command ? `:${listener.command}` : ""}`).join(", ");
|
|
486
|
+
return {
|
|
487
|
+
ready: false,
|
|
488
|
+
reason: `port ${params.port} is owned by non-Dench process(es): ${detail}`
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
if (!startManagedWebRuntime({
|
|
492
|
+
stateDir: params.stateDir,
|
|
493
|
+
port: params.port,
|
|
494
|
+
gatewayPort: params.gatewayPort
|
|
495
|
+
}).started) return {
|
|
496
|
+
ready: false,
|
|
497
|
+
reason: "managed web runtime is missing after install"
|
|
498
|
+
};
|
|
499
|
+
const probe = await waitForWebRuntime(params.port);
|
|
500
|
+
return {
|
|
501
|
+
ready: probe.ok,
|
|
502
|
+
reason: probe.reason
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function resolveOpenClawCommandOrThrow() {
|
|
506
|
+
try {
|
|
507
|
+
const first = execFileSync(process$1.platform === "win32" ? "where" : "which", ["openclaw"], { encoding: "utf-8" }).trim().split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
508
|
+
if (first) return first;
|
|
509
|
+
throw new Error("openclaw command not found");
|
|
510
|
+
} catch {
|
|
511
|
+
throw new Error("Global `openclaw` CLI was not found on PATH. Install it with: npm install -g openclaw");
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
16
515
|
//#endregion
|
|
17
516
|
//#region src/cli/workspace-seed.ts
|
|
18
517
|
const SEED_OBJECTS = [
|
|
@@ -328,13 +827,9 @@ function seedWorkspaceFromAssets(params) {
|
|
|
328
827
|
//#endregion
|
|
329
828
|
//#region src/cli/bootstrap-external.ts
|
|
330
829
|
const DEFAULT_DENCHCLAW_PROFILE = "dench";
|
|
331
|
-
const DENCHCLAW_STATE_DIRNAME = ".openclaw-dench";
|
|
332
830
|
const DEFAULT_GATEWAY_PORT = 18789;
|
|
333
831
|
const DENCHCLAW_GATEWAY_PORT_START = 19001;
|
|
334
832
|
const MAX_PORT_SCAN_ATTEMPTS = 100;
|
|
335
|
-
const DEFAULT_WEB_APP_PORT = 3100;
|
|
336
|
-
const WEB_APP_PROBE_ATTEMPTS = 20;
|
|
337
|
-
const WEB_APP_PROBE_DELAY_MS = 750;
|
|
338
833
|
const DEFAULT_BOOTSTRAP_ROLLOUT_STAGE = "default";
|
|
339
834
|
const DEFAULT_GATEWAY_LAUNCH_AGENT_LABEL = "ai.openclaw.gateway";
|
|
340
835
|
const REQUIRED_TOOLS_PROFILE = "full";
|
|
@@ -397,7 +892,7 @@ async function runCommandWithTimeout(argv, options) {
|
|
|
397
892
|
});
|
|
398
893
|
});
|
|
399
894
|
}
|
|
400
|
-
function parseOptionalPort(value) {
|
|
895
|
+
function parseOptionalPort$1(value) {
|
|
401
896
|
if (value === void 0) return;
|
|
402
897
|
const raw = typeof value === "number" ? value : Number.parseInt(String(value), 10);
|
|
403
898
|
if (!Number.isFinite(raw) || raw <= 0) return;
|
|
@@ -447,16 +942,12 @@ function normalizeVersionOutput(raw) {
|
|
|
447
942
|
const first = raw?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
448
943
|
return first && first.length > 0 ? first : void 0;
|
|
449
944
|
}
|
|
450
|
-
function firstNonEmptyLine(...values) {
|
|
945
|
+
function firstNonEmptyLine$1(...values) {
|
|
451
946
|
for (const value of values) {
|
|
452
947
|
const first = value?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
453
948
|
if (first) return first;
|
|
454
949
|
}
|
|
455
950
|
}
|
|
456
|
-
function resolveProfileStateDir(profile, env = process$1.env) {
|
|
457
|
-
const home = resolveRequiredHomeDir(env, os.homedir);
|
|
458
|
-
return path.join(home, DENCHCLAW_STATE_DIRNAME);
|
|
459
|
-
}
|
|
460
951
|
function resolveGatewayLaunchAgentLabel(profile) {
|
|
461
952
|
const normalized = profile.trim().toLowerCase();
|
|
462
953
|
if (!normalized || normalized === "default") return DEFAULT_GATEWAY_LAUNCH_AGENT_LABEL;
|
|
@@ -551,67 +1042,6 @@ async function ensureToolsProfile(openclawCommand, profile) {
|
|
|
551
1042
|
errorMessage: `Failed to set tools.profile=${REQUIRED_TOOLS_PROFILE}.`
|
|
552
1043
|
});
|
|
553
1044
|
}
|
|
554
|
-
async function probeForWebApp(port) {
|
|
555
|
-
const controller = new AbortController();
|
|
556
|
-
const timer = setTimeout(() => controller.abort(), 1500);
|
|
557
|
-
try {
|
|
558
|
-
const response = await fetch(`http://127.0.0.1:${port}/api/profiles`, {
|
|
559
|
-
method: "GET",
|
|
560
|
-
signal: controller.signal,
|
|
561
|
-
redirect: "manual"
|
|
562
|
-
});
|
|
563
|
-
if (response.status < 200 || response.status >= 400) return false;
|
|
564
|
-
const payload = await response.json().catch(() => null);
|
|
565
|
-
return Boolean(payload && typeof payload === "object" && Array.isArray(payload.profiles) && typeof payload.activeProfile === "string");
|
|
566
|
-
} catch {
|
|
567
|
-
return false;
|
|
568
|
-
} finally {
|
|
569
|
-
clearTimeout(timer);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
async function waitForWebApp(preferredPort) {
|
|
573
|
-
for (let attempt = 0; attempt < WEB_APP_PROBE_ATTEMPTS; attempt += 1) {
|
|
574
|
-
if (await probeForWebApp(preferredPort)) return true;
|
|
575
|
-
await sleep(WEB_APP_PROBE_DELAY_MS);
|
|
576
|
-
}
|
|
577
|
-
return false;
|
|
578
|
-
}
|
|
579
|
-
function resolveCliPackageRoot() {
|
|
580
|
-
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
581
|
-
for (let i = 0; i < 5; i++) {
|
|
582
|
-
if (existsSync(path.join(dir, "package.json"))) return dir;
|
|
583
|
-
dir = path.dirname(dir);
|
|
584
|
-
}
|
|
585
|
-
return process$1.cwd();
|
|
586
|
-
}
|
|
587
|
-
/**
|
|
588
|
-
* Spawn the pre-built standalone Next.js server as a detached background
|
|
589
|
-
* process if it isn't already running on the target port.
|
|
590
|
-
*/
|
|
591
|
-
function startWebAppIfNeeded(port, stateDir, gatewayPort) {
|
|
592
|
-
const pkgRoot = resolveCliPackageRoot();
|
|
593
|
-
const standaloneServer = path.join(pkgRoot, "apps/web/.next/standalone/apps/web/server.js");
|
|
594
|
-
if (!existsSync(standaloneServer)) return;
|
|
595
|
-
const logDir = path.join(stateDir, "logs");
|
|
596
|
-
mkdirSync(logDir, { recursive: true });
|
|
597
|
-
const outFd = openSync(path.join(logDir, "web-app.log"), "a");
|
|
598
|
-
const errFd = openSync(path.join(logDir, "web-app.err.log"), "a");
|
|
599
|
-
spawn(process$1.execPath, [standaloneServer], {
|
|
600
|
-
cwd: path.dirname(standaloneServer),
|
|
601
|
-
detached: true,
|
|
602
|
-
stdio: [
|
|
603
|
-
"ignore",
|
|
604
|
-
outFd,
|
|
605
|
-
errFd
|
|
606
|
-
],
|
|
607
|
-
env: {
|
|
608
|
-
...process$1.env,
|
|
609
|
-
PORT: String(port),
|
|
610
|
-
HOSTNAME: "127.0.0.1",
|
|
611
|
-
OPENCLAW_GATEWAY_PORT: String(gatewayPort)
|
|
612
|
-
}
|
|
613
|
-
}).unref();
|
|
614
|
-
}
|
|
615
1045
|
async function runOpenClaw(openclawCommand, args, timeoutMs, ioMode = "capture", env) {
|
|
616
1046
|
return await runCommandWithTimeout([openclawCommand, ...args], {
|
|
617
1047
|
timeoutMs,
|
|
@@ -622,7 +1052,7 @@ async function runOpenClaw(openclawCommand, args, timeoutMs, ioMode = "capture",
|
|
|
622
1052
|
async function runOpenClawOrThrow(params) {
|
|
623
1053
|
const result = await runOpenClaw(params.openclawCommand, params.args, params.timeoutMs);
|
|
624
1054
|
if (result.code === 0) return result;
|
|
625
|
-
const detail = firstNonEmptyLine(result.stderr, result.stdout);
|
|
1055
|
+
const detail = firstNonEmptyLine$1(result.stderr, result.stdout);
|
|
626
1056
|
throw new Error(detail ? `${params.errorMessage}\n${detail}` : params.errorMessage);
|
|
627
1057
|
}
|
|
628
1058
|
/**
|
|
@@ -632,7 +1062,7 @@ async function runOpenClawOrThrow(params) {
|
|
|
632
1062
|
async function runOpenClawInteractiveOrThrow(params) {
|
|
633
1063
|
const result = await runOpenClaw(params.openclawCommand, params.args, params.timeoutMs, "inherit");
|
|
634
1064
|
if (result.code === 0) return result;
|
|
635
|
-
const detail = firstNonEmptyLine(result.stderr, result.stdout);
|
|
1065
|
+
const detail = firstNonEmptyLine$1(result.stderr, result.stdout);
|
|
636
1066
|
throw new Error(detail ? `${params.errorMessage}\n${detail}` : params.errorMessage);
|
|
637
1067
|
}
|
|
638
1068
|
/**
|
|
@@ -689,7 +1119,7 @@ async function runOpenClawWithProgress(params) {
|
|
|
689
1119
|
s.stop(params.successMessage);
|
|
690
1120
|
return result;
|
|
691
1121
|
}
|
|
692
|
-
const detail = firstNonEmptyLine(result.stderr, result.stdout);
|
|
1122
|
+
const detail = firstNonEmptyLine$1(result.stderr, result.stdout);
|
|
693
1123
|
const stopMessage = detail ? `${params.errorMessage}: ${detail}` : params.errorMessage;
|
|
694
1124
|
s.stop(stopMessage);
|
|
695
1125
|
throw new Error(detail ? `${params.errorMessage}\n${detail}` : params.errorMessage);
|
|
@@ -737,7 +1167,7 @@ async function resolveNpmGlobalBinDir() {
|
|
|
737
1167
|
"-g"
|
|
738
1168
|
], { timeoutMs: 8e3 }).catch(() => null);
|
|
739
1169
|
if (!result || result.code !== 0) return;
|
|
740
|
-
const prefix = firstNonEmptyLine(result.stdout);
|
|
1170
|
+
const prefix = firstNonEmptyLine$1(result.stdout);
|
|
741
1171
|
if (!prefix) return;
|
|
742
1172
|
return process$1.platform === "win32" ? prefix : path.join(prefix, "bin");
|
|
743
1173
|
}
|
|
@@ -748,7 +1178,7 @@ function resolveGlobalOpenClawCommand(globalBinDir) {
|
|
|
748
1178
|
async function resolveShellOpenClawPath() {
|
|
749
1179
|
const result = await runCommandWithTimeout([process$1.platform === "win32" ? "where" : "which", "openclaw"], { timeoutMs: 4e3 }).catch(() => null);
|
|
750
1180
|
if (!result || result.code !== 0) return;
|
|
751
|
-
return firstNonEmptyLine(result.stdout);
|
|
1181
|
+
return firstNonEmptyLine$1(result.stdout);
|
|
752
1182
|
}
|
|
753
1183
|
function isProjectLocalOpenClawPath(commandPath) {
|
|
754
1184
|
if (!commandPath) return false;
|
|
@@ -803,7 +1233,7 @@ async function probeGateway(openclawCommand, profile, gatewayPort) {
|
|
|
803
1233
|
if (result.code === 0) return { ok: true };
|
|
804
1234
|
return {
|
|
805
1235
|
ok: false,
|
|
806
|
-
detail: firstNonEmptyLine(result.stderr, result.stdout)
|
|
1236
|
+
detail: firstNonEmptyLine$1(result.stderr, result.stdout)
|
|
807
1237
|
};
|
|
808
1238
|
}
|
|
809
1239
|
function readLogTail(logPath, maxLines = 16) {
|
|
@@ -911,7 +1341,7 @@ async function attemptGatewayAutoFix(params) {
|
|
|
911
1341
|
steps.push({
|
|
912
1342
|
name: command.name,
|
|
913
1343
|
ok: result.code === 0,
|
|
914
|
-
detail: result.code === 0 ? void 0 : firstNonEmptyLine(result.stderr, result.stdout)
|
|
1344
|
+
detail: result.code === 0 ? void 0 : firstNonEmptyLine$1(result.stderr, result.stdout)
|
|
915
1345
|
});
|
|
916
1346
|
}
|
|
917
1347
|
let finalProbe = await probeGateway(params.openclawCommand, params.profile, params.gatewayPort);
|
|
@@ -948,7 +1378,11 @@ function remediationForGatewayFailure(detail, port, profile) {
|
|
|
948
1378
|
return `Run \`openclaw --profile ${profile} doctor --fix\` and retry \`denchclaw bootstrap --profile ${profile} --force-onboard\`.`;
|
|
949
1379
|
}
|
|
950
1380
|
function remediationForWebUiFailure(port) {
|
|
951
|
-
return
|
|
1381
|
+
return [
|
|
1382
|
+
`Web UI did not respond on ${port}.`,
|
|
1383
|
+
`Run \`dench update --web-port ${port}\` to refresh the managed web runtime.`,
|
|
1384
|
+
`If the port is stuck, run \`dench stop --web-port ${port}\` first.`
|
|
1385
|
+
].join(" ");
|
|
952
1386
|
}
|
|
953
1387
|
function describeWorkspaceSeedResult(result) {
|
|
954
1388
|
if (result.seeded) return `seeded ${result.dbPath}`;
|
|
@@ -1116,7 +1550,7 @@ async function bootstrapCommand(opts, runtime = defaultRuntime) {
|
|
|
1116
1550
|
successMessage: "OpenClaw is up to date.",
|
|
1117
1551
|
errorMessage: "OpenClaw update failed"
|
|
1118
1552
|
});
|
|
1119
|
-
const explicitPort = parseOptionalPort(opts.gatewayPort);
|
|
1553
|
+
const explicitPort = parseOptionalPort$1(opts.gatewayPort);
|
|
1120
1554
|
let gatewayPort;
|
|
1121
1555
|
let portAutoAssigned = false;
|
|
1122
1556
|
if (explicitPort) gatewayPort = explicitPort;
|
|
@@ -1156,9 +1590,10 @@ async function bootstrapCommand(opts, runtime = defaultRuntime) {
|
|
|
1156
1590
|
timeoutMs: 12 * 6e4,
|
|
1157
1591
|
errorMessage: "OpenClaw onboarding failed."
|
|
1158
1592
|
});
|
|
1593
|
+
const packageRoot = resolveCliPackageRoot();
|
|
1159
1594
|
const workspaceSeed = seedWorkspaceFromAssets({
|
|
1160
1595
|
workspaceDir,
|
|
1161
|
-
packageRoot
|
|
1596
|
+
packageRoot
|
|
1162
1597
|
});
|
|
1163
1598
|
await ensureGatewayModeLocal(openclawCommand, profile);
|
|
1164
1599
|
await ensureGatewayPort(openclawCommand, profile, gatewayPort);
|
|
@@ -1180,9 +1615,15 @@ async function bootstrapCommand(opts, runtime = defaultRuntime) {
|
|
|
1180
1615
|
};
|
|
1181
1616
|
}
|
|
1182
1617
|
const gatewayUrl = `ws://127.0.0.1:${gatewayPort}`;
|
|
1183
|
-
const preferredWebPort = parseOptionalPort(opts.webPort) ?? DEFAULT_WEB_APP_PORT;
|
|
1184
|
-
|
|
1185
|
-
|
|
1618
|
+
const preferredWebPort = parseOptionalPort$1(opts.webPort) ?? DEFAULT_WEB_APP_PORT;
|
|
1619
|
+
const webRuntimeStatus = await ensureManagedWebRuntime({
|
|
1620
|
+
stateDir,
|
|
1621
|
+
packageRoot,
|
|
1622
|
+
denchVersion: VERSION,
|
|
1623
|
+
port: preferredWebPort,
|
|
1624
|
+
gatewayPort
|
|
1625
|
+
});
|
|
1626
|
+
const webReachable = webRuntimeStatus.ready;
|
|
1186
1627
|
const webUrl = `http://localhost:${preferredWebPort}`;
|
|
1187
1628
|
const diagnostics = buildBootstrapDiagnostics({
|
|
1188
1629
|
profile,
|
|
@@ -1200,6 +1641,7 @@ async function bootstrapCommand(opts, runtime = defaultRuntime) {
|
|
|
1200
1641
|
const shouldOpen = !opts.noOpen && !opts.json;
|
|
1201
1642
|
const opened = shouldOpen ? await openUrl(webUrl) : false;
|
|
1202
1643
|
if (!opts.json) {
|
|
1644
|
+
if (!webRuntimeStatus.ready) runtime.log(theme.warn(`Managed web runtime check failed: ${webRuntimeStatus.reason}`));
|
|
1203
1645
|
if (installResult.installed) runtime.log(theme.muted("Installed global OpenClaw CLI via npm."));
|
|
1204
1646
|
if (isProjectLocalOpenClawPath(installResult.shellCommandPath)) {
|
|
1205
1647
|
runtime.log(theme.warn(`\`openclaw\` currently resolves to a project-local binary (${installResult.shellCommandPath}).`));
|
|
@@ -1327,4 +1769,397 @@ function registerBootstrapCommand(program) {
|
|
|
1327
1769
|
}
|
|
1328
1770
|
|
|
1329
1771
|
//#endregion
|
|
1330
|
-
|
|
1772
|
+
//#region src/cli/web-runtime-command.ts
|
|
1773
|
+
function parseOptionalPort(value) {
|
|
1774
|
+
if (value === void 0) return;
|
|
1775
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value), 10);
|
|
1776
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return;
|
|
1777
|
+
return parsed;
|
|
1778
|
+
}
|
|
1779
|
+
function firstNonEmptyLine(...values) {
|
|
1780
|
+
for (const value of values) {
|
|
1781
|
+
const first = value?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
1782
|
+
if (first) return first;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async function runOpenClawUpdateWithProgress(openclawCommand) {
|
|
1786
|
+
const s = spinner();
|
|
1787
|
+
s.start("Updating OpenClaw (required for this major Dench upgrade)...");
|
|
1788
|
+
const result = await new Promise((resolve, reject) => {
|
|
1789
|
+
const child = spawn(openclawCommand, ["update", "--yes"], {
|
|
1790
|
+
stdio: [
|
|
1791
|
+
"ignore",
|
|
1792
|
+
"pipe",
|
|
1793
|
+
"pipe"
|
|
1794
|
+
],
|
|
1795
|
+
env: process$1.env
|
|
1796
|
+
});
|
|
1797
|
+
let stdout = "";
|
|
1798
|
+
let stderr = "";
|
|
1799
|
+
let settled = false;
|
|
1800
|
+
const timer = setTimeout(() => {
|
|
1801
|
+
if (!settled) child.kill("SIGKILL");
|
|
1802
|
+
}, 8 * 6e4);
|
|
1803
|
+
const updateSpinner = (chunk) => {
|
|
1804
|
+
const line = chunk.split(/\r?\n/).map((item) => item.trim()).filter(Boolean).at(-1);
|
|
1805
|
+
if (line) s.message(line.length > 72 ? `${line.slice(0, 69)}...` : line);
|
|
1806
|
+
};
|
|
1807
|
+
child.stdout?.on("data", (chunk) => {
|
|
1808
|
+
const text = String(chunk);
|
|
1809
|
+
stdout += text;
|
|
1810
|
+
updateSpinner(text);
|
|
1811
|
+
});
|
|
1812
|
+
child.stderr?.on("data", (chunk) => {
|
|
1813
|
+
const text = String(chunk);
|
|
1814
|
+
stderr += text;
|
|
1815
|
+
updateSpinner(text);
|
|
1816
|
+
});
|
|
1817
|
+
child.once("error", (error) => {
|
|
1818
|
+
if (settled) return;
|
|
1819
|
+
settled = true;
|
|
1820
|
+
clearTimeout(timer);
|
|
1821
|
+
reject(error);
|
|
1822
|
+
});
|
|
1823
|
+
child.once("close", (code) => {
|
|
1824
|
+
if (settled) return;
|
|
1825
|
+
settled = true;
|
|
1826
|
+
clearTimeout(timer);
|
|
1827
|
+
resolve({
|
|
1828
|
+
code: typeof code === "number" ? code : 1,
|
|
1829
|
+
stdout,
|
|
1830
|
+
stderr
|
|
1831
|
+
});
|
|
1832
|
+
});
|
|
1833
|
+
});
|
|
1834
|
+
if (result.code === 0) {
|
|
1835
|
+
s.stop("OpenClaw update complete.");
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
const detail = firstNonEmptyLine(result.stderr, result.stdout);
|
|
1839
|
+
s.stop(detail ? `OpenClaw update failed: ${detail}` : "OpenClaw update failed.");
|
|
1840
|
+
throw new Error(detail ? `OpenClaw update failed.\n${detail}` : "OpenClaw update failed. Fix this before running `dench update` again.");
|
|
1841
|
+
}
|
|
1842
|
+
async function ensureMajorUpgradeAcknowledged(params) {
|
|
1843
|
+
if (!params.required) return;
|
|
1844
|
+
if (params.nonInteractive || !process$1.stdin.isTTY) {
|
|
1845
|
+
if (!params.yes) throw new Error(`Major Dench upgrade detected (${params.previousVersion ?? "unknown"} -> ${params.currentVersion}). Re-run with --yes to approve the required OpenClaw update.`);
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
if (params.yes) return;
|
|
1849
|
+
const decision = await confirm({
|
|
1850
|
+
message: stylePromptMessage(`Major Dench upgrade detected (${params.previousVersion ?? "unknown"} -> ${params.currentVersion}). Continue with mandatory OpenClaw update now?`),
|
|
1851
|
+
initialValue: true
|
|
1852
|
+
});
|
|
1853
|
+
if (isCancel(decision) || !decision) {
|
|
1854
|
+
params.runtime.log(theme.warn("Update cancelled. OpenClaw update is required for major upgrades."));
|
|
1855
|
+
throw new Error("Major upgrade requires OpenClaw update approval.");
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
function resolveGatewayPort(stateDir) {
|
|
1859
|
+
const manifest = readManagedWebRuntimeManifest(stateDir);
|
|
1860
|
+
if (typeof manifest?.lastGatewayPort === "number" && Number.isFinite(manifest.lastGatewayPort) && manifest.lastGatewayPort > 0) return manifest.lastGatewayPort;
|
|
1861
|
+
for (const name of ["openclaw.json", "config.json"]) {
|
|
1862
|
+
const port = readConfigGatewayPort(path.join(stateDir, name));
|
|
1863
|
+
if (typeof port === "number") return port;
|
|
1864
|
+
}
|
|
1865
|
+
return 18789;
|
|
1866
|
+
}
|
|
1867
|
+
function readConfigGatewayPort(configPath) {
|
|
1868
|
+
try {
|
|
1869
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1870
|
+
const parsedPort = typeof raw.gateway?.port === "number" ? raw.gateway.port : typeof raw.gateway?.port === "string" ? Number.parseInt(raw.gateway.port, 10) : void 0;
|
|
1871
|
+
if (typeof parsedPort === "number" && Number.isFinite(parsedPort) && parsedPort > 0) return parsedPort;
|
|
1872
|
+
return;
|
|
1873
|
+
} catch {
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
async function updateWebRuntimeCommand(opts, runtime = defaultRuntime) {
|
|
1878
|
+
const appliedProfile = applyCliProfileEnv({ profile: opts.profile });
|
|
1879
|
+
const profile = appliedProfile.effectiveProfile;
|
|
1880
|
+
if (appliedProfile.warning && !opts.json) runtime.log(theme.warn(appliedProfile.warning));
|
|
1881
|
+
const stateDir = resolveProfileStateDir(profile);
|
|
1882
|
+
const packageRoot = resolveCliPackageRoot();
|
|
1883
|
+
const previousManifest = readManagedWebRuntimeManifest(stateDir);
|
|
1884
|
+
const transition = evaluateMajorVersionTransition({
|
|
1885
|
+
previousVersion: previousManifest?.deployedDenchVersion,
|
|
1886
|
+
currentVersion: VERSION
|
|
1887
|
+
});
|
|
1888
|
+
const nonInteractive = Boolean(opts.nonInteractive || opts.json);
|
|
1889
|
+
await ensureMajorUpgradeAcknowledged({
|
|
1890
|
+
required: transition.isMajorTransition,
|
|
1891
|
+
previousVersion: previousManifest?.deployedDenchVersion,
|
|
1892
|
+
currentVersion: VERSION,
|
|
1893
|
+
nonInteractive,
|
|
1894
|
+
yes: Boolean(opts.yes),
|
|
1895
|
+
runtime
|
|
1896
|
+
});
|
|
1897
|
+
if (transition.isMajorTransition) await runOpenClawUpdateWithProgress(resolveOpenClawCommandOrThrow());
|
|
1898
|
+
const selectedPort = parseOptionalPort(opts.webPort) ?? parseOptionalPort(previousManifest?.lastPort) ?? readLastKnownWebPort(stateDir) ?? DEFAULT_WEB_APP_PORT;
|
|
1899
|
+
const gatewayPort = resolveGatewayPort(stateDir);
|
|
1900
|
+
const stopResult = await stopManagedWebRuntime({
|
|
1901
|
+
stateDir,
|
|
1902
|
+
port: selectedPort,
|
|
1903
|
+
includeLegacyStandalone: true
|
|
1904
|
+
});
|
|
1905
|
+
const ensureResult = await ensureManagedWebRuntime({
|
|
1906
|
+
stateDir,
|
|
1907
|
+
packageRoot,
|
|
1908
|
+
denchVersion: VERSION,
|
|
1909
|
+
port: selectedPort,
|
|
1910
|
+
gatewayPort
|
|
1911
|
+
});
|
|
1912
|
+
const summary = {
|
|
1913
|
+
profile,
|
|
1914
|
+
webPort: selectedPort,
|
|
1915
|
+
version: VERSION,
|
|
1916
|
+
majorGate: {
|
|
1917
|
+
required: transition.isMajorTransition,
|
|
1918
|
+
previousVersion: previousManifest?.deployedDenchVersion,
|
|
1919
|
+
currentVersion: VERSION
|
|
1920
|
+
},
|
|
1921
|
+
stoppedPids: stopResult.stoppedPids,
|
|
1922
|
+
skippedForeignPids: stopResult.skippedForeignPids,
|
|
1923
|
+
ready: ensureResult.ready,
|
|
1924
|
+
reason: ensureResult.reason
|
|
1925
|
+
};
|
|
1926
|
+
if (!opts.json) {
|
|
1927
|
+
runtime.log("");
|
|
1928
|
+
runtime.log(theme.heading("Dench web update"));
|
|
1929
|
+
runtime.log(`Profile: ${profile}`);
|
|
1930
|
+
runtime.log(`Version: ${VERSION}`);
|
|
1931
|
+
runtime.log(`Web port: ${selectedPort}`);
|
|
1932
|
+
runtime.log(`Stopped web processes: ${summary.stoppedPids.length}`);
|
|
1933
|
+
if (summary.skippedForeignPids.length > 0) runtime.log(theme.warn(`Skipped non-Dench listeners on ${selectedPort}: ${summary.skippedForeignPids.join(", ")}`));
|
|
1934
|
+
runtime.log(`Web runtime: ${summary.ready ? "ready" : "not ready"}`);
|
|
1935
|
+
if (!summary.ready) runtime.log(theme.warn(summary.reason));
|
|
1936
|
+
}
|
|
1937
|
+
if (!summary.ready) throw new Error(`Web runtime update failed: ${summary.reason}`);
|
|
1938
|
+
if (opts.json) runtime.log(JSON.stringify(summary, null, 2));
|
|
1939
|
+
return summary;
|
|
1940
|
+
}
|
|
1941
|
+
async function stopWebRuntimeCommand(opts, runtime = defaultRuntime) {
|
|
1942
|
+
const appliedProfile = applyCliProfileEnv({ profile: opts.profile });
|
|
1943
|
+
const profile = appliedProfile.effectiveProfile;
|
|
1944
|
+
if (appliedProfile.warning && !opts.json) runtime.log(theme.warn(appliedProfile.warning));
|
|
1945
|
+
const stateDir = resolveProfileStateDir(profile);
|
|
1946
|
+
const selectedPort = parseOptionalPort(opts.webPort) ?? readLastKnownWebPort(stateDir);
|
|
1947
|
+
const stopResult = await stopManagedWebRuntime({
|
|
1948
|
+
stateDir,
|
|
1949
|
+
port: selectedPort,
|
|
1950
|
+
includeLegacyStandalone: true
|
|
1951
|
+
});
|
|
1952
|
+
const summary = {
|
|
1953
|
+
profile,
|
|
1954
|
+
webPort: selectedPort,
|
|
1955
|
+
stoppedPids: stopResult.stoppedPids,
|
|
1956
|
+
skippedForeignPids: stopResult.skippedForeignPids
|
|
1957
|
+
};
|
|
1958
|
+
if (opts.json) {
|
|
1959
|
+
runtime.log(JSON.stringify(summary, null, 2));
|
|
1960
|
+
return summary;
|
|
1961
|
+
}
|
|
1962
|
+
runtime.log("");
|
|
1963
|
+
runtime.log(theme.heading("Dench web stop"));
|
|
1964
|
+
runtime.log(`Profile: ${profile}`);
|
|
1965
|
+
runtime.log(`Web port: ${selectedPort}`);
|
|
1966
|
+
runtime.log(summary.stoppedPids.length > 0 ? `Stopped web processes: ${summary.stoppedPids.join(", ")}` : "Stopped web processes: none");
|
|
1967
|
+
if (summary.skippedForeignPids.length > 0) runtime.log(theme.warn(`Left non-Dench listener(s) running on ${selectedPort}: ${summary.skippedForeignPids.join(", ")}`));
|
|
1968
|
+
return summary;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
//#endregion
|
|
1972
|
+
//#region src/cli/program/register.stop.ts
|
|
1973
|
+
function registerStopCommand(program) {
|
|
1974
|
+
program.command("stop").description("Stop Dench managed web runtime on the configured port").option("--profile <name>", "Compatibility flag; non-dench values are ignored with a warning").option("--web-port <port>", "Web runtime port override").option("--json", "Output summary as JSON", false).action(async (opts) => {
|
|
1975
|
+
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
1976
|
+
await stopWebRuntimeCommand({
|
|
1977
|
+
profile: opts.profile,
|
|
1978
|
+
webPort: opts.webPort,
|
|
1979
|
+
json: Boolean(opts.json)
|
|
1980
|
+
});
|
|
1981
|
+
});
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
//#endregion
|
|
1986
|
+
//#region src/cli/program/register.update.ts
|
|
1987
|
+
function registerUpdateCommand(program) {
|
|
1988
|
+
program.command("update").description("Update Dench managed web runtime without onboarding").option("--profile <name>", "Compatibility flag; non-dench values are ignored with a warning").option("--web-port <port>", "Web runtime port override").option("--non-interactive", "Fail instead of prompting for major-gate approval", false).option("--yes", "Approve mandatory major-gate OpenClaw update", false).option("--json", "Output summary as JSON", false).action(async (opts) => {
|
|
1989
|
+
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
1990
|
+
await updateWebRuntimeCommand({
|
|
1991
|
+
profile: opts.profile,
|
|
1992
|
+
webPort: opts.webPort,
|
|
1993
|
+
nonInteractive: Boolean(opts.nonInteractive),
|
|
1994
|
+
yes: Boolean(opts.yes),
|
|
1995
|
+
json: Boolean(opts.json)
|
|
1996
|
+
});
|
|
1997
|
+
});
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
//#endregion
|
|
2002
|
+
//#region src/cli/program/command-registry.ts
|
|
2003
|
+
const CORE_CLI_ENTRIES = [
|
|
2004
|
+
{
|
|
2005
|
+
name: "bootstrap",
|
|
2006
|
+
description: "Bootstrap DenchClaw + OpenClaw and launch the web UI",
|
|
2007
|
+
register: ({ program }) => {
|
|
2008
|
+
registerBootstrapCommand(program);
|
|
2009
|
+
}
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
name: "update",
|
|
2013
|
+
description: "Update Dench web runtime without onboarding",
|
|
2014
|
+
register: ({ program }) => {
|
|
2015
|
+
registerUpdateCommand(program);
|
|
2016
|
+
}
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
name: "stop",
|
|
2020
|
+
description: "Stop Dench managed web runtime",
|
|
2021
|
+
register: ({ program }) => {
|
|
2022
|
+
registerStopCommand(program);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
];
|
|
2026
|
+
const CORE_CLI_ENTRY_BY_NAME = new Map(CORE_CLI_ENTRIES.map((entry) => [entry.name, entry]));
|
|
2027
|
+
function getCoreCliCommandsWithSubcommands() {
|
|
2028
|
+
return [];
|
|
2029
|
+
}
|
|
2030
|
+
function registerCoreCliCommands(program, ctx, argv) {
|
|
2031
|
+
const primary = getPrimaryCommand(argv);
|
|
2032
|
+
if (primary) {
|
|
2033
|
+
const entry = CORE_CLI_ENTRY_BY_NAME.get(primary);
|
|
2034
|
+
if (!entry) return;
|
|
2035
|
+
entry.register({
|
|
2036
|
+
program,
|
|
2037
|
+
ctx,
|
|
2038
|
+
argv
|
|
2039
|
+
});
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
for (const entry of CORE_CLI_ENTRIES) entry.register({
|
|
2043
|
+
program,
|
|
2044
|
+
ctx,
|
|
2045
|
+
argv
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
function registerProgramCommands(program, ctx, argv = process.argv) {
|
|
2049
|
+
registerCoreCliCommands(program, ctx, argv);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
//#endregion
|
|
2053
|
+
//#region src/cli/program/context.ts
|
|
2054
|
+
function createProgramContext() {
|
|
2055
|
+
const channelOptions = ["web"];
|
|
2056
|
+
return {
|
|
2057
|
+
programVersion: VERSION,
|
|
2058
|
+
channelOptions,
|
|
2059
|
+
messageChannelOptions: channelOptions.join("|"),
|
|
2060
|
+
agentChannelOptions: ["last", ...channelOptions].join("|")
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
//#endregion
|
|
2065
|
+
//#region src/cli/program/help.ts
|
|
2066
|
+
const CLI_NAME = resolveCliName();
|
|
2067
|
+
const CLI_NAME_PATTERN = escapeRegExp(CLI_NAME);
|
|
2068
|
+
const ROOT_COMMANDS_WITH_SUBCOMMANDS = new Set(getCoreCliCommandsWithSubcommands());
|
|
2069
|
+
const ROOT_COMMANDS_HINT = "Hint: commands suffixed with * have subcommands. Run <command> --help for details.";
|
|
2070
|
+
const EXAMPLES = [
|
|
2071
|
+
["openclaw update", "Refresh the managed web runtime and enforce major upgrade gates."],
|
|
2072
|
+
["openclaw stop --web-port 3100", "Stop only the managed web runtime on a specific port."],
|
|
2073
|
+
["openclaw models --help", "Show detailed help for the models command."],
|
|
2074
|
+
["openclaw channels login --verbose", "Link personal WhatsApp Web and show QR + connection logs."],
|
|
2075
|
+
["openclaw message send --target +15555550123 --message \"Hi\" --json", "Send via your web session and print JSON result."],
|
|
2076
|
+
["openclaw gateway --port 18789", "Run the WebSocket Gateway locally."],
|
|
2077
|
+
["openclaw --profile team-a gateway", "Compatibility flag example: warns and still runs with --profile dench."],
|
|
2078
|
+
["openclaw gateway --force", "Kill anything bound to the default gateway port, then start it."],
|
|
2079
|
+
["openclaw gateway ...", "Gateway control via WebSocket."],
|
|
2080
|
+
["openclaw agent --to +15555550123 --message \"Run summary\" --deliver", "Talk directly to the agent using the Gateway; optionally send the WhatsApp reply."],
|
|
2081
|
+
["openclaw message send --channel telegram --target @mychat --message \"Hi\"", "Send via your Telegram bot."]
|
|
2082
|
+
];
|
|
2083
|
+
function configureProgramHelp(program, ctx) {
|
|
2084
|
+
program.name(CLI_NAME).description("").version(ctx.programVersion).option("--dev", "Compatibility flag; DenchClaw always uses --profile dench and ~/.openclaw-dench").option("--profile <name>", "Compatibility flag; non-dench values are ignored with a warning");
|
|
2085
|
+
program.option("--no-color", "Disable ANSI colors", false);
|
|
2086
|
+
program.helpOption("-h, --help", "Display help for command");
|
|
2087
|
+
program.helpCommand("help [command]", "Display help for command");
|
|
2088
|
+
program.configureHelp({
|
|
2089
|
+
sortSubcommands: true,
|
|
2090
|
+
sortOptions: true,
|
|
2091
|
+
optionTerm: (option) => theme.option(option.flags),
|
|
2092
|
+
subcommandTerm: (cmd) => {
|
|
2093
|
+
const hasSubcommands = cmd.parent === program && ROOT_COMMANDS_WITH_SUBCOMMANDS.has(cmd.name());
|
|
2094
|
+
return theme.command(hasSubcommands ? `${cmd.name()} *` : cmd.name());
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
const formatHelpOutput = (str) => {
|
|
2098
|
+
let output = str;
|
|
2099
|
+
if (new RegExp(`^Usage:\\s+${CLI_NAME_PATTERN}\\s+\\[options\\]\\s+\\[command\\]\\s*$`, "m").test(output) && /^Commands:/m.test(output)) output = output.replace(/^Commands:/m, `Commands:\n ${theme.muted(ROOT_COMMANDS_HINT)}`);
|
|
2100
|
+
return output.replace(/^Usage:/gm, theme.heading("Usage:")).replace(/^Options:/gm, theme.heading("Options:")).replace(/^Commands:/gm, theme.heading("Commands:"));
|
|
2101
|
+
};
|
|
2102
|
+
program.configureOutput({
|
|
2103
|
+
writeOut: (str) => {
|
|
2104
|
+
process.stdout.write(formatHelpOutput(str));
|
|
2105
|
+
},
|
|
2106
|
+
writeErr: (str) => {
|
|
2107
|
+
process.stderr.write(formatHelpOutput(str));
|
|
2108
|
+
},
|
|
2109
|
+
outputError: (str, write) => write(theme.error(str))
|
|
2110
|
+
});
|
|
2111
|
+
if (hasFlag(process.argv, "-V") || hasFlag(process.argv, "--version") || hasRootVersionAlias(process.argv)) {
|
|
2112
|
+
console.log(ctx.programVersion);
|
|
2113
|
+
process.exit(0);
|
|
2114
|
+
}
|
|
2115
|
+
program.addHelpText("beforeAll", () => {
|
|
2116
|
+
if (hasEmittedCliBanner()) return "";
|
|
2117
|
+
const rich = isRich();
|
|
2118
|
+
return `\n${formatCliBannerLine(ctx.programVersion, { richTty: rich })}\n`;
|
|
2119
|
+
});
|
|
2120
|
+
const fmtExamples = EXAMPLES.map(([cmd, desc]) => ` ${theme.command(replaceCliName(cmd, CLI_NAME))}\n ${theme.muted(desc)}`).join("\n");
|
|
2121
|
+
program.addHelpText("afterAll", ({ command }) => {
|
|
2122
|
+
if (command !== program) return "";
|
|
2123
|
+
const docs = formatDocsLink("/cli", "docs.openclaw.ai/cli");
|
|
2124
|
+
return `\n${theme.heading("Examples:")}\n${fmtExamples}\n\n${theme.muted("Docs:")} ${docs}\n`;
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
//#endregion
|
|
2129
|
+
//#region src/cli/program/preaction.ts
|
|
2130
|
+
function setProcessTitleForCommand(actionCommand) {
|
|
2131
|
+
let current = actionCommand;
|
|
2132
|
+
while (current.parent && current.parent.parent) current = current.parent;
|
|
2133
|
+
const name = current.name();
|
|
2134
|
+
const cliName = resolveCliName();
|
|
2135
|
+
if (!name || name === cliName) return;
|
|
2136
|
+
process.title = `${cliName}-${name}`;
|
|
2137
|
+
}
|
|
2138
|
+
function registerPreActionHooks(program, programVersion) {
|
|
2139
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
2140
|
+
setProcessTitleForCommand(actionCommand);
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
//#endregion
|
|
2145
|
+
//#region src/cli/program/program-context.ts
|
|
2146
|
+
const PROGRAM_CONTEXT_SYMBOL = Symbol.for("openclaw.cli.programContext");
|
|
2147
|
+
function setProgramContext(program, ctx) {
|
|
2148
|
+
program[PROGRAM_CONTEXT_SYMBOL] = ctx;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
//#endregion
|
|
2152
|
+
//#region src/cli/program/build-program.ts
|
|
2153
|
+
function buildProgram() {
|
|
2154
|
+
const program = new Command();
|
|
2155
|
+
const ctx = createProgramContext();
|
|
2156
|
+
const argv = process.argv;
|
|
2157
|
+
setProgramContext(program, ctx);
|
|
2158
|
+
configureProgramHelp(program, ctx);
|
|
2159
|
+
registerPreActionHooks(program, ctx.programVersion);
|
|
2160
|
+
registerProgramCommands(program, ctx, argv);
|
|
2161
|
+
return program;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
//#endregion
|
|
2165
|
+
export { buildProgram };
|