@parall/sdk 1.47.0 → 1.49.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/client.d.ts +118 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +180 -5
- package/dist/constants.d.ts +13 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +19 -0
- package/dist/types.d.ts +261 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/client.ts +272 -6
- package/src/constants.ts +25 -0
- package/src/types.ts +277 -0
package/src/client.ts
CHANGED
|
@@ -167,6 +167,8 @@ import type {
|
|
|
167
167
|
DispatchExpireResult,
|
|
168
168
|
ResolveRefsResponse,
|
|
169
169
|
BacklinksResponse,
|
|
170
|
+
OutboundRefsRequest,
|
|
171
|
+
OutboundRefsResponse,
|
|
170
172
|
RefGraphResponse,
|
|
171
173
|
BrokenRefsResponse,
|
|
172
174
|
PushSubscribeRequest,
|
|
@@ -209,6 +211,8 @@ import type {
|
|
|
209
211
|
InvokeClipResponse,
|
|
210
212
|
OnlineClipInfo,
|
|
211
213
|
RegistryClipInfo,
|
|
214
|
+
ClipRegistryEntry,
|
|
215
|
+
InstallRegistryClipResponse,
|
|
212
216
|
MachineClip,
|
|
213
217
|
BrowserProfile,
|
|
214
218
|
MachineBrowserProfile,
|
|
@@ -221,12 +225,20 @@ import type {
|
|
|
221
225
|
BrowserProfileLifecycleRequest,
|
|
222
226
|
BrowserViewerCommandRequest,
|
|
223
227
|
BrowserViewerCommandResponse,
|
|
228
|
+
EdgeViewerCommandRequest,
|
|
229
|
+
EdgeViewerCommandResponse,
|
|
224
230
|
GrantBrowserProfileConsentRequest,
|
|
225
231
|
EdgeDevice,
|
|
226
232
|
EdgePlacement,
|
|
227
233
|
EdgeBrowserProfile,
|
|
234
|
+
EdgeProfileProxyStatus,
|
|
235
|
+
SetEdgeProfileProxyRequest,
|
|
228
236
|
ClipConnection,
|
|
229
237
|
EdgeOnboardingStatus,
|
|
238
|
+
ExecEdgeClipRequest,
|
|
239
|
+
EdgeClipExecResult,
|
|
240
|
+
ReactionSummary,
|
|
241
|
+
ToggleReactionResponse,
|
|
230
242
|
} from './types.js';
|
|
231
243
|
|
|
232
244
|
export interface ParallClientOptions {
|
|
@@ -417,6 +429,8 @@ export class ParallClient {
|
|
|
417
429
|
timeoutMs?: number;
|
|
418
430
|
signal?: AbortSignal;
|
|
419
431
|
keepalive?: boolean;
|
|
432
|
+
/** Additional request preconditions such as If-Match. */
|
|
433
|
+
headers?: Record<string, string>;
|
|
420
434
|
/** Observes the final HTTP status of a successful request (e.g. 200-idempotent-replay vs 201-created). */
|
|
421
435
|
onStatus?: (status: number) => void;
|
|
422
436
|
},
|
|
@@ -439,7 +453,7 @@ export class ParallClient {
|
|
|
439
453
|
if (qs) url += `?${qs}`;
|
|
440
454
|
}
|
|
441
455
|
|
|
442
|
-
const headers = this.buildHeaders(path);
|
|
456
|
+
const headers = this.buildHeaders(path, opts?.headers);
|
|
443
457
|
|
|
444
458
|
// Cancellation: the caller's AbortSignal (e.g. a superseded search query)
|
|
445
459
|
// races the per-request timeout. AbortSignal.any aborts as soon as either
|
|
@@ -1091,6 +1105,20 @@ export class ParallClient {
|
|
|
1091
1105
|
return this.request('GET', ENDPOINTS.MESSAGE_REPLIES(id), undefined, params);
|
|
1092
1106
|
}
|
|
1093
1107
|
|
|
1108
|
+
// ---- Reactions ----
|
|
1109
|
+
|
|
1110
|
+
async toggleReaction(messageId: string, emoji: string): Promise<ToggleReactionResponse> {
|
|
1111
|
+
return this.request('PUT', ENDPOINTS.MESSAGE_REACTION(messageId, emoji));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
async listReactions(messageId: string): Promise<ReactionSummary[]> {
|
|
1115
|
+
const res = await this.request<{ reactions: ReactionSummary[] }>(
|
|
1116
|
+
'GET',
|
|
1117
|
+
ENDPOINTS.MESSAGE_REACTIONS(messageId),
|
|
1118
|
+
);
|
|
1119
|
+
return res.reactions;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1094
1122
|
// ---- File Upload ----
|
|
1095
1123
|
|
|
1096
1124
|
async getUploadPresignUrl(orgId: string, req: PresignUploadRequest): Promise<PresignResponse> {
|
|
@@ -1753,6 +1781,28 @@ export class ParallClient {
|
|
|
1753
1781
|
);
|
|
1754
1782
|
}
|
|
1755
1783
|
|
|
1784
|
+
/**
|
|
1785
|
+
* Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
|
|
1786
|
+
* tab nav for a hosted browser (Cloud Profile). Same request/reply shape as
|
|
1787
|
+
* browserViewerCommand, on the v3 edge pipe; additive — the v2 browser-profile
|
|
1788
|
+
* viewer is unchanged.
|
|
1789
|
+
*/
|
|
1790
|
+
async edgeViewerCommand(
|
|
1791
|
+
orgId: string,
|
|
1792
|
+
edgeId: string,
|
|
1793
|
+
req: EdgeViewerCommandRequest,
|
|
1794
|
+
opts?: { timeoutMs?: number; keepalive?: boolean },
|
|
1795
|
+
): Promise<EdgeViewerCommandResponse> {
|
|
1796
|
+
return this.request(
|
|
1797
|
+
'POST',
|
|
1798
|
+
ENDPOINTS.ORG_EDGE_VIEWER_COMMAND(orgId, edgeId),
|
|
1799
|
+
req,
|
|
1800
|
+
undefined,
|
|
1801
|
+
false,
|
|
1802
|
+
opts,
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1756
1806
|
async resizeMachine(
|
|
1757
1807
|
orgId: string,
|
|
1758
1808
|
machineId: string,
|
|
@@ -1809,9 +1859,23 @@ export class ParallClient {
|
|
|
1809
1859
|
|
|
1810
1860
|
// ---- Unread ----
|
|
1811
1861
|
|
|
1812
|
-
async getUnreadCounts(
|
|
1862
|
+
async getUnreadCounts(
|
|
1863
|
+
orgId?: string,
|
|
1864
|
+
opts?: {
|
|
1865
|
+
/** Add unread thread replies (per-thread cursors) to count/mentions.
|
|
1866
|
+
* Opt-in: only clients that can clear thread cursors should pass it. */
|
|
1867
|
+
includeThreadReplies?: boolean;
|
|
1868
|
+
},
|
|
1869
|
+
): Promise<Record<string, UnreadEntry>> {
|
|
1813
1870
|
const endpoint = orgId ? ENDPOINTS.ORG_UNREAD(orgId) : ENDPOINTS.UNREAD;
|
|
1814
|
-
const res = await this.request<{ data: Record<string, UnreadEntry> }>(
|
|
1871
|
+
const res = await this.request<{ data: Record<string, UnreadEntry> }>(
|
|
1872
|
+
'GET',
|
|
1873
|
+
endpoint,
|
|
1874
|
+
undefined,
|
|
1875
|
+
{
|
|
1876
|
+
include_thread_replies: opts?.includeThreadReplies ? 'true' : undefined,
|
|
1877
|
+
},
|
|
1878
|
+
);
|
|
1815
1879
|
return res.data;
|
|
1816
1880
|
}
|
|
1817
1881
|
|
|
@@ -1819,6 +1883,14 @@ export class ParallClient {
|
|
|
1819
1883
|
return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
|
|
1820
1884
|
}
|
|
1821
1885
|
|
|
1886
|
+
/** Mark everything in a chat read: the channel cursor jumps to the latest
|
|
1887
|
+
* top-level message and every thread cursor to its latest reply, in one
|
|
1888
|
+
* idempotent call. The server echoes the same per-cursor WS events the
|
|
1889
|
+
* single-cursor routes emit and auto-clears covered inbox items. */
|
|
1890
|
+
async markAllRead(orgId: string, chatId: string): Promise<void> {
|
|
1891
|
+
return this.request('POST', ENDPOINTS.CHAT_READ_ALL(orgId, chatId));
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1822
1894
|
async getThreadUnread(
|
|
1823
1895
|
orgId: string,
|
|
1824
1896
|
chatId: string,
|
|
@@ -3157,6 +3229,17 @@ export class ParallClient {
|
|
|
3157
3229
|
return this.request('GET', ENDPOINTS.REFS_BACKLINKS(orgId), undefined, params);
|
|
3158
3230
|
}
|
|
3159
3231
|
|
|
3232
|
+
/**
|
|
3233
|
+
* Outbound prll:// refs authored in a set of sources, as raw ref_links rows
|
|
3234
|
+
* (dedupe/group client-side). Pass `{ thread_root_id }` to list a whole
|
|
3235
|
+
* thread's refs (root + all replies, resolved server-side — the client's
|
|
3236
|
+
* reply window may be partial), or `{ source_type, source_ids }` for
|
|
3237
|
+
* explicit sources (max 500; v1 accepts only message sources).
|
|
3238
|
+
*/
|
|
3239
|
+
async listOutboundRefs(orgId: string, req: OutboundRefsRequest): Promise<OutboundRefsResponse> {
|
|
3240
|
+
return this.request('POST', ENDPOINTS.REFS_OUTBOUND(orgId), req);
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3160
3243
|
/**
|
|
3161
3244
|
* Bounded multi-hop walk of the prll:// reference graph around `uri`. `uri`
|
|
3162
3245
|
* must be an entity-level prll:// URI — a refined URI (path/query/fragment) is
|
|
@@ -3472,6 +3555,43 @@ export class ParallClient {
|
|
|
3472
3555
|
return resp.data;
|
|
3473
3556
|
}
|
|
3474
3557
|
|
|
3558
|
+
// ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
|
|
3559
|
+
|
|
3560
|
+
/**
|
|
3561
|
+
* List registry clips visible to the org: its own plus public+approved
|
|
3562
|
+
* cross-org entries. This is the surface `installRegistryClip` and clip
|
|
3563
|
+
* connections operate on — NOT the Pinix catalog proxy
|
|
3564
|
+
* ({@link listRegistryClips}), whose entries carry no `crg_` id.
|
|
3565
|
+
*/
|
|
3566
|
+
async listOrgRegistryClips(orgId: string): Promise<ClipRegistryEntry[]> {
|
|
3567
|
+
// Server default page is 50 (max 100); one max-size page covers today's
|
|
3568
|
+
// catalogs — revisit with real pagination if registries outgrow it.
|
|
3569
|
+
const resp = await this.request<ClipRegistryEntry[] | null>(
|
|
3570
|
+
'GET',
|
|
3571
|
+
`${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`,
|
|
3572
|
+
);
|
|
3573
|
+
return resp ?? [];
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
/**
|
|
3577
|
+
* Install a registry clip into the org (a reference in `clip_installs`, not a
|
|
3578
|
+
* copy). Idempotent: installing an already-installed clip returns the same
|
|
3579
|
+
* `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
|
|
3580
|
+
* not eligible (cross-org requires public + approved).
|
|
3581
|
+
*/
|
|
3582
|
+
async installRegistryClip(orgId: string, clipId: string): Promise<InstallRegistryClipResponse> {
|
|
3583
|
+
return this.request('POST', ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
/** List the org's installed registry clips (full entries). */
|
|
3587
|
+
async listInstalledRegistryClips(orgId: string): Promise<ClipRegistryEntry[]> {
|
|
3588
|
+
const resp = await this.request<ClipRegistryEntry[] | null>(
|
|
3589
|
+
'GET',
|
|
3590
|
+
ENDPOINTS.ORG_CLIPS_INSTALLED(orgId),
|
|
3591
|
+
);
|
|
3592
|
+
return resp ?? [];
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3475
3595
|
// ---- Edge devices ----
|
|
3476
3596
|
|
|
3477
3597
|
async listEdgeDevices(orgId: string): Promise<{ data: EdgeDevice[] }> {
|
|
@@ -3495,8 +3615,8 @@ export class ParallClient {
|
|
|
3495
3615
|
}
|
|
3496
3616
|
|
|
3497
3617
|
/**
|
|
3498
|
-
* Delete a hosted Cloud Profile. Hosted only —
|
|
3499
|
-
*
|
|
3618
|
+
* Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
|
|
3619
|
+
* for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
|
|
3500
3620
|
*
|
|
3501
3621
|
* Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
|
|
3502
3622
|
* call and on every repeat. The device stops being usable immediately (no exec, no
|
|
@@ -3507,6 +3627,17 @@ export class ParallClient {
|
|
|
3507
3627
|
return this.request('DELETE', ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
|
|
3508
3628
|
}
|
|
3509
3629
|
|
|
3630
|
+
/**
|
|
3631
|
+
* Remove the interactive human caller's own offline BYOC registration.
|
|
3632
|
+
*
|
|
3633
|
+
* Synchronous and idempotent: a committed removal and a repeat after removal
|
|
3634
|
+
* both resolve with no response body. A live connection returns `EDGE_ONLINE`;
|
|
3635
|
+
* callers must not clear local device identity until this method resolves.
|
|
3636
|
+
*/
|
|
3637
|
+
async unregisterEdgeDevice(orgId: string, edgeId: string): Promise<void> {
|
|
3638
|
+
await this.request('DELETE', ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3510
3641
|
async getEdgeOnboarding(orgId: string): Promise<EdgeOnboardingStatus> {
|
|
3511
3642
|
return this.request('GET', ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
|
|
3512
3643
|
}
|
|
@@ -3515,6 +3646,137 @@ export class ParallClient {
|
|
|
3515
3646
|
return this.request('GET', ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
|
|
3516
3647
|
}
|
|
3517
3648
|
|
|
3649
|
+
/**
|
|
3650
|
+
* Read a hosted Cloud Profile's egress-proxy status (manager-only: hosted
|
|
3651
|
+
* human maintainer or org admin). Sanitized — the password never comes back.
|
|
3652
|
+
* Typed errors: `EDGE_NOT_HOSTED` (BYOC device), `PROXY_CONFIG_CORRUPT` /
|
|
3653
|
+
* validation codes as 422 when a stored config no longer passes current rules.
|
|
3654
|
+
* `can_mutate` and `lease_status` are the authoritative idle gate; device
|
|
3655
|
+
* list status is not a substitute.
|
|
3656
|
+
*/
|
|
3657
|
+
async getEdgeProfileProxy(
|
|
3658
|
+
orgId: string,
|
|
3659
|
+
edgeId: string,
|
|
3660
|
+
profileName: string,
|
|
3661
|
+
): Promise<EdgeProfileProxyStatus> {
|
|
3662
|
+
return this.request('GET', ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName));
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
/**
|
|
3666
|
+
* Set/replace the profile's egress proxy (full triple every time) — IDLE
|
|
3667
|
+
* ONLY: while the profile's hosted browser is running (a viewer session is
|
|
3668
|
+
* open or a pod is otherwise live) the server answers 409
|
|
3669
|
+
* `EDGE_PROFILE_IN_USE`; close the viewer, wait for idle scale-to-zero, and
|
|
3670
|
+
* retry. The next cold start uses the new egress; browser login state is
|
|
3671
|
+
* preserved across it. Other typed errors: the validation vocabulary
|
|
3672
|
+
* (`INVALID_PROXY_SERVER`, `PROXY_AUTH_INCOMPLETE`,
|
|
3673
|
+
* `PROXY_SERVER_FORBIDDEN_TARGET`, …), `EDGE_DELETING` (409), and
|
|
3674
|
+
* `SECRETBOX_UNCONFIGURED` (503 — server cannot store credentials safely),
|
|
3675
|
+
* and `EDGE_PROFILE_PROXY_STALE` (409 — expectedVersion lost a tab race).
|
|
3676
|
+
*/
|
|
3677
|
+
async setEdgeProfileProxy(
|
|
3678
|
+
orgId: string,
|
|
3679
|
+
edgeId: string,
|
|
3680
|
+
profileName: string,
|
|
3681
|
+
req: SetEdgeProfileProxyRequest,
|
|
3682
|
+
expectedVersion?: string,
|
|
3683
|
+
): Promise<EdgeProfileProxyStatus> {
|
|
3684
|
+
return this.request(
|
|
3685
|
+
'PUT',
|
|
3686
|
+
ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName),
|
|
3687
|
+
req,
|
|
3688
|
+
undefined,
|
|
3689
|
+
false,
|
|
3690
|
+
{
|
|
3691
|
+
headers: expectedVersion ? { 'If-Match': `"proxy-${expectedVersion}"` } : undefined,
|
|
3692
|
+
},
|
|
3693
|
+
);
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
/**
|
|
3697
|
+
* Clear the profile's egress proxy; the next pod start egresses directly.
|
|
3698
|
+
* Idle-only like set — 409 `EDGE_PROFILE_IN_USE` while the browser is live.
|
|
3699
|
+
*/
|
|
3700
|
+
async clearEdgeProfileProxy(
|
|
3701
|
+
orgId: string,
|
|
3702
|
+
edgeId: string,
|
|
3703
|
+
profileName: string,
|
|
3704
|
+
expectedVersion?: string,
|
|
3705
|
+
): Promise<EdgeProfileProxyStatus> {
|
|
3706
|
+
return this.request(
|
|
3707
|
+
'DELETE',
|
|
3708
|
+
ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName),
|
|
3709
|
+
undefined,
|
|
3710
|
+
undefined,
|
|
3711
|
+
false,
|
|
3712
|
+
{
|
|
3713
|
+
headers: expectedVersion ? { 'If-Match': `"proxy-${expectedVersion}"` } : undefined,
|
|
3714
|
+
},
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
/**
|
|
3719
|
+
* Execute a registry clip command on an Edge device.
|
|
3720
|
+
*
|
|
3721
|
+
* A hosted (Cloud Profile) device is reachable ONLY through an explicit
|
|
3722
|
+
* `connection` (id `ccn_…` or alias) — there is no implicit route to an
|
|
3723
|
+
* org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
|
|
3724
|
+
* nothing for the caller's own online device).
|
|
3725
|
+
*
|
|
3726
|
+
* Returns the result envelope on completion (`success` may be false when the
|
|
3727
|
+
* command RAN and failed — `error`/`error_code` describe why). Everything
|
|
3728
|
+
* else throws a typed {@link ApiError}; match on `err.code`:
|
|
3729
|
+
*
|
|
3730
|
+
* Safe to retry (guaranteed nothing was dispatched):
|
|
3731
|
+
* - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
|
|
3732
|
+
* Bounded backoff, same `correlation_id` across the loop.
|
|
3733
|
+
* - `EDGE_BUSY` 409 — the device is executing another request.
|
|
3734
|
+
* - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
|
|
3735
|
+
* - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
|
|
3736
|
+
*
|
|
3737
|
+
* NOT retryable:
|
|
3738
|
+
* - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
|
|
3739
|
+
* MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
|
|
3740
|
+
* automatically: verify the effect first, then decide. The message carries
|
|
3741
|
+
* the request id for audit.
|
|
3742
|
+
* - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
|
|
3743
|
+
* - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
|
|
3744
|
+
* `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
|
|
3745
|
+
* - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
|
|
3746
|
+
* `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
|
|
3747
|
+
* `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
|
|
3748
|
+
*/
|
|
3749
|
+
async execEdgeClip(orgId: string, req: ExecEdgeClipRequest): Promise<EdgeClipExecResult> {
|
|
3750
|
+
// The server holds the connection open for timeout + ~5s of result wait;
|
|
3751
|
+
// give the HTTP layer headroom past that so a slow-but-successful exec is
|
|
3752
|
+
// not chopped locally into a fake transport error. The effective value
|
|
3753
|
+
// mirrors the SERVER's rule exactly (out-of-range → its 30s default): a
|
|
3754
|
+
// local clamp that disagreed would abort the HTTP call while the server
|
|
3755
|
+
// legitimately keeps executing — manufacturing a transport error for a
|
|
3756
|
+
// request that may still succeed.
|
|
3757
|
+
const t = req.timeout;
|
|
3758
|
+
const serverTimeout = t !== undefined && t > 0 && t <= 120000 ? t : 30000;
|
|
3759
|
+
const timeoutMs = serverTimeout + 10_000;
|
|
3760
|
+
try {
|
|
3761
|
+
return await this.request('POST', ENDPOINTS.ORG_EDGE_EXEC(orgId), req, undefined, false, {
|
|
3762
|
+
timeoutMs,
|
|
3763
|
+
});
|
|
3764
|
+
} catch (err) {
|
|
3765
|
+
// A 422 is the RESULT envelope (the command ran and failed), not the
|
|
3766
|
+
// standard error envelope — lift its stable `error_code` into
|
|
3767
|
+
// ApiError.code so callers match one field for every failure.
|
|
3768
|
+
if (
|
|
3769
|
+
err instanceof ApiError &&
|
|
3770
|
+
err.status === 422 &&
|
|
3771
|
+
!err.code &&
|
|
3772
|
+
typeof err.extras?.error_code === 'string'
|
|
3773
|
+
) {
|
|
3774
|
+
err.code = err.extras.error_code;
|
|
3775
|
+
}
|
|
3776
|
+
throw err;
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
|
|
3518
3780
|
// ---- Clip connections ----
|
|
3519
3781
|
|
|
3520
3782
|
async listClipConnections(orgId: string, clipId: string): Promise<{ data: ClipConnection[] }> {
|
|
@@ -3550,6 +3812,8 @@ function normalizeWikiChangeset(changeset: WikiChangeset): WikiChangeset {
|
|
|
3550
3812
|
|
|
3551
3813
|
export class ApiError extends Error {
|
|
3552
3814
|
extras?: Record<string, unknown>;
|
|
3815
|
+
/** Retry-After delta seconds when the server supplies one. */
|
|
3816
|
+
retryAfterSeconds?: number;
|
|
3553
3817
|
/** Attempted action (authorization denials) — e.g. "chat.add_member". */
|
|
3554
3818
|
action?: string;
|
|
3555
3819
|
/** Target resource URI that was evaluated — e.g. "prll://cht_…". */
|
|
@@ -3577,7 +3841,7 @@ export class ApiError extends Error {
|
|
|
3577
3841
|
* See docs/engineering-design/error-contract-design.md
|
|
3578
3842
|
*/
|
|
3579
3843
|
function buildApiError(
|
|
3580
|
-
res: { status: number; statusText: string },
|
|
3844
|
+
res: { status: number; statusText: string; headers?: { get(name: string): string | null } },
|
|
3581
3845
|
rawErrorBody: unknown,
|
|
3582
3846
|
): ApiError {
|
|
3583
3847
|
const errorBody =
|
|
@@ -3600,6 +3864,8 @@ function buildApiError(
|
|
|
3600
3864
|
(typeof errorObj?.code === 'string' ? errorObj.code : undefined) ??
|
|
3601
3865
|
(typeof errorBody.code === 'string' ? (errorBody.code as string) : undefined);
|
|
3602
3866
|
const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
|
|
3867
|
+
const retryAfter = Number(res.headers?.get('Retry-After'));
|
|
3868
|
+
if (Number.isFinite(retryAfter) && retryAfter > 0) apiError.retryAfterSeconds = retryAfter;
|
|
3603
3869
|
// Machine anchors: present under `error`, with a legacy flat fallback.
|
|
3604
3870
|
const anchors = errorObj ?? errorBody;
|
|
3605
3871
|
if (typeof anchors.action === 'string') apiError.action = anchors.action;
|
package/src/constants.ts
CHANGED
|
@@ -410,6 +410,9 @@ export const ENDPOINTS = {
|
|
|
410
410
|
MESSAGE_WATCH: (id: string) => `${API_BASE}/messages/${id}/watch`,
|
|
411
411
|
MESSAGE_WATCHERS: (id: string) => `${API_BASE}/messages/${id}/watchers`,
|
|
412
412
|
MESSAGE_WATCHING: (id: string) => `${API_BASE}/messages/${id}/watching`,
|
|
413
|
+
MESSAGE_REACTIONS: (id: string) => `${API_BASE}/messages/${id}/reactions`,
|
|
414
|
+
MESSAGE_REACTION: (id: string, emoji: string) =>
|
|
415
|
+
`${API_BASE}/messages/${id}/reactions/${encodeURIComponent(emoji)}`,
|
|
413
416
|
|
|
414
417
|
// Upload (org-scoped)
|
|
415
418
|
UPLOAD_PRESIGN: (orgId: string) => `${API_BASE}/orgs/${orgId}/upload/presign`,
|
|
@@ -780,6 +783,8 @@ export const ENDPOINTS = {
|
|
|
780
783
|
UNREAD: `${API_BASE}/me/unread`,
|
|
781
784
|
ORG_UNREAD: (orgId: string) => `${API_BASE}/orgs/${orgId}/unread`,
|
|
782
785
|
CHAT_READ: (orgId: string, chatId: string) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
|
|
786
|
+
CHAT_READ_ALL: (orgId: string, chatId: string) =>
|
|
787
|
+
`${API_BASE}/orgs/${orgId}/chats/${chatId}/read-all`,
|
|
783
788
|
THREAD_UNREAD: (orgId: string, chatId: string, threadRootId: string) =>
|
|
784
789
|
`${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/unread`,
|
|
785
790
|
THREAD_READ: (orgId: string, chatId: string, threadRootId: string) =>
|
|
@@ -788,6 +793,7 @@ export const ENDPOINTS = {
|
|
|
788
793
|
// References (org-scoped)
|
|
789
794
|
REFS_RESOLVE: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
|
|
790
795
|
REFS_BACKLINKS: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
|
|
796
|
+
REFS_OUTBOUND: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/outbound`,
|
|
791
797
|
REFS_GRAPH: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/graph`,
|
|
792
798
|
REFS_CHECK: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/check`,
|
|
793
799
|
|
|
@@ -871,13 +877,30 @@ export const ENDPOINTS = {
|
|
|
871
877
|
ORG_EDGE: (orgId: string) => `/api/v1/orgs/${orgId}/edge`,
|
|
872
878
|
ORG_EDGE_DEVICES: (orgId: string) => `/api/v1/orgs/${orgId}/edge/devices`,
|
|
873
879
|
ORG_EDGE_DEVICE: (orgId: string, edgeId: string) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
|
|
880
|
+
ORG_EDGE_DEVICE_UNREGISTER: (orgId: string, edgeId: string) =>
|
|
881
|
+
`/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
|
|
874
882
|
ORG_EDGE_ONBOARDING: (orgId: string) => `/api/v1/orgs/${orgId}/edge/onboarding`,
|
|
875
883
|
ORG_EDGE_PROFILES: (orgId: string, edgeId: string) =>
|
|
876
884
|
`/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
|
|
885
|
+
// Per-profile egress proxy (hosted Cloud Profiles; manager-only, human-only).
|
|
886
|
+
ORG_EDGE_PROFILE_PROXY: (orgId: string, edgeId: string, profileName: string) =>
|
|
887
|
+
`/api/v1/orgs/${orgId}/edge/${edgeId}/profiles/${encodeURIComponent(profileName)}/proxy`,
|
|
888
|
+
ORG_EDGE_EXEC: (orgId: string) => `/api/v1/orgs/${orgId}/edge/exec`,
|
|
889
|
+
// Cloud Edge live viewer command (V1b) — api-server, gated on cap:edge-viewer.
|
|
890
|
+
// Same request/reply shape as the v2 browser-profile viewer, on the v3 edge
|
|
891
|
+
// pipe. Additive: does NOT replace BROWSER_PROFILE_VIEWER_COMMAND.
|
|
892
|
+
ORG_EDGE_VIEWER_COMMAND: (orgId: string, edgeId: string) =>
|
|
893
|
+
`/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
|
|
877
894
|
CLIP_CONNECTIONS: (orgId: string, clipId: string) =>
|
|
878
895
|
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
|
|
879
896
|
CLIP_CONNECTION: (orgId: string, connId: string) =>
|
|
880
897
|
`/api/v1/orgs/${orgId}/clip-connections/${connId}`,
|
|
898
|
+
|
|
899
|
+
// Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
|
|
900
|
+
// Pinix Hub catalog proxy above is a different, id-less surface)
|
|
901
|
+
ORG_CLIP_REGISTRY: (orgId: string) => `/api/v1/orgs/${orgId}/clip-registry`,
|
|
902
|
+
ORG_CLIP_INSTALL: (orgId: string) => `/api/v1/orgs/${orgId}/clips/install`,
|
|
903
|
+
ORG_CLIPS_INSTALLED: (orgId: string) => `/api/v1/orgs/${orgId}/clips/installed`,
|
|
881
904
|
} as const;
|
|
882
905
|
|
|
883
906
|
/**
|
|
@@ -932,6 +955,7 @@ export const WS_EVENTS = {
|
|
|
932
955
|
MESSAGE_PATCH: 'message.patch',
|
|
933
956
|
MESSAGE_EDIT: 'message.edit',
|
|
934
957
|
MESSAGE_DELETE: 'message.delete',
|
|
958
|
+
MESSAGE_REACTION_UPDATED: 'message.reaction.updated',
|
|
935
959
|
TYPING_UPDATE: 'typing.update',
|
|
936
960
|
CHAT_UPDATE: 'chat.update',
|
|
937
961
|
CHAT_DELETED: 'chat.deleted',
|
|
@@ -972,6 +996,7 @@ export const WS_EVENTS = {
|
|
|
972
996
|
INBOX_UPDATE: 'inbox.update',
|
|
973
997
|
INBOX_BULK_UPDATE: 'inbox.bulk_update',
|
|
974
998
|
READ_POSITION_UPDATED: 'read_position.updated',
|
|
999
|
+
THREAD_READ_POSITION_UPDATED: 'thread_read_position.updated',
|
|
975
1000
|
DISPATCH_NEW: 'dispatch.new',
|
|
976
1001
|
DISPATCH_RECEIVED: 'dispatch.received',
|
|
977
1002
|
DISPATCH_RESOLVED: 'dispatch.resolved',
|