@hachej/boring-ui-cli 0.1.13 → 0.1.16

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.
Files changed (68) hide show
  1. package/README.md +209 -16
  2. package/dist/index.js +9 -137
  3. package/dist/server/cli.js +305 -0
  4. package/dist/server/localWorkspaces.js +159 -0
  5. package/package.json +19 -5
  6. package/public/assets/{CodeEditor-DQqOn4xz-43ZRJnBk.js → CodeEditor-DQqOn4xz-KzhiCz--.js} +1 -1
  7. package/public/assets/{FileTree-BVfqs3rR-Cj1lLRuM.js → FileTree-Dl-qUAB0-B2la_qys.js} +10 -10
  8. package/public/assets/{MarkdownEditor-CcCDF65H-wNyV9k_h.js → MarkdownEditor-yc6mFsnI-But_i4l5.js} +59 -59
  9. package/public/assets/{_baseUniq-C6fgNBwH.js → _baseUniq-DdgAi7yq.js} +1 -1
  10. package/public/assets/{arc-DHH-gLyV.js → arc-CCqUtds5.js} +1 -1
  11. package/public/assets/{architectureDiagram-Q4EWVU46-kET4ZFwD.js → architectureDiagram-Q4EWVU46-B_KUn6cs.js} +1 -1
  12. package/public/assets/{blockDiagram-DXYQGD6D-BuyaW2GC.js → blockDiagram-DXYQGD6D-DZgTILG7.js} +1 -1
  13. package/public/assets/{c4Diagram-AHTNJAMY-DUSmbamx.js → c4Diagram-AHTNJAMY-CJflQswY.js} +1 -1
  14. package/public/assets/channel-DGvkobtu.js +1 -0
  15. package/public/assets/{chunk-4BX2VUAB-hMkrRbra.js → chunk-4BX2VUAB-iDI_gnLq.js} +1 -1
  16. package/public/assets/{chunk-4TB4RGXK-DAkJYV0r.js → chunk-4TB4RGXK-DKWIFYxg.js} +1 -1
  17. package/public/assets/{chunk-55IACEB6-CQ4cPkeu.js → chunk-55IACEB6-D__oMXZt.js} +1 -1
  18. package/public/assets/{chunk-EDXVE4YY-BhU6gznk.js → chunk-EDXVE4YY-Do1Ka_m9.js} +1 -1
  19. package/public/assets/{chunk-FMBD7UC4-CXkmSrcX.js → chunk-FMBD7UC4-lANxhjIk.js} +1 -1
  20. package/public/assets/{chunk-OYMX7WX6-B6YmKjoP.js → chunk-OYMX7WX6-D5V4Ykah.js} +1 -1
  21. package/public/assets/{chunk-QZHKN3VN-DaBewk7d.js → chunk-QZHKN3VN-Qd0rJnNE.js} +1 -1
  22. package/public/assets/{chunk-YZCP3GAM-wVeKMZui.js → chunk-YZCP3GAM-CFwEplp0.js} +1 -1
  23. package/public/assets/classDiagram-6PBFFD2Q-CbsNjbES.js +1 -0
  24. package/public/assets/classDiagram-v2-HSJHXN6E-CbsNjbES.js +1 -0
  25. package/public/assets/clone-CGN5Up_u.js +1 -0
  26. package/public/assets/{cose-bilkent-S5V4N54A-DI8T3pQ5.js → cose-bilkent-S5V4N54A-DfVWRV6i.js} +1 -1
  27. package/public/assets/{dagre-KV5264BT-BzhUQX3L.js → dagre-KV5264BT-DwkYud_A.js} +1 -1
  28. package/public/assets/{diagram-5BDNPKRD-BmjVDoqS.js → diagram-5BDNPKRD-BVmqnZKF.js} +1 -1
  29. package/public/assets/{diagram-G4DWMVQ6-0qBOUHAQ.js → diagram-G4DWMVQ6-Be8h-c7h.js} +1 -1
  30. package/public/assets/{diagram-MMDJMWI5-ZuUygJfX.js → diagram-MMDJMWI5-CeQIY42x.js} +1 -1
  31. package/public/assets/{diagram-TYMM5635-DLicNSlG.js → diagram-TYMM5635-2m1E87ZC.js} +1 -1
  32. package/public/assets/{erDiagram-SMLLAGMA-BIqDaKsJ.js → erDiagram-SMLLAGMA-CEAPDf64.js} +1 -1
  33. package/public/assets/{flowDiagram-DWJPFMVM-BuMuUYvo.js → flowDiagram-DWJPFMVM-70-QVqzZ.js} +1 -1
  34. package/public/assets/{ganttDiagram-T4ZO3ILL-h_0R8kpx.js → ganttDiagram-T4ZO3ILL-CQUeK6YZ.js} +1 -1
  35. package/public/assets/{gitGraphDiagram-UUTBAWPF-CFZuKUPu.js → gitGraphDiagram-UUTBAWPF-BAxMyzkG.js} +1 -1
  36. package/public/assets/{graph-BvcPX0LE.js → graph-DBYJAYcR.js} +1 -1
  37. package/public/assets/{highlighted-body-OFNGDK62-B5LPCsvB.js → highlighted-body-OFNGDK62-D32MCa1A.js} +1 -1
  38. package/public/assets/index-CyEGNm7Z.css +1 -0
  39. package/public/assets/index-DfV9nLZO.js +1324 -0
  40. package/public/assets/{index-CZBRICYr.js → index-Dnr-P_qO.js} +1 -1
  41. package/public/assets/{infoDiagram-42DDH7IO-C0QkIqtm.js → infoDiagram-42DDH7IO-D4XwdS2Z.js} +1 -1
  42. package/public/assets/{ishikawaDiagram-UXIWVN3A-DkzZIthw.js → ishikawaDiagram-UXIWVN3A-7F_PxA8e.js} +1 -1
  43. package/public/assets/{journeyDiagram-VCZTEJTY-BEpMOI6B.js → journeyDiagram-VCZTEJTY-CBJ4F9tD.js} +1 -1
  44. package/public/assets/{kanban-definition-6JOO6SKY-DGBlgtiV.js → kanban-definition-6JOO6SKY-DNxo2cA_.js} +1 -1
  45. package/public/assets/{layout-m9mU-Es4.js → layout-xHv827iK.js} +1 -1
  46. package/public/assets/{linear-gvQOMf93.js → linear-C4D-wE9b.js} +1 -1
  47. package/public/assets/{min-DbPrSRXJ.js → min-DRKJR04f.js} +1 -1
  48. package/public/assets/{mindmap-definition-QFDTVHPH-D-w8aNmQ.js → mindmap-definition-QFDTVHPH-BFEYUDaK.js} +1 -1
  49. package/public/assets/{pieDiagram-DEJITSTG-BJfiij1X.js → pieDiagram-DEJITSTG-OgFPC-fQ.js} +1 -1
  50. package/public/assets/{quadrantDiagram-34T5L4WZ-SEDnxGzO.js → quadrantDiagram-34T5L4WZ-gcFm5ymy.js} +1 -1
  51. package/public/assets/{requirementDiagram-MS252O5E-CRXbp7i_.js → requirementDiagram-MS252O5E-C-NXT7Xx.js} +1 -1
  52. package/public/assets/{sankeyDiagram-XADWPNL6-BQ5yTiY0.js → sankeyDiagram-XADWPNL6-BVTpXbQ9.js} +1 -1
  53. package/public/assets/{sequenceDiagram-FGHM5R23-D3fAknEI.js → sequenceDiagram-FGHM5R23-6nnB6xSY.js} +1 -1
  54. package/public/assets/{stateDiagram-FHFEXIEX-DME_bKgM.js → stateDiagram-FHFEXIEX-vvjjWHqR.js} +1 -1
  55. package/public/assets/stateDiagram-v2-QKLJ7IA2-UKC2Fz8g.js +1 -0
  56. package/public/assets/{timeline-definition-GMOUNBTQ-CGOu_F4b.js → timeline-definition-GMOUNBTQ-DfgoENQV.js} +1 -1
  57. package/public/assets/{vennDiagram-DHZGUBPP-BLPdNseJ.js → vennDiagram-DHZGUBPP-CwtXgny7.js} +1 -1
  58. package/public/assets/{wardley-RL74JXVD-B8d_2bb8.js → wardley-RL74JXVD-YWQ--wCx.js} +1 -1
  59. package/public/assets/{wardleyDiagram-NUSXRM2D-waI-JAEe.js → wardleyDiagram-NUSXRM2D-0iSD2F3A.js} +1 -1
  60. package/public/assets/{xychartDiagram-5P7HB3ND-BUIUqkqr.js → xychartDiagram-5P7HB3ND-DpVtsfdW.js} +1 -1
  61. package/public/index.html +3 -3
  62. package/public/assets/channel-CUyWgwUc.js +0 -1
  63. package/public/assets/classDiagram-6PBFFD2Q--KL4rJah.js +0 -1
  64. package/public/assets/classDiagram-v2-HSJHXN6E--KL4rJah.js +0 -1
  65. package/public/assets/clone-DAST79i1.js +0 -1
  66. package/public/assets/index-DKanL3AM.css +0 -1
  67. package/public/assets/index-y7tKhGxl.js +0 -1349
  68. package/public/assets/stateDiagram-v2-QKLJ7IA2-Cup0egSL.js +0 -1
