forge-memory 0.2.110 → 0.2.112

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/README.md CHANGED
@@ -40,8 +40,9 @@ show up as a tool result instead of a closed MCP transport.
40
40
  `pair-ios` prefers the Iroh QR. Forge starts a Rust Iroh host, prints a QR payload
41
41
  with the desktop node id, pairing token, optional relay hint, and ALPN
42
42
  `forge-companion/1`, and the iPhone app connects through its native Rust bridge. The
43
- CLI renders a compact QR and saves the same compact payload under
44
- `~/.forge/pairing/` so you can paste it into the iPhone app if the camera cannot scan.
43
+ CLI renders a short-schema QR to keep the terminal code scannable and saves the
44
+ full manual payload under `~/.forge/pairing/` so you can paste it into the iPhone
45
+ app if the camera cannot scan.
45
46
  Use `--manual-http` only when you intentionally want a LAN, Tailscale, or direct
46
47
  HTTP/TCP route. For a real iPhone, pass a phone-reachable URL:
47
48
 
@@ -1451,12 +1451,34 @@ async function removeHermesAdapterConfig() {
1451
1451
  "forge",
1452
1452
  "config.json"
1453
1453
  );
1454
- const changed = fs.existsSync(forgeConfigPath);
1455
- if (changed) {
1454
+ let changed = false;
1455
+ if (fs.existsSync(forgeConfigPath)) {
1456
1456
  await backupIfExists(forgeConfigPath);
1457
1457
  await fsp.rm(forgeConfigPath, { force: true });
1458
+ changed = true;
1458
1459
  }
1459
- return { filePath: forgeConfigPath, changed };
1460
+ const forgeConfigDir = path.dirname(forgeConfigPath);
1461
+ if (fs.existsSync(forgeConfigDir)) {
1462
+ await fsp.rm(forgeConfigDir, { recursive: false, force: true }).catch(() => {});
1463
+ }
1464
+
1465
+ const hermesYamlPath = path.join(homeDir(), ".hermes", "config.yaml");
1466
+ if (fs.existsSync(hermesYamlPath)) {
1467
+ const raw = await fsp.readFile(hermesYamlPath, "utf8");
1468
+ const doc = YAML.parseDocument(raw);
1469
+ const root = doc.toJSON() ?? {};
1470
+ const enabled = Array.isArray(root.plugins?.enabled)
1471
+ ? root.plugins.enabled
1472
+ : null;
1473
+ if (enabled?.includes("forge")) {
1474
+ root.plugins.enabled = enabled.filter((entry) => entry !== "forge");
1475
+ doc.contents = doc.createNode(root);
1476
+ await backupIfExists(hermesYamlPath);
1477
+ await fsp.writeFile(hermesYamlPath, String(doc), "utf8");
1478
+ changed = true;
1479
+ }
1480
+ }
1481
+ return { filePath: forgeConfigPath, yamlPath: hermesYamlPath, changed };
1460
1482
  }
1461
1483
 
1462
1484
  async function removeCodexAdapterConfig() {
@@ -1744,6 +1766,48 @@ function compactPairingPayload(payload) {
1744
1766
  });
1745
1767
  }
1746
1768
 
