@camstack/addon-google-services 0.2.42 → 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-D7ZKm4nb.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).
@@ -7234,17 +7251,35 @@ var RecordIntegrationInstances = class {
7234
7251
  ...kind !== void 0 ? { kind } : {}
7235
7252
  });
7236
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
+ }));
7237
7263
  await this.handle.put({
7238
7264
  id: minted,
7239
- data: this.hooks.initialBody(name, kind)
7265
+ data: initial
7240
7266
  }, { ifRev: 0 });
7241
- const patch = this.patchOf(values ?? {});
7242
7267
  if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
7243
7268
  await this.hooks.onChanged?.(minted, "created");
7244
7269
  return { id: minted };
7245
7270
  };
7246
7271
  updateInstance = async ({ id, name, values }) => {
7247
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
+ }
7248
7283
  if (name !== void 0) patch[this.nameField] = name;
7249
7284
  await this.handle.patch(id, patch);
7250
7285
  await this.hooks.onChanged?.(id, "updated");
@@ -10586,7 +10621,6 @@ var CAP_NAMES = Object.freeze([
10586
10621
  "notifier",
10587
10622
  "numeric-sensor",
10588
10623
  "oauth-integration",
10589
- "oidc-connections",
10590
10624
  "osd",
10591
10625
  "osd-manager",
10592
10626
  "pet-feeder",
@@ -12478,8 +12512,7 @@ Object.freeze({
12478
12512
  "device-provider": { "reloadDevice": 18e4 },
12479
12513
  "llm": {
12480
12514
  "generate": 6e5,
12481
- "generateVision": 6e5,
12482
- "testProfile": 6e5
12515
+ "generateVision": 6e5
12483
12516
  },
12484
12517
  "llm-runtime": {
12485
12518
  "complete": 6e5,
@@ -14104,17 +14137,18 @@ function composeInstanceKinds(parts, base) {
14104
14137
  * message to what the kind supports — callers never special-case a service.
14105
14138
  *
14106
14139
  * DESIGN DECISIONS (locked):
14107
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
14108
- * `setTargetEnabled`), each provider persisting via the `settings-store`
14109
- * cap. Rationale: the admin UI needs one uniform surface across the
14110
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
14111
- * alternative would fork the UI per addon and cannot host the
14112
- * discovery→adopt flow.
14113
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
14114
- * the generated cap-mount auto-`concatCollection`-fans them across every
14115
- * registered provider (notifiers addon + HA addon) so one catalog is
14116
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
14117
- * `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.
14118
14152
  * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
14119
14153
  * transformer) + UDS MsgPack both round-trip typed arrays — already used by
14120
14154
  * `storage` / `storage-provider` / `recording` caps over the same path. No
@@ -14359,7 +14393,7 @@ var TargetKindCapsSchema = object({
14359
14393
  * the union is large and not meant for runtime validation here; the exported
14360
14394
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
14361
14395
  */
14362
- var ConfigSchemaPassthrough$4 = unknown();
14396
+ var ConfigSchemaPassthrough$3 = unknown();
14363
14397
  var TargetKindSchema = object({
14364
14398
  kind: string(),
14365
14399
  label: string(),
@@ -14396,7 +14430,7 @@ var TargetKindSchema = object({
14396
14430
  * its raster path, which is the safe default for an unknown image.
14397
14431
  */
14398
14432
  iconMediaType: string().optional(),
14399
- configSchema: ConfigSchemaPassthrough$4,
14433
+ configSchema: ConfigSchemaPassthrough$3,
14400
14434
  /**
14401
14435
  * The config keys that decide WHERE this kind's secrets are sent (server,
14402
14436
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -14418,9 +14452,11 @@ var TargetKindSchema = object({
14418
14452
  caps: TargetKindCapsSchema
14419
14453
  });
14420
14454
  /**
14421
- * A persisted target. `config` holds secrets; providers REDACT secret fields
14422
- * (return a presence marker only) when serving `listTargets` — never
14423
- * 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.
14424
14460
  */
14425
14461
  var TargetSchema = object({
14426
14462
  id: string(),
@@ -14463,13 +14499,13 @@ var ProbeTargetConfigResultSchema = object({
14463
14499
  stateInstance: string().optional(),
14464
14500
  error: string().optional()
14465
14501
  });
14466
- 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({
14467
14503
  kind: string(),
14468
14504
  config: record(string(), unknown()).optional(),
14469
14505
  /**
14470
14506
  * When editing / duplicating, the blank password field never round-trips
14471
14507
  * the stored token. Pass the source target id so the provider can restore
14472
- * secrets server-side before dialling (same merge as upsert).
14508
+ * secrets server-side before dialling.
14473
14509
  */
14474
14510
  targetId: string().optional()
14475
14511
  }), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
@@ -14492,27 +14528,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
14492
14528
  }), TestResultSchema, {
14493
14529
  kind: "mutation",
14494
14530
  auth: "admin"
14495
- }), method(object({
14496
- target: TargetSchema,
14497
- /**
14498
- * Duplicate flow: create a NEW target id while copying secrets from an
14499
- * existing row. The UI never holds the stored token; blank / redacted
14500
- * password fields are restored from this source (D634 still applies if
14501
- * destinations diverge without a re-typed secret).
14502
- */
14503
- cloneSecretsFrom: string().optional()
14504
- }), TargetSchema, {
14505
- kind: "mutation",
14506
- auth: "admin"
14507
- }), method(object({ targetId: string() }), _void(), {
14508
- kind: "mutation",
14509
- auth: "admin"
14510
- }), method(object({
14511
- targetId: string(),
14512
- enabled: boolean()
14513
- }), _void(), {
14514
- kind: "mutation",
14515
- auth: "admin"
14516
14531
  });
14517
14532
  looseObject({
14518
14533
  name: string().min(1),
@@ -22780,7 +22795,6 @@ var SYSTEM_CAP_VOCABULARY = {
22780
22795
  "notification-output": system("notification delivery — reached through notifier devices and rules"),
22781
22796
  "notification-rules": system("notification rules — authored on their own page"),
22782
22797
  "oauth-integration": system(IDENTITY),
22783
- "oidc-connections": system(IDENTITY),
22784
22798
  "osd-manager": system(PIPELINE),
22785
22799
  "pipeline-executor": system(PIPELINE),
22786
22800
  "pipeline-orchestrator": system(PIPELINE),
@@ -54515,7 +54529,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
54515
54529
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
54516
54530
  * clears it.
54517
54531
  */
54518
- var ConfigSchemaPassthrough$3 = custom();
54532
+ var ConfigSchemaPassthrough$2 = custom();
54519
54533
  var IntegrationInstanceIdSchema = string().min(1);
54520
54534
  var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
54521
54535
  /**
@@ -54561,7 +54575,16 @@ var IntegrationInstanceKindSchema = object({
54561
54575
  /** Relative hub path to start an interactive account link of an instance of this kind. */
54562
54576
  connectPath: string().min(1).optional(),
54563
54577
  /** Relative hub path to disconnect that account. */
54564
- 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()
54565
54588
  });
54566
54589
  /**
54567
54590
  * How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
@@ -54666,7 +54689,7 @@ var integrationInstancesCapability = {
54666
54689
  id: IntegrationInstanceIdSchema.nullable(),
54667
54690
  /** The kind of the create form (`id: null`) of a provider with several. */
54668
54691
  kind: string().min(1).optional()
54669
- }), ConfigSchemaPassthrough$3, { auth: "admin" }),
54692
+ }), ConfigSchemaPassthrough$2, { auth: "admin" }),
54670
54693
  listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
54671
54694
  getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
54672
54695
  /**
@@ -55617,12 +55640,12 @@ var LlmModelInfoSchema = object({
55617
55640
  });
55618
55641
  /**
55619
55642
  * The provider-specific part of a profile, validated by the provider that owns
55620
- * 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
55621
55644
  * `connectionSchema` declares `type: 'password'` is a secret: it is held in the
55622
55645
  * keychain, never in the row, and reads back as the redaction marker.
55623
55646
  */
55624
55647
  var LlmProviderConfigSchema = record(string(), unknown());
55625
- var LlmProfileSchema = object({
55648
+ object({
55626
55649
  id: string(),
55627
55650
  name: string(),
55628
55651
  /** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
@@ -55716,8 +55739,8 @@ var LlmProviderWidgetSchema = object({
55716
55739
  /** ConfigUISchema tree passed through untyped on the wire (the
55717
55740
  * notification-output `ConfigSchemaPassthrough` precedent at
55718
55741
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
55719
- var ConfigSchemaPassthrough$2 = unknown();
55720
- var LlmProfileKindDescriptorSchema = object({
55742
+ var ConfigSchemaPassthrough$1 = unknown();
55743
+ object({
55721
55744
  kind: LlmProfileKindSchema,
55722
55745
  label: string(),
55723
55746
  /** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
@@ -55725,9 +55748,9 @@ var LlmProfileKindDescriptorSchema = object({
55725
55748
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
55726
55749
  addonId: string(),
55727
55750
  /** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
55728
- connectionSchema: ConfigSchemaPassthrough$2,
55751
+ connectionSchema: ConfigSchemaPassthrough$1,
55729
55752
  /** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
55730
- advancedSchema: ConfigSchemaPassthrough$2.optional(),
55753
+ advancedSchema: ConfigSchemaPassthrough$1.optional(),
55731
55754
  /** The kind's defaults; a model from `listModels` may override any of them. */
55732
55755
  features: LlmModelFeaturesSchema,
55733
55756
  /** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
@@ -55856,20 +55879,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
55856
55879
  }), method(object({
55857
55880
  addonId: string().optional(),
55858
55881
  requestId: string()
55859
- }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
55860
- kind: "mutation",
55861
- auth: "admin"
55862
- }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
55863
- kind: "mutation",
55864
- auth: "admin"
55865
- }), method(ProfileRefInputSchema, _void(), {
55866
- kind: "mutation",
55867
- auth: "admin"
55868
- }), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
55869
- kind: "mutation",
55870
- auth: "admin",
55871
- timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
55872
- }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
55882
+ }), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
55873
55883
  kind: "mutation",
55874
55884
  auth: "admin"
55875
55885
  }), method(object({}), array(LlmDefaultSchema)), method(object({
@@ -56864,20 +56874,7 @@ method(_void(), MeshStatusSchema), method(object({
56864
56874
  /** Optional hostname override the host should advertise once joined. */
56865
56875
  hostname: string().optional() }), object({
56866
56876
  /** Authentication URL the operator should open in a browser. */
56867
- 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({
56868
- /** Optional auth key — when provided, probes the key validity
56869
- * against the provider's API. Omit when already joined to
56870
- * just ping the daemon. */
56871
- authKey: string().optional() }), object({
56872
- ok: boolean(),
56873
- /** Provider-side identifier resolved by the probe (tailnet
56874
- * name for Tailscale, network id for ZeroTier, etc.). */
56875
- tenant: string().optional(),
56876
- /** Daemon binary version, when reachable. */
56877
- daemonVersion: string().optional(),
56878
- /** Human-readable error when `ok: false`. */
56879
- error: string().optional()
56880
- }), { 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() }));
56881
56878
  object({
56882
56879
  value: number().nullable().meta(readingMeta("device", { label: "Total" })),
56883
56880
  lastFetchedAt: number()
@@ -66535,61 +66532,6 @@ var OauthIntegrationDescriptorSchema = object({
66535
66532
  });
66536
66533
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
66537
66534
  /**
66538
- * `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
66539
- *
66540
- * One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
66541
- * ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
66542
- * Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
66543
- * immutable after create.
66544
- */
66545
- var ConfigSchemaPassthrough$1 = unknown();
66546
- var OidcConnectionSchema = object({
66547
- id: string().min(1),
66548
- displayName: string(),
66549
- icon: string(),
66550
- issuerUrl: string(),
66551
- clientId: string(),
66552
- /** Client id + secret present — never the secret value. */
66553
- configured: boolean(),
66554
- /**
66555
- * Values for the connection form schema. `clientSecret` is a keychain
66556
- * {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
66557
- */
66558
- formValues: record(string(), unknown()).optional()
66559
- });
66560
- /**
66561
- * Patch one connection. Omitted or empty `clientSecret` keeps the stored
66562
- * secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
66563
- */
66564
- var OidcConnectionUpdateInputSchema = object({
66565
- id: string().min(1),
66566
- displayName: string().min(1),
66567
- icon: string().optional(),
66568
- issuerUrl: string().optional(),
66569
- clientId: string().optional(),
66570
- clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
66571
- redirectUri: string().optional(),
66572
- scopes: string().optional(),
66573
- usernameClaim: _enum([
66574
- "preferred_username",
66575
- "email",
66576
- "sub"
66577
- ]).optional()
66578
- });
66579
- method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
66580
- id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
66581
- displayName: string().min(1)
66582
- }), object({ id: string() }), {
66583
- kind: "mutation",
66584
- auth: "admin"
66585
- }), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
66586
- kind: "mutation",
66587
- auth: "admin"
66588
- }), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
66589
- kind: "mutation",
66590
- auth: "admin"
66591
- });
66592
- /**
66593
66535
  * Generic on-screen-display (video overlay) cap. Each camera exposes
66594
66536
  * a firmware-defined list of overlays (channel name, timestamp,
66595
66537
  * watermark, logo, …); the cap models them as a homogeneous array of
@@ -72775,21 +72717,6 @@ method(object({
72775
72717
  */
