ai-or-die 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.
Files changed (78) hide show
  1. package/.cursor/commands/commit-push.md +18 -0
  2. package/.github/agents/architect.md +26 -0
  3. package/.github/agents/engineer.md +29 -0
  4. package/.github/agents/qa-reviewer.md +31 -0
  5. package/.github/agents/researcher.md +30 -0
  6. package/.github/agents/troubleshooter.md +33 -0
  7. package/.github/copilot-instructions.md +55 -0
  8. package/.github/pull_request_template.md +21 -0
  9. package/.github/workflows/build-binaries.yml +76 -0
  10. package/.github/workflows/ci.yml +70 -0
  11. package/.github/workflows/release-on-main.yml +73 -0
  12. package/.prompts/log.md +9 -0
  13. package/AGENTS.md +84 -0
  14. package/CHANGELOG.md +25 -0
  15. package/CLAUDE.md +130 -0
  16. package/CONTRIBUTING.md +76 -0
  17. package/LICENSE +22 -0
  18. package/README.md +165 -0
  19. package/bin/ai-or-die.js +203 -0
  20. package/docs/.nojekyll +1 -0
  21. package/docs/README.md +37 -0
  22. package/docs/adrs/0000-template.md +35 -0
  23. package/docs/adrs/0001-bridge-base-class.md +53 -0
  24. package/docs/adrs/0002-devtunnels-over-ngrok.md +56 -0
  25. package/docs/adrs/0003-multi-tool-architecture.md +71 -0
  26. package/docs/adrs/0004-cross-platform-support.md +101 -0
  27. package/docs/adrs/0005-single-binary-distribution.md +58 -0
  28. package/docs/agent-instructions/00-philosophy.md +55 -0
  29. package/docs/agent-instructions/01-research-and-web.md +49 -0
  30. package/docs/agent-instructions/02-testing-and-validation.md +63 -0
  31. package/docs/agent-instructions/03-tooling-and-pipelines.md +59 -0
  32. package/docs/architecture/bridge-pattern.md +510 -0
  33. package/docs/architecture/overview.md +216 -0
  34. package/docs/architecture/websocket-protocol.md +609 -0
  35. package/docs/history/README.md +26 -0
  36. package/docs/specs/authentication.md +167 -0
  37. package/docs/specs/bridges.md +210 -0
  38. package/docs/specs/client-app.md +308 -0
  39. package/docs/specs/e2e-testing.md +311 -0
  40. package/docs/specs/server.md +334 -0
  41. package/docs/specs/session-store.md +170 -0
  42. package/docs/specs/usage-analytics.md +342 -0
  43. package/nul +0 -0
  44. package/package.json +54 -0
  45. package/scripts/build-sea.js +187 -0
  46. package/scripts/pty-sea-shim.js +21 -0
  47. package/scripts/publish-both.sh +21 -0
  48. package/scripts/release-pr.sh +73 -0
  49. package/scripts/smoke-test-binary.js +190 -0
  50. package/scripts/validate.ps1 +25 -0
  51. package/scripts/validate.sh +16 -0
  52. package/sea-bootstrap.js +54 -0
  53. package/site/ADVANCED_ANALYTICS.md +174 -0
  54. package/site/index.html +151 -0
  55. package/site/script.js +17 -0
  56. package/site/style.css +60 -0
  57. package/src/base-bridge.js +340 -0
  58. package/src/claude-bridge.js +48 -0
  59. package/src/codex-bridge.js +27 -0
  60. package/src/copilot-bridge.js +29 -0
  61. package/src/gemini-bridge.js +26 -0
  62. package/src/public/app.js +2123 -0
  63. package/src/public/auth.js +244 -0
  64. package/src/public/icon-generator.js +26 -0
  65. package/src/public/icons.js +36 -0
  66. package/src/public/index.html +397 -0
  67. package/src/public/manifest.json +45 -0
  68. package/src/public/plan-detector.js +186 -0
  69. package/src/public/service-worker.js +108 -0
  70. package/src/public/session-manager.js +1124 -0
  71. package/src/public/splits.js +574 -0
  72. package/src/public/style.css +2090 -0
  73. package/src/server.js +1269 -0
  74. package/src/terminal-bridge.js +49 -0
  75. package/src/usage-analytics.js +494 -0
  76. package/src/usage-reader.js +895 -0
  77. package/src/utils/auth.js +123 -0
  78. package/src/utils/session-store.js +181 -0
