feat(fleet): add reviewed v1-to-v2 migration preview (#788)
This commit was merged in pull request #788.
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
type PersonaResolution,
|
||||
type RoleAuthority,
|
||||
} from '../commands/fleet-personas.js';
|
||||
import { compareCodePoints } from './deterministic-order.js';
|
||||
|
||||
export const ROSTER_V2_SUPPORTED_RUNTIMES = ['claude', 'codex', 'opencode', 'pi'] as const;
|
||||
export const ROSTER_V2_REASONING_LEVELS = ['low', 'medium', 'high'] as const;
|
||||
@@ -172,7 +173,7 @@ export const ROSTER_V2_JSON_SCHEMA: JsonSchema = {
|
||||
additionalProperties: false,
|
||||
required: ['socket_name', 'holder_session'],
|
||||
properties: {
|
||||
socket_name: { type: 'string', pattern: '^[A-Za-z0-9_.-]+$' },
|
||||
socket_name: { type: 'string', pattern: '^[A-Za-z0-9_.-]*$' },
|
||||
holder_session: { type: 'string', pattern: '^[A-Za-z0-9_.-]+$' },
|
||||
},
|
||||
},
|
||||
@@ -272,6 +273,7 @@ const AGENT_KEYS = [
|
||||
const LIFECYCLE_KEYS = ['enabled', 'desired_state'];
|
||||
const LAUNCH_KEYS = ['yolo'];
|
||||
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
||||
const TMUX_SOCKET_IDENTIFIER = /^[A-Za-z0-9_.-]*$/;
|
||||
const TMUX_IDENTIFIER = /^[A-Za-z0-9_.-]+$/;
|
||||
const POLICY_IDENTIFIER = /^[a-z][a-z0-9-]*$/;
|
||||
|
||||
@@ -327,7 +329,7 @@ function normalizeTmux(value: unknown): FleetRosterV2Tmux {
|
||||
const raw = requiredObject(value, 'Roster v2 tmux');
|
||||
assertKnownKeys(raw, 'Roster v2 tmux', TMUX_KEYS);
|
||||
return {
|
||||
socketName: requiredTmuxIdentifier(raw.socket_name, 'Roster v2 tmux socket_name'),
|
||||
socketName: requiredTmuxSocket(raw.socket_name, 'Roster v2 tmux socket_name'),
|
||||
holderSession: requiredTmuxIdentifier(raw.holder_session, 'Roster v2 tmux holder_session'),
|
||||
};
|
||||
}
|
||||
@@ -348,7 +350,7 @@ function normalizeRuntimes(value: unknown): Readonly<Record<string, FleetRosterV
|
||||
throw new RosterV2ValidationError('Roster v2 runtimes must not be empty.');
|
||||
|
||||
const result: Record<string, FleetRosterV2Runtime> = {};
|
||||
for (const name of names.sort()) {
|
||||
for (const name of names.sort(compareCodePoints)) {
|
||||
const runtime = requiredRuntime(name, 'Roster v2 runtime name');
|
||||
const config = requiredObject(raw[name], `Roster v2 runtime "${runtime}"`);
|
||||
assertKnownKeys(config, `Roster v2 runtime "${runtime}"`, RUNTIME_KEYS);
|
||||
@@ -416,7 +418,7 @@ function normalizeAgents(
|
||||
};
|
||||
});
|
||||
return agents.sort((left: FleetRosterV2Agent, right: FleetRosterV2Agent): number =>
|
||||
left.name.localeCompare(right.name),
|
||||
compareCodePoints(left.name, right.name),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -473,6 +475,17 @@ function requiredIdentifier(value: unknown, label: string): string {
|
||||
return result;
|
||||
}
|
||||
|
||||
function requiredTmuxSocket(value: unknown, label: string): string {
|
||||
if (typeof value !== 'string') {
|
||||
throw new RosterV2ValidationError(`${label} is required and must be a string.`);
|
||||
}
|
||||
const result = value.trim();
|
||||
if (!TMUX_SOCKET_IDENTIFIER.test(result)) {
|
||||
throw new RosterV2ValidationError(`Invalid ${label}: ${result}.`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function requiredTmuxIdentifier(value: unknown, label: string): string {
|
||||
const result = requiredString(value, label);
|
||||
if (!TMUX_IDENTIFIER.test(result))
|
||||
@@ -524,7 +537,7 @@ function toSourceShape(roster: FleetRosterV2): Record<string, unknown> {
|
||||
},
|
||||
runtimes: Object.fromEntries(
|
||||
Object.entries(roster.runtimes)
|
||||
.sort(([left], [right]): number => left.localeCompare(right))
|
||||
.sort(([left], [right]): number => compareCodePoints(left, right))
|
||||
.map(([name, runtime]): [string, unknown] => [
|
||||
name,
|
||||
{ reset_command: runtime.resetCommand },
|
||||
@@ -532,7 +545,7 @@ function toSourceShape(roster: FleetRosterV2): Record<string, unknown> {
|
||||
),
|
||||
agents: [...roster.agents]
|
||||
.sort((left: FleetRosterV2Agent, right: FleetRosterV2Agent): number =>
|
||||
left.name.localeCompare(right.name),
|
||||
compareCodePoints(left.name, right.name),
|
||||
)
|
||||
.map(
|
||||
(agent: FleetRosterV2Agent): Record<string, unknown> => ({
|
||||
|
||||
Reference in New Issue
Block a user