@space3-npm/cybersoul-client 1.4.9 → 1.4.12

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/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { InteractRequestType, } from "./types.js";
2
2
  import { robustJsonParse } from "./utils/json.utils.js";
3
3
  import { GenericLLMProvider } from "./llm.provider.js";
4
+ import { CyberSoulApiError, CyberSoulAuthError, CyberSoulError, CyberSoulInsufficientPointsError, CyberSoulNetworkError, CyberSoulTimeoutError, CyberSoulWalletError, } from "./errors.js";
4
5
  export class CyberSoulClient {
5
6
  config;
6
7
  llm;
@@ -47,10 +48,12 @@ export class CyberSoulClient {
47
48
  }
48
49
  catch (error) {
49
50
  if (error instanceof Error && error.name === "AbortError") {
50
- lastError = new Error(`Request timed out after ${this.requestTimeoutMs}ms: ${method} ${endpoint}`);
51
+ lastError = new CyberSoulTimeoutError(endpoint, method, this.requestTimeoutMs);
51
52
  }
52
53
  else {
53
- lastError = error;
54
+ lastError = new CyberSoulNetworkError(endpoint, method, error instanceof Error
55
+ ? `Network request failed: ${method} ${endpoint}: ${error.message}`
56
+ : `Network request failed: ${method} ${endpoint}`, { cause: error });
54
57
  }
55
58
  if (attempt >= retryLimit) {
56
59
  throw lastError;
@@ -60,14 +63,34 @@ export class CyberSoulClient {
60
63
  clearTimeout(timeout);
61
64
  }
62
65
  }
66
+ // Defensive: the loop above either returns a Response, throws the
67
+ // wrapped network error, or continues to the next attempt. Reaching
68
+ // this point means the retry budget was exhausted without ever
69
+ // populating `lastError` (logically unreachable, but TypeScript
70
+ // cannot prove that).
63
71
  throw lastError instanceof Error
64
72
  ? lastError
65
- : new Error("Request failed unexpectedly");
73
+ : new CyberSoulNetworkError(endpoint, method, `Request failed unexpectedly: ${method} ${endpoint}`);
66
74
  }
67
75
  async fetchRemoteState() {
68
- const res = await this.apiFetch("/api/v1/cyber-soul/state");
69
- if (!res.ok)
70
- throw new Error("Failed to fetch character state");
76
+ const endpoint = "/api/v1/cyber-soul/state";
77
+ const res = await this.apiFetch(endpoint);
78
+ if (!res.ok) {
79
+ let body;
80
+ try {
81
+ body = await res.json();
82
+ }
83
+ catch {
84
+ body = undefined;
85
+ }
86
+ const detail = (body && typeof body === "object" && "error" in body
87
+ ? String(body.error)
88
+ : undefined) ?? `HTTP ${res.status}`;
89
+ if (res.status === 401 || res.status === 403) {
90
+ throw new CyberSoulAuthError(endpoint, "GET", res.status, `Character credential rejected by backend (${detail}). The character may have been deleted.`, body);
91
+ }
92
+ throw new CyberSoulApiError(endpoint, "GET", res.status, `Failed to fetch character state: ${detail}`, body);
93
+ }
71
94
  const json = await res.json();
72
95
  return json.data;
73
96
  }
@@ -99,7 +122,8 @@ export class CyberSoulClient {
99
122
  return availableOutfits;
100
123
  }
101
124
  async generatePrimitive(type, payload) {
102
- const res = await this.apiFetch(`/api/v1/cyber-soul/${type}/generate`, {
125
+ const endpoint = `/api/v1/cyber-soul/${type}/generate`;
126
+ const res = await this.apiFetch(endpoint, {
103
127
  method: "POST",
104
128
  body: JSON.stringify(payload),
105
129
  });
@@ -110,9 +134,23 @@ export class CyberSoulClient {
110
134
  }
111
135
  catch (e) { }
112
136
  const msg = errData?.message || errData?.error || `Status ${res.status}`;
113
- const err = new Error(`Failed to generate ${type}: ${msg}`);
114
- err.code = errData?.code || "UNKNOWN_ERROR";
115
- throw err;
137
+ const code = errData?.code || "UNKNOWN_ERROR";
138
+ const detailedMessage = `Failed to generate ${type}: ${msg}`;
139
+ if (res.status === 402 || code === "INSUFFICIENT_POINTS") {
140
+ throw new CyberSoulInsufficientPointsError(endpoint, "POST", res.status, detailedMessage, errData, code);
141
+ }
142
+ if (code === "WALLET_DEDUCTION_ERROR") {
143
+ throw new CyberSoulWalletError(endpoint, "POST", res.status, detailedMessage, errData, code);
144
+ }
145
+ if (res.status === 401 || res.status === 403) {
146
+ throw new CyberSoulAuthError(endpoint, "POST", res.status, detailedMessage, errData);
147
+ }
148
+ const apiErr = new CyberSoulApiError(endpoint, "POST", res.status, detailedMessage, errData);
149
+ // Preserve the legacy duck-typed `code` field so existing callers
150
+ // that branch on `e.code` (including this SDK's own `interact()`
151
+ // mediaTasks catch block) keep working unchanged.
152
+ apiErr.code = code;
153
+ throw apiErr;
116
154
  }
117
155
  return res.json();
118
156
  }
@@ -764,6 +802,14 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
764
802
  };
765
803
  }
