cyberia 3.2.9 → 3.2.12

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 (169) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +6 -0
  2. package/.github/workflows/npmpkg.ci.yml +1 -0
  3. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  4. package/.github/workflows/release.cd.yml +1 -0
  5. package/.vscode/extensions.json +9 -9
  6. package/.vscode/settings.json +20 -4
  7. package/CHANGELOG.md +213 -1
  8. package/CLI-HELP.md +92 -23
  9. package/README.md +190 -348
  10. package/bin/build.js +24 -8
  11. package/bin/build.template.js +187 -0
  12. package/bin/cyberia.js +229 -52
  13. package/bin/deploy.js +12 -2
  14. package/bin/index.js +229 -52
  15. package/bump.config.js +26 -0
  16. package/conf.js +130 -24
  17. package/deployment.yaml +4 -2
  18. package/hardhat/package-lock.json +113 -144
  19. package/hardhat/package.json +4 -3
  20. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  21. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  22. package/manifests/deployment/dd-cyberia-development/deployment.yaml +4 -2
  23. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  24. package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
  25. package/manifests/kind-config-dev.yaml +8 -0
  26. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  27. package/manifests/mongodb/pv-pvc.yaml +44 -8
  28. package/manifests/mongodb/statefulset.yaml +55 -68
  29. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  30. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  31. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  32. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  33. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  34. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  35. package/manifests/valkey/statefulset.yaml +1 -1
  36. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  37. package/package.json +27 -15
  38. package/scripts/ipxe-setup.sh +52 -49
  39. package/scripts/k3s-node-setup.sh +81 -46
  40. package/scripts/lxd-vm-setup.sh +193 -8
  41. package/scripts/maas-nat-firewalld.sh +145 -0
  42. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  43. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  44. package/src/api/core/core.router.js +19 -14
  45. package/src/api/core/core.service.js +5 -5
  46. package/src/api/crypto/crypto.router.js +18 -12
  47. package/src/api/crypto/crypto.service.js +3 -3
  48. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  49. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  50. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  51. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  52. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  53. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  55. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  56. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  57. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  58. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  59. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  60. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  61. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  62. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  63. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  64. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  65. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  66. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  67. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  68. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  69. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  70. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  71. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  72. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  73. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +451 -0
  74. package/src/api/default/default.router.js +22 -18
  75. package/src/api/default/default.service.js +5 -5
  76. package/src/api/document/document.router.js +28 -23
  77. package/src/api/document/document.service.js +100 -23
  78. package/src/api/file/file.router.js +19 -13
  79. package/src/api/file/file.service.js +9 -7
  80. package/src/api/instance/instance.router.js +29 -24
  81. package/src/api/instance/instance.service.js +6 -6
  82. package/src/api/ipfs/ipfs.router.js +21 -16
  83. package/src/api/ipfs/ipfs.service.js +8 -8
  84. package/src/api/object-layer/object-layer.router.js +512 -507
  85. package/src/api/object-layer/object-layer.service.js +17 -14
  86. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  87. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  88. package/src/api/test/test.router.js +17 -12
  89. package/src/api/types.js +24 -0
  90. package/src/api/user/guest.service.js +5 -4
  91. package/src/api/user/user.router.js +297 -288
  92. package/src/api/user/user.service.js +100 -35
  93. package/src/cli/baremetal.js +132 -101
  94. package/src/cli/cluster.js +700 -232
  95. package/src/cli/db.js +59 -60
  96. package/src/cli/deploy.js +216 -137
  97. package/src/cli/fs.js +13 -3
  98. package/src/cli/index.js +80 -15
  99. package/src/cli/ipfs.js +4 -6
  100. package/src/cli/kubectl.js +4 -1
  101. package/src/cli/lxd.js +1099 -223
  102. package/src/cli/monitor.js +9 -3
  103. package/src/cli/release.js +334 -140
  104. package/src/cli/repository.js +68 -23
  105. package/src/cli/run.js +193 -49
  106. package/src/cli/secrets.js +11 -2
  107. package/src/cli/test.js +9 -3
  108. package/src/client/Default.index.js +9 -3
  109. package/src/client/components/core/Auth.js +5 -0
  110. package/src/client/components/core/ClientEvents.js +76 -0
  111. package/src/client/components/core/EventBus.js +4 -0
  112. package/src/client/components/core/Modal.js +82 -41
  113. package/src/client/components/core/PanelForm.js +56 -52
  114. package/src/client/components/core/Worker.js +162 -363
  115. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  116. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  117. package/src/client/public/cyberia-docs/ARCHITECTURE.md +50 -410
  118. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  119. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  120. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +203 -185
  121. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  122. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  123. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  124. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  125. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  126. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  127. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  128. package/src/client/sw/core.sw.js +174 -112
  129. package/src/db/DataBaseProvider.js +115 -15
  130. package/src/db/mariadb/MariaDB.js +2 -1
  131. package/src/db/mongo/MongoBootstrap.js +657 -0
  132. package/src/db/mongo/MongooseDB.js +129 -21
  133. package/src/grpc/cyberia/grpc-server.js +25 -57
  134. package/src/index.js +1 -1
  135. package/src/runtime/cyberia-client/Dockerfile +24 -3
  136. package/src/runtime/cyberia-client/Dockerfile.dev +82 -0
  137. package/src/runtime/cyberia-server/Dockerfile +29 -4
  138. package/src/runtime/cyberia-server/Dockerfile.dev +71 -0
  139. package/src/runtime/express/Express.js +2 -2
  140. package/src/runtime/wp/Wp.js +8 -5
  141. package/src/server/auth.js +2 -2
  142. package/src/server/client-build-docs.js +1 -1
  143. package/src/server/client-build.js +94 -129
  144. package/src/server/conf.js +86 -83
  145. package/src/server/process.js +180 -19
  146. package/src/server/proxy.js +9 -2
  147. package/src/server/runtime.js +1 -1
  148. package/src/server/start.js +17 -5
  149. package/src/server/valkey.js +2 -0
  150. package/src/ws/IoInterface.js +16 -16
  151. package/src/ws/core/channels/core.ws.chat.js +11 -11
  152. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  153. package/src/ws/core/channels/core.ws.stream.js +19 -19
  154. package/src/ws/core/core.ws.connection.js +8 -8
  155. package/src/ws/core/core.ws.server.js +6 -5
  156. package/src/ws/default/channels/default.ws.main.js +10 -10
  157. package/src/ws/default/default.ws.connection.js +4 -4
  158. package/src/ws/default/default.ws.server.js +4 -3
  159. package/bin/file.js +0 -202
  160. package/bin/vs.js +0 -74
  161. package/bin/zed.js +0 -84
  162. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  163. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  164. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  165. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  166. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  167. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  168. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  169. /package/src/client/ssr/{pages → views}/Test.js +0 -0
