@parall/parall 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.
@@ -51482,10 +51482,17 @@ var ENDPOINTS = {
51482
51482
  CHANNEL_CONVERSATION_SESSION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/session`,
51483
51483
  CHANNEL_MESSAGE: (orgId, messageId) => `${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
51484
51484
  CHANNEL_PROVISIONING: (orgId) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
51485
+ CHANNEL_SLACK_MANIFEST_LINK: (orgId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/slack/manifest-link`,
51485
51486
  CHANNEL_PROVISIONING_SESSION: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
51486
51487
  CHANNEL_PROVISIONING_CANCEL: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
51487
51488
  // Tier-B platform verb (agent-only): send one message as the bound bot.
51488
51489
  CHANNEL_SEND: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/channel-send`,
51490
+ // Tier-B read verbs (agent-only): workspace visibility as the bot sees it.
51491
+ SLACK_CHANNELS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/channels`,
51492
+ SLACK_USERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/users`,
51493
+ SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
51494
+ SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
51495
+ SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
51489
51496
  // Invitations (org-scoped, admin)
51490
51497
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
51491
51498
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -51580,6 +51587,7 @@ var ENDPOINTS = {
51580
51587
  ORG_UNREAD: (orgId) => `${API_BASE}/orgs/${orgId}/unread`,
51581
51588
  CHAT_READ: (orgId, chatId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
51582
51589
  THREAD_UNREAD: (orgId, chatId, threadRootId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/unread`,
51590
+ THREAD_READ: (orgId, chatId, threadRootId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/read`,
51583
51591
  // References (org-scoped)
51584
51592
  REFS_RESOLVE: (orgId) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
51585
51593
  REFS_BACKLINKS: (orgId) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
@@ -51641,10 +51649,17 @@ var ENDPOINTS = {
51641
51649
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51642
51650
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
51643
51651
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
51652
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
51644
51653
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
51645
51654
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
51655
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
51646
51656
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51647
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
51657
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51658
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51659
+ // Pinix Hub catalog proxy above is a different, id-less surface)
51660
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51661
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51662
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
51648
51663
  };
51649
51664
  var WS_EVENTS = {
51650
51665
  // Client -> Server
@@ -52670,10 +52685,16 @@ var ParallClient = class _ParallClient {
52670
52685
  async resizeMachine(orgId, machineId, spec) {
52671
52686
  return this.request("PATCH", ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
52672
52687
  }
52673
- /** Signal a local daemon-mode Machine to check for and apply an update. */
52688
+ /** @deprecated Retired server-side (daemon-control-authorization §4.2):
52689
+ * daemons update autonomously (CDN poll + platform release signal). The
52690
+ * endpoint now answers 409 LOCAL_UPDATE_NOT_SUPPORTED unconditionally. */
52674
52691
  async requestMachineUpdate(orgId, machineId, mandatory = false) {
52675
52692
  await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
52676
52693
  }
52694
+ /** @deprecated Retired server-side (daemon-control-authorization §4.2):
52695
+ * remote filesystem browse of a member's machine was remote device access.
52696
+ * The endpoint now answers 409 LOCAL_BROWSE_NOT_SUPPORTED unconditionally;
52697
+ * workspace paths are typed in (or picked on the machine's own Desktop). */
52677
52698
  async browseMachineFilesystem(orgId, machineId, path9) {
52678
52699
  return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path9 }, void 0, false, { timeoutMs: 15e3 });
52679
52700
  }
@@ -52698,6 +52719,14 @@ var ParallClient = class _ParallClient {
52698
52719
  async getThreadUnread(orgId, chatId, threadRootId) {
52699
52720
  return this.request("GET", ENDPOINTS.THREAD_UNREAD(orgId, chatId, threadRootId));
52700
52721
  }
52722
+ /** Advance the per-thread read cursor (forward-only). The server auto-clears
52723
+ * thread-scoped inbox items (thread_reply + in-thread mentions) the cursor
52724
+ * now covers. */
52725
+ async markThreadRead(orgId, chatId, threadRootId, messageId) {
52726
+ return this.request("POST", ENDPOINTS.THREAD_READ(orgId, chatId, threadRootId), {
52727
+ message_id: messageId
52728
+ });
52729
+ }
52701
52730
  // ---- Inbox ----
52702
52731
  async getInbox(orgId, params) {
52703
52732
  return this.request("GET", ENDPOINTS.INBOX(orgId), void 0, params);
@@ -52721,9 +52750,12 @@ var ParallClient = class _ParallClient {
52721
52750
  async archiveAllInbox(orgId) {
52722
52751
  return this.request("POST", ENDPOINTS.INBOX_ARCHIVE_ALL(orgId));
52723
52752
  }
52724
- /** Mark an inbox item as read by its source (source_type + source_id) rather than inbox item ID. */
52725
- async ackInbox(orgId, source) {
52726
- return this.request("POST", ENDPOINTS.INBOX_ACK(orgId), source);
52753
+ /** Mark inbox items as read by their source (source_type + source_id) or by
52754
+ * group_key, rather than by inbox item ID. The group_key form clears a whole
52755
+ * group at once (e.g. `task:{taskId}` — task_assign/task_update/task_comment
52756
+ * share it), used by the task detail view's auto-ack on open. */
52757
+ async ackInbox(orgId, target) {
52758
+ return this.request("POST", ENDPOINTS.INBOX_ACK(orgId), target);
52727
52759
  }
52728
52760
  async deleteInboxItem(orgId, id) {
52729
52761
  return this.request("DELETE", ENDPOINTS.INBOX_ITEM(orgId, id));
@@ -53005,6 +53037,15 @@ var ParallClient = class _ParallClient {
53005
53037
  async initiateChannelProvisioning(orgId, input) {
53006
53038
  return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
53007
53039
  }
53040
+ /**
53041
+ * Mint a pending slack connection + api.slack.com manifest-prefill link
53042
+ * (guided manual path). Activate the returned connection_id with
53043
+ * deliverChannelCredentials once the user brings back the bot token +
53044
+ * signing secret.
53045
+ */
53046
+ async createSlackManifestLink(orgId, input) {
53047
+ return this.request("POST", ENDPOINTS.CHANNEL_SLACK_MANIFEST_LINK(orgId), input);
53048
+ }
53008
53049
  /**
53009
53050
  * Lazy status poll — server-side this may forward one provider poll, so
53010
53051
  * call it at the session's `poll_interval_seconds` cadence, not faster.
@@ -53023,6 +53064,38 @@ var ParallClient = class _ParallClient {
53023
53064
  async sendChannelMessage(orgId, input) {
53024
53065
  return this.request("POST", ENDPOINTS.CHANNEL_SEND(orgId), input);
53025
53066
  }
53067
+ slackReadQuery(base, query, extra) {
53068
+ const params = new URLSearchParams();
53069
+ if (query?.cursor)
53070
+ params.set("cursor", query.cursor);
53071
+ if (query?.limit)
53072
+ params.set("limit", String(query.limit));
53073
+ for (const [k, v] of Object.entries(extra ?? {}))
53074
+ params.set(k, v);
53075
+ const qs = params.toString();
53076
+ return qs ? `${base}?${qs}` : base;
53077
+ }
53078
+ /**
53079
+ * Tier-B read verbs (agent-only): workspace visibility as the bot sees
53080
+ * it. Same live gate as the send verb; authorization beyond it is the
53081
+ * bot's own Slack permissions.
53082
+ */
53083
+ async listSlackChannels(orgId, query) {
53084
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_CHANNELS(orgId), query));
53085
+ }
53086
+ async listSlackUsers(orgId, query) {
53087
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_USERS(orgId), query));
53088
+ }
53089
+ async slackHistory(orgId, conversationId, query) {
53090
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_HISTORY(orgId), query, { conversation: conversationId }));
53091
+ }
53092
+ async slackMembers(orgId, conversationId, query) {
53093
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_MEMBERS(orgId), query, { conversation: conversationId }));
53094
+ }
53095
+ /** Set/clear the Agents-pane "typing…" indicator (best-effort cosmetic). */
53096
+ async setSlackStatus(orgId, input) {
53097
+ await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
53098
+ }
53026
53099
  async listChannelConversations(orgId, connectionId) {
53027
53100
  return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
53028
53101
  }
@@ -53527,6 +53600,31 @@ var ParallClient = class _ParallClient {
53527
53600
  const resp = await this.request("GET", url);
53528
53601
  return resp.data;
53529
53602
  }
53603
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53604
+ /**
53605
+ * List registry clips visible to the org: its own plus public+approved
53606
+ * cross-org entries. This is the surface `installRegistryClip` and clip
53607
+ * connections operate on — NOT the Pinix catalog proxy
53608
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53609
+ */
53610
+ async listOrgRegistryClips(orgId) {
53611
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
53612
+ return resp ?? [];
53613
+ }
53614
+ /**
53615
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
53616
+ * copy). Idempotent: installing an already-installed clip returns the same
53617
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
53618
+ * not eligible (cross-org requires public + approved).
53619
+ */
53620
+ async installRegistryClip(orgId, clipId) {
53621
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53622
+ }
53623
+ /** List the org's installed registry clips (full entries). */
53624
+ async listInstalledRegistryClips(orgId) {
53625
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53626
+ return resp ?? [];
53627
+ }
53530
53628
  // ---- Edge devices ----
53531
53629
  async listEdgeDevices(orgId) {
53532
53630
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -53544,8 +53642,8 @@ var ParallClient = class _ParallClient {
53544
53642
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
53545
53643
  }
53546
53644
  /**
53547
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
53548
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
53645
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
53646
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
53549
53647
  *
53550
53648
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
53551
53649
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -53555,12 +53653,68 @@ var ParallClient = class _ParallClient {
53555
53653
  async deleteEdgeDevice(orgId, edgeId) {
53556
53654
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
53557
53655
  }
53656
+ /**
53657
+ * Remove the interactive human caller's own offline BYOC registration.
53658
+ *
53659
+ * Synchronous and idempotent: a committed removal and a repeat after removal
53660
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
53661
+ * callers must not clear local device identity until this method resolves.
53662
+ */
53663
+ async unregisterEdgeDevice(orgId, edgeId) {
53664
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
53665
+ }
53558
53666
  async getEdgeOnboarding(orgId) {
53559
53667
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
53560
53668
  }
53561
53669
  async listEdgeProfiles(orgId, edgeId) {
53562
53670
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
53563
53671
  }
53672
+ /**
53673
+ * Execute a registry clip command on an Edge device.
53674
+ *
53675
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
53676
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
53677
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
53678
+ * nothing for the caller's own online device).
53679
+ *
53680
+ * Returns the result envelope on completion (`success` may be false when the
53681
+ * command RAN and failed — `error`/`error_code` describe why). Everything
53682
+ * else throws a typed {@link ApiError}; match on `err.code`:
53683
+ *
53684
+ * Safe to retry (guaranteed nothing was dispatched):
53685
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
53686
+ * Bounded backoff, same `correlation_id` across the loop.
53687
+ * - `EDGE_BUSY` 409 — the device is executing another request.
53688
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
53689
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
53690
+ *
53691
+ * NOT retryable:
53692
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
53693
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
53694
+ * automatically: verify the effect first, then decide. The message carries
53695
+ * the request id for audit.
53696
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
53697
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
53698
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
53699
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
53700
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
53701
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
53702
+ */
53703
+ async execEdgeClip(orgId, req) {
53704
+ const t = req.timeout;
53705
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
53706
+ const timeoutMs = serverTimeout + 1e4;
53707
+ try {
53708
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
53709
+ timeoutMs
53710
+ });
53711
+ } catch (err) {
53712
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
53713
+ err.code = err.extras.error_code;
53714
+ }
53715
+ throw err;
53716
+ }
53717
+ }
53564
53718
  // ---- Clip connections ----
53565
53719
  async listClipConnections(orgId, clipId) {
53566
53720
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/parall",
3
- "version": "1.46.0",
3
+ "version": "1.48.0",
4
4
  "description": "OpenClaw channel plugin for Parall IM",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  "openclaw.plugin.json"
17
17
  ],
18
18
  "dependencies": {
19
- "@parall/sdk": "1.46.0",
20
- "@parall/agent-core": "1.46.0"
19
+ "@parall/sdk": "1.48.0",
20
+ "@parall/agent-core": "1.48.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^22.0.0",
@@ -25,6 +25,27 @@ parall clip invoke github-tools list-repos '{"org":"acme"}'
25
25
 
26
26
  Results are JSON on stdout; failures print an error.
27
27
 
28
+ ## Execute on an Edge device (registry clips)
29
+
30
+ Registry clips run on an Edge — a member's desktop, or an org-shared cloud
31
+ profile. **Name the target explicitly.** A cloud profile has NO implicit
32
+ route; omitting the target entirely is a desktop-only legacy form that
33
+ reaches just YOUR OWN online desktop device — never a shared cloud profile.
34
+
35
+ ```bash
36
+ parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
37
+ parall clip exec browser-tools screenshot '{"url":"…"}' --connection cloud-main
38
+ ```
39
+
40
+ - A cloud (hosted) profile is reachable ONLY via `--connection` — the clip
41
+ connection its maintainer bound (`ccn_…` id or alias). That binding IS your
42
+ authorization; without one the server answers `HOSTED_CONNECTION_REQUIRED`
43
+ and the fix is to ask an owner/admin to bind the clip, never to retry.
44
+ - `--edge <edgeId>` targets only a desktop device YOU own.
45
+ - Cold cloud profiles are handled by the CLI: it absorbs `EDGE_ACTIVATING`
46
+ with a bounded wait (~60s) while the profile starts. If the command still
47
+ fails, report the error — do not blind-retry in a loop.
48
+
28
49
  ## Behavior rules
29
50
 
30
51
  - An authorization error (clip not bound to you) is a fail-fast: ask the
@@ -34,6 +55,12 @@ Results are JSON on stdout; failures print an error.
34
55
  - Hosted browser activation is handled by the CLI: it waits (bounded) while a
35
56
  cold hosted browser starts, so if the invoke still fails, report the error —
36
57
  do not blind-retry in a loop.
58
+ - **`OUTCOME_UNKNOWN` is never retryable.** It means the command was
59
+ dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
60
+ could post, order or delete twice. Verify the effect through the system you
61
+ acted on (or tell the human, quoting the request id from the error) before
62
+ ever re-running. `EDGE_BUSY` is the opposite: guaranteed-unexecuted — wait
63
+ briefly, then one retry is safe.
37
64
  - A clip may act through a person's real logged-in account — outward,
38
65
  irreversible, or spending actions (post, order, delete, pay) get the same
39
66
  caution as any shared-state change: confirm when intent isn't explicit.