@fugood/buttress-server 2.26.0-beta.19 → 2.26.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Agent endpoints (EXPERIMENTAL).
3
3
  *
4
- * GET /agents configured agent names
4
+ * GET /agents configured agents: names, plus details
5
+ * (model, tools, MCP server names, limits)
5
6
  * POST /agents/:name/run run a prompt; ?stream=1 for SSE events
6
7
  * (first a `session` event with the id,
7
8
  * then `agent` events, then result/error)
@@ -18,12 +19,40 @@
18
19
  * accepts workspace JWTs. The server's own ephemeral internal token (also
19
20
  * written to the 0600 runtime file for same-host CLIs) is always accepted.
20
21
  */
21
- import { type AgentsService } from '../agent/types';
22
+ import { type AgentDefinition, type AgentsService } from '../agent/types';
23
+ /**
24
+ * What a client may show about an agent. MCP servers are listed by name only:
25
+ * their urls, headers, commands and env can hold credentials.
26
+ */
27
+ export declare const describeAgent: (agent: AgentDefinition) => {
28
+ name: string;
29
+ model: string;
30
+ tools: string[];
31
+ mcpServers: string[];
32
+ maxTurns: number;
33
+ localDevices: boolean;
34
+ codemode: boolean;
35
+ decisionModel: string | null;
36
+ };
37
+ export type ToolResultSummary = {
38
+ text: string | null;
39
+ images: number;
40
+ truncated: boolean;
41
+ };
42
+ /**
43
+ * A tool result for the wire: its text (bounded) and how many images it holds.
44
+ * Image bytes never go out; the transcript endpoint has the full result.
45
+ *
46
+ * pi passes the tool's own return value: normally `{ content: (text | image)[] }`,
47
+ * but untyped tools may return a bare string or no content at all.
48
+ */
49
+ export declare const summarizeToolResult: (result: unknown) => ToolResultSummary;
22
50
  /**
23
51
  * Slim pi agent events for the wire. `message_update` carries the FULL partial
24
52
  * assistant message on every delta — per-token that would dwarf the payload —
25
- * and tool results can be huge; clients that want the transcript fetch it
26
- * from the sessions endpoint afterwards.
53
+ * and tool results can be huge (`tool_execution_end` carries a bounded text
54
+ * summary); clients that want the full transcript fetch it from the sessions
55
+ * endpoint afterwards.
27
56
  */
28
57
  export declare const slimAgentEvent: (event: any) => unknown;
