pi2dsh 0.2.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/LICENSE +21 -0
- package/README.md +122 -0
- package/README.zh.md +122 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.mjs +128 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/compat/pi-ai.d.mts +2597 -0
- package/dist/compat/pi-ai.d.mts.map +1 -0
- package/dist/compat/pi-ai.mjs +4669 -0
- package/dist/compat/pi-ai.mjs.map +1 -0
- package/dist/compat/pi-coding-agent.d.mts +745 -0
- package/dist/compat/pi-coding-agent.d.mts.map +1 -0
- package/dist/compat/pi-coding-agent.mjs +4 -0
- package/dist/compat/pi-tui.d.mts +3 -0
- package/dist/compat/pi-tui.mjs +3622 -0
- package/dist/compat/pi-tui.mjs.map +1 -0
- package/dist/host.d.mts +35 -0
- package/dist/host.d.mts.map +1 -0
- package/dist/host.mjs +197 -0
- package/dist/host.mjs.map +1 -0
- package/dist/index.d.mts +69 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/mcp-config-jL9w70It.mjs +1535 -0
- package/dist/mcp-config-jL9w70It.mjs.map +1 -0
- package/dist/pi-coding-agent-Dsg6_0ua.mjs +2060 -0
- package/dist/pi-coding-agent-Dsg6_0ua.mjs.map +1 -0
- package/dist/pi-config-shim-CZ1wFzqM.mjs +27 -0
- package/dist/pi-config-shim-CZ1wFzqM.mjs.map +1 -0
- package/dist/pi-tui-iHoF2tFc.d.mts +1043 -0
- package/dist/pi-tui-iHoF2tFc.d.mts.map +1 -0
- package/dist/pi-tui-utils-CcaVtm-3.mjs +895 -0
- package/dist/pi-tui-utils-CcaVtm-3.mjs.map +1 -0
- package/dist/pi-types-KazmR2O5.d.mts +62 -0
- package/dist/pi-types-KazmR2O5.d.mts.map +1 -0
- package/dist/pi-uuid-Db8ShZsK.mjs +47 -0
- package/dist/pi-uuid-Db8ShZsK.mjs.map +1 -0
- package/dist/rolldown-runtime-C2Q2p085.mjs +15 -0
- package/dist/runtime-D84Hv_3m.mjs +1499 -0
- package/dist/runtime-D84Hv_3m.mjs.map +1 -0
- package/dist/runtime.d.mts +31 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +3 -0
- package/dist/source-D7Ir-rPT.mjs +154 -0
- package/dist/source-D7Ir-rPT.mjs.map +1 -0
- package/dist/types-7IWJPPvS.d.mts +59 -0
- package/dist/types-7IWJPPvS.d.mts.map +1 -0
- package/package.json +135 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 weijiafu14 and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# pi2dsh
|
|
2
|
+
|
|
3
|
+
**English** | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
**Bridging the Pi and DeepSeek Harness ecosystems.** pi2dsh is dedicated to connecting [Pi](https://pi.dev/)'s extension ecosystem with [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): one general **Pi Host ABI compatibility layer** that runs unmodified Pi extensions as native DSH plugins — not per-package patches.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
# one bundle, any Pi packages, no conversion
|
|
9
|
+
pi2dsh host --packages '@juicesharp/rpiv-web-tools@2.4.0,pi-simplify@0.2.3' --out ./my-pi-host
|
|
10
|
+
dsh plugin --profile headless add file:$PWD/my-pi-host
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
The bridge implements Pi's public extension surface **once**, mapping every call onto DSH's native services. A package that sticks to Pi's public API runs verbatim; capabilities with no safe mapping fail explicitly instead of faking success.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Pi package (unmodified npm dependency)
|
|
19
|
+
│ loaded verbatim: default-export factory, package.json pi.extensions
|
|
20
|
+
▼
|
|
21
|
+
┌─────────────────── Pi Host ABI (pi2dsh) ───────────────────┐
|
|
22
|
+
│ registerTool / setActiveTools → DSH tools + per-agent restrict │
|
|
23
|
+
│ 33 Pi lifecycle events → DSH durable events & hook seams│
|
|
24
|
+
│ exec → DSH subprocess (local / E2B) │
|
|
25
|
+
│ sendMessage / sendUserMessage → DSH inject / steer / followup │
|
|
26
|
+
│ ui.select/confirm/input → DSH userQuestions (real waits) │
|
|
27
|
+
│ session entries/labels/name → durable sidecar + log projection│
|
|
28
|
+
│ images → DSH attachments (refs, not b64)│
|
|
29
|
+
│ pi-tui / pi-coding-agent / pi-ai imports → vendored/headless │
|
|
30
|
+
│ shims (width/keys/session math byte-identical to Pi, MIT) │
|
|
31
|
+
│ setModel / setThinkingLevel → agent/request seam overrides │
|
|
32
|
+
└────────────────────────────────────────────────────────────────┘
|
|
33
|
+
▼
|
|
34
|
+
DeepSeek Harness native services (Cordis composition)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Three delivery modes:
|
|
38
|
+
|
|
39
|
+
| Mode | What it does |
|
|
40
|
+
|---|---|
|
|
41
|
+
| **Host bundle** (recommended) | One installable DSH bundle mounts any list of unmodified Pi packages as ordinary npm dependencies |
|
|
42
|
+
| **Convert** | A reviewable per-package bundle: vendored source snapshot + machine-readable compatibility report, for supply-chain-sensitive installs |
|
|
43
|
+
| **MCP config translation** | Pi's six `mcpServers` layers → official `@deepseek-ai/dsh-mcp-client` patch entries. The Pi MCP adapter's code never runs; `$VAR` becomes `!!js process.env.VAR`, literal secrets are warned about |
|
|
44
|
+
|
|
45
|
+
Three hard rules keep it general:
|
|
46
|
+
|
|
47
|
+
1. The core contains **no `if (packageName === …)`** branching.
|
|
48
|
+
2. Every capability has a **public-API contract test** (`pnpm test`, 41 tests); "some plugin loads" is never the success criterion.
|
|
49
|
+
3. The top-50 corpus is verified **black-box only**: failures file public ABI gaps, and fixing one gap unlocks every package that hits it (e.g. one jiti subpath-alias fix unlocked 4 packages at once).
|
|
50
|
+
|
|
51
|
+
## Progress: Pi catalog top 50 by monthly downloads
|
|
52
|
+
|
|
53
|
+
Status as of 2026-08-14. Static analysis screens; the black-box run certifies. Full per-package machine-readable evidence in [community/](community/).
|
|
54
|
+
|
|
55
|
+
| Tier | Count | Meaning |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| ✅ **Tested working** | **32 / 50** | Mounted in a real DSH runtime AND real execution verified: 29 returned success, 3 ran their business logic end-to-end and rejected the synthetic probe arguments (4 of the 32 additionally passed deep verification: real LSP subprocess, web search/fetch, PNG generation, official `dsh plugin` add/activate/remove) |
|
|
58
|
+
| 🟡 **Mounts, not fully verified** | **7 / 50** | Loads and registers its tools/commands/skills in a real DSH runtime; full execution needs user credentials/services (3), is an event-hook package with no callable surface to probe (3), or hit a test-harness limitation (1: strict live-agent identity checks in userQuestions — the same path passes in the deep-verification layer) |
|
|
59
|
+
| ❌ **Not yet supported** | **11 / 50** | Attributed below — all on the roadmap |
|
|
60
|
+
| **Total mountable today** | **39 / 50** | |
|
|
61
|
+
|
|
62
|
+
Additional verified layers: a **host bundle** mounting two unmodified packages passed the official plugin-manager flow end-to-end; a **real model run** (`deepseek-v4-flash`) called a migrated Pi tool with the durable session log asserted and zero credential persistence ([evidence](community/live-deepseek-results.json)).
|
|
63
|
+
|
|
64
|
+
### The 11 not yet supported, attributed
|
|
65
|
+
|
|
66
|
+
*Pi-internal runtime users (4) — need bespoke adapters, next on the roadmap:*
|
|
67
|
+
`@tintinweb/pi-subagents` (calls `createAgentSession`/`createCodingTools` at load), `pi-landstrip` (calls `createBashToolDefinition`, Pi's built-in tool constructors), `pi-provider-litellm` (needs Pi's provider SDK factories; in DSH, model routing belongs to native llm adapters), `pi-fabric` (uses `wrapRegisteredTool` + references build-time-generated worker assets missing from its published tarball).
|
|
68
|
+
|
|
69
|
+
*Package defects visible under any host (5) — will be reported upstream:*
|
|
70
|
+
`pi-lens` (resource manifest escapes the package root), `pi-hermes-memory` + `@mjasnikovs/pi-task` (Bun-only `bun:sqlite`), `pi-harness-runtime` (imports playwright without declaring it), `mitsupi` (imports googleapis/ws without declaring them).
|
|
71
|
+
|
|
72
|
+
*Convert-snapshot limitation (2) — host mode already covers them:*
|
|
73
|
+
`pi-hashline-edit-pro`, `pi-interview` (read package files at runtime that static closure analysis cannot prove; the host bundle keeps the whole package directory).
|
|
74
|
+
|
|
75
|
+
### Roadmap: all 50
|
|
76
|
+
|
|
77
|
+
1. Lift the 7 "mounts, not fully verified" to tested-working (credentialed fixtures, per-package probe arguments).
|
|
78
|
+
2. Bridge Pi's internal `AgentSession`/tool-constructor surfaces onto DSH natives to unlock the 4 internal-runtime packages.
|
|
79
|
+
3. File upstream issues for the 5 package defects; adopt fixes as they land.
|
|
80
|
+
4. Route the 2 snapshot-limited packages through host mode by default.
|
|
81
|
+
|
|
82
|
+
## Quick start
|
|
83
|
+
|
|
84
|
+
Requires Node.js 22.19+ and DeepSeek Harness.
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
git clone https://github.com/weijiafu14/pi2dsh.git && cd pi2dsh
|
|
88
|
+
corepack pnpm@11.7.0 install && pnpm build
|
|
89
|
+
|
|
90
|
+
node dist/cli.mjs inspect @narumitw/pi-lsp # compatibility report
|
|
91
|
+
node dist/cli.mjs convert @narumitw/pi-lsp --out ./dsh-pi-lsp
|
|
92
|
+
node dist/cli.mjs host --packages 'pi-simplify' --out ./pi-host
|
|
93
|
+
node dist/cli.mjs mcp-config # Pi mcpServers → DSH patch
|
|
94
|
+
dsh plugin --profile headless add file:$PWD/pi-host
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Compatibility boundaries (explicit, never silent)
|
|
98
|
+
|
|
99
|
+
| Area | Mapping |
|
|
100
|
+
|---|---|
|
|
101
|
+
| Tools | Native DSH tools; Pi's in-place `tool_call` argument mutation works for Pi-owned tools (DSH-native tools reject it — DSH logs arguments before policy) |
|
|
102
|
+
| Sessions | Messages project from DSH's durable log; Pi custom entries/labels/names persist in a pi2dsh sidecar (DSH has no out-of-repo plugin-event channel yet) |
|
|
103
|
+
| Pi TUI | Pure logic vendored byte-identical; components construct headlessly; `ui.custom` resolves `undefined` exactly like Pi's own rpc mode |
|
|
104
|
+
| Providers/OAuth | Declarations recorded; transports and credentials stay native to DSH `llm`/`credentials` |
|
|
105
|
+
| Session tree writes | `fork`/`navigateTree`/`switchSession` fail explicitly (DSH lists pi-style entry trees as deferred) |
|
|
106
|
+
| Terminal decoration | footer/statusline/shortcuts register but never fire — matching Pi's own non-TUI modes |
|
|
107
|
+
|
|
108
|
+
Full machine-readable matrix: `pi2dsh matrix --json`. Capability-by-capability acceptance evidence: [docs/acceptance.md](docs/acceptance.md).
|
|
109
|
+
|
|
110
|
+
## Development and verification
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
pnpm verify # typecheck + 41 contract tests + packaging
|
|
114
|
+
pnpm audit:community # static screening, top 50
|
|
115
|
+
node scripts/blackbox-community.mjs community/blackbox-results.json --exercise
|
|
116
|
+
pnpm test:community # deep runtime + official manager + host e2e
|
|
117
|
+
DEEPSEEK_API_KEY=… pnpm test:live # real-model acceptance (key from env only)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT. Vendored Pi sources retain their upstream MIT license (`src/compat/vendor/PI-LICENSE`); generated bundles retain copied upstream license/notice files.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# pi2dsh
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **中文**
|
|
4
|
+
|
|
5
|
+
**打通 Pi 与 DeepSeek Harness 生态。** pi2dsh 致力于连接 [Pi](https://pi.dev/) 的扩展生态与 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH):用一层通用的 **Pi Host ABI 兼容层**,让未经修改的 Pi 扩展作为原生 DSH 插件运行——一次开发、批量兼容,不做逐包补丁。
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
# 一个 bundle 装任意 Pi 包,零转换
|
|
9
|
+
pi2dsh host --packages '@juicesharp/rpiv-web-tools@2.4.0,pi-simplify@0.2.3' --out ./my-pi-host
|
|
10
|
+
dsh plugin --profile headless add file:$PWD/my-pi-host
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 架构
|
|
14
|
+
|
|
15
|
+
桥只实现**一次** Pi 的公共扩展面,把每个调用映射到 DSH 原生服务。只用公共 API 的包原样运行;无法安全映射的能力显式失败,绝不伪装成功。
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Pi 包(未经修改的 npm 依赖)
|
|
19
|
+
│ 原样加载:default 导出工厂函数,package.json 的 pi.extensions
|
|
20
|
+
▼
|
|
21
|
+
┌────────────────── Pi Host ABI(pi2dsh)─────────────────────┐
|
|
22
|
+
│ registerTool / setActiveTools → DSH tools + 按 agent restrict │
|
|
23
|
+
│ 33 个 Pi 生命周期事件 → DSH durable 事件与 hook 缝 │
|
|
24
|
+
│ exec → DSH subprocess(local / E2B) │
|
|
25
|
+
│ sendMessage / sendUserMessage → DSH inject / steer / followup │
|
|
26
|
+
│ ui.select/confirm/input → DSH userQuestions(真实等待) │
|
|
27
|
+
│ 会话 entry / label / name → durable sidecar + 日志投影 │
|
|
28
|
+
│ 图片 → DSH attachments(引用非 base64)│
|
|
29
|
+
│ pi-tui / pi-coding-agent / pi-ai 导入 → vendored/headless shim │
|
|
30
|
+
│ (宽度/按键/会话数学与 Pi 字节一致,MIT 保留版权) │
|
|
31
|
+
│ setModel / setThinkingLevel → agent/request 缝按 agent 覆盖 │
|
|
32
|
+
└────────────────────────────────────────────────────────────────┘
|
|
33
|
+
▼
|
|
34
|
+
DeepSeek Harness 原生服务(Cordis 组合)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
三种使用方式:
|
|
38
|
+
|
|
39
|
+
| 方式 | 作用 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| **Host bundle**(推荐) | 单一可安装 DSH bundle,把任意 Pi 包列表作为普通 npm 依赖挂载 |
|
|
42
|
+
| **Convert** | 逐包可审查产物:vendored 源码快照 + 机器可读兼容报告,适合供应链敏感场景 |
|
|
43
|
+
| **MCP 配置转换** | Pi 的六层 `mcpServers` 配置 → 官方 `@deepseek-ai/dsh-mcp-client` patch 条目。不运行 pi-mcp-adapter 的代码;`$VAR` 转成 `!!js process.env.VAR`,字面量密钥会告警 |
|
|
44
|
+
|
|
45
|
+
保持通用性的三条硬规则:
|
|
46
|
+
|
|
47
|
+
1. 核心**没有任何 `if (packageName === …)`** 包名分支。
|
|
48
|
+
2. 每项能力有**公共 API 契约测试**(`pnpm test`,41 个);"某个插件能加载"从不作为成功标准。
|
|
49
|
+
3. 前 50 只做**黑盒验收**:失败产生公共 ABI 缺口工单,修一个缺口、同类包一起解锁(例:一次 jiti 子路径 alias 修复同时解锁 4 个包)。
|
|
50
|
+
|
|
51
|
+
## 进度:Pi 官方目录下载量前 50
|
|
52
|
+
|
|
53
|
+
以 2026-08-14 为准。静态分析只做筛查,黑盒真跑才算认证。逐包机器可读证据在 [community/](community/)。
|
|
54
|
+
|
|
55
|
+
| 档位 | 数量 | 含义 |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| ✅ **已测可用** | **32 / 50** | 在真实 DSH runtime 挂载且**真实执行**验证:29 个成功返回,3 个业务逻辑端到端真跑(拒绝了合成探针参数);其中 4 个另通过深度验证:真 LSP 子进程、真搜索/抓取、PNG 真落盘、官方 `dsh plugin` 安装/激活/卸载全流程 |
|
|
58
|
+
| 🟡 **能接入、未全测** | **7 / 50** | 在真实 DSH runtime 加载并注册出工具/命令/skill;完整执行还需用户凭证或外部服务(3 个)、纯事件钩子包没有可探测调用面(3 个)、测试装置限制(1 个:userQuestions 严格校验 live agent 身份——同一链路在深度验证层已通过) |
|
|
59
|
+
| ❌ **尚未接入** | **11 / 50** | 逐个归因见下——全部在路线图上 |
|
|
60
|
+
| **今天即可挂载** | **39 / 50** | |
|
|
61
|
+
|
|
62
|
+
另有两层验证:**host bundle** 合装两个原样 Pi 包走完官方插件管理器全流程;**真实模型**(deepseek-v4-flash)调用迁移后的 Pi 工具,durable 会话日志逐项断言、凭证零落盘([证据](community/live-deepseek-results.json))。
|
|
63
|
+
|
|
64
|
+
### 尚未接入的 11 个,逐个归因
|
|
65
|
+
|
|
66
|
+
**依赖 Pi 内部运行时(4 个)——需要专用适配,路线图下一步:**
|
|
67
|
+
`@tintinweb/pi-subagents`(加载即调 `createAgentSession`/`createCodingTools`)、`pi-landstrip`(调 `createBashToolDefinition` 等 Pi 内建工具构造器)、`pi-provider-litellm`(要 Pi 的 provider SDK 工厂;DSH 里模型路由属于原生 llm adapter 职权)、`pi-fabric`(用 `wrapRegisteredTool` 内部 API + 引用发布包里根本不存在的构建期生成文件)。
|
|
68
|
+
|
|
69
|
+
**包自身缺陷,换任何宿主都一样(5 个)——将向上游提 issue:**
|
|
70
|
+
`pi-lens`(资源清单越界逃出包根)、`pi-hermes-memory` 与 `@mjasnikovs/pi-task`(Bun 专属 `bun:sqlite`)、`pi-harness-runtime`(import 了未声明的 playwright)、`mitsupi`(import 了未声明的 googleapis/ws)。
|
|
71
|
+
|
|
72
|
+
**Convert 快照模式限制(2 个)——host 模式已可覆盖:**
|
|
73
|
+
`pi-hashline-edit-pro`、`pi-interview`(运行时读包内文件,静态闭包证不出来;host bundle 保留整包目录即可用)。
|
|
74
|
+
|
|
75
|
+
### 路线图:50 个全部接入
|
|
76
|
+
|
|
77
|
+
1. 把 7 个"能接入、未全测"提升为"已测可用"(带凭证的 fixture、按包定制探针参数)。
|
|
78
|
+
2. 做 Pi 内部 `AgentSession`/工具构造器面 → DSH 原生服务的桥,解锁 4 个内部运行时包。
|
|
79
|
+
3. 给 5 个包缺陷向上游提 issue,跟进修复。
|
|
80
|
+
4. 2 个快照受限包默认走 host 模式。
|
|
81
|
+
|
|
82
|
+
## 快速开始
|
|
83
|
+
|
|
84
|
+
需要 Node.js 22.19+ 与 DeepSeek Harness。
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
git clone https://github.com/weijiafu14/pi2dsh.git && cd pi2dsh
|
|
88
|
+
corepack pnpm@11.7.0 install && pnpm build
|
|
89
|
+
|
|
90
|
+
node dist/cli.mjs inspect @narumitw/pi-lsp # 兼容报告
|
|
91
|
+
node dist/cli.mjs convert @narumitw/pi-lsp --out ./dsh-pi-lsp
|
|
92
|
+
node dist/cli.mjs host --packages 'pi-simplify' --out ./pi-host
|
|
93
|
+
node dist/cli.mjs mcp-config # Pi mcpServers → DSH patch
|
|
94
|
+
dsh plugin --profile headless add file:$PWD/pi-host
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 兼容边界(显式声明,绝不静默)
|
|
98
|
+
|
|
99
|
+
| 领域 | 映射 |
|
|
100
|
+
|---|---|
|
|
101
|
+
| 工具 | 原生 DSH 工具;Pi 的 `tool_call` 原地改参对 Pi 自有工具生效(DSH 原生工具拒绝——DSH 有意先记日志后跑策略) |
|
|
102
|
+
| 会话 | 消息从 DSH durable 日志投影;Pi 自定义 entry/label/name 持久化在 pi2dsh sidecar(DSH 目前没有第三方插件事件通道) |
|
|
103
|
+
| Pi TUI | 纯逻辑 vendored 字节一致;组件类同签名 headless 构造;`ui.custom` 与 Pi 官方 rpc 模式一样返回 undefined |
|
|
104
|
+
| Provider/OAuth | 声明被记录;传输与密钥始终由 DSH `llm`/`credentials` 原生持有 |
|
|
105
|
+
| 会话树写操作 | `fork`/`navigateTree`/`switchSession` 显式失败(DSH 官方将 pi 式 entry tree 列为 deferred) |
|
|
106
|
+
| 终端装饰 | footer/statusline/快捷键注册成功但永不触发——与 Pi 自己的非 TUI 模式一致 |
|
|
107
|
+
|
|
108
|
+
完整机器可读矩阵:`pi2dsh matrix --json`。十项能力逐项验收证据:[docs/acceptance.md](docs/acceptance.md)。
|
|
109
|
+
|
|
110
|
+
## 开发与验证
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
pnpm verify # 类型检查 + 41 契约测试 + 打包检查
|
|
114
|
+
pnpm audit:community # 前 50 静态筛查
|
|
115
|
+
node scripts/blackbox-community.mjs community/blackbox-results.json --exercise
|
|
116
|
+
pnpm test:community # 深链路 + 官方插件管理器 + host e2e
|
|
117
|
+
DEEPSEEK_API_KEY=… pnpm test:live # 真实模型验收(key 仅从环境读)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT。Vendored 的 Pi 源码保留其上游 MIT 许可(`src/compat/vendor/PI-LICENSE`);生成的 bundle 保留上游 license/notice 文件。
|
package/dist/cli.d.mts
ADDED
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as analyzePackage, c as EVENT_RULES, i as generateBundle, l as HOST_IMPORT_RULES, n as convertPiMcpConfig, o as API_RULES, p as UI_CONTEXT_RULES, r as renderMcpPatch, s as CONTEXT_RULES } from "./mcp-config-jL9w70It.mjs";
|
|
3
|
+
import { t as resolvePiPackage } from "./source-D7Ir-rPT.mjs";
|
|
4
|
+
import { writeFileSync } from "node:fs";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
//#region src/cli.ts
|
|
7
|
+
function usage() {
|
|
8
|
+
return "pi2dsh — migrate Pi packages to DeepSeek Harness\n\nUsage:\n pi2dsh inspect <package-or-path> [--json]\n pi2dsh convert <package-or-path> --out <directory> [--runtime <spec>] [--strict] [--allow-unsupported]\n pi2dsh mcp-config [--cwd <dir>] [--out <file>] [--json]\n pi2dsh host --packages <spec,spec,...> --out <directory> [--name <bundle-name>]\n pi2dsh matrix [--json]\n\nhost generates ONE installable DSH bundle that mounts the listed Pi\npackages as ordinary npm dependencies — no per-package conversion.\n\nmcp-config reads Pi's standard mcpServers files (six-layer precedence)\nand emits DSH cordis.patch.yml entries for the official dsh-mcp-client —\nPi's MCP adapter code is never executed.\n";
|
|
9
|
+
}
|
|
10
|
+
function reportText(report) {
|
|
11
|
+
const lines = [
|
|
12
|
+
`${report.package.name}@${report.package.version}: ${report.verdict.toUpperCase()}`,
|
|
13
|
+
`full=${report.summary.full} partial=${report.summary.partial} unsupported=${report.summary.unsupported}`,
|
|
14
|
+
`extensions=${report.resources.extensions.length} skills=${report.resources.skills.length} prompts=${report.resources.prompts.length} themes=${report.resources.themes.length}`
|
|
15
|
+
];
|
|
16
|
+
for (const finding of report.findings) lines.push(`${finding.level.toUpperCase().padEnd(11)} ${finding.file}:${finding.line} ${finding.capability} — ${finding.detail}`);
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
function matrix(json) {
|
|
20
|
+
const value = {
|
|
21
|
+
api: API_RULES,
|
|
22
|
+
events: EVENT_RULES,
|
|
23
|
+
context: CONTEXT_RULES,
|
|
24
|
+
uiContext: UI_CONTEXT_RULES,
|
|
25
|
+
hostImports: HOST_IMPORT_RULES
|
|
26
|
+
};
|
|
27
|
+
if (json) console.log(JSON.stringify(value, null, 2));
|
|
28
|
+
else for (const [kind, rules] of Object.entries(value)) {
|
|
29
|
+
console.log(`${kind}:`);
|
|
30
|
+
for (const [name, candidate] of Object.entries(rules)) if ("level" in candidate && "detail" in candidate) console.log(` ${candidate.level.padEnd(11)} ${name} — ${candidate.detail}`);
|
|
31
|
+
else {
|
|
32
|
+
const hostRules = candidate;
|
|
33
|
+
for (const [exportName, hostRule] of Object.entries(hostRules)) console.log(` ${hostRule.level.padEnd(11)} ${name}:${exportName} — ${hostRule.detail}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function main() {
|
|
38
|
+
const parsed = parseArgs({
|
|
39
|
+
allowPositionals: true,
|
|
40
|
+
strict: true,
|
|
41
|
+
options: {
|
|
42
|
+
json: {
|
|
43
|
+
type: "boolean",
|
|
44
|
+
default: false
|
|
45
|
+
},
|
|
46
|
+
out: { type: "string" },
|
|
47
|
+
cwd: { type: "string" },
|
|
48
|
+
packages: { type: "string" },
|
|
49
|
+
name: { type: "string" },
|
|
50
|
+
runtime: { type: "string" },
|
|
51
|
+
strict: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default: false
|
|
54
|
+
},
|
|
55
|
+
"allow-unsupported": {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
default: false
|
|
58
|
+
},
|
|
59
|
+
help: {
|
|
60
|
+
type: "boolean",
|
|
61
|
+
short: "h",
|
|
62
|
+
default: false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const [command, source] = parsed.positionals;
|
|
67
|
+
if (parsed.values.help || command === void 0) {
|
|
68
|
+
console.log(usage());
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (command === "matrix") {
|
|
72
|
+
matrix(parsed.values.json);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (command === "host") {
|
|
76
|
+
if (parsed.values.out === void 0) throw new Error("host requires --out <directory>");
|
|
77
|
+
if (parsed.values.packages === void 0) throw new Error("host requires --packages <spec,spec,...>");
|
|
78
|
+
const { generateHostBundle } = await import("./host.mjs");
|
|
79
|
+
const result = await generateHostBundle({
|
|
80
|
+
outDir: parsed.values.out,
|
|
81
|
+
packages: parsed.values.packages.split(",").map((spec) => spec.trim()).filter(Boolean),
|
|
82
|
+
...parsed.values.name === void 0 ? {} : { bundleName: parsed.values.name }
|
|
83
|
+
});
|
|
84
|
+
console.log(`Generated host bundle ${result.packageName} in ${result.outDir}`);
|
|
85
|
+
console.log(`Install with: dsh plugin --profile headless add file:${result.outDir}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (command === "mcp-config") {
|
|
89
|
+
const result = convertPiMcpConfig(parsed.values.cwd ?? process.cwd());
|
|
90
|
+
for (const warning of result.warnings) console.error(`warning: ${warning.server}: ${warning.message}`);
|
|
91
|
+
const output = parsed.values.json ? `${JSON.stringify(result, null, 2)}\n` : renderMcpPatch(result);
|
|
92
|
+
if (parsed.values.out === void 0) process.stdout.write(output);
|
|
93
|
+
else {
|
|
94
|
+
writeFileSync(parsed.values.out, output);
|
|
95
|
+
console.error(`wrote ${result.entries.length} MCP server entr${result.entries.length === 1 ? "y" : "ies"} from ${result.sources.length} Pi config file(s) to ${parsed.values.out}`);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (command !== "inspect" && command !== "convert" || source === void 0) throw new Error(`invalid command\n\n${usage()}`);
|
|
100
|
+
const pkg = await resolvePiPackage(source);
|
|
101
|
+
try {
|
|
102
|
+
if (command === "inspect") {
|
|
103
|
+
const report = await analyzePackage(pkg);
|
|
104
|
+
console.log(parsed.values.json ? JSON.stringify(report, null, 2) : reportText(report));
|
|
105
|
+
if (report.verdict === "blocked") process.exitCode = 2;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (parsed.values.out === void 0) throw new Error("convert requires --out <directory>");
|
|
109
|
+
const result = await generateBundle(pkg, {
|
|
110
|
+
outDir: parsed.values.out,
|
|
111
|
+
...parsed.values.runtime !== void 0 ? { runtimeSpec: parsed.values.runtime } : {},
|
|
112
|
+
strict: parsed.values.strict,
|
|
113
|
+
allowUnsupported: parsed.values["allow-unsupported"]
|
|
114
|
+
});
|
|
115
|
+
console.log(`Generated ${result.packageName} in ${result.outDir}`);
|
|
116
|
+
console.log(reportText(result.report));
|
|
117
|
+
} finally {
|
|
118
|
+
await pkg.dispose();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
main().catch((error) => {
|
|
122
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
});
|
|
125
|
+
//#endregion
|
|
126
|
+
export {};
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { writeFileSync } from 'node:fs'\nimport { parseArgs } from 'node:util'\nimport { analyzePackage } from './analyzer.js'\nimport { API_RULES, CONTEXT_RULES, EVENT_RULES, HOST_IMPORT_RULES, UI_CONTEXT_RULES } from './compatibility.js'\nimport { generateBundle } from './generator.js'\nimport { convertPiMcpConfig, renderMcpPatch } from './mcp-config.js'\nimport { resolvePiPackage } from './source.js'\nimport type { CompatibilityReport } from './types.js'\n\nfunction usage(): string {\n return `pi2dsh — migrate Pi packages to DeepSeek Harness\\n\\n`\n + `Usage:\\n`\n + ` pi2dsh inspect <package-or-path> [--json]\\n`\n + ` pi2dsh convert <package-or-path> --out <directory> [--runtime <spec>] [--strict] [--allow-unsupported]\\n`\n + ` pi2dsh mcp-config [--cwd <dir>] [--out <file>] [--json]\\n`\n + ` pi2dsh host --packages <spec,spec,...> --out <directory> [--name <bundle-name>]\\n`\n + ` pi2dsh matrix [--json]\\n\\n`\n + `host generates ONE installable DSH bundle that mounts the listed Pi\\n`\n + `packages as ordinary npm dependencies — no per-package conversion.\\n\\n`\n + `mcp-config reads Pi's standard mcpServers files (six-layer precedence)\\n`\n + `and emits DSH cordis.patch.yml entries for the official dsh-mcp-client —\\n`\n + `Pi's MCP adapter code is never executed.\\n`\n}\n\nfunction reportText(report: CompatibilityReport): string {\n const lines = [\n `${report.package.name}@${report.package.version}: ${report.verdict.toUpperCase()}`,\n `full=${report.summary.full} partial=${report.summary.partial} unsupported=${report.summary.unsupported}`,\n `extensions=${report.resources.extensions.length} skills=${report.resources.skills.length} prompts=${report.resources.prompts.length} themes=${report.resources.themes.length}`,\n ]\n for (const finding of report.findings) {\n lines.push(`${finding.level.toUpperCase().padEnd(11)} ${finding.file}:${finding.line} ${finding.capability} — ${finding.detail}`)\n }\n return lines.join('\\n')\n}\n\nfunction matrix(json: boolean): void {\n const value = {\n api: API_RULES,\n events: EVENT_RULES,\n context: CONTEXT_RULES,\n uiContext: UI_CONTEXT_RULES,\n hostImports: HOST_IMPORT_RULES,\n }\n if (json) console.log(JSON.stringify(value, null, 2))\n else {\n for (const [kind, rules] of Object.entries(value)) {\n console.log(`${kind}:`)\n for (const [name, candidate] of Object.entries(rules)) {\n if ('level' in candidate && 'detail' in candidate) {\n console.log(` ${candidate.level.padEnd(11)} ${name} — ${candidate.detail}`)\n } else {\n const hostRules = candidate as Readonly<Record<string, { level: string; detail: string }>>\n for (const [exportName, hostRule] of Object.entries(hostRules)) {\n console.log(` ${hostRule.level.padEnd(11)} ${name}:${exportName} — ${hostRule.detail}`)\n }\n }\n }\n }\n }\n}\n\nasync function main(): Promise<void> {\n const parsed = parseArgs({\n allowPositionals: true,\n strict: true,\n options: {\n json: { type: 'boolean', default: false },\n out: { type: 'string' },\n cwd: { type: 'string' },\n packages: { type: 'string' },\n name: { type: 'string' },\n runtime: { type: 'string' },\n strict: { type: 'boolean', default: false },\n 'allow-unsupported': { type: 'boolean', default: false },\n help: { type: 'boolean', short: 'h', default: false },\n },\n })\n const [command, source] = parsed.positionals\n if (parsed.values.help || command === undefined) {\n console.log(usage())\n return\n }\n if (command === 'matrix') {\n matrix(parsed.values.json)\n return\n }\n if (command === 'host') {\n if (parsed.values.out === undefined) throw new Error('host requires --out <directory>')\n if (parsed.values.packages === undefined) throw new Error('host requires --packages <spec,spec,...>')\n const { generateHostBundle } = await import('./host.js')\n const result = await generateHostBundle({\n outDir: parsed.values.out,\n packages: parsed.values.packages.split(',').map(spec => spec.trim()).filter(Boolean),\n ...(parsed.values.name === undefined ? {} : { bundleName: parsed.values.name }),\n })\n console.log(`Generated host bundle ${result.packageName} in ${result.outDir}`)\n console.log(`Install with: dsh plugin --profile headless add file:${result.outDir}`)\n return\n }\n if (command === 'mcp-config') {\n const result = convertPiMcpConfig(parsed.values.cwd ?? process.cwd())\n for (const warning of result.warnings) {\n console.error(`warning: ${warning.server}: ${warning.message}`)\n }\n const output = parsed.values.json ? `${JSON.stringify(result, null, 2)}\\n` : renderMcpPatch(result)\n if (parsed.values.out === undefined) process.stdout.write(output)\n else {\n writeFileSync(parsed.values.out, output)\n console.error(`wrote ${result.entries.length} MCP server entr${result.entries.length === 1 ? 'y' : 'ies'} from ${result.sources.length} Pi config file(s) to ${parsed.values.out}`)\n }\n return\n }\n if ((command !== 'inspect' && command !== 'convert') || source === undefined) {\n throw new Error(`invalid command\\n\\n${usage()}`)\n }\n\n const pkg = await resolvePiPackage(source)\n try {\n if (command === 'inspect') {\n const report = await analyzePackage(pkg)\n console.log(parsed.values.json ? JSON.stringify(report, null, 2) : reportText(report))\n if (report.verdict === 'blocked') process.exitCode = 2\n return\n }\n if (parsed.values.out === undefined) throw new Error('convert requires --out <directory>')\n const result = await generateBundle(pkg, {\n outDir: parsed.values.out,\n ...(parsed.values.runtime !== undefined ? { runtimeSpec: parsed.values.runtime } : {}),\n strict: parsed.values.strict,\n allowUnsupported: parsed.values['allow-unsupported'],\n })\n console.log(`Generated ${result.packageName} in ${result.outDir}`)\n console.log(reportText(result.report))\n } finally {\n await pkg.dispose()\n }\n}\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error))\n process.exitCode = 1\n})\n"],"mappings":"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"}
|