camstack 1.2.189 → 1.2.191

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/cli.js CHANGED
@@ -37,7 +37,7 @@ async function runServe(args) {
37
37
  ...typeof values.data === "string" ? { data: values.data } : {}
38
38
  };
39
39
  Object.assign(process.env, buildServeEnv(opts));
40
- await import("./launcher-LKHQ7I4L.js");
40
+ await import("./launcher-AZI5I6X5.js");
41
41
  }
42
42
 
43
43
  // src/commands/agent.ts
@@ -82,7 +82,7 @@ async function runAgent(args) {
82
82
  ...typeof values.port === "string" ? { port: values.port } : {}
83
83
  };
84
84
  Object.assign(process.env, buildAgentEnv(opts));
85
- await import("./launcher-LKHQ7I4L.js");
85
+ await import("./launcher-AZI5I6X5.js");
86
86
  }
87
87
 
88
88
  // src/commands/setup.ts
@@ -91,7 +91,7 @@ import * as path from "path";
91
91
  import * as fs from "fs";
92
92
  import { parseArgs as parseArgs3 } from "util";
93
93
 
94
- // ../types/dist/event-category-BvOFi6PY.mjs
94
+ // ../types/dist/event-category-DOl51-Ij.mjs
95
95
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
96
96
  EventCategory2["SystemBoot"] = "system.boot";
97
97
  EventCategory2["SystemAddonsReady"] = "system.addons-ready";
@@ -111,6 +111,7 @@ var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
111
111
  EventCategory2["AddonRunnerRefused"] = "addon.runner-refused";
112
112
  EventCategory2["ProcessStateChanged"] = "process.state-changed";
113
113
  EventCategory2["SystemRunnerCheck"] = "system.runner-check";
114
+ EventCategory2["AlertEpisode"] = "alert.episode";
114
115
  EventCategory2["AddonError"] = "addon.error";
115
116
  EventCategory2["AddonPageReady"] = "addon.page-ready";
116
117
  EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
@@ -14772,7 +14773,7 @@ function date4(params) {
14772
14773
  // ../../node_modules/zod/v4/classic/external.js
14773
14774
  config(en_default());
14774
14775
 
14775
- // ../types/dist/sleep-BegKjwUu.mjs
14776
+ // ../types/dist/sleep-BxjU_fJM.mjs
14776
14777
  var AUDIO_CHUNK_FORMATS = [
14777
14778
  "f32le",
14778
14779
  "pcmu",
@@ -14967,6 +14968,7 @@ var WELL_KNOWN_TABS = [
14967
14968
  ];
14968
14969
  var WELL_KNOWN_TAB_MAP = Object.fromEntries(WELL_KNOWN_TABS.map((t) => [t.id, t]));
14969
14970
  var SECRET_RETRY_MAX_MS = 5 * 6e4;
14971
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
14970
14972
  var PEER_BYTES_CHUNK_BYTES = 256 * 1024;
14971
14973
  var PeerBytesTicketSchema = external_exports.object({
14972
14974
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -18451,6 +18453,150 @@ var ConvertResultSchema = external_exports.object({
18451
18453
  entry: ModelCatalogEntrySchema,
18452
18454
  artifacts: external_exports.array(ConvertArtifactSchema).readonly()
18453
18455
  });
18456
+ var MethodAccessSchema = external_exports.enum([
18457
+ "view",
18458
+ "create",
18459
+ "delete"
18460
+ ]);
18461
+ var AllowedProviderSchema = external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]);
18462
+ var AllowedDevicesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]));
18463
+ var CapScopeSchema = external_exports.enum(["device", "system"]);
18464
+ var DeviceSelectorSchema = external_exports.discriminatedUnion("kind", [
18465
+ external_exports.object({ kind: external_exports.literal("all") }),
18466
+ external_exports.object({
18467
+ kind: external_exports.literal("ids"),
18468
+ ids: external_exports.array(external_exports.number().int()).min(1)
18469
+ }),
18470
+ external_exports.object({
18471
+ kind: external_exports.literal("types"),
18472
+ types: external_exports.array(external_exports.enum(DeviceType)).min(1)
18473
+ }),
18474
+ external_exports.object({
18475
+ kind: external_exports.literal("locations"),
18476
+ locations: external_exports.array(external_exports.string().min(1)).min(1)
18477
+ })
18478
+ ]);
18479
+ var DeviceTokenScopeSchema = external_exports.object({
18480
+ type: external_exports.literal("device"),
18481
+ /** The device SET this grant covers — resolved against the live fleet. */
18482
+ selector: DeviceSelectorSchema,
18483
+ access: external_exports.array(MethodAccessSchema).min(1),
18484
+ /**
18485
+ * Whether a grant on a PARENT device transparently covers its accessory
18486
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
18487
+ * Direction is parent → children ONLY.
18488
+ *
18489
+ * Absent → the matcher DERIVES it from the access flavour: `view`
18490
+ * inherits (a camera viewer sees the camera's accessories), `create` /
18491
+ * `delete` do NOT (actuating/removing a child is an explicit act the
18492
+ * operator must grant on the child, not inherit from the parent). Set it
18493
+ * explicitly to override that default per grant.
18494
+ */
18495
+ includeLinked: external_exports.boolean().optional()
18496
+ });
18497
+ function migrateLegacyTokenScope(raw) {
18498
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
18499
+ if (Reflect.get(raw, "type") !== "device") return raw;
18500
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
18501
+ const targets = Reflect.get(raw, "targets");
18502
+ if (!Array.isArray(targets)) return raw;
18503
+ return {
18504
+ type: "device",
18505
+ selector: {
18506
+ kind: "ids",
18507
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
18508
+ },
18509
+ access: Reflect.get(raw, "access")
18510
+ };
18511
+ }
18512
+ var TokenScopeSchema = external_exports.preprocess(migrateLegacyTokenScope, external_exports.discriminatedUnion("type", [
18513
+ external_exports.object({
18514
+ type: external_exports.literal("category"),
18515
+ target: CapScopeSchema,
18516
+ access: external_exports.array(MethodAccessSchema).min(1)
18517
+ }),
18518
+ external_exports.object({
18519
+ type: external_exports.literal("capability"),
18520
+ target: external_exports.string(),
18521
+ access: external_exports.array(MethodAccessSchema).min(1)
18522
+ }),
18523
+ external_exports.object({
18524
+ type: external_exports.literal("addon"),
18525
+ target: external_exports.string(),
18526
+ access: external_exports.array(MethodAccessSchema).min(1)
18527
+ }),
18528
+ DeviceTokenScopeSchema
18529
+ ]));
18530
+ var UserRecordSchema = external_exports.object({
18531
+ id: external_exports.string(),
18532
+ username: external_exports.string(),
18533
+ passwordHash: external_exports.string(),
18534
+ /**
18535
+ * Admin bypass. When true, the middleware skips the scope-access
18536
+ * check entirely. There is no other axis of privilege; the legacy
18537
+ * role enum collapsed onto this boolean in v2.
18538
+ */
18539
+ isAdmin: external_exports.boolean().default(false),
18540
+ allowedProviders: AllowedProviderSchema,
18541
+ allowedDevices: AllowedDevicesSchema,
18542
+ /**
18543
+ * Scopes granted to this user. Admins bypass; their `scopes` is
18544
+ * ignored. Non-admins without scopes are locked out of every
18545
+ * protected call.
18546
+ */
18547
+ scopes: external_exports.array(TokenScopeSchema).default([]),
18548
+ createdAt: external_exports.number(),
18549
+ updatedAt: external_exports.number()
18550
+ });
18551
+ var ApiKeyRecordSchema = external_exports.object({
18552
+ id: external_exports.string(),
18553
+ label: external_exports.string(),
18554
+ isAdmin: external_exports.boolean().default(false),
18555
+ allowedProviders: AllowedProviderSchema,
18556
+ allowedDevices: AllowedDevicesSchema,
18557
+ tokenHash: external_exports.string(),
18558
+ tokenPrefix: external_exports.string(),
18559
+ createdAt: external_exports.number(),
18560
+ lastUsedAt: external_exports.number().optional()
18561
+ });
18562
+ var ScopedTokenSchema = external_exports.object({
18563
+ id: external_exports.string(),
18564
+ userId: external_exports.string(),
18565
+ name: external_exports.string(),
18566
+ tokenHash: external_exports.string(),
18567
+ tokenPrefix: external_exports.string(),
18568
+ scopes: external_exports.array(TokenScopeSchema),
18569
+ expiresAt: external_exports.number().nullish(),
18570
+ lastUsedAt: external_exports.number().nullish(),
18571
+ createdAt: external_exports.number()
18572
+ });
18573
+ var ShareViewGrantSchema = external_exports.object({
18574
+ kind: external_exports.enum(SHARE_VIEW_KINDS),
18575
+ deviceIds: external_exports.array(external_exports.number().int().nonnegative()).min(1).max(64),
18576
+ /** Talk-back, opt-in at mint time; absent means off. */
18577
+ talk: external_exports.boolean().optional()
18578
+ });
18579
+ var ShareViewScopeSchema = external_exports.object({
18580
+ type: external_exports.literal("share-view"),
18581
+ grant: ShareViewGrantSchema
18582
+ });
18583
+ var AddonRoutesScopeSchema = external_exports.object({
18584
+ type: external_exports.literal("addon-routes"),
18585
+ access: external_exports.array(MethodAccessSchema).min(1)
18586
+ });
18587
+ var CredentialScopeSchema = external_exports.union([
18588
+ external_exports.literal("*"),
18589
+ ShareViewScopeSchema,
18590
+ AddonRoutesScopeSchema,
18591
+ TokenScopeSchema
18592
+ ]);
18593
+ var CREDENTIAL_TOKEN_KIND = "credential";
18594
+ var CredentialTokenClaimsSchema = external_exports.object({
18595
+ kind: external_exports.literal(CREDENTIAL_TOKEN_KIND),
18596
+ sub: external_exports.string().min(1),
18597
+ jti: external_exports.string().min(16),
18598
+ scopes: external_exports.array(CredentialScopeSchema).min(1)
18599
+ });
18454
18600
  var AppFlowPurposeSchema = external_exports.enum(["new-session", "step-up"]);
