pi-web-ui 0.91.0 → 0.93.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +90 -1
  2. package/README.md +69 -14
  3. package/README.zh-CN.md +36 -10
  4. package/bin/pi-web-ui.mjs +39 -8
  5. package/dist/server/agent-service.js +938 -163
  6. package/dist/server/auth-cookie.js +26 -0
  7. package/dist/server/claim-files-tool.js +115 -0
  8. package/dist/server/claim-store.js +301 -0
  9. package/dist/server/client-state.js +114 -6
  10. package/dist/server/conversation-read-tool.js +371 -28
  11. package/dist/server/conversation-touches.js +357 -0
  12. package/dist/server/delegate-task.js +10 -3
  13. package/dist/server/dsh/dsh-agent-service.js +50 -1
  14. package/dist/server/files-service.js +78 -26
  15. package/dist/server/http-proxy.js +72 -0
  16. package/dist/server/index.js +94 -11
  17. package/dist/server/model-admin.js +124 -2
  18. package/dist/server/model-enrich.js +478 -0
  19. package/dist/server/patch-node-pty.js +11 -0
  20. package/dist/server/plugin-schedule.js +27 -2
  21. package/dist/server/plugins.js +66 -10
  22. package/dist/server/present-files-tool.js +303 -0
  23. package/dist/server/protocol-version.js +1 -1
  24. package/dist/server/read-tool.js +125 -0
  25. package/dist/server/resolve-global-sdk.js +72 -0
  26. package/dist/server/sdk-origin.js +64 -0
  27. package/dist/server/serialize.js +80 -22
  28. package/dist/server/settings-service.js +19 -1
  29. package/dist/server/skill-tool.js +2 -1
  30. package/dist/server/subagents.js +30 -13
  31. package/dist/server/terminals.js +38 -18
  32. package/dist/server/text-sniff.js +15 -0
  33. package/dist/server/themes.js +32 -2
  34. package/dist/server/tool-info.js +89 -0
  35. package/dist/server/tool-manager.js +110 -11
  36. package/package.json +2 -2
  37. package/plugin-sdk/README.md +1 -0
  38. package/plugins/catalog.json +20 -0
  39. package/themes/ayu-light.css +136 -0
  40. package/themes/catppuccin-latte.css +140 -0
  41. package/themes/catppuccin.css +144 -0
  42. package/themes/codex.css +136 -0
  43. package/themes/cyberpunk.css +4 -0
  44. package/themes/dazzle.css +4 -0
  45. package/themes/everforest-light.css +136 -0
  46. package/themes/geist.css +136 -0
  47. package/themes/gruvbox-light.css +136 -0
  48. package/themes/kanagawa-lotus.css +136 -0
  49. package/themes/md-preview.css +4 -0
  50. package/themes/mist.css +4 -0
  51. package/themes/nord.css +134 -0
  52. package/themes/one-dark.css +135 -0
  53. package/themes/paper.css +16 -12
  54. package/themes/rose-pine-dawn.css +136 -0
  55. package/themes/sakura.css +4 -0
  56. package/themes/solarized-light.css +135 -0
  57. package/themes/tokyo-night.css +144 -0
  58. package/themes/white.css +4 -0
  59. package/web/dist/assets/{TerminalPanel-GtDQ1h85.js → TerminalPanel-C1A23aI-.js} +1 -1
  60. package/web/dist/assets/index-B48QWf0I.js +364 -0
  61. package/web/dist/assets/index-DGL5OInG.css +1 -0
  62. package/web/dist/index.html +2 -2
  63. package/web/dist/assets/index-CdsKltOS.css +0 -1
  64. package/web/dist/assets/index-OoULNF70.js +0 -363
package/bin/pi-web-ui.mjs CHANGED
@@ -55,6 +55,13 @@ import { fileURLToPath } from "node:url";
55
55
  const BIN_DIR = dirname(fileURLToPath(import.meta.url));
56
56
  /** <pkg>/dist/server/index.js — the actual server entry. */
57
57
  const SERVER_ENTRY = join(BIN_DIR, "..", "dist", "server", "index.js");
