@suveren/gateway 0.7.2 → 0.7.3

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/README.md CHANGED
@@ -119,7 +119,16 @@ suveren-gateway stop # stop a detached run
119
119
 
120
120
  Open `http://localhost:3400`. The MCP server is at `http://localhost:3430`.
121
121
 
122
- To upgrade later: `npm install -g @suveren/gateway@latest && suveren-gateway restart`.
122
+ To upgrade later, run these two commands:
123
+
124
+ ```
125
+ npm install -g @suveren/gateway@latest
126
+ suveren-gateway restart
127
+ ```
128
+
129
+ (Written as two lines on purpose: `&&` is not a valid separator in Windows
130
+ PowerShell 5.1, which is what ships with Windows. Use `;` there if you want
131
+ them on one line.)
123
132
 
124
133
  ### Connecting an MCP client
125
134
 
@@ -685,6 +685,64 @@ function createAuthRouter(vault2, logoutAuth, loginRateLimit2) {
685
685
 
686
686
  // src/routes/vault.ts
687
687
  import { Router as Router2 } from "express";
688
+
689
+ // src/lib/credential-meta.ts
690
+ var META_KEY = "__updatedAt";
691
+ var MIN_PREVIEW_LENGTH = 12;
692
+ var PREFIX_RE = /^([A-Za-z][A-Za-z0-9]*(?:[-_][A-Za-z0-9]+)*[-_])/;
693
+ var MAX_PREFIX_LENGTH = 12;
694
+ var HIDDEN_FLOOR = 12;
695
+ function previewSecret(value) {
696
+ if (value.length < MIN_PREVIEW_LENGTH) return { preview: null };
697
+ const match = PREFIX_RE.exec(value)?.[1] ?? "";
698
+ const prefix = match && match.length <= MAX_PREFIX_LENGTH && value.length - match.length - 4 >= HIDDEN_FLOOR ? match : "";
699
+ if (!prefix && value.length - 4 < 8) return { preview: null };
700
+ return { preview: `${prefix}\u2026${value.slice(-4)}` };
701
+ }
702
+ var AI_CONFIG_TEXT_FIELDS = /* @__PURE__ */ new Set(["provider", "endpoint", "model"]);
703
+ function textFieldsFor(name, manifests) {
704
+ if (name === "ai-config") return AI_CONFIG_TEXT_FIELDS;
705
+ const list = Array.isArray(manifests) ? manifests : [];
706
+ const manifest = list.find((m) => m?.id === name);
707
+ const out = /* @__PURE__ */ new Set();
708
+ for (const f of manifest?.credentials?.fields ?? []) {
709
+ if (typeof f?.key === "string" && f?.type === "text") out.add(f.key);
710
+ }
711
+ return out;
712
+ }
713
+ function credentialView(cred, textFields) {
714
+ const fields = {};
715
+ const secrets = {};
716
+ let updatedAt;
717
+ for (const [key, value] of Object.entries(cred)) {
718
+ if (key === META_KEY) {
719
+ updatedAt = value;
720
+ continue;
721
+ }
722
+ if (textFields.has(key)) fields[key] = value;
723
+ else secrets[key] = previewSecret(value);
724
+ }
725
+ return {
726
+ configured: true,
727
+ fieldNames: Object.keys(fields).concat(Object.keys(secrets)),
728
+ fields,
729
+ secrets,
730
+ ...updatedAt ? { updatedAt } : {}
731
+ };
732
+ }
733
+
734
+ // src/routes/vault.ts
735
+ var manifestCache = null;
736
+ async function cachedManifests() {
737
+ if (manifestCache && Date.now() - manifestCache.at < 6e4) return manifestCache.value;
738
+ try {
739
+ const m = await getManifests();
740
+ manifestCache = { at: Date.now(), value: m?.manifests ?? m };
741
+ } catch {
742
+ manifestCache = { at: Date.now(), value: [] };
743
+ }
744
+ return manifestCache.value;
745
+ }
688
746
  function createVaultRouter(vault2) {
689
747
  const router = Router2();
690
748
  router.get("/status", (_req, res) => {
@@ -696,14 +754,14 @@ function createVaultRouter(vault2) {
696
754
  serviceCount: services.length
697
755
  });
698
756
  });
699
- router.get("/credentials/:name", (req, res) => {
757
+ router.get("/credentials/:name", async (req, res) => {
700
758
  const { name } = req.params;
701
759
  const cred = vault2.getCredential(name);
702
760
  if (!cred) {
703
761
  res.json({ configured: false });
704
762
  return;
705
763
  }
706
- res.json({ configured: true, fieldNames: Object.keys(cred), fields: cred });
764
+ res.json(credentialView(cred, textFieldsFor(name, await cachedManifests())));
707
765
  });
708
766
  router.put("/credentials/:name", async (req, res) => {
709
767
  const { name } = req.params;
@@ -714,6 +772,7 @@ function createVaultRouter(vault2) {
714
772
  if (existing) fields = { ...existing, ...incoming };
715
773
  } catch {
716
774
  }
775
+ fields = { ...fields, [META_KEY]: (/* @__PURE__ */ new Date()).toISOString() };
717
776
  vault2.setCredential(name, fields);
718
777
  try {
719
778
  await pushServiceCredentials(name, fields);
@@ -2327,7 +2386,7 @@ function resolveOrigin(req) {
2327
2386
  return `${req.protocol}://${req.get("host")}`;
2328
2387
  }
2329
2388
  var oauthRedirectUris = /* @__PURE__ */ new Map();
2330
- var manifestCache = null;
2389
+ var manifestCache2 = null;
2331
2390
  function resolvePath(obj, path) {
2332
2391
  const v = path.split(".").reduce((acc, key) => {
2333
2392
  return acc && typeof acc === "object" ? acc[key] : void 0;
@@ -2347,15 +2406,15 @@ function decodeJwtEmail(idToken) {
2347
2406
  }
2348
2407
  }
2349
2408
  async function getOAuthManifest(integrationId) {
2350
- if (!manifestCache) {
2409
+ if (!manifestCache2) {
2351
2410
  try {
2352
2411
  const data = await getManifests();
2353
- manifestCache = data.manifests;
2412
+ manifestCache2 = data.manifests;
2354
2413
  } catch {
2355
2414
  return null;
2356
2415
  }
2357
2416
  }
2358
- return manifestCache?.find((m) => m.id === integrationId && m.oauth) ?? null;
2417
+ return manifestCache2?.find((m) => m.id === integrationId && m.oauth) ?? null;
2359
2418
  }
2360
2419
  app.get("/auth/oauth/:integrationId/start", async (req, res) => {
2361
2420
  const { integrationId } = req.params;