18455
18601
  var AppPageLangSchema = external_exports.enum(["en", "it"]);
18456
18602
  var APP_FLOW_REFUSAL_MESSAGES = {
@@ -19086,7 +19232,14 @@ var AlertSeveritySchema = external_exports.enum([
19086
19232
  ]);
19087
19233
  var AlertActionSchema = external_exports.object({
19088
19234
  label: external_exports.string(),
19089
- href: external_exports.string().optional()
19235
+ href: external_exports.string().optional(),
19236
+ /**
19237
+ * How `href` is followed. Absent / `app`: an admin-ui route (client-side).
19238
+ * `server`: a route the HUB serves (an addon HTTP route such as an OAuth
19239
+ * start) — the browser must leave the SPA with a full navigation, because
19240
+ * the SPA has no such route and would bounce to the home page.
19241
+ */
19242
+ navigation: external_exports.enum(["app", "server"]).optional()
19090
19243
  });
19091
19244
  var AlertStatusSchema = external_exports.enum([
19092
19245
  "active",
@@ -48535,6 +48688,9 @@ var NcSystemEventKindSchema = external_exports.enum([
48535
48688
  "server-updated",
48536
48689
  "export-completed",
48537
48690
  "backup-completed",
48691
+ "alert-critical",
48692
+ "alert-error",
48693
+ "alert-critical-resolved",
48538
48694
  "camera-online",
48539
48695
  "camera-offline",
48540
48696
  "camera-disabled",
@@ -50253,123 +50409,6 @@ var notificationRulesCapability = {
50253
50409
  getAlarmSensors: method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" })
50254
50410
  }
50255
50411
  };
50256
- var MethodAccessSchema = external_exports.enum([
50257
- "view",
50258
- "create",
50259
- "delete"
50260
- ]);
50261
- var AllowedProviderSchema = external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]);
50262
- var AllowedDevicesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.literal("*"), external_exports.array(external_exports.string())]));
50263
- var CapScopeSchema = external_exports.enum(["device", "system"]);
50264
- var DeviceSelectorSchema = external_exports.discriminatedUnion("kind", [
50265
- external_exports.object({ kind: external_exports.literal("all") }),
50266
- external_exports.object({
50267
- kind: external_exports.literal("ids"),
50268
- ids: external_exports.array(external_exports.number().int()).min(1)
50269
- }),
50270
- external_exports.object({
50271
- kind: external_exports.literal("types"),
50272
- types: external_exports.array(external_exports.enum(DeviceType)).min(1)
50273
- }),
50274
- external_exports.object({
50275
- kind: external_exports.literal("locations"),
50276
- locations: external_exports.array(external_exports.string().min(1)).min(1)
50277
- })
50278
- ]);
50279
- var DeviceTokenScopeSchema = external_exports.object({
50280
- type: external_exports.literal("device"),
50281
- /** The device SET this grant covers — resolved against the live fleet. */
50282
- selector: DeviceSelectorSchema,
50283
- access: external_exports.array(MethodAccessSchema).min(1),
50284
- /**
50285
- * Whether a grant on a PARENT device transparently covers its accessory
50286
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
50287
- * Direction is parent → children ONLY.
50288
- *
50289
- * Absent → the matcher DERIVES it from the access flavour: `view`
50290
- * inherits (a camera viewer sees the camera's accessories), `create` /
50291
- * `delete` do NOT (actuating/removing a child is an explicit act the
50292
- * operator must grant on the child, not inherit from the parent). Set it
50293
- * explicitly to override that default per grant.
50294
- */
50295
- includeLinked: external_exports.boolean().optional()
50296
- });
50297
- function migrateLegacyTokenScope(raw) {
50298
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
50299
- if (Reflect.get(raw, "type") !== "device") return raw;
50300
- if (Reflect.get(raw, "selector") !== void 0) return raw;
50301
- const targets = Reflect.get(raw, "targets");
50302
- if (!Array.isArray(targets)) return raw;
50303
- return {
50304
- type: "device",
50305
- selector: {
50306
- kind: "ids",
50307
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
50308
- },
50309
- access: Reflect.get(raw, "access")
50310
- };
50311
- }
50312
- var TokenScopeSchema = external_exports.preprocess(migrateLegacyTokenScope, external_exports.discriminatedUnion("type", [
50313
- external_exports.object({
50314
- type: external_exports.literal("category"),
50315
- target: CapScopeSchema,
50316
- access: external_exports.array(MethodAccessSchema).min(1)
50317
- }),
50318
- external_exports.object({
50319
- type: external_exports.literal("capability"),
50320
- target: external_exports.string(),
50321
- access: external_exports.array(MethodAccessSchema).min(1)
50322
- }),
50323
- external_exports.object({
50324
- type: external_exports.literal("addon"),
50325
- target: external_exports.string(),
50326
- access: external_exports.array(MethodAccessSchema).min(1)
50327
- }),
50328
- DeviceTokenScopeSchema
50329
- ]));
50330
- var UserRecordSchema = external_exports.object({
50331
- id: external_exports.string(),
50332
- username: external_exports.string(),
50333
- passwordHash: external_exports.string(),
50334
- /**
50335
- * Admin bypass. When true, the middleware skips the scope-access
50336
- * check entirely. There is no other axis of privilege; the legacy
50337
- * role enum collapsed onto this boolean in v2.
50338
- */
50339
- isAdmin: external_exports.boolean().default(false),
50340
- allowedProviders: AllowedProviderSchema,
50341
- allowedDevices: AllowedDevicesSchema,
50342
- /**
50343
- * Scopes granted to this user. Admins bypass; their `scopes` is
50344
- * ignored. Non-admins without scopes are locked out of every
50345
- * protected call.
50346
- */
50347
- scopes: external_exports.array(TokenScopeSchema).default([]),
50348
- createdAt: external_exports.number(),
50349
- updatedAt: external_exports.number()
50350
- });
50351
- var ApiKeyRecordSchema = external_exports.object({
50352
- id: external_exports.string(),
50353
- label: external_exports.string(),
50354
- isAdmin: external_exports.boolean().default(false),
50355
- allowedProviders: AllowedProviderSchema,
50356
- allowedDevices: AllowedDevicesSchema,
50357
- tokenHash: external_exports.string(),
50358
- tokenPrefix: external_exports.string(),
50359
- createdAt: external_exports.number(),
50360
- lastUsedAt: external_exports.number().optional()
50361
- });
50362
- var ScopedTokenSchema = external_exports.object({
50363
- id: external_exports.string(),
50364
- userId: external_exports.string(),
50365
- name: external_exports.string(),
50366
- tokenHash: external_exports.string(),
50367
- tokenPrefix: external_exports.string(),
50368
- scopes: external_exports.array(TokenScopeSchema),
50369
- expiresAt: external_exports.number().nullish(),
50370
- lastUsedAt: external_exports.number().nullish(),
50371
- createdAt: external_exports.number()
50372
- });
50373
50412
  var OauthIntegrationDescriptorSchema = external_exports.object({
50374
50413
  /** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
50375
50414
  integrationId: external_exports.string(),
@@ -57900,6 +57939,18 @@ var storageMigrationCapability = {
57900
57939
  */
57901
57940
  movers: method(external_exports.object({}), external_exports.array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }),
57902
57941
  /**
57942
+ * May a mover that touches these locations run NOW? One claim per physical
57943
+ * root is owned by the storage orchestrator: a single-location repoint holds
57944
+ * it while it carries its subtree, and a mover (`relocateMedia`,
57945
+ * `relocateFootage`, a migration move) asks here AFTER registering its job and
57946
+ * cancels itself on `clear: false`. The repoint, in turn, refuses while a
57947
+ * registered mover touches its root. Asked, never polled (D3).
57948
+ */
57949
+ moverClearance: method(external_exports.object({ locationIds: external_exports.array(external_exports.string()).readonly() }), external_exports.object({
57950
+ clear: external_exports.boolean(),
57951
+ repointLocationId: external_exports.string().optional()
57952
+ }), { auth: "admin" }),
57953
+ /**
57903
57954
  * What each class's SOURCE still holds, from the archive — never from the
57904
57955
  * resident index (D295). Only classes with something left (or something
57905
57956
  * unknown) are listed, so an empty list means there is nothing to drain and
@@ -62115,43 +62166,139 @@ var DishwasherStateSchema = external_exports.enum([
62115
62166
  "error",
62116
62167
  "unavailable"
62117
62168
  ]);
62169
+ var DishwasherOperationStateSchema = external_exports.enum([
62170
+ "Inactive",
62171
+ "Ready",
62172
+ "DelayedStart",
62173
+ "Run",
62174
+ "Pause",
62175
+ "ActionRequired",
62176
+ "Finished",
62177
+ "Error",
62178
+ "Aborting"
62179
+ ]);
62180
+ var DishwasherAvailabilitySchema = external_exports.enum([
62181
+ "available",
62182
+ "offline",
62183
+ "description-missing"
62184
+ ]);
62118
62185
  var DishwasherConsumableLevelSchema = external_exports.enum([
62119
62186
  "ok",
62120
62187
  "low",
62121
62188
  "empty"
62122
62189
  ]);
62123
62190
  var DishwasherStatusSchema = external_exports.object({
62191
+ availability: DishwasherAvailabilitySchema,
62192
+ /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
62124
62193
  state: DishwasherStateSchema,
62125
- /** Active program id/name, or null when none selected / unknown. */
62194
+ /** The appliance's operation state as reported; null when not known. */
62195
+ operationState: DishwasherOperationStateSchema.nullable(),
62196
+ /** Feature key of the ACTIVE (running) program, or null when none runs / not known. */
62126
62197
  program: external_exports.string().nullable(),
62127
- /** Program tokens the hardware accepts — drives the UI selector. */
62198
+ /** Feature key of the SELECTED program (what Start would run), or null when none / not known. */
62199
+ selectedProgram: external_exports.string().nullable(),
62200
+ /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
62128
62201
  availablePrograms: external_exports.array(external_exports.string()),
62129
- /** Minutes remaining on the active cycle, or null when not running. */
62130
- remainingMinutes: external_exports.number().int().nonnegative().nullable(),
62202
+ /** Seconds remaining on the active cycle, or null when not running / not known. */
62203
+ remainingSeconds: external_exports.number().int().nonnegative().nullable(),
62204
+ /** Seconds the delayed start is set to (time until the cycle starts), or null. */
62205
+ delayedStartSeconds: external_exports.number().int().nonnegative().nullable(),
62131
62206
  /** Cycle completion 0..100, or null when not applicable. */
62132
62207
  progressPercent: external_exports.number().min(0).max(100).nullable(),
62133
- /** True when the door is reported open. */
62134
- doorOpen: external_exports.boolean(),
62135
- /** True when remote start is allowed (door closed, power on, etc.). */
62136
- remoteStartAllowed: external_exports.boolean(),
62137
- /** Main power / switched-on state. */
62138
- powerOn: external_exports.boolean(),
62208
+ /** True when the door is reported open; null when the door state is not known. */
62209
+ doorOpen: external_exports.boolean().nullable(),
62210
+ /** True when the door is latched (locked) during a cycle; null when not reported. */
62211
+ doorLocked: external_exports.boolean().nullable(),
62212
+ /** True when remote start is allowed (door closed, power on, etc.); null when not known. */
62213
+ remoteStartAllowed: external_exports.boolean().nullable(),
62214
+ /** True when the appliance accepts remote control now; null when not known. */
62215
+ remoteControlActive: external_exports.boolean().nullable(),
62216
+ /** True while somebody operates the appliance at its panel; null when not reported. */
62217
+ localControlActive: external_exports.boolean().nullable(),
62218
+ /** Main power / switched-on state; null when not known. */
62219
+ powerOn: external_exports.boolean().nullable(),
62220
+ /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
62221
+ powerState: external_exports.string().nullable(),
62139
62222
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
62140
62223
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
62141
- halfLoad: external_exports.boolean(),
62142
- hygienePlus: external_exports.boolean(),
62224
+ /** Option states: null when the appliance does not expose the option or has not said. */
62225
+ halfLoad: external_exports.boolean().nullable(),
62226
+ hygienePlus: external_exports.boolean().nullable(),
62143
62227
  /** Extra dry option when the appliance exposes it. */
62144
- extraDry: external_exports.boolean(),
62228
+ extraDry: external_exports.boolean().nullable(),
62145
62229
  /** Intensive zone (lower basket) when the appliance exposes it. */
62146
- intensiveZone: external_exports.boolean(),
62230
+ intensiveZone: external_exports.boolean().nullable(),
62147
62231
  /** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
62148
- varioSpeedPlus: external_exports.boolean(),
62149
- /** Child lock (controls disabled on the appliance). */
62150
- childLock: external_exports.boolean(),
62232
+ varioSpeedPlus: external_exports.boolean().nullable(),
62233
+ /** Child lock (controls disabled on the appliance); null when not known. */
62234
+ childLock: external_exports.boolean().nullable(),
62235
+ /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
62236
+ activeEvents: external_exports.array(external_exports.string()),
62237
+ /** Numeric error code when the appliance reports one; null otherwise. */
62151
62238
  errorCode: external_exports.number().nullable(),
62239
+ /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
62152
62240
  errorLabel: external_exports.string().nullable(),
62241
+ /** Ms epoch of the last value received from the appliance; 0 = never. */
62153
62242
  lastFetchedAt: external_exports.number()
62154
62243
  });
