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 +28 -3
- package/dist/cli.js +63 -6
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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(
|
|
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
|
|
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.
|
|
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."
|