29
58
  export declare function streamAgentRun(agents: AgentsService, name: string, body: {
@@ -1,6 +1,7 @@
1
1
  /**
2
- * OpenAI-compatible API endpoints for ggml-llm (EXPERIMENTAL)
3
- * Provides /oai-compat/v1/chat/completions and /oai-compat/v1/models endpoints
2
+ * OpenAI-compatible API endpoints for ggml-llm and ggml-decision (EXPERIMENTAL)
3
+ * Provides /oai-compat/v1/chat/completions, /oai-compat/v1/decisions and
4
+ * /oai-compat/v1/models endpoints
4
5
  *
5
6
  * Enable via TOML config: [openai_compat] enabled = true
6
7
  *
@@ -14,4 +15,14 @@ import type { EventStream } from '../types';
14
15
  export declare function streamChatCompletion(completionStream: ReadableStream<EventStream>, completionId: string, created: number, modelId: string, includeUsage: boolean): AsyncGenerator<{
15
16
  readonly data: string;
16
17
  }, void, unknown>;
18
+ /**
19
+ * Model id -> what this server can do with it, from the config alone (no model
20
+ * load), in the `architecture` shape that llama.cpp server and OpenRouter
21
+ * publish: `text` output means chat completions, `decisions` means
22
+ * `/v1/decisions`. Clients should test membership and ignore unknown values.
23
+ */
24
+ export declare const collectModelArchitectures: (generators: any[]) => Map<string, {
25
+ input_modalities: string[];
26
+ output_modalities: string[];
27
+ }>;
17
28
  export default function factory(): import("../types").ButtressApp;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * OpenAI Decisions API (`POST /v1/decisions`) <-> native decision requests.
3
+ *
4
+ * Spec: https://developers.openai.com/api/docs/guides/decisions
5
+ *
6
+ * The native request follows the TypeSafe `/v1/systemone` contract, so the
7
+ * question types map one to one: `predicate` -> `noul`, `choice` -> `choice`,
8
+ * `score` -> `score`. What differs is the shape: OpenAI sends an ordered array
9
+ * of optionally named questions with typed choice values and returns an
10
+ * ordered array of answers; native keys questions and options by string.
11
+ * The local models never refuse, so no `refusal` answer is produced.
12
+ */
13
+ import type { DecisionRequest, DecisionResult } from '../types';
14
+ type ChoiceValue = string | boolean;
15
+ type QuestionPlan = {
16
+ id: string;
17
+ name: string | null;
18
+ type: 'predicate';
19
+ } | {
20
+ id: string;
21
+ name: string | null;
22
+ type: 'choice';
23
+ options: {
24
+ key: string;
25
+ value: ChoiceValue;
26
+ }[];
27
+ } | {
28
+ id: string;
29
+ name: string | null;
30
+ type: 'score';
31
+ labels: string[];
32
+ };
33
+ export type DecisionPlan = {
34
+ questions: QuestionPlan[];
35
+ };
36
+ export declare class DecisionRequestError extends Error {
37
+ param: string | null;
38
+ constructor(param: string | null, message: string);
39
+ }
40
+ /**
41
+ * Validate an OpenAI decision request and convert it to the native one.
42
+ * Throws a DecisionRequestError naming the offending field.
43
+ */
44
+ export declare const toNativeDecisionRequest: (body: unknown) => {
45
+ model: string;
46
+ request: DecisionRequest;
47
+ plan: DecisionPlan;
48
+ };
49
+ /** Convert a native result to an OpenAI `Decision` object, answers in request order. */
50
+ export declare const toOpenAIDecision: (result: DecisionResult, plan: DecisionPlan, model: string) => {
51
+ model: string;
52
+ answers: ({
53
+ type: string;
54
+ name: string | null;
55
+ probability: number;
56
+ choice?: undefined;
57
+ score?: undefined;
58
+ probabilities?: undefined;
59
+ confidence?: undefined;
60
+ } | {
61
+ probability?: undefined;
62
+ type: string;
63
+ name: string | null;
64
+ choice: any;
65
+ probabilities: {
66
+ value: ChoiceValue;
67
+ probability: number;
68
+ }[];
69
+ confidence: number;
70
+ score?: undefined;
71
+ } | {
72
+ probability?: undefined;
73
+ choice?: undefined;
74
+ type: string;
75
+ name: string | null;
76
+ score: number;
77
+ probabilities: {
78
+ value: number;
79
+ label: string;
80
+ probability: number;
81
+ }[];
82
+ confidence: number;
83
+ })[];
84
+ usage: {
85
+ input_tokens: number;
86
+ input_tokens_details: {
87
+ cached_tokens: number;
88
+ cache_write_tokens: number;
89
+ };
90
+ output_tokens: number;
91
+ output_tokens_details: {
92
+ reasoning_tokens: number;
93
+ };
94
+ total_tokens: number;
95
+ };
96
+ };
97
+ export {};
@@ -1,4 +1,5 @@
1
+ import type { State } from '../types';
1
2
  declare const _default: import("elysia").default<any, import("elysia").SingletonBase & {
2
- store: import("../types").State;
3
+ store: State;
3
4
  }, any, any, any, any, any>;
4
5
  export default _default;
@@ -9,10 +9,9 @@
9
9
  *
10
10
  * Enable via TOML config: [typesafe_compat] enabled = true
11
11
  *
12
- * `model` selects a configured decision generator by its published id, or by
13
- * a name mapped in `[typesafe_compat.models]` (SDKs send `jev-latest` unless
14
- * told otherwise). An unknown model is rejected, never substituted: another
15
- * model answers with different calibration, which the caller cannot detect.
12
+ * `model` selects a configured decision generator by its published id. An
13
+ * unknown model is rejected, never substituted: another model answers with
14
+ * different calibration, which the caller cannot detect.
16
15
  */
17
16
  type ValidationIssue = {
18
17
  loc: (string | number)[];
@@ -25,7 +24,7 @@ type ValidationIssue = {
25
24
  * precisely, with the field location the SDKs print.
26
25
  */
27
26
  export declare const validateSystemOneRequest: (body: any) => ValidationIssue[];
28
- /** Published id -> generator id, for the configured decision generators and the aliases. */
29
- export declare const buildModelTable: (config: any) => Map<string, string>;
27
+ /** Published ids of the configured decision generators, in config order. */
28
+ export declare const decisionModelIds: (config: any) => string[];
30
29
  export default function factory(): import("../types").ButtressApp;
31
30
  export {};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,t}from"./client-BVyIuSyr.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};
2
+ import{a as e,t}from"./client-qqI-HCOj.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/lib/types.d.ts CHANGED
@@ -61,8 +61,6 @@ export type GlobalConfig = {
61
61
  typesafe_compat?: {
62
62
  enabled?: boolean;
63
63
  cors_allowed_origins?: string | string[];
64
- /** Request `model` names (e.g. `jev-latest`) mapped to a decision generator id. */
65
- models?: Record<string, string>;
66
64
  };
67
65
  functions?: {
68
66
  enabled?: boolean;
@@ -195,6 +193,20 @@ export type State = {
195
193
  config: Config;
196
194
  sessions: Map<string, Session>;
197
195
  workspaceState: import('./utils/workspaceState').WorkspaceState;
196
+ /** Epoch ms when `createServer` began, reported on `/buttress/status`. */
197
+ startedAt: number;
198
+ /** The compatibility endpoints this process serves (env var or config flag). */
199
+ endpoints: {
200
+ openaiCompat: boolean;
201
+ anthropicMessages: boolean;
202
+ typesafeCompat: boolean;
203
+ };
204
+ /** Local functions; null when the feature is off or failed to start. */
205
+ functionsService: import('./functions').FunctionsService | null;
206
+ /** Config-defined agents; null when none are configured or they failed to start. */
207
+ agentsService: import('./agent/types').AgentsService | null;
208
+ /** This machine, read once at startup for `/buttress/status`. */
209
+ host: import('./utils/hostFacts').HostFacts;
198
210
  };
199
211
  type ButtressSingleton = SingletonBase & {
200
212
  store: State;
@@ -0,0 +1,2 @@
1
+ /** `bricks-buttress ui …`. Exits the process: 0 after the window closes, 1 on any failure. */
2
+ export declare const runUiCommand: (argv: string[]) => Promise<void>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { LaunchButtressUi } from '@fugood/buttress-ui/src/host';
2
+ export declare const UI_PACKAGE = "@fugood/buttress-ui";
3
+ export type WindowModule = {
4
+ launchButtressUi: LaunchButtressUi;
5
+ };
6
+ export declare const importWindowModule: () => Promise<WindowModule>;
@@ -15,6 +15,7 @@ export declare const resolveSupportedGenerators: (config: Config, defaultTypes:
15
15
  }[];
16
16
  /**
17
17
  * Process raw user config (HumanReadableConfig) into Config.
18
- * Handles: device defaults, value normalization (bytes/ms), structure splitting.
18
+ * Handles: device defaults, value normalization (bytes/ms), structure splitting,
19
+ * and dropping generators with `enabled = false`.
19
20
  */
20
21
  export declare const processConfig: (input: HumanReadableConfig | null) => Config;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Model pre-downloads (`download = true`) for `/buttress/status`: the ones in
3
+ * flight plus a short newest-first history.
4
+ *
5
+ * The backends report progress as a 0–1 fraction only, so byte counts stay
6
+ * unknown (`receivedBytes` 0, `totalBytes` / `bytesPerSecond` null) and the
7
+ * fraction is exposed as `progress`. Downloads a session starts on demand go
8
+ * through backend-core's download manager, which tracks no metadata, so they
9
+ * are not listed here.
10
+ *
11
+ * A module-scoped singleton like the other status trackers: the CLI records
12
+ * into it and the status route snapshots it.
13
+ */
14
+ import type { GeneratorType } from '../types';
15
+ export type ActiveDownload = {
16
+ type: GeneratorType;
17
+ repoId: string;
18
+ filename: string | null;
19
+ receivedBytes: number;
20
+ totalBytes: number | null;
21
+ bytesPerSecond: number | null;
22
+ startedAt: string;
23
+ /** 0–1 as reported by the backend; null before the first report. */
24
+ progress: number | null;
25
+ };
26
+ export type FinishedDownload = {
27
+ type: GeneratorType;
28
+ repoId: string;
29
+ filename: string | null;
30
+ totalBytes: number | null;
31
+ durationMs: number;
32
+ finishedAt: string;
33
+ success: boolean;
34
+ error?: string;
35
+ };
36
+ export type DownloadsSnapshot = {
37
+ active: ActiveDownload[];
38
+ recent: FinishedDownload[];
39
+ };
40
+ /** Feeds one download's callbacks into the tracker. Calls after the first ending are no-ops. */
41
+ export type DownloadHandle = {
42
+ progress(fraction: number): void;
43
+ /** Replaces the repo named at `begin` with the one the backend resolved. */
44
+ setRepoId(repoId: string): void;
45
+ complete(): void;
46
+ fail(error: unknown): void;
47
+ /** Ends the entry without history: nothing was downloaded (already cached, or joined another download). */
48
+ discard(): void;
49
+ };
50
+ export declare const downloadStatus: {
51
+ begin(download: {
52
+ type: GeneratorType;
53
+ repoId: string;
54
+ filename?: string | null;
55
+ }): DownloadHandle;
56
+ snapshot(): DownloadsSnapshot;
57
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * This machine, as `/buttress/status` and the settings window describe it. Same shape as
3
+ * `HostInfo` in @fugood/buttress-ui, kept local so the emitted types do not need that package.
4
+ */
5
+ export type HostFacts = {
6
+ cpuModel: string;
7
+ cpuCores: number;
8
+ totalMemoryBytes: number;
9
+ /** Unified memory: the GPU shares `totalMemoryBytes`. */
10
+ appleSilicon: boolean;
11
+ gpuName?: string;
12
+ };
13
+ export declare const readHostFacts: () => HostFacts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fugood/buttress-server",
3
- "version": "2.26.0-beta.19",
3
+ "version": "2.26.0-beta.20",
4
4
  "main": "lib/index.mjs",
5
5
  "types": "lib/index.d.ts",
6
6
  "type": "module",
@@ -19,13 +19,13 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "typecheck": "tsc --noEmit",
22
- "build": "tsdown -c rolldown.config.js --config-loader native && tsc --noCheck --emitDeclarationOnly && cp ../buttress-backend-core/src/backends/mlx-bridge.py lib/mlx-bridge.py && cp ../../node_modules/quickjs-wasi/quickjs.wasm lib/quickjs.wasm",
23
- "build:dist": "$npm_execpath scripts/build-distribution.js",
24
- "release": "$npm_execpath scripts/build-distribution.js",
25
- "release-beta": "$npm_execpath scripts/build-distribution.js --beta",
22
+ "build": "bun scripts/copy-status-page.js && tsdown -c rolldown.config.js --config-loader native && tsc --noCheck --emitDeclarationOnly && cp ../buttress-backend-core/src/backends/mlx-bridge.py lib/mlx-bridge.py && cp ../../node_modules/quickjs-wasi/quickjs.wasm lib/quickjs.wasm",
23
+ "build:dist": "$npm_execpath run --cwd ../buttress-ui build && $npm_execpath scripts/copy-status-page.js --require && $npm_execpath scripts/build-distribution.js",
24
+ "release": "$npm_execpath run --cwd ../buttress-ui build && $npm_execpath scripts/copy-status-page.js --require && $npm_execpath scripts/build-distribution.js",
25
+ "release-beta": "$npm_execpath run --cwd ../buttress-ui build && $npm_execpath scripts/copy-status-page.js --require && $npm_execpath scripts/build-distribution.js --beta",
26
26
  "upload-release": "node scripts/upload-release.js",
27
27
  "upload-beta": "node scripts/upload-release.js --beta",
28
- "prepublish": "bun run build",
28
+ "prepublish": "bun run --cwd ../buttress-ui build && bun scripts/copy-status-page.js --require && bun run build",
29
29
  "dev": "bun src/index.ts",
30
30
  "start": "bun lib/index.mjs",
31
31
  "start-with-node": "node lib/index.mjs"
@@ -70,10 +70,12 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@fugood/agent-tool-runtime": "^2.26.0-beta.19",
73
- "@fugood/bricks-cli": "^2.26.0-beta.19",
73
+ "@fugood/bricks-cli": "^2.26.0-beta.20",
74
74
  "@fugood/bricks-devtools-core": "^2.26.0-beta.18",
75
+ "@fugood/buttress-config": "^2.26.0-beta.20",
76
+ "@fugood/buttress-ui": "^2.26.0-beta.20",
75
77
  "tsdown": "^0.22.4",
76
78
  "typescript": "^7.0.2"
77
79
  },
78
- "gitHead": "59c100eee13c4152667a221954959f741ef0194f"
80
+ "gitHead": "ca31dd0b8e73e39b22addc5e34c33f5240ef64a1"
79
81
  }