72776
72718
  ttlSec: union([number().int().positive(), literal("never")]).optional()
72777
72719
  }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
72778
- var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
72779
- providerId: string().min(1),
72780
- displayName: string().min(1),
72781
- configSchema: unknown(),
72782
- nodeLocal: boolean(),
72783
- shouldSaveDiskSpace: literal(true),
72784
- minFreePercent: number().min(0).max(100)
72785
- }), object({
72786
- providerId: string().min(1),
72787
- displayName: string().min(1),
72788
- configSchema: unknown(),
72789
- nodeLocal: boolean(),
72790
- shouldSaveDiskSpace: literal(false),
72791
- minFreePercent: literal(null)
72792
- })]);
72793
72720
  method(object({
72794
72721
  location: StorageLocationRefSchema,
72795
72722
  relativePath: string()
@@ -72851,42 +72778,7 @@ method(object({
72851
72778
  }), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
72852
72779
  kind: "mutation",
72853
72780
  auth: "admin"
72854
- }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
72855
- createdAt: true,
72856
- updatedAt: true
72857
- }), StorageLocationSchema, {
72858
- kind: "mutation",
72859
- auth: "admin"
72860
- }), method(object({
72861
- id: string(),
72862
- force: boolean().optional()
72863
- }), _void(), {
72864
- kind: "mutation",
72865
- auth: "admin"
72866
- }), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
72867
- ok: boolean(),
72868
- error: string().optional()
72869
- }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
72870
- providerId: string(),
72871
- /**
72872
- * The location this config is an UNSAVED edit of, when there is one.
72873
- *
72874
- * `listLocations` replaces every declared secret with the redaction
72875
- * sentinel, so the edit modal's form state holds the sentinel for any
72876
- * credential the operator did not retype — and posting that here
72877
- * without a way to resolve it makes the provider try to authenticate
72878
- * as `__camstack_redacted__` and report the operator's own working
72879
- * password as wrong. Given this id, the orchestrator restores each
72880
- * sentinel from the stored config (same rule as `upsertLocation`)
72881
- * before dispatching. Omitted by the "Add location" wizard, where
72882
- * every value was typed just now and nothing is stored yet.
72883
- */
72884
- locationId: string().optional(),
72885
- config: record(string(), unknown())
72886
- }), object({
72887
- ok: boolean(),
72888
- error: string().optional()
72889
- }), { auth: "admin" });
72781
+ }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
72890
72782
  /** One table of the settings database. */
