Files
fox-cli/src/command.ts
T
chenyunda218 80b784a9f3 feat(cli): 實作 CLI 各指令與 help(M1+M2,issue #13)
- 加入 pnpm workspace;Node.js ≥22 + TypeScript 實作
- 指令:auth status、tokens verify/create/list/revoke、novels get
- 非互動、stdout 僅輸出單一 JSON;exit code 0/1/2
- 設定優先順序:--token/--api-url > FOX_API_TOKEN/FOX_API_URL > ~/.config/fox/cli.json
- novels list/create/update 待後端接受 Bearer token 後補齊(README 授權缺口)
- 已對模擬後端完成端到端驗證(含 401/404/網路錯誤、冪等 revoke)
2026-09-04 20:26:11 +08:00

90 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 命令定義:名稱、說明、參數與執行。供 help 與分發使用。 */
export interface ParsedArgs {
/** 位置參數(子命令後的非選項值)。 */
positionals: string[];
/** 選項旗標(--key value 或 --flag)。 */
flags: Record<string, string | boolean>;
}
export interface CommandContext {
args: ParsedArgs;
/** 輸出 JSON 到 stdout(唯一 stdout 內容)。 */
print: (value: unknown) => void;
}
export interface Command {
/** 子命令路徑,例如 ["tokens", "verify"]。 */
path: string[];
summary: string;
usage: string;
/** 位置參數說明,例如 ["<slug>"]。與 usage 一致即可。 */
run: (ctx: CommandContext) => Promise<void> | void;
}
/** 解析 argv:--key value、--key=value、--flag(布林)、-- 結束選項。 */
export function parseArgs(argv: string[]): ParsedArgs {
const positionals: string[] = [];
const flags: Record<string, string | boolean> = {};
let i = 0;
let noMoreFlags = false;
while (i < argv.length) {
const arg = argv[i++]!;
if (!noMoreFlags && arg === "--") {
noMoreFlags = true;
continue;
}
if (!noMoreFlags && arg.startsWith("--")) {
const eq = arg.indexOf("=");
if (eq !== -1) {
const key = arg.slice(2, eq);
const value = arg.slice(eq + 1);
flags[key] = value;
} else {
const key = arg.slice(2);
const next = argv[i];
if (next !== undefined && !next.startsWith("--")) {
flags[key] = next;
i += 1;
} else {
flags[key] = true;
}
}
continue;
}
positionals.push(arg);
}
return { positionals, flags };
}
export function flagString(flags: Record<string, string | boolean>, key: string): string | undefined {
const v = flags[key];
return typeof v === "string" ? v : undefined;
}
/** 註冊表:以完整路徑字串("tokens verify")索引。 */
export class Registry {
private readonly commands = new Map<string, Command>();
add(command: Command): void {
this.commands.set(command.path.join(" "), command);
}
/** 找出命令;prefix 可指向群組(回傳其子命令列表供 help)。 */
find(prefix: string[]): Command | undefined {
return this.commands.get(prefix.join(" "));
}
/** 列出 prefix 下的下一層子命令(供群組 help)。 */
children(prefix: string[]): Command[] {
return Array.from(this.commands.values()).filter((c) => {
if (c.path.length !== prefix.length + 1) return false;
return prefix.every((p, idx) => c.path[idx] === p);
});
}
all(): Command[] {
return Array.from(this.commands.values());
}
}