62244
+ var DeviceIdSchema = external_exports.number().int().nonnegative();
62245
+ var DishwasherValueSchema = external_exports.union([
62246
+ external_exports.boolean(),
62247
+ external_exports.string(),
62248
+ external_exports.number()
62249
+ ]);
62250
+ var DishwasherAccessSchema = external_exports.enum([
62251
+ "none",
62252
+ "read",
62253
+ "readWrite",
62254
+ "writeOnly",
62255
+ "readStatic"
62256
+ ]);
62257
+ var DishwasherOptionDescriptorSchema = external_exports.object({
62258
+ /** Feature key (`Dishcare.Dishwasher.Option.ExtraDry`). */
62259
+ key: external_exports.string(),
62260
+ /** Last segment of the key, for display. */
62261
+ name: external_exports.string(),
62262
+ type: external_exports.enum([
62263
+ "boolean",
62264
+ "number",
62265
+ "enum",
62266
+ "string"
62267
+ ]),
62268
+ access: DishwasherAccessSchema,
62269
+ /** The appliance reports this entity as currently available. */
62270
+ available: external_exports.boolean(),
62271
+ /** Unit the description declares for a number; null when it declares none. */
62272
+ unit: external_exports.string().nullable(),
62273
+ min: external_exports.number().nullable(),
62274
+ max: external_exports.number().nullable(),
62275
+ step: external_exports.number().nullable(),
62276
+ /** Allowed members of an enum; null for other types. */
62277
+ enumValues: external_exports.array(external_exports.string()).nullable(),
62278
+ defaultValue: DishwasherValueSchema.nullable()
62279
+ });
62280
+ var DishwasherProgramSchema = external_exports.object({
62281
+ /** Feature key (`Dishcare.Dishwasher.Program.Eco50`) — what `setProgram` / `start` take. */
62282
+ key: external_exports.string(),
62283
+ name: external_exports.string(),
62284
+ /** The appliance currently offers this program. */
62285
+ available: external_exports.boolean(),
62286
+ /** The options this program lists, with the program-level access / availability / default. */
62287
+ options: external_exports.array(DishwasherOptionDescriptorSchema)
62288
+ });
62289
+ var DishwasherOptionsSchema = external_exports.object({
62290
+ availability: DishwasherAvailabilitySchema,
62291
+ /** What the appliance can be told to do, from the commands its description declares. */
62292
+ supportsStart: external_exports.boolean(),
62293
+ supportsPause: external_exports.boolean(),
62294
+ supportsResume: external_exports.boolean(),
62295
+ supportsStop: external_exports.boolean(),
62296
+ supportsPower: external_exports.boolean(),
62297
+ /** Every program the description lists, with its options. */
62298
+ programs: external_exports.array(DishwasherProgramSchema),
62299
+ /** Every option and setting entity the description lists (programs reference these by key). */
62300
+ options: external_exports.array(DishwasherOptionDescriptorSchema)
62301
+ });
62155
62302
  var dishwasherCapability = {
62156
62303
  name: "dishwasher",
62157
62304
  scope: "device",
@@ -62159,86 +62306,102 @@ var dishwasherCapability = {
62159
62306
  mode: "singleton",
62160
62307
  deviceTypes: [DeviceType.Dishwasher],
62161
62308
  methods: {
62162
- start: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
62309
+ /**
62310
+ * Start a program. Without `program` the selected one starts. `options` are
62311
+ * option feature keys; each is checked against the program's constraints.
62312
+ */
62313
+ start: method(external_exports.object({
62314
+ deviceId: DeviceIdSchema,
62315
+ program: external_exports.string().min(1).optional(),
62316
+ options: external_exports.record(external_exports.string(), DishwasherValueSchema).optional()
62317
+ }), external_exports.void(), {
62163
62318
  kind: "mutation",
62164
62319
  auth: "protected"
62165
62320
  }),
62166
- pause: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
62321
+ pause: method(external_exports.object({ deviceId: DeviceIdSchema }), external_exports.void(), {
62167
62322
  kind: "mutation",
62168
62323
  auth: "protected"
62169
62324
  }),
62170
- stop: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
62325
+ resume: method(external_exports.object({ deviceId: DeviceIdSchema }), external_exports.void(), {
62326
+ kind: "mutation",
62327
+ auth: "protected"
62328
+ }),
62329
+ stop: method(external_exports.object({ deviceId: DeviceIdSchema }), external_exports.void(), {
62171
62330
  kind: "mutation",
62172
62331
  auth: "protected"
62173
62332
  }),
62333
+ /** Select (not start) a program; refused when the appliance does not offer it. */
62174
62334
  setProgram: method(external_exports.object({
62175
- deviceId: external_exports.number().int().nonnegative(),
62335
+ deviceId: DeviceIdSchema,
62176
62336
  program: external_exports.string().min(1)
62177
62337
  }), external_exports.void(), {
62178
62338
  kind: "mutation",
62179
62339
  auth: "protected"
62180
62340
  }),
62181
62341
  setPower: method(external_exports.object({
62182
- deviceId: external_exports.number().int().nonnegative(),
62342
+ deviceId: DeviceIdSchema,
62183
62343
  on: external_exports.boolean()
62184
62344
  }), external_exports.void(), {
62185
62345
  kind: "mutation",
62186
62346
  auth: "protected"
62187
62347
  }),
62188
62348
  setHalfLoad: method(external_exports.object({
62189
- deviceId: external_exports.number().int().nonnegative(),
62349
+ deviceId: DeviceIdSchema,
62190
62350
  on: external_exports.boolean()
62191
62351
  }), external_exports.void(), {
62192
62352
  kind: "mutation",
62193
62353
  auth: "protected"
62194
62354
  }),
62195
62355
  setHygienePlus: method(external_exports.object({
62196
- deviceId: external_exports.number().int().nonnegative(),
62356
+ deviceId: DeviceIdSchema,
62197
62357
  on: external_exports.boolean()
62198
62358
  }), external_exports.void(), {
62199
62359
  kind: "mutation",
62200
62360
  auth: "protected"
62201
62361
  }),
62202
62362
  setExtraDry: method(external_exports.object({
62203
- deviceId: external_exports.number().int().nonnegative(),
62363
+ deviceId: DeviceIdSchema,
62204
62364
  on: external_exports.boolean()
62205
62365
  }), external_exports.void(), {
62206
62366
  kind: "mutation",
62207
62367
  auth: "protected"
62208
62368
  }),
62209
62369
  setIntensiveZone: method(external_exports.object({
62210
- deviceId: external_exports.number().int().nonnegative(),
62370
+ deviceId: DeviceIdSchema,
62211
62371
  on: external_exports.boolean()
62212
62372
  }), external_exports.void(), {
62213
62373
  kind: "mutation",
62214
62374
  auth: "protected"
62215
62375
  }),
62216
62376
  setVarioSpeedPlus: method(external_exports.object({
62217
- deviceId: external_exports.number().int().nonnegative(),
62377
+ deviceId: DeviceIdSchema,
62218
62378
  on: external_exports.boolean()
62219
62379
  }), external_exports.void(), {
62220
62380
  kind: "mutation",
62221
62381
  auth: "protected"
62222
62382
  }),
62223
62383
  setChildLock: method(external_exports.object({
62224
- deviceId: external_exports.number().int().nonnegative(),
62384
+ deviceId: DeviceIdSchema,
62225
62385
  on: external_exports.boolean()
62226
62386
  }), external_exports.void(), {
62227
62387
  kind: "mutation",
62228
62388
  auth: "protected"
62229
62389
  }),
62390
+ /**
62391
+ * Write any option or setting by feature key. Refused when the description does
62392
+ * not list it, it is not writable or not available now, or the value is outside
62393
+ * its declared range / members.
62394
+ */
62230
62395
  setOption: method(external_exports.object({
62231
- deviceId: external_exports.number().int().nonnegative(),
62396
+ deviceId: DeviceIdSchema,
62232
62397
  key: external_exports.string().min(1),
62233
- value: external_exports.union([
62234
- external_exports.boolean(),
62235
- external_exports.string(),
62236
- external_exports.number()
62237
- ])
62398
+ value: DishwasherValueSchema
62238
62399
  }), external_exports.void(), {
62239
62400
  kind: "mutation",
62240
62401
  auth: "protected"
62241
- })
62402
+ }),
62403
+ /** What this appliance can do, from its own description — the UI is derived from it. */
62404
+ getOptions: method(external_exports.object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
62242
62405
  },
62243
62406
  status: {
62244
62407
  schema: DishwasherStatusSchema,
@@ -74012,6 +74175,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
74012
74175
  addonId: null,
74013
74176
  access: "view"
74014
74177
  },
74178
+ "dishwasher.getOptions": {
74179
+ capName: "dishwasher",
74180
+ capScope: "device",
74181
+ addonId: null,
74182
+ access: "view"
74183
+ },
74015
74184
  "dishwasher.getStatus": {
74016
74185
  capName: "dishwasher",
74017
74186
  capScope: "device",
@@ -74024,6 +74193,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
74024
74193
  addonId: null,
74025
74194
  access: "create"
74026
74195
  },
74196
+ "dishwasher.resume": {
74197
+ capName: "dishwasher",
74198
+ capScope: "device",
74199
+ addonId: null,
74200
+ access: "create"
74201
+ },
74027
74202
  "dishwasher.setChildLock": {
74028
74203
  capName: "dishwasher",
74029
74204
  capScope: "device",
@@ -78524,6 +78699,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
78524
78699
  addonId: null,
78525
78700
  access: "view"
78526
78701
  },
78702
+ "storageMigration.moverClearance": {
78703
+ capName: "storage-migration",
78704
+ capScope: "system",
78705
+ addonId: null,
78706
+ access: "view"
78707
+ },
78527
78708
  "storageMigration.movers": {
78528
78709
  capName: "storage-migration",
78529
78710
  capScope: "system",
@@ -80767,6 +80948,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
80767
80948
  form: "single",
80768
80949
  optional: false
80769
80950
  }],
