@parall/sdk 1.23.0 → 1.25.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/sdk",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "description": "TypeScript client SDK for Parall — REST + WebSocket client, shared types",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ENDPOINTS } from './constants.js';
2
2
  import type {
3
3
  AuthTokens,
4
+ AvatarUploadResponse,
4
5
  RegisterRequest,
5
6
  RegisterResponse,
6
7
  LoginRequest,
@@ -22,6 +23,7 @@ import type {
22
23
  PatchMessageRequest,
23
24
  PaginatedResponse,
24
25
  Approval,
26
+ CreateApprovalRequest,
25
27
  PresignResponse,
26
28
  PresignUploadRequest,
27
29
  FileUrlResponse,
@@ -110,6 +112,15 @@ import type {
110
112
  UpdateAgentRequest,
111
113
  UpdateChatMemberRequest,
112
114
  UnreadEntry,
115
+ BillingSummary,
116
+ CreditTransaction,
117
+ CreateCheckoutRequest,
118
+ CreateCheckoutResponse,
119
+ CreateSetupIntentResponse,
120
+ AutoReloadSettings,
121
+ UpdateAutoReloadRequest,
122
+ ComputePricing,
123
+ ComputePricingResponse,
113
124
  } from './types.js';
114
125
 
115
126
  export interface ParallClientOptions {
@@ -287,11 +298,15 @@ export class ParallClient {
287
298
  }
288
299
 
289
300
  if (!res.ok) {
290
- const errorBody = await res.json().catch(() => ({}));
301
+ const rawErrorBody = await res.json().catch(() => ({}));
302
+ const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
291
303
  const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
292
304
  const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
293
305
  const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
294
- throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
306
+ const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
307
+ const { error: _e, code: _c, message: _m, ...extras } = errorBody;
308
+ if (Object.keys(extras).length > 0) apiError.extras = extras;
309
+ throw apiError;
295
310
  }
296
311
 
297
312
  if (res.status === 204) return undefined as T;
@@ -355,11 +370,15 @@ export class ParallClient {
355
370
  }
356
371
 
357
372
  if (!res.ok) {
358
- const errorBody = await res.json().catch(() => ({}));
373
+ const rawErrorBody = await res.json().catch(() => ({}));
374
+ const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
359
375
  const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
360
376
  const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
361
377
  const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
362
- throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
378
+ const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
379
+ const { error: _e, code: _c, message: _m, ...extras } = errorBody;
380
+ if (Object.keys(extras).length > 0) apiError.extras = extras;
381
+ throw apiError;
363
382
  }
364
383
 
365
384
  if (res.status === 204) return undefined as T;
@@ -416,6 +435,30 @@ export class ParallClient {
416
435
  return this.request('PATCH', ENDPOINTS.USERS_ME, data);
417
436
  }
418
437
 
438
+ async deleteAccount(): Promise<void> {
439
+ return this.request('DELETE', ENDPOINTS.USERS_ME);
440
+ }
441
+
442
+ async uploadAvatar(file: File | Blob): Promise<AvatarUploadResponse> {
443
+ const fd = new FormData();
444
+ fd.append('file', file);
445
+ return this.multipartRequest('POST', ENDPOINTS.USER_AVATAR, fd);
446
+ }
447
+
448
+ async deleteAvatar(): Promise<void> {
449
+ return this.request('DELETE', ENDPOINTS.USER_AVATAR);
450
+ }
451
+
452
+ async uploadAgentAvatar(orgId: string, agentId: string, file: File | Blob): Promise<AvatarUploadResponse> {
453
+ const fd = new FormData();
454
+ fd.append('file', file);
455
+ return this.multipartRequest('POST', ENDPOINTS.AGENT_AVATAR(orgId, agentId), fd);
456
+ }
457
+
458
+ async deleteAgentAvatar(orgId: string, agentId: string): Promise<void> {
459
+ return this.request('DELETE', ENDPOINTS.AGENT_AVATAR(orgId, agentId));
460
+ }
461
+
419
462
  async getUser(id: string): Promise<User> {
420
463
  return this.request('GET', ENDPOINTS.USER(id));
421
464
  }
@@ -680,15 +723,32 @@ export class ParallClient {
680
723
 
681
724
  // ---- Approvals ----
682
725
 
726
+ async getApproval(id: string): Promise<Approval> {
727
+ return this.request('GET', ENDPOINTS.APPROVAL(id));
728
+ }
729
+
730
+ async requestApproval(orgId: string, req: CreateApprovalRequest): Promise<Approval> {
731
+ return this.request('POST', ENDPOINTS.APPROVAL_REQUESTS(orgId), req);
732
+ }
733
+
683
734
  async decideApproval(id: string, decision: 'approve' | 'reject'): Promise<Approval> {
684
735
  return this.request('POST', ENDPOINTS.APPROVAL_DECIDE(id), { decision });
685
736
  }
686
737
 
738
+ async cancelApproval(id: string): Promise<void> {
739
+ return this.request('POST', ENDPOINTS.APPROVAL_CANCEL(id));
740
+ }
741
+
687
742
  async getPendingApprovals(): Promise<Approval[]> {
688
743
  const res = await this.request<{ data: Approval[] }>('GET', ENDPOINTS.APPROVALS_PENDING);
689
744
  return res.data;
690
745
  }
691
746
 
747
+ async getApprovableActions(): Promise<string[]> {
748
+ const res = await this.request<{ data: string[] }>('GET', ENDPOINTS.APPROVALS_ACTIONS);
749
+ return res.data;
750
+ }
751
+
692
752
  // ---- Agents (org-scoped) ----
693
753
 
694
754
  async createAgent(orgId: string, req: CreateAgentRequest): Promise<CreateAgentResponse> {
@@ -945,6 +1005,10 @@ export class ParallClient {
945
1005
  return this.request('PATCH', ENDPOINTS.INBOX_ITEM_READ(orgId, id));
946
1006
  }
947
1007
 
1008
+ async markInboxUnread(orgId: string, id: string): Promise<void> {
1009
+ return this.request('PATCH', ENDPOINTS.INBOX_ITEM_UNREAD(orgId, id));
1010
+ }
1011
+
948
1012
  async archiveInboxItem(orgId: string, id: string): Promise<void> {
949
1013
  return this.request('PATCH', ENDPOINTS.INBOX_ITEM_ARCHIVE(orgId, id));
950
1014
  }
@@ -1012,11 +1076,15 @@ export class ParallClient {
1012
1076
  if (res.status === 304) return null;
1013
1077
 
1014
1078
  if (!res.ok) {
1015
- const errorBody = await res.json().catch(() => ({}));
1079
+ const rawErrorBody = await res.json().catch(() => ({}));
1080
+ const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
1016
1081
  const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
1017
1082
  const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
1018
1083
  const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
1019
- throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
1084
+ const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
1085
+ const { error: _e, code: _c, message: _m, ...extras } = errorBody;
1086
+ if (Object.keys(extras).length > 0) apiError.extras = extras;
1087
+ throw apiError;
1020
1088
  }
1021
1089
 
1022
1090
  return res.json() as Promise<PlatformConfigResponse>;
@@ -1523,6 +1591,61 @@ export class ParallClient {
1523
1591
  async getFeatureFlags(orgId: string): Promise<FeatureFlagsResponse> {
1524
1592
  return this.request('GET', ENDPOINTS.FEATURE_FLAGS(orgId));
1525
1593
  }
1594
+
1595
+ // ---- Billing & Credits (org-scoped) ----
1596
+
1597
+ async getBilling(orgId: string): Promise<BillingSummary> {
1598
+ return this.request('GET', ENDPOINTS.BILLING(orgId));
1599
+ }
1600
+
1601
+ async listBillingTransactions(orgId: string, opts?: { cursor?: string; limit?: number; types?: string[] }): Promise<{ items: CreditTransaction[]; next_cursor: string | null }> {
1602
+ const params = new URLSearchParams();
1603
+ if (opts?.cursor) params.set('cursor', opts.cursor);
1604
+ if (opts?.limit) params.set('limit', String(opts.limit));
1605
+ if (opts?.types?.length) params.set('types', opts.types.join(','));
1606
+ const qs = params.toString();
1607
+ return this.request('GET', `${ENDPOINTS.BILLING_TRANSACTIONS(orgId)}${qs ? `?${qs}` : ''}`);
1608
+ }
1609
+
1610
+ async createCheckout(orgId: string, req: CreateCheckoutRequest): Promise<CreateCheckoutResponse> {
1611
+ return this.request('POST', ENDPOINTS.BILLING_CHECKOUT(orgId), req);
1612
+ }
1613
+
1614
+ /**
1615
+ * Creates a Stripe-hosted card-collection session (Checkout in mode=setup)
1616
+ * and returns the redirect URL. The frontend navigates to `setup_url` and
1617
+ * Stripe handles the card form; the resulting payment_method is recorded
1618
+ * server-side via the `setup_intent.succeeded` webhook. Mirrors
1619
+ * `POST /billing/setup-intent`.
1620
+ */
1621
+ async createSetupIntent(orgId: string): Promise<CreateSetupIntentResponse> {
1622
+ return this.request('POST', ENDPOINTS.BILLING_SETUP_INTENT(orgId));
1623
+ }
1624
+
1625
+ async getAutoReloadSettings(orgId: string): Promise<AutoReloadSettings> {
1626
+ return this.request('GET', ENDPOINTS.BILLING_AUTO_RELOAD(orgId));
1627
+ }
1628
+
1629
+ async updateAutoReloadSettings(orgId: string, req: UpdateAutoReloadRequest): Promise<AutoReloadSettings> {
1630
+ return this.request('PUT', ENDPOINTS.BILLING_AUTO_RELOAD(orgId), req);
1631
+ }
1632
+
1633
+ // NOTE: `resizeMachine` was intentionally removed before merge — the
1634
+ // matching `PATCH /api/v1/orgs/{orgId}/machines/{machineId}/spec` route
1635
+ // is not yet wired on the server, so exposing the method shipped a
1636
+ // method that always 404'd. Reintroduce together with the server route
1637
+ // when machine in-place resize lands.
1638
+ //
1639
+ // Similarly, the admin grant endpoints (POST/GET /internal/admin/orgs/
1640
+ // {id}/grants) intentionally do NOT live on this public client — they
1641
+ // are mounted on the unauthenticated `/internal/*` surface and reachable
1642
+ // only via internal network paths. The admin dashboard maintains its
1643
+ // own thin client (`ts/admin/lib/api.ts`) that hits these directly.
1644
+
1645
+ async getComputePricing(): Promise<ComputePricingResponse> {
1646
+ const resp = await this.request<{ data: ComputePricingResponse }>('GET', ENDPOINTS.COMPUTE_PRICING());
1647
+ return resp.data;
1648
+ }
1526
1649
  }
1527
1650
 
1528
1651
  function normalizeWikiChangeset(changeset: WikiChangeset): WikiChangeset {
@@ -1534,6 +1657,8 @@ function normalizeWikiChangeset(changeset: WikiChangeset): WikiChangeset {
1534
1657
  }
1535
1658
 
1536
1659
  export class ApiError extends Error {
1660
+ extras?: Record<string, unknown>;
1661
+
1537
1662
  constructor(
1538
1663
  public status: number,
1539
1664
  message: string,
package/src/constants.ts CHANGED
@@ -5,19 +5,24 @@ export const API_BASE = '/api/v1';
5
5
  export const WIKI_BASE = '/wiki/v1';
6
6
 
7
7
  // LLM model catalog — mirrors server/pkg/llmproxy/models.go DefaultModels.
8
- // SSOT is the Go side; keep this list in sync when models change.
8
+ // SSOT is the Go side; keep this list in sync when models change. `provider`
9
+ // is used by clients to filter runtime-specific pickers.
9
10
  export const PLATFORM_MODELS = [
10
- { id: 'anthropic/claude-opus-4.6', name: 'Claude Opus 4.6' },
11
- { id: 'anthropic/claude-sonnet-4.6', name: 'Claude Sonnet 4.6' },
12
- { id: 'anthropic/claude-sonnet-4.5', name: 'Claude Sonnet 4.5' },
13
- { id: 'openai/gpt-5.4', name: 'GPT-5.4' },
14
- { id: 'openai/o4-mini', name: 'o4 Mini' },
15
- { id: 'openai/gpt-4.1', name: 'GPT-4.1' },
16
- { id: 'google/gemini-2.5-pro', name: 'Gemini 2.5 Pro' },
17
- { id: 'google/gemini-2.5-flash', name: 'Gemini 2.5 Flash' },
18
- { id: 'moonshotai/kimi-k2.5', name: 'Kimi K2.5' },
19
- { id: 'z-ai/glm-5', name: 'GLM 5' },
20
- { id: 'qwen/qwen3.5-plus-02-15', name: 'Qwen3.5 Plus' },
11
+ { id: 'anthropic/claude-opus-4.7', name: 'Claude Opus 4.7', provider: 'anthropic' },
12
+ { id: 'anthropic/claude-opus-4.6', name: 'Claude Opus 4.6', provider: 'anthropic' },
13
+ { id: 'anthropic/claude-sonnet-4.6', name: 'Claude Sonnet 4.6', provider: 'anthropic' },
14
+ { id: 'anthropic/claude-sonnet-4.5', name: 'Claude Sonnet 4.5', provider: 'anthropic' },
15
+ { id: 'openai/gpt-5-codex', name: 'GPT-5 Codex', provider: 'openai' },
16
+ { id: 'openai/gpt-5.4', name: 'GPT-5.4', provider: 'openai' },
17
+ { id: 'openai/o4-mini', name: 'o4 Mini', provider: 'openai' },
18
+ { id: 'openai/gpt-4.1', name: 'GPT-4.1', provider: 'openai' },
19
+ { id: 'google/gemini-2.5-pro', name: 'Gemini 2.5 Pro', provider: 'google' },
20
+ { id: 'google/gemini-2.5-flash', name: 'Gemini 2.5 Flash', provider: 'google' },
21
+ { id: 'moonshotai/kimi-k2.5', name: 'Kimi K2.5', provider: 'moonshotai' },
22
+ { id: 'z-ai/glm-5', name: 'GLM 5', provider: 'z-ai' },
23
+ { id: 'deepseek/deepseek-v4-pro', name: 'DeepSeek V4 Pro', provider: 'deepseek' },
24
+ { id: 'deepseek/deepseek-v4-flash', name: 'DeepSeek V4 Flash', provider: 'deepseek' },
25
+ { id: 'qwen/qwen3.5-plus-02-15', name: 'Qwen3.5 Plus', provider: 'qwen' },
21
26
  ] as const;
22
27
 
23
28
  // REST endpoints
@@ -34,6 +39,7 @@ export const ENDPOINTS = {
34
39
 
35
40
  // Users
36
41
  USERS_ME: `${API_BASE}/users/me`,
42
+ USER_AVATAR: `${API_BASE}/users/me/avatar`,
37
43
  USER: (id: string) => `${API_BASE}/users/${id}`,
38
44
 
39
45
  // WebSocket ticket
@@ -81,9 +87,15 @@ export const ENDPOINTS = {
81
87
  UPLOAD_COMPLETE: (orgId: string) => `${API_BASE}/orgs/${orgId}/upload/complete`,
82
88
  FILE: (id: string) => `${API_BASE}/files/${id}`,
83
89
 
90
+ // Approval requests (org-scoped)
91
+ APPROVAL_REQUESTS: (orgId: string) => `${API_BASE}/orgs/${orgId}/approval-requests`,
92
+
84
93
  // Approvals (global)
94
+ APPROVAL: (id: string) => `${API_BASE}/approvals/${id}`,
85
95
  APPROVAL_DECIDE: (id: string) => `${API_BASE}/approvals/${id}/decide`,
96
+ APPROVAL_CANCEL: (id: string) => `${API_BASE}/approvals/${id}/cancel`,
86
97
  APPROVALS_PENDING: `${API_BASE}/approvals/pending`,
98
+ APPROVALS_ACTIONS: `${API_BASE}/approvals/actions`,
87
99
 
88
100
  // Agents (org-scoped)
89
101
  AGENTS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents`,
@@ -92,6 +104,8 @@ export const ENDPOINTS = {
92
104
  `${API_BASE}/orgs/${orgId}/agents/${agentId}/api-keys`,
93
105
  AGENT_API_KEY: (orgId: string, agentId: string, key: string) =>
94
106
  `${API_BASE}/orgs/${orgId}/agents/${agentId}/api-keys/${key}`,
107
+ AGENT_AVATAR: (orgId: string, agentId: string) =>
108
+ `${API_BASE}/orgs/${orgId}/agents/${agentId}/avatar`,
95
109
  AGENT_ACTIVITY: (orgId: string, agentId: string) =>
96
110
  `${API_BASE}/orgs/${orgId}/agents/${agentId}/activity`,
97
111
  AGENT_MONITOR: (orgId: string, agentId: string) =>
@@ -259,6 +273,7 @@ export const ENDPOINTS = {
259
273
  INBOX: (orgId: string) => `${API_BASE}/orgs/${orgId}/inbox`,
260
274
  INBOX_UNREAD_COUNT: (orgId: string) => `${API_BASE}/orgs/${orgId}/inbox/unread-count`,
261
275
  INBOX_ITEM_READ: (orgId: string, id: string) => `${API_BASE}/orgs/${orgId}/inbox/${id}/read`,
276
+ INBOX_ITEM_UNREAD: (orgId: string, id: string) => `${API_BASE}/orgs/${orgId}/inbox/${id}/unread`,
262
277
  INBOX_ITEM_ARCHIVE: (orgId: string, id: string) => `${API_BASE}/orgs/${orgId}/inbox/${id}/archive`,
263
278
  // Snooze/Unsnooze deferred until un-snooze cron worker is implemented
264
279
  INBOX_MARK_ALL_READ: (orgId: string) => `${API_BASE}/orgs/${orgId}/inbox/mark-all-read`,
@@ -296,6 +311,21 @@ export const ENDPOINTS = {
296
311
  // Feature flags (org-scoped, server-evaluated)
297
312
  FEATURE_FLAGS: (orgId: string) => `${API_BASE}/orgs/${orgId}/feature-flags`,
298
313
 
314
+ // Billing & Credits (org-scoped)
315
+ BILLING: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing`,
316
+ BILLING_TRANSACTIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing/transactions`,
317
+ BILLING_CHECKOUT: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing/checkout`,
318
+ BILLING_AUTO_RELOAD: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing/auto-reload`,
319
+ BILLING_SETUP_INTENT: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing/setup-intent`,
320
+ // MACHINE_SPEC is intentionally absent until the server registers
321
+ // `PATCH /machines/{machineId}/spec`. Reintroduce with ParallClient.resizeMachine
322
+ // once the in-place resize endpoint lands.
323
+ COMPUTE_PRICING: () => `${API_BASE}/billing/compute-pricing`,
324
+
325
+ // ADMIN_GRANTS intentionally NOT exported here — it sits under the
326
+ // unauthenticated `/internal/admin/*` surface and must not bleed into the
327
+ // public SDK. Admin dashboard hits the URL directly from its own client.
328
+
299
329
  } as const;
300
330
 
301
331
  // WebSocket event types
@@ -354,6 +384,11 @@ export const WS_EVENTS = {
354
384
  SCHEDULE_UPDATED: 'schedule.updated',
355
385
  SCHEDULE_DELETED: 'schedule.deleted',
356
386
  SCHEDULE_FIRED: 'schedule.fired',
387
+ BILLING_BALANCE_UPDATED: 'billing.balance_updated',
388
+ BILLING_LOW_BALANCE: 'billing.low_balance',
389
+ BILLING_MACHINE_STOPPED: 'billing.machine_stopped',
390
+ BILLING_INSUFFICIENT: 'billing.insufficient',
391
+ USER_UPDATED: 'user.updated',
357
392
  } as const;
358
393
 
359
394
  // Canonical target URI builders for unified comments.