nextclaw 0.11.0 → 0.12.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 (35) hide show
  1. package/dist/cli/index.js +1002 -487
  2. package/package.json +4 -4
  3. package/ui-dist/assets/ChannelsList-C7F_As4r.js +1 -0
  4. package/ui-dist/assets/ChatPage-Oo7-OUsx.js +37 -0
  5. package/ui-dist/assets/{DocBrowser-DDX2HMXW.js → DocBrowser-Dsd8Dlq8.js} +1 -1
  6. package/ui-dist/assets/{LogoBadge-J53F_3JA.js → LogoBadge-2ChEc_oz.js} +1 -1
  7. package/ui-dist/assets/{MarketplacePage-0BZ4bza0.js → MarketplacePage-BXck6-X3.js} +3 -3
  8. package/ui-dist/assets/{ModelConfig-Wzq9wGHV.js → ModelConfig-CgHRSD0b.js} +1 -1
  9. package/ui-dist/assets/ProvidersList-PPfZucvS.js +1 -0
  10. package/ui-dist/assets/{RuntimeConfig-N771_AM6.js → RuntimeConfig-ClLEKNTN.js} +1 -1
  11. package/ui-dist/assets/{SearchConfig-DVt5QVa_.js → SearchConfig-CuXVCbrf.js} +1 -1
  12. package/ui-dist/assets/{SecretsConfig-CkwauPa8.js → SecretsConfig-udJz6Ake.js} +1 -1
  13. package/ui-dist/assets/{SessionsConfig-C3mnHzkZ.js → SessionsConfig-C1XnFfiC.js} +2 -2
  14. package/ui-dist/assets/{chat-message-pxr79GDs.js → chat-message-BETwXLD4.js} +1 -1
  15. package/ui-dist/assets/{index-GdpEEKnz.js → index-COJdlL0e.js} +1 -1
  16. package/ui-dist/assets/index-CsvP4CER.js +8 -0
  17. package/ui-dist/assets/index-D-bXl7qL.css +1 -0
  18. package/ui-dist/assets/{label-CmksBHgc.js → label-BGL-ztxh.js} +1 -1
  19. package/ui-dist/assets/{page-layout-Db0GbnhS.js → page-layout-aw88k7tG.js} +1 -1
  20. package/ui-dist/assets/popover-DyEvzhmV.js +1 -0
  21. package/ui-dist/assets/{security-config-CjLFME5Q.js → security-config-BuPAQn82.js} +1 -1
  22. package/ui-dist/assets/skeleton-drzO_tdU.js +1 -0
  23. package/ui-dist/assets/{switch-C24d-UJU.js → switch-BK8jIzto.js} +1 -1
  24. package/ui-dist/assets/tabs-custom-Da3cEOji.js +1 -0
  25. package/ui-dist/assets/{useConfirmDialog-BeP35LcG.js → useConfirmDialog-z0CE92iS.js} +1 -1
  26. package/ui-dist/assets/{vendor-psXJBy9u.js → vendor-CkJHmX1g.js} +1 -1
  27. package/ui-dist/index.html +3 -3
  28. package/ui-dist/assets/ChannelsList-DBcoVJRW.js +0 -1
  29. package/ui-dist/assets/ChatPage-CD3cxyyM.js +0 -37
  30. package/ui-dist/assets/ProvidersList-kwzRS8_M.js +0 -1
  31. package/ui-dist/assets/index-BIvFMkN4.js +0 -1
  32. package/ui-dist/assets/index-CzkY1reu.js +0 -8
  33. package/ui-dist/assets/index-RZ0kHHRI.css +0 -1
  34. package/ui-dist/assets/skeleton-CkpQeVWN.js +0 -1
  35. package/ui-dist/assets/tabs-custom-D89bh-fc.js +0 -1
package/dist/cli/index.js CHANGED
@@ -6,12 +6,12 @@ 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 loadConfig7,
9
+ loadConfig as loadConfig8,
10
10
  saveConfig as saveConfig6,
11
11
  getConfigPath as getConfigPath4,
12
12
  getDataDir as getDataDir8,
13
13
  ConfigSchema as ConfigSchema2,
14
- getWorkspacePath as getWorkspacePath7,
14
+ getWorkspacePath as getWorkspacePath9,
15
15
  expandHome as expandHome2,
16
16
  MessageBus as MessageBus2,
17
17
  AgentLoop,
@@ -22,12 +22,12 @@ import {
22
22
  DEFAULT_WORKSPACE_PATH
23
23
  } from "@nextclaw/core";
24
24
  import {
25
- getPluginChannelBindings as getPluginChannelBindings3,
25
+ getPluginChannelBindings as getPluginChannelBindings4,
26
26
  resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
27
27
  setPluginRuntimeBridge as setPluginRuntimeBridge2
28
28
  } from "@nextclaw/openclaw-compat";
29
29
  import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
