nextclaw 0.10.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.
- package/README.md +2 -0
- package/dist/cli/index.js +2661 -753
- package/package.json +9 -5
- package/templates/USAGE.md +57 -0
- package/ui-dist/assets/ChannelsList-C7F_As4r.js +1 -0
- package/ui-dist/assets/ChatPage-Oo7-OUsx.js +37 -0
- package/ui-dist/assets/{DocBrowser-B9ws5JL7.js → DocBrowser-Dsd8Dlq8.js} +1 -1
- package/ui-dist/assets/{LogoBadge-DvGAzkZ3.js → LogoBadge-2ChEc_oz.js} +1 -1
- package/ui-dist/assets/MarketplacePage-BXck6-X3.js +49 -0
- package/ui-dist/assets/{ModelConfig-BL_HsOsm.js → ModelConfig-CgHRSD0b.js} +1 -1
- package/ui-dist/assets/ProvidersList-PPfZucvS.js +1 -0
- package/ui-dist/assets/RuntimeConfig-ClLEKNTN.js +1 -0
- package/ui-dist/assets/{SearchConfig-BhaI0fUf.js → SearchConfig-CuXVCbrf.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-CFoimOh9.js → SecretsConfig-udJz6Ake.js} +2 -2
- package/ui-dist/assets/SessionsConfig-C1XnFfiC.js +2 -0
- package/ui-dist/assets/{session-run-status-TkIuGbVw.js → chat-message-BETwXLD4.js} +3 -3
- package/ui-dist/assets/{index-uMsNsQX6.js → index-COJdlL0e.js} +1 -1
- package/ui-dist/assets/index-CsvP4CER.js +8 -0
- package/ui-dist/assets/index-D-bXl7qL.css +1 -0
- package/ui-dist/assets/{label-D8ly4a2P.js → label-BGL-ztxh.js} +1 -1
- package/ui-dist/assets/{page-layout-BSYfvwbp.js → page-layout-aw88k7tG.js} +1 -1
- package/ui-dist/assets/popover-DyEvzhmV.js +1 -0
- package/ui-dist/assets/security-config-BuPAQn82.js +1 -0
- package/ui-dist/assets/skeleton-drzO_tdU.js +1 -0
- package/ui-dist/assets/{switch-Ce_g9lpN.js → switch-BK8jIzto.js} +1 -1
- package/ui-dist/assets/{tabs-custom-Cf5azvT5.js → tabs-custom-Da3cEOji.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-A8Ek8Wu7.js → useConfirmDialog-z0CE92iS.js} +2 -2
- package/ui-dist/assets/{vendor-B7ozqnFC.js → vendor-CkJHmX1g.js} +65 -70
- package/ui-dist/index.html +3 -3
- package/ui-dist/assets/ChannelsList-DF2U-LY1.js +0 -1
- package/ui-dist/assets/ChatPage-BX39y0U5.js +0 -36
- package/ui-dist/assets/MarketplacePage-DG5mHWJ8.js +0 -49
- package/ui-dist/assets/ProvidersList-CH5z00YT.js +0 -1
- package/ui-dist/assets/RuntimeConfig-BplBgkwo.js +0 -1
- package/ui-dist/assets/SessionsConfig-BHTAYn9T.js +0 -2
- package/ui-dist/assets/index-BLeJkJ0o.css +0 -1
- package/ui-dist/assets/index-DK4TS5ev.js +0 -8
- package/ui-dist/assets/index-X5J6Mm--.js +0 -1
- package/ui-dist/assets/security-config-DlKEYHNN.js +0 -1
- package/ui-dist/assets/skeleton-CWbsNx2h.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
|
|
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
|
|
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
|
|
25
|
+
getPluginChannelBindings as getPluginChannelBindings4,
|
|
26
26
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
|
|
27
27
|
setPluginRuntimeBridge as setPluginRuntimeBridge2
|
|
28
28
|
} from "@nextclaw/openclaw-compat";
|
|
29
|
-
import { existsSync as
|
|
30
|
-
import { join as join7, resolve as
|
|
29
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
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
|
|
119
|
-
mkdirSync(resolve(
|
|
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(
|
|
124
|
+
writeFileSync(path2, `${JSON.stringify(file, null, 2)}
|
|
125
125
|
`, "utf-8");
|
|
126
|
-
return
|
|
126
|
+
return path2;
|
|
127
127
|
}
|
|
128
128
|
async function consumeRestartSentinel() {
|
|
129
|
-
const
|
|
130
|
-
if (!existsSync(
|
|
129
|
+
const path2 = resolveRestartSentinelPath();
|
|
130
|
+
if (!existsSync(path2)) {
|
|
131
131
|
return null;
|
|
132
132
|
}
|
|
133
133
|
try {
|
|
134
|
-
const raw = readFileSync(
|
|
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(
|
|
137
|
+
rmSync(path2, { force: true });
|
|
138
138
|
return null;
|
|
139
139
|
}
|
|
140
|
-
rmSync(
|
|
140
|
+
rmSync(path2, { force: true });
|
|
141
141
|
return parsed;
|
|
142
142
|
} catch {
|
|
143
|
-
rmSync(
|
|
143
|
+
rmSync(path2, { force: true });
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -186,24 +186,236 @@ function parseSessionKey(sessionKey) {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// src/cli/skills/marketplace.ts
|
|
189
|
-
import { cpSync, existsSync as
|
|
190
|
-
import { basename, dirname, isAbsolute, join, relative, resolve as
|
|
189
|
+
import { cpSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
190
|
+
import { basename, dirname, isAbsolute, join, relative, resolve as resolve3 } from "path";
|
|
191
191
|
import { SkillsLoader } from "@nextclaw/core";
|
|
192
|
+
|
|
193
|
+
// src/cli/skills/marketplace.metadata.ts
|
|
194
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
195
|
+
import { resolve as resolve2 } from "path";
|
|
196
|
+
import { parse as parseYaml } from "yaml";
|
|
197
|
+
var DEFAULT_MARKETPLACE_META_FILENAME = "marketplace.json";
|
|
198
|
+
function parseSkillFrontmatter(raw) {
|
|
199
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
200
|
+
const match = normalized.match(/^---\n([\s\S]*?)\n---/);
|
|
201
|
+
if (!match || !match[1]) {
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
let parsed;
|
|
205
|
+
try {
|
|
206
|
+
parsed = parseYaml(match[1]);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
209
|
+
throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
|
|
210
|
+
}
|
|
211
|
+
if (!isRecord(parsed)) {
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
const summaryI18n = readLocalizedTextMapField(parsed, [["summaryi18n"], ["summary_i18n"]]);
|
|
215
|
+
const descriptionI18n = readLocalizedTextMapField(parsed, [["descriptioni18n"], ["description_i18n"]]);
|
|
216
|
+
const summaryZh = readFrontmatterStringField(parsed, [["summaryzh"], ["summary_zh"]]);
|
|
217
|
+
const descriptionZh = readFrontmatterStringField(parsed, [["descriptionzh"], ["description_zh"]]);
|
|
218
|
+
return {
|
|
219
|
+
name: readFrontmatterStringField(parsed, [["name"]]),
|
|
220
|
+
summary: readFrontmatterStringField(parsed, [["summary"]]),
|
|
221
|
+
summaryI18n: mergeLocalizedTextMap(summaryI18n, { zh: summaryZh }),
|
|
222
|
+
description: readFrontmatterStringField(parsed, [["description"]]),
|
|
223
|
+
descriptionI18n: mergeLocalizedTextMap(descriptionI18n, { zh: descriptionZh }),
|
|
224
|
+
author: readFrontmatterStringField(parsed, [["author"]]),
|
|
225
|
+
tags: readFrontmatterTags(parsed)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function buildLocalizedTextMap(englishText, ...maps) {
|
|
229
|
+
const normalized = mergeLocalizedTextMap(...maps);
|
|
230
|
+
return {
|
|
231
|
+
...normalized ?? {},
|
|
232
|
+
en: englishText
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function readMarketplaceMetadataFile(skillDir, explicitMetaFile) {
|
|
236
|
+
const metadataPath = resolveMarketplaceMetadataPath(skillDir, explicitMetaFile);
|
|
237
|
+
if (!metadataPath) {
|
|
238
|
+
return {};
|
|
239
|
+
}
|
|
240
|
+
let parsed;
|
|
241
|
+
try {
|
|
242
|
+
parsed = JSON.parse(readFileSync2(metadataPath, "utf8"));
|
|
243
|
+
} catch (error) {
|
|
244
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
245
|
+
throw new Error(`Invalid marketplace metadata file: ${metadataPath} (${message})`);
|
|
246
|
+
}
|
|
247
|
+
if (!isRecord(parsed)) {
|
|
248
|
+
throw new Error(`Invalid marketplace metadata file: ${metadataPath} (root must be an object)`);
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
slug: readMetadataString(parsed, "slug"),
|
|
252
|
+
name: readMetadataString(parsed, "name"),
|
|
253
|
+
summary: readMetadataString(parsed, "summary"),
|
|
254
|
+
summaryI18n: readMetadataLocalizedTextMap(parsed, "summaryI18n"),
|
|
255
|
+
description: readMetadataString(parsed, "description"),
|
|
256
|
+
descriptionI18n: readMetadataLocalizedTextMap(parsed, "descriptionI18n"),
|
|
257
|
+
author: readMetadataString(parsed, "author"),
|
|
258
|
+
tags: readMetadataStringArray(parsed, "tags"),
|
|
259
|
+
sourceRepo: readMetadataString(parsed, "sourceRepo"),
|
|
260
|
+
homepage: readMetadataString(parsed, "homepage"),
|
|
261
|
+
publishedAt: readMetadataString(parsed, "publishedAt"),
|
|
262
|
+
updatedAt: readMetadataString(parsed, "updatedAt")
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function resolveMarketplaceMetadataPath(skillDir, explicitMetaFile) {
|
|
266
|
+
const resolved = explicitMetaFile?.trim() ? resolve2(explicitMetaFile) : resolve2(skillDir, DEFAULT_MARKETPLACE_META_FILENAME);
|
|
267
|
+
return existsSync2(resolved) ? resolved : void 0;
|
|
268
|
+
}
|
|
269
|
+
function mergeLocalizedTextMap(...maps) {
|
|
270
|
+
const localized = {};
|
|
271
|
+
for (const map of maps) {
|
|
272
|
+
for (const [locale, text] of Object.entries(map ?? {})) {
|
|
273
|
+
const normalizedText = typeof text === "string" ? text.trim() : "";
|
|
274
|
+
if (!normalizedText) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
localized[normalizeLocaleTag(locale)] = normalizedText;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return Object.keys(localized).length > 0 ? localized : void 0;
|
|
281
|
+
}
|
|
282
|
+
function readMetadataString(record, fieldName) {
|
|
283
|
+
const value = record[fieldName];
|
|
284
|
+
if (value == null) {
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
if (typeof value !== "string") {
|
|
288
|
+
throw new Error(`Invalid marketplace metadata field: ${fieldName} must be a string`);
|
|
289
|
+
}
|
|
290
|
+
const normalized = value.trim();
|
|
291
|
+
return normalized || void 0;
|
|
292
|
+
}
|
|
293
|
+
function readMetadataStringArray(record, fieldName) {
|
|
294
|
+
const value = record[fieldName];
|
|
295
|
+
if (value == null) {
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
if (!Array.isArray(value)) {
|
|
299
|
+
throw new Error(`Invalid marketplace metadata field: ${fieldName} must be an array`);
|
|
300
|
+
}
|
|
301
|
+
const tags = value.map((entry, index) => {
|
|
302
|
+
if (typeof entry !== "string") {
|
|
303
|
+
throw new Error(`Invalid marketplace metadata field: ${fieldName}[${index}] must be a string`);
|
|
304
|
+
}
|
|
305
|
+
return entry.trim();
|
|
306
|
+
}).filter(Boolean);
|
|
307
|
+
return tags.length > 0 ? tags : void 0;
|
|
308
|
+
}
|
|
309
|
+
function readMetadataLocalizedTextMap(record, fieldName) {
|
|
310
|
+
const value = record[fieldName];
|
|
311
|
+
if (value == null) {
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
if (!isRecord(value)) {
|
|
315
|
+
throw new Error(`Invalid marketplace metadata field: ${fieldName} must be an object`);
|
|
316
|
+
}
|
|
317
|
+
const localized = {};
|
|
318
|
+
for (const [locale, text] of Object.entries(value)) {
|
|
319
|
+
if (typeof text !== "string") {
|
|
320
|
+
throw new Error(`Invalid marketplace metadata field: ${fieldName}.${locale} must be a string`);
|
|
321
|
+
}
|
|
322
|
+
const normalized = text.trim();
|
|
323
|
+
if (!normalized) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
localized[normalizeLocaleTag(locale)] = normalized;
|
|
327
|
+
}
|
|
328
|
+
return Object.keys(localized).length > 0 ? localized : void 0;
|
|
329
|
+
}
|
|
330
|
+
function readFrontmatterStringField(record, keyPaths) {
|
|
331
|
+
for (const keyPath of keyPaths) {
|
|
332
|
+
const value = readNestedFrontmatterValue(record, keyPath);
|
|
333
|
+
if (typeof value !== "string") {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const normalized = value.trim();
|
|
337
|
+
if (normalized) {
|
|
338
|
+
return normalized;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
function readLocalizedTextMapField(record, keyPaths) {
|
|
344
|
+
for (const keyPath of keyPaths) {
|
|
345
|
+
const value = readNestedFrontmatterValue(record, keyPath);
|
|
346
|
+
if (!isRecord(value)) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const normalized = {};
|
|
350
|
+
for (const [locale, text] of Object.entries(value)) {
|
|
351
|
+
if (typeof text !== "string") {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const trimmed = text.trim();
|
|
355
|
+
if (!trimmed) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
normalized[normalizeLocaleTag(locale)] = trimmed;
|
|
359
|
+
}
|
|
360
|
+
if (Object.keys(normalized).length > 0) {
|
|
361
|
+
return normalized;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
function readFrontmatterTags(record) {
|
|
367
|
+
const rawTags = readNestedFrontmatterValue(record, ["tags"]);
|
|
368
|
+
if (Array.isArray(rawTags)) {
|
|
369
|
+
const tags2 = rawTags.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
|
|
370
|
+
return tags2.length > 0 ? tags2 : void 0;
|
|
371
|
+
}
|
|
372
|
+
if (typeof rawTags !== "string") {
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
const tags = rawTags.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
376
|
+
return tags.length > 0 ? tags : void 0;
|
|
377
|
+
}
|
|
378
|
+
function readNestedFrontmatterValue(record, keyPath) {
|
|
379
|
+
let current = record;
|
|
380
|
+
for (const rawKey of keyPath) {
|
|
381
|
+
if (!isRecord(current)) {
|
|
382
|
+
return void 0;
|
|
383
|
+
}
|
|
384
|
+
const normalizedKey = normalizeFrontmatterKey(rawKey);
|
|
385
|
+
const matchingKey = Object.keys(current).find((candidate) => normalizeFrontmatterKey(candidate) === normalizedKey);
|
|
386
|
+
if (!matchingKey) {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
current = current[matchingKey];
|
|
390
|
+
}
|
|
391
|
+
return current;
|
|
392
|
+
}
|
|
393
|
+
function normalizeFrontmatterKey(raw) {
|
|
394
|
+
return raw.replace(/[-_]/g, "").toLowerCase();
|
|
395
|
+
}
|
|
396
|
+
function normalizeLocaleTag(raw) {
|
|
397
|
+
return raw.trim().toLowerCase();
|
|
398
|
+
}
|
|
399
|
+
function isRecord(value) {
|
|
400
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/cli/skills/marketplace.ts
|
|
192
404
|
var DEFAULT_MARKETPLACE_API_BASE = "https://marketplace-api.nextclaw.io";
|
|
193
405
|
async function installMarketplaceSkill(options) {
|
|
194
406
|
const slug = validateSkillSlug(options.slug.trim(), "slug");
|
|
195
|
-
const workdir =
|
|
196
|
-
if (!
|
|
407
|
+
const workdir = resolve3(options.workdir);
|
|
408
|
+
if (!existsSync3(workdir)) {
|
|
197
409
|
throw new Error(`Workdir does not exist: ${workdir}`);
|
|
198
410
|
}
|
|
199
411
|
const dirName = options.dir?.trim() || "skills";
|
|
200
|
-
const destinationDir = isAbsolute(dirName) ?
|
|
412
|
+
const destinationDir = isAbsolute(dirName) ? resolve3(dirName, slug) : resolve3(workdir, dirName, slug);
|
|
201
413
|
const skillFile = join(destinationDir, "SKILL.md");
|
|
202
414
|
const apiBase = resolveMarketplaceApiBase(options.apiBaseUrl);
|
|
203
415
|
const item = await fetchMarketplaceSkillItem(apiBase, slug);
|
|
204
416
|
if (item.install.kind === "builtin") {
|
|
205
|
-
if (!options.force &&
|
|
206
|
-
if (
|
|
417
|
+
if (!options.force && existsSync3(destinationDir)) {
|
|
418
|
+
if (existsSync3(skillFile)) {
|
|
207
419
|
return {
|
|
208
420
|
slug,
|
|
209
421
|
destinationDir,
|
|
@@ -213,7 +425,7 @@ async function installMarketplaceSkill(options) {
|
|
|
213
425
|
}
|
|
214
426
|
throw new Error(`Skill directory already exists: ${destinationDir} (use --force)`);
|
|
215
427
|
}
|
|
216
|
-
if (
|
|
428
|
+
if (existsSync3(destinationDir) && options.force) {
|
|
217
429
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
218
430
|
}
|
|
219
431
|
installBuiltinSkill(workdir, destinationDir, slug);
|
|
@@ -224,7 +436,7 @@ async function installMarketplaceSkill(options) {
|
|
|
224
436
|
};
|
|
225
437
|
}
|
|
226
438
|
const filesPayload = await fetchMarketplaceSkillFiles(apiBase, slug);
|
|
227
|
-
if (!options.force &&
|
|
439
|
+
if (!options.force && existsSync3(destinationDir)) {
|
|
228
440
|
const existingDirState = inspectMarketplaceSkillDirectory(destinationDir, filesPayload.files);
|
|
229
441
|
if (existingDirState === "installed") {
|
|
230
442
|
return {
|
|
@@ -240,12 +452,12 @@ async function installMarketplaceSkill(options) {
|
|
|
240
452
|
throw new Error(`Skill directory already exists: ${destinationDir} (use --force)`);
|
|
241
453
|
}
|
|
242
454
|
}
|
|
243
|
-
if (
|
|
455
|
+
if (existsSync3(destinationDir) && options.force) {
|
|
244
456
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
245
457
|
}
|
|
246
458
|
mkdirSync2(destinationDir, { recursive: true });
|
|
247
459
|
for (const file of filesPayload.files) {
|
|
248
|
-
const targetPath =
|
|
460
|
+
const targetPath = resolve3(destinationDir, ...file.path.split("/"));
|
|
249
461
|
const rel = relative(destinationDir, targetPath);
|
|
250
462
|
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
251
463
|
throw new Error(`Invalid marketplace file path: ${file.path}`);
|
|
@@ -254,7 +466,7 @@ async function installMarketplaceSkill(options) {
|
|
|
254
466
|
const bytes = file.contentBase64 ? decodeMarketplaceFileContent(file.path, file.contentBase64) : await fetchMarketplaceSkillFileBlob(apiBase, slug, file);
|
|
255
467
|
writeFileSync2(targetPath, bytes);
|
|
256
468
|
}
|
|
257
|
-
if (!
|
|
469
|
+
if (!existsSync3(join(destinationDir, "SKILL.md"))) {
|
|
258
470
|
throw new Error(`Marketplace skill ${slug} does not include SKILL.md`);
|
|
259
471
|
}
|
|
260
472
|
return {
|
|
@@ -264,7 +476,7 @@ async function installMarketplaceSkill(options) {
|
|
|
264
476
|
};
|
|
265
477
|
}
|
|
266
478
|
function inspectMarketplaceSkillDirectory(destinationDir, files) {
|
|
267
|
-
if (
|
|
479
|
+
if (existsSync3(join(destinationDir, "SKILL.md"))) {
|
|
268
480
|
return "installed";
|
|
269
481
|
}
|
|
270
482
|
const discoveredFiles = collectRelativeFiles(destinationDir);
|
|
@@ -300,28 +512,31 @@ function collectRelativeFiles(rootDir) {
|
|
|
300
512
|
};
|
|
301
513
|
return walk(rootDir) ? output : null;
|
|
302
514
|
}
|
|
303
|
-
function normalizeMarketplaceRelativePath(
|
|
304
|
-
return
|
|
515
|
+
function normalizeMarketplaceRelativePath(path2) {
|
|
516
|
+
return path2.replace(/\\/g, "/");
|
|
305
517
|
}
|
|
306
|
-
function isIgnorableMarketplaceResidue(
|
|
307
|
-
return
|
|
518
|
+
function isIgnorableMarketplaceResidue(path2) {
|
|
519
|
+
return path2 === ".DS_Store";
|
|
308
520
|
}
|
|
309
521
|
async function publishMarketplaceSkill(options) {
|
|
310
|
-
const skillDir =
|
|
311
|
-
if (!
|
|
522
|
+
const skillDir = resolve3(options.skillDir);
|
|
523
|
+
if (!existsSync3(skillDir)) {
|
|
312
524
|
throw new Error(`Skill directory not found: ${skillDir}`);
|
|
313
525
|
}
|
|
314
526
|
const files = collectFiles(skillDir);
|
|
315
527
|
if (!files.some((file) => file.path === "SKILL.md")) {
|
|
316
528
|
throw new Error(`Skill directory must include SKILL.md: ${skillDir}`);
|
|
317
529
|
}
|
|
318
|
-
const parsedFrontmatter = parseSkillFrontmatter(
|
|
319
|
-
const
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
const
|
|
530
|
+
const parsedFrontmatter = parseSkillFrontmatter(readFileSync3(join(skillDir, "SKILL.md"), "utf8"));
|
|
531
|
+
const metadata = readMarketplaceMetadataFile(skillDir, options.metaFile);
|
|
532
|
+
const slug = validateSkillSlug(options.slug?.trim() || metadata.slug || basename(skillDir), "slug");
|
|
533
|
+
const name = options.name?.trim() || metadata.name || parsedFrontmatter.name || slug;
|
|
534
|
+
const description = options.description?.trim() || metadata.description || metadata.descriptionI18n?.en || parsedFrontmatter.description;
|
|
535
|
+
const summary = options.summary?.trim() || metadata.summary || metadata.summaryI18n?.en || parsedFrontmatter.summary || description || `${slug} skill`;
|
|
536
|
+
const summaryI18n = buildLocalizedTextMap(summary, parsedFrontmatter.summaryI18n, metadata.summaryI18n, options.summaryI18n);
|
|
537
|
+
const descriptionI18n = description ? buildLocalizedTextMap(description, parsedFrontmatter.descriptionI18n, metadata.descriptionI18n, options.descriptionI18n) : void 0;
|
|
538
|
+
const author = options.author?.trim() || metadata.author || parsedFrontmatter.author || "nextclaw";
|
|
539
|
+
const tags = normalizeTags(options.tags && options.tags.length > 0 ? options.tags : metadata.tags ?? parsedFrontmatter.tags);
|
|
325
540
|
const apiBase = resolveMarketplaceApiBase(options.apiBaseUrl);
|
|
326
541
|
const token = resolveMarketplaceAdminToken(options.token);
|
|
327
542
|
if (options.requireExisting) {
|
|
@@ -337,13 +552,15 @@ async function publishMarketplaceSkill(options) {
|
|
|
337
552
|
slug,
|
|
338
553
|
name,
|
|
339
554
|
summary,
|
|
555
|
+
summaryI18n,
|
|
340
556
|
description,
|
|
557
|
+
descriptionI18n,
|
|
341
558
|
author,
|
|
342
559
|
tags,
|
|
343
|
-
sourceRepo: options.sourceRepo?.trim() ||
|
|
344
|
-
homepage: options.homepage?.trim() ||
|
|
345
|
-
publishedAt: options.publishedAt?.trim() ||
|
|
346
|
-
updatedAt: options.updatedAt?.trim() ||
|
|
560
|
+
sourceRepo: options.sourceRepo?.trim() || metadata.sourceRepo,
|
|
561
|
+
homepage: options.homepage?.trim() || metadata.homepage,
|
|
562
|
+
publishedAt: options.publishedAt?.trim() || metadata.publishedAt,
|
|
563
|
+
updatedAt: options.updatedAt?.trim() || metadata.updatedAt,
|
|
347
564
|
files
|
|
348
565
|
})
|
|
349
566
|
});
|
|
@@ -372,7 +589,7 @@ function collectFiles(rootDir) {
|
|
|
372
589
|
if (!entry.isFile()) {
|
|
373
590
|
continue;
|
|
374
591
|
}
|
|
375
|
-
const content =
|
|
592
|
+
const content = readFileSync3(absolute);
|
|
376
593
|
output.push({
|
|
377
594
|
path: relativePath,
|
|
378
595
|
contentBase64: content.toString("base64")
|
|
@@ -422,11 +639,11 @@ async function fetchMarketplaceSkillFiles(apiBase, slug) {
|
|
|
422
639
|
const message = payload.error?.message || `marketplace skill file fetch failed: ${response.status}`;
|
|
423
640
|
throw new Error(message);
|
|
424
641
|
}
|
|
425
|
-
if (!
|
|
642
|
+
if (!isRecord2(payload.data) || !Array.isArray(payload.data.files)) {
|
|
426
643
|
throw new Error("Invalid marketplace skill file manifest response");
|
|
427
644
|
}
|
|
428
645
|
const files = payload.data.files.map((entry, index) => {
|
|
429
|
-
if (!
|
|
646
|
+
if (!isRecord2(entry) || typeof entry.path !== "string" || entry.path.trim().length === 0) {
|
|
430
647
|
throw new Error(`Invalid marketplace skill file manifest at index ${index}`);
|
|
431
648
|
}
|
|
432
649
|
const normalized = {
|
|
@@ -456,10 +673,10 @@ async function fetchMarketplaceSkillFileBlob(apiBase, slug, file) {
|
|
|
456
673
|
const arrayBuffer = await response.arrayBuffer();
|
|
457
674
|
return Buffer.from(arrayBuffer);
|
|
458
675
|
}
|
|
459
|
-
function decodeMarketplaceFileContent(
|
|
676
|
+
function decodeMarketplaceFileContent(path2, contentBase64) {
|
|
460
677
|
const normalized = contentBase64.replace(/\s+/g, "");
|
|
461
678
|
if (!normalized || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
|
|
462
|
-
throw new Error(`Invalid marketplace file contentBase64 for path: ${
|
|
679
|
+
throw new Error(`Invalid marketplace file contentBase64 for path: ${path2}`);
|
|
463
680
|
}
|
|
464
681
|
return Buffer.from(normalized, "base64");
|
|
465
682
|
}
|
|
@@ -494,7 +711,7 @@ async function readMarketplaceEnvelope(response) {
|
|
|
494
711
|
} catch {
|
|
495
712
|
throw new Error(`Invalid marketplace response: ${response.status}`);
|
|
496
713
|
}
|
|
497
|
-
if (!
|
|
714
|
+
if (!isRecord2(payload) || typeof payload.ok !== "boolean") {
|
|
498
715
|
throw new Error(`Invalid marketplace response shape: ${response.status}`);
|
|
499
716
|
}
|
|
500
717
|
return payload;
|
|
@@ -526,56 +743,17 @@ function normalizeTags(rawTags) {
|
|
|
526
743
|
}
|
|
527
744
|
return output.length > 0 ? output : ["skill"];
|
|
528
745
|
}
|
|
529
|
-
function
|
|
530
|
-
const normalized = raw.replace(/\r\n/g, "\n");
|
|
531
|
-
const match = normalized.match(/^---\n([\s\S]*?)\n---/);
|
|
532
|
-
if (!match || !match[1]) {
|
|
533
|
-
return {};
|
|
534
|
-
}
|
|
535
|
-
const metadata = /* @__PURE__ */ new Map();
|
|
536
|
-
for (const line of match[1].split("\n")) {
|
|
537
|
-
const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.+)$/);
|
|
538
|
-
if (!parsed) {
|
|
539
|
-
continue;
|
|
540
|
-
}
|
|
541
|
-
const key = parsed[1]?.trim().toLowerCase();
|
|
542
|
-
const value = parsed[2]?.trim();
|
|
543
|
-
if (!key || !value) {
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
metadata.set(key, trimYamlString(value));
|
|
547
|
-
}
|
|
548
|
-
const rawTags = metadata.get("tags");
|
|
549
|
-
let tags;
|
|
550
|
-
if (rawTags) {
|
|
551
|
-
if (rawTags.startsWith("[") && rawTags.endsWith("]")) {
|
|
552
|
-
tags = rawTags.slice(1, -1).split(",").map((entry) => trimYamlString(entry)).filter(Boolean);
|
|
553
|
-
} else {
|
|
554
|
-
tags = rawTags.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return {
|
|
558
|
-
name: metadata.get("name"),
|
|
559
|
-
summary: metadata.get("summary"),
|
|
560
|
-
description: metadata.get("description"),
|
|
561
|
-
author: metadata.get("author"),
|
|
562
|
-
tags
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
function trimYamlString(raw) {
|
|
566
|
-
return raw.replace(/^['"]/, "").replace(/['"]$/, "").trim();
|
|
567
|
-
}
|
|
568
|
-
function isRecord(value) {
|
|
746
|
+
function isRecord2(value) {
|
|
569
747
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
570
748
|
}
|
|
571
749
|
|
|
572
750
|
// src/cli/update/runner.ts
|
|
573
751
|
import { spawnSync } from "child_process";
|
|
574
|
-
import { resolve as
|
|
752
|
+
import { resolve as resolve5 } from "path";
|
|
575
753
|
|
|
576
754
|
// src/cli/utils.ts
|
|
577
|
-
import { existsSync as
|
|
578
|
-
import { join as join2, resolve as
|
|
755
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync as rmSync3 } from "fs";
|
|
756
|
+
import { join as join2, resolve as resolve4 } from "path";
|
|
579
757
|
import { spawn } from "child_process";
|
|
580
758
|
import { isIP } from "net";
|
|
581
759
|
import { fileURLToPath } from "url";
|
|
@@ -628,33 +806,33 @@ function buildServeArgs(options) {
|
|
|
628
806
|
return [cliPath, "serve", "--ui-port", String(options.uiPort)];
|
|
629
807
|
}
|
|
630
808
|
function readServiceState() {
|
|
631
|
-
const
|
|
632
|
-
if (!
|
|
809
|
+
const path2 = resolveServiceStatePath();
|
|
810
|
+
if (!existsSync4(path2)) {
|
|
633
811
|
return null;
|
|
634
812
|
}
|
|
635
813
|
try {
|
|
636
|
-
const raw =
|
|
814
|
+
const raw = readFileSync4(path2, "utf-8");
|
|
637
815
|
return JSON.parse(raw);
|
|
638
816
|
} catch {
|
|
639
817
|
return null;
|
|
640
818
|
}
|
|
641
819
|
}
|
|
642
820
|
function writeServiceState(state) {
|
|
643
|
-
const
|
|
644
|
-
mkdirSync3(
|
|
645
|
-
writeFileSync3(
|
|
821
|
+
const path2 = resolveServiceStatePath();
|
|
822
|
+
mkdirSync3(resolve4(path2, ".."), { recursive: true });
|
|
823
|
+
writeFileSync3(path2, JSON.stringify(state, null, 2));
|
|
646
824
|
}
|
|
647
825
|
function clearServiceState() {
|
|
648
|
-
const
|
|
649
|
-
if (
|
|
650
|
-
rmSync3(
|
|
826
|
+
const path2 = resolveServiceStatePath();
|
|
827
|
+
if (existsSync4(path2)) {
|
|
828
|
+
rmSync3(path2, { force: true });
|
|
651
829
|
}
|
|
652
830
|
}
|
|
653
831
|
function resolveServiceStatePath() {
|
|
654
|
-
return
|
|
832
|
+
return resolve4(getDataDir2(), "run", "service.json");
|
|
655
833
|
}
|
|
656
834
|
function resolveServiceLogPath() {
|
|
657
|
-
return
|
|
835
|
+
return resolve4(getDataDir2(), "logs", "service.log");
|
|
658
836
|
}
|
|
659
837
|
function isProcessRunning(pid) {
|
|
660
838
|
try {
|
|
@@ -670,7 +848,7 @@ async function waitForExit(pid, timeoutMs) {
|
|
|
670
848
|
if (!isProcessRunning(pid)) {
|
|
671
849
|
return true;
|
|
672
850
|
}
|
|
673
|
-
await new Promise((
|
|
851
|
+
await new Promise((resolve13) => setTimeout(resolve13, 200));
|
|
674
852
|
}
|
|
675
853
|
return !isProcessRunning(pid);
|
|
676
854
|
}
|
|
@@ -680,8 +858,8 @@ function resolveUiStaticDir() {
|
|
|
680
858
|
if (envDir) {
|
|
681
859
|
candidates.push(envDir);
|
|
682
860
|
}
|
|
683
|
-
const cliDir =
|
|
684
|
-
const pkgRoot =
|
|
861
|
+
const cliDir = resolve4(fileURLToPath(new URL(".", import.meta.url)));
|
|
862
|
+
const pkgRoot = resolve4(cliDir, "..", "..");
|
|
685
863
|
candidates.push(join2(pkgRoot, "ui-dist"));
|
|
686
864
|
candidates.push(join2(pkgRoot, "ui"));
|
|
687
865
|
candidates.push(join2(pkgRoot, "..", "ui-dist"));
|
|
@@ -693,7 +871,7 @@ function resolveUiStaticDir() {
|
|
|
693
871
|
candidates.push(join2(pkgRoot, "..", "..", "packages", "nextclaw-ui", "dist"));
|
|
694
872
|
candidates.push(join2(pkgRoot, "..", "..", "nextclaw-ui", "dist"));
|
|
695
873
|
for (const dir of candidates) {
|
|
696
|
-
if (
|
|
874
|
+
if (existsSync4(join2(dir, "index.html"))) {
|
|
697
875
|
return dir;
|
|
698
876
|
}
|
|
699
877
|
}
|
|
@@ -744,7 +922,7 @@ function findExecutableOnPath(binary, env = process.env, platform = process.plat
|
|
|
744
922
|
return null;
|
|
745
923
|
}
|
|
746
924
|
if (target.includes("/") || target.includes("\\")) {
|
|
747
|
-
return
|
|
925
|
+
return existsSync4(target) ? target : null;
|
|
748
926
|
}
|
|
749
927
|
const rawPath = env.PATH ?? env.Path ?? env.path ?? "";
|
|
750
928
|
if (!rawPath.trim()) {
|
|
@@ -754,7 +932,7 @@ function findExecutableOnPath(binary, env = process.env, platform = process.plat
|
|
|
754
932
|
if (entries.length === 0) {
|
|
755
933
|
return null;
|
|
756
934
|
}
|
|
757
|
-
const checkCandidates = (candidate) =>
|
|
935
|
+
const checkCandidates = (candidate) => existsSync4(candidate) ? candidate : null;
|
|
758
936
|
for (const dir of entries) {
|
|
759
937
|
const direct = checkCandidates(join2(dir, target));
|
|
760
938
|
if (direct) {
|
|
@@ -776,12 +954,12 @@ function which(binary) {
|
|
|
776
954
|
return findExecutableOnPath(binary) !== null;
|
|
777
955
|
}
|
|
778
956
|
function resolveVersionFromPackageTree(startDir, expectedName) {
|
|
779
|
-
let current =
|
|
957
|
+
let current = resolve4(startDir);
|
|
780
958
|
while (current.length > 0) {
|
|
781
959
|
const pkgPath = join2(current, "package.json");
|
|
782
|
-
if (
|
|
960
|
+
if (existsSync4(pkgPath)) {
|
|
783
961
|
try {
|
|
784
|
-
const raw =
|
|
962
|
+
const raw = readFileSync4(pkgPath, "utf-8");
|
|
785
963
|
const parsed = JSON.parse(raw);
|
|
786
964
|
if (typeof parsed.version === "string") {
|
|
787
965
|
if (!expectedName || parsed.name === expectedName) {
|
|
@@ -791,7 +969,7 @@ function resolveVersionFromPackageTree(startDir, expectedName) {
|
|
|
791
969
|
} catch {
|
|
792
970
|
}
|
|
793
971
|
}
|
|
794
|
-
const parent =
|
|
972
|
+
const parent = resolve4(current, "..");
|
|
795
973
|
if (parent === current) {
|
|
796
974
|
break;
|
|
797
975
|
}
|
|
@@ -800,7 +978,7 @@ function resolveVersionFromPackageTree(startDir, expectedName) {
|
|
|
800
978
|
return null;
|
|
801
979
|
}
|
|
802
980
|
function getPackageVersion() {
|
|
803
|
-
const cliDir =
|
|
981
|
+
const cliDir = resolve4(fileURLToPath(new URL(".", import.meta.url)));
|
|
804
982
|
return resolveVersionFromPackageTree(cliDir, "nextclaw") ?? resolveVersionFromPackageTree(cliDir) ?? getCorePackageVersion();
|
|
805
983
|
}
|
|
806
984
|
function printAgentResponse(response) {
|
|
@@ -809,8 +987,8 @@ function printAgentResponse(response) {
|
|
|
809
987
|
async function prompt(rl, question) {
|
|
810
988
|
rl.setPrompt(question);
|
|
811
989
|
rl.prompt();
|
|
812
|
-
return new Promise((
|
|
813
|
-
rl.once("line", (line) =>
|
|
990
|
+
return new Promise((resolve13) => {
|
|
991
|
+
rl.once("line", (line) => resolve13(line));
|
|
814
992
|
});
|
|
815
993
|
}
|
|
816
994
|
|
|
@@ -845,7 +1023,7 @@ function runSelfUpdate(options = {}) {
|
|
|
845
1023
|
return { ok: result.status === 0, code: result.status };
|
|
846
1024
|
};
|
|
847
1025
|
if (updateCommand) {
|
|
848
|
-
const cwd = options.cwd ?
|
|
1026
|
+
const cwd = options.cwd ? resolve5(options.cwd) : process.cwd();
|
|
849
1027
|
const shellCommand = resolveShellCommand(updateCommand);
|
|
850
1028
|
const ok = runStep(shellCommand.cmd, shellCommand.args, cwd);
|
|
851
1029
|
if (!ok.ok) {
|
|
@@ -866,168 +1044,546 @@ function runSelfUpdate(options = {}) {
|
|
|
866
1044
|
|
|
867
1045
|
// src/cli/commands/plugins.ts
|
|
868
1046
|
import {
|
|
869
|
-
|
|
870
|
-
buildPluginStatusReport,
|
|
871
|
-
disablePluginInConfig,
|
|
872
|
-
enablePluginInConfig,
|
|
873
|
-
installPluginFromNpmSpec,
|
|
874
|
-
installPluginFromPath,
|
|
1047
|
+
buildPluginStatusReport as buildPluginStatusReport2,
|
|
875
1048
|
loadOpenClawPlugins,
|
|
876
|
-
|
|
877
|
-
resolveUninstallDirectoryTarget,
|
|
878
|
-
uninstallPlugin
|
|
1049
|
+
resolveUninstallDirectoryTargets
|
|
879
1050
|
} from "@nextclaw/openclaw-compat";
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
saveConfig,
|
|
883
|
-
getWorkspacePath,
|
|
884
|
-
expandHome
|
|
885
|
-
} from "@nextclaw/core";
|
|
1051
|
+
|
|
1052
|
+
// src/cli/commands/plugin-command-utils.ts
|
|
886
1053
|
import { builtinProviderIds } from "@nextclaw/runtime";
|
|
887
|
-
import { createInterface } from "readline";
|
|
888
|
-
import { existsSync as existsSync4 } from "fs";
|
|
889
|
-
import { resolve as resolve5 } from "path";
|
|
890
1054
|
var RESERVED_PROVIDER_IDS = builtinProviderIds();
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
],
|
|
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],
|
|
914
1077
|
reservedChannelIds: [],
|
|
915
1078
|
reservedProviderIds: RESERVED_PROVIDER_IDS,
|
|
916
1079
|
reservedEngineKinds: ["native"],
|
|
917
|
-
|
|
918
|
-
info: (message) => console.log(message),
|
|
919
|
-
warn: (message) => console.warn(message),
|
|
920
|
-
error: (message) => console.error(message),
|
|
921
|
-
debug: (message) => console.debug(message)
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
function toExtensionRegistry(pluginRegistry) {
|
|
926
|
-
return {
|
|
927
|
-
tools: pluginRegistry.tools.map((tool) => ({
|
|
928
|
-
extensionId: tool.pluginId,
|
|
929
|
-
factory: tool.factory,
|
|
930
|
-
names: tool.names,
|
|
931
|
-
optional: tool.optional,
|
|
932
|
-
source: tool.source
|
|
933
|
-
})),
|
|
934
|
-
channels: pluginRegistry.channels.map((channel) => ({
|
|
935
|
-
extensionId: channel.pluginId,
|
|
936
|
-
channel: channel.channel,
|
|
937
|
-
source: channel.source
|
|
938
|
-
})),
|
|
939
|
-
engines: pluginRegistry.engines.map((engine) => ({
|
|
940
|
-
extensionId: engine.pluginId,
|
|
941
|
-
kind: engine.kind,
|
|
942
|
-
factory: engine.factory,
|
|
943
|
-
source: engine.source
|
|
944
|
-
})),
|
|
945
|
-
diagnostics: pluginRegistry.diagnostics.map((diag) => ({
|
|
946
|
-
level: diag.level,
|
|
947
|
-
message: diag.message,
|
|
948
|
-
extensionId: diag.pluginId,
|
|
949
|
-
source: diag.source
|
|
950
|
-
}))
|
|
1080
|
+
reservedNcpAgentRuntimeKinds: ["native"]
|
|
951
1081
|
};
|
|
952
1082
|
}
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
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(", ")}`);
|
|
962
1098
|
}
|
|
963
1099
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
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;
|
|
971
1137
|
}
|
|
972
|
-
|
|
1138
|
+
const secondAtIndex = trimmed.indexOf("@", slashIndex + 1);
|
|
1139
|
+
return secondAtIndex < 0 ? trimmed : trimmed.slice(0, secondAtIndex);
|
|
973
1140
|
}
|
|
974
|
-
|
|
975
|
-
return
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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()) {
|
|
983
1152
|
continue;
|
|
984
1153
|
}
|
|
985
|
-
const
|
|
986
|
-
|
|
1154
|
+
const packageDir = path.join(workspaceExtensionsDir, entry.name);
|
|
1155
|
+
const pkg = readJsonFile(path.join(packageDir, "package.json"));
|
|
1156
|
+
if (!pkg || !hasOpenClawExtensions(pkg)) {
|
|
987
1157
|
continue;
|
|
988
1158
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
}
|
|
1159
|
+
const packageName = readString(pkg.name);
|
|
1160
|
+
if (!packageName?.startsWith("@nextclaw/")) {
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
packages.push({
|
|
1164
|
+
packageName,
|
|
1165
|
+
dir: packageDir
|
|
1166
|
+
});
|
|
993
1167
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
var PluginCommands = class {
|
|
1001
|
-
constructor() {
|
|
1168
|
+
return packages;
|
|
1169
|
+
};
|
|
1170
|
+
var resolveDevFirstPartyPluginLoadPaths = (config2, workspaceExtensionsDir) => {
|
|
1171
|
+
const rootDir = workspaceExtensionsDir?.trim();
|
|
1172
|
+
if (!rootDir) {
|
|
1173
|
+
return [];
|
|
1002
1174
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
if (opts.json) {
|
|
1015
|
-
console.log(
|
|
1016
|
-
JSON.stringify(
|
|
1017
|
-
{
|
|
1018
|
-
workspaceDir,
|
|
1019
|
-
plugins: list,
|
|
1020
|
-
diagnostics: report.diagnostics
|
|
1021
|
-
},
|
|
1022
|
-
null,
|
|
1023
|
-
2
|
|
1024
|
-
)
|
|
1025
|
-
);
|
|
1026
|
-
return;
|
|
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;
|
|
1027
1186
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
|
1252
|
+
import {
|
|
1253
|
+
addPluginLoadPath,
|
|
1254
|
+
buildPluginStatusReport,
|
|
1255
|
+
disablePluginInConfig,
|
|
1256
|
+
enablePluginInConfig,
|
|
1257
|
+
installPluginFromNpmSpec,
|
|
1258
|
+
installPluginFromPath,
|
|
1259
|
+
recordPluginInstall,
|
|
1260
|
+
uninstallPlugin
|
|
1261
|
+
} from "@nextclaw/openclaw-compat";
|
|
1262
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1263
|
+
import { resolve as resolve6 } from "path";
|
|
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
|
|
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);
|
|
1451
|
+
return loadOpenClawPlugins({
|
|
1452
|
+
config: configWithDevPluginPaths,
|
|
1453
|
+
workspaceDir,
|
|
1454
|
+
excludeRoots: excludedRoots,
|
|
1455
|
+
...buildReservedPluginLoadOptions(),
|
|
1456
|
+
logger: {
|
|
1457
|
+
info: (message) => console.log(message),
|
|
1458
|
+
warn: (message) => console.warn(message),
|
|
1459
|
+
error: (message) => console.error(message),
|
|
1460
|
+
debug: (message) => console.debug(message)
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
function toExtensionRegistry(pluginRegistry) {
|
|
1465
|
+
return {
|
|
1466
|
+
tools: pluginRegistry.tools.map((tool) => ({
|
|
1467
|
+
extensionId: tool.pluginId,
|
|
1468
|
+
factory: tool.factory,
|
|
1469
|
+
names: tool.names,
|
|
1470
|
+
optional: tool.optional,
|
|
1471
|
+
source: tool.source
|
|
1472
|
+
})),
|
|
1473
|
+
channels: pluginRegistry.channels.map((channel) => ({
|
|
1474
|
+
extensionId: channel.pluginId,
|
|
1475
|
+
channel: channel.channel,
|
|
1476
|
+
source: channel.source
|
|
1477
|
+
})),
|
|
1478
|
+
engines: pluginRegistry.engines.map((engine) => ({
|
|
1479
|
+
extensionId: engine.pluginId,
|
|
1480
|
+
kind: engine.kind,
|
|
1481
|
+
factory: engine.factory,
|
|
1482
|
+
source: engine.source
|
|
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
|
+
})),
|
|
1491
|
+
diagnostics: pluginRegistry.diagnostics.map((diag) => ({
|
|
1492
|
+
level: diag.level,
|
|
1493
|
+
message: diag.message,
|
|
1494
|
+
extensionId: diag.pluginId,
|
|
1495
|
+
source: diag.source
|
|
1496
|
+
}))
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
function logPluginDiagnostics(registry) {
|
|
1500
|
+
for (const diag of registry.diagnostics) {
|
|
1501
|
+
const prefix = diag.pluginId ? `${diag.pluginId}: ` : "";
|
|
1502
|
+
const text = `${prefix}${diag.message}`;
|
|
1503
|
+
if (diag.level === "error") {
|
|
1504
|
+
console.error(`[plugins] ${text}`);
|
|
1505
|
+
} else {
|
|
1506
|
+
console.warn(`[plugins] ${text}`);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
function toPluginConfigView(config2, bindings) {
|
|
1511
|
+
const view = JSON.parse(JSON.stringify(config2));
|
|
1512
|
+
const channels2 = view.channels && typeof view.channels === "object" && !Array.isArray(view.channels) ? { ...view.channels } : {};
|
|
1513
|
+
for (const binding of bindings) {
|
|
1514
|
+
const pluginConfig = config2.plugins.entries?.[binding.pluginId]?.config;
|
|
1515
|
+
if (!pluginConfig || typeof pluginConfig !== "object" || Array.isArray(pluginConfig)) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
channels2[binding.channelId] = JSON.parse(JSON.stringify(pluginConfig));
|
|
1519
|
+
}
|
|
1520
|
+
view.channels = channels2;
|
|
1521
|
+
return view;
|
|
1522
|
+
}
|
|
1523
|
+
function mergePluginConfigView(baseConfig, pluginViewConfig, bindings) {
|
|
1524
|
+
const next = JSON.parse(JSON.stringify(baseConfig));
|
|
1525
|
+
const pluginChannels = pluginViewConfig.channels && typeof pluginViewConfig.channels === "object" && !Array.isArray(pluginViewConfig.channels) ? pluginViewConfig.channels : {};
|
|
1526
|
+
const entries = { ...next.plugins.entries ?? {} };
|
|
1527
|
+
for (const binding of bindings) {
|
|
1528
|
+
if (!Object.prototype.hasOwnProperty.call(pluginChannels, binding.channelId)) {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
const channelConfig = pluginChannels[binding.channelId];
|
|
1532
|
+
if (!channelConfig || typeof channelConfig !== "object" || Array.isArray(channelConfig)) {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
entries[binding.pluginId] = {
|
|
1536
|
+
...entries[binding.pluginId] ?? {},
|
|
1537
|
+
config: channelConfig
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
next.plugins = {
|
|
1541
|
+
...next.plugins,
|
|
1542
|
+
entries
|
|
1543
|
+
};
|
|
1544
|
+
return next;
|
|
1545
|
+
}
|
|
1546
|
+
var PluginCommands = class {
|
|
1547
|
+
constructor() {
|
|
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
|
+
}
|
|
1561
|
+
pluginsList(opts = {}) {
|
|
1562
|
+
const config2 = loadConfig2();
|
|
1563
|
+
const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
|
|
1564
|
+
const report = buildPluginStatusReport2({
|
|
1565
|
+
config: config2,
|
|
1566
|
+
workspaceDir,
|
|
1567
|
+
...buildReservedPluginLoadOptions()
|
|
1568
|
+
});
|
|
1569
|
+
const list = opts.enabled ? report.plugins.filter((plugin) => plugin.status === "loaded") : report.plugins;
|
|
1570
|
+
if (opts.json) {
|
|
1571
|
+
console.log(
|
|
1572
|
+
JSON.stringify(
|
|
1573
|
+
{
|
|
1574
|
+
workspaceDir,
|
|
1575
|
+
plugins: list,
|
|
1576
|
+
diagnostics: report.diagnostics
|
|
1577
|
+
},
|
|
1578
|
+
null,
|
|
1579
|
+
2
|
|
1580
|
+
)
|
|
1581
|
+
);
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
if (list.length === 0) {
|
|
1585
|
+
console.log("No plugins discovered.");
|
|
1586
|
+
return;
|
|
1031
1587
|
}
|
|
1032
1588
|
for (const plugin of list) {
|
|
1033
1589
|
const status = plugin.status === "loaded" ? "loaded" : plugin.status === "disabled" ? "disabled" : "error";
|
|
@@ -1043,17 +1599,10 @@ var PluginCommands = class {
|
|
|
1043
1599
|
if (plugin.version) {
|
|
1044
1600
|
console.log(` version: ${plugin.version}`);
|
|
1045
1601
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
console.log(` channels: ${plugin.channelIds.join(", ")}`);
|
|
1051
|
-
}
|
|
1052
|
-
if (plugin.providerIds.length > 0) {
|
|
1053
|
-
console.log(` providers: ${plugin.providerIds.join(", ")}`);
|
|
1054
|
-
}
|
|
1055
|
-
if (plugin.engineKinds.length > 0) {
|
|
1056
|
-
console.log(` engines: ${plugin.engineKinds.join(", ")}`);
|
|
1602
|
+
const capabilityLines = [];
|
|
1603
|
+
appendPluginCapabilityLines(capabilityLines, plugin);
|
|
1604
|
+
for (const line of capabilityLines) {
|
|
1605
|
+
console.log(` ${line.toLowerCase()}`);
|
|
1057
1606
|
}
|
|
1058
1607
|
if (plugin.error) {
|
|
1059
1608
|
console.log(` error: ${plugin.error}`);
|
|
@@ -1062,14 +1611,12 @@ var PluginCommands = class {
|
|
|
1062
1611
|
}
|
|
1063
1612
|
}
|
|
1064
1613
|
pluginsInfo(id, opts = {}) {
|
|
1065
|
-
const config2 =
|
|
1066
|
-
const workspaceDir =
|
|
1067
|
-
const report =
|
|
1614
|
+
const config2 = loadConfig2();
|
|
1615
|
+
const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
|
|
1616
|
+
const report = buildPluginStatusReport2({
|
|
1068
1617
|
config: config2,
|
|
1069
1618
|
workspaceDir,
|
|
1070
|
-
|
|
1071
|
-
reservedProviderIds: RESERVED_PROVIDER_IDS,
|
|
1072
|
-
reservedEngineKinds: ["native"]
|
|
1619
|
+
...buildReservedPluginLoadOptions()
|
|
1073
1620
|
});
|
|
1074
1621
|
const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
|
|
1075
1622
|
if (!plugin) {
|
|
@@ -1096,18 +1643,7 @@ var PluginCommands = class {
|
|
|
1096
1643
|
if (plugin.version) {
|
|
1097
1644
|
lines.push(`Version: ${plugin.version}`);
|
|
1098
1645
|
}
|
|
1099
|
-
|
|
1100
|
-
lines.push(`Tools: ${plugin.toolNames.join(", ")}`);
|
|
1101
|
-
}
|
|
1102
|
-
if (plugin.channelIds.length > 0) {
|
|
1103
|
-
lines.push(`Channels: ${plugin.channelIds.join(", ")}`);
|
|
1104
|
-
}
|
|
1105
|
-
if (plugin.providerIds.length > 0) {
|
|
1106
|
-
lines.push(`Providers: ${plugin.providerIds.join(", ")}`);
|
|
1107
|
-
}
|
|
1108
|
-
if (plugin.engineKinds.length > 0) {
|
|
1109
|
-
lines.push(`Engines: ${plugin.engineKinds.join(", ")}`);
|
|
1110
|
-
}
|
|
1646
|
+
appendPluginCapabilityLines(lines, plugin);
|
|
1111
1647
|
if (plugin.error) {
|
|
1112
1648
|
lines.push(`Error: ${plugin.error}`);
|
|
1113
1649
|
}
|
|
@@ -1133,33 +1669,37 @@ var PluginCommands = class {
|
|
|
1133
1669
|
console.log(lines.join("\n"));
|
|
1134
1670
|
}
|
|
1135
1671
|
async pluginsEnable(id) {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
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
|
+
}
|
|
1140
1679
|
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1141
1680
|
}
|
|
1142
1681
|
async pluginsDisable(id) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
+
}
|
|
1147
1689
|
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1148
1690
|
}
|
|
1149
1691
|
async pluginsUninstall(id, opts = {}) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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({
|
|
1153
1698
|
config: config2,
|
|
1154
1699
|
workspaceDir,
|
|
1155
|
-
|
|
1156
|
-
reservedProviderIds: RESERVED_PROVIDER_IDS,
|
|
1157
|
-
reservedEngineKinds: ["native"]
|
|
1700
|
+
...buildReservedPluginLoadOptions()
|
|
1158
1701
|
});
|
|
1159
1702
|
const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
|
|
1160
|
-
if (opts.keepConfig) {
|
|
1161
|
-
console.log("`--keep-config` is deprecated, use `--keep-files`.");
|
|
1162
|
-
}
|
|
1163
1703
|
const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
|
|
1164
1704
|
const pluginId = plugin?.id ?? id;
|
|
1165
1705
|
const hasEntry = pluginId in (config2.plugins.entries ?? {});
|
|
@@ -1175,7 +1715,7 @@ var PluginCommands = class {
|
|
|
1175
1715
|
process.exit(1);
|
|
1176
1716
|
}
|
|
1177
1717
|
const install = config2.plugins.installs?.[pluginId];
|
|
1178
|
-
const isLinked = install?.source === "path" && (!install.installPath || !install.sourcePath ||
|
|
1718
|
+
const isLinked = install?.source === "path" && (!install.installPath || !install.sourcePath || resolve7(install.installPath) === resolve7(install.sourcePath));
|
|
1179
1719
|
const preview = [];
|
|
1180
1720
|
if (hasEntry) {
|
|
1181
1721
|
preview.push("config entry");
|
|
@@ -1189,12 +1729,13 @@ var PluginCommands = class {
|
|
|
1189
1729
|
if (isLinked && install?.sourcePath && config2.plugins.load?.paths?.includes(install.sourcePath)) {
|
|
1190
1730
|
preview.push("load path");
|
|
1191
1731
|
}
|
|
1192
|
-
const
|
|
1732
|
+
const deleteTargets = !keepFiles ? resolveUninstallDirectoryTargets({
|
|
1733
|
+
config: config2,
|
|
1193
1734
|
pluginId,
|
|
1194
1735
|
hasInstall,
|
|
1195
1736
|
installRecord: install
|
|
1196
|
-
}) :
|
|
1197
|
-
|
|
1737
|
+
}) : [];
|
|
1738
|
+
for (const deleteTarget of deleteTargets) {
|
|
1198
1739
|
preview.push(`directory: ${deleteTarget}`);
|
|
1199
1740
|
}
|
|
1200
1741
|
const pluginName = plugin?.name || pluginId;
|
|
@@ -1212,132 +1753,35 @@ var PluginCommands = class {
|
|
|
1212
1753
|
return;
|
|
1213
1754
|
}
|
|
1214
1755
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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));
|
|
1222
1764
|
process.exit(1);
|
|
1223
1765
|
}
|
|
1224
|
-
for (const warning of result.warnings) {
|
|
1225
|
-
console.warn(warning);
|
|
1226
|
-
}
|
|
1227
|
-
saveConfig(result.config);
|
|
1228
|
-
const removed = [];
|
|
1229
|
-
if (result.actions.entry) {
|
|
1230
|
-
removed.push("config entry");
|
|
1231
|
-
}
|
|
1232
|
-
if (result.actions.install) {
|
|
1233
|
-
removed.push("install record");
|
|
1234
|
-
}
|
|
1235
|
-
if (result.actions.allowlist) {
|
|
1236
|
-
removed.push("allowlist");
|
|
1237
|
-
}
|
|
1238
|
-
if (result.actions.loadPath) {
|
|
1239
|
-
removed.push("load path");
|
|
1240
|
-
}
|
|
1241
|
-
if (result.actions.directory) {
|
|
1242
|
-
removed.push("directory");
|
|
1243
|
-
}
|
|
1244
|
-
console.log(`Uninstalled plugin "${pluginId}". Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`);
|
|
1245
1766
|
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1246
1767
|
}
|
|
1247
1768
|
async pluginsInstall(pathOrSpec, opts = {}) {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
console.
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
const normalized = fileSpec && fileSpec.ok ? fileSpec.path : pathOrSpec;
|
|
1254
|
-
const resolved = resolve5(expandHome(normalized));
|
|
1255
|
-
const config2 = loadConfig();
|
|
1256
|
-
if (existsSync4(resolved)) {
|
|
1257
|
-
if (opts.link) {
|
|
1258
|
-
const probe = await installPluginFromPath({ path: resolved, dryRun: true });
|
|
1259
|
-
if (!probe.ok) {
|
|
1260
|
-
console.error(probe.error);
|
|
1261
|
-
process.exit(1);
|
|
1262
|
-
}
|
|
1263
|
-
let next3 = addPluginLoadPath(config2, resolved);
|
|
1264
|
-
next3 = enablePluginInConfig(next3, probe.pluginId);
|
|
1265
|
-
next3 = recordPluginInstall(next3, {
|
|
1266
|
-
pluginId: probe.pluginId,
|
|
1267
|
-
source: "path",
|
|
1268
|
-
sourcePath: resolved,
|
|
1269
|
-
installPath: resolved,
|
|
1270
|
-
version: probe.version
|
|
1271
|
-
});
|
|
1272
|
-
saveConfig(next3);
|
|
1273
|
-
console.log(`Linked plugin path: ${resolved}`);
|
|
1274
|
-
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1275
|
-
return;
|
|
1276
|
-
}
|
|
1277
|
-
const result2 = await installPluginFromPath({
|
|
1278
|
-
path: resolved,
|
|
1279
|
-
logger: {
|
|
1280
|
-
info: (message) => console.log(message),
|
|
1281
|
-
warn: (message) => console.warn(message)
|
|
1282
|
-
}
|
|
1283
|
-
});
|
|
1284
|
-
if (!result2.ok) {
|
|
1285
|
-
console.error(result2.error);
|
|
1286
|
-
process.exit(1);
|
|
1287
|
-
}
|
|
1288
|
-
let next2 = enablePluginInConfig(config2, result2.pluginId);
|
|
1289
|
-
next2 = recordPluginInstall(next2, {
|
|
1290
|
-
pluginId: result2.pluginId,
|
|
1291
|
-
source: this.isArchivePath(resolved) ? "archive" : "path",
|
|
1292
|
-
sourcePath: resolved,
|
|
1293
|
-
installPath: result2.targetDir,
|
|
1294
|
-
version: result2.version
|
|
1295
|
-
});
|
|
1296
|
-
saveConfig(next2);
|
|
1297
|
-
console.log(`Installed plugin: ${result2.pluginId}`);
|
|
1298
|
-
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1299
|
-
return;
|
|
1300
|
-
}
|
|
1301
|
-
if (opts.link) {
|
|
1302
|
-
console.error("`--link` requires a local path.");
|
|
1303
|
-
process.exit(1);
|
|
1304
|
-
}
|
|
1305
|
-
if (this.looksLikePath(pathOrSpec)) {
|
|
1306
|
-
console.error(`Path not found: ${resolved}`);
|
|
1307
|
-
process.exit(1);
|
|
1308
|
-
}
|
|
1309
|
-
const result = await installPluginFromNpmSpec({
|
|
1310
|
-
spec: pathOrSpec,
|
|
1311
|
-
logger: {
|
|
1312
|
-
info: (message) => console.log(message),
|
|
1313
|
-
warn: (message) => console.warn(message)
|
|
1314
|
-
}
|
|
1315
|
-
});
|
|
1316
|
-
if (!result.ok) {
|
|
1317
|
-
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));
|
|
1318
1774
|
process.exit(1);
|
|
1319
1775
|
}
|
|
1320
|
-
let next = enablePluginInConfig(config2, result.pluginId);
|
|
1321
|
-
next = recordPluginInstall(next, {
|
|
1322
|
-
pluginId: result.pluginId,
|
|
1323
|
-
source: "npm",
|
|
1324
|
-
spec: pathOrSpec,
|
|
1325
|
-
installPath: result.targetDir,
|
|
1326
|
-
version: result.version
|
|
1327
|
-
});
|
|
1328
|
-
saveConfig(next);
|
|
1329
|
-
console.log(`Installed plugin: ${result.pluginId}`);
|
|
1330
1776
|
console.log("If gateway is running, plugin changes are hot-applied automatically.");
|
|
1331
1777
|
}
|
|
1332
1778
|
pluginsDoctor() {
|
|
1333
|
-
const config2 =
|
|
1334
|
-
const workspaceDir =
|
|
1335
|
-
const report =
|
|
1779
|
+
const config2 = loadConfig2();
|
|
1780
|
+
const workspaceDir = getWorkspacePath2(config2.agents.defaults.workspace);
|
|
1781
|
+
const report = buildPluginStatusReport2({
|
|
1336
1782
|
config: config2,
|
|
1337
1783
|
workspaceDir,
|
|
1338
|
-
|
|
1339
|
-
reservedProviderIds: RESERVED_PROVIDER_IDS,
|
|
1340
|
-
reservedEngineKinds: ["native"]
|
|
1784
|
+
...buildReservedPluginLoadOptions()
|
|
1341
1785
|
});
|
|
1342
1786
|
const pluginErrors = report.plugins.filter((plugin) => plugin.status === "error");
|
|
1343
1787
|
const diagnostics = report.diagnostics.filter((diag) => diag.level === "error");
|
|
@@ -1367,47 +1811,17 @@ var PluginCommands = class {
|
|
|
1367
1811
|
input: process.stdin,
|
|
1368
1812
|
output: process.stdout
|
|
1369
1813
|
});
|
|
1370
|
-
const answer = await new Promise((
|
|
1371
|
-
rl.question(`${question} [y/N] `, (line) =>
|
|
1814
|
+
const answer = await new Promise((resolve13) => {
|
|
1815
|
+
rl.question(`${question} [y/N] `, (line) => resolve13(line));
|
|
1372
1816
|
});
|
|
1373
1817
|
rl.close();
|
|
1374
1818
|
const normalized = answer.trim().toLowerCase();
|
|
1375
1819
|
return normalized === "y" || normalized === "yes";
|
|
1376
1820
|
}
|
|
1377
|
-
resolveFileNpmSpecToLocalPath(raw) {
|
|
1378
|
-
const trimmed = raw.trim();
|
|
1379
|
-
if (!trimmed.toLowerCase().startsWith("file:")) {
|
|
1380
|
-
return null;
|
|
1381
|
-
}
|
|
1382
|
-
const rest = trimmed.slice("file:".length);
|
|
1383
|
-
if (!rest) {
|
|
1384
|
-
return { ok: false, error: "unsupported file: spec: missing path" };
|
|
1385
|
-
}
|
|
1386
|
-
if (rest.startsWith("///")) {
|
|
1387
|
-
return { ok: true, path: rest.slice(2) };
|
|
1388
|
-
}
|
|
1389
|
-
if (rest.startsWith("//localhost/")) {
|
|
1390
|
-
return { ok: true, path: rest.slice("//localhost".length) };
|
|
1391
|
-
}
|
|
1392
|
-
if (rest.startsWith("//")) {
|
|
1393
|
-
return {
|
|
1394
|
-
ok: false,
|
|
1395
|
-
error: 'unsupported file: URL host (expected "file:<path>" or "file:///abs/path")'
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
return { ok: true, path: rest };
|
|
1399
|
-
}
|
|
1400
|
-
looksLikePath(raw) {
|
|
1401
|
-
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");
|
|
1402
|
-
}
|
|
1403
|
-
isArchivePath(filePath) {
|
|
1404
|
-
const lower = filePath.toLowerCase();
|
|
1405
|
-
return lower.endsWith(".zip") || lower.endsWith(".tgz") || lower.endsWith(".tar.gz") || lower.endsWith(".tar");
|
|
1406
|
-
}
|
|
1407
1821
|
};
|
|
1408
1822
|
|
|
1409
1823
|
// src/cli/commands/config.ts
|
|
1410
|
-
import { buildReloadPlan, diffConfigPaths, loadConfig as
|
|
1824
|
+
import { buildReloadPlan, diffConfigPaths, loadConfig as loadConfig3, saveConfig as saveConfig2 } from "@nextclaw/core";
|
|
1411
1825
|
|
|
1412
1826
|
// src/cli/config-path.ts
|
|
1413
1827
|
function isIndexSegment(raw) {
|
|
@@ -1602,7 +2016,7 @@ var ConfigCommands = class {
|
|
|
1602
2016
|
this.deps = deps;
|
|
1603
2017
|
}
|
|
1604
2018
|
configGet(pathExpr, opts = {}) {
|
|
1605
|
-
const config2 =
|
|
2019
|
+
const config2 = loadConfig3();
|
|
1606
2020
|
let parsedPath;
|
|
1607
2021
|
try {
|
|
1608
2022
|
parsedPath = parseRequiredConfigPath(pathExpr);
|
|
@@ -1644,7 +2058,7 @@ var ConfigCommands = class {
|
|
|
1644
2058
|
process.exit(1);
|
|
1645
2059
|
return;
|
|
1646
2060
|
}
|
|
1647
|
-
const prevConfig =
|
|
2061
|
+
const prevConfig = loadConfig3();
|
|
1648
2062
|
const nextConfig = structuredClone(prevConfig);
|
|
1649
2063
|
try {
|
|
1650
2064
|
setAtConfigPath(nextConfig, parsedPath, parsedValue);
|
|
@@ -1670,7 +2084,7 @@ var ConfigCommands = class {
|
|
|
1670
2084
|
process.exit(1);
|
|
1671
2085
|
return;
|
|
1672
2086
|
}
|
|
1673
|
-
const prevConfig =
|
|
2087
|
+
const prevConfig = loadConfig3();
|
|
1674
2088
|
const nextConfig = structuredClone(prevConfig);
|
|
1675
2089
|
const removed = unsetAtConfigPath(nextConfig, parsedPath);
|
|
1676
2090
|
if (!removed) {
|
|
@@ -1703,12 +2117,12 @@ var ConfigCommands = class {
|
|
|
1703
2117
|
};
|
|
1704
2118
|
|
|
1705
2119
|
// src/cli/commands/secrets.ts
|
|
1706
|
-
import { readFileSync as
|
|
2120
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
1707
2121
|
import {
|
|
1708
2122
|
buildReloadPlan as buildReloadPlan2,
|
|
1709
2123
|
diffConfigPaths as diffConfigPaths2,
|
|
1710
2124
|
getConfigPath,
|
|
1711
|
-
loadConfig as
|
|
2125
|
+
loadConfig as loadConfig4,
|
|
1712
2126
|
resolveConfigSecrets,
|
|
1713
2127
|
saveConfig as saveConfig3
|
|
1714
2128
|
} from "@nextclaw/core";
|
|
@@ -1761,17 +2175,17 @@ function parseRefsPatch(raw) {
|
|
|
1761
2175
|
throw new Error("refs patch must be an object");
|
|
1762
2176
|
}
|
|
1763
2177
|
const output = {};
|
|
1764
|
-
for (const [
|
|
2178
|
+
for (const [path2, value] of Object.entries(raw)) {
|
|
1765
2179
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1766
|
-
throw new Error(`invalid ref for ${
|
|
2180
|
+
throw new Error(`invalid ref for ${path2}`);
|
|
1767
2181
|
}
|
|
1768
2182
|
const source = normalizeSecretSource(value.source);
|
|
1769
2183
|
const id = normalizeOptionalString(value.id);
|
|
1770
2184
|
const provider = normalizeOptionalString(value.provider);
|
|
1771
2185
|
if (!source || !id) {
|
|
1772
|
-
throw new Error(`invalid ref for ${
|
|
2186
|
+
throw new Error(`invalid ref for ${path2}: source/id is required`);
|
|
1773
2187
|
}
|
|
1774
|
-
output[
|
|
2188
|
+
output[path2] = { source, ...provider ? { provider } : {}, id };
|
|
1775
2189
|
}
|
|
1776
2190
|
return output;
|
|
1777
2191
|
}
|
|
@@ -1804,21 +2218,21 @@ var SecretsCommands = class {
|
|
|
1804
2218
|
this.deps = deps;
|
|
1805
2219
|
}
|
|
1806
2220
|
secretsAudit(opts = {}) {
|
|
1807
|
-
const config2 =
|
|
2221
|
+
const config2 = loadConfig4();
|
|
1808
2222
|
const configPath = getConfigPath();
|
|
1809
2223
|
const refs = config2.secrets.refs;
|
|
1810
2224
|
const items = [];
|
|
1811
|
-
for (const [
|
|
2225
|
+
for (const [path2, ref] of Object.entries(refs)) {
|
|
1812
2226
|
const provider = inferProviderAlias(config2, ref);
|
|
1813
2227
|
const scopedConfig = structuredClone(config2);
|
|
1814
|
-
scopedConfig.secrets.refs = { [
|
|
2228
|
+
scopedConfig.secrets.refs = { [path2]: ref };
|
|
1815
2229
|
try {
|
|
1816
2230
|
const resolved = resolveConfigSecrets(scopedConfig, { configPath });
|
|
1817
|
-
const parsedPath = parseRequiredConfigPath(
|
|
2231
|
+
const parsedPath = parseRequiredConfigPath(path2);
|
|
1818
2232
|
const target = getAtConfigPath(resolved, parsedPath);
|
|
1819
2233
|
if (!target.found) {
|
|
1820
2234
|
items.push({
|
|
1821
|
-
path,
|
|
2235
|
+
path: path2,
|
|
1822
2236
|
source: ref.source,
|
|
1823
2237
|
provider,
|
|
1824
2238
|
id: ref.id,
|
|
@@ -1830,7 +2244,7 @@ var SecretsCommands = class {
|
|
|
1830
2244
|
const resolvedValue = target.value;
|
|
1831
2245
|
const detail = typeof resolvedValue === "string" ? `resolved (length=${resolvedValue.length})` : `resolved (${typeof resolvedValue})`;
|
|
1832
2246
|
items.push({
|
|
1833
|
-
path,
|
|
2247
|
+
path: path2,
|
|
1834
2248
|
source: ref.source,
|
|
1835
2249
|
provider,
|
|
1836
2250
|
id: ref.id,
|
|
@@ -1839,7 +2253,7 @@ var SecretsCommands = class {
|
|
|
1839
2253
|
});
|
|
1840
2254
|
} catch (error) {
|
|
1841
2255
|
items.push({
|
|
1842
|
-
path,
|
|
2256
|
+
path: path2,
|
|
1843
2257
|
source: ref.source,
|
|
1844
2258
|
provider,
|
|
1845
2259
|
id: ref.id,
|
|
@@ -1872,7 +2286,7 @@ var SecretsCommands = class {
|
|
|
1872
2286
|
if (!alias) {
|
|
1873
2287
|
throw new Error("provider alias is required");
|
|
1874
2288
|
}
|
|
1875
|
-
const prevConfig =
|
|
2289
|
+
const prevConfig = loadConfig4();
|
|
1876
2290
|
const nextConfig = structuredClone(prevConfig);
|
|
1877
2291
|
const remove = Boolean(opts.remove);
|
|
1878
2292
|
if (remove) {
|
|
@@ -1893,13 +2307,13 @@ var SecretsCommands = class {
|
|
|
1893
2307
|
...normalizeOptionalString(opts.prefix) ? { prefix: normalizeOptionalString(opts.prefix) } : {}
|
|
1894
2308
|
};
|
|
1895
2309
|
} else if (source === "file") {
|
|
1896
|
-
const
|
|
1897
|
-
if (!
|
|
2310
|
+
const path2 = normalizeOptionalString(opts.path);
|
|
2311
|
+
if (!path2) {
|
|
1898
2312
|
throw new Error("file source requires --path");
|
|
1899
2313
|
}
|
|
1900
2314
|
nextConfig.secrets.providers[alias] = {
|
|
1901
2315
|
source,
|
|
1902
|
-
path,
|
|
2316
|
+
path: path2,
|
|
1903
2317
|
format: "json"
|
|
1904
2318
|
};
|
|
1905
2319
|
} else {
|
|
@@ -1934,7 +2348,7 @@ var SecretsCommands = class {
|
|
|
1934
2348
|
console.log(`Secrets provider ${remove ? "removed" : "configured"}: ${alias}`);
|
|
1935
2349
|
}
|
|
1936
2350
|
async secretsApply(opts) {
|
|
1937
|
-
const prevConfig =
|
|
2351
|
+
const prevConfig = loadConfig4();
|
|
1938
2352
|
const nextConfig = structuredClone(prevConfig);
|
|
1939
2353
|
if (opts.enable && opts.disable) {
|
|
1940
2354
|
throw new Error("cannot set --enable and --disable at the same time");
|
|
@@ -1946,7 +2360,7 @@ var SecretsCommands = class {
|
|
|
1946
2360
|
nextConfig.secrets.enabled = false;
|
|
1947
2361
|
}
|
|
1948
2362
|
if (opts.file) {
|
|
1949
|
-
const raw =
|
|
2363
|
+
const raw = readFileSync5(opts.file, "utf-8");
|
|
1950
2364
|
const patch = parseApplyFile(raw);
|
|
1951
2365
|
if (patch.defaults) {
|
|
1952
2366
|
nextConfig.secrets.defaults = patch.defaults;
|
|
@@ -1962,12 +2376,12 @@ var SecretsCommands = class {
|
|
|
1962
2376
|
}
|
|
1963
2377
|
}
|
|
1964
2378
|
if (opts.path) {
|
|
1965
|
-
const
|
|
1966
|
-
if (!
|
|
2379
|
+
const path2 = opts.path.trim();
|
|
2380
|
+
if (!path2) {
|
|
1967
2381
|
throw new Error("path is empty");
|
|
1968
2382
|
}
|
|
1969
2383
|
if (opts.remove) {
|
|
1970
|
-
delete nextConfig.secrets.refs[
|
|
2384
|
+
delete nextConfig.secrets.refs[path2];
|
|
1971
2385
|
} else {
|
|
1972
2386
|
const source = normalizeSecretSource(opts.source);
|
|
1973
2387
|
const id = normalizeOptionalString(opts.id);
|
|
@@ -1975,7 +2389,7 @@ var SecretsCommands = class {
|
|
|
1975
2389
|
throw new Error("apply single ref requires --source and --id");
|
|
1976
2390
|
}
|
|
1977
2391
|
const provider = normalizeOptionalString(opts.provider);
|
|
1978
|
-
nextConfig.secrets.refs[
|
|
2392
|
+
nextConfig.secrets.refs[path2] = {
|
|
1979
2393
|
source,
|
|
1980
2394
|
id,
|
|
1981
2395
|
...provider ? { provider } : {}
|
|
@@ -1999,7 +2413,7 @@ var SecretsCommands = class {
|
|
|
1999
2413
|
console.log("Secrets applied.");
|
|
2000
2414
|
}
|
|
2001
2415
|
async secretsReload(opts = {}) {
|
|
2002
|
-
const config2 =
|
|
2416
|
+
const config2 = loadConfig4();
|
|
2003
2417
|
const configPath = getConfigPath();
|
|
2004
2418
|
resolveConfigSecrets(config2, { configPath });
|
|
2005
2419
|
saveConfig3(config2);
|
|
@@ -2027,9 +2441,9 @@ var SecretsCommands = class {
|
|
|
2027
2441
|
|
|
2028
2442
|
// src/cli/commands/channels.ts
|
|
2029
2443
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2030
|
-
import { getWorkspacePath as
|
|
2444
|
+
import { getWorkspacePath as getWorkspacePath3, loadConfig as loadConfig5, saveConfig as saveConfig4 } from "@nextclaw/core";
|
|
2031
2445
|
import { BUILTIN_CHANNEL_PLUGIN_IDS, builtinProviderIds as builtinProviderIds2 } from "@nextclaw/runtime";
|
|
2032
|
-
import { buildPluginStatusReport as
|
|
2446
|
+
import { buildPluginStatusReport as buildPluginStatusReport3, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
|
|
2033
2447
|
var CHANNEL_LABELS = {
|
|
2034
2448
|
telegram: "Telegram",
|
|
2035
2449
|
whatsapp: "WhatsApp",
|
|
@@ -2048,7 +2462,7 @@ var ChannelCommands = class {
|
|
|
2048
2462
|
this.deps = deps;
|
|
2049
2463
|
}
|
|
2050
2464
|
channelsStatus() {
|
|
2051
|
-
const config2 =
|
|
2465
|
+
const config2 = loadConfig5();
|
|
2052
2466
|
console.log("Channel Status");
|
|
2053
2467
|
const channelConfig = config2.channels;
|
|
2054
2468
|
for (const channelId of BUILTIN_CHANNEL_PLUGIN_IDS) {
|
|
@@ -2056,8 +2470,8 @@ var ChannelCommands = class {
|
|
|
2056
2470
|
const enabled = channelConfig[channelId]?.enabled === true;
|
|
2057
2471
|
console.log(`${label}: ${enabled ? "\u2713" : "\u2717"}`);
|
|
2058
2472
|
}
|
|
2059
|
-
const workspaceDir =
|
|
2060
|
-
const report =
|
|
2473
|
+
const workspaceDir = getWorkspacePath3(config2.agents.defaults.workspace);
|
|
2474
|
+
const report = buildPluginStatusReport3({
|
|
2061
2475
|
config: config2,
|
|
2062
2476
|
workspaceDir,
|
|
2063
2477
|
reservedChannelIds: [],
|
|
@@ -2087,8 +2501,8 @@ var ChannelCommands = class {
|
|
|
2087
2501
|
console.error("--channel is required");
|
|
2088
2502
|
process.exit(1);
|
|
2089
2503
|
}
|
|
2090
|
-
const config2 =
|
|
2091
|
-
const workspaceDir =
|
|
2504
|
+
const config2 = loadConfig5();
|
|
2505
|
+
const workspaceDir = getWorkspacePath3(config2.agents.defaults.workspace);
|
|
2092
2506
|
const pluginRegistry = loadPluginRegistry(config2, workspaceDir);
|
|
2093
2507
|
const bindings = getPluginChannelBindings(pluginRegistry);
|
|
2094
2508
|
const binding = bindings.find((entry) => entry.channelId === channelId || entry.pluginId === channelId);
|
|
@@ -2217,15 +2631,15 @@ var CronCommands = class {
|
|
|
2217
2631
|
|
|
2218
2632
|
// src/cli/commands/diagnostics.ts
|
|
2219
2633
|
import { createServer as createNetServer } from "net";
|
|
2220
|
-
import { existsSync as
|
|
2221
|
-
import { resolve as
|
|
2634
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
2635
|
+
import { resolve as resolve8 } from "path";
|
|
2222
2636
|
import {
|
|
2223
2637
|
APP_NAME,
|
|
2224
2638
|
getConfigPath as getConfigPath2,
|
|
2225
2639
|
getDataDir as getDataDir4,
|
|
2226
|
-
getWorkspacePath as
|
|
2640
|
+
getWorkspacePath as getWorkspacePath4,
|
|
2227
2641
|
hasSecretRef,
|
|
2228
|
-
loadConfig as
|
|
2642
|
+
loadConfig as loadConfig6
|
|
2229
2643
|
} from "@nextclaw/core";
|
|
2230
2644
|
import { listBuiltinProviders } from "@nextclaw/runtime";
|
|
2231
2645
|
var DiagnosticsCommands = class {
|
|
@@ -2387,9 +2801,9 @@ var DiagnosticsCommands = class {
|
|
|
2387
2801
|
}
|
|
2388
2802
|
async collectRuntimeStatus(params) {
|
|
2389
2803
|
const configPath = getConfigPath2();
|
|
2390
|
-
const config2 =
|
|
2391
|
-
const workspacePath =
|
|
2392
|
-
const serviceStatePath =
|
|
2804
|
+
const config2 = loadConfig6();
|
|
2805
|
+
const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
|
|
2806
|
+
const serviceStatePath = resolve8(getDataDir4(), "run", "service.json");
|
|
2393
2807
|
const fixActions = [];
|
|
2394
2808
|
let serviceState = readServiceState();
|
|
2395
2809
|
if (params.fix && serviceState && !isProcessRunning(serviceState.pid)) {
|
|
@@ -2429,11 +2843,11 @@ var DiagnosticsCommands = class {
|
|
|
2429
2843
|
});
|
|
2430
2844
|
const issues = [];
|
|
2431
2845
|
const recommendations = [];
|
|
2432
|
-
if (!
|
|
2846
|
+
if (!existsSync6(configPath)) {
|
|
2433
2847
|
issues.push("Config file is missing.");
|
|
2434
2848
|
recommendations.push(`Run ${APP_NAME} init to create config files.`);
|
|
2435
2849
|
}
|
|
2436
|
-
if (!
|
|
2850
|
+
if (!existsSync6(workspacePath)) {
|
|
2437
2851
|
issues.push("Workspace directory does not exist.");
|
|
2438
2852
|
recommendations.push(`Run ${APP_NAME} init to create workspace templates.`);
|
|
2439
2853
|
}
|
|
@@ -2466,13 +2880,13 @@ var DiagnosticsCommands = class {
|
|
|
2466
2880
|
return {
|
|
2467
2881
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2468
2882
|
configPath,
|
|
2469
|
-
configExists:
|
|
2883
|
+
configExists: existsSync6(configPath),
|
|
2470
2884
|
workspacePath,
|
|
2471
|
-
workspaceExists:
|
|
2885
|
+
workspaceExists: existsSync6(workspacePath),
|
|
2472
2886
|
model: config2.agents.defaults.model,
|
|
2473
2887
|
providers,
|
|
2474
2888
|
serviceStatePath,
|
|
2475
|
-
serviceStateExists:
|
|
2889
|
+
serviceStateExists: existsSync6(serviceStatePath),
|
|
2476
2890
|
fixActions,
|
|
2477
2891
|
process: {
|
|
2478
2892
|
managedByState,
|
|
@@ -2521,12 +2935,12 @@ var DiagnosticsCommands = class {
|
|
|
2521
2935
|
clearTimeout(timer);
|
|
2522
2936
|
}
|
|
2523
2937
|
}
|
|
2524
|
-
readLogTail(
|
|
2525
|
-
if (!
|
|
2938
|
+
readLogTail(path2, maxLines = 25) {
|
|
2939
|
+
if (!existsSync6(path2)) {
|
|
2526
2940
|
return [];
|
|
2527
2941
|
}
|
|
2528
2942
|
try {
|
|
2529
|
-
const lines =
|
|
2943
|
+
const lines = readFileSync6(path2, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
2530
2944
|
if (lines.length <= maxLines) {
|
|
2531
2945
|
return lines;
|
|
2532
2946
|
}
|
|
@@ -2536,17 +2950,17 @@ var DiagnosticsCommands = class {
|
|
|
2536
2950
|
}
|
|
2537
2951
|
}
|
|
2538
2952
|
async checkPortAvailability(params) {
|
|
2539
|
-
return await new Promise((
|
|
2953
|
+
return await new Promise((resolve13) => {
|
|
2540
2954
|
const server = createNetServer();
|
|
2541
2955
|
server.once("error", (error) => {
|
|
2542
|
-
|
|
2956
|
+
resolve13({
|
|
2543
2957
|
available: false,
|
|
2544
2958
|
detail: `bind failed on ${params.host}:${params.port} (${String(error)})`
|
|
2545
2959
|
});
|
|
2546
2960
|
});
|
|
2547
2961
|
server.listen(params.port, params.host, () => {
|
|
2548
2962
|
server.close(() => {
|
|
2549
|
-
|
|
2963
|
+
resolve13({
|
|
2550
2964
|
available: true,
|
|
2551
2965
|
detail: `bind ok on ${params.host}:${params.port}`
|
|
2552
2966
|
});
|
|
@@ -2559,24 +2973,24 @@ var DiagnosticsCommands = class {
|
|
|
2559
2973
|
// src/cli/commands/service.ts
|
|
2560
2974
|
import * as NextclawCore from "@nextclaw/core";
|
|
2561
2975
|
import {
|
|
2562
|
-
getPluginChannelBindings as
|
|
2976
|
+
getPluginChannelBindings as getPluginChannelBindings3,
|
|
2563
2977
|
resolvePluginChannelMessageToolHints,
|
|
2564
2978
|
setPluginRuntimeBridge,
|
|
2565
|
-
startPluginChannelGateways,
|
|
2566
|
-
stopPluginChannelGateways
|
|
2979
|
+
startPluginChannelGateways as startPluginChannelGateways2,
|
|
2980
|
+
stopPluginChannelGateways as stopPluginChannelGateways2
|
|
2567
2981
|
} from "@nextclaw/openclaw-compat";
|
|
2568
2982
|
import { startUiServer } from "@nextclaw/server";
|
|
2569
|
-
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as
|
|
2570
|
-
import { dirname as dirname2, join as join5, resolve as
|
|
2983
|
+
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync, rmSync as rmSync4 } from "fs";
|
|
2984
|
+
import { dirname as dirname2, join as join5, resolve as resolve10 } from "path";
|
|
2571
2985
|
import { spawn as spawn2 } from "child_process";
|
|
2572
2986
|
import { request as httpRequest } from "http";
|
|
2573
2987
|
import { request as httpsRequest } from "https";
|
|
2574
|
-
import {
|
|
2988
|
+
import { createServer as createNetServer2 } from "net";
|
|
2575
2989
|
import chokidar from "chokidar";
|
|
2576
2990
|
|
|
2577
2991
|
// src/cli/gateway/controller.ts
|
|
2578
2992
|
import { createHash } from "crypto";
|
|
2579
|
-
import { existsSync as
|
|
2993
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
2580
2994
|
import {
|
|
2581
2995
|
buildConfigSchema,
|
|
2582
2996
|
ConfigSchema,
|
|
@@ -2585,11 +2999,11 @@ import {
|
|
|
2585
2999
|
} from "@nextclaw/core";
|
|
2586
3000
|
var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
2587
3001
|
var readConfigSnapshot = (getConfigPath5) => {
|
|
2588
|
-
const
|
|
3002
|
+
const path2 = getConfigPath5();
|
|
2589
3003
|
let raw = "";
|
|
2590
3004
|
let parsed = {};
|
|
2591
|
-
if (
|
|
2592
|
-
raw =
|
|
3005
|
+
if (existsSync7(path2)) {
|
|
3006
|
+
raw = readFileSync7(path2, "utf-8");
|
|
2593
3007
|
try {
|
|
2594
3008
|
parsed = JSON.parse(raw);
|
|
2595
3009
|
} catch {
|
|
@@ -2900,11 +3314,15 @@ var ConfigReloader = class {
|
|
|
2900
3314
|
this.currentConfig = nextConfig;
|
|
2901
3315
|
this.options.providerManager?.setConfig(nextConfig);
|
|
2902
3316
|
const plan = buildReloadPlan3(changedPaths);
|
|
3317
|
+
let reloadPluginsResult = void 0;
|
|
2903
3318
|
if (plan.reloadPlugins) {
|
|
2904
|
-
await this.reloadPlugins(
|
|
3319
|
+
reloadPluginsResult = await this.reloadPlugins({
|
|
3320
|
+
config: nextConfig,
|
|
3321
|
+
changedPaths
|
|
3322
|
+
});
|
|
2905
3323
|
console.log("Config reload: plugins reloaded.");
|
|
2906
3324
|
}
|
|
2907
|
-
if (plan.restartChannels) {
|
|
3325
|
+
if (plan.restartChannels || reloadPluginsResult?.restartChannels) {
|
|
2908
3326
|
await this.reloadChannels(nextConfig);
|
|
2909
3327
|
console.log("Config reload: channels restarted.");
|
|
2910
3328
|
}
|
|
@@ -2999,11 +3417,11 @@ var ConfigReloader = class {
|
|
|
2999
3417
|
this.providerReloadTask = null;
|
|
3000
3418
|
}
|
|
3001
3419
|
}
|
|
3002
|
-
async reloadPlugins(
|
|
3420
|
+
async reloadPlugins(params) {
|
|
3003
3421
|
if (!this.options.reloadPlugins) {
|
|
3004
3422
|
return;
|
|
3005
3423
|
}
|
|
3006
|
-
await this.options.reloadPlugins(
|
|
3424
|
+
return await this.options.reloadPlugins(params);
|
|
3007
3425
|
}
|
|
3008
3426
|
};
|
|
3009
3427
|
|
|
@@ -3025,6 +3443,104 @@ var MissingProvider = class extends LLMProvider {
|
|
|
3025
3443
|
}
|
|
3026
3444
|
};
|
|
3027
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
|
+
|
|
3028
3544
|
// src/cli/commands/agent-runtime-pool.ts
|
|
3029
3545
|
import {
|
|
3030
3546
|
NativeAgentEngine,
|
|
@@ -3032,7 +3548,7 @@ import {
|
|
|
3032
3548
|
createAssistantStreamDeltaControlMessage,
|
|
3033
3549
|
createAssistantStreamResetControlMessage,
|
|
3034
3550
|
AgentRouteResolver,
|
|
3035
|
-
getWorkspacePath as
|
|
3551
|
+
getWorkspacePath as getWorkspacePath6,
|
|
3036
3552
|
parseAgentScopedSessionKey
|
|
3037
3553
|
} from "@nextclaw/core";
|
|
3038
3554
|
function normalizeAgentId(value) {
|
|
@@ -3077,7 +3593,7 @@ function resolveAgentProfiles(config2) {
|
|
|
3077
3593
|
}
|
|
3078
3594
|
return Array.from(unique.values()).map((entry) => ({
|
|
3079
3595
|
id: entry.id,
|
|
3080
|
-
workspace:
|
|
3596
|
+
workspace: getWorkspacePath6(entry.workspace ?? defaults.workspace),
|
|
3081
3597
|
model: entry.model ?? defaults.model,
|
|
3082
3598
|
engine: normalizeEngineKind(entry.engine ?? defaults.engine),
|
|
3083
3599
|
engineConfig: entry.engineConfig ?? toRecord(defaults.engineConfig),
|
|
@@ -3342,7 +3858,7 @@ var GatewayAgentRuntimePool = class {
|
|
|
3342
3858
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
3343
3859
|
return this.resolvedProfiles.find((profile) => profile.id === normalizedAgentId) ?? this.resolvedProfiles.find((profile) => profile.id === this.defaultAgentId) ?? this.resolvedProfiles[0] ?? {
|
|
3344
3860
|
id: this.defaultAgentId,
|
|
3345
|
-
workspace:
|
|
3861
|
+
workspace: getWorkspacePath6(this.options.config.agents.defaults.workspace),
|
|
3346
3862
|
model: this.options.config.agents.defaults.model,
|
|
3347
3863
|
maxIterations: this.options.config.agents.defaults.maxToolIterations,
|
|
3348
3864
|
contextTokens: this.options.config.agents.defaults.contextTokens,
|
|
@@ -3455,72 +3971,1400 @@ var GatewayAgentRuntimePool = class {
|
|
|
3455
3971
|
engine
|
|
3456
3972
|
});
|
|
3457
3973
|
}
|
|
3458
|
-
this.runtimes = nextRuntimes;
|
|
3459
|
-
this.dynamicEngineRuntimes.clear();
|
|
3974
|
+
this.runtimes = nextRuntimes;
|
|
3975
|
+
this.dynamicEngineRuntimes.clear();
|
|
3976
|
+
}
|
|
3977
|
+
};
|
|
3978
|
+
function parseCommandArgsFromText(commandName, rawTail, specs) {
|
|
3979
|
+
if (!rawTail) {
|
|
3980
|
+
return {};
|
|
3981
|
+
}
|
|
3982
|
+
const command = specs.find((item) => item.name.trim().toLowerCase() === commandName);
|
|
3983
|
+
const options = command?.options;
|
|
3984
|
+
if (!options || options.length === 0) {
|
|
3985
|
+
return {};
|
|
3986
|
+
}
|
|
3987
|
+
const tokens = rawTail.split(/\s+/).filter(Boolean);
|
|
3988
|
+
const args = {};
|
|
3989
|
+
let cursor = 0;
|
|
3990
|
+
for (let i = 0; i < options.length; i += 1) {
|
|
3991
|
+
if (cursor >= tokens.length) {
|
|
3992
|
+
break;
|
|
3993
|
+
}
|
|
3994
|
+
const option = options[i];
|
|
3995
|
+
const isLastOption = i === options.length - 1;
|
|
3996
|
+
const rawValue = isLastOption ? tokens.slice(cursor).join(" ") : tokens[cursor];
|
|
3997
|
+
cursor += isLastOption ? tokens.length - cursor : 1;
|
|
3998
|
+
const parsedValue = parseCommandOptionValue(option.type, rawValue);
|
|
3999
|
+
if (parsedValue !== void 0) {
|
|
4000
|
+
args[option.name] = parsedValue;
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
return args;
|
|
4004
|
+
}
|
|
4005
|
+
function parseCommandOptionValue(type, rawValue) {
|
|
4006
|
+
const value = rawValue.trim();
|
|
4007
|
+
if (!value) {
|
|
4008
|
+
return void 0;
|
|
4009
|
+
}
|
|
4010
|
+
if (type === "number") {
|
|
4011
|
+
const parsed = Number(value);
|
|
4012
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
4013
|
+
}
|
|
4014
|
+
if (type === "boolean") {
|
|
4015
|
+
const lowered = value.toLowerCase();
|
|
4016
|
+
if (["1", "true", "yes", "on"].includes(lowered)) {
|
|
4017
|
+
return true;
|
|
4018
|
+
}
|
|
4019
|
+
if (["0", "false", "no", "off"].includes(lowered)) {
|
|
4020
|
+
return false;
|
|
4021
|
+
}
|
|
4022
|
+
return void 0;
|
|
4023
|
+
}
|
|
4024
|
+
return value;
|
|
4025
|
+
}
|
|
4026
|
+
function formatUserFacingError(error, maxChars = 320) {
|
|
4027
|
+
const raw = error instanceof Error ? error.message || error.name || "Unknown error" : String(error ?? "Unknown error");
|
|
4028
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
4029
|
+
if (!normalized) {
|
|
4030
|
+
return "Unknown error";
|
|
4031
|
+
}
|
|
4032
|
+
if (normalized.length <= maxChars) {
|
|
4033
|
+
return normalized;
|
|
4034
|
+
}
|
|
4035
|
+
return `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
4036
|
+
}
|
|
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
|
+
|
|
4051
|
+
// src/cli/commands/ncp/create-ui-ncp-agent.ts
|
|
4052
|
+
import {
|
|
4053
|
+
DisposableStore
|
|
4054
|
+
} from "@nextclaw/core";
|
|
4055
|
+
import { DefaultNcpAgentRuntime } from "@nextclaw/ncp-agent-runtime";
|
|
4056
|
+
import {
|
|
4057
|
+
readAssistantReasoningNormalizationMode,
|
|
4058
|
+
readAssistantReasoningNormalizationModeFromMetadata,
|
|
4059
|
+
writeAssistantReasoningNormalizationModeToMetadata
|
|
4060
|
+
} from "@nextclaw/ncp";
|
|
4061
|
+
import { createAgentClientFromServer, DefaultNcpAgentBackend } from "@nextclaw/ncp-toolkit";
|
|
4062
|
+
|
|
4063
|
+
// src/cli/commands/ncp/nextclaw-ncp-context-builder.ts
|
|
4064
|
+
import {
|
|
4065
|
+
ContextBuilder,
|
|
4066
|
+
InputBudgetPruner,
|
|
4067
|
+
getWorkspacePath as getWorkspacePath7,
|
|
4068
|
+
parseThinkingLevel,
|
|
4069
|
+
resolveThinkingLevel
|
|
4070
|
+
} from "@nextclaw/core";
|
|
4071
|
+
|
|
4072
|
+
// src/cli/commands/ncp/nextclaw-ncp-message-bridge.ts
|
|
4073
|
+
import {
|
|
4074
|
+
sanitizeAssistantReplyTags
|
|
4075
|
+
} from "@nextclaw/ncp";
|
|
4076
|
+
function normalizeString(value) {
|
|
4077
|
+
if (typeof value !== "string") {
|
|
4078
|
+
return null;
|
|
4079
|
+
}
|
|
4080
|
+
const trimmed = value.trim();
|
|
4081
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
4082
|
+
}
|
|
4083
|
+
function isRecord3(value) {
|
|
4084
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4085
|
+
}
|
|
4086
|
+
function cloneMetadata(value) {
|
|
4087
|
+
return isRecord3(value) ? structuredClone(value) : void 0;
|
|
4088
|
+
}
|
|
4089
|
+
function readStringArray(value) {
|
|
4090
|
+
if (!Array.isArray(value)) {
|
|
4091
|
+
return null;
|
|
4092
|
+
}
|
|
4093
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
4094
|
+
for (const item of value) {
|
|
4095
|
+
const normalized = normalizeString(item);
|
|
4096
|
+
if (normalized) {
|
|
4097
|
+
deduped.add(normalized);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
return [...deduped];
|
|
4101
|
+
}
|
|
4102
|
+
function mergeSessionMetadata(currentMetadata, inputMetadata) {
|
|
4103
|
+
if (!inputMetadata) {
|
|
4104
|
+
return currentMetadata;
|
|
4105
|
+
}
|
|
4106
|
+
const nextMetadata = {
|
|
4107
|
+
...currentMetadata,
|
|
4108
|
+
...structuredClone(inputMetadata)
|
|
4109
|
+
};
|
|
4110
|
+
const model = normalizeString(inputMetadata.model) ?? normalizeString(inputMetadata.preferred_model) ?? normalizeString(inputMetadata.preferredModel);
|
|
4111
|
+
if (model) {
|
|
4112
|
+
nextMetadata.model = model;
|
|
4113
|
+
nextMetadata.preferred_model = model;
|
|
4114
|
+
}
|
|
4115
|
+
const thinking = normalizeString(inputMetadata.thinking) ?? normalizeString(inputMetadata.preferred_thinking) ?? normalizeString(inputMetadata.thinking_level) ?? normalizeString(inputMetadata.thinkingLevel);
|
|
4116
|
+
if (thinking) {
|
|
4117
|
+
nextMetadata.thinking = thinking;
|
|
4118
|
+
nextMetadata.preferred_thinking = thinking;
|
|
4119
|
+
}
|
|
4120
|
+
const sessionType = normalizeString(inputMetadata.session_type) ?? normalizeString(inputMetadata.sessionType);
|
|
4121
|
+
if (sessionType) {
|
|
4122
|
+
nextMetadata.session_type = sessionType;
|
|
4123
|
+
}
|
|
4124
|
+
const label = normalizeString(inputMetadata.label) ?? normalizeString(inputMetadata.session_label);
|
|
4125
|
+
if (label) {
|
|
4126
|
+
nextMetadata.label = label;
|
|
4127
|
+
}
|
|
4128
|
+
const requestedSkills = readStringArray(inputMetadata.requested_skills) ?? readStringArray(inputMetadata.requestedSkills);
|
|
4129
|
+
if (requestedSkills) {
|
|
4130
|
+
nextMetadata.requested_skills = requestedSkills;
|
|
4131
|
+
}
|
|
4132
|
+
return nextMetadata;
|
|
4133
|
+
}
|
|
4134
|
+
function extractMessageMetadata(messages) {
|
|
4135
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
4136
|
+
const message = messages[index];
|
|
4137
|
+
if (message?.role !== "user") {
|
|
4138
|
+
continue;
|
|
4139
|
+
}
|
|
4140
|
+
const metadata = cloneMetadata(message.metadata);
|
|
4141
|
+
if (metadata) {
|
|
4142
|
+
return metadata;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
return void 0;
|
|
4146
|
+
}
|
|
4147
|
+
function ensureIsoTimestamp(value, fallback) {
|
|
4148
|
+
if (typeof value !== "string") {
|
|
4149
|
+
return fallback;
|
|
4150
|
+
}
|
|
4151
|
+
const timestamp = Date.parse(value);
|
|
4152
|
+
if (!Number.isFinite(timestamp)) {
|
|
4153
|
+
return fallback;
|
|
4154
|
+
}
|
|
4155
|
+
return new Date(timestamp).toISOString();
|
|
4156
|
+
}
|
|
4157
|
+
function serializeToolArgs(args) {
|
|
4158
|
+
if (typeof args === "string") {
|
|
4159
|
+
return args;
|
|
4160
|
+
}
|
|
4161
|
+
return JSON.stringify(args ?? {});
|
|
4162
|
+
}
|
|
4163
|
+
function serializeLegacyContent(parts) {
|
|
4164
|
+
const text = parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
4165
|
+
if (text.length > 0) {
|
|
4166
|
+
return text;
|
|
4167
|
+
}
|
|
4168
|
+
if (parts.length === 0) {
|
|
4169
|
+
return "";
|
|
4170
|
+
}
|
|
4171
|
+
return structuredClone(parts);
|
|
4172
|
+
}
|
|
4173
|
+
function extractTextFromNcpMessage(message) {
|
|
4174
|
+
if (!message) {
|
|
4175
|
+
return "";
|
|
4176
|
+
}
|
|
4177
|
+
const normalizedMessage = message.role === "assistant" ? sanitizeAssistantReplyTags(message) : message;
|
|
4178
|
+
return normalizedMessage.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
4179
|
+
}
|
|
4180
|
+
function toLegacyMessages(messages) {
|
|
4181
|
+
const legacyMessages = [];
|
|
4182
|
+
for (const rawMessage of messages) {
|
|
4183
|
+
const message = rawMessage.role === "assistant" ? sanitizeAssistantReplyTags(rawMessage) : rawMessage;
|
|
4184
|
+
const timestamp = ensureIsoTimestamp(message.timestamp, (/* @__PURE__ */ new Date()).toISOString());
|
|
4185
|
+
if (message.role === "assistant") {
|
|
4186
|
+
const textContent = extractTextFromNcpMessage(message);
|
|
4187
|
+
const reasoningContent = message.parts.filter((part) => part.type === "reasoning").map((part) => part.text).join("");
|
|
4188
|
+
const toolInvocations = message.parts.filter(
|
|
4189
|
+
(part) => part.type === "tool-invocation"
|
|
4190
|
+
);
|
|
4191
|
+
const assistantMessage = {
|
|
4192
|
+
role: "assistant",
|
|
4193
|
+
content: textContent,
|
|
4194
|
+
timestamp,
|
|
4195
|
+
ncp_message_id: message.id,
|
|
4196
|
+
ncp_parts: structuredClone(message.parts)
|
|
4197
|
+
};
|
|
4198
|
+
if (typeof message.metadata?.reply_to === "string" && message.metadata.reply_to.trim().length > 0) {
|
|
4199
|
+
assistantMessage.reply_to = message.metadata.reply_to.trim();
|
|
4200
|
+
}
|
|
4201
|
+
if (reasoningContent.length > 0) {
|
|
4202
|
+
assistantMessage.reasoning_content = reasoningContent;
|
|
4203
|
+
}
|
|
4204
|
+
if (toolInvocations.length > 0) {
|
|
4205
|
+
assistantMessage.tool_calls = toolInvocations.map((toolInvocation, index) => ({
|
|
4206
|
+
id: toolInvocation.toolCallId ?? `${message.id}:tool:${index}`,
|
|
4207
|
+
type: "function",
|
|
4208
|
+
function: {
|
|
4209
|
+
name: toolInvocation.toolName,
|
|
4210
|
+
arguments: serializeToolArgs(toolInvocation.args)
|
|
4211
|
+
}
|
|
4212
|
+
}));
|
|
4213
|
+
}
|
|
4214
|
+
legacyMessages.push(assistantMessage);
|
|
4215
|
+
for (const toolInvocation of toolInvocations) {
|
|
4216
|
+
if (toolInvocation.state !== "result") {
|
|
4217
|
+
continue;
|
|
4218
|
+
}
|
|
4219
|
+
legacyMessages.push({
|
|
4220
|
+
role: "tool",
|
|
4221
|
+
name: toolInvocation.toolName,
|
|
4222
|
+
tool_call_id: toolInvocation.toolCallId,
|
|
4223
|
+
content: typeof toolInvocation.result === "string" ? toolInvocation.result : JSON.stringify(toolInvocation.result ?? null),
|
|
4224
|
+
timestamp,
|
|
4225
|
+
ncp_message_id: message.id
|
|
4226
|
+
});
|
|
4227
|
+
}
|
|
4228
|
+
continue;
|
|
4229
|
+
}
|
|
4230
|
+
legacyMessages.push({
|
|
4231
|
+
role: message.role,
|
|
4232
|
+
content: serializeLegacyContent(message.parts),
|
|
4233
|
+
timestamp,
|
|
4234
|
+
ncp_message_id: message.id
|
|
4235
|
+
});
|
|
4236
|
+
}
|
|
4237
|
+
return legacyMessages;
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
// src/cli/commands/ncp/nextclaw-ncp-tool-registry.ts
|
|
4241
|
+
import {
|
|
4242
|
+
CronTool,
|
|
4243
|
+
EditFileTool,
|
|
4244
|
+
ExecTool,
|
|
4245
|
+
ExtensionToolAdapter,
|
|
4246
|
+
GatewayTool,
|
|
4247
|
+
ListDirTool,
|
|
4248
|
+
MemoryGetTool,
|
|
4249
|
+
MemorySearchTool,
|
|
4250
|
+
MessageTool,
|
|
4251
|
+
ReadFileTool,
|
|
4252
|
+
SessionsHistoryTool,
|
|
4253
|
+
SessionsListTool,
|
|
4254
|
+
SessionsSendTool,
|
|
4255
|
+
SpawnTool,
|
|
4256
|
+
SubagentManager,
|
|
4257
|
+
SubagentsTool,
|
|
4258
|
+
ToolRegistry,
|
|
4259
|
+
WebFetchTool,
|
|
4260
|
+
WebSearchTool,
|
|
4261
|
+
WriteFileTool
|
|
4262
|
+
} from "@nextclaw/core";
|
|
4263
|
+
function toToolParams(args) {
|
|
4264
|
+
if (isRecord3(args)) {
|
|
4265
|
+
return args;
|
|
4266
|
+
}
|
|
4267
|
+
if (typeof args === "string") {
|
|
4268
|
+
try {
|
|
4269
|
+
const parsed = JSON.parse(args);
|
|
4270
|
+
return isRecord3(parsed) ? parsed : {};
|
|
4271
|
+
} catch {
|
|
4272
|
+
return {};
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
return {};
|
|
4276
|
+
}
|
|
4277
|
+
function readMetadataAccountId(metadata, sessionMetadata) {
|
|
4278
|
+
const candidates = [
|
|
4279
|
+
metadata.accountId,
|
|
4280
|
+
metadata.account_id,
|
|
4281
|
+
sessionMetadata.last_account_id
|
|
4282
|
+
];
|
|
4283
|
+
for (const candidate of candidates) {
|
|
4284
|
+
const normalized = normalizeString(candidate);
|
|
4285
|
+
if (normalized) {
|
|
4286
|
+
return normalized;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
return void 0;
|
|
4290
|
+
}
|
|
4291
|
+
var CoreToolNcpAdapter = class {
|
|
4292
|
+
constructor(tool, executeTool) {
|
|
4293
|
+
this.tool = tool;
|
|
4294
|
+
this.executeTool = executeTool;
|
|
4295
|
+
}
|
|
4296
|
+
get name() {
|
|
4297
|
+
return this.tool.name;
|
|
4298
|
+
}
|
|
4299
|
+
get description() {
|
|
4300
|
+
return this.tool.description;
|
|
4301
|
+
}
|
|
4302
|
+
get parameters() {
|
|
4303
|
+
return this.tool.parameters;
|
|
4304
|
+
}
|
|
4305
|
+
async execute(args) {
|
|
4306
|
+
return this.executeTool(this.tool.name, args);
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
var NextclawNcpToolRegistry = class {
|
|
4310
|
+
constructor(options) {
|
|
4311
|
+
this.options = options;
|
|
4312
|
+
const initialConfig = this.options.getConfig();
|
|
4313
|
+
this.subagents = new SubagentManager({
|
|
4314
|
+
providerManager: this.options.providerManager,
|
|
4315
|
+
workspace: initialConfig.agents.defaults.workspace,
|
|
4316
|
+
bus: this.options.bus,
|
|
4317
|
+
model: initialConfig.agents.defaults.model,
|
|
4318
|
+
contextTokens: initialConfig.agents.defaults.contextTokens,
|
|
4319
|
+
searchConfig: initialConfig.search,
|
|
4320
|
+
execConfig: initialConfig.tools.exec,
|
|
4321
|
+
restrictToWorkspace: initialConfig.tools.restrictToWorkspace
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
subagents;
|
|
4325
|
+
registry = new ToolRegistry();
|
|
4326
|
+
tools = /* @__PURE__ */ new Map();
|
|
4327
|
+
currentExtensionToolContext = {};
|
|
4328
|
+
prepareForRun(context) {
|
|
4329
|
+
this.subagents.updateRuntimeOptions({
|
|
4330
|
+
model: context.model,
|
|
4331
|
+
maxTokens: context.maxTokens,
|
|
4332
|
+
contextTokens: context.contextTokens,
|
|
4333
|
+
searchConfig: context.searchConfig,
|
|
4334
|
+
execConfig: { timeout: context.execTimeoutSeconds },
|
|
4335
|
+
restrictToWorkspace: context.restrictToWorkspace
|
|
4336
|
+
});
|
|
4337
|
+
this.currentExtensionToolContext = {
|
|
4338
|
+
config: context.config,
|
|
4339
|
+
workspaceDir: context.workspace,
|
|
4340
|
+
sessionKey: context.sessionId,
|
|
4341
|
+
channel: context.channel,
|
|
4342
|
+
chatId: context.chatId,
|
|
4343
|
+
sandboxed: context.restrictToWorkspace
|
|
4344
|
+
};
|
|
4345
|
+
this.registry = new ToolRegistry();
|
|
4346
|
+
this.tools.clear();
|
|
4347
|
+
this.registerDefaultTools(context);
|
|
4348
|
+
this.registerExtensionTools(context);
|
|
4349
|
+
}
|
|
4350
|
+
listTools() {
|
|
4351
|
+
return [...this.tools.values()];
|
|
4352
|
+
}
|
|
4353
|
+
getTool(name) {
|
|
4354
|
+
return this.tools.get(name);
|
|
4355
|
+
}
|
|
4356
|
+
getToolDefinitions() {
|
|
4357
|
+
return this.listTools().map((tool) => ({
|
|
4358
|
+
name: tool.name,
|
|
4359
|
+
description: tool.description,
|
|
4360
|
+
parameters: tool.parameters
|
|
4361
|
+
}));
|
|
4362
|
+
}
|
|
4363
|
+
async execute(toolCallId, toolName, args) {
|
|
4364
|
+
return this.registry.execute(toolName, toToolParams(args), toolCallId);
|
|
4365
|
+
}
|
|
4366
|
+
registerDefaultTools(context) {
|
|
4367
|
+
const allowedDir = context.restrictToWorkspace ? context.workspace : void 0;
|
|
4368
|
+
this.registerTool(new ReadFileTool(allowedDir));
|
|
4369
|
+
this.registerTool(new WriteFileTool(allowedDir));
|
|
4370
|
+
this.registerTool(new EditFileTool(allowedDir));
|
|
4371
|
+
this.registerTool(new ListDirTool(allowedDir));
|
|
4372
|
+
const execTool = new ExecTool({
|
|
4373
|
+
workingDir: context.workspace,
|
|
4374
|
+
timeout: context.execTimeoutSeconds,
|
|
4375
|
+
restrictToWorkspace: context.restrictToWorkspace
|
|
4376
|
+
});
|
|
4377
|
+
execTool.setContext({
|
|
4378
|
+
sessionKey: context.sessionId,
|
|
4379
|
+
channel: context.channel,
|
|
4380
|
+
chatId: context.chatId
|
|
4381
|
+
});
|
|
4382
|
+
this.registerTool(execTool);
|
|
4383
|
+
this.registerTool(new WebSearchTool(context.searchConfig));
|
|
4384
|
+
this.registerTool(new WebFetchTool());
|
|
4385
|
+
const messageTool = new MessageTool((message) => this.options.bus.publishOutbound(message));
|
|
4386
|
+
messageTool.setContext(context.channel, context.chatId);
|
|
4387
|
+
this.registerTool(messageTool);
|
|
4388
|
+
const spawnTool = new SpawnTool(this.subagents);
|
|
4389
|
+
spawnTool.setContext(
|
|
4390
|
+
context.channel,
|
|
4391
|
+
context.chatId,
|
|
4392
|
+
context.model,
|
|
4393
|
+
context.sessionId,
|
|
4394
|
+
context.agentId
|
|
4395
|
+
);
|
|
4396
|
+
this.registerTool(spawnTool);
|
|
4397
|
+
this.registerTool(new SessionsListTool(this.options.sessionManager));
|
|
4398
|
+
this.registerTool(new SessionsHistoryTool(this.options.sessionManager));
|
|
4399
|
+
const sessionsSendTool = new SessionsSendTool(this.options.sessionManager, this.options.bus);
|
|
4400
|
+
sessionsSendTool.setContext({
|
|
4401
|
+
currentSessionKey: context.sessionId,
|
|
4402
|
+
currentAgentId: context.agentId,
|
|
4403
|
+
channel: context.channel,
|
|
4404
|
+
chatId: context.chatId,
|
|
4405
|
+
maxPingPongTurns: context.config.session?.agentToAgent?.maxPingPongTurns ?? 0,
|
|
4406
|
+
currentHandoffDepth: context.handoffDepth
|
|
4407
|
+
});
|
|
4408
|
+
this.registerTool(sessionsSendTool);
|
|
4409
|
+
this.registerTool(new MemorySearchTool(context.workspace));
|
|
4410
|
+
this.registerTool(new MemoryGetTool(context.workspace));
|
|
4411
|
+
this.registerTool(new SubagentsTool(this.subagents));
|
|
4412
|
+
const gatewayTool = new GatewayTool(this.options.gatewayController);
|
|
4413
|
+
gatewayTool.setContext({ sessionKey: context.sessionId });
|
|
4414
|
+
this.registerTool(gatewayTool);
|
|
4415
|
+
if (this.options.cronService) {
|
|
4416
|
+
const cronTool = new CronTool(this.options.cronService);
|
|
4417
|
+
cronTool.setContext(context.channel, context.chatId);
|
|
4418
|
+
this.registerTool(cronTool);
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
4421
|
+
registerExtensionTools(context) {
|
|
4422
|
+
const extensionRegistry = this.options.getExtensionRegistry?.();
|
|
4423
|
+
if (!extensionRegistry || extensionRegistry.tools.length === 0) {
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
const seen = new Set(this.registry.toolNames);
|
|
4427
|
+
for (const registration of extensionRegistry.tools) {
|
|
4428
|
+
for (const alias of registration.names) {
|
|
4429
|
+
if (seen.has(alias)) {
|
|
4430
|
+
continue;
|
|
4431
|
+
}
|
|
4432
|
+
seen.add(alias);
|
|
4433
|
+
this.registerTool(
|
|
4434
|
+
new ExtensionToolAdapter({
|
|
4435
|
+
registration,
|
|
4436
|
+
alias,
|
|
4437
|
+
config: context.config,
|
|
4438
|
+
workspaceDir: context.workspace,
|
|
4439
|
+
contextProvider: () => this.currentExtensionToolContext,
|
|
4440
|
+
diagnostics: extensionRegistry.diagnostics
|
|
4441
|
+
})
|
|
4442
|
+
);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
registerTool(tool) {
|
|
4447
|
+
this.registry.register(tool);
|
|
4448
|
+
this.tools.set(
|
|
4449
|
+
tool.name,
|
|
4450
|
+
new CoreToolNcpAdapter(tool, async (toolName, args) => this.registry.execute(toolName, toToolParams(args)))
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
};
|
|
4454
|
+
function resolveAgentHandoffDepth(metadata) {
|
|
4455
|
+
const rawDepth = Number(metadata.agent_handoff_depth ?? 0);
|
|
4456
|
+
if (!Number.isFinite(rawDepth) || rawDepth < 0) {
|
|
4457
|
+
return 0;
|
|
4458
|
+
}
|
|
4459
|
+
return Math.trunc(rawDepth);
|
|
4460
|
+
}
|
|
4461
|
+
function readAccountIdForHints(metadata, sessionMetadata) {
|
|
4462
|
+
return readMetadataAccountId(metadata, sessionMetadata);
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
// src/cli/commands/ncp/nextclaw-ncp-context-builder.ts
|
|
4466
|
+
var TIME_HINT_TRIGGER_PATTERNS = [
|
|
4467
|
+
/\b(now|right now|current time|what time|today|tonight|tomorrow|yesterday|this morning|this afternoon|this evening|date)\b/i,
|
|
4468
|
+
/(现在|此刻|当前时间|现在几点|几点了|今天|今晚|今早|今晨|明天|昨天|日期)/
|
|
4469
|
+
];
|
|
4470
|
+
function isRecord4(value) {
|
|
4471
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4472
|
+
}
|
|
4473
|
+
function mergeInputMetadata(input) {
|
|
4474
|
+
const messageMetadata = input.messages.slice().reverse().find((message) => isRecord4(message.metadata))?.metadata;
|
|
4475
|
+
return {
|
|
4476
|
+
...isRecord4(messageMetadata) ? structuredClone(messageMetadata) : {},
|
|
4477
|
+
...isRecord4(input.metadata) ? structuredClone(input.metadata) : {}
|
|
4478
|
+
};
|
|
4479
|
+
}
|
|
4480
|
+
function resolveRequestedSkillNames(metadata) {
|
|
4481
|
+
const rawValue = metadata.requested_skills ?? metadata.requestedSkills;
|
|
4482
|
+
const values = [];
|
|
4483
|
+
if (Array.isArray(rawValue)) {
|
|
4484
|
+
for (const item of rawValue) {
|
|
4485
|
+
const normalized = normalizeString(item);
|
|
4486
|
+
if (normalized) {
|
|
4487
|
+
values.push(normalized);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
} else if (typeof rawValue === "string") {
|
|
4491
|
+
values.push(
|
|
4492
|
+
...rawValue.split(/[,\s]+/g).map((item) => item.trim()).filter(Boolean)
|
|
4493
|
+
);
|
|
4494
|
+
}
|
|
4495
|
+
return Array.from(new Set(values)).slice(0, 8);
|
|
4496
|
+
}
|
|
4497
|
+
function resolveRequestedToolNames(metadata) {
|
|
4498
|
+
const rawValue = metadata.requested_tools ?? metadata.requestedTools;
|
|
4499
|
+
if (!Array.isArray(rawValue)) {
|
|
4500
|
+
return [];
|
|
4501
|
+
}
|
|
4502
|
+
return Array.from(
|
|
4503
|
+
new Set(
|
|
4504
|
+
rawValue.map((item) => normalizeString(item)).filter((item) => Boolean(item))
|
|
4505
|
+
)
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
function normalizeOptionalString2(value) {
|
|
4509
|
+
return normalizeString(value) ?? void 0;
|
|
4510
|
+
}
|
|
4511
|
+
function readMetadataModel(metadata) {
|
|
4512
|
+
const candidates = [metadata.model, metadata.llm_model, metadata.agent_model, metadata.session_model];
|
|
4513
|
+
for (const candidate of candidates) {
|
|
4514
|
+
const normalized = normalizeString(candidate);
|
|
4515
|
+
if (normalized) {
|
|
4516
|
+
return normalized;
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
return null;
|
|
4520
|
+
}
|
|
4521
|
+
function readMetadataThinking(metadata) {
|
|
4522
|
+
const candidates = [
|
|
4523
|
+
metadata.thinking,
|
|
4524
|
+
metadata.thinking_level,
|
|
4525
|
+
metadata.thinkingLevel,
|
|
4526
|
+
metadata.thinking_effort,
|
|
4527
|
+
metadata.thinkingEffort
|
|
4528
|
+
];
|
|
4529
|
+
for (const candidate of candidates) {
|
|
4530
|
+
if (typeof candidate !== "string") {
|
|
4531
|
+
continue;
|
|
4532
|
+
}
|
|
4533
|
+
const normalized = candidate.trim().toLowerCase();
|
|
4534
|
+
if (!normalized) {
|
|
4535
|
+
continue;
|
|
4536
|
+
}
|
|
4537
|
+
if (normalized === "clear" || normalized === "reset" || normalized === "off!") {
|
|
4538
|
+
return "__clear__";
|
|
4539
|
+
}
|
|
4540
|
+
const level = parseThinkingLevel(normalized);
|
|
4541
|
+
if (level) {
|
|
4542
|
+
return level;
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
return null;
|
|
4546
|
+
}
|
|
4547
|
+
function resolvePrimaryAgentProfile(config2) {
|
|
4548
|
+
const configuredDefaultAgentId = config2.agents.list.find((entry) => entry.default)?.id?.trim() || config2.agents.list[0]?.id?.trim() || "main";
|
|
4549
|
+
const profile = config2.agents.list.find((entry) => entry.id.trim() === configuredDefaultAgentId);
|
|
4550
|
+
return {
|
|
4551
|
+
agentId: configuredDefaultAgentId,
|
|
4552
|
+
workspace: getWorkspacePath7(profile?.workspace ?? config2.agents.defaults.workspace),
|
|
4553
|
+
model: profile?.model ?? config2.agents.defaults.model,
|
|
4554
|
+
maxIterations: profile?.maxToolIterations ?? config2.agents.defaults.maxToolIterations,
|
|
4555
|
+
contextTokens: profile?.contextTokens ?? config2.agents.defaults.contextTokens,
|
|
4556
|
+
restrictToWorkspace: config2.tools.restrictToWorkspace,
|
|
4557
|
+
searchConfig: config2.search,
|
|
4558
|
+
execTimeoutSeconds: config2.tools.exec.timeout
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function shouldAppendTimeHint(content) {
|
|
4562
|
+
const normalized = content.trim();
|
|
4563
|
+
if (!normalized) {
|
|
4564
|
+
return false;
|
|
4565
|
+
}
|
|
4566
|
+
return TIME_HINT_TRIGGER_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
4567
|
+
}
|
|
4568
|
+
function buildMinutePrecisionTimeHint(date) {
|
|
4569
|
+
const year = date.getFullYear();
|
|
4570
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
4571
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
4572
|
+
const hour = String(date.getHours()).padStart(2, "0");
|
|
4573
|
+
const minute = String(date.getMinutes()).padStart(2, "0");
|
|
4574
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
4575
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
4576
|
+
const absMinutes = Math.abs(offsetMinutes);
|
|
4577
|
+
const offsetHour = String(Math.floor(absMinutes / 60)).padStart(2, "0");
|
|
4578
|
+
const offsetMinute = String(absMinutes % 60).padStart(2, "0");
|
|
4579
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
|
|
4580
|
+
return `${year}-${month}-${day} ${hour}:${minute} ${sign}${offsetHour}:${offsetMinute} (${timezone})`;
|
|
4581
|
+
}
|
|
4582
|
+
function appendTimeHintForPrompt(content, timestamp) {
|
|
4583
|
+
if (!shouldAppendTimeHint(content)) {
|
|
4584
|
+
return content;
|
|
4585
|
+
}
|
|
4586
|
+
const date = Number.isNaN(timestamp.getTime()) ? /* @__PURE__ */ new Date() : timestamp;
|
|
4587
|
+
return `${content}
|
|
4588
|
+
|
|
4589
|
+
[time_hint_local_minute] ${buildMinutePrecisionTimeHint(date)}`;
|
|
4590
|
+
}
|
|
4591
|
+
function prependRequestedSkills(content, requestedSkillNames) {
|
|
4592
|
+
if (requestedSkillNames.length === 0) {
|
|
4593
|
+
return content;
|
|
4594
|
+
}
|
|
4595
|
+
return `[Requested skills for this turn: ${requestedSkillNames.join(", ")}]
|
|
4596
|
+
|
|
4597
|
+
${content}`;
|
|
4598
|
+
}
|
|
4599
|
+
function filterTools(toolDefinitions, requestedToolNames) {
|
|
4600
|
+
if (toolDefinitions.length === 0) {
|
|
4601
|
+
return void 0;
|
|
4602
|
+
}
|
|
4603
|
+
if (requestedToolNames.length === 0) {
|
|
4604
|
+
return [...toolDefinitions];
|
|
4605
|
+
}
|
|
4606
|
+
const requested = new Set(requestedToolNames);
|
|
4607
|
+
const filtered = toolDefinitions.filter((tool) => requested.has(tool.function.name));
|
|
4608
|
+
return filtered.length > 0 ? filtered : void 0;
|
|
4609
|
+
}
|
|
4610
|
+
var NextclawNcpContextBuilder = class {
|
|
4611
|
+
constructor(options) {
|
|
4612
|
+
this.options = options;
|
|
4613
|
+
}
|
|
4614
|
+
inputBudgetPruner = new InputBudgetPruner();
|
|
4615
|
+
prepare(input, _options) {
|
|
4616
|
+
const config2 = this.options.getConfig();
|
|
4617
|
+
const profile = resolvePrimaryAgentProfile(config2);
|
|
4618
|
+
const requestMetadata = mergeInputMetadata(input);
|
|
4619
|
+
const session = this.options.sessionManager.getOrCreate(input.sessionId);
|
|
4620
|
+
const clearModel = requestMetadata.clear_model === true || requestMetadata.reset_model === true;
|
|
4621
|
+
if (clearModel) {
|
|
4622
|
+
delete session.metadata.preferred_model;
|
|
4623
|
+
}
|
|
4624
|
+
const inboundModel = readMetadataModel(requestMetadata);
|
|
4625
|
+
if (inboundModel) {
|
|
4626
|
+
session.metadata.preferred_model = inboundModel;
|
|
4627
|
+
}
|
|
4628
|
+
const effectiveModel = normalizeOptionalString2(session.metadata.preferred_model) ?? profile.model;
|
|
4629
|
+
const clearThinking = requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true;
|
|
4630
|
+
if (clearThinking) {
|
|
4631
|
+
delete session.metadata.preferred_thinking;
|
|
4632
|
+
}
|
|
4633
|
+
const inboundThinking = readMetadataThinking(requestMetadata);
|
|
4634
|
+
if (inboundThinking === "__clear__") {
|
|
4635
|
+
delete session.metadata.preferred_thinking;
|
|
4636
|
+
} else if (inboundThinking) {
|
|
4637
|
+
session.metadata.preferred_thinking = inboundThinking;
|
|
4638
|
+
}
|
|
4639
|
+
const runtimeThinking = resolveThinkingLevel({
|
|
4640
|
+
config: config2,
|
|
4641
|
+
agentId: profile.agentId,
|
|
4642
|
+
model: effectiveModel,
|
|
4643
|
+
sessionThinkingLevel: parseThinkingLevel(session.metadata.preferred_thinking) ?? null
|
|
4644
|
+
});
|
|
4645
|
+
const channel = normalizeOptionalString2(requestMetadata.channel) ?? normalizeOptionalString2(session.metadata.last_channel) ?? "ui";
|
|
4646
|
+
const chatId = normalizeOptionalString2(requestMetadata.chatId) ?? normalizeOptionalString2(requestMetadata.chat_id) ?? normalizeOptionalString2(session.metadata.last_to) ?? "web-ui";
|
|
4647
|
+
session.metadata.last_channel = channel;
|
|
4648
|
+
session.metadata.last_to = chatId;
|
|
4649
|
+
const requestedSkillNames = resolveRequestedSkillNames(requestMetadata);
|
|
4650
|
+
const requestedToolNames = resolveRequestedToolNames(requestMetadata);
|
|
4651
|
+
const currentUserText = extractTextFromNcpMessage(input.messages[input.messages.length - 1]);
|
|
4652
|
+
const currentMessage = appendTimeHintForPrompt(
|
|
4653
|
+
prependRequestedSkills(currentUserText, requestedSkillNames),
|
|
4654
|
+
new Date(
|
|
4655
|
+
ensureIsoTimestamp(
|
|
4656
|
+
input.messages[input.messages.length - 1]?.timestamp,
|
|
4657
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
4658
|
+
)
|
|
4659
|
+
)
|
|
4660
|
+
);
|
|
4661
|
+
this.options.toolRegistry.prepareForRun({
|
|
4662
|
+
sessionId: input.sessionId,
|
|
4663
|
+
channel,
|
|
4664
|
+
chatId,
|
|
4665
|
+
agentId: profile.agentId,
|
|
4666
|
+
config: config2,
|
|
4667
|
+
contextTokens: profile.contextTokens,
|
|
4668
|
+
execTimeoutSeconds: profile.execTimeoutSeconds,
|
|
4669
|
+
handoffDepth: resolveAgentHandoffDepth(requestMetadata),
|
|
4670
|
+
maxTokens: void 0,
|
|
4671
|
+
metadata: requestMetadata,
|
|
4672
|
+
model: effectiveModel,
|
|
4673
|
+
restrictToWorkspace: profile.restrictToWorkspace,
|
|
4674
|
+
searchConfig: profile.searchConfig,
|
|
4675
|
+
workspace: profile.workspace
|
|
4676
|
+
});
|
|
4677
|
+
const accountId = readAccountIdForHints(requestMetadata, session.metadata);
|
|
4678
|
+
const messageToolHints = this.options.resolveMessageToolHints?.({
|
|
4679
|
+
sessionKey: input.sessionId,
|
|
4680
|
+
channel,
|
|
4681
|
+
chatId,
|
|
4682
|
+
accountId: accountId ?? null
|
|
4683
|
+
});
|
|
4684
|
+
const contextBuilder = new ContextBuilder(profile.workspace, config2.agents.context);
|
|
4685
|
+
const sessionMessages = _options?.sessionMessages ?? [];
|
|
4686
|
+
const messages = contextBuilder.buildMessages({
|
|
4687
|
+
history: toLegacyMessages([...sessionMessages]),
|
|
4688
|
+
currentMessage,
|
|
4689
|
+
channel,
|
|
4690
|
+
chatId,
|
|
4691
|
+
sessionKey: input.sessionId,
|
|
4692
|
+
thinkingLevel: runtimeThinking,
|
|
4693
|
+
skillNames: requestedSkillNames,
|
|
4694
|
+
messageToolHints
|
|
4695
|
+
});
|
|
4696
|
+
const pruned = this.inputBudgetPruner.prune({
|
|
4697
|
+
messages,
|
|
4698
|
+
contextTokens: profile.contextTokens
|
|
4699
|
+
});
|
|
4700
|
+
const toolDefinitions = this.options.toolRegistry.getToolDefinitions().map((tool) => ({
|
|
4701
|
+
type: "function",
|
|
4702
|
+
function: {
|
|
4703
|
+
name: tool.name,
|
|
4704
|
+
description: tool.description,
|
|
4705
|
+
parameters: tool.parameters
|
|
4706
|
+
}
|
|
4707
|
+
}));
|
|
4708
|
+
return {
|
|
4709
|
+
messages: pruned.messages,
|
|
4710
|
+
tools: filterTools(toolDefinitions, requestedToolNames),
|
|
4711
|
+
model: effectiveModel,
|
|
4712
|
+
thinkingLevel: runtimeThinking
|
|
4713
|
+
};
|
|
4714
|
+
}
|
|
4715
|
+
};
|
|
4716
|
+
|
|
4717
|
+
// src/cli/commands/ncp/nextclaw-agent-session-store.ts
|
|
4718
|
+
import { sanitizeAssistantReplyTags as sanitizeAssistantReplyTags2 } from "@nextclaw/ncp";
|
|
4719
|
+
function tryParseJson(value) {
|
|
4720
|
+
try {
|
|
4721
|
+
return JSON.parse(value);
|
|
4722
|
+
} catch {
|
|
4723
|
+
return value;
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
function toTextPart(text) {
|
|
4727
|
+
return text.length > 0 ? { type: "text", text } : null;
|
|
4728
|
+
}
|
|
4729
|
+
function contentToParts(content) {
|
|
4730
|
+
if (typeof content === "string") {
|
|
4731
|
+
const textPart = toTextPart(content);
|
|
4732
|
+
return textPart ? [textPart] : [];
|
|
4733
|
+
}
|
|
4734
|
+
if (Array.isArray(content)) {
|
|
4735
|
+
return content.length > 0 ? [
|
|
4736
|
+
{
|
|
4737
|
+
type: "extension",
|
|
4738
|
+
extensionType: "nextclaw.legacy.content-array",
|
|
4739
|
+
data: structuredClone(content)
|
|
4740
|
+
}
|
|
4741
|
+
] : [];
|
|
4742
|
+
}
|
|
4743
|
+
if (content && typeof content === "object") {
|
|
4744
|
+
return [
|
|
4745
|
+
{
|
|
4746
|
+
type: "extension",
|
|
4747
|
+
extensionType: "nextclaw.legacy.content-object",
|
|
4748
|
+
data: structuredClone(content)
|
|
4749
|
+
}
|
|
4750
|
+
];
|
|
4751
|
+
}
|
|
4752
|
+
return [];
|
|
4753
|
+
}
|
|
4754
|
+
function parseLegacyToolCalls(value) {
|
|
4755
|
+
if (!Array.isArray(value)) {
|
|
4756
|
+
return [];
|
|
4757
|
+
}
|
|
4758
|
+
return value.map((entry) => {
|
|
4759
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
4760
|
+
return null;
|
|
4761
|
+
}
|
|
4762
|
+
const toolCall = entry;
|
|
4763
|
+
const id = normalizeString(toolCall.id);
|
|
4764
|
+
const rawFunction = toolCall.function;
|
|
4765
|
+
if (!id || !rawFunction || typeof rawFunction !== "object" || Array.isArray(rawFunction)) {
|
|
4766
|
+
return null;
|
|
4767
|
+
}
|
|
4768
|
+
const fn = rawFunction;
|
|
4769
|
+
const name = normalizeString(fn.name);
|
|
4770
|
+
const args = typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {});
|
|
4771
|
+
if (!name) {
|
|
4772
|
+
return null;
|
|
4773
|
+
}
|
|
4774
|
+
return {
|
|
4775
|
+
id,
|
|
4776
|
+
type: "function",
|
|
4777
|
+
function: {
|
|
4778
|
+
name,
|
|
4779
|
+
arguments: args
|
|
4780
|
+
}
|
|
4781
|
+
};
|
|
4782
|
+
}).filter((entry) => entry !== null);
|
|
4783
|
+
}
|
|
4784
|
+
function createMessageId(sessionId, index, role, timestamp) {
|
|
4785
|
+
const safeRole = role.trim().toLowerCase() || "message";
|
|
4786
|
+
return `${sessionId}:${safeRole}:${index}:${timestamp}`;
|
|
4787
|
+
}
|
|
4788
|
+
function isNcpMessagePart(value) {
|
|
4789
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.type === "string";
|
|
4790
|
+
}
|
|
4791
|
+
function readStoredNcpParts(message) {
|
|
4792
|
+
const rawParts = message.ncp_parts;
|
|
4793
|
+
if (!Array.isArray(rawParts)) {
|
|
4794
|
+
return null;
|
|
4795
|
+
}
|
|
4796
|
+
const parts = rawParts.filter((part) => isNcpMessagePart(part));
|
|
4797
|
+
if (parts.length !== rawParts.length) {
|
|
4798
|
+
return null;
|
|
4799
|
+
}
|
|
4800
|
+
return structuredClone(parts);
|
|
4801
|
+
}
|
|
4802
|
+
function buildAssistantMessage(params) {
|
|
4803
|
+
const timestamp = ensureIsoTimestamp(params.message.timestamp, (/* @__PURE__ */ new Date()).toISOString());
|
|
4804
|
+
const replyTo = typeof params.message.reply_to === "string" && params.message.reply_to.trim().length > 0 ? params.message.reply_to.trim() : void 0;
|
|
4805
|
+
const storedParts = readStoredNcpParts(params.message);
|
|
4806
|
+
if (storedParts) {
|
|
4807
|
+
return sanitizeAssistantReplyTags2({
|
|
4808
|
+
id: createMessageId(params.sessionId, params.index, "assistant", timestamp),
|
|
4809
|
+
sessionId: params.sessionId,
|
|
4810
|
+
role: "assistant",
|
|
4811
|
+
status: "final",
|
|
4812
|
+
timestamp,
|
|
4813
|
+
parts: storedParts,
|
|
4814
|
+
metadata: replyTo ? { reply_to: replyTo } : void 0
|
|
4815
|
+
});
|
|
4816
|
+
}
|
|
4817
|
+
const toolCalls = parseLegacyToolCalls(params.message.tool_calls);
|
|
4818
|
+
const parts = [...contentToParts(params.message.content)];
|
|
4819
|
+
const reasoning = normalizeString(params.message.reasoning_content);
|
|
4820
|
+
if (reasoning) {
|
|
4821
|
+
parts.push({
|
|
4822
|
+
type: "reasoning",
|
|
4823
|
+
text: reasoning
|
|
4824
|
+
});
|
|
4825
|
+
}
|
|
4826
|
+
for (const toolCall of toolCalls) {
|
|
4827
|
+
parts.push({
|
|
4828
|
+
type: "tool-invocation",
|
|
4829
|
+
toolCallId: toolCall.id,
|
|
4830
|
+
toolName: toolCall.function.name,
|
|
4831
|
+
state: "call",
|
|
4832
|
+
args: tryParseJson(toolCall.function.arguments)
|
|
4833
|
+
});
|
|
4834
|
+
}
|
|
4835
|
+
return sanitizeAssistantReplyTags2({
|
|
4836
|
+
id: createMessageId(params.sessionId, params.index, "assistant", timestamp),
|
|
4837
|
+
sessionId: params.sessionId,
|
|
4838
|
+
role: "assistant",
|
|
4839
|
+
status: "final",
|
|
4840
|
+
timestamp,
|
|
4841
|
+
parts,
|
|
4842
|
+
metadata: replyTo ? { reply_to: replyTo } : void 0
|
|
4843
|
+
});
|
|
4844
|
+
}
|
|
4845
|
+
function buildGenericMessage(params) {
|
|
4846
|
+
const timestamp = ensureIsoTimestamp(params.message.timestamp, (/* @__PURE__ */ new Date()).toISOString());
|
|
4847
|
+
return {
|
|
4848
|
+
id: createMessageId(params.sessionId, params.index, params.role, timestamp),
|
|
4849
|
+
sessionId: params.sessionId,
|
|
4850
|
+
role: params.role,
|
|
4851
|
+
status: "final",
|
|
4852
|
+
timestamp,
|
|
4853
|
+
parts: contentToParts(params.message.content)
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
function attachToolResult(target, toolCallId, result, toolName) {
|
|
4857
|
+
target.parts = target.parts.map((part) => {
|
|
4858
|
+
if (part.type !== "tool-invocation" || part.toolCallId !== toolCallId) {
|
|
4859
|
+
return part;
|
|
4860
|
+
}
|
|
4861
|
+
return {
|
|
4862
|
+
...part,
|
|
4863
|
+
toolName: toolName ?? part.toolName,
|
|
4864
|
+
state: "result",
|
|
4865
|
+
result
|
|
4866
|
+
};
|
|
4867
|
+
});
|
|
4868
|
+
}
|
|
4869
|
+
function toNcpMessages(sessionId, messages) {
|
|
4870
|
+
const ncpMessages = [];
|
|
4871
|
+
const assistantIndexByToolCallId = /* @__PURE__ */ new Map();
|
|
4872
|
+
messages.forEach((message, index) => {
|
|
4873
|
+
const role = normalizeString(message.role)?.toLowerCase() ?? "assistant";
|
|
4874
|
+
if (role === "tool") {
|
|
4875
|
+
const toolCallId = normalizeString(message.tool_call_id);
|
|
4876
|
+
if (toolCallId) {
|
|
4877
|
+
const assistantIndex = assistantIndexByToolCallId.get(toolCallId);
|
|
4878
|
+
if (assistantIndex !== void 0) {
|
|
4879
|
+
attachToolResult(
|
|
4880
|
+
ncpMessages[assistantIndex],
|
|
4881
|
+
toolCallId,
|
|
4882
|
+
structuredClone(message.content),
|
|
4883
|
+
normalizeString(message.name) ?? void 0
|
|
4884
|
+
);
|
|
4885
|
+
return;
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
ncpMessages.push(
|
|
4889
|
+
buildGenericMessage({
|
|
4890
|
+
sessionId,
|
|
4891
|
+
index,
|
|
4892
|
+
role: "tool",
|
|
4893
|
+
message
|
|
4894
|
+
})
|
|
4895
|
+
);
|
|
4896
|
+
return;
|
|
4897
|
+
}
|
|
4898
|
+
if (role === "assistant") {
|
|
4899
|
+
const assistant = buildAssistantMessage({ sessionId, index, message });
|
|
4900
|
+
const assistantPosition = ncpMessages.push(assistant) - 1;
|
|
4901
|
+
for (const part of assistant.parts) {
|
|
4902
|
+
if (part.type === "tool-invocation" && part.toolCallId) {
|
|
4903
|
+
assistantIndexByToolCallId.set(part.toolCallId, assistantPosition);
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
const normalizedRole = role === "system" || role === "user" || role === "service" ? role : "user";
|
|
4909
|
+
ncpMessages.push(
|
|
4910
|
+
buildGenericMessage({
|
|
4911
|
+
sessionId,
|
|
4912
|
+
index,
|
|
4913
|
+
role: normalizedRole,
|
|
4914
|
+
message
|
|
4915
|
+
})
|
|
4916
|
+
);
|
|
4917
|
+
});
|
|
4918
|
+
return ncpMessages;
|
|
4919
|
+
}
|
|
4920
|
+
function resolveLegacyEventType(message) {
|
|
4921
|
+
const role = normalizeString(message.role)?.toLowerCase() ?? "";
|
|
4922
|
+
if (role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
4923
|
+
return "assistant.tool_call";
|
|
4924
|
+
}
|
|
4925
|
+
if (role === "tool") {
|
|
4926
|
+
return "tool.result";
|
|
4927
|
+
}
|
|
4928
|
+
if (role === "assistant") {
|
|
4929
|
+
return "message.assistant";
|
|
4930
|
+
}
|
|
4931
|
+
if (role === "user") {
|
|
4932
|
+
return "message.user";
|
|
4933
|
+
}
|
|
4934
|
+
if (role === "system") {
|
|
4935
|
+
return "message.system";
|
|
4936
|
+
}
|
|
4937
|
+
return `message.${role || "other"}`;
|
|
4938
|
+
}
|
|
4939
|
+
var NextclawAgentSessionStore = class {
|
|
4940
|
+
constructor(sessionManager, options = {}) {
|
|
4941
|
+
this.sessionManager = sessionManager;
|
|
4942
|
+
this.options = options;
|
|
4943
|
+
}
|
|
4944
|
+
async getSession(sessionId) {
|
|
4945
|
+
const session = this.sessionManager.getIfExists(sessionId);
|
|
4946
|
+
if (!session) {
|
|
4947
|
+
return null;
|
|
4948
|
+
}
|
|
4949
|
+
return {
|
|
4950
|
+
sessionId,
|
|
4951
|
+
messages: toNcpMessages(sessionId, session.messages),
|
|
4952
|
+
updatedAt: session.updatedAt.toISOString(),
|
|
4953
|
+
metadata: structuredClone(session.metadata)
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4956
|
+
async listSessions() {
|
|
4957
|
+
const records = this.sessionManager.listSessions();
|
|
4958
|
+
const sessions = [];
|
|
4959
|
+
for (const record of records) {
|
|
4960
|
+
const sessionId = normalizeString(record.key);
|
|
4961
|
+
if (!sessionId) {
|
|
4962
|
+
continue;
|
|
4963
|
+
}
|
|
4964
|
+
const session = this.sessionManager.getIfExists(sessionId);
|
|
4965
|
+
if (!session) {
|
|
4966
|
+
continue;
|
|
4967
|
+
}
|
|
4968
|
+
sessions.push({
|
|
4969
|
+
sessionId,
|
|
4970
|
+
messages: toNcpMessages(sessionId, session.messages),
|
|
4971
|
+
updatedAt: session.updatedAt.toISOString(),
|
|
4972
|
+
metadata: structuredClone(session.metadata)
|
|
4973
|
+
});
|
|
4974
|
+
}
|
|
4975
|
+
sessions.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
4976
|
+
return sessions;
|
|
4977
|
+
}
|
|
4978
|
+
async saveSession(sessionRecord) {
|
|
4979
|
+
if (this.options.writeMode === "runtime-owned") {
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
const session = this.sessionManager.getIfExists(sessionRecord.sessionId) ?? this.sessionManager.getOrCreate(sessionRecord.sessionId);
|
|
4983
|
+
const legacyMessages = toLegacyMessages(sessionRecord.messages);
|
|
4984
|
+
const nextMetadata = mergeSessionMetadata(
|
|
4985
|
+
session.metadata,
|
|
4986
|
+
extractMessageMetadata(sessionRecord.messages)
|
|
4987
|
+
);
|
|
4988
|
+
session.metadata = mergeSessionMetadata(nextMetadata, cloneMetadata(sessionRecord.metadata));
|
|
4989
|
+
this.sessionManager.clear(session);
|
|
4990
|
+
for (const message of legacyMessages) {
|
|
4991
|
+
this.sessionManager.appendEvent(session, {
|
|
4992
|
+
type: resolveLegacyEventType(message),
|
|
4993
|
+
timestamp: ensureIsoTimestamp(message.timestamp, (/* @__PURE__ */ new Date()).toISOString()),
|
|
4994
|
+
data: {
|
|
4995
|
+
message
|
|
4996
|
+
}
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
if (legacyMessages.length === 0) {
|
|
5000
|
+
session.updatedAt = new Date(ensureIsoTimestamp(sessionRecord.updatedAt, (/* @__PURE__ */ new Date()).toISOString()));
|
|
5001
|
+
}
|
|
5002
|
+
this.sessionManager.save(session);
|
|
5003
|
+
}
|
|
5004
|
+
async deleteSession(sessionId) {
|
|
5005
|
+
const existing = await this.getSession(sessionId);
|
|
5006
|
+
if (!existing) {
|
|
5007
|
+
return null;
|
|
5008
|
+
}
|
|
5009
|
+
this.sessionManager.delete(sessionId);
|
|
5010
|
+
return existing;
|
|
5011
|
+
}
|
|
5012
|
+
};
|
|
5013
|
+
|
|
5014
|
+
// src/cli/commands/ncp/provider-manager-ncp-llm-api.ts
|
|
5015
|
+
import { parseThinkingLevel as parseThinkingLevel2 } from "@nextclaw/core";
|
|
5016
|
+
function normalizeModel(value) {
|
|
5017
|
+
if (typeof value !== "string") {
|
|
5018
|
+
return null;
|
|
5019
|
+
}
|
|
5020
|
+
const trimmed = value.trim();
|
|
5021
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
5022
|
+
}
|
|
5023
|
+
function normalizeThinkingLevel(value) {
|
|
5024
|
+
return parseThinkingLevel2(value);
|
|
5025
|
+
}
|
|
5026
|
+
function normalizeFinishReason(value) {
|
|
5027
|
+
if (typeof value !== "string") {
|
|
5028
|
+
return "stop";
|
|
5029
|
+
}
|
|
5030
|
+
const trimmed = value.trim();
|
|
5031
|
+
return trimmed.length > 0 ? trimmed : "stop";
|
|
5032
|
+
}
|
|
5033
|
+
function toToolCallDelta(toolCall, index) {
|
|
5034
|
+
return {
|
|
5035
|
+
index,
|
|
5036
|
+
id: toolCall.id,
|
|
5037
|
+
type: "function",
|
|
5038
|
+
function: {
|
|
5039
|
+
name: toolCall.name,
|
|
5040
|
+
arguments: JSON.stringify(toolCall.arguments ?? {})
|
|
5041
|
+
}
|
|
5042
|
+
};
|
|
5043
|
+
}
|
|
5044
|
+
function toFinalChunk(response, options) {
|
|
5045
|
+
const delta = {};
|
|
5046
|
+
if (options.includeText && typeof response.content === "string" && response.content.length > 0) {
|
|
5047
|
+
delta.content = response.content;
|
|
5048
|
+
}
|
|
5049
|
+
if (options.includeReasoning && typeof response.reasoningContent === "string" && response.reasoningContent.length > 0) {
|
|
5050
|
+
delta.reasoning_content = response.reasoningContent;
|
|
5051
|
+
}
|
|
5052
|
+
if (options.includeToolCalls && response.toolCalls.length > 0) {
|
|
5053
|
+
delta.tool_calls = response.toolCalls.map((toolCall, index) => toToolCallDelta(toolCall, index));
|
|
5054
|
+
}
|
|
5055
|
+
return {
|
|
5056
|
+
choices: [
|
|
5057
|
+
{
|
|
5058
|
+
delta,
|
|
5059
|
+
finish_reason: normalizeFinishReason(response.finishReason)
|
|
5060
|
+
}
|
|
5061
|
+
],
|
|
5062
|
+
usage: response.usage
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
5065
|
+
var ProviderManagerNcpLLMApi = class {
|
|
5066
|
+
constructor(providerManager) {
|
|
5067
|
+
this.providerManager = providerManager;
|
|
5068
|
+
}
|
|
5069
|
+
async *generate(input, options) {
|
|
5070
|
+
const model = normalizeModel(input.model) ?? this.providerManager.get(null).getDefaultModel();
|
|
5071
|
+
const thinkingLevel = normalizeThinkingLevel(input.thinkingLevel);
|
|
5072
|
+
let sawTextDelta = false;
|
|
5073
|
+
let sawReasoningDelta = false;
|
|
5074
|
+
let sawToolCallDelta = false;
|
|
5075
|
+
for await (const event of this.providerManager.chatStream({
|
|
5076
|
+
messages: input.messages,
|
|
5077
|
+
tools: input.tools,
|
|
5078
|
+
model,
|
|
5079
|
+
...thinkingLevel ? { thinkingLevel } : {},
|
|
5080
|
+
maxTokens: input.max_tokens,
|
|
5081
|
+
signal: options?.signal
|
|
5082
|
+
})) {
|
|
5083
|
+
if (event.type === "delta") {
|
|
5084
|
+
sawTextDelta = true;
|
|
5085
|
+
yield {
|
|
5086
|
+
choices: [
|
|
5087
|
+
{
|
|
5088
|
+
delta: {
|
|
5089
|
+
content: event.delta
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
]
|
|
5093
|
+
};
|
|
5094
|
+
continue;
|
|
5095
|
+
}
|
|
5096
|
+
if (event.type === "reasoning_delta") {
|
|
5097
|
+
sawReasoningDelta = true;
|
|
5098
|
+
yield {
|
|
5099
|
+
choices: [
|
|
5100
|
+
{
|
|
5101
|
+
delta: {
|
|
5102
|
+
reasoning_content: event.delta
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
5105
|
+
]
|
|
5106
|
+
};
|
|
5107
|
+
continue;
|
|
5108
|
+
}
|
|
5109
|
+
if (event.type === "tool_call_delta") {
|
|
5110
|
+
sawToolCallDelta = true;
|
|
5111
|
+
yield {
|
|
5112
|
+
choices: [
|
|
5113
|
+
{
|
|
5114
|
+
delta: {
|
|
5115
|
+
tool_calls: event.toolCalls
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
]
|
|
5119
|
+
};
|
|
5120
|
+
continue;
|
|
5121
|
+
}
|
|
5122
|
+
yield toFinalChunk(event.response, {
|
|
5123
|
+
includeText: !sawTextDelta,
|
|
5124
|
+
includeReasoning: !sawReasoningDelta,
|
|
5125
|
+
includeToolCalls: !sawToolCallDelta
|
|
5126
|
+
});
|
|
5127
|
+
}
|
|
3460
5128
|
}
|
|
3461
5129
|
};
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
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;
|
|
3465
5137
|
}
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
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
|
+
});
|
|
3470
5167
|
}
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
break;
|
|
3477
|
-
}
|
|
3478
|
-
const option = options[i];
|
|
3479
|
-
const isLastOption = i === options.length - 1;
|
|
3480
|
-
const rawValue = isLastOption ? tokens.slice(cursor).join(" ") : tokens[cursor];
|
|
3481
|
-
cursor += isLastOption ? tokens.length - cursor : 1;
|
|
3482
|
-
const parsedValue = parseCommandOptionValue(option.type, rawValue);
|
|
3483
|
-
if (parsedValue !== void 0) {
|
|
3484
|
-
args[option.name] = parsedValue;
|
|
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}`);
|
|
3485
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
|
+
});
|
|
3486
5183
|
}
|
|
3487
|
-
|
|
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
|
+
|
|
5204
|
+
// src/cli/commands/ncp/create-ui-ncp-agent.ts
|
|
5205
|
+
function isRecord5(value) {
|
|
5206
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3488
5207
|
}
|
|
3489
|
-
function
|
|
3490
|
-
const
|
|
3491
|
-
|
|
3492
|
-
|
|
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
|
+
}
|
|
5222
|
+
async function createUiNcpAgent(params) {
|
|
5223
|
+
const sessionStore = new NextclawAgentSessionStore(params.sessionManager);
|
|
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
|
+
}
|
|
5241
|
+
const toolRegistry = new NextclawNcpToolRegistry({
|
|
5242
|
+
bus: params.bus,
|
|
5243
|
+
providerManager: params.providerManager,
|
|
5244
|
+
sessionManager: params.sessionManager,
|
|
5245
|
+
cronService: params.cronService,
|
|
5246
|
+
gatewayController: params.gatewayController,
|
|
5247
|
+
getConfig: params.getConfig,
|
|
5248
|
+
getExtensionRegistry: params.getExtensionRegistry
|
|
5249
|
+
});
|
|
5250
|
+
return new DefaultNcpAgentRuntime({
|
|
5251
|
+
contextBuilder: new NextclawNcpContextBuilder({
|
|
5252
|
+
sessionManager: params.sessionManager,
|
|
5253
|
+
toolRegistry,
|
|
5254
|
+
getConfig: params.getConfig,
|
|
5255
|
+
resolveMessageToolHints: params.resolveMessageToolHints
|
|
5256
|
+
}),
|
|
5257
|
+
llmApi: new ProviderManagerNcpLLMApi(params.providerManager),
|
|
5258
|
+
toolRegistry,
|
|
5259
|
+
stateManager,
|
|
5260
|
+
reasoningNormalizationMode
|
|
5261
|
+
});
|
|
5262
|
+
}
|
|
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
|
+
});
|
|
5304
|
+
await backend.start();
|
|
5305
|
+
return {
|
|
5306
|
+
basePath: "/api/ncp/agent",
|
|
5307
|
+
agentClientEndpoint: createAgentClientFromServer(backend),
|
|
5308
|
+
streamProvider: backend,
|
|
5309
|
+
sessionApi: backend,
|
|
5310
|
+
listSessionTypes: () => {
|
|
5311
|
+
refreshPluginRuntimeRegistrations();
|
|
5312
|
+
return runtimeRegistry.listSessionTypes();
|
|
5313
|
+
},
|
|
5314
|
+
applyExtensionRegistry: (extensionRegistry) => {
|
|
5315
|
+
activeExtensionRegistry = extensionRegistry;
|
|
5316
|
+
syncPluginRuntimeRegistrations(extensionRegistry);
|
|
5317
|
+
}
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
|
|
5321
|
+
// src/cli/commands/service-marketplace-helpers.ts
|
|
5322
|
+
var containsAbsoluteFsPath = (line) => {
|
|
5323
|
+
const normalized = line.trim();
|
|
5324
|
+
if (!normalized) {
|
|
5325
|
+
return false;
|
|
3493
5326
|
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
return
|
|
5327
|
+
const lowered = normalized.toLowerCase();
|
|
5328
|
+
if (lowered.includes("http://") || lowered.includes("https://")) {
|
|
5329
|
+
return false;
|
|
3497
5330
|
}
|
|
3498
|
-
if (
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
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;
|
|
3502
5344
|
}
|
|
3503
|
-
if (
|
|
5345
|
+
if (containsAbsoluteFsPath(line)) {
|
|
3504
5346
|
return false;
|
|
3505
5347
|
}
|
|
3506
|
-
return
|
|
3507
|
-
}
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
function formatUserFacingError(error, maxChars = 320) {
|
|
3511
|
-
const raw = error instanceof Error ? error.message || error.name || "Unknown error" : String(error ?? "Unknown error");
|
|
3512
|
-
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
3513
|
-
if (!normalized) {
|
|
3514
|
-
return "Unknown error";
|
|
5348
|
+
return true;
|
|
5349
|
+
});
|
|
5350
|
+
if (visibleLines.length === 0) {
|
|
5351
|
+
return fallback;
|
|
3515
5352
|
}
|
|
3516
|
-
|
|
3517
|
-
|
|
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");
|
|
3518
5362
|
}
|
|
3519
|
-
return
|
|
3520
|
-
}
|
|
5363
|
+
return args;
|
|
5364
|
+
};
|
|
3521
5365
|
|
|
3522
5366
|
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
3523
|
-
import { existsSync as
|
|
5367
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
3524
5368
|
import { join as join4 } from "path";
|
|
3525
5369
|
import {
|
|
3526
5370
|
getDataDir as getDataDir5,
|
|
@@ -3536,7 +5380,7 @@ function createRunId() {
|
|
|
3536
5380
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
3537
5381
|
return `run-${now}-${rand}`;
|
|
3538
5382
|
}
|
|
3539
|
-
function
|
|
5383
|
+
function isRecord6(value) {
|
|
3540
5384
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3541
5385
|
}
|
|
3542
5386
|
function readOptionalString(value) {
|
|
@@ -3726,7 +5570,7 @@ var UiChatRunCoordinator = class {
|
|
|
3726
5570
|
const parsedAgentId = parseAgentScopedSessionKey2(sessionKey)?.agentId;
|
|
3727
5571
|
const agentId = explicitAgentId ?? readOptionalString(parsedAgentId);
|
|
3728
5572
|
const model = readOptionalString(input.model);
|
|
3729
|
-
const metadata =
|
|
5573
|
+
const metadata = isRecord6(input.metadata) ? { ...input.metadata } : {};
|
|
3730
5574
|
if (model) {
|
|
3731
5575
|
metadata.model = model;
|
|
3732
5576
|
}
|
|
@@ -3947,14 +5791,14 @@ var UiChatRunCoordinator = class {
|
|
|
3947
5791
|
this.emitRunUpdated(run);
|
|
3948
5792
|
}
|
|
3949
5793
|
waitForRunUpdate(run, signal) {
|
|
3950
|
-
return new Promise((
|
|
5794
|
+
return new Promise((resolve13) => {
|
|
3951
5795
|
const wake = () => {
|
|
3952
5796
|
cleanup();
|
|
3953
|
-
|
|
5797
|
+
resolve13();
|
|
3954
5798
|
};
|
|
3955
5799
|
const onAbort = () => {
|
|
3956
5800
|
cleanup();
|
|
3957
|
-
|
|
5801
|
+
resolve13();
|
|
3958
5802
|
};
|
|
3959
5803
|
const cleanup = () => {
|
|
3960
5804
|
run.waiters.delete(wake);
|
|
@@ -4075,16 +5919,16 @@ var UiChatRunCoordinator = class {
|
|
|
4075
5919
|
`);
|
|
4076
5920
|
}
|
|
4077
5921
|
loadPersistedRuns() {
|
|
4078
|
-
if (!
|
|
5922
|
+
if (!existsSync8(RUNS_DIR)) {
|
|
4079
5923
|
return;
|
|
4080
5924
|
}
|
|
4081
5925
|
for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
|
|
4082
5926
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
4083
5927
|
continue;
|
|
4084
5928
|
}
|
|
4085
|
-
const
|
|
5929
|
+
const path2 = join4(RUNS_DIR, entry.name);
|
|
4086
5930
|
try {
|
|
4087
|
-
const parsed = JSON.parse(
|
|
5931
|
+
const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
4088
5932
|
const runId = readOptionalString(parsed.runId);
|
|
4089
5933
|
const sessionKey = readOptionalString(parsed.sessionKey);
|
|
4090
5934
|
if (!runId || !sessionKey) {
|
|
@@ -4147,10 +5991,10 @@ var {
|
|
|
4147
5991
|
getDataDir: getDataDir6,
|
|
4148
5992
|
getProvider,
|
|
4149
5993
|
getProviderName,
|
|
4150
|
-
getWorkspacePath:
|
|
5994
|
+
getWorkspacePath: getWorkspacePath8,
|
|
4151
5995
|
HeartbeatService,
|
|
4152
5996
|
LiteLLMProvider,
|
|
4153
|
-
loadConfig:
|
|
5997
|
+
loadConfig: loadConfig7,
|
|
4154
5998
|
MessageBus,
|
|
4155
5999
|
ProviderManager,
|
|
4156
6000
|
resolveConfigSecrets: resolveConfigSecrets2,
|
|
@@ -4165,64 +6009,18 @@ function createSkillsLoader(workspace) {
|
|
|
4165
6009
|
}
|
|
4166
6010
|
return new ctor(workspace);
|
|
4167
6011
|
}
|
|
4168
|
-
function containsAbsoluteFsPath(line) {
|
|
4169
|
-
const normalized = line.trim();
|
|
4170
|
-
if (!normalized) {
|
|
4171
|
-
return false;
|
|
4172
|
-
}
|
|
4173
|
-
const lowered = normalized.toLowerCase();
|
|
4174
|
-
if (lowered.includes("http://") || lowered.includes("https://")) {
|
|
4175
|
-
return false;
|
|
4176
|
-
}
|
|
4177
|
-
if (/^[A-Za-z]:\\/.test(normalized)) {
|
|
4178
|
-
return true;
|
|
4179
|
-
}
|
|
4180
|
-
return /(?:^|\s)(?:~\/|\/[^\s]+)/.test(normalized);
|
|
4181
|
-
}
|
|
4182
|
-
function pickUserFacingCommandSummary(output, fallback) {
|
|
4183
|
-
const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
4184
|
-
if (lines.length === 0) {
|
|
4185
|
-
return fallback;
|
|
4186
|
-
}
|
|
4187
|
-
const visibleLines = lines.filter((line) => {
|
|
4188
|
-
if (/^(path|install path|source path|destination|location)\s*:/i.test(line)) {
|
|
4189
|
-
return false;
|
|
4190
|
-
}
|
|
4191
|
-
if (containsAbsoluteFsPath(line)) {
|
|
4192
|
-
return false;
|
|
4193
|
-
}
|
|
4194
|
-
return true;
|
|
4195
|
-
});
|
|
4196
|
-
if (visibleLines.length === 0) {
|
|
4197
|
-
return fallback;
|
|
4198
|
-
}
|
|
4199
|
-
const preferred = [...visibleLines].reverse().find(
|
|
4200
|
-
(line) => /\b(installed|enabled|disabled|uninstalled|published|updated|already installed|removed)\b/i.test(line)
|
|
4201
|
-
);
|
|
4202
|
-
return preferred ?? visibleLines[visibleLines.length - 1] ?? fallback;
|
|
4203
|
-
}
|
|
4204
|
-
function buildMarketplaceSkillInstallArgs(params) {
|
|
4205
|
-
const args = ["skills", "install", params.slug, "--workdir", params.workspace];
|
|
4206
|
-
if (params.force) {
|
|
4207
|
-
args.push("--force");
|
|
4208
|
-
}
|
|
4209
|
-
return args;
|
|
4210
|
-
}
|
|
4211
|
-
function resolveCliSubcommandEntry(params) {
|
|
4212
|
-
const argvEntry = params.argvEntry?.trim();
|
|
4213
|
-
if (argvEntry) {
|
|
4214
|
-
return resolve7(argvEntry);
|
|
4215
|
-
}
|
|
4216
|
-
return fileURLToPath2(new URL("../index.js", params.importMetaUrl));
|
|
4217
|
-
}
|
|
4218
6012
|
var ServiceCommands = class {
|
|
4219
6013
|
constructor(deps) {
|
|
4220
6014
|
this.deps = deps;
|
|
4221
6015
|
}
|
|
6016
|
+
applyLiveConfigReload = null;
|
|
6017
|
+
liveUiNcpAgent = null;
|
|
4222
6018
|
async startGateway(options = {}) {
|
|
6019
|
+
this.applyLiveConfigReload = null;
|
|
6020
|
+
this.liveUiNcpAgent = null;
|
|
4223
6021
|
const runtimeConfigPath = getConfigPath3();
|
|
4224
|
-
const config2 = resolveConfigSecrets2(
|
|
4225
|
-
const workspace =
|
|
6022
|
+
const config2 = resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath });
|
|
6023
|
+
const workspace = getWorkspacePath8(config2.agents.defaults.workspace);
|
|
4226
6024
|
let pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
4227
6025
|
let extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
4228
6026
|
logPluginDiagnostics(pluginRegistry);
|
|
@@ -4266,7 +6064,7 @@ var ServiceCommands = class {
|
|
|
4266
6064
|
sessionManager,
|
|
4267
6065
|
providerManager,
|
|
4268
6066
|
makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
|
|
4269
|
-
loadConfig: () => resolveConfigSecrets2(
|
|
6067
|
+
loadConfig: () => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
|
|
4270
6068
|
getExtensionChannels: () => extensionRegistry.channels,
|
|
4271
6069
|
onRestartRequired: (paths) => {
|
|
4272
6070
|
void this.deps.requestRestart({
|
|
@@ -4276,6 +6074,9 @@ var ServiceCommands = class {
|
|
|
4276
6074
|
});
|
|
4277
6075
|
}
|
|
4278
6076
|
});
|
|
6077
|
+
this.applyLiveConfigReload = async () => {
|
|
6078
|
+
await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }));
|
|
6079
|
+
};
|
|
4279
6080
|
const gatewayController = new GatewayControllerImpl({
|
|
4280
6081
|
reloader,
|
|
4281
6082
|
cron: cron2,
|
|
@@ -4307,38 +6108,42 @@ var ServiceCommands = class {
|
|
|
4307
6108
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
4308
6109
|
registry: pluginRegistry,
|
|
4309
6110
|
channel,
|
|
4310
|
-
cfg: resolveConfigSecrets2(
|
|
6111
|
+
cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
|
|
4311
6112
|
accountId
|
|
4312
6113
|
})
|
|
4313
6114
|
});
|
|
4314
6115
|
reloader.setApplyAgentRuntimeConfig((nextConfig) => runtimePool.applyRuntimeConfig(nextConfig));
|
|
4315
|
-
reloader.setReloadPlugins(async (nextConfig) => {
|
|
4316
|
-
const
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
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
|
|
4324
6126
|
});
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
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);
|
|
4331
6133
|
runtimePool.applyRuntimeConfig(nextConfig);
|
|
4332
|
-
|
|
6134
|
+
if (result.restartChannels) {
|
|
6135
|
+
console.log("Config reload: plugin channel gateways restarted.");
|
|
6136
|
+
}
|
|
6137
|
+
return { restartChannels: result.restartChannels };
|
|
4333
6138
|
});
|
|
4334
|
-
let pluginChannelBindings =
|
|
6139
|
+
let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
|
|
4335
6140
|
setPluginRuntimeBridge({
|
|
4336
|
-
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(
|
|
6141
|
+
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }), pluginChannelBindings),
|
|
4337
6142
|
writeConfigFile: async (nextConfigView) => {
|
|
4338
6143
|
if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
|
|
4339
6144
|
throw new Error("plugin runtime writeConfigFile expects an object config");
|
|
4340
6145
|
}
|
|
4341
|
-
const current =
|
|
6146
|
+
const current = loadConfig7();
|
|
4342
6147
|
const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
|
|
4343
6148
|
saveConfig5(next);
|
|
4344
6149
|
},
|
|
@@ -4405,19 +6210,36 @@ var ServiceCommands = class {
|
|
|
4405
6210
|
} else {
|
|
4406
6211
|
console.log("Warning: No channels enabled");
|
|
4407
6212
|
}
|
|
4408
|
-
this.startUiIfEnabled(
|
|
6213
|
+
await this.startUiIfEnabled(
|
|
6214
|
+
uiConfig,
|
|
6215
|
+
uiStaticDir,
|
|
6216
|
+
cron2,
|
|
6217
|
+
runtimePool,
|
|
6218
|
+
sessionManager,
|
|
6219
|
+
providerManager,
|
|
6220
|
+
bus,
|
|
6221
|
+
gatewayController,
|
|
6222
|
+
() => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
|
|
6223
|
+
() => extensionRegistry,
|
|
6224
|
+
({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
6225
|
+
registry: pluginRegistry,
|
|
6226
|
+
channel,
|
|
6227
|
+
cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
|
|
6228
|
+
accountId
|
|
6229
|
+
})
|
|
6230
|
+
);
|
|
4409
6231
|
const cronStatus = cron2.status();
|
|
4410
6232
|
if (cronStatus.jobs > 0) {
|
|
4411
6233
|
console.log(`\u2713 Cron: ${cronStatus.jobs} scheduled jobs`);
|
|
4412
6234
|
}
|
|
4413
6235
|
console.log("\u2713 Heartbeat: every 30m");
|
|
4414
|
-
const configPath =
|
|
6236
|
+
const configPath = resolve10(getConfigPath3());
|
|
4415
6237
|
const watcher = chokidar.watch(configPath, {
|
|
4416
6238
|
ignoreInitial: true,
|
|
4417
6239
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
4418
6240
|
});
|
|
4419
6241
|
watcher.on("all", (event, changedPath) => {
|
|
4420
|
-
if (
|
|
6242
|
+
if (resolve10(changedPath) !== configPath) {
|
|
4421
6243
|
return;
|
|
4422
6244
|
}
|
|
4423
6245
|
if (event === "add") {
|
|
@@ -4435,7 +6257,7 @@ var ServiceCommands = class {
|
|
|
4435
6257
|
await cron2.start();
|
|
4436
6258
|
await heartbeat.start();
|
|
4437
6259
|
try {
|
|
4438
|
-
const startedPluginGateways = await
|
|
6260
|
+
const startedPluginGateways = await startPluginChannelGateways2({
|
|
4439
6261
|
registry: pluginRegistry,
|
|
4440
6262
|
logger: pluginGatewayLogger
|
|
4441
6263
|
});
|
|
@@ -4445,7 +6267,9 @@ var ServiceCommands = class {
|
|
|
4445
6267
|
await this.wakeFromRestartSentinel({ bus, sessionManager });
|
|
4446
6268
|
await runtimePool.run();
|
|
4447
6269
|
} finally {
|
|
4448
|
-
|
|
6270
|
+
this.applyLiveConfigReload = null;
|
|
6271
|
+
this.liveUiNcpAgent = null;
|
|
6272
|
+
await stopPluginChannelGateways2(pluginGatewayHandles);
|
|
4449
6273
|
setPluginRuntimeBridge(null);
|
|
4450
6274
|
}
|
|
4451
6275
|
}
|
|
@@ -4509,7 +6333,7 @@ var ServiceCommands = class {
|
|
|
4509
6333
|
if (!sentinel) {
|
|
4510
6334
|
return;
|
|
4511
6335
|
}
|
|
4512
|
-
await new Promise((
|
|
6336
|
+
await new Promise((resolve13) => setTimeout(resolve13, 750));
|
|
4513
6337
|
const payload = sentinel.payload;
|
|
4514
6338
|
const summary = formatRestartSentinelMessage(payload);
|
|
4515
6339
|
const sentinelSessionKey = this.normalizeOptionalString(payload.sessionKey);
|
|
@@ -4555,7 +6379,7 @@ var ServiceCommands = class {
|
|
|
4555
6379
|
});
|
|
4556
6380
|
}
|
|
4557
6381
|
async runForeground(options) {
|
|
4558
|
-
const config2 =
|
|
6382
|
+
const config2 = loadConfig7();
|
|
4559
6383
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
4560
6384
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
4561
6385
|
if (options.open) {
|
|
@@ -4568,7 +6392,7 @@ var ServiceCommands = class {
|
|
|
4568
6392
|
});
|
|
4569
6393
|
}
|
|
4570
6394
|
async startService(options) {
|
|
4571
|
-
const config2 =
|
|
6395
|
+
const config2 = loadConfig7();
|
|
4572
6396
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
4573
6397
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
4574
6398
|
const apiUrl = `${uiUrl}/api`;
|
|
@@ -4616,8 +6440,19 @@ var ServiceCommands = class {
|
|
|
4616
6440
|
if (!staticDir) {
|
|
4617
6441
|
console.log("Warning: UI frontend not found in package assets.");
|
|
4618
6442
|
}
|
|
6443
|
+
const healthUrl = `${apiUrl}/health`;
|
|
6444
|
+
const portPreflight = await this.checkUiPortPreflight({
|
|
6445
|
+
host: uiConfig.host,
|
|
6446
|
+
port: uiConfig.port,
|
|
6447
|
+
healthUrl
|
|
6448
|
+
});
|
|
6449
|
+
if (!portPreflight.ok) {
|
|
6450
|
+
console.error(`Error: Cannot start ${APP_NAME2} because UI port ${uiConfig.port} is already occupied.`);
|
|
6451
|
+
console.error(portPreflight.message);
|
|
6452
|
+
return;
|
|
6453
|
+
}
|
|
4619
6454
|
const logPath = resolveServiceLogPath();
|
|
4620
|
-
const logDir =
|
|
6455
|
+
const logDir = resolve10(logPath, "..");
|
|
4621
6456
|
mkdirSync5(logDir, { recursive: true });
|
|
4622
6457
|
const logFd = openSync(logPath, "a");
|
|
4623
6458
|
const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
|
|
@@ -4645,13 +6480,12 @@ var ServiceCommands = class {
|
|
|
4645
6480
|
this.printStartupFailureDiagnostics({
|
|
4646
6481
|
uiUrl,
|
|
4647
6482
|
apiUrl,
|
|
4648
|
-
healthUrl
|
|
6483
|
+
healthUrl,
|
|
4649
6484
|
logPath,
|
|
4650
6485
|
lastProbeError: null
|
|
4651
6486
|
});
|
|
4652
6487
|
return;
|
|
4653
6488
|
}
|
|
4654
|
-
const healthUrl = `${apiUrl}/health`;
|
|
4655
6489
|
this.appendStartupStage(logPath, `health probe started: ${healthUrl} (phase=quick, timeoutMs=${quickPhaseTimeoutMs})`);
|
|
4656
6490
|
let readiness = await this.waitForBackgroundServiceReady({
|
|
4657
6491
|
pid: child.pid,
|
|
@@ -4766,14 +6600,14 @@ var ServiceCommands = class {
|
|
|
4766
6600
|
const probe = await this.probeHealthEndpoint(params.healthUrl);
|
|
4767
6601
|
if (!probe.healthy) {
|
|
4768
6602
|
lastProbeError = probe.error;
|
|
4769
|
-
await new Promise((
|
|
6603
|
+
await new Promise((resolve13) => setTimeout(resolve13, 200));
|
|
4770
6604
|
continue;
|
|
4771
6605
|
}
|
|
4772
|
-
await new Promise((
|
|
6606
|
+
await new Promise((resolve13) => setTimeout(resolve13, 300));
|
|
4773
6607
|
if (isProcessRunning(params.pid)) {
|
|
4774
6608
|
return { ready: true, lastProbeError: null };
|
|
4775
6609
|
}
|
|
4776
|
-
await new Promise((
|
|
6610
|
+
await new Promise((resolve13) => setTimeout(resolve13, 200));
|
|
4777
6611
|
}
|
|
4778
6612
|
return { ready: false, lastProbeError };
|
|
4779
6613
|
}
|
|
@@ -4810,6 +6644,58 @@ var ServiceCommands = class {
|
|
|
4810
6644
|
}
|
|
4811
6645
|
console.error(lines.join("\n"));
|
|
4812
6646
|
}
|
|
6647
|
+
async checkUiPortPreflight(params) {
|
|
6648
|
+
const availability = await this.checkPortAvailability({
|
|
6649
|
+
host: params.host,
|
|
6650
|
+
port: params.port
|
|
6651
|
+
});
|
|
6652
|
+
if (availability.available) {
|
|
6653
|
+
return { ok: true };
|
|
6654
|
+
}
|
|
6655
|
+
const probe = await this.probeHealthEndpoint(params.healthUrl);
|
|
6656
|
+
const lines = [
|
|
6657
|
+
`Port probe: ${availability.detail}`
|
|
6658
|
+
];
|
|
6659
|
+
if (probe.healthy) {
|
|
6660
|
+
lines.push(
|
|
6661
|
+
`Health probe: ${params.healthUrl} is already healthy. Another process is already serving this UI/API port.`
|
|
6662
|
+
);
|
|
6663
|
+
} else if (probe.error) {
|
|
6664
|
+
lines.push(`Health probe: ${probe.error}`);
|
|
6665
|
+
lines.push(
|
|
6666
|
+
"The port is occupied by a process that does not answer as a healthy NextClaw HTTP server."
|
|
6667
|
+
);
|
|
6668
|
+
}
|
|
6669
|
+
lines.push(
|
|
6670
|
+
`Fix: free port ${params.port} or start NextClaw with another port via --ui-port <port>.`
|
|
6671
|
+
);
|
|
6672
|
+
lines.push(
|
|
6673
|
+
`Inspect locally with: ss -ltnp | grep ${params.port} || lsof -iTCP:${params.port} -sTCP:LISTEN -n -P`
|
|
6674
|
+
);
|
|
6675
|
+
return {
|
|
6676
|
+
ok: false,
|
|
6677
|
+
message: lines.join("\n")
|
|
6678
|
+
};
|
|
6679
|
+
}
|
|
6680
|
+
async checkPortAvailability(params) {
|
|
6681
|
+
return await new Promise((resolve13) => {
|
|
6682
|
+
const server = createNetServer2();
|
|
6683
|
+
server.once("error", (error) => {
|
|
6684
|
+
resolve13({
|
|
6685
|
+
available: false,
|
|
6686
|
+
detail: `bind failed on ${params.host}:${params.port} (${String(error)})`
|
|
6687
|
+
});
|
|
6688
|
+
});
|
|
6689
|
+
server.listen(params.port, params.host, () => {
|
|
6690
|
+
server.close(() => {
|
|
6691
|
+
resolve13({
|
|
6692
|
+
available: true,
|
|
6693
|
+
detail: `bind ok on ${params.host}:${params.port}`
|
|
6694
|
+
});
|
|
6695
|
+
});
|
|
6696
|
+
});
|
|
6697
|
+
});
|
|
6698
|
+
}
|
|
4813
6699
|
getHeaderValue(headers, key) {
|
|
4814
6700
|
const value = headers[key];
|
|
4815
6701
|
if (typeof value === "string") {
|
|
@@ -4838,7 +6724,7 @@ var ServiceCommands = class {
|
|
|
4838
6724
|
return { healthy: false, error: "invalid health URL" };
|
|
4839
6725
|
}
|
|
4840
6726
|
const requestImpl = parsed.protocol === "https:" ? httpsRequest : httpRequest;
|
|
4841
|
-
return new Promise((
|
|
6727
|
+
return new Promise((resolve13) => {
|
|
4842
6728
|
const req = requestImpl(
|
|
4843
6729
|
{
|
|
4844
6730
|
protocol: parsed.protocol,
|
|
@@ -4874,19 +6760,19 @@ var ServiceCommands = class {
|
|
|
4874
6760
|
if (bodySnippet) {
|
|
4875
6761
|
details.push(`body=${bodySnippet}`);
|
|
4876
6762
|
}
|
|
4877
|
-
|
|
6763
|
+
resolve13({ healthy: false, error: details.join("; ") });
|
|
4878
6764
|
return;
|
|
4879
6765
|
}
|
|
4880
6766
|
try {
|
|
4881
6767
|
const payload = JSON.parse(responseText);
|
|
4882
6768
|
const healthy = payload?.ok === true && payload?.data?.status === "ok";
|
|
4883
6769
|
if (!healthy) {
|
|
4884
|
-
|
|
6770
|
+
resolve13({ healthy: false, error: "health payload not ok" });
|
|
4885
6771
|
return;
|
|
4886
6772
|
}
|
|
4887
|
-
|
|
6773
|
+
resolve13({ healthy: true, error: null });
|
|
4888
6774
|
} catch {
|
|
4889
|
-
|
|
6775
|
+
resolve13({ healthy: false, error: "invalid health JSON response" });
|
|
4890
6776
|
}
|
|
4891
6777
|
});
|
|
4892
6778
|
}
|
|
@@ -4895,7 +6781,7 @@ var ServiceCommands = class {
|
|
|
4895
6781
|
req.destroy(new Error("probe timeout"));
|
|
4896
6782
|
});
|
|
4897
6783
|
req.on("error", (error) => {
|
|
4898
|
-
|
|
6784
|
+
resolve13({ healthy: false, error: error.message || String(error) });
|
|
4899
6785
|
});
|
|
4900
6786
|
req.end();
|
|
4901
6787
|
});
|
|
@@ -4945,13 +6831,22 @@ var ServiceCommands = class {
|
|
|
4945
6831
|
const publicBase = `http://${publicIp}:${port}`;
|
|
4946
6832
|
console.log(`Public UI (if firewall/NAT allows): ${publicBase}`);
|
|
4947
6833
|
console.log(`Public API (if firewall/NAT allows): ${publicBase}/api`);
|
|
6834
|
+
console.log(
|
|
6835
|
+
`Public deploy note: NextClaw serves plain HTTP on ${port}.`
|
|
6836
|
+
);
|
|
6837
|
+
console.log(
|
|
6838
|
+
`For https:// or standard 80/443 access, terminate TLS in Nginx/Caddy and proxy to http://127.0.0.1:${port}.`
|
|
6839
|
+
);
|
|
6840
|
+
console.log(
|
|
6841
|
+
`If a reverse proxy returns 502, verify its upstream is http://127.0.0.1:${port} (not https://, not a stale port, and not a stopped process).`
|
|
6842
|
+
);
|
|
4948
6843
|
}
|
|
4949
6844
|
printServiceControlHints() {
|
|
4950
6845
|
console.log("Service controls:");
|
|
4951
6846
|
console.log(` - Check status: ${APP_NAME2} status`);
|
|
4952
6847
|
console.log(` - If you need to stop the service, run: ${APP_NAME2} stop`);
|
|
4953
6848
|
}
|
|
4954
|
-
startUiIfEnabled(uiConfig, uiStaticDir, cronService, runtimePool, sessionManager) {
|
|
6849
|
+
async startUiIfEnabled(uiConfig, uiStaticDir, cronService, runtimePool, sessionManager, providerManager, bus, gatewayController, getConfig, getExtensionRegistry, resolveMessageToolHints) {
|
|
4955
6850
|
if (!uiConfig.enabled) {
|
|
4956
6851
|
return;
|
|
4957
6852
|
}
|
|
@@ -4991,13 +6886,7 @@ var ServiceCommands = class {
|
|
|
4991
6886
|
if (sessionType === "native") {
|
|
4992
6887
|
return "Native";
|
|
4993
6888
|
}
|
|
4994
|
-
|
|
4995
|
-
return "Codex";
|
|
4996
|
-
}
|
|
4997
|
-
if (sessionType === "claude-agent-sdk") {
|
|
4998
|
-
return "Claude Code";
|
|
4999
|
-
}
|
|
5000
|
-
return sessionType;
|
|
6889
|
+
return sessionType.trim().split(/[-_]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || sessionType;
|
|
5001
6890
|
};
|
|
5002
6891
|
let publishUiEvent = null;
|
|
5003
6892
|
runtimePool.setSystemSessionUpdatedHandler(({ sessionKey, message }) => {
|
|
@@ -5022,6 +6911,20 @@ var ServiceCommands = class {
|
|
|
5022
6911
|
publishUiEvent?.({ type: "run.updated", payload: { run } });
|
|
5023
6912
|
}
|
|
5024
6913
|
});
|
|
6914
|
+
const ncpAgent = await createUiNcpAgent({
|
|
6915
|
+
bus,
|
|
6916
|
+
providerManager,
|
|
6917
|
+
sessionManager,
|
|
6918
|
+
cronService,
|
|
6919
|
+
gatewayController,
|
|
6920
|
+
getConfig,
|
|
6921
|
+
getExtensionRegistry,
|
|
6922
|
+
resolveMessageToolHints: ({ channel, accountId }) => resolveMessageToolHints({
|
|
6923
|
+
channel,
|
|
6924
|
+
accountId
|
|
6925
|
+
})
|
|
6926
|
+
});
|
|
6927
|
+
this.liveUiNcpAgent = ncpAgent;
|
|
5025
6928
|
const uiServer = startUiServer({
|
|
5026
6929
|
host: uiConfig.host,
|
|
5027
6930
|
port: uiConfig.port,
|
|
@@ -5040,6 +6943,7 @@ var ServiceCommands = class {
|
|
|
5040
6943
|
uninstallSkill: (slug) => this.uninstallMarketplaceSkill(slug)
|
|
5041
6944
|
}
|
|
5042
6945
|
},
|
|
6946
|
+
ncpAgent,
|
|
5043
6947
|
chatRuntime: {
|
|
5044
6948
|
listSessionTypes: async () => {
|
|
5045
6949
|
const options = runtimePool.listAvailableEngineKinds().map((value) => ({
|
|
@@ -5119,9 +7023,9 @@ var ServiceCommands = class {
|
|
|
5119
7023
|
}
|
|
5120
7024
|
}
|
|
5121
7025
|
async installMarketplacePlugin(spec) {
|
|
5122
|
-
const
|
|
5123
|
-
|
|
5124
|
-
return { message:
|
|
7026
|
+
const result = await installPluginMutation(spec);
|
|
7027
|
+
await this.applyLiveConfigReload?.();
|
|
7028
|
+
return { message: result.message };
|
|
5125
7029
|
}
|
|
5126
7030
|
async installMarketplaceSkill(params) {
|
|
5127
7031
|
if (params.kind === "builtin") {
|
|
@@ -5134,7 +7038,7 @@ var ServiceCommands = class {
|
|
|
5134
7038
|
if (params.kind && params.kind !== "marketplace") {
|
|
5135
7039
|
throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
|
|
5136
7040
|
}
|
|
5137
|
-
const workspace =
|
|
7041
|
+
const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
|
|
5138
7042
|
const args = buildMarketplaceSkillInstallArgs({
|
|
5139
7043
|
slug: params.slug,
|
|
5140
7044
|
workspace,
|
|
@@ -5153,24 +7057,26 @@ var ServiceCommands = class {
|
|
|
5153
7057
|
}
|
|
5154
7058
|
}
|
|
5155
7059
|
async enableMarketplacePlugin(id) {
|
|
5156
|
-
const
|
|
5157
|
-
|
|
5158
|
-
return { message:
|
|
7060
|
+
const result = await enablePluginMutation(id);
|
|
7061
|
+
await this.applyLiveConfigReload?.();
|
|
7062
|
+
return { message: result.message };
|
|
5159
7063
|
}
|
|
5160
7064
|
async disableMarketplacePlugin(id) {
|
|
5161
|
-
const
|
|
5162
|
-
|
|
5163
|
-
return { message:
|
|
7065
|
+
const result = await disablePluginMutation(id);
|
|
7066
|
+
await this.applyLiveConfigReload?.();
|
|
7067
|
+
return { message: result.message };
|
|
5164
7068
|
}
|
|
5165
7069
|
async uninstallMarketplacePlugin(id) {
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
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 };
|
|
5169
7075
|
}
|
|
5170
7076
|
async uninstallMarketplaceSkill(slug) {
|
|
5171
|
-
const workspace =
|
|
7077
|
+
const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
|
|
5172
7078
|
const targetDir = join5(workspace, "skills", slug);
|
|
5173
|
-
if (!
|
|
7079
|
+
if (!existsSync9(targetDir)) {
|
|
5174
7080
|
throw new Error(`Skill not installed in workspace: ${slug}`);
|
|
5175
7081
|
}
|
|
5176
7082
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
@@ -5179,10 +7085,10 @@ var ServiceCommands = class {
|
|
|
5179
7085
|
};
|
|
5180
7086
|
}
|
|
5181
7087
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
5182
|
-
const workspace =
|
|
7088
|
+
const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
|
|
5183
7089
|
const destination = join5(workspace, "skills", slug);
|
|
5184
7090
|
const destinationSkillFile = join5(destination, "SKILL.md");
|
|
5185
|
-
if (
|
|
7091
|
+
if (existsSync9(destinationSkillFile) && !force) {
|
|
5186
7092
|
return {
|
|
5187
7093
|
message: `${slug} is already installed`
|
|
5188
7094
|
};
|
|
@@ -5190,7 +7096,7 @@ var ServiceCommands = class {
|
|
|
5190
7096
|
const loader = createSkillsLoader(workspace);
|
|
5191
7097
|
const builtin = (loader?.listSkills(false) ?? []).find((skill) => skill.name === slug && skill.source === "builtin");
|
|
5192
7098
|
if (!builtin) {
|
|
5193
|
-
if (
|
|
7099
|
+
if (existsSync9(destinationSkillFile)) {
|
|
5194
7100
|
return {
|
|
5195
7101
|
message: `${slug} is already installed`
|
|
5196
7102
|
};
|
|
@@ -5257,9 +7163,9 @@ ${stderr}`.trim();
|
|
|
5257
7163
|
};
|
|
5258
7164
|
|
|
5259
7165
|
// src/cli/workspace.ts
|
|
5260
|
-
import { cpSync as cpSync3, existsSync as
|
|
5261
|
-
import { createRequire } from "module";
|
|
5262
|
-
import { dirname as dirname3, join as join6, resolve as
|
|
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";
|
|
7167
|
+
import { createRequire as createRequire2 } from "module";
|
|
7168
|
+
import { dirname as dirname3, join as join6, resolve as resolve11 } from "path";
|
|
5263
7169
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5264
7170
|
import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
|
|
5265
7171
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -5290,27 +7196,27 @@ var WorkspaceManager = class {
|
|
|
5290
7196
|
];
|
|
5291
7197
|
for (const entry of templateFiles) {
|
|
5292
7198
|
const filePath = join6(workspace, entry.target);
|
|
5293
|
-
if (!force &&
|
|
7199
|
+
if (!force && existsSync10(filePath)) {
|
|
5294
7200
|
continue;
|
|
5295
7201
|
}
|
|
5296
7202
|
const templatePath = join6(templateDir, entry.source);
|
|
5297
|
-
if (!
|
|
7203
|
+
if (!existsSync10(templatePath)) {
|
|
5298
7204
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
5299
7205
|
continue;
|
|
5300
7206
|
}
|
|
5301
|
-
const raw =
|
|
7207
|
+
const raw = readFileSync9(templatePath, "utf-8");
|
|
5302
7208
|
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
|
|
5303
7209
|
mkdirSync6(dirname3(filePath), { recursive: true });
|
|
5304
7210
|
writeFileSync5(filePath, content);
|
|
5305
7211
|
created.push(entry.target);
|
|
5306
7212
|
}
|
|
5307
7213
|
const memoryDir = join6(workspace, "memory");
|
|
5308
|
-
if (!
|
|
7214
|
+
if (!existsSync10(memoryDir)) {
|
|
5309
7215
|
mkdirSync6(memoryDir, { recursive: true });
|
|
5310
7216
|
created.push(join6("memory", ""));
|
|
5311
7217
|
}
|
|
5312
7218
|
const skillsDir = join6(workspace, "skills");
|
|
5313
|
-
if (!
|
|
7219
|
+
if (!existsSync10(skillsDir)) {
|
|
5314
7220
|
mkdirSync6(skillsDir, { recursive: true });
|
|
5315
7221
|
created.push(join6("skills", ""));
|
|
5316
7222
|
}
|
|
@@ -5332,11 +7238,11 @@ var WorkspaceManager = class {
|
|
|
5332
7238
|
continue;
|
|
5333
7239
|
}
|
|
5334
7240
|
const src = join6(sourceDir, entry.name);
|
|
5335
|
-
if (!
|
|
7241
|
+
if (!existsSync10(join6(src, "SKILL.md"))) {
|
|
5336
7242
|
continue;
|
|
5337
7243
|
}
|
|
5338
7244
|
const dest = join6(targetDir, entry.name);
|
|
5339
|
-
if (!force &&
|
|
7245
|
+
if (!force && existsSync10(dest)) {
|
|
5340
7246
|
continue;
|
|
5341
7247
|
}
|
|
5342
7248
|
try {
|
|
@@ -5351,15 +7257,15 @@ var WorkspaceManager = class {
|
|
|
5351
7257
|
}
|
|
5352
7258
|
resolveBuiltinSkillsDir() {
|
|
5353
7259
|
try {
|
|
5354
|
-
const
|
|
5355
|
-
const entry =
|
|
5356
|
-
const pkgRoot =
|
|
7260
|
+
const require3 = createRequire2(import.meta.url);
|
|
7261
|
+
const entry = require3.resolve("@nextclaw/core");
|
|
7262
|
+
const pkgRoot = resolve11(dirname3(entry), "..");
|
|
5357
7263
|
const distSkills = join6(pkgRoot, "dist", "skills");
|
|
5358
|
-
if (
|
|
7264
|
+
if (existsSync10(distSkills)) {
|
|
5359
7265
|
return distSkills;
|
|
5360
7266
|
}
|
|
5361
7267
|
const srcSkills = join6(pkgRoot, "src", "agent", "skills");
|
|
5362
|
-
if (
|
|
7268
|
+
if (existsSync10(srcSkills)) {
|
|
5363
7269
|
return srcSkills;
|
|
5364
7270
|
}
|
|
5365
7271
|
return null;
|
|
@@ -5372,11 +7278,11 @@ var WorkspaceManager = class {
|
|
|
5372
7278
|
if (override) {
|
|
5373
7279
|
return override;
|
|
5374
7280
|
}
|
|
5375
|
-
const cliDir =
|
|
5376
|
-
const pkgRoot =
|
|
7281
|
+
const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
|
|
7282
|
+
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
5377
7283
|
const candidates = [join6(pkgRoot, "templates")];
|
|
5378
7284
|
for (const candidate of candidates) {
|
|
5379
|
-
if (
|
|
7285
|
+
if (existsSync10(candidate)) {
|
|
5380
7286
|
return candidate;
|
|
5381
7287
|
}
|
|
5382
7288
|
}
|
|
@@ -5384,21 +7290,21 @@ var WorkspaceManager = class {
|
|
|
5384
7290
|
}
|
|
5385
7291
|
getBridgeDir() {
|
|
5386
7292
|
const userBridge = join6(getDataDir7(), "bridge");
|
|
5387
|
-
if (
|
|
7293
|
+
if (existsSync10(join6(userBridge, "dist", "index.js"))) {
|
|
5388
7294
|
return userBridge;
|
|
5389
7295
|
}
|
|
5390
7296
|
if (!which("npm")) {
|
|
5391
7297
|
console.error("npm not found. Please install Node.js >= 18.");
|
|
5392
7298
|
process.exit(1);
|
|
5393
7299
|
}
|
|
5394
|
-
const cliDir =
|
|
5395
|
-
const pkgRoot =
|
|
7300
|
+
const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
|
|
7301
|
+
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
5396
7302
|
const pkgBridge = join6(pkgRoot, "bridge");
|
|
5397
7303
|
const srcBridge = join6(pkgRoot, "..", "..", "bridge");
|
|
5398
7304
|
let source = null;
|
|
5399
|
-
if (
|
|
7305
|
+
if (existsSync10(join6(pkgBridge, "package.json"))) {
|
|
5400
7306
|
source = pkgBridge;
|
|
5401
|
-
} else if (
|
|
7307
|
+
} else if (existsSync10(join6(srcBridge, "package.json"))) {
|
|
5402
7308
|
source = srcBridge;
|
|
5403
7309
|
}
|
|
5404
7310
|
if (!source) {
|
|
@@ -5406,8 +7312,8 @@ var WorkspaceManager = class {
|
|
|
5406
7312
|
process.exit(1);
|
|
5407
7313
|
}
|
|
5408
7314
|
console.log(`${this.logo} Setting up bridge...`);
|
|
5409
|
-
mkdirSync6(
|
|
5410
|
-
if (
|
|
7315
|
+
mkdirSync6(resolve11(userBridge, ".."), { recursive: true });
|
|
7316
|
+
if (existsSync10(userBridge)) {
|
|
5411
7317
|
rmSync5(userBridge, { recursive: true, force: true });
|
|
5412
7318
|
}
|
|
5413
7319
|
cpSync3(source, userBridge, {
|
|
@@ -5443,7 +7349,7 @@ function resolveSkillsInstallWorkdir(params) {
|
|
|
5443
7349
|
if (params.explicitWorkdir) {
|
|
5444
7350
|
return expandHome2(params.explicitWorkdir);
|
|
5445
7351
|
}
|
|
5446
|
-
return
|
|
7352
|
+
return getWorkspacePath9(params.configuredWorkspace);
|
|
5447
7353
|
}
|
|
5448
7354
|
var CliRuntime = class {
|
|
5449
7355
|
logo;
|
|
@@ -5542,7 +7448,7 @@ var CliRuntime = class {
|
|
|
5542
7448
|
const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
|
|
5543
7449
|
const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath4(new URL("./index.js", import.meta.url));
|
|
5544
7450
|
const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
|
|
5545
|
-
const serviceStatePath =
|
|
7451
|
+
const serviceStatePath = resolve12(getDataDir8(), "run", "service.json");
|
|
5546
7452
|
const helperScript = [
|
|
5547
7453
|
'const { spawnSync } = require("node:child_process");',
|
|
5548
7454
|
'const { readFileSync } = require("node:fs");',
|
|
@@ -5672,15 +7578,15 @@ var CliRuntime = class {
|
|
|
5672
7578
|
const force = Boolean(options.force);
|
|
5673
7579
|
const configPath = getConfigPath4();
|
|
5674
7580
|
let createdConfig = false;
|
|
5675
|
-
if (!
|
|
7581
|
+
if (!existsSync11(configPath)) {
|
|
5676
7582
|
const config3 = ConfigSchema2.parse({});
|
|
5677
7583
|
saveConfig6(config3);
|
|
5678
7584
|
createdConfig = true;
|
|
5679
7585
|
}
|
|
5680
|
-
const config2 =
|
|
7586
|
+
const config2 = loadConfig8();
|
|
5681
7587
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
5682
7588
|
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join7(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
5683
|
-
const workspaceExisted =
|
|
7589
|
+
const workspaceExisted = existsSync11(workspacePath);
|
|
5684
7590
|
mkdirSync7(workspacePath, { recursive: true });
|
|
5685
7591
|
const templateResult = this.workspaceManager.createWorkspaceTemplates(
|
|
5686
7592
|
workspacePath,
|
|
@@ -5713,7 +7619,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5713
7619
|
async login(opts = {}) {
|
|
5714
7620
|
await this.init({ source: "login", auto: true });
|
|
5715
7621
|
const configPath = getConfigPath4();
|
|
5716
|
-
const config2 =
|
|
7622
|
+
const config2 = loadConfig8(configPath);
|
|
5717
7623
|
const providers = config2.providers;
|
|
5718
7624
|
const nextclawProvider = providers.nextclaw ?? {
|
|
5719
7625
|
displayName: "",
|
|
@@ -5870,15 +7776,15 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5870
7776
|
}
|
|
5871
7777
|
async agent(opts) {
|
|
5872
7778
|
const configPath = getConfigPath4();
|
|
5873
|
-
const config2 = resolveConfigSecrets3(
|
|
5874
|
-
const workspace =
|
|
7779
|
+
const config2 = resolveConfigSecrets3(loadConfig8(), { configPath });
|
|
7780
|
+
const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
|
|
5875
7781
|
const pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
5876
7782
|
const extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
5877
7783
|
logPluginDiagnostics(pluginRegistry);
|
|
5878
|
-
const pluginChannelBindings =
|
|
7784
|
+
const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
|
|
5879
7785
|
setPluginRuntimeBridge2({
|
|
5880
7786
|
loadConfig: () => toPluginConfigView(
|
|
5881
|
-
resolveConfigSecrets3(
|
|
7787
|
+
resolveConfigSecrets3(loadConfig8(), { configPath }),
|
|
5882
7788
|
pluginChannelBindings
|
|
5883
7789
|
),
|
|
5884
7790
|
writeConfigFile: async (nextConfigView) => {
|
|
@@ -5887,7 +7793,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5887
7793
|
"plugin runtime writeConfigFile expects an object config"
|
|
5888
7794
|
);
|
|
5889
7795
|
}
|
|
5890
|
-
const current =
|
|
7796
|
+
const current = loadConfig8();
|
|
5891
7797
|
const next = mergePluginConfigView(
|
|
5892
7798
|
current,
|
|
5893
7799
|
nextConfigView,
|
|
@@ -5919,7 +7825,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5919
7825
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
|
|
5920
7826
|
registry: pluginRegistry,
|
|
5921
7827
|
channel,
|
|
5922
|
-
cfg: resolveConfigSecrets3(
|
|
7828
|
+
cfg: resolveConfigSecrets3(loadConfig8(), { configPath }),
|
|
5923
7829
|
accountId
|
|
5924
7830
|
})
|
|
5925
7831
|
});
|
|
@@ -5939,9 +7845,9 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5939
7845
|
`
|
|
5940
7846
|
);
|
|
5941
7847
|
const historyFile = join7(getDataDir8(), "history", "cli_history");
|
|
5942
|
-
const historyDir =
|
|
7848
|
+
const historyDir = resolve12(historyFile, "..");
|
|
5943
7849
|
mkdirSync7(historyDir, { recursive: true });
|
|
5944
|
-
const history =
|
|
7850
|
+
const history = existsSync11(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
5945
7851
|
const rl = createInterface2({
|
|
5946
7852
|
input: process.stdin,
|
|
5947
7853
|
output: process.stdout
|
|
@@ -6096,7 +8002,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
6096
8002
|
await this.diagnosticsCommands.doctor(opts);
|
|
6097
8003
|
}
|
|
6098
8004
|
async skillsInstall(options) {
|
|
6099
|
-
const config2 =
|
|
8005
|
+
const config2 = loadConfig8();
|
|
6100
8006
|
const workdir = resolveSkillsInstallWorkdir({
|
|
6101
8007
|
explicitWorkdir: options.workdir,
|
|
6102
8008
|
configuredWorkspace: config2.agents.defaults.workspace
|
|
@@ -6118,6 +8024,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
6118
8024
|
async skillsPublish(options) {
|
|
6119
8025
|
const result = await publishMarketplaceSkill({
|
|
6120
8026
|
skillDir: expandHome2(options.dir),
|
|
8027
|
+
metaFile: options.meta ? expandHome2(options.meta) : void 0,
|
|
6121
8028
|
slug: options.slug,
|
|
6122
8029
|
name: options.name,
|
|
6123
8030
|
summary: options.summary,
|
|
@@ -6131,12 +8038,13 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
6131
8038
|
apiBaseUrl: options.apiBaseUrl,
|
|
6132
8039
|
token: options.token
|
|
6133
8040
|
});
|
|
6134
|
-
console.log(result.created ? `\u2713 Published new skill: ${result.slug}` : `\u2713 Updated skill: ${result.slug}`
|
|
6135
|
-
|
|
8041
|
+
console.log(`${result.created ? `\u2713 Published new skill: ${result.slug}` : `\u2713 Updated skill: ${result.slug}`}
|
|
8042
|
+
Files: ${result.fileCount}`);
|
|
6136
8043
|
}
|
|
6137
8044
|
async skillsUpdate(options) {
|
|
6138
8045
|
const result = await publishMarketplaceSkill({
|
|
6139
8046
|
skillDir: expandHome2(options.dir),
|
|
8047
|
+
metaFile: options.meta ? expandHome2(options.meta) : void 0,
|
|
6140
8048
|
slug: options.slug,
|
|
6141
8049
|
name: options.name,
|
|
6142
8050
|
summary: options.summary,
|
|
@@ -6173,8 +8081,8 @@ program.command("update").description(`Update ${APP_NAME5}`).option("--timeout <
|
|
|
6173
8081
|
var skills = program.command("skills").description("Manage skills");
|
|
6174
8082
|
skills.command("install <slug>").description("Install a skill from NextClaw marketplace").option("--api-base <url>", "Marketplace API base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => runtime.skillsInstall({ slug, ...opts, apiBaseUrl: opts.apiBase }));
|
|
6175
8083
|
var withRepeatableTag = (value, previous = []) => [...previous, value];
|
|
6176
|
-
skills.command("publish <dir>").description("Upload or create a skill in marketplace").option("--slug <slug>", "Skill slug (default: directory name)").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--published-at <datetime>", "Published time (ISO datetime)").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token").action(async (dir, opts) => runtime.skillsPublish({ dir, ...opts, apiBaseUrl: opts.apiBase }));
|
|
6177
|
-
skills.command("update <dir>").description("Update an existing skill in marketplace").option("--slug <slug>", "Skill slug (default: directory name)").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token").action(async (dir, opts) => runtime.skillsUpdate({ dir, ...opts, apiBaseUrl: opts.apiBase }));
|
|
8084
|
+
skills.command("publish <dir>").description("Upload or create a skill in marketplace").option("--meta <path>", "Marketplace metadata file (default: <dir>/marketplace.json)").option("--slug <slug>", "Skill slug (default: directory name)").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--published-at <datetime>", "Published time (ISO datetime)").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token").action(async (dir, opts) => runtime.skillsPublish({ dir, ...opts, apiBaseUrl: opts.apiBase }));
|
|
8085
|
+
skills.command("update <dir>").description("Update an existing skill in marketplace").option("--meta <path>", "Marketplace metadata file (default: <dir>/marketplace.json)").option("--slug <slug>", "Skill slug (default: directory name)").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token").action(async (dir, opts) => runtime.skillsUpdate({ dir, ...opts, apiBaseUrl: opts.apiBase }));
|
|
6178
8086
|
var plugins = program.command("plugins").description("Manage OpenClaw-compatible plugins");
|
|
6179
8087
|
plugins.command("list").description("List discovered plugins").option("--json", "Print JSON").option("--enabled", "Only show enabled plugins", false).option("--verbose", "Show detailed entries", false).action((opts) => runtime.pluginsList(opts));
|
|
6180
8088
|
plugins.command("info <id>").description("Show plugin details").option("--json", "Print JSON").action((id, opts) => runtime.pluginsInfo(id, opts));
|
|
@@ -6184,9 +8092,9 @@ plugins.command("uninstall <id>").description("Uninstall a plugin").option("--ke
|
|
|
6184
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));
|
|
6185
8093
|
plugins.command("doctor").description("Report plugin load issues").action(() => runtime.pluginsDoctor());
|
|
6186
8094
|
var config = program.command("config").description("Manage config values");
|
|
6187
|
-
config.command("get <path>").description("Get a config value by dot path").option("--json", "Output JSON", false).action((
|
|
6188
|
-
config.command("set <path> <value>").description("Set a config value by dot path").option("--json", "Parse value as JSON", false).action((
|
|
6189
|
-
config.command("unset <path>").description("Remove a config value by dot path").action((
|
|
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));
|
|
6190
8098
|
var secrets = program.command("secrets").description("Manage secrets refs/providers");
|
|
6191
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));
|
|
6192
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(
|