@cstar.help/js 0.12.1 → 0.14.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.
package/dist/index.js CHANGED
@@ -6,102 +6,211 @@ var ErrorType = {
6
6
  NOT_FOUND_ERROR: "not_found_error",
7
7
  CONFLICT_ERROR: "conflict_error",
8
8
  RATE_LIMIT_ERROR: "rate_limit_error",
9
+ FEATURE_NOT_CONFIGURED: "feature_not_configured",
9
10
  API_ERROR: "api_error",
10
11
  SERVER_ERROR: "server_error"
11
12
  };
13
+ var DEFAULT_DOC_URL = "https://cstar.help/developers/api/errors";
14
+ function normalizeArgs(messageOrStatus, maybeBody, defaults) {
15
+ if (typeof messageOrStatus === "string") {
16
+ return {
17
+ statusCode: defaults?.code === "rate_limit_error" ? 429 : 0,
18
+ body: {
19
+ type: defaults?.type ?? ErrorType.API_ERROR,
20
+ code: defaults?.code ?? "sdk_error",
21
+ message: messageOrStatus,
22
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL,
23
+ ...defaults?.feature ? { feature: defaults.feature } : {},
24
+ ...defaults?.enable_url ? { enable_url: defaults.enable_url } : {}
25
+ }
26
+ };
27
+ }
28
+ return {
29
+ statusCode: messageOrStatus,
30
+ body: maybeBody ?? {
31
+ type: defaults?.type ?? ErrorType.API_ERROR,
32
+ code: defaults?.code ?? "unknown_error",
33
+ message: defaults?.message ?? `Request failed with status ${messageOrStatus}`,
34
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL
35
+ }
36
+ };
37
+ }
12
38
  var CStarError = class extends Error {
13
- constructor(statusCode, body) {
14
- super(body.message);
39
+ constructor(messageOrStatus, body) {
40
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body);
41
+ super(resolved.message);
15
42
  this.name = "CStarError";
16
- this.type = body.type;
17
- this.code = body.code;
18
- this.param = body.param;
19
- this.docUrl = body.doc_url;
20
- this.requestId = body.request_id;
43
+ this.type = resolved.type;
44
+ this.code = resolved.code;
45
+ this.param = resolved.param;
46
+ this.docUrl = resolved.doc_url;
47
+ this.requestId = resolved.request_id;
21
48
  this.statusCode = statusCode;
22
49
  }
23
50
  };
24
51
  var CStarAuthenticationError = class extends CStarError {
25
- constructor(statusCode, body) {
26
- super(statusCode, body);
52
+ constructor(messageOrStatus, body) {
53
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
54
+ type: ErrorType.AUTHENTICATION_ERROR,
55
+ code: "authentication_failed",
56
+ doc_url: `${DEFAULT_DOC_URL}#authentication-error`
57
+ });
58
+ super(statusCode, resolved);
27
59
  this.name = "CStarAuthenticationError";
28
60
  }
29
61
  };
30
62
  var CStarRateLimitError = class extends CStarError {
31
- constructor(statusCode, body, retryAfter) {
32
- super(statusCode, body);
63
+ constructor(messageOrStatus, body, retryAfter) {
64
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
65
+ type: ErrorType.RATE_LIMIT_ERROR,
66
+ code: "rate_limit_exceeded",
67
+ doc_url: `${DEFAULT_DOC_URL}#rate-limit-error`
68
+ });
69
+ super(statusCode === 0 ? 429 : statusCode, resolved);
33
70
  this.name = "CStarRateLimitError";
34
- this.retryAfter = retryAfter;
71
+ this.retryAfter = retryAfter ?? 60;
35
72
  }
36
73
  };
37
74
  var CStarNotFoundError = class extends CStarError {
38
- constructor(statusCode, body) {
39
- super(statusCode, body);
75
+ constructor(messageOrStatus, body) {
76
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
77
+ type: ErrorType.NOT_FOUND_ERROR,
78
+ code: "resource_not_found",
79
+ doc_url: `${DEFAULT_DOC_URL}#not-found-error`
80
+ });
81
+ super(statusCode, resolved);
40
82
  this.name = "CStarNotFoundError";
41
83
  }
42
84
  };
