@camstack/addon-admin-ui 1.2.40 → 1.2.42
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/assets/{AddLocationWizard-_UpnD2Rb.js → AddLocationWizard-CQwIKMPj.js} +1 -1
- package/dist/assets/{AddonCollectionPage-DsqpERSx.js → AddonCollectionPage-D47hM2eT.js} +1 -1
- package/dist/assets/Addons-CnIGTOiE.js +1 -0
- package/dist/assets/{AdminTabs-C1b21Umm.js → AdminTabs-CZRSZTPU.js} +1 -1
- package/dist/assets/{AiProfiles-CxmKPOGN.js → AiProfiles-BZzDob0L.js} +1 -1
- package/dist/assets/{BrokerForm-BxVQ7-k2.js → BrokerForm-BAqaxvfH.js} +1 -1
- package/dist/assets/{Build-eERcQhc5.js → Build-ut1CQDDo.js} +4 -4
- package/dist/assets/{Capabilities-CkTJoG8P.js → Capabilities-NKE6_qP5.js} +1 -1
- package/dist/assets/{CapabilityBadges-Pi0oFNyA.js → CapabilityBadges-BC_kpGZQ.js} +1 -1
- package/dist/assets/Cluster-ej3HxB6z.js +1 -0
- package/dist/assets/{ClusterRecordingTab-g0pESRbc.js → ClusterRecordingTab-Rj_59RkE.js} +1 -1
- package/dist/assets/{Dashboard-BOdpB8Zi.js → Dashboard-C2O6I-_T.js} +1 -1
- package/dist/assets/{Data-D_lbeA81.js → Data-BTpXqPiI.js} +1 -1
- package/dist/assets/{DetectionIntelligence-Dsj8oMfR.js → DetectionIntelligence-CwuQGH6V.js} +1 -1
- package/dist/assets/DeviceAggregateSection-BUZ_WJht.js +1 -0
- package/dist/assets/DeviceDetail-DSCcC8RF.js +2 -0
- package/dist/assets/{Devices-Ctgv5UD9.js → Devices-DM0iJ9yF.js} +1 -1
- package/dist/assets/{EmbedPlayerPage-BH6rYrQY.js → EmbedPlayerPage-DWnlloAu.js} +1 -1
- package/dist/assets/Events-7ddWruIT.js +1 -0
- package/dist/assets/{FormBuilder-4y2vfIFX.js → FormBuilder-kyBE_B5G.js} +1 -1
- package/dist/assets/{Identity-lIu4PKp0.js → Identity-Dt3ylW9N.js} +1 -1
- package/dist/assets/IntegrationDetail-w86mAryt.js +1 -0
- package/dist/assets/Integrations-Cc34FTD5.js +1 -0
- package/dist/assets/{Integrations-CtXQURhT.js → Integrations-DJV-So6R.js} +1 -1
- package/dist/assets/{Logs-CjKk3Pid.js → Logs-BndkeK5m.js} +1 -1
- package/dist/assets/{MotionZonesSettings-Lydieqfe.js → MotionZonesSettings-BQJ4kVFF.js} +1 -1
- package/dist/assets/{MyAccess-W28I5kd-.js → MyAccess-CSmeU_6Z.js} +1 -1
- package/dist/assets/Network-Cu_VSmA2.js +1 -0
- package/dist/assets/{NodeAddonsSettingsPanel-BI-P31wN.js → NodeAddonsSettingsPanel-SSVryw6D.js} +1 -1
- package/dist/assets/{NodeInferenceDevices-BmhhZKpR.js → NodeInferenceDevices-DtEFMSa-.js} +1 -1
- package/dist/assets/{Notifications-BGKlEsy-.js → Notifications-DkHNN7u-.js} +1 -1
- package/dist/assets/Pipeline-C-9Jkm22.js +1 -0
- package/dist/assets/{Plates-CXXsVO_i.js → Plates-CpWDWN0r.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-Cm6_Ayev.js → PrivacyMaskSettings-CkdB1xCI.js} +1 -1
- package/dist/assets/{ProviderIcon-NBKDrcT9.js → ProviderIcon-bdbnq4N6.js} +1 -1
- package/dist/assets/Recordings-DBw-hB4-.js +1 -0
- package/dist/assets/{ScanNetworkModal-BEJE3rpa.js → ScanNetworkModal-_YV1Zq_l.js} +1 -1
- package/dist/assets/{Settings-2DUxyOI7.js → Settings-BiLVD3It.js} +1 -1
- package/dist/assets/{Showroom-DsDBDxPb.js → Showroom-4VD16iez.js} +1 -1
- package/dist/assets/{Terminal-DsDxn595.js → Terminal-BnUlIw8O.js} +2 -2
- package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DuoUM9go.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-BmDbz_rz.js} +1 -1
- package/dist/assets/{boxes-byknuFCy.js → boxes-e-GYxfpL.js} +1 -1
- package/dist/assets/{build-cluster-graph-gWLixa2m.js → build-cluster-graph-DxvZ0xhr.js} +2 -2
- package/dist/assets/circle-check-big-B-yIR8q2.js +1 -0
- package/dist/assets/circle-x-D03G0GdO.js +1 -0
- package/dist/assets/clock-BXGwoKYC.js +1 -0
- package/dist/assets/{copy-DtilJJvc.js → copy-CqwrIa4N.js} +1 -1
- package/dist/assets/{debounced-invalidate-DHj6wyxg.js → debounced-invalidate-DBoL8JpN.js} +1 -1
- package/dist/assets/{dist-CL9jy06y.js → dist-BJ0Zt6VD.js} +1 -1
- package/dist/assets/dist-DXPrpRpS.js +345 -0
- package/dist/assets/{download-Dj8UGwXy.js → download-CmuC-FeE.js} +1 -1
- package/dist/assets/{face-bbox-BFrLPrmw.js → face-bbox-CJXXrwZD.js} +1 -1
- package/dist/assets/{hostInit-dir45U0P.js → hostInit-Bl7Xplbe.js} +1 -1
- package/dist/assets/index-DKn-frpn.js +4 -0
- package/dist/assets/{key-round-DmdC7g1C.js → key-round-CM8vUNHb.js} +1 -1
- package/dist/assets/{mf-entry-bootstrap-0-7417bcdb.js → mf-entry-bootstrap-0-87bf9ee5.js} +2 -2
- package/dist/assets/{pencil-CM0OcKoQ.js → pencil-c6tQI1wA.js} +1 -1
- package/dist/assets/play-Q0Jaad1z.js +1 -0
- package/dist/assets/{player-overlays-CM9K8NbB.js → player-overlays-CLg7IUhf.js} +1 -1
- package/dist/assets/plus-XQw0_Xxm.js +1 -0
- package/dist/assets/{radio-C1-CuToc.js → radio-aWUgXHcO.js} +1 -1
- package/dist/assets/{refresh-cw-BCQ0uqSo.js → refresh-cw-tMjElbNM.js} +1 -1
- package/dist/assets/remoteEntry-BKnWOAq9.js +1 -0
- package/dist/assets/rotate-ccw-BrUDF1Jl.js +1 -0
- package/dist/assets/rotate-cw-BHhlWumi.js +1 -0
- package/dist/assets/search-vkCcifvz.js +1 -0
- package/dist/assets/{send-85b3c2pF.js → send-CMFUzdc4.js} +1 -1
- package/dist/assets/{server-CqHxFx4j.js → server-B8x_DAB-.js} +1 -1
- package/dist/assets/square-BKiKY80C.js +1 -0
- package/dist/assets/{square-check-big-BFcA-8wr.js → square-check-big-kCHymcdg.js} +1 -1
- package/dist/assets/src-c6oHWYCY.js +345 -0
- package/dist/assets/src-vvsO4SIS.js +67 -0
- package/dist/assets/{undo-2-DDQAKjyU.js → undo-2-CHnYllAb.js} +1 -1
- package/dist/assets/{upload-D0e5f6HZ.js → upload-I6q-wv2N.js} +1 -1
- package/dist/assets/{useEventInvalidation-BnSiq41g.js → useEventInvalidation-JO5cEBnp.js} +1 -1
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-1o9kxso_.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-C7terUe9.js} +2 -2
- package/dist/assets/{wifi-CkMq-H2F.js → wifi-DMRZBskQ.js} +1 -1
- package/dist/assets/{zap-C0CZz-JF.js → zap-BFD9xYRb.js} +1 -1
- package/dist/index.html +5 -5
- package/dist/server/addon.js +369 -6
- package/package.json +1 -1
- package/dist/assets/Addons-Cbzvs2ne.js +0 -1
- package/dist/assets/Cluster-D7MeMUz9.js +0 -1
- package/dist/assets/DeviceAggregateSection-UayHxH_7.js +0 -1
- package/dist/assets/DeviceDetail-2vx05ko3.js +0 -2
- package/dist/assets/Events-GU0DqH5U.js +0 -1
- package/dist/assets/IntegrationDetail-C0Zcxbgi.js +0 -1
- package/dist/assets/Integrations-BP2jk3NT.js +0 -1
- package/dist/assets/Network-Cnq4bp10.js +0 -1
- package/dist/assets/Pipeline-6vY94dNS.js +0 -1
- package/dist/assets/Recordings-fIC-_Plr.js +0 -1
- package/dist/assets/circle-check-big-D8Wjv0ct.js +0 -1
- package/dist/assets/clock-ISV-5mJ1.js +0 -1
- package/dist/assets/dist-DwLd0mpq.js +0 -345
- package/dist/assets/index-BMoYnIQI.js +0 -4
- package/dist/assets/play-CBwgjWMW.js +0 -1
- package/dist/assets/plus-CByuXPqh.js +0 -1
- package/dist/assets/remoteEntry-BtIghGso.js +0 -1
- package/dist/assets/rotate-ccw-CiIETsd8.js +0 -1
- package/dist/assets/rotate-cw-Bo4UhVCj.js +0 -1
- package/dist/assets/search-m5N8S4EI.js +0 -1
- package/dist/assets/square-CwYcDkPi.js +0 -1
- package/dist/assets/src-BaYQ7lHC.js +0 -345
- package/dist/assets/src-s1tm1Ako.js +0 -67
package/dist/server/addon.js
CHANGED
|
@@ -7094,6 +7094,104 @@ object({
|
|
|
7094
7094
|
})
|
|
7095
7095
|
});
|
|
7096
7096
|
/**
|
|
7097
|
+
* Adoption job — the background form of `device-adoption.adopt`.
|
|
7098
|
+
*
|
|
7099
|
+
* ## Why this exists
|
|
7100
|
+
*
|
|
7101
|
+
* `adopt({childNativeIds: [...]})` materialises one CamStack device per
|
|
7102
|
+
* candidate PLUS every accessory child, and the whole array shares ONE UDS
|
|
7103
|
+
* request deadline (60s). Measured on the live hub against Home Assistant:
|
|
7104
|
+
* each device the kernel creates costs ~450 ms — `devices.create` pre-seeds
|
|
7105
|
+
* meta with up to eleven SEQUENTIAL round trips (`setName`, `setType`,
|
|
7106
|
+
* `setRole`, … `persistConfig`) before the class is constructed — and an
|
|
7107
|
+
* accessory child costs the same as its parent. So the real unit of work is
|
|
7108
|
+
* the CHILD, not the candidate:
|
|
7109
|
+
*
|
|
7110
|
+
* - 25 candidates averaging 6 children → ~150 devices → **>60s, times out**
|
|
7111
|
+
* - ONE candidate with 217 children → ~217 devices → **>60s, times out**
|
|
7112
|
+
*
|
|
7113
|
+
* That second line is why this is a job and not a smaller batch. No chunking,
|
|
7114
|
+
* no bounded concurrency over candidates and no per-call tuning can fix a
|
|
7115
|
+
* shape where **N=1 already exceeds the deadline** — the count that blows the
|
|
7116
|
+
* budget is the source system's accessory fan-out, which the operator does not
|
|
7117
|
+
* choose and cannot see. A design that only works below some N is the same bug
|
|
7118
|
+
* deferred.
|
|
7119
|
+
*
|
|
7120
|
+
* ## What the timeout did NOT do
|
|
7121
|
+
*
|
|
7122
|
+
* It did not stop the work. The UDS deadline ends the CALLER's wait; the
|
|
7123
|
+
* provider's loop runs to completion. Measured: a 25-candidate adopt that
|
|
7124
|
+
* "failed" at 60s had adopted 17 by 87s and all 25 by ~130s. The operator saw
|
|
7125
|
+
* an error and had no way to learn that. Every field below exists so that
|
|
7126
|
+
* question has an answer.
|
|
7127
|
+
*
|
|
7128
|
+
* ## Idempotency
|
|
7129
|
+
*
|
|
7130
|
+
* Jobs are in-RAM; a restart forgets them. That is safe here because adoption
|
|
7131
|
+
* is keyed by a stable id (`ha:<broker>:dev:<nativeId>` and equivalents), so
|
|
7132
|
+
* re-running a job re-adopts nothing: an already-adopted candidate is SKIPPED
|
|
7133
|
+
* by the engine before any provider call and lands in `alreadyAdopted`. It is
|
|
7134
|
+
* never a duplicate device, and never an error the operator has to interpret.
|
|
7135
|
+
*/
|
|
7136
|
+
var AdoptionJobStateSchema = _enum([
|
|
7137
|
+
"running",
|
|
7138
|
+
"done",
|
|
7139
|
+
"failed",
|
|
7140
|
+
"cancelled"
|
|
7141
|
+
]);
|
|
7142
|
+
/**
|
|
7143
|
+
* Per-candidate result. Every candidate the job was asked to adopt ends in
|
|
7144
|
+
* exactly one of these buckets — there is no silent drop, and the operator can
|
|
7145
|
+
* always answer "which of my 25 landed?".
|
|
7146
|
+
*
|
|
7147
|
+
* - `adopted` — created now by this job.
|
|
7148
|
+
* - `already-adopted` — a device for this candidate existed before the job
|
|
7149
|
+
* reached it (a re-run, or a retry after a timeout). Not an error.
|
|
7150
|
+
* - `failed` — the provider threw; `error` carries the message.
|
|
7151
|
+
* - `cancelled` — the operator cancelled before this candidate was reached.
|
|
7152
|
+
*/
|
|
7153
|
+
var AdoptionOutcomeSchema = _enum([
|
|
7154
|
+
"adopted",
|
|
7155
|
+
"already-adopted",
|
|
7156
|
+
"failed",
|
|
7157
|
+
"cancelled"
|
|
7158
|
+
]);
|
|
7159
|
+
var AdoptionCandidateResultSchema = object({
|
|
7160
|
+
childNativeId: string(),
|
|
7161
|
+
outcome: AdoptionOutcomeSchema,
|
|
7162
|
+
/** The materialised parent device id — null for `failed` / `cancelled`. */
|
|
7163
|
+
parentDeviceId: number().int().nonnegative().nullable(),
|
|
7164
|
+
/** Accessory children created for this candidate. */
|
|
7165
|
+
accessoryCount: number().int().nonnegative(),
|
|
7166
|
+
/** Failure message; null unless `outcome === 'failed'`. */
|
|
7167
|
+
error: string().nullable()
|
|
7168
|
+
});
|
|
7169
|
+
var AdoptionJobSchema = object({
|
|
7170
|
+
jobId: string(),
|
|
7171
|
+
/** The integration provider this job adopts through (the `addonId` pin). */
|
|
7172
|
+
addonId: string(),
|
|
7173
|
+
integrationId: string(),
|
|
7174
|
+
state: AdoptionJobStateSchema,
|
|
7175
|
+
/** Candidates the job was asked to adopt. Known up front, so never null. */
|
|
7176
|
+
total: number().int().nonnegative(),
|
|
7177
|
+
/** Candidates that have reached a terminal bucket. */
|
|
7178
|
+
processed: number().int().nonnegative(),
|
|
7179
|
+
adopted: number().int().nonnegative(),
|
|
7180
|
+
alreadyAdopted: number().int().nonnegative(),
|
|
7181
|
+
failed: number().int().nonnegative(),
|
|
7182
|
+
/** Accessory child devices created across every candidate — the real unit
|
|
7183
|
+
* of work, surfaced so a slow job is legible rather than mysterious. */
|
|
7184
|
+
accessoriesCreated: number().int().nonnegative(),
|
|
7185
|
+
/** The candidate currently being adopted; null when idle or finished. */
|
|
7186
|
+
currentChildNativeId: string().nullable(),
|
|
7187
|
+
/** One entry per candidate, in the order they were processed. */
|
|
7188
|
+
results: array(AdoptionCandidateResultSchema).readonly(),
|
|
7189
|
+
startedAt: number(),
|
|
7190
|
+
finishedAt: number().nullable(),
|
|
7191
|
+
/** Set only when the job itself broke (not a per-candidate failure). */
|
|
7192
|
+
error: string().nullable()
|
|
7193
|
+
});
|
|
7194
|
+
/**
|
|
7097
7195
|
* Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
|
|
7098
7196
|
* pipeline functions an operator thinks in terms of.
|
|
7099
7197
|
*
|
|
@@ -11760,6 +11858,15 @@ method(object({
|
|
|
11760
11858
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
11761
11859
|
kind: "mutation",
|
|
11762
11860
|
auth: "admin"
|
|
11861
|
+
}), method(AdoptInputSchema.extend({ addonId: string() }), object({ jobId: string() }), {
|
|
11862
|
+
kind: "mutation",
|
|
11863
|
+
auth: "admin"
|
|
11864
|
+
}), method(object({
|
|
11865
|
+
addonId: string(),
|
|
11866
|
+
integrationId: string().optional()
|
|
11867
|
+
}), array(AdoptionJobSchema).readonly(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
11868
|
+
kind: "mutation",
|
|
11869
|
+
auth: "admin"
|
|
11763
11870
|
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
11764
11871
|
kind: "mutation",
|
|
11765
11872
|
auth: "admin"
|
|
@@ -14639,6 +14746,76 @@ object({
|
|
|
14639
14746
|
* Each provider returns a static descriptor; the core enumerates them
|
|
14640
14747
|
* to validate the `integration=` query param and resolve the consent
|
|
14641
14748
|
* label + the scopes baked into the issued token.
|
|
14749
|
+
*
|
|
14750
|
+
* ## Declaring one
|
|
14751
|
+
*
|
|
14752
|
+
* An OAuth client is integration-specific knowledge — who the client is, what
|
|
14753
|
+
* it may ask for, where it may be sent — so it is declared by the ADDON that
|
|
14754
|
+
* owns the integration, never by the kernel and never as a branch inside
|
|
14755
|
+
* `oauth2-routes.ts` ([D101](../../../../docs/decisions/adr-0101.md)). Three
|
|
14756
|
+
* steps, no others:
|
|
14757
|
+
*
|
|
14758
|
+
* 1. Add `{ "name": "oauth-integration" }` to the addon's `camstack.addons[]`
|
|
14759
|
+
* manifest entry. This is also what tells the hub, at addon-LOAD time, that
|
|
14760
|
+
* a descriptor is owed — see "the boot window" below.
|
|
14761
|
+
* 2. Return a provider from `onInitialize()`:
|
|
14762
|
+
*
|
|
14763
|
+
* ```ts
|
|
14764
|
+
* const provider: IOauthIntegrationProvider = {
|
|
14765
|
+
* getDescriptor: async () => ({
|
|
14766
|
+
* integrationId: 'my-thing', // the `integration=` query param
|
|
14767
|
+
* displayName: 'My Thing',
|
|
14768
|
+
* requestedScopes: [ … ], // see below
|
|
14769
|
+
* allowedRedirectPrefixes: ['https://callback.example/'],
|
|
14770
|
+
* }),
|
|
14771
|
+
* }
|
|
14772
|
+
* return [{ capability: oauthIntegrationCapability, provider }]
|
|
14773
|
+
* ```
|
|
14774
|
+
*
|
|
14775
|
+
* The descriptor must be **static** — it is read on the authorize path, so
|
|
14776
|
+
* never put an await on network or disk behind it, and never register it
|
|
14777
|
+
* behind one either (a provider is registered only once `onInitialize`
|
|
14778
|
+
* RETURNS, so anything awaited before the return delays linking).
|
|
14779
|
+
* 3. Nothing else. There is no allow-list to join, no id to register with the
|
|
14780
|
+
* core, and no per-integration branch anywhere: `/api/oauth2/authorize` and
|
|
14781
|
+
* `/api/oauth2/integrations` are built from this collection alone.
|
|
14782
|
+
*
|
|
14783
|
+
* **Scopes. `requestedScopes` has exactly ONE meaning: what the integration
|
|
14784
|
+
* NEEDS to function.** Not a blast radius, not a conservative
|
|
14785
|
+
* under-declaration, not a description of some other path the addon happens to
|
|
14786
|
+
* have. Derive it from what the client actually calls **with this token** —
|
|
14787
|
+
* every tRPC path against `METHOD_ACCESS_MAP`, plus an `addon:` grant for every
|
|
14788
|
+
* addon HTTP route it posts to — and write the call that justifies each entry
|
|
14789
|
+
* next to it. Two integrations once used this field to mean two different
|
|
14790
|
+
* things; the operator ruled there is one meaning, and any third integration
|
|
14791
|
+
* inherits it (2026-08-09).
|
|
14792
|
+
*
|
|
14793
|
+
* This is not documentation, it is the ENFORCEMENT INPUT. Since
|
|
14794
|
+
* [D103](../../../../docs/decisions/adr-0103.md) the `/addon/:addonId/*` gate
|
|
14795
|
+
* checks an integration token's grant before letting it reach an
|
|
14796
|
+
* `access: 'authenticated'` route, so an **under-declaration is an integration
|
|
14797
|
+
* that stops working** — a missing `addon:` entry means `403 Token scope
|
|
14798
|
+
* mismatch` on every control the client tries to actuate. Widen the descriptor
|
|
14799
|
+
* honestly rather than weakening a check to make a route pass.
|
|
14800
|
+
*
|
|
14801
|
+
* Prefer a narrow `capability:` scope to a `category:` one unless the client
|
|
14802
|
+
* genuinely needs a whole family; a category scope grants every future member
|
|
14803
|
+
* of that category too. `category:system [create]` has been rejected once and
|
|
14804
|
+
* should stay rejected: it hands `addons.installPackage` to an integration.
|
|
14805
|
+
*
|
|
14806
|
+
* Calls the ADDON itself makes over `ctx.api` run as the addon and are not
|
|
14807
|
+
* scope-checked, so they are not what this field describes — but reaching the
|
|
14808
|
+
* addon's route in the first place IS, and that is the entry to declare.
|
|
14809
|
+
*
|
|
14810
|
+
* **The boot window.** An addon registers its provider after its runner forks
|
|
14811
|
+
* and initialises, so between hub start and that moment this collection is
|
|
14812
|
+
* incomplete and an `integrationId` can be legitimately absent. The core does
|
|
14813
|
+
* not wait, poll or cache around this ([D3](../../../../docs/decisions/adr-0003.md)):
|
|
14814
|
+
* it compares the manifest declarers against the registered providers and
|
|
14815
|
+
* answers `503 temporarily_unavailable` (with `Retry-After` and the pending
|
|
14816
|
+
* addon ids) instead of `400 unknown integration`, and reports
|
|
14817
|
+
* `complete: false` on `GET /api/oauth2/integrations`. A client should retry
|
|
14818
|
+
* while the list is incomplete rather than conclude the hub cannot do OAuth.
|
|
14642
14819
|
*/
|
|
14643
14820
|
var OauthIntegrationDescriptorSchema = object({
|
|
14644
14821
|
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
@@ -14669,7 +14846,30 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
14669
14846
|
* present, /api/oauth2/authorize bakes THIS into the code instead of the
|
|
14670
14847
|
* hub-global `publicHubUrl()`, so a forked exporter addon (which can't set
|
|
14671
14848
|
* the hub's env) drives the claim that its cloud Lambda routes back on. */
|
|
14672
|
-
hubUrl: string().optional()
|
|
14849
|
+
hubUrl: string().optional(),
|
|
14850
|
+
/**
|
|
14851
|
+
* How long a REFRESH token issued for this integration lives — seconds, or
|
|
14852
|
+
* `'never'` for a token minted with no `exp` claim at all. Omit to keep the
|
|
14853
|
+
* 30-day default, which is what every link used before this field existed.
|
|
14854
|
+
*
|
|
14855
|
+
* Declared here for the same reason `requestedScopes` is: the integration
|
|
14856
|
+
* knows what it needs. Amazon's account linking and a Home Assistant config
|
|
14857
|
+
* entry are both meant to survive indefinitely, and re-linking is a manual
|
|
14858
|
+
* user action, so a 30-day expiry silently unlinks a working integration.
|
|
14859
|
+
*
|
|
14860
|
+
* **The security posture, stated so it is owned deliberately.** A refresh
|
|
14861
|
+
* token that never expires is permanent access if it leaks. What bounds it is
|
|
14862
|
+
* revocation, not time: `oauthRefresh` re-reads the session on every use and
|
|
14863
|
+
* returns `null` once `revokedAt` is set, as does `oauthVerifyAccessToken`.
|
|
14864
|
+
* The one gap is the ACCESS token — it is a plain signed JWT that nothing
|
|
14865
|
+
* re-checks against the session on the `/trpc` and `/addon/*` paths, so
|
|
14866
|
+
* revoking a link takes effect there only after its remaining hour. That hour
|
|
14867
|
+
* is why the access TTL is not configurable.
|
|
14868
|
+
*
|
|
14869
|
+
* The value is baked into the authorization code at `/authorize` and travels
|
|
14870
|
+
* on the tokens, so editing this field changes FUTURE links only.
|
|
14871
|
+
*/
|
|
14872
|
+
refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
14673
14873
|
});
|
|
14674
14874
|
method(_void(), OauthIntegrationDescriptorSchema);
|
|
14675
14875
|
/**
|
|
@@ -17697,6 +17897,37 @@ DeviceType.Camera, method(object({
|
|
|
17697
17897
|
lastCapturedAt: number().nullable(),
|
|
17698
17898
|
cacheAgeMs: number().nullable(),
|
|
17699
17899
|
etag: string().nullable()
|
|
17900
|
+
}))), systemMethod(object({
|
|
17901
|
+
/** The tiles a surface is actually rendering. One entry per (device,
|
|
17902
|
+
* width) the caller will paint — the width is snapped to the server's
|
|
17903
|
+
* ladder and becomes part of the link's SIGNED identity. */
|
|
17904
|
+
targets: array(object({
|
|
17905
|
+
deviceId: number(),
|
|
17906
|
+
/** Target width in px. Omit for the frame as captured — correct
|
|
17907
|
+
* for a full-bleed surface, wrong (and expensive) for a grid. */
|
|
17908
|
+
width: number().int().positive().optional()
|
|
17909
|
+
})).min(1).max(200) }), array(object({
|
|
17910
|
+
deviceId: number(),
|
|
17911
|
+
/** Root-relative signed path, or null when the link plane is not
|
|
17912
|
+
* served (no data-plane facility). Present even for a device that has
|
|
17913
|
+
* never captured — the request is what triggers the first one (D94). */
|
|
17914
|
+
url: string().nullable(),
|
|
17915
|
+
/** Epoch ms of the frame this link serves. Null = never captured.
|
|
17916
|
+
* THE honest age: the tRPC path carried none before this. */
|
|
17917
|
+
capturedAt: number().nullable(),
|
|
17918
|
+
/** Age of that frame at the moment the answer was built. */
|
|
17919
|
+
ageMs: number().nullable(),
|
|
17920
|
+
/** Epoch ms after which `url` stops verifying. */
|
|
17921
|
+
expiresAt: number().nullable(),
|
|
17922
|
+
/** Ladder rung the bytes are at; null = the frame as captured. */
|
|
17923
|
+
width: number().nullable(),
|
|
17924
|
+
/** The device has never produced a frame. An empty state, not a
|
|
17925
|
+
* failure — and never a reason to withhold the link (D94). */
|
|
17926
|
+
neverCaptured: boolean(),
|
|
17927
|
+
/** A sleeping battery camera: the frame is deliberately stale and will
|
|
17928
|
+
* NOT refresh in the background. A surface should say so rather than
|
|
17929
|
+
* present it as current. */
|
|
17930
|
+
sleeping: boolean()
|
|
17700
17931
|
})));
|
|
17701
17932
|
/**
|
|
17702
17933
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
@@ -17742,11 +17973,29 @@ var SsoBridgeClaimsSchema = object({
|
|
|
17742
17973
|
codeChallenge: string().optional(),
|
|
17743
17974
|
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
17744
17975
|
* tokens so the verify path can check the session is not revoked. */
|
|
17745
|
-
sessionId: string().optional()
|
|
17976
|
+
sessionId: string().optional(),
|
|
17977
|
+
/**
|
|
17978
|
+
* The refresh lifetime this LINK was created with, in seconds, or `'never'`.
|
|
17979
|
+
* Baked into the code at `/authorize` from the integration's descriptor and
|
|
17980
|
+
* carried forward so `oauthRefresh` re-mints with the same lifetime. It rides
|
|
17981
|
+
* on the token rather than being re-read from the descriptor on purpose:
|
|
17982
|
+
* editing a descriptor must not retroactively extend or shorten a link the
|
|
17983
|
+
* operator already consented to.
|
|
17984
|
+
*/
|
|
17985
|
+
refreshTtl: union([number().int().positive(), literal("never")]).optional()
|
|
17746
17986
|
});
|
|
17747
17987
|
method(object({
|
|
17748
17988
|
claims: SsoBridgeClaimsSchema,
|
|
17749
|
-
|
|
17989
|
+
/**
|
|
17990
|
+
* Seconds, or `'never'` for a token minted with NO `exp` claim.
|
|
17991
|
+
*
|
|
17992
|
+
* `'never'` is a literal rather than `undefined`/`0` because omitting
|
|
17993
|
+
* this field already means "the 5-minute SSO hand-off default", and
|
|
17994
|
+
* `jwt.sign` THROWS on `{ expiresIn: undefined }` — a "no expiry" that
|
|
17995
|
+
* went through the numeric path would fail at mint time and break
|
|
17996
|
+
* linking rather than produce an eternal token.
|
|
17997
|
+
*/
|
|
17998
|
+
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
17750
17999
|
}), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
|
|
17751
18000
|
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
17752
18001
|
providerId: string().min(1),
|
|
@@ -19447,6 +19696,37 @@ DeviceType.Light, method(object({
|
|
|
19447
19696
|
mireds: number().int().optional(),
|
|
19448
19697
|
lastChangedAt: number()
|
|
19449
19698
|
});
|
|
19699
|
+
var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
|
|
19700
|
+
object({
|
|
19701
|
+
outcome: literal("validated"),
|
|
19702
|
+
/** Round-trip of the sign-in, when the provider measured it. */
|
|
19703
|
+
latencyMs: number().nonnegative().optional(),
|
|
19704
|
+
/** Optional human detail worth showing next to the tick
|
|
19705
|
+
* ("3 devices visible on this account"). */
|
|
19706
|
+
detail: string().optional()
|
|
19707
|
+
}).strict(),
|
|
19708
|
+
object({
|
|
19709
|
+
outcome: literal("rejected"),
|
|
19710
|
+
error: string()
|
|
19711
|
+
}).strict(),
|
|
19712
|
+
object({
|
|
19713
|
+
outcome: literal("inconclusive"),
|
|
19714
|
+
error: string()
|
|
19715
|
+
}).strict()
|
|
19716
|
+
]);
|
|
19717
|
+
var ConnectionTestInputSchema = object({
|
|
19718
|
+
/** Candidate integration settings, exactly as the create form collected them. */
|
|
19719
|
+
settings: record(string(), unknown()) });
|
|
19720
|
+
/**
|
|
19721
|
+
* What the provider's test actually DOES, so the UI can say it in words before
|
|
19722
|
+
* the operator presses the button ("Signs in to the Dreo cloud"). Purely
|
|
19723
|
+
* descriptive — it never changes routing.
|
|
19724
|
+
*/
|
|
19725
|
+
var ConnectionTestDescriptorSchema = object({ label: string() });
|
|
19726
|
+
method(ConnectionTestInputSchema, ConnectionTestOutcomeSchema, {
|
|
19727
|
+
kind: "mutation",
|
|
19728
|
+
auth: "admin"
|
|
19729
|
+
}), method(_void(), ConnectionTestDescriptorSchema, { auth: "admin" });
|
|
19450
19730
|
object({
|
|
19451
19731
|
/** True when the upstream system considers the entity connected. */
|
|
19452
19732
|
connected: boolean(),
|
|
@@ -20348,15 +20628,57 @@ var AvailableIntegrationTypeSchema = object({
|
|
|
20348
20628
|
* flow can import (e.g. HA areas). Drives the adopt modal's "import
|
|
20349
20629
|
* locations" checkbox. Provider-declared in the addon manifest. */
|
|
20350
20630
|
supportsLocationImport: boolean(),
|
|
20631
|
+
/**
|
|
20632
|
+
* True when this integration DECLARES a pre-creation test (the
|
|
20633
|
+
* `connection-test` cap, or a broker whose settings it stores). Drives the
|
|
20634
|
+
* Test button: an integration that cannot be tested must say so up front
|
|
20635
|
+
* rather than offering a button that always answers the same nonsense.
|
|
20636
|
+
*/
|
|
20637
|
+
canTest: boolean(),
|
|
20351
20638
|
existingInstances: array(object({
|
|
20352
20639
|
id: string(),
|
|
20353
20640
|
name: string()
|
|
20354
20641
|
})),
|
|
20355
20642
|
canAdd: boolean()
|
|
20356
20643
|
});
|
|
20644
|
+
/**
|
|
20645
|
+
* Why a test could not be answered as a plain boolean.
|
|
20646
|
+
*
|
|
20647
|
+
* `success` alone collapsed four different situations into one red box, and the
|
|
20648
|
+
* one that mattered most — "nobody ever asked the remote anything" — looked
|
|
20649
|
+
* exactly like "the remote said no". The status is the discriminator:
|
|
20650
|
+
*
|
|
20651
|
+
* - `validated` — a provider-declared test ran and the remote ACCEPTED.
|
|
20652
|
+
* - `rejected` — a provider-declared test ran and the remote REFUSED.
|
|
20653
|
+
* The only status that blocks `integrations.create`.
|
|
20654
|
+
* - `inconclusive` — a test IS declared but could not complete (timeout,
|
|
20655
|
+
* DNS, 5xx). Nothing was observed; not a failure.
|
|
20656
|
+
* - `unsupported` — this integration declares NO test. Nothing was
|
|
20657
|
+
* observed either; not a failure, and not a pass.
|
|
20658
|
+
*
|
|
20659
|
+
* `unsupported` and `inconclusive` both carry `success: false` so an older
|
|
20660
|
+
* client can never read them as a green tick, and both carry an `error` string
|
|
20661
|
+
* that SAYS the test did not run rather than inventing a failure.
|
|
20662
|
+
*/
|
|
20663
|
+
var TestConnectionStatusEnum = _enum([
|
|
20664
|
+
"validated",
|
|
20665
|
+
"rejected",
|
|
20666
|
+
"inconclusive",
|
|
20667
|
+
"unsupported"
|
|
20668
|
+
]);
|
|
20357
20669
|
var TestConnectionResultSchema$1 = object({
|
|
20670
|
+
/** True ONLY for `validated`. Never true for a test that did not run. */
|
|
20358
20671
|
success: boolean(),
|
|
20359
|
-
error: string().optional()
|
|
20672
|
+
error: string().optional(),
|
|
20673
|
+
/** Optional for wire back-compat with clients built before the tri-state;
|
|
20674
|
+
* the server always sets it. */
|
|
20675
|
+
status: TestConnectionStatusEnum.optional(),
|
|
20676
|
+
/** Addon id whose declared test answered — `null` when none did. Lets the UI
|
|
20677
|
+
* attribute a result instead of blaming "the integration". */
|
|
20678
|
+
testedBy: string().nullable().optional(),
|
|
20679
|
+
latencyMs: number().nonnegative().optional(),
|
|
20680
|
+
/** Human detail from a `validated` result ("3 devices on this account"). */
|
|
20681
|
+
detail: string().optional()
|
|
20360
20682
|
});
|
|
20361
20683
|
var CreateIntegrationInputSchema = object({
|
|
20362
20684
|
addonId: string(),
|
|
@@ -23409,7 +23731,12 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
23409
23731
|
hubUrl: string(),
|
|
23410
23732
|
/** PKCE (RFC 7636) S256 challenge. Baked into the signed code; a code
|
|
23411
23733
|
* that carries one can ONLY be exchanged with the matching verifier. */
|
|
23412
|
-
codeChallenge: string().optional()
|
|
23734
|
+
codeChallenge: string().optional(),
|
|
23735
|
+
/** The integration's declared refresh lifetime — seconds, or `'never'`.
|
|
23736
|
+
* From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
|
|
23737
|
+
* code so the link carries its own lifetime; omit for the 30-day
|
|
23738
|
+
* default. */
|
|
23739
|
+
refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
23413
23740
|
}), object({ code: string() }), {
|
|
23414
23741
|
kind: "mutation",
|
|
23415
23742
|
access: "create"
|
|
@@ -24602,6 +24929,18 @@ Object.freeze({
|
|
|
24602
24929
|
addonId: null,
|
|
24603
24930
|
access: "create"
|
|
24604
24931
|
},
|
|
24932
|
+
"connectionTest.describeTest": {
|
|
24933
|
+
capName: "connection-test",
|
|
24934
|
+
capScope: "system",
|
|
24935
|
+
addonId: null,
|
|
24936
|
+
access: "view"
|
|
24937
|
+
},
|
|
24938
|
+
"connectionTest.testSettings": {
|
|
24939
|
+
capName: "connection-test",
|
|
24940
|
+
capScope: "system",
|
|
24941
|
+
addonId: null,
|
|
24942
|
+
access: "create"
|
|
24943
|
+
},
|
|
24605
24944
|
"consumables.reset": {
|
|
24606
24945
|
capName: "consumables",
|
|
24607
24946
|
capScope: "device",
|
|
@@ -24992,6 +25331,12 @@ Object.freeze({
|
|
|
24992
25331
|
addonId: null,
|
|
24993
25332
|
access: "create"
|
|
24994
25333
|
},
|
|
25334
|
+
"deviceManager.adoptionCancelJob": {
|
|
25335
|
+
capName: "device-manager",
|
|
25336
|
+
capScope: "system",
|
|
25337
|
+
addonId: null,
|
|
25338
|
+
access: "create"
|
|
25339
|
+
},
|
|
24995
25340
|
"deviceManager.adoptionListCandidateFilters": {
|
|
24996
25341
|
capName: "device-manager",
|
|
24997
25342
|
capScope: "system",
|
|
@@ -25004,6 +25349,12 @@ Object.freeze({
|
|
|
25004
25349
|
addonId: null,
|
|
25005
25350
|
access: "view"
|
|
25006
25351
|
},
|
|
25352
|
+
"deviceManager.adoptionListJobs": {
|
|
25353
|
+
capName: "device-manager",
|
|
25354
|
+
capScope: "system",
|
|
25355
|
+
addonId: null,
|
|
25356
|
+
access: "view"
|
|
25357
|
+
},
|
|
25007
25358
|
"deviceManager.adoptionRefresh": {
|
|
25008
25359
|
capName: "device-manager",
|
|
25009
25360
|
capScope: "system",
|
|
@@ -25022,6 +25373,12 @@ Object.freeze({
|
|
|
25022
25373
|
addonId: null,
|
|
25023
25374
|
access: "create"
|
|
25024
25375
|
},
|
|
25376
|
+
"deviceManager.adoptionStartJob": {
|
|
25377
|
+
capName: "device-manager",
|
|
25378
|
+
capScope: "system",
|
|
25379
|
+
addonId: null,
|
|
25380
|
+
access: "create"
|
|
25381
|
+
},
|
|
25025
25382
|
"deviceManager.allocateDeviceId": {
|
|
25026
25383
|
capName: "device-manager",
|
|
25027
25384
|
capScope: "system",
|
|
@@ -28160,6 +28517,12 @@ Object.freeze({
|
|
|
28160
28517
|
addonId: null,
|
|
28161
28518
|
access: "view"
|
|
28162
28519
|
},
|
|
28520
|
+
"snapshot.getSnapshotLinks": {
|
|
28521
|
+
capName: "snapshot",
|
|
28522
|
+
capScope: "device",
|
|
28523
|
+
addonId: null,
|
|
28524
|
+
access: "view"
|
|
28525
|
+
},
|
|
28163
28526
|
"snapshot.getSnapshotOverview": {
|
|
28164
28527
|
capName: "snapshot",
|
|
28165
28528
|
capScope: "device",
|
|
@@ -29453,7 +29816,7 @@ var AdminUIAddon = class extends BaseAddon {
|
|
|
29453
29816
|
capability: adminUiCapability,
|
|
29454
29817
|
provider: {
|
|
29455
29818
|
getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
|
|
29456
|
-
getVersion: async () => ({ version: "1.2.
|
|
29819
|
+
getVersion: async () => ({ version: "1.2.42" })
|
|
29457
29820
|
}
|
|
29458
29821
|
}];
|
|
29459
29822
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Ct as e,Dt as t,Hr as n,Ir as r,Mt as i,Nt as a,Ot as o,St as s,Tt as c,_t as l,bt as u,g as ee,gt as d,ht as f,io as te,jt as p,kt as m,m as h,mt as g,pt as _,so as v,uo as ne,v as re,vt as ie,wt as y,yt as ae}from"./src-s1tm1Ako.js";import{I as b,K as x,Q as S,R as oe,S as C,W as w,b as T,o as se,q as E,x as D,z as O}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DTufy7Ue.js";import{$ as ce}from"./src-BaYQ7lHC.js";import{p as le}from"./player-overlays-CM9K8NbB.js";import{t as k}from"./clock-ISV-5mJ1.js";import{t as A}from"./download-Dj8UGwXy.js";import{t as ue}from"./plus-CByuXPqh.js";import{t as j}from"./refresh-cw-BCQ0uqSo.js";import{t as de}from"./search-m5N8S4EI.js";import{t as M}from"./upload-D0e5f6HZ.js";import{G as N,L as P,N as fe,R as F,S as I,Y as L,g as pe,i as R,m as z,p as me,q as B,s as V}from"./index-BMoYnIQI.js";import{t as H}from"./AdminPage-Dq33XE9j.js";import{t as he}from"./NodeAddonsSettingsPanel-BI-P31wN.js";var ge=N(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),U=N(`octagon-alert`,[[`path`,{d:`M12 16h.01`,key:`1drbdi`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z`,key:`1fd625`}]]),W=N(`package`,[[`path`,{d:`M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z`,key:`1a0edw`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}],[`polyline`,{points:`3.29 7 12 12 20.71 7`,key:`ousv84`}],[`path`,{d:`m7.5 4.27 9 5.15`,key:`1c824w`}]]),G=N(`skip-forward`,[[`polygon`,{points:`5 4 15 12 5 20 5 4`,key:`16p6eg`}],[`line`,{x1:`19`,x2:`19`,y1:`5`,y2:`19`,key:`futhcm`}]]);S();var K=new Set([`completed`,`failed`,`partially-failed`,`cancelled`]),q=new Set([`done`,`failed`,`skipped`]);function J(e){return!q.has(e.phase)&&!K.has(e.jobState)}function _e(e,t){let n=new Map,r=[...e].sort((e,t)=>e.createdAtMs-t.createdAtMs);for(let e of r){let r=K.has(e.state);for(let i of e.tasks)r&&i.finishedAtMs!==null&&t-i.finishedAtMs>6e4||n.set(i.packageName,{jobId:e.jobId,packageName:i.packageName,phase:i.phase,fromVersion:i.fromVersion,toVersion:i.toVersion,error:i.error,jobState:e.state,finishedAtMs:i.finishedAtMs})}return n}function ve(){let{data:e}=s({activeOnly:!0},{refetchOnWindowFocus:!1,refetchInterval:e=>(e.state.data?.length??0)>0?3e3:!1}),{map:t}=ne(ce.AddonsJobProgress,e=>e.job.jobId);return w(()=>{let n=new Map;for(let t of e??[])n.set(t.jobId,t);for(let[e,r]of t)n.set(e,r.job);return _e([...n.values()],Date.now())},[e,t])}var Y={queued:{icon:k,cls:`bg-muted text-foreground-subtle`,label:`queued`},fetching:{icon:I,cls:`bg-primary/10 text-primary`,label:`fetching`,spin:!0},staged:{icon:I,cls:`bg-primary/10 text-primary`,label:`staged`,spin:!0},validating:{icon:I,cls:`bg-primary/10 text-primary`,label:`validating`,spin:!0},applying:{icon:I,cls:`bg-primary/10 text-primary`,label:`applying`,spin:!0},restarting:{icon:I,cls:`bg-amber-500/10 text-amber-600 dark:text-amber-400`,label:`restarting`,spin:!0},applied:{icon:I,cls:`bg-primary/10 text-primary`,label:`finalizing`,spin:!0},done:{icon:F,cls:`bg-success/10 text-success`,label:`updated`},failed:{icon:U,cls:`bg-danger/10 text-danger`,label:`failed`},skipped:{icon:G,cls:`bg-muted text-foreground-subtle`,label:`skipped`}};function ye({progress:e}){let t=Y[e.phase],n=t.icon,r=e.error??`${e.packageName}: ${e.fromVersion??`?`} → ${e.toVersion}`;return C(`span`,{"data-testid":`addon-job-progress`,className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded shrink-0 ${t.cls}`,title:r,children:[D(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label,C(`span`,{className:`font-mono`,children:[`→ `,e.toVersion]})]})}function be({packageName:e,currentVersion:t,installSource:n,onInstall:r,onClose:i,onSelect:a,selectedVersion:o}){let s=n===`upload`||n===`local`,{data:c,isLoading:l}=ie({name:e});return C(`div`,{className:`fixed z-50 w-72 rounded-lg border border-border bg-surface shadow-xl overflow-hidden`,style:{top:`auto`,bottom:`auto`},ref:e=>{if(!e)return;let t=e.parentElement?.getBoundingClientRect();if(!t)return;let n=window.innerHeight-t.bottom,r=t.top;n<280&&r>n?(e.style.bottom=`${window.innerHeight-t.top+4}px`,e.style.top=`auto`):(e.style.top=`${t.bottom+4}px`,e.style.bottom=`auto`),e.style.right=`${window.innerWidth-t.right}px`},onClick:e=>e.stopPropagation(),children:[C(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border`,children:[D(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle font-medium`,children:`Versions`}),D(`button`,{type:`button`,onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:D(R,{className:`h-3 w-3`})})]}),s&&C(`div`,{className:`flex items-start gap-2 px-3 py-2 border-b border-border bg-cyan-500/10 text-[10px]`,children:[D(M,{className:`h-3 w-3 mt-0.5 text-cyan-500 shrink-0`}),C(`div`,{className:`text-cyan-700 dark:text-cyan-200`,children:[C(`span`,{className:`font-semibold`,children:[n===`upload`?`Uploaded copy`:`Local copy`,` in place.`]}),` `,`Re-install `,D(`span`,{className:`font-mono`,children:t}),` from npm to track upstream releases again.`]})]}),l&&C(`div`,{className:`flex items-center justify-center gap-2 py-6 text-[10px] text-foreground-subtle`,children:[D(I,{className:`h-3.5 w-3.5 animate-spin`}),`Loading versions...`]}),c&&D(`div`,{className:`max-h-64 overflow-auto divide-y divide-border`,children:c.map(e=>{let n=e.version===t,i=a!==void 0&&e.version===o,c=/-(alpha|beta|rc|dev|canary|next)/i.test(e.version),l=C(T,{children:[D(`span`,{className:`font-mono flex-1 text-left ${n?`text-primary font-semibold`:c?`text-amber-400`:`text-foreground`}`,children:e.version}),n&&D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium bg-primary/10 text-primary`,children:`current`}),e.distTags.map(e=>D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e===`latest`?`bg-emerald-500/15 text-emerald-400`:e===`beta`?`bg-amber-500/15 text-amber-400`:`bg-gray-500/15 text-gray-400`}`,children:e},e)),e.deprecated&&D(`span`,{className:`text-[9px] text-red-400 line-through`,children:`deprecated`}),D(`span`,{className:`text-[9px] text-foreground-subtle shrink-0`,children:new Date(e.publishedAt).toLocaleDateString()})]});return a?C(`button`,{type:`button`,"aria-label":`Select ${e.version}`,onClick:()=>a(e.version),className:`w-full flex items-center gap-2 px-3 py-2 text-[11px] ${i?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[l,i?D(F,{className:`h-3 w-3 text-primary shrink-0`}):D(`span`,{className:`w-3 shrink-0`})]},e.version):C(`div`,{className:`flex items-center gap-2 px-3 py-2 text-[11px] ${n?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[l,n&&!s?D(F,{className:`h-3 w-3 text-primary shrink-0`}):D(`button`,{type:`button`,onClick:()=>r(e.version),className:`shrink-0 rounded p-0.5 hover:bg-primary/20 text-foreground-subtle hover:text-primary transition-colors`,title:n?`Re-install ${e.version} from npm`:`Install ${e.version}`,children:D(A,{className:`h-3 w-3`})})]},e.version)})}),c&&c.length===0&&D(`div`,{className:`py-4 text-center text-[10px] text-foreground-subtle`,children:`No versions found`})]})}var xe={queued:{icon:k,bg:`bg-muted text-foreground-subtle`,label:`queued`},updating:{icon:I,bg:`bg-primary/10 text-primary`,label:`updating`,spin:!0},"done-pending-restart":{icon:j,bg:`bg-amber-500/10 text-amber-700`,label:`pending restart`},done:{icon:F,bg:`bg-success/10 text-success`,label:`done`},failed:{icon:U,bg:`bg-danger/10 text-danger`,label:`failed`}};function Se({item:e}){let t=xe[e.status],n=t.icon;return C(`span`,{className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded ${t.bg}`,title:e.error,children:[D(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label]})}S();var X=`device-export`;function Ce(e){return e.capabilities.some(e=>(typeof e==`string`?e:e.name)===X)}function we({isBundle:e,installSource:t,addonId:n,iconPath:r,color:i}){let[a,o]=E(!1),s=i?{backgroundColor:`${i.startsWith(`#`)?i:`#${i}`}26`}:void 0,c=e?`bg-indigo-500/20 text-indigo-700 dark:text-indigo-300`:`bg-purple-500/20 text-purple-700 dark:text-purple-300`,l=n&&r?`/api/addon-assets/${n}/${r}`:null,u=!!l&&!a;return C(`div`,{className:`relative flex-shrink-0`,children:[D(`div`,{className:`h-9 w-9 rounded-lg flex items-center justify-center ${s?``:c}`,style:s,children:u?D(`img`,{src:l,alt:n,className:`h-5 w-5`,onError:()=>o(!0)}):D(e?W:pe,{className:`h-4 w-4`})}),t===`local`&&D(`span`,{className:`absolute -top-1.5 -left-1.5 text-[7px] font-bold leading-none bg-orange-500 text-white px-1 py-0.5 rounded -rotate-12 shadow-sm`,children:`DEV`}),t===`npm`&&D(`span`,{className:`absolute -top-1 -left-1 w-4 h-4 rounded bg-[#cb3837] flex items-center justify-center -rotate-12 shadow-sm`,children:D(`svg`,{viewBox:`0 0 780 250`,className:`w-[11px] h-[11px]`,children:D(`path`,{fill:`#fff`,d:`M240 250h100V50h100v200h340V0H0v250z`})})}),t===`upload`&&D(`span`,{className:`absolute -top-1.5 -left-1.5 flex items-center justify-center h-4 w-4 rounded bg-cyan-500 -rotate-12 shadow-sm`,children:D(M,{className:`h-2.5 w-2.5 text-white`})})]})}function Te({version:e,availableUpdate:t,selectedVersion:n,isUpdating:r,installSource:i,disabled:a,onUpdate:o,onOpenPicker:s,children:c}){if(r)return C(`span`,{className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-blue-500/30 bg-blue-500/10 text-blue-400 shrink-0`,children:[D(I,{className:`h-3 w-3 animate-spin`}),`Updating…`]});if(t){let r=n??t;return C(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[o&&C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),o()},disabled:a,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-semibold border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 disabled:opacity-50 disabled:cursor-not-allowed transition-colors`,title:`Update to ${r}`,children:[D(A,{className:`h-3 w-3`}),`Update`]}),C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),s()},disabled:a,"aria-label":`Select version to install`,title:`Installed ${e} — update available. Selected ${r}; tap to choose another version.`,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-bold font-mono border border-amber-500/40 bg-amber-500/15 text-amber-500 hover:bg-amber-500/25 animate-pulse disabled:opacity-50 disabled:cursor-not-allowed transition-colors`,children:[D(`span`,{className:`font-normal opacity-70`,children:e}),D(`span`,{className:`font-normal opacity-70`,children:`→`}),r,D(P,{className:`h-3 w-3`})]}),c]})}return C(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),s()},disabled:a,className:[`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-bold font-mono border transition-colors disabled:opacity-50 disabled:cursor-not-allowed`,i===`local`?`border-orange-500/30 bg-orange-500/10 text-orange-400 hover:bg-orange-500/20`:`border-emerald-500/30 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20`].join(` `),title:`Version ${e} — tap to view all versions`,children:[e,D(P,{className:`h-3 w-3 opacity-50`})]}),c]})}function Ee({addonId:e}){let t=se(),{data:n=`inherit`}=f({addonId:e}),r=m({onSuccess:()=>{t.invalidateQueries({queryKey:[[`addons`,`getAddonAutoUpdate`]]})}});return D(`div`,{className:`flex rounded-lg border border-border overflow-hidden text-[10px] font-medium`,children:[`inherit`,`off`,`latest`,`beta`].map(t=>D(`button`,{type:`button`,onClick:n=>{n.stopPropagation(),r.mutate({addonId:e,channel:t})},disabled:r.isPending,className:`px-2.5 py-1 transition-colors capitalize ${n===t?t===`inherit`?`bg-gray-700/50 text-gray-700 dark:text-gray-300`:t===`off`?`bg-gray-700 text-gray-700 dark:text-gray-300`:t===`latest`?`bg-blue-500/20 text-blue-400`:`bg-amber-500/20 text-amber-400`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`} ${t===`inherit`?``:`border-l border-border`}`,children:t},t))})}function De({addon:e,agents:n=[],clusterStatus:r,hideVersion:a,availableUpdate:o,onUpdate:s,isUpdating:l,jobProgress:u,bundle:d,bulkItem:f,disabledByBulk:p,isSystem:m}){let[g,_]=E(!1),[v,ne]=E(!1),[ie,y]=E(!1),[b,x]=E(null);O(()=>{x(null)},[o]);let S=b??o,[oe,w]=E(!1),[ce,k]=E(!1),[A,de]=E(`logs`),M=se(),N=le(),fe=B(),F=te(),{manifest:I}=e,L=e.installedOn??[],pe=!d&&!I.protected&&I.removable!==!1,H=!!d,G=d?.displayName??I.name,K=d?.packageName??I.packageName,q=d?.version??I.packageVersion??I.version,J=d?.installSource??e.installSource,_e=d?d.bundle?.description??d.addons[0]?.manifest.description:I.description,ve=i({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]})}}),Y=c({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]})}}),xe=ae({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]}),M.invalidateQueries({queryKey:[[`integrations`]]})}}),X=t({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`alerts`]]})}}),Z=e.health,Q=Z?.phase===`failed`&&!Z.inGracePeriod,Oe=Q&&Z?.nextRetryAt&&!Z.retrying;return C(`div`,{className:`rounded-lg border bg-surface overflow-hidden ${Q?`border-red-500/50`:`border-border`}`,children:[Q&&Z&&C(`div`,{className:`bg-danger/10 border-b border-danger/30 px-4 py-2.5 flex items-start gap-2.5`,children:[D(U,{className:`h-4 w-4 text-danger shrink-0 mt-0.5`}),C(`div`,{className:`flex-1 min-w-0`,children:[C(`div`,{className:`text-xs font-semibold text-danger`,children:[`Failed to load (attempt #`,Z.retryCount,`)`]}),D(`div`,{className:`text-[11px] text-foreground mt-0.5 break-words font-mono`,children:Z.lastError?.message??`Unknown error`}),Oe&&Z.nextRetryAt&&C(`div`,{className:`text-[10px] text-foreground-subtle mt-1`,children:[`Auto-retry at `,new Date(Z.nextRetryAt).toLocaleTimeString()]})]}),C(`button`,{type:`button`,disabled:X.isPending||Z.retrying,onClick:e=>{e.stopPropagation(),X.mutate({packageName:K})},className:`inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-semibold border border-red-400/40 bg-red-400/10 text-red-700 dark:text-red-300 hover:bg-red-400/20 disabled:opacity-50 disabled:cursor-not-allowed transition-colors shrink-0`,title:`Reset retry counter and force immediate reload`,children:[D(j,{className:`h-3 w-3 ${X.isPending||Z.retrying?`animate-spin`:``}`}),`Retry now`]})]}),C(`div`,{className:`flex items-start gap-3 px-4 py-3`,children:[D(we,{isBundle:H||!!I.components&&I.components.length>1,installSource:J,addonId:H?d.addons[0]?.manifest.id:I.id,iconPath:H?d.bundle?.icon??d.addons[0]?.manifest.icon:I.icon,color:H?d.addons[0]?.manifest.color:I.color}),C(`div`,{className:`flex-1 min-w-0`,children:[C(`div`,{className:`flex items-start justify-between gap-2`,children:[D(`span`,{className:`text-sm font-semibold text-foreground truncate min-w-0`,children:G}),!a&&D(Te,{version:q,availableUpdate:o,...S===void 0?{}:{selectedVersion:S},isUpdating:l,installSource:J,disabled:p,...s&&S!==void 0?{onUpdate:()=>s(S)}:{},onOpenPicker:()=>y(e=>!e),children:ie&&D(be,{packageName:K,currentVersion:q,...J?{installSource:J}:{},...o!==void 0&&S!==void 0?{selectedVersion:S,onSelect:e=>{x(e),y(!1)}}:{},onInstall:e=>{y(!1),xe.mutate({packageName:K,version:e})},onClose:()=>y(!1)})})]}),C(`div`,{className:`flex flex-wrap items-center gap-x-1.5 gap-y-1 mt-0.5`,children:[K!==G&&D(`span`,{className:`text-[10px] text-foreground-subtle font-mono truncate max-w-full`,children:K}),H&&C(`span`,{className:`text-[10px] rounded-full bg-primary/10 text-primary px-2 py-0.5 font-medium shrink-0 inline-flex items-center gap-1`,children:[D(W,{className:`h-3 w-3`}),d.addons.length]}),m&&C(`span`,{className:`text-[10px] rounded-full bg-amber-500/15 text-amber-700 dark:text-amber-300 px-2 py-0.5 font-medium shrink-0 inline-flex items-center gap-1`,title:`System package — updating restarts the hub`,children:[D(j,{className:`h-3 w-3`}),`System`]}),f!=null&&D(Se,{item:f}),u!=null&&D(ye,{progress:u})]}),_e&&D(`p`,{className:`text-[10px] text-foreground-subtle mt-1 line-clamp-1`,children:_e}),!H&&e.process&&C(`div`,{className:`flex flex-wrap items-center gap-1.5 mt-1`,children:[D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e.process.mode===`forked`?`bg-blue-500/15 text-blue-400`:`bg-gray-500/15 text-gray-400`}`,children:e.process.mode===`forked`?`forked`:`in-process`}),e.process.pid!=null&&C(`span`,{className:`text-[9px] font-mono text-foreground-subtle`,children:[`PID `,e.process.pid]}),D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e.process.state===`running`?`bg-emerald-500/15 text-emerald-400`:e.process.state===`crashed`||e.process.state===`failed`?`bg-red-500/15 text-red-400`:`bg-gray-500/15 text-gray-400`}`,children:e.process.state})]})]})]}),!H&&C(`div`,{className:`flex items-center gap-1 px-4 py-1.5 border-t border-border/50 bg-surface-hover/20`,children:[C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),Y.mutate({addonId:I.id})},disabled:Y.isPending||p,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-border text-foreground-subtle hover:text-blue-400 hover:border-blue-500/30 hover:bg-blue-500/10 disabled:opacity-50 transition-colors`,children:[D(j,{className:`h-3 w-3 ${Y.isPending?`animate-spin`:``}`}),`Restart`]}),C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),w(e=>!e)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${oe?`border-primary/30 bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground hover:border-primary/30 hover:bg-primary/10`}`,children:[D(z,{className:`h-3 w-3`}),`Logs`]}),!H&&C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),k(!0)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${ce?`border-primary/30 bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground hover:border-primary/30 hover:bg-primary/10`}`,children:[D(me,{className:`h-3 w-3`}),`Settings`]}),D(`div`,{className:`flex-1`}),pe&&C(`button`,{type:`button`,onClick:async e=>{e.stopPropagation(),await F({title:`Uninstall ${I.name}?`,message:`This will remove the addon package and all its data. This action cannot be undone.`,confirmLabel:`Uninstall`,variant:`danger`})&&ve.mutate({packageName:I.packageName})},disabled:ve.isPending||p,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-border text-foreground-subtle hover:text-red-400 hover:border-red-500/30 hover:bg-red-500/10 disabled:opacity-50 transition-colors`,children:[D(V,{className:`h-3 w-3`}),ve.isPending?`Removing...`:`Uninstall`]})]}),r&&r.nodes.length>1&&!H&&C(`div`,{className:`border-t border-border px-4 py-2 bg-muted/10`,children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle mb-1.5`,children:`Cluster Deployment`}),D(`div`,{className:`space-y-1`,children:r.nodes.map(e=>C(`div`,{className:`flex items-center gap-2 text-[10px]`,children:[D(`div`,{className:`h-1.5 w-1.5 rounded-full shrink-0 ${e.status===`offline`?`bg-muted-foreground/30`:e.synced?`bg-success`:`bg-amber-500`}`}),D(`span`,{className:`font-medium text-foreground min-w-[60px]`,children:e.name}),D(`span`,{className:`font-mono text-foreground-subtle`,children:e.version}),!e.synced&&e.status!==`offline`&&D(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-400 border border-amber-500/20`,children:`outdated`}),e.status===`offline`&&D(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-muted text-muted-foreground`,children:`offline`}),e.synced&&e.status!==`offline`&&D(`span`,{className:`text-[9px] text-success`,children:`synced`})]},e.nodeId))})]}),oe&&!H&&C(`div`,{className:`border-t border-border`,children:[C(`div`,{className:`flex items-center px-4 py-1 bg-surface-hover/20 border-b border-border/50`,children:[C(`div`,{className:`flex rounded border border-border overflow-hidden text-[9px]`,children:[D(`button`,{type:`button`,onClick:()=>de(`logs`),className:`px-2 py-0.5 transition-colors ${A===`logs`?`bg-primary/10 text-primary`:`text-foreground-subtle hover:bg-surface-hover`}`,children:`Logs`}),D(`button`,{type:`button`,onClick:()=>de(`events`),className:`px-2 py-0.5 border-l border-border transition-colors ${A===`events`?`bg-primary/10 text-primary`:`text-foreground-subtle hover:bg-surface-hover`}`,children:`Events`})]}),D(`div`,{className:`flex-1`}),D(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),w(!1)},className:`text-foreground-subtle hover:text-foreground`,children:D(R,{className:`h-3.5 w-3.5`})})]}),A===`logs`&&D(ee,{addonId:I.id,maxHeight:`max-h-64`,showFilters:!1}),A===`events`&&D(h,{addonId:I.id,category:`addon.*`,maxHeight:`max-h-64`})]}),H&&C(T,{children:[D(`button`,{type:`button`,onClick:()=>_(e=>!e),className:`w-full flex items-center justify-center border-t border-border py-1 hover:bg-surface-hover/50 transition-colors`,children:D(g?ge:P,{className:`h-3.5 w-3.5 text-foreground-subtle`})}),g&&D(`div`,{className:`border-t border-border px-3 py-2 space-y-2`,children:d.addons.map(e=>D(De,{addon:e,agents:n,hideVersion:!0},e.manifest.id))})]}),ce&&!H&&D(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,onClick:()=>k(!1),children:C(`div`,{className:`w-full max-w-lg max-h-[80vh] rounded-xl border border-border bg-surface shadow-2xl flex flex-col overflow-hidden`,onClick:e=>e.stopPropagation(),children:[C(`div`,{className:`flex items-center justify-between px-5 py-3 border-b border-border flex-shrink-0`,children:[C(`div`,{className:`flex items-center gap-2`,children:[D(me,{className:`h-4 w-4 text-primary`}),C(`h2`,{className:`text-sm font-semibold text-foreground`,children:[G,` Settings`]})]}),D(`button`,{onClick:()=>k(!1),className:`text-foreground-subtle hover:text-foreground transition-colors p-1 rounded`,children:D(R,{className:`h-4 w-4`})})]}),C(`div`,{className:`flex-1 overflow-y-auto px-5 py-4 space-y-4`,children:[D(he,{nodeId:`hub`,addonIds:[I.id],level:`global`}),Ce(I)&&C(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Device export`}),D(re,{addonId:I.id,trpc:N.trpcClient,onOpenDevice:e=>fe(`/devices/${e}`)})]}),J===`npm`&&C(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Auto-Update`}),D(Ee,{addonId:I.id})]}),I.components&&I.components.length>0&&C(`div`,{children:[C(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:[`Components (`,I.components.length,`)`]}),D(`div`,{className:`flex flex-wrap gap-1`,children:I.components.map(e=>D(`span`,{className:`text-[10px] rounded bg-background border border-border px-1.5 py-0.5 text-foreground-subtle font-mono`,children:e},e))})]}),n.length>=2&&C(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Installed on`}),C(`div`,{className:`flex items-center gap-1.5 flex-wrap`,children:[L.length===0&&D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Hub (local)`}),L.map(e=>D(`span`,{className:`rounded-full bg-surface-hover border border-border px-2 py-0.5 text-[10px] text-foreground-subtle`,children:e},e)),C(`div`,{className:`relative`,children:[C(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),ne(e=>!e)},className:`inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2 py-0.5 text-[10px] text-foreground-subtle hover:text-foreground hover:border-primary transition-colors`,title:`Install on more agents`,children:[D(ue,{className:`h-3 w-3`}),`Add agent`]}),v&&C(`div`,{className:`absolute left-0 top-full mt-1 z-10 min-w-[160px] rounded-md border border-border bg-surface shadow-lg overflow-hidden`,children:[n.filter(e=>!e.isHub&&!L.includes(e.name)).map(e=>D(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),ne(!1)},className:`w-full text-left px-3 py-1.5 text-[10px] text-foreground hover:bg-surface-hover transition-colors`,children:e.name},e.id)),n.filter(e=>!e.isHub&&!L.includes(e.name)).length===0&&D(`div`,{className:`px-3 py-1.5 text-[10px] text-foreground-subtle`,children:`No available agents`})]})]})]})]})]}),D(`div`,{className:`flex items-center justify-end px-5 py-3 border-t border-border flex-shrink-0`,children:D(`button`,{type:`button`,onClick:()=>k(!1),className:`rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-foreground-subtle hover:text-foreground hover:bg-surface-hover transition-colors`,children:`Close`})})]})})]})}S();var Z=`.tgz,.tar.gz,.zip`;function Q({onUploadSuccess:e}){let[t,n]=E(!1),r=x(null),i=b(async t=>{n(!0);let r=new FormData;r.append(`file`,t);let i=localStorage.getItem(`camstack_admin_token`)??``;try{(await(await fetch(`/api/addons/upload`,{method:`POST`,headers:{Authorization:`Bearer ${i}`},body:r})).json()).success&&e()}finally{n(!1)}},[e]);return C(T,{children:[D(`input`,{ref:r,type:`file`,accept:Z,onChange:b(e=>{let t=e.target.files?.[0];t&&i(t),e.target.value=``},[i]),className:`hidden`}),C(`button`,{type:`button`,onClick:()=>r.current?.click(),disabled:t,className:`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,children:[t?D(I,{className:`w-3.5 h-3.5 animate-spin`}):D(M,{className:`w-3.5 h-3.5`}),t?`Uploading...`:`Upload`]})]})}S();var Oe=new Set([`@camstack/system`,`@camstack/types`,`@camstack/sdk`,`@camstack/shm-ring`,`@camstack/ui-library`]);function ke(e){return e!==void 0&&Oe.has(e)}function Ae(e,t){let n=e??`hub-only`;return n===`any-node`?!0:n===`hub-only`?t:!t}function je(e){let t=e;if(!t?.manifest)return null;let n={...t.manifest,name:t.manifest.name??t.manifest.id,version:t.manifest.version??`0.0.0`,capabilities:t.manifest.capabilities??[]},r=t.process,i=r?{pid:r.pid,mode:r.mode===`forked`?`forked`:`in-process`,state:r.state??`unknown`}:void 0,a=t.health,o=t.declaration?.execution,s=t.manifest.execution,c=o?.placement??s?.placement;return{manifest:n,enabled:!0,source:t.source,installSource:t.installSource,installedOn:[],...i?{process:i}:{},hasBackup:t.hasBackup,...a?{health:a}:{},...c?{placement:c}:{}}}function Me(e){let t=new Map,n=[];for(let r of e){let e=r.manifest.packageName;if(!e){n.push(r);continue}let i=t.get(e);if(i)i.addons.push(r);else{let n=r.manifest.bundle,i={packageName:e,displayName:n?.displayName??r.manifest.packageDisplayName??r.manifest.name,version:r.manifest.packageVersion,addons:[r],...r.installSource===void 0?{}:{installSource:r.installSource},...n===void 0?{}:{bundle:n}};t.set(e,i)}}let r=[];for(let e of t.values())e.bundle!==void 0||e.addons.length>=2?r.push({bundle:e,representative:e.addons[0]}):r.push(e.addons[0]);return r.push(...n),r}function $(e){return`bundle`in e}function Ne({addons:t,clusterStatus:n,selectedNode:r,onRefresh:i,isRefreshing:s}){let c=se(),[u,ee]=E(``),f=oe(u),[m,h]=E(!1),g=x(null);O(()=>{if(!m)return;let e=e=>{g.current&&!g.current.contains(e.target)&&h(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[m]);let{data:v}=e({nodeId:r.id}),ne=w(()=>{let e=new Map;for(let t of v??[])e.set(t.name,t.latestVersion);return e},[v]),re=w(()=>(v??[]).filter(e=>!e.isSystem).length,[v]),[ie,y]=E(()=>new Set),[S,T]=E(()=>new Map),ce=b(e=>{y(t=>new Set(t).add(e))},[]),le=b(e=>{y(t=>{if(!t.has(e))return t;let n=new Set(t);return n.delete(e),n})},[]),k=b((e,t)=>{T(n=>new Map(n).set(e,t))},[]),ue=b(e=>{T(t=>{if(!t.has(e))return t;let n=new Map(t);return n.delete(e),n})},[]),M=a({onMutate:e=>{ce(e.name),ue(e.name)},onSuccess:(e,t)=>{le(t.name);let n=e;if(n&&n.success===!1){k(t.name,n.error??`Update failed (no error message returned by server)`);return}c.invalidateQueries({queryKey:[[`addons`]]}),c.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]})},onError:(e,t)=>{le(t.name),k(t.name,e instanceof Error?e.message:String(e))}}),N=ve(),P=x(new Set);O(()=>{let e=new Set;for(let[t,n]of N)J(n)&&e.add(t);let t=[...P.current].some(t=>!e.has(t));P.current=e,t&&(c.invalidateQueries({queryKey:[[`addons`]]}),c.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]}))},[N,c]);let{data:pe}=d(void 0),R=_({onSuccess:()=>{c.invalidateQueries({queryKey:[[`addons`,`getAutoUpdateSettings`]]})}}),{data:z,isFetching:me}=o({query:f||void 0},{enabled:m}),B=p(),{data:V}=l(void 0),H=V?.requestedAt,[he,ge]=E(null),[U,W]=E(!1);O(()=>{if(V?.kind===`framework-bulk-update`&&V.requestedAt!==he){ge(V.requestedAt),W(!0);let e=window.setTimeout(()=>W(!1),6e3);return()=>window.clearTimeout(e)}},[H,he,V]),te();let G=b(async()=>{let e=(v??[]).filter(e=>!e.isSystem);e.length!==0&&await B.mutateAsync({kind:`update`,targets:e.map(e=>({name:e.name,version:e.latestVersion})),nodeIds:[r.id]})},[v,r.id,B]),K=ae({onSuccess:()=>{c.invalidateQueries({queryKey:[[`addons`]]}),c.invalidateQueries({queryKey:[[`addonPages`]]}),c.invalidateQueries({queryKey:[[`capabilities`]]}),c.invalidateQueries({queryKey:[[`integrations`]]})}}),[q,_e]=E(`type`),[Y,ye]=E(()=>typeof window>`u`?`grouped`:window.localStorage.getItem(`camstack.addons.viewMode`)===`flat`?`flat`:`grouped`),be=e=>{ye(e),typeof window<`u`&&window.localStorage.setItem(`camstack.addons.viewMode`,e)},[xe,Se]=L(),X=xe.get(`capability`),Ce=w(()=>{let e=t.filter(e=>!ke(e.manifest.packageName));return r.isHub||(e=e.filter(e=>Ae(e.placement,r.isHub))),X&&(e=e.filter(e=>(e.manifest.capabilities??[]).some(e=>(typeof e==`string`?e:e.name)===X))),e},[t,r.isHub,X]),we=()=>{let e=new URLSearchParams(xe);e.delete(`capability`),Se(e,{replace:!0})},Te=Y===`flat`?Ce:Me(Ce),Ee=w(()=>{let e=[...Te];switch(q){case`type`:e.sort((e,t)=>{let n=+!$(e),r=+!$(t);if(n!==r)return n-r;let i=$(e)?e.bundle.displayName:e.manifest.name,a=$(t)?t.bundle.displayName:t.manifest.name;return i.localeCompare(a)});break;case`name`:e.sort((e,t)=>{let n=$(e)?e.bundle.displayName:e.manifest.name,r=$(t)?t.bundle.displayName:t.manifest.name;return n.localeCompare(r)});break;case`source`:e.sort((e,t)=>{let n=$(e)?e.bundle.installSource??``:e.installSource??``,r=$(t)?t.bundle.installSource??``:t.installSource??``;return n.localeCompare(r)});break}return e},[Te,q]);return C(`div`,{className:`space-y-4`,children:[[...S.entries()].map(([e,t])=>D(`div`,{className:`rounded-md border border-danger/30 bg-danger/5 px-3 py-2 text-[11px]`,children:C(`div`,{className:`flex items-start justify-between gap-3`,children:[C(`div`,{className:`space-y-0.5`,children:[C(`div`,{className:`font-medium text-danger`,children:[`Failed to update `,e]}),D(`div`,{className:`text-foreground-subtle font-mono`,children:t})]}),D(`button`,{type:`button`,onClick:()=>ue(e),className:`text-foreground-subtle hover:text-foreground text-[10px]`,children:`dismiss`})]})},e)),C(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[C(`div`,{className:`flex items-center gap-1.5`,children:[D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Sort:`}),C(`select`,{value:q,onChange:e=>_e(e.target.value),className:`rounded-md border border-border bg-surface px-2 py-1.5 text-[11px] text-foreground focus:outline-none focus:border-primary/50`,children:[D(`option`,{value:`type`,children:`Bundles first`}),D(`option`,{value:`name`,children:`Name`}),D(`option`,{value:`source`,children:`Install source`})]})]}),C(`div`,{className:`flex items-center gap-1.5`,children:[D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`View:`}),D(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`grouped`,`flat`].map(e=>D(`button`,{type:`button`,onClick:()=>be(e),className:`px-2.5 py-1.5 transition-colors capitalize ${Y===e?`bg-primary/10 text-primary`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`}`,title:e===`grouped`?`Group multi-addon packages into a bundle card`:`Show every addon as its own card`,children:e},e))})]}),r.isHub&&C(`div`,{className:`flex items-center gap-1.5`,children:[D(j,{className:`h-3 w-3 text-foreground-subtle`}),D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Auto-Update:`}),D(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`off`,`latest`,`beta`].map(e=>D(`button`,{type:`button`,onClick:()=>R.mutate({channel:e}),disabled:R.isPending,className:`px-2.5 py-1.5 transition-colors capitalize ${pe?.channel===e?e===`off`?`bg-gray-700 text-gray-200`:e===`latest`?`bg-blue-500/20 text-blue-400`:`bg-amber-500/20 text-amber-400`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`} ${e===`off`?``:`border-l border-border`}`,children:e},e))})]}),re>0&&C(`button`,{type:`button`,onClick:()=>void G(),disabled:B.isPending,className:`flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 border border-blue-500/30 disabled:opacity-50 transition-colors font-medium`,children:[B.isPending?D(I,{className:`w-3 h-3 animate-spin`}):D(A,{className:`w-3 h-3`}),B.isPending?`Updating…`:`Update All (${re})`]}),C(`button`,{type:`button`,onClick:i,disabled:s||B.isPending,className:`flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,children:[D(j,{className:`w-3 h-3 ${s?`animate-spin`:``}`}),`Refresh`]})]}),C(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3`,children:[C(`div`,{className:`relative flex-1 min-w-0 sm:min-w-[200px]`,ref:g,children:[D(de,{className:`absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-foreground-subtle`}),D(`input`,{type:`text`,value:u,onChange:e=>{ee(e.target.value),h(!0)},onFocus:()=>h(!0),placeholder:`Search addons on npm...`,disabled:B.isPending,className:`w-full rounded-lg border border-border bg-background pl-9 pr-3 py-2 text-xs text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed`}),me&&D(I,{className:`absolute right-2.5 top-1/2 -translate-y-1/2 h-3 w-3 text-foreground-subtle animate-spin`}),m&&z&&z.length>0&&C(`div`,{className:`absolute left-0 right-0 top-full mt-1 z-40 rounded-lg border border-border bg-surface shadow-xl overflow-hidden`,children:[z.length>0&&C(`div`,{className:`px-3 py-1.5 text-[10px] uppercase tracking-wide text-foreground-subtle border-b border-border`,children:[`npm results (`,z.length,`)`]}),z&&z.length>0&&D(`div`,{className:`max-h-72 overflow-auto divide-y divide-border`,children:z.map(e=>C(`div`,{className:`flex items-center gap-3 px-3 py-2 hover:bg-surface-hover transition-colors`,children:[D(`div`,{className:`w-6 h-6 rounded-md bg-primary/10 flex items-center justify-center text-primary text-[9px] font-bold shrink-0`,children:e.name.replace(`@camstack/addon-`,``).charAt(0).toUpperCase()}),C(`div`,{className:`flex-1 min-w-0`,children:[C(`div`,{className:`flex items-baseline gap-2 min-w-0`,children:[D(`span`,{className:`text-[11px] font-semibold break-words`,children:e.name.replace(/^@camstack\/(addon-)?/,``)}),C(`span`,{className:`text-[10px] text-foreground-subtle shrink-0`,children:[`v`,e.version]})]}),D(`div`,{className:`font-mono text-[9px] text-foreground-subtle/80 truncate`,children:e.name}),e.description&&D(`div`,{className:`text-[10px] text-foreground-subtle truncate`,children:e.description})]}),e.installed?C(`span`,{className:`inline-flex items-center gap-1 px-2 py-1 text-[10px] font-medium rounded-md border border-emerald-500/30 bg-emerald-500/10 text-emerald-400 shrink-0`,children:[D(F,{className:`h-3 w-3`}),e.installedVersion??`Installed`]}):C(`button`,{type:`button`,onClick:()=>K.mutate({packageName:e.name}),disabled:K.isPending,className:`inline-flex items-center gap-1 px-2 py-1 text-[10px] font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors shrink-0`,children:[K.isPending?D(I,{className:`h-3 w-3 animate-spin`}):D(A,{className:`h-3 w-3`}),`Install`]})]},e.name))})]})]}),D(Q,{onUploadSuccess:()=>{c.invalidateQueries({queryKey:[[`addons`]]}),i()}})]}),X&&C(`div`,{className:`flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/30 text-sm`,children:[D(`span`,{className:`text-foreground-subtle`,children:`Filtering by capability:`}),D(`span`,{className:`font-mono text-primary font-semibold`,children:X}),D(`button`,{type:`button`,onClick:we,className:`ml-auto text-xs text-foreground-subtle hover:text-foreground underline`,children:`Clear`})]}),U&&C(`div`,{className:`fixed top-[calc(env(safe-area-inset-top)+1rem)] right-[calc(env(safe-area-inset-right)+1rem)] z-50 flex items-start gap-2 px-3 py-2.5 rounded-md border border-green-500/30 bg-green-500/10 shadow-lg backdrop-blur-sm max-w-sm pointer-events-auto`,children:[D(fe,{className:`w-4 h-4 shrink-0 mt-0.5 text-green-400`}),C(`div`,{children:[D(`div`,{className:`text-[11px] font-semibold text-foreground`,children:`Bulk update completed`}),D(`div`,{className:`text-[10px] text-foreground-subtle mt-0.5`,children:`System packages updated — hub restarted successfully.`})]})]}),Ce.length===0&&D(`div`,{className:`text-xs text-foreground-subtle`,children:t.length===0?`No addons installed`:X?`No installed addons declare the "${X}" capability. Browse the npm catalog above to find a compatible addon.`:`No addons compatible with ${r.name} (placement filter active)`}),D(`div`,{className:`space-y-2`,children:Ee.map(e=>{let t=$(e)?e.bundle.packageName:e.manifest.packageName,i=ne.get(t),a=$(e)?e.representative:e,o=i?e=>M.mutate({name:t,version:e,nodeId:r.id}):void 0,s=N.get(t)??null,c=s!==null&&J(s);return D(De,{addon:a,clusterStatus:n?.[a.manifest.id],bundle:$(e)?e.bundle:void 0,availableUpdate:i,isUpdating:ie.has(t)||c,jobProgress:s,onUpdate:o,disabledByBulk:B.isPending},$(e)?t:e.manifest.id)})})]})}function Pe(){let e=se(),{data:t,isLoading:i,isError:a}=u(),{data:o}=r(void 0),{data:s}=n(void 0,{refetchInterval:1e4}),c=w(()=>{let e=new Map;e.set(`hub`,{id:`hub`,name:`Hub`,isHub:!0});for(let t of s??[]){let n=String(t.id??``);n&&e.set(n,{id:n,name:String(t.name??n),isHub:!!t.isHub})}return[...e.values()]},[s]),[l,ee]=E(`hub`),d=c.find(e=>e.id===l)??c[0],f=g(),te=y(),p=f.isPending||te.isPending,m=async()=>{await f.mutateAsync({nodeId:l}),await te.mutateAsync(),e.invalidateQueries({queryKey:[[`addons`]]})},h=x(f);h.current=f,O(()=>{h.current.mutate({nodeId:`hub`})},[]);let _=(t??[]).map(je).filter(e=>e!==null);return C(H,{children:[C(`div`,{className:`flex items-center justify-between gap-3 flex-wrap`,children:[D(v,{selectedNodeId:l,onSelect:ee}),C(`div`,{className:`flex items-center gap-3 flex-wrap`,children:[!i&&!a&&C(`span`,{className:`text-[10px] rounded-full bg-primary/10 text-primary px-2 py-0.5 font-medium`,children:[_.length,` installed`]}),C(`button`,{type:`button`,onClick:()=>void m(),disabled:p,className:`inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,title:`Re-fetch addons + check npm for new versions`,children:[D(j,{className:`w-3 h-3 ${p?`animate-spin`:``}`}),`Check for updates`]})]})]}),!d.isHub&&C(`div`,{className:`rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[11px] text-foreground-subtle`,children:[`Showing only addons compatible with`,` `,D(`span`,{className:`font-medium text-foreground`,children:d.name}),` — agents only run`,` `,D(`code`,{className:`font-mono`,children:`agent-only`}),` and`,` `,D(`code`,{className:`font-mono`,children:`any-node`}),` addons. Install / uninstall is hub-driven; agents auto-sync from the hub.`]}),i&&D(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading…`}),a&&D(`div`,{className:`text-xs text-danger`,children:`Failed to load addons`}),!i&&!a&&D(Ne,{addons:_,clusterStatus:o,selectedNode:d,onRefresh:()=>void m(),isRefreshing:p})]})}export{Pe as AddonsPage};
|