diff --git a/iii-config.docker.yaml b/iii-config.docker.yaml index aa6c3b99..291fb742 100644 --- a/iii-config.docker.yaml +++ b/iii-config.docker.yaml @@ -46,7 +46,8 @@ workers: logs_console_output: true - name: iii-exec config: + # See iii-config.yaml: watch the shipped artifact, not the source. watch: - - src/**/*.ts + - dist/**/*.mjs exec: - node dist/index.mjs diff --git a/iii-config.yaml b/iii-config.yaml index f7e0f188..c329e025 100644 --- a/iii-config.yaml +++ b/iii-config.yaml @@ -46,7 +46,11 @@ workers: logs_console_output: true - name: iii-exec config: + # Watch the shipped artifact, not the source tree. The npm package + # ships only `dist/` — `src/**/*.ts` resolves to nothing for end + # users, and on some platforms an empty watch glob causes iii-exec + # to abort startup before it ever spawns the node child. watch: - - src/**/*.ts + - dist/**/*.mjs exec: - node dist/index.mjs diff --git a/src/cli.ts b/src/cli.ts index e1ea9757..fc171def 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -74,6 +74,21 @@ const IS_RESET = args.includes("--reset"); const IIPINNED_VERSION = process.env["AGENTMEMORY_III_VERSION"] || "0.11.2"; +// How long the wrapper waits for iii-engine's REST API to start responding +// before giving up and printing the "REST API never responded" error. +// The default 15s is too tight on Windows: iii spawns the `iii-exec` worker +// (`node dist/index.mjs`), which has to reconnect over WebSocket and +// register HTTP triggers before the REST surface answers — and Windows cold +// node startup + native deps regularly push that past 15s. Bumping to 60s +// makes Windows installs work out of the box; AGENTMEMORY_READY_TIMEOUT_MS +// lets users tune it further (e.g. CI containers, slow disks). +const READY_TIMEOUT_MS = (() => { + const raw = process.env["AGENTMEMORY_READY_TIMEOUT_MS"]; + if (!raw) return 60000; + const parsed = Number.parseInt(raw, 10); + return Number.isFinite(parsed) && parsed > 0 ? parsed : 60000; +})(); + // Map Node platform/arch → the asset name iii-hq/iii ships under // https://github.com/iii-hq/iii/releases/download/iii/v/ function iiiReleaseAsset(): string | null { @@ -153,6 +168,10 @@ Environment: AGENTMEMORY_USE_DOCKER=1 Prefer the bundled docker-compose path over the native iii-engine binary on first run. AGENTMEMORY_III_VERSION Override pinned iii-engine version (default ${IIPINNED_VERSION}). + AGENTMEMORY_READY_TIMEOUT_MS How long to wait for iii-engine's REST API to + come up before giving up (default 60000). + Raise on slow disks / CI; lower for fast-fail + smoke tests. Quick start: npx @agentmemory/agentmemory # start with local iii-engine or Docker @@ -1037,10 +1056,10 @@ async function main() { const s = p.spinner(); s.start("Waiting for iii-engine to be ready..."); - const ready = await waitForEngine(15000); + const ready = await waitForEngine(READY_TIMEOUT_MS); if (!ready) { const port = getRestPort(); - s.stop("iii-engine did not become ready within 15s"); + s.stop(`iii-engine did not become ready within ${Math.round(READY_TIMEOUT_MS / 1000)}s`); if (startupFailure?.kind === "engine-crashed" || startupFailure?.kind === "docker-crashed") { p.log.error("The iii-engine process crashed on startup."); @@ -1384,10 +1403,12 @@ function buildDoctorEffects(): DoctorEffects { try { const started = await startEngine(); if (!started) return { ok: false, message: "startEngine() returned false" }; - const ready = await waitForEngine(15000); + const ready = await waitForEngine(READY_TIMEOUT_MS); return { ok: ready, - message: ready ? "Engine ready" : "Engine did not become ready within 15s", + message: ready + ? "Engine ready" + : `Engine did not become ready within ${Math.round(READY_TIMEOUT_MS / 1000)}s`, }; } catch (err) { return { diff --git a/src/config.ts b/src/config.ts index eed5725e..74d5e396 100644 --- a/src/config.ts +++ b/src/config.ts @@ -137,7 +137,11 @@ export function loadConfig(): AgentMemoryConfig { const provider = detectProvider(env); return { - engineUrl: env["III_ENGINE_URL"] || "ws://localhost:49134", + // Use 127.0.0.1 (not "localhost") so we force IPv4. On Windows, + // `localhost` prefers IPv6 (::1) but iii-engine binds IPv4 only, so + // resolving via "localhost" leads to an ECONNREFUSED reconnect loop + // and the iii-exec worker never registers its HTTP triggers. + engineUrl: env["III_ENGINE_URL"] || "ws://127.0.0.1:49134", restPort: parseInt(env["III_REST_PORT"] || "3111", 10) || 3111, streamsPort: parseInt(env["III_STREAMS_PORT"] || "3112", 10) || 3112, provider,