forge-memory 0.2.114 → 0.2.116
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 +12 -9
- package/bin/forge-memory.mjs +321 -19
- 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
|
|
|
@@ -56,10 +57,12 @@ the iOS Simulator but not for a physical phone.
|
|
|
56
57
|
The base install stays one command on purpose. The detailed companion transport
|
|
57
58
|
reference lives in the Forge repo at `docs/companion-iroh.md` and in the published
|
|
58
59
|
docs at `https://albertbuchard.github.io/forge/companion-transport.html`. Forge
|
|
59
|
-
Memory ships
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
Memory ships Forge's Iroh host source and lockfile, not native desktop binaries. When
|
|
61
|
+
the default iOS pairing flow is selected, the installer checks for Cargo, offers to
|
|
62
|
+
install the minimal Rust toolchain when the platform supports it, builds the local
|
|
63
|
+
host from the bundled source, then creates the QR. If Cargo cannot be installed
|
|
64
|
+
automatically, `install`, `configure`, and `pair-ios` stop with platform-specific
|
|
65
|
+
steps instead of printing a localhost QR that a physical iPhone cannot use.
|
|
63
66
|
|
|
64
67
|
`configure` reruns the full guided flow using the current config as defaults.
|
|
65
68
|
Install and configure run Forge doctor before finishing. `doctor --repair` creates
|
package/bin/forge-memory.mjs
CHANGED
|
@@ -277,6 +277,231 @@ function runCapture(command, args, timeoutMs = 2_000) {
|
|
|
277
277
|
return `${result.stdout}${result.stderr}`.trim();
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
function binaryNameForPlatform() {
|
|
281
|
+
return process.platform === "win32"
|
|
282
|
+
? "forge-companion-iroh.exe"
|
|
283
|
+
: "forge-companion-iroh";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function candidateIrohRoots(config) {
|
|
287
|
+
const roots = [];
|
|
288
|
+
if (config.mode === "dev" && config.repo) {
|
|
289
|
+
roots.push(config.repo);
|
|
290
|
+
roots.push(path.join(config.repo, "openclaw-plugin", "dist"));
|
|
291
|
+
}
|
|
292
|
+
const pluginRoot = resolveOpenClawPluginRoot();
|
|
293
|
+
if (pluginRoot) {
|
|
294
|
+
roots.push(pluginRoot);
|
|
295
|
+
roots.push(path.join(pluginRoot, "dist"));
|
|
296
|
+
}
|
|
297
|
+
return [...new Set(roots.map((entry) => path.resolve(entry)))];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function candidateIrohBinariesForInstall(config) {
|
|
301
|
+
const binaryName = binaryNameForPlatform();
|
|
302
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
303
|
+
const explicitBin = process.env.FORGE_COMPANION_IROH_BIN?.trim();
|
|
304
|
+
return [
|
|
305
|
+
...(explicitBin ? [explicitBin] : []),
|
|
306
|
+
...candidateIrohRoots(config).flatMap((root) => [
|
|
307
|
+
path.join(root, "companion-iroh", "target", "release", binaryName),
|
|
308
|
+
path.join(root, "companion-iroh", "target", "debug", binaryName),
|
|
309
|
+
path.join(root, "companion-iroh-src", "target", "release", binaryName),
|
|
310
|
+
path.join(root, "companion-iroh-src", "target", "debug", binaryName),
|
|
311
|
+
path.join(root, "companion-iroh", platformKey, binaryName),
|
|
312
|
+
path.join(root, "companion-iroh", binaryName)
|
|
313
|
+
])
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function findIrohBinaryForInstall(config) {
|
|
318
|
+
return candidateIrohBinariesForInstall(config).find((candidate) =>
|
|
319
|
+
fs.existsSync(candidate)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function candidateIrohManifestsForInstall(config) {
|
|
324
|
+
return candidateIrohRoots(config).flatMap((root) => [
|
|
325
|
+
path.join(root, "companion-iroh", "Cargo.toml"),
|
|
326
|
+
path.join(root, "companion-iroh-src", "Cargo.toml")
|
|
327
|
+
]);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function findIrohManifestForInstall(config) {
|
|
331
|
+
return candidateIrohManifestsForInstall(config).find((candidate) =>
|
|
332
|
+
fs.existsSync(candidate)
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function rustInstallGuidance() {
|
|
337
|
+
if (process.platform === "darwin") {
|
|
338
|
+
return [
|
|
339
|
+
"Install Apple's command line tools first if prompted: xcode-select --install",
|
|
340
|
+
"Then install Rust with the official minimal installer:",
|
|
341
|
+
"curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal",
|
|
342
|
+
"Restart the terminal or run: source ~/.cargo/env"
|
|
343
|
+
];
|
|
344
|
+
}
|
|
345
|
+
if (process.platform === "linux") {
|
|
346
|
+
return [
|
|
347
|
+
"Install build tools with your system package manager, for example: sudo apt-get install -y build-essential pkg-config libssl-dev",
|
|
348
|
+
"Then install Rust with the official minimal installer:",
|
|
349
|
+
"curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal",
|
|
350
|
+
"Restart the terminal or run: source ~/.cargo/env"
|
|
351
|
+
];
|
|
352
|
+
}
|
|
353
|
+
if (process.platform === "win32") {
|
|
354
|
+
return [
|
|
355
|
+
"Install Rustup for Windows:",
|
|
356
|
+
"winget install Rustlang.Rustup",
|
|
357
|
+
"Then reopen PowerShell and rerun: npx forge-memory install"
|
|
358
|
+
];
|
|
359
|
+
}
|
|
360
|
+
return [
|
|
361
|
+
"Install Rust/Cargo from https://rustup.rs, then rerun: npx forge-memory install"
|
|
362
|
+
];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function refreshCargoPath() {
|
|
366
|
+
const cargoBin = path.join(homeDir(), ".cargo", "bin");
|
|
367
|
+
if (fs.existsSync(cargoBin)) {
|
|
368
|
+
const current = process.env.PATH ?? "";
|
|
369
|
+
if (!current.split(path.delimiter).includes(cargoBin)) {
|
|
370
|
+
process.env.PATH = `${cargoBin}${path.delimiter}${current}`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function maybeInstallRustToolchain(flags) {
|
|
376
|
+
refreshCargoPath();
|
|
377
|
+
if (commandExists("cargo")) {
|
|
378
|
+
return { ok: true, installed: false };
|
|
379
|
+
}
|
|
380
|
+
const guidance = rustInstallGuidance();
|
|
381
|
+
const canUseRustupScript =
|
|
382
|
+
(process.platform === "darwin" || process.platform === "linux") &&
|
|
383
|
+
commandExists("curl");
|
|
384
|
+
const canUseWinget = process.platform === "win32" && commandExists("winget");
|
|
385
|
+
if (!canUseRustupScript && !canUseWinget) {
|
|
386
|
+
return {
|
|
387
|
+
ok: false,
|
|
388
|
+
installed: false,
|
|
389
|
+
guidance:
|
|
390
|
+
process.platform === "darwin" || process.platform === "linux"
|
|
391
|
+
? ["Install curl first, then install Rust/Cargo.", ...guidance]
|
|
392
|
+
: guidance
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (flags?.json || flags?.dryRun) {
|
|
396
|
+
return { ok: false, installed: false, guidance };
|
|
397
|
+
}
|
|
398
|
+
const shouldInstall = flags?.yes
|
|
399
|
+
? true
|
|
400
|
+
: await promptYesNo(
|
|
401
|
+
"Forge Companion Iroh needs Rust/Cargo to build the local transport host. Install the minimal Rust toolchain now?",
|
|
402
|
+
true
|
|
403
|
+
);
|
|
404
|
+
if (!shouldInstall) {
|
|
405
|
+
return { ok: false, installed: false, guidance };
|
|
406
|
+
}
|
|
407
|
+
console.log(color.cyan("Installing minimal Rust toolchain..."));
|
|
408
|
+
const result = canUseWinget
|
|
409
|
+
? await runCommand("winget", [
|
|
410
|
+
"install",
|
|
411
|
+
"--id",
|
|
412
|
+
"Rustlang.Rustup",
|
|
413
|
+
"-e",
|
|
414
|
+
"--source",
|
|
415
|
+
"winget",
|
|
416
|
+
"--accept-package-agreements",
|
|
417
|
+
"--accept-source-agreements"
|
|
418
|
+
])
|
|
419
|
+
: await runCommand("sh", [
|
|
420
|
+
"-c",
|
|
421
|
+
"curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal"
|
|
422
|
+
]);
|
|
423
|
+
refreshCargoPath();
|
|
424
|
+
return {
|
|
425
|
+
ok: result.ok && commandExists("cargo"),
|
|
426
|
+
installed: result.ok,
|
|
427
|
+
guidance
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function ensureIrohTransportPrepared(config, flags = {}) {
|
|
432
|
+
const existingBinary = findIrohBinaryForInstall(config);
|
|
433
|
+
if (existingBinary) {
|
|
434
|
+
return { ok: true, built: false, binary: existingBinary };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (config.mode !== "dev") {
|
|
438
|
+
await ensurePackagedRuntimeInstalled();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const manifestPath = findIrohManifestForInstall(config);
|
|
442
|
+
if (!manifestPath) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
[
|
|
445
|
+
"Forge could not find the bundled companion Iroh source.",
|
|
446
|
+
"Run npx forge-memory doctor --repair so Forge Memory refreshes the packaged runtime.",
|
|
447
|
+
`Runtime log: ${logPath()}.`
|
|
448
|
+
].join(" ")
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const rust = await maybeInstallRustToolchain(flags);
|
|
453
|
+
if (!rust.ok) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
[
|
|
456
|
+
"Forge Companion Iroh is source-built on this machine, but Rust/Cargo is not installed yet.",
|
|
457
|
+
"Install steps:",
|
|
458
|
+
...rustInstallGuidance().map((entry) => `- ${entry}`),
|
|
459
|
+
"Then rerun: npx forge-memory install",
|
|
460
|
+
"For a temporary direct network fallback, use: npx forge-memory pair-ios --manual-http --public-url <phone-reachable Forge URL>"
|
|
461
|
+
].join("\n")
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const result = await runLoggedCommand(
|
|
466
|
+
"cargo",
|
|
467
|
+
[
|
|
468
|
+
"build",
|
|
469
|
+
"--release",
|
|
470
|
+
"--manifest-path",
|
|
471
|
+
manifestPath,
|
|
472
|
+
"--bin",
|
|
473
|
+
"forge-companion-iroh"
|
|
474
|
+
],
|
|
475
|
+
{
|
|
476
|
+
cwd: path.dirname(manifestPath),
|
|
477
|
+
dryRun: flags.dryRun,
|
|
478
|
+
env: process.env,
|
|
479
|
+
logFile: logPath()
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
if (!result.ok) {
|
|
483
|
+
throw new Error(
|
|
484
|
+
[
|
|
485
|
+
"Forge could not build the local companion Iroh transport host from source.",
|
|
486
|
+
`Manifest: ${manifestPath}`,
|
|
487
|
+
`Log: ${logPath()}`,
|
|
488
|
+
"Install or repair Rust/Cargo, then rerun: npx forge-memory install"
|
|
489
|
+
].join(" ")
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const binary = findIrohBinaryForInstall(config);
|
|
493
|
+
if (!binary && !flags.dryRun) {
|
|
494
|
+
throw new Error(
|
|
495
|
+
[
|
|
496
|
+
"Cargo finished, but Forge could not find the built companion Iroh binary.",
|
|
497
|
+
`Manifest: ${manifestPath}`,
|
|
498
|
+
`Expected one of: ${candidateIrohBinariesForInstall(config).join(", ")}`
|
|
499
|
+
].join(" ")
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
return { ok: true, built: true, binary: binary ?? null, manifestPath };
|
|
503
|
+
}
|
|
504
|
+
|
|
280
505
|
function detectOpenClaw() {
|
|
281
506
|
const installed =
|
|
282
507
|
commandExists("openclaw") ||
|
|
@@ -606,6 +831,59 @@ function findForgeRepo(start = process.cwd()) {
|
|
|
606
831
|
}
|
|
607
832
|
}
|
|
608
833
|
|
|
834
|
+
function resolveRuntimeStorageRoot(healthResult) {
|
|
835
|
+
const storageRoot = healthResult?.payload?.runtime?.storageRoot;
|
|
836
|
+
return typeof storageRoot === "string" && storageRoot.trim()
|
|
837
|
+
? path.resolve(storageRoot)
|
|
838
|
+
: null;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function pathsMatch(left, right) {
|
|
842
|
+
return path.resolve(left) === path.resolve(right);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
async function resolveInstallRuntimeTarget({
|
|
846
|
+
origin,
|
|
847
|
+
requestedPort,
|
|
848
|
+
requestedWebPort,
|
|
849
|
+
dataRoot,
|
|
850
|
+
dataRootWasExplicit
|
|
851
|
+
}) {
|
|
852
|
+
if (requestedPort !== 0) {
|
|
853
|
+
const desiredConfig = { origin, port: requestedPort };
|
|
854
|
+
const desiredHealth = await health(desiredConfig);
|
|
855
|
+
if (isHealthyForgeRuntime(desiredHealth)) {
|
|
856
|
+
const liveDataRoot = resolveRuntimeStorageRoot(desiredHealth);
|
|
857
|
+
if (liveDataRoot && !pathsMatch(liveDataRoot, dataRoot)) {
|
|
858
|
+
if (dataRootWasExplicit) {
|
|
859
|
+
throw new Error(
|
|
860
|
+
[
|
|
861
|
+
`A healthy Forge runtime is already running at ${baseUrl(desiredConfig)}, but it uses a different data folder.`,
|
|
862
|
+
`Live data folder: ${liveDataRoot}.`,
|
|
863
|
+
`Requested data folder: ${path.resolve(dataRoot)}.`,
|
|
864
|
+
"Stop or restart that runtime before switching data folders. Your data folder is unchanged."
|
|
865
|
+
].join(" ")
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
dataRoot = liveDataRoot;
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
port: requestedPort,
|
|
872
|
+
webPort: requestedWebPort,
|
|
873
|
+
dataRoot: path.resolve(dataRoot),
|
|
874
|
+
adoptedExistingRuntime: true
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return {
|
|
880
|
+
port: await findFreePort(requestedPort),
|
|
881
|
+
webPort: await findFreePort(requestedWebPort),
|
|
882
|
+
dataRoot: path.resolve(dataRoot),
|
|
883
|
+
adoptedExistingRuntime: false
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
|
|
609
887
|
async function buildInstallConfig(parsed, currentConfig, discovery, command) {
|
|
610
888
|
const repo = parsed.values.repo
|
|
611
889
|
? path.resolve(parsed.values.repo)
|
|
@@ -634,22 +912,25 @@ async function buildInstallConfig(parsed, currentConfig, discovery, command) {
|
|
|
634
912
|
const dataRoot = parsed.flags.yes
|
|
635
913
|
? dataRootDefault
|
|
636
914
|
: await promptLine("Forge data folder", dataRootDefault);
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
normalizePort(
|
|
915
|
+
const origin = parsed.values.origin ?? currentConfig.origin ?? DEFAULT_ORIGIN;
|
|
916
|
+
const runtimeTarget = await resolveInstallRuntimeTarget({
|
|
917
|
+
origin,
|
|
918
|
+
requestedPort: normalizePort(parsed.values.port ?? currentConfig.port, DEFAULT_PORT),
|
|
919
|
+
requestedWebPort: normalizePort(
|
|
641
920
|
parsed.values.webPort ?? currentConfig.webPort,
|
|
642
921
|
DEFAULT_WEB_PORT
|
|
643
|
-
)
|
|
644
|
-
|
|
922
|
+
),
|
|
923
|
+
dataRoot,
|
|
924
|
+
dataRootWasExplicit: typeof parsed.values.dataRoot === "string"
|
|
925
|
+
});
|
|
645
926
|
|
|
646
927
|
return {
|
|
647
928
|
version: VERSION,
|
|
648
929
|
mode: parsed.flags.dev ? "dev" : mode,
|
|
649
|
-
origin
|
|
650
|
-
port,
|
|
651
|
-
webPort,
|
|
652
|
-
dataRoot:
|
|
930
|
+
origin,
|
|
931
|
+
port: runtimeTarget.port,
|
|
932
|
+
webPort: runtimeTarget.webPort,
|
|
933
|
+
dataRoot: runtimeTarget.dataRoot,
|
|
653
934
|
adapters,
|
|
654
935
|
repo,
|
|
655
936
|
command
|
|
@@ -1623,9 +1904,8 @@ class PairingTransportUnavailableError extends Error {
|
|
|
1623
1904
|
this.code = "pairing_transport_unavailable";
|
|
1624
1905
|
this.detail = detail;
|
|
1625
1906
|
this.guidance = [
|
|
1626
|
-
"Run npx forge-memory
|
|
1627
|
-
"
|
|
1628
|
-
"On unsupported platforms, install Rust/Cargo so Forge can build the bundled companion Iroh source fallback.",
|
|
1907
|
+
"Run npx forge-memory install or npx forge-memory configure so the installer can prepare the Iroh transport.",
|
|
1908
|
+
"If Rust/Cargo is missing, the installer will guide you through installing the minimal Rust toolchain and building Forge's bundled Iroh host source.",
|
|
1629
1909
|
"For an explicit Tailscale or LAN fallback, rerun with npx forge-memory pair-ios --manual-http --public-url <phone-reachable Forge URL>.",
|
|
1630
1910
|
"Do not scan a QR whose API URL is 127.0.0.1 on a physical iPhone; that address only works in the iOS Simulator."
|
|
1631
1911
|
];
|
|
@@ -2004,6 +2284,25 @@ async function runInstall(parsed, command) {
|
|
|
2004
2284
|
dryRun: parsed.flags.dryRun
|
|
2005
2285
|
})
|
|
2006
2286
|
);
|
|
2287
|
+
const shouldPair =
|
|
2288
|
+
parsed.flags.pairIos ||
|
|
2289
|
+
(!parsed.flags.skipPairIos &&
|
|
2290
|
+
(parsed.flags.yes
|
|
2291
|
+
? true
|
|
2292
|
+
: await promptYesNo("Pair the iOS companion now?", true)));
|
|
2293
|
+
let irohTransportResult = null;
|
|
2294
|
+
if (
|
|
2295
|
+
shouldPair &&
|
|
2296
|
+
!parsed.flags.manualHttp &&
|
|
2297
|
+
!parsed.flags.dryRun
|
|
2298
|
+
) {
|
|
2299
|
+
irohTransportResult = await withProgress(
|
|
2300
|
+
"Preparing Forge Companion Iroh transport",
|
|
2301
|
+
"checking Rust/Cargo and building the local host",
|
|
2302
|
+
parsed.flags,
|
|
2303
|
+
() => ensureIrohTransportPrepared(config, parsed.flags)
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2007
2306
|
let runtimeResult = null;
|
|
2008
2307
|
if (!parsed.flags.noStart && !parsed.flags.dryRun) {
|
|
2009
2308
|
runtimeResult = await withProgress(
|
|
@@ -2041,12 +2340,6 @@ async function runInstall(parsed, command) {
|
|
|
2041
2340
|
}
|
|
2042
2341
|
}
|
|
2043
2342
|
}
|
|
2044
|
-
const shouldPair =
|
|
2045
|
-
parsed.flags.pairIos ||
|
|
2046
|
-
(!parsed.flags.skipPairIos &&
|
|
2047
|
-
(parsed.flags.yes
|
|
2048
|
-
? true
|
|
2049
|
-
: await promptYesNo("Pair the iOS companion now?", true)));
|
|
2050
2343
|
let pairing = null;
|
|
2051
2344
|
if (shouldPair && !parsed.flags.dryRun) {
|
|
2052
2345
|
if (!runtimeResult) {
|
|
@@ -2085,6 +2378,7 @@ async function runInstall(parsed, command) {
|
|
|
2085
2378
|
adapterResults,
|
|
2086
2379
|
runtimeResult,
|
|
2087
2380
|
doctorResult,
|
|
2381
|
+
irohTransportResult,
|
|
2088
2382
|
pairing: Boolean(pairing)
|
|
2089
2383
|
};
|
|
2090
2384
|
if (parsed.flags.json) console.log(JSON.stringify(summary, null, 2));
|
|
@@ -2295,6 +2589,14 @@ async function runPairIos(parsed) {
|
|
|
2295
2589
|
transportMode,
|
|
2296
2590
|
publicUrl: parsed.values.publicUrl
|
|
2297
2591
|
});
|
|
2592
|
+
if (transportMode === "iroh") {
|
|
2593
|
+
await withProgress(
|
|
2594
|
+
"Preparing Forge Companion Iroh transport",
|
|
2595
|
+
"checking Rust/Cargo and building the local host",
|
|
2596
|
+
parsed.flags,
|
|
2597
|
+
() => ensureIrohTransportPrepared(config, parsed.flags)
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2298
2600
|
if (!parsed.flags.noStart) {
|
|
2299
2601
|
const runtimeResult = await withProgress(
|
|
2300
2602
|
"Starting Forge runtime for iOS pairing",
|
package/package.json
CHANGED