pi-webapp 0.1.4 → 0.1.6

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
@@ -16,6 +16,8 @@ npx pi-webapp
16
16
 
17
17
  This starts Pi in the background and opens the default browser on a local graphical desktop, leaving the terminal free. If Pi is missing, the launcher installs the official `@earendil-works/pi-coding-agent` package with npm. An existing Pi installation must be version 0.87.1 or later. The launcher does not replace an older installation automatically. Running the launcher again reopens the existing server. Remote or headless sessions print the address instead; set `PI_WEBAPP_AUTO_OPEN=0` to disable automatic opening.
18
18
 
19
+ In Settings → General → App updates, the web page checks npm for a newer `pi-webapp` release. When started by the background launcher from an npm package, **Update and restart** installs the verified release under Pi's `pi-web/releases/` data directory, restarts the server, and reloads the same browser address. Wait for any active reply to finish first. If the new version cannot start, the launcher restores the previous service. This works for both `npx` and global-command launches; later launches select the managed release automatically. It does not change the npm global installation. Source checkouts and `/web` pages opened from an existing Pi terminal show version information but cannot restart from the web page. If the service does not return, run `pi-webapp status` and check `~/.pi/agent/pi-web/launcher.log`.
20
+
19
21
  Use `npx pi-webapp status` to show the address and `npx pi-webapp stop` to stop the background service. After a global install, use `pi-webapp status` and `pi-webapp stop`.
20
22
 
21
23
  You can also install the launcher globally:
@@ -66,7 +68,7 @@ The source lives in `extension/`, `shared/`, and `web/src/`. The npm package con
66
68
 
