pi-webapp 0.1.18 → 0.1.20

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 CHANGED
@@ -55,6 +55,14 @@ Chats run independently: switching workspaces or chats, or creating a new chat,
55
55
 
56
56
  On startup, the app discovers workspaces from Pi's saved sessions, so conversations from other directories appear in the sidebar. Workspace records and attachments stay under Pi's existing `pi-web/` data directory. Removing a workspace from the sidebar does not delete its files or sessions; you can add it again manually.
57
57
 
58
+ ## Optional desktop application
59
+
60
+ The same UI can run in the **pi** Electron desktop application while the browser and `/web` entry points remain available. Desktop packages include Node/Pi, reuse compatible running services, and let you open the same service in a browser or choose whether to keep background tasks running on exit.
61
+
62
+ From source: `npm ci` → `npm run desktop:prepare` → `npm run desktop:start`. Run `npm run desktop:package` to create a host-native installer under `desktop-release/`. Local QA targets macOS arm64; Windows/Linux configurations need verification on their respective systems. Initial packages are unsigned and do not auto-update. Installers include a desktop build number; check **pi → 关于 pi** for the running build and executable location. Quit the old desktop before replacing it; an external compatible Web backend need not be stopped. Finish tasks and stop an owned runtime before replacing that runtime.
63
+
64
+ See [desktop build, testing and release boundaries](docs/desktop.md).
65
+
58
66
  ## Develop locally
59
67
 
60
68
  ```bash
package/README.zh-CN.md CHANGED
@@ -55,6 +55,14 @@ pi
55
55
 
56
56
  启动时会从 Pi 已保存的会话中发现工作区,因此先前在其他目录使用 Pi 的会话也会出现在侧边栏。工作区记录和附件继续存放在 Pi 现有的 `pi-web/` 数据目录下。从侧边栏移除工作区不会删除其文件或会话;移除后如需再次显示,可以手动添加。
57
57
 
58
+ ## 桌面端(可选)
59
+
60
+ 同一套界面也可在名为 **pi** 的 Electron 桌面程序中使用,现有浏览器与 `/web` 方式保持不变。桌面包自带 Node/Pi,支持独立窗口、已有服务复用、在浏览器打开同一服务,以及退出时选择保留或停止后台任务。
61
+
62
+ 源码构建:`npm ci` → `npm run desktop:prepare` → `npm run desktop:start`。运行 `npm run desktop:package` 生成当前系统/架构的安装包,输出到 `desktop-release/`。当前本地验证目标为 macOS arm64;Windows/Linux 打包配置需在对应系统验证。首版安装包未签名,尚无桌面自动更新。安装包文件名包含桌面构建号;“pi → 关于 pi”显示实际运行的构建号和应用位置。替换前先退出旧桌面,外部兼容 Web 后台无需停止;若替换桌面自有运行时,请先结束任务并停止该自有后台。
63
+
64
+ 详细说明见 [桌面端构建、测试与发布边界](docs/desktop.md)。
65
+
58
66
  ## 本地开发
59
67
 
60
68
  ```bash
package/bin/pi-webapp.mjs CHANGED
@@ -11,6 +11,9 @@ import { openWebPage } from "./open-web-page.mjs";
11
11
  import { npmCommand } from "./npm-command.mjs";
12
12
 
13
13
  const minimum = [0, 87, 1];
14
+ const packageVersion = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8")).version;
15
+ const runtimeKind = process.env.PI_WEBAPP_RUNTIME === "desktop" ? "desktop" : "npm";
16
+ const runtimeInfo = { version: packageVersion, bridgeVersion: 1, runtimeKind };
14
17
  const launcher = fileURLToPath(import.meta.url);
15
18
  const extension = fileURLToPath(new URL("../dist/extension.js", import.meta.url));
