chore: import upstream snapshot with attribution
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
Continuous Deployment / Deploy to Production (push) Has been cancelled
Continuous Deployment / Rollback Deployment (push) Has been cancelled
Continuous Deployment / Post-deployment Monitoring (push) Has been cancelled
Continuous Deployment / Notify Deployment Status (push) Has been cancelled
Continuous Deployment / Deploy to Staging (push) Has been cancelled
Security Scanning / Security Report (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 11:59:54 +08:00
commit 9740bc64c9
2636 changed files with 1330642 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
/**
* Subprocess wrapper for Cognitum Cog binaries (CLI variant).
* Mirrors tools/ruview-mcp/src/cog.ts.
*/
import { spawn } from "node:child_process";
export type Result<T> = { ok: true; data: T } | { ok: false; error: string };
const COG_TIMEOUT_MS = 15_000;
export async function runCog(binary: string, args: string[]): Promise<Result<string>> {
return new Promise((resolve) => {
let stdout = "";
let stderr = "";
const child = spawn(binary, args, {
timeout: COG_TIMEOUT_MS,
stdio: ["ignore", "pipe", "pipe"],
});
child.stdout?.on("data", (chunk: Buffer) => { stdout += chunk.toString(); });
child.stderr?.on("data", (chunk: Buffer) => { stderr += chunk.toString(); });
child.on("error", (e) => {
resolve(err(
`Failed to launch "${binary}" (${args.join(" ")}): ${e.message}. ` +
`Set RUVIEW_POSE_COG_BINARY / RUVIEW_COUNT_COG_BINARY or install the cog.`
));
});
child.on("close", (code) => {
if (code !== 0) {
resolve(err(`Cog "${binary} ${args.join(" ")}" exited with code ${code}. stderr: ${stderr.trim() || "(empty)"}`));
} else {
resolve({ ok: true, data: stdout });
}
});
});
}
function err(error: string): { ok: false; error: string } {
return { ok: false, error };
}
+88
View File
@@ -0,0 +1,88 @@
/**
* ruview cogs — Cognitum edge module registry commands.
*
* cogs list — list cogs from the registry (via sensing-server ADR-102 proxy).
*/
import type { Argv } from "yargs";
import { sensingGet } from "../http.js";
import { loadConfig } from "../config.js";
export function cogsCommand(cli: Argv): void {
cli.command(
"cogs <action>",
"Edge module registry commands",
(y) =>
y
.positional("action", {
choices: ["list"] as const,
description: "Action to perform",
})
.option("category", {
type: "string",
description:
"Filter by category: health, security, building, retail, industrial, " +
"research, ai, swarm, signal, network, developer",
})
.option("search", {
type: "string",
description: "Search substring matched against cog id and name (case-insensitive)",
})
.option("refresh", {
type: "boolean",
default: false,
description: "Bypass the 1-hour registry cache",
})
.option("url", {
type: "string",
description: "Override the sensing-server URL",
}),
async (args) => {
const config = loadConfig();
const baseUrl = (args["url"] as string | undefined) ?? config.sensingServerUrl;
if (args.action === "list") {
const qs = args.refresh ? "?refresh=1" : "";
const result = await sensingGet<{
registry?: { cogs?: object[]; apps?: object[] };
}>(baseUrl, `/api/v1/edge/registry${qs}`, config.apiToken);
if (!result.ok) {
process.stderr.write(`[WARN] ${result.error}\n`);
process.stdout.write(
JSON.stringify({ ok: false, warn: true, error: result.error }) + "\n"
);
process.exit(0);
}
const payload = result.data;
let cogs: object[] =
payload.registry?.cogs ?? payload.registry?.apps ?? [];
if (args.category) {
const cat = (args.category as string).toLowerCase();
cogs = cogs.filter(
(c) =>
(c as Record<string, unknown>)["category"]
?.toString()
.toLowerCase() === cat
);
}
if (args.search) {
const q = (args.search as string).toLowerCase();
cogs = cogs.filter((c) => {
const rec = c as Record<string, unknown>;
return (
rec["id"]?.toString().toLowerCase().includes(q) ||
rec["name"]?.toString().toLowerCase().includes(q)
);
});
}
process.stdout.write(
JSON.stringify({ ok: true, total: cogs.length, cogs }, null, 2) + "\n"
);
}
}
);
}
+100
View File
@@ -0,0 +1,100 @@
/**
* ruview count — Person count commands.
*
* count infer — run single-shot person-count inference.
*/
import type { Argv } from "yargs";
import { runCog } from "../cog.js";
import { loadConfig } from "../config.js";
export function countCommand(cli: Argv): void {
cli.command(
"count <action>",
"Person count commands",
(y) =>
y
.positional("action", {
choices: ["infer"] as const,
description: "Action to perform",
})
.option("window", {
type: "string",
description: "Path to a CSI window JSON file (omit to use live sensing-server)",
})
.option("binary", {
type: "string",
description: "Path to cog-person-count binary (default: RUVIEW_COUNT_COG_BINARY)",
})
.option("max-persons", {
type: "number",
default: 7,
description: "Upper bound on person count (17, default: 7)",
}),
async (args) => {
const config = loadConfig();
const binary = (args["binary"] as string | undefined) ?? config.countCogBinary;
if (args.action === "infer") {
const t0 = Date.now();
const health = await runCog(binary, ["health"]);
const latencyMs = Date.now() - t0;
if (!health.ok) {
process.stderr.write(
`[WARN] Cog health check failed: ${health.error}\n` +
`Set RUVIEW_COUNT_COG_BINARY or install cog-person-count (ADR-103).\n`
);
process.stdout.write(
JSON.stringify({
ok: false,
warn: true,
error: health.error,
result: { count: 0, confidence: 0, count_p95_low: 0, count_p95_high: 0, backend: "unavailable", latency_ms: 0 },
}) + "\n"
);
process.exit(0);
}
let backend = "unknown";
let count = 0;
let confidence = 0;
let p95Low = 0;
let p95High = 0;
for (const line of health.data.split("\n")) {
try {
const ev = JSON.parse(line.trim()) as Record<string, unknown>;
if (ev["event"] === "health.ok") {
const fields = ev["fields"] as Record<string, unknown>;
backend = String(fields["backend"] ?? "unknown");
count = Number(fields["synthetic_count"] ?? 0);
confidence = Number(fields["synthetic_confidence"] ?? 0);
const p95 = fields["synthetic_p95_range"] as number[];
p95Low = p95?.[0] ?? 0;
p95High = p95?.[1] ?? 0;
break;
}
} catch { /* skip */ }
}
process.stdout.write(
JSON.stringify({
ok: true,
synthetic_window: true,
note: "M2: real inference on synthetic CSI window via cog health check.",
result: {
ts: Date.now() / 1000,
count,
confidence,
count_p95_low: p95Low,
count_p95_high: p95High,
backend,
latency_ms: latencyMs,
},
}) + "\n"
);
}
}
);
}
+64
View File
@@ -0,0 +1,64 @@
/**
* ruview csi — CSI frame commands.
*
* csi tail — stream live CSI frames from the sensing-server.
*/
import type { Argv } from "yargs";
import { sensingGet } from "../http.js";
import { loadConfig } from "../config.js";
export function csiCommand(cli: Argv): void {
cli.command(
"csi <action>",
"CSI frame commands",
(y) =>
y
.positional("action", {
choices: ["tail"] as const,
description: "Action to perform",
})
.option("url", {
type: "string",
description:
"Sensing-server URL (default: RUVIEW_SENSING_SERVER_URL or http://localhost:3000)",
})
.option("interval", {
type: "number",
default: 500,
description: "Polling interval in milliseconds (default: 500)",
}),
async (args) => {
const config = loadConfig();
const baseUrl = (args["url"] as string | undefined) ?? config.sensingServerUrl;
if (args.action === "tail") {
process.stderr.write(
`[ruview csi tail] Streaming from ${baseUrl} every ${args.interval}ms. Ctrl-C to stop.\n`
);
// Streaming poll loop.
// eslint-disable-next-line no-constant-condition
while (true) {
const result = await sensingGet<object>(
baseUrl,
"/api/v1/sensing/latest",
config.apiToken
);
if (!result.ok) {
process.stderr.write(
`[WARN] ${result.error} — retrying in ${args.interval}ms\n`
);
} else {
process.stdout.write(JSON.stringify(result.data) + "\n");
}
await new Promise<void>((resolve) =>
setTimeout(resolve, args.interval as number)
);
}
}
}
);
}
+73
View File
@@ -0,0 +1,73 @@
/**
* ruview job — Job management commands.
*
* job status --id <job_id> — poll a background training job.
*/
import type { Argv } from "yargs";
import { readFileSync, existsSync } from "node:fs";
import { loadConfig } from "../config.js";
export function jobCommand(cli: Argv): void {
cli.command(
"job <action>",
"Job management commands",
(y) =>
y
.positional("action", {
choices: ["status"] as const,
description: "Action to perform",
})
.option("id", {
type: "string",
demandOption: true,
description: "Job ID returned by ruview train count",
}),
async (args) => {
const config = loadConfig();
if (args.action === "status") {
const jobId = args.id as string;
const { default: path } = await import("node:path");
const logPath = path.join(config.jobsDir, `${jobId}.log`);
if (!existsSync(logPath)) {
process.stdout.write(
JSON.stringify({
ok: false,
error: `Job ${jobId} not found at ${logPath}. ` +
"The CLI process that started the job may have been restarted.",
}) + "\n"
);
process.exit(0);
}
const content = readFileSync(logPath, "utf8");
const lines = content.split("\n");
const recentLog = lines.slice(Math.max(0, lines.length - 20));
// Derive status from the log content.
let status: string = "running";
if (content.includes("# exit code: 0")) {
status = "done";
} else if (content.includes("# exit code:") || content.includes("# ERROR:")) {
status = "failed";
}
process.stdout.write(
JSON.stringify(
{
ok: true,
job_id: jobId,
status,
log_path: logPath,
recent_log: recentLog,
},
null,
2
) + "\n"
);
}
}
);
}
+86
View File
@@ -0,0 +1,86 @@
/**
* ruview pose — Pose estimation commands.
*
* pose infer — run single-shot 17-keypoint inference.
*/
import type { Argv } from "yargs";
import { runCog } from "../cog.js";
import { loadConfig } from "../config.js";
export function poseCommand(cli: Argv): void {
cli.command(
"pose <action>",
"Pose estimation commands",
(y) =>
y
.positional("action", {
choices: ["infer"] as const,
description: "Action to perform",
})
.option("window", {
type: "string",
description: "Path to a CSI window JSON file (omit to use live sensing-server)",
})
.option("binary", {
type: "string",
description: "Path to cog-pose-estimation binary (default: RUVIEW_POSE_COG_BINARY)",
}),
async (args) => {
const config = loadConfig();
const binary = (args["binary"] as string | undefined) ?? config.poseCogBinary;
if (args.action === "infer") {
const t0 = Date.now();
const health = await runCog(binary, ["health"]);
const latencyMs = Date.now() - t0;
if (!health.ok) {
process.stderr.write(
`[WARN] Cog health check failed: ${health.error}\n` +
`Set RUVIEW_POSE_COG_BINARY or install cog-pose-estimation (ADR-101).\n`
);
process.stdout.write(
JSON.stringify({
ok: false,
warn: true,
error: health.error,
result: { n_persons: 0, persons: [], backend: "unavailable", latency_ms: 0 },
}) + "\n"
);
process.exit(0);
}
// Parse the health.ok event for real inference output.
let backend = "unknown";
let confidence = 0;
for (const line of health.data.split("\n")) {
try {
const ev = JSON.parse(line.trim()) as Record<string, unknown>;
if (ev["event"] === "health.ok") {
const fields = ev["fields"] as Record<string, unknown>;
backend = String(fields["backend"] ?? "unknown");
confidence = Number(fields["synthetic_output_confidence"] ?? 0);
break;
}
} catch { /* skip */ }
}
process.stdout.write(
JSON.stringify({
ok: true,
synthetic_window: true,
note: "M2: real inference on synthetic CSI window via cog health check.",
result: {
ts: Date.now() / 1000,
n_persons: confidence > 0.1 ? 1 : 0,
persons: confidence > 0.1 ? [{ keypoints: Array.from({ length: 17 }, (_, i) => [0.5, 0.1 + i * 0.05]), confidence }] : [],
backend,
latency_ms: latencyMs,
},
}) + "\n"
);
}
}
);
}
+119
View File
@@ -0,0 +1,119 @@
/**
* ruview train — Training commands.
*
* train count --paired <jsonl> — kick off a count-cog training run.
*/
import type { Argv } from "yargs";
import { randomUUID } from "node:crypto";
import { mkdirSync, appendFileSync, openSync } from "node:fs";
import path from "node:path";
import os from "node:os";
import { spawn } from "node:child_process";
import { loadConfig } from "../config.js";
export function trainCommand(cli: Argv): void {
cli.command(
"train <task>",
"Training commands",
(y) =>
y
.positional("task", {
choices: ["count"] as const,
description: "Which cog to train",
})
.option("paired", {
type: "string",
demandOption: true,
description:
"Path to the paired JSONL training file (produced by scripts/align-ground-truth.js)",
})
.option("epochs", {
type: "number",
default: 400,
description: "Training epochs (default: 400)",
})
.option("lr", {
type: "number",
default: 1e-3,
description: "Initial learning rate (default: 0.001)",
})
.option("output-dir", {
type: "string",
description: "Output directory for model artifacts",
}),
async (args) => {
const config = loadConfig();
const jobId = randomUUID();
const logDir = config.jobsDir;
mkdirSync(logDir, { recursive: true });
const logPath = path.join(logDir, `${jobId}.log`);
const queuedAt = Date.now() / 1000;
const outputDir =
(args["output-dir"] as string | undefined) ??
"v2/crates/cog-person-count/cog/artifacts";
const header = [
`# RuView training job ${jobId}`,
`# started: ${new Date().toISOString()}`,
`# task: ${args.task}`,
`# paired: ${args.paired}`,
`# epochs: ${args.epochs}`,
`# lr: ${args.lr}`,
`# output-dir: ${outputDir}`,
"",
].join("\n");
appendFileSync(logPath, header);
const logFdOut = openSync(logPath, "a");
const logFdErr = openSync(logPath, "a");
const cargoArgs = [
"run",
"--release",
"-p",
"wifi-densepose-train",
"--",
"--task",
"count",
"--paired",
args.paired as string,
"--epochs",
String(args.epochs),
"--lr",
String(args.lr),
"--output-dir",
outputDir,
];
const child = spawn("cargo", cargoArgs, {
detached: true,
stdio: ["ignore", logFdOut, logFdErr],
});
child.unref();
child.on("error", (e) => {
appendFileSync(logPath, `\n# ERROR: ${e.message}\n`);
});
child.on("close", (code) => {
appendFileSync(logPath, `\n# exit code: ${code}\n`);
});
process.stdout.write(
JSON.stringify(
{
ok: true,
job_id: jobId,
status: "running",
log_path: logPath,
queued_at: queuedAt,
note: `Poll with: ruview job status --id ${jobId}`,
},
null,
2
) + "\n"
);
}
);
}
+35
View File
@@ -0,0 +1,35 @@
/**
* Configuration loader for the RuView CLI.
* Mirrors tools/ruview-mcp/src/config.ts — sourced from environment variables.
*/
import os from "node:os";
import path from "node:path";
export interface RuviewCliConfig {
sensingServerUrl: string;
apiToken: string | undefined;
poseCogBinary: string;
countCogBinary: string;
jobsDir: string;
}
function envOrDefault(key: string, fallback: string): string {
return process.env[key] ?? fallback;
}
export function loadConfig(): RuviewCliConfig {
return {
sensingServerUrl: envOrDefault(
"RUVIEW_SENSING_SERVER_URL",
"http://localhost:3000"
),
apiToken: process.env["RUVIEW_API_TOKEN"],
poseCogBinary: envOrDefault("RUVIEW_POSE_COG_BINARY", "cog-pose-estimation"),
countCogBinary: envOrDefault("RUVIEW_COUNT_COG_BINARY", "cog-person-count"),
jobsDir: envOrDefault(
"RUVIEW_JOBS_DIR",
path.join(os.homedir(), ".ruview", "jobs")
),
};
}
+53
View File
@@ -0,0 +1,53 @@
/**
* Lightweight HTTP client (re-used in CLI commands).
* Identical to tools/ruview-mcp/src/http.ts but kept separate to avoid a
* workspace dependency — both packages are standalone and independently publishable.
*/
const REQUEST_TIMEOUT_MS = 10_000;
export type Ok<T> = { ok: true; data: T };
export type Err = { ok: false; error: string };
export type Result<T> = Ok<T> | Err;
export function ok<T>(data: T): Ok<T> {
return { ok: true, data };
}
export function err(error: string): Err {
return { ok: false, error };
}
export async function sensingGet<T>(
baseUrl: string,
path: string,
token: string | undefined
): Promise<Result<T>> {
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
const headers: Record<string, string> = { Accept: "application/json" };
if (token) headers["Authorization"] = `Bearer ${token}`;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const res = await fetch(url, { headers, signal: controller.signal });
clearTimeout(timer);
if (!res.ok) {
return err(`HTTP ${res.status} from ${url}: ${await res.text().catch(() => "(no body)")}`);
}
let body: unknown;
try {
body = await res.json();
} catch {
return err(`Non-JSON response from ${url}`);
}
return ok(body as T);
} catch (e: unknown) {
clearTimeout(timer);
if (e instanceof Error && e.name === "AbortError") {
return err(`Request to ${url} timed out after ${REQUEST_TIMEOUT_MS}ms`);
}
return err(`Network error fetching ${url}: ${String(e)}`);
}
}
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env node
/**
* @ruv/ruview-cli — RuView CLI
*
* Shell access to RuView sensing, inference, and training capabilities.
*
* Subcommands:
* ruview csi tail [--url <url>] stream live CSI frames
* ruview pose infer [--window <path>] 17-keypoint pose estimation
* ruview count infer [--window <path>] person-count inference
* ruview cogs list [--category <cat>] [--search q] list edge module registry
* ruview train count --paired <jsonl> kick off count-cog training
* ruview job status --id <job_id> poll a training job
*
* All subcommands write JSON to stdout and exit 0 on success.
* WARN-level outputs write to stderr; the exit code is still 0 so pipelines
* are not broken by a temporarily unreachable sensing-server.
*
* Usage:
* npx ruview --version
* npx ruview csi tail
* npx ruview pose infer --window ./window.json
* RUVIEW_SENSING_SERVER_URL=http://cognitum-v0:3000 npx ruview cogs list
*
* See ADR-104 for the full design rationale and security model.
*/
import { createRequire } from "node:module";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { csiCommand } from "./commands/csi.js";
import { poseCommand } from "./commands/pose.js";
import { countCommand } from "./commands/count.js";
import { cogsCommand } from "./commands/cogs.js";
import { trainCommand } from "./commands/train.js";
import { jobCommand } from "./commands/job.js";
// Single-source the version from package.json (ADR-265 D3).
const require = createRequire(import.meta.url);
const VERSION: string = (require("../package.json") as { version: string }).version;
// Bin name is `ruview-cli`: the bare `ruview` bin belongs to @ruvnet/ruview
// (ADR-264 O9 / ADR-265 D4).
const cli = yargs(hideBin(process.argv))
.scriptName("ruview-cli")
.version(VERSION)
.usage("$0 <command> [options]")
.strict()
.help()
.wrap(100);
// Register all top-level commands.
csiCommand(cli);
poseCommand(cli);
countCommand(cli);
cogsCommand(cli);
trainCommand(cli);
jobCommand(cli);
cli.demandCommand(1, "Specify a subcommand. Use --help for a list.").parse();