wattetheria 0.2.9 → 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 CHANGED
@@ -553,10 +553,28 @@ CLI prerequisites:
553
553
  The CLI handles image pull, deployment directory setup, environment generation, container start,
554
554
  and health checks internally.
555
555
  Agent commands such as `identity`, `wallet`, `servicenet`, and `publish` are forwarded to the
556
- platform native `wattetheria-client-cli` bundled under `bin/native/<platform>-<arch>/`; installed
557
- release packages should not require Rust on the user's machine. Release publishing can stage a
558
- native CLI binary with `npm run stage:native-cli -- --platform <platform> --arch <arch> --source <path>`.
559
- `WATTETHERIA_CLI_BIN` remains an advanced override for custom local binaries.
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.
560
578
 
561
579
  Version commands:
562
580
 
package/lib/cli.js CHANGED
@@ -30,6 +30,7 @@ const WINDOWS_DOCKER_CANDIDATES = [
30
30
  "C:\\Program Files\\Docker\\cli-plugins\\docker.exe"
31
31
  ];
32
32
  const RUST_CLI_BASE_NAME = "wattetheria-client-cli";
33
+ const NATIVE_CLI_PACKAGE_PREFIX = "@wattetheria/cli";
33
34
  const NATIVE_ARCH_ALIASES = new Map([
34
35
  ["x64", "x64"],
35
36
  ["arm64", "arm64"]
@@ -506,11 +507,11 @@ function formatCliVersionString() {
506
507
 
507
508
  function formatBanner(options) {
508
509
  const wordmark = [
509
- " __ __ _ _ _ _ _ _ ",
510
- " \\ \\ / /_ _| |_| |_| |__ ___| |_| |__ ___ _ __(_) __ _ ",
511
- " \\ \\ /\\ / / _` | __| __| '_ \\ / _ \\ __| '_ \\ / _ \\ '__| |/ _` |",
512
- " \\ V V / (_| | |_| |_| | | | __/ |_| | | | __/ | | | (_| |",
513
- " \\_/\\_/ \\__,_|\\__|\\__|_| |_|\\___|\\__|_| |_|\\___|_| |_|\\__,_|"
510
+ " __ __ _ _ _ _ _ ",
511
+ " \\ \\ / /_ _| |_| |_ ___| |_| |__ ___ _ __(_) __ _ ",
512
+ " \\ \\ /\\ / / _` | __| __/ _ \\ __| '_ \\ / _ \\ '__| |/ _` |",
513
+ " \\ V V / (_| | |_| || __/ |_| | | | __/ | | | (_| |",
514
+ " \\_/\\_/ \\__,_|\\__|\\__\\___|\\__|_| |_|\\___|_| |_|\\__,_|"
514
515
  ].join("\n");
515
516
  const subtitle = `${formatReleaseVersionString(options)} - local agent runtime, swarm sync, external agent reach`;
516
517
 
@@ -726,6 +727,27 @@ function composeFilePath(options) {
726
727
  return path.join(options.dir, "docker-compose.yml");
727
728
  }
728
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
+
729
751
  function readEnvFile(filePath) {
730
752
  const envMap = new Map();
731
753
  const content = fs.readFileSync(filePath, "utf8");
@@ -1240,6 +1262,27 @@ function rustCliBinaryName(platform = process.platform) {
1240
1262
  return platform === "win32" ? `${RUST_CLI_BASE_NAME}.exe` : RUST_CLI_BASE_NAME;
1241
1263
  }
1242
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
+
1243
1286
  function bundledRustBinaryPath() {
1244
1287
  const platformKey = nativePlatformKey();
1245
1288
  if (!platformKey) {
@@ -1248,12 +1291,71 @@ function bundledRustBinaryPath() {
1248
1291
  return path.join(PACKAGE_ROOT, "bin", "native", platformKey, rustCliBinaryName());
1249
1292
  }
1250
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
+
1251
1352
  function forwardToRustBinary(commandName, rawArgv) {
1252
1353
  // Only the Rust binary name is allowed here. The bare `wattetheria` name
1253
1354
  // resolves to this JS shim on most user PATHs (via the npm bin link), so
1254
1355
  // including it would create an infinite spawn loop.
1255
1356
  const candidates = [
1256
1357
  process.env.WATTETHERIA_CLI_BIN,
1358
+ nativePackageRustBinaryPath(),
1257
1359
  bundledRustBinaryPath(),
1258
1360
  RUST_CLI_BASE_NAME,
1259
1361
  ].filter(Boolean);
@@ -1273,12 +1375,13 @@ function forwardToRustBinary(commandName, rawArgv) {
1273
1375
  }
1274
1376
  }
1275
1377
 
1276
- const platformKey = nativePlatformKey() || `${process.platform}-${process.arch}`;
1277
- throw new Error(
1278
- `Could not find the Wattetheria native CLI for ${platformKey}. ` +
1279
- `Install a package that includes bin/native/${platformKey}/${rustCliBinaryName()}, ` +
1280
- `or set WATTETHERIA_CLI_BIN to the full path of ${rustCliBinaryName()}.`
1281
- );
1378
+ const deployment = deploymentState();
1379
+ if (deployment.runnable) {
1380
+ runInstalledNodeCli(commandName, rawArgv, deployment);
1381
+ return;
1382
+ }
1383
+
1384
+ throw missingNativeCliError(commandName);
1282
1385
  }
1283
1386
 
1284
1387
  async function run(argv) {
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "wattetheria",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "description": "Wattetheria deployment CLI",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
7
- "bin": "bin/wattetheria.js",
7
+ "bin": {
8
+ "wattetheria": "bin/wattetheria.js"
9
+ },
8
10
  "files": [
9
11
  "bin/",
10
12
  "lib/",
11
- "scripts/stage-native-cli.js",
12
13
  ".env.release",
13
14
  "docker-compose.release.yml",
14
15
  "README.md",
@@ -20,8 +21,16 @@
20
21
  "publishConfig": {
21
22
  "access": "public"
22
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
+ },
23
32
  "scripts": {
24
- "check": "node --check bin/wattetheria.js && node --check lib/cli.js && node --check scripts/stage-native-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",
25
34
  "stage:native-cli": "node scripts/stage-native-cli.js"
26
35
  },
27
36
  "keywords": [
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require("node:fs");
4
- const os = require("node:os");
5
- const path = require("node:path");
6
-
7
- const ROOT_DIR = path.resolve(__dirname, "..");
8
- const BASE_NAME = "wattetheria-client-cli";
9
- const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"]);
10
- const SUPPORTED_ARCHES = new Set(["x64", "arm64"]);
11
-
12
- function parseArgs(argv) {
13
- const options = {
14
- platform: process.env.WATTETHERIA_NATIVE_PLATFORM || process.platform,
15
- arch: process.env.WATTETHERIA_NATIVE_ARCH || process.arch,
16
- source: process.env.WATTETHERIA_NATIVE_CLI_BIN || "",
17
- };
18
-
19
- for (let index = 0; index < argv.length; index += 1) {
20
- const arg = argv[index];
21
- if (arg === "--platform") {
22
- options.platform = requireValue(arg, argv[++index]);
23
- } else if (arg === "--arch") {
24
- options.arch = requireValue(arg, argv[++index]);
25
- } else if (arg === "--source") {
26
- options.source = requireValue(arg, argv[++index]);
27
- } else {
28
- throw new Error(`Unknown option: ${arg}`);
29
- }
30
- }
31
-
32
- return options;
33
- }
34
-
35
- function requireValue(flag, value) {
36
- if (!value || value.startsWith("-")) {
37
- throw new Error(`Missing value for ${flag}`);
38
- }
39
- return value;
40
- }
41
-
42
- function binaryName(platform) {
43
- return platform === "win32" ? `${BASE_NAME}.exe` : BASE_NAME;
44
- }
45
-
46
- function defaultSource(platform) {
47
- return path.join(ROOT_DIR, "target", "release", binaryName(platform));
48
- }
49
-
50
- function targetKey(platform, arch) {
51
- if (!SUPPORTED_PLATFORMS.has(platform)) {
52
- throw new Error(`Unsupported native CLI platform: ${platform}`);
53
- }
54
- if (!SUPPORTED_ARCHES.has(arch)) {
55
- throw new Error(`Unsupported native CLI arch: ${arch}`);
56
- }
57
- return `${platform}-${arch}`;
58
- }
59
-
60
- function stageNativeCli(options) {
61
- const key = targetKey(options.platform, options.arch);
62
- const source = path.resolve(options.source || defaultSource(options.platform));
63
- if (!fs.existsSync(source)) {
64
- throw new Error(
65
- `Native CLI binary not found at ${source}. Build it first or pass --source.`
66
- );
67
- }
68
-
69
- const targetDir = path.join(ROOT_DIR, "bin", "native", key);
70
- const target = path.join(targetDir, binaryName(options.platform));
71
- fs.mkdirSync(targetDir, { recursive: true });
72
- fs.copyFileSync(source, target);
73
- if (options.platform !== "win32") {
74
- fs.chmodSync(target, 0o755);
75
- }
76
- console.log(`staged ${target}`);
77
- }
78
-
79
- try {
80
- stageNativeCli(parseArgs(process.argv.slice(2)));
81
- } catch (error) {
82
- console.error(error && error.message ? error.message : String(error));
83
- process.exit(1);
84
- }