monk-pi 0.2.0 → 0.10.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 +71 -8
- package/dist/chunk-37UZNIY3.js +1772 -0
- package/dist/cli.js +6 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-6OMOTI3T.js +0 -1056
package/dist/chunk-6OMOTI3T.js
DELETED
|
@@ -1,1056 +0,0 @@
|
|
|
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
|
-
function getPiExtensionsDir() {
|
|
50
|
-
return path.join(getPiHomeDir(), "extensions");
|
|
51
|
-
}
|
|
52
|
-
function getPiMonkExtensionFile() {
|
|
53
|
-
return path.join(getPiExtensionsDir(), "monk.ts");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/config.ts
|
|
57
|
-
import fs from "fs";
|
|
58
|
-
|
|
59
|
-
// src/extension/code.ts
|
|
60
|
-
var MONK_EXTENSION_CODE = `// @monk-managed-pi-extension
|
|
61
|
-
// Monk \xD7 Pi Native Extension
|
|
62
|
-
// Features: Footer status, /monk slash command, and context overflow auto-compaction
|
|
63
|
-
|
|
64
|
-
const MONK_MODELS = [
|
|
65
|
-
{ id: "monk-coding", label: "monk-coding (\u4EE3\u7801\u4E3B\u529B - \u63A8\u8350 \xB7 \u6DF1\u5EA6\u5DE5\u5177\u8C03\u7528)" },
|
|
66
|
-
{ id: "monk-fast", label: "monk-fast (\u6781\u901F\u63A8\u7406 \xB7 \u5FEB\u901F\u95EE\u7B54\u4E0E\u8F7B\u91CF\u4EFB\u52A1)" },
|
|
67
|
-
{ id: "monk", label: "monk (\u878D\u5408\u65D7\u8230 \xB7 \u8D28\u91CF\u4F18\u5148\u4E0E\u7EFC\u5408\u63A8\u7406)" },
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
const OVERFLOW_PATTERNS = [
|
|
71
|
-
/context.*length/i,
|
|
72
|
-
/maximum.*tokens/i,
|
|
73
|
-
/token.*limit/i,
|
|
74
|
-
/too many tokens/i,
|
|
75
|
-
/prompt.*too long/i,
|
|
76
|
-
/request.*too large/i,
|
|
77
|
-
/context_window_exceeded/i,
|
|
78
|
-
/exceed.*context/i,
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
export default function monkExtension(pi) {
|
|
82
|
-
let turnCount = 0;
|
|
83
|
-
|
|
84
|
-
function updateStatus(ctx) {
|
|
85
|
-
if (!ctx.ui) return;
|
|
86
|
-
const model = ctx.model;
|
|
87
|
-
const isMonk = model && model.provider === "monk";
|
|
88
|
-
|
|
89
|
-
if (!isMonk) {
|
|
90
|
-
ctx.ui.setStatus("monk", undefined);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const theme = ctx.ui.theme;
|
|
95
|
-
const indicator = theme.fg("success", "\u25CF");
|
|
96
|
-
const label = theme.fg("accent", \`Monk: \${model.id}\`);
|
|
97
|
-
const meta = theme.fg("dim", " (1M ctx)");
|
|
98
|
-
ctx.ui.setStatus("monk", \`\${indicator} \${label}\${meta}\`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 1. Lifecycle Events: update footer status
|
|
102
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
103
|
-
updateStatus(ctx);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
pi.on("model_select", async (_event, ctx) => {
|
|
107
|
-
updateStatus(ctx);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
pi.on("turn_start", async (_event, ctx) => {
|
|
111
|
-
turnCount++;
|
|
112
|
-
if (!ctx.ui) return;
|
|
113
|
-
const model = ctx.model;
|
|
114
|
-
if (model && model.provider === "monk") {
|
|
115
|
-
const theme = ctx.ui.theme;
|
|
116
|
-
const spinner = theme.fg("warning", "\u25B2");
|
|
117
|
-
const label = theme.fg("accent", \`Monk: \${model.id}\`);
|
|
118
|
-
const text = theme.fg("dim", \` [Turn \${turnCount}]\`);
|
|
119
|
-
ctx.ui.setStatus("monk", \`\${spinner} \${label}\${text}\`);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
pi.on("turn_end", async (_event, ctx) => {
|
|
124
|
-
updateStatus(ctx);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// 2. Intelligent Context Overflow & Compaction Recovery
|
|
128
|
-
pi.on("message_end", async (event, ctx) => {
|
|
129
|
-
const message = event.message;
|
|
130
|
-
if (!message || message.role !== "assistant") return;
|
|
131
|
-
if (message.stopReason !== "error") return;
|
|
132
|
-
|
|
133
|
-
const isMonk = message.provider === "monk" || (ctx.model && ctx.model.provider === "monk");
|
|
134
|
-
if (!isMonk) return;
|
|
135
|
-
|
|
136
|
-
const errorMsg = message.errorMessage || "";
|
|
137
|
-
if (errorMsg.includes("context_length_exceeded")) return;
|
|
138
|
-
|
|
139
|
-
const isOverflow = OVERFLOW_PATTERNS.some((p) => p.test(errorMsg));
|
|
140
|
-
if (isOverflow) {
|
|
141
|
-
ctx.ui && ctx.ui.notify(
|
|
142
|
-
"Monk: \u68C0\u6D4B\u5230\u4E0A\u4E0B\u6587\u8D85\u957F\uFF0C\u5DF2\u81EA\u52A8\u91CD\u5199\u9519\u8BEF\u5E76\u89E6\u53D1\u667A\u80FD\u538B\u7F29 (Compaction) \u91CD\u8BD5...",
|
|
143
|
-
"warning"
|
|
144
|
-
);
|
|
145
|
-
return {
|
|
146
|
-
message: {
|
|
147
|
-
...message,
|
|
148
|
-
errorMessage: \`context_length_exceeded: \${errorMsg}\`,
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// 3. Custom Slash Command: /monk
|
|
155
|
-
pi.registerCommand("monk", {
|
|
156
|
-
description: "Monk \u4E13\u5C5E\u63A7\u5236\u53F0 (/monk [model|ping|status|account])",
|
|
157
|
-
handler: async (args, ctx) => {
|
|
158
|
-
const sub = (args || "").trim().toLowerCase();
|
|
159
|
-
|
|
160
|
-
if (sub === "model" || sub === "switch") {
|
|
161
|
-
await handleModelSwitch(pi, ctx);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (sub === "ping" || sub === "status") {
|
|
166
|
-
await handlePing(ctx);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (sub === "account" || sub === "quota") {
|
|
171
|
-
ctx.ui && ctx.ui.notify("Monk \u7528\u91CF\u4E0E\u5230\u671F\u67E5\u8BE2: https://monk.party/account/", "info");
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Default: interactive menu
|
|
176
|
-
await handleMenu(pi, ctx);
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async function handleModelSwitch(pi, ctx) {
|
|
182
|
-
if (!ctx.ui) return;
|
|
183
|
-
|
|
184
|
-
const choices = MONK_MODELS.map((m) => m.label);
|
|
185
|
-
const selectedLabel = await ctx.ui.select("\u9009\u62E9\u5F53\u524D\u4F1A\u8BDD\u7684 Monk \u6A21\u578B:", choices);
|
|
186
|
-
|
|
187
|
-
if (!selectedLabel) return;
|
|
188
|
-
|
|
189
|
-
const matched = MONK_MODELS.find((m) => m.label === selectedLabel);
|
|
190
|
-
if (!matched) return;
|
|
191
|
-
|
|
192
|
-
const model = ctx.modelRegistry.find("monk", matched.id);
|
|
193
|
-
if (!model) {
|
|
194
|
-
ctx.ui.notify(\`\u672A\u5728\u914D\u7F6E\u4E2D\u627E\u5230\u6A21\u578B: monk/\${matched.id}\`, "error");
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const success = await pi.setModel(model);
|
|
199
|
-
if (success) {
|
|
200
|
-
ctx.ui.notify(\`\u4E3B\u529B\u6A21\u578B\u5DF2\u5207\u6362\u81F3: \${matched.id}\`, "info");
|
|
201
|
-
} else {
|
|
202
|
-
ctx.ui.notify(\`\u5207\u6362\u5931\u8D25\uFF0C\u672A\u80FD\u8BBE\u7F6E\u6A21\u578B: \${matched.id}\`, "error");
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function handlePing(ctx) {
|
|
207
|
-
if (!ctx.ui) return;
|
|
208
|
-
|
|
209
|
-
const startTime = Date.now();
|
|
210
|
-
const apiKey = process.env.MONK_API_KEY || "";
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
const controller = new AbortController();
|
|
214
|
-
const timeout = setTimeout(() => controller.abort(), 6000);
|
|
215
|
-
|
|
216
|
-
const res = await fetch("https://monk.party/v1/models", {
|
|
217
|
-
headers: { Authorization: \`Bearer \${apiKey}\` },
|
|
218
|
-
signal: controller.signal,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
clearTimeout(timeout);
|
|
222
|
-
const latency = Date.now() - startTime;
|
|
223
|
-
|
|
224
|
-
if (res.ok) {
|
|
225
|
-
ctx.ui.notify(\`Monk API \u8FDE\u901A\u6B63\u5E38 \xB7 \u5EF6\u8FDF \${latency}ms\`, "info");
|
|
226
|
-
} else {
|
|
227
|
-
ctx.ui.notify(\`Monk API \u54CD\u5E94\u5F02\u5E38 (HTTP \${res.status})\`, "warning");
|
|
228
|
-
}
|
|
229
|
-
} catch (err) {
|
|
230
|
-
ctx.ui.notify(
|
|
231
|
-
\`Monk API \u8FDE\u63A5\u5931\u8D25: \${err instanceof Error ? err.message : String(err)}\`,
|
|
232
|
-
"error"
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async function handleMenu(pi, ctx) {
|
|
238
|
-
if (!ctx.ui) return;
|
|
239
|
-
|
|
240
|
-
const choice = await ctx.ui.select("Monk API \u63A7\u5236\u53F0", [
|
|
241
|
-
"1. \u5207\u6362\u4F1A\u8BDD\u6A21\u578B (Switch Model)",
|
|
242
|
-
"2. \u63A2\u6D4B\u7F51\u7EDC\u5EF6\u8FDF (Ping API)",
|
|
243
|
-
"3. \u67E5\u8BE2\u7528\u91CF\u4E0E\u5230\u671F\u65F6\u95F4 (Account Info)",
|
|
244
|
-
]);
|
|
245
|
-
|
|
246
|
-
if (!choice) return;
|
|
247
|
-
|
|
248
|
-
if (choice.startsWith("1")) {
|
|
249
|
-
await handleModelSwitch(pi, ctx);
|
|
250
|
-
} else if (choice.startsWith("2")) {
|
|
251
|
-
await handlePing(ctx);
|
|
252
|
-
} else if (choice.startsWith("3")) {
|
|
253
|
-
ctx.ui.notify("\u8BF7\u5728\u6D4F\u89C8\u5668\u6253\u5F00 https://monk.party/account/ \u67E5\u770B\u7528\u91CF\u4E0E\u5269\u4F59\u5929\u6570", "info");
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
`;
|
|
257
|
-
|
|
258
|
-
// src/config.ts
|
|
259
|
-
function loadMonkConfig() {
|
|
260
|
-
const configFile = getMonkConfigFile();
|
|
261
|
-
try {
|
|
262
|
-
if (fs.existsSync(configFile)) {
|
|
263
|
-
const content = fs.readFileSync(configFile, "utf-8");
|
|
264
|
-
return JSON.parse(content);
|
|
265
|
-
}
|
|
266
|
-
} catch {
|
|
267
|
-
}
|
|
268
|
-
return {};
|
|
269
|
-
}
|
|
270
|
-
function saveMonkConfig(config) {
|
|
271
|
-
const homeDir = getMonkHomeDir();
|
|
272
|
-
if (!fs.existsSync(homeDir)) {
|
|
273
|
-
fs.mkdirSync(homeDir, { recursive: true });
|
|
274
|
-
}
|
|
275
|
-
const configFile = getMonkConfigFile();
|
|
276
|
-
const existing = loadMonkConfig();
|
|
277
|
-
const merged = { ...existing, ...config };
|
|
278
|
-
fs.writeFileSync(configFile, JSON.stringify(merged, null, 2), "utf-8");
|
|
279
|
-
}
|
|
280
|
-
function resolveApiKey() {
|
|
281
|
-
const envKey = process.env.MONK_API_KEY?.trim();
|
|
282
|
-
if (envKey) {
|
|
283
|
-
return { key: envKey, source: "env" };
|
|
284
|
-
}
|
|
285
|
-
const localConfig = loadMonkConfig();
|
|
286
|
-
if (localConfig.apiKey?.trim()) {
|
|
287
|
-
return { key: localConfig.apiKey.trim(), source: "local" };
|
|
288
|
-
}
|
|
289
|
-
const piModelsFile = getPiModelsFile();
|
|
290
|
-
try {
|
|
291
|
-
if (fs.existsSync(piModelsFile)) {
|
|
292
|
-
const data = JSON.parse(fs.readFileSync(piModelsFile, "utf-8"));
|
|
293
|
-
const key = data?.providers?.monk?.apiKey;
|
|
294
|
-
if (typeof key === "string" && key.trim() && !key.startsWith("$")) {
|
|
295
|
-
return { key: key.trim(), source: "pi" };
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} catch {
|
|
299
|
-
}
|
|
300
|
-
return { key: "", source: "none" };
|
|
301
|
-
}
|
|
302
|
-
function syncPiModelsJson(apiKey) {
|
|
303
|
-
const piDir = getPiHomeDir();
|
|
304
|
-
const piModelsFile = getPiModelsFile();
|
|
305
|
-
try {
|
|
306
|
-
if (!fs.existsSync(piDir)) {
|
|
307
|
-
fs.mkdirSync(piDir, { recursive: true });
|
|
308
|
-
}
|
|
309
|
-
let modelsData = { providers: {} };
|
|
310
|
-
if (fs.existsSync(piModelsFile)) {
|
|
311
|
-
const raw = fs.readFileSync(piModelsFile, "utf-8");
|
|
312
|
-
try {
|
|
313
|
-
modelsData = JSON.parse(raw);
|
|
314
|
-
if (!modelsData.providers) {
|
|
315
|
-
modelsData.providers = {};
|
|
316
|
-
}
|
|
317
|
-
} catch {
|
|
318
|
-
modelsData = { providers: {} };
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const monkProviderConfig = {
|
|
322
|
-
name: "Monk",
|
|
323
|
-
baseUrl: MONK_BASE_URL,
|
|
324
|
-
api: "openai-completions",
|
|
325
|
-
apiKey: apiKey && !apiKey.startsWith("$") ? apiKey : "$MONK_API_KEY",
|
|
326
|
-
compat: {
|
|
327
|
-
supportsDeveloperRole: false,
|
|
328
|
-
maxTokensField: "max_tokens",
|
|
329
|
-
requiresToolResultName: true
|
|
330
|
-
},
|
|
331
|
-
models: MONK_MODELS.map((m) => ({
|
|
332
|
-
id: m.id,
|
|
333
|
-
name: m.name,
|
|
334
|
-
reasoning: m.reasoning,
|
|
335
|
-
input: ["text"],
|
|
336
|
-
contextWindow: m.contextWindow,
|
|
337
|
-
maxTokens: m.maxTokens,
|
|
338
|
-
cost: {
|
|
339
|
-
input: 0,
|
|
340
|
-
output: 0,
|
|
341
|
-
cacheRead: 0,
|
|
342
|
-
cacheWrite: 0
|
|
343
|
-
}
|
|
344
|
-
}))
|
|
345
|
-
};
|
|
346
|
-
modelsData.providers.monk = monkProviderConfig;
|
|
347
|
-
fs.writeFileSync(piModelsFile, JSON.stringify(modelsData, null, 2), "utf-8");
|
|
348
|
-
return { success: true, path: piModelsFile };
|
|
349
|
-
} catch (err) {
|
|
350
|
-
return {
|
|
351
|
-
success: false,
|
|
352
|
-
path: piModelsFile,
|
|
353
|
-
error: err instanceof Error ? err.message : String(err)
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
function syncPiExtension() {
|
|
358
|
-
const extDir = getPiExtensionsDir();
|
|
359
|
-
const extFile = getPiMonkExtensionFile();
|
|
360
|
-
try {
|
|
361
|
-
if (!fs.existsSync(extDir)) {
|
|
362
|
-
fs.mkdirSync(extDir, { recursive: true });
|
|
363
|
-
}
|
|
364
|
-
fs.writeFileSync(extFile, MONK_EXTENSION_CODE.trim() + "\n", "utf-8");
|
|
365
|
-
return { success: true, path: extFile };
|
|
366
|
-
} catch (err) {
|
|
367
|
-
return {
|
|
368
|
-
success: false,
|
|
369
|
-
path: extFile,
|
|
370
|
-
error: err instanceof Error ? err.message : String(err)
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
function getDefaultModel() {
|
|
375
|
-
const config = loadMonkConfig();
|
|
376
|
-
if (config.defaultModel && MONK_MODELS.some((m) => m.id === config.defaultModel)) {
|
|
377
|
-
return config.defaultModel;
|
|
378
|
-
}
|
|
379
|
-
return DEFAULT_MONK_MODEL;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// src/monk-api.ts
|
|
383
|
-
async function validateApiKey(apiKey, baseUrl = MONK_BASE_URL) {
|
|
384
|
-
const trimmedKey = apiKey.trim();
|
|
385
|
-
if (!trimmedKey) {
|
|
386
|
-
return {
|
|
387
|
-
valid: false,
|
|
388
|
-
latencyMs: 0,
|
|
389
|
-
models: [],
|
|
390
|
-
error: "API Key \u4E0D\u80FD\u4E3A\u7A7A"
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
const endpoint = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
394
|
-
const startTime = Date.now();
|
|
395
|
-
try {
|
|
396
|
-
const controller = new AbortController();
|
|
397
|
-
const timeoutId = setTimeout(() => controller.abort(), 8e3);
|
|
398
|
-
const response = await fetch(endpoint, {
|
|
399
|
-
method: "GET",
|
|
400
|
-
headers: {
|
|
401
|
-
Authorization: `Bearer ${trimmedKey}`,
|
|
402
|
-
Accept: "application/json"
|
|
403
|
-
},
|
|
404
|
-
signal: controller.signal
|
|
405
|
-
});
|
|
406
|
-
clearTimeout(timeoutId);
|
|
407
|
-
const latencyMs = Date.now() - startTime;
|
|
408
|
-
if (response.status === 401 || response.status === 403) {
|
|
409
|
-
const body = await response.json().catch(() => ({}));
|
|
410
|
-
return {
|
|
411
|
-
valid: false,
|
|
412
|
-
latencyMs,
|
|
413
|
-
models: [],
|
|
414
|
-
statusCode: response.status,
|
|
415
|
-
error: body.error?.message || "API Key \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u68C0\u67E5 monk.party \u7684\u8BA2\u9605\u51ED\u8BC1"
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
if (!response.ok) {
|
|
419
|
-
return {
|
|
420
|
-
valid: false,
|
|
421
|
-
latencyMs,
|
|
422
|
-
models: [],
|
|
423
|
-
statusCode: response.status,
|
|
424
|
-
error: `Monk API \u54CD\u5E94\u5F02\u5E38 (HTTP ${response.status})`
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
const data = await response.json().catch(() => ({}));
|
|
428
|
-
const models = Array.isArray(data.data) ? data.data.map((m) => m.id) : [];
|
|
429
|
-
return {
|
|
430
|
-
valid: true,
|
|
431
|
-
latencyMs,
|
|
432
|
-
models,
|
|
433
|
-
statusCode: response.status
|
|
434
|
-
};
|
|
435
|
-
} catch (err) {
|
|
436
|
-
const latencyMs = Date.now() - startTime;
|
|
437
|
-
const isAbort = err instanceof Error && err.name === "AbortError";
|
|
438
|
-
return {
|
|
439
|
-
valid: false,
|
|
440
|
-
latencyMs,
|
|
441
|
-
models: [],
|
|
442
|
-
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)}`
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
async function testChatCompletion(apiKey, model = "monk-fast", baseUrl = MONK_BASE_URL) {
|
|
447
|
-
const endpoint = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
|
|
448
|
-
const startTime = Date.now();
|
|
449
|
-
try {
|
|
450
|
-
const controller = new AbortController();
|
|
451
|
-
const timeoutId = setTimeout(() => controller.abort(), 12e3);
|
|
452
|
-
const response = await fetch(endpoint, {
|
|
453
|
-
method: "POST",
|
|
454
|
-
headers: {
|
|
455
|
-
"Content-Type": "application/json",
|
|
456
|
-
Authorization: `Bearer ${apiKey.trim()}`
|
|
457
|
-
},
|
|
458
|
-
body: JSON.stringify({
|
|
459
|
-
model,
|
|
460
|
-
messages: [{ role: "user", content: "pong" }],
|
|
461
|
-
max_tokens: 10,
|
|
462
|
-
temperature: 0.1
|
|
463
|
-
}),
|
|
464
|
-
signal: controller.signal
|
|
465
|
-
});
|
|
466
|
-
clearTimeout(timeoutId);
|
|
467
|
-
const latencyMs = Date.now() - startTime;
|
|
468
|
-
if (!response.ok) {
|
|
469
|
-
const errJson = await response.json().catch(() => ({}));
|
|
470
|
-
return {
|
|
471
|
-
success: false,
|
|
472
|
-
latencyMs,
|
|
473
|
-
error: errJson.error?.message || `HTTP ${response.status}`
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
const json = await response.json().catch(() => ({}));
|
|
477
|
-
const reply = json.choices?.[0]?.message?.content?.trim() || "";
|
|
478
|
-
return {
|
|
479
|
-
success: true,
|
|
480
|
-
latencyMs,
|
|
481
|
-
reply
|
|
482
|
-
};
|
|
483
|
-
} catch (err) {
|
|
484
|
-
const latencyMs = Date.now() - startTime;
|
|
485
|
-
return {
|
|
486
|
-
success: false,
|
|
487
|
-
latencyMs,
|
|
488
|
-
error: err instanceof Error ? err.message : String(err)
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// src/ui.ts
|
|
494
|
-
import pc from "picocolors";
|
|
495
|
-
import { password, select, confirm } from "@inquirer/prompts";
|
|
496
|
-
function printBanner() {
|
|
497
|
-
const line = pc.dim("\u2500".repeat(54));
|
|
498
|
-
console.log();
|
|
499
|
-
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")));
|
|
500
|
-
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")));
|
|
501
|
-
console.log(line);
|
|
502
|
-
console.log(
|
|
503
|
-
` ${pc.bold("Monk \xD7 Pi Harness")} ${pc.dim("\xB7")} ${pc.green("OpenAI-Compatible Fusion Agent")}`
|
|
504
|
-
);
|
|
505
|
-
console.log(
|
|
506
|
-
` ${pc.dim("\u6A21\u578B:")} ${pc.yellow("monk-coding")} ${pc.dim("|")} ${pc.cyan("monk-fast")} ${pc.dim("|")} ${pc.magenta("monk")}`
|
|
507
|
-
);
|
|
508
|
-
console.log(line);
|
|
509
|
-
console.log();
|
|
510
|
-
}
|
|
511
|
-
function logSuccess(msg) {
|
|
512
|
-
console.log(` ${pc.green("\u2714")} ${msg}`);
|
|
513
|
-
}
|
|
514
|
-
function logInfo(msg) {
|
|
515
|
-
console.log(` ${pc.cyan("\u2139")} ${msg}`);
|
|
516
|
-
}
|
|
517
|
-
function logWarn(msg) {
|
|
518
|
-
console.log(` ${pc.yellow("\u25B2")} ${msg}`);
|
|
519
|
-
}
|
|
520
|
-
function logError(msg) {
|
|
521
|
-
console.log(` ${pc.red("\u2716")} ${msg}`);
|
|
522
|
-
}
|
|
523
|
-
function logStep(step, total, msg) {
|
|
524
|
-
console.log(` ${pc.dim(`[${step}/${total}]`)} ${pc.bold(msg)}`);
|
|
525
|
-
}
|
|
526
|
-
async function promptForApiKey(currentKey) {
|
|
527
|
-
console.log();
|
|
528
|
-
console.log(` ${pc.bold("\u6B22\u8FCE\u4F7F\u7528 Monk \xD7 Pi \u7F16\u7801\u4F34\u4FA3\uFF01")}`);
|
|
529
|
-
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")}`);
|
|
530
|
-
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)")}`);
|
|
531
|
-
console.log();
|
|
532
|
-
const key = await password({
|
|
533
|
-
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-...):",
|
|
534
|
-
mask: "*",
|
|
535
|
-
validate: (val) => {
|
|
536
|
-
const trimmed = val.trim();
|
|
537
|
-
if (!trimmed && currentKey) return true;
|
|
538
|
-
if (!trimmed) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
|
|
539
|
-
if (!trimmed.startsWith("sk-")) {
|
|
540
|
-
return "Key \u683C\u5F0F\u901A\u5E38\u4EE5 sk- \u5F00\u5934\uFF0C\u8BF7\u786E\u8BA4\u590D\u5236\u5B8C\u6574";
|
|
541
|
-
}
|
|
542
|
-
return true;
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
const finalKey = key.trim() || currentKey || "";
|
|
546
|
-
return finalKey;
|
|
547
|
-
}
|
|
548
|
-
async function promptSelectModel(currentModel) {
|
|
549
|
-
const choices = MONK_MODELS.map((m) => ({
|
|
550
|
-
name: `${m.name.padEnd(28)} ${pc.dim(m.description)}`,
|
|
551
|
-
value: m.id,
|
|
552
|
-
description: m.description
|
|
553
|
-
}));
|
|
554
|
-
const selected = await select({
|
|
555
|
-
message: "\u8BF7\u9009\u62E9\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B:",
|
|
556
|
-
choices,
|
|
557
|
-
default: currentModel || "monk-coding"
|
|
558
|
-
});
|
|
559
|
-
return selected;
|
|
560
|
-
}
|
|
561
|
-
async function promptInstallPi() {
|
|
562
|
-
logWarn("\u672A\u68C0\u6D4B\u5230\u5168\u5C40\u5B89\u88C5\u7684 pi \u7F16\u7801\u52A9\u624B (@earendil-works/pi-coding-agent)\u3002");
|
|
563
|
-
console.log(` ${pc.dim("Monk Harness \u4F9D\u8D56 Pi \u4F5C\u4E3A\u5E95\u5C42 TUI/Agent \u8FD0\u884C\u65F6\u3002")}`);
|
|
564
|
-
console.log();
|
|
565
|
-
return await confirm({
|
|
566
|
-
message: "\u662F\u5426\u7ACB\u5373\u4F7F\u7528 npm \u5168\u5C40\u5B89\u88C5 @earendil-works/pi-coding-agent\uFF1F",
|
|
567
|
-
default: true
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
function maskKey(key) {
|
|
571
|
-
if (!key || key.length < 10) return "******";
|
|
572
|
-
return `${key.slice(0, 7)}...${key.slice(-4)}`;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// src/pi-runner.ts
|
|
576
|
-
import fs2 from "fs";
|
|
577
|
-
import path2 from "path";
|
|
578
|
-
import { fileURLToPath } from "url";
|
|
579
|
-
import spawn from "cross-spawn";
|
|
580
|
-
import pc2 from "picocolors";
|
|
581
|
-
function resolvePiBinary() {
|
|
582
|
-
try {
|
|
583
|
-
const globalCheck = spawn.sync("pi", ["--version"], {
|
|
584
|
-
encoding: "utf-8",
|
|
585
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
586
|
-
});
|
|
587
|
-
if (globalCheck.status === 0 && globalCheck.stdout) {
|
|
588
|
-
return {
|
|
589
|
-
type: "global",
|
|
590
|
-
command: "pi",
|
|
591
|
-
argsPrefix: [],
|
|
592
|
-
version: globalCheck.stdout.trim(),
|
|
593
|
-
sourceDescription: "\u7CFB\u7EDF\u5168\u5C40\u547D\u4EE4 (PATH)"
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
} catch {
|
|
597
|
-
}
|
|
598
|
-
const candidateBinDirs = [
|
|
599
|
-
path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "../node_modules/.bin/pi"),
|
|
600
|
-
path2.resolve(process.cwd(), "node_modules/.bin/pi")
|
|
601
|
-
];
|
|
602
|
-
for (const binPath of candidateBinDirs) {
|
|
603
|
-
if (fs2.existsSync(binPath)) {
|
|
604
|
-
try {
|
|
605
|
-
const localCheck = spawn.sync(binPath, ["--version"], {
|
|
606
|
-
encoding: "utf-8",
|
|
607
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
608
|
-
});
|
|
609
|
-
if (localCheck.status === 0 && localCheck.stdout) {
|
|
610
|
-
return {
|
|
611
|
-
type: "local",
|
|
612
|
-
command: binPath,
|
|
613
|
-
argsPrefix: [],
|
|
614
|
-
version: localCheck.stdout.trim(),
|
|
615
|
-
sourceDescription: `\u672C\u5730\u4F9D\u8D56 (${path2.basename(path2.dirname(binPath))})`
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
} catch {
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
try {
|
|
623
|
-
const mainEntry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
|
624
|
-
const cliEntry = path2.join(path2.dirname(mainEntry), "bundle", "cli.js");
|
|
625
|
-
if (fs2.existsSync(cliEntry)) {
|
|
626
|
-
const moduleCheck = spawn.sync(process.execPath, [cliEntry, "--version"], {
|
|
627
|
-
encoding: "utf-8",
|
|
628
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
629
|
-
});
|
|
630
|
-
if (moduleCheck.status === 0 && moduleCheck.stdout) {
|
|
631
|
-
return {
|
|
632
|
-
type: "bundled",
|
|
633
|
-
command: process.execPath,
|
|
634
|
-
argsPrefix: [cliEntry],
|
|
635
|
-
version: moduleCheck.stdout.trim(),
|
|
636
|
-
sourceDescription: "Monk-Pi \u5185\u5D4C\u6838\u5FC3 (\u514D\u5B89\u88C5\u8FD0\u884C)"
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
} catch {
|
|
641
|
-
}
|
|
642
|
-
return {
|
|
643
|
-
type: "missing",
|
|
644
|
-
command: "",
|
|
645
|
-
argsPrefix: [],
|
|
646
|
-
sourceDescription: "\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684 Pi \u6838\u5FC3"
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
function detectPi() {
|
|
650
|
-
const resolved = resolvePiBinary();
|
|
651
|
-
return {
|
|
652
|
-
installed: resolved.type !== "missing",
|
|
653
|
-
version: resolved.version,
|
|
654
|
-
type: resolved.type,
|
|
655
|
-
description: resolved.sourceDescription
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
function detectPackageManager() {
|
|
659
|
-
try {
|
|
660
|
-
const bunCheck = spawn.sync("bun", ["--version"], { stdio: "ignore" });
|
|
661
|
-
if (bunCheck.status === 0) return "bun";
|
|
662
|
-
} catch {
|
|
663
|
-
}
|
|
664
|
-
try {
|
|
665
|
-
const pnpmCheck = spawn.sync("pnpm", ["--version"], { stdio: "ignore" });
|
|
666
|
-
if (pnpmCheck.status === 0) return "pnpm";
|
|
667
|
-
} catch {
|
|
668
|
-
}
|
|
669
|
-
return "npm";
|
|
670
|
-
}
|
|
671
|
-
async function installPi() {
|
|
672
|
-
const pm = detectPackageManager();
|
|
673
|
-
let cmd = "npm";
|
|
674
|
-
let args = ["install", "-g", "@earendil-works/pi-coding-agent"];
|
|
675
|
-
if (pm === "bun") {
|
|
676
|
-
cmd = "bun";
|
|
677
|
-
args = ["add", "-g", "@earendil-works/pi-coding-agent"];
|
|
678
|
-
} else if (pm === "pnpm") {
|
|
679
|
-
cmd = "pnpm";
|
|
680
|
-
args = ["add", "-g", "@earendil-works/pi-coding-agent"];
|
|
681
|
-
}
|
|
682
|
-
logInfo(`\u68C0\u6D4B\u5230\u5305\u7BA1\u7406\u5668: ${pc2.bold(pm)}\uFF0C\u6B63\u5728\u6267\u884C: ${pc2.yellow(`${cmd} ${args.join(" ")}`)} ...`);
|
|
683
|
-
try {
|
|
684
|
-
const child = spawn.sync(cmd, args, {
|
|
685
|
-
stdio: "inherit"
|
|
686
|
-
});
|
|
687
|
-
if (child.status === 0) {
|
|
688
|
-
logSuccess("Pi \u6838\u5FC3\u5957\u4EF6\u5B89\u88C5\u6210\u529F\uFF01");
|
|
689
|
-
return true;
|
|
690
|
-
}
|
|
691
|
-
logError(`\u5B89\u88C5\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${child.status}`);
|
|
692
|
-
logWarn(`\u82E5\u56E0\u6743\u9650\u95EE\u9898 (EACCES) \u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5:`);
|
|
693
|
-
console.log(` ${pc2.cyan(`sudo ${cmd} ${args.join(" ")}`)}`);
|
|
694
|
-
console.log(` \u6216\u76F4\u63A5\u4F7F\u7528\u514D\u5B89\u88C5\u65B9\u6848: ${pc2.cyan("npx monk-pi")}`);
|
|
695
|
-
return false;
|
|
696
|
-
} catch (err) {
|
|
697
|
-
logError(`\u6267\u884C\u5B89\u88C5\u5F02\u5E38: ${err instanceof Error ? err.message : String(err)}`);
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
function launchPi(options) {
|
|
702
|
-
const resolved = resolvePiBinary();
|
|
703
|
-
if (resolved.type === "missing") {
|
|
704
|
-
logError("\u672A\u627E\u5230\u53EF\u7528\u7684 Pi \u6838\u5FC3\u8FD0\u884C\u73AF\u5883\u3002");
|
|
705
|
-
return 1;
|
|
706
|
-
}
|
|
707
|
-
const { apiKey, defaultModel, passthroughArgs } = options;
|
|
708
|
-
const extraArgs = [];
|
|
709
|
-
const hasProvider = passthroughArgs.includes("--provider");
|
|
710
|
-
const hasModel = passthroughArgs.some((arg, idx) => {
|
|
711
|
-
return arg === "--model" || arg === "-m" || passthroughArgs[idx - 1] === "--model";
|
|
712
|
-
});
|
|
713
|
-
if (!hasProvider && !hasModel) {
|
|
714
|
-
extraArgs.push("--provider", "monk", "--model", defaultModel);
|
|
715
|
-
} else if (!hasProvider && hasModel) {
|
|
716
|
-
extraArgs.push("--provider", "monk");
|
|
717
|
-
}
|
|
718
|
-
const fullArgs = [...resolved.argsPrefix, ...extraArgs, ...passthroughArgs];
|
|
719
|
-
const env = {
|
|
720
|
-
...process.env,
|
|
721
|
-
MONK_API_KEY: apiKey
|
|
722
|
-
};
|
|
723
|
-
const child = spawn(resolved.command, fullArgs, {
|
|
724
|
-
stdio: "inherit",
|
|
725
|
-
env
|
|
726
|
-
});
|
|
727
|
-
const forwardSignal = (signal) => {
|
|
728
|
-
try {
|
|
729
|
-
child.kill(signal);
|
|
730
|
-
} catch {
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
process.on("SIGINT", () => forwardSignal("SIGINT"));
|
|
734
|
-
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
|
|
735
|
-
return new Promise((resolve) => {
|
|
736
|
-
child.on("close", (code) => {
|
|
737
|
-
resolve(code ?? 0);
|
|
738
|
-
});
|
|
739
|
-
child.on("error", (err) => {
|
|
740
|
-
logError(`\u542F\u52A8 Pi \u53D1\u751F\u9519\u8BEF: ${err.message}`);
|
|
741
|
-
resolve(1);
|
|
742
|
-
});
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// src/commands/doctor.ts
|
|
747
|
-
import fs3 from "fs";
|
|
748
|
-
import os2 from "os";
|
|
749
|
-
import pc3 from "picocolors";
|
|
750
|
-
async function doctorCommand() {
|
|
751
|
-
printBanner();
|
|
752
|
-
console.log(` ${pc3.bold("\u3010Monk \xD7 Pi \u73AF\u5883\u8BCA\u65AD (Doctor)\u3011")}`);
|
|
753
|
-
console.log();
|
|
754
|
-
let hasIssue = false;
|
|
755
|
-
const nodeVersion = process.version;
|
|
756
|
-
const major = parseInt(nodeVersion.slice(1).split(".")[0], 10);
|
|
757
|
-
if (major >= 18) {
|
|
758
|
-
logSuccess(`Node.js \u7248\u672C: ${pc3.bold(nodeVersion)} (\u6EE1\u8DB3 >= 18 \u8981\u6C42)`);
|
|
759
|
-
} else {
|
|
760
|
-
hasIssue = true;
|
|
761
|
-
logError(`Node.js \u7248\u672C: ${pc3.bold(nodeVersion)} (\u8FC7\u4F4E\uFF0C\u8BF7\u5347\u7EA7\u5230 Node 18+)`);
|
|
762
|
-
}
|
|
763
|
-
logInfo(`\u64CD\u4F5C\u7CFB\u7EDF: ${os2.type()} ${os2.release()} (${os2.arch()})`);
|
|
764
|
-
const pi = detectPi();
|
|
765
|
-
if (pi.installed) {
|
|
766
|
-
logSuccess(
|
|
767
|
-
`Pi \u6838\u5FC3\u5957\u4EF6: ${pc3.bold(`\u5DF2\u5C31\u7EEA (v${pi.version})`)} \xB7 ${pc3.dim(`[\u6765\u6E90: ${pi.description}]`)}`
|
|
768
|
-
);
|
|
769
|
-
} else {
|
|
770
|
-
hasIssue = true;
|
|
771
|
-
logError(`Pi \u6838\u5FC3\u5957\u4EF6: ${pc3.bold("\u672A\u627E\u5230\u53EF\u7528\u8FD0\u884C\u65F6")}`);
|
|
772
|
-
console.log(` ${pc3.dim("\u89E3\u51B3\u529E\u6CD5: \u8FD0\u884C npm install -g @earendil-works/pi-coding-agent")}`);
|
|
773
|
-
}
|
|
774
|
-
const { key, source } = resolveApiKey();
|
|
775
|
-
if (key) {
|
|
776
|
-
logSuccess(`Monk API Key: ${pc3.bold(maskKey(key))} (\u6765\u6E90: ${source})`);
|
|
777
|
-
} else {
|
|
778
|
-
hasIssue = true;
|
|
779
|
-
logError(`Monk API Key: \u672A\u914D\u7F6E`);
|
|
780
|
-
console.log(` ${pc3.dim("\u89E3\u51B3\u529E\u6CD5: \u8FD0\u884C monk-pi login")}`);
|
|
781
|
-
}
|
|
782
|
-
const modelsFile = getPiModelsFile();
|
|
783
|
-
if (fs3.existsSync(modelsFile)) {
|
|
784
|
-
try {
|
|
785
|
-
const data = JSON.parse(fs3.readFileSync(modelsFile, "utf-8"));
|
|
786
|
-
const monk = data?.providers?.monk;
|
|
787
|
-
if (monk) {
|
|
788
|
-
const hasCompat = monk.compat?.supportsDeveloperRole === false && monk.compat?.maxTokensField === "max_tokens";
|
|
789
|
-
if (hasCompat) {
|
|
790
|
-
logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: ${pc3.bold("\u5DF2\u914D\u7F6E Monk \u4E13\u5C5E\u4F18\u5316\u517C\u5BB9\u53C2\u6570")}`);
|
|
791
|
-
} else {
|
|
792
|
-
logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5B58\u5728 Monk \u914D\u7F6E\uFF0C\u4F46\u7F3A\u5C11\u6700\u4F73 compat \u53C2\u6570`);
|
|
793
|
-
console.log(` ${pc3.dim("\u6B63\u5728\u4E3A\u60A8\u81EA\u52A8\u7EA0\u6B63...")}`);
|
|
794
|
-
syncPiModelsJson(key);
|
|
795
|
-
logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u5DF2\u81EA\u52A8\u4FEE\u590D\u4F18\u5316`);
|
|
796
|
-
}
|
|
797
|
-
} else {
|
|
798
|
-
logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5C1A\u672A\u6CE8\u5165 Monk \u4F9B\u5E94\u5546`);
|
|
799
|
-
syncPiModelsJson(key);
|
|
800
|
-
logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u5DF2\u6210\u529F\u81EA\u52A8\u6CE8\u5165`);
|
|
801
|
-
}
|
|
802
|
-
} catch {
|
|
803
|
-
logError(`Pi \u914D\u7F6E\u6587\u4EF6: ${modelsFile} \u5B58\u5728\u8BED\u6CD5\u9519\u8BEF`);
|
|
804
|
-
}
|
|
805
|
-
} else {
|
|
806
|
-
logWarn(`Pi \u914D\u7F6E\u6587\u4EF6: \u5C1A\u672A\u521B\u5EFA\uFF0C\u6B63\u5728\u4E3A\u60A8\u81EA\u52A8\u521B\u5EFA...`);
|
|
807
|
-
syncPiModelsJson(key);
|
|
808
|
-
logSuccess(`Pi \u914D\u7F6E\u6587\u4EF6: \u521B\u5EFA\u5B8C\u6210`);
|
|
809
|
-
}
|
|
810
|
-
const extFile = getPiMonkExtensionFile();
|
|
811
|
-
if (fs3.existsSync(extFile)) {
|
|
812
|
-
logSuccess(`Pi \u539F\u751F\u6269\u5C55: ${pc3.bold("\u5DF2\u5B89\u88C5")} (/monk \u6307\u4EE4\u3001\u72B6\u6001\u680F\u4E0E\u6EA2\u51FA\u81EA\u52A8\u91CD\u8BD5\u5C31\u7EEA)`);
|
|
813
|
-
} else {
|
|
814
|
-
logWarn(`Pi \u539F\u751F\u6269\u5C55: \u672A\u5B89\u88C5\uFF0C\u6B63\u5728\u4E3A\u60A8\u81EA\u52A8\u90E8\u7F72...`);
|
|
815
|
-
const extRes = syncPiExtension();
|
|
816
|
-
if (extRes.success) {
|
|
817
|
-
logSuccess(`Pi \u539F\u751F\u6269\u5C55: \u90E8\u7F72\u5B8C\u6210`);
|
|
818
|
-
} else {
|
|
819
|
-
logError(`Pi \u539F\u751F\u6269\u5C55\u90E8\u7F72\u5931\u8D25: ${extRes.error}`);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
if (key) {
|
|
823
|
-
process.stdout.write(` ${pc3.dim("\u23F3 \u6B63\u5728\u63A2\u6D4B\u4E0E monk.party \u63A5\u53E3\u7684\u8FDE\u901A\u6027...")} `);
|
|
824
|
-
const val = await validateApiKey(key);
|
|
825
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
826
|
-
if (val.valid) {
|
|
827
|
-
logSuccess(`Monk \u8FDE\u901A\u6027: ${pc3.green("\u4F18\u79C0")} \xB7 \u5EF6\u8FDF ${val.latencyMs}ms`);
|
|
828
|
-
} else {
|
|
829
|
-
hasIssue = true;
|
|
830
|
-
logError(`Monk \u8FDE\u901A\u6027\u5F02\u5E38: ${val.error}`);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
console.log();
|
|
834
|
-
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"));
|
|
835
|
-
if (hasIssue) {
|
|
836
|
-
logWarn("\u68C0\u6D4B\u5230\u73AF\u5883\u5B58\u5728\u90E8\u5206\u95EE\u9898\uFF0C\u8BF7\u53C2\u8003\u4E0A\u65B9\u63D0\u793A\u5904\u7406\u3002");
|
|
837
|
-
} else {
|
|
838
|
-
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"));
|
|
839
|
-
}
|
|
840
|
-
console.log();
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// src/commands/login.ts
|
|
844
|
-
import pc4 from "picocolors";
|
|
845
|
-
async function loginCommand() {
|
|
846
|
-
printBanner();
|
|
847
|
-
const current = resolveApiKey();
|
|
848
|
-
const apiKey = await promptForApiKey(current.key || void 0);
|
|
849
|
-
if (!apiKey) {
|
|
850
|
-
logWarn("\u672A\u8F93\u5165 API Key\uFF0C\u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002");
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
|
-
logInfo("\u6B63\u5728\u8FDE\u63A5 Monk API \u9A8C\u8BC1\u5BC6\u94A5...");
|
|
854
|
-
const result = await validateApiKey(apiKey);
|
|
855
|
-
if (!result.valid) {
|
|
856
|
-
logError(`\u9A8C\u8BC1\u5931\u8D25: ${result.error || "\u672A\u77E5\u9519\u8BEF"}`);
|
|
857
|
-
logWarn("\u8BF7\u6838\u5BF9\u540E\u91CD\u65B0\u8FD0\u884C `monk-pi login`\u3002");
|
|
858
|
-
return false;
|
|
859
|
-
}
|
|
860
|
-
logSuccess(`\u9A8C\u8BC1\u901A\u8FC7\uFF01\u7F51\u7EDC\u5EF6\u8FDF: ${pc4.green(`${result.latencyMs}ms`)}`);
|
|
861
|
-
if (result.models.length > 0) {
|
|
862
|
-
logInfo(`\u53EF\u7528\u6A21\u578B: ${pc4.yellow(result.models.join(", "))}`);
|
|
863
|
-
}
|
|
864
|
-
saveMonkConfig({
|
|
865
|
-
apiKey,
|
|
866
|
-
lastVerified: (/* @__PURE__ */ new Date()).toISOString()
|
|
867
|
-
});
|
|
868
|
-
logSuccess(`\u51ED\u8BC1\u5DF2\u4FDD\u5B58\u81F3\u672C\u5730\u914D\u7F6E`);
|
|
869
|
-
const syncResult = syncPiModelsJson(apiKey);
|
|
870
|
-
const extResult = syncPiExtension();
|
|
871
|
-
if (syncResult.success) {
|
|
872
|
-
logSuccess(`\u5DF2\u6210\u529F\u540C\u6B65\u5E76\u4F18\u5316 Pi \u914D\u7F6E\u6587\u4EF6: ${pc4.dim(syncResult.path)}`);
|
|
873
|
-
} else {
|
|
874
|
-
logWarn(`\u540C\u6B65 Pi \u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${syncResult.error}`);
|
|
875
|
-
}
|
|
876
|
-
if (extResult.success) {
|
|
877
|
-
logSuccess(`\u5DF2\u5B89\u88C5 Monk \u539F\u751F TUI \u6269\u5C55: ${pc4.dim(extResult.path)}`);
|
|
878
|
-
}
|
|
879
|
-
console.log();
|
|
880
|
-
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`);
|
|
881
|
-
console.log();
|
|
882
|
-
return true;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// src/commands/model.ts
|
|
886
|
-
import pc5 from "picocolors";
|
|
887
|
-
async function modelCommand(targetModel) {
|
|
888
|
-
printBanner();
|
|
889
|
-
const currentModel = getDefaultModel();
|
|
890
|
-
if (targetModel) {
|
|
891
|
-
const matched2 = MONK_MODELS.find(
|
|
892
|
-
(m) => m.id === targetModel || m.id.endsWith(targetModel)
|
|
893
|
-
);
|
|
894
|
-
if (!matched2) {
|
|
895
|
-
logWarn(`\u672A\u77E5\u6A21\u578B: "${targetModel}"`);
|
|
896
|
-
logInfo(`\u652F\u6301\u7684\u6A21\u578B\u5217\u8868: ${MONK_MODELS.map((m) => m.id).join(", ")}`);
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
saveMonkConfig({ defaultModel: matched2.id });
|
|
900
|
-
logSuccess(`\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B\u5DF2\u5207\u6362\u4E3A: ${pc5.bold(pc5.yellow(matched2.name))}`);
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
const selected = await promptSelectModel(currentModel);
|
|
904
|
-
saveMonkConfig({ defaultModel: selected });
|
|
905
|
-
const matched = MONK_MODELS.find((m) => m.id === selected);
|
|
906
|
-
console.log();
|
|
907
|
-
logSuccess(`\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B\u5DF2\u6210\u529F\u8BBE\u5B9A\u4E3A: ${pc5.bold(pc5.yellow(matched?.name || selected))}`);
|
|
908
|
-
console.log();
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// src/commands/run.ts
|
|
912
|
-
async function runCommand(passthroughArgs) {
|
|
913
|
-
const piCheck = detectPi();
|
|
914
|
-
if (!piCheck.installed) {
|
|
915
|
-
const shouldInstall = await promptInstallPi();
|
|
916
|
-
if (shouldInstall) {
|
|
917
|
-
const installed = await installPi();
|
|
918
|
-
if (!installed) {
|
|
919
|
-
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");
|
|
920
|
-
return 1;
|
|
921
|
-
}
|
|
922
|
-
} else {
|
|
923
|
-
logWarn("\u5DF2\u53D6\u6D88\u8FD0\u884C\u3002Monk Harness \u9700\u8981 Pi \u4F5C\u4E3A\u8FD0\u884C\u5E95\u5EA7\u3002");
|
|
924
|
-
return 1;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
let { key } = resolveApiKey();
|
|
928
|
-
if (!key) {
|
|
929
|
-
logWarn("\u672A\u68C0\u6D4B\u5230 Monk API Key\uFF0C\u6B63\u5728\u4E3A\u60A8\u542F\u52A8\u9996\u6B21\u914D\u7F6E\u5F15\u5BFC...");
|
|
930
|
-
const ok = await loginCommand();
|
|
931
|
-
if (!ok) {
|
|
932
|
-
return 1;
|
|
933
|
-
}
|
|
934
|
-
const resolved = resolveApiKey();
|
|
935
|
-
key = resolved.key;
|
|
936
|
-
}
|
|
937
|
-
if (!key) {
|
|
938
|
-
logError("\u672A\u627E\u5230\u6709\u6548\u7684 Monk API Key\uFF0C\u9000\u51FA\u8FD0\u884C\u3002");
|
|
939
|
-
return 1;
|
|
940
|
-
}
|
|
941
|
-
syncPiModelsJson(key);
|
|
942
|
-
syncPiExtension();
|
|
943
|
-
const defaultModel = getDefaultModel();
|
|
944
|
-
return launchPi({
|
|
945
|
-
apiKey: key,
|
|
946
|
-
defaultModel,
|
|
947
|
-
passthroughArgs
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
// src/commands/status.ts
|
|
952
|
-
import pc6 from "picocolors";
|
|
953
|
-
async function statusCommand() {
|
|
954
|
-
printBanner();
|
|
955
|
-
console.log(` ${pc6.bold("\u3010\u670D\u52A1\u4E0E\u73AF\u5883\u72B6\u6001\u68C0\u6D4B\u3011")}`);
|
|
956
|
-
console.log();
|
|
957
|
-
const piDetect = detectPi();
|
|
958
|
-
if (piDetect.installed) {
|
|
959
|
-
logSuccess(
|
|
960
|
-
`Pi \u8FD0\u884C\u73AF\u5883: ${pc6.bold("\u5DF2\u5C31\u7EEA")} (v${piDetect.version}) \xB7 ${pc6.dim(`[\u6765\u6E90: ${piDetect.description}]`)}`
|
|
961
|
-
);
|
|
962
|
-
} else {
|
|
963
|
-
logWarn(`Pi \u8FD0\u884C\u73AF\u5883: ${pc6.red("\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u8FD0\u884C\u65F6")}`);
|
|
964
|
-
console.log(` ${pc6.dim("\u8BF7\u8FD0\u884C npm install -g @earendil-works/pi-coding-agent")}`);
|
|
965
|
-
}
|
|
966
|
-
const { key, source } = resolveApiKey();
|
|
967
|
-
const defaultModel = getDefaultModel();
|
|
968
|
-
if (!key) {
|
|
969
|
-
logError(`Monk API Key: ${pc6.red("\u672A\u914D\u7F6E")}`);
|
|
970
|
-
console.log(` ${pc6.dim("\u8BF7\u8FD0\u884C `monk-pi login` \u8F93\u5165\u60A8\u7684 Key")}`);
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
const sourceLabels = {
|
|
974
|
-
env: "\u73AF\u5883\u53D8\u91CF ($MONK_API_KEY)",
|
|
975
|
-
local: `\u672C\u5730\u914D\u7F6E (${getMonkConfigFile()})`,
|
|
976
|
-
pi: `Pi \u914D\u7F6E\u6587\u4EF6 (${getPiModelsFile()})`,
|
|
977
|
-
none: "\u65E0"
|
|
978
|
-
};
|
|
979
|
-
logSuccess(`Monk API Key: ${pc6.bold(maskKey(key))} ${pc6.dim(`[\u6765\u6E90: ${sourceLabels[source]}]`)}`);
|
|
980
|
-
logInfo(`\u5F53\u524D\u9ED8\u8BA4\u4E3B\u529B\u6A21\u578B: ${pc6.yellow(defaultModel)}`);
|
|
981
|
-
logInfo(`Monk \u57FA\u7840\u5730\u5740: ${pc6.dim(MONK_BASE_URL)}`);
|
|
982
|
-
process.stdout.write(` ${pc6.dim("\u23F3 \u6B63\u5728\u6D4B\u8BD5 Monk API \u8FDE\u63A5\u4E0E\u8BA4\u8BC1...")} `);
|
|
983
|
-
const valResult = await validateApiKey(key);
|
|
984
|
-
if (valResult.valid) {
|
|
985
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
986
|
-
logSuccess(
|
|
987
|
-
`Monk API \u8FDE\u63A5\u6B63\u5E38 ${pc6.dim("\xB7")} \u8BA4\u8BC1\u6709\u6548 ${pc6.dim("\xB7")} \u5EF6\u8FDF: ${pc6.green(`${valResult.latencyMs}ms`)}`
|
|
988
|
-
);
|
|
989
|
-
if (valResult.models.length > 0) {
|
|
990
|
-
logInfo(`\u652F\u6301\u7684\u6A21\u578B\u7AEF\u70B9: ${pc6.dim(valResult.models.join(" \xB7 "))}`);
|
|
991
|
-
}
|
|
992
|
-
} else {
|
|
993
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
994
|
-
logError(`Monk API \u9A8C\u8BC1\u5F02\u5E38: ${valResult.error}`);
|
|
995
|
-
return;
|
|
996
|
-
}
|
|
997
|
-
process.stdout.write(` ${pc6.dim(`\u23F3 \u6B63\u5728\u5BF9 ${defaultModel} \u53D1\u8D77\u6781\u7B80\u63E1\u624B\u63A8\u6F14...`)} `);
|
|
998
|
-
const testRes = await testChatCompletion(key, defaultModel);
|
|
999
|
-
if (testRes.success) {
|
|
1000
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
1001
|
-
logSuccess(
|
|
1002
|
-
`\u63A8\u7406\u901A\u9053\u63E1\u624B\u6210\u529F ${pc6.dim("\xB7")} \u54CD\u5E94\u5EF6\u8FDF: ${pc6.green(`${testRes.latencyMs}ms`)}`
|
|
1003
|
-
);
|
|
1004
|
-
} else {
|
|
1005
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
1006
|
-
logWarn(`\u63A8\u7406\u901A\u9053\u63E1\u624B\u8F7B\u5FAE\u544A\u8B66: ${testRes.error || "\u672A\u8FD4\u56DE\u5E38\u89C4\u5185\u5BB9"}`);
|
|
1007
|
-
}
|
|
1008
|
-
console.log();
|
|
1009
|
-
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"));
|
|
1010
|
-
console.log(` ${pc6.dim("\u5B98\u65B9\u7F51\u7AD9:")} ${pc6.cyan(MONK_WEBSITE)}`);
|
|
1011
|
-
console.log(` ${pc6.dim("\u7528\u91CF\u4E0E\u5230\u671F\u67E5\u8BE2:")} ${pc6.cyan(MONK_ACCOUNT_URL)}`);
|
|
1012
|
-
console.log();
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
export {
|
|
1016
|
-
MONK_BASE_URL,
|
|
1017
|
-
MONK_WEBSITE,
|
|
1018
|
-
MONK_ACCOUNT_URL,
|
|
1019
|
-
MONK_MODELS,
|
|
1020
|
-
DEFAULT_MONK_MODEL,
|
|
1021
|
-
getMonkHomeDir,
|
|
1022
|
-
getMonkConfigFile,
|
|
1023
|
-
getPiHomeDir,
|
|
1024
|
-
getPiModelsFile,
|
|
1025
|
-
getPiSettingsFile,
|
|
1026
|
-
getPiExtensionsDir,
|
|
1027
|
-
getPiMonkExtensionFile,
|
|
1028
|
-
loadMonkConfig,
|
|
1029
|
-
saveMonkConfig,
|
|
1030
|
-
resolveApiKey,
|
|
1031
|
-
syncPiModelsJson,
|
|
1032
|
-
syncPiExtension,
|
|
1033
|
-
getDefaultModel,
|
|
1034
|
-
validateApiKey,
|
|
1035
|
-
testChatCompletion,
|
|
1036
|
-
printBanner,
|
|
1037
|
-
logSuccess,
|
|
1038
|
-
logInfo,
|
|
1039
|
-
logWarn,
|
|
1040
|
-
logError,
|
|
1041
|
-
logStep,
|
|
1042
|
-
promptForApiKey,
|
|
1043
|
-
promptSelectModel,
|
|
1044
|
-
promptInstallPi,
|
|
1045
|
-
maskKey,
|
|
1046
|
-
resolvePiBinary,
|
|
1047
|
-
detectPi,
|
|
1048
|
-
detectPackageManager,
|
|
1049
|
-
installPi,
|
|
1050
|
-
launchPi,
|
|
1051
|
-
doctorCommand,
|
|
1052
|
-
loginCommand,
|
|
1053
|
-
modelCommand,
|
|
1054
|
-
runCommand,
|
|
1055
|
-
statusCommand
|
|
1056
|
-
};
|