@humain/terminal 0.1.1 → 0.2.2
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/CHANGELOG.md +22 -0
- package/dist/bundle/chunks/{anthropic-messages-XULXUBIG.js → anthropic-messages-QFCEKGI3.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-JEG2XOYC.js → azure-openai-responses-G4ZMHV37.js} +1 -1
- package/dist/bundle/chunks/chunk-7T6XCLJF.js +22 -0
- package/dist/bundle/chunks/{chunk-LYAMSPR2.js → chunk-ADQYVZ6A.js} +483 -506
- package/dist/bundle/chunks/chunk-D6HHRSB5.js +103 -0
- package/dist/bundle/chunks/{chunk-JUPSJTFQ.js → chunk-K4JAZYQH.js} +1 -1
- package/dist/bundle/chunks/{chunk-HPPQHK3J.js → chunk-KKIB2TVR.js} +1 -1
- package/dist/bundle/chunks/chunk-LI2YVPWP.js +2 -0
- package/dist/bundle/chunks/chunk-NMPIM7MN.js +2 -0
- package/dist/bundle/chunks/{chunk-BZQ53VGE.js → chunk-XSR434CO.js} +1 -1
- package/dist/bundle/chunks/compat-CN7WN6DA.js +2 -0
- package/dist/bundle/chunks/dist-PRPIUY3U.js +2 -0
- package/dist/bundle/chunks/esm-E7UVEYTO.js +4 -0
- package/dist/bundle/chunks/{extract-TRW5FBY6.js → extract-CTIFQZDJ.js} +15 -14
- package/dist/bundle/chunks/{google-generative-ai-OX6VSCA6.js → google-generative-ai-RSUU4MWO.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-GW76GSS2.js → google-vertex-CKQPOITO.js} +1 -1
- package/dist/bundle/chunks/host-WP3MVKS2.js +2 -0
- package/dist/bundle/chunks/{node-KOIP55YZ.js → node-NF43OPII.js} +1 -1
- package/dist/bundle/chunks/{node-XY3CBY3C.js → node-UMFM7CDZ.js} +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-NPLWP6ZC.js → openai-codex-responses-S34V5ZSD.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-OT2GTJKC.js → openai-completions-T2IGVUEI.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-5A6CR7GI.js → openai-responses-GYZODCT3.js} +1 -1
- package/dist/bundle/chunks/readability-LAGF34IB.js +3 -0
- package/dist/bundle/chunks/turndown.cjs-HRMZVRUL.js +2 -0
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/model-runtime.d.ts +21 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +34 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +21 -8
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/subagent-env.d.ts +20 -0
- package/dist/core/tools/subagent-env.d.ts.map +1 -0
- package/dist/core/tools/subagent-env.js +47 -0
- package/dist/core/tools/subagent-env.js.map +1 -0
- package/dist/core/tools/subagent-scheduler.d.ts +36 -0
- package/dist/core/tools/subagent-scheduler.d.ts.map +1 -0
- package/dist/core/tools/subagent-scheduler.js +213 -0
- package/dist/core/tools/subagent-scheduler.js.map +1 -0
- package/dist/core/tools/subagent.d.ts +83 -3
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +215 -76
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +78 -0
- package/dist/humain/mcp-adapter/vendor/README.md +125 -19
- package/dist/humain/mcp-adapter/vendor/agent-plugin-loader.ts +69 -30
- package/dist/humain/mcp-adapter/vendor/agent-plugin-provenance.ts +36 -0
- package/dist/humain/mcp-adapter/vendor/claude-plugin-loader.ts +365 -0
- package/dist/humain/mcp-adapter/vendor/commands.ts +13 -15
- package/dist/humain/mcp-adapter/vendor/config.ts +198 -30
- package/dist/humain/mcp-adapter/vendor/consent-manager.ts +40 -9
- package/dist/humain/mcp-adapter/vendor/direct-tool-surface.ts +250 -0
- package/dist/humain/mcp-adapter/vendor/direct-tools.ts +9 -228
- package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js +74 -32
- package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.d.ts +1 -0
- package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.js +31 -0
- package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.js.map +1 -0
- package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.d.ts +9 -0
- package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.js +362 -0
- package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.js.map +1 -0
- package/dist/humain/mcp-adapter/vendor/dist/config.d.ts +3 -1
- package/dist/humain/mcp-adapter/vendor/dist/config.js +189 -27
- package/dist/humain/mcp-adapter/vendor/dist/config.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js +7 -16
- package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js +5 -17
- package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/types.d.ts +30 -4
- package/dist/humain/mcp-adapter/vendor/dist/types.js +26 -0
- package/dist/humain/mcp-adapter/vendor/dist/types.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/utils.d.ts +6 -0
- package/dist/humain/mcp-adapter/vendor/dist/utils.js +65 -6
- package/dist/humain/mcp-adapter/vendor/dist/utils.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/host-html-template.ts +20 -19
- package/dist/humain/mcp-adapter/vendor/http-ca.ts +84 -0
- package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +37102 -15103
- package/dist/humain/mcp-adapter/vendor/index.ts +828 -164
- package/dist/humain/mcp-adapter/vendor/init.ts +48 -10
- package/dist/humain/mcp-adapter/vendor/lazy-loader.ts +7 -0
- package/dist/humain/mcp-adapter/vendor/mcp-auth-fetch.ts +113 -0
- package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +256 -77
- package/dist/humain/mcp-adapter/vendor/mcp-auth.ts +316 -54
- package/dist/humain/mcp-adapter/vendor/mcp-code.ts +45 -18
- package/dist/humain/mcp-adapter/vendor/mcp-install.ts +60 -0
- package/dist/humain/mcp-adapter/vendor/mcp-oauth-provider.ts +70 -6
- package/dist/humain/mcp-adapter/vendor/mcp-output-guard.ts +53 -42
- package/dist/humain/mcp-adapter/vendor/mcp-panel-theme.ts +105 -0
- package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +366 -331
- package/dist/humain/mcp-adapter/vendor/mcp-references.ts +9 -14
- package/dist/humain/mcp-adapter/vendor/mcp-script-worker.mjs +3 -1
- package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +383 -341
- package/dist/humain/mcp-adapter/vendor/mcp-status.ts +5 -0
- package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +7 -16
- package/dist/humain/mcp-adapter/vendor/namespace-tools.ts +61 -3
- package/dist/humain/mcp-adapter/vendor/oauth.ts +6 -2
- package/dist/humain/mcp-adapter/vendor/package-mcp-loader.ts +5 -17
- package/dist/humain/mcp-adapter/vendor/package.json +13 -3
- package/dist/humain/mcp-adapter/vendor/prompts.ts +35 -3
- package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +108 -7
- package/dist/humain/mcp-adapter/vendor/runtime-owner.ts +16 -1
- package/dist/humain/mcp-adapter/vendor/sampling-handler.ts +9 -32
- package/dist/humain/mcp-adapter/vendor/sandbox-proxy-template.ts +20 -93
- package/dist/humain/mcp-adapter/vendor/server-manager.ts +227 -40
- package/dist/humain/mcp-adapter/vendor/session-approvals.ts +187 -0
- package/dist/humain/mcp-adapter/vendor/skills/mcp-scripting/SKILL.md +9 -1
- package/dist/humain/mcp-adapter/vendor/state.ts +10 -1
- package/dist/humain/mcp-adapter/vendor/tool-approval.ts +10 -22
- package/dist/humain/mcp-adapter/vendor/tool-metadata.ts +14 -0
- package/dist/humain/mcp-adapter/vendor/types.ts +57 -4
- package/dist/humain/mcp-adapter/vendor/ui-server.ts +36 -28
- package/dist/humain/mcp-adapter/vendor/utils.ts +64 -6
- package/dist/humain/web-access/vendor/abortable.d.ts +3 -0
- package/dist/humain/web-access/vendor/abortable.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/abortable.js +22 -0
- package/dist/humain/web-access/vendor/abortable.js.map +1 -0
- package/dist/humain/web-access/vendor/crawl4ai.d.ts +9 -0
- package/dist/humain/web-access/vendor/crawl4ai.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/crawl4ai.js +187 -0
- package/dist/humain/web-access/vendor/crawl4ai.js.map +1 -0
- package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/credential-source.js +1 -0
- package/dist/humain/web-access/vendor/credential-source.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.js +20 -2
- package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-run.d.ts +11 -0
- package/dist/humain/web-access/vendor/curator-run.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/curator-run.js +38 -0
- package/dist/humain/web-access/vendor/curator-run.js.map +1 -0
- package/dist/humain/web-access/vendor/curator-server.d.ts +1 -0
- package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-server.js +2 -0
- package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
- package/dist/humain/web-access/vendor/duckduckgo.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/duckduckgo.js +1 -1
- package/dist/humain/web-access/vendor/duckduckgo.js.map +1 -1
- package/dist/humain/web-access/vendor/extract.d.ts +1 -1
- package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/extract.js +58 -11
- package/dist/humain/web-access/vendor/extract.js.map +1 -1
- package/dist/humain/web-access/vendor/fetch-params.js +1 -1
- package/dist/humain/web-access/vendor/fetch-params.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.d.ts +2 -2
- package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.js +12 -5
- package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
- package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/index.js +23 -19
- package/dist/humain/web-access/vendor/index.js.map +1 -1
- package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/openai-search.js +40 -14
- package/dist/humain/web-access/vendor/openai-search.js.map +1 -1
- package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/page-query.js +6 -2
- package/dist/humain/web-access/vendor/page-query.js.map +1 -1
- package/dist/humain/web-access/vendor/query-rewrite.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/query-rewrite.js +6 -2
- package/dist/humain/web-access/vendor/query-rewrite.js.map +1 -1
- package/dist/humain/web-access/vendor/serpapi.d.ts +4 -0
- package/dist/humain/web-access/vendor/serpapi.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/serpapi.js +215 -0
- package/dist/humain/web-access/vendor/serpapi.js.map +1 -0
- package/dist/humain/web-access/vendor/ssrf-protection.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/ssrf-protection.js +5 -1
- package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -1
- package/dist/humain/web-access/vendor/storage.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/storage.js +9 -0
- package/dist/humain/web-access/vendor/storage.js.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.d.ts +1 -1
- package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.js +35 -24
- package/dist/humain/web-access/vendor/summary-review.js.map +1 -1
- package/dist/humain/web-access/vendor/utils.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/utils.js +5 -3
- package/dist/humain/web-access/vendor/utils.js.map +1 -1
- package/dist/modes/json-event.d.ts +8 -0
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +17 -0
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +3 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/docs/environment-variables.md +9 -0
- package/docs/json.md +16 -0
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
- package/src/humain/mcp-adapter/UPSTREAM.md +56 -7
- package/src/humain/mcp-adapter/upstream.json +6 -4
- package/src/humain/mcp-adapter/vendor/CHANGELOG.md +78 -0
- package/src/humain/web-access/UPSTREAM.md +18 -8
- package/src/humain/web-access/upstream.json +6 -5
- package/dist/bundle/chunks/chunk-2LC3S3VV.js +0 -105
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { mkdirSync, statSync } from "node:fs";
|
|
2
|
+
import { BlockList, isIP } from "node:net";
|
|
2
3
|
import { isDeepStrictEqual } from "node:util";
|
|
3
4
|
import {
|
|
4
5
|
Client,
|
|
5
6
|
SdkError,
|
|
6
7
|
SdkErrorCode,
|
|
7
8
|
SdkHttpError,
|
|
9
|
+
SseError,
|
|
8
10
|
SSEClientTransport,
|
|
9
11
|
StreamableHTTPClientTransport,
|
|
10
12
|
UnauthorizedError,
|
|
13
|
+
type FetchLike,
|
|
11
14
|
type GetPromptResult,
|
|
12
15
|
type ListToolsResult,
|
|
13
16
|
type ReadResourceResult,
|
|
@@ -38,9 +41,16 @@ import { resolveNpxBinary } from "./npx-resolver.ts";
|
|
|
38
41
|
import { createJsonSchemaValidator } from "./json-schema-validator.ts";
|
|
39
42
|
import { logger } from "./logger.ts";
|
|
40
43
|
import { RESOURCE_MIME_TYPE } from "./ui-app-bridge-helpers.ts";
|
|
44
|
+
import { isBuiltInAgentPlugin } from "./agent-plugin-provenance.ts";
|
|
41
45
|
import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
|
|
42
46
|
import { extractOAuthConfig, supportsOAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
|
|
43
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
captureOAuthAuthority,
|
|
49
|
+
inspectAuthForUrl,
|
|
50
|
+
invalidateAuthEntryCache,
|
|
51
|
+
type AuthStorageOptions,
|
|
52
|
+
type OAuthAuthority,
|
|
53
|
+
} from "./mcp-auth.ts";
|
|
44
54
|
import { getBearerTokenForUrl } from "./mcp-bearer-store.ts";
|
|
45
55
|
import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.ts";
|
|
46
56
|
import {
|
|
@@ -66,7 +76,9 @@ import {
|
|
|
66
76
|
traceTransportKind,
|
|
67
77
|
wrapTransportWithMcpTrace,
|
|
68
78
|
} from "./mcp-trace.ts";
|
|
79
|
+
import { createOAuthFetch, resolveOAuthHeaders } from "./mcp-auth-fetch.ts";
|
|
69
80
|
import { createRequestHeadersCommandFetch } from "./request-headers-command.ts";
|
|
81
|
+
import { createCaFetch, validateCaFile } from "./http-ca.ts";
|
|
70
82
|
|
|
71
83
|
const MAX_CAPTURED_STDERR_BYTES = 8 * 1024;
|
|
72
84
|
const MAX_CAPTURED_STDERR_LINES = 3;
|
|
@@ -79,6 +91,35 @@ type HttpAuthProviderState =
|
|
|
79
91
|
| { status: "explicit"; provider: McpOAuthProvider }
|
|
80
92
|
| { status: "implicit-challenged"; provider: McpOAuthProvider };
|
|
81
93
|
|
|
94
|
+
function isLiteralLocalAddress(url: string): boolean {
|
|
95
|
+
const hostname = new URL(url).hostname.replace(/^\[|\]$/g, "");
|
|
96
|
+
const family = isIP(hostname);
|
|
97
|
+
if (!family) return false;
|
|
98
|
+
const local = new BlockList();
|
|
99
|
+
local.addSubnet("10.0.0.0", 8);
|
|
100
|
+
local.addSubnet("172.16.0.0", 12);
|
|
101
|
+
local.addSubnet("192.168.0.0", 16);
|
|
102
|
+
local.addSubnet("169.254.0.0", 16);
|
|
103
|
+
local.addSubnet("fc00::", 7, "ipv6");
|
|
104
|
+
local.addSubnet("fe80::", 10, "ipv6");
|
|
105
|
+
return local.check(hostname, family === 6 ? "ipv6" : "ipv4");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function localNetworkFailureCodes(error: unknown, seen = new Set<object>()): string[] {
|
|
109
|
+
if (typeof error !== "object" || error === null || seen.has(error)) return [];
|
|
110
|
+
seen.add(error);
|
|
111
|
+
const codes: string[] = [];
|
|
112
|
+
if ("code" in error && typeof error.code === "string"
|
|
113
|
+
&& ["EHOSTUNREACH", "ENETUNREACH", "EACCES"].includes(error.code)) {
|
|
114
|
+
codes.push(error.code);
|
|
115
|
+
}
|
|
116
|
+
if ("cause" in error) codes.push(...localNetworkFailureCodes(error.cause, seen));
|
|
117
|
+
if (error instanceof AggregateError) {
|
|
118
|
+
for (const nested of error.errors) codes.push(...localNetworkFailureCodes(nested, seen));
|
|
119
|
+
}
|
|
120
|
+
return [...new Set(codes)];
|
|
121
|
+
}
|
|
122
|
+
|
|
82
123
|
function isUnauthorizedHttpError(error: unknown): boolean {
|
|
83
124
|
return error instanceof UnauthorizedError || (error instanceof SdkHttpError && error.status === 401);
|
|
84
125
|
}
|
|
@@ -139,6 +180,8 @@ export interface ServerConnection {
|
|
|
139
180
|
/** Monotonic guard against older refresh responses replacing newer notifications. */
|
|
140
181
|
toolsRevision?: number;
|
|
141
182
|
resources: McpResource[];
|
|
183
|
+
/** True when resources were advertised but resources/list failed. */
|
|
184
|
+
resourceDiscoveryFailed?: boolean;
|
|
142
185
|
prompts: McpPrompt[];
|
|
143
186
|
/** True when prompts were advertised but prompts/list failed. */
|
|
144
187
|
promptDiscoveryFailed?: boolean;
|
|
@@ -172,6 +215,7 @@ export type ToolRefreshResult = "updated" | "unchanged" | "superseded" | "refres
|
|
|
172
215
|
|
|
173
216
|
type ToolListCacheHints = Partial<Pick<ListToolsResult, "ttlMs" | "cacheScope">>;
|
|
174
217
|
type ToolListResult = { tools: McpTool[]; hints?: ToolListCacheHints };
|
|
218
|
+
type ResourceListResult = { resources: McpResource[]; failed: boolean };
|
|
175
219
|
|
|
176
220
|
const KEEP_ALIVE_REFRESH_TIMEOUT_MS = 5_000;
|
|
177
221
|
const LISTEN_RETRY_DELAY_MS = 5_000;
|
|
@@ -190,6 +234,7 @@ export function isTransientHttpConnectError(error: unknown): boolean {
|
|
|
190
234
|
export class McpServerManager {
|
|
191
235
|
private connections = new Map<string, ServerConnection>();
|
|
192
236
|
private connectPromises = new Map<string, Promise<ServerConnection>>();
|
|
237
|
+
private connectOAuthAuthorities = new Map<string, OAuthAuthority>();
|
|
193
238
|
private reconnectPromises = new Map<string, Promise<ServerConnection>>();
|
|
194
239
|
private uiStreamListeners = new Map<string, UiStreamListener>();
|
|
195
240
|
private samplingConfig: ServerSamplingConfig | undefined;
|
|
@@ -305,17 +350,39 @@ export class McpServerManager {
|
|
|
305
350
|
}
|
|
306
351
|
|
|
307
352
|
async connect(name: string, definition: ServerDefinition, signal?: AbortSignal): Promise<ServerConnection> {
|
|
353
|
+
validateCaFile(definition);
|
|
308
354
|
if (isServerDisabled(definition)) throw new Error(`MCP server "${name}" is disabled`);
|
|
309
355
|
if (this.stopped) throw new Error("MCP server manager is closed");
|
|
310
356
|
const ownedSignal = combineAbortSignals(this.runtimeSignal, signal);
|
|
311
357
|
throwIfAborted(ownedSignal);
|
|
358
|
+
const oauthAuthority = definition.url && supportsOAuth(definition)
|
|
359
|
+
? captureOAuthAuthority(name, false)
|
|
360
|
+
: undefined;
|
|
312
361
|
const closing = this.closePromises.get(name);
|
|
313
362
|
if (closing) await abortable(closing, ownedSignal);
|
|
314
363
|
throwIfAborted(ownedSignal);
|
|
315
364
|
|
|
316
365
|
// Dedupe concurrent connection attempts.
|
|
317
|
-
|
|
318
|
-
|
|
366
|
+
const existingConnect = this.connectPromises.get(name);
|
|
367
|
+
if (existingConnect) {
|
|
368
|
+
const existingAuthority = this.connectOAuthAuthorities.get(name);
|
|
369
|
+
let reusable = oauthAuthority === undefined && existingAuthority === undefined;
|
|
370
|
+
if (oauthAuthority && existingAuthority) {
|
|
371
|
+
try {
|
|
372
|
+
existingAuthority();
|
|
373
|
+
reusable = true;
|
|
374
|
+
} catch {
|
|
375
|
+
reusable = false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (reusable) return abortable(existingConnect, ownedSignal);
|
|
379
|
+
const replacedAttempt = this.connectAttempts.get(name);
|
|
380
|
+
replacedAttempt?.abort(new Error(`MCP connection ${name} was replaced`));
|
|
381
|
+
if (this.connectAttempts.get(name) === replacedAttempt) this.connectAttempts.delete(name);
|
|
382
|
+
if (this.connectPromises.get(name) === existingConnect) this.connectPromises.delete(name);
|
|
383
|
+
if (this.connectOAuthAuthorities.get(name) === existingAuthority) {
|
|
384
|
+
this.connectOAuthAuthorities.delete(name);
|
|
385
|
+
}
|
|
319
386
|
}
|
|
320
387
|
|
|
321
388
|
const existing = this.connections.get(name);
|
|
@@ -329,11 +396,20 @@ export class McpServerManager {
|
|
|
329
396
|
const generation = this.closeGenerations.get(name) ?? 0;
|
|
330
397
|
const attemptController = new AbortController();
|
|
331
398
|
const attemptSignal = combineAbortSignals(ownedSignal, attemptController.signal);
|
|
332
|
-
const connectionAttempt = this.createConnection(
|
|
399
|
+
const connectionAttempt = this.createConnection(
|
|
400
|
+
name,
|
|
401
|
+
definition,
|
|
402
|
+
attemptSignal,
|
|
403
|
+
ownedSignal,
|
|
404
|
+
credentialsInvalidated,
|
|
405
|
+
oauthAuthority,
|
|
406
|
+
attemptController,
|
|
407
|
+
);
|
|
333
408
|
const promise = definition.url
|
|
334
409
|
? connectionAttempt.catch(async error => { throw await this.enrichHttpConnectionError(definition, error); })
|
|
335
410
|
: connectionAttempt;
|
|
336
411
|
this.connectPromises.set(name, promise);
|
|
412
|
+
if (oauthAuthority) this.connectOAuthAuthorities.set(name, oauthAuthority);
|
|
337
413
|
this.connectAttempts.set(name, attemptController);
|
|
338
414
|
|
|
339
415
|
try {
|
|
@@ -351,6 +427,9 @@ export class McpServerManager {
|
|
|
351
427
|
return connection;
|
|
352
428
|
} finally {
|
|
353
429
|
if (this.connectPromises.get(name) === promise) this.connectPromises.delete(name);
|
|
430
|
+
if (this.connectOAuthAuthorities.get(name) === oauthAuthority) {
|
|
431
|
+
this.connectOAuthAuthorities.delete(name);
|
|
432
|
+
}
|
|
354
433
|
if (this.connectAttempts.get(name) === attemptController) this.connectAttempts.delete(name);
|
|
355
434
|
}
|
|
356
435
|
}
|
|
@@ -673,7 +752,10 @@ export class McpServerManager {
|
|
|
673
752
|
!isDeepStrictEqual(expectedConnection.tools, nextTools.tools) ||
|
|
674
753
|
!isDeepStrictEqual(expectedConnection.toolListHints, nextTools.hints)
|
|
675
754
|
)) ||
|
|
676
|
-
(nextResources !== undefined &&
|
|
755
|
+
(nextResources !== undefined && (
|
|
756
|
+
!isDeepStrictEqual(expectedConnection.resources, nextResources.resources) ||
|
|
757
|
+
expectedConnection.resourceDiscoveryFailed !== nextResources.failed
|
|
758
|
+
)) ||
|
|
677
759
|
(nextPrompts !== undefined && (
|
|
678
760
|
!isDeepStrictEqual(expectedConnection.prompts, nextPrompts.prompts) ||
|
|
679
761
|
expectedConnection.promptDiscoveryFailed !== false
|
|
@@ -690,7 +772,10 @@ export class McpServerManager {
|
|
|
690
772
|
expectedConnection.toolListHints = nextTools.hints;
|
|
691
773
|
expectedConnection.toolsRevision = (expectedConnection.toolsRevision ?? 0) + 1;
|
|
692
774
|
}
|
|
693
|
-
if (nextResources !== undefined)
|
|
775
|
+
if (nextResources !== undefined) {
|
|
776
|
+
expectedConnection.resources = nextResources.resources;
|
|
777
|
+
expectedConnection.resourceDiscoveryFailed = nextResources.failed;
|
|
778
|
+
}
|
|
694
779
|
if (nextPrompts !== undefined) {
|
|
695
780
|
expectedConnection.prompts = nextPrompts.prompts;
|
|
696
781
|
expectedConnection.promptDiscoveryFailed = false;
|
|
@@ -767,6 +852,8 @@ export class McpServerManager {
|
|
|
767
852
|
signal?: AbortSignal,
|
|
768
853
|
requestSignal?: AbortSignal,
|
|
769
854
|
credentialsInvalidated = false,
|
|
855
|
+
oauthAuthority?: OAuthAuthority,
|
|
856
|
+
attemptOwner?: AbortController,
|
|
770
857
|
): Promise<ServerConnection> {
|
|
771
858
|
throwIfAborted(signal);
|
|
772
859
|
|
|
@@ -795,8 +882,11 @@ export class McpServerManager {
|
|
|
795
882
|
if (definition.command) {
|
|
796
883
|
client = this.createClient(name, definition);
|
|
797
884
|
let command = definition.command;
|
|
798
|
-
|
|
799
|
-
const
|
|
885
|
+
const literalArgs = isBuiltInAgentPlugin(definition, "args");
|
|
886
|
+
const literalCwd = isBuiltInAgentPlugin(definition, "cwd");
|
|
887
|
+
let args = literalArgs ? [...(definition.args ?? [])] : (definition.args ?? []).map((argument) => interpolateEnvVars(argument));
|
|
888
|
+
const cwd = (literalCwd ? definition.cwd : resolveConfigPath(definition.cwd)) ?? this.defaultCwd;
|
|
889
|
+
if (definition.pluginDataDir) mkdirSync(definition.pluginDataDir, { recursive: true });
|
|
800
890
|
if (cwd !== undefined) {
|
|
801
891
|
const cwdStats = statSync(cwd, { throwIfNoEntry: false });
|
|
802
892
|
if (!cwdStats) throw new Error(`MCP server "${name}" configured cwd does not exist: "${cwd}"`);
|
|
@@ -813,11 +903,15 @@ export class McpServerManager {
|
|
|
813
903
|
}
|
|
814
904
|
throwIfAborted(signal);
|
|
815
905
|
|
|
816
|
-
if (definition.pluginDataDir) mkdirSync(definition.pluginDataDir, { recursive: true });
|
|
817
906
|
const stdioTransport = new StdioClientTransport({
|
|
818
907
|
command,
|
|
819
908
|
args,
|
|
820
|
-
env: resolveEnv(
|
|
909
|
+
env: resolveEnv(
|
|
910
|
+
definition.env,
|
|
911
|
+
name,
|
|
912
|
+
isBuiltInAgentPlugin(definition, "env") || definition.literalEnv === true,
|
|
913
|
+
definition.inheritEnv !== false,
|
|
914
|
+
),
|
|
821
915
|
...(cwd !== undefined ? { cwd } : {}),
|
|
822
916
|
stderr: definition.debug ? "inherit" : "pipe",
|
|
823
917
|
});
|
|
@@ -837,6 +931,8 @@ export class McpServerManager {
|
|
|
837
931
|
signal,
|
|
838
932
|
traceObserver,
|
|
839
933
|
invalidated,
|
|
934
|
+
oauthAuthority,
|
|
935
|
+
attemptOwner,
|
|
840
936
|
);
|
|
841
937
|
client = httpConnection.client;
|
|
842
938
|
transport = httpConnection.transport;
|
|
@@ -905,9 +1001,10 @@ export class McpServerManager {
|
|
|
905
1001
|
// identity so a stale connection's late close can never clobber a fresh
|
|
906
1002
|
// connection. The SDK client owns the transport callbacks.
|
|
907
1003
|
client.onclose = () => {
|
|
908
|
-
if (
|
|
909
|
-
|
|
910
|
-
|
|
1004
|
+
if (connection.status === "closed") return;
|
|
1005
|
+
connection.status = "closed";
|
|
1006
|
+
if (this.connections.get(name) !== connection) return;
|
|
1007
|
+
this.metadataListChangedListener?.(name, "remote-close");
|
|
911
1008
|
};
|
|
912
1009
|
|
|
913
1010
|
// Discover tools, resources, and prompts. Resource and prompt listing is
|
|
@@ -917,9 +1014,13 @@ export class McpServerManager {
|
|
|
917
1014
|
this.fetchAllResources(client, requestOptions),
|
|
918
1015
|
this.fetchAllPrompts(client, requestOptions),
|
|
919
1016
|
]);
|
|
1017
|
+
if (connection.status !== "connected") {
|
|
1018
|
+
throw new Error(`MCP connection for ${name} closed during metadata discovery`);
|
|
1019
|
+
}
|
|
920
1020
|
connection.tools = toolResult.tools;
|
|
921
1021
|
connection.toolListHints = toolResult.hints;
|
|
922
|
-
connection.resources = resources;
|
|
1022
|
+
connection.resources = resources.resources;
|
|
1023
|
+
connection.resourceDiscoveryFailed = resources.failed;
|
|
923
1024
|
connection.prompts = promptResult.prompts;
|
|
924
1025
|
connection.promptDiscoveryFailed = promptResult.failed;
|
|
925
1026
|
|
|
@@ -977,6 +1078,12 @@ export class McpServerManager {
|
|
|
977
1078
|
|
|
978
1079
|
private async enrichHttpConnectionError(definition: ServerDefinition, error: unknown): Promise<Error> {
|
|
979
1080
|
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
1081
|
+
if (process.platform === "darwin") {
|
|
1082
|
+
const codes = localNetworkFailureCodes(error);
|
|
1083
|
+
if (codes.length > 0 && isLiteralLocalAddress(resolveServerUrl(definition)!)) {
|
|
1084
|
+
return new Error(`${originalMessage} — ${codes.join(", ")} — macOS Local Network Privacy may be blocking access. Check System Settings > Privacy & Security > Local Network for the app hosting Pi; enable access if listed and restart it. Try launching Pi from Terminal.app or SSH. Routing or firewall problems can also cause this error.`, { cause: error });
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
980
1087
|
if (isTransientHttpConnectError(error)) {
|
|
981
1088
|
return new Error(`${originalMessage} — endpoint is temporarily unavailable (HTTP 503)`, { cause: error });
|
|
982
1089
|
}
|
|
@@ -1142,6 +1249,7 @@ export class McpServerManager {
|
|
|
1142
1249
|
const connection = this.connections.get(serverName);
|
|
1143
1250
|
if (!connection || connection.client !== client || connection.status !== "connected") return;
|
|
1144
1251
|
connection.resources = resources;
|
|
1252
|
+
connection.resourceDiscoveryFailed = false;
|
|
1145
1253
|
this.metadataListChangedListener?.(serverName, "resources-list-changed");
|
|
1146
1254
|
this.pendingMetadataPublications.delete(serverName);
|
|
1147
1255
|
}
|
|
@@ -1179,6 +1287,8 @@ export class McpServerManager {
|
|
|
1179
1287
|
signal?: AbortSignal,
|
|
1180
1288
|
traceObserver?: McpTraceObserver,
|
|
1181
1289
|
credentialsInvalidated = false,
|
|
1290
|
+
oauthAuthority?: OAuthAuthority,
|
|
1291
|
+
attemptOwner?: AbortController,
|
|
1182
1292
|
): Promise<{ client: Client; transport: Transport; status: "connected" | "needs-auth"; credentialsInvalidated: boolean }> {
|
|
1183
1293
|
throwIfAborted(signal);
|
|
1184
1294
|
const serverUrl = resolveServerUrl(definition)!;
|
|
@@ -1186,12 +1296,21 @@ export class McpServerManager {
|
|
|
1186
1296
|
|
|
1187
1297
|
// Resolve secret commands only for this connection attempt, without
|
|
1188
1298
|
// mutating the persisted configuration.
|
|
1189
|
-
const
|
|
1299
|
+
const literalHeaders = isBuiltInAgentPlugin(definition, "headers");
|
|
1300
|
+
const hasCommandHeader = !literalHeaders && Object.values(definition.headers ?? {})
|
|
1190
1301
|
.some(value => value.startsWith("!") && !value.startsWith("!!"));
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1302
|
+
const oauthEnabled = supportsOAuth(definition);
|
|
1303
|
+
let headers: Record<string, string>;
|
|
1304
|
+
if (literalHeaders) {
|
|
1305
|
+
headers = { ...definition.headers };
|
|
1306
|
+
} else if (oauthEnabled) {
|
|
1307
|
+
headers = Object.fromEntries(resolveOAuthHeaders(definition.headers));
|
|
1308
|
+
} else {
|
|
1309
|
+
headers = resolveCommandSecretsRecord(
|
|
1310
|
+
definition.headers,
|
|
1311
|
+
key => `MCP server "${serverName}" HTTP header "${key}"`,
|
|
1312
|
+
) ?? {};
|
|
1313
|
+
}
|
|
1195
1314
|
|
|
1196
1315
|
// Resolve bearer auth before creating requestInit so every attempted
|
|
1197
1316
|
// transport receives the same headers.
|
|
@@ -1216,18 +1335,34 @@ export class McpServerManager {
|
|
|
1216
1335
|
}
|
|
1217
1336
|
}
|
|
1218
1337
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1338
|
+
// Do not give origin-bound headers to SDK requestInit: it reuses those
|
|
1339
|
+
// defaults for discovered OAuth endpoints, including other origins.
|
|
1340
|
+
const requestInit = !oauthEnabled && Object.keys(headers).length > 0 ? { headers } : undefined;
|
|
1341
|
+
const serviceHeaders = oauthEnabled ? new Headers(headers) : new Headers();
|
|
1342
|
+
let caFetch = createCaFetch(definition);
|
|
1343
|
+
try {
|
|
1344
|
+
const createAuthProvider = (): McpOAuthProvider => {
|
|
1345
|
+
const currentAttempt = this.connectAttempts.get(serverName);
|
|
1346
|
+
if (currentAttempt && currentAttempt !== attemptOwner) {
|
|
1347
|
+
throw new Error(`MCP connection for ${serverName} was replaced while connecting`);
|
|
1348
|
+
}
|
|
1349
|
+
if (!oauthAuthority) throw new Error(`Missing OAuth authority for ${serverName}`);
|
|
1350
|
+
const provider = new McpOAuthProvider(
|
|
1351
|
+
serverName,
|
|
1352
|
+
serverUrl,
|
|
1353
|
+
extractOAuthConfig(definition),
|
|
1354
|
+
{ onRedirect: async () => {} },
|
|
1355
|
+
this.authStorageOptions,
|
|
1356
|
+
this.oauthRuntime?.signal,
|
|
1357
|
+
undefined,
|
|
1358
|
+
oauthAuthority,
|
|
1359
|
+
);
|
|
1360
|
+
provider.setAuthFetch(createOAuthFetch(serverUrl, () => serviceHeaders,
|
|
1361
|
+
combineAbortSignals(this.oauthRuntime?.signal, signal), {
|
|
1362
|
+
...(caFetch ? { delegate: caFetch.fetch } : {}),
|
|
1363
|
+
}));
|
|
1364
|
+
return provider;
|
|
1365
|
+
};
|
|
1231
1366
|
|
|
1232
1367
|
// Explicit OAuth checks secure storage immediately. Implicit OAuth keeps
|
|
1233
1368
|
// anonymous servers provider-free unless URL-bound credentials are already
|
|
@@ -1252,6 +1387,16 @@ export class McpServerManager {
|
|
|
1252
1387
|
: { status: "explicit", provider: createAuthProvider() }
|
|
1253
1388
|
: { status: "disabled" };
|
|
1254
1389
|
|
|
1390
|
+
const commandFetch = definition.requestHeadersCommand
|
|
1391
|
+
? createRequestHeadersCommandFetch(definition.requestHeadersCommand, caFetch?.fetch)
|
|
1392
|
+
: caFetch?.fetch;
|
|
1393
|
+
const requestFetch = oauthEnabled
|
|
1394
|
+
? createOAuthFetch(serverUrl, () => serviceHeaders, this.oauthRuntime?.signal, {
|
|
1395
|
+
// MCP streams outlive individual auth requests; retain SDK request deadlines.
|
|
1396
|
+
timeout: false,
|
|
1397
|
+
...(commandFetch ? { delegate: commandFetch } : {}),
|
|
1398
|
+
})
|
|
1399
|
+
: commandFetch;
|
|
1255
1400
|
const attempt = async (
|
|
1256
1401
|
kind: "streamable-http" | "sse",
|
|
1257
1402
|
): Promise<
|
|
@@ -1259,9 +1404,21 @@ export class McpServerManager {
|
|
|
1259
1404
|
| { status: "failed"; client: Client; transport: Transport; error: unknown }
|
|
1260
1405
|
> => {
|
|
1261
1406
|
const authProvider = "provider" in authState ? authState.provider : undefined;
|
|
1407
|
+
let sseFetchFailure: unknown;
|
|
1408
|
+
const transportFetch: FetchLike | undefined = kind === "sse" && process.platform === "darwin" && isLiteralLocalAddress(serverUrl)
|
|
1409
|
+
? async (input, init) => {
|
|
1410
|
+
try {
|
|
1411
|
+
return await (requestFetch ?? globalThis.fetch)(input, init);
|
|
1412
|
+
} catch (error) {
|
|
1413
|
+
// EventSource discards the fetch cause before the SDK creates SseError.
|
|
1414
|
+
if (localNetworkFailureCodes(error).length > 0) sseFetchFailure = error;
|
|
1415
|
+
throw error;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
: requestFetch;
|
|
1262
1419
|
const transportOptions = {
|
|
1263
1420
|
...(requestInit !== undefined ? { requestInit } : {}),
|
|
1264
|
-
...(
|
|
1421
|
+
...(transportFetch !== undefined ? { fetch: transportFetch } : {}),
|
|
1265
1422
|
...(authProvider !== undefined ? { authProvider } : {}),
|
|
1266
1423
|
...(authProvider !== undefined
|
|
1267
1424
|
&& definition.oauth !== false
|
|
@@ -1281,6 +1438,9 @@ export class McpServerManager {
|
|
|
1281
1438
|
await this.connectClientWithAbort(client, transport, requestOptions, signal);
|
|
1282
1439
|
return { status: "connected", client, transport };
|
|
1283
1440
|
} catch (error) {
|
|
1441
|
+
if (error instanceof SseError && sseFetchFailure !== undefined) {
|
|
1442
|
+
error = new AggregateError([error, sseFetchFailure], error.message);
|
|
1443
|
+
}
|
|
1284
1444
|
const abortCleanupFailed = error instanceof AggregateError
|
|
1285
1445
|
&& error.message === "MCP connection abort cleanup failed";
|
|
1286
1446
|
if (!abortCleanupFailed) {
|
|
@@ -1301,7 +1461,24 @@ export class McpServerManager {
|
|
|
1301
1461
|
let invalidated = credentialsInvalidated;
|
|
1302
1462
|
for (;;) {
|
|
1303
1463
|
const result = await attempt(kind);
|
|
1304
|
-
if (result.status === "connected")
|
|
1464
|
+
if (result.status === "connected") {
|
|
1465
|
+
const ownedCa = caFetch;
|
|
1466
|
+
if (ownedCa) {
|
|
1467
|
+
const close = result.transport.close.bind(result.transport);
|
|
1468
|
+
const onclose = result.transport.onclose;
|
|
1469
|
+
result.transport.onclose = () => {
|
|
1470
|
+
void ownedCa.close().catch(error => {
|
|
1471
|
+
logger.debug(`MCP: CA dispatcher cleanup failed for ${serverName}: ${String(error)}`);
|
|
1472
|
+
});
|
|
1473
|
+
onclose?.();
|
|
1474
|
+
};
|
|
1475
|
+
result.transport.close = async () => {
|
|
1476
|
+
try { await close(); } finally { await ownedCa.close(); }
|
|
1477
|
+
};
|
|
1478
|
+
caFetch = undefined;
|
|
1479
|
+
}
|
|
1480
|
+
return { ...result, credentialsInvalidated: invalidated };
|
|
1481
|
+
}
|
|
1305
1482
|
if (result.error instanceof AggregateError
|
|
1306
1483
|
&& result.error.message === "MCP connection abort cleanup failed") {
|
|
1307
1484
|
throw result.error;
|
|
@@ -1334,6 +1511,9 @@ export class McpServerManager {
|
|
|
1334
1511
|
}
|
|
1335
1512
|
throw result.error;
|
|
1336
1513
|
}
|
|
1514
|
+
} finally {
|
|
1515
|
+
await caFetch?.close();
|
|
1516
|
+
}
|
|
1337
1517
|
}
|
|
1338
1518
|
|
|
1339
1519
|
private async fetchAllTools(client: Client, requestOptions?: CacheableRequestOptions): Promise<ToolListResult> {
|
|
@@ -1391,9 +1571,9 @@ export class McpServerManager {
|
|
|
1391
1571
|
}
|
|
1392
1572
|
}
|
|
1393
1573
|
|
|
1394
|
-
private async fetchAllResources(client: Client, requestOptions?: RequestOptions, strict = false): Promise<
|
|
1574
|
+
private async fetchAllResources(client: Client, requestOptions?: RequestOptions, strict = false): Promise<ResourceListResult> {
|
|
1395
1575
|
const capabilities = client.getServerCapabilities?.();
|
|
1396
|
-
if (!capabilities?.resources) return [];
|
|
1576
|
+
if (!capabilities?.resources) return { resources: [], failed: false };
|
|
1397
1577
|
|
|
1398
1578
|
try {
|
|
1399
1579
|
const allResources: McpResource[] = [];
|
|
@@ -1405,7 +1585,7 @@ export class McpServerManager {
|
|
|
1405
1585
|
cursor = result.nextCursor;
|
|
1406
1586
|
} while (cursor);
|
|
1407
1587
|
|
|
1408
|
-
return allResources;
|
|
1588
|
+
return { resources: allResources, failed: false };
|
|
1409
1589
|
} catch (error) {
|
|
1410
1590
|
if (requestOptions?.signal?.aborted) {
|
|
1411
1591
|
throwIfAborted(requestOptions.signal);
|
|
@@ -1413,7 +1593,7 @@ export class McpServerManager {
|
|
|
1413
1593
|
if (isUnauthorizedHttpError(error)) throw error;
|
|
1414
1594
|
if (strict) throw error;
|
|
1415
1595
|
// The server advertises resources but the listing failed
|
|
1416
|
-
return [];
|
|
1596
|
+
return { resources: [], failed: true };
|
|
1417
1597
|
}
|
|
1418
1598
|
}
|
|
1419
1599
|
|
|
@@ -1634,10 +1814,17 @@ export class McpServerManager {
|
|
|
1634
1814
|
/**
|
|
1635
1815
|
* Resolve environment variables with interpolation.
|
|
1636
1816
|
*/
|
|
1637
|
-
function resolveEnv(
|
|
1817
|
+
function resolveEnv(
|
|
1818
|
+
env: Record<string, string> | undefined,
|
|
1819
|
+
serverName: string,
|
|
1820
|
+
literalEnv = false,
|
|
1821
|
+
inheritEnv = true,
|
|
1822
|
+
): Record<string, string> {
|
|
1638
1823
|
const resolved: Record<string, string> = {};
|
|
1639
|
-
|
|
1640
|
-
|
|
1824
|
+
if (inheritEnv) {
|
|
1825
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
1826
|
+
if (value !== undefined) resolved[key] = value;
|
|
1827
|
+
}
|
|
1641
1828
|
}
|
|
1642
1829
|
if (literalEnv) return env ? { ...resolved, ...env } : resolved;
|
|
1643
1830
|
|