30
- import { join as join7, resolve as resolve10 } from "path";
30
+ import { join as join7, 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";
@@ -115,32 +115,32 @@ function resolveRestartSentinelPath() {
115
115
  return resolve(getDataDir(), "run", RESTART_SENTINEL_FILENAME);
116
116
  }
117
117
  async function writeRestartSentinel(payload) {
118
- const path = resolveRestartSentinelPath();
119
- mkdirSync(resolve(path, ".."), { recursive: true });
118
+ const path2 = resolveRestartSentinelPath();
119
+ mkdirSync(resolve(path2, ".."), { recursive: true });
120
120
  const file = {
121
121
  version: 1,
122
122
  payload
123
123
  };
124
- writeFileSync(path, `${JSON.stringify(file, null, 2)}
124
+ writeFileSync(path2, `${JSON.stringify(file, null, 2)}
125
125
  `, "utf-8");
126
- return path;
126
+ return path2;
127
127
  }
128
128
  async function consumeRestartSentinel() {
129
- const path = resolveRestartSentinelPath();
130
- if (!existsSync(path)) {
129
+ const path2 = resolveRestartSentinelPath();
130
+ if (!existsSync(path2)) {
131
131
  return null;
132
132
  }
133
133
  try {
134
- const raw = readFileSync(path, "utf-8");
134
+ const raw = readFileSync(path2, "utf-8");
135
135
  const parsed = JSON.parse(raw);
136
136
  if (!parsed || parsed.version !== 1 || !parsed.payload) {
137
- rmSync(path, { force: true });
137
+ rmSync(path2, { force: true });
138
138
  return null;
139
139
  }
140
- rmSync(path, { force: true });
140
+ rmSync(path2, { force: true });
141
141
  return parsed;
142
142
  } catch {
143
- rmSync(path, { force: true });
143
+ rmSync(path2, { force: true });
144
144
  return null;
145
145
  }
146
146
  }
@@ -512,11 +512,11 @@ function collectRelativeFiles(rootDir) {
512
512
  };
513
513
  return walk(rootDir) ? output : null;
514
514
  }
515
- function normalizeMarketplaceRelativePath(path) {
516
- return path.replace(/\\/g, "/");
515
+ function normalizeMarketplaceRelativePath(path2) {
516
+ return path2.replace(/\\/g, "/");
517
517
  }
518
- function isIgnorableMarketplaceResidue(path) {
519
- return path === ".DS_Store";
518
+ function isIgnorableMarketplaceResidue(path2) {
519
+ return path2 === ".DS_Store";
520
520
  }
521
521
  async function publishMarketplaceSkill(options) {
522
522
  const skillDir = resolve3(options.skillDir);
@@ -673,10 +673,10 @@ async function fetchMarketplaceSkillFileBlob(apiBase, slug, file) {
673
673
  const arrayBuffer = await response.arrayBuffer();
674
674
  return Buffer.from(arrayBuffer);
675
675
  }
676
- function decodeMarketplaceFileContent(path, contentBase64) {
676
+ function decodeMarketplaceFileContent(path2, contentBase64) {
677
677
  const normalized = contentBase64.replace(/\s+/g, "");
678
678
  if (!normalized || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
679
- throw new Error(`Invalid marketplace file contentBase64 for path: ${path}`);
679
+ throw new Error(`Invalid marketplace file contentBase64 for path: ${path2}`);
680
680
  }
681
681
  return Buffer.from(normalized, "base64");
682
682
  }
@@ -806,26 +806,26 @@ function buildServeArgs(options) {
806
806
  return [cliPath, "serve", "--ui-port", String(options.uiPort)];
807
807
  }
808
808
  function readServiceState() {
809
- const path = resolveServiceStatePath();
810
- if (!existsSync4(path)) {
809
+ const path2 = resolveServiceStatePath();
810
+ if (!existsSync4(path2)) {
811
811
  return null;
812
812
  }
813
813
  try {
814
- const raw = readFileSync4(path, "utf-8");
814
+ const raw = readFileSync4(path2, "utf-8");
815
815
  return JSON.parse(raw);
816
816
  } catch {
817
817
  return null;
818
818
  }
819
819
  }
820
820
  function writeServiceState(state) {
821
- const path = resolveServiceStatePath();
822
- mkdirSync3(resolve4(path, ".."), { recursive: true });
823
- writeFileSync3(path, JSON.stringify(state, null, 2));
821
+ const path2 = resolveServiceStatePath();
822
+ mkdirSync3(resolve4(path2, ".."), { recursive: true });
823
+ writeFileSync3(path2, JSON.stringify(state, null, 2));
824
824
  }
825
825
  function clearServiceState() {
826
- const path = resolveServiceStatePath();
827
- if (existsSync4(path)) {
828
- rmSync3(path, { force: true });
826
+ const path2 = resolveServiceStatePath();
827
+ if (existsSync4(path2)) {
828
+ rmSync3(path2, { force: true });
829
829
  }
830
830
  }
831
831
  function resolveServiceStatePath() {
@@ -848,7 +848,7 @@ async function waitForExit(pid, timeoutMs) {
848
848
  if (!isProcessRunning(pid)) {
849
849
  return true;
850
850
  }
851
- await new Promise((resolve11) => setTimeout(resolve11, 200));
851
+ await new Promise((resolve13) => setTimeout(resolve13, 200));
852
852
  }
853
853
  return !isProcessRunning(pid);
854
854
  }
@@ -987,8 +987,8 @@ function printAgentResponse(response) {
987
987
  async function prompt(rl, question) {
988
988
  rl.setPrompt(question);
989
989
  rl.prompt();
990
- return new Promise((resolve11) => {
991
- rl.once("line", (line) => resolve11(line));
990
+ return new Promise((resolve13) => {
991
+ rl.once("line", (line) => resolve13(line));
992
992
  });
993
993
  }
994
994
 
@@ -1043,6 +1043,212 @@ function runSelfUpdate(options = {}) {
1043
1043
  }
1044
1044
 
1045
1045
  // src/cli/commands/plugins.ts
1046
+ import {
1047
+ buildPluginStatusReport as buildPluginStatusReport2,
1048
+ loadOpenClawPlugins,
1049
+ resolveUninstallDirectoryTargets
1050
+ } from "@nextclaw/openclaw-compat";
1051
+
1052
+ // src/cli/commands/plugin-command-utils.ts
1053
+ import { builtinProviderIds } from "@nextclaw/runtime";
1054
+ var RESERVED_PROVIDER_IDS = builtinProviderIds();
1055
+ var RESERVED_TOOL_NAMES = [
1056
+ "read_file",
1057
+ "write_file",
1058
+ "edit_file",
1059
+ "list_dir",
1060
+ "exec",
1061
+ "web_search",
1062
+ "web_fetch",
1063
+ "message",
1064
+ "spawn",
1065
+ "sessions_list",
1066
+ "sessions_history",
1067
+ "sessions_send",
1068
+ "memory_search",
1069
+ "memory_get",
1070
+ "subagents",
1071
+ "gateway",
1072
+ "cron"
1073
+ ];
1074
+ function buildReservedPluginLoadOptions() {
1075
+ return {
1076
+ reservedToolNames: [...RESERVED_TOOL_NAMES],
1077
+ reservedChannelIds: [],
1078
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
1079
+ reservedEngineKinds: ["native"],
1080
+ reservedNcpAgentRuntimeKinds: ["native"]
1081
+ };
1082
+ }
1083
+ function appendPluginCapabilityLines(lines, plugin) {
1084
+ if (plugin.toolNames.length > 0) {
1085
+ lines.push(`Tools: ${plugin.toolNames.join(", ")}`);
1086
+ }
1087
+ if (plugin.channelIds.length > 0) {
1088
+ lines.push(`Channels: ${plugin.channelIds.join(", ")}`);
1089
+ }
1090
+ if (plugin.providerIds.length > 0) {
1091
+ lines.push(`Providers: ${plugin.providerIds.join(", ")}`);
1092
+ }
1093
+ if (plugin.engineKinds.length > 0) {
1094
+ lines.push(`Engines: ${plugin.engineKinds.join(", ")}`);
1095
+ }
1096
+ if (plugin.ncpAgentRuntimeKinds.length > 0) {
1097
+ lines.push(`NCP runtimes: ${plugin.ncpAgentRuntimeKinds.join(", ")}`);
1098
+ }
1099
+ }
1100
+
1101
+ // src/cli/commands/dev-first-party-plugin-load-paths.ts
1102
+ import fs from "fs";
1103
+ import path from "path";
1104
+ var readJsonFile = (filePath) => {
1105
+ try {
1106
+ const raw = fs.readFileSync(filePath, "utf-8");
1107
+ const parsed = JSON.parse(raw);
1108
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
1109
+ } catch {
1110
+ return null;
1111
+ }
1112
+ };
1113
+ var readString = (value) => {
1114
+ if (typeof value !== "string") {
1115
+ return void 0;
1116
+ }
1117
+ const trimmed = value.trim();
1118
+ return trimmed || void 0;
1119
+ };
1120
+ var hasOpenClawExtensions = (pkg) => {
1121
+ const openclaw = pkg.openclaw;
1122
+ if (!openclaw || typeof openclaw !== "object" || Array.isArray(openclaw)) {
1123
+ return false;
1124
+ }
1125
+ const extensions = openclaw.extensions;
1126
+ return Array.isArray(extensions) && extensions.some((entry) => typeof entry === "string" && entry.trim().length > 0);
1127
+ };
1128
+ var normalizePackageSpec = (spec) => {
1129
+ const trimmed = spec.trim();
1130
+ if (!trimmed) {
1131
+ return void 0;
1132
+ }
1133
+ if (trimmed.startsWith("@")) {
1134
+ const slashIndex = trimmed.indexOf("/");
1135
+ if (slashIndex < 0) {
1136
+ return void 0;
1137
+ }
1138
+ const secondAtIndex = trimmed.indexOf("@", slashIndex + 1);
1139
+ return secondAtIndex < 0 ? trimmed : trimmed.slice(0, secondAtIndex);
1140
+ }
1141
+ const versionIndex = trimmed.indexOf("@");
1142
+ return versionIndex < 0 ? trimmed : trimmed.slice(0, versionIndex);
1143
+ };
1144
+ var readWorkspacePluginPackages = (workspaceExtensionsDir) => {
1145
+ if (!workspaceExtensionsDir.trim() || !fs.existsSync(workspaceExtensionsDir)) {
1146
+ return [];
1147
+ }
1148
+ const entries = fs.readdirSync(workspaceExtensionsDir, { withFileTypes: true });
1149
+ const packages = [];
1150
+ for (const entry of entries) {
1151
+ if (!entry.isDirectory()) {
1152
+ continue;
1153
+ }
1154
+ const packageDir = path.join(workspaceExtensionsDir, entry.name);
1155
+ const pkg = readJsonFile(path.join(packageDir, "package.json"));
1156
+ if (!pkg || !hasOpenClawExtensions(pkg)) {
1157
+ continue;
1158
+ }
1159
+ const packageName = readString(pkg.name);
1160
+ if (!packageName?.startsWith("@nextclaw/")) {
1161
+ continue;
1162
+ }
1163
+ packages.push({
1164
+ packageName,
1165
+ dir: packageDir
1166
+ });
1167
+ }
1168
+ return packages;
1169
+ };
1170
+ var resolveDevFirstPartyPluginLoadPaths = (config2, workspaceExtensionsDir) => {
1171
+ const rootDir = workspaceExtensionsDir?.trim();
1172
+ if (!rootDir) {
1173
+ return [];
1174
+ }
1175
+ const workspacePackages = readWorkspacePluginPackages(rootDir);
1176
+ if (workspacePackages.length === 0) {
1177
+ return [];
1178
+ }
1179
+ const packageDirByName = new Map(workspacePackages.map((entry) => [entry.packageName, entry.dir]));
1180
+ const loadPaths = [];
1181
+ const installs = config2.plugins.installs ?? {};
1182
+ for (const installRecord of Object.values(installs)) {
1183
+ const packageName = normalizePackageSpec(installRecord.spec ?? "");
1184
+ if (!packageName) {
1185
+ continue;
1186
+ }
1187
+ const packageDir = packageDirByName.get(packageName);
1188
+ if (!packageDir || loadPaths.includes(packageDir)) {
1189
+ continue;
1190
+ }
1191
+ loadPaths.push(packageDir);
1192
+ }
1193
+ return loadPaths;
1194
+ };
1195
+ var resolveDevFirstPartyPluginInstallRoots = (config2, workspaceExtensionsDir) => {
1196
+ const rootDir = workspaceExtensionsDir?.trim();
1197
+ if (!rootDir) {
1198
+ return [];
1199
+ }
1200
+ const workspacePackages = readWorkspacePluginPackages(rootDir);
1201
+ if (workspacePackages.length === 0) {
1202
+ return [];
1203
+ }
1204
+ const packageNames = new Set(workspacePackages.map((entry) => entry.packageName));
1205
+ const installRoots = [];
1206
+ for (const installRecord of Object.values(config2.plugins.installs ?? {})) {
1207
+ const packageName = normalizePackageSpec(installRecord.spec ?? "");
1208
+ if (!packageName || !packageNames.has(packageName)) {
1209
+ continue;
1210
+ }
1211
+ const installPath = readString(installRecord.installPath);
1212
+ if (!installPath || installRoots.includes(installPath)) {
1213
+ continue;
1214
+ }
1215
+ installRoots.push(installPath);
1216
+ }
1217
+ return installRoots;
1218
+ };
1219
+ var applyDevFirstPartyPluginLoadPaths = (config2, workspaceExtensionsDir) => {
1220
+ const devLoadPaths = resolveDevFirstPartyPluginLoadPaths(config2, workspaceExtensionsDir);
1221
+ if (devLoadPaths.length === 0) {
1222
+ return config2;
1223
+ }
1224
+ const existingLoadPaths = Array.isArray(config2.plugins.load?.paths) ? config2.plugins.load.paths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
1225
+ const mergedLoadPaths = [...devLoadPaths];
1226
+ for (const entry of existingLoadPaths) {
1227
+ if (!mergedLoadPaths.includes(entry)) {
1228
+ mergedLoadPaths.push(entry);
1229
+ }
1230
+ }
1231
+ return {
1232
+ ...config2,
1233
+ plugins: {
1234
+ ...config2.plugins,
1235
+ load: {
1236
+ ...config2.plugins.load,
1237
+ paths: mergedLoadPaths
1238
+ }
1239
+ }
1240
+ };
1241
+ };
1242
+
1243
+ // src/cli/commands/plugins.ts
1244
+ import {
1245
+ loadConfig as loadConfig2,
1246
+ getWorkspacePath as getWorkspacePath2
1247
+ } from "@nextclaw/core";
1248
+ import { createInterface } from "readline";
1249
+ import { resolve as resolve7 } from "path";
1250
+
1251
+ // src/cli/commands/plugin-mutation-actions.ts
1046
1252
  import {
1047
1253
  addPluginLoadPath,
1048
1254
  buildPluginStatusReport,
@@ -1050,48 +1256,203 @@ import {
1050
1256
  enablePluginInConfig,
1051
1257
  installPluginFromNpmSpec,
1052
1258
  installPluginFromPath,
1053
- loadOpenClawPlugins,
1054
1259
  recordPluginInstall,
1055
- resolveUninstallDirectoryTarget,
1056
1260
  uninstallPlugin
1057
1261
  } from "@nextclaw/openclaw-compat";
1058
- import {
1059
- loadConfig,
1060
- saveConfig,
1061
- getWorkspacePath,
1062
- expandHome
1063
- } from "@nextclaw/core";
1064
- import { builtinProviderIds } from "@nextclaw/runtime";
1065
- import { createInterface } from "readline";
1066
1262
  import { existsSync as existsSync5 } from "fs";
1067
1263
  import { resolve as resolve6 } from "path";
1068
- var RESERVED_PROVIDER_IDS = builtinProviderIds();
1264
+ import { expandHome, getWorkspacePath, loadConfig, saveConfig } from "@nextclaw/core";
1265
+ function resolveFileNpmSpecToLocalPath(raw) {
1266
+ const trimmed = raw.trim();
1267
+ if (!trimmed.toLowerCase().startsWith("file:")) {
1268
+ return null;
1269
+ }
1270
+ const rest = trimmed.slice("file:".length);
1271
+ if (!rest) {
1272
+ return { ok: false, error: "unsupported file: spec: missing path" };
1273
+ }
1274
+ if (rest.startsWith("///")) {
1275
+ return { ok: true, path: rest.slice(2) };
1276
+ }
1277
+ if (rest.startsWith("//localhost/")) {
1278
+ return { ok: true, path: rest.slice("//localhost".length) };
1279
+ }
1280
+ if (rest.startsWith("//")) {
1281
+ return {
1282
+ ok: false,
1283
+ error: 'unsupported file: URL host (expected "file:<path>" or "file:///abs/path")'
1284
+ };
1285
+ }
1286
+ return { ok: true, path: rest };
1287
+ }
1288
+ function looksLikePath(raw) {
1289
+ return raw.startsWith(".") || raw.startsWith("~") || raw.startsWith("/") || raw.endsWith(".ts") || raw.endsWith(".js") || raw.endsWith(".mjs") || raw.endsWith(".cjs") || raw.endsWith(".tgz") || raw.endsWith(".tar.gz") || raw.endsWith(".tar") || raw.endsWith(".zip");
1290
+ }
1291
+ function isArchivePath(filePath) {
1292
+ const lower = filePath.toLowerCase();
1293
+ return lower.endsWith(".zip") || lower.endsWith(".tgz") || lower.endsWith(".tar.gz") || lower.endsWith(".tar");
1294
+ }
1295
+ async function enablePluginMutation(id) {
1296
+ const config2 = loadConfig();
1297
+ const next = enablePluginInConfig(config2, id);
1298
+ saveConfig(next);
1299
+ return {
1300
+ message: `Enabled plugin "${id}".`
1301
+ };
1302
+ }
1303
+ async function disablePluginMutation(id) {
1304
+ const config2 = loadConfig();
1305
+ const next = disablePluginInConfig(config2, id);
1306
+ saveConfig(next);
1307
+ return {
1308
+ message: `Disabled plugin "${id}".`
1309
+ };
1310
+ }
1311
+ async function uninstallPluginMutation(id, opts = {}) {
1312
+ const config2 = loadConfig();
1313
+ const workspaceDir = getWorkspacePath(config2.agents.defaults.workspace);
1314
+ const report = buildPluginStatusReport({
1315
+ config: config2,
1316
+ workspaceDir,
1317
+ ...buildReservedPluginLoadOptions()
1318
+ });
1319
+ const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
1320
+ const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
1321
+ const pluginId = plugin?.id ?? id;
1322
+ const hasEntry = pluginId in (config2.plugins.entries ?? {});
1323
+ const hasInstall = pluginId in (config2.plugins.installs ?? {});
1324
+ if (!hasEntry && !hasInstall) {
1325
+ if (plugin) {
1326
+ throw new Error(
1327
+ `Plugin "${pluginId}" is not managed by plugins config/install records and cannot be uninstalled.`
1328
+ );
1329
+ }
1330
+ throw new Error(`Plugin not found: ${id}`);
1331
+ }
1332
+ const result = await uninstallPlugin({
1333
+ config: config2,
1334
+ pluginId,
1335
+ deleteFiles: !keepFiles
1336
+ });
1337
+ if (!result.ok) {
1338
+ throw new Error(result.error);
1339
+ }
1340
+ saveConfig(result.config);
1341
+ const removed = [];
1342
+ if (result.actions.entry) {
1343
+ removed.push("config entry");
1344
+ }
1345
+ if (result.actions.install) {
1346
+ removed.push("install record");
1347
+ }
1348
+ if (result.actions.allowlist) {
1349
+ removed.push("allowlist");
1350
+ }
1351
+ if (result.actions.loadPath) {
1352
+ removed.push("load path");
1353
+ }
1354
+ if (result.actions.directory) {
1355
+ removed.push("directory");
1356
+ }
1357
+ return {
1358
+ message: `Uninstalled plugin "${pluginId}". Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`,
1359
+ warnings: result.warnings
1360
+ };
1361
+ }
1362
+ async function installPluginMutation(pathOrSpec, opts = {}) {
1363
+ const fileSpec = resolveFileNpmSpecToLocalPath(pathOrSpec);
1364
+ if (fileSpec && !fileSpec.ok) {
1365
+ throw new Error(fileSpec.error);
1366
+ }
1367
+ const normalized = fileSpec && fileSpec.ok ? fileSpec.path : pathOrSpec;
1368
+ const resolved = resolve6(expandHome(normalized));
1369
+ const config2 = loadConfig();
1370
+ if (existsSync5(resolved)) {
1371
+ if (opts.link) {
1372
+ const probe = await installPluginFromPath({ path: resolved, dryRun: true });
1373
+ if (!probe.ok) {
1374
+ throw new Error(probe.error);
1375
+ }
1376
+ let next3 = addPluginLoadPath(config2, resolved);
1377
+ next3 = enablePluginInConfig(next3, probe.pluginId);
1378
+ next3 = recordPluginInstall(next3, {
1379
+ pluginId: probe.pluginId,
1380
+ source: "path",
1381
+ sourcePath: resolved,
1382
+ installPath: resolved,
1383
+ version: probe.version
1384
+ });
1385
+ saveConfig(next3);
1386
+ return {
1387
+ message: `Linked plugin path: ${resolved}`
1388
+ };
1389
+ }
1390
+ const result2 = await installPluginFromPath({
1391
+ path: resolved,
1392
+ logger: {
1393
+ info: (message) => console.log(message),
1394
+ warn: (message) => console.warn(message)
1395
+ }
1396
+ });
1397
+ if (!result2.ok) {
1398
+ throw new Error(result2.error);
1399
+ }
1400
+ let next2 = enablePluginInConfig(config2, result2.pluginId);
1401
+ next2 = recordPluginInstall(next2, {
1402
+ pluginId: result2.pluginId,
1403
+ source: isArchivePath(resolved) ? "archive" : "path",
1404
+ sourcePath: resolved,
1405
+ installPath: result2.targetDir,
1406
+ version: result2.version
1407
+ });
1408
+ saveConfig(next2);
1409
+ return {
1410
+ message: `Installed plugin: ${result2.pluginId}`
1411
+ };
1412
+ }
1413
+ if (opts.link) {
1414
+ throw new Error("`--link` requires a local path.");
1415
+ }
1416
+ if (looksLikePath(pathOrSpec)) {
1417
+ throw new Error(`Path not found: ${resolved}`);
1418
+ }
1419
+ const result = await installPluginFromNpmSpec({
1420
+ spec: pathOrSpec,
1421
+ logger: {
1422
+ info: (message) => console.log(message),
1423
+ warn: (message) => console.warn(message)
1424
+ }
1425
+ });
1426
+ if (!result.ok) {
1427
+ throw new Error(result.error);
1428
+ }
1429
+ let next = enablePluginInConfig(config2, result.pluginId);
1430
+ next = recordPluginInstall(next, {
1431
+ pluginId: result.pluginId,
1432
+ source: "npm",
1433
+ spec: pathOrSpec,
1434
+ installPath: result.targetDir,
1435
+ version: result.version
1436
+ });
1437
+ saveConfig(next);
1438
+ return {
1439
+ message: `Installed plugin: ${result.pluginId}`
1440
+ };
1441
+ }
1442
+
1443
+ // src/cli/commands/plugins.ts
1069
1444
  function loadPluginRegistry(config2, workspaceDir) {
1445
+ const workspaceExtensionsDir = process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR;
1446
+ const configWithDevPluginPaths = applyDevFirstPartyPluginLoadPaths(
1447
+ config2,
1448
+ workspaceExtensionsDir
1449
+ );
1450
+ const excludedRoots = resolveDevFirstPartyPluginInstallRoots(config2, workspaceExtensionsDir);
1070
1451
  return loadOpenClawPlugins({
1071
- config: config2,
1452
+ config: configWithDevPluginPaths,
1072
1453
  workspaceDir,
1073
- reservedToolNames: [
1074
- "read_file",
1075
- "write_file",
1076
- "edit_file",
1077
- "list_dir",
1078
- "exec",
1079
- "web_search",
1080
- "web_fetch",
1081
- "message",
1082
- "spawn",
1083
- "sessions_list",
1084
- "sessions_history",
1085
- "sessions_send",
1086
- "memory_search",
1087
- "memory_get",
1088
- "subagents",
1089
- "gateway",
1090
- "cron"
1091
- ],
1092
- reservedChannelIds: [],
1093
- reservedProviderIds: RESERVED_PROVIDER_IDS,
1094
- reservedEngineKinds: ["native"],
1454
+ excludeRoots: excludedRoots,
1455
+ ...buildReservedPluginLoadOptions(),
1095
1456
  logger: {
1096
1457
  info: (message) => console.log(message),
1097
1458
  warn: (message) => console.warn(message),
@@ -1120,6 +1481,13 @@ function toExtensionRegistry(pluginRegistry) {
1120
1481
  factory: engine.factory,
1121
1482
  source: engine.source
1122
1483
  })),
1484
+ ncpAgentRuntimes: pluginRegistry.ncpAgentRuntimes.map((runtime2) => ({
1485
+ pluginId: runtime2.pluginId,
1486
+ kind: runtime2.kind,
1487
+ label: runtime2.label,
1488
+ createRuntime: runtime2.createRuntime,
1489
+ source: runtime2.source
1490
+ })),
1123
1491
  diagnostics: pluginRegistry.diagnostics.map((diag) => ({
1124
1492
  level: diag.level,
1125
1493
  message: diag.message,
@@ -1178,15 +1546,25 @@ function mergePluginConfigView(baseConfig, pluginViewConfig, bindings) {
1178
1546
  var PluginCommands = class {
1179
1547
  constructor() {
1180
1548
  }
1549
+ async enablePlugin(id) {
1550
+ return await enablePluginMutation(id);
1551
+ }
1552
+ async disablePlugin(id) {
1553
+ return await disablePluginMutation(id);
1554
+ }
1555
+ async uninstallPlugin(id, opts = {}) {
1556
+ return await uninstallPluginMutation(id, opts);
1557
+ }
1558
+ async installPlugin(pathOrSpec, opts = {}) {
1559
+ return await installPluginMutation(pathOrSpec, opts);
1560
+ }
1181
1561
  pluginsList(opts = {}) {
1182
- const config2 = loadConfig();
1183
- const workspaceDir = getWorkspacePath(config2.agents.defaults.workspace);
1184
- const report = buildPluginStatusReport({
1562
+ const config2 = loadConfig2();
1563
+ const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
1564
+ const report = buildPluginStatusReport2({
1185
1565
  config: config2,
1186
1566
  workspaceDir,
1187
- reservedChannelIds: [],
1188
- reservedProviderIds: RESERVED_PROVIDER_IDS,
1189
- reservedEngineKinds: ["native"]
1567
+ ...buildReservedPluginLoadOptions()
1190
1568
  });
1191
1569
  const list = opts.enabled ? report.plugins.filter((plugin) => plugin.status === "loaded") : report.plugins;
1192
1570
  if (opts.json) {
@@ -1221,17 +1599,10 @@ var PluginCommands = class {
1221
1599
  if (plugin.version) {
1222
1600
  console.log(` version: ${plugin.version}`);
1223
1601
  }
1224
- if (plugin.toolNames.length > 0) {
1225
- console.log(` tools: ${plugin.toolNames.join(", ")}`);
1226
- }
1227
- if (plugin.channelIds.length > 0) {
1228
- console.log(` channels: ${plugin.channelIds.join(", ")}`);
1229
- }
1230
- if (plugin.providerIds.length > 0) {
1231
- console.log(` providers: ${plugin.providerIds.join(", ")}`);
1232
- }
1233
- if (plugin.engineKinds.length > 0) {
1234
- console.log(` engines: ${plugin.engineKinds.join(", ")}`);
1602
+ const capabilityLines = [];
1603
+ appendPluginCapabilityLines(capabilityLines, plugin);
1604
+ for (const line of capabilityLines) {
1605
+ console.log(` ${line.toLowerCase()}`);
1235
1606
  }
1236
1607
  if (plugin.error) {
1237
1608
  console.log(` error: ${plugin.error}`);
@@ -1240,14 +1611,12 @@ var PluginCommands = class {
1240
1611
  }
1241
1612
  }
1242
1613
  pluginsInfo(id, opts = {}) {
1243
- const config2 = loadConfig();
1244
- const workspaceDir = getWorkspacePath(config2.agents.defaults.workspace);
1245
- const report = buildPluginStatusReport({
1614
+ const config2 = loadConfig2();
1615
+ const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
1616
+ const report = buildPluginStatusReport2({
1246
1617
  config: config2,
1247
1618
  workspaceDir,
1248
- reservedChannelIds: [],
1249
- reservedProviderIds: RESERVED_PROVIDER_IDS,
1250
- reservedEngineKinds: ["native"]
1619
+ ...buildReservedPluginLoadOptions()
1251
1620
  });
1252
1621
  const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
1253
1622
  if (!plugin) {
@@ -1274,18 +1643,7 @@ var PluginCommands = class {
1274
1643
  if (plugin.version) {
1275
1644
  lines.push(`Version: ${plugin.version}`);
1276
1645
  }
1277
- if (plugin.toolNames.length > 0) {
1278
- lines.push(`Tools: ${plugin.toolNames.join(", ")}`);
1279
- }
1280
- if (plugin.channelIds.length > 0) {
1281
- lines.push(`Channels: ${plugin.channelIds.join(", ")}`);
1282
- }
1283
- if (plugin.providerIds.length > 0) {
1284
- lines.push(`Providers: ${plugin.providerIds.join(", ")}`);
1285
- }
1286
- if (plugin.engineKinds.length > 0) {
1287
- lines.push(`Engines: ${plugin.engineKinds.join(", ")}`);
1288
- }
1646
+ appendPluginCapabilityLines(lines, plugin);
1289
1647
  if (plugin.error) {
1290
1648
  lines.push(`Error: ${plugin.error}`);
1291
1649
  }
@@ -1311,33 +1669,37 @@ var PluginCommands = class {
1311
1669
  console.log(lines.join("\n"));
1312
1670
  }
1313
1671
  async pluginsEnable(id) {
1314
- const config2 = loadConfig();
1315
- const next = enablePluginInConfig(config2, id);
1316
- saveConfig(next);
1317
- console.log(`Enabled plugin "${id}".`);
1672
+ try {
1673
+ const result = await this.enablePlugin(id);
1674
+ console.log(result.message);
1675
+ } catch (error) {
1676
+ console.error(error instanceof Error ? error.message : String(error));
1677
+ process.exit(1);
1678
+ }
1318
1679
  console.log("If gateway is running, plugin changes are hot-applied automatically.");
1319
1680
  }
1320
1681
  async pluginsDisable(id) {
1321
- const config2 = loadConfig();
1322
- const next = disablePluginInConfig(config2, id);
1323
- saveConfig(next);
1324
- console.log(`Disabled plugin "${id}".`);
1682
+ try {
1683
+ const result = await this.disablePlugin(id);
1684
+ console.log(result.message);
1685
+ } catch (error) {
1686
+ console.error(error instanceof Error ? error.message : String(error));
1687
+ process.exit(1);
1688
+ }
1325
1689
  console.log("If gateway is running, plugin changes are hot-applied automatically.");
1326
1690
  }
1327
1691
  async pluginsUninstall(id, opts = {}) {
1328
- const config2 = loadConfig();
1329
- const workspaceDir = getWorkspacePath(config2.agents.defaults.workspace);
1330
- const report = buildPluginStatusReport({
1692
+ if (opts.keepConfig) {
1693
+ console.log("`--keep-config` is deprecated, use `--keep-files`.");
1694
+ }
1695
+ const config2 = loadConfig2();
1696
+ const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
1697
+ const report = buildPluginStatusReport2({
1331
1698
  config: config2,
1332
1699
  workspaceDir,
1333
- reservedChannelIds: [],
1334
- reservedProviderIds: RESERVED_PROVIDER_IDS,
1335
- reservedEngineKinds: ["native"]
1700
+ ...buildReservedPluginLoadOptions()
1336
1701
  });
1337
1702
  const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
1338
- if (opts.keepConfig) {
1339
- console.log("`--keep-config` is deprecated, use `--keep-files`.");
1340
- }
1341
1703
  const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
1342
1704
  const pluginId = plugin?.id ?? id;
1343
1705
  const hasEntry = pluginId in (config2.plugins.entries ?? {});
@@ -1353,7 +1715,7 @@ var PluginCommands = class {
1353
1715
  process.exit(1);
1354
1716
  }
1355
1717
  const install = config2.plugins.installs?.[pluginId];
1356
- const isLinked = install?.source === "path" && (!install.installPath || !install.sourcePath || resolve6(install.installPath) === resolve6(install.sourcePath));
1718
+ const isLinked = install?.source === "path" && (!install.installPath || !install.sourcePath || resolve7(install.installPath) === resolve7(install.sourcePath));
1357
1719
  const preview = [];
1358
1720
  if (hasEntry) {
1359
1721
  preview.push("config entry");
@@ -1367,12 +1729,13 @@ var PluginCommands = class {
1367
1729
  if (isLinked && install?.sourcePath && config2.plugins.load?.paths?.includes(install.sourcePath)) {
1368
1730
  preview.push("load path");
1369
1731
  }
1370
- const deleteTarget = !keepFiles ? resolveUninstallDirectoryTarget({
1732
+ const deleteTargets = !keepFiles ? resolveUninstallDirectoryTargets({
1733
+ config: config2,
1371
1734
  pluginId,
1372
1735
  hasInstall,
1373
1736
  installRecord: install
1374
- }) : null;
1375
- if (deleteTarget) {
1737
+ }) : [];
1738
+ for (const deleteTarget of deleteTargets) {
1376
1739
  preview.push(`directory: ${deleteTarget}`);
1377
1740
  }
1378
1741
  const pluginName = plugin?.name || pluginId;
@@ -1390,132 +1753,35 @@ var PluginCommands = class {
1390
1753
  return;
1391
1754
  }
1392
1755
  }
1393
- const result = await uninstallPlugin({
1394
- config: config2,
1395
- pluginId,
1396
- deleteFiles: !keepFiles
1397
- });
1398
- if (!result.ok) {
1399
- console.error(result.error);
1756
+ try {
1757
+ const result = await this.uninstallPlugin(id, opts);
1758
+ for (const warning of result.warnings) {
1759
+ console.warn(warning);
1760
+ }
1761
+ console.log(result.message);
1762
+ } catch (error) {
1763
+ console.error(error instanceof Error ? error.message : String(error));
1400
1764
  process.exit(1);
1401
1765
  }
1402
- for (const warning of result.warnings) {
1403
- console.warn(warning);
1404
- }
1405
- saveConfig(result.config);
1406
- const removed = [];
1407
- if (result.actions.entry) {
1408
- removed.push("config entry");
1409
- }
1410
- if (result.actions.install) {
1411
- removed.push("install record");
1412
- }
1413
- if (result.actions.allowlist) {
1414
- removed.push("allowlist");
1415
- }
1416
- if (result.actions.loadPath) {
1417
- removed.push("load path");
1418
- }
1419
- if (result.actions.directory) {
1420
- removed.push("directory");
1421
- }
1422
- console.log(`Uninstalled plugin "${pluginId}". Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`);
1423
1766
  console.log("If gateway is running, plugin changes are hot-applied automatically.");
1424
1767
  }
1425
1768
  async pluginsInstall(pathOrSpec, opts = {}) {
1426
- const fileSpec = this.resolveFileNpmSpecToLocalPath(pathOrSpec);
1427
- if (fileSpec && !fileSpec.ok) {
1428
- console.error(fileSpec.error);
1429
- process.exit(1);
1430
- }
1431
- const normalized = fileSpec && fileSpec.ok ? fileSpec.path : pathOrSpec;
1432
- const resolved = resolve6(expandHome(normalized));
1433
- const config2 = loadConfig();
1434
- if (existsSync5(resolved)) {
1435
- if (opts.link) {
1436
- const probe = await installPluginFromPath({ path: resolved, dryRun: true });
1437
- if (!probe.ok) {
1438
- console.error(probe.error);
1439
- process.exit(1);
1440
- }
1441
- let next3 = addPluginLoadPath(config2, resolved);
1442
- next3 = enablePluginInConfig(next3, probe.pluginId);
1443
- next3 = recordPluginInstall(next3, {
1444
- pluginId: probe.pluginId,
1445
- source: "path",
1446
- sourcePath: resolved,
1447
- installPath: resolved,
1448
- version: probe.version
1449
- });
1450
- saveConfig(next3);
1451
- console.log(`Linked plugin path: ${resolved}`);
1452
- console.log("If gateway is running, plugin changes are hot-applied automatically.");
1453
- return;
1454
- }
1455
- const result2 = await installPluginFromPath({
1456
- path: resolved,
1457
- logger: {
1458
- info: (message) => console.log(message),
1459
- warn: (message) => console.warn(message)
1460
- }
1461
- });
1462
- if (!result2.ok) {
1463
- console.error(result2.error);
1464
- process.exit(1);
1465
- }
1466
- let next2 = enablePluginInConfig(config2, result2.pluginId);
1467
- next2 = recordPluginInstall(next2, {
1468
- pluginId: result2.pluginId,
1469
- source: this.isArchivePath(resolved) ? "archive" : "path",
1470
- sourcePath: resolved,
1471
- installPath: result2.targetDir,
1472
- version: result2.version
1473
- });
1474
- saveConfig(next2);
1475
- console.log(`Installed plugin: ${result2.pluginId}`);
1476
- console.log("If gateway is running, plugin changes are hot-applied automatically.");
1477
- return;
1478
- }
1479
- if (opts.link) {
1480
- console.error("`--link` requires a local path.");
1481
- process.exit(1);
1482
- }
1483
- if (this.looksLikePath(pathOrSpec)) {
1484
- console.error(`Path not found: ${resolved}`);
1485
- process.exit(1);
1486
- }
1487
- const result = await installPluginFromNpmSpec({
1488
- spec: pathOrSpec,
1489
- logger: {
1490
- info: (message) => console.log(message),
1491
- warn: (message) => console.warn(message)
1492
- }
1493
- });
1494
- if (!result.ok) {
1495
- console.error(result.error);
1769
+ try {
1770
+ const result = await this.installPlugin(pathOrSpec, opts);
1771
+ console.log(result.message);
1772
+ } catch (error) {
1773
+ console.error(error instanceof Error ? error.message : String(error));
1496
1774
  process.exit(1);
1497
1775
  }
1498
- let next = enablePluginInConfig(config2, result.pluginId);
1499
- next = recordPluginInstall(next, {
1500
- pluginId: result.pluginId,
1501
- source: "npm",
1502
- spec: pathOrSpec,
1503
- installPath: result.targetDir,
1504
- version: result.version
1505
- });
1506
- saveConfig(next);
1507
- console.log(`Installed plugin: ${result.pluginId}`);
1508
1776
  console.log("If gateway is running, plugin changes are hot-applied automatically.");
1509
1777
  }
1510
1778
  pluginsDoctor() {
1511
- const config2 = loadConfig();
1512
- const workspaceDir = getWorkspacePath(config2.agents.defaults.workspace);
1513
- const report = buildPluginStatusReport({
1779
+ const config2 = loadConfig2();
1780
+ const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
1781
+ const report = buildPluginStatusReport2({
1514
1782
  config: config2,
1515
1783
  workspaceDir,
1516
- reservedChannelIds: [],
1517
- reservedProviderIds: RESERVED_PROVIDER_IDS,
1518
- reservedEngineKinds: ["native"]
1784
+ ...buildReservedPluginLoadOptions()
1519
1785
  });
1520
1786
  const pluginErrors = report.plugins.filter((plugin) => plugin.status === "error");
1521
1787
  const diagnostics = report.diagnostics.filter((diag) => diag.level === "error");
@@ -1545,47 +1811,17 @@ var PluginCommands = class {
1545
1811
  input: process.stdin,
1546
1812
  output: process.stdout
1547
1813
  });
1548
- const answer = await new Promise((resolve11) => {
1549
- rl.question(`${question} [y/N] `, (line) => resolve11(line));
1814
+ const answer = await new Promise((resolve13) => {
1815
+ rl.question(`${question} [y/N] `, (line) => resolve13(line));
1550
1816
  });
1551
1817
  rl.close();
1552
1818
  const normalized = answer.trim().toLowerCase();
1553
1819
  return normalized === "y" || normalized === "yes";
1554
1820
  }
1555
- resolveFileNpmSpecToLocalPath(raw) {
1556
- const trimmed = raw.trim();
1557
- if (!trimmed.toLowerCase().startsWith("file:")) {
1558
- return null;
1559
- }
1560
- const rest = trimmed.slice("file:".length);
1561
- if (!rest) {
1562
- return { ok: false, error: "unsupported file: spec: missing path" };
1563
- }
1564
- if (rest.startsWith("///")) {
1565
- return { ok: true, path: rest.slice(2) };
1566
- }
1567
- if (rest.startsWith("//localhost/")) {
1568
- return { ok: true, path: rest.slice("//localhost".length) };
1569
- }
1570
- if (rest.startsWith("//")) {
1571
- return {
1572
- ok: false,
1573
- error: 'unsupported file: URL host (expected "file:<path>" or "file:///abs/path")'
1574
- };
1575
- }
1576
- return { ok: true, path: rest };
1577
- }
1578
- looksLikePath(raw) {
1579
- return raw.startsWith(".") || raw.startsWith("~") || raw.startsWith("/") || raw.endsWith(".ts") || raw.endsWith(".js") || raw.endsWith(".mjs") || raw.endsWith(".cjs") || raw.endsWith(".tgz") || raw.endsWith(".tar.gz") || raw.endsWith(".tar") || raw.endsWith(".zip");
1580
- }
1581
- isArchivePath(filePath) {
1582
- const lower = filePath.toLowerCase();
1583
- return lower.endsWith(".zip") || lower.endsWith(".tgz") || lower.endsWith(".tar.gz") || lower.endsWith(".tar");
1584
- }
1585
1821
  };
1586
1822
 
1587
1823
  // src/cli/commands/config.ts
1588
- import { buildReloadPlan, diffConfigPaths, loadConfig as loadConfig2, saveConfig as saveConfig2 } from "@nextclaw/core";
1824
+ import { buildReloadPlan, diffConfigPaths, loadConfig as loadConfig3, saveConfig as saveConfig2 } from "@nextclaw/core";
1589
1825
 
1590
1826
  // src/cli/config-path.ts
1591
1827
  function isIndexSegment(raw) {
@@ -1780,7 +2016,7 @@ var ConfigCommands = class {
1780
2016
  this.deps = deps;
1781
2017
  }
1782
2018
  configGet(pathExpr, opts = {}) {
1783
- const config2 = loadConfig2();
2019
+ const config2 = loadConfig3();
1784
2020
  let parsedPath;
1785
2021
  try {
1786
2022
  parsedPath = parseRequiredConfigPath(pathExpr);
@@ -1822,7 +2058,7 @@ var ConfigCommands = class {
1822
2058
  process.exit(1);
1823
2059
  return;
1824
2060
  }
1825
- const prevConfig = loadConfig2();
2061
+ const prevConfig = loadConfig3();
1826
2062
  const nextConfig = structuredClone(prevConfig);
1827
2063
  try {
1828
2064
  setAtConfigPath(nextConfig, parsedPath, parsedValue);
@@ -1848,7 +2084,7 @@ var ConfigCommands = class {
1848
2084
  process.exit(1);
1849
2085
  return;
1850
2086
  }
1851
- const prevConfig = loadConfig2();
2087
+ const prevConfig = loadConfig3();
1852
2088
  const nextConfig = structuredClone(prevConfig);
1853
2089
  const removed = unsetAtConfigPath(nextConfig, parsedPath);
1854
2090
  if (!removed) {
@@ -1886,7 +2122,7 @@ import {
1886
2122
  buildReloadPlan as buildReloadPlan2,
1887
2123
  diffConfigPaths as diffConfigPaths2,
1888
2124
  getConfigPath,
1889
- loadConfig as loadConfig3,
2125
+ loadConfig as loadConfig4,
1890
2126
  resolveConfigSecrets,
1891
2127
  saveConfig as saveConfig3
1892
2128
  } from "@nextclaw/core";
@@ -1939,17 +2175,17 @@ function parseRefsPatch(raw) {
1939
2175
  throw new Error("refs patch must be an object");
1940
2176
  }
1941
2177
  const output = {};
1942
- for (const [path, value] of Object.entries(raw)) {
2178
+ for (const [path2, value] of Object.entries(raw)) {
1943
2179
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1944
- throw new Error(`invalid ref for ${path}`);
2180
+ throw new Error(`invalid ref for ${path2}`);
1945
2181
  }
1946
2182
  const source = normalizeSecretSource(value.source);
1947
2183
  const id = normalizeOptionalString(value.id);
1948
2184
  const provider = normalizeOptionalString(value.provider);
1949
2185
  if (!source || !id) {
1950
- throw new Error(`invalid ref for ${path}: source/id is required`);
2186
+ throw new Error(`invalid ref for ${path2}: source/id is required`);
1951
2187
  }
1952
- output[path] = { source, ...provider ? { provider } : {}, id };
2188
+ output[path2] = { source, ...provider ? { provider } : {}, id };
1953
2189
  }
1954
2190
  return output;
1955
2191
  }
@@ -1982,21 +2218,21 @@ var SecretsCommands = class {
1982
2218
  this.deps = deps;
1983
2219
  }
1984
2220
  secretsAudit(opts = {}) {
1985
- const config2 = loadConfig3();
2221
+ const config2 = loadConfig4();
1986
2222
  const configPath = getConfigPath();
1987
2223
  const refs = config2.secrets.refs;
1988
2224
  const items = [];
1989
- for (const [path, ref] of Object.entries(refs)) {
2225
+ for (const [path2, ref] of Object.entries(refs)) {
1990
2226
  const provider = inferProviderAlias(config2, ref);
1991
2227
  const scopedConfig = structuredClone(config2);
1992
- scopedConfig.secrets.refs = { [path]: ref };
2228
+ scopedConfig.secrets.refs = { [path2]: ref };
1993
2229
  try {
1994
2230
  const resolved = resolveConfigSecrets(scopedConfig, { configPath });
1995
- const parsedPath = parseRequiredConfigPath(path);
2231
+ const parsedPath = parseRequiredConfigPath(path2);
1996
2232
  const target = getAtConfigPath(resolved, parsedPath);
1997
2233
  if (!target.found) {
1998
2234
  items.push({
1999
- path,
2235
+ path: path2,
2000
2236
  source: ref.source,
2001
2237
  provider,
2002
2238
  id: ref.id,
@@ -2008,7 +2244,7 @@ var SecretsCommands = class {
2008
2244
  const resolvedValue = target.value;
2009
2245
  const detail = typeof resolvedValue === "string" ? `resolved (length=${resolvedValue.length})` : `resolved (${typeof resolvedValue})`;
2010
2246
  items.push({
2011
- path,
2247
+ path: path2,
2012
2248
  source: ref.source,
2013
2249
  provider,
2014
2250
  id: ref.id,
@@ -2017,7 +2253,7 @@ var SecretsCommands = class {
2017
2253
  });
2018
2254
  } catch (error) {
2019
2255
  items.push({
2020
- path,
2256
+ path: path2,
2021
2257
  source: ref.source,
2022
2258
  provider,
2023
2259
  id: ref.id,
@@ -2050,7 +2286,7 @@ var SecretsCommands = class {
2050
2286
  if (!alias) {
2051
2287
  throw new Error("provider alias is required");
2052
2288
  }
2053
- const prevConfig = loadConfig3();
2289
+ const prevConfig = loadConfig4();
2054
2290
  const nextConfig = structuredClone(prevConfig);
2055
2291
  const remove = Boolean(opts.remove);
2056
2292
  if (remove) {
@@ -2071,13 +2307,13 @@ var SecretsCommands = class {
2071
2307
  ...normalizeOptionalString(opts.prefix) ? { prefix: normalizeOptionalString(opts.prefix) } : {}
2072
2308
  };
2073
2309
  } else if (source === "file") {
2074
- const path = normalizeOptionalString(opts.path);
2075
- if (!path) {
2310
+ const path2 = normalizeOptionalString(opts.path);
2311
+ if (!path2) {
2076
2312
  throw new Error("file source requires --path");
2077
2313
  }
2078
2314
  nextConfig.secrets.providers[alias] = {
2079
2315
  source,
2080
- path,
2316
+ path: path2,
2081
2317
  format: "json"
2082
2318
  };
2083
2319
  } else {
@@ -2112,7 +2348,7 @@ var SecretsCommands = class {
2112
2348
  console.log(`Secrets provider ${remove ? "removed" : "configured"}: ${alias}`);
2113
2349
  }
2114
2350
  async secretsApply(opts) {
2115
- const prevConfig = loadConfig3();
2351
+ const prevConfig = loadConfig4();
2116
2352
  const nextConfig = structuredClone(prevConfig);
2117
2353
  if (opts.enable && opts.disable) {
2118
2354
  throw new Error("cannot set --enable and --disable at the same time");
@@ -2140,12 +2376,12 @@ var SecretsCommands = class {
2140
2376
  }
2141
2377
  }
2142
2378
  if (opts.path) {
2143
- const path = opts.path.trim();
2144
- if (!path) {
2379
+ const path2 = opts.path.trim();
2380
+ if (!path2) {
2145
2381
  throw new Error("path is empty");
2146
2382
  }
2147
2383
  if (opts.remove) {
2148
- delete nextConfig.secrets.refs[path];
2384
+ delete nextConfig.secrets.refs[path2];
2149
2385
  } else {
2150
2386
  const source = normalizeSecretSource(opts.source);
2151
2387
  const id = normalizeOptionalString(opts.id);
@@ -2153,7 +2389,7 @@ var SecretsCommands = class {
2153
2389
  throw new Error("apply single ref requires --source and --id");
2154
2390
  }
2155
2391
  const provider = normalizeOptionalString(opts.provider);
2156
- nextConfig.secrets.refs[path] = {
2392
+ nextConfig.secrets.refs[path2] = {
2157
2393
  source,
2158
2394
  id,
2159
2395
  ...provider ? { provider } : {}
@@ -2177,7 +2413,7 @@ var SecretsCommands = class {
2177
2413
  console.log("Secrets applied.");
2178
2414
  }
2179
2415
  async secretsReload(opts = {}) {
2180
- const config2 = loadConfig3();
2416
+ const config2 = loadConfig4();
2181
2417
  const configPath = getConfigPath();
2182
2418
  resolveConfigSecrets(config2, { configPath });
2183
2419
  saveConfig3(config2);
@@ -2205,9 +2441,9 @@ var SecretsCommands = class {
2205
2441
 
2206
2442
  // src/cli/commands/channels.ts
2207
2443
  import { spawnSync as spawnSync2 } from "child_process";
2208
- import { getWorkspacePath as getWorkspacePath2, loadConfig as loadConfig4, saveConfig as saveConfig4 } from "@nextclaw/core";
2444
+ import { getWorkspacePath as getWorkspacePath3, loadConfig as loadConfig5, saveConfig as saveConfig4 } from "@nextclaw/core";
2209
2445
  import { BUILTIN_CHANNEL_PLUGIN_IDS, builtinProviderIds as builtinProviderIds2 } from "@nextclaw/runtime";
2210
- import { buildPluginStatusReport as buildPluginStatusReport2, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
2446
+ import { buildPluginStatusReport as buildPluginStatusReport3, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
2211
2447
  var CHANNEL_LABELS = {
2212
2448
  telegram: "Telegram",
2213
2449
  whatsapp: "WhatsApp",
@@ -2226,7 +2462,7 @@ var ChannelCommands = class {
2226
2462
  this.deps = deps;
2227
2463
  }
2228
2464
  channelsStatus() {
2229
- const config2 = loadConfig4();
2465
+ const config2 = loadConfig5();
2230
2466
  console.log("Channel Status");
2231
2467
  const channelConfig = config2.channels;
2232
2468
  for (const channelId of BUILTIN_CHANNEL_PLUGIN_IDS) {
@@ -2234,8 +2470,8 @@ var ChannelCommands = class {
2234
2470
  const enabled = channelConfig[channelId]?.enabled === true;
2235
2471
  console.log(`${label}: ${enabled ? "\u2713" : "\u2717"}`);
2236
2472
  }
2237
- const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
2238
- const report = buildPluginStatusReport2({
2473
+ const workspaceDir = getWorkspacePath3(config2.agents.defaults.workspace);
2474
+ const report = buildPluginStatusReport3({
2239
2475
  config: config2,
2240
2476
  workspaceDir,
2241
2477
  reservedChannelIds: [],
@@ -2265,8 +2501,8 @@ var ChannelCommands = class {
2265
2501
  console.error("--channel is required");
2266
2502
  process.exit(1);
2267
2503
  }
2268
- const config2 = loadConfig4();
2269
- const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
2504
+ const config2 = loadConfig5();
2505
+ const workspaceDir = getWorkspacePath3(config2.agents.defaults.workspace);
2270
2506
  const pluginRegistry = loadPluginRegistry(config2, workspaceDir);
2271
2507
  const bindings = getPluginChannelBindings(pluginRegistry);
2272
2508
  const binding = bindings.find((entry) => entry.channelId === channelId || entry.pluginId === channelId);
@@ -2396,14 +2632,14 @@ var CronCommands = class {
2396
2632
  // src/cli/commands/diagnostics.ts
2397
2633
  import { createServer as createNetServer } from "net";
2398
2634
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
2399
- import { resolve as resolve7 } from "path";
2635
+ import { resolve as resolve8 } from "path";
2400
2636
  import {
2401
2637
  APP_NAME,
2402
2638
  getConfigPath as getConfigPath2,
2403
2639
  getDataDir as getDataDir4,
2404
- getWorkspacePath as getWorkspacePath3,
2640
+ getWorkspacePath as getWorkspacePath4,
2405
2641
  hasSecretRef,
2406
- loadConfig as loadConfig5
2642
+ loadConfig as loadConfig6
2407
2643
  } from "@nextclaw/core";
2408
2644
  import { listBuiltinProviders } from "@nextclaw/runtime";
2409
2645
  var DiagnosticsCommands = class {
@@ -2565,9 +2801,9 @@ var DiagnosticsCommands = class {
2565
2801
  }
2566
2802
  async collectRuntimeStatus(params) {
2567
2803
  const configPath = getConfigPath2();
2568
- const config2 = loadConfig5();
2569
- const workspacePath = getWorkspacePath3(config2.agents.defaults.workspace);
2570
- const serviceStatePath = resolve7(getDataDir4(), "run", "service.json");
2804
+ const config2 = loadConfig6();
2805
+ const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
2806
+ const serviceStatePath = resolve8(getDataDir4(), "run", "service.json");
2571
2807
  const fixActions = [];
2572
2808
  let serviceState = readServiceState();
2573
2809
  if (params.fix && serviceState && !isProcessRunning(serviceState.pid)) {
@@ -2699,12 +2935,12 @@ var DiagnosticsCommands = class {
2699
2935
  clearTimeout(timer);
2700
2936
  }
2701
2937
  }
2702
- readLogTail(path, maxLines = 25) {
2703
- if (!existsSync6(path)) {
2938
+ readLogTail(path2, maxLines = 25) {
2939
+ if (!existsSync6(path2)) {
2704
2940
  return [];
2705
2941
  }
2706
2942
  try {
2707
- const lines = readFileSync6(path, "utf-8").split(/\r?\n/).filter(Boolean);
2943
+ const lines = readFileSync6(path2, "utf-8").split(/\r?\n/).filter(Boolean);
2708
2944
  if (lines.length <= maxLines) {
2709
2945
  return lines;
2710
2946
  }
@@ -2714,17 +2950,17 @@ var DiagnosticsCommands = class {
2714
2950
  }
2715
2951
  }
2716
2952
  async checkPortAvailability(params) {
2717
- return await new Promise((resolve11) => {
2953
+ return await new Promise((resolve13) => {
2718
2954
  const server = createNetServer();
2719
2955
  server.once("error", (error) => {
2720
- resolve11({
2956
+ resolve13({
2721
2957
  available: false,
2722
2958
  detail: `bind failed on ${params.host}:${params.port} (${String(error)})`
2723
2959
  });
2724
2960
  });
2725
2961
  server.listen(params.port, params.host, () => {
2726
2962
  server.close(() => {
2727
- resolve11({
2963
+ resolve13({
2728
2964
  available: true,
2729
2965
  detail: `bind ok on ${params.host}:${params.port}`
2730
2966
  });
@@ -2737,20 +2973,19 @@ var DiagnosticsCommands = class {
2737
2973
  // src/cli/commands/service.ts
2738
2974
  import * as NextclawCore from "@nextclaw/core";
2739
2975
  import {
2740
- getPluginChannelBindings as getPluginChannelBindings2,
2976
+ getPluginChannelBindings as getPluginChannelBindings3,
2741
2977
  resolvePluginChannelMessageToolHints,
2742
2978
  setPluginRuntimeBridge,
2743
- startPluginChannelGateways,
2744
- stopPluginChannelGateways
2979
+ startPluginChannelGateways as startPluginChannelGateways2,
2980
+ stopPluginChannelGateways as stopPluginChannelGateways2
2745
2981
  } from "@nextclaw/openclaw-compat";
2746
2982
  import { startUiServer } from "@nextclaw/server";
2747
2983
  import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync, rmSync as rmSync4 } from "fs";
2748
- import { dirname as dirname2, join as join5, resolve as resolve8 } from "path";
2984
+ import { dirname as dirname2, join as join5, resolve as resolve10 } from "path";
2749
2985
  import { spawn as spawn2 } from "child_process";
2750
2986
  import { request as httpRequest } from "http";
2751
2987
  import { request as httpsRequest } from "https";
2752
2988
  import { createServer as createNetServer2 } from "net";
2753
- import { fileURLToPath as fileURLToPath2 } from "url";
2754
2989
  import chokidar from "chokidar";
2755
2990
 
2756
2991
  // src/cli/gateway/controller.ts
@@ -2764,11 +2999,11 @@ import {
2764
2999
  } from "@nextclaw/core";
2765
3000
  var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
2766
3001
  var readConfigSnapshot = (getConfigPath5) => {
2767
- const path = getConfigPath5();
3002
+ const path2 = getConfigPath5();
2768
3003
  let raw = "";
2769
3004
  let parsed = {};
2770
- if (existsSync7(path)) {
2771
- raw = readFileSync7(path, "utf-8");
3005
+ if (existsSync7(path2)) {
3006
+ raw = readFileSync7(path2, "utf-8");
2772
3007
  try {
2773
3008
  parsed = JSON.parse(raw);
2774
3009
  } catch {
@@ -3079,11 +3314,15 @@ var ConfigReloader = class {
3079
3314
  this.currentConfig = nextConfig;
3080
3315
  this.options.providerManager?.setConfig(nextConfig);
3081
3316
  const plan = buildReloadPlan3(changedPaths);
3317
+ let reloadPluginsResult = void 0;
3082
3318
  if (plan.reloadPlugins) {
3083
- await this.reloadPlugins(nextConfig);
3319
+ reloadPluginsResult = await this.reloadPlugins({
3320
+ config: nextConfig,
3321
+ changedPaths
3322
+ });
3084
3323
  console.log("Config reload: plugins reloaded.");
3085
3324
  }
3086
- if (plan.restartChannels) {
3325
+ if (plan.restartChannels || reloadPluginsResult?.restartChannels) {
3087
3326
  await this.reloadChannels(nextConfig);
3088
3327
  console.log("Config reload: channels restarted.");
3089
3328
  }
@@ -3178,11 +3417,11 @@ var ConfigReloader = class {
3178
3417
  this.providerReloadTask = null;
3179
3418
  }
3180
3419
  }
3181
- async reloadPlugins(nextConfig) {
3420
+ async reloadPlugins(params) {
3182
3421
  if (!this.options.reloadPlugins) {
3183
3422
  return;
3184
3423
  }
3185
- await this.options.reloadPlugins(nextConfig);
3424
+ return await this.options.reloadPlugins(params);
3186
3425
  }
3187
3426
  };
3188
3427
 
@@ -3204,6 +3443,104 @@ var MissingProvider = class extends LLMProvider {
3204
3443
  }
3205
3444
  };
3206
3445
 
3446
+ // src/cli/commands/service-plugin-reload.ts
3447
+ import { getWorkspacePath as getWorkspacePath5 } from "@nextclaw/core";
3448
+ import {
3449
+ getPluginChannelBindings as getPluginChannelBindings2,
3450
+ startPluginChannelGateways,
3451
+ stopPluginChannelGateways
3452
+ } from "@nextclaw/openclaw-compat";
3453
+
3454
+ // src/cli/commands/plugin-reload.ts
3455
+ function buildPluginChannelBindingSignature(binding) {
3456
+ return `${binding.pluginId}:${binding.channelId}`;
3457
+ }
3458
+ function buildSortedBindingSignatures(bindings) {
3459
+ return bindings.map(buildPluginChannelBindingSignature).sort();
3460
+ }
3461
+ function buildSortedExtensionChannelIds(channels2) {
3462
+ return channels2.map((registration) => registration.channel.id).filter((id) => typeof id === "string" && id.trim().length > 0).sort();
3463
+ }
3464
+ function areSortedStringListsEqual(left, right) {
3465
+ if (left.length !== right.length) {
3466
+ return false;
3467
+ }
3468
+ for (let index = 0; index < left.length; index += 1) {
3469
+ if (left[index] !== right[index]) {
3470
+ return false;
3471
+ }
3472
+ }
3473
+ return true;
3474
+ }
3475
+ function readPluginIdFromPluginsEntryPath(path2) {
3476
+ const prefix = "plugins.entries.";
3477
+ if (!path2.startsWith(prefix)) {
3478
+ return null;
3479
+ }
3480
+ const suffix = path2.slice(prefix.length);
3481
+ const [pluginId] = suffix.split(".");
3482
+ return pluginId?.trim() ? pluginId.trim() : null;
3483
+ }
3484
+ function shouldRestartChannelsForPluginReload(params) {
3485
+ const currentBindingSignatures = buildSortedBindingSignatures(params.currentPluginChannelBindings);
3486
+ const nextBindingSignatures = buildSortedBindingSignatures(params.nextPluginChannelBindings);
3487
+ if (!areSortedStringListsEqual(currentBindingSignatures, nextBindingSignatures)) {
3488
+ return true;
3489
+ }
3490
+ const currentExtensionChannelIds = buildSortedExtensionChannelIds(params.currentExtensionChannels);
3491
+ const nextExtensionChannelIds = buildSortedExtensionChannelIds(params.nextExtensionChannels);
3492
+ if (!areSortedStringListsEqual(currentExtensionChannelIds, nextExtensionChannelIds)) {
3493
+ return true;
3494
+ }
3495
+ const channelPluginIds = /* @__PURE__ */ new Set();
3496
+ for (const binding of params.currentPluginChannelBindings) {
3497
+ channelPluginIds.add(binding.pluginId);
3498
+ }
3499
+ for (const binding of params.nextPluginChannelBindings) {
3500
+ channelPluginIds.add(binding.pluginId);
3501
+ }
3502
+ for (const path2 of params.changedPaths) {
3503
+ const pluginId = readPluginIdFromPluginsEntryPath(path2);
3504
+ if (pluginId && channelPluginIds.has(pluginId)) {
3505
+ return true;
3506
+ }
3507
+ }
3508
+ return false;
3509
+ }
3510
+
3511
+ // src/cli/commands/service-plugin-reload.ts
3512
+ async function reloadServicePlugins(params) {
3513
+ const nextWorkspace = getWorkspacePath5(params.nextConfig.agents.defaults.workspace);
3514
+ const nextPluginRegistry = loadPluginRegistry(params.nextConfig, nextWorkspace);
3515
+ const nextExtensionRegistry = toExtensionRegistry(nextPluginRegistry);
3516
+ const nextPluginChannelBindings = getPluginChannelBindings2(nextPluginRegistry);
3517
+ const shouldRestartChannels = shouldRestartChannelsForPluginReload({
3518
+ changedPaths: params.changedPaths,
3519
+ currentPluginChannelBindings: params.pluginChannelBindings,
3520
+ nextPluginChannelBindings,
3521
+ currentExtensionChannels: params.extensionRegistry.channels,
3522
+ nextExtensionChannels: nextExtensionRegistry.channels
3523
+ });
3524
+ logPluginDiagnostics(nextPluginRegistry);
3525
+ let pluginGatewayHandles = params.pluginGatewayHandles;
3526
+ if (shouldRestartChannels) {
3527
+ await stopPluginChannelGateways(pluginGatewayHandles);
3528
+ const startedPluginGateways = await startPluginChannelGateways({
3529
+ registry: nextPluginRegistry,
3530
+ logger: params.pluginGatewayLogger
3531
+ });
3532
+ pluginGatewayHandles = startedPluginGateways.handles;
3533
+ params.logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
3534
+ }
3535
+ return {
3536
+ pluginRegistry: nextPluginRegistry,
3537
+ extensionRegistry: nextExtensionRegistry,
3538
+ pluginChannelBindings: nextPluginChannelBindings,
3539
+ pluginGatewayHandles,
3540
+ restartChannels: shouldRestartChannels
3541
+ };
3542
+ }
3543
+
3207
3544
  // src/cli/commands/agent-runtime-pool.ts
3208
3545
  import {
3209
3546
  NativeAgentEngine,
@@ -3211,7 +3548,7 @@ import {
3211
3548
  createAssistantStreamDeltaControlMessage,
3212
3549
  createAssistantStreamResetControlMessage,
3213
3550
  AgentRouteResolver,
3214
- getWorkspacePath as getWorkspacePath4,
3551
+ getWorkspacePath as getWorkspacePath6,
3215
3552
  parseAgentScopedSessionKey
3216
3553
  } from "@nextclaw/core";
3217
3554
  function normalizeAgentId(value) {
@@ -3256,7 +3593,7 @@ function resolveAgentProfiles(config2) {
3256
3593
  }
3257
3594
  return Array.from(unique.values()).map((entry) => ({
3258
3595
  id: entry.id,
3259
- workspace: getWorkspacePath4(entry.workspace ?? defaults.workspace),
3596
+ workspace: getWorkspacePath6(entry.workspace ?? defaults.workspace),
3260
3597
  model: entry.model ?? defaults.model,
3261
3598
  engine: normalizeEngineKind(entry.engine ?? defaults.engine),
3262
3599
  engineConfig: entry.engineConfig ?? toRecord(defaults.engineConfig),
@@ -3521,7 +3858,7 @@ var GatewayAgentRuntimePool = class {
3521
3858
  const normalizedAgentId = normalizeAgentId(agentId);
3522
3859
  return this.resolvedProfiles.find((profile) => profile.id === normalizedAgentId) ?? this.resolvedProfiles.find((profile) => profile.id === this.defaultAgentId) ?? this.resolvedProfiles[0] ?? {
3523
3860
  id: this.defaultAgentId,
3524
- workspace: getWorkspacePath4(this.options.config.agents.defaults.workspace),
3861
+ workspace: getWorkspacePath6(this.options.config.agents.defaults.workspace),
3525
3862
  model: this.options.config.agents.defaults.model,
3526
3863
  maxIterations: this.options.config.agents.defaults.maxToolIterations,
3527
3864
  contextTokens: this.options.config.agents.defaults.contextTokens,
@@ -3698,15 +4035,36 @@ function formatUserFacingError(error, maxChars = 320) {
3698
4035
  return `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
3699
4036
  }
3700
4037
 
4038
+ // src/cli/commands/cli-subcommand-launch.ts
4039
+ import { createRequire } from "module";
4040
+ import { extname, resolve as resolve9 } from "path";
4041
+ import { fileURLToPath as fileURLToPath2 } from "url";
4042
+ var require2 = createRequire(import.meta.url);
4043
+ var resolveCliSubcommandEntry = (params) => {
4044
+ const argvEntry = params.argvEntry?.trim();
4045
+ if (argvEntry) {
4046
+ return resolve9(argvEntry);
4047
+ }
4048
+ return fileURLToPath2(new URL("../index.js", params.importMetaUrl));
4049
+ };
4050
+
3701
4051
  // src/cli/commands/ncp/create-ui-ncp-agent.ts
4052
+ import {
4053
+ DisposableStore
4054
+ } from "@nextclaw/core";
3702
4055
  import { DefaultNcpAgentRuntime } from "@nextclaw/ncp-agent-runtime";
4056
+ import {
4057
+ readAssistantReasoningNormalizationMode,
4058
+ readAssistantReasoningNormalizationModeFromMetadata,
4059
+ writeAssistantReasoningNormalizationModeToMetadata
4060
+ } from "@nextclaw/ncp";
3703
4061
  import { createAgentClientFromServer, DefaultNcpAgentBackend } from "@nextclaw/ncp-toolkit";
3704
4062
 
3705
4063
  // src/cli/commands/ncp/nextclaw-ncp-context-builder.ts
3706
4064
  import {
3707
4065
  ContextBuilder,
3708
4066
  InputBudgetPruner,
3709
- getWorkspacePath as getWorkspacePath5,
4067
+ getWorkspacePath as getWorkspacePath7,
3710
4068
  parseThinkingLevel,
3711
4069
  resolveThinkingLevel
3712
4070
  } from "@nextclaw/core";
@@ -4191,7 +4549,7 @@ function resolvePrimaryAgentProfile(config2) {
4191
4549
  const profile = config2.agents.list.find((entry) => entry.id.trim() === configuredDefaultAgentId);
4192
4550
  return {
4193
4551
  agentId: configuredDefaultAgentId,
4194
- workspace: getWorkspacePath5(profile?.workspace ?? config2.agents.defaults.workspace),
4552
+ workspace: getWorkspacePath7(profile?.workspace ?? config2.agents.defaults.workspace),
4195
4553
  model: profile?.model ?? config2.agents.defaults.model,
4196
4554
  maxIterations: profile?.maxToolIterations ?? config2.agents.defaults.maxToolIterations,
4197
4555
  contextTokens: profile?.contextTokens ?? config2.agents.defaults.contextTokens,
@@ -4770,13 +5128,116 @@ var ProviderManagerNcpLLMApi = class {
4770
5128
  }
4771
5129
  };
4772
5130
 
5131
+ // src/cli/commands/ncp/ui-ncp-runtime-registry.ts
5132
+ import { toDisposable } from "@nextclaw/core";
5133
+ var DEFAULT_UI_NCP_RUNTIME_KIND = "native";
5134
+ function normalizeRuntimeKind(value) {
5135
+ if (typeof value !== "string") {
5136
+ return null;
5137
+ }
5138
+ const normalized = value.trim().toLowerCase();
5139
+ return normalized.length > 0 ? normalized : null;
5140
+ }
5141
+ function readRequestedRuntimeKind(sessionMetadata) {
5142
+ return normalizeRuntimeKind(sessionMetadata.session_type) ?? normalizeRuntimeKind(sessionMetadata.sessionType) ?? null;
5143
+ }
5144
+ var UiNcpRuntimeRegistry = class {
5145
+ constructor(defaultKind = DEFAULT_UI_NCP_RUNTIME_KIND) {
5146
+ this.defaultKind = defaultKind;
5147
+ }
5148
+ registrations = /* @__PURE__ */ new Map();
5149
+ register(registration) {
5150
+ const normalizedKind = normalizeRuntimeKind(registration.kind);
5151
+ if (!normalizedKind) {
5152
+ throw new Error("ui ncp runtime kind must be a non-empty string");
5153
+ }
5154
+ const token = Symbol(normalizedKind);
5155
+ this.registrations.set(normalizedKind, {
5156
+ ...registration,
5157
+ kind: normalizedKind,
5158
+ token
5159
+ });
5160
+ return toDisposable(() => {
5161
+ const current = this.registrations.get(normalizedKind);
5162
+ if (!current || current.token !== token) {
5163
+ return;
5164
+ }
5165
+ this.registrations.delete(normalizedKind);
5166
+ });
5167
+ }
5168
+ createRuntime(params) {
5169
+ const requestedKind = readRequestedRuntimeKind(params.sessionMetadata) ?? this.defaultKind;
5170
+ const registration = this.registrations.get(requestedKind);
5171
+ if (!registration) {
5172
+ throw new Error(`ncp runtime unavailable: ${requestedKind}`);
5173
+ }
5174
+ const nextSessionMetadata = {
5175
+ ...params.sessionMetadata,
5176
+ session_type: registration.kind
5177
+ };
5178
+ params.setSessionMetadata(nextSessionMetadata);
5179
+ return registration.createRuntime({
5180
+ ...params,
5181
+ sessionMetadata: nextSessionMetadata
5182
+ });
5183
+ }
5184
+ listSessionTypes() {
5185
+ const options = [...this.registrations.values()].map((registration) => ({
5186
+ value: registration.kind,
5187
+ label: registration.label
5188
+ })).sort((left, right) => {
5189
+ if (left.value === this.defaultKind) {
5190
+ return -1;
5191
+ }
5192
+ if (right.value === this.defaultKind) {
5193
+ return 1;
5194
+ }
5195
+ return left.value.localeCompare(right.value);
5196
+ });
5197
+ return {
5198
+ defaultType: this.defaultKind,
5199
+ options
5200
+ };
5201
+ }
5202
+ };
5203
+
4773
5204
  // src/cli/commands/ncp/create-ui-ncp-agent.ts
5205
+ function isRecord5(value) {
5206
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5207
+ }
5208
+ function resolveNativeReasoningNormalizationMode(params) {
5209
+ const runtimeEntry = params.config.ui.ncp.runtimes.native;
5210
+ const runtimeMetadata = isRecord5(runtimeEntry) ? runtimeEntry : {};
5211
+ return readAssistantReasoningNormalizationModeFromMetadata(params.sessionMetadata) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoningNormalizationMode) ?? readAssistantReasoningNormalizationMode(runtimeMetadata.reasoning_normalization_mode) ?? "think-tags";
5212
+ }
5213
+ function buildPluginRuntimeSnapshotKey(extensionRegistry) {
5214
+ const registrations = extensionRegistry?.ncpAgentRuntimes ?? [];
5215
+ return registrations.map((registration) => [
5216
+ registration.pluginId,
5217
+ registration.kind,
5218
+ registration.label,
5219
+ registration.source
5220
+ ].join(":")).join("|");
5221
+ }
4774
5222
  async function createUiNcpAgent(params) {
4775
5223
  const sessionStore = new NextclawAgentSessionStore(params.sessionManager);
4776
- const backend = new DefaultNcpAgentBackend({
4777
- endpointId: "nextclaw-ui-agent",
4778
- sessionStore,
4779
- createRuntime: ({ sessionId: _sessionId, stateManager }) => {
5224
+ const runtimeRegistry = new UiNcpRuntimeRegistry();
5225
+ runtimeRegistry.register({
5226
+ kind: "native",
5227
+ label: "Native",
5228
+ createRuntime: ({ stateManager, sessionMetadata, setSessionMetadata }) => {
5229
+ const reasoningNormalizationMode = resolveNativeReasoningNormalizationMode({
5230
+ config: params.getConfig(),
5231
+ sessionMetadata
5232
+ });
5233
+ if (reasoningNormalizationMode !== "off" && readAssistantReasoningNormalizationModeFromMetadata(sessionMetadata) !== reasoningNormalizationMode) {
5234
+ setSessionMetadata(
5235
+ writeAssistantReasoningNormalizationModeToMetadata(
5236
+ sessionMetadata,
5237
+ reasoningNormalizationMode
5238
+ )
5239
+ );
5240
+ }
4780
5241
  const toolRegistry = new NextclawNcpToolRegistry({
4781
5242
  bus: params.bus,
4782
5243
  providerManager: params.providerManager,
@@ -4795,19 +5256,113 @@ async function createUiNcpAgent(params) {
4795
5256
  }),
4796
5257
  llmApi: new ProviderManagerNcpLLMApi(params.providerManager),
4797
5258
  toolRegistry,
4798
- stateManager
5259
+ stateManager,
5260
+ reasoningNormalizationMode
4799
5261
  });
4800
5262
  }
4801
5263
  });
5264
+ const pluginRuntimeScopes = /* @__PURE__ */ new Map();
5265
+ let pluginRuntimeSnapshotKey = "";
5266
+ let activeExtensionRegistry;
5267
+ const syncPluginRuntimeRegistrations = (extensionRegistry) => {
5268
+ const nextSnapshotKey = buildPluginRuntimeSnapshotKey(extensionRegistry);
5269
+ if (nextSnapshotKey === pluginRuntimeSnapshotKey) {
5270
+ return;
5271
+ }
5272
+ pluginRuntimeSnapshotKey = nextSnapshotKey;
5273
+ for (const scope of pluginRuntimeScopes.values()) {
5274
+ scope.dispose();
5275
+ }
5276
+ pluginRuntimeScopes.clear();
5277
+ for (const registration of extensionRegistry?.ncpAgentRuntimes ?? []) {
5278
+ const pluginId = registration.pluginId.trim() || registration.kind;
5279
+ let scope = pluginRuntimeScopes.get(pluginId);
5280
+ if (!scope) {
5281
+ scope = new DisposableStore();
5282
+ pluginRuntimeScopes.set(pluginId, scope);
5283
+ }
5284
+ scope.add(runtimeRegistry.register({
5285
+ kind: registration.kind,
5286
+ label: registration.label,
5287
+ createRuntime: registration.createRuntime
5288
+ }));
5289
+ }
5290
+ };
5291
+ const resolveActiveExtensionRegistry = () => activeExtensionRegistry ?? params.getExtensionRegistry?.();
5292
+ const refreshPluginRuntimeRegistrations = () => {
5293
+ syncPluginRuntimeRegistrations(resolveActiveExtensionRegistry());
5294
+ };
5295
+ refreshPluginRuntimeRegistrations();
5296
+ const backend = new DefaultNcpAgentBackend({
5297
+ endpointId: "nextclaw-ui-agent",
5298
+ sessionStore,
5299
+ createRuntime: (runtimeParams) => {
5300
+ refreshPluginRuntimeRegistrations();
5301
+ return runtimeRegistry.createRuntime(runtimeParams);
5302
+ }
5303
+ });
4802
5304
  await backend.start();
4803
5305
  return {
4804
5306
  basePath: "/api/ncp/agent",
4805
5307
  agentClientEndpoint: createAgentClientFromServer(backend),
4806
5308
  streamProvider: backend,
4807
- sessionApi: backend
5309
+ sessionApi: backend,
5310
+ listSessionTypes: () => {
5311
+ refreshPluginRuntimeRegistrations();
5312
+ return runtimeRegistry.listSessionTypes();
5313
+ },
5314
+ applyExtensionRegistry: (extensionRegistry) => {
5315
+ activeExtensionRegistry = extensionRegistry;
5316
+ syncPluginRuntimeRegistrations(extensionRegistry);
5317
+ }
4808
5318
  };
4809
5319
  }
4810
5320
 
5321
+ // src/cli/commands/service-marketplace-helpers.ts
5322
+ var containsAbsoluteFsPath = (line) => {
5323
+ const normalized = line.trim();
5324
+ if (!normalized) {
5325
+ return false;
5326
+ }
5327
+ const lowered = normalized.toLowerCase();
5328
+ if (lowered.includes("http://") || lowered.includes("https://")) {
5329
+ return false;
5330
+ }
5331
+ if (/^[A-Za-z]:\\/.test(normalized)) {
5332
+ return true;
5333
+ }
5334
+ return /(?:^|\s)(?:~\/|\/[^\s]+)/.test(normalized);
5335
+ };
5336
+ var pickUserFacingCommandSummary = (output, fallback) => {
5337
+ const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
5338
+ if (lines.length === 0) {
5339
+ return fallback;
5340
+ }
5341
+ const visibleLines = lines.filter((line) => {
5342
+ if (/^(path|install path|source path|destination|location)\s*:/i.test(line)) {
5343
+ return false;
5344
+ }
5345
+ if (containsAbsoluteFsPath(line)) {
5346
+ return false;
5347
+ }
5348
+ return true;
5349
+ });
5350
+ if (visibleLines.length === 0) {
5351
+ return fallback;
5352
+ }
5353
+ const preferred = [...visibleLines].reverse().find(
5354
+ (line) => /\b(installed|enabled|disabled|uninstalled|published|updated|already installed|removed)\b/i.test(line)
5355
+ );
5356
+ return preferred ?? visibleLines[visibleLines.length - 1] ?? fallback;
5357
+ };
5358
+ var buildMarketplaceSkillInstallArgs = (params) => {
5359
+ const args = ["skills", "install", params.slug, "--workdir", params.workspace];
5360
+ if (params.force) {
5361
+ args.push("--force");
5362
+ }
5363
+ return args;
5364
+ };
5365
+
4811
5366
  // src/cli/commands/ui-chat-run-coordinator.ts
4812
5367
  import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
4813
5368
  import { join as join4 } from "path";
@@ -4825,7 +5380,7 @@ function createRunId() {
4825
5380
  const rand = Math.random().toString(36).slice(2, 10);
4826
5381
  return `run-${now}-${rand}`;
4827
5382
  }
4828
- function isRecord5(value) {
5383
+ function isRecord6(value) {
4829
5384
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4830
5385
  }
4831
5386
  function readOptionalString(value) {
@@ -5015,7 +5570,7 @@ var UiChatRunCoordinator = class {
5015
5570
  const parsedAgentId = parseAgentScopedSessionKey2(sessionKey)?.agentId;
5016
5571
  const agentId = explicitAgentId ?? readOptionalString(parsedAgentId);
5017
5572
  const model = readOptionalString(input.model);
5018
- const metadata = isRecord5(input.metadata) ? { ...input.metadata } : {};
5573
+ const metadata = isRecord6(input.metadata) ? { ...input.metadata } : {};
5019
5574
  if (model) {
5020
5575
  metadata.model = model;
5021
5576
  }
@@ -5236,14 +5791,14 @@ var UiChatRunCoordinator = class {
5236
5791
  this.emitRunUpdated(run);
5237
5792
  }
5238
5793
  waitForRunUpdate(run, signal) {
5239
- return new Promise((resolve11) => {
5794
+ return new Promise((resolve13) => {
5240
5795
  const wake = () => {
5241
5796
  cleanup();
5242
- resolve11();
5797
+ resolve13();
5243
5798
  };
5244
5799
  const onAbort = () => {
5245
5800
  cleanup();
5246
- resolve11();
5801
+ resolve13();
5247
5802
  };
5248
5803
  const cleanup = () => {
5249
5804
  run.waiters.delete(wake);
@@ -5371,9 +5926,9 @@ var UiChatRunCoordinator = class {
5371
5926
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
5372
5927
  continue;
5373
5928
  }
5374
- const path = join4(RUNS_DIR, entry.name);
5929
+ const path2 = join4(RUNS_DIR, entry.name);
5375
5930
  try {
5376
- const parsed = JSON.parse(readFileSync8(path, "utf-8"));
5931
+ const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
5377
5932
  const runId = readOptionalString(parsed.runId);
5378
5933
  const sessionKey = readOptionalString(parsed.sessionKey);
5379
5934
  if (!runId || !sessionKey) {
@@ -5436,10 +5991,10 @@ var {
5436
5991
  getDataDir: getDataDir6,
5437
5992
  getProvider,
5438
5993
  getProviderName,
5439
- getWorkspacePath: getWorkspacePath6,
5994
+ getWorkspacePath: getWorkspacePath8,
5440
5995
  HeartbeatService,
5441
5996
  LiteLLMProvider,
5442
- loadConfig: loadConfig6,
5997
+ loadConfig: loadConfig7,
5443
5998
  MessageBus,
5444
5999
  ProviderManager,
5445
6000
  resolveConfigSecrets: resolveConfigSecrets2,
@@ -5454,64 +6009,18 @@ function createSkillsLoader(workspace) {
5454
6009
  }
5455
6010
  return new ctor(workspace);
5456
6011
  }
5457
- function containsAbsoluteFsPath(line) {
5458
- const normalized = line.trim();
5459
- if (!normalized) {
5460
- return false;
5461
- }
5462
- const lowered = normalized.toLowerCase();
5463
- if (lowered.includes("http://") || lowered.includes("https://")) {
5464
- return false;
5465
- }
5466
- if (/^[A-Za-z]:\\/.test(normalized)) {
5467
- return true;
5468
- }
5469
- return /(?:^|\s)(?:~\/|\/[^\s]+)/.test(normalized);
5470
- }
5471
- function pickUserFacingCommandSummary(output, fallback) {
5472
- const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
5473
- if (lines.length === 0) {
5474
- return fallback;
5475
- }
5476
- const visibleLines = lines.filter((line) => {
5477
- if (/^(path|install path|source path|destination|location)\s*:/i.test(line)) {
5478
- return false;
5479
- }
5480
- if (containsAbsoluteFsPath(line)) {
5481
- return false;
5482
- }
5483
- return true;
5484
- });
5485
- if (visibleLines.length === 0) {
5486
- return fallback;
5487
- }
5488
- const preferred = [...visibleLines].reverse().find(
5489
- (line) => /\b(installed|enabled|disabled|uninstalled|published|updated|already installed|removed)\b/i.test(line)
5490
- );
5491
- return preferred ?? visibleLines[visibleLines.length - 1] ?? fallback;
5492
- }
5493
- function buildMarketplaceSkillInstallArgs(params) {
5494
- const args = ["skills", "install", params.slug, "--workdir", params.workspace];
5495
- if (params.force) {
5496
- args.push("--force");
5497
- }
5498
- return args;
5499
- }
5500
- function resolveCliSubcommandEntry(params) {
5501
- const argvEntry = params.argvEntry?.trim();
5502
- if (argvEntry) {
5503
- return resolve8(argvEntry);
5504
- }
5505
- return fileURLToPath2(new URL("../index.js", params.importMetaUrl));
5506
- }
5507
6012
  var ServiceCommands = class {
5508
6013
  constructor(deps) {
5509
6014
  this.deps = deps;
5510
6015
  }
6016
+ applyLiveConfigReload = null;
6017
+ liveUiNcpAgent = null;
5511
6018
  async startGateway(options = {}) {
6019
+ this.applyLiveConfigReload = null;
6020
+ this.liveUiNcpAgent = null;
5512
6021
  const runtimeConfigPath = getConfigPath3();
5513
- const config2 = resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath });
5514
- const workspace = getWorkspacePath6(config2.agents.defaults.workspace);
6022
+ const config2 = resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath });
6023
+ const workspace = getWorkspacePath8(config2.agents.defaults.workspace);
5515
6024
  let pluginRegistry = loadPluginRegistry(config2, workspace);
5516
6025
  let extensionRegistry = toExtensionRegistry(pluginRegistry);
5517
6026
  logPluginDiagnostics(pluginRegistry);
@@ -5555,7 +6064,7 @@ var ServiceCommands = class {
5555
6064
  sessionManager,
5556
6065
  providerManager,
5557
6066
  makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
5558
- loadConfig: () => resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath }),
6067
+ loadConfig: () => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
5559
6068
  getExtensionChannels: () => extensionRegistry.channels,
5560
6069
  onRestartRequired: (paths) => {
5561
6070
  void this.deps.requestRestart({
@@ -5565,6 +6074,9 @@ var ServiceCommands = class {
5565
6074
  });
5566
6075
  }
5567
6076
  });
6077
+ this.applyLiveConfigReload = async () => {
6078
+ await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }));
6079
+ };
5568
6080
  const gatewayController = new GatewayControllerImpl({
5569
6081
  reloader,
5570
6082
  cron: cron2,
@@ -5596,38 +6108,42 @@ var ServiceCommands = class {
5596
6108
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
5597
6109
  registry: pluginRegistry,
5598
6110
  channel,
5599
- cfg: resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath }),
6111
+ cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
5600
6112
  accountId
5601
6113
  })
5602
6114
  });
5603
6115
  reloader.setApplyAgentRuntimeConfig((nextConfig) => runtimePool.applyRuntimeConfig(nextConfig));
5604
- reloader.setReloadPlugins(async (nextConfig) => {
5605
- const nextWorkspace = getWorkspacePath6(nextConfig.agents.defaults.workspace);
5606
- const nextPluginRegistry = loadPluginRegistry(nextConfig, nextWorkspace);
5607
- const nextExtensionRegistry = toExtensionRegistry(nextPluginRegistry);
5608
- logPluginDiagnostics(nextPluginRegistry);
5609
- await stopPluginChannelGateways(pluginGatewayHandles);
5610
- const startedPluginGateways = await startPluginChannelGateways({
5611
- registry: nextPluginRegistry,
5612
- logger: pluginGatewayLogger
6116
+ reloader.setReloadPlugins(async ({ config: nextConfig, changedPaths }) => {
6117
+ const result = await reloadServicePlugins({
6118
+ nextConfig,
6119
+ changedPaths,
6120
+ pluginRegistry,
6121
+ extensionRegistry,
6122
+ pluginChannelBindings,
6123
+ pluginGatewayHandles,
6124
+ pluginGatewayLogger,
6125
+ logPluginGatewayDiagnostics
5613
6126
  });
5614
- pluginGatewayHandles = startedPluginGateways.handles;
5615
- logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
5616
- pluginRegistry = nextPluginRegistry;
5617
- extensionRegistry = nextExtensionRegistry;
5618
- pluginChannelBindings = getPluginChannelBindings2(nextPluginRegistry);
5619
- runtimePool.applyExtensionRegistry(nextExtensionRegistry);
6127
+ pluginRegistry = result.pluginRegistry;
6128
+ extensionRegistry = result.extensionRegistry;
6129
+ pluginChannelBindings = result.pluginChannelBindings;
6130
+ pluginGatewayHandles = result.pluginGatewayHandles;
6131
+ runtimePool.applyExtensionRegistry(result.extensionRegistry);
6132
+ this.liveUiNcpAgent?.applyExtensionRegistry?.(result.extensionRegistry);
5620
6133
  runtimePool.applyRuntimeConfig(nextConfig);
5621
- console.log("Config reload: plugin channel gateways restarted.");
6134
+ if (result.restartChannels) {
6135
+ console.log("Config reload: plugin channel gateways restarted.");
6136
+ }
6137
+ return { restartChannels: result.restartChannels };
5622
6138
  });
5623
- let pluginChannelBindings = getPluginChannelBindings2(pluginRegistry);
6139
+ let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
5624
6140
  setPluginRuntimeBridge({
5625
- loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath }), pluginChannelBindings),
6141
+ loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }), pluginChannelBindings),
5626
6142
  writeConfigFile: async (nextConfigView) => {
5627
6143
  if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
5628
6144
  throw new Error("plugin runtime writeConfigFile expects an object config");
5629
6145
  }
5630
- const current = loadConfig6();
6146
+ const current = loadConfig7();
5631
6147
  const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
5632
6148
  saveConfig5(next);
5633
6149
  },
@@ -5703,12 +6219,12 @@ var ServiceCommands = class {
5703
6219
  providerManager,
5704
6220
  bus,
5705
6221
  gatewayController,
5706
- () => resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath }),
6222
+ () => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
5707
6223
  () => extensionRegistry,
5708
6224
  ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
5709
6225
  registry: pluginRegistry,
5710
6226
  channel,
5711
- cfg: resolveConfigSecrets2(loadConfig6(), { configPath: runtimeConfigPath }),
6227
+ cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
5712
6228
  accountId
5713
6229
  })
5714
6230
  );
