ai-or-die 0.1.67 → 0.1.68
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 +12 -7
- package/bin/ai-or-die.js +13 -6
- package/bin/supervisor.js +11 -1
- package/package.json +1 -1
- package/src/public/app.js +8 -0
- package/src/public/icon-144.png +0 -0
- package/src/public/icon-16.png +0 -0
- package/src/public/icon-180.png +0 -0
- package/src/public/icon-192.png +0 -0
- package/src/public/icon-32.png +0 -0
- package/src/public/icon-512.png +0 -0
- package/src/public/index.html +4 -1
- package/src/public/manifest.json +1 -1
- package/src/public/voice-handler.js +1 -1
- package/src/server.js +11 -29
package/README.md
CHANGED
|
@@ -112,9 +112,12 @@ Download from [Releases](https://github.com/animeshkundu/ai-or-die/releases) —
|
|
|
112
112
|
## Usage
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
|
-
# Default —
|
|
115
|
+
# Default — prints the URL (with secure token); does NOT auto-open a browser
|
|
116
116
|
ai-or-die
|
|
117
117
|
|
|
118
|
+
# Open the browser automatically on start
|
|
119
|
+
ai-or-die --open
|
|
120
|
+
|
|
118
121
|
# Custom port
|
|
119
122
|
ai-or-die --port 8080
|
|
120
123
|
|
|
@@ -146,11 +149,11 @@ ai-or-die --stt
|
|
|
146
149
|
| `--disable-auth` | Disable authentication | `false` |
|
|
147
150
|
| `--tunnel` | Enable Microsoft Dev Tunnel | `false` |
|
|
148
151
|
| `--tunnel-allow-anonymous` | Allow anonymous tunnel access | `false` |
|
|
149
|
-
| `--https` | Enable HTTPS | `false` |
|
|
152
|
+
| `--https` | Enable HTTPS (auto-generates a self-signed cert if `--cert`/`--key` not given) | `false` |
|
|
150
153
|
| `--cert <path>` | SSL certificate file | |
|
|
151
154
|
| `--key <path>` | SSL private key file | |
|
|
152
155
|
| `--dev` | Verbose logging | `false` |
|
|
153
|
-
| `--
|
|
156
|
+
| `--open` | Open the browser on start (never on supervised restart) | `false` |
|
|
154
157
|
| `--plan <type>` | Subscription plan (`pro`, `max5`, `max20`) | `max20` |
|
|
155
158
|
| `--stt` | Enable local speech-to-text | `false` |
|
|
156
159
|
| `--stt-endpoint <url>` | External STT endpoint (OpenAI-compatible) | |
|
|
@@ -190,11 +193,13 @@ ai-or-die is an installable progressive web app. Open Settings → Install in th
|
|
|
190
193
|
|
|
191
194
|
**Browser support**: Chrome / Edge / Samsung Internet support one-tap install. Firefox desktop does not have native install — use the browser menu to pin the tab. Safari iOS uses Share → Add to Home Screen.
|
|
192
195
|
|
|
193
|
-
**Installing on LAN devices**: Browsers refuse to install PWAs over an HTTPS origin whose certificate isn't trusted by the device.
|
|
196
|
+
**Installing on LAN devices**: Browsers refuse to install PWAs over an HTTPS origin whose certificate isn't trusted by the device, and public CAs (Let's Encrypt etc.) won't issue certs for a LAN IP or `localhost`. The `--https` self-signed cert therefore triggers a browser warning, and the in-app Install panel will say *"Not available in this browser."* on a LAN IP. Ways to get an installable origin:
|
|
197
|
+
|
|
198
|
+
1. **On the host machine itself** — just open **`http://localhost:<port>`**. `localhost` is a secure context, so the PWA installs with **no cert and no setup** (no `--https` needed).
|
|
199
|
+
2. **On other devices** — use **`--tunnel`** *(recommended)*: Microsoft Dev Tunnels gives a public URL with a real publicly-trusted cert, so any device installs the PWA with **no per-device setup and no admin**.
|
|
200
|
+
3. **Bring your own trusted cert** via `--cert` / `--key` (e.g. a cert for a hostname your devices already trust).
|
|
194
201
|
|
|
195
|
-
|
|
196
|
-
2. **Trust the self-signed cert** on each device — copy `~/.ai-or-die/certs/server.cert` to the device and install it as a trusted root in the OS certificate store.
|
|
197
|
-
3. **Provide a CA-signed cert** via `--cert` / `--key`. [mkcert](https://github.com/FiloSottile/mkcert) is a low-friction option for development.
|
|
202
|
+
See [docs/history/pwa-install-lan-self-signed-cert.md](docs/history/pwa-install-lan-self-signed-cert.md) for the underlying browser-policy reasoning.
|
|
198
203
|
|
|
199
204
|
See [docs/history/pwa-install-lan-self-signed-cert.md](docs/history/pwa-install-lan-self-signed-cert.md) for the device-by-device procedure and the underlying browser-policy reasoning.
|
|
200
205
|
|
package/bin/ai-or-die.js
CHANGED
|
@@ -20,7 +20,7 @@ program
|
|
|
20
20
|
.description('ai-or-die — Universal AI coding terminal')
|
|
21
21
|
.version(require('../package.json').version)
|
|
22
22
|
.option('-p, --port <number>', 'port to run the server on', '7777')
|
|
23
|
-
.option('--
|
|
23
|
+
.option('--open', 'open the browser on start (default: off; never on supervised restart)')
|
|
24
24
|
.option('--auth <token>', 'authentication token for secure access')
|
|
25
25
|
.option('--disable-auth', 'disable authentication (not recommended for production)')
|
|
26
26
|
.option('--https', 'enable HTTPS (auto-generates self-signed cert if --cert/--key not provided)')
|
|
@@ -38,8 +38,11 @@ program
|
|
|
38
38
|
.option('--stt', 'enable local speech-to-text (downloads ~670MB Parakeet V3 model on first use)')
|
|
39
39
|
.option('--stt-endpoint <url>', 'use external STT endpoint (OpenAI-compatible)')
|
|
40
40
|
.option('--stt-model-dir <path>', 'custom directory for STT model files')
|
|
41
|
-
.option('--stt-threads <number>', 'CPU threads for STT inference (default: auto, max 4)')
|
|
42
|
-
|
|
41
|
+
.option('--stt-threads <number>', 'CPU threads for STT inference (default: auto, max 4)');
|
|
42
|
+
|
|
43
|
+
// Auto-open is OFF by default and opt-in via --open. Legacy callers may still pass
|
|
44
|
+
// --no-open (the old opt-out flag); filter it out so it parses harmlessly as a no-op.
|
|
45
|
+
program.parse(process.argv.filter((arg) => arg !== '--no-open'));
|
|
43
46
|
|
|
44
47
|
const options = program.opts();
|
|
45
48
|
|
|
@@ -131,7 +134,11 @@ async function main() {
|
|
|
131
134
|
console.log(' For LAN access, restart with \x1b[1m--https\x1b[0m or \x1b[1m--tunnel\x1b[0m.');
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
// Dev tunnel or browser open
|
|
137
|
+
// Dev tunnel or browser open.
|
|
138
|
+
// Auto-open only when explicitly requested (--open) AND this is the first launch,
|
|
139
|
+
// never on a supervised restart (the supervisor sets AOD_SUPERVISOR_RESTART on respawn),
|
|
140
|
+
// so crash/memory restarts don't spawn a new browser tab each time.
|
|
141
|
+
const shouldOpen = !!options.open && !process.env.AOD_SUPERVISOR_RESTART;
|
|
135
142
|
let tunnel = null;
|
|
136
143
|
if (options.tunnel) {
|
|
137
144
|
const { TunnelManager } = require('../src/tunnel-manager');
|
|
@@ -141,12 +148,12 @@ async function main() {
|
|
|
141
148
|
dev: options.dev,
|
|
142
149
|
onUrl: (tunnelUrl) => {
|
|
143
150
|
console.log(`\n \x1b[1m\x1b[32mTunnel ready:\x1b[0m \x1b[1m\x1b[4m${tunnelUrl}\x1b[0m\n`);
|
|
144
|
-
if (open &&
|
|
151
|
+
if (open && shouldOpen) open(tunnelUrl).catch(() => {});
|
|
145
152
|
}
|
|
146
153
|
});
|
|
147
154
|
app.setTunnelManager(tunnel);
|
|
148
155
|
await tunnel.start();
|
|
149
|
-
} else if (
|
|
156
|
+
} else if (shouldOpen) {
|
|
150
157
|
try { if (open) await open(url); } catch (error) {
|
|
151
158
|
console.warn(' Could not automatically open browser:', error.message);
|
|
152
159
|
}
|
package/bin/supervisor.js
CHANGED
|
@@ -45,6 +45,7 @@ const forwardedArgs = process.argv.slice(2);
|
|
|
45
45
|
|
|
46
46
|
let child = null;
|
|
47
47
|
let shuttingDown = false;
|
|
48
|
+
let spawnCount = 0;
|
|
48
49
|
let crashTimestamps = [];
|
|
49
50
|
let pendingRestartTimer = null;
|
|
50
51
|
|
|
@@ -110,9 +111,18 @@ function startServer() {
|
|
|
110
111
|
pendingRestartTimer = null;
|
|
111
112
|
const nodeArgs = ['--expose-gc', serverScript, ...forwardedArgs];
|
|
112
113
|
|
|
114
|
+
// Mark every spawn after the first as a supervised restart, so the child suppresses
|
|
115
|
+
// browser auto-open (--open) on crash/memory restarts and only opens on first launch.
|
|
116
|
+
const isRestart = spawnCount > 0;
|
|
117
|
+
spawnCount += 1;
|
|
118
|
+
|
|
113
119
|
child = spawn(process.execPath, nodeArgs, {
|
|
114
120
|
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
115
|
-
env: {
|
|
121
|
+
env: {
|
|
122
|
+
...process.env,
|
|
123
|
+
SUPERVISED: '1',
|
|
124
|
+
...(isRestart ? { AOD_SUPERVISOR_RESTART: '1' } : {})
|
|
125
|
+
}
|
|
116
126
|
});
|
|
117
127
|
|
|
118
128
|
// Flush a queued supervisor_warning into the new child's IPC channel.
|
package/package.json
CHANGED
package/src/public/app.js
CHANGED
|
@@ -3756,6 +3756,14 @@ class ClaudeCodeWebInterface {
|
|
|
3756
3756
|
case 'dismissed':
|
|
3757
3757
|
statusText.textContent = 'Install was cancelled. Reload the page to try again.';
|
|
3758
3758
|
break;
|
|
3759
|
+
case 'unavailable':
|
|
3760
|
+
// Chromium-family browser in a secure context, but beforeinstallprompt
|
|
3761
|
+
// hasn't fired yet (engagement-gated, or already consumed). The app can't
|
|
3762
|
+
// trigger install itself, but the browser's own menu can — and the
|
|
3763
|
+
// beforeinstallprompt listener stays active, so this upgrades to 'available'
|
|
3764
|
+
// if the event fires later.
|
|
3765
|
+
statusText.textContent = 'If no install button appears, use your browser menu → "Install this site as an app".';
|
|
3766
|
+
break;
|
|
3759
3767
|
default:
|
|
3760
3768
|
statusText.textContent = 'Not available in this browser.';
|
|
3761
3769
|
break;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/public/index.html
CHANGED
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
<meta name="format-detection" content="telephone=no">
|
|
17
17
|
|
|
18
18
|
<!-- Web App Manifest -->
|
|
19
|
-
|
|
19
|
+
<!-- crossorigin=use-credentials: send the session cookie with the manifest (and its icon)
|
|
20
|
+
fetches so they are not redirected to an auth page behind a credentialed proxy/tunnel
|
|
21
|
+
(e.g. Microsoft devtunnel), which otherwise 404s the manifest and breaks installability. -->
|
|
22
|
+
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|
|
20
23
|
|
|
21
24
|
<!-- Icons for various platforms -->
|
|
22
25
|
<link rel="icon" type="image/png" sizes="32x32" href="/icon-32.png">
|
package/src/public/manifest.json
CHANGED
|
@@ -135,7 +135,7 @@ SpeechRecognitionRecorder.prototype.start = function () {
|
|
|
135
135
|
var recognition = new SpeechRecognitionCtor();
|
|
136
136
|
recognition.continuous = true;
|
|
137
137
|
recognition.interimResults = false;
|
|
138
|
-
recognition.lang = 'en-
|
|
138
|
+
recognition.lang = 'en-IN';
|
|
139
139
|
|
|
140
140
|
self._recognition = recognition;
|
|
141
141
|
self._resultText = '';
|
package/src/server.js
CHANGED
|
@@ -913,34 +913,11 @@ class ClaudeCodeWebServer {
|
|
|
913
913
|
fallthrough: false,
|
|
914
914
|
}));
|
|
915
915
|
|
|
916
|
-
// PWA
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
const r = s * 0.1;
|
|
922
|
-
const svg = `
|
|
923
|
-
<svg width="${s}" height="${s}" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
924
|
-
<rect width="100" height="100" fill="#1a1a1a" rx="${r > 1 ? 10 : 0}"/>
|
|
925
|
-
<path d="M50 18 C28 18 18 32 18 48 C18 58 24 66 32 70 L32 74 C32 78 36 80 40 78 L44 76"
|
|
926
|
-
fill="none" stroke="#ff6b00" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
|
927
|
-
<path d="M50 18 C72 18 82 32 82 48 C82 58 76 66 68 70 L68 74 C68 78 64 80 60 78 L56 76"
|
|
928
|
-
fill="none" stroke="#ff6b00" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
|
929
|
-
<circle cx="38" cy="38" r="3" fill="#ff6b00" opacity="0.5"/>
|
|
930
|
-
<circle cx="62" cy="38" r="3" fill="#ff6b00" opacity="0.5"/>
|
|
931
|
-
<circle cx="50" cy="28" r="2.5" fill="#ff6b00" opacity="0.4"/>
|
|
932
|
-
<text x="50" y="62" text-anchor="middle" dominant-baseline="middle"
|
|
933
|
-
font-family="'JetBrains Mono',monospace" font-size="28" font-weight="700" fill="#ff6b00">
|
|
934
|
-
>_
|
|
935
|
-
</text>
|
|
936
|
-
</svg>
|
|
937
|
-
`;
|
|
938
|
-
const svgBuffer = Buffer.from(svg);
|
|
939
|
-
res.setHeader('Content-Type', 'image/svg+xml');
|
|
940
|
-
res.setHeader('Cache-Control', 'public, max-age=31536000');
|
|
941
|
-
res.send(svgBuffer);
|
|
942
|
-
});
|
|
943
|
-
});
|
|
916
|
+
// PWA icons are static PNG files in src/public/ (icon-<size>.png), served by
|
|
917
|
+
// express.static above (or the SEA asset middleware). They are real PNGs so the
|
|
918
|
+
// served Content-Type matches the manifest's declared `image/png` — required for
|
|
919
|
+
// Chromium installability and iOS apple-touch-icon. Regenerate with
|
|
920
|
+
// `node scripts/generate-pwa-icons.js`.
|
|
944
921
|
|
|
945
922
|
// PWA Screenshot routes - serve pre-built branded screenshots
|
|
946
923
|
this.app.get('/screenshot-wide.png', (req, res) => {
|
|
@@ -3022,7 +2999,11 @@ class ClaudeCodeWebServer {
|
|
|
3022
2999
|
cert = fs.readFileSync(this.certFile);
|
|
3023
3000
|
key = fs.readFileSync(this.keyFile);
|
|
3024
3001
|
} else {
|
|
3025
|
-
// Auto-generate self-signed cert for LAN use
|
|
3002
|
+
// Auto-generate self-signed cert for LAN use.
|
|
3003
|
+
// Note: a self-signed cert is not a trusted "secure context", so on a LAN IP the
|
|
3004
|
+
// browser warns and the PWA can't be installed. For an installable trusted origin
|
|
3005
|
+
// use --tunnel (public cert, no admin) or access the app on http://localhost (a
|
|
3006
|
+
// secure context with no cert needed).
|
|
3026
3007
|
const { ensureCert } = require('./utils/self-signed-cert');
|
|
3027
3008
|
const certInfo = ensureCert();
|
|
3028
3009
|
cert = certInfo.cert;
|
|
@@ -3034,6 +3015,7 @@ class ClaudeCodeWebServer {
|
|
|
3034
3015
|
}
|
|
3035
3016
|
console.log(` Cached at: ${certInfo.certPath}`);
|
|
3036
3017
|
console.log(' Browsers will show a security warning on first visit.');
|
|
3018
|
+
console.log(' For a trusted, installable origin use \x1b[1m--tunnel\x1b[0m.');
|
|
3037
3019
|
}
|
|
3038
3020
|
server = https.createServer({ cert, key }, this.app);
|
|
3039
3021
|
} else {
|