@camstack/addon-google-services 0.2.41 → 0.2.43

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.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  let node_crypto = require("node:crypto");
6
- //#region ../types/dist/event-category-BmPm98n0.mjs
6
+ //#region ../types/dist/event-category-78tcBemo.mjs
7
7
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
8
8
  EventCategory["SystemBoot"] = "system.boot";
9
9
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -124,6 +124,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
124
124
  */
125
125
  EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
126
126
  /**
127
+ * An addon is RUNNING although its installed package is below what the
128
+ * running server requires (`camstack.requires.addons` of the server, or the
129
+ * addon's own `requires.server`). Raised once per streak by the unsupported-
130
+ * addon tracker, on the hub and on agents; AlertCenter turns it into one
131
+ * operator alert per node and package. The addon is never held back for it.
132
+ *
133
+ * Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
134
+ */
135
+ EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
136
+ /**
137
+ * The package of an {@link AddonUnsupportedVersion} alert reached a supported
138
+ * version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
139
+ *
140
+ * Payload: `{ packageName, addonId, nodeId }`.
141
+ */
142
+ EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
143
+ /**
127
144
  * Monitor scheduled the next retry for a failed addon. Transient —
128
145
  * surfaced to the UI for live-updating the "next retry in Ns"
129
146
  * countdown on the Addons page row, NOT persisted as an alert.
@@ -234,7 +251,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
234
251
  EventCategory["ProcessRestarted"] = "process.restarted";
235
252
  /**
236
253
  * The SET of storage locations changed — one was created, edited, enabled,
237
- * disabled or deleted through `storage.upsertLocation` / `deleteLocation`.
254
+ * disabled or deleted through the location instances or the forced-delete action.
238
255
  *
239
256
  * Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
240
257
  * it must also converge on its own periodic path, because a dropped event
@@ -5985,7 +6002,7 @@ var ZodIssueCode = {
5985
6002
  var ZodFirstPartyTypeKind;
5986
6003
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5987
6004
  //#endregion
5988
- //#region ../types/dist/sleep-CdKka2ri.mjs
6005
+ //#region ../types/dist/sleep-AEnTN3b4.mjs
5989
6006
  /**
5990
6007
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5991
6008
  * window to float samples (D455).
@@ -6694,10 +6711,33 @@ function integrationRowsApiOver(api) {
6694
6711
  };
6695
6712
  }
6696
6713
  var PREFIX = "records/";
6714
+ /**
6715
+ * Cross-realm brand. An addon bundles its own copy of this module (D15) while
6716
+ * `ctx.records` is the HOST's door, so an error thrown by the host is an
6717
+ * instance of the host's classes, not of the addon's. The brand plus the code
6718
+ * make `instanceof` answer by identity of the failure, not of the copy.
6719
+ */
6720
+ var RECORDS_ERROR_BRAND = Symbol.for("camstack.records-error");
6721
+ function carriesBrand(value) {
6722
+ return typeof value === "object" && value !== null && Reflect.get(value, RECORDS_ERROR_BRAND) === true;
6723
+ }
6697
6724
  /** The base of every records failure. `detail` is the human part of the message. */
