@aion0/forge 0.1.4 → 0.1.6

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 CHANGED
@@ -166,7 +166,8 @@ All config lives in `~/.forge/`:
166
166
 
167
167
  ```
168
168
  ~/.forge/
169
- settings.yaml # Main configuration
169
+ .env.local # Environment variables (AUTH_SECRET, API keys, etc.)
170
+ settings.yaml # Main configuration
170
171
  password.json # Daily auto-generated login password
171
172
  data.db # SQLite database (tasks, sessions)
172
173
  terminal-state.json # Terminal tab layout
@@ -174,6 +175,18 @@ All config lives in `~/.forge/`:
174
175
  bin/ # Auto-downloaded binaries (cloudflared)
175
176
  ```
176
177
 
178
+ ### .env.local (optional)
179
+
180
+ ```env
181
+ # Fixed auth secret (optional — auto-generated if not set)
182
+ AUTH_SECRET=<random-string>
183
+
184
+ # Optional: AI provider API keys for multi-model chat
185
+ # ANTHROPIC_API_KEY=sk-ant-...
186
+ # OPENAI_API_KEY=sk-...
187
+ # GOOGLE_GENERATIVE_AI_API_KEY=AI...
188
+ ```
189
+
177
190
  ### settings.yaml
178
191
 
179
192
  ```yaml
@@ -28,6 +28,20 @@ const isStop = process.argv.includes('--stop');
28
28
  process.chdir(ROOT);
29
29
  mkdirSync(DATA_DIR, { recursive: true });
30
30
 
31
+ // ── Load ~/.forge/.env.local ──
32
+ const envFile = join(DATA_DIR, '.env.local');
33
+ if (existsSync(envFile)) {
34
+ for (const line of readFileSync(envFile, 'utf-8').split('\n')) {
35
+ const trimmed = line.trim();
36
+ if (!trimmed || trimmed.startsWith('#')) continue;
37
+ const eq = trimmed.indexOf('=');
38
+ if (eq === -1) continue;
39
+ const key = trimmed.slice(0, eq).trim();
40
+ const val = trimmed.slice(eq + 1).trim();
41
+ if (!process.env[key]) process.env[key] = val;
42
+ }
43
+ }
44
+
31
45
  // ── Stop ──
32
46
  if (isStop) {
33
47
  try {
@@ -5,10 +5,27 @@
5
5
  export async function register() {
6
6
  // Only run on server, not Edge
7
7
  if (process.env.NEXT_RUNTIME === 'nodejs') {
8
+ // Load ~/.forge/.env.local if it exists (works for both pnpm dev and forge-server)
9
+ const { existsSync, readFileSync } = await import('node:fs');
10
+ const { join } = await import('node:path');
11
+ const { homedir } = await import('node:os');
12
+ const envFile = join(homedir(), '.forge', '.env.local');
13
+ if (existsSync(envFile)) {
14
+ for (const line of readFileSync(envFile, 'utf-8').split('\n')) {
15
+ const trimmed = line.trim();
16
+ if (!trimmed || trimmed.startsWith('#')) continue;
17
+ const eq = trimmed.indexOf('=');
18
+ if (eq === -1) continue;
19
+ const key = trimmed.slice(0, eq).trim();
20
+ const val = trimmed.slice(eq + 1).trim();
21
+ if (!process.env[key]) process.env[key] = val;
22
+ }
23
+ }
24
+
8
25
  const { getPassword } = await import('./lib/password');
9
26
  const password = getPassword();
10
27
  process.env.MW_PASSWORD = password;
11
28
  console.log(`[init] Login password: ${password}`);
12
- console.log('[init] Forgot password? Run: mw password');
29
+ console.log('[init] Forgot password? Run: forge password');
13
30
  }
14
31
  }
@@ -352,18 +352,7 @@ wss.on('connection', (ws: WebSocket) => {
352
352
  if (sessionName) trackDetach(ws, sessionName);
353
353
  createdAt.delete(ws);
354
354
 
355
- // Grace period cleanup: if no client reconnects within 10s, kill the session (unless renamed)
356
- if (disconnectedSession) {
357
- setTimeout(() => {
358
- const clients = sessionClients.get(disconnectedSession)?.size ?? 0;
359
- if (clients === 0 && tmuxSessionExists(disconnectedSession)) {
360
- const renamed = getRenamedSessions();
361
- if (!renamed.has(disconnectedSession)) {
362
- console.log(`[terminal] Auto-killing orphaned session "${disconnectedSession}" (no clients after 10s, not renamed)`);
363
- killTmuxSession(disconnectedSession);
364
- }
365
- }
366
- }, 10_000);
367
- }
355
+ // Orphan cleanup is handled by the periodic cleanupOrphanedSessions() (every 30s)
356
+ // which checks sessionClients and getRenamedSessions() from terminal-state.json
368
357
  });
369
358
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {