feat(api): expose REST-only agent routes
Agent CI/CD / docker-image (push) Failing after 35s
Agent CI/CD / deploy-fallback-log (push) Successful in 1s

This commit is contained in:
2026-07-30 20:38:52 +08:00
parent 2415f75841
commit 94529cb141
29 changed files with 3525 additions and 378 deletions
+4 -5
View File
@@ -20,10 +20,10 @@ export function readJsonFile(path: string, label: string): unknown {
export function parseBurstFile(path: string): [string[], number[]] {
let raw = readJsonFile(path, "burst");
if (isRecord(raw) && "bursts" in raw) raw = raw.bursts;
if (isRecord(raw) && "burst_ID" in raw && "burst_size" in raw && Array.isArray(raw.burst_ID) && Array.isArray(raw.burst_size)) {
const ids = raw.burst_ID.map(String);
if (isRecord(raw) && "burst_id" in raw && "burst_size" in raw && Array.isArray(raw.burst_id) && Array.isArray(raw.burst_size)) {
const ids = raw.burst_id.map(String);
const sizes = raw.burst_size.map(Number);
if (ids.length !== sizes.length) throw new CliError("CLI 参数错误", "BURST_FILE_INVALID", "burst file burst_ID and burst_size must have the same length", 2);
if (ids.length !== sizes.length) throw new CliError("CLI 参数错误", "BURST_FILE_INVALID", "burst file burst_id and burst_size must have the same length", 2);
return [ids, sizes];
}
if (Array.isArray(raw)) {
@@ -35,7 +35,7 @@ export function parseBurstFile(path: string): [string[], number[]] {
raw.map((item) => Number((item as Record<string, unknown>).size)),
];
}
throw new CliError("CLI 参数错误", "BURST_FILE_INVALID", "burst file must be a JSON array or object with burst_ID/burst_size", 2);
throw new CliError("CLI 参数错误", "BURST_FILE_INVALID", "burst file must be a JSON array or object with burst_id/burst_size", 2);
}
export function parseValveSettingFile(path: string): [string[], number[]] {
@@ -66,4 +66,3 @@ export function assignDatasetKeys(target: Record<string, unknown>, path: string,
}
}
}
+2 -11
View File
@@ -1,5 +1,4 @@
import { CliError, errorMessage } from "./errors.js";
import { requireNetwork, requireUsername } from "./runtime.js";
import { success } from "./output.js";
import type { RequestOptions, RuntimeContext } from "./types.js";
@@ -35,18 +34,10 @@ function appendParams(url: URL, params: Record<string, unknown> = {}): void {
}
}
function withNetworkParam(params: Record<string, unknown> | undefined, network: string): Record<string, unknown> {
if (params?.network !== undefined || params?.network_name !== undefined || params?.name !== undefined) return params ?? {};
return { ...(params ?? {}), network };
}
export async function requestJson(ctx: RuntimeContext, request: RequestOptions): Promise<[unknown, number]> {
const { method, path, params, body, requireAuth = true, requireProject = false, requireNetworkCtx = false, requireUsernameCtx = false } = request;
const network = requireNetworkCtx ? requireNetwork(ctx) : null;
if (requireUsernameCtx) requireUsername(ctx);
const { method, path, params, body, requireAuth = true, requireProject = false } = request;
const url = new URL(`/api/v1${path}`, ctx.server.replace(/\/+$/, ""));
const requestParams = network && (params !== undefined || body === undefined) ? withNetworkParam(params, network) : params;
appendParams(url, requestParams);
appendParams(url, params);
const started = performance.now();
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), ctx.timeout * 1000);
-14
View File
@@ -31,8 +31,6 @@ export async function loadAuthContext(authStdin: boolean): Promise<AuthContext>
server: process.env.TJWATER_SERVER,
access_token: process.env.TJWATER_ACCESS_TOKEN,
project_id: process.env.TJWATER_PROJECT_ID,
username: process.env.TJWATER_USERNAME,
network: process.env.TJWATER_NETWORK,
headers: process.env.TJWATER_EXTRA_HEADERS ? JSON.parse(process.env.TJWATER_EXTRA_HEADERS) : {},
};
const headers = (raw.headers ?? {}) as unknown;
@@ -43,8 +41,6 @@ export async function loadAuthContext(authStdin: boolean): Promise<AuthContext>
server: pick(raw, "server", "base_url"),
accessToken: pick(raw, "access_token", "token", "accessToken"),
projectId: pick(raw, "project_id", "projectId", "x_project_id"),
username: pick(raw, "username", "preferred_username"),
network: pick(raw, "network", "project_code", "projectCode", "project"),
headers: Object.fromEntries(Object.entries(headers as Record<string, unknown>).map(([key, value]) => [String(key), String(value)])),
};
}
@@ -81,16 +77,6 @@ export async function buildRuntime(globals: GlobalArgs): Promise<RuntimeContext>
};
}
export function requireNetwork(ctx: RuntimeContext): string {
if (ctx.auth.network) return ctx.auth.network;
throw new CliError("认证失败", "NETWORK_CONTEXT_REQUIRED", "missing network in auth context for legacy network-based endpoints", 3, false, null, ["add network to auth context"]);
}
export function requireUsername(ctx: RuntimeContext): string {
if (ctx.auth.username) return ctx.auth.username;
throw new CliError("认证失败", "USERNAME_CONTEXT_REQUIRED", "missing username in auth context", 3, false, null, ["add username to auth context"]);
}
export function resolveScheme(ctx: RuntimeContext, explicit: string | undefined, must = false): string | null {
const scheme = explicit || ctx.scheme;
if (must && !scheme) throw new CliError("CLI 参数错误", "SCHEME_REQUIRED", "missing scheme; use --scheme", 2);
-4
View File
@@ -6,8 +6,6 @@ export interface AuthContext {
server: string | null;
accessToken: string | null;
projectId: string | null;
username: string | null;
network: string | null;
headers: Record<string, string>;
}
@@ -48,8 +46,6 @@ export interface RequestOptions {
body?: unknown;
requireAuth?: boolean;
requireProject?: boolean;
requireNetworkCtx?: boolean;
requireUsernameCtx?: boolean;
}
export type Handler = (ctx: RuntimeContext, argv: string[]) => Promise<void> | void;