@camstack/types 1.2.346 → 1.2.348
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/calendar-source.cap.d.ts +1 -0
- package/dist/capabilities/capability-definition.d.ts +3 -2
- package/dist/capabilities/training-provider.cap.d.ts +8 -19
- package/dist/generated/provider-kind-map.d.ts +1 -1
- package/dist/index.js +24 -22
- package/dist/index.mjs +24 -22
- package/package.json +1 -1
|
@@ -46,6 +46,7 @@ export declare const calendarSourceCapability: {
|
|
|
46
46
|
readonly scope: 'system';
|
|
47
47
|
readonly mode: 'collection';
|
|
48
48
|
readonly internal: true;
|
|
49
|
+
readonly providerKind: 'calendar';
|
|
49
50
|
readonly methods: {
|
|
50
51
|
readonly listCalendars: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
51
52
|
id: z.ZodString;
|
|
@@ -61,11 +61,12 @@ export type CapabilityMethodCaller = 'required';
|
|
|
61
61
|
*
|
|
62
62
|
* - `broker` — message brokers (MQTT, future Kafka)
|
|
63
63
|
* - `device-export` — outbound exporters (HomeKit, Alexa, HA-MQTT)
|
|
64
|
-
* - `email` — email delivery relays (SMTP,
|
|
64
|
+
* - `email` — email delivery relays (SMTP, Gmail API, …)
|
|
65
|
+
* - `calendar` — calendar sources (Google Calendar, waste, HA, …)
|
|
65
66
|
* - `mesh` — mesh-VPN providers (Tailscale, Headscale, …)
|
|
66
67
|
* - `ingress` — public-ingress tunnels (Cloudflare Tunnel, …)
|
|
67
68
|
*/
|
|
68
|
-
export type ProviderKind = 'broker' | 'device-export' | 'email' | 'mesh' | 'ingress' | 'ai';
|
|
69
|
+
export type ProviderKind = 'broker' | 'device-export' | 'email' | 'calendar' | 'mesh' | 'ingress' | 'ai';
|
|
69
70
|
/**
|
|
70
71
|
* Access flavour for a capability method. Drives the scope-access check
|
|
71
72
|
* in `protectedProcedure`. Default inferred at codegen time:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import type { ConfigUISchema } from '../interfaces/config-ui.js';
|
|
2
3
|
import { type InferProvider } from './capability-definition.js';
|
|
3
4
|
/**
|
|
4
5
|
* `training-provider` — ONE cloud that can run the CamStack trainer.
|
|
@@ -56,8 +57,11 @@ export declare const TrainingProviderTypeSchema: z.ZodObject<{
|
|
|
56
57
|
label: z.ZodString;
|
|
57
58
|
iconAsset: z.ZodString;
|
|
58
59
|
addonId: z.ZodString;
|
|
60
|
+
configSchema: z.ZodOptional<z.ZodUnknown>;
|
|
59
61
|
}, z.core.$strip>;
|
|
60
|
-
export type TrainingProviderType = z.infer<typeof TrainingProviderTypeSchema
|
|
62
|
+
export type TrainingProviderType = Omit<z.infer<typeof TrainingProviderTypeSchema>, 'configSchema'> & {
|
|
63
|
+
readonly configSchema?: ConfigUISchema;
|
|
64
|
+
};
|
|
61
65
|
/** One configured account / workspace under a provider type. */
|
|
62
66
|
export declare const TrainingProviderEntrySchema: z.ZodObject<{
|
|
63
67
|
entryId: z.ZodString;
|
|
@@ -98,15 +102,7 @@ export declare const TrainingProviderDescriptorSchema: z.ZodObject<{
|
|
|
98
102
|
maxRuntimeSec: z.ZodNumber;
|
|
99
103
|
failedDatasetGraceHours: z.ZodNumber;
|
|
100
104
|
}, z.core.$strip>;
|
|
101
|
-
|
|
102
|
-
environment: z.ZodOptional<z.ZodString>;
|
|
103
|
-
hasTokenSecret: z.ZodOptional<z.ZodBoolean>;
|
|
104
|
-
tokenSecretView: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
105
|
-
$secret: z.ZodNullable<z.ZodString>;
|
|
106
|
-
v: z.ZodLiteral<1>;
|
|
107
|
-
set: z.ZodLiteral<true>;
|
|
108
|
-
inline: z.ZodOptional<z.ZodLiteral<true>>;
|
|
109
|
-
}, z.core.$strip>>>;
|
|
105
|
+
formValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
110
106
|
}, z.core.$strip>;
|
|
111
107
|
export type TrainingProviderDescriptor = z.infer<typeof TrainingProviderDescriptorSchema>;
|
|
112
108
|
/**
|
|
@@ -230,15 +226,7 @@ export declare const trainingProviderCapability: {
|
|
|
230
226
|
maxRuntimeSec: z.ZodNumber;
|
|
231
227
|
failedDatasetGraceHours: z.ZodNumber;
|
|
232
228
|
}, z.core.$strip>;
|
|
233
|
-
|
|
234
|
-
environment: z.ZodOptional<z.ZodString>;
|
|
235
|
-
hasTokenSecret: z.ZodOptional<z.ZodBoolean>;
|
|
236
|
-
tokenSecretView: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
237
|
-
$secret: z.ZodNullable<z.ZodString>;
|
|
238
|
-
v: z.ZodLiteral<1>;
|
|
239
|
-
set: z.ZodLiteral<true>;
|
|
240
|
-
inline: z.ZodOptional<z.ZodLiteral<true>>;
|
|
241
|
-
}, z.core.$strip>>>;
|
|
229
|
+
formValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
242
230
|
}, z.core.$strip>>>, import("./capability-definition.js").CapabilityMethodKind>;
|
|
243
231
|
/** Creatable types for the Add-entry dialog. Concat-fans. */
|
|
244
232
|
readonly describeTypes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{}, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
@@ -246,6 +234,7 @@ export declare const trainingProviderCapability: {
|
|
|
246
234
|
label: z.ZodString;
|
|
247
235
|
iconAsset: z.ZodString;
|
|
248
236
|
addonId: z.ZodString;
|
|
237
|
+
configSchema: z.ZodOptional<z.ZodUnknown>;
|
|
249
238
|
}, z.core.$strip>>>, import("./capability-definition.js").CapabilityMethodKind>;
|
|
250
239
|
/** Configured entries (pills). Concat-fans. */
|
|
251
240
|
readonly listEntries: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{}, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `providerKind` set lights up its section on that page automatically
|
|
9
9
|
* — no manual registry edit required.
|
|
10
10
|
*
|
|
11
|
-
* Coverage:
|
|
11
|
+
* Coverage: 8 capabilities tagged with providerKind.
|
|
12
12
|
*/
|
|
13
13
|
import type { ProviderKind } from '../capabilities/capability-definition.js';
|
|
14
14
|
export declare const CAP_PROVIDER_KIND_MAP: Readonly<Record<string, ProviderKind>>;
|
package/dist/index.js
CHANGED
|
@@ -17814,14 +17814,14 @@ var LlmProfileSchema = zod.z.object({
|
|
|
17814
17814
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
17815
17815
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
17816
17816
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
17817
|
-
var ConfigSchemaPassthrough$
|
|
17817
|
+
var ConfigSchemaPassthrough$2 = zod.z.unknown();
|
|
17818
17818
|
var LlmProfileKindDescriptorSchema = zod.z.object({
|
|
17819
17819
|
kind: LlmProfileKindSchema,
|
|
17820
17820
|
label: zod.z.string(),
|
|
17821
17821
|
icon: zod.z.string(),
|
|
17822
17822
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
17823
17823
|
addonId: zod.z.string(),
|
|
17824
|
-
configSchema: ConfigSchemaPassthrough$
|
|
17824
|
+
configSchema: ConfigSchemaPassthrough$2
|
|
17825
17825
|
});
|
|
17826
17826
|
var LlmDefaultSelectorSchema = zod.z.union([zod.z.object({ consumer: zod.z.string() }), zod.z.object({ purpose: zod.z.enum(["text", "vision"]) })]);
|
|
17827
17827
|
var LlmDefaultSchema = zod.z.object({
|
|
@@ -19321,7 +19321,7 @@ var TargetKindCapsSchema = zod.z.object({
|
|
|
19321
19321
|
* the union is large and not meant for runtime validation here; the exported
|
|
19322
19322
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
19323
19323
|
*/
|
|
19324
|
-
var ConfigSchemaPassthrough = zod.z.unknown();
|
|
19324
|
+
var ConfigSchemaPassthrough$1 = zod.z.unknown();
|
|
19325
19325
|
var TargetKindSchema = zod.z.object({
|
|
19326
19326
|
kind: zod.z.string(),
|
|
19327
19327
|
label: zod.z.string(),
|
|
@@ -19358,7 +19358,7 @@ var TargetKindSchema = zod.z.object({
|
|
|
19358
19358
|
* its raster path, which is the safe default for an unknown image.
|
|
19359
19359
|
*/
|
|
19360
19360
|
iconMediaType: zod.z.string().optional(),
|
|
19361
|
-
configSchema: ConfigSchemaPassthrough,
|
|
19361
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
19362
19362
|
/**
|
|
19363
19363
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
19364
19364
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -37233,6 +37233,7 @@ var calendarSourceCapability = {
|
|
|
37233
37233
|
scope: "system",
|
|
37234
37234
|
mode: "collection",
|
|
37235
37235
|
internal: true,
|
|
37236
|
+
providerKind: "calendar",
|
|
37236
37237
|
methods: {
|
|
37237
37238
|
listCalendars: require_sleep.method(zod.z.void(), zod.z.array(CalendarInfoSchema).readonly(), { auth: "admin" }),
|
|
37238
37239
|
listEvents: require_sleep.method(CalendarListEventsInputSchema, zod.z.array(CalendarEventSchema).readonly(), { auth: "admin" })
|
|
@@ -49972,6 +49973,12 @@ var TrainingProviderDefaultsSchema = zod.z.object({
|
|
|
49972
49973
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
49973
49974
|
failedDatasetGraceHours: zod.z.number().nonnegative()
|
|
49974
49975
|
});
|
|
49976
|
+
/**
|
|
49977
|
+
* Entry form schema for a provider type — same seam as notification
|
|
49978
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
49979
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
49980
|
+
*/
|
|
49981
|
+
var ConfigSchemaPassthrough = zod.z.unknown();
|
|
49975
49982
|
/** One creatable provider type, for the Add-entry dialog. */
|
|
49976
49983
|
var TrainingProviderTypeSchema = zod.z.object({
|
|
49977
49984
|
typeId: zod.z.string(),
|
|
@@ -49979,7 +49986,13 @@ var TrainingProviderTypeSchema = zod.z.object({
|
|
|
49979
49986
|
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
49980
49987
|
iconAsset: zod.z.string(),
|
|
49981
49988
|
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
49982
|
-
addonId: zod.z.string()
|
|
49989
|
+
addonId: zod.z.string(),
|
|
49990
|
+
/**
|
|
49991
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
49992
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
49993
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
49994
|
+
*/
|
|
49995
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
49983
49996
|
});
|
|
49984
49997
|
/** One configured account / workspace under a provider type. */
|
|
49985
49998
|
var TrainingProviderEntrySchema = zod.z.object({
|
|
@@ -50011,24 +50024,11 @@ var TrainingProviderDescriptorSchema = zod.z.object({
|
|
|
50011
50024
|
configured: zod.z.boolean(),
|
|
50012
50025
|
defaults: TrainingProviderDefaultsSchema,
|
|
50013
50026
|
/**
|
|
50014
|
-
*
|
|
50015
|
-
*
|
|
50016
|
-
*
|
|
50017
|
-
*/
|
|
50018
|
-
tokenId: zod.z.string().optional(),
|
|
50019
|
-
environment: zod.z.string().optional(),
|
|
50020
|
-
hasTokenSecret: zod.z.boolean().optional(),
|
|
50021
|
-
/**
|
|
50022
|
-
* Keychain view for the entry's token secret — for `SecretFieldControl` on
|
|
50023
|
-
* edit. Create has no view (`null` / omitted); the form uses a plain
|
|
50024
|
-
* `PasswordInput` instead.
|
|
50027
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
50028
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
50029
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
50025
50030
|
*/
|
|
50026
|
-
|
|
50027
|
-
$secret: zod.z.string().nullable(),
|
|
50028
|
-
v: zod.z.literal(1),
|
|
50029
|
-
set: zod.z.literal(true),
|
|
50030
|
-
inline: zod.z.literal(true).optional()
|
|
50031
|
-
}).nullable().optional()
|
|
50031
|
+
formValues: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
50032
50032
|
});
|
|
50033
50033
|
/**
|
|
50034
50034
|
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
@@ -69453,6 +69453,7 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
|
|
|
69453
69453
|
//#region src/generated/provider-kind-map.ts
|
|
69454
69454
|
var CAP_PROVIDER_KIND_MAP = Object.freeze({
|
|
69455
69455
|
"broker": "broker",
|
|
69456
|
+
"calendar-source": "calendar",
|
|
69456
69457
|
"device-export": "device-export",
|
|
69457
69458
|
"llm": "ai",
|
|
69458
69459
|
"mesh-network": "mesh",
|
|
@@ -69463,6 +69464,7 @@ var CAP_PROVIDER_KIND_MAP = Object.freeze({
|
|
|
69463
69464
|
/** Every cap name tagged with a `providerKind`. */
|
|
69464
69465
|
var PROVIDER_KIND_CAP_NAMES = [
|
|
69465
69466
|
"broker",
|
|
69467
|
+
"calendar-source",
|
|
69466
69468
|
"device-export",
|
|
69467
69469
|
"llm",
|
|
69468
69470
|
"mesh-network",
|
package/dist/index.mjs
CHANGED
|
@@ -17813,14 +17813,14 @@ var LlmProfileSchema = z.object({
|
|
|
17813
17813
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
17814
17814
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
17815
17815
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
17816
|
-
var ConfigSchemaPassthrough$
|
|
17816
|
+
var ConfigSchemaPassthrough$2 = z.unknown();
|
|
17817
17817
|
var LlmProfileKindDescriptorSchema = z.object({
|
|
17818
17818
|
kind: LlmProfileKindSchema,
|
|
17819
17819
|
label: z.string(),
|
|
17820
17820
|
icon: z.string(),
|
|
17821
17821
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
17822
17822
|
addonId: z.string(),
|
|
17823
|
-
configSchema: ConfigSchemaPassthrough$
|
|
17823
|
+
configSchema: ConfigSchemaPassthrough$2
|
|
17824
17824
|
});
|
|
17825
17825
|
var LlmDefaultSelectorSchema = z.union([z.object({ consumer: z.string() }), z.object({ purpose: z.enum(["text", "vision"]) })]);
|
|
17826
17826
|
var LlmDefaultSchema = z.object({
|
|
@@ -19320,7 +19320,7 @@ var TargetKindCapsSchema = z.object({
|
|
|
19320
19320
|
* the union is large and not meant for runtime validation here; the exported
|
|
19321
19321
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
19322
19322
|
*/
|
|
19323
|
-
var ConfigSchemaPassthrough = z.unknown();
|
|
19323
|
+
var ConfigSchemaPassthrough$1 = z.unknown();
|
|
19324
19324
|
var TargetKindSchema = z.object({
|
|
19325
19325
|
kind: z.string(),
|
|
19326
19326
|
label: z.string(),
|
|
@@ -19357,7 +19357,7 @@ var TargetKindSchema = z.object({
|
|
|
19357
19357
|
* its raster path, which is the safe default for an unknown image.
|
|
19358
19358
|
*/
|
|
19359
19359
|
iconMediaType: z.string().optional(),
|
|
19360
|
-
configSchema: ConfigSchemaPassthrough,
|
|
19360
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
19361
19361
|
/**
|
|
19362
19362
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
19363
19363
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -37232,6 +37232,7 @@ var calendarSourceCapability = {
|
|
|
37232
37232
|
scope: "system",
|
|
37233
37233
|
mode: "collection",
|
|
37234
37234
|
internal: true,
|
|
37235
|
+
providerKind: "calendar",
|
|
37235
37236
|
methods: {
|
|
37236
37237
|
listCalendars: method(z.void(), z.array(CalendarInfoSchema).readonly(), { auth: "admin" }),
|
|
37237
37238
|
listEvents: method(CalendarListEventsInputSchema, z.array(CalendarEventSchema).readonly(), { auth: "admin" })
|
|
@@ -49971,6 +49972,12 @@ var TrainingProviderDefaultsSchema = z.object({
|
|
|
49971
49972
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
49972
49973
|
failedDatasetGraceHours: z.number().nonnegative()
|
|
49973
49974
|
});
|
|
49975
|
+
/**
|
|
49976
|
+
* Entry form schema for a provider type — same seam as notification
|
|
49977
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
49978
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
49979
|
+
*/
|
|
49980
|
+
var ConfigSchemaPassthrough = z.unknown();
|
|
49974
49981
|
/** One creatable provider type, for the Add-entry dialog. */
|
|
49975
49982
|
var TrainingProviderTypeSchema = z.object({
|
|
49976
49983
|
typeId: z.string(),
|
|
@@ -49978,7 +49985,13 @@ var TrainingProviderTypeSchema = z.object({
|
|
|
49978
49985
|
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
49979
49986
|
iconAsset: z.string(),
|
|
49980
49987
|
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
49981
|
-
addonId: z.string()
|
|
49988
|
+
addonId: z.string(),
|
|
49989
|
+
/**
|
|
49990
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
49991
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
49992
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
49993
|
+
*/
|
|
49994
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
49982
49995
|
});
|
|
49983
49996
|
/** One configured account / workspace under a provider type. */
|
|
49984
49997
|
var TrainingProviderEntrySchema = z.object({
|
|
@@ -50010,24 +50023,11 @@ var TrainingProviderDescriptorSchema = z.object({
|
|
|
50010
50023
|
configured: z.boolean(),
|
|
50011
50024
|
defaults: TrainingProviderDefaultsSchema,
|
|
50012
50025
|
/**
|
|
50013
|
-
*
|
|
50014
|
-
*
|
|
50015
|
-
*
|
|
50016
|
-
*/
|
|
50017
|
-
tokenId: z.string().optional(),
|
|
50018
|
-
environment: z.string().optional(),
|
|
50019
|
-
hasTokenSecret: z.boolean().optional(),
|
|
50020
|
-
/**
|
|
50021
|
-
* Keychain view for the entry's token secret — for `SecretFieldControl` on
|
|
50022
|
-
* edit. Create has no view (`null` / omitted); the form uses a plain
|
|
50023
|
-
* `PasswordInput` instead.
|
|
50026
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
50027
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
50028
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
50024
50029
|
*/
|
|
50025
|
-
|
|
50026
|
-
$secret: z.string().nullable(),
|
|
50027
|
-
v: z.literal(1),
|
|
50028
|
-
set: z.literal(true),
|
|
50029
|
-
inline: z.literal(true).optional()
|
|
50030
|
-
}).nullable().optional()
|
|
50030
|
+
formValues: z.record(z.string(), z.unknown()).optional()
|
|
50031
50031
|
});
|
|
50032
50032
|
/**
|
|
50033
50033
|
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
@@ -69452,6 +69452,7 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
|
|
|
69452
69452
|
//#region src/generated/provider-kind-map.ts
|
|
69453
69453
|
var CAP_PROVIDER_KIND_MAP = Object.freeze({
|
|
69454
69454
|
"broker": "broker",
|
|
69455
|
+
"calendar-source": "calendar",
|
|
69455
69456
|
"device-export": "device-export",
|
|
69456
69457
|
"llm": "ai",
|
|
69457
69458
|
"mesh-network": "mesh",
|
|
@@ -69462,6 +69463,7 @@ var CAP_PROVIDER_KIND_MAP = Object.freeze({
|
|
|
69462
69463
|
/** Every cap name tagged with a `providerKind`. */
|
|
69463
69464
|
var PROVIDER_KIND_CAP_NAMES = [
|
|
69464
69465
|
"broker",
|
|
69466
|
+
"calendar-source",
|
|
69465
69467
|
"device-export",
|
|
69466
69468
|
"llm",
|
|
69467
69469
|
"mesh-network",
|