@alexberardi/jarvis-admin 0.1.65 → 0.1.67

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.
Files changed (60) hide show
  1. package/package.json +1 -1
  2. package/public/assets/index-CAv1RM9y.css +1 -0
  3. package/public/assets/index-CPK3ZbaB.js +18 -0
  4. package/public/index.html +2 -2
  5. package/server/dist/app.d.ts.map +1 -1
  6. package/server/dist/app.js +24 -0
  7. package/server/dist/app.js.map +1 -1
  8. package/server/dist/data/image-digests.json +58 -0
  9. package/server/dist/data/service-registry.json +13 -6
  10. package/server/dist/middleware/auth.d.ts +14 -0
  11. package/server/dist/middleware/auth.d.ts.map +1 -1
  12. package/server/dist/middleware/auth.js +19 -0
  13. package/server/dist/middleware/auth.js.map +1 -1
  14. package/server/dist/routes/install.d.ts.map +1 -1
  15. package/server/dist/routes/install.js +35 -8
  16. package/server/dist/routes/install.js.map +1 -1
  17. package/server/dist/routes/nodes.d.ts.map +1 -1
  18. package/server/dist/routes/nodes.js +5 -1
  19. package/server/dist/routes/nodes.js.map +1 -1
  20. package/server/dist/routes/setup.d.ts.map +1 -1
  21. package/server/dist/routes/setup.js +2 -1
  22. package/server/dist/routes/setup.js.map +1 -1
  23. package/server/dist/routes/traces.d.ts.map +1 -1
  24. package/server/dist/routes/traces.js +9 -2
  25. package/server/dist/routes/traces.js.map +1 -1
  26. package/server/dist/services/commandCenter.d.ts +11 -0
  27. package/server/dist/services/commandCenter.d.ts.map +1 -0
  28. package/server/dist/services/commandCenter.js +20 -0
  29. package/server/dist/services/commandCenter.js.map +1 -0
  30. package/server/dist/services/generators/compose-generator.d.ts +11 -1
  31. package/server/dist/services/generators/compose-generator.d.ts.map +1 -1
  32. package/server/dist/services/generators/compose-generator.js +151 -39
  33. package/server/dist/services/generators/compose-generator.js.map +1 -1
  34. package/server/dist/services/generators/env-generator.d.ts.map +1 -1
  35. package/server/dist/services/generators/env-generator.js +17 -2
  36. package/server/dist/services/generators/env-generator.js.map +1 -1
  37. package/server/dist/services/generators/secret-generator.d.ts +1 -1
  38. package/server/dist/services/generators/secret-generator.d.ts.map +1 -1
  39. package/server/dist/services/generators/secret-generator.js +15 -0
  40. package/server/dist/services/generators/secret-generator.js.map +1 -1
  41. package/server/dist/services/upgrade/compose-upgrader.d.ts +47 -0
  42. package/server/dist/services/upgrade/compose-upgrader.d.ts.map +1 -1
  43. package/server/dist/services/upgrade/compose-upgrader.js +122 -33
  44. package/server/dist/services/upgrade/compose-upgrader.js.map +1 -1
  45. package/server/dist/services/upgrade/image-digest-resolver.d.ts +16 -0
  46. package/server/dist/services/upgrade/image-digest-resolver.d.ts.map +1 -0
  47. package/server/dist/services/upgrade/image-digest-resolver.js +80 -0
  48. package/server/dist/services/upgrade/image-digest-resolver.js.map +1 -0
  49. package/server/dist/services/upgrade/minisign.d.ts +53 -0
  50. package/server/dist/services/upgrade/minisign.d.ts.map +1 -0
  51. package/server/dist/services/upgrade/minisign.js +126 -0
  52. package/server/dist/services/upgrade/minisign.js.map +1 -0
  53. package/server/dist/services/upgrade/self-updater.d.ts.map +1 -1
  54. package/server/dist/services/upgrade/self-updater.js +61 -1
  55. package/server/dist/services/upgrade/self-updater.js.map +1 -1
  56. package/server/dist/types/wizard.d.ts +8 -0
  57. package/server/dist/types/wizard.d.ts.map +1 -1
  58. package/server/src/data/service-registry.json +13 -6
  59. package/public/assets/index-DeByHCh9.css +0 -1
  60. package/public/assets/index-DeCnqmbD.js +0 -18
@@ -1,6 +1,6 @@
1
1
  import { getCoreServices, getRecommendedServices, getOptionalServices, getRequiredInfrastructure, } from './service-registry.js';
2
2
  import { serviceIdToPortVar } from './port-utils.js';