6698
6725
  var RecordsError = class extends Error {
6726
+ /** The code a subclass stands for; the base stands for every code. */
6727
+ static code = void 0;
6728
+ /**
6729
+ * `instanceof` across copies of this module: any branded error is a
6730
+ * `RecordsError`, and is an instance of a subclass only when its code is the
6731
+ * subclass's code. Same-copy answers are unchanged.
6732
+ */
6733
+ static [Symbol.hasInstance](value) {
6734
+ if (Function.prototype[Symbol.hasInstance].call(this, value)) return true;
6735
+ if (!carriesBrand(value)) return false;
6736
+ return this.code === void 0 || value.code === this.code;
6737
+ }
6699
6738
  code;
6700
6739
  detail;
6740
+ [RECORDS_ERROR_BRAND] = true;
6701
6741
  constructor(code, detail) {
6702
6742
  super(`${PREFIX}${code}: ${detail}`);
6703
6743
  this.name = "RecordsError";
@@ -6706,22 +6746,25 @@ var RecordsError = class extends Error {
6706
6746
  }
6707
6747
  };
6708
6748
  /** The keychain could not store or serve a value: explicit, never an empty string. */
6709
- var RecordSecretUnavailableError = class extends RecordsError {
6749
+ var RecordSecretUnavailableError = class RecordSecretUnavailableError extends RecordsError {
6750
+ static code = "secret-unavailable";
6710
6751
  constructor(detail) {
6711
- super("secret-unavailable", detail);
6752
+ super(RecordSecretUnavailableError.code, detail);
6712
6753
  this.name = "RecordSecretUnavailableError";
6713
6754
  }
6714
6755
  };
6715
- var InvalidRecordError = class extends RecordsError {
6756
+ var InvalidRecordError = class InvalidRecordError extends RecordsError {
6757
+ static code = "invalid-record";
6716
6758
  constructor(detail) {
6717
- super("invalid-record", detail);
6759
+ super(InvalidRecordError.code, detail);
6718
6760
  this.name = "InvalidRecordError";
6719
6761
  }
6720
6762
  };
6721
6763
  /** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
6722
- var DeclarationRefusedError = class extends RecordsError {
6764
+ var DeclarationRefusedError = class DeclarationRefusedError extends RecordsError {
6765
+ static code = "declaration-refused";
6723
6766
  constructor(detail) {
6724
- super("declaration-refused", detail);
6767
+ super(DeclarationRefusedError.code, detail);
6725
6768
  this.name = "DeclarationRefusedError";
6726
6769
  }
6727
6770
  };
@@ -7208,17 +7251,35 @@ var RecordIntegrationInstances = class {
7208
7251
  ...kind !== void 0 ? { kind } : {}
7209
7252
  });
7210
7253
  if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
7254
+ const initial = this.hooks.initialBody(name, kind);
7255
+ const patch = this.patchOf(values ?? {});
7256
+ if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
7257
+ kind,
7258
+ body: {
7259
+ ...initial,
7260
+ ...patch
7261
+ }
7262
+ }));
7211
7263
  await this.handle.put({
7212
7264
  id: minted,
7213
- data: this.hooks.initialBody(name, kind)
7265
+ data: initial
7214
7266
  }, { ifRev: 0 });
7215
- const patch = this.patchOf(values ?? {});
7216
7267
  if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
7217
7268
  await this.hooks.onChanged?.(minted, "created");
7218
7269
  return { id: minted };
7219
7270
  };
7220
7271
  updateInstance = async ({ id, name, values }) => {
7221
7272
  const patch = this.patchOf(values);
7273
+ if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
7274
+ const stored = await this.handle.get(id);
7275
+ if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
7276
+ kind: void 0,
7277
+ body: {
7278
+ ...stored.data,
7279
+ ...patch
7280
+ }
7281
+ }));
7282
+ }
7222
7283
  if (name !== void 0) patch[this.nameField] = name;
7223
7284
  await this.handle.patch(id, patch);
7224
7285
  await this.hooks.onChanged?.(id, "updated");
@@ -10560,7 +10621,6 @@ var CAP_NAMES = Object.freeze([
10560
10621
  "notifier",
10561
10622
  "numeric-sensor",
10562
10623
  "oauth-integration",
10563
- "oidc-connections",
10564
10624
  "osd",
10565
10625
  "osd-manager",
10566
10626
  "pet-feeder",
@@ -12452,8 +12512,7 @@ Object.freeze({
12452
12512
  "device-provider": { "reloadDevice": 18e4 },
12453
12513
  "llm": {
12454
12514
  "generate": 6e5,
12455
- "generateVision": 6e5,
12456
- "testProfile": 6e5
12515
+ "generateVision": 6e5
12457
12516
  },
12458
12517
  "llm-runtime": {
12459
12518
  "complete": 6e5,
@@ -14078,17 +14137,18 @@ function composeInstanceKinds(parts, base) {
14078
14137
  * message to what the kind supports — callers never special-case a service.
14079
14138
  *
14080
14139
  * DESIGN DECISIONS (locked):
14081
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
14082
- * `setTargetEnabled`), each provider persisting via the `settings-store`
14083
- * cap. Rationale: the admin UI needs one uniform surface across the
14084
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
14085
- * alternative would fork the UI per addon and cannot host the
14086
- * discovery→adopt flow.
14087
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
14088
- * the generated cap-mount auto-`concatCollection`-fans them across every
14089
- * registered provider (notifiers addon + HA addon) so one catalog is
14090
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
14091
- * `addonId` the generated collection router extracts from the call input.
14140
+ * - A target is an INSTANCE of this function cap (epic #440, step 6): the
14141
+ * providers serve their targets as `integration-instances` records, one
14142
+ * record kind per target kind, and the admin lists, adds, edits, switches
14143
+ * and deletes them through that cap. This cap keeps what an instance cannot
14144
+ * say: the kind catalogue (`listTargetKinds`: capabilities, delivery
14145
+ * parameters, brand icon), dispatch (`send`, `testTarget`) and the form
14146
+ * helpers (`discoverTargets`, `probeTargetConfig`).
14147
+ * - `listTargetKinds` / `discoverTargets` return arrays → the generated
14148
+ * cap-mount auto-`concatCollection`-fans them across every registered
14149
+ * provider so one catalog is routable. `send` / `testTarget` /
14150
+ * `probeTargetConfig` route to ONE provider by the `addonId` the generated
14151
+ * collection router extracts from the call input.
14092
14152
  * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
14093
14153
  * transformer) + UDS MsgPack both round-trip typed arrays — already used by
14094
14154
  * `storage` / `storage-provider` / `recording` caps over the same path. No
@@ -14333,7 +14393,7 @@ var TargetKindCapsSchema = object({
14333
14393
  * the union is large and not meant for runtime validation here; the exported
14334
14394
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
14335
14395
  */
14336
- var ConfigSchemaPassthrough$4 = unknown();
14396
+ var ConfigSchemaPassthrough$3 = unknown();
14337
14397
  var TargetKindSchema = object({
14338
14398
  kind: string(),
14339
14399
  label: string(),
@@ -14370,7 +14430,7 @@ var TargetKindSchema = object({
14370
14430
  * its raster path, which is the safe default for an unknown image.
14371
14431
  */
14372
14432
  iconMediaType: string().optional(),
14373
- configSchema: ConfigSchemaPassthrough$4,
14433
+ configSchema: ConfigSchemaPassthrough$3,
14374
14434
  /**
14375
14435
  * The config keys that decide WHERE this kind's secrets are sent (server,
14376
14436
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -14392,9 +14452,11 @@ var TargetKindSchema = object({
14392
14452
  caps: TargetKindCapsSchema
14393
14453
  });
14394
14454
  /**
14395
- * A persisted target. `config` holds secrets; providers REDACT secret fields
14396
- * (return a presence marker only) when serving `listTargets` — never
14397
- * round-trip a stored secret to the UI.
14455
+ * A target as a provider's own code sees it (the notification-center bridge, the
14456
+ * send path): the flat record body split into the name, the switch and the
14457
+ * kind's fields. `config` holds secrets; a reader that serves it to a client
14458
+ * REDACTS secret fields (a presence marker only) — never round-trip a stored
14459
+ * secret to the UI.
14398
14460
  */
14399
14461
  var TargetSchema = object({
14400
14462
  id: string(),
@@ -14437,13 +14499,13 @@ var ProbeTargetConfigResultSchema = object({
14437
14499
  stateInstance: string().optional(),
14438
14500
  error: string().optional()
14439
14501
  });
14440
- method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
14502
+ method(object({}), array(TargetKindSchema)), method(object({
14441
14503
  kind: string(),
14442
14504
  config: record(string(), unknown()).optional(),
14443
14505
  /**
14444
14506
  * When editing / duplicating, the blank password field never round-trips
14445
14507
  * the stored token. Pass the source target id so the provider can restore
14446
- * secrets server-side before dialling (same merge as upsert).
14508
+ * secrets server-side before dialling.
14447
14509
  */
14448
14510
  targetId: string().optional()
14449
14511
  }), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
@@ -14466,27 +14528,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
14466
14528
  }), TestResultSchema, {
14467
14529
  kind: "mutation",
14468
14530
  auth: "admin"
14469
- }), method(object({
14470
- target: TargetSchema,
14471
- /**
14472
- * Duplicate flow: create a NEW target id while copying secrets from an
14473
- * existing row. The UI never holds the stored token; blank / redacted
14474
- * password fields are restored from this source (D634 still applies if
14475
- * destinations diverge without a re-typed secret).
14476
- */
14477
- cloneSecretsFrom: string().optional()
14478
- }), TargetSchema, {
14479
- kind: "mutation",
14480
- auth: "admin"
14481
- }), method(object({ targetId: string() }), _void(), {
14482
- kind: "mutation",
14483
- auth: "admin"
14484
- }), method(object({
14485
- targetId: string(),
14486
- enabled: boolean()
14487
- }), _void(), {
14488
- kind: "mutation",
14489
- auth: "admin"
14490
14531
  });
14491
14532
  looseObject({
14492
14533
  name: string().min(1),
@@ -22754,7 +22795,6 @@ var SYSTEM_CAP_VOCABULARY = {
22754
22795
  "notification-output": system("notification delivery — reached through notifier devices and rules"),
22755
22796
  "notification-rules": system("notification rules — authored on their own page"),
22756
22797
  "oauth-integration": system(IDENTITY),
22757
- "oidc-connections": system(IDENTITY),
22758
22798
  "osd-manager": system(PIPELINE),
22759
22799
  "pipeline-executor": system(PIPELINE),
22760
22800
  "pipeline-orchestrator": system(PIPELINE),
@@ -54489,7 +54529,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
54489
54529
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
54490
54530
  * clears it.
54491
54531
  */
54492
- var ConfigSchemaPassthrough$3 = custom();
54532
+ var ConfigSchemaPassthrough$2 = custom();
54493
54533
  var IntegrationInstanceIdSchema = string().min(1);
54494
54534
  var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
54495
54535
  /**
@@ -54535,7 +54575,16 @@ var IntegrationInstanceKindSchema = object({
54535
54575
  /** Relative hub path to start an interactive account link of an instance of this kind. */
54536
54576
  connectPath: string().min(1).optional(),
54537
54577
  /** Relative hub path to disconnect that account. */
54538
- disconnectPath: string().min(1).optional()
54578
+ disconnectPath: string().min(1).optional(),
54579
+ /**
54580
+ * Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
54581
+ * GENERIC widget id the client maps to a component; a client that knows none of them shows none.
54582
+ * `connection` widgets write connection values, `model` widgets own the model choice.
54583
+ */
54584
+ widgets: array(object({
54585
+ id: string().min(1),
54586
+ placement: _enum(["connection", "model"])
54587
+ })).optional()
54539
54588
  });
54540
54589
  /**
54541
54590
  * How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
@@ -54640,7 +54689,7 @@ var integrationInstancesCapability = {
54640
54689
  id: IntegrationInstanceIdSchema.nullable(),
54641
54690
  /** The kind of the create form (`id: null`) of a provider with several. */
54642
54691
  kind: string().min(1).optional()
54643
- }), ConfigSchemaPassthrough$3, { auth: "admin" }),
54692
+ }), ConfigSchemaPassthrough$2, { auth: "admin" }),
54644
54693
  listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
54645
54694
  getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
54646
54695
  /**
@@ -55591,12 +55640,12 @@ var LlmModelInfoSchema = object({
55591
55640
  });
55592
55641
  /**
55593
55642
  * The provider-specific part of a profile, validated by the provider that owns
55594
- * the kind (`upsertProfile` refuses what it does not accept). A field its
55643
+ * the kind (saving an instance refuses what it does not accept). A field its
55595
55644
  * `connectionSchema` declares `type: 'password'` is a secret: it is held in the
55596
55645
  * keychain, never in the row, and reads back as the redaction marker.
55597
55646
  */
55598
55647
  var LlmProviderConfigSchema = record(string(), unknown());
55599
- var LlmProfileSchema = object({
55648
+ object({
55600
55649
  id: string(),
55601
55650
  name: string(),
55602
55651
  /** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
@@ -55690,8 +55739,8 @@ var LlmProviderWidgetSchema = object({
55690
55739
  /** ConfigUISchema tree passed through untyped on the wire (the
55691
55740
  * notification-output `ConfigSchemaPassthrough` precedent at
55692
55741
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
55693
- var ConfigSchemaPassthrough$2 = unknown();
55694
- var LlmProfileKindDescriptorSchema = object({
55742
+ var ConfigSchemaPassthrough$1 = unknown();
55743
+ object({
55695
55744
  kind: LlmProfileKindSchema,
55696
55745
  label: string(),
55697
55746
  /** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
@@ -55699,9 +55748,9 @@ var LlmProfileKindDescriptorSchema = object({
55699
55748
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
55700
55749
  addonId: string(),
55701
55750
  /** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
55702
- connectionSchema: ConfigSchemaPassthrough$2,
55751
+ connectionSchema: ConfigSchemaPassthrough$1,
55703
55752
  /** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
55704
- advancedSchema: ConfigSchemaPassthrough$2.optional(),
55753
+ advancedSchema: ConfigSchemaPassthrough$1.optional(),
55705
55754
  /** The kind's defaults; a model from `listModels` may override any of them. */
55706
55755
  features: LlmModelFeaturesSchema,
55707
55756
  /** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
@@ -55830,20 +55879,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
55830
55879
  }), method(object({
55831
55880
  addonId: string().optional(),
55832
55881
  requestId: string()
55833
- }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
55834
- kind: "mutation",
55835
- auth: "admin"
55836
- }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
55837
- kind: "mutation",
55838
- auth: "admin"
55839
- }), method(ProfileRefInputSchema, _void(), {
55840
- kind: "mutation",
55841
- auth: "admin"
55842
- }), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
55843
- kind: "mutation",
55844
- auth: "admin",
55845
- timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
55846
- }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
55882
+ }), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
55847
55883
  kind: "mutation",
55848
55884
  auth: "admin"
55849
55885
  }), method(object({}), array(LlmDefaultSchema)), method(object({
@@ -56838,20 +56874,7 @@ method(_void(), MeshStatusSchema), method(object({
56838
56874
  /** Optional hostname override the host should advertise once joined. */
56839
56875
  hostname: string().optional() }), object({
56840
56876
  /** Authentication URL the operator should open in a browser. */
56841
- loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() })), method(object({
56842
- /** Optional auth key — when provided, probes the key validity
56843
- * against the provider's API. Omit when already joined to
56844
- * just ping the daemon. */
56845
- authKey: string().optional() }), object({
56846
- ok: boolean(),
56847
- /** Provider-side identifier resolved by the probe (tailnet
56848
- * name for Tailscale, network id for ZeroTier, etc.). */
56849
- tenant: string().optional(),
56850
- /** Daemon binary version, when reachable. */
56851
- daemonVersion: string().optional(),
56852
- /** Human-readable error when `ok: false`. */
56853
- error: string().optional()
56854
- }), { kind: "mutation" });
56877
+ loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }));
56855
56878
  object({
56856
56879
  value: number().nullable().meta(readingMeta("device", { label: "Total" })),
56857
56880
  lastFetchedAt: number()
@@ -66509,61 +66532,6 @@ var OauthIntegrationDescriptorSchema = object({
66509
66532
  });
66510
66533
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
66511
66534
  /**
66512
- * `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
66513
- *
66514
- * One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
66515
- * ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
66516
- * Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
66517
- * immutable after create.
66518
- */
66519
- var ConfigSchemaPassthrough$1 = unknown();
66520
- var OidcConnectionSchema = object({
66521
- id: string().min(1),
66522
- displayName: string(),
66523
- icon: string(),
66524
- issuerUrl: string(),
66525
- clientId: string(),
66526
- /** Client id + secret present — never the secret value. */
66527
- configured: boolean(),
66528
- /**
66529
- * Values for the connection form schema. `clientSecret` is a keychain
66530
- * {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
66531
- */
66532
- formValues: record(string(), unknown()).optional()
66533
- });
66534
- /**
66535
- * Patch one connection. Omitted or empty `clientSecret` keeps the stored
66536
- * secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
66537
- */
66538
- var OidcConnectionUpdateInputSchema = object({
66539
- id: string().min(1),
66540
- displayName: string().min(1),
66541
- icon: string().optional(),
66542
- issuerUrl: string().optional(),
66543
- clientId: string().optional(),
66544
- clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
66545
- redirectUri: string().optional(),
66546
- scopes: string().optional(),
66547
- usernameClaim: _enum([
66548
- "preferred_username",
66549
- "email",
66550
- "sub"
66551
- ]).optional()
66552
- });
66553
- method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
66554
- id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
66555
- displayName: string().min(1)
66556
- }), object({ id: string() }), {
66557
- kind: "mutation",
66558
- auth: "admin"
66559
- }), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
66560
- kind: "mutation",
66561
- auth: "admin"
66562
- }), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
66563
- kind: "mutation",
66564
- auth: "admin"
66565
- });
66566
- /**
66567
66535
  * Generic on-screen-display (video overlay) cap. Each camera exposes
66568
66536
  * a firmware-defined list of overlays (channel name, timestamp,
66569
66537
  * watermark, logo, …); the cap models them as a homogeneous array of
@@ -72749,21 +72717,6 @@ method(object({
72749
72717
  */
