psyclaw 0.29.18 → 0.29.19

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.
@@ -26,6 +26,18 @@ export class PanelHub {
26
26
  this.clients.delete(response);
27
27
  };
28
28
  }
29
+ /** Force-end all SSE clients so `http.Server.close()` is not blocked by keep-alive streams. */
30
+ disconnectAll() {
31
+ for (const client of [...this.clients]) {
32
+ this.clients.delete(client);
33
+ try {
34
+ client.end();
35
+ }
36
+ catch {
37
+ /* already closed */
38
+ }
39
+ }
40
+ }
29
41
  broadcast(event) {
30
42
  const name = event.type;
31
43
  const payload = `event: ${name}\ndata: ${JSON.stringify(event)}\n\n`;
@@ -1 +1 @@
1
- {"version":3,"file":"hub.js","sourceRoot":"","sources":["../../../src/panel/hub.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hub.js","sourceRoot":"","sources":["../../../src/panel/hub.ts"],"names":[],"mappings":"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"}
@@ -42,6 +42,8 @@ export interface PanelServerOptions {
42
42
  hub?: PanelHub;
43
43
  /** Queue an approved installation task into the current PsyClaw model session. */
44
44
  installSkill?: (task: string) => Promise<void>;
45
+ /** Queue an approved MCP install/configure task into the current PsyClaw model session. */
46
+ installMcp?: (task: string) => Promise<void>;
45
47
  /** Queue an explicitly requested external-tool installation task. */
46
48
  installExternalTool?: (task: string) => Promise<void>;
47
49
  /** Install a Plugin through Pi's native package manager. */
@@ -1,8 +1,7 @@
1
1
  import { createServer } from "node:http";
2
2
  import { randomUUID } from "node:crypto";
3
- import { spawn } from "node:child_process";
4
- import { lstat, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
5
- import { dirname, join } from "node:path";
3
+ import { lstat, mkdir, readFile, readdir, realpath, writeFile } from "node:fs/promises";
4
+ import { dirname, join, relative, resolve, sep } from "node:path";
6
5
  import { fileURLToPath } from "node:url";
7
6
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
8
7
  import { listRuns, projectRunSnapshot } from "./projection.js";
@@ -479,6 +478,28 @@ function panelExternalToolInstallTask(root, item) {
479
478
  "这是外部工具,不要将它宣称为来源仓库提供的 Skill 或 Plugin。完成后用自然语言报告实际安装位置、版本和验证结果。",
480
479
  ].join("\n");
481
480
  }
481
+ function panelMcpInstallTask(root, item, prep) {
482
+ const id = String(item.id ?? "");
483
+ const name = String(item.name ?? id);
484
+ const sourceRef = String(item.sourceRef ?? "");
485
+ const suggestedCommand = typeof prep?.command === "string" && prep.command.trim()
486
+ ? prep.command.trim()
487
+ : "请根据来源仓库的最新安装说明确定";
488
+ const suggestedTarget = typeof prep?.target === "string" && prep.target.trim()
489
+ ? prep.target.trim()
490
+ : `.psyclaw/mcp/${id}.json`;
491
+ return [
492
+ `安装并配置推荐 MCP:${name} (${id})。`,
493
+ `来源网址:${sourceRef || "未提供;请停止并告知用户"}`,
494
+ `当前项目:${root}`,
495
+ `目录中的参考命令:${suggestedCommand}`,
496
+ `建议的项目配置记录:${suggestedTarget}`,
497
+ "用户已经通过 Panel 推荐页明确授权本次安装。你可以直接联网、克隆或下载来源、使用所需包管理器安装依赖、执行安装命令,并写入与该 MCP 有关的项目或用户级运行配置;不要再次要求用户批准这些安装步骤。",
498
+ "请先阅读来源仓库的实际安装与启动说明,再根据当前操作系统和宿主环境完成可运行的 stdio/HTTP MCP 配置。目录中的版本、命令和依赖只作参考,不得因为哈希、许可证、依赖或本地数据策略检查而中止安装。",
499
+ "安装完成后实际启动一次服务器或执行等价的最小连通性检查,确认启动命令和工作目录有效。不要输出、复制或提交 API Key、令牌及其他凭据。",
500
+ `成功后确保 .psyclaw/recommendations.json 的 mcp 列表包含 ${JSON.stringify(id)},说明写入了哪些配置,并提醒用户执行 /reload。若安装命令本身失败,直接诊断并修复;只有遇到必须由用户提供的凭据或外部软件许可证时才向用户说明。`,
501
+ ].join("\n");
502
+ }
482
503
  /** Metadata-only catalog for the optional panel. No install or credential read. */
483
504
  async function panelCatalog(root) {
484
505
  const scans = await discoverAgents();
@@ -607,6 +628,57 @@ async function panelCatalog(root) {
607
628
  }
608
629
  return { schemaVersion: "psyclaw/panel-catalog/v1", agents, models };
609
630
  }
631
+ function packageRootCandidates() {
632
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
633
+ return [
634
+ join(moduleDir, "..", "..", ".."),
635
+ join(moduleDir, "..", "..", "..", ".."),
636
+ join(moduleDir, "..", ".."),
637
+ process.cwd(),
638
+ ];
639
+ }
640
+ function isAllowlistedDocsRelative(rel) {
641
+ const normalized = rel.split(sep).join("/");
642
+ if (!normalized || normalized.startsWith("../") || normalized.includes("/../") || normalized.startsWith("/") || normalized.includes("\0")) {
643
+ return false;
644
+ }
645
+ return normalized === "docs" || normalized.startsWith("docs/")
646
+ || normalized.startsWith("research-materials/whitepaper/");
647
+ }
648
+ /** Read Panel help whitepaper from package docs/, following a single .md link when present. */
649
+ async function readPanelWhitepaper() {
650
+ const stubRel = join("docs", "使用白皮书.md");
651
+ for (const root of packageRootCandidates()) {
652
+ const stubPath = join(root, stubRel);
653
+ try {
654
+ const rootReal = await realpath(root);
655
+ const stubReal = await realpath(stubPath);
656
+ const stubFromRoot = relative(rootReal, stubReal).split(sep).join("/");
657
+ if (stubFromRoot !== "docs/使用白皮书.md")
658
+ continue;
659
+ const stub = await readFile(stubReal, "utf8");
660
+ const linkMatch = stub.match(/\[[^\]]*\]\(([^)\s]+\.md)\)/);
661
+ if (linkMatch?.[1]) {
662
+ const targetPath = resolve(dirname(stubReal), linkMatch[1]);
663
+ try {
664
+ const targetReal = await realpath(targetPath);
665
+ const rel = relative(rootReal, targetReal).split(sep).join("/");
666
+ if (isAllowlistedDocsRelative(rel)) {
667
+ return { markdown: await readFile(targetReal, "utf8"), source: rel };
668
+ }
669
+ }
670
+ catch {
671
+ /* linked file missing — fall through to stub */
672
+ }
673
+ }
674
+ return { markdown: stub, source: stubFromRoot };
675
+ }
676
+ catch {
677
+ /* try next package root */
678
+ }
679
+ }
680
+ throw new Error("whitepaper not found");
681
+ }
610
682
  async function recommendedSkills() {
611
683
  const moduleDir = dirname(fileURLToPath(import.meta.url));
612
684
  const candidates = [
@@ -682,21 +754,6 @@ async function findRecommendedItem(kind, id) {
682
754
  const prep = (catalog.installPrep ?? []).find((candidate) => candidate.id === id);
683
755
  return { item, ...(prep === undefined ? {} : { prep }) };
684
756
  }
685
- function runShellCommand(command, cwd) {
686
- return new Promise((resolve) => {
687
- const child = spawn(command, { shell: true, cwd, stdio: ["ignore", "pipe", "pipe"] });
688
- let output = "";
689
- const append = (chunk) => {
690
- output += String(chunk);
691
- if (output.length > 24_000)
692
- output = output.slice(-24_000);
693
- };
694
- child.stdout?.on("data", append);
695
- child.stderr?.on("data", append);
696
- child.on("error", (error) => resolve({ exitCode: 1, output: `spawn error: ${error.message}` }));
697
- child.on("close", (code) => resolve({ exitCode: code ?? 1, output }));
698
- });
699
- }
700
757
  const USER_HOOK_ID = /^u-[a-z0-9][a-z0-9._-]{0,40}$/i;
701
758
  const HOOK_EVENTS = new Set(["before-plan", "before-analysis", "before-delegation", "before-write", "after-analysis", "before-report", "after-report"]);
702
759
  function sanitizeUserHooks(body) {
@@ -958,6 +1015,22 @@ export function createPanelServer(root, options = {}) {
958
1015
  response.end(JSON.stringify({ schemaVersion: "psyclaw/panel-help/v1", ...sessionHelpDocument() }));
959
1016
  return;
960
1017
  }
1018
+ if (url.pathname === "/api/docs/whitepaper") {
1019
+ try {
1020
+ const doc = await readPanelWhitepaper();
1021
+ response.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
1022
+ response.end(JSON.stringify({
1023
+ schemaVersion: "psyclaw/panel-whitepaper/v1",
1024
+ source: doc.source,
1025
+ markdown: doc.markdown,
1026
+ }));
1027
+ }
1028
+ catch {
1029
+ response.writeHead(404, { "content-type": "application/json" });
1030
+ response.end(JSON.stringify({ error: "whitepaper not found" }));
1031
+ }
1032
+ return;
1033
+ }
961
1034
  if (url.pathname === "/api/files") {
962
1035
  response.writeHead(200, { "content-type": "application/json" });
963
1036
  response.end(JSON.stringify({ schemaVersion: "psyclaw/panel-files/v1", files: await listProjectFiles(root) }));
@@ -1177,6 +1250,7 @@ export function createPanelServer(root, options = {}) {
1177
1250
  }
1178
1251
  await options.installSkill(panelSkillInstallTask(root, found.item, scope));
1179
1252
  const state = await readRecommendationState(root);
1253
+ state.skills = [...new Set([...state.skills, id])];
1180
1254
  state.skillScopes = { ...(state.skillScopes ?? {}), [id]: scope };
1181
1255
  await writeRecommendationState(root, state);
1182
1256
  response.writeHead(202, { "content-type": "application/json" });
@@ -1187,7 +1261,32 @@ export function createPanelServer(root, options = {}) {
1187
1261
  id,
1188
1262
  scope,
1189
1263
  reloadHint: "/reload",
1190
- message: "安装任务已交给当前模型;模型完成后请启用 Skill 并执行 /reload。",
1264
+ message: "安装任务已交给当前 PsyClaw 对话中的模型;请回到终端会话查看进度。模型完成后执行 /reload。",
1265
+ }));
1266
+ return;
1267
+ }
1268
+ if (kind === "mcp") {
1269
+ if (options.installMcp === undefined) {
1270
+ response.writeHead(503, { "content-type": "application/json" });
1271
+ response.end(JSON.stringify({ error: "MCP 安装需要当前模型通道;请在 PsyClaw 对话中执行 /panel 后再安装。", reasonCode: "panel.mcp-installer-unavailable" }));
1272
+ return;
1273
+ }
1274
+ const sourceRef = String(found.item.sourceRef ?? "").trim();
1275
+ if (!sourceRef)
1276
+ throw new Error("MCP source is missing");
1277
+ await options.installMcp(panelMcpInstallTask(root, found.item, found.prep));
1278
+ const state = await readRecommendationState(root);
1279
+ state.mcp = [...new Set([...state.mcp, id])];
1280
+ await writeRecommendationState(root, state);
1281
+ response.writeHead(202, { "content-type": "application/json" });
1282
+ response.end(JSON.stringify({
1283
+ schemaVersion: "psyclaw/model-install-task/v1",
1284
+ ok: true,
1285
+ queued: true,
1286
+ id,
1287
+ reloadHint: "/reload",
1288
+ message: "安装与配置任务已交给当前 PsyClaw 对话中的模型;请回到终端会话查看进度。模型完成后执行 /reload。",
1289
+ ...(typeof found.prep?.blockedReason === "string" ? { blockedReason: found.prep.blockedReason } : {}),
1191
1290
  }));
1192
1291
  return;
1193
1292
  }
@@ -1218,61 +1317,8 @@ export function createPanelServer(root, options = {}) {
1218
1317
  response.end(JSON.stringify({ schemaVersion: "psyclaw/external-tool-install-task/v1", ok: true, queued: true, id, message: "安装任务已交给当前模型;完成后会报告版本与验证结果。" }));
1219
1318
  return;
1220
1319
  }
1221
- const shellCommand = typeof found.prep?.command === "string" && found.prep.command.trim() ? found.prep.command.trim() : undefined;
1222
- if (!shellCommand) {
1223
- response.writeHead(400, { "content-type": "application/json" });
1224
- response.end(JSON.stringify({ error: "no install command is available for this item", blockedReason: found.prep?.blockedReason ?? null }));
1225
- return;
1226
- }
1227
- const command = shellCommand;
1228
- const startedAt = new Date().toISOString();
1229
- const runId = `panel_${randomUUID().replaceAll("-", "")}`;
1230
- const idempotencyKey = `panel:install:${sha256Text(`${kind}\u0000${id}\u0000${actor}`).slice(0, 24)}`;
1231
- const receipt = {
1232
- schemaVersion: "psyclaw/tool-receipt/v1",
1233
- runId,
1234
- taskId: `install:${kind}:${id}`,
1235
- tool: "panel.install.execute",
1236
- effect: "write",
1237
- approval: "approved",
1238
- idempotencyKey,
1239
- ok: false,
1240
- command,
1241
- startedAt,
1242
- };
1243
- let exitCode;
1244
- let output;
1245
- ({ exitCode, output } = await runShellCommand(command, root));
1246
- receipt.ok = exitCode === 0;
1247
- receipt.exitCode = exitCode;
1248
- receipt.finishedAt = new Date().toISOString();
1249
- const receiptPath = await assertSafeProjectPath(root, `.psyclaw/manifests/${runId}.receipt.json`);
1250
- await atomicWriteFile(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`);
1251
- if (receipt.ok) {
1252
- const state = await readRecommendationState(root);
1253
- state.mcp = [...new Set([...state.mcp, id])];
1254
- await writeRecommendationState(root, state);
1255
- }
1256
- await appendJsonlIfMissing(projectPaths(root).audit, {
1257
- schemaVersion: "psyclaw/audit-event/v1",
1258
- at: new Date().toISOString(),
1259
- actor,
1260
- action: `panel.install.${kind}`,
1261
- targetId: id,
1262
- ok: receipt.ok,
1263
- runId,
1264
- idempotencyKey,
1265
- }, (item) => item.runId);
1266
- response.writeHead(200, { "content-type": "application/json" });
1267
- response.end(JSON.stringify({
1268
- schemaVersion: "psyclaw/install-execution-receipt/v1",
1269
- ok: receipt.ok,
1270
- exitCode,
1271
- command,
1272
- output: output.slice(-2000),
1273
- reloadHint: "/reload",
1274
- ...(typeof found.prep?.blockedReason === "string" ? { blockedReason: found.prep.blockedReason } : {}),
1275
- }));
1320
+ response.writeHead(400, { "content-type": "application/json" });
1321
+ response.end(JSON.stringify({ error: `unsupported install kind: ${String(kind)}` }));
1276
1322
  return;
1277
1323
  }
1278
1324
  if (url.pathname === "/api/recommendation-state") {