create-op-node 0.6.0 → 0.7.0

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/README.md CHANGED
@@ -30,9 +30,23 @@ Configures macOS power settings, installs Homebrew + the CLI tool list, sets up
30
30
 
31
31
  ### Choosing the LLM model
32
32
 
33
- By default bootstrap pulls `qwen3.5:9b` (LLM) and `nomic-embed-text`
34
- (embeddings) small enough to validate the inference path on first
35
- run. Override with flags:
33
+ When you run `bootstrap` interactively (no `-y`, no `--llm-model` flag),
34
+ you'll get a curated picker:
35
+
36
+ - `qwen2.5:72b` — 72B, ~50 GB Ollama RAM. **Default**, recommended for
37
+ 128 GB Studios. Best Spanish + multilingual quality.
38
+ - `qwen2.5:32b` — 32B, ~22 GB Ollama RAM. Middle tier for 64 GB Studios.
39
+ - `qwen3.5:9b` — 9B, ~8 GB Ollama RAM. Validation runs or smaller
40
+ Studios (36–48 GB).
41
+ - `Other…` — any Ollama model name you specify.
42
+
43
+ The picker is Qwen-only because the Opus Populi platform serves
44
+ Spanish-speaking civic users and Qwen has the strongest Spanish (and
45
+ broader multilingual) capability of the open-weight models in this
46
+ size class. Pick "Other…" for non-Qwen models.
47
+
48
+ For non-interactive runs (`-y`) or scripted invocations, the LLM
49
+ defaults to `qwen3.5:9b` (small, fast) — pass `--llm-model` to override:
36
50
 
37
51
  ```bash
38
52
  # Just swap the LLM, keep the default embedding model:
@@ -527,6 +541,17 @@ pnpm build # tsup → dist/
527
541
  node dist/cli.js --help # test the built binary
528
542
  ```
529
543
 
