psyclaw 0.29.20 → 0.30.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 +21 -5
- package/dist/apps/panel/index.html +23 -51
- package/dist/src/adapters/pi/extension.js +18 -7
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/ars/bridge.js +3 -5
- package/dist/src/ars/bridge.js.map +1 -1
- package/dist/src/ars/doctor.js +28 -4
- package/dist/src/ars/doctor.js.map +1 -1
- package/dist/src/ars/pi-panel-executor.js +4 -6
- package/dist/src/ars/pi-panel-executor.js.map +1 -1
- package/dist/src/ars/re-review.js +3 -5
- package/dist/src/ars/re-review.js.map +1 -1
- package/dist/src/cli.js +12 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/integrations/mcp.js +3 -1
- package/dist/src/integrations/mcp.js.map +1 -1
- package/dist/src/panel/server.js +147 -6
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/platform/python.d.ts +22 -0
- package/dist/src/platform/python.js +63 -0
- package/dist/src/platform/python.js.map +1 -0
- package/dist/src/platform/windows-spawn.d.ts +11 -0
- package/dist/src/platform/windows-spawn.js +19 -0
- package/dist/src/platform/windows-spawn.js.map +1 -0
- package/dist/src/session/help.js +1 -1
- package/dist/src/session/help.js.map +1 -1
- package/dist/src/style/cli-ui.js +1 -1
- package/dist/src/tui/app.js +6 -1
- package/dist/src/tui/app.js.map +1 -1
- package/package.json +1 -1
- package/skills/core/academic-grill/SKILL.md +1 -1
package/dist/src/panel/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { lstat, mkdir, readFile, readdir, realpath, writeFile } from "node:fs/promises";
|
|
4
5
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -540,17 +541,117 @@ async function panelCatalog(root) {
|
|
|
540
541
|
},
|
|
541
542
|
};
|
|
542
543
|
});
|
|
543
|
-
|
|
544
|
-
const
|
|
544
|
+
// 1. Read stored keys from auth.json in getAgentDir() and ~/.pi/agent/auth.json
|
|
545
|
+
const authProviders = new Set();
|
|
546
|
+
for (const dir of [getAgentDir(), join(homedir(), ".pi", "agent"), join(homedir(), ".codex")]) {
|
|
547
|
+
try {
|
|
548
|
+
const raw = await readFile(join(dir, "auth.json"), "utf8");
|
|
549
|
+
const parsed = JSON.parse(raw);
|
|
550
|
+
if (parsed && typeof parsed === "object") {
|
|
551
|
+
for (const [key, val] of Object.entries(parsed)) {
|
|
552
|
+
if (val) {
|
|
553
|
+
authProviders.add(key.toLowerCase());
|
|
554
|
+
authProviders.add(key);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
catch { /* ignore */ }
|
|
560
|
+
}
|
|
561
|
+
// 2. Read stored providers from models.json in getAgentDir() and ~/.pi/agent/models.json
|
|
562
|
+
const modelsProviders = new Set();
|
|
563
|
+
for (const dir of [getAgentDir(), join(homedir(), ".pi", "agent")]) {
|
|
564
|
+
try {
|
|
565
|
+
const raw = await readFile(join(dir, "models.json"), "utf8");
|
|
566
|
+
const parsed = JSON.parse(raw);
|
|
567
|
+
if (parsed?.providers && typeof parsed.providers === "object") {
|
|
568
|
+
for (const key of Object.keys(parsed.providers)) {
|
|
569
|
+
modelsProviders.add(key.toLowerCase());
|
|
570
|
+
modelsProviders.add(key);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
catch { /* ignore */ }
|
|
575
|
+
}
|
|
576
|
+
// 3. Read .psyclaw/.env or ~/.psyclaw/.env
|
|
577
|
+
const dotEnvVars = {};
|
|
578
|
+
for (const envFile of [join(root, ".psyclaw", ".env"), join(homedir(), ".psyclaw", ".env")]) {
|
|
579
|
+
try {
|
|
580
|
+
const raw = await readFile(envFile, "utf8");
|
|
581
|
+
for (const line of raw.split("\n")) {
|
|
582
|
+
const trimmed = line.trim();
|
|
583
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
584
|
+
continue;
|
|
585
|
+
const eqIdx = trimmed.indexOf("=");
|
|
586
|
+
if (eqIdx > 0) {
|
|
587
|
+
const k = trimmed.slice(0, eqIdx).trim();
|
|
588
|
+
const v = trimmed.slice(eqIdx + 1).trim();
|
|
589
|
+
if (k && v)
|
|
590
|
+
dotEnvVars[k] = v;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
catch { /* ignore */ }
|
|
595
|
+
}
|
|
596
|
+
// 4. Read .psyclaw/config.yaml or ~/.psyclaw/config.yaml for configured provider
|
|
597
|
+
let configYamlProvider;
|
|
598
|
+
for (const yamlFile of [join(root, ".psyclaw", "config.yaml"), join(homedir(), ".psyclaw", "config.yaml")]) {
|
|
599
|
+
try {
|
|
600
|
+
const raw = await readFile(yamlFile, "utf8");
|
|
601
|
+
const match = raw.match(/^provider:\s*([^\s#]+)/m);
|
|
602
|
+
if (match && match[1]) {
|
|
603
|
+
configYamlProvider = match[1].trim();
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch { /* ignore */ }
|
|
608
|
+
}
|
|
609
|
+
const knownEnvForProvider = {
|
|
545
610
|
deepseek: "DEEPSEEK_API_KEY",
|
|
546
611
|
openai: "OPENAI_API_KEY",
|
|
547
612
|
anthropic: "ANTHROPIC_API_KEY",
|
|
548
613
|
google: "GEMINI_API_KEY",
|
|
549
|
-
|
|
614
|
+
gemini: "GEMINI_API_KEY",
|
|
615
|
+
opencode: "OPENCODE_API_KEY",
|
|
616
|
+
"opencode-go": "OPENCODE_API_KEY",
|
|
617
|
+
moonshotai: "MOONSHOT_API_KEY",
|
|
618
|
+
"moonshotai-cn": "MOONSHOT_API_KEY",
|
|
619
|
+
minimax: "MINIMAX_API_KEY",
|
|
620
|
+
"minimax-cn": "MINIMAX_API_KEY",
|
|
621
|
+
zai: "ZAI_API_KEY",
|
|
622
|
+
"zai-coding-cn": "ZAI_API_KEY",
|
|
623
|
+
qwen: "DASHSCOPE_API_KEY",
|
|
624
|
+
dashscope: "DASHSCOPE_API_KEY",
|
|
625
|
+
alibaba: "DASHSCOPE_API_KEY",
|
|
626
|
+
groq: "GROQ_API_KEY",
|
|
627
|
+
mistral: "MISTRAL_API_KEY",
|
|
628
|
+
together: "TOGETHER_API_KEY",
|
|
629
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
630
|
+
xai: "XAI_API_KEY",
|
|
631
|
+
};
|
|
632
|
+
const envForProvider = (provider) => (knownEnvForProvider[provider] ??
|
|
633
|
+
knownEnvForProvider[provider.toLowerCase()] ??
|
|
634
|
+
PROVIDER_PRESETS.find((preset) => preset.id === provider)?.apiKeyEnv ??
|
|
635
|
+
`${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`);
|
|
550
636
|
const configured = (provider) => {
|
|
637
|
+
if (provider === "ollama")
|
|
638
|
+
return true;
|
|
639
|
+
const lower = provider.toLowerCase();
|
|
640
|
+
if (authProviders.has(provider) || authProviders.has(lower))
|
|
641
|
+
return true;
|
|
642
|
+
if (modelsProviders.has(provider) || modelsProviders.has(lower))
|
|
643
|
+
return true;
|
|
644
|
+
if (configYamlProvider && (configYamlProvider === provider || configYamlProvider.toLowerCase() === lower))
|
|
645
|
+
return true;
|
|
551
646
|
const envName = envForProvider(provider);
|
|
552
|
-
|
|
647
|
+
if (envName && (Boolean(process.env[envName]) || Boolean(dotEnvVars[envName])))
|
|
648
|
+
return true;
|
|
649
|
+
const genericEnv = `${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`;
|
|
650
|
+
if (Boolean(process.env[genericEnv]) || Boolean(dotEnvVars[genericEnv]))
|
|
651
|
+
return true;
|
|
652
|
+
return false;
|
|
553
653
|
};
|
|
654
|
+
const spec = deepSeekProviderSpec();
|
|
554
655
|
const helperModels = spec.models.map((model) => ({
|
|
555
656
|
id: `${spec.id}/${model.id}`,
|
|
556
657
|
kind: "model",
|
|
@@ -1188,11 +1289,51 @@ export function createPanelServer(root, options = {}) {
|
|
|
1188
1289
|
if (url.pathname === "/api/active-provider") {
|
|
1189
1290
|
if (request.method === "GET") {
|
|
1190
1291
|
const settings = await readAgentSettings();
|
|
1292
|
+
let provider = typeof settings.defaultProvider === "string" ? settings.defaultProvider : undefined;
|
|
1293
|
+
let model = typeof settings.defaultModel === "string" ? settings.defaultModel : undefined;
|
|
1294
|
+
if (!provider || !model) {
|
|
1295
|
+
try {
|
|
1296
|
+
const piSettings = JSON.parse(await readFile(join(homedir(), ".pi", "agent", "settings.json"), "utf8"));
|
|
1297
|
+
if (!provider && typeof piSettings.defaultProvider === "string")
|
|
1298
|
+
provider = piSettings.defaultProvider;
|
|
1299
|
+
if (!model && typeof piSettings.defaultModel === "string")
|
|
1300
|
+
model = piSettings.defaultModel;
|
|
1301
|
+
}
|
|
1302
|
+
catch { /* ignore */ }
|
|
1303
|
+
}
|
|
1304
|
+
if (!provider || !model) {
|
|
1305
|
+
for (const yamlFile of [join(root, ".psyclaw", "config.yaml"), join(homedir(), ".psyclaw", "config.yaml")]) {
|
|
1306
|
+
try {
|
|
1307
|
+
const raw = await readFile(yamlFile, "utf8");
|
|
1308
|
+
const pMatch = raw.match(/^provider:\s*([^\s#]+)/m);
|
|
1309
|
+
const mMatch = raw.match(/^model:\s*([^\s#]+)/m);
|
|
1310
|
+
if (!provider && pMatch?.[1])
|
|
1311
|
+
provider = pMatch[1].trim();
|
|
1312
|
+
if (!model && mMatch?.[1])
|
|
1313
|
+
model = mMatch[1].trim();
|
|
1314
|
+
if (provider && model)
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
1317
|
+
catch { /* ignore */ }
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (!provider) {
|
|
1321
|
+
try {
|
|
1322
|
+
const catalog = await panelCatalog(root);
|
|
1323
|
+
const firstConfigured = catalog.models.find((m) => m.configured);
|
|
1324
|
+
if (firstConfigured) {
|
|
1325
|
+
provider = firstConfigured.provider;
|
|
1326
|
+
if (!model)
|
|
1327
|
+
model = firstConfigured.version || firstConfigured.id.split("/").slice(1).join("/");
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
catch { /* ignore */ }
|
|
1331
|
+
}
|
|
1191
1332
|
response.writeHead(200, { "content-type": "application/json" });
|
|
1192
1333
|
response.end(JSON.stringify({
|
|
1193
1334
|
schemaVersion: "psyclaw/active-provider/v1",
|
|
1194
|
-
provider
|
|
1195
|
-
model
|
|
1335
|
+
provider,
|
|
1336
|
+
model,
|
|
1196
1337
|
}));
|
|
1197
1338
|
return;
|
|
1198
1339
|
}
|