766
804
  catch (error) {
805
+ // Typed SDK errors (insufficient points, wallet failure, auth, etc.)
806
+ // are part of the public contract — let callers branch on
807
+ // `instanceof` instead of string-sniffing a generic status:"error"
808
+ // envelope. Only truly-unexpected throws fall back to the legacy
809
+ // envelope so we don't break callers that don't yet handle throws.
810
+ if (error instanceof CyberSoulError) {
811
+ throw error;
812
+ }
767
813
  console.error("[CyberSoulClient] Interface Error: ", error);
768
814
  return {
769
815
  status: "error",
@@ -1016,6 +1062,10 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
1016
1062
  };
1017
1063
  }
1018
1064
  catch (error) {
1065
+ // Mirror `interact()`: preserve typed SDK errors for the caller.
1066
+ if (error instanceof CyberSoulError) {
1067
+ throw error;
1068
+ }
1019
1069
  console.error("[CyberSoulClient] Proactive Interact Error: ", error);
1020
1070
  return { status: "error", error: error.message };
1021
1071
  }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Typed error hierarchy thrown by the CyberSoul SDK.
3
+ *
4
+ * Consumers should branch on `instanceof` rather than parsing error
5
+ * messages — message strings are not a stable API.
6
+ */
7
+ export declare class CyberSoulError extends Error {
8
+ /** Stable discriminator usable in serialized logs / event payloads. */
9
+ readonly kind: string;
10
+ constructor(kind: string, message: string, options?: {
11
+ cause?: unknown;
12
+ });
13
+ }
14
+ /**
15
+ * The underlying fetch call never produced a response (DNS failure,
16
+ * connection refused, TLS error, offline, etc.). The remote service may
17
+ * or may not have processed the request; callers should treat this as a
18
+ * transient failure and retry later when connectivity is restored.
19
+ */
20
+ export declare class CyberSoulNetworkError extends CyberSoulError {
21
+ readonly endpoint: string;
22
+ readonly method: string;
23
+ constructor(endpoint: string, method: string, message: string, options?: {
24
+ cause?: unknown;
25
+ });
26
+ }
27
+ /**
28
+ * The request was aborted because it exceeded the configured
29
+ * `requestTimeoutMs`. A specialization of network error — callers that
30
+ * want to distinguish "no connection" from "slow connection" can use
31
+ * `instanceof CyberSoulTimeoutError`.
32
+ */
33
+ export declare class CyberSoulTimeoutError extends CyberSoulNetworkError {
34
+ readonly timeoutMs: number;
35
+ constructor(endpoint: string, method: string, timeoutMs: number);
36
+ }
37
+ /**
38
+ * The backend returned a non-2xx HTTP response. Callers can inspect
39
+ * `status` to decide how to react (e.g. surface a user-facing message
40
+ * for 4xx vs. retry on 5xx).
41
+ */
42
+ export declare class CyberSoulApiError extends CyberSoulError {
43
+ readonly status: number;
44
+ readonly endpoint: string;
45
+ readonly method: string;
46
+ /** Parsed JSON body when available, otherwise `undefined`. */
47
+ readonly body?: unknown;
48
+ constructor(endpoint: string, method: string, status: number, message: string, body?: unknown, kind?: string);
49
+ }
50
+ /**
51
+ * The backend rejected the SDK's character credential (HTTP 401/403 on
52
+ * a character-scoped endpoint). The most common cause is that the
53
+ * character profile bound to this `characterKey` has been deleted on
54
+ * the backend; the binding is effectively terminal for this client.
55
+ */
56
+ export declare class CyberSoulAuthError extends CyberSoulApiError {
57
+ constructor(endpoint: string, method: string, status: number, message: string, body?: unknown);
58
+ }
59
+ /**
60
+ * The backend rejected a paid action because the user's wallet does
61
+ * not have enough points to cover it (HTTP 402 / `INSUFFICIENT_POINTS`).
62
+ * Callers should surface a top-up prompt rather than retrying.
63
+ */
64
+ export declare class CyberSoulInsufficientPointsError extends CyberSoulApiError {
65
+ /**
66
+ * Backend-supplied machine code. Always `"INSUFFICIENT_POINTS"` today;
67
+ * kept as a field so future variants (e.g. per-feature paywalls) can
68
+ * piggy-back on the same class.
69
+ */
70
+ readonly code: string;
71
+ constructor(endpoint: string, method: string, status: number, message: string, body?: unknown, code?: string);
72
+ }
73
+ /**
74
+ * The wallet-deduction call failed for a reason *other than* insufficient
75
+ * balance (e.g. wallet service unavailable, accounting bug). Distinct from
76
+ * `CyberSoulInsufficientPointsError` because the user can't fix this by
77
+ * topping up — it's an upstream infrastructure issue.
78
+ */
79
+ export declare class CyberSoulWalletError extends CyberSoulApiError {
80
+ readonly code: string;
81
+ constructor(endpoint: string, method: string, status: number, message: string, body?: unknown, code?: string);
82
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Typed error hierarchy thrown by the CyberSoul SDK.
3
+ *
4
+ * Consumers should branch on `instanceof` rather than parsing error
5
+ * messages — message strings are not a stable API.
6
+ */
7
+ export class CyberSoulError extends Error {
8
+ /** Stable discriminator usable in serialized logs / event payloads. */
9
+ kind;
10
+ constructor(kind, message, options) {
11
+ super(message);
12
+ this.name = new.target.name;
13
+ this.kind = kind;
14
+ if (options?.cause !== undefined) {
15
+ // ES2022 `cause` field. We assign defensively so older targets
16
+ // don't drop it on the floor.
17
+ this.cause = options.cause;
18
+ }
19
+ // Preserve prototype chain for downlevel-transpiled `extends`.
20
+ Object.setPrototypeOf(this, new.target.prototype);
21
+ }
22
+ }
23
+ /**
24
+ * The underlying fetch call never produced a response (DNS failure,
25
+ * connection refused, TLS error, offline, etc.). The remote service may
26
+ * or may not have processed the request; callers should treat this as a
27
+ * transient failure and retry later when connectivity is restored.
28
+ */
29
+ export class CyberSoulNetworkError extends CyberSoulError {
30
+ endpoint;
31
+ method;
32
+ constructor(endpoint, method, message, options) {
33
+ super("network", message, options);
34
+ this.endpoint = endpoint;
35
+ this.method = method;
36
+ }
37
+ }
38
+ /**
39
+ * The request was aborted because it exceeded the configured
40
+ * `requestTimeoutMs`. A specialization of network error — callers that
41
+ * want to distinguish "no connection" from "slow connection" can use
42
+ * `instanceof CyberSoulTimeoutError`.
43
+ */
44
+ export class CyberSoulTimeoutError extends CyberSoulNetworkError {
45
+ timeoutMs;
46
+ constructor(endpoint, method, timeoutMs) {
47
+ super(endpoint, method, `Request timed out after ${timeoutMs}ms: ${method} ${endpoint}`);
48
+ this.timeoutMs = timeoutMs;
49
+ }
50
+ }
51
+ /**
52
+ * The backend returned a non-2xx HTTP response. Callers can inspect
53
+ * `status` to decide how to react (e.g. surface a user-facing message
54
+ * for 4xx vs. retry on 5xx).
55
+ */
56
+ export class CyberSoulApiError extends CyberSoulError {
57
+ status;
58
+ endpoint;
59
+ method;
60
+ /** Parsed JSON body when available, otherwise `undefined`. */
61
+ body;
62
+ constructor(endpoint, method, status, message, body, kind = "api") {
63
+ super(kind, message);
64
+ this.status = status;
65
+ this.endpoint = endpoint;
66
+ this.method = method;
67
+ this.body = body;
68
+ }
69
+ }
70
+ /**
71
+ * The backend rejected the SDK's character credential (HTTP 401/403 on
72
+ * a character-scoped endpoint). The most common cause is that the
73
+ * character profile bound to this `characterKey` has been deleted on
74
+ * the backend; the binding is effectively terminal for this client.
75
+ */
76
+ export class CyberSoulAuthError extends CyberSoulApiError {
77
+ constructor(endpoint, method, status, message, body) {
78
+ super(endpoint, method, status, message, body, "auth");
79
+ }
80
+ }
81
+ /**
82
+ * The backend rejected a paid action because the user's wallet does
83
+ * not have enough points to cover it (HTTP 402 / `INSUFFICIENT_POINTS`).
84
+ * Callers should surface a top-up prompt rather than retrying.
85
+ */
86
+ export class CyberSoulInsufficientPointsError extends CyberSoulApiError {
87
+ /**
88
+ * Backend-supplied machine code. Always `"INSUFFICIENT_POINTS"` today;
89
+ * kept as a field so future variants (e.g. per-feature paywalls) can
90
+ * piggy-back on the same class.
91
+ */
92
+ code;
93
+ constructor(endpoint, method, status, message, body, code = "INSUFFICIENT_POINTS") {
94
+ super(endpoint, method, status, message, body, "insufficient-points");
95
+ this.code = code;
96
+ }
97
+ }
98
+ /**
99
+ * The wallet-deduction call failed for a reason *other than* insufficient
100
+ * balance (e.g. wallet service unavailable, accounting bug). Distinct from
101
+ * `CyberSoulInsufficientPointsError` because the user can't fix this by
102
+ * topping up — it's an upstream infrastructure issue.
103
+ */
104
+ export class CyberSoulWalletError extends CyberSoulApiError {
105
+ code;
106
+ constructor(endpoint, method, status, message, body, code = "WALLET_DEDUCTION_ERROR") {
107
+ super(endpoint, method, status, message, body, "wallet");
108
+ this.code = code;
109
+ }
110
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './client.js';
3
3
  export * from './llm.provider.js';
4
+ export * from './errors.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './client.js';
3
3
  export * from './llm.provider.js';
4
+ export * from './errors.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@space3-npm/cybersoul-client",
3
- "version": "1.4.9",
3
+ "version": "1.4.12",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",