@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/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, type AgentOptions, openAgent } from './agent.ts';\nimport { browserGuard } from './browser/guard.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport { agentUsers, warnSupport } from './kernel/agent-users.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { AGENT_DIR, ROOT_HOME } from './layout.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { lickTriage } from './licks/triage.ts';\nimport { setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { pageOnly, type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { createPages } from './remote/pages.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport { repin, secretsExtension, shellGrants } from './secrets/extension.ts';\nimport { secretNames } from './secrets/names.ts';\nimport { scrubWith } from './secrets/scrub.ts';\nimport { listenVault, Vault } from './secrets/vault.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n mcpRedirect?: string;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n const users = usersOf(admin, options.users, attach);\n await warnSupport(users, client, options.users);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\n return { client, users, home, at: { home: HOME, ...(users ? { agentDir: AGENT_DIR } : {}) } };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra'],\n dir = `${HOME}/.pi/agent`\n) {\n const settings = await client.fs.readFile(`${dir}/settings.json`).catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nexport function seesImages(models: Pick<Models, 'getModel'>, ref: ModelRef | undefined): boolean {\n const found = ref && models.getModel(ref.provider, ref.modelId);\n return found ? found.input.includes('image') : true;\n}\n\nexport const MCP_CALLBACK = '/oauth/mcp-callback';\n\nasync function openRepinned(\n options: AgentWorkerOptions,\n base: Omit<AgentOptions, 'storage' | 'beforeResume'>\n): Promise<Agent> {\n const storage = await (options.storage ?? (() => openOpfsSqliteStorage()))();\n return openAgent({\n ...base,\n storage,\n beforeResume: (harness, using) => repin(harness, storage, using),\n });\n}\n\nasync function secretsFor(home: SliccKernelEnv, vault: Vault) {\n scrubWith(vault);\n const names = secretNames(home, vault);\n await names.refresh(BACKGROUND_CONTEXT);\n return names;\n}\n\nasync function modelsOf(options: AgentWorkerOptions, version: string) {\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders(version);\n return { credentials, providers, models: createSliccModels(credentials, providers) };\n}\n\nfunction baseRegistry(facts: Awaited<ReturnType<typeof factsOf>>) {\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n const read = (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration;\n registry.install(imageReads(read));\n return registry;\n}\n\nasync function factsOf(client: KernelClient & { transport: Transport }, native: typeof fetch) {\n return {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n browser: await browserConnected(client),\n };\n}\n\nfunction routeFetch(scope: AgentWorkerScope, client: KernelClient & { transport: Transport }) {\n const native = scope.fetch.bind(scope);\n const loopback = client.loopbackFetch?.bind(client);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native, loopback);\n return native;\n}\n\nasync function prepare(\n scope: AgentWorkerScope,\n kernel: { client: KernelClient & { transport: Transport }; home: SliccKernelEnv; vault: Vault },\n options: AgentWorkerOptions\n) {\n const names = await secretsFor(kernel.home, kernel.vault);\n const native = routeFetch(scope, kernel.client);\n const facts = await factsOf(kernel.client, native);\n return {\n names,\n native,\n facts,\n ...(await modelsOf(options, facts.version)),\n registry: baseRegistry(facts),\n };\n}\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions,\n vault: Vault\n): Promise<AgentHost> {\n const { client, users, home, at } = await attached(ports, options);\n const ready = await prepare(scope, { client, home, vault }, options);\n const { names, native, facts, credentials, providers, models, registry } = ready;\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n registry.install(browserGuard([scope.location.origin], coding));\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode, at.agentDir);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n licks.licks.triage(lickTriage(models));\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n registry.install(secretsExtension({ names, vault }));\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const people = users ? agentUsers(users, client, home, () => opened?.agents) : undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n scrub: vault,\n spawn: shellGrants(names),\n ...(people ? { route: people.route } : {}),\n });\n const agent = await openRepinned(options, { models, model, registry, env: environment });\n opened = agent;\n await people?.start(agent.agents);\n const sources = licks.attach(agent, {\n env: home,\n home: HOME,\n activity,\n ...(people ? { dirs: () => people.lickDirs() } : {}),\n });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n {\n env: home,\n ...at,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n ...(people ? { people: people.people } : {}),\n },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n ...at,\n credentials,\n licks: licks.licks,\n fetch: scope.fetch,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: pageOnly(client.transport),\n redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href,\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n ...(people?.sprinkles() ?? {}),\n },\n BACKGROUND_CONTEXT\n );\n people?.follow([sources, skillsRuntime, sprinkles]);\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n ...at,\n ...(people ? { home: ROOT_HOME } : {}),\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME, at.agentDir)),\n ...(people ? { layout: people.layout } : {}),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });\n const pages = createPages({ claim: sprinkles.claim });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n ...at,\n ...(people\n ? { layout: people.layout, spools: people.spools, enforced: people.enforced }\n : {}),\n alive: alive(client),\n reads: skillsRuntime.dirs,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsCommand({ agents: agent.agents, settings, catalog: models }),\n panel: pages.command,\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n ...(await people?.gelatiere(home)),\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n pages,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n secrets: { vault: vault.state, list: () => names.state.value, close: names.close },\n uploads: {\n env: environment,\n sees: (ref) => seesImages(models, ref),\n },\n monitor: {\n kernel: client,\n groups,\n ...(users ? { users: () => users.users.list() } : {}),\n secrets: () => mcpRuntime.secrets(),\n },\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const vault = new Vault();\n listenVault(scope, vault);\n const host = start(scope, kernelPorts(scope), options, vault);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAGnC;AAAA,EACE;AAAA,OAIK;AACP,SAAS,mBAAmB;AAC5B,SAAwC,iBAAiB;AACzD,SAAS,oBAAoB;AAC7B,SAAS,eAAe,kBAAkB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,
|
|
4
|
+
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Provider } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, type AgentOptions, openAgent } from './agent.ts';\nimport { browserGuard } from './browser/guard.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport { agentUsers, warnSupport } from './kernel/agent-users.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { AGENT_DIR, ROOT_HOME } from './layout.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { lickTriage } from './licks/triage.ts';\nimport { type McpAccounts, setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { pageOnly, type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { createPages } from './remote/pages.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport { vaultCredentials } from './secrets/credentials.ts';\nimport {\n BEDROCK_ENDPOINTS,\n type ModelBases,\n modelRoute,\n vaultEndpoints,\n} from './secrets/endpoints.ts';\nimport { repin, secretsExtension, shellGrants } from './secrets/extension.ts';\nimport { grantFetch, mcpFetch, vaultFetch } from './secrets/fetch.ts';\nimport { secretNames } from './secrets/names.ts';\nimport { type VaultRecords, vaultRecords } from './secrets/records.ts';\nimport { scrubWith } from './secrets/scrub.ts';\nimport { fileStore, type PlainStore } from './secrets/store.ts';\nimport { listenVault, Vault } from './secrets/vault.ts';\nimport {\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n providerFields,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n store?: () => Promise<PlainStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n mcpRedirect?: string;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\nexport const ACCOUNTS_FILE = `${STORE_DIR}/accounts.json`;\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n const users = usersOf(admin, options.users, attach);\n await warnSupport(users, client, options.users);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\n return { client, users, home, at: { home: HOME, ...(users ? { agentDir: AGENT_DIR } : {}) } };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra'],\n dir = `${HOME}/.pi/agent`\n) {\n const settings = await client.fs.readFile(`${dir}/settings.json`).catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nexport function seesImages(models: Pick<Models, 'getModel'>, ref: ModelRef | undefined): boolean {\n const found = ref && models.getModel(ref.provider, ref.modelId);\n return found ? found.input.includes('image') : true;\n}\n\nexport const MCP_CALLBACK = '/oauth/mcp-callback';\n\nasync function openRepinned(\n options: AgentWorkerOptions,\n base: Omit<AgentOptions, 'storage' | 'beforeResume'>\n): Promise<Agent> {\n const storage = await (options.storage ?? (() => openOpfsSqliteStorage()))();\n return openAgent({\n ...base,\n storage,\n beforeResume: (harness, using) => repin(harness, storage, using),\n });\n}\n\nasync function secretsFor(home: SliccKernelEnv, vault: Vault) {\n scrubWith(vault);\n const names = secretNames(home, vault);\n await names.refresh(BACKGROUND_CONTEXT);\n return names;\n}\n\nexport function modelBases(\n providers: readonly Provider[],\n models: Pick<ReturnType<typeof createSliccModels>, 'getModels'>\n): ModelBases[] {\n return providers.map((provider) => ({\n provider: provider.id,\n urls: [\n ...new Set(\n [\n provider.baseUrl,\n ...models.getModels(provider.id).map((model) => model.baseUrl),\n ...(provider.id === BEDROCK ? BEDROCK_ENDPOINTS : []),\n ].filter((url): url is string => typeof url === 'string' && url !== '')\n ),\n ],\n }));\n}\n\nfunction mcpVault(\n vault: Vault,\n made: { store: PlainStore; records: VaultRecords; endpoints: ReturnType<typeof vaultEndpoints> }\n) {\n const { store, records, endpoints } = made;\n return {\n store,\n vault: {\n records,\n fetch: (server: string, base: typeof fetch) => mcpFetch(vault, base, server),\n grant: (server: string, base: typeof fetch) => grantFetch(vault, server, base),\n servers: endpoints.servers,\n token: endpoints.token,\n },\n };\n}\n\nfunction hostSecrets(\n vault: Vault,\n names: Awaited<ReturnType<typeof secretsFor>>,\n parts: readonly { close(): void }[]\n) {\n return {\n vault: vault.state,\n list: () => names.state.value,\n close: () => {\n for (const part of parts) part.close();\n },\n };\n}\n\nasync function modelsOf(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions,\n given: { vault: Vault; home: SliccKernelEnv; transport: typeof fetch; version: string }\n) {\n const { vault, home, transport, version } = given;\n const store = await (options.store ?? (async () => fileStore(home, ACCOUNTS_FILE)))();\n const records = vaultRecords(vault);\n void records.refresh(BACKGROUND_CONTEXT).catch(() => undefined);\n const providers = options.providers ?? sliccProviders(version);\n const fields = await providerFields(store, providers);\n const models = createSliccModels(vaultCredentials(records, fields.env), providers);\n const bases = () => modelBases(providers, models);\n scope.fetch = vaultFetch(\n vault,\n transport,\n modelRoute(bases, () => records.state.value)\n );\n const endpoints = vaultEndpoints(vault);\n void endpoints.models(bases()).catch(() => undefined);\n return { store, records, providers, fields, models, bases, endpoints };\n}\n\nfunction baseRegistry(facts: Awaited<ReturnType<typeof factsOf>>) {\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n const read = (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration;\n registry.install(imageReads(read));\n return registry;\n}\n\nasync function factsOf(client: KernelClient & { transport: Transport }, native: typeof fetch) {\n return {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n browser: await browserConnected(client),\n };\n}\n\nfunction routeFetch(scope: AgentWorkerScope, client: KernelClient & { transport: Transport }) {\n const native = scope.fetch.bind(scope);\n const loopback = client.loopbackFetch?.bind(client);\n const transport = transportFetch(client.transport, scope.location.origin, native, loopback);\n scope.fetch = transport;\n return { native, transport };\n}\n\nasync function prepare(\n scope: AgentWorkerScope,\n kernel: { client: KernelClient & { transport: Transport }; home: SliccKernelEnv; vault: Vault },\n options: AgentWorkerOptions\n) {\n const names = await secretsFor(kernel.home, kernel.vault);\n const { native, transport } = routeFetch(scope, kernel.client);\n const facts = await factsOf(kernel.client, native);\n const made = await modelsOf(scope, options, {\n vault: kernel.vault,\n home: kernel.home,\n transport,\n version: facts.version,\n });\n return { names, native, transport, facts, ...made, registry: baseRegistry(facts) };\n}\n\nfunction modelsOfAgent(\n agent: Agent,\n settings: Awaited<ReturnType<typeof createAgentSettings>>,\n catalog: Models,\n vault: Vault\n) {\n const locked = () => vault.state.value.locked !== 'no';\n return modelsCommand({ agents: agent.agents, settings, catalog, locked });\n}\n\nasync function settingsOf(made: Awaited<ReturnType<typeof modelsOf>>, mcp: McpAccounts) {\n const { models, records, fields, providers, bases, endpoints } = made;\n const settings = await createAgentSettings(models, { records, fields }, providers, { mcp });\n settings.state.subscribe(() => void endpoints.models(bases()).catch(() => undefined));\n return settings;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions,\n vault: Vault\n): Promise<AgentHost> {\n const { client, users, home, at } = await attached(ports, options);\n const ready = await prepare(scope, { client, home, vault }, options);\n const { names, native, transport, facts, models, registry } = ready;\n const { records, endpoints } = ready;\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n registry.install(browserGuard([scope.location.origin], coding));\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode, at.agentDir);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n licks.licks.triage(lickTriage(models));\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n registry.install(secretsExtension({ names, vault }));\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const people = users ? agentUsers(users, client, home, () => opened?.agents) : undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n scrub: vault,\n spawn: shellGrants(names),\n ...(people ? { route: people.route } : {}),\n });\n const agent = await openRepinned(options, { models, model, registry, env: environment });\n opened = agent;\n await people?.start(agent.agents);\n const sources = licks.attach(agent, {\n env: home,\n home: HOME,\n activity,\n ...(people ? { dirs: () => people.lickDirs() } : {}),\n });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n {\n env: home,\n ...at,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n ...(people ? { people: people.people } : {}),\n },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n ...at,\n ...mcpVault(vault, ready),\n licks: licks.licks,\n fetch: transport,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: pageOnly(client.transport),\n redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href,\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n ...(people?.sprinkles() ?? {}),\n },\n BACKGROUND_CONTEXT\n );\n people?.follow([sources, skillsRuntime, sprinkles]);\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n ...at,\n ...(people ? { home: ROOT_HOME } : {}),\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME, at.agentDir)),\n ...(people ? { layout: people.layout } : {}),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await settingsOf(ready, mcpRuntime);\n const pages = createPages({ claim: sprinkles.claim });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n ...at,\n ...(people\n ? { layout: people.layout, spools: people.spools, enforced: people.enforced }\n : {}),\n alive: alive(client),\n reads: skillsRuntime.dirs,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsOfAgent(agent, settings, models, vault),\n panel: pages.command,\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n ...(await people?.gelatiere(home)),\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n pages,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n secrets: hostSecrets(vault, names, [names, records, endpoints]),\n uploads: {\n env: environment,\n sees: (ref) => seesImages(models, ref),\n },\n monitor: {\n kernel: client,\n groups,\n ...(users ? { users: () => users.users.list() } : {}),\n },\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const vault = new Vault();\n listenVault(scope, vault);\n const host = start(scope, kernelPorts(scope), options, vault);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/accounts.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
|
+
import { resolveHeaders, secretNames } from "./credentials.js";
|
|
2
3
|
import { MCP_ACCOUNT } from "./label.js";
|
|
3
4
|
import { beginSignIn, finishSignIn } from "./oauth.js";
|
|
5
|
+
const TOKENS_IN_VAULT = "MCP tokens are kept in seven's vault: update seven";
|
|
4
6
|
function kindOf(connection) {
|
|
5
7
|
return secretNames(connection.entry.config).length ? "token" : "oauth";
|
|
6
8
|
}
|
|
@@ -14,11 +16,12 @@ function row(connection, kind, stored) {
|
|
|
14
16
|
auth: kind === "token" ? "api-key" : "oauth",
|
|
15
17
|
needs: null,
|
|
16
18
|
group: "mcp",
|
|
19
|
+
store: "vault",
|
|
17
20
|
...kind === "token" ? { secret: "token" } : {}
|
|
18
21
|
};
|
|
19
22
|
}
|
|
20
23
|
function mcpAccounts(catalog, sources, restart, listeners) {
|
|
21
|
-
const {
|
|
24
|
+
const { records } = sources;
|
|
22
25
|
const find = (id, want) => {
|
|
23
26
|
const connection = id.startsWith(MCP_ACCOUNT) ? catalog.connections.get(id.slice(MCP_ACCOUNT.length)) : void 0;
|
|
24
27
|
const kind = connection && kindOf(connection);
|
|
@@ -28,13 +31,13 @@ function mcpAccounts(catalog, sources, restart, listeners) {
|
|
|
28
31
|
);
|
|
29
32
|
return { connection, kind };
|
|
30
33
|
};
|
|
31
|
-
const
|
|
34
|
+
const reread = () => records.refresh(BACKGROUND_CONTEXT).catch(() => void 0);
|
|
32
35
|
return {
|
|
33
36
|
async accounts() {
|
|
34
37
|
const rows = [];
|
|
35
38
|
for (const connection of catalog.connections.values()) {
|
|
36
39
|
const kind = kindOf(connection);
|
|
37
|
-
const stored = kind === "token" ?
|
|
40
|
+
const stored = kind === "token" ? resolveHeaders(connection.name, connection.entry.config, records).ok : await sources.oauth(connection.entry).signedIn();
|
|
38
41
|
if (kind === "oauth" && !stored && connection.state !== "needs-auth") continue;
|
|
39
42
|
rows.push(row(connection, kind, stored));
|
|
40
43
|
}
|
|
@@ -43,28 +46,28 @@ function mcpAccounts(catalog, sources, restart, listeners) {
|
|
|
43
46
|
async signIn(id) {
|
|
44
47
|
const { connection } = find(id, "oauth");
|
|
45
48
|
return beginSignIn(sources.oauth(connection.entry), connection.entry, {
|
|
46
|
-
fetch: sources.fetch
|
|
49
|
+
fetch: sources.fetch(connection.name),
|
|
50
|
+
register: (tokenUrl) => sources.register(connection.name, tokenUrl)
|
|
47
51
|
});
|
|
48
52
|
},
|
|
49
53
|
async connect(id, secret) {
|
|
50
54
|
const { connection, kind } = find(id);
|
|
51
|
-
if (kind === "oauth")
|
|
55
|
+
if (kind === "oauth") {
|
|
56
|
+
if (secret === null) throw new Error(`${id} needs a sign-in`);
|
|
52
57
|
await finishSignIn(
|
|
53
58
|
sources.oauth(connection.entry),
|
|
54
59
|
connection.entry,
|
|
55
60
|
secret,
|
|
56
|
-
sources.
|
|
61
|
+
sources.grant(connection.name)
|
|
57
62
|
);
|
|
58
|
-
else
|
|
59
|
-
|
|
60
|
-
type: "api_key",
|
|
61
|
-
key: secret
|
|
62
|
-
}));
|
|
63
|
+
} else if (secret !== null) throw new Error(TOKENS_IN_VAULT);
|
|
64
|
+
await reread();
|
|
63
65
|
restart(connection);
|
|
64
66
|
},
|
|
65
67
|
async disconnect(id) {
|
|
66
|
-
const { connection } = find(id);
|
|
67
|
-
|
|
68
|
+
const { connection, kind } = find(id);
|
|
69
|
+
if (kind === "oauth") await sources.oauth(connection.entry).forget();
|
|
70
|
+
await reread();
|
|
68
71
|
restart(connection);
|
|
69
72
|
},
|
|
70
73
|
onChange(listener) {
|
|
@@ -74,6 +77,7 @@ function mcpAccounts(catalog, sources, restart, listeners) {
|
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
79
|
export {
|
|
80
|
+
TOKENS_IN_VAULT,
|
|
77
81
|
mcpAccounts
|
|
78
82
|
};
|
|
79
83
|
//# sourceMappingURL=accounts.js.map
|
package/dist/mcp/accounts.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/accounts.ts"],
|
|
4
|
-
"sourcesContent": ["import type {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { McpFetch } from '@earendil-works/pi-mcp';\nimport type { AccountState } from '../services.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport type { McpServerEntry } from './config.ts';\nimport type { McpServerConnection } from './connection.ts';\nimport { type McpRecords, resolveHeaders, secretNames } from './credentials.ts';\nimport { MCP_ACCOUNT } from './label.ts';\nimport { beginSignIn, finishSignIn, type McpSignIn, type StoredOAuth } from './oauth.ts';\n\nexport const TOKENS_IN_VAULT = \"MCP tokens are kept in seven's vault: update seven\";\n\nexport interface McpAccounts {\n accounts(): Promise<AccountState[]>;\n signIn(id: string): Promise<McpSignIn>;\n connect(id: string, secret: string | null): Promise<void>;\n disconnect(id: string): Promise<void>;\n onChange(listener: () => void): () => void;\n}\n\nexport interface AccountSources {\n records: McpRecords & { refresh(context: Context): Promise<void> };\n oauth: (entry: McpServerEntry) => StoredOAuth;\n fetch: (server: string) => McpFetch;\n grant: (server: string) => McpFetch;\n register: (server: string, tokenUrl: string) => Promise<void>;\n}\n\ntype Kind = 'token' | 'oauth';\n\nfunction kindOf(connection: McpServerConnection): Kind {\n return secretNames(connection.entry.config).length ? 'token' : 'oauth';\n}\n\nfunction row(connection: McpServerConnection, kind: Kind, stored: boolean): AccountState {\n const rejected = connection.state === 'needs-auth';\n return {\n id: `${MCP_ACCOUNT}${connection.name}`,\n provider: connection.name,\n identity: new URL(connection.entry.config.url).host,\n status:\n rejected && (kind === 'token' || stored)\n ? 'expired'\n : stored && connection.live\n ? 'connected'\n : 'disconnected',\n auth: kind === 'token' ? 'api-key' : 'oauth',\n needs: null,\n group: 'mcp',\n store: 'vault',\n ...(kind === 'token' ? { secret: 'token' as const } : {}),\n };\n}\n\nexport function mcpAccounts(\n catalog: McpCatalog,\n sources: AccountSources,\n restart: (connection: McpServerConnection) => void,\n listeners: Set<() => void>\n): McpAccounts {\n const { records } = sources;\n const find = (id: string, want?: Kind) => {\n const connection = id.startsWith(MCP_ACCOUNT)\n ? catalog.connections.get(id.slice(MCP_ACCOUNT.length))\n : undefined;\n const kind = connection && kindOf(connection);\n if (!connection || (want && kind !== want))\n throw new Error(\n `${id} is not an MCP server that ${want === 'oauth' ? 'signs in' : 'takes a token or a sign-in'}`\n );\n return { connection, kind: kind as Kind };\n };\n const reread = () => records.refresh(BACKGROUND_CONTEXT).catch(() => undefined);\n return {\n async accounts() {\n const rows: AccountState[] = [];\n for (const connection of catalog.connections.values()) {\n const kind = kindOf(connection);\n const stored =\n kind === 'token'\n ? resolveHeaders(connection.name, connection.entry.config, records).ok\n : await sources.oauth(connection.entry).signedIn();\n if (kind === 'oauth' && !stored && connection.state !== 'needs-auth') continue;\n rows.push(row(connection, kind, stored));\n }\n return rows;\n },\n async signIn(id) {\n const { connection } = find(id, 'oauth');\n return beginSignIn(sources.oauth(connection.entry), connection.entry, {\n fetch: sources.fetch(connection.name),\n register: (tokenUrl) => sources.register(connection.name, tokenUrl),\n });\n },\n async connect(id, secret) {\n const { connection, kind } = find(id);\n if (kind === 'oauth') {\n if (secret === null) throw new Error(`${id} needs a sign-in`);\n await finishSignIn(\n sources.oauth(connection.entry),\n connection.entry,\n secret,\n sources.grant(connection.name)\n );\n } else if (secret !== null) throw new Error(TOKENS_IN_VAULT);\n await reread();\n restart(connection);\n },\n async disconnect(id) {\n const { connection, kind } = find(id);\n if (kind === 'oauth') await sources.oauth(connection.entry).forget();\n await reread();\n restart(connection);\n },\n onChange(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/connection.js
CHANGED
|
@@ -56,7 +56,6 @@ class McpServerConnection {
|
|
|
56
56
|
resources = [];
|
|
57
57
|
resourceTemplates = [];
|
|
58
58
|
instructions;
|
|
59
|
-
secrets = [];
|
|
60
59
|
#client;
|
|
61
60
|
#opening;
|
|
62
61
|
#shutdown = new AbortController();
|
|
@@ -179,7 +178,6 @@ class McpServerConnection {
|
|
|
179
178
|
this.missing = made.missing;
|
|
180
179
|
throw new MissingSecret(made.missing);
|
|
181
180
|
}
|
|
182
|
-
this.secrets = made.secrets;
|
|
183
181
|
const client = new McpClient({
|
|
184
182
|
name: CLIENT_NAME,
|
|
185
183
|
version: this.#options.version,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/connection.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type CallToolResult,\n JSON_RPC_ERROR_CODES,\n type ListResourcesResult,\n type ListResourceTemplatesResult,\n McpAuthRequiredError,\n McpClient,\n McpError,\n McpHttpError,\n type McpRequestOptions,\n McpSessionExpiredError,\n type Tool as McpTool,\n type McpTransport,\n type ReadResourceResult,\n type Resource,\n type ResourceTemplate,\n} from '@earendil-works/pi-mcp';\nimport { McpOAuthAuthorizationRequiredError } from '@earendil-works/pi-mcp/oauth';\nimport { DEFAULT_TIMEOUT_SECONDS, type McpServerEntry, usesOAuth } from './config.ts';\nimport { secretNames } from './credentials.ts';\n\nexport type ServerState =\n | 'connecting'\n | 'connected'\n | 'needs-auth'\n | 'needs-secret'\n | 'failed'\n | 'closed';\n\nexport type TransportFactory = (\n entry: McpServerEntry\n) => Promise<{ transport: McpTransport; secrets: string[] } | { missing: string[] }>;\n\nexport const CONNECT_RETRY_DELAYS_MS = [250, 1_000];\nexport const CLIENT_NAME = 'slicc';\n\nexport function signInNeeded(error: unknown): boolean {\n return (\n error instanceof McpAuthRequiredError || error instanceof McpOAuthAuthorizationRequiredError\n );\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function isTransient(error: unknown): boolean {\n if (error instanceof McpHttpError)\n return (\n error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501)\n );\n return error instanceof TypeError;\n}\n\nexport function isMcpApp(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n const uri = item.uri ?? item.uriTemplate ?? '';\n return uri.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? '');\n}\n\nasync function withoutTemplates<T>(list: () => Promise<T>, empty: T): Promise<T> {\n try {\n return await list();\n } catch (error) {\n if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)\n return empty;\n throw error;\n }\n}\n\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true }\n );\n });\n\nexport interface ConnectionOptions {\n entry: McpServerEntry;\n version: string;\n transport: TransportFactory;\n onTools: (connection: McpServerConnection) => void;\n onChange?: (connection: McpServerConnection) => void;\n onLog?: (connection: McpServerConnection, params: unknown) => void;\n}\n\nexport class McpServerConnection {\n readonly entry: McpServerEntry;\n state: ServerState = 'connecting';\n error: string | undefined;\n missing: string[] = [];\n tools: McpTool[] = [];\n hasResources = false;\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n instructions: string | undefined;\n secrets: string[] = [];\n #client: McpClient | undefined;\n #opening: Promise<McpClient> | undefined;\n readonly #shutdown = new AbortController();\n readonly #busy = new Map<McpClient, number>();\n #seen = false;\n readonly #expired = new Set<McpClient>();\n readonly #options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.entry = options.entry;\n this.#options = options;\n }\n\n get name(): string {\n return this.entry.name;\n }\n\n get timeoutMs(): number {\n return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n }\n\n get live(): boolean {\n return this.state === 'connected' || (this.state === 'connecting' && this.#seen);\n }\n\n get closed(): boolean {\n return this.#shutdown.signal.aborted;\n }\n\n getClient(): Promise<McpClient> {\n if (this.closed) return Promise.reject(new Error(`MCP server \"${this.name}\" is shut down`));\n if (this.#client?.connectionState === 'connected') return Promise.resolve(this.#client);\n this.#opening ??= this.#open().finally(() => {\n this.#opening = undefined;\n });\n return this.#opening;\n }\n\n callTool(\n name: string,\n args: Record<string, unknown>,\n options: McpRequestOptions\n ): Promise<CallToolResult> {\n return this.#with((client) => client.callTool(name, args, options));\n }\n\n readResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult> {\n return this.#with((client) => client.readResource(uri, options), true);\n }\n\n resourcesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourcesResult> {\n return this.#with((client) => client.listResourcesPage(cursor, options), true);\n }\n\n resourceTemplatesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourceTemplatesResult> {\n return this.#with(\n (client) =>\n withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), {\n resourceTemplates: [],\n }),\n true\n );\n }\n\n allResources(options: McpRequestOptions): Promise<Resource[]> {\n return this.#with((client) => client.listResources(options), true);\n }\n\n allResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]> {\n return this.#with(\n (client) => withoutTemplates(() => client.listResourceTemplates(options), []),\n true\n );\n }\n\n async #with<T>(run: (client: McpClient) => Promise<T>, readOnly = false): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n const client = await this.getClient();\n this.#busy.set(client, (this.#busy.get(client) ?? 0) + 1);\n try {\n return await run(client);\n } catch (error) {\n if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransient(error)) {\n await sleep(CONNECT_RETRY_DELAYS_MS[0] as number, this.#shutdown.signal);\n continue;\n }\n if (error instanceof McpSessionExpiredError && attempt === 1) {\n if (this.#client === client) {\n this.#client = undefined;\n this.#expired.add(client);\n }\n continue;\n }\n if (!signInNeeded(error)) throw error;\n await this.#drop(client);\n this.#mark('needs-auth');\n throw new Error(this.signInMessage());\n } finally {\n this.#release(client);\n }\n }\n }\n\n #release(client: McpClient): void {\n const left = (this.#busy.get(client) as number) - 1;\n this.#busy.set(client, left);\n if (left > 0 || !this.#expired.delete(client)) return;\n this.#busy.delete(client);\n void this.#drop(client);\n }\n\n signInMessage(): string {\n return usesOAuth(this.entry.config) && !secretNames(this.entry.config).length\n ? `MCP server \"${this.name}\" needs you to sign in. Sign in under Settings \u203A MCP servers.`\n : `MCP server \"${this.name}\" rejected its token (401). Update it in Settings \u203A MCP servers.`;\n }\n\n #mark(state: ServerState, error?: string): void {\n this.state = state;\n this.error = error;\n this.#options.onChange?.(this);\n }\n\n async #drop(client: McpClient): Promise<void> {\n if (this.#client === client) this.#client = undefined;\n await client.close().catch(() => undefined);\n }\n\n async #open(): Promise<McpClient> {\n this.#mark('connecting');\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.#connectOnce();\n } catch (error) {\n const delay = CONNECT_RETRY_DELAYS_MS[attempt];\n if (this.closed || delay === undefined || !isTransient(error)) throw this.#failed(error);\n await sleep(delay, this.#shutdown.signal);\n if (this.closed) throw this.#failed(error);\n }\n }\n }\n\n async #connectOnce(): Promise<McpClient> {\n const made = await this.#options.transport(this.entry);\n if ('missing' in made) {\n this.missing = made.missing;\n throw new MissingSecret(made.missing);\n }\n this.secrets = made.secrets;\n const client = new McpClient({\n name: CLIENT_NAME,\n version: this.#options.version,\n requestTimeoutMs: this.timeoutMs,\n });\n const log = this.#options.onLog;\n if (log) client.onNotification('notifications/message', (params) => log(this, params));\n let closing: Promise<void> | undefined;\n const closeClient = () => {\n closing ??= client.close().catch(() => undefined);\n return closing;\n };\n this.#shutdown.signal.addEventListener('abort', closeClient, { once: true });\n try {\n await client.connect(made.transport);\n client.onNotification('notifications/tools/list_changed', () => {\n void this.#refreshTools(client);\n });\n client.onNotification('notifications/resources/list_changed', () => {\n void this.#refreshResources(client);\n });\n const hasResources = client.serverCapabilities?.resources !== undefined;\n const [tools, resources] = await Promise.all([\n client.serverCapabilities?.tools ? client.listTools() : [],\n hasResources ? this.#fetchResources(client) : { resources: [], resourceTemplates: [] },\n ]);\n if (this.closed) throw new Error('shut down while connecting');\n if (client.connectionState !== 'connected') throw new Error('connection closed during setup');\n this.#client = client;\n this.tools = tools;\n this.hasResources = hasResources;\n this.resources = resources.resources;\n this.resourceTemplates = resources.resourceTemplates;\n this.instructions = client.instructions?.trim() || undefined;\n this.missing = [];\n this.#seen = true;\n this.state = 'connected';\n this.error = undefined;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n return client;\n } catch (error) {\n await closeClient();\n throw error;\n } finally {\n this.#shutdown.signal.removeEventListener('abort', closeClient);\n }\n }\n\n async #fetchResources(client: McpClient) {\n const [resources, resourceTemplates] = await Promise.all([\n client.listResources().catch(() => [] as Resource[]),\n withoutTemplates(() => client.listResourceTemplates(), [] as ResourceTemplate[]).catch(\n () => [] as ResourceTemplate[]\n ),\n ]);\n return {\n resources: resources.filter((resource) => !isMcpApp(resource)),\n resourceTemplates: resourceTemplates.filter((template) => !isMcpApp(template)),\n };\n }\n\n #failed(error: unknown): Error {\n if (error instanceof MissingSecret) {\n this.#mark('needs-secret');\n return new Error(error.message);\n }\n if (signInNeeded(error) && !this.closed) {\n this.#mark('needs-auth');\n return new Error(this.signInMessage());\n }\n this.#mark(this.closed ? 'closed' : 'failed', errorMessage(error));\n return new Error(`MCP server \"${this.name}\" failed to connect: ${this.error}`);\n }\n\n async #refreshTools(client: McpClient): Promise<void> {\n try {\n const tools = await client.listTools();\n if (this.#client !== client || this.closed) return;\n this.tools = tools;\n this.#options.onTools(this);\n } catch (error) {\n this.error = `Failed to refresh tools: ${errorMessage(error)}`;\n }\n this.#options.onChange?.(this);\n }\n\n async #refreshResources(client: McpClient): Promise<void> {\n const { resources, resourceTemplates } = await this.#fetchResources(client);\n if (this.#client !== client || this.closed) return;\n this.resources = resources;\n this.resourceTemplates = resourceTemplates;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n }\n\n async close(): Promise<void> {\n this.#shutdown.abort();\n this.state = 'closed';\n this.#options.onChange?.(this);\n const client = this.#client;\n this.#client = undefined;\n await Promise.allSettled([client?.close(), this.#opening]);\n }\n}\n\nexport class MissingSecret extends Error {\n readonly names: string[];\n\n constructor(names: string[]) {\n super(\n `needs ${names.map((name) => `\\${${name}}`).join(', ')}: add the value in Settings \u203A MCP servers`\n );\n this.names = names;\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAMK;AACP,SAAS,0CAA0C;AACnD,SAAS,yBAA8C,iBAAiB;AACxE,SAAS,mBAAmB;AAcrB,MAAM,0BAA0B,CAAC,KAAK,GAAK;AAC3C,MAAM,cAAc;AAEpB,SAAS,aAAa,OAAyB;AACpD,SACE,iBAAiB,wBAAwB,iBAAiB;AAE9D;AAEO,SAAS,aAAa,OAAwB;AACnD,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEO,SAAS,YAAY,OAAyB;AACnD,MAAI,iBAAiB;AACnB,WACE,MAAM,WAAW,OAAO,MAAM,WAAW,OAAQ,MAAM,UAAU,OAAO,MAAM,WAAW;AAE7F,SAAO,iBAAiB;AAC1B;AAEO,SAAS,SAAS,MAA0E;AACjG,QAAM,MAAM,KAAK,OAAO,KAAK,eAAe;AAC5C,SAAO,IAAI,WAAW,OAAO,KAAK,iCAAiC,KAAK,KAAK,YAAY,EAAE;AAC7F;AAEA,eAAe,iBAAoB,MAAwB,OAAsB;AAC/E,MAAI;AACF,WAAO,MAAM,KAAK;AAAA,EACpB,SAAS,OAAO;AACd,QAAI,iBAAiB,YAAY,MAAM,SAAS,qBAAqB;AACnE,aAAO;AACT,UAAM;AAAA,EACR;AACF;AAEA,MAAM,QAAQ,CAAC,IAAY,WACzB,IAAI,QAAc,CAAC,YAAY;AAC7B,QAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AACJ,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AAAA,IACA,EAAE,MAAM,KAAK;AAAA,EACf;AACF,CAAC;AAWI,MAAM,oBAAoB;AAAA,EACtB;AAAA,EACT,QAAqB;AAAA,EACrB;AAAA,EACA,UAAoB,CAAC;AAAA,EACrB,QAAmB,CAAC;AAAA,EACpB,eAAe;AAAA,EACf,YAAwB,CAAC;AAAA,EACzB,oBAAwC,CAAC;AAAA,EACzC;AAAA,EACA
|
|
4
|
+
"sourcesContent": ["import {\n type CallToolResult,\n JSON_RPC_ERROR_CODES,\n type ListResourcesResult,\n type ListResourceTemplatesResult,\n McpAuthRequiredError,\n McpClient,\n McpError,\n McpHttpError,\n type McpRequestOptions,\n McpSessionExpiredError,\n type Tool as McpTool,\n type McpTransport,\n type ReadResourceResult,\n type Resource,\n type ResourceTemplate,\n} from '@earendil-works/pi-mcp';\nimport { McpOAuthAuthorizationRequiredError } from '@earendil-works/pi-mcp/oauth';\nimport { DEFAULT_TIMEOUT_SECONDS, type McpServerEntry, usesOAuth } from './config.ts';\nimport { secretNames } from './credentials.ts';\n\nexport type ServerState =\n | 'connecting'\n | 'connected'\n | 'needs-auth'\n | 'needs-secret'\n | 'failed'\n | 'closed';\n\nexport type TransportFactory = (\n entry: McpServerEntry\n) => Promise<{ transport: McpTransport } | { missing: string[] }>;\n\nexport const CONNECT_RETRY_DELAYS_MS = [250, 1_000];\nexport const CLIENT_NAME = 'slicc';\n\nexport function signInNeeded(error: unknown): boolean {\n return (\n error instanceof McpAuthRequiredError || error instanceof McpOAuthAuthorizationRequiredError\n );\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function isTransient(error: unknown): boolean {\n if (error instanceof McpHttpError)\n return (\n error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501)\n );\n return error instanceof TypeError;\n}\n\nexport function isMcpApp(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n const uri = item.uri ?? item.uriTemplate ?? '';\n return uri.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? '');\n}\n\nasync function withoutTemplates<T>(list: () => Promise<T>, empty: T): Promise<T> {\n try {\n return await list();\n } catch (error) {\n if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)\n return empty;\n throw error;\n }\n}\n\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true }\n );\n });\n\nexport interface ConnectionOptions {\n entry: McpServerEntry;\n version: string;\n transport: TransportFactory;\n onTools: (connection: McpServerConnection) => void;\n onChange?: (connection: McpServerConnection) => void;\n onLog?: (connection: McpServerConnection, params: unknown) => void;\n}\n\nexport class McpServerConnection {\n readonly entry: McpServerEntry;\n state: ServerState = 'connecting';\n error: string | undefined;\n missing: string[] = [];\n tools: McpTool[] = [];\n hasResources = false;\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n instructions: string | undefined;\n #client: McpClient | undefined;\n #opening: Promise<McpClient> | undefined;\n readonly #shutdown = new AbortController();\n readonly #busy = new Map<McpClient, number>();\n #seen = false;\n readonly #expired = new Set<McpClient>();\n readonly #options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.entry = options.entry;\n this.#options = options;\n }\n\n get name(): string {\n return this.entry.name;\n }\n\n get timeoutMs(): number {\n return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n }\n\n get live(): boolean {\n return this.state === 'connected' || (this.state === 'connecting' && this.#seen);\n }\n\n get closed(): boolean {\n return this.#shutdown.signal.aborted;\n }\n\n getClient(): Promise<McpClient> {\n if (this.closed) return Promise.reject(new Error(`MCP server \"${this.name}\" is shut down`));\n if (this.#client?.connectionState === 'connected') return Promise.resolve(this.#client);\n this.#opening ??= this.#open().finally(() => {\n this.#opening = undefined;\n });\n return this.#opening;\n }\n\n callTool(\n name: string,\n args: Record<string, unknown>,\n options: McpRequestOptions\n ): Promise<CallToolResult> {\n return this.#with((client) => client.callTool(name, args, options));\n }\n\n readResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult> {\n return this.#with((client) => client.readResource(uri, options), true);\n }\n\n resourcesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourcesResult> {\n return this.#with((client) => client.listResourcesPage(cursor, options), true);\n }\n\n resourceTemplatesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourceTemplatesResult> {\n return this.#with(\n (client) =>\n withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), {\n resourceTemplates: [],\n }),\n true\n );\n }\n\n allResources(options: McpRequestOptions): Promise<Resource[]> {\n return this.#with((client) => client.listResources(options), true);\n }\n\n allResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]> {\n return this.#with(\n (client) => withoutTemplates(() => client.listResourceTemplates(options), []),\n true\n );\n }\n\n async #with<T>(run: (client: McpClient) => Promise<T>, readOnly = false): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n const client = await this.getClient();\n this.#busy.set(client, (this.#busy.get(client) ?? 0) + 1);\n try {\n return await run(client);\n } catch (error) {\n if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransient(error)) {\n await sleep(CONNECT_RETRY_DELAYS_MS[0] as number, this.#shutdown.signal);\n continue;\n }\n if (error instanceof McpSessionExpiredError && attempt === 1) {\n if (this.#client === client) {\n this.#client = undefined;\n this.#expired.add(client);\n }\n continue;\n }\n if (!signInNeeded(error)) throw error;\n await this.#drop(client);\n this.#mark('needs-auth');\n throw new Error(this.signInMessage());\n } finally {\n this.#release(client);\n }\n }\n }\n\n #release(client: McpClient): void {\n const left = (this.#busy.get(client) as number) - 1;\n this.#busy.set(client, left);\n if (left > 0 || !this.#expired.delete(client)) return;\n this.#busy.delete(client);\n void this.#drop(client);\n }\n\n signInMessage(): string {\n return usesOAuth(this.entry.config) && !secretNames(this.entry.config).length\n ? `MCP server \"${this.name}\" needs you to sign in. Sign in under Settings \u203A MCP servers.`\n : `MCP server \"${this.name}\" rejected its token (401). Update it in Settings \u203A MCP servers.`;\n }\n\n #mark(state: ServerState, error?: string): void {\n this.state = state;\n this.error = error;\n this.#options.onChange?.(this);\n }\n\n async #drop(client: McpClient): Promise<void> {\n if (this.#client === client) this.#client = undefined;\n await client.close().catch(() => undefined);\n }\n\n async #open(): Promise<McpClient> {\n this.#mark('connecting');\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.#connectOnce();\n } catch (error) {\n const delay = CONNECT_RETRY_DELAYS_MS[attempt];\n if (this.closed || delay === undefined || !isTransient(error)) throw this.#failed(error);\n await sleep(delay, this.#shutdown.signal);\n if (this.closed) throw this.#failed(error);\n }\n }\n }\n\n async #connectOnce(): Promise<McpClient> {\n const made = await this.#options.transport(this.entry);\n if ('missing' in made) {\n this.missing = made.missing;\n throw new MissingSecret(made.missing);\n }\n const client = new McpClient({\n name: CLIENT_NAME,\n version: this.#options.version,\n requestTimeoutMs: this.timeoutMs,\n });\n const log = this.#options.onLog;\n if (log) client.onNotification('notifications/message', (params) => log(this, params));\n let closing: Promise<void> | undefined;\n const closeClient = () => {\n closing ??= client.close().catch(() => undefined);\n return closing;\n };\n this.#shutdown.signal.addEventListener('abort', closeClient, { once: true });\n try {\n await client.connect(made.transport);\n client.onNotification('notifications/tools/list_changed', () => {\n void this.#refreshTools(client);\n });\n client.onNotification('notifications/resources/list_changed', () => {\n void this.#refreshResources(client);\n });\n const hasResources = client.serverCapabilities?.resources !== undefined;\n const [tools, resources] = await Promise.all([\n client.serverCapabilities?.tools ? client.listTools() : [],\n hasResources ? this.#fetchResources(client) : { resources: [], resourceTemplates: [] },\n ]);\n if (this.closed) throw new Error('shut down while connecting');\n if (client.connectionState !== 'connected') throw new Error('connection closed during setup');\n this.#client = client;\n this.tools = tools;\n this.hasResources = hasResources;\n this.resources = resources.resources;\n this.resourceTemplates = resources.resourceTemplates;\n this.instructions = client.instructions?.trim() || undefined;\n this.missing = [];\n this.#seen = true;\n this.state = 'connected';\n this.error = undefined;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n return client;\n } catch (error) {\n await closeClient();\n throw error;\n } finally {\n this.#shutdown.signal.removeEventListener('abort', closeClient);\n }\n }\n\n async #fetchResources(client: McpClient) {\n const [resources, resourceTemplates] = await Promise.all([\n client.listResources().catch(() => [] as Resource[]),\n withoutTemplates(() => client.listResourceTemplates(), [] as ResourceTemplate[]).catch(\n () => [] as ResourceTemplate[]\n ),\n ]);\n return {\n resources: resources.filter((resource) => !isMcpApp(resource)),\n resourceTemplates: resourceTemplates.filter((template) => !isMcpApp(template)),\n };\n }\n\n #failed(error: unknown): Error {\n if (error instanceof MissingSecret) {\n this.#mark('needs-secret');\n return new Error(error.message);\n }\n if (signInNeeded(error) && !this.closed) {\n this.#mark('needs-auth');\n return new Error(this.signInMessage());\n }\n this.#mark(this.closed ? 'closed' : 'failed', errorMessage(error));\n return new Error(`MCP server \"${this.name}\" failed to connect: ${this.error}`);\n }\n\n async #refreshTools(client: McpClient): Promise<void> {\n try {\n const tools = await client.listTools();\n if (this.#client !== client || this.closed) return;\n this.tools = tools;\n this.#options.onTools(this);\n } catch (error) {\n this.error = `Failed to refresh tools: ${errorMessage(error)}`;\n }\n this.#options.onChange?.(this);\n }\n\n async #refreshResources(client: McpClient): Promise<void> {\n const { resources, resourceTemplates } = await this.#fetchResources(client);\n if (this.#client !== client || this.closed) return;\n this.resources = resources;\n this.resourceTemplates = resourceTemplates;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n }\n\n async close(): Promise<void> {\n this.#shutdown.abort();\n this.state = 'closed';\n this.#options.onChange?.(this);\n const client = this.#client;\n this.#client = undefined;\n await Promise.allSettled([client?.close(), this.#opening]);\n }\n}\n\nexport class MissingSecret extends Error {\n readonly names: string[];\n\n constructor(names: string[]) {\n super(\n `needs ${names.map((name) => `\\${${name}}`).join(', ')}: add the value in Settings \u203A MCP servers`\n );\n this.names = names;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/credentials.js
CHANGED
|
@@ -13,33 +13,24 @@ function secretNames(config) {
|
|
|
13
13
|
for (const name of placeholders(value)) names.add(name);
|
|
14
14
|
return [...names];
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
function resolveHeaders(server, config, records) {
|
|
17
17
|
const names = secretNames(config);
|
|
18
|
-
if (!names.length) return { ok: true, headers: { ...config.headers }
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const key = stored?.type === "api_key" ? stored.key : void 0;
|
|
22
|
-
const value = (name) => env[name] ?? (names.length === 1 ? key : void 0);
|
|
23
|
-
const missing = names.filter((name) => !value(name));
|
|
18
|
+
if (!names.length) return { ok: true, headers: { ...config.headers } };
|
|
19
|
+
const placeholder = (name) => (records.mcp(server, name) ?? (names.length === 1 ? records.mcp(server) : void 0))?.placeholder;
|
|
20
|
+
const missing = names.filter((name) => !placeholder(name));
|
|
24
21
|
if (missing.length) return { ok: false, missing };
|
|
25
22
|
const headers = {};
|
|
26
23
|
for (const [header, text] of Object.entries(config.headers ?? {}))
|
|
27
24
|
headers[header] = text.replace(
|
|
28
25
|
/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
|
|
29
|
-
(_, name) => String(
|
|
26
|
+
(_, name) => String(placeholder(name))
|
|
30
27
|
);
|
|
31
|
-
return { ok: true, headers
|
|
32
|
-
}
|
|
33
|
-
function scrub(text, secrets) {
|
|
34
|
-
let out = text;
|
|
35
|
-
for (const secret of secrets) if (secret.length >= 4) out = out.split(secret).join("[redacted]");
|
|
36
|
-
return out;
|
|
28
|
+
return { ok: true, headers };
|
|
37
29
|
}
|
|
38
30
|
export {
|
|
39
31
|
MCP_CREDENTIAL_PREFIX,
|
|
40
32
|
credentialKey,
|
|
41
33
|
resolveHeaders,
|
|
42
|
-
scrub,
|
|
43
34
|
secretNames
|
|
44
35
|
};
|
|
45
36
|
//# sourceMappingURL=credentials.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/credentials.ts"],
|
|
4
|
-
"sourcesContent": ["import
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { type McpServerConfig, placeholders } from './config.ts';\n\nexport const MCP_CREDENTIAL_PREFIX = 'mcp:';\n\nexport interface McpRecords {\n mcp(server: string, name?: string): { placeholder: string } | undefined;\n}\n\nasync function digest(text: string): Promise<string> {\n const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(bytes).slice(0, 12)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\nexport async function credentialKey(server: string, url: string): Promise<string> {\n return `${MCP_CREDENTIAL_PREFIX}${server}|${await digest(new URL(url).href)}`;\n}\n\nexport function secretNames(config: McpServerConfig): string[] {\n const names = new Set<string>();\n for (const value of Object.values(config.headers ?? {}))\n for (const name of placeholders(value)) names.add(name);\n return [...names];\n}\n\nexport type Resolved =\n | { ok: true; headers: Record<string, string> }\n | { ok: false; missing: string[] };\n\nexport function resolveHeaders(\n server: string,\n config: McpServerConfig,\n records: McpRecords\n): Resolved {\n const names = secretNames(config);\n if (!names.length) return { ok: true, headers: { ...config.headers } };\n const placeholder = (name: string) =>\n (records.mcp(server, name) ?? (names.length === 1 ? records.mcp(server) : undefined))\n ?.placeholder;\n const missing = names.filter((name) => !placeholder(name));\n if (missing.length) return { ok: false, missing };\n const headers: Record<string, string> = {};\n for (const [header, text] of Object.entries(config.headers ?? {}))\n headers[header] = text.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, name: string) =>\n String(placeholder(name))\n );\n return { ok: true, headers };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAA+B,oBAAoB;AAE5C,MAAM,wBAAwB;AAMrC,eAAe,OAAO,MAA+B;AACnD,QAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAClF,SAAO,CAAC,GAAG,IAAI,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,EAC1C,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAChD,KAAK,EAAE;AACZ;AAEA,eAAsB,cAAc,QAAgB,KAA8B;AAChF,SAAO,GAAG,qBAAqB,GAAG,MAAM,IAAI,MAAM,OAAO,IAAI,IAAI,GAAG,EAAE,IAAI,CAAC;AAC7E;AAEO,SAAS,YAAY,QAAmC;AAC7D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC;AACpD,eAAW,QAAQ,aAAa,KAAK,EAAG,OAAM,IAAI,IAAI;AACxD,SAAO,CAAC,GAAG,KAAK;AAClB;AAMO,SAAS,eACd,QACA,QACA,SACU;AACV,QAAM,QAAQ,YAAY,MAAM;AAChC,MAAI,CAAC,MAAM,OAAQ,QAAO,EAAE,IAAI,MAAM,SAAS,EAAE,GAAG,OAAO,QAAQ,EAAE;AACrE,QAAM,cAAc,CAAC,UAClB,QAAQ,IAAI,QAAQ,IAAI,MAAM,MAAM,WAAW,IAAI,QAAQ,IAAI,MAAM,IAAI,UACtE;AACN,QAAM,UAAU,MAAM,OAAO,CAAC,SAAS,CAAC,YAAY,IAAI,CAAC;AACzD,MAAI,QAAQ,OAAQ,QAAO,EAAE,IAAI,OAAO,QAAQ;AAChD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC;AAC9D,YAAQ,MAAM,IAAI,KAAK;AAAA,MAAQ;AAAA,MAAmC,CAAC,GAAG,SACpE,OAAO,YAAY,IAAI,CAAC;AAAA,IAC1B;AACF,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/index.js
CHANGED
package/dist/mcp/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type {\n Agent,\n Registry,\n ToolExecutionApi,\n ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { CodemodeExtra } from './allow.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport { parseMcpConfig } from './config.ts';\nimport { configPath, shown } from './paths.ts';\nimport type { McpAttach, McpRuntime } from './runtime.ts';\n\nexport { allows, type CodemodeExtra, McpAllowDoc, mcpEntries, serverAllowed } from './allow.ts';\nexport type { McpAccounts, McpAttach, McpRuntime, ServerStatus } from './runtime.ts';\n\nexport type McpLoader = () => Promise<typeof import('./runtime.ts')>;\n\nexport const loadRuntime: McpLoader = () => import('./runtime.ts');\n\nexport interface McpSetup {\n catalog(): McpCatalog | undefined;\n direct(names: readonly string[]): ToolRegistration[];\n codemode(api: ToolExecutionApi, agent: Agent, context: Context): Promise<CodemodeExtra>;\n attach(options: McpAttach, context: Context): Promise<McpRuntime>;\n}\n\nconst idle: McpRuntime = {\n accounts: async () => [],\n signIn: async (id) => {\n throw new Error(`${id} is not an MCP server that signs in`);\n },\n connect: async (id) => {\n throw new Error(`${id} is not an MCP server that takes a token`);\n },\n disconnect: async () => undefined,\n onChange: () => () => undefined,\n status: () => [],\n
|
|
5
|
-
"mappings": "AASA,SAAS,sBAAsB;AAC/B,SAAS,YAAY,aAAa;AAGlC,SAAS,QAA4B,aAAa,YAAY,qBAAqB;AAK5E,MAAM,cAAyB,MAAM,OAAO,cAAc;AASjE,MAAM,OAAmB;AAAA,EACvB,UAAU,YAAY,CAAC;AAAA,EACvB,QAAQ,OAAO,OAAO;AACpB,UAAM,IAAI,MAAM,GAAG,EAAE,qCAAqC;AAAA,EAC5D;AAAA,EACA,SAAS,OAAO,OAAO;AACrB,UAAM,IAAI,MAAM,GAAG,EAAE,0CAA0C;AAAA,EACjE;AAAA,EACA,YAAY,YAAY;AAAA,EACxB,UAAU,MAAM,MAAM;AAAA,EACtB,QAAQ,MAAM,CAAC;AAAA,EACf,
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type {\n Agent,\n Registry,\n ToolExecutionApi,\n ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { CodemodeExtra } from './allow.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport { parseMcpConfig } from './config.ts';\nimport { configPath, shown } from './paths.ts';\nimport type { McpAttach, McpRuntime } from './runtime.ts';\n\nexport { allows, type CodemodeExtra, McpAllowDoc, mcpEntries, serverAllowed } from './allow.ts';\nexport type { McpAccounts, McpAttach, McpRuntime, ServerStatus } from './runtime.ts';\n\nexport type McpLoader = () => Promise<typeof import('./runtime.ts')>;\n\nexport const loadRuntime: McpLoader = () => import('./runtime.ts');\n\nexport interface McpSetup {\n catalog(): McpCatalog | undefined;\n direct(names: readonly string[]): ToolRegistration[];\n codemode(api: ToolExecutionApi, agent: Agent, context: Context): Promise<CodemodeExtra>;\n attach(options: McpAttach, context: Context): Promise<McpRuntime>;\n}\n\nconst idle: McpRuntime = {\n accounts: async () => [],\n signIn: async (id) => {\n throw new Error(`${id} is not an MCP server that signs in`);\n },\n connect: async (id) => {\n throw new Error(`${id} is not an MCP server that takes a token`);\n },\n disconnect: async () => undefined,\n onChange: () => () => undefined,\n status: () => [],\n reload: async () => undefined,\n close: async () => undefined,\n};\n\nasync function unavailable(options: McpAttach, error: unknown, context: Context) {\n const file = configPath(options.home, options.agentDir);\n const named = shown(file, options.home);\n const read = await options.env.readTextFile(file, context);\n if (!parseMcpConfig(read.ok ? read.value : undefined).servers.length) return;\n await options.licks\n .deliver(\n {\n channel: 'fswatch',\n source: `mcp:${named}`,\n title: \"MCP servers can't start in this SLICC\",\n text: error instanceof Error ? error.message : String(error),\n body: `The MCP client didn't load, so the servers in ${named} stay off until SLICC is updated.`,\n target: 'cone',\n severity: 'warn',\n eventId: 'mcp\\nunavailable',\n },\n context\n )\n .catch(() => undefined);\n}\n\nexport function setupMcp(registry: Registry, load: McpLoader = loadRuntime): McpSetup {\n let catalog: McpCatalog | undefined;\n return {\n catalog: () => catalog,\n direct: (names) => catalog?.direct(names) ?? [],\n codemode: async (api, agent, context) =>\n catalog ? catalog.codemode(api, agent, context) : { tools: [], globals: [] },\n async attach(options, context) {\n let started: Awaited<ReturnType<Awaited<ReturnType<McpLoader>>['startMcp']>>;\n try {\n started = await (await load()).startMcp(registry, options, context);\n } catch (error) {\n await unavailable(options, error, context);\n return idle;\n }\n catalog = started.catalog;\n return started.runtime;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AASA,SAAS,sBAAsB;AAC/B,SAAS,YAAY,aAAa;AAGlC,SAAS,QAA4B,aAAa,YAAY,qBAAqB;AAK5E,MAAM,cAAyB,MAAM,OAAO,cAAc;AASjE,MAAM,OAAmB;AAAA,EACvB,UAAU,YAAY,CAAC;AAAA,EACvB,QAAQ,OAAO,OAAO;AACpB,UAAM,IAAI,MAAM,GAAG,EAAE,qCAAqC;AAAA,EAC5D;AAAA,EACA,SAAS,OAAO,OAAO;AACrB,UAAM,IAAI,MAAM,GAAG,EAAE,0CAA0C;AAAA,EACjE;AAAA,EACA,YAAY,YAAY;AAAA,EACxB,UAAU,MAAM,MAAM;AAAA,EACtB,QAAQ,MAAM,CAAC;AAAA,EACf,QAAQ,YAAY;AAAA,EACpB,OAAO,YAAY;AACrB;AAEA,eAAe,YAAY,SAAoB,OAAgB,SAAkB;AAC/E,QAAM,OAAO,WAAW,QAAQ,MAAM,QAAQ,QAAQ;AACtD,QAAM,QAAQ,MAAM,MAAM,QAAQ,IAAI;AACtC,QAAM,OAAO,MAAM,QAAQ,IAAI,aAAa,MAAM,OAAO;AACzD,MAAI,CAAC,eAAe,KAAK,KAAK,KAAK,QAAQ,MAAS,EAAE,QAAQ,OAAQ;AACtE,QAAM,QAAQ,MACX;AAAA,IACC;AAAA,MACE,SAAS;AAAA,MACT,QAAQ,OAAO,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC3D,MAAM,iDAAiD,KAAK;AAAA,MAC5D,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,IACA;AAAA,EACF,EACC,MAAM,MAAM,MAAS;AAC1B;AAEO,SAAS,SAAS,UAAoB,OAAkB,aAAuB;AACpF,MAAI;AACJ,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,QAAQ,CAAC,UAAU,SAAS,OAAO,KAAK,KAAK,CAAC;AAAA,IAC9C,UAAU,OAAO,KAAK,OAAO,YAC3B,UAAU,QAAQ,SAAS,KAAK,OAAO,OAAO,IAAI,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAC7E,MAAM,OAAO,SAAS,SAAS;AAC7B,UAAI;AACJ,UAAI;AACF,kBAAU,OAAO,MAAM,KAAK,GAAG,SAAS,UAAU,SAAS,OAAO;AAAA,MACpE,SAAS,OAAO;AACd,cAAM,YAAY,SAAS,OAAO,OAAO;AACzC,eAAO;AAAA,MACT;AACA,gBAAU,QAAQ;AAClB,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/oauth.js
CHANGED
|
@@ -32,7 +32,8 @@ function authProvider(provider, server) {
|
|
|
32
32
|
await authorizeMcp(provider, {
|
|
33
33
|
serverUrl: context.serverUrl,
|
|
34
34
|
...challenge.resourceMetadataUrl ? { resourceMetadataUrl: challenge.resourceMetadataUrl } : {},
|
|
35
|
-
|
|
35
|
+
skipRefresh: true,
|
|
36
|
+
...step ? { scope: stepUpScope(granted?.scope, challenge.scope) } : challenge.scope ? { scope: challenge.scope } : {},
|
|
36
37
|
...configured ? { authorizationServerMetadataUrl: new URL(configured) } : {},
|
|
37
38
|
fetch: context.fetch
|
|
38
39
|
});
|
|
@@ -69,12 +70,12 @@ function randomState() {
|
|
|
69
70
|
class StoredOAuth {
|
|
70
71
|
redirectUrl;
|
|
71
72
|
clientMetadata;
|
|
72
|
-
#
|
|
73
|
+
#store;
|
|
73
74
|
#server;
|
|
74
75
|
#writes = Promise.resolve();
|
|
75
|
-
constructor(server,
|
|
76
|
+
constructor(server, store, redirectUrl) {
|
|
76
77
|
this.#server = server;
|
|
77
|
-
this.#
|
|
78
|
+
this.#store = store;
|
|
78
79
|
this.redirectUrl = redirectUrl;
|
|
79
80
|
this.clientMetadata = {
|
|
80
81
|
client_name: server.config.oauth?.clientName ?? CLIENT_NAME,
|
|
@@ -93,26 +94,17 @@ class StoredOAuth {
|
|
|
93
94
|
}
|
|
94
95
|
async load() {
|
|
95
96
|
await this.#writes;
|
|
96
|
-
const
|
|
97
|
-
const state = stored?.type === "oauth" ? stored.state : void 0;
|
|
97
|
+
const state = await this.#store.get(await this.#key());
|
|
98
98
|
return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };
|
|
99
99
|
}
|
|
100
100
|
async update(change) {
|
|
101
101
|
const write = this.#writes.then(async () => {
|
|
102
102
|
const key = await this.#key();
|
|
103
|
-
await this.#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
type: "oauth",
|
|
110
|
-
access: next.tokens?.access_token ?? "",
|
|
111
|
-
refresh: next.tokens?.refresh_token ?? "",
|
|
112
|
-
expires: next.tokensExpireAt ?? 0,
|
|
113
|
-
state: next
|
|
114
|
-
};
|
|
115
|
-
});
|
|
103
|
+
const before = await this.#store.get(key);
|
|
104
|
+
await this.#store.set(
|
|
105
|
+
key,
|
|
106
|
+
change(before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl })
|
|
107
|
+
);
|
|
116
108
|
});
|
|
117
109
|
this.#writes = write.catch(() => void 0);
|
|
118
110
|
await write;
|
|
@@ -176,7 +168,7 @@ class StoredOAuth {
|
|
|
176
168
|
}
|
|
177
169
|
async forget() {
|
|
178
170
|
await this.#writes;
|
|
179
|
-
await this.#
|
|
171
|
+
await this.#store.set(await this.#key(), void 0);
|
|
180
172
|
}
|
|
181
173
|
}
|
|
182
174
|
async function beginSignIn(provider, server, options) {
|
|
@@ -203,6 +195,9 @@ async function beginSignIn(provider, server, options) {
|
|
|
203
195
|
});
|
|
204
196
|
await provider.saveClientInformation(client);
|
|
205
197
|
}
|
|
198
|
+
await options.register(
|
|
199
|
+
new URL(metadata?.token_endpoint ?? new URL("/token", info.authorizationServerUrl)).href
|
|
200
|
+
);
|
|
206
201
|
const { verifier, challenge } = await pkce();
|
|
207
202
|
const state = randomState();
|
|
208
203
|
await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));
|