feat: move web-probe to top-level cli (#933)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-26 09:57:14 +08:00
committed by GitHub
parent 0328967e58
commit 241a2d511f
18 changed files with 249 additions and 274 deletions
+35 -35
View File
@@ -172,7 +172,7 @@ export function runWebProbeSentinelCommand(spec: HwlabRuntimeLaneSpec, options:
}
function runSentinelImage(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "image" }>): RenderedCliResult {
const command = `hwlab nodes web-probe sentinel image ${options.action}`;
const command = `web-probe sentinel image ${options.action}`;
if (options.action === "build" && options.confirm) {
if (!options.wait) return renderAsyncSentinelJob(state, "image", "build", options.timeoutSeconds);
return runSentinelImageBuildConfirmed(state, options);
@@ -192,10 +192,10 @@ function runSentinelImage(state: SentinelCicdState, options: Extract<WebProbeSen
registry,
blocker: null,
next: {
status: `bun scripts/cli.ts hwlab nodes web-probe sentinel image status --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
dryRun: `bun scripts/cli.ts hwlab nodes web-probe sentinel image build --node ${state.spec.nodeId} --lane ${state.spec.lane} --dry-run`,
confirm: `bun scripts/cli.ts hwlab nodes web-probe sentinel image build --node ${state.spec.nodeId} --lane ${state.spec.lane} --confirm`,
controlPlanePlan: `bun scripts/cli.ts hwlab nodes web-probe sentinel control-plane plan --node ${state.spec.nodeId} --lane ${state.spec.lane} --dry-run`,
status: `bun scripts/cli.ts web-probe sentinel image status --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
dryRun: `bun scripts/cli.ts web-probe sentinel image build --node ${state.spec.nodeId} --lane ${state.spec.lane} --dry-run`,
confirm: `bun scripts/cli.ts web-probe sentinel image build --node ${state.spec.nodeId} --lane ${state.spec.lane} --confirm`,
controlPlanePlan: `bun scripts/cli.ts web-probe sentinel control-plane plan --node ${state.spec.nodeId} --lane ${state.spec.lane} --dry-run`,
},
valuesRedacted: true,
};
@@ -203,7 +203,7 @@ function runSentinelImage(state: SentinelCicdState, options: Extract<WebProbeSen
}
function runSentinelControlPlane(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "control-plane" }>): RenderedCliResult {
const command = `hwlab nodes web-probe sentinel control-plane ${options.action}`;
const command = `web-probe sentinel control-plane ${options.action}`;
const mutationAction = options.action === "apply" || options.action === "trigger-current";
if (options.confirm && mutationAction) {
if (!options.wait) return renderAsyncSentinelJob(state, "control-plane", options.action, options.timeoutSeconds);
@@ -512,7 +512,7 @@ function probeImageRegistry(state: SentinelCicdState, timeoutSeconds: number): R
}
function runSentinelImageBuildConfirmed(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "image" }>): RenderedCliResult {
const command = "hwlab nodes web-probe sentinel image build";
const command = "web-probe sentinel image build";
const publish = runSentinelPublishJob(state, false, options.timeoutSeconds);
const registry = probeImageRegistry(state, options.timeoutSeconds);
const registryReady = record(registry.probe).present === true;
@@ -531,8 +531,8 @@ function runSentinelImageBuildConfirmed(state: SentinelCicdState, options: Extra
warnings: sentinelElapsedWarnings(record(publish).elapsedMs),
blocker: null,
next: {
status: `bun scripts/cli.ts hwlab nodes web-probe sentinel image status --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
controlPlaneTrigger: `bun scripts/cli.ts hwlab nodes web-probe sentinel control-plane trigger-current --node ${state.spec.nodeId} --lane ${state.spec.lane} --confirm`,
status: `bun scripts/cli.ts web-probe sentinel image status --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
controlPlaneTrigger: `bun scripts/cli.ts web-probe sentinel control-plane trigger-current --node ${state.spec.nodeId} --lane ${state.spec.lane} --confirm`,
},
valuesRedacted: true,
};
@@ -540,7 +540,7 @@ function runSentinelImageBuildConfirmed(state: SentinelCicdState, options: Extra
}
function runSentinelControlPlaneConfirmed(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "control-plane" }>): RenderedCliResult {
const command = `hwlab nodes web-probe sentinel control-plane ${options.action}`;
const command = `web-probe sentinel control-plane ${options.action}`;
const applyOnly = options.action === "apply";
const publish = applyOnly ? null : runSentinelPublishJob(state, true, options.timeoutSeconds);
const flush = !applyOnly && record(publish).ok === true
@@ -627,10 +627,10 @@ function runSentinelControlPlaneConfirmed(state: SentinelCicdState, options: Ext
function renderAsyncSentinelJob(state: SentinelCicdState, domain: "image" | "control-plane", action: string, timeoutSeconds: number): RenderedCliResult {
const args = domain === "image"
? ["hwlab", "nodes", "web-probe", "sentinel", "image", action, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)]
: ["hwlab", "nodes", "web-probe", "sentinel", "control-plane", action, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)];
? ["web-probe", "sentinel", "image", action, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)]
: ["web-probe", "sentinel", "control-plane", action, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)];
const job = startJob(`hwlab_nodes_${state.spec.lane}_web_probe_sentinel_${domain}_${action}`, ["bun", "scripts/cli.ts", ...args], `Run HWLAB ${state.spec.lane} web-probe sentinel ${domain} ${action} for node ${state.spec.nodeId}`);
const command = `hwlab nodes web-probe sentinel ${domain} ${action}`;
const command = `web-probe sentinel ${domain} ${action}`;
const result = {
ok: true,
command,
@@ -1121,17 +1121,17 @@ function controlPlaneNext(state: SentinelCicdState, action: WebProbeSentinelCont
const node = state.spec.nodeId;
const lane = state.spec.lane;
return {
plan: `bun scripts/cli.ts hwlab nodes web-probe sentinel control-plane plan --node ${node} --lane ${lane} --dry-run`,
status: `bun scripts/cli.ts hwlab nodes web-probe sentinel control-plane status --node ${node} --lane ${lane}`,
image: `bun scripts/cli.ts hwlab nodes web-probe sentinel image status --node ${node} --lane ${lane}`,
triggerCurrent: `bun scripts/cli.ts hwlab nodes web-probe sentinel control-plane trigger-current --node ${node} --lane ${lane} --dry-run`,
plan: `bun scripts/cli.ts web-probe sentinel control-plane plan --node ${node} --lane ${lane} --dry-run`,
status: `bun scripts/cli.ts web-probe sentinel control-plane status --node ${node} --lane ${lane}`,
image: `bun scripts/cli.ts web-probe sentinel image status --node ${node} --lane ${lane}`,
triggerCurrent: `bun scripts/cli.ts web-probe sentinel control-plane trigger-current --node ${node} --lane ${lane} --dry-run`,
issue: "https://github.com/pikasTech/unidesk/issues/889",
currentAction: action,
};
}
function runSentinelMaintenance(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "maintenance" }>): RenderedCliResult {
const command = `hwlab nodes web-probe sentinel maintenance ${options.action}`;
const command = `web-probe sentinel maintenance ${options.action}`;
const serviceHealth = callSentinelService(state, "GET", "/api/health", null, options.timeoutSeconds);
if (options.action === "status") {
const maintenance = callSentinelService(state, "GET", "/api/maintenance", null, options.timeoutSeconds);
@@ -1206,7 +1206,7 @@ function runSentinelMaintenance(state: SentinelCicdState, options: Extract<WebPr
}
function runSentinelValidate(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "validate" }>): RenderedCliResult {
const command = "hwlab nodes web-probe sentinel validate";
const command = "web-probe sentinel validate";
const initialHealth = callSentinelService(state, "GET", "/api/health", null, options.timeoutSeconds);
let quickVerify: Record<string, unknown> | null = null;
if (options.quickVerify) {
@@ -1271,7 +1271,7 @@ function runSentinelValidate(state: SentinelCicdState, options: Extract<WebProbe
}
function runSentinelReport(state: SentinelCicdState, options: Extract<WebProbeSentinelOptions, { kind: "report" }>): RenderedCliResult {
const command = `hwlab nodes web-probe sentinel report ${options.latest ? "--latest " : ""}--view ${options.view}`;
const command = `web-probe sentinel report ${options.latest ? "--latest " : ""}--view ${options.view}`;
const query = new URLSearchParams({ view: options.view });
if (options.runId !== null) query.set("run", options.runId);
if (options.traceId !== null) query.set("traceId", options.traceId);
@@ -1284,12 +1284,12 @@ function runSentinelReport(state: SentinelCicdState, options: Extract<WebProbeSe
}
function renderAsyncP5Job(state: SentinelCicdState, subcommand: readonly string[], timeoutSeconds: number, releaseId: string | null, reason: string | null, quickVerify: boolean): RenderedCliResult {
const args = ["hwlab", "nodes", "web-probe", "sentinel", ...subcommand, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)];
const args = ["web-probe", "sentinel", ...subcommand, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--confirm", "--wait", "--timeout-seconds", String(timeoutSeconds)];
if (releaseId !== null) args.push("--release-id", releaseId);
if (reason !== null) args.push("--reason", reason);
if (quickVerify) args.push("--quick-verify");
const job = startJob(`hwlab_nodes_${state.spec.lane}_web_probe_sentinel_${subcommand.join("_")}`, ["bun", "scripts/cli.ts", ...args], `Run HWLAB ${state.spec.lane} web-probe sentinel ${subcommand.join(" ")} for node ${state.spec.nodeId}`);
const command = `hwlab nodes web-probe sentinel ${subcommand.join(" ")}`;
const command = `web-probe sentinel ${subcommand.join(" ")}`;
return rendered(true, command, renderAsyncJobResult({
ok: true,
command,
@@ -1318,7 +1318,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
const runId = `sentinel-run-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
const steps: Record<string, unknown>[] = [];
const startArgs = [
"hwlab", "nodes", "web-probe", "observe", "start",
"web-probe", "observe", "start",
"--node", state.spec.nodeId,
"--lane", state.spec.lane,
"--target-path", stringAt(scenario, "observeTargetPath"),
@@ -1360,7 +1360,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
promptSource: prompts.summary,
}));
}
const args = ["hwlab", "nodes", "web-probe", "observe", "command", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--type", type, "--wait-ms", "55000", "--command-timeout-seconds", String(remainingSeconds(deadline, 55))];
const args = ["web-probe", "observe", "command", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--type", type, "--wait-ms", "55000", "--command-timeout-seconds", String(remainingSeconds(deadline, 55))];
if (type === "selectProvider") args.push("--provider", stringAt(item, "provider"));
if (type === "sendPrompt") {
args.push("--text", prompts.prompts[promptIndex % prompts.prompts.length] ?? "");
@@ -1399,7 +1399,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
}
}
}
const analysis = runChildCli(["hwlab", "nodes", "web-probe", "observe", "analyze", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--command-timeout-seconds", String(remainingSeconds(deadline, 120))], remainingSeconds(deadline, 120));
const analysis = runChildCli(["web-probe", "observe", "analyze", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--command-timeout-seconds", String(remainingSeconds(deadline, 120))], remainingSeconds(deadline, 120));
steps.push({ phase: "observe-analyze", ok: analysis.ok, result: analysis.result });
const indexEntry = readLocalObserveIndex(observerId);
const artifactSummary = indexEntry === null ? { ok: false, reason: "observe-index-entry-missing", observerId, valuesRedacted: true } : readAnalysisSummaryFromWorkspace(state, indexEntry.stateDir, remainingSeconds(deadline, 30));
@@ -1446,7 +1446,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
const cleanupSteps: Record<string, unknown>[] = [];
if (input.promptIndex > 0) {
const cancel = runChildCli([
"hwlab", "nodes", "web-probe", "observe", "command", input.observerId,
"web-probe", "observe", "command", input.observerId,
"--node", state.spec.nodeId,
"--lane", state.spec.lane,
"--type", "cancel",
@@ -1456,7 +1456,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
cleanupSteps.push({ phase: "observe-cancel-after-failure", ok: cancel.ok, result: cancel.result });
}
const stop = runChildCli([
"hwlab", "nodes", "web-probe", "observe", "stop", input.observerId,
"web-probe", "observe", "stop", input.observerId,
"--node", state.spec.nodeId,
"--lane", state.spec.lane,
"--force",
@@ -1464,7 +1464,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
], 30);
cleanupSteps.push({ phase: "observe-stop-after-failure", ok: stop.ok, result: stop.result });
const analysis = runChildCli([
"hwlab", "nodes", "web-probe", "observe", "analyze", input.observerId,
"web-probe", "observe", "analyze", input.observerId,
"--node", state.spec.nodeId,
"--lane", state.spec.lane,
"--command-timeout-seconds", "55",
@@ -1815,7 +1815,7 @@ function readAnalysisSummaryFromWorkspace(state: SentinelCicdState, stateDir: st
}
function collectObserveView(state: SentinelCicdState, observerId: string, view: "turn-summary" | "trace-frame", turn: number | null, timeoutSeconds: number): Record<string, unknown> {
const args = ["hwlab", "nodes", "web-probe", "observe", "collect", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--view", view, "--command-timeout-seconds", String(Math.max(5, Math.min(timeoutSeconds, 55))), "--raw", "--compact-raw"];
const args = ["web-probe", "observe", "collect", observerId, "--node", state.spec.nodeId, "--lane", state.spec.lane, "--view", view, "--command-timeout-seconds", String(Math.max(5, Math.min(timeoutSeconds, 55))), "--raw", "--compact-raw"];
if (turn !== null) args.push("--turn", String(turn));
const result = runChildCli(args, timeoutSeconds);
const payload = cliDataPayload(result.parsed);
@@ -2124,7 +2124,7 @@ function serviceUnavailableBlocker(state: SentinelCicdState): Record<string, unk
code: "sentinel-service-unavailable",
policy: stringAt(state.cicd, "targetValidation.serviceUnavailablePolicy"),
reason: "sentinel service must be reachable through k3s internal Service DNS before quick verify can run; no public/fallback path is used.",
retry: `bun scripts/cli.ts hwlab nodes web-probe sentinel validate --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
retry: `bun scripts/cli.ts web-probe sentinel validate --node ${state.spec.nodeId} --lane ${state.spec.lane}`,
valuesRedacted: true,
};
}
@@ -2133,11 +2133,11 @@ function sentinelP5Next(state: SentinelCicdState): Record<string, string> {
const node = state.spec.nodeId;
const lane = state.spec.lane;
return {
validate: `bun scripts/cli.ts hwlab nodes web-probe sentinel validate --node ${node} --lane ${lane}`,
quickVerify: `bun scripts/cli.ts hwlab nodes web-probe sentinel validate --node ${node} --lane ${lane} --quick-verify --confirm --wait`,
maintenanceStart: `bun scripts/cli.ts hwlab nodes web-probe sentinel maintenance start --node ${node} --lane ${lane} --confirm --wait`,
maintenanceStop: `bun scripts/cli.ts hwlab nodes web-probe sentinel maintenance stop --node ${node} --lane ${lane} --confirm --wait`,
report: `bun scripts/cli.ts hwlab nodes web-probe sentinel report --node ${node} --lane ${lane} --view summary`,
validate: `bun scripts/cli.ts web-probe sentinel validate --node ${node} --lane ${lane}`,
quickVerify: `bun scripts/cli.ts web-probe sentinel validate --node ${node} --lane ${lane} --quick-verify --confirm --wait`,
maintenanceStart: `bun scripts/cli.ts web-probe sentinel maintenance start --node ${node} --lane ${lane} --confirm --wait`,
maintenanceStop: `bun scripts/cli.ts web-probe sentinel maintenance stop --node ${node} --lane ${lane} --confirm --wait`,
report: `bun scripts/cli.ts web-probe sentinel report --node ${node} --lane ${lane} --view summary`,
};
}