Merge pull request #3958 from jeremymcs/fix/3949-auto-resume-resolve
fix(auto): resolve resource-loader.js from GSD_PKG_ROOT on resume
This commit is contained in:
commit
925d6088af
4 changed files with 81 additions and 15 deletions
|
|
@ -110,6 +110,11 @@ if (!existsSync(appRoot)) {
|
|||
// GSD_CODING_AGENT_DIR — tells pi's getAgentDir() to return ~/.gsd/agent/ instead of ~/.gsd/agent/
|
||||
process.env.GSD_CODING_AGENT_DIR = agentDir
|
||||
|
||||
// GSD_PKG_ROOT — absolute path to gsd-pi package root. Used by deployed extensions
|
||||
// (e.g. auto.ts resume path) to import modules like resource-loader.js that live
|
||||
// in the package tree, not in the deployed ~/.gsd/agent/ tree.
|
||||
process.env.GSD_PKG_ROOT = gsdRoot
|
||||
|
||||
// RTK environment — make ~/.gsd/agent/bin visible to all child-process paths,
|
||||
// not just the bash tool, and force-disable RTK telemetry for GSD-managed use.
|
||||
applyRtkProcessEnv(process.env)
|
||||
|
|
|
|||
|
|
@ -125,9 +125,9 @@ import {
|
|||
} from "./metrics.js";
|
||||
import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
|
||||
import { homedir } from "node:os";
|
||||
import { join, dirname } from "node:path";
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { atomicWriteSync } from "./atomic-write.js";
|
||||
import {
|
||||
autoCommitCurrentBranch,
|
||||
|
|
@ -1334,13 +1334,17 @@ export async function startAuto(
|
|||
restoreHookState(s.basePath);
|
||||
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
||||
// bundled extension updates before resume-time verification/state logic runs.
|
||||
// GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
|
||||
// The relative import ("../../../resource-loader.js") only works from the source
|
||||
// tree; deployed extensions live at ~/.gsd/agent/extensions/gsd/ where the
|
||||
// relative path resolves to ~/.gsd/agent/resource-loader.js which doesn't exist.
|
||||
// Using GSD_PKG_ROOT constructs a correct absolute path in both contexts (#3949).
|
||||
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
|
||||
// Resolve resource-loader from the gsd-pi package root — the relative
|
||||
// "../../../resource-loader.js" path only works from the source tree but
|
||||
// breaks when extensions are deployed to ~/.gsd/agent/extensions/gsd/.
|
||||
const _req = createRequire(import.meta.url);
|
||||
const pkgRoot = dirname(_req.resolve("gsd-pi/package.json"));
|
||||
const { initResources } = await import(join(pkgRoot, "dist", "resource-loader.js"));
|
||||
const pkgRoot = process.env.GSD_PKG_ROOT;
|
||||
const resourceLoaderPath = pkgRoot
|
||||
? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
|
||||
: new URL("../../../resource-loader.js", import.meta.url).href;
|
||||
const { initResources } = await import(resourceLoaderPath);
|
||||
initResources(agentDir);
|
||||
// Open the project DB before rebuild/derive so resume uses DB-backed
|
||||
// state instead of falling back to stale markdown parsing (#2940).
|
||||
|
|
|
|||
|
|
@ -22,16 +22,17 @@ describe("resource-loader import path", () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("uses createRequire to resolve resource-loader from package root", () => {
|
||||
// The fix uses createRequire to find gsd-pi/package.json, then imports
|
||||
// dist/resource-loader.js from there — works in both source and deployed.
|
||||
test("uses GSD_PKG_ROOT to resolve resource-loader from package root", () => {
|
||||
// The fix uses GSD_PKG_ROOT (set by loader.ts) to construct an absolute
|
||||
// file URL to dist/resource-loader.js — works in both source and deployed,
|
||||
// and on Windows where raw paths fail with ERR_UNSUPPORTED_ESM_URL_SCHEME.
|
||||
assert.ok(
|
||||
autoSrc.includes('createRequire(import.meta.url)'),
|
||||
"auto.ts should use createRequire to resolve resource-loader",
|
||||
autoSrc.includes('process.env.GSD_PKG_ROOT'),
|
||||
"auto.ts should use GSD_PKG_ROOT to resolve resource-loader",
|
||||
);
|
||||
assert.ok(
|
||||
autoSrc.includes('resolve("gsd-pi/package.json")'),
|
||||
"auto.ts should resolve gsd-pi package root via package.json",
|
||||
autoSrc.includes('pathToFileURL'),
|
||||
"auto.ts should convert path to file URL for cross-platform import()",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
56
src/tests/auto-resume-resource-loader.test.ts
Normal file
56
src/tests/auto-resume-resource-loader.test.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// GSD2 — Regression test: auto-mode resume resolves resource-loader.js from deployed path (#3949)
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join, resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const autoTsPath = join(__dirname, "..", "resources", "extensions", "gsd", "auto.ts");
|
||||
const loaderTsPath = join(__dirname, "..", "loader.ts");
|
||||
|
||||
test("loader.ts sets GSD_PKG_ROOT env var", () => {
|
||||
const loaderSrc = readFileSync(loaderTsPath, "utf-8");
|
||||
assert.ok(
|
||||
loaderSrc.includes("process.env.GSD_PKG_ROOT"),
|
||||
"loader.ts must set GSD_PKG_ROOT so deployed extensions can locate package-root modules",
|
||||
);
|
||||
});
|
||||
|
||||
test("auto.ts resume uses GSD_PKG_ROOT for resource-loader import, not bare relative path", () => {
|
||||
const autoSrc = readFileSync(autoTsPath, "utf-8");
|
||||
|
||||
// Must reference GSD_PKG_ROOT to build an absolute path
|
||||
assert.ok(
|
||||
autoSrc.includes("process.env.GSD_PKG_ROOT"),
|
||||
"auto.ts must use GSD_PKG_ROOT to resolve resource-loader.js from deployed extension path",
|
||||
);
|
||||
|
||||
// The import must use the computed variable (resourceLoaderPath), not a hardcoded relative path.
|
||||
assert.ok(
|
||||
autoSrc.includes("await import(resourceLoaderPath)"),
|
||||
"auto.ts resource-loader import must use the computed resourceLoaderPath variable, not a hardcoded relative path",
|
||||
);
|
||||
|
||||
// The resourceLoaderPath must be constructed from GSD_PKG_ROOT via pathToFileURL
|
||||
// (raw filesystem paths break on Windows with ERR_UNSUPPORTED_ESM_URL_SCHEME)
|
||||
assert.ok(
|
||||
autoSrc.includes("pathToFileURL(join(pkgRoot,"),
|
||||
"auto.ts must convert the constructed path to a file URL for cross-platform import()",
|
||||
);
|
||||
});
|
||||
|
||||
test("GSD_PKG_ROOT resolves resource-loader.js correctly from package root", () => {
|
||||
// Simulate what auto.ts does: given GSD_PKG_ROOT, construct the path
|
||||
const pkgRoot = resolve(__dirname, "..", "..");
|
||||
const resourceLoaderPath = join(pkgRoot, "dist", "resource-loader.js");
|
||||
|
||||
// After build, dist/resource-loader.js should exist
|
||||
// (this test runs post-build in CI; in dev it validates the path construction)
|
||||
const expectedDir = dirname(resourceLoaderPath);
|
||||
assert.ok(
|
||||
expectedDir.endsWith(join("dist")),
|
||||
`resource-loader path should be under dist/, got: ${expectedDir}`,
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue