@satori-sh/cli 0.0.30 → 0.0.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@satori-sh/cli",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "CLI tool for Satori memory server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,264 +0,0 @@
1
- // src/ui.tsx
2
- import { render, useRenderer, useTerminalDimensions } from "@opentui/solid";
3
- import { For, Show, createSignal, onMount, onCleanup } from "solid-js";
4
- import cliSpinners from "cli-spinners";
5
-
6
- // src/logo.ts
7
- import { dirname, join } from "path";
8
- import { fileURLToPath } from "url";
9
- async function loadLogo() {
10
- const { default: fs } = await import("fs");
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
- const logoPath = join(__dirname, "..", "logos", "satori.ans");
13
- return fs.readFileSync(logoPath, "utf8");
14
- }
15
-
16
- // src/ui.tsx
17
- import { jsx, jsxs } from "solid-js/h/jsx-runtime";
18
- async function runInteractiveApp({
19
- initialPrompt,
20
- options,
21
- processUserInput,
22
- infoLine,
23
- infoDisplay
24
- }) {
25
- const logo = await loadLogo();
26
- console.log(` ${logo}`);
27
- const rows = process.stdout.rows ?? 24;
28
- const logoHeight = logo.endsWith("\n") ? logo.slice(0, -1).split("\n").length : logo.split("\n").length;
29
- const splitHeight = Math.max(1, rows - logoHeight - 1);
30
- render(
31
- () => /* @__PURE__ */ jsx(
32
- App,
33
- {
34
- initialPrompt,
35
- options,
36
- processUserInput,
37
- infoLine,
38
- infoDisplay
39
- }
40
- ),
41
- {
42
- useAlternateScreen: false,
43
- exitOnCtrlC: true,
44
- useMouse: true,
45
- enableMouseMovement: true,
46
- experimental_splitHeight: splitHeight
47
- }
48
- );
49
- }
50
- function App({ initialPrompt, options, processUserInput, infoLine, infoDisplay }) {
51
- const renderer = useRenderer();
52
- const dimensions = useTerminalDimensions();
53
- const [messages, setMessages] = createSignal([]);
54
- const [inputValue, setInputValue] = createSignal("");
55
- const [showIntro, setShowIntro] = createSignal(true);
56
- const [isFullScreen, setIsFullScreen] = createSignal(false);
57
- const [spinnerFrame, setSpinnerFrame] = createSignal(0);
58
- const [isLoading, setIsLoading] = createSignal(false);
59
- const promptFg = "#00ffff";
60
- const responseFg = "#ffffff";
61
- const promptBg = "#2b2b2b";
62
- let inputRef;
63
- let currentMemoryId = options.memoryId;
64
- let messageId = 0;
65
- const usageText = infoDisplay?.usageLine ?? infoLine ?? "";
66
- const versionText = infoDisplay?.versionLine ?? "";
67
- const modelText = infoDisplay?.modelLine ?? "";
68
- const appendMessage = (role, text) => {
69
- setMessages((prev) => [...prev, { id: messageId++, role, text }]);
70
- };
71
- const exitApp = () => {
72
- renderer.destroy();
73
- process.exit(0);
74
- };
75
- const submitPrompt = async (raw) => {
76
- const trimmed = raw.trim();
77
- if (!trimmed) return;
78
- if (trimmed.toLowerCase() === "exit" || trimmed.toLowerCase() === "quit") {
79
- exitApp();
80
- return;
81
- }
82
- if (showIntro()) {
83
- setShowIntro(false);
84
- }
85
- if (!isFullScreen()) {
86
- setIsFullScreen(true);
87
- }
88
- setInputValue("");
89
- if (inputRef) {
90
- inputRef.value = "";
91
- }
92
- appendMessage("prompt", trimmed);
93
- try {
94
- setIsLoading(true);
95
- const result = await processUserInput(trimmed, { ...options, memoryId: currentMemoryId }, "tui");
96
- currentMemoryId = result.memoryId;
97
- appendMessage("response", result.response);
98
- if (result.instruction) {
99
- appendMessage("response", result.instruction);
100
- }
101
- } catch (error) {
102
- const message = error instanceof Error ? error.message : String(error);
103
- appendMessage("response", `Error: ${message}`);
104
- } finally {
105
- setIsLoading(false);
106
- }
107
- };
108
- onMount(async () => {
109
- const spinner = cliSpinners.dots;
110
- const timer = setInterval(() => {
111
- if (isLoading()) {
112
- setSpinnerFrame((prev) => (prev + 1) % spinner.frames.length);
113
- }
114
- }, spinner.interval);
115
- onCleanup(() => clearInterval(timer));
116
- if (initialPrompt) {
117
- await submitPrompt(initialPrompt);
118
- }
119
- if (inputRef) {
120
- inputRef.focus();
121
- }
122
- });
123
- const inputBoxWidth = () => Math.max(1, Math.round(dimensions().width * 0.6));
124
- const inputBoxLeft = () => Math.max(0, Math.round(dimensions().width * 0.15));
125
- const inputBoxHeight = () => isFullScreen() ? 7 : 14;
126
- const inputBoxTop = () => isFullScreen() ? Math.max(1, dimensions().height - inputBoxHeight() - 2) : Math.max(1, Math.round(dimensions().height * 0.666));
127
- const messagesTop = () => 1;
128
- const messagesHeight = () => Math.max(1, inputBoxTop() - messagesTop() - 1);
129
- const messagesWidth = () => Math.min(dimensions().width - 2, inputBoxWidth() + 10);
130
- const messagesLeft = () => Math.max(1, inputBoxLeft() - 5);
131
- return /* @__PURE__ */ jsxs("box", { width: "100%", height: "100%", flexDirection: "column", children: [
132
- /* @__PURE__ */ jsx(
133
- "scrollbox",
134
- {
135
- id: "messages",
136
- width: messagesWidth(),
137
- height: messagesHeight(),
138
- position: "absolute",
139
- left: messagesLeft(),
140
- top: messagesTop(),
141
- paddingLeft: 1,
142
- paddingRight: 1,
143
- focused: true,
144
- stickyScroll: true,
145
- stickyStart: "bottom",
146
- children: /* @__PURE__ */ jsx("box", { width: "100%", flexDirection: "column", children: /* @__PURE__ */ jsx(For, { each: messages(), children: (message) => /* @__PURE__ */ jsx(
147
- "box",
148
- {
149
- width: "100%",
150
- flexDirection: "row",
151
- justifyContent: message.role === "prompt" ? "flex-start" : "flex-end",
152
- marginBottom: 1,
153
- children: /* @__PURE__ */ jsx(
154
- "box",
155
- {
156
- paddingLeft: 1,
157
- paddingRight: 1,
158
- paddingTop: 1,
159
- paddingBottom: 1,
160
- backgroundColor: message.role === "prompt" ? promptBg : void 0,
161
- children: /* @__PURE__ */ jsx(
162
- "text",
163
- {
164
- fg: message.role === "prompt" ? promptFg : responseFg,
165
- width: "100%",
166
- wrapMode: "word",
167
- selectable: false,
168
- children: message.text
169
- }
170
- )
171
- }
172
- )
173
- }
174
- ) }) })
175
- }
176
- ),
177
- /* @__PURE__ */ jsxs(
178
- "box",
179
- {
180
- id: "input-box",
181
- width: inputBoxWidth(),
182
- height: inputBoxHeight(),
183
- position: "absolute",
184
- left: inputBoxLeft(),
185
- top: inputBoxTop(),
186
- paddingLeft: 1,
187
- paddingRight: 1,
188
- paddingTop: 1,
189
- flexDirection: "column",
190
- children: [
191
- /* @__PURE__ */ jsx(For, { each: !isFullScreen() && showIntro() ? [
192
- "Use Satori just like you would use ChatGPT.",
193
- "Except, it stores your conversations in a long term memory.",
194
- "The memories you store here can be accessed through the SDK."
195
- ] : [], children: (line) => /* @__PURE__ */ jsx("text", { fg: "cyan", children: line }) }),
196
- /* @__PURE__ */ jsxs(
197
- "box",
198
- {
199
- id: "input-box",
200
- width: inputBoxWidth(),
201
- height: 5,
202
- backgroundColor: "#1a1a1a",
203
- flexDirection: "column",
204
- justifyContent: "center",
205
- children: [
206
- /* @__PURE__ */ jsx(
207
- "input",
208
- {
209
- id: "input",
210
- width: "100%",
211
- height: 1,
212
- placeholder: "Type a message and press Enter...",
213
- focusedBackgroundColor: "#1a1a1a",
214
- onInput: (value) => setInputValue(value),
215
- onSubmit: () => submitPrompt(inputValue()),
216
- ref: (r) => {
217
- inputRef = r;
218
- }
219
- }
220
- ),
221
- /* @__PURE__ */ jsx("box", { flexDirection: "row", flexShrink: 0, paddingTop: 1, children: /* @__PURE__ */ jsx("text", { fg: "#ffffff", children: modelText }) })
222
- ]
223
- }
224
- )
225
- ]
226
- }
227
- ),
228
- /* @__PURE__ */ jsx(Show, { when: isLoading(), children: /* @__PURE__ */ jsx(
229
- "box",
230
- {
231
- id: "spinner",
232
- position: "absolute",
233
- left: inputBoxLeft(),
234
- top: inputBoxTop() + inputBoxHeight(),
235
- paddingLeft: 1,
236
- children: /* @__PURE__ */ jsx("text", { fg: "#00ffff", children: cliSpinners.dots.frames[spinnerFrame()] })
237
- }
238
- ) }),
239
- /* @__PURE__ */ jsxs(
240
- "box",
241
- {
242
- id: "footer",
243
- width: dimensions().width,
244
- height: 1,
245
- position: "absolute",
246
- bottom: 0,
247
- left: 0,
248
- backgroundColor: "#000000",
249
- paddingLeft: 1,
250
- paddingRight: 1,
251
- flexDirection: "row",
252
- justifyContent: "space-between",
253
- alignItems: "center",
254
- children: [
255
- /* @__PURE__ */ jsx("text", { fg: "#00ffff", wrapMode: "none", width: "100%", children: usageText }),
256
- /* @__PURE__ */ jsx("box", { flexShrink: 0, paddingLeft: 1, children: /* @__PURE__ */ jsx("text", { fg: "#00ffff", children: versionText }) })
257
- ]
258
- }
259
- )
260
- ] });
261
- }
262
- export {
263
- runInteractiveApp
264
- };