@modelcontextprotocol/inspector 2.13.0 → 2.14.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.
- package/README.md +1 -1
- package/clients/cli/build/index.js +1013 -80
- package/clients/mcpdo/build/mcp-bin.js +50 -22
- package/clients/mcpdo/build/mcpdod.js +1 -1
- package/clients/mcpdo/build/{server-BHuKGkMj.js → server-Dv339Ejx.js} +886 -48
- package/clients/tui/build/index.js +781 -46
- package/clients/web/build/index.js +223 -124
- package/clients/web/dist/assets/fredoka-normal-hebrew-CjvktpUN.woff2 +0 -0
- package/clients/web/dist/assets/fredoka-normal-latin-DOQG_Jwn.woff2 +0 -0
- package/clients/web/dist/assets/fredoka-normal-latin-ext-tYNUQHO6.woff2 +0 -0
- package/clients/web/dist/assets/index-BCud2uMC.js +1594 -0
- package/clients/web/dist/assets/{index-dqp8Sd8W.css → index-Dval2a7I.css} +1 -1
- package/clients/web/dist/assets/{prism-light-BIhJtJkv.js → prism-light-CoVRM9R2.js} +1 -1
- package/clients/web/dist/assets/roboto-mono-italic-cyrillic-C1E63de8.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-italic-cyrillic-ext-DR9ovEqT.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-italic-greek-Cs2u2nVh.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-italic-latin-D_P-kX1t.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-italic-latin-ext-CErTcBzw.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-italic-vietnamese-D4UvzJMM.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-cyrillic-DfzWIUPd.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-cyrillic-ext-D8QBEkUe.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-greek-DlC4X1lQ.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-latin-BAKpfWyj.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-latin-ext-CSSVNFqV.woff2 +0 -0
- package/clients/web/dist/assets/roboto-mono-normal-vietnamese-DFkHeOHB.woff2 +0 -0
- package/clients/web/dist/fonts/Fredoka-OFL.txt +93 -0
- package/clients/web/dist/fonts/RobotoMono-OFL.txt +93 -0
- package/clients/web/dist/index.html +2 -5
- package/clients/web/static/sandbox_proxy.html +177 -6
- package/package.json +5 -4
- package/skills/mcpdo/SKILL.md +14 -0
- package/clients/web/dist/assets/index-BqEbhCDX.js +0 -1587
|
@@ -18,7 +18,7 @@ import nodeFs, { existsSync, readFileSync, rmdirSync, statSync } from "node:fs";
|
|
|
18
18
|
import properLockfile from "proper-lockfile";
|
|
19
19
|
import * as crypto$1 from "node:crypto";
|
|
20
20
|
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
21
|
-
import { homedir } from "node:os";
|
|
21
|
+
import { homedir, tmpdir } from "node:os";
|
|
22
22
|
import { Hono } from "hono";
|
|
23
23
|
import { streamSSE } from "hono/streaming";
|
|
24
24
|
import { bodyLimit } from "hono/body-limit";
|
|
@@ -32,7 +32,10 @@ import { bindTaskReceiver } from "@modelcontextprotocol/ext-tasks/receiver";
|
|
|
32
32
|
import { runtimeCodecFromStandardSchema, taskId, toJsonValue } from "@modelcontextprotocol/ext-tasks/core";
|
|
33
33
|
import { TASKS_EXTENSION_ID_V2 as TASKS_EXTENSION_KEY } from "@modelcontextprotocol/ext-tasks/core/v2";
|
|
34
34
|
import { RESOURCE_MIME_TYPE, getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
35
|
-
import
|
|
35
|
+
import Ajv from "ajv";
|
|
36
|
+
import { Ajv2019 } from "ajv/dist/2019.js";
|
|
37
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
38
|
+
import { addFormats } from "@modelcontextprotocol/client/validators/ajv";
|
|
36
39
|
import { createServer } from "node:http";
|
|
37
40
|
import open from "open";
|
|
38
41
|
import { createInterface } from "node:readline/promises";
|
|
@@ -5114,7 +5117,8 @@ const EXIT_CODES = {
|
|
|
5114
5117
|
SKILL_INCOMPLETE: 8,
|
|
5115
5118
|
SKILL_UNVERIFIABLE: 9,
|
|
5116
5119
|
LOAD_THRESHOLD_EXCEEDED: 10,
|
|
5117
|
-
RATE_LIMITED: 11
|
|
5120
|
+
RATE_LIMITED: 11,
|
|
5121
|
+
INTERRUPTED: 130
|
|
5118
5122
|
};
|
|
5119
5123
|
function rateLimitFieldsOf(error) {
|
|
5120
5124
|
const data = error?.data;
|
|
@@ -7048,26 +7052,37 @@ function expectedSecretFields(stored) {
|
|
|
7048
7052
|
if (isStdioStored(stored) && stored.env) for (const k of Object.keys(stored.env)) fields.push(envSecretField(k));
|
|
7049
7053
|
return fields;
|
|
7050
7054
|
}
|
|
7051
|
-
const
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
"@modelcontextprotocol/server-filesystem",
|
|
7058
|
-
"/tmp"
|
|
7059
|
-
]
|
|
7060
|
-
},
|
|
7061
|
-
"everything-server-default": {
|
|
7062
|
-
type: "stdio",
|
|
7063
|
-
command: "npx",
|
|
7064
|
-
args: ["-y", "@modelcontextprotocol/server-everything"]
|
|
7065
|
-
},
|
|
7066
|
-
"example-server-default": {
|
|
7067
|
-
type: "streamable-http",
|
|
7068
|
-
url: "https://example-server.modelcontextprotocol.io/mcp"
|
|
7055
|
+
const EXAMPLE_SERVER_URL = "https://example-server.modelcontextprotocol.io/mcp";
|
|
7056
|
+
function adHocServerName(config) {
|
|
7057
|
+
if (config.type === "sse" || config.type === "streamable-http") try {
|
|
7058
|
+
return new URL(config.url).host || "server";
|
|
7059
|
+
} catch {
|
|
7060
|
+
return "server";
|
|
7069
7061
|
}
|
|
7070
|
-
|
|
7062
|
+
return config.command?.split(/[\\/]/).pop() || "server";
|
|
7063
|
+
}
|
|
7064
|
+
function defaultSeedConfig(tempDir) {
|
|
7065
|
+
return { mcpServers: {
|
|
7066
|
+
"filesystem-server-default": {
|
|
7067
|
+
type: "stdio",
|
|
7068
|
+
command: "npx",
|
|
7069
|
+
args: [
|
|
7070
|
+
"-y",
|
|
7071
|
+
"@modelcontextprotocol/server-filesystem",
|
|
7072
|
+
tempDir
|
|
7073
|
+
]
|
|
7074
|
+
},
|
|
7075
|
+
"everything-server-default": {
|
|
7076
|
+
type: "stdio",
|
|
7077
|
+
command: "npx",
|
|
7078
|
+
args: ["-y", "@modelcontextprotocol/server-everything"]
|
|
7079
|
+
},
|
|
7080
|
+
"example-server-default": {
|
|
7081
|
+
type: "streamable-http",
|
|
7082
|
+
url: EXAMPLE_SERVER_URL
|
|
7083
|
+
}
|
|
7084
|
+
} };
|
|
7085
|
+
}
|
|
7071
7086
|
//#endregion
|
|
7072
7087
|
//#region ../../core/mcp/import/clientConfig.ts
|
|
7073
7088
|
function parseJsonObject(raw) {
|
|
@@ -7202,9 +7217,13 @@ function waitForProgressToken(waits, token) {
|
|
|
7202
7217
|
//#endregion
|
|
7203
7218
|
//#region ../../core/mcp/remote/node/remote-session.ts
|
|
7204
7219
|
var RemoteSession = class RemoteSession {
|
|
7220
|
+
static DEFAULT_DETACH_GRACE_MS = 6e4;
|
|
7221
|
+
static MAX_QUEUED_EVENTS = 1e3;
|
|
7205
7222
|
sessionId;
|
|
7206
7223
|
transport;
|
|
7207
7224
|
eventQueue = [];
|
|
7225
|
+
droppedEventCount = 0;
|
|
7226
|
+
detachTimer;
|
|
7208
7227
|
eventConsumer = null;
|
|
7209
7228
|
transportDead = false;
|
|
7210
7229
|
transportError = null;
|
|
@@ -7235,13 +7254,47 @@ var RemoteSession = class RemoteSession {
|
|
|
7235
7254
|
this.transport.setProtocolVersion?.(version);
|
|
7236
7255
|
}
|
|
7237
7256
|
setEventConsumer(consumer) {
|
|
7257
|
+
this.cancelDetachGrace();
|
|
7238
7258
|
this.eventConsumer = consumer;
|
|
7259
|
+
if (this.droppedEventCount > 0) {
|
|
7260
|
+
const dropped = this.droppedEventCount;
|
|
7261
|
+
this.droppedEventCount = 0;
|
|
7262
|
+
consumer({
|
|
7263
|
+
type: "stdio_log",
|
|
7264
|
+
data: {
|
|
7265
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7266
|
+
message: `[inspector] ${dropped} event(s) were dropped while no client was attached (the queue holds at most ${RemoteSession.MAX_QUEUED_EVENTS})`
|
|
7267
|
+
}
|
|
7268
|
+
});
|
|
7269
|
+
}
|
|
7239
7270
|
while (this.eventQueue.length > 0) consumer(this.eventQueue.shift());
|
|
7240
7271
|
}
|
|
7241
7272
|
clearEventConsumer() {
|
|
7242
7273
|
this.eventConsumer = null;
|
|
7243
7274
|
return this.transportDead;
|
|
7244
7275
|
}
|
|
7276
|
+
detachEventConsumer(consumer) {
|
|
7277
|
+
if (this.eventConsumer !== consumer) return false;
|
|
7278
|
+
this.eventConsumer = null;
|
|
7279
|
+
return true;
|
|
7280
|
+
}
|
|
7281
|
+
armDetachGrace(graceMs, onExpire) {
|
|
7282
|
+
this.cancelDetachGrace();
|
|
7283
|
+
this.detachTimer = setTimeout(() => {
|
|
7284
|
+
this.detachTimer = void 0;
|
|
7285
|
+
onExpire();
|
|
7286
|
+
}, graceMs);
|
|
7287
|
+
this.detachTimer.unref();
|
|
7288
|
+
}
|
|
7289
|
+
cancelDetachGrace() {
|
|
7290
|
+
if (this.detachTimer !== void 0) {
|
|
7291
|
+
clearTimeout(this.detachTimer);
|
|
7292
|
+
this.detachTimer = void 0;
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
hasPendingDetachGrace() {
|
|
7296
|
+
return this.detachTimer !== void 0;
|
|
7297
|
+
}
|
|
7245
7298
|
markTransportDead(error) {
|
|
7246
7299
|
this.transportDead = true;
|
|
7247
7300
|
this.transportError = error;
|
|
@@ -7352,7 +7405,13 @@ var RemoteSession = class RemoteSession {
|
|
|
7352
7405
|
}
|
|
7353
7406
|
pushEvent(event) {
|
|
7354
7407
|
if (this.eventConsumer) this.eventConsumer(event);
|
|
7355
|
-
else
|
|
7408
|
+
else {
|
|
7409
|
+
if (this.eventQueue.length >= RemoteSession.MAX_QUEUED_EVENTS) {
|
|
7410
|
+
this.eventQueue.shift();
|
|
7411
|
+
this.droppedEventCount++;
|
|
7412
|
+
}
|
|
7413
|
+
this.eventQueue.push(event);
|
|
7414
|
+
}
|
|
7356
7415
|
}
|
|
7357
7416
|
onMessage(message) {
|
|
7358
7417
|
if ("id" in message && message.id !== null && message.id !== void 0 && ("result" in message || "error" in message)) this.settleRequestWait(message.id);
|
|
@@ -7832,6 +7891,14 @@ function createRemoteApp(options) {
|
|
|
7832
7891
|
const authToken = dangerouslyOmitAuth ? "" : options.authToken || process.env[API_SERVER_ENV_VARS.AUTH_TOKEN] || randomBytes(32).toString("hex");
|
|
7833
7892
|
const app = new Hono();
|
|
7834
7893
|
const sessions = /* @__PURE__ */ new Map();
|
|
7894
|
+
const sessionDetachGraceMs = options.sessionDetachGraceMs ?? RemoteSession.DEFAULT_DETACH_GRACE_MS;
|
|
7895
|
+
const armSessionReap = (session) => {
|
|
7896
|
+
session.armDetachGrace(sessionDetachGraceMs, () => {
|
|
7897
|
+
sessions.delete(session.sessionId);
|
|
7898
|
+
session.rejectActiveRequestWaits(/* @__PURE__ */ new Error("Session closed: no client attached"));
|
|
7899
|
+
session.transport.close().catch(() => {});
|
|
7900
|
+
});
|
|
7901
|
+
};
|
|
7835
7902
|
const { logger: fileLogger, allowedOrigins } = options;
|
|
7836
7903
|
const storageDir = options.storageDir ?? getDefaultStorageDir();
|
|
7837
7904
|
const mcpConfigPath = options.mcpConfigPath ?? getDefaultMcpConfigPath();
|
|
@@ -8018,6 +8085,7 @@ function createRemoteApp(options) {
|
|
|
8018
8085
|
return c.json({ error: `Failed to start transport: ${msg}` }, is401 ? 401 : 500);
|
|
8019
8086
|
}
|
|
8020
8087
|
sessions.set(sessionId, session);
|
|
8088
|
+
armSessionReap(session);
|
|
8021
8089
|
return c.json({ sessionId });
|
|
8022
8090
|
});
|
|
8023
8091
|
app.post("/api/mcp/send", async (c) => {
|
|
@@ -8108,13 +8176,14 @@ function createRemoteApp(options) {
|
|
|
8108
8176
|
const session = sessions.get(sessionId);
|
|
8109
8177
|
if (!session) return c.json({ error: "Session not found" }, 404);
|
|
8110
8178
|
return streamSSE(c, async (stream) => {
|
|
8111
|
-
|
|
8179
|
+
const consumer = (event) => {
|
|
8112
8180
|
const data = JSON.stringify(event);
|
|
8113
8181
|
stream.writeSSE({
|
|
8114
8182
|
event: event.type,
|
|
8115
8183
|
data
|
|
8116
8184
|
});
|
|
8117
|
-
}
|
|
8185
|
+
};
|
|
8186
|
+
session.setEventConsumer(consumer);
|
|
8118
8187
|
if (session.isTransportDead()) {
|
|
8119
8188
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
8120
8189
|
session.clearEventConsumer();
|
|
@@ -8122,7 +8191,12 @@ function createRemoteApp(options) {
|
|
|
8122
8191
|
return;
|
|
8123
8192
|
}
|
|
8124
8193
|
await primeAndHoldSseStream(stream, () => {
|
|
8125
|
-
if (session.
|
|
8194
|
+
if (!session.detachEventConsumer(consumer)) return;
|
|
8195
|
+
if (session.isTransportDead()) {
|
|
8196
|
+
sessions.delete(sessionId);
|
|
8197
|
+
return;
|
|
8198
|
+
}
|
|
8199
|
+
armSessionReap(session);
|
|
8126
8200
|
});
|
|
8127
8201
|
});
|
|
8128
8202
|
});
|
|
@@ -8137,6 +8211,7 @@ function createRemoteApp(options) {
|
|
|
8137
8211
|
if (!sessionId) return c.json({ error: "Missing sessionId" }, 400);
|
|
8138
8212
|
const session = sessions.get(sessionId);
|
|
8139
8213
|
if (session) {
|
|
8214
|
+
session.cancelDetachGrace();
|
|
8140
8215
|
session.clearEventConsumer();
|
|
8141
8216
|
await session.transport.close();
|
|
8142
8217
|
sessions.delete(sessionId);
|
|
@@ -8800,8 +8875,9 @@ function createRemoteApp(options) {
|
|
|
8800
8875
|
const settled = await withWriteLock(async () => {
|
|
8801
8876
|
const rawInside = await readStoreFile(mcpConfigPath);
|
|
8802
8877
|
if (rawInside === null) {
|
|
8803
|
-
|
|
8804
|
-
|
|
8878
|
+
const seed = defaultSeedConfig(tmpdir());
|
|
8879
|
+
await writeMcpAndTrackMtime(serializeStore(seed));
|
|
8880
|
+
return seed;
|
|
8805
8881
|
}
|
|
8806
8882
|
const parsedInside = parseStore(rawInside);
|
|
8807
8883
|
const inside = { mcpServers: normalizeMcpServers(parsedInside?.mcpServers) };
|
|
@@ -9356,8 +9432,9 @@ async function loadServerEntries(serverOptions) {
|
|
|
9356
9432
|
}
|
|
9357
9433
|
const configs = resolveServerConfigs(serverOptions, "multi");
|
|
9358
9434
|
if (configs.length === 0) throw new Error("At least one server is required. Use --catalog/--config <path> or an ad-hoc target (command/URL).");
|
|
9359
|
-
|
|
9360
|
-
|
|
9435
|
+
const config = configs[0];
|
|
9436
|
+
return { [adHocServerName(config)]: {
|
|
9437
|
+
config,
|
|
9361
9438
|
settings: mergeSettings(void 0, serverOptions)
|
|
9362
9439
|
} };
|
|
9363
9440
|
}
|
|
@@ -11177,6 +11254,627 @@ function validateToolOutput(provider, tool, result) {
|
|
|
11177
11254
|
return;
|
|
11178
11255
|
}
|
|
11179
11256
|
}
|
|
11257
|
+
//#endregion
|
|
11258
|
+
//#region ../../core/json/linearRegExp.ts
|
|
11259
|
+
var CharMatcher = class {
|
|
11260
|
+
re;
|
|
11261
|
+
ascii = /* @__PURE__ */ new Uint8Array(128);
|
|
11262
|
+
constructor(source, unicode) {
|
|
11263
|
+
this.re = new RegExp(`^(?:${source})$`, unicode ? "u" : "");
|
|
11264
|
+
}
|
|
11265
|
+
matches(cp, unicode) {
|
|
11266
|
+
if (cp < 128) {
|
|
11267
|
+
const memo = this.ascii[cp];
|
|
11268
|
+
if (memo !== 0) return memo === 1;
|
|
11269
|
+
const result = this.re.test(String.fromCharCode(cp));
|
|
11270
|
+
this.ascii[cp] = result ? 1 : 2;
|
|
11271
|
+
return result;
|
|
11272
|
+
}
|
|
11273
|
+
return this.re.test(unicode ? String.fromCodePoint(cp) : String.fromCharCode(cp));
|
|
11274
|
+
}
|
|
11275
|
+
};
|
|
11276
|
+
var Unsupported = class extends Error {};
|
|
11277
|
+
const MAX_GROUP_DEPTH = 64;
|
|
11278
|
+
const MAX_PROGRAM_SIZE = 1e4;
|
|
11279
|
+
const MAX_COMPILE_STEPS = 10 * MAX_PROGRAM_SIZE;
|
|
11280
|
+
const MAX_PATTERN_LENGTH = 8192;
|
|
11281
|
+
const COMPILE_BUDGET_EXHAUSTED = "the patterns together exceed the compilation budget";
|
|
11282
|
+
function isCompileBudgetExhausted(budget) {
|
|
11283
|
+
return budget.steps < 0 || budget.size < 0;
|
|
11284
|
+
}
|
|
11285
|
+
const QUANTIFIER_BRACES = /\{(\d+)(?:(,)(\d*))?\}/y;
|
|
11286
|
+
const HEX4 = /^[0-9a-fA-F]{4}$/;
|
|
11287
|
+
const HEX2 = /^[0-9a-fA-F]{2}$/;
|
|
11288
|
+
var Parser = class {
|
|
11289
|
+
pos = 0;
|
|
11290
|
+
src;
|
|
11291
|
+
unicode;
|
|
11292
|
+
constructor(src, unicode) {
|
|
11293
|
+
this.src = src;
|
|
11294
|
+
this.unicode = unicode;
|
|
11295
|
+
}
|
|
11296
|
+
parse() {
|
|
11297
|
+
const node = this.parseAlternation(0);
|
|
11298
|
+
/* v8 ignore next -- the native RegExp constructor rejects an unbalanced `)` first */
|
|
11299
|
+
if (this.pos !== this.src.length) throw new Unsupported("unbalanced group");
|
|
11300
|
+
return node;
|
|
11301
|
+
}
|
|
11302
|
+
parseAlternation(depth) {
|
|
11303
|
+
const options = [this.parseSequence(depth)];
|
|
11304
|
+
while (this.src[this.pos] === "|") {
|
|
11305
|
+
this.pos++;
|
|
11306
|
+
options.push(this.parseSequence(depth));
|
|
11307
|
+
}
|
|
11308
|
+
return options.length === 1 ? options[0] : {
|
|
11309
|
+
kind: "alt",
|
|
11310
|
+
options
|
|
11311
|
+
};
|
|
11312
|
+
}
|
|
11313
|
+
parseSequence(depth) {
|
|
11314
|
+
const items = [];
|
|
11315
|
+
while (this.pos < this.src.length && this.src[this.pos] !== "|" && this.src[this.pos] !== ")") items.push(this.parseTerm(depth));
|
|
11316
|
+
return items.length === 1 ? items[0] : {
|
|
11317
|
+
kind: "seq",
|
|
11318
|
+
items
|
|
11319
|
+
};
|
|
11320
|
+
}
|
|
11321
|
+
parseTerm(depth) {
|
|
11322
|
+
const atom = this.parseAtom(depth);
|
|
11323
|
+
const quantifier = this.readQuantifier();
|
|
11324
|
+
if (!quantifier) return atom;
|
|
11325
|
+
if (this.src[this.pos] === "?") this.pos++;
|
|
11326
|
+
return {
|
|
11327
|
+
kind: "repeat",
|
|
11328
|
+
body: atom,
|
|
11329
|
+
...quantifier
|
|
11330
|
+
};
|
|
11331
|
+
}
|
|
11332
|
+
readQuantifier() {
|
|
11333
|
+
const c = this.src[this.pos];
|
|
11334
|
+
if (c === "*") {
|
|
11335
|
+
this.pos++;
|
|
11336
|
+
return {
|
|
11337
|
+
min: 0,
|
|
11338
|
+
max: Infinity
|
|
11339
|
+
};
|
|
11340
|
+
}
|
|
11341
|
+
if (c === "+") {
|
|
11342
|
+
this.pos++;
|
|
11343
|
+
return {
|
|
11344
|
+
min: 1,
|
|
11345
|
+
max: Infinity
|
|
11346
|
+
};
|
|
11347
|
+
}
|
|
11348
|
+
if (c === "?") {
|
|
11349
|
+
this.pos++;
|
|
11350
|
+
return {
|
|
11351
|
+
min: 0,
|
|
11352
|
+
max: 1
|
|
11353
|
+
};
|
|
11354
|
+
}
|
|
11355
|
+
if (c === "{") {
|
|
11356
|
+
QUANTIFIER_BRACES.lastIndex = this.pos;
|
|
11357
|
+
const m = QUANTIFIER_BRACES.exec(this.src);
|
|
11358
|
+
if (!m) return void 0;
|
|
11359
|
+
this.pos += m[0].length;
|
|
11360
|
+
const min = Number(m[1]);
|
|
11361
|
+
return {
|
|
11362
|
+
min,
|
|
11363
|
+
max: m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity
|
|
11364
|
+
};
|
|
11365
|
+
}
|
|
11366
|
+
}
|
|
11367
|
+
parseAtom(depth) {
|
|
11368
|
+
switch (this.src[this.pos]) {
|
|
11369
|
+
case "(": return this.parseGroup(depth);
|
|
11370
|
+
case "[": return this.parseClass();
|
|
11371
|
+
case "\\": return this.parseEscape();
|
|
11372
|
+
case ".":
|
|
11373
|
+
this.pos++;
|
|
11374
|
+
return {
|
|
11375
|
+
kind: "char",
|
|
11376
|
+
source: "."
|
|
11377
|
+
};
|
|
11378
|
+
case "^":
|
|
11379
|
+
this.pos++;
|
|
11380
|
+
return {
|
|
11381
|
+
kind: "assert",
|
|
11382
|
+
assertion: "start"
|
|
11383
|
+
};
|
|
11384
|
+
case "$":
|
|
11385
|
+
this.pos++;
|
|
11386
|
+
return {
|
|
11387
|
+
kind: "assert",
|
|
11388
|
+
assertion: "end"
|
|
11389
|
+
};
|
|
11390
|
+
case "*":
|
|
11391
|
+
case "+":
|
|
11392
|
+
case "?":
|
|
11393
|
+
/* v8 ignore next -- "nothing to repeat" is a native syntax error */
|
|
11394
|
+
throw new Unsupported("nothing to repeat");
|
|
11395
|
+
case "{":
|
|
11396
|
+
QUANTIFIER_BRACES.lastIndex = this.pos;
|
|
11397
|
+
if (QUANTIFIER_BRACES.test(this.src))
|
|
11398
|
+
/* v8 ignore next -- rejected by the native RegExp constructor first */
|
|
11399
|
+
throw new Unsupported("nothing to repeat");
|
|
11400
|
+
this.pos++;
|
|
11401
|
+
return {
|
|
11402
|
+
kind: "char",
|
|
11403
|
+
source: "\\{"
|
|
11404
|
+
};
|
|
11405
|
+
default: {
|
|
11406
|
+
const width = this.codePointWidth(this.pos);
|
|
11407
|
+
const source = this.src.slice(this.pos, this.pos + width);
|
|
11408
|
+
this.pos += width;
|
|
11409
|
+
return {
|
|
11410
|
+
kind: "char",
|
|
11411
|
+
source
|
|
11412
|
+
};
|
|
11413
|
+
}
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11416
|
+
codePointWidth(at) {
|
|
11417
|
+
if (!this.unicode) return 1;
|
|
11418
|
+
return this.src.codePointAt(at) > 65535 ? 2 : 1;
|
|
11419
|
+
}
|
|
11420
|
+
parseGroup(depth) {
|
|
11421
|
+
if (depth >= MAX_GROUP_DEPTH) throw new Unsupported("groups nested too deeply");
|
|
11422
|
+
this.pos++;
|
|
11423
|
+
if (this.src.startsWith("?:", this.pos)) this.pos += 2;
|
|
11424
|
+
else if (this.src.startsWith("?<", this.pos) && this.src[this.pos + 2] !== "=" && this.src[this.pos + 2] !== "!") {
|
|
11425
|
+
const close = this.src.indexOf(">", this.pos);
|
|
11426
|
+
/* v8 ignore next -- an unterminated group name is a native syntax error */
|
|
11427
|
+
if (close < 0) throw new Unsupported("unterminated group name");
|
|
11428
|
+
this.pos = close + 1;
|
|
11429
|
+
} else if (this.src[this.pos] === "?") throw new Unsupported("lookaround or group modifier");
|
|
11430
|
+
const body = this.parseAlternation(depth + 1);
|
|
11431
|
+
/* v8 ignore next -- an unterminated group is a native syntax error */
|
|
11432
|
+
if (this.src[this.pos] !== ")") throw new Unsupported("unterminated group");
|
|
11433
|
+
this.pos++;
|
|
11434
|
+
return body;
|
|
11435
|
+
}
|
|
11436
|
+
parseClass() {
|
|
11437
|
+
const start = this.pos;
|
|
11438
|
+
this.pos++;
|
|
11439
|
+
while (this.pos < this.src.length && this.src[this.pos] !== "]") this.pos += this.src[this.pos] === "\\" ? 2 : 1;
|
|
11440
|
+
/* v8 ignore next -- an unterminated class is a native syntax error */
|
|
11441
|
+
if (this.pos >= this.src.length) throw new Unsupported("unterminated class");
|
|
11442
|
+
this.pos++;
|
|
11443
|
+
return {
|
|
11444
|
+
kind: "char",
|
|
11445
|
+
source: this.src.slice(start, this.pos)
|
|
11446
|
+
};
|
|
11447
|
+
}
|
|
11448
|
+
parseEscape() {
|
|
11449
|
+
const next = this.src[this.pos + 1];
|
|
11450
|
+
/* v8 ignore next -- a trailing backslash is a native syntax error */
|
|
11451
|
+
if (next === void 0) throw new Unsupported("trailing backslash");
|
|
11452
|
+
switch (next) {
|
|
11453
|
+
case "b":
|
|
11454
|
+
this.pos += 2;
|
|
11455
|
+
return {
|
|
11456
|
+
kind: "assert",
|
|
11457
|
+
assertion: "wordBoundary"
|
|
11458
|
+
};
|
|
11459
|
+
case "B":
|
|
11460
|
+
this.pos += 2;
|
|
11461
|
+
return {
|
|
11462
|
+
kind: "assert",
|
|
11463
|
+
assertion: "notWordBoundary"
|
|
11464
|
+
};
|
|
11465
|
+
case "1":
|
|
11466
|
+
case "2":
|
|
11467
|
+
case "3":
|
|
11468
|
+
case "4":
|
|
11469
|
+
case "5":
|
|
11470
|
+
case "6":
|
|
11471
|
+
case "7":
|
|
11472
|
+
case "8":
|
|
11473
|
+
case "9":
|
|
11474
|
+
case "k": throw new Unsupported("backreference");
|
|
11475
|
+
case "0":
|
|
11476
|
+
if (/[0-9]/.test(this.src[this.pos + 2] ?? "")) throw new Unsupported("octal escape");
|
|
11477
|
+
return this.takeChar(2);
|
|
11478
|
+
case "c":
|
|
11479
|
+
if (!/[a-zA-Z]/.test(this.src[this.pos + 2] ?? "")) throw new Unsupported("incomplete control escape");
|
|
11480
|
+
return this.takeChar(3);
|
|
11481
|
+
case "p":
|
|
11482
|
+
case "P":
|
|
11483
|
+
if (this.unicode && this.src[this.pos + 2] === "{") return this.takeThrough("}");
|
|
11484
|
+
return this.takeChar(2);
|
|
11485
|
+
case "x": return this.takeChar(HEX2.test(this.src.slice(this.pos + 2, this.pos + 4)) ? 4 : 2);
|
|
11486
|
+
case "u": return this.parseUnicodeEscape();
|
|
11487
|
+
default: return this.takeChar(1 + this.codePointWidth(this.pos + 1));
|
|
11488
|
+
}
|
|
11489
|
+
}
|
|
11490
|
+
parseUnicodeEscape() {
|
|
11491
|
+
if (this.unicode && this.src[this.pos + 2] === "{") return this.takeThrough("}");
|
|
11492
|
+
const hex = this.src.slice(this.pos + 2, this.pos + 6);
|
|
11493
|
+
if (!HEX4.test(hex)) return this.takeChar(2);
|
|
11494
|
+
const unit = parseInt(hex, 16);
|
|
11495
|
+
if (this.unicode && unit >= 55296 && unit <= 56319 && this.src.startsWith("\\u", this.pos + 6)) {
|
|
11496
|
+
const low = this.src.slice(this.pos + 8, this.pos + 12);
|
|
11497
|
+
if (HEX4.test(low)) {
|
|
11498
|
+
const lowUnit = parseInt(low, 16);
|
|
11499
|
+
if (lowUnit >= 56320 && lowUnit <= 57343) return this.takeChar(12);
|
|
11500
|
+
}
|
|
11501
|
+
}
|
|
11502
|
+
return this.takeChar(6);
|
|
11503
|
+
}
|
|
11504
|
+
takeChar(length) {
|
|
11505
|
+
const source = this.src.slice(this.pos, this.pos + length);
|
|
11506
|
+
this.pos += length;
|
|
11507
|
+
return {
|
|
11508
|
+
kind: "char",
|
|
11509
|
+
source
|
|
11510
|
+
};
|
|
11511
|
+
}
|
|
11512
|
+
takeThrough(terminator) {
|
|
11513
|
+
const end = this.src.indexOf(terminator, this.pos);
|
|
11514
|
+
/* v8 ignore next -- an unterminated `\p{` / `\u{` is a native syntax error */
|
|
11515
|
+
if (end < 0) throw new Unsupported("unterminated escape");
|
|
11516
|
+
return this.takeChar(end + 1 - this.pos);
|
|
11517
|
+
}
|
|
11518
|
+
};
|
|
11519
|
+
var Compiler = class {
|
|
11520
|
+
prog = [];
|
|
11521
|
+
matchers = /* @__PURE__ */ new Map();
|
|
11522
|
+
unicode;
|
|
11523
|
+
budget;
|
|
11524
|
+
steps = 0;
|
|
11525
|
+
constructor(unicode, budget) {
|
|
11526
|
+
this.unicode = unicode;
|
|
11527
|
+
this.budget = budget;
|
|
11528
|
+
}
|
|
11529
|
+
emit(inst) {
|
|
11530
|
+
if (this.prog.length >= 1e4) throw new Unsupported("pattern too complex");
|
|
11531
|
+
if (this.budget && --this.budget.size < 0) throw new Unsupported(COMPILE_BUDGET_EXHAUSTED);
|
|
11532
|
+
this.prog.push(inst);
|
|
11533
|
+
return this.prog.length - 1;
|
|
11534
|
+
}
|
|
11535
|
+
matcher(source) {
|
|
11536
|
+
let matcher = this.matchers.get(source);
|
|
11537
|
+
if (!matcher) {
|
|
11538
|
+
try {
|
|
11539
|
+
matcher = new CharMatcher(source, this.unicode);
|
|
11540
|
+
} catch {
|
|
11541
|
+
/* v8 ignore start -- defensive: each atom is a slice the native constructor already accepted in context */
|
|
11542
|
+
throw new Unsupported("unrecognized character syntax");
|
|
11543
|
+
}
|
|
11544
|
+
this.matchers.set(source, matcher);
|
|
11545
|
+
}
|
|
11546
|
+
return matcher;
|
|
11547
|
+
}
|
|
11548
|
+
compile(node) {
|
|
11549
|
+
if (++this.steps > 1e5) throw new Unsupported("pattern too complex");
|
|
11550
|
+
if (this.budget && --this.budget.steps < 0) throw new Unsupported(COMPILE_BUDGET_EXHAUSTED);
|
|
11551
|
+
switch (node.kind) {
|
|
11552
|
+
case "char":
|
|
11553
|
+
this.emit({
|
|
11554
|
+
op: "char",
|
|
11555
|
+
matcher: this.matcher(node.source)
|
|
11556
|
+
});
|
|
11557
|
+
return;
|
|
11558
|
+
case "assert":
|
|
11559
|
+
this.emit({
|
|
11560
|
+
op: "assert",
|
|
11561
|
+
assertion: node.assertion
|
|
11562
|
+
});
|
|
11563
|
+
return;
|
|
11564
|
+
case "seq":
|
|
11565
|
+
for (const item of node.items) this.compile(item);
|
|
11566
|
+
return;
|
|
11567
|
+
case "alt":
|
|
11568
|
+
this.compileAlternation(node.options);
|
|
11569
|
+
return;
|
|
11570
|
+
case "repeat":
|
|
11571
|
+
this.compileRepeat(node.body, node.min, node.max);
|
|
11572
|
+
return;
|
|
11573
|
+
}
|
|
11574
|
+
}
|
|
11575
|
+
compileAlternation(options) {
|
|
11576
|
+
const jumps = [];
|
|
11577
|
+
for (let i = 0; i < options.length - 1; i++) {
|
|
11578
|
+
const split = this.emit({
|
|
11579
|
+
op: "split",
|
|
11580
|
+
a: 0,
|
|
11581
|
+
b: 0
|
|
11582
|
+
});
|
|
11583
|
+
this.patchSplit(split, split + 1, -1);
|
|
11584
|
+
this.compile(options[i]);
|
|
11585
|
+
jumps.push(this.emit({
|
|
11586
|
+
op: "jmp",
|
|
11587
|
+
to: 0
|
|
11588
|
+
}));
|
|
11589
|
+
this.patchSplit(split, -1, this.prog.length);
|
|
11590
|
+
}
|
|
11591
|
+
this.compile(options[options.length - 1]);
|
|
11592
|
+
for (const jump of jumps) this.patchJump(jump, this.prog.length);
|
|
11593
|
+
}
|
|
11594
|
+
compileRepeat(body, min, max) {
|
|
11595
|
+
for (let i = 0; i < min; i++) {
|
|
11596
|
+
const before = this.prog.length;
|
|
11597
|
+
this.compile(body);
|
|
11598
|
+
if (this.prog.length === before) return;
|
|
11599
|
+
}
|
|
11600
|
+
if (max === Infinity) {
|
|
11601
|
+
const split = this.emit({
|
|
11602
|
+
op: "split",
|
|
11603
|
+
a: 0,
|
|
11604
|
+
b: 0
|
|
11605
|
+
});
|
|
11606
|
+
this.compile(body);
|
|
11607
|
+
this.emit({
|
|
11608
|
+
op: "jmp",
|
|
11609
|
+
to: split
|
|
11610
|
+
});
|
|
11611
|
+
this.patchSplit(split, split + 1, this.prog.length);
|
|
11612
|
+
return;
|
|
11613
|
+
}
|
|
11614
|
+
const splits = [];
|
|
11615
|
+
for (let i = min; i < max; i++) {
|
|
11616
|
+
const split = this.emit({
|
|
11617
|
+
op: "split",
|
|
11618
|
+
a: 0,
|
|
11619
|
+
b: 0
|
|
11620
|
+
});
|
|
11621
|
+
this.patchSplit(split, split + 1, -1);
|
|
11622
|
+
splits.push(split);
|
|
11623
|
+
this.compile(body);
|
|
11624
|
+
}
|
|
11625
|
+
for (const split of splits) this.patchSplit(split, -1, this.prog.length);
|
|
11626
|
+
}
|
|
11627
|
+
patchSplit(at, a, b) {
|
|
11628
|
+
const inst = this.prog[at];
|
|
11629
|
+
if (a >= 0) inst.a = a;
|
|
11630
|
+
if (b >= 0) inst.b = b;
|
|
11631
|
+
}
|
|
11632
|
+
patchJump(at, to) {
|
|
11633
|
+
this.prog[at].to = to;
|
|
11634
|
+
}
|
|
11635
|
+
};
|
|
11636
|
+
function isWordUnit(unit) {
|
|
11637
|
+
return unit >= 48 && unit <= 57 || unit >= 65 && unit <= 90 || unit >= 97 && unit <= 122 || unit === 95;
|
|
11638
|
+
}
|
|
11639
|
+
var LinearRegExp = class {
|
|
11640
|
+
anchoredStart;
|
|
11641
|
+
prog;
|
|
11642
|
+
unicode;
|
|
11643
|
+
constructor(prog, unicode) {
|
|
11644
|
+
this.prog = prog;
|
|
11645
|
+
this.unicode = unicode;
|
|
11646
|
+
const first = prog[0];
|
|
11647
|
+
this.anchoredStart = first !== void 0 && first.op === "assert" && first.assertion === "start";
|
|
11648
|
+
}
|
|
11649
|
+
get size() {
|
|
11650
|
+
return this.prog.length;
|
|
11651
|
+
}
|
|
11652
|
+
cost(input) {
|
|
11653
|
+
return (input.length + 1) * this.prog.length;
|
|
11654
|
+
}
|
|
11655
|
+
search(input, budget) {
|
|
11656
|
+
if (this.cost(input) > budget) return void 0;
|
|
11657
|
+
const prog = this.prog;
|
|
11658
|
+
const size = prog.length;
|
|
11659
|
+
const mark = new Int32Array(size).fill(-1);
|
|
11660
|
+
const stack = [];
|
|
11661
|
+
let clist = new Int32Array(size);
|
|
11662
|
+
let nlist = new Int32Array(size);
|
|
11663
|
+
const add = (list, count, start, pos, generation) => {
|
|
11664
|
+
stack.push(start);
|
|
11665
|
+
while (stack.length > 0) {
|
|
11666
|
+
const pc = stack.pop();
|
|
11667
|
+
if (mark[pc] === generation) continue;
|
|
11668
|
+
mark[pc] = generation;
|
|
11669
|
+
const inst = prog[pc];
|
|
11670
|
+
switch (inst.op) {
|
|
11671
|
+
case "match":
|
|
11672
|
+
stack.length = 0;
|
|
11673
|
+
return -1;
|
|
11674
|
+
case "jmp":
|
|
11675
|
+
stack.push(inst.to);
|
|
11676
|
+
break;
|
|
11677
|
+
case "split":
|
|
11678
|
+
stack.push(inst.b, inst.a);
|
|
11679
|
+
break;
|
|
11680
|
+
case "assert":
|
|
11681
|
+
if (this.holds(inst.assertion, input, pos)) stack.push(pc + 1);
|
|
11682
|
+
break;
|
|
11683
|
+
case "char": list[count++] = pc;
|
|
11684
|
+
}
|
|
11685
|
+
}
|
|
11686
|
+
return count;
|
|
11687
|
+
};
|
|
11688
|
+
let pos = 0;
|
|
11689
|
+
let generation = 0;
|
|
11690
|
+
let clen = add(clist, 0, 0, pos, generation);
|
|
11691
|
+
if (clen < 0) return true;
|
|
11692
|
+
while (pos < input.length) {
|
|
11693
|
+
if (this.anchoredStart && clen === 0) return false;
|
|
11694
|
+
const cp = this.unicode ? input.codePointAt(pos) : input.charCodeAt(pos);
|
|
11695
|
+
const next = pos + (this.unicode && cp > 65535 ? 2 : 1);
|
|
11696
|
+
generation++;
|
|
11697
|
+
let nlen = 0;
|
|
11698
|
+
for (let i = 0; i < clen; i++) {
|
|
11699
|
+
const pc = clist[i];
|
|
11700
|
+
if (prog[pc].matcher.matches(cp, this.unicode)) {
|
|
11701
|
+
nlen = add(nlist, nlen, pc + 1, next, generation);
|
|
11702
|
+
if (nlen < 0) return true;
|
|
11703
|
+
}
|
|
11704
|
+
}
|
|
11705
|
+
nlen = add(nlist, nlen, 0, next, generation);
|
|
11706
|
+
if (nlen < 0) return true;
|
|
11707
|
+
[clist, nlist] = [nlist, clist];
|
|
11708
|
+
clen = nlen;
|
|
11709
|
+
pos = next;
|
|
11710
|
+
}
|
|
11711
|
+
return false;
|
|
11712
|
+
}
|
|
11713
|
+
holds(assertion, input, pos) {
|
|
11714
|
+
switch (assertion) {
|
|
11715
|
+
case "start": return pos === 0;
|
|
11716
|
+
case "end": return pos === input.length;
|
|
11717
|
+
case "wordBoundary":
|
|
11718
|
+
case "notWordBoundary": return (pos > 0 && isWordUnit(input.charCodeAt(pos - 1))) !== (pos < input.length && isWordUnit(input.charCodeAt(pos))) === (assertion === "wordBoundary");
|
|
11719
|
+
}
|
|
11720
|
+
}
|
|
11721
|
+
};
|
|
11722
|
+
function compileLinearRegExp(pattern, unicode, budget) {
|
|
11723
|
+
if (pattern.length > 8192) return {
|
|
11724
|
+
ok: false,
|
|
11725
|
+
reason: `pattern longer than ${MAX_PATTERN_LENGTH} characters`
|
|
11726
|
+
};
|
|
11727
|
+
if (budget) {
|
|
11728
|
+
budget.steps -= pattern.length;
|
|
11729
|
+
if (isCompileBudgetExhausted(budget)) return {
|
|
11730
|
+
ok: false,
|
|
11731
|
+
reason: COMPILE_BUDGET_EXHAUSTED
|
|
11732
|
+
};
|
|
11733
|
+
}
|
|
11734
|
+
new RegExp(pattern, unicode ? "u" : "");
|
|
11735
|
+
try {
|
|
11736
|
+
const ast = new Parser(pattern, unicode).parse();
|
|
11737
|
+
const compiler = new Compiler(unicode, budget);
|
|
11738
|
+
compiler.compile(ast);
|
|
11739
|
+
compiler.prog.push({ op: "match" });
|
|
11740
|
+
return {
|
|
11741
|
+
ok: true,
|
|
11742
|
+
regExp: new LinearRegExp(compiler.prog, unicode)
|
|
11743
|
+
};
|
|
11744
|
+
} catch (error) {
|
|
11745
|
+
/* v8 ignore next -- every throw inside the parser/compiler is an Unsupported */
|
|
11746
|
+
if (!(error instanceof Unsupported)) throw error;
|
|
11747
|
+
return {
|
|
11748
|
+
ok: false,
|
|
11749
|
+
reason: error.message
|
|
11750
|
+
};
|
|
11751
|
+
}
|
|
11752
|
+
}
|
|
11753
|
+
//#endregion
|
|
11754
|
+
//#region ../../core/mcp/linearPatternValidator.ts
|
|
11755
|
+
const PATTERN_EVALUATION_BUDGET = 2e7;
|
|
11756
|
+
const SCHEMA_COMPILE_BUDGET = {
|
|
11757
|
+
steps: 10 * MAX_COMPILE_STEPS,
|
|
11758
|
+
size: 10 * MAX_PROGRAM_SIZE
|
|
11759
|
+
};
|
|
11760
|
+
const MAX_QUOTED_PATTERN = 120;
|
|
11761
|
+
const DRAFT_2020_12 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2020-12/schema", "http://json-schema.org/draft/2020-12/schema"]);
|
|
11762
|
+
const DRAFT_2019_09 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2019-09/schema", "http://json-schema.org/draft/2019-09/schema"]);
|
|
11763
|
+
const DRAFT_07_OR_06 = /* @__PURE__ */ new Set([
|
|
11764
|
+
"https://json-schema.org/draft-07/schema",
|
|
11765
|
+
"http://json-schema.org/draft-07/schema",
|
|
11766
|
+
"https://json-schema.org/draft-06/schema",
|
|
11767
|
+
"http://json-schema.org/draft-06/schema"
|
|
11768
|
+
]);
|
|
11769
|
+
function declaredDialect(schema) {
|
|
11770
|
+
const declared = schema.$schema;
|
|
11771
|
+
if (typeof declared !== "string") return "2020-12";
|
|
11772
|
+
const uri = declared.replace(/#$/, "");
|
|
11773
|
+
if (DRAFT_2020_12.has(uri)) return "2020-12";
|
|
11774
|
+
if (DRAFT_2019_09.has(uri)) return "2019-09";
|
|
11775
|
+
if (DRAFT_07_OR_06.has(uri)) return "draft-7";
|
|
11776
|
+
throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${declared.slice(0, 200)}"). Supported: JSON Schema 2020-12, 2019-09, draft-07, and draft-06.`);
|
|
11777
|
+
}
|
|
11778
|
+
function quote(pattern) {
|
|
11779
|
+
return pattern.length > MAX_QUOTED_PATTERN ? `${pattern.slice(0, MAX_QUOTED_PATTERN)}…` : pattern;
|
|
11780
|
+
}
|
|
11781
|
+
var PatternLedger = class {
|
|
11782
|
+
remaining = PATTERN_EVALUATION_BUDGET;
|
|
11783
|
+
unevaluated = /* @__PURE__ */ new Set();
|
|
11784
|
+
reset() {
|
|
11785
|
+
this.remaining = PATTERN_EVALUATION_BUDGET;
|
|
11786
|
+
this.unevaluated.clear();
|
|
11787
|
+
}
|
|
11788
|
+
};
|
|
11789
|
+
var LinearPattern = class {
|
|
11790
|
+
source;
|
|
11791
|
+
flags;
|
|
11792
|
+
compiled;
|
|
11793
|
+
ledger;
|
|
11794
|
+
constructor(source, flags, compiled, ledger) {
|
|
11795
|
+
this.source = source;
|
|
11796
|
+
this.flags = flags;
|
|
11797
|
+
this.compiled = compiled;
|
|
11798
|
+
this.ledger = ledger;
|
|
11799
|
+
}
|
|
11800
|
+
test(input) {
|
|
11801
|
+
if (!this.compiled.ok) return this.skip(this.compiled.reason);
|
|
11802
|
+
const { regExp } = this.compiled;
|
|
11803
|
+
const cost = regExp.cost(input);
|
|
11804
|
+
if (cost > this.ledger.remaining) return this.skip(`checking a ${input.length}-character value would exceed the evaluation budget`);
|
|
11805
|
+
this.ledger.remaining -= cost;
|
|
11806
|
+
return regExp.search(input, cost) === true;
|
|
11807
|
+
}
|
|
11808
|
+
skip(reason) {
|
|
11809
|
+
this.ledger.unevaluated.add(`pattern "${quote(this.source)}" was not evaluated (${reason})`);
|
|
11810
|
+
return true;
|
|
11811
|
+
}
|
|
11812
|
+
toString() {
|
|
11813
|
+
return `/${this.source}/${this.flags}`;
|
|
11814
|
+
}
|
|
11815
|
+
};
|
|
11816
|
+
function createRegExpEngine(ledger, budget) {
|
|
11817
|
+
const engine = (pattern, flags) => new LinearPattern(pattern, flags, compileLinearRegExp(pattern, flags.includes("u"), budget()), ledger);
|
|
11818
|
+
return Object.assign(engine, { code: "linearRegExp" });
|
|
11819
|
+
}
|
|
11820
|
+
var LinearPatternJsonSchemaValidator = class {
|
|
11821
|
+
ledger = new PatternLedger();
|
|
11822
|
+
engines = /* @__PURE__ */ new Map();
|
|
11823
|
+
compileBudget = { ...SCHEMA_COMPILE_BUDGET };
|
|
11824
|
+
overBudget = /* @__PURE__ */ new WeakSet();
|
|
11825
|
+
engineFor(dialect) {
|
|
11826
|
+
let engine = this.engines.get(dialect);
|
|
11827
|
+
if (!engine) {
|
|
11828
|
+
const options = {
|
|
11829
|
+
strict: false,
|
|
11830
|
+
validateFormats: true,
|
|
11831
|
+
validateSchema: false,
|
|
11832
|
+
allErrors: true,
|
|
11833
|
+
code: { regExp: createRegExpEngine(this.ledger, () => this.compileBudget) }
|
|
11834
|
+
};
|
|
11835
|
+
engine = dialect === "2020-12" ? new Ajv2020(options) : dialect === "2019-09" ? new Ajv2019(options) : new Ajv(options);
|
|
11836
|
+
addFormats(engine);
|
|
11837
|
+
this.engines.set(dialect, engine);
|
|
11838
|
+
}
|
|
11839
|
+
return engine;
|
|
11840
|
+
}
|
|
11841
|
+
getValidator(schema) {
|
|
11842
|
+
const engine = this.engineFor(declaredDialect(schema));
|
|
11843
|
+
const id = schema.$id;
|
|
11844
|
+
const validate = this.tracked(() => (typeof id === "string" ? engine.getSchema(id) : void 0) ?? engine.compile(schema));
|
|
11845
|
+
const overBudget = this.overBudget.has(validate);
|
|
11846
|
+
return (input) => {
|
|
11847
|
+
this.ledger.reset();
|
|
11848
|
+
const valid = validate(input);
|
|
11849
|
+
const unevaluated = [...this.ledger.unevaluated];
|
|
11850
|
+
if (overBudget) unevaluated.unshift("the schema's patterns exceed the compilation budget");
|
|
11851
|
+
if (valid && unevaluated.length === 0) return {
|
|
11852
|
+
valid: true,
|
|
11853
|
+
data: input,
|
|
11854
|
+
errorMessage: void 0
|
|
11855
|
+
};
|
|
11856
|
+
const messages = valid ? [] : [engine.errorsText(validate.errors)];
|
|
11857
|
+
if (unevaluated.length > 0) messages.push(`${unevaluated.join("; ")}, so the value could not be verified against the schema`);
|
|
11858
|
+
return {
|
|
11859
|
+
valid: false,
|
|
11860
|
+
data: void 0,
|
|
11861
|
+
errorMessage: messages.join("; ")
|
|
11862
|
+
};
|
|
11863
|
+
};
|
|
11864
|
+
}
|
|
11865
|
+
tracked(run) {
|
|
11866
|
+
const outer = this.compileBudget;
|
|
11867
|
+
const budget = { ...SCHEMA_COMPILE_BUDGET };
|
|
11868
|
+
this.compileBudget = budget;
|
|
11869
|
+
try {
|
|
11870
|
+
const validate = run();
|
|
11871
|
+
if (isCompileBudgetExhausted(budget)) this.overBudget.add(validate);
|
|
11872
|
+
return validate;
|
|
11873
|
+
} finally {
|
|
11874
|
+
this.compileBudget = outer;
|
|
11875
|
+
}
|
|
11876
|
+
}
|
|
11877
|
+
};
|
|
11180
11878
|
function listPaginationExceeded(method) {
|
|
11181
11879
|
return /* @__PURE__ */ new Error(`Salvage walk for ${method} exceeded 64 pages without the server's pagination converging`);
|
|
11182
11880
|
}
|
|
@@ -12923,6 +13621,16 @@ var RemoteClientTransport = class {
|
|
|
12923
13621
|
}
|
|
12924
13622
|
return await res.json();
|
|
12925
13623
|
}
|
|
13624
|
+
releaseOnPageHide() {
|
|
13625
|
+
if (!this._sessionId) return false;
|
|
13626
|
+
this.fetchFn(`${this.baseUrl}/api/mcp/disconnect`, {
|
|
13627
|
+
method: "POST",
|
|
13628
|
+
headers: this.headers,
|
|
13629
|
+
body: JSON.stringify({ sessionId: this._sessionId }),
|
|
13630
|
+
keepalive: true
|
|
13631
|
+
}).catch(() => {});
|
|
13632
|
+
return true;
|
|
13633
|
+
}
|
|
12926
13634
|
async close() {
|
|
12927
13635
|
if (this.closed) return;
|
|
12928
13636
|
this.closed = true;
|
|
@@ -13860,7 +14568,7 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
|
|
|
13860
14568
|
params: request.params,
|
|
13861
14569
|
signal: controller.signal
|
|
13862
14570
|
});
|
|
13863
|
-
this.outputValidator ??= new
|
|
14571
|
+
this.outputValidator ??= new LinearPatternJsonSchemaValidator();
|
|
13864
14572
|
const invalid = validateAppElicitResult(this.outputValidator, request.params, result);
|
|
13865
14573
|
if (invalid) {
|
|
13866
14574
|
this.logger.warn({
|
|
@@ -14459,7 +15167,7 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
|
|
|
14459
15167
|
});
|
|
14460
15168
|
}
|
|
14461
15169
|
validateToolOutput(tool, result) {
|
|
14462
|
-
this.outputValidator ??= new
|
|
15170
|
+
this.outputValidator ??= new LinearPatternJsonSchemaValidator();
|
|
14463
15171
|
return validateToolOutput(this.outputValidator, tool, result);
|
|
14464
15172
|
}
|
|
14465
15173
|
cancelPendingTaskInput(taskId) {
|
|
@@ -15306,6 +16014,10 @@ var InspectorClient = class InspectorClient extends InspectorClientEventTarget {
|
|
|
15306
16014
|
getRemoteBackendSessionId() {
|
|
15307
16015
|
if (this.baseTransport instanceof RemoteClientTransport) return this.baseTransport.getRemoteBackendSessionId();
|
|
15308
16016
|
}
|
|
16017
|
+
releaseRemoteSessionOnPageHide() {
|
|
16018
|
+
if (this.baseTransport instanceof RemoteClientTransport) return this.baseTransport.releaseOnPageHide();
|
|
16019
|
+
return false;
|
|
16020
|
+
}
|
|
15309
16021
|
async resumeAfterOAuth(authorizationCode, options) {
|
|
15310
16022
|
await this.completeOAuthFlow(authorizationCode, options?.iss, options?.state);
|
|
15311
16023
|
const remoteSessionId = options?.remoteSessionId;
|
|
@@ -16032,6 +16744,7 @@ function resolveLoadOptions(flags) {
|
|
|
16032
16744
|
};
|
|
16033
16745
|
}
|
|
16034
16746
|
const OTHER_ERRORS = "(other)";
|
|
16747
|
+
const NO_TOOL_ERROR_TEXT = "(no text content)";
|
|
16035
16748
|
const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
|
|
16036
16749
|
const realClock = {
|
|
16037
16750
|
now: () => performance.now(),
|
|
@@ -16101,10 +16814,45 @@ var ErrorTally = class {
|
|
|
16101
16814
|
return list;
|
|
16102
16815
|
}
|
|
16103
16816
|
};
|
|
16817
|
+
function isLoadStop(value) {
|
|
16818
|
+
if (typeof value !== "object" || value === null) return false;
|
|
16819
|
+
const { reason, detail } = value;
|
|
16820
|
+
return (reason === "interrupted" || reason === "connection_lost" || reason === "consecutive_errors") && typeof detail === "string";
|
|
16821
|
+
}
|
|
16822
|
+
function toolErrorText(content) {
|
|
16823
|
+
const blocks = Array.isArray(content) ? content : [];
|
|
16824
|
+
for (const block of blocks) {
|
|
16825
|
+
if (typeof block !== "object" || block === null) continue;
|
|
16826
|
+
const { type, text } = block;
|
|
16827
|
+
if (type !== "text" || typeof text !== "string") continue;
|
|
16828
|
+
const trimmed = text.trim();
|
|
16829
|
+
if (trimmed === "") continue;
|
|
16830
|
+
return trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed;
|
|
16831
|
+
}
|
|
16832
|
+
return NO_TOOL_ERROR_TEXT;
|
|
16833
|
+
}
|
|
16834
|
+
const LOAD_FLAG_CONFLICT = "load_flag_conflict";
|
|
16835
|
+
function loadSummaryWarnings(summary) {
|
|
16836
|
+
const warnings = [];
|
|
16837
|
+
const stop = summary.stoppedEarly;
|
|
16838
|
+
if (stop !== void 0 && stop.reason !== "interrupted") warnings.push(`Warning: the load run stopped early (${stop.reason}): ${stop.detail}.`);
|
|
16839
|
+
const shortfall = loadRateShortfallWarning(summary);
|
|
16840
|
+
if (shortfall !== void 0) warnings.push(shortfall);
|
|
16841
|
+
return warnings;
|
|
16842
|
+
}
|
|
16843
|
+
function loadRateShortfallWarning(summary) {
|
|
16844
|
+
const { rate, achievedRate, plannedStarts, droppedSlots } = summary;
|
|
16845
|
+
if (rate === void 0 || plannedStarts === void 0 || droppedSlots === void 0 || plannedStarts === 0) return;
|
|
16846
|
+
const started = plannedStarts - droppedSlots;
|
|
16847
|
+
if (started >= plannedStarts * .9) return void 0;
|
|
16848
|
+
return `Warning: --load-rate ${rate}/s was not reached: ${started} of ${plannedStarts} planned calls started (${achievedRate}/s); ${droppedSlots} slots were dropped because every worker was busy. Raise --load-concurrency or lower --load-rate.`;
|
|
16849
|
+
}
|
|
16104
16850
|
async function runLoad(call, options, clock = realClock, signal) {
|
|
16105
16851
|
const { concurrency, durationMs, requests, rate } = options;
|
|
16106
|
-
const
|
|
16852
|
+
const okLatencies = [];
|
|
16853
|
+
const errorLatencies = [];
|
|
16107
16854
|
const tally = new ErrorTally();
|
|
16855
|
+
const toolTally = new ErrorTally();
|
|
16108
16856
|
let ok = 0;
|
|
16109
16857
|
let toolErrors = 0;
|
|
16110
16858
|
let errors = 0;
|
|
@@ -16113,6 +16861,37 @@ async function runLoad(call, options, clock = realClock, signal) {
|
|
|
16113
16861
|
const deadline = durationMs === void 0 ? Infinity : start + durationMs;
|
|
16114
16862
|
const interval = rate === void 0 ? 0 : 1e3 / rate;
|
|
16115
16863
|
let nextStart = start;
|
|
16864
|
+
const halt = new AbortController();
|
|
16865
|
+
let stoppedEarly;
|
|
16866
|
+
let stoppedAt = 0;
|
|
16867
|
+
const stopRun = (stop) => {
|
|
16868
|
+
if (stoppedEarly) {
|
|
16869
|
+
if (stop.reason === "interrupted") stoppedEarly = stop;
|
|
16870
|
+
return;
|
|
16871
|
+
}
|
|
16872
|
+
stoppedEarly = stop;
|
|
16873
|
+
stoppedAt = clock.now();
|
|
16874
|
+
halt.abort();
|
|
16875
|
+
};
|
|
16876
|
+
const onAbort = () => {
|
|
16877
|
+
const reason = signal?.reason;
|
|
16878
|
+
stopRun(isLoadStop(reason) ? reason : {
|
|
16879
|
+
reason: "interrupted",
|
|
16880
|
+
detail: "the run was interrupted"
|
|
16881
|
+
});
|
|
16882
|
+
};
|
|
16883
|
+
if (signal?.aborted) onAbort();
|
|
16884
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
16885
|
+
let errorStreak = 0;
|
|
16886
|
+
let streakSince = 0;
|
|
16887
|
+
const noteThrown = (now) => {
|
|
16888
|
+
if (errorStreak++ === 0) streakSince = now;
|
|
16889
|
+
const window = now - streakSince;
|
|
16890
|
+
if ((requests === void 0 || started < requests) && now < deadline && nextStart < deadline && (errorStreak >= 50 || errorStreak >= 5 && window >= 1e3)) stopRun({
|
|
16891
|
+
reason: "consecutive_errors",
|
|
16892
|
+
detail: `${errorStreak} calls in a row threw over ${round(window)}ms; the server looks unavailable`
|
|
16893
|
+
});
|
|
16894
|
+
};
|
|
16116
16895
|
let turnstile = Promise.resolve();
|
|
16117
16896
|
const claimSlot = async () => {
|
|
16118
16897
|
const ahead = turnstile;
|
|
@@ -16122,7 +16901,7 @@ async function runLoad(call, options, clock = realClock, signal) {
|
|
|
16122
16901
|
try {
|
|
16123
16902
|
if (started > 0 && started % 64 === 0) await yieldToEventLoop();
|
|
16124
16903
|
for (;;) {
|
|
16125
|
-
if (signal
|
|
16904
|
+
if (halt.signal.aborted) return false;
|
|
16126
16905
|
if (requests !== void 0 && started >= requests) return false;
|
|
16127
16906
|
const now = clock.now();
|
|
16128
16907
|
if (now >= deadline) return false;
|
|
@@ -16133,7 +16912,7 @@ async function runLoad(call, options, clock = realClock, signal) {
|
|
|
16133
16912
|
nextStart = now - nextStart < interval ? nextStart + interval : now + interval;
|
|
16134
16913
|
return true;
|
|
16135
16914
|
}
|
|
16136
|
-
await sleepUnlessAborted(clock, scheduled - now, signal);
|
|
16915
|
+
await sleepUnlessAborted(clock, scheduled - now, halt.signal);
|
|
16137
16916
|
}
|
|
16138
16917
|
} finally {
|
|
16139
16918
|
release();
|
|
@@ -16141,26 +16920,57 @@ async function runLoad(call, options, clock = realClock, signal) {
|
|
|
16141
16920
|
};
|
|
16142
16921
|
const worker = async (index) => {
|
|
16143
16922
|
while (await claimSlot()) {
|
|
16144
|
-
if (signal
|
|
16923
|
+
if (halt.signal.aborted) {
|
|
16924
|
+
started--;
|
|
16925
|
+
return;
|
|
16926
|
+
}
|
|
16145
16927
|
const t0 = clock.now();
|
|
16146
16928
|
try {
|
|
16147
|
-
|
|
16148
|
-
|
|
16929
|
+
const outcome = await call(index);
|
|
16930
|
+
const latency = clock.now() - t0;
|
|
16931
|
+
errorStreak = 0;
|
|
16932
|
+
if (outcome === "ok") {
|
|
16933
|
+
ok++;
|
|
16934
|
+
okLatencies.push(latency);
|
|
16935
|
+
} else {
|
|
16936
|
+
toolErrors++;
|
|
16937
|
+
errorLatencies.push(latency);
|
|
16938
|
+
toolTally.add(typeof outcome === "object" ? outcome.message : NO_TOOL_ERROR_TEXT);
|
|
16939
|
+
}
|
|
16149
16940
|
} catch (err) {
|
|
16941
|
+
const now = clock.now();
|
|
16150
16942
|
errors++;
|
|
16943
|
+
errorLatencies.push(now - t0);
|
|
16151
16944
|
tally.add(err instanceof Error ? err.message : String(err));
|
|
16945
|
+
noteThrown(now);
|
|
16152
16946
|
}
|
|
16153
|
-
latencies.push(clock.now() - t0);
|
|
16154
16947
|
}
|
|
16155
16948
|
};
|
|
16156
|
-
|
|
16157
|
-
|
|
16949
|
+
try {
|
|
16950
|
+
await Promise.all(Array.from({ length: concurrency }, (_, i) => worker(i)));
|
|
16951
|
+
} finally {
|
|
16952
|
+
signal?.removeEventListener("abort", onAbort);
|
|
16953
|
+
}
|
|
16954
|
+
const end = clock.now();
|
|
16955
|
+
const requestCapped = requests !== void 0 && started >= requests;
|
|
16956
|
+
const elapsedMs = durationMs !== void 0 && stoppedEarly === void 0 && !requestCapped ? Math.max(end - start, durationMs) : end - start;
|
|
16158
16957
|
const completed = ok + toolErrors + errors;
|
|
16958
|
+
const startWindowEnd = () => {
|
|
16959
|
+
let windowEnd = deadline;
|
|
16960
|
+
if (requestCapped) windowEnd = Math.min(windowEnd, nextStart);
|
|
16961
|
+
if (stoppedEarly !== void 0) windowEnd = Math.min(windowEnd, stoppedAt);
|
|
16962
|
+
/* v8 ignore next -- defensive: a run with no duration, no request cap
|
|
16963
|
+
and no stop never ends, so one of the three always applies. */
|
|
16964
|
+
return Number.isFinite(windowEnd) ? windowEnd : end;
|
|
16965
|
+
};
|
|
16159
16966
|
return {
|
|
16160
16967
|
concurrency,
|
|
16161
16968
|
...durationMs !== void 0 && { durationMs },
|
|
16162
16969
|
...requests !== void 0 && { requestLimit: requests },
|
|
16163
|
-
...rate !== void 0 &&
|
|
16970
|
+
...rate !== void 0 && rateFigures(rate, {
|
|
16971
|
+
started,
|
|
16972
|
+
windowMs: startWindowEnd() - start
|
|
16973
|
+
}),
|
|
16164
16974
|
requests: completed,
|
|
16165
16975
|
ok,
|
|
16166
16976
|
toolErrors,
|
|
@@ -16168,8 +16978,23 @@ async function runLoad(call, options, clock = realClock, signal) {
|
|
|
16168
16978
|
errorRate: completed === 0 ? 0 : round((toolErrors + errors) / completed, 4),
|
|
16169
16979
|
elapsedMs: round(elapsedMs),
|
|
16170
16980
|
throughput: elapsedMs > 0 ? round(completed * 1e3 / elapsedMs) : 0,
|
|
16171
|
-
|
|
16172
|
-
|
|
16981
|
+
okThroughput: elapsedMs > 0 ? round(ok * 1e3 / elapsedMs) : 0,
|
|
16982
|
+
latencyMs: summarizeLatency(okLatencies),
|
|
16983
|
+
errorLatencyMs: summarizeLatency(errorLatencies),
|
|
16984
|
+
errorMessages: tally.entries(),
|
|
16985
|
+
toolErrorMessages: toolTally.entries(),
|
|
16986
|
+
...stoppedEarly !== void 0 && { stoppedEarly }
|
|
16987
|
+
};
|
|
16988
|
+
}
|
|
16989
|
+
function rateFigures(rate, run) {
|
|
16990
|
+
const { started, windowMs } = run;
|
|
16991
|
+
const slots = Math.ceil(windowMs * rate / 1e3 - 1e-9);
|
|
16992
|
+
const plannedStarts = Math.max(started, slots);
|
|
16993
|
+
return {
|
|
16994
|
+
rate,
|
|
16995
|
+
plannedStarts,
|
|
16996
|
+
droppedSlots: plannedStarts - started,
|
|
16997
|
+
achievedRate: windowMs > 0 ? round(started * 1e3 / windowMs) : 0
|
|
16173
16998
|
};
|
|
16174
16999
|
}
|
|
16175
17000
|
async function runLoadAcrossConnections(primary, openConnection, callOn, options, clock = realClock, signal) {
|
|
@@ -16231,7 +17056,11 @@ async function runToolLoad(inspectorClient, args, options, extra = {}) {
|
|
|
16231
17056
|
/* v8 ignore next 3 -- defensive: `ToolCallInvocation.result` is nullable,
|
|
16232
17057
|
but both call paths throw on failure rather than return a null result. */
|
|
16233
17058
|
if (invocation.result === null) throw new Error(invocation.error ?? "Tool call failed");
|
|
16234
|
-
|
|
17059
|
+
if (invocation.result.isError !== true) return "ok";
|
|
17060
|
+
return {
|
|
17061
|
+
outcome: "tool_error",
|
|
17062
|
+
message: toolErrorText(invocation.result.content)
|
|
17063
|
+
};
|
|
16235
17064
|
};
|
|
16236
17065
|
const summary = await runLoadAcrossConnections(inspectorClient, extra.openConnection, call, options, extra.clock, extra.signal);
|
|
16237
17066
|
return {
|
|
@@ -16239,6 +17068,41 @@ async function runToolLoad(inspectorClient, args, options, extra = {}) {
|
|
|
16239
17068
|
...summary
|
|
16240
17069
|
};
|
|
16241
17070
|
}
|
|
17071
|
+
async function withLoadInterrupt(run, options = {}) {
|
|
17072
|
+
const controller = new AbortController();
|
|
17073
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
17074
|
+
const now = options.now ?? (() => performance.now());
|
|
17075
|
+
let first;
|
|
17076
|
+
const onSignal = (signal) => {
|
|
17077
|
+
if (first !== void 0) {
|
|
17078
|
+
if (signal === first.signal && now() - first.at < 500) return;
|
|
17079
|
+
exit(EXIT_CODES.INTERRUPTED);
|
|
17080
|
+
return;
|
|
17081
|
+
}
|
|
17082
|
+
first = {
|
|
17083
|
+
signal,
|
|
17084
|
+
at: now()
|
|
17085
|
+
};
|
|
17086
|
+
const stop = {
|
|
17087
|
+
reason: "interrupted",
|
|
17088
|
+
detail: `received ${signal}`
|
|
17089
|
+
};
|
|
17090
|
+
controller.abort(stop);
|
|
17091
|
+
if (!options.quiet) process.stderr.write(`Interrupted (${signal}): no further calls will start; waiting for in-flight calls to finish. Press Ctrl+C again to exit now.\n`);
|
|
17092
|
+
};
|
|
17093
|
+
process.on("SIGINT", onSignal);
|
|
17094
|
+
process.on("SIGTERM", onSignal);
|
|
17095
|
+
try {
|
|
17096
|
+
return await run(controller.signal);
|
|
17097
|
+
} finally {
|
|
17098
|
+
process.off("SIGINT", onSignal);
|
|
17099
|
+
process.off("SIGTERM", onSignal);
|
|
17100
|
+
}
|
|
17101
|
+
}
|
|
17102
|
+
function assertNotInterrupted(summary) {
|
|
17103
|
+
if (summary.stoppedEarly?.reason !== "interrupted") return;
|
|
17104
|
+
throw new CliExitCodeError(EXIT_CODES.INTERRUPTED, `Load run interrupted (${summary.stoppedEarly.detail}); the summary covers the ${summary.requests} calls that completed.`, { code: "interrupted" });
|
|
17105
|
+
}
|
|
16242
17106
|
//#endregion
|
|
16243
17107
|
//#region ../../core/cli/handlers/load-thresholds.ts
|
|
16244
17108
|
const LOAD_ERROR_RATE_EXCEEDED = "load_error_rate_exceeded";
|
|
@@ -16274,7 +17138,7 @@ function p99Breach(summary, max) {
|
|
|
16274
17138
|
const p99 = summary.latencyMs?.p99;
|
|
16275
17139
|
if (p99 === void 0) return {
|
|
16276
17140
|
code: LOAD_P99_EXCEEDED,
|
|
16277
|
-
message: `no call
|
|
17141
|
+
message: `no call succeeded, so there is no p99 latency to check against --load-max-p99 ${max}`
|
|
16278
17142
|
};
|
|
16279
17143
|
if (p99 <= max) return void 0;
|
|
16280
17144
|
return {
|
|
@@ -16843,21 +17707,38 @@ async function runMethod(inspectorClient, args, options = {}) {
|
|
|
16843
17707
|
//#region ../../core/auth/node/oauth-callback-server.ts
|
|
16844
17708
|
const DEFAULT_HOSTNAME = "127.0.0.1";
|
|
16845
17709
|
const DEFAULT_CALLBACK_PATH = "/oauth/callback";
|
|
16846
|
-
const SUCCESS_HTML =
|
|
16847
|
-
|
|
16848
|
-
|
|
16849
|
-
|
|
16850
|
-
|
|
16851
|
-
function errorHtml(
|
|
17710
|
+
const SUCCESS_HTML = page("OAuth complete", "OAuth complete. You can close this window.");
|
|
17711
|
+
const NOT_CALLBACK_HTML = page("Not the OAuth callback", "This address is not the OAuth callback the Inspector is waiting for. Sign-in has not finished; return to the Inspector to continue.");
|
|
17712
|
+
const STATE_MISMATCH_HTML = page("OAuth callback ignored", "This callback does not belong to the sign-in in progress and was ignored. Sign-in has not finished; the Inspector is still waiting for the authorization server.");
|
|
17713
|
+
const ALREADY_HANDLED_HTML = page("OAuth callback already received", "This sign-in has already received its callback. You can close this window.");
|
|
17714
|
+
const STOPPED_HTML = page("OAuth callback not expected", "The Inspector is no longer waiting for this sign-in. Start sign-in again from the Inspector.");
|
|
17715
|
+
function errorHtml(serverText) {
|
|
17716
|
+
return page("OAuth error", `OAuth failed. The authorization server said: ${serverText}`);
|
|
17717
|
+
}
|
|
17718
|
+
function exchangeErrorHtml(message) {
|
|
17719
|
+
return page("OAuth error", `OAuth failed: ${message}`);
|
|
17720
|
+
}
|
|
17721
|
+
function page(title, text) {
|
|
16852
17722
|
return `<!DOCTYPE html>
|
|
16853
17723
|
<html>
|
|
16854
|
-
<head><meta charset="utf-8"><title
|
|
16855
|
-
<body><p
|
|
17724
|
+
<head><meta charset="utf-8"><title>${escapeHtml(title)}</title></head>
|
|
17725
|
+
<body><p>${escapeHtml(text)}</p></body>
|
|
16856
17726
|
</html>`;
|
|
16857
17727
|
}
|
|
16858
17728
|
function escapeHtml(s) {
|
|
16859
17729
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
16860
17730
|
}
|
|
17731
|
+
const ANY_STATE = Symbol("any-state");
|
|
17732
|
+
const STOPPED = Symbol("stopped");
|
|
17733
|
+
function createStopSignal() {
|
|
17734
|
+
let fire;
|
|
17735
|
+
return {
|
|
17736
|
+
stopped: new Promise((resolve) => {
|
|
17737
|
+
fire = () => resolve(STOPPED);
|
|
17738
|
+
}),
|
|
17739
|
+
fire
|
|
17740
|
+
};
|
|
17741
|
+
}
|
|
16861
17742
|
var OAuthCallbackServer = class {
|
|
16862
17743
|
server = null;
|
|
16863
17744
|
port = 0;
|
|
@@ -16865,6 +17746,8 @@ var OAuthCallbackServer = class {
|
|
|
16865
17746
|
handled = false;
|
|
16866
17747
|
onCallback;
|
|
16867
17748
|
onError;
|
|
17749
|
+
expectedState = Promise.resolve(ANY_STATE);
|
|
17750
|
+
stopSignal = createStopSignal();
|
|
16868
17751
|
async start(options = {}) {
|
|
16869
17752
|
const { port = 0, hostname = DEFAULT_HOSTNAME, path = DEFAULT_CALLBACK_PATH, onCallback, onError } = options;
|
|
16870
17753
|
if (!path.startsWith("/")) return Promise.reject(/* @__PURE__ */ new Error("Callback path must start with '/' (absolute path)"));
|
|
@@ -16872,6 +17755,8 @@ var OAuthCallbackServer = class {
|
|
|
16872
17755
|
this.onError = onError;
|
|
16873
17756
|
this.handled = false;
|
|
16874
17757
|
this.callbackPath = path;
|
|
17758
|
+
this.stopSignal = createStopSignal();
|
|
17759
|
+
this.expectedState = "expectedState" in options ? Promise.race([Promise.resolve(options.expectedState).catch(() => STOPPED), this.stopSignal.stopped]) : Promise.resolve(ANY_STATE);
|
|
16875
17760
|
return new Promise((resolve, reject) => {
|
|
16876
17761
|
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
16877
17762
|
this.server.on("error", reject);
|
|
@@ -16895,6 +17780,7 @@ var OAuthCallbackServer = class {
|
|
|
16895
17780
|
}
|
|
16896
17781
|
async stop() {
|
|
16897
17782
|
if (!this.server) return;
|
|
17783
|
+
this.stopSignal.fire();
|
|
16898
17784
|
await new Promise((resolve) => {
|
|
16899
17785
|
this.server.close(() => resolve());
|
|
16900
17786
|
});
|
|
@@ -16903,11 +17789,14 @@ var OAuthCallbackServer = class {
|
|
|
16903
17789
|
handleRequest(req, res) {
|
|
16904
17790
|
const needJson = req.headers["accept"]?.includes("application/json");
|
|
16905
17791
|
const send = (status, body, contentType = "text/html; charset=utf-8") => {
|
|
16906
|
-
res.writeHead(status, {
|
|
17792
|
+
res.writeHead(status, {
|
|
17793
|
+
"Content-Type": contentType,
|
|
17794
|
+
Connection: "close"
|
|
17795
|
+
});
|
|
16907
17796
|
res.end(body);
|
|
16908
17797
|
};
|
|
16909
17798
|
if (req.method !== "GET") {
|
|
16910
|
-
send(405, needJson ? "{\"error\":\"Method Not Allowed\"}" :
|
|
17799
|
+
send(405, needJson ? "{\"error\":\"Method Not Allowed\"}" : NOT_CALLBACK_HTML);
|
|
16911
17800
|
return;
|
|
16912
17801
|
}
|
|
16913
17802
|
let pathname;
|
|
@@ -16924,15 +17813,26 @@ var OAuthCallbackServer = class {
|
|
|
16924
17813
|
search = u.search;
|
|
16925
17814
|
state = u.searchParams.get("state") ?? void 0;
|
|
16926
17815
|
} catch {
|
|
16927
|
-
send(400, needJson ? "{\"error\":\"Bad Request\"}" :
|
|
17816
|
+
send(400, needJson ? "{\"error\":\"Bad Request\"}" : NOT_CALLBACK_HTML);
|
|
16928
17817
|
return;
|
|
16929
17818
|
}
|
|
16930
17819
|
if (pathname !== this.callbackPath) {
|
|
16931
|
-
send(404, needJson ? "{\"error\":\"Not Found\"}" :
|
|
17820
|
+
send(404, needJson ? "{\"error\":\"Not Found\"}" : NOT_CALLBACK_HTML);
|
|
17821
|
+
return;
|
|
17822
|
+
}
|
|
17823
|
+
this.expectedState.then((expected) => this.handleCallback(expected, state, search, needJson, send));
|
|
17824
|
+
}
|
|
17825
|
+
handleCallback(expected, state, search, needJson, send) {
|
|
17826
|
+
if (expected === STOPPED) {
|
|
17827
|
+
send(503, needJson ? "{\"error\":\"Not waiting for a callback\"}" : STOPPED_HTML);
|
|
17828
|
+
return;
|
|
17829
|
+
}
|
|
17830
|
+
if (expected !== ANY_STATE && state !== expected) {
|
|
17831
|
+
send(400, needJson ? "{\"error\":\"State mismatch\"}" : STATE_MISMATCH_HTML);
|
|
16932
17832
|
return;
|
|
16933
17833
|
}
|
|
16934
17834
|
if (this.handled) {
|
|
16935
|
-
send(409, needJson ? "{\"error\":\"Callback already handled\"}" :
|
|
17835
|
+
send(409, needJson ? "{\"error\":\"Callback already handled\"}" : ALREADY_HANDLED_HTML);
|
|
16936
17836
|
return;
|
|
16937
17837
|
}
|
|
16938
17838
|
const params = parseOAuthCallbackParams(search);
|
|
@@ -16950,9 +17850,10 @@ var OAuthCallbackServer = class {
|
|
|
16950
17850
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16951
17851
|
this.onError?.({
|
|
16952
17852
|
error: "callback_error",
|
|
16953
|
-
error_description: msg
|
|
17853
|
+
error_description: msg,
|
|
17854
|
+
source: "callback"
|
|
16954
17855
|
});
|
|
16955
|
-
send(500,
|
|
17856
|
+
send(500, exchangeErrorHtml(msg));
|
|
16956
17857
|
this.stop();
|
|
16957
17858
|
});
|
|
16958
17859
|
else {
|
|
@@ -16965,7 +17866,8 @@ var OAuthCallbackServer = class {
|
|
|
16965
17866
|
const msg = generateOAuthErrorDescription(params);
|
|
16966
17867
|
this.onError?.({
|
|
16967
17868
|
error: params.error,
|
|
16968
|
-
error_description: params.error_description ?? void 0
|
|
17869
|
+
error_description: params.error_description ?? void 0,
|
|
17870
|
+
source: "redirect"
|
|
16969
17871
|
});
|
|
16970
17872
|
send(400, errorHtml(msg));
|
|
16971
17873
|
}
|
|
@@ -17029,6 +17931,14 @@ function toRunnerOAuthError(err) {
|
|
|
17029
17931
|
}
|
|
17030
17932
|
return err instanceof Error ? err : new Error(String(err));
|
|
17031
17933
|
}
|
|
17934
|
+
function stateOf(authorizationUrl) {
|
|
17935
|
+
return authorizationUrl.searchParams.get("state") ?? void 0;
|
|
17936
|
+
}
|
|
17937
|
+
function callbackErrorMessage(params) {
|
|
17938
|
+
if (params.source === "callback") return params.error_description ?? "OAuth callback failed";
|
|
17939
|
+
if (!params.error && !params.error_description) return "OAuth error";
|
|
17940
|
+
return `OAuth authorization failed. The authorization server said: ${params.error_description ? `${params.error_description} (${params.error})` : params.error}`;
|
|
17941
|
+
}
|
|
17032
17942
|
async function runRunnerInteractiveOAuth(options) {
|
|
17033
17943
|
const server = (options.createCallbackServer ?? createOAuthCallbackServer)();
|
|
17034
17944
|
let flowResolve;
|
|
@@ -17058,11 +17968,16 @@ async function runRunnerInteractiveOAuth(options) {
|
|
|
17058
17968
|
process.on("SIGTERM", onSignal);
|
|
17059
17969
|
}
|
|
17060
17970
|
let timeoutId;
|
|
17971
|
+
let resolveExpectedState;
|
|
17972
|
+
const expectedState = new Promise((resolve) => {
|
|
17973
|
+
resolveExpectedState = resolve;
|
|
17974
|
+
});
|
|
17061
17975
|
try {
|
|
17062
17976
|
const { redirectUrl } = await racingSignals(server.start({
|
|
17063
17977
|
hostname: options.callbackListen.hostname,
|
|
17064
17978
|
port: options.callbackListen.port,
|
|
17065
17979
|
path: options.callbackListen.pathname,
|
|
17980
|
+
expectedState,
|
|
17066
17981
|
onCallback: async (params) => {
|
|
17067
17982
|
try {
|
|
17068
17983
|
await options.client.completeOAuthFlow(params.code, params.iss, params.state);
|
|
@@ -17072,10 +17987,7 @@ async function runRunnerInteractiveOAuth(options) {
|
|
|
17072
17987
|
}
|
|
17073
17988
|
},
|
|
17074
17989
|
onError: (params) => {
|
|
17075
|
-
flowReject(new Error(
|
|
17076
|
-
/* v8 ignore next -- params.error is a required non-null string, so the "OAuth error" fallback is unreachable */
|
|
17077
|
-
params.error_description ?? params.error ?? "OAuth error"
|
|
17078
|
-
));
|
|
17990
|
+
flowReject(new Error(callbackErrorMessage(params)));
|
|
17079
17991
|
}
|
|
17080
17992
|
}));
|
|
17081
17993
|
options.onCallbackServer?.(server);
|
|
@@ -17091,10 +18003,16 @@ async function runRunnerInteractiveOAuth(options) {
|
|
|
17091
18003
|
})]);
|
|
17092
18004
|
waitForCallback.catch(() => {});
|
|
17093
18005
|
if (options.authorizationUrl) {
|
|
18006
|
+
resolveExpectedState(stateOf(options.authorizationUrl));
|
|
17094
18007
|
await racingSignals(options.client.beginInteractiveAuthorization(options.authorizationUrl));
|
|
17095
18008
|
await waitForCallback;
|
|
17096
|
-
} else
|
|
17097
|
-
|
|
18009
|
+
} else {
|
|
18010
|
+
const authUrl = await racingSignals(options.client.authenticate());
|
|
18011
|
+
if (authUrl !== void 0) {
|
|
18012
|
+
resolveExpectedState(stateOf(authUrl));
|
|
18013
|
+
await waitForCallback;
|
|
18014
|
+
} else return { kind: "already_authorized" };
|
|
18015
|
+
}
|
|
17098
18016
|
if (options.authChallenge) {
|
|
17099
18017
|
if (!await racingSignals(options.client.checkAuthChallengeSatisfied(options.authChallenge, { scopeOnly: true }))) return {
|
|
17100
18018
|
kind: "insufficient_scope",
|
|
@@ -17460,7 +18378,7 @@ function readInspectorVersion(callerUrl) {
|
|
|
17460
18378
|
const validLogLevels = Object.values(LoggingLevelSchema.enum);
|
|
17461
18379
|
const CLI_CLIENT_NAME = "inspector-cli";
|
|
17462
18380
|
async function callMethod(serverConfig, serverSettings, args, clientConfig, cliAuthOverrides, callbackUrlConfig, storedAuthOnly, relogin, revoke, load, loadThresholds) {
|
|
17463
|
-
if (load?.connections !== void 0 && load.connections > 1 && !isOAuthCapableServerConfig(serverConfig)) throw new CliExitCodeError(EXIT_CODES.USAGE, "--load-connections above 1 requires an HTTP or SSE server; over stdio each connection would spawn its own server process.");
|
|
18381
|
+
if (load?.connections !== void 0 && load.connections > 1 && !isOAuthCapableServerConfig(serverConfig)) throw new CliExitCodeError(EXIT_CODES.USAGE, "--load-connections above 1 requires an HTTP or SSE server; over stdio each connection would spawn its own server process.", { code: LOAD_FLAG_CONFLICT });
|
|
17464
18382
|
if (relogin) {
|
|
17465
18383
|
if (!("url" in serverConfig && serverConfig.url)) throw new Error("--relogin requires an HTTP/SSE server URL (no OAuth store entry for stdio)");
|
|
17466
18384
|
const revocation = await clearStoredAuthForRelogin(serverConfig.url, { revoke: revoke && serverSettings?.oauthRevokeOnClear !== false });
|
|
@@ -17527,12 +18445,17 @@ async function callMethod(serverConfig, serverSettings, args, clientConfig, cliA
|
|
|
17527
18445
|
quiet: args.quiet
|
|
17528
18446
|
});
|
|
17529
18447
|
if (load) {
|
|
17530
|
-
const summary = await withCliAuthRecoveryRetry(inspectorClient, serverConfig, redirectUrlProvider, callbackUrlConfig, serverSettings, () => runToolLoad(inspectorClient, args, load, {
|
|
18448
|
+
const summary = await withLoadInterrupt((signal) => withCliAuthRecoveryRetry(inspectorClient, serverConfig, redirectUrlProvider, callbackUrlConfig, serverSettings, () => runToolLoad(inspectorClient, args, load, {
|
|
18449
|
+
openConnection: openLoadConnection,
|
|
18450
|
+
signal
|
|
18451
|
+
}), {
|
|
17531
18452
|
storedAuthOnly,
|
|
17532
18453
|
autoOpenControl,
|
|
17533
18454
|
quiet: args.quiet
|
|
17534
|
-
});
|
|
18455
|
+
}), { quiet: args.quiet });
|
|
17535
18456
|
await writeFormattedResult(summary, args.format === "json" ? "json" : "text");
|
|
18457
|
+
if (!args.quiet) for (const warning of loadSummaryWarnings(summary)) await awaitableError(`${warning}\n`);
|
|
18458
|
+
assertNotInterrupted(summary);
|
|
17536
18459
|
checkLoadThresholds(summary, loadThresholds);
|
|
17537
18460
|
return;
|
|
17538
18461
|
}
|
|
@@ -17798,7 +18721,7 @@ async function parseArgs(argv) {
|
|
|
17798
18721
|
];
|
|
17799
18722
|
if (!validTransports.includes(value)) throw new Error(`Invalid transport type: ${value}. Valid types are: ${validTransports.join(", ")}`);
|
|
17800
18723
|
return value;
|
|
17801
|
-
}).option("--server-url <url>", "Server URL for SSE/HTTP transport").option("--header <headers...>", "HTTP headers as \"HeaderName: Value\" pairs (for HTTP/SSE transports)", parseHeaderPair, {}).option("--metadata <pairs...>", "General metadata as key=value pairs (applied to all methods)", parseKeyValuePair, {}).option("--tool-metadata <pairs...>", "Tool-specific metadata as key=value pairs (for tools/call method only)", parseKeyValuePair, {}).option("--app-info", "Probe the tool's MCP App UI metadata (resourceUri, csp, permissions, domain) and emit it as one JSON line; exit 2 when the tool has no app. Use with --method tools/call --tool-name <name> (the tool itself is not invoked) or --method tools/list (one NDJSON line per tool).").option("--advertise-apps", "Advertise the MCP Apps UI extension (io.modelcontextprotocol/ui) at initialize. Off by default because the CLI cannot render an App; set it when a server only exposes its App tools to a client that claims App support, e.g. for an --app-info probe.").option("--strict", "Report tool-schema portability problems in full (path, issue, suggested fix) on stderr, and exit 6 if any is error-severity. Use with --method tools/list. Without it, a one-line count is printed instead.").option("--verify", "Run the SEP-2640 conformance and digest checks over the skills returned, emit one JSON report per skill on stdout, and exit 7 if any fails or 8 if any could not be fully checked within the read bounds. Use with --method skills/list or --method skills/get.").option("--require-digests", "With --verify: exit 9 when a skill advertises no digests (resources: \"dynamic\"), instead of reporting it as unverifiable and exiting 0.").option("--load", "Load-test one tool: call it repeatedly and concurrently, then print a latency and error-rate summary instead of a result. Use with --method tools/call --tool-name <name>. Bounded by --load-duration (default 10s) and/or --load-requests.").option("--load-concurrency <n>", "With --load: most calls in flight at once (positive integer; default 10).", positiveIntegerParser("--load-concurrency")).option("--load-connections <n>", "With --load: spread the calls over this many independent connections (sessions) to an HTTP or SSE server, opened before timing starts (positive integer, at most --load-concurrency; default 1).", positiveIntegerParser("--load-connections")).option("--load-duration <sec>", "With --load: stop starting calls after this many seconds; in-flight calls still finish (default 10 when --load-requests is not set).", positiveNumberParser("--load-duration")).option("--load-requests <n>", "With --load: stop after starting this many calls (positive integer).", positiveIntegerParser("--load-requests")).option("--load-rate <n>", "With --load: start at most this many calls per second, spread evenly (positive number; default unlimited).", positiveNumberParser("--load-rate")).option("--load-max-error-rate <fraction>", "With --load: exit 10 (load_error_rate_exceeded) when the share of failed calls is above this value, after printing the summary (0 to 1; 0 fails on any failed call).", fractionParser("--load-max-error-rate")).option("--load-max-p99 <ms>", "With --load: exit 10 (load_p99_exceeded) when the p99 latency is above this many milliseconds, or when no call
|
|
18724
|
+
}).option("--server-url <url>", "Server URL for SSE/HTTP transport").option("--header <headers...>", "HTTP headers as \"HeaderName: Value\" pairs (for HTTP/SSE transports)", parseHeaderPair, {}).option("--metadata <pairs...>", "General metadata as key=value pairs (applied to all methods)", parseKeyValuePair, {}).option("--tool-metadata <pairs...>", "Tool-specific metadata as key=value pairs (for tools/call method only)", parseKeyValuePair, {}).option("--app-info", "Probe the tool's MCP App UI metadata (resourceUri, csp, permissions, domain) and emit it as one JSON line; exit 2 when the tool has no app. Use with --method tools/call --tool-name <name> (the tool itself is not invoked) or --method tools/list (one NDJSON line per tool).").option("--advertise-apps", "Advertise the MCP Apps UI extension (io.modelcontextprotocol/ui) at initialize. Off by default because the CLI cannot render an App; set it when a server only exposes its App tools to a client that claims App support, e.g. for an --app-info probe.").option("--strict", "Report tool-schema portability problems in full (path, issue, suggested fix) on stderr, and exit 6 if any is error-severity. Use with --method tools/list. Without it, a one-line count is printed instead.").option("--verify", "Run the SEP-2640 conformance and digest checks over the skills returned, emit one JSON report per skill on stdout, and exit 7 if any fails or 8 if any could not be fully checked within the read bounds. Use with --method skills/list or --method skills/get.").option("--require-digests", "With --verify: exit 9 when a skill advertises no digests (resources: \"dynamic\"), instead of reporting it as unverifiable and exiting 0.").option("--load", "Load-test one tool: call it repeatedly and concurrently, then print a latency and error-rate summary instead of a result. Use with --method tools/call --tool-name <name>. Bounded by --load-duration (default 10s) and/or --load-requests.").option("--load-concurrency <n>", "With --load: most calls in flight at once (positive integer; default 10).", positiveIntegerParser("--load-concurrency")).option("--load-connections <n>", "With --load: spread the calls over this many independent connections (sessions) to an HTTP or SSE server, opened before timing starts (positive integer, at most --load-concurrency; default 1).", positiveIntegerParser("--load-connections")).option("--load-duration <sec>", "With --load: stop starting calls after this many seconds; in-flight calls still finish (default 10 when --load-requests is not set).", positiveNumberParser("--load-duration")).option("--load-requests <n>", "With --load: stop after starting this many calls (positive integer).", positiveIntegerParser("--load-requests")).option("--load-rate <n>", "With --load: start at most this many calls per second, spread evenly (positive number; default unlimited).", positiveNumberParser("--load-rate")).option("--load-max-error-rate <fraction>", "With --load: exit 10 (load_error_rate_exceeded) when the share of failed calls is above this value, after printing the summary (0 to 1; 0 fails on any failed call).", fractionParser("--load-max-error-rate")).option("--load-max-p99 <ms>", "With --load: exit 10 (load_p99_exceeded) when the p99 latency of the successful calls is above this many milliseconds, or when no call succeeded, after printing the summary (positive number).", maxP99Parser).option("--connect-timeout <ms>", `Connection timeout in ms (default ${DEFAULT_CONNECT_TIMEOUT_MS} for ad-hoc --server-url / target invocations; 0 = no timeout).`, (v) => {
|
|
17802
18725
|
const n = Number(v);
|
|
17803
18726
|
if (!Number.isFinite(n) || n < 0) throw new Error(`--connect-timeout must be a non-negative number.`);
|
|
17804
18727
|
return n;
|
|
@@ -17849,12 +18772,12 @@ async function parseArgs(argv) {
|
|
|
17849
18772
|
["--load-max-error-rate", options.loadMaxErrorRate],
|
|
17850
18773
|
["--load-max-p99", options.loadMaxP99]
|
|
17851
18774
|
];
|
|
17852
|
-
for (const [flag, value] of loadBounds) if (value !== void 0 && !options.load) throw
|
|
18775
|
+
for (const [flag, value] of loadBounds) if (value !== void 0 && !options.load) throw loadFlagConflict(`${flag} requires --load.`);
|
|
17853
18776
|
if (options.load) {
|
|
17854
|
-
if (options.method !== "tools/call") throw
|
|
17855
|
-
if (options.appInfo) throw
|
|
17856
|
-
if (options.listStoredAuth || options.printHandoff) throw
|
|
17857
|
-
if (options.output !== void 0 || options.outputFormat !== void 0) throw
|
|
18777
|
+
if (options.method !== "tools/call") throw loadFlagConflict("--load requires --method tools/call.");
|
|
18778
|
+
if (options.appInfo) throw loadFlagConflict("--load cannot be combined with --app-info.");
|
|
18779
|
+
if (options.listStoredAuth || options.printHandoff) throw loadFlagConflict("--load cannot be combined with --list-stored-auth or --print-handoff.");
|
|
18780
|
+
if (options.output !== void 0 || options.outputFormat !== void 0) throw loadFlagConflict("--load cannot be combined with --output or --output-format; redirect stdout to keep the summary.");
|
|
17858
18781
|
}
|
|
17859
18782
|
if (options.advertiseApps && (options.listStoredAuth || options.printHandoff || options.method === "servers/list" || options.method === "servers/show" || isCatalogWriteMethod(options.method))) throw new Error("--advertise-apps requires a command that connects to a server; it has no effect with --list-stored-auth, --print-handoff, or a --method servers/* catalog command.");
|
|
17860
18783
|
const oauthStatePath = getStateFilePath();
|
|
@@ -18002,14 +18925,24 @@ async function parseArgs(argv) {
|
|
|
18002
18925
|
storedAuthOnly: options.storedAuthOnly === true,
|
|
18003
18926
|
relogin: options.relogin === true,
|
|
18004
18927
|
revoke: options.revoke !== false,
|
|
18005
|
-
...options.load && { load:
|
|
18928
|
+
...options.load && { load: resolveCliLoadOptions(options) }
|
|
18929
|
+
};
|
|
18930
|
+
}
|
|
18931
|
+
function loadFlagConflict(message) {
|
|
18932
|
+
return new CliExitCodeError(EXIT_CODES.USAGE, message, { code: LOAD_FLAG_CONFLICT });
|
|
18933
|
+
}
|
|
18934
|
+
function resolveCliLoadOptions(options) {
|
|
18935
|
+
try {
|
|
18936
|
+
return resolveLoadOptions({
|
|
18006
18937
|
concurrency: options.loadConcurrency,
|
|
18007
18938
|
connections: options.loadConnections,
|
|
18008
18939
|
durationSec: options.loadDuration,
|
|
18009
18940
|
requests: options.loadRequests,
|
|
18010
18941
|
rate: options.loadRate
|
|
18011
|
-
})
|
|
18012
|
-
}
|
|
18942
|
+
});
|
|
18943
|
+
} catch (err) {
|
|
18944
|
+
throw loadFlagConflict(err.message);
|
|
18945
|
+
}
|
|
18013
18946
|
}
|
|
18014
18947
|
function discardWarning() {}
|
|
18015
18948
|
async function runCli(argv) {
|