72891
72783
  var StorageTableSchema = object({
72892
72784
  /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
@@ -80479,13 +80371,6 @@ Object.freeze({
80479
80371
  access: "delete",
80480
80372
  role: "admin"
80481
80373
  },
80482
- "llm.deleteProfile": {
80483
- capName: "llm",
80484
- capScope: "system",
80485
- addonId: null,
80486
- access: "delete",
80487
- role: "admin"
80488
- },
80489
80374
  "llm.generate": {
80490
80375
  capName: "llm",
80491
80376
  capScope: "system",
@@ -80549,20 +80434,6 @@ Object.freeze({
80549
80434
  access: "view",
80550
80435
  role: "admin"
80551
80436
  },
80552
- "llm.listProfileKinds": {
80553
- capName: "llm",
80554
- capScope: "system",
80555
- addonId: null,
80556
- access: "view",
80557
- role: "admin"
80558
- },
80559
- "llm.listProfiles": {
80560
- capName: "llm",
80561
- capScope: "system",
80562
- addonId: null,
80563
- access: "view",
80564
- role: "admin"
80565
- },
80566
80437
  "llm.listRuntimeNodes": {
80567
80438
  capName: "llm",
80568
80439
  capScope: "system",
@@ -80570,13 +80441,6 @@ Object.freeze({
80570
80441
  access: "view",
80571
80442
  role: "admin"
80572
80443
  },
80573
- "llm.migrateRawSecrets": {
80574
- capName: "llm",
80575
- capScope: "system",
80576
- addonId: null,
80577
- access: "create",
80578
- role: "admin"
80579
- },
80580
80444
  "llm.resolveModelRef": {
80581
80445
  capName: "llm",
80582
80446
  capScope: "system",
@@ -80605,20 +80469,6 @@ Object.freeze({
80605
80469
  access: "create",
80606
80470
  role: "admin"
80607
80471
  },
80608
- "llm.testProfile": {
80609
- capName: "llm",
80610
- capScope: "system",
80611
- addonId: null,
80612
- access: "create",
80613
- role: "admin"
80614
- },
80615
- "llm.upsertProfile": {
80616
- capName: "llm",
80617
- capScope: "system",
80618
- addonId: null,
80619
- access: "create",
80620
- role: "admin"
80621
- },
80622
80472
  "llmRuntime.appleAvailability": {
80623
80473
  capName: "llm-runtime",
80624
80474
  capScope: "system",
@@ -81123,13 +80973,6 @@ Object.freeze({
81123
80973
  access: "create",
81124
80974
  role: "admin"
81125
80975
  },
81126
- "meshNetwork.testConnection": {
81127
- capName: "mesh-network",
81128
- capScope: "system",
81129
- addonId: null,
81130
- access: "create",
81131
- role: "admin"
81132
- },
81133
80976
  "meter.getStatus": {
81134
80977
  capName: "meter",
81135
80978
  capScope: "device",
@@ -81788,13 +81631,6 @@ Object.freeze({
81788
81631
  access: "create",
81789
81632
  role: "admin"
81790
81633
  },
81791
- "notificationOutput.deleteTarget": {
81792
- capName: "notification-output",
81793
- capScope: "system",
81794
- addonId: null,
81795
- access: "delete",
81796
- role: "admin"
81797
- },
81798
81634
  "notificationOutput.discoverTargets": {
81799
81635
  capName: "notification-output",
81800
81636
  capScope: "system",
@@ -81802,13 +81638,6 @@ Object.freeze({
81802
81638
  access: "view",
81803
81639
  role: "admin"
81804
81640
  },
81805
- "notificationOutput.getTarget": {
81806
- capName: "notification-output",
81807
- capScope: "system",
81808
- addonId: null,
81809
- access: "view",
81810
- role: "admin"
81811
- },
81812
81641
  "notificationOutput.listTargetKinds": {
81813
81642
  capName: "notification-output",
81814
81643
  capScope: "system",
@@ -81816,13 +81645,6 @@ Object.freeze({
81816
81645
  access: "view",
81817
81646
  role: "admin"
81818
81647
  },
81819
- "notificationOutput.listTargets": {
81820
- capName: "notification-output",
81821
- capScope: "system",
81822
- addonId: null,
81823
- access: "view",
81824
- role: "admin"
81825
- },
81826
81648
  "notificationOutput.probeTargetConfig": {
81827
81649
  capName: "notification-output",
81828
81650
  capScope: "system",
@@ -81837,13 +81659,6 @@ Object.freeze({
81837
81659
  access: "create",
81838
81660
  role: "admin"
81839
81661
  },
81840
- "notificationOutput.setTargetEnabled": {
81841
- capName: "notification-output",
81842
- capScope: "system",
81843
- addonId: null,
81844
- access: "create",
81845
- role: "admin"
81846
- },
81847
81662
  "notificationOutput.testTarget": {
81848
81663
  capName: "notification-output",
81849
81664
  capScope: "system",
@@ -81851,13 +81666,6 @@ Object.freeze({
81851
81666
  access: "create",
81852
81667
  role: "admin"
81853
81668
  },
81854
- "notificationOutput.upsertTarget": {
81855
- capName: "notification-output",
81856
- capScope: "system",
81857
- addonId: null,
81858
- access: "create",
81859
- role: "admin"
81860
- },
81861
81669
  "notificationRules.activateAlarmProfile": {
81862
81670
  capName: "notification-rules",
81863
81671
  capScope: "system",
@@ -82117,41 +81925,6 @@ Object.freeze({
82117
81925
  access: "view",
82118
81926
  role: "admin"
82119
81927
  },
82120
- "oidcConnections.connectionFormSchema": {
82121
- capName: "oidc-connections",
82122
- capScope: "system",
82123
- addonId: null,
82124
- access: "view",
82125
- role: "admin"
82126
- },
82127
- "oidcConnections.createConnection": {
82128
- capName: "oidc-connections",
82129
- capScope: "system",
82130
- addonId: null,
82131
- access: "create",
82132
- role: "admin"
82133
- },
82134
- "oidcConnections.deleteConnection": {
82135
- capName: "oidc-connections",
82136
- capScope: "system",
82137
- addonId: null,
82138
- access: "delete",
82139
- role: "admin"
82140
- },
82141
- "oidcConnections.listConnections": {
82142
- capName: "oidc-connections",
82143
- capScope: "system",
82144
- addonId: null,
82145
- access: "view",
82146
- role: "admin"
82147
- },
82148
- "oidcConnections.updateConnection": {
82149
- capName: "oidc-connections",
82150
- capScope: "system",
82151
- addonId: null,
82152
- access: "create",
82153
- role: "admin"
82154
- },
82155
81928
  "osd.getStatus": {
82156
81929
  capName: "osd",
82157
81930
  capScope: "device",
@@ -85183,13 +84956,6 @@ Object.freeze({
85183
84956
  access: "delete",
85184
84957
  role: "admin"
85185
84958
  },
85186
- "storage.deleteLocation": {
85187
- capName: "storage",
85188
- capScope: "system",
85189
- addonId: null,
85190
- access: "delete",
85191
- role: "admin"
85192
- },
85193
84959
  "storage.endDownload": {
85194
84960
  capName: "storage",
85195
84961
  capScope: "system",
@@ -85246,13 +85012,6 @@ Object.freeze({
85246
85012
  access: "view",
85247
85013
  role: "admin"
85248
85014
  },
85249
- "storage.listProviders": {
85250
- capName: "storage",
85251
- capScope: "system",
85252
- addonId: null,
85253
- access: "view",
85254
- role: "admin"
85255
- },
85256
85015
  "storage.read": {
85257
85016
  capName: "storage",
85258
85017
  capScope: "system",
@@ -85274,27 +85033,6 @@ Object.freeze({
85274
85033
  access: "view",
85275
85034
  role: "admin"
85276
85035
  },
85277
- "storage.testConfig": {
85278
- capName: "storage",
85279
- capScope: "system",
85280
- addonId: null,
85281
- access: "view",
85282
- role: "admin"
85283
- },
85284
- "storage.testLocation": {
85285
- capName: "storage",
85286
- capScope: "system",
85287
- addonId: null,
85288
- access: "view",
85289
- role: "admin"
85290
- },
85291
- "storage.upsertLocation": {
85292
- capName: "storage",
85293
- capScope: "system",
85294
- addonId: null,
85295
- access: "create",
85296
- role: "admin"
85297
- },
85298
85036
  "storage.write": {
85299
85037
  capName: "storage",
85300
85038
  capScope: "system",
@@ -91847,222 +91585,6 @@ var GoogleBrokerRegistry = class {
91847
91585
  }
91848
91586
  };
91849
91587
  //#endregion
91850
- //#region src/google-settings-copy.ts
91851
- /**
91852
- * Operator-facing setup copy for Connectivity → Calendars / Email.
91853
- * Keep Connect as a same-origin relative link so the admin session cookie
91854
- * rides along (an absolute Public URL from a LAN admin tab drops the cookie).
91855
- * Root-relative hrefs must survive `sanitizeInfoHtml` in ui-library.
91856
- */
91857
- /** Enable APIs library in Google Cloud Console. */
91858
- var GOOGLE_CONSOLE_APIS_URL = "https://console.cloud.google.com/apis/library";
91859
- /** Create / manage OAuth Web clients. */
91860
- var GOOGLE_CONSOLE_CREDENTIALS_URL = "https://console.cloud.google.com/apis/credentials";
91861
- function redirectUriDisplay(publicOrigin) {
91862
- if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
91863
- return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
91864
- }
91865
- function consoleLinksHtml$1() {
91866
- return [
91867
- `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
91868
- ` · `,
91869
- `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
91870
- ].join("");
91871
- }
91872
- function introHtml(cap, publicOrigin) {
91873
- const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
91874
- const console = consoleLinksHtml$1();
91875
- if (cap === "smtp-provider") return [
91876
- "<ol>",
91877
- `<li>Open Google Cloud Console (${console}) and enable the <strong>Gmail API</strong> (and Calendar API if you also use Calendars).</li>`,
91878
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91879
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91880
- "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
91881
- `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
91882
- "<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>",
91883
- "</ol>"
91884
- ].join("");
91885
- if (cap === "calendar-source") return [
91886
- "<ol>",
91887
- `<li>Open Google Cloud Console (${console}) and enable the <strong>Google Calendar API</strong> (and Gmail API if you also use Email).</li>`,
91888
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91889
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91890
- "<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
91891
- `<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
91892
- "<li>Optionally restrict which calendars are exposed with the filter below.</li>",
91893
- "</ol>"
91894
- ].join("");
91895
- return [
91896
- "<ol>",
91897
- `<li>Open Google Cloud Console (${console}) and enable <strong>Google Calendar API</strong> and <strong>Gmail API</strong>.</li>`,
91898
- "<li>Create a <strong>Web application</strong> OAuth client under Credentials.</li>",
91899
- `<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
91900
- `<li>Paste Client ID / secret, Save, then <a href="${OAUTH_START_PATH}">Connect Google</a>.</li>`,
91901
- "<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>",
91902
- "</ol>"
91903
- ].join("");
91904
- }
91905
- function connectHtml(cap, publicOrigin) {
91906
- const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
91907
- if (cap === "smtp-provider") return [
91908
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
91909
- "Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
91910
- `Google must list exactly: <code>${redirect}</code>. `,
91911
- "If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again. ",
91912
- `Console: ${consoleLinksHtml$1()}.`
91913
- ].join("");
91914
- if (cap === "calendar-source") return [
91915
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
91916
- "Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
91917
- `Google must list exactly: <code>${redirect}</code>. `,
91918
- `Console: ${consoleLinksHtml$1()}.`
91919
- ].join("");
91920
- return [
91921
- `After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `,
91922
- `Redirect URI registered in Google Cloud must be <code>${redirect}</code>. `,
91923
- `Console: ${consoleLinksHtml$1()}.`
91924
- ].join("");
91925
- }
91926
- function escapeHtml$1(s) {
91927
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
91928
- }
91929
- //#endregion
91930
- //#region src/google-broker-settings-schema.ts
91931
- /**
91932
- * Per-kind broker creation/edit form for kind=`google`.
91933
- * Account linking (Sign in) is driven by the kind's `connectPath`
91934
- * on the instances page — not a hard-coded Connect link in this form.
91935
- */
91936
- function consoleLinksHtml() {
91937
- return [
91938
- `<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
91939
- ` · `,
91940
- `<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
91941
- ].join("");
91942
- }
91943
- /** Setup steps shown on Add / Edit — includes Audience / Test users. */
91944
- function googleBrokerSetupHtml(publicOrigin) {
91945
- const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
91946
- return [
91947
- "<ol>",
91948
- `<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>`,
91949
- "<li>In that project, enable <strong>Google Calendar API</strong> and/or <strong>Gmail API</strong> (API library).</li>",
91950
- "<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>",
91951
- "<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
91952
- `<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
91953
- "<li>Paste Client ID and Client secret below. <strong>Test connection</strong> checks those with Google (not the account yet).</li>",
91954
- "<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>",
91955
- "</ol>"
91956
- ].join("");
91957
- }
91958
- function buildGoogleBrokerSettingsSchema(publicOrigin) {
91959
- return { sections: [
91960
- {
91961
- id: "setup-intro",
91962
- title: "Setup",
91963
- titleKey: "google-services:googleBrokerSettings.s_setup-intro.title",
91964
- description: "Google Cloud Console — register the hub redirect before Add / Sign in.",
91965
- descriptionKey: "google-services:googleBrokerSettings.s_setup-intro.description",
91966
- columns: 1,
91967
- fields: [{
91968
- type: "info",
91969
- key: "_setupSteps",
91970
- label: "Steps",
91971
- labelKey: "google-services:googleBrokerSettings.s_setup-intro._setupSteps.label",
91972
- format: "html",
91973
- content: googleBrokerSetupHtml(publicOrigin)
91974
- }]
91975
- },
91976
- {
91977
- id: "google-oauth-client",
91978
- title: "Google OAuth client",
91979
- titleKey: "google-services:googleBrokerSettings.s_google-oauth-client.title",
91980
- description: "Web application client from Google Cloud Console. The same Cloud project can be reused across Google brokers.",
91981
- descriptionKey: "google-services:googleBrokerSettings.s_google-oauth-client.description",
91982
- columns: 1,
91983
- fields: [{
91984
- type: "text",
91985
- key: "clientId",
91986
- label: "Client ID",
91987
- labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientId.label"
91988
- }, {
91989
- type: "password",
91990
- key: "clientSecret",
91991
- label: "Client secret",
91992
- labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientSecret.label",
91993
- showToggle: true
91994
- }]
91995
- },
91996
- {
91997
- id: "gmail-send",
91998
- title: "Gmail send",
91999
- titleKey: "google-services:googleBrokerSettings.s_gmail-send.title",
92000
- description: "From must be this connected account or a verified send-as. Disable Email on Connectivity to turn Gmail off without removing the broker.",
92001
- descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.description",
92002
- columns: 1,
92003
- fields: [{
92004
- type: "text",
92005
- key: "fromAddress",
92006
- label: "From address",
92007
- labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.label",
92008
- description: "Defaults to the connected Google account email when empty.",
92009
- descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.description"
92010
- }, {
92011
- type: "text",
92012
- key: "fromName",
92013
- label: "From display name (optional)",
92014
- labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromName.label"
92015
- }]
92016
- }
92017
- ] };
92018
- }
92019
- /** Connectivity Calendars / Email pages — the calendar import picker is on each broker. */
92020
- function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
92021
- const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
92022
- if (cap === "calendar-source") return { sections: [{
92023
- id: "setup-intro",
92024
- title: "Google brokers",
92025
- titleKey: "google-services:googleFeatureSettings.s_setup-intro.title",
92026
- columns: 1,
92027
- fields: [{
92028
- type: "info",
92029
- key: "_setupSteps",
92030
- label: "Setup",
92031
- labelKey: "google-services:googleFeatureSettings.s_setup-intro._setupSteps.label",
92032
- format: "html",
92033
- content: [
92034
- "<p>Link a Google account under Connectivity → <strong>Brokers</strong> ",
92035
- "(Client ID / secret + Sign in), then add a calendar under Connectivity → <strong>Calendars</strong>. ",
92036
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92037
- ].join("")
92038
- }]
92039
- }] };
92040
- return { sections: [{
92041
- id: "setup-intro",
92042
- title: cap === "smtp-provider" ? "Gmail send uses Google brokers" : "Google brokers",
92043
- columns: 1,
92044
- fields: [{
92045
- type: "info",
92046
- key: "_setupSteps",
92047
- label: "Setup",
92048
- labelKey: "google-services:googleFeatureSettings._setupSteps.label",
92049
- format: "html",
92050
- content: cap === "smtp-provider" ? [
92051
- "<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
92052
- "(Client ID / secret + Sign in). From address and send-as live on that broker. ",
92053
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92054
- ].join("") : [
92055
- "<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
92056
- "(Client ID / secret + Sign in). ",
92057
- `OAuth redirect URI: <code>${redirect}</code>.</p>`
92058
- ].join("")
92059
- }]
92060
- }] };
92061
- }
92062
- function escapeHtml(s) {
92063
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
92064
- }
92065
- //#endregion
92066
91588
  //#region src/google-calendar-source.ts
92067
91589
  function parseGoogleInstant(value) {
92068
91590
  const ms = Date.parse(value);
@@ -92583,6 +92105,179 @@ function buildGoogleCalendarInstances(records, deps) {
92583
92105
  });
92584
92106
  }
92585
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
92586
92281
  //#region src/google-instances.ts
92587
92282
  /**
92588
92283
  * The Google accounts as `integration-instances` (planning#408 P3): one instance per broker
@@ -93869,22 +93564,7 @@ var GoogleServicesAddon = class extends BaseAddon {
93869
93564
  this.publicOrigin = null;
93870
93565
  }
93871
93566
  }
93872
- /**
93873
- * Connectivity Calendars / Email: point at Brokers (no duplicate OAuth fields).
93874
- * Addon page without cap: same pointer + note.
93875
- */
93876
- globalSettingsSchema(cap) {
93877
- const scoped = asSettingsCap(cap);
93878
- return this.schema(buildGoogleFeatureSettingsSchema(scoped, this.publicOrigin));
93879
- }
93880
- async getGlobalSettings(overlay, cap, nodeId) {
93881
- await this.refreshPublicOrigin();
93882
- return super.getGlobalSettings(overlay, cap, nodeId);
93883
- }
93884
93567
  };
93885
- function asSettingsCap(cap) {
93886
- if (cap === "calendar-source" || cap === "smtp-provider") return cap;
93887
- }
93888
93568
  //#endregion
93889
93569
  exports.DEFAULT_GOOGLE_CONFIG = DEFAULT_GOOGLE_CONFIG;
93890
93570
  exports.GOOGLE_BROKER_KIND = GOOGLE_BROKER_KIND;