Merge pull request #3933 from jeremymcs/fix/broken-resource-loader-import

fix(gsd): resolve resource-loader import for deployed extensions
This commit is contained in:
Jeremy McSpadden 2026-04-10 10:39:30 -05:00 committed by GitHub
commit 65ecf669cc
4 changed files with 47 additions and 4 deletions

View file

@ -20,7 +20,7 @@ import { createInterface } from 'readline';
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(__dirname, '..');
const MAP_PATH = resolve(REPO_ROOT, 'docs/FILE-SYSTEM-MAP.md');
const MAP_PATH = resolve(REPO_ROOT, 'docs/dev/FILE-SYSTEM-MAP.md');
// ---------------------------------------------------------------------------
// Risk tier definitions

View file

@ -125,8 +125,9 @@ import {
} from "./metrics.js";
import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
import { homedir } from "node:os";
import { join } from "node:path";
import { join, dirname } from "node:path";
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
import { createRequire } from "node:module";
import { atomicWriteSync } from "./atomic-write.js";
import {
autoCommitCurrentBranch,
@ -1334,7 +1335,12 @@ export async function startAuto(
// Re-sync managed resources on resume so long-lived auto sessions pick up
// bundled extension updates before resume-time verification/state logic runs.
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
const { initResources } = await import("../../../" + "resource-loader.js");
// 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"));
initResources(agentDir);
// Open the project DB before rebuild/derive so resume uses DB-backed
// state instead of falling back to stale markdown parsing (#2940).

View file

@ -18,7 +18,7 @@ const VALID_INVITE = "https://discord.com/invite/nKXTsAcmbT";
/** Files that contain user-facing Discord invite links. */
const FILES_WITH_INVITE_LINKS: string[] = [
"README.md",
"docs/what-is-pi/15-pi-packages-the-ecosystem.md",
"docs/dev/what-is-pi/15-pi-packages-the-ecosystem.md",
];
describe("Discord invite links (#2699)", () => {

View file

@ -0,0 +1,37 @@
// GSD2 — Regression test for broken resource-loader import path
// Ensures auto.ts imports resource-loader via package resolution, not a
// relative path that breaks when deployed to ~/.gsd/agent/extensions/gsd/.
import { describe, test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { join } from "node:path";
const autoSrc = readFileSync(join(import.meta.dirname, "..", "auto.ts"), "utf-8");
describe("resource-loader import path", () => {
test("must not use relative import reaching above extensions/", () => {
// The old broken pattern: import("../../../" + "resource-loader.js")
// This resolves to ~/.gsd/resource-loader.js from deployed location, which
// doesn't exist. Regression introduced in #3899.
const brokenPattern = /import\(\s*["']\.\.\/\.\.\/\.\..*resource-loader/;
assert.ok(
!brokenPattern.test(autoSrc),
"auto.ts must not import resource-loader via relative path above extensions/ — " +
"breaks when deployed to ~/.gsd/agent/extensions/gsd/ (see #3899)",
);
});
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.
assert.ok(
autoSrc.includes('createRequire(import.meta.url)'),
"auto.ts should use createRequire to resolve resource-loader",
);
assert.ok(
autoSrc.includes('resolve("gsd-pi/package.json")'),
"auto.ts should resolve gsd-pi package root via package.json",
);
});
});