forge-memory 0.2.114 → 0.2.115
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 +6 -5
- package/bin/forge-memory.mjs +66 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,11 +38,12 @@ tools). It also exposes `forge_memory_mcp_diagnostics` so adapter startup issues
|
|
|
38
38
|
show up as a tool result instead of a closed MCP transport.
|
|
39
39
|
|
|
40
40
|
`pair-ios` prefers the Iroh QR. Forge starts a Rust Iroh host, prints a QR payload
|
|
41
|
-
with the desktop node id, pairing token, optional relay hint,
|
|
42
|
-
`forge-companion/1`, and the
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
with the desktop node id, pairing token, optional relay hint, ALPN
|
|
42
|
+
`forge-companion/1`, and the request URL as a direct fallback when it is
|
|
43
|
+
phone-reachable. The iPhone app connects through its native Rust bridge first and can
|
|
44
|
+
retry through URLSession when that bridge times out. The CLI renders a short-schema QR
|
|
45
|
+
to keep the terminal code scannable and saves the full manual payload under
|
|
46
|
+
`~/.forge/pairing/` so you can paste it into the iPhone app if the camera cannot scan.
|
|
46
47
|
Use `--manual-http` only when you intentionally want a LAN, Tailscale, or direct
|
|
47
48
|
HTTP/TCP route. For a real iPhone, pass a phone-reachable URL:
|
|
48
49
|
|
package/bin/forge-memory.mjs
CHANGED
|
@@ -606,6 +606,59 @@ function findForgeRepo(start = process.cwd()) {
|
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
function resolveRuntimeStorageRoot(healthResult) {
|
|
610
|
+
const storageRoot = healthResult?.payload?.runtime?.storageRoot;
|
|
611
|
+
return typeof storageRoot === "string" && storageRoot.trim()
|
|
612
|
+
? path.resolve(storageRoot)
|
|
613
|
+
: null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function pathsMatch(left, right) {
|
|
617
|
+
return path.resolve(left) === path.resolve(right);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function resolveInstallRuntimeTarget({
|
|
621
|
+
origin,
|
|
622
|
+
requestedPort,
|
|
623
|
+
requestedWebPort,
|
|
624
|
+
dataRoot,
|
|
625
|
+
dataRootWasExplicit
|
|
626
|
+
}) {
|
|
627
|
+
if (requestedPort !== 0) {
|
|
628
|
+
const desiredConfig = { origin, port: requestedPort };
|
|
629
|
+
const desiredHealth = await health(desiredConfig);
|
|
630
|
+
if (isHealthyForgeRuntime(desiredHealth)) {
|
|
631
|
+
const liveDataRoot = resolveRuntimeStorageRoot(desiredHealth);
|
|
632
|
+
if (liveDataRoot && !pathsMatch(liveDataRoot, dataRoot)) {
|
|
633
|
+
if (dataRootWasExplicit) {
|
|
634
|
+
throw new Error(
|
|
635
|
+
[
|
|
636
|
+
`A healthy Forge runtime is already running at ${baseUrl(desiredConfig)}, but it uses a different data folder.`,
|
|
637
|
+
`Live data folder: ${liveDataRoot}.`,
|
|
638
|
+
`Requested data folder: ${path.resolve(dataRoot)}.`,
|
|
639
|
+
"Stop or restart that runtime before switching data folders. Your data folder is unchanged."
|
|
640
|
+
].join(" ")
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
dataRoot = liveDataRoot;
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
port: requestedPort,
|
|
647
|
+
webPort: requestedWebPort,
|
|
648
|
+
dataRoot: path.resolve(dataRoot),
|
|
649
|
+
adoptedExistingRuntime: true
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
port: await findFreePort(requestedPort),
|
|
656
|
+
webPort: await findFreePort(requestedWebPort),
|
|
657
|
+
dataRoot: path.resolve(dataRoot),
|
|
658
|
+
adoptedExistingRuntime: false
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
|
|
609
662
|
async function buildInstallConfig(parsed, currentConfig, discovery, command) {
|
|
610
663
|
const repo = parsed.values.repo
|
|
611
664
|
? path.resolve(parsed.values.repo)
|
|
@@ -634,22 +687,25 @@ async function buildInstallConfig(parsed, currentConfig, discovery, command) {
|
|
|
634
687
|
const dataRoot = parsed.flags.yes
|
|
635
688
|
? dataRootDefault
|
|
636
689
|
: await promptLine("Forge data folder", dataRootDefault);
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
normalizePort(
|
|
690
|
+
const origin = parsed.values.origin ?? currentConfig.origin ?? DEFAULT_ORIGIN;
|
|
691
|
+
const runtimeTarget = await resolveInstallRuntimeTarget({
|
|
692
|
+
origin,
|
|
693
|
+
requestedPort: normalizePort(parsed.values.port ?? currentConfig.port, DEFAULT_PORT),
|
|
694
|
+
requestedWebPort: normalizePort(
|
|
641
695
|
parsed.values.webPort ?? currentConfig.webPort,
|
|
642
696
|
DEFAULT_WEB_PORT
|
|
643
|
-
)
|
|
644
|
-
|
|
697
|
+
),
|
|
698
|
+
dataRoot,
|
|
699
|
+
dataRootWasExplicit: typeof parsed.values.dataRoot === "string"
|
|
700
|
+
});
|
|
645
701
|
|
|
646
702
|
return {
|
|
647
703
|
version: VERSION,
|
|
648
704
|
mode: parsed.flags.dev ? "dev" : mode,
|
|
649
|
-
origin
|
|
650
|
-
port,
|
|
651
|
-
webPort,
|
|
652
|
-
dataRoot:
|
|
705
|
+
origin,
|
|
706
|
+
port: runtimeTarget.port,
|
|
707
|
+
webPort: runtimeTarget.webPort,
|
|
708
|
+
dataRoot: runtimeTarget.dataRoot,
|
|
653
709
|
adapters,
|
|
654
710
|
repo,
|
|
655
711
|
command
|
package/package.json
CHANGED