otherwise-cli 0.1.0
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 +193 -0
- package/bin/otherwise.js +5 -0
- package/frontend/404.html +84 -0
- package/frontend/assets/OpenDyslexic3-Bold-CDyRs55Y.ttf +0 -0
- package/frontend/assets/OpenDyslexic3-Regular-CIBXa4WE.ttf +0 -0
- package/frontend/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/frontend/assets/conversational-worker-CeKiciGk.js +2929 -0
- package/frontend/assets/dictation-worker-D0aYfq8b.js +29 -0
- package/frontend/assets/gemini-color-CgSQmmva.png +0 -0
- package/frontend/assets/index-BLux5ps4.js +21 -0
- package/frontend/assets/index-Blh8_TEM.js +5272 -0
- package/frontend/assets/index-BpQ1PuKu.js +18 -0
- package/frontend/assets/index-Df737c8w.css +1 -0
- package/frontend/assets/index-xaYHL6wb.js +113 -0
- package/frontend/assets/ort-wasm-simd-threaded.asyncify-BynIiDiv.wasm +0 -0
- package/frontend/assets/ort-wasm-simd-threaded.jsep-B0T3yYHD.wasm +0 -0
- package/frontend/assets/transformers-tULNc5V3.js +31 -0
- package/frontend/assets/tts-worker-DPJWqT7N.js +2899 -0
- package/frontend/assets/voice-mode-worker-GzvIE_uh.js +2927 -0
- package/frontend/assets/worker-2d5ABSLU.js +31 -0
- package/frontend/banner.png +0 -0
- package/frontend/favicon.svg +3 -0
- package/frontend/google55e5ec47ee14a5f8.html +1 -0
- package/frontend/index.html +234 -0
- package/frontend/manifest.json +17 -0
- package/frontend/pdf.worker.min.mjs +21 -0
- package/frontend/robots.txt +5 -0
- package/frontend/sitemap.xml +27 -0
- package/package.json +81 -0
- package/src/agent/index.js +1066 -0
- package/src/agent/location.js +51 -0
- package/src/agent/prompt.js +548 -0
- package/src/agent/tools.js +4372 -0
- package/src/browser/detect.js +68 -0
- package/src/browser/session.js +1109 -0
- package/src/config.js +137 -0
- package/src/email/client.js +503 -0
- package/src/index.js +557 -0
- package/src/inference/anthropic.js +113 -0
- package/src/inference/google.js +373 -0
- package/src/inference/index.js +81 -0
- package/src/inference/ollama.js +383 -0
- package/src/inference/openai.js +140 -0
- package/src/inference/openrouter.js +378 -0
- package/src/inference/xai.js +200 -0
- package/src/logBridge.js +9 -0
- package/src/models.js +146 -0
- package/src/remote/client.js +225 -0
- package/src/scheduler/cron.js +243 -0
- package/src/server.js +3876 -0
- package/src/storage/db.js +1135 -0
- package/src/storage/supabase.js +364 -0
- package/src/tunnel/cloudflare.js +241 -0
- package/src/ui/components/App.jsx +687 -0
- package/src/ui/components/BrowserSelect.jsx +111 -0
- package/src/ui/components/FilePicker.jsx +472 -0
- package/src/ui/components/Header.jsx +444 -0
- package/src/ui/components/HelpPanel.jsx +173 -0
- package/src/ui/components/HistoryPanel.jsx +158 -0
- package/src/ui/components/MessageList.jsx +235 -0
- package/src/ui/components/ModelSelector.jsx +304 -0
- package/src/ui/components/PromptInput.jsx +515 -0
- package/src/ui/components/StreamingResponse.jsx +134 -0
- package/src/ui/components/ThinkingIndicator.jsx +365 -0
- package/src/ui/components/ToolExecution.jsx +714 -0
- package/src/ui/components/index.js +82 -0
- package/src/ui/context/TerminalContext.jsx +150 -0
- package/src/ui/context/index.js +13 -0
- package/src/ui/hooks/index.js +16 -0
- package/src/ui/hooks/useChatState.js +675 -0
- package/src/ui/hooks/useCommands.js +280 -0
- package/src/ui/hooks/useFileAttachments.js +216 -0
- package/src/ui/hooks/useKeyboardShortcuts.js +173 -0
- package/src/ui/hooks/useNotifications.js +185 -0
- package/src/ui/hooks/useTerminalSize.js +151 -0
- package/src/ui/hooks/useWebSocket.js +273 -0
- package/src/ui/index.js +94 -0
- package/src/ui/ink-runner.js +22 -0
- package/src/ui/utils/formatters.js +424 -0
- package/src/ui/utils/index.js +6 -0
- package/src/ui/utils/markdown.js +166 -0
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Otherwise CLI
|
|
2
|
+
|
|
3
|
+
Your personal AI assistant that lives on your computer.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
**From npm (recommended)**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g otherwise-cli
|
|
11
|
+
otherwise
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**From source**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd cli
|
|
18
|
+
npm install
|
|
19
|
+
npm run build:frontend # build web UI (required before first run)
|
|
20
|
+
npm link # makes 'otherwise' available globally
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Start the server
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
otherwise # Start server + Ink terminal UI
|
|
29
|
+
otherwise --verbose # Start server, no Ink UI — raw logs in terminal, chat in browser
|
|
30
|
+
otherwise -server-only # Start server only (no CLI, no logs)
|
|
31
|
+
otherwise -port 8080 # Use different port
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Configure API keys
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
otherwise config # Show current config
|
|
38
|
+
otherwise config set anthropic sk-... # Set Anthropic API key
|
|
39
|
+
otherwise config set openai sk-... # Set OpenAI API key
|
|
40
|
+
otherwise config set google ... # Set Google API key
|
|
41
|
+
otherwise config set xai ... # Set xAI API key
|
|
42
|
+
otherwise config set model gpt-4o # Change default model
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Deploy publicly
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
otherwise deploy -quick # Quick tunnel (temporary URL)
|
|
49
|
+
otherwise deploy # Named tunnel (custom domain)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Connect to otherwise.ai (remote)
|
|
53
|
+
|
|
54
|
+
Link this CLI to your otherwise.ai account so the web app routes chat to this machine:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
otherwise connect # Device code flow: enter short code at otherwise.ai/connect
|
|
58
|
+
otherwise connect <token> # Or paste a pairing token from Settings → Connect your CLI
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Chat persistence:** When the frontend is connected to the backend (otherwise.ai), chat list and history are read from Supabase. For new chats created on this CLI to appear in the cloud and survive refresh, set Supabase env vars on the machine running the CLI:
|
|
62
|
+
|
|
63
|
+
- `SUPABASE_URL` – your project URL
|
|
64
|
+
- `SUPABASE_SERVICE_KEY` – service role key (backend only)
|
|
65
|
+
|
|
66
|
+
If these are not set, the CLI will log: _"Frontend sent accessToken but Supabase is not configured..."_ and new chats will only exist locally (they disappear from the web app on refresh).
|
|
67
|
+
|
|
68
|
+
## Features
|
|
69
|
+
|
|
70
|
+
- **Local AI Server** - Runs on your computer with access to your filesystem
|
|
71
|
+
- **Multiple Providers** - Claude, GPT, Gemini, Grok, Ollama
|
|
72
|
+
- **Agent Tools** - File read/write, shell commands, web search
|
|
73
|
+
- **Email Integration** - IMAP/SMTP for reading and sending email
|
|
74
|
+
- **Scheduled Tasks** - Cron-based task scheduling
|
|
75
|
+
- **Public Access** - Cloudflare Tunnel for custom domain hosting
|
|
76
|
+
|
|
77
|
+
## Project Structure
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
cli/
|
|
81
|
+
├── bin/otherwise.js # CLI entry point
|
|
82
|
+
├── src/
|
|
83
|
+
│ ├── index.js # Commander CLI
|
|
84
|
+
│ ├── server.js # Fastify server
|
|
85
|
+
│ ├── config.js # Configuration store
|
|
86
|
+
│ ├── inference/ # LLM providers
|
|
87
|
+
│ ├── agent/ # Agent loop & tools
|
|
88
|
+
│ ├── storage/ # SQLite database
|
|
89
|
+
│ ├── scheduler/ # Cron tasks
|
|
90
|
+
│ ├── email/ # IMAP/SMTP
|
|
91
|
+
│ └── tunnel/ # Cloudflare
|
|
92
|
+
└── frontend/ # Built React app (after build)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Build frontend and copy to CLI
|
|
99
|
+
npm run build:frontend
|
|
100
|
+
|
|
101
|
+
# Run in development mode
|
|
102
|
+
npm run dev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
Config is stored at `~/.otherwise/config.json`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"apiKeys": {
|
|
112
|
+
"anthropic": "sk-...",
|
|
113
|
+
"openai": "sk-...",
|
|
114
|
+
"google": "...",
|
|
115
|
+
"xai": "..."
|
|
116
|
+
},
|
|
117
|
+
"model": "claude-sonnet-4-20250514",
|
|
118
|
+
"permissions": {
|
|
119
|
+
"fileRead": ["~/*"],
|
|
120
|
+
"fileWrite": ["~/ai-workspace/*"],
|
|
121
|
+
"shell": true,
|
|
122
|
+
"email": true
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Agent Tools
|
|
128
|
+
|
|
129
|
+
The AI can use these tools during conversations:
|
|
130
|
+
|
|
131
|
+
| Tool | Description |
|
|
132
|
+
| ---------------------- | ------------------------- |
|
|
133
|
+
| `read_file` | Read file contents |
|
|
134
|
+
| `write_file` | Create or update files |
|
|
135
|
+
| `list_directory` | List files in a directory |
|
|
136
|
+
| `search_files` | Search for files by name |
|
|
137
|
+
| `execute_command` | Run shell commands |
|
|
138
|
+
| `web_search` | Search the web |
|
|
139
|
+
| `fetch_url` | Fetch content from a URL |
|
|
140
|
+
| `check_email` | Check inbox (IMAP) |
|
|
141
|
+
| `send_email` | Send email (SMTP) |
|
|
142
|
+
| `schedule_task` | Create a scheduled task |
|
|
143
|
+
| `list_scheduled_tasks` | View scheduled tasks |
|
|
144
|
+
| `cancel_task` | Cancel a scheduled task |
|
|
145
|
+
|
|
146
|
+
### Debugging web search
|
|
147
|
+
|
|
148
|
+
When an engine returns 0 results, the CLI logs pattern counts and **the first 2,500 characters of the response HTML** so you can see what was actually returned. Run with **verbose** to see these logs:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
otherwise -verbose
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
To write the **full HTML** for failed engines to a temp file (e.g. `otherwise-websearch-google-1234567890.html`), set:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
WEBSEARCH_DEBUG=1 otherwise -verbose
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Google and Bing requests use crawler User-Agents (AdsBot-Google and bingbot) to reduce bot detection and improve the chance of receiving static SERP HTML instead of captchas or JS-only pages.
|
|
161
|
+
|
|
162
|
+
When an engine returns **0 results**, the CLI automatically retries that engine using a **headless browser** (Playwright) so JavaScript-rendered or bot-blocked pages (e.g. Google, Ecosia/Cloudflare) can be parsed. To disable this fallback (e.g. to avoid launching Chromium), set:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
WEBSEARCH_USE_BROWSER=0 otherwise -verbose
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Proxy for Google and Bing
|
|
169
|
+
|
|
170
|
+
To reduce captcha/blocking from Google and Bing, you can route **all web search traffic** (initial fetch and browser retry) through a proxy:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# HTTP/HTTPS proxy (used for every search engine request and headless browser)
|
|
174
|
+
export WEBSEARCH_PROXY=http://proxy.example.com:8080
|
|
175
|
+
# Or use standard env vars (same behavior)
|
|
176
|
+
export HTTPS_PROXY=http://proxy.example.com:8080
|
|
177
|
+
# With auth:
|
|
178
|
+
export WEBSEARCH_PROXY=http://user:pass@proxy.example.com:8080
|
|
179
|
+
otherwise -verbose
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Only **web search** requests use this proxy; other tools (e.g. `fetch_url`, API calls) are unchanged.
|
|
183
|
+
|
|
184
|
+
### SerpAPI (optional) for Google and Bing
|
|
185
|
+
|
|
186
|
+
For reliable Google and Bing results without captcha or scraping, set **SerpAPI** (free tier: 100 searches/month):
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
export SERPAPI_API_KEY=your_serpapi_key
|
|
190
|
+
otherwise
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
When `SERPAPI_API_KEY` is set, Google and Bing use SerpAPI’s JSON API instead of scraping. Other engines (Brave, DuckDuckGo, Startpage, Yahoo, Ecosia) still use direct fetch/browser. Get a key at [serpapi.com](https://serpapi.com/).
|
package/bin/otherwise.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>404 - Page Not Found | Otherwise</title>
|
|
7
|
+
<meta name="robots" content="noindex, follow" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="The page you're looking for doesn't exist. Return to Otherwise homepage."
|
|
11
|
+
/>
|
|
12
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
13
|
+
<link rel="alternate icon" href="/favicon.ico" />
|
|
14
|
+
|
|
15
|
+
<style media="screen">
|
|
16
|
+
body {
|
|
17
|
+
background: #111827;
|
|
18
|
+
color: #e5e7eb;
|
|
19
|
+
font-family:
|
|
20
|
+
ui-sans-serif,
|
|
21
|
+
system-ui,
|
|
22
|
+
-apple-system,
|
|
23
|
+
BlinkMacSystemFont,
|
|
24
|
+
'Segoe UI',
|
|
25
|
+
Roboto,
|
|
26
|
+
'Helvetica Neue',
|
|
27
|
+
Arial,
|
|
28
|
+
'Noto Sans',
|
|
29
|
+
sans-serif,
|
|
30
|
+
'Apple Color Emoji',
|
|
31
|
+
'Segoe UI Emoji',
|
|
32
|
+
'Segoe UI Symbol',
|
|
33
|
+
'Noto Color Emoji';
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
align-items: center;
|
|
40
|
+
height: 100vh;
|
|
41
|
+
text-align: center;
|
|
42
|
+
}
|
|
43
|
+
pre {
|
|
44
|
+
font-family:
|
|
45
|
+
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
46
|
+
'Liberation Mono', 'Courier New', monospace;
|
|
47
|
+
font-size: 0.875rem;
|
|
48
|
+
line-height: 1.25rem;
|
|
49
|
+
white-space: pre;
|
|
50
|
+
}
|
|
51
|
+
h1 {
|
|
52
|
+
font-size: 1.875rem; /* 30px */
|
|
53
|
+
line-height: 2.25rem; /* 36px */
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
margin-top: 2rem;
|
|
56
|
+
margin-bottom: 1rem;
|
|
57
|
+
}
|
|
58
|
+
h2 {
|
|
59
|
+
font-size: 1.125rem; /* 18px */
|
|
60
|
+
line-height: 1.75rem; /* 28px */
|
|
61
|
+
font-weight: 400;
|
|
62
|
+
margin-bottom: 2rem;
|
|
63
|
+
}
|
|
64
|
+
a {
|
|
65
|
+
display: inline-block;
|
|
66
|
+
background: #4f46e5;
|
|
67
|
+
color: white;
|
|
68
|
+
padding: 0.75rem 1.5rem;
|
|
69
|
+
border-radius: 0.5rem;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
transition: background-color 0.2s;
|
|
73
|
+
}
|
|
74
|
+
a:hover {
|
|
75
|
+
background: #4338ca;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<h1>404 - Page Not Found</h1>
|
|
81
|
+
<h2>Looks like you're lost in the digital wilderness.</h2>
|
|
82
|
+
<a href="/">Go back home</a>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={};export{e as default};
|