58
+ /** 可选的「优先用全局/祖先那份 pi SDK」解析钩子(issue #260,`PI_WEB_SDK=global` 才生效)。
59
+ * 必须早于任何 SDK 静态 import 加载 —— 所以每条启动路径都把它当 `--import` 传进去;
60
+ * 默认不启用时它自己什么都不做(见 server/resolve-global-sdk.ts)。 */
61
+ const SDK_HOOK = join(BIN_DIR, "..", "dist", "server", "resolve-global-sdk.js");
62
+ /** dist 可能是旧构建(没有这个钩子文件)—— 只有文件在才注入:`--import <missing>` 会让
63
+ * CLI/服务直接起不来,那比少个开关严重得多。 */
64
+ const HAS_SDK_HOOK = existsSync(SDK_HOOK);
58
65
  const NODE = process.execPath;
59
66
  let pkg = { version: "0.0.0" };
60
67
  try {
@@ -390,6 +397,7 @@ async function startForeground(opts) {
390
397
  if (opts.host) process.env.PI_WEB_HOST = opts.host;
391
398
  if (opts.agentDir) process.env.PI_CODING_AGENT_DIR = resolve(opts.agentDir);
392
399
  const url = `http://localhost:${effectivePort(opts)}`;
400
+ if (HAS_SDK_HOOK) await import(pathToFileURL(SDK_HOOK).href);
393
401
  await import(pathToFileURL(SERVER_ENTRY).href);
394
402
  if (!opts.noBrowser) openBrowserWhenUp(url);
395
403
  }
@@ -623,7 +631,7 @@ function buildWinShortcutPs1(env, cwd, taskName, url, logPath, pidPath) {
623
631
  " try { if ((Invoke-WebRequest -Uri $h -UseBasicParsing -TimeoutSec 2).StatusCode -eq 200) { Start-Process $u | Out-Null; break } } catch {}",
624
632
  " }",
625
633
  "} -ArgumentList $url",
626
- `& ${psQuote(node)} ${psQuote(SERVER_ENTRY)} *>> $log`,
634
+ `& ${psQuote(node)} ${HAS_SDK_HOOK ? `--import ${psQuote(SDK_HOOK)} ` : ""}${psQuote(SERVER_ENTRY)} *>> $log`,
627
635
  "Remove-Item $pidFile -ErrorAction SilentlyContinue",
628
636
  "",
629
637
  ].join("\r\n");
@@ -726,6 +734,7 @@ URL=${shQuote(url)}
726
734
  LOG=/tmp/pi-web-ui-shortcut.log
727
735
  NODE=${shQuote(node)}
728
736
  ENTRY=${shQuote(SERVER_ENTRY)}
737
+ SDK_HOOK_ARG=${HAS_SDK_HOOK ? `${shQuote("--import")} ${shQuote(SDK_HOOK)} ` : ""}
729
738
  ${exports}
730
739
 
731
740
  if launchctl print "gui/$(id -u)/$LABEL" >/dev/null 2>&1; then
@@ -734,7 +743,7 @@ elif [ -f "$PLIST" ]; then
734
743
  launchctl bootstrap "gui/$(id -u)" "$PLIST"
735
744
  else
736
745
  # 未安装服务:在本终端前台运行服务器(关闭窗口即停止)
737
- "$NODE" "$ENTRY" >>"$LOG" 2>&1 &
746
+ "$NODE" $SDK_HOOK_ARG"$ENTRY" >>"$LOG" 2>&1 &
738
747
  SERVER_PID=$!
739
748
  trap 'kill "$SERVER_PID" 2>/dev/null' EXIT
740
749
  fi
@@ -786,13 +795,14 @@ LOG=${shQuote(log)}
786
795
  URL=${shQuote(url)}
787
796
  NODE=${shQuote(node)}
788
797
  ENTRY=${shQuote(SERVER_ENTRY)}
798
+ SDK_HOOK_ARG=${HAS_SDK_HOOK ? `${shQuote("--import")} ${shQuote(SDK_HOOK)} ` : ""}
789
799
  UNIT=${shQuote(unitName)}.service
790
800
 
791
801
  if ! curl -sf "$URL/api/health" >/dev/null 2>&1; then
792
802
  systemctl start "$UNIT" 2>/dev/null || true
793
803
  if ! curl -sf "$URL/api/health" >/dev/null 2>&1; then
794
804
  mkdir -p "$(dirname "$LOG")"
795
- "$NODE" "$ENTRY" >>"$LOG" 2>&1 &
805
+ "$NODE" $SDK_HOOK_ARG"$ENTRY" >>"$LOG" 2>&1 &
796
806
  SERVER_PID=$!
797
807
  trap 'kill "$SERVER_PID" 2>/dev/null' EXIT
798
808
  fi
@@ -909,7 +919,7 @@ function buildWinStartPs1(env, cwd, logPath, pidPath) {
909
919
  `$PID | Out-File -Encoding ascii ${psQuote(pidPath)}`,
910
920
  "try {",
911
921
  " while ($true) {",
912
- ` & ${psQuote(realNode())} ${psQuote(SERVER_ENTRY)} *>> ${psQuote(logPath)}`,
922
+ ` & ${psQuote(realNode())} ${HAS_SDK_HOOK ? `--import ${psQuote(SDK_HOOK)} ` : ""}${psQuote(SERVER_ENTRY)} *>> ${psQuote(logPath)}`,
913
923
  " Start-Sleep 10",
914
924
  " }",
915
925
  "} finally {",
@@ -968,7 +978,9 @@ function buildPlist(label, cwd, env) {
968
978
  <key>ProgramArguments</key>
969
979
  <array>
970
980
  <string>${esc(NODE)}</string>
971
- <string>${esc(SERVER_ENTRY)}</string>
981
+ ${
982
+ HAS_SDK_HOOK ? ` <string>--import</string>\n <string>${esc(SDK_HOOK)}</string>\n` : ""
983
+ } <string>${esc(SERVER_ENTRY)}</string>
972
984
  </array>
973
985
 
974
986
  <key>RunAtLoad</key>
@@ -1000,6 +1012,17 @@ function systemdQuote(value) {
1000
1012
  return JSON.stringify(String(value)).replace(/%/g, "%%");
1001
1013
  }
1002
1014
 
1015
+ /**
1016
+ * systemd paths (WorkingDirectory=) must NOT be quoted: unlike Environment= and
1017
+ * ExecStart=, the path directives do not strip surrounding double quotes, so
1018
+ * WorkingDirectory="/home/me/work" makes systemd fail with "path is not absolute".
1019
+ * The whole (trimmed) value is the path, so inner spaces need no escaping —
1020
+ * only % has to be doubled to survive specifier expansion.
1021
+ */
1022
+ function systemdPath(value) {
1023
+ return String(value).replace(/%/g, "%%");
1024
+ }
1025
+
1003
1026
  /** Build the systemd unit file. */
1004
1027
  function buildUnit(cwd, env) {
1005
1028
  const envLines = Object.entries(env)
@@ -1017,9 +1040,9 @@ After=network.target
1017
1040
  [Service]
1018
1041
  Type=simple
1019
1042
  User=${process.env.SUDO_USER ?? userInfo().username}
1020
- WorkingDirectory=${systemdQuote(cwd)}
1043
+ WorkingDirectory=${systemdPath(cwd)}
1021
1044
  ${envLines}
1022
- ${capabilities}ExecStart=${JSON.stringify(NODE)} ${JSON.stringify(SERVER_ENTRY)}
1045
+ ${capabilities}ExecStart=${JSON.stringify(NODE)}${HAS_SDK_HOOK ? ` --import ${JSON.stringify(SDK_HOOK)}` : ""} ${JSON.stringify(SERVER_ENTRY)}
1023
1046
  Restart=always
1024
1047
  RestartSec=5
1025
1048
 
@@ -1147,7 +1170,15 @@ function installSystemd(opts) {
1147
1170
  return;
1148
1171
  }
1149
1172
  // Generated units can contain credentials. install also corrects an existing unit's mode.
1150
- runSystemdRoot("install", ["-m", "600", "/dev/stdin", unitPath], content);
1173
+ // Stage through a temp file: `install /dev/stdin` fails on some hosts (WSL) with
1174
+ // ENXIO when stdin is a pipe rather than a tty.
1175
+ const staged = join(tmpdir(), `pi-web-ui-${name}-${process.pid}.service`);
1176
+ writeFileSync(staged, content, { mode: 0o600 });
1177
+ try {
1178
+ runSystemdRoot("install", ["-m", "600", staged, unitPath]);
1179
+ } finally {
1180
+ rmSync(staged, { force: true });
1181
+ }
1151
1182
  runSystemdRoot("systemctl", ["daemon-reload"]);
1152
1183
  runSystemdRoot("systemctl", ["enable", `${name}.service`]);
1153
1184
  // enable --now does not restart an already-active unit after configuration changes.