package/README.md CHANGED
@@ -1,42 +1,235 @@
1
- # @hachej/boring-ui
1
+ # @hachej/boring-ui-cli
2
2
 
3
- **Turn an agent into an app.**
3
+ <div align="center">
4
4
 
5
- Start a full agent workspace pointed at your current directory — chat, panels, file tree, command palette — in one command.
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![npm](https://img.shields.io/npm/v/@hachej/boring-ui-cli.svg)](https://www.npmjs.com/package/@hachej/boring-ui-cli)
7
+
8
+ </div>
9
+
10
+ **Turn an agent into an app — in one command.** Start a full IDE-style workbench pointed at your current directory: chat, file tree, editor, command palette. No clone. No database. No config.
6
11
 
7
12
  ```bash
8
13
  npx @hachej/boring-ui-cli
9
14
  ```
10
15
 
11
- No config. No clone. No database. Requires an Anthropic API key (or login via Claude/Copilot/Gemini on first run).
16
+ ---
17
+
18
+ ## TL;DR
19
+
20
+ **The Problem**: You want a coding agent in a browser IDE — but you don't want to clone a repo, set up Postgres, configure auth, or deploy anything. You just want to talk to an AI about your code.
21
+
22
+ **The Solution**: `npx @hachej/boring-ui-cli` starts a full workbench locally, using your current directory as the workspace. It opens a browser tab with chat, file explorer, and panels. Zero setup, zero config, zero deploy.
23
+
24
+ ### Why Use @hachej/boring-ui-cli?
25
+
26
+ | Feature | What It Does |
27
+ |---------|--------------|
28
+ | **Zero-config startup** | `npx @hachej/boring-ui-cli` — that's it. Opens your browser to a full agent workbench. |
29
+ | **Simple auth** | Set `ANTHROPIC_API_KEY` in your environment. The agent runs with direct filesystem access to your cwd. |
30
+ | **Full workspace** | Chat, file tree, editor panels, command palette — all running against your real directory. |
31
+ | **No database** | Runs in-memory. State persists for the session. No external dependencies. |
32
+ | **Customizable port + root** | `PORT=8080` and `BORING_AGENT_WORKSPACE_ROOT=/path` env vars for power users. |
33
+
34
+ ---
35
+
36
+ ## Quick Example
37
+
38
+ ```bash
39
+ # Navigate to any project
40
+ cd /path/to/my-project
41
+
42
+ # Start the CLI — opens browser at localhost:5200
43
+ npx @hachej/boring-ui-cli
44
+
45
+ # With a custom port
46
+ PORT=8080 npx @hachej/boring-ui-cli
47
+
48
+ # Point at a specific directory
49
+ BORING_AGENT_WORKSPACE_ROOT=/path/to/project npx @hachej/boring-ui-cli
50
+
51
+ # With API key
52
+ ANTHROPIC_API_KEY=sk-ant-... npx @hachej/boring-ui-cli
53
+ ```
54
+
55
+ Once the browser opens, you can:
56
+ ```
57
+ # In the chat box:
58
+ "read my README and suggest improvements"
59
+ "find all TypeScript files that import 'react'"
60
+ "rewrite the test file to use vitest"
61
+ ```
12
62
 
13
63
  ---
14
64
 
15
- ## What it starts
65
+ ## Installation
66
+
67
+ No installation needed — use `npx`:
16
68
 
17
- - A chat interface wired to a real coding agent
18
- - A workspace with panels, a file tree, and a command palette
19
- - The agent runs with full access to your current directory
69
+ ```bash
70
+ npx @hachej/boring-ui-cli
71
+ ```
20
72
 
21
- ## API key
73
+ Or install globally for repeated use:
74
+
75
+ ```bash
76
+ # npm
77
+ npm install -g @hachej/boring-ui-cli
78
+
79
+ # pnpm
80
+ pnpm add -g @hachej/boring-ui-cli
81
+
82
+ # Then just run:
83
+ boring-ui
84
+ ```
22
85
 
23
- Set it in your environment:
86
+ ### From Source
87
+
88
+ ```bash
89
+ git clone https://github.com/hachej/boring-ui.git
90
+ cd boring-ui && pnpm install
91
+ pnpm --filter @hachej/boring-ui-cli build
92
+ npx ./packages/cli/dist/index.js
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Authentication
98
+
99
+ The CLI talks to Anthropic Claude via the agent runtime. You need a valid API key:
24
100
 
25
101
  ```bash
26
102
  ANTHROPIC_API_KEY=sk-ant-... npx @hachej/boring-ui-cli
27
103
  ```
28
104
 
29
- Or run without it the first launch will open a login prompt to authenticate via Claude, Copilot, Gemini, or Codex. Credentials are stored and reused on future runs.
105
+ Only Anthropic Claude is wired in v1. The agent harness supports other providers via the `AgentHarness` interface, but only Anthropic is implemented.
106
+
107
+ ---
30
108
 
31
109
  ## Options
32
110
 
33
- ```bash
34
- PORT=8080 npx @hachej/boring-ui-cli # custom port (default: 5200)
35
- BORING_AGENT_WORKSPACE_ROOT=/my/project npx @hachej/boring-ui-cli # custom root
111
+ | Environment Variable | Default | Description |
112
+ |---------------------|---------|-------------|
113
+ | `ANTHROPIC_API_KEY` | Yes | Anthropic API key. The agent requires a valid key to function. |
114
+ | `PORT` | `5200` | Port to run the server on |
115
+ | `BORING_AGENT_WORKSPACE_ROOT` | `.` (cwd) | Root directory for the workspace. The agent sees this as its filesystem. |
116
+ | `BORING_AGENT_MODE` | `direct` | `direct` (no sandbox) or `local` (bwrap sandbox, Linux only) |
117
+ | `BORING_AGENT_DEFAULT_MODEL_ID` | `claude-sonnet-4-6` | Default model to use |
118
+
119
+ ---
120
+
121
+ ## Architecture
122
+
123
+ ```
124
+ npx @hachej/boring-ui-cli
125
+ ├── Boot Fastify server (direct mode, in-memory)
126
+ ├── Serve frontend SPA (Vite-built bundle)
127
+ ├── Open browser → http://localhost:5200
128
+ └── Workspace = your current directory (or $BORING_AGENT_WORKSPACE_ROOT)
36
129
  ```
37
130
 
131
+ The CLI is the zero-config entry point to the full boring-ui stack. Under the hood it wires together:
132
+
133
+ - `@hachej/boring-agent` — agent runtime, tools, chat UI
134
+ - `@hachej/boring-workspace` — file tree, panels, command palette, plugins
135
+ - `@hachej/boring-ui-kit` — shared UI primitives
136
+
137
+ All running locally against your real filesystem with no database.
138
+
139
+ ---
140
+
141
+ ## How @hachej/boring-ui-cli Compares
142
+
143
+ | Feature | @hachej/boring-ui-cli | Claude Code | Codex CLI | Cursor |
144
+ |---------|------------------------|-------------|-----------|--------|
145
+ | Browser UI | ✅ Full IDE with panels | ❌ Terminal only | ❌ Terminal only | ✅ Desktop app |
146
+ | File tree | ✅ Side panel | ❌ | ❌ | ✅ |
147
+ | Zero setup | ✅ `npx` anywhere | ⚠️ Install + login | ⚠️ Install + login | ❌ Desktop app download |
148
+ | Panel system | ✅ Dockview splittable panels | ❌ | ❌ | ❌ |
149
+ | Plugin extensibility | ✅ Panels, commands, catalogs | ❌ | ❌ | ⚠️ Extensions |
150
+ | Local filesystem | ✅ Direct access | ✅ | ✅ | ✅ |
151
+ | Database required | ❌ None | ❌ | ❌ | ❌ |
152
+
153
+ **When to use @hachej/boring-ui-cli:**
154
+ - You want a browser-based coding agent with file tree and panels
155
+ - You don't want to install anything — just `npx`
156
+ - You want plugin extensibility (custom panels, data catalogs, etc.)
157
+
158
+ **When it might not fit:**
159
+ - You prefer terminal-only agent workflows (use Claude Code or Codex CLI)
160
+ - You need multi-user auth, workspaces, or a database (use `@hachej/boring-core`)
161
+ - You want a full desktop IDE with LSP, debugging, and git (use Cursor or VS Code)
162
+
163
+ ---
164
+
165
+ ## Troubleshooting
166
+
167
+ | Error | Cause | Fix |
168
+ |-------|-------|-----|
169
+ | `ANTHROPIC_API_KEY not set` | No API key | `export ANTHROPIC_API_KEY=sk-ant-...` before running |
170
+ | `port already in use` | Port 5200 occupied | `PORT=5201 npx @hachej/boring-ui-cli` |
171
+ | Browser doesn't open | `BROWSER=none` or no display | Manually navigate to `http://localhost:5200` |
172
+ | Agent returns errors | Invalid API key | Verify your Anthropic API key is valid and has quota |
173
+ | `workspace root not found` | `BORING_AGENT_WORKSPACE_ROOT` points to non-existent dir | Create the directory or unset the variable to use cwd |
174
+
175
+ ---
176
+
177
+ ## Limitations
178
+
179
+ - **In-memory only**: No database, no persistent workspaces. State is lost when the CLI exits.
180
+ - **Single workspace**: Points at one directory. No multi-workspace switching.
181
+ - **No auth management**: No user accounts, invites, or role-based access.
182
+ - **Direct mode only**: No bwrap sandbox by default (use `BORING_AGENT_MODE=local` on Linux with bubblewrap installed).
183
+ - **Not for production**: This is a developer tool, not a deployment target. Use `@hachej/boring-core` for multi-user apps.
184
+ - **Only Anthropic Claude**: No OpenAI, Google, or other model providers wired in v1.
185
+
186
+ ---
187
+
188
+ ## FAQ
189
+
190
+ **Q: Do I need to install anything first?**
191
+ A: No. `npx` downloads and runs the package on first use. Subsequent runs use the cached version.
192
+
193
+ **Q: What happens when I close the browser?**
194
+ A: The server keeps running. Stop it with `Ctrl+C` in the terminal.
195
+
196
+ **Q: Can I use this with OpenAI models?**
197
+ A: Only Anthropic Claude is wired in v1. Additional providers may be supported in future versions.
198
+
199
+ **Q: Is my code sent to the cloud?**
200
+ A: Yes — the agent sends file contents and chat messages to the LLM provider (e.g. Anthropic). The filesystem operations run locally on your machine.
201
+
202
+ **Q: How is this different from `npx @hachej/boring-agent`?**
203
+ A: `@hachej/boring-ui-cli` ships the full workbench (file tree, editor, command palette, plugins). `@hachej/boring-agent` is just the agent + chat. The CLI is the batteries-included zero-config entry point.
204
+
205
+ **Q: Can I extend the CLI with plugins?**
206
+ A: Not directly in v1. The CLI uses the default agent + workspace configuration. For plugin extensibility, build a custom app using `@hachej/boring-workspace` + `@hachej/boring-core`.
207
+
208
+ ---
209
+
210
+ ## Building Something Bigger?
211
+
212
+ `@hachej/boring-ui-cli` is the zero-config entry point. For a full app with:
213
+ - Multi-user authentication
214
+ - Persistent workspaces with Postgres
215
+ - Email invites and password resets
216
+ - Custom domain plugins
217
+
218
+ See the [boring-ui monorepo](https://github.com/hachej/boring-ui) and its packages:
219
+
220
+ | Package | Purpose |
221
+ |---------|---------|
222
+ | `@hachej/boring-core` | Auth, DB, app factory, multi-user |
223
+ | `@hachej/boring-workspace` | Plugin system, panels, layouts |
224
+ | `@hachej/boring-agent` | Agent runtime, tools, chat UI |
225
+ | `@hachej/boring-ui-kit` | Shared React UI primitives |
226
+
227
+ ---
228
+
229
+ *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
230
+
38
231
  ---
39
232
 
40
- ## Building something bigger?
233
+ ## License
41
234
 
42
- `@hachej/boring-ui` is the zero-config entry point. For a full app with auth, workspaces, and a database, see the [boring-ui monorepo](https://github.com/hachej/boring-ui).
235
+ MIT
package/dist/index.js CHANGED
@@ -1,142 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { createWorkspaceAgentServer } from "@hachej/boring-workspace/app/server";
3
- import fastifyStatic from "@fastify/static";
4
- import { AuthStorage, LoginDialogComponent, OAuthSelectorComponent, initTheme } from "@mariozechner/pi-coding-agent";
5
- import { ProcessTerminal, TUI } from "@mariozechner/pi-tui";
6
- import { execSync } from "node:child_process";
7
- import { existsSync } from "node:fs";
8
- import { createInterface } from "node:readline";
9
- import { basename, dirname, resolve } from "node:path";
2
+ import { dirname, resolve } from "node:path";
10
3
  import { fileURLToPath } from "node:url";
11
- import { parseArgs } from "node:util";
12
- const { values: args } = parseArgs({
13
- options: {
14
- port: { type: "string", short: "p" },
15
- host: { type: "string" },
16
- mode: { type: "string", short: "m" }
17
- },
18
- strict: false
19
- });
20
- const PORT = Number(args.port ?? process.env.PORT) || 5200;
21
- const HOST = args.host ?? process.env.HOST ?? "0.0.0.0";
22
- const MODE_MAP = {
23
- "local": "direct",
24
- // no sandbox, full network access
25
- "local-sandbox": "local"
26
- // bwrap isolated, no network (Linux only)
27
- };
28
- const rawMode = args.mode ?? process.env.BORING_MODE ?? "local-sandbox";
29
- if (!(rawMode in MODE_MAP)) {
30
- console.error(`
31
- Error: invalid --mode "${rawMode}". Valid options: ${Object.keys(MODE_MAP).join(", ")}
32
- `);
33
- process.exit(1);
34
- }
35
- const CLI_MODE = rawMode;
36
- const MODE = MODE_MAP[CLI_MODE];
4
+ import { runCli } from "./server/cli.js";
37
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
38
- const publicDir = resolve(__dirname, "..", "public");
39
- const workspaceRoot = process.env.BORING_AGENT_WORKSPACE_ROOT ?? process.cwd();
40
- const projectName = basename(resolve(workspaceRoot)) || "workspace";
41
- if (!existsSync(publicDir)) {
42
- console.error("\nError: boring-ui frontend not found.");
43
- console.error("Run `pnpm build:full` in packages/cli to build it first.\n");
44
- process.exit(1);
45
- }
46
- function openBrowser(url) {
47
- try {
48
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
49
- execSync(`${opener} ${url}`, { stdio: "ignore" });
50
- } catch {
51
- }
52
- }
53
- async function loginWithPi(auth) {
54
- const term = new ProcessTerminal();
55
- const tui = new TUI(term, false);
56
- initTheme("dark");
57
- tui.start();
58
- const providerId = await new Promise((resolve2, reject) => {
59
- const selector = new OAuthSelectorComponent(
60
- "login",
61
- auth,
62
- resolve2,
63
- () => reject(new Error("Login cancelled"))
64
- );
65
- tui.addChild(selector);
66
- tui.setFocus(selector);
67
- tui.requestRender();
68
- });
69
- await new Promise((resolve2, reject) => {
70
- const dialog = new LoginDialogComponent(tui, providerId, (success) => {
71
- success !== false ? resolve2() : reject(new Error("Login cancelled"));
72
- });
73
- tui.addChild(dialog);
74
- tui.setFocus(dialog);
75
- tui.requestRender();
76
- auth.login(providerId, {
77
- onAuth: ({ url }) => {
78
- openBrowser(url);
79
- dialog.showAuth(url, "Your browser should open automatically. Waiting for login\u2026");
80
- },
81
- onProgress: (msg) => dialog.showProgress(msg),
82
- onPrompt: ({ message }) => new Promise((res) => {
83
- dialog.showManualInput(message);
84
- const rl = createInterface({ input: process.stdin, output: process.stdout });
85
- rl.question("", (code) => {
86
- rl.close();
87
- res(code.trim());
88
- });
89
- })
90
- }).catch(reject);
6
+ try {
7
+ await runCli({
8
+ argv: process.argv.slice(2),
9
+ publicDir: resolve(__dirname, "..", "public")
91
10
  });
92
- tui.stop();
93
- term.stop();
94
- }
95
- async function resolveApiKey() {
96
- if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY;
97
- const auth = AuthStorage.create();
98
- await auth.reload();
99
- const stored = await auth.getApiKey("anthropic");
100
- if (stored) return stored;
101
- console.log("\nNo API key found \u2014 launching login\u2026\n");
102
- await loginWithPi(auth);
103
- const key = await auth.getApiKey("anthropic");
104
- if (!key) {
105
- console.error("\nLogin failed. Set ANTHROPIC_API_KEY manually.\n");
106
- process.exit(1);
107
- }
108
- return key;
11
+ } catch (error) {
12
+ console.error(error instanceof Error ? error.message : String(error));
13
+ process.exit(1);
109
14
  }
110
- const apiKey = await resolveApiKey();
111
- process.env.ANTHROPIC_API_KEY = apiKey;
112
- console.log(`
113
- ${projectName}`);
114
- console.log(` workspace ${workspaceRoot}`);
115
- console.log(` mode ${CLI_MODE}`);
116
- console.log(` port ${PORT}`);
117
- console.log(` host ${HOST}`);
118
- const app = await createWorkspaceAgentServer({
119
- workspaceRoot,
120
- mode: MODE,
121
- logger: false
122
- });
123
- app.get("/api/v1/workspace/meta", async () => ({
124
- workspaceRoot,
125
- projectName
126
- }));
127
- await app.register(fastifyStatic, {
128
- root: publicDir,
129
- prefix: "/",
130
- wildcard: false
131
- });
132
- app.setNotFoundHandler(async (req, reply) => {
133
- if (req.url.startsWith("/api/")) {
134
- return reply.code(404).send({ error: "Not found" });
135
- }
136
- return reply.sendFile("index.html", publicDir);
137
- });
138
- await app.listen({ port: PORT, host: HOST });
139
- console.log(`
140
- http://localhost:${PORT}
141
- `);
142
- openBrowser(`http://localhost:${PORT}`);