package/bin/cyberia.js CHANGED
@@ -20,7 +20,7 @@ import stringify from 'fast-json-stable-stringify';
20
20
  import { shellExec } from '../src/server/process.js';
21
21
  import { loggerFactory } from '../src/server/logger.js';
22
22
  import { generateBesuManifests, deployBesu, removeBesu } from '../src/server/besu-genesis-generator.js';
23
- import { DataBaseProvider } from '../src/db/DataBaseProvider.js';
23
+ import { DataBaseProviderService } from '../src/db/DataBaseProvider.js';
24
24
  import { loadConfServerJson } from '../src/server/conf.js';
25
25
  import {
26
26
  ObjectLayerEngine,
@@ -45,7 +45,7 @@ import {
45
45
  DefaultCyberiaDialogues,
46
46
  DefaultCyberiaActions,
47
47
  DefaultCyberiaQuests,
48
- } from '../src/client/components/cyberia-portal/CommonCyberiaPortal.js';
48
+ } from '../src/api/cyberia-server-defaults/cyberia-server-defaults.js';
49
49
 
50
50
  /**
51
51
  * Connect to the project MongoDB instance using the standard env / conf layout.
@@ -72,14 +72,14 @@ async function connectDbForChain({ envPath, mongoHost }) {
72
72
 
73
73
  db.host = mongoHost ? mongoHost : db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
74
74
 
75
- await DataBaseProvider.load({
75
+ await DataBaseProviderService.load({
76
76
  apis: ['object-layer'],
77
77
  host,
78
78
  path,
79
79
  db,
80
80
  });
81
81
 
82
- const ObjectLayer = DataBaseProvider.instance[`${host}${path}`].mongoose.models.ObjectLayer;
82
+ const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
83
83
  return { ObjectLayer, host, path };
84
84
  }
85
85
 
@@ -213,7 +213,7 @@ try {
213
213
  db,
214
214
  });
215
215
 
216
- await DataBaseProvider.load({
216
+ await DataBaseProviderService.load({
217
217
  apis: ['object-layer', 'object-layer-render-frames', 'atlas-sprite-sheet', 'file', 'ipfs'],
218
218
  host,
219
219
  path,
@@ -221,16 +221,15 @@ try {
221
221
  });
222
222
 
223
223
  /** @type {import('mongoose').Model} */