16
19
  const agentDir = resolve((process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent")).replace(/^~(?=$|[\\/])/, homedir()));
@@ -109,7 +112,7 @@ async function serve(pi, args) {
109
112
  try {
110
113
  lock = await open(statePath, "wx", 0o600);
111
114
  claimed = true;
112
- await lock.writeFile(JSON.stringify({ pid: process.pid, nonce, cwd: process.cwd(), starting: true }));
115
+ await lock.writeFile(JSON.stringify({ pid: process.pid, nonce, cwd: process.cwd(), starting: true, ...runtimeInfo }));
113
116
  } catch (error) {
114
117
  if (claimed) await unlink(statePath).catch(() => {});
115
118
  notifyParent({ type: "error", message: error?.code === "EEXIST" ? "pi-webapp 已在启动或运行" : String(error) });
@@ -117,7 +120,8 @@ async function serve(pi, args) {
117
120
  } finally { await lock?.close().catch(() => {}); }
118
121
 
119
122
  const pluginRoots = (process.env.PI_WEBAPP_PLUGIN_DEV_ROOTS ?? "").split(delimiter).filter(Boolean).map((root) => resolve(root));
120
- const child = spawn(pi, ["--mode", "rpc", "-e", extension, ...pluginRoots.flatMap((root) => ["-e", root]), ...args], {
123
+ const piPrefix = process.env.PI_WEBAPP_PI_CLI ? [resolve(process.env.PI_WEBAPP_PI_CLI)] : [];
124
+ const child = spawn(pi, [...piPrefix, "--mode", "rpc", "-e", extension, ...pluginRoots.flatMap((root) => ["-e", root]), ...args], {
121
125
  cwd: process.cwd(), env: { ...process.env, PI_WEBAPP_AUTO_OPEN: "0", PI_WEBAPP_LAUNCHER_NONCE: nonce }, stdio: ["pipe", "pipe", "pipe"],
122
126
  });
123
127
  child.stderr?.pipe(process.stderr, { end: false });
@@ -173,7 +177,7 @@ async function serve(pi, args) {
173
177
  try {
174
178
  const request = JSON.parse(await readFile(restartPath, "utf8"));
175
179
  const prepared = JSON.parse(await readFile(updatePath, "utf8"));
176
- if (request.nonce === nonce && request.launcher === prepared.launcher && existsSync(request.launcher)) {
180
+ if (runtimeKind !== "desktop" && request.nonce === nonce && request.launcher === prepared.launcher && existsSync(request.launcher)) {
177
181
  restartRequest = request;
178
182
  shutdown("restart_request");
179
183
  }
@@ -222,7 +226,7 @@ async function serve(pi, args) {
222
226
  if (url.hostname !== "127.0.0.1" || !url.hash) continue;
223
227
  runningUrl = url.href;
224
228
  opening = true;
225
- stateWrite = saveState({ pid: process.pid, piPid: child.pid, nonce, cwd: process.cwd(), url: url.href })
229
+ stateWrite = saveState({ pid: process.pid, piPid: child.pid, nonce, cwd: process.cwd(), url: url.href, ...runtimeInfo })
226
230
  .then(() => {
227
231
  if (closed || failed) return;
228
232
  ready = true;
@@ -331,7 +335,7 @@ async function start(args) {
331
335
  if (!nodeVersion || !compatible(nodeVersion, [22, 19, 0])) throw new Error("需要 Node.js 22.19 或更新版本。");
332
336
  if (!existsSync(extension)) throw new Error("找不到构建后的扩展。请先运行 npm run build。");
333
337
  if (args.some((arg) => ["--mode", "--print", "-p"].includes(arg))) throw new Error("后台启动不支持覆盖 Pi 的运行模式");
334
- const pi = probe("pi") ?? installPi();
338
+ const pi = process.env.PI_WEBAPP_PI_CLI ? process.execPath : probe("pi") ?? installPi();
335
339
  await mkdir(dirname(statePath), { recursive: true });
336
340
  const logFd = openSync(logPath, "a", 0o600);
337
341
  let worker;