import type { HexisMcpConfig } from './config.js'; /** * Local materialization of a plugin, per the Agent Plugins runtime contract. * * A stdio MCP server is a subprocess on THIS machine, and the spec requires * its client to provide two real directories: `PLUGIN_ROOT` (the plugin's own * files) and `PLUGIN_DATA` (client-managed persistent state), expand exactly * those two placeholders in `args` elements, `env` values and `cwd`, and * resolve a `./` command against the plugin root — refusing anything that * escapes it. None of that is possible against a knowledge base that lives on * a server, so the plugin's files are fetched here first, via the deployment's * plugin-archive endpoint (key-authenticated, per-file read-ACL'd). * * Layout: `~/.hexis/plugins//` as PLUGIN_ROOT, refreshed on * every server start (a stale copy is the cost of a running session, not of a * lifetime) — or, when that root is held by a process a previous instance * leaked, a FALLBACK root under the RESERVED sibling namespace * `~/.hexis/plugins//.hexis-fallbacks//<12-hex>` (see * `materializePlugin`); `~/.hexis/plugin-data//` as PLUGIN_DATA, * created once and NEVER cleared — it is the server's persistent state, and * the spec says the client manages its lifetime, not its contents. * * `.hexis-fallbacks` is a reserved name: no plugin may be called that in any * casing (`assertSegment` refuses it, and forbids path separators, so no * plugin ever materializes INSIDE a subdirectory either). The reservation is what lets the * stale-fallback sweep run inside that namespace ONLY — a legitimate plugin * whose folder merely LOOKS like an old-style fallback (`GTM.abcdef123456`) * can never be mistaken for one and swept. */ /** Where a deployment's materialized plugins live, keyed by host so two workspaces never collide. */ export declare function hexisHome(): string; /** * One deployment's on-disk identity. Shared with the OAuth credential store * (`oauth.ts`) so a plugin tree and a sign-in for the same deployment key the * same way — and two deployments never collide in either. */ export declare function hostKey(baseUrl: string): string; /** * The reserved sibling directory (under each host's plugin dir) that holds * busy-rm FALLBACK roots, keyed by plugin: `.hexis-fallbacks//<12-hex>`. * Reserved so the stale-fallback sweep can never confuse a real plugin folder * with a fallback — see the layout note above. */ export declare const FALLBACKS_DIR = ".hexis-fallbacks"; export interface MaterializedPlugin { pluginRoot: string; pluginData: string; } /** * Stream a response body to a file under a hard byte cap, counting as bytes * ARRIVE — the whole point is refusing before the allocation exists, so * `arrayBuffer()` (which buffers everything first) cannot be the mechanism. * A file, not an in-memory accumulation: chunks-then-concat holds the payload * twice at its peak, so the cap would bound the download but not the memory it * was set to protect. Each chunk is written and released; the cap bounds disk, * and memory stays at chunk size. The stream is cancelled on refusal, which * also releases the connection. * Exported for direct testing; the cap in production is `MAX_ARCHIVE_BYTES`. */ export declare function readBodyCappedToFile(body: ReadableStream | null, cap: number, label: string, dest: string): Promise; /** * Fetch the plugin into the local root, byte-for-byte. The archive is built * server-side and filtered per file by the caller's own read access — a file * the key cannot read is a file that stays remote. */ export declare function materializePlugin(config: HexisMcpConfig, folder: string): Promise; /** Exported for direct testing; `materializePlugin` is the production caller. */ export declare function extractArchive(archivePath: string, pluginRoot: string, folder: string, MAX_EXTRACTED_BYTES: number, MAX_ENTRIES: number): Promise; /** * Expansion per the spec: a single, non-recursive textual replacement of the * two placeholders — applied ONLY to `args` elements, `env` values and `cwd`, * never to `command` or `env` keys. Unrecognized `${…}` text stays literal. * One pass over the input only: replacement text is never rescanned, so an * expansion value that itself contains a placeholder token stays literal data. */ export declare function expandPlaceholders(value: string, m: MaterializedPlugin): string; /** * Resolve a stdio `command` token: a bare name passes through to PATH lookup; * a `./` path resolves against the plugin root and must STAY within it after * symlink resolution — `realpath` is what makes a link pointing outside the * root a refusal instead of an escape. Anything else is refused outright (the * spec allows exactly those two shapes). */ export declare function resolveCommand(command: string, m: MaterializedPlugin): Promise; /** The spawn spec as it appears inside a UTCP mcp call template's config. */ export interface StdioServerSpec { command: string; args?: string[]; env?: Record; cwd?: string; } /** * Apply the whole runtime contract to one stdio server spec: expand, resolve, * contain, and add the two environment variables — which the server's own * `env` MUST NOT declare (the client owns them; a plugin shadowing them would * be lying to the process about where it lives). */ export declare function prepareStdioSpec(spec: StdioServerSpec, m: MaterializedPlugin): Promise; //# sourceMappingURL=materialize.d.ts.map