totopo 0.7.0 → 0.8.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/bin/totopo.js +41 -59
- package/{src/core/commands/dev.ts → dist/commands/dev.js} +165 -244
- package/dist/commands/doctor.js +61 -0
- package/dist/commands/manage.js +128 -0
- package/dist/commands/menu.js +37 -0
- package/dist/commands/onboard.js +108 -0
- package/dist/commands/rebuild.js +24 -0
- package/dist/commands/settings.js +50 -0
- package/dist/commands/stop.js +26 -0
- package/dist/commands/sync-dockerfile.js +30 -0
- package/{src/core/lib/config.ts → dist/lib/config.js} +5 -15
- package/{src/core/lib/detect-host.ts → dist/lib/detect-host.js} +15 -28
- package/{src/core/lib/generate-dockerfile.ts → dist/lib/generate-dockerfile.js} +49 -111
- package/{src/core/lib/select-tools.ts → dist/lib/select-tools.js} +6 -21
- package/package.json +7 -7
- package/templates/post-start.mjs +1 -1
- package/src/core/commands/doctor.ts +0 -72
- package/src/core/commands/manage.ts +0 -151
- package/src/core/commands/menu.ts +0 -47
- package/src/core/commands/onboard.ts +0 -130
- package/src/core/commands/rebuild.ts +0 -29
- package/src/core/commands/settings.ts +0 -68
- package/src/core/commands/stop.ts +0 -34
- package/src/core/commands/sync-dockerfile.ts +0 -44
- package/tsconfig.json +0 -28
package/bin/totopo.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// =============================================================================
|
|
6
6
|
|
|
7
7
|
import { execSync, spawnSync } from "node:child_process";
|
|
8
|
-
import { existsSync,
|
|
9
|
-
import { basename, dirname
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { basename, dirname } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
|
|
12
12
|
// ─── Guard: inside container ──────────────────────────────────────────────────
|
|
@@ -25,9 +25,8 @@ try {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// ─── Paths ────────────────────────────────────────────────────────────────────
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
const packageDir = dirname(dirname(realpathSync(fileURLToPath(import.meta.url))));
|
|
28
|
+
// dirname(dirname(...)) walks up from bin/ to the package root.
|
|
29
|
+
const packageDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
31
30
|
|
|
32
31
|
let repoRoot;
|
|
33
32
|
try {
|
|
@@ -44,34 +43,39 @@ try {
|
|
|
44
43
|
process.env.TOTOPO_PACKAGE_DIR = packageDir;
|
|
45
44
|
process.env.TOTOPO_REPO_ROOT = repoRoot;
|
|
46
45
|
|
|
47
|
-
// ───
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} catch {}
|
|
56
|
-
execSync(`${pm} install --silent`, { cwd: packageDir, stdio: "inherit" });
|
|
57
|
-
process.stdout.write("\r\x1b[2K"); // clear the line
|
|
46
|
+
// ─── Guard: dist/ must exist ─────────────────────────────────────────────────
|
|
47
|
+
if (!existsSync(new URL("../dist/commands/sync-dockerfile.js", import.meta.url))) {
|
|
48
|
+
console.error("");
|
|
49
|
+
console.error(" totopo: compiled output not found.");
|
|
50
|
+
console.error(" This should not happen with a published package.");
|
|
51
|
+
console.error(" If you are developing locally, run: pnpm build");
|
|
52
|
+
console.error("");
|
|
53
|
+
process.exit(1);
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
// ───
|
|
61
|
-
const run
|
|
56
|
+
// ─── Import compiled commands ─────────────────────────────────────────────────
|
|
57
|
+
const { run: syncDockerfile } = await import("../dist/commands/sync-dockerfile.js");
|
|
58
|
+
const { run: doctor } = await import("../dist/commands/doctor.js");
|
|
59
|
+
const { run: onboard } = await import("../dist/commands/onboard.js");
|
|
60
|
+
const { run: menu } = await import("../dist/commands/menu.js");
|
|
61
|
+
const { run: dev } = await import("../dist/commands/dev.js");
|
|
62
|
+
const { run: stop } = await import("../dist/commands/stop.js");
|
|
63
|
+
const { run: rebuild } = await import("../dist/commands/rebuild.js");
|
|
64
|
+
const { run: manage } = await import("../dist/commands/manage.js");
|
|
65
|
+
const { run: settings } = await import("../dist/commands/settings.js");
|
|
62
66
|
|
|
63
67
|
// ─── Onboarding ───────────────────────────────────────────────────────────────
|
|
64
|
-
if (!existsSync(
|
|
65
|
-
|
|
66
|
-
if (!
|
|
68
|
+
if (!existsSync(`${repoRoot}/.totopo/Dockerfile`)) {
|
|
69
|
+
const completed = await onboard(packageDir, repoRoot);
|
|
70
|
+
if (!completed) process.exit(0);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
// ─── Sync Dockerfile with host runtimes ───────────────────────────────────────
|
|
70
|
-
|
|
74
|
+
await syncDockerfile(packageDir, repoRoot);
|
|
71
75
|
|
|
72
76
|
// ─── Doctor (silent pre-check) ────────────────────────────────────────────────
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
77
|
+
const doctorResult = await doctor(repoRoot, false);
|
|
78
|
+
if (!doctorResult.ok) {
|
|
75
79
|
console.error(" Fix the issues above and re-run totopo.");
|
|
76
80
|
console.error("");
|
|
77
81
|
process.exit(1);
|
|
@@ -85,7 +89,6 @@ const dockerResult = spawnSync("docker", ["ps", "--filter", "name=totopo-managed
|
|
|
85
89
|
});
|
|
86
90
|
const activeCount = dockerResult.stdout ? dockerResult.stdout.trim().split("\n").filter(Boolean).length : 0;
|
|
87
91
|
|
|
88
|
-
// Is THIS project's container running?
|
|
89
92
|
const projectContainerResult = spawnSync("docker", ["ps", "--filter", `name=totopo-managed-${projectName}`, "--format", "{{.Names}}"], {
|
|
90
93
|
encoding: "utf8",
|
|
91
94
|
});
|
|
@@ -95,12 +98,11 @@ const projectRunning = (projectContainerResult.stdout ?? "")
|
|
|
95
98
|
.filter(Boolean)
|
|
96
99
|
.some((n) => n === `totopo-managed-${projectName}`);
|
|
97
100
|
|
|
98
|
-
// Does THIS project's image exist?
|
|
99
101
|
const projectImageResult = spawnSync("docker", ["images", "-q", `totopo-managed-${projectName}`], { encoding: "utf8" });
|
|
100
102
|
const projectImageExists = (projectImageResult.stdout ?? "").trim().length > 0;
|
|
101
103
|
|
|
102
104
|
let hasKey = false;
|
|
103
|
-
const envPath =
|
|
105
|
+
const envPath = `${repoRoot}/.totopo/.env`;
|
|
104
106
|
if (existsSync(envPath)) {
|
|
105
107
|
for (const line of readFileSync(envPath, "utf8").split("\n")) {
|
|
106
108
|
const trimmed = line.trim();
|
|
@@ -113,55 +115,35 @@ if (existsSync(envPath)) {
|
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
// ─── Interactive menu
|
|
117
|
-
// stdout → /dev/tty so the clack UI renders on the terminal
|
|
118
|
-
// stderr → pipe so the selected action string is captured
|
|
119
|
-
const ttyFd = openSync("/dev/tty", "w");
|
|
120
|
-
|
|
118
|
+
// ─── Interactive menu loop ────────────────────────────────────────────────────
|
|
121
119
|
let showMenu = true;
|
|
122
120
|
while (showMenu) {
|
|
123
121
|
showMenu = false;
|
|
124
122
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
[
|
|
128
|
-
join(packageDir, "src/core/commands/menu.ts"),
|
|
129
|
-
projectName,
|
|
130
|
-
String(activeCount),
|
|
131
|
-
String(hasKey),
|
|
132
|
-
String(projectRunning),
|
|
133
|
-
String(projectImageExists),
|
|
134
|
-
],
|
|
135
|
-
{
|
|
136
|
-
stdio: ["inherit", ttyFd, "pipe"],
|
|
137
|
-
encoding: "utf8",
|
|
138
|
-
},
|
|
139
|
-
);
|
|
140
|
-
const action = (menuResult.stderr ?? "").trim();
|
|
141
|
-
|
|
142
|
-
// ─── Execute selection ────────────────────────────────────────────────────────
|
|
123
|
+
const action = await menu({ projectName, activeCount, hasKey, projectRunning, projectImageExists });
|
|
124
|
+
|
|
143
125
|
switch (action) {
|
|
144
126
|
case "dev":
|
|
145
|
-
|
|
127
|
+
await dev(packageDir, repoRoot);
|
|
146
128
|
break;
|
|
147
129
|
case "stop":
|
|
148
|
-
|
|
130
|
+
await stop(projectName);
|
|
149
131
|
break;
|
|
150
132
|
case "rebuild":
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
await rebuild(projectName);
|
|
134
|
+
await dev(packageDir, repoRoot);
|
|
153
135
|
break;
|
|
154
136
|
case "manage": {
|
|
155
|
-
const result =
|
|
156
|
-
if (result
|
|
137
|
+
const result = await manage(projectName, repoRoot);
|
|
138
|
+
if (result === "back") showMenu = true;
|
|
157
139
|
break;
|
|
158
140
|
}
|
|
159
141
|
case "doctor":
|
|
160
|
-
|
|
142
|
+
await doctor(repoRoot, true);
|
|
161
143
|
break;
|
|
162
144
|
case "settings": {
|
|
163
|
-
const result =
|
|
164
|
-
if (result
|
|
145
|
+
const result = await settings(packageDir, repoRoot);
|
|
146
|
+
if (result === "back") showMenu = true;
|
|
165
147
|
break;
|
|
166
148
|
}
|
|
167
149
|
default:
|