cyberia 3.2.9 → 3.2.22

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 (184) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +7 -0
  2. package/.github/workflows/engine-cyberia.ci.yml +14 -2
  3. package/.github/workflows/ghpkg.ci.yml +1 -0
  4. package/.github/workflows/npmpkg.ci.yml +10 -5
  5. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  6. package/.github/workflows/release.cd.yml +1 -0
  7. package/.vscode/extensions.json +9 -9
  8. package/.vscode/settings.json +20 -4
  9. package/CHANGELOG.md +363 -1
  10. package/CLI-HELP.md +975 -1061
  11. package/README.md +190 -348
  12. package/bin/build.js +102 -125
  13. package/bin/build.template.js +33 -0
  14. package/bin/cyberia.js +238 -56
  15. package/bin/deploy.js +16 -3
  16. package/bin/index.js +238 -56
  17. package/bump.config.js +26 -0
  18. package/conf.js +131 -24
  19. package/deployment.yaml +76 -2
  20. package/hardhat/package-lock.json +113 -144
  21. package/hardhat/package.json +4 -3
  22. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  23. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  24. package/manifests/deployment/dd-cyberia-development/deployment.yaml +76 -2
  25. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  26. package/manifests/kind-config-dev.yaml +8 -0
  27. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  28. package/manifests/mongodb/pv-pvc.yaml +44 -8
  29. package/manifests/mongodb/statefulset.yaml +55 -68
  30. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  31. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  32. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  33. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  34. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  35. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  36. package/manifests/valkey/statefulset.yaml +1 -1
  37. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  38. package/package.json +31 -19
  39. package/scripts/ipxe-setup.sh +52 -49
  40. package/scripts/k3s-node-setup.sh +81 -46
  41. package/scripts/link-local-underpost-cli.sh +6 -0
  42. package/scripts/lxd-vm-setup.sh +193 -8
  43. package/scripts/maas-nat-firewalld.sh +145 -0
  44. package/scripts/test-monitor.sh +250 -0
  45. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  46. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  47. package/src/api/core/core.router.js +19 -14
  48. package/src/api/core/core.service.js +5 -5
  49. package/src/api/crypto/crypto.router.js +18 -12
  50. package/src/api/crypto/crypto.service.js +3 -3
  51. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  52. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  53. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  55. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  56. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  57. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  58. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  59. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  60. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  61. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  62. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  63. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  64. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  65. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  66. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  67. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  68. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  69. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  70. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  71. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  72. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  73. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  74. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  75. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  76. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +458 -0
  77. package/src/api/default/default.router.js +22 -18
  78. package/src/api/default/default.service.js +5 -5
  79. package/src/api/document/document.router.js +28 -23
  80. package/src/api/document/document.service.js +100 -23
  81. package/src/api/file/file.router.js +19 -13
  82. package/src/api/file/file.service.js +9 -7
  83. package/src/api/instance/instance.router.js +29 -24
  84. package/src/api/instance/instance.service.js +6 -6
  85. package/src/api/ipfs/ipfs.router.js +21 -16
  86. package/src/api/ipfs/ipfs.service.js +8 -8
  87. package/src/api/object-layer/object-layer.router.js +512 -507
  88. package/src/api/object-layer/object-layer.service.js +17 -14
  89. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  90. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  91. package/src/api/test/test.router.js +17 -12
  92. package/src/api/types.js +24 -0
  93. package/src/api/user/guest.service.js +5 -4
  94. package/src/api/user/user.router.js +297 -288
  95. package/src/api/user/user.service.js +100 -35
  96. package/src/cli/baremetal.js +132 -101
  97. package/src/cli/cluster.js +700 -232
  98. package/src/cli/db.js +59 -60
  99. package/src/cli/deploy.js +291 -294
  100. package/src/cli/env.js +1 -4
  101. package/src/cli/fs.js +13 -3
  102. package/src/cli/image.js +58 -4
  103. package/src/cli/index.js +127 -15
  104. package/src/cli/ipfs.js +4 -6
  105. package/src/cli/kubectl.js +4 -1
  106. package/src/cli/lxd.js +1099 -223
  107. package/src/cli/monitor.js +396 -9
  108. package/src/cli/release.js +355 -146
  109. package/src/cli/repository.js +169 -30
  110. package/src/cli/run.js +347 -117
  111. package/src/cli/secrets.js +11 -2
  112. package/src/cli/test.js +9 -3
  113. package/src/client/Default.index.js +9 -3
  114. package/src/client/components/core/Auth.js +5 -0
  115. package/src/client/components/core/ClientEvents.js +76 -0
  116. package/src/client/components/core/EventBus.js +4 -0
  117. package/src/client/components/core/Modal.js +82 -41
  118. package/src/client/components/core/PanelForm.js +14 -10
  119. package/src/client/components/core/Worker.js +162 -363
  120. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  121. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  122. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +55 -1
  123. package/src/client/public/cyberia-docs/ARCHITECTURE.md +223 -361
  124. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  125. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  126. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +212 -185
  127. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  128. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  129. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +23 -1
  130. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  131. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  132. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  133. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  134. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  135. package/src/client/sw/core.sw.js +174 -112
  136. package/src/db/DataBaseProvider.js +115 -15
  137. package/src/db/mariadb/MariaDB.js +2 -1
  138. package/src/db/mongo/MongoBootstrap.js +657 -0
  139. package/src/db/mongo/MongooseDB.js +130 -21
  140. package/src/grpc/cyberia/grpc-server.js +25 -57
  141. package/src/index.js +1 -1
  142. package/src/runtime/cyberia-client/Dockerfile +10 -7
  143. package/src/runtime/cyberia-client/Dockerfile.dev +67 -0
  144. package/src/runtime/cyberia-server/Dockerfile +11 -6
  145. package/src/runtime/cyberia-server/Dockerfile.dev +47 -0
  146. package/src/runtime/express/Express.js +2 -2
  147. package/src/runtime/wp/Dockerfile +3 -3
  148. package/src/runtime/wp/Wp.js +8 -5
  149. package/src/server/auth.js +2 -2
  150. package/src/server/catalog-underpost.js +61 -0
  151. package/src/server/catalog.js +77 -0
  152. package/src/server/client-build-docs.js +1 -1
  153. package/src/server/client-build.js +94 -129
  154. package/src/server/conf.js +496 -135
  155. package/src/server/ipfs-client.js +5 -3
  156. package/src/server/process.js +180 -19
  157. package/src/server/proxy.js +9 -2
  158. package/src/server/runtime-status.js +235 -0
  159. package/src/server/runtime.js +1 -1
  160. package/src/server/start.js +44 -11
  161. package/src/server/valkey.js +2 -0
  162. package/src/ws/IoInterface.js +16 -16
  163. package/src/ws/core/channels/core.ws.chat.js +11 -11
  164. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  165. package/src/ws/core/channels/core.ws.stream.js +19 -19
  166. package/src/ws/core/core.ws.connection.js +8 -8
  167. package/src/ws/core/core.ws.server.js +6 -5
  168. package/src/ws/default/channels/default.ws.main.js +10 -10
  169. package/src/ws/default/default.ws.connection.js +4 -4
  170. package/src/ws/default/default.ws.server.js +4 -3
  171. package/test/deploy-monitor.test.js +251 -0
  172. package/bin/file.js +0 -202
  173. package/bin/vs.js +0 -74
  174. package/bin/zed.js +0 -84
  175. package/manifests/deployment/dd-test-development/deployment.yaml +0 -254
  176. package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
  177. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  178. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  179. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  180. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  181. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  182. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  183. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  184. /package/src/client/ssr/{pages → views}/Test.js +0 -0
