fix(beehive): separate registration from runtime setup

Signed-off-by: BetterCallSol <886fabdb38ceb1aede220bc237b7bb34736490f12a7d79e235e5ecfd63a36514@buzz.block.builderlab.xyz>
This commit is contained in:
BetterCallSol
2026-09-16 17:10:54 -04:00
parent 24a9e43cad
commit a64d831e9b
3 changed files with 16 additions and 9 deletions

View File

@@ -373,14 +373,15 @@ A Stop result is not a recent host report that confirms the agent is stopped.` }
const report = this.agentReport(key);
if (settings.revision !== pending.settingsRevision || !report || !this.fresh(report) || JSON.stringify([report.host,report.agent]) !== pending.target || report.revision !== pending.revision || report.body.phase !== 'stopped' || report.body.actualRun) throw plain('Settings or host selection changed. Start again.');
}
runtime ??= this.saveAgentConfiguration(v);
runtime ??= pending ? this.saveAgentConfiguration(v) : undefined;
const { profile, profileState } = this.profilePreview;
await this.credential({ action: 'register-agent', directory: this.hostDirectory, secret: v.secret, publicKey:key, profile: { profile, profileState }, runtimeId:runtime.id, expectedRevision:readSettings(this.hostDirectory).revision },abort.signal); check();
await this.credential({ action: 'register-agent', directory: this.hostDirectory, secret: v.secret, publicKey:key, profile: { profile, profileState }, ...(runtime ? { runtimeId:runtime.id } : {}), expectedRevision:readSettings(this.hostDirectory).revision },abort.signal); check();
const committed = readSettings(this.hostDirectory);
if (!committed.agents.some(agent => agent.publicKey === key)) throw plain('Agent registration completion was not verified. Inspect saved registration before retrying.');
this.profilePreview = undefined;
if (!pending) this.status = 'Agent registered. Configuration saved.';
if (!pending) this.status = 'Agent registered. You can configure it before Start.';
else {
if (!runtime) throw plain('Select an available harness and provider before Start.');
const guard = () => {
check();
if (this.continuation !== pending || this.owner !== pending.owner || !this.directory.some(a => a.publicKey === pending.agent)) throw plain('Start selection changed. Inspect saved registration.');

View File

@@ -73,8 +73,9 @@ async function registerForm(continuation?: string, agent?: string) {
const result = await request('profile-preview',{secret,...(agent ? {agent} : {}),...(continuation ? {continuation} : {})});
if (result.state !== 'completed') { settled = true; return; }
const preview = snapshot.profilePreview; if (!preview) return;
const configuration = await configurationForm(screen, () => snapshot, request); if (!configuration) return;
if (!await screen.confirm(`Register agent\n${preview.profile?.name ?? 'Name unavailable'}\n${preview.publicKey}\n${continuation ? 'Register and Start?' : 'Save registration?'}`)) return;
const configuration = continuation ? await configurationForm(screen, () => snapshot, request) : {};
if (!configuration) return;
if (!await screen.confirm(`Register agent\n${preview.profile?.name ?? 'Name unavailable'}\n${preview.publicKey}\n${continuation ? 'Register, configure and Start?' : 'Save registration? You can configure it afterward.'}`)) return;
await request('register-agent',{secret,...configuration,...(agent ? {agent} : {}),...(continuation ? {continuation} : {})}); settled = true;
} finally { secret = ''; if (continuation && !settled) await request('retire-continuation'); }
}

View File

@@ -246,16 +246,21 @@ test('cancelling after host enrollment preserves stopped placement and cannot la
});
test('Other-agent registration requires that exact identity even without a Start continuation', async t => {
test('Other-agent registration requires that exact identity and does not require runtime setup', async t => {
const f = await fixture(t);
const other = 'f'.repeat(64);
assert.equal((await f.request('profile-preview', { secret: f.nsec, agent: other })).state, 'failed');
assert.equal(readSettings(f.directory).agents.length, 0);
assert.equal((await f.request('profile-preview', { secret: f.nsec, agent: f.agent })).state, 'completed');
assert.equal((await f.request('register-agent', { secret: f.nsec, agent: other, runtime: f.runtimeId })).state, 'failed');
assert.equal((await f.request('register-agent', { secret: f.nsec, agent: other })).state, 'failed');
assert.equal(readSettings(f.directory).agents.length, 0);
assert.equal((await f.request('register-agent', { secret: f.nsec, agent: f.agent, runtime: f.runtimeId })).state, 'completed');
assert.equal(readSettings(f.directory).agents[0]?.publicKey, f.agent);
const before = readSettings(f.directory);
assert.equal((await f.request('register-agent', { secret: f.nsec, agent: f.agent })).state, 'completed');
const after = readSettings(f.directory);
assert.equal(after.agents[0]?.publicKey, f.agent);
assert.equal(after.agents[0]?.runtimeId, undefined);
assert.deepEqual(after.providers, before.providers);
assert.deepEqual(after.runtimes, before.runtimes);
const rows = sectionRows(f.snapshot, 1);
assert.equal(rows.filter(row => row.id === f.agent).length, 1, 'registered identity has one row');
assert.ok(rows.indexOf(rows.find(row => row.id === f.agent)!) < rows.findIndex(row => row.id === 'other-agents'), 'successful registration moves the row immediately without Start');