chore: improve migration UX + add changeset for cutover-aware ao update

Refs #2129
This commit is contained in:
harshitsinghbhandari 2026-06-18 00:55:58 +05:30
parent cbb714aad9
commit 1b721328e2
No known key found for this signature in database
2 changed files with 24 additions and 2 deletions

View File

@ -0,0 +1,11 @@
---
"@aoagents/ao-cli": minor
---
Teach `ao update` to perform the legacy-to-rewrite cutover (bridge 0.9.6).
When a rewrite build is published under the npm `next` dist-tag (or `AO_CUTOVER_VERSION` is set) and the current install is still legacy (`major.minor < 0.10`), `ao update` now migrates the user's data via `ao migrate` (issue #2129) and installs the rewrite at the exact pinned version instead of running the normal channel update.
The cutover flow: refuse if any active worker session is running (the orchestrator's own state never blocks), require a terminal confirmation (never auto-confirm a dashboard/api-invoked spawn), stop the daemon without restoring, run migration before the install replaces the legacy binary, install the rewrite, verify the new version, and finish without restarting the legacy daemon. `--check` now also reports `cutoverAvailable` and `cutoverTarget`.
When no `next` build exists (the common case), `ao update` behaves exactly as before across all install methods.

View File

@ -404,7 +404,9 @@ interface MigrationResult {
* from stdout the contract is documented on {@link MigrationSummary}.
*/
async function runMigration(): Promise<MigrationResult> {
const result = await runCommandCapture("ao", ["migrate", "--json"], { echo: true });
// No echo: `--json` emits a machine-readable blob, not human progress. We
// surface a readable summary on success and the raw output on failure.
const result = await runCommandCapture("ao", ["migrate", "--json"]);
return {
exitCode: result.exitCode,
summary: parseMigrationSummary(result.output),
@ -412,6 +414,13 @@ async function runMigration(): Promise<MigrationResult> {
};
}
/** One-line human summary of a migration result, for the success path. */
function formatMigrationSummary(summary: MigrationSummary | null): string {
if (!summary) return "Migration complete.";
const { projects, orchestrators } = summary;
return `Migration complete: ${projects.created} project(s), ${orchestrators.created} orchestrator(s) migrated.`;
}
function parseMigrationSummary(output: string): MigrationSummary | null {
const match = output.match(/\{[\s\S]*\}/);
if (!match) return null;
@ -513,13 +522,15 @@ async function handleCutover(method: InstallMethod, target: string): Promise<voi
console.error(
chalk.red(`\nMigration failed (exit ${migration.exitCode}). AO was NOT updated.`),
);
if (migration.output.trim()) console.error(chalk.dim(migration.output.trim()));
console.error(
chalk.yellow(
"You are still on the legacy version. Resolve the migration error above and retry `ao update`.",
"You are still on the legacy version. Resolve the migration error and retry `ao update`.",
),
);
process.exit(migration.exitCode);
}
console.log(chalk.dim(formatMigrationSummary(migration.summary)));
// 5. Install the rewrite at the target.
const cmd = getCutoverInstallCommand(method, target);