@parall/sdk 1.46.0 → 1.48.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 +98 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +161 -6
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +15 -0
- package/dist/types.d.ts +210 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/client.ts +215 -6
- package/src/constants.ts +19 -0
- package/src/types.ts +231 -6
package/src/client.ts
CHANGED
|
@@ -95,6 +95,13 @@ import type {
|
|
|
95
95
|
CreateChannelConnectionInput,
|
|
96
96
|
InitiateChannelProvisioningInput,
|
|
97
97
|
SendChannelMessageInput,
|
|
98
|
+
SlackChannelsPage,
|
|
99
|
+
SlackHistoryPage,
|
|
100
|
+
SlackManifestLinkResult,
|
|
101
|
+
SlackMembersPage,
|
|
102
|
+
SlackReadPageQuery,
|
|
103
|
+
SlackStatusInput,
|
|
104
|
+
SlackUsersPage,
|
|
98
105
|
SentChannelMessage,
|
|
99
106
|
UpdateChannelConnectionInput,
|
|
100
107
|
ExternalIngressEvent,
|
|
@@ -202,6 +209,8 @@ import type {
|
|
|
202
209
|
InvokeClipResponse,
|
|
203
210
|
OnlineClipInfo,
|
|
204
211
|
RegistryClipInfo,
|
|
212
|
+
ClipRegistryEntry,
|
|
213
|
+
InstallRegistryClipResponse,
|
|
205
214
|
MachineClip,
|
|
206
215
|
BrowserProfile,
|
|
207
216
|
MachineBrowserProfile,
|
|
@@ -220,6 +229,8 @@ import type {
|
|
|
220
229
|
EdgeBrowserProfile,
|
|
221
230
|
ClipConnection,
|
|
222
231
|
EdgeOnboardingStatus,
|
|
232
|
+
ExecEdgeClipRequest,
|
|
233
|
+
EdgeClipExecResult,
|
|
223
234
|
} from './types.js';
|
|
224
235
|
|
|
225
236
|
export interface ParallClientOptions {
|
|
@@ -1754,11 +1765,17 @@ export class ParallClient {
|
|
|
1754
1765
|
return this.request('PATCH', ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
|
|
1755
1766
|
}
|
|
1756
1767
|
|
|
1757
|
-
/**
|
|
1768
|
+
/** @deprecated Retired server-side (daemon-control-authorization §4.2):
|
|
1769
|
+
* daemons update autonomously (CDN poll + platform release signal). The
|
|
1770
|
+
* endpoint now answers 409 LOCAL_UPDATE_NOT_SUPPORTED unconditionally. */
|
|
1758
1771
|
async requestMachineUpdate(orgId: string, machineId: string, mandatory = false): Promise<void> {
|
|
1759
1772
|
await this.request('POST', ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
|
|
1760
1773
|
}
|
|
1761
1774
|
|
|
1775
|
+
/** @deprecated Retired server-side (daemon-control-authorization §4.2):
|
|
1776
|
+
* remote filesystem browse of a member's machine was remote device access.
|
|
1777
|
+
* The endpoint now answers 409 LOCAL_BROWSE_NOT_SUPPORTED unconditionally;
|
|
1778
|
+
* workspace paths are typed in (or picked on the machine's own Desktop). */
|
|
1762
1779
|
async browseMachineFilesystem(
|
|
1763
1780
|
orgId: string,
|
|
1764
1781
|
machineId: string,
|
|
@@ -1814,6 +1831,20 @@ export class ParallClient {
|
|
|
1814
1831
|
return this.request('GET', ENDPOINTS.THREAD_UNREAD(orgId, chatId, threadRootId));
|
|
1815
1832
|
}
|
|
1816
1833
|
|
|
1834
|
+
/** Advance the per-thread read cursor (forward-only). The server auto-clears
|
|
1835
|
+
* thread-scoped inbox items (thread_reply + in-thread mentions) the cursor
|
|
1836
|
+
* now covers. */
|
|
1837
|
+
async markThreadRead(
|
|
1838
|
+
orgId: string,
|
|
1839
|
+
chatId: string,
|
|
1840
|
+
threadRootId: string,
|
|
1841
|
+
messageId: string,
|
|
1842
|
+
): Promise<void> {
|
|
1843
|
+
return this.request('POST', ENDPOINTS.THREAD_READ(orgId, chatId, threadRootId), {
|
|
1844
|
+
message_id: messageId,
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1817
1848
|
// ---- Inbox ----
|
|
1818
1849
|
|
|
1819
1850
|
async getInbox(
|
|
@@ -1854,9 +1885,15 @@ export class ParallClient {
|
|
|
1854
1885
|
return this.request('POST', ENDPOINTS.INBOX_ARCHIVE_ALL(orgId));
|
|
1855
1886
|
}
|
|
1856
1887
|
|
|
1857
|
-
/** Mark
|
|
1858
|
-
|
|
1859
|
-
|
|
1888
|
+
/** Mark inbox items as read by their source (source_type + source_id) or by
|
|
1889
|
+
* group_key, rather than by inbox item ID. The group_key form clears a whole
|
|
1890
|
+
* group at once (e.g. `task:{taskId}` — task_assign/task_update/task_comment
|
|
1891
|
+
* share it), used by the task detail view's auto-ack on open. */
|
|
1892
|
+
async ackInbox(
|
|
1893
|
+
orgId: string,
|
|
1894
|
+
target: { source_type: string; source_id: string } | { group_key: string },
|
|
1895
|
+
): Promise<void> {
|
|
1896
|
+
return this.request('POST', ENDPOINTS.INBOX_ACK(orgId), target);
|
|
1860
1897
|
}
|
|
1861
1898
|
|
|
1862
1899
|
async deleteInboxItem(orgId: string, id: string): Promise<void> {
|
|
@@ -2420,6 +2457,19 @@ export class ParallClient {
|
|
|
2420
2457
|
return this.request('POST', ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
|
|
2421
2458
|
}
|
|
2422
2459
|
|
|
2460
|
+
/**
|
|
2461
|
+
* Mint a pending slack connection + api.slack.com manifest-prefill link
|
|
2462
|
+
* (guided manual path). Activate the returned connection_id with
|
|
2463
|
+
* deliverChannelCredentials once the user brings back the bot token +
|
|
2464
|
+
* signing secret.
|
|
2465
|
+
*/
|
|
2466
|
+
async createSlackManifestLink(
|
|
2467
|
+
orgId: string,
|
|
2468
|
+
input: { agent_id: string },
|
|
2469
|
+
): Promise<SlackManifestLinkResult> {
|
|
2470
|
+
return this.request('POST', ENDPOINTS.CHANNEL_SLACK_MANIFEST_LINK(orgId), input);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2423
2473
|
/**
|
|
2424
2474
|
* Lazy status poll — server-side this may forward one provider poll, so
|
|
2425
2475
|
* call it at the session's `poll_interval_seconds` cadence, not faster.
|
|
@@ -2450,6 +2500,55 @@ export class ParallClient {
|
|
|
2450
2500
|
return this.request('POST', ENDPOINTS.CHANNEL_SEND(orgId), input);
|
|
2451
2501
|
}
|
|
2452
2502
|
|
|
2503
|
+
private slackReadQuery(base: string, query?: SlackReadPageQuery, extra?: Record<string, string>) {
|
|
2504
|
+
const params = new URLSearchParams();
|
|
2505
|
+
if (query?.cursor) params.set('cursor', query.cursor);
|
|
2506
|
+
if (query?.limit) params.set('limit', String(query.limit));
|
|
2507
|
+
for (const [k, v] of Object.entries(extra ?? {})) params.set(k, v);
|
|
2508
|
+
const qs = params.toString();
|
|
2509
|
+
return qs ? `${base}?${qs}` : base;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
/**
|
|
2513
|
+
* Tier-B read verbs (agent-only): workspace visibility as the bot sees
|
|
2514
|
+
* it. Same live gate as the send verb; authorization beyond it is the
|
|
2515
|
+
* bot's own Slack permissions.
|
|
2516
|
+
*/
|
|
2517
|
+
async listSlackChannels(orgId: string, query?: SlackReadPageQuery): Promise<SlackChannelsPage> {
|
|
2518
|
+
return this.request('GET', this.slackReadQuery(ENDPOINTS.SLACK_CHANNELS(orgId), query));
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
async listSlackUsers(orgId: string, query?: SlackReadPageQuery): Promise<SlackUsersPage> {
|
|
2522
|
+
return this.request('GET', this.slackReadQuery(ENDPOINTS.SLACK_USERS(orgId), query));
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
async slackHistory(
|
|
2526
|
+
orgId: string,
|
|
2527
|
+
conversationId: string,
|
|
2528
|
+
query?: SlackReadPageQuery,
|
|
2529
|
+
): Promise<SlackHistoryPage> {
|
|
2530
|
+
return this.request(
|
|
2531
|
+
'GET',
|
|
2532
|
+
this.slackReadQuery(ENDPOINTS.SLACK_HISTORY(orgId), query, { conversation: conversationId }),
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
async slackMembers(
|
|
2537
|
+
orgId: string,
|
|
2538
|
+
conversationId: string,
|
|
2539
|
+
query?: SlackReadPageQuery,
|
|
2540
|
+
): Promise<SlackMembersPage> {
|
|
2541
|
+
return this.request(
|
|
2542
|
+
'GET',
|
|
2543
|
+
this.slackReadQuery(ENDPOINTS.SLACK_MEMBERS(orgId), query, { conversation: conversationId }),
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
/** Set/clear the Agents-pane "typing…" indicator (best-effort cosmetic). */
|
|
2548
|
+
async setSlackStatus(orgId: string, input: SlackStatusInput): Promise<void> {
|
|
2549
|
+
await this.request('POST', ENDPOINTS.SLACK_STATUS(orgId), input);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2453
2552
|
async listChannelConversations(
|
|
2454
2553
|
orgId: string,
|
|
2455
2554
|
connectionId: string,
|
|
@@ -3377,6 +3476,43 @@ export class ParallClient {
|
|
|
3377
3476
|
return resp.data;
|
|
3378
3477
|
}
|
|
3379
3478
|
|
|
3479
|
+
// ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
|
|
3480
|
+
|
|
3481
|
+
/**
|
|
3482
|
+
* List registry clips visible to the org: its own plus public+approved
|
|
3483
|
+
* cross-org entries. This is the surface `installRegistryClip` and clip
|
|
3484
|
+
* connections operate on — NOT the Pinix catalog proxy
|
|
3485
|
+
* ({@link listRegistryClips}), whose entries carry no `crg_` id.
|
|
3486
|
+
*/
|
|
3487
|
+
async listOrgRegistryClips(orgId: string): Promise<ClipRegistryEntry[]> {
|
|
3488
|
+
// Server default page is 50 (max 100); one max-size page covers today's
|
|
3489
|
+
// catalogs — revisit with real pagination if registries outgrow it.
|
|
3490
|
+
const resp = await this.request<ClipRegistryEntry[] | null>(
|
|
3491
|
+
'GET',
|
|
3492
|
+
`${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`,
|
|
3493
|
+
);
|
|
3494
|
+
return resp ?? [];
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
/**
|
|
3498
|
+
* Install a registry clip into the org (a reference in `clip_installs`, not a
|
|
3499
|
+
* copy). Idempotent: installing an already-installed clip returns the same
|
|
3500
|
+
* `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
|
|
3501
|
+
* not eligible (cross-org requires public + approved).
|
|
3502
|
+
*/
|
|
3503
|
+
async installRegistryClip(orgId: string, clipId: string): Promise<InstallRegistryClipResponse> {
|
|
3504
|
+
return this.request('POST', ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
/** List the org's installed registry clips (full entries). */
|
|
3508
|
+
async listInstalledRegistryClips(orgId: string): Promise<ClipRegistryEntry[]> {
|
|
3509
|
+
const resp = await this.request<ClipRegistryEntry[] | null>(
|
|
3510
|
+
'GET',
|
|
3511
|
+
ENDPOINTS.ORG_CLIPS_INSTALLED(orgId),
|
|
3512
|
+
);
|
|
3513
|
+
return resp ?? [];
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3380
3516
|
// ---- Edge devices ----
|
|
3381
3517
|
|
|
3382
3518
|
async listEdgeDevices(orgId: string): Promise<{ data: EdgeDevice[] }> {
|
|
@@ -3400,8 +3536,8 @@ export class ParallClient {
|
|
|
3400
3536
|
}
|
|
3401
3537
|
|
|
3402
3538
|
/**
|
|
3403
|
-
* Delete a hosted Cloud Profile. Hosted only —
|
|
3404
|
-
*
|
|
3539
|
+
* Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
|
|
3540
|
+
* for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
|
|
3405
3541
|
*
|
|
3406
3542
|
* Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
|
|
3407
3543
|
* call and on every repeat. The device stops being usable immediately (no exec, no
|
|
@@ -3412,6 +3548,17 @@ export class ParallClient {
|
|
|
3412
3548
|
return this.request('DELETE', ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
|
|
3413
3549
|
}
|
|
3414
3550
|
|
|
3551
|
+
/**
|
|
3552
|
+
* Remove the interactive human caller's own offline BYOC registration.
|
|
3553
|
+
*
|
|
3554
|
+
* Synchronous and idempotent: a committed removal and a repeat after removal
|
|
3555
|
+
* both resolve with no response body. A live connection returns `EDGE_ONLINE`;
|
|
3556
|
+
* callers must not clear local device identity until this method resolves.
|
|
3557
|
+
*/
|
|
3558
|
+
async unregisterEdgeDevice(orgId: string, edgeId: string): Promise<void> {
|
|
3559
|
+
await this.request('DELETE', ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3415
3562
|
async getEdgeOnboarding(orgId: string): Promise<EdgeOnboardingStatus> {
|
|
3416
3563
|
return this.request('GET', ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
|
|
3417
3564
|
}
|
|
@@ -3420,6 +3567,68 @@ export class ParallClient {
|
|
|
3420
3567
|
return this.request('GET', ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
|
|
3421
3568
|
}
|
|
3422
3569
|
|
|
3570
|
+
/**
|
|
3571
|
+
* Execute a registry clip command on an Edge device.
|
|
3572
|
+
*
|
|
3573
|
+
* A hosted (Cloud Profile) device is reachable ONLY through an explicit
|
|
3574
|
+
* `connection` (id `ccn_…` or alias) — there is no implicit route to an
|
|
3575
|
+
* org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
|
|
3576
|
+
* nothing for the caller's own online device).
|
|
3577
|
+
*
|
|
3578
|
+
* Returns the result envelope on completion (`success` may be false when the
|
|
3579
|
+
* command RAN and failed — `error`/`error_code` describe why). Everything
|
|
3580
|
+
* else throws a typed {@link ApiError}; match on `err.code`:
|
|
3581
|
+
*
|
|
3582
|
+
* Safe to retry (guaranteed nothing was dispatched):
|
|
3583
|
+
* - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
|
|
3584
|
+
* Bounded backoff, same `correlation_id` across the loop.
|
|
3585
|
+
* - `EDGE_BUSY` 409 — the device is executing another request.
|
|
3586
|
+
* - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
|
|
3587
|
+
* - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
|
|
3588
|
+
*
|
|
3589
|
+
* NOT retryable:
|
|
3590
|
+
* - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
|
|
3591
|
+
* MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
|
|
3592
|
+
* automatically: verify the effect first, then decide. The message carries
|
|
3593
|
+
* the request id for audit.
|
|
3594
|
+
* - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
|
|
3595
|
+
* - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
|
|
3596
|
+
* `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
|
|
3597
|
+
* - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
|
|
3598
|
+
* `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
|
|
3599
|
+
* `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
|
|
3600
|
+
*/
|
|
3601
|
+
async execEdgeClip(orgId: string, req: ExecEdgeClipRequest): Promise<EdgeClipExecResult> {
|
|
3602
|
+
// The server holds the connection open for timeout + ~5s of result wait;
|
|
3603
|
+
// give the HTTP layer headroom past that so a slow-but-successful exec is
|
|
3604
|
+
// not chopped locally into a fake transport error. The effective value
|
|
3605
|
+
// mirrors the SERVER's rule exactly (out-of-range → its 30s default): a
|
|
3606
|
+
// local clamp that disagreed would abort the HTTP call while the server
|
|
3607
|
+
// legitimately keeps executing — manufacturing a transport error for a
|
|
3608
|
+
// request that may still succeed.
|
|
3609
|
+
const t = req.timeout;
|
|
3610
|
+
const serverTimeout = t !== undefined && t > 0 && t <= 120000 ? t : 30000;
|
|
3611
|
+
const timeoutMs = serverTimeout + 10_000;
|
|
3612
|
+
try {
|
|
3613
|
+
return await this.request('POST', ENDPOINTS.ORG_EDGE_EXEC(orgId), req, undefined, false, {
|
|
3614
|
+
timeoutMs,
|
|
3615
|
+
});
|
|
3616
|
+
} catch (err) {
|
|
3617
|
+
// A 422 is the RESULT envelope (the command ran and failed), not the
|
|
3618
|
+
// standard error envelope — lift its stable `error_code` into
|
|
3619
|
+
// ApiError.code so callers match one field for every failure.
|
|
3620
|
+
if (
|
|
3621
|
+
err instanceof ApiError &&
|
|
3622
|
+
err.status === 422 &&
|
|
3623
|
+
!err.code &&
|
|
3624
|
+
typeof err.extras?.error_code === 'string'
|
|
3625
|
+
) {
|
|
3626
|
+
err.code = err.extras.error_code;
|
|
3627
|
+
}
|
|
3628
|
+
throw err;
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3423
3632
|
// ---- Clip connections ----
|
|
3424
3633
|
|
|
3425
3634
|
async listClipConnections(orgId: string, clipId: string): Promise<{ data: ClipConnection[] }> {
|
package/src/constants.ts
CHANGED
|
@@ -628,12 +628,20 @@ export const ENDPOINTS = {
|
|
|
628
628
|
CHANNEL_MESSAGE: (orgId: string, messageId: string) =>
|
|
629
629
|
`${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
|
|
630
630
|
CHANNEL_PROVISIONING: (orgId: string) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
|
|
631
|
+
CHANNEL_SLACK_MANIFEST_LINK: (orgId: string) =>
|
|
632
|
+
`${API_BASE}/orgs/${orgId}/channel-provisioning/slack/manifest-link`,
|
|
631
633
|
CHANNEL_PROVISIONING_SESSION: (orgId: string, sessionId: string) =>
|
|
632
634
|
`${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
|
|
633
635
|
CHANNEL_PROVISIONING_CANCEL: (orgId: string, sessionId: string) =>
|
|
634
636
|
`${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
|
|
635
637
|
// Tier-B platform verb (agent-only): send one message as the bound bot.
|
|
636
638
|
CHANNEL_SEND: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/channel-send`,
|
|
639
|
+
// Tier-B read verbs (agent-only): workspace visibility as the bot sees it.
|
|
640
|
+
SLACK_CHANNELS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/channels`,
|
|
641
|
+
SLACK_USERS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/users`,
|
|
642
|
+
SLACK_HISTORY: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
|
|
643
|
+
SLACK_MEMBERS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
|
|
644
|
+
SLACK_STATUS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
|
|
637
645
|
|
|
638
646
|
// Invitations (org-scoped, admin)
|
|
639
647
|
ORG_INVITATIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
@@ -774,6 +782,8 @@ export const ENDPOINTS = {
|
|
|
774
782
|
CHAT_READ: (orgId: string, chatId: string) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
|
|
775
783
|
THREAD_UNREAD: (orgId: string, chatId: string, threadRootId: string) =>
|
|
776
784
|
`${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/unread`,
|
|
785
|
+
THREAD_READ: (orgId: string, chatId: string, threadRootId: string) =>
|
|
786
|
+
`${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/read`,
|
|
777
787
|
|
|
778
788
|
// References (org-scoped)
|
|
779
789
|
REFS_RESOLVE: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
|
|
@@ -861,13 +871,22 @@ export const ENDPOINTS = {
|
|
|
861
871
|
ORG_EDGE: (orgId: string) => `/api/v1/orgs/${orgId}/edge`,
|
|
862
872
|
ORG_EDGE_DEVICES: (orgId: string) => `/api/v1/orgs/${orgId}/edge/devices`,
|
|
863
873
|
ORG_EDGE_DEVICE: (orgId: string, edgeId: string) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
|
|
874
|
+
ORG_EDGE_DEVICE_UNREGISTER: (orgId: string, edgeId: string) =>
|
|
875
|
+
`/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
|
|
864
876
|
ORG_EDGE_ONBOARDING: (orgId: string) => `/api/v1/orgs/${orgId}/edge/onboarding`,
|
|
865
877
|
ORG_EDGE_PROFILES: (orgId: string, edgeId: string) =>
|
|
866
878
|
`/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
|
|
879
|
+
ORG_EDGE_EXEC: (orgId: string) => `/api/v1/orgs/${orgId}/edge/exec`,
|
|
867
880
|
CLIP_CONNECTIONS: (orgId: string, clipId: string) =>
|
|
868
881
|
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
|
|
869
882
|
CLIP_CONNECTION: (orgId: string, connId: string) =>
|
|
870
883
|
`/api/v1/orgs/${orgId}/clip-connections/${connId}`,
|
|
884
|
+
|
|
885
|
+
// Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
|
|
886
|
+
// Pinix Hub catalog proxy above is a different, id-less surface)
|
|
887
|
+
ORG_CLIP_REGISTRY: (orgId: string) => `/api/v1/orgs/${orgId}/clip-registry`,
|
|
888
|
+
ORG_CLIP_INSTALL: (orgId: string) => `/api/v1/orgs/${orgId}/clips/install`,
|
|
889
|
+
ORG_CLIPS_INSTALLED: (orgId: string) => `/api/v1/orgs/${orgId}/clips/installed`,
|
|
871
890
|
} as const;
|
|
872
891
|
|
|
873
892
|
/**
|
package/src/types.ts
CHANGED
|
@@ -2497,6 +2497,14 @@ export interface InboxItem {
|
|
|
2497
2497
|
grouping_priority: number;
|
|
2498
2498
|
/** Number of events in this group. Populated by the grouped list query. */
|
|
2499
2499
|
group_count: number;
|
|
2500
|
+
/**
|
|
2501
|
+
* True when ANY row in this item's group is unread — the same group-level
|
|
2502
|
+
* definition the unread-count badge uses. The representative row's own
|
|
2503
|
+
* `read_at` can be set while older rows in its group are still unread, so
|
|
2504
|
+
* read-state display must key off this field. Optional: absent on locally
|
|
2505
|
+
* cached rows written before the field shipped (fall back to `read_at`).
|
|
2506
|
+
*/
|
|
2507
|
+
group_unread?: boolean;
|
|
2500
2508
|
read_at: string | null;
|
|
2501
2509
|
archived_at: string | null;
|
|
2502
2510
|
snoozed_until: string | null;
|
|
@@ -2535,6 +2543,14 @@ export interface ChannelConnection {
|
|
|
2535
2543
|
ingress_url?: string;
|
|
2536
2544
|
/** Returned once at mint time (create / token regeneration). */
|
|
2537
2545
|
ingress_token?: string;
|
|
2546
|
+
/**
|
|
2547
|
+
* The in-product scope-upgrade re-auth can act on this connection
|
|
2548
|
+
* (slack, active, provisioned-family — its auth_config carries the
|
|
2549
|
+
* OAuth client). Mirrors the upgrade endpoint's preconditions exactly.
|
|
2550
|
+
* Manual-family connections upgrade via the Slack console; the UI
|
|
2551
|
+
* hides the upgrade CTA when this is false/absent.
|
|
2552
|
+
*/
|
|
2553
|
+
upgrade_eligible?: boolean;
|
|
2538
2554
|
created_at: string;
|
|
2539
2555
|
updated_at: string;
|
|
2540
2556
|
}
|
|
@@ -2649,16 +2665,35 @@ export type InitiateChannelProvisioningInput =
|
|
|
2649
2665
|
provider: 'slack';
|
|
2650
2666
|
/**
|
|
2651
2667
|
* The short-lived app-configuration token (12h, generated at
|
|
2652
|
-
* api.slack.com/apps). Transits once into apps.manifest.create
|
|
2653
|
-
* never stored.
|
|
2668
|
+
* api.slack.com/apps). Transits once into apps.manifest.create/update
|
|
2669
|
+
* and is never stored.
|
|
2654
2670
|
*/
|
|
2655
2671
|
config_token: string;
|
|
2672
|
+
/**
|
|
2673
|
+
* Scope-upgrade re-auth: converge the agent's EXISTING active
|
|
2674
|
+
* connection's app on the current manifest and re-authorize it (bot
|
|
2675
|
+
* token replaced in place, app id preserved) instead of creating a
|
|
2676
|
+
* new app.
|
|
2677
|
+
*/
|
|
2678
|
+
upgrade?: boolean;
|
|
2656
2679
|
}
|
|
2657
2680
|
| {
|
|
2658
2681
|
agent_id: string;
|
|
2659
2682
|
provider: Exclude<ChannelProvider, 'slack'>;
|
|
2660
2683
|
};
|
|
2661
2684
|
|
|
2685
|
+
/**
|
|
2686
|
+
* Manifest-prefill link for the guided manual slack path: the platform
|
|
2687
|
+
* mints a pending connection and returns an api.slack.com link that opens
|
|
2688
|
+
* the create-app form with everything preset (events, scopes, this
|
|
2689
|
+
* connection's webhook URL). The user installs the app themselves and
|
|
2690
|
+
* activates the SAME connection by delivering bot token + signing secret.
|
|
2691
|
+
*/
|
|
2692
|
+
export interface SlackManifestLinkResult {
|
|
2693
|
+
connection_id: string;
|
|
2694
|
+
manifest_url: string;
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2662
2697
|
/**
|
|
2663
2698
|
* Tier-B platform verb request (agent-only): send as the bound bot.
|
|
2664
2699
|
* Deliberately narrowed to 'slack' — the verb surface is per-vendor
|
|
@@ -2670,9 +2705,12 @@ export interface SendChannelMessageInput {
|
|
|
2670
2705
|
/** Vendor-native conversation id from the inbound event (e.g. C…/D…). */
|
|
2671
2706
|
conversation_id: string;
|
|
2672
2707
|
/**
|
|
2673
|
-
* External message id being answered (channel-domain format
|
|
2674
|
-
*
|
|
2675
|
-
*
|
|
2708
|
+
* External message id being answered (channel-domain format:
|
|
2709
|
+
* `{channel}:{ts}`, thread children `{channel}:{root}#{ts}`). REQUIRED
|
|
2710
|
+
* for channel conversations (the reply lands in that message's thread).
|
|
2711
|
+
* In DMs the SESSION never forks, but reply POSITION follows the
|
|
2712
|
+
* question: a thread-child id lands the reply inside that thread, a
|
|
2713
|
+
* bare id (or omitting the field) keeps the flattened main flow.
|
|
2676
2714
|
*/
|
|
2677
2715
|
reply_to?: string;
|
|
2678
2716
|
text: string;
|
|
@@ -2681,12 +2719,94 @@ export interface SendChannelMessageInput {
|
|
|
2681
2719
|
export interface SentChannelMessage {
|
|
2682
2720
|
channel_type: 'slack';
|
|
2683
2721
|
conversation_id: string;
|
|
2684
|
-
/**
|
|
2722
|
+
/**
|
|
2723
|
+
* Channel-domain external id — `{channel}:{ts}` for top-level posts,
|
|
2724
|
+
* `{channel}:{root}#{ts}` when the reply landed inside a thread — usable
|
|
2725
|
+
* directly as a future reply_to.
|
|
2726
|
+
*/
|
|
2685
2727
|
message_id: string;
|
|
2686
2728
|
thread_anchor?: string;
|
|
2687
2729
|
sent_at: string;
|
|
2688
2730
|
}
|
|
2689
2731
|
|
|
2732
|
+
/**
|
|
2733
|
+
* Tier-B read verbs (agent-only): workspace visibility as the bot sees it.
|
|
2734
|
+
* Authorization is the bot's own Slack permissions (invite the bot to a
|
|
2735
|
+
* channel to make its history readable) — reads have no external side
|
|
2736
|
+
* effect, so the seen-conversation restriction of the send verb does not
|
|
2737
|
+
* apply. Cursor pagination follows Slack's contract (empty = last page).
|
|
2738
|
+
*/
|
|
2739
|
+
export interface SlackChannelInfo {
|
|
2740
|
+
id: string;
|
|
2741
|
+
name: string;
|
|
2742
|
+
is_private: boolean;
|
|
2743
|
+
is_member: boolean;
|
|
2744
|
+
num_members?: number;
|
|
2745
|
+
topic?: string;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
export interface SlackChannelsPage {
|
|
2749
|
+
channels: SlackChannelInfo[];
|
|
2750
|
+
next_cursor?: string;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
export interface SlackUserInfo {
|
|
2754
|
+
id: string;
|
|
2755
|
+
name: string;
|
|
2756
|
+
real_name?: string;
|
|
2757
|
+
is_bot: boolean;
|
|
2758
|
+
deleted?: boolean;
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
export interface SlackUsersPage {
|
|
2762
|
+
users: SlackUserInfo[];
|
|
2763
|
+
next_cursor?: string;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
export interface SlackHistoryMessage {
|
|
2767
|
+
/**
|
|
2768
|
+
* Synthesized channel-domain id — `{conversation}:{ts}`, or
|
|
2769
|
+
* `{conversation}:{root}#{ts}` for thread children — directly usable as
|
|
2770
|
+
* the send verb's `reply_to` (no client-side root#child derivation).
|
|
2771
|
+
*/
|
|
2772
|
+
message_id: string;
|
|
2773
|
+
ts: string;
|
|
2774
|
+
thread_ts?: string;
|
|
2775
|
+
user?: string;
|
|
2776
|
+
bot_id?: string;
|
|
2777
|
+
text: string;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
export interface SlackHistoryPage {
|
|
2781
|
+
messages: SlackHistoryMessage[];
|
|
2782
|
+
next_cursor?: string;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
export interface SlackMembersPage {
|
|
2786
|
+
/** Bare Slack user ids — resolve display names via the users verb. */
|
|
2787
|
+
members: string[];
|
|
2788
|
+
next_cursor?: string;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
/**
|
|
2792
|
+
* Slack-native pagination for the read verbs. Filters are deliberately
|
|
2793
|
+
* FIXED server-side (channels: public+private, archived excluded) — the
|
|
2794
|
+
* public contract is cursor/limit only; widening it is a deliberate scope
|
|
2795
|
+
* decision, not a missing passthrough.
|
|
2796
|
+
*/
|
|
2797
|
+
export interface SlackReadPageQuery {
|
|
2798
|
+
cursor?: string;
|
|
2799
|
+
limit?: number;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
/** Agents-pane "typing…" indicator for one assistant thread (empty status clears). */
|
|
2803
|
+
export interface SlackStatusInput {
|
|
2804
|
+
conversation: string;
|
|
2805
|
+
/** The assistant thread's root ts. */
|
|
2806
|
+
thread_ts: string;
|
|
2807
|
+
status?: string;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2690
2810
|
// ============================================================
|
|
2691
2811
|
// Dispatch Types (agent event delivery)
|
|
2692
2812
|
// ============================================================
|
|
@@ -4023,6 +4143,15 @@ export type EdgePlacement = 'byoc' | 'hosted';
|
|
|
4023
4143
|
*/
|
|
4024
4144
|
export type EdgeHostedState = 'idle' | 'deleting';
|
|
4025
4145
|
|
|
4146
|
+
/** Stable API error codes returned by the BYOC unregister contract. */
|
|
4147
|
+
export type EdgeDeviceUnregisterErrorCode =
|
|
4148
|
+
| 'JWT_REQUIRED'
|
|
4149
|
+
| 'HUMAN_REQUIRED'
|
|
4150
|
+
| 'FORBIDDEN'
|
|
4151
|
+
| 'EDGE_PLACEMENT_UNSUPPORTED'
|
|
4152
|
+
| 'EDGE_ONLINE'
|
|
4153
|
+
| 'EDGE_INTEGRITY_ERROR';
|
|
4154
|
+
|
|
4026
4155
|
export interface EdgeDevice {
|
|
4027
4156
|
id: string;
|
|
4028
4157
|
org_id: string;
|
|
@@ -4073,6 +4202,102 @@ export interface ClipConnection {
|
|
|
4073
4202
|
updated_at: string;
|
|
4074
4203
|
}
|
|
4075
4204
|
|
|
4205
|
+
/**
|
|
4206
|
+
* A clip in the api-server org registry (`crg_…`, table `clip_registry`) — the
|
|
4207
|
+
* v3 registry that install and clip connections operate on. Distinct from
|
|
4208
|
+
* {@link RegistryClipInfo}, which is the clip-service → Pinix Hub catalog proxy
|
|
4209
|
+
* and carries no registry id.
|
|
4210
|
+
*/
|
|
4211
|
+
export interface ClipRegistryEntry {
|
|
4212
|
+
id: string;
|
|
4213
|
+
org_id: string;
|
|
4214
|
+
name: string;
|
|
4215
|
+
description?: string;
|
|
4216
|
+
visibility: 'public' | 'private';
|
|
4217
|
+
version?: string;
|
|
4218
|
+
/** clip.json manifest. `type` absent/null means browser (predates the field). */
|
|
4219
|
+
manifest: Record<string, unknown> | null;
|
|
4220
|
+
author_id: string;
|
|
4221
|
+
/** Reviewed snapshot served cross-org. Absent = never approved. */
|
|
4222
|
+
approved_version_id?: string;
|
|
4223
|
+
created_at: string;
|
|
4224
|
+
updated_at: string;
|
|
4225
|
+
/** Author-org-only computed fields (state of the latest submitted version). */
|
|
4226
|
+
review_status?: string;
|
|
4227
|
+
review_note?: string;
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
export interface InstallRegistryClipResponse {
|
|
4231
|
+
ok: boolean;
|
|
4232
|
+
clip_id: string;
|
|
4233
|
+
name: string;
|
|
4234
|
+
}
|
|
4235
|
+
|
|
4236
|
+
/**
|
|
4237
|
+
* Execute a registry (Edge) clip command on an Edge device
|
|
4238
|
+
* (`POST /orgs/{orgId}/edge/exec`).
|
|
4239
|
+
*
|
|
4240
|
+
* Routing is EXPLICIT — exactly one of:
|
|
4241
|
+
* - `connection`: a clip connection id (`ccn_…`) or its org-local alias. The
|
|
4242
|
+
* ONLY route to a hosted (Cloud Profile) device: the connection its
|
|
4243
|
+
* maintainer bound IS the org-wide authorization. Also valid for BYOC.
|
|
4244
|
+
* - `edge_id`: a BYOC desktop device the CALLER owns. Naming a hosted device
|
|
4245
|
+
* here is refused (`400 HOSTED_CONNECTION_REQUIRED`).
|
|
4246
|
+
* - neither: legacy BYOC fallback — resolves only to the caller's OWN online
|
|
4247
|
+
* desktop device, never to a hosted one.
|
|
4248
|
+
*/
|
|
4249
|
+
export interface ExecEdgeClipRequest {
|
|
4250
|
+
/**
|
|
4251
|
+
* Clip name in the org's clip registry (or an installed marketplace clip).
|
|
4252
|
+
* Exactly ONE of `clip` or `clip_id` is required — same identity contract as
|
|
4253
|
+
* clip invoke: bare names keep the fail-closed collision behavior (own-org
|
|
4254
|
+
* shadows installs; two installed namesakes → `409 CLIP_NAME_AMBIGUOUS`).
|
|
4255
|
+
*/
|
|
4256
|
+
clip?: string;
|
|
4257
|
+
/**
|
|
4258
|
+
* Exact registry Clip id (`crg_…`). Selects the exact object without
|
|
4259
|
+
* weakening execution trust (cross-org still requires public + approved +
|
|
4260
|
+
* installed and runs only the approved snapshot).
|
|
4261
|
+
*/
|
|
4262
|
+
clip_id?: string;
|
|
4263
|
+
/** Command to run — resolves to `<command>.js` in the clip's files. */
|
|
4264
|
+
command: string;
|
|
4265
|
+
/** Command arguments, forwarded verbatim to the clip. */
|
|
4266
|
+
args?: unknown;
|
|
4267
|
+
/** Connection id (`ccn_…`) or alias. Required for hosted devices. */
|
|
4268
|
+
connection?: string;
|
|
4269
|
+
/** Owned BYOC device id. Mutually exclusive with `connection`. */
|
|
4270
|
+
edge_id?: string;
|
|
4271
|
+
/**
|
|
4272
|
+
* Browser profile name. With `connection` it may only restate the profile
|
|
4273
|
+
* that connection grants (`400 CONNECTION_PROFILE_MISMATCH` otherwise).
|
|
4274
|
+
*/
|
|
4275
|
+
profile?: string;
|
|
4276
|
+
/** Execution timeout in MILLISECONDS (default 30000, max 120000). */
|
|
4277
|
+
timeout?: number;
|
|
4278
|
+
/**
|
|
4279
|
+
* Optional audit tag (≤128 chars). Keep the SAME id across an
|
|
4280
|
+
* EDGE_ACTIVATING retry loop so the server's audit log reads the retries as
|
|
4281
|
+
* one logical call. It is NOT an idempotency key — it deduplicates nothing.
|
|
4282
|
+
*/
|
|
4283
|
+
correlation_id?: string;
|
|
4284
|
+
}
|
|
4285
|
+
|
|
4286
|
+
/**
|
|
4287
|
+
* A completed execution's result envelope (HTTP 200). Every non-success is
|
|
4288
|
+
* thrown as a typed {@link ApiError} instead — see
|
|
4289
|
+
* {@link ParallClient.execEdgeClip} for the full code table and, critically,
|
|
4290
|
+
* which codes are safe to retry.
|
|
4291
|
+
*/
|
|
4292
|
+
export interface EdgeClipExecResult {
|
|
4293
|
+
request_id: string;
|
|
4294
|
+
success: boolean;
|
|
4295
|
+
data?: unknown;
|
|
4296
|
+
error?: string;
|
|
4297
|
+
error_code?: string;
|
|
4298
|
+
duration_ms: number;
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4076
4301
|
/**
|
|
4077
4302
|
* Two INDEPENDENT onboarding journeys. The original fields mean what they always
|
|
4078
4303
|
* meant — "has this org set up the desktop journey?" — and hosted-backed state
|