224
- const ObjectLayer = DataBaseProvider.instance[`${host}${path}`].mongoose.models.ObjectLayer;
224
+ const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
225
225
  /** @type {import('mongoose').Model} */
226
- const ObjectLayerRenderFrames =
227
- DataBaseProvider.instance[`${host}${path}`].mongoose.models.ObjectLayerRenderFrames;
226
+ const ObjectLayerRenderFrames = DataBaseProviderService.getModel('object-layer-render-frames', { host, path });
228
227
  /** @type {import('mongoose').Model} */
229
- const AtlasSpriteSheet = DataBaseProvider.instance[`${host}${path}`].mongoose.models.AtlasSpriteSheet;
228
+ const AtlasSpriteSheet = DataBaseProviderService.getModel('atlas-sprite-sheet', { host, path });
230
229
  /** @type {import('mongoose').Model} */
231
- const File = DataBaseProvider.instance[`${host}${path}`].mongoose.models.File;
230
+ const File = DataBaseProviderService.getModel('file', { host, path });
232
231
  /** @type {import('mongoose').Model} */
233
- const Ipfs = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Ipfs;
232
+ const Ipfs = DataBaseProviderService.getModel('ipfs', { host, path });
234
233
 
235
234
  if (options.drop) {
236
235
  // Parse comma-separated item IDs for targeted drop; if none provided, drop everything
@@ -1694,7 +1693,7 @@ try {
1694
1693
  }
1695
1694
  }
1696
1695
 
1697
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
1696
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
1698
1697
  },
1699
1698
  )
1700
1699
  .description('Object layer management');
