@exposify/express 0.3.12 → 0.3.14

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
@@ -35,10 +35,10 @@ and exception filters apply exactly as for any HTTP client, so a guarded
35
35
  route refuses an agent without credentials and a `ValidationPipe` error
36
36
  reaches the agent as an actionable 400. `npx @exposify/express setup` and
37
37
  `inspect` find `src/main.ts` from `nest-cli.json` and load it with your
38
- `ts-node`/`swc-node`. The Fastify platform isn't supported yet. See
39
- `examples/nest-demo`.
38
+ `ts-node`/`swc-node`. The Fastify platform isn't supported yet. See the
39
+ [`nest-demo` example](https://github.com/ayadagain/agentready/tree/main/examples/nest-demo).
40
40
 
41
- With `@nestjs/swagger`, [`@exposify/nestjs`](../nestjs) does this in one line
41
+ With `@nestjs/swagger`, [`@exposify/nestjs`](https://github.com/ayadagain/agentready/tree/main/packages/nestjs) does this in one line
42
42
  (`mountAiReady(app)`) and adds `@McpExposed()` / `@McpHidden()` /
43
43
  `@McpDescribe()` decorators. Or pass the document yourself, and tools get its
44
44
  DTO schemas and `@ApiOperation` summaries:
@@ -137,9 +137,9 @@ flagged for you to check. In CI, `describe --check` exits 1 if a tool has no
137
137
  description or one is out of date, and `describe --ci` writes AI
138
138
  descriptions for read routes only, listing the writes for a person.
139
139
 
140
- `npx @exposify/express inspect` prints every route with its tool name,
141
- whether agents get it, and why (`--json` for scripts): the answer to "why
142
- isn't my route showing up?".
140
+ `npx @exposify/express inspect` prints every route with its tool name and
141
+ whether agents get it; add `--why` for the reason on each (`--json` for
142
+ scripts): the answer to "why isn't my route showing up?".
143
143
 
144
144
  **Catch tool changes in CI:** `npx @exposify/express inspect --snapshot` writes
145
145
  `exposify.tools.json` (each route agents can call, and its tool name).
@@ -188,7 +188,7 @@ same way. `loginVerify: { path, fields, body? }` names the second step;
188
188
  **No login found, and no sign-in method in the dashboard?** Then agents use
189
189
  your app signed out: `/mcp` doesn't ask them to sign in, and your own checks
190
190
  decide what they can do. The startup log says which it is
191
- (`[agentready] How agents sign in: ...`), and so does the dashboard's "How
191
+ (`[exposify] How agents sign in: ...`), and so does the dashboard's "How
192
192
  agents sign in" page. Adding a sign-in method there takes effect within a
193
193
  minute.
194
194
 
@@ -273,17 +273,17 @@ On boot (`npm run dev`, or in production), right after your routes
273
273
  register, `aiReady()` prints one line saying whether MCP is up:
274
274
 
275
275
  ```
276
- [agentready] MCP ready at /mcp: 6 tools (4 read, 1 write, search_docs), 1 write route hidden (opt in with exposed()), 2 docs indexed, auth: passthrough
276
+ [exposify] MCP ready at /mcp: 6 tools (4 read, 1 write, search_docs), 1 write route hidden (opt in with exposed()), 2 docs indexed, auth: passthrough
277
277
  ```
278
278
 
279
279
  When something's wrong, the line names the problem and a fix instead:
280
280
 
281
281
  ```
282
- [agentready] MCP failed to start: <reason>
282
+ [exposify] MCP failed to start: <reason>
283
283
  fix: <what to do>
284
- [agentready] warning: MCP is up at /mcp but exposes no route tools
284
+ [exposify] warning: MCP is up at /mcp but exposes no route tools
285
285
  fix: register app.use(aiReady()) before your routers, ...
286
- [agentready] warning: can't verify access tokens from oauth.issuer https://...: <reason>
286
+ [exposify] warning: can't verify access tokens from oauth.issuer https://...: <reason>
287
287
  fix: make oauth.issuer match the tokens' `iss` exactly ...
288
288
  ```
289
289
 
@@ -385,6 +385,7 @@ default above.
385
385
  `req.ip`, `req.hostname`, `req.protocol` and `trust proxy` see the agent
386
386
  exactly as they would for a direct request, never as localhost.
387
387
 
388
- See the repo README for
389
- the full picture across all `@exposify/*` packages, including
390
- `@exposify/supabase`.
388
+ See the
389
+ [repo README](https://github.com/ayadagain/agentready#readme) for the full
390
+ picture across all `@exposify/*` packages, including
391
+ [`@exposify/supabase`](https://github.com/ayadagain/agentready/tree/main/packages/supabase).
package/dist/index.cjs CHANGED
@@ -184,7 +184,7 @@ function warnMountSkipped() {
184
184
  warnedAboutSkippedMount = true;
185
185
  try {
186
186
  console.warn(
187
- `[agentready] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
187
+ `[exposify] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
188
188
  );
189
189
  } catch {
190
190
  }
@@ -341,7 +341,7 @@ async function loadOpenApi(cwd, option, warn) {
341
341
 
342
342
  // src/surface.ts
343
343
  var import_core4 = require("@exposify/core");
344
- var warnOnce = (m) => console.warn(`[agentready] warning: ${m}`);
344
+ var warnOnce = (m) => console.warn(`[exposify] warning: ${m}`);
345
345
  var NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
346
346
  var IDENTITY_HEADERS = ["host", "forwarded", "x-forwarded-for", "x-forwarded-proto", "x-forwarded-host", "x-forwarded-port"];
347
347
  var DISPATCH_HEADER = "x-exposify-dispatch";
@@ -553,7 +553,7 @@ var import_core7 = require("@exposify/core");
553
553
  var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("exposify.express.aiReadyOptions");
554
554
  var MCP_PATH = "/mcp";
555
555
  var PRM_ROOT = "/.well-known/oauth-protected-resource";
556
- var LOG_PREFIX = "[agentready]";
556
+ var LOG_PREFIX = "[exposify]";
557
557
  function toolsSummary(routes, docs, cfg) {
558
558
  const counts = (0, import_core6.countTools)(routes, cfg);
559
559
  const details = counts.hiddenWrites > 0 ? [`${counts.hiddenWrites} write route${counts.hiddenWrites === 1 ? "" : "s"} hidden (opt in with exposed())`] : [];
@@ -681,7 +681,7 @@ function aiReady(options = {}) {
681
681
  const login = loginOf(req.app);
682
682
  if (!login) return next();
683
683
  void serveLoginPage(req, res, login).catch((err) => {
684
- console.warn("[agentready] sign-in form failed:", err);
684
+ console.warn("[exposify] sign-in form failed:", err);
685
685
  if (!res.headersSent) res.writeHead(503).end("sign-in is unavailable");
686
686
  });
687
687
  return;
@@ -699,7 +699,7 @@ function aiReady(options = {}) {
699
699
  }
700
700
  if (path !== MCP_PATH && !path.startsWith(`${MCP_PATH}/`)) return next();
701
701
  if (disabled) {
702
- res.writeHead(503).end("agentready is disabled due to an internal error");
702
+ res.writeHead(503).end("exposify is disabled due to an internal error");
703
703
  return;
704
704
  }
705
705
  const authorized = gate ? gate.check(headersOf(req)).then(async (result) => {
@@ -707,7 +707,7 @@ function aiReady(options = {}) {
707
707
  await sendWebResponse(res, result.response);
708
708
  return null;
709
709
  }, (err) => {
710
- console.warn("[agentready] oauth gate failed:", err);
710
+ console.warn("[exposify] oauth gate failed:", err);
711
711
  if (!res.headersSent) res.writeHead(503).end("authorization check failed");
712
712
  return null;
713
713
  }) : brokerGate ? report(req, resourceOf(req, path, MCP_PATH)).then(async (verdict) => {
@@ -717,7 +717,7 @@ function aiReady(options = {}) {
717
717
  await sendWebResponse(res, result.response);
718
718
  return null;
719
719
  }).catch((err) => {
720
- console.warn("[agentready] broker check failed:", err);
720
+ console.warn("[exposify] broker check failed:", err);
721
721
  if (!res.headersSent) res.writeHead(503).end("authorization check failed");
722
722
  return null;
723
723
  }) : Promise.resolve({});
@@ -752,7 +752,7 @@ function aiReady(options = {}) {
752
752
  err
753
753
  );
754
754
  }
755
- if (!res.headersSent) res.writeHead(503).end("agentready failed to initialize");
755
+ if (!res.headersSent) res.writeHead(503).end("exposify failed to initialize");
756
756
  });
757
757
  };
758
758
  async function serveLoginPage(req, res, login) {
package/dist/index.js CHANGED
@@ -185,7 +185,7 @@ function warnMountSkipped() {
185
185
  warnedAboutSkippedMount = true;
186
186
  try {
187
187
  console.warn(
188
- `[agentready] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
188
+ `[exposify] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
189
189
  );
190
190
  } catch {
191
191
  }
@@ -342,7 +342,7 @@ async function loadOpenApi(cwd, option, warn) {
342
342
 
343
343
  // src/surface.ts
344
344
  import { applyOpenApi } from "@exposify/core";
345
- var warnOnce = (m) => console.warn(`[agentready] warning: ${m}`);
345
+ var warnOnce = (m) => console.warn(`[exposify] warning: ${m}`);
346
346
  var NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
347
347
  var IDENTITY_HEADERS = ["host", "forwarded", "x-forwarded-for", "x-forwarded-proto", "x-forwarded-host", "x-forwarded-port"];
348
348
  var DISPATCH_HEADER = "x-exposify-dispatch";
@@ -561,7 +561,7 @@ import {
561
561
  var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("exposify.express.aiReadyOptions");
562
562
  var MCP_PATH = "/mcp";
563
563
  var PRM_ROOT = "/.well-known/oauth-protected-resource";
564
- var LOG_PREFIX = "[agentready]";
564
+ var LOG_PREFIX = "[exposify]";
565
565
  function toolsSummary(routes, docs, cfg) {
566
566
  const counts = countTools(routes, cfg);
567
567
  const details = counts.hiddenWrites > 0 ? [`${counts.hiddenWrites} write route${counts.hiddenWrites === 1 ? "" : "s"} hidden (opt in with exposed())`] : [];
@@ -689,7 +689,7 @@ function aiReady(options = {}) {
689
689
  const login = loginOf(req.app);
690
690
  if (!login) return next();
691
691
  void serveLoginPage(req, res, login).catch((err) => {
692
- console.warn("[agentready] sign-in form failed:", err);
692
+ console.warn("[exposify] sign-in form failed:", err);
693
693
  if (!res.headersSent) res.writeHead(503).end("sign-in is unavailable");
694
694
  });
695
695
  return;
@@ -707,7 +707,7 @@ function aiReady(options = {}) {
707
707
  }
708
708
  if (path !== MCP_PATH && !path.startsWith(`${MCP_PATH}/`)) return next();
709
709
  if (disabled) {
710
- res.writeHead(503).end("agentready is disabled due to an internal error");
710
+ res.writeHead(503).end("exposify is disabled due to an internal error");
711
711
  return;
712
712
  }
713
713
  const authorized = gate ? gate.check(headersOf(req)).then(async (result) => {
@@ -715,7 +715,7 @@ function aiReady(options = {}) {
715
715
  await sendWebResponse(res, result.response);
716
716
  return null;
717
717
  }, (err) => {
718
- console.warn("[agentready] oauth gate failed:", err);
718
+ console.warn("[exposify] oauth gate failed:", err);
719
719
  if (!res.headersSent) res.writeHead(503).end("authorization check failed");
720
720
  return null;
721
721
  }) : brokerGate ? report(req, resourceOf(req, path, MCP_PATH)).then(async (verdict) => {
@@ -725,7 +725,7 @@ function aiReady(options = {}) {
725
725
  await sendWebResponse(res, result.response);
726
726
  return null;
727
727
  }).catch((err) => {
728
- console.warn("[agentready] broker check failed:", err);
728
+ console.warn("[exposify] broker check failed:", err);
729
729
  if (!res.headersSent) res.writeHead(503).end("authorization check failed");
730
730
  return null;
731
731
  }) : Promise.resolve({});
@@ -760,7 +760,7 @@ function aiReady(options = {}) {
760
760
  err
761
761
  );
762
762
  }
763
- if (!res.headersSent) res.writeHead(503).end("agentready failed to initialize");
763
+ if (!res.headersSent) res.writeHead(503).end("exposify failed to initialize");
764
764
  });
765
765
  };
766
766
  async function serveLoginPage(req, res, login) {
@@ -2137,6 +2137,8 @@ function parseLoginTheme(raw) {
2137
2137
  var DEFAULT_TIMEOUT_MS = 5e3;
2138
2138
  var REPORT_TIMEOUT_MS = 3e3;
2139
2139
  var REPORT_REFRESH_MS = 6e4;
2140
+ var CONNECT_INFO_TTL_MS = 6e4;
2141
+ var CONNECT_INFO_RETRY_MS = 5e3;
2140
2142
  var DEFAULT_MAX_CACHE_MS = 3e4;
2141
2143
  var EXPIRY_SKEW_MS = 6e4;
2142
2144
  var MAX_CACHE_ENTRIES = 500;
@@ -2247,6 +2249,21 @@ function createHostedAuthResolver(config) {
2247
2249
  let passwordDirect;
2248
2250
  const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
2249
2251
  const stepSealer = createSealer(licenseKey, "exposify login-step v1");
2252
+ let connectInfo;
2253
+ let connectInflight;
2254
+ const fetchConnectInfo = async () => {
2255
+ try {
2256
+ const res = await fetchImpl(`${issuer}/connect-info`, { signal: AbortSignal.timeout(REPORT_TIMEOUT_MS) });
2257
+ if (res.status === 404) {
2258
+ connectInfo = { body: null, at: Date.now() - CONNECT_INFO_TTL_MS + CONNECT_INFO_RETRY_MS };
2259
+ } else if (res.ok) {
2260
+ const body = await res.json();
2261
+ connectInfo = { body: body && typeof body === "object" ? body : null, at: Date.now() };
2262
+ }
2263
+ } catch {
2264
+ }
2265
+ return connectInfo?.body ?? null;
2266
+ };
2250
2267
  const info = {
2251
2268
  issuer,
2252
2269
  sealLoginStep: (text) => stepSealer.seal(text),
@@ -2297,6 +2314,13 @@ function createHostedAuthResolver(config) {
2297
2314
  await brokerPost("session-ended", {}, authorization);
2298
2315
  },
2299
2316
  supabasePasswordDirect: () => passwordDirect,
2317
+ connectInfo() {
2318
+ if (connectInfo && Date.now() - connectInfo.at < CONNECT_INFO_TTL_MS) return Promise.resolve(connectInfo.body);
2319
+ return connectInflight ??= fetchConnectInfo().finally(() => connectInflight = void 0);
2320
+ },
2321
+ countConnectOpen() {
2322
+ void brokerPost("connect-open", {});
2323
+ },
2300
2324
  reportLogin(report) {
2301
2325
  const key = JSON.stringify(report);
2302
2326
  const send2 = async () => {
@@ -2339,7 +2363,7 @@ function hostedResolverFromEnv() {
2339
2363
  if (!key) return void 0;
2340
2364
  if (!parseExposifyKey(key)) {
2341
2365
  try {
2342
- console.warn(`[agentready] warning: ${EXPOSIFY_KEY_ENV} isn't a secret key from the Exposify dashboard (exk_...); ignoring it`);
2366
+ console.warn(`[exposify] warning: ${EXPOSIFY_KEY_ENV} isn't a secret key from the Exposify dashboard (exk_...); ignoring it`);
2343
2367
  } catch {
2344
2368
  }
2345
2369
  return void 0;
@@ -6758,7 +6782,7 @@ function warnMountSkipped() {
6758
6782
  warnedAboutSkippedMount = true;
6759
6783
  try {
6760
6784
  console.warn(
6761
- `[agentready] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
6785
+ `[exposify] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
6762
6786
  );
6763
6787
  } catch {
6764
6788
  }
@@ -2141,6 +2141,8 @@ function parseLoginTheme(raw) {
2141
2141
  var DEFAULT_TIMEOUT_MS = 5e3;
2142
2142
  var REPORT_TIMEOUT_MS = 3e3;
2143
2143
  var REPORT_REFRESH_MS = 6e4;
2144
+ var CONNECT_INFO_TTL_MS = 6e4;
2145
+ var CONNECT_INFO_RETRY_MS = 5e3;
2144
2146
  var DEFAULT_MAX_CACHE_MS = 3e4;
2145
2147
  var EXPIRY_SKEW_MS = 6e4;
2146
2148
  var MAX_CACHE_ENTRIES = 500;
@@ -2251,6 +2253,21 @@ function createHostedAuthResolver(config) {
2251
2253
  let passwordDirect;
2252
2254
  const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
2253
2255
  const stepSealer = createSealer(licenseKey, "exposify login-step v1");
2256
+ let connectInfo;
2257
+ let connectInflight;
2258
+ const fetchConnectInfo = async () => {
2259
+ try {
2260
+ const res = await fetchImpl(`${issuer}/connect-info`, { signal: AbortSignal.timeout(REPORT_TIMEOUT_MS) });
2261
+ if (res.status === 404) {
2262
+ connectInfo = { body: null, at: Date.now() - CONNECT_INFO_TTL_MS + CONNECT_INFO_RETRY_MS };
2263
+ } else if (res.ok) {
2264
+ const body = await res.json();
2265
+ connectInfo = { body: body && typeof body === "object" ? body : null, at: Date.now() };
2266
+ }
2267
+ } catch {
2268
+ }
2269
+ return connectInfo?.body ?? null;
2270
+ };
2254
2271
  const info = {
2255
2272
  issuer,
2256
2273
  sealLoginStep: (text) => stepSealer.seal(text),
@@ -2301,6 +2318,13 @@ function createHostedAuthResolver(config) {
2301
2318
  await brokerPost("session-ended", {}, authorization);
2302
2319
  },
2303
2320
  supabasePasswordDirect: () => passwordDirect,
2321
+ connectInfo() {
2322
+ if (connectInfo && Date.now() - connectInfo.at < CONNECT_INFO_TTL_MS) return Promise.resolve(connectInfo.body);
2323
+ return connectInflight ??= fetchConnectInfo().finally(() => connectInflight = void 0);
2324
+ },
2325
+ countConnectOpen() {
2326
+ void brokerPost("connect-open", {});
2327
+ },
2304
2328
  reportLogin(report) {
2305
2329
  const key = JSON.stringify(report);
2306
2330
  const send2 = async () => {
@@ -2343,7 +2367,7 @@ function hostedResolverFromEnv() {
2343
2367
  if (!key) return void 0;
2344
2368
  if (!parseExposifyKey(key)) {
2345
2369
  try {
2346
- console.warn(`[agentready] warning: ${EXPOSIFY_KEY_ENV} isn't a secret key from the Exposify dashboard (exk_...); ignoring it`);
2370
+ console.warn(`[exposify] warning: ${EXPOSIFY_KEY_ENV} isn't a secret key from the Exposify dashboard (exk_...); ignoring it`);
2347
2371
  } catch {
2348
2372
  }
2349
2373
  return void 0;
@@ -6762,7 +6786,7 @@ function warnMountSkipped() {
6762
6786
  warnedAboutSkippedMount = true;
6763
6787
  try {
6764
6788
  console.warn(
6765
- `[agentready] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
6789
+ `[exposify] skipped one or more mounted routers: their mount path could not be determined automatically, so routes under them will not be exposed as tools. If aiReady() is registered before they're mounted, this is usually automatic (Express 5's mount recorder); otherwise pass them explicitly via aiReady({ mounts: { "/path": router } }).`
6766
6790
  );
6767
6791
  } catch {
6768
6792
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exposify/express",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,8 +26,8 @@
26
26
  "prepack": "yarn build"
27
27
  },
28
28
  "dependencies": {
29
- "@exposify/cli": "0.0.7",
30
- "@exposify/core": "0.3.17"
29
+ "@exposify/cli": "0.0.9",
30
+ "@exposify/core": "0.3.19"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "express": ">=4 <6"