@camstack/addon-admin-ui 0.1.33 → 0.1.35

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.
Files changed (34) hide show
  1. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DJQnlBBe.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DYU-qDnu.js} +1 -1
  2. package/dist/assets/{devices-BnLu5dxg.js → devices-D4k34mru.js} +1 -1
  3. package/dist/assets/{hostInit-DQ5UjxJa.js → hostInit-CBKchP1G.js} +1 -1
  4. package/dist/assets/index-BEewR-UX.js +1 -0
  5. package/dist/assets/{index-BmzE49kT.js → index-BGZqVJZt.js} +1 -1
  6. package/dist/assets/{index-DIIqKU3D.js → index-BI3Z7SSi.js} +1 -1
  7. package/dist/assets/index-BhNfKRLZ.js +1156 -0
  8. package/dist/assets/index-BqMPfvQx.js +1 -0
  9. package/dist/assets/{index-dePEzy7t.js → index-Bwcmc2hW.js} +1 -1
  10. package/dist/assets/index-CP1R6E2R.js +88 -0
  11. package/dist/assets/index-CwjnPcOV.js +152 -0
  12. package/dist/assets/{index-CMxz0koW.js → index-D92THlot.js} +1 -1
  13. package/dist/assets/index-Dmon2mdO.js +1 -0
  14. package/dist/assets/index-KlKJOjpC.js +1 -0
  15. package/dist/assets/index-bBEaoekV.css +1 -0
  16. package/dist/assets/method-access-map-BFPyQ2-G.js +1 -0
  17. package/dist/assets/{remoteEntry-D28y34bi.js → remoteEntry-BRybuw2S.js} +1 -1
  18. package/dist/assets/{schemas-DZqswtjx.js → schemas-BEt-CulV.js} +1 -1
  19. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-BXwMMqlX.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-DUkp33c_.js} +2 -2
  20. package/dist/index.html +7 -7
  21. package/dist/mf-entry-bootstrap-0.js +2 -2
  22. package/dist/server/addon.js +196 -235
  23. package/dist/server/addon.js.map +1 -1
  24. package/dist/sw.js +1 -1
  25. package/package.json +1 -1
  26. package/dist/assets/index-DFRMwfPq.js +0 -1
  27. package/dist/assets/index-DRW7rVhn.js +0 -1133
  28. package/dist/assets/index-DYSrVZ4G.js +0 -1
  29. package/dist/assets/index-DeVjiPqm.js +0 -1
  30. package/dist/assets/index-DuHBxCOe.js +0 -1
  31. package/dist/assets/index-Dv0-1gwL.js +0 -80
  32. package/dist/assets/index-NNW--T4H.js +0 -80
  33. package/dist/assets/index-n9RexO7D.css +0 -1
  34. package/dist/assets/method-access-map-OoQznstl.js +0 -1
@@ -8502,10 +8502,30 @@ const StatusSchema = object({
8502
8502
  }
8503
8503
  });
8504
8504
  const LinkStateSchema = _enum(["unlinked", "linked", "error"]);
8505
+ const ExportSetupFieldSchema = object({
8506
+ label: string(),
8507
+ value: string(),
8508
+ /** Mask the value by default + render a reveal toggle (client id, secrets). */
8509
+ secret: boolean().optional()
8510
+ });
8511
+ const ExportSetupSchema = object({
8512
+ /** A string to render as a scannable QR — HAP `X-HM://…` URI, a pairing URL, etc. Omitted when there's nothing to scan. */
8513
+ qr: string().optional(),
8514
+ /** Label/value rows shown with a copy button (HAP setup code, OAuth URLs, client id, linked-account count, …). */
8515
+ fields: array(ExportSetupFieldSchema).readonly().optional(),
8516
+ /** Free-form operator instructions rendered above the fields. */
8517
+ note: string().optional()
8518
+ });
8505
8519
  const DeviceExportStatusSchema = object({
8506
8520
  linkState: LinkStateSchema,
8507
8521
  exposedDeviceCount: number(),
8508
- error: string().optional()
8522
+ error: string().optional(),
8523
+ /**
8524
+ * Optional pairing/account info the panel renders in a generic
8525
+ * "Setup" section. Addon-agnostic — the addon id identifies the
8526
+ * export target, never an `ecosystem` key here.
8527
+ */
8528
+ setup: ExportSetupSchema.optional()
8509
8529
  });
