psyclaw 0.30.4 → 0.30.5
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 +6 -5
- package/dist/src/adapters/pi/extension.js +79 -5
- package/dist/src/adapters/pi/extension.js.map +1 -1
- 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/style/cli-ui.js.map +1 -1
- package/dist/src/tui/provider-picker.d.ts +22 -0
- package/dist/src/tui/provider-picker.js +58 -0
- package/dist/src/tui/provider-picker.js.map +1 -1
- package/dist/src/tui/skill-manager.d.ts +2 -0
- package/dist/src/tui/skill-manager.js +29 -3
- package/dist/src/tui/skill-manager.js.map +1 -1
- package/package.json +1 -1
- package/skills/recommended/catalog.json +61 -25
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、
|
|
|
4
4
|
|
|
5
5
|
PsyClaw 自有代码使用 MIT 许可证;随 npm 包内置的 Academic Research Skills 位于 `vendor/ars`,保持上游署名并单独遵循 CC BY-NC 4.0,仅限非商业用途。
|
|
6
6
|
|
|
7
|
-
当前版本:`0.30.
|
|
7
|
+
当前版本:`0.30.5`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
|
|
8
8
|
|
|
9
9
|
## 发布流程
|
|
10
10
|
|
|
@@ -21,19 +21,19 @@ RELEASE_MESSAGE="release: describe the change" pnpm release:push
|
|
|
21
21
|
需要 Node.js `>=22.19.0`。官方 npm 源:
|
|
22
22
|
|
|
23
23
|
```powershell
|
|
24
|
-
npm install -g psyclaw@0.30.
|
|
24
|
+
npm install -g psyclaw@0.30.5
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g psyclaw@0.30.
|
|
30
|
+
npm install -g psyclaw@0.30.5 --registry=https://registry.npmjs.org/
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
中国大陆网络较慢或无法访问官方源时:
|
|
34
34
|
|
|
35
35
|
```powershell
|
|
36
|
-
npm install -g psyclaw@0.30.
|
|
36
|
+
npm install -g psyclaw@0.30.5 --registry=https://registry.npmmirror.com
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
或安装脚本:
|
|
@@ -46,7 +46,7 @@ PSYCLAW_CN=1 curl -fsSL https://exekiel179.github.io/psyclaw/install.sh | sh
|
|
|
46
46
|
```powershell
|
|
47
47
|
# Windows PowerShell
|
|
48
48
|
irm https://exekiel179.github.io/psyclaw/install.ps1 | iex
|
|
49
|
-
# 可选:$env:PSYCLAW_CN = "1";$env:PSYCLAW_VERSION = "0.30.
|
|
49
|
+
# 可选:$env:PSYCLAW_CN = "1";$env:PSYCLAW_VERSION = "0.30.5"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
检测到国内 npm registry(如 npmmirror)、`PSYCLAW_CN=1` 或 `PSYCLAW_GITHUB_MIRROR` 时,内置路径统一走国内可用路由:
|
|
@@ -218,3 +218,4 @@ rm -rf ~/.psyclaw
|
|
|
218
218
|
- 遥测默认开启(匿名产品使用与错误,不含研究正文)。首次启动会说明如何关闭:`psyclaw telemetry off`。详见 [docs/telemetry.md](docs/telemetry.md)。
|
|
219
219
|
|
|
220
220
|
许可证:MIT。
|
|
221
|
+
|
|
@@ -22,7 +22,8 @@ import { panelHub } from "../../panel/hub.js";
|
|
|
22
22
|
import { appendChoiceRecord } from "../../research/choice.js";
|
|
23
23
|
import { enabledLocalSkillPaths, enabledLocalPromptPaths, readUserSkillState, scanLocalSkills, setLocalSkillEnabled, setLocalSkillsEnabled, skillNamesInPaths, userSkillId, } from "../../skills/user-skills.js";
|
|
24
24
|
import { RuntimeMcpRegistry, setUserMcpConfigEnabled, } from "../../integrations/mcp-runtime.js";
|
|
25
|
-
import { SecretInputComponent, ProviderPickerComponent, } from "../../tui/provider-picker.js";
|
|
25
|
+
import { SecretInputComponent, TextInputComponent, ProviderPickerComponent, } from "../../tui/provider-picker.js";
|
|
26
|
+
import { resolveProviderBaseUrl } from "../../core/provider-endpoint.js";
|
|
26
27
|
import { isArsPiActive, isArsPiTurn, psyclawArsPatch, setArsPiSessionActive, } from "../../ars/profile.js";
|
|
27
28
|
import { buildArsDoctorReport, ensurePdfEngineForExport } from "../../ars/doctor.js";
|
|
28
29
|
import { formatAcademicSoftRouteInvocation, rankAcademicSoftRoutes, resolveAcademicSoftRoute, } from "../../ars/academic-router.js";
|
|
@@ -131,6 +132,48 @@ async function promptProviderKey(ctx, providerName, envName) {
|
|
|
131
132
|
const result = await ctx.ui.custom((tui, theme, keybindings, done) => new SecretInputComponent(`配置 ${providerName}`, envName, tui, theme, keybindings, done));
|
|
132
133
|
return result.type === "submit" ? result.value.trim() : undefined;
|
|
133
134
|
}
|
|
135
|
+
async function promptProviderText(ctx, title, hint, initial = "") {
|
|
136
|
+
if (typeof ctx.ui.custom !== "function") {
|
|
137
|
+
throw new Error(`${title}:当前环境不支持交互输入。请在交互式终端运行 /provider。`);
|
|
138
|
+
}
|
|
139
|
+
const result = await ctx.ui.custom((tui, theme, keybindings, done) => new TextInputComponent(title, hint, tui, theme, keybindings, done, initial));
|
|
140
|
+
return result.type === "submit" ? result.value.trim() : undefined;
|
|
141
|
+
}
|
|
142
|
+
/** Interactive setup for the built-in `custom` OpenAI-compatible provider. */
|
|
143
|
+
async function configureCustomProvider(ctx) {
|
|
144
|
+
const preset = PROVIDER_PRESETS.find((item) => item.id === "custom");
|
|
145
|
+
if (!preset)
|
|
146
|
+
throw new Error("内置 custom Provider 预设缺失");
|
|
147
|
+
if (!ctx.hasUI || typeof ctx.ui.custom !== "function") {
|
|
148
|
+
throw new Error("请在交互式终端运行 /provider,选择「自定义 OpenAI 兼容接口」,并填写 Base URL 与模型 ID");
|
|
149
|
+
}
|
|
150
|
+
const baseUrlRaw = await promptProviderText(ctx, "自定义 Provider · Base URL", "例如 https://api.example.com/v1(须为 http/https,不含凭据)");
|
|
151
|
+
if (baseUrlRaw === undefined)
|
|
152
|
+
return undefined;
|
|
153
|
+
let baseUrl = "";
|
|
154
|
+
try {
|
|
155
|
+
baseUrl = resolveProviderBaseUrl(baseUrlRaw);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
throw new Error(error instanceof Error ? error.message : "Base URL 无效");
|
|
159
|
+
}
|
|
160
|
+
if (!baseUrl)
|
|
161
|
+
throw new Error("自定义 Provider 必须填写 Base URL");
|
|
162
|
+
const modelId = await promptProviderText(ctx, "自定义 Provider · 模型 ID", "填写网关要求的模型名,例如 gpt-4o-mini 或 deepseek-chat");
|
|
163
|
+
if (modelId === undefined)
|
|
164
|
+
return undefined;
|
|
165
|
+
if (!modelId || !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/.test(modelId)) {
|
|
166
|
+
throw new Error("模型 ID 无效;请使用字母数字及 . _ : / -");
|
|
167
|
+
}
|
|
168
|
+
const key = await promptProviderKey(ctx, preset.name, preset.apiKeyEnv);
|
|
169
|
+
if (key === undefined)
|
|
170
|
+
return undefined;
|
|
171
|
+
const credential = await providerCredentialSource(preset);
|
|
172
|
+
if (!key && credential === "missing") {
|
|
173
|
+
throw new Error(`未找到 ${preset.apiKeyEnv};请输入 API Key 后再继续`);
|
|
174
|
+
}
|
|
175
|
+
return { baseUrl, modelId, ...(key ? { apiKey: key } : {}) };
|
|
176
|
+
}
|
|
134
177
|
function parseModelRef(args) {
|
|
135
178
|
const value = args.trim();
|
|
136
179
|
const slash = value.indexOf("/");
|
|
@@ -1604,7 +1647,7 @@ export default function psyclawExtension(pi) {
|
|
|
1604
1647
|
});
|
|
1605
1648
|
if (!legacyTestApi)
|
|
1606
1649
|
pi.registerCommand("provider", {
|
|
1607
|
-
description: "
|
|
1650
|
+
description: "查看、配置或切换 Provider(含自定义 OpenAI 兼容接口)",
|
|
1608
1651
|
handler: async (args, ctx) => {
|
|
1609
1652
|
try {
|
|
1610
1653
|
let requested = args.trim();
|
|
@@ -1616,7 +1659,7 @@ export default function psyclawExtension(pi) {
|
|
|
1616
1659
|
}
|
|
1617
1660
|
if (!requested) {
|
|
1618
1661
|
const current = ctx.model?.provider ?? "none";
|
|
1619
|
-
const available = new Map(PROVIDER_PRESETS.filter((preset) => preset.models.length > 0).map((preset) => [preset.id, preset]));
|
|
1662
|
+
const available = new Map(PROVIDER_PRESETS.filter((preset) => preset.models.length > 0 || preset.id === "custom").map((preset) => [preset.id, preset]));
|
|
1620
1663
|
for (const id of providers.keys())
|
|
1621
1664
|
if (!available.has(id))
|
|
1622
1665
|
available.set(id, {
|
|
@@ -1629,13 +1672,15 @@ export default function psyclawExtension(pi) {
|
|
|
1629
1672
|
});
|
|
1630
1673
|
if (!ctx.hasUI || typeof ctx.ui.custom !== "function") {
|
|
1631
1674
|
const lines = [...available].map(([id, preset]) => `${id}${id === current ? " *" : ""} — ${preset.name}`);
|
|
1632
|
-
ctx.ui.notify([`当前 Provider: ${current}`, ...lines, "", "切换:/provider <id
|
|
1675
|
+
ctx.ui.notify([`当前 Provider: ${current}`, ...lines, "", "切换:/provider <id>(自定义:/provider custom)"].join("\n"), "info");
|
|
1633
1676
|
return;
|
|
1634
1677
|
}
|
|
1635
1678
|
const selectedProvider = await pickProviderItem(ctx, "选择模型 Provider", [...available].map(([id, preset]) => ({
|
|
1636
1679
|
id,
|
|
1637
1680
|
label: preset.name,
|
|
1638
|
-
description:
|
|
1681
|
+
description: id === "custom"
|
|
1682
|
+
? "自建/第三方 OpenAI 兼容网关:填写 Base URL、模型 ID 与 API Key"
|
|
1683
|
+
: `${id} · ${providers.get(id)?.length ?? preset.models.length} 个模型`,
|
|
1639
1684
|
current: id === current,
|
|
1640
1685
|
})));
|
|
1641
1686
|
if (!selectedProvider)
|
|
@@ -1644,6 +1689,35 @@ export default function psyclawExtension(pi) {
|
|
|
1644
1689
|
}
|
|
1645
1690
|
if (!/^[A-Za-z0-9._:-]+$/.test(requested))
|
|
1646
1691
|
throw new Error("请使用 /provider 打开选择,或 /provider <provider-id>");
|
|
1692
|
+
if (requested === "custom") {
|
|
1693
|
+
const custom = await configureCustomProvider(ctx);
|
|
1694
|
+
if (!custom)
|
|
1695
|
+
return;
|
|
1696
|
+
const preset = PROVIDER_PRESETS.find((item) => item.id === "custom");
|
|
1697
|
+
await saveProviderConfig({
|
|
1698
|
+
...preset,
|
|
1699
|
+
baseUrl: custom.baseUrl,
|
|
1700
|
+
models: [{ id: custom.modelId, name: custom.modelId }],
|
|
1701
|
+
...(custom.apiKey ? { apiKey: custom.apiKey } : {}),
|
|
1702
|
+
});
|
|
1703
|
+
const refreshed = await Promise.race([
|
|
1704
|
+
ctx.modelRegistry.refresh().then(() => true),
|
|
1705
|
+
new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
|
|
1706
|
+
]);
|
|
1707
|
+
if (!refreshed) {
|
|
1708
|
+
ctx.ui.notify("自定义 Provider 已保存;模型目录刷新超时,请重新启动 PsyClaw 后使用。", "warning");
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
const selected = ctx.modelRegistry.getAll().find((model) => model.provider === "custom" && model.id === custom.modelId);
|
|
1712
|
+
if (!selected)
|
|
1713
|
+
throw new Error(`自定义模型未出现在目录中:custom/${custom.modelId};请检查 Base URL 后重试`);
|
|
1714
|
+
const changed = await pi.setModel(selected);
|
|
1715
|
+
if (!changed)
|
|
1716
|
+
throw new Error("未找到 custom 的可用凭据;请重新运行 /provider custom 并输入 API Key");
|
|
1717
|
+
await saveDefaultModel("custom", selected.id);
|
|
1718
|
+
ctx.ui.notify(`已切换并设为默认模型:custom/${selected.id}(${custom.baseUrl})`, "info");
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1647
1721
|
const preset = PROVIDER_PRESETS.find((item) => item.id === requested);
|
|
1648
1722
|
let models = providers.get(requested) ?? [];
|
|
1649
1723
|
const modelChoices = models.length > 0 ? models : (preset?.models ?? []);
|