544
+ ### Releases
545
+
546
+ Releases are automated via [release-please](https://github.com/googleapis/release-please-action):
547
+
548
+ 1. **Write Conventional Commits.** `feat:` → minor bump (or major on `BREAKING CHANGE:`); `fix:` → patch; `docs:`/`refactor:`/`perf:` show up in CHANGELOG; `chore:`/`test:`/`build:`/`ci:` are hidden.
549
+ 2. **Merge PRs to `main`** as normal.
550
+ 3. **release-please opens a `chore(main): release X.Y.Z` PR** that bumps `package.json` + `src/cli.ts`'s `VERSION` constant + adds a CHANGELOG section. It updates the PR continuously as more commits land.
551
+ 4. **Merge the release PR.** That creates the `vX.Y.Z` tag + a GitHub Release. `publish.yml` fires on the tag push and runs `npm publish --provenance --access public`.
552
+
553
+ No manual version bumps, no manual tagging. To trigger a release with no functional changes (re-publishing after a CI fix, etc.), push a commit with `chore: trigger release` — release-please will skip it but still update the PR.
554
+
530
555
  ## License
531
556
 
532
557
  [AGPL-3.0-or-later](./LICENSE). The Opus Populi platform code is AGPL-3.0 + dual commercial; this CLI inherits the AGPL-3.0 terms.
package/dist/cli.js CHANGED
@@ -1690,7 +1690,11 @@ var bootstrapCommand = new Command("bootstrap").description(
1690
1690
  const repoName = `opuspopuli-node-${region}`;
1691
1691
  const composeFileDefault = opts.localOnly ? ["docker-compose-prod.yml"] : ["docker-compose-prod.yml", "docker-compose-backup.yml"];
1692
1692
  const composeFile = opts.composeFile ?? composeFileDefault;
1693
- const [embeddingModel, llmModel] = resolveModels(opts);
1693
+ const llmModelChoice = opts.llmModel ?? await selectLlmModel(opts);
1694
+ const [embeddingModel, llmModel] = resolveModels({
1695
+ llmModel: llmModelChoice,
1696
+ ...opts.embeddingModel !== void 0 ? { embeddingModel: opts.embeddingModel } : {}
1697
+ });
1694
1698
  const sysSpin = p3.spinner();
1695
1699
  sysSpin.start("Inspecting macOS\u2026");
1696
1700
  const snap = await inspectSystem();
@@ -1751,7 +1755,7 @@ var bootstrapCommand = new Command("bootstrap").description(
1751
1755
  unwrap(await p3.confirm({ message: "Homebrew installed?", initialValue: true }));
1752
1756
  }
1753
1757
  const brewSpin = p3.spinner();
1754
- brewSpin.start("Installing Studio packages\u2026");
1758
+ brewSpin.start("Installing Studio packages\u2026 (~5\u201315 min first run)");
1755
1759
  const report = await installPackages(STUDIO_PACKAGES, (pkg, status) => {
1756
1760
  brewSpin.message(`${status}: ${pkg.name}`);
1757
1761
  });
@@ -1852,7 +1856,7 @@ var bootstrapCommand = new Command("bootstrap").description(
1852
1856
  ghcrSpin.stop(pc2.green("\u2713 Logged in to ghcr.io."));
1853
1857
  if (!opts.skipOllama) {
1854
1858
  const olSpin = p3.spinner();
1855
- olSpin.start("Pulling + warming Ollama models\u2026");
1859
+ olSpin.start(`Pulling + warming Ollama models\u2026 (${estimatedPullTime(llmModel)})`);
1856
1860
  let olHealth = await checkOllamaHealth();
1857
1861
  if (!olHealth.reachable) {
1858
1862
  olSpin.message("Ollama not reachable \u2014 running `brew services start ollama`\u2026");
@@ -1917,7 +1921,7 @@ var bootstrapCommand = new Command("bootstrap").description(
1917
1921
  );
1918
1922
  }
1919
1923
  const pullSpin = p3.spinner();
1920
- pullSpin.start("Pulling images from ghcr.io\u2026");
1924
+ pullSpin.start("Pulling images from ghcr.io\u2026 (~5\u201315 min first run, < 1 min cached)");
1921
1925
  const pull = await composePull(composeOpts);
1922
1926
  if (!pull.ok) {
1923
1927
  pullSpin.stop(pc2.red("\u2717 compose pull failed."));
@@ -1935,7 +1939,7 @@ var bootstrapCommand = new Command("bootstrap").description(
1935
1939
  }
1936
1940
  upSpin.stop(pc2.green("\u2713 Containers started \u2014 waiting for healthy\u2026"));
1937
1941
  const healthSpin = p3.spinner();
1938
- healthSpin.start("Polling for healthy containers (5s, up to 5 minutes)\u2026");
1942
+ healthSpin.start("Polling for healthy containers\u2026 (every 5s, up to 5 min)");
1939
1943
  const outcome = await waitForHealthy(composeOpts, {
1940
1944
  onPoll: (snaps) => {
1941
1945
  const healthy = snaps.filter((s) => s.health === "healthy").length;
@@ -2001,6 +2005,59 @@ function resolveModels(opts) {
2001
2005
  opts.llmModel ?? DEFAULT_LLM_MODEL
2002
2006
  ];
2003
2007
  }
2008
+ var LLM_MODEL_CHOICES = [
2009
+ {
2010
+ value: "qwen2.5:72b",
2011
+ label: "qwen2.5:72b",
2012
+ hint: "72B, ~50 GB Ollama RAM. Recommended for 128 GB Studios. Best Spanish + multilingual quality. Pull ~40 GB, 30\u201360 min."
2013
+ },
2014
+ {
2015
+ value: "qwen2.5:32b",
2016
+ label: "qwen2.5:32b",
2017
+ hint: "32B, ~22 GB Ollama RAM. Middle-tier for 64 GB Studios. Solid Spanish, faster than 72B. Pull ~20 GB, 15\u201330 min."
2018
+ },
2019
+ {
2020
+ value: "qwen3.5:9b",
2021
+ label: "qwen3.5:9b",
2022
+ hint: "9B, ~8 GB Ollama RAM. Validation / smaller Studios (36\u201348 GB). Pull ~5 GB, 3\u20135 min."
2023
+ }
2024
+ ];
2025
+ var OTHER_SENTINEL = "__OTHER__";
2026
+ async function selectLlmModel(opts) {
2027
+ if (opts.yes) return DEFAULT_LLM_MODEL;
2028
+ const choice = unwrap(
2029
+ await p3.select({
2030
+ message: "Choose the LLM model to pull + run",
2031
+ initialValue: "qwen2.5:72b",
2032
+ options: [
2033
+ ...LLM_MODEL_CHOICES.map((c) => ({ value: c.value, label: c.label, hint: c.hint })),
2034
+ {
2035
+ value: OTHER_SENTINEL,
2036
+ label: "Other\u2026",
2037
+ hint: "Specify any Ollama model name (e.g. mistral-small:24b)"
2038
+ }
2039
+ ]
2040
+ })
2041
+ );
2042
+ if (choice !== OTHER_SENTINEL) return choice;
2043
+ return unwrap(
2044
+ await p3.text({
2045
+ message: "Ollama model name",
2046
+ placeholder: "e.g. mistral-small:24b",
2047
+ validate: (v) => /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(v ?? "") ? void 0 : "Letters, digits, `.`, `:`, `_`, `-`, `/` only; must start with alphanumeric"
2048
+ })
2049
+ );
2050
+ }
2051
+ function estimatedPullTime(model) {
2052
+ if (/\d+x\d+b/i.test(model)) return "~60+ min for frontier MoE";
2053
+ const match = /(\d+)b\b/i.exec(model);
2054
+ if (!match) return "time depends on model size";
2055
+ const size = Number.parseInt(match[1], 10);
2056
+ if (size <= 13) return "~3\u20135 min for \u2264 13B-class";
2057
+ if (size <= 35) return "~15\u201330 min for ~32B-class";
2058
+ if (size <= 80) return "~30\u201360 min for 70B-class";
2059
+ return "~60+ min for frontier-class";
2060
+ }
2004
2061
  function kvBool(b) {
2005
2062
  return b ? pc2.green("on") : pc2.yellow("off");
2006
2063
  }
@@ -3843,7 +3900,7 @@ async function looksLikeRegionsRepo(dir) {
3843
3900
  }
3844
3901
 
3845
3902
  // src/cli.ts
3846
- var VERSION = "0.6.0";
3903
+ var VERSION = "0.7.0";
3847
3904
  var program = new Command();
3848
3905
  program.name("create-op-node").description(
3849
3906
  "Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."