@exposify/express 0.3.9 → 0.3.10
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 +7 -0
- package/dist/index.cjs +24 -7
- package/dist/index.js +26 -7
- package/dist/scan-preload.cjs +44 -14
- package/dist/scan-preload.js +44 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -173,6 +173,13 @@ signs in with your app's own login, with no code:
|
|
|
173
173
|
`login: { path, fields, body? }` names another route; `login: false` turns
|
|
174
174
|
it off. Routers introspection can't see (sub-apps) need `mounts`.
|
|
175
175
|
|
|
176
|
+
**No login found, and no sign-in method in the dashboard?** Then agents use
|
|
177
|
+
your app signed out: `/mcp` doesn't ask them to sign in, and your own checks
|
|
178
|
+
decide what they can do. The startup log says which it is
|
|
179
|
+
(`[agentready] How agents sign in: ...`), and so does the dashboard's "How
|
|
180
|
+
agents sign in" page. Adding a sign-in method there takes effect within a
|
|
181
|
+
minute.
|
|
182
|
+
|
|
176
183
|
## "Click Connect and log in" (OAuth)
|
|
177
184
|
|
|
178
185
|
By default the MCP client has to be handed a token by hand. If your app's
|
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ __export(index_exports, {
|
|
|
31
31
|
routesFromExpress: () => routesFromExpress
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
var import_node_fs = require("fs");
|
|
34
35
|
var import_node_path3 = require("path");
|
|
35
36
|
var import_core6 = require("@exposify/core");
|
|
36
37
|
var import_node2 = require("@exposify/core/node");
|
|
@@ -569,13 +570,25 @@ function aiReady(options = {}) {
|
|
|
569
570
|
const given = options.login;
|
|
570
571
|
const value = given ? { path: given.path, fields: given.fields.map((name) => ({ name, kind: (0, import_core6.fieldKind)(name) })), body: given.body ?? "json" } : findExpressLogin(app, mounts)?.login ?? null;
|
|
571
572
|
loginFound = { value };
|
|
572
|
-
if (value)
|
|
573
|
-
cfg.signedOutPaths = [value.path];
|
|
574
|
-
if (logReady) console.log(`${LOG_PREFIX} AI assistants sign in with your app's own login (POST ${value.path}) at ${MCP_PATH}/login`);
|
|
575
|
-
}
|
|
573
|
+
if (value) cfg.signedOutPaths = [value.path];
|
|
576
574
|
}
|
|
577
575
|
return loginFound.value;
|
|
578
576
|
};
|
|
577
|
+
let stacks;
|
|
578
|
+
const stacksOf = () => {
|
|
579
|
+
if (!stacks) {
|
|
580
|
+
try {
|
|
581
|
+
stacks = (0, import_core6.authStacksOf)((0, import_core6.dependencyNames)(JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path3.join)(cwd, "package.json"), "utf8")))).map((s) => s.pkg);
|
|
582
|
+
} catch {
|
|
583
|
+
stacks = [];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return stacks;
|
|
587
|
+
};
|
|
588
|
+
const report = (req, resource) => {
|
|
589
|
+
const login = loginOf(req.app);
|
|
590
|
+
return (0, import_core6.reportAppLogin)(cfg.auth, resource, login, { ...login ? { where: `POST ${login.path}` } : {}, stacks: stacksOf(), log: logReady });
|
|
591
|
+
};
|
|
579
592
|
const appLoginFor = (req, login) => ({
|
|
580
593
|
label: `POST ${login.path}`,
|
|
581
594
|
fields: login.fields,
|
|
@@ -633,7 +646,9 @@ function aiReady(options = {}) {
|
|
|
633
646
|
}
|
|
634
647
|
if (brokerGate && (path === PRM_ROOT || path === `${PRM_ROOT}${MCP_PATH}`)) {
|
|
635
648
|
const resource = resourceOf(req, path, MCP_PATH);
|
|
636
|
-
void (
|
|
649
|
+
void report(req, resource).then(
|
|
650
|
+
(verdict) => verdict === "open" ? void res.writeHead(404).end("not found") : sendWebResponse(res, brokerGate.metadataResponse(req.method, resource))
|
|
651
|
+
);
|
|
637
652
|
return;
|
|
638
653
|
}
|
|
639
654
|
if (path !== MCP_PATH && !path.startsWith(`${MCP_PATH}/`)) return next();
|
|
@@ -649,11 +664,13 @@ function aiReady(options = {}) {
|
|
|
649
664
|
console.warn("[agentready] oauth gate failed:", err);
|
|
650
665
|
if (!res.headersSent) res.writeHead(503).end("authorization check failed");
|
|
651
666
|
return null;
|
|
652
|
-
}) : brokerGate ? (
|
|
667
|
+
}) : brokerGate ? report(req, resourceOf(req, path, MCP_PATH)).then(async (verdict) => {
|
|
668
|
+
if (verdict === "open") return {};
|
|
669
|
+
const result = await brokerGate.check(headersOf(req), resourceOf(req, path, MCP_PATH));
|
|
653
670
|
if (result.ok) return {};
|
|
654
671
|
await sendWebResponse(res, result.response);
|
|
655
672
|
return null;
|
|
656
|
-
}
|
|
673
|
+
}).catch((err) => {
|
|
657
674
|
console.warn("[agentready] broker check failed:", err);
|
|
658
675
|
if (!res.headersSent) res.writeHead(503).end("authorization check failed");
|
|
659
676
|
return null;
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
|
+
import { readFileSync } from "fs";
|
|
9
10
|
import { join as join3 } from "path";
|
|
10
11
|
import {
|
|
12
|
+
authStacksOf,
|
|
11
13
|
countTools,
|
|
12
14
|
createBrokerGate,
|
|
13
15
|
createOAuthGate,
|
|
16
|
+
dependencyNames,
|
|
14
17
|
describeAuth,
|
|
15
18
|
formatFailed,
|
|
16
19
|
formatReady,
|
|
@@ -574,13 +577,25 @@ function aiReady(options = {}) {
|
|
|
574
577
|
const given = options.login;
|
|
575
578
|
const value = given ? { path: given.path, fields: given.fields.map((name) => ({ name, kind: fieldKind(name) })), body: given.body ?? "json" } : findExpressLogin(app, mounts)?.login ?? null;
|
|
576
579
|
loginFound = { value };
|
|
577
|
-
if (value)
|
|
578
|
-
cfg.signedOutPaths = [value.path];
|
|
579
|
-
if (logReady) console.log(`${LOG_PREFIX} AI assistants sign in with your app's own login (POST ${value.path}) at ${MCP_PATH}/login`);
|
|
580
|
-
}
|
|
580
|
+
if (value) cfg.signedOutPaths = [value.path];
|
|
581
581
|
}
|
|
582
582
|
return loginFound.value;
|
|
583
583
|
};
|
|
584
|
+
let stacks;
|
|
585
|
+
const stacksOf = () => {
|
|
586
|
+
if (!stacks) {
|
|
587
|
+
try {
|
|
588
|
+
stacks = authStacksOf(dependencyNames(JSON.parse(readFileSync(join3(cwd, "package.json"), "utf8")))).map((s) => s.pkg);
|
|
589
|
+
} catch {
|
|
590
|
+
stacks = [];
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return stacks;
|
|
594
|
+
};
|
|
595
|
+
const report = (req, resource) => {
|
|
596
|
+
const login = loginOf(req.app);
|
|
597
|
+
return reportAppLogin(cfg.auth, resource, login, { ...login ? { where: `POST ${login.path}` } : {}, stacks: stacksOf(), log: logReady });
|
|
598
|
+
};
|
|
584
599
|
const appLoginFor = (req, login) => ({
|
|
585
600
|
label: `POST ${login.path}`,
|
|
586
601
|
fields: login.fields,
|
|
@@ -638,7 +653,9 @@ function aiReady(options = {}) {
|
|
|
638
653
|
}
|
|
639
654
|
if (brokerGate && (path === PRM_ROOT || path === `${PRM_ROOT}${MCP_PATH}`)) {
|
|
640
655
|
const resource = resourceOf(req, path, MCP_PATH);
|
|
641
|
-
void
|
|
656
|
+
void report(req, resource).then(
|
|
657
|
+
(verdict) => verdict === "open" ? void res.writeHead(404).end("not found") : sendWebResponse(res, brokerGate.metadataResponse(req.method, resource))
|
|
658
|
+
);
|
|
642
659
|
return;
|
|
643
660
|
}
|
|
644
661
|
if (path !== MCP_PATH && !path.startsWith(`${MCP_PATH}/`)) return next();
|
|
@@ -654,11 +671,13 @@ function aiReady(options = {}) {
|
|
|
654
671
|
console.warn("[agentready] oauth gate failed:", err);
|
|
655
672
|
if (!res.headersSent) res.writeHead(503).end("authorization check failed");
|
|
656
673
|
return null;
|
|
657
|
-
}) : brokerGate ?
|
|
674
|
+
}) : brokerGate ? report(req, resourceOf(req, path, MCP_PATH)).then(async (verdict) => {
|
|
675
|
+
if (verdict === "open") return {};
|
|
676
|
+
const result = await brokerGate.check(headersOf(req), resourceOf(req, path, MCP_PATH));
|
|
658
677
|
if (result.ok) return {};
|
|
659
678
|
await sendWebResponse(res, result.response);
|
|
660
679
|
return null;
|
|
661
|
-
}
|
|
680
|
+
}).catch((err) => {
|
|
662
681
|
console.warn("[agentready] broker check failed:", err);
|
|
663
682
|
if (!res.headersSent) res.writeHead(503).end("authorization check failed");
|
|
664
683
|
return null;
|
package/dist/scan-preload.cjs
CHANGED
|
@@ -2077,9 +2077,21 @@ function requireHttpsBaseUrl(optionName, value) {
|
|
|
2077
2077
|
// ../core/src/hosted-resolver.ts
|
|
2078
2078
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
2079
2079
|
var REPORT_TIMEOUT_MS = 3e3;
|
|
2080
|
+
var REPORT_REFRESH_MS = 6e4;
|
|
2080
2081
|
var DEFAULT_MAX_CACHE_MS = 3e4;
|
|
2081
2082
|
var EXPIRY_SKEW_MS = 6e4;
|
|
2082
2083
|
var MAX_CACHE_ENTRIES = 500;
|
|
2084
|
+
function signInMethodsOf(body) {
|
|
2085
|
+
const methods = body?.signIn?.methods;
|
|
2086
|
+
if (!Array.isArray(methods)) return null;
|
|
2087
|
+
return methods.map((m) => {
|
|
2088
|
+
const { kind, label } = m && typeof m === "object" ? m : {};
|
|
2089
|
+
return {
|
|
2090
|
+
kind: kind === "app-login" || kind === "oauth" || kind === "session" ? kind : "other",
|
|
2091
|
+
label: typeof label === "string" && label.trim() ? label.trim().slice(0, 100) : "a sign-in method"
|
|
2092
|
+
};
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2083
2095
|
function createHostedAuthResolver(config) {
|
|
2084
2096
|
const givenKey = config.key ?? config.licenseKey;
|
|
2085
2097
|
const projectId = config.projectId ?? (config.key ? parseExposifyKey(config.key)?.projectId : void 0);
|
|
@@ -2173,7 +2185,7 @@ function createHostedAuthResolver(config) {
|
|
|
2173
2185
|
}
|
|
2174
2186
|
const sessionWrites = /* @__PURE__ */ new Map();
|
|
2175
2187
|
const reported = /* @__PURE__ */ new Map();
|
|
2176
|
-
const apiUrl = config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL;
|
|
2188
|
+
const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
|
|
2177
2189
|
const info = {
|
|
2178
2190
|
issuer,
|
|
2179
2191
|
resource: config.resource,
|
|
@@ -2222,21 +2234,34 @@ function createHostedAuthResolver(config) {
|
|
|
2222
2234
|
},
|
|
2223
2235
|
reportLogin(report) {
|
|
2224
2236
|
const key = JSON.stringify(report);
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
+
const send2 = async () => {
|
|
2238
|
+
const res = await fetchImpl(`${apiUrl.replace(/\/+$/, "")}/api/v1/app/login`, {
|
|
2239
|
+
method: "PUT",
|
|
2240
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${licenseKey}` },
|
|
2241
|
+
body: JSON.stringify(report),
|
|
2242
|
+
signal: AbortSignal.timeout(REPORT_TIMEOUT_MS)
|
|
2243
|
+
});
|
|
2244
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
2245
|
+
return signInMethodsOf(await res.json().catch(() => null));
|
|
2246
|
+
};
|
|
2247
|
+
const entry2 = reported.get(key);
|
|
2248
|
+
if (!entry2) {
|
|
2249
|
+
const pending = send2();
|
|
2250
|
+
reported.set(key, { answer: pending, at: Date.now() });
|
|
2237
2251
|
pending.catch(() => reported.delete(key));
|
|
2252
|
+
return pending;
|
|
2253
|
+
}
|
|
2254
|
+
if (!entry2.refreshing && Date.now() - entry2.at >= REPORT_REFRESH_MS) {
|
|
2255
|
+
entry2.refreshing = true;
|
|
2256
|
+
send2().then(
|
|
2257
|
+
(methods) => reported.set(key, { answer: Promise.resolve(methods), at: Date.now() }),
|
|
2258
|
+
() => {
|
|
2259
|
+
entry2.at = Date.now();
|
|
2260
|
+
entry2.refreshing = false;
|
|
2261
|
+
}
|
|
2262
|
+
);
|
|
2238
2263
|
}
|
|
2239
|
-
return
|
|
2264
|
+
return entry2.answer;
|
|
2240
2265
|
}
|
|
2241
2266
|
};
|
|
2242
2267
|
Object.defineProperty(resolver, HOSTED_RESOLVER, { value: info });
|
|
@@ -6483,6 +6508,11 @@ function applyOpenApi(routes, doc) {
|
|
|
6483
6508
|
|
|
6484
6509
|
// ../core/src/own-login.ts
|
|
6485
6510
|
init_cjs_shims();
|
|
6511
|
+
|
|
6512
|
+
// ../core/src/startup.ts
|
|
6513
|
+
init_cjs_shims();
|
|
6514
|
+
|
|
6515
|
+
// ../core/src/own-login.ts
|
|
6486
6516
|
var PASSWORD_NAME = /^(password|passwd|pass|pwd|pin|passcode|secret)$/i;
|
|
6487
6517
|
var PASSWORD_PART = /(password|passwd|passcode)/i;
|
|
6488
6518
|
var CONFIRM_NAME = /(confirm|repeat|again|verify|retype|new).*pass|pass.*(confirm|repeat|again|2)$/i;
|
package/dist/scan-preload.js
CHANGED
|
@@ -2081,9 +2081,21 @@ function requireHttpsBaseUrl(optionName, value) {
|
|
|
2081
2081
|
// ../core/src/hosted-resolver.ts
|
|
2082
2082
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
2083
2083
|
var REPORT_TIMEOUT_MS = 3e3;
|
|
2084
|
+
var REPORT_REFRESH_MS = 6e4;
|
|
2084
2085
|
var DEFAULT_MAX_CACHE_MS = 3e4;
|
|
2085
2086
|
var EXPIRY_SKEW_MS = 6e4;
|
|
2086
2087
|
var MAX_CACHE_ENTRIES = 500;
|
|
2088
|
+
function signInMethodsOf(body) {
|
|
2089
|
+
const methods = body?.signIn?.methods;
|
|
2090
|
+
if (!Array.isArray(methods)) return null;
|
|
2091
|
+
return methods.map((m) => {
|
|
2092
|
+
const { kind, label } = m && typeof m === "object" ? m : {};
|
|
2093
|
+
return {
|
|
2094
|
+
kind: kind === "app-login" || kind === "oauth" || kind === "session" ? kind : "other",
|
|
2095
|
+
label: typeof label === "string" && label.trim() ? label.trim().slice(0, 100) : "a sign-in method"
|
|
2096
|
+
};
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2087
2099
|
function createHostedAuthResolver(config) {
|
|
2088
2100
|
const givenKey = config.key ?? config.licenseKey;
|
|
2089
2101
|
const projectId = config.projectId ?? (config.key ? parseExposifyKey(config.key)?.projectId : void 0);
|
|
@@ -2177,7 +2189,7 @@ function createHostedAuthResolver(config) {
|
|
|
2177
2189
|
}
|
|
2178
2190
|
const sessionWrites = /* @__PURE__ */ new Map();
|
|
2179
2191
|
const reported = /* @__PURE__ */ new Map();
|
|
2180
|
-
const apiUrl = config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL;
|
|
2192
|
+
const apiUrl = requireHttpsBaseUrl("apiUrl", config.apiUrl ?? readEnv(API_URL_ENV) ?? DEFAULT_API_URL);
|
|
2181
2193
|
const info = {
|
|
2182
2194
|
issuer,
|
|
2183
2195
|
resource: config.resource,
|
|
@@ -2226,21 +2238,34 @@ function createHostedAuthResolver(config) {
|
|
|
2226
2238
|
},
|
|
2227
2239
|
reportLogin(report) {
|
|
2228
2240
|
const key = JSON.stringify(report);
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
+
const send2 = async () => {
|
|
2242
|
+
const res = await fetchImpl(`${apiUrl.replace(/\/+$/, "")}/api/v1/app/login`, {
|
|
2243
|
+
method: "PUT",
|
|
2244
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${licenseKey}` },
|
|
2245
|
+
body: JSON.stringify(report),
|
|
2246
|
+
signal: AbortSignal.timeout(REPORT_TIMEOUT_MS)
|
|
2247
|
+
});
|
|
2248
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
2249
|
+
return signInMethodsOf(await res.json().catch(() => null));
|
|
2250
|
+
};
|
|
2251
|
+
const entry2 = reported.get(key);
|
|
2252
|
+
if (!entry2) {
|
|
2253
|
+
const pending = send2();
|
|
2254
|
+
reported.set(key, { answer: pending, at: Date.now() });
|
|
2241
2255
|
pending.catch(() => reported.delete(key));
|
|
2256
|
+
return pending;
|
|
2257
|
+
}
|
|
2258
|
+
if (!entry2.refreshing && Date.now() - entry2.at >= REPORT_REFRESH_MS) {
|
|
2259
|
+
entry2.refreshing = true;
|
|
2260
|
+
send2().then(
|
|
2261
|
+
(methods) => reported.set(key, { answer: Promise.resolve(methods), at: Date.now() }),
|
|
2262
|
+
() => {
|
|
2263
|
+
entry2.at = Date.now();
|
|
2264
|
+
entry2.refreshing = false;
|
|
2265
|
+
}
|
|
2266
|
+
);
|
|
2242
2267
|
}
|
|
2243
|
-
return
|
|
2268
|
+
return entry2.answer;
|
|
2244
2269
|
}
|
|
2245
2270
|
};
|
|
2246
2271
|
Object.defineProperty(resolver, HOSTED_RESOLVER, { value: info });
|
|
@@ -6487,6 +6512,11 @@ function applyOpenApi(routes, doc) {
|
|
|
6487
6512
|
|
|
6488
6513
|
// ../core/src/own-login.ts
|
|
6489
6514
|
init_esm_shims();
|
|
6515
|
+
|
|
6516
|
+
// ../core/src/startup.ts
|
|
6517
|
+
init_esm_shims();
|
|
6518
|
+
|
|
6519
|
+
// ../core/src/own-login.ts
|
|
6490
6520
|
var PASSWORD_NAME = /^(password|passwd|pass|pwd|pin|passcode|secret)$/i;
|
|
6491
6521
|
var PASSWORD_PART = /(password|passwd|passcode)/i;
|
|
6492
6522
|
var CONFIRM_NAME = /(confirm|repeat|again|verify|retype|new).*pass|pass.*(confirm|repeat|again|2)$/i;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exposify/express",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
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.
|
|
30
|
-
"@exposify/core": "0.3.
|
|
29
|
+
"@exposify/cli": "0.0.2",
|
|
30
|
+
"@exposify/core": "0.3.14"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"express": ">=4 <6"
|