diff --git a/cli/tjwater-cli b/cli/tjwater-cli index c9fe518..1806d41 100755 --- a/cli/tjwater-cli +++ b/cli/tjwater-cli @@ -180,6 +180,18 @@ async function main() { return 0; } + if (isLocalHelpCommand(parsed.command)) { + const result = await dispatch(parsed, null); + emitSuccess({ + summary: result.summary, + data: result.local, + ctx: null, + durationMs: 0, + nextCommands: result.nextCommands, + }); + return 0; + } + const ctx = await buildContext(parsed.global); const startedAt = Date.now(); const result = await dispatch(parsed, ctx); @@ -194,14 +206,27 @@ async function main() { return 0; } +function isLocalHelpCommand(command) { + if (command.length === 1 && GROUPS[command[0]]) return true; + return command.length > 1 && command.at(-1) === "help"; +} + async function dispatch(parsed, ctx) { const [group, ...rest] = parsed.command; if (group === "network") { + if (rest.length === 0 || rest[0] === "help") return helpResult(["network"], NETWORK_COMMANDS); return commandFromMap(NETWORK_COMMANDS, rest, parsed, ctx); } if (group === "component" && rest[0] === "option") { + if (rest.length === 1 || rest[1] === "help") return helpResult(["component", "option"], { + schema: { summary: "读取选项 schema" }, + get: { summary: "读取选项属性" }, + }); return componentOption(rest.slice(1), parsed, ctx); } + if (group === "component" && (rest.length === 0 || rest[0] === "help")) { + return helpResult(["component"], { option: { summary: GROUPS.component } }); + } if (group === "simulation" && rest[0] === "run") { requireOptions(parsed.args, { "start-time": { required: true }, duration: { required: true } }); const start = parseTime(parsed.args["start-time"], "--start-time"); @@ -218,10 +243,24 @@ async function dispatch(parsed, ctx) { ], }; } + if (group === "simulation" && (rest.length === 0 || rest[0] === "help")) { + return helpResult(["simulation"], { run: { summary: "触发指定绝对时间的模拟运行" } }); + } if (group === "analysis") { + if (rest.length === 0 || rest[0] === "help") return helpResult(["analysis"], { + age: { summary: "执行水龄分析" }, + leakage: { summary: "漏损分析相关命令" }, + "burst-detection": { summary: "爆管检测相关命令" }, + "sensor-placement": { summary: "传感器选址相关命令" }, + }); return analysis(rest, parsed, ctx); } if (group === "data") { + if (rest.length === 0 || rest[0] === "help") return helpResult(["data"], { + timeseries: { summary: "时序数据查询命令" }, + scada: { summary: "SCADA 元数据查询命令" }, + scheme: { summary: "方案数据查询命令" }, + }); return dataCommand(rest, parsed, ctx); } throw cliError("未找到命令", "COMMAND_NOT_FOUND", `unknown command: ${parsed.command.join(" ")}`, 2, { @@ -229,6 +268,19 @@ async function dispatch(parsed, ctx) { }); } +function helpResult(command, commands) { + return { + summary: `命令帮助:${command.join(" ")}`, + local: { + command: command.join(" "), + commands: Object.fromEntries( + Object.entries(commands).map(([name, spec]) => [name, spec.summary ?? ""]), + ), + examples: examplesFor(command), + }, + }; +} + function commandFromMap(map, commandPath, parsed, ctx) { const name = commandPath[0]; const spec = map[name]; @@ -383,6 +435,9 @@ function dataCommand(rest, parsed, ctx) { } async function requestJson(ctx, request) { + if (request.local !== undefined) { + return request.local; + } if (request.requireProject) { requireProject(ctx); } @@ -557,15 +612,45 @@ function emitSuccess({ summary, data, ctx, durationMs, nextCommands }) { schema_version: SCHEMA_VERSION, summary, data, - metadata: { - server: ctx.server, - request_id: ctx.requestId, - duration_ms: durationMs, - }, + metadata: ctx + ? { + server: ctx.server, + request_id: ctx.requestId, + duration_ms: durationMs, + } + : undefined, next_commands: nextCommands, })); } +function examplesFor(command) { + const key = command.join(" "); + const examples = { + network: [ + "tjwater-cli network get-all-pipes-properties", + "tjwater-cli network get-pipe-properties --pipe P1", + ], + component: ["tjwater-cli component option help"], + "component option": [ + "tjwater-cli component option schema --kind time", + "tjwater-cli component option get --kind network", + ], + simulation: [ + "tjwater-cli simulation run --start-time 2025-01-01T00:00:00+08:00 --duration 30", + ], + analysis: [ + "tjwater-cli analysis age --start-time 2025-01-01T00:00:00+08:00 --duration 900", + "tjwater-cli analysis leakage schemes list", + ], + data: [ + "tjwater-cli data timeseries realtime links --start-time 2025-01-01T00:00:00+08:00 --end-time 2025-01-01T01:00:00+08:00", + "tjwater-cli data scada list", + "tjwater-cli data scheme list", + ], + }; + return examples[key] ?? ["tjwater-cli help"]; +} + function emitFailure(error) { const payload = { ok: false,