@meshxdata/fops 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshxdata/fops",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "CLI to install and manage data mesh platforms",
5
5
  "keywords": [
6
6
  "fops",
@@ -22,8 +22,8 @@ function resolveServiceName(root, input) {
22
22
  export function registerLifecycleCommands(program, registry) {
23
23
  program
24
24
  .command("up")
25
- .description("Start all Foundation services (docker compose up)")
26
- .argument("[component]", "Component to run from a specific branch (backend, frontend, watcher, processor, scheduler, storage, hive, traefik, data)")
25
+ .description("Start Foundation services (all or specific component)")
26
+ .argument("[component]", "Component to start (backend, frontend, watcher, processor, scheduler, storage, hive, traefik, data). Add [branch] to checkout first.")
27
27
  .argument("[branch]", "Git branch to checkout in that component (e.g. FOU-2072)")
28
28
  .option("-d, --detach", "Run in background", true)
29
29
  .option("--k3s", "Include k3s Kubernetes services (default: true for local)", true)
@@ -51,13 +51,28 @@ export function registerLifecycleCommands(program, registry) {
51
51
 
52
52
  program
53
53
  .command("down")
54
- .description("Stop all Foundation services")
54
+ .description("Stop Foundation services (all or specific)")
55
+ .argument("[services...]", "Optional: stop only these services (e.g. watcher, backend)")
55
56
  .option("--clean", "Remove volumes and orphans (make clean)")
56
- .action(async (opts) => {
57
+ .action(async (services, opts) => {
57
58
  const root = requireRoot(program);
58
59
  const { execa } = await import("execa");
59
60
  await runHook(registry, "before:down", { root });
60
- if (opts.clean) {
61
+
62
+ // Resolve short service names to docker compose service names
63
+ const serviceNames = (services || []).filter(Boolean).flatMap((s) => {
64
+ const sub = COMPONENT_SUBMODULES[s];
65
+ if (sub) return sub.restart;
66
+ return [s.startsWith("foundation-") ? s : `foundation-${s}`];
67
+ });
68
+
69
+ if (serviceNames.length) {
70
+ // Stop specific services only
71
+ console.log(chalk.cyan(` Stopping ${serviceNames.join(", ")}...`));
72
+ await dockerCompose(root, ["stop", "-t", "5", ...serviceNames], { timeout: 60000 });
73
+ // Remove the stopped containers so they don't show as "exited"
74
+ await dockerCompose(root, ["rm", "-f", ...serviceNames], { timeout: 30000 });
75
+ } else if (opts.clean) {
61
76
  const result = await dockerCompose(root, ["down", "-v", "--remove-orphans", "-t", "5"], { timeout: 120000 });
62
77
  if (result?.timedOut) {
63
78
  console.log(chalk.yellow(" Compose down timed out — force-killing containers..."));
@@ -403,6 +418,11 @@ async function runUp(program, registry, opts) {
403
418
 
404
419
  // Optional: checkout a component submodule to a specific branch before starting (e.g. fops up backend FOU-2072)
405
420
  const comp = opts.component && COMPONENT_SUBMODULES[opts.component];
421
+ if (opts.component && !comp) {
422
+ console.error(chalk.red(`\n Unknown component: ${opts.component}`));
423
+ console.error(chalk.dim(` Available: ${Object.keys(COMPONENT_SUBMODULES).join(", ")}\n`));
424
+ process.exit(1);
425
+ }
406
426
  if (comp && opts.branch) {
407
427
  const componentDir = path.join(root, comp.dir);
408
428
  if (!fs.existsSync(path.join(componentDir, ".git"))) {
@@ -902,8 +922,8 @@ async function runUp(program, registry, opts) {
902
922
  startSpinner();
903
923
  }
904
924
 
905
- // When fops up <component> <branch>, only pull and up that component (and deps) — avoid full stack rebuild
906
- const componentOnlyUp = comp && opts.branch;
925
+ // When fops up <component> [branch], only pull and up that component — avoid full stack rebuild
926
+ const componentOnlyUp = !!comp;
907
927
 
908
928
  // Pull images only when --pull is passed (or use `fops pull` separately)
909
929
  if (opts.pull) {
package/src/doctor.js CHANGED
@@ -558,7 +558,7 @@ export async function runDoctor(opts = {}, registry = null) {
558
558
  } else {
559
559
  fail("Claude CLI not found", "install globally: npm install -g @anthropic-ai/claude-code", async () => {
560
560
  console.log(chalk.cyan(" ▶ npm install -g @anthropic-ai/claude-code"));
561
- await execa("npm", ["install", "-g", "@anthropic-ai/claude-code"], { stdio: "inherit", timeout: 300_000 });
561
+ await execa("npm", ["install", "-g", "--loglevel=error", "@anthropic-ai/claude-code"], { stdio: "inherit", timeout: 300_000 });
562
562
  });
563
563
  }
564
564
  }
@@ -67,9 +67,9 @@ async function installTool(name, { brew, brewCask, winget, apt, npm: npmPkg } =
67
67
  const cmd = needsSudo ? `sudo npm install -g ${pkg}` : `npm install -g ${pkg}`;
68
68
  console.log(ACCENT(` ▶ ${cmd}`));
69
69
  if (needsSudo) {
70
- await execa("sudo", ["npm", "install", "-g", pkg], { stdio: "inherit", timeout: 300_000 });
70
+ await execa("sudo", ["npm", "install", "-g", "--loglevel=error", pkg], { stdio: "inherit", timeout: 300_000 });
71
71
  } else {
72
- await execa("npm", ["install", "-g", pkg], { stdio: "inherit", timeout: 300_000 });
72
+ await execa("npm", ["install", "-g", "--loglevel=error", pkg], { stdio: "inherit", timeout: 300_000 });
73
73
  }
74
74
  return true;
75
75
  }