feat: 支持账号优先级异构批量应用

This commit is contained in:
pikastech
2026-07-24 08:43:55 +02:00
parent d30a92c5fe
commit 1ae9d360cc
4 changed files with 50 additions and 7 deletions
@@ -16,6 +16,7 @@ export interface RuntimeOptions {
template: string | null;
kind: "temp-unschedulable" | "priority" | "account-name" | "capacity" | "groups" | "model-mapping";
priority: number | null;
priorities: Record<string, number> | null;
capacity: number | null;
groupIds: number[];
name: string | null;
@@ -49,6 +50,7 @@ export function parseRuntimeOptions(args: string[]): RuntimeOptions {
let template: string | null = null;
let kind: RuntimeOptions["kind"] = "temp-unschedulable";
let priority: number | null = null;
let priorities: Record<string, number> | null = null;
let capacity: number | null = null;
let groupIds: number[] = [];
let name: string | null = null;
@@ -98,6 +100,8 @@ export function parseRuntimeOptions(args: string[]): RuntimeOptions {
else if (arg.startsWith("--kind=")) kind = parseRuntimeKind(arg.slice("--kind=".length));
else if (arg === "--priority") priority = parsePriority(readValue("--priority"));
else if (arg.startsWith("--priority=")) priority = parsePriority(arg.slice("--priority=".length));
else if (arg === "--priorities-json") priorities = parsePriorities(readValue("--priorities-json"));
else if (arg.startsWith("--priorities-json=")) priorities = parsePriorities(arg.slice("--priorities-json=".length));
else if (arg === "--capacity") capacity = parseCapacity(readValue("--capacity"));
else if (arg.startsWith("--capacity=")) capacity = parseCapacity(arg.slice("--capacity=".length));
else if (arg === "--groups") groupIds = parseGroupIds(readValue("--groups"));
@@ -134,7 +138,7 @@ export function parseRuntimeOptions(args: string[]): RuntimeOptions {
if (account !== null && accounts.length > 0) throw new Error("use only one of --account or --accounts");
if (selectConfiguredPolicy && (account !== null || accounts.length > 0)) throw new Error("--select-configured-policy cannot be combined with --account or --accounts");
if ((action === "get" || action === "infrastructure") && !account) throw new Error(`runtime ${action} requires --account <name-or-id>`);
if ((action === "apply" || action === "delete") && account === null && accounts.length === 0 && !selectConfiguredPolicy) {
if ((action === "apply" || action === "delete") && account === null && accounts.length === 0 && !selectConfiguredPolicy && priorities === null) {
throw new Error(`runtime ${action} requires --account, --accounts, or --select-configured-policy`);
}
if (accounts.length > 0 && action !== "apply" && action !== "delete") throw new Error(`runtime ${action} does not accept --accounts`);
@@ -156,9 +160,11 @@ export function parseRuntimeOptions(args: string[]): RuntimeOptions {
if (!selectConfiguredPolicy && excludeAccounts.length > 0) throw new Error("--exclude-accounts requires --select-configured-policy");
if (action === "apply" && kind === "temp-unschedulable" && !template) throw new Error("runtime apply --kind temp-unschedulable requires --template <id>");
if (kind === "priority" && action !== "apply" && action !== "create") throw new Error("runtime --kind priority only supports apply or create");
if (kind === "priority" && priority === null) throw new Error("runtime apply --kind priority requires --priority <1-1000>");
if (kind === "priority" && priority === null && priorities === null) throw new Error("runtime apply --kind priority requires --priority <1-1000> or --priorities-json <object>");
if (kind === "priority" && priority !== null && priorities !== null) throw new Error("use only one of --priority or --priorities-json");
if (priorities !== null && (account !== null || accounts.length > 0 || selectConfiguredPolicy)) throw new Error("--priorities-json provides its own account ID selection");
if (kind === "priority" && template !== null) throw new Error("runtime apply --kind priority does not accept --template");
if (kind !== "priority" && priority !== null && action !== "create") throw new Error("--priority requires --kind priority");
if (kind !== "priority" && (priority !== null || priorities !== null) && action !== "create") throw new Error("--priority and --priorities-json require --kind priority");
if (kind === "capacity" && action !== "apply" && action !== "create") throw new Error("runtime --kind capacity only supports apply or create");
if (kind === "capacity" && capacity === null) throw new Error("runtime apply --kind capacity requires --capacity <1-100000>");
if (kind === "capacity" && template !== null) throw new Error("runtime apply --kind capacity does not accept --template");
@@ -187,7 +193,7 @@ export function parseRuntimeOptions(args: string[]): RuntimeOptions {
if (!/^\d+[mhd]$/u.test(since)) throw new Error("--since must use <number>m, <number>h, or <number>d");
if (["list", "get", "errors", "events", "infrastructure"].includes(action) && confirm) throw new Error(`runtime ${action} does not accept --confirm`);
if ([full, raw, json].filter(Boolean).length > 1) throw new Error("use only one of --json, --full, or --raw");
return { action, account, accounts, selectConfiguredPolicy, accountType, excludeAccounts, group, allGroups, platform, pageToken, template, kind, priority, capacity, groupIds, name, baseUrl, proxyId, modelMappings, confirm, full, raw, json, recent, since, tail, targetId };
return { action, account, accounts, selectConfiguredPolicy, accountType, excludeAccounts, group, allGroups, platform, pageToken, template, kind, priority, priorities, capacity, groupIds, name, baseUrl, proxyId, modelMappings, confirm, full, raw, json, recent, since, tail, targetId };
}
function parseBaseUrl(value: string): string {
@@ -263,6 +269,24 @@ function parsePriority(value: string): number {
return parsed;
}
function parsePriorities(value: string): Record<string, number> {
let parsed: unknown;
try {
parsed = JSON.parse(value);
} catch {
throw new Error("--priorities-json must be a JSON object");
}
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("--priorities-json must be a JSON object");
const entries = Object.entries(parsed);
if (entries.length === 0 || entries.length > 100) throw new Error("--priorities-json requires 1 to 100 account priorities");
const result: Record<string, number> = {};
for (const [accountId, priority] of entries) {
if (!/^[1-9]\d*$/u.test(accountId)) throw new Error("--priorities-json keys must be positive numeric account IDs");
result[accountId] = parsePriority(String(priority));
}
return result;
}
function parseRuntimeTail(value: string): number {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed < 100 || parsed > 200_000) throw new Error("--tail must be an integer from 100 to 200000");
@@ -1183,7 +1183,12 @@ def mutation_plan(detail, action, include_rules):
}
if PAYLOAD.get("kind") == "priority":
before_priority = int(detail.get("priority") or 0)
desired_priority = int(PAYLOAD.get("priority"))
priorities = PAYLOAD.get("priorities")
desired_priority = int(
priorities.get(str(detail.get("id")))
if isinstance(priorities, dict)
else PAYLOAD.get("priority")
)
return {
"accountName": detail.get("name"),
"accountId": detail.get("id"),
@@ -24,7 +24,7 @@ export async function codexPoolRuntime(config: UniDeskConfig, args: string[]): P
const payload = {
action: options.action,
account: options.account,
accounts: options.accounts,
accounts: options.priorities === null ? options.accounts : Object.keys(options.priorities),
selectConfiguredPolicy: options.selectConfiguredPolicy,
accountType: options.accountType,
excludeAccounts: options.excludeAccounts,
@@ -34,6 +34,7 @@ export async function codexPoolRuntime(config: UniDeskConfig, args: string[]): P
pageToken: options.pageToken,
kind: options.kind,
priority: options.priority,
priorities: options.priorities,
capacity: options.capacity,
groupIds: options.groupIds,
name: options.name,
@@ -101,6 +102,7 @@ export async function codexPoolRuntime(config: UniDeskConfig, args: string[]): P
template: options.template,
kind: options.kind,
priority: options.priority,
priorities: options.priorities,
capacity: options.capacity,
groupIds: options.groupIds,
name: options.name,
@@ -198,7 +200,7 @@ function renderRuntimeHelp(args: string[]): RenderedCliResult {
"",
"Kinds:",
" temp-unschedulable Requires --template <id>",
" priority Requires --priority <1-1000>; lower is higher",
" priority Requires --priority <1-1000> or heterogeneous --priorities-json <account-id-to-priority>; lower is higher",
" capacity Requires --capacity <1-100000>; account concurrency guard",
" account-name Requires one --account and --name <new-name>",
" groups Requires one --account and --groups <id,...>; replaces all memberships",