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.
Files changed (45) hide show
  1. package/dist/cli/app/index.js +163 -17805
  2. package/dist/cli/launcher/index.js +2 -52
  3. package/package.json +18 -17
  4. package/ui-dist/assets/api-lwyw9j7i.js +15 -0
  5. package/ui-dist/assets/{app-manager-provider-BZr5VxCe.js → app-manager-provider-C0ONQxUg.js} +1 -1
  6. package/ui-dist/assets/{app-navigation.config-BjIj_FLm.js → app-navigation.config-DgiR0c5_.js} +1 -1
  7. package/ui-dist/assets/{channels-list-page-L6FFtRYn.js → channels-list-page-Dl839n02.js} +1 -1
  8. package/ui-dist/assets/{chat-ZQIVJChB.js → chat-DwUf7AKR.js} +36 -36
  9. package/ui-dist/assets/chat-page-B-FvPmA7.js +1 -0
  10. package/ui-dist/assets/{desktop-update-config-AXzb7OEa.js → desktop-update-config-D5g_gPak.js} +1 -1
  11. package/ui-dist/assets/{dialog-DgybjpeU.js → dialog-CdtCU2xX.js} +1 -1
  12. package/ui-dist/assets/{dist-CxcPOISF.js → dist-CuqvE--P.js} +1 -1
  13. package/ui-dist/assets/{es2015-BVRlEE06.js → es2015-yYU5Ad5w.js} +1 -1
  14. package/ui-dist/assets/{index-B7RsQ-ne.js → index-Doxyk7L2.js} +2 -2
  15. package/ui-dist/assets/marketplace-page-BRHkZaO5.js +1 -0
  16. package/ui-dist/assets/{marketplace-page-ClRW-W3g.js → marketplace-page-CawcdL6Y.js} +1 -1
  17. package/ui-dist/assets/mcp-marketplace-page-CL7BF4dD.js +1 -0
  18. package/ui-dist/assets/{mcp-marketplace-page-DtngnIi0.js → mcp-marketplace-page-DEGfJ_70.js} +1 -1
  19. package/ui-dist/assets/{model-config-D_y8F0j6.js → model-config-r-1RPSrZ.js} +1 -1
  20. package/ui-dist/assets/{notice-card-uzwjFyML.js → notice-card-BPtCVEKW.js} +1 -1
  21. package/ui-dist/assets/{popover-C8tMB7tR.js → popover-jbfQhYQh.js} +1 -1
  22. package/ui-dist/assets/{provider-scoped-model-input-ORZutTDv.js → provider-scoped-model-input-gdk2lmRi.js} +1 -1
  23. package/ui-dist/assets/{providers-list-DSc3d8me.js → providers-list-DpISIr3M.js} +1 -1
  24. package/ui-dist/assets/remote-BnRNqMlb.js +1 -0
  25. package/ui-dist/assets/{runtime-config-page-B4uSax1I.js → runtime-config-page-DQ8YY8Lc.js} +1 -1
  26. package/ui-dist/assets/{search-config-CaqqlsdW.js → search-config-BWqz8nqY.js} +1 -1
  27. package/ui-dist/assets/{secrets-config-llf5ROde.js → secrets-config-CjzSNg0Y.js} +1 -1
  28. package/ui-dist/assets/{select-uO-zhYsH.js → select-Cw5Zkb1w.js} +1 -1
  29. package/ui-dist/assets/{sessions-config-page-BqOXte9x.js → sessions-config-page-beoDPtII.js} +2 -2
  30. package/ui-dist/assets/{setting-row-BIiXR4hx.js → setting-row-Cjl2d40s.js} +1 -1
  31. package/ui-dist/assets/{tag-chip-DVbgpsYW.js → tag-chip-CoWHxYJj.js} +1 -1
  32. package/ui-dist/assets/{theme-provider-BU77FNSB.js → theme-provider-B5XReW_-.js} +1 -1
  33. package/ui-dist/assets/{tooltip-CvAtG-kT.js → tooltip-GYzH-Hfq.js} +1 -1
  34. package/ui-dist/assets/{use-config-D2QgG7qc.js → use-config-BhJHD3-G.js} +1 -1
  35. package/ui-dist/assets/{use-confirm-dialog-BBClFV8E.js → use-confirm-dialog-Bqgy3Gi-.js} +1 -1
  36. package/ui-dist/assets/{use-infinite-scroll-loader-CWzpUecQ.js → use-infinite-scroll-loader-BfexitoF.js} +1 -1
  37. package/ui-dist/assets/{use-viewport-layout-C6EN0_eq.js → use-viewport-layout-D33zVbr5.js} +1 -1
  38. package/ui-dist/index.html +15 -15
  39. package/ui-dist/sw.js +4 -7
  40. package/dist/npm-runtime-update-state.store-75vzvn0B.js +0 -633
  41. package/ui-dist/assets/api-Dai6UR3J.js +0 -15
  42. package/ui-dist/assets/chat-page-CR1yI96r.js +0 -1
  43. package/ui-dist/assets/marketplace-page-Bj55-6F2.js +0 -1
  44. package/ui-dist/assets/mcp-marketplace-page-_Wu2VnHy.js +0 -1
  45. 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 };