easyclaw-link 1.5.0 → 1.6.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 +110 -272
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -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
  }
@@ -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) {
@@ -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));
@@ -3524,13 +3521,7 @@ async function skillViewAction(id, options) {
3524
3521
  const res = await fetchWithRetry(`${BASE_URL}/api/assets/${id}`, {
3525
3522
  headers: { Authorization: `Bearer ${apiKey}` }
3526
3523
  });
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
- }
3524
+ await assertOk(res);
3534
3525
  const data = await res.json();
3535
3526
  const asset = data.asset ?? data;
3536
3527
  if (options.json) {
@@ -3565,15 +3556,7 @@ async function skillDeleteAction(id, options) {
3565
3556
  method: "DELETE",
3566
3557
  headers: { Authorization: `Bearer ${apiKey}` }
3567
3558
  });
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
- }
3559
+ await assertOk(res);
3577
3560
  if (options.json) {
3578
3561
  console.log(JSON.stringify({ success: true, id }));
3579
3562
  return;
@@ -3585,13 +3568,7 @@ async function skillDownloadAction(id, options) {
3585
3568
  const res = await fetchWithRetry(`${BASE_URL}/api/assets/${id}/download`, {
3586
3569
  headers: { Authorization: `Bearer ${apiKey}` }
3587
3570
  });
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
- }
3571
+ await assertOk(res);
3595
3572
  const buffer = Buffer.from(await res.arrayBuffer());
3596
3573
  const outPath = options.out || `skill-${id}.zip`;
3597
3574
  const resolved = path6.resolve(outPath);
@@ -3612,10 +3589,7 @@ async function skillStarAction(id, options) {
3612
3589
  method: "POST",
3613
3590
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3614
3591
  });
3615
- if (!res.ok) {
3616
- console.error(`\u274C \u64CD\u4F5C\u5931\u8D25 (${res.status})`);
3617
- process.exit(1);
3618
- }
3592
+ await assertOk(res);
3619
3593
  const data = await res.json();
3620
3594
  if (options.json) {
3621
3595
  console.log(JSON.stringify(data, null, 2));
@@ -3630,10 +3604,7 @@ async function skillUseAction(id, options) {
3630
3604
  method: "POST",
3631
3605
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3632
3606
  });
3633
- if (!res.ok) {
3634
- console.error(`\u274C \u64CD\u4F5C\u5931\u8D25 (${res.status})`);
3635
- process.exit(1);
3636
- }
3607
+ await assertOk(res);
3637
3608
  const data = await res.json();
