nextclaw 0.13.0 → 0.13.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/dist/cli/index.js +148 -432
- package/package.json +6 -6
- package/templates/USAGE.md +0 -9
- package/ui-dist/assets/{ChannelsList-DH1Ur9XW.js → ChannelsList-i00X_bon.js} +1 -1
- package/ui-dist/assets/ChatPage-BEkVUTgU.js +38 -0
- package/ui-dist/assets/{DocBrowser-Bi-RpLIw.js → DocBrowser-DhSfSjNp.js} +1 -1
- package/ui-dist/assets/{LogoBadge-BCR9CU7n.js → LogoBadge-E8XCymGk.js} +1 -1
- package/ui-dist/assets/{MarketplacePage-BgCdiku7.js → MarketplacePage-DSa7G0ro.js} +1 -1
- package/ui-dist/assets/{McpMarketplacePage-nyCbiQH6.js → McpMarketplacePage-bDGyqSad.js} +1 -1
- package/ui-dist/assets/{ModelConfig-Cf4AAYaB.js → ModelConfig-Dvsyt1Tg.js} +1 -1
- package/ui-dist/assets/{ProvidersList-CfkfKQbw.js → ProvidersList-DMbXOYsb.js} +1 -1
- package/ui-dist/assets/{RuntimeConfig-BI-zClCl.js → RuntimeConfig-ToBbIBNn.js} +1 -1
- package/ui-dist/assets/{SearchConfig-MBmvco1J.js → SearchConfig-BV_SO-L3.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-CC2B6pVQ.js → SecretsConfig-D5J_q_Za.js} +1 -1
- package/ui-dist/assets/{SessionsConfig-CTxJeVQs.js → SessionsConfig-B8gi8Vbi.js} +1 -1
- package/ui-dist/assets/{chat-message-5OiyZViy.js → chat-message-v-cXhn7X.js} +1 -1
- package/ui-dist/assets/{index-LgjZxLjc.js → index-BXJPYlRo.js} +2 -2
- package/ui-dist/assets/index-BoDFsNXm.css +1 -0
- package/ui-dist/assets/{label-CPdcDrir.js → label-352ph_Yg.js} +1 -1
- package/ui-dist/assets/{page-layout-B8V5_vM_.js → page-layout-BwIR7lB8.js} +1 -1
- package/ui-dist/assets/{popover-Bk53A74_.js → popover-BdOI7aUv.js} +1 -1
- package/ui-dist/assets/{security-config-BNjgoyo4.js → security-config-BEhAoZux.js} +1 -1
- package/ui-dist/assets/{skeleton-NPxxR-L0.js → skeleton-C3DLX_PO.js} +1 -1
- package/ui-dist/assets/{switch-EowdzMK2.js → switch-CgUsIol7.js} +1 -1
- package/ui-dist/assets/{tabs-custom-BjLv-uCT.js → tabs-custom-Dc_3h5fO.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-TJcJQMfu.js → useConfirmDialog-DSrny346.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/ChatPage-YTDcN7XS.js +0 -38
- package/ui-dist/assets/index-C2OKcVdN.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import { APP_NAME as APP_NAME5, APP_TAGLINE } from "@nextclaw/core";
|
|
|
6
6
|
|
|
7
7
|
// src/cli/runtime.ts
|
|
8
8
|
import {
|
|
9
|
-
loadConfig as
|
|
9
|
+
loadConfig as loadConfig11,
|
|
10
10
|
saveConfig as saveConfig8,
|
|
11
|
-
getConfigPath as
|
|
12
|
-
getDataDir as
|
|
11
|
+
getConfigPath as getConfigPath4,
|
|
12
|
+
getDataDir as getDataDir8,
|
|
13
13
|
ConfigSchema as ConfigSchema2,
|
|
14
14
|
getWorkspacePath as getWorkspacePath10,
|
|
15
15
|
expandHome as expandHome2,
|
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
|
|
27
27
|
setPluginRuntimeBridge as setPluginRuntimeBridge2
|
|
28
28
|
} from "@nextclaw/openclaw-compat";
|
|
29
|
-
import { existsSync as
|
|
30
|
-
import { join as
|
|
29
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
30
|
+
import { join as join8, resolve as resolve12 } from "path";
|
|
31
31
|
import { createInterface as createInterface2 } from "readline";
|
|
32
32
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
33
33
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -74,9 +74,9 @@ var RestartCoordinator = class {
|
|
|
74
74
|
message: "Restart already scheduled; skipping duplicate request."
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
const
|
|
77
|
+
const delay = typeof request.delayMs === "number" && Number.isFinite(request.delayMs) ? Math.max(0, Math.floor(request.delayMs)) : 100;
|
|
78
78
|
this.exitScheduled = true;
|
|
79
|
-
this.deps.scheduleProcessExit(
|
|
79
|
+
this.deps.scheduleProcessExit(delay, reason);
|
|
80
80
|
return {
|
|
81
81
|
status: "exit-scheduled",
|
|
82
82
|
message: `Restart scheduled (${reason}).`
|
|
@@ -1486,7 +1486,6 @@ function toExtensionRegistry(pluginRegistry) {
|
|
|
1486
1486
|
kind: runtime2.kind,
|
|
1487
1487
|
label: runtime2.label,
|
|
1488
1488
|
createRuntime: runtime2.createRuntime,
|
|
1489
|
-
describeSessionType: runtime2.describeSessionType,
|
|
1490
1489
|
source: runtime2.source
|
|
1491
1490
|
})),
|
|
1492
1491
|
diagnostics: pluginRegistry.diagnostics.map((diag) => ({
|
|
@@ -2859,281 +2858,17 @@ var CronCommands = class {
|
|
|
2859
2858
|
}
|
|
2860
2859
|
};
|
|
2861
2860
|
|
|
2862
|
-
// src/cli/commands/remote.ts
|
|
2863
|
-
import { getConfigPath as getConfigPath2, getDataDir as getDataDir4, loadConfig as loadConfig7 } from "@nextclaw/core";
|
|
2864
|
-
import { ensureUiBridgeSecret } from "@nextclaw/server";
|
|
2865
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2866
|
-
import { dirname as dirname2, join as join4 } from "path";
|
|
2867
|
-
import { hostname, platform as readPlatform } from "os";
|
|
2868
|
-
function encodeBase64(bytes) {
|
|
2869
|
-
return Buffer.from(bytes).toString("base64");
|
|
2870
|
-
}
|
|
2871
|
-
function decodeBase64(base64) {
|
|
2872
|
-
if (!base64) {
|
|
2873
|
-
return new Uint8Array();
|
|
2874
|
-
}
|
|
2875
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
2876
|
-
}
|
|
2877
|
-
function delay(ms) {
|
|
2878
|
-
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
2879
|
-
}
|
|
2880
|
-
function ensureDir(path2) {
|
|
2881
|
-
mkdirSync4(path2, { recursive: true });
|
|
2882
|
-
}
|
|
2883
|
-
function readJsonFile2(path2) {
|
|
2884
|
-
if (!existsSync6(path2)) {
|
|
2885
|
-
return null;
|
|
2886
|
-
}
|
|
2887
|
-
try {
|
|
2888
|
-
return JSON.parse(readFileSync6(path2, "utf-8"));
|
|
2889
|
-
} catch {
|
|
2890
|
-
return null;
|
|
2891
|
-
}
|
|
2892
|
-
}
|
|
2893
|
-
function writeJsonFile(path2, value) {
|
|
2894
|
-
ensureDir(dirname2(path2));
|
|
2895
|
-
writeFileSync4(path2, `${JSON.stringify(value, null, 2)}
|
|
2896
|
-
`, "utf-8");
|
|
2897
|
-
}
|
|
2898
|
-
var RemoteCommands = class {
|
|
2899
|
-
remoteDir = join4(getDataDir4(), "remote");
|
|
2900
|
-
devicePath = join4(this.remoteDir, "device.json");
|
|
2901
|
-
ensureDeviceInstallId() {
|
|
2902
|
-
const existing = readJsonFile2(this.devicePath);
|
|
2903
|
-
if (existing?.deviceInstallId?.trim()) {
|
|
2904
|
-
return existing.deviceInstallId.trim();
|
|
2905
|
-
}
|
|
2906
|
-
const deviceInstallId = crypto.randomUUID();
|
|
2907
|
-
ensureDir(this.remoteDir);
|
|
2908
|
-
writeJsonFile(this.devicePath, { deviceInstallId });
|
|
2909
|
-
return deviceInstallId;
|
|
2910
|
-
}
|
|
2911
|
-
resolvePlatformAccess(opts) {
|
|
2912
|
-
const config2 = loadConfig7(getConfigPath2());
|
|
2913
|
-
const providers = config2.providers;
|
|
2914
|
-
const nextclawProvider = providers.nextclaw;
|
|
2915
|
-
const token = typeof nextclawProvider?.apiKey === "string" ? nextclawProvider.apiKey.trim() : "";
|
|
2916
|
-
if (!token) {
|
|
2917
|
-
throw new Error('NextClaw platform token is missing. Run "nextclaw login" first.');
|
|
2918
|
-
}
|
|
2919
|
-
const configuredApiBase = typeof nextclawProvider?.apiBase === "string" ? nextclawProvider.apiBase.trim() : "";
|
|
2920
|
-
const rawApiBase = typeof opts.apiBase === "string" && opts.apiBase.trim().length > 0 ? opts.apiBase.trim() : configuredApiBase;
|
|
2921
|
-
if (!rawApiBase) {
|
|
2922
|
-
throw new Error("Platform API base is missing. Pass --api-base or run nextclaw login.");
|
|
2923
|
-
}
|
|
2924
|
-
const platformBase = rawApiBase.replace(/\/v1\/?$/i, "");
|
|
2925
|
-
return { platformBase, token, config: config2 };
|
|
2926
|
-
}
|
|
2927
|
-
resolveLocalOrigin(config2, opts) {
|
|
2928
|
-
if (typeof opts.localOrigin === "string" && opts.localOrigin.trim().length > 0) {
|
|
2929
|
-
return opts.localOrigin.trim().replace(/\/$/, "");
|
|
2930
|
-
}
|
|
2931
|
-
const state = readServiceState();
|
|
2932
|
-
if (state && isProcessRunning(state.pid) && Number.isFinite(state.uiPort)) {
|
|
2933
|
-
return `http://127.0.0.1:${state.uiPort}`;
|
|
2934
|
-
}
|
|
2935
|
-
const configuredPort = typeof config2.ui?.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
|
|
2936
|
-
return `http://127.0.0.1:${configuredPort}`;
|
|
2937
|
-
}
|
|
2938
|
-
async ensureLocalUiHealthy(localOrigin) {
|
|
2939
|
-
const response = await fetch(`${localOrigin}/api/health`);
|
|
2940
|
-
if (!response.ok) {
|
|
2941
|
-
throw new Error(`Local UI is not healthy at ${localOrigin}. Start NextClaw first.`);
|
|
2942
|
-
}
|
|
2943
|
-
}
|
|
2944
|
-
async registerDevice(params) {
|
|
2945
|
-
const response = await fetch(`${params.platformBase}/platform/remote/devices/register`, {
|
|
2946
|
-
method: "POST",
|
|
2947
|
-
headers: {
|
|
2948
|
-
"content-type": "application/json",
|
|
2949
|
-
authorization: `Bearer ${params.token}`
|
|
2950
|
-
},
|
|
2951
|
-
body: JSON.stringify({
|
|
2952
|
-
deviceInstallId: params.deviceInstallId,
|
|
2953
|
-
displayName: params.displayName,
|
|
2954
|
-
platform: readPlatform(),
|
|
2955
|
-
appVersion: getPackageVersion(),
|
|
2956
|
-
localOrigin: params.localOrigin
|
|
2957
|
-
})
|
|
2958
|
-
});
|
|
2959
|
-
const payload = await response.json();
|
|
2960
|
-
if (!response.ok || !payload.ok || !payload.data?.device) {
|
|
2961
|
-
throw new Error(payload.error?.message ?? `Failed to register remote device (${response.status}).`);
|
|
2962
|
-
}
|
|
2963
|
-
return payload.data.device;
|
|
2964
|
-
}
|
|
2965
|
-
async requestBridgeCookie(localOrigin) {
|
|
2966
|
-
const response = await fetch(`${localOrigin}/api/auth/bridge`, {
|
|
2967
|
-
method: "POST",
|
|
2968
|
-
headers: {
|
|
2969
|
-
"x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret()
|
|
2970
|
-
}
|
|
2971
|
-
});
|
|
2972
|
-
const payload = await response.json();
|
|
2973
|
-
if (!response.ok || !payload.ok) {
|
|
2974
|
-
throw new Error(payload.error?.message ?? `Failed to request local auth bridge (${response.status}).`);
|
|
2975
|
-
}
|
|
2976
|
-
return typeof payload.data?.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
|
|
2977
|
-
}
|
|
2978
|
-
async handleRelayRequest(params) {
|
|
2979
|
-
const bridgeCookie = await this.requestBridgeCookie(params.localOrigin);
|
|
2980
|
-
const url = new URL(params.frame.path, params.localOrigin);
|
|
2981
|
-
const headers = new Headers();
|
|
2982
|
-
for (const [key, value] of params.frame.headers) {
|
|
2983
|
-
const lower = key.toLowerCase();
|
|
2984
|
-
if ([
|
|
2985
|
-
"host",
|
|
2986
|
-
"connection",
|
|
2987
|
-
"content-length",
|
|
2988
|
-
"cookie",
|
|
2989
|
-
"x-forwarded-for",
|
|
2990
|
-
"x-forwarded-proto",
|
|
2991
|
-
"cf-connecting-ip"
|
|
2992
|
-
].includes(lower)) {
|
|
2993
|
-
continue;
|
|
2994
|
-
}
|
|
2995
|
-
headers.set(key, value);
|
|
2996
|
-
}
|
|
2997
|
-
if (bridgeCookie) {
|
|
2998
|
-
headers.set("cookie", bridgeCookie);
|
|
2999
|
-
}
|
|
3000
|
-
const bodyBytes = decodeBase64(params.frame.bodyBase64);
|
|
3001
|
-
const response = await fetch(url, {
|
|
3002
|
-
method: params.frame.method,
|
|
3003
|
-
headers,
|
|
3004
|
-
body: params.frame.method === "GET" || params.frame.method === "HEAD" ? void 0 : bodyBytes
|
|
3005
|
-
});
|
|
3006
|
-
const responseHeaders = Array.from(response.headers.entries()).filter(([key]) => {
|
|
3007
|
-
const lower = key.toLowerCase();
|
|
3008
|
-
return !["content-length", "connection", "transfer-encoding", "set-cookie"].includes(lower);
|
|
3009
|
-
});
|
|
3010
|
-
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
3011
|
-
if (response.body && contentType.startsWith("text/event-stream")) {
|
|
3012
|
-
params.socket.send(JSON.stringify({
|
|
3013
|
-
type: "response.start",
|
|
3014
|
-
requestId: params.frame.requestId,
|
|
3015
|
-
status: response.status,
|
|
3016
|
-
headers: responseHeaders
|
|
3017
|
-
}));
|
|
3018
|
-
const reader = response.body.getReader();
|
|
3019
|
-
try {
|
|
3020
|
-
while (true) {
|
|
3021
|
-
const { value, done } = await reader.read();
|
|
3022
|
-
if (done) {
|
|
3023
|
-
break;
|
|
3024
|
-
}
|
|
3025
|
-
if (value && value.length > 0) {
|
|
3026
|
-
params.socket.send(JSON.stringify({
|
|
3027
|
-
type: "response.chunk",
|
|
3028
|
-
requestId: params.frame.requestId,
|
|
3029
|
-
bodyBase64: encodeBase64(value)
|
|
3030
|
-
}));
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
} finally {
|
|
3034
|
-
reader.releaseLock();
|
|
3035
|
-
}
|
|
3036
|
-
params.socket.send(JSON.stringify({
|
|
3037
|
-
type: "response.end",
|
|
3038
|
-
requestId: params.frame.requestId
|
|
3039
|
-
}));
|
|
3040
|
-
return;
|
|
3041
|
-
}
|
|
3042
|
-
const responseBody = response.body ? new Uint8Array(await response.arrayBuffer()) : new Uint8Array();
|
|
3043
|
-
params.socket.send(JSON.stringify({
|
|
3044
|
-
type: "response",
|
|
3045
|
-
requestId: params.frame.requestId,
|
|
3046
|
-
status: response.status,
|
|
3047
|
-
headers: responseHeaders,
|
|
3048
|
-
bodyBase64: encodeBase64(responseBody)
|
|
3049
|
-
}));
|
|
3050
|
-
}
|
|
3051
|
-
async connectOnce(params) {
|
|
3052
|
-
await new Promise((resolve13, reject) => {
|
|
3053
|
-
const socket = new WebSocket(params.wsUrl);
|
|
3054
|
-
const pingTimer = setInterval(() => {
|
|
3055
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
3056
|
-
socket.send(JSON.stringify({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
3057
|
-
}
|
|
3058
|
-
}, 15e3);
|
|
3059
|
-
socket.addEventListener("open", () => {
|
|
3060
|
-
console.log(`\u2713 Remote connector connected: ${params.wsUrl}`);
|
|
3061
|
-
});
|
|
3062
|
-
socket.addEventListener("message", (event) => {
|
|
3063
|
-
void (async () => {
|
|
3064
|
-
let frame = null;
|
|
3065
|
-
try {
|
|
3066
|
-
frame = JSON.parse(String(event.data ?? ""));
|
|
3067
|
-
} catch {
|
|
3068
|
-
return;
|
|
3069
|
-
}
|
|
3070
|
-
if (!frame || frame.type !== "request") {
|
|
3071
|
-
return;
|
|
3072
|
-
}
|
|
3073
|
-
try {
|
|
3074
|
-
await this.handleRelayRequest({ frame, localOrigin: params.localOrigin, socket });
|
|
3075
|
-
} catch (error) {
|
|
3076
|
-
socket.send(JSON.stringify({
|
|
3077
|
-
type: "response.error",
|
|
3078
|
-
requestId: frame.requestId,
|
|
3079
|
-
message: error instanceof Error ? error.message : String(error)
|
|
3080
|
-
}));
|
|
3081
|
-
}
|
|
3082
|
-
})();
|
|
3083
|
-
});
|
|
3084
|
-
socket.addEventListener("close", () => {
|
|
3085
|
-
clearInterval(pingTimer);
|
|
3086
|
-
resolve13();
|
|
3087
|
-
});
|
|
3088
|
-
socket.addEventListener("error", () => {
|
|
3089
|
-
clearInterval(pingTimer);
|
|
3090
|
-
reject(new Error("Remote connector websocket failed."));
|
|
3091
|
-
});
|
|
3092
|
-
});
|
|
3093
|
-
}
|
|
3094
|
-
async connect(opts = {}) {
|
|
3095
|
-
const { platformBase, token, config: config2 } = this.resolvePlatformAccess(opts);
|
|
3096
|
-
const localOrigin = this.resolveLocalOrigin(config2, opts);
|
|
3097
|
-
await this.ensureLocalUiHealthy(localOrigin);
|
|
3098
|
-
const deviceInstallId = this.ensureDeviceInstallId();
|
|
3099
|
-
const displayName = typeof opts.name === "string" && opts.name.trim().length > 0 ? opts.name.trim() : hostname();
|
|
3100
|
-
const device = await this.registerDevice({
|
|
3101
|
-
platformBase,
|
|
3102
|
-
token,
|
|
3103
|
-
deviceInstallId,
|
|
3104
|
-
displayName,
|
|
3105
|
-
localOrigin
|
|
3106
|
-
});
|
|
3107
|
-
console.log(`\u2713 Remote device registered: ${device.displayName} (${device.id})`);
|
|
3108
|
-
console.log(`\u2713 Local origin: ${localOrigin}`);
|
|
3109
|
-
console.log(`\u2713 Platform: ${platformBase}`);
|
|
3110
|
-
const wsUrl = `${platformBase.replace(/^http/i, "ws")}/platform/remote/connect?deviceId=${encodeURIComponent(device.id)}&token=${encodeURIComponent(token)}`;
|
|
3111
|
-
do {
|
|
3112
|
-
try {
|
|
3113
|
-
await this.connectOnce({ wsUrl, localOrigin });
|
|
3114
|
-
} catch (error) {
|
|
3115
|
-
console.error(`Remote connector error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3116
|
-
}
|
|
3117
|
-
if (opts.once) {
|
|
3118
|
-
break;
|
|
3119
|
-
}
|
|
3120
|
-
console.log("Remote connector disconnected. Reconnecting in 3s...");
|
|
3121
|
-
await delay(3e3);
|
|
3122
|
-
} while (!opts.once);
|
|
3123
|
-
}
|
|
3124
|
-
};
|
|
3125
|
-
|
|
3126
2861
|
// src/cli/commands/diagnostics.ts
|
|
3127
2862
|
import { createServer as createNetServer } from "net";
|
|
3128
|
-
import { existsSync as
|
|
2863
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3129
2864
|
import { resolve as resolve8 } from "path";
|
|
3130
2865
|
import {
|
|
3131
2866
|
APP_NAME,
|
|
3132
|
-
getConfigPath as
|
|
3133
|
-
getDataDir as
|
|
2867
|
+
getConfigPath as getConfigPath2,
|
|
2868
|
+
getDataDir as getDataDir4,
|
|
3134
2869
|
getWorkspacePath as getWorkspacePath4,
|
|
3135
2870
|
hasSecretRef,
|
|
3136
|
-
loadConfig as
|
|
2871
|
+
loadConfig as loadConfig7
|
|
3137
2872
|
} from "@nextclaw/core";
|
|
3138
2873
|
import { listBuiltinProviders } from "@nextclaw/runtime";
|
|
3139
2874
|
var DiagnosticsCommands = class {
|
|
@@ -3294,10 +3029,10 @@ var DiagnosticsCommands = class {
|
|
|
3294
3029
|
process.exitCode = exitCode;
|
|
3295
3030
|
}
|
|
3296
3031
|
async collectRuntimeStatus(params) {
|
|
3297
|
-
const configPath =
|
|
3298
|
-
const config2 =
|
|
3032
|
+
const configPath = getConfigPath2();
|
|
3033
|
+
const config2 = loadConfig7();
|
|
3299
3034
|
const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
|
|
3300
|
-
const serviceStatePath = resolve8(
|
|
3035
|
+
const serviceStatePath = resolve8(getDataDir4(), "run", "service.json");
|
|
3301
3036
|
const fixActions = [];
|
|
3302
3037
|
let serviceState = readServiceState();
|
|
3303
3038
|
if (params.fix && serviceState && !isProcessRunning(serviceState.pid)) {
|
|
@@ -3337,11 +3072,11 @@ var DiagnosticsCommands = class {
|
|
|
3337
3072
|
});
|
|
3338
3073
|
const issues = [];
|
|
3339
3074
|
const recommendations = [];
|
|
3340
|
-
if (!
|
|
3075
|
+
if (!existsSync6(configPath)) {
|
|
3341
3076
|
issues.push("Config file is missing.");
|
|
3342
3077
|
recommendations.push(`Run ${APP_NAME} init to create config files.`);
|
|
3343
3078
|
}
|
|
3344
|
-
if (!
|
|
3079
|
+
if (!existsSync6(workspacePath)) {
|
|
3345
3080
|
issues.push("Workspace directory does not exist.");
|
|
3346
3081
|
recommendations.push(`Run ${APP_NAME} init to create workspace templates.`);
|
|
3347
3082
|
}
|
|
@@ -3374,13 +3109,13 @@ var DiagnosticsCommands = class {
|
|
|
3374
3109
|
return {
|
|
3375
3110
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3376
3111
|
configPath,
|
|
3377
|
-
configExists:
|
|
3112
|
+
configExists: existsSync6(configPath),
|
|
3378
3113
|
workspacePath,
|
|
3379
|
-
workspaceExists:
|
|
3114
|
+
workspaceExists: existsSync6(workspacePath),
|
|
3380
3115
|
model: config2.agents.defaults.model,
|
|
3381
3116
|
providers,
|
|
3382
3117
|
serviceStatePath,
|
|
3383
|
-
serviceStateExists:
|
|
3118
|
+
serviceStateExists: existsSync6(serviceStatePath),
|
|
3384
3119
|
fixActions,
|
|
3385
3120
|
process: {
|
|
3386
3121
|
managedByState,
|
|
@@ -3430,11 +3165,11 @@ var DiagnosticsCommands = class {
|
|
|
3430
3165
|
}
|
|
3431
3166
|
}
|
|
3432
3167
|
readLogTail(path2, maxLines = 25) {
|
|
3433
|
-
if (!
|
|
3168
|
+
if (!existsSync6(path2)) {
|
|
3434
3169
|
return [];
|
|
3435
3170
|
}
|
|
3436
3171
|
try {
|
|
3437
|
-
const lines =
|
|
3172
|
+
const lines = readFileSync6(path2, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
3438
3173
|
if (lines.length <= maxLines) {
|
|
3439
3174
|
return lines;
|
|
3440
3175
|
}
|
|
@@ -3474,8 +3209,8 @@ import {
|
|
|
3474
3209
|
stopPluginChannelGateways as stopPluginChannelGateways2
|
|
3475
3210
|
} from "@nextclaw/openclaw-compat";
|
|
3476
3211
|
import { startUiServer } from "@nextclaw/server";
|
|
3477
|
-
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as
|
|
3478
|
-
import { dirname as
|
|
3212
|
+
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, openSync } from "fs";
|
|
3213
|
+
import { dirname as dirname2, join as join6, resolve as resolve10 } from "path";
|
|
3479
3214
|
import { spawn as spawn2 } from "child_process";
|
|
3480
3215
|
import { request as httpRequest } from "http";
|
|
3481
3216
|
import { request as httpsRequest } from "https";
|
|
@@ -3484,7 +3219,7 @@ import chokidar from "chokidar";
|
|
|
3484
3219
|
|
|
3485
3220
|
// src/cli/gateway/controller.ts
|
|
3486
3221
|
import { createHash } from "crypto";
|
|
3487
|
-
import { existsSync as
|
|
3222
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
3488
3223
|
import {
|
|
3489
3224
|
buildConfigSchema,
|
|
3490
3225
|
ConfigSchema,
|
|
@@ -3492,12 +3227,12 @@ import {
|
|
|
3492
3227
|
redactConfigObject
|
|
3493
3228
|
} from "@nextclaw/core";
|
|
3494
3229
|
var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
3495
|
-
var readConfigSnapshot = (
|
|
3496
|
-
const path2 =
|
|
3230
|
+
var readConfigSnapshot = (getConfigPath5) => {
|
|
3231
|
+
const path2 = getConfigPath5();
|
|
3497
3232
|
let raw = "";
|
|
3498
3233
|
let parsed = {};
|
|
3499
|
-
if (
|
|
3500
|
-
raw =
|
|
3234
|
+
if (existsSync7(path2)) {
|
|
3235
|
+
raw = readFileSync7(path2, "utf-8");
|
|
3501
3236
|
try {
|
|
3502
3237
|
parsed = JSON.parse(raw);
|
|
3503
3238
|
} catch {
|
|
@@ -3604,11 +3339,11 @@ var GatewayControllerImpl = class {
|
|
|
3604
3339
|
await this.deps.requestRestart(options);
|
|
3605
3340
|
return;
|
|
3606
3341
|
}
|
|
3607
|
-
const
|
|
3342
|
+
const delay = typeof options?.delayMs === "number" && Number.isFinite(options.delayMs) ? Math.max(0, options.delayMs) : 100;
|
|
3608
3343
|
console.log(`Gateway restart requested via tool${options?.reason ? ` (${options.reason})` : ""}.`);
|
|
3609
3344
|
setTimeout(() => {
|
|
3610
3345
|
process.exit(0);
|
|
3611
|
-
},
|
|
3346
|
+
}, delay);
|
|
3612
3347
|
}
|
|
3613
3348
|
status() {
|
|
3614
3349
|
return {
|
|
@@ -3954,9 +3689,9 @@ var MissingProvider = class extends LLMProvider {
|
|
|
3954
3689
|
};
|
|
3955
3690
|
|
|
3956
3691
|
// src/cli/commands/service-marketplace-installer.ts
|
|
3957
|
-
import { getWorkspacePath as getWorkspacePath5, loadConfig as
|
|
3958
|
-
import { existsSync as
|
|
3959
|
-
import { join as
|
|
3692
|
+
import { getWorkspacePath as getWorkspacePath5, loadConfig as loadConfig9 } from "@nextclaw/core";
|
|
3693
|
+
import { existsSync as existsSync8, rmSync as rmSync4 } from "fs";
|
|
3694
|
+
import { join as join4 } from "path";
|
|
3960
3695
|
|
|
3961
3696
|
// src/cli/commands/service-marketplace-helpers.ts
|
|
3962
3697
|
var containsAbsoluteFsPath = (line) => {
|
|
@@ -4004,7 +3739,7 @@ var buildMarketplaceSkillInstallArgs = (params) => {
|
|
|
4004
3739
|
};
|
|
4005
3740
|
|
|
4006
3741
|
// src/cli/commands/service-mcp-marketplace-ops.ts
|
|
4007
|
-
import { loadConfig as
|
|
3742
|
+
import { loadConfig as loadConfig8, saveConfig as saveConfig6 } from "@nextclaw/core";
|
|
4008
3743
|
import { McpDoctorFacade as McpDoctorFacade2, McpMutationService as McpMutationService2 } from "@nextclaw/mcp";
|
|
4009
3744
|
var ServiceMcpMarketplaceOps = class {
|
|
4010
3745
|
constructor(options) {
|
|
@@ -4072,13 +3807,13 @@ var ServiceMcpMarketplaceOps = class {
|
|
|
4072
3807
|
}
|
|
4073
3808
|
createMutationService() {
|
|
4074
3809
|
return new McpMutationService2({
|
|
4075
|
-
getConfig: () =>
|
|
3810
|
+
getConfig: () => loadConfig8(),
|
|
4076
3811
|
saveConfig: (config2) => saveConfig6(config2)
|
|
4077
3812
|
});
|
|
4078
3813
|
}
|
|
4079
3814
|
createDoctorFacade() {
|
|
4080
3815
|
return new McpDoctorFacade2({
|
|
4081
|
-
getConfig: () =>
|
|
3816
|
+
getConfig: () => loadConfig8()
|
|
4082
3817
|
});
|
|
4083
3818
|
}
|
|
4084
3819
|
};
|
|
@@ -4119,7 +3854,7 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4119
3854
|
if (params.kind && params.kind !== "marketplace") {
|
|
4120
3855
|
throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
|
|
4121
3856
|
}
|
|
4122
|
-
const workspace = getWorkspacePath5(
|
|
3857
|
+
const workspace = getWorkspacePath5(loadConfig9().agents.defaults.workspace);
|
|
4123
3858
|
const args = buildMarketplaceSkillInstallArgs({
|
|
4124
3859
|
slug: params.slug,
|
|
4125
3860
|
workspace,
|
|
@@ -4158,9 +3893,9 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4158
3893
|
return { message: result.message };
|
|
4159
3894
|
}
|
|
4160
3895
|
async uninstallSkill(slug) {
|
|
4161
|
-
const workspace = getWorkspacePath5(
|
|
4162
|
-
const targetDir =
|
|
4163
|
-
if (!
|
|
3896
|
+
const workspace = getWorkspacePath5(loadConfig9().agents.defaults.workspace);
|
|
3897
|
+
const targetDir = join4(workspace, "skills", slug);
|
|
3898
|
+
if (!existsSync8(targetDir)) {
|
|
4164
3899
|
throw new Error(`Skill not installed in workspace: ${slug}`);
|
|
4165
3900
|
}
|
|
4166
3901
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
@@ -5940,33 +5675,22 @@ var UiNcpRuntimeRegistry = class {
|
|
|
5940
5675
|
sessionMetadata: nextSessionMetadata
|
|
5941
5676
|
});
|
|
5942
5677
|
}
|
|
5943
|
-
|
|
5944
|
-
const options =
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
};
|
|
5957
|
-
})
|
|
5958
|
-
);
|
|
5678
|
+
listSessionTypes() {
|
|
5679
|
+
const options = [...this.registrations.values()].map((registration) => ({
|
|
5680
|
+
value: registration.kind,
|
|
5681
|
+
label: registration.label
|
|
5682
|
+
})).sort((left, right) => {
|
|
5683
|
+
if (left.value === this.defaultKind) {
|
|
5684
|
+
return -1;
|
|
5685
|
+
}
|
|
5686
|
+
if (right.value === this.defaultKind) {
|
|
5687
|
+
return 1;
|
|
5688
|
+
}
|
|
5689
|
+
return left.value.localeCompare(right.value);
|
|
5690
|
+
});
|
|
5959
5691
|
return {
|
|
5960
5692
|
defaultType: this.defaultKind,
|
|
5961
|
-
options
|
|
5962
|
-
if (left.value === this.defaultKind) {
|
|
5963
|
-
return -1;
|
|
5964
|
-
}
|
|
5965
|
-
if (right.value === this.defaultKind) {
|
|
5966
|
-
return 1;
|
|
5967
|
-
}
|
|
5968
|
-
return left.value.localeCompare(right.value);
|
|
5969
|
-
})
|
|
5693
|
+
options
|
|
5970
5694
|
};
|
|
5971
5695
|
}
|
|
5972
5696
|
};
|
|
@@ -6072,8 +5796,7 @@ async function createUiNcpAgent(params) {
|
|
|
6072
5796
|
scope.add(runtimeRegistry.register({
|
|
6073
5797
|
kind: registration.kind,
|
|
6074
5798
|
label: registration.label,
|
|
6075
|
-
createRuntime: registration.createRuntime
|
|
6076
|
-
describeSessionType: registration.describeSessionType
|
|
5799
|
+
createRuntime: registration.createRuntime
|
|
6077
5800
|
}));
|
|
6078
5801
|
}
|
|
6079
5802
|
};
|
|
@@ -6121,14 +5844,14 @@ async function createUiNcpAgent(params) {
|
|
|
6121
5844
|
}
|
|
6122
5845
|
|
|
6123
5846
|
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
6124
|
-
import { existsSync as
|
|
6125
|
-
import { join as
|
|
5847
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
5848
|
+
import { join as join5 } from "path";
|
|
6126
5849
|
import {
|
|
6127
|
-
getDataDir as
|
|
5850
|
+
getDataDir as getDataDir5,
|
|
6128
5851
|
parseAgentScopedSessionKey as parseAgentScopedSessionKey2,
|
|
6129
5852
|
safeFilename
|
|
6130
5853
|
} from "@nextclaw/core";
|
|
6131
|
-
var RUNS_DIR =
|
|
5854
|
+
var RUNS_DIR = join5(getDataDir5(), "runs");
|
|
6132
5855
|
var NON_TERMINAL_STATES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
6133
5856
|
var DEFAULT_SESSION_TYPE = "native";
|
|
6134
5857
|
var SESSION_TYPE_METADATA_KEY = "session_type";
|
|
@@ -6187,7 +5910,7 @@ function hasToolSessionEvent(run) {
|
|
|
6187
5910
|
var UiChatRunCoordinator = class {
|
|
6188
5911
|
constructor(options) {
|
|
6189
5912
|
this.options = options;
|
|
6190
|
-
|
|
5913
|
+
mkdirSync4(RUNS_DIR, { recursive: true });
|
|
6191
5914
|
this.loadPersistedRuns();
|
|
6192
5915
|
}
|
|
6193
5916
|
runs = /* @__PURE__ */ new Map();
|
|
@@ -6654,7 +6377,7 @@ var UiChatRunCoordinator = class {
|
|
|
6654
6377
|
};
|
|
6655
6378
|
}
|
|
6656
6379
|
getRunPath(runId) {
|
|
6657
|
-
return
|
|
6380
|
+
return join5(RUNS_DIR, `${safeFilename(runId)}.json`);
|
|
6658
6381
|
}
|
|
6659
6382
|
persistRun(run) {
|
|
6660
6383
|
const persisted = {
|
|
@@ -6672,20 +6395,20 @@ var UiChatRunCoordinator = class {
|
|
|
6672
6395
|
...typeof run.reply === "string" ? { reply: run.reply } : {},
|
|
6673
6396
|
events: run.events
|
|
6674
6397
|
};
|
|
6675
|
-
|
|
6398
|
+
writeFileSync4(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
|
|
6676
6399
|
`);
|
|
6677
6400
|
}
|
|
6678
6401
|
loadPersistedRuns() {
|
|
6679
|
-
if (!
|
|
6402
|
+
if (!existsSync9(RUNS_DIR)) {
|
|
6680
6403
|
return;
|
|
6681
6404
|
}
|
|
6682
6405
|
for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
|
|
6683
6406
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
6684
6407
|
continue;
|
|
6685
6408
|
}
|
|
6686
|
-
const path2 =
|
|
6409
|
+
const path2 = join5(RUNS_DIR, entry.name);
|
|
6687
6410
|
try {
|
|
6688
|
-
const parsed = JSON.parse(
|
|
6411
|
+
const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
6689
6412
|
const runId = readOptionalString(parsed.runId);
|
|
6690
6413
|
const sessionKey = readOptionalString(parsed.sessionKey);
|
|
6691
6414
|
if (!runId || !sessionKey) {
|
|
@@ -6744,14 +6467,14 @@ var {
|
|
|
6744
6467
|
ChannelManager: ChannelManager2,
|
|
6745
6468
|
CronService: CronService2,
|
|
6746
6469
|
getApiBase,
|
|
6747
|
-
getConfigPath:
|
|
6748
|
-
getDataDir:
|
|
6470
|
+
getConfigPath: getConfigPath3,
|
|
6471
|
+
getDataDir: getDataDir6,
|
|
6749
6472
|
getProvider,
|
|
6750
6473
|
getProviderName,
|
|
6751
6474
|
getWorkspacePath: getWorkspacePath9,
|
|
6752
6475
|
HeartbeatService,
|
|
6753
6476
|
LiteLLMProvider,
|
|
6754
|
-
loadConfig:
|
|
6477
|
+
loadConfig: loadConfig10,
|
|
6755
6478
|
MessageBus,
|
|
6756
6479
|
ProviderManager,
|
|
6757
6480
|
resolveConfigSecrets: resolveConfigSecrets2,
|
|
@@ -6775,8 +6498,8 @@ var ServiceCommands = class {
|
|
|
6775
6498
|
async startGateway(options = {}) {
|
|
6776
6499
|
this.applyLiveConfigReload = null;
|
|
6777
6500
|
this.liveUiNcpAgent = null;
|
|
6778
|
-
const runtimeConfigPath =
|
|
6779
|
-
const config2 = resolveConfigSecrets2(
|
|
6501
|
+
const runtimeConfigPath = getConfigPath3();
|
|
6502
|
+
const config2 = resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath });
|
|
6780
6503
|
const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
|
|
6781
6504
|
let pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
6782
6505
|
let extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -6806,7 +6529,7 @@ var ServiceCommands = class {
|
|
|
6806
6529
|
}
|
|
6807
6530
|
}
|
|
6808
6531
|
};
|
|
6809
|
-
const cronStorePath =
|
|
6532
|
+
const cronStorePath = join6(getDataDir6(), "cron", "jobs.json");
|
|
6810
6533
|
const cron2 = new CronService2(cronStorePath);
|
|
6811
6534
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
6812
6535
|
const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
|
|
@@ -6821,7 +6544,7 @@ var ServiceCommands = class {
|
|
|
6821
6544
|
sessionManager,
|
|
6822
6545
|
providerManager,
|
|
6823
6546
|
makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
|
|
6824
|
-
loadConfig: () => resolveConfigSecrets2(
|
|
6547
|
+
loadConfig: () => resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
|
|
6825
6548
|
getExtensionChannels: () => extensionRegistry.channels,
|
|
6826
6549
|
onRestartRequired: (paths) => {
|
|
6827
6550
|
void this.deps.requestRestart({
|
|
@@ -6832,13 +6555,13 @@ var ServiceCommands = class {
|
|
|
6832
6555
|
}
|
|
6833
6556
|
});
|
|
6834
6557
|
this.applyLiveConfigReload = async () => {
|
|
6835
|
-
await reloader.applyReloadPlan(resolveConfigSecrets2(
|
|
6558
|
+
await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }));
|
|
6836
6559
|
};
|
|
6837
6560
|
const gatewayController = new GatewayControllerImpl({
|
|
6838
6561
|
reloader,
|
|
6839
6562
|
cron: cron2,
|
|
6840
6563
|
sessionManager,
|
|
6841
|
-
getConfigPath:
|
|
6564
|
+
getConfigPath: getConfigPath3,
|
|
6842
6565
|
saveConfig: saveConfig7,
|
|
6843
6566
|
requestRestart: async (options2) => {
|
|
6844
6567
|
await this.deps.requestRestart({
|
|
@@ -6865,7 +6588,7 @@ var ServiceCommands = class {
|
|
|
6865
6588
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
6866
6589
|
registry: pluginRegistry,
|
|
6867
6590
|
channel,
|
|
6868
|
-
cfg: resolveConfigSecrets2(
|
|
6591
|
+
cfg: resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
|
|
6869
6592
|
accountId
|
|
6870
6593
|
})
|
|
6871
6594
|
});
|
|
@@ -6898,12 +6621,12 @@ var ServiceCommands = class {
|
|
|
6898
6621
|
});
|
|
6899
6622
|
let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
|
|
6900
6623
|
setPluginRuntimeBridge({
|
|
6901
|
-
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(
|
|
6624
|
+
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }), pluginChannelBindings),
|
|
6902
6625
|
writeConfigFile: async (nextConfigView) => {
|
|
6903
6626
|
if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
|
|
6904
6627
|
throw new Error("plugin runtime writeConfigFile expects an object config");
|
|
6905
6628
|
}
|
|
6906
|
-
const current =
|
|
6629
|
+
const current = loadConfig10();
|
|
6907
6630
|
const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
|
|
6908
6631
|
saveConfig7(next);
|
|
6909
6632
|
},
|
|
@@ -6979,12 +6702,12 @@ var ServiceCommands = class {
|
|
|
6979
6702
|
providerManager,
|
|
6980
6703
|
bus,
|
|
6981
6704
|
gatewayController,
|
|
6982
|
-
() => resolveConfigSecrets2(
|
|
6705
|
+
() => resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
|
|
6983
6706
|
() => extensionRegistry,
|
|
6984
6707
|
({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
6985
6708
|
registry: pluginRegistry,
|
|
6986
6709
|
channel,
|
|
6987
|
-
cfg: resolveConfigSecrets2(
|
|
6710
|
+
cfg: resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
|
|
6988
6711
|
accountId
|
|
6989
6712
|
})
|
|
6990
6713
|
);
|
|
@@ -7021,7 +6744,7 @@ var ServiceCommands = class {
|
|
|
7021
6744
|
return trimmed || void 0;
|
|
7022
6745
|
}
|
|
7023
6746
|
watchConfigFile(reloader) {
|
|
7024
|
-
const configPath = resolve10(
|
|
6747
|
+
const configPath = resolve10(getConfigPath3());
|
|
7025
6748
|
const watcher = chokidar.watch(configPath, {
|
|
7026
6749
|
ignoreInitial: true,
|
|
7027
6750
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
@@ -7142,7 +6865,7 @@ var ServiceCommands = class {
|
|
|
7142
6865
|
});
|
|
7143
6866
|
}
|
|
7144
6867
|
async runForeground(options) {
|
|
7145
|
-
const config2 =
|
|
6868
|
+
const config2 = loadConfig10();
|
|
7146
6869
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7147
6870
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7148
6871
|
if (options.open) {
|
|
@@ -7155,7 +6878,7 @@ var ServiceCommands = class {
|
|
|
7155
6878
|
});
|
|
7156
6879
|
}
|
|
7157
6880
|
async startService(options) {
|
|
7158
|
-
const config2 =
|
|
6881
|
+
const config2 = loadConfig10();
|
|
7159
6882
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7160
6883
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7161
6884
|
const apiUrl = `${uiUrl}/api`;
|
|
@@ -7216,7 +6939,7 @@ var ServiceCommands = class {
|
|
|
7216
6939
|
}
|
|
7217
6940
|
const logPath = resolveServiceLogPath();
|
|
7218
6941
|
const logDir = resolve10(logPath, "..");
|
|
7219
|
-
|
|
6942
|
+
mkdirSync5(logDir, { recursive: true });
|
|
7220
6943
|
const logFd = openSync(logPath, "a");
|
|
7221
6944
|
const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
|
|
7222
6945
|
const quickPhaseTimeoutMs = Math.min(8e3, readinessTimeoutMs);
|
|
@@ -7569,7 +7292,7 @@ var ServiceCommands = class {
|
|
|
7569
7292
|
return null;
|
|
7570
7293
|
}
|
|
7571
7294
|
console.error("Error: No API key configured.");
|
|
7572
|
-
console.error(`Set one in ${
|
|
7295
|
+
console.error(`Set one in ${getConfigPath3()} under providers section`);
|
|
7573
7296
|
process.exit(1);
|
|
7574
7297
|
}
|
|
7575
7298
|
return new LiteLLMProvider({
|
|
@@ -7696,7 +7419,7 @@ var ServiceCommands = class {
|
|
|
7696
7419
|
const uiServer = startUiServer({
|
|
7697
7420
|
host: uiConfig.host,
|
|
7698
7421
|
port: uiConfig.port,
|
|
7699
|
-
configPath:
|
|
7422
|
+
configPath: getConfigPath3(),
|
|
7700
7423
|
productVersion: getPackageVersion(),
|
|
7701
7424
|
staticDir: uiStaticDir ?? void 0,
|
|
7702
7425
|
cronService,
|
|
@@ -7784,10 +7507,10 @@ var ServiceCommands = class {
|
|
|
7784
7507
|
}
|
|
7785
7508
|
}
|
|
7786
7509
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
7787
|
-
const workspace = getWorkspacePath9(
|
|
7788
|
-
const destination =
|
|
7789
|
-
const destinationSkillFile =
|
|
7790
|
-
if (
|
|
7510
|
+
const workspace = getWorkspacePath9(loadConfig10().agents.defaults.workspace);
|
|
7511
|
+
const destination = join6(workspace, "skills", slug);
|
|
7512
|
+
const destinationSkillFile = join6(destination, "SKILL.md");
|
|
7513
|
+
if (existsSync10(destinationSkillFile) && !force) {
|
|
7791
7514
|
return {
|
|
7792
7515
|
message: `${slug} is already installed`
|
|
7793
7516
|
};
|
|
@@ -7795,15 +7518,15 @@ var ServiceCommands = class {
|
|
|
7795
7518
|
const loader = createSkillsLoader(workspace);
|
|
7796
7519
|
const builtin = (loader?.listSkills(false) ?? []).find((skill) => skill.name === slug && skill.source === "builtin");
|
|
7797
7520
|
if (!builtin) {
|
|
7798
|
-
if (
|
|
7521
|
+
if (existsSync10(destinationSkillFile)) {
|
|
7799
7522
|
return {
|
|
7800
7523
|
message: `${slug} is already installed`
|
|
7801
7524
|
};
|
|
7802
7525
|
}
|
|
7803
7526
|
return null;
|
|
7804
7527
|
}
|
|
7805
|
-
|
|
7806
|
-
cpSync2(
|
|
7528
|
+
mkdirSync5(join6(workspace, "skills"), { recursive: true });
|
|
7529
|
+
cpSync2(dirname2(builtin.path), destination, { recursive: true, force: true });
|
|
7807
7530
|
return {
|
|
7808
7531
|
message: `Installed skill: ${slug}`
|
|
7809
7532
|
};
|
|
@@ -7862,11 +7585,11 @@ ${stderr}`.trim();
|
|
|
7862
7585
|
};
|
|
7863
7586
|
|
|
7864
7587
|
// src/cli/workspace.ts
|
|
7865
|
-
import { cpSync as cpSync3, existsSync as
|
|
7588
|
+
import { cpSync as cpSync3, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
7866
7589
|
import { createRequire as createRequire2 } from "module";
|
|
7867
|
-
import { dirname as
|
|
7590
|
+
import { dirname as dirname3, join as join7, resolve as resolve11 } from "path";
|
|
7868
7591
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7869
|
-
import { APP_NAME as APP_NAME3, getDataDir as
|
|
7592
|
+
import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
|
|
7870
7593
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7871
7594
|
var WorkspaceManager = class {
|
|
7872
7595
|
constructor(logo) {
|
|
@@ -7894,30 +7617,30 @@ var WorkspaceManager = class {
|
|
|
7894
7617
|
{ source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
|
|
7895
7618
|
];
|
|
7896
7619
|
for (const entry of templateFiles) {
|
|
7897
|
-
const filePath =
|
|
7898
|
-
if (!force &&
|
|
7620
|
+
const filePath = join7(workspace, entry.target);
|
|
7621
|
+
if (!force && existsSync11(filePath)) {
|
|
7899
7622
|
continue;
|
|
7900
7623
|
}
|
|
7901
|
-
const templatePath =
|
|
7902
|
-
if (!
|
|
7624
|
+
const templatePath = join7(templateDir, entry.source);
|
|
7625
|
+
if (!existsSync11(templatePath)) {
|
|
7903
7626
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
7904
7627
|
continue;
|
|
7905
7628
|
}
|
|
7906
|
-
const raw =
|
|
7629
|
+
const raw = readFileSync9(templatePath, "utf-8");
|
|
7907
7630
|
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
|
|
7908
|
-
|
|
7909
|
-
|
|
7631
|
+
mkdirSync6(dirname3(filePath), { recursive: true });
|
|
7632
|
+
writeFileSync5(filePath, content);
|
|
7910
7633
|
created.push(entry.target);
|
|
7911
7634
|
}
|
|
7912
|
-
const memoryDir =
|
|
7913
|
-
if (!
|
|
7914
|
-
|
|
7915
|
-
created.push(
|
|
7635
|
+
const memoryDir = join7(workspace, "memory");
|
|
7636
|
+
if (!existsSync11(memoryDir)) {
|
|
7637
|
+
mkdirSync6(memoryDir, { recursive: true });
|
|
7638
|
+
created.push(join7("memory", ""));
|
|
7916
7639
|
}
|
|
7917
|
-
const skillsDir =
|
|
7918
|
-
if (!
|
|
7919
|
-
|
|
7920
|
-
created.push(
|
|
7640
|
+
const skillsDir = join7(workspace, "skills");
|
|
7641
|
+
if (!existsSync11(skillsDir)) {
|
|
7642
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
7643
|
+
created.push(join7("skills", ""));
|
|
7921
7644
|
}
|
|
7922
7645
|
const seeded = this.seedBuiltinSkills(skillsDir, { force });
|
|
7923
7646
|
if (seeded > 0) {
|
|
@@ -7936,12 +7659,12 @@ var WorkspaceManager = class {
|
|
|
7936
7659
|
if (!entry.isDirectory()) {
|
|
7937
7660
|
continue;
|
|
7938
7661
|
}
|
|
7939
|
-
const src =
|
|
7940
|
-
if (!
|
|
7662
|
+
const src = join7(sourceDir, entry.name);
|
|
7663
|
+
if (!existsSync11(join7(src, "SKILL.md"))) {
|
|
7941
7664
|
continue;
|
|
7942
7665
|
}
|
|
7943
|
-
const dest =
|
|
7944
|
-
if (!force &&
|
|
7666
|
+
const dest = join7(targetDir, entry.name);
|
|
7667
|
+
if (!force && existsSync11(dest)) {
|
|
7945
7668
|
continue;
|
|
7946
7669
|
}
|
|
7947
7670
|
try {
|
|
@@ -7958,13 +7681,13 @@ var WorkspaceManager = class {
|
|
|
7958
7681
|
try {
|
|
7959
7682
|
const require3 = createRequire2(import.meta.url);
|
|
7960
7683
|
const entry = require3.resolve("@nextclaw/core");
|
|
7961
|
-
const pkgRoot = resolve11(
|
|
7962
|
-
const distSkills =
|
|
7963
|
-
if (
|
|
7684
|
+
const pkgRoot = resolve11(dirname3(entry), "..");
|
|
7685
|
+
const distSkills = join7(pkgRoot, "dist", "skills");
|
|
7686
|
+
if (existsSync11(distSkills)) {
|
|
7964
7687
|
return distSkills;
|
|
7965
7688
|
}
|
|
7966
|
-
const srcSkills =
|
|
7967
|
-
if (
|
|
7689
|
+
const srcSkills = join7(pkgRoot, "src", "agent", "skills");
|
|
7690
|
+
if (existsSync11(srcSkills)) {
|
|
7968
7691
|
return srcSkills;
|
|
7969
7692
|
}
|
|
7970
7693
|
return null;
|
|
@@ -7979,17 +7702,17 @@ var WorkspaceManager = class {
|
|
|
7979
7702
|
}
|
|
7980
7703
|
const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
|
|
7981
7704
|
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
7982
|
-
const candidates = [
|
|
7705
|
+
const candidates = [join7(pkgRoot, "templates")];
|
|
7983
7706
|
for (const candidate of candidates) {
|
|
7984
|
-
if (
|
|
7707
|
+
if (existsSync11(candidate)) {
|
|
7985
7708
|
return candidate;
|
|
7986
7709
|
}
|
|
7987
7710
|
}
|
|
7988
7711
|
return null;
|
|
7989
7712
|
}
|
|
7990
7713
|
getBridgeDir() {
|
|
7991
|
-
const userBridge =
|
|
7992
|
-
if (
|
|
7714
|
+
const userBridge = join7(getDataDir7(), "bridge");
|
|
7715
|
+
if (existsSync11(join7(userBridge, "dist", "index.js"))) {
|
|
7993
7716
|
return userBridge;
|
|
7994
7717
|
}
|
|
7995
7718
|
if (!which("npm")) {
|
|
@@ -7998,12 +7721,12 @@ var WorkspaceManager = class {
|
|
|
7998
7721
|
}
|
|
7999
7722
|
const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
|
|
8000
7723
|
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
8001
|
-
const pkgBridge =
|
|
8002
|
-
const srcBridge =
|
|
7724
|
+
const pkgBridge = join7(pkgRoot, "bridge");
|
|
7725
|
+
const srcBridge = join7(pkgRoot, "..", "..", "bridge");
|
|
8003
7726
|
let source = null;
|
|
8004
|
-
if (
|
|
7727
|
+
if (existsSync11(join7(pkgBridge, "package.json"))) {
|
|
8005
7728
|
source = pkgBridge;
|
|
8006
|
-
} else if (
|
|
7729
|
+
} else if (existsSync11(join7(srcBridge, "package.json"))) {
|
|
8007
7730
|
source = srcBridge;
|
|
8008
7731
|
}
|
|
8009
7732
|
if (!source) {
|
|
@@ -8011,8 +7734,8 @@ var WorkspaceManager = class {
|
|
|
8011
7734
|
process.exit(1);
|
|
8012
7735
|
}
|
|
8013
7736
|
console.log(`${this.logo} Setting up bridge...`);
|
|
8014
|
-
|
|
8015
|
-
if (
|
|
7737
|
+
mkdirSync6(resolve11(userBridge, ".."), { recursive: true });
|
|
7738
|
+
if (existsSync11(userBridge)) {
|
|
8016
7739
|
rmSync5(userBridge, { recursive: true, force: true });
|
|
8017
7740
|
}
|
|
8018
7741
|
cpSync3(source, userBridge, {
|
|
@@ -8063,7 +7786,6 @@ var CliRuntime = class {
|
|
|
8063
7786
|
pluginCommands;
|
|
8064
7787
|
channelCommands;
|
|
8065
7788
|
cronCommands;
|
|
8066
|
-
remoteCommands;
|
|
8067
7789
|
diagnosticsCommands;
|
|
8068
7790
|
constructor(options = {}) {
|
|
8069
7791
|
this.logo = options.logo ?? LOGO;
|
|
@@ -8085,7 +7807,6 @@ var CliRuntime = class {
|
|
|
8085
7807
|
requestRestart: (params) => this.requestRestart(params)
|
|
8086
7808
|
});
|
|
8087
7809
|
this.cronCommands = new CronCommands();
|
|
8088
|
-
this.remoteCommands = new RemoteCommands();
|
|
8089
7810
|
this.diagnosticsCommands = new DiagnosticsCommands({ logo: this.logo });
|
|
8090
7811
|
this.restartCoordinator = new RestartCoordinator({
|
|
8091
7812
|
readServiceState,
|
|
@@ -8151,7 +7872,7 @@ var CliRuntime = class {
|
|
|
8151
7872
|
const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
|
|
8152
7873
|
const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath4(new URL("./index.js", import.meta.url));
|
|
8153
7874
|
const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
|
|
8154
|
-
const serviceStatePath = resolve12(
|
|
7875
|
+
const serviceStatePath = resolve12(getDataDir8(), "run", "service.json");
|
|
8155
7876
|
const helperScript = [
|
|
8156
7877
|
'const { spawnSync } = require("node:child_process");',
|
|
8157
7878
|
'const { readFileSync } = require("node:fs");',
|
|
@@ -8279,18 +8000,18 @@ var CliRuntime = class {
|
|
|
8279
8000
|
const source = options.source ?? "init";
|
|
8280
8001
|
const prefix = options.auto ? "Auto init" : "Init";
|
|
8281
8002
|
const force = Boolean(options.force);
|
|
8282
|
-
const configPath =
|
|
8003
|
+
const configPath = getConfigPath4();
|
|
8283
8004
|
let createdConfig = false;
|
|
8284
|
-
if (!
|
|
8005
|
+
if (!existsSync12(configPath)) {
|
|
8285
8006
|
const config3 = ConfigSchema2.parse({});
|
|
8286
8007
|
saveConfig8(config3);
|
|
8287
8008
|
createdConfig = true;
|
|
8288
8009
|
}
|
|
8289
|
-
const config2 =
|
|
8010
|
+
const config2 = loadConfig11();
|
|
8290
8011
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
8291
|
-
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ?
|
|
8292
|
-
const workspaceExisted =
|
|
8293
|
-
|
|
8012
|
+
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join8(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
8013
|
+
const workspaceExisted = existsSync12(workspacePath);
|
|
8014
|
+
mkdirSync7(workspacePath, { recursive: true });
|
|
8294
8015
|
const templateResult = this.workspaceManager.createWorkspaceTemplates(
|
|
8295
8016
|
workspacePath,
|
|
8296
8017
|
{ force }
|
|
@@ -8321,8 +8042,8 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8321
8042
|
}
|
|
8322
8043
|
async login(opts = {}) {
|
|
8323
8044
|
await this.init({ source: "login", auto: true });
|
|
8324
|
-
const configPath =
|
|
8325
|
-
const config2 =
|
|
8045
|
+
const configPath = getConfigPath4();
|
|
8046
|
+
const config2 = loadConfig11(configPath);
|
|
8326
8047
|
const providers = config2.providers;
|
|
8327
8048
|
const nextclawProvider = providers.nextclaw ?? {
|
|
8328
8049
|
displayName: "",
|
|
@@ -8389,9 +8110,6 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8389
8110
|
console.log(`\u2713 Account: ${email} (${role})`);
|
|
8390
8111
|
console.log(`\u2713 Token saved into providers.nextclaw.apiKey`);
|
|
8391
8112
|
}
|
|
8392
|
-
async remoteConnect(opts = {}) {
|
|
8393
|
-
await this.remoteCommands.connect(opts);
|
|
8394
|
-
}
|
|
8395
8113
|
async gateway(opts) {
|
|
8396
8114
|
const uiOverrides = {
|
|
8397
8115
|
host: FORCED_PUBLIC_UI_HOST
|
|
@@ -8481,8 +8199,8 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8481
8199
|
await this.serviceCommands.stopService();
|
|
8482
8200
|
}
|
|
8483
8201
|
async agent(opts) {
|
|
8484
|
-
const configPath =
|
|
8485
|
-
const config2 = resolveConfigSecrets3(
|
|
8202
|
+
const configPath = getConfigPath4();
|
|
8203
|
+
const config2 = resolveConfigSecrets3(loadConfig11(), { configPath });
|
|
8486
8204
|
const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
|
|
8487
8205
|
const pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
8488
8206
|
const extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -8490,7 +8208,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8490
8208
|
const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
|
|
8491
8209
|
setPluginRuntimeBridge2({
|
|
8492
8210
|
loadConfig: () => toPluginConfigView(
|
|
8493
|
-
resolveConfigSecrets3(
|
|
8211
|
+
resolveConfigSecrets3(loadConfig11(), { configPath }),
|
|
8494
8212
|
pluginChannelBindings
|
|
8495
8213
|
),
|
|
8496
8214
|
writeConfigFile: async (nextConfigView) => {
|
|
@@ -8499,7 +8217,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8499
8217
|
"plugin runtime writeConfigFile expects an object config"
|
|
8500
8218
|
);
|
|
8501
8219
|
}
|
|
8502
|
-
const current =
|
|
8220
|
+
const current = loadConfig11();
|
|
8503
8221
|
const next = mergePluginConfigView(
|
|
8504
8222
|
current,
|
|
8505
8223
|
nextConfigView,
|
|
@@ -8531,7 +8249,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8531
8249
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
|
|
8532
8250
|
registry: pluginRegistry,
|
|
8533
8251
|
channel,
|
|
8534
|
-
cfg: resolveConfigSecrets3(
|
|
8252
|
+
cfg: resolveConfigSecrets3(loadConfig11(), { configPath }),
|
|
8535
8253
|
accountId
|
|
8536
8254
|
})
|
|
8537
8255
|
});
|
|
@@ -8550,10 +8268,10 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8550
8268
|
`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
|
|
8551
8269
|
`
|
|
8552
8270
|
);
|
|
8553
|
-
const historyFile =
|
|
8271
|
+
const historyFile = join8(getDataDir8(), "history", "cli_history");
|
|
8554
8272
|
const historyDir = resolve12(historyFile, "..");
|
|
8555
|
-
|
|
8556
|
-
const history =
|
|
8273
|
+
mkdirSync7(historyDir, { recursive: true });
|
|
8274
|
+
const history = existsSync12(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
8557
8275
|
const rl = createInterface2({
|
|
8558
8276
|
input: process.stdin,
|
|
8559
8277
|
output: process.stdout
|
|
@@ -8562,7 +8280,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8562
8280
|
const merged = history.concat(
|
|
8563
8281
|
rl.history ?? []
|
|
8564
8282
|
);
|
|
8565
|
-
|
|
8283
|
+
writeFileSync6(historyFile, merged.join("\n"));
|
|
8566
8284
|
process.exit(0);
|
|
8567
8285
|
});
|
|
8568
8286
|
let running = true;
|
|
@@ -8726,7 +8444,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8726
8444
|
await this.diagnosticsCommands.doctor(opts);
|
|
8727
8445
|
}
|
|
8728
8446
|
async skillsInstall(options) {
|
|
8729
|
-
const config2 =
|
|
8447
|
+
const config2 = loadConfig11();
|
|
8730
8448
|
const workdir = resolveSkillsInstallWorkdir({
|
|
8731
8449
|
explicitWorkdir: options.workdir,
|
|
8732
8450
|
configuredWorkspace: config2.agents.defaults.workspace
|
|
@@ -8794,8 +8512,6 @@ program.name(APP_NAME5).description(`${LOGO} ${APP_NAME5} - ${APP_TAGLINE}`).ver
|
|
|
8794
8512
|
program.command("onboard").description(`Initialize ${APP_NAME5} configuration and workspace`).action(async () => runtime.onboard());
|
|
8795
8513
|
program.command("init").description(`Initialize ${APP_NAME5} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
|
|
8796
8514
|
program.command("login").description("Login to NextClaw platform and save token into providers.nextclaw.apiKey").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--email <email>", "Login email").option("--password <password>", "Login password").option("--register", "Register first, then login", false).action(async (opts) => runtime.login(opts));
|
|
8797
|
-
var remote = program.command("remote").description("Manage remote access");
|
|
8798
|
-
remote.command("connect").description("Register this machine as a remote device and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:18791)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime.remoteConnect(opts));
|
|
8799
8515
|
program.command("gateway").description(`Start the ${APP_NAME5} gateway`).option("-p, --port <port>", "Gateway port", "18790").option("-v, --verbose", "Verbose output", false).option("--ui", "Enable UI server", false).option("--ui-port <port>", "UI port").option("--ui-open", "Open browser when UI starts", false).action(async (opts) => runtime.gateway(opts));
|
|
8800
8516
|
program.command("ui").description(`Start the ${APP_NAME5} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.ui(opts));
|
|
8801
8517
|
program.command("start").description(`Start the ${APP_NAME5} gateway + UI in the background`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after start", false).action(async (opts) => runtime.start(opts));
|