pi2dsh 0.2.0 → 0.3.1

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.
Files changed (53) hide show
  1. package/README.md +44 -19
  2. package/README.zh.md +44 -19
  3. package/dist/build-DsGYAgiT.mjs +4416 -0
  4. package/dist/build-DsGYAgiT.mjs.map +1 -0
  5. package/dist/cli.mjs +2 -2
  6. package/dist/compat/pi-ai.d.mts +195 -2435
  7. package/dist/compat/pi-ai.d.mts.map +1 -1
  8. package/dist/compat/pi-ai.mjs +3 -4668
  9. package/dist/compat/pi-coding-agent.d.mts +333 -14
  10. package/dist/compat/pi-coding-agent.d.mts.map +1 -1
  11. package/dist/compat/pi-coding-agent.mjs +2 -2
  12. package/dist/compat/pi-tui.mjs +1 -3620
  13. package/dist/credentials-oauth.d.mts +27 -0
  14. package/dist/credentials-oauth.d.mts.map +1 -0
  15. package/dist/credentials-oauth.mjs +91 -0
  16. package/dist/credentials-oauth.mjs.map +1 -0
  17. package/dist/host.mjs +4 -2
  18. package/dist/host.mjs.map +1 -1
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +2 -2
  21. package/dist/{mcp-config-jL9w70It.mjs → mcp-config-gqg7GJEE.mjs} +115 -51
  22. package/dist/mcp-config-gqg7GJEE.mjs.map +1 -0
  23. package/dist/oauth-bridge-BpPrppjR.mjs +359 -0
  24. package/dist/oauth-bridge-BpPrppjR.mjs.map +1 -0
  25. package/dist/pi-ai-CWFlgigJ.mjs +2947 -0
  26. package/dist/pi-ai-CWFlgigJ.mjs.map +1 -0
  27. package/dist/pi-coding-agent-Z1hTs61i.mjs +5203 -0
  28. package/dist/pi-coding-agent-Z1hTs61i.mjs.map +1 -0
  29. package/dist/pi-tui-5CYLcj-_.mjs +4512 -0
  30. package/dist/pi-tui-5CYLcj-_.mjs.map +1 -0
  31. package/dist/pi-types-BLt46jaf.d.mts +2494 -0
  32. package/dist/pi-types-BLt46jaf.d.mts.map +1 -0
  33. package/dist/{rolldown-runtime-C2Q2p085.mjs → rolldown-runtime-D-uZhY3_.mjs} +3 -2
  34. package/dist/{runtime-D84Hv_3m.mjs → runtime-6DefpI6h.mjs} +344 -23
  35. package/dist/runtime-6DefpI6h.mjs.map +1 -0
  36. package/dist/runtime.d.mts.map +1 -1
  37. package/dist/runtime.mjs +1 -1
  38. package/dist/{source-D7Ir-rPT.mjs → source-0sA5z08z.mjs} +6 -2
  39. package/dist/source-0sA5z08z.mjs.map +1 -0
  40. package/package.json +14 -1
  41. package/dist/compat/pi-ai.mjs.map +0 -1
  42. package/dist/compat/pi-tui.mjs.map +0 -1
  43. package/dist/mcp-config-jL9w70It.mjs.map +0 -1
  44. package/dist/pi-coding-agent-Dsg6_0ua.mjs +0 -2060
  45. package/dist/pi-coding-agent-Dsg6_0ua.mjs.map +0 -1
  46. package/dist/pi-tui-utils-CcaVtm-3.mjs +0 -895
  47. package/dist/pi-tui-utils-CcaVtm-3.mjs.map +0 -1
  48. package/dist/pi-types-KazmR2O5.d.mts +0 -62
  49. package/dist/pi-types-KazmR2O5.d.mts.map +0 -1
  50. package/dist/pi-uuid-Db8ShZsK.mjs +0 -47
  51. package/dist/pi-uuid-Db8ShZsK.mjs.map +0 -1
  52. package/dist/runtime-D84Hv_3m.mjs.map +0 -1
  53. package/dist/source-D7Ir-rPT.mjs.map +0 -1