72750
72718
  ttlSec: union([number().int().positive(), literal("never")]).optional()
72751
72719
  }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
72752
- var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
72753
- providerId: string().min(1),
72754
- displayName: string().min(1),
72755
- configSchema: unknown(),
72756
- nodeLocal: boolean(),
72757
- shouldSaveDiskSpace: literal(true),
72758
- minFreePercent: number().min(0).max(100)
72759
- }), object({
72760
- providerId: string().min(1),
72761
- displayName: string().min(1),
72762
- configSchema: unknown(),
72763
- nodeLocal: boolean(),
72764
- shouldSaveDiskSpace: literal(false),
72765
- minFreePercent: literal(null)
72766
- })]);
72767
72720
  method(object({
72768
72721
  location: StorageLocationRefSchema,
72769
72722
  relativePath: string()
@@ -72825,42 +72778,7 @@ method(object({
72825
72778
  }), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
72826
72779
  kind: "mutation",
72827
72780
  auth: "admin"
72828
- }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
72829
- createdAt: true,
72830
- updatedAt: true
72831
- }), StorageLocationSchema, {
72832
- kind: "mutation",
72833
- auth: "admin"
72834
- }), method(object({
72835
- id: string(),
72836
- force: boolean().optional()
72837
- }), _void(), {
72838
- kind: "mutation",
72839
- auth: "admin"
72840
- }), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
72841
- ok: boolean(),
72842
- error: string().optional()
72843
- }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
72844
- providerId: string(),
72845
- /**
72846
- * The location this config is an UNSAVED edit of, when there is one.
72847
- *
72848
- * `listLocations` replaces every declared secret with the redaction
72849
- * sentinel, so the edit modal's form state holds the sentinel for any
72850
- * credential the operator did not retype — and posting that here
72851
- * without a way to resolve it makes the provider try to authenticate
72852
- * as `__camstack_redacted__` and report the operator's own working
72853
- * password as wrong. Given this id, the orchestrator restores each
72854
- * sentinel from the stored config (same rule as `upsertLocation`)
72855
- * before dispatching. Omitted by the "Add location" wizard, where
72856
- * every value was typed just now and nothing is stored yet.
72857
- */
72858
- locationId: string().optional(),
72859
- config: record(string(), unknown())
72860
- }), object({
72861
- ok: boolean(),
72862
- error: string().optional()
72863
- }), { auth: "admin" });
72781
+ }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
72864
72782
  /** One table of the settings database. */
