pi-web-ui 0.80.0 → 0.80.1
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/CHANGELOG.md +20 -3
- package/README.md +10 -8
- package/README.zh-CN.md +1 -1
- package/bin/pi-web-ui.mjs +21 -4
- package/dist/server/agent-service.js +22 -7
- package/dist/server/dsh/dsh-agent-service.js +16 -6
- package/dist/server/index.js +38 -1
- package/dist/server/launch-origin.js +148 -0
- package/dist/server/slash-commands.js +19 -3
- package/dist/server/terminals.js +17 -9
- package/package.json +1 -1
- package/web/dist/assets/{TerminalPanel-BYigWYyx.js → TerminalPanel-DrYGGZSV.js} +1 -1
- package/web/dist/assets/{index-D7BpHJ59.js → index-BFA8Pn_5.js} +40 -40
- package/web/dist/assets/{index-GKv6Qo91.css → index-BduNm7_u.css} +1 -1
- package/web/dist/index.html +2 -2
package/dist/server/terminals.js
CHANGED
|
@@ -1366,18 +1366,24 @@ let oneShotBashSeq = 0;
|
|
|
1366
1366
|
/** 应用 head / tail 参数到输出顶层行(替代 `| head` / `| tail` 管道——管道会
|
|
1367
1367
|
* 缓冲输出、让可见终端全程哑火,还容易白白触发静默解阻)。两者同时给时先
|
|
1368
1368
|
* 截头再截尾。 */
|
|
1369
|
-
export function applyHeadTail(text, head, tail) {
|
|
1369
|
+
export function applyHeadTail(text, head, tail, lang = "en") {
|
|
1370
1370
|
// 只对真实数据行切片;省略提示行单独存,最后再包回输出,避免提示行在
|
|
1371
1371
|
// head+tail 组合时被当成数据行参与第二次截取(导致尾部少截一行)。
|
|
1372
1372
|
let data = text.split("\n");
|
|
1373
1373
|
let headNote = null;
|
|
1374
1374
|
let tailNote = null;
|
|
1375
1375
|
if (head && head > 0 && data.length > head) {
|
|
1376
|
-
|
|
1376
|
+
const n = data.length - head;
|
|
1377
|
+
headNote = pick(lang, `…(后 ${n} 行已省略)`, `…[${n} lines omitted below]…`, "terminals.headtail.omitted.below", {
|
|
1378
|
+
n,
|
|
1379
|
+
});
|
|
1377
1380
|
data = data.slice(0, head);
|
|
1378
1381
|
}
|
|
1379
1382
|
if (tail && tail > 0 && data.length > tail) {
|
|
1380
|
-
|
|
1383
|
+
const n = data.length - tail;
|
|
1384
|
+
tailNote = pick(lang, `…(前 ${n} 行已省略)`, `…[${n} lines omitted above]…`, "terminals.headtail.omitted.above", {
|
|
1385
|
+
n,
|
|
1386
|
+
});
|
|
1381
1387
|
data = data.slice(-tail);
|
|
1382
1388
|
}
|
|
1383
1389
|
const parts = [];
|
|
@@ -1460,10 +1466,12 @@ export function makeTerminalBashTool(terminals, opts) {
|
|
|
1460
1466
|
const redirect = stripped && limiter.kind === "tail" ? detectStdoutRedirect(runCommand) : null;
|
|
1461
1467
|
const tailFile = redirect ? { file: redirect.file, lines: limiter.lines ?? 10 } : undefined;
|
|
1462
1468
|
// 复杂子表达式先 hoist 成干净 const(issue #91 v2:vars key 不写复杂表达式)。
|
|
1463
|
-
|
|
1464
|
-
const
|
|
1465
|
-
const
|
|
1466
|
-
const
|
|
1469
|
+
// 注意:limiter 对「没有尾部限输出管道」的命令是 null(issue #121)——
|
|
1470
|
+
// 这些 const 一律走可选链,只在 stripped=true(limiterNote 才被取用)时才有意义。
|
|
1471
|
+
const limiterSegment = limiter?.segment ?? "";
|
|
1472
|
+
const limiterTailLines = limiter?.lines ?? 10;
|
|
1473
|
+
const limiterTailZh = limiter?.kind === "tail" ? `本次返回末尾 ${limiterTailLines} 行。` : "本次返回全部输出。";
|
|
1474
|
+
const limiterTailEn = limiter?.kind === "tail"
|
|
1467
1475
|
? `Returning the last ${limiterTailLines} lines this time.`
|
|
1468
1476
|
: "Returning the full output this time.";
|
|
1469
1477
|
const limiterNote = stripped
|
|
@@ -1497,7 +1505,7 @@ export function makeTerminalBashTool(terminals, opts) {
|
|
|
1497
1505
|
if (m) {
|
|
1498
1506
|
terminals.setSentinelPending(termId, false);
|
|
1499
1507
|
closeOneShot();
|
|
1500
|
-
const text = applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail);
|
|
1508
|
+
const text = applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail, lang);
|
|
1501
1509
|
return {
|
|
1502
1510
|
content: [
|
|
1503
1511
|
{
|
|
@@ -1517,7 +1525,7 @@ export function makeTerminalBashTool(terminals, opts) {
|
|
|
1517
1525
|
}
|
|
1518
1526
|
// 静默解阻(仅持久终端):转后台 + 注册完成观察器,立即把控制权还给模型。
|
|
1519
1527
|
if (persist && idleMs > 0 && Date.now() - lastDataAt >= idleMs) {
|
|
1520
|
-
return backgroundResult(terminals, opts, runCommand, applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail), Math.round((Date.now() - lastDataAt) / 1000), lang);
|
|
1528
|
+
return backgroundResult(terminals, opts, runCommand, applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail, lang), Math.round((Date.now() - lastDataAt) / 1000), lang);
|
|
1521
1529
|
}
|
|
1522
1530
|
}
|
|
1523
1531
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.1",
|
|
4
4
|
"description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as l,j as n}from"./markdown-D3PKeHAZ.js";import{u as P,b as O,T as M,a as p,c as W,F as Y,d as z,e as Z,f as H,g as ee,h as ne,i as te,j as se,r as ae}from"./index-
|
|
1
|
+
import{a as l,j as n}from"./markdown-D3PKeHAZ.js";import{u as P,b as O,T as M,a as p,c as W,F as Y,d as z,e as Z,f as H,g as ee,h as ne,i as te,j as se,r as ae}from"./index-BFA8Pn_5.js";import{D as re,o as ie}from"./xterm-B96xOxS9.js";import"./react-w24rH0km.js";function le(t){let c=null;return{clean:t.replace(/\r?\n?\[pi-term-exit:(-?\d+)\]\r?\n?/g,(h,u)=>(c=Number(u),`\r
|
|
2
2
|
`)).replace(/\r?\n?\x1b\[90m\[(?:进程已退出,退出码 |Process exited with code )-?\d+\]\x1b\[0m\r?\n?/g,`\r
|
|
3
3
|
`),exitCode:c}}function ce({conversationId:t,terminalId:c,command:s,cwd:h,title:u,active:w,running:f,exitCode:k,register:g}){const C=l.useRef(null),j=l.useRef(null),{locale:o}=P(),y=l.useRef(o);y.current=o;const E=s?JSON.stringify(s):"";l.useEffect(()=>{const m=C.current;if(!m)return;const r=new re({theme:O(),fontFamily:'"SF Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace',fontSize:13,cursorBlink:!0,scrollback:8e3}),v=new ie;r.loadAddon(v),r.open(m),j.current={term:r,fit:v},w&&r.focus();const b=()=>{r.options.theme=O()};window.addEventListener(M,b),r.attachCustomKeyEventHandler(d=>{if(d.type!=="keydown")return!0;const S=d.key?.toLowerCase();if((d.ctrlKey||d.metaKey)&&S==="v")return!1;if(d.ctrlKey&&!d.shiftKey&&!d.altKey&&S==="c"&&r.hasSelection()){const D=r.textarea;return D&&(D.value=r.getSelection(),D.select()),!1}return!0});const _=g(t,c,{write:d=>r.write(le(d).clean),dispose:()=>r.dispose()}),$=()=>{try{v.fit(),p({type:"terminal_resize",terminalId:c,conversationId:t,cols:r.cols,rows:r.rows})}catch{}},B=requestAnimationFrame(()=>{try{v.fit()}catch{}s?p({type:"run_command",terminalId:c,conversationId:t,command:s,cols:r.cols,rows:r.rows}):p({type:"terminal_create",terminalId:c,title:u,locale:y.current,conversationId:t,cwd:h,cols:r.cols,rows:r.rows})}),R=r.onData(d=>{p({type:"terminal_input",terminalId:c,conversationId:t,data:d})});let T=null;return typeof ResizeObserver<"u"&&(T=new ResizeObserver(()=>{m.offsetWidth>0&&m.offsetHeight>0&&$()}),T.observe(m)),()=>{cancelAnimationFrame(B),R.dispose(),window.removeEventListener(M,b),T?.disconnect(),_(),r.dispose(),j.current=null}},[t,c,E,g]),l.useEffect(()=>{if(!w)return;const m=requestAnimationFrame(()=>{const r=j.current;if(r){try{r.fit.fit(),p({type:"terminal_resize",terminalId:c,conversationId:t,cols:r.term.cols,rows:r.term.rows})}catch{}r.term.focus()}});return()=>cancelAnimationFrame(m)},[w]);const{t:F}=P(),x=l.useRef(void 0);return l.useEffect(()=>{if(f===x.current||(x.current=f,f!==!1))return;const m=j.current;m&&m.term.write(`\r
|
|
4
4
|
\x1B[90m${F("exitBanner",{code:k??""})}\x1B[0m\r
|