echospace 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +27 -58
- package/dist/cli/index.js +196 -48
- package/dist/client/assets/Geist-Italic-Variable-vKc54d3Z.woff2 +0 -0
- package/dist/client/assets/Geist-Variable-CrgPqtmy.woff2 +0 -0
- package/dist/client/assets/GeistMono-Italic-Variable-MBthCoE1.woff2 +0 -0
- package/dist/client/assets/GeistMono-Variable-BNLlm6Cd.woff2 +0 -0
- package/dist/client/assets/GeistPixel-Square-CwnHaJd_.woff2 +0 -0
- package/dist/client/assets/index-gZkfzHQ0.css +1 -0
- package/dist/client/assets/index-ja1djPyy.js +168 -0
- package/dist/client/echospace-logo.svg +12 -11
- package/dist/client/index.html +4 -10
- package/dist/core/chunk-LGVA3Y5G.js +110 -0
- package/dist/core/echo/index.d.ts +2 -2
- package/dist/core/echo/index.js +1 -1
- package/dist/core/providers/index.d.ts +1 -1
- package/dist/core/providers/index.js +3 -3
- package/dist/core/smart-paste/index.d.ts +1 -1
- package/dist/core/smart-paste/index.js +1 -1
- package/dist/core/types-DonjLc45.d.ts +89 -0
- package/package.json +3 -1
- package/dist/client/assets/index-C3HpfhnB.js +0 -168
- package/dist/client/assets/index-kxAuiugv.css +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/echospace-logo/echospace-logo.png" alt="EchoSpace" width="128" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">EchoSpace</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>The open-source, local-first prompt debugging workspace for LLM developers.</strong></p>
|
|
4
8
|
|
|
5
9
|
Debug, iterate, and manage your prompts across OpenAI, Anthropic, and Google — all from your terminal. No cloud, no accounts, no lock-in.
|
|
6
10
|
|
|
@@ -18,7 +22,7 @@ MIT-licensed. Pluggable provider adapters (OpenAI, Anthropic, Google). YAML-base
|
|
|
18
22
|
|
|
19
23
|
### CLI & SDK
|
|
20
24
|
|
|
21
|
-
`npx echospace` to launch. Core TypeScript modules (`parseEcho`, `serializeEcho`, `smartParse`, provider registry) can be imported as a library for building your own tools.
|
|
25
|
+
`npx echospace@alpha` to launch. Core TypeScript modules (`parseEcho`, `serializeEcho`, `smartParse`, provider registry) can be imported as a library for building your own tools.
|
|
22
26
|
|
|
23
27
|
---
|
|
24
28
|
|
|
@@ -41,28 +45,36 @@ MIT-licensed. Pluggable provider adapters (OpenAI, Anthropic, Google). YAML-base
|
|
|
41
45
|
|
|
42
46
|
EchoSpace ships with agent skills that work with any coding agent — [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [OpenAI Codex](https://openai.com/index/codex/), or any tool that supports the skills/SKILL.md convention.
|
|
43
47
|
|
|
44
|
-
**Claude Code:**
|
|
45
|
-
|
|
46
48
|
```bash
|
|
47
|
-
|
|
49
|
+
npx skills add https://github.com/stonexer/echospace/tree/master/skills/echospace
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
**Other agents:** Copy the `skills/echospace/` directory into your project's skills folder.
|
|
51
|
-
|
|
52
52
|
### 2. Configure providers
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
```bash
|
|
55
|
+
npx echospace init
|
|
56
|
+
```
|
|
55
57
|
|
|
56
58
|
```
|
|
57
|
-
|
|
59
|
+
◆ Which LLM service do you use?
|
|
60
|
+
│ ○ OpenAI
|
|
61
|
+
│ ○ Anthropic (Claude)
|
|
62
|
+
│ ○ Google (Gemini)
|
|
63
|
+
│ ○ Vercel AI Gateway
|
|
64
|
+
│ ○ Custom Gateway (OpenAI-compatible)
|
|
65
|
+
|
|
66
|
+
◆ Enter your API Key:
|
|
67
|
+
│ sk-xxxxxxxx
|
|
68
|
+
|
|
69
|
+
✓ Config saved to ~/.echospace/config.yaml
|
|
58
70
|
```
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
Or use the agent skill: `/echospace:init`
|
|
61
73
|
|
|
62
74
|
### 3. Launch
|
|
63
75
|
|
|
64
76
|
```bash
|
|
65
|
-
npx echospace
|
|
77
|
+
npx echospace@alpha
|
|
66
78
|
```
|
|
67
79
|
|
|
68
80
|
Or install globally:
|
|
@@ -82,7 +94,7 @@ This will:
|
|
|
82
94
|
║ EchoSpace v0.1.0 ║
|
|
83
95
|
╠══════════════════════════════════════╣
|
|
84
96
|
║ Workspace: /my-project/.echo ║
|
|
85
|
-
║ URL: http://localhost:
|
|
97
|
+
║ URL: http://localhost:3240 ║
|
|
86
98
|
╚══════════════════════════════════════╝
|
|
87
99
|
```
|
|
88
100
|
|
|
@@ -100,7 +112,7 @@ This will:
|
|
|
100
112
|
echospace [workdir] [options]
|
|
101
113
|
|
|
102
114
|
[workdir] Workspace directory (default: ".")
|
|
103
|
-
-p, --port <port> Port to serve on (default: auto-select
|
|
115
|
+
-p, --port <port> Port to serve on (default: auto-select 3240-3249)
|
|
104
116
|
--no-open Don't open browser automatically
|
|
105
117
|
```
|
|
106
118
|
|
|
@@ -120,24 +132,6 @@ Each message can contain multiple part types: `text`, `thinking`, `tool_call`, `
|
|
|
120
132
|
|
|
121
133
|
---
|
|
122
134
|
|
|
123
|
-
## Architecture
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
┌─────────┐ ┌──────────────┐ ┌──────────────────┐
|
|
127
|
-
│ CLI │──────▶│ Hono Server │──────▶│ Provider Adapters│
|
|
128
|
-
│ (Commander) │ (localhost) │ │ ┌──────────────┐ │
|
|
129
|
-
└─────────┘ │ │ │ │ OpenAI │ │──▶ LLM APIs
|
|
130
|
-
│ /api/* │ │ │ Anthropic │ │
|
|
131
|
-
┌─────────┐ │ │ │ │ Google │ │
|
|
132
|
-
│ React UI│◀─────▶│ Static files│ │ └──────────────┘ │
|
|
133
|
-
│ (browser)│ └──────────────┘ └──────────────────┘
|
|
134
|
-
└─────────┘
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Tech stack:** React 19 · Hono · Zustand · Tailwind CSS 4 · Vite · CodeMirror · tiktoken
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
135
|
## Configuration
|
|
142
136
|
|
|
143
137
|
On first launch, EchoSpace creates `~/.echospace/config.yaml`. Fill in the `api_key` for the providers you want to use — unconfigured providers are automatically ignored.
|
|
@@ -177,38 +171,13 @@ providers:
|
|
|
177
171
|
|
|
178
172
|
---
|
|
179
173
|
|
|
180
|
-
## SDK / Programmatic Usage
|
|
181
|
-
|
|
182
|
-
Core modules can be imported directly for building your own tooling:
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
import { parseEcho, serializeEcho } from "echospace/core/echo";
|
|
186
|
-
import { smartParse, detectFormat } from "echospace/core/smart-paste";
|
|
187
|
-
import { createProviderRegistry } from "echospace/core/providers/registry";
|
|
188
|
-
|
|
189
|
-
// Parse a .echo file
|
|
190
|
-
const conversation = parseEcho(fileContents);
|
|
191
|
-
console.log(conversation.meta.title);
|
|
192
|
-
console.log(conversation.messages.length);
|
|
193
|
-
|
|
194
|
-
// Detect and convert from other formats
|
|
195
|
-
const format = detectFormat(clipboardText); // "openai" | "anthropic" | "google" | "raw" | ...
|
|
196
|
-
const messages = smartParse(clipboardText);
|
|
197
|
-
|
|
198
|
-
// Create a provider registry
|
|
199
|
-
const registry = createProviderRegistry();
|
|
200
|
-
const adapter = registry.get("openai");
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
174
|
## Development
|
|
206
175
|
|
|
207
176
|
```bash
|
|
208
177
|
# Install dependencies
|
|
209
178
|
pnpm install
|
|
210
179
|
|
|
211
|
-
# Start dev server (UI on :5173, API on :
|
|
180
|
+
# Start dev server (UI on :5173, API on :3240)
|
|
212
181
|
pnpm dev
|
|
213
182
|
|
|
214
183
|
# Other scripts
|
package/dist/cli/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import getPort, { portNumbers } from "get-port";
|
|
6
6
|
import fs3 from "fs";
|
|
7
|
-
import { homedir } from "os";
|
|
8
|
-
import
|
|
7
|
+
import { homedir as homedir2 } from "os";
|
|
8
|
+
import path5 from "path";
|
|
9
9
|
import open from "open";
|
|
10
10
|
|
|
11
11
|
// src/server/services/config.ts
|
|
@@ -104,7 +104,7 @@ function toAnthropicContent(msg) {
|
|
|
104
104
|
case "tool_result":
|
|
105
105
|
blocks.push({
|
|
106
106
|
type: "tool_result",
|
|
107
|
-
tool_use_id: part.
|
|
107
|
+
tool_use_id: part.tool_call_id,
|
|
108
108
|
content: typeof part.output === "string" ? part.output : JSON.stringify(part.output),
|
|
109
109
|
is_error: part.is_error
|
|
110
110
|
});
|
|
@@ -132,7 +132,7 @@ var anthropicAdapter = {
|
|
|
132
132
|
const url = `${baseUrl.replace(/\/$/, "")}/v1/messages`;
|
|
133
133
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
134
134
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
135
|
-
const system = systemMessages.flatMap((m) => m.parts.filter((
|
|
135
|
+
const system = systemMessages.flatMap((m) => m.parts.filter((p2) => p2.type === "text")).map((p2) => p2.text).join("\n\n");
|
|
136
136
|
const body = {
|
|
137
137
|
model: settings.model ?? config.models[0],
|
|
138
138
|
messages: nonSystemMessages.map((msg) => ({
|
|
@@ -232,7 +232,7 @@ function toGeminiContent(msg) {
|
|
|
232
232
|
case "tool_result":
|
|
233
233
|
parts.push({
|
|
234
234
|
functionResponse: {
|
|
235
|
-
name: part.
|
|
235
|
+
name: part.tool_call_id,
|
|
236
236
|
response: { result: part.output }
|
|
237
237
|
}
|
|
238
238
|
});
|
|
@@ -262,7 +262,7 @@ var googleAdapter = {
|
|
|
262
262
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
263
263
|
const systemInstruction = systemMessages.length > 0 ? {
|
|
264
264
|
parts: systemMessages.flatMap(
|
|
265
|
-
(m) => m.parts.filter((
|
|
265
|
+
(m) => m.parts.filter((p2) => p2.type === "text").map((p2) => ({ text: p2.text }))
|
|
266
266
|
)
|
|
267
267
|
} : void 0;
|
|
268
268
|
const body = {
|
|
@@ -332,20 +332,20 @@ var googleAdapter = {
|
|
|
332
332
|
// src/core/providers/openai.ts
|
|
333
333
|
function toOpenAIMessage(msg) {
|
|
334
334
|
const result = { role: msg.role };
|
|
335
|
-
const textParts = msg.parts.filter((
|
|
336
|
-
const toolCalls = msg.parts.filter((
|
|
337
|
-
const toolResults = msg.parts.filter((
|
|
338
|
-
const thinkingParts = msg.parts.filter((
|
|
335
|
+
const textParts = msg.parts.filter((p2) => p2.type === "text");
|
|
336
|
+
const toolCalls = msg.parts.filter((p2) => p2.type === "tool_call");
|
|
337
|
+
const toolResults = msg.parts.filter((p2) => p2.type === "tool_result");
|
|
338
|
+
const thinkingParts = msg.parts.filter((p2) => p2.type === "thinking");
|
|
339
339
|
if (msg.role === "tool" && toolResults.length > 0) {
|
|
340
340
|
const tr = toolResults[0];
|
|
341
341
|
result.content = typeof tr.output === "string" ? tr.output : JSON.stringify(tr.output);
|
|
342
|
-
result.tool_call_id = tr.
|
|
342
|
+
result.tool_call_id = tr.tool_call_id;
|
|
343
343
|
return result;
|
|
344
344
|
}
|
|
345
345
|
if (msg.role === "assistant") {
|
|
346
|
-
result.content = textParts.map((
|
|
346
|
+
result.content = textParts.map((p2) => p2.text).join("") || null;
|
|
347
347
|
if (thinkingParts.length > 0) {
|
|
348
|
-
result.reasoning_content = thinkingParts.map((
|
|
348
|
+
result.reasoning_content = thinkingParts.map((p2) => p2.text).join("");
|
|
349
349
|
}
|
|
350
350
|
if (toolCalls.length > 0) {
|
|
351
351
|
result.tool_calls = toolCalls.map((tc) => ({
|
|
@@ -359,7 +359,7 @@ function toOpenAIMessage(msg) {
|
|
|
359
359
|
}
|
|
360
360
|
return result;
|
|
361
361
|
}
|
|
362
|
-
result.content = textParts.map((
|
|
362
|
+
result.content = textParts.map((p2) => p2.text).join("");
|
|
363
363
|
return result;
|
|
364
364
|
}
|
|
365
365
|
var openaiAdapter = {
|
|
@@ -471,7 +471,7 @@ function chatRoutes(options) {
|
|
|
471
471
|
const config = loadConfig(options.configDir);
|
|
472
472
|
const providerList = config.providers ?? [];
|
|
473
473
|
const providerConfig = providerList.find(
|
|
474
|
-
(
|
|
474
|
+
(p2) => p2.name === providerName
|
|
475
475
|
);
|
|
476
476
|
if (!providerConfig) {
|
|
477
477
|
return c.json(
|
|
@@ -589,22 +589,22 @@ function normalizeMessage(message) {
|
|
|
589
589
|
if (!msg.created_at && (raw.ts || raw.timestamp)) {
|
|
590
590
|
msg = { ...msg, created_at: raw.ts ?? raw.timestamp };
|
|
591
591
|
}
|
|
592
|
-
const needsPartNormalization = msg.parts.some((
|
|
593
|
-
if (
|
|
594
|
-
const r =
|
|
595
|
-
return !r.
|
|
592
|
+
const needsPartNormalization = msg.parts.some((p2) => {
|
|
593
|
+
if (p2.type !== "tool_result") return false;
|
|
594
|
+
const r = p2;
|
|
595
|
+
return !r.tool_call_id && (r.id || r.tool_use_id);
|
|
596
596
|
});
|
|
597
597
|
if (needsPartNormalization) {
|
|
598
598
|
msg = {
|
|
599
599
|
...msg,
|
|
600
|
-
parts: msg.parts.map((
|
|
601
|
-
if (
|
|
602
|
-
const r =
|
|
603
|
-
if (!r.
|
|
604
|
-
const {
|
|
605
|
-
return { ...rest,
|
|
600
|
+
parts: msg.parts.map((p2) => {
|
|
601
|
+
if (p2.type !== "tool_result") return p2;
|
|
602
|
+
const r = p2;
|
|
603
|
+
if (!r.tool_call_id && (r.id || r.tool_use_id)) {
|
|
604
|
+
const { id, tool_use_id, ...rest } = r;
|
|
605
|
+
return { ...rest, tool_call_id: id ?? tool_use_id };
|
|
606
606
|
}
|
|
607
|
-
return
|
|
607
|
+
return p2;
|
|
608
608
|
})
|
|
609
609
|
};
|
|
610
610
|
}
|
|
@@ -804,14 +804,14 @@ function configRoutes(options) {
|
|
|
804
804
|
app.get("/providers", async (c) => {
|
|
805
805
|
const config = loadConfig(options.configDir);
|
|
806
806
|
const providerList = config.providers ?? [];
|
|
807
|
-
const providers = providerList.filter((
|
|
808
|
-
if (
|
|
809
|
-
const resolved =
|
|
807
|
+
const providers = providerList.filter((p2) => {
|
|
808
|
+
if (p2.api_key == null) return true;
|
|
809
|
+
const resolved = p2.api_key.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "");
|
|
810
810
|
return resolved.length > 0;
|
|
811
|
-
}).map((
|
|
812
|
-
name:
|
|
813
|
-
type:
|
|
814
|
-
models:
|
|
811
|
+
}).map((p2) => ({
|
|
812
|
+
name: p2.name,
|
|
813
|
+
type: p2.type,
|
|
814
|
+
models: p2.models ?? []
|
|
815
815
|
}));
|
|
816
816
|
return c.json({ providers });
|
|
817
817
|
});
|
|
@@ -862,11 +862,156 @@ function startServer(options) {
|
|
|
862
862
|
return app;
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
-
// src/cli/
|
|
866
|
-
|
|
865
|
+
// src/cli/init.ts
|
|
866
|
+
import * as p from "@clack/prompts";
|
|
867
|
+
import { homedir } from "os";
|
|
868
|
+
import path4 from "path";
|
|
867
869
|
var CONFIG_DIR = path4.join(homedir(), ".echospace");
|
|
870
|
+
var PROVIDER_PRESETS = {
|
|
871
|
+
openai: {
|
|
872
|
+
name: "openai",
|
|
873
|
+
type: "openai",
|
|
874
|
+
models: [
|
|
875
|
+
"gpt-4.1",
|
|
876
|
+
"gpt-4.1-mini",
|
|
877
|
+
"gpt-4.1-nano",
|
|
878
|
+
"gpt-4o",
|
|
879
|
+
"gpt-4o-mini",
|
|
880
|
+
"o3",
|
|
881
|
+
"o3-mini",
|
|
882
|
+
"o4-mini"
|
|
883
|
+
]
|
|
884
|
+
},
|
|
885
|
+
anthropic: {
|
|
886
|
+
name: "anthropic",
|
|
887
|
+
type: "anthropic",
|
|
888
|
+
models: ["claude-sonnet-4-6", "claude-haiku-4-5"]
|
|
889
|
+
},
|
|
890
|
+
google: {
|
|
891
|
+
name: "google",
|
|
892
|
+
type: "google",
|
|
893
|
+
models: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"]
|
|
894
|
+
},
|
|
895
|
+
vercel: {
|
|
896
|
+
name: "vercel-ai-gateway",
|
|
897
|
+
type: "openai",
|
|
898
|
+
base_url: "https://ai-gateway.vercel.sh/v1",
|
|
899
|
+
models: [
|
|
900
|
+
"anthropic/claude-sonnet-4-6",
|
|
901
|
+
"anthropic/claude-haiku-4-5",
|
|
902
|
+
"openai/gpt-4.1",
|
|
903
|
+
"openai/gpt-4.1-mini",
|
|
904
|
+
"openai/gpt-4o",
|
|
905
|
+
"openai/gpt-4o-mini",
|
|
906
|
+
"google/gemini-2.5-pro",
|
|
907
|
+
"google/gemini-2.5-flash",
|
|
908
|
+
"google/gemini-2.0-flash"
|
|
909
|
+
]
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
function isCancel2(value) {
|
|
913
|
+
return p.isCancel(value);
|
|
914
|
+
}
|
|
915
|
+
async function runInit() {
|
|
916
|
+
p.intro("EchoSpace Setup");
|
|
917
|
+
const service = await p.select({
|
|
918
|
+
message: "Which LLM service do you use?",
|
|
919
|
+
options: [
|
|
920
|
+
{ value: "openai", label: "OpenAI" },
|
|
921
|
+
{ value: "anthropic", label: "Anthropic (Claude)" },
|
|
922
|
+
{ value: "google", label: "Google (Gemini)" },
|
|
923
|
+
{ value: "vercel", label: "Vercel AI Gateway" },
|
|
924
|
+
{ value: "custom", label: "Custom Gateway (OpenAI-compatible)" }
|
|
925
|
+
]
|
|
926
|
+
});
|
|
927
|
+
if (isCancel2(service)) {
|
|
928
|
+
p.cancel("Setup cancelled.");
|
|
929
|
+
process.exit(0);
|
|
930
|
+
}
|
|
931
|
+
const apiKey = await p.password({
|
|
932
|
+
message: "Enter your API Key:"
|
|
933
|
+
});
|
|
934
|
+
if (isCancel2(apiKey)) {
|
|
935
|
+
p.cancel("Setup cancelled.");
|
|
936
|
+
process.exit(0);
|
|
937
|
+
}
|
|
938
|
+
let provider;
|
|
939
|
+
if (service === "custom") {
|
|
940
|
+
const baseUrl = await p.text({
|
|
941
|
+
message: "Base URL (e.g. https://my-proxy.example.com/v1):",
|
|
942
|
+
validate: (v) => {
|
|
943
|
+
if (!v.trim()) return "URL is required";
|
|
944
|
+
try {
|
|
945
|
+
new URL(v.trim());
|
|
946
|
+
} catch {
|
|
947
|
+
return "Invalid URL";
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
if (isCancel2(baseUrl)) {
|
|
952
|
+
p.cancel("Setup cancelled.");
|
|
953
|
+
process.exit(0);
|
|
954
|
+
}
|
|
955
|
+
const apiType = await p.select({
|
|
956
|
+
message: "Compatible API type:",
|
|
957
|
+
options: [
|
|
958
|
+
{ value: "openai", label: "OpenAI" },
|
|
959
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
960
|
+
{ value: "google", label: "Google" }
|
|
961
|
+
]
|
|
962
|
+
});
|
|
963
|
+
if (isCancel2(apiType)) {
|
|
964
|
+
p.cancel("Setup cancelled.");
|
|
965
|
+
process.exit(0);
|
|
966
|
+
}
|
|
967
|
+
const modelsInput = await p.text({
|
|
968
|
+
message: "Model names (comma-separated, or press enter to skip):",
|
|
969
|
+
defaultValue: ""
|
|
970
|
+
});
|
|
971
|
+
if (isCancel2(modelsInput)) {
|
|
972
|
+
p.cancel("Setup cancelled.");
|
|
973
|
+
process.exit(0);
|
|
974
|
+
}
|
|
975
|
+
const models = modelsInput.split(",").map((m) => m.trim()).filter(Boolean);
|
|
976
|
+
provider = {
|
|
977
|
+
name: "custom",
|
|
978
|
+
type: apiType,
|
|
979
|
+
api_key: apiKey,
|
|
980
|
+
base_url: baseUrl.trim(),
|
|
981
|
+
models
|
|
982
|
+
};
|
|
983
|
+
} else {
|
|
984
|
+
const preset = PROVIDER_PRESETS[service];
|
|
985
|
+
provider = {
|
|
986
|
+
...preset,
|
|
987
|
+
api_key: apiKey
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
const config = loadConfig(CONFIG_DIR);
|
|
991
|
+
const providers = config.providers ?? [];
|
|
992
|
+
const existingIdx = providers.findIndex((existing) => {
|
|
993
|
+
if (provider.base_url || existing.base_url) {
|
|
994
|
+
return existing.type === provider.type && existing.base_url === provider.base_url;
|
|
995
|
+
}
|
|
996
|
+
return existing.type === provider.type;
|
|
997
|
+
});
|
|
998
|
+
if (existingIdx >= 0) {
|
|
999
|
+
providers[existingIdx] = provider;
|
|
1000
|
+
} else {
|
|
1001
|
+
providers.push(provider);
|
|
1002
|
+
}
|
|
1003
|
+
config.providers = providers;
|
|
1004
|
+
saveConfig(CONFIG_DIR, config);
|
|
1005
|
+
const configPath = path4.join(CONFIG_DIR, "config.yaml");
|
|
1006
|
+
p.log.success(`Config saved to ${configPath}`);
|
|
1007
|
+
p.outro("You're all set! Run `npx echospace` to start.");
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/cli/index.ts
|
|
1011
|
+
var VERSION = "0.1.0-alpha.3";
|
|
1012
|
+
var CONFIG_DIR2 = path5.join(homedir2(), ".echospace");
|
|
868
1013
|
function loadEnvFile(dir) {
|
|
869
|
-
const envPath =
|
|
1014
|
+
const envPath = path5.join(dir, ".env");
|
|
870
1015
|
if (!fs3.existsSync(envPath)) return;
|
|
871
1016
|
const lines = fs3.readFileSync(envPath, "utf-8").split("\n");
|
|
872
1017
|
for (const line of lines) {
|
|
@@ -883,26 +1028,28 @@ function loadEnvFile(dir) {
|
|
|
883
1028
|
}
|
|
884
1029
|
var program = new Command();
|
|
885
1030
|
program.name("echospace").description("The best open-source local prompt debugging tool").version(VERSION).argument("[workdir]", "Workspace directory", ".").option("-p, --port <port>", "Port to serve on").option("--no-open", "Don't open browser automatically").action(async (workdir, options) => {
|
|
886
|
-
const baseDir =
|
|
887
|
-
const workspaceDir =
|
|
1031
|
+
const baseDir = path5.resolve(process.cwd(), workdir);
|
|
1032
|
+
const workspaceDir = path5.join(baseDir, ".echo");
|
|
888
1033
|
loadEnvFile(baseDir);
|
|
889
1034
|
loadEnvFile(process.cwd());
|
|
890
1035
|
if (!fs3.existsSync(workspaceDir)) {
|
|
891
1036
|
fs3.mkdirSync(workspaceDir, { recursive: true });
|
|
892
1037
|
}
|
|
893
|
-
fs3.mkdirSync(
|
|
894
|
-
ensureConfig(
|
|
895
|
-
const port = options.port ? parseInt(options.port, 10) : await getPort({ port: portNumbers(
|
|
1038
|
+
fs3.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
1039
|
+
ensureConfig(CONFIG_DIR2);
|
|
1040
|
+
const port = options.port ? parseInt(options.port, 10) : await getPort({ port: portNumbers(3240, 3249) });
|
|
1041
|
+
const url = `http://localhost:${port}`;
|
|
896
1042
|
console.log(`
|
|
897
|
-
\
|
|
898
|
-
\
|
|
899
|
-
\
|
|
900
|
-
\
|
|
901
|
-
\
|
|
902
|
-
\
|
|
1043
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
1044
|
+
\u2588\u2588 \u2588\u2588 EchoSpace v${VERSION}
|
|
1045
|
+
\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588
|
|
1046
|
+
\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 Workspace ${workspaceDir}
|
|
1047
|
+
\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 URL ${url}
|
|
1048
|
+
\u2588\u2588 \u2588\u2588
|
|
1049
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
903
1050
|
`);
|
|
904
1051
|
const isDev = process.env.NODE_ENV !== "production" && import.meta.url.includes("/src/");
|
|
905
|
-
startServer({ port, workspaceDir, configDir:
|
|
1052
|
+
startServer({ port, workspaceDir, configDir: CONFIG_DIR2, dev: isDev });
|
|
906
1053
|
if (options.open) {
|
|
907
1054
|
await open(`http://localhost:${port}`);
|
|
908
1055
|
}
|
|
@@ -913,4 +1060,5 @@ program.name("echospace").description("The best open-source local prompt debuggi
|
|
|
913
1060
|
process.on("SIGINT", shutdown);
|
|
914
1061
|
process.on("SIGTERM", shutdown);
|
|
915
1062
|
});
|
|
1063
|
+
program.command("init").description("Interactive setup for LLM provider configuration").action(runInit);
|
|
916
1064
|
program.parse();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Geist", -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "noto sans", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;--font-serif:"Geist Pixel Square", ui-monospace, monospace;--font-mono:"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-black:#000;--color-white:#fff;--spacing:.25rem;--font-weight-medium:500;--tracking-wider:.05em;--radius-sm:2px;--radius-md:4px;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg-1:var(--bg-1);--color-bg-1-1:var(--bg-1-1);--color-bg-1-2:var(--bg-1-2);--color-bg-2:var(--bg-2);--color-bg-3:var(--bg-3);--color-bg-4:var(--bg-4);--color-bg-5:var(--bg-5);--color-primary:var(--primary);--color-destructive:var(--danger);--color-border:var(--border-1);--color-text-normal:var(--text-normal);--color-text-secondary:var(--text-secondary);--color-text-desc:var(--text-desc);--color-text-placeholder:var(--text-placeholder)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.-top-1{top:calc(var(--spacing) * -1)}.-top-3{top:calc(var(--spacing) * -3)}.top-\[7px\]{top:7px}.top-\[calc\(100\%\+2px\)\]{top:calc(100% + 2px)}.top-full{top:100%}.-right-1{right:calc(var(--spacing) * -1)}.right-0{right:calc(var(--spacing) * 0)}.right-2{right:calc(var(--spacing) * 2)}.bottom-full{bottom:100%}.left-0{left:calc(var(--spacing) * 0)}.left-\[3px\]{left:3px}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-\[7px\]{width:7px;height:7px}.size-\[9px\]{width:9px;height:9px}.size-\[10px\]{width:10px;height:10px}.size-\[11px\]{width:11px;height:11px}.size-\[80px\]{width:80px;height:80px}.h-0{height:calc(var(--spacing) * 0)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[200px\]{max-height:200px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[36px\]{min-height:36px}.w-7{width:calc(var(--spacing) * 7)}.w-12{width:calc(var(--spacing) * 12)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.max-w-\[120px\]{max-width:120px}.max-w-\[240px\]{max-width:240px}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[180px\]{min-width:180px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.translate-y-1{--tw-translate-y:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-\[1\.4\]{scale:1.4}.scale-\[1\.6\]{scale:1.6}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-\[3px\]{gap:3px}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[var\(--success\)\]\/30{border-color:var(--success)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--success\)\]\/30{border-color:color-mix(in oklab,var(--success) 30%,transparent)}}.border-border,.border-border\/60{border-color:var(--color-border)}@supports (color:color-mix(in lab,red,red)){.border-border\/60{border-color:color-mix(in oklab,var(--color-border) 60%,transparent)}}.border-destructive\/30{border-color:var(--color-destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/30{border-color:color-mix(in oklab,var(--color-destructive) 30%,transparent)}}.border-primary,.border-primary\/40{border-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/40{border-color:color-mix(in oklab,var(--color-primary) 40%,transparent)}}.bg-\[var\(--role-assistant-bg\)\]{background-color:var(--role-assistant-bg)}.bg-\[var\(--role-system-bg\)\]{background-color:var(--role-system-bg)}.bg-\[var\(--role-tool-bg\)\]{background-color:var(--role-tool-bg)}.bg-\[var\(--role-user-bg\)\]{background-color:var(--role-user-bg)}.bg-\[var\(--success\)\]\/10{background-color:var(--success)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--success\)\]\/10{background-color:color-mix(in oklab,var(--success) 10%,transparent)}}.bg-\[var\(--success\)\]\/60{background-color:var(--success)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--success\)\]\/60{background-color:color-mix(in oklab,var(--success) 60%,transparent)}}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/15{background-color:#f99c0026}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/15{background-color:color-mix(in oklab,var(--color-amber-500) 15%,transparent)}}.bg-bg-1{background-color:var(--color-bg-1)}.bg-bg-1-1{background-color:var(--color-bg-1-1)}.bg-bg-1-2{background-color:var(--color-bg-1-2)}.bg-bg-2{background-color:var(--color-bg-2)}.bg-bg-3{background-color:var(--color-bg-3)}.bg-bg-5{background-color:var(--color-bg-5)}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab,red,red)){.bg-black\/20{background-color:color-mix(in oklab,var(--color-black) 20%,transparent)}}.bg-border{background-color:var(--color-border)}.bg-destructive,.bg-destructive\/5{background-color:var(--color-destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/5{background-color:color-mix(in oklab,var(--color-destructive) 5%,transparent)}}.bg-destructive\/90{background-color:var(--color-destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/90{background-color:color-mix(in oklab,var(--color-destructive) 90%,transparent)}}.bg-primary,.bg-primary\/15{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/15{background-color:color-mix(in oklab,var(--color-primary) 15%,transparent)}}.bg-primary\/50{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/50{background-color:color-mix(in oklab,var(--color-primary) 50%,transparent)}}.bg-text-normal{background-color:var(--color-text-normal)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing) * 1)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-\[3px\]{padding:3px}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-\[1px\]{padding-block:1px}.py-\[3px\]{padding-block:3px}.py-\[5px\]{padding-block:5px}.py-\[6px\]{padding-block:6px}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-serif{font-family:var(--font-serif)}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[18px\]{font-size:18px}.leading-\[16px\]{--tw-leading:16px;line-height:16px}.leading-\[18px\]{--tw-leading:18px;line-height:18px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#7a5a96\]{color:#7a5a96}.text-\[\#9a7030\]{color:#9a7030}.text-\[var\(--role-assistant-text\)\]{color:var(--role-assistant-text)}.text-\[var\(--role-system-text\)\]{color:var(--role-system-text)}.text-\[var\(--role-tool-text\)\]{color:var(--role-tool-text)}.text-\[var\(--role-user-text\)\]{color:var(--role-user-text)}.text-\[var\(--success\)\]{color:var(--success)}.text-amber-600{color:var(--color-amber-600)}.text-primary{color:var(--color-primary)}.text-text-desc{color:var(--color-text-desc)}.text-text-normal{color:var(--color-text-normal)}.text-text-placeholder{color:var(--color-text-placeholder)}.text-text-secondary{color:var(--color-text-secondary)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.accent-primary{accent-color:var(--color-primary)}.opacity-0{opacity:0}.opacity-90{opacity:.9}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_12px_var\(--shadow-streaming\)\]{--tw-shadow:0 0 12px var(--tw-shadow-color,var(--shadow-streaming));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-panel{--tw-shadow:var(--shadow-panel-value);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.\[animation-duration\:2s\]{animation-duration:2s}@media(hover:hover){.group-hover\:block:is(:where(.group):hover *){display:block}.group-hover\:bg-\[var\(--success\)\]:is(:where(.group):hover *){background-color:var(--success)}.group-hover\:bg-amber-400:is(:where(.group):hover *){background-color:var(--color-amber-400)}.group-hover\:bg-text-desc:is(:where(.group):hover *){background-color:var(--color-text-desc)}.group-hover\:text-text-desc:is(:where(.group):hover *){color:var(--color-text-desc)}.group-hover\/img\:opacity-100:is(:where(.group\/img):hover *){opacity:1}.group-hover\/stats\:pointer-events-auto:is(:where(.group\/stats):hover *){pointer-events:auto}.group-hover\/stats\:opacity-100:is(:where(.group\/stats):hover *){opacity:1}}.placeholder\:text-text-placeholder::placeholder{color:var(--color-text-placeholder)}@media(hover:hover){.hover\:border-bg-5:hover{border-color:var(--color-bg-5)}.hover\:bg-\[var\(--success\)\]:hover{background-color:var(--success)}.hover\:bg-amber-500\/25:hover{background-color:#f99c0040}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/25:hover{background-color:color-mix(in oklab,var(--color-amber-500) 25%,transparent)}}.hover\:bg-bg-1-2:hover{background-color:var(--color-bg-1-2)}.hover\:bg-bg-2:hover{background-color:var(--color-bg-2)}.hover\:bg-bg-4:hover{background-color:var(--color-bg-4)}.hover\:bg-destructive:hover,.hover\:bg-destructive\/90:hover{background-color:var(--color-destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--color-destructive) 90%,transparent)}}.hover\:bg-primary\/10:hover{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/10:hover{background-color:color-mix(in oklab,var(--color-primary) 10%,transparent)}}.hover\:bg-primary\/40:hover{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/40:hover{background-color:color-mix(in oklab,var(--color-primary) 40%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--color-primary) 90%,transparent)}}.hover\:bg-text-desc:hover{background-color:var(--color-text-desc)}.hover\:text-destructive:hover{color:var(--color-destructive)}.hover\:text-text-secondary:hover{color:var(--color-text-secondary)}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-100:hover{opacity:1}}.focus\:border-bg-5:focus{border-color:var(--color-bg-5)}.focus\:border-primary:focus{border-color:var(--color-primary)}.active\:bg-primary\/60:active{background-color:var(--color-primary)}@supports (color:color-mix(in lab,red,red)){.active\:bg-primary\/60:active{background-color:color-mix(in oklab,var(--color-primary) 60%,transparent)}}.disabled\:opacity-30:disabled{opacity:.3}@media not all and (min-width:48rem){.max-md\:opacity-100{opacity:1}}@media(min-width:48rem){.md\:opacity-0{opacity:0}@media(hover:hover){.md\:group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}}}@font-face{font-family:Geist;src:url(/assets/Geist-Variable-CrgPqtmy.woff2)format("woff2");font-weight:100 900;font-style:normal;font-display:swap}@font-face{font-family:Geist;src:url(/assets/Geist-Italic-Variable-vKc54d3Z.woff2)format("woff2");font-weight:100 900;font-style:italic;font-display:swap}@font-face{font-family:Geist Mono;src:url(/assets/GeistMono-Variable-BNLlm6Cd.woff2)format("woff2");font-weight:100 900;font-style:normal;font-display:swap}@font-face{font-family:Geist Mono;src:url(/assets/GeistMono-Italic-Variable-MBthCoE1.woff2)format("woff2");font-weight:100 900;font-style:italic;font-display:swap}@font-face{font-family:Geist Pixel Square;src:url(/assets/GeistPixel-Square-CwnHaJd_.woff2)format("woff2");font-weight:400;font-style:normal;font-display:swap}:root{--bg-1:#faf7f1;--bg-1-1:#f5f1e9;--bg-1-2:#efeadf;--bg-2:#e9e3d6;--bg-3:#e2dace;--bg-4:#d6cebf;--bg-5:#c7bead;--text-emphasize:#1a1510;--text-normal:#2e2720;--text-secondary:#544b40;--text-desc:#847768;--text-placeholder:#b0a393;--text-disable:#ccc2b4;--border-1:#d8d0c2;--primary:#a3623a;--primary-light:#a3623a1a;--danger:#b04a3a;--danger-light:#b04a3a14;--success:#5a7e45;--success-light:#5a7e451a;--role-system-bg:#785a961a;--role-system-text:#7a5a96;--role-user-bg:#a3623a1a;--role-user-text:#a3623a;--role-assistant-bg:#5a7e451a;--role-assistant-text:#4a7040;--role-tool-bg:#b282321a;--role-tool-text:#9a7030;--selection-color:#a3623a2e;--shadow-card-value:0 1px 3px 0 #503c1e0f, 0 1px 2px -1px #503c1e0f;--shadow-panel-value:0 4px 12px 0 #503c1e14;--shadow-modal-value:0 8px 24px 0 #503c1e1f;--shadow-focus-value:0 0 0 3px #a3623a26;--shadow-streaming:#a3623a14}[data-theme=dusk]{--bg-1:#f4f5f6;--bg-1-1:#edeff2;--bg-1-2:#e4e7eb;--bg-2:#dcdfe5;--bg-3:#d4d7de;--bg-4:#c8ccd4;--bg-5:#bcc1ca;--text-emphasize:#191c21;--text-normal:#282c33;--text-secondary:#444952;--text-desc:#747a85;--text-placeholder:#a0a5ae;--text-disable:#c0c4cb;--border-1:#d0d3d9;--primary:#6a7fa8;--primary-light:#6a7fa81a;--danger:#b04a3a;--danger-light:#b04a3a14;--success:#5a8060;--success-light:#5a80601a;--role-system-bg:#8264961a;--role-system-text:#7a5a96;--role-user-bg:#6a7fa81f;--role-user-text:#4a6590;--role-assistant-bg:#50915f1f;--role-assistant-text:#3d7a4a;--role-tool-bg:#aa8c3c1a;--role-tool-text:#8a7030;--selection-color:#6a7fa82e;--shadow-card-value:0 1px 3px 0 #3237410f, 0 1px 2px -1px #3237410f;--shadow-panel-value:0 4px 12px 0 #32374114;--shadow-modal-value:0 8px 24px 0 #3237411f;--shadow-focus-value:0 0 0 3px #6a7fa826;--shadow-streaming:#6a7fa814}[data-theme=ember]{--bg-1:#1a1815;--bg-1-1:#222019;--bg-1-2:#2a2722;--bg-2:#2e2b26;--bg-3:#3a352e;--bg-4:#454037;--bg-5:#544e44;--text-emphasize:#f5e1c0;--text-normal:#dcd2bc;--text-secondary:#b8ab94;--text-desc:#8a7e6a;--text-placeholder:#665c4d;--text-disable:#4d453a;--border-1:#3a352e;--primary:#c4784a;--primary-light:#c4784a1f;--danger:#b04a3a;--danger-light:#b04a3a1f;--success:#5a7e45;--success-light:#5a7e451f;--role-system-bg:#8264a026;--role-system-text:#a088c0;--role-user-bg:#c4784a33;--role-user-text:#c4784a;--role-assistant-bg:#5a7e4526;--role-assistant-text:#7eaa62;--role-tool-bg:#b2823226;--role-tool-text:#d4a017;--selection-color:#c4784a40;--shadow-card-value:0 1px 4px 0 #00000080, 0 1px 2px -1px #0000004d;--shadow-panel-value:0 4px 12px 0 #0006;--shadow-modal-value:0 8px 24px 0 #00000080;--shadow-focus-value:0 0 0 3px #c4784a33;--shadow-streaming:#c4784a1a}[data-theme=slate]{--bg-1:#191c21;--bg-1-1:#1e2128;--bg-1-2:#262a32;--bg-2:#2d323d;--bg-3:#363c4a;--bg-4:#414858;--bg-5:#4d5568;--text-emphasize:#f1f3f5;--text-normal:#e4e7eb;--text-secondary:#a0a5ae;--text-desc:#747a85;--text-placeholder:#5c6370;--text-disable:#444b5a;--border-1:#2d323d;--primary:#6a7fa8;--primary-light:#6a7fa81f;--danger:#c55a4a;--danger-light:#c55a4a1f;--success:#5a8060;--success-light:#5a80601f;--role-system-bg:#8264a026;--role-system-text:#a088c0;--role-user-bg:#6a7fa826;--role-user-text:#7a9bcc;--role-assistant-bg:#5a806026;--role-assistant-text:#81c784;--role-tool-bg:#aa8c3c26;--role-tool-text:#e2b340;--selection-color:#6a7fa840;--shadow-card-value:0 1px 4px 0 #00000080, 0 1px 2px -1px #0000004d;--shadow-panel-value:0 4px 12px 0 #0006;--shadow-modal-value:0 8px 24px 0 #00000080;--shadow-focus-value:0 0 0 3px #6a7fa833;--shadow-streaming:#6a7fa81a}*{border-color:var(--border-1)}body{background-color:var(--bg-1);color:var(--text-normal);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;padding:0;font-size:12.5px;line-height:19px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--bg-5);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-disable)}.scrollbar-none{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-none::-webkit-scrollbar{display:none}::selection{background:var(--selection-color)}textarea{font-family:inherit}textarea:focus{outline:none}.section-label{font-family:var(--font-serif);letter-spacing:.04em;color:var(--text-desc);font-size:11px}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
|