culater 1.0.5 → 1.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 +55 -26
- package/assets/culater-demo.gif +0 -0
- package/bin/culater.js +24 -3
- package/lib/server.js +994 -155
- package/package.json +13 -2
- package/remotion/Root.jsx +16 -0
- package/remotion/Video.jsx +437 -0
- package/remotion/index.jsx +4 -0
- package/remotion/public/demo/phone-live.png +0 -0
- package/remotion/public/demo/phone-login.png +0 -0
- package/remotion/public/demo/phone-start.png +0 -0
- package/scripts/make-readme-gif.mjs +256 -0
package/README.md
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# culater
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Your AI terminal, anywhere.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`culater` gives you fast phone access to your local Claude Code shell through a secure Cloudflare tunnel, so you can check output, unblock an agent, and keep moving when you're away from your desk.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Demo
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="assets/culater-demo.gif" alt="culater demo" width="720">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
## Why culater
|
|
14
|
+
|
|
15
|
+
- Agent-first remote terminal workflow from any mobile browser
|
|
16
|
+
- Session survives refreshes and temporary network drops
|
|
17
|
+
- Project-aware startup (recent folders remembered in `~/.culater/config.json`)
|
|
18
|
+
- Manual `AI` launch when you want Claude, no forced auto-start
|
|
19
|
+
- One command to start, no account setup
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
### 1) Install requirements
|
|
14
24
|
|
|
15
25
|
- Node.js 18+
|
|
16
26
|
- [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/)
|
|
@@ -23,10 +33,39 @@ brew install cloudflared
|
|
|
23
33
|
sudo apt install cloudflared
|
|
24
34
|
```
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
### 2) Start culater
|
|
27
37
|
|
|
38
|
+
```bash
|
|
39
|
+
npx culater mypassword
|
|
28
40
|
```
|
|
29
|
-
|
|
41
|
+
|
|
42
|
+
### 3) Open it on your phone
|
|
43
|
+
|
|
44
|
+
- Scan the terminal QR code
|
|
45
|
+
- Enter the password
|
|
46
|
+
- Start shell (or auto-start if no previous project history exists)
|
|
47
|
+
|
|
48
|
+
## How It Works
|
|
49
|
+
|
|
50
|
+
1. `culater` starts a local PTY shell.
|
|
51
|
+
2. It creates a temporary Cloudflare tunnel URL.
|
|
52
|
+
3. Your phone connects to the web terminal and controls that shell in real time.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- Mobile-optimized terminal UI
|
|
57
|
+
- Touch scrolling with momentum
|
|
58
|
+
- Quick action buttons (`/`, `Esc`, `Enter`, cursor pad)
|
|
59
|
+
- Keyboard-aware controls that stay above the fold
|
|
60
|
+
- Auto-reconnect and persistent session resume
|
|
61
|
+
- Manual `AI` button to run Claude on demand
|
|
62
|
+
- Password gate before terminal access
|
|
63
|
+
- Optional ntfy push notifications for tunnel URL
|
|
64
|
+
|
|
65
|
+
## CLI Options
|
|
66
|
+
|
|
67
|
+
```txt
|
|
68
|
+
-n, --ntfy <topic> ntfy.sh topic for push notifications (saved)
|
|
30
69
|
-d, --dir <path> Working directory (default: current)
|
|
31
70
|
-h, --help Show help
|
|
32
71
|
```
|
|
@@ -37,27 +76,17 @@ sudo apt install cloudflared
|
|
|
37
76
|
# Start with password
|
|
38
77
|
npx culater mysecret
|
|
39
78
|
|
|
40
|
-
#
|
|
41
|
-
npx culater mysecret -n my-ntfy-topic
|
|
42
|
-
|
|
43
|
-
# Subsequent runs use saved ntfy topic automatically
|
|
44
|
-
npx culater mysecret
|
|
45
|
-
|
|
46
|
-
# Specific directory
|
|
79
|
+
# Use a specific directory
|
|
47
80
|
npx culater mysecret -d ~/projects/myapp
|
|
81
|
+
|
|
82
|
+
# Enable ntfy notifications (saved for later runs)
|
|
83
|
+
npx culater mysecret -n my-ntfy-topic
|
|
48
84
|
```
|
|
49
85
|
|
|
50
|
-
##
|
|
86
|
+
## Notes
|
|
51
87
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
- Quick action buttons (/, Esc, ↓, Enter)
|
|
55
|
-
- Auto-reconnect on disconnect
|
|
56
|
-
- Keyboard-aware button positioning
|
|
57
|
-
- Streaming indicator
|
|
58
|
-
- Password protection
|
|
59
|
-
- Secure cloudflare tunnel
|
|
60
|
-
- Push notifications via ntfy.sh (remembers your topic)
|
|
88
|
+
- Config is stored at `~/.culater/config.json`.
|
|
89
|
+
- First tunnel open can briefly show Cloudflare `1033` while edge registration finishes.
|
|
61
90
|
|
|
62
91
|
## License
|
|
63
92
|
|
|
Binary file
|
package/bin/culater.js
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
5
7
|
|
|
6
|
-
const
|
|
8
|
+
const CONFIG_DIR = path.join(os.homedir(), '.culater');
|
|
9
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
10
|
+
const LEGACY_CONFIG_FILE = '/tmp/culater.json';
|
|
11
|
+
const MAX_RECENT_DIRS = 12;
|
|
7
12
|
|
|
8
13
|
// Check for cloudflared
|
|
9
14
|
try {
|
|
@@ -19,13 +24,18 @@ function loadConfig() {
|
|
|
19
24
|
try {
|
|
20
25
|
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
21
26
|
} catch {
|
|
22
|
-
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(fs.readFileSync(LEGACY_CONFIG_FILE, 'utf8'));
|
|
29
|
+
} catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
// Save config
|
|
27
36
|
function saveConfig(config) {
|
|
28
37
|
try {
|
|
38
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
29
39
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
30
40
|
} catch {}
|
|
31
41
|
}
|
|
@@ -63,20 +73,31 @@ if (!password) {
|
|
|
63
73
|
process.exit(1);
|
|
64
74
|
}
|
|
65
75
|
|
|
76
|
+
workDir = path.resolve(workDir);
|
|
77
|
+
|
|
66
78
|
// Load config and use saved ntfy if not provided
|
|
67
79
|
const config = loadConfig();
|
|
68
80
|
if (ntfyTopic) {
|
|
69
81
|
// Save new ntfy topic
|
|
70
82
|
config.ntfyTopic = ntfyTopic;
|
|
71
|
-
saveConfig(config);
|
|
72
83
|
} else if (config.ntfyTopic) {
|
|
73
84
|
// Use saved ntfy topic
|
|
74
85
|
ntfyTopic = config.ntfyTopic;
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
const recentDirs = Array.isArray(config.recentDirs) ? config.recentDirs : [];
|
|
89
|
+
const normalizedRecent = recentDirs
|
|
90
|
+
.filter(dir => typeof dir === 'string' && dir.trim())
|
|
91
|
+
.map(dir => path.resolve(dir));
|
|
92
|
+
const previousRecentDirs = Array.from(new Set(normalizedRecent)).slice(0, MAX_RECENT_DIRS);
|
|
93
|
+
const nextRecentDirs = [workDir, ...previousRecentDirs.filter(dir => dir !== workDir)].slice(0, MAX_RECENT_DIRS);
|
|
94
|
+
config.recentDirs = nextRecentDirs;
|
|
95
|
+
saveConfig(config);
|
|
96
|
+
|
|
77
97
|
// Set env and run server
|
|
78
98
|
process.env.REMOTE_PASSWORD = password;
|
|
79
99
|
process.env.NTFY_TOPIC = ntfyTopic || '';
|
|
80
100
|
process.env.WORK_DIR = workDir;
|
|
101
|
+
process.env.RECENT_DIRS = JSON.stringify(previousRecentDirs);
|
|
81
102
|
|
|
82
103
|
require('../lib/server.js');
|