3638
3609
  if (options.json) {
3639
3610
  console.log(JSON.stringify(data, null, 2));
@@ -3644,19 +3615,20 @@ async function skillUseAction(id, options) {
3644
3615
  async function skillSearchAction(keyword, options) {
3645
3616
  if (!keyword?.trim()) {
3646
3617
  console.error("\u274C \u8BF7\u63D0\u4F9B\u641C\u7D22\u5173\u952E\u8BCD");
3647
- process.exit(1);
3618
+ process.exit(EXIT.VALIDATION);
3648
3619
  }
3649
3620
  const apiKey = requireApiKey();
3650
3621
  const parsed = parseInt(options.limit || "20", 10);
3651
3622
  const limit = isNaN(parsed) || parsed <= 0 ? 20 : Math.min(parsed, 50);
3652
3623
  const qs = new URLSearchParams({ q: keyword, limit: String(limit) });
3624
+ if (options.category)
3625
+ qs.set("category", options.category);
3626
+ if (options.grade)
3627
+ qs.set("grade", options.grade.toUpperCase());
3653
3628
  const res = await fetchWithRetry(`${BASE_URL}/api/assets?${qs}`, {
3654
3629
  headers: { Authorization: `Bearer ${apiKey}` }
3655
3630
  });
3656
- if (!res.ok) {
3657
- console.error(`\u274C \u641C\u7D22\u5931\u8D25 (${res.status})`);
3658
- process.exit(1);
3659
- }
3631
+ await assertOk(res);
3660
3632
  const data = await res.json();
3661
3633
  if (options.json) {
3662
3634
  console.log(JSON.stringify(data, null, 2));
@@ -3691,10 +3663,7 @@ async function bountyListAction(options) {
3691
3663
  `${BASE_URL}/api/bounties?status=${status}&limit=${limit}&sort=date_desc`,
3692
3664
  { headers: { Authorization: `Bearer ${apiKey}` } }
3693
3665
  );
3694
- if (!res.ok) {
3695
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3696
- process.exit(1);
3697
- }
3666
+ await assertOk(res);
3698
3667
  const data = await res.json();
3699
3668
  if (options.json) {
3700
3669
  console.log(JSON.stringify(data, null, 2));
@@ -3722,10 +3691,7 @@ async function bountyMineAction(options) {
3722
3691
  const res = await fetchWithRetry(`${BASE_URL}/api/bounties?mine=true`, {
3723
3692
  headers: { Authorization: `Bearer ${apiKey}` }
3724
3693
  });
3725
- if (!res.ok) {
3726
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3727
- process.exit(1);
3728
- }
3694
+ await assertOk(res);
3729
3695
  const data = await res.json();
3730
3696
  if (options.json) {
3731
3697
  console.log(JSON.stringify(data, null, 2));
@@ -3747,13 +3713,7 @@ async function bountyViewAction(id, options) {
3747
3713
  const res = await fetchWithRetry(`${BASE_URL}/api/bounties/${id}`, {
3748
3714
  headers: { Authorization: `Bearer ${apiKey}` }
3749
3715
  });
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
- }
3716
+ await assertOk(res);
3757
3717
  const data = await res.json();
3758
3718
  const bounty = data.bounty ?? data;
3759
3719
  if (options.json) {
@@ -3811,11 +3771,7 @@ async function bountySubmitAction(id, content, options) {
3811
3771
  },
3812
3772
  body: JSON.stringify({ content: body })
3813
3773
  });
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
- }
3774
+ await assertOk(res);
3819
3775
  const data = await res.json();
3820
3776
  if (options.json) {
3821
3777
  console.log(JSON.stringify(data, null, 2));
@@ -3866,11 +3822,7 @@ async function bountyCreateAction(options) {
3866
3822
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
3867
3823
  body: JSON.stringify(body)
3868
3824
  });
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
- }
3825
+ await assertOk(res);
3874
3826
  const data = await res.json();
3875
3827
  if (options.json) {
3876
3828
  console.log(JSON.stringify(data, null, 2));
@@ -3885,11 +3837,7 @@ async function bountyAcceptAction(bountyId, submissionId, options) {
3885
3837
  method: "POST",
3886
3838
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3887
3839
  });
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
- }
3840
+ await assertOk(res);
3893
3841
  const data = await res.json();
3894
3842
  if (options.json) {
3895
3843
  console.log(JSON.stringify(data, null, 2));
@@ -3903,11 +3851,7 @@ async function bountyCancelAction(bountyId, options) {
3903
3851
  method: "POST",
3904
3852
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3905
3853
  });
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
- }
3854
+ await assertOk(res);
3911
3855
  const data = await res.json();
3912
3856
  if (options.json) {
3913
3857
  console.log(JSON.stringify(data, null, 2));
@@ -3922,11 +3866,7 @@ async function bountyVoteAction(bountyId, submissionId, options) {
3922
3866
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
3923
3867
  body: JSON.stringify({ submission_id: parseInt(submissionId, 10) })
3924
3868
  });
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
- }
3869
+ await assertOk(res);
3930
3870
  const data = await res.json();
3931
3871
  if (options.json) {
3932
3872
  console.log(JSON.stringify(data, null, 2));
@@ -3941,10 +3881,7 @@ async function msgInboxAction(options) {
3941
3881
  const res = await fetchWithRetry(`${BASE_URL}/api/messages`, {
3942
3882
  headers: { Authorization: `Bearer ${apiKey}` }
3943
3883
  });
3944
- if (!res.ok) {
3945
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
3946
- process.exit(1);
3947
- }
3884
+ await assertOk(res);
3948
3885
  const data = await res.json();
3949
3886
  if (options.json) {
3950
3887
  console.log(JSON.stringify(data, null, 2));
@@ -3971,13 +3908,7 @@ async function msgReadAction(username, options) {
3971
3908
  const res = await fetchWithRetry(`${BASE_URL}/api/messages/${username}`, {
3972
3909
  headers: { Authorization: `Bearer ${apiKey}` }
3973
3910
  });
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
- }
3911
+ await assertOk(res);
3981
3912
  const data = await res.json();
3982
3913
  if (options.json) {
3983
3914
  console.log(JSON.stringify(data, null, 2));
@@ -4021,11 +3952,7 @@ async function msgSendAction(username, text, options) {
4021
3952
  },
4022
3953
  body: JSON.stringify({ content: text })
4023
3954
  });
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
- }
3955
+ await assertOk(res);
4029
3956
  const data = await res.json();
4030
3957
  if (options.json) {
4031
3958
  console.log(JSON.stringify(data, null, 2));
@@ -4055,10 +3982,7 @@ async function doctorListAction(options) {
4055
3982
  const res = await fetchWithRetry(`${BASE_URL}/api/doctor/cases?${qs}`, {
4056
3983
  headers: { Authorization: `Bearer ${apiKey}` }
4057
3984
  });
4058
- if (!res.ok) {
4059
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4060
- process.exit(1);
4061
- }
3985
+ await assertOk(res);
4062
3986
  const data = await res.json();
4063
3987
  if (options.json) {
4064
3988
  console.log(JSON.stringify(data, null, 2));
@@ -4092,11 +4016,7 @@ async function doctorCreateAction(options) {
4092
4016
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4093
4017
  body: JSON.stringify({ title, category, description })
4094
4018
  });
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
- }
4019
+ await assertOk(res);
4100
4020
  const data = await res.json();
4101
4021
  if (options.json) {
4102
4022
  console.log(JSON.stringify(data, null, 2));
@@ -4109,13 +4029,7 @@ async function doctorViewAction(id, options) {
4109
4029
  const res = await fetchWithRetry(`${BASE_URL}/api/doctor/cases/${id}`, {
4110
4030
  headers: { Authorization: `Bearer ${apiKey}` }
4111
4031
  });
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
- }
4032
+ await assertOk(res);
4119
4033
  const data = await res.json();
4120
4034
  if (options.json) {
4121
4035
  console.log(JSON.stringify(data, null, 2));
@@ -4147,11 +4061,7 @@ async function patchCaseAction(id, action, options) {
4147
4061
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4148
4062
  body: JSON.stringify({ action })
4149
4063
  });
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
- }
4064
+ await assertOk(res);
4155
4065
  const data = await res.json();
4156
4066
  if (options.json) {
4157
4067
  console.log(JSON.stringify(data, null, 2));
@@ -4173,11 +4083,7 @@ async function doctorMsgAction(id, text, options) {
4173
4083
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4174
4084
  body: JSON.stringify({ message: text })
4175
4085
  });
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
- }
4086
+ await assertOk(res);
4181
4087
  const data = await res.json();
4182
4088
  if (options.json) {
4183
4089
  console.log(JSON.stringify(data, null, 2));
@@ -4192,10 +4098,7 @@ async function saasServicesAction(options) {
4192
4098
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/services`, {
4193
4099
  headers: { Authorization: `Bearer ${apiKey}` }
4194
4100
  });
4195
- if (!res.ok) {
4196
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4197
- process.exit(1);
4198
- }
4101
+ await assertOk(res);
4199
4102
  const data = await res.json();
4200
4103
  if (options.json) {
4201
4104
  console.log(JSON.stringify(data, null, 2));
@@ -4229,11 +4132,7 @@ async function saasRunAction(serviceId, inputJson, options = {}) {
4229
4132
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4230
4133
  body: JSON.stringify(body)
4231
4134
  });
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
- }
4135
+ await assertOk(res);
4237
4136
  const data = await res.json();
4238
4137
  const taskId = data.task?.id;
4239
4138
  if (!taskId) {
@@ -4307,10 +4206,7 @@ async function saasTasksAction(options) {
4307
4206
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/tasks?limit=${limit}`, {
4308
4207
  headers: { Authorization: `Bearer ${apiKey}` }
4309
4208
  });
4310
- if (!res.ok) {
4311
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4312
- process.exit(1);
4313
- }
4209
+ await assertOk(res);
4314
4210
  const data = await res.json();
4315
4211
  if (options.json) {
4316
4212
  console.log(JSON.stringify(data, null, 2));
@@ -4333,13 +4229,7 @@ async function saasTaskViewAction(taskId, options) {
4333
4229
  const res = await fetchWithRetry(`${BASE_URL}/api/saas/tasks/${taskId}`, {
4334
4230
  headers: { Authorization: `Bearer ${apiKey}` }
4335
4231
  });
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
- }
4232
+ await assertOk(res);
4343
4233
  const data = await res.json();
4344
4234
  if (options.json) {
4345
4235
  console.log(JSON.stringify(data, null, 2));
@@ -4372,11 +4262,7 @@ async function saasPolishAction(text, options) {
4372
4262
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4373
4263
  body: JSON.stringify({ text })
4374
4264
  });
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
- }
4265
+ await assertOk(res);
4380
4266
  const data = await res.json();
4381
4267
  if (options.json) {
4382
4268
  console.log(JSON.stringify(data, null, 2));
@@ -4395,10 +4281,7 @@ async function agentListAction(options) {
4395
4281
  const res = await fetchWithRetry(`${BASE_URL}/api/a2a/agents?limit=${limit}`, {
4396
4282
  headers: { Authorization: `Bearer ${apiKey}` }
4397
4283
  });
4398
- if (!res.ok) {
4399
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4400
- process.exit(1);
4401
- }
4284
+ await assertOk(res);
4402
4285
  const data = await res.json();
4403
4286
  if (options.json) {
4404
4287
  console.log(JSON.stringify(data, null, 2));
@@ -4460,11 +4343,7 @@ async function agentCallAction(username, intent, dataJson, options = {}) {
4460
4343
  },
4461
4344
  body: JSON.stringify(body)
4462
4345
  });
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
- }
4346
+ await assertOk(res);
4468
4347
  const data = await res.json();
4469
4348
  if (options.json) {
4470
4349
  console.log(JSON.stringify(data, null, 2));
@@ -4496,10 +4375,7 @@ async function forumListAction(options) {
4496
4375
  const res = await fetchWithRetry(`${BASE_URL}/api/forum?${qs}`, {
4497
4376
  headers: { Authorization: `Bearer ${apiKey}` }
4498
4377
  });
4499
- if (!res.ok) {
4500
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4501
- process.exit(1);
4502
- }
4378
+ await assertOk(res);
4503
4379
  const data = await res.json();
4504
4380
  if (options.json) {
4505
4381
  console.log(JSON.stringify(data, null, 2));
@@ -4523,13 +4399,7 @@ async function forumViewAction(slug, options) {
4523
4399
  const res = await fetchWithRetry(`${BASE_URL}/api/forum/${slug}`, {
4524
4400
  headers: { Authorization: `Bearer ${apiKey}` }
4525
4401
  });
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
- }
4402
+ await assertOk(res);
4533
4403
  const data = await res.json();
4534
4404
  if (options.json) {
4535
4405
  console.log(JSON.stringify(data, null, 2));
@@ -4576,11 +4446,7 @@ async function forumPostAction(title, filePath, options) {
4576
4446
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4577
4447
  body: JSON.stringify(body)
4578
4448
  });
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
- }
4449
+ await assertOk(res);
4584
4450
  const data = await res.json();
4585
4451
  if (options.json) {
4586
4452
  console.log(JSON.stringify(data, null, 2));
@@ -4601,11 +4467,7 @@ async function forumCommentAction(slug, text, options) {
4601
4467
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4602
4468
  body: JSON.stringify({ content: text })
4603
4469
  });
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
- }
4470
+ await assertOk(res);
4609
4471
  const data = await res.json();
4610
4472
  if (options.json) {
4611
4473
  console.log(JSON.stringify(data, null, 2));
@@ -4626,11 +4488,7 @@ async function forumDeleteAction(slug, options) {
4626
4488
  method: "DELETE",
4627
4489
  headers: { Authorization: `Bearer ${apiKey}` }
4628
4490
  });
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
- }
4491
+ await assertOk(res);
4634
4492
  const data = await res.json();
4635
4493
  if (options.json) {
4636
4494
  console.log(JSON.stringify(data, null, 2));
@@ -4647,10 +4505,7 @@ async function radioListAction(options) {
4647
4505
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations`, {
4648
4506
  headers: { Authorization: `Bearer ${apiKey}` }
4649
4507
  });
4650
- if (!res.ok) {
4651
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4652
- process.exit(1);
4653
- }
4508
+ await assertOk(res);
4654
4509
  const data = await res.json();
4655
4510
  if (options.json) {
4656
4511
  console.log(JSON.stringify(data, null, 2));
@@ -4673,13 +4528,7 @@ async function radioViewAction(id, options) {
4673
4528
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations/${id}`, {
4674
4529
  headers: { Authorization: `Bearer ${apiKey}` }
4675
4530
  });
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
- }
4531
+ await assertOk(res);
4683
4532
  const data = await res.json();
4684
4533
  if (options.json) {
4685
4534
  console.log(JSON.stringify(data, null, 2));
@@ -4700,10 +4549,7 @@ async function radioTracksAction(id, options) {
4700
4549
  const res = await fetchWithRetry(`${BASE_URL}/api/radio/stations/${id}/tracks`, {
4701
4550
  headers: { Authorization: `Bearer ${apiKey}` }
4702
4551
  });
4703
- if (!res.ok) {
4704
- console.error(`\u274C \u83B7\u53D6\u5931\u8D25 (${res.status})`);
4705
- process.exit(1);
4706
- }
4552
+ await assertOk(res);
4707
4553
  const data = await res.json();
4708
4554
  if (options.json) {
4709
4555
  console.log(JSON.stringify(data, null, 2));
@@ -4735,11 +4581,7 @@ async function radioCreateAction(name, options) {
4735
4581
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
4736
4582
  body: JSON.stringify(body)
4737
4583
  });
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
- }
4584
+ await assertOk(res);
4743
4585
  const data = await res.json();
4744
4586
  if (options.json) {
4745
4587
  console.log(JSON.stringify(data, null, 2));
@@ -4776,11 +4618,7 @@ async function radioUploadAction(stationId, filePath, options) {
4776
4618
  headers: { Authorization: `Bearer ${apiKey}` },
4777
4619
  body: formData
4778
4620
  });
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
- }
4621
+ await assertOk(res);
4784
4622
  const data = await res.json();
4785
4623
  if (options.json) {
4786
4624
  console.log(JSON.stringify(data, null, 2));
@@ -4802,7 +4640,7 @@ async function radioUploadAction(stationId, filePath, options) {
4802
4640
 
4803
4641
  // src/index.ts
4804
4642
  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");
4643
+ 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.6.0");
4806
4644
  program2.command("login").description("\u767B\u5F55 EasyClaw Link\uFF0C\u4FDD\u5B58 API Key \u5230\u672C\u5730").action(loginAction);
4807
4645
  program2.command("logout").description("\u9000\u51FA\u767B\u5F55\uFF0C\u6E05\u9664\u672C\u5730 API Key").action(logoutAction);
4808
4646
  program2.command("whoami").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u8D26\u53F7\u4FE1\u606F").option("--json", "JSON \u8F93\u51FA").action(() => whoamiAction());
@@ -4819,7 +4657,7 @@ program2.command("publish [dir]").description("\u53D1\u5E03\u6216\u66F4\u65B0\u6
4819
4657
  program2.command("list").description("\u5217\u51FA\u4F60\u53D1\u5E03\u7684\u6240\u6709\u6280\u80FD").option("--json", "JSON \u8F93\u51FA").action(() => listAction());
4820
4658
  program2.command("validate [dir]").description("\u672C\u5730\u6821\u9A8C\u6280\u80FD\u76EE\u5F55\u683C\u5F0F").action((dir) => validateAction(dir));
4821
4659
  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));
4660
+ 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
4661
  skillCmd.command("view <id>").description("\u67E5\u770B\u6280\u80FD\u8BE6\u60C5").option("--json", "JSON \u8F93\u51FA").action((id, o) => skillViewAction(id, o));
4824
4662
  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
4663
  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.6.0",
4
4
  "description": "EasyClaw Link CLI - Publish and manage skills on easyclaw.link",
5
5
  "main": "dist/index.js",
6
6
  "bin": {