wattetheria 0.2.8 → 0.3.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 +23 -0
- package/lib/cli.js +175 -9
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -552,6 +552,29 @@ CLI prerequisites:
|
|
|
552
552
|
|
|
553
553
|
The CLI handles image pull, deployment directory setup, environment generation, container start,
|
|
554
554
|
and health checks internally.
|
|
555
|
+
Agent commands such as `identity`, `wallet`, `servicenet`, and `publish` are forwarded to the
|
|
556
|
+
native `wattetheria-client-cli`; installed release packages should not require Rust on the user's
|
|
557
|
+
machine. The JS wrapper resolves `WATTETHERIA_CLI_BIN` first, then the matching optional native
|
|
558
|
+
package such as `@wattetheria/cli-win32-x64`, then `bin/native/<platform>-<arch>/`, then `PATH`.
|
|
559
|
+
If no host native CLI is available but the local Wattetheria node deployment is installed, the
|
|
560
|
+
wrapper runs the same command inside the `kernel` container using the node's
|
|
561
|
+
`/var/lib/wattetheria` data directory.
|
|
562
|
+
|
|
563
|
+
NPM publish flow:
|
|
564
|
+
|
|
565
|
+
1. Set the repository secret `NPM_TOKEN` to an npm token that can publish `wattetheria`.
|
|
566
|
+
2. Run the manual GitHub Actions workflow `npm-publish`.
|
|
567
|
+
3. Keep `dry_run` enabled first. It checks syntax, packs the main package, and runs npm publish
|
|
568
|
+
dry-run without building native packages.
|
|
569
|
+
4. Rerun with `dry_run=false` to publish the main `wattetheria` package.
|
|
570
|
+
|
|
571
|
+
The main package does not block on platform native packages. Windows and other platform-specific
|
|
572
|
+
publisher execution is handled either by the optional native package or by the installed
|
|
573
|
+
Wattetheria node image fallback.
|
|
574
|
+
|
|
575
|
+
Standalone native publisher packages are optional and can be released separately with the manual
|
|
576
|
+
`npm-native-cli-release` workflow. Use that workflow when publishing new `@wattetheria/cli-*`
|
|
577
|
+
packages for users who want to run `servicenet` or `publish` before installing the local node.
|
|
555
578
|
|
|
556
579
|
Version commands:
|
|
557
580
|
|
package/lib/cli.js
CHANGED
|
@@ -29,6 +29,27 @@ const WINDOWS_DOCKER_CANDIDATES = [
|
|
|
29
29
|
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe",
|
|
30
30
|
"C:\\Program Files\\Docker\\cli-plugins\\docker.exe"
|
|
31
31
|
];
|
|
32
|
+
const RUST_CLI_BASE_NAME = "wattetheria-client-cli";
|
|
33
|
+
const NATIVE_CLI_PACKAGE_PREFIX = "@wattetheria/cli";
|
|
34
|
+
const NATIVE_ARCH_ALIASES = new Map([
|
|
35
|
+
["x64", "x64"],
|
|
36
|
+
["arm64", "arm64"]
|
|
37
|
+
]);
|
|
38
|
+
const BANNER_COMMANDS = new Set([
|
|
39
|
+
"help",
|
|
40
|
+
"install",
|
|
41
|
+
"start",
|
|
42
|
+
"up",
|
|
43
|
+
"update",
|
|
44
|
+
"restart",
|
|
45
|
+
"stop",
|
|
46
|
+
"down",
|
|
47
|
+
"uninstall",
|
|
48
|
+
"doctor"
|
|
49
|
+
]);
|
|
50
|
+
const ANSI_ORANGE = "\x1b[38;5;166m";
|
|
51
|
+
const ANSI_MUTED = "\x1b[38;5;244m";
|
|
52
|
+
const ANSI_RESET = "\x1b[0m";
|
|
32
53
|
|
|
33
54
|
function printHelp() {
|
|
34
55
|
console.log(`Wattetheria CLI ${PACKAGE_JSON.version}
|
|
@@ -52,7 +73,7 @@ Commands:
|
|
|
52
73
|
doctor Check local prerequisites
|
|
53
74
|
help Show this help
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
Agent subcommands (forwarded to the bundled native CLI when available):
|
|
56
77
|
identity init | show | export-seed
|
|
57
78
|
wallet manage wallet payment accounts
|
|
58
79
|
servicenet provider register
|
|
@@ -485,7 +506,19 @@ function formatCliVersionString() {
|
|
|
485
506
|
}
|
|
486
507
|
|
|
487
508
|
function formatBanner(options) {
|
|
488
|
-
|
|
509
|
+
const wordmark = [
|
|
510
|
+
" __ __ _ _ _ _ _ ",
|
|
511
|
+
" \\ \\ / /_ _| |_| |_ ___| |_| |__ ___ _ __(_) __ _ ",
|
|
512
|
+
" \\ \\ /\\ / / _` | __| __/ _ \\ __| '_ \\ / _ \\ '__| |/ _` |",
|
|
513
|
+
" \\ V V / (_| | |_| || __/ |_| | | | __/ | | | (_| |",
|
|
514
|
+
" \\_/\\_/ \\__,_|\\__|\\__\\___|\\__|_| |_|\\___|_| |_|\\__,_|"
|
|
515
|
+
].join("\n");
|
|
516
|
+
const subtitle = `${formatReleaseVersionString(options)} - local agent runtime, swarm sync, external agent reach`;
|
|
517
|
+
|
|
518
|
+
if (!supportsBannerColor()) {
|
|
519
|
+
return `${wordmark}\n${subtitle}`;
|
|
520
|
+
}
|
|
521
|
+
return `${ANSI_ORANGE}${wordmark}${ANSI_RESET}\n${ANSI_MUTED}${subtitle}${ANSI_RESET}`;
|
|
489
522
|
}
|
|
490
523
|
|
|
491
524
|
function formatDockerStatusMessage(status) {
|
|
@@ -694,6 +727,27 @@ function composeFilePath(options) {
|
|
|
694
727
|
return path.join(options.dir, "docker-compose.yml");
|
|
695
728
|
}
|
|
696
729
|
|
|
730
|
+
function deploymentState(dir = DEFAULT_DEPLOY_DIR) {
|
|
731
|
+
const envPath = path.join(dir, ".env");
|
|
732
|
+
const composePath = path.join(dir, "docker-compose.yml");
|
|
733
|
+
const stateDir = fs.existsSync(envPath)
|
|
734
|
+
? resolveConfiguredPath(
|
|
735
|
+
dir,
|
|
736
|
+
getEnvValue(readEnvFile(envPath), "WATTETHERIA_HOST_STATE_DIR", "./data/wattetheria")
|
|
737
|
+
)
|
|
738
|
+
: path.join(dir, "data", "wattetheria");
|
|
739
|
+
const tokenPath = path.join(stateDir, "control.token");
|
|
740
|
+
return {
|
|
741
|
+
dir,
|
|
742
|
+
envPath,
|
|
743
|
+
composePath,
|
|
744
|
+
stateDir,
|
|
745
|
+
tokenPath,
|
|
746
|
+
installed: fs.existsSync(envPath) || fs.existsSync(composePath) || fs.existsSync(tokenPath),
|
|
747
|
+
runnable: fs.existsSync(envPath) && fs.existsSync(composePath)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
|
|
697
751
|
function readEnvFile(filePath) {
|
|
698
752
|
const envMap = new Map();
|
|
699
753
|
const content = fs.readFileSync(filePath, "utf8");
|
|
@@ -1170,7 +1224,10 @@ function printImages(options) {
|
|
|
1170
1224
|
}
|
|
1171
1225
|
|
|
1172
1226
|
function shouldPrintBanner(command) {
|
|
1173
|
-
return
|
|
1227
|
+
return isInteractiveTerminal()
|
|
1228
|
+
&& !process.env.CI
|
|
1229
|
+
&& !process.env.WATTETHERIA_NO_BANNER
|
|
1230
|
+
&& BANNER_COMMANDS.has(command);
|
|
1174
1231
|
}
|
|
1175
1232
|
|
|
1176
1233
|
function printBanner(options) {
|
|
@@ -1178,6 +1235,12 @@ function printBanner(options) {
|
|
|
1178
1235
|
console.log("");
|
|
1179
1236
|
}
|
|
1180
1237
|
|
|
1238
|
+
function supportsBannerColor() {
|
|
1239
|
+
return process.stdout.isTTY
|
|
1240
|
+
&& !process.env.NO_COLOR
|
|
1241
|
+
&& process.env.TERM !== "dumb";
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1181
1244
|
// Forwarded subcommands delegate verbatim to the local Rust binary
|
|
1182
1245
|
// `wattetheria-client-cli`. Keep the JS side stupid — no flag parsing here.
|
|
1183
1246
|
const FORWARDED_SUBCOMMANDS = new Set([
|
|
@@ -1187,13 +1250,114 @@ const FORWARDED_SUBCOMMANDS = new Set([
|
|
|
1187
1250
|
"publish",
|
|
1188
1251
|
]);
|
|
1189
1252
|
|
|
1253
|
+
function nativePlatformKey(platform = process.platform, arch = process.arch) {
|
|
1254
|
+
const normalizedArch = NATIVE_ARCH_ALIASES.get(arch);
|
|
1255
|
+
if (!normalizedArch || !["darwin", "linux", "win32"].includes(platform)) {
|
|
1256
|
+
return "";
|
|
1257
|
+
}
|
|
1258
|
+
return `${platform}-${normalizedArch}`;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
function rustCliBinaryName(platform = process.platform) {
|
|
1262
|
+
return platform === "win32" ? `${RUST_CLI_BASE_NAME}.exe` : RUST_CLI_BASE_NAME;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function nativePackageName(platform = process.platform, arch = process.arch) {
|
|
1266
|
+
const platformKey = nativePlatformKey(platform, arch);
|
|
1267
|
+
return platformKey ? `${NATIVE_CLI_PACKAGE_PREFIX}-${platformKey}` : "";
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function nativePackageRustBinaryPath() {
|
|
1271
|
+
const packageName = nativePackageName();
|
|
1272
|
+
if (!packageName) {
|
|
1273
|
+
return "";
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
try {
|
|
1277
|
+
const manifestPath = require.resolve(`${packageName}/package.json`, {
|
|
1278
|
+
paths: [PACKAGE_ROOT]
|
|
1279
|
+
});
|
|
1280
|
+
return path.join(path.dirname(manifestPath), "bin", rustCliBinaryName());
|
|
1281
|
+
} catch (_error) {
|
|
1282
|
+
return "";
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
function bundledRustBinaryPath() {
|
|
1287
|
+
const platformKey = nativePlatformKey();
|
|
1288
|
+
if (!platformKey) {
|
|
1289
|
+
return "";
|
|
1290
|
+
}
|
|
1291
|
+
return path.join(PACKAGE_ROOT, "bin", "native", platformKey, rustCliBinaryName());
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function missingNativeCliError(commandName) {
|
|
1295
|
+
const platformKey = nativePlatformKey() || `${process.platform}-${process.arch}`;
|
|
1296
|
+
const deployment = deploymentState();
|
|
1297
|
+
if (deployment.installed) {
|
|
1298
|
+
return new Error(
|
|
1299
|
+
`Wattetheria node deployment was found at ${deployment.dir}, but this npm package does not include ` +
|
|
1300
|
+
`the native publisher CLI for ${platformKey}. ` +
|
|
1301
|
+
`Run 'npx wattetheria status' to check the installed node, or update the local node image.`
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
return new Error(
|
|
1305
|
+
`Could not run '${commandName}' because no Wattetheria node deployment or native CLI was found. ` +
|
|
1306
|
+
`Run 'npx wattetheria install' to install the local node, or set WATTETHERIA_CLI_BIN to ` +
|
|
1307
|
+
`the full path of ${rustCliBinaryName()}.`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function forwardedArgsForInstalledNode(commandName, rawArgv) {
|
|
1312
|
+
const args = [commandName];
|
|
1313
|
+
if (!rawArgv.some((arg) => arg === "--data-dir" || arg.startsWith("--data-dir="))) {
|
|
1314
|
+
args.push("--data-dir", "/var/lib/wattetheria");
|
|
1315
|
+
}
|
|
1316
|
+
args.push(...rawArgv.slice(1));
|
|
1317
|
+
return args;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function runInstalledNodeCli(commandName, rawArgv, deployment) {
|
|
1321
|
+
const dockerCommand = resolveDockerCommand();
|
|
1322
|
+
if (!dockerCommand) {
|
|
1323
|
+
throw new Error(
|
|
1324
|
+
`Wattetheria node deployment was found at ${deployment.dir}, but Docker is not available ` +
|
|
1325
|
+
`to run '${commandName}' inside the installed node. Start Docker Desktop and retry.`
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
const result = spawnSync(
|
|
1329
|
+
dockerCommand,
|
|
1330
|
+
[
|
|
1331
|
+
"compose",
|
|
1332
|
+
"--project-name",
|
|
1333
|
+
DEFAULT_PROJECT_NAME,
|
|
1334
|
+
"--env-file",
|
|
1335
|
+
deployment.envPath,
|
|
1336
|
+
"-f",
|
|
1337
|
+
deployment.composePath,
|
|
1338
|
+
"exec",
|
|
1339
|
+
"-T",
|
|
1340
|
+
"kernel",
|
|
1341
|
+
"/app/target/release/wattetheria-client-cli",
|
|
1342
|
+
...forwardedArgsForInstalledNode(commandName, rawArgv)
|
|
1343
|
+
],
|
|
1344
|
+
{ stdio: "inherit" }
|
|
1345
|
+
);
|
|
1346
|
+
if (typeof result.status === "number") {
|
|
1347
|
+
process.exit(result.status);
|
|
1348
|
+
}
|
|
1349
|
+
throw result.error ?? new Error(`Failed to run '${commandName}' inside the installed node`);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1190
1352
|
function forwardToRustBinary(commandName, rawArgv) {
|
|
1191
1353
|
// Only the Rust binary name is allowed here. The bare `wattetheria` name
|
|
1192
1354
|
// resolves to this JS shim on most user PATHs (via the npm bin link), so
|
|
1193
1355
|
// including it would create an infinite spawn loop.
|
|
1194
1356
|
const candidates = [
|
|
1195
1357
|
process.env.WATTETHERIA_CLI_BIN,
|
|
1196
|
-
|
|
1358
|
+
nativePackageRustBinaryPath(),
|
|
1359
|
+
bundledRustBinaryPath(),
|
|
1360
|
+
RUST_CLI_BASE_NAME,
|
|
1197
1361
|
].filter(Boolean);
|
|
1198
1362
|
|
|
1199
1363
|
for (const candidate of candidates) {
|
|
@@ -1211,11 +1375,13 @@ function forwardToRustBinary(commandName, rawArgv) {
|
|
|
1211
1375
|
}
|
|
1212
1376
|
}
|
|
1213
1377
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1378
|
+
const deployment = deploymentState();
|
|
1379
|
+
if (deployment.runnable) {
|
|
1380
|
+
runInstalledNodeCli(commandName, rawArgv, deployment);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
throw missingNativeCliError(commandName);
|
|
1219
1385
|
}
|
|
1220
1386
|
|
|
1221
1387
|
async function run(argv) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wattetheria",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Wattetheria deployment CLI",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
7
|
-
"bin":
|
|
7
|
+
"bin": {
|
|
8
|
+
"wattetheria": "bin/wattetheria.js"
|
|
9
|
+
},
|
|
8
10
|
"files": [
|
|
9
11
|
"bin/",
|
|
10
12
|
"lib/",
|
|
@@ -19,8 +21,17 @@
|
|
|
19
21
|
"publishConfig": {
|
|
20
22
|
"access": "public"
|
|
21
23
|
},
|
|
24
|
+
"optionalDependencies": {
|
|
25
|
+
"@wattetheria/cli-darwin-arm64": "0.3.0",
|
|
26
|
+
"@wattetheria/cli-darwin-x64": "0.3.0",
|
|
27
|
+
"@wattetheria/cli-linux-arm64": "0.3.0",
|
|
28
|
+
"@wattetheria/cli-linux-x64": "0.3.0",
|
|
29
|
+
"@wattetheria/cli-win32-arm64": "0.3.0",
|
|
30
|
+
"@wattetheria/cli-win32-x64": "0.3.0"
|
|
31
|
+
},
|
|
22
32
|
"scripts": {
|
|
23
|
-
"check": "node --check bin/wattetheria.js && node --check lib/cli.js"
|
|
33
|
+
"check": "node --check bin/wattetheria.js && node --check lib/cli.js && node --check scripts/stage-native-cli.js && node --check scripts/check-native-package-binary.js",
|
|
34
|
+
"stage:native-cli": "node scripts/stage-native-cli.js"
|
|
24
35
|
},
|
|
25
36
|
"keywords": [
|
|
26
37
|
"wattetheria",
|