@@ -0,0 +1,170 @@
1
+ # SessionStore Specification
2
+
3
+ Source: `src/utils/session-store.js`
4
+
5
+ ## Overview
6
+
7
+ `SessionStore` provides atomic, file-based persistence for session data. It serializes the server's in-memory `Map<sessionId, Session>` to JSON on disk and restores it on startup.
8
+
9
+ ---
10
+
11
+ ## Storage Path
12
+
13
+ ```
14
+ ~/.claude-code-web/sessions.json
15
+ ```
16
+
17
+ The directory `~/.claude-code-web/` is created on initialization (`mkdir -p` equivalent) if it does not exist. Future versions will migrate to `~/.ai-or-die/sessions.json`.
18
+
19
+ ---
20
+
21
+ ## Constructor
22
+
23
+ ```js
24
+ new SessionStore()
25
+ ```
26
+
27
+ - Sets `this.storageDir` to `path.join(os.homedir(), '.claude-code-web')`.
28
+ - Sets `this.sessionsFile` to `path.join(this.storageDir, 'sessions.json')`.
29
+ - Calls `initializeStorage()` to ensure the directory exists.
30
+
31
+ ---
32
+
33
+ ## Methods
34
+
35
+ ### `saveSessions(sessions: Map) => Promise<boolean>`
36
+
37
+ Serializes the session Map to disk.
38
+
39
+ **Process:**
40
+
41
+ 1. Ensures `storageDir` exists via `fs.mkdir(recursive: true)`.
42
+ 2. Converts the `Map` to an array of plain objects, applying these transformations:
43
+ - `active` is always set to `false` (processes cannot survive restarts).
44
+ - `connections` is serialized as an empty array (WebSocket references are not persistable).
45
+ - `outputBuffer` is truncated to the **last 100 lines** to limit file size.
46
+ - `sessionStartTime` and `sessionUsage` are preserved if present, otherwise default values are used.
47
+ 3. Wraps the array in an envelope:
48
+ ```json
49
+ {
50
+ "version": "1.0",
51
+ "savedAt": "2026-02-05T10:00:00.000Z",
52
+ "sessions": [ ... ]
53
+ }
54
+ ```
55
+ 4. Writes to a temp file (`sessions.json.tmp`) first.
56
+ 5. Renames temp file to `sessions.json` (atomic write to prevent corruption on crash).
57
+
58
+ **Returns:** `true` on success, `false` on error.
59
+
60
+ ### `loadSessions() => Promise<Map>`
61
+
62
+ Restores sessions from disk into an in-memory `Map`.
63
+
64
+ **Process:**
65
+
66
+ 1. Checks file existence via `fs.access`.
67
+ 2. Reads file contents as UTF-8.
68
+ 3. Handles empty/whitespace-only files by returning an empty `Map`.
69
+ 4. Parses JSON with error recovery:
70
+ - On parse failure, renames the corrupted file to `sessions.json.corrupted.<timestamp>` for forensics, then returns an empty `Map`.
71
+ 5. Validates the parsed structure:
72
+ - Must be a non-null object with a `sessions` array property.
73
+ - Returns an empty `Map` if the structure is invalid.
74
+ 6. Checks the `savedAt` timestamp -- if older than **7 days**, the data is considered stale and an empty `Map` is returned.
75
+ 7. For each session entry:
76
+ - Skips entries without an `id` field.
77
+ - Deserializes `created` and `lastActivity` back to `Date` objects.
78
+ - Forces `active = false`.
79
+ - Converts `connections` back to an empty `Set`.
80
+ - Restores `outputBuffer` (defaults to `[]`).
81
+ - Sets `maxBufferSize` to `1000`.
82
+ - Restores `usageData` if available.
83
+
84
+ **Returns:** `Map<sessionId, Session>`.
85
+
86
+ **Error handling:** If the file does not exist (`ENOENT`), silently returns an empty `Map`. Other errors are logged to stderr.
87
+
88
+ ### `clearOldSessions() => Promise<boolean>`
89
+
90
+ Deletes the `sessions.json` file entirely.
91
+
92
+ ### `getSessionMetadata() => Promise<Object>`
93
+
94
+ Returns metadata about the persistence file without loading full session data.
95
+
96
+ **Success response:**
97
+ ```json
98
+ {
99
+ "exists": true,
100
+ "savedAt": "2026-02-05T10:00:00.000Z",
101
+ "sessionCount": 5,
102
+ "fileSize": 12345,
103
+ "version": "1.0"
104
+ }
105
+ ```
106
+
107
+ **Failure response:**
108
+ ```json
109
+ {
110
+ "exists": false,
111
+ "error": "ENOENT: no such file or directory"
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Serialized Session Schema
118
+
119
+ Each session in the `sessions` array has this shape:
120
+
121
+ ```json
122
+ {
123
+ "id": "uuid",
124
+ "name": "Session 2/5/2026, 10:30:00 AM",
125
+ "created": "2026-02-05T10:30:00.000Z",
126
+ "lastActivity": "2026-02-05T11:00:00.000Z",
127
+ "workingDir": "/home/user/project",
128
+ "active": false,
129
+ "outputBuffer": ["line1", "line2", "...up to 100"],
130
+ "connections": [],
131
+ "lastAccessed": 1738750800000,
132
+ "sessionStartTime": "2026-02-05T10:30:00.000Z",
133
+ "sessionUsage": {
134
+ "requests": 0,
135
+ "inputTokens": 0,
136
+ "outputTokens": 0,
137
+ "cacheTokens": 0,
138
+ "totalCost": 0,
139
+ "models": {}
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Integration with Server
147
+
148
+ The server calls `SessionStore` in these contexts:
149
+
150
+ | Trigger | Method |
151
+ |---------|--------|
152
+ | Server startup | `loadSessions()` -- restores sessions into `claudeSessions` Map |
153
+ | Every 30 seconds | `saveSessions()` via `setInterval` |
154
+ | After session create | `saveSessions()` |
155
+ | After session delete | `saveSessions()` |
156
+ | On `SIGINT` / `SIGTERM` | `saveSessions()` via `handleShutdown()` |
157
+ | On `beforeExit` | `saveSessions()` |
158
+ | `GET /api/sessions/persistence` | `getSessionMetadata()` |
159
+
160
+ ---
161
+
162
+ ## Corruption Recovery
163
+
164
+ The store handles corruption gracefully:
165
+
166
+ 1. **Empty file** -- Detected by checking `!data || !data.trim()`. Returns empty Map.
167
+ 2. **Invalid JSON** -- Caught by `JSON.parse` try/catch. The corrupted file is renamed with a `.corrupted.<timestamp>` suffix for manual inspection, then returns empty Map.
168
+ 3. **Invalid structure** -- If parsed data is not an object or lacks a `sessions` array, returns empty Map.
169
+ 4. **Stale data** -- Sessions older than 7 days are discarded entirely.
170
+ 5. **Invalid session entries** -- Individual entries without an `id` field are silently skipped.
@@ -0,0 +1,342 @@
1
+ # Usage Analytics Specification
2
+
3
+ Two classes work together to track, analyze, and predict API usage: `UsageReader` reads raw JSONL log files from the Claude CLI, and `UsageAnalytics` performs session windowing, burn rate analysis, and depletion predictions.
4
+
5
+ ---
6
+
7
+ ## UsageReader
8
+
9
+ Source: `src/usage-reader.js`
10
+
11
+ ### Overview
12
+
13
+ Reads usage data from Claude CLI's JSONL log files at `~/.claude/projects/`. Each project directory contains per-conversation JSONL files with one JSON object per line, recording every API request and response.
14
+
15
+ ### Constructor
16
+
17
+ ```js
18
+ new UsageReader(sessionDurationHours = 5)
19
+ ```
20
+
21
+ - `claudeProjectsPath`: `~/.claude/projects/`
22
+ - Cache with a 5-second TTL for `getUsageStats()`.
23
+ - `sessionDurationHours`: configurable window size (default 5 hours).
24
+
25
+ ### JSONL File Discovery
26
+
27
+ `findJsonlFiles(onlyRecent = false)`:
28
+ - Recursively scans all project directories under `~/.claude/projects/`.
29
+ - Collects all `.jsonl` files.
30
+ - When `onlyRecent = true`, filters to files modified within the last 24 hours.
31
+
32
+ ### Entry Parsing
33
+
34
+ `readJsonlFile(filePath, cutoffTime)`:
35
+ - Streams the file line by line via `readline`.
36
+ - Parses each line as JSON. Malformed lines are silently ignored.
37
+ - Filters by `cutoffTime` (only entries with `timestamp >= cutoffTime`).
38
+ - Deduplicates entries within each file using a hash of `message_id:request_id`.
39
+ - Extracts only assistant messages with usage data (`entry.type === 'assistant'` or `entry.message.role === 'assistant'`).
40
+
41
+ ### Extracted Entry Fields
42
+
43
+ ```js
44
+ {
45
+ timestamp: string,
46
+ model: 'opus' | 'sonnet' | 'haiku' | 'unknown',
47
+ inputTokens: number,
48
+ outputTokens: number,
49
+ cacheCreationTokens: number,
50
+ cacheReadTokens: number,
51
+ totalCost: number,
52
+ sessionId: string,
53
+ messageId: string,
54
+ requestId: string
55
+ }
56
+ ```
57
+
58
+ ### Model Normalization
59
+
60
+ `normalizeModelName(model)`:
61
+ - Any model string containing `"opus"` -> `"opus"`
62
+ - Any model string containing `"sonnet"` -> `"sonnet"`
63
+ - Any model string containing `"haiku"` -> `"haiku"`
64
+ - Otherwise -> `"unknown"`
65
+
66
+ ### Pricing
67
+
68
+ Costs are calculated per-token when `usage.total_cost` is not available:
69
+
70
+ | Model | Input (per 1M tokens) | Output (per 1M tokens) | Cache Creation | Cache Read |
71
+ |-------|----------------------|------------------------|----------------|------------|
72
+ | Opus | $15.00 | $75.00 | Same as input | 10% of input |
73
+ | Sonnet | $3.00 | $15.00 | Same as input | 10% of input |
74
+ | Haiku | $0.25 | $1.25 | Same as input | 10% of input |
75
+
76
+ When `usage.total_cost` is present:
77
+ - If the value is > 1, it is assumed to be in cents and divided by 100.
78
+ - Otherwise, used as-is (dollars).
79
+
80
+ ### Session Boundary Detection
81
+
82
+ `getDailySessionBoundaries()`:
83
+
84
+ 1. Gets all entries for the current calendar day (midnight to 23:59:59).
85
+ 2. Sorts chronologically.
86
+ 3. Groups into sessions: a new session starts when an entry falls outside the current session's window (start + `sessionDurationHours`).
87
+ 4. Each session start is rounded down to the nearest hour.
88
+ 5. Session end is `startTime + sessionDurationHours` or midnight, whichever is earlier.
89
+ 6. Returns array of `{ sessionNumber, startTime, endTime, sessionId }`.
90
+
91
+ `getCurrentSession()`:
92
+ - Calls `getDailySessionBoundaries()` and returns the session containing `now`, or `null`.
93
+
94
+ ### Key Methods
95
+
96
+ #### `getUsageStats(hoursBack = 24)`
97
+
98
+ Returns aggregated statistics for the given lookback window. Cached for 5 seconds.
99
+
100
+ **Response:**
101
+ ```js
102
+ {
103
+ requests: number,
104
+ totalTokens: number, // inputTokens + outputTokens (no cache tokens)
105
+ inputTokens: number,
106
+ outputTokens: number,
107
+ cacheCreationTokens: number,
108
+ cacheReadTokens: number,
109
+ cacheTokens: number, // cacheCreation + cacheRead
110
+ totalCost: number,
111
+ periodHours: number,
112
+ firstEntry: string,
113
+ lastEntry: string,
114
+ models: { [model]: { requests, inputTokens, outputTokens, cost } },
115
+ hourlyRate: number,
116
+ projectedDaily: number,
117
+ tokensPerHour: number,
118
+ costPerHour: number,
119
+ requestPercentage: number,
120
+ tokenPercentage: number
121
+ }
122
+ ```
123
+
124
+ #### `getCurrentSessionStats()`
125
+
126
+ Returns statistics for the current active session window.
127
+
128
+ **Response:**
129
+ ```js
130
+ {
131
+ requests: number,
132
+ inputTokens: number,
133
+ outputTokens: number,
134
+ cacheCreationTokens: number,
135
+ cacheReadTokens: number,
136
+ cacheTokens: number,
137
+ totalTokens: number,
138
+ totalCost: number,
139
+ models: { ... },
140
+ sessionStartTime: string, // ISO timestamp
141
+ lastUpdate: string,
142
+ sessionId: string,
143
+ sessionNumber: number, // 1-indexed within the day
144
+ isExpired: boolean,
145
+ remainingTokens: null
146
+ }
147
+ ```
148
+
149
+ #### `calculateBurnRate(minutes = 60)`
150
+
151
+ Calculates token consumption rate over the given time window.
152
+
153
+ **Response:**
154
+ ```js
155
+ {
156
+ rate: number, // tokens per minute
157
+ confidence: number, // 0.0 to 1.0, based on data points (min(count/10, 1))
158
+ dataPoints: number
159
+ }
160
+ ```
161
+
162
+ #### `detectOverlappingSessions()`
163
+
164
+ Looks back `2 * sessionDurationHours`, groups entries into sessions by time gaps, and identifies overlapping session windows. Returns the session list (not the overlap pairs, which are stored in `this.overlappingSessions`).
165
+
166
+ ---
167
+
168
+ ## UsageAnalytics
169
+
170
+ Source: `src/usage-analytics.js`
171
+
172
+ Extends `EventEmitter`. Provides real-time analytics, burn rate tracking with trend analysis, and depletion predictions.
173
+
174
+ ### Constructor
175
+
176
+ ```js
177
+ new UsageAnalytics(options)
178
+ ```
179
+
180
+ | Option | Default | Description |
181
+ |--------|---------|-------------|
182
+ | `sessionDurationHours` | `5` | Session window size |
183
+ | `confidenceThreshold` | `0.95` | Minimum confidence for predictions |
184
+ | `burnRateWindow` | `60` | Minutes of data for burn rate calculation |
185
+ | `updateInterval` | `10000` | Milliseconds between analytics updates |
186
+ | `plan` | `'custom'` | Subscription plan type |
187
+ | `customCostLimit` | `76.89` | Dollar limit for custom plans |
188
+
189
+ ### Plan Limits
190
+
191
+ ```js
192
+ {
193
+ 'pro': { tokens: 19000, cost: 18.00, messages: 250, algorithm: 'fixed' },
194
+ 'max5': { tokens: 88000, cost: 35.00, messages: 1000, algorithm: 'fixed' },
195
+ 'max20': { tokens: 220000, cost: 140.00, messages: 2000, algorithm: 'fixed' },
196
+ 'custom': { tokens: null, cost: 76.89, messages: 1019, algorithm: 'p90' }
197
+ }
198
+ ```
199
+
200
+ Legacy aliases `claude-pro`, `claude-max5`, and `claude-max20` are maintained for backwards compatibility with the same values.
201
+
202
+ ### Token Limit Resolution
203
+
204
+ `getTokenLimit()`:
205
+ - For `fixed` algorithm plans: returns `plan.tokens` directly.
206
+ - For `p90` algorithm plans: returns the calculated P90 value, or `188,026` as the default fallback.
207
+
208
+ ### P90 Calculation
209
+
210
+ `calculateP90Limit(historicalSessions)`:
211
+ - Requires at least 10 historical sessions.
212
+ - Sorts all session token counts ascending.
213
+ - Returns the value at the 90th percentile index.
214
+ - Emits `p90-calculated` event.
215
+
216
+ ### Burn Rate Analysis
217
+
218
+ `calculateBurnRate()`:
219
+
220
+ 1. Sorts recent usage data by timestamp.
221
+ 2. Calculates token consumption rates over multiple time windows: 5, 10, 15, 30, and 60 minutes.
222
+ 3. Each window's rate is weighted by data density (`min(dataPoints / 10, 1)`).
223
+ 4. The final burn rate is a weighted average across all windows.
224
+ 5. Only counts input + output tokens (excludes cache tokens).
225
+
226
+ `analyzeTrend()`:
227
+
228
+ Compares the average burn rate from the first half vs. the second half of the history:
229
+ - Change > +15%: `'increasing'`
230
+ - Change < -15%: `'decreasing'`
231
+ - Otherwise: `'stable'`
232
+
233
+ Requires at least 5 data points. Burn rate history is kept for the last hour.
234
+
235
+ ### Depletion Predictions
236
+
237
+ `updatePredictions()`:
238
+
239
+ 1. Gets the current session and its token limit.
240
+ 2. Calculates `remaining = limit - used`.
241
+ 3. If remaining <= 0: depletion is now, confidence = 1.
242
+ 4. Otherwise: `minutesToDepletion = remaining / currentBurnRate`.
243
+ 5. Adjusts for velocity trend:
244
+ - `'increasing'`: pulls depletion 10% sooner.
245
+ - `'decreasing'`: pushes depletion 10% later.
246
+
247
+ ### Confidence Scoring
248
+
249
+ `calculateConfidence()` combines three factors:
250
+
251
+ | Factor | Weight | Score Calculation |
252
+ |--------|--------|-------------------|
253
+ | Data quantity | 0.3 | `min(recentUsage.length / 20, 1)` |
254
+ | Rate consistency | 0.4 | `1 - coefficient_of_variation` (requires 4+ history points) |
255
+ | Trend stability | 0.3 | `1.0` if stable, `0.7` if trending |
256
+
257
+ ### Session Management
258
+
259
+ `startSession(sessionId, startTime)`:
260
+ - Creates a session object with `startTime`, calculated `endTime`, and zero usage counters.
261
+ - Stores in `activeSessions` Map.
262
+ - Updates rolling windows.
263
+ - Emits `session-started`.
264
+
265
+ `updateRollingWindows()`:
266
+ - Clears existing windows.
267
+ - Creates a window for each active session that started within the last `sessionDurationHours`.
268
+ - Each window tracks total tokens, cost, and remaining tokens.
269
+
270
+ `cleanup()`:
271
+ - Removes expired sessions (where `endTime < now`) to `sessionHistory`.
272
+ - Trims `sessionHistory` to the last 24 hours.
273
+
274
+ ### Events
275
+
276
+ | Event | Payload | Trigger |
277
+ |-------|---------|---------|
278
+ | `usage-update` | Usage entry | New data point added |
279
+ | `session-started` | Session object | Session tracking started |
280
+ | `windows-updated` | Window array | Rolling windows recalculated |
281
+ | `burn-rate-updated` | `{ rate, trend, confidence }` | Burn rate recalculated |
282
+ | `prediction-updated` | `{ depletionTime, confidence, remaining, burnRate }` | Prediction refreshed |
283
+ | `p90-calculated` | `{ limit, sampleSize, confidence }` | P90 limit computed |
284
+ | `plan-changed` | Plan type string | User changed plan |
285
+
286
+ ### Comprehensive Analytics Response
287
+
288
+ `getAnalytics()` returns the full state:
289
+
290
+ ```js
291
+ {
292
+ currentSession: {
293
+ id, startTime, endTime, tokens, remaining, percentUsed
294
+ },
295
+ burnRate: {
296
+ current: number, // tokens per minute
297
+ trend: 'stable' | 'increasing' | 'decreasing',
298
+ history: [{ timestamp, rate }] // last 10 data points
299
+ },
300
+ predictions: {
301
+ depletionTime: Date | null,
302
+ confidence: number, // 0.0 to 1.0
303
+ minutesRemaining: number | null
304
+ },
305
+ plan: {
306
+ type: string,
307
+ limits: { tokens, cost, messages, algorithm },
308
+ p90Limit: number | null
309
+ },
310
+ windows: [ ... ],
311
+ activeSessions: [ { id, startTime, endTime, isActive, tokens } ]
312
+ }
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Session Timer (Server Integration)
318
+
319
+ Source: `src/server.js`, `handleGetUsage()`
320
+
321
+ The server combines `UsageReader` and `UsageAnalytics` data into a session timer object sent via the `usage_update` WebSocket message:
322
+
323
+ ```js
324
+ {
325
+ startTime: string, // ISO timestamp of session start
326
+ elapsed: number, // milliseconds since session start
327
+ remaining: number, // milliseconds until session expires
328
+ formatted: "HH:MM:SS", // elapsed time formatted
329
+ remainingFormatted: "HH:MM", // remaining time formatted
330
+ hours: number,
331
+ minutes: number,
332
+ seconds: number,
333
+ remainingMs: number,
334
+ sessionDurationHours: number, // configured window size (e.g. 5)
335
+ sessionNumber: number, // 1-indexed session within the day
336
+ isExpired: boolean,
337
+ burnRate: number, // tokens per minute
338
+ burnRateConfidence: number,
339
+ depletionTime: Date | null,
340
+ depletionConfidence: number
341
+ }
342
+ ```
package/nul ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "ai-or-die",
3
+ "version": "0.1.0",
4
+ "description": "Universal AI coding terminal — Claude, Copilot, Gemini & more in your browser",
5
+ "main": "src/server.js",
6
+ "bin": {
7
+ "ai-or-die": "./bin/ai-or-die.js",
8
+ "aiordie": "./bin/ai-or-die.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/ai-or-die.js",
12
+ "dev": "node bin/ai-or-die.js --dev",
13
+ "test": "mocha --exit test/*.test.js",
14
+ "build:bundle": "node scripts/build-sea.js bundle",
15
+ "build:sea": "node scripts/build-sea.js",
16
+ "release:pr": "bash scripts/release-pr.sh"
17
+ },
18
+ "keywords": [
19
+ "ai-or-die",
20
+ "aiordie",
21
+ "ai",
22
+ "terminal",
23
+ "claude",
24
+ "copilot",
25
+ "gemini",
26
+ "web",
27
+ "browser",
28
+ "remote"
29
+ ],
30
+ "author": "Animesh Kundu",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "commander": "^12.1.0",
34
+ "cors": "^2.8.5",
35
+ "express": "^4.19.2",
36
+ "@lydell/node-pty": "1.2.0-beta.10",
37
+ "open": "^10.1.0",
38
+ "uuid": "^10.0.0",
39
+ "ws": "^8.18.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=22.0.0"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/animeshkundu/ai-or-die.git"
47
+ },
48
+ "homepage": "https://github.com/animeshkundu/ai-or-die#readme",
49
+ "devDependencies": {
50
+ "esbuild": "^0.24.0",
51
+ "mocha": "^11.7.1",
52
+ "postject": "^1.0.0-alpha.6"
53
+ }
54
+ }