72865
72783
  var StorageTableSchema = object({
72866
72784
  /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
@@ -80453,13 +80371,6 @@ Object.freeze({
80453
80371
  access: "delete",
80454
80372
  role: "admin"
80455
80373
  },
80456
- "llm.deleteProfile": {
80457
- capName: "llm",
80458
- capScope: "system",
80459
- addonId: null,
80460
- access: "delete",
80461
- role: "admin"
80462
- },
80463
80374
  "llm.generate": {
80464
80375
  capName: "llm",
80465
80376
  capScope: "system",
@@ -80523,20 +80434,6 @@ Object.freeze({
80523
80434
  access: "view",
80524
80435
  role: "admin"
80525
80436
  },
80526
- "llm.listProfileKinds": {
80527
- capName: "llm",
80528
- capScope: "system",
80529
- addonId: null,
80530
- access: "view",
80531
- role: "admin"
80532
- },
80533
- "llm.listProfiles": {
80534
- capName: "llm",
80535
- capScope: "system",
80536
- addonId: null,
80537
- access: "view",
80538
- role: "admin"
80539
- },
80540
80437
  "llm.listRuntimeNodes": {
80541
80438
  capName: "llm",
80542
80439
  capScope: "system",
@@ -80544,13 +80441,6 @@ Object.freeze({
80544
80441
  access: "view",
80545
80442
  role: "admin"
80546
80443
  },
80547
- "llm.migrateRawSecrets": {
80548
- capName: "llm",
80549
- capScope: "system",
80550
- addonId: null,
80551
- access: "create",
80552
- role: "admin"
80553
- },
80554
80444
  "llm.resolveModelRef": {
80555
80445
  capName: "llm",
80556
80446
  capScope: "system",
@@ -80579,20 +80469,6 @@ Object.freeze({
80579
80469
  access: "create",
80580
80470
  role: "admin"
80581
80471
  },
80582
- "llm.testProfile": {
80583
- capName: "llm",
80584
- capScope: "system",
80585
- addonId: null,
80586
- access: "create",
80587
- role: "admin"
80588
- },
80589
- "llm.upsertProfile": {
80590
- capName: "llm",
80591
- capScope: "system",
80592
- addonId: null,
80593
- access: "create",
80594
- role: "admin"
80595
- },
80596
80472
  "llmRuntime.appleAvailability": {
80597
80473
  capName: "llm-runtime",
80598
80474
  capScope: "system",
@@ -81097,13 +80973,6 @@ Object.freeze({
81097
80973
  access: "create",
81098
80974
  role: "admin"
81099
80975
  },
81100
- "meshNetwork.testConnection": {
81101
- capName: "mesh-network",
81102
- capScope: "system",
81103
- addonId: null,
81104
- access: "create",
81105
- role: "admin"
81106
- },
81107
80976
  "meter.getStatus": {
81108
80977
  capName: "meter",
81109
80978
  capScope: "device",
@@ -81762,13 +81631,6 @@ Object.freeze({
81762
81631
  access: "create",
81763
81632
  role: "admin"
81764
81633
  },
81765
- "notificationOutput.deleteTarget": {
81766
- capName: "notification-output",
81767
- capScope: "system",
81768
- addonId: null,
81769
- access: "delete",
81770
- role: "admin"
81771
- },
81772
81634
  "notificationOutput.discoverTargets": {
81773
81635
  capName: "notification-output",
81774
81636
  capScope: "system",
@@ -81776,13 +81638,6 @@ Object.freeze({
81776
81638
  access: "view",
81777
81639
  role: "admin"
81778
81640
  },
81779
- "notificationOutput.getTarget": {
81780
- capName: "notification-output",
81781
- capScope: "system",
81782
- addonId: null,
81783
- access: "view",
81784
- role: "admin"
81785
- },
81786
81641
  "notificationOutput.listTargetKinds": {
81787
81642
  capName: "notification-output",
81788
81643
  capScope: "system",
@@ -81790,13 +81645,6 @@ Object.freeze({
81790
81645
  access: "view",
81791
81646
  role: "admin"
81792
81647
  },
81793
- "notificationOutput.listTargets": {
81794
- capName: "notification-output",
81795
- capScope: "system",
81796
- addonId: null,
81797
- access: "view",
81798
- role: "admin"
81799
- },
81800
81648
  "notificationOutput.probeTargetConfig": {
81801
81649
  capName: "notification-output",
81802
81650
  capScope: "system",
@@ -81811,13 +81659,6 @@ Object.freeze({
81811
81659
  access: "create",
81812
81660
  role: "admin"
81813
81661
  },
81814
- "notificationOutput.setTargetEnabled": {
81815
- capName: "notification-output",
81816
- capScope: "system",
81817
- addonId: null,
81818
- access: "create",
81819
- role: "admin"
81820
- },
81821
81662
  "notificationOutput.testTarget": {
81822
81663
  capName: "notification-output",
81823
81664
  capScope: "system",
@@ -81825,13 +81666,6 @@ Object.freeze({
81825
81666
  access: "create",
81826
81667
  role: "admin"
81827
81668
  },
81828
- "notificationOutput.upsertTarget": {
81829
- capName: "notification-output",
81830
- capScope: "system",
81831
- addonId: null,
81832
- access: "create",
81833
- role: "admin"
81834
- },
81835
81669
  "notificationRules.activateAlarmProfile": {
81836
81670
  capName: "notification-rules",
81837
81671
  capScope: "system",
@@ -82091,41 +81925,6 @@ Object.freeze({
82091
81925
  access: "view",
82092
81926
  role: "admin"
82093
81927
  },
82094
- "oidcConnections.connectionFormSchema": {
82095
- capName: "oidc-connections",
82096
- capScope: "system",
82097
- addonId: null,
82098
- access: "view",
82099
- role: "admin"
82100
- },
82101
- "oidcConnections.createConnection": {
82102
- capName: "oidc-connections",
82103
- capScope: "system",
82104
- addonId: null,
82105
- access: "create",
82106
- role: "admin"
82107
- },
82108
- "oidcConnections.deleteConnection": {
82109
- capName: "oidc-connections",
82110
- capScope: "system",
82111
- addonId: null,
82112
- access: "delete",
82113
- role: "admin"
82114
- },
82115
- "oidcConnections.listConnections": {
82116
- capName: "oidc-connections",
82117
- capScope: "system",
82118
- addonId: null,
82119
- access: "view",
82120
- role: "admin"
82121
- },
82122
- "oidcConnections.updateConnection": {
82123
- capName: "oidc-connections",
82124
- capScope: "system",
82125
- addonId: null,
82126
- access: "create",
82127
- role: "admin"
82128
- },
82129
81928
  "osd.getStatus": {
82130
81929
  capName: "osd",
82131
81930
  capScope: "device",
@@ -85157,13 +84956,6 @@ Object.freeze({
85157
84956
  access: "delete",
85158
84957
  role: "admin"
85159
84958
  },
85160
- "storage.deleteLocation": {
85161
- capName: "storage",
85162
- capScope: "system",
85163
- addonId: null,
85164
- access: "delete",
85165
- role: "admin"
85166
- },
85167
84959
  "storage.endDownload": {
85168
84960
  capName: "storage",
85169
84961
  capScope: "system",
@@ -85220,13 +85012,6 @@ Object.freeze({
85220
85012
  access: "view",
85221
85013
  role: "admin"
85222
85014
  },
85223
- "storage.listProviders": {
85224
- capName: "storage",
85225
- capScope: "system",
85226
- addonId: null,
85227
- access: "view",
85228
- role: "admin"
85229
- },
85230
85015
  "storage.read": {
85231
85016
  capName: "storage",
85232
85017
  capScope: "system",
@@ -85248,27 +85033,6 @@ Object.freeze({
85248
85033
  access: "view",
85249
85034
  role: "admin"
85250
85035
  },
85251
- "storage.testConfig": {
85252
- capName: "storage",
85253
- capScope: "system",
85254
- addonId: null,
85255
- access: "view",
85256
- role: "admin"
85257
- },
85258
- "storage.testLocation": {
85259
- capName: "storage",
85260
- capScope: "system",
85261
- addonId: null,
85262
- access: "view",
85263
- role: "admin"
85264
- },
85265
- "storage.upsertLocation": {
85266
- capName: "storage",
85267
- capScope: "system",
85268
- addonId: null,
85269
- access: "create",
85270
- role: "admin"
85271
- },
85272
85036
  "storage.write": {
85273
85037
  capName: "storage",
85274
85038
  capScope: "system",
@@ -91821,222 +91585,6 @@ var GoogleBrokerRegistry = class {
91821
91585
  }
91822
91586
  };
91823
91587
  //#endregion
91824
- //#region src/google-settings-copy.ts
91825
- /**
91826
- * Operator-facing setup copy for Connectivity → Calendars / Email.
91827
- * Keep Connect as a same-origin relative link so the admin session cookie
91828
- * rides along (an absolute Public URL from a LAN admin tab drops the cookie).
91829
- * Root-relative hrefs must survive `sanitizeInfoHtml` in ui-library.
91830
- */
91831
- /** Enable APIs library in Google Cloud Console. */
91832
- var GOOGLE_CONSOLE_APIS_URL = "https://console.cloud.google.com/apis/library";
91833
- /** Create / manage OAuth Web clients. */
91834
- var GOOGLE_CONSOLE_CREDENTIALS_URL = "https://console.cloud.google.com/apis/credentials";
91835
- function redirectUriDisplay(publicOrigin) {
91836
- if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
91837
- return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
91838
- }
91839
- function consoleLinksHtml$1() {
91840
- return [
91841
- `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
91842
- ` · `,
91843
- `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
91844
- ].join("");
91845
- }
91846
- function introHtml(cap, publicOrigin) {
91847
- const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
91848
- const console = consoleLinksHtml$1();
91849
- if (cap === "smtp-provider") return [
91850
- "<ol>",
91851
- `<li>Open Google Cloud Console (${console}) and enable the <strong>Gmail API</strong> (and Calendar API if you also use Calendars).</li>`,
91852
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91853
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91854
- "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
91855
- `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
91856
- "<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <strong>Enable</strong> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
91857
- "</ol>"
91858
- ].join("");
91859
- if (cap === "calendar-source") return [
91860
- "<ol>",
91861
- `<li>Open Google Cloud Console (${console}) and enable the <strong>Google Calendar API</strong> (and Gmail API if you also use Email).</li>`,
91862
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91863
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91864
- "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
91865
- `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
91866
- "<li>Optionally restrict which calendars are exposed with the filter below.</li>",
91867
- "</ol>"
91868
- ].join("");
91869
- return [
91870
- "<ol>",
91871
- `<li>Open Google Cloud Console (${console}) and enable <strong>Google Calendar API</strong> and <strong>Gmail API</strong>.</li>`,
91872
- "<li>Create a <strong>Web application</strong> OAuth client under Credentials.</li>",
91873
- `<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
91874
- `<li>Paste Client ID / secret, Save, then <a href="${OAUTH_START_PATH}">Connect Google</a>.</li>`,
91875
- "<li>Calendars and Email share one Google account; use each page’s <strong>Enable</strong> toggle to turn a capability off without revoking OAuth.</li>",
91876
- "</ol>"
91877
- ].join("");
91878
- }
91879
- function connectHtml(cap, publicOrigin) {
91880
- const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
91881
- if (cap === "smtp-provider") return [
91882
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
91883
- "Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
91884
- `Google must list exactly: <code>${redirect}</code>. `,
91885
- "If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again. ",
91886
- `Console: ${consoleLinksHtml$1()}.`
91887
- ].join("");
91888
- if (cap === "calendar-source") return [
91889
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
91890
- "Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
91891
- `Google must list exactly: <code>${redirect}</code>. `,
91892
- `Console: ${consoleLinksHtml$1()}.`
91893
- ].join("");
91894
- return [
91895
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `,
91896
- `Redirect URI registered in Google Cloud must be <code>${redirect}</code>. `,
91897
- `Console: ${consoleLinksHtml$1()}.`
91898
- ].join("");
91899
- }
91900
- function escapeHtml$1(s) {
91901
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
91902
- }
91903
- //#endregion
91904
- //#region src/google-broker-settings-schema.ts
91905
- /**
91906
- * Per-kind broker creation/edit form for kind=`google`.
91907
- * Account linking (Sign in) is driven by the kind's `connectPath`
91908
- * on the instances page — not a hard-coded Connect link in this form.
91909
- */
91910
- function consoleLinksHtml() {
91911
- return [
91912
- `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
91913
- ` · `,
91914
- `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
91915
- ].join("");
91916
- }
91917
- /** Setup steps shown on Add / Edit — includes Audience / Test users. */
91918
- function googleBrokerSetupHtml(publicOrigin) {
91919
- const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
91920
- return [
91921
- "<ol>",
91922
- `<li>Open Google Cloud Console (${consoleLinksHtml()}). Create a <strong>new project</strong> (or pick an existing one) from the project picker at the top.</li>`,
91923
- "<li>In that project, enable <strong>Google Calendar API</strong> and/or <strong>Gmail API</strong> (API library).</li>",
91924
- "<li>Open <strong>Google Auth Platform → Audience</strong> (or APIs &amp; Services → OAuth consent screen → <strong>Audience</strong>). User type <strong>External</strong> + publishing status <strong>Testing</strong> is fine for a personal hub. Under <strong>Test users</strong>, add the Google account(s) that will Sign in — otherwise Google returns <code>403: access_denied</code>. (If you only see Branding / Data Access, open the <strong>Audience</strong> tab in the left sidebar of Google Auth Platform.) Publishing / verification is optional for a private hub.</li>",
91925
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91926
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91927
- "<li>Paste Client ID and Client secret below. <strong>Test connection</strong> checks those with Google (not the account yet).</li>",
91928
- "<li>Add the broker — the hub then opens Google sign-in in a popup to link the account (Calendar + Gmail). Use <strong>Sign in</strong> on the row if you skip or cancel.</li>",
91929
- "</ol>"
91930
- ].join("");
91931
- }
91932
- function buildGoogleBrokerSettingsSchema(publicOrigin) {
91933
- return { sections: [
91934
- {
91935
- id: "setup-intro",
91936
- title: "Setup",
91937
- titleKey: "google-services:googleBrokerSettings.s_setup-intro.title",
91938
- description: "Google Cloud Console — register the hub redirect before Add / Sign in.",
91939
- descriptionKey: "google-services:googleBrokerSettings.s_setup-intro.description",
91940
- columns: 1,
91941
- fields: [{
91942
- type: "info",
91943
- key: "_setupSteps",
91944
- label: "Steps",
91945
- labelKey: "google-services:googleBrokerSettings.s_setup-intro._setupSteps.label",
91946
- format: "html",
91947
- content: googleBrokerSetupHtml(publicOrigin)
91948
- }]
91949
- },
91950
- {
91951
- id: "google-oauth-client",
91952
- title: "Google OAuth client",
91953
- titleKey: "google-services:googleBrokerSettings.s_google-oauth-client.title",
91954
- description: "Web application client from Google Cloud Console. The same Cloud project can be reused across Google brokers.",
91955
- descriptionKey: "google-services:googleBrokerSettings.s_google-oauth-client.description",
91956
- columns: 1,
91957
- fields: [{
91958
- type: "text",
91959
- key: "clientId",
91960
- label: "Client ID",
91961
- labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientId.label"
91962
- }, {
91963
- type: "password",
91964
- key: "clientSecret",
91965
- label: "Client secret",
91966
- labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientSecret.label",
91967
- showToggle: true
91968
- }]
91969
- },
91970
- {
91971
- id: "gmail-send",
91972
- title: "Gmail send",
91973
- titleKey: "google-services:googleBrokerSettings.s_gmail-send.title",
91974
- description: "From must be this connected account or a verified send-as. Disable Email on Connectivity to turn Gmail off without removing the broker.",
91975
- descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.description",
91976
- columns: 1,
91977
- fields: [{
91978
- type: "text",
91979
- key: "fromAddress",
91980
- label: "From address",
91981
- labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.label",
91982
- description: "Defaults to the connected Google account email when empty.",
91983
- descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.description"
91984
- }, {
91985
- type: "text",
91986
- key: "fromName",
91987
- label: "From display name (optional)",
91988
- labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromName.label"
91989
- }]
91990
- }
91991
- ] };
91992
- }
91993
- /** Connectivity Calendars / Email pages — the calendar import picker is on each broker. */
91994
- function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
91995
- const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
91996
- if (cap === "calendar-source") return { sections: [{
91997
- id: "setup-intro",
91998
- title: "Google brokers",
91999
- titleKey: "google-services:googleFeatureSettings.s_setup-intro.title",
92000
- columns: 1,
92001
- fields: [{
92002
- type: "info",
92003
- key: "_setupSteps",
92004
- label: "Setup",
92005
- labelKey: "google-services:googleFeatureSettings.s_setup-intro._setupSteps.label",
92006
- format: "html",
92007
- content: [
92008
- "<p>Link a Google account under Connectivity → <strong>Brokers</strong> ",
92009
- "(Client ID / secret + Sign in), then add a calendar under Connectivity → <strong>Calendars</strong>. ",
92010
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92011
- ].join("")
92012
- }]
92013
- }] };
92014
- return { sections: [{
92015
- id: "setup-intro",
92016
- title: cap === "smtp-provider" ? "Gmail send uses Google brokers" : "Google brokers",
92017
- columns: 1,
92018
- fields: [{
92019
- type: "info",
92020
- key: "_setupSteps",
92021
- label: "Setup",
92022
- labelKey: "google-services:googleFeatureSettings._setupSteps.label",
92023
- format: "html",
92024
- content: cap === "smtp-provider" ? [
92025
- "<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
92026
- "(Client ID / secret + Sign in). From address and send-as live on that broker. ",
92027
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92028
- ].join("") : [
92029
- "<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
92030
- "(Client ID / secret + Sign in). ",
92031
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92032
- ].join("")
92033
- }]
92034
- }] };
92035
- }
92036
- function escapeHtml(s) {
92037
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
92038
- }
92039
- //#endregion
92040
91588
  //#region src/google-calendar-source.ts
92041
91589
  function parseGoogleInstant(value) {
92042
91590
  const ms = Date.parse(value);
@@ -92557,6 +92105,179 @@ function buildGoogleCalendarInstances(records, deps) {
92557
92105
  });
92558
92106
  }
92559
92107
  //#endregion
92108
+ //#region src/google-settings-copy.ts
92109
+ /**
92110
+ * Operator-facing setup copy for Connectivity → Calendars / Email.
92111
+ * Keep Connect as a same-origin relative link so the admin session cookie
92112
+ * rides along (an absolute Public URL from a LAN admin tab drops the cookie).
92113
+ * Root-relative hrefs must survive `sanitizeInfoHtml` in ui-library.
92114
+ */
92115
+ /** Enable APIs library in Google Cloud Console. */
92116
+ var GOOGLE_CONSOLE_APIS_URL = "https://console.cloud.google.com/apis/library";
92117
+ /** Create / manage OAuth Web clients. */
92118
+ var GOOGLE_CONSOLE_CREDENTIALS_URL = "https://console.cloud.google.com/apis/credentials";
92119
+ function redirectUriDisplay(publicOrigin) {
92120
+ if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
92121
+ return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
92122
+ }
92123
+ function consoleLinksHtml$1() {
92124
+ return [
92125
+ `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
92126
+ ` · `,
92127
+ `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
92128
+ ].join("");
92129
+ }
92130
+ function introHtml(cap, publicOrigin) {
92131
+ const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
92132
+ const console = consoleLinksHtml$1();
92133
+ if (cap === "smtp-provider") return [
92134
+ "<ol>",
92135
+ `<li>Open Google Cloud Console (${console}) and enable the <strong>Gmail API</strong> (and Calendar API if you also use Calendars).</li>`,
92136
+ "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
92137
+ `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
92138
+ "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
92139
+ `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
92140
+ "<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <strong>Enable</strong> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
92141
+ "</ol>"
92142
+ ].join("");
92143
+ if (cap === "calendar-source") return [
92144
+ "<ol>",
92145
+ `<li>Open Google Cloud Console (${console}) and enable the <strong>Google Calendar API</strong> (and Gmail API if you also use Email).</li>`,
92146
+ "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
92147
+ `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
92148
+ "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
92149
+ `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
92150
+ "<li>Optionally restrict which calendars are exposed with the filter below.</li>",
92151
+ "</ol>"
92152
+ ].join("");
92153
+ return [
92154
+ "<ol>",
92155
+ `<li>Open Google Cloud Console (${console}) and enable <strong>Google Calendar API</strong> and <strong>Gmail API</strong>.</li>`,
92156
+ "<li>Create a <strong>Web application</strong> OAuth client under Credentials.</li>",
92157
+ `<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
92158
+ `<li>Paste Client ID / secret, Save, then <a href="${OAUTH_START_PATH}">Connect Google</a>.</li>`,
92159
+ "<li>Calendars and Email share one Google account; use each page’s <strong>Enable</strong> toggle to turn a capability off without revoking OAuth.</li>",
92160
+ "</ol>"
92161
+ ].join("");
92162
+ }
92163
+ function connectHtml(cap, publicOrigin) {
92164
+ const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
92165
+ if (cap === "smtp-provider") return [
92166
+ `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
92167
+ "Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
92168
+ `Google must list exactly: <code>${redirect}</code>. `,
92169
+ "If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again. ",
92170
+ `Console: ${consoleLinksHtml$1()}.`
92171
+ ].join("");
92172
+ if (cap === "calendar-source") return [
92173
+ `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
92174
+ "Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
92175
+ `Google must list exactly: <code>${redirect}</code>. `,
92176
+ `Console: ${consoleLinksHtml$1()}.`
92177
+ ].join("");
92178
+ return [
92179
+ `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `,
92180
+ `Redirect URI registered in Google Cloud must be <code>${redirect}</code>. `,
92181
+ `Console: ${consoleLinksHtml$1()}.`
92182
+ ].join("");
92183
+ }
92184
+ function escapeHtml$1(s) {
92185
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
92186
+ }
92187
+ //#endregion
92188
+ //#region src/google-broker-settings-schema.ts
92189
+ /**
92190
+ * Per-kind broker creation/edit form for kind=`google`.
92191
+ * Account linking (Sign in) is driven by the kind's `connectPath`
92192
+ * on the instances page — not a hard-coded Connect link in this form.
92193
+ */
92194
+ function consoleLinksHtml() {
92195
+ return [
92196
+ `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
92197
+ ` · `,
92198
+ `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
92199
+ ].join("");
92200
+ }
92201
+ /** Setup steps shown on Add / Edit — includes Audience / Test users. */
92202
+ function googleBrokerSetupHtml(publicOrigin) {
92203
+ const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
92204
+ return [
92205
+ "<ol>",
92206
+ `<li>Open Google Cloud Console (${consoleLinksHtml()}). Create a <strong>new project</strong> (or pick an existing one) from the project picker at the top.</li>`,
92207
+ "<li>In that project, enable <strong>Google Calendar API</strong> and/or <strong>Gmail API</strong> (API library).</li>",
92208
+ "<li>Open <strong>Google Auth Platform → Audience</strong> (or APIs &amp; Services → OAuth consent screen → <strong>Audience</strong>). User type <strong>External</strong> + publishing status <strong>Testing</strong> is fine for a personal hub. Under <strong>Test users</strong>, add the Google account(s) that will Sign in — otherwise Google returns <code>403: access_denied</code>. (If you only see Branding / Data Access, open the <strong>Audience</strong> tab in the left sidebar of Google Auth Platform.) Publishing / verification is optional for a private hub.</li>",
92209
+ "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
92210
+ `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
92211
+ "<li>Paste Client ID and Client secret below. <strong>Test connection</strong> checks those with Google (not the account yet).</li>",
92212
+ "<li>Add the broker — the hub then opens Google sign-in in a popup to link the account (Calendar + Gmail). Use <strong>Sign in</strong> on the row if you skip or cancel.</li>",
92213
+ "</ol>"
92214
+ ].join("");
92215
+ }
92216
+ function buildGoogleBrokerSettingsSchema(publicOrigin) {
92217
+ return { sections: [
92218
+ {
92219
+ id: "setup-intro",
92220
+ title: "Setup",
92221
+ titleKey: "google-services:googleBrokerSettings.s_setup-intro.title",
92222
+ description: "Google Cloud Console — register the hub redirect before Add / Sign in.",
92223
+ descriptionKey: "google-services:googleBrokerSettings.s_setup-intro.description",
92224
+ columns: 1,
92225
+ fields: [{
92226
+ type: "info",
92227
+ key: "_setupSteps",
92228
+ label: "Steps",
92229
+ labelKey: "google-services:googleBrokerSettings.s_setup-intro._setupSteps.label",
92230
+ format: "html",
92231
+ content: googleBrokerSetupHtml(publicOrigin)
92232
+ }]
92233
+ },
92234
+ {
92235
+ id: "google-oauth-client",
92236
+ title: "Google OAuth client",
92237
+ titleKey: "google-services:googleBrokerSettings.s_google-oauth-client.title",
92238
+ description: "Web application client from Google Cloud Console. The same Cloud project can be reused across Google brokers.",
92239
+ descriptionKey: "google-services:googleBrokerSettings.s_google-oauth-client.description",
92240
+ columns: 1,
92241
+ fields: [{
92242
+ type: "text",
92243
+ key: "clientId",
92244
+ label: "Client ID",
92245
+ labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientId.label"
92246
+ }, {
92247
+ type: "password",
92248
+ key: "clientSecret",
92249
+ label: "Client secret",
92250
+ labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientSecret.label",
92251
+ showToggle: true
92252
+ }]
92253
+ },
92254
+ {
92255
+ id: "gmail-send",
92256
+ title: "Gmail send",
92257
+ titleKey: "google-services:googleBrokerSettings.s_gmail-send.title",
92258
+ description: "From must be this connected account or a verified send-as. Disable Email on Connectivity to turn Gmail off without removing the broker.",
92259
+ descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.description",
92260
+ columns: 1,
92261
+ fields: [{
92262
+ type: "text",
92263
+ key: "fromAddress",
92264
+ label: "From address",
92265
+ labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.label",
92266
+ description: "Defaults to the connected Google account email when empty.",
92267
+ descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.description"
92268
+ }, {
92269
+ type: "text",
92270
+ key: "fromName",
92271
+ label: "From display name (optional)",
92272
+ labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromName.label"
92273
+ }]
92274
+ }
92275
+ ] };
92276
+ }
92277
+ function escapeHtml(s) {
92278
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
92279
+ }
92280
+ //#endregion
92560
92281
  //#region src/google-instances.ts
92561
92282
  /**
92562
92283
  * The Google accounts as `integration-instances` (planning#408 P3): one instance per broker
@@ -93843,22 +93564,7 @@ var GoogleServicesAddon = class extends BaseAddon {
93843
93564
  this.publicOrigin = null;
93844
93565
  }
93845
93566
  }
93846
- /**
93847
- * Connectivity Calendars / Email: point at Brokers (no duplicate OAuth fields).
93848
- * Addon page without cap: same pointer + note.
93849
- */
93850
- globalSettingsSchema(cap) {
93851
- const scoped = asSettingsCap(cap);
93852
- return this.schema(buildGoogleFeatureSettingsSchema(scoped, this.publicOrigin));
93853
- }
93854
- async getGlobalSettings(overlay, cap, nodeId) {
93855
- await this.refreshPublicOrigin();
93856
- return super.getGlobalSettings(overlay, cap, nodeId);
93857
- }
93858
93567
  };
93859
- function asSettingsCap(cap) {
93860
- if (cap === "calendar-source" || cap === "smtp-provider") return cap;
93861
- }
93862
93568
  //#endregion
93863
93569
  exports.DEFAULT_GOOGLE_CONFIG = DEFAULT_GOOGLE_CONFIG;
93864
93570
  exports.GOOGLE_BROKER_KIND = GOOGLE_BROKER_KIND;