@aion0/forge 0.4.1 → 0.4.2

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/RELEASE_NOTES.md CHANGED
@@ -1,11 +1,15 @@
1
- # Forge v0.4.1
1
+ # Forge v0.4.2
2
2
 
3
3
  Released: 2026-03-21
4
4
 
5
- ## Changes since v0.4.0
5
+ ## Changes since v0.4.1
6
6
 
7
7
  ### Bug Fixes
8
8
  - fix: prevent double console.log wrapping in production mode
9
+ - fix: auto-recover from PTY exhaustion + add npm ENOTEMPTY troubleshooting
9
10
 
11
+ ### Documentation
12
+ - fix: auto-recover from PTY exhaustion + add npm ENOTEMPTY troubleshooting
10
13
 
11
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.0...v0.4.1
14
+
15
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.1...v0.4.2
@@ -72,23 +72,6 @@ const LOG_FILE = join(DATA_DIR, 'forge.log');
72
72
 
73
73
  process.chdir(ROOT);
74
74
 
75
- // ── Init logger (timestamps + file output) ──
76
- try {
77
- const { initLogger } = await import('../lib/logger.ts');
78
- initLogger();
79
- } catch {
80
- // logger.ts is TypeScript, may not load directly in .mjs — fallback inline
81
- const _key = Symbol.for('forge-logger-init');
82
- if (!globalThis[_key]) {
83
- globalThis[_key] = true;
84
- const _origLog = console.log, _origErr = console.error, _origWarn = console.warn;
85
- const _ts = () => new Date().toISOString().replace('T', ' ').slice(0, 19);
86
- console.log = (...a) => _origLog(`[${_ts()}]`, ...a);
87
- console.error = (...a) => _origErr(`[${_ts()}]`, ...a);
88
- console.warn = (...a) => _origWarn(`[${_ts()}]`, ...a);
89
- }
90
- }
91
-
92
75
  // ── Migrate old layout (~/.forge/*) to new (~/.forge/data/*) ──
93
76
  if (!getArg('--dir')) {
94
77
  const oldSettings = join(homedir(), '.forge', 'settings.yaml');
@@ -61,6 +61,13 @@ rm -rf .next
61
61
  pnpm build # or forge server rebuild
62
62
  ```
63
63
 
64
+ ### npm install fails with ENOTEMPTY
65
+ Previous install was interrupted. Clean up and retry:
66
+ ```bash
67
+ rm -rf $(npm root -g)/@aion0/forge $(npm root -g)/@aion0/.forge-*
68
+ npm install -g @aion0/forge
69
+ ```
70
+
64
71
  ## Logs
65
72
 
66
73
  - Background server: `~/.forge/data/forge.log`
package/lib/logger.ts CHANGED
@@ -15,13 +15,18 @@ export function initLogger() {
15
15
  (globalThis as any)[loggerKey] = true;
16
16
 
17
17
  // Determine log file path
18
+ // In production mode (FORGE_EXTERNAL_SERVICES=1), stdout is already redirected to forge.log
19
+ // by forge-server.mjs, so we only need to write to file in dev mode
20
+ const isProduction = process.env.FORGE_EXTERNAL_SERVICES === '1';
18
21
  let logFile: string | null = null;
19
- try {
20
- const { getDataDir } = require('./dirs');
21
- const dataDir = getDataDir();
22
- if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
23
- logFile = join(dataDir, 'forge.log');
24
- } catch {}
22
+ if (!isProduction) {
23
+ try {
24
+ const { getDataDir } = require('./dirs');
25
+ const dataDir = getDataDir();
26
+ if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
27
+ logFile = join(dataDir, 'forge.log');
28
+ } catch {}
29
+ }
25
30
 
26
31
  const origLog = console.log;
27
32
  const origError = console.error;
@@ -149,10 +149,36 @@ function createTmuxSession(cols: number, rows: number): string {
149
149
 
150
150
  const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
151
151
  const name = `${SESSION_PREFIX}${id}`;
152
- execSync(`${TMUX} new-session -d -s ${name} -x ${cols} -y ${rows}`, {
153
- cwd: getDefaultCwd(),
154
- env: { ...process.env, TERM: 'xterm-256color' },
155
- });
152
+ try {
153
+ execSync(`${TMUX} new-session -d -s ${name} -x ${cols} -y ${rows}`, {
154
+ cwd: getDefaultCwd(),
155
+ env: { ...process.env, TERM: 'xterm-256color' },
156
+ });
157
+ } catch (e: any) {
158
+ const msg = e.stderr?.toString() || e.message || '';
159
+ if (msg.includes('posix_spawn') || msg.includes('fork failed') || msg.includes('No such file')) {
160
+ // PTY exhausted — aggressive cleanup: kill ALL idle sessions
161
+ console.error(`[terminal] PTY exhausted, cleaning up all idle sessions...`);
162
+ const all = listTmuxSessions();
163
+ for (const s of all) {
164
+ if (!s.attached) {
165
+ killTmuxSession(s.name);
166
+ console.log(`[terminal] Killed idle session: ${s.name}`);
167
+ }
168
+ }
169
+ // Retry once
170
+ try {
171
+ execSync(`${TMUX} new-session -d -s ${name} -x ${cols} -y ${rows}`, {
172
+ cwd: getDefaultCwd(),
173
+ env: { ...process.env, TERM: 'xterm-256color' },
174
+ });
175
+ } catch {
176
+ throw new Error('Failed to create terminal session. PTY devices exhausted. Run: sudo sysctl kern.tty.ptmx_max=2048');
177
+ }
178
+ } else {
179
+ throw e;
180
+ }
181
+ }
156
182
  // Enable mouse scrolling and set large scrollback buffer
157
183
  try {
158
184
  execSync(`${TMUX} set-option -t ${name} mouse on 2>/dev/null`);
package/next-env.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="next" />
2
2
  /// <reference types="next/image-types/global" />
3
- import "./.next/types/routes.d.ts";
3
+ import "./.next/dev/types/routes.d.ts";
4
4
 
5
5
  // NOTE: This file should not be edited
6
6
  // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {