@rubytech/create-maxy-code 0.1.333 → 0.1.334

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/dist/index.js CHANGED
@@ -1564,6 +1564,28 @@ WantedBy=multi-user.target
1564
1564
  spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
1565
1565
  console.log(" [privileged] systemctl enable");
1566
1566
  shell("systemctl", ["enable", serviceName], { sudo: true });
1567
+ // Tear down a legacy-package dedicated Neo4j unit pinning the same port.
1568
+ // The `-code` rewrite packages (`create-<brand>-code`) replace the legacy
1569
+ // `create-<brand>` packages. When migrating a device from legacy to -code,
1570
+ // both packages' dedicated Neo4j units exist (`neo4j-<brand>.service` and
1571
+ // `neo4j-<brand>-code.service`) and both try to bind the same dedicated
1572
+ // bolt port. The legacy unit wins the race (it boots first because it's
1573
+ // alphabetically earlier in systemd's load order), the new unit silently
1574
+ // fails with `bind failed -98 Address already in use`, and the seed step
1575
+ // talks to the legacy instance with the wrong password — auth fails with
1576
+ // a misleading "client is unauthorized" (observed on beacons).
1577
+ if (brandSuffix.endsWith("-code")) {
1578
+ const legacyBrand = brandSuffix.slice(0, -"-code".length);
1579
+ const legacyUnit = `neo4j-${legacyBrand}.service`;
1580
+ const legacyUnitFile = `/etc/systemd/system/${legacyUnit}`;
1581
+ if (existsSync(legacyUnitFile)) {
1582
+ const legacyMsg = ` [neo4j] legacy unit detected (${legacyUnit}) — stopping + disabling so the -code unit owns port ${NEO4J_PORT}`;
1583
+ console.log(legacyMsg);
1584
+ logFile(legacyMsg);
1585
+ shell("systemctl", ["stop", legacyUnit], { sudo: true, bestEffort: true });
1586
+ shell("systemctl", ["disable", legacyUnit], { sudo: true, bestEffort: true });
1587
+ }
1588
+ }
1567
1589
  // skip stop+disable when a peer brand on this host still depends
1568
1590
  // on the apt `neo4j.service` (port 7687). Disabling it would kill the peer's
1569
1591
  // database reproducer on Neo's laptop. The kept-active path is
@@ -2856,7 +2878,29 @@ function writeInstallDefaults(accountId) {
2856
2878
  mkdirSync(publicDir, { recursive: true });
2857
2879
  const publicTemplatesDir = join(INSTALL_DIR, "platform/templates/agents/public");
2858
2880
  const publicIdentityDst = join(publicDir, "IDENTITY.md");
2859
- cpSync(join(publicTemplatesDir, "IDENTITY.md"), publicIdentityDst);
2881
+ const publicIdentitySrc = join(publicTemplatesDir, "IDENTITY.md");
2882
+ cpSync(publicIdentitySrc, publicIdentityDst);
2883
+ // Defensive verify: a 0-byte IDENTITY.md silently traps the brand server
2884
+ // in STARTING (the reachability auditor logs `reachable=false reason=files
2885
+ // -missing IDENTITY.md:empty` every minute and the splash never clears).
2886
+ // Observed on 192.168.88.16 after a re-install: source 1655 bytes, dest
2887
+ // 0 bytes. Loud-fail here so a corrupt copy can never reach the device's
2888
+ // runtime.
2889
+ try {
2890
+ const srcBytes = statSync(publicIdentitySrc).size;
2891
+ const dstBytes = statSync(publicIdentityDst).size;
2892
+ if (dstBytes === 0 || dstBytes !== srcBytes) {
2893
+ throw new Error(`public-identity copy verify failed: src=${publicIdentitySrc} (${srcBytes} bytes) ` +
2894
+ `dst=${publicIdentityDst} (${dstBytes} bytes). A 0-byte or partial copy traps the brand ` +
2895
+ `server in STARTING forever — re-run the installer after restoring the template, or ` +
2896
+ `report this as a copy regression.`);
2897
+ }
2898
+ }
2899
+ catch (err) {
2900
+ if (err instanceof Error && err.message.startsWith("public-identity copy verify failed"))
2901
+ throw err;
2902
+ throw new Error(`public-identity verify stat failed: ${err instanceof Error ? err.message : String(err)}`);
2903
+ }
2860
2904
  console.log(` [install-defaults] public-identity path=${publicIdentityDst}`);
2861
2905
  logFile(` [install-defaults] public-identity path=${publicIdentityDst}`);
2862
2906
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.333",
3
+ "version": "0.1.334",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -145,6 +145,15 @@ SETTINGS_EOF
145
145
  [ "$_AGENT_NAME" = "admin" ] && continue
146
146
  [ -f "$_AGENT_DIR/IDENTITY.md" ] || continue
147
147
  cp "$TEMPLATES_DIR/agents/public/IDENTITY.md" "$_AGENT_DIR/IDENTITY.md"
148
+ # Defensive verify — a 0-byte cp traps the brand server in STARTING (the
149
+ # reachability auditor reports `reachable=false reason=files-missing
150
+ # IDENTITY.md:empty` every minute). Observed on 192.168.88.16 after a
151
+ # re-install: source 1655 bytes, dest 0 bytes. Loud-fail here so the
152
+ # operator sees the bad write at install time, not the splash forever.
153
+ if [ ! -s "$_AGENT_DIR/IDENTITY.md" ]; then
154
+ echo " [setup] FATAL agents/$_AGENT_NAME/IDENTITY.md is empty after copy from $TEMPLATES_DIR/agents/public/IDENTITY.md ($(wc -c < "$TEMPLATES_DIR/agents/public/IDENTITY.md") bytes); aborting before the brand server starts" >&2
155
+ return 1
156
+ fi
148
157
  echo " agents/$_AGENT_NAME/IDENTITY.md re-synced"
149
158
  done
150
159