43
85
  var CStarValidationError = class extends CStarError {
44
- constructor(statusCode, body) {
45
- super(statusCode, body);
86
+ constructor(messageOrStatus, body) {
87
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
88
+ type: ErrorType.INVALID_REQUEST_ERROR,
89
+ code: "validation_error",
90
+ doc_url: `${DEFAULT_DOC_URL}#validation-error`
91
+ });
92
+ super(statusCode, resolved);
46
93
  this.name = "CStarValidationError";
47
94
  }
48
95
  };
49
96
  var CStarPermissionError = class extends CStarError {
50
- constructor(statusCode, body) {
51
- super(statusCode, body);
97
+ constructor(messageOrStatus, body) {
98
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
99
+ type: ErrorType.PERMISSION_ERROR,
100
+ code: "permission_denied",
101
+ doc_url: `${DEFAULT_DOC_URL}#permission-error`
102
+ });
103
+ super(statusCode, resolved);
52
104
  this.name = "CStarPermissionError";
53
105
  }
54
106
  };
55
107
  var CStarConflictError = class extends CStarError {
56
- constructor(statusCode, body) {
57
- super(statusCode, body);
108
+ constructor(messageOrStatus, body) {
109
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
110
+ type: ErrorType.CONFLICT_ERROR,
111
+ code: "conflict",
112
+ doc_url: `${DEFAULT_DOC_URL}#conflict-error`
113
+ });
114
+ super(statusCode, resolved);
58
115
  this.name = "CStarConflictError";
59
116
  }
60
117
  };
61
118
  var CStarServerError = class extends CStarError {
62
- constructor(statusCode, body) {
63
- super(statusCode, body);
119
+ constructor(messageOrStatus, body) {
120
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
121
+ type: ErrorType.SERVER_ERROR,
122
+ code: "server_error",
123
+ doc_url: `${DEFAULT_DOC_URL}#server-error`
124
+ });
125
+ super(statusCode, resolved);
64
126
  this.name = "CStarServerError";
65
127
  }
66
128
  };
