monk-pi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # Monk × Pi Harness (`monk-pi`)
2
+
3
+ > **开箱即用 · 零配置 · 极速高性价比终端 Coding Agent**
4
+ > 将 [Monk](https://monk.party/) 的 OpenAI 兼容融合模型(月卡 ¥30 / 1M 上下文 / Agent 原生适配)与 [Pi](https://github.com/earendil-works/pi) 的极速灵活终端 Agent 架构深度结合。
5
+
6
+ ---
7
+
8
+ ## 💡 为什么需要 Monk-Pi?
9
+
10
+ - **极低门槛**:官方 Claude 3.7 / Opus / GPT-4o 价格高昂且易受网络限制。Monk 融合顶尖 Flash 模型,提供 100 万 Token 上下文与 Agent 原生工具调用能力。
11
+ - **零配置接入**:彻底告别繁琐的手写 `models.json` 和 `compat` 参数配置。首次启动自动完成密钥检验与环境注入。
12
+ - **专为编码调优**:预置代码主力模型(`monk-coding`)、极速推理模型(`monk-fast`)与融合旗舰(`monk`),并自动应用最佳 `max_tokens` 与角色兼容补丁。
13
+ - **完全兼容原生 Pi**:保留 Pi 原生的所有命令行参数、会话管理、TUI 快捷键和插件生态。
14
+
15
+ ---
16
+
17
+ ## 🚀 快速开始
18
+
19
+ ### 1. 一键运行 (推荐)
20
+
21
+ 无需全局安装,直接通过 `npx` 启动:
22
+
23
+ ```bash
24
+ npx monk-pi
25
+ ```
26
+
27
+ 或者全局安装命令:
28
+
29
+ ```bash
30
+ npm install -g monk-pi
31
+ monk-pi
32
+ ```
33
+
34
+ 首次运行会自动弹出向导,提示输入您的 Monk API Key(可在 [monk.party](https://monk.party) 订阅获取),随后即刻进入极速编码终端。
35
+
36
+ ---
37
+
38
+ ## 🛠️ 常用使用场景
39
+
40
+ ### 1. 日常交互式编程
41
+
42
+ ```bash
43
+ # 直接进入交互式编码 Agent (默认使用 monk-coding)
44
+ monk-pi
45
+
46
+ # 带着初始任务进入
47
+ monk-pi "重构当前目录下的鉴权模块,补充单元测试"
48
+
49
+ # 恢复上一轮历史会话
50
+ monk-pi --continue
51
+ ```
52
+
53
+ ### 2. 命令行单次执行 (Print Mode)
54
+
55
+ ```bash
56
+ # 检查依赖并直接输出结果后退出
57
+ monk-pi -p "解释当前 package.json 的依赖版本"
58
+
59
+ # 审查 Git 改动
60
+ monk-pi -p "检查 git diff,给出简洁的 commit message"
61
+ ```
62
+
63
+ ### 3. 指定临时模型
64
+
65
+ ```bash
66
+ # 使用极速模型进行轻量提问
67
+ monk-pi --model monk-fast "这行正则是什么意思?"
68
+
69
+ # 使用综合质量优先模型
70
+ monk-pi --model monk "帮我设计一套分布式系统的架构方案"
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 🧰 专属管理命令
76
+
77
+ | 命令 | 说明 |
78
+ | :--- | :--- |
79
+ | `monk-pi login` (或 `auth`) | 交互式配置或更新 Monk API Key,并自动同步至 Pi |
80
+ | `monk-pi status` (或 `check`) | 检测 API 网络延迟、认证有效性及推演通道 |
81
+ | `monk-pi model [name]` | 查看或切换默认主力模型 (`monk-coding` / `monk-fast` / `monk`) |
82
+ | `monk-pi doctor` | 一键诊断 Node、Pi 运行时、配置文件健康度与网络状况 |
83
+
84
+ ---
85
+
86
+ ## ⚙️ 自动注入的 Pi 最佳配置
87
+
88
+ `monk-pi` 会在 `~/.pi/agent/models.json` 中安全注入以下经深度调优的配置,不会覆盖您原有的其他自定义 Provider:
89
+
90
+ ```json
91
+ {
92
+ "providers": {
93
+ "monk": {
94
+ "name": "Monk",
95
+ "baseUrl": "https://monk.party/v1",
96
+ "api": "openai-completions",
97
+ "apiKey": "$MONK_API_KEY",
98
+ "compat": {
99
+ "supportsDeveloperRole": false,
100
+ "maxTokensField": "max_tokens",
101
+ "requiresToolResultName": true
102
+ },
103
+ "models": [
104
+ {
105
+ "id": "monk-coding",
106
+ "name": "Monk Coding (代码主力 - 推荐)",
107
+ "reasoning": true,
108
+ "contextWindow": 1000000,
109
+ "maxTokens": 64000
110
+ },
111
+ {
112
+ "id": "monk-fast",
113
+ "name": "Monk Fast (极速推理)",
114
+ "reasoning": true,
115
+ "contextWindow": 1000000,
116
+ "maxTokens": 64000
117
+ },
118
+ {
119
+ "id": "monk",
120
+ "name": "Monk (融合模型)",
121
+ "reasoning": true,
122
+ "contextWindow": 1000000,
123
+ "maxTokens": 64000
124
+ }
125
+ ]
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 🗺️ 后续迭代规划 (Roadmap)
134
+
135
+ - [x] **Phase 1: 轻量脚手架与启动器**
136
+ - [x] 交互式 Key 引导与实时有效性校验
137
+ - [x] Pi 运行时环境检测与自动注入
138
+ - [x] `status` / `doctor` / `model` 诊断指令
139
+ - [x] 完整参数透传与 TTY 会话继承
140
+ - [ ] **Phase 2: Monk 原生 Pi Extension**
141
+ - [ ] TUI 底部状态栏展示 Monk 订阅剩余天数与今日用量
142
+ - [ ] 针对 Monk 代理的上游报错拦截与自动 Context Compaction 重试机制
143
+ - [ ] `/monk` 专属 Slash 快捷指令
144
+ - [ ] **Phase 3: 生态共建**
145
+ - [ ] 提交收录至 monk.party 帮助中心客户端推荐列表
146
+
147
+ ---
148
+
149
+ ## 📄 开源许可
150
+
151
+ MIT License
@@ -0,0 +1,812 @@
1
+ // src/constants.ts
2
+ import os from "os";
3
+ import path from "path";
4
+ var MONK_BASE_URL = "https://monk.party/v1";
5
+ var MONK_WEBSITE = "https://monk.party";
6
+ var MONK_ACCOUNT_URL = "https://monk.party/account/";
7
+ var MONK_MODELS = [
8
+ {
9
+ id: "monk-coding",
10
+ name: "Monk Coding (\u4EE3\u7801\u4E3B\u529B - \u63A8\u8350)",
11
+ description: "\u4E13\u4E3A Agent \u6DF1\u5EA6\u4F18\u5316\uFF0C\u517C\u5907\u9AD8\u541E\u5410\u903B\u8F91\u63A8\u6F14\u4E0E\u5DE5\u5177\u8C03\u7528\u80FD\u529B",
12
+ reasoning: true,
13
+ contextWindow: 1e6,
14
+ maxTokens: 64e3
15
+ },
16
+ {
17
+ id: "monk-fast",
18
+ name: "Monk Fast (\u6781\u901F\u63A8\u7406)",
19
+ description: "\u6781\u901F\u54CD\u5E94\uFF0C\u9002\u5408\u65E5\u5E38\u95EE\u7B54\u3001\u5FEB\u901F\u4EE3\u7801\u63A2\u7D22\u4E0E\u5355\u6587\u4EF6\u9605\u8BFB",
20
+ reasoning: true,
21
+ contextWindow: 1e6,
22
+ maxTokens: 64e3
23
+ },
24
+ {
25
+ id: "monk",
26
+ name: "Monk (\u878D\u5408\u6A21\u578B)",
27
+ description: "\u96C6\u4E09\u5927 Flash \u4E4B\u6240\u957F\uFF0C\u7EFC\u5408\u8D28\u91CF\u4E0E\u601D\u8003\u80FD\u529B\u4F18\u5148",
28
+ reasoning: true,
29
+ contextWindow: 1e6,
30
+ maxTokens: 64e3
31
+ }
32
+ ];
33
+ var DEFAULT_MONK_MODEL = "monk-coding";
34
+ function getMonkHomeDir() {
35
+ return path.join(os.homedir(), ".monk-pi");
36
+ }
37
+ function getMonkConfigFile() {
38
+ return path.join(getMonkHomeDir(), "config.json");
39
+ }
40
+ function getPiHomeDir() {
41
+ return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent");
42
+ }
43
+ function getPiModelsFile() {
44
+ return path.join(getPiHomeDir(), "models.json");
45
+ }
46
+ function getPiSettingsFile() {
47
+ return path.join(getPiHomeDir(), "settings.json");
48
+ }
49
+
50
+ // src/config.ts
51
+ import fs from "fs";
52
+ function loadMonkConfig() {
53
+ const configFile = getMonkConfigFile();
54
+ try {
55
+ if (fs.existsSync(configFile)) {
56
+ const content = fs.readFileSync(configFile, "utf-8");
57
+ return JSON.parse(content);
58
+ }
59
+ } catch {
60
+ }
61
+ return {};
62
+ }
63
+ function saveMonkConfig(config) {
64
+ const homeDir = getMonkHomeDir();
65
+ if (!fs.existsSync(homeDir)) {
66
+ fs.mkdirSync(homeDir, { recursive: true });
67
+ }
68
+ const configFile = getMonkConfigFile();
69
+ const existing = loadMonkConfig();
70
+ const merged = { ...existing, ...config };
71
+ fs.writeFileSync(configFile, JSON.stringify(merged, null, 2), "utf-8");
72
+ }
73
+ function resolveApiKey() {
74
+ const envKey = process.env.MONK_API_KEY?.trim();
75
+ if (envKey) {
76
+ return { key: envKey, source: "env" };
77
+ }
78
+ const localConfig = loadMonkConfig();
79
+ if (localConfig.apiKey?.trim()) {
80
+ return { key: localConfig.apiKey.trim(), source: "local" };
81
+ }
82
+ const piModelsFile = getPiModelsFile();
83
+ try {
84
+ if (fs.existsSync(piModelsFile)) {
85
+ const data = JSON.parse(fs.readFileSync(piModelsFile, "utf-8"));
86
+ const key = data?.providers?.monk?.apiKey;
87
+ if (typeof key === "string" && key.trim() && !key.startsWith("$")) {
88
+ return { key: key.trim(), source: "pi" };
89
+ }
90
+ }
91
+ } catch {
92
+ }
93
+ return { key: "", source: "none" };
94
+ }
95
+ function syncPiModelsJson(apiKey) {
96
+ const piDir = getPiHomeDir();
97
+ const piModelsFile = getPiModelsFile();
98
+ try {
99
+ if (!fs.existsSync(piDir)) {
100
+ fs.mkdirSync(piDir, { recursive: true });
101
+ }
102
+ let modelsData = { providers: {} };
103
+ if (fs.existsSync(piModelsFile)) {
104
+ const raw = fs.readFileSync(piModelsFile, "utf-8");
105
+ try {
106
+ modelsData = JSON.parse(raw);
107
+ if (!modelsData.providers) {
108
+ modelsData.providers = {};
109
+ }
110
+ } catch {
111
+ modelsData = { providers: {} };
112
+ }
113
+ }
114
+ const monkProviderConfig = {
115
+ name: "Monk",
116
+ baseUrl: MONK_BASE_URL,
117
+ api: "openai-completions",
118
+ apiKey: apiKey && !apiKey.startsWith("$") ? apiKey : "$MONK_API_KEY",
119
+ compat: {
120
+ supportsDeveloperRole: false,
121
+ maxTokensField: "max_tokens",
122
+ requiresToolResultName: true
123
+ },
124
+ models: MONK_MODELS.map((m) => ({
125
+ id: m.id,
126
+ name: m.name,
127
+ reasoning: m.reasoning,
128
+ input: ["text"],
129
+ contextWindow: m.contextWindow,
130
+ maxTokens: m.maxTokens,
131
+ cost: {
132
+ input: 0,
133
+ output: 0,
134
+ cacheRead: 0,
135
+ cacheWrite: 0
136
+ }
137
+ }))
138
+ };
139
+ modelsData.providers.monk = monkProviderConfig;
140
+ fs.writeFileSync(piModelsFile, JSON.stringify(modelsData, null, 2), "utf-8");
141
+ return { success: true, path: piModelsFile };
142
+ } catch (err) {
143
+ return {
144
+ success: false,
145
+ path: piModelsFile,
146
+ error: err instanceof Error ? err.message : String(err)
147
+ };
148
+ }
149
+ }
150
+ function getDefaultModel() {
151
+ const config = loadMonkConfig();
152
+ if (config.defaultModel && MONK_MODELS.some((m) => m.id === config.defaultModel)) {
153
+ return config.defaultModel;
154
+ }
155
+ return DEFAULT_MONK_MODEL;
156
+ }
157
+
158
+ // src/monk-api.ts
159
+ async function validateApiKey(apiKey, baseUrl = MONK_BASE_URL) {
160
+ const trimmedKey = apiKey.trim();
161
+ if (!trimmedKey) {
162
+ return {
163
+ valid: false,
164
+ latencyMs: 0,
165
+ models: [],
166
+ error: "API Key \u4E0D\u80FD\u4E3A\u7A7A"
167
+ };
168
+ }
169
+ const endpoint = `${baseUrl.replace(/\/+$/, "")}/models`;
170
+ const startTime = Date.now();
171
+ try {
172
+ const controller = new AbortController();
173
+ const timeoutId = setTimeout(() => controller.abort(), 8e3);
174
+ const response = await fetch(endpoint, {
175
+ method: "GET",
176
+ headers: {
177
+ Authorization: `Bearer ${trimmedKey}`,
178
+ Accept: "application/json"
179
+ },
180
+ signal: controller.signal
181
+ });
182
+ clearTimeout(timeoutId);
183
+ const latencyMs = Date.now() - startTime;
184
+ if (response.status === 401 || response.status === 403) {
185
+ const body = await response.json().catch(() => ({}));
186
+ return {
187
+ valid: false,
188
+ latencyMs,
189
+ models: [],
190
+ statusCode: response.status,
191
+ error: body.error?.message || "API Key \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u68C0\u67E5 monk.party \u7684\u8BA2\u9605\u51ED\u8BC1"
192
+ };
193
+ }
194
+ if (!response.ok) {
195
+ return {
196
+ valid: false,
197
+ latencyMs,
198
+ models: [],
199
+ statusCode: response.status,
200
+ error: `Monk API \u54CD\u5E94\u5F02\u5E38 (HTTP ${response.status})`
201
+ };
202
+ }
203
+ const data = await response.json().catch(() => ({}));
204
+ const models = Array.isArray(data.data) ? data.data.map((m) => m.id) : [];
205
+ return {
206
+ valid: true,
207
+ latencyMs,
208
+ models,
209
+ statusCode: response.status
210
+ };
211
+ } catch (err) {
212
+ const latencyMs = Date.now() - startTime;
213
+ const isAbort = err instanceof Error && err.name === "AbortError";
214
+ return {
215
+ valid: false,
216
+ latencyMs,
217
+ models: [],
218
+ error: isAbort ? "\u8FDE\u63A5 Monk \u670D\u52A1\u8D85\u65F6\uFF08\u8D85\u8FC7 8 \u79D2\uFF09\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5" : `\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
219
+ };
220
+ }
221
+ }
222
+ async function testChatCompletion(apiKey, model = "monk-fast", baseUrl = MONK_BASE_URL) {
223
+ const endpoint = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
224
+ const startTime = Date.now();
225
+ try {
226
+ const controller = new AbortController();
227
+ const timeoutId = setTimeout(() => controller.abort(), 12e3);
228
+ const response = await fetch(endpoint, {
229
+ method: "POST",
230
+ headers: {
231
+ "Content-Type": "application/json",
232
+ Authorization: `Bearer ${apiKey.trim()}`
233
+ },
234
+ body: JSON.stringify({
235
+ model,
236
+ messages: [{ role: "user", content: "pong" }],
237
+ max_tokens: 10,
238
+ temperature: 0.1
239
+ }),
240
+ signal: controller.signal
241
+ });
242
+ clearTimeout(timeoutId);
243
+ const latencyMs = Date.now() - startTime;
244
+ if (!response.ok) {
245
+ const errJson = await response.json().catch(() => ({}));
246
+ return {
247
+ success: false,
248
+ latencyMs,
249
+ error: errJson.error?.message || `HTTP ${response.status}`
250
+ };
251
+ }
252
+ const json = await response.json().catch(() => ({}));
253
+ const reply = json.choices?.[0]?.message?.content?.trim() || "";
254
+ return {
255
+ success: true,
256
+ latencyMs,
257
+ reply
258
+ };
259
+ } catch (err) {
260
+ const latencyMs = Date.now() - startTime;
261
+ return {
262
+ success: false,
263
+ latencyMs,
264
+ error: err instanceof Error ? err.message : String(err)
265
+ };
266
+ }
267
+ }
268
+
269
+ // src/ui.ts
270
+ import pc from "picocolors";
271
+ import { password, select, confirm } from "@inquirer/prompts";
272
+ function printBanner() {
273
+ const line = pc.dim("\u2500".repeat(54));
274
+ console.log();
275
+ console.log(pc.bold(pc.yellow(" \u2588\u2580\u2584\u2580\u2588 \u2588\u2580\u2588 \u2588\u2584\u2591\u2588 \u2588\u2584\u2580") + " \u2715 " + pc.cyan("\u2588\u2580\u2588 \u2588")));
276
+ console.log(pc.bold(pc.yellow(" \u2588\u2591\u2580\u2591\u2588 \u2588\u2584\u2588 \u2588\u2591\u2580\u2588 \u2588\u2591\u2588") + " " + pc.cyan("\u2588\u2580\u2580 \u2588")));
277
+ console.log(line);
278
+ console.log(
279
+ ` ${pc.bold("Monk \xD7 Pi Harness")} ${pc.dim("\xB7")} ${pc.green("OpenAI-Compatible Fusion Agent")}`
280
+ );
281
+ console.log(
282
+ ` ${pc.dim("\u6A21\u578B:")} ${pc.yellow("monk-coding")} ${pc.dim("|")} ${pc.cyan("monk-fast")} ${pc.dim("|")} ${pc.magenta("monk")}`
283
+ );
284
+ console.log(line);
285
+ console.log();
286
+ }
287
+ function logSuccess(msg) {
288
+ console.log(` ${pc.green("\u2714")} ${msg}`);
289
+ }
290
+ function logInfo(msg) {
291
+ console.log(` ${pc.cyan("\u2139")} ${msg}`);
292
+ }
293
+ function logWarn(msg) {
294
+ console.log(` ${pc.yellow("\u25B2")} ${msg}`);
295
+ }
296
+ function logError(msg) {
297
+ console.log(` ${pc.red("\u2716")} ${msg}`);
298
+ }
299
+ function logStep(step, total, msg) {
300
+ console.log(` ${pc.dim(`[${step}/${total}]`)} ${pc.bold(msg)}`);
301
+ }
302
+ async function promptForApiKey(currentKey) {
303
+ console.log();
304
+ console.log(` ${pc.bold("\u6B22\u8FCE\u4F7F\u7528 Monk \xD7 Pi \u7F16\u7801\u4F34\u4FA3\uFF01")}`);
305
+ console.log(` ${pc.dim("Monk \u4E13\u4E3A Agent \u63D0\u4F9B\u9AD8\u5E76\u53D1\u6781\u901F\u878D\u5408\u6A21\u578B\u3002\u8BF7\u914D\u7F6E\u60A8\u7684 API Key\u3002")}`);
306
+ console.log(` ${pc.dim("\u8FD8\u6CA1\u6709 Key\uFF1F\u53EF\u4EE5\u5728")} ${pc.underline(pc.yellow(MONK_WEBSITE))} ${pc.dim("\u8BA2\u9605\u6708\u5361 (\xA530/\u6708)")}`);
307
+ console.log();
308
+ const key = await password({
309
+ message: currentKey ? `\u8BF7\u8F93\u5165 Monk API Key (\u76F4\u63A5\u56DE\u8F66\u4FDD\u6301\u5F53\u524D: ${maskKey(currentKey)}):` : "\u8BF7\u8F93\u5165 Monk API Key (\u683C\u5F0F sk-monk-...):",
310
+ mask: "*",
311
+ validate: (val) => {
312
+ const trimmed = val.trim();
313
+ if (!trimmed && currentKey) return true;
314
+ if (!trimmed) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
315
+ if (!trimmed.startsWith("sk-")) {
316
+ return "Key \u683C\u5F0F\u901A\u5E38\u4EE5 sk- \u5F00\u5934\uFF0C\u8BF7\u786E\u8BA4\u590D\u5236\u5B8C\u6574";
317
+ }
318
+ return true;
319
+ }
320
+ });
321
+ const finalKey = key.trim() || currentKey || "";
322
+ return finalKey;
323
+ }
324
+ async function promptSelectModel(currentModel) {
325
+ const choices = MONK_MODELS.map((m) => ({
326
+ name: `${m.name.padEnd(28)} ${pc.dim(m.description)}`,
327
+ value: m.id,
328
+ description: m.description
329
+ }));
330
+ const selected = await select({
331
+ message: "\u8BF7\u9009\u62E9\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B:",
332
+ choices,
333
+ default: currentModel || "monk-coding"
334
+ });
335
+ return selected;
336
+ }
337
+ async function promptInstallPi() {
338
+ logWarn("\u672A\u68C0\u6D4B\u5230\u5168\u5C40\u5B89\u88C5\u7684 pi \u7F16\u7801\u52A9\u624B (@earendil-works/pi-coding-agent)\u3002");
339
+ console.log(` ${pc.dim("Monk Harness \u4F9D\u8D56 Pi \u4F5C\u4E3A\u5E95\u5C42 TUI/Agent \u8FD0\u884C\u65F6\u3002")}`);
340
+ console.log();
341
+ return await confirm({
342
+ message: "\u662F\u5426\u7ACB\u5373\u4F7F\u7528 npm \u5168\u5C40\u5B89\u88C5 @earendil-works/pi-coding-agent\uFF1F",
343
+ default: true
344
+ });
345
+ }
346
+ function maskKey(key) {
347
+ if (!key || key.length < 10) return "******";
348
+ return `${key.slice(0, 7)}...${key.slice(-4)}`;
349
+ }
350
+
351
+ // src/pi-runner.ts
352
+ import fs2 from "fs";
353
+ import path2 from "path";
354
+ import { fileURLToPath } from "url";
355
+ import spawn from "cross-spawn";
356
+ import pc2 from "picocolors";
357
+ function resolvePiBinary() {
358
+ try {
359
+ const globalCheck = spawn.sync("pi", ["--version"], {
360
+ encoding: "utf-8",
361
+ stdio: ["ignore", "pipe", "ignore"]
362
+ });
363
+ if (globalCheck.status === 0 && globalCheck.stdout) {
364
+ return {
365
+ type: "global",
366
+ command: "pi",
367
+ argsPrefix: [],
368
+ version: globalCheck.stdout.trim(),
369
+ sourceDescription: "\u7CFB\u7EDF\u5168\u5C40\u547D\u4EE4 (PATH)"
370
+ };
371
+ }
372
+ } catch {
373
+ }
374
+ const candidateBinDirs = [
375
+ path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "../node_modules/.bin/pi"),
376
+ path2.resolve(process.cwd(), "node_modules/.bin/pi")
377
+ ];
378
+ for (const binPath of candidateBinDirs) {
379
+ if (fs2.existsSync(binPath)) {
380
+ try {
381
+ const localCheck = spawn.sync(binPath, ["--version"], {
382
+ encoding: "utf-8",
383
+ stdio: ["ignore", "pipe", "ignore"]
384
+ });
385
+ if (localCheck.status === 0 && localCheck.stdout) {
386
+ return {
387
+ type: "local",
388
+ command: binPath,
389
+ argsPrefix: [],
390
+ version: localCheck.stdout.trim(),
391
+ sourceDescription: `\u672C\u5730\u4F9D\u8D56 (${path2.basename(path2.dirname(binPath))})`
392
+ };
393
+ }
394
+ } catch {
395
+ }
396
+ }
397
+ }
398
+ try {
399
+ const mainEntry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
400
+ const cliEntry = path2.join(path2.dirname(mainEntry), "bundle", "cli.js");
401
+ if (fs2.existsSync(cliEntry)) {
402
+ const moduleCheck = spawn.sync(process.execPath, [cliEntry, "--version"], {
403
+ encoding: "utf-8",
404
+ stdio: ["ignore", "pipe", "ignore"]
405
+ });
406
+ if (moduleCheck.status === 0 && moduleCheck.stdout) {
407
+ return {
408
+ type: "bundled",
409
+ command: process.execPath,
410
+ argsPrefix: [cliEntry],
411
+ version: moduleCheck.stdout.trim(),
412
+ sourceDescription: "Monk-Pi \u5185\u5D4C\u6838\u5FC3 (\u514D\u5B89\u88C5\u8FD0\u884C)"
413
+ };
414
+ }
415
+ }
416
+ } catch {
417
+ }
418
+ return {
419
+ type: "missing",
420
+ command: "",
421
+ argsPrefix: [],
422
+ sourceDescription: "\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684 Pi \u6838\u5FC3"
423
+ };
424
+ }
425
+ function detectPi() {
426
+ const resolved = resolvePiBinary();
427
+ return {
428
+ installed: resolved.type !== "missing",
429
+ version: resolved.version,
430
+ type: resolved.type,
431
+ description: resolved.sourceDescription
432
+ };
433
+ }
434
+ function detectPackageManager() {
435
+ try {
436
+ const bunCheck = spawn.sync("bun", ["--version"], { stdio: "ignore" });
437
+ if (bunCheck.status === 0) return "bun";
438
+ } catch {
439
+ }
440
+ try {
441
+ const pnpmCheck = spawn.sync("pnpm", ["--version"], { stdio: "ignore" });
442
+ if (pnpmCheck.status === 0) return "pnpm";
443
+ } catch {
444
+ }
445
+ return "npm";
446
+ }
447
+ async function installPi() {
448
+ const pm = detectPackageManager();
449
+ let cmd = "npm";
450
+ let args = ["install", "-g", "@earendil-works/pi-coding-agent"];
451
+ if (pm === "bun") {
452
+ cmd = "bun";
453
+ args = ["add", "-g", "@earendil-works/pi-coding-agent"];
454
+ } else if (pm === "pnpm") {
455
+ cmd = "pnpm";
456
+ args = ["add", "-g", "@earendil-works/pi-coding-agent"];
457
+ }
458
+ logInfo(`\u68C0\u6D4B\u5230\u5305\u7BA1\u7406\u5668: ${pc2.bold(pm)}\uFF0C\u6B63\u5728\u6267\u884C: ${pc2.yellow(`${cmd} ${args.join(" ")}`)} ...`);
459
+ try {
460
+ const child = spawn.sync(cmd, args, {
461
+ stdio: "inherit"
462
+ });
463
+ if (child.status === 0) {
464
+ logSuccess("Pi \u6838\u5FC3\u5957\u4EF6\u5B89\u88C5\u6210\u529F\uFF01");
465
+ return true;
466
+ }
467
+ logError(`\u5B89\u88C5\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${child.status}`);
468
+ logWarn(`\u82E5\u56E0\u6743\u9650\u95EE\u9898 (EACCES) \u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5:`);
469
+ console.log(` ${pc2.cyan(`sudo ${cmd} ${args.join(" ")}`)}`);
470
+ console.log(` \u6216\u76F4\u63A5\u4F7F\u7528\u514D\u5B89\u88C5\u65B9\u6848: ${pc2.cyan("npx monk-pi")}`);
471
+ return false;
472
+ } catch (err) {
473
+ logError(`\u6267\u884C\u5B89\u88C5\u5F02\u5E38: ${err instanceof Error ? err.message : String(err)}`);
474
+ return false;
475
+ }
476
+ }
477
+ function launchPi(options) {
478
+ const resolved = resolvePiBinary();
479
+ if (resolved.type === "missing") {
480
+ logError("\u672A\u627E\u5230\u53EF\u7528\u7684 Pi \u6838\u5FC3\u8FD0\u884C\u73AF\u5883\u3002");
481
+ return 1;
482
+ }
483
+ const { apiKey, defaultModel, passthroughArgs } = options;
484
+ const extraArgs = [];
485
+ const hasProvider = passthroughArgs.includes("--provider");
486
+ const hasModel = passthroughArgs.some((arg, idx) => {
487
+ return arg === "--model" || arg === "-m" || passthroughArgs[idx - 1] === "--model";
488
+ });
489
+ if (!hasProvider && !hasModel) {
490
+ extraArgs.push("--provider", "monk", "--model", defaultModel);
491
+ } else if (!hasProvider && hasModel) {
492
+ extraArgs.push("--provider", "monk");
493
+ }
494
+ const fullArgs = [...resolved.argsPrefix, ...extraArgs, ...passthroughArgs];
495
+ const env = {
496
+ ...process.env,
497
+ MONK_API_KEY: apiKey
498
+ };
499
+ const child = spawn(resolved.command, fullArgs, {
500
+ stdio: "inherit",
501
+ env
502
+ });
503
+ const forwardSignal = (signal) => {
504
+ try {
505
+ child.kill(signal);
506
+ } catch {
507
+ }
508
+ };
509
+ process.on("SIGINT", () => forwardSignal("SIGINT"));
510
+ process.on("SIGTERM", () => forwardSignal("SIGTERM"));
511
+ return new Promise((resolve) => {
512
+ child.on("close", (code) => {
513
+ resolve(code ?? 0);
514
+ });
515
+ child.on("error", (err) => {
516
+ logError(`\u542F\u52A8 Pi \u53D1\u751F\u9519\u8BEF: ${err.message}`);
517
+ resolve(1);
518
+ });
519
+ });
520
+ }
521
+
522
+ // src/commands/doctor.ts
523
+ import fs3 from "fs";
524
+ import os2 from "os";
525
+ import pc3 from "picocolors";
526
+ async function doctorCommand() {
527
+ printBanner();
528
+ console.log(` ${pc3.bold("\u3010Monk \xD7 Pi \u73AF\u5883\u8BCA\u65AD (Doctor)\u3011")}`);
529
+ console.log();
530
+ let hasIssue = false;
531
+ const nodeVersion = process.version;
532
+ const major = parseInt(nodeVersion.slice(1).split(".")[0], 10);
533
+ if (major >= 18) {
534
+ logSuccess(`Node.js \u7248\u672C: ${pc3.bold(nodeVersion)} (\u6EE1\u8DB3 >= 18 \u8981\u6C42)`);
535
+ } else {
536
+ hasIssue = true;
537
+ logError(`Node.js \u7248\u672C: ${pc3.bold(nodeVersion)} (\u8FC7\u4F4E\uFF0C\u8BF7\u5347\u7EA7\u5230 Node 18+)`);
538
+ }
539
+ logInfo(`\u64CD\u4F5C\u7CFB\u7EDF: ${os2.type()} ${os2.release()} (${os2.arch()})`);
540
+ const pi = detectPi();
541
+ if (pi.installed) {
542
+ logSuccess(
543
+ `Pi \u6838\u5FC3\u5957\u4EF6: ${pc3.bold(`\u5DF2\u5C31\u7EEA (v${pi.version})`)} \xB7 ${pc3.dim(`[\u6765\u6E90: ${pi.description}]`)}`
544
+ );
545
+ } else {
546
+ hasIssue = true;
547
+ logError(`Pi \u6838\u5FC3\u5957\u4EF6: ${pc3.bold("\u672A\u627E\u5230\u53EF\u7528\u8FD0\u884C\u65F6")}`);
548
+ console.log(` ${pc3.dim("\u89E3\u51B3\u529E\u6CD5: \u8FD0\u884C npm install -g @earendil-works/pi-coding-agent")}`);
549
+ }
550
+ const { key, source } = resolveApiKey();
551
+ if (key) {
552
+ logSuccess(`Monk API Key: ${pc3.bold(maskKey(key))} (\u6765\u6E90: ${source})`);
553
+ } else {
554
+ hasIssue = true;
555
+ logError(`Monk API Key: \u672A\u914D\u7F6E`);
556
+ console.log(` ${pc3.dim("\u89E3\u51B3\u529E\u6CD5: \u8FD0\u884C monk-pi login")}`);
557
+ }
558
+ const modelsFile = getPiModelsFile();
559
+ if (fs3.existsSync(modelsFile)) {
560
+ try {
561
+ const data = JSON.parse(fs3.readFileSync(modelsFile, "utf-8"));
562
+ const monk = data?.providers?.monk;
563
+ if (monk) {
564
+ const hasCompat = monk.compat?.supportsDeveloperRole === false && monk.compat?.maxTokensField === "max_tokens";
565
+ if (hasCompat) {
566
+ logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: ${pc3.bold("\u5DF2\u914D\u7F6E Monk \u4E13\u5C5E\u4F18\u5316\u517C\u5BB9\u53C2\u6570")}`);
567
+ } else {
568
+ logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5B58\u5728 Monk \u914D\u7F6E\uFF0C\u4F46\u7F3A\u5C11\u6700\u4F73 compat \u53C2\u6570`);
569
+ console.log(` ${pc3.dim("\u6B63\u5728\u4E3A\u60A8\u81EA\u52A8\u7EA0\u6B63...")}`);
570
+ syncPiModelsJson(key);
571
+ logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u5DF2\u81EA\u52A8\u4FEE\u590D\u4F18\u5316`);
572
+ }
573
+ } else {
574
+ logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5C1A\u672A\u6CE8\u5165 Monk \u4F9B\u5E94\u5546`);
575
+ syncPiModelsJson(key);
576
+ logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u5DF2\u6210\u529F\u81EA\u52A8\u6CE8\u5165`);
577
+ }
578
+ } catch {
579
+ logError(`Pi \u914D\u7F6E\u6587\u4EF6: ${modelsFile} \u5B58\u5728\u8BED\u6CD5\u9519\u8BEF`);
580
+ }
581
+ } else {
582
+ logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5C1A\u672A\u521B\u5EFA\uFF0C\u6B63\u5728\u4E3A\u60A8\u81EA\u52A8\u521B\u5EFA...`);
583
+ syncPiModelsJson(key);
584
+ logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u521B\u5EFA\u5B8C\u6210`);
585
+ }
586
+ if (key) {
587
+ process.stdout.write(` ${pc3.dim("\u23F3 \u6B63\u5728\u63A2\u6D4B\u4E0E monk.party \u63A5\u53E3\u7684\u8FDE\u901A\u6027...")} `);
588
+ const val = await validateApiKey(key);
589
+ process.stdout.write("\r" + " ".repeat(50) + "\r");
590
+ if (val.valid) {
591
+ logSuccess(`Monk \u8FDE\u901A\u6027: ${pc3.green("\u4F18\u79C0")} \xB7 \u5EF6\u8FDF ${val.latencyMs}ms`);
592
+ } else {
593
+ hasIssue = true;
594
+ logError(`Monk \u8FDE\u901A\u6027\u5F02\u5E38: ${val.error}`);
595
+ }
596
+ }
597
+ console.log();
598
+ console.log(pc3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
599
+ if (hasIssue) {
600
+ logWarn("\u68C0\u6D4B\u5230\u73AF\u5883\u5B58\u5728\u90E8\u5206\u95EE\u9898\uFF0C\u8BF7\u53C2\u8003\u4E0A\u65B9\u63D0\u793A\u5904\u7406\u3002");
601
+ } else {
602
+ logSuccess(pc3.bold("\u592A\u68D2\u4E86\uFF01\u6240\u6709\u68C0\u67E5\u9879\u5168\u90E8\u901A\u8FC7\uFF0C\u73AF\u5883\u5904\u4E8E\u6700\u4F73\u72B6\u6001\u3002"));
603
+ }
604
+ console.log();
605
+ }
606
+
607
+ // src/commands/login.ts
608
+ import pc4 from "picocolors";
609
+ async function loginCommand() {
610
+ printBanner();
611
+ const current = resolveApiKey();
612
+ const apiKey = await promptForApiKey(current.key || void 0);
613
+ if (!apiKey) {
614
+ logWarn("\u672A\u8F93\u5165 API Key\uFF0C\u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002");
615
+ return false;
616
+ }
617
+ logInfo("\u6B63\u5728\u8FDE\u63A5 Monk API \u9A8C\u8BC1\u5BC6\u94A5...");
618
+ const result = await validateApiKey(apiKey);
619
+ if (!result.valid) {
620
+ logError(`\u9A8C\u8BC1\u5931\u8D25: ${result.error || "\u672A\u77E5\u9519\u8BEF"}`);
621
+ logWarn("\u8BF7\u6838\u5BF9\u540E\u91CD\u65B0\u8FD0\u884C `monk-pi login`\u3002");
622
+ return false;
623
+ }
624
+ logSuccess(`\u9A8C\u8BC1\u901A\u8FC7\uFF01\u7F51\u7EDC\u5EF6\u8FDF: ${pc4.green(`${result.latencyMs}ms`)}`);
625
+ if (result.models.length > 0) {
626
+ logInfo(`\u53EF\u7528\u6A21\u578B: ${pc4.yellow(result.models.join(", "))}`);
627
+ }
628
+ saveMonkConfig({
629
+ apiKey,
630
+ lastVerified: (/* @__PURE__ */ new Date()).toISOString()
631
+ });
632
+ logSuccess(`\u51ED\u8BC1\u5DF2\u4FDD\u5B58\u81F3\u672C\u5730\u914D\u7F6E`);
633
+ const syncResult = syncPiModelsJson(apiKey);
634
+ if (syncResult.success) {
635
+ logSuccess(`\u5DF2\u6210\u529F\u540C\u6B65\u5E76\u4F18\u5316 Pi \u914D\u7F6E\u6587\u4EF6: ${pc4.dim(syncResult.path)}`);
636
+ } else {
637
+ logWarn(`\u540C\u6B65 Pi \u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${syncResult.error}`);
638
+ }
639
+ console.log();
640
+ logSuccess(`Monk \xD7 Pi \u5C31\u7EEA\uFF01\u73B0\u5728\u60A8\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165 ${pc4.bold(pc4.yellow("monk-pi"))} \u5F00\u59CB\u7F16\u7801\u3002`);
641
+ console.log();
642
+ return true;
643
+ }
644
+
645
+ // src/commands/model.ts
646
+ import pc5 from "picocolors";
647
+ async function modelCommand(targetModel) {
648
+ printBanner();
649
+ const currentModel = getDefaultModel();
650
+ if (targetModel) {
651
+ const matched2 = MONK_MODELS.find(
652
+ (m) => m.id === targetModel || m.id.endsWith(targetModel)
653
+ );
654
+ if (!matched2) {
655
+ logWarn(`\u672A\u77E5\u6A21\u578B: "${targetModel}"`);
656
+ logInfo(`\u652F\u6301\u7684\u6A21\u578B\u5217\u8868: ${MONK_MODELS.map((m) => m.id).join(", ")}`);
657
+ return;
658
+ }
659
+ saveMonkConfig({ defaultModel: matched2.id });
660
+ logSuccess(`\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B\u5DF2\u5207\u6362\u4E3A: ${pc5.bold(pc5.yellow(matched2.name))}`);
661
+ return;
662
+ }
663
+ const selected = await promptSelectModel(currentModel);
664
+ saveMonkConfig({ defaultModel: selected });
665
+ const matched = MONK_MODELS.find((m) => m.id === selected);
666
+ console.log();
667
+ logSuccess(`\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B\u5DF2\u6210\u529F\u8BBE\u5B9A\u4E3A: ${pc5.bold(pc5.yellow(matched?.name || selected))}`);
668
+ console.log();
669
+ }
670
+
671
+ // src/commands/run.ts
672
+ async function runCommand(passthroughArgs) {
673
+ const piCheck = detectPi();
674
+ if (!piCheck.installed) {
675
+ const shouldInstall = await promptInstallPi();
676
+ if (shouldInstall) {
677
+ const installed = await installPi();
678
+ if (!installed) {
679
+ logError("\u65E0\u6CD5\u81EA\u52A8\u5B89\u88C5 Pi\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C `npm install -g @earendil-works/pi-coding-agent` \u540E\u91CD\u8BD5\u3002");
680
+ return 1;
681
+ }
682
+ } else {
683
+ logWarn("\u5DF2\u53D6\u6D88\u8FD0\u884C\u3002Monk Harness \u9700\u8981 Pi \u4F5C\u4E3A\u8FD0\u884C\u5E95\u5EA7\u3002");
684
+ return 1;
685
+ }
686
+ }
687
+ let { key } = resolveApiKey();
688
+ if (!key) {
689
+ logWarn("\u672A\u68C0\u6D4B\u5230 Monk API Key\uFF0C\u6B63\u5728\u4E3A\u60A8\u542F\u52A8\u9996\u6B21\u914D\u7F6E\u5F15\u5BFC...");
690
+ const ok = await loginCommand();
691
+ if (!ok) {
692
+ return 1;
693
+ }
694
+ const resolved = resolveApiKey();
695
+ key = resolved.key;
696
+ }
697
+ if (!key) {
698
+ logError("\u672A\u627E\u5230\u6709\u6548\u7684 Monk API Key\uFF0C\u9000\u51FA\u8FD0\u884C\u3002");
699
+ return 1;
700
+ }
701
+ syncPiModelsJson(key);
702
+ const defaultModel = getDefaultModel();
703
+ return launchPi({
704
+ apiKey: key,
705
+ defaultModel,
706
+ passthroughArgs
707
+ });
708
+ }
709
+
710
+ // src/commands/status.ts
711
+ import pc6 from "picocolors";
712
+ async function statusCommand() {
713
+ printBanner();
714
+ console.log(` ${pc6.bold("\u3010\u670D\u52A1\u4E0E\u73AF\u5883\u72B6\u6001\u68C0\u6D4B\u3011")}`);
715
+ console.log();
716
+ const piDetect = detectPi();
717
+ if (piDetect.installed) {
718
+ logSuccess(
719
+ `Pi \u8FD0\u884C\u73AF\u5883: ${pc6.bold("\u5DF2\u5C31\u7EEA")} (v${piDetect.version}) \xB7 ${pc6.dim(`[\u6765\u6E90: ${piDetect.description}]`)}`
720
+ );
721
+ } else {
722
+ logWarn(`Pi \u8FD0\u884C\u73AF\u5883: ${pc6.red("\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u8FD0\u884C\u65F6")}`);
723
+ console.log(` ${pc6.dim("\u8BF7\u8FD0\u884C npm install -g @earendil-works/pi-coding-agent")}`);
724
+ }
725
+ const { key, source } = resolveApiKey();
726
+ const defaultModel = getDefaultModel();
727
+ if (!key) {
728
+ logError(`Monk API Key: ${pc6.red("\u672A\u914D\u7F6E")}`);
729
+ console.log(` ${pc6.dim("\u8BF7\u8FD0\u884C `monk-pi login` \u8F93\u5165\u60A8\u7684 Key")}`);
730
+ return;
731
+ }
732
+ const sourceLabels = {
733
+ env: "\u73AF\u5883\u53D8\u91CF ($MONK_API_KEY)",
734
+ local: `\u672C\u5730\u914D\u7F6E (${getMonkConfigFile()})`,
735
+ pi: `Pi \u914D\u7F6E\u6587\u4EF6 (${getPiModelsFile()})`,
736
+ none: "\u65E0"
737
+ };
738
+ logSuccess(`Monk API Key: ${pc6.bold(maskKey(key))} ${pc6.dim(`[\u6765\u6E90: ${sourceLabels[source]}]`)}`);
739
+ logInfo(`\u5F53\u524D\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B: ${pc6.yellow(defaultModel)}`);
740
+ logInfo(`Monk \u57FA\u7840\u5730\u5740: ${pc6.dim(MONK_BASE_URL)}`);
741
+ process.stdout.write(` ${pc6.dim("\u23F3 \u6B63\u5728\u6D4B\u8BD5 Monk API \u8FDE\u63A5\u4E0E\u8BA4\u8BC1...")} `);
742
+ const valResult = await validateApiKey(key);
743
+ if (valResult.valid) {
744
+ process.stdout.write("\r" + " ".repeat(50) + "\r");
745
+ logSuccess(
746
+ `Monk API \u8FDE\u63A5\u6B63\u5E38 ${pc6.dim("\xB7")} \u8BA4\u8BC1\u6709\u6548 ${pc6.dim("\xB7")} \u5EF6\u8FDF: ${pc6.green(`${valResult.latencyMs}ms`)}`
747
+ );
748
+ if (valResult.models.length > 0) {
749
+ logInfo(`\u652F\u6301\u7684\u6A21\u578B\u7AEF\u70B9: ${pc6.dim(valResult.models.join(" \xB7 "))}`);
750
+ }
751
+ } else {
752
+ process.stdout.write("\r" + " ".repeat(50) + "\r");
753
+ logError(`Monk API \u9A8C\u8BC1\u5F02\u5E38: ${valResult.error}`);
754
+ return;
755
+ }
756
+ process.stdout.write(` ${pc6.dim(`\u23F3 \u6B63\u5728\u5BF9 ${defaultModel} \u53D1\u8D77\u6781\u7B80\u63E1\u624B\u63A8\u6F14...`)} `);
757
+ const testRes = await testChatCompletion(key, defaultModel);
758
+ if (testRes.success) {
759
+ process.stdout.write("\r" + " ".repeat(50) + "\r");
760
+ logSuccess(
761
+ `\u63A8\u7406\u901A\u9053\u63E1\u624B\u6210\u529F ${pc6.dim("\xB7")} \u54CD\u5E94\u5EF6\u8FDF: ${pc6.green(`${testRes.latencyMs}ms`)}`
762
+ );
763
+ } else {
764
+ process.stdout.write("\r" + " ".repeat(50) + "\r");
765
+ logWarn(`\u63A8\u7406\u901A\u9053\u63E1\u624B\u8F7B\u5FAE\u544A\u8B66: ${testRes.error || "\u672A\u8FD4\u56DE\u5E38\u89C4\u5185\u5BB9"}`);
766
+ }
767
+ console.log();
768
+ console.log(pc6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
769
+ console.log(` ${pc6.dim("\u5B98\u65B9\u7F51\u7AD9:")} ${pc6.cyan(MONK_WEBSITE)}`);
770
+ console.log(` ${pc6.dim("\u7528\u91CF\u4E0E\u5230\u671F\u67E5\u8BE2:")} ${pc6.cyan(MONK_ACCOUNT_URL)}`);
771
+ console.log();
772
+ }
773
+
774
+ export {
775
+ MONK_BASE_URL,
776
+ MONK_WEBSITE,
777
+ MONK_ACCOUNT_URL,
778
+ MONK_MODELS,
779
+ DEFAULT_MONK_MODEL,
780
+ getMonkHomeDir,
781
+ getMonkConfigFile,
782
+ getPiHomeDir,
783
+ getPiModelsFile,
784
+ getPiSettingsFile,
785
+ loadMonkConfig,
786
+ saveMonkConfig,
787
+ resolveApiKey,
788
+ syncPiModelsJson,
789
+ getDefaultModel,
790
+ validateApiKey,
791
+ testChatCompletion,
792
+ printBanner,
793
+ logSuccess,
794
+ logInfo,
795
+ logWarn,
796
+ logError,
797
+ logStep,
798
+ promptForApiKey,
799
+ promptSelectModel,
800
+ promptInstallPi,
801
+ maskKey,
802
+ resolvePiBinary,
803
+ detectPi,
804
+ detectPackageManager,
805
+ installPi,
806
+ launchPi,
807
+ doctorCommand,
808
+ loginCommand,
809
+ modelCommand,
810
+ runCommand,
811
+ statusCommand
812
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MONK_ACCOUNT_URL,
4
+ MONK_WEBSITE,
5
+ doctorCommand,
6
+ loginCommand,
7
+ modelCommand,
8
+ printBanner,
9
+ runCommand,
10
+ statusCommand
11
+ } from "./chunk-DHF5QVTM.js";
12
+
13
+ // src/cli.ts
14
+ import { Command } from "commander";
15
+ import pc from "picocolors";
16
+ var rawArgs = process.argv.slice(2);
17
+ var firstArg = rawArgs[0];
18
+ var SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "auth", "status", "check", "model", "doctor"]);
19
+ async function main() {
20
+ if (firstArg && SUBCOMMANDS.has(firstArg)) {
21
+ const program = new Command();
22
+ program.name("monk-pi").description("Monk \xD7 Pi: \u6781\u901F\u9AD8\u6027\u4EF7\u6BD4 Coding Agent Harness").version("0.1.0");
23
+ program.command("login").alias("auth").description("\u914D\u7F6E\u6216\u66F4\u65B0 Monk API Key\uFF0C\u5E76\u81EA\u52A8\u540C\u6B65\u5230 Pi \u914D\u7F6E\u6587\u4EF6").action(async () => {
24
+ await loginCommand();
25
+ });
26
+ program.command("status").alias("check").description("\u6D4B\u8BD5 Monk API \u5EF6\u8FDF\u3001Key \u72B6\u6001\u53CA\u6A21\u578B\u8FDE\u901A\u6027").action(async () => {
27
+ await statusCommand();
28
+ });
29
+ program.command("model [name]").description("\u5207\u6362\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B (monk-coding / monk-fast / monk)").action(async (name) => {
30
+ await modelCommand(name);
31
+ });
32
+ program.command("doctor").description("\u5168\u9762\u8BCA\u65AD\u73AF\u5883\u3001Node \u7248\u672C\u3001Pi \u6838\u5FC3\u53CA\u7F51\u7EDC\u517C\u5BB9\u6027").action(async () => {
33
+ await doctorCommand();
34
+ });
35
+ await program.parseAsync(process.argv);
36
+ return;
37
+ }
38
+ if (firstArg === "--help" || firstArg === "-h") {
39
+ printBanner();
40
+ console.log(` ${pc.bold("\u7528\u6CD5:")}`);
41
+ console.log(` ${pc.yellow("monk-pi")} \u8FDB\u5165\u4EA4\u4E92\u5F0F\u7F16\u7801\u6A21\u5F0F (\u9ED8\u8BA4\u4F7F\u7528 monk-coding)`);
42
+ console.log(` ${pc.yellow("monk-pi")} [\u9009\u9879/\u63D0\u793A\u8BCD...] \u4F20\u9012\u6307\u4EE4\u6216\u6587\u4EF6\u7ED9 Pi`);
43
+ console.log();
44
+ console.log(` ${pc.bold("\u793A\u4F8B:")}`);
45
+ console.log(` ${pc.dim("# \u542F\u52A8\u4EA4\u4E92\u5F0F Agent")}`);
46
+ console.log(` ${pc.cyan("monk-pi")}`);
47
+ console.log();
48
+ console.log(` ${pc.dim("# \u5E26\u521D\u59CB\u9700\u6C42\u8FDB\u5165\u4EA4\u4E92\u5F0F\u7EC8\u7AEF")}`);
49
+ console.log(` ${pc.cyan('monk-pi "\u91CD\u6784\u8FD9\u4E2A\u76EE\u5F55\u4E0B\u7684\u4EE3\u7801"')}`);
50
+ console.log();
51
+ console.log(` ${pc.dim("# \u975E\u4EA4\u4E92\u5F0F\u547D\u4EE4\u884C\u6A21\u5F0F (\u6253\u5370\u7ED3\u679C\u5E76\u9000\u51FA)")}`);
52
+ console.log(` ${pc.cyan('monk-pi -p "\u89E3\u91CA\u5F53\u524D package.json \u7684\u4F9D\u8D56"')}`);
53
+ console.log();
54
+ console.log(` ${pc.dim("# \u6307\u5B9A\u4F7F\u7528\u6781\u901F\u63A8\u7406\u6A21\u578B")}`);
55
+ console.log(` ${pc.cyan('monk-pi --model monk-fast "\u5FEB\u901F\u5BA1\u67E5\u8FD9\u4E2A\u63D0\u4EA4"')}`);
56
+ console.log();
57
+ console.log(` ${pc.dim("# \u6062\u590D\u4E0A\u4E00\u8F6E\u5386\u53F2\u4F1A\u8BDD")}`);
58
+ console.log(` ${pc.cyan("monk-pi --continue")}`);
59
+ console.log();
60
+ console.log(` ${pc.bold("\u4E13\u5C5E\u7BA1\u7406\u6307\u4EE4:")}`);
61
+ console.log(` ${pc.yellow("monk-pi login")} \u914D\u7F6E / \u66F4\u65B0 Monk API Key`);
62
+ console.log(` ${pc.yellow("monk-pi status")} \u68C0\u6D4B API \u5EF6\u8FDF\u3001Key \u72B6\u6001\u4E0E\u8FDE\u901A\u6027`);
63
+ console.log(` ${pc.yellow("monk-pi model [name]")} \u67E5\u770B\u6216\u5207\u6362\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B`);
64
+ console.log(` ${pc.yellow("monk-pi doctor")} \u4E00\u952E\u8BCA\u65AD\u73AF\u5883\u4F9D\u8D56\u4E0E\u517C\u5BB9\u914D\u7F6E`);
65
+ console.log();
66
+ console.log(` ${pc.bold("\u76F8\u5173\u94FE\u63A5:")}`);
67
+ console.log(` Monk \u5B98\u7F51: ${pc.underline(pc.cyan(MONK_WEBSITE))}`);
68
+ console.log(` \u7528\u91CF\u67E5\u8BE2: ${pc.underline(pc.cyan(MONK_ACCOUNT_URL))}`);
69
+ console.log();
70
+ return;
71
+ }
72
+ if (firstArg === "--version" || firstArg === "-v") {
73
+ console.log("monk-pi v0.1.0");
74
+ return;
75
+ }
76
+ const exitCode = await runCommand(rawArgs);
77
+ process.exit(exitCode);
78
+ }
79
+ main().catch((err) => {
80
+ console.error(pc.red(`
81
+ [monk-pi \u5F02\u5E38]: ${err.message || err}
82
+ `));
83
+ process.exit(1);
84
+ });
@@ -0,0 +1,152 @@
1
+ declare const MONK_BASE_URL = "https://monk.party/v1";
2
+ declare const MONK_WEBSITE = "https://monk.party";
3
+ declare const MONK_ACCOUNT_URL = "https://monk.party/account/";
4
+ declare const MONK_MODELS: readonly [{
5
+ readonly id: "monk-coding";
6
+ readonly name: "Monk Coding (代码主力 - 推荐)";
7
+ readonly description: "专为 Agent 深度优化,兼备高吞吐逻辑推演与工具调用能力";
8
+ readonly reasoning: true;
9
+ readonly contextWindow: 1000000;
10
+ readonly maxTokens: 64000;
11
+ }, {
12
+ readonly id: "monk-fast";
13
+ readonly name: "Monk Fast (极速推理)";
14
+ readonly description: "极速响应,适合日常问答、快速代码探索与单文件阅读";
15
+ readonly reasoning: true;
16
+ readonly contextWindow: 1000000;
17
+ readonly maxTokens: 64000;
18
+ }, {
19
+ readonly id: "monk";
20
+ readonly name: "Monk (融合模型)";
21
+ readonly description: "集三大 Flash 之所长,综合质量与思考能力优先";
22
+ readonly reasoning: true;
23
+ readonly contextWindow: 1000000;
24
+ readonly maxTokens: 64000;
25
+ }];
26
+ type MonkModelId = (typeof MONK_MODELS)[number]["id"];
27
+ declare const DEFAULT_MONK_MODEL: MonkModelId;
28
+ declare function getMonkHomeDir(): string;
29
+ declare function getMonkConfigFile(): string;
30
+ declare function getPiHomeDir(): string;
31
+ declare function getPiModelsFile(): string;
32
+ declare function getPiSettingsFile(): string;
33
+
34
+ interface MonkLocalConfig {
35
+ apiKey?: string;
36
+ defaultModel?: MonkModelId;
37
+ baseUrl?: string;
38
+ lastVerified?: string;
39
+ email?: string;
40
+ tradeNo?: string;
41
+ }
42
+ declare function loadMonkConfig(): MonkLocalConfig;
43
+ declare function saveMonkConfig(config: MonkLocalConfig): void;
44
+ /**
45
+ * Resolves the Monk API Key by checking env -> local config -> pi models.json
46
+ */
47
+ declare function resolveApiKey(): {
48
+ key: string;
49
+ source: "env" | "local" | "pi" | "none";
50
+ };
51
+ /**
52
+ * Safely updates or injects the Monk provider config into Pi's models.json
53
+ */
54
+ declare function syncPiModelsJson(apiKey?: string): {
55
+ success: boolean;
56
+ path: string;
57
+ error?: string;
58
+ };
59
+ /**
60
+ * Gets the current default model
61
+ */
62
+ declare function getDefaultModel(): MonkModelId;
63
+
64
+ interface ValidateResult {
65
+ valid: boolean;
66
+ latencyMs: number;
67
+ models: string[];
68
+ error?: string;
69
+ statusCode?: number;
70
+ }
71
+ interface CompletionTestResult {
72
+ success: boolean;
73
+ latencyMs: number;
74
+ reply?: string;
75
+ error?: string;
76
+ }
77
+ /**
78
+ * Validates the Monk API Key by checking GET /v1/models
79
+ */
80
+ declare function validateApiKey(apiKey: string, baseUrl?: string): Promise<ValidateResult>;
81
+ /**
82
+ * Tests an actual chat completion to ensure the model responds properly
83
+ */
84
+ declare function testChatCompletion(apiKey: string, model?: string, baseUrl?: string): Promise<CompletionTestResult>;
85
+
86
+ type PiBinaryType = "global" | "local" | "bundled" | "missing";
87
+ interface ResolvedPi {
88
+ type: PiBinaryType;
89
+ command: string;
90
+ argsPrefix: string[];
91
+ version?: string;
92
+ sourceDescription: string;
93
+ }
94
+ /**
95
+ * Resolves Pi executable across global PATH, local project, and bundled dependencies
96
+ */
97
+ declare function resolvePiBinary(): ResolvedPi;
98
+ declare function detectPi(): {
99
+ installed: boolean;
100
+ version?: string;
101
+ type?: PiBinaryType;
102
+ description?: string;
103
+ };
104
+ /**
105
+ * Detects the available package manager on the host machine
106
+ */
107
+ declare function detectPackageManager(): "bun" | "pnpm" | "npm";
108
+ /**
109
+ * Installs Pi globally using the best detected package manager
110
+ */
111
+ declare function installPi(): Promise<boolean>;
112
+ interface LaunchPiOptions {
113
+ apiKey: string;
114
+ defaultModel: string;
115
+ passthroughArgs: string[];
116
+ }
117
+ /**
118
+ * Launches Pi with Monk provider & model flags, passing through all user arguments
119
+ */
120
+ declare function launchPi(options: LaunchPiOptions): number;
121
+
122
+ declare function printBanner(): void;
123
+ declare function logSuccess(msg: string): void;
124
+ declare function logInfo(msg: string): void;
125
+ declare function logWarn(msg: string): void;
126
+ declare function logError(msg: string): void;
127
+ declare function logStep(step: number, total: number, msg: string): void;
128
+ /**
129
+ * Interactive prompt for API Key
130
+ */
131
+ declare function promptForApiKey(currentKey?: string): Promise<string>;
132
+ /**
133
+ * Interactive prompt for selecting default model
134
+ */
135
+ declare function promptSelectModel(currentModel?: MonkModelId): Promise<MonkModelId>;
136
+ /**
137
+ * Prompt to install pi if missing
138
+ */
139
+ declare function promptInstallPi(): Promise<boolean>;
140
+ declare function maskKey(key: string): string;
141
+
142
+ declare function loginCommand(): Promise<boolean>;
143
+
144
+ declare function statusCommand(): Promise<void>;
145
+
146
+ declare function modelCommand(targetModel?: string): Promise<void>;
147
+
148
+ declare function doctorCommand(): Promise<void>;
149
+
150
+ declare function runCommand(passthroughArgs: string[]): Promise<number>;
151
+
152
+ export { type CompletionTestResult, DEFAULT_MONK_MODEL, type LaunchPiOptions, MONK_ACCOUNT_URL, MONK_BASE_URL, MONK_MODELS, MONK_WEBSITE, type MonkLocalConfig, type MonkModelId, type PiBinaryType, type ResolvedPi, type ValidateResult, detectPackageManager, detectPi, doctorCommand, getDefaultModel, getMonkConfigFile, getMonkHomeDir, getPiHomeDir, getPiModelsFile, getPiSettingsFile, installPi, launchPi, loadMonkConfig, logError, logInfo, logStep, logSuccess, logWarn, loginCommand, maskKey, modelCommand, printBanner, promptForApiKey, promptInstallPi, promptSelectModel, resolveApiKey, resolvePiBinary, runCommand, saveMonkConfig, statusCommand, syncPiModelsJson, testChatCompletion, validateApiKey };
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ import {
2
+ DEFAULT_MONK_MODEL,
3
+ MONK_ACCOUNT_URL,
4
+ MONK_BASE_URL,
5
+ MONK_MODELS,
6
+ MONK_WEBSITE,
7
+ detectPackageManager,
8
+ detectPi,
9
+ doctorCommand,
10
+ getDefaultModel,
11
+ getMonkConfigFile,
12
+ getMonkHomeDir,
13
+ getPiHomeDir,
14
+ getPiModelsFile,
15
+ getPiSettingsFile,
16
+ installPi,
17
+ launchPi,
18
+ loadMonkConfig,
19
+ logError,
20
+ logInfo,
21
+ logStep,
22
+ logSuccess,
23
+ logWarn,
24
+ loginCommand,
25
+ maskKey,
26
+ modelCommand,
27
+ printBanner,
28
+ promptForApiKey,
29
+ promptInstallPi,
30
+ promptSelectModel,
31
+ resolveApiKey,
32
+ resolvePiBinary,
33
+ runCommand,
34
+ saveMonkConfig,
35
+ statusCommand,
36
+ syncPiModelsJson,
37
+ testChatCompletion,
38
+ validateApiKey
39
+ } from "./chunk-DHF5QVTM.js";
40
+ export {
41
+ DEFAULT_MONK_MODEL,
42
+ MONK_ACCOUNT_URL,
43
+ MONK_BASE_URL,
44
+ MONK_MODELS,
45
+ MONK_WEBSITE,
46
+ detectPackageManager,
47
+ detectPi,
48
+ doctorCommand,
49
+ getDefaultModel,
50
+ getMonkConfigFile,
51
+ getMonkHomeDir,
52
+ getPiHomeDir,
53
+ getPiModelsFile,
54
+ getPiSettingsFile,
55
+ installPi,
56
+ launchPi,
57
+ loadMonkConfig,
58
+ logError,
59
+ logInfo,
60
+ logStep,
61
+ logSuccess,
62
+ logWarn,
63
+ loginCommand,
64
+ maskKey,
65
+ modelCommand,
66
+ printBanner,
67
+ promptForApiKey,
68
+ promptInstallPi,
69
+ promptSelectModel,
70
+ resolveApiKey,
71
+ resolvePiBinary,
72
+ runCommand,
73
+ saveMonkConfig,
74
+ statusCommand,
75
+ syncPiModelsJson,
76
+ testChatCompletion,
77
+ validateApiKey
78
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "monk-pi",
3
+ "version": "0.1.0",
4
+ "description": "Zero-config harness for running Pi Coding Agent with Monk API (monk.party)",
5
+ "type": "module",
6
+ "bin": "./dist/cli.js",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "start": "node ./dist/cli.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "monk",
21
+ "pi",
22
+ "coding-agent",
23
+ "llm",
24
+ "ai",
25
+ "harness",
26
+ "cli"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@earendil-works/pi-coding-agent": "^0.85.1",
35
+ "@inquirer/prompts": "^8.7.2",
36
+ "commander": "^15.0.0",
37
+ "cross-spawn": "^7.0.6",
38
+ "picocolors": "^1.1.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/cross-spawn": "^6.0.6",
42
+ "@types/node": "^26.5.1",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "^5.9.3"
45
+ }
46
+ }