wattetheria 0.3.2 → 0.3.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.
Files changed (3) hide show
  1. package/.env.release +1 -1
  2. package/lib/cli.js +188 -0
  3. package/package.json +1 -1
package/.env.release CHANGED
@@ -1,5 +1,5 @@
1
1
  # Coordinated release image set
2
- RELEASE_TAG=1.0.3
2
+ RELEASE_TAG=1.0.4
3
3
 
4
4
  WATTETHERIA_KERNEL_IMAGE=ghcr.io/wattetheria/wattetheria-kernel:${RELEASE_TAG}
5
5
  WATTSWARM_KERNEL_IMAGE=ghcr.io/wattetheria/wattswarm-kernel:${RELEASE_TAG}
package/lib/cli.js CHANGED
@@ -1315,6 +1315,159 @@ function forwardedArgsForInstalledNode(commandName, rawArgv) {
1315
1315
  return args;
1316
1316
  }
1317
1317
 
1318
+ function isServicenetAgentCardInit(commandName, rawArgv) {
1319
+ return commandName === "servicenet"
1320
+ && rawArgv[1] === "agent-card"
1321
+ && rawArgv[2] === "init";
1322
+ }
1323
+
1324
+ function isServicenetProviderRegister(commandName, rawArgv) {
1325
+ return commandName === "servicenet"
1326
+ && rawArgv[1] === "provider"
1327
+ && rawArgv[2] === "register";
1328
+ }
1329
+
1330
+ function isServicenetPublish(commandName, rawArgv) {
1331
+ return commandName === "servicenet"
1332
+ && rawArgv[1] === "publish";
1333
+ }
1334
+
1335
+ function usesWorkspaceMount(commandName, rawArgv) {
1336
+ return isServicenetAgentCardInit(commandName, rawArgv)
1337
+ || isServicenetProviderRegister(commandName, rawArgv)
1338
+ || isServicenetPublish(commandName, rawArgv);
1339
+ }
1340
+
1341
+ function findExistingAncestor(targetPath) {
1342
+ let cursor = path.resolve(targetPath);
1343
+ while (!fs.existsSync(cursor)) {
1344
+ const parent = path.dirname(cursor);
1345
+ if (parent === cursor) {
1346
+ throw new Error(`No existing parent directory found for ${targetPath}`);
1347
+ }
1348
+ cursor = parent;
1349
+ }
1350
+ return cursor;
1351
+ }
1352
+
1353
+ function toContainerPath(relativePath) {
1354
+ if (!relativePath || relativePath === ".") {
1355
+ return "/workspace";
1356
+ }
1357
+ return `/workspace/${relativePath.split(path.sep).join("/")}`;
1358
+ }
1359
+
1360
+ function parsePathOption(rawArgv, optionName, startIndex) {
1361
+ const equalsPrefix = `${optionName}=`;
1362
+ for (let index = startIndex; index < rawArgv.length; index += 1) {
1363
+ const arg = rawArgv[index];
1364
+ if (arg === optionName) {
1365
+ return { index, value: rawArgv[index + 1], style: "separate" };
1366
+ }
1367
+ if (arg.startsWith(equalsPrefix)) {
1368
+ return { index, value: arg.slice(equalsPrefix.length), style: "equals" };
1369
+ }
1370
+ }
1371
+ return null;
1372
+ }
1373
+
1374
+ function pathMountFor(targetPath) {
1375
+ const requestedPath = path.resolve(targetPath);
1376
+ const existingPath = fs.existsSync(requestedPath) ? requestedPath : "";
1377
+ const mountSource = existingPath
1378
+ ? (
1379
+ fs.statSync(existingPath).isDirectory()
1380
+ ? existingPath
1381
+ : path.dirname(existingPath)
1382
+ )
1383
+ : findExistingAncestor(requestedPath);
1384
+ return {
1385
+ mountSource,
1386
+ containerPath: toContainerPath(path.relative(mountSource, requestedPath))
1387
+ };
1388
+ }
1389
+
1390
+ function rewritePathOption(rawArgv, parsedOption, containerPath) {
1391
+ const rewrittenArgv = [...rawArgv];
1392
+ if (parsedOption.style === "separate") {
1393
+ rewrittenArgv[parsedOption.index + 1] = containerPath;
1394
+ } else {
1395
+ rewrittenArgv[parsedOption.index] = `${rewrittenArgv[parsedOption.index].split("=")[0]}=${containerPath}`;
1396
+ }
1397
+ return rewrittenArgv;
1398
+ }
1399
+
1400
+ function dockerWorkspaceInvocation(commandName, rawArgv) {
1401
+ if (isServicenetAgentCardInit(commandName, rawArgv)) {
1402
+ const outputArg = parsePathOption(rawArgv, "--out", 3);
1403
+ const outputPath = outputArg?.value || process.cwd();
1404
+ if (outputArg && !outputArg.value) {
1405
+ throw new Error("Missing value for --out");
1406
+ }
1407
+ const outputMount = pathMountFor(outputPath);
1408
+ const rewrittenArgv = outputArg
1409
+ ? rewritePathOption(rawArgv, outputArg, outputMount.containerPath)
1410
+ : rawArgv;
1411
+ return {
1412
+ args: outputArg
1413
+ ? forwardedArgsForInstalledNode(commandName, rewrittenArgv)
1414
+ : [...forwardedArgsForInstalledNode(commandName, rawArgv), "--out", outputMount.containerPath],
1415
+ mountSource: outputMount.mountSource
1416
+ };
1417
+ }
1418
+
1419
+ if (isServicenetProviderRegister(commandName, rawArgv)) {
1420
+ const cardArg = parsePathOption(rawArgv, "--card", 3);
1421
+ if (!cardArg) {
1422
+ return {
1423
+ args: forwardedArgsForInstalledNode(commandName, rawArgv),
1424
+ mountSource: process.cwd()
1425
+ };
1426
+ }
1427
+ if (!cardArg.value) {
1428
+ throw new Error("Missing value for --card");
1429
+ }
1430
+ const cardMount = pathMountFor(cardArg.value);
1431
+ return {
1432
+ args: forwardedArgsForInstalledNode(
1433
+ commandName,
1434
+ rewritePathOption(rawArgv, cardArg, cardMount.containerPath)
1435
+ ),
1436
+ mountSource: cardMount.mountSource
1437
+ };
1438
+ }
1439
+
1440
+ return {
1441
+ args: forwardedArgsForInstalledNode(commandName, rawArgv),
1442
+ mountSource: process.cwd()
1443
+ };
1444
+ }
1445
+
1446
+ function hostPathFromWorkspace(containerPath, mountSource) {
1447
+ if (containerPath === "/workspace") {
1448
+ return mountSource;
1449
+ }
1450
+ if (!containerPath.startsWith("/workspace/")) {
1451
+ return containerPath;
1452
+ }
1453
+ return path.join(mountSource, containerPath.slice("/workspace/".length));
1454
+ }
1455
+
1456
+ function rewriteWorkspaceJsonStdout(stdout, mountSource) {
1457
+ if (!stdout.trim()) {
1458
+ return stdout;
1459
+ }
1460
+ try {
1461
+ const payload = JSON.parse(stdout);
1462
+ if (typeof payload.card === "string") {
1463
+ payload.card = hostPathFromWorkspace(payload.card, mountSource);
1464
+ }
1465
+ return `${JSON.stringify(payload, null, 2)}\n`;
1466
+ } catch (_error) {
1467
+ return stdout;
1468
+ }
1469
+ }
1470
+
1318
1471
  function runInstalledNodeCli(commandName, rawArgv, deployment) {
1319
1472
  const dockerCommand = resolveDockerCommand();
1320
1473
  if (!dockerCommand) {
@@ -1323,6 +1476,41 @@ function runInstalledNodeCli(commandName, rawArgv, deployment) {
1323
1476
  `to run '${commandName}' inside the installed node. Start Docker Desktop and retry.`
1324
1477
  );
1325
1478
  }
1479
+ if (usesWorkspaceMount(commandName, rawArgv)) {
1480
+ const invocation = dockerWorkspaceInvocation(commandName, rawArgv);
1481
+ const result = spawnSync(
1482
+ dockerCommand,
1483
+ [
1484
+ "compose",
1485
+ "--project-name",
1486
+ DEFAULT_PROJECT_NAME,
1487
+ "--env-file",
1488
+ deployment.envPath,
1489
+ "-f",
1490
+ deployment.composePath,
1491
+ "run",
1492
+ "--rm",
1493
+ "--no-deps",
1494
+ "-T",
1495
+ "--entrypoint",
1496
+ "/app/target/release/wattetheria-client-cli",
1497
+ "-v",
1498
+ `${invocation.mountSource}:/workspace`,
1499
+ "-w",
1500
+ "/workspace",
1501
+ "kernel",
1502
+ ...invocation.args
1503
+ ],
1504
+ { stdio: ["inherit", "pipe", "inherit"], encoding: "utf8" }
1505
+ );
1506
+ if (result.stdout) {
1507
+ process.stdout.write(rewriteWorkspaceJsonStdout(result.stdout, invocation.mountSource));
1508
+ }
1509
+ if (typeof result.status === "number") {
1510
+ process.exit(result.status);
1511
+ }
1512
+ throw result.error ?? new Error(`Failed to run '${commandName}' inside the installed node`);
1513
+ }
1326
1514
  const result = spawnSync(
1327
1515
  dockerCommand,
1328
1516
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wattetheria",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Wattetheria deployment CLI",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",