import { afterEach, describe, expect, test } from "bun:test";import { existsSync, mkdirSync, mkdtempSync } from "node:fs";import { tmpdir } from "node:os";import path from "node:path";import { resolveBundledPluginDirectory } from "../src/plugin-resources";// The bundled plugin directory used to be found via a fixed three-rung cwd// ladder, so it resolved from the repo root and frontend/ and nowhere else.// Every other working directory the runtime actually runs in — the deployed// systemd unit (services/agent-runtime) and the standalone Next server// (frontend/.next/standalone/frontend) — silently got null, which dropped the// "Local Studio" plugin source and left gmail/google-calendar untrusted and// without their account block. Pin resolution from cwds far from the repo root.const repoRoot = path.resolve(import.meta.dirname, "..", "..", "..");const bundled = path.join(repoRoot, "frontend", "desktop", "resources", "plugins");const originalCwd = process.cwd();const originalResources = process.env.LOCAL_STUDIO_RESOURCES_PATH;afterEach(() => { process.chdir(originalCwd); if (originalResources === undefined) delete process.env.LOCAL_STUDIO_RESOURCES_PATH; else process.env.LOCAL_STUDIO_RESOURCES_PATH = originalResources;});describe("resolveBundledPluginDirectory", () => { test("resolves from a cwd nested well below the repo root", () => { delete process.env.LOCAL_STUDIO_RESOURCES_PATH; // frontend/.next/standalone/frontend is where `npm run start` puts the // Next server; that tree has no desktop/ of its own. const standalone = path.join(repoRoot, "frontend", ".next", "standalone", "frontend"); process.chdir(existsSync(standalone) ? standalone : import.meta.dirname); expect(resolveBundledPluginDirectory()).toBe(bundled); }); test("resolves from the deployed agent-runtime working directory", () => { delete process.env.LOCAL_STUDIO_RESOURCES_PATH; process.chdir(path.resolve(import.meta.dirname, "..")); expect(resolveBundledPluginDirectory()).toBe(bundled); }); test("prefers LOCAL_STUDIO_RESOURCES_PATH when it points at real resources", () => { const root = mkdtempSync(path.join(tmpdir(), "plugin-resources-")); const packaged = path.join(root, "desktop", "resources", "plugins"); mkdirSync(packaged, { recursive: true }); process.env.LOCAL_STUDIO_RESOURCES_PATH = root; expect(resolveBundledPluginDirectory()).toBe(packaged); }); test("falls back to the cwd walk when the env path has no resources", () => { process.env.LOCAL_STUDIO_RESOURCES_PATH = mkdtempSync(path.join(tmpdir(), "plugin-empty-")); process.chdir(repoRoot); expect(resolveBundledPluginDirectory()).toBe(bundled); });});
import { afterEach, describe, expect, test } from "bun:test";
import { existsSync, mkdirSync, mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { resolveBundledPluginDirectory } from "../src/plugin-resources";
// The bundled plugin directory used to be found via a fixed three-rung cwd
// ladder, so it resolved from the repo root and frontend/ and nowhere else.
// Every other working directory the runtime actually runs in — the deployed
// systemd unit (services/agent-runtime) and the standalone Next server
// (frontend/.next/standalone/frontend) — silently got null, which dropped the
// "Local Studio" plugin source and left gmail/google-calendar untrusted and
// without their account block. Pin resolution from cwds far from the repo root.
const repoRoot = path.resolve(import.meta.dirname, "..", "..", "..");
const bundled = path.join(repoRoot, "frontend", "desktop", "resources", "plugins");
const originalCwd = process.cwd();
const originalResources = process.env.LOCAL_STUDIO_RESOURCES_PATH;
afterEach(() => {
process.chdir(originalCwd);
if (originalResources === undefined) delete process.env.LOCAL_STUDIO_RESOURCES_PATH;
else process.env.LOCAL_STUDIO_RESOURCES_PATH = originalResources;
});
describe("resolveBundledPluginDirectory", () => {
test("resolves from a cwd nested well below the repo root", () => {
delete process.env.LOCAL_STUDIO_RESOURCES_PATH;
// frontend/.next/standalone/frontend is where `npm run start` puts the
// Next server; that tree has no desktop/ of its own.
const standalone = path.join(repoRoot, "frontend", ".next", "standalone", "frontend");
process.chdir(existsSync(standalone) ? standalone : import.meta.dirname);
expect(resolveBundledPluginDirectory()).toBe(bundled);
});
test("resolves from the deployed agent-runtime working directory", () => {
delete process.env.LOCAL_STUDIO_RESOURCES_PATH;
process.chdir(path.resolve(import.meta.dirname, ".."));
expect(resolveBundledPluginDirectory()).toBe(bundled);
});
test("prefers LOCAL_STUDIO_RESOURCES_PATH when it points at real resources", () => {
const root = mkdtempSync(path.join(tmpdir(), "plugin-resources-"));
const packaged = path.join(root, "desktop", "resources", "plugins");
mkdirSync(packaged, { recursive: true });
process.env.LOCAL_STUDIO_RESOURCES_PATH = root;
expect(resolveBundledPluginDirectory()).toBe(packaged);
});
test("falls back to the cwd walk when the env path has no resources", () => {
process.env.LOCAL_STUDIO_RESOURCES_PATH = mkdtempSync(path.join(tmpdir(), "plugin-empty-"));
process.chdir(repoRoot);
expect(resolveBundledPluginDirectory()).toBe(bundled);
});
});