80951
+ "dishwasher.getOptions": [{
80952
+ name: "deviceId",
80953
+ form: "single",
80954
+ optional: false
80955
+ }],
80770
80956
  "dishwasher.getStatus": [{
80771
80957
  name: "deviceId",
80772
80958
  form: "single",
@@ -80777,6 +80963,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
80777
80963
  form: "single",
80778
80964
  optional: false
80779
80965
  }],
80966
+ "dishwasher.resume": [{
80967
+ name: "deviceId",
80968
+ form: "single",
80969
+ optional: false
80970
+ }],
80780
80971
  "dishwasher.setChildLock": [{
80781
80972
  name: "deviceId",
80782
80973
  form: "single",
@@ -83062,6 +83253,9 @@ var NC_SYSTEM_EVENT_FAMILY = {
83062
83253
  "wrapper-update-available": "system",
83063
83254
  "backup-completed": "system",
83064
83255
  "export-completed": "system",
83256
+ "alert-critical": "system",
83257
+ "alert-error": "system",
83258
+ "alert-critical-resolved": "system",
83065
83259
  "storage-location-low": "system",
83066
83260
  "storage-location-critical": "system",
83067
83261
  "storage-location-ok": "system",
@@ -83187,6 +83381,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
83187
83381
  "storage-location-ok"
83188
83382
  ]
83189
83383
  },
83384
+ {
83385
+ id: "alerts",
83386
+ family: "system",
83387
+ label: "Alerts",
83388
+ kinds: [
83389
+ "alert-critical",
83390
+ "alert-error",
83391
+ "alert-critical-resolved"
83392
+ ]
83393
+ },
83190
83394
  {
83191
83395
  id: "alarm",
83192
83396
  family: "alarm",
@@ -83208,6 +83412,21 @@ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.ent
83208
83412
  return [[descriptor.kind, field]];
83209
83413
  })));