@@ -0,0 +1,27 @@
1
+
2
+ import { CredentialInfo, CredentialProvider, CredentialRef, ResolvedCredential } from "@deepseek-ai/dsh-credentials";
3
+ import { Context } from "@deepseek-ai/cordis";
4
+ //#region src/credentials-oauth.d.ts
5
+ type UnknownRecord = Record<string, unknown>;
6
+ declare function oauthCredentialRef(providerId: string): string;
7
+ interface PiOAuthCredentialProviderOptions {
8
+ /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */
9
+ authPath?: string;
10
+ /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */
11
+ providers?: ReadonlyMap<string, UnknownRecord>;
12
+ }
13
+ declare class PiOAuthCredentialProvider extends CredentialProvider {
14
+ private readonly oauthStore;
15
+ private readonly extraProviders;
16
+ constructor(ctx: Context, options?: PiOAuthCredentialProviderOptions);
17
+ private oauthConfigFor;
18
+ resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined>;
19
+ describe(ref: CredentialRef): Promise<CredentialInfo>;
20
+ set(ref: CredentialRef, _value: string): Promise<void>;
21
+ unset(ref: CredentialRef): Promise<void>;
22
+ }
23
+ declare const name = "pi2dsh-credentials-oauth";
24
+ declare function apply(ctx: Context, config?: PiOAuthCredentialProviderOptions): void;
25
+ //#endregion
26
+ export { PiOAuthCredentialProvider, PiOAuthCredentialProviderOptions, apply, name, oauthCredentialRef };
27
+ //# sourceMappingURL=credentials-oauth.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials-oauth.d.mts","names":[],"sources":["../src/credentials-oauth.ts"],"mappings":";;;;KAOK,gBAAgB;iBAgBL,mBAAmB;UASlB;;EAEf;;EAEA,YAAY,oBAAoB;;cAGrB,kCAAkC;mBAC5B;mBACA;EAEL,YAAA,KAAK,SAAS,UAAS;UAM3B;EAQF,QAAQ,KAAK,gBAAgB,QAAQ;EAiBrC,SAAS,KAAK,gBAAgB,QAAQ;EAUtC,IAAI,KAAK,eAAe,iBAAiB;EAIzC,MAAM,KAAK,gBAAgB;;cAUtB;iBAEG,MAAM,KAAK,SAAS,SAAQ"}
@@ -0,0 +1,91 @@
1
+
2
+ import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
3
+ import "./pi-coding-agent-Z1hTs61i.mjs";
4
+ import { a as storedOAuthCredential, i as resolveOAuthApiKey, r as providerSupportsOAuth, t as FileCredentialStore } from "./oauth-bridge-BpPrppjR.mjs";
5
+ import { r as builtinProviders } from "./pi-ai-CWFlgigJ.mjs";
6
+ import { join } from "node:path";
7
+ import { CredentialProvider } from "@deepseek-ai/dsh-credentials";
8
+ //#region src/credentials-oauth.ts
9
+ const OAUTH_REF_PREFIX = "PI2DSH_OAUTH_";
10
+ function oauthCredentialRef(providerId) {
11
+ return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll("-", "_")}`;
12
+ }
13
+ function providerIdOfRef(ref) {
14
+ if (!ref.startsWith(OAUTH_REF_PREFIX)) return void 0;
15
+ return ref.slice(13).toLowerCase().replaceAll("_", "-");
16
+ }
17
+ var PiOAuthCredentialProvider = class extends CredentialProvider {
18
+ oauthStore;
19
+ extraProviders;
20
+ constructor(ctx, options = {}) {
21
+ super(ctx);
22
+ this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), "auth.json"));
23
+ this.extraProviders = options.providers ?? /* @__PURE__ */ new Map();
24
+ }
25
+ oauthConfigFor(providerId) {
26
+ const registered = this.extraProviders.get(providerId);
27
+ if (providerSupportsOAuth(registered)) return registered;
28
+ const builtin = builtinProviders().find((provider) => provider.id === providerId);
29
+ if (builtin === void 0) return void 0;
30
+ return {
31
+ name: builtin.name,
32
+ baseUrl: builtin.baseUrl,
33
+ oauth: builtin.auth.oauth
34
+ };
35
+ }
36
+ async resolve(ref) {
37
+ const providerId = providerIdOfRef(String(ref));
38
+ if (providerId === void 0) {
39
+ const value = process.env[String(ref)];
40
+ return value !== void 0 && value.length > 0 ? {
41
+ value,
42
+ source: "env"
43
+ } : void 0;
44
+ }
45
+ const config = this.oauthConfigFor(providerId);
46
+ if (config === void 0) return void 0;
47
+ const value = await resolveOAuthApiKey({
48
+ providerId,
49
+ providerName: typeof config.name === "string" ? config.name : providerId,
50
+ providerConfig: config,
51
+ store: this.oauthStore
52
+ });
53
+ return value !== void 0 && value.length > 0 ? {
54
+ value,
55
+ source: "pi-oauth"
56
+ } : void 0;
57
+ }
58
+ async describe(ref) {
59
+ const providerId = providerIdOfRef(String(ref));
60
+ if (providerId === void 0) {
61
+ const value = process.env[String(ref)];
62
+ return {
63
+ configured: value !== void 0 && value.length > 0,
64
+ ...value ? { source: "env" } : {},
65
+ writable: false
66
+ };
67
+ }
68
+ const stored = await storedOAuthCredential(this.oauthStore, providerId);
69
+ return {
70
+ configured: stored !== void 0,
71
+ ...stored !== void 0 ? { source: "pi-oauth" } : {},
72
+ writable: false
73
+ };
74
+ }
75
+ async set(ref, _value) {
76
+ throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`);
77
+ }
78
+ async unset(ref) {
79
+ const providerId = providerIdOfRef(String(ref));
80
+ if (providerId === void 0) throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`);
81
+ await this.oauthStore.delete(providerId, void 0);
82
+ }
83
+ };
84
+ const name = "pi2dsh-credentials-oauth";
85
+ function apply(ctx, config = {}) {
86
+ new PiOAuthCredentialProvider(ctx, config);
87
+ }
88
+ //#endregion
89
+ export { PiOAuthCredentialProvider, apply, name, oauthCredentialRef };
90
+
91
+ //# sourceMappingURL=credentials-oauth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials-oauth.mjs","names":[],"sources":["../src/credentials-oauth.ts"],"sourcesContent":["import { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { CredentialProvider, type CredentialInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials'\nimport { FileCredentialStore, providerSupportsOAuth, resolveOAuthApiKey, storedOAuthCredential } from './oauth-bridge.js'\nimport { builtinProviders } from './compat/pi-ai.js'\nimport { getAgentDir } from './compat/pi-coding-agent.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// L4 of the OAuth host seam: a standard dsh-credentials provider that serves\n// Pi OAuth tokens to DSH's native LLM request path. A route configured as\n// `apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX` resolves per request through this\n// provider — Pi's double-checked-lock refresh runs on every resolution, so a\n// rotated token reaches the next model call with no restart, exactly the\n// per-operation semantics the credentials seam demands. Every other reference\n// falls through to the process environment, so one provider instance serves a\n// whole composition.\n//\n// Reference convention: PI2DSH_OAUTH_<PROVIDER_ID> where the provider id is\n// upper-cased with `-` as `_` (openai-codex → PI2DSH_OAUTH_OPENAI_CODEX).\n\nconst OAUTH_REF_PREFIX = 'PI2DSH_OAUTH_'\n\nexport function oauthCredentialRef(providerId: string): string {\n return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll('-', '_')}`\n}\n\nfunction providerIdOfRef(ref: string): string | undefined {\n if (!ref.startsWith(OAUTH_REF_PREFIX)) return undefined\n return ref.slice(OAUTH_REF_PREFIX.length).toLowerCase().replaceAll('_', '-')\n}\n\nexport interface PiOAuthCredentialProviderOptions {\n /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */\n authPath?: string\n /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */\n providers?: ReadonlyMap<string, UnknownRecord>\n}\n\nexport class PiOAuthCredentialProvider extends CredentialProvider {\n private readonly oauthStore: FileCredentialStore\n private readonly extraProviders: ReadonlyMap<string, UnknownRecord>\n\n constructor(ctx: Context, options: PiOAuthCredentialProviderOptions = {}) {\n super(ctx)\n this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), 'auth.json'))\n this.extraProviders = options.providers ?? new Map()\n }\n\n private oauthConfigFor(providerId: string): UnknownRecord | undefined {\n const registered = this.extraProviders.get(providerId)\n if (providerSupportsOAuth(registered)) return registered\n const builtin = builtinProviders().find(provider => provider.id === providerId)\n if (builtin === undefined) return undefined\n return { name: builtin.name, baseUrl: builtin.baseUrl, oauth: builtin.auth.oauth }\n }\n\n async resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return value !== undefined && value.length > 0 ? { value, source: 'env' } : undefined\n }\n const config = this.oauthConfigFor(providerId)\n if (config === undefined) return undefined\n const value = await resolveOAuthApiKey({\n providerId,\n providerName: typeof config.name === 'string' ? config.name : providerId,\n providerConfig: config,\n store: this.oauthStore,\n })\n return value !== undefined && value.length > 0 ? { value, source: 'pi-oauth' } : undefined\n }\n\n async describe(ref: CredentialRef): Promise<CredentialInfo> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return { configured: value !== undefined && value.length > 0, ...(value ? { source: 'env' } : {}), writable: false }\n }\n const stored = await storedOAuthCredential(this.oauthStore, providerId)\n return { configured: stored !== undefined, ...(stored !== undefined ? { source: 'pi-oauth' } : {}), writable: false }\n }\n\n async set(ref: CredentialRef, _value: string): Promise<void> {\n throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`)\n }\n\n async unset(ref: CredentialRef): Promise<void> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`)\n }\n // Logging out is a legitimate unset: drop the stored token.\n await this.oauthStore.delete(providerId, undefined)\n }\n}\n\nexport const name = 'pi2dsh-credentials-oauth'\n\nexport function apply(ctx: Context, config: PiOAuthCredentialProviderOptions = {}): void {\n // The Service constructor registers itself as ctx.credentials.\n void new PiOAuthCredentialProvider(ctx, config)\n}\n"],"mappings":";;;;;;;;AAqBA,MAAM,mBAAmB;AAEzB,SAAgB,mBAAmB,YAA4B;CAC7D,OAAO,GAAG,mBAAmB,WAAW,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG;AAC3E;AAEA,SAAS,gBAAgB,KAAiC;CACxD,IAAI,CAAC,IAAI,WAAW,gBAAgB,GAAG,OAAO,KAAA;CAC9C,OAAO,IAAI,MAAM,EAAuB,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG;AAC7E;AASA,IAAa,4BAAb,cAA+C,mBAAmB;CAChE;CACA;CAEA,YAAY,KAAc,UAA4C,CAAC,GAAG;EACxE,MAAM,GAAG;EACT,KAAK,aAAa,IAAI,oBAAoB,QAAQ,YAAY,KAAK,YAAY,GAAG,WAAW,CAAC;EAC9F,KAAK,iBAAiB,QAAQ,6BAAa,IAAI,IAAI;CACrD;CAEA,eAAuB,YAA+C;EACpE,MAAM,aAAa,KAAK,eAAe,IAAI,UAAU;EACrD,IAAI,sBAAsB,UAAU,GAAG,OAAO;EAC9C,MAAM,UAAU,iBAAiB,CAAC,CAAC,MAAK,aAAY,SAAS,OAAO,UAAU;EAC9E,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;EAClC,OAAO;GAAE,MAAM,QAAQ;GAAM,SAAS,QAAQ;GAAS,OAAO,QAAQ,KAAK;EAAM;CACnF;CAEA,MAAM,QAAQ,KAA6D;EACzE,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GAAW;GAC5B,MAAM,QAAQ,QAAQ,IAAI,OAAO,GAAG;GACpC,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,IAAI;IAAE;IAAO,QAAQ;GAAM,IAAI,KAAA;EAC9E;EACA,MAAM,SAAS,KAAK,eAAe,UAAU;EAC7C,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;EACjC,MAAM,QAAQ,MAAM,mBAAmB;GACrC;GACA,cAAc,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GAC9D,gBAAgB;GAChB,OAAO,KAAK;EACd,CAAC;EACD,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,IAAI;GAAE;GAAO,QAAQ;EAAW,IAAI,KAAA;CACnF;CAEA,MAAM,SAAS,KAA6C;EAC1D,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GAAW;GAC5B,MAAM,QAAQ,QAAQ,IAAI,OAAO,GAAG;GACpC,OAAO;IAAE,YAAY,UAAU,KAAA,KAAa,MAAM,SAAS;IAAG,GAAI,QAAQ,EAAE,QAAQ,MAAM,IAAI,CAAC;IAAI,UAAU;GAAM;EACrH;EACA,MAAM,SAAS,MAAM,sBAAsB,KAAK,YAAY,UAAU;EACtE,OAAO;GAAE,YAAY,WAAW,KAAA;GAAW,GAAI,WAAW,KAAA,IAAY,EAAE,QAAQ,WAAW,IAAI,CAAC;GAAI,UAAU;EAAM;CACtH;CAEA,MAAM,IAAI,KAAoB,QAA+B;EAC3D,MAAM,IAAI,MAAM,cAAc,OAAO,GAAG,EAAE,wFAAwF;CACpI;CAEA,MAAM,MAAM,KAAmC;EAC7C,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MAAM,cAAc,OAAO,GAAG,EAAE,gEAAgE;EAG5G,MAAM,KAAK,WAAW,OAAO,YAAY,KAAA,CAAS;CACpD;AACF;AAEA,MAAa,OAAO;AAEpB,SAAgB,MAAM,KAAc,SAA2C,CAAC,GAAS;CAEvF,IAAS,0BAA0B,KAAK,MAAM;AAChD"}
package/dist/host.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
- import { t as applyPiPackage } from "./runtime-D84Hv_3m.mjs";
3
- import { t as resolvePiPackage } from "./source-D7Ir-rPT.mjs";
2
+ import { t as applyPiPackage } from "./runtime-6DefpI6h.mjs";
3
+ import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
6
6
  import { basename, dirname, join, relative } from "node:path";
