archive: 紫鸟/HubStudio 文档快照(3889 篇 Markdown)
来源:紫鸟帮助中心/博客/主站资讯 + HubStudio API 文档/帮助中心 + 三个官方 skill 仓库。 只入库 Markdown 与元数据;原始 HTML 与 tarball 留在本机。
This commit is contained in:
commit
322b7737eb
3976 files changed
+695714
No files matched your search
@@ -0,0 +1,604 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const YAML = require("yaml");
|
||||
|
||||
const GENERATED_PATH = path.join(__dirname, "commands.generated.json");
|
||||
const OPENAPI_PATH = path.join(__dirname, "openapi.yaml");
|
||||
const generated = JSON.parse(fs.readFileSync(GENERATED_PATH, "utf8"));
|
||||
const BASE_URL = process.env.HUBSTUDIO_BASE_URL || generated.baseUrl || "http://127.0.0.1:6873";
|
||||
const COMMANDS = generated.commands || [];
|
||||
const COMMAND_MAP = new Map(COMMANDS.map((c) => [c.command, c]));
|
||||
|
||||
const ALIASES = {
|
||||
browserCreate: "postV1EnvCreate",
|
||||
browserStart: "postV1BrowserStart",
|
||||
browserStop: "postV1BrowserStop",
|
||||
browserStatus: "postV1BrowserAllBrowserStatus",
|
||||
browserForeground: "postV1BrowserForeground",
|
||||
browserStopAll: "postV1BrowserStopAll",
|
||||
displayAll: "postV1DisplayAll",
|
||||
browserArrange: "postV1BrowserArrange",
|
||||
};
|
||||
|
||||
class InputValidationError extends Error {
|
||||
constructor(message, fieldErrors = [], command = null) {
|
||||
super(message);
|
||||
this.name = "InputValidationError";
|
||||
this.code = "VALIDATION_ERROR";
|
||||
this.fieldErrors = fieldErrors;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
function loadOpenApi() {
|
||||
try {
|
||||
const content = fs.readFileSync(OPENAPI_PATH, "utf8");
|
||||
return YAML.parse(content) || {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const OPENAPI = loadOpenApi();
|
||||
|
||||
function resolveSchema(schema) {
|
||||
if (!schema || typeof schema !== "object") {
|
||||
return schema;
|
||||
}
|
||||
if (!schema.$ref || typeof schema.$ref !== "string") {
|
||||
return schema;
|
||||
}
|
||||
if (!schema.$ref.startsWith("#/")) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const parts = schema.$ref
|
||||
.replace(/^#\//, "")
|
||||
.split("/")
|
||||
.map((x) => x.replace(/~1/g, "/").replace(/~0/g, "~"));
|
||||
let current = OPENAPI;
|
||||
for (const part of parts) {
|
||||
if (!current || typeof current !== "object" || !(part in current)) {
|
||||
return schema;
|
||||
}
|
||||
current = current[part];
|
||||
}
|
||||
if (!current || typeof current !== "object") {
|
||||
return schema;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function getOperationSchema(method, endpointPath) {
|
||||
const methodKey = String(method || "post").toLowerCase();
|
||||
const op = OPENAPI && OPENAPI.paths && OPENAPI.paths[endpointPath] && OPENAPI.paths[endpointPath][methodKey];
|
||||
if (!op) {
|
||||
return { querySchema: null, bodySchema: null, requestBodyRequired: false };
|
||||
}
|
||||
|
||||
const queryParams = Array.isArray(op.parameters) ? op.parameters.filter((p) => p && p.in === "query") : [];
|
||||
const queryProperties = {};
|
||||
const queryRequired = [];
|
||||
for (const param of queryParams) {
|
||||
if (!param || !param.name) continue;
|
||||
queryProperties[param.name] = resolveSchema(param.schema || {});
|
||||
if (param.required) {
|
||||
queryRequired.push(param.name);
|
||||
}
|
||||
}
|
||||
|
||||
const querySchema = queryParams.length
|
||||
? {
|
||||
type: "object",
|
||||
properties: queryProperties,
|
||||
required: queryRequired,
|
||||
}
|
||||
: null;
|
||||
|
||||
const jsonBodySchema =
|
||||
op.requestBody &&
|
||||
op.requestBody.content &&
|
||||
op.requestBody.content["application/json"] &&
|
||||
op.requestBody.content["application/json"].schema
|
||||
? resolveSchema(op.requestBody.content["application/json"].schema)
|
||||
: null;
|
||||
|
||||
return {
|
||||
querySchema,
|
||||
bodySchema: jsonBodySchema,
|
||||
requestBodyRequired: Boolean(op.requestBody && op.requestBody.required),
|
||||
};
|
||||
}
|
||||
|
||||
function isPlainObject(value) {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function isEmptyValue(value) {
|
||||
if (value === undefined || value === null) return true;
|
||||
if (typeof value === "string") return value.trim().length === 0;
|
||||
if (Array.isArray(value)) return value.length === 0;
|
||||
if (isPlainObject(value)) return Object.keys(value).length === 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
function pushValidationError(errors, field, reason, expected, actual) {
|
||||
errors.push({
|
||||
field,
|
||||
reason,
|
||||
expected,
|
||||
actualType: actual,
|
||||
});
|
||||
}
|
||||
|
||||
function validateStringFormat(value, schema, fieldPath, errors) {
|
||||
const format = schema.format;
|
||||
if (!format) return;
|
||||
|
||||
if (format === "email") {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(value)) {
|
||||
pushValidationError(errors, fieldPath, "invalid email format", "email", "string");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (format === "uri" || format === "url") {
|
||||
try {
|
||||
new URL(value);
|
||||
} catch {
|
||||
pushValidationError(errors, fieldPath, "invalid URL format", "url", "string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateBySchema(value, schema, fieldPath, errors, options = {}) {
|
||||
const resolved = resolveSchema(schema || {});
|
||||
if (!resolved || typeof resolved !== "object") return;
|
||||
|
||||
if (value === undefined) return;
|
||||
if (value === null) {
|
||||
if (resolved.nullable || resolved.type === "null") return;
|
||||
pushValidationError(errors, fieldPath, "value cannot be null", resolved.type || "non-null", "null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(resolved.type)) {
|
||||
const typePass = resolved.type.some((oneType) => {
|
||||
const localErrors = [];
|
||||
validateBySchema(value, { ...resolved, type: oneType }, fieldPath, localErrors, options);
|
||||
return localErrors.length === 0;
|
||||
});
|
||||
if (!typePass) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", resolved.type.join("|"), typeof value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const inferredType = resolved.type || (resolved.properties ? "object" : undefined);
|
||||
if (!inferredType) return;
|
||||
|
||||
if (inferredType === "string") {
|
||||
if (typeof value !== "string") {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "string", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
if (typeof resolved.minLength === "number" && value.length < resolved.minLength) {
|
||||
pushValidationError(errors, fieldPath, "string too short", `minLength=${resolved.minLength}`, "string");
|
||||
}
|
||||
if (typeof resolved.maxLength === "number" && value.length > resolved.maxLength) {
|
||||
pushValidationError(errors, fieldPath, "string too long", `maxLength=${resolved.maxLength}`, "string");
|
||||
}
|
||||
if (resolved.pattern) {
|
||||
try {
|
||||
const reg = new RegExp(resolved.pattern);
|
||||
if (!reg.test(value)) {
|
||||
pushValidationError(errors, fieldPath, "string pattern mismatch", resolved.pattern, "string");
|
||||
}
|
||||
} catch {
|
||||
// Ignore invalid server-side regex pattern definitions.
|
||||
}
|
||||
}
|
||||
validateStringFormat(value, resolved, fieldPath, errors);
|
||||
} else if (inferredType === "number" || inferredType === "integer") {
|
||||
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", inferredType, Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
if (inferredType === "integer" && !Number.isInteger(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "integer", "number");
|
||||
}
|
||||
if (typeof resolved.minimum === "number" && value < resolved.minimum) {
|
||||
pushValidationError(errors, fieldPath, "number below minimum", `>=${resolved.minimum}`, "number");
|
||||
}
|
||||
if (typeof resolved.maximum === "number" && value > resolved.maximum) {
|
||||
pushValidationError(errors, fieldPath, "number above maximum", `<=${resolved.maximum}`, "number");
|
||||
}
|
||||
if (typeof resolved.exclusiveMinimum === "number" && value <= resolved.exclusiveMinimum) {
|
||||
pushValidationError(errors, fieldPath, "number below exclusiveMinimum", `>${resolved.exclusiveMinimum}`, "number");
|
||||
}
|
||||
if (typeof resolved.exclusiveMaximum === "number" && value >= resolved.exclusiveMaximum) {
|
||||
pushValidationError(errors, fieldPath, "number above exclusiveMaximum", `<${resolved.exclusiveMaximum}`, "number");
|
||||
}
|
||||
} else if (inferredType === "boolean") {
|
||||
if (typeof value !== "boolean") {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "boolean", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
} else if (inferredType === "array") {
|
||||
if (!Array.isArray(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "array", typeof value);
|
||||
return;
|
||||
}
|
||||
if (typeof resolved.minItems === "number" && value.length < resolved.minItems) {
|
||||
pushValidationError(errors, fieldPath, "array too short", `minItems=${resolved.minItems}`, "array");
|
||||
}
|
||||
if (typeof resolved.maxItems === "number" && value.length > resolved.maxItems) {
|
||||
pushValidationError(errors, fieldPath, "array too long", `maxItems=${resolved.maxItems}`, "array");
|
||||
}
|
||||
if (resolved.items) {
|
||||
value.forEach((item, idx) => {
|
||||
validateBySchema(item, resolved.items, `${fieldPath}[${idx}]`, errors, options);
|
||||
});
|
||||
}
|
||||
} else if (inferredType === "object") {
|
||||
if (!isPlainObject(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "object", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
const properties = resolved.properties || {};
|
||||
const required = Array.isArray(resolved.required) ? resolved.required : [];
|
||||
|
||||
for (const reqKey of required) {
|
||||
const reqPath = fieldPath ? `${fieldPath}.${reqKey}` : reqKey;
|
||||
if (!(reqKey in value) || isEmptyValue(value[reqKey])) {
|
||||
pushValidationError(errors, reqPath, "required field missing or empty", "required", "missing");
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(value)) {
|
||||
const childPath = fieldPath ? `${fieldPath}.${key}` : key;
|
||||
if (!(key in properties)) {
|
||||
if (options.allowUnknownFields) {
|
||||
continue;
|
||||
}
|
||||
pushValidationError(errors, childPath, "unknown field", "defined property", "unknown");
|
||||
continue;
|
||||
}
|
||||
validateBySchema(value[key], properties[key], childPath, errors, options);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(resolved.enum) && resolved.enum.length > 0) {
|
||||
const enumPass = resolved.enum.some((x) => x === value);
|
||||
if (!enumPass) {
|
||||
pushValidationError(errors, fieldPath, "enum mismatch", JSON.stringify(resolved.enum), typeof value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateCommandInputs(commandName, generatedCmd, body, query, strictValidation = true) {
|
||||
const errors = [];
|
||||
const { querySchema, bodySchema, requestBodyRequired } = getOperationSchema(generatedCmd.method, generatedCmd.path);
|
||||
const options = { allowUnknownFields: !strictValidation };
|
||||
|
||||
if (requestBodyRequired && isEmptyValue(body)) {
|
||||
pushValidationError(errors, "body", "required request body missing or empty", "non-empty object", "missing");
|
||||
}
|
||||
|
||||
if (querySchema) {
|
||||
validateBySchema(query || {}, querySchema, "query", errors, options);
|
||||
} else if (query && isPlainObject(query) && Object.keys(query).length > 0) {
|
||||
pushValidationError(errors, "query", "unexpected query parameters", "no query params", "object");
|
||||
}
|
||||
|
||||
if (bodySchema) {
|
||||
validateBySchema(body || {}, bodySchema, "body", errors, options);
|
||||
} else if (body && isPlainObject(body) && Object.keys(body).length > 0) {
|
||||
pushValidationError(errors, "body", "unexpected request body", "no body", "object");
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new InputValidationError("Request validation failed", errors, commandName);
|
||||
}
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log(`HubStudio CLI
|
||||
|
||||
Usage:
|
||||
node hubstudio.js <command> [args] [--body '{"k":"v"}'] [--query '{"k":"v"}'] [--strict true|false]
|
||||
|
||||
Commands:
|
||||
browserCreate [name] Create browser environment
|
||||
browserStart <containerCode> Start browser environment
|
||||
browserStop <containerCode> Stop browser environment
|
||||
browserStatus [containerCode] Query running browser status
|
||||
browserForeground <containerCode> Bring browser window to front
|
||||
browserStopAll [clearOpening] Close all environments
|
||||
displayAll Get all physical screens
|
||||
browserArrange [screenId] Arrange browser windows
|
||||
list List all generated commands
|
||||
testAll Test all generated endpoints
|
||||
help Show this help
|
||||
|
||||
Generated commands:
|
||||
${COMMANDS.length} commands are available, e.g.:
|
||||
- postV1BrowserStart
|
||||
- postV1BrowserStop
|
||||
- postV1EnvCreate
|
||||
|
||||
Env:
|
||||
HUBSTUDIO_BASE_URL Default: http://127.0.0.1:6873
|
||||
HUBSTUDIO_STRICT_VALIDATION Default: true (set false to allow unknown fields)
|
||||
`);
|
||||
}
|
||||
|
||||
function parseJsonArg(flag, value) {
|
||||
if (!value) {
|
||||
throw new Error(`Missing value for ${flag}`);
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (err) {
|
||||
throw new Error(`Invalid JSON for ${flag}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseCommandArgs(rawArgs) {
|
||||
const positional = [];
|
||||
let bodyOverride = null;
|
||||
let queryOverride = null;
|
||||
let strictValidation = process.env.HUBSTUDIO_STRICT_VALIDATION !== "false";
|
||||
|
||||
for (let i = 0; i < rawArgs.length; i += 1) {
|
||||
const token = rawArgs[i];
|
||||
if (token === "--body") {
|
||||
bodyOverride = parseJsonArg("--body", rawArgs[i + 1]);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (token === "--query") {
|
||||
queryOverride = parseJsonArg("--query", rawArgs[i + 1]);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (token === "--strict") {
|
||||
const next = rawArgs[i + 1];
|
||||
if (!next || next.startsWith("--")) {
|
||||
strictValidation = true;
|
||||
} else {
|
||||
const normalized = String(next).toLowerCase();
|
||||
if (normalized === "true") {
|
||||
strictValidation = true;
|
||||
} else if (normalized === "false") {
|
||||
strictValidation = false;
|
||||
} else {
|
||||
throw new InputValidationError("Request validation failed", [
|
||||
{
|
||||
field: "--strict",
|
||||
reason: "invalid boolean flag value",
|
||||
expected: "true|false",
|
||||
actualType: typeof next,
|
||||
},
|
||||
]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
positional.push(token);
|
||||
}
|
||||
|
||||
return { positional, bodyOverride, queryOverride, strictValidation };
|
||||
}
|
||||
|
||||
async function post(endpointPath, body = {}, query = null) {
|
||||
const url = new URL(endpointPath, BASE_URL);
|
||||
if (query && typeof query === "object") {
|
||||
for (let [k, v] of Object.entries(query)) {
|
||||
if (v !== undefined && v !== null) {
|
||||
url.searchParams.set(k, String(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
let payload = null;
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch {
|
||||
payload = { raw: await response.text() };
|
||||
}
|
||||
return { status: response.status, payload };
|
||||
}
|
||||
|
||||
function printResult(result) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
|
||||
function requiredArg(name, value) {
|
||||
if (!value) {
|
||||
throw new InputValidationError("Request validation failed", [
|
||||
{
|
||||
field: name,
|
||||
reason: "required argument missing or empty",
|
||||
expected: "non-empty",
|
||||
actualType: "missing",
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function getGeneratedCommand(commandName) {
|
||||
const resolved = ALIASES[commandName] || commandName;
|
||||
const cmd = COMMAND_MAP.get(resolved);
|
||||
if (!cmd) {
|
||||
throw new Error(`Unknown command: ${commandName}`);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function resolveCallInputs(commandName, positional, bodyOverride, queryOverride) {
|
||||
const generatedCmd = getGeneratedCommand(commandName);
|
||||
const body = { ...(generatedCmd.defaultBody || {}) };
|
||||
const query = { ...(generatedCmd.defaultQuery || {}) };
|
||||
|
||||
if (commandName === "browserCreate") {
|
||||
const name = positional[0] || `NodeAuto-${Date.now()}`;
|
||||
body.containerName = name;
|
||||
body.asDynamicType = 1;
|
||||
body.proxyTypeName = "不使用代理";
|
||||
} else if (commandName === "browserStart" || commandName === "browserForeground") {
|
||||
requiredArg("containerCode", positional[0]);
|
||||
body.containerCode = positional[0];
|
||||
} else if (commandName === "browserStop") {
|
||||
requiredArg("containerCode", positional[0]);
|
||||
body.containerCode = positional[0];
|
||||
query.containerCode = positional[0];
|
||||
} else if (commandName === "browserStatus") {
|
||||
if (positional[0]) {
|
||||
body.containerCode = [positional[0]];
|
||||
}
|
||||
} else if (commandName === "browserStopAll") {
|
||||
body.clearOpening = positional[0] ? positional[0] === "true" : false;
|
||||
} else if (commandName === "browserArrange" && positional[0]) {
|
||||
body.screenId = Number(positional[0]);
|
||||
}
|
||||
|
||||
const finalBody = bodyOverride && typeof bodyOverride === "object" ? bodyOverride : body;
|
||||
const finalQuery = queryOverride && typeof queryOverride === "object" ? queryOverride : query;
|
||||
return { generatedCmd, finalBody, finalQuery };
|
||||
}
|
||||
|
||||
function normalizeResult(command, generatedCmd, result, body, query) {
|
||||
return {
|
||||
command,
|
||||
method: generatedCmd.method,
|
||||
path: generatedCmd.path,
|
||||
status: result.status,
|
||||
code: result.payload && Object.prototype.hasOwnProperty.call(result.payload, "code") ? result.payload.code : null,
|
||||
msg: result.payload && Object.prototype.hasOwnProperty.call(result.payload, "msg") ? result.payload.msg : null,
|
||||
success:
|
||||
result.status < 400 &&
|
||||
(!result.payload || !Object.prototype.hasOwnProperty.call(result.payload, "code") || result.payload.code === 0),
|
||||
request: { body, query },
|
||||
payload: result.payload,
|
||||
};
|
||||
}
|
||||
|
||||
async function runTestAll() {
|
||||
const results = [];
|
||||
for (const cmd of COMMANDS) {
|
||||
try {
|
||||
const result = await post(cmd.path, cmd.defaultBody || {}, cmd.defaultQuery || {});
|
||||
results.push(normalizeResult(cmd.command, cmd, result, cmd.defaultBody || {}, cmd.defaultQuery || {}));
|
||||
} catch (err) {
|
||||
results.push({
|
||||
command: cmd.command,
|
||||
method: cmd.method,
|
||||
path: cmd.path,
|
||||
status: null,
|
||||
code: null,
|
||||
msg: err.message,
|
||||
success: false,
|
||||
request: { body: cmd.defaultBody || {}, query: cmd.defaultQuery || {} },
|
||||
payload: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const summary = {
|
||||
total: results.length,
|
||||
success: results.filter((x) => x.success).length,
|
||||
failed: results.filter((x) => !x.success).length,
|
||||
};
|
||||
const report = {
|
||||
baseUrl: BASE_URL,
|
||||
testedAt: new Date().toISOString(),
|
||||
summary,
|
||||
results,
|
||||
};
|
||||
|
||||
const reportPath = path.join(__dirname, "scripts", "node_test_all_report.json");
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
|
||||
return { summary, reportPath, report };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [, , command, ...rawArgs] = process.argv;
|
||||
|
||||
if (!command || command === "help" || command === "--help" || command === "-h") {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "list") {
|
||||
const items = COMMANDS.map((c) => ({
|
||||
command: c.command,
|
||||
method: c.method,
|
||||
path: c.path,
|
||||
summary: c.summary,
|
||||
}));
|
||||
printResult({ total: items.length, commands: items });
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "testAll") {
|
||||
const out = await runTestAll();
|
||||
printResult({
|
||||
summary: out.summary,
|
||||
reportPath: out.reportPath,
|
||||
});
|
||||
if (out.summary.failed > 0) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { positional, bodyOverride, queryOverride } = parseCommandArgs(rawArgs);
|
||||
const { generatedCmd, finalBody, finalQuery } = resolveCallInputs(
|
||||
command,
|
||||
positional,
|
||||
bodyOverride,
|
||||
queryOverride
|
||||
);
|
||||
validateCommandInputs(command, generatedCmd, finalBody, finalQuery);
|
||||
const result = await post(generatedCmd.path, finalBody, finalQuery);
|
||||
const normalized = normalizeResult(command, generatedCmd, result, finalBody, finalQuery);
|
||||
printResult(normalized);
|
||||
|
||||
if (normalized.status >= 400) {
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
if (normalized.code !== null && normalized.code !== 0) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
if (err instanceof InputValidationError) {
|
||||
const payload = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
command: err.command || null,
|
||||
fieldErrors: err.fieldErrors || [],
|
||||
},
|
||||
};
|
||||
console.error(JSON.stringify(payload, null, 2));
|
||||
process.exit(3);
|
||||
return;
|
||||
}
|
||||
console.error(err.message || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user