feat: 实现 Queue Q1 API 和 CLI 骨架

This commit is contained in:
Codex
2026-06-01 22:20:09 +08:00
parent f4a26e5961
commit 237b10c4da
11 changed files with 566 additions and 14 deletions
+39 -1
View File
@@ -1,5 +1,5 @@
import { createHash, randomUUID } from "node:crypto";
import type { BackendProfile, CreateCommandInput, CreateRunInput, ExecutionPolicy, JsonRecord, JsonValue, ResourceBundleRef, SecretRef, SessionRef } from "./types.js";
import type { BackendProfile, CreateCommandInput, CreateQueueTaskInput, CreateRunInput, ExecutionPolicy, JsonRecord, JsonValue, QueueTaskState, ResourceBundleRef, SecretRef, SessionRef } from "./types.js";
import { AgentRunError } from "./errors.js";
import { backendProfileSpec, backendProfiles, isBackendProfile } from "./backend-profiles.js";
@@ -168,3 +168,41 @@ export function validateCreateCommand(input: unknown): CreateCommandInput {
const idempotencyKey = typeof record.idempotencyKey === "string" && record.idempotencyKey.trim().length > 0 ? record.idempotencyKey.trim() : undefined;
return { type, payload, ...(idempotencyKey ? { idempotencyKey } : {}) };
}
export function validateCreateQueueTask(input: unknown): CreateQueueTaskInput {
const record = asRecord(input, "queueTask");
const tenantId = requiredString(record, "tenantId");
if (!allowedTenants.has(tenantId)) throw new AgentRunError("tenant-policy-denied", `tenantId ${tenantId} is not allowed`, { httpStatus: 403 });
const backendProfileValue = optionalString(record.backendProfile) ?? "codex";
if (!isBackendProfile(backendProfileValue)) throw new AgentRunError("schema-invalid", `backendProfile ${backendProfileValue} is not supported in v0.1`, { httpStatus: 400, details: { allowedBackends: [...backendProfiles] } });
const queue = optionalString(record.queue) ?? "default";
const lane = optionalString(record.lane) ?? "default";
const priorityValue = record.priority ?? 0;
if (typeof priorityValue !== "number" || !Number.isFinite(priorityValue)) throw new AgentRunError("schema-invalid", "priority must be a finite number", { httpStatus: 400 });
const referencesValue = record.references ?? [];
if (!Array.isArray(referencesValue)) throw new AgentRunError("schema-invalid", "references must be an array", { httpStatus: 400 });
const result: CreateQueueTaskInput = {
tenantId,
projectId: requiredString(record, "projectId"),
queue,
lane,
title: requiredString(record, "title"),
priority: priorityValue,
backendProfile: backendProfileValue,
providerId: optionalString(record.providerId) ?? null,
workspaceRef: record.workspaceRef === undefined || record.workspaceRef === null ? null : requiredRecord(record, "workspaceRef") as CreateQueueTaskInput["workspaceRef"],
executionPolicy: record.executionPolicy === undefined || record.executionPolicy === null ? null : validateExecutionPolicy(requiredRecord(record, "executionPolicy")),
resourceBundleRef: validateResourceBundleRef(record.resourceBundleRef),
payload: record.payload === undefined ? {} : asRecord(record.payload, "payload"),
references: referencesValue.map((item, index) => asRecord(item, `references[${index}]`)),
metadata: record.metadata === undefined ? {} : asRecord(record.metadata, "metadata"),
};
const idempotencyKey = optionalString(record.idempotencyKey);
if (idempotencyKey) result.idempotencyKey = idempotencyKey;
return result;
}
export function validateQueueTaskState(value: string): QueueTaskState {
if (value === "pending" || value === "running" || value === "completed" || value === "failed" || value === "blocked" || value === "cancelled") return value;
throw new AgentRunError("schema-invalid", `queue task state ${value} is not supported`, { httpStatus: 400 });
}