wattetheria 0.2.9 → 0.3.1
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/.env.release +1 -1
- package/README.md +43 -5
- package/lib/cli.js +115 -14
- package/package.json +13 -4
- package/scripts/stage-native-cli.js +0 -84
package/.env.release
CHANGED
package/README.md
CHANGED
|
@@ -552,11 +552,49 @@ 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`,
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
555
|
+
Agent commands such as `identity`, `wallet`, and `servicenet` 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 commands before installing the local node.
|
|
578
|
+
|
|
579
|
+
ServiceNet publishing is a two-step flow. First register the provider context with an A2A
|
|
580
|
+
`AgentCard`; the ServiceNet node returns the provider id, the CLI derives the agent id, and the
|
|
581
|
+
CLI saves the provider/card context locally:
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
npx wattetheria servicenet agent-card init
|
|
585
|
+
# or write the template into a specific directory:
|
|
586
|
+
npx wattetheria servicenet agent-card init --out ./agents/hermes
|
|
587
|
+
|
|
588
|
+
npx wattetheria servicenet provider register --card ./agent-card.json
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Then publish through the ServiceNet business command with the returned agent id. The publish
|
|
592
|
+
command reads the saved provider id, ServiceNet URL, endpoint URL, and card path from local
|
|
593
|
+
context instead of asking for repeated flags:
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
npx wattetheria servicenet publish <agent-id>
|
|
597
|
+
```
|
|
560
598
|
|
|
561
599
|
Version commands:
|
|
562
600
|
|
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"]
|
|
@@ -75,8 +76,7 @@ Commands:
|
|
|
75
76
|
Agent subcommands (forwarded to the bundled native CLI when available):
|
|
76
77
|
identity init | show | export-seed
|
|
77
78
|
wallet manage wallet payment accounts
|
|
78
|
-
servicenet provider register
|
|
79
|
-
publish publish an agent card to a watt-servicenet node
|
|
79
|
+
servicenet agent-card init | provider register | publish <agent-id>
|
|
80
80
|
|
|
81
81
|
Options:
|
|
82
82
|
--version, -v Alias for \`version\`
|
|
@@ -506,11 +506,11 @@ function formatCliVersionString() {
|
|
|
506
506
|
|
|
507
507
|
function formatBanner(options) {
|
|
508
508
|
const wordmark = [
|
|
509
|
-
" __ __ _ _
|
|
510
|
-
" \\ \\ / /_ _| |_| |_
|
|
511
|
-
" \\ \\ /\\ / / _` | __| __
|
|
512
|
-
" \\ V V / (_| | |_|
|
|
513
|
-
" \\_/\\_/ \\__,_|\\__|\\__
|
|
509
|
+
" __ __ _ _ _ _ _ ",
|
|
510
|
+
" \\ \\ / /_ _| |_| |_ ___| |_| |__ ___ _ __(_) __ _ ",
|
|
511
|
+
" \\ \\ /\\ / / _` | __| __/ _ \\ __| '_ \\ / _ \\ '__| |/ _` |",
|
|
512
|
+
" \\ V V / (_| | |_| || __/ |_| | | | __/ | | | (_| |",
|
|
513
|
+
" \\_/\\_/ \\__,_|\\__|\\__\\___|\\__|_| |_|\\___|_| |_|\\__,_|"
|
|
514
514
|
].join("\n");
|
|
515
515
|
const subtitle = `${formatReleaseVersionString(options)} - local agent runtime, swarm sync, external agent reach`;
|
|
516
516
|
|
|
@@ -726,6 +726,27 @@ function composeFilePath(options) {
|
|
|
726
726
|
return path.join(options.dir, "docker-compose.yml");
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
function deploymentState(dir = DEFAULT_DEPLOY_DIR) {
|
|
730
|
+
const envPath = path.join(dir, ".env");
|
|
731
|
+
const composePath = path.join(dir, "docker-compose.yml");
|
|
732
|
+
const stateDir = fs.existsSync(envPath)
|
|
733
|
+
? resolveConfiguredPath(
|
|
734
|
+
dir,
|
|
735
|
+
getEnvValue(readEnvFile(envPath), "WATTETHERIA_HOST_STATE_DIR", "./data/wattetheria")
|
|
736
|
+
)
|
|
737
|
+
: path.join(dir, "data", "wattetheria");
|
|
738
|
+
const tokenPath = path.join(stateDir, "control.token");
|
|
739
|
+
return {
|
|
740
|
+
dir,
|
|
741
|
+
envPath,
|
|
742
|
+
composePath,
|
|
743
|
+
stateDir,
|
|
744
|
+
tokenPath,
|
|
745
|
+
installed: fs.existsSync(envPath) || fs.existsSync(composePath) || fs.existsSync(tokenPath),
|
|
746
|
+
runnable: fs.existsSync(envPath) && fs.existsSync(composePath)
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
729
750
|
function readEnvFile(filePath) {
|
|
730
751
|
const envMap = new Map();
|
|
731
752
|
const content = fs.readFileSync(filePath, "utf8");
|
|
@@ -1225,7 +1246,6 @@ const FORWARDED_SUBCOMMANDS = new Set([
|
|
|
1225
1246
|
"identity",
|
|
1226
1247
|
"wallet",
|
|
1227
1248
|
"servicenet",
|
|
1228
|
-
"publish",
|
|
1229
1249
|
]);
|
|
1230
1250
|
|
|
1231
1251
|
function nativePlatformKey(platform = process.platform, arch = process.arch) {
|
|
@@ -1240,6 +1260,27 @@ function rustCliBinaryName(platform = process.platform) {
|
|
|
1240
1260
|
return platform === "win32" ? `${RUST_CLI_BASE_NAME}.exe` : RUST_CLI_BASE_NAME;
|
|
1241
1261
|
}
|
|
1242
1262
|
|
|
1263
|
+
function nativePackageName(platform = process.platform, arch = process.arch) {
|
|
1264
|
+
const platformKey = nativePlatformKey(platform, arch);
|
|
1265
|
+
return platformKey ? `${NATIVE_CLI_PACKAGE_PREFIX}-${platformKey}` : "";
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
function nativePackageRustBinaryPath() {
|
|
1269
|
+
const packageName = nativePackageName();
|
|
1270
|
+
if (!packageName) {
|
|
1271
|
+
return "";
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
try {
|
|
1275
|
+
const manifestPath = require.resolve(`${packageName}/package.json`, {
|
|
1276
|
+
paths: [PACKAGE_ROOT]
|
|
1277
|
+
});
|
|
1278
|
+
return path.join(path.dirname(manifestPath), "bin", rustCliBinaryName());
|
|
1279
|
+
} catch (_error) {
|
|
1280
|
+
return "";
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1243
1284
|
function bundledRustBinaryPath() {
|
|
1244
1285
|
const platformKey = nativePlatformKey();
|
|
1245
1286
|
if (!platformKey) {
|
|
@@ -1248,12 +1289,71 @@ function bundledRustBinaryPath() {
|
|
|
1248
1289
|
return path.join(PACKAGE_ROOT, "bin", "native", platformKey, rustCliBinaryName());
|
|
1249
1290
|
}
|
|
1250
1291
|
|
|
1292
|
+
function missingNativeCliError(commandName) {
|
|
1293
|
+
const platformKey = nativePlatformKey() || `${process.platform}-${process.arch}`;
|
|
1294
|
+
const deployment = deploymentState();
|
|
1295
|
+
if (deployment.installed) {
|
|
1296
|
+
return new Error(
|
|
1297
|
+
`Wattetheria node deployment was found at ${deployment.dir}, but this npm package does not include ` +
|
|
1298
|
+
`the native publisher CLI for ${platformKey}. ` +
|
|
1299
|
+
`Run 'npx wattetheria status' to check the installed node, or update the local node image.`
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
return new Error(
|
|
1303
|
+
`Could not run '${commandName}' because no Wattetheria node deployment or native CLI was found. ` +
|
|
1304
|
+
`Run 'npx wattetheria install' to install the local node, or set WATTETHERIA_CLI_BIN to ` +
|
|
1305
|
+
`the full path of ${rustCliBinaryName()}.`
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function forwardedArgsForInstalledNode(commandName, rawArgv) {
|
|
1310
|
+
const args = [commandName];
|
|
1311
|
+
if (!rawArgv.some((arg) => arg === "--data-dir" || arg.startsWith("--data-dir="))) {
|
|
1312
|
+
args.push("--data-dir", "/var/lib/wattetheria");
|
|
1313
|
+
}
|
|
1314
|
+
args.push(...rawArgv.slice(1));
|
|
1315
|
+
return args;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
function runInstalledNodeCli(commandName, rawArgv, deployment) {
|
|
1319
|
+
const dockerCommand = resolveDockerCommand();
|
|
1320
|
+
if (!dockerCommand) {
|
|
1321
|
+
throw new Error(
|
|
1322
|
+
`Wattetheria node deployment was found at ${deployment.dir}, but Docker is not available ` +
|
|
1323
|
+
`to run '${commandName}' inside the installed node. Start Docker Desktop and retry.`
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
const result = spawnSync(
|
|
1327
|
+
dockerCommand,
|
|
1328
|
+
[
|
|
1329
|
+
"compose",
|
|
1330
|
+
"--project-name",
|
|
1331
|
+
DEFAULT_PROJECT_NAME,
|
|
1332
|
+
"--env-file",
|
|
1333
|
+
deployment.envPath,
|
|
1334
|
+
"-f",
|
|
1335
|
+
deployment.composePath,
|
|
1336
|
+
"exec",
|
|
1337
|
+
"-T",
|
|
1338
|
+
"kernel",
|
|
1339
|
+
"/app/target/release/wattetheria-client-cli",
|
|
1340
|
+
...forwardedArgsForInstalledNode(commandName, rawArgv)
|
|
1341
|
+
],
|
|
1342
|
+
{ stdio: "inherit" }
|
|
1343
|
+
);
|
|
1344
|
+
if (typeof result.status === "number") {
|
|
1345
|
+
process.exit(result.status);
|
|
1346
|
+
}
|
|
1347
|
+
throw result.error ?? new Error(`Failed to run '${commandName}' inside the installed node`);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1251
1350
|
function forwardToRustBinary(commandName, rawArgv) {
|
|
1252
1351
|
// Only the Rust binary name is allowed here. The bare `wattetheria` name
|
|
1253
1352
|
// resolves to this JS shim on most user PATHs (via the npm bin link), so
|
|
1254
1353
|
// including it would create an infinite spawn loop.
|
|
1255
1354
|
const candidates = [
|
|
1256
1355
|
process.env.WATTETHERIA_CLI_BIN,
|
|
1356
|
+
nativePackageRustBinaryPath(),
|
|
1257
1357
|
bundledRustBinaryPath(),
|
|
1258
1358
|
RUST_CLI_BASE_NAME,
|
|
1259
1359
|
].filter(Boolean);
|
|
@@ -1273,12 +1373,13 @@ function forwardToRustBinary(commandName, rawArgv) {
|
|
|
1273
1373
|
}
|
|
1274
1374
|
}
|
|
1275
1375
|
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1376
|
+
const deployment = deploymentState();
|
|
1377
|
+
if (deployment.runnable) {
|
|
1378
|
+
runInstalledNodeCli(commandName, rawArgv, deployment);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
throw missingNativeCliError(commandName);
|
|
1282
1383
|
}
|
|
1283
1384
|
|
|
1284
1385
|
async function run(argv) {
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wattetheria",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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/",
|
|
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
|
-
}
|