@suveren/gateway 0.21.0 → 0.22.0

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.
@@ -117,7 +117,13 @@
117
117
  "staticExecution": {
118
118
  "action_type": "send"
119
119
  },
120
- "actionLabel": "Quote sent"
120
+ "actionLabel": "Quote sent",
121
+ "preview": {
122
+ "tool": "get_quote",
123
+ "args": { "id": "id", "revision": "revision" },
124
+ "version": { "arg": "revision", "field": "revision" },
125
+ "fields": ["number", "revision", "status", "customer_id", "lines", "net_total", "discount_pct", "valid_until"]
126
+ }
121
127
  },
122
128
  "convert_quote_to_order": {
123
129
  "executionMapping": {
@@ -128,7 +134,13 @@
128
134
  "staticExecution": {
129
135
  "action_type": "order"
130
136
  },
131
- "actionLabel": "Quote converted to order"
137
+ "actionLabel": "Quote converted to order",
138
+ "preview": {
139
+ "tool": "get_quote",
140
+ "args": { "id": "id", "revision": "revision" },
141
+ "version": { "arg": "revision", "field": "revision" },
142
+ "fields": ["number", "revision", "status", "customer_id", "lines", "net_total", "discount_pct", "valid_until"]
143
+ }
132
144
  },
133
145
  "load_simulation": {
134
146
  "executionMapping": {},
@@ -5,8 +5,8 @@ import {
5
5
 
6
6
  // src/index.ts
7
7
  import { randomBytes as randomBytes3 } from "crypto";
8
- import { existsSync as existsSync11, readFileSync as readFileSync13, renameSync as renameSync2 } from "fs";
9
- import { dirname as dirname9, join as join13 } from "path";
8
+ import { existsSync as existsSync12, readFileSync as readFileSync14, renameSync as renameSync2 } from "fs";
9
+ import { dirname as dirname9, join as join14 } from "path";
10
10
  import { homedir as homedir7 } from "os";
11
11
  import express from "express";
12
12
  import { createProxyMiddleware } from "http-proxy-middleware";
@@ -599,6 +599,25 @@ async function exportReport(gatewayVersion) {
599
599
  const filename = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? "suveren-report.html";
600
600
  return { html, filename };
601
601
  }
602
+ async function getToolPreview(data) {
603
+ const controller = new AbortController();
604
+ const timeout = setTimeout(() => controller.abort(), 12e3);
605
+ try {
606
+ const res = await fetch(`${MCP_BASE}/internal/preview`, {
607
+ method: "POST",
608
+ headers: internalHeaders(),
609
+ body: JSON.stringify(data),
610
+ signal: controller.signal
611
+ });
612
+ if (!res.ok) {
613
+ const err = await res.json().catch(() => ({ error: "Unknown error" }));
614
+ throw new Error(`MCP preview failed: ${err.error ?? res.status}`);
615
+ }
616
+ return res.json();
617
+ } finally {
618
+ clearTimeout(timeout);
619
+ }
620
+ }
602
621
  async function getAgentContact() {
603
622
  const res = await fetch(`${MCP_BASE}/internal/agent-contact`, {
604
623
  headers: internalHeaders()
@@ -3404,8 +3423,121 @@ function createEvidenceExportRouter(spUrl, vault2, dataDir, lockAsTlsMismatch2)
3404
3423
  return router;
3405
3424
  }
3406
3425
 
3407
- // src/routes/evidence.ts
3426
+ // src/routes/proposal-preview.ts
3408
3427
  import { Router as Router10 } from "express";
3428
+
3429
+ // src/lib/execution-journal-reader.ts
3430
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
3431
+ import { join as join10 } from "path";
3432
+ function loadJournalEntries(dataDir) {
3433
+ const path = join10(dataDir, "execution-journal.json");
3434
+ if (!existsSync10(path)) return [];
3435
+ try {
3436
+ const parsed = JSON.parse(readFileSync10(path, "utf-8"));
3437
+ return parsed.entries ?? [];
3438
+ } catch {
3439
+ return [];
3440
+ }
3441
+ }
3442
+ function findLatestByProposal(dataDir, proposalId) {
3443
+ const rows = loadJournalEntries(dataDir).filter((e) => e.proposalId === proposalId);
3444
+ return rows[rows.length - 1];
3445
+ }
3446
+
3447
+ // src/routes/proposal-preview.ts
3448
+ var AS_FETCH_TIMEOUT_MS2 = 1e4;
3449
+ function createProposalStatusRouter(spUrl, vault2, dataDir, lockAsTlsMismatch2) {
3450
+ const router = Router10();
3451
+ router.get("/:id/preview", async (req, res) => {
3452
+ const proposalId = req.params.id;
3453
+ const cookie = vault2.getSpCookie();
3454
+ if (!cookie) {
3455
+ res.status(401).json({ error: "No Authority Server session \u2014 sign in to preview this proposal." });
3456
+ return;
3457
+ }
3458
+ let proposal;
3459
+ try {
3460
+ const controller = new AbortController();
3461
+ const timeout = setTimeout(() => controller.abort(), AS_FETCH_TIMEOUT_MS2);
3462
+ const pin = readPairing(dataDir);
3463
+ let asStatus;
3464
+ let asBody;
3465
+ try {
3466
+ const { res: asRes } = await fetchAs(
3467
+ `${spUrl}/api/proposals/${encodeURIComponent(proposalId)}`,
3468
+ { headers: { Cookie: cookie }, signal: controller.signal },
3469
+ {
3470
+ enforce: resolvePinTls(dataDir),
3471
+ pinnedSpkiHex: pin && pin.asUrl === spUrl ? pin.tlsSpkiPinHex : void 0
3472
+ }
3473
+ );
3474
+ asStatus = asRes.status;
3475
+ asBody = await asRes.json().catch(() => ({}));
3476
+ } finally {
3477
+ clearTimeout(timeout);
3478
+ }
3479
+ if (asStatus < 200 || asStatus >= 300 || !asBody.proposal) {
3480
+ res.status(asStatus >= 400 ? asStatus : 502).json({
3481
+ error: asBody.error ?? `Could not fetch proposal ${proposalId} (${asStatus})`
3482
+ });
3483
+ return;
3484
+ }
3485
+ proposal = asBody.proposal;
3486
+ } catch (err) {
3487
+ if (err instanceof AsTlsMismatchError) {
3488
+ lockAsTlsMismatch2();
3489
+ res.status(502).json({ error: err.message });
3490
+ return;
3491
+ }
3492
+ res.status(502).json({ error: `Authority Server unreachable: ${err instanceof Error ? err.message : String(err)}` });
3493
+ return;
3494
+ }
3495
+ try {
3496
+ const result = await getToolPreview({ proposalId, tool: proposal.tool, toolArgs: proposal.toolArgs });
3497
+ if (result.status !== "ok") {
3498
+ res.json(result);
3499
+ return;
3500
+ }
3501
+ const response = { ...result, readAt: Date.now() };
3502
+ res.json(response);
3503
+ } catch (err) {
3504
+ res.status(502).json({ error: `MCP server unavailable: ${err instanceof Error ? err.message : String(err)}` });
3505
+ }
3506
+ });
3507
+ router.get("/:id/outcome", (req, res) => {
3508
+ try {
3509
+ const entry = findLatestByProposal(dataDir, req.params.id);
3510
+ if (!entry) {
3511
+ const none = { state: "none" };
3512
+ res.json(none);
3513
+ return;
3514
+ }
3515
+ if (entry.state === "intent") {
3516
+ const intent = { state: "intent", at: entry.startedAt };
3517
+ res.json(intent);
3518
+ return;
3519
+ }
3520
+ if (entry.state === "done") {
3521
+ const done = { state: "done", at: entry.finishedAt ?? entry.startedAt };
3522
+ res.json(done);
3523
+ return;
3524
+ }
3525
+ const failed = {
3526
+ state: "failed",
3527
+ at: entry.finishedAt ?? entry.startedAt,
3528
+ ...entry.outcome ? { outcome: entry.outcome } : {},
3529
+ ...entry.detail ? { detail: entry.detail } : {}
3530
+ };
3531
+ res.json(failed);
3532
+ } catch (err) {
3533
+ res.status(500).json({ error: `Could not read the execution journal: ${err instanceof Error ? err.message : String(err)}` });
3534
+ }
3535
+ });
3536
+ return router;
3537
+ }
3538
+
3539
+ // src/routes/evidence.ts
3540
+ import { Router as Router11 } from "express";
3409
3541
  import { verifyTicketSignature, encodeDidKey } from "@hap/core";
3410
3542
  function issuerFromPublicKeyHex(publicKeyHex) {
3411
3543
  return encodeDidKey(Buffer.from(publicKeyHex, "hex"));
@@ -3471,7 +3603,7 @@ function mergeAuthorizations(evidence) {
3471
3603
  return out;
3472
3604
  }
3473
3605
  function createEvidenceRouter() {
3474
- const router = Router10();
3606
+ const router = Router11();
3475
3607
  router.get("/authorizations", async (_req, res) => {
3476
3608
  try {
3477
3609
  const evidence = await getLocalEvidence();
@@ -3514,7 +3646,7 @@ function createEvidenceRouter() {
3514
3646
  }
3515
3647
 
3516
3648
  // src/routes/report.ts
3517
- import { Router as Router11 } from "express";
3649
+ import { Router as Router12 } from "express";
3518
3650
  function handleError(res, err) {
3519
3651
  if (err instanceof McpLockedError) {
3520
3652
  res.status(503).json({ error: err.message });
@@ -3526,7 +3658,7 @@ function handleError(res, err) {
3526
3658
  });
3527
3659
  }
3528
3660
  function createReportRouter(getGatewayVersion) {
3529
- const router = Router11();
3661
+ const router = Router12();
3530
3662
  router.get("/", async (_req, res) => {
3531
3663
  try {
3532
3664
  res.json(await getReport());
@@ -3572,9 +3704,9 @@ function createReportRouter(getGatewayVersion) {
3572
3704
  }
3573
3705
 
3574
3706
  // src/routes/decrypt-intent.ts
3575
- import { Router as Router12 } from "express";
3707
+ import { Router as Router13 } from "express";
3576
3708
  function createDecryptIntentRouter(vault2) {
3577
- const router = Router12();
3709
+ const router = Router13();
3578
3710
  router.post("/", async (req, res) => {
3579
3711
  const { intentCiphertext, encryptedKey, approverId } = req.body;
3580
3712
  if (typeof intentCiphertext !== "string" || intentCiphertext.length === 0) {
@@ -3750,22 +3882,22 @@ data: ${data}
3750
3882
  }
3751
3883
 
3752
3884
  // src/routes/gateway-settings.ts
3753
- import { Router as Router13 } from "express";
3885
+ import { Router as Router14 } from "express";
3754
3886
 
3755
3887
  // src/lib/gateway-settings.ts
3756
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
3888
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
3757
3889
  import { homedir as homedir5 } from "os";
3758
- import { join as join10, dirname as dirname7 } from "path";
3890
+ import { join as join11, dirname as dirname7 } from "path";
3759
3891
  var DEFAULT_SETTINGS = {
3760
3892
  desktopNotifications: true
3761
3893
  };
3762
3894
  function settingsPath(dataDir) {
3763
- const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join10(homedir5(), ".suveren");
3764
- return join10(dir, "settings.json");
3895
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join11(homedir5(), ".suveren");
3896
+ return join11(dir, "settings.json");
3765
3897
  }
3766
3898
  function readSettings(dataDir) {
3767
3899
  try {
3768
- const raw = JSON.parse(readFileSync10(settingsPath(dataDir), "utf8"));
3900
+ const raw = JSON.parse(readFileSync11(settingsPath(dataDir), "utf8"));
3769
3901
  return {
3770
3902
  desktopNotifications: typeof raw.desktopNotifications === "boolean" ? raw.desktopNotifications : DEFAULT_SETTINGS.desktopNotifications
3771
3903
  };
@@ -3786,7 +3918,7 @@ function writeSettings(patch, dataDir) {
3786
3918
 
3787
3919
  // src/routes/gateway-settings.ts
3788
3920
  function createGatewaySettingsRouter() {
3789
- const router = Router13();
3921
+ const router = Router14();
3790
3922
  router.get("/", (_req, res) => {
3791
3923
  res.json(readSettings());
3792
3924
  });
@@ -3809,23 +3941,23 @@ function createGatewaySettingsRouter() {
3809
3941
  }
3810
3942
 
3811
3943
  // src/routes/archived-mandates.ts
3812
- import { Router as Router14 } from "express";
3944
+ import { Router as Router15 } from "express";
3813
3945
 
3814
3946
  // src/lib/archived-mandates-store.ts
3815
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, renameSync } from "fs";
3947
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, renameSync } from "fs";
3816
3948
  import { homedir as homedir6 } from "os";
3817
- import { join as join11, dirname as dirname8 } from "path";
3949
+ import { join as join12, dirname as dirname8 } from "path";
3818
3950
  var ID_PATTERN = /^[A-Za-z0-9_.:-]{1,200}$/;
3819
3951
  function isValidMandateId(id) {
3820
3952
  return typeof id === "string" && ID_PATTERN.test(id);
3821
3953
  }
3822
3954
  function storePath(dataDir) {
3823
- const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join11(homedir6(), ".suveren");
3824
- return join11(dir, "archived-mandates.json");
3955
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join12(homedir6(), ".suveren");
3956
+ return join12(dir, "archived-mandates.json");
3825
3957
  }
3826
3958
  function readArchived(dataDir) {
3827
3959
  try {
3828
- const raw = JSON.parse(readFileSync11(storePath(dataDir), "utf8"));
3960
+ const raw = JSON.parse(readFileSync12(storePath(dataDir), "utf8"));
3829
3961
  if (!Array.isArray(raw.archived)) return [];
3830
3962
  return [...new Set(raw.archived.filter(isValidMandateId))];
3831
3963
  } catch {
@@ -3852,7 +3984,7 @@ function unarchiveMandate(id, dataDir) {
3852
3984
 
3853
3985
  // src/routes/archived-mandates.ts
3854
3986
  function createArchivedMandatesRouter(dataDir) {
3855
- const router = Router14();
3987
+ const router = Router15();
3856
3988
  router.get("/", (_req, res) => {
3857
3989
  res.json({ archived: readArchived(dataDir) });
3858
3990
  });
@@ -3874,7 +4006,7 @@ function createArchivedMandatesRouter(dataDir) {
3874
4006
  }
3875
4007
 
3876
4008
  // src/routes/internal-events.ts
3877
- import { Router as Router15 } from "express";
4009
+ import { Router as Router16 } from "express";
3878
4010
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
3879
4011
  var ALLOWED = ["proposal-added", "action-approval-needed"];
3880
4012
  var SESSION_EXPIRED = "session-expired";
@@ -3889,7 +4021,7 @@ function secretMatches(provided, expected) {
3889
4021
  return timingSafeEqual2(a, b);
3890
4022
  }
3891
4023
  function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch, onAsTlsMismatch) {
3892
- const router = Router15();
4024
+ const router = Router16();
3893
4025
  router.post("/event", (req, res) => {
3894
4026
  if (!secretMatches(req.headers["x-internal-secret"], getSecret())) {
3895
4027
  res.status(401).json({ error: "Unauthorized" });
@@ -3923,7 +4055,7 @@ function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch
3923
4055
  }
3924
4056
 
3925
4057
  // src/routes/internal-mandate.ts
3926
- import { Router as Router16 } from "express";
4058
+ import { Router as Router17 } from "express";
3927
4059
  import { timingSafeEqual as timingSafeEqual3, randomUUID } from "crypto";
3928
4060
 
3929
4061
  // src/lib/mandate-ceremony.ts
@@ -4105,7 +4237,7 @@ function secretMatches2(provided, expected) {
4105
4237
  return a.length === b.length && timingSafeEqual3(a, b);
4106
4238
  }
4107
4239
  function createInternalMandateRouter(getSecret, deps) {
4108
- const router = Router16();
4240
+ const router = Router17();
4109
4241
  router.post("/mandate", async (req, res) => {
4110
4242
  if (!secretMatches2(req.header("X-Internal-Secret") ?? void 0, getSecret())) {
4111
4243
  res.status(401).json({ error: "unauthorized" });
@@ -4346,17 +4478,17 @@ function buildSessionHealth(vault2) {
4346
4478
  }
4347
4479
 
4348
4480
  // src/lib/denials-reader.ts
4349
- import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
4350
- import { join as join12 } from "path";
4481
+ import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
4482
+ import { join as join13 } from "path";
4351
4483
  function loadDenials(dataDir, decrypt) {
4352
- const encPath = join12(dataDir, "denials.enc.json");
4353
- const plainPath = join12(dataDir, "denials.json");
4354
- if (existsSync10(encPath)) {
4355
- const { blob } = JSON.parse(readFileSync12(encPath, "utf-8"));
4484
+ const encPath = join13(dataDir, "denials.enc.json");
4485
+ const plainPath = join13(dataDir, "denials.json");
4486
+ if (existsSync11(encPath)) {
4487
+ const { blob } = JSON.parse(readFileSync13(encPath, "utf-8"));
4356
4488
  return JSON.parse(decrypt(blob)).records ?? [];
4357
4489
  }
4358
- if (existsSync10(plainPath)) {
4359
- return JSON.parse(readFileSync12(plainPath, "utf-8")).records ?? [];
4490
+ if (existsSync11(plainPath)) {
4491
+ return JSON.parse(readFileSync13(plainPath, "utf-8")).records ?? [];
4360
4492
  }
4361
4493
  return [];
4362
4494
  }
@@ -4380,9 +4512,9 @@ function isSimulationMode() {
4380
4512
  // src/index.ts
4381
4513
  (function migrateDataDir() {
4382
4514
  if (process.env.SUVEREN_DATA_DIR) return;
4383
- const oldDir = join13(homedir7(), ".hap");
4384
- const newDir = join13(homedir7(), ".suveren");
4385
- if (!existsSync11(newDir) && existsSync11(oldDir)) {
4515
+ const oldDir = join14(homedir7(), ".hap");
4516
+ const newDir = join14(homedir7(), ".suveren");
4517
+ if (!existsSync12(newDir) && existsSync12(oldDir)) {
4386
4518
  try {
4387
4519
  renameSync2(oldDir, newDir);
4388
4520
  console.error(`[Control Plane] Migrated data directory: ${oldDir} \u2192 ${newDir}`);
@@ -4392,14 +4524,14 @@ function isSimulationMode() {
4392
4524
  }
4393
4525
  }
4394
4526
  })();
4395
- var DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join13(homedir7(), ".suveren");
4527
+ var DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join14(homedir7(), ".suveren");
4396
4528
  var SP_URL = resolveAsUrl(DATA_DIR);
4397
4529
  validatePinTlsForUrl(resolvePinTls(DATA_DIR), SP_URL);
4398
4530
  var POLICY_LOCKED = [...readPolicy().locked];
4399
4531
  var POLICY_LOCKED_INSTALL_METHOD = readPolicy().policy.installMethod;
4400
4532
  var port = parseInt(process.env.SUVEREN_CP_PORT ?? "3402", 10);
4401
4533
  var HAP_MODE = process.env.HAP_MODE ?? "personal";
4402
- var UI_DIST = process.env.HAP_UI_DIST ?? join13(import.meta.dirname ?? __dirname, "../../ui/dist");
4534
+ var UI_DIST = process.env.HAP_UI_DIST ?? join14(import.meta.dirname ?? __dirname, "../../ui/dist");
4403
4535
  var vault = new Vault(DATA_DIR);
4404
4536
  (function repairIfAsUrlChanged() {
4405
4537
  const pairing = readPairing(DATA_DIR);
@@ -4689,6 +4821,7 @@ app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRou
4689
4821
  app.use("/api/archived-mandates", jsonParser, authGuard, createArchivedMandatesRouter());
4690
4822
  app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
4691
4823
  app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch));
4824
+ app.use("/proposal-status", authGuard, createProposalStatusRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch));
4692
4825
  app.use("/api/evidence", authGuard, createEvidenceRouter());
4693
4826
  app.use("/api/report", jsonParser, authGuard, createReportRouter(() => RUNNING_VERSION));
4694
4827
  app.use("/api/decrypt-intent", jsonParser, authGuard, createDecryptIntentRouter(vault));
@@ -4851,7 +4984,7 @@ app.get("/as-pairing", authGuard, (_req, res) => {
4851
4984
  });
4852
4985
  app.get("/denials", authGuard, (req, res) => {
4853
4986
  try {
4854
- const dataDir = process.env.SUVEREN_DATA_DIR ?? join13(homedir7(), ".suveren");
4987
+ const dataDir = process.env.SUVEREN_DATA_DIR ?? join14(homedir7(), ".suveren");
4855
4988
  const all = loadDenials(dataDir, (blob) => vault.decrypt(blob));
4856
4989
  const since = Number(req.query.since);
4857
4990
  const limit = Number(req.query.limit);
@@ -4992,7 +5125,7 @@ app.use(
4992
5125
  function detectInstallMethod2() {
4993
5126
  return detectInstallMethod({
4994
5127
  dir: import.meta.dirname ?? __dirname,
4995
- dockerEnv: existsSync11("/.dockerenv"),
5128
+ dockerEnv: existsSync12("/.dockerenv"),
4996
5129
  managed: process.env.SUVEREN_INSTALL_METHOD === "managed" || POLICY_LOCKED_INSTALL_METHOD === "managed"
4997
5130
  });
4998
5131
  }
@@ -5002,10 +5135,10 @@ function detectRunningVersion() {
5002
5135
  return process.env.HAP_BUILD_SHA ?? "dev";
5003
5136
  }
5004
5137
  const dir = import.meta.dirname ?? __dirname;
5005
- const bundlePkg = join13(dir, "..", "..", "package.json");
5006
- if (existsSync11(bundlePkg)) {
5138
+ const bundlePkg = join14(dir, "..", "..", "package.json");
5139
+ if (existsSync12(bundlePkg)) {
5007
5140
  try {
5008
- const pkg = JSON.parse(readFileSync13(bundlePkg, "utf8"));
5141
+ const pkg = JSON.parse(readFileSync14(bundlePkg, "utf8"));
5009
5142
  if (pkg.name === "@suveren/gateway" && typeof pkg.version === "string") {
5010
5143
  return pkg.version;
5011
5144
  }
@@ -5047,7 +5180,7 @@ app.get("/health", async (req, res) => {
5047
5180
  }
5048
5181
  });
5049
5182
  });
5050
- if (existsSync11(UI_DIST)) {
5183
+ if (existsSync12(UI_DIST)) {
5051
5184
  app.use(express.static(UI_DIST, {
5052
5185
  setHeaders: (res, filePath) => {
5053
5186
  if (filePath.endsWith("index.html")) {
@@ -5057,7 +5190,7 @@ if (existsSync11(UI_DIST)) {
5057
5190
  }));
5058
5191
  app.get("*", (_req, res) => {
5059
5192
  res.setHeader("Cache-Control", "no-store, must-revalidate");
5060
- res.sendFile(join13(UI_DIST, "index.html"));
5193
+ res.sendFile(join14(UI_DIST, "index.html"));
5061
5194
  });
5062
5195
  } else {
5063
5196
  app.get("/", (_req, res) => {