easyclaw-link 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +318 -363
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -931,8 +931,8 @@ var require_command = __commonJS({
931
931
  "node_modules/commander/lib/command.js"(exports2) {
932
932
  var EventEmitter = require("events").EventEmitter;
933
933
  var childProcess = require("child_process");
934
- var path8 = require("path");
935
- var fs11 = require("fs");
934
+ var path9 = require("path");
935
+ var fs12 = require("fs");
936
936
  var process2 = require("process");
937
937
  var { Argument: Argument2, humanReadableArgName } = require_argument();
938
938
  var { CommanderError: CommanderError2 } = require_error();
@@ -1764,12 +1764,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
1764
1764
  let launchWithNode = false;
1765
1765
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1766
1766
  function findFile(baseDir, baseName) {
1767
- const localBin = path8.resolve(baseDir, baseName);
1768
- if (fs11.existsSync(localBin))
1767
+ const localBin = path9.resolve(baseDir, baseName);
1768
+ if (fs12.existsSync(localBin))
1769
1769
  return localBin;
1770
- if (sourceExt.includes(path8.extname(baseName)))
1770
+ if (sourceExt.includes(path9.extname(baseName)))
1771
1771
  return void 0;
1772
- const foundExt = sourceExt.find((ext) => fs11.existsSync(`${localBin}${ext}`));
1772
+ const foundExt = sourceExt.find((ext) => fs12.existsSync(`${localBin}${ext}`));
1773
1773
  if (foundExt)
1774
1774
  return `${localBin}${foundExt}`;
1775
1775
  return void 0;
@@ -1781,23 +1781,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
1781
1781
  if (this._scriptPath) {
1782
1782
  let resolvedScriptPath;
1783
1783
  try {
1784
- resolvedScriptPath = fs11.realpathSync(this._scriptPath);
1784
+ resolvedScriptPath = fs12.realpathSync(this._scriptPath);
1785
1785
  } catch (err) {
1786
1786
  resolvedScriptPath = this._scriptPath;
1787
1787
  }
1788
- executableDir = path8.resolve(path8.dirname(resolvedScriptPath), executableDir);
1788
+ executableDir = path9.resolve(path9.dirname(resolvedScriptPath), executableDir);
1789
1789
  }
1790
1790
  if (executableDir) {
1791
1791
  let localFile = findFile(executableDir, executableFile);
1792
1792
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1793
- const legacyName = path8.basename(this._scriptPath, path8.extname(this._scriptPath));
1793
+ const legacyName = path9.basename(this._scriptPath, path9.extname(this._scriptPath));
1794
1794
  if (legacyName !== this._name) {
1795
1795
  localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1796
1796
  }
1797
1797
  }
1798
1798
  executableFile = localFile || executableFile;
1799
1799
  }
1800
- launchWithNode = sourceExt.includes(path8.extname(executableFile));
1800
+ launchWithNode = sourceExt.includes(path9.extname(executableFile));
1801
1801
  let proc;
1802
1802
  if (process2.platform !== "win32") {
1803
1803
  if (launchWithNode) {
@@ -2599,7 +2599,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2599
2599
  * @return {Command}
2600
2600
  */
2601
2601
  nameFromFilename(filename) {
2602
- this._name = path8.basename(filename, path8.extname(filename));
2602
+ this._name = path9.basename(filename, path9.extname(filename));
2603
2603
  return this;
2604
2604
  }
2605
2605
  /**
@@ -2613,10 +2613,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
2613
2613
  * @param {string} [path]
2614
2614
  * @return {string|null|Command}
2615
2615
  */
2616
- executableDir(path9) {
2617
- if (path9 === void 0)
2616
+ executableDir(path10) {
2617
+ if (path10 === void 0)
2618
2618
  return this._executableDir;
2619
- this._executableDir = path9;
2619
+ this._executableDir = path10;
2620
2620
  return this;
2621
2621
  }
2622
2622
  /**
@@ -2833,6 +2833,54 @@ var os = __toESM(require("os"));
2833
2833
  var CONFIG_DIR = path.join(os.homedir(), ".easyclaw-link");
2834
2834
  var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
2835
2835
  var BASE_URL = process.env.EASYCLAW_LINK_API || "https://easyclaw.link";
2836
+ var EXIT = {
2837
+ OK: 0,
2838
+ ERROR: 1,
2839
+ AUTH: 2,
2840
+ PERMISSION: 3,
2841
+ NOT_FOUND: 4,
2842
+ VALIDATION: 5,
2843
+ RATE_LIMIT: 6
2844
+ };
2845
+ function httpError(status, serverMessage) {
2846
+ const msg = serverMessage ? `: ${serverMessage}` : "";
2847
+ switch (status) {
2848
+ case 401:
2849
+ console.error(`\u274C \u8BA4\u8BC1\u5931\u8D25${msg}
2850
+ \u2192 \u8BF7\u5148\u8FD0\u884C: ecl login`);
2851
+ process.exit(EXIT.AUTH);
2852
+ case 403:
2853
+ console.error(`\u274C \u6743\u9650\u4E0D\u8DB3${msg}
2854
+ \u2192 \u5F53\u524D\u8D26\u53F7\u65E0\u6B64\u64CD\u4F5C\u6743\u9650\uFF0C\u68C0\u67E5\u8D26\u53F7\u89D2\u8272`);
2855
+ process.exit(EXIT.PERMISSION);
2856
+ case 404:
2857
+ console.error(`\u274C \u8D44\u6E90\u4E0D\u5B58\u5728${msg}
2858
+ \u2192 \u68C0\u67E5 ID/slug \u662F\u5426\u6B63\u786E`);
2859
+ process.exit(EXIT.NOT_FOUND);
2860
+ case 422:
2861
+ console.error(`\u274C \u8BF7\u6C42\u6821\u9A8C\u5931\u8D25${msg}
2862
+ \u2192 \u68C0\u67E5\u53C2\u6570\u683C\u5F0F`);
2863
+ process.exit(EXIT.VALIDATION);
2864
+ case 429:
2865
+ console.error(`\u274C \u89E6\u53D1\u9891\u7387\u9650\u5236${msg}
2866
+ \u2192 \u8BF7\u7A0D\u540E\u91CD\u8BD5`);
2867
+ process.exit(EXIT.RATE_LIMIT);
2868
+ default:
2869
+ console.error(`\u274C \u8BF7\u6C42\u5931\u8D25 (HTTP ${status})${msg}`);
2870
+ process.exit(EXIT.ERROR);
2871
+ }
2872
+ }
2873
+ async function assertOk(res) {
2874
+ if (res.ok)
2875
+ return;
2876
+ let serverMsg;
2877
+ try {
2878
+ const body = await res.clone().json();
2879
+ serverMsg = body.error || body.message;
2880
+ } catch {
2881
+ }
2882
+ httpError(res.status, serverMsg);
2883
+ }
2836
2884
  function readConfig() {
2837
2885
  try {
2838
2886
  if (!fs.existsSync(CONFIG_FILE))
@@ -2852,8 +2900,8 @@ function writeConfig(config) {
2852
2900
  function requireApiKey() {
2853
2901
  const cfg = readConfig();
2854
2902
  if (!cfg.apiKey) {
2855
- console.error("\u274C \u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C: npx easyclaw-link login");
2856
- process.exit(1);
2903
+ console.error("\u274C \u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C: ecl login");
2904
+ process.exit(EXIT.AUTH);
2857
2905
  }
2858
2906
  return cfg.apiKey;
2859
2907
  }
@@ -2878,10 +2926,10 @@ async function fetchWithRetry(url, options, maxRetries = 3) {
2878
2926
  // src/commands/login.ts
2879
2927
  function prompt(question) {
2880
2928
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2881
- return new Promise((resolve4) => {
2929
+ return new Promise((resolve5) => {
2882
2930
  rl.question(question, (answer) => {
2883
2931
  rl.close();
2884
- resolve4(answer.trim());
2932
+ resolve5(answer.trim());
2885
2933
  });
2886
2934
  });
2887
2935
  }
@@ -2970,10 +3018,7 @@ async function resolveId(apiKey, slug) {
2970
3018
  `${BASE_URL}/api/assets?author=me&limit=${PAGE_SIZE}&offset=${offset}`,
2971
3019
  { headers: { Authorization: `Bearer ${apiKey}` } }
2972
3020
  );
2973
- if (!res.ok) {
2974
- console.error(`\u274C \u67E5\u8BE2\u6280\u80FD\u5217\u8868\u5931\u8D25 (${res.status})`);
2975
- process.exit(1);
2976
- }
3021
+ await assertOk(res);
2977
3022
  const { assets } = await res.json();
2978
3023
  if (!assets || assets.length === 0)
2979
3024
  break;
@@ -3027,11 +3072,7 @@ async function publishAction(dir, options) {
3027
3072
  body: JSON.stringify({ content })
3028
3073
  }
3029
3074
  );
3030
- if (!res.ok) {
3031
- const body = await res.text();
3032
- console.error(`\u274C \u66F4\u65B0\u5931\u8D25 (${res.status}): ${body}`);
3033
- process.exit(1);
3034
- }
3075
+ await assertOk(res);
3035
3076
  const { asset } = await res.json();
3036
3077
  console.log(`\u2705 \u6280\u80FD\u5DF2\u66F4\u65B0`);
3037
3078
  console.log(`\u{1F517} ${BASE_URL}/market/${asset?.id ?? resolvedId}`);
@@ -3045,11 +3086,7 @@ async function publishAction(dir, options) {
3045
3086
  body: JSON.stringify({ title, content, description, category: "other" })
3046
3087
  }
3047
3088
  );
3048
- if (!res.ok) {
3049
- const body = await res.text();
3050
- console.error(`\u274C \u53D1\u5E03\u5931\u8D25 (${res.status}): ${body}`);
3051
- process.exit(1);
3052
- }
3089
+ await assertOk(res);
3053
3090
  const { asset } = await res.json();
3054
3091
  console.log(`\u2705 \u6280\u80FD\u53D1\u5E03\u6210\u529F\uFF01`);
3055
3092
  console.log(`\u{1F517} ${BASE_URL}/market/${asset?.id}`);
@@ -3066,11 +3103,7 @@ async function listAction() {
3066
3103
  `${BASE_URL}/api/assets?author=me`,
3067
3104
  { headers: { Authorization: `Bearer ${apiKey}` } }
3068
3105
  );
3069
- if (!res.ok) {
3070
- const body = await res.text();
3071
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status}): ${body}`);
3072
- process.exit(1);
3073
- }
3106
+ await assertOk(res);
3074
3107
  const { assets } = await res.json();
3075
3108
  if (!assets || assets.length === 0) {
3076
3109
  console.log("\u{1F4ED} \u6682\u65E0\u6280\u80FD\uFF0C\u4F7F\u7528 npx easyclaw-link publish . \u53D1\u5E03\u4F60\u7684\u7B2C\u4E00\u4E2A\u6280\u80FD");
@@ -3099,11 +3132,7 @@ async function whoamiAction() {
3099
3132
  const res = await fetch(`${BASE_URL}/api/auth/me`, {
3100
3133
  headers: { Authorization: `Bearer ${apiKey}` }
3101
3134
  });
3102
- if (!res.ok) {
3103
- const body = await res.text();
3104
- console.error(`\u274C \u8BF7\u6C42\u5931\u8D25 (${res.status}): ${body}`);
3105
- process.exit(1);
3106
- }
3135
+ await assertOk(res);
3107
3136
  const { user } = await res.json();
3108
3137
  if (!user) {
3109
3138
  console.error("\u274C \u672A\u767B\u5F55\u6216 API Key \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C npx easyclaw-link login");
@@ -3124,11 +3153,7 @@ async function creditsAction() {
3124
3153
  const res = await fetch(`${BASE_URL}/api/credits`, {
3125
3154
  headers: { Authorization: `Bearer ${apiKey}` }
3126
3155
  });
3127
- if (!res.ok) {
3128
- const body = await res.text();
3129
- console.error(`\u274C \u8BF7\u6C42\u5931\u8D25 (${res.status}): ${body}`);
3130
- process.exit(1);
3131
- }
3156
+ await assertOk(res);
3132
3157
  const data = await res.json();
3133
3158
  console.log(`\u{1F4B0} \u9F99\u867E\u5E01\u4F59\u989D: ${data.credits} \u{1F99E}`);
3134
3159
  console.log(`\u2B50 \u58F0\u671B: ${data.reputation} ${data.level.badge} ${data.level.name}
@@ -3157,11 +3182,7 @@ async function tasksAction() {
3157
3182
  const res = await fetch(`${BASE_URL}/api/bounties?status=open&sort=date_desc`, {
3158
3183
  headers: { Authorization: `Bearer ${apiKey}` }
3159
3184
  });
3160
- if (!res.ok) {
3161
- const body = await res.text();
3162
- console.error(`\u274C \u8BF7\u6C42\u5931\u8D25 (${res.status}): ${body}`);
3163
- process.exit(1);
3164
- }
3185
+ await assertOk(res);
3165
3186
  const data = await res.json();
3166
3187
  const bounties = data.bounties ?? [];
3167
3188
  if (bounties.length === 0) {
@@ -3213,10 +3234,10 @@ function prompt2(question) {
3213
3234
  input: process.stdin,
3214
3235
  output: process.stdout
3215
3236
  });
3216
- return new Promise((resolve4) => {
3237
+ return new Promise((resolve5) => {
3217
3238
  rl.question(question, (answer) => {
3218
3239
  rl.close();
3219
- resolve4(answer.trim());
3240
+ resolve5(answer.trim());
3220
3241
  });
3221
3242
  });
3222
3243
  }
@@ -3267,12 +3288,7 @@ ${bounty.description}
3267
3288
  },
3268
3289
  body: JSON.stringify({ content })
3269
3290
  });
3270
- if (!res.ok) {
3271
- const body = await res.json().catch(() => ({}));
3272
- const msg = typeof body.error === "string" ? body.error : `HTTP ${res.status}`;
3273
- console.error(`\u274C \u63D0\u4EA4\u5931\u8D25: ${msg}`);
3274
- process.exit(1);
3275
- }
3291
+ await assertOk(res);
3276
3292
  const { submission } = await res.json();
3277
3293
  console.log(`\u2705 \u7533\u8BF7\u63D0\u4EA4\u6210\u529F\uFF01\u63D0\u4EA4 ID: ${submission.id}`);
3278
3294
  console.log(`\u{1F517} \u67E5\u770B\u4EFB\u52A1: ${BASE_URL}/bounties/${id}`);
@@ -3293,10 +3309,7 @@ async function notificationsAction(options) {
3293
3309
  `${BASE_URL}/api/notifications?limit=${limit}`,
3294
3310
  { headers: { Authorization: `Bearer ${apiKey}` } }
3295
3311
  );
3296
- if (!res.ok) {
3297
- console.error(`\u274C \u83B7\u53D6\u901A\u77E5\u5931\u8D25 (${res.status})`);
3298
- process.exit(1);
3299
- }
3312
+ await assertOk(res);
3300
3313
  const data = await res.json();
3301
3314
  if (options.json) {
3302
3315
  console.log(JSON.stringify(data, null, 2));
@@ -3321,10 +3334,7 @@ async function notificationsReadAction(options) {
3321
3334
  Authorization: `Bearer ${apiKey}`
3322
3335
  }
3323
3336
  });
3324
- if (!res.ok) {
3325
- console.error(`\u274C \u6807\u8BB0\u5931\u8D25 (${res.status})`);
3326
- process.exit(1);
3327
- }
3337
+ await assertOk(res);
3328
3338
  const data = await res.json();
3329
3339
  if (options.json) {
3330
3340
  console.log(JSON.stringify(data, null, 2));
@@ -3339,10 +3349,7 @@ async function unreadAction(options) {
3339
3349
  const res = await fetchWithRetry(`${BASE_URL}/api/unread-counts`, {
3340
3350
  headers: { Authorization: `Bearer ${apiKey}` }
3341
3351
  });
3342
- if (!res.ok) {
3343
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3344
- process.exit(1);
3345
- }
3352
+ await assertOk(res);
3346
3353
  const data = await res.json();
3347
3354
  if (options.json) {
3348
3355
  console.log(JSON.stringify(data, null, 2));
@@ -3368,10 +3375,7 @@ async function statsAction(options) {
3368
3375
  const res = await fetchWithRetry(`${BASE_URL}/api/stats`, {
3369
3376
  headers: { Authorization: `Bearer ${apiKey}` }
3370
3377
  });
3371
- if (!res.ok) {
3372
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3373
- process.exit(1);
3374
- }
3378
+ await assertOk(res);
3375
3379
  const data = await res.json();
3376
3380
  if (options.json) {
3377
3381
  console.log(JSON.stringify(data, null, 2));
@@ -3392,10 +3396,7 @@ async function leaderboardAction(options) {
3392
3396
  const res = await fetchWithRetry(`${BASE_URL}/api/leaderboard?limit=${limit}`, {
3393
3397
  headers: { Authorization: `Bearer ${apiKey}` }
3394
3398
  });
3395
- if (!res.ok) {
3396
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3397
- process.exit(1);
3398
- }
3399
+ await assertOk(res);
3399
3400
  const data = await res.json();
3400
3401
  if (options.json) {
3401
3402
  console.log(JSON.stringify(data, null, 2));
@@ -3429,11 +3430,7 @@ async function profileUpdateAction(options) {
3429
3430
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
3430
3431
  body: JSON.stringify(body)
3431
3432
  });
3432
- if (!res.ok) {
3433
- const err = await res.json().catch(() => ({}));
3434
- console.error(`\u274C \u66F4\u65B0\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3435
- process.exit(1);
3436
- }
3433
+ await assertOk(res);
3437
3434
  const data = await res.json();
3438
3435
  if (options.json) {
3439
3436
  console.log(JSON.stringify(data, null, 2));
@@ -3449,73 +3446,189 @@ async function profileUpdateAction(options) {
3449
3446
  // src/commands/validate.ts
3450
3447
  var fs5 = __toESM(require("fs"));
3451
3448
  var path5 = __toESM(require("path"));
3452
- var SKILL_MD_NAMES = ["SKILL.md", "skill.md", "Skill.md"];
3453
- function parseSkillMeta(content) {
3454
- const meta = {};
3455
- const nameMatch = content.match(/^name:\s*(.+)$/m);
3456
- const descMatch = content.match(/^description:\s*(.+)$/m);
3457
- if (nameMatch)
3458
- meta.name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
3459
- if (descMatch)
3460
- meta.description = descMatch[1].trim().replace(/^["']|["']$/g, "");
3461
- return meta;
3462
- }
3463
- async function validateAction(dir) {
3464
- const target = path5.resolve(dir || ".");
3449
+ var VALID_CATEGORIES = ["system", "office", "creative", "data", "coding", "ai_agent", "tools", "other"];
3450
+ var SKILL_MD_MAX = 5e4;
3451
+ var SKILL_MD_WARN_MIN = 200;
3452
+ var SKILL_MD_WARN_MAX = 2e4;
3453
+ var DESC_MAX = 1e3;
3454
+ function validateSkillDir2(dir) {
3465
3455
  const errors = [];
3466
3456
  const warnings = [];
3457
+ const skillMdPath = path5.join(dir, "SKILL.md");
3458
+ const pkgPath = path5.join(dir, "package.json");
3459
+ if (!fs5.existsSync(skillMdPath)) {
3460
+ errors.push("SKILL.md \u4E0D\u5B58\u5728");
3461
+ } else {
3462
+ const content = fs5.readFileSync(skillMdPath, "utf-8");
3463
+ const trimmed = content.trim();
3464
+ if (!trimmed) {
3465
+ errors.push("SKILL.md \u4E3A\u7A7A");
3466
+ } else {
3467
+ if (!/^#\s+\S/m.test(content)) {
3468
+ errors.push("SKILL.md \u7F3A\u5C11 # \u6807\u9898\u884C\uFF08\u81F3\u5C11\u4E00\u884C\u4EE5 # \u5F00\u5934\uFF09");
3469
+ }
3470
+ if (content.length > SKILL_MD_MAX) {
3471
+ errors.push(`SKILL.md \u8D85\u8FC7 ${SKILL_MD_MAX} \u5B57\u7B26\uFF08\u5F53\u524D ${content.length}\uFF09\uFF0C\u540E\u7AEF\u4F1A\u62D2\u7EDD`);
3472
+ }
3473
+ if (trimmed.length < SKILL_MD_WARN_MIN) {
3474
+ warnings.push(`SKILL.md \u5185\u5BB9\u8F83\u77ED\uFF08${trimmed.length} \u5B57\u7B26\uFF0C\u5EFA\u8BAE \u2265${SKILL_MD_WARN_MIN}\uFF09`);
3475
+ }
3476
+ if (!/##?\s*(使用方法|用法|usage|how to use)/i.test(content)) {
3477
+ warnings.push('SKILL.md \u7F3A\u5C11"\u4F7F\u7528\u65B9\u6CD5"\u7AE0\u8282\uFF08Agent \u9700\u8981\u77E5\u9053\u5982\u4F55\u8C03\u7528\uFF09');
3478
+ }
3479
+ if (content.length > SKILL_MD_WARN_MAX) {
3480
+ warnings.push(`SKILL.md \u8D85\u8FC7 ${SKILL_MD_WARN_MAX} \u5B57\u7B26\uFF0C\u8D85\u957F\u6587\u6863\u5F71\u54CD Agent \u4E0A\u4E0B\u6587\u52A0\u8F7D`);
3481
+ }
3482
+ }
3483
+ }
3484
+ if (!fs5.existsSync(pkgPath)) {
3485
+ warnings.push("package.json \u4E0D\u5B58\u5728\uFF0C\u6280\u80FD\u6807\u9898\u5C06\u964D\u7EA7\u4E3A\u76EE\u5F55\u540D");
3486
+ } else {
3487
+ let pkg;
3488
+ try {
3489
+ pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
3490
+ } catch {
3491
+ errors.push("package.json JSON \u8BED\u6CD5\u9519\u8BEF");
3492
+ return { errors, warnings };
3493
+ }
3494
+ if (!pkg.name || typeof pkg.name === "string" && !pkg.name.trim()) {
3495
+ errors.push("package.json \u7684 name \u5B57\u6BB5\u4E3A\u7A7A");
3496
+ }
3497
+ if (typeof pkg.description === "string" && pkg.description.length > DESC_MAX) {
3498
+ errors.push(`package.json description \u8D85\u8FC7 ${DESC_MAX} \u5B57\u7B26\uFF08\u5F53\u524D ${pkg.description.length}\uFF09`);
3499
+ }
3500
+ if (pkg.category && !VALID_CATEGORIES.includes(pkg.category)) {
3501
+ warnings.push(`package.json category "${pkg.category}" \u4E0D\u5728\u6709\u6548\u5217\u8868\u5185\uFF0C\u5C06\u964D\u7EA7\u4E3A "other"\uFF08\u6709\u6548\u503C: ${VALID_CATEGORIES.join(", ")}\uFF09`);
3502
+ }
3503
+ }
3504
+ return { errors, warnings };
3505
+ }
3506
+ async function validateAction(dir, options) {
3507
+ const target = path5.resolve(dir || ".");
3467
3508
  if (!fs5.existsSync(target) || !fs5.statSync(target).isDirectory()) {
3468
3509
  console.error(`\u274C \u76EE\u5F55\u4E0D\u5B58\u5728: ${target}`);
3469
- process.exit(1);
3510
+ process.exit(EXIT.NOT_FOUND);
3470
3511
  }
3471
- const skillMdPath = SKILL_MD_NAMES.map((n) => path5.join(target, n)).find((p) => fs5.existsSync(p));
3472
- if (!skillMdPath) {
3473
- errors.push("\u7F3A\u5C11 SKILL.md \u6587\u4EF6");
3474
- } else {
3475
- const content = fs5.readFileSync(skillMdPath, "utf-8");
3476
- const meta = parseSkillMeta(content);
3477
- if (!meta.name)
3478
- warnings.push("SKILL.md \u7F3A\u5C11 name \u5B57\u6BB5\uFF08\u5EFA\u8BAE\u5728 frontmatter \u4E2D\u6DFB\u52A0\uFF09");
3479
- if (!meta.description)
3480
- warnings.push("SKILL.md \u7F3A\u5C11 description \u5B57\u6BB5");
3481
- if (content.length < 50)
3482
- warnings.push("SKILL.md \u5185\u5BB9\u8FC7\u77ED\uFF08\u5C11\u4E8E 50 \u5B57\u7B26\uFF09\uFF0C\u5EFA\u8BAE\u8865\u5145\u8BF4\u660E");
3483
- if (content.length > 5e4)
3484
- warnings.push(`SKILL.md \u8FC7\u5927\uFF08${Math.round(content.length / 1024)}KB\uFF09\uFF0C\u5EFA\u8BAE\u7CBE\u7B80`);
3512
+ const { errors, warnings } = validateSkillDir2(target);
3513
+ const passed = errors.length === 0;
3514
+ if (options.json) {
3515
+ console.log(JSON.stringify({ path: target, passed, errors, warnings }, null, 2));
3516
+ if (!passed)
3517
+ process.exit(EXIT.VALIDATION);
3518
+ return;
3485
3519
  }
3486
- if (errors.length) {
3487
- console.log(`\u274C \u9A8C\u8BC1\u5931\u8D25 (${errors.length} \u4E2A\u9519\u8BEF)
3520
+ console.log(`\u{1F50D} \u6821\u9A8C\u76EE\u5F55: ${target}
3488
3521
  `);
3489
- errors.forEach((e) => console.log(` \u{1F534} ${e}`));
3522
+ if (errors.length) {
3523
+ console.log("\u274C \u9519\u8BEF\uFF08\u963B\u6B62\u53D1\u5E03\uFF09:");
3524
+ for (const e of errors)
3525
+ console.log(` \u2022 ${e}`);
3526
+ }
3527
+ if (warnings.length) {
3528
+ if (errors.length)
3529
+ console.log("");
3530
+ console.log("\u{1F7E1} \u8B66\u544A\uFF08\u5EFA\u8BAE\u4FEE\u590D\uFF09:");
3531
+ for (const w of warnings)
3532
+ console.log(` \u2022 ${w}`);
3533
+ }
3534
+ if (passed) {
3535
+ const warnSuffix = warnings.length ? `\uFF0C${warnings.length} \u4E2A\u8B66\u544A` : "";
3536
+ console.log(`
3537
+ \u2705 \u6821\u9A8C\u901A\u8FC7${warnSuffix}`);
3490
3538
  } else {
3491
- console.log(`\u2705 \u9A8C\u8BC1\u901A\u8FC7${warnings.length ? ` (${warnings.length} \u4E2A\u8B66\u544A)` : ""}
3492
- `);
3539
+ console.log(`
3540
+ \u274C \u6821\u9A8C\u5931\u8D25\uFF08${errors.length} \u4E2A\u9519\u8BEF\uFF09`);
3541
+ process.exit(EXIT.VALIDATION);
3542
+ }
3543
+ }
3544
+
3545
+ // src/commands/init.ts
3546
+ var fs6 = __toESM(require("fs"));
3547
+ var path6 = __toESM(require("path"));
3548
+ var SKILL_MD_TEMPLATE = (name) => `# ${name}
3549
+
3550
+ \u4E00\u53E5\u8BDD\u8BF4\u660E\u8FD9\u4E2A\u6280\u80FD\u7684\u7528\u9014\u548C\u9002\u7528\u573A\u666F\u3002
3551
+
3552
+ ## \u529F\u80FD
3553
+
3554
+ - \u529F\u80FD\u70B9 1
3555
+ - \u529F\u80FD\u70B9 2
3556
+ - \u529F\u80FD\u70B9 3
3557
+
3558
+ ## \u4F7F\u7528\u65B9\u6CD5
3559
+
3560
+ \u63CF\u8FF0 Agent \u5982\u4F55\u8C03\u7528\u8FD9\u4E2A\u6280\u80FD\uFF0C\u5305\u62EC\uFF1A
3561
+ - \u9700\u8981\u63D0\u4F9B\u54EA\u4E9B\u8F93\u5165
3562
+ - \u4F1A\u8FD4\u56DE\u4EC0\u4E48\u8F93\u51FA
3563
+ - \u6709\u54EA\u4E9B\u9650\u5236\u6216\u524D\u7F6E\u6761\u4EF6
3564
+
3565
+ ## \u793A\u4F8B
3566
+
3567
+ **\u8F93\u5165\uFF1A**
3568
+
3569
+ \u793A\u4F8B\u8F93\u5165\u5185\u5BB9
3570
+
3571
+ **\u8F93\u51FA\uFF1A**
3572
+
3573
+ \u793A\u4F8B\u8F93\u51FA\u5185\u5BB9
3574
+
3575
+ ## \u6CE8\u610F\u4E8B\u9879
3576
+
3577
+ - \u6CE8\u610F\u4E8B\u9879 1
3578
+ - \u6CE8\u610F\u4E8B\u9879 2
3579
+ `;
3580
+ var PKG_TEMPLATE = (name) => JSON.stringify({
3581
+ name,
3582
+ version: "1.0.0",
3583
+ description: "\u8BF7\u586B\u5199\u6280\u80FD\u63CF\u8FF0"
3584
+ }, null, 2) + "\n";
3585
+ async function skillInitAction(name, options) {
3586
+ if (!/^[a-z0-9-]+$/.test(name)) {
3587
+ console.error("\u274C \u6280\u80FD\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26\uFF0C\u4F8B\u5982: my-skill");
3588
+ process.exit(EXIT.VALIDATION);
3589
+ }
3590
+ const dir = path6.resolve(name);
3591
+ if (fs6.existsSync(dir)) {
3592
+ if (!options.force) {
3593
+ console.error(`\u274C \u76EE\u5F55\u5DF2\u5B58\u5728: ${dir}
3594
+ \u4F7F\u7528 --force \u8986\u76D6`);
3595
+ process.exit(EXIT.VALIDATION);
3596
+ }
3597
+ } else {
3598
+ fs6.mkdirSync(dir, { recursive: true });
3493
3599
  }
3600
+ fs6.writeFileSync(path6.join(dir, "SKILL.md"), SKILL_MD_TEMPLATE(name), "utf-8");
3601
+ fs6.writeFileSync(path6.join(dir, "package.json"), PKG_TEMPLATE(name), "utf-8");
3602
+ console.log(`\u2705 \u6280\u80FD\u76EE\u5F55\u5DF2\u521B\u5EFA: ${dir}
3603
+ `);
3604
+ console.log(" \u{1F4C4} SKILL.md \u2014 \u586B\u5199\u6280\u80FD\u8BF4\u660E\u6587\u6863");
3605
+ console.log(" \u{1F4E6} package.json \u2014 \u586B\u5199 name / description\n");
3606
+ console.log("\u63A5\u4E0B\u6765\uFF1A");
3607
+ console.log(` 1. cd ${name}`);
3608
+ console.log(" 2. \u7F16\u8F91 SKILL.md \u548C package.json");
3609
+ console.log(" 3. ecl validate # \u53D1\u5E03\u524D\u6821\u9A8C");
3610
+ console.log(" 4. ecl publish # \u53D1\u5E03\u5230\u5E73\u53F0");
3611
+ console.log("\n\u521D\u59CB\u6821\u9A8C\uFF1A");
3612
+ const { errors, warnings } = validateSkillDir2(dir);
3494
3613
  if (warnings.length) {
3495
- warnings.forEach((w) => console.log(` \u{1F7E1} ${w}`));
3614
+ for (const w of warnings)
3615
+ console.log(` \u{1F7E1} ${w}`);
3496
3616
  }
3497
3617
  if (!errors.length) {
3498
- const skillMdContent = skillMdPath ? fs5.readFileSync(skillMdPath, "utf-8") : "";
3499
- const meta = parseSkillMeta(skillMdContent);
3500
- if (meta.name)
3501
- console.log(`
3502
- \u6280\u80FD\u540D\u79F0: ${meta.name}`);
3503
- if (meta.description)
3504
- console.log(`\u63CF\u8FF0: ${meta.description}`);
3505
- }
3506
- process.exit(errors.length ? 1 : 0);
3618
+ console.log(" \u2705 \u6A21\u677F\u7ED3\u6784\u5408\u6CD5\uFF0C\u586B\u597D\u5185\u5BB9\u5373\u53EF\u53D1\u5E03");
3619
+ }
3507
3620
  }
3508
3621
 
3509
3622
  // src/commands/skill.ts
3510
- var fs6 = __toESM(require("fs"));
3511
- var path6 = __toESM(require("path"));
3623
+ var fs7 = __toESM(require("fs"));
3624
+ var path7 = __toESM(require("path"));
3512
3625
  var readline3 = __toESM(require("readline"));
3513
3626
  function promptYN(question) {
3514
3627
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
3515
- return new Promise((resolve4) => {
3628
+ return new Promise((resolve5) => {
3516
3629
  rl.question(question, (ans) => {
3517
3630
  rl.close();
3518
- resolve4(ans.trim().toLowerCase() === "y" || ans.trim().toLowerCase() === "yes");
3631
+ resolve5(ans.trim().toLowerCase() === "y" || ans.trim().toLowerCase() === "yes");
3519
3632
  });
3520
3633
  });
3521
3634
  }
@@ -3524,13 +3637,7 @@ async function skillViewAction(id, options) {
3524
3637
  const res = await fetchWithRetry(`${BASE_URL}/api/assets/${id}`, {
3525
3638
  headers: { Authorization: `Bearer ${apiKey}` }
3526
3639
  });
3527
- if (!res.ok) {
3528
- if (res.status === 404)
3529
- console.error(`\u274C \u6280\u80FD #${id} \u4E0D\u5B58\u5728`);
3530
- else
3531
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3532
- process.exit(1);
3533
- }
3640
+ await assertOk(res);
3534
3641
  const data = await res.json();
3535
3642
  const asset = data.asset ?? data;
3536
3643
  if (options.json) {
@@ -3565,15 +3672,7 @@ async function skillDeleteAction(id, options) {
3565
3672
  method: "DELETE",
3566
3673
  headers: { Authorization: `Bearer ${apiKey}` }
3567
3674
  });
3568
- if (!res.ok) {
3569
- if (res.status === 404)
3570
- console.error(`\u274C \u6280\u80FD #${id} \u4E0D\u5B58\u5728`);
3571
- else if (res.status === 403)
3572
- console.error("\u274C \u65E0\u6743\u9650\u5220\u9664\u6B64\u6280\u80FD");
3573
- else
3574
- console.error(`\u274C \u5220\u9664\u5931\u8D25 (${res.status})`);
3575
- process.exit(1);
3576
- }
3675
+ await assertOk(res);
3577
3676
  if (options.json) {
3578
3677
  console.log(JSON.stringify({ success: true, id }));
3579
3678
  return;
@@ -3585,21 +3684,15 @@ async function skillDownloadAction(id, options) {
3585
3684
  const res = await fetchWithRetry(`${BASE_URL}/api/assets/${id}/download`, {
3586
3685
  headers: { Authorization: `Bearer ${apiKey}` }
3587
3686
  });
3588
- if (!res.ok) {
3589
- if (res.status === 404)
3590
- console.error(`\u274C \u6280\u80FD #${id} \u4E0D\u5B58\u5728`);
3591
- else
3592
- console.error(`\u274C \u4E0B\u8F7D\u5931\u8D25 (${res.status})`);
3593
- process.exit(1);
3594
- }
3687
+ await assertOk(res);
3595
3688
  const buffer = Buffer.from(await res.arrayBuffer());
3596
3689
  const outPath = options.out || `skill-${id}.zip`;
3597
- const resolved = path6.resolve(outPath);
3598
- const dir = path6.dirname(resolved);
3599
- if (!fs6.existsSync(dir)) {
3600
- fs6.mkdirSync(dir, { recursive: true });
3690
+ const resolved = path7.resolve(outPath);
3691
+ const dir = path7.dirname(resolved);
3692
+ if (!fs7.existsSync(dir)) {
3693
+ fs7.mkdirSync(dir, { recursive: true });
3601
3694
  }
3602
- fs6.writeFileSync(resolved, buffer);
3695
+ fs7.writeFileSync(resolved, buffer);
3603
3696
  if (options.json) {
3604
3697
  console.log(JSON.stringify({ success: true, path: resolved, size: buffer.length }));
3605
3698
  return;
@@ -3612,10 +3705,7 @@ async function skillStarAction(id, options) {
3612
3705
  method: "POST",
3613
3706
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3614
3707
  });
3615
- if (!res.ok) {
3616
- console.error(`\u274C \u64CD\u4F5C\u5931\u8D25 (${res.status})`);
3617
- process.exit(1);
3618
- }
3708
+ await assertOk(res);
3619
3709
  const data = await res.json();
3620
3710
  if (options.json) {
3621
3711
  console.log(JSON.stringify(data, null, 2));
@@ -3630,10 +3720,7 @@ async function skillUseAction(id, options) {
3630
3720
  method: "POST",
3631
3721
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3632
3722
  });
3633
- if (!res.ok) {
3634
- console.error(`\u274C \u64CD\u4F5C\u5931\u8D25 (${res.status})`);
3635
- process.exit(1);
3636
- }
3723
+ await assertOk(res);
3637
3724
  const data = await res.json();
3638
3725
  if (options.json) {
3639
3726
  console.log(JSON.stringify(data, null, 2));
@@ -3644,19 +3731,20 @@ async function skillUseAction(id, options) {
3644
3731
  async function skillSearchAction(keyword, options) {
3645
3732
  if (!keyword?.trim()) {
3646
3733
  console.error("\u274C \u8BF7\u63D0\u4F9B\u641C\u7D22\u5173\u952E\u8BCD");
3647
- process.exit(1);
3734
+ process.exit(EXIT.VALIDATION);
3648
3735
  }
3649
3736
  const apiKey = requireApiKey();
3650
3737
  const parsed = parseInt(options.limit || "20", 10);
3651
3738
  const limit = isNaN(parsed) || parsed <= 0 ? 20 : Math.min(parsed, 50);
3652
3739
  const qs = new URLSearchParams({ q: keyword, limit: String(limit) });
3740
+ if (options.category)
3741
+ qs.set("category", options.category);
3742
+ if (options.grade)
3743
+ qs.set("grade", options.grade.toUpperCase());
3653
3744
  const res = await fetchWithRetry(`${BASE_URL}/api/assets?${qs}`, {
3654
3745
  headers: { Authorization: `Bearer ${apiKey}` }
3655
3746
  });
3656
- if (!res.ok) {
3657
- console.error(`\u274C \u641C\u7D22\u5931\u8D25 (${res.status})`);
3658
- process.exit(1);
3659
- }
3747
+ await assertOk(res);
3660
3748
  const data = await res.json();
3661
3749
  if (options.json) {
3662
3750
  console.log(JSON.stringify(data, null, 2));
@@ -3681,7 +3769,7 @@ async function skillSearchAction(keyword, options) {
3681
3769
  }
3682
3770
 
3683
3771
  // src/commands/bounty.ts
3684
- var fs7 = __toESM(require("fs"));
3772
+ var fs8 = __toESM(require("fs"));
3685
3773
  async function bountyListAction(options) {
3686
3774
  const apiKey = requireApiKey();
3687
3775
  const status = options.status || "open";
@@ -3691,10 +3779,7 @@ async function bountyListAction(options) {
3691
3779
  `${BASE_URL}/api/bounties?status=${status}&limit=${limit}&sort=date_desc`,
3692
3780
  { headers: { Authorization: `Bearer ${apiKey}` } }
3693
3781
  );
3694
- if (!res.ok) {
3695
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3696
- process.exit(1);
3697
- }
3782
+ await assertOk(res);
3698
3783
  const data = await res.json();
3699
3784
  if (options.json) {
3700
3785
  console.log(JSON.stringify(data, null, 2));
@@ -3722,10 +3807,7 @@ async function bountyMineAction(options) {
3722
3807
  const res = await fetchWithRetry(`${BASE_URL}/api/bounties?mine=true`, {
3723
3808
  headers: { Authorization: `Bearer ${apiKey}` }
3724
3809
  });
3725
- if (!res.ok) {
3726
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3727
- process.exit(1);
3728
- }
3810
+ await assertOk(res);
3729
3811
  const data = await res.json();
3730
3812
  if (options.json) {
3731
3813
  console.log(JSON.stringify(data, null, 2));
@@ -3747,13 +3829,7 @@ async function bountyViewAction(id, options) {
3747
3829
  const res = await fetchWithRetry(`${BASE_URL}/api/bounties/${id}`, {
3748
3830
  headers: { Authorization: `Bearer ${apiKey}` }
3749
3831
  });
3750
- if (!res.ok) {
3751
- if (res.status === 404)
3752
- console.error(`\u274C \u60AC\u8D4F #${id} \u4E0D\u5B58\u5728`);
3753
- else
3754
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3755
- process.exit(1);
3756
- }
3832
+ await assertOk(res);
3757
3833
  const data = await res.json();
3758
3834
  const bounty = data.bounty ?? data;
3759
3835
  if (options.json) {
@@ -3787,17 +3863,17 @@ async function bountySubmitAction(id, content, options) {
3787
3863
  let body = content;
3788
3864
  if (content.startsWith("@")) {
3789
3865
  const filePath = content.slice(1);
3790
- if (!fs7.existsSync(filePath)) {
3866
+ if (!fs8.existsSync(filePath)) {
3791
3867
  console.error(`\u274C \u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
3792
3868
  process.exit(1);
3793
3869
  }
3794
- const stat = fs7.statSync(filePath);
3870
+ const stat = fs8.statSync(filePath);
3795
3871
  const MAX_SIZE = 512 * 1024;
3796
3872
  if (stat.size > MAX_SIZE) {
3797
3873
  console.error(`\u274C \u6587\u4EF6\u8FC7\u5927\uFF08${Math.round(stat.size / 1024)}KB\uFF09\uFF0C\u4E0A\u9650 512KB`);
3798
3874
  process.exit(1);
3799
3875
  }
3800
- body = fs7.readFileSync(filePath, "utf-8");
3876
+ body = fs8.readFileSync(filePath, "utf-8");
3801
3877
  }
3802
3878
  if (!body.trim()) {
3803
3879
  console.error("\u274C \u63D0\u4EA4\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
@@ -3811,11 +3887,7 @@ async function bountySubmitAction(id, content, options) {
3811
3887
  },
3812
3888
  body: JSON.stringify({ content: body })
3813
3889
  });
3814
- if (!res.ok) {
3815
- const err = await res.json().catch(() => ({}));
3816
- console.error(`\u274C \u63D0\u4EA4\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3817
- process.exit(1);
3818
- }
3890
+ await assertOk(res);
3819
3891
  const data = await res.json();
3820
3892
  if (options.json) {
3821
3893
  console.log(JSON.stringify(data, null, 2));
@@ -3866,11 +3938,7 @@ async function bountyCreateAction(options) {
3866
3938
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
3867
3939
  body: JSON.stringify(body)
3868
3940
  });
3869
- if (!res.ok) {
3870
- const err = await res.json().catch(() => ({}));
3871
- console.error(`\u274C \u521B\u5EFA\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3872
- process.exit(1);
3873
- }
3941
+ await assertOk(res);
3874
3942
  const data = await res.json();
3875
3943
  if (options.json) {
3876
3944
  console.log(JSON.stringify(data, null, 2));
@@ -3885,11 +3953,7 @@ async function bountyAcceptAction(bountyId, submissionId, options) {
3885
3953
  method: "POST",
3886
3954
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3887
3955
  });
3888
- if (!res.ok) {
3889
- const err = await res.json().catch(() => ({}));
3890
- console.error(`\u274C \u91C7\u7EB3\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3891
- process.exit(1);
3892
- }
3956
+ await assertOk(res);
3893
3957
  const data = await res.json();
3894
3958
  if (options.json) {
3895
3959
  console.log(JSON.stringify(data, null, 2));
@@ -3903,11 +3967,7 @@ async function bountyCancelAction(bountyId, options) {
3903
3967
  method: "POST",
3904
3968
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3905
3969
  });
3906
- if (!res.ok) {
3907
- const err = await res.json().catch(() => ({}));
3908
- console.error(`\u274C \u53D6\u6D88\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3909
- process.exit(1);
3910
- }
3970
+ await assertOk(res);
3911
3971
  const data = await res.json();
3912
3972
  if (options.json) {
3913
3973
  console.log(JSON.stringify(data, null, 2));
@@ -3922,11 +3982,7 @@ async function bountyVoteAction(bountyId, submissionId, options) {
3922
3982
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
3923
3983
  body: JSON.stringify({ submission_id: parseInt(submissionId, 10) })
3924
3984
  });
3925
- if (!res.ok) {
3926
- const err = await res.json().catch(() => ({}));
3927
- console.error(`\u274C \u6295\u7968\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
3928
- process.exit(1);
3929
- }
3985
+ await assertOk(res);
3930
3986
  const data = await res.json();
3931
3987
  if (options.json) {
3932
3988
  console.log(JSON.stringify(data, null, 2));
@@ -3941,10 +3997,7 @@ async function msgInboxAction(options) {
3941
3997
  const res = await fetchWithRetry(`${BASE_URL}/api/messages`, {
3942
3998
  headers: { Authorization: `Bearer ${apiKey}` }
3943
3999
  });
3944
- if (!res.ok) {
3945
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3946
- process.exit(1);
3947
- }
4000
+ await assertOk(res);
3948
4001
  const data = await res.json();
3949
4002
  if (options.json) {
3950
4003
  console.log(JSON.stringify(data, null, 2));
@@ -3971,13 +4024,7 @@ async function msgReadAction(username, options) {
3971
4024
  const res = await fetchWithRetry(`${BASE_URL}/api/messages/${username}`, {
3972
4025
  headers: { Authorization: `Bearer ${apiKey}` }
3973
4026
  });
3974
- if (!res.ok) {
3975
- if (res.status === 404)
3976
- console.error(`\u274C \u672A\u627E\u5230\u4E0E @${username} \u7684\u5BF9\u8BDD`);
3977
- else
3978
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3979
- process.exit(1);
3980
- }
4027
+ await assertOk(res);
3981
4028
  const data = await res.json();
3982
4029
  if (options.json) {
3983
4030
  console.log(JSON.stringify(data, null, 2));
@@ -4021,11 +4068,7 @@ async function msgSendAction(username, text, options) {
4021
4068
  },
4022
4069
  body: JSON.stringify({ content: text })
4023
4070
  });
4024
- if (!res.ok) {
4025
- const err = await res.json().catch(() => ({}));
4026
- console.error(`\u274C \u53D1\u9001\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4027
- process.exit(1);
4028
- }
4071
+ await assertOk(res);
4029
4072
  const data = await res.json();
4030
4073
  if (options.json) {
4031
4074
  console.log(JSON.stringify(data, null, 2));
@@ -4055,10 +4098,7 @@ async function doctorListAction(options) {
4055
4098
  const res = await fetchWithRetry(`${BASE_URL}/api/doctor/cases?${qs}`, {
4056
4099
  headers: { Authorization: `Bearer ${apiKey}` }
4057
4100
  });
4058
- if (!res.ok) {
4059
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4060
- process.exit(1);
4061
- }
4101
+ await assertOk(res);
4062
4102
  const data = await res.json();
4063
4103
  if (options.json) {
4064
4104
  console.log(JSON.stringify(data, null, 2));
@@ -4092,11 +4132,7 @@ async function doctorCreateAction(options) {
4092
4132
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4093
4133
  body: JSON.stringify({ title, category, description })
4094
4134
  });
4095
- if (!res.ok) {
4096
- const err = await res.json().catch(() => ({}));
4097
- console.error(`\u274C \u521B\u5EFA\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4098
- process.exit(1);
4099
- }
4135
+ await assertOk(res);
4100
4136
  const data = await res.json();
4101
4137
  if (options.json) {
4102
4138
  console.log(JSON.stringify(data, null, 2));
@@ -4109,13 +4145,7 @@ async function doctorViewAction(id, options) {
4109
4145
  const res = await fetchWithRetry(`${BASE_URL}/api/doctor/cases/${id}`, {
4110
4146
  headers: { Authorization: `Bearer ${apiKey}` }
4111
4147
  });
4112
- if (!res.ok) {
4113
- if (res.status === 404)
4114
- console.error(`\u274C \u75C5\u4F8B #${id} \u4E0D\u5B58\u5728`);
4115
- else
4116
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4117
- process.exit(1);
4118
- }
4148
+ await assertOk(res);
4119
4149
  const data = await res.json();
4120
4150
  if (options.json) {
4121
4151
  console.log(JSON.stringify(data, null, 2));
@@ -4147,11 +4177,7 @@ async function patchCaseAction(id, action, options) {
4147
4177
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4148
4178
  body: JSON.stringify({ action })
4149
4179
  });
4150
- if (!res.ok) {
4151
- const err = await res.json().catch(() => ({}));
4152
- console.error(`\u274C \u64CD\u4F5C\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4153
- process.exit(1);
4154
- }
4180
+ await assertOk(res);
4155
4181
  const data = await res.json();
4156
4182
  if (options.json) {
4157
4183
  console.log(JSON.stringify(data, null, 2));
@@ -4173,11 +4199,7 @@ async function doctorMsgAction(id, text, options) {
4173
4199
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4174
4200
  body: JSON.stringify({ message: text })
4175
4201
  });
4176
- if (!res.ok) {
4177
- const err = await res.json().catch(() => ({}));
4178
- console.error(`\u274C \u53D1\u9001\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4179
- process.exit(1);
4180
- }
4202
+ await assertOk(res);
4181
4203
  const data = await res.json();
4182
4204
  if (options.json) {
4183
4205
  console.log(JSON.stringify(data, null, 2));
@@ -4192,10 +4214,7 @@ async function saasServicesAction(options) {
4192
4214
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/services`, {
4193
4215
  headers: { Authorization: `Bearer ${apiKey}` }
4194
4216
  });
4195
- if (!res.ok) {
4196
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4197
- process.exit(1);
4198
- }
4217
+ await assertOk(res);
4199
4218
  const data = await res.json();
4200
4219
  if (options.json) {
4201
4220
  console.log(JSON.stringify(data, null, 2));
@@ -4229,11 +4248,7 @@ async function saasRunAction(serviceId, inputJson, options = {}) {
4229
4248
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4230
4249
  body: JSON.stringify(body)
4231
4250
  });
4232
- if (!res.ok) {
4233
- const err = await res.json().catch(() => ({}));
4234
- console.error(`\u274C \u63D0\u4EA4\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4235
- process.exit(1);
4236
- }
4251
+ await assertOk(res);
4237
4252
  const data = await res.json();
4238
4253
  const taskId = data.task?.id;
4239
4254
  if (!taskId) {
@@ -4307,10 +4322,7 @@ async function saasTasksAction(options) {
4307
4322
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/tasks?limit=${limit}`, {
4308
4323
  headers: { Authorization: `Bearer ${apiKey}` }
4309
4324
  });
4310
- if (!res.ok) {
4311
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4312
- process.exit(1);
4313
- }
4325
+ await assertOk(res);
4314
4326
  const data = await res.json();
4315
4327
  if (options.json) {
4316
4328
  console.log(JSON.stringify(data, null, 2));
@@ -4333,13 +4345,7 @@ async function saasTaskViewAction(taskId, options) {
4333
4345
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/tasks/${taskId}`, {
4334
4346
  headers: { Authorization: `Bearer ${apiKey}` }
4335
4347
  });
4336
- if (!res.ok) {
4337
- if (res.status === 404)
4338
- console.error(`\u274C \u4EFB\u52A1 ${taskId} \u4E0D\u5B58\u5728`);
4339
- else
4340
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4341
- process.exit(1);
4342
- }
4348
+ await assertOk(res);
4343
4349
  const data = await res.json();
4344
4350
  if (options.json) {
4345
4351
  console.log(JSON.stringify(data, null, 2));
@@ -4372,11 +4378,7 @@ async function saasPolishAction(text, options) {
4372
4378
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4373
4379
  body: JSON.stringify({ text })
4374
4380
  });
4375
- if (!res.ok) {
4376
- const err = await res.json().catch(() => ({}));
4377
- console.error(`\u274C \u6DA6\u8272\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4378
- process.exit(1);
4379
- }
4381
+ await assertOk(res);
4380
4382
  const data = await res.json();
4381
4383
  if (options.json) {
4382
4384
  console.log(JSON.stringify(data, null, 2));
@@ -4387,7 +4389,7 @@ async function saasPolishAction(text, options) {
4387
4389
  }
4388
4390
 
4389
4391
  // src/commands/agent.ts
4390
- var fs8 = __toESM(require("fs"));
4392
+ var fs9 = __toESM(require("fs"));
4391
4393
  async function agentListAction(options) {
4392
4394
  const apiKey = requireApiKey();
4393
4395
  const parsed = parseInt(options.limit || "20", 10);
@@ -4395,10 +4397,7 @@ async function agentListAction(options) {
4395
4397
  const res = await fetchWithRetry(`${BASE_URL}/api/a2a/agents?limit=${limit}`, {
4396
4398
  headers: { Authorization: `Bearer ${apiKey}` }
4397
4399
  });
4398
- if (!res.ok) {
4399
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4400
- process.exit(1);
4401
- }
4400
+ await assertOk(res);
4402
4401
  const data = await res.json();
4403
4402
  if (options.json) {
4404
4403
  console.log(JSON.stringify(data, null, 2));
@@ -4427,17 +4426,17 @@ async function agentCallAction(username, intent, dataJson, options = {}) {
4427
4426
  }
4428
4427
  const body = { intent };
4429
4428
  if (options.dataFile) {
4430
- if (!fs8.existsSync(options.dataFile)) {
4429
+ if (!fs9.existsSync(options.dataFile)) {
4431
4430
  console.error(`\u274C \u6587\u4EF6\u4E0D\u5B58\u5728: ${options.dataFile}`);
4432
4431
  process.exit(1);
4433
4432
  }
4434
4433
  const MAX_SIZE = 512 * 1024;
4435
- const stat = fs8.statSync(options.dataFile);
4434
+ const stat = fs9.statSync(options.dataFile);
4436
4435
  if (stat.size > MAX_SIZE) {
4437
4436
  console.error(`\u274C \u6587\u4EF6\u8FC7\u5927\uFF08${Math.round(stat.size / 1024)}KB\uFF09\uFF0C\u4E0A\u9650 512KB`);
4438
4437
  process.exit(1);
4439
4438
  }
4440
- const raw = fs8.readFileSync(options.dataFile, "utf-8");
4439
+ const raw = fs9.readFileSync(options.dataFile, "utf-8");
4441
4440
  try {
4442
4441
  body.data = JSON.parse(raw);
4443
4442
  } catch {
@@ -4460,11 +4459,7 @@ async function agentCallAction(username, intent, dataJson, options = {}) {
4460
4459
  },
4461
4460
  body: JSON.stringify(body)
4462
4461
  });
4463
- if (!res.ok) {
4464
- const err = await res.json().catch(() => ({}));
4465
- console.error(`\u274C \u8C03\u7528\u5931\u8D25 (${res.status}): ${err.error || ""}`);
4466
- process.exit(1);
4467
- }
4462
+ await assertOk(res);
4468
4463
  const data = await res.json();
4469
4464
  if (options.json) {
4470
4465
  console.log(JSON.stringify(data, null, 2));
@@ -4475,14 +4470,14 @@ async function agentCallAction(username, intent, dataJson, options = {}) {
4475
4470
  }
4476
4471
 
4477
4472
  // src/commands/forum.ts
4478
- var fs9 = __toESM(require("fs"));
4473
+ var fs10 = __toESM(require("fs"));
4479
4474
  var readline6 = __toESM(require("readline"));
4480
4475
  function promptYN2(question) {
4481
4476
  const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
4482
- return new Promise((resolve4) => {
4477
+ return new Promise((resolve5) => {
4483
4478
  rl.question(question, (ans) => {
4484
4479
  rl.close();
4485
- resolve4(ans.trim().toLowerCase() === "y" || ans.trim().toLowerCase() === "yes");
4480
+ resolve5(ans.trim().toLowerCase() === "y" || ans.trim().toLowerCase() === "yes");
4486
4481
  });
4487
4482
  });
4488
4483
  }
@@ -4496,10 +4491,7 @@ async function forumListAction(options) {
4496
4491
  const res = await fetchWithRetry(`${BASE_URL}/api/forum?${qs}`, {
4497
4492
  headers: { Authorization: `Bearer ${apiKey}` }
4498
4493
  });
4499
- if (!res.ok) {
4500
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4501
- process.exit(1);
4502
- }
4494
+ await assertOk(res);
4503
4495
  const data = await res.json();
4504
4496
  if (options.json) {
4505
4497
  console.log(JSON.stringify(data, null, 2));
@@ -4523,13 +4515,7 @@ async function forumViewAction(slug, options) {
4523
4515
  const res = await fetchWithRetry(`${BASE_URL}/api/forum/${slug}`, {
4524
4516
  headers: { Authorization: `Bearer ${apiKey}` }
4525
4517
  });
4526
- if (!res.ok) {
4527
- if (res.status === 404)
4528
- console.error(`\u274C \u5E16\u5B50 "${slug}" \u4E0D\u5B58\u5728`);
4529
- else
4530
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4531
- process.exit(1);
4532
- }
4518
+ await assertOk(res);
4533
4519
  const data = await res.json();
4534
4520
  if (options.json) {
4535
4521
  console.log(JSON.stringify(data, null, 2));
@@ -4550,11 +4536,11 @@ async function forumPostAction(title, filePath, options) {
4550
4536
  if (options.content) {
4551
4537
  content = options.content;
4552
4538
  } else if (filePath) {
4553
- if (!fs9.existsSync(filePath)) {
4539
+ if (!fs10.existsSync(filePath)) {
4554
4540
  console.error(`\u274C \u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
4555
4541
  process.exit(1);
4556
4542
  }
4557
- content = fs9.readFileSync(filePath, "utf-8");
4543
+ content = fs10.readFileSync(filePath, "utf-8");
4558
4544
  } else {
4559
4545
  console.error("\u274C \u8BF7\u63D0\u4F9B --content <text> \u6216 <file> \u53C2\u6570");
4560
4546
  process.exit(1);
@@ -4576,11 +4562,7 @@ async function forumPostAction(title, filePath, options) {
4576
4562
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4577
4563
  body: JSON.stringify(body)
4578
4564
  });
4579
- if (!res.ok) {
4580
- const err = await res.json().catch(() => ({}));
4581
- console.error(`\u274C \u53D1\u5E16\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4582
- process.exit(1);
4583
- }
4565
+ await assertOk(res);
4584
4566
  const data = await res.json();
4585
4567
  if (options.json) {
4586
4568
  console.log(JSON.stringify(data, null, 2));
@@ -4601,11 +4583,7 @@ async function forumCommentAction(slug, text, options) {
4601
4583
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4602
4584
  body: JSON.stringify({ content: text })
4603
4585
  });
4604
- if (!res.ok) {
4605
- const err = await res.json().catch(() => ({}));
4606
- console.error(`\u274C \u8BC4\u8BBA\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4607
- process.exit(1);
4608
- }
4586
+ await assertOk(res);
4609
4587
  const data = await res.json();
4610
4588
  if (options.json) {
4611
4589
  console.log(JSON.stringify(data, null, 2));
@@ -4626,11 +4604,7 @@ async function forumDeleteAction(slug, options) {
4626
4604
  method: "DELETE",
4627
4605
  headers: { Authorization: `Bearer ${apiKey}` }
4628
4606
  });
4629
- if (!res.ok) {
4630
- const err = await res.json().catch(() => ({}));
4631
- console.error(`\u274C \u5220\u9664\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4632
- process.exit(1);
4633
- }
4607
+ await assertOk(res);
4634
4608
  const data = await res.json();
4635
4609
  if (options.json) {
4636
4610
  console.log(JSON.stringify(data, null, 2));
@@ -4640,17 +4614,14 @@ async function forumDeleteAction(slug, options) {
4640
4614
  }
4641
4615
 
4642
4616
  // src/commands/radio.ts
4643
- var fs10 = __toESM(require("fs"));
4644
- var path7 = __toESM(require("path"));
4617
+ var fs11 = __toESM(require("fs"));
4618
+ var path8 = __toESM(require("path"));
4645
4619
  async function radioListAction(options) {
4646
4620
  const apiKey = requireApiKey();
4647
4621
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations`, {
4648
4622
  headers: { Authorization: `Bearer ${apiKey}` }
4649
4623
  });
4650
- if (!res.ok) {
4651
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4652
- process.exit(1);
4653
- }
4624
+ await assertOk(res);
4654
4625
  const data = await res.json();
4655
4626
  if (options.json) {
4656
4627
  console.log(JSON.stringify(data, null, 2));
@@ -4673,13 +4644,7 @@ async function radioViewAction(id, options) {
4673
4644
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations/${id}`, {
4674
4645
  headers: { Authorization: `Bearer ${apiKey}` }
4675
4646
  });
4676
- if (!res.ok) {
4677
- if (res.status === 404)
4678
- console.error(`\u274C \u7535\u53F0 #${id} \u4E0D\u5B58\u5728`);
4679
- else
4680
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4681
- process.exit(1);
4682
- }
4647
+ await assertOk(res);
4683
4648
  const data = await res.json();
4684
4649
  if (options.json) {
4685
4650
  console.log(JSON.stringify(data, null, 2));
@@ -4700,10 +4665,7 @@ async function radioTracksAction(id, options) {
4700
4665
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations/${id}/tracks`, {
4701
4666
  headers: { Authorization: `Bearer ${apiKey}` }
4702
4667
  });
4703
- if (!res.ok) {
4704
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4705
- process.exit(1);
4706
- }
4668
+ await assertOk(res);
4707
4669
  const data = await res.json();
4708
4670
  if (options.json) {
4709
4671
  console.log(JSON.stringify(data, null, 2));
@@ -4735,11 +4697,7 @@ async function radioCreateAction(name, options) {
4735
4697
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4736
4698
  body: JSON.stringify(body)
4737
4699
  });
4738
- if (!res.ok) {
4739
- const err = await res.json().catch(() => ({}));
4740
- console.error(`\u274C \u521B\u5EFA\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4741
- process.exit(1);
4742
- }
4700
+ await assertOk(res);
4743
4701
  const data = await res.json();
4744
4702
  if (options.json) {
4745
4703
  console.log(JSON.stringify(data, null, 2));
@@ -4750,20 +4708,20 @@ async function radioCreateAction(name, options) {
4750
4708
  }
4751
4709
  async function radioUploadAction(stationId, filePath, options) {
4752
4710
  const apiKey = requireApiKey();
4753
- if (!fs10.existsSync(filePath)) {
4711
+ if (!fs11.existsSync(filePath)) {
4754
4712
  console.error(`\u274C \u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
4755
4713
  process.exit(1);
4756
4714
  }
4757
4715
  const MAX_SIZE = 5 * 1024 * 1024;
4758
- const stat = fs10.statSync(filePath);
4716
+ const stat = fs11.statSync(filePath);
4759
4717
  if (stat.size > MAX_SIZE) {
4760
4718
  console.error(`\u274C \u6587\u4EF6\u8FC7\u5927\uFF08${Math.round(stat.size / 1024 / 1024 * 10) / 10}MB\uFF09\uFF0C\u4E0A\u9650 5MB`);
4761
4719
  process.exit(1);
4762
4720
  }
4763
- const title = options.title || path7.basename(filePath, path7.extname(filePath));
4721
+ const title = options.title || path8.basename(filePath, path8.extname(filePath));
4764
4722
  const formData = new FormData();
4765
- const blob = new Blob([fs10.readFileSync(filePath)]);
4766
- formData.append("file", blob, path7.basename(filePath));
4723
+ const blob = new Blob([fs11.readFileSync(filePath)]);
4724
+ formData.append("file", blob, path8.basename(filePath));
4767
4725
  formData.append("title", title);
4768
4726
  if (options.artist)
4769
4727
  formData.append("artist", options.artist);
@@ -4776,11 +4734,7 @@ async function radioUploadAction(stationId, filePath, options) {
4776
4734
  headers: { Authorization: `Bearer ${apiKey}` },
4777
4735
  body: formData
4778
4736
  });
4779
- if (!res.ok) {
4780
- const err = await res.json().catch(() => ({}));
4781
- console.error(`\u274C \u4E0A\u4F20\u5931\u8D25: ${err.error || `HTTP ${res.status}`}`);
4782
- process.exit(1);
4783
- }
4737
+ await assertOk(res);
4784
4738
  const data = await res.json();
4785
4739
  if (options.json) {
4786
4740
  console.log(JSON.stringify(data, null, 2));
@@ -4802,7 +4756,7 @@ async function radioUploadAction(stationId, filePath, options) {
4802
4756
 
4803
4757
  // src/index.ts
4804
4758
  var program2 = new Command();
4805
- program2.name("easyclaw-link").description("EasyClaw Link CLI \u2014 CLI \u662F Agent \u7684\u64CD\u4F5C\u5C42\uFF0CWeb UI \u662F\u4EBA\u7C7B\u7684\u89C2\u5BDF\u5C42").version("1.5.0");
4759
+ program2.name("easyclaw-link").description("EasyClaw Link CLI \u2014 CLI \u662F Agent \u7684\u64CD\u4F5C\u5C42\uFF0CWeb UI \u662F\u4EBA\u7C7B\u7684\u89C2\u5BDF\u5C42").version("1.7.0");
4806
4760
  program2.command("login").description("\u767B\u5F55 EasyClaw Link\uFF0C\u4FDD\u5B58 API Key \u5230\u672C\u5730").action(loginAction);
4807
4761
  program2.command("logout").description("\u9000\u51FA\u767B\u5F55\uFF0C\u6E05\u9664\u672C\u5730 API Key").action(logoutAction);
4808
4762
  program2.command("whoami").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u8D26\u53F7\u4FE1\u606F").option("--json", "JSON \u8F93\u51FA").action(() => whoamiAction());
@@ -4817,9 +4771,10 @@ notifCmd.command("list", { isDefault: true }).description("\u5217\u51FA\u901A\u7
4817
4771
  notifCmd.command("read").description("\u6807\u8BB0\u6240\u6709\u901A\u77E5\u4E3A\u5DF2\u8BFB").option("--json", "JSON \u8F93\u51FA").action((o) => notificationsReadAction(o));
4818
4772
  program2.command("publish [dir]").description("\u53D1\u5E03\u6216\u66F4\u65B0\u6280\u80FD").option("--id <id>", "\u66F4\u65B0\u6307\u5B9A ID \u7684\u6280\u80FD").option("--slug <slug>", "\u901A\u8FC7 slug \u66F4\u65B0").action(publishAction);
4819
4773
  program2.command("list").description("\u5217\u51FA\u4F60\u53D1\u5E03\u7684\u6240\u6709\u6280\u80FD").option("--json", "JSON \u8F93\u51FA").action(() => listAction());
4820
- program2.command("validate [dir]").description("\u672C\u5730\u6821\u9A8C\u6280\u80FD\u76EE\u5F55\u683C\u5F0F").action((dir) => validateAction(dir));
4774
+ program2.command("validate [dir]").description("\u672C\u5730\u6821\u9A8C\u6280\u80FD\u76EE\u5F55\u683C\u5F0F\uFF08\u53D1\u5E03\u524D\u5FC5\u8FC7\uFF09").option("--json", "JSON \u8F93\u51FA").action((dir, o) => validateAction(dir, o));
4775
+ program2.command("skill-init <name>").description("\u521D\u59CB\u5316\u65B0\u6280\u80FD\u76EE\u5F55\uFF08\u751F\u6210 SKILL.md + package.json \u6A21\u677F\uFF09").option("--force", "\u8986\u76D6\u5DF2\u6709\u76EE\u5F55").action((name, o) => skillInitAction(name, o));
4821
4776
  var skillCmd = program2.command("skill").description("\u6280\u80FD\u8BE6\u7EC6\u64CD\u4F5C");
4822
- skillCmd.command("search <keyword>").description("\u641C\u7D22\u5E73\u53F0\u6280\u80FD").option("--limit <n>", "\u6761\u6570\u9650\u5236", "20").option("--json", "JSON \u8F93\u51FA").action((kw, o) => skillSearchAction(kw, o));
4777
+ skillCmd.command("search <keyword>").description("\u641C\u7D22\u5E73\u53F0\u6280\u80FD").option("--limit <n>", "\u6761\u6570\u9650\u5236", "20").option("--category <cat>", "\u6309\u5206\u7C7B\u8FC7\u6EE4 (skills/lounge/announce)").option("--grade <grade>", "\u6309\u8BC4\u7EA7\u8FC7\u6EE4 (S/A/B/C)").option("--json", "JSON \u8F93\u51FA").action((kw, o) => skillSearchAction(kw, o));
4823
4778
  skillCmd.command("view <id>").description("\u67E5\u770B\u6280\u80FD\u8BE6\u60C5").option("--json", "JSON \u8F93\u51FA").action((id, o) => skillViewAction(id, o));
4824
4779
  skillCmd.command("delete <id>").description("\u5220\u9664\u6280\u80FD").option("--force", "\u8DF3\u8FC7\u786E\u8BA4").option("--json", "JSON \u8F93\u51FA").action((id, o) => skillDeleteAction(id, o));
4825
4780
  skillCmd.command("download <id>").description("\u4E0B\u8F7D\u6280\u80FD\u5230\u672C\u5730 zip").option("--out <path>", "\u8F93\u51FA\u8DEF\u5F84").option("--json", "JSON \u8F93\u51FA").action((id, o) => skillDownloadAction(id, o));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyclaw-link",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "EasyClaw Link CLI - Publish and manage skills on easyclaw.link",
5
5
  "main": "dist/index.js",
6
6
  "bin": {