@aion0/forge 0.2.18 → 0.2.19
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/app/api/upgrade/route.ts +19 -18
- package/components/Dashboard.tsx +3 -3
- package/components/SessionView.tsx +30 -41
- package/package.json +1 -1
package/app/api/upgrade/route.ts
CHANGED
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { lstatSync } from 'node:fs';
|
|
4
3
|
import { execSync } from 'node:child_process';
|
|
5
4
|
|
|
6
5
|
export async function POST() {
|
|
7
6
|
try {
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// Run upgrade with cache bypass
|
|
8
|
+
const output = execSync(
|
|
9
|
+
'cd /tmp && npm install -g @aion0/forge@latest --prefer-online 2>&1',
|
|
10
|
+
{ encoding: 'utf-8', timeout: 120000 }
|
|
11
|
+
);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
return NextResponse.json({
|
|
14
|
-
ok: false,
|
|
15
|
-
error: 'Local dev install (npm link). Run: git pull && pnpm install && pnpm build',
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Upgrade from npm
|
|
20
|
-
execSync('cd /tmp && npm install -g @aion0/forge', { timeout: 120000 });
|
|
21
|
-
|
|
22
|
-
// Install devDependencies for build (npm -g doesn't install them)
|
|
13
|
+
// Verify the installed version
|
|
23
14
|
const pkgRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
24
15
|
const forgeRoot = join(pkgRoot, '@aion0', 'forge');
|
|
16
|
+
|
|
17
|
+
// Install devDependencies for build (npm -g doesn't install them)
|
|
18
|
+
try {
|
|
19
|
+
execSync('npm install --include=dev 2>&1', { cwd: forgeRoot, timeout: 120000 });
|
|
20
|
+
} catch {}
|
|
21
|
+
|
|
22
|
+
// Read installed version
|
|
23
|
+
let installedVersion = '';
|
|
25
24
|
try {
|
|
26
|
-
|
|
25
|
+
const pkg = JSON.parse(require('fs').readFileSync(join(forgeRoot, 'package.json'), 'utf-8'));
|
|
26
|
+
installedVersion = pkg.version;
|
|
27
27
|
} catch {}
|
|
28
28
|
|
|
29
29
|
return NextResponse.json({
|
|
30
30
|
ok: true,
|
|
31
|
-
message:
|
|
31
|
+
message: `Upgraded to v${installedVersion}. Restart server to apply.`,
|
|
32
32
|
});
|
|
33
33
|
} catch (e) {
|
|
34
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
34
35
|
return NextResponse.json({
|
|
35
36
|
ok: false,
|
|
36
|
-
error: `Upgrade failed: ${
|
|
37
|
+
error: `Upgrade failed: ${msg.slice(0, 200)}`,
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
}
|
package/components/Dashboard.tsx
CHANGED
|
@@ -122,7 +122,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
122
122
|
setUpgrading(false);
|
|
123
123
|
}}
|
|
124
124
|
className="text-[9px] px-1.5 py-0.5 bg-[var(--accent)] text-white rounded hover:opacity-90 disabled:opacity-50"
|
|
125
|
-
title={`Update to v${versionInfo.latest}`}
|
|
125
|
+
title={`Update to v${versionInfo.latest}\nOr run: forge upgrade`}
|
|
126
126
|
>
|
|
127
127
|
{upgrading ? 'Upgrading...' : `Update v${versionInfo.latest}`}
|
|
128
128
|
</button>
|
|
@@ -134,7 +134,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
134
134
|
const data = await res.json();
|
|
135
135
|
setVersionInfo(data);
|
|
136
136
|
}}
|
|
137
|
-
className="text-[9px] px-1
|
|
137
|
+
className="text-[9px] px-1 py-0.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
|
|
138
138
|
title="Check for updates"
|
|
139
139
|
>
|
|
140
140
|
↻
|
|
@@ -241,7 +241,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
241
241
|
onClick={() => setViewMode('sessions')}
|
|
242
242
|
className={`text-xs ${viewMode === 'sessions' ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'}`}
|
|
243
243
|
>
|
|
244
|
-
|
|
244
|
+
Monitor
|
|
245
245
|
</button>
|
|
246
246
|
<button
|
|
247
247
|
onClick={() => setShowSettings(true)}
|
|
@@ -64,7 +64,6 @@ export default function SessionView({
|
|
|
64
64
|
const [batchMode, setBatchMode] = useState(false);
|
|
65
65
|
const [selectedIds, setSelectedIds] = useState<Map<string, Set<string>>>(new Map());
|
|
66
66
|
const [monitor, setMonitor] = useState<MonitorData | null>(null);
|
|
67
|
-
const [monitorOpen, setMonitorOpen] = useState(true);
|
|
68
67
|
const bottomRef = useRef<HTMLDivElement>(null);
|
|
69
68
|
|
|
70
69
|
// Load cached sessions tree
|
|
@@ -348,51 +347,41 @@ export default function SessionView({
|
|
|
348
347
|
</div>
|
|
349
348
|
)}
|
|
350
349
|
|
|
351
|
-
{/* Monitor */}
|
|
350
|
+
{/* Monitor — always visible */}
|
|
352
351
|
{monitor && (
|
|
353
|
-
<div className="border-b border-[var(--border)]">
|
|
354
|
-
<
|
|
355
|
-
|
|
356
|
-
className="w-full flex items-center gap-1.5 px-2 py-1.5 hover:bg-[var(--bg-tertiary)] transition-colors"
|
|
357
|
-
>
|
|
358
|
-
<span className="text-[10px] text-[var(--text-secondary)]">{monitorOpen ? '▼' : '▶'}</span>
|
|
359
|
-
<span className="text-[9px] font-semibold text-[var(--text-secondary)] uppercase">Monitor</span>
|
|
352
|
+
<div className="border-b border-[var(--border)] px-2 py-2 space-y-1.5">
|
|
353
|
+
<div className="flex items-center justify-between">
|
|
354
|
+
<span className="text-[9px] font-semibold text-[var(--text-secondary)] uppercase">Processes</span>
|
|
360
355
|
{monitor.uptime && (
|
|
361
|
-
<span className="text-[8px] text-[var(--text-secondary)]
|
|
356
|
+
<span className="text-[8px] text-[var(--text-secondary)]">up {monitor.uptime}</span>
|
|
362
357
|
)}
|
|
363
|
-
</
|
|
364
|
-
{
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
].
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
{monitor.processes.tunnel.running && monitor.processes.tunnel.url && (
|
|
380
|
-
<div className="text-[9px] text-[var(--accent)] truncate pl-4">{monitor.processes.tunnel.url}</div>
|
|
381
|
-
)}
|
|
358
|
+
</div>
|
|
359
|
+
{[
|
|
360
|
+
{ label: 'Next.js', ...monitor.processes.nextjs },
|
|
361
|
+
{ label: 'Terminal', ...monitor.processes.terminal },
|
|
362
|
+
{ label: 'Telegram', ...monitor.processes.telegram },
|
|
363
|
+
{ label: 'Tunnel', ...monitor.processes.tunnel },
|
|
364
|
+
].map(p => (
|
|
365
|
+
<div key={p.label} className="flex items-center gap-1.5 text-[10px]">
|
|
366
|
+
<span className={p.running ? 'text-green-400' : 'text-gray-500'}>●</span>
|
|
367
|
+
<span className="text-[var(--text-primary)]">{p.label}</span>
|
|
368
|
+
<span className="text-[var(--text-secondary)] font-mono ml-auto">{p.running ? `pid:${p.pid}` : 'stopped'}</span>
|
|
369
|
+
</div>
|
|
370
|
+
))}
|
|
371
|
+
{monitor.processes.tunnel.running && monitor.processes.tunnel.url && (
|
|
372
|
+
<div className="text-[9px] text-[var(--accent)] truncate pl-4">{monitor.processes.tunnel.url}</div>
|
|
373
|
+
)}
|
|
382
374
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
<span className="text-[8px] text-[var(--text-secondary)]">{s.attached ? 'attached' : 'detached'}</span>
|
|
392
|
-
</div>
|
|
393
|
-
))}
|
|
375
|
+
{monitor.sessions.length > 0 && (
|
|
376
|
+
<div className="pt-1">
|
|
377
|
+
<span className="text-[9px] font-semibold text-[var(--text-secondary)] uppercase">Tmux ({monitor.sessions.length})</span>
|
|
378
|
+
{monitor.sessions.map(s => (
|
|
379
|
+
<div key={s.name} className="flex items-center gap-1.5 text-[10px] mt-0.5">
|
|
380
|
+
<span className={s.attached ? 'text-green-400' : 'text-yellow-500'}>●</span>
|
|
381
|
+
<span className="font-mono text-[var(--text-primary)] truncate flex-1">{s.name}</span>
|
|
382
|
+
<span className="text-[8px] text-[var(--text-secondary)]">{s.attached ? 'attached' : 'detached'}</span>
|
|
394
383
|
</div>
|
|
395
|
-
)}
|
|
384
|
+
))}
|
|
396
385
|
</div>
|
|
397
386
|
)}
|
|
398
387
|
</div>
|
package/package.json
CHANGED