@aion0/forge 0.4.1 → 0.4.3
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 +1 -1
- package/RELEASE_NOTES.md +4 -4
- package/app/icon.png +0 -0
- package/app/login/page.tsx +1 -0
- package/bin/forge-server.mjs +0 -17
- package/components/Dashboard.tsx +1 -0
- package/forge-logo.png +0 -0
- package/lib/help-docs/10-troubleshooting.md +7 -0
- package/lib/logger.ts +11 -6
- package/lib/terminal-standalone.ts +30 -4
- package/middleware.ts +2 -1
- package/next-env.d.ts +1 -1
- package/package.json +1 -1
- package/app/icon.svg +0 -26
package/README.md
CHANGED
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Forge v0.4.
|
|
1
|
+
# Forge v0.4.3
|
|
2
2
|
|
|
3
3
|
Released: 2026-03-21
|
|
4
4
|
|
|
5
|
-
## Changes since v0.4.
|
|
5
|
+
## Changes since v0.4.2
|
|
6
6
|
|
|
7
7
|
### Bug Fixes
|
|
8
|
-
- fix:
|
|
8
|
+
- fix: restore Forge name in header + allow icon.png on login page
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.
|
|
11
|
+
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.2...v0.4.3
|
package/app/icon.png
ADDED
|
Binary file
|
package/app/login/page.tsx
CHANGED
|
@@ -38,6 +38,7 @@ export default function LoginPage() {
|
|
|
38
38
|
<div className="min-h-screen flex items-center justify-center">
|
|
39
39
|
<div className="w-80 space-y-6">
|
|
40
40
|
<div className="text-center">
|
|
41
|
+
<img src="/icon.png" alt="Forge" width={48} height={48} className="rounded mx-auto mb-2" />
|
|
41
42
|
<h1 className="text-2xl font-bold text-[var(--text-primary)]">Forge</h1>
|
|
42
43
|
<p className="text-sm text-[var(--text-secondary)] mt-1">
|
|
43
44
|
{isRemote ? 'Remote Access' : 'Local Access'}
|
package/bin/forge-server.mjs
CHANGED
|
@@ -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');
|
package/components/Dashboard.tsx
CHANGED
|
@@ -166,6 +166,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
166
166
|
{/* Top bar */}
|
|
167
167
|
<header className="h-12 border-b-2 border-[var(--border)] flex items-center justify-between px-4 shrink-0 bg-[var(--bg-secondary)]">
|
|
168
168
|
<div className="flex items-center gap-4">
|
|
169
|
+
<img src="/icon.png" alt="Forge" width={28} height={28} className="rounded" />
|
|
169
170
|
<span className="text-sm font-bold text-[var(--accent)]">Forge</span>
|
|
170
171
|
{versionInfo && (
|
|
171
172
|
<span className="flex items-center gap-1.5">
|
package/forge-logo.png
ADDED
|
Binary file
|
|
@@ -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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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/middleware.ts
CHANGED
|
@@ -9,7 +9,8 @@ export function middleware(req: NextRequest) {
|
|
|
9
9
|
pathname.startsWith('/api/auth') ||
|
|
10
10
|
pathname.startsWith('/api/telegram') ||
|
|
11
11
|
pathname.startsWith('/_next') ||
|
|
12
|
-
pathname === '/favicon.ico'
|
|
12
|
+
pathname === '/favicon.ico' ||
|
|
13
|
+
pathname === '/icon.png'
|
|
13
14
|
) {
|
|
14
15
|
return NextResponse.next();
|
|
15
16
|
}
|
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
package/app/icon.svg
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
-
<defs>
|
|
3
|
-
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
-
<stop offset="0%" stop-color="#60a5fa"/>
|
|
5
|
-
<stop offset="100%" stop-color="#3b82f6"/>
|
|
6
|
-
</linearGradient>
|
|
7
|
-
</defs>
|
|
8
|
-
<!-- Background -->
|
|
9
|
-
<rect width="32" height="32" rx="7" fill="#1a1a2e"/>
|
|
10
|
-
<!-- Anvil base -->
|
|
11
|
-
<rect x="6" y="22" width="20" height="3" rx="1" fill="url(#g)"/>
|
|
12
|
-
<!-- Anvil middle -->
|
|
13
|
-
<rect x="9" y="18" width="14" height="4" fill="url(#g)" opacity="0.9"/>
|
|
14
|
-
<!-- Anvil top face -->
|
|
15
|
-
<polygon points="9,15 23,15 23,18 9,18" fill="url(#g)" opacity="0.85"/>
|
|
16
|
-
<!-- Anvil horn (left) -->
|
|
17
|
-
<polygon points="9,15 4,17 4,18 9,18" fill="#60a5fa" opacity="0.8"/>
|
|
18
|
-
<!-- Hammer handle -->
|
|
19
|
-
<rect x="19" y="4" width="2.5" height="10" rx="1" fill="#f59e0b" transform="rotate(-35 20.25 9)"/>
|
|
20
|
-
<!-- Hammer head -->
|
|
21
|
-
<rect x="17" y="2.5" width="7" height="3.5" rx="1" fill="#f59e0b" transform="rotate(-35 20.5 4.25)"/>
|
|
22
|
-
<!-- Sparks -->
|
|
23
|
-
<circle cx="14" cy="12.5" r="1.2" fill="#fbbf24" opacity="0.9"/>
|
|
24
|
-
<circle cx="11" cy="10.5" r="0.8" fill="#fbbf24" opacity="0.7"/>
|
|
25
|
-
<circle cx="17" cy="10" r="0.7" fill="#fbbf24" opacity="0.6"/>
|
|
26
|
-
</svg>
|