8510
8530
  const DeviceKindSchema = string();
8511
8531
  const ExposedDeviceSchema = object({
@@ -10175,51 +10195,6 @@ const AuthResultSchema = object({
10175
10195
  validateToken: method(object({ token: string() }), AuthResultSchema.nullable())
10176
10196
  }
10177
10197
  });
10178
- const AuthProviderInfoSchema = object({
10179
- /** Stable id matching the addon id (used for `getLoginUrl({addonId,…})`). */
10180
- addonId: string(),
10181
- /**
10182
- * Per-instance id when one addon registers multiple "logical"
10183
- * providers (e.g. OIDC with Google + Microsoft + custom). The login
10184
- * URL becomes `/addon/${addonId}/${instanceId}/start` — handler reads
10185
- * `:instanceId` from the route. Empty/unset means the addon is a
10186
- * single-instance provider; the URL is `/addon/${addonId}/start`.
10187
- */
10188
- instanceId: string().optional(),
10189
- /** Display label shown on the login button + admin row. */
10190
- displayName: string(),
10191
- /** Optional iconography hint (lucide-react icon name OR emoji). */
10192
- icon: string().optional(),
10193
- /** When true, the provider exposes a redirect-based login flow
10194
- * (`getLoginUrl` returns a URL the browser navigates to). */
10195
- hasRedirectFlow: boolean(),
10196
- /** When true, the provider exposes a credential-form login flow
10197
- * (`validateCredentials` accepts username + password). */
10198
- hasCredentialFlow: boolean(),
10199
- /** Provider kind, drives admin-UI hint dispatch (oidc / saml / totp / …). */
10200
- kind: string().optional(),
10201
- /** Operator-facing status string (e.g. "Connected to https://login.acme.com"). */
10202
- status: string().optional(),
10203
- /** When false, the provider is registered but disabled by config; the
10204
- * UI surfaces it as inactive without enumerating it for login. */
10205
- enabled: boolean()
10206
- });
10207
- ({
10208
- methods: {
10209
- /** All registered auth providers, both enabled and disabled. */
10210
- listProviders: method(_void(), array(AuthProviderInfoSchema).readonly()),
10211
- /**
10212
- * Toggle a provider's enabled flag. Disabled providers stay
10213
- * registered but aren't surfaced on the login page. The orchestrator
10214
- * persists the state in `addon-settings` so it survives restarts.
10215
- */
10216
- setProviderEnabled: method(
10217
- object({ addonId: string(), enabled: boolean() }),
10218
- object({ success: literal(true) }),
10219
- { kind: "mutation", auth: "admin" }
10220
- )
10221
- }
10222
- });
10223
10198
  const NetworkEndpointSchema = object({
10224
10199
  url: string(),
10225
10200
  hostname: string(),
@@ -10251,55 +10226,13 @@ const NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
10251
10226
  getEndpoint: method(_void(), NetworkEndpointSchema.nullable()),
10252
10227
  getStatus: method(_void(), NetworkAccessStatusSchema),
10253
10228
  /**
10254
- * Enumerate every active ingress entry. Default implementation (when
10255
- * the provider omits this method) is derived from `getEndpoint()` —
10256
- * see the remote-access orchestrator for the fallback path.
10229
+ * Enumerate every active ingress entry. Providers that expose only a
10230
+ * single endpoint may omit this method; callers fall back to
10231
+ * `getEndpoint()` in that case.
10257
10232
  */
10258
10233
  listEndpoints: method(_void(), array(NetworkEndpointEntrySchema).readonly())
10259
10234
  }
10260
10235
  });