1769
+ function compactQrPairingPayload(payload) {
1770
+ const manualPayload = compactPairingPayload(payload);
1771
+ const transport = manualPayload.transport
1772
+ ? compactObject({
1773
+ p: manualPayload.transport.protocol,
1774
+ d: manualPayload.transport.provider,
1775
+ s: manualPayload.transport.status,
1776
+ pb: manualPayload.transport.publicBaseUrl,
1777
+ lb: manualPayload.transport.localBaseUrl,
1778
+ n: manualPayload.transport.nodeId,
1779
+ r: manualPayload.transport.relay,
1780
+ a: manualPayload.transport.alpn,
1781
+ g: manualPayload.transport.agent,
1782
+ pp: manualPayload.transport.pairPayload
1783
+ ? compactObject({
1784
+ v: manualPayload.transport.pairPayload.v,
1785
+ n:
1786
+ manualPayload.transport.pairPayload.node_id ??
1787
+ manualPayload.transport.pairPayload.nodeId,
1788
+ t: manualPayload.transport.pairPayload.token,
1789
+ h:
1790
+ manualPayload.transport.pairPayload.host_name ??
1791
+ manualPayload.transport.pairPayload.hostName,
1792
+ r: manualPayload.transport.pairPayload.relay
1793
+ })
1794
+ : undefined,
1795
+ le: manualPayload.transport.lastError
1796
+ })
1797
+ : undefined;
1798
+ return compactObject({
1799
+ k: "fcp1",
1800
+ a: manualPayload.apiBaseUrl,
1801
+ u: manualPayload.uiBaseUrl,
1802
+ m: manualPayload.transportMode,
1803
+ t: transport,
1804
+ s: manualPayload.sessionId,
1805
+ pt: manualPayload.pairingToken,
1806
+ e: manualPayload.expiresAt,
1807
+ c: manualPayload.capabilities
1808
+ });
1809
+ }
1810
+
1747
1811
  function compactObject(value) {
1748
1812
  if (Array.isArray(value)) {
1749
1813
  const compacted = value.map((entry) => compactObject(entry)).filter((entry) => entry !== undefined);
@@ -1783,27 +1847,28 @@ function isLoopbackPairingUrl(value) {
1783
1847
  }
1784
1848
 
1785
1849
  async function printPairing(pairing) {
1786
- const payload = compactPairingPayload(pairing.qrPayload);
1787
- const payloadText = JSON.stringify(payload);
1850
+ const manualPayload = compactPairingPayload(pairing.qrPayload);
1851
+ const qrPayload = compactQrPairingPayload(pairing.qrPayload);
1852
+ const qrPayloadText = JSON.stringify(qrPayload);
1788
1853
  const terminalColumns = process.stdout.columns ?? 120;
1789
- if (terminalColumns >= 72 && payloadText.length <= 2_950) {
1854
+ if (terminalColumns >= 72 && qrPayloadText.length <= 1_500) {
1790
1855
  console.log("\nScan this compact QR in Forge Companion:\n");
1791
- qrcode.generate(payloadText, { small: true });
1856
+ qrcode.generate(qrPayloadText, { small: true });
1792
1857
  } else {
1793
1858
  console.log("");
1794
1859
  console.log(color.yellow("QR skipped because the terminal is too narrow or the payload is too large to scan reliably."));
1795
1860
  console.log("Use Manual connection in the iPhone app and paste the saved payload below.");
1796
1861
  }
1797
- const transport = payload.transport;
1862
+ const transport = manualPayload.transport;
1798
1863
  if (transport?.provider) {
1799
1864
  const label =
1800
- payload.transport?.protocol === "iroh"
1865
+ manualPayload.transport?.protocol === "iroh"
1801
1866
  ? "Iroh"
1802
- : payload.transportMode === "iroh"
1867
+ : manualPayload.transportMode === "iroh"
1803
1868
  ? "Iroh"
1804
1869
  : "Manual HTTP";
1805
- console.log(`${color.cyan(label)}: ${payload.apiBaseUrl}`);
1806
- if (label === "Manual HTTP" && isLoopbackPairingUrl(payload.apiBaseUrl)) {
1870
+ console.log(`${color.cyan(label)}: ${manualPayload.apiBaseUrl}`);
1871
+ if (label === "Manual HTTP" && isLoopbackPairingUrl(manualPayload.apiBaseUrl)) {
1807
1872
  console.log(
1808
1873
  color.yellow(
1809
1874
  "Manual HTTP points at this machine's loopback address. That is only useful for the iOS Simulator; a real iPhone needs Iroh, Tailscale, or a LAN URL passed with --public-url."
@@ -1815,7 +1880,7 @@ async function printPairing(pairing) {
1815
1880
  }
1816
1881
  }
1817
1882
  try {
1818
- const filePath = await writePairingPayloadFile(payload);
1883
+ const filePath = await writePairingPayloadFile(manualPayload);
1819
1884
  console.log("");
1820
1885
  console.log(color.bold("If the QR is too large or the camera will not scan:"));
1821
1886
  console.log("1. Open Manual connection in the iPhone app.");
@@ -1823,14 +1888,18 @@ async function printPairing(pairing) {
1823
1888
  console.log(`3. Paste the payload saved at: ${filePath}`);
1824
1889
  console.log(color.dim(` cat ${filePath}`));
1825
1890
  console.log("");
1826
- console.log(color.dim(`Compact payload bytes: ${payloadText.length}`));
1891
+ console.log(
1892
+ color.dim(
1893
+ `QR payload bytes: ${qrPayloadText.length}; manual payload bytes: ${JSON.stringify(manualPayload).length}`
1894
+ )
1895
+ );
1827
1896
  } catch (error) {
1828
1897
  console.log(
1829
1898
  color.yellow(
1830
1899
  `Could not save pairing payload file: ${error instanceof Error ? error.message : String(error)}`
1831
1900
  )
1832
1901
  );
1833
- console.log(payloadText);
1902
+ console.log(JSON.stringify(manualPayload));
1834
1903
  }
1835
1904
  }
1836
1905
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-memory",
3
- "version": "0.2.110",
3
+ "version": "0.2.112",
4
4
  "description": "Guided Forge installer and local runtime manager for the Forge UI, OpenClaw, Hermes, Codex, and iOS pairing.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",