nextclaw 0.9.13 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +472 -420
- package/package.json +3 -3
- package/templates/USAGE.md +3 -2
- package/ui-dist/assets/{ChannelsList-Dz8AGmaQ.js → ChannelsList-DACqpUYZ.js} +1 -1
- package/ui-dist/assets/ChatPage-iji0RkTR.js +34 -0
- package/ui-dist/assets/{DocBrowser-CkKvzF7m.js → DocBrowser-D7mjKkGe.js} +1 -1
- package/ui-dist/assets/{LogoBadge-C_ygxoGB.js → LogoBadge-BlDT-g9R.js} +1 -1
- package/ui-dist/assets/{MarketplacePage-DEvRs-Jc.js → MarketplacePage-CZq3jVgg.js} +3 -3
- package/ui-dist/assets/{ModelConfig-BGfliN2Z.js → ModelConfig-DwRU5qrw.js} +1 -1
- package/ui-dist/assets/{ProvidersList-BHLGLSvs.js → ProvidersList-DFxN3pjx.js} +1 -1
- package/ui-dist/assets/{RuntimeConfig-Clltld_h.js → RuntimeConfig-C7BRLGSC.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-CaJLf7oJ.js → SecretsConfig-D5xZh7VF.js} +1 -1
- package/ui-dist/assets/{SessionsConfig-3QF7K9wm.js → SessionsConfig-ovpj_otA.js} +1 -1
- package/ui-dist/assets/{card-DXo3NsaB.js → card-Bf4CtrW8.js} +1 -1
- package/ui-dist/assets/index-C_DhisNo.css +1 -0
- package/ui-dist/assets/{index-CGo5Vnh0.js → index-dKTqKCJo.js} +5 -5
- package/ui-dist/assets/{input-CzTldMKo.js → input-CaKJyoWZ.js} +1 -1
- package/ui-dist/assets/{label-De__vsU7.js → label-BaXSWTKI.js} +1 -1
- package/ui-dist/assets/{page-layout-BOgLC2tK.js → page-layout-DA6PFRtQ.js} +1 -1
- package/ui-dist/assets/{session-run-status-DQVCDxTb.js → session-run-status-CllIZxNf.js} +1 -1
- package/ui-dist/assets/{switch-pMrS4heA.js → switch-Cvd5wZs-.js} +1 -1
- package/ui-dist/assets/{tabs-custom-DhOxWfCb.js → tabs-custom-0PybLkXs.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-CseKBGh5.js → useConfirmDialog-DdtpSju1.js} +2 -2
- package/ui-dist/assets/{vendor-D33xZtEC.js → vendor-C--HHaLf.js} +1 -1
- package/ui-dist/index.html +3 -3
- package/ui-dist/assets/ChatPage-BXDyt7BL.js +0 -34
- package/ui-dist/assets/index-DcxYzrFm.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
|
|
27
27
|
setPluginRuntimeBridge as setPluginRuntimeBridge2
|
|
28
28
|
} from "@nextclaw/openclaw-compat";
|
|
29
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
29
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
30
30
|
import { join as join7, resolve as resolve9 } from "path";
|
|
31
31
|
import { createInterface as createInterface2 } from "readline";
|
|
32
32
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -185,15 +185,13 @@ function parseSessionKey(sessionKey) {
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
// src/cli/skills/
|
|
189
|
-
import {
|
|
190
|
-
import {
|
|
191
|
-
import {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
throw new Error("Skill slug is required.");
|
|
196
|
-
}
|
|
188
|
+
// src/cli/skills/marketplace.ts
|
|
189
|
+
import { cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
190
|
+
import { basename, dirname, isAbsolute, join, relative, resolve as resolve2 } from "path";
|
|
191
|
+
import { SkillsLoader } from "@nextclaw/core";
|
|
192
|
+
var DEFAULT_MARKETPLACE_API_BASE = "https://marketplace-api.nextclaw.io";
|
|
193
|
+
async function installMarketplaceSkill(options) {
|
|
194
|
+
const slug = validateSkillSlug(options.slug.trim(), "slug");
|
|
197
195
|
const workdir = resolve2(options.workdir);
|
|
198
196
|
if (!existsSync2(workdir)) {
|
|
199
197
|
throw new Error(`Workdir does not exist: ${workdir}`);
|
|
@@ -205,62 +203,256 @@ async function installClawHubSkill(options) {
|
|
|
205
203
|
if (existsSync2(skillFile)) {
|
|
206
204
|
return {
|
|
207
205
|
slug,
|
|
208
|
-
version: options.version,
|
|
209
|
-
registry: options.registry,
|
|
210
206
|
destinationDir,
|
|
211
|
-
alreadyInstalled: true
|
|
207
|
+
alreadyInstalled: true,
|
|
208
|
+
source: "marketplace"
|
|
212
209
|
};
|
|
213
210
|
}
|
|
214
211
|
throw new Error(`Skill directory already exists: ${destinationDir} (use --force)`);
|
|
215
212
|
}
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
213
|
+
const apiBase = resolveMarketplaceApiBase(options.apiBaseUrl);
|
|
214
|
+
const item = await fetchMarketplaceSkillItem(apiBase, slug);
|
|
215
|
+
if (item.install.kind === "builtin") {
|
|
216
|
+
if (existsSync2(destinationDir) && options.force) {
|
|
217
|
+
rmSync2(destinationDir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
installBuiltinSkill(workdir, destinationDir, slug);
|
|
220
|
+
return {
|
|
221
|
+
slug,
|
|
222
|
+
destinationDir,
|
|
223
|
+
source: "builtin"
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const filesPayload = await fetchMarketplaceSkillFiles(apiBase, slug);
|
|
227
|
+
if (existsSync2(destinationDir) && options.force) {
|
|
228
|
+
rmSync2(destinationDir, { recursive: true, force: true });
|
|
224
229
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
+
mkdirSync2(destinationDir, { recursive: true });
|
|
231
|
+
for (const file of filesPayload.files) {
|
|
232
|
+
const targetPath = resolve2(destinationDir, ...file.path.split("/"));
|
|
233
|
+
const rel = relative(destinationDir, targetPath);
|
|
234
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
235
|
+
throw new Error(`Invalid marketplace file path: ${file.path}`);
|
|
236
|
+
}
|
|
237
|
+
mkdirSync2(dirname(targetPath), { recursive: true });
|
|
238
|
+
writeFileSync2(targetPath, Buffer.from(file.contentBase64, "base64"));
|
|
239
|
+
}
|
|
240
|
+
if (!existsSync2(join(destinationDir, "SKILL.md"))) {
|
|
241
|
+
throw new Error(`Marketplace skill ${slug} does not include SKILL.md`);
|
|
230
242
|
}
|
|
231
243
|
return {
|
|
232
244
|
slug,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
destinationDir
|
|
245
|
+
destinationDir,
|
|
246
|
+
source: "marketplace"
|
|
236
247
|
};
|
|
237
248
|
}
|
|
238
|
-
function
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
241
|
-
|
|
249
|
+
async function publishMarketplaceSkill(options) {
|
|
250
|
+
const skillDir = resolve2(options.skillDir);
|
|
251
|
+
if (!existsSync2(skillDir)) {
|
|
252
|
+
throw new Error(`Skill directory not found: ${skillDir}`);
|
|
253
|
+
}
|
|
254
|
+
const files = collectFiles(skillDir);
|
|
255
|
+
if (!files.some((file) => file.path === "SKILL.md")) {
|
|
256
|
+
throw new Error(`Skill directory must include SKILL.md: ${skillDir}`);
|
|
257
|
+
}
|
|
258
|
+
const parsedFrontmatter = parseSkillFrontmatter(readFileSync2(join(skillDir, "SKILL.md"), "utf8"));
|
|
259
|
+
const slug = validateSkillSlug(options.slug?.trim() || basename(skillDir), "slug");
|
|
260
|
+
const name = options.name?.trim() || parsedFrontmatter.name || slug;
|
|
261
|
+
const description = options.description?.trim() || parsedFrontmatter.description;
|
|
262
|
+
const summary = options.summary?.trim() || parsedFrontmatter.summary || description || `${slug} skill`;
|
|
263
|
+
const author = options.author?.trim() || parsedFrontmatter.author || "nextclaw";
|
|
264
|
+
const tags = normalizeTags(options.tags && options.tags.length > 0 ? options.tags : parsedFrontmatter.tags);
|
|
265
|
+
const apiBase = resolveMarketplaceApiBase(options.apiBaseUrl);
|
|
266
|
+
const token = resolveMarketplaceAdminToken(options.token);
|
|
267
|
+
if (options.requireExisting) {
|
|
268
|
+
await fetchMarketplaceSkillItem(apiBase, slug);
|
|
269
|
+
}
|
|
270
|
+
const response = await fetch(`${apiBase}/api/v1/admin/skills/upsert`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: {
|
|
273
|
+
"content-type": "application/json",
|
|
274
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
275
|
+
},
|
|
276
|
+
body: JSON.stringify({
|
|
277
|
+
slug,
|
|
278
|
+
name,
|
|
279
|
+
summary,
|
|
280
|
+
description,
|
|
281
|
+
author,
|
|
282
|
+
tags,
|
|
283
|
+
sourceRepo: options.sourceRepo?.trim() || void 0,
|
|
284
|
+
homepage: options.homepage?.trim() || void 0,
|
|
285
|
+
publishedAt: options.publishedAt?.trim() || void 0,
|
|
286
|
+
updatedAt: options.updatedAt?.trim() || void 0,
|
|
287
|
+
files
|
|
288
|
+
})
|
|
289
|
+
});
|
|
290
|
+
const payload = await readMarketplaceEnvelope(response);
|
|
291
|
+
if (!payload.ok || !payload.data) {
|
|
292
|
+
const message = payload.error?.message || `marketplace publish failed: HTTP ${response.status}`;
|
|
293
|
+
throw new Error(message);
|
|
242
294
|
}
|
|
243
|
-
|
|
244
|
-
|
|
295
|
+
return {
|
|
296
|
+
created: payload.data.created,
|
|
297
|
+
slug,
|
|
298
|
+
fileCount: payload.data.fileCount
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function collectFiles(rootDir) {
|
|
302
|
+
const output = [];
|
|
303
|
+
const walk = (dir, prefix) => {
|
|
304
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
305
|
+
for (const entry of entries) {
|
|
306
|
+
const absolute = join(dir, entry.name);
|
|
307
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
308
|
+
if (entry.isDirectory()) {
|
|
309
|
+
walk(absolute, relativePath);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!entry.isFile()) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const content = readFileSync2(absolute);
|
|
316
|
+
output.push({
|
|
317
|
+
path: relativePath,
|
|
318
|
+
contentBase64: content.toString("base64")
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
walk(rootDir, "");
|
|
323
|
+
return output;
|
|
324
|
+
}
|
|
325
|
+
function installBuiltinSkill(workdir, destinationDir, skillName) {
|
|
326
|
+
const loader = new SkillsLoader(workdir);
|
|
327
|
+
const builtin = loader.listSkills(false).find((skill) => skill.name === skillName && skill.source === "builtin");
|
|
328
|
+
if (!builtin) {
|
|
329
|
+
throw new Error(`Builtin skill not found in local core bundle: ${skillName}`);
|
|
245
330
|
}
|
|
246
|
-
|
|
247
|
-
|
|
331
|
+
cpSync(dirname(builtin.path), destinationDir, { recursive: true, force: true });
|
|
332
|
+
}
|
|
333
|
+
async function fetchMarketplaceSkillItem(apiBase, slug) {
|
|
334
|
+
const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}`, {
|
|
335
|
+
headers: {
|
|
336
|
+
Accept: "application/json"
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
const payload = await readMarketplaceEnvelope(response);
|
|
340
|
+
if (!payload.ok || !payload.data) {
|
|
341
|
+
const message = payload.error?.message || `marketplace skill fetch failed: ${response.status}`;
|
|
342
|
+
throw new Error(message);
|
|
248
343
|
}
|
|
249
|
-
|
|
250
|
-
|
|
344
|
+
const kind = payload.data.install?.kind;
|
|
345
|
+
if (kind !== "builtin" && kind !== "marketplace") {
|
|
346
|
+
throw new Error(`Unsupported skill install kind from marketplace: ${String(kind)}`);
|
|
251
347
|
}
|
|
252
|
-
|
|
253
|
-
|
|
348
|
+
return {
|
|
349
|
+
install: {
|
|
350
|
+
kind
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async function fetchMarketplaceSkillFiles(apiBase, slug) {
|
|
355
|
+
const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}/files`, {
|
|
356
|
+
headers: {
|
|
357
|
+
Accept: "application/json"
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
const payload = await readMarketplaceEnvelope(response);
|
|
361
|
+
if (!payload.ok || !payload.data) {
|
|
362
|
+
const message = payload.error?.message || `marketplace skill file fetch failed: ${response.status}`;
|
|
363
|
+
throw new Error(message);
|
|
254
364
|
}
|
|
255
|
-
return
|
|
365
|
+
return payload.data;
|
|
366
|
+
}
|
|
367
|
+
async function readMarketplaceEnvelope(response) {
|
|
368
|
+
const raw = await response.text();
|
|
369
|
+
let payload;
|
|
370
|
+
try {
|
|
371
|
+
payload = raw.length > 0 ? JSON.parse(raw) : null;
|
|
372
|
+
} catch {
|
|
373
|
+
throw new Error(`Invalid marketplace response: ${response.status}`);
|
|
374
|
+
}
|
|
375
|
+
if (!isRecord(payload) || typeof payload.ok !== "boolean") {
|
|
376
|
+
throw new Error(`Invalid marketplace response shape: ${response.status}`);
|
|
377
|
+
}
|
|
378
|
+
return payload;
|
|
379
|
+
}
|
|
380
|
+
function resolveMarketplaceApiBase(explicitBase) {
|
|
381
|
+
const raw = explicitBase?.trim() || process.env.NEXTCLAW_MARKETPLACE_API_BASE?.trim() || DEFAULT_MARKETPLACE_API_BASE;
|
|
382
|
+
return raw.replace(/\/+$/, "");
|
|
383
|
+
}
|
|
384
|
+
function resolveMarketplaceAdminToken(explicitToken) {
|
|
385
|
+
const token = explicitToken?.trim() || process.env.NEXTCLAW_MARKETPLACE_ADMIN_TOKEN?.trim();
|
|
386
|
+
return token && token.length > 0 ? token : void 0;
|
|
387
|
+
}
|
|
388
|
+
function validateSkillSlug(raw, fieldName) {
|
|
389
|
+
if (!/^[A-Za-z0-9._-]+$/.test(raw)) {
|
|
390
|
+
throw new Error(`Invalid ${fieldName}: ${raw}`);
|
|
391
|
+
}
|
|
392
|
+
return raw;
|
|
393
|
+
}
|
|
394
|
+
function normalizeTags(rawTags) {
|
|
395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
396
|
+
const output = [];
|
|
397
|
+
for (const rawTag of rawTags ?? []) {
|
|
398
|
+
const tag = rawTag.trim();
|
|
399
|
+
if (!tag || seen.has(tag)) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
seen.add(tag);
|
|
403
|
+
output.push(tag);
|
|
404
|
+
}
|
|
405
|
+
return output.length > 0 ? output : ["skill"];
|
|
406
|
+
}
|
|
407
|
+
function parseSkillFrontmatter(raw) {
|
|
408
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
409
|
+
const match = normalized.match(/^---\n([\s\S]*?)\n---/);
|
|
410
|
+
if (!match || !match[1]) {
|
|
411
|
+
return {};
|
|
412
|
+
}
|
|
413
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
414
|
+
for (const line of match[1].split("\n")) {
|
|
415
|
+
const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.+)$/);
|
|
416
|
+
if (!parsed) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const key = parsed[1]?.trim().toLowerCase();
|
|
420
|
+
const value = parsed[2]?.trim();
|
|
421
|
+
if (!key || !value) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
metadata.set(key, trimYamlString(value));
|
|
425
|
+
}
|
|
426
|
+
const rawTags = metadata.get("tags");
|
|
427
|
+
let tags;
|
|
428
|
+
if (rawTags) {
|
|
429
|
+
if (rawTags.startsWith("[") && rawTags.endsWith("]")) {
|
|
430
|
+
tags = rawTags.slice(1, -1).split(",").map((entry) => trimYamlString(entry)).filter(Boolean);
|
|
431
|
+
} else {
|
|
432
|
+
tags = rawTags.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
name: metadata.get("name"),
|
|
437
|
+
summary: metadata.get("summary"),
|
|
438
|
+
description: metadata.get("description"),
|
|
439
|
+
author: metadata.get("author"),
|
|
440
|
+
tags
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function trimYamlString(raw) {
|
|
444
|
+
return raw.replace(/^['"]/, "").replace(/['"]$/, "").trim();
|
|
445
|
+
}
|
|
446
|
+
function isRecord(value) {
|
|
447
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
256
448
|
}
|
|
257
449
|
|
|
258
450
|
// src/cli/update/runner.ts
|
|
259
|
-
import { spawnSync
|
|
451
|
+
import { spawnSync } from "child_process";
|
|
260
452
|
import { resolve as resolve4 } from "path";
|
|
261
453
|
|
|
262
454
|
// src/cli/utils.ts
|
|
263
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
455
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, rmSync as rmSync3 } from "fs";
|
|
264
456
|
import { join as join2, resolve as resolve3 } from "path";
|
|
265
457
|
import { spawn } from "child_process";
|
|
266
458
|
import { isIP } from "net";
|
|
@@ -319,7 +511,7 @@ function readServiceState() {
|
|
|
319
511
|
return null;
|
|
320
512
|
}
|
|
321
513
|
try {
|
|
322
|
-
const raw =
|
|
514
|
+
const raw = readFileSync3(path, "utf-8");
|
|
323
515
|
return JSON.parse(raw);
|
|
324
516
|
} catch {
|
|
325
517
|
return null;
|
|
@@ -327,13 +519,13 @@ function readServiceState() {
|
|
|
327
519
|
}
|
|
328
520
|
function writeServiceState(state) {
|
|
329
521
|
const path = resolveServiceStatePath();
|
|
330
|
-
|
|
331
|
-
|
|
522
|
+
mkdirSync3(resolve3(path, ".."), { recursive: true });
|
|
523
|
+
writeFileSync3(path, JSON.stringify(state, null, 2));
|
|
332
524
|
}
|
|
333
525
|
function clearServiceState() {
|
|
334
526
|
const path = resolveServiceStatePath();
|
|
335
527
|
if (existsSync3(path)) {
|
|
336
|
-
|
|
528
|
+
rmSync3(path, { force: true });
|
|
337
529
|
}
|
|
338
530
|
}
|
|
339
531
|
function resolveServiceStatePath() {
|
|
@@ -467,7 +659,7 @@ function resolveVersionFromPackageTree(startDir, expectedName) {
|
|
|
467
659
|
const pkgPath = join2(current, "package.json");
|
|
468
660
|
if (existsSync3(pkgPath)) {
|
|
469
661
|
try {
|
|
470
|
-
const raw =
|
|
662
|
+
const raw = readFileSync3(pkgPath, "utf-8");
|
|
471
663
|
const parsed = JSON.parse(raw);
|
|
472
664
|
if (typeof parsed.version === "string") {
|
|
473
665
|
if (!expectedName || parsed.name === expectedName) {
|
|
@@ -514,7 +706,7 @@ function runSelfUpdate(options = {}) {
|
|
|
514
706
|
return { cmd: process.env.SHELL || "sh", args: ["-c", command] };
|
|
515
707
|
};
|
|
516
708
|
const runStep = (cmd, args, cwd) => {
|
|
517
|
-
const result =
|
|
709
|
+
const result = spawnSync(cmd, args, {
|
|
518
710
|
cwd,
|
|
519
711
|
encoding: "utf-8",
|
|
520
712
|
timeout: timeoutMs,
|
|
@@ -1389,7 +1581,7 @@ var ConfigCommands = class {
|
|
|
1389
1581
|
};
|
|
1390
1582
|
|
|
1391
1583
|
// src/cli/commands/secrets.ts
|
|
1392
|
-
import { readFileSync as
|
|
1584
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1393
1585
|
import {
|
|
1394
1586
|
buildReloadPlan as buildReloadPlan2,
|
|
1395
1587
|
diffConfigPaths as diffConfigPaths2,
|
|
@@ -1632,7 +1824,7 @@ var SecretsCommands = class {
|
|
|
1632
1824
|
nextConfig.secrets.enabled = false;
|
|
1633
1825
|
}
|
|
1634
1826
|
if (opts.file) {
|
|
1635
|
-
const raw =
|
|
1827
|
+
const raw = readFileSync4(opts.file, "utf-8");
|
|
1636
1828
|
const patch = parseApplyFile(raw);
|
|
1637
1829
|
if (patch.defaults) {
|
|
1638
1830
|
nextConfig.secrets.defaults = patch.defaults;
|
|
@@ -1712,7 +1904,7 @@ var SecretsCommands = class {
|
|
|
1712
1904
|
};
|
|
1713
1905
|
|
|
1714
1906
|
// src/cli/commands/channels.ts
|
|
1715
|
-
import { spawnSync as
|
|
1907
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1716
1908
|
import { getWorkspacePath as getWorkspacePath2, loadConfig as loadConfig4, saveConfig as saveConfig4 } from "@nextclaw/core";
|
|
1717
1909
|
import { BUILTIN_CHANNEL_PLUGIN_IDS, builtinProviderIds as builtinProviderIds2 } from "@nextclaw/runtime";
|
|
1718
1910
|
import { buildPluginStatusReport as buildPluginStatusReport2, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
|
|
@@ -1762,7 +1954,7 @@ var ChannelCommands = class {
|
|
|
1762
1954
|
const bridgeDir = this.deps.getBridgeDir();
|
|
1763
1955
|
console.log(`${this.deps.logo} Starting bridge...`);
|
|
1764
1956
|
console.log("Scan the QR code to connect.\n");
|
|
1765
|
-
const result =
|
|
1957
|
+
const result = spawnSync2("npm", ["start"], { cwd: bridgeDir, stdio: "inherit" });
|
|
1766
1958
|
if (result.status !== 0) {
|
|
1767
1959
|
console.error(`Bridge failed: ${result.status ?? 1}`);
|
|
1768
1960
|
}
|
|
@@ -1903,7 +2095,7 @@ var CronCommands = class {
|
|
|
1903
2095
|
|
|
1904
2096
|
// src/cli/commands/diagnostics.ts
|
|
1905
2097
|
import { createServer as createNetServer } from "net";
|
|
1906
|
-
import { existsSync as existsSync5, readFileSync as
|
|
2098
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
1907
2099
|
import { resolve as resolve6 } from "path";
|
|
1908
2100
|
import {
|
|
1909
2101
|
APP_NAME,
|
|
@@ -2212,7 +2404,7 @@ var DiagnosticsCommands = class {
|
|
|
2212
2404
|
return [];
|
|
2213
2405
|
}
|
|
2214
2406
|
try {
|
|
2215
|
-
const lines =
|
|
2407
|
+
const lines = readFileSync5(path, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
2216
2408
|
if (lines.length <= maxLines) {
|
|
2217
2409
|
return lines;
|
|
2218
2410
|
}
|
|
@@ -2252,9 +2444,8 @@ import {
|
|
|
2252
2444
|
stopPluginChannelGateways
|
|
2253
2445
|
} from "@nextclaw/openclaw-compat";
|
|
2254
2446
|
import { startUiServer } from "@nextclaw/server";
|
|
2255
|
-
import { appendFileSync, closeSync, cpSync, existsSync as existsSync8, mkdirSync as
|
|
2256
|
-
import { dirname
|
|
2257
|
-
import { tmpdir } from "os";
|
|
2447
|
+
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync8, mkdirSync as mkdirSync5, openSync, rmSync as rmSync4 } from "fs";
|
|
2448
|
+
import { dirname as dirname2, join as join5, resolve as resolve7 } from "path";
|
|
2258
2449
|
import { spawn as spawn2 } from "child_process";
|
|
2259
2450
|
import { request as httpRequest } from "http";
|
|
2260
2451
|
import { request as httpsRequest } from "https";
|
|
@@ -2263,7 +2454,7 @@ import chokidar from "chokidar";
|
|
|
2263
2454
|
|
|
2264
2455
|
// src/cli/gateway/controller.ts
|
|
2265
2456
|
import { createHash } from "crypto";
|
|
2266
|
-
import { existsSync as existsSync6, readFileSync as
|
|
2457
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
2267
2458
|
import {
|
|
2268
2459
|
buildConfigSchema,
|
|
2269
2460
|
ConfigSchema,
|
|
@@ -2276,7 +2467,7 @@ var readConfigSnapshot = (getConfigPath5) => {
|
|
|
2276
2467
|
let raw = "";
|
|
2277
2468
|
let parsed = {};
|
|
2278
2469
|
if (existsSync6(path)) {
|
|
2279
|
-
raw =
|
|
2470
|
+
raw = readFileSync6(path, "utf-8");
|
|
2280
2471
|
try {
|
|
2281
2472
|
parsed = JSON.parse(raw);
|
|
2282
2473
|
} catch {
|
|
@@ -2715,6 +2906,7 @@ var MissingProvider = class extends LLMProvider {
|
|
|
2715
2906
|
// src/cli/commands/agent-runtime-pool.ts
|
|
2716
2907
|
import {
|
|
2717
2908
|
NativeAgentEngine,
|
|
2909
|
+
CommandRegistry,
|
|
2718
2910
|
AgentRouteResolver,
|
|
2719
2911
|
getWorkspacePath as getWorkspacePath4,
|
|
2720
2912
|
parseAgentScopedSessionKey
|
|
@@ -2804,6 +2996,14 @@ var GatewayAgentRuntimePool = class {
|
|
|
2804
2996
|
metadata: params.metadata,
|
|
2805
2997
|
agentId: params.agentId
|
|
2806
2998
|
});
|
|
2999
|
+
const commandResult = await this.executeDirectCommand(params.content, {
|
|
3000
|
+
channel: message.channel,
|
|
3001
|
+
chatId: message.chatId,
|
|
3002
|
+
sessionKey: route.sessionKey
|
|
3003
|
+
});
|
|
3004
|
+
if (commandResult) {
|
|
3005
|
+
return commandResult;
|
|
3006
|
+
}
|
|
2807
3007
|
const runtime2 = this.resolveRuntime(route.agentId);
|
|
2808
3008
|
return runtime2.engine.processDirect({
|
|
2809
3009
|
content: params.content,
|
|
@@ -2816,6 +3016,42 @@ var GatewayAgentRuntimePool = class {
|
|
|
2816
3016
|
onSessionEvent: params.onSessionEvent
|
|
2817
3017
|
});
|
|
2818
3018
|
}
|
|
3019
|
+
async executeDirectCommand(rawContent, ctx) {
|
|
3020
|
+
const trimmed = rawContent.trim();
|
|
3021
|
+
if (!trimmed.startsWith("/")) {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
const registry = new CommandRegistry(this.options.config, this.options.sessionManager);
|
|
3025
|
+
const executeText = registry.executeText;
|
|
3026
|
+
if (typeof executeText === "function") {
|
|
3027
|
+
const result2 = await executeText.call(registry, rawContent, {
|
|
3028
|
+
channel: ctx.channel,
|
|
3029
|
+
chatId: ctx.chatId,
|
|
3030
|
+
senderId: "user",
|
|
3031
|
+
sessionKey: ctx.sessionKey
|
|
3032
|
+
});
|
|
3033
|
+
return result2?.content ?? null;
|
|
3034
|
+
}
|
|
3035
|
+
const commandRaw = trimmed.slice(1).trim();
|
|
3036
|
+
if (!commandRaw) {
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
const [nameToken, ...restTokens] = commandRaw.split(/\s+/);
|
|
3040
|
+
const commandName = nameToken.trim().toLowerCase();
|
|
3041
|
+
if (!commandName) {
|
|
3042
|
+
return null;
|
|
3043
|
+
}
|
|
3044
|
+
const commandTail = restTokens.join(" ").trim();
|
|
3045
|
+
const specs = registry.listSlashCommands();
|
|
3046
|
+
const args = parseCommandArgsFromText(commandName, commandTail, specs);
|
|
3047
|
+
const result = await registry.execute(commandName, args, {
|
|
3048
|
+
channel: ctx.channel,
|
|
3049
|
+
chatId: ctx.chatId,
|
|
3050
|
+
senderId: "user",
|
|
3051
|
+
sessionKey: ctx.sessionKey
|
|
3052
|
+
});
|
|
3053
|
+
return result?.content ?? null;
|
|
3054
|
+
}
|
|
2819
3055
|
supportsTurnAbort(params) {
|
|
2820
3056
|
const { route } = this.resolveDirectRoute({
|
|
2821
3057
|
content: "",
|
|
@@ -2988,9 +3224,57 @@ var GatewayAgentRuntimePool = class {
|
|
|
2988
3224
|
this.runtimes = nextRuntimes;
|
|
2989
3225
|
}
|
|
2990
3226
|
};
|
|
3227
|
+
function parseCommandArgsFromText(commandName, rawTail, specs) {
|
|
3228
|
+
if (!rawTail) {
|
|
3229
|
+
return {};
|
|
3230
|
+
}
|
|
3231
|
+
const command = specs.find((item) => item.name.trim().toLowerCase() === commandName);
|
|
3232
|
+
const options = command?.options;
|
|
3233
|
+
if (!options || options.length === 0) {
|
|
3234
|
+
return {};
|
|
3235
|
+
}
|
|
3236
|
+
const tokens = rawTail.split(/\s+/).filter(Boolean);
|
|
3237
|
+
const args = {};
|
|
3238
|
+
let cursor = 0;
|
|
3239
|
+
for (let i = 0; i < options.length; i += 1) {
|
|
3240
|
+
if (cursor >= tokens.length) {
|
|
3241
|
+
break;
|
|
3242
|
+
}
|
|
3243
|
+
const option = options[i];
|
|
3244
|
+
const isLastOption = i === options.length - 1;
|
|
3245
|
+
const rawValue = isLastOption ? tokens.slice(cursor).join(" ") : tokens[cursor];
|
|
3246
|
+
cursor += isLastOption ? tokens.length - cursor : 1;
|
|
3247
|
+
const parsedValue = parseCommandOptionValue(option.type, rawValue);
|
|
3248
|
+
if (parsedValue !== void 0) {
|
|
3249
|
+
args[option.name] = parsedValue;
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
return args;
|
|
3253
|
+
}
|
|
3254
|
+
function parseCommandOptionValue(type, rawValue) {
|
|
3255
|
+
const value = rawValue.trim();
|
|
3256
|
+
if (!value) {
|
|
3257
|
+
return void 0;
|
|
3258
|
+
}
|
|
3259
|
+
if (type === "number") {
|
|
3260
|
+
const parsed = Number(value);
|
|
3261
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
3262
|
+
}
|
|
3263
|
+
if (type === "boolean") {
|
|
3264
|
+
const lowered = value.toLowerCase();
|
|
3265
|
+
if (["1", "true", "yes", "on"].includes(lowered)) {
|
|
3266
|
+
return true;
|
|
3267
|
+
}
|
|
3268
|
+
if (["0", "false", "no", "off"].includes(lowered)) {
|
|
3269
|
+
return false;
|
|
3270
|
+
}
|
|
3271
|
+
return void 0;
|
|
3272
|
+
}
|
|
3273
|
+
return value;
|
|
3274
|
+
}
|
|
2991
3275
|
|
|
2992
3276
|
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
2993
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
3277
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2994
3278
|
import { join as join4 } from "path";
|
|
2995
3279
|
import {
|
|
2996
3280
|
getDataDir as getDataDir5,
|
|
@@ -3004,7 +3288,7 @@ function createRunId() {
|
|
|
3004
3288
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
3005
3289
|
return `run-${now}-${rand}`;
|
|
3006
3290
|
}
|
|
3007
|
-
function
|
|
3291
|
+
function isRecord2(value) {
|
|
3008
3292
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3009
3293
|
}
|
|
3010
3294
|
function readOptionalString(value) {
|
|
@@ -3035,7 +3319,7 @@ function cloneEvent(event) {
|
|
|
3035
3319
|
var UiChatRunCoordinator = class {
|
|
3036
3320
|
constructor(options) {
|
|
3037
3321
|
this.options = options;
|
|
3038
|
-
|
|
3322
|
+
mkdirSync4(RUNS_DIR, { recursive: true });
|
|
3039
3323
|
this.loadPersistedRuns();
|
|
3040
3324
|
}
|
|
3041
3325
|
runs = /* @__PURE__ */ new Map();
|
|
@@ -3172,7 +3456,7 @@ var UiChatRunCoordinator = class {
|
|
|
3172
3456
|
const parsedAgentId = parseAgentScopedSessionKey2(sessionKey)?.agentId;
|
|
3173
3457
|
const agentId = explicitAgentId ?? readOptionalString(parsedAgentId);
|
|
3174
3458
|
const model = readOptionalString(input.model);
|
|
3175
|
-
const metadata =
|
|
3459
|
+
const metadata = isRecord2(input.metadata) ? { ...input.metadata } : {};
|
|
3176
3460
|
if (model) {
|
|
3177
3461
|
metadata.model = model;
|
|
3178
3462
|
}
|
|
@@ -3414,20 +3698,20 @@ var UiChatRunCoordinator = class {
|
|
|
3414
3698
|
...typeof run.reply === "string" ? { reply: run.reply } : {},
|
|
3415
3699
|
events: run.events
|
|
3416
3700
|
};
|
|
3417
|
-
|
|
3701
|
+
writeFileSync4(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
|
|
3418
3702
|
`);
|
|
3419
3703
|
}
|
|
3420
3704
|
loadPersistedRuns() {
|
|
3421
3705
|
if (!existsSync7(RUNS_DIR)) {
|
|
3422
3706
|
return;
|
|
3423
3707
|
}
|
|
3424
|
-
for (const entry of
|
|
3708
|
+
for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
|
|
3425
3709
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
3426
3710
|
continue;
|
|
3427
3711
|
}
|
|
3428
3712
|
const path = join4(RUNS_DIR, entry.name);
|
|
3429
3713
|
try {
|
|
3430
|
-
const parsed = JSON.parse(
|
|
3714
|
+
const parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
3431
3715
|
const runId = readOptionalString(parsed.runId);
|
|
3432
3716
|
const sessionKey = readOptionalString(parsed.sessionKey);
|
|
3433
3717
|
if (!runId || !sessionKey) {
|
|
@@ -3508,6 +3792,42 @@ function createSkillsLoader(workspace) {
|
|
|
3508
3792
|
}
|
|
3509
3793
|
return new ctor(workspace);
|
|
3510
3794
|
}
|
|
3795
|
+
function containsAbsoluteFsPath(line) {
|
|
3796
|
+
const normalized = line.trim();
|
|
3797
|
+
if (!normalized) {
|
|
3798
|
+
return false;
|
|
3799
|
+
}
|
|
3800
|
+
const lowered = normalized.toLowerCase();
|
|
3801
|
+
if (lowered.includes("http://") || lowered.includes("https://")) {
|
|
3802
|
+
return false;
|
|
3803
|
+
}
|
|
3804
|
+
if (/^[A-Za-z]:\\/.test(normalized)) {
|
|
3805
|
+
return true;
|
|
3806
|
+
}
|
|
3807
|
+
return /(?:^|\s)(?:~\/|\/[^\s]+)/.test(normalized);
|
|
3808
|
+
}
|
|
3809
|
+
function pickUserFacingCommandSummary(output, fallback) {
|
|
3810
|
+
const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
3811
|
+
if (lines.length === 0) {
|
|
3812
|
+
return fallback;
|
|
3813
|
+
}
|
|
3814
|
+
const visibleLines = lines.filter((line) => {
|
|
3815
|
+
if (/^(path|install path|source path|destination|location)\s*:/i.test(line)) {
|
|
3816
|
+
return false;
|
|
3817
|
+
}
|
|
3818
|
+
if (containsAbsoluteFsPath(line)) {
|
|
3819
|
+
return false;
|
|
3820
|
+
}
|
|
3821
|
+
return true;
|
|
3822
|
+
});
|
|
3823
|
+
if (visibleLines.length === 0) {
|
|
3824
|
+
return fallback;
|
|
3825
|
+
}
|
|
3826
|
+
const preferred = [...visibleLines].reverse().find(
|
|
3827
|
+
(line) => /\b(installed|enabled|disabled|uninstalled|published|updated|already installed|removed)\b/i.test(line)
|
|
3828
|
+
);
|
|
3829
|
+
return preferred ?? visibleLines[visibleLines.length - 1] ?? fallback;
|
|
3830
|
+
}
|
|
3511
3831
|
var ServiceCommands = class {
|
|
3512
3832
|
constructor(deps) {
|
|
3513
3833
|
this.deps = deps;
|
|
@@ -3911,7 +4231,7 @@ var ServiceCommands = class {
|
|
|
3911
4231
|
}
|
|
3912
4232
|
const logPath = resolveServiceLogPath();
|
|
3913
4233
|
const logDir = resolve7(logPath, "..");
|
|
3914
|
-
|
|
4234
|
+
mkdirSync5(logDir, { recursive: true });
|
|
3915
4235
|
const logFd = openSync(logPath, "a");
|
|
3916
4236
|
const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
|
|
3917
4237
|
const quickPhaseTimeoutMs = Math.min(8e3, readinessTimeoutMs);
|
|
@@ -4311,8 +4631,8 @@ var ServiceCommands = class {
|
|
|
4311
4631
|
}
|
|
4312
4632
|
async installMarketplacePlugin(spec) {
|
|
4313
4633
|
const output = await this.runCliSubcommand(["plugins", "install", spec]);
|
|
4314
|
-
const summary =
|
|
4315
|
-
return { message: summary
|
|
4634
|
+
const summary = pickUserFacingCommandSummary(output, `Installed plugin: ${spec}`);
|
|
4635
|
+
return { message: summary };
|
|
4316
4636
|
}
|
|
4317
4637
|
async installMarketplaceSkill(params) {
|
|
4318
4638
|
if (params.kind === "builtin") {
|
|
@@ -4322,23 +4642,17 @@ var ServiceCommands = class {
|
|
|
4322
4642
|
}
|
|
4323
4643
|
return result;
|
|
4324
4644
|
}
|
|
4325
|
-
if (params.kind
|
|
4326
|
-
|
|
4645
|
+
if (params.kind && params.kind !== "marketplace") {
|
|
4646
|
+
throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
|
|
4327
4647
|
}
|
|
4328
4648
|
const args = ["skills", "install", params.slug];
|
|
4329
|
-
if (params.version) {
|
|
4330
|
-
args.push("--version", params.version);
|
|
4331
|
-
}
|
|
4332
|
-
if (params.registry) {
|
|
4333
|
-
args.push("--registry", params.registry);
|
|
4334
|
-
}
|
|
4335
4649
|
if (params.force) {
|
|
4336
4650
|
args.push("--force");
|
|
4337
4651
|
}
|
|
4338
4652
|
try {
|
|
4339
4653
|
const output = await this.runCliSubcommand(args);
|
|
4340
|
-
const summary =
|
|
4341
|
-
return { message: summary
|
|
4654
|
+
const summary = pickUserFacingCommandSummary(output, `Installed skill: ${params.slug}`);
|
|
4655
|
+
return { message: summary };
|
|
4342
4656
|
} catch (error) {
|
|
4343
4657
|
const fallback = this.installBuiltinMarketplaceSkill(params.slug, params.force);
|
|
4344
4658
|
if (!fallback) {
|
|
@@ -4347,258 +4661,20 @@ var ServiceCommands = class {
|
|
|
4347
4661
|
return fallback;
|
|
4348
4662
|
}
|
|
4349
4663
|
}
|
|
4350
|
-
async installGitMarketplaceSkill(params) {
|
|
4351
|
-
const source = params.slug.trim();
|
|
4352
|
-
if (!source) {
|
|
4353
|
-
throw new Error("Git skill source is required");
|
|
4354
|
-
}
|
|
4355
|
-
const workspace = getWorkspacePath5(loadConfig6().agents.defaults.workspace);
|
|
4356
|
-
const skillName = this.resolveGitSkillName(params.skill, source);
|
|
4357
|
-
const destination = this.resolveSkillInstallPath(workspace, params.installPath, skillName);
|
|
4358
|
-
const destinationSkillFile = join5(destination, "SKILL.md");
|
|
4359
|
-
if (existsSync8(destinationSkillFile) && !params.force) {
|
|
4360
|
-
return {
|
|
4361
|
-
message: `${skillName} is already installed`,
|
|
4362
|
-
output: destination
|
|
4363
|
-
};
|
|
4364
|
-
}
|
|
4365
|
-
if (existsSync8(destination) && !params.force) {
|
|
4366
|
-
throw new Error(`Skill install path already exists: ${destination} (use force to overwrite)`);
|
|
4367
|
-
}
|
|
4368
|
-
if (existsSync8(destination) && params.force) {
|
|
4369
|
-
rmSync3(destination, { recursive: true, force: true });
|
|
4370
|
-
}
|
|
4371
|
-
const materialized = await this.materializeMarketplaceGitSkillSource({ source, skillName });
|
|
4372
|
-
try {
|
|
4373
|
-
const installSkillFile = join5(materialized.skillDir, "SKILL.md");
|
|
4374
|
-
if (!existsSync8(installSkillFile)) {
|
|
4375
|
-
throw new Error(`git skill source does not contain SKILL.md for ${skillName}`);
|
|
4376
|
-
}
|
|
4377
|
-
mkdirSync4(dirname(destination), { recursive: true });
|
|
4378
|
-
cpSync(materialized.skillDir, destination, { recursive: true, force: true });
|
|
4379
|
-
return {
|
|
4380
|
-
message: `Installed skill: ${skillName}`,
|
|
4381
|
-
output: [
|
|
4382
|
-
`Source: ${source}`,
|
|
4383
|
-
`Materialized skill: ${materialized.skillDir}`,
|
|
4384
|
-
`Workspace target: ${destination}`,
|
|
4385
|
-
materialized.commandOutput
|
|
4386
|
-
].filter(Boolean).join("\n")
|
|
4387
|
-
};
|
|
4388
|
-
} finally {
|
|
4389
|
-
rmSync3(materialized.tempRoot, { recursive: true, force: true });
|
|
4390
|
-
}
|
|
4391
|
-
}
|
|
4392
|
-
async materializeMarketplaceGitSkillSource(params) {
|
|
4393
|
-
const parsed = this.parseMarketplaceGitSkillSource(params.source);
|
|
4394
|
-
const gitPath = this.resolveGitExecutablePath();
|
|
4395
|
-
const fallbackNotes = [];
|
|
4396
|
-
if (gitPath) {
|
|
4397
|
-
try {
|
|
4398
|
-
return await this.materializeMarketplaceGitSkillViaGit({ gitPath, parsed });
|
|
4399
|
-
} catch (error) {
|
|
4400
|
-
fallbackNotes.push(`Git fast path failed: ${String(error)}`);
|
|
4401
|
-
}
|
|
4402
|
-
} else {
|
|
4403
|
-
fallbackNotes.push("Git fast path unavailable: git executable not found");
|
|
4404
|
-
}
|
|
4405
|
-
const httpResult = await this.materializeMarketplaceGitSkillViaGithubApi(parsed);
|
|
4406
|
-
return {
|
|
4407
|
-
...httpResult,
|
|
4408
|
-
commandOutput: [...fallbackNotes, httpResult.commandOutput].filter(Boolean).join("\n")
|
|
4409
|
-
};
|
|
4410
|
-
}
|
|
4411
|
-
resolveGitExecutablePath() {
|
|
4412
|
-
return findExecutableOnPath("git");
|
|
4413
|
-
}
|
|
4414
|
-
async materializeMarketplaceGitSkillViaGit(params) {
|
|
4415
|
-
const tempRoot = mkdtempSync(join5(tmpdir(), "nextclaw-marketplace-skill-"));
|
|
4416
|
-
const repoDir = join5(tempRoot, "repo");
|
|
4417
|
-
try {
|
|
4418
|
-
const cloneArgs = ["clone", "--depth", "1", "--filter=blob:none", "--sparse"];
|
|
4419
|
-
if (params.parsed.ref) {
|
|
4420
|
-
cloneArgs.push("--branch", params.parsed.ref);
|
|
4421
|
-
}
|
|
4422
|
-
cloneArgs.push(params.parsed.repoUrl, repoDir);
|
|
4423
|
-
const cloneResult = await this.runCommand(params.gitPath, cloneArgs, {
|
|
4424
|
-
cwd: tempRoot,
|
|
4425
|
-
timeoutMs: 18e4
|
|
4426
|
-
});
|
|
4427
|
-
const sparseResult = await this.runCommand(params.gitPath, ["-C", repoDir, "sparse-checkout", "set", params.parsed.skillPath], {
|
|
4428
|
-
cwd: tempRoot,
|
|
4429
|
-
timeoutMs: 6e4
|
|
4430
|
-
});
|
|
4431
|
-
const skillDir = join5(repoDir, params.parsed.skillPath);
|
|
4432
|
-
return {
|
|
4433
|
-
tempRoot,
|
|
4434
|
-
skillDir,
|
|
4435
|
-
commandOutput: [
|
|
4436
|
-
"Installer path: git-sparse",
|
|
4437
|
-
`Git repository: ${params.parsed.repoUrl}`,
|
|
4438
|
-
`Git path: ${params.parsed.skillPath}`,
|
|
4439
|
-
this.mergeCommandOutput(cloneResult.stdout, cloneResult.stderr),
|
|
4440
|
-
this.mergeCommandOutput(sparseResult.stdout, sparseResult.stderr)
|
|
4441
|
-
].filter(Boolean).join("\n")
|
|
4442
|
-
};
|
|
4443
|
-
} catch (error) {
|
|
4444
|
-
rmSync3(tempRoot, { recursive: true, force: true });
|
|
4445
|
-
throw error;
|
|
4446
|
-
}
|
|
4447
|
-
}
|
|
4448
|
-
async materializeMarketplaceGitSkillViaGithubApi(parsed) {
|
|
4449
|
-
const tempRoot = mkdtempSync(join5(tmpdir(), "nextclaw-marketplace-skill-"));
|
|
4450
|
-
const skillDir = join5(tempRoot, "skill");
|
|
4451
|
-
const downloadedFiles = [];
|
|
4452
|
-
try {
|
|
4453
|
-
mkdirSync4(skillDir, { recursive: true });
|
|
4454
|
-
await this.downloadGithubDirectoryToPath({
|
|
4455
|
-
parsed,
|
|
4456
|
-
remotePath: parsed.skillPath,
|
|
4457
|
-
localDir: skillDir,
|
|
4458
|
-
relativePrefix: "",
|
|
4459
|
-
downloadedFiles
|
|
4460
|
-
});
|
|
4461
|
-
return {
|
|
4462
|
-
tempRoot,
|
|
4463
|
-
skillDir,
|
|
4464
|
-
commandOutput: [
|
|
4465
|
-
"Installer path: github-http",
|
|
4466
|
-
`Git repository: ${parsed.repoUrl}`,
|
|
4467
|
-
`Git path: ${parsed.skillPath}`,
|
|
4468
|
-
`Downloaded files: ${downloadedFiles.length}`
|
|
4469
|
-
].join("\n")
|
|
4470
|
-
};
|
|
4471
|
-
} catch (error) {
|
|
4472
|
-
rmSync3(tempRoot, { recursive: true, force: true });
|
|
4473
|
-
throw error;
|
|
4474
|
-
}
|
|
4475
|
-
}
|
|
4476
|
-
async downloadGithubDirectoryToPath(params) {
|
|
4477
|
-
const encodedPath = params.remotePath.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
4478
|
-
const apiUrl = new URL(`https://api.github.com/repos/${params.parsed.owner}/${params.parsed.repo}/contents/${encodedPath}`);
|
|
4479
|
-
if (params.parsed.ref) {
|
|
4480
|
-
apiUrl.searchParams.set("ref", params.parsed.ref);
|
|
4481
|
-
}
|
|
4482
|
-
const payload = await this.fetchJsonWithHeaders(apiUrl.toString(), {
|
|
4483
|
-
Accept: "application/vnd.github+json",
|
|
4484
|
-
"User-Agent": `${APP_NAME2}/${getPackageVersion()}`
|
|
4485
|
-
});
|
|
4486
|
-
if (!Array.isArray(payload)) {
|
|
4487
|
-
throw new Error(`GitHub path is not a directory: ${params.remotePath}`);
|
|
4488
|
-
}
|
|
4489
|
-
for (const entry of payload) {
|
|
4490
|
-
if (!entry || typeof entry !== "object") {
|
|
4491
|
-
continue;
|
|
4492
|
-
}
|
|
4493
|
-
const item = entry;
|
|
4494
|
-
const itemName = typeof item.name === "string" ? item.name.trim() : "";
|
|
4495
|
-
const itemPath = typeof item.path === "string" ? item.path.trim() : "";
|
|
4496
|
-
if (!itemName || !itemPath) {
|
|
4497
|
-
continue;
|
|
4498
|
-
}
|
|
4499
|
-
if (item.type === "dir") {
|
|
4500
|
-
const childLocalDir = join5(params.localDir, itemName);
|
|
4501
|
-
mkdirSync4(childLocalDir, { recursive: true });
|
|
4502
|
-
await this.downloadGithubDirectoryToPath({
|
|
4503
|
-
parsed: params.parsed,
|
|
4504
|
-
remotePath: itemPath,
|
|
4505
|
-
localDir: childLocalDir,
|
|
4506
|
-
relativePrefix: params.relativePrefix ? `${params.relativePrefix}/${itemName}` : itemName,
|
|
4507
|
-
downloadedFiles: params.downloadedFiles
|
|
4508
|
-
});
|
|
4509
|
-
continue;
|
|
4510
|
-
}
|
|
4511
|
-
if (item.type !== "file") {
|
|
4512
|
-
throw new Error(`Unsupported GitHub skill entry type: ${item.type ?? "unknown"}`);
|
|
4513
|
-
}
|
|
4514
|
-
if (!item.download_url) {
|
|
4515
|
-
throw new Error(`GitHub skill file missing download_url: ${itemPath}`);
|
|
4516
|
-
}
|
|
4517
|
-
const content = await this.fetchBinaryWithHeaders(item.download_url, {
|
|
4518
|
-
"User-Agent": `${APP_NAME2}/${getPackageVersion()}`
|
|
4519
|
-
});
|
|
4520
|
-
const localFile = join5(params.localDir, itemName);
|
|
4521
|
-
writeFileSync4(localFile, content);
|
|
4522
|
-
params.downloadedFiles.push(params.relativePrefix ? `${params.relativePrefix}/${itemName}` : itemName);
|
|
4523
|
-
}
|
|
4524
|
-
}
|
|
4525
|
-
async fetchJsonWithHeaders(url, headers) {
|
|
4526
|
-
const response = await fetch(url, { headers });
|
|
4527
|
-
if (!response.ok) {
|
|
4528
|
-
throw new Error(`HTTP ${response.status} when fetching ${url}`);
|
|
4529
|
-
}
|
|
4530
|
-
return await response.json();
|
|
4531
|
-
}
|
|
4532
|
-
async fetchBinaryWithHeaders(url, headers) {
|
|
4533
|
-
const response = await fetch(url, { headers });
|
|
4534
|
-
if (!response.ok) {
|
|
4535
|
-
throw new Error(`HTTP ${response.status} when downloading ${url}`);
|
|
4536
|
-
}
|
|
4537
|
-
const bytes = await response.arrayBuffer();
|
|
4538
|
-
return Buffer.from(bytes);
|
|
4539
|
-
}
|
|
4540
|
-
parseMarketplaceGitSkillSource(source) {
|
|
4541
|
-
const trimmed = source.trim();
|
|
4542
|
-
if (!trimmed) {
|
|
4543
|
-
throw new Error("Git skill source is required");
|
|
4544
|
-
}
|
|
4545
|
-
if (trimmed.includes("://")) {
|
|
4546
|
-
const parsedUrl = new URL(trimmed);
|
|
4547
|
-
if (parsedUrl.hostname !== "github.com") {
|
|
4548
|
-
throw new Error(`Unsupported git skill source host: ${parsedUrl.hostname}`);
|
|
4549
|
-
}
|
|
4550
|
-
const parts2 = parsedUrl.pathname.split("/").filter(Boolean);
|
|
4551
|
-
if (parts2.length < 5 || parts2[2] !== "tree" && parts2[2] !== "blob") {
|
|
4552
|
-
throw new Error(`Unsupported GitHub skill source URL: ${source}`);
|
|
4553
|
-
}
|
|
4554
|
-
const [owner2, repoRaw, , ref2, ...pathParts2] = parts2;
|
|
4555
|
-
const repo2 = repoRaw.replace(/\.git$/i, "");
|
|
4556
|
-
if (!owner2 || !repo2 || !ref2 || pathParts2.length === 0) {
|
|
4557
|
-
throw new Error(`Unsupported GitHub skill source URL: ${source}`);
|
|
4558
|
-
}
|
|
4559
|
-
return {
|
|
4560
|
-
owner: owner2,
|
|
4561
|
-
repo: repo2,
|
|
4562
|
-
repoUrl: `https://github.com/${owner2}/${repo2}.git`,
|
|
4563
|
-
skillPath: pathParts2.join("/"),
|
|
4564
|
-
ref: ref2
|
|
4565
|
-
};
|
|
4566
|
-
}
|
|
4567
|
-
const parts = trimmed.split("/").filter(Boolean);
|
|
4568
|
-
if (parts.length < 3) {
|
|
4569
|
-
throw new Error(`Unsupported git skill source: ${source}`);
|
|
4570
|
-
}
|
|
4571
|
-
const owner = parts[0] ?? "";
|
|
4572
|
-
const repoWithOptionalRef = parts[1] ?? "";
|
|
4573
|
-
const pathParts = parts.slice(2);
|
|
4574
|
-
const atIndex = repoWithOptionalRef.indexOf("@");
|
|
4575
|
-
const repo = (atIndex >= 0 ? repoWithOptionalRef.slice(0, atIndex) : repoWithOptionalRef).replace(/\.git$/i, "");
|
|
4576
|
-
const ref = atIndex >= 0 ? repoWithOptionalRef.slice(atIndex + 1) : void 0;
|
|
4577
|
-
if (!owner || !repo || pathParts.length === 0) {
|
|
4578
|
-
throw new Error(`Unsupported git skill source: ${source}`);
|
|
4579
|
-
}
|
|
4580
|
-
return {
|
|
4581
|
-
owner,
|
|
4582
|
-
repo,
|
|
4583
|
-
repoUrl: `https://github.com/${owner}/${repo}.git`,
|
|
4584
|
-
skillPath: pathParts.join("/"),
|
|
4585
|
-
ref: ref && ref.trim().length > 0 ? ref.trim() : void 0
|
|
4586
|
-
};
|
|
4587
|
-
}
|
|
4588
4664
|
async enableMarketplacePlugin(id) {
|
|
4589
4665
|
const output = await this.runCliSubcommand(["plugins", "enable", id]);
|
|
4590
|
-
const summary =
|
|
4591
|
-
return { message: summary
|
|
4666
|
+
const summary = pickUserFacingCommandSummary(output, `Enabled plugin: ${id}`);
|
|
4667
|
+
return { message: summary };
|
|
4592
4668
|
}
|
|
4593
4669
|
async disableMarketplacePlugin(id) {
|
|
4594
4670
|
const output = await this.runCliSubcommand(["plugins", "disable", id]);
|
|
4595
|
-
const summary =
|
|
4596
|
-
return { message: summary
|
|
4671
|
+
const summary = pickUserFacingCommandSummary(output, `Disabled plugin: ${id}`);
|
|
4672
|
+
return { message: summary };
|
|
4597
4673
|
}
|
|
4598
4674
|
async uninstallMarketplacePlugin(id) {
|
|
4599
4675
|
const output = await this.runCliSubcommand(["plugins", "uninstall", id, "--force"]);
|
|
4600
|
-
const summary =
|
|
4601
|
-
return { message: summary
|
|
4676
|
+
const summary = pickUserFacingCommandSummary(output, `Uninstalled plugin: ${id}`);
|
|
4677
|
+
return { message: summary };
|
|
4602
4678
|
}
|
|
4603
4679
|
async uninstallMarketplaceSkill(slug) {
|
|
4604
4680
|
const workspace = getWorkspacePath5(loadConfig6().agents.defaults.workspace);
|
|
@@ -4606,10 +4682,9 @@ var ServiceCommands = class {
|
|
|
4606
4682
|
if (!existsSync8(targetDir)) {
|
|
4607
4683
|
throw new Error(`Skill not installed in workspace: ${slug}`);
|
|
4608
4684
|
}
|
|
4609
|
-
|
|
4685
|
+
rmSync4(targetDir, { recursive: true, force: true });
|
|
4610
4686
|
return {
|
|
4611
|
-
message: `Uninstalled skill: ${slug}
|
|
4612
|
-
output: `Removed ${targetDir}`
|
|
4687
|
+
message: `Uninstalled skill: ${slug}`
|
|
4613
4688
|
};
|
|
4614
4689
|
}
|
|
4615
4690
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
@@ -4618,8 +4693,7 @@ var ServiceCommands = class {
|
|
|
4618
4693
|
const destinationSkillFile = join5(destination, "SKILL.md");
|
|
4619
4694
|
if (existsSync8(destinationSkillFile) && !force) {
|
|
4620
4695
|
return {
|
|
4621
|
-
message: `${slug} is already installed
|
|
4622
|
-
output: destination
|
|
4696
|
+
message: `${slug} is already installed`
|
|
4623
4697
|
};
|
|
4624
4698
|
}
|
|
4625
4699
|
const loader = createSkillsLoader(workspace);
|
|
@@ -4627,50 +4701,17 @@ var ServiceCommands = class {
|
|
|
4627
4701
|
if (!builtin) {
|
|
4628
4702
|
if (existsSync8(destinationSkillFile)) {
|
|
4629
4703
|
return {
|
|
4630
|
-
message: `${slug} is already installed
|
|
4631
|
-
output: destination
|
|
4704
|
+
message: `${slug} is already installed`
|
|
4632
4705
|
};
|
|
4633
4706
|
}
|
|
4634
4707
|
return null;
|
|
4635
4708
|
}
|
|
4636
|
-
|
|
4637
|
-
|
|
4709
|
+
mkdirSync5(join5(workspace, "skills"), { recursive: true });
|
|
4710
|
+
cpSync2(dirname2(builtin.path), destination, { recursive: true, force: true });
|
|
4638
4711
|
return {
|
|
4639
|
-
message: `Installed skill: ${slug}
|
|
4640
|
-
output: `Copied builtin skill to ${destination}`
|
|
4712
|
+
message: `Installed skill: ${slug}`
|
|
4641
4713
|
};
|
|
4642
4714
|
}
|
|
4643
|
-
resolveGitSkillName(skill, source) {
|
|
4644
|
-
const fromRequest = typeof skill === "string" ? skill.trim() : "";
|
|
4645
|
-
if (fromRequest) {
|
|
4646
|
-
return this.validateSkillName(fromRequest);
|
|
4647
|
-
}
|
|
4648
|
-
const normalizedSource = source.replace(/[?#].*$/, "").replace(/\/+$/, "");
|
|
4649
|
-
const parts = normalizedSource.split("/").filter(Boolean);
|
|
4650
|
-
const inferred = parts.length > 0 ? parts[parts.length - 1] : "";
|
|
4651
|
-
if (!inferred) {
|
|
4652
|
-
throw new Error("Git skill install requires a specific skill name");
|
|
4653
|
-
}
|
|
4654
|
-
return this.validateSkillName(inferred);
|
|
4655
|
-
}
|
|
4656
|
-
validateSkillName(skillName) {
|
|
4657
|
-
if (!/^[A-Za-z0-9._-]+$/.test(skillName)) {
|
|
4658
|
-
throw new Error(`Invalid skill name: ${skillName}`);
|
|
4659
|
-
}
|
|
4660
|
-
return skillName;
|
|
4661
|
-
}
|
|
4662
|
-
resolveSkillInstallPath(workspace, installPath, skillName) {
|
|
4663
|
-
const requested = typeof installPath === "string" && installPath.trim().length > 0 ? installPath.trim() : join5("skills", skillName);
|
|
4664
|
-
if (isAbsolute2(requested)) {
|
|
4665
|
-
throw new Error("installPath must be relative to workspace");
|
|
4666
|
-
}
|
|
4667
|
-
const destination = resolve7(workspace, requested);
|
|
4668
|
-
const rel = relative(workspace, destination);
|
|
4669
|
-
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
4670
|
-
throw new Error("installPath escapes workspace");
|
|
4671
|
-
}
|
|
4672
|
-
return destination;
|
|
4673
|
-
}
|
|
4674
4715
|
mergeCommandOutput(stdout, stderr) {
|
|
4675
4716
|
return `${stdout}
|
|
4676
4717
|
${stderr}`.trim();
|
|
@@ -4682,22 +4723,6 @@ ${stderr}`.trim();
|
|
|
4682
4723
|
timeoutMs
|
|
4683
4724
|
}).then((result) => this.mergeCommandOutput(result.stdout, result.stderr));
|
|
4684
4725
|
}
|
|
4685
|
-
async runCommandWithFallback(commandCandidates, args, options = {}) {
|
|
4686
|
-
let lastError = null;
|
|
4687
|
-
for (const command of commandCandidates) {
|
|
4688
|
-
try {
|
|
4689
|
-
return await this.runCommand(command, args, options);
|
|
4690
|
-
} catch (error) {
|
|
4691
|
-
const message = String(error);
|
|
4692
|
-
lastError = error instanceof Error ? error : new Error(message);
|
|
4693
|
-
if (message.startsWith("failed to start command:")) {
|
|
4694
|
-
continue;
|
|
4695
|
-
}
|
|
4696
|
-
throw error;
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
throw lastError ?? new Error("failed to start command");
|
|
4700
|
-
}
|
|
4701
4726
|
runCommand(command, args, options = {}) {
|
|
4702
4727
|
const timeoutMs = options.timeoutMs ?? 18e4;
|
|
4703
4728
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
@@ -4735,19 +4760,15 @@ ${stderr}`.trim();
|
|
|
4735
4760
|
});
|
|
4736
4761
|
});
|
|
4737
4762
|
}
|
|
4738
|
-
pickLastOutputLine(output) {
|
|
4739
|
-
const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
4740
|
-
return lines.length > 0 ? lines[lines.length - 1] : null;
|
|
4741
|
-
}
|
|
4742
4763
|
};
|
|
4743
4764
|
|
|
4744
4765
|
// src/cli/workspace.ts
|
|
4745
|
-
import { cpSync as
|
|
4766
|
+
import { cpSync as cpSync3, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync8, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
4746
4767
|
import { createRequire } from "module";
|
|
4747
|
-
import { dirname as
|
|
4768
|
+
import { dirname as dirname3, join as join6, resolve as resolve8 } from "path";
|
|
4748
4769
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4749
4770
|
import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
|
|
4750
|
-
import { spawnSync as
|
|
4771
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
4751
4772
|
var WorkspaceManager = class {
|
|
4752
4773
|
constructor(logo) {
|
|
4753
4774
|
this.logo = logo;
|
|
@@ -4783,20 +4804,20 @@ var WorkspaceManager = class {
|
|
|
4783
4804
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
4784
4805
|
continue;
|
|
4785
4806
|
}
|
|
4786
|
-
const raw =
|
|
4807
|
+
const raw = readFileSync8(templatePath, "utf-8");
|
|
4787
4808
|
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
|
|
4788
|
-
|
|
4809
|
+
mkdirSync6(dirname3(filePath), { recursive: true });
|
|
4789
4810
|
writeFileSync5(filePath, content);
|
|
4790
4811
|
created.push(entry.target);
|
|
4791
4812
|
}
|
|
4792
4813
|
const memoryDir = join6(workspace, "memory");
|
|
4793
4814
|
if (!existsSync9(memoryDir)) {
|
|
4794
|
-
|
|
4815
|
+
mkdirSync6(memoryDir, { recursive: true });
|
|
4795
4816
|
created.push(join6("memory", ""));
|
|
4796
4817
|
}
|
|
4797
4818
|
const skillsDir = join6(workspace, "skills");
|
|
4798
4819
|
if (!existsSync9(skillsDir)) {
|
|
4799
|
-
|
|
4820
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
4800
4821
|
created.push(join6("skills", ""));
|
|
4801
4822
|
}
|
|
4802
4823
|
const seeded = this.seedBuiltinSkills(skillsDir, { force });
|
|
@@ -4812,7 +4833,7 @@ var WorkspaceManager = class {
|
|
|
4812
4833
|
}
|
|
4813
4834
|
const force = Boolean(options.force);
|
|
4814
4835
|
let seeded = 0;
|
|
4815
|
-
for (const entry of
|
|
4836
|
+
for (const entry of readdirSync3(sourceDir, { withFileTypes: true })) {
|
|
4816
4837
|
if (!entry.isDirectory()) {
|
|
4817
4838
|
continue;
|
|
4818
4839
|
}
|
|
@@ -4824,7 +4845,7 @@ var WorkspaceManager = class {
|
|
|
4824
4845
|
if (!force && existsSync9(dest)) {
|
|
4825
4846
|
continue;
|
|
4826
4847
|
}
|
|
4827
|
-
|
|
4848
|
+
cpSync3(src, dest, { recursive: true, force: true });
|
|
4828
4849
|
seeded += 1;
|
|
4829
4850
|
}
|
|
4830
4851
|
return seeded;
|
|
@@ -4833,7 +4854,7 @@ var WorkspaceManager = class {
|
|
|
4833
4854
|
try {
|
|
4834
4855
|
const require2 = createRequire(import.meta.url);
|
|
4835
4856
|
const entry = require2.resolve("@nextclaw/core");
|
|
4836
|
-
const pkgRoot = resolve8(
|
|
4857
|
+
const pkgRoot = resolve8(dirname3(entry), "..");
|
|
4837
4858
|
const distSkills = join6(pkgRoot, "dist", "skills");
|
|
4838
4859
|
if (existsSync9(distSkills)) {
|
|
4839
4860
|
return distSkills;
|
|
@@ -4886,15 +4907,15 @@ var WorkspaceManager = class {
|
|
|
4886
4907
|
process.exit(1);
|
|
4887
4908
|
}
|
|
4888
4909
|
console.log(`${this.logo} Setting up bridge...`);
|
|
4889
|
-
|
|
4910
|
+
mkdirSync6(resolve8(userBridge, ".."), { recursive: true });
|
|
4890
4911
|
if (existsSync9(userBridge)) {
|
|
4891
|
-
|
|
4912
|
+
rmSync5(userBridge, { recursive: true, force: true });
|
|
4892
4913
|
}
|
|
4893
|
-
|
|
4914
|
+
cpSync3(source, userBridge, {
|
|
4894
4915
|
recursive: true,
|
|
4895
4916
|
filter: (src) => !src.includes("node_modules") && !src.includes("dist")
|
|
4896
4917
|
});
|
|
4897
|
-
const install =
|
|
4918
|
+
const install = spawnSync3("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
|
|
4898
4919
|
if (install.status !== 0) {
|
|
4899
4920
|
console.error(`Bridge install failed: ${install.status ?? 1}`);
|
|
4900
4921
|
if (install.stderr) {
|
|
@@ -4902,7 +4923,7 @@ var WorkspaceManager = class {
|
|
|
4902
4923
|
}
|
|
4903
4924
|
process.exit(1);
|
|
4904
4925
|
}
|
|
4905
|
-
const build =
|
|
4926
|
+
const build = spawnSync3("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
|
|
4906
4927
|
if (build.status !== 0) {
|
|
4907
4928
|
console.error(`Bridge build failed: ${build.status ?? 1}`);
|
|
4908
4929
|
if (build.stderr) {
|
|
@@ -5155,7 +5176,7 @@ var CliRuntime = class {
|
|
|
5155
5176
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
5156
5177
|
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join7(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
5157
5178
|
const workspaceExisted = existsSync10(workspacePath);
|
|
5158
|
-
|
|
5179
|
+
mkdirSync7(workspacePath, { recursive: true });
|
|
5159
5180
|
const templateResult = this.workspaceManager.createWorkspaceTemplates(
|
|
5160
5181
|
workspacePath,
|
|
5161
5182
|
{ force }
|
|
@@ -5344,8 +5365,8 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5344
5365
|
);
|
|
5345
5366
|
const historyFile = join7(getDataDir8(), "history", "cli_history");
|
|
5346
5367
|
const historyDir = resolve9(historyFile, "..");
|
|
5347
|
-
|
|
5348
|
-
const history = existsSync10(historyFile) ?
|
|
5368
|
+
mkdirSync7(historyDir, { recursive: true });
|
|
5369
|
+
const history = existsSync10(historyFile) ? readFileSync9(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
5349
5370
|
const rl = createInterface2({
|
|
5350
5371
|
input: process.stdin,
|
|
5351
5372
|
output: process.stdout
|
|
@@ -5501,25 +5522,58 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5501
5522
|
}
|
|
5502
5523
|
async skillsInstall(options) {
|
|
5503
5524
|
const workdir = options.workdir ? expandHome2(options.workdir) : getWorkspacePath6();
|
|
5504
|
-
const result = await
|
|
5525
|
+
const result = await installMarketplaceSkill({
|
|
5505
5526
|
slug: options.slug,
|
|
5506
|
-
version: options.version,
|
|
5507
|
-
registry: options.registry,
|
|
5508
5527
|
workdir,
|
|
5509
5528
|
dir: options.dir,
|
|
5510
|
-
force: options.force
|
|
5529
|
+
force: options.force,
|
|
5530
|
+
apiBaseUrl: options.apiBaseUrl
|
|
5511
5531
|
});
|
|
5512
|
-
const versionLabel = result.version ?? "latest";
|
|
5513
5532
|
if (result.alreadyInstalled) {
|
|
5514
5533
|
console.log(`\u2713 ${result.slug} is already installed`);
|
|
5515
5534
|
} else {
|
|
5516
|
-
console.log(`\u2713 Installed ${result.slug}
|
|
5517
|
-
}
|
|
5518
|
-
if (result.registry) {
|
|
5519
|
-
console.log(` Registry: ${result.registry}`);
|
|
5535
|
+
console.log(`\u2713 Installed ${result.slug} (${result.source})`);
|
|
5520
5536
|
}
|
|
5521
5537
|
console.log(` Path: ${result.destinationDir}`);
|
|
5522
5538
|
}
|
|
5539
|
+
async skillsPublish(options) {
|
|
5540
|
+
const result = await publishMarketplaceSkill({
|
|
5541
|
+
skillDir: expandHome2(options.dir),
|
|
5542
|
+
slug: options.slug,
|
|
5543
|
+
name: options.name,
|
|
5544
|
+
summary: options.summary,
|
|
5545
|
+
description: options.description,
|
|
5546
|
+
author: options.author,
|
|
5547
|
+
tags: options.tag,
|
|
5548
|
+
sourceRepo: options.sourceRepo,
|
|
5549
|
+
homepage: options.homepage,
|
|
5550
|
+
publishedAt: options.publishedAt,
|
|
5551
|
+
updatedAt: options.updatedAt,
|
|
5552
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
5553
|
+
token: options.token
|
|
5554
|
+
});
|
|
5555
|
+
console.log(result.created ? `\u2713 Published new skill: ${result.slug}` : `\u2713 Updated skill: ${result.slug}`);
|
|
5556
|
+
console.log(` Files: ${result.fileCount}`);
|
|
5557
|
+
}
|
|
5558
|
+
async skillsUpdate(options) {
|
|
5559
|
+
const result = await publishMarketplaceSkill({
|
|
5560
|
+
skillDir: expandHome2(options.dir),
|
|
5561
|
+
slug: options.slug,
|
|
5562
|
+
name: options.name,
|
|
5563
|
+
summary: options.summary,
|
|
5564
|
+
description: options.description,
|
|
5565
|
+
author: options.author,
|
|
5566
|
+
tags: options.tag,
|
|
5567
|
+
sourceRepo: options.sourceRepo,
|
|
5568
|
+
homepage: options.homepage,
|
|
5569
|
+
updatedAt: options.updatedAt,
|
|
5570
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
5571
|
+
token: options.token,
|
|
5572
|
+
requireExisting: true
|
|
5573
|
+
});
|
|
5574
|
+
console.log(`\u2713 Updated skill: ${result.slug}`);
|
|
5575
|
+
console.log(` Files: ${result.fileCount}`);
|
|
5576
|
+
}
|
|
5523
5577
|
};
|
|
5524
5578
|
|
|
5525
5579
|
// src/cli/index.ts
|
|
@@ -5536,13 +5590,11 @@ program.command("serve").description(`Run the ${APP_NAME5} gateway + UI in the f
|
|
|
5536
5590
|
program.command("stop").description(`Stop the ${APP_NAME5} background service`).action(async () => runtime.stop());
|
|
5537
5591
|
program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--model <model>", "Session model override for this run").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
|
|
5538
5592
|
program.command("update").description(`Update ${APP_NAME5}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
|
|
5539
|
-
var registerClawHubInstall = (cmd) => {
|
|
5540
|
-
cmd.command("install <slug>").description("Install a skill from ClawHub").option("--version <version>", "Skill version (default: latest)").option("--registry <url>", "ClawHub registry 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 }));
|
|
5541
|
-
};
|
|
5542
5593
|
var skills = program.command("skills").description("Manage skills");
|
|
5543
|
-
|
|
5544
|
-
var
|
|
5545
|
-
|
|
5594
|
+
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 }));
|
|
5595
|
+
var withRepeatableTag = (value, previous = []) => [...previous, value];
|
|
5596
|
+
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 }));
|
|
5597
|
+
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 }));
|
|
5546
5598
|
var plugins = program.command("plugins").description("Manage OpenClaw-compatible plugins");
|
|
5547
5599
|
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));
|
|
5548
5600
|
plugins.command("info <id>").description("Show plugin details").option("--json", "Print JSON").action((id, opts) => runtime.pluginsInfo(id, opts));
|