83210
83414
  var NC_SENSOR_TRANSITION_KINDS = Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
83415
+ var AlertEpisodePayloadSchema = external_exports.object({
83416
+ phase: external_exports.enum(["raised", "resolved"]),
83417
+ episodeId: external_exports.string().min(1),
83418
+ alertId: external_exports.string().min(1),
83419
+ severity: AlertSeveritySchema,
83420
+ category: external_exports.string(),
83421
+ title: external_exports.string(),
83422
+ message: external_exports.string(),
83423
+ sourceType: external_exports.string().optional(),
83424
+ sourceId: external_exports.string().optional(),
83425
+ nodeId: external_exports.string().optional(),
83426
+ /** The alert's own action link (where the operator acts on it), when it has one. */
83427
+ actionUrl: external_exports.string().optional(),
83428
+ at: external_exports.number()
83429
+ });
83211
83430
  var RULE_BODY_ONLY = ["body"];
83212
83431
  var RULE_TEXT = ["title", "body"];
83213
83432
  var BATTERY = ["device-battery-low", "device-battery-normal"];
@@ -83217,6 +83436,11 @@ var TANK = [
83217
83436
  "device-tank-full",
83218
83437
  "device-tank-normal"
83219
83438
  ];
83439
+ var ALERT_EPISODE = [
83440
+ "alert-critical",
83441
+ "alert-error",
83442
+ "alert-critical-resolved"
83443
+ ];
83220
83444
  var UPDATES = [
83221
83445
  "addon-update-available",
83222
83446
  "server-update-available",
@@ -83738,7 +83962,8 @@ var NC_TEMPLATE_VARS = [
83738
83962
  "server-updated",
83739
83963
  "storage-location-low",
83740
83964
  "storage-location-critical",
83741
- "storage-location-ok"
83965
+ "storage-location-ok",
83966
+ ...ALERT_EPISODE
83742
83967
  ]
83743
83968
  },
83744
83969
  {
@@ -83760,7 +83985,8 @@ var NC_TEMPLATE_VARS = [
83760
83985
  "server-updated",
83761
83986
  "storage-location-low",
83762
83987
  "storage-location-critical",
83763
- "storage-location-ok"
83988
+ "storage-location-ok",
83989
+ ...ALERT_EPISODE
83764
83990
  ]
83765
83991
  },
