pi2dsh 0.18.0 → 0.20.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 +46 -2
- package/README.zh.md +26 -0
- package/dist/{analyzer-BcuRfHLG.mjs → analyzer-BL7Nfvko.mjs} +2 -2
- package/dist/{analyzer-BcuRfHLG.mjs.map → analyzer-BL7Nfvko.mjs.map} +1 -1
- package/dist/cli.mjs +3 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/client.js +118 -1
- package/dist/compat/pi-coding-agent.d.mts +2 -2
- package/dist/compat/pi-coding-agent.mjs +2 -2
- package/dist/{mcp-config-NvRpjToH.mjs → compatibility-D_PV1zgF.mjs} +10 -187
- package/dist/compatibility-D_PV1zgF.mjs.map +1 -0
- package/dist/credentials-oauth.mjs +1 -1
- package/dist/host.d.mts +9 -2
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +7 -2
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +8 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +154 -8
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-config-C1K-IzzB.mjs +180 -0
- package/dist/mcp-config-C1K-IzzB.mjs.map +1 -0
- package/dist/{pi-coding-agent-DXJ_Jzo0.mjs → pi-coding-agent-CPXFo0UO.mjs} +0 -0
- package/dist/pi-coding-agent-CPXFo0UO.mjs.map +1 -0
- package/dist/{pi-coding-agent-69yY0ke-.d.mts → pi-coding-agent-Pvy0m9tS.d.mts} +6 -2
- package/dist/{pi-coding-agent-69yY0ke-.d.mts.map → pi-coding-agent-Pvy0m9tS.d.mts.map} +1 -1
- package/dist/{runtime-C-cmLDY0.mjs → runtime-CBZcd1uU.mjs} +592 -21
- package/dist/runtime-CBZcd1uU.mjs.map +1 -0
- package/dist/runtime.d.mts +106 -68
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/package.json +7 -2
- package/dist/mcp-config-NvRpjToH.mjs.map +0 -1
- package/dist/pi-coding-agent-DXJ_Jzo0.mjs.map +0 -1
- package/dist/runtime-C-cmLDY0.mjs.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/engine.ts","../src/index.ts"],"sourcesContent":["// The pi2dsh engine: ONE installed copy of the bridge that mounts every Pi\n// package the user has added to their DSH profile.\n//\n// dsh plugin --profile p add pi2dsh ← the engine (this plugin)\n// dsh plugin --profile p add @kassing/pi-vision ← plain npm dependency\n// dsh plugin --profile p add pi-vision-tool ← plain npm dependency\n//\n// DSH's plugin manager records only packages that declare `dsh.bundle` as\n// profile layers; everything else stays an ordinary dependency (\"a plain\n// library is fine\"). The engine reads the profile manifest's DIRECT\n// dependencies — every entry there was an explicit `dsh plugin add` — and\n// mounts each package that identifies as a Pi package, all through one\n// bridge instance: one model ledger, one command space, one upgrade unit.\n//\n// Discovery is manifest-driven, never a node_modules scan (the lesson from\n// Prettier 3 dropping directory-based plugin discovery): the dependency list\n// is the user's explicit intent, and package identification uses the same\n// Pi markers `resolvePiPackage` has always used (the `pi` manifest field,\n// with Pi's directory conventions as fallback).\n\nimport { readFile } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPreparedPiHost, preparePiHost, type PreparedPiHostPackage } from './host.js'\nimport { registerVisionCompanions } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\n\nexport interface EngineConfig {\n /** Mount exactly these packages (skips discovery). */\n packages?: string[]\n /** Never mount these packages even when discovered. */\n exclude?: string[]\n /**\n * Image-admission companion routes. Default: AUTOMATIC — every text-only\n * llm route gets a \\`<route>-vision\\` companion that admits pasted images\n * (a mounted vision extension analyzes them; without one the image is\n * materialized to a file any image-capable tool can read). \\`false\\`\n * turns companions off; an explicit \\`{ <route>: [modelIds] }\\` narrows.\n */\n visionCompanions?: false | Record<string, readonly string[]>\n}\n\n/** Locate the DSH profile root: the nearest ancestor holding cordis.yml. */\nexport function findProfileRoot(start: string): string | undefined {\n let dir = start\n for (;;) {\n if (existsSync(join(dir, 'cordis.yml')) && existsSync(join(dir, 'package.json'))) return dir\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\ninterface DiscoveredPackage {\n name: string\n dir: string\n /** Resolution anchor when the package is carried by a suite (its members\n * are the suite's dependencies, unreachable from the profile root under\n * pnpm's isolated layout). */\n anchor?: string\n}\n\ninterface ProfileManifest {\n dependencies?: Record<string, string>\n dsh?: { profile?: { bundles?: string[] } }\n}\n\nasync function readProfileManifest(profileRoot: string): Promise<ProfileManifest> {\n return JSON.parse(await readFile(join(profileRoot, 'package.json'), 'utf8')) as ProfileManifest\n}\n\nfunction resolveDependencyDir(profileRoot: string, name: string): string | undefined {\n // Direct dependencies of the profile live under its node_modules by name\n // (pnpm links them there); resolving by path needs no exports gymnastics.\n const dir = join(profileRoot, 'node_modules', name)\n return existsSync(join(dir, 'package.json')) ? dir : undefined\n}\n\n/**\n * The profile's direct dependencies that identify as Pi packages: not the\n * engine itself, not a `dsh.bundle` (those are DSH plugins, not Pi\n * packages), carrying either Pi's `pi` manifest field or extension sources\n * under Pi's directory conventions.\n */\nexport async function discoverProfilePiPackages(\n profileRoot: string,\n options: { exclude?: string[], warn?: (message: string) => void } = {},\n): Promise<DiscoveredPackage[]> {\n const warn = options.warn ?? (() => {})\n const excluded = new Set(options.exclude ?? [])\n const manifest = await readProfileManifest(profileRoot)\n const discovered: DiscoveredPackage[] = []\n for (const name of Object.keys(manifest.dependencies ?? {})) {\n if (name === 'pi2dsh' || excluded.has(name)) continue\n const dir = resolveDependencyDir(profileRoot, name)\n if (dir === undefined) {\n warn(`[pi2dsh engine] dependency ${JSON.stringify(name)} is not installed under the profile; skipping`)\n continue\n }\n let packageJson: Record<string, unknown>\n try {\n packageJson = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8')) as Record<string, unknown>\n } catch (error) {\n warn(`[pi2dsh engine] cannot read ${name}/package.json (${error instanceof Error ? error.message : String(error)}); skipping`)\n continue\n }\n // A suite: `pi2dsh: { suite: [names...] }` mounts the listed Pi packages\n // as if the user had added each one. The list is an explicit manifest —\n // the same discovery covenant as the profile dependency list, one hop\n // deeper — and the members are the suite's own dependencies, so each\n // resolves anchored at the suite package (pnpm keeps transitive\n // dependencies out of the profile root). One level only, no recursion.\n const suite = (packageJson.pi2dsh as { suite?: unknown } | undefined)?.suite\n if (Array.isArray(suite)) {\n for (const member of suite) {\n if (typeof member !== 'string' || member.length === 0) continue\n if (member === 'pi2dsh' || excluded.has(member)) continue\n discovered.push({ name: member, dir, anchor: join(dir, 'package.json') })\n }\n continue\n }\n // A dsh.bundle-declaring package is a DSH plugin layer, never a Pi package.\n if ((packageJson.dsh as { bundle?: unknown } | undefined)?.bundle !== undefined) continue\n if (packageJson.pi !== undefined && typeof packageJson.pi === 'object') {\n discovered.push({ name, dir })\n continue\n }\n // No `pi` field: fall back to Pi's directory conventions via the same\n // resolver every other mount path uses (a package with zero extension\n // sources is a plain library and stays unmounted).\n try {\n const pkg = await resolvePiPackage(dir)\n try {\n if (pkg.resources.extensions.length > 0) discovered.push({ name, dir })\n } finally {\n await pkg.dispose()\n }\n } catch {\n // Not resolvable as a Pi package — a plain library.\n }\n }\n // One mount per name. A package that is both a direct dependency and a\n // suite member mounts as the DIRECT dependency (the user's own explicit\n // add, resolved from the profile root) — the suite copy yields.\n const byName = new Map<string, DiscoveredPackage>()\n for (const pkg of discovered) {\n const existing = byName.get(pkg.name)\n if (existing === undefined || (existing.anchor !== undefined && pkg.anchor === undefined)) {\n byName.set(pkg.name, pkg)\n }\n }\n return [...byName.values()]\n}\n\ninterface AgentScopedMount {\n ready: Promise<void>\n /** Set when the mount failed; the agent then runs as a plain DSH agent. */\n failure?: string\n}\n\n/** The loose shapes of the official DSH seams this file consumes. */\ninterface AgentLike {\n ctx: Context\n}\ninterface EngineHostContext {\n /** Un-injected reflection access (the ctx.get('loader') idiom): the engine\n * must not hard-depend on the agent registry — compositions without one\n * (bare test hosts) simply have no agents to mount for. */\n get?(name: string): unknown\n tools?: { schemas?(scope: unknown): Array<{ name: string }> }\n on(name: string, listener: (...args: never[]) => unknown): () => void\n}\ninterface AgentRegistryLike {\n roots?(): AgentLike[]\n}\n\n/**\n * The single mount path on every DSH surface: one Pi runtime per root Agent.\n *\n * Pi instantiates its extensions once per session; DSH's twin of that scope is\n * the Agent with its public `agent.ctx` (\"contributions are agent-local,\n * unwind on disposal\"). Mounting is driven purely by official core seams, so\n * the same semantics hold on the TUI, web, headless, ACP and config-declared\n * agents alike:\n *\n * - `agent/created` fires on every publication path before the loop can run\n * a first turn; it eagerly starts that agent's mount.\n * - `system-prompt/assemble` (awaited waterfall, runs in every step's\n * preStep) gates the assembly on the mount and patches the pre-waterfall\n * tools snapshot from the official scoped projection `tools.schemas()` —\n * stock DSH collects `assembly.tools` before dispatching the waterfall,\n * so a mount that lands during the wait would otherwise miss turn one.\n * - `tools/pre-execute` (awaited waterfall) closes the same window for\n * direct executions that bypass assembly.\n *\n * `agent/session-start` deliberately is NOT the trigger: DSH documents it as a\n * veto-less notification that cannot gate startup. The waterfalls above are\n * the officially awaited seams, and the exact pattern of registering onto a\n * foreign agent's ctx from a lifecycle event is what DSH's own schedule\n * plugin ships.\n *\n * A mount failure never takes the Agent down: the agent keeps running as a\n * plain DSH agent, the failure is reported loudly once, and only the Pi\n * packages are missing — the capability-gap discipline, not a rollback.\n */\nexport function installAgentScopedMounts(\n ctx: Context,\n preparedPackages: Promise<readonly PreparedPiHostPackage[]>,\n report: { warn(message: string): void },\n): void {\n const host = ctx as unknown as EngineHostContext\n const mounts = new WeakMap<object, AgentScopedMount>()\n\n const registry = (): AgentRegistryLike | undefined => {\n try {\n return host.get?.('agents') as AgentRegistryLike | undefined\n } catch {\n return undefined\n }\n }\n\n const isRootAgent = (agent: object): boolean => {\n // Subagents keep Pi's own sub-session semantics through the session\n // bridge; only runtime roots receive a Pi runtime (the same distinction\n // DSH's schedule plugin draws via agents.roots()).\n const agents = registry()\n const roots = agents?.roots\n if (typeof roots !== 'function') return true\n try {\n return (roots.call(agents) as unknown[]).includes(agent)\n } catch {\n return true\n }\n }\n\n const ensureMount = (agent: AgentLike): AgentScopedMount => {\n let mount = mounts.get(agent)\n if (mount === undefined) {\n const started: AgentScopedMount = { ready: Promise.resolve() }\n started.ready = preparedPackages.then(async prepared => {\n if (prepared.length === 0) return\n try {\n await applyPreparedPiHost(agent.ctx, prepared, agent as unknown as Record<string, unknown>)\n } catch (error) {\n // Loud, once per agent; the gates resolve so the plain DSH agent\n // keeps working. Faking success is forbidden — the message names\n // what is missing.\n started.failure = error instanceof Error ? error.message : String(error)\n report.warn(`[pi2dsh engine] Pi packages failed to mount for this agent; it continues without them: ${started.failure}`)\n }\n })\n mounts.set(agent, started)\n mount = started\n }\n return mount\n }\n\n // Eager start on publication. `agent/created` reaches host-level listeners\n // on every create/resume/config path, before the loop can open a turn.\n host.on('agent/created', ((payload: { agent: AgentLike }) => {\n if (isRootAgent(payload.agent)) ensureMount(payload.agent)\n }) as never)\n\n // Correctness boundary #1: no prompt assembly for a root agent proceeds\n // before its Pi runtime is mounted, and the tools snapshot taken before\n // this waterfall is reconciled against the official scoped projection.\n host.on('system-prompt/assemble', (async (\n assembly: { tools: Array<{ name: string }> },\n context: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n const agent = context.agent\n if (agent !== undefined && isRootAgent(agent)) {\n await ensureMount(agent).ready\n const schemas = host.tools?.schemas\n if (typeof schemas === 'function') {\n const present = new Set(assembly.tools.map(tool => tool.name))\n for (const schema of schemas.call(host.tools, agent)) {\n if (!present.has(schema.name)) assembly.tools.push(schema)\n }\n }\n }\n return next()\n }) as never)\n\n // Correctness boundary #2: tool execution for a root agent waits for the\n // same mount (serially awaited by the tool runtime before dispatch).\n host.on('tools/pre-execute', (async (\n exec: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n if (exec.agent !== undefined && isRootAgent(exec.agent)) await ensureMount(exec.agent).ready\n return next()\n }) as never)\n\n // Backfill: agents published before this plugin finished loading (a surface\n // that skips the official await-the-loader pattern DSH's headless runner\n // uses). Their next assembly still passes the gates above.\n void preparedPackages.then(() => {\n const agents = registry()\n const roots = agents?.roots\n if (typeof roots !== 'function') return\n for (const agent of roots.call(agents)) ensureMount(agent)\n })\n}\n\n/** Cordis plugin surface: `dsh plugin add pi2dsh` mounts this. */\nexport const name = 'pi2dsh'\nexport const inject = ['tools', 'systemPrompt', 'commands', 'skills']\n\nexport async function apply(ctx: Context, config: EngineConfig = {}): Promise<void> {\n // Same emission as the runtime's logger helper: the cordis logger AND the\n // console — profile logger levels must never hide what the engine mounted.\n const log = (ctx as unknown as { logger?: { info?(m: string): void, warn?(m: string): void } }).logger\n const warn = (message: string): void => { log?.warn?.(message); console.warn(message) }\n const info = (message: string): void => { log?.info?.(message); console.log(message) }\n\n // The loader resolves plugins against the profile's baseUrl; that IS the\n // profile root. The ancestor walk from the installed engine is the\n // fallback for compositions without a loader (tests, hand-built hosts).\n const baseUrl = (ctx as unknown as { baseUrl?: string }).baseUrl\n const profileRoot = (baseUrl !== undefined ? findProfileRoot(fileURLToPath(new URL('.', baseUrl))) : undefined)\n ?? findProfileRoot(dirname(fileURLToPath(import.meta.url)))\n if (profileRoot === undefined) {\n throw new Error('pi2dsh engine: no DSH profile root (cordis.yml + package.json) above the installed engine — is pi2dsh installed via `dsh plugin add pi2dsh`?')\n }\n\n // The single mount path, before any await: gates and lifecycle listeners\n // must exist the moment a surface can publish its first Agent. Package\n // preparation resolves behind this promise; the gates hold each agent's\n // first assembly until its own mount lands.\n let resolvePrepared!: (prepared: readonly PreparedPiHostPackage[]) => void\n const preparedPackages = new Promise<readonly PreparedPiHostPackage[]>(resolve => {\n resolvePrepared = resolve\n })\n installAgentScopedMounts(ctx, preparedPackages, { warn })\n\n registerVisionCompanions(ctx, config.visionCompanions)\n\n try {\n // The host half, mounted exactly once regardless of packages or agents:\n // Pi's built-in provider directory, `/login`, and credential recovery.\n // Without it a fresh engine cannot run `/login openai-codex`: DSH treats\n // the unknown slash line as a model prompt and fails on the unrelated\n // default provider credential. Community packages join the same\n // SharedHostState (keyed on ctx.root), so host-level resources stay\n // single-instance no matter which agent scope mounts them.\n await applyPreparedPiHost(ctx, [{\n name: 'pi2dsh-builtins',\n rootUrl: new URL('.', import.meta.url),\n manifest: {\n schemaVersion: 1,\n package: { name: 'pi2dsh-builtins', version: '0.0.0', source: 'engine' },\n extensions: [],\n skillDirs: [],\n prompts: [],\n },\n }])\n\n const packages: Array<{ name: string, anchor?: string }> = Array.isArray(config.packages) && config.packages.length > 0\n ? config.packages.map(name => ({ name }))\n : await discoverProfilePiPackages(profileRoot, {\n ...(Array.isArray(config.exclude) ? { exclude: config.exclude } : {}),\n warn,\n })\n if (packages.length === 0) {\n info('[pi2dsh engine] no Pi packages installed in this profile yet — add one with: dsh plugin --profile <p> add <pi-package>')\n resolvePrepared([])\n return\n }\n info(`[pi2dsh engine] preparing ${packages.length} Pi package(s): ${packages.map(pkg => pkg.name).join(', ')}`)\n const prepared = await preparePiHost(\n {\n packages: packages.map(pkg =>\n pkg.anchor === undefined ? { name: pkg.name } : { name: pkg.name, anchor: pkg.anchor }),\n },\n join(profileRoot, 'package.json'),\n )\n // Host anchors, before the per-Agent gates open: every package's\n // HOST-level contributions (provider routes, OAuth accounts, /login,\n // credential recovery, companions, skills) exist from engine apply — a\n // surface with zero live Agents (web at boot) still advertises them, the\n // first Agent's model resolution finds its routes, and routes survive\n // Agent churn. Anchors serve no Agent; sessions belong to the per-Agent\n // instances the gates mount.\n await applyPreparedPiHost(ctx, prepared, undefined, { hostAnchor: true })\n resolvePrepared(prepared)\n } catch (error) {\n // The gates must never hang on a failed preparation; agents keep running\n // as plain DSH agents while the engine failure propagates loudly.\n resolvePrepared([])\n throw error\n }\n}\n","// Engine surface: `dsh plugin add pi2dsh` resolves this entry as a cordis\n// plugin (named exports, no default — a default export would make the\n// loader discard the function-plugin namespace).\nexport { apply, inject, name, discoverProfilePiPackages, findProfileRoot, installAgentScopedMounts, type EngineConfig } from './engine.js'\n\n// The CLI-only analysis surface loads lazily: its static-analysis dependency\n// (the 23 MB typescript compiler, an optional peer) must never be pulled\n// into a profile that installs the ENGINE. The dynamic import below keeps\n// the analyzer in its own chunk, off the engine's load path.\nimport type { CompatibilityReport, ResolvedPiPackage } from './types.js'\n\n/**\n * Static compatibility analysis of one resolved Pi package (CLI `inspect`).\n * @param pkg - the resolved package.\n * @returns the compatibility report.\n */\nexport async function analyzePackage(pkg: ResolvedPiPackage): Promise<CompatibilityReport> {\n const { analyzePackage: run } = await import('./analyzer.js')\n return run(pkg)\n}\n\nexport {\n API_RULES,\n CONTEXT_RULES,\n EVENT_RULES,\n HOST_IMPORT_RULES,\n PI_AI_PACKAGES,\n PI_CODING_AGENT_PACKAGES,\n PI_TUI_PACKAGES,\n UI_CONTEXT_RULES,\n ruleForApi,\n ruleForContextProperty,\n ruleForEvent,\n ruleForHostImport,\n ruleForUiContextProperty,\n} from './compatibility.js'\nexport { applyPiHost, applyPreparedPiHost, manifestForInstalled, preparePiHost } from './host.js'\nexport { collectPiMcpServers, convertPiMcpConfig, renderMcpPatch } from './mcp-config.js'\nexport { resolvePiPackage } from './source.js'\nexport type * from './types.js'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/engine.ts","../src/index.ts"],"sourcesContent":["// The pi2dsh engine: ONE installed copy of the bridge that mounts every Pi\n// package the user has added to their DSH profile.\n//\n// dsh plugin --profile p add pi2dsh ← the engine (this plugin)\n// dsh plugin --profile p add @kassing/pi-vision ← plain npm dependency\n// dsh plugin --profile p add pi-vision-tool ← plain npm dependency\n//\n// DSH's plugin manager records only packages that declare `dsh.bundle` as\n// profile layers; everything else stays an ordinary dependency (\"a plain\n// library is fine\"). The engine reads the profile manifest's DIRECT\n// dependencies — every entry there was an explicit `dsh plugin add` — and\n// mounts each package that identifies as a Pi package, all through one\n// bridge instance: one model ledger, one command space, one upgrade unit.\n//\n// Discovery is manifest-driven, never a node_modules scan (the lesson from\n// Prettier 3 dropping directory-based plugin discovery): the dependency list\n// is the user's explicit intent, and package identification uses the same\n// Pi markers `resolvePiPackage` has always used (the `pi` manifest field,\n// with Pi's directory conventions as fallback).\n\nimport { readFile } from 'node:fs/promises'\nimport { existsSync, realpathSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPreparedPiHost, preparePiHost, type PreparedPiHostPackage } from './host.js'\nimport { getSharedChildExtensionCatalog, registerChildExtensionCatalog, registerVisionCompanions, runtimeInternals } from './runtime.js'\nimport { providePiExtensionDiscovery } from './compat/pi-coding-agent.js'\nimport { resolvePiPackage } from './source.js'\n\nexport interface EngineConfig {\n /** Mount exactly these packages (skips discovery). */\n packages?: string[]\n /** Never mount these packages even when discovered. */\n exclude?: string[]\n /**\n * Image-admission companion routes. Default: AUTOMATIC — every text-only\n * llm route gets a \\`<route>-vision\\` companion that admits pasted images\n * (a mounted vision extension analyzes them; without one the image is\n * materialized to a file any image-capable tool can read). \\`false\\`\n * turns companions off; an explicit \\`{ <route>: [modelIds] }\\` narrows.\n */\n visionCompanions?: false | Record<string, readonly string[]>\n /**\n * Serve DSH-native subagents with the profile's Pi packages (default: OFF —\n * such children run as plain DSH agents, today's behavior). Children the Pi\n * subagent bridge mints are NEVER covered: they already receive the creator's\n * own per-spawn loader mount and are recognized by the `pi2dsh-sub-` session-id\n * prefix.\n */\n serveNativeSubagents?: boolean\n}\n\n/** Locate the DSH profile root: the nearest ancestor holding cordis.yml. */\nexport function findProfileRoot(start: string): string | undefined {\n let dir = start\n for (;;) {\n if (existsSync(join(dir, 'cordis.yml')) && existsSync(join(dir, 'package.json'))) return dir\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\ninterface DiscoveredPackage {\n name: string\n dir: string\n /** Resolution anchor when the package is carried by a suite (its members\n * are the suite's dependencies, unreachable from the profile root under\n * pnpm's isolated layout). */\n anchor?: string\n}\n\ninterface ProfileManifest {\n dependencies?: Record<string, string>\n dsh?: { profile?: { bundles?: string[] } }\n}\n\nasync function readProfileManifest(profileRoot: string): Promise<ProfileManifest> {\n return JSON.parse(await readFile(join(profileRoot, 'package.json'), 'utf8')) as ProfileManifest\n}\n\nfunction resolveDependencyDir(profileRoot: string, name: string): string | undefined {\n // Direct dependencies of the profile live under its node_modules by name\n // (pnpm links them there); resolving by path needs no exports gymnastics.\n const dir = join(profileRoot, 'node_modules', name)\n return existsSync(join(dir, 'package.json')) ? dir : undefined\n}\n\n/**\n * The profile's direct dependencies that identify as Pi packages: not the\n * engine itself, not a `dsh.bundle` (those are DSH plugins, not Pi\n * packages), carrying either Pi's `pi` manifest field or extension sources\n * under Pi's directory conventions.\n */\nexport async function discoverProfilePiPackages(\n profileRoot: string,\n options: { exclude?: string[], warn?: (message: string) => void } = {},\n): Promise<DiscoveredPackage[]> {\n const warn = options.warn ?? (() => {})\n const excluded = new Set(options.exclude ?? [])\n const manifest = await readProfileManifest(profileRoot)\n const discovered: DiscoveredPackage[] = []\n for (const name of Object.keys(manifest.dependencies ?? {})) {\n if (name === 'pi2dsh' || excluded.has(name)) continue\n const dir = resolveDependencyDir(profileRoot, name)\n if (dir === undefined) {\n warn(`[pi2dsh engine] dependency ${JSON.stringify(name)} is not installed under the profile; skipping`)\n continue\n }\n let packageJson: Record<string, unknown>\n try {\n packageJson = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8')) as Record<string, unknown>\n } catch (error) {\n warn(`[pi2dsh engine] cannot read ${name}/package.json (${error instanceof Error ? error.message : String(error)}); skipping`)\n continue\n }\n // A suite: `pi2dsh: { suite: [names...] }` mounts the listed Pi packages\n // as if the user had added each one. The list is an explicit manifest —\n // the same discovery covenant as the profile dependency list, one hop\n // deeper — and the members are the suite's own dependencies, so each\n // resolves anchored at the suite package (pnpm keeps transitive\n // dependencies out of the profile root). One level only, no recursion.\n const suite = (packageJson.pi2dsh as { suite?: unknown } | undefined)?.suite\n if (Array.isArray(suite)) {\n // The anchor must be the suite's REAL directory: the profile's\n // node_modules entry is a pnpm symlink into .pnpm, and Node resolution\n // walks up from the anchor's literal path — only the realpath has the\n // suite's own dependencies as .pnpm neighbours.\n let anchorDir = dir\n try {\n anchorDir = realpathSync(dir)\n } catch { /* an unreadable dir keeps the literal path and fails loud in prepare */ }\n for (const member of suite) {\n if (typeof member !== 'string' || member.length === 0) continue\n if (member === 'pi2dsh' || excluded.has(member)) continue\n discovered.push({ name: member, dir, anchor: join(anchorDir, 'package.json') })\n }\n continue\n }\n // A dsh.bundle-declaring package is a DSH plugin layer, never a Pi package.\n if ((packageJson.dsh as { bundle?: unknown } | undefined)?.bundle !== undefined) continue\n if (packageJson.pi !== undefined && typeof packageJson.pi === 'object') {\n discovered.push({ name, dir })\n continue\n }\n // No `pi` field: fall back to Pi's directory conventions via the same\n // resolver every other mount path uses (a package with zero extension\n // sources is a plain library and stays unmounted).\n try {\n const pkg = await resolvePiPackage(dir)\n try {\n if (pkg.resources.extensions.length > 0) discovered.push({ name, dir })\n } finally {\n await pkg.dispose()\n }\n } catch {\n // Not resolvable as a Pi package — a plain library.\n }\n }\n // One mount per name. A package that is both a direct dependency and a\n // suite member mounts as the DIRECT dependency (the user's own explicit\n // add, resolved from the profile root) — the suite copy yields.\n const byName = new Map<string, DiscoveredPackage>()\n for (const pkg of discovered) {\n const existing = byName.get(pkg.name)\n if (existing === undefined || (existing.anchor !== undefined && pkg.anchor === undefined)) {\n byName.set(pkg.name, pkg)\n }\n }\n return [...byName.values()]\n}\n\ninterface AgentScopedMount {\n ready: Promise<void>\n /** Set when the mount failed; the agent then runs as a plain DSH agent. */\n failure?: string\n}\n\n/** The loose shapes of the official DSH seams this file consumes. */\ninterface AgentLike {\n ctx: Context\n}\ninterface EngineHostContext {\n /** Un-injected reflection access (the ctx.get('loader') idiom): the engine\n * must not hard-depend on the agent registry — compositions without one\n * (bare test hosts) simply have no agents to mount for. */\n get?(name: string): unknown\n tools?: { schemas?(scope: unknown): Array<{ name: string }> }\n on(name: string, listener: (...args: never[]) => unknown): () => void\n}\ninterface AgentRegistryLike {\n roots?(): AgentLike[]\n}\n\n/**\n * The single mount path on every DSH surface: one Pi runtime per root Agent.\n *\n * Pi instantiates its extensions once per session; DSH's twin of that scope is\n * the Agent with its public `agent.ctx` (\"contributions are agent-local,\n * unwind on disposal\"). Mounting is driven purely by official core seams, so\n * the same semantics hold on the TUI, web, headless, ACP and config-declared\n * agents alike:\n *\n * - `agent/created` fires on every publication path before the loop can run\n * a first turn; it eagerly starts that agent's mount.\n * - `system-prompt/assemble` (awaited waterfall, runs in every step's\n * preStep) gates the assembly on the mount and patches the pre-waterfall\n * tools snapshot from the official scoped projection `tools.schemas()` —\n * stock DSH collects `assembly.tools` before dispatching the waterfall,\n * so a mount that lands during the wait would otherwise miss turn one.\n * - `tools/pre-execute` (awaited waterfall) closes the same window for\n * direct executions that bypass assembly.\n *\n * `agent/session-start` deliberately is NOT the trigger: DSH documents it as a\n * veto-less notification that cannot gate startup. The waterfalls above are\n * the officially awaited seams, and the exact pattern of registering onto a\n * foreign agent's ctx from a lifecycle event is what DSH's own schedule\n * plugin ships.\n *\n * A mount failure never takes the Agent down: the agent keeps running as a\n * plain DSH agent, the failure is reported loudly once, and only the Pi\n * packages are missing — the capability-gap discipline, not a rollback.\n */\nexport function installAgentScopedMounts(\n ctx: Context,\n preparedPackages: Promise<readonly PreparedPiHostPackage[]>,\n report: { warn(message: string): void },\n): void {\n const host = ctx as unknown as EngineHostContext\n const mounts = new WeakMap<object, AgentScopedMount>()\n\n const registry = (): AgentRegistryLike | undefined => {\n try {\n return host.get?.('agents') as AgentRegistryLike | undefined\n } catch {\n return undefined\n }\n }\n\n const isRootAgent = (agent: object): boolean => {\n // Subagents keep Pi's own sub-session semantics through the session\n // bridge; only runtime roots receive a Pi runtime (the same distinction\n // DSH's schedule plugin draws via agents.roots()).\n const agents = registry()\n const roots = agents?.roots\n if (typeof roots !== 'function') return true\n try {\n return (roots.call(agents) as unknown[]).includes(agent)\n } catch {\n return true\n }\n }\n\n const ensureMount = (agent: AgentLike): AgentScopedMount => {\n let mount = mounts.get(agent)\n if (mount === undefined) {\n const started: AgentScopedMount = { ready: Promise.resolve() }\n started.ready = preparedPackages.then(async prepared => {\n if (prepared.length === 0) return\n try {\n await applyPreparedPiHost(agent.ctx, prepared, agent as unknown as Record<string, unknown>)\n } catch (error) {\n // Loud, once per agent; the gates resolve so the plain DSH agent\n // keeps working. Faking success is forbidden — the message names\n // what is missing.\n started.failure = error instanceof Error ? error.message : String(error)\n report.warn(`[pi2dsh engine] Pi packages failed to mount for this agent; it continues without them: ${started.failure}`)\n }\n })\n mounts.set(agent, started)\n mount = started\n }\n return mount\n }\n\n // Eager start on publication. `agent/created` reaches host-level listeners\n // on every create/resume/config path, before the loop can open a turn.\n host.on('agent/created', ((payload: { agent: AgentLike }) => {\n if (isRootAgent(payload.agent)) ensureMount(payload.agent)\n }) as never)\n\n // Correctness boundary #1: no prompt assembly for a root agent proceeds\n // before its Pi runtime is mounted, and the tools snapshot taken before\n // this waterfall is reconciled against the official scoped projection.\n host.on('system-prompt/assemble', (async (\n assembly: { tools: Array<{ name: string }> },\n context: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n const agent = context.agent\n if (agent !== undefined && isRootAgent(agent)) {\n await ensureMount(agent).ready\n const schemas = host.tools?.schemas\n if (typeof schemas === 'function') {\n const present = new Set(assembly.tools.map(tool => tool.name))\n for (const schema of schemas.call(host.tools, agent)) {\n if (!present.has(schema.name)) assembly.tools.push(schema)\n }\n }\n }\n return next()\n }) as never)\n\n // Correctness boundary #2: tool execution for a root agent waits for the\n // same mount (serially awaited by the tool runtime before dispatch).\n host.on('tools/pre-execute', (async (\n exec: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n if (exec.agent !== undefined && isRootAgent(exec.agent)) await ensureMount(exec.agent).ready\n return next()\n }) as never)\n\n // Backfill: agents published before this plugin finished loading (a surface\n // that skips the official await-the-loader pattern DSH's headless runner\n // uses). Their next assembly still passes the gates above.\n void preparedPackages.then(() => {\n const agents = registry()\n const roots = agents?.roots\n if (typeof roots !== 'function') return\n for (const agent of roots.call(agents)) ensureMount(agent)\n })\n}\n\n/** Session-id prefix minted by the Pi subagent bridge (src/subagent-bridge.ts). */\nexport const BRIDGE_CHILD_SESSION_ID_PREFIX = 'pi2dsh-sub-'\n\n/**\n * Optional coverage for DSH-native subagents, enabled by the\n * `serveNativeSubagents` engine config.\n *\n * The Pi subagent bridge already serves the children IT mints: the creator's\n * per-spawn loader mount (a8b7a0a) runs at creation and again on a persisted\n * resume. This covers the OTHER lineage — children a DSH surface mints\n * directly (DSH-native delegation, headless subagents, a second agent in the\n * web UI) — which carry no Pi lineage and would otherwise run as plain DSH\n * agents with none of the profile's Pi extensions.\n *\n * One lineage check per created Agent (maintainer point 2): subagent origin\n * AND a session id that does not carry the bridge prefix. The bridge mints\n * `pi2dsh-sub-` ids at creation and a persisted resume keeps the original id,\n * so the skip holds on both bridge paths and a bridge child is never mounted\n * twice.\n *\n * The mount reuses the engine's own child-extension catalog (maintainer\n * point 3) — the same object the bridge's hook consumes — so a native child's\n * tool set is exactly Pi's default-discovered set (the catalog's no-loader\n * path), the mount lands on the child's OWN ctx and unwinds with the child,\n * and there is no second registration path to drift out of sync.\n *\n * Partition with the root-mount path: agents the registry reports as runtime\n * roots are installAgentScopedMounts' territory (it gates them the same way\n * this path does). Live delegation shows a DSH-native child of a top-level\n * session CAN be a runtime root — serving it here as well raced two mount\n * passes onto the same scope (each package survived via the prompt-section\n * guard, but the double attempt is exactly what this flag must not do). This\n * listener therefore serves exactly the remainder: subagent-origin,\n * non-bridge, non-root agents. The predicate is the root path's own, applied\n * at the same event; its fail-open (roots() unavailable ⇒ every agent is a\n * root) mirrors here as fail-closed (this listener serves nothing).\n *\n * The readiness promise is memoized on the child's SCOPE (agent.ctx), not the\n * agent object: a re-announced agent for the same session reuses the scope,\n * and the dedupe must key on what the mount actually lands in.\n *\n * First-turn gates mirror the root path's exactly: the child's first prompt\n * assembly and its direct tool executions await the mount, and the\n * pre-waterfall tools snapshot is reconciled against the official scoped\n * projection. Without the gate a mount that lands after the first turn's\n * snapshot leaves the child's first (and sometimes only) turn tool-less —\n * proven live at depth 2, where the child's grandchild saw zero extension\n * tools because its mount raced its first model call.\n *\n * A mount failure never takes the child down: the same capability-gap\n * discipline as root mounting — the child keeps running as a plain DSH agent\n * and the failure is reported loudly once.\n */\nexport function installNativeSubagentMounts(\n ctx: Context,\n preparedPackages: Promise<readonly PreparedPiHostPackage[]>,\n enabled: boolean,\n report: { warn(message: string): void },\n): void {\n if (!enabled) return\n const host = ctx as unknown as EngineHostContext\n\n // The root path's own predicate, applied at the same event it applies its\n // (see installAgentScopedMounts): a runtime root is served — and gated —\n // there. Failing closed keeps the partition exact when roots() is absent.\n const isRootAgent = (agent: object): boolean => {\n let agents: AgentRegistryLike | undefined\n try {\n agents = host.get?.('agents') as AgentRegistryLike | undefined\n } catch {\n return true\n }\n const roots = agents?.roots\n if (typeof roots !== 'function') return true\n try {\n return (roots.call(agents) as unknown[]).includes(agent)\n } catch {\n return true\n }\n }\n\n // child scope (agent.ctx) -> its mount's readiness. Reading the catalog\n // after preparation (never before) covers children published while the\n // engine was still preparing; no catalog = plain child, exactly the bridge\n // hook's no-catalog behavior.\n const mounts = new WeakMap<object, Promise<void>>()\n\n const ensureMount = (agent: AgentLike): Promise<void> => {\n const scope = agent.ctx\n let ready = mounts.get(scope)\n if (ready === undefined) {\n ready = preparedPackages.then(async () => {\n const catalog = getSharedChildExtensionCatalog(ctx)\n if (catalog === undefined) return\n // Native children have no creator loader: the full discovered set is\n // Pi's default discovery (the no-loader path of\n // resolveChildExtensionPackages).\n const names = [...new Set(catalog.packageByEntryPath.values())]\n if (names.length === 0) return\n try {\n const failures = await catalog.mount(agent as unknown as Record<string, unknown>, names)\n for (const failure of failures) {\n // Same message shape the bridge's own mount path uses.\n report.warn(`[pi2dsh] child extension ${failure.name} did not mount: ${failure.error}`)\n }\n } catch (error) {\n report.warn(`[pi2dsh] child extension mount failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n })\n mounts.set(scope, ready)\n }\n return ready\n }\n\n const handle = (agent: AgentLike): void => {\n // The single lineage check: DSH-native subagents only. Pi-origin\n // (bridge) children are already served by the creator's per-spawn loader\n // mount; the `pi2dsh-sub-` prefix identifies them on creation AND on a\n // persisted resume, where the original session id survives.\n if (!runtimeInternals.isSubagentOrigin(agent as unknown as Record<string, unknown>)) return\n const session = (agent as { session?: { id?: unknown } }).session ?? agent\n if (String((session as { id?: unknown }).id ?? '').startsWith(BRIDGE_CHILD_SESSION_ID_PREFIX)) return\n // Runtime roots are the root-mount path's territory; serving them here\n // would race a second mount onto the same scope.\n if (isRootAgent(agent)) return\n void ensureMount(agent)\n }\n\n host.on('agent/created', ((payload: { agent: AgentLike }) => {\n if (payload.agent !== undefined) handle(payload.agent)\n }) as never)\n\n // Correctness boundary #1 (mirror of the root path's): no prompt assembly\n // for a served child proceeds before its mount lands, and the pre-waterfall\n // tools snapshot is reconciled against the official scoped projection.\n host.on('system-prompt/assemble', (async (\n assembly: { tools: Array<{ name: string }> },\n context: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n const agent = context.agent\n const ready = agent === undefined ? undefined : mounts.get(agent.ctx)\n if (ready !== undefined) {\n await ready\n const schemas = host.tools?.schemas\n if (typeof schemas === 'function') {\n const present = new Set(assembly.tools.map(tool => tool.name))\n for (const schema of schemas.call(host.tools, agent)) {\n if (!present.has(schema.name)) assembly.tools.push(schema)\n }\n }\n }\n return next()\n }) as never)\n\n // Correctness boundary #2 (mirror of the root path's): direct tool\n // executions for a served child wait for the same mount.\n host.on('tools/pre-execute', (async (\n exec: { agent?: AgentLike },\n next: () => Promise<unknown>,\n ) => {\n const agent = exec.agent\n const ready = agent === undefined ? undefined : mounts.get(agent.ctx)\n if (ready !== undefined) await ready\n return next()\n }) as never)\n}\n\n/** Cordis plugin surface: `dsh plugin add pi2dsh` mounts this. */\nexport const name = 'pi2dsh'\nexport const inject = ['tools', 'systemPrompt', 'commands', 'skills']\n\nexport async function apply(ctx: Context, config: EngineConfig = {}): Promise<void> {\n // Same emission as the runtime's logger helper: the cordis logger AND the\n // console — profile logger levels must never hide what the engine mounted.\n const log = (ctx as unknown as { logger?: { info?(m: string): void, warn?(m: string): void } }).logger\n const warn = (message: string): void => { log?.warn?.(message); console.warn(message) }\n const info = (message: string): void => { log?.info?.(message); console.log(message) }\n\n // The loader resolves plugins against the profile's baseUrl; that IS the\n // profile root. The ancestor walk from the installed engine is the\n // fallback for compositions without a loader (tests, hand-built hosts).\n const baseUrl = (ctx as unknown as { baseUrl?: string }).baseUrl\n const profileRoot = (baseUrl !== undefined ? findProfileRoot(fileURLToPath(new URL('.', baseUrl))) : undefined)\n ?? findProfileRoot(dirname(fileURLToPath(import.meta.url)))\n if (profileRoot === undefined) {\n throw new Error('pi2dsh engine: no DSH profile root (cordis.yml + package.json) above the installed engine — is pi2dsh installed via `dsh plugin add pi2dsh`?')\n }\n\n // The single mount path, before any await: gates and lifecycle listeners\n // must exist the moment a surface can publish its first Agent. Package\n // preparation resolves behind this promise; the gates hold each agent's\n // first assembly until its own mount lands.\n let resolvePrepared!: (prepared: readonly PreparedPiHostPackage[]) => void\n const preparedPackages = new Promise<readonly PreparedPiHostPackage[]>(resolve => {\n resolvePrepared = resolve\n })\n installAgentScopedMounts(ctx, preparedPackages, { warn })\n installNativeSubagentMounts(ctx, preparedPackages, config.serveNativeSubagents === true, { warn })\n\n registerVisionCompanions(ctx, config.visionCompanions)\n\n try {\n // The host half, mounted exactly once regardless of packages or agents:\n // Pi's built-in provider directory, `/login`, and credential recovery.\n // Without it a fresh engine cannot run `/login openai-codex`: DSH treats\n // the unknown slash line as a model prompt and fails on the unrelated\n // default provider credential. Community packages join the same\n // SharedHostState (keyed on ctx.root), so host-level resources stay\n // single-instance no matter which agent scope mounts them.\n await applyPreparedPiHost(ctx, [{\n name: 'pi2dsh-builtins',\n rootUrl: new URL('.', import.meta.url),\n manifest: {\n schemaVersion: 1,\n package: { name: 'pi2dsh-builtins', version: '0.0.0', source: 'engine' },\n extensions: [],\n skillDirs: [],\n prompts: [],\n },\n }])\n\n const packages: Array<{ name: string, anchor?: string }> = Array.isArray(config.packages) && config.packages.length > 0\n ? config.packages.map(name => ({ name }))\n : await discoverProfilePiPackages(profileRoot, {\n ...(Array.isArray(config.exclude) ? { exclude: config.exclude } : {}),\n warn,\n })\n if (packages.length === 0) {\n info('[pi2dsh engine] no Pi packages installed in this profile yet — add one with: dsh plugin --profile <p> add <pi-package>')\n resolvePrepared([])\n return\n }\n info(`[pi2dsh engine] preparing ${packages.length} Pi package(s): ${packages.map(pkg => pkg.name).join(', ')}`)\n const prepared = await preparePiHost(\n {\n packages: packages.map(pkg =>\n pkg.anchor === undefined ? { name: pkg.name } : { name: pkg.name, anchor: pkg.anchor }),\n },\n join(profileRoot, 'package.json'),\n )\n // Child-extension catalog: what real Pi's createAgentSession \"default-\n // discovered extensions\" means on this host. Entries are each package's\n // DECLARED pi extension files (absolute, inside the installed dir), so a\n // creator's own filter code (pi-subagents' extensions/exclude/ext:\n // machinery) canonicalizes them exactly as it does on Pi. Mounting lands\n // on the child agent's OWN ctx — contributions unwind with the agent,\n // which is what makes per-child instances leak-free by construction.\n const entryCatalog = new Map<string, string>()\n for (const pkg of prepared) {\n const rootDir = fileURLToPath(pkg.rootUrl)\n // Resolved keys, because the consumer looks entries up by resolved path.\n for (const rel of pkg.manifest.extensions) entryCatalog.set(resolve(rootDir, rel), pkg.name)\n }\n providePiExtensionDiscovery([...entryCatalog.keys()].map(path => ({ path })))\n registerChildExtensionCatalog(ctx, {\n packageByEntryPath: entryCatalog,\n mount: async (childAgent, packageNames) => {\n const wanted = new Set(packageNames)\n const subset = prepared.filter(pkg => wanted.has(pkg.name))\n const scope = (childAgent as { ctx?: Context }).ctx\n if (scope === undefined) {\n return subset.map(pkg => ({ name: pkg.name, error: 'the child agent exposes no ctx scope to mount into' }))\n }\n return applyPreparedPiHost(scope, subset, childAgent)\n },\n })\n\n // Host anchors, before the per-Agent gates open: every package's\n // HOST-level contributions (provider routes, OAuth accounts, /login,\n // credential recovery, companions, skills) exist from engine apply — a\n // surface with zero live Agents (web at boot) still advertises them, the\n // first Agent's model resolution finds its routes, and routes survive\n // Agent churn. Anchors serve no Agent; sessions belong to the per-Agent\n // instances the gates mount.\n await applyPreparedPiHost(ctx, prepared, undefined, { hostAnchor: true })\n resolvePrepared(prepared)\n } catch (error) {\n // The gates must never hang on a failed preparation; agents keep running\n // as plain DSH agents while the engine failure propagates loudly.\n resolvePrepared([])\n throw error\n }\n}\n","// Engine surface: `dsh plugin add pi2dsh` resolves this entry as a cordis\n// plugin (named exports, no default — a default export would make the\n// loader discard the function-plugin namespace).\nexport { apply, inject, name, discoverProfilePiPackages, findProfileRoot, installAgentScopedMounts, type EngineConfig } from './engine.js'\n\n// The CLI-only analysis surface loads lazily: its static-analysis dependency\n// (the 23 MB typescript compiler, an optional peer) must never be pulled\n// into a profile that installs the ENGINE. The dynamic import below keeps\n// the analyzer in its own chunk, off the engine's load path.\nimport type { CompatibilityReport, ResolvedPiPackage } from './types.js'\n\n/**\n * Static compatibility analysis of one resolved Pi package (CLI `inspect`).\n * @param pkg - the resolved package.\n * @returns the compatibility report.\n */\nexport async function analyzePackage(pkg: ResolvedPiPackage): Promise<CompatibilityReport> {\n const { analyzePackage: run } = await import('./analyzer.js')\n return run(pkg)\n}\n\nexport {\n API_RULES,\n CONTEXT_RULES,\n EVENT_RULES,\n HOST_IMPORT_RULES,\n PI_AI_PACKAGES,\n PI_CODING_AGENT_PACKAGES,\n PI_TUI_PACKAGES,\n UI_CONTEXT_RULES,\n ruleForApi,\n ruleForContextProperty,\n ruleForEvent,\n ruleForHostImport,\n ruleForUiContextProperty,\n} from './compatibility.js'\nexport { applyPiHost, applyPreparedPiHost, manifestForInstalled, preparePiHost } from './host.js'\nexport { collectPiMcpServers, convertPiMcpConfig, renderMcpPatch } from './mcp-config.js'\nexport { resolvePiPackage } from './source.js'\nexport type * from './types.js'\n"],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
|
|
2
|
+
import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
//#region src/mcp-config.ts
|
|
7
|
+
/** Pi's documented config precedence, lowest to highest. */
|
|
8
|
+
function piMcpConfigPaths(cwd) {
|
|
9
|
+
const home = homedir();
|
|
10
|
+
return [
|
|
11
|
+
join(home, ".config", "mcp", "mcp.json"),
|
|
12
|
+
join(home, ".agents", "mcp.json"),
|
|
13
|
+
join(home, ".agents", "mcp", "mcp.json"),
|
|
14
|
+
join(getAgentDir(), "mcp.json"),
|
|
15
|
+
join(cwd, ".mcp.json"),
|
|
16
|
+
join(cwd, ".pi", "mcp.json")
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
function stringRecord(value) {
|
|
20
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
21
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
22
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
23
|
+
}
|
|
24
|
+
function parseServer(name, raw, sourcePath) {
|
|
25
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
26
|
+
const record = raw;
|
|
27
|
+
const disabled = record.disabled === true;
|
|
28
|
+
if (typeof record.url === "string") return {
|
|
29
|
+
name,
|
|
30
|
+
sourcePath,
|
|
31
|
+
transport: "streamable-http",
|
|
32
|
+
url: record.url,
|
|
33
|
+
...stringRecord(record.headers) === void 0 ? {} : { headers: stringRecord(record.headers) },
|
|
34
|
+
disabled
|
|
35
|
+
};
|
|
36
|
+
if (typeof record.command === "string") return {
|
|
37
|
+
name,
|
|
38
|
+
sourcePath,
|
|
39
|
+
transport: "stdio",
|
|
40
|
+
command: record.command,
|
|
41
|
+
...Array.isArray(record.args) ? { args: record.args.map(String) } : {},
|
|
42
|
+
...stringRecord(record.env) === void 0 ? {} : { env: stringRecord(record.env) },
|
|
43
|
+
...typeof record.cwd === "string" ? { cwd: record.cwd } : {},
|
|
44
|
+
disabled
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function collectPiMcpServers(cwd, extraPaths = []) {
|
|
48
|
+
const servers = /* @__PURE__ */ new Map();
|
|
49
|
+
const sources = [];
|
|
50
|
+
for (const path of [...piMcpConfigPaths(cwd), ...extraPaths]) {
|
|
51
|
+
if (!existsSync(path)) continue;
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
55
|
+
} catch {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const mcpServers = parsed?.mcpServers;
|
|
59
|
+
if (typeof mcpServers !== "object" || mcpServers === null) continue;
|
|
60
|
+
sources.push(path);
|
|
61
|
+
for (const [name, raw] of Object.entries(mcpServers)) {
|
|
62
|
+
const record = raw;
|
|
63
|
+
const existing = servers.get(name);
|
|
64
|
+
if (existing !== void 0 && typeof record === "object" && record !== null && record.command === void 0 && record.url === void 0 && typeof record.disabled === "boolean") {
|
|
65
|
+
servers.set(name, {
|
|
66
|
+
...existing,
|
|
67
|
+
disabled: record.disabled
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const server = parseServer(name, raw, path);
|
|
72
|
+
if (server !== void 0) servers.set(name, server);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
servers,
|
|
77
|
+
sources
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]{1,32}$/u;
|
|
81
|
+
const ENV_REFERENCE_PATTERN = /^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/u;
|
|
82
|
+
const SECRETISH_KEY_PATTERN = /token|secret|key|password|credential/iu;
|
|
83
|
+
function js(expression) {
|
|
84
|
+
return { __pi2dshJs: expression };
|
|
85
|
+
}
|
|
86
|
+
function convertValueMap(server, kind, values, warnings) {
|
|
87
|
+
if (values === void 0) return void 0;
|
|
88
|
+
const output = {};
|
|
89
|
+
for (const [key, value] of Object.entries(values)) {
|
|
90
|
+
const reference = ENV_REFERENCE_PATTERN.exec(value);
|
|
91
|
+
if (reference !== null) {
|
|
92
|
+
output[key] = js(`process.env.${reference[1]}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (SECRETISH_KEY_PATTERN.test(key)) warnings.push({
|
|
96
|
+
server,
|
|
97
|
+
message: `${kind}.${key} carries a literal value; move the secret to an environment variable and reference it as $NAME so the patch stays credential-free`
|
|
98
|
+
});
|
|
99
|
+
output[key] = value;
|
|
100
|
+
}
|
|
101
|
+
return output;
|
|
102
|
+
}
|
|
103
|
+
function convertPiMcpConfig(cwd, extraPaths = []) {
|
|
104
|
+
const { servers, sources } = collectPiMcpServers(cwd, extraPaths);
|
|
105
|
+
const warnings = [];
|
|
106
|
+
const entries = [];
|
|
107
|
+
for (const server of servers.values()) {
|
|
108
|
+
if (server.disabled) {
|
|
109
|
+
warnings.push({
|
|
110
|
+
server: server.name,
|
|
111
|
+
message: "skipped: disabled in Pi configuration"
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!SERVER_NAME_PATTERN.test(server.name)) {
|
|
116
|
+
warnings.push({
|
|
117
|
+
server: server.name,
|
|
118
|
+
message: "skipped: DSH serverName must match [A-Za-z0-9_-]{1,32}; rename the server in the Pi config"
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const config = {
|
|
123
|
+
serverName: server.name,
|
|
124
|
+
transport: server.transport
|
|
125
|
+
};
|
|
126
|
+
if (server.transport === "stdio") {
|
|
127
|
+
config.command = server.command;
|
|
128
|
+
if (server.args !== void 0) config.args = server.args;
|
|
129
|
+
const env = convertValueMap(server.name, "env", server.env, warnings);
|
|
130
|
+
if (env !== void 0) config.env = env;
|
|
131
|
+
if (server.cwd !== void 0) config.cwd = server.cwd;
|
|
132
|
+
} else {
|
|
133
|
+
config.url = server.url;
|
|
134
|
+
const headers = convertValueMap(server.name, "headers", server.headers, warnings);
|
|
135
|
+
if (headers !== void 0) config.headers = headers;
|
|
136
|
+
}
|
|
137
|
+
entries.push({
|
|
138
|
+
id: `mcp-${server.name}`,
|
|
139
|
+
name: "@deepseek-ai/dsh-mcp-client",
|
|
140
|
+
config
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
servers: [...servers.values()],
|
|
145
|
+
entries,
|
|
146
|
+
warnings,
|
|
147
|
+
sources
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function yamlScalar(value, indent) {
|
|
151
|
+
if (typeof value === "object" && value !== null && "__pi2dshJs" in value) return `!!js ${value.__pi2dshJs}`;
|
|
152
|
+
if (typeof value === "string") return /^[A-Za-z0-9@._\/:-]+$/u.test(value) ? value : JSON.stringify(value);
|
|
153
|
+
if (Array.isArray(value)) {
|
|
154
|
+
if (value.length === 0) return "[]";
|
|
155
|
+
return `\n${value.map((item) => `${indent} - ${yamlScalar(item, `${indent} `)}`).join("\n")}`;
|
|
156
|
+
}
|
|
157
|
+
if (typeof value === "object" && value !== null) {
|
|
158
|
+
const record = value;
|
|
159
|
+
const keys = Object.keys(record);
|
|
160
|
+
if (keys.length === 0) return "{}";
|
|
161
|
+
return `\n${keys.map((key) => `${indent} ${key}: ${yamlScalar(record[key], `${indent} `)}`).join("\n")}`;
|
|
162
|
+
}
|
|
163
|
+
return String(value);
|
|
164
|
+
}
|
|
165
|
+
/** Render the converted entries as a cordis.patch.yml `insert` block. */
|
|
166
|
+
function renderMcpPatch(result) {
|
|
167
|
+
if (result.entries.length === 0) return "# no enabled MCP servers found in Pi configuration\n";
|
|
168
|
+
const lines = ["- insert:"];
|
|
169
|
+
for (const entry of result.entries) {
|
|
170
|
+
lines.push(` - id: ${String(entry.id)}`);
|
|
171
|
+
lines.push(` name: '@deepseek-ai/dsh-mcp-client'`);
|
|
172
|
+
lines.push(` inject: [tools]`);
|
|
173
|
+
lines.push(` config:${yamlScalar(entry.config, " ")}`);
|
|
174
|
+
}
|
|
175
|
+
return `${lines.join("\n")}\n`;
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
export { renderMcpPatch as i, convertPiMcpConfig as n, piMcpConfigPaths as r, collectPiMcpServers as t };
|
|
179
|
+
|
|
180
|
+
//# sourceMappingURL=mcp-config-C1K-IzzB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-config-C1K-IzzB.mjs","names":[],"sources":["../src/mcp-config.ts"],"sourcesContent":["// Pi MCP configuration -> DSH mcp-client patch entries.\n//\n// Pi (via pi-mcp-adapter) reads the standard `mcpServers` files. DSH ships an\n// official MCP client (`@deepseek-ai/dsh-mcp-client`) configured through\n// cordis patch layers — so migration is config translation, not running Pi's\n// adapter code. This module reads Pi's documented six-layer precedence,\n// resolves `disabled` flags, and emits one patch entry per enabled server.\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { getAgentDir } from './compat/vendor/pi-config-shim.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiMcpServer {\n name: string\n sourcePath: string\n transport: 'stdio' | 'streamable-http'\n command?: string\n args?: string[]\n env?: Record<string, string>\n cwd?: string\n url?: string\n headers?: Record<string, string>\n disabled: boolean\n}\n\nexport interface McpConversionWarning {\n server: string\n message: string\n}\n\nexport interface McpConversionResult {\n servers: PiMcpServer[]\n entries: UnknownRecord[]\n warnings: McpConversionWarning[]\n sources: string[]\n}\n\n/** Pi's documented config precedence, lowest to highest. */\nexport function piMcpConfigPaths(cwd: string): string[] {\n const home = homedir()\n return [\n join(home, '.config', 'mcp', 'mcp.json'),\n join(home, '.agents', 'mcp.json'),\n join(home, '.agents', 'mcp', 'mcp.json'),\n join(getAgentDir(), 'mcp.json'),\n join(cwd, '.mcp.json'),\n join(cwd, '.pi', 'mcp.json'),\n ]\n}\n\nfunction stringRecord(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined\n const entries = Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string')\n return entries.length === 0 ? undefined : Object.fromEntries(entries)\n}\n\nfunction parseServer(name: string, raw: unknown, sourcePath: string): PiMcpServer | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const record = raw as UnknownRecord\n const disabled = record.disabled === true\n if (typeof record.url === 'string') {\n return {\n name, sourcePath, transport: 'streamable-http', url: record.url,\n ...(stringRecord(record.headers) === undefined ? {} : { headers: stringRecord(record.headers)! }),\n disabled,\n }\n }\n if (typeof record.command === 'string') {\n return {\n name, sourcePath, transport: 'stdio', command: record.command,\n ...(Array.isArray(record.args) ? { args: record.args.map(String) } : {}),\n ...(stringRecord(record.env) === undefined ? {} : { env: stringRecord(record.env)! }),\n ...(typeof record.cwd === 'string' ? { cwd: record.cwd } : {}),\n disabled,\n }\n }\n return undefined\n}\n\nexport function collectPiMcpServers(cwd: string, extraPaths: string[] = []): { servers: Map<string, PiMcpServer>; sources: string[] } {\n const servers = new Map<string, PiMcpServer>()\n const sources: string[] = []\n for (const path of [...piMcpConfigPaths(cwd), ...extraPaths]) {\n if (!existsSync(path)) continue\n let parsed: unknown\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'))\n } catch {\n continue\n }\n const mcpServers = (parsed as UnknownRecord | undefined)?.mcpServers\n if (typeof mcpServers !== 'object' || mcpServers === null) continue\n sources.push(path)\n for (const [name, raw] of Object.entries(mcpServers)) {\n const record = raw as UnknownRecord | null\n const existing = servers.get(name)\n // Later (higher-precedence) layers override whole entries; a bare\n // `disabled` toggle updates the effective server from a lower layer.\n // The toggle keeps the DEFINING file's sourcePath — a project-local\n // disable of a global server must not reclassify it as project-defined\n // (the Settings view groups by where the definition lives).\n if (existing !== undefined && typeof record === 'object' && record !== null\n && record.command === undefined && record.url === undefined && typeof record.disabled === 'boolean') {\n servers.set(name, { ...existing, disabled: record.disabled })\n continue\n }\n const server = parseServer(name, raw, path)\n if (server !== undefined) servers.set(name, server)\n }\n }\n return { servers, sources }\n}\n\nconst SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]{1,32}$/u\nconst ENV_REFERENCE_PATTERN = /^\\$\\{?([A-Za-z_][A-Za-z0-9_]*)\\}?$/u\nconst SECRETISH_KEY_PATTERN = /token|secret|key|password|credential/iu\n\ninterface JsExpression {\n __pi2dshJs: string\n}\n\nfunction js(expression: string): JsExpression {\n return { __pi2dshJs: expression }\n}\n\nfunction convertValueMap(\n server: string,\n kind: 'env' | 'headers',\n values: Record<string, string> | undefined,\n warnings: McpConversionWarning[],\n): Record<string, string | JsExpression> | undefined {\n if (values === undefined) return undefined\n const output: Record<string, string | JsExpression> = {}\n for (const [key, value] of Object.entries(values)) {\n const reference = ENV_REFERENCE_PATTERN.exec(value)\n if (reference !== null) {\n output[key] = js(`process.env.${reference[1]}`)\n continue\n }\n if (SECRETISH_KEY_PATTERN.test(key)) {\n warnings.push({\n server,\n message: `${kind}.${key} carries a literal value; move the secret to an environment variable and reference it as $NAME so the patch stays credential-free`,\n })\n }\n output[key] = value\n }\n return output\n}\n\nexport function convertPiMcpConfig(cwd: string, extraPaths: string[] = []): McpConversionResult {\n const { servers, sources } = collectPiMcpServers(cwd, extraPaths)\n const warnings: McpConversionWarning[] = []\n const entries: UnknownRecord[] = []\n for (const server of servers.values()) {\n if (server.disabled) {\n warnings.push({ server: server.name, message: 'skipped: disabled in Pi configuration' })\n continue\n }\n if (!SERVER_NAME_PATTERN.test(server.name)) {\n warnings.push({\n server: server.name,\n message: 'skipped: DSH serverName must match [A-Za-z0-9_-]{1,32}; rename the server in the Pi config',\n })\n continue\n }\n const config: UnknownRecord = { serverName: server.name, transport: server.transport }\n if (server.transport === 'stdio') {\n config.command = server.command\n if (server.args !== undefined) config.args = server.args\n const env = convertValueMap(server.name, 'env', server.env, warnings)\n if (env !== undefined) config.env = env\n if (server.cwd !== undefined) config.cwd = server.cwd\n } else {\n config.url = server.url\n const headers = convertValueMap(server.name, 'headers', server.headers, warnings)\n if (headers !== undefined) config.headers = headers\n }\n entries.push({ id: `mcp-${server.name}`, name: '@deepseek-ai/dsh-mcp-client', config })\n }\n return { servers: [...servers.values()], entries, warnings, sources }\n}\n\nfunction yamlScalar(value: unknown, indent: string): string {\n if (typeof value === 'object' && value !== null && '__pi2dshJs' in (value as JsExpression)) {\n return `!!js ${(value as JsExpression).__pi2dshJs}`\n }\n if (typeof value === 'string') {\n return /^[A-Za-z0-9@._\\/:-]+$/u.test(value) ? value : JSON.stringify(value)\n }\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n return `\\n${value.map(item => `${indent} - ${yamlScalar(item, `${indent} `)}`).join('\\n')}`\n }\n if (typeof value === 'object' && value !== null) {\n const record = value as UnknownRecord\n const keys = Object.keys(record)\n if (keys.length === 0) return '{}'\n return `\\n${keys.map(key => `${indent} ${key}: ${yamlScalar(record[key], `${indent} `)}`).join('\\n')}`\n }\n return String(value)\n}\n\n/** Render the converted entries as a cordis.patch.yml `insert` block. */\nexport function renderMcpPatch(result: McpConversionResult): string {\n if (result.entries.length === 0) return '# no enabled MCP servers found in Pi configuration\\n'\n const lines: string[] = ['- insert:']\n for (const entry of result.entries) {\n lines.push(` - id: ${String(entry.id)}`)\n lines.push(` name: '@deepseek-ai/dsh-mcp-client'`)\n lines.push(` inject: [tools]`)\n lines.push(` config:${yamlScalar(entry.config, ' ')}`)\n }\n return `${lines.join('\\n')}\\n`\n}\n"],"mappings":"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"}
|
|
Binary file
|