@@ -1749,7 +1748,7 @@ try {
1749
1748
 
1750
1749
  logger.info('instance env', { env: options.envPath, deployId, host, path, db });
1751
1750
 
1752
- await DataBaseProvider.load({
1751
+ await DataBaseProviderService.load({
1753
1752
  apis: [
1754
1753
  'cyberia-instance',
1755
1754
  'cyberia-instance-conf',
@@ -1767,16 +1766,15 @@ try {
1767
1766
  db,
1768
1767
  });
1769
1768
 
1770
- const dbModels = DataBaseProvider.instance[`${host}${path}`].mongoose.models;
1771
- const CyberiaInstance = dbModels.CyberiaInstance;
1772
- const CyberiaInstanceConf = dbModels.CyberiaInstanceConf;
1773
- const CyberiaDialogue = dbModels.CyberiaDialogue;
1774
- const CyberiaMap = dbModels.CyberiaMap;
1775
- const ObjectLayer = dbModels.ObjectLayer;
1776
- const ObjectLayerRenderFrames = dbModels.ObjectLayerRenderFrames;
1777
- const AtlasSpriteSheet = dbModels.AtlasSpriteSheet;
1778
- const File = dbModels.File;
1779
- const Ipfs = dbModels.Ipfs;
1769
+ const CyberiaInstance = DataBaseProviderService.getModel('cyberia-instance', { host, path });
1770
+ const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
1771
+ const CyberiaDialogue = DataBaseProviderService.getModel('cyberia-dialogue', { host, path });
1772
+ const CyberiaMap = DataBaseProviderService.getModel('cyberia-map', { host, path });
1773
+ const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
1774
+ const ObjectLayerRenderFrames = DataBaseProviderService.getModel('object-layer-render-frames', { host, path });
1775
+ const AtlasSpriteSheet = DataBaseProviderService.getModel('atlas-sprite-sheet', { host, path });
1776
+ const File = DataBaseProviderService.getModel('file', { host, path });
1777
+ const Ipfs = DataBaseProviderService.getModel('ipfs', { host, path });
1780
1778
 
1781
1779
  const toBuffer = (value) => {
1782
1780
  if (!value) return null;
@@ -1911,7 +1909,7 @@ try {
1911
1909
  const instance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
1912
1910
  if (!instance) {
1913
1911
  logger.error(`CyberiaInstance with code "${instanceCode}" not found`);
1914
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
1912
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
1915
1913
  process.exit(1);
1916
1914
  }
1917
1915
 
@@ -1978,7 +1976,7 @@ try {
1978
1976
  backupDir,
1979
1977
  exportedFiles: ['cyberia-instance.json', 'cyberia-instance-conf.json'],
1980
1978
  });
1981
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
1979
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
1982
1980
  return;
1983
1981
  }
1984
1982
 
@@ -2293,7 +2291,7 @@ try {
2293
2291
  for (const failure of ipfsPayloadFailures) {
2294
2292
  logger.error('Canonical IPFS payload export failed', failure);
2295
2293
  }
2296
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
2294
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
2297
2295
  process.exit(1);
2298
2296
  }
2299
2297
 
@@ -2372,7 +2370,7 @@ try {
2372
2370
 
2373
2371
  if (!fs.existsSync(backupDir)) {
2374
2372
  logger.error(`Backup directory not found: ${backupDir}`);
2375
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
2373
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
2376
2374
  process.exit(1);
2377
2375
  }
2378
2376
 
@@ -2594,7 +2592,7 @@ try {
2594
2592
  backupDir,
2595
2593
  importedFiles: ['cyberia-instance.json', 'cyberia-instance-conf.json'],
2596
2594
  });
2597
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
2595
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
2598
2596
  return;
2599
2597
  }
2600
2598
 
@@ -3211,7 +3209,106 @@ try {
3211
3209
  logger.error('Specify --export, --import, or --drop flag');
3212
3210
  }
3213
3211
 
3214
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
3212
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
3213
+ });
3214
+
3215
+ // ── client-hints: presentation hints management ──────────────────────────
3216
+ program
3217
+ .command('client-hints [instance-code]')
3218
+ .option('--export [path]', 'Export CyberiaClientHints document to JSON (default: ./client-hints-<code>.json)')
3219
+ .option('--import [path]', 'Upsert CyberiaClientHints from a JSON file')
3220
+ .option('--seed-defaults', 'Upsert canonical presentation-hint defaults for the given instance code')
3221
+ .option('--drop', 'Remove the CyberiaClientHints document for the given instance code')
3222
+ .option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
3223
+ .option('--mongo-host <mongo-host>', 'Mongo host override')
3224
+ .option('--dev', 'Force development environment')
3225
+ .description('Manage per-instance client presentation hints (palette, camera, status icons, interpolation)')
3226
+ .action(async (instanceCode, options = {}) => {
3227
+ try {
3228
+ const envPath =
3229
+ options.envPath || `./engine-private/conf/dd-cyberia/.env.${options.dev ? 'development' : 'production'}`;
3230
+ if (fs.existsSync(envPath)) dotenv.config({ path: envPath, override: true });
3231
+
3232
+ const { CYBERIA_CLIENT_HINTS_DEFAULTS, buildClientHints } =
3233
+ await import('../src/client/components/cyberia/SharedDefaultsCyberia.js');
3234
+
3235
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
3236
+ const host = process.env.DEFAULT_DEPLOY_HOST;
3237
+ const path = process.env.DEFAULT_DEPLOY_PATH;
3238
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
3239
+ if (!fs.existsSync(confServerPath)) throw new Error(`Config not found: ${confServerPath}`);
3240
+ const confServer = loadConfServerJson(confServerPath, { resolve: true });
3241
+ const { db } = confServer[host][path];
3242
+ db.host = options.mongoHost ? options.mongoHost : db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
3243
+
3244
+ await DataBaseProviderService.load({ apis: ['cyberia-client-hints'], host, path, db });
3245
+ const CyberiaClientHints = DataBaseProviderService.getModel('cyberia-client-hints', { host, path });
3246
+
3247
+ if (!instanceCode && !options.seedDefaults) {
3248
+ logger.error('instance-code required for client-hints operations (omit only with --seed-defaults on all)');
3249
+ process.exit(1);
3250
+ }
3251
+
3252
+ if (options.drop) {
3253
+ if (!instanceCode) {
3254
+ logger.error('instance-code required for --drop');
3255
+ process.exit(1);
3256
+ }
3257
+ const result = await CyberiaClientHints.deleteOne({ code: instanceCode });
3258
+ logger.info(`client-hints --drop: removed ${result.deletedCount} document(s) for code="${instanceCode}"`);
3259
+ }
3260
+
3261
+ if (options.seedDefaults) {
3262
+ const codes = instanceCode ? [instanceCode] : [];
3263
+ if (codes.length === 0) {
3264
+ logger.error('instance-code required for --seed-defaults');
3265
+ process.exit(1);
3266
+ }
3267
+ for (const code of codes) {
3268
+ await CyberiaClientHints.findOneAndUpdate(
3269
+ { code },
3270
+ { $setOnInsert: { code, ...CYBERIA_CLIENT_HINTS_DEFAULTS } },
3271
+ { upsert: true, returnDocument: 'after' },
3272
+ );
3273
+ logger.info(`client-hints --seed-defaults: seeded defaults for code="${code}"`);
3274
+ }
3275
+ }
3276
+
3277
+ if (options.import) {
3278
+ const filePath = typeof options.import === 'string' ? options.import : `./client-hints-${instanceCode}.json`;
3279
+ if (!fs.existsSync(filePath)) throw new Error(`Import file not found: ${filePath}`);
3280
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
3281
+ const code = data.code || instanceCode;
3282
+ if (!code) {
3283
+ logger.error('instance-code required (from file.code or CLI argument)');
3284
+ process.exit(1);
3285
+ }
3286
+ await CyberiaClientHints.findOneAndUpdate({ code }, { $set: { code, ...data } }, { upsert: true, new: true });
3287
+ logger.info(`client-hints --import: upserted code="${code}" from ${filePath}`);
3288
+ }
3289
+
3290
+ if (options.export) {
3291
+ if (!instanceCode) {
3292
+ logger.error('instance-code required for --export');
3293
+ process.exit(1);
3294
+ }
3295
+ const doc = await CyberiaClientHints.findOne({ code: instanceCode }).lean();
3296
+ if (!doc) {
3297
+ logger.warn(`No client-hints document found for code="${instanceCode}", exporting defaults`);
3298
+ }
3299
+ const outPath = typeof options.export === 'string' ? options.export : `./client-hints-${instanceCode}.json`;
3300
+ fs.writeFileSync(
3301
+ outPath,
3302
+ JSON.stringify(doc || { code: instanceCode, ...CYBERIA_CLIENT_HINTS_DEFAULTS }, null, 2),
3303
+ );
3304
+ logger.info(`client-hints --export: wrote ${outPath}`);
3305
+ }
3306
+
3307
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
3308
+ } catch (err) {
3309
+ logger.error('client-hints command error:', err);
3310
+ process.exit(1);
3311
+ }
3215
3312
  });
3216
3313
 
3217
3314
  // ── chain: Hyperledger Besu / ERC-1155 lifecycle commands ────────────────
@@ -3411,7 +3508,7 @@ try {
3411
3508
  logger.info(` SHA-256: ${resolved.sha256}`);
3412
3509
 
3413
3510
  // Close the DB connection after resolving
3414
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
3511
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
3415
3512
  } catch (dbErr) {
3416
3513
  logger.error(`Failed to resolve canonical CID from database: ${dbErr.message}`);
3417
3514
  process.exit(1);
@@ -3979,7 +4076,7 @@ try {
3979
4076
  }
3980
4077
 
3981
4078
  try {
3982
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
4079
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
3983
4080
  } catch (_) {
3984
4081
  /* ignore close errors */
3985
4082
  }
@@ -4034,12 +4131,28 @@ try {
4034
4131
  runner
4035
4132
  .command('import-default-items')
4036
4133
  .option('--dev', 'Force development environment (loads .env.development for IPFS localhost, etc.)')
4037
- .description('Import default Object Layer items, skill config, and dialogues into MongoDB')
4134
+ .description('Import default Object Layer items, skill config, dialogues, and client-hints into MongoDB')
4038
4135
  .action(async (options) => {
4136
+ // Pre-flight: every item id referenced by the fallback world must
4137
+ // exist in DefaultCyberiaItems. Drift here causes silent missing
4138
+ // sprites at runtime, so fail loudly before we touch MongoDB.
4139
+ const { auditFallbackItemIds } = await import('../src/api/cyberia-instance/cyberia-fallback-world.js');
4140
+ const missing = auditFallbackItemIds();
4141
+ if (missing.length > 0) {
4142
+ logger.error(
4143
+ 'import-default-items aborted: item ids referenced by defaults are missing from DefaultCyberiaItems:',
4144
+ missing.join(', '),
4145
+ '— add them to cyberia-server-defaults.js before seeding.',
4146
+ );
4147
+ process.exit(1);
4148
+ }
4149
+
4039
4150
  const devFlag = options.dev ? ' --dev' : '';
4151
+ const instanceCode = process.env.INSTANCE_CODE || 'cyberia-main';
4040
4152
  shellExec(`node bin/cyberia ol ${DefaultCyberiaItems.map((e) => e.item.id)} --import${devFlag}`);
4041
4153
  shellExec(`node bin/cyberia run-workflow seed-skill-config${devFlag}`);
4042
4154
  shellExec(`node bin/cyberia run-workflow seed-dialogues${devFlag}`);
4155
+ shellExec(`node bin/cyberia client-hints ${instanceCode} --seed-defaults${devFlag}`);
4043
4156
  });
4044
4157
 
4045
4158
  runner
@@ -4079,10 +4192,10 @@ try {
4079
4192
 
4080
4193
  logger.info('seed-skill-config', { instanceCode, deployId, host, path, db });
4081
4194
 
4082
- await DataBaseProvider.load({ apis: ['cyberia-instance', 'cyberia-instance-conf'], host, path, db });
4195
+ await DataBaseProviderService.load({ apis: ['cyberia-instance', 'cyberia-instance-conf'], host, path, db });
4083
4196
 
4084
- const CyberiaInstance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.CyberiaInstance;
4085
- const CyberiaInstanceConf = DataBaseProvider.instance[`${host}${path}`].mongoose.models.CyberiaInstanceConf;
4197
+ const CyberiaInstance = DataBaseProviderService.getModel('cyberia-instance', { host, path });
4198
+ const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
4086
4199
 
4087
4200
  const instance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
4088
4201
 
@@ -4110,7 +4223,7 @@ try {
4110
4223
  DefaultSkillConfig.map((e) => `${e.triggerItemId} → [${e.logicEventIds.join(', ')}]`),
4111
4224
  );
4112
4225
 
4113
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
4226
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
4114
4227
  });
4115
4228
 
4116
4229
  runner
@@ -4148,9 +4261,9 @@ try {
4148
4261
 
4149
4262
  logger.info('seed-dialogues', { deployId, host, path, db });
4150
4263
 
4151
- await DataBaseProvider.load({ apis: ['cyberia-dialogue'], host, path, db });
4264
+ await DataBaseProviderService.load({ apis: ['cyberia-dialogue'], host, path, db });
4152
4265
 
4153
- const CyberiaDialogue = DataBaseProvider.instance[`${host}${path}`].mongoose.models.CyberiaDialogue;
4266
+ const CyberiaDialogue = DataBaseProviderService.getModel('cyberia-dialogue', { host, path });
4154
4267
 
4155
4268
  // Upsert each dialogue record keyed by (code, order) — idempotent.
4156
4269
  let upserted = 0;
@@ -4165,7 +4278,7 @@ try {
4165
4278
 
4166
4279
  logger.info(`seed-dialogues: ${upserted} dialogue records upserted`);
4167
4280
 
4168
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
4281
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
4169
4282
  });
4170
4283
 
4171
4284
  runner
@@ -4231,25 +4344,89 @@ try {
4231
4344
 
4232
4345
  runner
4233
4346
  .command('build-manifest')
4234
- .description('Build k8s resource manifest YAML files from templates')
4235
- .action(() => {
4236
- shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-client,./cyberia-client' --kubeadm`);
4237
- shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-client,./cyberia-client' --kind --dev`);
4238
- shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-server,./cyberia-server' --kubeadm`);
4239
- shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-server,./cyberia-server' --kind --dev`);
4347
+ .option(
4348
+ '--dev',
4349
+ 'Build dev-variant manifests (kind cluster, Dockerfile.dev). Default builds prod (kubeadm, Dockerfile).',
4350
+ )
4351
+ .description(
4352
+ 'Build k8s resource manifests for the Cyberia mmo-server + mmo-client instances. ' +
4353
+ 'Without --dev: production manifests (Dockerfile, kubeadm). With --dev: dev manifests (Dockerfile.dev, kind).',
4354
+ )
4355
+ .action((options) => {
4356
+ const isDev = !!options.dev;
4357
+ const flags = isDev ? '--kind --dev' : '--kubeadm';
4358
+ // shellExec is fail-fast by default: any non-zero exit throws
4359
+ // ShellExecError, which propagates to the outer catch and exits
4360
+ // the CLI non-zero — observable by GitHub Actions.
4361
+ shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-client,./cyberia-client' ${flags}`);
4362
+ shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-server,./cyberia-server' ${flags}`);
4363
+ // Copy canonical doc sources into the generated project READMEs.
4364
+ // Edit the canonical sources; never hand-edit these generated outputs.
4365
+ fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-CLIENT.md', './cyberia-client/README.md');
4366
+ fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-SERVER.md', './cyberia-server/README.md');
4367
+ logger.info(`run-workflow build-manifest complete (${isDev ? 'dev' : 'prod'})`);
4240
4368
  });
4241
4369
 
4242
- if (underpostProgram.commands.find((c) => c._name == process.argv[2]))
4370
+ runner
4371
+ .command('build-server-dashboard')
4372
+ .option(
4373
+ '--dev',
4374
+ 'Build a development variant of the dashboard with dev-specific env vars (e.g. localhost API endpoints).',
4375
+ )
4376
+ .option(
4377
+ '--output-path <path>',
4378
+ 'Override output path for the rendered HTML (default: ./cyberia-server/public/index.html). ' +
4379
+ 'Used by CI when this command is invoked from inside an engine checkout that lives ' +
4380
+ 'alongside (not inside) the cyberia-server repo — pass e.g. ../public/index.html.',
4381
+ )
4382
+ .description('Build a static HTML dashboard for cyberia-server metrics and operational status. ')
4383
+ .action((options) => {
4384
+ const outputPath = options.outputPath || './cyberia-server/public/index.html';
4385
+ shellExec(
4386
+ `node bin static --page ./src/client/ssr/views/CyberiaServerMetrics.js` +
4387
+ ` --output-path ${outputPath}` +
4388
+ ` --title 'Cyberia Server Metrics'` +
4389
+ ` --favicon /favicon.ico` +
4390
+ ` --description 'Operational dashboard for the cyberia-server MMO runtime.'` +
4391
+ ` --lang en` +
4392
+ ` --env ${options.dev ? 'development' : 'production'}`,
4393
+ );
4394
+ });
4395
+
4396
+ // Passthrough check: if the user invoked a command that is OWNED by the
4397
+ // underpost CLI (not the cyberia overlay), throw the sentinel error so
4398
+ // the catch block below can re-run argv through underpost. The match is
4399
+ // strict on process.argv[2] (the first positional after `node bin/cyberia`)
4400
+ // so we only passthrough when the top-level command name actually
4401
+ // belongs to underpost.
4402
+ if (
4403
+ process.argv[2] &&
4404
+ underpostProgram.commands.find((c) => c._name === process.argv[2]) &&
4405
+ !program.commands.find((c) => c._name === process.argv[2])
4406
+ ) {
4243
4407
  throw new Error('Trigger underpost passthrough');
4408
+ }
4244
4409
 
4245
4410
  program.parse();
4246
4411
  } catch (error) {
4247
- logger.warn(error);
4248
- process.argv = process.argv.filter((c) => c !== 'underpost');
4249
- logger.warn('Rerouting to underpost cli...');
4250
- try {
4251
- underpostProgram.parse();
4252
- } catch (error) {
4412
+ // ONLY reroute on the explicit passthrough sentinel. Any other thrown
4413
+ // error (subprocess non-zero from shellExec's fail-fast default, CLI
4414
+ // parse errors, missing modules) must propagate as a non-zero process
4415
+ // exit so GitHub Actions / CI parents observe the failure. Without this
4416
+ // guard, a genuine build failure was being silently rerouted into the
4417
+ // underpost CLI and then masked behind a misleading "unknown command"
4418
+ // line.
4419
+ if (error && error.message === 'Trigger underpost passthrough') {
4420
+ process.argv = process.argv.filter((c) => c !== 'underpost');
4421
+ logger.warn('Rerouting to underpost cli...');
4422
+ try {
4423
+ underpostProgram.parse();
4424
+ } catch (err) {
4425
+ logger.error(err);
4426
+ process.exit(1);
4427
+ }
4428
+ } else {
4253
4429
  logger.error(error);
4430
+ process.exit(1);
4254
4431
  }
4255
4432
  }
package/bin/deploy.js CHANGED
@@ -714,7 +714,7 @@ nvidia/gpu-operator \
714
714
  }
715
715
 
716
716
  case 'cyberia': {
717
- const { CyberiaDependencies } = await import(`../src/client/components/cyberia-portal/CommonCyberiaPortal.js`);
717
+ const { CyberiaDependencies } = await import(`../src/api/cyberia-server-defaults/cyberia-server-defaults.js`);
718
718
  for (const dep of Object.keys(CyberiaDependencies)) {
719
719
  const ver = CyberiaDependencies[dep];
720
720
  shellExec(`npm install ${dep}@${ver}`);
@@ -823,7 +823,7 @@ nvidia/gpu-operator \
823
823
 
824
824
  // Delete merged local and remote branches
825
825
  for (const { branch, isAlreadyMerged } of mergedBranches) {
826
- shellExec(`git branch -D ${branch}`, { silent: true });
826
+ shellExec(`git branch -D ${branch}`, { silent: true, silentOnError: true });
827
827
  // logger.info(`Deleting remote branch: ${branch}${isAlreadyMerged ? ' (already merged)' : ''}`);
828
828
  // shellExec(`git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}.git --delete ${branch}`, {
829
829
  // disableLog: true,
@@ -1528,6 +1528,16 @@ nvidia/gpu-operator \
1528
1528
 
1529
1529
  break;
1530
1530
  }
1531
+
1532
+ case 'k3s-template-env': {
1533
+ if (fs.existsSync('./engine-private/conf/dd-default')) {
1534
+ console.log('Cleaning up existing dd-default config for VM template environment setup');
1535
+ fs.removeSync('./engine-private/conf/dd-default');
1536
+ }
1537
+ shellExec(`node bin env clean`);
1538
+ shellExec(`sed -i "s/127.0.0.1/$(underpost ip --dhcp)/g" .env.example`);
1539
+ break;
1540
+ }
1531
1541
  }
1532
1542
  } catch (error) {
1533
1543
  logger.error(error, error.stack);