@parall/daemon 1.47.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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.47.0",
3
- "built_at": "2026-07-20T03:34:22.601Z",
2
+ "version": "1.48.0",
3
+ "built_at": "2026-07-21T07:52:25.995Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,29 +16,29 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-browser-pod.js": {
19
- "sha256": "a3dcf5eaae3552b8c8220231dfc1af9abc07991d5e9d18a33c1e6e94550c5740",
20
- "size": 2889611
19
+ "sha256": "d35ed50bf1fc1266cb8001c9dd3e694dff9847738d125582ccaca81bc3db45d5",
20
+ "size": 2894062
21
21
  },
22
22
  "parall-channel-exec.js": {
23
23
  "sha256": "7c2a6e12483a45e9eb4a42cd7d4cdd3c4fa21dd09ac1347d9a3e177b4ddee930",
24
24
  "size": 7220
25
25
  },
26
26
  "parall-claude-agent.js": {
27
- "sha256": "ba2f015614ac1b2177fb1326129aba73ed1575bad646f279c2e4b3e7106a838c",
28
- "size": 2721373
27
+ "sha256": "852f7b7d3840f243d39d5ec2f23735ee80fce6fb9c5f8ab5ff1cfb7d5281ad51",
28
+ "size": 2727384
29
29
  },
30
30
  "parall-codex-agent.js": {
31
- "sha256": "35bcc6c1651f40e4e169eef9afb1969ca97bb946ebc6f80beb1eef61cf76c65f",
32
- "size": 2763113
31
+ "sha256": "c331674763f9c237d6a3ee03d9bc8f4137c154e2e576caa5fd23bbe32f0f2b49",
32
+ "size": 2769124
33
33
  },
34
34
  "parall-daemon.js": {
35
- "sha256": "5bcbd939b1c753687ec7a79f5df98210aee6d92d825e043d4ad1b38b2daa5883",
36
- "size": 2950822
35
+ "sha256": "0c41c7255f98339d095faf96e7b712c150d442696eefcb3965d3e59ad0f251fa",
36
+ "size": 2955625
37
37
  },
38
38
  "parall-openclaw-agent.js": {
39
39
  "sha256": "856a1c3a9ae0ffef8efe2481fa1171dbeb1d67cccb88616aa9bf16da96a69355",
40
40
  "size": 9923
41
41
  }
42
42
  },
43
- "signature": "jEnwJR/MRRqU9qfDoaqSVZdhjBLn4MLME3hjhe5l5AM7KRGMlrbTjc2r/8Vq3STH/d8RCJl++tBgL0rB31i+CA=="
43
+ "signature": "u4mhCrFPCejnf/mBjQdNvKkDaQE0MjumZpTc5eHA+Qz7cbemy1WDRhy+j0A4pMvBezJqwUIoyQqn84jfjcgBDA=="
44
44
  }
@@ -63852,10 +63852,17 @@ var ENDPOINTS = {
63852
63852
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
63853
63853
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
63854
63854
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
63855
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
63855
63856
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
63856
63857
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
63858
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
63857
63859
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
63858
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
63860
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
63861
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
63862
+ // Pinix Hub catalog proxy above is a different, id-less surface)
63863
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
63864
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
63865
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
63859
63866
  };
63860
63867
 
63861
63868
  // ts/sdk/dist/client.js
@@ -65712,6 +65719,31 @@ var ParallClient = class _ParallClient {
65712
65719
  const resp = await this.request("GET", url);
65713
65720
  return resp.data;
65714
65721
  }
65722
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
65723
+ /**
65724
+ * List registry clips visible to the org: its own plus public+approved
65725
+ * cross-org entries. This is the surface `installRegistryClip` and clip
65726
+ * connections operate on — NOT the Pinix catalog proxy
65727
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
65728
+ */
65729
+ async listOrgRegistryClips(orgId) {
65730
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
65731
+ return resp ?? [];
65732
+ }
65733
+ /**
65734
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
65735
+ * copy). Idempotent: installing an already-installed clip returns the same
65736
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
65737
+ * not eligible (cross-org requires public + approved).
65738
+ */
65739
+ async installRegistryClip(orgId, clipId) {
65740
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
65741
+ }
65742
+ /** List the org's installed registry clips (full entries). */
65743
+ async listInstalledRegistryClips(orgId) {
65744
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
65745
+ return resp ?? [];
65746
+ }
65715
65747
  // ---- Edge devices ----
