feat(FN-1229): add node mesh CLI commands for node and mesh management

- Add fn node list command to list all registered nodes with status and URL
- Add fn node show command to display detailed node information including system metrics
- Add fn node health-check command to trigger and verify node health status
- Add fn node remove command to unregister nodes from the mesh
- Add fn mesh status command to show mesh topology with peer connections
- Add comprehensive tests for all node and mesh CLI commands
- Update bin.ts to wire up new node and mesh subcommands
- Update memory with CLI mock pattern for testing
This commit is contained in:
gsxdsm
2026-04-09 12:54:16 -07:00
parent 1808ab8e06
commit a53da8890c
7 changed files with 952 additions and 105 deletions

View File

@@ -49,7 +49,7 @@ const { runGitStatus, runGitFetch, runGitPull, runGitPush } = await import("./co
const { runBackupCreate, runBackupList, runBackupRestore, runBackupCleanup } = await import("./commands/backup.js");
const { runMissionCreate, runMissionList, runMissionShow, runMissionDelete, runMissionActivateSlice } = await import("./commands/mission.js");
const { runProjectList, runProjectAdd, runProjectRemove, runProjectShow, runProjectInfo, runProjectSetDefault, runProjectDetect } = await import("./commands/project.js");
const { runNodeList, runNodeAdd, runNodeRemove, runNodeShow, runNodeHealth } = await import("./commands/node.js");
const { runNodeList, runNodeConnect, runNodeDisconnect, runNodeShow, runNodeHealth, runMeshStatus } = await import("./commands/node.js");
const { runInit } = await import("./commands/init.js");
const { runAgentStop, runAgentStart } = await import("./commands/agent.js");
const { runAgentImport } = await import("./commands/agent-import.js");
@@ -109,13 +109,13 @@ Usage:
fn project set-default | default <name>
Set default project
fn project detect Detect project from current directory
fn node list | ls [--json] List all nodes
fn node add <name> [--url <url>] [--api-key <key>] [--max-concurrent <n>]
Register a new node
fn node remove | rm <name> [--force]
Unregister a node
fn node show | info [name] Show node details
fn node health <name> Check node health
fn node list | ls [--json] List all nodes with status and type
fn node connect <name> --url <url> [--api-key <key>] [--max-concurrent <n>]
Connect to a remote node
fn node disconnect <name> [--force] Remove a node connection
fn node show | info [name] [--json] Show node details
fn node health <name> Health check a node
fn mesh status [--json] Show full mesh state
fn settings Show current Fusion configuration
fn settings set <key> <value> Update a configuration setting
fn settings export [opts] Export settings to a JSON file
@@ -391,24 +391,51 @@ async function main() {
await runNodeList({ json: args.includes("--json") });
break;
}
case "add": {
case "connect": {
const name = args[2];
await runNodeAdd(name, {
url: getFlagValue(args, "--url"),
const url = getFlagValue(args, "--url");
if (!url) {
console.error("Usage: fn node connect <name> --url <url> [--api-key <key>] [--max-concurrent <n>]");
process.exit(1);
}
await runNodeConnect(name, {
url,
apiKey: getFlagValue(args, "--api-key"),
maxConcurrent: getFlagValueNumber(args, "--max-concurrent"),
});
break;
}
case "disconnect": {
const name = args[2];
await runNodeDisconnect(name, { force: args.includes("--force") });
break;
}
case "add": {
// Legacy alias for connect
const name = args[2];
const url = getFlagValue(args, "--url");
if (url) {
await runNodeConnect(name, {
url,
apiKey: getFlagValue(args, "--api-key"),
maxConcurrent: getFlagValueNumber(args, "--max-concurrent"),
});
} else {
console.error("Usage: fn node add <name> --url <url> [--api-key <key>] [--max-concurrent <n>]");
process.exit(1);
}
break;
}
case "remove":
case "rm": {
// Legacy alias for disconnect
const name = args[2];
await runNodeRemove(name, { force: args.includes("--force") });
await runNodeDisconnect(name, { force: args.includes("--force") });
break;
}
case "show":
case "info": {
await runNodeShow(args[2]);
await runNodeShow(args[2], { json: args.includes("--json") });
break;
}
case "health": {
@@ -417,7 +444,22 @@ async function main() {
}
default:
console.error(`Unknown subcommand: node ${subcommand || ""}`);
console.log("Try: fn node list | add | remove | show | health");
console.log("Try: fn node list | connect | disconnect | show | health");
process.exit(1);
}
break;
}
case "mesh": {
const subcommand = args[1];
switch (subcommand) {
case "status": {
await runMeshStatus({ json: args.includes("--json") });
break;
}
default:
console.error(`Unknown subcommand: mesh ${subcommand || ""}`);
console.log("Try: fn mesh status");
process.exit(1);
}
break;