@docyrus/docyrus 0.0.34 → 0.0.35
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 +25 -0
- package/agent-loader.js +3 -2
- package/agent-loader.js.map +2 -2
- package/main.js +82162 -46093
- package/main.js.map +4 -4
- package/package.json +12 -3
- package/resources/chrome-tools/browser-content.js +46 -46
- package/resources/chrome-tools/browser-cookies.js +16 -16
- package/resources/chrome-tools/browser-eval.js +27 -27
- package/resources/chrome-tools/browser-hn-scraper.js +1 -1
- package/resources/chrome-tools/browser-nav.js +23 -23
- package/resources/chrome-tools/browser-pick.js +127 -127
- package/resources/chrome-tools/browser-screenshot.js +10 -10
- package/resources/chrome-tools/browser-start.js +38 -38
- package/resources/pi-agent/extensions/answer.ts +392 -384
- package/resources/pi-agent/extensions/context.ts +415 -415
- package/resources/pi-agent/extensions/control.ts +1287 -1287
- package/resources/pi-agent/extensions/diff.ts +171 -171
- package/resources/pi-agent/extensions/files.ts +155 -155
- package/resources/pi-agent/extensions/knowledge.ts +664 -0
- package/resources/pi-agent/extensions/loop.ts +375 -375
- package/resources/pi-agent/extensions/pi-bash-live-view/index.ts +1 -1
- package/resources/pi-agent/extensions/pi-bash-live-view/package.json +22 -22
- package/resources/pi-agent/extensions/pi-bash-live-view/pty-execute.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/pty-session.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/spawn-helper.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/terminal-emulator.ts +18 -18
- package/resources/pi-agent/extensions/pi-bash-live-view/truncate.ts +1 -1
- package/resources/pi-agent/extensions/pi-bash-live-view/widget.ts +4 -4
- package/resources/pi-agent/extensions/pi-custom-compaction/package.json +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/app-bridge.bundle.js +14 -14
- package/resources/pi-agent/extensions/pi-mcp-adapter/commands.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/config.ts +9 -9
- package/resources/pi-agent/extensions/pi-mcp-adapter/consent-manager.ts +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/direct-tools.ts +13 -13
- package/resources/pi-agent/extensions/pi-mcp-adapter/glimpse-ui.ts +5 -5
- package/resources/pi-agent/extensions/pi-mcp-adapter/host-html-template.ts +13 -13
- package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +14 -14
- package/resources/pi-agent/extensions/pi-mcp-adapter/init.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/lifecycle.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/logger.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/mcp-panel.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/metadata-cache.ts +9 -9
- package/resources/pi-agent/extensions/pi-mcp-adapter/npx-resolver.ts +35 -35
- package/resources/pi-agent/extensions/pi-mcp-adapter/oauth-handler.ts +1 -1
- package/resources/pi-agent/extensions/pi-mcp-adapter/proxy-modes.ts +12 -12
- package/resources/pi-agent/extensions/pi-mcp-adapter/server-manager.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/tool-metadata.ts +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/types.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-resource-handler.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-server.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-session.ts +22 -22
- package/resources/pi-agent/extensions/pi-mcp-adapter/utils.ts +2 -2
- package/resources/pi-agent/extensions/prompt-editor.ts +900 -900
- package/resources/pi-agent/extensions/prompt-url-widget.ts +122 -122
- package/resources/pi-agent/extensions/redraws.ts +14 -14
- package/resources/pi-agent/extensions/review.ts +1533 -1533
- package/resources/pi-agent/extensions/todos.ts +1735 -1735
- package/resources/pi-agent/extensions/tps.ts +40 -40
- package/resources/pi-agent/extensions/whimsical.ts +3 -3
- package/resources/pi-agent/prompts/agent-system.md +2 -0
- package/resources/pi-agent/prompts/coder-system.md +2 -0
- package/server-loader.js +82 -1
- package/server-loader.js.map +3 -3
- package/tui.mjs +2 -0
- package/tui.mjs.map +1 -1
|
@@ -44,12 +44,12 @@ export function getMetadataCachePath(): string {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export function loadMetadataCache(): MetadataCache | null {
|
|
47
|
-
if (!existsSync(CACHE_PATH)) return null;
|
|
47
|
+
if (!existsSync(CACHE_PATH)) {return null;}
|
|
48
48
|
try {
|
|
49
49
|
const raw = JSON.parse(readFileSync(CACHE_PATH, "utf-8"));
|
|
50
|
-
if (!raw || typeof raw !== "object") return null;
|
|
51
|
-
if (raw.version !== CACHE_VERSION) return null;
|
|
52
|
-
if (!raw.servers || typeof raw.servers !== "object") return null;
|
|
50
|
+
if (!raw || typeof raw !== "object") {return null;}
|
|
51
|
+
if (raw.version !== CACHE_VERSION) {return null;}
|
|
52
|
+
if (!raw.servers || typeof raw.servers !== "object") {return null;}
|
|
53
53
|
return raw as MetadataCache;
|
|
54
54
|
} catch {
|
|
55
55
|
return null;
|
|
@@ -105,9 +105,9 @@ export function isServerCacheValid(
|
|
|
105
105
|
definition: ServerEntry,
|
|
106
106
|
maxAgeMs: number = CACHE_MAX_AGE_MS
|
|
107
107
|
): boolean {
|
|
108
|
-
if (!entry || entry.configHash !== computeServerHash(definition)) return false;
|
|
109
|
-
if (!entry.cachedAt || typeof entry.cachedAt !== "number") return false;
|
|
110
|
-
if (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs) return false;
|
|
108
|
+
if (!entry || entry.configHash !== computeServerHash(definition)) {return false;}
|
|
109
|
+
if (!entry.cachedAt || typeof entry.cachedAt !== "number") {return false;}
|
|
110
|
+
if (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs) {return false;}
|
|
111
111
|
return true;
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -120,7 +120,7 @@ export function reconstructToolMetadata(
|
|
|
120
120
|
const metadata: ToolMetadata[] = [];
|
|
121
121
|
|
|
122
122
|
for (const tool of entry.tools ?? []) {
|
|
123
|
-
if (!tool?.name) continue;
|
|
123
|
+
if (!tool?.name) {continue;}
|
|
124
124
|
metadata.push({
|
|
125
125
|
name: formatToolName(tool.name, serverName, prefix),
|
|
126
126
|
originalName: tool.name,
|
|
@@ -133,7 +133,7 @@ export function reconstructToolMetadata(
|
|
|
133
133
|
|
|
134
134
|
if (exposeResources !== false) {
|
|
135
135
|
for (const resource of entry.resources ?? []) {
|
|
136
|
-
if (!resource?.name || !resource?.uri) continue;
|
|
136
|
+
if (!resource?.name || !resource?.uri) {continue;}
|
|
137
137
|
const baseName = `get_${resourceNameToToolName(resource.name)}`;
|
|
138
138
|
metadata.push({
|
|
139
139
|
name: formatToolName(baseName, serverName, prefix),
|
|
@@ -42,7 +42,7 @@ export async function resolveNpxBinary(
|
|
|
42
42
|
? parseNpmExecArgs(args)
|
|
43
43
|
: null;
|
|
44
44
|
|
|
45
|
-
if (!parsed) return null;
|
|
45
|
+
if (!parsed) {return null;}
|
|
46
46
|
|
|
47
47
|
const cacheKey = JSON.stringify([command, ...args]);
|
|
48
48
|
const cache = loadCache();
|
|
@@ -85,19 +85,19 @@ function parseNpxArgs(args: string[]): ParsedInvocation | null {
|
|
|
85
85
|
positionals.push(arg);
|
|
86
86
|
continue;
|
|
87
87
|
}
|
|
88
|
-
if (arg === "-y" || arg === "--yes") continue;
|
|
88
|
+
if (arg === "-y" || arg === "--yes") {continue;}
|
|
89
89
|
if (arg === "-p" || arg === "--package") {
|
|
90
90
|
const value = before[i + 1];
|
|
91
|
-
if (!value || value.startsWith("-")) return null;
|
|
92
|
-
if (!packageSpec) packageSpec = value;
|
|
91
|
+
if (!value || value.startsWith("-")) {return null;}
|
|
92
|
+
if (!packageSpec) {packageSpec = value;}
|
|
93
93
|
sawPackageFlag = true;
|
|
94
94
|
i++;
|
|
95
95
|
continue;
|
|
96
96
|
}
|
|
97
97
|
if (arg.startsWith("--package=")) {
|
|
98
98
|
const value = arg.slice("--package=".length);
|
|
99
|
-
if (!value) return null;
|
|
100
|
-
if (!packageSpec) packageSpec = value;
|
|
99
|
+
if (!value) {return null;}
|
|
100
|
+
if (!packageSpec) {packageSpec = value;}
|
|
101
101
|
sawPackageFlag = true;
|
|
102
102
|
continue;
|
|
103
103
|
}
|
|
@@ -110,22 +110,22 @@ function parseNpxArgs(args: string[]): ParsedInvocation | null {
|
|
|
110
110
|
|
|
111
111
|
if (sawPackageFlag) {
|
|
112
112
|
const binName = positionals[0];
|
|
113
|
-
if (!packageSpec || !binName) return null;
|
|
113
|
+
if (!packageSpec || !binName) {return null;}
|
|
114
114
|
const extraArgs = positionals.slice(1).concat(after);
|
|
115
115
|
return { packageSpec, binName, extraArgs };
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const packagePositional = positionals[0];
|
|
119
|
-
if (!packagePositional) return null;
|
|
119
|
+
if (!packagePositional) {return null;}
|
|
120
120
|
const extraArgs = positionals.slice(1).concat(after);
|
|
121
121
|
return { packageSpec: packagePositional, extraArgs };
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function parseNpmExecArgs(args: string[]): ParsedInvocation | null {
|
|
125
|
-
if (args[0] !== "exec") return null;
|
|
125
|
+
if (args[0] !== "exec") {return null;}
|
|
126
126
|
const execArgs = args.slice(1);
|
|
127
127
|
const separatorIndex = execArgs.indexOf("--");
|
|
128
|
-
if (separatorIndex < 0) return null;
|
|
128
|
+
if (separatorIndex < 0) {return null;}
|
|
129
129
|
|
|
130
130
|
const before = execArgs.slice(0, separatorIndex);
|
|
131
131
|
const after = execArgs.slice(separatorIndex + 1);
|
|
@@ -133,18 +133,18 @@ function parseNpmExecArgs(args: string[]): ParsedInvocation | null {
|
|
|
133
133
|
let packageSpec: string | undefined;
|
|
134
134
|
for (let i = 0; i < before.length; i++) {
|
|
135
135
|
const arg = before[i];
|
|
136
|
-
if (arg === "-y" || arg === "--yes") continue;
|
|
136
|
+
if (arg === "-y" || arg === "--yes") {continue;}
|
|
137
137
|
if (arg === "--package") {
|
|
138
138
|
const value = before[i + 1];
|
|
139
|
-
if (!value || value.startsWith("-")) return null;
|
|
140
|
-
if (!packageSpec) packageSpec = value;
|
|
139
|
+
if (!value || value.startsWith("-")) {return null;}
|
|
140
|
+
if (!packageSpec) {packageSpec = value;}
|
|
141
141
|
i++;
|
|
142
142
|
continue;
|
|
143
143
|
}
|
|
144
144
|
if (arg.startsWith("--package=")) {
|
|
145
145
|
const value = arg.slice("--package=".length);
|
|
146
|
-
if (!value) return null;
|
|
147
|
-
if (!packageSpec) packageSpec = value;
|
|
146
|
+
if (!value) {return null;}
|
|
147
|
+
if (!packageSpec) {packageSpec = value;}
|
|
148
148
|
continue;
|
|
149
149
|
}
|
|
150
150
|
if (arg.startsWith("-")) {
|
|
@@ -153,23 +153,23 @@ function parseNpmExecArgs(args: string[]): ParsedInvocation | null {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const binName = after[0];
|
|
156
|
-
if (!packageSpec || !binName) return null;
|
|
156
|
+
if (!packageSpec || !binName) {return null;}
|
|
157
157
|
const extraArgs = after.slice(1);
|
|
158
158
|
return { packageSpec, binName, extraArgs };
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
function resolveFromNpmCache(packageSpec: string, binName?: string): NpxCacheEntry | null {
|
|
162
162
|
const cacheDir = getNpmCacheDir();
|
|
163
|
-
if (!cacheDir) return null;
|
|
163
|
+
if (!cacheDir) {return null;}
|
|
164
164
|
|
|
165
165
|
const packageName = extractPackageName(packageSpec);
|
|
166
|
-
if (!packageName) return null;
|
|
166
|
+
if (!packageName) {return null;}
|
|
167
167
|
|
|
168
168
|
const packageDir = findCachedPackageDir(cacheDir, packageName);
|
|
169
|
-
if (!packageDir) return null;
|
|
169
|
+
if (!packageDir) {return null;}
|
|
170
170
|
|
|
171
171
|
const packageJsonPath = join(packageDir, "package.json");
|
|
172
|
-
if (!existsSync(packageJsonPath)) return null;
|
|
172
|
+
if (!existsSync(packageJsonPath)) {return null;}
|
|
173
173
|
|
|
174
174
|
let pkg: { bin?: string | Record<string, string>; version?: string } | null = null;
|
|
175
175
|
try {
|
|
@@ -182,7 +182,7 @@ function resolveFromNpmCache(packageSpec: string, binName?: string): NpxCacheEnt
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const binField = pkg?.bin;
|
|
185
|
-
if (!binField) return null;
|
|
185
|
+
if (!binField) {return null;}
|
|
186
186
|
|
|
187
187
|
const candidates = buildBinCandidates(packageName, binName);
|
|
188
188
|
let chosenBinName: string | undefined;
|
|
@@ -208,14 +208,14 @@ function resolveFromNpmCache(packageSpec: string, binName?: string): NpxCacheEnt
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
if (!binRel) return null;
|
|
211
|
+
if (!binRel) {return null;}
|
|
212
212
|
|
|
213
213
|
const nodeModulesDir = findNodeModulesDir(packageDir);
|
|
214
214
|
const binLink = chosenBinName ? join(nodeModulesDir, ".bin", chosenBinName) : null;
|
|
215
215
|
let resolvedBin = binLink && existsSync(binLink) ? safeRealpath(binLink) : "";
|
|
216
216
|
if (!resolvedBin) {
|
|
217
217
|
resolvedBin = resolve(packageDir, binRel);
|
|
218
|
-
if (!existsSync(resolvedBin)) return null;
|
|
218
|
+
if (!existsSync(resolvedBin)) {return null;}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
const isJs = detectJsBinary(resolvedBin);
|
|
@@ -252,13 +252,13 @@ async function forceNpxCache(packageSpec: string): Promise<void> {
|
|
|
252
252
|
|
|
253
253
|
function buildBinCandidates(packageName: string, explicitBin?: string): string[] {
|
|
254
254
|
const candidates: string[] = [];
|
|
255
|
-
if (explicitBin) candidates.push(explicitBin);
|
|
255
|
+
if (explicitBin) {candidates.push(explicitBin);}
|
|
256
256
|
|
|
257
257
|
if (packageName.startsWith("@")) {
|
|
258
258
|
const namePart = packageName.split("/")[1] ?? "";
|
|
259
259
|
const scopePart = packageName.split("/")[0]?.replace("@", "") ?? "";
|
|
260
|
-
if (namePart) candidates.push(namePart);
|
|
261
|
-
if (scopePart && namePart) candidates.push(`${scopePart}-${namePart}`);
|
|
260
|
+
if (namePart) {candidates.push(namePart);}
|
|
261
|
+
if (scopePart && namePart) {candidates.push(`${scopePart}-${namePart}`);}
|
|
262
262
|
} else {
|
|
263
263
|
candidates.push(packageName);
|
|
264
264
|
}
|
|
@@ -268,10 +268,10 @@ function buildBinCandidates(packageName: string, explicitBin?: string): string[]
|
|
|
268
268
|
|
|
269
269
|
function extractPackageName(spec: string): string | null {
|
|
270
270
|
const trimmed = spec.trim();
|
|
271
|
-
if (!trimmed) return null;
|
|
271
|
+
if (!trimmed) {return null;}
|
|
272
272
|
if (trimmed.startsWith("@")) {
|
|
273
273
|
const slashIndex = trimmed.indexOf("/");
|
|
274
|
-
if (slashIndex < 0) return null;
|
|
274
|
+
if (slashIndex < 0) {return null;}
|
|
275
275
|
const atIndex = trimmed.lastIndexOf("@");
|
|
276
276
|
if (atIndex > slashIndex) {
|
|
277
277
|
return trimmed.slice(0, atIndex);
|
|
@@ -292,7 +292,7 @@ function defaultBinName(packageName: string): string {
|
|
|
292
292
|
|
|
293
293
|
function findCachedPackageDir(cacheDir: string, packageName: string): string | null {
|
|
294
294
|
const npxDir = join(cacheDir, "_npx");
|
|
295
|
-
if (!existsSync(npxDir)) return null;
|
|
295
|
+
if (!existsSync(npxDir)) {return null;}
|
|
296
296
|
|
|
297
297
|
const packagePathParts = packageName.startsWith("@")
|
|
298
298
|
? packageName.split("/")
|
|
@@ -328,7 +328,7 @@ function findNodeModulesDir(packageDir: string): string {
|
|
|
328
328
|
|
|
329
329
|
function detectJsBinary(binPath: string): boolean {
|
|
330
330
|
const ext = extname(binPath).toLowerCase();
|
|
331
|
-
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return true;
|
|
331
|
+
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {return true;}
|
|
332
332
|
try {
|
|
333
333
|
const fd = openSync(binPath, "r");
|
|
334
334
|
try {
|
|
@@ -347,7 +347,7 @@ function detectJsBinary(binPath: string): boolean {
|
|
|
347
347
|
let npmCacheDirCached: string | null | undefined;
|
|
348
348
|
|
|
349
349
|
function getNpmCacheDir(): string | null {
|
|
350
|
-
if (npmCacheDirCached !== undefined) return npmCacheDirCached;
|
|
350
|
+
if (npmCacheDirCached !== undefined) {return npmCacheDirCached;}
|
|
351
351
|
if (process.env.NPM_CONFIG_CACHE) {
|
|
352
352
|
npmCacheDirCached = process.env.NPM_CONFIG_CACHE;
|
|
353
353
|
return npmCacheDirCached;
|
|
@@ -368,12 +368,12 @@ function getNpmCacheDir(): string | null {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
function loadCache(): NpxCache | null {
|
|
371
|
-
if (!existsSync(CACHE_PATH)) return null;
|
|
371
|
+
if (!existsSync(CACHE_PATH)) {return null;}
|
|
372
372
|
try {
|
|
373
373
|
const raw = JSON.parse(readFileSync(CACHE_PATH, "utf-8"));
|
|
374
|
-
if (!raw || typeof raw !== "object") return null;
|
|
375
|
-
if (raw.version !== CACHE_VERSION) return null;
|
|
376
|
-
if (!raw.entries || typeof raw.entries !== "object") return null;
|
|
374
|
+
if (!raw || typeof raw !== "object") {return null;}
|
|
375
|
+
if (raw.version !== CACHE_VERSION) {return null;}
|
|
376
|
+
if (!raw.entries || typeof raw.entries !== "object") {return null;}
|
|
377
377
|
return raw as NpxCache;
|
|
378
378
|
} catch {
|
|
379
379
|
return null;
|
|
@@ -26,7 +26,7 @@ function getTokensPath(serverName: string): string {
|
|
|
26
26
|
export function getStoredTokens(serverName: string): OAuthTokens | undefined {
|
|
27
27
|
const tokensPath = getTokensPath(serverName);
|
|
28
28
|
|
|
29
|
-
if (!existsSync(tokensPath)) return undefined;
|
|
29
|
+
if (!existsSync(tokensPath)) {return undefined;}
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
const stored = JSON.parse(readFileSync(tokensPath, "utf-8"));
|
|
@@ -217,7 +217,7 @@ export function executeSearch(
|
|
|
217
217
|
if (!server && getPiTools) {
|
|
218
218
|
const piTools = getPiTools();
|
|
219
219
|
for (const tool of piTools) {
|
|
220
|
-
if (tool.name === "mcp") continue;
|
|
220
|
+
if (tool.name === "mcp") {continue;}
|
|
221
221
|
|
|
222
222
|
if (pattern.test(tool.name) || pattern.test(tool.description ?? "")) {
|
|
223
223
|
piMatches.push({
|
|
@@ -229,7 +229,7 @@ export function executeSearch(
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
232
|
-
if (server && serverName !== server) continue;
|
|
232
|
+
if (server && serverName !== server) {continue;}
|
|
233
233
|
for (const tool of metadata) {
|
|
234
234
|
if (pattern.test(tool.name) || pattern.test(tool.description)) {
|
|
235
235
|
matches.push({
|
|
@@ -347,7 +347,7 @@ export function executeList(state: McpExtensionState, server: string): ProxyTool
|
|
|
347
347
|
const desc = descMap.get(tool) ?? "";
|
|
348
348
|
const truncated = truncateAtWord(desc, 50);
|
|
349
349
|
text += `- ${tool}`;
|
|
350
|
-
if (truncated) text += ` - ${truncated}`;
|
|
350
|
+
if (truncated) {text += ` - ${truncated}`;}
|
|
351
351
|
text += "\n";
|
|
352
352
|
}
|
|
353
353
|
|
|
@@ -444,10 +444,10 @@ export async function executeCall(
|
|
|
444
444
|
|
|
445
445
|
for (const { name: configuredServer } of candidates) {
|
|
446
446
|
const failedAgo = getFailureAgeSeconds(state, configuredServer);
|
|
447
|
-
if (failedAgo !== null) continue;
|
|
447
|
+
if (failedAgo !== null) {continue;}
|
|
448
448
|
const connected = await lazyConnect(state, configuredServer);
|
|
449
|
-
if (!connected) continue;
|
|
450
|
-
if (!prefixMatchedServer) prefixMatchedServer = configuredServer;
|
|
449
|
+
if (!connected) {continue;}
|
|
450
|
+
if (!prefixMatchedServer) {prefixMatchedServer = configuredServer;}
|
|
451
451
|
toolMeta = findToolByName(state.toolMetadata.get(configuredServer), toolName);
|
|
452
452
|
if (toolMeta) {
|
|
453
453
|
serverName = configuredServer;
|
|
@@ -540,12 +540,12 @@ export async function executeCall(
|
|
|
540
540
|
|
|
541
541
|
uiSession = toolMeta.uiResourceUri
|
|
542
542
|
? await maybeStartUiSession(state, {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
543
|
+
serverName,
|
|
544
|
+
toolName: toolMeta.originalName,
|
|
545
|
+
toolArgs: args ?? {},
|
|
546
|
+
uiResourceUri: toolMeta.uiResourceUri,
|
|
547
|
+
streamMode: toolMeta.uiStreamMode,
|
|
548
|
+
})
|
|
549
549
|
: null;
|
|
550
550
|
|
|
551
551
|
const resultPromise = connection.client.callTool({
|
|
@@ -206,7 +206,7 @@ export class McpServerManager {
|
|
|
206
206
|
private attachAdapterNotificationHandlers(serverName: string, client: Client): void {
|
|
207
207
|
client.setNotificationHandler(serverStreamResultPatchNotificationSchema, (notification) => {
|
|
208
208
|
const listener = this.uiStreamListeners.get(notification.params.streamToken);
|
|
209
|
-
if (!listener) return;
|
|
209
|
+
if (!listener) {return;}
|
|
210
210
|
listener(serverName, notification.params);
|
|
211
211
|
});
|
|
212
212
|
}
|
|
@@ -237,7 +237,7 @@ export class McpServerManager {
|
|
|
237
237
|
|
|
238
238
|
async close(name: string): Promise<void> {
|
|
239
239
|
const connection = this.connections.get(name);
|
|
240
|
-
if (!connection) return;
|
|
240
|
+
if (!connection) {return;}
|
|
241
241
|
|
|
242
242
|
// Delete from map BEFORE async cleanup to prevent a race where a
|
|
243
243
|
// concurrent connect() creates a new connection that our deferred
|
|
@@ -284,8 +284,8 @@ export class McpServerManager {
|
|
|
284
284
|
|
|
285
285
|
isIdle(name: string, timeoutMs: number): boolean {
|
|
286
286
|
const connection = this.connections.get(name);
|
|
287
|
-
if (!connection || connection.status !== "connected") return false;
|
|
288
|
-
if (connection.inFlight > 0) return false;
|
|
287
|
+
if (!connection || connection.status !== "connected") {return false;}
|
|
288
|
+
if (connection.inFlight > 0) {return false;}
|
|
289
289
|
return (Date.now() - connection.lastUsedAt) > timeoutMs;
|
|
290
290
|
}
|
|
291
291
|
}
|
|
@@ -302,7 +302,7 @@ function resolveEnv(env?: Record<string, string>): Record<string, string> {
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
if (!env) return resolved;
|
|
305
|
+
if (!env) {return resolved;}
|
|
306
306
|
|
|
307
307
|
for (const [key, value] of Object.entries(env)) {
|
|
308
308
|
// Support ${VAR} and $env:VAR interpolation
|
|
@@ -318,7 +318,7 @@ function resolveEnv(env?: Record<string, string>): Record<string, string> {
|
|
|
318
318
|
* Resolve headers with environment variable interpolation.
|
|
319
319
|
*/
|
|
320
320
|
function resolveHeaders(headers?: Record<string, string>): Record<string, string> | undefined {
|
|
321
|
-
if (!headers) return undefined;
|
|
321
|
+
if (!headers) {return undefined;}
|
|
322
322
|
|
|
323
323
|
const resolved: Record<string, string> = {};
|
|
324
324
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -64,9 +64,9 @@ export function totalToolCount(state: McpExtensionState): number {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export function findToolByName(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | undefined {
|
|
67
|
-
if (!metadata) return undefined;
|
|
67
|
+
if (!metadata) {return undefined;}
|
|
68
68
|
const exact = metadata.find(m => m.name === toolName);
|
|
69
|
-
if (exact) return exact;
|
|
69
|
+
if (exact) {return exact;}
|
|
70
70
|
const normalized = toolName.replace(/-/g, "_");
|
|
71
71
|
return metadata.find(m => m.name.replace(/-/g, "_") === normalized);
|
|
72
72
|
}
|
|
@@ -129,8 +129,8 @@ function formatProperty(name: string, schema: unknown, required: boolean, indent
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
parts.push(`${indent}${name}`);
|
|
132
|
-
if (typeStr) parts.push(`(${typeStr})`);
|
|
133
|
-
if (required) parts.push("*required*");
|
|
132
|
+
if (typeStr) {parts.push(`(${typeStr})`);}
|
|
133
|
+
if (required) {parts.push("*required*");}
|
|
134
134
|
|
|
135
135
|
if (s.description && typeof s.description === "string") {
|
|
136
136
|
parts.push(`- ${s.description}`);
|
|
@@ -345,10 +345,10 @@ export function getServerPrefix(
|
|
|
345
345
|
serverName: string,
|
|
346
346
|
mode: "server" | "none" | "short"
|
|
347
347
|
): string {
|
|
348
|
-
if (mode === "none") return "";
|
|
348
|
+
if (mode === "none") {return "";}
|
|
349
349
|
if (mode === "short") {
|
|
350
350
|
let short = serverName.replace(/-?mcp$/i, "").replace(/-/g, "_");
|
|
351
|
-
if (!short) short = "mcp";
|
|
351
|
+
if (!short) {short = "mcp";}
|
|
352
352
|
return short;
|
|
353
353
|
}
|
|
354
354
|
return serverName.replace(/-/g, "_");
|
|
@@ -80,9 +80,9 @@ export class UiResourceHandler {
|
|
|
80
80
|
|
|
81
81
|
private getListResourceMeta(serverName: string, uri: string): Record<string, unknown> | undefined {
|
|
82
82
|
const connection = this.manager.getConnection(serverName);
|
|
83
|
-
if (!connection?.resources?.length) return undefined;
|
|
83
|
+
if (!connection?.resources?.length) {return undefined;}
|
|
84
84
|
const resource = connection.resources.find((entry) => entry.uri === uri);
|
|
85
|
-
if (!resource || !resource._meta || typeof resource._meta !== "object") return undefined;
|
|
85
|
+
if (!resource || !resource._meta || typeof resource._meta !== "object") {return undefined;}
|
|
86
86
|
return resource._meta;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -94,12 +94,12 @@ function selectContent(result: ReadResourceResult, preferredUri: string): Resour
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const byUri = contents.find((content) => content.uri === preferredUri);
|
|
97
|
-
if (byUri) return byUri;
|
|
97
|
+
if (byUri) {return byUri;}
|
|
98
98
|
|
|
99
99
|
const byHtmlMime = contents.find(
|
|
100
100
|
(content) => content.mimeType && isHtmlMimeType(content.mimeType)
|
|
101
101
|
);
|
|
102
|
-
if (byHtmlMime) return byHtmlMime;
|
|
102
|
+
if (byHtmlMime) {return byHtmlMime;}
|
|
103
103
|
|
|
104
104
|
return contents[0];
|
|
105
105
|
}
|
|
@@ -122,9 +122,9 @@ function toHtml(content: ResourceContentRecord): string {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function extractUiMeta(meta: Record<string, unknown> | undefined): UiResourceMeta {
|
|
125
|
-
if (!meta || typeof meta !== "object") return {};
|
|
125
|
+
if (!meta || typeof meta !== "object") {return {};}
|
|
126
126
|
const ui = meta.ui as Record<string, unknown> | undefined;
|
|
127
|
-
if (!ui || typeof ui !== "object") return {};
|
|
127
|
+
if (!ui || typeof ui !== "object") {return {};}
|
|
128
128
|
|
|
129
129
|
const out: UiResourceMeta = {};
|
|
130
130
|
|
|
@@ -123,7 +123,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
123
123
|
|
|
124
124
|
const updateStreamSummary = (payload: unknown) => {
|
|
125
125
|
const envelope = getVisualizationStreamEnvelope((payload as { structuredContent?: unknown } | null)?.structuredContent);
|
|
126
|
-
if (!envelope) return;
|
|
126
|
+
if (!envelope) {return;}
|
|
127
127
|
if (!streamSummary) {
|
|
128
128
|
streamSummary = {
|
|
129
129
|
streamId: envelope.streamId,
|
|
@@ -156,7 +156,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
const pruneEventLog = () => {
|
|
159
|
-
if (eventLog.length <= MAX_EVENT_LOG) return;
|
|
159
|
+
if (eventLog.length <= MAX_EVENT_LOG) {return;}
|
|
160
160
|
const latestCheckpointIndex = getLatestCheckpointIndex();
|
|
161
161
|
|
|
162
162
|
if (latestCheckpointIndex > 0) {
|
|
@@ -169,7 +169,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
169
169
|
};
|
|
170
170
|
|
|
171
171
|
const pushEvent = (name: string, payload: unknown) => {
|
|
172
|
-
if (completed) return;
|
|
172
|
+
if (completed) {return;}
|
|
173
173
|
const eventId = nextEventId++;
|
|
174
174
|
eventLog.push({ id: eventId, name, payload });
|
|
175
175
|
updateStreamSummary(payload);
|
|
@@ -189,9 +189,9 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
189
189
|
const eventsToReplay = Number.isFinite(parsedLastId)
|
|
190
190
|
? eventLog.filter((entry) => entry.id > parsedLastId)
|
|
191
191
|
: (() => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
const latestCheckpointIndex = getLatestCheckpointIndex();
|
|
193
|
+
return latestCheckpointIndex >= 0 ? eventLog.slice(latestCheckpointIndex) : eventLog;
|
|
194
|
+
})();
|
|
195
195
|
|
|
196
196
|
for (const entry of eventsToReplay) {
|
|
197
197
|
try {
|
|
@@ -213,13 +213,13 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
213
213
|
};
|
|
214
214
|
|
|
215
215
|
const stopWatchdog = () => {
|
|
216
|
-
if (!watchdog) return;
|
|
216
|
+
if (!watchdog) {return;}
|
|
217
217
|
clearInterval(watchdog);
|
|
218
218
|
watchdog = null;
|
|
219
219
|
};
|
|
220
220
|
|
|
221
221
|
const markCompleted = (reason: string) => {
|
|
222
|
-
if (completed) return;
|
|
222
|
+
if (completed) {return;}
|
|
223
223
|
log.debug("Session completed", { reason });
|
|
224
224
|
pushEvent("session-complete", { reason });
|
|
225
225
|
completed = true;
|
|
@@ -227,13 +227,13 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
227
227
|
options.onComplete?.(reason);
|
|
228
228
|
};
|
|
229
229
|
|
|
230
|
-
const server = http.createServer(async
|
|
230
|
+
const server = http.createServer(async(req, res) => {
|
|
231
231
|
try {
|
|
232
232
|
const method = req.method || "GET";
|
|
233
233
|
const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
|
|
234
234
|
|
|
235
235
|
if (method === "GET" && url.pathname === "/") {
|
|
236
|
-
if (!validateTokenQuery(url, sessionToken, res)) return;
|
|
236
|
+
if (!validateTokenQuery(url, sessionToken, res)) {return;}
|
|
237
237
|
touchHeartbeat();
|
|
238
238
|
|
|
239
239
|
const html = buildHostHtmlTemplate({
|
|
@@ -257,7 +257,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
if (method === "GET" && url.pathname === "/events") {
|
|
260
|
-
if (!validateTokenQuery(url, sessionToken, res)) return;
|
|
260
|
+
if (!validateTokenQuery(url, sessionToken, res)) {return;}
|
|
261
261
|
touchHeartbeat();
|
|
262
262
|
log.debug("SSE client connected", { clientCount: sseClients.size + 1 });
|
|
263
263
|
res.writeHead(200, {
|
|
@@ -276,13 +276,13 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
if (method === "GET" && url.pathname === "/health") {
|
|
279
|
-
if (!validateTokenQuery(url, sessionToken, res)) return;
|
|
279
|
+
if (!validateTokenQuery(url, sessionToken, res)) {return;}
|
|
280
280
|
sendJson(res, 200, { ok: true, result: { healthy: true } });
|
|
281
281
|
return;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
if (method === "GET" && url.pathname === "/ui-app") {
|
|
285
|
-
if (!validateTokenQuery(url, sessionToken, res)) return;
|
|
285
|
+
if (!validateTokenQuery(url, sessionToken, res)) {return;}
|
|
286
286
|
touchHeartbeat();
|
|
287
287
|
// Serve the MCP app's UI HTML directly (avoids blob URL security issues)
|
|
288
288
|
// Apply CSP meta tag if specified in resource metadata
|
|
@@ -318,8 +318,8 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
const body = await parseBody(req, res);
|
|
321
|
-
if (!body) return;
|
|
322
|
-
if (!validateTokenBody(body, sessionToken, res)) return;
|
|
321
|
+
if (!body) {return;}
|
|
322
|
+
if (!validateTokenBody(body, sessionToken, res)) {return;}
|
|
323
323
|
const params = body.params ?? {};
|
|
324
324
|
touchHeartbeat();
|
|
325
325
|
|
|
@@ -478,8 +478,8 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
watchdog = setInterval(() => {
|
|
481
|
-
if (completed) return;
|
|
482
|
-
if (Date.now() - lastHeartbeatAt <= ABANDONED_GRACE_MS) return;
|
|
481
|
+
if (completed) {return;}
|
|
482
|
+
if (Date.now() - lastHeartbeatAt <= ABANDONED_GRACE_MS) {return;}
|
|
483
483
|
markCompleted("stale");
|
|
484
484
|
try {
|
|
485
485
|
server.close();
|