framework
Version:
The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.
118 lines • 5.1 kB
JavaScript
/**
* The latest checklist verdict (#431), from the `checklist`/`improve`/`done` bootstrap
* events. Null until a checklist has run — a prototype build never loops, and neither
* does a run with no review configured (#1372: no preset, no serve); `done` with zero
* passes is not a loop ending, so it does not open a status either. `done` closes an
* open status out with the final verdict.
*/
export function loopStatus(events) {
let status = null;
for (const event of events) {
if (event.kind !== 'bootstrap')
continue;
const e = event.event;
if (e.type === 'checklist') {
status = { pass: e.pass, passing: e.passing, blockers: [...e.blockers], productionGrade: e.passing, finished: false };
}
else if (e.type === 'improve') {
status = { pass: e.pass, passing: false, blockers: [...e.blockers], productionGrade: false, finished: false };
}
else if (e.type === 'done') {
const r = e.result;
if (r.passes === 0 && !status)
continue;
status = {
pass: r.passes,
passing: r.productionGrade,
blockers: [...r.blockers],
productionGrade: r.productionGrade,
finished: true,
};
}
}
return status;
}
/** The deploy plan the run decided on, or null before the deploy phase ran. Latest wins. */
export function deployPlan(events) {
for (let i = events.length - 1; i >= 0; i--) {
const event = events[i];
if (event.kind !== 'bootstrap' || event.event.type !== 'deploy')
continue;
const p = event.event.plan;
return { render: p.render, target: p.target, reason: p.reason };
}
return null;
}
/**
* The run's lifecycle progress (#326): the latest `session-name` the agent set and whether
* a `ready-for-merge` has fired. Drives the dashboard status label + dot (orange building,
* green ready). Always returns a value — an untouched run is `{ readyForMerge: false }`.
*/
export function runProgress(events) {
const progress = { readyForMerge: false };
for (const event of events) {
if (event.kind === 'session-name')
progress.sessionName = event.name;
else if (event.kind === 'ready-for-merge')
progress.readyForMerge = true;
}
return progress;
}
/**
* What the session is armed to hand back, folded from its own events (#1102).
*
* Both halves start armed, so a run from before this existed — which emits no `handoff-armed` —
* reads as armed, which is what it will actually do once it is running new code. Latest wins: the
* checkboxes re-emit on every change.
*
* `initial` seeds the armed pair for a reader whose event stream missed the opening
* `handoff-armed` (#1376): the run writes it as its very first event, before the live channel has
* attached, so a live tab can only learn the real state from the run record's mirror
* (`RunRecord.handoff`) — without it, a session the launcher armed push-only reads as "Open PR".
* A `handoff-armed` event in the stream still wins: it is newer than any record snapshot.
*/
export function handoffState(events, initial) {
const state = { push: initial?.push ?? true, pr: initial?.pr ?? true, merge: initial?.merge ?? false };
for (const event of events) {
if (event.kind === 'handoff-armed') {
state.push = event.push;
state.pr = event.pr;
// Absent on pre-#1382 events: keep the seed rather than flipping an armed merge off.
if (event.merge !== undefined)
state.merge = event.merge;
}
else if (event.kind === 'handoff') {
state.result =
event.outcome === 'done'
? { outcome: 'done', ...(event.url ? { url: event.url } : {}) }
: event.outcome === 'failed'
? { outcome: 'failed', error: event.error }
: { outcome: 'skipped', reason: event.reason };
}
}
return state;
}
/**
* The session behind the run (#431): the driver + workspace from the opening `session`
* event, then the id and any deep link from the latest `session-update`. Null before the
* session opens. The link is what the old dashboard surfaced as "open session".
*/
export function sessionInfo(events) {
let info = null;
for (const event of events) {
if (event.kind === 'session') {
info = {
driver: event.driver,
fake: event.fake,
workspace: event.workspace,
...(event.sessionLink ? { sessionLink: event.sessionLink } : {}),
...(event.model ? { model: event.model } : {}),
};
}
else if (event.kind === 'session-update') {
info = { ...(info ?? {}), sessionId: event.sessionId, ...(event.sessionLink ? { sessionLink: event.sessionLink } : {}) };
}
}
return info;
}
//# sourceMappingURL=run-view.js.map