mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 23:47:26 +00:00
224 lines
5.4 KiB
JavaScript
224 lines
5.4 KiB
JavaScript
export function readEnvNumber(name, env = process.env) {
|
|
const raw = env[name]?.trim();
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
const parsed = Number.parseFloat(raw);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
export function readFlagValue(args, name) {
|
|
for (let index = 0; index < args.length; index += 1) {
|
|
const arg = args[index];
|
|
if (arg === name) {
|
|
return args[index + 1];
|
|
}
|
|
if (arg.startsWith(`${name}=`)) {
|
|
return arg.slice(name.length + 1);
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function consumeStringFlag(argv, index, flag) {
|
|
const inlineValue = readInlineFlagValue(argv[index], flag);
|
|
if (inlineValue !== null) {
|
|
if (!inlineValue) {
|
|
throw new Error(`${flag} requires a value`);
|
|
}
|
|
return {
|
|
nextIndex: index,
|
|
value: inlineValue,
|
|
};
|
|
}
|
|
if (argv[index] !== flag) {
|
|
return null;
|
|
}
|
|
const value = argv[index + 1];
|
|
if (!value || value.startsWith("--")) {
|
|
throw new Error(`${flag} requires a value`);
|
|
}
|
|
return {
|
|
nextIndex: index + 1,
|
|
value,
|
|
};
|
|
}
|
|
|
|
function consumeIntFlag(argv, index, flag, options = {}) {
|
|
const raw = readFlagOptionValue(argv, index, flag);
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
const parsed = parseIntegerFlagValue(raw.value, flag);
|
|
const min = options.min ?? Number.NEGATIVE_INFINITY;
|
|
if (parsed < min) {
|
|
throw new Error(`${flag} must be at least ${min}`);
|
|
}
|
|
return {
|
|
nextIndex: raw.nextIndex,
|
|
value: parsed,
|
|
};
|
|
}
|
|
|
|
function consumeFloatFlag(argv, index, flag, options = {}) {
|
|
const raw = readFlagOptionValue(argv, index, flag);
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
const parsed = parseFloatFlagValue(raw.value, flag);
|
|
const min = options.min ?? Number.NEGATIVE_INFINITY;
|
|
const includeMin = options.includeMin ?? true;
|
|
const isValid = Number.isFinite(parsed) && (includeMin ? parsed >= min : parsed > min);
|
|
if (!isValid) {
|
|
const comparator = includeMin ? "at least" : "greater than";
|
|
throw new Error(`${flag} must be ${comparator} ${min}`);
|
|
}
|
|
return {
|
|
nextIndex: raw.nextIndex,
|
|
value: parsed,
|
|
};
|
|
}
|
|
|
|
function readInlineFlagValue(arg, flag) {
|
|
const prefix = `${flag}=`;
|
|
return arg.startsWith(prefix) ? arg.slice(prefix.length) : null;
|
|
}
|
|
|
|
function readFlagOptionValue(argv, index, flag) {
|
|
const inlineValue = readInlineFlagValue(argv[index], flag);
|
|
if (inlineValue !== null) {
|
|
if (!inlineValue) {
|
|
throw new Error(`${flag} requires a value`);
|
|
}
|
|
return { nextIndex: index, value: inlineValue };
|
|
}
|
|
if (argv[index] !== flag) {
|
|
return null;
|
|
}
|
|
const value = argv[index + 1];
|
|
if (!value) {
|
|
throw new Error(`${flag} requires a value`);
|
|
}
|
|
return { nextIndex: index + 1, value };
|
|
}
|
|
|
|
function parseIntegerFlagValue(raw, flag) {
|
|
const text = String(raw).trim();
|
|
if (!/^-?\d+$/u.test(text)) {
|
|
throw new Error(`${flag} must be an integer`);
|
|
}
|
|
const parsed = Number(text);
|
|
if (!Number.isSafeInteger(parsed)) {
|
|
throw new Error(`${flag} must be a safe integer`);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function parseFloatFlagValue(raw, flag) {
|
|
const text = String(raw).trim();
|
|
if (!/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(text)) {
|
|
throw new Error(`${flag} must be a number`);
|
|
}
|
|
const parsed = Number(text);
|
|
if (!Number.isFinite(parsed)) {
|
|
throw new Error(`${flag} must be a finite number`);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
export function stringFlag(flag, key) {
|
|
return {
|
|
consume(argv, index) {
|
|
const option = consumeStringFlag(argv, index, flag);
|
|
if (!option) {
|
|
return null;
|
|
}
|
|
return {
|
|
nextIndex: option.nextIndex,
|
|
apply(target) {
|
|
target[key] = option.value;
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
function createAssignedValueFlag(consumeOption) {
|
|
return {
|
|
consume(argv, index, args) {
|
|
const option = consumeOption(argv, index, args);
|
|
if (!option) {
|
|
return null;
|
|
}
|
|
return {
|
|
nextIndex: option.nextIndex,
|
|
apply(target) {
|
|
target[option.key] = option.value;
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
export function intFlag(flag, key, options) {
|
|
return createAssignedValueFlag((argv, index) => {
|
|
const option = consumeIntFlag(argv, index, flag, options);
|
|
return option ? { ...option, key } : null;
|
|
});
|
|
}
|
|
|
|
export function floatFlag(flag, key, options) {
|
|
return createAssignedValueFlag((argv, index) => {
|
|
const option = consumeFloatFlag(argv, index, flag, options);
|
|
return option ? { ...option, key } : null;
|
|
});
|
|
}
|
|
|
|
export function booleanFlag(flag, key, value = true) {
|
|
return {
|
|
consume(argv, index) {
|
|
if (argv[index] !== flag) {
|
|
return null;
|
|
}
|
|
return {
|
|
nextIndex: index,
|
|
apply(target) {
|
|
target[key] = value;
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
export function parseFlagArgs(argv, args, specs, options = {}) {
|
|
const ignoreDoubleDash = options.ignoreDoubleDash ?? true;
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
const arg = argv[i];
|
|
if (arg === "--" && ignoreDoubleDash) {
|
|
continue;
|
|
}
|
|
let handled = false;
|
|
for (const spec of specs) {
|
|
const option = spec.consume(argv, i, args);
|
|
if (!option) {
|
|
continue;
|
|
}
|
|
option.apply(args);
|
|
i = option.nextIndex;
|
|
handled = true;
|
|
break;
|
|
}
|
|
if (handled) {
|
|
continue;
|
|
}
|
|
const fallbackResult = options.onUnhandledArg?.(arg, args);
|
|
if (fallbackResult === "handled") {
|
|
continue;
|
|
}
|
|
if (!options.allowUnknownOptions && arg.startsWith("-")) {
|
|
throw new Error(`Unknown option: ${arg}`);
|
|
}
|
|
}
|
|
return args;
|
|
}
|