65716
65748
  async listEdgeDevices(orgId) {
65717
65749
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -65729,8 +65761,8 @@ var ParallClient = class _ParallClient {
65729
65761
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
65730
65762
  }
65731
65763
  /**
65732
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
65733
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
65764
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
65765
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
65734
65766
  *
65735
65767
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
65736
65768
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -65740,12 +65772,68 @@ var ParallClient = class _ParallClient {
65740
65772
  async deleteEdgeDevice(orgId, edgeId) {
65741
65773
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
65742
65774
  }
65775
+ /**
65776
+ * Remove the interactive human caller's own offline BYOC registration.
65777
+ *
65778
+ * Synchronous and idempotent: a committed removal and a repeat after removal
65779
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
65780
+ * callers must not clear local device identity until this method resolves.
65781
+ */
65782
+ async unregisterEdgeDevice(orgId, edgeId) {
65783
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
65784
+ }
65743
65785
  async getEdgeOnboarding(orgId) {
65744
65786
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
65745
65787
  }
65746
65788
  async listEdgeProfiles(orgId, edgeId) {
65747
65789
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
65748
65790
  }
65791
+ /**
65792
+ * Execute a registry clip command on an Edge device.
65793
+ *
65794
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
65795
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
65796
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
65797
+ * nothing for the caller's own online device).
65798
+ *
65799
+ * Returns the result envelope on completion (`success` may be false when the
65800
+ * command RAN and failed — `error`/`error_code` describe why). Everything
65801
+ * else throws a typed {@link ApiError}; match on `err.code`:
65802
+ *
65803
+ * Safe to retry (guaranteed nothing was dispatched):
65804
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
65805
+ * Bounded backoff, same `correlation_id` across the loop.
65806
+ * - `EDGE_BUSY` 409 — the device is executing another request.
65807
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
65808
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
65809
+ *
65810
+ * NOT retryable:
65811
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
65812
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
65813
+ * automatically: verify the effect first, then decide. The message carries
65814
+ * the request id for audit.
65815
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
65816
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
65817
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
65818
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
65819
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
65820
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
65821
+ */
65822
+ async execEdgeClip(orgId, req) {
65823
+ const t = req.timeout;
65824
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
65825
+ const timeoutMs = serverTimeout + 1e4;
65826
+ try {
65827
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
65828
+ timeoutMs
65829
+ });
65830
+ } catch (err) {
65831
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
65832
+ err.code = err.extras.error_code;
65833
+ }
65834
+ throw err;
65835
+ }
65836
+ }
65749
65837
  // ---- Clip connections ----
