reskill 1.22.0 → 1.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -3480,6 +3480,65 @@ ${CURSOR_BRIDGE_MARKER}
3480
3480
  }
3481
3481
  }
3482
3482
  // ============================================================================
3483
+ // Internal helpers
3484
+ // ============================================================================
3485
+ /**
3486
+ * Extract a human-readable reason from an error thrown by `fetch()`.
3487
+ *
3488
+ * Node's undici throws a generic `TypeError: fetch failed` and stashes the
3489
+ * real reason in `error.cause`. The chain can be deeper than one level —
3490
+ * e.g. through a corporate proxy:
3491
+ * TypeError: fetch failed
3492
+ * → DOMException { message: 'Request was cancelled' }
3493
+ * → RequestAbortedError { code: 'UND_ERR_ABORTED', message: 'aborted' }
3494
+ *
3495
+ * Walk the `cause` chain and prefer the deepest entry that carries a `code`,
3496
+ * since that's the actionable bit for triage. Falls back to the deepest
3497
+ * available `message`, then to the outermost error's message.
3498
+ *
3499
+ * Produces strings like `UND_ERR_CONNECT_TIMEOUT: Connect Timeout Error`.
3500
+ */ function formatFetchCause(err) {
3501
+ // Cap traversal depth to defend against pathological self-referential chains.
3502
+ const MAX_DEPTH = 8;
3503
+ let bestCode;
3504
+ let bestMessage;
3505
+ let fallbackMessage;
3506
+ const seen = new Set();
3507
+ let current = err;
3508
+ for(let depth = 0; depth < MAX_DEPTH && null != current; depth++){
3509
+ if (seen.has(current)) break;
3510
+ seen.add(current);
3511
+ if ('object' != typeof current) break;
3512
+ const node = current;
3513
+ if ('string' == typeof node.code && node.code) {
3514
+ // Prefer the deepest code, so keep overwriting as we descend.
3515
+ bestCode = node.code;
3516
+ if ('string' == typeof node.message && node.message) bestMessage = node.message;
3517
+ } else if ('string' == typeof node.message && node.message && !fallbackMessage) // Remember the first non-empty message in case no code is ever found.
3518
+ // Skip undici's generic outer wrapper to avoid surfacing "fetch failed".
3519
+ {
3520
+ if ('fetch failed' !== node.message) fallbackMessage = node.message;
3521
+ }
3522
+ current = node.cause;
3523
+ }
3524
+ if (bestCode && bestMessage) return `${bestCode}: ${bestMessage}`;
3525
+ if (bestCode) return bestCode;
3526
+ if (fallbackMessage) return fallbackMessage;
3527
+ return err?.message || String(err);
3528
+ }
3529
+ /**
3530
+ * Render a URL for inclusion in error messages without leaking the query string.
3531
+ * Signed download URLs put the signature in the query, so we strip it.
3532
+ * Returns `<origin><pathname>`, or `<unknown url>` if parsing fails.
3533
+ */ function describeUrl(rawUrl) {
3534
+ try {
3535
+ const u = new URL(rawUrl);
3536
+ return `${u.origin}${u.pathname}`;
3537
+ } catch {
3538
+ return '<unknown url>';
3539
+ }
3540
+ }
3541
+ // ============================================================================
3483
3542
  // RegistryClient Class
3484
3543
  // ============================================================================