10261
- const RemoteAccessEndpointSchema = object({
10262
- url: string(),
10263
- hostname: string(),
10264
- port: number(),
10265
- protocol: _enum(["http", "https"])
10266
- });
10267
- const RemoteAccessProviderInfoSchema = object({
10268
- /** Stable id matching the addon id. */
10269
- addonId: string(),
10270
- /** Display label shown on the admin row — sourced from the addon manifest. */
10271
- displayName: string(),
10272
- /** When false, the provider is registered but disabled. */
10273
- enabled: boolean(),
10274
- /** True when the underlying tunnel/connection is up. */
10275
- connected: boolean(),
10276
- /** Public-facing endpoint, when connected. Null otherwise. */
10277
- endpoint: RemoteAccessEndpointSchema.nullable(),
10278
- /** Last error message (when connected=false), if available. */
10279
- error: string().optional()
10280
- });
10281
- ({
10282
- methods: {
10283
- /** All registered remote-access providers + their live status. */
10284
- listProviders: method(_void(), array(RemoteAccessProviderInfoSchema).readonly()),
10285
- /**
10286
- * Start a specific provider's tunnel. Per-provider config still
10287
- * lives on the addon's settings panel; this is just the on/off
10288
- * trigger so the admin UI can manage the lifecycle from one place.
10289
- */
10290
- startProvider: method(
10291
- object({ addonId: string() }),
10292
- RemoteAccessEndpointSchema,
10293
- { kind: "mutation", auth: "admin" }
10294
- ),
10295
- /** Stop a specific provider's tunnel (idempotent on already-stopped). */
10296
- stopProvider: method(
10297
- object({ addonId: string() }),
10298
- object({ success: literal(true) }),
10299
- { kind: "mutation", auth: "admin" }
10300
- )
10301
- }
10302
- });
10303
10236
  const TurnServerSchema = object({
10304
10237
  /** Single URL or list of URLs (e.g. "turn:turn.example.com:3478?transport=udp"). */
10305
10238
  urls: union([string(), array(string())]),
@@ -10319,45 +10252,6 @@ const TurnServerSchema = object({
10319
10252
  )
10320
10253
  }
10321
10254
  });
10322
- const TurnProviderInfoSchema = object({
10323
- /** Stable id matching the addon id. */
10324
- addonId: string(),
10325
- /** Display label shown on the admin row — sourced from the addon manifest. */
10326
- displayName: string(),
10327
- /** When false, the provider is registered but disabled. */
10328
- enabled: boolean(),
10329
- /** Number of servers this provider is currently exposing. */
10330
- serverCount: number(),
10331
- /**
10332
- * Flat list of every TURN/STUN URL this provider currently exposes.
10333
- * One row per URL (multi-URL ICE server entries are flattened). The
10334
- * admin UI shows this in a compact per-provider list so operators
10335
- * can verify what's actually being negotiated without having to dig
10336
- * into the combined `getAllServers` output.
10337
- */
10338
- urls: array(string()).readonly(),
10339
- /** Last fetch error (when serverCount=0 due to API failure), if any. */
10340
- error: string().optional()
10341
- });
10342
- ({
10343
- methods: {
10344
- /** All registered TURN providers + per-provider stats. */
10345
- listProviders: method(_void(), array(TurnProviderInfoSchema).readonly()),
10346
- /**
10347
- * Combined list of TURN/STUN servers from all ENABLED providers.
10348
- * Consumed by the WebRTC layer at session-creation time —
10349
- * implementations may fetch fresh short-lived credentials each
10350
- * call (e.g. Cloudflare API), so consumers SHOULD call per-session.
10351
- */
10352
- getAllServers: method(_void(), array(TurnServerSchema).readonly()),
10353
- /** Toggle a provider's enabled flag. */
10354
- setProviderEnabled: method(
10355
- object({ addonId: string(), enabled: boolean() }),
10356
- object({ success: literal(true) }),
10357
- { kind: "mutation", auth: "admin" }
10358
- )
10359
- }
10360
- });
10361
10255
  const SnapshotImageSchema = object({
10362
10256
  base64: string(),
10363
10257
  contentType: string()
@@ -11825,7 +11719,7 @@ const AllowedAddressesSchema = object({
11825
11719
  )
11826
11720
  }
11827
11721
  });