65750
65838
  async listClipConnections(orgId, clipId) {
65751
65839
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
@@ -51651,10 +51651,17 @@ var ENDPOINTS = {
51651
51651
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51652
51652
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
51653
51653
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
51654
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
51654
51655
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
51655
51656
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
51657
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
51656
51658
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51657
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
51659
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51660
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51661
+ // Pinix Hub catalog proxy above is a different, id-less surface)
51662
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51663
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51664
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
51658
51665
  };
51659
51666
  var WS_EVENTS = {
51660
51667
  // Client -> Server
@@ -53595,6 +53602,31 @@ var ParallClient = class _ParallClient {
53595
53602
  const resp = await this.request("GET", url);
53596
53603
  return resp.data;
53597
53604
  }
53605
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53606
+ /**
53607
+ * List registry clips visible to the org: its own plus public+approved
53608
+ * cross-org entries. This is the surface `installRegistryClip` and clip
53609
+ * connections operate on — NOT the Pinix catalog proxy
53610
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53611
+ */
53612
+ async listOrgRegistryClips(orgId) {
53613
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
53614
+ return resp ?? [];
53615
+ }
53616
+ /**
53617
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
53618
+ * copy). Idempotent: installing an already-installed clip returns the same
53619
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
53620
+ * not eligible (cross-org requires public + approved).
53621
+ */
53622
+ async installRegistryClip(orgId, clipId) {
53623
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53624
+ }
53625
+ /** List the org's installed registry clips (full entries). */
53626
+ async listInstalledRegistryClips(orgId) {
53627
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53628
+ return resp ?? [];
53629
+ }
53598
53630
  // ---- Edge devices ----
53599
53631
  async listEdgeDevices(orgId) {
53600
53632
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -53612,8 +53644,8 @@ var ParallClient = class _ParallClient {
53612
53644
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
53613
53645
  }
53614
53646
  /**
53615
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
53616
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
53647
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
53648
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
53617
53649
  *
53618
53650
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
53619
53651
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -53623,12 +53655,68 @@ var ParallClient = class _ParallClient {
53623
53655
  async deleteEdgeDevice(orgId, edgeId) {
53624
53656
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
53625
53657
  }
53658
+ /**
53659
+ * Remove the interactive human caller's own offline BYOC registration.
53660
+ *
53661
+ * Synchronous and idempotent: a committed removal and a repeat after removal
53662
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
53663
+ * callers must not clear local device identity until this method resolves.
53664
+ */
53665
+ async unregisterEdgeDevice(orgId, edgeId) {
53666
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
53667
+ }
53626
53668
  async getEdgeOnboarding(orgId) {
53627
53669
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
53628
53670
  }
53629
53671
  async listEdgeProfiles(orgId, edgeId) {
53630
53672
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
53631
53673
  }
53674
+ /**
53675
+ * Execute a registry clip command on an Edge device.
53676
+ *
53677
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
53678
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
53679
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
53680
+ * nothing for the caller's own online device).
53681
+ *
53682
+ * Returns the result envelope on completion (`success` may be false when the
53683
+ * command RAN and failed — `error`/`error_code` describe why). Everything
53684
+ * else throws a typed {@link ApiError}; match on `err.code`:
53685
+ *
53686
+ * Safe to retry (guaranteed nothing was dispatched):
53687
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
53688
+ * Bounded backoff, same `correlation_id` across the loop.
53689
+ * - `EDGE_BUSY` 409 — the device is executing another request.
53690
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
53691
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
53692
+ *
53693
+ * NOT retryable:
53694
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
53695
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
53696
+ * automatically: verify the effect first, then decide. The message carries
53697
+ * the request id for audit.
53698
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
53699
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
53700
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
53701
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
53702
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
53703
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
53704
+ */
53705
+ async execEdgeClip(orgId, req) {
53706
+ const t = req.timeout;
53707
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
53708
+ const timeoutMs = serverTimeout + 1e4;
53709
+ try {
53710
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
53711
+ timeoutMs
53712
+ });
53713
+ } catch (err) {
53714
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
53715
+ err.code = err.extras.error_code;
53716
+ }
53717
+ throw err;
53718
+ }
53719
+ }
53632
53720
  // ---- Clip connections ----
