mirror of
https://github.com/ruvnet/RuView
synced 2026-07-31 18:51:42 +00:00
feat(tools): scaffold ruview MCP server + CLI + ADR-104 (#705)
Adds two new npm packages that expose RuView's WiFi-DensePose sensing capabilities outside the Cognitum appliance ecosystem: - tools/ruview-mcp/ (@ruv/ruview-mcp) — MCP server with 6 tools: ruview_csi_latest, ruview_pose_infer, ruview_count_infer, ruview_registry_list, ruview_train_count, ruview_job_status. Uses @modelcontextprotocol/sdk with stdio transport. 6/6 smoke tests pass. TypeScript strict mode, Node 20. - tools/ruview-cli/ (@ruv/ruview-cli) — Yargs CLI with matching subcommands: csi tail, pose infer, count infer, cogs list, train count, job status. Same fail-open pattern as the cog binaries (WARN to stderr, exit 0 on unavailable sensing-server). - docs/adr/ADR-104-ruview-mcp-cli-distribution.md — design rationale, 6-row threat table, packaging plan, acceptance gates, failure modes. - docs/research/sota-2026-05-22/HORIZON.md — 12-hour horizon plan with 7 milestones tracked (M1 complete in this commit). Both packages are private:true pending the user's publish decision. Inference is via subprocess to the signed cog binaries (ADR-100/101/103) — no JS/WASM ML engine bundled.
This commit is contained in:
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user