wattetheria 0.4.2 → 0.4.3
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/lib/cli.js +86 -9
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -8,8 +8,10 @@ const readline = require("node:readline");
|
|
|
8
8
|
|
|
9
9
|
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
10
10
|
const PACKAGE_JSON = require(path.join(PACKAGE_ROOT, "package.json"));
|
|
11
|
+
const WATTETHERIA_HOME_DIR = path.join(os.homedir(), ".wattetheria");
|
|
11
12
|
const DEFAULT_DEPLOY_DIR = path.join(os.homedir(), ".wattetheria", "deploy");
|
|
12
13
|
const DEFAULT_PROJECT_NAME = "wattetheria";
|
|
14
|
+
const POSTGRES_VOLUME_SERVICE_NAME = "wattswarm_pg_data";
|
|
13
15
|
const DEFAULT_COMMAND = "help";
|
|
14
16
|
const RELEASE_ENV_TEMPLATE_PATH = path.join(PACKAGE_ROOT, ".env.release");
|
|
15
17
|
const IMAGE_KEYS = [
|
|
@@ -85,7 +87,7 @@ Options:
|
|
|
85
87
|
--force Refresh deployment defaults and compose assets
|
|
86
88
|
--no-health-checks Skip HTTP health checks
|
|
87
89
|
--volumes With uninstall, remove named docker volumes
|
|
88
|
-
--purge With uninstall, remove
|
|
90
|
+
--purge With uninstall, remove ~/.wattetheria and PostgreSQL data
|
|
89
91
|
--data-dir <path> With mcp-proxy or doctor, override Wattetheria host state directory
|
|
90
92
|
--control-plane <url> With mcp-proxy or doctor, override local control-plane endpoint
|
|
91
93
|
|
|
@@ -955,6 +957,74 @@ function runCompose(options, args, capture = false) {
|
|
|
955
957
|
return result;
|
|
956
958
|
}
|
|
957
959
|
|
|
960
|
+
function runDocker(args, capture = false) {
|
|
961
|
+
const dockerCommand = resolveDockerCommand() || "docker";
|
|
962
|
+
const result = spawnSync(dockerCommand, args, {
|
|
963
|
+
stdio: capture ? "pipe" : "inherit",
|
|
964
|
+
encoding: capture ? "utf8" : undefined
|
|
965
|
+
});
|
|
966
|
+
if (result.error) {
|
|
967
|
+
throw result.error;
|
|
968
|
+
}
|
|
969
|
+
if (result.status !== 0) {
|
|
970
|
+
const stderr = capture ? (result.stderr || "").trim() : "";
|
|
971
|
+
throw new Error(stderr || `docker ${args.join(" ")} failed`);
|
|
972
|
+
}
|
|
973
|
+
return result;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function postgresVolumeName(options) {
|
|
977
|
+
return `${options.projectName}_${POSTGRES_VOLUME_SERVICE_NAME}`;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function dockerVolumeExists(volumeName) {
|
|
981
|
+
const dockerCommand = resolveDockerCommand() || "docker";
|
|
982
|
+
const result = spawnSync(dockerCommand, ["volume", "inspect", volumeName], {
|
|
983
|
+
stdio: "ignore"
|
|
984
|
+
});
|
|
985
|
+
return !result.error && result.status === 0;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function removeDockerVolumeIfExists(volumeName) {
|
|
989
|
+
if (!dockerVolumeExists(volumeName)) {
|
|
990
|
+
console.log(`PostgreSQL volume not found: ${volumeName}`);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
runDocker(["volume", "rm", volumeName]);
|
|
994
|
+
if (dockerVolumeExists(volumeName)) {
|
|
995
|
+
throw new Error(`PostgreSQL volume still exists after removal: ${volumeName}`);
|
|
996
|
+
}
|
|
997
|
+
console.log(`Removed PostgreSQL volume: ${volumeName}`);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function comparablePath(filePath) {
|
|
1001
|
+
const resolved = path.resolve(filePath);
|
|
1002
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function pathIsInsideOrEqual(childPath, parentPath) {
|
|
1006
|
+
const child = comparablePath(childPath);
|
|
1007
|
+
const parent = comparablePath(parentPath);
|
|
1008
|
+
const relative = path.relative(parent, child);
|
|
1009
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function removeWattetheriaHomeDir() {
|
|
1013
|
+
const homeDir = WATTETHERIA_HOME_DIR;
|
|
1014
|
+
if (!fs.existsSync(homeDir)) {
|
|
1015
|
+
console.log(`Wattetheria home directory not found: ${homeDir}`);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (pathIsInsideOrEqual(process.cwd(), homeDir)) {
|
|
1019
|
+
process.chdir(os.homedir());
|
|
1020
|
+
}
|
|
1021
|
+
fs.rmSync(homeDir, { recursive: true, force: true });
|
|
1022
|
+
if (fs.existsSync(homeDir)) {
|
|
1023
|
+
throw new Error(`Wattetheria home directory still exists after removal: ${homeDir}`);
|
|
1024
|
+
}
|
|
1025
|
+
console.log(`Removed Wattetheria home directory: ${homeDir}`);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
958
1028
|
const RELEASE_SERVICES = [
|
|
959
1029
|
"kernel",
|
|
960
1030
|
"wattswarm-postgres",
|
|
@@ -1268,14 +1338,21 @@ async function stop(options) {
|
|
|
1268
1338
|
|
|
1269
1339
|
async function uninstall(options) {
|
|
1270
1340
|
await ensureDockerAvailable();
|
|
1271
|
-
const
|
|
1272
|
-
if (
|
|
1273
|
-
args
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1341
|
+
const hasDeploymentFiles = fs.existsSync(composeFilePath(options)) && fs.existsSync(envFilePath(options));
|
|
1342
|
+
if (hasDeploymentFiles) {
|
|
1343
|
+
const args = ["down"];
|
|
1344
|
+
if (options.purge) {
|
|
1345
|
+
args.push("--remove-orphans");
|
|
1346
|
+
} else if (options.volumes) {
|
|
1347
|
+
args.push("-v");
|
|
1348
|
+
}
|
|
1349
|
+
runCompose(options, args);
|
|
1350
|
+
} else {
|
|
1351
|
+
console.log(`Deployment compose/env files not found in: ${options.dir}`);
|
|
1352
|
+
}
|
|
1353
|
+
if (options.purge) {
|
|
1354
|
+
removeDockerVolumeIfExists(postgresVolumeName(options));
|
|
1355
|
+
removeWattetheriaHomeDir();
|
|
1279
1356
|
}
|
|
1280
1357
|
}
|
|
1281
1358
|
|