@fugood/buttress-server 2.26.0-beta.4 → 2.26.0-beta.5

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.
@@ -43,6 +43,18 @@ export type StreamRunBody = {
43
43
  sessionId?: string;
44
44
  fork?: boolean;
45
45
  };
46
+ /**
47
+ * Whether a `--fork` chat has actually forked yet, i.e. whether the flag has
48
+ * been consumed and later prompts should continue rather than fork again.
49
+ *
50
+ * The server only forks once it accepts the run, and announces the new id
51
+ * (`session` event, or `result.sessionId` on an older server). A run that
52
+ * failed before that — a rejected token, an unknown agent, a dropped
53
+ * connection, an immediate Ctrl+C — leaves the client on the SOURCE session, so
54
+ * the flag has to survive: clearing it would make the user's retry append to
55
+ * the transcript `--fork` exists to keep untouched.
56
+ */
57
+ export declare const forkTookEffect: (startedFrom: string | null, sessionId: string | null) => boolean;
46
58
  /**
47
59
  * Run a prompt over the streaming endpoint, delivering every parsed frame to
48
60
  * `onFrame` and returning the terminal outcome. Frame handler errors are the
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import{a as e,i as t,n,r,t as i}from"./client-BCBBen9i.mjs";import a from"node:readline";const o=`\x1B[2m`,s=`\x1B[33m`,c=`\x1B[31m`,l=`\x1B[0m`,u=e=>{let t={name:null,configArg:null,url:null,token:null,sessionId:null,fork:!1,listSessions:!1,plain:!1};for(let n=0;n<e.length;n+=1){let r=e[n];r===`-h`||r===`--help`?(console.log(`
3
+ bricks-buttress agent — chat with a configured agent on a running server
4
+
5
+ Usage:
6
+ bricks-buttress agent [name] [options]
7
+
8
+ Options:
9
+ -c, --config <path|toml> Config file (finds server port + local token)
10
+ --url <url> Server base URL (default from config, else http://127.0.0.1:2080)
11
+ --token <token> Access token (default: the config's runtime token file)
12
+ --session <id> Continue an existing session
13
+ --fork <id> Fork a session, then continue the fork
14
+ --sessions List the agent's sessions and exit
15
+ --plain Line-based output (default when not a TTY)
16
+ -h, --help Show this help
17
+
18
+ In the chat: /exit quits, /new starts a fresh session, Ctrl+C aborts the
19
+ current run (a second Ctrl+C quits).
20
+ `),process.exit(0)):r===`-c`||r===`--config`?t.configArg=e[++n]??null:r===`--url`?t.url=e[++n]??null:r===`--token`?t.token=e[++n]??null:r===`--session`?t.sessionId=e[++n]??null:r===`--fork`?(t.sessionId=e[++n]??null,t.fork=!0):r===`--sessions`?t.listSessions=!0:r===`--plain`?t.plain=!0:!r.startsWith(`-`)&&!t.name?t.name=r:(console.error(`Unknown argument: ${r}`),process.exit(1))}return t},d=e=>{console.error(`${c}${e}${l}`),process.exit(1)},f=async(t,n,r,i,a,u)=>{let d=i,f=!1,p=!1,m=()=>{f&&=(process.stdout.write(`${l}\n`),!1)},h=await e(t,n,{prompt:r,sessionId:i??void 0,fork:a},{signal:u,onFrame:({event:e,payload:t})=>{if(e===`session`){t.sessionId&&t.sessionId!==d&&(d=t.sessionId,i||console.log(`${o}session ${d}${l}`));return}if(e!==`agent`)return;let n=t?.event;if(t?.type===`message_update`&&n)n.type===`thinking_delta`?(f||=(process.stdout.write(`${o}`),!0),process.stdout.write(n.delta??``),p=!0):n.type===`text_delta`&&(m(),process.stdout.write(n.delta??``),p=!0);else if(t?.type===`tool_execution_start`){m(),p&&process.stdout.write(`
21
+ `);let e=JSON.stringify(t.args??{});console.log(`${s}⚙ ${t.toolName}${l}${o}(${e.length>160?`${e.slice(0,160)}…`:e})${l}`),p=!1}else t?.type===`tool_execution_end`?console.log(t.isError?`${c}✗ ${t.toolName} failed${l}`:`${o}✓ ${t.toolName}${l}`):t?.type===`tool_emit`&&console.log(`${o} ${t.toolName} → ${t.event}${l}`)}});if(m(),h.kind===`result`){let e=h.result;d=e.sessionId??d;let t=e.usage||{};return process.stdout.write(`
22
+ `),console.log(`${o}— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${d}${l}`),{sessionId:d,ok:!0}}return h.kind===`aborted`?(console.log(`\n${s}(aborted)${l}`),{sessionId:h.sessionId??d,ok:!1}):h.kind===`error`?(console.error(`\n${c}Run failed: ${h.message}${l}`),{sessionId:h.sessionId??d,ok:!1}):(console.error(`\n${c}Connection lost mid-run.${l}`),{sessionId:h.sessionId??d,ok:!1})},p=async(e,t,n,r)=>{let s=n,u=r;console.log(`${t}${l} @ ${e.baseUrl}`+(s?` ${o}(${u?`forking`:`continuing`} ${s})${l}`:``)),console.log(`${o}/exit to quit, /new for a fresh session, Ctrl+C aborts a running turn${l}`);let d=a.createInterface({input:process.stdin,output:process.stdout}),p=!1;d.on(`close`,()=>{p=!0});let m=e=>new Promise(t=>{if(p){t(null);return}d.question(e,t),d.once(`close`,()=>t(null))}),h=null;for(d.on(`SIGINT`,()=>{h?(h.abort(),h=null):(d.close(),process.exit(0))});;){let n=await m(`> ${l}`);if(n==null)break;let r=n.trim();if(!r)continue;if(r===`/exit`||r===`/quit`)break;if(r===`/new`){s=null,u=!1,console.log(`${o}Started a fresh session.${l}`);continue}h=new AbortController;let a=s;try{let n=await f(e,t,r,s,u,h.signal);n.sessionId&&(s=n.sessionId),i(a,s)&&(u=!1)}catch(e){console.error(`${c}${e?.message||e}${l}`)}finally{h=null}}d.close()},m=async e=>{let i=u(e),{config:a,agentsConfig:s}=n(i.configArg),c=a.server.port||2080,f=(i.url||`http://127.0.0.1:${c}`).replace(/\/$/,``),m={baseUrl:f,token:i.token||(s?r(s.runtimeTokenFile):null)||process.env.BUTTRESS_AGENT_TOKEN||null},h=await t(m,`/agents`).catch(e=>d(`Cannot reach ${f}: ${e?.message||e} (is the server running?)`));if(!h.ok){let e=await h.text().catch(()=>``);d(`Server rejected the request (${h.status}): ${e.slice(0,300)}`)}let{agents:g}=await h.json();if(!i.name){console.log(`Configured agents on ${f}:`);for(let e of g)console.log(`- ${e}`);g.length===0&&console.log(`(none — add [[agents]] to the server config)`);return}if(g.includes(i.name)||d(`Unknown agent "${i.name}" (configured: ${g.join(`, `)||`none`})`),i.listSessions){let{sessions:e}=await(await t(m,`/agents/${encodeURIComponent(i.name)}/sessions`)).json();if(!e?.length){console.log(`No sessions yet.`);return}for(let t of e)console.log(`${t.sessionId} ${o}${t.updatedAt}${l}${t.parentSession?` ${o}(fork of ${t.parentSession})${l}`:``}`),t.preview&&console.log(` ${o}${t.preview}${l}`);return}if(process.stdin.isTTY&&process.stdout.isTTY&&!i.plain){let{runAgentTui:e}=await import(`./tui-7B7x6A08.mjs`);await e({connection:m,agentName:i.name,sessionId:i.sessionId,fork:i.fork});return}await p(m,i.name,i.sessionId,i.fork)};export{m as runAgentCommand};
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import{a as e,n as t}from"./config-lP89VahD.mjs";import n from"node:path";import r from"node:fs";import i from"@iarna/toml";const a=a=>{let o={},s=process.cwd();a&&(r.existsSync(a)?(o=i.parse(r.readFileSync(a,`utf8`)),s=n.dirname(n.resolve(a))):o=i.parse(a));let c=e(o);return{config:c,configDir:s,agentsConfig:t(c,{configDir:s})}},o=e=>{try{return r.readFileSync(e,`utf8`).trim()||null}catch{return null}},s=async(e,t,n)=>fetch(`${e.baseUrl}${t}`,{...n,headers:{"content-type":`application/json`,...e.token?{authorization:`Bearer ${e.token}`}:{},...n?.headers}});async function*c(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let{done:e,value:i}=await t.read();if(e)break;r+=n.decode(i,{stream:!0});let a=r.indexOf(`
3
+
4
+ `);for(;a!==-1;){let e=r.slice(0,a);r=r.slice(a+2),a=r.indexOf(`
5
+
6
+ `);let t=`message`,n=[];for(let r of e.split(`
7
+ `))r.startsWith(`event:`)?t=r.slice(6).trim():r.startsWith(`data:`)&&n.push(r.slice(5).trimStart());n.length>0&&(yield{event:t,data:n.join(`
8
+ `)})}}}finally{t.releaseLock()}}const l=(e,t)=>t!=null&&t!==e,u=async(e,t,n,{signal:r,onFrame:i})=>{let a=n.sessionId??null,o;try{o=await s(e,`/agents/${encodeURIComponent(t)}/run?stream=1`,{method:`POST`,body:JSON.stringify({prompt:n.prompt,...n.sessionId?{sessionId:n.sessionId}:{},...n.fork?{fork:!0}:{}}),signal:r})}catch(e){if(r?.aborted)return{kind:`aborted`,sessionId:a};throw e}if(!o.ok||!o.body){let e=await o.text().catch(()=>``);return{kind:`error`,message:`Run request failed (${o.status}): ${e.slice(0,400)}`,sessionId:a}}try{for await(let e of c(o.body)){let t;try{t=JSON.parse(e.data)}catch{continue}if(e.event===`session`&&t?.sessionId&&(a=t.sessionId),e.event===`result`)return i({event:e.event,payload:t}),{kind:`result`,result:t};if(e.event===`error`)return i({event:e.event,payload:t}),{kind:`error`,message:t?.message||`run failed`,sessionId:t?.sessionId??a};i({event:e.event,payload:t})}}catch(e){if(r?.aborted)return{kind:`aborted`,sessionId:a};throw e}return r?.aborted?{kind:`aborted`,sessionId:a}:{kind:`disconnected`,sessionId:a}};export{u as a,s as i,a as n,o as r,l as t};
package/lib/index.mjs CHANGED
@@ -628,7 +628,7 @@ Options:
628
628
  Up to date.`),process.exit(0)),console.log(`\nUpdate available: ${s} -> ${d}. Run 'bricks-buttress update' to install.`),process.exit(1)),p&&(console.log(`
629
629
  Already running the latest version.`),await Z9({message:`Reinstall anyway?`,defaultValue:!1,autoYes:t})||(console.log(`Skipped.`),process.exit(0)));let m=[`npm`,`bun`,`binary`].includes(c.method);m||console.log(`Cannot auto-detect installer (method: ${c.method}); falling back to binary install.`);let h=m?c.method:`binary`;await Z9({message:`Update using "${h}" method?`,defaultValue:!0,autoYes:t})||(console.log(`Cancelled.`),process.exit(0));let g=async e=>{console.log(`\n-> Running ${e} update...`);try{return await ixg(e,l,c,{ggmlVariant:o}),!0}catch(t){return console.error(`\n${e} update failed: ${t.message}`),!1}},_=await g(h),v=h;!_&&h!==`binary`&&await Z9({message:`Try the binary installer as a fallback?`,defaultValue:!0,autoYes:t})&&(v=`binary`,_=await g(`binary`)),_||(console.error(`
630
630
  Update failed. See messages above.`),v===`binary`&&console.error(`The previously installed version is still in place.`),process.exit(1)),console.log(`
631
- Update complete.`),process.exit(0)},Bxg=U7();var Vxg=async()=>{if(process.argv[2]===`update`){await zxg(process.argv.slice(3));return}if(process.argv[2]===`agent`){let{runAgentCommand:e}=await import(`./cli-w1S7I0HL.mjs`);await e(process.argv.slice(3)),process.exit(0)}(process.argv.includes(`--version`)||process.argv.includes(`-v`))&&(console.log(Bxg.version),process.exit(0)),(process.argv.includes(`--help`)||process.argv.includes(`-h`))&&(console.log(`
631
+ Update complete.`),process.exit(0)},Bxg=U7();var Vxg=async()=>{if(process.argv[2]===`update`){await zxg(process.argv.slice(3));return}if(process.argv[2]===`agent`){let{runAgentCommand:e}=await import(`./cli-DrbWX4ea.mjs`);await e(process.argv.slice(3)),process.exit(0)}(process.argv.includes(`--version`)||process.argv.includes(`-v`))&&(console.log(Bxg.version),process.exit(0)),(process.argv.includes(`--help`)||process.argv.includes(`-h`))&&(console.log(`
632
632
  bricks-buttress v${Bxg.version}
633
633
 
634
634
  Buttress server for remote inference with GGML backends.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{a as e,t}from"./client-BCBBen9i.mjs";import{Container as n,Editor as r,Loader as i,Markdown as a,ProcessTerminal as o,Text as s,TuiMainScreen as c}from"@earendil-works/pi-tui";const l=e=>t=>`\x1b[${e}m${t}\x1b[0m`,u=l(`2`),d=l(`1`),f=l(`36`),p=l(`33`),m=l(`31`),h=l(`32`),g=l(`35`),_={heading:e=>d(f(e)),link:f,linkUrl:e=>u(f(e)),code:p,codeBlock:p,codeBlockBorder:u,quote:u,quoteBorder:u,hr:u,listBullet:f,bold:d,italic:l(`3`),strikethrough:l(`9`),underline:l(`4`)},v={borderColor:u,selectList:{selectedPrefix:f,selectedText:d,description:u,scrollInfo:u,noMatch:u}},y=(e,t)=>e.length>t?`${e.slice(0,t)}…`:e;var b=class{tui;chat;thinking=null;thinkingBuffer=``;answer=null;answerBuffer=``;constructor(e,t){this.tui=e,this.chat=t}touch(){this.tui.requestRender()}appendThinking(e){this.thinking||(this.thinking=new s(``,0,0),this.chat.addChild(this.thinking)),this.thinkingBuffer+=e,this.thinking.setText(u(this.thinkingBuffer.trimEnd())),this.touch()}appendAnswer(e){this.answer||(this.answer=new a(``,0,0,_),this.chat.addChild(this.answer)),this.answerBuffer+=e,this.answer.setText(this.answerBuffer),this.touch()}addLine(e){this.answer=null,this.answerBuffer=``,this.chat.addChild(new s(e,0,0)),this.touch()}};const x=async({connection:a,agentName:l,sessionId:_,fork:x})=>{let S=new o,C=new c(S);S.setTitle(`buttress agent · ${l}`);let w=new n,T=new s(``,0,0),E=new r(C,v);C.addChild(w),C.addChild(T),C.addChild(E),C.setFocus(E);let D=_,O=x,k=null,A=null,j=e=>{T.setText(e),C.requestRender()},M=()=>{A||(A=new i(C,f,u,`thinking…`),w.addChild(A),C.requestRender())},N=()=>{A&&(w.removeChild(A),A=null,C.requestRender())},P=`${d(l)} ${u(`@ ${a.baseUrl}`)}`+(D?` ${u(`(${O?`forking`:`continuing`} ${D})`)}`:``);w.addChild(new s(P,0,0)),j(u(`Enter to send · /new fresh session · /exit or Ctrl+C to quit`));let F=async()=>{C.stop(),await S.drainInput().catch(()=>{}),process.exit(0)};C.addInputListener(e=>{if(e===``)return k?(k.abort(),k=null,{consume:!0}):(F(),{consume:!0})});let I=async n=>{w.addChild(new s(`${f(`>`)} ${n}`,0,0));let r=new b(C,w),i=D;k=new AbortController,M(),j(u(`running · Ctrl+C aborts this turn`));let o;try{o=await e(a,l,{prompt:n,sessionId:D??void 0,fork:O},{signal:k.signal,onFrame:({event:e,payload:t})=>{if(e===`session`&&t?.sessionId){t.sessionId!==D&&(D=t.sessionId,w.addChild(new s(u(`session ${D}`),0,0)));return}if(e!==`agent`)return;N();let n=t?.event;if(t?.type===`message_update`&&n)n.type===`thinking_delta`?r.appendThinking(n.delta??``):n.type===`text_delta`&&r.appendAnswer(n.delta??``);else if(t?.type===`tool_execution_start`){let e=JSON.stringify(t.args??{});r.addLine(`${p(`⚙ ${t.toolName}`)}${u(`(${y(e,120)})`)}`)}else t?.type===`tool_execution_end`?r.addLine(t.isError?m(`✗ ${t.toolName} failed`):u(`✓ ${t.toolName}`)):t?.type===`tool_emit`&&r.addLine(u(` ${t.toolName} → ${t.event}`))}})}catch(e){o={kind:`error`,message:e?.message||String(e),sessionId:D}}if(N(),k=null,o.kind===`result`){let{result:e}=o;D=e.sessionId??D;let t=e.usage||{};w.addChild(new s(u(`— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${D}`),0,0))}else o.kind===`aborted`?(D=o.sessionId??D,w.addChild(new s(p(`(aborted — session is continuable)`),0,0))):o.kind===`error`?(D=o.sessionId??D,w.addChild(new s(m(`Run failed: ${o.message}`),0,0))):w.addChild(new s(m(`Connection lost mid-run.`),0,0));t(i,D)&&(O=!1),j(u(`Enter to send · /new fresh session · /exit or Ctrl+C to quit`)),C.requestRender()};E.onSubmit=e=>{let t=e.trim();if(E.setText(``),t){if(k){j(p(`A turn is still running — Ctrl+C aborts it.`));return}if(t===`/exit`||t===`/quit`){F();return}if(t===`/new`){D=null,O=!1,w.addChild(new s(`${h(`•`)} ${u(`started a fresh session`)}`,0,0)),C.requestRender();return}if(t===`/session`){w.addChild(new s(u(D?`session ${D}`:`no session yet`),0,0)),C.requestRender();return}I(t)}},w.addChild(new s(u(g(`ready`)),0,0)),C.start(),await new Promise(()=>{})};export{x as runAgentTui};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fugood/buttress-server",
3
- "version": "2.26.0-beta.4",
3
+ "version": "2.26.0-beta.5",
4
4
  "main": "lib/index.mjs",
5
5
  "types": "lib/index.d.ts",
6
6
  "type": "module",
@@ -72,5 +72,5 @@
72
72
  "tsdown": "^0.22.4",
73
73
  "typescript": "^7.0.2"
74
74
  },
75
- "gitHead": "13b0d97e181e2d090b6df915b446c4f7673af04f"
75
+ "gitHead": "8ac1ca8f5561e0c89bf5bc3bc81aaf8125f3d57e"
76
76
  }
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env node
2
- import{i as e,n as t,r as n,t as r}from"./client-Dk7Dx_vo.mjs";import i from"node:readline";const a=`\x1B[2m`,o=`\x1B[33m`,s=`\x1B[31m`,c=`\x1B[0m`,l=e=>{let t={name:null,configArg:null,url:null,token:null,sessionId:null,fork:!1,listSessions:!1,plain:!1};for(let n=0;n<e.length;n+=1){let r=e[n];r===`-h`||r===`--help`?(console.log(`
3
- bricks-buttress agent — chat with a configured agent on a running server
4
-
5
- Usage:
6
- bricks-buttress agent [name] [options]
7
-
8
- Options:
9
- -c, --config <path|toml> Config file (finds server port + local token)
10
- --url <url> Server base URL (default from config, else http://127.0.0.1:2080)
11
- --token <token> Access token (default: the config's runtime token file)
12
- --session <id> Continue an existing session
13
- --fork <id> Fork a session, then continue the fork
14
- --sessions List the agent's sessions and exit
15
- --plain Line-based output (default when not a TTY)
16
- -h, --help Show this help
17
-
18
- In the chat: /exit quits, /new starts a fresh session, Ctrl+C aborts the
19
- current run (a second Ctrl+C quits).
20
- `),process.exit(0)):r===`-c`||r===`--config`?t.configArg=e[++n]??null:r===`--url`?t.url=e[++n]??null:r===`--token`?t.token=e[++n]??null:r===`--session`?t.sessionId=e[++n]??null:r===`--fork`?(t.sessionId=e[++n]??null,t.fork=!0):r===`--sessions`?t.listSessions=!0:r===`--plain`?t.plain=!0:!r.startsWith(`-`)&&!t.name?t.name=r:(console.error(`Unknown argument: ${r}`),process.exit(1))}return t},u=e=>{console.error(`${s}${e}${c}`),process.exit(1)},d=async(t,n,r,i,l,u)=>{let d=i,f=!1,p=!1,m=()=>{f&&=(process.stdout.write(`${c}\n`),!1)},h=await e(t,n,{prompt:r,sessionId:i??void 0,fork:l},{signal:u,onFrame:({event:e,payload:t})=>{if(e===`session`){t.sessionId&&t.sessionId!==d&&(d=t.sessionId,i||console.log(`${a}session ${d}${c}`));return}if(e!==`agent`)return;let n=t?.event;if(t?.type===`message_update`&&n)n.type===`thinking_delta`?(f||=(process.stdout.write(`${a}`),!0),process.stdout.write(n.delta??``),p=!0):n.type===`text_delta`&&(m(),process.stdout.write(n.delta??``),p=!0);else if(t?.type===`tool_execution_start`){m(),p&&process.stdout.write(`
21
- `);let e=JSON.stringify(t.args??{});console.log(`${o}⚙ ${t.toolName}${c}${a}(${e.length>160?`${e.slice(0,160)}…`:e})${c}`),p=!1}else t?.type===`tool_execution_end`?console.log(t.isError?`${s}✗ ${t.toolName} failed${c}`:`${a}✓ ${t.toolName}${c}`):t?.type===`tool_emit`&&console.log(`${a} ${t.toolName} → ${t.event}${c}`)}});if(m(),h.kind===`result`){let e=h.result;d=e.sessionId??d;let t=e.usage||{};return process.stdout.write(`
22
- `),console.log(`${a}— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${d}${c}`),{sessionId:d,ok:!0}}return h.kind===`aborted`?(console.log(`\n${o}(aborted)${c}`),{sessionId:h.sessionId??d,ok:!1}):h.kind===`error`?(console.error(`\n${s}Run failed: ${h.message}${c}`),{sessionId:h.sessionId??d,ok:!1}):(console.error(`\n${s}Connection lost mid-run.${c}`),{sessionId:h.sessionId??d,ok:!1})},f=async(e,t,n,r)=>{let o=n,l=r;console.log(`${t}${c} @ ${e.baseUrl}`+(o?` ${a}(${l?`forking`:`continuing`} ${o})${c}`:``)),console.log(`${a}/exit to quit, /new for a fresh session, Ctrl+C aborts a running turn${c}`);let u=i.createInterface({input:process.stdin,output:process.stdout}),f=!1;u.on(`close`,()=>{f=!0});let p=e=>new Promise(t=>{if(f){t(null);return}u.question(e,t),u.once(`close`,()=>t(null))}),m=null;for(u.on(`SIGINT`,()=>{m?(m.abort(),m=null):(u.close(),process.exit(0))});;){let n=await p(`> ${c}`);if(n==null)break;let r=n.trim();if(r){if(r===`/exit`||r===`/quit`)break;if(r===`/new`){o=null,l=!1,console.log(`${a}Started a fresh session.${c}`);continue}m=new AbortController;try{let n=await d(e,t,r,o,l,m.signal);n.sessionId&&(o=n.sessionId),l=!1}catch(e){console.error(`${s}${e?.message||e}${c}`)}finally{m=null}}}u.close()},p=async e=>{let i=l(e),{config:o,agentsConfig:s}=r(i.configArg),d=o.server.port||2080,p=(i.url||`http://127.0.0.1:${d}`).replace(/\/$/,``),m={baseUrl:p,token:i.token||(s?t(s.runtimeTokenFile):null)||process.env.BUTTRESS_AGENT_TOKEN||null},h=await n(m,`/agents`).catch(e=>u(`Cannot reach ${p}: ${e?.message||e} (is the server running?)`));if(!h.ok){let e=await h.text().catch(()=>``);u(`Server rejected the request (${h.status}): ${e.slice(0,300)}`)}let{agents:g}=await h.json();if(!i.name){console.log(`Configured agents on ${p}:`);for(let e of g)console.log(`- ${e}`);g.length===0&&console.log(`(none — add [[agents]] to the server config)`);return}if(g.includes(i.name)||u(`Unknown agent "${i.name}" (configured: ${g.join(`, `)||`none`})`),i.listSessions){let{sessions:e}=await(await n(m,`/agents/${encodeURIComponent(i.name)}/sessions`)).json();if(!e?.length){console.log(`No sessions yet.`);return}for(let t of e)console.log(`${t.sessionId} ${a}${t.updatedAt}${c}${t.parentSession?` ${a}(fork of ${t.parentSession})${c}`:``}`),t.preview&&console.log(` ${a}${t.preview}${c}`);return}if(process.stdin.isTTY&&process.stdout.isTTY&&!i.plain){let{runAgentTui:e}=await import(`./tui-Ca5GU7rB.mjs`);await e({connection:m,agentName:i.name,sessionId:i.sessionId,fork:i.fork});return}await f(m,i.name,i.sessionId,i.fork)};export{p as runAgentCommand};
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as e,n as t}from"./config-lP89VahD.mjs";import n from"node:path";import r from"node:fs";import i from"@iarna/toml";const a=a=>{let o={},s=process.cwd();a&&(r.existsSync(a)?(o=i.parse(r.readFileSync(a,`utf8`)),s=n.dirname(n.resolve(a))):o=i.parse(a));let c=e(o);return{config:c,configDir:s,agentsConfig:t(c,{configDir:s})}},o=e=>{try{return r.readFileSync(e,`utf8`).trim()||null}catch{return null}},s=async(e,t,n)=>fetch(`${e.baseUrl}${t}`,{...n,headers:{"content-type":`application/json`,...e.token?{authorization:`Bearer ${e.token}`}:{},...n?.headers}});async function*c(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let{done:e,value:i}=await t.read();if(e)break;r+=n.decode(i,{stream:!0});let a=r.indexOf(`
3
-
4
- `);for(;a!==-1;){let e=r.slice(0,a);r=r.slice(a+2),a=r.indexOf(`
5
-
6
- `);let t=`message`,n=[];for(let r of e.split(`
7
- `))r.startsWith(`event:`)?t=r.slice(6).trim():r.startsWith(`data:`)&&n.push(r.slice(5).trimStart());n.length>0&&(yield{event:t,data:n.join(`
8
- `)})}}}finally{t.releaseLock()}}const l=async(e,t,n,{signal:r,onFrame:i})=>{let a=n.sessionId??null,o;try{o=await s(e,`/agents/${encodeURIComponent(t)}/run?stream=1`,{method:`POST`,body:JSON.stringify({prompt:n.prompt,...n.sessionId?{sessionId:n.sessionId}:{},...n.fork?{fork:!0}:{}}),signal:r})}catch(e){if(r?.aborted)return{kind:`aborted`,sessionId:a};throw e}if(!o.ok||!o.body){let e=await o.text().catch(()=>``);return{kind:`error`,message:`Run request failed (${o.status}): ${e.slice(0,400)}`,sessionId:a}}try{for await(let e of c(o.body)){let t;try{t=JSON.parse(e.data)}catch{continue}if(e.event===`session`&&t?.sessionId&&(a=t.sessionId),e.event===`result`)return i({event:e.event,payload:t}),{kind:`result`,result:t};if(e.event===`error`)return i({event:e.event,payload:t}),{kind:`error`,message:t?.message||`run failed`,sessionId:t?.sessionId??a};i({event:e.event,payload:t})}}catch(e){if(r?.aborted)return{kind:`aborted`,sessionId:a};throw e}return r?.aborted?{kind:`aborted`,sessionId:a}:{kind:`disconnected`,sessionId:a}};export{l as i,o as n,s as r,a as t};
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{i as e}from"./client-Dk7Dx_vo.mjs";import{Container as t,Editor as n,Loader as r,Markdown as i,ProcessTerminal as a,Text as o,TuiMainScreen as s}from"@earendil-works/pi-tui";const c=e=>t=>`\x1b[${e}m${t}\x1b[0m`,l=c(`2`),u=c(`1`),d=c(`36`),f=c(`33`),p=c(`31`),m=c(`32`),h=c(`35`),g={heading:e=>u(d(e)),link:d,linkUrl:e=>l(d(e)),code:f,codeBlock:f,codeBlockBorder:l,quote:l,quoteBorder:l,hr:l,listBullet:d,bold:u,italic:c(`3`),strikethrough:c(`9`),underline:c(`4`)},_={borderColor:l,selectList:{selectedPrefix:d,selectedText:u,description:l,scrollInfo:l,noMatch:l}},v=(e,t)=>e.length>t?`${e.slice(0,t)}…`:e;var y=class{tui;chat;thinking=null;thinkingBuffer=``;answer=null;answerBuffer=``;constructor(e,t){this.tui=e,this.chat=t}touch(){this.tui.requestRender()}appendThinking(e){this.thinking||(this.thinking=new o(``,0,0),this.chat.addChild(this.thinking)),this.thinkingBuffer+=e,this.thinking.setText(l(this.thinkingBuffer.trimEnd())),this.touch()}appendAnswer(e){this.answer||(this.answer=new i(``,0,0,g),this.chat.addChild(this.answer)),this.answerBuffer+=e,this.answer.setText(this.answerBuffer),this.touch()}addLine(e){this.answer=null,this.answerBuffer=``,this.chat.addChild(new o(e,0,0)),this.touch()}};const b=async({connection:i,agentName:c,sessionId:g,fork:b})=>{let x=new a,S=new s(x);x.setTitle(`buttress agent · ${c}`);let C=new t,w=new o(``,0,0),T=new n(S,_);S.addChild(C),S.addChild(w),S.addChild(T),S.setFocus(T);let E=g,D=b,O=null,k=null,A=e=>{w.setText(e),S.requestRender()},j=()=>{k||(k=new r(S,d,l,`thinking…`),C.addChild(k),S.requestRender())},M=()=>{k&&(C.removeChild(k),k=null,S.requestRender())},N=`${u(c)} ${l(`@ ${i.baseUrl}`)}`+(E?` ${l(`(${D?`forking`:`continuing`} ${E})`)}`:``);C.addChild(new o(N,0,0)),A(l(`Enter to send · /new fresh session · /exit or Ctrl+C to quit`));let P=async()=>{S.stop(),await x.drainInput().catch(()=>{}),process.exit(0)};S.addInputListener(e=>{if(e===``)return O?(O.abort(),O=null,{consume:!0}):(P(),{consume:!0})});let F=async t=>{C.addChild(new o(`${d(`>`)} ${t}`,0,0));let n=new y(S,C);O=new AbortController,j(),A(l(`running · Ctrl+C aborts this turn`));let r;try{r=await e(i,c,{prompt:t,sessionId:E??void 0,fork:D},{signal:O.signal,onFrame:({event:e,payload:t})=>{if(e===`session`&&t?.sessionId){t.sessionId!==E&&(E=t.sessionId,C.addChild(new o(l(`session ${E}`),0,0)));return}if(e!==`agent`)return;M();let r=t?.event;if(t?.type===`message_update`&&r)r.type===`thinking_delta`?n.appendThinking(r.delta??``):r.type===`text_delta`&&n.appendAnswer(r.delta??``);else if(t?.type===`tool_execution_start`){let e=JSON.stringify(t.args??{});n.addLine(`${f(`⚙ ${t.toolName}`)}${l(`(${v(e,120)})`)}`)}else t?.type===`tool_execution_end`?n.addLine(t.isError?p(`✗ ${t.toolName} failed`):l(`✓ ${t.toolName}`)):t?.type===`tool_emit`&&n.addLine(l(` ${t.toolName} → ${t.event}`))}})}catch(e){r={kind:`error`,message:e?.message||String(e),sessionId:E}}if(M(),O=null,D=!1,r.kind===`result`){let{result:e}=r;E=e.sessionId??E;let t=e.usage||{};C.addChild(new o(l(`— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${E}`),0,0))}else r.kind===`aborted`?(E=r.sessionId??E,C.addChild(new o(f(`(aborted — session is continuable)`),0,0))):r.kind===`error`?(E=r.sessionId??E,C.addChild(new o(p(`Run failed: ${r.message}`),0,0))):C.addChild(new o(p(`Connection lost mid-run.`),0,0));A(l(`Enter to send · /new fresh session · /exit or Ctrl+C to quit`)),S.requestRender()};T.onSubmit=e=>{let t=e.trim();if(T.setText(``),t){if(O){A(f(`A turn is still running — Ctrl+C aborts it.`));return}if(t===`/exit`||t===`/quit`){P();return}if(t===`/new`){E=null,D=!1,C.addChild(new o(`${m(`•`)} ${l(`started a fresh session`)}`,0,0)),S.requestRender();return}if(t===`/session`){C.addChild(new o(l(E?`session ${E}`:`no session yet`),0,0)),S.requestRender();return}F(t)}},C.addChild(new o(l(h(`ready`)),0,0)),S.start(),await new Promise(()=>{})};export{b as runAgentTui};