11828
- const MeshEndpointSchema$1 = object({
11722
+ const MeshEndpointSchema = object({
11829
11723
  /** Stable identifier within the provider (e.g. `mesh-ipv4`, `magicdns`, `funnel`). */
11830
11724
  id: string(),
11831
11725
  /** Operator-facing label (e.g. "Mesh IPv4", "MagicDNS"). */
@@ -11898,7 +11792,7 @@ const MeshStatusSchema = object({
11898
11792
  /** Number of peers visible to this host (excluding self). */
11899
11793
  peerCount: number(),
11900
11794
  /** Every endpoint this provider exposes for the current host. */
11901
- endpoints: array(MeshEndpointSchema$1).readonly(),
11795
+ endpoints: array(MeshEndpointSchema).readonly(),
11902
11796
  /** Last error from the daemon, when not joined. */
11903
11797
  error: string().optional(),
11904
11798
  // ── Account / tenant identity (generic across providers) ────────
@@ -11931,7 +11825,25 @@ const MeshStatusSchema = object({
11931
11825
  * doesn't rotate keys for the bound host. Operator-facing surface
11932
11826
  * for "your access expires on …" banners.
11933
11827
  */
11934
- keyExpiry: number().nullable()
11828
+ keyExpiry: number().nullable(),
11829
+ // ── Onboard-daemon handoff (Tailscale, generic slot) ────────────
11830
+ /**
11831
+ * When the provider runs its OWN mesh daemon (e.g. the Tailscale
11832
+ * client addon in `onboard` mode spawns a private `tailscaled`),
11833
+ * this carries the local control-socket path. Companion addons that
11834
+ * must drive the SAME daemon — chiefly `tailscale-ingress` for
11835
+ * Serve/Funnel — read it to point their CLI at the right socket
11836
+ * instead of the system default. Empty when the provider uses the
11837
+ * host's system daemon (or doesn't have the concept).
11838
+ */
11839
+ daemonSocket: string().optional(),
11840
+ /**
11841
+ * Path to the mesh CLI binary the provider downloaded for onboard
11842
+ * mode. Companion addons reuse it so they don't need a system
11843
+ * install when the operator chose a fully self-contained mesh.
11844
+ * Empty in host mode.
11845
+ */
11846
+ daemonCliPath: string().optional()
11935
11847
  });
11936
11848
  ({
11937
11849
  methods: {
@@ -12043,105 +11955,6 @@ const MeshStatusSchema = object({
12043
11955
  // tabs driven by this cap.
12044
11956
  }
12045
11957
  });
12046
- const MeshEndpointSchema = object({
12047
- id: string(),
12048
- label: string(),
12049
- scope: _enum(["mesh", "public"]),
12050
- url: string(),
12051
- hostname: string(),
12052
- port: number(),
12053
- protocol: _enum(["http", "https"])
12054
- });
12055
- const MeshProviderInfoSchema = object({
12056
- /** Stable id matching the addon id. */
12057
- addonId: string(),
12058
- /** Display label shown on the admin row — sourced from the addon manifest. */
12059
- displayName: string(),
12060
- /** True when the host is joined to this provider's mesh. */
12061
- joined: boolean(),
12062
- /** Local mesh IP (empty when not joined). */
12063
- meshIp: string(),
12064
- /** MagicDNS / mesh hostname (empty when not configured). */
12065
- magicDnsHostname: string(),
12066
- /** Peer count (excluding self). */
12067
- peerCount: number(),
12068
- /** Active endpoints (mesh IP + MagicDNS + optional public Funnel). */
12069
- endpoints: array(MeshEndpointSchema).readonly(),
12070
- /** Last error reported by the provider. */
12071
- error: string().optional(),
12072
- // ── Generic identity fields mirrored from MeshStatus ─────────────
12073
- /** Tenant / tailnet / network display name. Empty pre-join. */
12074
- tenantName: string(),
12075
- /** Mesh DNS suffix (e.g. tailXXXX.ts.net). Empty when not configured. */
12076
- magicDnsSuffix: string(),
12077
- /** Authenticated user / account login. Null for token-only providers. */
12078
- userLogin: string().nullable(),
12079
- /** Provider control-plane URL. */
12080
- controlPlaneUrl: string(),
12081
- /** Machine-key expiry (epoch ms). Null when keys don't rotate. */
12082
- keyExpiry: number().nullable()
12083
- });
12084
- ({
12085
- methods: {
12086
- /** All registered mesh-network providers + live status. */
12087
- listProviders: method(_void(), array(MeshProviderInfoSchema).readonly()),
12088
- /**
12089
- * Join the mesh of a specific provider. Per-provider config still
12090
- * lives on its settings panel; the orchestrator forwards.
12091
- */
12092
- joinProvider: method(
12093
- object({
12094
- addonId: string(),
12095
- authKey: string().min(8),
12096
- hostname: string().optional()
12097
- }),
12098
- object({ joined: literal(true) }),
12099
- { kind: "mutation" }
12100
- ),
12101
- leaveProvider: method(
12102
- object({ addonId: string() }),
12103
- object({ success: literal(true) }),
12104
- { kind: "mutation" }
12105
- ),
12106
- /**
12107
- * Browser-redirect login flow. Forwards to the named provider's
12108
- * `mesh-network.startLogin` and returns the URL the daemon
12109
- * prints. UI opens it in a new tab, then polls `listProviders`
12110
- * for `joined: true`.
12111
- */
12112
- startLoginProvider: method(
12113
- object({
12114
- addonId: string(),
12115
- hostname: string().optional()
12116
- }),
12117
- object({ loginUrl: string() }),
12118
- { kind: "mutation" }
12119
- ),
12120
- /**
12121
- * Sign out of the provider's account entirely (`mesh-network.logout`).
12122
- * Distinct from `leaveProvider` which only takes the host off-mesh;
12123
- * `logoutProvider` wipes credentials so the next start requires a
12124
- * fresh login.
12125
- */
12126
- logoutProvider: method(
12127
- object({ addonId: string() }),
12128
- object({ loggedOut: literal(true) }),
12129
- { kind: "mutation" }
12130
- ),
12131
- /**
12132
- * Per-provider peer list. Forwards to `mesh-network.listPeers` on
12133
- * the addressed provider. Separate from `listProviders` because
12134
- * peer payloads can be large on a heavily-populated tailnet —
12135
- * fetch only when the operator opens the Peers tab.
12136
- */
12137
- listProviderPeers: method(
12138
- object({ addonId: string() }),
12139
- object({
12140
- peers: array(MeshPeerSchema).readonly()
12141
- })
12142
- )
12143
- }
12144
- });
12145
11958
  const MethodAccessSchema = _enum(["view", "create", "delete"]);
12146
11959
  const AllowedProviderSchema = union([literal("*"), array(string())]);
12147
11960
  const AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
@@ -12817,6 +12630,21 @@ const AddonAutoUpdateSchema = ChannelWithInheritSchema;
12817
12630
  const RestartAddonResultSchema = unknown();
12818
12631
  const InstallPackageResultSchema = unknown();
12819
12632
  const ReloadPackagesResultSchema = unknown();
12633
+ const UpdateFrameworkPackageResultSchema = object({
12634
+ packageName: string(),
12635
+ fromVersion: string(),
12636
+ toVersion: string(),
12637
+ /** Ms-epoch the server scheduled its self-restart. */
12638
+ restartingAt: number()
12639
+ });
12640
+ const FrameworkPackageStatusSchema = object({
12641
+ packageName: string(),
12642
+ currentVersion: string(),
12643
+ latestVersion: string().nullable(),
12644
+ hasUpdate: boolean(),
12645
+ /** Optional manifest description for the row tooltip. */
12646
+ description: string().optional()
12647
+ });
12820
12648
  const LogStreamEntrySchema = object({
12821
12649
  timestamp: string(),
12822
12650
  level: string(),
@@ -12876,13 +12704,29 @@ const CustomActionInputSchema = object({
12876
12704
  object({ query: string().optional() }),
12877
12705
  array(SearchResultSchema)
12878
12706
  ),
12707
+ /**
12708
+ * Available package updates for a node. `nodeId` omitted (or
12709
+ * `'hub'`) checks the hub's own installed packages; an agent
12710
+ * `nodeId` checks that agent's installed roster against npm
12711
+ * (the hub does the npm lookups + diff — agents stay npm-free).
12712
+ */
12879
12713
  listUpdates: method(
12880
- _void(),
12714
+ object({ nodeId: string().optional() }),
12881
12715
  array(PackageUpdateSchema).readonly(),
12882
12716
  { auth: "admin" }
12883
12717
  ),
12718
+ /**
12719
+ * Update one package on a node. `nodeId` omitted (or `'hub'`)
12720
+ * installs on the hub via npm; an agent `nodeId` makes the hub
12721
+ * pack the resolved version and push the tarball to that agent
12722
+ * (`$agent.deploy` + `$agent.reload`) — agents need no npm runtime.
12723
+ */
12884
12724
  updatePackage: method(
12885
- object({ name: string().min(1), version: string().optional() }),
12725
+ object({
12726
+ name: string().min(1),
12727
+ version: string().optional(),
12728
+ nodeId: string().optional()
12729
+ }),
12886
12730
  unknown(),
12887
12731
  { kind: "mutation", auth: "admin" }
12888
12732
  ),
@@ -12903,12 +12747,128 @@ const CustomActionInputSchema = object({
12903
12747
  object({ rolledBackTo: string().nullable() }),
12904
12748
  { kind: "mutation", auth: "admin" }
12905
12749
  ),
12906
- forceRefresh: method(_void(), unknown(), { kind: "mutation", auth: "admin" }),
12750
+ /** Re-check updates for a node, bypassing any cache. `nodeId`
12751
+ * omitted (or `'hub'`) refreshes the hub; an agent `nodeId`
12752
+ * re-checks that agent's roster. */
12753
+ forceRefresh: method(
12754
+ object({ nodeId: string().optional() }),
12755
+ unknown(),
12756
+ { kind: "mutation", auth: "admin" }
12757
+ ),
12907
12758
  restartServer: method(
12908
12759
  object({ confirm: literal(true) }),
12909
12760
  unknown(),
12910
12761
  { kind: "mutation", auth: "admin" }
12911
12762
  ),
12763
+ /**
12764
+ * Most-recent restart marker (kind / packageName / from→to versions
12765
+ * / requestedBy / requestedAt). Returns `null` when this process
12766
+ * didn't boot from a tracked restart, or when the
12767
+ * post-boot retention window (5 min) has elapsed.
12768
+ *
12769
+ * Drives the admin-UI reconnect overlay's success toast — the
12770
+ * `system.restart-completed` event itself is fired before the
12771
+ * client has time to re-subscribe, so the client queries this on
12772
+ * first reconnect instead.
12773
+ */
12774
+ getLastRestart: method(
12775
+ _void(),
12776
+ object({
12777
+ kind: _enum(["framework-update", "manual", "system"]),
12778
+ packageName: string().optional(),
12779
+ fromVersion: string().optional(),
12780
+ toVersion: string().optional(),
12781
+ requestedBy: string().optional(),
12782
+ requestedAt: number()
12783
+ }).nullable(),
12784
+ { auth: "admin" }
12785
+ ),
12786
+ /**
12787
+ * Snapshot of the framework packages installed under the hub's
12788
+ * `<appRoot>/node_modules/`. Each row carries the currently
12789
+ * installed version and (best-effort) the latest version
12790
+ * available on npm. Drives the admin-UI "System packages" panel.
12791
+ *
12792
+ * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
12793
+ */
12794
+ listFrameworkPackages: method(
12795
+ _void(),
12796
+ array(FrameworkPackageStatusSchema).readonly(),
12797
+ { auth: "admin" }
12798
+ ),
12799
+ /**
12800
+ * Cluster-wide capability-provider discovery. Returns the list of
12801
+ * `{ addonId, mode, isActive }` tuples for whatever addon(s)
12802
+ * currently provide the requested capability across the cluster.
12803
+ *
12804
+ * Why this lives on `addons` (and not on a `capabilities` cap of
12805
+ * its own): the hub's main-process `CapabilityRegistry` already
12806
+ * aggregates registrations from every forked group-runner and
12807
+ * remote agent via Moleculer event propagation — there's no
12808
+ * cross-process registry mirror to build, just an introspection
12809
+ * shim.
12810
+ *
12811
+ * Use this from addon code when you need to know whether another
12812
+ * addon has registered a specific cap (e.g. `tailscale-ingress`
12813
+ * checking `tailscale-client` is up before calling `tailscale
12814
+ * serve`). Don't reach for `ctx.capabilities.getCollectionEntries`
12815
+ * — that reads the LOCAL registry of the calling addon's group
12816
+ * runner and never sees providers in other processes. See
12817
+ * `CLAUDE.md` → Critical rules → ctx.api vs ctx.capabilities.
12818
+ */
12819
+ listCapabilityProviders: method(
12820
+ object({ capName: string().min(1) }),
12821
+ array(object({
12822
+ addonId: string(),
12823
+ mode: _enum(["singleton", "collection"]),
12824
+ isActive: boolean()
12825
+ })).readonly()
12826
+ ),
12827
+ /**
12828
+ * Toggle a single collection-cap provider on/off. Generic write-side
12829
+ * counterpart of `listCapabilityProviders` — drives the per-provider
12830
+ * Enable/Disable affordance in admin pages (TURN servers, etc.)
12831
+ * without needing a bespoke orchestrator cap.
12832
+ *
12833
+ * Reaches the hub's `CapabilityRegistry` directly:
12834
+ * `enableCollectionProvider` / `disableCollectionProvider` flip the
12835
+ * registry-level `disabledProviders` set. `getCollectionEntries`
12836
+ * already filters disabled providers out, so a disabled provider
12837
+ * drops out of every collection aggregate immediately. Only valid
12838
+ * for `mode: 'collection'` caps — the registry no-ops + warns for
12839
+ * singletons.
12840
+ */
12841
+ setCapabilityProviderEnabled: method(
12842
+ object({
12843
+ capName: string().min(1),
12844
+ addonId: string().min(1),
12845
+ enabled: boolean()
12846
+ }),
12847
+ object({ success: literal(true) }),
12848
+ { kind: "mutation", auth: "admin" }
12849
+ ),
12850
+ /**
12851
+ * Live-update one of the framework packages marked
12852
+ * `camstack.system: true` (`@camstack/types|kernel|core|sdk|ui-library`).
12853
+ * Runs `npm install --prefix <appRoot> <name>@<version> --no-save`,
12854
+ * writes a `.restart-pending` marker, emits `system.restarting`
12855
+ * and schedules a graceful process exit. The supervisor (Docker /
12856
+ * Electron / systemd) brings the hub back up; on first boot after
12857
+ * the restart the marker fires `system.restart-completed`.
12858
+ *
12859
+ * `version` defaults to `'latest'`. The allow-list of valid
12860
+ * `packageName` values is enforced server-side.
12861
+ *
12862
+ * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
12863
+ */
12864
+ updateFrameworkPackage: method(
12865
+ object({
12866
+ packageName: string().min(1),
12867
+ version: string().optional()
12868
+ }),
12869
+ UpdateFrameworkPackageResultSchema,
12870
+ { kind: "mutation", auth: "admin" }
12871
+ ),
12912
12872
  getVersions: method(
12913
12873
  object({ name: string() }),
12914
12874
  array(PackageVersionInfoSchema).readonly()
@@ -12979,6 +12939,7 @@ var EventCategory = /* @__PURE__ */ ((EventCategory2) => {
12979
12939
  EventCategory2["SystemBoot"] = "system.boot";
12980
12940
  EventCategory2["SystemAddonsReady"] = "system.addons-ready";
12981
12941
  EventCategory2["SystemRestarting"] = "system.restarting";
12942
+ EventCategory2["SystemRestartCompleted"] = "system.restart-completed";
12982
12943
  EventCategory2["SystemReadyState"] = "system.ready-state";
12983
12944
  EventCategory2["AddonStarted"] = "addon.started";
12984
12945
  EventCategory2["AddonStopped"] = "addon.stopped";