@ai-ecoverse/slicc-agent 3.52.1 → 4.0.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 +17 -13
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +2 -2
- package/dist/main.js +95 -22
- package/dist/main.js.map +2 -2
- package/dist/mcp/accounts.js +18 -14
- package/dist/mcp/accounts.js.map +2 -2
- package/dist/mcp/connection.js +0 -2
- package/dist/mcp/connection.js.map +2 -2
- package/dist/mcp/credentials.js +6 -15
- package/dist/mcp/credentials.js.map +2 -2
- package/dist/mcp/index.js +0 -1
- package/dist/mcp/index.js.map +2 -2
- package/dist/mcp/oauth.js +15 -20
- package/dist/mcp/oauth.js.map +2 -2
- package/dist/mcp/runtime.js +36 -14
- package/dist/mcp/runtime.js.map +2 -2
- package/dist/mcp/tools.js +4 -6
- package/dist/mcp/tools.js.map +2 -2
- package/dist/models/index.js +7 -4
- package/dist/models/index.js.map +2 -2
- package/dist/monitor/index.js +5 -10
- package/dist/monitor/index.js.map +2 -2
- package/dist/monitor/redact.js +4 -5
- package/dist/monitor/redact.js.map +2 -2
- package/dist/secrets/credentials.js +32 -0
- package/dist/secrets/credentials.js.map +7 -0
- package/dist/secrets/endpoints.js +111 -0
- package/dist/secrets/endpoints.js.map +7 -0
- package/dist/secrets/fetch.js +160 -0
- package/dist/secrets/fetch.js.map +7 -0
- package/dist/secrets/records.js +35 -0
- package/dist/secrets/records.js.map +7 -0
- package/dist/secrets/store.js +50 -0
- package/dist/secrets/store.js.map +7 -0
- package/dist/secrets/vault.js +58 -13
- package/dist/secrets/vault.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/settings.js +85 -23
- package/dist/settings.js.map +2 -2
- package/dist/spectrum/settings-port.js +24 -6
- package/dist/spectrum/settings-port.js.map +2 -2
- package/dist/types/host.d.ts +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/main.d.ts +7 -2
- package/dist/types/mcp/accounts.d.ts +10 -4
- package/dist/types/mcp/connection.d.ts +0 -2
- package/dist/types/mcp/credentials.d.ts +6 -4
- package/dist/types/mcp/oauth.d.ts +3 -2
- package/dist/types/mcp/runtime.d.ts +18 -4
- package/dist/types/models/index.d.ts +2 -0
- package/dist/types/monitor/index.d.ts +0 -1
- package/dist/types/monitor/redact.d.ts +2 -2
- package/dist/types/secrets/credentials.d.ts +5 -0
- package/dist/types/secrets/endpoints.d.ts +27 -0
- package/dist/types/secrets/fetch.d.ts +13 -0
- package/dist/types/secrets/records.d.ts +13 -0
- package/dist/types/secrets/store.d.ts +7 -0
- package/dist/types/secrets/vault.d.ts +31 -1
- package/dist/types/services.d.ts +8 -1
- package/dist/types/settings.d.ts +17 -2
- package/dist/types/spectrum/settings-port.d.ts +6 -4
- package/package.json +5 -3
- package/dist/credentials.js +0 -100
- package/dist/credentials.js.map +0 -7
- package/dist/types/credentials.d.ts +0 -12
package/dist/mcp/oauth.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/oauth.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CredentialStore } from '@earendil-works/pi-ai';\nimport type { AuthProvider, McpFetch, UnauthorizedContext } from '@earendil-works/pi-mcp';\nimport {\n authorizeMcp,\n discoverOAuthServerInfo,\n McpOAuthAuthorizationRequiredError,\n type McpOAuthState,\n type OAuthChallenge,\n parseWwwAuthenticate,\n registerClient,\n selectResource,\n stepUpScope,\n} from '@earendil-works/pi-mcp/oauth';\nimport type { McpSignIn } from '../services.ts';\nimport type { McpServerConfig } from './config.ts';\nimport { credentialKey } from './credentials.ts';\n\ntype Provider = Parameters<typeof authorizeMcp>[0];\ntype Tokens = Awaited<ReturnType<Provider['tokens']>>;\ntype Client = Awaited<ReturnType<Provider['clientInformation']>>;\ntype Discovery = NonNullable<McpOAuthState['discovery']>;\n\nexport const CLIENT_NAME = 'SLICC';\n\nexport function sentence(text: string): string {\n const trimmed = text.trim();\n const lowered = /^[A-Z][a-z]/.test(trimmed)\n ? `${trimmed[0]?.toLowerCase()}${trimmed.slice(1)}`\n : trimmed;\n return /[.!?]$/.test(lowered) ? lowered : `${lowered}.`;\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nexport function secureUrl(url: URL): boolean {\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n}\n\nexport function signInRequired(cause?: unknown): McpOAuthAuthorizationRequiredError {\n const error = new McpOAuthAuthorizationRequiredError();\n if (cause !== undefined) error.cause = cause;\n return error;\n}\n\nexport function authProvider(provider: StoredOAuth, server: OAuthServer): AuthProvider {\n const configured = server.config.oauth?.authServerMetadataUrl;\n let inFlight: Promise<void> | undefined;\n const authorize = async (\n context: UnauthorizedContext,\n step: boolean,\n challenge: OAuthChallenge\n ) => {\n const granted = step ? await provider.tokens() : undefined;\n try {\n await authorizeMcp(provider, {\n serverUrl: context.serverUrl,\n ...(challenge.resourceMetadataUrl\n ? { resourceMetadataUrl: challenge.resourceMetadataUrl }\n : {}),\n ...(step\n ? { scope: stepUpScope(granted?.scope, challenge.scope), skipRefresh: true }\n : challenge.scope\n ? { scope: challenge.scope }\n : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch: context.fetch,\n });\n } catch (error) {\n throw error instanceof McpOAuthAuthorizationRequiredError ? error : signInRequired(error);\n }\n };\n return {\n token: async () => (await provider.tokens())?.access_token,\n async onUnauthorized(context) {\n const challenge = parseWwwAuthenticate(context.response.headers.get('www-authenticate'));\n const step = challenge.error === 'insufficient_scope';\n if (!step && !inFlight && context.token !== undefined) {\n const current = (await provider.tokens())?.access_token;\n if (current !== undefined && current !== context.token) return;\n }\n inFlight ??= authorize(context, step, challenge).finally(() => {\n inFlight = undefined;\n });\n await inFlight;\n },\n };\n}\n\nconst bytesToBase64Url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nconst random = (length: number) => crypto.getRandomValues(new Uint8Array(length));\n\nexport async function pkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = bytesToBase64Url(random(32));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return { verifier, challenge: bytesToBase64Url(new Uint8Array(digest)) };\n}\n\nexport function randomState(): string {\n return [...random(32)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport interface OAuthServer {\n name: string;\n config: McpServerConfig;\n}\n\nexport class StoredOAuth implements Provider {\n readonly redirectUrl: string;\n readonly clientMetadata: Provider['clientMetadata'];\n readonly #credentials: CredentialStore;\n readonly #server: OAuthServer;\n #writes: Promise<unknown> = Promise.resolve();\n\n constructor(server: OAuthServer, credentials: CredentialStore, redirectUrl: string) {\n this.#server = server;\n this.#credentials = credentials;\n this.redirectUrl = redirectUrl;\n this.clientMetadata = {\n client_name: server.config.oauth?.clientName ?? CLIENT_NAME,\n redirect_uris: [redirectUrl],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n };\n }\n\n get serverUrl(): string {\n return String(new URL(this.#server.config.url));\n }\n\n async #key(): Promise<string> {\n return credentialKey(this.#server.name, this.#server.config.url);\n }\n\n async load(): Promise<McpOAuthState> {\n await this.#writes;\n const stored = await this.#credentials.read(await this.#key());\n const state =\n stored?.type === 'oauth' ? (stored.state as McpOAuthState | undefined) : undefined;\n return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };\n }\n\n async update(change: (state: McpOAuthState) => McpOAuthState): Promise<void> {\n const write = this.#writes.then(async () => {\n const key = await this.#key();\n await this.#credentials.modify(key, async (current) => {\n const before =\n current?.type === 'oauth' ? (current.state as McpOAuthState | undefined) : undefined;\n const next = change(\n before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl }\n );\n return {\n type: 'oauth',\n access: next.tokens?.access_token ?? '',\n refresh: next.tokens?.refresh_token ?? '',\n expires: next.tokensExpireAt ?? 0,\n state: next,\n };\n });\n });\n this.#writes = write.catch(() => undefined);\n await write;\n }\n\n async signedIn(): Promise<boolean> {\n return (await this.load()).tokens !== undefined;\n }\n\n state(): string {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async clientInformation(): Promise<Client> {\n const configured = this.#server.config.oauth?.clientId;\n return configured ? { client_id: configured } : (await this.load()).clientInformation;\n }\n\n async saveClientInformation(information: NonNullable<Client>): Promise<void> {\n if (this.#server.config.oauth?.clientId) return;\n await this.update((state) => ({ ...state, clientInformation: information }));\n }\n\n async tokens(): Promise<Tokens> {\n return (await this.load()).tokens;\n }\n\n async saveTokens(tokens: NonNullable<Tokens>): Promise<void> {\n const expires =\n tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;\n await this.update((state) => {\n const next: McpOAuthState = { ...state, tokens };\n if (expires === undefined) delete next.tokensExpireAt;\n else next.tokensExpireAt = expires;\n return next;\n });\n }\n\n redirectToAuthorization(): void {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.update((state) => ({ ...state, codeVerifier: verifier }));\n }\n\n async codeVerifier(): Promise<string> {\n const verifier = (await this.load()).codeVerifier;\n if (!verifier) throw new Error('No OAuth PKCE code verifier is stored');\n return verifier;\n }\n\n async invalidateCredentials(\n kind: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'\n ): Promise<void> {\n await this.update((state) => {\n const next = { ...state };\n if (kind === 'all' || kind === 'client') delete next.clientInformation;\n if (kind === 'all' || kind === 'tokens') {\n delete next.tokens;\n delete next.tokensExpireAt;\n }\n if (kind === 'all' || kind === 'verifier') delete next.codeVerifier;\n if (kind === 'all' || kind === 'discovery') delete next.discovery;\n if (kind === 'all') delete next.oauthState;\n return next;\n });\n }\n\n async saveDiscoveryState(discovery: Discovery): Promise<void> {\n await this.update((state) => ({ ...state, discovery }));\n }\n\n async discoveryState(): Promise<Discovery | undefined> {\n return (await this.load()).discovery;\n }\n\n async forget(): Promise<void> {\n await this.#writes;\n await this.#credentials.delete(await this.#key());\n }\n}\n\nexport type { McpSignIn } from '../services.ts';\n\nexport async function beginSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n options: { fetch: McpFetch; resourceMetadataUrl?: URL }\n): Promise<McpSignIn> {\n const configured = server.config.oauth?.authServerMetadataUrl;\n const info = await discoverOAuthServerInfo(server.config.url, {\n fetch: options.fetch,\n ...(options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n });\n if (!configured) await provider.saveDiscoveryState(info);\n const metadata = info.authorizationServerMetadata;\n if (metadata && !metadata.response_types_supported.includes('code'))\n throw new Error(\"The authorization server doesn't support authorization codes.\");\n if (\n metadata?.code_challenge_methods_supported &&\n !metadata.code_challenge_methods_supported.includes('S256')\n )\n throw new Error(\"The authorization server doesn't support PKCE S256.\");\n const scope =\n server.config.oauth?.scope || info.resourceMetadata?.scopes_supported?.join(' ') || undefined;\n let client = await provider.clientInformation();\n if (!client) {\n client = await registerClient(info.authorizationServerUrl, {\n ...(metadata ? { metadata } : {}),\n clientMetadata: provider.clientMetadata,\n ...(scope ? { scope } : {}),\n fetch: options.fetch,\n });\n await provider.saveClientInformation(client);\n }\n const { verifier, challenge } = await pkce();\n const state = randomState();\n await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));\n const url = new URL(\n metadata?.authorization_endpoint ?? new URL('/authorize', info.authorizationServerUrl)\n );\n if (!secureUrl(url)) throw new Error(\"The authorization server's sign-in address isn't https.\");\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', client.client_id);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('redirect_uri', provider.redirectUrl);\n url.searchParams.set('state', state);\n if (scope) url.searchParams.set('scope', scope);\n if (scope?.split(/\\s+/).includes('offline_access')) url.searchParams.set('prompt', 'consent');\n const resource = selectResource(server.config.url, info.resourceMetadata);\n if (resource) url.searchParams.set('resource', resource);\n return { url: url.href, state };\n}\n\nexport interface Callback {\n code?: string;\n state?: string;\n iss?: string;\n error?: string;\n error_description?: string;\n}\n\nexport async function finishSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n answer: string,\n fetch: McpFetch\n): Promise<void> {\n let callback: Callback;\n try {\n callback = JSON.parse(answer) as Callback;\n } catch {\n throw new Error(\"The sign-in answer isn't a callback.\");\n }\n const expected = (await provider.load()).oauthState;\n if (!expected || callback.state !== expected)\n throw new Error(\"The sign-in answer doesn't match this sign-in. Try again.\");\n if (callback.error)\n throw new Error(\n `Sign-in failed: ${sentence(callback.error_description?.trim() || callback.error)}`\n );\n if (!callback.code) throw new Error('The sign-in answer has no authorization code.');\n const configured = server.config.oauth?.authServerMetadataUrl;\n await authorizeMcp(provider, {\n serverUrl: server.config.url,\n authorizationCode: callback.code,\n ...(callback.iss ? { iss: callback.iss } : {}),\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch,\n });\n await provider.update((state) => {\n const next = { ...state };\n delete next.oauthState;\n delete next.codeVerifier;\n return next;\n });\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { AuthProvider, McpFetch, UnauthorizedContext } from '@earendil-works/pi-mcp';\nimport {\n authorizeMcp,\n discoverOAuthServerInfo,\n McpOAuthAuthorizationRequiredError,\n type McpOAuthState,\n type OAuthChallenge,\n parseWwwAuthenticate,\n registerClient,\n selectResource,\n stepUpScope,\n} from '@earendil-works/pi-mcp/oauth';\nimport type { PlainStore } from '../secrets/store.ts';\nimport type { McpSignIn } from '../services.ts';\nimport type { McpServerConfig } from './config.ts';\nimport { credentialKey } from './credentials.ts';\n\ntype Provider = Parameters<typeof authorizeMcp>[0];\ntype Tokens = Awaited<ReturnType<Provider['tokens']>>;\ntype Client = Awaited<ReturnType<Provider['clientInformation']>>;\ntype Discovery = NonNullable<McpOAuthState['discovery']>;\n\nexport const CLIENT_NAME = 'SLICC';\n\nexport function sentence(text: string): string {\n const trimmed = text.trim();\n const lowered = /^[A-Z][a-z]/.test(trimmed)\n ? `${trimmed[0]?.toLowerCase()}${trimmed.slice(1)}`\n : trimmed;\n return /[.!?]$/.test(lowered) ? lowered : `${lowered}.`;\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nexport function secureUrl(url: URL): boolean {\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n}\n\nexport function signInRequired(cause?: unknown): McpOAuthAuthorizationRequiredError {\n const error = new McpOAuthAuthorizationRequiredError();\n if (cause !== undefined) error.cause = cause;\n return error;\n}\n\nexport function authProvider(provider: StoredOAuth, server: OAuthServer): AuthProvider {\n const configured = server.config.oauth?.authServerMetadataUrl;\n let inFlight: Promise<void> | undefined;\n const authorize = async (\n context: UnauthorizedContext,\n step: boolean,\n challenge: OAuthChallenge\n ) => {\n const granted = step ? await provider.tokens() : undefined;\n try {\n await authorizeMcp(provider, {\n serverUrl: context.serverUrl,\n ...(challenge.resourceMetadataUrl\n ? { resourceMetadataUrl: challenge.resourceMetadataUrl }\n : {}),\n skipRefresh: true,\n ...(step\n ? { scope: stepUpScope(granted?.scope, challenge.scope) }\n : challenge.scope\n ? { scope: challenge.scope }\n : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch: context.fetch,\n });\n } catch (error) {\n throw error instanceof McpOAuthAuthorizationRequiredError ? error : signInRequired(error);\n }\n };\n return {\n token: async () => (await provider.tokens())?.access_token,\n async onUnauthorized(context) {\n const challenge = parseWwwAuthenticate(context.response.headers.get('www-authenticate'));\n const step = challenge.error === 'insufficient_scope';\n if (!step && !inFlight && context.token !== undefined) {\n const current = (await provider.tokens())?.access_token;\n if (current !== undefined && current !== context.token) return;\n }\n inFlight ??= authorize(context, step, challenge).finally(() => {\n inFlight = undefined;\n });\n await inFlight;\n },\n };\n}\n\nconst bytesToBase64Url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nconst random = (length: number) => crypto.getRandomValues(new Uint8Array(length));\n\nexport async function pkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = bytesToBase64Url(random(32));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return { verifier, challenge: bytesToBase64Url(new Uint8Array(digest)) };\n}\n\nexport function randomState(): string {\n return [...random(32)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport interface OAuthServer {\n name: string;\n config: McpServerConfig;\n}\n\nexport class StoredOAuth implements Provider {\n readonly redirectUrl: string;\n readonly clientMetadata: Provider['clientMetadata'];\n readonly #store: PlainStore;\n readonly #server: OAuthServer;\n #writes: Promise<unknown> = Promise.resolve();\n\n constructor(server: OAuthServer, store: PlainStore, redirectUrl: string) {\n this.#server = server;\n this.#store = store;\n this.redirectUrl = redirectUrl;\n this.clientMetadata = {\n client_name: server.config.oauth?.clientName ?? CLIENT_NAME,\n redirect_uris: [redirectUrl],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n };\n }\n\n get serverUrl(): string {\n return String(new URL(this.#server.config.url));\n }\n\n async #key(): Promise<string> {\n return credentialKey(this.#server.name, this.#server.config.url);\n }\n\n async load(): Promise<McpOAuthState> {\n await this.#writes;\n const state = (await this.#store.get(await this.#key())) as McpOAuthState | undefined;\n return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };\n }\n\n async update(change: (state: McpOAuthState) => McpOAuthState): Promise<void> {\n const write = this.#writes.then(async () => {\n const key = await this.#key();\n const before = (await this.#store.get(key)) as McpOAuthState | undefined;\n await this.#store.set(\n key,\n change(before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl })\n );\n });\n this.#writes = write.catch(() => undefined);\n await write;\n }\n\n async signedIn(): Promise<boolean> {\n return (await this.load()).tokens !== undefined;\n }\n\n state(): string {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async clientInformation(): Promise<Client> {\n const configured = this.#server.config.oauth?.clientId;\n return configured ? { client_id: configured } : (await this.load()).clientInformation;\n }\n\n async saveClientInformation(information: NonNullable<Client>): Promise<void> {\n if (this.#server.config.oauth?.clientId) return;\n await this.update((state) => ({ ...state, clientInformation: information }));\n }\n\n async tokens(): Promise<Tokens> {\n return (await this.load()).tokens;\n }\n\n async saveTokens(tokens: NonNullable<Tokens>): Promise<void> {\n const expires =\n tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;\n await this.update((state) => {\n const next: McpOAuthState = { ...state, tokens };\n if (expires === undefined) delete next.tokensExpireAt;\n else next.tokensExpireAt = expires;\n return next;\n });\n }\n\n redirectToAuthorization(): void {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.update((state) => ({ ...state, codeVerifier: verifier }));\n }\n\n async codeVerifier(): Promise<string> {\n const verifier = (await this.load()).codeVerifier;\n if (!verifier) throw new Error('No OAuth PKCE code verifier is stored');\n return verifier;\n }\n\n async invalidateCredentials(\n kind: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'\n ): Promise<void> {\n await this.update((state) => {\n const next = { ...state };\n if (kind === 'all' || kind === 'client') delete next.clientInformation;\n if (kind === 'all' || kind === 'tokens') {\n delete next.tokens;\n delete next.tokensExpireAt;\n }\n if (kind === 'all' || kind === 'verifier') delete next.codeVerifier;\n if (kind === 'all' || kind === 'discovery') delete next.discovery;\n if (kind === 'all') delete next.oauthState;\n return next;\n });\n }\n\n async saveDiscoveryState(discovery: Discovery): Promise<void> {\n await this.update((state) => ({ ...state, discovery }));\n }\n\n async discoveryState(): Promise<Discovery | undefined> {\n return (await this.load()).discovery;\n }\n\n async forget(): Promise<void> {\n await this.#writes;\n await this.#store.set(await this.#key(), undefined);\n }\n}\n\nexport type { McpSignIn } from '../services.ts';\n\nexport async function beginSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n options: { fetch: McpFetch; resourceMetadataUrl?: URL; register(tokenUrl: string): Promise<void> }\n): Promise<McpSignIn> {\n const configured = server.config.oauth?.authServerMetadataUrl;\n const info = await discoverOAuthServerInfo(server.config.url, {\n fetch: options.fetch,\n ...(options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n });\n if (!configured) await provider.saveDiscoveryState(info);\n const metadata = info.authorizationServerMetadata;\n if (metadata && !metadata.response_types_supported.includes('code'))\n throw new Error(\"The authorization server doesn't support authorization codes.\");\n if (\n metadata?.code_challenge_methods_supported &&\n !metadata.code_challenge_methods_supported.includes('S256')\n )\n throw new Error(\"The authorization server doesn't support PKCE S256.\");\n const scope =\n server.config.oauth?.scope || info.resourceMetadata?.scopes_supported?.join(' ') || undefined;\n let client = await provider.clientInformation();\n if (!client) {\n client = await registerClient(info.authorizationServerUrl, {\n ...(metadata ? { metadata } : {}),\n clientMetadata: provider.clientMetadata,\n ...(scope ? { scope } : {}),\n fetch: options.fetch,\n });\n await provider.saveClientInformation(client);\n }\n await options.register(\n new URL(metadata?.token_endpoint ?? new URL('/token', info.authorizationServerUrl)).href\n );\n const { verifier, challenge } = await pkce();\n const state = randomState();\n await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));\n const url = new URL(\n metadata?.authorization_endpoint ?? new URL('/authorize', info.authorizationServerUrl)\n );\n if (!secureUrl(url)) throw new Error(\"The authorization server's sign-in address isn't https.\");\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', client.client_id);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('redirect_uri', provider.redirectUrl);\n url.searchParams.set('state', state);\n if (scope) url.searchParams.set('scope', scope);\n if (scope?.split(/\\s+/).includes('offline_access')) url.searchParams.set('prompt', 'consent');\n const resource = selectResource(server.config.url, info.resourceMetadata);\n if (resource) url.searchParams.set('resource', resource);\n return { url: url.href, state };\n}\n\nexport interface Callback {\n code?: string;\n state?: string;\n iss?: string;\n error?: string;\n error_description?: string;\n}\n\nexport async function finishSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n answer: string,\n fetch: McpFetch\n): Promise<void> {\n let callback: Callback;\n try {\n callback = JSON.parse(answer) as Callback;\n } catch {\n throw new Error(\"The sign-in answer isn't a callback.\");\n }\n const expected = (await provider.load()).oauthState;\n if (!expected || callback.state !== expected)\n throw new Error(\"The sign-in answer doesn't match this sign-in. Try again.\");\n if (callback.error)\n throw new Error(\n `Sign-in failed: ${sentence(callback.error_description?.trim() || callback.error)}`\n );\n if (!callback.code) throw new Error('The sign-in answer has no authorization code.');\n const configured = server.config.oauth?.authServerMetadataUrl;\n await authorizeMcp(provider, {\n serverUrl: server.config.url,\n authorizationCode: callback.code,\n ...(callback.iss ? { iss: callback.iss } : {}),\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch,\n });\n await provider.update((state) => {\n const next = { ...state };\n delete next.oauthState;\n delete next.codeVerifier;\n return next;\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StreamableHttpTransport } from "@earendil-works/pi-mcp";
|
|
2
|
+
import { scrubText } from "../secrets/scrub.js";
|
|
2
3
|
import { mcpAccounts } from "./accounts.js";
|
|
3
4
|
import { McpCatalog } from "./catalog.js";
|
|
4
5
|
import {
|
|
@@ -7,7 +8,7 @@ import {
|
|
|
7
8
|
usesOAuth
|
|
8
9
|
} from "./config.js";
|
|
9
10
|
import { McpServerConnection } from "./connection.js";
|
|
10
|
-
import { resolveHeaders,
|
|
11
|
+
import { resolveHeaders, secretNames } from "./credentials.js";
|
|
11
12
|
import { authProvider as oauthAuth, StoredOAuth } from "./oauth.js";
|
|
12
13
|
import { configPath, logPath, shown } from "./paths.js";
|
|
13
14
|
import { patchSse } from "./sse.js";
|
|
@@ -54,7 +55,7 @@ ${connection.missing.join(",")}`
|
|
|
54
55
|
auth`
|
|
55
56
|
};
|
|
56
57
|
if (connection.state !== "failed") return void 0;
|
|
57
|
-
const error =
|
|
58
|
+
const error = connection.error ?? "unknown error";
|
|
58
59
|
const blocked = cors && CORS_ERROR.test(error);
|
|
59
60
|
return {
|
|
60
61
|
...base,
|
|
@@ -79,9 +80,9 @@ connected
|
|
|
79
80
|
${connection.entry.config.url}`
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
|
-
function logLine(server, params,
|
|
83
|
+
function logLine(server, params, at) {
|
|
83
84
|
const { level, logger, data } = params ?? {};
|
|
84
|
-
const text =
|
|
85
|
+
const text = typeof data === "string" ? data : JSON.stringify(data) ?? "";
|
|
85
86
|
return `${at.toISOString()} [${server}] ${level ?? "info"}${logger ? ` ${logger}` : ""}: ${text}
|
|
86
87
|
`;
|
|
87
88
|
}
|
|
@@ -91,8 +92,9 @@ function serverLog(options, context) {
|
|
|
91
92
|
const { env } = options;
|
|
92
93
|
return {
|
|
93
94
|
write(connection, params) {
|
|
94
|
-
const
|
|
95
|
+
const text = logLine(connection.name, params, /* @__PURE__ */ new Date());
|
|
95
96
|
logging = logging.then(async () => {
|
|
97
|
+
const line = await scrubText(text);
|
|
96
98
|
const info = await env.fileInfo(path, context);
|
|
97
99
|
if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)
|
|
98
100
|
await env.renameFile(path, `${path}.1`, context);
|
|
@@ -118,22 +120,30 @@ async function attachMcp(catalog, options, context) {
|
|
|
118
120
|
let initial = /* @__PURE__ */ new Set();
|
|
119
121
|
let first = true;
|
|
120
122
|
const log = serverLog(options, context);
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
+
const { vault } = options;
|
|
124
|
+
const base = (input, init) => options.fetch(input, init);
|
|
125
|
+
const fetcher = (server) => vault.fetch(server, base);
|
|
126
|
+
const oauth = (entry) => new StoredOAuth(entry, options.store, options.redirect);
|
|
123
127
|
const transport = async (entry) => {
|
|
124
|
-
const resolved =
|
|
128
|
+
const resolved = resolveHeaders(entry.name, entry.config, vault.records);
|
|
125
129
|
if (!resolved.ok) return { missing: resolved.missing };
|
|
126
130
|
const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : void 0;
|
|
127
131
|
return {
|
|
128
132
|
transport: new StreamableHttpTransport({
|
|
129
133
|
url: entry.config.url,
|
|
130
134
|
headers: resolved.headers,
|
|
131
|
-
fetch: fetcher,
|
|
135
|
+
fetch: fetcher(entry.name),
|
|
132
136
|
...authProvider ? { authProvider } : {}
|
|
133
|
-
})
|
|
134
|
-
secrets: resolved.secrets
|
|
137
|
+
})
|
|
135
138
|
};
|
|
136
139
|
};
|
|
140
|
+
const register = (entries) => vault.servers(
|
|
141
|
+
[...entries].map((entry) => ({
|
|
142
|
+
server: entry.name,
|
|
143
|
+
url: entry.config.url,
|
|
144
|
+
names: secretNames(entry.config)
|
|
145
|
+
}))
|
|
146
|
+
).catch(() => void 0);
|
|
137
147
|
const open = (entry) => {
|
|
138
148
|
const connection = new McpServerConnection({
|
|
139
149
|
entry,
|
|
@@ -170,6 +180,7 @@ async function attachMcp(catalog, options, context) {
|
|
|
170
180
|
}
|
|
171
181
|
if (first) initial = new Set(wanted.keys());
|
|
172
182
|
first = false;
|
|
183
|
+
void register(wanted.values());
|
|
173
184
|
for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);
|
|
174
185
|
catalog.rebuild();
|
|
175
186
|
notify();
|
|
@@ -192,10 +203,21 @@ async function attachMcp(catalog, options, context) {
|
|
|
192
203
|
void connection.close();
|
|
193
204
|
catalog.rebuild();
|
|
194
205
|
};
|
|
206
|
+
const stopRecords = vault.records.state.subscribe(() => {
|
|
207
|
+
for (const connection of [...catalog.connections.values()])
|
|
208
|
+
if (connection.state === "needs-secret") restart(connection);
|
|
209
|
+
notify();
|
|
210
|
+
});
|
|
195
211
|
return {
|
|
196
212
|
...mcpAccounts(
|
|
197
213
|
catalog,
|
|
198
|
-
{
|
|
214
|
+
{
|
|
215
|
+
records: vault.records,
|
|
216
|
+
oauth,
|
|
217
|
+
fetch: fetcher,
|
|
218
|
+
grant: (server) => vault.grant(server, fetcher(server)),
|
|
219
|
+
register: (server, url) => vault.token(server, url)
|
|
220
|
+
},
|
|
199
221
|
restart,
|
|
200
222
|
listeners
|
|
201
223
|
),
|
|
@@ -203,12 +225,12 @@ async function attachMcp(catalog, options, context) {
|
|
|
203
225
|
name: connection.name,
|
|
204
226
|
state: connection.state,
|
|
205
227
|
tools: connection.tools.length,
|
|
206
|
-
...connection.error ? { error:
|
|
228
|
+
...connection.error ? { error: connection.error } : {}
|
|
207
229
|
})),
|
|
208
|
-
secrets: () => [...catalog.connections.values()].flatMap((connection) => connection.secrets),
|
|
209
230
|
reload,
|
|
210
231
|
async close(using) {
|
|
211
232
|
clearTimeout(timer);
|
|
233
|
+
stopRecords();
|
|
212
234
|
await watcher?.close(using);
|
|
213
235
|
const all = [...catalog.connections.values()];
|
|
214
236
|
catalog.connections.clear();
|
package/dist/mcp/runtime.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/runtime.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { CredentialStore } from '@earendil-works/pi-ai';\nimport type { Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { StreamableHttpTransport } from '@earendil-works/pi-mcp';\nimport type { LickEvent, Licks } from '../licks/licks.ts';\nimport { type McpAccounts, mcpAccounts } from './accounts.ts';\nimport { McpCatalog } from './catalog.ts';\nimport {\n type McpConfigProblem,\n type McpServerEntry,\n parseMcpConfig,\n sameConfig,\n usesOAuth,\n} from './config.ts';\nimport { McpServerConnection } from './connection.ts';\nimport { resolveHeaders, scrub, secretNames } from './credentials.ts';\nimport { authProvider as oauthAuth, StoredOAuth } from './oauth.ts';\nimport { configPath, logPath, shown } from './paths.ts';\nimport { patchSse } from './sse.ts';\n\nexport const MCP_LOG_MAX_BYTES = 5 * 1024 * 1024;\n\nexport { configPath, logPath, shown } from './paths.ts';\n\nexport interface McpAttach {\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n agentDir?: string;\n credentials: CredentialStore;\n fetch: typeof fetch;\n version: string;\n reloadMs: number;\n codemode: boolean;\n cors: boolean;\n redirect: string;\n}\n\nexport interface ServerStatus {\n name: string;\n state: string;\n tools: number;\n error?: string;\n}\n\nexport type { McpAccounts } from './accounts.ts';\n\nexport interface McpRuntime extends McpAccounts {\n status(): ServerStatus[];\n secrets(): string[];\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport function problemLick(problem: McpConfigProblem, home: string, dir?: string): LickEvent {\n const file = shown(configPath(home, dir), home);\n const where = problem.server ? `server \"${problem.server}\"` : file;\n const field = problem.field ? `, field ${problem.field}` : '';\n return {\n channel: 'fswatch',\n source: `mcp:${file}`,\n title: `MCP config problem: ${where}${field}`,\n text: problem.message,\n body: `${file}: the entry is skipped until it is fixed. The format is in the slicc-agent README.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${problem.server ?? ''}\\n${problem.field ?? ''}\\n${problem.message}`,\n };\n}\n\nconst CORS_ERROR = /failed to fetch|networkerror|load failed|cors/i;\n\nexport function stateLick(connection: McpServerConnection, cors: boolean): LickEvent | undefined {\n const name = connection.name;\n const base = { channel: 'fswatch' as const, source: `mcp:${name}`, target: 'cone' as const };\n if (connection.state === 'needs-secret')\n return {\n ...base,\n title: `MCP server \"${name}\" needs a token`,\n text: `Its headers use ${connection.missing.map((item) => `\\${${item}}`).join(', ')}. Add the value in Settings \u203A MCP servers.`,\n severity: 'warn',\n eventId: `${name}\\nsecret\\n${connection.missing.join(',')}`,\n };\n if (connection.state === 'needs-auth')\n return {\n ...base,\n title: secretNames(connection.entry.config).length\n ? `MCP server \"${name}\" rejected its token`\n : `MCP server \"${name}\" needs sign-in`,\n text: connection.signInMessage(),\n severity: 'warn',\n eventId: `${name}\\nauth`,\n };\n if (connection.state !== 'failed') return undefined;\n const error = scrub(connection.error ?? 'unknown error', connection.secrets);\n const blocked = cors && CORS_ERROR.test(error);\n return {\n ...base,\n title: `MCP server \"${name}\" can't connect`,\n text: blocked\n ? \"Seven reaches it with the page's own fetch, and the server sends no CORS headers. Link the `slicc` CLI or connect the SLICC extension; either fetches without CORS.\"\n : error,\n severity: 'warn',\n eventId: `${name}\\nfailed\\n${blocked ? 'cors' : error}`,\n };\n}\n\nexport function connectedLick(connection: McpServerConnection): LickEvent {\n const count = connection.tools.length;\n return {\n channel: 'fswatch',\n source: `mcp:${connection.name}`,\n title: `MCP server \"${connection.name}\" connected`,\n text: `${connection.entry.config.url} offers ${count} tool${count === 1 ? '' : 's'}.`,\n target: 'cone',\n eventId: `${connection.name}\\nconnected\\n${connection.entry.config.url}`,\n };\n}\n\nexport function logLine(\n server: string,\n params: unknown,\n secrets: readonly string[],\n at: Date\n): string {\n const { level, logger, data } = (params ?? {}) as {\n level?: string;\n logger?: string;\n data?: unknown;\n };\n const text = scrub(typeof data === 'string' ? data : (JSON.stringify(data) ?? ''), secrets);\n return `${at.toISOString()} [${server}] ${level ?? 'info'}${logger ? ` ${logger}` : ''}: ${text}\\n`;\n}\n\nfunction serverLog(options: McpAttach, context: Context) {\n let logging = Promise.resolve();\n const path = logPath(options.home, options.agentDir);\n const { env } = options;\n return {\n write(connection: McpServerConnection, params: unknown) {\n const line = logLine(connection.name, params, connection.secrets, new Date());\n logging = logging.then(async () => {\n const info = await env.fileInfo(path, context);\n if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)\n await env.renameFile(path, `${path}.1`, context);\n await env.appendFile(path, line, context);\n });\n },\n settled: () => logging,\n };\n}\n\nexport async function attachMcp(\n catalog: McpCatalog,\n options: McpAttach,\n context: Context\n): Promise<McpRuntime> {\n const { env, home } = options;\n catalog.codemodeOn = options.codemode;\n const reported = new Set<string>();\n const report = async (lick: LickEvent | undefined, using: Context) => {\n if (!lick?.eventId || reported.has(lick.eventId)) return;\n reported.add(lick.eventId);\n await options.licks.deliver(lick, using).catch(() => undefined);\n };\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n let initial: Set<string> = new Set();\n let first = true;\n const log = serverLog(options, context);\n const fetcher = (input: string | URL, init?: RequestInit) => options.fetch(input, init);\n const oauth = (entry: McpServerEntry) =>\n new StoredOAuth(entry, options.credentials, options.redirect);\n const transport = async (entry: McpServerEntry) => {\n const resolved = await resolveHeaders(entry.name, entry.config, options.credentials);\n if (!resolved.ok) return { missing: resolved.missing };\n const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : undefined;\n return {\n transport: new StreamableHttpTransport({\n url: entry.config.url,\n headers: resolved.headers,\n fetch: fetcher,\n ...(authProvider ? { authProvider } : {}),\n }),\n secrets: resolved.secrets,\n };\n };\n const open = (entry: McpServerEntry) => {\n const connection = new McpServerConnection({\n entry,\n version: options.version,\n transport,\n onTools: () => catalog.rebuild(),\n onLog: log.write,\n onChange: (changed) => {\n if (catalog.connections.get(changed.name) !== changed) return;\n if (changed.state === 'connected' && !initial.has(changed.name))\n void report(connectedLick(changed), context);\n void report(stateLick(changed, options.cors), context);\n if (changed.state !== 'connected') catalog.rebuild();\n notify();\n },\n });\n catalog.connections.set(entry.name, connection);\n void connection.getClient().catch(() => undefined);\n };\n const reload = async (using: Context) => {\n const read = await env.readTextFile(configPath(home, options.agentDir), using);\n const parsed = parseMcpConfig(read.ok ? read.value : undefined);\n for (const problem of parsed.problems)\n await report(problemLick(problem, home, options.agentDir), using);\n const wanted = new Map(\n parsed.servers\n .filter((entry) => entry.config.enabled !== false)\n .map((entry) => [entry.name, entry])\n );\n const closing: Promise<void>[] = [];\n for (const [name, connection] of [...catalog.connections]) {\n const next = wanted.get(name);\n if (next && sameConfig(next.config, connection.entry.config)) continue;\n catalog.connections.delete(name);\n closing.push(connection.close());\n }\n if (first) initial = new Set(wanted.keys());\n first = false;\n for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);\n catalog.rebuild();\n notify();\n await Promise.all(closing);\n };\n await reload(context);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const schedule = () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n };\n const watched = await env.watch(\n [{ path: configPath(home, options.agentDir) }],\n schedule,\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const restart = (connection: McpServerConnection) => {\n open(connection.entry);\n void connection.close();\n catalog.rebuild();\n };\n return {\n ...mcpAccounts(\n catalog,\n { credentials: options.credentials, oauth, fetch: fetcher },\n restart,\n listeners\n ),\n status: () =>\n [...catalog.connections.values()].map((connection) => ({\n name: connection.name,\n state: connection.state,\n tools: connection.tools.length,\n ...(connection.error ? { error: scrub(connection.error, connection.secrets) } : {}),\n })),\n secrets: () => [...catalog.connections.values()].flatMap((connection) => connection.secrets),\n reload,\n async close(using) {\n clearTimeout(timer);\n await watcher?.close(using);\n const all = [...catalog.connections.values()];\n catalog.connections.clear();\n await Promise.all(all.map((connection) => connection.close()));\n catalog.rebuild();\n await log.settled();\n },\n };\n}\n\nexport async function startMcp(\n registry: Registry,\n options: McpAttach,\n context: Context\n): Promise<{ catalog: McpCatalog; runtime: McpRuntime }> {\n patchSse();\n const catalog = new McpCatalog(registry);\n return { catalog, runtime: await attachMcp(catalog, options, context) };\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { StreamableHttpTransport } from '@earendil-works/pi-mcp';\nimport type { LickEvent, Licks } from '../licks/licks.ts';\nimport type { McpServerSource } from '../secrets/endpoints.ts';\nimport { scrubText } from '../secrets/scrub.ts';\nimport type { PlainStore } from '../secrets/store.ts';\nimport { type McpAccounts, mcpAccounts } from './accounts.ts';\nimport { McpCatalog } from './catalog.ts';\nimport {\n type McpConfigProblem,\n type McpServerEntry,\n parseMcpConfig,\n sameConfig,\n usesOAuth,\n} from './config.ts';\nimport { McpServerConnection } from './connection.ts';\nimport { type McpRecords, resolveHeaders, secretNames } from './credentials.ts';\nimport { authProvider as oauthAuth, StoredOAuth } from './oauth.ts';\nimport { configPath, logPath, shown } from './paths.ts';\nimport { patchSse } from './sse.ts';\n\nexport const MCP_LOG_MAX_BYTES = 5 * 1024 * 1024;\n\nexport { configPath, logPath, shown } from './paths.ts';\n\nexport interface McpVault {\n records: McpRecords & {\n refresh(context: Context): Promise<void>;\n state: { subscribe(listener: () => void): () => void };\n };\n fetch(server: string, base: typeof fetch): typeof fetch;\n grant(server: string, base: typeof fetch): typeof fetch;\n servers(list: readonly McpServerSource[]): Promise<void>;\n token(server: string, url: string): Promise<void>;\n}\n\nexport interface McpAttach {\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n agentDir?: string;\n store: PlainStore;\n vault: McpVault;\n fetch: typeof fetch;\n version: string;\n reloadMs: number;\n codemode: boolean;\n cors: boolean;\n redirect: string;\n}\n\nexport interface ServerStatus {\n name: string;\n state: string;\n tools: number;\n error?: string;\n}\n\nexport type { McpAccounts } from './accounts.ts';\n\nexport interface McpRuntime extends McpAccounts {\n status(): ServerStatus[];\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport function problemLick(problem: McpConfigProblem, home: string, dir?: string): LickEvent {\n const file = shown(configPath(home, dir), home);\n const where = problem.server ? `server \"${problem.server}\"` : file;\n const field = problem.field ? `, field ${problem.field}` : '';\n return {\n channel: 'fswatch',\n source: `mcp:${file}`,\n title: `MCP config problem: ${where}${field}`,\n text: problem.message,\n body: `${file}: the entry is skipped until it is fixed. The format is in the slicc-agent README.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${problem.server ?? ''}\\n${problem.field ?? ''}\\n${problem.message}`,\n };\n}\n\nconst CORS_ERROR = /failed to fetch|networkerror|load failed|cors/i;\n\nexport function stateLick(connection: McpServerConnection, cors: boolean): LickEvent | undefined {\n const name = connection.name;\n const base = { channel: 'fswatch' as const, source: `mcp:${name}`, target: 'cone' as const };\n if (connection.state === 'needs-secret')\n return {\n ...base,\n title: `MCP server \"${name}\" needs a token`,\n text: `Its headers use ${connection.missing.map((item) => `\\${${item}}`).join(', ')}. Add the value in Settings \u203A MCP servers.`,\n severity: 'warn',\n eventId: `${name}\\nsecret\\n${connection.missing.join(',')}`,\n };\n if (connection.state === 'needs-auth')\n return {\n ...base,\n title: secretNames(connection.entry.config).length\n ? `MCP server \"${name}\" rejected its token`\n : `MCP server \"${name}\" needs sign-in`,\n text: connection.signInMessage(),\n severity: 'warn',\n eventId: `${name}\\nauth`,\n };\n if (connection.state !== 'failed') return undefined;\n const error = connection.error ?? 'unknown error';\n const blocked = cors && CORS_ERROR.test(error);\n return {\n ...base,\n title: `MCP server \"${name}\" can't connect`,\n text: blocked\n ? \"Seven reaches it with the page's own fetch, and the server sends no CORS headers. Link the `slicc` CLI or connect the SLICC extension; either fetches without CORS.\"\n : error,\n severity: 'warn',\n eventId: `${name}\\nfailed\\n${blocked ? 'cors' : error}`,\n };\n}\n\nexport function connectedLick(connection: McpServerConnection): LickEvent {\n const count = connection.tools.length;\n return {\n channel: 'fswatch',\n source: `mcp:${connection.name}`,\n title: `MCP server \"${connection.name}\" connected`,\n text: `${connection.entry.config.url} offers ${count} tool${count === 1 ? '' : 's'}.`,\n target: 'cone',\n eventId: `${connection.name}\\nconnected\\n${connection.entry.config.url}`,\n };\n}\n\nexport function logLine(server: string, params: unknown, at: Date): string {\n const { level, logger, data } = (params ?? {}) as {\n level?: string;\n logger?: string;\n data?: unknown;\n };\n const text = typeof data === 'string' ? data : (JSON.stringify(data) ?? '');\n return `${at.toISOString()} [${server}] ${level ?? 'info'}${logger ? ` ${logger}` : ''}: ${text}\\n`;\n}\n\nfunction serverLog(options: McpAttach, context: Context) {\n let logging = Promise.resolve();\n const path = logPath(options.home, options.agentDir);\n const { env } = options;\n return {\n write(connection: McpServerConnection, params: unknown) {\n const text = logLine(connection.name, params, new Date());\n logging = logging.then(async () => {\n const line = await scrubText(text);\n const info = await env.fileInfo(path, context);\n if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)\n await env.renameFile(path, `${path}.1`, context);\n await env.appendFile(path, line, context);\n });\n },\n settled: () => logging,\n };\n}\n\nexport async function attachMcp(\n catalog: McpCatalog,\n options: McpAttach,\n context: Context\n): Promise<McpRuntime> {\n const { env, home } = options;\n catalog.codemodeOn = options.codemode;\n const reported = new Set<string>();\n const report = async (lick: LickEvent | undefined, using: Context) => {\n if (!lick?.eventId || reported.has(lick.eventId)) return;\n reported.add(lick.eventId);\n await options.licks.deliver(lick, using).catch(() => undefined);\n };\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n let initial: Set<string> = new Set();\n let first = true;\n const log = serverLog(options, context);\n const { vault } = options;\n const base = (input: string | URL | Request, init?: RequestInit) => options.fetch(input, init);\n const fetcher = (server: string) => vault.fetch(server, base);\n const oauth = (entry: McpServerEntry) => new StoredOAuth(entry, options.store, options.redirect);\n const transport = async (entry: McpServerEntry) => {\n const resolved = resolveHeaders(entry.name, entry.config, vault.records);\n if (!resolved.ok) return { missing: resolved.missing };\n const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : undefined;\n return {\n transport: new StreamableHttpTransport({\n url: entry.config.url,\n headers: resolved.headers,\n fetch: fetcher(entry.name),\n ...(authProvider ? { authProvider } : {}),\n }),\n };\n };\n const register = (entries: Iterable<McpServerEntry>) =>\n vault\n .servers(\n [...entries].map((entry) => ({\n server: entry.name,\n url: entry.config.url,\n names: secretNames(entry.config),\n }))\n )\n .catch(() => undefined);\n const open = (entry: McpServerEntry) => {\n const connection = new McpServerConnection({\n entry,\n version: options.version,\n transport,\n onTools: () => catalog.rebuild(),\n onLog: log.write,\n onChange: (changed) => {\n if (catalog.connections.get(changed.name) !== changed) return;\n if (changed.state === 'connected' && !initial.has(changed.name))\n void report(connectedLick(changed), context);\n void report(stateLick(changed, options.cors), context);\n if (changed.state !== 'connected') catalog.rebuild();\n notify();\n },\n });\n catalog.connections.set(entry.name, connection);\n void connection.getClient().catch(() => undefined);\n };\n const reload = async (using: Context) => {\n const read = await env.readTextFile(configPath(home, options.agentDir), using);\n const parsed = parseMcpConfig(read.ok ? read.value : undefined);\n for (const problem of parsed.problems)\n await report(problemLick(problem, home, options.agentDir), using);\n const wanted = new Map(\n parsed.servers\n .filter((entry) => entry.config.enabled !== false)\n .map((entry) => [entry.name, entry])\n );\n const closing: Promise<void>[] = [];\n for (const [name, connection] of [...catalog.connections]) {\n const next = wanted.get(name);\n if (next && sameConfig(next.config, connection.entry.config)) continue;\n catalog.connections.delete(name);\n closing.push(connection.close());\n }\n if (first) initial = new Set(wanted.keys());\n first = false;\n void register(wanted.values());\n for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);\n catalog.rebuild();\n notify();\n await Promise.all(closing);\n };\n await reload(context);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const schedule = () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n };\n const watched = await env.watch(\n [{ path: configPath(home, options.agentDir) }],\n schedule,\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const restart = (connection: McpServerConnection) => {\n open(connection.entry);\n void connection.close();\n catalog.rebuild();\n };\n const stopRecords = vault.records.state.subscribe(() => {\n for (const connection of [...catalog.connections.values()])\n if (connection.state === 'needs-secret') restart(connection);\n notify();\n });\n return {\n ...mcpAccounts(\n catalog,\n {\n records: vault.records,\n oauth,\n fetch: fetcher,\n grant: (server) => vault.grant(server, fetcher(server)),\n register: (server, url) => vault.token(server, url),\n },\n restart,\n listeners\n ),\n status: () =>\n [...catalog.connections.values()].map((connection) => ({\n name: connection.name,\n state: connection.state,\n tools: connection.tools.length,\n ...(connection.error ? { error: connection.error } : {}),\n })),\n reload,\n async close(using) {\n clearTimeout(timer);\n stopRecords();\n await watcher?.close(using);\n const all = [...catalog.connections.values()];\n catalog.connections.clear();\n await Promise.all(all.map((connection) => connection.close()));\n catalog.rebuild();\n await log.settled();\n },\n };\n}\n\nexport async function startMcp(\n registry: Registry,\n options: McpAttach,\n context: Context\n): Promise<{ catalog: McpCatalog; runtime: McpRuntime }> {\n patchSse();\n const catalog = new McpCatalog(registry);\n return { catalog, runtime: await attachMcp(catalog, options, context) };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["configPath", "logPath", "shown"]
|
|
7
7
|
}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
scriptResult,
|
|
16
16
|
spiller
|
|
17
17
|
} from "./content.js";
|
|
18
|
-
import { scrub } from "./credentials.js";
|
|
19
18
|
const LIST_MCP_RESOURCES = "list_mcp_resources";
|
|
20
19
|
const LIST_MCP_RESOURCE_TEMPLATES = "list_mcp_resource_templates";
|
|
21
20
|
function parametersOf(schema) {
|
|
@@ -56,9 +55,8 @@ function progressText(progress) {
|
|
|
56
55
|
const total = progress.total === void 0 ? "" : `/${progress.total}`;
|
|
57
56
|
return progress.message ?? `Progress ${progress.progress}${total}`;
|
|
58
57
|
}
|
|
59
|
-
function failure(
|
|
60
|
-
|
|
61
|
-
throw new Error(scrub(message, entry.connection.secrets));
|
|
58
|
+
function failure(error) {
|
|
59
|
+
throw new Error(error instanceof Error ? error.message : String(error));
|
|
62
60
|
}
|
|
63
61
|
function directTool(entry, readable) {
|
|
64
62
|
return defineTool({
|
|
@@ -75,7 +73,7 @@ function directTool(entry, readable) {
|
|
|
75
73
|
};
|
|
76
74
|
const signal = signalOf(context);
|
|
77
75
|
if (signal) options.signal = signal;
|
|
78
|
-
const result = await entry.connection.callTool(entry.tool.name, args, options).catch(
|
|
76
|
+
const result = await entry.connection.callTool(entry.tool.name, args, options).catch(failure);
|
|
79
77
|
const converted = await convertResult(
|
|
80
78
|
entry.server,
|
|
81
79
|
entry.tool.name,
|
|
@@ -109,7 +107,7 @@ function scriptTool(entry, signal) {
|
|
|
109
107
|
const result = await entry.connection.callTool(entry.tool.name, args ?? {}, {
|
|
110
108
|
signal: combined,
|
|
111
109
|
timeoutMs: entry.connection.timeoutMs
|
|
112
|
-
}).catch(
|
|
110
|
+
}).catch(failure);
|
|
113
111
|
return scriptResult(result);
|
|
114
112
|
}
|
|
115
113
|
};
|
package/dist/mcp/tools.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/tools.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context, JsonValue } from '@earendil-works/chord';\nimport {\n type CodemodeJsonSchema,\n type CodemodeTool,\n renderToolSample,\n toCodemodeIdentifier,\n} from '@earendil-works/pi-codemode';\nimport {\n defineTool,\n type ToolExecutionApi,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { McpRequestOptions, Tool as McpTool } from '@earendil-works/pi-mcp';\nimport { scrubbedOutput } from '../secrets/scrub.ts';\nimport type { McpExposure } from './config.ts';\nimport { errorMessage, type McpServerConnection } from './connection.ts';\nimport {\n type Content,\n convertResult,\n limitContent,\n modelContent,\n READ_MCP_RESOURCE,\n type Saver,\n scriptResult,\n spiller,\n} from './content.ts';\nimport { scrub } from './credentials.ts';\n\nexport const LIST_MCP_RESOURCES = 'list_mcp_resources';\nexport const LIST_MCP_RESOURCE_TEMPLATES = 'list_mcp_resource_templates';\nexport { READ_MCP_RESOURCE };\n\nexport interface McpToolEntry {\n server: string;\n name: string;\n exposure: McpExposure;\n tool: McpTool;\n connection: McpServerConnection;\n}\n\nexport type McpDetails = { server: string; tool: string; fullOutputPath?: string };\n\nfunction parametersOf(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _schema, ...rest } = schema;\n return {\n ...rest,\n type: rest.type ?? 'object',\n ...(rest.properties === undefined ? { properties: {} } : {}),\n };\n}\n\nexport function resultSchema(structured: Record<string, unknown> | undefined): CodemodeJsonSchema {\n return {\n type: 'object',\n properties: {\n content: { type: 'array', items: { type: 'object' } },\n ...(structured ? { structuredContent: structured } : {}),\n isError: { type: 'boolean' },\n },\n required: ['content'],\n };\n}\n\nfunction describe(entry: McpToolEntry): string {\n const title = entry.tool.title ?? entry.tool.annotations?.title;\n return (\n entry.tool.description?.trim() ||\n title ||\n `MCP tool ${entry.tool.name} from server ${entry.server}`\n );\n}\n\nfunction signalOf(context: Context): AbortSignal | undefined {\n return context.abortSignal as AbortSignal | undefined;\n}\n\nexport function apiSaver(api: ToolExecutionApi, prefix: string, context: Context): Saver {\n let counter = 0;\n return spiller(\n async (path, data, using) => (await api.env?.writeFile(path, data, using))?.ok === true,\n () => `${api.callId}-${prefix}${++counter}`,\n context\n );\n}\n\nfunction progressText(progress: { progress: number; total?: number; message?: string }): string {\n const total = progress.total === undefined ? '' : `/${progress.total}`;\n return progress.message ?? `Progress ${progress.progress}${total}`;\n}\n\nfunction failure(entry: { connection: McpServerConnection }, error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(scrub(message, entry.connection.secrets));\n}\n\nexport function directTool(entry: McpToolEntry, readable: () => boolean): ToolRegistration {\n return defineTool({\n name: entry.name,\n description: describe(entry),\n parameters: parametersOf(entry.tool.inputSchema) as never,\n replay: 'unsafe',\n async execute(args, api, context) {\n const save = apiSaver(api, '', context);\n const options: McpRequestOptions = {\n timeoutMs: entry.connection.timeoutMs,\n onProgress: (progress) => void scrubbedOutput(api, `${progressText(progress)}\\n`),\n };\n const signal = signalOf(context);\n if (signal) options.signal = signal;\n const result = await entry.connection\n .callTool(entry.tool.name, args as Record<string, unknown>, options)\n .catch((error) => failure(entry, error));\n const converted = await convertResult(\n entry.server,\n entry.tool.name,\n result,\n save,\n readable()\n );\n const details: McpDetails = {\n server: entry.server,\n tool: entry.tool.name,\n ...(converted.fullOutputPath ? { fullOutputPath: converted.fullOutputPath } : {}),\n };\n return {\n content: converted.content,\n details: details as JsonValue,\n ...(converted.isError ? { isError: true } : {}),\n };\n },\n }) as ToolRegistration;\n}\n\nexport function scriptTool(entry: McpToolEntry, signal: AbortSignal | undefined): CodemodeTool {\n const inputSchema = parametersOf(entry.tool.inputSchema);\n const outputSchema = resultSchema(entry.tool.outputSchema as Record<string, unknown> | undefined);\n return {\n name: entry.name,\n description: describe(entry),\n inputSchema,\n outputSchema,\n async execute(args, { signal: own }) {\n const combined = signal ? AbortSignal.any([own, signal]) : own;\n const result = await entry.connection\n .callTool(entry.tool.name, (args ?? {}) as Record<string, unknown>, {\n signal: combined,\n timeoutMs: entry.connection.timeoutMs,\n })\n .catch((error) => failure(entry, error));\n return scriptResult(result);\n },\n };\n}\n\nexport interface ResourceServer {\n name: string;\n connection: McpServerConnection;\n}\n\nfunction stringArgument(params: unknown, key: string): string | undefined {\n const value = (params as Record<string, unknown> | undefined)?.[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string') throw new Error(`${key} must be a string`);\n return value.trim() || undefined;\n}\n\nfunction listed(server: string, item: object): Record<string, unknown> {\n const { _meta, icons: _icons, ...rest } = item as Record<string, unknown>;\n return { server, ...rest };\n}\n\nconst stringProperty = (description: string) => ({ type: 'string', description });\nconst LIST_PARAMETERS = {\n type: 'object',\n properties: {\n server: stringProperty('MCP server name. Omit to list every server with resources.'),\n cursor: stringProperty(\n 'Opaque cursor from a previous call with the same server; omit for the first page.'\n ),\n },\n additionalProperties: false,\n};\nconst READ_PARAMETERS = {\n type: 'object',\n properties: {\n server: stringProperty(\n \"MCP server name exactly as configured. Must match the 'server' field returned by list_mcp_resources.\"\n ),\n uri: stringProperty(\n 'Resource URI to read. Must be one of the URIs returned by list_mcp_resources.'\n ),\n },\n required: ['server', 'uri'],\n additionalProperties: false,\n};\n\nconst DESCRIPTIONS: Record<string, string> = {\n [LIST_MCP_RESOURCES]:\n 'Lists resources provided by MCP servers. Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Prefer resources over web search when possible.',\n [LIST_MCP_RESOURCE_TEMPLATES]:\n 'Lists resource templates provided by MCP servers. Parameterized resource templates allow servers to share data that takes parameters and provides context to language models, such as files, database schemas, or application-specific information. Prefer resource templates over web search when possible.',\n [READ_MCP_RESOURCE]:\n 'Read a specific resource from an MCP server given the server name and resource URI.',\n};\n\ntype Key = 'resources' | 'resourceTemplates';\n\nfunction find(servers: readonly ResourceServer[], name: string): ResourceServer {\n const server = servers.find((candidate) => candidate.name === name);\n if (server) return server;\n const available = servers.map((candidate) => candidate.name).join(', ');\n throw new Error(\n `MCP server \"${name}\" has no resources${available ? `. Servers with resources: ${available}` : ''}`\n );\n}\n\nasync function listing(\n servers: readonly ResourceServer[],\n params: unknown,\n signal: AbortSignal | undefined,\n key: Key\n): Promise<Record<string, unknown>> {\n const serverName = stringArgument(params, 'server');\n const cursor = stringArgument(params, 'cursor');\n const visible = (item: object) => {\n const { uri, uriTemplate, mimeType } = item as Record<string, string | undefined>;\n const target = uri ?? uriTemplate ?? '';\n return !(target.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(mimeType ?? ''));\n };\n const options = (server: ResourceServer): McpRequestOptions => ({\n timeoutMs: server.connection.timeoutMs,\n ...(signal ? { signal } : {}),\n });\n if (serverName) {\n const server = find(servers, serverName);\n const page =\n key === 'resources'\n ? await server.connection.resourcesPage(cursor, options(server)).then((result) => ({\n items: result.resources as object[],\n nextCursor: result.nextCursor,\n }))\n : await server.connection.resourceTemplatesPage(cursor, options(server)).then((result) => ({\n items: result.resourceTemplates as object[],\n nextCursor: result.nextCursor,\n }));\n return {\n server: server.name,\n [key]: page.items.filter(visible).map((item) => listed(server.name, item)),\n ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),\n };\n }\n if (cursor) throw new Error('cursor can only be used when a server is specified');\n const sorted = [...servers].sort((a, b) => a.name.localeCompare(b.name));\n const results = await Promise.allSettled(\n sorted.map((server) =>\n key === 'resources'\n ? server.connection.allResources(options(server))\n : server.connection.allResourceTemplates(options(server))\n )\n );\n const items: Record<string, unknown>[] = [];\n const errors: { server: string; error: string }[] = [];\n results.forEach((result, index) => {\n const server = (sorted[index] as ResourceServer).name;\n if (result.status === 'fulfilled')\n items.push(...(result.value as object[]).filter(visible).map((item) => listed(server, item)));\n else\n errors.push({\n server,\n error: errorMessage(result.reason),\n });\n });\n return { [key]: items, ...(errors.length ? { errors } : {}) };\n}\n\nasync function reading(\n servers: readonly ResourceServer[],\n params: unknown,\n signal: AbortSignal | undefined,\n save: Saver\n): Promise<{ content: Content[]; payload: Record<string, unknown>; fullOutputPath?: string }> {\n const serverName = stringArgument(params, 'server');\n const uri = stringArgument(params, 'uri');\n if (!serverName) throw new Error('server must be provided');\n if (!uri) throw new Error('uri must be provided');\n const server = find(servers, serverName);\n const result = await server.connection.readResource(uri, {\n timeoutMs: server.connection.timeoutMs,\n ...(signal ? { signal } : {}),\n });\n const blocks = result.contents.flatMap((contents) => [\n ...(result.contents.length > 1 ? [{ type: 'text' as const, text: `${contents.uri}:` }] : []),\n { type: 'resource' as const, resource: contents },\n ]);\n const converted = await modelContent(server.name, blocks, save);\n const limited = await limitContent(\n converted.length ? converted : [{ type: 'text', text: `Resource ${uri} is empty.` }],\n save\n );\n const contents = result.contents.map((item) => {\n const { _meta: _ignored, ...rest } = item as typeof item & { _meta?: unknown };\n return rest;\n });\n return { ...limited, payload: { server: server.name, uri, contents } };\n}\n\nexport type ServersFor = (\n api: ToolExecutionApi,\n context: Context\n) => Promise<readonly ResourceServer[]>;\n\nexport function resourceTools(servers: ServersFor): ToolRegistration[] {\n const listTool = (name: string, key: Key) =>\n defineTool({\n name,\n description: DESCRIPTIONS[name] as string,\n parameters: LIST_PARAMETERS as never,\n replay: 'safe',\n async execute(args, api, context) {\n const payload = await listing(await servers(api, context), args, signalOf(context), key);\n const limited = await limitContent(\n [{ type: 'text', text: JSON.stringify(payload) }],\n apiSaver(api, '', context)\n );\n return { content: limited.content };\n },\n }) as ToolRegistration;\n return [\n listTool(LIST_MCP_RESOURCES, 'resources'),\n listTool(LIST_MCP_RESOURCE_TEMPLATES, 'resourceTemplates'),\n defineTool({\n name: READ_MCP_RESOURCE,\n description: DESCRIPTIONS[READ_MCP_RESOURCE] as string,\n parameters: READ_PARAMETERS as never,\n replay: 'safe',\n async execute(args, api, context) {\n const read = await reading(\n await servers(api, context),\n args,\n signalOf(context),\n apiSaver(api, '', context)\n );\n return { content: read.content };\n },\n }) as ToolRegistration,\n ];\n}\n\nexport function resourceScriptTools(\n servers: () => readonly ResourceServer[],\n signal: AbortSignal | undefined,\n save: Saver\n): CodemodeTool[] {\n const combine = (own: AbortSignal) => (signal ? AbortSignal.any([own, signal]) : own);\n return [\n {\n name: LIST_MCP_RESOURCES,\n description: DESCRIPTIONS[LIST_MCP_RESOURCES] as string,\n inputSchema: LIST_PARAMETERS,\n execute: (args, { signal: own }) => listing(servers(), args, combine(own), 'resources'),\n },\n {\n name: LIST_MCP_RESOURCE_TEMPLATES,\n description: DESCRIPTIONS[LIST_MCP_RESOURCE_TEMPLATES] as string,\n inputSchema: LIST_PARAMETERS,\n execute: (args, { signal: own }) =>\n listing(servers(), args, combine(own), 'resourceTemplates'),\n },\n {\n name: READ_MCP_RESOURCE,\n description: DESCRIPTIONS[READ_MCP_RESOURCE] as string,\n inputSchema: READ_PARAMETERS,\n execute: async (args, { signal: own }) =>\n (await reading(servers(), args, combine(own), save)).payload,\n },\n ];\n}\n\nexport interface Namespace {\n name: string;\n description?: string;\n instructions?: string;\n}\n\nfunction isNamespaceName(namespace: string, query: string): boolean {\n const id = toCodemodeIdentifier(namespace);\n const queryId = toCodemodeIdentifier(query);\n const suffix = (name: string) => name.slice(name.lastIndexOf('__') + 2);\n return (\n namespace === query || id === queryId || suffix(namespace) === query || suffix(id) === queryId\n );\n}\n\nfunction words(text: string): string[] {\n return text\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((word) => word.length > 1);\n}\n\nexport function rank(\n query: string,\n documents: readonly { name: string; text: string }[],\n limit: number\n): string[] {\n const terms = words(query);\n if (!terms.length) return [];\n const tokenized = documents.map((doc) => ({ name: doc.name, words: words(doc.text) }));\n const frequency = new Map<string, number>();\n for (const doc of tokenized)\n for (const term of new Set(doc.words)) frequency.set(term, (frequency.get(term) ?? 0) + 1);\n const total = tokenized.length;\n const scored = tokenized.map((doc) => {\n let score = 0;\n for (const term of terms) {\n const count = doc.words.filter((word) => word === term || word.startsWith(term)).length;\n if (!count) continue;\n const df = frequency.get(term) ?? 0;\n const idf = Math.log(1 + (total - df + 0.5) / (df + 0.5));\n score += idf * ((count * 2.2) / (count + 1.2 * (0.25 + (0.75 * doc.words.length) / 20)));\n }\n return { name: doc.name, score };\n });\n return scored\n .filter((doc) => doc.score > 0)\n .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))\n .slice(0, limit)\n .map((doc) => doc.name);\n}\n\nexport const DEFAULT_SEARCH_LIMIT = 8;\n\nexport function discoveryGlobals(\n tools: readonly CodemodeTool[],\n namespaceOf: (tool: string) => Namespace | undefined\n): CodemodeTool[] {\n const samples = new Map(tools.map((tool) => [tool.name, renderToolSample(tool)]));\n const entry = (name: string) => ({\n name: toCodemodeIdentifier(name),\n description: samples.get(name) as string,\n });\n return [\n {\n name: 'searchTools',\n spread: true,\n execute: (args) => {\n const [query, options] = args as [\n unknown,\n { limit?: unknown; namespace?: unknown } | undefined,\n ];\n if (typeof query !== 'string') throw new Error('searchTools() expects a query string');\n const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0)\n throw new Error('searchTools() limit must be a positive integer');\n const namespace = options?.namespace;\n if (namespace !== undefined && namespace !== null && typeof namespace !== 'string')\n throw new Error('searchTools() namespace must be a string');\n const documents = tools.flatMap((tool) => {\n const space = namespaceOf(tool.name);\n if (namespace && (!space || !isNamespaceName(space.name, namespace))) return [];\n return [\n {\n name: tool.name,\n text: [\n tool.name.replace(/_/g, ' '),\n tool.description as string,\n space?.description ?? '',\n ].join(' '),\n },\n ];\n });\n return rank(query, documents, limit).map(entry);\n },\n },\n {\n name: 'describeTool',\n spread: true,\n execute: (args) => {\n const [name] = args as unknown[];\n if (typeof name !== 'string') throw new Error('describeTool() expects a tool name');\n const tool = tools.find(\n (candidate) => candidate.name === name || toCodemodeIdentifier(candidate.name) === name\n );\n return tool ? samples.get(tool.name) : undefined;\n },\n },\n {\n name: 'describeNamespace',\n spread: true,\n execute: (args) => {\n const [name] = args as unknown[];\n if (typeof name !== 'string')\n throw new Error('describeNamespace() expects a namespace name');\n let found: Namespace | undefined;\n const names: string[] = [];\n for (const tool of tools) {\n const space = namespaceOf(tool.name);\n if (!space || !isNamespaceName(space.name, name)) continue;\n found ??= space;\n names.push(toCodemodeIdentifier(tool.name));\n }\n if (!found) return undefined;\n return {\n name: found.name,\n ...(found.description ? { description: found.description } : {}),\n ...(found.instructions ? { instructions: found.instructions } : {}),\n tools: names,\n };\n },\n },\n ];\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAGK;AAEP,SAAS,sBAAsB;AAE/B,SAAS,oBAA8C;AACvD;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AAEf,MAAM,qBAAqB;AAC3B,MAAM,8BAA8B;AAa3C,SAAS,aAAa,QAA0D;AAC9E,QAAM,EAAE,SAAS,SAAS,GAAG,KAAK,IAAI;AACtC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,KAAK,QAAQ;AAAA,IACnB,GAAI,KAAK,eAAe,SAAY,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC;AAAA,EAC5D;AACF;AAEO,SAAS,aAAa,YAAqE;AAChG,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;AAAA,MACpD,GAAI,aAAa,EAAE,mBAAmB,WAAW,IAAI,CAAC;AAAA,MACtD,SAAS,EAAE,MAAM,UAAU;AAAA,IAC7B;AAAA,IACA,UAAU,CAAC,SAAS;AAAA,EACtB;AACF;AAEA,SAAS,SAAS,OAA6B;AAC7C,QAAM,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK,aAAa;AAC1D,SACE,MAAM,KAAK,aAAa,KAAK,KAC7B,SACA,YAAY,MAAM,KAAK,IAAI,gBAAgB,MAAM,MAAM;AAE3D;AAEA,SAAS,SAAS,SAA2C;AAC3D,SAAO,QAAQ;AACjB;AAEO,SAAS,SAAS,KAAuB,QAAgB,SAAyB;AACvF,MAAI,UAAU;AACd,SAAO;AAAA,IACL,OAAO,MAAM,MAAM,WAAW,MAAM,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,IAAI,OAAO;AAAA,IACnF,MAAM,GAAG,IAAI,MAAM,IAAI,MAAM,GAAG,EAAE,OAAO;AAAA,IACzC;AAAA,EACF;AACF;AAEA,SAAS,aAAa,UAA0E;AAC9F,QAAM,QAAQ,SAAS,UAAU,SAAY,KAAK,IAAI,SAAS,KAAK;AACpE,SAAO,SAAS,WAAW,YAAY,SAAS,QAAQ,GAAG,KAAK;AAClE;AAEA,SAAS,QAAQ,OAA4C,OAAuB;AAClF,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAM,IAAI,MAAM,MAAM,SAAS,MAAM,WAAW,OAAO,CAAC;AAC1D;AAEO,SAAS,WAAW,OAAqB,UAA2C;AACzF,SAAO,WAAW;AAAA,IAChB,MAAM,MAAM;AAAA,IACZ,aAAa,SAAS,KAAK;AAAA,IAC3B,YAAY,aAAa,MAAM,KAAK,WAAW;AAAA,IAC/C,QAAQ;AAAA,IACR,MAAM,QAAQ,MAAM,KAAK,SAAS;AAChC,YAAM,OAAO,SAAS,KAAK,IAAI,OAAO;AACtC,YAAM,UAA6B;AAAA,QACjC,WAAW,MAAM,WAAW;AAAA,QAC5B,YAAY,CAAC,aAAa,KAAK,eAAe,KAAK,GAAG,aAAa,QAAQ,CAAC;AAAA,CAAI;AAAA,MAClF;AACA,YAAM,SAAS,SAAS,OAAO;AAC/B,UAAI,OAAQ,SAAQ,SAAS;AAC7B,YAAM,SAAS,MAAM,MAAM,WACxB,SAAS,MAAM,KAAK,MAAM,MAAiC,OAAO,EAClE,MAAM,CAAC,UAAU,QAAQ,OAAO,KAAK,CAAC;AACzC,YAAM,YAAY,MAAM;AAAA,QACtB,MAAM;AAAA,QACN,MAAM,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX;AACA,YAAM,UAAsB;AAAA,QAC1B,QAAQ,MAAM;AAAA,QACd,MAAM,MAAM,KAAK;AAAA,QACjB,GAAI,UAAU,iBAAiB,EAAE,gBAAgB,UAAU,eAAe,IAAI,CAAC;AAAA,MACjF;AACA,aAAO;AAAA,QACL,SAAS,UAAU;AAAA,QACnB;AAAA,QACA,GAAI,UAAU,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,MAC/C;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEO,SAAS,WAAW,OAAqB,QAA+C;AAC7F,QAAM,cAAc,aAAa,MAAM,KAAK,WAAW;AACvD,QAAM,eAAe,aAAa,MAAM,KAAK,YAAmD;AAChG,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,aAAa,SAAS,KAAK;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,MAAM,QAAQ,MAAM,EAAE,QAAQ,IAAI,GAAG;AACnC,YAAM,WAAW,SAAS,YAAY,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI;AAC3D,YAAM,SAAS,MAAM,MAAM,WACxB,SAAS,MAAM,KAAK,MAAO,QAAQ,CAAC,GAA+B;AAAA,QAClE,QAAQ;AAAA,QACR,WAAW,MAAM,WAAW;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,UAAU,QAAQ,OAAO,KAAK,CAAC;AACzC,aAAO,aAAa,MAAM;AAAA,IAC5B;AAAA,EACF;AACF;AAOA,SAAS,eAAe,QAAiB,KAAiC;AACxE,QAAM,QAAS,SAAiD,GAAG;AACnE,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,GAAG,GAAG,mBAAmB;AACxE,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,OAAO,QAAgB,MAAuC;AACrE,QAAM,EAAE,OAAO,OAAO,QAAQ,GAAG,KAAK,IAAI;AAC1C,SAAO,EAAE,QAAQ,GAAG,KAAK;AAC3B;AAEA,MAAM,iBAAiB,CAAC,iBAAyB,EAAE,MAAM,UAAU,YAAY;AAC/E,MAAM,kBAAkB;AAAA,EACtB,MAAM;AAAA,EACN,YAAY;AAAA,IACV,QAAQ,eAAe,4DAA4D;AAAA,IACnF,QAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAAA,EACA,sBAAsB;AACxB;AACA,MAAM,kBAAkB;AAAA,EACtB,MAAM;AAAA,EACN,YAAY;AAAA,IACV,QAAQ;AAAA,MACN;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU,CAAC,UAAU,KAAK;AAAA,EAC1B,sBAAsB;AACxB;AAEA,MAAM,eAAuC;AAAA,EAC3C,CAAC,kBAAkB,GACjB;AAAA,EACF,CAAC,2BAA2B,GAC1B;AAAA,EACF,CAAC,iBAAiB,GAChB;AACJ;AAIA,SAAS,KAAK,SAAoC,MAA8B;AAC9E,QAAM,SAAS,QAAQ,KAAK,CAAC,cAAc,UAAU,SAAS,IAAI;AAClE,MAAI,OAAQ,QAAO;AACnB,QAAM,YAAY,QAAQ,IAAI,CAAC,cAAc,UAAU,IAAI,EAAE,KAAK,IAAI;AACtE,QAAM,IAAI;AAAA,IACR,eAAe,IAAI,qBAAqB,YAAY,6BAA6B,SAAS,KAAK,EAAE;AAAA,EACnG;AACF;AAEA,eAAe,QACb,SACA,QACA,QACA,KACkC;AAClC,QAAM,aAAa,eAAe,QAAQ,QAAQ;AAClD,QAAM,SAAS,eAAe,QAAQ,QAAQ;AAC9C,QAAM,UAAU,CAAC,SAAiB;AAChC,UAAM,EAAE,KAAK,aAAa,SAAS,IAAI;AACvC,UAAM,SAAS,OAAO,eAAe;AACrC,WAAO,EAAE,OAAO,WAAW,OAAO,KAAK,iCAAiC,KAAK,YAAY,EAAE;AAAA,EAC7F;AACA,QAAM,UAAU,CAAC,YAA+C;AAAA,IAC9D,WAAW,OAAO,WAAW;AAAA,IAC7B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC7B;AACA,MAAI,YAAY;AACd,UAAM,SAAS,KAAK,SAAS,UAAU;AACvC,UAAM,OACJ,QAAQ,cACJ,MAAM,OAAO,WAAW,cAAc,QAAQ,QAAQ,MAAM,CAAC,EAAE,KAAK,CAAC,YAAY;AAAA,MAC/E,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,IACrB,EAAE,IACF,MAAM,OAAO,WAAW,sBAAsB,QAAQ,QAAQ,MAAM,CAAC,EAAE,KAAK,CAAC,YAAY;AAAA,MACvF,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,IACrB,EAAE;AACR,WAAO;AAAA,MACL,QAAQ,OAAO;AAAA,MACf,CAAC,GAAG,GAAG,KAAK,MAAM,OAAO,OAAO,EAAE,IAAI,CAAC,SAAS,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,MACzE,GAAI,KAAK,eAAe,SAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;AAAA,IACzE;AAAA,EACF;AACA,MAAI,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AAChF,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACvE,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,OAAO;AAAA,MAAI,CAAC,WACV,QAAQ,cACJ,OAAO,WAAW,aAAa,QAAQ,MAAM,CAAC,IAC9C,OAAO,WAAW,qBAAqB,QAAQ,MAAM,CAAC;AAAA,IAC5D;AAAA,EACF;AACA,QAAM,QAAmC,CAAC;AAC1C,QAAM,SAA8C,CAAC;AACrD,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,UAAM,SAAU,OAAO,KAAK,EAAqB;AACjD,QAAI,OAAO,WAAW;AACpB,YAAM,KAAK,GAAI,OAAO,MAAmB,OAAO,OAAO,EAAE,IAAI,CAAC,SAAS,OAAO,QAAQ,IAAI,CAAC,CAAC;AAAA;AAE5F,aAAO,KAAK;AAAA,QACV;AAAA,QACA,OAAO,aAAa,OAAO,MAAM;AAAA,MACnC,CAAC;AAAA,EACL,CAAC;AACD,SAAO,EAAE,CAAC,GAAG,GAAG,OAAO,GAAI,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAC9D;AAEA,eAAe,QACb,SACA,QACA,QACA,MAC4F;AAC5F,QAAM,aAAa,eAAe,QAAQ,QAAQ;AAClD,QAAM,MAAM,eAAe,QAAQ,KAAK;AACxC,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,yBAAyB;AAC1D,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sBAAsB;AAChD,QAAM,SAAS,KAAK,SAAS,UAAU;AACvC,QAAM,SAAS,MAAM,OAAO,WAAW,aAAa,KAAK;AAAA,IACvD,WAAW,OAAO,WAAW;AAAA,IAC7B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC7B,CAAC;AACD,QAAM,SAAS,OAAO,SAAS,QAAQ,CAACA,cAAa;AAAA,IACnD,GAAI,OAAO,SAAS,SAAS,IAAI,CAAC,EAAE,MAAM,QAAiB,MAAM,GAAGA,UAAS,GAAG,IAAI,CAAC,IAAI,CAAC;AAAA,IAC1F,EAAE,MAAM,YAAqB,UAAUA,UAAS;AAAA,EAClD,CAAC;AACD,QAAM,YAAY,MAAM,aAAa,OAAO,MAAM,QAAQ,IAAI;AAC9D,QAAM,UAAU,MAAM;AAAA,IACpB,UAAU,SAAS,YAAY,CAAC,EAAE,MAAM,QAAQ,MAAM,YAAY,GAAG,aAAa,CAAC;AAAA,IACnF;AAAA,EACF;AACA,QAAM,WAAW,OAAO,SAAS,IAAI,CAAC,SAAS;AAC7C,UAAM,EAAE,OAAO,UAAU,GAAG,KAAK,IAAI;AACrC,WAAO;AAAA,EACT,CAAC;AACD,SAAO,EAAE,GAAG,SAAS,SAAS,EAAE,QAAQ,OAAO,MAAM,KAAK,SAAS,EAAE;AACvE;AAOO,SAAS,cAAc,SAAyC;AACrE,QAAM,WAAW,CAAC,MAAc,QAC9B,WAAW;AAAA,IACT;AAAA,IACA,aAAa,aAAa,IAAI;AAAA,IAC9B,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,MAAM,QAAQ,MAAM,KAAK,SAAS;AAChC,YAAM,UAAU,MAAM,QAAQ,MAAM,QAAQ,KAAK,OAAO,GAAG,MAAM,SAAS,OAAO,GAAG,GAAG;AACvF,YAAM,UAAU,MAAM;AAAA,QACpB,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,EAAE,CAAC;AAAA,QAChD,SAAS,KAAK,IAAI,OAAO;AAAA,MAC3B;AACA,aAAO,EAAE,SAAS,QAAQ,QAAQ;AAAA,IACpC;AAAA,EACF,CAAC;AACH,SAAO;AAAA,IACL,SAAS,oBAAoB,WAAW;AAAA,IACxC,SAAS,6BAA6B,mBAAmB;AAAA,IACzD,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa,aAAa,iBAAiB;AAAA,MAC3C,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,MAAM,QAAQ,MAAM,KAAK,SAAS;AAChC,cAAM,OAAO,MAAM;AAAA,UACjB,MAAM,QAAQ,KAAK,OAAO;AAAA,UAC1B;AAAA,UACA,SAAS,OAAO;AAAA,UAChB,SAAS,KAAK,IAAI,OAAO;AAAA,QAC3B;AACA,eAAO,EAAE,SAAS,KAAK,QAAQ;AAAA,MACjC;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,SAAS,oBACd,SACA,QACA,MACgB;AAChB,QAAM,UAAU,CAAC,QAAsB,SAAS,YAAY,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI;AACjF,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,aAAa,aAAa,kBAAkB;AAAA,MAC5C,aAAa;AAAA,MACb,SAAS,CAAC,MAAM,EAAE,QAAQ,IAAI,MAAM,QAAQ,QAAQ,GAAG,MAAM,QAAQ,GAAG,GAAG,WAAW;AAAA,IACxF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa,aAAa,2BAA2B;AAAA,MACrD,aAAa;AAAA,MACb,SAAS,CAAC,MAAM,EAAE,QAAQ,IAAI,MAC5B,QAAQ,QAAQ,GAAG,MAAM,QAAQ,GAAG,GAAG,mBAAmB;AAAA,IAC9D;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa,aAAa,iBAAiB;AAAA,MAC3C,aAAa;AAAA,MACb,SAAS,OAAO,MAAM,EAAE,QAAQ,IAAI,OACjC,MAAM,QAAQ,QAAQ,GAAG,MAAM,QAAQ,GAAG,GAAG,IAAI,GAAG;AAAA,IACzD;AAAA,EACF;AACF;AAQA,SAAS,gBAAgB,WAAmB,OAAwB;AAClE,QAAM,KAAK,qBAAqB,SAAS;AACzC,QAAM,UAAU,qBAAqB,KAAK;AAC1C,QAAM,SAAS,CAAC,SAAiB,KAAK,MAAM,KAAK,YAAY,IAAI,IAAI,CAAC;AACtE,SACE,cAAc,SAAS,OAAO,WAAW,OAAO,SAAS,MAAM,SAAS,OAAO,EAAE,MAAM;AAE3F;AAEA,SAAS,MAAM,MAAwB;AACrC,SAAO,KACJ,YAAY,EACZ,MAAM,YAAY,EAClB,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AACrC;AAEO,SAAS,KACd,OACA,WACA,OACU;AACV,QAAM,QAAQ,MAAM,KAAK;AACzB,MAAI,CAAC,MAAM,OAAQ,QAAO,CAAC;AAC3B,QAAM,YAAY,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,MAAM,OAAO,MAAM,IAAI,IAAI,EAAE,EAAE;AACrF,QAAM,YAAY,oBAAI,IAAoB;AAC1C,aAAW,OAAO;AAChB,eAAW,QAAQ,IAAI,IAAI,IAAI,KAAK,EAAG,WAAU,IAAI,OAAO,UAAU,IAAI,IAAI,KAAK,KAAK,CAAC;AAC3F,QAAM,QAAQ,UAAU;AACxB,QAAM,SAAS,UAAU,IAAI,CAAC,QAAQ;AACpC,QAAI,QAAQ;AACZ,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,IAAI,MAAM,OAAO,CAAC,SAAS,SAAS,QAAQ,KAAK,WAAW,IAAI,CAAC,EAAE;AACjF,UAAI,CAAC,MAAO;AACZ,YAAM,KAAK,UAAU,IAAI,IAAI,KAAK;AAClC,YAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,KAAK,QAAQ,KAAK,IAAI;AACxD,eAAS,OAAQ,QAAQ,OAAQ,QAAQ,OAAO,OAAQ,OAAO,IAAI,MAAM,SAAU;AAAA,IACrF;AACA,WAAO,EAAE,MAAM,IAAI,MAAM,MAAM;AAAA,EACjC,CAAC;AACD,SAAO,OACJ,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,EAC7B,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,EAChE,MAAM,GAAG,KAAK,EACd,IAAI,CAAC,QAAQ,IAAI,IAAI;AAC1B;AAEO,MAAM,uBAAuB;AAE7B,SAAS,iBACd,OACA,aACgB;AAChB,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,iBAAiB,IAAI,CAAC,CAAC,CAAC;AAChF,QAAM,QAAQ,CAAC,UAAkB;AAAA,IAC/B,MAAM,qBAAqB,IAAI;AAAA,IAC/B,aAAa,QAAQ,IAAI,IAAI;AAAA,EAC/B;AACA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,CAAC,SAAS;AACjB,cAAM,CAAC,OAAO,OAAO,IAAI;AAIzB,YAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,sCAAsC;AACrF,cAAM,QAAQ,SAAS,SAAS;AAChC,YAAI,OAAO,UAAU,YAAY,CAAC,OAAO,UAAU,KAAK,KAAK,SAAS;AACpE,gBAAM,IAAI,MAAM,gDAAgD;AAClE,cAAM,YAAY,SAAS;AAC3B,YAAI,cAAc,UAAa,cAAc,QAAQ,OAAO,cAAc;AACxE,gBAAM,IAAI,MAAM,0CAA0C;AAC5D,cAAM,YAAY,MAAM,QAAQ,CAAC,SAAS;AACxC,gBAAM,QAAQ,YAAY,KAAK,IAAI;AACnC,cAAI,cAAc,CAAC,SAAS,CAAC,gBAAgB,MAAM,MAAM,SAAS,GAAI,QAAO,CAAC;AAC9E,iBAAO;AAAA,YACL;AAAA,cACE,MAAM,KAAK;AAAA,cACX,MAAM;AAAA,gBACJ,KAAK,KAAK,QAAQ,MAAM,GAAG;AAAA,gBAC3B,KAAK;AAAA,gBACL,OAAO,eAAe;AAAA,cACxB,EAAE,KAAK,GAAG;AAAA,YACZ;AAAA,UACF;AAAA,QACF,CAAC;AACD,eAAO,KAAK,OAAO,WAAW,KAAK,EAAE,IAAI,KAAK;AAAA,MAChD;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,CAAC,SAAS;AACjB,cAAM,CAAC,IAAI,IAAI;AACf,YAAI,OAAO,SAAS,SAAU,OAAM,IAAI,MAAM,oCAAoC;AAClF,cAAM,OAAO,MAAM;AAAA,UACjB,CAAC,cAAc,UAAU,SAAS,QAAQ,qBAAqB,UAAU,IAAI,MAAM;AAAA,QACrF;AACA,eAAO,OAAO,QAAQ,IAAI,KAAK,IAAI,IAAI;AAAA,MACzC;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,CAAC,SAAS;AACjB,cAAM,CAAC,IAAI,IAAI;AACf,YAAI,OAAO,SAAS;AAClB,gBAAM,IAAI,MAAM,8CAA8C;AAChE,YAAI;AACJ,cAAM,QAAkB,CAAC;AACzB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,QAAQ,YAAY,KAAK,IAAI;AACnC,cAAI,CAAC,SAAS,CAAC,gBAAgB,MAAM,MAAM,IAAI,EAAG;AAClD,oBAAU;AACV,gBAAM,KAAK,qBAAqB,KAAK,IAAI,CAAC;AAAA,QAC5C;AACA,YAAI,CAAC,MAAO,QAAO;AACnB,eAAO;AAAA,UACL,MAAM,MAAM;AAAA,UACZ,GAAI,MAAM,cAAc,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;AAAA,UAC9D,GAAI,MAAM,eAAe,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;AAAA,UACjE,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
4
|
+
"sourcesContent": ["import type { Context, JsonValue } from '@earendil-works/chord';\nimport {\n type CodemodeJsonSchema,\n type CodemodeTool,\n renderToolSample,\n toCodemodeIdentifier,\n} from '@earendil-works/pi-codemode';\nimport {\n defineTool,\n type ToolExecutionApi,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { McpRequestOptions, Tool as McpTool } from '@earendil-works/pi-mcp';\nimport { scrubbedOutput } from '../secrets/scrub.ts';\nimport type { McpExposure } from './config.ts';\nimport { errorMessage, type McpServerConnection } from './connection.ts';\nimport {\n type Content,\n convertResult,\n limitContent,\n modelContent,\n READ_MCP_RESOURCE,\n type Saver,\n scriptResult,\n spiller,\n} from './content.ts';\n\nexport const LIST_MCP_RESOURCES = 'list_mcp_resources';\nexport const LIST_MCP_RESOURCE_TEMPLATES = 'list_mcp_resource_templates';\nexport { READ_MCP_RESOURCE };\n\nexport interface McpToolEntry {\n server: string;\n name: string;\n exposure: McpExposure;\n tool: McpTool;\n connection: McpServerConnection;\n}\n\nexport type McpDetails = { server: string; tool: string; fullOutputPath?: string };\n\nfunction parametersOf(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _schema, ...rest } = schema;\n return {\n ...rest,\n type: rest.type ?? 'object',\n ...(rest.properties === undefined ? { properties: {} } : {}),\n };\n}\n\nexport function resultSchema(structured: Record<string, unknown> | undefined): CodemodeJsonSchema {\n return {\n type: 'object',\n properties: {\n content: { type: 'array', items: { type: 'object' } },\n ...(structured ? { structuredContent: structured } : {}),\n isError: { type: 'boolean' },\n },\n required: ['content'],\n };\n}\n\nfunction describe(entry: McpToolEntry): string {\n const title = entry.tool.title ?? entry.tool.annotations?.title;\n return (\n entry.tool.description?.trim() ||\n title ||\n `MCP tool ${entry.tool.name} from server ${entry.server}`\n );\n}\n\nfunction signalOf(context: Context): AbortSignal | undefined {\n return context.abortSignal as AbortSignal | undefined;\n}\n\nexport function apiSaver(api: ToolExecutionApi, prefix: string, context: Context): Saver {\n let counter = 0;\n return spiller(\n async (path, data, using) => (await api.env?.writeFile(path, data, using))?.ok === true,\n () => `${api.callId}-${prefix}${++counter}`,\n context\n );\n}\n\nfunction progressText(progress: { progress: number; total?: number; message?: string }): string {\n const total = progress.total === undefined ? '' : `/${progress.total}`;\n return progress.message ?? `Progress ${progress.progress}${total}`;\n}\n\nfunction failure(error: unknown): never {\n throw new Error(error instanceof Error ? error.message : String(error));\n}\n\nexport function directTool(entry: McpToolEntry, readable: () => boolean): ToolRegistration {\n return defineTool({\n name: entry.name,\n description: describe(entry),\n parameters: parametersOf(entry.tool.inputSchema) as never,\n replay: 'unsafe',\n async execute(args, api, context) {\n const save = apiSaver(api, '', context);\n const options: McpRequestOptions = {\n timeoutMs: entry.connection.timeoutMs,\n onProgress: (progress) => void scrubbedOutput(api, `${progressText(progress)}\\n`),\n };\n const signal = signalOf(context);\n if (signal) options.signal = signal;\n const result = await entry.connection\n .callTool(entry.tool.name, args as Record<string, unknown>, options)\n .catch(failure);\n const converted = await convertResult(\n entry.server,\n entry.tool.name,\n result,\n save,\n readable()\n );\n const details: McpDetails = {\n server: entry.server,\n tool: entry.tool.name,\n ...(converted.fullOutputPath ? { fullOutputPath: converted.fullOutputPath } : {}),\n };\n return {\n content: converted.content,\n details: details as JsonValue,\n ...(converted.isError ? { isError: true } : {}),\n };\n },\n }) as ToolRegistration;\n}\n\nexport function scriptTool(entry: McpToolEntry, signal: AbortSignal | undefined): CodemodeTool {\n const inputSchema = parametersOf(entry.tool.inputSchema);\n const outputSchema = resultSchema(entry.tool.outputSchema as Record<string, unknown> | undefined);\n return {\n name: entry.name,\n description: describe(entry),\n inputSchema,\n outputSchema,\n async execute(args, { signal: own }) {\n const combined = signal ? AbortSignal.any([own, signal]) : own;\n const result = await entry.connection\n .callTool(entry.tool.name, (args ?? {}) as Record<string, unknown>, {\n signal: combined,\n timeoutMs: entry.connection.timeoutMs,\n })\n .catch(failure);\n return scriptResult(result);\n },\n };\n}\n\nexport interface ResourceServer {\n name: string;\n connection: McpServerConnection;\n}\n\nfunction stringArgument(params: unknown, key: string): string | undefined {\n const value = (params as Record<string, unknown> | undefined)?.[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string') throw new Error(`${key} must be a string`);\n return value.trim() || undefined;\n}\n\nfunction listed(server: string, item: object): Record<string, unknown> {\n const { _meta, icons: _icons, ...rest } = item as Record<string, unknown>;\n return { server, ...rest };\n}\n\nconst stringProperty = (description: string) => ({ type: 'string', description });\nconst LIST_PARAMETERS = {\n type: 'object',\n properties: {\n server: stringProperty('MCP server name. Omit to list every server with resources.'),\n cursor: stringProperty(\n 'Opaque cursor from a previous call with the same server; omit for the first page.'\n ),\n },\n additionalProperties: false,\n};\nconst READ_PARAMETERS = {\n type: 'object',\n properties: {\n server: stringProperty(\n \"MCP server name exactly as configured. Must match the 'server' field returned by list_mcp_resources.\"\n ),\n uri: stringProperty(\n 'Resource URI to read. Must be one of the URIs returned by list_mcp_resources.'\n ),\n },\n required: ['server', 'uri'],\n additionalProperties: false,\n};\n\nconst DESCRIPTIONS: Record<string, string> = {\n [LIST_MCP_RESOURCES]:\n 'Lists resources provided by MCP servers. Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Prefer resources over web search when possible.',\n [LIST_MCP_RESOURCE_TEMPLATES]:\n 'Lists resource templates provided by MCP servers. Parameterized resource templates allow servers to share data that takes parameters and provides context to language models, such as files, database schemas, or application-specific information. Prefer resource templates over web search when possible.',\n [READ_MCP_RESOURCE]:\n 'Read a specific resource from an MCP server given the server name and resource URI.',\n};\n\ntype Key = 'resources' | 'resourceTemplates';\n\nfunction find(servers: readonly ResourceServer[], name: string): ResourceServer {\n const server = servers.find((candidate) => candidate.name === name);\n if (server) return server;\n const available = servers.map((candidate) => candidate.name).join(', ');\n throw new Error(\n `MCP server \"${name}\" has no resources${available ? `. Servers with resources: ${available}` : ''}`\n );\n}\n\nasync function listing(\n servers: readonly ResourceServer[],\n params: unknown,\n signal: AbortSignal | undefined,\n key: Key\n): Promise<Record<string, unknown>> {\n const serverName = stringArgument(params, 'server');\n const cursor = stringArgument(params, 'cursor');\n const visible = (item: object) => {\n const { uri, uriTemplate, mimeType } = item as Record<string, string | undefined>;\n const target = uri ?? uriTemplate ?? '';\n return !(target.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(mimeType ?? ''));\n };\n const options = (server: ResourceServer): McpRequestOptions => ({\n timeoutMs: server.connection.timeoutMs,\n ...(signal ? { signal } : {}),\n });\n if (serverName) {\n const server = find(servers, serverName);\n const page =\n key === 'resources'\n ? await server.connection.resourcesPage(cursor, options(server)).then((result) => ({\n items: result.resources as object[],\n nextCursor: result.nextCursor,\n }))\n : await server.connection.resourceTemplatesPage(cursor, options(server)).then((result) => ({\n items: result.resourceTemplates as object[],\n nextCursor: result.nextCursor,\n }));\n return {\n server: server.name,\n [key]: page.items.filter(visible).map((item) => listed(server.name, item)),\n ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),\n };\n }\n if (cursor) throw new Error('cursor can only be used when a server is specified');\n const sorted = [...servers].sort((a, b) => a.name.localeCompare(b.name));\n const results = await Promise.allSettled(\n sorted.map((server) =>\n key === 'resources'\n ? server.connection.allResources(options(server))\n : server.connection.allResourceTemplates(options(server))\n )\n );\n const items: Record<string, unknown>[] = [];\n const errors: { server: string; error: string }[] = [];\n results.forEach((result, index) => {\n const server = (sorted[index] as ResourceServer).name;\n if (result.status === 'fulfilled')\n items.push(...(result.value as object[]).filter(visible).map((item) => listed(server, item)));\n else\n errors.push({\n server,\n error: errorMessage(result.reason),\n });\n });\n return { [key]: items, ...(errors.length ? { errors } : {}) };\n}\n\nasync function reading(\n servers: readonly ResourceServer[],\n params: unknown,\n signal: AbortSignal | undefined,\n save: Saver\n): Promise<{ content: Content[]; payload: Record<string, unknown>; fullOutputPath?: string }> {\n const serverName = stringArgument(params, 'server');\n const uri = stringArgument(params, 'uri');\n if (!serverName) throw new Error('server must be provided');\n if (!uri) throw new Error('uri must be provided');\n const server = find(servers, serverName);\n const result = await server.connection.readResource(uri, {\n timeoutMs: server.connection.timeoutMs,\n ...(signal ? { signal } : {}),\n });\n const blocks = result.contents.flatMap((contents) => [\n ...(result.contents.length > 1 ? [{ type: 'text' as const, text: `${contents.uri}:` }] : []),\n { type: 'resource' as const, resource: contents },\n ]);\n const converted = await modelContent(server.name, blocks, save);\n const limited = await limitContent(\n converted.length ? converted : [{ type: 'text', text: `Resource ${uri} is empty.` }],\n save\n );\n const contents = result.contents.map((item) => {\n const { _meta: _ignored, ...rest } = item as typeof item & { _meta?: unknown };\n return rest;\n });\n return { ...limited, payload: { server: server.name, uri, contents } };\n}\n\nexport type ServersFor = (\n api: ToolExecutionApi,\n context: Context\n) => Promise<readonly ResourceServer[]>;\n\nexport function resourceTools(servers: ServersFor): ToolRegistration[] {\n const listTool = (name: string, key: Key) =>\n defineTool({\n name,\n description: DESCRIPTIONS[name] as string,\n parameters: LIST_PARAMETERS as never,\n replay: 'safe',\n async execute(args, api, context) {\n const payload = await listing(await servers(api, context), args, signalOf(context), key);\n const limited = await limitContent(\n [{ type: 'text', text: JSON.stringify(payload) }],\n apiSaver(api, '', context)\n );\n return { content: limited.content };\n },\n }) as ToolRegistration;\n return [\n listTool(LIST_MCP_RESOURCES, 'resources'),\n listTool(LIST_MCP_RESOURCE_TEMPLATES, 'resourceTemplates'),\n defineTool({\n name: READ_MCP_RESOURCE,\n description: DESCRIPTIONS[READ_MCP_RESOURCE] as string,\n parameters: READ_PARAMETERS as never,\n replay: 'safe',\n async execute(args, api, context) {\n const read = await reading(\n await servers(api, context),\n args,\n signalOf(context),\n apiSaver(api, '', context)\n );\n return { content: read.content };\n },\n }) as ToolRegistration,\n ];\n}\n\nexport function resourceScriptTools(\n servers: () => readonly ResourceServer[],\n signal: AbortSignal | undefined,\n save: Saver\n): CodemodeTool[] {\n const combine = (own: AbortSignal) => (signal ? AbortSignal.any([own, signal]) : own);\n return [\n {\n name: LIST_MCP_RESOURCES,\n description: DESCRIPTIONS[LIST_MCP_RESOURCES] as string,\n inputSchema: LIST_PARAMETERS,\n execute: (args, { signal: own }) => listing(servers(), args, combine(own), 'resources'),\n },\n {\n name: LIST_MCP_RESOURCE_TEMPLATES,\n description: DESCRIPTIONS[LIST_MCP_RESOURCE_TEMPLATES] as string,\n inputSchema: LIST_PARAMETERS,\n execute: (args, { signal: own }) =>\n listing(servers(), args, combine(own), 'resourceTemplates'),\n },\n {\n name: READ_MCP_RESOURCE,\n description: DESCRIPTIONS[READ_MCP_RESOURCE] as string,\n inputSchema: READ_PARAMETERS,\n execute: async (args, { signal: own }) =>\n (await reading(servers(), args, combine(own), save)).payload,\n },\n ];\n}\n\nexport interface Namespace {\n name: string;\n description?: string;\n instructions?: string;\n}\n\nfunction isNamespaceName(namespace: string, query: string): boolean {\n const id = toCodemodeIdentifier(namespace);\n const queryId = toCodemodeIdentifier(query);\n const suffix = (name: string) => name.slice(name.lastIndexOf('__') + 2);\n return (\n namespace === query || id === queryId || suffix(namespace) === query || suffix(id) === queryId\n );\n}\n\nfunction words(text: string): string[] {\n return text\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((word) => word.length > 1);\n}\n\nexport function rank(\n query: string,\n documents: readonly { name: string; text: string }[],\n limit: number\n): string[] {\n const terms = words(query);\n if (!terms.length) return [];\n const tokenized = documents.map((doc) => ({ name: doc.name, words: words(doc.text) }));\n const frequency = new Map<string, number>();\n for (const doc of tokenized)\n for (const term of new Set(doc.words)) frequency.set(term, (frequency.get(term) ?? 0) + 1);\n const total = tokenized.length;\n const scored = tokenized.map((doc) => {\n let score = 0;\n for (const term of terms) {\n const count = doc.words.filter((word) => word === term || word.startsWith(term)).length;\n if (!count) continue;\n const df = frequency.get(term) ?? 0;\n const idf = Math.log(1 + (total - df + 0.5) / (df + 0.5));\n score += idf * ((count * 2.2) / (count + 1.2 * (0.25 + (0.75 * doc.words.length) / 20)));\n }\n return { name: doc.name, score };\n });\n return scored\n .filter((doc) => doc.score > 0)\n .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))\n .slice(0, limit)\n .map((doc) => doc.name);\n}\n\nexport const DEFAULT_SEARCH_LIMIT = 8;\n\nexport function discoveryGlobals(\n tools: readonly CodemodeTool[],\n namespaceOf: (tool: string) => Namespace | undefined\n): CodemodeTool[] {\n const samples = new Map(tools.map((tool) => [tool.name, renderToolSample(tool)]));\n const entry = (name: string) => ({\n name: toCodemodeIdentifier(name),\n description: samples.get(name) as string,\n });\n return [\n {\n name: 'searchTools',\n spread: true,\n execute: (args) => {\n const [query, options] = args as [\n unknown,\n { limit?: unknown; namespace?: unknown } | undefined,\n ];\n if (typeof query !== 'string') throw new Error('searchTools() expects a query string');\n const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0)\n throw new Error('searchTools() limit must be a positive integer');\n const namespace = options?.namespace;\n if (namespace !== undefined && namespace !== null && typeof namespace !== 'string')\n throw new Error('searchTools() namespace must be a string');\n const documents = tools.flatMap((tool) => {\n const space = namespaceOf(tool.name);\n if (namespace && (!space || !isNamespaceName(space.name, namespace))) return [];\n return [\n {\n name: tool.name,\n text: [\n tool.name.replace(/_/g, ' '),\n tool.description as string,\n space?.description ?? '',\n ].join(' '),\n },\n ];\n });\n return rank(query, documents, limit).map(entry);\n },\n },\n {\n name: 'describeTool',\n spread: true,\n execute: (args) => {\n const [name] = args as unknown[];\n if (typeof name !== 'string') throw new Error('describeTool() expects a tool name');\n const tool = tools.find(\n (candidate) => candidate.name === name || toCodemodeIdentifier(candidate.name) === name\n );\n return tool ? samples.get(tool.name) : undefined;\n },\n },\n {\n name: 'describeNamespace',\n spread: true,\n execute: (args) => {\n const [name] = args as unknown[];\n if (typeof name !== 'string')\n throw new Error('describeNamespace() expects a namespace name');\n let found: Namespace | undefined;\n const names: string[] = [];\n for (const tool of tools) {\n const space = namespaceOf(tool.name);\n if (!space || !isNamespaceName(space.name, name)) continue;\n found ??= space;\n names.push(toCodemodeIdentifier(tool.name));\n }\n if (!found) return undefined;\n return {\n name: found.name,\n ...(found.description ? { description: found.description } : {}),\n ...(found.instructions ? { instructions: found.instructions } : {}),\n tools: names,\n };\n },\n },\n ];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["contents"]
|
|
7
7
|
}
|
package/dist/models/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const USAGE = `usage:
|
|
|
8
8
|
levels: ${LEVELS}
|
|
9
9
|
In an agent's bash, models switches that cone or scoop; in a terminal, the active cone.
|
|
10
10
|
`;
|
|
11
|
+
const LOCKED_NOTE = "the vault is locked: unlock it to use these accounts";
|
|
11
12
|
const ok = (out) => ({ code: 0, out });
|
|
12
13
|
const fail = (out, code = 1) => ({ code, out: `models: ${out}
|
|
13
14
|
` });
|
|
@@ -78,14 +79,15 @@ function missing(state, id) {
|
|
|
78
79
|
);
|
|
79
80
|
return connected ? `current: ${id}, which its account no longer offers` : `current: ${id}, whose account is not connected`;
|
|
80
81
|
}
|
|
81
|
-
function list(state, current, json) {
|
|
82
|
+
function list(state, current, json, locked) {
|
|
82
83
|
const listed = rows(state, current);
|
|
83
84
|
if (json) return ok(`${JSON.stringify(listed, null, 2)}
|
|
84
85
|
`);
|
|
85
86
|
const lines = listed.map(
|
|
86
87
|
(row) => `${row.current ? "*" : " "} ${row.id} ${row.label} ${row.account}`
|
|
87
88
|
);
|
|
88
|
-
if (
|
|
89
|
+
if (locked) lines.push(LOCKED_NOTE);
|
|
90
|
+
if (!listed.length) lines.push("no models: connect an account in Settings \u203A Accounts");
|
|
89
91
|
if (current.id && !listed.some((row) => row.current)) lines.push(missing(state, current.id));
|
|
90
92
|
return ok(`${lines.join("\n")}
|
|
91
93
|
`);
|
|
@@ -109,7 +111,7 @@ function refuse(options, state, wanted) {
|
|
|
109
111
|
const known = options.catalog.getModels(providerId);
|
|
110
112
|
if (disconnected && (!known.length || known.some((model) => model.id === modelId)))
|
|
111
113
|
return fail(
|
|
112
|
-
`${wanted} needs the ${disconnected.provider} account; connect it in Settings, then try again`
|
|
114
|
+
`${wanted} needs the ${disconnected.provider} account; connect it in Settings \u203A Accounts, then try again`
|
|
113
115
|
);
|
|
114
116
|
const ids = [
|
|
115
117
|
...state.models.map((choice) => choice.id),
|
|
@@ -168,13 +170,14 @@ function modelsCommand(options) {
|
|
|
168
170
|
await conversationOf(options.agents, caller, context),
|
|
169
171
|
context
|
|
170
172
|
);
|
|
171
|
-
return listing ? list(state, current, json) : show(state, current, json);
|
|
173
|
+
return listing ? list(state, current, json, options.locked?.() ?? false) : show(state, current, json);
|
|
172
174
|
} catch (error) {
|
|
173
175
|
return fail(error.message);
|
|
174
176
|
}
|
|
175
177
|
};
|
|
176
178
|
}
|
|
177
179
|
export {
|
|
180
|
+
LOCKED_NOTE,
|
|
178
181
|
USAGE,
|
|
179
182
|
closest,
|
|
180
183
|
modelsCommand
|