53633
53721
  async listClipConnections(orgId, clipId) {
53634
53722
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
@@ -59715,6 +59803,27 @@ parall clip invoke github-tools list-repos '{"org":"acme"}'
59715
59803
 
59716
59804
  Results are JSON on stdout; failures print an error.
59717
59805
 
59806
+ ## Execute on an Edge device (registry clips)
59807
+
59808
+ Registry clips run on an Edge \u2014 a member's desktop, or an org-shared cloud
59809
+ profile. **Name the target explicitly.** A cloud profile has NO implicit
59810
+ route; omitting the target entirely is a desktop-only legacy form that
59811
+ reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
59812
+
59813
+ \`\`\`bash
59814
+ parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
59815
+ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
59816
+ \`\`\`
59817
+
59818
+ - A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
59819
+ connection its maintainer bound (\`ccn_\u2026\` id or alias). That binding IS your
59820
+ authorization; without one the server answers \`HOSTED_CONNECTION_REQUIRED\`
59821
+ and the fix is to ask an owner/admin to bind the clip, never to retry.
59822
+ - \`--edge <edgeId>\` targets only a desktop device YOU own.
59823
+ - Cold cloud profiles are handled by the CLI: it absorbs \`EDGE_ACTIVATING\`
59824
+ with a bounded wait (~60s) while the profile starts. If the command still
59825
+ fails, report the error \u2014 do not blind-retry in a loop.
59826
+
59718
59827
  ## Behavior rules
59719
59828
 
59720
59829
  - An authorization error (clip not bound to you) is a fail-fast: ask the
@@ -59724,6 +59833,12 @@ Results are JSON on stdout; failures print an error.
59724
59833
  - Hosted browser activation is handled by the CLI: it waits (bounded) while a
59725
59834
  cold hosted browser starts, so if the invoke still fails, report the error \u2014
59726
59835
  do not blind-retry in a loop.
59836
+ - **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
59837
+ dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
59838
+ could post, order or delete twice. Verify the effect through the system you
59839
+ acted on (or tell the human, quoting the request id from the error) before
59840
+ ever re-running. \`EDGE_BUSY\` is the opposite: guaranteed-unexecuted \u2014 wait
59841
+ briefly, then one retry is safe.
59727
59842
  - A clip may act through a person's real logged-in account \u2014 outward,
59728
59843
  irreversible, or spending actions (post, order, delete, pay) get the same
59729
59844
  caution as any shared-state change: confirm when intent isn't explicit.
@@ -51651,10 +51651,17 @@ var ENDPOINTS = {
51651
51651
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51652
51652
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
51653
51653
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
51654
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
51654
51655
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
51655
51656
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
51657
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
51656
51658
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51657
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
51659
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51660
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51661
+ // Pinix Hub catalog proxy above is a different, id-less surface)
51662
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51663
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51664
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
51658
51665
  };
51659
51666
  var WS_EVENTS = {
51660
51667
  // Client -> Server
@@ -53595,6 +53602,31 @@ var ParallClient = class _ParallClient {
53595
53602
  const resp = await this.request("GET", url);
53596
53603
  return resp.data;
53597
53604
  }
53605
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53606
+ /**
53607
+ * List registry clips visible to the org: its own plus public+approved
53608
+ * cross-org entries. This is the surface `installRegistryClip` and clip
53609
+ * connections operate on — NOT the Pinix catalog proxy
53610
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53611
+ */
53612
+ async listOrgRegistryClips(orgId) {
53613
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
53614
+ return resp ?? [];
53615
+ }
53616
+ /**
53617
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
53618
+ * copy). Idempotent: installing an already-installed clip returns the same
53619
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
53620
+ * not eligible (cross-org requires public + approved).
53621
+ */
53622
+ async installRegistryClip(orgId, clipId) {
53623
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53624
+ }
53625
+ /** List the org's installed registry clips (full entries). */
53626
+ async listInstalledRegistryClips(orgId) {
53627
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53628
+ return resp ?? [];
53629
+ }
53598
53630
  // ---- Edge devices ----
53599
53631
  async listEdgeDevices(orgId) {
53600
53632
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -53612,8 +53644,8 @@ var ParallClient = class _ParallClient {
53612
53644
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
53613
53645
  }
53614
53646
  /**
53615
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
53616
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
53647
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
53648
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
53617
53649
  *
53618
53650
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
53619
53651
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -53623,12 +53655,68 @@ var ParallClient = class _ParallClient {
53623
53655
  async deleteEdgeDevice(orgId, edgeId) {
53624
53656
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
53625
53657
  }
53658
+ /**
53659
+ * Remove the interactive human caller's own offline BYOC registration.
53660
+ *
53661
+ * Synchronous and idempotent: a committed removal and a repeat after removal
53662
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
53663
+ * callers must not clear local device identity until this method resolves.
53664
+ */
53665
+ async unregisterEdgeDevice(orgId, edgeId) {
53666
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
53667
+ }
53626
53668
  async getEdgeOnboarding(orgId) {
53627
53669
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
53628
53670
  }
53629
53671
  async listEdgeProfiles(orgId, edgeId) {
53630
53672
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
53631
53673
  }
53674
+ /**
53675
+ * Execute a registry clip command on an Edge device.
53676
+ *
53677
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
53678
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
53679
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
53680
+ * nothing for the caller's own online device).
53681
+ *
53682
+ * Returns the result envelope on completion (`success` may be false when the
53683
+ * command RAN and failed — `error`/`error_code` describe why). Everything
53684
+ * else throws a typed {@link ApiError}; match on `err.code`:
53685
+ *
53686
+ * Safe to retry (guaranteed nothing was dispatched):
53687
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
53688
+ * Bounded backoff, same `correlation_id` across the loop.
53689
+ * - `EDGE_BUSY` 409 — the device is executing another request.
53690
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
53691
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
53692
+ *
53693
+ * NOT retryable:
53694
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
53695
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
53696
+ * automatically: verify the effect first, then decide. The message carries
53697
+ * the request id for audit.
53698
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
53699
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
53700
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
53701
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
53702
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
53703
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
53704
+ */
53705
+ async execEdgeClip(orgId, req) {
53706
+ const t = req.timeout;
53707
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
53708
+ const timeoutMs = serverTimeout + 1e4;
53709
+ try {
53710
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
53711
+ timeoutMs
53712
+ });
53713
+ } catch (err) {
53714
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
53715
+ err.code = err.extras.error_code;
53716
+ }
53717
+ throw err;
53718
+ }
53719
+ }
53632
53720
  // ---- Clip connections ----
53633
53721
  async listClipConnections(orgId, clipId) {
53634
53722
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
@@ -59715,6 +59803,27 @@ parall clip invoke github-tools list-repos '{"org":"acme"}'
59715
59803
 
59716
59804
  Results are JSON on stdout; failures print an error.
59717
59805
 
59806
+ ## Execute on an Edge device (registry clips)
59807
+
59808
+ Registry clips run on an Edge \u2014 a member's desktop, or an org-shared cloud
59809
+ profile. **Name the target explicitly.** A cloud profile has NO implicit
59810
+ route; omitting the target entirely is a desktop-only legacy form that
59811
+ reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
59812
+
59813
+ \`\`\`bash
59814
+ parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
59815
+ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
59816
+ \`\`\`
59817
+
59818
+ - A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
59819
+ connection its maintainer bound (\`ccn_\u2026\` id or alias). That binding IS your
59820
+ authorization; without one the server answers \`HOSTED_CONNECTION_REQUIRED\`
59821
+ and the fix is to ask an owner/admin to bind the clip, never to retry.
59822
+ - \`--edge <edgeId>\` targets only a desktop device YOU own.
59823
+ - Cold cloud profiles are handled by the CLI: it absorbs \`EDGE_ACTIVATING\`
59824
+ with a bounded wait (~60s) while the profile starts. If the command still
59825
+ fails, report the error \u2014 do not blind-retry in a loop.
59826
+
59718
59827
  ## Behavior rules
59719
59828
 
59720
59829
  - An authorization error (clip not bound to you) is a fail-fast: ask the
@@ -59724,6 +59833,12 @@ Results are JSON on stdout; failures print an error.
59724
59833
  - Hosted browser activation is handled by the CLI: it waits (bounded) while a
59725
59834
  cold hosted browser starts, so if the invoke still fails, report the error \u2014
59726
59835
  do not blind-retry in a loop.
59836
+ - **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
59837
+ dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
59838
+ could post, order or delete twice. Verify the effect through the system you
59839
+ acted on (or tell the human, quoting the request id from the error) before
59840
+ ever re-running. \`EDGE_BUSY\` is the opposite: guaranteed-unexecuted \u2014 wait
59841
+ briefly, then one retry is safe.
59727
59842
  - A clip may act through a person's real logged-in account \u2014 outward,
59728
59843
  irreversible, or spending actions (post, order, delete, pay) get the same
59729
59844
  caution as any shared-state change: confirm when intent isn't explicit.
@@ -491,10 +491,17 @@ var init_constants = __esm({
491
491
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
492
492
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
493
493
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
494
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
494
495
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
495
496
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
497
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
496
498
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
497
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
499
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
500
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
501
+ // Pinix Hub catalog proxy above is a different, id-less surface)
502
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
503
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
504
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
498
505
  };
499
506
  WS_EVENTS = {
500
507
  // Client -> Server
@@ -2476,6 +2483,31 @@ var init_client = __esm({
2476
2483
  const resp = await this.request("GET", url);
2477
2484
  return resp.data;
2478
2485
  }
2486
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
2487
+ /**
2488
+ * List registry clips visible to the org: its own plus public+approved
2489
+ * cross-org entries. This is the surface `installRegistryClip` and clip
2490
+ * connections operate on — NOT the Pinix catalog proxy
2491
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
2492
+ */
2493
+ async listOrgRegistryClips(orgId) {
2494
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
2495
+ return resp ?? [];
2496
+ }
2497
+ /**
2498
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
2499
+ * copy). Idempotent: installing an already-installed clip returns the same
2500
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
2501
+ * not eligible (cross-org requires public + approved).
2502
+ */
2503
+ async installRegistryClip(orgId, clipId) {
2504
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
2505
+ }
2506
+ /** List the org's installed registry clips (full entries). */
2507
+ async listInstalledRegistryClips(orgId) {
2508
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
2509
+ return resp ?? [];
2510
+ }
2479
2511
  // ---- Edge devices ----
2480
2512
  async listEdgeDevices(orgId) {
2481
2513
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -2493,8 +2525,8 @@ var init_client = __esm({
2493
2525
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
2494
2526
  }
2495
2527
  /**
2496
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
2497
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
2528
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
2529
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
2498
2530
  *
2499
2531
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
2500
2532
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -2504,12 +2536,68 @@ var init_client = __esm({
2504
2536
  async deleteEdgeDevice(orgId, edgeId) {
2505
2537
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
2506
2538
  }
2539
+ /**
2540
+ * Remove the interactive human caller's own offline BYOC registration.
2541
+ *
2542
+ * Synchronous and idempotent: a committed removal and a repeat after removal
2543
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
2544
+ * callers must not clear local device identity until this method resolves.
2545
+ */
2546
+ async unregisterEdgeDevice(orgId, edgeId) {
2547
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
2548
+ }
2507
2549
  async getEdgeOnboarding(orgId) {
2508
2550
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
2509
2551
  }
2510
2552
  async listEdgeProfiles(orgId, edgeId) {
2511
2553
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
2512
2554
  }
2555
+ /**
2556
+ * Execute a registry clip command on an Edge device.
2557
+ *
2558
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
2559
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
2560
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
2561
+ * nothing for the caller's own online device).
2562
+ *
2563
+ * Returns the result envelope on completion (`success` may be false when the
2564
+ * command RAN and failed — `error`/`error_code` describe why). Everything
2565
+ * else throws a typed {@link ApiError}; match on `err.code`:
2566
+ *
2567
+ * Safe to retry (guaranteed nothing was dispatched):
2568
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
2569
+ * Bounded backoff, same `correlation_id` across the loop.
2570
+ * - `EDGE_BUSY` 409 — the device is executing another request.
2571
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
2572
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
2573
+ *
2574
+ * NOT retryable:
2575
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
2576
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
2577
+ * automatically: verify the effect first, then decide. The message carries
2578
+ * the request id for audit.
2579
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
2580
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
2581
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
2582
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
2583
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
2584
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
2585
+ */
2586
+ async execEdgeClip(orgId, req) {
2587
+ const t = req.timeout;
2588
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
2589
+ const timeoutMs = serverTimeout + 1e4;
2590
+ try {
2591
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
2592
+ timeoutMs
2593
+ });
2594
+ } catch (err) {
2595
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
2596
+ err.code = err.extras.error_code;
2597
+ }
2598
+ throw err;
2599
+ }
2600
+ }
2513
2601
  // ---- Clip connections ----
2514
2602
  async listClipConnections(orgId, clipId) {
2515
2603
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.47.0",
3
+ "version": "1.48.0",
4
4
  "description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,11 +33,11 @@
33
33
  "@aws-sdk/client-s3": "3.984.0",
34
34
  "@pinixai/bb-browser-pro": "0.15.0",
35
35
  "ws": "^8.18.0",
36
- "@parall/agent-core": "1.47.0",
37
- "@parall/claude-agent": "1.47.0",
38
- "@parall/openclaw-agent": "1.47.0",
39
- "@parall/sdk": "1.47.0",
40
- "@parall/codex-agent": "1.47.0"
36
+ "@parall/sdk": "1.48.0",
37
+ "@parall/codex-agent": "1.48.0",
38
+ "@parall/openclaw-agent": "1.48.0",
39
+ "@parall/claude-agent": "1.48.0",
40
+ "@parall/agent-core": "1.48.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.0.0",