3
- import { SECRET_KEYS } from './secret-generator.js';
3
+ import { SECRET_KEYS, generateHexSecret } from './secret-generator.js';
4
4
  export function generateEnv(state, registry) {
5
5
  const lines = [];
6
6
  lines.push('# Jarvis Configuration');
@@ -9,7 +9,11 @@ export function generateEnv(state, registry) {
9
9
  // Secrets
10
10
  lines.push('# --- Secrets ---');
11
11
  for (const key of SECRET_KEYS) {
12
- const value = state.secrets[key] ?? '';
12
+ // Never emit an empty secret: with JARVIS_ENV=production the boot-time secret
13
+ // guard would refuse to start. A reconstructed install carries its existing
14
+ // value through; a genuinely missing one gets a fresh strong secret (passwords
15
+ // 16 bytes, tokens/keys 32 — matching generateAllSecrets).
16
+ const value = state.secrets[key] || generateHexSecret(key.includes('PASSWORD') ? 16 : 32);
13
17
  lines.push(`${key}=${value}`);
14
18
  }
15
19
  lines.push('');
@@ -106,6 +110,17 @@ export function generateEnv(state, registry) {
106
110
  lines.push('# --- Release Track ---');
107
111
  lines.push(`JARVIS_IMAGE_TAG=${state.releaseTrack === 'dev' ? 'dev' : 'latest'}`);
108
112
  lines.push('');
113
+ // MQTT broker lock. Fresh installs lock the broker from the first boot
114
+ // (allow_anonymous=false): the command-center reads MQTT_PASSWORD from its
115
+ // env and every node fetches broker creds over authenticated HTTP *before* it
116
+ // ever opens an MQTT connection, so there's no anonymous client to strand.
117
+ // The transition window (true) is reserved for in-place UPGRADES of fleets
118
+ // whose nodes predate credential auto-fetch — handled in the compose-upgrader,
119
+ // not here. Compose still defaults `${MQTT_ALLOW_ANON:-true}` as a safety
120
+ // fallback if this line is ever missing.
121
+ lines.push('# --- MQTT Broker ---');
122
+ lines.push('MQTT_ALLOW_ANON=false');
123
+ lines.push('');
109
124
  // Native services (macOS only). Comma-separated list of service IDs the user
110
125
  // opted to run as LaunchAgents instead of in Docker. Read back by
111
126
  // state-reconstructor so reconcile flows keep them out of compose.
@@ -1 +1 @@
1
- {"version":3,"file":"env-generator.js","sourceRoot":"","sources":["../../../src/services/generators/env-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"env-generator.js","sourceRoot":"","sources":["../../../src/services/generators/env-generator.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * Generates a cryptographically secure hex string using Node.js crypto.
3
3
  */
4
4
  export declare function generateHexSecret(byteLength?: number): string;
5
- export declare const SECRET_KEYS: readonly ["AUTH_SECRET_KEY", "JARVIS_CONFIG_ADMIN_TOKEN", "JARVIS_AUTH_ADMIN_TOKEN", "POSTGRES_PASSWORD", "REDIS_PASSWORD", "ADMIN_API_KEY"];
5
+ export declare const SECRET_KEYS: readonly ["AUTH_SECRET_KEY", "JARVIS_CONFIG_ADMIN_TOKEN", "JARVIS_AUTH_ADMIN_TOKEN", "POSTGRES_PASSWORD", "REDIS_PASSWORD", "ADMIN_API_KEY", "MODEL_SERVICE_TOKEN", "MQTT_PASSWORD", "GRAFANA_ADMIN_PASSWORD"];
6
6
  export type SecretKey = (typeof SECRET_KEYS)[number];
7
7
  /**
8
8
  * Generates all required secrets.
@@ -1 +1 @@
1
- {"version":3,"file":"secret-generator.d.ts","sourceRoot":"","sources":["../../../src/services/generators/secret-generator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,GAAE,MAAW,GAAG,MAAM,CAEjE;AAED,eAAO,MAAM,WAAW,8IAOd,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAO9D"}
1
+ {"version":3,"file":"secret-generator.d.ts","sourceRoot":"","sources":["../../../src/services/generators/secret-generator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,GAAE,MAAW,GAAG,MAAM,CAEjE;AAED,eAAO,MAAM,WAAW,gNAsBd,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAO9D"}
@@ -12,6 +12,21 @@ export const SECRET_KEYS = [
12
12
  'POSTGRES_PASSWORD',
13
13
  'REDIS_PASSWORD',
14
14
  'ADMIN_API_KEY',
15
+ // Internal auth between the llm-proxy API/worker and its model service (:7705).
16
+ // The model service 503s ALL inference when this is unset (while /health stays
17
+ // green), so it must be generated + injected on every install. No 'PASSWORD' in
18
+ // the name -> 32 bytes / 64 hex, matching the installer.
19
+ 'MODEL_SERVICE_TOKEN',
20
+ // Shared MQTT broker credential (username is the literal 'jarvis'). The
21
+ // mosquitto container hashes this into a password_file at startup; every MQTT
22
+ // client (command-center, nodes) authenticates with it. 'PASSWORD' in the name
23
+ // -> 16 bytes / 32 hex. Preserved across regen so nodes keep authenticating.
24
+ 'MQTT_PASSWORD',
25
+ // Grafana admin password. Without this the registry default was the well-known
26
+ // literal 'jarvis' on a 0.0.0.0:3000 bind — any LAN client could log in and read
27
+ // all Loki logs (voice transcripts / PII) + SSRF-pivot via the datasource proxy.
28
+ // 'PASSWORD' in the name -> 16 bytes / 32 hex.
29
+ 'GRAFANA_ADMIN_PASSWORD',
15
30
  ];
16
31
  /**
17
32
  * Generates all required secrets.
@@ -1 +1 @@
1
- {"version":3,"file":"secret-generator.js","sourceRoot":"","sources":["../../../src/services/generators/secret-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,aAAqB,EAAE;IACvD,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB;IACjB,2BAA2B;IAC3B,yBAAyB;IACzB,mBAAmB;IACnB,gBAAgB;IAChB,eAAe;CACP,CAAA;AAIV;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACrD,OAAO,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,OAAoC,CAAA;AAC7C,CAAC"}
1
+ {"version":3,"file":"secret-generator.js","sourceRoot":"","sources":["../../../src/services/generators/secret-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,aAAqB,EAAE;IACvD,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB;IACjB,2BAA2B;IAC3B,yBAAyB;IACzB,mBAAmB;IACnB,gBAAgB;IAChB,eAAe;IACf,gFAAgF;IAChF,+EAA+E;IAC/E,gFAAgF;IAChF,yDAAyD;IACzD,qBAAqB;IACrB,wEAAwE;IACxE,8EAA8E;IAC9E,+EAA+E;IAC/E,6EAA6E;IAC7E,eAAe;IACf,+EAA+E;IAC/E,iFAAiF;IACjF,iFAAiF;IACjF,+CAA+C;IAC/C,wBAAwB;CAChB,CAAA;AAIV;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACrD,OAAO,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,OAAoC,CAAA;AAC7C,CAAC"}
@@ -1,4 +1,43 @@
1
1
  import type { FastifyInstance } from 'fastify';
2
+ import type { ServiceRegistry } from '../../types/service-registry.js';
3
+ import type { WhisperBackend } from '../../types/wizard.js';
4
+ import { type ImageDigestMap } from '../generators/compose-generator.js';
5
+ export declare function loadEnvFile(composePath: string): Record<string, string>;
6
+ export interface UpgradedComposeFiles {
7
+ compose: string;
8
+ env: string;
9
+ initDb: string;
10
+ }
11
+ /**
12
+ * PURE regeneration core: reconstruct the wizard state from an existing install's
13
+ * env and produce the upgraded compose / .env / init-db.sh as STRINGS. Writes
14
+ * nothing. Both the in-place upgrader and the download/CLI path build on this so
15
+ * they can never diverge.
16
+ *
17
+ * Secrets and user config are preserved because they're recovered from
18
+ * ``existingEnv`` (via reconstructWizardState + mergeEnv); named data volumes are
19
+ * matched by Docker on ``up`` since the generator emits stable names, and
20
+ * bind-mount host paths come from env too. A regenerated file is CANONICAL — any
21
+ * hand-edits to the original compose are intentionally not carried over.
22
+ */
23
+ export declare function buildUpgradedComposeFiles(existingEnv: Record<string, string>, registry: ServiceRegistry, overrides?: UpgradeOverrides, hostComposePath?: string, digests?: ImageDigestMap): UpgradedComposeFiles;
24
+ /**
25
+ * Non-destructive regeneration for the "download / hand it to the user" flow:
26
+ * read the existing install's env from ``composePath`` and return the upgraded
27
+ * files as strings WITHOUT touching anything on disk. Used by the compose-mode
28
+ * migrator (installer CLI) and the admin download endpoint.
29
+ */
30
+ export declare function regenerateComposeFiles(composePath: string, overrides?: UpgradeOverrides, hostComposePath?: string): UpgradedComposeFiles;
31
+ /**
32
+ * Like {@link regenerateComposeFiles}, but first refreshes the pinned image
33
+ * digests from GHCR so the downloaded compose points at the NEWEST published
34
+ * build (the bundled map is frozen at admin-build time). Async — the refresh is
35
+ * a network round-trip; a resolver failure degrades to the bundled map and never
36
+ * throws. Backs the "Update stack to latest" download: the operator applies it
37
+ * themselves with `docker compose pull && docker compose up -d` (so it can also
38
+ * update jarvis-admin, which the in-place reconcile can't recreate from itself).
39
+ */
40
+ export declare function regenerateComposeFilesLatest(composePath: string, overrides?: UpgradeOverrides, hostComposePath?: string): Promise<UpgradedComposeFiles>;
2
41
  /**
3
42
  * Upgrade the compose + env files in place, preserving secrets and user config.
4
43
  *
@@ -15,7 +54,15 @@ export interface UpgradeOverrides {
15
54
  relayEnabled?: boolean;
16
55
  relayUrl?: string;
17
56
  whisperModelPath?: string;
57
+ whisperBackend?: WhisperBackend;
18
58
  releaseTrack?: 'stable' | 'dev';
59
+ /**
60
+ * Flip the MQTT broker's ``allow_anonymous`` (the transition→lockdown control).
61
+ * ``false`` locks the broker to authenticated clients only; ``true`` re-opens
62
+ * it. Undefined leaves the current state untouched. This is an operational
63
+ * flag, not user config, so an explicit value wins over env preservation.
64
+ */
65
+ mqttAllowAnon?: boolean;
19
66
  }
20
67
  export declare function upgradeCompose(_app: FastifyInstance, overrides?: UpgradeOverrides): Promise<void>;
21
68
  //# sourceMappingURL=compose-upgrader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose-upgrader.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/compose-upgrader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AA2B9C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAChC;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,eAAe,EACrB,SAAS,CAAC,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA8Df"}
1
+ {"version":3,"file":"compose-upgrader.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/compose-upgrader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAyBhH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYvE;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,QAAQ,EAAE,eAAe,EACzB,SAAS,CAAC,EAAE,gBAAgB,EAC5B,eAAe,CAAC,EAAE,MAAM,EACxB,OAAO,CAAC,EAAE,cAAc,GACvB,oBAAoB,CAkDtB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,gBAAgB,EAC5B,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,CAItB;AAED;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CAChD,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,gBAAgB,EAC5B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAW/B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,YAAY,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;IAC/B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,eAAe,EACrB,SAAS,CAAC,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAoDf"}
@@ -5,12 +5,26 @@ import { generateEnv } from '../generators/env-generator.js';
5
5
  import { generateInitDbScript } from '../generators/init-db-generator.js';
6
6
  import { parseRegistry } from '../generators/service-registry.js';
7
7
  import { reconstructWizardState } from './state-reconstructor.js';
8
+ import { refreshDigestsForTrack } from './image-digest-resolver.js';
8
9
  import { mergeEnv } from './env-merger.js';
9
10
  import { VERSION } from '../../version.js';
10
11
  import { getComposePath } from '../compose-path.js';
11
12
  import { getHostComposePath } from '../host-paths.js';
12
13
  import registryData from '../../data/service-registry.json' with { type: 'json' };
13
- function loadEnvFile(composePath) {
14
+ /**
15
+ * Set ``key=value`` in a .env string: replace the line in place if the key
16
+ * exists, otherwise append it. Used for operational flags (e.g. the broker lock)
17
+ * that must win over the merge preservation rule.
18
+ */
19
+ function upsertEnvVar(env, key, value) {
20
+ const line = `${key}=${value}`;
21
+ const pattern = new RegExp(`^${key}=.*$`, 'm');
22
+ if (pattern.test(env))
23
+ return env.replace(pattern, line);
24
+ const base = env.endsWith('\n') ? env : env + '\n';
25
+ return base + line + '\n';
26
+ }
27
+ export function loadEnvFile(composePath) {
14
28
  const envFile = join(composePath, '.env');
15
29
  if (!existsSync(envFile))
16
30
  return {};
@@ -26,23 +40,19 @@ function loadEnvFile(composePath) {
26
40
  }
27
41
  return vars;
28
42
  }
29
- export async function upgradeCompose(_app, overrides) {
30
- const composePath = getComposePath();
31
- const registry = parseRegistry(registryData);
32
- // Step 1: Backup
33
- const backupDir = join(composePath, `backup-${VERSION}`);
34
- if (!existsSync(backupDir)) {
35
- mkdirSync(backupDir, { recursive: true });
36
- for (const file of ['docker-compose.yml', '.env', 'init-db.sh']) {
37
- const src = join(composePath, file);
38
- if (existsSync(src)) {
39
- cpSync(src, join(backupDir, file));
40
- }
41
- }
42
- }
43
- // Step 2: Load existing env
44
- const existingEnv = loadEnvFile(composePath);
45
- // Step 3: Reconstruct state, apply overrides from reconcile options
43
+ /**
44
+ * PURE regeneration core: reconstruct the wizard state from an existing install's
45
+ * env and produce the upgraded compose / .env / init-db.sh as STRINGS. Writes
46
+ * nothing. Both the in-place upgrader and the download/CLI path build on this so
47
+ * they can never diverge.
48
+ *
49
+ * Secrets and user config are preserved because they're recovered from
50
+ * ``existingEnv`` (via reconstructWizardState + mergeEnv); named data volumes are
51
+ * matched by Docker on ``up`` since the generator emits stable names, and
52
+ * bind-mount host paths come from env too. A regenerated file is CANONICAL — any
53
+ * hand-edits to the original compose are intentionally not carried over.
54
+ */
55
+ export function buildUpgradedComposeFiles(existingEnv, registry, overrides, hostComposePath, digests) {
46
56
  const state = reconstructWizardState(existingEnv, registry);
47
57
  if (overrides?.enabledModules) {
48
58
  state.enabledModules = overrides.enabledModules;
@@ -56,30 +66,109 @@ export async function upgradeCompose(_app, overrides) {
56
66
  if (overrides?.whisperModelPath) {
57
67
  state.whisperModelPath = overrides.whisperModelPath;
58
68
  }
69
+ if (overrides?.whisperBackend) {
70
+ state.whisperBackend = overrides.whisperBackend;
71
+ }
59
72
  if (overrides?.releaseTrack) {
60
73
  state.releaseTrack = overrides.releaseTrack;
61
74
  }
62
- // When admin runs in docker, fetch the absolute host path of the compose
63
- // dir we were mounted at. env-generator uses it to write MODELS_DIR so
64
- // bind mounts in the regenerated compose resolve on the host (otherwise
65
- // they resolve to /host/compose/.models — a path that exists in the admin
66
- // container but not on the host, so the daemon binds an empty directory).
67
- const hostPath = await getHostComposePath();
68
- if (hostPath) {
69
- state.hostComposePath = hostPath;
75
+ if (hostComposePath) {
76
+ state.hostComposePath = hostComposePath;
70
77
  }
71
- // Step 4: Generate new compose
72
- const newCompose = generateCompose(state, registry);
73
- writeFileSync(join(composePath, 'docker-compose.yml'), newCompose);
74
- // Step 5: Generate new env template, merge
78
+ const compose = generateCompose(state, registry, digests);
75
79
  const newEnvTemplate = generateEnv(state, registry);
76
- const mergedEnv = mergeEnv(existingEnv, newEnvTemplate);
77
- writeFileSync(join(composePath, '.env'), mergedEnv);
78
- // Step 6: Regenerate init-db.sh
80
+ let env = mergeEnv(existingEnv, newEnvTemplate);
81
+ // Broker lock flag is operational, not user config.
82
+ // - An explicit override always wins over mergeEnv's "existing value wins".
83
+ // - A previously-written MQTT_ALLOW_ANON is preserved by mergeEnv, so a lock
84
+ // stays locked (and an open transition stays open) across routine regens.
85
+ // - The fresh-install template now sets MQTT_ALLOW_ANON=false, but an UPGRADE
86
+ // of an install that predates that change (no MQTT_ALLOW_ANON in its .env)
87
+ // must NOT inherit the locked default — its nodes may not have fetched
88
+ // broker creds yet, and locking would drop the whole fleet. Force the
89
+ // transition window (true) in that case; the operator locks explicitly from
90
+ // the admin UI once nodes have migrated.
91
+ if (overrides?.mqttAllowAnon !== undefined) {
92
+ env = upsertEnvVar(env, 'MQTT_ALLOW_ANON', String(overrides.mqttAllowAnon));
93
+ }
94
+ else if (existingEnv['MQTT_ALLOW_ANON'] === undefined) {
95
+ env = upsertEnvVar(env, 'MQTT_ALLOW_ANON', 'true');
96
+ }
79
97
  const enabledServices = getAllEnabledServices(state, registry);
80
98
  const primaryDb = registry.infrastructure.find((i) => i.id === 'postgres')
81
99
  ?.envVars.find((e) => e.name === 'POSTGRES_DB')?.default ?? 'jarvis_config';
82
100
  const initDb = generateInitDbScript(enabledServices, primaryDb);
101
+ return { compose, env, initDb };
102
+ }
103
+ /**
104
+ * Non-destructive regeneration for the "download / hand it to the user" flow:
105
+ * read the existing install's env from ``composePath`` and return the upgraded
106
+ * files as strings WITHOUT touching anything on disk. Used by the compose-mode
107
+ * migrator (installer CLI) and the admin download endpoint.
108
+ */
109
+ export function regenerateComposeFiles(composePath, overrides, hostComposePath) {
110
+ const registry = parseRegistry(registryData);
111
+ const existingEnv = loadEnvFile(composePath);
112
+ return buildUpgradedComposeFiles(existingEnv, registry, overrides, hostComposePath);
113
+ }
114
+ /**
115
+ * Like {@link regenerateComposeFiles}, but first refreshes the pinned image
116
+ * digests from GHCR so the downloaded compose points at the NEWEST published
117
+ * build (the bundled map is frozen at admin-build time). Async — the refresh is
118
+ * a network round-trip; a resolver failure degrades to the bundled map and never
119
+ * throws. Backs the "Update stack to latest" download: the operator applies it
120
+ * themselves with `docker compose pull && docker compose up -d` (so it can also
121
+ * update jarvis-admin, which the in-place reconcile can't recreate from itself).
122
+ */
123
+ export async function regenerateComposeFilesLatest(composePath, overrides, hostComposePath) {
124
+ const registry = parseRegistry(registryData);
125
+ const existingEnv = loadEnvFile(composePath);
126
+ // dev floats (no digest pins), so only the stable track needs a refresh.
127
+ const track = overrides?.releaseTrack === 'dev' ||
128
+ (overrides?.releaseTrack === undefined && existingEnv.JARVIS_IMAGE_TAG === 'dev')
129
+ ? 'dev'
130
+ : 'latest';
131
+ const digests = track === 'latest' ? await refreshDigestsForTrack('latest') : undefined;
132
+ return buildUpgradedComposeFiles(existingEnv, registry, overrides, hostComposePath, digests);
133
+ }
134
+ export async function upgradeCompose(_app, overrides) {
135
+ const composePath = getComposePath();
136
+ const registry = parseRegistry(registryData);
137
+ // Step 1: Backup
138
+ const backupDir = join(composePath, `backup-${VERSION}`);
139
+ if (!existsSync(backupDir)) {
140
+ mkdirSync(backupDir, { recursive: true });
141
+ for (const file of ['docker-compose.yml', '.env', 'init-db.sh']) {
142
+ const src = join(composePath, file);
143
+ if (existsSync(src)) {
144
+ cpSync(src, join(backupDir, file));
145
+ }
146
+ }
147
+ }
148
+ // Step 2: Load existing env
149
+ const existingEnv = loadEnvFile(composePath);
150
+ // Refresh the pinned image digests from GHCR so the regenerated compose points
151
+ // at the newest published build and `docker compose pull` actually fetches it
152
+ // (the bundled digest map is frozen at admin-build time, which otherwise makes
153
+ // pull a no-op and deadlocks updates). Only the stable track pins — dev floats
154
+ // — and a resolver failure degrades to the bundled map, so this never blocks
155
+ // an upgrade.
156
+ const track = overrides?.releaseTrack === 'dev' ||
157
+ (overrides?.releaseTrack === undefined && existingEnv.JARVIS_IMAGE_TAG === 'dev')
158
+ ? 'dev'
159
+ : 'latest';
160
+ const digests = track === 'latest' ? await refreshDigestsForTrack('latest') : undefined;
161
+ // Step 3-6: Reconstruct + regenerate via the shared pure core.
162
+ //
163
+ // When admin runs in docker, fetch the absolute host path of the compose
164
+ // dir we were mounted at. env-generator uses it to write MODELS_DIR so
165
+ // bind mounts in the regenerated compose resolve on the host (otherwise
166
+ // they resolve to /host/compose/.models — a path that exists in the admin
167
+ // container but not on the host, so the daemon binds an empty directory).
168
+ const hostPath = await getHostComposePath();
169
+ const { compose, env, initDb } = buildUpgradedComposeFiles(existingEnv, registry, overrides, hostPath || undefined, digests);
170
+ writeFileSync(join(composePath, 'docker-compose.yml'), compose);
171
+ writeFileSync(join(composePath, '.env'), env);
83
172
  writeFileSync(join(composePath, 'init-db.sh'), initDb, { mode: 0o755 });
84
173
  }
85
174
  //# sourceMappingURL=compose-upgrader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose-upgrader.js","sourceRoot":"","sources":["../../../src/services/upgrade/compose-upgrader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose-upgrader.js","sourceRoot":"","sources":["../../../src/services/upgrade/compose-upgrader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ImageDigestMap } from '../generators/compose-generator.js';
2
+ /**
3
+ * Resolve one `repo:tag` to its manifest digest via the GHCR HTTP API. Returns
4
+ * null on any failure (unpublished tag, rate limit, network error, malformed
5
+ * digest) — the caller falls back to the bundled digest, so a refresh is never
6
+ * worse than today's frozen map.
7
+ */
8
+ export declare function resolveManifestDigest(repo: string, tag: string, fetchImpl?: typeof fetch): Promise<string | null>;
9
+ /**
10
+ * Return a fresh digest map for the active `track`: every bundled `(repo, tag)`
11
+ * whose tag belongs to that track is re-resolved against GHCR; any that fails to
12
+ * resolve keeps its bundled digest. Other tracks' entries are copied through
13
+ * untouched. Never throws.
14
+ */
15
+ export declare function refreshDigestsForTrack(track: 'latest' | 'dev', base?: ImageDigestMap, fetchImpl?: typeof fetch): Promise<ImageDigestMap>;
16
+ //# sourceMappingURL=image-digest-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-digest-resolver.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/image-digest-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AA2BxE;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,QAAQ,GAAG,KAAK,EACvB,IAAI,GAAE,cAAiD,EACvD,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,cAAc,CAAC,CAsBzB"}
@@ -0,0 +1,80 @@
1
+ import bundledDigests from '../../data/image-digests.json' with { type: 'json' };
2
+ // The compose generator pins first-party images to a `@sha256` digest from the
3
+ // bundled image-digests.json, which is frozen at admin-build time. That makes
4
+ // `docker compose pull` a no-op — it re-pulls the exact digest already running,
5
+ // so services (including admin itself) can never update. This resolver refreshes
6
+ // those digests from GHCR at update time so a regenerated compose points at the
7
+ // newest published image and `docker compose pull` actually fetches it.
8
+ //
9
+ // Resolution uses the plain GHCR HTTP registry API (anonymous pull token, then
10
+ // the manifest's Docker-Content-Digest header) — no docker CLI dependency, and
11
+ // fully mockable in tests. Multi-arch repos publish a manifest LIST whose
12
+ // top-level digest covers every arch, so one pinned digest works cross-arch.
13
+ const REGISTRY_HOST = 'ghcr.io';
14
+ const REGISTRY_NAMESPACE = 'alexberardi';
15
+ const DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
16
+ const TIMEOUT_MS = 15_000;
17
+ const MANIFEST_ACCEPT = [
18
+ 'application/vnd.oci.image.index.v1+json',
19
+ 'application/vnd.docker.distribution.manifest.list.v2+json',
20
+ 'application/vnd.oci.image.manifest.v1+json',
21
+ 'application/vnd.docker.distribution.manifest.v2+json',
22
+ ].join(', ');
23
+ /**
24
+ * Resolve one `repo:tag` to its manifest digest via the GHCR HTTP API. Returns
25
+ * null on any failure (unpublished tag, rate limit, network error, malformed
26
+ * digest) — the caller falls back to the bundled digest, so a refresh is never
27
+ * worse than today's frozen map.
28
+ */
29
+ export async function resolveManifestDigest(repo, tag, fetchImpl = fetch) {
30
+ try {
31
+ const tokenRes = await fetchImpl(`https://${REGISTRY_HOST}/token?service=${REGISTRY_HOST}&scope=repository:${REGISTRY_NAMESPACE}/${repo}:pull`, { signal: AbortSignal.timeout(TIMEOUT_MS) });
32
+ if (!tokenRes.ok)
33
+ return null;
34
+ const token = (await tokenRes.json()).token;
35
+ if (!token)
36
+ return null;
37
+ const manifestRes = await fetchImpl(`https://${REGISTRY_HOST}/v2/${REGISTRY_NAMESPACE}/${repo}/manifests/${tag}`, {
38
+ method: 'HEAD',
39
+ headers: { Authorization: `Bearer ${token}`, Accept: MANIFEST_ACCEPT },
40
+ signal: AbortSignal.timeout(TIMEOUT_MS),
41
+ });
42
+ if (!manifestRes.ok)
43
+ return null;
44
+ const digest = manifestRes.headers.get('docker-content-digest');
45
+ return digest && DIGEST_RE.test(digest) ? digest : null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * Return a fresh digest map for the active `track`: every bundled `(repo, tag)`
53
+ * whose tag belongs to that track is re-resolved against GHCR; any that fails to
54
+ * resolve keeps its bundled digest. Other tracks' entries are copied through
55
+ * untouched. Never throws.
56
+ */
57
+ export async function refreshDigestsForTrack(track, base = bundledDigests, fetchImpl = fetch) {
58
+ const result = {};
59
+ const jobs = [];
60
+ for (const [repo, tags] of Object.entries(base)) {
61
+ result[repo] = { ...tags };
62
+ for (const tag of Object.keys(tags)) {
63
+ // Only the active track is used by the generator (the other track's pins
64
+ // are dead weight), so don't spend network calls resolving it.
65
+ if (tag !== track && !tag.startsWith(`${track}-`))
66
+ continue;
67
+ // Resolve concurrently. Sequential await-per-tag made a refresh cost the
68
+ // SUM of ~17 round-trips — seconds per upgrade, and it blew unit-test
69
+ // timeouts under load. resolveManifestDigest self-contains its errors +
70
+ // timeout (returns null), so Promise.all never rejects.
71
+ jobs.push(resolveManifestDigest(repo, tag, fetchImpl).then((fresh) => {
72
+ if (fresh)
73
+ result[repo][tag] = fresh;
74
+ }));
75
+ }
76
+ }
77
+ await Promise.all(jobs);
78
+ return result;
79
+ }
80
+ //# sourceMappingURL=image-digest-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-digest-resolver.js","sourceRoot":"","sources":["../../../src/services/upgrade/image-digest-resolver.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,MAAM,+BAA+B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAEhF,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,wEAAwE;AACxE,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,0EAA0E;AAC1E,6EAA6E;AAE7E,MAAM,aAAa,GAAG,SAAS,CAAA;AAC/B,MAAM,kBAAkB,GAAG,aAAa,CAAA;AACxC,MAAM,SAAS,GAAG,uBAAuB,CAAA;AACzC,MAAM,UAAU,GAAG,MAAM,CAAA;AAEzB,MAAM,eAAe,GAAG;IACtB,yCAAyC;IACzC,2DAA2D;IAC3D,4CAA4C;IAC5C,sDAAsD;CACvD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAY,EACZ,GAAW,EACX,YAA0B,KAAK;IAE/B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAC9B,WAAW,aAAa,kBAAkB,aAAa,qBAAqB,kBAAkB,IAAI,IAAI,OAAO,EAC7G,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAC5C,CAAA;QACD,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QAC7B,MAAM,KAAK,GAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC,KAAK,CAAA;QACnE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,WAAW,GAAG,MAAM,SAAS,CACjC,WAAW,aAAa,OAAO,kBAAkB,IAAI,IAAI,cAAc,GAAG,EAAE,EAC5E;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE;YACtE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CACF,CAAA;QACD,IAAI,CAAC,WAAW,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QAChC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;QAC/D,OAAO,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAuB,EACvB,OAAuB,cAAgC,EACvD,YAA0B,KAAK;IAE/B,MAAM,MAAM,GAAmB,EAAE,CAAA;IACjC,MAAM,IAAI,GAAoB,EAAE,CAAA;IAChC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAA;QAC1B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,yEAAyE;YACzE,+DAA+D;YAC/D,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC;gBAAE,SAAQ;YAC3D,yEAAyE;YACzE,sEAAsE;YACtE,wEAAwE;YACxE,wDAAwD;YACxD,IAAI,CAAC,IAAI,CACP,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzD,IAAI,KAAK;oBAAE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;YACtC,CAAC,CAAC,CACH,CAAA;QACH,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACvB,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Verify a minisign signature over `artifact` against `publicKeyB64` (the base64
3
+ * line of a minisign public key). Returns true ONLY for a valid signature by
4
+ * that key. Supports minisign's prehashed (BLAKE2b-512) and legacy modes.
5
+ * In-language (Node crypto) — no `minisign` binary dependency.
6
+ */
7
+ export declare function verifyMinisign(publicKeyB64: string, minisigContent: string, artifact: Buffer): boolean;
8
+ /**
9
+ * Parse a `sha256sum`-format checksums file into filename → lowercase-hex-digest.
10
+ * Handles both text (`hash␠␠name`) and binary (`hash␠*name`) lines; ignores any
11
+ * malformed line. Only 64-hex digests are accepted, so a truncated/garbage line
12
+ * can never masquerade as a valid checksum.
13
+ */
14
+ export declare function parseChecksums(content: string): Map<string, string>;
15
+ export type UpdateVerificationInput = {
16
+ /** The trusted minisign public key (base64 line) baked into the client. */
17
+ publicKeyB64: string;
18
+ /** Raw bytes of the release's `checksums.txt`, or null if it couldn't be fetched. */
19
+ checksums: Buffer | null;
20
+ /** Contents of the release's `checksums.txt.minisig`, or null if absent. */
21
+ minisigContent: string | null;
22
+ /** Each downloaded artifact + its bytes, keyed by the filename in checksums.txt. */
23
+ artifacts: {
24
+ filename: string;
25
+ data: Buffer;
26
+ }[];
27
+ /** Operator opt-in (JARVIS_ALLOW_UNSIGNED_UPDATE) to accept an UNSIGNED release. */
28
+ allowUnsigned: boolean;
29
+ };
30
+ export type UpdateVerificationResult = {
31
+ allow: true;
32
+ signed: boolean;
33
+ note?: string;
34
+ } | {
35
+ allow: false;
36
+ reason: string;
37
+ };
38
+ /**
39
+ * The self-update trust policy — pure, so it can be exhaustively tested without
40
+ * touching the network or the filesystem.
41
+ *
42
+ * - No signature material → UNSIGNED. Allowed only if the operator opted in
43
+ * via `allowUnsigned`; we never fabricate trust.
44
+ * - Signature present but invalid for our key → refuse. This is a tampering /
45
+ * wrong-signer signal and is **not** overridable by `allowUnsigned` (the
46
+ * escape hatch covers "no signature", never "bad signature").
47
+ * - Signature valid → every artifact must be listed in checksums.txt AND match
48
+ * its SHA-256. Any missing/mismatched artifact → refuse (also not
49
+ * overridable — a validly-signed manifest that doesn't match the bytes we
50
+ * downloaded means the download was tampered).
51
+ */
52
+ export declare function decideUpdateVerification(input: UpdateVerificationInput): UpdateVerificationResult;
53
+ //# sourceMappingURL=minisign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minisign.d.ts","sourceRoot":"","sources":["../../../src/services/upgrade/minisign.ts"],"names":[],"mappings":"AA6CA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAmBtG;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAOnE;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAA;IACpB,qFAAqF;IACrF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,oFAAoF;IACpF,SAAS,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,oFAAoF;IACpF,aAAa,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CA0BjG"}