67
69
  `npm publish` runs the checks, tests, and production build. The `pi-package` keyword makes the published npm package eligible for the [Pi package catalog](https://pi.dev/packages); catalog updates may lag behind npm.
68
70
 
69
- GitHub Actions runs `check`, `test`, and `build` on pull requests to `main`. A push to `main` repeats those checks and then publishes the next patch version to npm. The release version is selected from the greater of the source version and the npm `latest` version; it is set in the package during CI and is not committed back to Git. To start a new minor or major series, raise the version in `package.json` and `package-lock.json` in the pull request. Each release also updates the package's `pi.image` URL to its own version.
71
+ GitHub Actions runs `check`, `test`, and `build` on pull requests to `main`. A push to `main` repeats those checks, chooses the next patch version from the source version and npm `latest`, then commits the updated `package.json` and `package-lock.json` to `main` before publishing. The release commit uses the workflow's `GITHUB_TOKEN` and does not start another workflow run. To start a new minor or major series, raise the version in both package files in the pull request. Each release also updates the package's `pi.image` URL to its own version. The repository must allow the workflow to push to `main` for publishing to proceed.
70
72
 
71
73
  Before the first automated release, configure npm Trusted Publishing for the `pi-webapp` package: GitHub owner `chengzhiyi`, repository `pi-webapp`, workflow filename `ci.yml`, no environment, and allow direct `npm publish`. This workflow uses GitHub's OIDC identity and does not need an npm token. Keep the package's repository URL in `package.json` aligned with the GitHub repository.
72
74
 
package/README.zh-CN.md CHANGED
@@ -16,6 +16,8 @@ npx pi-webapp
16
16
 
17
17
  该命令会在后台启动 Pi,并在本地图形桌面用默认浏览器打开网页,终端可继续使用。若未找到 Pi,会自动通过 npm 安装官方 `@earendil-works/pi-coding-agent`。已有 Pi 需要 0.87.1 或更新版本;启动器不会自动覆盖旧版本。再次运行启动命令会重新打开已有服务。远程或无图形界面时只显示地址;设置 `PI_WEBAPP_AUTO_OPEN=0` 可关闭自动打开。
18
18
 
19
+ 网页的「设置 → 通用设置 → 应用更新」会检查 npm 上的 `pi-webapp` 新版本。通过 npm 包的后台启动器运行时,点击「升级并重启」会先将新版安装并校验到 Pi 数据目录的 `pi-web/releases/`,然后重启服务,网页会在原地址重新加载。请先等待当前回复结束。若新版无法启动,启动器会恢复旧版服务。`npx` 和全局命令启动均可使用;后续启动会自动选用已安装的新版。此操作不会改动 npm 全局安装。源码目录运行和已有 Pi 终端中的 `/web` 页面可以查看版本,但不能从网页重启。若服务未恢复,可运行 `pi-webapp status` 并查看 `~/.pi/agent/pi-web/launcher.log`。
20
+
19
21
  可用 `npx pi-webapp status` 查看地址,用 `npx pi-webapp stop` 停止后台服务。全局安装后可直接运行 `pi-webapp status` 和 `pi-webapp stop`。
20
22
 
21
23
  也可以将启动器安装为全局命令:
@@ -66,7 +68,7 @@ npm run stop
66
68
 
67
69
  `npm publish` 会运行检查、测试和生产构建。`pi-package` 关键词使发布到 npm 的软件包有资格进入 [Pi 软件包目录](https://pi.dev/packages);目录更新可能晚于 npm。
68
70
 
69
- GitHub Actions 会在针对 `main` 的拉取请求中运行 `check`、`test` 和 `build`。代码推送到 `main` 后会再次运行这些检查,全部通过才自动发布下一个补丁版本。发布版本根据源码版本和 npm `latest` 版本确定,仅在 CI 中写入包文件,不会提交回 Git。如需开始新的次版本或主版本,请在拉取请求中同步提高 `package.json` 和 `package-lock.json` 的版本。每次发布也会将包内 `pi.image` 的地址更新为该版本。
71
+ GitHub Actions 会在针对 `main` 的拉取请求中运行 `check`、`test` 和 `build`。代码推送到 `main` 后会再次运行这些检查,根据源码版本和 npm `latest` 版本确定下一个补丁版本,先将更新后的 `package.json` 和 `package-lock.json` 提交回 `main`,再发布到 npm。版本同步提交使用工作流的 `GITHUB_TOKEN`,不会再次触发工作流。如需开始新的次版本或主版本,请在拉取请求中同步提高两个包文件的版本。每次发布也会将包内 `pi.image` 的地址更新为该版本。仓库必须允许工作流推送到 `main`,发布才能继续。
70
72
 
71
73
  首次自动发布前,请在 npm 的 `pi-webapp` 包设置中配置 Trusted Publishing:GitHub 所有者为 `chengzhiyi`,仓库为 `pi-webapp`,工作流文件名为 `ci.yml`,不设置环境,并允许直接执行 `npm publish`。工作流通过 GitHub OIDC 认证,无需 npm token。`package.json` 中的仓库地址应与 GitHub 仓库保持一致。
72
74
 
package/bin/pi-webapp.mjs CHANGED
@@ -15,6 +15,8 @@ const extension = fileURLToPath(new URL("../dist/extension.js", import.meta.url)
15
15
  const agentDir = resolve((process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent")).replace(/^~(?=$|[\\/])/, homedir()));
16
16
  const statePath = join(agentDir, "pi-web", "launcher.json");
17
17
  const stopPath = join(agentDir, "pi-web", "launcher.stop");
18
+ const restartPath = join(agentDir, "pi-web", "launcher.restart");
19
+ const updatePath = join(agentDir, "pi-web", "update.json");
18
20
  const logPath = join(agentDir, "pi-web", "launcher.log");
19
21
 
20
22
  function versionParts(value) {
@@ -112,7 +114,7 @@ async function serve(pi, args) {
112
114
  } finally { await lock?.close().catch(() => {}); }
113
115
 
114
116
  const child = spawn(pi, ["--mode", "rpc", "-e", extension, ...args], {
115
- cwd: process.cwd(), env: { ...process.env, PI_WEBAPP_AUTO_OPEN: "0" }, stdio: ["pipe", "pipe", "pipe"],
117
+ cwd: process.cwd(), env: { ...process.env, PI_WEBAPP_AUTO_OPEN: "0", PI_WEBAPP_LAUNCHER_NONCE: nonce }, stdio: ["pipe", "pipe", "pipe"],
116
118
  });
117
119
  child.stderr?.pipe(process.stderr, { end: false });
118
120
  let ready = false;
@@ -121,6 +123,8 @@ async function serve(pi, args) {
121
123
  let closed = false;
122
124
  let stateWrite = Promise.resolve();
123
125
  let stopping = false;
126
+ let restartRequest = null;
127
+ let runningUrl = null;
124
128
  let buffer = "";
125
129
  let killTimer;
126
130
  const shutdown = () => {
@@ -140,6 +144,15 @@ async function serve(pi, args) {
140
144
  const stopTimer = setInterval(async () => {
141
145
  try { if ((await readFile(stopPath, "utf8")).trim() === nonce) shutdown(); }
142
146
  catch { /* No stop request. */ }
147
+ if (stopping) return;
148
+ try {
149
+ const request = JSON.parse(await readFile(restartPath, "utf8"));
150
+ const prepared = JSON.parse(await readFile(updatePath, "utf8"));
151
+ if (request.nonce === nonce && request.launcher === prepared.launcher && existsSync(request.launcher)) {
152
+ restartRequest = request;
153
+ shutdown();
154
+ }
155
+ } catch { /* No valid restart request. */ }
143
156
  }, 400);
144
157
  process.on("SIGTERM", shutdown);
145
158
  process.on("SIGINT", shutdown);
@@ -175,6 +188,7 @@ async function serve(pi, args) {
175
188
  try { url = new URL(record.message.slice("pi-webapp: ".length)); }
176
189
  catch { continue; }
177
190
  if (url.hostname !== "127.0.0.1" || !url.hash) continue;
191
+ runningUrl = url.href;
178
192
  opening = true;
179
193
  stateWrite = saveState({ pid: process.pid, piPid: child.pid, nonce, cwd: process.cwd(), url: url.href })
180
194
  .then(() => {
@@ -206,6 +220,42 @@ async function serve(pi, args) {
206
220
  try { if (JSON.parse(await readFile(statePath, "utf8")).nonce === nonce) await unlink(statePath); }
207
221
  catch { /* Already removed. */ }
208
222
  }
223
+ if (restartRequest && runningUrl) {
224
+ await unlink(restartPath).catch(() => {});
225
+ const oldUrl = new URL(runningUrl);
226
+ const fd = openSync(logPath, "a", 0o600);
227
+ const restartEnv = { ...process.env, PI_WEBAPP_AUTO_OPEN: "0", PI_WEBAPP_RESTART_PORT: oldUrl.port, PI_WEBAPP_RESTART_TOKEN: oldUrl.hash.slice(1) };
228
+ try {
229
+ const runLauncher = (path) => new Promise((resolveExit) => {
230
+ const next = spawnNode(process.execPath, [path, "start", ...args], { cwd: process.cwd(), env: restartEnv, stdio: ["ignore", fd, fd] });
231
+ next.once("error", () => resolveExit(1));
232
+ next.once("exit", (code) => resolveExit(code ?? 1));
233
+ });
234
+ if (await runLauncher(restartRequest.launcher) !== 0) {
235
+ await clearManagedLauncher(restartRequest.launcher);
236
+ console.error("pi-webapp: 新版启动失败,正在恢复旧版服务");
237
+ if (await runLauncher(launcher) !== 0) console.error(`pi-webapp: 旧版恢复失败。日志:${logPath}`);
238
+ }
239
+ } finally { closeSync(fd); }
240
+ }
241
+ }
242
+
243
+ async function clearManagedLauncher(path) {
244
+ try {
245
+ if (JSON.parse(await readFile(updatePath, "utf8")).launcher === path) await unlink(updatePath);
246
+ } catch { /* No matching managed release. */ }
247
+ }
248
+
249
+ async function managedLauncher() {
250
+ try {
251
+ if (existsSync(fileURLToPath(new URL("../.git", import.meta.url)))) return null;
252
+ const update = JSON.parse(await readFile(updatePath, "utf8"));
253
+ const current = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
254
+ if (!/^\d+\.\d+\.\d+$/.test(update.version) || !/^\d+\.\d+\.\d+$/.test(current.version)) return null;
255
+ if (!compatible(versionParts(update.version), versionParts(current.version))) return null;
256
+ if (update.version === current.version || !existsSync(update.launcher)) return null;
257
+ return update.launcher;
258
+ } catch { return null; }
209
259
  }
210
260
 
211
261
  async function start(args) {
@@ -215,6 +265,17 @@ async function start(args) {
215
265
  if (existing.url) await openPageIfLocal(existing.url);
216
266
  return;
217
267
  }
268
+ const managed = await managedLauncher();
269
+ if (managed) {
270
+ const next = spawnNode(process.execPath, [managed, "start", ...args], { cwd: process.cwd(), env: process.env, stdio: "inherit" });
271
+ const code = await new Promise((resolveExit, rejectExit) => { next.once("error", rejectExit); next.once("exit", (status) => resolveExit(status)); });
272
+ if (code !== 0) {
273
+ await clearManagedLauncher(managed);
274
+ console.error(`pi-webapp: 新版启动失败(代码 ${code ?? "未知"}),正在恢复当前安装`);
275
+ await start(args);
276
+ }
277
+ return;
278
+ }
218
279
  const nodeVersion = versionParts(process.versions.node);
219
280
  if (!nodeVersion || !compatible(nodeVersion, [22, 19, 0])) throw new Error("需要 Node.js 22.19 或更新版本。");
220
281
  if (!existsSync(extension)) throw new Error("找不到构建后的扩展。请先运行 npm run build。");
package/dist/extension.js CHANGED
@@ -1,7 +1,7 @@
1
- import{dirname as Xt,join as K,resolve as Qt}from"node:path";import{realpath as O}from"node:fs/promises";import{fileURLToPath as Zt}from"node:url";import{DefaultPackageManager as Me,getAgentDir as T,ModelRuntime as qt,SessionManager as Ae,SettingsManager as ei}from"@earendil-works/pi-coding-agent";import{getSupportedThinkingLevels as ti}from"@earendil-works/pi-ai";import{randomBytes as Et,timingSafeEqual as Ct}from"node:crypto";import{readFile as Mt}from"node:fs/promises";import{createServer as At}from"node:http";import{join as Tt}from"node:path";import{mkdir as je,opendir as Re,stat as Ve}from"node:fs/promises";import{homedir as _e}from"node:os";import{basename as Be,dirname as Y,join as X,posix as Oe,resolve as Q,win32 as Ue}from"node:path";function Z(t){return process.platform==="win32"?Ue.isAbsolute(t)&&/^(?:[A-Za-z]:[\\/]|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)/.test(t):Oe.isAbsolute(t)}function De(t,e,i){if(t.length===i&&e.name.localeCompare(t[t.length-1].name)>=0)return!0;let r=0,n=t.length;for(;r<n;){let p=r+n>>>1;e.name.localeCompare(t[p].name)<0?n=p:r=p+1}return t.splice(r,0,e),t.length<=i?!1:(t.pop(),!0)}async function q(t){if(t!==void 0&&!Z(t))throw new Error("\u76EE\u5F55\u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");let e=_e(),i=Q(t??e),r=[];for(let a=i;;a=Y(a)){let u=Y(a);if(r.unshift({name:u===a?a:Be(a),path:a,hidden:!1}),u===a)break}let n=[],p=!1,o=await Re(i);for await(let a of o)!a.isDirectory()&&!a.isSymbolicLink()||De(n,{name:a.name,isDirectory:a.isDirectory(),isSymbolicLink:a.isSymbolicLink()},1001)&&(p=!0);let l=[];for(let a of n){let u=X(i,a.name);if(!a.isDirectory&&a.isSymbolicLink)try{if(!(await Ve(u)).isDirectory())continue}catch{continue}if(l.length===1e3){p=!0;break}l.push({name:a.name,path:u,hidden:a.name.startsWith(".")})}return{path:i,home:e,crumbs:r,entries:l,truncated:p}}async function ee(t,e){if(!Z(t))throw new Error("\u7236\u76EE\u5F55\u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");if(!e.trim()||e==="."||e===".."||/[/\\]/.test(e))throw new Error("\u6587\u4EF6\u5939\u540D\u79F0\u65E0\u6548");let i=X(Q(t),e);return await je(i),i}import{execFile as $e}from"node:child_process";import{accessSync as Fe,constants as He}from"node:fs";import{delimiter as Je,join as Ge}from"node:path";var Ke=(t,e,i)=>new Promise((r,n)=>{$e(t,[...e],{encoding:"utf8",signal:i,windowsHide:!0},(p,o,l)=>{p?n(Object.assign(new Error(p.message,{cause:p}),{code:p.code,stdout:o,stderr:l})):r({stdout:o,stderr:l})})});function ze(t){for(let e of(t??"").split(Je))if(e)for(let i of["zenity","kdialog"])try{return Fe(Ge(e,i),He.X_OK),!0}catch{}return!1}function te(t=process.platform,e=process.env){return e.SSH_CONNECTION||e.SSH_TTY?"browse":t==="darwin"||t==="linux"&&(e.DISPLAY||e.WAYLAND_DISPLAY)&&ze(e.PATH)?"native":"browse"}function L(t){if(!(typeof t!="object"||t===null||!("code"in t)))return typeof t.code=="string"||typeof t.code=="number"?t.code:void 0}function Ye(t){return typeof t=="object"&&t!==null&&"stderr"in t&&typeof t.stderr=="string"?t.stderr:""}function U(t){return t.replace(/[\r\n]+$/,"")||null}async function ie(t,e=process.platform,i=Ke){if(e==="darwin")try{let r=await i("osascript",["-e",'set selectedFolder to choose folder with prompt "Select Workspace Directory"',"-e","POSIX path of selectedFolder"],t);return U(r.stdout)}catch(r){if(!t.aborted&&L(r)===1&&/(?:User canceled|-128)/i.test(Ye(r)))return null;throw r}if(e==="linux"){try{return U((await i("zenity",["--file-selection","--directory","--title=Select Workspace Directory"],t)).stdout)}catch(r){if(t.aborted)throw r;if(L(r)===1)return null;if(L(r)!=="ENOENT")throw r}try{return U((await i("kdialog",["--getexistingdirectory",".","--title","Select Workspace Directory"],t)).stdout)}catch(r){if(t.aborted)throw r;if(L(r)===1)return null;throw r}}throw new Error(`\u5F53\u524D\u7CFB\u7EDF\u4E0D\u652F\u6301\u672C\u5730\u76EE\u5F55\u9009\u62E9\u5668\uFF1A${e}`)}import{createHash as Xe,randomUUID as Qe}from"node:crypto";import{mkdir as Ze,readFile as qe,writeFile as et}from"node:fs/promises";import{join as re}from"node:path";import{getAgentDir as tt}from"@earendil-works/pi-coding-agent";var N=20*1024*1024,se=20,it=/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/iu;function rt(t){let e=t.indexOf("."),i=(e<0?t:t.slice(0,e)).replace(/[. ]+$/u,"");return it.test(i)}function st(t,e){let i=0,r="";for(let n of Buffer.from(t).toString("utf8")){let p=Buffer.byteLength(n);if(i+p>e)break;r+=n,i+=p}return r}function nt(t){if(t===void 0)return"file";let i=t.slice(Math.max(t.lastIndexOf("/"),t.lastIndexOf("\\"))+1).replace(/[\u0000-\u001f\u007f]/g,"").replace(/[<>:"|?*]/g,"_").trim().replace(/[. ]+$/u,"");return rt(i)&&(i=`_${i}`),i=st(i,255).replace(/[. ]+$/u,""),i===""||i==="."||i===".."?"file":i}function ot(t){return t.length>=8&&t.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))?"image/png":t.length>=3&&t.subarray(0,3).equals(Buffer.from([255,216,255]))?"image/jpeg":t.length>=6&&["GIF87a","GIF89a"].includes(t.toString("ascii",0,6))?"image/gif":t.length>=12&&t.toString("ascii",0,4)==="RIFF"&&t.toString("ascii",8,12)==="WEBP"?"image/webp":null}async function ne(t,e,i,r){if(r.byteLength===0||r.byteLength>N)throw new Error("\u6587\u4EF6\u4E3A\u7A7A\u6216\u8D85\u8FC7 20 MB");let n=nt(e),p=Xe("sha256").update(r).digest("hex"),o=re(tt(),"pi-web","attachments",p);await Ze(o,{recursive:!0});let l=re(o,n);try{await et(l,r,{flag:"wx",mode:384})}catch(a){if(!(a instanceof Error&&"code"in a&&a.code==="EEXIST"))throw a}return{id:Qe(),name:n,bytes:r.byteLength,path:l,mediaType:ot(r)??i.replace(/^image\//,"application/"),sessionId:t}}async function oe(t){return{type:"image",data:(await qe(t.path)).toString("base64"),mimeType:t.mediaType}}import{randomBytes as at}from"node:crypto";function ae(t,e,i){return t!=="openai-codex"||e!=="oauth"?i:{...i,prompt:r=>r.type==="select"&&r.options.some(n=>n.id==="browser")&&r.options.some(n=>n.id==="device_code")?Promise.resolve("browser"):i.prompt(r)}}var j=class{flows=new Map;start(e,i,r,n){let p=this.getActive();if(p){if(p.providerId===e&&p.method===i&&n===void 0)return p;throw new Error("\u5176\u4ED6\u63D0\u4F9B\u65B9\u7684\u767B\u5F55\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u5148\u5B8C\u6210\u6216\u53D6\u6D88\u5F53\u524D\u767B\u5F55")}this.flows.clear();let o=at(18).toString("base64url"),l={view:{id:o,providerId:e,method:i,status:"running"},controller:new AbortController};return this.flows.set(o,l),r(e,i,{signal:l.controller.signal,notify:a=>{l.view={...l.view,event:a,...a.type==="auth_url"||a.type==="device_code"?{authorization:a}:{}}},prompt:a=>{if(a.type==="secret"&&n!==void 0){let u=n;return n=void 0,Promise.resolve(u)}return new Promise((u,h)=>{let{signal:g,...c}=a;l.answer=u,l.reject=h,l.view={...l.view,status:"waiting",prompt:c},g?.aborted?h(new Error("\u767B\u5F55\u8F93\u5165\u5DF2\u53D6\u6D88")):g?.addEventListener("abort",()=>h(new Error("\u767B\u5F55\u8F93\u5165\u5DF2\u53D6\u6D88")),{once:!0})})}}).then(()=>{l.view.status!=="cancelled"&&(l.view={id:o,providerId:e,method:i,status:"done"})}).catch(()=>{l.view.status!=="cancelled"&&(l.view={id:o,providerId:e,method:i,status:"error",error:"\u767B\u5F55\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u63D0\u4F9B\u65B9\u63D0\u793A\u5E76\u91CD\u8BD5\u3002"})}),{...l.view}}get(e){let i=this.flows.get(e)?.view;return i?{...i}:void 0}getActive(){let e=[...this.flows.values()].find(({view:i})=>i.status==="running"||i.status==="waiting")?.view;return e?{...e}:void 0}respond(e,i){let r=this.flows.get(e);if(!r?.answer||r.view.status!=="waiting"||r.view.prompt?.type==="select"&&!r.view.prompt.options.some(p=>p.id===i))return!1;let n=r.answer;return r.answer=void 0,r.reject=void 0,r.view={...r.view,status:"running",prompt:void 0},n(i),!0}cancel(e){let i=this.flows.get(e);return!i||i.view.status==="done"||i.view.status==="error"?!1:(i.view={...i.view,status:"cancelled",prompt:void 0},i.controller.abort(),i.reject?.(new Error("\u767B\u5F55\u5DF2\u53D6\u6D88")),i.answer=void 0,i.reject=void 0,!0)}close(){for(let e of this.flows.keys())this.cancel(e);this.flows.clear()}};import{randomBytes as dt}from"node:crypto";import{readFile as lt,rename as ct,rm as pt,stat as ut,writeFile as mt,mkdir as ft}from"node:fs/promises";import{dirname as gt}from"node:path";var wt=["openai-completions","openai-responses","anthropic-messages"];function D(t){if(!t||typeof t!="object")return!1;let e=t;if(typeof e.id!="string"||!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(e.id)||typeof e.name!="string"||!e.name.trim()||e.name.length>100||typeof e.modelId!="string"||!e.modelId.trim()||e.modelId.length>200||!wt.includes(e.api)||typeof e.baseUrl!="string"||e.baseUrl.length>2048)return!1;try{let i=new URL(e.baseUrl);return(i.protocol==="https:"||i.protocol==="http:")&&!!i.hostname&&!i.username&&!i.password&&!i.hash}catch{return!1}}async function de(t,e){if(!D(e))throw new Error("\u81EA\u5B9A\u4E49\u63D0\u4F9B\u65B9\u540D\u79F0\u3001\u5730\u5740\u6216\u6A21\u578B\u65E0\u6548");let i;try{i=await lt(t,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let r;if(i===void 0)r={providers:{}};else{try{r=JSON.parse(i)}catch{throw new Error("models.json \u542B\u6CE8\u91CA\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u624B\u52A8\u6DFB\u52A0\u63D0\u4F9B\u65B9")}if(!r||typeof r!="object"||!r.providers||typeof r.providers!="object"||Array.isArray(r.providers))throw new Error("models.json \u7ED3\u6784\u65E0\u6548")}if(Object.hasOwn(r.providers,e.id))throw new Error("\u63D0\u4F9B\u65B9\u5DF2\u5B58\u5728");let n={...r,providers:{...r.providers,[e.id]:{name:e.name.trim(),baseUrl:e.baseUrl,api:e.api,models:[{id:e.modelId.trim()}]}}};await ft(gt(t),{recursive:!0});let p=i===void 0?384:(await ut(t)).mode&511,o=`${t}.pi-web-${dt(6).toString("hex")}.tmp`;try{await mt(o,`${JSON.stringify(n,null,2)}
2
- `,{mode:p,flag:"wx"}),await ct(o,t)}finally{await pt(o,{force:!0})}}import{randomBytes as ht}from"node:crypto";import{mkdir as yt,readFile as vt,rename as kt,rm as bt,stat as Pt,writeFile as St}from"node:fs/promises";import{dirname as xt}from"node:path";function le(t){if(!t||t.length>2048)return!1;try{let e=new URL(t);return(e.protocol==="https:"||e.protocol==="http:")&&!!e.hostname&&!e.username&&!e.password&&!e.search&&!e.hash}catch{return!1}}function ce(t){if(!t||typeof t!="object")return!1;let e=t;return e.action==="save"?(e.originalId===void 0||typeof e.originalId=="string")&&pe(e.model):e.action==="base_url"?e.baseUrl===null||typeof e.baseUrl=="string"&&le(e.baseUrl):e.action==="remove"&&typeof e.id=="string"&&e.id.length>0&&e.id.length<=200}function pe(t){if(!t||typeof t!="object")return!1;let e=t;if(typeof e.id!="string"||!e.id.trim()||e.id.length>200||e.name!==void 0&&(typeof e.name!="string"||!e.name.trim()||e.name.length>200))return!1;for(let i of[e.contextWindow,e.maxTokens])if(i!==void 0&&(!Number.isSafeInteger(i)||i<=0))return!1;return e.input===void 0||Array.isArray(e.input)&&e.input.length>0&&e.input.every(i=>i==="text"||i==="image")&&new Set(e.input).size===e.input.length}async function ue(t){let e;try{e=await vt(t,"utf8")}catch(r){if(r.code!=="ENOENT")throw r}if(e===void 0)return{config:{providers:{}}};let i;try{i=JSON.parse(e)}catch{throw new Error("models.json \u542B\u6CE8\u91CA\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u624B\u52A8\u4FEE\u6539")}if(!i||typeof i!="object"||!i.providers||typeof i.providers!="object"||Array.isArray(i.providers))throw new Error("models.json \u7ED3\u6784\u65E0\u6548");return{config:i,existing:e}}async function It(t,e,i){await yt(xt(t),{recursive:!0});let r=i===void 0?384:(await Pt(t)).mode&511,n=`${t}.pi-web-${ht(6).toString("hex")}.tmp`;try{await St(n,`${JSON.stringify(e,null,2)}
3
- `,{mode:r,flag:"wx"}),await kt(n,t)}finally{await bt(n,{force:!0})}}async function me(t,e,i){if(!i.getProvider(e))throw new Error("\u63D0\u4F9B\u65B9\u4E0D\u5B58\u5728");let{config:r}=await ue(t),n=r.providers[e],p=new Set(n?.models?.map(o=>o.id)??[]);return{providerId:e,baseUrl:n?.baseUrl,models:i.getModels(e).map(o=>({id:o.id,name:o.name,contextWindow:o.contextWindow,maxTokens:o.maxTokens,input:[...o.input],source:p.has(o.id)?"custom":"builtin",overridden:!!n?.modelOverrides?.[o.id]}))}}async function fe(t,e,i,r){if(!r.getProvider(e))throw new Error("\u63D0\u4F9B\u65B9\u4E0D\u5B58\u5728");let{config:n,existing:p}=await ue(t),o={...n.providers[e]??{}},l=[...o.models??[]],a={...o.modelOverrides??{}};if(i.action==="base_url"){if(i.baseUrl!==null&&!le(i.baseUrl))throw new Error("API \u5730\u5740\u65E0\u6548");i.baseUrl===null?delete o.baseUrl:o.baseUrl=i.baseUrl}else if(i.action==="save"){if(!pe(i.model))throw new Error("\u6A21\u578B\u53C2\u6570\u65E0\u6548");let u={id:i.model.id.trim(),...i.model.name===void 0?{}:{name:i.model.name},...i.model.contextWindow===void 0?{}:{contextWindow:i.model.contextWindow},...i.model.maxTokens===void 0?{}:{maxTokens:i.model.maxTokens},...i.model.input===void 0?{}:{input:i.model.input}},h=i.originalId,g=h===void 0?-1:l.findIndex(c=>c.id===h);if(h===void 0&&(l.some(c=>c.id===u.id)||r.getModel(e,u.id)))throw new Error("\u6A21\u578B ID \u5DF2\u5B58\u5728");if(g>=0){if(u.id!==h&&(l.some(f=>f.id===u.id)||r.getModel(e,u.id)))throw new Error("\u6A21\u578B ID \u5DF2\u5B58\u5728");let c={...l[g],...u};for(let f of["name","contextWindow","maxTokens","input"])u[f]===void 0&&delete c[f];l[g]=c}else if(h!==void 0&&r.getModel(e,h)){if(u.id!==h)throw new Error("\u5185\u7F6E\u6A21\u578B ID \u4E0D\u53EF\u4FEE\u6539");let{id:c,...f}=u;a[h]={...a[h],...f};for(let C of["name","contextWindow","maxTokens","input"])u[C]===void 0&&delete a[h][C]}else{if(h!==void 0)throw new Error("\u6A21\u578B\u4E0D\u5B58\u5728");l.push(u)}}else{let u=l.findIndex(h=>h.id===i.id);if(u>=0)l.splice(u,1);else if(Object.hasOwn(a,i.id))delete a[i.id];else throw new Error("\u53EA\u80FD\u79FB\u9664\u81EA\u5B9A\u4E49\u6A21\u578B\u6216\u6062\u590D\u5DF2\u4FEE\u6539\u7684\u5185\u7F6E\u6A21\u578B")}l.length?o.models=l:delete o.models,Object.keys(a).length?o.modelOverrides=a:delete o.modelOverrides,Object.keys(o).length?n.providers[e]=o:delete n.providers[e],await It(t,n,p)}function ge(t){return/^(?:npm:|git:|https?:\/\/|ssh:\/\/|git@)/i.test(t.trim())}var Wt={"/":{path:"index.html",type:"text/html; charset=utf-8"},"/assets/app.js":{path:"assets/app.js",type:"text/javascript; charset=utf-8"},"/assets/style.css":{path:"assets/style.css",type:"text/css; charset=utf-8"}};function m(t,e,i){t.writeHead(e,{"Content-Type":"application/json; charset=utf-8","Cache-Control":"no-store"}),t.end(JSON.stringify(i))}function Lt(t,e){let i=t.headers.authorization;if(typeof i!="string"||!i.startsWith("Bearer "))return!1;let r=Buffer.from(i.slice(7),"utf8");return r.length===e.length&&Ct(r,e)}async function Nt(t){let e=0,i=[];for await(let r of t){let n=Buffer.isBuffer(r)?r:Buffer.from(r);if(e+=n.length,e>64*1024)throw new Error("\u8BF7\u6C42\u5185\u5BB9\u8FC7\u5927");i.push(n)}return JSON.parse(Buffer.concat(i).toString("utf8"))}async function jt(t){let e=0,i=[];for await(let r of t){let n=Buffer.isBuffer(r)?r:Buffer.from(r);if(e+=n.length,e>N)throw new Error("\u6587\u4EF6\u8D85\u8FC7 20 MB");i.push(n)}return Buffer.concat(i)}async function Rt(t){await new Promise((i,r)=>{t.once("error",r),t.listen(0,"127.0.0.1",()=>{t.off("error",r),i()})});let e=t.address();if(e===null||typeof e=="string")throw new Error("\u65E0\u6CD5\u786E\u5B9A\u76D1\u542C\u7AEF\u53E3");return e.port}async function we(t,e){let i=Et(24).toString("base64url"),r=Buffer.from(i),n=te(),p=new Set,o=new Map,l=new j,a="",u=At(async(g,c)=>{c.setHeader("X-Content-Type-Options","nosniff"),c.setHeader("Referrer-Policy","no-referrer"),c.setHeader("Content-Security-Policy","default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data: blob:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'");let f;try{f=new URL(g.url??"/",a).pathname}catch{m(c,400,{error:"\u8BF7\u6C42\u5730\u5740\u65E0\u6548"});return}let C=Wt[f];if(g.method==="GET"&&C){try{let d=await Mt(Tt(e,C.path));c.writeHead(200,{"Content-Type":C.type,"Cache-Control":"no-store"}),c.end(d)}catch{m(c,500,{error:"\u9875\u9762\u6587\u4EF6\u4E0D\u53EF\u7528"})}return}if(!f.startsWith("/api/")){m(c,404,{error:"\u672A\u627E\u5230"});return}if(!Lt(g,r)){m(c,401,{error:"\u8BBF\u95EE\u4EE4\u724C\u65E0\u6548"});return}if(g.method==="GET"&&f==="/api/image"){let d=new URL(g.url??f,a),w=d.searchParams.get("sessionId")??"",v=d.searchParams.get("messageId")??"",y=Number(d.searchParams.get("index"));if(!w||w!==t.snapshot().sessionId||!v||!Number.isInteger(y)||y<0||y>100){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u56FE\u7247\u5730\u5740\u65E0\u6548"});return}let E=t.image?.(w,v,y);if(!E||!["image/png","image/jpeg","image/webp","image/gif"].includes(E.mimeType)){m(c,404,{error:"\u56FE\u7247\u4E0D\u53EF\u7528"});return}c.writeHead(200,{"Content-Type":E.mimeType,"Cache-Control":"private, no-store"}),c.end(E.data);return}if(g.method==="POST"&&f==="/api/attachment"){if(g.headers.origin!==void 0&&g.headers.origin!==a){m(c,403,{error:"\u6765\u6E90\u4E0D\u5339\u914D"});return}let d=new URL(g.url??f,a),w=d.searchParams.get("sessionId"),v=d.searchParams.get("name");try{if(!w||!v||v.length>1024||w!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u6587\u4EF6\u540D\u65E0\u6548"});return}if(Number(g.headers["content-length"]??0)>N){m(c,413,{error:"\u6587\u4EF6\u8D85\u8FC7 20 MB"});return}let y=await ne(w,v,g.headers["content-type"]??"application/octet-stream",await jt(g));if(w!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}o.set(y.id,y),m(c,201,{id:y.id,name:y.name,bytes:y.bytes})}catch(y){m(c,400,{error:y instanceof Error?y.message:"\u4E0A\u4F20\u5931\u8D25"})}return}if(g.method==="GET"&&f==="/api/session"){try{m(c,200,t.snapshot())}catch{m(c,503,{error:"\u5F53\u524D\u4F1A\u8BDD\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/providers"){try{m(c,200,await t.providers?.()??[])}catch{m(c,503,{error:"\u6A21\u578B\u63D0\u4F9B\u65B9\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/provider/models"){let d=new URL(g.url??f,a).searchParams.get("providerId")??"";try{if(!d||!t.providerModels){m(c,400,{error:"\u63D0\u4F9B\u65B9\u65E0\u6548"});return}m(c,200,await t.providerModels(d))}catch(w){m(c,400,{error:w instanceof Error?w.message:"\u6A21\u578B\u76EE\u5F55\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/provider/login/active"){m(c,200,{active:l.getActive()??null});return}if(g.method==="GET"&&f==="/api/provider/login"){let d=new URL(g.url??f,a).searchParams.get("id")??"",w=l.get(d);m(c,w?200:404,w??{error:"\u767B\u5F55\u6D41\u7A0B\u4E0D\u5B58\u5728"});return}if(g.method==="GET"&&(f==="/api/models"||f==="/api/config"||f==="/api/workspaces"||f==="/api/commands")){try{m(c,200,f==="/api/models"?await t.models():f==="/api/config"?await t.config():f==="/api/commands"?t.commands?.()??[]:await t.workspaces())}catch{m(c,503,{error:"Pi \u914D\u7F6E\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/directory/capability"){m(c,200,{kind:n});return}if(g.method==="GET"&&f==="/api/events"){let d;try{d=t.snapshot()}catch{m(c,503,{error:"\u5F53\u524D\u4F1A\u8BDD\u4E0D\u53EF\u7528"});return}c.writeHead(200,{"Content-Type":"application/x-ndjson; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive","X-Accel-Buffering":"no"}),p.add(c),c.write(`${JSON.stringify({type:"snapshot",session:d})}
4
- `),c.on("close",()=>p.delete(c));return}if(g.method==="POST"&&["/api/message","/api/attachment/remove","/api/abort","/api/compact","/api/new-session","/api/model","/api/thinking-level","/api/config","/api/provider/login","/api/provider/login/respond","/api/provider/login/cancel","/api/provider/custom","/api/provider/models","/api/provider/logout","/api/workspace/add","/api/workspace/select","/api/workspace/new-session","/api/workspace/remove","/api/session/select","/api/directory/list","/api/directory/create","/api/directory/pick"].includes(f)){if(g.headers.origin!==void 0&&g.headers.origin!==a){m(c,403,{error:"\u6765\u6E90\u4E0D\u5339\u914D"});return}if(!g.headers["content-type"]?.startsWith("application/json")){m(c,415,{error:"\u9700\u8981 JSON \u8BF7\u6C42"});return}try{let d=await Nt(g);if(typeof d!="object"||d===null||!("sessionId"in d)||typeof d.sessionId!="string"){m(c,400,{error:"\u7F3A\u5C11\u4F1A\u8BDD\u6807\u8BC6"});return}if(f==="/api/provider/custom"){if(d.sessionId!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(!("provider"in d)||!D(d.provider)||!t.addCustomProvider){m(c,400,{error:"\u81EA\u5B9A\u4E49\u63D0\u4F9B\u65B9\u65E0\u6548"});return}await t.addCustomProvider(d.sessionId,d.provider),m(c,201,{accepted:!0});return}if(f==="/api/provider/models"||f==="/api/provider/logout"){if(d.sessionId!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(!("providerId"in d)||typeof d.providerId!="string"){m(c,400,{error:"\u63D0\u4F9B\u65B9\u65E0\u6548"});return}if(f==="/api/provider/models"){if(!("change"in d)||!ce(d.change)||!t.updateProviderModel){m(c,400,{error:"\u6A21\u578B\u53C2\u6570\u65E0\u6548"});return}await t.updateProviderModel(d.sessionId,d.providerId,d.change)}else{if(!t.logoutProvider){m(c,400,{error:"\u65E0\u6CD5\u79FB\u9664\u8BA4\u8BC1"});return}await t.logoutProvider(d.sessionId,d.providerId)}m(c,200,{accepted:!0});return}if(f.startsWith("/api/provider/login")){if(d.sessionId!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(f==="/api/provider/login"){if(!("providerId"in d)||typeof d.providerId!="string"||!("method"in d)||d.method!=="api_key"&&d.method!=="oauth"||!t.loginProvider||!t.providers){m(c,400,{error:"\u767B\u5F55\u65B9\u5F0F\u65E0\u6548"});return}if(!(await t.providers()).some(E=>E.id===d.providerId&&E.methods.includes(d.method))){m(c,400,{error:"\u63D0\u4F9B\u65B9\u4E0D\u652F\u6301\u6B64\u767B\u5F55\u65B9\u5F0F"});return}let v="initialSecret"in d?d.initialSecret:void 0;if(v!==void 0&&(d.method!=="api_key"||typeof v!="string"||!v.trim()||v.length>16384)){m(c,400,{error:"API Key \u65E0\u6548"});return}let y=l.start(d.providerId,d.method,t.loginProvider,v);m(c,202,{id:y.id});return}if(!("id"in d)||typeof d.id!="string"){m(c,400,{error:"\u767B\u5F55\u6D41\u7A0B\u65E0\u6548"});return}if(f==="/api/provider/login/cancel"){m(c,l.cancel(d.id)?202:404,{accepted:!0});return}if(!("value"in d)||typeof d.value!="string"||d.value.length>16384||!l.respond(d.id,d.value)){m(c,400,{error:"\u767B\u5F55\u8F93\u5165\u65E0\u6548"});return}m(c,202,{accepted:!0});return}if(f==="/api/attachment/remove"){if(!("id"in d)||typeof d.id!="string"||d.sessionId!==t.snapshot().sessionId){m(c,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u9644\u4EF6\u65E0\u6548"});return}o.get(d.id)?.sessionId===d.sessionId&&o.delete(d.id),m(c,202,{accepted:!0});return}if(f==="/api/directory/pick"){if(n!=="native"){m(c,409,{error:"\u672C\u5730\u76EE\u5F55\u9009\u62E9\u5668\u4E0D\u53EF\u7528"});return}let w=new AbortController;c.once("close",()=>w.abort()),m(c,200,{path:await ie(w.signal)});return}if(f==="/api/directory/list"){if("path"in d&&(typeof d.path!="string"||d.path.length>4096)){m(c,400,{error:"\u76EE\u5F55\u8DEF\u5F84\u65E0\u6548"});return}m(c,200,await q("path"in d?d.path:void 0));return}if(f==="/api/directory/create"){if(!("path"in d)||typeof d.path!="string"||d.path.length>4096||!("name"in d)||typeof d.name!="string"||d.name.length>255){m(c,400,{error:"\u6587\u4EF6\u5939\u540D\u79F0\u6216\u8DEF\u5F84\u65E0\u6548"});return}m(c,200,{path:await ee(d.path,d.name)});return}if(f==="/api/workspace/add"){if(!("path"in d)||typeof d.path!="string"||d.path.length>4096||!("create"in d)||typeof d.create!="boolean"){m(c,400,{error:"\u5DE5\u4F5C\u533A\u8DEF\u5F84\u65E0\u6548"});return}await t.addWorkspace(d.path,d.create),m(c,202,{accepted:!0});return}if(f==="/api/workspace/select"||f==="/api/workspace/remove"||f==="/api/workspace/new-session"){if(!("id"in d)||typeof d.id!="string"){m(c,400,{error:"\u5DE5\u4F5C\u533A\u65E0\u6548"});return}f==="/api/workspace/select"?await t.selectWorkspace(d.id):f==="/api/workspace/new-session"?await t.newSessionInWorkspace(d.id):await t.removeWorkspace(d.id),m(c,202,{accepted:!0});return}if(f==="/api/session/select"){if(!("workspaceId"in d)||typeof d.workspaceId!="string"||!("id"in d)||typeof d.id!="string"||!("path"in d)||typeof d.path!="string"){m(c,400,{error:"\u4F1A\u8BDD\u65E0\u6548"});return}await t.selectSession(d.workspaceId,d.id,d.path),m(c,202,{accepted:!0});return}if(f==="/api/thinking-level"){if(!("level"in d)||typeof d.level!="string"){m(c,400,{error:"\u63A8\u7406\u5F3A\u5EA6\u65E0\u6548"});return}await t.setThinkingLevel(d.sessionId,d.level)}else if(f==="/api/model"){if(!("provider"in d)||typeof d.provider!="string"||!("id"in d)||typeof d.id!="string"){m(c,400,{error:"\u6A21\u578B\u65E0\u6548"});return}await t.setModel(d.sessionId,d.provider,d.id)}else if(f==="/api/config"){let w=d;if(!["packages","extensions","skills"].includes(w.kind)||!["global","project"].includes(w.scope)||!["add","remove"].includes(w.action)||typeof w.value!="string"||!w.value.trim()||w.value.length>2048){m(c,400,{error:"\u914D\u7F6E\u9879\u65E0\u6548"});return}if(w.action==="add"&&w.kind!=="packages"&&ge(w.value)){m(c,400,{error:"npm \u6216 Git \u6765\u6E90\u8BF7\u5728\u300C\u5305\u300D\u4E2D\u6DFB\u52A0\uFF1B\u6269\u5C55\u548C\u6280\u80FD\u53EA\u63A5\u53D7\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84"});return}m(c,200,await t.updateConfig(w));return}else if(f==="/api/message"){let w="attachments"in d?d.attachments:[];if(!Array.isArray(w)||w.length>se||w.some(b=>typeof b!="string")||new Set(w).size!==w.length){m(c,400,{error:"\u9644\u4EF6\u65E0\u6548\u6216\u8FC7\u591A"});return}let v=w.map(b=>o.get(b));if(v.some(b=>!b||b.sessionId!==d.sessionId)){m(c,400,{error:"\u9644\u4EF6\u4E0D\u53EF\u7528"});return}if(!("text"in d)||typeof d.text!="string"||!d.text.trim()&&w.length===0||d.text.length>32e3){m(c,400,{error:"\u6D88\u606F\u65E0\u6548\u6216\u8FC7\u957F"});return}let y=v,E=[d.text.trim(),...y.map(b=>`\u9644\u4EF6 ${JSON.stringify(b.name)}\uFF1A${b.path}`)].filter(Boolean).join(`
1
+ import{dirname as wr,join as q,resolve as Ue}from"node:path";import{realpath as J}from"node:fs/promises";import{fileURLToPath as hr}from"node:url";import{DefaultPackageManager as Be,getAgentDir as N,ModelRuntime as yr,SessionManager as Oe,SettingsManager as vr}from"@earendil-works/pi-coding-agent";import{getSupportedThinkingLevels as kr}from"@earendil-works/pi-ai";import{randomBytes as Vt,timingSafeEqual as Ut}from"node:crypto";import{readFile as Bt}from"node:fs/promises";import{createServer as Ot}from"node:http";import{join as Dt}from"node:path";import{mkdir as He,opendir as Ge,stat as ze}from"node:fs/promises";import{homedir as Ke}from"node:os";import{basename as Ye,dirname as te,join as re,posix as Xe,resolve as ie,win32 as Ze}from"node:path";function se(t){return process.platform==="win32"?Ze.isAbsolute(t)&&/^(?:[A-Za-z]:[\\/]|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)/.test(t):Xe.isAbsolute(t)}function Qe(t,e,r){if(t.length===r&&e.name.localeCompare(t[t.length-1].name)>=0)return!0;let i=0,n=t.length;for(;i<n;){let p=i+n>>>1;e.name.localeCompare(t[p].name)<0?n=p:i=p+1}return t.splice(i,0,e),t.length<=r?!1:(t.pop(),!0)}async function ne(t){if(t!==void 0&&!se(t))throw new Error("\u76EE\u5F55\u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");let e=Ke(),r=ie(t??e),i=[];for(let a=r;;a=te(a)){let u=te(a);if(i.unshift({name:u===a?a:Ye(a),path:a,hidden:!1}),u===a)break}let n=[],p=!1,o=await Ge(r);for await(let a of o)!a.isDirectory()&&!a.isSymbolicLink()||Qe(n,{name:a.name,isDirectory:a.isDirectory(),isSymbolicLink:a.isSymbolicLink()},1001)&&(p=!0);let l=[];for(let a of n){let u=re(r,a.name);if(!a.isDirectory&&a.isSymbolicLink)try{if(!(await ze(u)).isDirectory())continue}catch{continue}if(l.length===1e3){p=!0;break}l.push({name:a.name,path:u,hidden:a.name.startsWith(".")})}return{path:r,home:e,crumbs:i,entries:l,truncated:p}}async function oe(t,e){if(!se(t))throw new Error("\u7236\u76EE\u5F55\u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");if(!e.trim()||e==="."||e===".."||/[/\\]/.test(e))throw new Error("\u6587\u4EF6\u5939\u540D\u79F0\u65E0\u6548");let r=re(ie(t),e);return await He(r),r}import{execFile as qe}from"node:child_process";import{accessSync as et,constants as tt}from"node:fs";import{delimiter as rt,join as it}from"node:path";var st=(t,e,r)=>new Promise((i,n)=>{qe(t,[...e],{encoding:"utf8",signal:r,windowsHide:!0},(p,o,l)=>{p?n(Object.assign(new Error(p.message,{cause:p}),{code:p.code,stdout:o,stderr:l})):i({stdout:o,stderr:l})})});function nt(t){for(let e of(t??"").split(rt))if(e)for(let r of["zenity","kdialog"])try{return et(it(e,r),tt.X_OK),!0}catch{}return!1}function ae(t=process.platform,e=process.env){return e.SSH_CONNECTION||e.SSH_TTY?"browse":t==="darwin"||t==="linux"&&(e.DISPLAY||e.WAYLAND_DISPLAY)&&nt(e.PATH)?"native":"browse"}function L(t){if(!(typeof t!="object"||t===null||!("code"in t)))return typeof t.code=="string"||typeof t.code=="number"?t.code:void 0}function ot(t){return typeof t=="object"&&t!==null&&"stderr"in t&&typeof t.stderr=="string"?t.stderr:""}function H(t){return t.replace(/[\r\n]+$/,"")||null}async function ce(t,e=process.platform,r=st){if(e==="darwin")try{let i=await r("osascript",["-e",'set selectedFolder to choose folder with prompt "Select Workspace Directory"',"-e","POSIX path of selectedFolder"],t);return H(i.stdout)}catch(i){if(!t.aborted&&L(i)===1&&/(?:User canceled|-128)/i.test(ot(i)))return null;throw i}if(e==="linux"){try{return H((await r("zenity",["--file-selection","--directory","--title=Select Workspace Directory"],t)).stdout)}catch(i){if(t.aborted)throw i;if(L(i)===1)return null;if(L(i)!=="ENOENT")throw i}try{return H((await r("kdialog",["--getexistingdirectory",".","--title","Select Workspace Directory"],t)).stdout)}catch(i){if(t.aborted)throw i;if(L(i)===1)return null;throw i}}throw new Error(`\u5F53\u524D\u7CFB\u7EDF\u4E0D\u652F\u6301\u672C\u5730\u76EE\u5F55\u9009\u62E9\u5668\uFF1A${e}`)}import{createHash as at,randomUUID as ct}from"node:crypto";import{mkdir as lt,readFile as dt,writeFile as pt}from"node:fs/promises";import{join as le}from"node:path";import{getAgentDir as ut}from"@earendil-works/pi-coding-agent";var _=20*1024*1024,de=20,mt=/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/iu;function ft(t){let e=t.indexOf("."),r=(e<0?t:t.slice(0,e)).replace(/[. ]+$/u,"");return mt.test(r)}function gt(t,e){let r=0,i="";for(let n of Buffer.from(t).toString("utf8")){let p=Buffer.byteLength(n);if(r+p>e)break;i+=n,r+=p}return i}function wt(t){if(t===void 0)return"file";let r=t.slice(Math.max(t.lastIndexOf("/"),t.lastIndexOf("\\"))+1).replace(/[\u0000-\u001f\u007f]/g,"").replace(/[<>:"|?*]/g,"_").trim().replace(/[. ]+$/u,"");return ft(r)&&(r=`_${r}`),r=gt(r,255).replace(/[. ]+$/u,""),r===""||r==="."||r===".."?"file":r}function ht(t){return t.length>=8&&t.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))?"image/png":t.length>=3&&t.subarray(0,3).equals(Buffer.from([255,216,255]))?"image/jpeg":t.length>=6&&["GIF87a","GIF89a"].includes(t.toString("ascii",0,6))?"image/gif":t.length>=12&&t.toString("ascii",0,4)==="RIFF"&&t.toString("ascii",8,12)==="WEBP"?"image/webp":null}async function pe(t,e,r,i){if(i.byteLength===0||i.byteLength>_)throw new Error("\u6587\u4EF6\u4E3A\u7A7A\u6216\u8D85\u8FC7 20 MB");let n=wt(e),p=at("sha256").update(i).digest("hex"),o=le(ut(),"pi-web","attachments",p);await lt(o,{recursive:!0});let l=le(o,n);try{await pt(l,i,{flag:"wx",mode:384})}catch(a){if(!(a instanceof Error&&"code"in a&&a.code==="EEXIST"))throw a}return{id:ct(),name:n,bytes:i.byteLength,path:l,mediaType:ht(i)??r.replace(/^image\//,"application/"),sessionId:t}}async function ue(t){return{type:"image",data:(await dt(t.path)).toString("base64"),mimeType:t.mediaType}}import{randomBytes as yt}from"node:crypto";function me(t,e,r){return t!=="openai-codex"||e!=="oauth"?r:{...r,prompt:i=>i.type==="select"&&i.options.some(n=>n.id==="browser")&&i.options.some(n=>n.id==="device_code")?Promise.resolve("browser"):r.prompt(i)}}var V=class{flows=new Map;start(e,r,i,n){let p=this.getActive();if(p){if(p.providerId===e&&p.method===r&&n===void 0)return p;throw new Error("\u5176\u4ED6\u63D0\u4F9B\u65B9\u7684\u767B\u5F55\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u5148\u5B8C\u6210\u6216\u53D6\u6D88\u5F53\u524D\u767B\u5F55")}this.flows.clear();let o=yt(18).toString("base64url"),l={view:{id:o,providerId:e,method:r,status:"running"},controller:new AbortController};return this.flows.set(o,l),i(e,r,{signal:l.controller.signal,notify:a=>{l.view={...l.view,event:a,...a.type==="auth_url"||a.type==="device_code"?{authorization:a}:{}}},prompt:a=>{if(a.type==="secret"&&n!==void 0){let u=n;return n=void 0,Promise.resolve(u)}return new Promise((u,h)=>{let{signal:y,...b}=a;l.answer=u,l.reject=h,l.view={...l.view,status:"waiting",prompt:b},y?.aborted?h(new Error("\u767B\u5F55\u8F93\u5165\u5DF2\u53D6\u6D88")):y?.addEventListener("abort",()=>h(new Error("\u767B\u5F55\u8F93\u5165\u5DF2\u53D6\u6D88")),{once:!0})})}}).then(()=>{l.view.status!=="cancelled"&&(l.view={id:o,providerId:e,method:r,status:"done"})}).catch(()=>{l.view.status!=="cancelled"&&(l.view={id:o,providerId:e,method:r,status:"error",error:"\u767B\u5F55\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u63D0\u4F9B\u65B9\u63D0\u793A\u5E76\u91CD\u8BD5\u3002"})}),{...l.view}}get(e){let r=this.flows.get(e)?.view;return r?{...r}:void 0}getActive(){let e=[...this.flows.values()].find(({view:r})=>r.status==="running"||r.status==="waiting")?.view;return e?{...e}:void 0}respond(e,r){let i=this.flows.get(e);if(!i?.answer||i.view.status!=="waiting"||i.view.prompt?.type==="select"&&!i.view.prompt.options.some(p=>p.id===r))return!1;let n=i.answer;return i.answer=void 0,i.reject=void 0,i.view={...i.view,status:"running",prompt:void 0},n(r),!0}cancel(e){let r=this.flows.get(e);return!r||r.view.status==="done"||r.view.status==="error"?!1:(r.view={...r.view,status:"cancelled",prompt:void 0},r.controller.abort(),r.reject?.(new Error("\u767B\u5F55\u5DF2\u53D6\u6D88")),r.answer=void 0,r.reject=void 0,!0)}close(){for(let e of this.flows.keys())this.cancel(e);this.flows.clear()}};import{randomBytes as vt}from"node:crypto";import{readFile as kt,rename as bt,rm as Pt,stat as St,writeFile as xt,mkdir as Et}from"node:fs/promises";import{dirname as It}from"node:path";var Ct=["openai-completions","openai-responses","anthropic-messages"];function G(t){if(!t||typeof t!="object")return!1;let e=t;if(typeof e.id!="string"||!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(e.id)||typeof e.name!="string"||!e.name.trim()||e.name.length>100||typeof e.modelId!="string"||!e.modelId.trim()||e.modelId.length>200||!Ct.includes(e.api)||typeof e.baseUrl!="string"||e.baseUrl.length>2048)return!1;try{let r=new URL(e.baseUrl);return(r.protocol==="https:"||r.protocol==="http:")&&!!r.hostname&&!r.username&&!r.password&&!r.hash}catch{return!1}}async function fe(t,e){if(!G(e))throw new Error("\u81EA\u5B9A\u4E49\u63D0\u4F9B\u65B9\u540D\u79F0\u3001\u5730\u5740\u6216\u6A21\u578B\u65E0\u6548");let r;try{r=await kt(t,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let i;if(r===void 0)i={providers:{}};else{try{i=JSON.parse(r)}catch{throw new Error("models.json \u542B\u6CE8\u91CA\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u624B\u52A8\u6DFB\u52A0\u63D0\u4F9B\u65B9")}if(!i||typeof i!="object"||!i.providers||typeof i.providers!="object"||Array.isArray(i.providers))throw new Error("models.json \u7ED3\u6784\u65E0\u6548")}if(Object.hasOwn(i.providers,e.id))throw new Error("\u63D0\u4F9B\u65B9\u5DF2\u5B58\u5728");let n={...i,providers:{...i.providers,[e.id]:{name:e.name.trim(),baseUrl:e.baseUrl,api:e.api,models:[{id:e.modelId.trim()}]}}};await Et(It(t),{recursive:!0});let p=r===void 0?384:(await St(t)).mode&511,o=`${t}.pi-web-${vt(6).toString("hex")}.tmp`;try{await xt(o,`${JSON.stringify(n,null,2)}
2
+ `,{mode:p,flag:"wx"}),await bt(o,t)}finally{await Pt(o,{force:!0})}}import{randomBytes as Mt}from"node:crypto";import{mkdir as At,readFile as Tt,rename as Nt,rm as Rt,stat as Wt,writeFile as jt}from"node:fs/promises";import{dirname as Lt}from"node:path";function ge(t){if(!t||t.length>2048)return!1;try{let e=new URL(t);return(e.protocol==="https:"||e.protocol==="http:")&&!!e.hostname&&!e.username&&!e.password&&!e.search&&!e.hash}catch{return!1}}function we(t){if(!t||typeof t!="object")return!1;let e=t;return e.action==="save"?(e.originalId===void 0||typeof e.originalId=="string")&&he(e.model):e.action==="base_url"?e.baseUrl===null||typeof e.baseUrl=="string"&&ge(e.baseUrl):e.action==="remove"&&typeof e.id=="string"&&e.id.length>0&&e.id.length<=200}function he(t){if(!t||typeof t!="object")return!1;let e=t;if(typeof e.id!="string"||!e.id.trim()||e.id.length>200||e.name!==void 0&&(typeof e.name!="string"||!e.name.trim()||e.name.length>200))return!1;for(let r of[e.contextWindow,e.maxTokens])if(r!==void 0&&(!Number.isSafeInteger(r)||r<=0))return!1;return e.input===void 0||Array.isArray(e.input)&&e.input.length>0&&e.input.every(r=>r==="text"||r==="image")&&new Set(e.input).size===e.input.length}async function ye(t){let e;try{e=await Tt(t,"utf8")}catch(i){if(i.code!=="ENOENT")throw i}if(e===void 0)return{config:{providers:{}}};let r;try{r=JSON.parse(e)}catch{throw new Error("models.json \u542B\u6CE8\u91CA\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u624B\u52A8\u4FEE\u6539")}if(!r||typeof r!="object"||!r.providers||typeof r.providers!="object"||Array.isArray(r.providers))throw new Error("models.json \u7ED3\u6784\u65E0\u6548");return{config:r,existing:e}}async function _t(t,e,r){await At(Lt(t),{recursive:!0});let i=r===void 0?384:(await Wt(t)).mode&511,n=`${t}.pi-web-${Mt(6).toString("hex")}.tmp`;try{await jt(n,`${JSON.stringify(e,null,2)}
3
+ `,{mode:i,flag:"wx"}),await Nt(n,t)}finally{await Rt(n,{force:!0})}}async function ve(t,e,r){if(!r.getProvider(e))throw new Error("\u63D0\u4F9B\u65B9\u4E0D\u5B58\u5728");let{config:i}=await ye(t),n=i.providers[e],p=new Set(n?.models?.map(o=>o.id)??[]);return{providerId:e,baseUrl:n?.baseUrl,models:r.getModels(e).map(o=>({id:o.id,name:o.name,contextWindow:o.contextWindow,maxTokens:o.maxTokens,input:[...o.input],source:p.has(o.id)?"custom":"builtin",overridden:!!n?.modelOverrides?.[o.id]}))}}async function ke(t,e,r,i){if(!i.getProvider(e))throw new Error("\u63D0\u4F9B\u65B9\u4E0D\u5B58\u5728");let{config:n,existing:p}=await ye(t),o={...n.providers[e]??{}},l=[...o.models??[]],a={...o.modelOverrides??{}};if(r.action==="base_url"){if(r.baseUrl!==null&&!ge(r.baseUrl))throw new Error("API \u5730\u5740\u65E0\u6548");r.baseUrl===null?delete o.baseUrl:o.baseUrl=r.baseUrl}else if(r.action==="save"){if(!he(r.model))throw new Error("\u6A21\u578B\u53C2\u6570\u65E0\u6548");let u={id:r.model.id.trim(),...r.model.name===void 0?{}:{name:r.model.name},...r.model.contextWindow===void 0?{}:{contextWindow:r.model.contextWindow},...r.model.maxTokens===void 0?{}:{maxTokens:r.model.maxTokens},...r.model.input===void 0?{}:{input:r.model.input}},h=r.originalId,y=h===void 0?-1:l.findIndex(b=>b.id===h);if(h===void 0&&(l.some(b=>b.id===u.id)||i.getModel(e,u.id)))throw new Error("\u6A21\u578B ID \u5DF2\u5B58\u5728");if(y>=0){if(u.id!==h&&(l.some(g=>g.id===u.id)||i.getModel(e,u.id)))throw new Error("\u6A21\u578B ID \u5DF2\u5B58\u5728");let b={...l[y],...u};for(let g of["name","contextWindow","maxTokens","input"])u[g]===void 0&&delete b[g];l[y]=b}else if(h!==void 0&&i.getModel(e,h)){if(u.id!==h)throw new Error("\u5185\u7F6E\u6A21\u578B ID \u4E0D\u53EF\u4FEE\u6539");let{id:b,...g}=u;a[h]={...a[h],...g};for(let d of["name","contextWindow","maxTokens","input"])u[d]===void 0&&delete a[h][d]}else{if(h!==void 0)throw new Error("\u6A21\u578B\u4E0D\u5B58\u5728");l.push(u)}}else{let u=l.findIndex(h=>h.id===r.id);if(u>=0)l.splice(u,1);else if(Object.hasOwn(a,r.id))delete a[r.id];else throw new Error("\u53EA\u80FD\u79FB\u9664\u81EA\u5B9A\u4E49\u6A21\u578B\u6216\u6062\u590D\u5DF2\u4FEE\u6539\u7684\u5185\u7F6E\u6A21\u578B")}l.length?o.models=l:delete o.models,Object.keys(a).length?o.modelOverrides=a:delete o.modelOverrides,Object.keys(o).length?n.providers[e]=o:delete n.providers[e],await _t(t,n,p)}function be(t){return/^(?:npm:|git:|https?:\/\/|ssh:\/\/|git@)/i.test(t.trim())}var $t={"/":{path:"index.html",type:"text/html; charset=utf-8"},"/assets/app.js":{path:"assets/app.js",type:"text/javascript; charset=utf-8"},"/assets/style.css":{path:"assets/style.css",type:"text/css; charset=utf-8"}};function m(t,e,r){t.writeHead(e,{"Content-Type":"application/json; charset=utf-8","Cache-Control":"no-store"}),t.end(JSON.stringify(r))}function Ft(t,e){let r=t.headers.authorization;if(typeof r!="string"||!r.startsWith("Bearer "))return!1;let i=Buffer.from(r.slice(7),"utf8");return i.length===e.length&&Ut(i,e)}async function Jt(t){let e=0,r=[];for await(let i of t){let n=Buffer.isBuffer(i)?i:Buffer.from(i);if(e+=n.length,e>64*1024)throw new Error("\u8BF7\u6C42\u5185\u5BB9\u8FC7\u5927");r.push(n)}return JSON.parse(Buffer.concat(r).toString("utf8"))}async function Ht(t){let e=0,r=[];for await(let i of t){let n=Buffer.isBuffer(i)?i:Buffer.from(i);if(e+=n.length,e>_)throw new Error("\u6587\u4EF6\u8D85\u8FC7 20 MB");r.push(n)}return Buffer.concat(r)}async function Gt(t,e=0){await new Promise((i,n)=>{t.once("error",n),t.listen(e,"127.0.0.1",()=>{t.off("error",n),i()})});let r=t.address();if(r===null||typeof r=="string")throw new Error("\u65E0\u6CD5\u786E\u5B9A\u76D1\u542C\u7AEF\u53E3");return r.port}async function Pe(t,e){let r=process.env.PI_WEBAPP_RESTART_TOKEN,i=r&&/^[A-Za-z0-9_-]{32}$/.test(r)?r:Vt(24).toString("base64url"),n=Buffer.from(i),p=ae(),o=new Set,l=new Map,a=new V,u="",h=Ot(async(g,d)=>{d.setHeader("X-Content-Type-Options","nosniff"),d.setHeader("Referrer-Policy","no-referrer"),d.setHeader("Content-Security-Policy","default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data: blob:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'");let f;try{f=new URL(g.url??"/",u).pathname}catch{m(d,400,{error:"\u8BF7\u6C42\u5730\u5740\u65E0\u6548"});return}let W=$t[f];if(g.method==="GET"&&W){try{let c=await Bt(Dt(e,W.path));d.writeHead(200,{"Content-Type":W.type,"Cache-Control":"no-store"}),d.end(c)}catch{m(d,500,{error:"\u9875\u9762\u6587\u4EF6\u4E0D\u53EF\u7528"})}return}if(!f.startsWith("/api/")){m(d,404,{error:"\u672A\u627E\u5230"});return}if(!Ft(g,n)){m(d,401,{error:"\u8BBF\u95EE\u4EE4\u724C\u65E0\u6548"});return}if(g.method==="GET"&&f==="/api/update/version"){try{m(d,200,{current:await t.selfUpdate?.runningVersion()??null})}catch(c){m(d,503,{error:c instanceof Error?c.message:"\u65E0\u6CD5\u8BFB\u53D6\u7248\u672C"})}return}if(g.method==="GET"&&f==="/api/update"){try{m(d,200,await t.selfUpdate?.check()??{canRestart:!1,reason:"\u5F53\u524D\u6269\u5C55\u4E0D\u652F\u6301\u5728\u7EBF\u5347\u7EA7"})}catch(c){m(d,503,{error:c instanceof Error?c.message:"\u68C0\u67E5\u66F4\u65B0\u5931\u8D25"})}return}if(g.method==="GET"&&f==="/api/image"){let c=new URL(g.url??f,u),w=c.searchParams.get("sessionId")??"",k=c.searchParams.get("messageId")??"",v=Number(c.searchParams.get("index"));if(!w||w!==t.snapshot().sessionId||!k||!Number.isInteger(v)||v<0||v>100){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u56FE\u7247\u5730\u5740\u65E0\u6548"});return}let A=t.image?.(w,k,v);if(!A||!["image/png","image/jpeg","image/webp","image/gif"].includes(A.mimeType)){m(d,404,{error:"\u56FE\u7247\u4E0D\u53EF\u7528"});return}d.writeHead(200,{"Content-Type":A.mimeType,"Cache-Control":"private, no-store"}),d.end(A.data);return}if(g.method==="POST"&&f==="/api/attachment"){if(g.headers.origin!==void 0&&g.headers.origin!==u){m(d,403,{error:"\u6765\u6E90\u4E0D\u5339\u914D"});return}let c=new URL(g.url??f,u),w=c.searchParams.get("sessionId"),k=c.searchParams.get("name");try{if(!w||!k||k.length>1024||w!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u6587\u4EF6\u540D\u65E0\u6548"});return}if(Number(g.headers["content-length"]??0)>_){m(d,413,{error:"\u6587\u4EF6\u8D85\u8FC7 20 MB"});return}let v=await pe(w,k,g.headers["content-type"]??"application/octet-stream",await Ht(g));if(w!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}l.set(v.id,v),m(d,201,{id:v.id,name:v.name,bytes:v.bytes})}catch(v){m(d,400,{error:v instanceof Error?v.message:"\u4E0A\u4F20\u5931\u8D25"})}return}if(g.method==="GET"&&f==="/api/session"){try{m(d,200,t.snapshot())}catch{m(d,503,{error:"\u5F53\u524D\u4F1A\u8BDD\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/providers"){try{m(d,200,await t.providers?.()??[])}catch{m(d,503,{error:"\u6A21\u578B\u63D0\u4F9B\u65B9\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/provider/models"){let c=new URL(g.url??f,u).searchParams.get("providerId")??"";try{if(!c||!t.providerModels){m(d,400,{error:"\u63D0\u4F9B\u65B9\u65E0\u6548"});return}m(d,200,await t.providerModels(c))}catch(w){m(d,400,{error:w instanceof Error?w.message:"\u6A21\u578B\u76EE\u5F55\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/provider/login/active"){m(d,200,{active:a.getActive()??null});return}if(g.method==="GET"&&f==="/api/provider/login"){let c=new URL(g.url??f,u).searchParams.get("id")??"",w=a.get(c);m(d,w?200:404,w??{error:"\u767B\u5F55\u6D41\u7A0B\u4E0D\u5B58\u5728"});return}if(g.method==="GET"&&(f==="/api/models"||f==="/api/config"||f==="/api/workspaces"||f==="/api/commands")){try{m(d,200,f==="/api/models"?await t.models():f==="/api/config"?await t.config():f==="/api/commands"?t.commands?.()??[]:await t.workspaces())}catch{m(d,503,{error:"Pi \u914D\u7F6E\u4E0D\u53EF\u7528"})}return}if(g.method==="GET"&&f==="/api/directory/capability"){m(d,200,{kind:p});return}if(g.method==="GET"&&f==="/api/events"){let c;try{c=t.snapshot()}catch{m(d,503,{error:"\u5F53\u524D\u4F1A\u8BDD\u4E0D\u53EF\u7528"});return}d.writeHead(200,{"Content-Type":"application/x-ndjson; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive","X-Accel-Buffering":"no"}),o.add(d),d.write(`${JSON.stringify({type:"snapshot",session:c})}
4
+ `),d.on("close",()=>o.delete(d));return}if(g.method==="POST"&&["/api/update","/api/message","/api/attachment/remove","/api/abort","/api/compact","/api/new-session","/api/model","/api/thinking-level","/api/config","/api/provider/login","/api/provider/login/respond","/api/provider/login/cancel","/api/provider/custom","/api/provider/models","/api/provider/logout","/api/workspace/add","/api/workspace/select","/api/workspace/new-session","/api/workspace/remove","/api/session/select","/api/directory/list","/api/directory/create","/api/directory/pick"].includes(f)){if(g.headers.origin!==void 0&&g.headers.origin!==u){m(d,403,{error:"\u6765\u6E90\u4E0D\u5339\u914D"});return}if(!g.headers["content-type"]?.startsWith("application/json")){m(d,415,{error:"\u9700\u8981 JSON \u8BF7\u6C42"});return}try{let c=await Jt(g);if(typeof c!="object"||c===null||!("sessionId"in c)||typeof c.sessionId!="string"){m(d,400,{error:"\u7F3A\u5C11\u4F1A\u8BDD\u6807\u8BC6"});return}if(f==="/api/update"){if(c.sessionId!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(!t.snapshot().idle){m(d,409,{error:"\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u540E\u518D\u5347\u7EA7"});return}if(!t.selfUpdate){m(d,409,{error:"\u5F53\u524D\u6269\u5C55\u4E0D\u652F\u6301\u5728\u7EBF\u5347\u7EA7"});return}let w=await t.selfUpdate.prepare();m(d,202,{accepted:!0,version:w.version}),setTimeout(()=>{t.selfUpdate?.requestRestart(w).catch(k=>console.error("pi-webapp restart:",k))},500).unref();return}if(f==="/api/provider/custom"){if(c.sessionId!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(!("provider"in c)||!G(c.provider)||!t.addCustomProvider){m(d,400,{error:"\u81EA\u5B9A\u4E49\u63D0\u4F9B\u65B9\u65E0\u6548"});return}await t.addCustomProvider(c.sessionId,c.provider),m(d,201,{accepted:!0});return}if(f==="/api/provider/models"||f==="/api/provider/logout"){if(c.sessionId!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(!("providerId"in c)||typeof c.providerId!="string"){m(d,400,{error:"\u63D0\u4F9B\u65B9\u65E0\u6548"});return}if(f==="/api/provider/models"){if(!("change"in c)||!we(c.change)||!t.updateProviderModel){m(d,400,{error:"\u6A21\u578B\u53C2\u6570\u65E0\u6548"});return}await t.updateProviderModel(c.sessionId,c.providerId,c.change)}else{if(!t.logoutProvider){m(d,400,{error:"\u65E0\u6CD5\u79FB\u9664\u8BA4\u8BC1"});return}await t.logoutProvider(c.sessionId,c.providerId)}m(d,200,{accepted:!0});return}if(f.startsWith("/api/provider/login")){if(c.sessionId!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362"});return}if(f==="/api/provider/login"){if(!("providerId"in c)||typeof c.providerId!="string"||!("method"in c)||c.method!=="api_key"&&c.method!=="oauth"||!t.loginProvider||!t.providers){m(d,400,{error:"\u767B\u5F55\u65B9\u5F0F\u65E0\u6548"});return}if(!(await t.providers()).some(A=>A.id===c.providerId&&A.methods.includes(c.method))){m(d,400,{error:"\u63D0\u4F9B\u65B9\u4E0D\u652F\u6301\u6B64\u767B\u5F55\u65B9\u5F0F"});return}let k="initialSecret"in c?c.initialSecret:void 0;if(k!==void 0&&(c.method!=="api_key"||typeof k!="string"||!k.trim()||k.length>16384)){m(d,400,{error:"API Key \u65E0\u6548"});return}let v=a.start(c.providerId,c.method,t.loginProvider,k);m(d,202,{id:v.id});return}if(!("id"in c)||typeof c.id!="string"){m(d,400,{error:"\u767B\u5F55\u6D41\u7A0B\u65E0\u6548"});return}if(f==="/api/provider/login/cancel"){m(d,a.cancel(c.id)?202:404,{accepted:!0});return}if(!("value"in c)||typeof c.value!="string"||c.value.length>16384||!a.respond(c.id,c.value)){m(d,400,{error:"\u767B\u5F55\u8F93\u5165\u65E0\u6548"});return}m(d,202,{accepted:!0});return}if(f==="/api/attachment/remove"){if(!("id"in c)||typeof c.id!="string"||c.sessionId!==t.snapshot().sessionId){m(d,409,{error:"\u4F1A\u8BDD\u5DF2\u5207\u6362\u6216\u9644\u4EF6\u65E0\u6548"});return}l.get(c.id)?.sessionId===c.sessionId&&l.delete(c.id),m(d,202,{accepted:!0});return}if(f==="/api/directory/pick"){if(p!=="native"){m(d,409,{error:"\u672C\u5730\u76EE\u5F55\u9009\u62E9\u5668\u4E0D\u53EF\u7528"});return}let w=new AbortController;d.once("close",()=>w.abort()),m(d,200,{path:await ce(w.signal)});return}if(f==="/api/directory/list"){if("path"in c&&(typeof c.path!="string"||c.path.length>4096)){m(d,400,{error:"\u76EE\u5F55\u8DEF\u5F84\u65E0\u6548"});return}m(d,200,await ne("path"in c?c.path:void 0));return}if(f==="/api/directory/create"){if(!("path"in c)||typeof c.path!="string"||c.path.length>4096||!("name"in c)||typeof c.name!="string"||c.name.length>255){m(d,400,{error:"\u6587\u4EF6\u5939\u540D\u79F0\u6216\u8DEF\u5F84\u65E0\u6548"});return}m(d,200,{path:await oe(c.path,c.name)});return}if(f==="/api/workspace/add"){if(!("path"in c)||typeof c.path!="string"||c.path.length>4096||!("create"in c)||typeof c.create!="boolean"){m(d,400,{error:"\u5DE5\u4F5C\u533A\u8DEF\u5F84\u65E0\u6548"});return}await t.addWorkspace(c.path,c.create),m(d,202,{accepted:!0});return}if(f==="/api/workspace/select"||f==="/api/workspace/remove"||f==="/api/workspace/new-session"){if(!("id"in c)||typeof c.id!="string"){m(d,400,{error:"\u5DE5\u4F5C\u533A\u65E0\u6548"});return}f==="/api/workspace/select"?await t.selectWorkspace(c.id):f==="/api/workspace/new-session"?await t.newSessionInWorkspace(c.id):await t.removeWorkspace(c.id),m(d,202,{accepted:!0});return}if(f==="/api/session/select"){if(!("workspaceId"in c)||typeof c.workspaceId!="string"||!("id"in c)||typeof c.id!="string"||!("path"in c)||typeof c.path!="string"){m(d,400,{error:"\u4F1A\u8BDD\u65E0\u6548"});return}await t.selectSession(c.workspaceId,c.id,c.path),m(d,202,{accepted:!0});return}if(f==="/api/thinking-level"){if(!("level"in c)||typeof c.level!="string"){m(d,400,{error:"\u63A8\u7406\u5F3A\u5EA6\u65E0\u6548"});return}await t.setThinkingLevel(c.sessionId,c.level)}else if(f==="/api/model"){if(!("provider"in c)||typeof c.provider!="string"||!("id"in c)||typeof c.id!="string"){m(d,400,{error:"\u6A21\u578B\u65E0\u6548"});return}await t.setModel(c.sessionId,c.provider,c.id)}else if(f==="/api/config"){let w=c;if(!["packages","extensions","skills"].includes(w.kind)||!["global","project"].includes(w.scope)||!["add","remove"].includes(w.action)||typeof w.value!="string"||!w.value.trim()||w.value.length>2048){m(d,400,{error:"\u914D\u7F6E\u9879\u65E0\u6548"});return}if(w.action==="add"&&w.kind!=="packages"&&be(w.value)){m(d,400,{error:"npm \u6216 Git \u6765\u6E90\u8BF7\u5728\u300C\u5305\u300D\u4E2D\u6DFB\u52A0\uFF1B\u6269\u5C55\u548C\u6280\u80FD\u53EA\u63A5\u53D7\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84"});return}m(d,200,await t.updateConfig(w));return}else if(f==="/api/message"){let w="attachments"in c?c.attachments:[];if(!Array.isArray(w)||w.length>de||w.some(S=>typeof S!="string")||new Set(w).size!==w.length){m(d,400,{error:"\u9644\u4EF6\u65E0\u6548\u6216\u8FC7\u591A"});return}let k=w.map(S=>l.get(S));if(k.some(S=>!S||S.sessionId!==c.sessionId)){m(d,400,{error:"\u9644\u4EF6\u4E0D\u53EF\u7528"});return}if(!("text"in c)||typeof c.text!="string"||!c.text.trim()&&w.length===0||c.text.length>32e3){m(d,400,{error:"\u6D88\u606F\u65E0\u6548\u6216\u8FC7\u957F"});return}let v=k,A=[c.text.trim(),...v.map(S=>`\u9644\u4EF6 ${JSON.stringify(S.name)}\uFF1A${S.path}`)].filter(Boolean).join(`
5
5
 
6
- `),Ne=await Promise.all(y.filter(b=>["image/png","image/jpeg","image/webp","image/gif"].includes(b.mediaType)).map(oe));await t.send(d.sessionId,E,Ne);for(let b of w)o.delete(b)}else if(f==="/api/compact"){if(!t.compact)throw new Error("\u5F53\u524D\u4F1A\u8BDD\u4E0D\u652F\u6301\u538B\u7F29");await t.compact(d.sessionId)}else f==="/api/abort"?t.abort(d.sessionId):await t.newSession(d.sessionId);m(c,202,{accepted:!0})}catch(d){let w=d instanceof Error?d.message:"\u8BF7\u6C42\u5931\u8D25";m(c,w==="\u4F1A\u8BDD\u5DF2\u5207\u6362"?409:400,{error:w})}return}m(c,404,{error:"\u672A\u627E\u5230"})});return a=`http://127.0.0.1:${await Rt(u)}`,{protocolVersion:3,url:`${a}/#${i}`,publish(g){if(g.type==="snapshot")for(let[f,C]of o)C.sessionId!==g.session.sessionId&&o.delete(f);let c=`${JSON.stringify(g)}
7
- `;for(let f of p)f.write(c)},async close(){l.close();for(let g of p)g.end();p.clear(),await new Promise((g,c)=>u.close(f=>f?c(f):g()))}}}import{getSupportedThinkingLevels as Vt}from"@earendil-works/pi-ai";function he(t){let e=[],i=t;for(;;){let r=/(^|\n\n)附件 ("(?:\\.|[^"\\])*"):([^\n]+)$/u.exec(i);if(!r||!/[\\/]pi-web[\\/]attachments[\\/][a-f0-9]{64}[\\/]/u.test(r[3]))break;let n;try{n=JSON.parse(r[2])}catch{break}e.unshift({name:n,image:/\.(?:png|jpe?g|webp|gif)$/iu.test(n)}),i=i.slice(0,r.index)}return{text:i,attachments:e}}function M(t){return typeof t=="string"?[{kind:"text",text:t}]:t.flatMap(e=>e.type==="text"?[{kind:"text",text:e.text}]:e.type==="thinking"?[{kind:"thinking",text:e.thinking}]:e.type==="image"?[{kind:"image",text:"\u56FE\u7247\u9644\u4EF6"}]:e.type==="toolCall"?[{kind:"toolCall",text:JSON.stringify(e.arguments??{}),toolName:e.name,toolCallId:e.id}]:[])}function $(t){if(t.type==="custom_message")return t.display?{id:t.id,role:"notice",timestamp:t.timestamp,blocks:M(t.content)}:null;if(t.type!=="message")return null;let{message:e}=t;return e.role==="user"||e.role==="assistant"?{id:t.id,role:e.role,timestamp:t.timestamp,blocks:M(e.content),...e.role==="assistant"?{...e.stopReason==="error"&&e.errorMessage?{error:e.errorMessage}:{},usage:{input:e.usage.input,output:e.usage.output,cacheRead:e.usage.cacheRead,cacheWrite:e.usage.cacheWrite,...e.usage.reasoning===void 0?{}:{reasoning:e.usage.reasoning},totalTokens:e.usage.totalTokens,cost:e.usage.cost.total},model:`${e.provider}/${e.model}`}:{}}:e.role==="toolResult"?{id:t.id,role:"tool",timestamp:t.timestamp,blocks:M(e.content),isError:e.isError,toolCallId:e.toolCallId,toolName:e.toolName}:null}function F(t,e){let i=t?.trim();if(i)return i;let r=e.find(p=>p.role==="user"),n=he(r?.blocks.find(p=>p.kind==="text")?.text??"").text.replace(/\s+/g," ").trim();return n?n.slice(0,50):"\u5F53\u524D\u4F1A\u8BDD"}function ye(t,e){let i=t.sessionManager.getBranch().flatMap(r=>{let n=$(r);return n?[n]:[]});return{schemaVersion:1,sessionId:t.sessionManager.getSessionId(),cwd:t.cwd,name:F(e,i),model:t.model?`${t.model.provider}/${t.model.id}`:null,thinkingLevel:t.thinkingLevel??null,thinkingLevels:t.model?Vt(t.model):["off"],idle:t.isIdle(),contextUsage:t.getContextUsage()??null,messages:i}}import{randomUUID as H}from"node:crypto";import{mkdir as ve,readFile as _t,realpath as ke,rename as Bt,rm as Ot,stat as be,writeFile as Ut}from"node:fs/promises";import{basename as Pe,dirname as Dt,isAbsolute as Se,join as $t}from"node:path";var R=class{items=[];hiddenPaths=[];mutationChain=Promise.resolve();file;constructor(e){this.file=e}async load(){try{let e=JSON.parse(await _t(this.file,"utf8")),i,r=[];if(Array.isArray(e)?i=e:e&&typeof e=="object"&&"version"in e&&e.version===2&&"items"in e&&"hiddenPaths"in e&&(i=e.items,r=e.hiddenPaths),!Array.isArray(i)||!Array.isArray(r))throw new Error("\u5DE5\u4F5C\u533A\u8BB0\u5F55\u65E0\u6548");this.items=i.filter(n=>typeof n=="object"&&n!==null&&typeof n.id=="string"&&typeof n.path=="string"&&typeof n.title=="string"),this.hiddenPaths=r.filter(n=>typeof n=="string")}catch(e){if(e.code!=="ENOENT")throw e}}list(){return[...this.items]}get(e){return this.items.find(i=>i.id===e)}async add(e,i=!1){if(!Se(e)||e.includes("\0"))throw new Error("\u8BF7\u8F93\u5165\u5B8C\u6574\u7684\u76EE\u5F55\u8DEF\u5F84");i&&await ve(e,{recursive:!0});let r=await ke(e);if(!(await be(r)).isDirectory())throw new Error("\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55");return this.mutate(async()=>{let n=this.hiddenPaths.filter(a=>a!==r),p=this.items.find(a=>a.path===r);if(p)return n.length!==this.hiddenPaths.length&&(await this.save(this.items,n),this.hiddenPaths=n),p;let o={id:H(),path:r,title:Pe(r)||r},l=[o,...this.items];return await this.save(l,n),this.items=l,this.hiddenPaths=n,o})}async discover(e){let i=(await Promise.all([...new Set(e)].filter(Se).map(async r=>{try{let n=await ke(r);return(await be(n)).isDirectory()?n:null}catch{return null}}))).filter(r=>r!==null);await this.mutate(async()=>{let r=new Set([...this.items.map(o=>o.path),...this.hiddenPaths]),n=[];for(let o of i)r.has(o)||(r.add(o),n.push({id:H(),path:o,title:Pe(o)||o}));if(n.length===0)return;let p=[...this.items,...n];await this.save(p,this.hiddenPaths),this.items=p})}async remove(e){await this.mutate(async()=>{let i=this.get(e);if(!i)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");let r=this.items.filter(p=>p.id!==e),n=[...new Set([...this.hiddenPaths,i.path])];await this.save(r,n),this.items=r,this.hiddenPaths=n})}mutate(e){let i=this.mutationChain.then(e,e);return this.mutationChain=i.then(()=>{},()=>{}),i}async save(e,i){await ve(Dt(this.file),{recursive:!0});let r=`${this.file}.${H()}.tmp`;try{await Ut(r,JSON.stringify({version:2,items:e,hiddenPaths:i},null,2),{mode:384}),await Bt(r,this.file)}catch(n){throw await Ot(r,{force:!0}),n}}};function xe(t){return $t(t,"pi-web","workspaces.json")}import{createAgentSession as Ht,DefaultResourceLoader as Jt,getAgentDir as Gt,SessionManager as V}from"@earendil-works/pi-coding-agent";import{realpath as Ee}from"node:fs/promises";import{realpath as Ie}from"node:fs/promises";import{SessionManager as Ft}from"@earendil-works/pi-coding-agent";async function W(t,e){let i=await Ie(t),r=e??await Ft.listAll(),n=await Promise.all(r.map(async p=>p.cwd!==""&&await Ie(p.cwd).catch(()=>null)===i));return r.filter((p,o)=>n[o])}var _=class{active=null;detach=null;cwd=null;lastStreamAt=0;publish;fallbackModel;constructor(e,i){this.publish=e,this.fallbackModel=i}get session(){return this.active}get path(){return this.cwd}async open(e,i){if(this.active&&!this.active.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u5DE5\u4F5C\u533A");let r=await Ee(e),n;if(i==="new")n=V.create(r);else if(i==="continue"){let a=(await W(r))[0];n=a?V.open(a.path):V.create(r)}else n=V.open(i.sessionFile);if(await Ee(n.getCwd())!==r)throw new Error("\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u8BE5\u5DE5\u4F5C\u533A");let p=n.buildSessionProjection().model?void 0:this.active?.model??this.fallbackModel(),o=new Jt({cwd:r,agentDir:Gt(),noExtensions:!0});await o.reload();let{session:l}=await Ht({cwd:r,sessionManager:n,model:p,resourceLoader:o});this.detach?.(),this.active?.dispose(),this.active=l,this.cwd=r,this.detach=l.subscribe(a=>{if(a.type==="message_update"&&a.message.role==="assistant"){let u=Date.now();u-this.lastStreamAt>=60&&(this.lastStreamAt=u,this.publish({type:"stream",message:{id:"stream",role:"assistant",timestamp:new Date(a.message.timestamp).toISOString(),blocks:M(a.message.content)}}))}(a.type==="message_end"||a.type==="agent_start"||a.type==="agent_settled"||a.type==="session_info_changed")&&(a.type==="message_end"&&a.message.role==="assistant"&&this.publish({type:"stream",message:null}),this.publish({type:"snapshot",session:this.snapshot()}))}),this.publish({type:"stream",message:null}),this.publish({type:"snapshot",session:this.snapshot()})}snapshot(){let e=this.active;if(!e||!this.cwd)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u5DE5\u4F5C\u533A\u4F1A\u8BDD");let i=e.sessionManager.getBranch().flatMap(r=>{let n=$(r);return n?[n]:[]});return{schemaVersion:1,sessionId:e.sessionManager.getSessionId(),cwd:this.cwd,name:F(e.sessionManager.getSessionName(),i),model:e.model?`${e.model.provider}/${e.model.id}`:null,thinkingLevel:e.thinkingLevel,thinkingLevels:e.getAvailableThinkingLevels(),idle:e.isIdle,contextUsage:e.getContextUsage()??null,messages:i}}async send(e,i=[]){if(!this.active||!this.active.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let r=this.active,n=!1,p,o,l=new Promise((a,u)=>{p=a,o=u});r.prompt(e,{images:i,preflightResult:a=>{a&&(n=!0,p())}}).then(()=>{n||p()}).catch(a=>{n?this.publish({type:"error",message:a instanceof Error?a.message:"Pi \u8BF7\u6C42\u5931\u8D25"}):o(a)}),await l}async stop(){await this.active?.abort()}async models(){return(await this.active?.modelRuntime.getAvailable()??[]).map(i=>({provider:i.provider,id:i.id,name:i.name}))}async setModel(e,i){if(!this.active||!this.active.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let r=this.active.modelRuntime.getModel(e,i);if(!r)throw new Error("\u6A21\u578B\u4E0D\u53EF\u7528");await this.active.setModel(r),this.publish({type:"snapshot",session:this.snapshot()})}dispose(){this.detach?.(),this.active?.dispose(),this.detach=null,this.active=null,this.cwd=null}};import{basename as J,dirname as Kt}from"node:path";function Ce(t,e){let i=r=>e[r].map(n=>({name:r==="skills"&&J(n.path).toLowerCase()==="skill.md"?J(Kt(n.path)):J(n.path),path:n.path,source:n.metadata.source,scope:n.metadata.scope,enabled:n.enabled}));return{packages:t.map(r=>({name:r.source,path:r.installedPath??"",source:r.source,scope:r.scope,enabled:!!r.installedPath})),extensions:i("extensions"),skills:i("skills")}}import{execFile as zt}from"node:child_process";var Yt=(t,e)=>new Promise((i,r)=>{zt(t,[...e],{windowsHide:!0},n=>{n?r(n):i()})});async function G(t,e=process.platform,i=process.env,r=Yt){if(i.SSH_CONNECTION||i.SSH_TTY||i.SSH_CLIENT||e==="linux"&&!i.DISPLAY&&!i.WAYLAND_DISPLAY)return!1;let n=e==="darwin"?"open":e==="linux"?"xdg-open":e==="win32"?"explorer.exe":null;return n===null?!1:(await r(n,[t]),!0)}var ii=Qt(Xt(Zt(import.meta.url)),"../web/dist"),ri=Symbol.for("pi-web.bridge-state"),s=globalThis[ri]??={bridge:null,current:null,commandContext:null,pi:null,replacementContext:null,replacementSessionId:null,lastStreamAt:0,workspaces:null,workspaceSessions:null,selected:"tui",activeWorkspaceId:null,autoOpened:!1},si=null;function ni(){return si??=qt.create({refreshOnCreate:!1})}var B=new Map;async function A(){let t=await ni(),e=s.selected==="tui"?s.current?.modelRegistry:void 0,i=new Set(e?.getRegisteredProviderIds()??[]);for(let r of B.keys())i.has(r)||(t.unregisterProvider(r),B.delete(r));for(let r of i){let n=e?.getRegisteredNativeProvider(r),p=e?.getRegisteredProviderConfig(r),o=n??p;!o||B.get(r)===o||(n?t.registerNativeProvider(n):p&&t.registerProvider(r,p),B.set(r,o))}return await t.refresh({allowNetwork:!1}),t}function k(){if(s.selected==="sdk")return s.workspaceSessions?.snapshot()??(()=>{throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD")})();if(s.current===null)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");return ye(s.current,s.current.sessionManager.getSessionName())}function S(t){if(k().sessionId!==t)throw new Error("\u4F1A\u8BDD\u5DF2\u5207\u6362")}async function Te(){let t=s.workspaces?.list()??[],e=k(),i=await O(e.cwd).catch(()=>e.cwd),r=await Ae.listAll(),n=(await Promise.all(t.map(async p=>{let l=(await W(p.path,r).catch(()=>[])).map(a=>({id:a.id,workspaceId:p.id,path:a.path,name:a.name||a.firstMessage?.slice(0,50)||"\u5F53\u524D\u4F1A\u8BDD",modified:a.modified.toISOString()}));return p.path===i&&!l.some(a=>a.id===e.sessionId)&&l.unshift({id:e.sessionId,workspaceId:p.id,path:null,name:e.name,modified:new Date().toISOString()}),l}))).flat();return{items:t,activeId:s.activeWorkspaceId,sessions:n}}async function I(){s.bridge&&s.bridge.publish({type:"workspaces",value:await Te()})}async function z(t,e=!1){let i=s.workspaces?.get(t);if(!i)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");if(await O(i.path),!e&&s.current&&i.path===await O(s.current.cwd)){if(s.selected==="sdk"&&s.workspaceSessions?.session&&!s.workspaceSessions.session.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u5DE5\u4F5C\u533A");s.selected="tui",s.activeWorkspaceId=t,s.bridge?.publish({type:"stream",message:null}),s.bridge?.publish({type:"snapshot",session:k()})}else{if(!s.workspaceSessions)throw new Error("Pi \u5DE5\u4F5C\u533A\u670D\u52A1\u4E0D\u53EF\u7528");await s.workspaceSessions.open(i.path,e?"new":"continue"),s.selected="sdk",s.activeWorkspaceId=t,s.bridge?.publish({type:"snapshot",session:k()})}await I()}function x(t){let e=s.current;if(e===null||e.sessionManager.getSessionId()!==t)throw new Error("\u4F1A\u8BDD\u5DF2\u5207\u6362");return e}function P(t){s.current=t,s.selected==="tui"&&s.bridge?.publish({type:"snapshot",session:k()})}function We(){if(s.current===null)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");let t=s.selected==="sdk"?s.workspaceSessions?.path:s.current.cwd;if(!t)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u5DE5\u4F5C\u533A");let e=s.selected==="tui"?s.current.isProjectTrusted():s.workspaceSessions?.session?.settingsManager.isProjectTrusted()??!1;return ei.create(t,void 0,{projectTrusted:e})}async function Le(){let t=We(),e=n=>({packages:(n.packages??[]).map(p=>typeof p=="string"?p:p.source),extensions:n.extensions??[],skills:n.skills??[]}),i=new Me({cwd:k().cwd,agentDir:T(),settingsManager:t}),r=Ce(i.listConfiguredPackages(),await i.resolve(async()=>"skip"));return{projectTrusted:t.isProjectTrusted(),global:e(t.getGlobalSettings()),project:e(t.getProjectSettings()),installed:r}}async function oi(t){S(t.sessionId);let e=s.selected==="tui"?x(t.sessionId):null;if(e?!e.isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let i=We();if(t.scope==="project"&&!i.isProjectTrusted())throw new Error("\u5F53\u524D\u9879\u76EE\u5C1A\u672A\u53D7\u4FE1\u4EFB\uFF0C\u65E0\u6CD5\u4FEE\u6539\u9879\u76EE\u8BBE\u7F6E");let r=t.value.trim(),n=t.scope==="project"?i.getProjectSettings():i.getGlobalSettings();if(t.kind==="packages"){let p=new Me({cwd:k().cwd,agentDir:T(),settingsManager:i});t.action==="add"?await p.installAndPersist(r,{local:t.scope==="project"}):await p.removeAndPersist(r,{local:t.scope==="project"})}else if(t.kind==="extensions"){let p=n.extensions??[],o=t.action==="add"?[...new Set([...p,r])]:p.filter(l=>l!==r);t.scope==="project"?i.setProjectExtensionPaths(o):i.setExtensionPaths(o)}else{let p=n.skills??[],o=t.action==="add"?[...new Set([...p,r])]:p.filter(l=>l!==r);t.scope==="project"?i.setProjectSkillPaths(o):i.setSkillPaths(o)}return await i.flush(),s.selected==="tui"?(s.commandContext&&await s.commandContext.reload(),s.current&&P(s.current)):(await s.workspaceSessions?.session?.resourceLoader.reload(),s.workspaceSessions?.session?.refreshContext()),await Le()}function ai(t,e=G){s.pi=t,t.on("session_start",(r,n)=>{s.replacementSessionId!==n.sessionManager.getSessionId()&&(s.replacementContext=null,s.replacementSessionId=null),P(n),s.selected==="tui"&&s.workspaces&&s.workspaces.add(n.cwd).then(async p=>{s.selected!=="tui"||s.current?.sessionManager.getSessionId()!==n.sessionManager.getSessionId()||(s.activeWorkspaceId=p.id,await I())}).catch(p=>{s.bridge?.publish({type:"error",message:p instanceof Error?p.message:"\u65E0\u6CD5\u6CE8\u518C\u5F53\u524D\u5DE5\u4F5C\u533A"})}),process.env.PI_WEBAPP_AUTO_OPEN==="1"&&n.mode==="tui"&&!s.autoOpened&&(s.autoOpened=!0,i(n).catch(p=>{n.ui.notify(p instanceof Error?p.message:"Web \u542F\u52A8\u5931\u8D25","error")}))}),t.on("agent_start",(r,n)=>P(n)),t.on("agent_end",(r,n)=>P(n)),t.on("agent_settled",(r,n)=>P(n)),t.on("model_select",(r,n)=>P(n)),t.on("thinking_level_select",(r,n)=>P(n)),t.on("message_end",(r,n)=>{s.selected==="tui"&&r.message.role==="assistant"&&s.bridge?.publish({type:"stream",message:null}),P(n),s.selected==="tui"&&I()}),t.on("session_info_changed",(r,n)=>P(n)),t.on("message_update",(r,n)=>{if(s.current=n,s.selected!=="tui"||r.message.role!=="assistant"||s.bridge===null)return;let p=Date.now();p-s.lastStreamAt<60||(s.lastStreamAt=p,s.bridge.publish({type:"stream",message:{id:"stream",role:"assistant",timestamp:new Date(r.message.timestamp).toISOString(),blocks:M(r.message.content)}}))}),t.on("session_shutdown",r=>{if(s.current=null,r.reason==="new"||r.reason==="resume"||r.reason==="fork")return;let n=s.bridge;s.bridge=null,s.commandContext=null,s.replacementContext=null,s.replacementSessionId=null,s.pi=null,s.workspaceSessions?.dispose(),s.workspaceSessions=null,s.selected="tui",s.activeWorkspaceId=null,s.autoOpened=!1,n&&n.close()});let i=async r=>{s.current=r,s.commandContext="newSession"in r?r:null,s.pi=t,s.workspaces===null&&(s.workspaces=new R(xe(T())),await s.workspaces.load());try{await s.workspaces.discover((await Ae.listAll()).map(o=>o.cwd))}catch(o){r.ui.notify(`\u65E0\u6CD5\u8BFB\u53D6\u90E8\u5206 Pi \u5386\u53F2\u5DE5\u4F5C\u533A\uFF1A${o instanceof Error?o.message:String(o)}`,"warning")}let n=await s.workspaces.add(r.cwd);if(s.selected==="tui"&&(s.activeWorkspaceId=n.id),s.bridge&&s.bridge.protocolVersion!==3){let o=s.bridge;s.bridge=null,await o.close()}s.bridge===null&&(s.workspaceSessions=new _(o=>{s.selected==="sdk"&&(s.bridge?.publish(o),o.type==="snapshot"&&o.session.idle&&I())},()=>s.current?.model),s.bridge=await we({snapshot:k,image(o,l,a){S(o);let h=(s.selected==="sdk"?s.workspaceSessions?.session?.sessionManager.getBranch():s.current?.sessionManager.getBranch())?.find(c=>c.id===l);if(h?.type!=="message"||h.message.role!=="user"||!Array.isArray(h.message.content))return null;let g=h.message.content[a];return g?.type!=="image"?null:{data:Buffer.from(g.data,"base64"),mimeType:g.mimeType}},async workspaces(){return Te()},async addWorkspace(o,l){let a=await s.workspaces.add(o,l);try{await z(a.id,!0)}catch(u){throw await I(),u}},async selectWorkspace(o){await z(o)},async newSessionInWorkspace(o){await z(o,!0)},async removeWorkspace(o){if(o===s.activeWorkspaceId)throw new Error("\u8BF7\u5148\u5207\u6362\u5230\u5176\u4ED6\u5DE5\u4F5C\u533A");await s.workspaces.remove(o),await I()},async selectSession(o,l,a){let u=s.workspaces?.get(o);if(!u)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");if(l===s.current?.sessionManager.getSessionId()&&u.path===await O(s.current.cwd)){if(s.selected==="sdk"&&s.workspaceSessions?.session&&!s.workspaceSessions.session.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u4F1A\u8BDD");s.selected="tui",s.activeWorkspaceId=o,s.bridge?.publish({type:"snapshot",session:k()})}else{if(s.selected==="sdk"&&s.workspaceSessions?.session?.sessionManager.getSessionId()===l&&s.workspaceSessions.path===u.path)return;if(!(await W(u.path)).some(g=>g.id===l&&g.path===a))throw new Error("\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u8BE5\u5DE5\u4F5C\u533A");await s.workspaceSessions.open(u.path,{sessionFile:a}),s.selected="sdk",s.activeWorkspaceId=o,s.bridge?.publish({type:"snapshot",session:k()})}await I()},async models(){if(s.selected==="sdk")return s.workspaceSessions?.models()??[];let o=s.current;if(!o)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");let l=o.scopedModels?.map(a=>`${a.model.provider}/${a.model.id}`)??[];return o.modelRegistry.getAvailable().filter(a=>l.length===0||l.includes(`${a.provider}/${a.id}`)).map(a=>({provider:a.provider,id:a.id,name:a.name}))},async providers(){let o=await A(),l=new Set((await o.listCredentials()).map(a=>a.providerId));return o.getProviders().map(a=>({id:a.id,name:a.name,configured:o.getProviderAuthStatus(a.id).configured,storedCredential:l.has(a.id),methods:[a.auth.apiKey?.login?"api_key":null,a.auth.oauth?"oauth":null].filter(u=>u!==null)}))},async providerModels(o){let l=await A();return me(K(T(),"models.json"),o,l)},async updateProviderModel(o,l,a){if(S(o),s.selected==="tui"?!x(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let u=k().model;if(a.action==="remove"&&u===`${l}/${a.id}`)throw new Error("\u8BF7\u5148\u5207\u6362\u5F53\u524D\u4F7F\u7528\u7684\u6A21\u578B");if(a.action==="save"&&a.originalId&&a.originalId!==a.model.id&&u===`${l}/${a.originalId}`)throw new Error("\u8BF7\u5148\u5207\u6362\u5F53\u524D\u4F7F\u7528\u7684\u6A21\u578B");let h=await A();await fe(K(T(),"models.json"),l,a,h),await h.refresh({providers:[l],allowNetwork:!1}),await s.current?.modelRegistry.refresh({providers:[l],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[l],allowNetwork:!1});let g=a.action==="save"?a.model.id:a.action==="base_url"&&u?.startsWith(`${l}/`)?u.slice(l.length+1):null;if(g&&u===`${l}/${g}`)if(s.selected==="sdk")try{await s.workspaceSessions.setModel(l,g)}catch{throw new Error("\u6A21\u578B\u5DF2\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u8BE5\u6A21\u578B\u4EE5\u5E94\u7528\u65B0\u53C2\u6570")}else{let c=x(o),f=c.modelRegistry.find(l,g);if(!f||!s.pi||!await s.pi.setModel(f))throw new Error("\u6A21\u578B\u5DF2\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u8BE5\u6A21\u578B\u4EE5\u5E94\u7528\u65B0\u53C2\u6570");P(c)}},async logoutProvider(o,l){if(S(o),s.selected==="tui"?!x(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let a=await A();if(!(await a.listCredentials()).some(u=>u.providerId===l))throw new Error("\u8BE5\u63D0\u4F9B\u65B9\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u8BA4\u8BC1");await a.logout(l),await s.current?.modelRegistry.refresh({providers:[l],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[l],allowNetwork:!1})},async loginProvider(o,l,a){let u=await A();await u.login(o,l,ae(o,l,a)),await u.refresh({providers:[o],allowNetwork:!1}),await s.current?.modelRegistry.refresh({providers:[o],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[o],allowNetwork:!1})},async addCustomProvider(o,l){if(S(o),s.selected==="tui"?!x(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let a=await A();if(a.getProvider(l.id))throw new Error("\u63D0\u4F9B\u65B9\u5DF2\u5B58\u5728");await de(K(T(),"models.json"),l),await a.refresh({allowNetwork:!1}),await s.current?.modelRegistry.refresh({allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({allowNetwork:!1})},commands(){return s.selected==="tui"?(s.pi?.getCommands()??[]).filter(o=>o.name!=="web").map(o=>({name:o.name,description:o.description})):[]},async compact(o){if(S(o),s.selected==="sdk"){let a=s.workspaceSessions?.session;if(!a||!a.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");await a.compact(),s.bridge?.publish({type:"snapshot",session:k()});return}let l=x(o);if(!l.isIdle())throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");l.compact()},async setModel(o,l,a){if(S(o),s.selected==="sdk"){await s.workspaceSessions.setModel(l,a);return}let u=x(o);if(!u.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let h=u.modelRegistry.find(l,a);if(!h||!u.modelRegistry.getAvailable().some(g=>g.provider===l&&g.id===a))throw new Error("\u6A21\u578B\u4E0D\u53EF\u7528");if(!s.pi||!await s.pi.setModel(h))throw new Error("\u6A21\u578B\u63D0\u4F9B\u65B9\u672A\u914D\u7F6E\u8BA4\u8BC1");P(u)},async setThinkingLevel(o,l){if(S(o),!["off","minimal","low","medium","high","xhigh"].includes(l))throw new Error("\u63A8\u7406\u5F3A\u5EA6\u65E0\u6548");if(s.selected==="sdk"){let h=s.workspaceSessions?.session;if(!h||!h.isIdle||!h.getAvailableThinkingLevels().includes(l))throw new Error("\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u6B64\u63A8\u7406\u5F3A\u5EA6");h.setThinkingLevel(l),s.bridge?.publish({type:"snapshot",session:k()});return}let u=x(o);if(!u.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(!u.model||!ti(u.model).includes(l))throw new Error("\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u6B64\u63A8\u7406\u5F3A\u5EA6");if(!s.pi)throw new Error("Pi \u4F1A\u8BDD\u4E0D\u53EF\u7528");s.pi.setThinkingLevel(l),P(u)},config:Le,updateConfig:oi,async send(o,l,a=[]){if(S(o),s.selected==="sdk"){await s.workspaceSessions.send(l,a);return}if(!x(o).isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(s.replacementContext!==null&&s.replacementSessionId===o){await s.replacementContext.sendUserMessage(a.length?[{type:"text",text:l},...a]:l,{expandPromptTemplates:!0});return}if(s.pi===null)throw new Error("Pi \u4F1A\u8BDD\u4E0D\u53EF\u7528");s.pi.sendUserMessage(a.length?[{type:"text",text:l},...a]:l,{expandPromptTemplates:!0})},abort(o){S(o),s.selected==="sdk"?s.workspaceSessions.stop():x(o).abort()},async newSession(o){if(S(o),s.selected==="sdk"){let u=s.workspaces?.get(s.activeWorkspaceId??"");if(!u)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");await s.workspaceSessions.open(u.path,"new"),await I();return}let l=x(o);if(!l.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(s.commandContext===null){await s.workspaceSessions.open(l.cwd,"new"),s.selected="sdk",s.bridge?.publish({type:"snapshot",session:k()}),await I();return}if((await s.commandContext.newSession({withSession:async u=>{s.commandContext=u,s.replacementContext=u,s.replacementSessionId=u.sessionManager.getSessionId(),s.current=u}})).cancelled)throw new Error("\u65B0\u4F1A\u8BDD\u5DF2\u53D6\u6D88");await I()}},ii));let p=await e(s.bridge.url).catch(()=>!1);r.ui.notify(`pi-webapp: ${s.bridge.url}`,p?"info":"warning"),r.ui.setStatus("pi-webapp","Web \u5DF2\u542F\u52A8 \xB7 /web \u91CD\u65B0\u6253\u5F00")};t.registerCommand("web",{description:"Open the current Pi session in a local web interface",handler:async(r,n)=>i(n)})}export{ai as default};
6
+ `),Je=await Promise.all(v.filter(S=>["image/png","image/jpeg","image/webp","image/gif"].includes(S.mediaType)).map(ue));await t.send(c.sessionId,A,Je);for(let S of w)l.delete(S)}else if(f==="/api/compact"){if(!t.compact)throw new Error("\u5F53\u524D\u4F1A\u8BDD\u4E0D\u652F\u6301\u538B\u7F29");await t.compact(c.sessionId)}else f==="/api/abort"?t.abort(c.sessionId):await t.newSession(c.sessionId);m(d,202,{accepted:!0})}catch(c){let w=c instanceof Error?c.message:"\u8BF7\u6C42\u5931\u8D25";m(d,w==="\u4F1A\u8BDD\u5DF2\u5207\u6362"?409:400,{error:w})}return}m(d,404,{error:"\u672A\u627E\u5230"})}),y=Number(process.env.PI_WEBAPP_RESTART_PORT);return u=`http://127.0.0.1:${await Gt(h,Number.isInteger(y)&&y>0&&y<65536&&r===i?y:0)}`,{protocolVersion:4,url:`${u}/#${i}`,publish(g){if(g.type==="snapshot")for(let[f,W]of l)W.sessionId!==g.session.sessionId&&l.delete(f);let d=`${JSON.stringify(g)}
7
+ `;for(let f of o)f.write(d)},async close(){a.close();for(let g of o)g.end();o.clear(),await new Promise((g,d)=>h.close(f=>f?d(f):g()))}}}import{getSupportedThinkingLevels as zt}from"@earendil-works/pi-ai";function Se(t){let e=[],r=t;for(;;){let i=/(^|\n\n)附件 ("(?:\\.|[^"\\])*"):([^\n]+)$/u.exec(r);if(!i||!/[\\/]pi-web[\\/]attachments[\\/][a-f0-9]{64}[\\/]/u.test(i[3]))break;let n;try{n=JSON.parse(i[2])}catch{break}e.unshift({name:n,image:/\.(?:png|jpe?g|webp|gif)$/iu.test(n)}),r=r.slice(0,i.index)}return{text:r,attachments:e}}function T(t){return typeof t=="string"?[{kind:"text",text:t}]:t.flatMap(e=>e.type==="text"?[{kind:"text",text:e.text}]:e.type==="thinking"?[{kind:"thinking",text:e.thinking}]:e.type==="image"?[{kind:"image",text:"\u56FE\u7247\u9644\u4EF6"}]:e.type==="toolCall"?[{kind:"toolCall",text:JSON.stringify(e.arguments??{}),toolName:e.name,toolCallId:e.id}]:[])}function z(t){if(t.type==="custom_message")return t.display?{id:t.id,role:"notice",timestamp:t.timestamp,blocks:T(t.content)}:null;if(t.type!=="message")return null;let{message:e}=t;return e.role==="user"||e.role==="assistant"?{id:t.id,role:e.role,timestamp:t.timestamp,blocks:T(e.content),...e.role==="assistant"?{...e.stopReason==="error"&&e.errorMessage?{error:e.errorMessage}:{},usage:{input:e.usage.input,output:e.usage.output,cacheRead:e.usage.cacheRead,cacheWrite:e.usage.cacheWrite,...e.usage.reasoning===void 0?{}:{reasoning:e.usage.reasoning},totalTokens:e.usage.totalTokens,cost:e.usage.cost.total},model:`${e.provider}/${e.model}`}:{}}:e.role==="toolResult"?{id:t.id,role:"tool",timestamp:t.timestamp,blocks:T(e.content),isError:e.isError,toolCallId:e.toolCallId,toolName:e.toolName}:null}function K(t,e){let r=t?.trim();if(r)return r;let i=e.find(p=>p.role==="user"),n=Se(i?.blocks.find(p=>p.kind==="text")?.text??"").text.replace(/\s+/g," ").trim();return n?n.slice(0,50):"\u5F53\u524D\u4F1A\u8BDD"}function xe(t,e){let r=t.sessionManager.getBranch().flatMap(i=>{let n=z(i);return n?[n]:[]});return{schemaVersion:1,sessionId:t.sessionManager.getSessionId(),cwd:t.cwd,name:K(e,r),model:t.model?`${t.model.provider}/${t.model.id}`:null,thinkingLevel:t.thinkingLevel??null,thinkingLevels:t.model?zt(t.model):["off"],idle:t.isIdle(),contextUsage:t.getContextUsage()??null,messages:r}}import{randomUUID as Y}from"node:crypto";import{mkdir as Ee,readFile as Kt,realpath as Ie,rename as Yt,rm as Xt,stat as Ce,writeFile as Zt}from"node:fs/promises";import{basename as Me,dirname as Qt,isAbsolute as Ae,join as qt}from"node:path";var U=class{items=[];hiddenPaths=[];mutationChain=Promise.resolve();file;constructor(e){this.file=e}async load(){try{let e=JSON.parse(await Kt(this.file,"utf8")),r,i=[];if(Array.isArray(e)?r=e:e&&typeof e=="object"&&"version"in e&&e.version===2&&"items"in e&&"hiddenPaths"in e&&(r=e.items,i=e.hiddenPaths),!Array.isArray(r)||!Array.isArray(i))throw new Error("\u5DE5\u4F5C\u533A\u8BB0\u5F55\u65E0\u6548");this.items=r.filter(n=>typeof n=="object"&&n!==null&&typeof n.id=="string"&&typeof n.path=="string"&&typeof n.title=="string"),this.hiddenPaths=i.filter(n=>typeof n=="string")}catch(e){if(e.code!=="ENOENT")throw e}}list(){return[...this.items]}get(e){return this.items.find(r=>r.id===e)}async add(e,r=!1){if(!Ae(e)||e.includes("\0"))throw new Error("\u8BF7\u8F93\u5165\u5B8C\u6574\u7684\u76EE\u5F55\u8DEF\u5F84");r&&await Ee(e,{recursive:!0});let i=await Ie(e);if(!(await Ce(i)).isDirectory())throw new Error("\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55");return this.mutate(async()=>{let n=this.hiddenPaths.filter(a=>a!==i),p=this.items.find(a=>a.path===i);if(p)return n.length!==this.hiddenPaths.length&&(await this.save(this.items,n),this.hiddenPaths=n),p;let o={id:Y(),path:i,title:Me(i)||i},l=[o,...this.items];return await this.save(l,n),this.items=l,this.hiddenPaths=n,o})}async discover(e){let r=(await Promise.all([...new Set(e)].filter(Ae).map(async i=>{try{let n=await Ie(i);return(await Ce(n)).isDirectory()?n:null}catch{return null}}))).filter(i=>i!==null);await this.mutate(async()=>{let i=new Set([...this.items.map(o=>o.path),...this.hiddenPaths]),n=[];for(let o of r)i.has(o)||(i.add(o),n.push({id:Y(),path:o,title:Me(o)||o}));if(n.length===0)return;let p=[...this.items,...n];await this.save(p,this.hiddenPaths),this.items=p})}async remove(e){await this.mutate(async()=>{let r=this.get(e);if(!r)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");let i=this.items.filter(p=>p.id!==e),n=[...new Set([...this.hiddenPaths,r.path])];await this.save(i,n),this.items=i,this.hiddenPaths=n})}mutate(e){let r=this.mutationChain.then(e,e);return this.mutationChain=r.then(()=>{},()=>{}),r}async save(e,r){await Ee(Qt(this.file),{recursive:!0});let i=`${this.file}.${Y()}.tmp`;try{await Zt(i,JSON.stringify({version:2,items:e,hiddenPaths:r},null,2),{mode:384}),await Yt(i,this.file)}catch(n){throw await Xt(i,{force:!0}),n}}};function Te(t){return qt(t,"pi-web","workspaces.json")}import{createAgentSession as tr,DefaultResourceLoader as rr,getAgentDir as ir,SessionManager as B}from"@earendil-works/pi-coding-agent";import{realpath as Re}from"node:fs/promises";import{realpath as Ne}from"node:fs/promises";import{SessionManager as er}from"@earendil-works/pi-coding-agent";async function j(t,e){let r=await Ne(t),i=e??await er.listAll(),n=await Promise.all(i.map(async p=>p.cwd!==""&&await Ne(p.cwd).catch(()=>null)===r));return i.filter((p,o)=>n[o])}var O=class{active=null;detach=null;cwd=null;lastStreamAt=0;publish;fallbackModel;constructor(e,r){this.publish=e,this.fallbackModel=r}get session(){return this.active}get path(){return this.cwd}async open(e,r){if(this.active&&!this.active.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u5DE5\u4F5C\u533A");let i=await Re(e),n;if(r==="new")n=B.create(i);else if(r==="continue"){let a=(await j(i))[0];n=a?B.open(a.path):B.create(i)}else n=B.open(r.sessionFile);if(await Re(n.getCwd())!==i)throw new Error("\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u8BE5\u5DE5\u4F5C\u533A");let p=n.buildSessionProjection().model?void 0:this.active?.model??this.fallbackModel(),o=new rr({cwd:i,agentDir:ir(),noExtensions:!0});await o.reload();let{session:l}=await tr({cwd:i,sessionManager:n,model:p,resourceLoader:o});this.detach?.(),this.active?.dispose(),this.active=l,this.cwd=i,this.detach=l.subscribe(a=>{if(a.type==="message_update"&&a.message.role==="assistant"){let u=Date.now();u-this.lastStreamAt>=60&&(this.lastStreamAt=u,this.publish({type:"stream",message:{id:"stream",role:"assistant",timestamp:new Date(a.message.timestamp).toISOString(),blocks:T(a.message.content)}}))}(a.type==="message_end"||a.type==="agent_start"||a.type==="agent_settled"||a.type==="session_info_changed")&&(a.type==="message_end"&&a.message.role==="assistant"&&this.publish({type:"stream",message:null}),this.publish({type:"snapshot",session:this.snapshot()}))}),this.publish({type:"stream",message:null}),this.publish({type:"snapshot",session:this.snapshot()})}snapshot(){let e=this.active;if(!e||!this.cwd)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u5DE5\u4F5C\u533A\u4F1A\u8BDD");let r=e.sessionManager.getBranch().flatMap(i=>{let n=z(i);return n?[n]:[]});return{schemaVersion:1,sessionId:e.sessionManager.getSessionId(),cwd:this.cwd,name:K(e.sessionManager.getSessionName(),r),model:e.model?`${e.model.provider}/${e.model.id}`:null,thinkingLevel:e.thinkingLevel,thinkingLevels:e.getAvailableThinkingLevels(),idle:e.isIdle,contextUsage:e.getContextUsage()??null,messages:r}}async send(e,r=[]){if(!this.active||!this.active.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let i=this.active,n=!1,p,o,l=new Promise((a,u)=>{p=a,o=u});i.prompt(e,{images:r,preflightResult:a=>{a&&(n=!0,p())}}).then(()=>{n||p()}).catch(a=>{n?this.publish({type:"error",message:a instanceof Error?a.message:"Pi \u8BF7\u6C42\u5931\u8D25"}):o(a)}),await l}async stop(){await this.active?.abort()}async models(){return(await this.active?.modelRuntime.getAvailable()??[]).map(r=>({provider:r.provider,id:r.id,name:r.name}))}async setModel(e,r){if(!this.active||!this.active.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let i=this.active.modelRuntime.getModel(e,r);if(!i)throw new Error("\u6A21\u578B\u4E0D\u53EF\u7528");await this.active.setModel(i),this.publish({type:"snapshot",session:this.snapshot()})}dispose(){this.detach?.(),this.active?.dispose(),this.detach=null,this.active=null,this.cwd=null}};import{basename as X,dirname as sr}from"node:path";function We(t,e){let r=i=>e[i].map(n=>({name:i==="skills"&&X(n.path).toLowerCase()==="skill.md"?X(sr(n.path)):X(n.path),path:n.path,source:n.metadata.source,scope:n.metadata.scope,enabled:n.enabled}));return{packages:t.map(i=>({name:i.source,path:i.installedPath??"",source:i.source,scope:i.scope,enabled:!!i.installedPath})),extensions:r("extensions"),skills:r("skills")}}import{execFile as nr}from"node:child_process";var or=(t,e)=>new Promise((r,i)=>{nr(t,[...e],{windowsHide:!0},n=>{n?i(n):r()})});async function Z(t,e=process.platform,r=process.env,i=or){if(r.SSH_CONNECTION||r.SSH_TTY||r.SSH_CLIENT||e==="linux"&&!r.DISPLAY&&!r.WAYLAND_DISPLAY)return!1;let n=e==="darwin"?"open":e==="linux"?"xdg-open":e==="win32"?"explorer.exe":null;return n===null?!1:(await i(n,[t]),!0)}import{execFile as ar}from"node:child_process";import{randomUUID as cr}from"node:crypto";import{existsSync as je}from"node:fs";import{access as lr,mkdir as dr,readFile as Le,rename as pr,rm as Q,writeFile as _e}from"node:fs/promises";import{join as x}from"node:path";import{promisify as ur}from"node:util";var mr=ur(ar),D=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;function fr(t,e){let r=D.exec(t),i=D.exec(e);if(!r||!i)throw new Error("\u7248\u672C\u53F7\u65E0\u6548");for(let n=1;n<=3;n++){let p=Number(r[n])-Number(i[n]);if(p)return p}return!r[4]&&i[4]?1:r[4]&&!i[4]?-1:(r[4]??"").localeCompare(i[4]??"",void 0,{numeric:!0})}async function gr(t){try{let{stdout:e}=await mr("npm",t,{timeout:18e4,maxBuffer:1048576,encoding:"utf8"});return e}catch(e){let r=e;throw new Error(r.stderr?.trim()||r.message)}}var $=class{agentDir;packageRoot;launcherNonce;runNpm;preparing=!1;restartPending=!1;constructor(e){this.agentDir=e.agentDir,this.packageRoot=e.packageRoot,this.launcherNonce=e.launcherNonce,this.runNpm=e.runNpm??gr}async current(){let e=JSON.parse(await Le(x(this.packageRoot,"package.json"),"utf8"));if(e.name!=="pi-webapp"||typeof e.version!="string"||!D.test(e.version))throw new Error("\u5F53\u524D pi-webapp \u7248\u672C\u65E0\u6548");return e.version}async runningVersion(){return this.current()}async check(){let e=await this.current(),r=je(x(this.packageRoot,".git")),i={current:e,latest:null,available:!1,canRestart:!!this.launcherNonce&&!r};r?i.reason="\u5F53\u524D\u4ECE\u6E90\u7801\u76EE\u5F55\u8FD0\u884C\u3002\u7F51\u9875\u5347\u7EA7\u4EC5\u652F\u6301 npm \u5B89\u88C5\u7684\u7248\u672C\uFF0C\u4EE5\u514D\u8986\u76D6\u672C\u5730\u4EE3\u7801\u3002":i.canRestart||(i.reason="\u5F53\u524D\u9875\u9762\u7531 Pi \u7EC8\u7AEF\u6253\u5F00\uFF0C\u65E0\u6CD5\u4ECE\u7F51\u9875\u81EA\u52A8\u91CD\u542F Pi\uFF1B\u8BF7\u4F7F\u7528 pi-webapp \u542F\u52A8\u5668\u3002");try{let n=JSON.parse((await this.runNpm(["view","pi-webapp","dist-tags.latest","--json","--prefer-online"])).trim());if(typeof n!="string"||!D.test(n))throw new Error("npm \u8FD4\u56DE\u7684\u7248\u672C\u53F7\u65E0\u6548");i.latest=n,i.available=fr(n,e)>0}catch(n){throw new Error(`\u68C0\u67E5 npm \u66F4\u65B0\u5931\u8D25\uFF1A${n instanceof Error?n.message:String(n)}`)}return i}async prepare(){if(!this.launcherNonce)throw new Error("\u5F53\u524D\u9875\u9762\u65E0\u6CD5\u81EA\u52A8\u91CD\u542F Pi");if(je(x(this.packageRoot,".git")))throw new Error("\u6E90\u7801\u76EE\u5F55\u4E0D\u80FD\u901A\u8FC7\u7F51\u9875\u5347\u7EA7");if(this.restartPending)throw new Error("\u5347\u7EA7\u5DF2\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F");if(this.preparing)throw new Error("\u5347\u7EA7\u6B63\u5728\u8FDB\u884C\u4E2D");this.preparing=!0;try{let e=await this.check();if(!e.available||!e.latest)throw new Error("\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C");let r=x(this.agentDir,"pi-web","releases",e.latest);await Q(r,{recursive:!0,force:!0}),await dr(r,{recursive:!0});try{await this.runNpm(["install",`pi-webapp@${e.latest}`,"--prefix",r,"--ignore-scripts","--no-save","--no-audit","--no-fund"]);let i=x(r,"node_modules","pi-webapp"),n=JSON.parse(await Le(x(i,"package.json"),"utf8"));if(n.name!=="pi-webapp"||n.version!==e.latest)throw new Error("\u4E0B\u8F7D\u7684\u5305\u4E0E\u76EE\u6807\u7248\u672C\u4E0D\u7B26");let p=x(i,"bin","pi-webapp.mjs");await Promise.all([p,x(i,"dist","extension.js"),x(i,"web","dist","index.html")].map(a=>lr(a)));let o=x(this.agentDir,"pi-web"),l=x(o,`update.${cr()}.tmp`);try{await _e(l,JSON.stringify({version:e.latest,launcher:p}),{mode:384}),await pr(l,x(o,"update.json"))}finally{await Q(l,{force:!0})}return this.restartPending=!0,{version:e.latest,launcher:p}}catch(i){throw await Q(r,{recursive:!0,force:!0}),i}}finally{this.preparing=!1}}async requestRestart(e){if(!this.launcherNonce)throw new Error("\u5F53\u524D\u9875\u9762\u65E0\u6CD5\u81EA\u52A8\u91CD\u542F Pi");try{await _e(x(this.agentDir,"pi-web","launcher.restart"),JSON.stringify({nonce:this.launcherNonce,launcher:e.launcher}),{mode:384})}catch(r){throw this.restartPending=!1,r}}};var Ve=Ue(wr(hr(import.meta.url)),"../web/dist"),br=Symbol.for("pi-web.bridge-state"),s=globalThis[br]??={bridge:null,current:null,commandContext:null,pi:null,replacementContext:null,replacementSessionId:null,lastStreamAt:0,workspaces:null,workspaceSessions:null,selected:"tui",activeWorkspaceId:null,autoOpened:!1},Pr=null;function Sr(){return Pr??=yr.create({refreshOnCreate:!1})}var F=new Map;async function R(){let t=await Sr(),e=s.selected==="tui"?s.current?.modelRegistry:void 0,r=new Set(e?.getRegisteredProviderIds()??[]);for(let i of F.keys())r.has(i)||(t.unregisterProvider(i),F.delete(i));for(let i of r){let n=e?.getRegisteredNativeProvider(i),p=e?.getRegisteredProviderConfig(i),o=n??p;!o||F.get(i)===o||(n?t.registerNativeProvider(n):p&&t.registerProvider(i,p),F.set(i,o))}return await t.refresh({allowNetwork:!1}),t}function P(){if(s.selected==="sdk")return s.workspaceSessions?.snapshot()??(()=>{throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD")})();if(s.current===null)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");return xe(s.current,s.current.sessionManager.getSessionName())}function I(t){if(P().sessionId!==t)throw new Error("\u4F1A\u8BDD\u5DF2\u5207\u6362")}async function De(){let t=s.workspaces?.list()??[],e=P(),r=await J(e.cwd).catch(()=>e.cwd),i=await Oe.listAll(),n=(await Promise.all(t.map(async p=>{let l=(await j(p.path,i).catch(()=>[])).map(a=>({id:a.id,workspaceId:p.id,path:a.path,name:a.name||a.firstMessage?.slice(0,50)||"\u5F53\u524D\u4F1A\u8BDD",modified:a.modified.toISOString()}));return p.path===r&&!l.some(a=>a.id===e.sessionId)&&l.unshift({id:e.sessionId,workspaceId:p.id,path:null,name:e.name,modified:new Date().toISOString()}),l}))).flat();return{items:t,activeId:s.activeWorkspaceId,sessions:n}}async function M(){s.bridge&&s.bridge.publish({type:"workspaces",value:await De()})}async function ee(t,e=!1){let r=s.workspaces?.get(t);if(!r)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");if(await J(r.path),!e&&s.current&&r.path===await J(s.current.cwd)){if(s.selected==="sdk"&&s.workspaceSessions?.session&&!s.workspaceSessions.session.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u5DE5\u4F5C\u533A");s.selected="tui",s.activeWorkspaceId=t,s.bridge?.publish({type:"stream",message:null}),s.bridge?.publish({type:"snapshot",session:P()})}else{if(!s.workspaceSessions)throw new Error("Pi \u5DE5\u4F5C\u533A\u670D\u52A1\u4E0D\u53EF\u7528");await s.workspaceSessions.open(r.path,e?"new":"continue"),s.selected="sdk",s.activeWorkspaceId=t,s.bridge?.publish({type:"snapshot",session:P()})}await M()}function C(t){let e=s.current;if(e===null||e.sessionManager.getSessionId()!==t)throw new Error("\u4F1A\u8BDD\u5DF2\u5207\u6362");return e}function E(t){s.current=t,s.selected==="tui"&&s.bridge?.publish({type:"snapshot",session:P()})}function $e(){if(s.current===null)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");let t=s.selected==="sdk"?s.workspaceSessions?.path:s.current.cwd;if(!t)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u5DE5\u4F5C\u533A");let e=s.selected==="tui"?s.current.isProjectTrusted():s.workspaceSessions?.session?.settingsManager.isProjectTrusted()??!1;return vr.create(t,void 0,{projectTrusted:e})}async function Fe(){let t=$e(),e=n=>({packages:(n.packages??[]).map(p=>typeof p=="string"?p:p.source),extensions:n.extensions??[],skills:n.skills??[]}),r=new Be({cwd:P().cwd,agentDir:N(),settingsManager:t}),i=We(r.listConfiguredPackages(),await r.resolve(async()=>"skip"));return{projectTrusted:t.isProjectTrusted(),global:e(t.getGlobalSettings()),project:e(t.getProjectSettings()),installed:i}}async function xr(t){I(t.sessionId);let e=s.selected==="tui"?C(t.sessionId):null;if(e?!e.isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let r=$e();if(t.scope==="project"&&!r.isProjectTrusted())throw new Error("\u5F53\u524D\u9879\u76EE\u5C1A\u672A\u53D7\u4FE1\u4EFB\uFF0C\u65E0\u6CD5\u4FEE\u6539\u9879\u76EE\u8BBE\u7F6E");let i=t.value.trim(),n=t.scope==="project"?r.getProjectSettings():r.getGlobalSettings();if(t.kind==="packages"){let p=new Be({cwd:P().cwd,agentDir:N(),settingsManager:r});t.action==="add"?await p.installAndPersist(i,{local:t.scope==="project"}):await p.removeAndPersist(i,{local:t.scope==="project"})}else if(t.kind==="extensions"){let p=n.extensions??[],o=t.action==="add"?[...new Set([...p,i])]:p.filter(l=>l!==i);t.scope==="project"?r.setProjectExtensionPaths(o):r.setExtensionPaths(o)}else{let p=n.skills??[],o=t.action==="add"?[...new Set([...p,i])]:p.filter(l=>l!==i);t.scope==="project"?r.setProjectSkillPaths(o):r.setSkillPaths(o)}return await r.flush(),s.selected==="tui"?(s.commandContext&&await s.commandContext.reload(),s.current&&E(s.current)):(await s.workspaceSessions?.session?.resourceLoader.reload(),s.workspaceSessions?.session?.refreshContext()),await Fe()}function Er(t,e=Z){s.pi=t,t.on("session_start",(i,n)=>{s.replacementSessionId!==n.sessionManager.getSessionId()&&(s.replacementContext=null,s.replacementSessionId=null),E(n),s.selected==="tui"&&s.workspaces&&s.workspaces.add(n.cwd).then(async p=>{s.selected!=="tui"||s.current?.sessionManager.getSessionId()!==n.sessionManager.getSessionId()||(s.activeWorkspaceId=p.id,await M())}).catch(p=>{s.bridge?.publish({type:"error",message:p instanceof Error?p.message:"\u65E0\u6CD5\u6CE8\u518C\u5F53\u524D\u5DE5\u4F5C\u533A"})}),process.env.PI_WEBAPP_AUTO_OPEN==="1"&&n.mode==="tui"&&!s.autoOpened&&(s.autoOpened=!0,r(n).catch(p=>{n.ui.notify(p instanceof Error?p.message:"Web \u542F\u52A8\u5931\u8D25","error")}))}),t.on("agent_start",(i,n)=>E(n)),t.on("agent_end",(i,n)=>E(n)),t.on("agent_settled",(i,n)=>E(n)),t.on("model_select",(i,n)=>E(n)),t.on("thinking_level_select",(i,n)=>E(n)),t.on("message_end",(i,n)=>{s.selected==="tui"&&i.message.role==="assistant"&&s.bridge?.publish({type:"stream",message:null}),E(n),s.selected==="tui"&&M()}),t.on("session_info_changed",(i,n)=>E(n)),t.on("message_update",(i,n)=>{if(s.current=n,s.selected!=="tui"||i.message.role!=="assistant"||s.bridge===null)return;let p=Date.now();p-s.lastStreamAt<60||(s.lastStreamAt=p,s.bridge.publish({type:"stream",message:{id:"stream",role:"assistant",timestamp:new Date(i.message.timestamp).toISOString(),blocks:T(i.message.content)}}))}),t.on("session_shutdown",i=>{if(s.current=null,i.reason==="new"||i.reason==="resume"||i.reason==="fork")return;let n=s.bridge;s.bridge=null,s.commandContext=null,s.replacementContext=null,s.replacementSessionId=null,s.pi=null,s.workspaceSessions?.dispose(),s.workspaceSessions=null,s.selected="tui",s.activeWorkspaceId=null,s.autoOpened=!1,n&&n.close()});let r=async i=>{s.current=i,s.commandContext="newSession"in i?i:null,s.pi=t,s.workspaces===null&&(s.workspaces=new U(Te(N())),await s.workspaces.load());try{await s.workspaces.discover((await Oe.listAll()).map(o=>o.cwd))}catch(o){i.ui.notify(`\u65E0\u6CD5\u8BFB\u53D6\u90E8\u5206 Pi \u5386\u53F2\u5DE5\u4F5C\u533A\uFF1A${o instanceof Error?o.message:String(o)}`,"warning")}let n=await s.workspaces.add(i.cwd);if(s.selected==="tui"&&(s.activeWorkspaceId=n.id),s.bridge&&s.bridge.protocolVersion!==4){let o=s.bridge;s.bridge=null,await o.close()}s.bridge===null&&(s.workspaceSessions=new O(o=>{s.selected==="sdk"&&(s.bridge?.publish(o),o.type==="snapshot"&&o.session.idle&&M())},()=>s.current?.model),s.bridge=await Pe({selfUpdate:new $({agentDir:N(),packageRoot:Ue(Ve,"../.."),launcherNonce:process.env.PI_WEBAPP_LAUNCHER_NONCE}),snapshot:P,image(o,l,a){I(o);let h=(s.selected==="sdk"?s.workspaceSessions?.session?.sessionManager.getBranch():s.current?.sessionManager.getBranch())?.find(b=>b.id===l);if(h?.type!=="message"||h.message.role!=="user"||!Array.isArray(h.message.content))return null;let y=h.message.content[a];return y?.type!=="image"?null:{data:Buffer.from(y.data,"base64"),mimeType:y.mimeType}},async workspaces(){return De()},async addWorkspace(o,l){let a=await s.workspaces.add(o,l);try{await ee(a.id,!0)}catch(u){throw await M(),u}},async selectWorkspace(o){await ee(o)},async newSessionInWorkspace(o){await ee(o,!0)},async removeWorkspace(o){if(o===s.activeWorkspaceId)throw new Error("\u8BF7\u5148\u5207\u6362\u5230\u5176\u4ED6\u5DE5\u4F5C\u533A");await s.workspaces.remove(o),await M()},async selectSession(o,l,a){let u=s.workspaces?.get(o);if(!u)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");if(l===s.current?.sessionManager.getSessionId()&&u.path===await J(s.current.cwd)){if(s.selected==="sdk"&&s.workspaceSessions?.session&&!s.workspaceSessions.session.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u518D\u5207\u6362\u4F1A\u8BDD");s.selected="tui",s.activeWorkspaceId=o,s.bridge?.publish({type:"snapshot",session:P()})}else{if(s.selected==="sdk"&&s.workspaceSessions?.session?.sessionManager.getSessionId()===l&&s.workspaceSessions.path===u.path)return;if(!(await j(u.path)).some(y=>y.id===l&&y.path===a))throw new Error("\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u8BE5\u5DE5\u4F5C\u533A");await s.workspaceSessions.open(u.path,{sessionFile:a}),s.selected="sdk",s.activeWorkspaceId=o,s.bridge?.publish({type:"snapshot",session:P()})}await M()},async models(){if(s.selected==="sdk")return s.workspaceSessions?.models()??[];let o=s.current;if(!o)throw new Error("\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD");let l=o.scopedModels?.map(a=>`${a.model.provider}/${a.model.id}`)??[];return o.modelRegistry.getAvailable().filter(a=>l.length===0||l.includes(`${a.provider}/${a.id}`)).map(a=>({provider:a.provider,id:a.id,name:a.name}))},async providers(){let o=await R(),l=new Set((await o.listCredentials()).map(a=>a.providerId));return o.getProviders().map(a=>({id:a.id,name:a.name,configured:o.getProviderAuthStatus(a.id).configured,storedCredential:l.has(a.id),methods:[a.auth.apiKey?.login?"api_key":null,a.auth.oauth?"oauth":null].filter(u=>u!==null)}))},async providerModels(o){let l=await R();return ve(q(N(),"models.json"),o,l)},async updateProviderModel(o,l,a){if(I(o),s.selected==="tui"?!C(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let u=P().model;if(a.action==="remove"&&u===`${l}/${a.id}`)throw new Error("\u8BF7\u5148\u5207\u6362\u5F53\u524D\u4F7F\u7528\u7684\u6A21\u578B");if(a.action==="save"&&a.originalId&&a.originalId!==a.model.id&&u===`${l}/${a.originalId}`)throw new Error("\u8BF7\u5148\u5207\u6362\u5F53\u524D\u4F7F\u7528\u7684\u6A21\u578B");let h=await R();await ke(q(N(),"models.json"),l,a,h),await h.refresh({providers:[l],allowNetwork:!1}),await s.current?.modelRegistry.refresh({providers:[l],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[l],allowNetwork:!1});let y=a.action==="save"?a.model.id:a.action==="base_url"&&u?.startsWith(`${l}/`)?u.slice(l.length+1):null;if(y&&u===`${l}/${y}`)if(s.selected==="sdk")try{await s.workspaceSessions.setModel(l,y)}catch{throw new Error("\u6A21\u578B\u5DF2\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u8BE5\u6A21\u578B\u4EE5\u5E94\u7528\u65B0\u53C2\u6570")}else{let b=C(o),g=b.modelRegistry.find(l,y);if(!g||!s.pi||!await s.pi.setModel(g))throw new Error("\u6A21\u578B\u5DF2\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u8BE5\u6A21\u578B\u4EE5\u5E94\u7528\u65B0\u53C2\u6570");E(b)}},async logoutProvider(o,l){if(I(o),s.selected==="tui"?!C(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let a=await R();if(!(await a.listCredentials()).some(u=>u.providerId===l))throw new Error("\u8BE5\u63D0\u4F9B\u65B9\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u8BA4\u8BC1");await a.logout(l),await s.current?.modelRegistry.refresh({providers:[l],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[l],allowNetwork:!1})},async loginProvider(o,l,a){let u=await R();await u.login(o,l,me(o,l,a)),await u.refresh({providers:[o],allowNetwork:!1}),await s.current?.modelRegistry.refresh({providers:[o],allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({providers:[o],allowNetwork:!1})},async addCustomProvider(o,l){if(I(o),s.selected==="tui"?!C(o).isIdle():!s.workspaceSessions?.session?.isIdle)throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let a=await R();if(a.getProvider(l.id))throw new Error("\u63D0\u4F9B\u65B9\u5DF2\u5B58\u5728");await fe(q(N(),"models.json"),l),await a.refresh({allowNetwork:!1}),await s.current?.modelRegistry.refresh({allowNetwork:!1}),await s.workspaceSessions?.session?.modelRuntime.refresh({allowNetwork:!1})},commands(){return s.selected==="tui"?(s.pi?.getCommands()??[]).filter(o=>o.name!=="web").map(o=>({name:o.name,description:o.description})):[]},async compact(o){if(I(o),s.selected==="sdk"){let a=s.workspaceSessions?.session;if(!a||!a.isIdle)throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");await a.compact(),s.bridge?.publish({type:"snapshot",session:P()});return}let l=C(o);if(!l.isIdle())throw new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");l.compact()},async setModel(o,l,a){if(I(o),s.selected==="sdk"){await s.workspaceSessions.setModel(l,a);return}let u=C(o);if(!u.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");let h=u.modelRegistry.find(l,a);if(!h||!u.modelRegistry.getAvailable().some(y=>y.provider===l&&y.id===a))throw new Error("\u6A21\u578B\u4E0D\u53EF\u7528");if(!s.pi||!await s.pi.setModel(h))throw new Error("\u6A21\u578B\u63D0\u4F9B\u65B9\u672A\u914D\u7F6E\u8BA4\u8BC1");E(u)},async setThinkingLevel(o,l){if(I(o),!["off","minimal","low","medium","high","xhigh"].includes(l))throw new Error("\u63A8\u7406\u5F3A\u5EA6\u65E0\u6548");if(s.selected==="sdk"){let h=s.workspaceSessions?.session;if(!h||!h.isIdle||!h.getAvailableThinkingLevels().includes(l))throw new Error("\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u6B64\u63A8\u7406\u5F3A\u5EA6");h.setThinkingLevel(l),s.bridge?.publish({type:"snapshot",session:P()});return}let u=C(o);if(!u.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(!u.model||!kr(u.model).includes(l))throw new Error("\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u6B64\u63A8\u7406\u5F3A\u5EA6");if(!s.pi)throw new Error("Pi \u4F1A\u8BDD\u4E0D\u53EF\u7528");s.pi.setThinkingLevel(l),E(u)},config:Fe,updateConfig:xr,async send(o,l,a=[]){if(I(o),s.selected==="sdk"){await s.workspaceSessions.send(l,a);return}if(!C(o).isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(s.replacementContext!==null&&s.replacementSessionId===o){await s.replacementContext.sendUserMessage(a.length?[{type:"text",text:l},...a]:l,{expandPromptTemplates:!0});return}if(s.pi===null)throw new Error("Pi \u4F1A\u8BDD\u4E0D\u53EF\u7528");s.pi.sendUserMessage(a.length?[{type:"text",text:l},...a]:l,{expandPromptTemplates:!0})},abort(o){I(o),s.selected==="sdk"?s.workspaceSessions.stop():C(o).abort()},async newSession(o){if(I(o),s.selected==="sdk"){let u=s.workspaces?.get(s.activeWorkspaceId??"");if(!u)throw new Error("\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728");await s.workspaceSessions.open(u.path,"new"),await M();return}let l=C(o);if(!l.isIdle())throw new Error("Pi \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u7ED3\u675F");if(s.commandContext===null){await s.workspaceSessions.open(l.cwd,"new"),s.selected="sdk",s.bridge?.publish({type:"snapshot",session:P()}),await M();return}if((await s.commandContext.newSession({withSession:async u=>{s.commandContext=u,s.replacementContext=u,s.replacementSessionId=u.sessionManager.getSessionId(),s.current=u}})).cancelled)throw new Error("\u65B0\u4F1A\u8BDD\u5DF2\u53D6\u6D88");await M()}},Ve));let p=await e(s.bridge.url).catch(()=>!1);i.ui.notify(`pi-webapp: ${s.bridge.url}`,p?"info":"warning"),i.ui.setStatus("pi-webapp","Web \u5DF2\u542F\u52A8 \xB7 /web \u91CD\u65B0\u6253\u5F00")};t.registerCommand("web",{description:"Open the current Pi session in a local web interface",handler:async(i,n)=>r(n)})}export{Er as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-webapp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "A browser interface for Pi with live chat, workspaces, models, and attachments",
6
6
  "repository": {
@@ -15,7 +15,7 @@
15
15
  "pi-webapp": "./bin/pi-webapp.mjs"
16
16
  },
17
17
  "pi": {
18
- "image": "https://unpkg.com/pi-webapp@0.1.4/assets/cover-webapp.png",
18
+ "image": "https://cdn.jsdelivr.net/npm/pi-webapp@0.1.6/assets/cover-webapp.png",
19
19
  "extensions": [
20
20
  "./dist/extension.js"
21
21
  ]