@annals/agent-mesh 0.15.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +474 -114
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2562,7 +2562,7 @@ var ERROR_HINTS = {
|
|
|
2562
2562
|
agent_offline: "Agent must be online for first publish. Run `agent-mesh connect` first.",
|
|
2563
2563
|
email_required: "Email required. Visit https://agents.hot/settings to add one.",
|
|
2564
2564
|
github_required: "GitHub account required. Visit https://agents.hot/settings to link one.",
|
|
2565
|
-
validation_error: "Invalid input. Check your
|
|
2565
|
+
validation_error: "Invalid input. Check your SKILL.md frontmatter or command flags.",
|
|
2566
2566
|
permission_denied: "You don't have permission to modify this skill.",
|
|
2567
2567
|
file_too_large: "Package file exceeds the 50MB limit.",
|
|
2568
2568
|
subscription_required: "This is a private agent. Subscribe first: agent-mesh subscribe <author-login>"
|
|
@@ -2593,6 +2593,31 @@ var PlatformClient = class {
|
|
|
2593
2593
|
async del(path, body) {
|
|
2594
2594
|
return this.request("DELETE", path, body);
|
|
2595
2595
|
}
|
|
2596
|
+
async getRaw(path) {
|
|
2597
|
+
const url = `${this.baseUrl}${path}`;
|
|
2598
|
+
let res;
|
|
2599
|
+
try {
|
|
2600
|
+
res = await fetch(url, {
|
|
2601
|
+
method: "GET",
|
|
2602
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
2603
|
+
});
|
|
2604
|
+
} catch (err) {
|
|
2605
|
+
throw new PlatformApiError(0, "network_error", `Network error: ${err.message}`);
|
|
2606
|
+
}
|
|
2607
|
+
if (!res.ok) {
|
|
2608
|
+
let errorCode = "unknown";
|
|
2609
|
+
let message = `HTTP ${res.status}`;
|
|
2610
|
+
try {
|
|
2611
|
+
const data = await res.json();
|
|
2612
|
+
errorCode = data.error ?? errorCode;
|
|
2613
|
+
message = data.error_description ?? data.message ?? message;
|
|
2614
|
+
} catch {
|
|
2615
|
+
}
|
|
2616
|
+
const hint = ERROR_HINTS[errorCode];
|
|
2617
|
+
throw new PlatformApiError(res.status, errorCode, hint ?? message);
|
|
2618
|
+
}
|
|
2619
|
+
return res;
|
|
2620
|
+
}
|
|
2596
2621
|
async postFormData(path, formData) {
|
|
2597
2622
|
const url = `${this.baseUrl}${path}`;
|
|
2598
2623
|
let res;
|
|
@@ -2919,6 +2944,12 @@ async function asyncChat(opts) {
|
|
|
2919
2944
|
process.stderr.write(` done
|
|
2920
2945
|
`);
|
|
2921
2946
|
process.stdout.write((task.result || "") + "\n");
|
|
2947
|
+
if (task.attachments?.length) {
|
|
2948
|
+
for (const att of task.attachments) {
|
|
2949
|
+
process.stdout.write(`${GRAY}[file: ${att.name} -> ${att.url}]${RESET}
|
|
2950
|
+
`);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2922
2953
|
return;
|
|
2923
2954
|
}
|
|
2924
2955
|
if (task.status === "failed") {
|
|
@@ -3121,11 +3152,11 @@ function registerChatCommand(program2) {
|
|
|
3121
3152
|
}
|
|
3122
3153
|
|
|
3123
3154
|
// src/commands/skills.ts
|
|
3124
|
-
import { readFile as readFile4, writeFile as
|
|
3155
|
+
import { readFile as readFile4, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm } from "fs/promises";
|
|
3125
3156
|
import { join as join8, resolve, relative as relative4 } from "path";
|
|
3126
3157
|
|
|
3127
3158
|
// src/utils/skill-parser.ts
|
|
3128
|
-
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
3159
|
+
import { readFile as readFile3, writeFile as writeFile2, stat as stat2 } from "fs/promises";
|
|
3129
3160
|
import { join as join7 } from "path";
|
|
3130
3161
|
function parseSkillMd(raw) {
|
|
3131
3162
|
const trimmed = raw.trimStart();
|
|
@@ -3186,36 +3217,13 @@ function parseSkillMd(raw) {
|
|
|
3186
3217
|
return { frontmatter, content };
|
|
3187
3218
|
}
|
|
3188
3219
|
async function loadSkillManifest(dir) {
|
|
3189
|
-
const skillJsonPath = join7(dir, "skill.json");
|
|
3190
|
-
try {
|
|
3191
|
-
const raw = await readFile3(skillJsonPath, "utf-8");
|
|
3192
|
-
const data = JSON.parse(raw);
|
|
3193
|
-
if (!data.name) throw new Error("skill.json missing required field: name");
|
|
3194
|
-
if (!data.version) throw new Error("skill.json missing required field: version");
|
|
3195
|
-
return {
|
|
3196
|
-
name: data.name,
|
|
3197
|
-
version: data.version,
|
|
3198
|
-
description: data.description,
|
|
3199
|
-
main: data.main || "SKILL.md",
|
|
3200
|
-
category: data.category,
|
|
3201
|
-
tags: data.tags,
|
|
3202
|
-
author: data.author,
|
|
3203
|
-
source_url: data.source_url,
|
|
3204
|
-
private: data.private,
|
|
3205
|
-
files: data.files
|
|
3206
|
-
};
|
|
3207
|
-
} catch (err) {
|
|
3208
|
-
if (err.code !== "ENOENT") {
|
|
3209
|
-
throw err;
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3212
3220
|
const skillMdPath = join7(dir, "SKILL.md");
|
|
3213
3221
|
try {
|
|
3214
3222
|
const raw = await readFile3(skillMdPath, "utf-8");
|
|
3215
3223
|
const { frontmatter } = parseSkillMd(raw);
|
|
3216
3224
|
const name = frontmatter.name;
|
|
3217
3225
|
if (!name) {
|
|
3218
|
-
throw new Error('
|
|
3226
|
+
throw new Error('SKILL.md has no "name" in frontmatter');
|
|
3219
3227
|
}
|
|
3220
3228
|
return {
|
|
3221
3229
|
name,
|
|
@@ -3230,7 +3238,7 @@ async function loadSkillManifest(dir) {
|
|
|
3230
3238
|
};
|
|
3231
3239
|
} catch (err) {
|
|
3232
3240
|
if (err.code === "ENOENT") {
|
|
3233
|
-
throw new Error(`No
|
|
3241
|
+
throw new Error(`No SKILL.md found in ${dir}`);
|
|
3234
3242
|
}
|
|
3235
3243
|
throw err;
|
|
3236
3244
|
}
|
|
@@ -3243,9 +3251,33 @@ async function pathExists(p) {
|
|
|
3243
3251
|
return false;
|
|
3244
3252
|
}
|
|
3245
3253
|
}
|
|
3254
|
+
async function updateFrontmatterField(filePath, field, value) {
|
|
3255
|
+
const raw = await readFile3(filePath, "utf-8");
|
|
3256
|
+
const trimmed = raw.trimStart();
|
|
3257
|
+
if (!trimmed.startsWith("---")) {
|
|
3258
|
+
throw new Error("SKILL.md has no frontmatter block");
|
|
3259
|
+
}
|
|
3260
|
+
const endIdx = trimmed.indexOf("\n---", 3);
|
|
3261
|
+
if (endIdx === -1) {
|
|
3262
|
+
throw new Error("SKILL.md has no frontmatter block");
|
|
3263
|
+
}
|
|
3264
|
+
const yamlBlock = trimmed.slice(4, endIdx);
|
|
3265
|
+
const after = trimmed.slice(endIdx);
|
|
3266
|
+
const fieldRegex = new RegExp(`^(${field}\\s*:\\s*)(.*)$`, "m");
|
|
3267
|
+
if (fieldRegex.test(yamlBlock)) {
|
|
3268
|
+
const updated = yamlBlock.replace(fieldRegex, `$1${value}`);
|
|
3269
|
+
await writeFile2(filePath, `---
|
|
3270
|
+
${updated}${after}`);
|
|
3271
|
+
} else {
|
|
3272
|
+
const updated = `${yamlBlock}
|
|
3273
|
+
${field}: ${value}`;
|
|
3274
|
+
await writeFile2(filePath, `---
|
|
3275
|
+
${updated}${after}`);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3246
3278
|
|
|
3247
3279
|
// src/utils/zip.ts
|
|
3248
|
-
import { deflateRawSync } from "zlib";
|
|
3280
|
+
import { deflateRawSync, inflateRawSync } from "zlib";
|
|
3249
3281
|
function dosTime(date) {
|
|
3250
3282
|
return {
|
|
3251
3283
|
time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
|
|
@@ -3341,6 +3373,55 @@ function createZipBuffer(entries) {
|
|
|
3341
3373
|
chunks.push(eocd);
|
|
3342
3374
|
return Buffer.concat(chunks);
|
|
3343
3375
|
}
|
|
3376
|
+
function extractZipBuffer(buf) {
|
|
3377
|
+
const entries = [];
|
|
3378
|
+
let eocdOffset = -1;
|
|
3379
|
+
for (let i = buf.length - 22; i >= 0; i--) {
|
|
3380
|
+
if (buf.readUInt32LE(i) === 101010256) {
|
|
3381
|
+
eocdOffset = i;
|
|
3382
|
+
break;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
if (eocdOffset === -1) {
|
|
3386
|
+
throw new Error("Invalid ZIP: EOCD not found");
|
|
3387
|
+
}
|
|
3388
|
+
const entryCount = buf.readUInt16LE(eocdOffset + 10);
|
|
3389
|
+
const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
3390
|
+
let offset = centralDirOffset;
|
|
3391
|
+
for (let i = 0; i < entryCount; i++) {
|
|
3392
|
+
if (buf.readUInt32LE(offset) !== 33639248) {
|
|
3393
|
+
throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
|
|
3394
|
+
}
|
|
3395
|
+
const compressionMethod = buf.readUInt16LE(offset + 10);
|
|
3396
|
+
const compressedSize = buf.readUInt32LE(offset + 20);
|
|
3397
|
+
const uncompressedSize = buf.readUInt32LE(offset + 24);
|
|
3398
|
+
const nameLen = buf.readUInt16LE(offset + 28);
|
|
3399
|
+
const extraLen = buf.readUInt16LE(offset + 30);
|
|
3400
|
+
const commentLen = buf.readUInt16LE(offset + 32);
|
|
3401
|
+
const localHeaderOffset = buf.readUInt32LE(offset + 42);
|
|
3402
|
+
const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
|
|
3403
|
+
if (!name.endsWith("/")) {
|
|
3404
|
+
const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
|
|
3405
|
+
const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
|
|
3406
|
+
const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
3407
|
+
const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
|
|
3408
|
+
let data;
|
|
3409
|
+
if (compressionMethod === 0) {
|
|
3410
|
+
data = Buffer.from(compressedData);
|
|
3411
|
+
} else if (compressionMethod === 8) {
|
|
3412
|
+
data = inflateRawSync(compressedData);
|
|
3413
|
+
} else {
|
|
3414
|
+
throw new Error(`Unsupported compression method: ${compressionMethod}`);
|
|
3415
|
+
}
|
|
3416
|
+
if (data.length !== uncompressedSize) {
|
|
3417
|
+
throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
|
|
3418
|
+
}
|
|
3419
|
+
entries.push({ path: name, data });
|
|
3420
|
+
}
|
|
3421
|
+
offset += 46 + nameLen + extraLen + commentLen;
|
|
3422
|
+
}
|
|
3423
|
+
return entries;
|
|
3424
|
+
}
|
|
3344
3425
|
|
|
3345
3426
|
// src/commands/skills.ts
|
|
3346
3427
|
var slog = {
|
|
@@ -3373,31 +3454,34 @@ function outputError(error, message, hint) {
|
|
|
3373
3454
|
function resolveSkillDir(pathArg) {
|
|
3374
3455
|
return pathArg ? resolve(pathArg) : process.cwd();
|
|
3375
3456
|
}
|
|
3457
|
+
function parseSkillRef(ref) {
|
|
3458
|
+
if (!ref.includes("/")) {
|
|
3459
|
+
outputError("validation_error", `Invalid skill reference: "${ref}". Use author/slug format (e.g. kcsx/code-review)`);
|
|
3460
|
+
}
|
|
3461
|
+
const [authorLogin, slug] = ref.split("/", 2);
|
|
3462
|
+
if (!authorLogin || !slug) {
|
|
3463
|
+
outputError("validation_error", `Invalid skill reference: "${ref}". Use author/slug format (e.g. kcsx/code-review)`);
|
|
3464
|
+
}
|
|
3465
|
+
return { authorLogin, slug };
|
|
3466
|
+
}
|
|
3467
|
+
function skillApiPath(authorLogin, slug) {
|
|
3468
|
+
return `/api/skills/${encodeURIComponent(authorLogin)}/${encodeURIComponent(slug)}`;
|
|
3469
|
+
}
|
|
3470
|
+
async function resolveSkillsRootAsync(pathArg) {
|
|
3471
|
+
const projectRoot = pathArg ? resolve(pathArg) : process.cwd();
|
|
3472
|
+
const agentsDir = join8(projectRoot, ".agents", "skills");
|
|
3473
|
+
if (await pathExists(agentsDir)) {
|
|
3474
|
+
return { projectRoot, skillsDir: agentsDir, convention: "agents" };
|
|
3475
|
+
}
|
|
3476
|
+
const claudeDir = join8(projectRoot, ".claude", "skills");
|
|
3477
|
+
return { projectRoot, skillsDir: claudeDir, convention: "claude" };
|
|
3478
|
+
}
|
|
3376
3479
|
async function collectPackFiles(dir, manifest) {
|
|
3377
3480
|
const results = [];
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
const s = await stat3(fullPath);
|
|
3383
|
-
if (s.isDirectory()) {
|
|
3384
|
-
const sub = await walkDir(fullPath);
|
|
3385
|
-
for (const f of sub) {
|
|
3386
|
-
results.push(relative4(dir, f));
|
|
3387
|
-
}
|
|
3388
|
-
} else {
|
|
3389
|
-
results.push(pattern);
|
|
3390
|
-
}
|
|
3391
|
-
} catch {
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
} else {
|
|
3395
|
-
const all = await walkDir(dir);
|
|
3396
|
-
for (const f of all) {
|
|
3397
|
-
const rel = relative4(dir, f);
|
|
3398
|
-
if (rel === "skill.json") continue;
|
|
3399
|
-
results.push(rel);
|
|
3400
|
-
}
|
|
3481
|
+
const all = await walkDir(dir);
|
|
3482
|
+
for (const f of all) {
|
|
3483
|
+
const rel = relative4(dir, f);
|
|
3484
|
+
results.push(rel);
|
|
3401
3485
|
}
|
|
3402
3486
|
const mainFile = manifest.main || "SKILL.md";
|
|
3403
3487
|
if (!results.includes(mainFile)) {
|
|
@@ -3468,8 +3552,42 @@ function bumpVersion(current, bump) {
|
|
|
3468
3552
|
throw new Error(`Invalid bump type: ${bump}. Use major, minor, patch, or a version string.`);
|
|
3469
3553
|
}
|
|
3470
3554
|
}
|
|
3555
|
+
async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
|
|
3556
|
+
const meta = await client.get(skillApiPath(authorLogin, slug));
|
|
3557
|
+
const targetDir = join8(skillsDir, slug);
|
|
3558
|
+
await mkdir2(targetDir, { recursive: true });
|
|
3559
|
+
if (meta.has_files) {
|
|
3560
|
+
const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/download`);
|
|
3561
|
+
const arrayBuf = await res.arrayBuffer();
|
|
3562
|
+
const buf = Buffer.from(arrayBuf);
|
|
3563
|
+
const entries = extractZipBuffer(buf);
|
|
3564
|
+
for (const entry of entries) {
|
|
3565
|
+
const filePath = join8(targetDir, entry.path);
|
|
3566
|
+
const dir = join8(filePath, "..");
|
|
3567
|
+
await mkdir2(dir, { recursive: true });
|
|
3568
|
+
await writeFile3(filePath, entry.data);
|
|
3569
|
+
}
|
|
3570
|
+
return {
|
|
3571
|
+
slug,
|
|
3572
|
+
name: meta.name,
|
|
3573
|
+
version: meta.version || "1.0.0",
|
|
3574
|
+
files_count: entries.length
|
|
3575
|
+
};
|
|
3576
|
+
} else {
|
|
3577
|
+
const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/raw`);
|
|
3578
|
+
const content = await res.text();
|
|
3579
|
+
await writeFile3(join8(targetDir, "SKILL.md"), content);
|
|
3580
|
+
return {
|
|
3581
|
+
slug,
|
|
3582
|
+
name: meta.name,
|
|
3583
|
+
version: meta.version || "1.0.0",
|
|
3584
|
+
files_count: 1
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3471
3588
|
var SKILL_MD_TEMPLATE = `---
|
|
3472
3589
|
name: {{name}}
|
|
3590
|
+
description: "{{description}}"
|
|
3473
3591
|
version: 1.0.0
|
|
3474
3592
|
---
|
|
3475
3593
|
|
|
@@ -3481,62 +3599,31 @@ version: 1.0.0
|
|
|
3481
3599
|
|
|
3482
3600
|
Describe how to use this skill.
|
|
3483
3601
|
`;
|
|
3484
|
-
var SKILL_JSON_TEMPLATE = (name, description) => ({
|
|
3485
|
-
name,
|
|
3486
|
-
version: "1.0.0",
|
|
3487
|
-
description,
|
|
3488
|
-
main: "SKILL.md",
|
|
3489
|
-
category: "general",
|
|
3490
|
-
tags: [],
|
|
3491
|
-
files: ["SKILL.md"]
|
|
3492
|
-
});
|
|
3493
3602
|
function registerSkillsCommand(program2) {
|
|
3494
|
-
const skills = program2.command("skills").description("Manage skill packages (publish, pack, version)");
|
|
3603
|
+
const skills = program2.command("skills").description("Manage skill packages (publish, install, pack, version)");
|
|
3495
3604
|
skills.command("init [path]").description("Initialize a new skill project").option("--name <name>", "Skill name").option("--description <desc>", "Skill description").action(async (pathArg, opts) => {
|
|
3496
3605
|
try {
|
|
3497
3606
|
const dir = resolveSkillDir(pathArg);
|
|
3498
3607
|
await mkdir2(dir, { recursive: true });
|
|
3499
|
-
let name = opts.name;
|
|
3500
|
-
let description = opts.description || "";
|
|
3501
3608
|
const skillMdPath = join8(dir, "SKILL.md");
|
|
3502
|
-
const skillJsonPath = join8(dir, "skill.json");
|
|
3503
|
-
if (await pathExists(skillJsonPath)) {
|
|
3504
|
-
outputError("already_exists", "skill.json already exists in this directory");
|
|
3505
|
-
}
|
|
3506
3609
|
if (await pathExists(skillMdPath)) {
|
|
3507
3610
|
const raw = await readFile4(skillMdPath, "utf-8");
|
|
3508
3611
|
const { frontmatter } = parseSkillMd(raw);
|
|
3509
3612
|
if (frontmatter.name) {
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
const manifest2 = {
|
|
3513
|
-
name,
|
|
3514
|
-
version: frontmatter.version || "1.0.0",
|
|
3515
|
-
description,
|
|
3516
|
-
main: "SKILL.md",
|
|
3517
|
-
category: frontmatter.category || "general",
|
|
3518
|
-
tags: frontmatter.tags || [],
|
|
3519
|
-
author: frontmatter.author,
|
|
3520
|
-
source_url: frontmatter.source_url,
|
|
3521
|
-
files: ["SKILL.md"]
|
|
3522
|
-
};
|
|
3523
|
-
await writeFile2(skillJsonPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
3524
|
-
slog.info(`Migrated frontmatter from SKILL.md to skill.json`);
|
|
3525
|
-
outputJson({ success: true, path: skillJsonPath, migrated: true });
|
|
3613
|
+
slog.info(`SKILL.md already exists with name: ${frontmatter.name}`);
|
|
3614
|
+
outputJson({ success: true, exists: true, path: skillMdPath });
|
|
3526
3615
|
return;
|
|
3527
3616
|
}
|
|
3528
3617
|
}
|
|
3618
|
+
let name = opts.name;
|
|
3619
|
+
const description = opts.description || "";
|
|
3529
3620
|
if (!name) {
|
|
3530
3621
|
name = dir.split("/").pop()?.replace(/[^a-z0-9-]/gi, "-").toLowerCase() || "my-skill";
|
|
3531
3622
|
}
|
|
3532
|
-
const
|
|
3533
|
-
await
|
|
3534
|
-
if (!await pathExists(skillMdPath)) {
|
|
3535
|
-
const content = SKILL_MD_TEMPLATE.replace(/\{\{name\}\}/g, name).replace(/\{\{description\}\}/g, description || "A new skill.");
|
|
3536
|
-
await writeFile2(skillMdPath, content);
|
|
3537
|
-
}
|
|
3623
|
+
const content = SKILL_MD_TEMPLATE.replace(/\{\{name\}\}/g, name).replace(/\{\{description\}\}/g, description || "A new skill.");
|
|
3624
|
+
await writeFile3(skillMdPath, content);
|
|
3538
3625
|
slog.info(`Initialized skill: ${name}`);
|
|
3539
|
-
outputJson({ success: true, path:
|
|
3626
|
+
outputJson({ success: true, path: skillMdPath });
|
|
3540
3627
|
} catch (err) {
|
|
3541
3628
|
if (err instanceof Error && err.message.includes("already_exists")) throw err;
|
|
3542
3629
|
outputError("init_failed", err.message);
|
|
@@ -3548,7 +3635,7 @@ function registerSkillsCommand(program2) {
|
|
|
3548
3635
|
const manifest = await loadSkillManifest(dir);
|
|
3549
3636
|
const result = await packSkill(dir, manifest);
|
|
3550
3637
|
const outPath = join8(dir, result.filename);
|
|
3551
|
-
await
|
|
3638
|
+
await writeFile3(outPath, result.buffer);
|
|
3552
3639
|
slog.info(`Packed ${result.files.length} files \u2192 ${result.filename} (${result.size} bytes)`);
|
|
3553
3640
|
outputJson({
|
|
3554
3641
|
success: true,
|
|
@@ -3618,11 +3705,13 @@ function registerSkillsCommand(program2) {
|
|
|
3618
3705
|
const client = createClient();
|
|
3619
3706
|
const result = await client.postFormData("/api/skills/publish", formData);
|
|
3620
3707
|
slog.success(`Skill ${result.action}: ${manifest.name}`);
|
|
3708
|
+
const authorLogin = result.skill.author_login;
|
|
3709
|
+
const skillUrl = authorLogin ? `https://agents.hot/skills/${authorLogin}/${result.skill.slug}` : `https://agents.hot/skills/${result.skill.slug}`;
|
|
3621
3710
|
outputJson({
|
|
3622
3711
|
success: true,
|
|
3623
3712
|
action: result.action,
|
|
3624
3713
|
skill: result.skill,
|
|
3625
|
-
url:
|
|
3714
|
+
url: skillUrl
|
|
3626
3715
|
});
|
|
3627
3716
|
} catch (err) {
|
|
3628
3717
|
if (err instanceof PlatformApiError) {
|
|
@@ -3631,16 +3720,17 @@ function registerSkillsCommand(program2) {
|
|
|
3631
3720
|
outputError("publish_failed", err.message);
|
|
3632
3721
|
}
|
|
3633
3722
|
});
|
|
3634
|
-
skills.command("info <
|
|
3723
|
+
skills.command("info <ref>").description("View skill details (use author/slug format)").option("--human", "Human-readable output").action(async (ref, opts) => {
|
|
3635
3724
|
try {
|
|
3725
|
+
const { authorLogin, slug } = parseSkillRef(ref);
|
|
3636
3726
|
const client = createClient();
|
|
3637
|
-
const data = await client.get(
|
|
3727
|
+
const data = await client.get(skillApiPath(authorLogin, slug));
|
|
3638
3728
|
if (opts.human) {
|
|
3639
3729
|
console.log("");
|
|
3640
3730
|
console.log(` ${BOLD}${data.name}${RESET} v${data.version || "?"}`);
|
|
3641
3731
|
if (data.description) console.log(` ${data.description}`);
|
|
3642
|
-
console.log(` ${GRAY}
|
|
3643
|
-
console.log(` ${GRAY}author${RESET} ${data.author || "\u2014"}`);
|
|
3732
|
+
console.log(` ${GRAY}ref${RESET} ${authorLogin}/${data.slug}`);
|
|
3733
|
+
console.log(` ${GRAY}author${RESET} ${data.author_login || data.author || "\u2014"}`);
|
|
3644
3734
|
console.log(` ${GRAY}category${RESET} ${data.category || "\u2014"}`);
|
|
3645
3735
|
console.log(` ${GRAY}installs${RESET} ${data.installs ?? 0}`);
|
|
3646
3736
|
console.log(` ${GRAY}private${RESET} ${data.is_private ? "yes" : "no"}`);
|
|
@@ -3669,12 +3759,14 @@ function registerSkillsCommand(program2) {
|
|
|
3669
3759
|
const table = renderTable(
|
|
3670
3760
|
[
|
|
3671
3761
|
{ key: "name", label: "NAME", width: 24 },
|
|
3762
|
+
{ key: "author", label: "AUTHOR", width: 16 },
|
|
3672
3763
|
{ key: "version", label: "VERSION", width: 12 },
|
|
3673
3764
|
{ key: "installs", label: "INSTALLS", width: 12, align: "right" },
|
|
3674
3765
|
{ key: "private", label: "PRIVATE", width: 10 }
|
|
3675
3766
|
],
|
|
3676
3767
|
data.owned.map((s) => ({
|
|
3677
3768
|
name: s.name,
|
|
3769
|
+
author: s.author_login || s.author || "\u2014",
|
|
3678
3770
|
version: s.version || "\u2014",
|
|
3679
3771
|
installs: String(s.installs ?? 0),
|
|
3680
3772
|
private: s.is_private ? "yes" : `${GREEN}no${RESET}`
|
|
@@ -3692,7 +3784,7 @@ function registerSkillsCommand(program2) {
|
|
|
3692
3784
|
],
|
|
3693
3785
|
data.authorized.map((s) => ({
|
|
3694
3786
|
name: s.name,
|
|
3695
|
-
author: s.author || "\u2014",
|
|
3787
|
+
author: s.author_login || s.author || "\u2014",
|
|
3696
3788
|
version: s.version || "\u2014"
|
|
3697
3789
|
}))
|
|
3698
3790
|
);
|
|
@@ -3708,11 +3800,12 @@ function registerSkillsCommand(program2) {
|
|
|
3708
3800
|
outputError("list_failed", err.message);
|
|
3709
3801
|
}
|
|
3710
3802
|
});
|
|
3711
|
-
skills.command("unpublish <
|
|
3803
|
+
skills.command("unpublish <ref>").description("Unpublish a skill (use author/slug format)").action(async (ref) => {
|
|
3712
3804
|
try {
|
|
3805
|
+
const { authorLogin, slug } = parseSkillRef(ref);
|
|
3713
3806
|
const client = createClient();
|
|
3714
|
-
const result = await client.del(
|
|
3715
|
-
slog.success(`Skill unpublished: ${slug}`);
|
|
3807
|
+
const result = await client.del(skillApiPath(authorLogin, slug));
|
|
3808
|
+
slog.success(`Skill unpublished: ${authorLogin}/${slug}`);
|
|
3716
3809
|
outputJson(result);
|
|
3717
3810
|
} catch (err) {
|
|
3718
3811
|
if (err instanceof PlatformApiError) {
|
|
@@ -3724,16 +3817,15 @@ function registerSkillsCommand(program2) {
|
|
|
3724
3817
|
skills.command("version <bump> [path]").description("Bump skill version (patch | minor | major | x.y.z)").action(async (bump, pathArg) => {
|
|
3725
3818
|
try {
|
|
3726
3819
|
const dir = resolveSkillDir(pathArg);
|
|
3727
|
-
const
|
|
3728
|
-
if (!await pathExists(
|
|
3729
|
-
outputError("not_found", "No
|
|
3820
|
+
const skillMdPath = join8(dir, "SKILL.md");
|
|
3821
|
+
if (!await pathExists(skillMdPath)) {
|
|
3822
|
+
outputError("not_found", "No SKILL.md found. Run `agent-mesh skills init` first.");
|
|
3730
3823
|
}
|
|
3731
|
-
const raw = await readFile4(
|
|
3732
|
-
const
|
|
3733
|
-
const oldVersion =
|
|
3824
|
+
const raw = await readFile4(skillMdPath, "utf-8");
|
|
3825
|
+
const { frontmatter } = parseSkillMd(raw);
|
|
3826
|
+
const oldVersion = frontmatter.version || "0.0.0";
|
|
3734
3827
|
const newVersion = bumpVersion(oldVersion, bump);
|
|
3735
|
-
|
|
3736
|
-
await writeFile2(skillJsonPath, JSON.stringify(data, null, 2) + "\n");
|
|
3828
|
+
await updateFrontmatterField(skillMdPath, "version", newVersion);
|
|
3737
3829
|
slog.success(`${oldVersion} \u2192 ${newVersion}`);
|
|
3738
3830
|
outputJson({ success: true, old: oldVersion, new: newVersion });
|
|
3739
3831
|
} catch (err) {
|
|
@@ -3741,6 +3833,196 @@ function registerSkillsCommand(program2) {
|
|
|
3741
3833
|
outputError("version_failed", err.message);
|
|
3742
3834
|
}
|
|
3743
3835
|
});
|
|
3836
|
+
skills.command("install <ref> [path]").description("Install a skill from agents.hot (use author/slug format)").option("--force", "Overwrite if already installed").action(async (ref, pathArg, opts) => {
|
|
3837
|
+
try {
|
|
3838
|
+
const { authorLogin, slug } = parseSkillRef(ref);
|
|
3839
|
+
const { skillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
3840
|
+
const targetDir = join8(skillsDir, slug);
|
|
3841
|
+
if (await pathExists(targetDir)) {
|
|
3842
|
+
if (!opts.force) {
|
|
3843
|
+
outputError("already_installed", `Skill "${slug}" is already installed at ${targetDir}. Use --force to overwrite.`);
|
|
3844
|
+
}
|
|
3845
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
3846
|
+
}
|
|
3847
|
+
slog.info(`Installing ${authorLogin}/${slug}...`);
|
|
3848
|
+
const client = createClient();
|
|
3849
|
+
const result = await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
|
|
3850
|
+
slog.success(`Installed ${result.name} (${result.files_count} files)`);
|
|
3851
|
+
outputJson({
|
|
3852
|
+
success: true,
|
|
3853
|
+
skill: {
|
|
3854
|
+
author: authorLogin,
|
|
3855
|
+
slug: result.slug,
|
|
3856
|
+
name: result.name,
|
|
3857
|
+
version: result.version
|
|
3858
|
+
},
|
|
3859
|
+
installed_to: targetDir,
|
|
3860
|
+
files_count: result.files_count
|
|
3861
|
+
});
|
|
3862
|
+
} catch (err) {
|
|
3863
|
+
if (err instanceof PlatformApiError) {
|
|
3864
|
+
outputError(err.errorCode, err.message);
|
|
3865
|
+
}
|
|
3866
|
+
outputError("install_failed", err.message);
|
|
3867
|
+
}
|
|
3868
|
+
});
|
|
3869
|
+
skills.command("update [ref] [path]").description("Update installed skill(s) from agents.hot").action(async (ref, pathArg) => {
|
|
3870
|
+
try {
|
|
3871
|
+
const { skillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
3872
|
+
const client = createClient();
|
|
3873
|
+
const updated = [];
|
|
3874
|
+
const skipped = [];
|
|
3875
|
+
const failed = [];
|
|
3876
|
+
if (ref) {
|
|
3877
|
+
const { authorLogin, slug } = parseSkillRef(ref);
|
|
3878
|
+
const targetDir = join8(skillsDir, slug);
|
|
3879
|
+
if (!await pathExists(targetDir)) {
|
|
3880
|
+
outputError("not_installed", `Skill "${slug}" is not installed. Use "skills install ${ref}" first.`);
|
|
3881
|
+
}
|
|
3882
|
+
const skillMdPath = join8(targetDir, "SKILL.md");
|
|
3883
|
+
let localVersion = "0.0.0";
|
|
3884
|
+
if (await pathExists(skillMdPath)) {
|
|
3885
|
+
const raw = await readFile4(skillMdPath, "utf-8");
|
|
3886
|
+
const { frontmatter } = parseSkillMd(raw);
|
|
3887
|
+
localVersion = frontmatter.version || "0.0.0";
|
|
3888
|
+
}
|
|
3889
|
+
const remote = await client.get(skillApiPath(authorLogin, slug));
|
|
3890
|
+
const remoteVersion = remote.version || "0.0.0";
|
|
3891
|
+
if (remoteVersion === localVersion) {
|
|
3892
|
+
slog.info(`${slug} is already up to date (v${localVersion})`);
|
|
3893
|
+
skipped.push({ slug, reason: "up_to_date" });
|
|
3894
|
+
} else {
|
|
3895
|
+
slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
|
|
3896
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
3897
|
+
await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
|
|
3898
|
+
updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
|
|
3899
|
+
slog.success(`Updated ${slug} to v${remoteVersion}`);
|
|
3900
|
+
}
|
|
3901
|
+
} else {
|
|
3902
|
+
if (!await pathExists(skillsDir)) {
|
|
3903
|
+
outputError("no_skills_dir", `Skills directory not found: ${skillsDir}`);
|
|
3904
|
+
}
|
|
3905
|
+
const entries = await readdir2(skillsDir, { withFileTypes: true });
|
|
3906
|
+
for (const entry of entries) {
|
|
3907
|
+
if (!entry.isDirectory()) continue;
|
|
3908
|
+
const slug = entry.name;
|
|
3909
|
+
const skillMdPath = join8(skillsDir, slug, "SKILL.md");
|
|
3910
|
+
if (!await pathExists(skillMdPath)) {
|
|
3911
|
+
skipped.push({ slug, reason: "no_skill_md" });
|
|
3912
|
+
continue;
|
|
3913
|
+
}
|
|
3914
|
+
const raw = await readFile4(skillMdPath, "utf-8");
|
|
3915
|
+
const { frontmatter } = parseSkillMd(raw);
|
|
3916
|
+
const localVersion = frontmatter.version || "0.0.0";
|
|
3917
|
+
const authorLogin = frontmatter.author;
|
|
3918
|
+
if (!authorLogin) {
|
|
3919
|
+
skipped.push({ slug, reason: "no_author_in_frontmatter" });
|
|
3920
|
+
continue;
|
|
3921
|
+
}
|
|
3922
|
+
try {
|
|
3923
|
+
const remote = await client.get(skillApiPath(authorLogin, slug));
|
|
3924
|
+
const remoteVersion = remote.version || "0.0.0";
|
|
3925
|
+
if (remoteVersion === localVersion) {
|
|
3926
|
+
skipped.push({ slug, reason: "up_to_date" });
|
|
3927
|
+
} else {
|
|
3928
|
+
slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
|
|
3929
|
+
await rm(join8(skillsDir, slug), { recursive: true, force: true });
|
|
3930
|
+
await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
|
|
3931
|
+
updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
|
|
3932
|
+
}
|
|
3933
|
+
} catch (err) {
|
|
3934
|
+
failed.push({ slug, error: err.message });
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
slog.success(`Update complete: ${updated.length} updated, ${skipped.length} skipped, ${failed.length} failed`);
|
|
3939
|
+
outputJson({ success: true, updated, skipped, failed });
|
|
3940
|
+
} catch (err) {
|
|
3941
|
+
if (err instanceof PlatformApiError) {
|
|
3942
|
+
outputError(err.errorCode, err.message);
|
|
3943
|
+
}
|
|
3944
|
+
outputError("update_failed", err.message);
|
|
3945
|
+
}
|
|
3946
|
+
});
|
|
3947
|
+
skills.command("remove <slug> [path]").description("Remove a locally installed skill").action(async (slug, pathArg) => {
|
|
3948
|
+
try {
|
|
3949
|
+
const { skillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
3950
|
+
const targetDir = join8(skillsDir, slug);
|
|
3951
|
+
if (!await pathExists(targetDir)) {
|
|
3952
|
+
outputError("not_installed", `Skill "${slug}" is not installed at ${targetDir}`);
|
|
3953
|
+
}
|
|
3954
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
3955
|
+
slog.success(`Removed skill: ${slug}`);
|
|
3956
|
+
outputJson({ success: true, removed: slug, path: targetDir });
|
|
3957
|
+
} catch (err) {
|
|
3958
|
+
outputError("remove_failed", err.message);
|
|
3959
|
+
}
|
|
3960
|
+
});
|
|
3961
|
+
skills.command("installed [path]").description("List locally installed skills").option("--check-updates", "Check for available updates").option("--human", "Human-readable table output").action(async (pathArg, opts) => {
|
|
3962
|
+
try {
|
|
3963
|
+
const { skillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
3964
|
+
if (!await pathExists(skillsDir)) {
|
|
3965
|
+
if (opts.human) {
|
|
3966
|
+
slog.info(`No skills directory found at ${skillsDir}`);
|
|
3967
|
+
return;
|
|
3968
|
+
}
|
|
3969
|
+
outputJson({ skills_dir: skillsDir, skills: [] });
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
const entries = await readdir2(skillsDir, { withFileTypes: true });
|
|
3973
|
+
const skills2 = [];
|
|
3974
|
+
for (const entry of entries) {
|
|
3975
|
+
if (!entry.isDirectory()) continue;
|
|
3976
|
+
const slug = entry.name;
|
|
3977
|
+
const skillMdPath = join8(skillsDir, slug, "SKILL.md");
|
|
3978
|
+
if (!await pathExists(skillMdPath)) continue;
|
|
3979
|
+
const raw = await readFile4(skillMdPath, "utf-8");
|
|
3980
|
+
const { frontmatter } = parseSkillMd(raw);
|
|
3981
|
+
const skillInfo = {
|
|
3982
|
+
slug,
|
|
3983
|
+
name: frontmatter.name || slug,
|
|
3984
|
+
version: frontmatter.version || "0.0.0",
|
|
3985
|
+
author: frontmatter.author
|
|
3986
|
+
};
|
|
3987
|
+
if (opts.checkUpdates && skillInfo.author) {
|
|
3988
|
+
try {
|
|
3989
|
+
const client = createClient();
|
|
3990
|
+
const remote = await client.get(skillApiPath(skillInfo.author, slug));
|
|
3991
|
+
skillInfo.remote_version = remote.version || "0.0.0";
|
|
3992
|
+
skillInfo.has_update = skillInfo.remote_version !== skillInfo.version;
|
|
3993
|
+
} catch {
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
skills2.push(skillInfo);
|
|
3997
|
+
}
|
|
3998
|
+
if (opts.human) {
|
|
3999
|
+
if (skills2.length === 0) {
|
|
4000
|
+
slog.info("No skills installed.");
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
const columns = [
|
|
4004
|
+
{ key: "name", label: "NAME", width: 24 },
|
|
4005
|
+
{ key: "version", label: "VERSION", width: 12 },
|
|
4006
|
+
{ key: "author", label: "AUTHOR", width: 16 }
|
|
4007
|
+
];
|
|
4008
|
+
if (opts.checkUpdates) {
|
|
4009
|
+
columns.push({ key: "update", label: "UPDATE", width: 14 });
|
|
4010
|
+
}
|
|
4011
|
+
const rows = skills2.map((s) => ({
|
|
4012
|
+
name: s.name,
|
|
4013
|
+
version: s.version,
|
|
4014
|
+
author: s.author || "\u2014",
|
|
4015
|
+
update: s.has_update ? `${GREEN}${s.remote_version}${RESET}` : "\u2014"
|
|
4016
|
+
}));
|
|
4017
|
+
slog.banner("Installed Skills");
|
|
4018
|
+
console.log(renderTable(columns, rows));
|
|
4019
|
+
return;
|
|
4020
|
+
}
|
|
4021
|
+
outputJson({ skills_dir: skillsDir, skills: skills2 });
|
|
4022
|
+
} catch (err) {
|
|
4023
|
+
outputError("installed_failed", err.message);
|
|
4024
|
+
}
|
|
4025
|
+
});
|
|
3744
4026
|
}
|
|
3745
4027
|
|
|
3746
4028
|
// src/commands/discover.ts
|
|
@@ -3806,6 +4088,25 @@ function registerDiscoverCommand(program2) {
|
|
|
3806
4088
|
// src/commands/call.ts
|
|
3807
4089
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3808
4090
|
var DEFAULT_BASE_URL4 = "https://agents.hot";
|
|
4091
|
+
async function submitRating(baseUrl, token, agentId, callId, rating) {
|
|
4092
|
+
const res = await fetch(`${baseUrl}/api/agents/${agentId}/rate`, {
|
|
4093
|
+
method: "POST",
|
|
4094
|
+
headers: {
|
|
4095
|
+
Authorization: `Bearer ${token}`,
|
|
4096
|
+
"Content-Type": "application/json"
|
|
4097
|
+
},
|
|
4098
|
+
body: JSON.stringify({ call_id: callId, rating })
|
|
4099
|
+
});
|
|
4100
|
+
if (!res.ok) {
|
|
4101
|
+
let msg = `HTTP ${res.status}`;
|
|
4102
|
+
try {
|
|
4103
|
+
const body = await res.json();
|
|
4104
|
+
msg = body.message || body.error || msg;
|
|
4105
|
+
} catch {
|
|
4106
|
+
}
|
|
4107
|
+
throw new Error(msg);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
3809
4110
|
function sleep5(ms) {
|
|
3810
4111
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3811
4112
|
}
|
|
@@ -3878,15 +4179,30 @@ async function asyncCall(opts) {
|
|
|
3878
4179
|
}
|
|
3879
4180
|
const result = task.result || "";
|
|
3880
4181
|
if (opts.json) {
|
|
3881
|
-
console.log(JSON.stringify({
|
|
4182
|
+
console.log(JSON.stringify({
|
|
4183
|
+
call_id,
|
|
4184
|
+
request_id,
|
|
4185
|
+
status: "completed",
|
|
4186
|
+
result,
|
|
4187
|
+
...task.attachments?.length ? { attachments: task.attachments } : {},
|
|
4188
|
+
rate_hint: `POST /api/agents/${opts.id}/rate body: { call_id: "${call_id}", rating: 1-5 }`
|
|
4189
|
+
}));
|
|
3882
4190
|
} else {
|
|
3883
4191
|
process.stdout.write(result + "\n");
|
|
4192
|
+
if (task.attachments?.length) {
|
|
4193
|
+
for (const att of task.attachments) {
|
|
4194
|
+
log.info(` ${GRAY}File:${RESET} ${att.name} ${GRAY}${att.url}${RESET}`);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
3884
4197
|
}
|
|
3885
4198
|
if (opts.outputFile && result) {
|
|
3886
4199
|
writeFileSync2(opts.outputFile, result);
|
|
3887
4200
|
if (!opts.json) log.info(`Saved to ${opts.outputFile}`);
|
|
3888
4201
|
}
|
|
3889
|
-
|
|
4202
|
+
if (!opts.json) {
|
|
4203
|
+
log.info(`${GRAY}Rate this call: agent-mesh rate ${call_id} <1-5> --agent ${opts.id}${RESET}`);
|
|
4204
|
+
}
|
|
4205
|
+
return { callId: call_id };
|
|
3890
4206
|
}
|
|
3891
4207
|
if (task.status === "failed") {
|
|
3892
4208
|
if (!opts.json) {
|
|
@@ -3965,6 +4281,7 @@ async function streamCall(opts) {
|
|
|
3965
4281
|
let buffer = "";
|
|
3966
4282
|
let outputBuffer = "";
|
|
3967
4283
|
let inThinkingBlock = false;
|
|
4284
|
+
let callId = res.headers.get("X-Call-Id") || "";
|
|
3968
4285
|
while (true) {
|
|
3969
4286
|
const { done, value } = await reader.read();
|
|
3970
4287
|
if (done) break;
|
|
@@ -3975,6 +4292,9 @@ async function streamCall(opts) {
|
|
|
3975
4292
|
if (data === "[DONE]") continue;
|
|
3976
4293
|
try {
|
|
3977
4294
|
const event = JSON.parse(data);
|
|
4295
|
+
if (event.type === "start" && event.call_id) {
|
|
4296
|
+
callId = event.call_id;
|
|
4297
|
+
}
|
|
3978
4298
|
if (opts.json) {
|
|
3979
4299
|
console.log(JSON.stringify(event));
|
|
3980
4300
|
} else {
|
|
@@ -4035,10 +4355,14 @@ Error: ${event.message}
|
|
|
4035
4355
|
if (!opts.json) {
|
|
4036
4356
|
console.log("\n");
|
|
4037
4357
|
log.success("Call completed");
|
|
4358
|
+
if (callId) {
|
|
4359
|
+
log.info(`${GRAY}Rate this call: agent-mesh rate ${callId} <1-5> --agent ${opts.id}${RESET}`);
|
|
4360
|
+
}
|
|
4038
4361
|
}
|
|
4362
|
+
return { callId };
|
|
4039
4363
|
}
|
|
4040
4364
|
function registerCallCommand(program2) {
|
|
4041
|
-
program2.command("call <agent>").description("Call an agent on the A2A network (default: async polling)").requiredOption("--task <description>", "Task description").option("--input-file <path>", "Read file and append to task description").option("--output-file <path>", "Save response text to file").option("--stream", "Use SSE streaming instead of async polling").option("--json", "Output JSONL events").option("--timeout <seconds>", "Timeout in seconds", "300").action(async (agentInput, opts) => {
|
|
4365
|
+
program2.command("call <agent>").description("Call an agent on the A2A network (default: async polling)").requiredOption("--task <description>", "Task description").option("--input-file <path>", "Read file and append to task description").option("--output-file <path>", "Save response text to file").option("--stream", "Use SSE streaming instead of async polling").option("--json", "Output JSONL events").option("--timeout <seconds>", "Timeout in seconds", "300").option("--rate <rating>", "Rate the agent after call (1-5)", parseInt).action(async (agentInput, opts) => {
|
|
4042
4366
|
try {
|
|
4043
4367
|
const token = loadToken();
|
|
4044
4368
|
if (!token) {
|
|
@@ -4069,12 +4393,23 @@ ${content}`;
|
|
|
4069
4393
|
outputFile: opts.outputFile,
|
|
4070
4394
|
signal: abortController.signal
|
|
4071
4395
|
};
|
|
4396
|
+
let result;
|
|
4072
4397
|
if (opts.stream) {
|
|
4073
|
-
await streamCall(callOpts);
|
|
4398
|
+
result = await streamCall(callOpts);
|
|
4074
4399
|
} else {
|
|
4075
|
-
await asyncCall(callOpts);
|
|
4400
|
+
result = await asyncCall(callOpts);
|
|
4076
4401
|
}
|
|
4077
4402
|
clearTimeout(timer);
|
|
4403
|
+
if (opts.rate && result.callId) {
|
|
4404
|
+
try {
|
|
4405
|
+
await submitRating(DEFAULT_BASE_URL4, token, id, result.callId, opts.rate);
|
|
4406
|
+
if (!opts.json) {
|
|
4407
|
+
log.success(`Rated ${opts.rate}/5`);
|
|
4408
|
+
}
|
|
4409
|
+
} catch (rateErr) {
|
|
4410
|
+
log.warn(`Rating failed: ${rateErr.message}`);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4078
4413
|
} catch (err) {
|
|
4079
4414
|
if (err.name === "AbortError") {
|
|
4080
4415
|
log.error("Call timed out");
|
|
@@ -4374,6 +4709,30 @@ function registerRegisterCommand(program2) {
|
|
|
4374
4709
|
});
|
|
4375
4710
|
}
|
|
4376
4711
|
|
|
4712
|
+
// src/commands/rate.ts
|
|
4713
|
+
var DEFAULT_BASE_URL6 = "https://agents.hot";
|
|
4714
|
+
function registerRateCommand(program2) {
|
|
4715
|
+
program2.command("rate <call-id> <rating>").description("Rate a completed A2A call (1-5)").requiredOption("--agent <agent-id>", "Agent UUID that was called").action(async (callId, ratingStr, opts) => {
|
|
4716
|
+
const token = loadToken();
|
|
4717
|
+
if (!token) {
|
|
4718
|
+
log.error("Not authenticated. Run `agent-mesh login` first.");
|
|
4719
|
+
process.exit(1);
|
|
4720
|
+
}
|
|
4721
|
+
const rating = parseInt(ratingStr, 10);
|
|
4722
|
+
if (isNaN(rating) || rating < 1 || rating > 5) {
|
|
4723
|
+
log.error("Rating must be an integer between 1 and 5");
|
|
4724
|
+
process.exit(1);
|
|
4725
|
+
}
|
|
4726
|
+
try {
|
|
4727
|
+
await submitRating(DEFAULT_BASE_URL6, token, opts.agent, callId, rating);
|
|
4728
|
+
log.success(`Rated ${rating}/5 for call ${callId.slice(0, 8)}...`);
|
|
4729
|
+
} catch (err) {
|
|
4730
|
+
log.error(err.message);
|
|
4731
|
+
process.exit(1);
|
|
4732
|
+
}
|
|
4733
|
+
});
|
|
4734
|
+
}
|
|
4735
|
+
|
|
4377
4736
|
// src/index.ts
|
|
4378
4737
|
var require2 = createRequire(import.meta.url);
|
|
4379
4738
|
var { version } = require2("../package.json");
|
|
@@ -4402,4 +4761,5 @@ registerConfigCommand(program);
|
|
|
4402
4761
|
registerStatsCommand(program);
|
|
4403
4762
|
registerSubscribeCommand(program);
|
|
4404
4763
|
registerRegisterCommand(program);
|
|
4764
|
+
registerRateCommand(program);
|
|
4405
4765
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@annals/agent-mesh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "CLI bridge connecting local AI agents to the Agents.Hot platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@annals/bridge-protocol": "^0.
|
|
11
|
+
"@annals/bridge-protocol": "^0.2.0",
|
|
12
12
|
"commander": "^13.0.0",
|
|
13
13
|
"ws": "^8.18.0"
|
|
14
14
|
},
|