@0dai-dev/cli 4.1.0 → 4.2.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.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Utilities for model table availability and footer rendering.
3
+ */
4
+ "use strict";
5
+
6
+ const { execFileSync } = require("child_process");
7
+ const { SUPPORTED_CLIS } = require("./constants");
8
+
9
+ function buildSupportedCliIndex(supportedClis = SUPPORTED_CLIS) {
10
+ const byName = new Map();
11
+ for (const cli of supportedClis) {
12
+ byName.set(cli.name, cli);
13
+ }
14
+ return byName;
15
+ }
16
+
17
+ function probeInstalledCliNames(supportedClis = SUPPORTED_CLIS, probe = execFileSync) {
18
+ const installed = new Set();
19
+ for (const cli of supportedClis) {
20
+ try {
21
+ probe(cli.bin, ["--version"], {
22
+ stdio: "ignore",
23
+ });
24
+ installed.add(cli.name);
25
+ } catch {}
26
+ }
27
+ return installed;
28
+ }
29
+
30
+ function summarizeModelAvailability(models, supportedClis = SUPPORTED_CLIS, installedCliNames = probeInstalledCliNames(supportedClis)) {
31
+ const supportedByName = buildSupportedCliIndex(supportedClis);
32
+ const referencedCliNames = [];
33
+ const seen = new Set();
34
+
35
+ for (const model of models) {
36
+ if (!model || typeof model.cli !== "string" || seen.has(model.cli)) continue;
37
+ seen.add(model.cli);
38
+ referencedCliNames.push(model.cli);
39
+ }
40
+
41
+ const installed = [];
42
+ const missing = [];
43
+ const unsupported = [];
44
+
45
+ for (const cliName of referencedCliNames) {
46
+ if (!supportedByName.has(cliName)) {
47
+ unsupported.push(cliName);
48
+ continue;
49
+ }
50
+ if (installedCliNames.has(cliName)) installed.push(cliName);
51
+ else missing.push(cliName);
52
+ }
53
+
54
+ return {
55
+ referencedCliNames,
56
+ installedCliNames: installed,
57
+ missingCliNames: missing,
58
+ unsupportedCliNames: unsupported,
59
+ totalModels: models.length,
60
+ availableModels: models.filter((model) => installedCliNames.has(model.cli)),
61
+ };
62
+ }
63
+
64
+ function formatAvailableFooter(summary, visibleCount) {
65
+ const installed = summary.installedCliNames.length ? summary.installedCliNames.join(", ") : "none";
66
+ const missing = summary.missingCliNames.length ? summary.missingCliNames.join(", ") : "none";
67
+ const unsupported = summary.unsupportedCliNames.length ? `; unsupported CLIs: ${summary.unsupportedCliNames.join(", ")}` : "";
68
+ const count = typeof visibleCount === "number" ? visibleCount : summary.availableModels.length;
69
+ return `--available: ${count} of ${summary.totalModels} models (installed CLIs: ${installed}; missing CLIs: ${missing}${unsupported})`;
70
+ }
71
+
72
+ module.exports = {
73
+ buildSupportedCliIndex,
74
+ probeInstalledCliNames,
75
+ summarizeModelAvailability,
76
+ formatAvailableFooter,
77
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0dai-dev/cli",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "One config layer for seven AI coding agents — Claude Code, Codex, OpenCode, Gemini, Aider, Qoder",
5
5
  "bin": {
6
6
  "0dai": "./bin/0dai.js"
@@ -29,16 +29,24 @@
29
29
  "node": ">=16"
30
30
  },
31
31
  "scripts": {
32
- "postinstall": "node scripts/postinstall.js || true"
32
+ "postinstall": "node scripts/postinstall.js || true",
33
+ "build:tui": "node scripts/build-tui.js",
34
+ "prepack": "node scripts/build-tui.js"
33
35
  },
34
36
  "files": [
35
37
  "bin/",
36
38
  "lib/",
37
39
  "scripts/",
38
- "README.md"
40
+ "README.md",
41
+ "!lib/tui/src/**"
39
42
  ],
40
43
  "dependencies": {
41
- "@clack/prompts": "^1.2.0"
44
+ "@clack/prompts": "^1.2.0",
45
+ "ink": "^5.0.1",
46
+ "react": "^18.3.1"
47
+ },
48
+ "devDependencies": {
49
+ "esbuild": "^0.23.0"
42
50
  },
43
51
  "optionalDependencies": {
44
52
  "node-pty": "^1.0.0"
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Build the TUI bundle (issue #373).
4
+ *
5
+ * esbuild compiles the TSX source tree under lib/tui/src/ into a single CJS
6
+ * file at lib/tui/index.js. `react` and `ink` stay external so the final
7
+ * package consumes them from node_modules (they're regular dependencies).
8
+ *
9
+ * Runs at `npm run build:tui` and automatically via `prepack` before publish.
10
+ * Silently skipped if esbuild isn't installed (user who only wants the CLI
11
+ * without TUI still builds/installs cleanly — the tui command degrades to a
12
+ * hint message).
13
+ */
14
+ "use strict";
15
+
16
+ const path = require("path");
17
+ const fs = require("fs");
18
+
19
+ const ROOT = path.resolve(__dirname, "..");
20
+ const SRC = path.join(ROOT, "lib", "tui", "src", "index.tsx");
21
+ const OUT = path.join(ROOT, "lib", "tui", "index.mjs");
22
+
23
+ if (!fs.existsSync(SRC)) {
24
+ // Nothing to build (pre-source-split checkout or stripped install).
25
+ process.exit(0);
26
+ }
27
+
28
+ let esbuild;
29
+ try {
30
+ esbuild = require("esbuild");
31
+ } catch {
32
+ console.warn("[tui-build] esbuild not installed — skipping TUI bundle");
33
+ process.exit(0);
34
+ }
35
+
36
+ // Ink 5.x is ESM-only (uses top-level await). Output an ESM bundle so
37
+ // the CLI can load it via dynamic import() from CJS. Everything bundles
38
+ // into one self-contained .mjs (~1.5MB).
39
+ //
40
+ // react-devtools-core is an Ink optional dep only used when DEV=true —
41
+ // shim it to a no-op so we don't ship a 2MB devtools binary.
42
+ const devtoolsStub = {
43
+ name: "stub-react-devtools-core",
44
+ setup(build) {
45
+ build.onResolve({ filter: /^react-devtools-core$/ }, (args) => ({
46
+ path: args.path,
47
+ namespace: "stub",
48
+ }));
49
+ build.onLoad({ filter: /.*/, namespace: "stub" }, () => ({
50
+ contents: "export default { connectToDevTools: () => {} };",
51
+ loader: "js",
52
+ }));
53
+ },
54
+ };
55
+
56
+ esbuild
57
+ .build({
58
+ entryPoints: [SRC],
59
+ outfile: OUT,
60
+ bundle: true,
61
+ platform: "node",
62
+ format: "esm",
63
+ target: "node18",
64
+ jsx: "automatic",
65
+ plugins: [devtoolsStub],
66
+ logLevel: "warning",
67
+ minify: false,
68
+ sourcemap: false,
69
+ banner: { js: "// @generated — do not edit. Source: lib/tui/src/" },
70
+ })
71
+ .then(() => {
72
+ console.log("[tui-build] wrote " + path.relative(ROOT, OUT));
73
+ })
74
+ .catch((err) => {
75
+ console.error("[tui-build] failed:", err.message || err);
76
+ process.exit(1);
77
+ });