package/src/cli/env.js CHANGED
@@ -95,10 +95,7 @@ class UnderpostRootEnv {
95
95
  get(key, value, options = { plain: false, disableLog: false, copy: false }) {
96
96
  const exeRootPath = `${getNpmRootPath()}/underpost`;
97
97
  const envPath = `${exeRootPath}/.env`;
98
- if (!fs.existsSync(envPath) || !fs.statSync(envPath).isFile()) {
99
- logger.warn(`Empty environment variables`);
100
- return undefined;
101
- }
98
+ if (!fs.existsSync(envPath) || !fs.statSync(envPath).isFile()) return undefined;
102
99
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
103
100
  if (!options.disableLog)
104
101
  options?.plain === true ? console.log(env[key]) : logger.info(`${key}(${typeof env[key]})`, env[key]);
package/src/cli/fs.js CHANGED
@@ -127,7 +127,11 @@ class UnderpostFileStorage {
127
127
  if (options.git === true) {
128
128
  const gitPath = hasPathFilter ? basePath : '.';
129
129
  shellExec(`cd ${gitPath} && git add .`);
130
- shellExec(`underpost cmt ${gitPath} feat`);
130
+ shellExec(`underpost cmt ${gitPath} feat`, {
131
+ silentOnError: true,
132
+ silent: true,
133
+ disableLog: true,
134
+ });
131
135
  }
132
136
 
133
137
  return;
@@ -154,7 +158,9 @@ class UnderpostFileStorage {
154
158
  // For bundle pulls into ./build the git step is unwanted and would error on a non-repo path.
155
159
  if (options.git === true) {
156
160
  Underpost.repo.initLocalRepo({ path });
157
- shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`);
161
+ shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`, {
162
+ silentOnError: true,
163
+ });
158
164
  }
159
165
  } else {
160
166
  const files =
@@ -173,7 +179,11 @@ class UnderpostFileStorage {
173
179
  Underpost.fs.writeStorageConf(storage, storageConf);
174
180
  if (options.git === true) {
175
181
  shellExec(`cd ${path} && git add .`);
176
- shellExec(`underpost cmt ${path} feat`);
182
+ shellExec(`underpost cmt ${path} feat`, {
183
+ silentOnError: true,
184
+ silent: true,
185
+ disableLog: true,
186
+ });
177
187
  }
178
188
  },
179
189
  /**
package/src/cli/image.js CHANGED
@@ -122,6 +122,63 @@ class UnderpostImage {
122
122
  else if (kubeadm === true) shellExec(`sudo ctr -n k8s.io images import ${tarFile}`);
123
123
  else if (k3s === true) shellExec(`sudo k3s ctr images import ${tarFile}`);
124
124
  },
125
+ /**
126
+ * @method getCurrentLoaded
127
+ * @description Retrieves the currently loaded images in the Kubernetes cluster.
128
+ * @param {string} [node='kind-worker'] - Node name to check for loaded images.
129
+ * @param {object} options - Options for the image retrieval.
130
+ * @param {boolean} options.spec - Whether to retrieve images from the pod specifications.
131
+ * @param {string} options.namespace - Kubernetes namespace to filter pods.
132
+ * @returns {Array<object>} - Array of objects containing pod names and their corresponding images.
133
+ * @memberof UnderpostImage
134
+ */
135
+ getCurrentLoaded(node = 'kind-worker', options = { spec: false, namespace: '' }) {
136
+ if (options.spec) {
137
+ const raw = shellExec(
138
+ `kubectl get pods ${options.namespace ? `--namespace ${options.namespace}` : `--all-namespaces`} -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
139
+ {
140
+ stdout: true,
141
+ silent: true,
142
+ },
143
+ );
144
+ return raw
145
+ .split(`\n`)
146
+ .map((lines) => ({
147
+ pod: lines.split('\t')[0].replaceAll(':', '').trim(),
148
+ image: lines.split('\t')[1] ? lines.split('\t')[1].replaceAll(',', '').trim() : null,
149
+ }))
150
+ .filter((o) => o.image);
151
+ }
152
+ const raw = shellExec(node === 'kind-worker' ? `docker exec -i ${node} crictl images` : `crictl images`, {
153
+ stdout: true,
154
+ silent: true,
155
+ });
156
+
157
+ const heads = raw
158
+ .split(`\n`)[0]
159
+ .split(' ')
160
+ .filter((_r) => _r.trim());
161
+
162
+ const pods = raw
163
+ .split(`\n`)
164
+ .filter((r) => !r.match('IMAGE'))
165
+ .map((r) => r.split(' ').filter((_r) => _r.trim()));
166
+
167
+ const result = [];
168
+
169
+ for (const row of pods) {
170
+ if (row.length === 0) continue;
171
+ const pod = {};
172
+ let index = -1;
173
+ for (const head of heads) {
174
+ if (head in pod) continue;
175
+ index++;
176
+ pod[head] = row[index];
177
+ }
178
+ result.push(pod);
179
+ }
180
+ return result;
181
+ },
125
182
  /**
126
183
  * @method list
127
184
  * @description Lists currently loaded Docker images in the specified Kubernetes cluster node.
@@ -139,10 +196,7 @@ class UnderpostImage {
139
196
  list(options = { nodeName: '', namespace: '', spec: false, log: false, k3s: false, kubeadm: false, kind: false }) {
140
197
  if ((options.kubeadm === true || options.k3s === true) && !options.nodeName)
141
198
  options.nodeName = shellExec('echo $HOSTNAME', { stdout: true, silent: true }).trim();
142
- const list = Underpost.deploy.getCurrentLoadedImages(
143
- options.nodeName ? options.nodeName : 'kind-worker',
144
- options,
145
- );
199
+ const list = Underpost.image.getCurrentLoaded(options.nodeName ? options.nodeName : 'kind-worker', options);
146
200
  if (options.log) console.table(list);
147
201
  return list;
148
202
  },
package/src/cli/index.js CHANGED
@@ -70,6 +70,10 @@ program
70
70
  '--pull-bundle',
71
71
  'Downloads the pre-built client bundle from Cloudinary via pull-bundle before starting. Use together with --skip-full-build to skip the local build entirely.',
72
72
  )
73
+ .option(
74
+ '--private-test-repo',
75
+ 'During --build, clone the private test source repo (engine-test-<id>) instead of the production engine-<id> repo.',
76
+ )
73
77
  .action(Underpost.start.callback)
74
78
  .description('Initiates application servers, build pipelines, or other defined services based on the deployment ID.');
75
79
 
@@ -124,6 +128,10 @@ program
124
128
  '--is-remote-repo <url-repo>',
125
129
  'Checks whether a remote Git repository URL is reachable. Prints true or false.',
126
130
  )
131
+ .option(
132
+ '--has-changes',
133
+ 'Prints "1" if there are staged or unstaged git changes in the repository, empty string otherwise.',
134
+ )
127
135
  .description('Manages commits to a GitHub repository, supporting various commit types and options.')
128
136
  .action(Underpost.repo.commit);
129
137
 
@@ -242,15 +250,27 @@ program
242
250
  .command('cluster')
243
251
  .argument('[pod-name]', 'Optional: Filters information by a specific pod name.')
244
252
  .option('--reset', `Deletes all clusters and prunes all related data and caches.`)
253
+ .option(
254
+ '--reset-mongodb',
255
+ `Performs a hard cleanup of only MongoDB-related resources (StatefulSet, PVCs/PVs, Secrets, ConfigMaps, caches) without restarting the whole node.`,
256
+ )
245
257
  .option('--mariadb', 'Initializes the cluster with a MariaDB statefulset.')
246
258
  .option('--mysql', 'Initializes the cluster with a MySQL statefulset.')
247
259
  .option('--mongodb', 'Initializes the cluster with a MongoDB statefulset.')
248
- .option('--mongo-db-host <host>', 'Set custom mongo db host')
260
+ .option('--service-host <host>', 'Set custom host/IP for exposed MongoDB and Valkey clients.')
249
261
  .option('--postgresql', 'Initializes the cluster with a PostgreSQL statefulset.')
250
262
  .option('--mongodb4', 'Initializes the cluster with a MongoDB 4.4 service.')
251
263
  .option('--valkey', 'Initializes the cluster with a Valkey service.')
252
264
  .option('--ipfs', 'Initializes the cluster with an ipfs-cluster statefulset.')
253
265
  .option('--contour', 'Initializes the cluster with Project Contour base HTTPProxy and Envoy.')
266
+ .option(
267
+ '--node-port',
268
+ 'Exposes enabled ready services (e.g. MongoDB 4.4, Valkey) to the host/public network via their NodePort Service manifest.',
269
+ )
270
+ .option(
271
+ '--node-selector <k8s-node-name>',
272
+ 'Pins the just-deployed StatefulSet (MongoDB 4.4 / Valkey) to the given Kubernetes node once it is ready (via a kubernetes.io/hostname nodeSelector).',
273
+ )
254
274
  .option('--cert-manager', "Initializes the cluster with a Let's Encrypt production ClusterIssuer.")
255
275
  .option('--dedicated-gpu', 'Initializes the cluster with dedicated GPU base resources and environment settings.')
256
276
  .option(
@@ -281,6 +301,10 @@ program
281
301
  .option('--k3s', 'Initializes the cluster using K3s (Lightweight Kubernetes).')
282
302
  .option('--hosts <hosts>', 'A comma-separated list of cluster hostnames or IP addresses.')
283
303
  .option('--remove-volume-host-paths', 'Removes specified volume host paths after execution.')
304
+ .option(
305
+ '--reset-mode <mode>',
306
+ 'Reset mode for --reset --k3s: "drain" (stop services, keep K3s installed) or "full" (uninstall + cleanup). Default: "full".',
307
+ )
284
308
  .option('--namespace <namespace>', 'Kubernetes namespace for cluster operations (defaults to "default").')
285
309
  .option('--replicas <replicas>', 'Sets a custom number of replicas for statefulset deployments.')
286
310
  .action(Underpost.cluster.init)
@@ -299,6 +323,12 @@ program
299
323
  .option('--expose', 'Exposes services matching the provided deployment ID list.')
300
324
  .option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
301
325
  .option('--cert-hosts <hosts>', 'Resets TLS/SSL certificate secrets for specified hosts.')
326
+ .option(
327
+ '--self-signed',
328
+ 'Use a pre-created self-signed TLS secret (kubernetes.io/tls) instead of cert-manager. ' +
329
+ 'The secret must already exist in the namespace with the same name as the host. ' +
330
+ 'Enables TLS in the Contour HTTPProxy virtualhost without requiring a production ClusterIssuer.',
331
+ )
302
332
  .option('--node <node>', 'Sets optional node for deployment operations.')
303
333
  .option(
304
334
  '--build-manifest',
@@ -316,6 +346,8 @@ program
316
346
  .option('--retry-count <count>', 'Sets HTTPProxy per-route retry count (e.g., 3).')
317
347
  .option('--retry-per-try-timeout <duration>', 'Sets HTTPProxy retry per-try timeout (e.g., "150ms").')
318
348
  .option('--disable-update-deployment', 'Disables updates to deployments.')
349
+ .option('--disable-runtime-probes', 'Omits the internal-status HTTP probes from generated deployment manifests.')
350
+ .option('--tcp-probes', 'Generates legacy TCP socket probes instead of HTTP internal-status probes (migration).')
319
351
  .option('--disable-update-proxy', 'Disables updates to proxies.')
320
352
  .option('--disable-deployment-proxy', 'Disables proxies of deployments.')
321
353
  .option('--disable-update-volume', 'Disables updates to volume mounts during deployment.')
@@ -327,8 +359,6 @@ program
327
359
  .option('--k3s', 'Enables the k3s context for deployment operations.')
328
360
  .option('--kind', 'Enables the kind context for deployment operations.')
329
361
  .option('--git-clean', 'Runs git clean on volume mount paths before copying.')
330
- .option('--etc-hosts', 'Enables the etc-hosts context for deployment operations.')
331
- .option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
332
362
  .option('--disable-update-underpost-config', 'Disables updates to Underpost configuration during deployment.')
333
363
  .option('--namespace <namespace>', 'Kubernetes namespace for deployment operations (defaults to "default").')
334
364
  .option('--kind-type <kind-type>', 'Specifies the Kind cluster type for deployment operations.')
@@ -337,6 +367,14 @@ program
337
367
  '--expose-port <port>',
338
368
  'Sets the local:remote port to expose when --expose is active (overrides auto-detected service port).',
339
369
  )
370
+ .option(
371
+ '--expose-local-port <port>',
372
+ 'Sets a different local port for --expose (e.g. 80) while keeping the remote service port. Useful for /etc/hosts local access without specifying a port in the browser.',
373
+ )
374
+ .option(
375
+ '--local-proxy',
376
+ 'Forward all service TCP ports locally and start the Node.js path-routing proxy. Enables full path-based routing (e.g. /wp alongside /) without needing --expose-local-port. Requires --expose.',
377
+ )
340
378
  .option('--cmd <cmd>', 'Custom initialization command for deployment (comma-separated commands).')
341
379
  .option(
342
380
  '--skip-full-build',
@@ -346,6 +384,16 @@ program
346
384
  '--pull-bundle',
347
385
  'Explicitly pull the pre-built client bundle from Cloudinary inside the container. Use together with --skip-full-build.',
348
386
  )
387
+ .option(
388
+ '--image-pull-policy <policy>',
389
+ 'Override container imagePullPolicy in the generated deployment manifest (Always, IfNotPresent, Never). Defaults to Never for localhost/ images and IfNotPresent otherwise.',
390
+ )
391
+ .option(
392
+ '--tls',
393
+ 'Enables TLS for the local proxy started by --expose --local-proxy. ' +
394
+ 'The proxy will serve HTTPS on port 443 using self-signed certificates resolved from the local SSL store. ' +
395
+ 'Use together with --expose and --local-proxy.',
396
+ )
349
397
  .description('Manages application deployments, defaulting to deploying development pods.')
350
398
  .action(Underpost.deploy.callback);
351
399
 
@@ -682,13 +730,37 @@ program
682
730
  '--pull-bundle',
683
731
  'Explicitly download the pre-built client bundle from Cloudinary inside the container (supported by: sync, template-deploy). Use together with --skip-full-build.',
684
732
  )
733
+ .option('--remove', 'Remove/teardown resources')
734
+ .option(
735
+ '--test',
736
+ 'Enables test/generic-purpose mode for the runner (e.g. use self-signed TLS instead of cert-manager).',
737
+ )
685
738
  .description('Runs specified scripts using various runners.')
686
739
  .action(Underpost.run.callback);
687
740
 
688
741
  program
689
742
  .command('lxd')
743
+ .argument(
744
+ '[vm-id]',
745
+ 'VM identifier shared by current-VM flags like --vm-create, --vm-delete, --vm-init, --vm-info, and --vm-test.',
746
+ )
690
747
  .option('--init', 'Initializes LXD on the current machine via preseed.')
691
- .option('--reset', 'Removes the LXD snap and purges all data.')
748
+ .option(
749
+ '--reset',
750
+ 'Host-safe reset: removes proxy devices, stops/deletes VMs, drops admin-profile and lxdbr0. Does NOT touch the LXD snap or storage pools.',
751
+ )
752
+ .option(
753
+ '--purge',
754
+ 'DESTRUCTIVE: gracefully shuts down the LXD daemon (60s timeout), then removes the LXD snap. Combine with --reset to wipe per-VM state first. Safe replacement for the prior aggressive teardown.',
755
+ )
756
+ .option(
757
+ '--shutdown',
758
+ 'Pre-host-reboot procedure: gracefully stops every VM and the LXD daemon. Run BEFORE any reboot/poweroff to keep the host bootable.',
759
+ )
760
+ .option(
761
+ '--restore',
762
+ 'Symmetric to --shutdown: starts the LXD daemon, waits for it to be responsive, then starts every VM. VMs created via admin-profile have boot.autostart=false, so this is the explicit "bring the lab back up" command.',
763
+ )
692
764
  .option('--install', 'Installs the LXD snap.')
693
765
  .option('--dev', 'Use local paths instead of the global npm installation.')
694
766
  .option('--create-virtual-network', 'Creates the lxdbr0 bridge network.')
@@ -696,25 +768,48 @@ program
696
768
  .option('--create-admin-profile', 'Creates the admin-profile for VM management.')
697
769
  .option('--control', 'Initialize the target VM as a K3s control plane node.')
698
770
  .option('--worker', 'Initialize the target VM as a K3s worker node.')
699
- .option('--create-vm <vm-name>', 'Copy the LXC launch command for a new K3s VM to the clipboard.')
700
- .option('--delete-vm <vm-name>', 'Stop and delete the specified VM.')
701
- .option('--init-vm <vm-name>', 'Run k3s-node-setup.sh on the specified VM (use with --control or --worker).')
702
- .option('--info-vm <vm-name>', 'Display full configuration and status for the specified VM.')
703
- .option('--test <vm-name>', 'Run connectivity and health checks on the specified VM.')
704
- .option('--root-size <gb-size>', 'Root disk size in GiB for --create-vm (default: 32).')
771
+ .option('--vm-create', 'Copy the LXC launch command for the command argument [vm-id] to the clipboard.')
772
+ .option(
773
+ '--vm-delete',
774
+ 'SAFELY stop and delete the command argument [vm-id] (removes proxy devices first, then stops, then deletes). Safe to re-run.',
775
+ )
776
+ .option(
777
+ '--vm-init',
778
+ 'Bring the command argument [vm-id] up as a K3s node end-to-end: OS base setup, mirror /home/dd/engine into the VM, then K3s role install via the local engine (use with --control or --worker).',
779
+ )
780
+ .option('--vm-info', 'Display full configuration and status for the command argument [vm-id].')
781
+ .option('--vm-test', 'Run connectivity and health checks on the command argument [vm-id].')
782
+ .option(
783
+ '--vm-sync-engine',
784
+ 'Re-copy the host engine source into the command argument [vm-id], overriding whatever is currently there (equivalent to the engine-bootstrap step of --vm-init in isolation).',
785
+ )
786
+ .option('--root-size <gb-size>', 'Root disk size in GiB for --vm-create (default: 32).')
705
787
  .option(
706
788
  '--join-node <nodes>',
707
789
  'Join a K3s worker to a control plane. Standalone format: "workerName,controlName". ' +
708
- 'When used with --init-vm --worker, provide just the control node name for auto-join.',
790
+ 'When used with --vm-init --worker, provide just the control node name for auto-join.',
709
791
  )
710
792
  .option('--expose <vm-name:ports>', 'Proxy host ports to a VM (e.g., "k3s-control:80,443").')
793
+ .option(
794
+ '--node-port <port>',
795
+ 'Customizes the VM-side (connect) port for --expose, so the host listens on the given port but proxies to this NodePort inside the VM (e.g. expose host 27017 -> VM NodePort 32017).',
796
+ )
711
797
  .option('--delete-expose <vm-name:ports>', 'Remove proxied ports from a VM (e.g., "k3s-control:80,443").')
712
- .option('--workflow-id <workflow-id>', 'Workflow ID to execute via runWorkflow.')
713
- .option('--vm-id <vm-name>', 'Target VM name for workflow execution.')
714
- .option('--deploy-id <deploy-id>', 'Deployment ID context for workflow execution.')
798
+ .option(
799
+ '--copy',
800
+ 'For two-phase flows that surface a command for the user to execute (e.g. --create-admin-profile phase 1), copy the command to the clipboard instead of printing it to the terminal.',
801
+ )
715
802
  .option('--namespace <namespace>', 'Kubernetes namespace context (defaults to "default").')
803
+ .option(
804
+ '--maas-project <project>',
805
+ 'LXD project managed by MAAS (e.g. "k3s-cluster"). When set, all lxc commands target this project so MAAS enumerates the VMs in its machines UI.',
806
+ )
807
+ .option(
808
+ '--move-to-project',
809
+ 'Stop the [vm-id] VM in the default project, move it to --maas-project, then start it so MAAS picks it up. Requires --maas-project.',
810
+ )
716
811
  .description('Manages LXD virtual machines as K3s nodes (control plane or workers).')
717
- .action(Underpost.lxd.callback);
812
+ .action((vmId, options) => Underpost.lxd.callback(vmId, options));
718
813
 
719
814
  program
720
815
  .command('baremetal [workflow-id]')
@@ -824,6 +919,23 @@ program
824
919
  '--pwa-build',
825
920
  'Runs the pwa-microservices-template update flow: always re-clones, syncs engine sources, installs, builds, and pushes.',
826
921
  )
922
+ .option(
923
+ '--dry-run',
924
+ 'For --build: previews version-bump changes (per-file substitution counts) without writing files or running downstream commands.',
925
+ )
926
+ .option(
927
+ '--mongo-host <host>',
928
+ 'For --build: override DB_HOST in the template .env.example for the smoke test (e.g., "192.168.1.82:27017").',
929
+ )
930
+ .option('--mongo-user <user>', 'For --build: override DB_USER in the template .env.example for the smoke test.')
931
+ .option(
932
+ '--mongo-password <password>',
933
+ 'For --build: override DB_PASSWORD in the template .env.example for the smoke test.',
934
+ )
935
+ .option(
936
+ '--valkey-host <host>',
937
+ 'For --build: override VALKEY_HOST in the template .env.example for the smoke test (e.g., "192.168.1.82").',
938
+ )
827
939
  .description('Release orchestrator for building new versions and deploying releases of the Underpost CLI.')
828
940
  .action(async (version, options) => {
829
941
  if (options.build) return Underpost.release.build(version, options);
package/src/cli/ipfs.js CHANGED
@@ -131,12 +131,10 @@ class UnderpostIPFS {
131
131
  // Apply UDP buffer sysctl on every Kind node so QUIC (used by IPFS) can reach the
132
132
  // recommended 7.5 MB buffer size. Kind nodes are containers and do NOT inherit the
133
133
  // host sysctl values, so this must be set via docker exec on each node directly.
134
- if (!options.kubeadm && !options.k3s) {
135
- logger.info('Applying UDP buffer sysctl on Kind nodes');
136
- shellExec(
137
- `for node in $(kind get nodes); do docker exec $node sysctl -w net.core.rmem_max=7500000 net.core.wmem_max=7500000; done`,
138
- );
139
- }
134
+ shellExec(
135
+ `sudo sysctl -w net.core.rmem_max=7500000
136
+ sudo sysctl -w net.core.wmem_max=7500000`,
137
+ );
140
138
 
141
139
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/ipfs/storage-class.yaml`);
142
140
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/ipfs -n ${options.namespace}`);
@@ -47,9 +47,12 @@ class UnderpostKubectl {
47
47
  * @memberof UnderpostKubectl
48
48
  */
49
49
  get(deployId, kindType = 'pods', namespace = '') {
50
+ // Existence-check style: a missing kubectl context, a non-existent
51
+ // namespace, or no pods matching the filter must return an empty
52
+ // list (not throw). silentOnError keeps the legacy contract.
50
53
  const raw = shellExec(
51
54
  `sudo kubectl get ${kindType}${namespace ? ` -n ${namespace}` : ` --all-namespaces`} -o wide`,
52
- { stdout: true, disableLog: true, silent: true },
55
+ { stdout: true, disableLog: true, silent: true, silentOnError: true },
53
56
  );
54
57
 
55
58
  const heads = raw