@@ -5717,13 +6233,13 @@ var ServiceCommands = class {
5717
6233
  console.log(`\u2713 Cron: ${cronStatus.jobs} scheduled jobs`);
5718
6234
  }
5719
6235
  console.log("\u2713 Heartbeat: every 30m");
5720
- const configPath = resolve8(getConfigPath3());
6236
+ const configPath = resolve10(getConfigPath3());
5721
6237
  const watcher = chokidar.watch(configPath, {
5722
6238
  ignoreInitial: true,
5723
6239
  awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
5724
6240
  });
5725
6241
  watcher.on("all", (event, changedPath) => {
5726
- if (resolve8(changedPath) !== configPath) {
6242
+ if (resolve10(changedPath) !== configPath) {
5727
6243
  return;
5728
6244
  }
5729
6245
  if (event === "add") {
@@ -5741,7 +6257,7 @@ var ServiceCommands = class {
5741
6257
  await cron2.start();
5742
6258
  await heartbeat.start();
5743
6259
  try {
5744
- const startedPluginGateways = await startPluginChannelGateways({
6260
+ const startedPluginGateways = await startPluginChannelGateways2({
5745
6261
  registry: pluginRegistry,
5746
6262
  logger: pluginGatewayLogger
5747
6263
  });
@@ -5751,7 +6267,9 @@ var ServiceCommands = class {
5751
6267
  await this.wakeFromRestartSentinel({ bus, sessionManager });
5752
6268
  await runtimePool.run();
5753
6269
  } finally {
5754
- await stopPluginChannelGateways(pluginGatewayHandles);
6270
+ this.applyLiveConfigReload = null;
6271
+ this.liveUiNcpAgent = null;
6272
+ await stopPluginChannelGateways2(pluginGatewayHandles);
5755
6273
  setPluginRuntimeBridge(null);
5756
6274
  }
5757
6275
  }
@@ -5815,7 +6333,7 @@ var ServiceCommands = class {
5815
6333
  if (!sentinel) {
5816
6334
  return;
5817
6335
  }
5818
- await new Promise((resolve11) => setTimeout(resolve11, 750));
6336
+ await new Promise((resolve13) => setTimeout(resolve13, 750));
5819
6337
  const payload = sentinel.payload;
5820
6338
  const summary = formatRestartSentinelMessage(payload);
5821
6339
  const sentinelSessionKey = this.normalizeOptionalString(payload.sessionKey);
@@ -5861,7 +6379,7 @@ var ServiceCommands = class {
5861
6379
  });
5862
6380
  }
5863
6381
  async runForeground(options) {
5864
- const config2 = loadConfig6();
6382
+ const config2 = loadConfig7();
5865
6383
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
5866
6384
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
5867
6385
  if (options.open) {
@@ -5874,7 +6392,7 @@ var ServiceCommands = class {
5874
6392
  });
5875
6393
  }
5876
6394
  async startService(options) {
5877
- const config2 = loadConfig6();
6395
+ const config2 = loadConfig7();
5878
6396
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
5879
6397
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
5880
6398
  const apiUrl = `${uiUrl}/api`;
@@ -5934,7 +6452,7 @@ var ServiceCommands = class {
5934
6452
  return;
5935
6453
  }
5936
6454
  const logPath = resolveServiceLogPath();
5937
- const logDir = resolve8(logPath, "..");
6455
+ const logDir = resolve10(logPath, "..");
5938
6456
  mkdirSync5(logDir, { recursive: true });
5939
6457
  const logFd = openSync(logPath, "a");
5940
6458
  const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
@@ -6082,14 +6600,14 @@ var ServiceCommands = class {
6082
6600
  const probe = await this.probeHealthEndpoint(params.healthUrl);
6083
6601
  if (!probe.healthy) {
6084
6602
  lastProbeError = probe.error;
6085
- await new Promise((resolve11) => setTimeout(resolve11, 200));
6603
+ await new Promise((resolve13) => setTimeout(resolve13, 200));
6086
6604
  continue;
6087
6605
  }
6088
- await new Promise((resolve11) => setTimeout(resolve11, 300));
6606
+ await new Promise((resolve13) => setTimeout(resolve13, 300));
6089
6607
  if (isProcessRunning(params.pid)) {
6090
6608
  return { ready: true, lastProbeError: null };
6091
6609
  }
6092
- await new Promise((resolve11) => setTimeout(resolve11, 200));
6610
+ await new Promise((resolve13) => setTimeout(resolve13, 200));
6093
6611
  }
6094
6612
  return { ready: false, lastProbeError };
6095
6613
  }
@@ -6160,17 +6678,17 @@ var ServiceCommands = class {
6160
6678
  };
6161
6679
  }
6162
6680
  async checkPortAvailability(params) {
6163
- return await new Promise((resolve11) => {
6681
+ return await new Promise((resolve13) => {
6164
6682
  const server = createNetServer2();
6165
6683
  server.once("error", (error) => {
6166
- resolve11({
6684
+ resolve13({
6167
6685
  available: false,
6168
6686
  detail: `bind failed on ${params.host}:${params.port} (${String(error)})`
6169
6687
  });
6170
6688
  });
6171
6689
  server.listen(params.port, params.host, () => {
6172
6690
  server.close(() => {
6173
- resolve11({
6691
+ resolve13({
6174
6692
  available: true,
6175
6693
  detail: `bind ok on ${params.host}:${params.port}`
6176
6694
  });
@@ -6206,7 +6724,7 @@ var ServiceCommands = class {
6206
6724
  return { healthy: false, error: "invalid health URL" };
6207
6725
  }
6208
6726
  const requestImpl = parsed.protocol === "https:" ? httpsRequest : httpRequest;
6209
- return new Promise((resolve11) => {
6727
+ return new Promise((resolve13) => {
6210
6728
  const req = requestImpl(
6211
6729
  {
6212
6730
  protocol: parsed.protocol,
@@ -6242,19 +6760,19 @@ var ServiceCommands = class {
6242
6760
  if (bodySnippet) {
6243
6761
  details.push(`body=${bodySnippet}`);
6244
6762
  }
6245
- resolve11({ healthy: false, error: details.join("; ") });
6763
+ resolve13({ healthy: false, error: details.join("; ") });
6246
6764
  return;
6247
6765
  }
6248
6766
  try {
6249
6767
  const payload = JSON.parse(responseText);
6250
6768
  const healthy = payload?.ok === true && payload?.data?.status === "ok";
6251
6769
  if (!healthy) {
6252
- resolve11({ healthy: false, error: "health payload not ok" });
6770
+ resolve13({ healthy: false, error: "health payload not ok" });
6253
6771
  return;
6254
6772
  }
6255
- resolve11({ healthy: true, error: null });
6773
+ resolve13({ healthy: true, error: null });
6256
6774
  } catch {
6257
- resolve11({ healthy: false, error: "invalid health JSON response" });
6775
+ resolve13({ healthy: false, error: "invalid health JSON response" });
6258
6776
  }
6259
6777
  });
6260
6778
  }
@@ -6263,7 +6781,7 @@ var ServiceCommands = class {
6263
6781
  req.destroy(new Error("probe timeout"));
6264
6782
  });
6265
6783
  req.on("error", (error) => {
6266
- resolve11({ healthy: false, error: error.message || String(error) });
6784
+ resolve13({ healthy: false, error: error.message || String(error) });
6267
6785
  });
6268
6786
  req.end();
6269
6787
  });
@@ -6368,13 +6886,7 @@ var ServiceCommands = class {
6368
6886
  if (sessionType === "native") {
6369
6887
  return "Native";
6370
6888
  }
6371
- if (sessionType === "codex-sdk") {
6372
- return "Codex";
6373
- }
6374
- if (sessionType === "claude-agent-sdk") {
6375
- return "Claude Code";
6376
- }
6377
- return sessionType;
6889
+ return sessionType.trim().split(/[-_]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || sessionType;
6378
6890
  };
6379
6891
  let publishUiEvent = null;
6380
6892
  runtimePool.setSystemSessionUpdatedHandler(({ sessionKey, message }) => {
@@ -6412,6 +6924,7 @@ var ServiceCommands = class {
6412
6924
  accountId
6413
6925
  })
6414
6926
  });
6927
+ this.liveUiNcpAgent = ncpAgent;
6415
6928
  const uiServer = startUiServer({
6416
6929
  host: uiConfig.host,
6417
6930
  port: uiConfig.port,
@@ -6510,9 +7023,9 @@ var ServiceCommands = class {
6510
7023
  }
6511
7024
  }
6512
7025
  async installMarketplacePlugin(spec) {
6513
- const output = await this.runCliSubcommand(["plugins", "install", spec]);
6514
- const summary = pickUserFacingCommandSummary(output, `Installed plugin: ${spec}`);
6515
- return { message: summary };
7026
+ const result = await installPluginMutation(spec);
7027
+ await this.applyLiveConfigReload?.();
7028
+ return { message: result.message };
6516
7029
  }
6517
7030
  async installMarketplaceSkill(params) {
6518
7031
  if (params.kind === "builtin") {
@@ -6525,7 +7038,7 @@ var ServiceCommands = class {
6525
7038
  if (params.kind && params.kind !== "marketplace") {
6526
7039
  throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
6527
7040
  }
6528
- const workspace = getWorkspacePath6(loadConfig6().agents.defaults.workspace);
7041
+ const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
6529
7042
  const args = buildMarketplaceSkillInstallArgs({
6530
7043
  slug: params.slug,
6531
7044
  workspace,
@@ -6544,22 +7057,24 @@ var ServiceCommands = class {
6544
7057
  }
6545
7058
  }
6546
7059
  async enableMarketplacePlugin(id) {
6547
- const output = await this.runCliSubcommand(["plugins", "enable", id]);
6548
- const summary = pickUserFacingCommandSummary(output, `Enabled plugin: ${id}`);
6549
- return { message: summary };
7060
+ const result = await enablePluginMutation(id);
7061
+ await this.applyLiveConfigReload?.();
7062
+ return { message: result.message };
6550
7063
  }
6551
7064
  async disableMarketplacePlugin(id) {
6552
- const output = await this.runCliSubcommand(["plugins", "disable", id]);
6553
- const summary = pickUserFacingCommandSummary(output, `Disabled plugin: ${id}`);
6554
- return { message: summary };
7065
+ const result = await disablePluginMutation(id);
7066
+ await this.applyLiveConfigReload?.();
7067
+ return { message: result.message };
6555
7068
  }
6556
7069
  async uninstallMarketplacePlugin(id) {
6557
- const output = await this.runCliSubcommand(["plugins", "uninstall", id, "--force"]);
6558
- const summary = pickUserFacingCommandSummary(output, `Uninstalled plugin: ${id}`);
6559
- return { message: summary };
7070
+ await disablePluginMutation(id);
7071
+ await this.applyLiveConfigReload?.();
7072
+ const result = await uninstallPluginMutation(id, { force: true });
7073
+ await this.applyLiveConfigReload?.();
7074
+ return { message: result.message };
6560
7075
  }
6561
7076
  async uninstallMarketplaceSkill(slug) {
6562
- const workspace = getWorkspacePath6(loadConfig6().agents.defaults.workspace);
7077
+ const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
6563
7078
  const targetDir = join5(workspace, "skills", slug);
6564
7079
  if (!existsSync9(targetDir)) {
6565
7080
  throw new Error(`Skill not installed in workspace: ${slug}`);
@@ -6570,7 +7085,7 @@ var ServiceCommands = class {
6570
7085
  };
6571
7086
  }
6572
7087
  installBuiltinMarketplaceSkill(slug, force) {
6573
- const workspace = getWorkspacePath6(loadConfig6().agents.defaults.workspace);
7088
+ const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
6574
7089
  const destination = join5(workspace, "skills", slug);
6575
7090
  const destinationSkillFile = join5(destination, "SKILL.md");
6576
7091
  if (existsSync9(destinationSkillFile) && !force) {
@@ -6649,8 +7164,8 @@ ${stderr}`.trim();
6649
7164
 
6650
7165
  // src/cli/workspace.ts
6651
7166
  import { cpSync as cpSync3, existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
6652
- import { createRequire } from "module";
6653
- import { dirname as dirname3, join as join6, resolve as resolve9 } from "path";
7167
+ import { createRequire as createRequire2 } from "module";
7168
+ import { dirname as dirname3, join as join6, resolve as resolve11 } from "path";
6654
7169
  import { fileURLToPath as fileURLToPath3 } from "url";
6655
7170
  import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
6656
7171
  import { spawnSync as spawnSync3 } from "child_process";
@@ -6742,9 +7257,9 @@ var WorkspaceManager = class {
6742
7257
  }
6743
7258
  resolveBuiltinSkillsDir() {
6744
7259
  try {
6745
- const require2 = createRequire(import.meta.url);
6746
- const entry = require2.resolve("@nextclaw/core");
6747
- const pkgRoot = resolve9(dirname3(entry), "..");
7260
+ const require3 = createRequire2(import.meta.url);
7261
+ const entry = require3.resolve("@nextclaw/core");
7262
+ const pkgRoot = resolve11(dirname3(entry), "..");
6748
7263
  const distSkills = join6(pkgRoot, "dist", "skills");
6749
7264
  if (existsSync10(distSkills)) {
6750
7265
  return distSkills;
@@ -6763,8 +7278,8 @@ var WorkspaceManager = class {
6763
7278
  if (override) {
6764
7279
  return override;
6765
7280
  }
6766
- const cliDir = resolve9(fileURLToPath3(new URL(".", import.meta.url)));
6767
- const pkgRoot = resolve9(cliDir, "..", "..");
7281
+ const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
7282
+ const pkgRoot = resolve11(cliDir, "..", "..");
6768
7283
  const candidates = [join6(pkgRoot, "templates")];
6769
7284
  for (const candidate of candidates) {
6770
7285
  if (existsSync10(candidate)) {
@@ -6782,8 +7297,8 @@ var WorkspaceManager = class {
6782
7297
  console.error("npm not found. Please install Node.js >= 18.");
6783
7298
  process.exit(1);
6784
7299
  }
6785
- const cliDir = resolve9(fileURLToPath3(new URL(".", import.meta.url)));
6786
- const pkgRoot = resolve9(cliDir, "..", "..");
7300
+ const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
7301
+ const pkgRoot = resolve11(cliDir, "..", "..");
6787
7302
  const pkgBridge = join6(pkgRoot, "bridge");
6788
7303
  const srcBridge = join6(pkgRoot, "..", "..", "bridge");
6789
7304
  let source = null;
@@ -6797,7 +7312,7 @@ var WorkspaceManager = class {
6797
7312
  process.exit(1);
6798
7313
  }
6799
7314
  console.log(`${this.logo} Setting up bridge...`);
6800
- mkdirSync6(resolve9(userBridge, ".."), { recursive: true });
7315
+ mkdirSync6(resolve11(userBridge, ".."), { recursive: true });
6801
7316
  if (existsSync10(userBridge)) {
6802
7317
  rmSync5(userBridge, { recursive: true, force: true });
6803
7318
  }
@@ -6834,7 +7349,7 @@ function resolveSkillsInstallWorkdir(params) {
6834
7349
  if (params.explicitWorkdir) {
6835
7350
  return expandHome2(params.explicitWorkdir);
6836
7351
  }
6837
- return getWorkspacePath7(params.configuredWorkspace);
7352
+ return getWorkspacePath9(params.configuredWorkspace);
6838
7353
  }
6839
7354
  var CliRuntime = class {
6840
7355
  logo;
@@ -6933,7 +7448,7 @@ var CliRuntime = class {
6933
7448
  const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
6934
7449
  const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath4(new URL("./index.js", import.meta.url));
6935
7450
  const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
6936
- const serviceStatePath = resolve10(getDataDir8(), "run", "service.json");
7451
+ const serviceStatePath = resolve12(getDataDir8(), "run", "service.json");
6937
7452
  const helperScript = [
6938
7453
  'const { spawnSync } = require("node:child_process");',
6939
7454
  'const { readFileSync } = require("node:fs");',
@@ -7068,7 +7583,7 @@ var CliRuntime = class {
7068
7583
  saveConfig6(config3);
7069
7584
  createdConfig = true;
7070
7585
  }
7071
- const config2 = loadConfig7();
7586
+ const config2 = loadConfig8();
7072
7587
  const workspaceSetting = config2.agents.defaults.workspace;
7073
7588
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join7(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
7074
7589
  const workspaceExisted = existsSync11(workspacePath);
@@ -7104,7 +7619,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7104
7619
  async login(opts = {}) {
7105
7620
  await this.init({ source: "login", auto: true });
7106
7621
  const configPath = getConfigPath4();
7107
- const config2 = loadConfig7(configPath);
7622
+ const config2 = loadConfig8(configPath);
7108
7623
  const providers = config2.providers;
7109
7624
  const nextclawProvider = providers.nextclaw ?? {
7110
7625
  displayName: "",
@@ -7261,15 +7776,15 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7261
7776
  }
7262
7777
  async agent(opts) {
7263
7778
  const configPath = getConfigPath4();
7264
- const config2 = resolveConfigSecrets3(loadConfig7(), { configPath });
7265
- const workspace = getWorkspacePath7(config2.agents.defaults.workspace);
7779
+ const config2 = resolveConfigSecrets3(loadConfig8(), { configPath });
7780
+ const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
7266
7781
  const pluginRegistry = loadPluginRegistry(config2, workspace);
7267
7782
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
7268
7783
  logPluginDiagnostics(pluginRegistry);
7269
- const pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
7784
+ const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
7270
7785
  setPluginRuntimeBridge2({
7271
7786
  loadConfig: () => toPluginConfigView(
7272
- resolveConfigSecrets3(loadConfig7(), { configPath }),
7787
+ resolveConfigSecrets3(loadConfig8(), { configPath }),
7273
7788
  pluginChannelBindings
7274
7789
  ),
7275
7790
  writeConfigFile: async (nextConfigView) => {
@@ -7278,7 +7793,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7278
7793
  "plugin runtime writeConfigFile expects an object config"
7279
7794
  );
7280
7795
  }
7281
- const current = loadConfig7();
7796
+ const current = loadConfig8();
7282
7797
  const next = mergePluginConfigView(
7283
7798
  current,
7284
7799
  nextConfigView,
@@ -7310,7 +7825,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7310
7825
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
7311
7826
  registry: pluginRegistry,
7312
7827
  channel,
7313
- cfg: resolveConfigSecrets3(loadConfig7(), { configPath }),
7828
+ cfg: resolveConfigSecrets3(loadConfig8(), { configPath }),
7314
7829
  accountId
7315
7830
  })
7316
7831
  });
@@ -7330,7 +7845,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7330
7845
  `
7331
7846
  );
7332
7847
  const historyFile = join7(getDataDir8(), "history", "cli_history");
7333
- const historyDir = resolve10(historyFile, "..");
7848
+ const historyDir = resolve12(historyFile, "..");
7334
7849
  mkdirSync7(historyDir, { recursive: true });
7335
7850
  const history = existsSync11(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
7336
7851
  const rl = createInterface2({
@@ -7487,7 +8002,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7487
8002
  await this.diagnosticsCommands.doctor(opts);
7488
8003
  }
7489
8004
  async skillsInstall(options) {
7490
- const config2 = loadConfig7();
8005
+ const config2 = loadConfig8();
7491
8006
  const workdir = resolveSkillsInstallWorkdir({
7492
8007
  explicitWorkdir: options.workdir,
7493
8008
  configuredWorkspace: config2.agents.defaults.workspace
@@ -7577,9 +8092,9 @@ plugins.command("uninstall <id>").description("Uninstall a plugin").option("--ke
7577
8092
  plugins.command("install <path-or-spec>").description("Install a plugin (path, archive, or npm spec)").option("-l, --link", "Link a local path instead of copying", false).action(async (pathOrSpec, opts) => runtime.pluginsInstall(pathOrSpec, opts));
7578
8093
  plugins.command("doctor").description("Report plugin load issues").action(() => runtime.pluginsDoctor());
7579
8094
  var config = program.command("config").description("Manage config values");
7580
- config.command("get <path>").description("Get a config value by dot path").option("--json", "Output JSON", false).action((path, opts) => runtime.configGet(path, opts));
7581
- config.command("set <path> <value>").description("Set a config value by dot path").option("--json", "Parse value as JSON", false).action((path, value, opts) => runtime.configSet(path, value, opts));
7582
- config.command("unset <path>").description("Remove a config value by dot path").action((path) => runtime.configUnset(path));
8095
+ config.command("get <path>").description("Get a config value by dot path").option("--json", "Output JSON", false).action((path2, opts) => runtime.configGet(path2, opts));
8096
+ config.command("set <path> <value>").description("Set a config value by dot path").option("--json", "Parse value as JSON", false).action((path2, value, opts) => runtime.configSet(path2, value, opts));
8097
+ config.command("unset <path>").description("Remove a config value by dot path").action((path2) => runtime.configUnset(path2));
7583
8098
  var secrets = program.command("secrets").description("Manage secrets refs/providers");
7584
8099
  secrets.command("audit").description("Audit secret refs resolution status").option("--json", "Output JSON", false).option("--strict", "Exit non-zero when unresolved refs exist", false).action((opts) => runtime.secretsAudit(opts));
7585
8100
  secrets.command("configure").description("Configure a secret provider alias").requiredOption("--provider <alias>", "Provider alias").option("--source <source>", "Provider source (env|file|exec)").option("--prefix <prefix>", "Env key prefix (env source)").option("--path <path>", "Secret JSON file path (file source)").option("--command <command>", "Command for exec source").option(