@@ -157,6 +157,8 @@ async function generateHostBundle(options) {
157
157
  marked: "^16.4.1",
158
158
  typebox: "^1.0.4",
159
159
  tinyglobby: "^0.2.15",
160
+ "cross-spawn": "^7.0.6",
161
+ diff: "^9.0.0",
160
162
  "@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.6"
161
163
  },
162
164
  peerDependencies: {
package/dist/host.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Instead of converting each package into a vendored bundle, the host bundle\n// declares Pi packages as ordinary npm dependencies; DSH's plugin manager\n// (pnpm) installs them, and at load time this module resolves each installed\n// package, discovers its Pi entry points, and mounts it through the same\n// package-agnostic runtime as converted bundles. One host, any package —\n// there is deliberately no per-package branching here.\n\nimport { readdir, readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n return dirname(require.resolve(`${packageName}/package.json`))\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n const errors: Array<{ name: string; error: string }> = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n await applyPiPackage(ctx, {\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const warn = log?.warn?.bind(log) ?? console.warn\n warn(`[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Host bundle generation\n// ---------------------------------------------------------------------------\n\nexport interface HostBundleOptions {\n outDir: string\n /** npm specs, e.g. \"@narumitw/pi-lsp@0.49.4\" or \"pi-ask-user\". */\n packages: string[]\n bundleName?: string\n}\n\nfunction splitSpec(spec: string): { name: string; range: string } {\n const at = spec.lastIndexOf('@')\n if (at > 0) return { name: spec.slice(0, at), range: spec.slice(at + 1) }\n return { name: spec, range: '*' }\n}\n\nasync function firstExisting(paths: string[]): Promise<string> {\n for (const path of paths) {\n try {\n await stat(path)\n return path\n } catch {\n // Try the next layout.\n }\n }\n throw new Error(`cannot locate pi2dsh runtime artifact; tried: ${paths.join(', ')}`)\n}\n\nasync function copyEmbeddedHostRuntime(outDir: string): Promise<void> {\n const moduleDir = dirname(fileURLToPath(import.meta.url))\n const hostSource = await firstExisting([\n join(moduleDir, 'host.mjs'),\n join(moduleDir, '../dist/host.mjs'),\n ])\n const distRoot = dirname(hostSource)\n const targetRoot = join(outDir, 'runtime')\n await mkdir(join(targetRoot, 'compat', 'vendor'), { recursive: true })\n for (const entry of await readdir(distRoot)) {\n if (entry.endsWith('.mjs') || entry.endsWith('.d.mts')) {\n await cp(join(distRoot, entry), join(targetRoot, entry))\n }\n }\n for (const sub of ['compat', join('compat', 'vendor')]) {\n const dir = join(distRoot, sub)\n try {\n for (const entry of await readdir(dir)) {\n if (entry.endsWith('.mjs')) await cp(join(dir, entry), join(targetRoot, sub, entry))\n }\n } catch {\n // dist layout without that subdirectory\n }\n }\n}\n\nexport async function generateHostBundle(options: HostBundleOptions): Promise<{ outDir: string; packageName: string }> {\n const specs = options.packages.map(splitSpec)\n if (specs.length === 0) throw new Error('host bundle requires at least one Pi package spec')\n const packageName = options.bundleName ?? 'dsh-pi-host'\n await mkdir(options.outDir, { recursive: true })\n await copyEmbeddedHostRuntime(options.outDir)\n\n const packageJson = {\n name: packageName,\n version: '0.1.0',\n description: `pi2dsh host bundle mounting ${specs.map(spec => spec.name).join(', ')} as native DSH plugins`,\n type: 'module',\n main: './index.js',\n files: ['index.js', 'cordis.patch.yml', 'runtime', 'PI2DSH-LICENSE', 'README.md'],\n dependencies: {\n ...Object.fromEntries(specs.map(spec => [spec.name, spec.range])),\n jiti: '^2.7.0',\n 'get-east-asian-width': '^1.6.0',\n marked: '^16.4.1',\n typebox: '^1.0.4',\n tinyglobby: '^0.2.15',\n '@deepseek-ai/dsh-skill-filesystem': '^0.1.0-rc.6',\n },\n peerDependencies: {\n '@deepseek-ai/dsh-llm': '^0.1.0-rc.6',\n '@deepseek-ai/dsh-system-prompt': '^0.1.0-rc.6',\n },\n keywords: ['dsh-plugin', 'deepseek-harness', 'pi-package', 'pi2dsh', 'pi-host'],\n license: 'MIT',\n dsh: { bundle: { patch: './cordis.patch.yml' } },\n }\n await writeFile(join(options.outDir, 'package.json'), `${JSON.stringify(packageJson, null, 2)}\\n`)\n\n const hostConfig: PiHostConfig = { packages: specs.map(spec => spec.name) }\n const indexSource = `import { applyPiHost } from './runtime/host.mjs'\\n\\n`\n + `export const name = ${JSON.stringify(packageName)}\\n`\n + `export const inject = ${JSON.stringify(['tools', 'systemPrompt', 'commands', 'skills'])}\\n\\n`\n + `const hostConfig = ${JSON.stringify(hostConfig, null, 2)}\\n\\n`\n + `export async function apply(ctx, config = {}) {\\n`\n + ` await applyPiHost(ctx, { ...hostConfig, ...config }, new URL(import.meta.url).pathname)\\n`\n + `}\\n`\n await writeFile(join(options.outDir, 'index.js'), indexSource)\n\n await writeFile(join(options.outDir, 'cordis.patch.yml'),\n `- insert:\\n - id: ${packageName}\\n name: ${JSON.stringify(packageName)}\\n`)\n\n const licenseSource = await firstExisting([\n join(dirname(fileURLToPath(import.meta.url)), 'compat/vendor/PI-LICENSE'),\n join(dirname(fileURLToPath(import.meta.url)), '../src/compat/vendor/PI-LICENSE'),\n ])\n await cp(licenseSource, join(options.outDir, 'PI2DSH-LICENSE'))\n\n await writeFile(join(options.outDir, 'README.md'),\n `# ${packageName}\\n\\n`\n + `A [pi2dsh](https://github.com/weijiafu14/pi2dsh) host bundle. The Pi packages below are installed as ordinary npm dependencies and mounted at load time — no per-package conversion, no vendored source snapshots.\\n\\n`\n + specs.map(spec => `- \\`${spec.name}@${spec.range}\\`\\n`).join('')\n + `\\nInstall:\\n\\n\\`\\`\\`sh\\ndsh plugin --profile headless add file:$PWD\\ndsh --profile headless --dump-config\\n\\`\\`\\`\\n`\n + `\\nThe packages execute their original Pi extension source inside the pi2dsh Host ABI; install only sources you trust. Run \\`pi2dsh inspect <package>\\` for each package's compatibility report.\\n`)\n\n return { outDir: options.outDir, packageName }\n}\n"],"mappings":";;;;;;;;AA+BA,SAAS,iBAAiB,MAAoE;CAC5F,MAAM,aAAa,KAAK,QAAQ,WAAW,IAAI;CAC/C,IAAI,CAAC,WAAW,WAAW,KAAK,GAAG,OAAO;EAAE,YAAY,CAAC;EAAG,MAAM;CAAW;CAC7E,MAAM,WAAW,WAAW,QAAQ,SAAS,CAAC;CAC9C,IAAI,aAAa,IAAI,OAAO;EAAE,YAAY,CAAC;EAAG,MAAM;CAAW;CAC/D,MAAM,aAAqC,CAAC;CAC5C,KAAK,MAAM,QAAQ,WAAW,MAAM,GAAG,QAAQ,CAAC,CAAC,MAAM,IAAI,GAAG;EAC5D,MAAM,YAAY,KAAK,QAAQ,GAAG;EAClC,IAAI,cAAc,IAAI;EACtB,MAAM,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;EAC1C,IAAI,QAAQ,KAAK,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;EAC3C,IAAK,MAAM,WAAW,IAAG,KAAK,MAAM,SAAS,IAAG,KAAO,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAChG,QAAQ,MAAM,MAAM,GAAG,EAAE;EAE3B,IAAI,IAAI,SAAS,GAAG,WAAW,OAAO;CACxC;CACA,OAAO;EAAE;EAAY,MAAM,WAAW,MAAM,WAAW,CAAC,CAAC,CAAC,KAAK;CAAE;AACnE;;AAGA,eAAsB,qBAAqB,KAA2D;CACpG,MAAM,cAAc,SAAyB,SAAS,IAAI,SAAS,IAAI,CAAC,CAAC,WAAW,MAAM,GAAG;CAE7F,MAAM,4BAAY,IAAI,IAAY;CAClC,KAAK,MAAM,QAAQ,IAAI,UAAU,QAE/B,UAAU,IAAI,WAAW,SAAS,IAAI,MAAM,aAAa,QAAQ,QAAQ,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC;CAGlG,MAAM,UAA+C,CAAC;CACtD,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,UAAU,IAAI,UAAU,SAAS;EAC1C,MAAM,OAAO,SAAS,QAAQ,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,QAAQ,kBAAkB,GAAG;EAChF,IAAI,YAAY,IAAI,IAAI,GAAG,MAAM,IAAI,MAAM,oCAAoC,IAAI,SAAS,KAAK,IAAI,MAAM;EAC3G,YAAY,IAAI,IAAI;EACpB,MAAM,EAAE,YAAY,SAAS,iBAAiB,MAAM,SAAS,QAAQ,MAAM,CAAC;EAC5E,MAAM,YAAY,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAI,SAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO;EAC5E,QAAQ,KAAK;GACX;GACA,aAAa,WAAW,eAAe,aAAa,0BAA0B;GAC9E,GAAI,WAAW,qBAAqB,KAAA,IAAY,EAAE,cAAc,WAAW,iBAAiB,IAAI,CAAC;GACjG,MAAM,WAAW,MAAM;EACzB,CAAC;CACH;CAEA,OAAO;EACL,eAAe;EACf,SAAS,IAAI;EACb,YAAY,IAAI,UAAU,WAAW,IAAI,UAAU;EACnD,WAAW,CAAC,GAAG,SAAS,CAAC,CAAC,KAAK;EAC/B;CACF;AACF;AAEA,SAAS,eAAe,QAA2C;CAEjE,QADiB,MAAM,QAAQ,QAAQ,QAAQ,IAAI,OAAO,WAAW,CAAC,EAAA,CACtD,KAAI,SAAS,OAAO,SAAS,WAAW,EAAE,MAAM,KAAK,IAAI,IAAK,CAAC,CAC5E,QAAO,SAAQ,OAAO,MAAM,SAAS,YAAY,KAAK,KAAK,SAAS,CAAC;AAC1E;AAEA,SAAS,oBAAoB,QAAgB,aAA6B;CACxE,MAAM,UAAU,cAAc,MAAM;CACpC,OAAO,QAAQ,QAAQ,QAAQ,GAAG,YAAY,cAAc,CAAC;AAC/D;;;;;;AAOA,eAAsB,YAAY,KAAc,QAAsB,QAAgC;CACpG,MAAM,aAAa,UAAU,cAAc,YAAY,GAAG;CAC1D,MAAM,SAAiD,CAAC;CACxD,KAAK,MAAM,QAAQ,eAAe,MAAM,GACtC,IAAI;EACF,MAAM,MAAM,oBAAoB,YAAY,KAAK,IAAI;EACrD,MAAM,MAAM,MAAM,iBAAiB,GAAG;EACtC,IAAI;GACF,MAAM,WAAW,MAAM,qBAAqB,GAAG;GAC/C,MAAM,eAAe,KAAK;IACxB,SAAS,cAAc,GAAG,IAAI,QAAQ,EAAE;IACxC;IACA,GAAI,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;GAC7D,CAAC;EACH,UAAU;GACR,MAAM,IAAI,QAAQ;EACpB;CACF,SAAS,OAAO;EACd,OAAO,KAAK;GAAE,MAAM,KAAK;GAAM,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAAE,CAAC;CAChG;CAEF,KAAK,MAAM,WAAW,QAAQ;EAC5B,MAAM,MAAO,IAAiE;EAE9E,CADa,KAAK,MAAM,KAAK,GAAG,KAAK,QAAQ,KAAA,CACxC,iCAAiC,QAAQ,KAAK,IAAI,QAAQ,OAAO;CACxE;CACA,IAAI,OAAO,SAAS,KAAK,OAAO,WAAW,eAAe,MAAM,CAAC,CAAC,QAChE,MAAM,IAAI,MAAM,mDAAmD,OAAO,EAAE,CAAE,KAAK,IAAI,OAAO,EAAE,CAAE,OAAO;AAE7G;AAaA,SAAS,UAAU,MAA+C;CAChE,MAAM,KAAK,KAAK,YAAY,GAAG;CAC/B,IAAI,KAAK,GAAG,OAAO;EAAE,MAAM,KAAK,MAAM,GAAG,EAAE;EAAG,OAAO,KAAK,MAAM,KAAK,CAAC;CAAE;CACxE,OAAO;EAAE,MAAM;EAAM,OAAO;CAAI;AAClC;AAEA,eAAe,cAAc,OAAkC;CAC7D,KAAK,MAAM,QAAQ,OACjB,IAAI;EACF,MAAM,KAAK,IAAI;EACf,OAAO;CACT,QAAQ,CAER;CAEF,MAAM,IAAI,MAAM,iDAAiD,MAAM,KAAK,IAAI,GAAG;AACrF;AAEA,eAAe,wBAAwB,QAA+B;CACpE,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;CACxD,MAAM,aAAa,MAAM,cAAc,CACrC,KAAK,WAAW,UAAU,GAC1B,KAAK,WAAW,kBAAkB,CACpC,CAAC;CACD,MAAM,WAAW,QAAQ,UAAU;CACnC,MAAM,aAAa,KAAK,QAAQ,SAAS;CACzC,MAAM,MAAM,KAAK,YAAY,UAAU,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACrE,KAAK,MAAM,SAAS,MAAM,QAAQ,QAAQ,GACxC,IAAI,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,QAAQ,GACnD,MAAM,GAAG,KAAK,UAAU,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC;CAG3D,KAAK,MAAM,OAAO,CAAC,UAAU,KAAK,UAAU,QAAQ,CAAC,GAAG;EACtD,MAAM,MAAM,KAAK,UAAU,GAAG;EAC9B,IAAI;GACF,KAAK,MAAM,SAAS,MAAM,QAAQ,GAAG,GACnC,IAAI,MAAM,SAAS,MAAM,GAAG,MAAM,GAAG,KAAK,KAAK,KAAK,GAAG,KAAK,YAAY,KAAK,KAAK,CAAC;EAEvF,QAAQ,CAER;CACF;AACF;AAEA,eAAsB,mBAAmB,SAA8E;CACrH,MAAM,QAAQ,QAAQ,SAAS,IAAI,SAAS;CAC5C,IAAI,MAAM,WAAW,GAAG,MAAM,IAAI,MAAM,mDAAmD;CAC3F,MAAM,cAAc,QAAQ,cAAc;CAC1C,MAAM,MAAM,QAAQ,QAAQ,EAAE,WAAW,KAAK,CAAC;CAC/C,MAAM,wBAAwB,QAAQ,MAAM;CAE5C,MAAM,cAAc;EAClB,MAAM;EACN,SAAS;EACT,aAAa,+BAA+B,MAAM,KAAI,SAAQ,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE;EACpF,MAAM;EACN,MAAM;EACN,OAAO;GAAC;GAAY;GAAoB;GAAW;GAAkB;EAAW;EAChF,cAAc;GACZ,GAAG,OAAO,YAAY,MAAM,KAAI,SAAQ,CAAC,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC;GAChE,MAAM;GACN,wBAAwB;GACxB,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,qCAAqC;EACvC;EACA,kBAAkB;GAChB,wBAAwB;GACxB,kCAAkC;EACpC;EACA,UAAU;GAAC;GAAc;GAAoB;GAAc;GAAU;EAAS;EAC9E,SAAS;EACT,KAAK,EAAE,QAAQ,EAAE,OAAO,qBAAqB,EAAE;CACjD;CACA,MAAM,UAAU,KAAK,QAAQ,QAAQ,cAAc,GAAG,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,EAAE,GAAG;CAEjG,MAAM,aAA2B,EAAE,UAAU,MAAM,KAAI,SAAQ,KAAK,IAAI,EAAE;CAC1E,MAAM,cAAc,2EACO,KAAK,UAAU,WAAW,EAAE,0BAC1B,KAAK,UAAU;EAAC;EAAS;EAAgB;EAAY;CAAQ,CAAC,EAAE,yBACnE,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE;CAI9D,MAAM,UAAU,KAAK,QAAQ,QAAQ,UAAU,GAAG,WAAW;CAE7D,MAAM,UAAU,KAAK,QAAQ,QAAQ,kBAAkB,GACrD,wBAAwB,YAAY,gBAAgB,KAAK,UAAU,WAAW,EAAE,GAAG;CAErF,MAAM,gBAAgB,MAAM,cAAc,CACxC,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,0BAA0B,GACxE,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,iCAAiC,CACjF,CAAC;CACD,MAAM,GAAG,eAAe,KAAK,QAAQ,QAAQ,gBAAgB,CAAC;CAE9D,MAAM,UAAU,KAAK,QAAQ,QAAQ,WAAW,GAC9C,KAAK,YAAY,8NAEf,MAAM,KAAI,SAAQ,OAAO,KAAK,KAAK,GAAG,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,IAC/D,8SACmM;CAEvM,OAAO;EAAE,QAAQ,QAAQ;EAAQ;CAAY;AAC/C"}
1
+ {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Instead of converting each package into a vendored bundle, the host bundle\n// declares Pi packages as ordinary npm dependencies; DSH's plugin manager\n// (pnpm) installs them, and at load time this module resolves each installed\n// package, discovers its Pi entry points, and mounts it through the same\n// package-agnostic runtime as converted bundles. One host, any package —\n// there is deliberately no per-package branching here.\n\nimport { readdir, readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n return dirname(require.resolve(`${packageName}/package.json`))\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n const errors: Array<{ name: string; error: string }> = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n await applyPiPackage(ctx, {\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const warn = log?.warn?.bind(log) ?? console.warn\n warn(`[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Host bundle generation\n// ---------------------------------------------------------------------------\n\nexport interface HostBundleOptions {\n outDir: string\n /** npm specs, e.g. \"@narumitw/pi-lsp@0.49.4\" or \"pi-ask-user\". */\n packages: string[]\n bundleName?: string\n}\n\nfunction splitSpec(spec: string): { name: string; range: string } {\n const at = spec.lastIndexOf('@')\n if (at > 0) return { name: spec.slice(0, at), range: spec.slice(at + 1) }\n return { name: spec, range: '*' }\n}\n\nasync function firstExisting(paths: string[]): Promise<string> {\n for (const path of paths) {\n try {\n await stat(path)\n return path\n } catch {\n // Try the next layout.\n }\n }\n throw new Error(`cannot locate pi2dsh runtime artifact; tried: ${paths.join(', ')}`)\n}\n\nasync function copyEmbeddedHostRuntime(outDir: string): Promise<void> {\n const moduleDir = dirname(fileURLToPath(import.meta.url))\n const hostSource = await firstExisting([\n join(moduleDir, 'host.mjs'),\n join(moduleDir, '../dist/host.mjs'),\n ])\n const distRoot = dirname(hostSource)\n const targetRoot = join(outDir, 'runtime')\n await mkdir(join(targetRoot, 'compat', 'vendor'), { recursive: true })\n for (const entry of await readdir(distRoot)) {\n if (entry.endsWith('.mjs') || entry.endsWith('.d.mts')) {\n await cp(join(distRoot, entry), join(targetRoot, entry))\n }\n }\n for (const sub of ['compat', join('compat', 'vendor')]) {\n const dir = join(distRoot, sub)\n try {\n for (const entry of await readdir(dir)) {\n if (entry.endsWith('.mjs')) await cp(join(dir, entry), join(targetRoot, sub, entry))\n }\n } catch {\n // dist layout without that subdirectory\n }\n }\n}\n\nexport async function generateHostBundle(options: HostBundleOptions): Promise<{ outDir: string; packageName: string }> {\n const specs = options.packages.map(splitSpec)\n if (specs.length === 0) throw new Error('host bundle requires at least one Pi package spec')\n const packageName = options.bundleName ?? 'dsh-pi-host'\n await mkdir(options.outDir, { recursive: true })\n await copyEmbeddedHostRuntime(options.outDir)\n\n const packageJson = {\n name: packageName,\n version: '0.1.0',\n description: `pi2dsh host bundle mounting ${specs.map(spec => spec.name).join(', ')} as native DSH plugins`,\n type: 'module',\n main: './index.js',\n files: ['index.js', 'cordis.patch.yml', 'runtime', 'PI2DSH-LICENSE', 'README.md'],\n dependencies: {\n ...Object.fromEntries(specs.map(spec => [spec.name, spec.range])),\n jiti: '^2.7.0',\n 'get-east-asian-width': '^1.6.0',\n marked: '^16.4.1',\n typebox: '^1.0.4',\n tinyglobby: '^0.2.15',\n // The vendored Pi built-in tool constructors spawn through cross-spawn\n // and diff exactly as Pi does — same runtime set the convert bundle\n // carries (generator.ts keeps the sibling list).\n 'cross-spawn': '^7.0.6',\n diff: '^9.0.0',\n '@deepseek-ai/dsh-skill-filesystem': '^0.1.0-rc.6',\n },\n peerDependencies: {\n '@deepseek-ai/dsh-llm': '^0.1.0-rc.6',\n '@deepseek-ai/dsh-system-prompt': '^0.1.0-rc.6',\n },\n keywords: ['dsh-plugin', 'deepseek-harness', 'pi-package', 'pi2dsh', 'pi-host'],\n license: 'MIT',\n dsh: { bundle: { patch: './cordis.patch.yml' } },\n }\n await writeFile(join(options.outDir, 'package.json'), `${JSON.stringify(packageJson, null, 2)}\\n`)\n\n const hostConfig: PiHostConfig = { packages: specs.map(spec => spec.name) }\n const indexSource = `import { applyPiHost } from './runtime/host.mjs'\\n\\n`\n + `export const name = ${JSON.stringify(packageName)}\\n`\n + `export const inject = ${JSON.stringify(['tools', 'systemPrompt', 'commands', 'skills'])}\\n\\n`\n + `const hostConfig = ${JSON.stringify(hostConfig, null, 2)}\\n\\n`\n + `export async function apply(ctx, config = {}) {\\n`\n + ` await applyPiHost(ctx, { ...hostConfig, ...config }, new URL(import.meta.url).pathname)\\n`\n + `}\\n`\n await writeFile(join(options.outDir, 'index.js'), indexSource)\n\n await writeFile(join(options.outDir, 'cordis.patch.yml'),\n `- insert:\\n - id: ${packageName}\\n name: ${JSON.stringify(packageName)}\\n`)\n\n const licenseSource = await firstExisting([\n join(dirname(fileURLToPath(import.meta.url)), 'compat/vendor/PI-LICENSE'),\n join(dirname(fileURLToPath(import.meta.url)), '../src/compat/vendor/PI-LICENSE'),\n ])\n await cp(licenseSource, join(options.outDir, 'PI2DSH-LICENSE'))\n\n await writeFile(join(options.outDir, 'README.md'),\n `# ${packageName}\\n\\n`\n + `A [pi2dsh](https://github.com/weijiafu14/pi2dsh) host bundle. The Pi packages below are installed as ordinary npm dependencies and mounted at load time — no per-package conversion, no vendored source snapshots.\\n\\n`\n + specs.map(spec => `- \\`${spec.name}@${spec.range}\\`\\n`).join('')\n + `\\nInstall:\\n\\n\\`\\`\\`sh\\ndsh plugin --profile headless add file:$PWD\\ndsh --profile headless --dump-config\\n\\`\\`\\`\\n`\n + `\\nThe packages execute their original Pi extension source inside the pi2dsh Host ABI; install only sources you trust. Run \\`pi2dsh inspect <package>\\` for each package's compatibility report.\\n`)\n\n return { outDir: options.outDir, packageName }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/analyzer.ts","../src/compatibility.ts","../src/generator.ts","../src/mcp-config.ts","../src/source.ts"],"mappings":";;;;iBAgbsB,eAAe,KAAK,oBAAoB,QAAQ;;;UC9a5D;EACR,OAAO;EACP;;cAKW;cAKA;cAKA;cASA,mBAAmB,SAAS,eAAe,SAAS,eAAe;cAoMnE,eAAe,SAAS,eAAe;cA2BvC,kBAAkB,SAAS,eAAe;cA+B1C,WAAW,SAAS,eAAe;cAgHnC,aAAa,SAAS,eAAe;iBA6FlC,WAAW,iBAAiB;iBAI5B,aAAa,gBAAgB;iBAO7B,kBAAkB,qBAAqB,uBAAuB;iBAS9D,uBAAuB,mBAAmB;iBAI1C,yBAAyB,mBAAmB;;;iBChMtC,eACpB,KAAK,mBACL,SAAS,kBACR;EAAU;EAAgB,QAAQ;EAAqB;;;;KCrTrD,gBAAgB;UAEJ;EACf;EACA;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,UAAU;EACV;;UAGe;EACf;EACA;;UAGe;EACf,SAAS;EACT,SAAS;EACT,UAAU;EACV;;iBA6Cc,oBAAoB,aAAa;EAA8B,SAAS,YAAY;EAAc;;iBAoElG,mBAAmB,aAAa,wBAA4B;;iBAsD5D,eAAe,QAAQ;;;iBClFjB,iBAAiB,gBAAgB,eAAsB,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/analyzer.ts","../src/compatibility.ts","../src/generator.ts","../src/mcp-config.ts","../src/source.ts"],"mappings":";;;;iBAgbsB,eAAe,KAAK,oBAAoB,QAAQ;;;UC9a5D;EACR,OAAO;EACP;;cAKW;cAKA;cAKA;cASA,mBAAmB,SAAS,eAAe,SAAS,eAAe;cAoMnE,eAAe,SAAS,eAAe;cA2BvC,kBAAkB,SAAS,eAAe;cA+B1C,WAAW,SAAS,eAAe;cAgHnC,aAAa,SAAS,eAAe;iBA6FlC,WAAW,iBAAiB;iBAI5B,aAAa,gBAAgB;iBAO7B,kBAAkB,qBAAqB,uBAAuB;iBAS9D,uBAAuB,mBAAmB;iBAI1C,yBAAyB,mBAAmB;;;iBC/KtC,eACpB,KAAK,mBACL,SAAS,kBACR;EAAU;EAAgB,QAAQ;EAAqB;;;;KCtUrD,gBAAgB;UAEJ;EACf;EACA;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,UAAU;EACV;;UAGe;EACf;EACA;;UAGe;EACf,SAAS;EACT,SAAS;EACT,UAAU;EACV;;iBA6Cc,oBAAoB,aAAa;EAA8B,SAAS,YAAY;EAAc;;iBAoElG,mBAAmB,aAAa,wBAA4B;;iBAsD5D,eAAe,QAAQ;;;iBC5EjB,iBAAiB,gBAAgB,eAAsB,QAAQ"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { _ as ruleForHostImport, a as analyzePackage, c as EVENT_RULES, d as PI_CODING_AGENT_PACKAGES, f as PI_TUI_PACKAGES, g as ruleForEvent, h as ruleForContextProperty, i as generateBundle, l as HOST_IMPORT_RULES, m as ruleForApi, n as convertPiMcpConfig, o as API_RULES, p as UI_CONTEXT_RULES, r as renderMcpPatch, s as CONTEXT_RULES, t as collectPiMcpServers, u as PI_AI_PACKAGES, v as ruleForUiContextProperty } from "./mcp-config-jL9w70It.mjs";
3
- import { t as resolvePiPackage } from "./source-D7Ir-rPT.mjs";
2
+ import { _ as ruleForHostImport, a as analyzePackage, c as EVENT_RULES, d as PI_CODING_AGENT_PACKAGES, f as PI_TUI_PACKAGES, g as ruleForEvent, h as ruleForContextProperty, i as generateBundle, l as HOST_IMPORT_RULES, m as ruleForApi, n as convertPiMcpConfig, o as API_RULES, p as UI_CONTEXT_RULES, r as renderMcpPatch, s as CONTEXT_RULES, t as collectPiMcpServers, u as PI_AI_PACKAGES, v as ruleForUiContextProperty } from "./mcp-config-gqg7GJEE.mjs";
3
+ import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { applyPiHost, generateHostBundle, manifestForInstalled } from "./host.mjs";
5
5
  export { API_RULES, CONTEXT_RULES, EVENT_RULES, HOST_IMPORT_RULES, PI_AI_PACKAGES, PI_CODING_AGENT_PACKAGES, PI_TUI_PACKAGES, UI_CONTEXT_RULES, analyzePackage, applyPiHost, collectPiMcpServers, convertPiMcpConfig, generateBundle, generateHostBundle, manifestForInstalled, renderMcpPatch, resolvePiPackage, ruleForApi, ruleForContextProperty, ruleForEvent, ruleForHostImport, ruleForUiContextProperty };
@@ -555,11 +555,16 @@ function localReferences(path, text) {
555
555
  const values = /* @__PURE__ */ new Map();
556
556
  const createRequireNames = /* @__PURE__ */ new Set(["createRequire"]);
557
557
  const requireNames = /* @__PURE__ */ new Set(["require"]);
558
- const add = (kind, specifier) => {
559
- if (specifier.startsWith(".") || specifier.startsWith("#")) values.set(`${kind}:${specifier}`, {
558
+ const add = (kind, specifier, lazy) => {
559
+ if (!specifier.startsWith(".") && !specifier.startsWith("#")) return;
560
+ const key = `${kind}:${specifier}`;
561
+ const existing = values.get(key);
562
+ if (existing === void 0) values.set(key, {
560
563
  kind,
561
- specifier
564
+ specifier,
565
+ lazy
562
566
  });
567
+ else if (existing.lazy && !lazy) existing.lazy = false;
563
568
  };
564
569
  function collectRequireAliases(node) {
565
570
  if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && (node.moduleSpecifier.text === "node:module" || node.moduleSpecifier.text === "module") && node.importClause?.namedBindings !== void 0 && ts.isNamedImports(node.importClause.namedBindings)) {
@@ -569,14 +574,14 @@ function localReferences(path, text) {
569
574
  }
570
575
  collectRequireAliases(source);
571
576
  function visit(node) {
572
- if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add("module", node.moduleSpecifier.text);
573
- else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add("module", node.moduleReference.expression.text);
577
+ if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add("module", node.moduleSpecifier.text, false);
578
+ else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add("module", node.moduleReference.expression.text, false);
574
579
  else if (ts.isCallExpression(node) && node.arguments.length > 0 && (node.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(node.expression) && requireNames.has(node.expression.text))) {
575
580
  const specifier = literalModule(node.arguments[0]);
576
- if (specifier !== void 0) add("module", specifier);
581
+ if (specifier !== void 0) add("module", specifier, insideFunctionBody(node));
577
582
  } else if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "URL" && isImportMetaUrl(node.arguments?.[1])) {
578
583
  const specifier = literalModule(node.arguments?.[0]);
579
- if (specifier !== void 0) add("asset", specifier);
584
+ if (specifier !== void 0) add("asset", specifier, false);
580
585
  }
581
586
  ts.forEachChild(node, visit);
582
587
  }
@@ -584,18 +589,41 @@ function localReferences(path, text) {
584
589
  return [...values.values()];
585
590
  }
586
591
  function externalPackage(specifier) {
587
- if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("#") || specifier.startsWith("node:") || builtinModules.includes(specifier)) return void 0;
592
+ if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("#") || specifier.startsWith("node:") || specifier.startsWith("bun:") || builtinModules.includes(specifier)) return void 0;
588
593
  const parts = specifier.split("/");
589
594
  return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
590
595
  }
596
+ function insideTry(node) {
597
+ let current = node.parent;
598
+ while (current !== void 0) {
599
+ if (ts.isTryStatement(current)) return true;
600
+ if (ts.isFunctionLike(current)) return false;
601
+ current = current.parent;
602
+ }
603
+ return false;
604
+ }
605
+ function insideFunctionBody(node) {
606
+ let current = node.parent;
607
+ while (current !== void 0) {
608
+ if (ts.isFunctionLike(current)) return true;
609
+ current = current.parent;
610
+ }
611
+ return false;
612
+ }
591
613
  function runtimeExternalPackages(path, text) {
592
614
  const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, sourceKind(path));
593
- const packages = /* @__PURE__ */ new Set();
615
+ const packages = /* @__PURE__ */ new Map();
594
616
  const createRequireNames = /* @__PURE__ */ new Set(["createRequire"]);
595
617
  const requireNames = /* @__PURE__ */ new Set(["require"]);
596
- const add = (specifier) => {
618
+ const add = (specifier, lazy) => {
597
619
  const name = externalPackage(specifier);
598
- if (name !== void 0 && name.length > 0) packages.add(name);
620
+ if (name === void 0 || name.length === 0) return;
621
+ const existing = packages.get(name);
622
+ if (existing === void 0) packages.set(name, {
623
+ name,
624
+ lazy
625
+ });
626
+ else if (existing.lazy && !lazy) existing.lazy = false;
599
627
  };
600
628
  function collectRequireAliases(node) {
601
629
  if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && (node.moduleSpecifier.text === "node:module" || node.moduleSpecifier.text === "module") && node.importClause?.namedBindings !== void 0 && ts.isNamedImports(node.importClause.namedBindings)) {
@@ -609,17 +637,19 @@ function runtimeExternalPackages(path, text) {
609
637
  const clause = node.importClause;
610
638
  const named = clause?.namedBindings;
611
639
  const namedImportsAreTypeOnly = named !== void 0 && ts.isNamedImports(named) && named.elements.length > 0 && named.elements.every((element) => element.isTypeOnly);
612
- if (clause === void 0 || !clause.isTypeOnly && (clause.name !== void 0 || !namedImportsAreTypeOnly)) add(node.moduleSpecifier.text);
613
- } else if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add(node.moduleSpecifier.text);
614
- else if (ts.isImportEqualsDeclaration(node) && !node.isTypeOnly && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add(node.moduleReference.expression.text);
640
+ if (clause === void 0 || !clause.isTypeOnly && (clause.name !== void 0 || !namedImportsAreTypeOnly)) add(node.moduleSpecifier.text, false);
641
+ } else if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) add(node.moduleSpecifier.text, false);
642
+ else if (ts.isImportEqualsDeclaration(node) && !node.isTypeOnly && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) add(node.moduleReference.expression.text, false);
615
643
  else if (ts.isCallExpression(node) && node.arguments.length > 0 && (node.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(node.expression) && requireNames.has(node.expression.text))) {
616
- const specifier = literalModule(node.arguments[0]);
617
- if (specifier !== void 0) add(specifier);
644
+ if (!insideTry(node)) {
645
+ const specifier = literalModule(node.arguments[0]);
646
+ if (specifier !== void 0) add(specifier, insideFunctionBody(node));
647
+ }
618
648
  }
619
649
  ts.forEachChild(node, visit);
620
650
  }
621
651
  visit(source);
622
- return [...packages];
652
+ return [...packages.values()];
623
653
  }
624
654
  function inside(rootDir, path) {
625
655
  const pathRelative = relative(rootDir, path);
@@ -714,34 +744,56 @@ async function expandAsset(path, rootDir) {
714
744
  return output;
715
745
  }
716
746
  async function collectLocalClosure(rootDir, entries) {
717
- const queue = entries.map((path) => resolve(path));
718
- const closure = /* @__PURE__ */ new Set();
719
- const issues = [];
720
747
  const importsMap = await packageImportsMap(rootDir);
748
+ const graph = /* @__PURE__ */ new Map();
749
+ const issues = [];
750
+ const queue = entries.map((path) => resolve(path));
721
751
  while (queue.length > 0) {
722
752
  const source = queue.shift();
723
- if (closure.has(source)) continue;
753
+ if (graph.has(source)) continue;
724
754
  if (!inside(rootDir, source)) throw new Error(`extension source escapes the Pi package: ${source}`);
725
755
  const info = await lstat(source);
726
756
  if (info.isSymbolicLink()) throw new Error(`refusing to copy symbolic link from Pi package: ${source}`);
727
757
  if (!info.isFile()) throw new Error(`extension closure contains a non-file path: ${source}`);
728
- closure.add(source);
758
+ const edges = [];
759
+ graph.set(source, edges);
729
760
  if (!SCRIPT_EXTENSIONS.has(extname(source))) continue;
730
761
  const text = await readFile(source, "utf8");
731
- for (const reference of localReferences(source, text)) try {
732
- if (reference.kind === "module") queue.push(await resolveModule(source, reference.specifier, rootDir, importsMap));
733
- else queue.push(...await expandAsset(resolve(dirname(source), reference.specifier), rootDir));
734
- } catch (error) {
735
- issues.push({
736
- file: source,
737
- kind: reference.kind,
738
- specifier: reference.specifier,
739
- detail: error instanceof Error ? error.message : String(error)
740
- });
762
+ for (const reference of localReferences(source, text)) {
763
+ const lazy = reference.lazy || reference.kind === "asset";
764
+ try {
765
+ const targets = reference.kind === "module" ? [await resolveModule(source, reference.specifier, rootDir, importsMap)] : await expandAsset(resolve(dirname(source), reference.specifier), rootDir);
766
+ edges.push({
767
+ lazy,
768
+ targets
769
+ });
770
+ queue.push(...targets);
771
+ } catch (error) {
772
+ issues.push({
773
+ file: source,
774
+ kind: reference.kind,
775
+ specifier: reference.specifier,
776
+ detail: error instanceof Error ? error.message : String(error),
777
+ lazy
778
+ });
779
+ }
741
780
  }
742
781
  }
782
+ const loadTimeFiles = /* @__PURE__ */ new Set();
783
+ const loadQueue = entries.map((path) => resolve(path)).filter((path) => graph.has(path));
784
+ while (loadQueue.length > 0) {
785
+ const source = loadQueue.shift();
786
+ if (loadTimeFiles.has(source)) continue;
787
+ loadTimeFiles.add(source);
788
+ for (const edge of graph.get(source) ?? []) {
789
+ if (edge.lazy) continue;
790
+ for (const target of edge.targets) if (!loadTimeFiles.has(target)) loadQueue.push(target);
791
+ }
792
+ }
793
+ for (const issue of issues) if (!loadTimeFiles.has(issue.file)) issue.lazy = true;
743
794
  return {
744
- files: [...closure].sort(),
795
+ files: [...graph.keys()].sort(),
796
+ loadTimeFiles,
745
797
  issues
746
798
  };
747
799
  }
@@ -1021,24 +1073,29 @@ async function analyzePackage(pkg) {
1021
1073
  line: 1,
1022
1074
  detail: "No ExtensionAPI use was statically proven across the local module closure; conversion fails closed instead of claiming compatibility."
1023
1075
  });
1024
- for (const issue of extensionClosure.issues) findings.push({
1025
- capability: `${issue.kind}(${issue.specifier})`,
1026
- level: "fatal",
1027
- file: relative(pkg.rootDir, issue.file).replaceAll("\\", "/"),
1028
- line: 1,
1029
- detail: `The local extension closure is incomplete: ${issue.detail}`
1030
- });
1076
+ for (const issue of extensionClosure.issues) {
1077
+ const lazyIssue = issue.lazy || issue.kind === "asset";
1078
+ findings.push({
1079
+ capability: `${issue.kind}(${issue.specifier})`,
1080
+ level: lazyIssue ? "partial" : "fatal",
1081
+ file: relative(pkg.rootDir, issue.file).replaceAll("\\", "/"),
1082
+ line: 1,
1083
+ detail: lazyIssue ? `Unresolved reference on a lazy path: ${issue.detail}. Extension load is unaffected; if the feature that evaluates it runs, it fails the same way under Pi (published file layout is preserved).` : `The local extension closure is incomplete: ${issue.detail}`
1084
+ });
1085
+ }
1031
1086
  const declaredDependencies = dependencyNames(pkg.packageJson);
1032
1087
  for (const file of extensionClosure.files.filter((candidate) => SCRIPT_EXTENSIONS.has(extname(candidate)))) {
1033
1088
  const text = await readFile(file, "utf8");
1034
- for (const packageName of runtimeExternalPackages(file, text)) {
1035
- if (PI_HOST_PACKAGES.has(packageName) || declaredDependencies.has(packageName)) continue;
1089
+ const lazyFile = !extensionClosure.loadTimeFiles.has(file);
1090
+ for (const use of runtimeExternalPackages(file, text)) {
1091
+ if (PI_HOST_PACKAGES.has(use.name) || declaredDependencies.has(use.name)) continue;
1092
+ const lazyUse = use.lazy || lazyFile;
1036
1093
  findings.push({
1037
- capability: `undeclared-runtime-dependency(${packageName})`,
1038
- level: "fatal",
1094
+ capability: lazyUse ? `optional-lazy-dependency(${use.name})` : `undeclared-runtime-dependency(${use.name})`,
1095
+ level: lazyUse ? "partial" : "fatal",
1039
1096
  file: relative(pkg.rootDir, file).replaceAll("\\", "/"),
1040
1097
  line: 1,
1041
- detail: `The extension imports ${JSON.stringify(packageName)} at runtime, but the Pi package does not declare it as a dependency.`
1098
+ detail: lazyUse ? `The extension imports ${JSON.stringify(use.name)} only on a lazily-evaluated path without declaring it; the feature that needs it asks for the module at call time, exactly as under Pi (install ${use.name} to use that feature).` : `The extension imports ${JSON.stringify(use.name)} at load time, but the Pi package does not declare it as a dependency.`
1042
1099
  });
1043
1100
  }
1044
1101
  }
@@ -1129,11 +1186,12 @@ async function copyExtensions(pkg, outDir) {
1129
1186
  const copied = [];
1130
1187
  const runtimePackages = /* @__PURE__ */ new Set();
1131
1188
  const closure = await collectLocalClosure(pkg.rootDir, pkg.resources.extensions);
1132
- if (closure.issues.length > 0) throw new Error(`cannot snapshot extension closure:\n${closure.issues.map((issue) => `- ${issue.file}: ${issue.detail}`).join("\n")}`);
1189
+ for (const issue of closure.issues) console.warn(`pi2dsh: unresolved ${issue.kind} reference ${JSON.stringify(issue.specifier)} from ${relative(pkg.rootDir, issue.file)} (${issue.lazy ? "lazy path — fails at feature use, as under Pi" : "load-time path"})`);
1133
1190
  for (const source of closure.files) {
1134
1191
  if (SCRIPT_EXTENSIONS.has(extname(source))) {
1135
1192
  const text = await readFile(source, "utf8");
1136
- for (const packageName of runtimeExternalPackages(source, text)) runtimePackages.add(packageName);
1193
+ const lazyFile = !closure.loadTimeFiles.has(source);
1194
+ for (const use of runtimeExternalPackages(source, text)) if (!use.lazy && !lazyFile || use.name in stringRecord$1(pkg.packageJson.dependencies)) runtimePackages.add(use.name);
1137
1195
  }
1138
1196
  const targetRelative = `vendor/${relative(pkg.rootDir, source).replaceAll("\\", "/")}`;
1139
1197
  const target = join(outDir, targetRelative);
@@ -1141,14 +1199,16 @@ async function copyExtensions(pkg, outDir) {
1141
1199
  await cp(source, target, { dereference: false });
1142
1200
  if (pkg.resources.extensions.map((entry) => resolve(entry)).includes(source)) copied.push(targetRelative);
1143
1201
  }
1202
+ const skillScriptPackages = /* @__PURE__ */ new Set();
1144
1203
  for (const source of pkg.resources.skills) {
1145
1204
  if (!SCRIPT_EXTENSIONS.has(extname(source))) continue;
1146
1205
  const text = await readFile(source, "utf8");
1147
- for (const packageName of runtimeExternalPackages(source, text)) runtimePackages.add(packageName);
1206
+ for (const use of runtimeExternalPackages(source, text)) skillScriptPackages.add(use.name);
1148
1207
  }
1149
1208
  return {
1150
1209
  entries: copied,
1151
- runtimePackages
1210
+ runtimePackages,
1211
+ skillScriptPackages
1152
1212
  };
1153
1213
  }
1154
1214
  async function copySkills(pkg, outDir) {
@@ -1230,7 +1290,9 @@ function generatedPackageJson(pkg, generatedName, runtimeSpec, runtimePackages,
1230
1290
  jiti: "^2.7.0",
1231
1291
  "get-east-asian-width": "^1.6.0",
1232
1292
  marked: "^16.4.1",
1233
- typebox: "^1.0.4"
1293
+ typebox: "^1.0.4",
1294
+ "cross-spawn": "^7.0.6",
1295
+ diff: "^9.0.0"
1234
1296
  } : {},
1235
1297
  ...hasSkills ? { "@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.6" } : {},
1236
1298
  ...runtimeSpec !== void 0 ? { pi2dsh: runtimeSpec } : {}
@@ -1342,6 +1404,8 @@ async function generateBundle(pkg, options) {
1342
1404
  };
1343
1405
  const runtimeSpec = options.runtimeSpec;
1344
1406
  if (runtimeSpec === void 0) await copyEmbeddedRuntime(outDir);
1407
+ const optionalSkillDeps = [...extensionSnapshot.skillScriptPackages].filter((name) => !SHIMMED_PI_HOST_PACKAGES.has(name) && !(name in stringRecord$1(pkg.packageJson.dependencies)));
1408
+ if (optionalSkillDeps.length > 0) console.warn(`pi2dsh: skill helper scripts reference ${optionalSkillDeps.join(", ")}; install them only if you use those skills`);
1345
1409
  await Promise.all([
1346
1410
  writeFile(join(outDir, "package.json"), `${JSON.stringify(generatedPackageJson(pkg, packageName, runtimeSpec, extensionSnapshot.runtimePackages, skillDirs.length > 0), null, 2)}\n`),
1347
1411
  writeFile(join(outDir, "pi2dsh.manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`),
@@ -1532,4 +1596,4 @@ function renderMcpPatch(result) {
1532
1596
  //#endregion
1533
1597
  export { ruleForHostImport as _, analyzePackage as a, EVENT_RULES as c, PI_CODING_AGENT_PACKAGES as d, PI_TUI_PACKAGES as f, ruleForEvent as g, ruleForContextProperty as h, generateBundle as i, HOST_IMPORT_RULES as l, ruleForApi as m, convertPiMcpConfig as n, API_RULES as o, UI_CONTEXT_RULES as p, renderMcpPatch as r, CONTEXT_RULES as s, collectPiMcpServers as t, PI_AI_PACKAGES as u, ruleForUiContextProperty as v };
1534
1598
 
1535
- //# sourceMappingURL=mcp-config-jL9w70It.mjs.map
1599
+ //# sourceMappingURL=mcp-config-gqg7GJEE.mjs.map