pi-mcp-adapter 2.5.3 → 2.6.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/CHANGELOG.md +24 -0
- package/README.md +11 -5
- package/banner.png +0 -0
- package/commands.ts +109 -55
- package/config.ts +12 -12
- package/consent-manager.ts +2 -2
- package/direct-tools.ts +21 -15
- package/host-html-template.ts +1 -1
- package/index.ts +22 -15
- package/init.ts +15 -15
- package/lifecycle.ts +3 -3
- package/mcp-auth-flow.ts +14 -5
- package/mcp-auth.ts +25 -5
- package/mcp-callback-server.ts +1 -1
- package/mcp-oauth-provider.ts +6 -7
- package/mcp-panel.ts +114 -38
- package/mcp-setup-panel.ts +4 -4
- package/metadata-cache.ts +6 -6
- package/npx-resolver.ts +1 -1
- package/oauth-handler.ts +1 -1
- package/onboarding-state.ts +1 -1
- package/package.json +7 -3
- package/proxy-modes.ts +11 -11
- package/sampling-handler.ts +38 -16
- package/server-manager.ts +9 -10
- package/state.ts +8 -8
- package/tool-metadata.ts +5 -5
- package/tool-registrar.ts +1 -1
- package/tool-result-renderer.ts +65 -0
- package/types.ts +11 -4
- package/ui-resource-handler.ts +4 -4
- package/ui-server.ts +6 -6
- package/ui-session.ts +5 -5
- package/utils.ts +9 -2
package/sampling-handler.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { complete, type AssistantMessage, type Message, type Model, type TextContent } from "@
|
|
2
|
-
import { truncateAtWord } from "./utils.
|
|
3
|
-
import type { ExtensionUIContext, ModelRegistry } from "@
|
|
1
|
+
import { complete, type Api, type AssistantMessage, type Message, type Model, type TextContent } from "@earendil-works/pi-ai";
|
|
2
|
+
import { truncateAtWord } from "./utils.ts";
|
|
3
|
+
import type { ExtensionUIContext, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
5
|
import {
|
|
6
6
|
CreateMessageRequestSchema,
|
|
7
7
|
type CreateMessageRequest,
|
|
8
8
|
type CreateMessageResult,
|
|
9
|
+
type ModelPreferences,
|
|
9
10
|
type SamplingMessage,
|
|
10
11
|
type SamplingMessageContentBlock,
|
|
11
12
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -15,7 +16,7 @@ export interface SamplingHandlerOptions {
|
|
|
15
16
|
autoApprove: boolean;
|
|
16
17
|
ui?: ExtensionUIContext;
|
|
17
18
|
modelRegistry: ModelRegistry;
|
|
18
|
-
getCurrentModel: () => Model<
|
|
19
|
+
getCurrentModel: () => Model<Api> | undefined;
|
|
19
20
|
getSignal: () => AbortSignal | undefined;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -50,7 +51,7 @@ export async function handleSamplingRequest(
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
const messages = params.messages.map(convertSamplingMessage);
|
|
53
|
-
const { model, apiKey, headers } = await resolveSamplingModel(options);
|
|
54
|
+
const { model, apiKey, headers } = await resolveSamplingModel(options, params.modelPreferences);
|
|
54
55
|
await confirmSampling(
|
|
55
56
|
options,
|
|
56
57
|
"Approve MCP sampling request",
|
|
@@ -114,28 +115,43 @@ function messageText(message: Message): string {
|
|
|
114
115
|
}).join("\n");
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
async function resolveSamplingModel(
|
|
118
|
-
|
|
118
|
+
async function resolveSamplingModel(
|
|
119
|
+
options: SamplingHandlerOptions,
|
|
120
|
+
modelPreferences: ModelPreferences | undefined,
|
|
121
|
+
): Promise<{
|
|
122
|
+
model: Model<Api>;
|
|
119
123
|
apiKey?: string;
|
|
120
124
|
headers?: Record<string, string>;
|
|
121
125
|
}> {
|
|
122
|
-
const candidates: Model<
|
|
123
|
-
const
|
|
124
|
-
if (currentModel) candidates.push(currentModel);
|
|
126
|
+
const candidates: Model<Api>[] = [];
|
|
127
|
+
const availableModels = options.modelRegistry.getAvailable();
|
|
125
128
|
|
|
126
|
-
for (const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
for (const hint of modelPreferences?.hints ?? []) {
|
|
130
|
+
const normalizedHint = hint.name?.trim().toLowerCase();
|
|
131
|
+
if (!normalizedHint) continue;
|
|
132
|
+
for (const model of availableModels) {
|
|
133
|
+
const searchableNames = [`${model.provider}/${model.id}`, model.id, model.name];
|
|
134
|
+
if (searchableNames.some((name) => name.toLowerCase().includes(normalizedHint))) {
|
|
135
|
+
addSamplingCandidate(candidates, model);
|
|
136
|
+
}
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
const currentModel = options.getCurrentModel();
|
|
141
|
+
if (currentModel) addSamplingCandidate(candidates, currentModel);
|
|
142
|
+
|
|
143
|
+
for (const model of availableModels) {
|
|
144
|
+
addSamplingCandidate(candidates, model);
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
const errors: string[] = [];
|
|
133
148
|
for (const model of candidates) {
|
|
134
149
|
const auth = await options.modelRegistry.getApiKeyAndHeaders(model);
|
|
135
|
-
if (auth.ok) {
|
|
136
|
-
|
|
150
|
+
if (auth.ok === false) {
|
|
151
|
+
errors.push(`${model.provider}/${model.id}: ${auth.error}`);
|
|
152
|
+
continue;
|
|
137
153
|
}
|
|
138
|
-
|
|
154
|
+
return { model, apiKey: auth.apiKey, headers: auth.headers };
|
|
139
155
|
}
|
|
140
156
|
|
|
141
157
|
if (errors.length > 0) {
|
|
@@ -144,6 +160,12 @@ async function resolveSamplingModel(options: SamplingHandlerOptions): Promise<{
|
|
|
144
160
|
throw new Error("No Pi model is available for MCP sampling");
|
|
145
161
|
}
|
|
146
162
|
|
|
163
|
+
function addSamplingCandidate(candidates: Model<Api>[], model: Model<Api>): void {
|
|
164
|
+
if (!candidates.some((candidate) => candidate.provider === model.provider && candidate.id === model.id)) {
|
|
165
|
+
candidates.push(model);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
147
169
|
async function confirmSampling(options: SamplingHandlerOptions, title: string, message: string): Promise<void> {
|
|
148
170
|
if (options.autoApprove) return;
|
|
149
171
|
if (!options.ui) {
|
package/server-manager.ts
CHANGED
|
@@ -10,14 +10,14 @@ import type {
|
|
|
10
10
|
ServerDefinition,
|
|
11
11
|
ServerStreamResultPatchNotification,
|
|
12
12
|
Transport,
|
|
13
|
-
} from "./types.
|
|
14
|
-
import { serverStreamResultPatchNotificationSchema } from "./types.
|
|
15
|
-
import { resolveNpxBinary } from "./npx-resolver.
|
|
16
|
-
import { logger } from "./logger.
|
|
17
|
-
import { McpOAuthProvider } from "./mcp-oauth-provider.
|
|
18
|
-
import { supportsOAuth } from "./mcp-auth-flow.
|
|
19
|
-
import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.
|
|
20
|
-
import { interpolateEnvRecord, resolveConfigPath } from "./utils.
|
|
13
|
+
} from "./types.ts";
|
|
14
|
+
import { serverStreamResultPatchNotificationSchema } from "./types.ts";
|
|
15
|
+
import { resolveNpxBinary } from "./npx-resolver.ts";
|
|
16
|
+
import { logger } from "./logger.ts";
|
|
17
|
+
import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
|
|
18
|
+
import { supportsOAuth } from "./mcp-auth-flow.ts";
|
|
19
|
+
import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.ts";
|
|
20
|
+
import { interpolateEnvRecord, resolveBearerToken, resolveConfigPath } from "./utils.ts";
|
|
21
21
|
|
|
22
22
|
interface ServerConnection {
|
|
23
23
|
client: Client;
|
|
@@ -170,8 +170,7 @@ export class McpServerManager {
|
|
|
170
170
|
|
|
171
171
|
// For bearer auth, add the token to headers BEFORE creating requestInit
|
|
172
172
|
if (definition.auth === "bearer") {
|
|
173
|
-
const token = definition
|
|
174
|
-
?? (definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined);
|
|
173
|
+
const token = resolveBearerToken(definition);
|
|
175
174
|
if (token) {
|
|
176
175
|
headers["Authorization"] = `Bearer ${token}`;
|
|
177
176
|
}
|
package/state.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@
|
|
2
|
-
import type { ConsentManager } from "./consent-manager.
|
|
3
|
-
import type { McpLifecycleManager } from "./lifecycle.
|
|
4
|
-
import type { McpServerManager } from "./server-manager.
|
|
5
|
-
import type { ToolMetadata, McpConfig, UiSessionMessages, UiStreamSummary } from "./types.
|
|
6
|
-
import type { UiResourceHandler } from "./ui-resource-handler.
|
|
7
|
-
import type { UiServerHandle } from "./ui-server.
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ConsentManager } from "./consent-manager.ts";
|
|
3
|
+
import type { McpLifecycleManager } from "./lifecycle.ts";
|
|
4
|
+
import type { McpServerManager } from "./server-manager.ts";
|
|
5
|
+
import type { ToolMetadata, McpConfig, UiSessionMessages, UiStreamSummary } from "./types.ts";
|
|
6
|
+
import type { UiResourceHandler } from "./ui-resource-handler.ts";
|
|
7
|
+
import type { UiServerHandle } from "./ui-server.ts";
|
|
8
8
|
|
|
9
9
|
export interface CompletedUiSession {
|
|
10
10
|
serverName: string;
|
|
@@ -18,7 +18,7 @@ export interface CompletedUiSession {
|
|
|
18
18
|
export type SendMessageFn = (
|
|
19
19
|
message: {
|
|
20
20
|
customType: string;
|
|
21
|
-
content: Array<{ type:
|
|
21
|
+
content: Array<{ type: "text"; text: string }>;
|
|
22
22
|
display?: string;
|
|
23
23
|
details?: unknown;
|
|
24
24
|
},
|
package/tool-metadata.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
2
|
-
import type { McpExtensionState } from "./state.
|
|
3
|
-
import type { ToolMetadata, McpTool, McpResource, ServerEntry } from "./types.
|
|
4
|
-
import { formatToolName, isToolExcluded } from "./types.
|
|
5
|
-
import { resourceNameToToolName } from "./resource-tools.
|
|
6
|
-
import { extractToolUiStreamMode } from "./utils.
|
|
2
|
+
import type { McpExtensionState } from "./state.ts";
|
|
3
|
+
import type { ToolMetadata, McpTool, McpResource, ServerEntry } from "./types.ts";
|
|
4
|
+
import { formatToolName, isToolExcluded } from "./types.ts";
|
|
5
|
+
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
6
|
+
import { extractToolUiStreamMode } from "./utils.ts";
|
|
7
7
|
|
|
8
8
|
export function buildToolMetadata(
|
|
9
9
|
tools: McpTool[],
|
package/tool-registrar.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// NOTE: Tools are NOT registered with Pi - only the unified `mcp` proxy tool is registered.
|
|
3
3
|
// This keeps the LLM context small (1 tool instead of 100s).
|
|
4
4
|
|
|
5
|
-
import type { McpContent, ContentBlock } from "./types.
|
|
5
|
+
import type { McpContent, ContentBlock } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Transform MCP content types to Pi content blocks.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AgentToolResult, ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
3
|
+
|
|
4
|
+
type McpToolResultDetails = Record<string, unknown> & { error?: unknown };
|
|
5
|
+
type McpToolContentBlock = AgentToolResult<McpToolResultDetails>["content"][number];
|
|
6
|
+
|
|
7
|
+
interface RenderTheme {
|
|
8
|
+
fg: (name: string, text: string) => string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface McpToolRenderContext {
|
|
12
|
+
isError: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface McpToolResultDisplay {
|
|
16
|
+
lines: string[];
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function blockToLines(block: McpToolContentBlock): string[] {
|
|
21
|
+
if (block.type === "text") {
|
|
22
|
+
return block.text.split("\n");
|
|
23
|
+
}
|
|
24
|
+
return [`[image: ${block.mimeType}]`];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatMcpToolResultLines(
|
|
28
|
+
result: Pick<AgentToolResult<McpToolResultDetails>, "content">,
|
|
29
|
+
expanded: boolean,
|
|
30
|
+
maxCollapsedLines = 3,
|
|
31
|
+
): McpToolResultDisplay {
|
|
32
|
+
const allLines = result.content.flatMap(blockToLines);
|
|
33
|
+
const lines = allLines.length > 0 ? allLines : ["(empty result)"];
|
|
34
|
+
|
|
35
|
+
if (expanded || lines.length <= maxCollapsedLines) {
|
|
36
|
+
return { lines, truncated: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
lines: [...lines.slice(0, maxCollapsedLines), "…"],
|
|
41
|
+
truncated: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function renderMcpToolResult(
|
|
46
|
+
result: AgentToolResult<McpToolResultDetails>,
|
|
47
|
+
options: ToolRenderResultOptions,
|
|
48
|
+
theme: RenderTheme,
|
|
49
|
+
context?: McpToolRenderContext,
|
|
50
|
+
) {
|
|
51
|
+
if (options.isPartial) {
|
|
52
|
+
return new Text(theme.fg("warning", "Running MCP tool..."), 0, 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const hasErrorDetails = Boolean(result.details.error);
|
|
56
|
+
const display = formatMcpToolResultLines(result, options.expanded || context?.isError === true || hasErrorDetails);
|
|
57
|
+
const output = display.lines
|
|
58
|
+
.map((line) => line === "…" ? theme.fg("muted", line) : theme.fg("toolOutput", line))
|
|
59
|
+
.join("\n");
|
|
60
|
+
const hint = display.truncated && !options.expanded
|
|
61
|
+
? `\n${theme.fg("muted", "(Ctrl+O to expand)")}`
|
|
62
|
+
: "";
|
|
63
|
+
|
|
64
|
+
return new Text(`${output}${hint}`, 0, 0);
|
|
65
|
+
}
|
package/types.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import type { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import type { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
4
|
import type { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
|
-
import type { TextContent, ImageContent } from "@
|
|
6
|
-
import type { UiStreamMode } from "./ui-stream-types.
|
|
5
|
+
import type { TextContent, ImageContent } from "@earendil-works/pi-ai";
|
|
6
|
+
import type { UiStreamMode } from "./ui-stream-types.ts";
|
|
7
7
|
|
|
8
8
|
// Transport type (stdio + HTTP)
|
|
9
9
|
export type Transport =
|
|
@@ -137,7 +137,7 @@ export {
|
|
|
137
137
|
type UiStreamResultPatchNotification,
|
|
138
138
|
type ServerStreamResultPatchNotification,
|
|
139
139
|
type UiStreamSummary,
|
|
140
|
-
} from "./ui-stream-types.
|
|
140
|
+
} from "./ui-stream-types.ts";
|
|
141
141
|
|
|
142
142
|
export interface UiMessageParams {
|
|
143
143
|
role?: string;
|
|
@@ -365,10 +365,17 @@ export interface ServerProvenance {
|
|
|
365
365
|
importKind?: string;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
+
export interface McpAuthResult {
|
|
369
|
+
ok: boolean;
|
|
370
|
+
message?: string;
|
|
371
|
+
}
|
|
372
|
+
|
|
368
373
|
export interface McpPanelCallbacks {
|
|
369
374
|
reconnect: (serverName: string) => Promise<boolean>;
|
|
375
|
+
canAuthenticate: (serverName: string) => boolean;
|
|
376
|
+
authenticate: (serverName: string) => Promise<McpAuthResult>;
|
|
370
377
|
getConnectionStatus: (serverName: string) => "connected" | "idle" | "failed" | "needs-auth";
|
|
371
|
-
refreshCacheAfterReconnect: (serverName: string) => import("./metadata-cache.
|
|
378
|
+
refreshCacheAfterReconnect: (serverName: string) => import("./metadata-cache.ts").ServerCacheEntry | null;
|
|
372
379
|
}
|
|
373
380
|
|
|
374
381
|
export interface McpPanelResult {
|
package/ui-resource-handler.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
2
2
|
import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
import { ResourceFetchError, ResourceParseError } from "./errors.
|
|
4
|
-
import { logger } from "./logger.
|
|
5
|
-
import type { McpServerManager } from "./server-manager.
|
|
6
|
-
import type { UiResourceContent, UiResourceMeta } from "./types.
|
|
3
|
+
import { ResourceFetchError, ResourceParseError } from "./errors.ts";
|
|
4
|
+
import { logger } from "./logger.ts";
|
|
5
|
+
import type { McpServerManager } from "./server-manager.ts";
|
|
6
|
+
import type { UiResourceContent, UiResourceMeta } from "./types.ts";
|
|
7
7
|
|
|
8
8
|
interface ResourceContentRecord {
|
|
9
9
|
uri?: string;
|
package/ui-server.ts
CHANGED
|
@@ -7,11 +7,11 @@ import type {
|
|
|
7
7
|
CallToolRequest,
|
|
8
8
|
CallToolResult,
|
|
9
9
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
-
import type { ConsentManager } from "./consent-manager.
|
|
11
|
-
import { ServerError, wrapError } from "./errors.
|
|
12
|
-
import { buildHostHtmlTemplate, buildCspMetaContent, applyCspMeta } from "./host-html-template.
|
|
13
|
-
import { logger } from "./logger.
|
|
14
|
-
import type { McpServerManager } from "./server-manager.
|
|
10
|
+
import type { ConsentManager } from "./consent-manager.ts";
|
|
11
|
+
import { ServerError, wrapError } from "./errors.ts";
|
|
12
|
+
import { buildHostHtmlTemplate, buildCspMetaContent, applyCspMeta } from "./host-html-template.ts";
|
|
13
|
+
import { logger } from "./logger.ts";
|
|
14
|
+
import type { McpServerManager } from "./server-manager.ts";
|
|
15
15
|
import {
|
|
16
16
|
extractUiPromptText,
|
|
17
17
|
getVisualizationStreamEnvelope,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
type UiResourceContent,
|
|
28
28
|
type UiSessionMessages,
|
|
29
29
|
type UiStreamSummary,
|
|
30
|
-
} from "./types.
|
|
30
|
+
} from "./types.ts";
|
|
31
31
|
|
|
32
32
|
const MAX_BODY_SIZE = 2 * 1024 * 1024;
|
|
33
33
|
const ABANDONED_GRACE_MS = 60_000;
|
package/ui-session.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
import type { McpExtensionState } from "./state.
|
|
3
|
+
import type { McpExtensionState } from "./state.ts";
|
|
4
4
|
import {
|
|
5
5
|
extractUiPromptText,
|
|
6
6
|
UI_STREAM_HOST_CONTEXT_KEY,
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
type UiMessageParams,
|
|
11
11
|
type UiModelContextParams,
|
|
12
12
|
type UiStreamMode,
|
|
13
|
-
} from "./types.
|
|
14
|
-
import { logger } from "./logger.
|
|
15
|
-
import { startUiServer, type UiServerHandle } from "./ui-server.
|
|
16
|
-
import { isGlimpseAvailable, openGlimpseWindow } from "./glimpse-ui.
|
|
13
|
+
} from "./types.ts";
|
|
14
|
+
import { logger } from "./logger.ts";
|
|
15
|
+
import { startUiServer, type UiServerHandle } from "./ui-server.ts";
|
|
16
|
+
import { isGlimpseAvailable, openGlimpseWindow } from "./glimpse-ui.ts";
|
|
17
17
|
|
|
18
18
|
let activeGlimpseWindow: { close(): void } | null = null;
|
|
19
19
|
|
package/utils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { homedir, platform } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import type { McpConfig } from "./types.
|
|
4
|
+
import type { McpConfig, ServerEntry } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
async function execOpen(pi: ExtensionAPI, target: string, browser?: string) {
|
|
7
7
|
const os = platform();
|
|
@@ -86,6 +86,13 @@ export function resolveConfigPath(value: string | undefined): string | undefined
|
|
|
86
86
|
return resolved;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export function resolveBearerToken(definition: Pick<ServerEntry, "bearerToken" | "bearerTokenEnv">): string | undefined {
|
|
90
|
+
if (definition.bearerToken !== undefined) {
|
|
91
|
+
return interpolateEnvVars(definition.bearerToken);
|
|
92
|
+
}
|
|
93
|
+
return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
export function truncateAtWord(text: string, target: number): string {
|
|
90
97
|
if (!text || text.length <= target) return text;
|
|
91
98
|
|