83766
83992
  {
@@ -83827,7 +84053,7 @@ var NC_TEMPLATE_VARS = [
83827
84053
  families: ["rule"],
83828
84054
  fields: [...RULE_TEXT],
83829
84055
  kinds: ["system"],
83830
- systemEventKinds: ["recording-degraded"]
84056
+ systemEventKinds: ["recording-degraded", ...ALERT_EPISODE]
83831
84057
  },
83832
84058
  {
83833
84059
  name: "locationName",
@@ -84024,13 +84250,53 @@ var NC_TEMPLATE_VARS = [
84024
84250
  },
84025
84251
  {
84026
84252
  name: "title",
84027
- label: "Export title",
84253
+ label: "Export or alert title",
84028
84254
  example: "Ingresso 08:00",
84029
84255
  group: "system",
84030
84256
  families: ["rule"],
84031
84257
  fields: [...RULE_TEXT],
84032
84258
  kinds: ["system"],
84033
- systemEventKinds: ["export-completed"]
84259
+ systemEventKinds: ["export-completed", ...ALERT_EPISODE]
84260
+ },
84261
+ {
84262
+ name: "severity",
84263
+ label: "Alert severity",
84264
+ example: "critical",
84265
+ group: "system",
84266
+ families: ["rule"],
84267
+ fields: [...RULE_TEXT],
84268
+ kinds: ["system"],
84269
+ systemEventKinds: [...ALERT_EPISODE]
84270
+ },
84271
+ {
84272
+ name: "category",
84273
+ label: "Alert category",
84274
+ example: "addon.runner-failed",
84275
+ group: "system",
84276
+ families: ["rule"],
84277
+ fields: [...RULE_TEXT],
84278
+ kinds: ["system"],
84279
+ systemEventKinds: [...ALERT_EPISODE]
84280
+ },
84281
+ {
84282
+ name: "source",
84283
+ label: "Alert source (addon, node or runner)",
84284
+ example: "recorder",
84285
+ group: "system",
84286
+ families: ["rule"],
84287
+ fields: [...RULE_TEXT],
84288
+ kinds: ["system"],
84289
+ systemEventKinds: [...ALERT_EPISODE]
84290
+ },
84291
+ {
84292
+ name: "actionUrl",
84293
+ label: "Alert action link",
84294
+ example: "/system/addons",
84295
+ group: "system",
84296
+ families: ["rule"],
84297
+ fields: [...RULE_TEXT],
84298
+ kinds: ["system"],
84299
+ systemEventKinds: [...ALERT_EPISODE]
84034
84300
  },
84035
84301
  {
84036
84302
  name: "exportId",
@@ -85715,6 +85981,7 @@ import * as clack from "@clack/prompts";
85715
85981
  import {
85716
85982
  clearSession,
85717
85983
  defaultSession,
85984
+ jwtClaims,
85718
85985
  listSessions,
85719
85986
  loadSession,
85720
85987
  saveSession
@@ -85730,20 +85997,11 @@ import {
85730
85997
  validateHubAddress
85731
85998
  } from "@camstack/develop";
85732
85999
  import {
85733
- discoverHubs,
85734
- normalizeHubUrl
86000
+ discoverHubs
85735
86001
  } from "@camstack/develop/hub-discovery";
85736
- var ADMIN_TOKEN_SUFFIX = ".admin-token";
85737
86002
  function validateManualHubUrl(value) {
85738
86003
  return validateHubAddress(value);
85739
86004
  }
85740
- function hubUrlFromAdminTokenFile(fileName) {
85741
- if (!fileName.endsWith(ADMIN_TOKEN_SUFFIX)) return null;
85742
- const slug = fileName.slice(0, -ADMIN_TOKEN_SUFFIX.length);
85743
- const match = /^(.+?)_(\d{1,5})$/.exec(slug);
85744
- const candidate = match ? `https://${match[1]}:${match[2]}` : `https://${slug}`;
85745
- return normalizeHubUrl(candidate)?.url ?? null;
85746
- }
85747
86005
  function knownHubCandidates(deps) {
85748
86006
  const dir = deps.sessionDir ?? defaultSessionDir();
85749
86007
  const env = deps.env ?? process.env;
@@ -85766,9 +86024,6 @@ function knownHubCandidates(deps) {
85766
86024
  }
85767
86025
  } catch {
85768
86026
  }
85769
- } else {
85770
- const url2 = hubUrlFromAdminTokenFile(file2);
85771
- if (url2 !== null) out.push({ url: url2, source: "saved admin token" });
85772
86027
  }
85773
86028
  }
85774
86029
  const fromEnv = resolveHubUrl({ env, session: null });
@@ -85896,30 +86151,32 @@ function isLoginPayload(value) {
85896
86151
  const u = v.user;
85897
86152
  return typeof u.id === "string" && typeof u.username === "string";
85898
86153
  }
85899
- function isLogoutPayload(value) {
85900
- if (value === null || typeof value !== "object") return false;
85901
- return Reflect.get(value, "success") === true && typeof Reflect.get(value, "revoked") === "number";
85902
- }
85903
- var SCOPED_TOKEN_PREFIX = "cst_";
85904
- var ADMIN_TOKEN_PREFIX = "csa_";
85905
- async function revokePriorSession(server, prior, authJwt) {
85906
- if (prior.token.startsWith(SCOPED_TOKEN_PREFIX) && prior.tokenId) {
85907
- await callTrpcMutation(
85908
- `${server}/trpc/userManagement.revokeScopedToken?batch=1`,
85909
- `Bearer ${authJwt}`,
85910
- { id: prior.tokenId },
85911
- isUnknown
85912
- );
85913
- return;
86154
+ function revocationFor(session) {
86155
+ if (session.tokenId) {
86156
+ return {
86157
+ procedure: "userManagement.revokeScopedToken",
86158
+ input: { id: session.tokenId },
86159
+ bearer: "fresh"
86160
+ };
85914
86161
  }
85915
- if (!prior.token.startsWith(SCOPED_TOKEN_PREFIX) && !prior.token.startsWith(ADMIN_TOKEN_PREFIX)) {
85916
- await callTrpcMutation(
85917
- `${server}/trpc/auth.logout?batch=1`,
85918
- `Bearer ${prior.token}`,
85919
- {},
85920
- isLogoutPayload
85921
- );
86162
+ if (jwtClaims(session.token) !== null) {
86163
+ return { procedure: "auth.logout", input: {}, bearer: "self" };
85922
86164
  }
86165
+ return null;
86166
+ }
86167
+ async function performRevocation(server, session, freshJwt) {
86168
+ const r = revocationFor(session);
86169
+ if (!r) return;
86170
+ const bearer = r.bearer === "fresh" && freshJwt ? freshJwt : session.token;
86171
+ await callTrpcMutation(
86172
+ `${server}/trpc/${r.procedure}?batch=1`,
86173
+ `Bearer ${bearer}`,
86174
+ r.input,
86175
+ isUnknown
86176
+ );
86177
+ }
86178
+ async function revokePriorSession(server, prior, authJwt) {
86179
+ await performRevocation(server, prior, authJwt);
85923
86180
  }
85924
86181
  async function completeSecondFactor(server, login) {
85925
86182
  const factors = login.secondFactors ?? [];
@@ -85936,7 +86193,7 @@ async function completeSecondFactor(server, login) {
85936
86193
  return await callTrpcMutation(
85937
86194
  `${server}/trpc/auth.loginVerifyTotp?batch=1`,
85938
86195
  void 0,
85939
- { challengeToken: login.token, code },
86196
+ { challengeToken: login.token, code, refreshable: true },
85940
86197
  isLoginPayload
85941
86198
  );
85942
86199
  } catch (err) {
@@ -86029,6 +86286,8 @@ async function loginCommand(opts) {
86029
86286
  let displayName;
86030
86287
  let userId;
86031
86288
  let isAdmin = false;
86289
+ let refreshToken;
86290
+ let expiresAt;
86032
86291
  while (true) {
86033
86292
  const authSpinner = clack.spinner();
86034
86293
  authSpinner.start(
@@ -86038,7 +86297,7 @@ async function loginCommand(opts) {
86038
86297
  const first = await callTrpcMutation(
86039
86298
  `${server}/trpc/auth.login?batch=1`,
86040
86299
  void 0,
86041
- { username, password: password2 },
86300
+ { username, password: password2, refreshable: true },
86042
86301
  isLoginPayload
86043
86302
  );
86044
86303
  const owesSecondFactor = (first.secondFactors ?? []).length > 0;
@@ -86048,6 +86307,8 @@ async function loginCommand(opts) {
86048
86307
  displayName = login.user.username;
86049
86308
  userId = login.user.id;
86050
86309
  isAdmin = login.user.isAdmin === true;
86310
+ refreshToken = login.refreshToken;
86311
+ expiresAt = login.expiresAt;
86051
86312
  if (!owesSecondFactor) authSpinner.stop(`Authenticated as ${displayName}`);
86052
86313
  break;
86053
86314
  } catch (err) {
@@ -86079,14 +86340,14 @@ async function loginCommand(opts) {
86079
86340
  token: jwt2,
86080
86341
  scopes: [],
86081
86342
  createdAt: Date.now(),
86082
- ...isAdmin ? { isAdmin: true } : {}
86343
+ ...isAdmin ? { isAdmin: true } : {},
86344
+ ...refreshToken ? { refreshToken } : {},
86345
+ ...expiresAt !== void 0 ? { expiresAt } : {}
86083
86346
  };
86084
86347
  const file2 = saveSession(session);
86085
86348
  const role = isAdmin ? "administrator" : "user";
86086
- clack.outro(
86087
- `\u2713 Logged in as ${displayName} (${role}) \u2192 ${server}
86088
- Cached at ${file2}`
86089
- );
86349
+ clack.outro(`\u2713 Logged in as ${displayName} (${role}) \u2192 ${server}
86350
+ Cached at ${file2}`);
86090
86351
  }
86091
86352
  async function logoutCommand(opts) {
86092
86353
  const session = loadSession(opts.server);
@@ -86098,21 +86359,7 @@ async function logoutCommand(opts) {
86098
86359
  const spinner3 = clack.spinner();
86099
86360
  spinner3.start(`Revoking token on ${session.server}`);
86100
86361
  try {
86101
- if (session.token.startsWith(SCOPED_TOKEN_PREFIX) && session.tokenId) {
86102
- await callTrpcMutation(
86103
- `${session.server}/trpc/userManagement.revokeScopedToken?batch=1`,
86104
- `Bearer ${session.token}`,
86105
- { id: session.tokenId },
86106
- isUnknown
86107
- );
86108
- } else if (!session.token.startsWith(SCOPED_TOKEN_PREFIX) && !session.token.startsWith(ADMIN_TOKEN_PREFIX)) {
86109
- await callTrpcMutation(
86110
- `${session.server}/trpc/auth.logout?batch=1`,
86111
- `Bearer ${session.token}`,
86112
- {},
86113
- isLogoutPayload
86114
- );
86115
- }
86362
+ await performRevocation(session.server, session, void 0);
86116
86363
  spinner3.stop("Session revoked server-side.");
86117
86364
  } catch (err) {
86118
86365
  spinner3.stop(
@@ -86158,7 +86405,15 @@ async function whoamiCommand(opts) {
86158
86405
  if (session.tokenId) console.log(` tokenId: ${session.tokenId}`);
86159
86406
  const scopeLine = session.scopes.length > 0 ? session.scopes.map((s) => `${s.type}:${s.target}`).join(", ") : "(login JWT)";
86160
86407
  console.log(` scopes: ${scopeLine}`);
86161
- console.log(` admin: ${session.isAdmin === true ? "yes" : "no"}`);
86408
+ const claims = jwtClaims(session.token);
86409
+ const admin = session.isAdmin === true || claims?.["isAdmin"] === true;
86410
+ console.log(` admin: ${admin ? "yes" : "no"}`);
86411
+ const expiresAt = session.expiresAt ?? (typeof claims?.["exp"] === "number" ? claims["exp"] * 1e3 : void 0);
86412
+ if (expiresAt !== void 0) {
86413
+ console.log(
86414
+ ` expires: ${new Date(expiresAt).toISOString()}${session.refreshToken ? " (renewed automatically)" : ""}`
86415
+ );
86416
+ }
86162
86417
  console.log(` createdAt: ${new Date(session.createdAt).toISOString()}`);
86163
86418
  console.log("");
86164
86419
  console.log(`[camstack] Pinging ${session.server}/api/auth/whoami\u2026`);
@@ -86203,7 +86458,7 @@ import {
86203
86458
  MissingCredentialsError,
86204
86459
  normalizeHubAddress as normalizeHubAddress3,
86205
86460
  touchSession as touchSession3,
86206
- resolveCredentials as resolveCredentials3
86461
+ resolveFreshCredentials as resolveFreshCredentials3
86207
86462
  } from "@camstack/develop";
86208
86463
 
86209
86464
  // src/commands/discover.ts
@@ -86450,7 +86705,20 @@ import * as fs7 from "fs";
86450
86705
  import * as path7 from "path";
86451
86706
  import { parseArgs as parseArgs6 } from "util";
86452
86707
  import * as clack3 from "@clack/prompts";
86453
- import { loadSession as loadSession2, resolveCredentials, touchSession } from "@camstack/develop";
86708
+ import { loadSession as loadSession2, resolveFreshCredentials, touchSession } from "@camstack/develop";
86709
+
86710
+ // src/http/hub-transport.ts
86711
+ var cliHubTransport = async (req) => {
86712
+ const res = await hubRequest(req.url, {
86713
+ method: req.method,
86714
+ headers: req.headers,
86715
+ ...req.body !== void 0 ? { body: req.body } : {},
86716
+ timeoutMs: req.timeoutMs
86717
+ });
86718
+ return { status: res.status, text: await res.text() };
86719
+ };
86720
+
86721
+ // src/commands/dev.ts
86454
86722
  var DEFAULT_DEBOUNCE_MS = 500;
86455
86723
  var DEFAULT_WATCH_SUBDIR = "src";
86456
86724
  function resolveAddonDir(arg) {
@@ -86482,12 +86750,12 @@ function watchRecursive(target, onEvent) {
86482
86750
  return { close: () => w.close(), recursive: false };
86483
86751
  }
86484
86752
  }
86485
- async function pushOnce(addonPath, opts, label) {
86753
+ async function pushOnce(addonPath, getOpts, label) {
86486
86754
  const start = Date.now();
86487
86755
  console.log(`
86488
86756
  [camstack dev] \u2192 ${label}\u2026`);
86489
86757
  try {
86490
- await deployAddon(addonPath, opts);
86758
+ await deployAddon(addonPath, await getOpts());
86491
86759
  const took = ((Date.now() - start) / 1e3).toFixed(1);
86492
86760
  console.log(`[camstack dev] \u2713 Synced in ${took}s`);
86493
86761
  } catch (err) {
@@ -86516,12 +86784,14 @@ async function runDev(args) {
86516
86784
  });
86517
86785
  const addonArg = positionals[0] ?? ".";
86518
86786
  const addonDir = resolveAddonDir(addonArg);
86519
- const creds = resolveCredentials({
86787
+ const credentialInputs = {
86520
86788
  ...typeof values.server === "string" ? { server: values.server } : {},
86521
86789
  ...typeof values.token === "string" ? { token: values.token } : {},
86522
86790
  ...typeof values["token-file"] === "string" ? { tokenFile: values["token-file"] } : {},
86523
- loadSession: loadSession2
86524
- });
86791
+ loadSession: loadSession2,
86792
+ transport: cliHubTransport
86793
+ };
86794
+ const creds = await resolveFreshCredentials(credentialInputs);
86525
86795
  if (creds.warning) console.error(`[camstack dev] Warning: ${creds.warning}`);
86526
86796
  if (creds.source === "session" && creds.server) touchSession(creds.server);
86527
86797
  const serverUrl = creds.server;
@@ -86540,6 +86810,10 @@ async function runDev(args) {
86540
86810
  buildMode: values["no-build"] === true ? "skip" : "auto",
86541
86811
  ...typeof values["build-script"] === "string" ? { buildScript: values["build-script"] } : {}
86542
86812
  };
86813
+ const freshOpts = async () => ({
86814
+ ...deployOpts,
86815
+ token: (await resolveFreshCredentials(credentialInputs)).token
86816
+ });
86543
86817
  const watchOverride = typeof values["watch-path"] === "string" ? values["watch-path"] : void 0;
86544
86818
  const watchTarget = watchOverride ? path7.resolve(addonDir, watchOverride) : fs7.existsSync(path7.join(addonDir, DEFAULT_WATCH_SUBDIR)) ? path7.join(addonDir, DEFAULT_WATCH_SUBDIR) : addonDir;
86545
86819
  if (!fs7.existsSync(watchTarget)) {
@@ -86553,7 +86827,7 @@ async function runDev(args) {
86553
86827
  );
86554
86828
  clack3.log.info(`Debounce: ${debounceMs}ms`);
86555
86829
  if (values["no-initial-push"] !== true) {
86556
- await pushOnce(addonDir, deployOpts, "Initial sync");
86830
+ await pushOnce(addonDir, freshOpts, "Initial sync");
86557
86831
  } else {
86558
86832
  console.log("[camstack dev] Skipping initial push (--no-initial-push). Waiting for changes\u2026");
86559
86833
  }
@@ -86569,7 +86843,7 @@ async function runDev(args) {
86569
86843
  }
86570
86844
  pushing = true;
86571
86845
  try {
86572
- await pushOnce(addonDir, deployOpts, "File change detected \u2014 rebuilding");
86846
+ await pushOnce(addonDir, freshOpts, "File change detected \u2014 rebuilding");
86573
86847
  } finally {
86574
86848
  pushing = false;
86575
86849
  if (pending) {
@@ -86605,7 +86879,7 @@ import * as clack4 from "@clack/prompts";
86605
86879
  import {
86606
86880
  loadSession as loadSession3,
86607
86881
  touchSession as touchSession2,
86608
- resolveCredentials as resolveCredentials2,
86882
+ resolveFreshCredentials as resolveFreshCredentials2,
86609
86883
  withAuthHint as withAuthHint3
86610
86884
  } from "@camstack/develop";
86611
86885
  var POLL_INTERVAL_MS2 = 2e3;
@@ -86669,12 +86943,14 @@ async function runWatch(args) {
86669
86943
  allowPositionals: true
86670
86944
  });
86671
86945
  const addonArg = positionals[0] ?? ".";
86672
- const creds = resolveCredentials2({
86946
+ const credentialInputs = {
86673
86947
  ...typeof values.server === "string" ? { server: values.server } : {},
86674
86948
  ...typeof values.token === "string" ? { token: values.token } : {},
86675
86949
  ...typeof values["token-file"] === "string" ? { tokenFile: values["token-file"] } : {},
86676
- loadSession: loadSession3
86677
- });
86950
+ loadSession: loadSession3,
86951
+ transport: cliHubTransport
86952
+ };
86953
+ const creds = await resolveFreshCredentials2(credentialInputs);
86678
86954
  if (creds.warning) console.error(`[camstack watch] Warning: ${creds.warning}`);
86679
86955
  if (creds.source === "session" && creds.server) touchSession2(creds.server);
86680
86956
  const serverUrl = creds.server;
@@ -86713,7 +86989,14 @@ async function runWatch(args) {
86713
86989
  process.on("SIGTERM", shutdown);
86714
86990
  while (!stop) {
86715
86991
  try {
86716
- const entries2 = await fetchLogs(serverUrl, token, addonId, LOG_BATCH_LIMIT, creds.source);
86992
+ const fresh = await resolveFreshCredentials2(credentialInputs);
86993
+ const entries2 = await fetchLogs(
86994
+ serverUrl,
86995
+ fresh.token,
86996
+ addonId,
86997
+ LOG_BATCH_LIMIT,
86998
+ creds.source
86999
+ );
86717
87000
  const sorted = [...entries2].toSorted((a, b) => {
86718
87001
  const ta = typeof a.timestamp === "number" ? a.timestamp : new Date(a.timestamp).getTime();
86719
87002
  const tb = typeof b.timestamp === "number" ? b.timestamp : new Date(b.timestamp).getTime();
@@ -87185,7 +87468,7 @@ function buildCommands() {
87185
87468
  " -p, --password <pwd> Password (skips prompt \u2014 prefer letting the CLI prompt)",
87186
87469
  ` --token-name <name> Ignored (kept for scripts); login uses your hub session JWT`,
87187
87470
  " --admin Ignored (kept for muscle memory); admin follows your account role",
87188
- " --admin-days <n> Ignored (kept for muscle memory); no separate admin-token mint"
87471
+ " --admin-days <n> Ignored (kept for muscle memory); one login, one token format"
87189
87472
  ].join("\n")
87190
87473
  },
87191
87474
  {
@@ -87196,7 +87479,7 @@ function buildCommands() {
87196
87479
  "Usage: camstack discover [options]",
87197
87480
  "",
87198
87481
  "Lists the hubs `camstack login` would offer: those announcing _camstack._tcp over",
87199
- " mDNS plus saved sessions, admin-token files, $CAMSTACK_HUB_URL and scratchpad/.hub-url,",
87482
+ " mDNS plus saved sessions, $CAMSTACK_HUB_URL and scratchpad/.hub-url,",
87200
87483
  " reachable ones only, each with its source. Agents never announce this service.",
87201
87484
  "",
87202
87485
  "Options:",
@@ -87473,7 +87756,7 @@ async function runWhoami(args) {
87473
87756
  const server = stringOpt(parsed.values, "server");
87474
87757
  await whoamiCommand(server ? { server: normalizeHubAddress3(server) } : {});
87475
87758
  }
87476
- function resolveClosureDeployOpts(values) {
87759
+ async function resolveClosureDeployOpts(values) {
87477
87760
  const nodeId = stringOpt(values, "node");
87478
87761
  const cluster = boolOpt(values, "cluster") || boolOpt(values, "all");
87479
87762
  if (cluster && nodeId) {
@@ -87481,7 +87764,7 @@ function resolveClosureDeployOpts(values) {
87481
87764
  process.exit(1);
87482
87765
  }
87483
87766
  const version2 = stringOpt(values, "version");
87484
- const creds = resolveCommandCredentials(values);
87767
+ const creds = await resolveCommandCredentials(values);
87485
87768
  return {
87486
87769
  serverUrl: creds.server ?? DEFAULT_SERVER_URL,
87487
87770
  token: creds.token,
@@ -87519,7 +87802,7 @@ async function runDeploy(args) {
87519
87802
  console.error("[camstack] Error: --closure does not take addon --build / --no-build flags.");
87520
87803
  process.exit(1);
87521
87804
  }
87522
- await deployServer(resolveClosureDeployOpts(parsed.values));
87805
+ await deployServer(await resolveClosureDeployOpts(parsed.values));
87523
87806
  return;
87524
87807
  }
87525
87808
  if (stringOpt(parsed.values, "version")) {
@@ -87527,7 +87810,7 @@ async function runDeploy(args) {
87527
87810
  process.exit(1);
87528
87811
  }
87529
87812
  const addonPath = parsed.positionals[0] ?? ".";
87530
- const creds = resolveCommandCredentials(parsed.values);
87813
+ const creds = await resolveCommandCredentials(parsed.values);
87531
87814
  const server = creds.server ?? DEFAULT_SERVER_URL;
87532
87815
  const token = creds.token;
87533
87816
  const nodeId = stringOpt(parsed.values, "node");
@@ -87579,19 +87862,20 @@ async function runDeployServer(args) {
87579
87862
  return;
87580
87863
  }
87581
87864
  console.log("[camstack] note: `deploy-server` is an alias of `deploy --closure`");
87582
- await deployServer(resolveClosureDeployOpts(parsed.values));
87865
+ await deployServer(await resolveClosureDeployOpts(parsed.values));
87583
87866
  }
87584
87867
  var DEFAULT_SERVER_URL = "https://localhost:4443";
87585
- function resolveCommandCredentials(values) {
87868
+ async function resolveCommandCredentials(values) {
87586
87869
  const server = stringOpt(values, "server");
87587
87870
  const token = stringOpt(values, "token");
87588
87871
  const tokenFile = stringOpt(values, "token-file");
87589
87872
  try {
87590
- const creds = resolveCredentials3({
87873
+ const creds = await resolveFreshCredentials3({
87591
87874
  ...server ? { server } : {},
87592
87875
  ...token ? { token } : {},
87593
87876
  ...tokenFile ? { tokenFile } : {},
87594
- loadSession: loadSession4
87877
+ loadSession: loadSession4,
87878
+ transport: cliHubTransport
87595
87879
  });
87596
87880
  if (creds.warning) console.error(`[camstack] Warning: ${creds.warning}`);
87597
87881
  if (creds.source === "session" && creds.server) touchSession3(creds.server);