129
+ var CStarFeatureDisabledError = class extends CStarError {
130
+ constructor(messageOrStatus, body) {
131
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
132
+ type: ErrorType.FEATURE_NOT_CONFIGURED,
133
+ code: "feature_not_configured",
134
+ doc_url: `${DEFAULT_DOC_URL}#feature-not-configured`
135
+ });
136
+ super(statusCode, resolved);
137
+ this.name = "CStarFeatureDisabledError";
138
+ this.feature = resolved.feature ?? "unknown";
139
+ this.enableUrl = resolved.enable_url ?? "";
140
+ }
141
+ };
142
+ var CStarRealtimeUnavailableError = class extends CStarError {
143
+ constructor(message) {
144
+ const resolved = message ?? "EventSource is not available in this environment. Real-time subscriptions only work in the browser. Move the call to a client-only path (`useEffect` / `onMount`), or pass an `eventSource` polyfill via the realtime options. See https://cstar.help/developers/sdk/realtime#ssr";
145
+ super(0, {
146
+ type: ErrorType.API_ERROR,
147
+ code: "realtime_unavailable",
148
+ message: resolved,
149
+ doc_url: "https://cstar.help/developers/sdk/realtime#ssr"
150
+ });
151
+ this.name = "CStarRealtimeUnavailableError";
152
+ }
153
+ };
154
+ var STATUS_TO_CLASS = {
155
+ 400: CStarValidationError,
156
+ 401: CStarAuthenticationError,
157
+ 403: CStarPermissionError,
158
+ 404: CStarNotFoundError,
159
+ 409: CStarConflictError,
160
+ 422: CStarValidationError,
161
+ 429: CStarRateLimitError
162
+ };
163
+ var TYPE_TO_CLASS = {
164
+ [ErrorType.AUTHENTICATION_ERROR]: CStarAuthenticationError,
165
+ [ErrorType.PERMISSION_ERROR]: CStarPermissionError,
166
+ [ErrorType.RATE_LIMIT_ERROR]: CStarRateLimitError,
167
+ [ErrorType.NOT_FOUND_ERROR]: CStarNotFoundError,
168
+ [ErrorType.INVALID_REQUEST_ERROR]: CStarValidationError,
169
+ [ErrorType.CONFLICT_ERROR]: CStarConflictError,
170
+ [ErrorType.SERVER_ERROR]: CStarServerError,
171
+ [ErrorType.FEATURE_NOT_CONFIGURED]: CStarFeatureDisabledError
172
+ };
67
173
  function parseError(statusCode, body, retryAfter) {
68
174
  const errorBody = body?.error ?? {
69
175
  type: ErrorType.API_ERROR,
70
176
  code: "unknown_error",
71
177
  message: `Request failed with status ${statusCode}`,
72
- doc_url: "https://cstar.help/developers/api/errors"
178
+ doc_url: DEFAULT_DOC_URL
73
179
  };
74
- switch (errorBody.type) {
75
- case ErrorType.AUTHENTICATION_ERROR:
76
- return new CStarAuthenticationError(statusCode, errorBody);
77
- case ErrorType.PERMISSION_ERROR:
78
- return new CStarPermissionError(statusCode, errorBody);
79
- case ErrorType.RATE_LIMIT_ERROR:
80
- return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
81
- case ErrorType.NOT_FOUND_ERROR:
82
- return new CStarNotFoundError(statusCode, errorBody);
83
- case ErrorType.INVALID_REQUEST_ERROR:
84
- return new CStarValidationError(statusCode, errorBody);
85
- case ErrorType.CONFLICT_ERROR:
86
- return new CStarConflictError(statusCode, errorBody);
87
- case ErrorType.SERVER_ERROR:
88
- return new CStarServerError(statusCode, errorBody);
89
- default:
90
- if (statusCode === 403) return new CStarPermissionError(statusCode, errorBody);
91
- if (statusCode === 409) return new CStarConflictError(statusCode, errorBody);
92
- if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
93
- return new CStarError(statusCode, errorBody);
180
+ if (statusCode === 429) {
181
+ return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
182
+ }
183
+ const StatusCls = STATUS_TO_CLASS[statusCode];
184
+ if (StatusCls) {
185
+ return new StatusCls(statusCode, errorBody);
94
186
  }
187
+ const TypeCls = TYPE_TO_CLASS[errorBody.type];
188
+ if (TypeCls) {
189
+ return new TypeCls(statusCode, errorBody);
190
+ }
191
+ if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
192
+ return new CStarError(statusCode, errorBody);
95
193
  }
96
194
 
97
195
  // src/client.ts
98
- var SDK_VERSION = "0.5.2";
196
+ var SDK_VERSION = "0.14.0";
99
197
  var DEFAULT_BASE_URL = "https://www.cstar.help";
100
198
  var DEFAULT_API_VERSION = "2026-03-01";
101
199
  var DEFAULT_MAX_RETRIES = 3;
102
200
  var DEFAULT_TIMEOUT = 3e4;
103
201
  var HttpClient = class {
104
202
  constructor(config) {
203
+ /**
204
+ * Side-channel metadata from the most recent response. Holds the server
205
+ * `requestId`, `timestamp`, and (for list endpoints) `pagination`. Mutable
206
+ * per-call — read it immediately after awaiting the call.
207
+ *
208
+ * ```ts
209
+ * const ticket = await cstar.tickets.create({...});
210
+ * console.error('Created ticket', ticket.id, 'request:', cstar.lastMeta.requestId);
211
+ * ```
212
+ */
213
+ this.lastMeta = { requestId: null, timestamp: null, pagination: null };
105
214
  if (!config || typeof config.apiKey !== "string" || config.apiKey.length === 0) {
106
215
  throw new Error(
107
216
  "[cStar] CStarClient: apiKey is required. Pass a secret key (sk_live_... / sk_test_...) or a publishable key (pk_live_... / pk_test_...). See https://cstar.help/developers/quickstart"
@@ -132,7 +241,32 @@ var HttpClient = class {
132
241
  * Automatically retries on 5xx errors and rate limits.
133
242
  */
134
243
  async request(method, path, options) {
135
- const url = this.buildUrl(path, options?.query);
244
+ return this._sendRequest(method, this.buildUrl(path, options?.query), options);
245
+ }
246
+ /**
247
+ * Like `request()` but skips the `/api/v1/teams/{teamId}` prefix. Used for
248
+ * the small set of non-team-scoped endpoints (`/auth/whoami`, etc).
249
+ *
250
+ * @internal — exposed to CStarClient for `whoami()` and similar; not part
251
+ * of the documented public surface.
252
+ */
253
+ async requestUnscoped(method, path, options) {
254
+ const baseUrl = this.config.baseUrl + "/api/v1" + path;
255
+ const finalUrl = this.appendQuery(baseUrl, options?.query);
256
+ return this._sendRequest(method, finalUrl, options);
257
+ }
258
+ appendQuery(url, query) {
259
+ if (!query) return url;
260
+ const params = new URLSearchParams();
261
+ for (const [key, value] of Object.entries(query)) {
262
+ if (value === void 0 || value === null) continue;
263
+ if (Array.isArray(value)) for (const v of value) params.append(`${key}[]`, v);
264
+ else params.set(key, String(value));
265
+ }
266
+ const qs = params.toString();
267
+ return qs ? `${url}?${qs}` : url;
268
+ }
269
+ async _sendRequest(method, url, options) {
136
270
  const headers = this.buildHeaders(options?.idempotencyKey);
137
271
  const body = options?.body ? JSON.stringify(options.body) : void 0;
138
272
  let lastError = null;
@@ -163,13 +297,9 @@ var HttpClient = class {
163
297
  if (response.ok) {
164
298
  if (response.status === 204) return void 0;
165
299
  const json = await response.json();
166
- const requestId2 = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
167
- if (requestId2 && json && typeof json === "object" && "meta" in json) {
168
- const existing = json.meta;
169
- const metaBase = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
170
- json.meta = { ...metaBase, requestId: requestId2 };
171
- }
172
- return json;
300
+ const headerRequestId = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
301
+ this.captureMeta(json, headerRequestId);
302
+ return this.unwrapEnvelope(json);
173
303
  }
174
304
  const errorBody = await response.json().catch(() => null);
175
305
  const retryAfter = parseInt(response.headers.get("Retry-After") ?? "", 10) || void 0;
@@ -191,6 +321,51 @@ var HttpClient = class {
191
321
  }
192
322
  throw lastError ?? new Error("Request failed after retries");
193
323
  }
324
+ /**
325
+ * Capture `meta`, `requestId`, and `pagination` from the raw response
326
+ * into `this.lastMeta` before unwrapping the envelope.
327
+ */
328
+ captureMeta(body, headerRequestId) {
329
+ if (body && typeof body === "object" && !Array.isArray(body)) {
330
+ const obj = body;
331
+ const meta = obj.meta && typeof obj.meta === "object" ? obj.meta : null;
332
+ const pagination = obj.pagination && typeof obj.pagination === "object" ? obj.pagination : null;
333
+ this.lastMeta = {
334
+ requestId: headerRequestId ?? (typeof meta?.requestId === "string" ? meta.requestId : null),
335
+ timestamp: typeof meta?.timestamp === "string" ? meta.timestamp : null,
336
+ pagination
337
+ };
338
+ return;
339
+ }
340
+ this.lastMeta = { requestId: headerRequestId, timestamp: null, pagination: null };
341
+ }
342
+ /**
343
+ * Unwrap the legacy `{success, data, meta, pagination?}` envelope.
344
+ * - `{success:true, data: array, pagination}` → `{data, pagination, hasMore}`
345
+ * - `{success:true, data: object}` → `data` (bare resource)
346
+ * - other shapes pass through unchanged so subpath SDKs (chat, library) keep
347
+ * their bespoke shapes for now.
348
+ */
349
+ unwrapEnvelope(body) {
350
+ if (!body || typeof body !== "object" || Array.isArray(body)) return body;
351
+ const obj = body;
352
+ if (obj.success !== true || !("data" in obj)) return body;
353
+ const data = obj.data;
354
+ if (Array.isArray(data)) {
355
+ const pagination = obj.pagination ?? null;
356
+ return {
357
+ data,
358
+ pagination: pagination ?? {
359
+ total: data.length,
360
+ page: 1,
361
+ pageSize: data.length,
362
+ hasMore: false
363
+ },
364
+ hasMore: pagination?.hasMore ?? false
365
+ };
366
+ }
367
+ return data;
368
+ }
194
369
  buildUrl(path, query) {
195
370
  const base = `${this.config.baseUrl}/api/v1/teams/${this.config.teamId}${path}`;
196
371
  if (!query) return base;
@@ -263,6 +438,18 @@ var CStarRealtime = class {
263
438
  this.autoReconnect = options?.autoReconnect ?? true;
264
439
  this.maxRetries = options?.maxRetries ?? 10;
265
440
  this.types = options?.types;
441
+ this.eventSourceCtor = options?.eventSource;
442
+ }
443
+ /**
444
+ * Resolve the EventSource constructor to use for this connection.
445
+ * Returns the explicit polyfill when one was injected, otherwise the
446
+ * global. Returns `undefined` when neither is available — callers must
447
+ * convert to `CStarRealtimeUnavailableError` before touching it.
448
+ */
449
+ resolveEventSource() {
450
+ if (this.eventSourceCtor) return this.eventSourceCtor;
451
+ const fromGlobal = globalThis.EventSource;
452
+ return typeof fromGlobal === "function" ? fromGlobal : void 0;
266
453
  }
267
454
  /** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
268
455
  on(eventType, handler) {
@@ -317,6 +504,9 @@ var CStarRealtime = class {
317
504
  /** Start the SSE connection. Called automatically on first handler registration. */
318
505
  connect() {
319
506
  if (this.eventSource || this.destroyed || this.connecting) return;
507
+ if (!this.resolveEventSource()) {
508
+ throw new CStarRealtimeUnavailableError();
509
+ }
320
510
  this.connecting = true;
321
511
  const headers = this.httpClient.getAuthHeaders();
322
512
  const rawKey = headers.Authorization?.replace("Bearer ", "") ?? "";
@@ -355,7 +545,12 @@ var CStarRealtime = class {
355
545
  }
356
546
  const separator = qs ? "&" : "?";
357
547
  const authenticatedUrl = authToken ? `${url}${separator}token=${authToken}` : url;
358
- this.eventSource = new EventSource(authenticatedUrl);
548
+ const Ctor = this.resolveEventSource();
549
+ if (!Ctor) {
550
+ this.connecting = false;
551
+ throw new CStarRealtimeUnavailableError();
552
+ }
553
+ this.eventSource = new Ctor(authenticatedUrl);
359
554
  this.eventSource.onopen = () => {
360
555
  this.connecting = false;
361
556
  this.retryCount = 0;
@@ -774,6 +969,14 @@ var BaseResource = class {
774
969
  async del(id) {
775
970
  return this.client.request("DELETE", `${this.basePath}/${id}`);
776
971
  }
972
+ /**
973
+ * Delete a resource by ID. Alias for `del()` — kept because IDE
974
+ * auto-complete and documentation both surface `delete` first. Closes
975
+ * NEW-v2-06.
976
+ */
977
+ async delete(id) {
978
+ return this.del(id);
979
+ }
777
980
  /**
778
981
  * Auto-paginating async iterator that yields every item across all pages.
779
982
  *
@@ -1872,7 +2075,12 @@ var GameResource = class {
1872
2075
  async getPlayer(memberId) {
1873
2076
  return this.client.request("GET", `/game/player/${memberId}`);
1874
2077
  }
1875
- /** Get the team leaderboard. */
2078
+ /**
2079
+ * Get the team leaderboard. Returns the unwrapped envelope from the
2080
+ * `/game/leaderboard` endpoint (`{entries, totalParticipants, period, ...}`)
2081
+ * — wider than a flat array because the server includes the period and
2082
+ * a participant count alongside the entries.
2083
+ */
1876
2084
  async leaderboard(params) {
1877
2085
  return this.client.request("GET", "/game/leaderboard", {
1878
2086
  query: params
@@ -1895,12 +2103,12 @@ var GameResource = class {
1895
2103
  */
1896
2104
  subscribeBoss(realtime) {
1897
2105
  const fetchFn = async () => {
1898
- const res = await this.boss.current();
2106
+ const boss = await this.boss.current();
2107
+ const list = boss.active ? [boss] : [];
1899
2108
  return {
1900
- success: true,
1901
- data: res.data.active ? [res.data] : [],
1902
- pagination: { total: res.data.active ? 1 : 0, page: 1, pageSize: 1, hasMore: false },
1903
- meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
2109
+ data: list,
2110
+ pagination: { total: list.length, page: 1, pageSize: 1, hasMore: false },
2111
+ hasMore: false
1904
2112
  };
1905
2113
  };
1906
2114
  return new Subscription(realtime, fetchFn, "/game/boss");
@@ -1911,12 +2119,11 @@ var GameResource = class {
1911
2119
  */
1912
2120
  subscribePlayer(realtime, userId) {
1913
2121
  const fetchFn = async () => {
1914
- const res = await this.player({ userId });
2122
+ const player = await this.player({ userId });
1915
2123
  return {
1916
- success: true,
1917
- data: [res.data],
2124
+ data: [player],
1918
2125
  pagination: { total: 1, page: 1, pageSize: 1, hasMore: false },
1919
- meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
2126
+ hasMore: false
1920
2127
  };
1921
2128
  };
1922
2129
  return new Subscription(realtime, fetchFn, "/game/player");
@@ -1927,14 +2134,12 @@ var GameResource = class {
1927
2134
  */
1928
2135
  subscribeLeaderboard(realtime, params) {
1929
2136
  const fetchFn = async () => {
1930
- const res = await this.leaderboard(params);
1931
- const envelope = res.data;
1932
- const entries = Array.isArray(envelope) ? envelope : envelope?.entries ?? [];
2137
+ const board = await this.leaderboard(params);
2138
+ const entries = board.entries ?? [];
1933
2139
  return {
1934
- success: true,
1935
2140
  data: entries,
1936
2141
  pagination: { total: entries.length, page: 1, pageSize: 100, hasMore: false },
1937
- meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
2142
+ hasMore: false
1938
2143
  };
1939
2144
  };
1940
2145
  return new Subscription(realtime, fetchFn, "/game/leaderboard");
@@ -1945,13 +2150,12 @@ var GameResource = class {
1945
2150
  */
1946
2151
  subscribeActivity(realtime) {
1947
2152
  const fetchFn = async () => {
1948
- const res = await this.activity.list({ limit: 50 });
1949
- const data = res.data.data || [];
2153
+ const feed = await this.activity.list({ limit: 50 });
2154
+ const items = feed.data ?? [];
1950
2155
  return {
1951
- success: true,
1952
- data,
1953
- pagination: { total: data.length, page: 1, pageSize: 50, hasMore: false },
1954
- meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
2156
+ data: items,
2157
+ pagination: { total: items.length, page: 1, pageSize: 50, hasMore: false },
2158
+ hasMore: false
1955
2159
  };
1956
2160
  };
1957
2161
  return new Subscription(realtime, fetchFn, "/game/activity");
@@ -2189,12 +2393,42 @@ var CStarClient = class {
2189
2393
  get isTestMode() {
2190
2394
  return this._httpClient.environment === "test";
2191
2395
  }
2396
+ /**
2397
+ * Validate the configured API key and return the team + key metadata it
2398
+ * resolves to. Useful for confirming the key is wired up correctly during
2399
+ * onboarding (and for AI assistants that want a one-call sanity check).
2400
+ *
2401
+ * Closes F-018.
2402
+ *
2403
+ * ```ts
2404
+ * const me = await cstar.whoami();
2405
+ * console.log(me.teamSlug, me.environment); // 'acme', 'test'
2406
+ * ```
2407
+ */
2408
+ async whoami() {
2409
+ return this._httpClient.requestUnscoped("GET", "/auth/whoami");
2410
+ }
2411
+ /**
2412
+ * Side-channel metadata from the most recent HTTP response. Holds the
2413
+ * server `requestId` (for support escalations), `timestamp`, and the
2414
+ * `pagination` info from list endpoints. Reset on every call — read it
2415
+ * immediately after awaiting the call you care about.
2416
+ *
2417
+ * ```ts
2418
+ * const ticket = await cstar.tickets.create({...});
2419
+ * console.error('Created', ticket.id, 'request:', cstar.lastMeta.requestId);
2420
+ * ```
2421
+ */
2422
+ get lastMeta() {
2423
+ return this._httpClient.lastMeta;
2424
+ }
2192
2425
  };
2193
2426
  export {
2194
2427
  CStarAuthenticationError,
2195
2428
  CStarClient,
2196
2429
  CStarConflictError,
2197
2430
  CStarError,
2431
+ CStarFeatureDisabledError,
2198
2432
  CStarNotFoundError,
2199
2433
  CStarPermissionError,
2200
2434
  CStarRateLimitError,