@exposify/express 0.3.14 → 0.3.15
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/LICENSE +1 -1
- package/README.md +19 -19
- package/dist/cli.js +5 -5
- package/dist/index.cjs +13 -13
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +13 -13
- package/dist/scan-preload.cjs +25 -22
- package/dist/scan-preload.js +25 -22
- package/package.json +6 -6
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ Agents then get validated inputs (a `minimum: 1` rejects `qty: 0` before any
|
|
|
52
52
|
request is made) and your summaries as tool descriptions. `openapi` also
|
|
53
53
|
takes a path to a JSON file, for any Express app with an OpenAPI document.
|
|
54
54
|
Description precedence: `describe()` in code, then
|
|
55
|
-
`
|
|
55
|
+
`agemno.descriptions.json`, then OpenAPI, then the derived text.
|
|
56
56
|
|
|
57
57
|
## Guided setup: `npx @exposify/express setup`
|
|
58
58
|
|
|
@@ -102,17 +102,17 @@ give your tools real descriptions (below).
|
|
|
102
102
|
the `CI` environment variable is set.
|
|
103
103
|
- **How your users sign in** (step 4): `setup` reads your `package.json`.
|
|
104
104
|
With Auth0, Clerk, WorkOS or Kinde it points you at the `oauth` option
|
|
105
|
-
(your provider already signs agents in). Otherwise it offers
|
|
106
|
-
hosted broker and a browser sign-in that writes `
|
|
105
|
+
(your provider already signs agents in). Otherwise it offers agemno's
|
|
106
|
+
hosted broker and a browser sign-in that writes `AGEMNO_KEY` to `.env`
|
|
107
107
|
(a new project named after your app, or an existing one). `--no-sign-in`
|
|
108
|
-
skips the step; `
|
|
108
|
+
skips the step; `AGEMNO_NO_BROWSER=1` prints the link instead of
|
|
109
109
|
opening it.
|
|
110
110
|
|
|
111
111
|
**Tool descriptions.** Agents pick tools by their descriptions, and by default
|
|
112
112
|
they get one derived from the route ("Create an order (POST /orders)").
|
|
113
113
|
`setup`'s step 5, or `npx @exposify/express describe` any time, replaces those:
|
|
114
114
|
|
|
115
|
-
- `a` sends each route's handler source to
|
|
115
|
+
- `a` sends each route's handler source to agemno's description service,
|
|
116
116
|
which writes a proposal with an LLM (`--no-source` sends only method, path
|
|
117
117
|
and middleware names). Strings that look like secrets are masked on your
|
|
118
118
|
machine first, and the CLI says exactly what it's about to send and where. The
|
|
@@ -124,12 +124,12 @@ they get one derived from the route ("Create an order (POST /orders)").
|
|
|
124
124
|
a write does is the worst thing an agent can be told. You don't need an
|
|
125
125
|
LLM key: without an account you get a small free allowance (2 routes a
|
|
126
126
|
day by default); when it runs out, the CLI links to a free sign-up and takes your project's secret key
|
|
127
|
-
(and offers to save it in `~/.config/
|
|
128
|
-
only by you). `--license-key` / `
|
|
129
|
-
`--api-url` / `
|
|
127
|
+
(and offers to save it in `~/.config/agemno/credentials.json`, readable
|
|
128
|
+
only by you). `--license-key` / `AGEMNO_KEY` pass one directly;
|
|
129
|
+
`--api-url` / `AGEMNO_API_URL` point at another service.
|
|
130
130
|
- `w` lets you write them yourself (`e` opens `$EDITOR`).
|
|
131
131
|
|
|
132
|
-
They're saved to `
|
|
132
|
+
They're saved to `agemno.descriptions.json`, which `aiReady()` loads at
|
|
133
133
|
startup (`descriptions: "path"` or `false` to change that); `describe()` text
|
|
134
134
|
in your code still wins. Text you wrote or edited is never overwritten. When
|
|
135
135
|
a route's handler changes, its AI description is offered again; yours is
|
|
@@ -142,7 +142,7 @@ whether agents get it; add `--why` for the reason on each (`--json` for
|
|
|
142
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
|
+
`agemno.tools.json` (each route agents can call, and its tool name).
|
|
146
146
|
`setup` offers to save the same file as its last question (default no;
|
|
147
147
|
`--snapshot` to save without asking). Commit it, and run
|
|
148
148
|
`npx @exposify/express inspect --check` in CI: it exits 1,
|
|
@@ -152,7 +152,7 @@ intended, so it shows up in the PR diff for review.
|
|
|
152
152
|
|
|
153
153
|
## AI assistants sign in with your own login
|
|
154
154
|
|
|
155
|
-
With `
|
|
155
|
+
With `AGEMNO_KEY` set (the hosted broker), an AI assistant that connects
|
|
156
156
|
signs in with your app's own login, with no code:
|
|
157
157
|
|
|
158
158
|
- On the first request, `aiReady()` finds your login among your routes: a
|
|
@@ -160,14 +160,14 @@ signs in with your app's own login, with no code:
|
|
|
160
160
|
`req.body`, and sets a cookie, a session (`req.session`, passport's
|
|
161
161
|
`req.login`) or signs a token. Sign-up, sign-out and reset routes are never
|
|
162
162
|
picked.
|
|
163
|
-
- When Claude connects, the person clicks Allow on
|
|
163
|
+
- When Claude connects, the person clicks Allow on agemno's page and lands
|
|
164
164
|
on a sign-in form served at `/mcp/login`, on your domain. On submit,
|
|
165
165
|
`aiReady()` sends your login route the fields through your own app (no
|
|
166
166
|
browser cookies, the person's address forwarded), and the session it
|
|
167
167
|
creates goes to the broker for this connection. Every tool call then runs
|
|
168
168
|
as that person; cookies you set later are kept; a 401 or a redirect to the
|
|
169
169
|
login ends the connection, so the assistant asks to connect again.
|
|
170
|
-
- `aiReady()` tells
|
|
170
|
+
- `aiReady()` tells agemno about the login and its MCP address before
|
|
171
171
|
answering `/mcp`, so there's nothing to add in the dashboard. The address
|
|
172
172
|
comes from the request's `Host` (and `X-Forwarded-Host` only when you've
|
|
173
173
|
set Express's `trust proxy`, as you should behind a proxy). The first
|
|
@@ -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
|
-
(`[
|
|
191
|
+
(`[agemno] 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
|
-
[
|
|
276
|
+
[agemno] 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
|
-
[
|
|
282
|
+
[agemno] MCP failed to start: <reason>
|
|
283
283
|
fix: <what to do>
|
|
284
|
-
[
|
|
284
|
+
[agemno] warning: MCP is up at /mcp but exposes no route tools
|
|
285
285
|
fix: register app.use(aiReady()) before your routers, ...
|
|
286
|
-
[
|
|
286
|
+
[agemno] 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
|
|
|
@@ -356,7 +356,7 @@ aiReady({
|
|
|
356
356
|
telemetry?: { enabled: boolean; sink?(event): void; capturePayloads?: boolean },
|
|
357
357
|
cwd?: string, // default process.cwd()
|
|
358
358
|
mounts?: Record<string, unknown>, // explicit router/sub-app overrides
|
|
359
|
-
descriptions?: string | false, // default "
|
|
359
|
+
descriptions?: string | false, // default "agemno.descriptions.json" if present
|
|
360
360
|
openapi?: string | object, // your OpenAPI document: tool schemas + descriptions
|
|
361
361
|
login?: false | { path, fields, body? }, // your own login, for the hosted broker (found by itself)
|
|
362
362
|
});
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// bin/
|
|
3
|
+
// bin/agemno-express.ts
|
|
4
4
|
import { runCli } from "@exposify/cli";
|
|
5
5
|
|
|
6
6
|
// bin/adapter.ts
|
|
@@ -168,11 +168,11 @@ import { fileURLToPath, pathToFileURL } from "url";
|
|
|
168
168
|
|
|
169
169
|
// bin/scan/env.ts
|
|
170
170
|
var SCAN_ENV = {
|
|
171
|
-
entry: "
|
|
171
|
+
entry: "AGEMNO_SCAN_ENTRY",
|
|
172
172
|
/** Set to "1" in the child so an app can skip heavy boot work if it wants. */
|
|
173
|
-
flag: "
|
|
173
|
+
flag: "AGEMNO_SCAN",
|
|
174
174
|
/** How long to wait for listen() after the last route registration (ms). */
|
|
175
|
-
quietMs: "
|
|
175
|
+
quietMs: "AGEMNO_SCAN_QUIET_MS"
|
|
176
176
|
};
|
|
177
177
|
|
|
178
178
|
// bin/scan/run.ts
|
|
@@ -285,7 +285,7 @@ function applyStep(result, config, viaNest, root, code) {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
// bin/
|
|
288
|
+
// bin/agemno-express.ts
|
|
289
289
|
runCli(process.argv.slice(2), expressAdapter).then(
|
|
290
290
|
(code) => process.exit(code),
|
|
291
291
|
(err) => {
|
package/dist/index.cjs
CHANGED
|
@@ -76,7 +76,7 @@ function installMountRecorder(routerCtor) {
|
|
|
76
76
|
} catch {
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("
|
|
79
|
+
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("agemno.express.recordMountOwner");
|
|
80
80
|
var taggedOwners = /* @__PURE__ */ new WeakMap();
|
|
81
81
|
function recordTaggedOwner(parent, args) {
|
|
82
82
|
const stack = [...args];
|
|
@@ -184,7 +184,7 @@ function warnMountSkipped() {
|
|
|
184
184
|
warnedAboutSkippedMount = true;
|
|
185
185
|
try {
|
|
186
186
|
console.warn(
|
|
187
|
-
`[
|
|
187
|
+
`[agemno] 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,10 +341,10 @@ 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(`[
|
|
344
|
+
var warnOnce = (m) => console.warn(`[agemno] 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
|
-
var DISPATCH_HEADER = "x-
|
|
347
|
+
var DISPATCH_HEADER = "x-agemno-dispatch";
|
|
348
348
|
var callerStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
349
349
|
function callerOf(req) {
|
|
350
350
|
const headers = {};
|
|
@@ -368,7 +368,7 @@ async function buildExpressSurface(app, cfg, cwd, opts) {
|
|
|
368
368
|
const info = await readPackageInfo(cwd);
|
|
369
369
|
return {
|
|
370
370
|
info,
|
|
371
|
-
// Text precedence: describe() in code >
|
|
371
|
+
// Text precedence: describe() in code > agemno.descriptions.json (reviewed) > OpenAPI > derived.
|
|
372
372
|
routes: (0, import_core3.applyDescriptions)(
|
|
373
373
|
(0, import_core4.applyOpenApi)(routesFromExpress(app, opts), await loadOpenApi(cwd, opts?.openapi, warnOnce)),
|
|
374
374
|
await loadDescriptions(cwd, opts?.descriptions, warnOnce)
|
|
@@ -550,10 +550,10 @@ function findExpressLogin(app, mounts) {
|
|
|
550
550
|
|
|
551
551
|
// src/index.ts
|
|
552
552
|
var import_core7 = require("@exposify/core");
|
|
553
|
-
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("
|
|
553
|
+
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("agemno.express.aiReadyOptions");
|
|
554
554
|
var MCP_PATH = "/mcp";
|
|
555
555
|
var PRM_ROOT = "/.well-known/oauth-protected-resource";
|
|
556
|
-
var LOG_PREFIX = "[
|
|
556
|
+
var LOG_PREFIX = "[agemno]";
|
|
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("[
|
|
684
|
+
console.warn("[agemno] 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("
|
|
702
|
+
res.writeHead(503).end("agemno 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("[
|
|
710
|
+
console.warn("[agemno] 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("[
|
|
720
|
+
console.warn("[agemno] 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("
|
|
755
|
+
if (!res.headersSent) res.writeHead(503).end("agemno failed to initialize");
|
|
756
756
|
});
|
|
757
757
|
};
|
|
758
758
|
async function serveLoginPage(req, res, login) {
|
|
@@ -845,7 +845,7 @@ async function initialize(app, cfg, cwd, mounts, extras, onReady) {
|
|
|
845
845
|
};
|
|
846
846
|
const getKb = async () => {
|
|
847
847
|
const s = await getSurface();
|
|
848
|
-
kbPromise ??= (0, import_node2.loadOrBuildKb)(s.docs, (0, import_node_path3.join)(cwd, ".
|
|
848
|
+
kbPromise ??= (0, import_node2.loadOrBuildKb)(s.docs, (0, import_node_path3.join)(cwd, ".agemno"));
|
|
849
849
|
return kbPromise;
|
|
850
850
|
};
|
|
851
851
|
const built = await getSurface();
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RouteInfo,
|
|
1
|
+
import { RouteInfo, AgemnoOptions } from '@exposify/core';
|
|
2
2
|
export { HostedAuthResolverConfig, completeMcpLogin, createHostedAuthResolver, describe, exposed, hidden, hostedBroker } from '@exposify/core';
|
|
3
3
|
|
|
4
4
|
interface RoutesFromExpressOptions {
|
|
@@ -18,7 +18,7 @@ declare function routesFromExpress(app: unknown, opts?: RoutesFromExpressOptions
|
|
|
18
18
|
/** Symbol under which `aiReady()`'s middleware carries the options it was given. */
|
|
19
19
|
declare const AIREADY_OPTIONS_TAG: unique symbol;
|
|
20
20
|
|
|
21
|
-
interface ExpressAiReadyOptions extends
|
|
21
|
+
interface ExpressAiReadyOptions extends AgemnoOptions {
|
|
22
22
|
/**
|
|
23
23
|
* The app's own login, for Connect with the hosted broker: AI assistants
|
|
24
24
|
* sign in on a form served at /mcp/login, which makes this login request
|
|
@@ -43,7 +43,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
43
43
|
fields: string[];
|
|
44
44
|
body?: "json" | "form";
|
|
45
45
|
};
|
|
46
|
-
/** Where docs globs and the .
|
|
46
|
+
/** Where docs globs and the .agemno cache resolve. Default: process.cwd() */
|
|
47
47
|
cwd?: string;
|
|
48
48
|
/**
|
|
49
49
|
* Explicit mount paths for routers (or sub-apps) introspection can't
|
|
@@ -58,7 +58,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
58
58
|
mounts?: Record<string, unknown>;
|
|
59
59
|
/**
|
|
60
60
|
* Tool descriptions file written by `@exposify/express setup`/`describe`.
|
|
61
|
-
* Default:
|
|
61
|
+
* Default: agemno.descriptions.json in `cwd`, if present. `false` ignores it.
|
|
62
62
|
* describe() text in code still wins over it.
|
|
63
63
|
*/
|
|
64
64
|
descriptions?: string | false;
|
|
@@ -66,7 +66,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
66
66
|
* The app's OpenAPI document: an object (e.g. `SwaggerModule.createDocument(app, …)`
|
|
67
67
|
* in NestJS) or a path to a JSON file. Matching operations give tools real
|
|
68
68
|
* input schemas, and their summary/description becomes the tool description
|
|
69
|
-
* (below describe() and
|
|
69
|
+
* (below describe() and agemno.descriptions.json).
|
|
70
70
|
*/
|
|
71
71
|
openapi?: string | object;
|
|
72
72
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RouteInfo,
|
|
1
|
+
import { RouteInfo, AgemnoOptions } from '@exposify/core';
|
|
2
2
|
export { HostedAuthResolverConfig, completeMcpLogin, createHostedAuthResolver, describe, exposed, hidden, hostedBroker } from '@exposify/core';
|
|
3
3
|
|
|
4
4
|
interface RoutesFromExpressOptions {
|
|
@@ -18,7 +18,7 @@ declare function routesFromExpress(app: unknown, opts?: RoutesFromExpressOptions
|
|
|
18
18
|
/** Symbol under which `aiReady()`'s middleware carries the options it was given. */
|
|
19
19
|
declare const AIREADY_OPTIONS_TAG: unique symbol;
|
|
20
20
|
|
|
21
|
-
interface ExpressAiReadyOptions extends
|
|
21
|
+
interface ExpressAiReadyOptions extends AgemnoOptions {
|
|
22
22
|
/**
|
|
23
23
|
* The app's own login, for Connect with the hosted broker: AI assistants
|
|
24
24
|
* sign in on a form served at /mcp/login, which makes this login request
|
|
@@ -43,7 +43,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
43
43
|
fields: string[];
|
|
44
44
|
body?: "json" | "form";
|
|
45
45
|
};
|
|
46
|
-
/** Where docs globs and the .
|
|
46
|
+
/** Where docs globs and the .agemno cache resolve. Default: process.cwd() */
|
|
47
47
|
cwd?: string;
|
|
48
48
|
/**
|
|
49
49
|
* Explicit mount paths for routers (or sub-apps) introspection can't
|
|
@@ -58,7 +58,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
58
58
|
mounts?: Record<string, unknown>;
|
|
59
59
|
/**
|
|
60
60
|
* Tool descriptions file written by `@exposify/express setup`/`describe`.
|
|
61
|
-
* Default:
|
|
61
|
+
* Default: agemno.descriptions.json in `cwd`, if present. `false` ignores it.
|
|
62
62
|
* describe() text in code still wins over it.
|
|
63
63
|
*/
|
|
64
64
|
descriptions?: string | false;
|
|
@@ -66,7 +66,7 @@ interface ExpressAiReadyOptions extends AgentReadyOptions {
|
|
|
66
66
|
* The app's OpenAPI document: an object (e.g. `SwaggerModule.createDocument(app, …)`
|
|
67
67
|
* in NestJS) or a path to a JSON file. Matching operations give tools real
|
|
68
68
|
* input schemas, and their summary/description becomes the tool description
|
|
69
|
-
* (below describe() and
|
|
69
|
+
* (below describe() and agemno.descriptions.json).
|
|
70
70
|
*/
|
|
71
71
|
openapi?: string | object;
|
|
72
72
|
}
|
package/dist/index.js
CHANGED
|
@@ -77,7 +77,7 @@ function installMountRecorder(routerCtor) {
|
|
|
77
77
|
} catch {
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("
|
|
80
|
+
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("agemno.express.recordMountOwner");
|
|
81
81
|
var taggedOwners = /* @__PURE__ */ new WeakMap();
|
|
82
82
|
function recordTaggedOwner(parent, args) {
|
|
83
83
|
const stack = [...args];
|
|
@@ -185,7 +185,7 @@ function warnMountSkipped() {
|
|
|
185
185
|
warnedAboutSkippedMount = true;
|
|
186
186
|
try {
|
|
187
187
|
console.warn(
|
|
188
|
-
`[
|
|
188
|
+
`[agemno] 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,10 +342,10 @@ 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(`[
|
|
345
|
+
var warnOnce = (m) => console.warn(`[agemno] 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
|
-
var DISPATCH_HEADER = "x-
|
|
348
|
+
var DISPATCH_HEADER = "x-agemno-dispatch";
|
|
349
349
|
var callerStore = new AsyncLocalStorage();
|
|
350
350
|
function callerOf(req) {
|
|
351
351
|
const headers = {};
|
|
@@ -369,7 +369,7 @@ async function buildExpressSurface(app, cfg, cwd, opts) {
|
|
|
369
369
|
const info = await readPackageInfo(cwd);
|
|
370
370
|
return {
|
|
371
371
|
info,
|
|
372
|
-
// Text precedence: describe() in code >
|
|
372
|
+
// Text precedence: describe() in code > agemno.descriptions.json (reviewed) > OpenAPI > derived.
|
|
373
373
|
routes: applyDescriptions(
|
|
374
374
|
applyOpenApi(routesFromExpress(app, opts), await loadOpenApi(cwd, opts?.openapi, warnOnce)),
|
|
375
375
|
await loadDescriptions(cwd, opts?.descriptions, warnOnce)
|
|
@@ -558,10 +558,10 @@ import {
|
|
|
558
558
|
createHostedAuthResolver,
|
|
559
559
|
hostedBroker
|
|
560
560
|
} from "@exposify/core";
|
|
561
|
-
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("
|
|
561
|
+
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("agemno.express.aiReadyOptions");
|
|
562
562
|
var MCP_PATH = "/mcp";
|
|
563
563
|
var PRM_ROOT = "/.well-known/oauth-protected-resource";
|
|
564
|
-
var LOG_PREFIX = "[
|
|
564
|
+
var LOG_PREFIX = "[agemno]";
|
|
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("[
|
|
692
|
+
console.warn("[agemno] 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("
|
|
710
|
+
res.writeHead(503).end("agemno 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("[
|
|
718
|
+
console.warn("[agemno] 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("[
|
|
728
|
+
console.warn("[agemno] 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("
|
|
763
|
+
if (!res.headersSent) res.writeHead(503).end("agemno failed to initialize");
|
|
764
764
|
});
|
|
765
765
|
};
|
|
766
766
|
async function serveLoginPage(req, res, login) {
|
|
@@ -853,7 +853,7 @@ async function initialize(app, cfg, cwd, mounts, extras, onReady) {
|
|
|
853
853
|
};
|
|
854
854
|
const getKb = async () => {
|
|
855
855
|
const s = await getSurface();
|
|
856
|
-
kbPromise ??= loadOrBuildKb(s.docs, join3(cwd, ".
|
|
856
|
+
kbPromise ??= loadOrBuildKb(s.docs, join3(cwd, ".agemno"));
|
|
857
857
|
return kbPromise;
|
|
858
858
|
};
|
|
859
859
|
const built = await getSurface();
|
package/dist/scan-preload.cjs
CHANGED
|
@@ -1828,10 +1828,10 @@ init_cjs_shims();
|
|
|
1828
1828
|
|
|
1829
1829
|
// ../core/src/markers.ts
|
|
1830
1830
|
init_cjs_shims();
|
|
1831
|
-
var
|
|
1831
|
+
var AGEMNO_MARKER = /* @__PURE__ */ Symbol.for("agemno.marker");
|
|
1832
1832
|
function getMarker(fn) {
|
|
1833
1833
|
if (typeof fn !== "function") return void 0;
|
|
1834
|
-
return fn[
|
|
1834
|
+
return fn[AGEMNO_MARKER];
|
|
1835
1835
|
}
|
|
1836
1836
|
|
|
1837
1837
|
// ../core/src/policy.ts
|
|
@@ -2022,16 +2022,16 @@ function resolveOAuthOptions(opts) {
|
|
|
2022
2022
|
};
|
|
2023
2023
|
}
|
|
2024
2024
|
|
|
2025
|
-
// ../core/src/
|
|
2025
|
+
// ../core/src/agemno-key.ts
|
|
2026
2026
|
init_cjs_shims();
|
|
2027
2027
|
var DEFAULT_BROKER_URL = "https://broker.agemno.com";
|
|
2028
|
-
var
|
|
2029
|
-
var BROKER_URL_ENV = "
|
|
2028
|
+
var AGEMNO_KEY_ENV = "AGEMNO_KEY";
|
|
2029
|
+
var BROKER_URL_ENV = "AGEMNO_BROKER_URL";
|
|
2030
2030
|
var DEFAULT_API_URL = "https://infra.agemno.com";
|
|
2031
|
-
var API_URL_ENV = "
|
|
2032
|
-
var HOSTED_RESOLVER = /* @__PURE__ */ Symbol.for("
|
|
2031
|
+
var API_URL_ENV = "AGEMNO_API_URL";
|
|
2032
|
+
var HOSTED_RESOLVER = /* @__PURE__ */ Symbol.for("agemno.hostedResolver");
|
|
2033
2033
|
var KEY_RE = /^exk_([0-9a-f]{32})_[A-Za-z0-9_-]{20,}$/;
|
|
2034
|
-
function
|
|
2034
|
+
function parseAgemnoKey(key) {
|
|
2035
2035
|
const m = KEY_RE.exec(key.trim());
|
|
2036
2036
|
if (!m) return null;
|
|
2037
2037
|
const h = m[1];
|
|
@@ -2155,8 +2155,8 @@ function signInMethodsOf(body) {
|
|
|
2155
2155
|
}
|
|
2156
2156
|
function createHostedAuthResolver(config) {
|
|
2157
2157
|
const givenKey = config.key ?? config.licenseKey;
|
|
2158
|
-
const projectId = config.projectId ?? (config.key ?
|
|
2159
|
-
if (!givenKey) throw new Error("createHostedAuthResolver: pass your secret key as `key` (the
|
|
2158
|
+
const projectId = config.projectId ?? (config.key ? parseAgemnoKey(config.key)?.projectId : void 0);
|
|
2159
|
+
if (!givenKey) throw new Error("createHostedAuthResolver: pass your secret key as `key` (the AGEMNO_KEY value)");
|
|
2160
2160
|
const licenseKey = givenKey;
|
|
2161
2161
|
if (!projectId) {
|
|
2162
2162
|
throw new Error("createHostedAuthResolver: that key doesn't carry a project id (expected exk_...); rotate it in the dashboard, or pass projectId");
|
|
@@ -2248,7 +2248,7 @@ function createHostedAuthResolver(config) {
|
|
|
2248
2248
|
const reported = /* @__PURE__ */ new Map();
|
|
2249
2249
|
let passwordDirect;
|
|
2250
2250
|
const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
|
|
2251
|
-
const stepSealer = createSealer(licenseKey, "
|
|
2251
|
+
const stepSealer = createSealer(licenseKey, "agemno login-step v1");
|
|
2252
2252
|
let connectInfo;
|
|
2253
2253
|
let connectInflight;
|
|
2254
2254
|
const fetchConnectInfo = async () => {
|
|
@@ -2321,6 +2321,9 @@ function createHostedAuthResolver(config) {
|
|
|
2321
2321
|
countConnectOpen() {
|
|
2322
2322
|
void brokerPost("connect-open", {});
|
|
2323
2323
|
},
|
|
2324
|
+
countConnectComponentLoad() {
|
|
2325
|
+
void brokerPost("connect-component-load", {});
|
|
2326
|
+
},
|
|
2324
2327
|
reportLogin(report) {
|
|
2325
2328
|
const key = JSON.stringify(report);
|
|
2326
2329
|
const send2 = async () => {
|
|
@@ -2359,11 +2362,11 @@ function createHostedAuthResolver(config) {
|
|
|
2359
2362
|
return resolver;
|
|
2360
2363
|
}
|
|
2361
2364
|
function hostedResolverFromEnv() {
|
|
2362
|
-
const key = readEnv(
|
|
2365
|
+
const key = readEnv(AGEMNO_KEY_ENV);
|
|
2363
2366
|
if (!key) return void 0;
|
|
2364
|
-
if (!
|
|
2367
|
+
if (!parseAgemnoKey(key)) {
|
|
2365
2368
|
try {
|
|
2366
|
-
console.warn(`[
|
|
2369
|
+
console.warn(`[agemno] warning: ${AGEMNO_KEY_ENV} isn't a secret key from the agemno dashboard (exk_...); ignoring it`);
|
|
2367
2370
|
} catch {
|
|
2368
2371
|
}
|
|
2369
2372
|
return void 0;
|
|
@@ -6474,7 +6477,7 @@ var coerce = {
|
|
|
6474
6477
|
var NEVER = INVALID;
|
|
6475
6478
|
|
|
6476
6479
|
// ../core/src/openapi.ts
|
|
6477
|
-
var
|
|
6480
|
+
var AGEMNO_EXTENSION = "x-agemno";
|
|
6478
6481
|
var METHODS = ["get", "put", "post", "delete", "patch", "head", "options"];
|
|
6479
6482
|
var MAX_DEPTH = 8;
|
|
6480
6483
|
function openApiPathToRoute(path) {
|
|
@@ -6519,7 +6522,7 @@ function toMatch(op, shared, schemas, doc) {
|
|
|
6519
6522
|
input.body = body?.required ? field : field.optional();
|
|
6520
6523
|
}
|
|
6521
6524
|
const text = [op.summary, op.description].find((t) => typeof t === "string" && t.trim().length > 0);
|
|
6522
|
-
const marker = markerFrom(op[
|
|
6525
|
+
const marker = markerFrom(op[AGEMNO_EXTENSION]);
|
|
6523
6526
|
return { ...text ? { description: text.trim() } : {}, ...marker ? { marker } : {}, input };
|
|
6524
6527
|
}
|
|
6525
6528
|
function markerFrom(ext) {
|
|
@@ -6677,7 +6680,7 @@ function installMountRecorder(routerCtor) {
|
|
|
6677
6680
|
} catch {
|
|
6678
6681
|
}
|
|
6679
6682
|
}
|
|
6680
|
-
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("
|
|
6683
|
+
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("agemno.express.recordMountOwner");
|
|
6681
6684
|
var taggedOwners = /* @__PURE__ */ new WeakMap();
|
|
6682
6685
|
function recordTaggedOwner(parent, args) {
|
|
6683
6686
|
const stack = [...args];
|
|
@@ -6782,7 +6785,7 @@ function warnMountSkipped() {
|
|
|
6782
6785
|
warnedAboutSkippedMount = true;
|
|
6783
6786
|
try {
|
|
6784
6787
|
console.warn(
|
|
6785
|
-
`[
|
|
6788
|
+
`[agemno] 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 } }).`
|
|
6786
6789
|
);
|
|
6787
6790
|
} catch {
|
|
6788
6791
|
}
|
|
@@ -6976,15 +6979,15 @@ function findExpressLogin(app, mounts) {
|
|
|
6976
6979
|
// bin/scan/env.ts
|
|
6977
6980
|
init_cjs_shims();
|
|
6978
6981
|
var SCAN_ENV = {
|
|
6979
|
-
entry: "
|
|
6982
|
+
entry: "AGEMNO_SCAN_ENTRY",
|
|
6980
6983
|
/** Set to "1" in the child so an app can skip heavy boot work if it wants. */
|
|
6981
|
-
flag: "
|
|
6984
|
+
flag: "AGEMNO_SCAN",
|
|
6982
6985
|
/** How long to wait for listen() after the last route registration (ms). */
|
|
6983
|
-
quietMs: "
|
|
6986
|
+
quietMs: "AGEMNO_SCAN_QUIET_MS"
|
|
6984
6987
|
};
|
|
6985
6988
|
|
|
6986
6989
|
// bin/scan/preload.ts
|
|
6987
|
-
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("
|
|
6990
|
+
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("agemno.express.aiReadyOptions");
|
|
6988
6991
|
var QUIET_MS = Number(process.env[SCAN_ENV.quietMs] ?? 3e3);
|
|
6989
6992
|
var NOTHING_CREATED_MS = 2e4;
|
|
6990
6993
|
var HARD_LIMIT_MS = 6e4;
|
package/dist/scan-preload.js
CHANGED
|
@@ -1832,10 +1832,10 @@ init_esm_shims();
|
|
|
1832
1832
|
|
|
1833
1833
|
// ../core/src/markers.ts
|
|
1834
1834
|
init_esm_shims();
|
|
1835
|
-
var
|
|
1835
|
+
var AGEMNO_MARKER = /* @__PURE__ */ Symbol.for("agemno.marker");
|
|
1836
1836
|
function getMarker(fn) {
|
|
1837
1837
|
if (typeof fn !== "function") return void 0;
|
|
1838
|
-
return fn[
|
|
1838
|
+
return fn[AGEMNO_MARKER];
|
|
1839
1839
|
}
|
|
1840
1840
|
|
|
1841
1841
|
// ../core/src/policy.ts
|
|
@@ -2026,16 +2026,16 @@ function resolveOAuthOptions(opts) {
|
|
|
2026
2026
|
};
|
|
2027
2027
|
}
|
|
2028
2028
|
|
|
2029
|
-
// ../core/src/
|
|
2029
|
+
// ../core/src/agemno-key.ts
|
|
2030
2030
|
init_esm_shims();
|
|
2031
2031
|
var DEFAULT_BROKER_URL = "https://broker.agemno.com";
|
|
2032
|
-
var
|
|
2033
|
-
var BROKER_URL_ENV = "
|
|
2032
|
+
var AGEMNO_KEY_ENV = "AGEMNO_KEY";
|
|
2033
|
+
var BROKER_URL_ENV = "AGEMNO_BROKER_URL";
|
|
2034
2034
|
var DEFAULT_API_URL = "https://infra.agemno.com";
|
|
2035
|
-
var API_URL_ENV = "
|
|
2036
|
-
var HOSTED_RESOLVER = /* @__PURE__ */ Symbol.for("
|
|
2035
|
+
var API_URL_ENV = "AGEMNO_API_URL";
|
|
2036
|
+
var HOSTED_RESOLVER = /* @__PURE__ */ Symbol.for("agemno.hostedResolver");
|
|
2037
2037
|
var KEY_RE = /^exk_([0-9a-f]{32})_[A-Za-z0-9_-]{20,}$/;
|
|
2038
|
-
function
|
|
2038
|
+
function parseAgemnoKey(key) {
|
|
2039
2039
|
const m = KEY_RE.exec(key.trim());
|
|
2040
2040
|
if (!m) return null;
|
|
2041
2041
|
const h = m[1];
|
|
@@ -2159,8 +2159,8 @@ function signInMethodsOf(body) {
|
|
|
2159
2159
|
}
|
|
2160
2160
|
function createHostedAuthResolver(config) {
|
|
2161
2161
|
const givenKey = config.key ?? config.licenseKey;
|
|
2162
|
-
const projectId = config.projectId ?? (config.key ?
|
|
2163
|
-
if (!givenKey) throw new Error("createHostedAuthResolver: pass your secret key as `key` (the
|
|
2162
|
+
const projectId = config.projectId ?? (config.key ? parseAgemnoKey(config.key)?.projectId : void 0);
|
|
2163
|
+
if (!givenKey) throw new Error("createHostedAuthResolver: pass your secret key as `key` (the AGEMNO_KEY value)");
|
|
2164
2164
|
const licenseKey = givenKey;
|
|
2165
2165
|
if (!projectId) {
|
|
2166
2166
|
throw new Error("createHostedAuthResolver: that key doesn't carry a project id (expected exk_...); rotate it in the dashboard, or pass projectId");
|
|
@@ -2252,7 +2252,7 @@ function createHostedAuthResolver(config) {
|
|
|
2252
2252
|
const reported = /* @__PURE__ */ new Map();
|
|
2253
2253
|
let passwordDirect;
|
|
2254
2254
|
const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
|
|
2255
|
-
const stepSealer = createSealer(licenseKey, "
|
|
2255
|
+
const stepSealer = createSealer(licenseKey, "agemno login-step v1");
|
|
2256
2256
|
let connectInfo;
|
|
2257
2257
|
let connectInflight;
|
|
2258
2258
|
const fetchConnectInfo = async () => {
|
|
@@ -2325,6 +2325,9 @@ function createHostedAuthResolver(config) {
|
|
|
2325
2325
|
countConnectOpen() {
|
|
2326
2326
|
void brokerPost("connect-open", {});
|
|
2327
2327
|
},
|
|
2328
|
+
countConnectComponentLoad() {
|
|
2329
|
+
void brokerPost("connect-component-load", {});
|
|
2330
|
+
},
|
|
2328
2331
|
reportLogin(report) {
|
|
2329
2332
|
const key = JSON.stringify(report);
|
|
2330
2333
|
const send2 = async () => {
|
|
@@ -2363,11 +2366,11 @@ function createHostedAuthResolver(config) {
|
|
|
2363
2366
|
return resolver;
|
|
2364
2367
|
}
|
|
2365
2368
|
function hostedResolverFromEnv() {
|
|
2366
|
-
const key = readEnv(
|
|
2369
|
+
const key = readEnv(AGEMNO_KEY_ENV);
|
|
2367
2370
|
if (!key) return void 0;
|
|
2368
|
-
if (!
|
|
2371
|
+
if (!parseAgemnoKey(key)) {
|
|
2369
2372
|
try {
|
|
2370
|
-
console.warn(`[
|
|
2373
|
+
console.warn(`[agemno] warning: ${AGEMNO_KEY_ENV} isn't a secret key from the agemno dashboard (exk_...); ignoring it`);
|
|
2371
2374
|
} catch {
|
|
2372
2375
|
}
|
|
2373
2376
|
return void 0;
|
|
@@ -6478,7 +6481,7 @@ var coerce = {
|
|
|
6478
6481
|
var NEVER = INVALID;
|
|
6479
6482
|
|
|
6480
6483
|
// ../core/src/openapi.ts
|
|
6481
|
-
var
|
|
6484
|
+
var AGEMNO_EXTENSION = "x-agemno";
|
|
6482
6485
|
var METHODS = ["get", "put", "post", "delete", "patch", "head", "options"];
|
|
6483
6486
|
var MAX_DEPTH = 8;
|
|
6484
6487
|
function openApiPathToRoute(path2) {
|
|
@@ -6523,7 +6526,7 @@ function toMatch(op, shared, schemas, doc) {
|
|
|
6523
6526
|
input.body = body?.required ? field : field.optional();
|
|
6524
6527
|
}
|
|
6525
6528
|
const text = [op.summary, op.description].find((t) => typeof t === "string" && t.trim().length > 0);
|
|
6526
|
-
const marker = markerFrom(op[
|
|
6529
|
+
const marker = markerFrom(op[AGEMNO_EXTENSION]);
|
|
6527
6530
|
return { ...text ? { description: text.trim() } : {}, ...marker ? { marker } : {}, input };
|
|
6528
6531
|
}
|
|
6529
6532
|
function markerFrom(ext) {
|
|
@@ -6681,7 +6684,7 @@ function installMountRecorder(routerCtor) {
|
|
|
6681
6684
|
} catch {
|
|
6682
6685
|
}
|
|
6683
6686
|
}
|
|
6684
|
-
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("
|
|
6687
|
+
var MOUNT_OWNER_TAG = /* @__PURE__ */ Symbol.for("agemno.express.recordMountOwner");
|
|
6685
6688
|
var taggedOwners = /* @__PURE__ */ new WeakMap();
|
|
6686
6689
|
function recordTaggedOwner(parent, args) {
|
|
6687
6690
|
const stack = [...args];
|
|
@@ -6786,7 +6789,7 @@ function warnMountSkipped() {
|
|
|
6786
6789
|
warnedAboutSkippedMount = true;
|
|
6787
6790
|
try {
|
|
6788
6791
|
console.warn(
|
|
6789
|
-
`[
|
|
6792
|
+
`[agemno] 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 } }).`
|
|
6790
6793
|
);
|
|
6791
6794
|
} catch {
|
|
6792
6795
|
}
|
|
@@ -6980,15 +6983,15 @@ function findExpressLogin(app, mounts) {
|
|
|
6980
6983
|
// bin/scan/env.ts
|
|
6981
6984
|
init_esm_shims();
|
|
6982
6985
|
var SCAN_ENV = {
|
|
6983
|
-
entry: "
|
|
6986
|
+
entry: "AGEMNO_SCAN_ENTRY",
|
|
6984
6987
|
/** Set to "1" in the child so an app can skip heavy boot work if it wants. */
|
|
6985
|
-
flag: "
|
|
6988
|
+
flag: "AGEMNO_SCAN",
|
|
6986
6989
|
/** How long to wait for listen() after the last route registration (ms). */
|
|
6987
|
-
quietMs: "
|
|
6990
|
+
quietMs: "AGEMNO_SCAN_QUIET_MS"
|
|
6988
6991
|
};
|
|
6989
6992
|
|
|
6990
6993
|
// bin/scan/preload.ts
|
|
6991
|
-
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("
|
|
6994
|
+
var AIREADY_OPTIONS_TAG = /* @__PURE__ */ Symbol.for("agemno.express.aiReadyOptions");
|
|
6992
6995
|
var QUIET_MS = Number(process.env[SCAN_ENV.quietMs] ?? 3e3);
|
|
6993
6996
|
var NOTHING_CREATED_MS = 2e4;
|
|
6994
6997
|
var HARD_LIMIT_MS = 6e4;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exposify/express",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"bin": {
|
|
10
|
-
"
|
|
10
|
+
"agemno-express": "./dist/cli.js"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean && tsup --entry.cli bin/
|
|
23
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean && tsup --entry.cli bin/agemno-express.ts --format esm --outDir dist --no-clean && tsup --config tsup.preload.config.ts",
|
|
24
24
|
"test": "vitest run",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
26
|
"prepack": "yarn build"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@exposify/cli": "0.0.
|
|
30
|
-
"@exposify/core": "0.3.
|
|
29
|
+
"@exposify/cli": "0.0.10",
|
|
30
|
+
"@exposify/core": "0.3.21"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"express": ">=4 <6"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"description": "Turn an Express app into an MCP server in one line: routes become AI agent tools automatically, writes are opt-in, and the caller's own credentials pass straight through.",
|
|
51
|
-
"author": "
|
|
51
|
+
"author": "agemno",
|
|
52
52
|
"keywords": [
|
|
53
53
|
"mcp",
|
|
54
54
|
"model-context-protocol",
|