3485
3544
  class RegistryClient {
@@ -3498,6 +3557,22 @@ class RegistryClient {
3498
3557
  return `${registry}/api`;
3499
3558
  }
3500
3559
  /**
3560
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
3561
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
3562
+ * {@link RegistryError} that surfaces the underlying `cause` code and
3563
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
3564
+ * which is not actionable for support triage.
3565
+ *
3566
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
3567
+ * still handle them as before.
3568
+ */ async safeFetch(url, init) {
3569
+ try {
3570
+ return await fetch(url, init);
3571
+ } catch (err) {
3572
+ throw new RegistryError(`Network error contacting ${describeUrl(url)}: ${formatFetchCause(err)}`);
3573
+ }
3574
+ }
3575
+ /**
3501
3576
  * Get authorization headers
3502
3577
  */ getAuthHeaders() {
3503
3578
  const headers = {
@@ -3511,7 +3586,7 @@ class RegistryClient {
3511
3586
  * Get current user info (whoami)
3512
3587
  */ async whoami() {
3513
3588
  const url = `${this.getApiBase()}/skill-auth/me`;
3514
- const response = await fetch(url, {
3589
+ const response = await this.safeFetch(url, {
3515
3590
  method: 'GET',
3516
3591
  headers: this.getAuthHeaders()
3517
3592
  });
@@ -3529,7 +3604,7 @@ class RegistryClient {
3529
3604
  * @throws RegistryError if authentication fails
3530
3605
  */ async loginCli() {
3531
3606
  const url = `${this.getApiBase()}/skill-auth/login-cli`;
3532
- const response = await fetch(url, {
3607
+ const response = await this.safeFetch(url, {
3533
3608
  method: 'POST',
3534
3609
  headers: this.getAuthHeaders()
3535
3610
  });
@@ -3585,7 +3660,7 @@ class RegistryClient {
3585
3660
  * @throws RegistryError if skill not found or request failed
3586
3661
  */ async getSkillInfo(skillName) {
3587
3662
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3588
- const response = await fetch(url, {
3663
+ const response = await this.safeFetch(url, {
3589
3664
  method: 'GET',
3590
3665
  headers: this.getAuthHeaders()
3591
3666
  });
@@ -3620,7 +3695,7 @@ class RegistryClient {
3620
3695
  if (void 0 !== options.limit) params.set('limit', String(options.limit));
3621
3696
  if (void 0 !== options.offset) params.set('offset', String(options.offset));
3622
3697
  const url = `${this.getApiBase()}/skills?${params.toString()}`;
3623
- const response = await fetch(url, {
3698
+ const response = await this.safeFetch(url, {
3624
3699
  method: 'GET',
3625
3700
  headers: this.getAuthHeaders()
3626
3701
  });
@@ -3654,7 +3729,7 @@ class RegistryClient {
3654
3729
  if (/^\d+\.\d+\.\d+/.test(version)) return version;
3655
3730
  // Otherwise treat it as a tag and query dist-tags
3656
3731
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3657
- const response = await fetch(url, {
3732
+ const response = await this.safeFetch(url, {
3658
3733
  method: 'GET',
3659
3734
  headers: this.getAuthHeaders()
3660
3735
  });
@@ -3692,7 +3767,7 @@ class RegistryClient {
3692
3767
  // Use redirect: 'manual' to capture x-integrity header from 302 responses.
3693
3768
  // The registry returns a 302 redirect to OSS with the integrity header,
3694
3769
  // which would be lost if fetch auto-follows the redirect.
3695
- const response = await fetch(url, {
3770
+ const response = await this.safeFetch(url, {
3696
3771
  method: 'GET',
3697
3772
  headers: this.getAuthHeaders(),
3698
3773
  redirect: 'manual'
@@ -3702,7 +3777,7 @@ class RegistryClient {
3702
3777
  const integrity = response.headers.get('x-integrity') || '';
3703
3778
  const location = response.headers.get('location');
3704
3779
  if (!location) throw new RegistryError('Missing redirect location in download response', response.status);
3705
- const downloadResponse = await fetch(location);
3780
+ const downloadResponse = await this.safeFetch(location);
3706
3781
  if (!downloadResponse.ok) throw new RegistryError(`Download from storage failed: ${downloadResponse.status}`, downloadResponse.status);
3707
3782
  const arrayBuffer = await downloadResponse.arrayBuffer();
3708
3783
  const tarball = Buffer.from(arrayBuffer);
@@ -3802,7 +3877,7 @@ class RegistryClient {
3802
3877
  });
3803
3878
  formData.append('tarball', tarballBlob, `${skillName.replace('/', '-')}.tgz`);
3804
3879
  // Send request
3805
- const response = await fetch(url, {
3880
+ const response = await this.safeFetch(url, {
3806
3881
  method: 'POST',
3807
3882
  headers: this.getAuthHeaders(),
3808
3883
  body: formData
@@ -3825,7 +3900,7 @@ class RegistryClient {
3825
3900
  path: groupPath
3826
3901
  });
3827
3902
  const url = `${this.getApiBase()}/skill-groups/resolve?${params.toString()}`;
3828
- const response = await fetch(url, {
3903
+ const response = await this.safeFetch(url, {
3829
3904
  method: 'GET',
3830
3905
  headers: this.getAuthHeaders()
3831
3906
  });
@@ -3848,7 +3923,7 @@ class RegistryClient {
3848
3923
  if (options.flat) params.set('flat', 'true');
3849
3924
  const qs = params.toString();
3850
3925
  const url = `${this.getApiBase()}/skill-groups${qs ? `?${qs}` : ''}`;
3851
- const response = await fetch(url, {
3926
+ const response = await this.safeFetch(url, {
3852
3927
  method: 'GET',
3853
3928
  headers: this.getAuthHeaders()
3854
3929
  });
@@ -3866,7 +3941,7 @@ class RegistryClient {
3866
3941
  * @returns Created group
3867
3942
  */ async createGroup(input) {
3868
3943
  const url = `${this.getApiBase()}/skill-groups`;
3869
- const response = await fetch(url, {
3944
+ const response = await this.safeFetch(url, {
3870
3945
  method: 'POST',
3871
3946
  headers: {
3872
3947
  ...this.getAuthHeaders(),
@@ -3891,7 +3966,7 @@ class RegistryClient {
3891
3966
  const params = dryRun ? '?dry_run=true' : '';
3892
3967
  const encodedGroupId = encodeURIComponent(groupId);
3893
3968
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}${params}`;
3894
- const response = await fetch(url, {
3969
+ const response = await this.safeFetch(url, {
3895
3970
  method: 'DELETE',
3896
3971
  headers: this.getAuthHeaders()
3897
3972
  });
@@ -3910,7 +3985,7 @@ class RegistryClient {
3910
3985
  */ async listGroupMembers(groupId) {
3911
3986
  const encodedGroupId = encodeURIComponent(groupId);
3912
3987
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3913
- const response = await fetch(url, {
3988
+ const response = await this.safeFetch(url, {
3914
3989
  method: 'GET',
3915
3990
  headers: this.getAuthHeaders()
3916
3991
  });
@@ -3930,7 +4005,7 @@ class RegistryClient {
3930
4005
  */ async addGroupMembers(groupId, userIds, role = 'developer') {
3931
4006
  const encodedGroupId = encodeURIComponent(groupId);
3932
4007
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3933
- const response = await fetch(url, {
4008
+ const response = await this.safeFetch(url, {
3934
4009
  method: 'POST',
3935
4010
  headers: {
3936
4011
  ...this.getAuthHeaders(),
@@ -3957,7 +4032,7 @@ class RegistryClient {
3957
4032
  });
3958
4033
  const encodedGroupId = encodeURIComponent(groupId);
3959
4034
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members?${params.toString()}`;
3960
- const response = await fetch(url, {
4035
+ const response = await this.safeFetch(url, {
3961
4036
  method: 'DELETE',
3962
4037
  headers: this.getAuthHeaders()
3963
4038
  });
@@ -3975,7 +4050,7 @@ class RegistryClient {
3975
4050
  */ async updateGroupMemberRole(groupId, userId, role) {
3976
4051
  const encodedGroupId = encodeURIComponent(groupId);
3977
4052
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3978
- const response = await fetch(url, {
4053
+ const response = await this.safeFetch(url, {
3979
4054
  method: 'PATCH',
3980
4055
  headers: {
3981
4056
  ...this.getAuthHeaders(),
@@ -110,6 +110,17 @@ export declare class RegistryClient {
110
110
  * @returns Base URL for API calls, e.g., 'https://example.com/api'
111
111
  */
112
112
  private getApiBase;
113
+ /**
114
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
115
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
116
+ * {@link RegistryError} that surfaces the underlying `cause` code and
117
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
118
+ * which is not actionable for support triage.
119
+ *
120
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
121
+ * still handle them as before.
122
+ */
123
+ private safeFetch;
113
124
  /**
114
125
  * Get authorization headers
115
126
  */
@@ -1 +1 @@
1
- {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/core/registry-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/core/registry-client.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3804,6 +3804,65 @@ const SNIPPET_MAX_LENGTH = 120;
3804
3804
  }
3805
3805
  }
3806
3806
  // ============================================================================
3807
+ // Internal helpers
3808
+ // ============================================================================
3809
+ /**
3810
+ * Extract a human-readable reason from an error thrown by `fetch()`.
3811
+ *
3812
+ * Node's undici throws a generic `TypeError: fetch failed` and stashes the
3813
+ * real reason in `error.cause`. The chain can be deeper than one level —
3814
+ * e.g. through a corporate proxy:
3815
+ * TypeError: fetch failed
3816
+ * → DOMException { message: 'Request was cancelled' }
3817
+ * → RequestAbortedError { code: 'UND_ERR_ABORTED', message: 'aborted' }
3818
+ *
3819
+ * Walk the `cause` chain and prefer the deepest entry that carries a `code`,
3820
+ * since that's the actionable bit for triage. Falls back to the deepest
3821
+ * available `message`, then to the outermost error's message.
3822
+ *
3823
+ * Produces strings like `UND_ERR_CONNECT_TIMEOUT: Connect Timeout Error`.
3824
+ */ function formatFetchCause(err) {
3825
+ // Cap traversal depth to defend against pathological self-referential chains.
3826
+ const MAX_DEPTH = 8;
3827
+ let bestCode;
3828
+ let bestMessage;
3829
+ let fallbackMessage;
3830
+ const seen = new Set();
3831
+ let current = err;
3832
+ for(let depth = 0; depth < MAX_DEPTH && null != current; depth++){
3833
+ if (seen.has(current)) break;
3834
+ seen.add(current);
3835
+ if ('object' != typeof current) break;
3836
+ const node = current;
3837
+ if ('string' == typeof node.code && node.code) {
3838
+ // Prefer the deepest code, so keep overwriting as we descend.
3839
+ bestCode = node.code;
3840
+ if ('string' == typeof node.message && node.message) bestMessage = node.message;
3841
+ } else if ('string' == typeof node.message && node.message && !fallbackMessage) // Remember the first non-empty message in case no code is ever found.
3842
+ // Skip undici's generic outer wrapper to avoid surfacing "fetch failed".
3843
+ {
3844
+ if ('fetch failed' !== node.message) fallbackMessage = node.message;
3845
+ }
3846
+ current = node.cause;
3847
+ }
3848
+ if (bestCode && bestMessage) return `${bestCode}: ${bestMessage}`;
3849
+ if (bestCode) return bestCode;
3850
+ if (fallbackMessage) return fallbackMessage;
3851
+ return err?.message || String(err);
3852
+ }
3853
+ /**
3854
+ * Render a URL for inclusion in error messages without leaking the query string.
3855
+ * Signed download URLs put the signature in the query, so we strip it.
3856
+ * Returns `<origin><pathname>`, or `<unknown url>` if parsing fails.
3857
+ */ function describeUrl(rawUrl) {
3858
+ try {
3859
+ const u = new URL(rawUrl);
3860
+ return `${u.origin}${u.pathname}`;
3861
+ } catch {
3862
+ return '<unknown url>';
3863
+ }
3864
+ }
3865
+ // ============================================================================
3807
3866
  // RegistryClient Class
3808
3867
  // ============================================================================
3809
3868
  class RegistryClient {
@@ -3822,6 +3881,22 @@ class RegistryClient {
3822
3881
  return `${registry}/api`;
3823
3882
  }
3824
3883
  /**
3884
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
3885
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
3886
+ * {@link RegistryError} that surfaces the underlying `cause` code and
3887
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
3888
+ * which is not actionable for support triage.
3889
+ *
3890
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
3891
+ * still handle them as before.
3892
+ */ async safeFetch(url, init) {
3893
+ try {
3894
+ return await fetch(url, init);
3895
+ } catch (err) {
3896
+ throw new RegistryError(`Network error contacting ${describeUrl(url)}: ${formatFetchCause(err)}`);
3897
+ }
3898
+ }
3899
+ /**
3825
3900
  * Get authorization headers
3826
3901
  */ getAuthHeaders() {
3827
3902
  const headers = {
@@ -3835,7 +3910,7 @@ class RegistryClient {
3835
3910
  * Get current user info (whoami)
3836
3911
  */ async whoami() {
3837
3912
  const url = `${this.getApiBase()}/skill-auth/me`;
3838
- const response = await fetch(url, {
3913
+ const response = await this.safeFetch(url, {
3839
3914
  method: 'GET',
3840
3915
  headers: this.getAuthHeaders()
3841
3916
  });
@@ -3853,7 +3928,7 @@ class RegistryClient {
3853
3928
  * @throws RegistryError if authentication fails
3854
3929
  */ async loginCli() {
3855
3930
  const url = `${this.getApiBase()}/skill-auth/login-cli`;
3856
- const response = await fetch(url, {
3931
+ const response = await this.safeFetch(url, {
3857
3932
  method: 'POST',
3858
3933
  headers: this.getAuthHeaders()
3859
3934
  });
@@ -3909,7 +3984,7 @@ class RegistryClient {
3909
3984
  * @throws RegistryError if skill not found or request failed
3910
3985
  */ async getSkillInfo(skillName) {
3911
3986
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3912
- const response = await fetch(url, {
3987
+ const response = await this.safeFetch(url, {
3913
3988
  method: 'GET',
3914
3989
  headers: this.getAuthHeaders()
3915
3990
  });
@@ -3944,7 +4019,7 @@ class RegistryClient {
3944
4019
  if (void 0 !== options.limit) params.set('limit', String(options.limit));
3945
4020
  if (void 0 !== options.offset) params.set('offset', String(options.offset));
3946
4021
  const url = `${this.getApiBase()}/skills?${params.toString()}`;
3947
- const response = await fetch(url, {
4022
+ const response = await this.safeFetch(url, {
3948
4023
  method: 'GET',
3949
4024
  headers: this.getAuthHeaders()
3950
4025
  });
@@ -3978,7 +4053,7 @@ class RegistryClient {
3978
4053
  if (/^\d+\.\d+\.\d+/.test(version)) return version;
3979
4054
  // Otherwise treat it as a tag and query dist-tags
3980
4055
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3981
- const response = await fetch(url, {
4056
+ const response = await this.safeFetch(url, {
3982
4057
  method: 'GET',
3983
4058
  headers: this.getAuthHeaders()
3984
4059
  });
@@ -4016,7 +4091,7 @@ class RegistryClient {
4016
4091
  // Use redirect: 'manual' to capture x-integrity header from 302 responses.
4017
4092
  // The registry returns a 302 redirect to OSS with the integrity header,
4018
4093
  // which would be lost if fetch auto-follows the redirect.
4019
- const response = await fetch(url, {
4094
+ const response = await this.safeFetch(url, {
4020
4095
  method: 'GET',
4021
4096
  headers: this.getAuthHeaders(),
4022
4097
  redirect: 'manual'
@@ -4026,7 +4101,7 @@ class RegistryClient {
4026
4101
  const integrity = response.headers.get('x-integrity') || '';
4027
4102
  const location = response.headers.get('location');
4028
4103
  if (!location) throw new RegistryError('Missing redirect location in download response', response.status);
4029
- const downloadResponse = await fetch(location);
4104
+ const downloadResponse = await this.safeFetch(location);
4030
4105
  if (!downloadResponse.ok) throw new RegistryError(`Download from storage failed: ${downloadResponse.status}`, downloadResponse.status);
4031
4106
  const arrayBuffer = await downloadResponse.arrayBuffer();
4032
4107
  const tarball = Buffer.from(arrayBuffer);
@@ -4126,7 +4201,7 @@ class RegistryClient {
4126
4201
  });
4127
4202
  formData.append('tarball', tarballBlob, `${skillName.replace('/', '-')}.tgz`);
4128
4203
  // Send request
4129
- const response = await fetch(url, {
4204
+ const response = await this.safeFetch(url, {
4130
4205
  method: 'POST',
4131
4206
  headers: this.getAuthHeaders(),
4132
4207
  body: formData
@@ -4149,7 +4224,7 @@ class RegistryClient {
4149
4224
  path: groupPath
4150
4225
  });
4151
4226
  const url = `${this.getApiBase()}/skill-groups/resolve?${params.toString()}`;
4152
- const response = await fetch(url, {
4227
+ const response = await this.safeFetch(url, {
4153
4228
  method: 'GET',
4154
4229
  headers: this.getAuthHeaders()
4155
4230
  });
@@ -4172,7 +4247,7 @@ class RegistryClient {
4172
4247
  if (options.flat) params.set('flat', 'true');
4173
4248
  const qs = params.toString();
4174
4249
  const url = `${this.getApiBase()}/skill-groups${qs ? `?${qs}` : ''}`;
4175
- const response = await fetch(url, {
4250
+ const response = await this.safeFetch(url, {
4176
4251
  method: 'GET',
4177
4252
  headers: this.getAuthHeaders()
4178
4253
  });
@@ -4190,7 +4265,7 @@ class RegistryClient {
4190
4265
  * @returns Created group
4191
4266
  */ async createGroup(input) {
4192
4267
  const url = `${this.getApiBase()}/skill-groups`;
4193
- const response = await fetch(url, {
4268
+ const response = await this.safeFetch(url, {
4194
4269
  method: 'POST',
4195
4270
  headers: {
4196
4271
  ...this.getAuthHeaders(),
@@ -4215,7 +4290,7 @@ class RegistryClient {
4215
4290
  const params = dryRun ? '?dry_run=true' : '';
4216
4291
  const encodedGroupId = encodeURIComponent(groupId);
4217
4292
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}${params}`;
4218
- const response = await fetch(url, {
4293
+ const response = await this.safeFetch(url, {
4219
4294
  method: 'DELETE',
4220
4295
  headers: this.getAuthHeaders()
4221
4296
  });
@@ -4234,7 +4309,7 @@ class RegistryClient {
4234
4309
  */ async listGroupMembers(groupId) {
4235
4310
  const encodedGroupId = encodeURIComponent(groupId);
4236
4311
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4237
- const response = await fetch(url, {
4312
+ const response = await this.safeFetch(url, {
4238
4313
  method: 'GET',
4239
4314
  headers: this.getAuthHeaders()
4240
4315
  });
@@ -4254,7 +4329,7 @@ class RegistryClient {
4254
4329
  */ async addGroupMembers(groupId, userIds, role = 'developer') {
4255
4330
  const encodedGroupId = encodeURIComponent(groupId);
4256
4331
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4257
- const response = await fetch(url, {
4332
+ const response = await this.safeFetch(url, {
4258
4333
  method: 'POST',
4259
4334
  headers: {
4260
4335
  ...this.getAuthHeaders(),
@@ -4281,7 +4356,7 @@ class RegistryClient {
4281
4356
  });
4282
4357
  const encodedGroupId = encodeURIComponent(groupId);
4283
4358
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members?${params.toString()}`;
4284
- const response = await fetch(url, {
4359
+ const response = await this.safeFetch(url, {
4285
4360
  method: 'DELETE',
4286
4361
  headers: this.getAuthHeaders()
4287
4362
  });
@@ -4299,7 +4374,7 @@ class RegistryClient {
4299
4374
  */ async updateGroupMemberRole(groupId, userId, role) {
4300
4375
  const encodedGroupId = encodeURIComponent(groupId);
4301
4376
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4302
- const response = await fetch(url, {
4377
+ const response = await this.safeFetch(url, {
4303
4378
  method: 'PATCH',
4304
4379
  headers: {
4305
4380
  ...this.getAuthHeaders(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reskill",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "description": "AI Skills Package Manager - Git-based skills management for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",