<label>Empty duration in minutes<inputtype="number"min="1"max="1440"requiredbind:value={settings.emptyMinutes}/></label>
<label>Action cooldown in minutes<inputtype="number"min="1"max="1440"requiredbind:value={settings.cooldownMinutes}/></label>
</div>
{#ifsettings.mapSlug==='*'}<p>Targets apply separately to every map, including all variants. Each map needs its own keys. Actions share one cooldown and run one at a time across the game.</p>{/if}
<p>Saved lobby keys must cover the maximum for each selected map before you can save a new policy, including Dry run.</p>
{#ifsettings.mapSlug==='*'}<p>Targets apply separately to every map, including all variants. All maps share this game’s key pool. Actions share one cooldown and run one at a time across the game.</p>{/if}
<p>Each new lobby needs an available key from the game pool. Creation waits when the pool is exhausted; keys return after confirmed removal.</p>
<p>Target: {preview.decision.desired} running lobbies{preview.policy.mapSlug==='*'?' across all maps':''}. {preview.snapshot.keyedNames.length}saved lobby keys. {preview.snapshot.members.length} existing or pending lobbies.</p>
<p>Target: {preview.decision.desired} running lobbies{preview.policy.mapSlug==='*'?' across all maps':''}. {preview.snapshot.keyPool.available}of {preview.snapshot.keyPool.total} game keys available. {preview.snapshot.members.length} existing or pending lobbies.</p>
{#if preview.policy.mode === 'create-only' || preview.policy.mode === 'create-destroy'}<p>Saving gives this policy exclusive control of the selected maps' lobby counts. Pause it before launching a competing collection or manual lifecycle action.</p>{/if}
<div class="heading"><h2 id={`title-${view.policy.id}`}>{view.policy.mapSlug === '*' ? 'Every map' : zombieMapBySlug(view.policy.game, view.policy.mapSlug)?.friendly ?? view.policy.mapSlug} · {view.policy.game.toUpperCase()}</h2><span>{modeLabel(view.policy.mode)}</span>{#if canOperate}<Button disabled={busy} onclick={() => edit(view.policy)}>Edit or pause</Button><Button variant="danger" disabled={busy || view.unsettled.length > 0} title={view.unsettled.length ? 'Pause the policy and settle outstanding work before deleting it.' : undefined} onclick={() => remove(view.policy)}>Delete policy</Button>{/if}</div>
<p class="counts">{view.decision.occupied} occupied · {view.decision.empty} empty and ready · {view.decision.pending} pending · {view.decision.unknown} unknown or stopped</p>
{#if view.unsettled.length}<div class="banner"><strong>Work is still settling.</strong><p>Pause this policy and settle its outstanding work before deleting it.</p>{#each view.unsettled as operation}<p><a href={`/provisioning/${operation.id}`}>{operation.action} · {operation.state}</a> — {operation.stage}</p>{/each}{#if view.ownsMap}<p>This policy retains ownership of its scope until its outstanding work settles.</p>{/if}</div>{/if}
{#if detail.canResumeRebuild}<Button variant="primary" loading={view.busy === 'resume'} disabled={!!view.busy} onclick={() => controller.act('resume', 'Rebuild cleanup requested. Fresh creation waits for local cleanup to finish.')}>Resume rebuild cleanup</Button>{/if}
{#if detail.canResumeCleanup}<Button variant="primary" loading={view.busy === 'resume'} disabled={!!view.busy} onclick={() => controller.act('resume', 'Local cleanup requested. Remote container deletion will not be repeated.')}>Resume cleanup</Button>{/if}
{#if detail.canCancel}<div><Button disabled={!!view.busy} loading={view.busy === 'cancel'} onclick={() => controller.act('cancel', 'Operation cancelled. Already-submitted commands may finish; no further steps will be started.')}>Cancel operation</Button><p class="action-help">Stops remaining work and health checks. Changes already made are not undone.</p></div>{/if}
{#if detail.canCancelCreation}<div><Button variant="danger" disabled={!!view.busy} loading={view.busy === 'cancel-creation'} onclick={() => controller.act('cancel-creation', 'Creation cancelled. Follow cleanup below until removal is confirmed.')}>Cancel creation and remove server</Button><p class="action-help">Stops this launch and deletes its container and instance files, even if player status is unknown or players have joined. Shared game files and the saved server key are kept.</p></div>{/if}
{#if detail.canCancelCreation}<div><Button variant="danger" disabled={!!view.busy} loading={view.busy === 'cancel-creation'} onclick={() => controller.act('cancel-creation', 'Creation cancelled. Follow cleanup below until removal is confirmed.')}>Cancel creation and remove server</Button><p class="action-help">Stops this launch and deletes its container and instance files, even if player status is unknown or players have joined. Shared game files are kept. The key returns to its game pool after removal is confirmed.</p></div>{/if}
plutoKey='';label='';open=false;notice='Key saved to the game pool.';awaitload();
}catch(e){error=(easError).message;}
finally{busy=false;}
}
asyncfunctionremove(id: string){
if(!awaitconfirmAction({title:'Remove saved key?',message:`Remove the key for ${serverTitle(id)} (${id})? Running servers are unaffected.`,confirmLabel:'Remove key',danger:true}))return;
asyncfunctionremove(key: SavedKey){
if(key.assigned_servers.length)return;
if(!awaitconfirmAction({title:'Remove saved key?',message:`Remove ${key.label||'this key'} from the ${key.game.toUpperCase()} pool?`,confirmLabel:'Remove key',danger: true}))return;
<pclass="key-help">Saved keys are reused when a server is created or rebuilt. Electric Cherry does not check whether Plutonium accepts them. Updating or removing a saved key does not change an existing server.</p>
<pclass="key-help">A key stays assigned while its server exists, including when stopped, and becomes available after removal is confirmed. Rebuilds keep the same key. Use the optional label to record its Plutonium hostname; saving a label does not change that hostname.</p>
<td>{key.deployed?'Server exists':'No server deployed'}</td>
<td>{savedAt(key.updated_at)}</td>
<td><divclass="actions">
<ButtonariaLabel={`Update key for ${serverTitle(key.server_id)}`}disabled={busy}onclick={()=>{serverId=key.server_id;plutoKey='';editing=true;error='';notice='';open=true;}}>Update</Button>
<ButtonariaLabel={`Remove key for ${serverTitle(key.server_id)}`}disabled={busy}onclick={()=>remove(key.server_id)}>Remove</Button>
{#if!editing}<labelclass="raw-toggle"><inputtype="checkbox"bind:checked={rawMode}disabled={busy}onchange={()=>{if(!rawMode)serverId=`${game}_zombie_${mapSlug}_${lobby}`; }}/> Enter a raw server ID</label>{/if}
{#ifediting||rawMode}<Fieldlabel="Server ID"hint="Use a T5 or T6 map slug and lobby number from 1 to 20, e.g. t5_zombie_theater_2 or t6_zombie_tomb_1."error={!validId&&serverId?'Choose a recognized map and lobby number 1–20.':null}>
{#if!editing&&keys.some(key=>key.server_id===chosenId)}<prole="status">A key is already saved for this ID. Saving replaces it for the next deployment; the running server keeps its current key.</p>{/if}
<Fieldlabel="Plutonium server key"hint="Used when this server ID is created or rebuilt. Existing servers keep their current key.">
{#if!validId||!plutoKey.trim()}<prole="status">Choose a valid server ID and enter its Plutonium key to save.</p>{/if}
<label>Game<selectbind:value={game}disabled={busy}><optionvalue="t5">Black Ops I (T5)</option><optionvalue="t6">Black Ops II (T6)</option></select></label>
<Fieldlabel="Label (optional)"hint="Record the hostname you chose in Plutonium, or another name that helps identify this key.">
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.