nextclaw 0.18.12 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/app/index.js +163 -17805
- package/dist/cli/launcher/index.js +2 -52
- package/package.json +18 -17
- package/ui-dist/assets/api-lwyw9j7i.js +15 -0
- package/ui-dist/assets/{app-manager-provider-BZr5VxCe.js → app-manager-provider-C0ONQxUg.js} +1 -1
- package/ui-dist/assets/{app-navigation.config-BjIj_FLm.js → app-navigation.config-DgiR0c5_.js} +1 -1
- package/ui-dist/assets/{channels-list-page-L6FFtRYn.js → channels-list-page-Dl839n02.js} +1 -1
- package/ui-dist/assets/{chat-ZQIVJChB.js → chat-DwUf7AKR.js} +36 -36
- package/ui-dist/assets/chat-page-B-FvPmA7.js +1 -0
- package/ui-dist/assets/{desktop-update-config-AXzb7OEa.js → desktop-update-config-D5g_gPak.js} +1 -1
- package/ui-dist/assets/{dialog-DgybjpeU.js → dialog-CdtCU2xX.js} +1 -1
- package/ui-dist/assets/{dist-CxcPOISF.js → dist-CuqvE--P.js} +1 -1
- package/ui-dist/assets/{es2015-BVRlEE06.js → es2015-yYU5Ad5w.js} +1 -1
- package/ui-dist/assets/{index-B7RsQ-ne.js → index-Doxyk7L2.js} +2 -2
- package/ui-dist/assets/marketplace-page-BRHkZaO5.js +1 -0
- package/ui-dist/assets/{marketplace-page-ClRW-W3g.js → marketplace-page-CawcdL6Y.js} +1 -1
- package/ui-dist/assets/mcp-marketplace-page-CL7BF4dD.js +1 -0
- package/ui-dist/assets/{mcp-marketplace-page-DtngnIi0.js → mcp-marketplace-page-DEGfJ_70.js} +1 -1
- package/ui-dist/assets/{model-config-D_y8F0j6.js → model-config-r-1RPSrZ.js} +1 -1
- package/ui-dist/assets/{notice-card-uzwjFyML.js → notice-card-BPtCVEKW.js} +1 -1
- package/ui-dist/assets/{popover-C8tMB7tR.js → popover-jbfQhYQh.js} +1 -1
- package/ui-dist/assets/{provider-scoped-model-input-ORZutTDv.js → provider-scoped-model-input-gdk2lmRi.js} +1 -1
- package/ui-dist/assets/{providers-list-DSc3d8me.js → providers-list-DpISIr3M.js} +1 -1
- package/ui-dist/assets/remote-BnRNqMlb.js +1 -0
- package/ui-dist/assets/{runtime-config-page-B4uSax1I.js → runtime-config-page-DQ8YY8Lc.js} +1 -1
- package/ui-dist/assets/{search-config-CaqqlsdW.js → search-config-BWqz8nqY.js} +1 -1
- package/ui-dist/assets/{secrets-config-llf5ROde.js → secrets-config-CjzSNg0Y.js} +1 -1
- package/ui-dist/assets/{select-uO-zhYsH.js → select-Cw5Zkb1w.js} +1 -1
- package/ui-dist/assets/{sessions-config-page-BqOXte9x.js → sessions-config-page-beoDPtII.js} +2 -2
- package/ui-dist/assets/{setting-row-BIiXR4hx.js → setting-row-Cjl2d40s.js} +1 -1
- package/ui-dist/assets/{tag-chip-DVbgpsYW.js → tag-chip-CoWHxYJj.js} +1 -1
- package/ui-dist/assets/{theme-provider-BU77FNSB.js → theme-provider-B5XReW_-.js} +1 -1
- package/ui-dist/assets/{tooltip-CvAtG-kT.js → tooltip-GYzH-Hfq.js} +1 -1
- package/ui-dist/assets/{use-config-D2QgG7qc.js → use-config-BhJHD3-G.js} +1 -1
- package/ui-dist/assets/{use-confirm-dialog-BBClFV8E.js → use-confirm-dialog-Bqgy3Gi-.js} +1 -1
- package/ui-dist/assets/{use-infinite-scroll-loader-CWzpUecQ.js → use-infinite-scroll-loader-BfexitoF.js} +1 -1
- package/ui-dist/assets/{use-viewport-layout-C6EN0_eq.js → use-viewport-layout-D33zVbr5.js} +1 -1
- package/ui-dist/index.html +15 -15
- package/ui-dist/sw.js +4 -7
- package/dist/npm-runtime-update-state.store-75vzvn0B.js +0 -633
- package/ui-dist/assets/api-Dai6UR3J.js +0 -15
- package/ui-dist/assets/chat-page-CR1yI96r.js +0 -1
- package/ui-dist/assets/marketplace-page-Bj55-6F2.js +0 -1
- package/ui-dist/assets/mcp-marketplace-page-_Wu2VnHy.js +0 -1
- package/ui-dist/assets/remote-rWiu3cys.js +0 -1
|
@@ -1,633 +0,0 @@
|
|
|
1
|
-
import { createExternalCommandEnv, getDataDir, getLogsPath, getPackageVersion, resolveLocalUiBaseUrl } from "@nextclaw/core";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
6
|
-
import { isIP } from "node:net";
|
|
7
|
-
import { cp, readdir, rename, rm } from "node:fs/promises";
|
|
8
|
-
//#region src/cli/shared/utils/cli.utils.ts
|
|
9
|
-
function resolveUiConfig(config, overrides) {
|
|
10
|
-
return {
|
|
11
|
-
...config.ui ?? {
|
|
12
|
-
enabled: false,
|
|
13
|
-
host: "127.0.0.1",
|
|
14
|
-
port: 55667,
|
|
15
|
-
open: false
|
|
16
|
-
},
|
|
17
|
-
...overrides ?? {}
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function resolveUiApiBase(host, port) {
|
|
21
|
-
return resolveLocalUiBaseUrl({
|
|
22
|
-
host,
|
|
23
|
-
port
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function isLoopbackHost(host) {
|
|
27
|
-
const normalized = host.trim().toLowerCase();
|
|
28
|
-
return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1";
|
|
29
|
-
}
|
|
30
|
-
const PUBLIC_IP_CHECK_URLS = ["https://api.ipify.org", "https://ifconfig.me/ip"];
|
|
31
|
-
async function fetchPublicIpFrom(url, timeoutMs) {
|
|
32
|
-
const controller = new AbortController();
|
|
33
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34
|
-
try {
|
|
35
|
-
const response = await fetch(url, {
|
|
36
|
-
signal: controller.signal,
|
|
37
|
-
headers: { Accept: "text/plain" }
|
|
38
|
-
});
|
|
39
|
-
if (!response.ok) return null;
|
|
40
|
-
const text = (await response.text()).trim();
|
|
41
|
-
return isIP(text) ? text : null;
|
|
42
|
-
} catch {
|
|
43
|
-
return null;
|
|
44
|
-
} finally {
|
|
45
|
-
clearTimeout(timer);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
async function resolvePublicIp(timeoutMs = 1500) {
|
|
49
|
-
for (const endpoint of PUBLIC_IP_CHECK_URLS) {
|
|
50
|
-
const candidate = await fetchPublicIpFrom(endpoint, timeoutMs);
|
|
51
|
-
if (candidate) return candidate;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
function resolveServiceLogPath() {
|
|
56
|
-
return resolve(getLogsPath(), "service.log");
|
|
57
|
-
}
|
|
58
|
-
function isProcessRunning(pid) {
|
|
59
|
-
try {
|
|
60
|
-
process.kill(pid, 0);
|
|
61
|
-
return true;
|
|
62
|
-
} catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async function waitForExit(pid, timeoutMs) {
|
|
67
|
-
const start = Date.now();
|
|
68
|
-
while (Date.now() - start < timeoutMs) {
|
|
69
|
-
if (!isProcessRunning(pid)) return true;
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
71
|
-
}
|
|
72
|
-
return !isProcessRunning(pid);
|
|
73
|
-
}
|
|
74
|
-
function runLookupCommand(command, args) {
|
|
75
|
-
try {
|
|
76
|
-
const result = spawnSync(command, args, {
|
|
77
|
-
encoding: "utf8",
|
|
78
|
-
env: createExternalCommandEnv(process.env)
|
|
79
|
-
});
|
|
80
|
-
if (result.error || result.status !== 0) return {
|
|
81
|
-
ok: false,
|
|
82
|
-
stdout: ""
|
|
83
|
-
};
|
|
84
|
-
return {
|
|
85
|
-
ok: true,
|
|
86
|
-
stdout: result.stdout ?? ""
|
|
87
|
-
};
|
|
88
|
-
} catch {
|
|
89
|
-
return {
|
|
90
|
-
ok: false,
|
|
91
|
-
stdout: ""
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function parseListeningPidFromLsof(raw) {
|
|
96
|
-
for (const line of raw.split("\n")) {
|
|
97
|
-
if (!line.startsWith("p")) continue;
|
|
98
|
-
const pid = Number(line.slice(1));
|
|
99
|
-
if (Number.isFinite(pid) && pid > 0) return pid;
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
function parseListeningPidFromNetstat(raw, port) {
|
|
104
|
-
const portSuffix = `:${port}`;
|
|
105
|
-
for (const line of raw.split("\n")) {
|
|
106
|
-
const trimmed = line.trim();
|
|
107
|
-
if (!trimmed || !trimmed.includes("LISTEN")) continue;
|
|
108
|
-
const columns = trimmed.split(/\s+/);
|
|
109
|
-
if (columns.length < 5) continue;
|
|
110
|
-
if (!(columns[1] ?? "").endsWith(portSuffix)) continue;
|
|
111
|
-
const pid = Number(columns[4]);
|
|
112
|
-
if (Number.isFinite(pid) && pid > 0) return pid;
|
|
113
|
-
}
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
function lookupProcessCommandByPid(pid) {
|
|
117
|
-
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
118
|
-
if (process.platform === "win32") {
|
|
119
|
-
const result = runLookupCommand("powershell", [
|
|
120
|
-
"-NoProfile",
|
|
121
|
-
"-Command",
|
|
122
|
-
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
123
|
-
]);
|
|
124
|
-
if (!result.ok) return null;
|
|
125
|
-
const command = result.stdout.trim();
|
|
126
|
-
return command.length > 0 ? command : null;
|
|
127
|
-
}
|
|
128
|
-
const result = runLookupCommand("ps", [
|
|
129
|
-
"-p",
|
|
130
|
-
String(pid),
|
|
131
|
-
"-o",
|
|
132
|
-
"command="
|
|
133
|
-
]);
|
|
134
|
-
if (!result.ok) return null;
|
|
135
|
-
const command = result.stdout.trim();
|
|
136
|
-
return command.length > 0 ? command : null;
|
|
137
|
-
}
|
|
138
|
-
function findListeningProcessByPort(port) {
|
|
139
|
-
if (!Number.isFinite(port) || port <= 0) return null;
|
|
140
|
-
let pid = null;
|
|
141
|
-
if (process.platform === "win32") {
|
|
142
|
-
const result = runLookupCommand("netstat", [
|
|
143
|
-
"-ano",
|
|
144
|
-
"-p",
|
|
145
|
-
"tcp"
|
|
146
|
-
]);
|
|
147
|
-
if (!result.ok) return null;
|
|
148
|
-
pid = parseListeningPidFromNetstat(result.stdout, port);
|
|
149
|
-
} else {
|
|
150
|
-
const result = runLookupCommand("lsof", [
|
|
151
|
-
"-nP",
|
|
152
|
-
`-iTCP:${port}`,
|
|
153
|
-
"-sTCP:LISTEN",
|
|
154
|
-
"-F",
|
|
155
|
-
"p"
|
|
156
|
-
]);
|
|
157
|
-
if (!result.ok) return null;
|
|
158
|
-
pid = parseListeningPidFromLsof(result.stdout);
|
|
159
|
-
}
|
|
160
|
-
if (!pid) return null;
|
|
161
|
-
return {
|
|
162
|
-
pid,
|
|
163
|
-
command: lookupProcessCommandByPid(pid)
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function findNearestPackageManifest(startDir, expectedName) {
|
|
167
|
-
let current = resolve(startDir);
|
|
168
|
-
while (current.length > 0) {
|
|
169
|
-
const pkgPath = join(current, "package.json");
|
|
170
|
-
if (existsSync(pkgPath)) try {
|
|
171
|
-
const raw = readFileSync(pkgPath, "utf-8");
|
|
172
|
-
const parsed = JSON.parse(raw);
|
|
173
|
-
if (!expectedName || parsed.name === expectedName) return {
|
|
174
|
-
rootDir: current,
|
|
175
|
-
version: typeof parsed.version === "string" ? parsed.version : void 0
|
|
176
|
-
};
|
|
177
|
-
} catch {}
|
|
178
|
-
const parent = resolve(current, "..");
|
|
179
|
-
if (parent === current) break;
|
|
180
|
-
current = parent;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
function resolveUiStaticDir(importMetaUrl = import.meta.url) {
|
|
185
|
-
if (process.env.NEXTCLAW_DISABLE_STATIC_UI === "1") return null;
|
|
186
|
-
const envDir = process.env.NEXTCLAW_UI_STATIC_DIR;
|
|
187
|
-
if (envDir) return existsSync(join(envDir, "index.html")) ? envDir : null;
|
|
188
|
-
const pkgRoot = findNearestPackageManifest(resolve(fileURLToPath(new URL(".", importMetaUrl))), "nextclaw")?.rootDir;
|
|
189
|
-
if (!pkgRoot) return null;
|
|
190
|
-
const bundledDir = join(pkgRoot, "ui-dist");
|
|
191
|
-
return existsSync(join(bundledDir, "index.html")) ? bundledDir : null;
|
|
192
|
-
}
|
|
193
|
-
function openBrowser(url) {
|
|
194
|
-
const platform = process.platform;
|
|
195
|
-
let command;
|
|
196
|
-
let args;
|
|
197
|
-
if (platform === "darwin") {
|
|
198
|
-
command = "open";
|
|
199
|
-
args = [url];
|
|
200
|
-
} else if (platform === "win32") {
|
|
201
|
-
command = "cmd";
|
|
202
|
-
args = [
|
|
203
|
-
"/c",
|
|
204
|
-
"start",
|
|
205
|
-
"",
|
|
206
|
-
url
|
|
207
|
-
];
|
|
208
|
-
} else {
|
|
209
|
-
command = "xdg-open";
|
|
210
|
-
args = [url];
|
|
211
|
-
}
|
|
212
|
-
if (!findExecutableOnPath(command)) return false;
|
|
213
|
-
try {
|
|
214
|
-
spawn(command, args, {
|
|
215
|
-
stdio: "ignore",
|
|
216
|
-
detached: true,
|
|
217
|
-
env: createExternalCommandEnv(process.env)
|
|
218
|
-
}).unref();
|
|
219
|
-
return true;
|
|
220
|
-
} catch {
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
function normalizePathEntries(rawPath, platform) {
|
|
225
|
-
const delimiter = platform === "win32" ? ";" : ":";
|
|
226
|
-
return rawPath.split(delimiter).map((entry) => entry.trim().replace(/^"+|"+$/g, "")).filter(Boolean);
|
|
227
|
-
}
|
|
228
|
-
function normalizeWindowsPathExt(rawPathExt) {
|
|
229
|
-
const source = rawPathExt && rawPathExt.trim().length > 0 ? rawPathExt : ".COM;.EXE;.BAT;.CMD";
|
|
230
|
-
const unique = /* @__PURE__ */ new Set();
|
|
231
|
-
for (const ext of source.split(";")) {
|
|
232
|
-
const trimmed = ext.trim();
|
|
233
|
-
if (!trimmed) continue;
|
|
234
|
-
const normalized = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
|
|
235
|
-
unique.add(normalized.toUpperCase());
|
|
236
|
-
}
|
|
237
|
-
return [...unique];
|
|
238
|
-
}
|
|
239
|
-
function hasFileExtension(binary) {
|
|
240
|
-
return binary.lastIndexOf(".") > Math.max(binary.lastIndexOf("/"), binary.lastIndexOf("\\"));
|
|
241
|
-
}
|
|
242
|
-
function findExecutableOnPath(binary, env = process.env, platform = process.platform) {
|
|
243
|
-
const target = binary.trim();
|
|
244
|
-
if (!target) return null;
|
|
245
|
-
if (target.includes("/") || target.includes("\\")) return existsSync(target) ? target : null;
|
|
246
|
-
const rawPath = env.PATH ?? env.Path ?? env.path ?? "";
|
|
247
|
-
if (!rawPath.trim()) return null;
|
|
248
|
-
const entries = normalizePathEntries(rawPath, platform);
|
|
249
|
-
if (entries.length === 0) return null;
|
|
250
|
-
for (const dir of entries) {
|
|
251
|
-
const direct = join(dir, target);
|
|
252
|
-
if (existsSync(direct)) return direct;
|
|
253
|
-
if (platform !== "win32" || hasFileExtension(target)) continue;
|
|
254
|
-
for (const ext of normalizeWindowsPathExt(env.PATHEXT)) {
|
|
255
|
-
const withExt = join(dir, `${target}${ext}`);
|
|
256
|
-
if (existsSync(withExt)) return withExt;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
function getPackageVersion$1() {
|
|
262
|
-
const cliDir = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
263
|
-
return findNearestPackageManifest(cliDir, "nextclaw")?.version ?? findNearestPackageManifest(cliDir)?.version ?? getPackageVersion();
|
|
264
|
-
}
|
|
265
|
-
function printAgentResponse(response) {
|
|
266
|
-
console.log("\n" + response + "\n");
|
|
267
|
-
}
|
|
268
|
-
async function prompt(rl, question) {
|
|
269
|
-
rl.setPrompt(question);
|
|
270
|
-
rl.prompt();
|
|
271
|
-
return new Promise((resolve) => {
|
|
272
|
-
rl.once("line", (line) => resolve(line));
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
//#endregion
|
|
276
|
-
//#region src/cli/launcher/npm-runtime-bundle-layout.store.ts
|
|
277
|
-
var NpmRuntimeBundleLayoutStore = class {
|
|
278
|
-
constructor(rootDirectory = join(getDataDir(), "launcher", "runtime-bundles")) {
|
|
279
|
-
this.rootDirectory = rootDirectory;
|
|
280
|
-
}
|
|
281
|
-
getRootDir = () => this.rootDirectory;
|
|
282
|
-
getVersionsDir = () => join(this.rootDirectory, "versions");
|
|
283
|
-
getVersionDir = (version) => join(this.getVersionsDir(), version);
|
|
284
|
-
getStagingDir = () => join(this.rootDirectory, "staging");
|
|
285
|
-
getCurrentPointerPath = () => join(this.rootDirectory, "current.json");
|
|
286
|
-
getPreviousPointerPath = () => join(this.rootDirectory, "previous.json");
|
|
287
|
-
getStatePath = () => join(getDataDir(), "launcher", "npm-runtime-update-state.json");
|
|
288
|
-
ensureLauncherDirs = () => {
|
|
289
|
-
mkdirSync(this.getVersionsDir(), { recursive: true });
|
|
290
|
-
mkdirSync(this.getStagingDir(), { recursive: true });
|
|
291
|
-
mkdirSync(dirname(this.getStatePath()), { recursive: true });
|
|
292
|
-
};
|
|
293
|
-
readCurrentPointer = () => this.readPointer(this.getCurrentPointerPath());
|
|
294
|
-
readPreviousPointer = () => this.readPointer(this.getPreviousPointerPath());
|
|
295
|
-
writeCurrentPointer = (pointer) => this.writePointer(this.getCurrentPointerPath(), pointer);
|
|
296
|
-
writePreviousPointer = (pointer) => this.writePointer(this.getPreviousPointerPath(), pointer);
|
|
297
|
-
readPointer = (pointerPath) => {
|
|
298
|
-
if (!existsSync(pointerPath)) return null;
|
|
299
|
-
const parsed = JSON.parse(readFileSync(pointerPath, "utf8"));
|
|
300
|
-
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
|
301
|
-
return version ? { version } : null;
|
|
302
|
-
};
|
|
303
|
-
writePointer = (pointerPath, pointer) => {
|
|
304
|
-
mkdirSync(dirname(pointerPath), { recursive: true });
|
|
305
|
-
writeFileSync(pointerPath, `${JSON.stringify(pointer, null, 2)}\n`, "utf8");
|
|
306
|
-
};
|
|
307
|
-
};
|
|
308
|
-
//#endregion
|
|
309
|
-
//#region src/cli/launcher/npm-runtime-bundle-manifest.service.ts
|
|
310
|
-
function readRequiredString(record, key, context) {
|
|
311
|
-
const value = record[key];
|
|
312
|
-
if (typeof value !== "string" || !value.trim()) throw new Error(`${context} missing required string field: ${key}`);
|
|
313
|
-
return value.trim();
|
|
314
|
-
}
|
|
315
|
-
function readRequiredObject(record, key, context) {
|
|
316
|
-
const value = record[key];
|
|
317
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${context} missing required object field: ${key}`);
|
|
318
|
-
return value;
|
|
319
|
-
}
|
|
320
|
-
var NpmRuntimeBundleManifestReader = class {
|
|
321
|
-
readFile = (filePath) => {
|
|
322
|
-
if (!existsSync(filePath)) throw new Error(`runtime bundle manifest not found: ${filePath}`);
|
|
323
|
-
return this.parse(JSON.parse(readFileSync(filePath, "utf8")), filePath);
|
|
324
|
-
};
|
|
325
|
-
parse = (input, context = "runtime bundle manifest") => {
|
|
326
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`${context} must be an object`);
|
|
327
|
-
const record = input;
|
|
328
|
-
const launcherCompatibility = readRequiredObject(record, "launcherCompatibility", context);
|
|
329
|
-
const entrypoints = readRequiredObject(record, "entrypoints", context);
|
|
330
|
-
const migrationVersion = Number(record.migrationVersion);
|
|
331
|
-
if (!Number.isInteger(migrationVersion) || migrationVersion < 0) throw new Error(`${context} has invalid migrationVersion`);
|
|
332
|
-
return {
|
|
333
|
-
bundleVersion: readRequiredString(record, "bundleVersion", context),
|
|
334
|
-
platform: readRequiredString(record, "platform", context),
|
|
335
|
-
arch: readRequiredString(record, "arch", context),
|
|
336
|
-
uiVersion: readRequiredString(record, "uiVersion", context),
|
|
337
|
-
runtimeVersion: readRequiredString(record, "runtimeVersion", context),
|
|
338
|
-
builtInPluginSetVersion: readRequiredString(record, "builtInPluginSetVersion", context),
|
|
339
|
-
launcherCompatibility: { minVersion: readRequiredString(launcherCompatibility, "minVersion", `${context}.launcherCompatibility`) },
|
|
340
|
-
entrypoints: { runtimeScript: readRequiredString(entrypoints, "runtimeScript", `${context}.entrypoints`) },
|
|
341
|
-
migrationVersion
|
|
342
|
-
};
|
|
343
|
-
};
|
|
344
|
-
};
|
|
345
|
-
//#endregion
|
|
346
|
-
//#region src/cli/launcher/npm-runtime-bundle.service.ts
|
|
347
|
-
function shouldRetryInstallWithCopy(error) {
|
|
348
|
-
if (!error || typeof error !== "object") return false;
|
|
349
|
-
const code = "code" in error ? String(error.code) : "";
|
|
350
|
-
return code === "EXDEV" || code === "EPERM";
|
|
351
|
-
}
|
|
352
|
-
var NpmRuntimeBundleService = class {
|
|
353
|
-
manifestReader;
|
|
354
|
-
platform;
|
|
355
|
-
arch;
|
|
356
|
-
launcherVersion;
|
|
357
|
-
now;
|
|
358
|
-
constructor(options) {
|
|
359
|
-
this.options = options;
|
|
360
|
-
this.manifestReader = options.manifestReader ?? new NpmRuntimeBundleManifestReader();
|
|
361
|
-
this.platform = options.platform ?? process.platform;
|
|
362
|
-
this.arch = options.arch ?? process.arch;
|
|
363
|
-
this.launcherVersion = options.launcherVersion?.trim() || null;
|
|
364
|
-
this.now = options.now ?? Date.now;
|
|
365
|
-
}
|
|
366
|
-
resolveCurrentBundle = () => {
|
|
367
|
-
const pointer = this.options.layout.readCurrentPointer();
|
|
368
|
-
if (!pointer) return null;
|
|
369
|
-
return this.resolveVersion(pointer.version);
|
|
370
|
-
};
|
|
371
|
-
resolveVersion = (version) => {
|
|
372
|
-
const bundleDirectory = this.options.layout.getVersionDir(version);
|
|
373
|
-
const bundle = this.verifyBundle(bundleDirectory);
|
|
374
|
-
if (bundle.manifest.bundleVersion !== version) throw new Error(`runtime bundle version mismatch: pointer expects ${version} but manifest is ${bundle.manifest.bundleVersion}`);
|
|
375
|
-
return bundle;
|
|
376
|
-
};
|
|
377
|
-
installFromDirectory = async (sourceDirectory) => {
|
|
378
|
-
this.options.layout.ensureLauncherDirs();
|
|
379
|
-
const sourceBundle = this.verifyBundle(sourceDirectory);
|
|
380
|
-
const targetDirectory = this.options.layout.getVersionDir(sourceBundle.manifest.bundleVersion);
|
|
381
|
-
if (existsSync(targetDirectory)) return this.verifyBundle(targetDirectory);
|
|
382
|
-
try {
|
|
383
|
-
await rename(sourceDirectory, targetDirectory);
|
|
384
|
-
return this.verifyBundle(targetDirectory);
|
|
385
|
-
} catch (error) {
|
|
386
|
-
if (!shouldRetryInstallWithCopy(error)) throw error;
|
|
387
|
-
}
|
|
388
|
-
const stagingDirectory = join(this.options.layout.getStagingDir(), `${sourceBundle.manifest.bundleVersion}-${this.now()}`);
|
|
389
|
-
await rm(stagingDirectory, {
|
|
390
|
-
recursive: true,
|
|
391
|
-
force: true
|
|
392
|
-
});
|
|
393
|
-
try {
|
|
394
|
-
await cp(sourceDirectory, stagingDirectory, { recursive: true });
|
|
395
|
-
await rename(stagingDirectory, targetDirectory);
|
|
396
|
-
return this.verifyBundle(targetDirectory);
|
|
397
|
-
} catch (error) {
|
|
398
|
-
await rm(stagingDirectory, {
|
|
399
|
-
recursive: true,
|
|
400
|
-
force: true
|
|
401
|
-
});
|
|
402
|
-
throw error;
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
activateVersion = (version) => {
|
|
406
|
-
this.resolveVersion(version);
|
|
407
|
-
const currentPointer = this.options.layout.readCurrentPointer();
|
|
408
|
-
if (currentPointer) this.options.layout.writePreviousPointer(currentPointer);
|
|
409
|
-
this.options.layout.writeCurrentPointer({ version });
|
|
410
|
-
this.options.stateStore.update((state) => ({
|
|
411
|
-
...state,
|
|
412
|
-
currentVersion: version,
|
|
413
|
-
previousVersion: currentPointer?.version ?? state.previousVersion,
|
|
414
|
-
candidateVersion: version,
|
|
415
|
-
candidateLaunchCount: 0,
|
|
416
|
-
downloadedVersion: null,
|
|
417
|
-
downloadedReleaseNotesUrl: null
|
|
418
|
-
}));
|
|
419
|
-
};
|
|
420
|
-
pruneRetainedArtifacts = async () => {
|
|
421
|
-
this.options.layout.ensureLauncherDirs();
|
|
422
|
-
const state = this.options.stateStore.read();
|
|
423
|
-
const retained = new Set([
|
|
424
|
-
state.currentVersion,
|
|
425
|
-
state.previousVersion,
|
|
426
|
-
state.candidateVersion,
|
|
427
|
-
state.lastKnownGoodVersion,
|
|
428
|
-
state.downloadedVersion,
|
|
429
|
-
this.options.layout.readCurrentPointer()?.version,
|
|
430
|
-
this.options.layout.readPreviousPointer()?.version
|
|
431
|
-
].filter((version) => Boolean(version?.trim())));
|
|
432
|
-
const entries = await readdir(this.options.layout.getVersionsDir(), { withFileTypes: true });
|
|
433
|
-
for (const entry of entries.sort((left, right) => compareNpmRuntimeVersions(left.name, right.name))) {
|
|
434
|
-
if (!entry.isDirectory() || retained.has(entry.name)) continue;
|
|
435
|
-
await rm(join(this.options.layout.getVersionsDir(), entry.name), {
|
|
436
|
-
recursive: true,
|
|
437
|
-
force: true
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
await this.clearStagingDirectory();
|
|
441
|
-
};
|
|
442
|
-
clearStagingDirectory = async () => {
|
|
443
|
-
const entries = await readdir(this.options.layout.getStagingDir(), { withFileTypes: true });
|
|
444
|
-
await Promise.all(entries.map(async (entry) => await rm(join(this.options.layout.getStagingDir(), entry.name), {
|
|
445
|
-
recursive: true,
|
|
446
|
-
force: true
|
|
447
|
-
})));
|
|
448
|
-
};
|
|
449
|
-
verifyBundle = (bundleDirectory) => {
|
|
450
|
-
const manifest = this.manifestReader.readFile(resolve(bundleDirectory, "manifest.json"));
|
|
451
|
-
if (manifest.platform !== this.platform) throw new Error(`runtime bundle platform mismatch: expected ${this.platform} but got ${manifest.platform}`);
|
|
452
|
-
if (manifest.arch !== this.arch) throw new Error(`runtime bundle arch mismatch: expected ${this.arch} but got ${manifest.arch}`);
|
|
453
|
-
if (this.launcherVersion && compareNpmRuntimeVersions(this.launcherVersion, manifest.launcherCompatibility.minVersion) < 0) throw new Error(`runtime bundle requires launcher >= ${manifest.launcherCompatibility.minVersion} but current launcher is ${this.launcherVersion}`);
|
|
454
|
-
const runtimeScriptPath = resolve(bundleDirectory, manifest.entrypoints.runtimeScript);
|
|
455
|
-
if (!existsSync(runtimeScriptPath)) throw new Error(`runtime bundle script missing: ${runtimeScriptPath}`);
|
|
456
|
-
return {
|
|
457
|
-
bundleDirectory,
|
|
458
|
-
manifest,
|
|
459
|
-
runtimeScriptPath
|
|
460
|
-
};
|
|
461
|
-
};
|
|
462
|
-
};
|
|
463
|
-
function compareNpmRuntimeVersions(left, right) {
|
|
464
|
-
const leftParts = parseVersionParts(left);
|
|
465
|
-
const rightParts = parseVersionParts(right);
|
|
466
|
-
const length = Math.max(leftParts.length, rightParts.length);
|
|
467
|
-
for (let index = 0; index < length; index += 1) {
|
|
468
|
-
const leftPart = leftParts[index] ?? 0;
|
|
469
|
-
const rightPart = rightParts[index] ?? 0;
|
|
470
|
-
if (leftPart !== rightPart) return leftPart - rightPart;
|
|
471
|
-
}
|
|
472
|
-
return left.localeCompare(right);
|
|
473
|
-
}
|
|
474
|
-
function resolveEffectiveNpmRuntimeVersion(params) {
|
|
475
|
-
const launcherVersion = params.launcherVersion?.trim() || null;
|
|
476
|
-
const currentBundleVersion = params.currentBundleVersion?.trim() || null;
|
|
477
|
-
if (!launcherVersion) return currentBundleVersion;
|
|
478
|
-
if (!currentBundleVersion) return launcherVersion;
|
|
479
|
-
return compareNpmRuntimeVersions(launcherVersion, currentBundleVersion) > 0 ? launcherVersion : currentBundleVersion;
|
|
480
|
-
}
|
|
481
|
-
function shouldPreferPackagedNpmRuntime(params) {
|
|
482
|
-
const launcherVersion = params.launcherVersion?.trim() || null;
|
|
483
|
-
const currentBundleVersion = params.currentBundleVersion?.trim() || null;
|
|
484
|
-
if (!launcherVersion || !currentBundleVersion) return false;
|
|
485
|
-
return resolveEffectiveNpmRuntimeVersion(params) === launcherVersion;
|
|
486
|
-
}
|
|
487
|
-
function parseVersionParts(version) {
|
|
488
|
-
return version.split(/[.-]/).map((part) => Number(part)).map((part) => Number.isFinite(part) ? part : 0);
|
|
489
|
-
}
|
|
490
|
-
//#endregion
|
|
491
|
-
//#region src/cli/launcher/npm-runtime-update-source.service.ts
|
|
492
|
-
const DEFAULT_NPM_RUNTIME_UPDATE_BASE_URL = "https://Peiiii.github.io/nextclaw/npm-runtime-updates";
|
|
493
|
-
function normalizeOptionalString$1(value) {
|
|
494
|
-
if (typeof value !== "string") return null;
|
|
495
|
-
const trimmed = value.trim();
|
|
496
|
-
return trimmed ? trimmed : null;
|
|
497
|
-
}
|
|
498
|
-
function normalizeChannel$1(value) {
|
|
499
|
-
return typeof value === "string" && value.trim().toLowerCase() === "beta" ? "beta" : "stable";
|
|
500
|
-
}
|
|
501
|
-
function inferDefaultNpmRuntimeReleaseChannel(launcherVersion) {
|
|
502
|
-
return typeof launcherVersion === "string" && launcherVersion.toLowerCase().includes("-beta") ? "beta" : "stable";
|
|
503
|
-
}
|
|
504
|
-
function resolvePackagedPublicKeyPath() {
|
|
505
|
-
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
506
|
-
const candidates = [
|
|
507
|
-
resolve(moduleDir, "..", "resources", "update-bundle-public.pem"),
|
|
508
|
-
resolve(moduleDir, "../../..", "resources", "update-bundle-public.pem"),
|
|
509
|
-
resolve(moduleDir, "../../../..", "resources", "update-bundle-public.pem")
|
|
510
|
-
];
|
|
511
|
-
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
512
|
-
}
|
|
513
|
-
var NpmRuntimeUpdateSourceService = class {
|
|
514
|
-
env;
|
|
515
|
-
platform;
|
|
516
|
-
arch;
|
|
517
|
-
constructor(options = {}) {
|
|
518
|
-
this.env = options.env ?? process.env;
|
|
519
|
-
this.platform = options.platform ?? process.platform;
|
|
520
|
-
this.arch = options.arch ?? process.arch;
|
|
521
|
-
}
|
|
522
|
-
resolveChannel = (explicitChannel, launcherVersion) => {
|
|
523
|
-
if (explicitChannel !== void 0 || this.env.NEXTCLAW_UPDATE_CHANNEL !== void 0) return normalizeChannel$1(explicitChannel ?? this.env.NEXTCLAW_UPDATE_CHANNEL);
|
|
524
|
-
return inferDefaultNpmRuntimeReleaseChannel(launcherVersion);
|
|
525
|
-
};
|
|
526
|
-
resolveManifestUrl = (channel, explicitManifestUrl) => {
|
|
527
|
-
const manifestUrl = normalizeOptionalString$1(explicitManifestUrl) ?? normalizeOptionalString$1(this.env.NEXTCLAW_UPDATE_MANIFEST_URL);
|
|
528
|
-
if (manifestUrl) return manifestUrl;
|
|
529
|
-
const baseUrl = normalizeOptionalString$1(this.env.NEXTCLAW_UPDATE_MANIFEST_BASE_URL) ?? DEFAULT_NPM_RUNTIME_UPDATE_BASE_URL;
|
|
530
|
-
return new URL(`${channel}/manifest-${channel}-${this.platform}-${this.arch}.json`, `${baseUrl.replace(/\/+$/, "")}/`).toString();
|
|
531
|
-
};
|
|
532
|
-
resolveBundlePublicKey = () => {
|
|
533
|
-
const explicitPublicKey = normalizeOptionalString$1(this.env.NEXTCLAW_UPDATE_BUNDLE_PUBLIC_KEY);
|
|
534
|
-
if (explicitPublicKey) return explicitPublicKey;
|
|
535
|
-
const publicKeyPath = normalizeOptionalString$1(this.env.NEXTCLAW_UPDATE_BUNDLE_PUBLIC_KEY_PATH);
|
|
536
|
-
if (!publicKeyPath || !existsSync(publicKeyPath)) {
|
|
537
|
-
const packagedPublicKeyPath = resolvePackagedPublicKeyPath();
|
|
538
|
-
return existsSync(packagedPublicKeyPath) ? readFileSync(packagedPublicKeyPath, "utf8").trim() : null;
|
|
539
|
-
}
|
|
540
|
-
return readFileSync(publicKeyPath, "utf8").trim();
|
|
541
|
-
};
|
|
542
|
-
};
|
|
543
|
-
//#endregion
|
|
544
|
-
//#region src/cli/launcher/npm-runtime-update-state.store.ts
|
|
545
|
-
function createDefaultState(channel) {
|
|
546
|
-
return {
|
|
547
|
-
channel,
|
|
548
|
-
currentVersion: null,
|
|
549
|
-
previousVersion: null,
|
|
550
|
-
candidateVersion: null,
|
|
551
|
-
candidateLaunchCount: 0,
|
|
552
|
-
lastKnownGoodVersion: null,
|
|
553
|
-
badVersions: [],
|
|
554
|
-
lastUpdateCheckAt: null,
|
|
555
|
-
downloadedVersion: null,
|
|
556
|
-
downloadedReleaseNotesUrl: null,
|
|
557
|
-
updatePreferences: {
|
|
558
|
-
automaticChecks: true,
|
|
559
|
-
autoDownload: true
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
function normalizeOptionalString(value) {
|
|
564
|
-
if (typeof value !== "string") return null;
|
|
565
|
-
const trimmed = value.trim();
|
|
566
|
-
return trimmed ? trimmed : null;
|
|
567
|
-
}
|
|
568
|
-
function normalizeChannel(value, fallback) {
|
|
569
|
-
if (typeof value !== "string") return fallback;
|
|
570
|
-
const trimmed = value.trim().toLowerCase();
|
|
571
|
-
if (trimmed === "beta") return "beta";
|
|
572
|
-
if (trimmed === "stable") return "stable";
|
|
573
|
-
return fallback;
|
|
574
|
-
}
|
|
575
|
-
function normalizeStringArray(value) {
|
|
576
|
-
if (!Array.isArray(value)) return [];
|
|
577
|
-
return [...new Set(value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean))];
|
|
578
|
-
}
|
|
579
|
-
function normalizeUpdatePreferences(value) {
|
|
580
|
-
const defaultState = createDefaultState("stable");
|
|
581
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return { ...defaultState.updatePreferences };
|
|
582
|
-
const record = value;
|
|
583
|
-
return {
|
|
584
|
-
automaticChecks: typeof record.automaticChecks === "boolean" ? record.automaticChecks : defaultState.updatePreferences.automaticChecks,
|
|
585
|
-
autoDownload: typeof record.autoDownload === "boolean" ? record.autoDownload : defaultState.updatePreferences.autoDownload
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function normalizeState(input, defaultChannel) {
|
|
589
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("npm runtime update state must be an object");
|
|
590
|
-
const record = input;
|
|
591
|
-
const candidateLaunchCount = Number(record.candidateLaunchCount);
|
|
592
|
-
return {
|
|
593
|
-
channel: normalizeChannel(record.channel, defaultChannel),
|
|
594
|
-
currentVersion: normalizeOptionalString(record.currentVersion),
|
|
595
|
-
previousVersion: normalizeOptionalString(record.previousVersion),
|
|
596
|
-
candidateVersion: normalizeOptionalString(record.candidateVersion),
|
|
597
|
-
candidateLaunchCount: Number.isInteger(candidateLaunchCount) && candidateLaunchCount >= 0 ? candidateLaunchCount : 0,
|
|
598
|
-
lastKnownGoodVersion: normalizeOptionalString(record.lastKnownGoodVersion),
|
|
599
|
-
badVersions: normalizeStringArray(record.badVersions),
|
|
600
|
-
lastUpdateCheckAt: normalizeOptionalString(record.lastUpdateCheckAt),
|
|
601
|
-
downloadedVersion: normalizeOptionalString(record.downloadedVersion),
|
|
602
|
-
downloadedReleaseNotesUrl: normalizeOptionalString(record.downloadedReleaseNotesUrl),
|
|
603
|
-
updatePreferences: normalizeUpdatePreferences(record.updatePreferences)
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
var NpmRuntimeUpdateStateStore = class {
|
|
607
|
-
defaultChannel;
|
|
608
|
-
constructor(statePath, options = {}) {
|
|
609
|
-
this.statePath = statePath;
|
|
610
|
-
this.defaultChannel = options.defaultChannel ?? "stable";
|
|
611
|
-
}
|
|
612
|
-
read = () => {
|
|
613
|
-
if (!existsSync(this.statePath)) {
|
|
614
|
-
const defaultState = createDefaultState(this.defaultChannel);
|
|
615
|
-
return {
|
|
616
|
-
...defaultState,
|
|
617
|
-
updatePreferences: { ...defaultState.updatePreferences }
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
return normalizeState(JSON.parse(readFileSync(this.statePath, "utf8")), this.defaultChannel);
|
|
621
|
-
};
|
|
622
|
-
write = (state) => {
|
|
623
|
-
mkdirSync(dirname(this.statePath), { recursive: true });
|
|
624
|
-
writeFileSync(this.statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
625
|
-
};
|
|
626
|
-
update = (updater) => {
|
|
627
|
-
const nextState = updater(this.read());
|
|
628
|
-
this.write(nextState);
|
|
629
|
-
return nextState;
|
|
630
|
-
};
|
|
631
|
-
};
|
|
632
|
-
//#endregion
|
|
633
|
-
export { waitForExit as S, resolvePublicIp as _, compareNpmRuntimeVersions as a, resolveUiConfig as b, NpmRuntimeBundleLayoutStore as c, getPackageVersion$1 as d, isLoopbackHost as f, prompt as g, printAgentResponse as h, NpmRuntimeBundleService as i, findExecutableOnPath as l, openBrowser as m, NpmRuntimeUpdateSourceService as n, resolveEffectiveNpmRuntimeVersion as o, isProcessRunning as p, inferDefaultNpmRuntimeReleaseChannel as r, shouldPreferPackagedNpmRuntime as s, NpmRuntimeUpdateStateStore as t, findListeningProcessByPort as u, resolveServiceLogPath as v, resolveUiStaticDir as x, resolveUiApiBase as y };
|