fix(FN-1952): merge pi package settings
This commit is contained in:
5
.changeset/merge-pi-settings.md
Normal file
5
.changeset/merge-pi-settings.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@gsxdsm/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Merge legacy Pi and Fusion agent package settings so package-provided models remain available.
|
||||||
45
packages/cli/src/commands/auth-paths.test.ts
Normal file
45
packages/cli/src/commands/auth-paths.test.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getFusionAgentDir, getLegacyAgentDir, getPackageManagerAgentDir } from "./auth-paths.js";
|
||||||
|
|
||||||
|
function writeJson(path: string, value: Record<string, unknown>): void {
|
||||||
|
writeFileSync(path, JSON.stringify(value, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("getPackageManagerAgentDir", () => {
|
||||||
|
it("falls back to legacy Pi settings when Fusion settings only contain Fusion metadata", () => {
|
||||||
|
const home = mkdtempSync(join(tmpdir(), "fusion-agent-dir-"));
|
||||||
|
const fusionAgentDir = getFusionAgentDir(home);
|
||||||
|
const legacyAgentDir = getLegacyAgentDir(home);
|
||||||
|
|
||||||
|
mkdirSync(fusionAgentDir, { recursive: true });
|
||||||
|
mkdirSync(legacyAgentDir, { recursive: true });
|
||||||
|
writeJson(join(fusionAgentDir, "settings.json"), {
|
||||||
|
fusionDisabledExtensions: ["/Users/example/.pi/agent/extensions/browse.ts"],
|
||||||
|
});
|
||||||
|
writeJson(join(legacyAgentDir, "settings.json"), {
|
||||||
|
packages: ["npm:pi-claude-cli"],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getPackageManagerAgentDir(home)).toBe(legacyAgentDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prefers Fusion settings when they contain package-manager settings", () => {
|
||||||
|
const home = mkdtempSync(join(tmpdir(), "fusion-agent-dir-"));
|
||||||
|
const fusionAgentDir = getFusionAgentDir(home);
|
||||||
|
const legacyAgentDir = getLegacyAgentDir(home);
|
||||||
|
|
||||||
|
mkdirSync(fusionAgentDir, { recursive: true });
|
||||||
|
mkdirSync(legacyAgentDir, { recursive: true });
|
||||||
|
writeJson(join(fusionAgentDir, "settings.json"), {
|
||||||
|
packages: ["npm:pi-claude-cli"],
|
||||||
|
});
|
||||||
|
writeJson(join(legacyAgentDir, "settings.json"), {
|
||||||
|
packages: ["npm:legacy-only"],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getPackageManagerAgentDir(home)).toBe(fusionAgentDir);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
|
||||||
export function getFusionAgentDir(home = process.env.HOME || process.env.USERPROFILE || homedir()): string {
|
export function getFusionAgentDir(home = process.env.HOME || process.env.USERPROFILE || homedir()): string {
|
||||||
@@ -41,15 +41,34 @@ export function getModelRegistryModelsPath(home = process.env.HOME || process.en
|
|||||||
return getLegacyModelsPaths(home).find((modelsPath) => existsSync(modelsPath)) ?? fusionModelsPath;
|
return getLegacyModelsPaths(home).find((modelsPath) => existsSync(modelsPath)) ?? fusionModelsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPackageManagerAgentDir(home = process.env.HOME || process.env.USERPROFILE || homedir()): string {
|
function readJsonObject(path: string): Record<string, unknown> {
|
||||||
const fusionAgentDir = getFusionAgentDir(home);
|
if (!existsSync(path)) {
|
||||||
if (
|
return {};
|
||||||
existsSync(join(fusionAgentDir, "settings.json")) ||
|
|
||||||
existsSync(join(fusionAgentDir, "extensions"))
|
|
||||||
) {
|
|
||||||
return fusionAgentDir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const legacyAgentDir = getLegacyAgentDir(home);
|
try {
|
||||||
return existsSync(legacyAgentDir) ? legacyAgentDir : fusionAgentDir;
|
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
||||||
|
return parsed && typeof parsed === "object" ? parsed as Record<string, unknown> : {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPackageManagerSettings(settings: Record<string, unknown>): boolean {
|
||||||
|
return Array.isArray(settings.packages) || Array.isArray(settings.npmCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPackageManagerAgentDir(home = process.env.HOME || process.env.USERPROFILE || homedir()): string {
|
||||||
|
const fusionAgentDir = getFusionAgentDir(home);
|
||||||
|
const legacyAgentDir = getLegacyAgentDir(home);
|
||||||
|
const fusionSettings = readJsonObject(join(fusionAgentDir, "settings.json"));
|
||||||
|
const legacySettings = readJsonObject(join(legacyAgentDir, "settings.json"));
|
||||||
|
|
||||||
|
if (hasPackageManagerSettings(fusionSettings) || !existsSync(legacyAgentDir)) {
|
||||||
|
return fusionAgentDir;
|
||||||
|
}
|
||||||
|
if (hasPackageManagerSettings(legacySettings)) {
|
||||||
|
return legacyAgentDir;
|
||||||
|
}
|
||||||
|
return existsSync(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,38 @@ describe("createReadOnlyProviderSettingsView", () => {
|
|||||||
expect(view.getProjectSettings()).toEqual({});
|
expect(view.getProjectSettings()).toEqual({});
|
||||||
expect(view.getNpmCommand()).toEqual(["pnpm"]);
|
expect(view.getNpmCommand()).toEqual(["pnpm"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("merges legacy Pi and Fusion agent settings with Fusion taking precedence", () => {
|
||||||
|
const home = mkdtempSync(join(tmpdir(), "fusion-provider-settings-"));
|
||||||
|
const cwd = join(home, "project");
|
||||||
|
const fusionAgentDir = join(home, ".fusion", "agent");
|
||||||
|
const legacyAgentDir = join(home, ".pi", "agent");
|
||||||
|
|
||||||
|
mkdirSync(join(cwd, ".fusion"), { recursive: true });
|
||||||
|
mkdirSync(fusionAgentDir, { recursive: true });
|
||||||
|
mkdirSync(legacyAgentDir, { recursive: true });
|
||||||
|
|
||||||
|
writeJson(join(legacyAgentDir, "settings.json"), {
|
||||||
|
packages: ["npm:pi-claude-cli"],
|
||||||
|
npmCommand: ["npm"],
|
||||||
|
shared: "legacy",
|
||||||
|
});
|
||||||
|
writeJson(join(fusionAgentDir, "settings.json"), {
|
||||||
|
fusionDisabledExtensions: ["/tmp/disabled.ts"],
|
||||||
|
npmCommand: ["pnpm"],
|
||||||
|
shared: "fusion",
|
||||||
|
});
|
||||||
|
|
||||||
|
const view = createReadOnlyProviderSettingsView(cwd, fusionAgentDir);
|
||||||
|
|
||||||
|
expect(view.getGlobalSettings()).toMatchObject({
|
||||||
|
packages: ["npm:pi-claude-cli"],
|
||||||
|
fusionDisabledExtensions: ["/tmp/disabled.ts"],
|
||||||
|
npmCommand: ["pnpm"],
|
||||||
|
shared: "fusion",
|
||||||
|
});
|
||||||
|
expect(view.getNpmCommand()).toEqual(["pnpm"]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("createProjectSettingsPersistence", () => {
|
describe("createProjectSettingsPersistence", () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||||
import { join, dirname } from "node:path";
|
import { join, dirname, basename } from "node:path";
|
||||||
|
|
||||||
export interface PackageManagerSettingsView {
|
export interface PackageManagerSettingsView {
|
||||||
getGlobalSettings(): Record<string, any>;
|
getGlobalSettings(): Record<string, any>;
|
||||||
@@ -7,6 +7,13 @@ export interface PackageManagerSettingsView {
|
|||||||
getNpmCommand(): string[] | undefined;
|
getNpmCommand(): string[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function siblingAgentDir(agentDir: string, siblingRoot: ".fusion" | ".pi"): string | undefined {
|
||||||
|
if (basename(agentDir) !== "agent") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return join(dirname(dirname(agentDir)), siblingRoot, "agent");
|
||||||
|
}
|
||||||
|
|
||||||
function readJsonObject(path: string): Record<string, any> {
|
function readJsonObject(path: string): Record<string, any> {
|
||||||
if (!existsSync(path)) {
|
if (!existsSync(path)) {
|
||||||
return {};
|
return {};
|
||||||
@@ -21,7 +28,16 @@ function readJsonObject(path: string): Record<string, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createReadOnlyProviderSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
export function createReadOnlyProviderSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
||||||
const globalSettings = readJsonObject(join(agentDir, "settings.json"));
|
const fusionAgentDir = agentDir.includes(`${join(".fusion", "agent")}`)
|
||||||
|
? agentDir
|
||||||
|
: siblingAgentDir(agentDir, ".fusion");
|
||||||
|
const legacyAgentDir = agentDir.includes(`${join(".pi", "agent")}`)
|
||||||
|
? agentDir
|
||||||
|
: siblingAgentDir(agentDir, ".pi");
|
||||||
|
const legacyGlobalSettings = legacyAgentDir ? readJsonObject(join(legacyAgentDir, "settings.json")) : {};
|
||||||
|
const fusionGlobalSettings = fusionAgentDir ? readJsonObject(join(fusionAgentDir, "settings.json")) : {};
|
||||||
|
const directGlobalSettings = readJsonObject(join(agentDir, "settings.json"));
|
||||||
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
||||||
const fusionProjectSettings = readJsonObject(join(cwd, ".fusion", "settings.json"));
|
const fusionProjectSettings = readJsonObject(join(cwd, ".fusion", "settings.json"));
|
||||||
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,9 @@ async function discoverDashboardPiExtensions(cwd: string): Promise<PiExtensionSe
|
|||||||
try {
|
try {
|
||||||
const { DefaultPackageManager } = await import("@mariozechner/pi-coding-agent");
|
const { DefaultPackageManager } = await import("@mariozechner/pi-coding-agent");
|
||||||
const agentDir = getPiPackageManagerAgentDir();
|
const agentDir = getPiPackageManagerAgentDir();
|
||||||
const globalSettings = readJsonObject(join(agentDir, "settings.json"));
|
const legacyGlobalSettings = readJsonObject(join(getLegacyPiAgentDir(), "settings.json"));
|
||||||
|
const fusionGlobalSettings = readJsonObject(join(getFusionAgentDir(), "settings.json"));
|
||||||
|
const globalSettings = { ...legacyGlobalSettings, ...fusionGlobalSettings };
|
||||||
const projectSettings = readJsonObject(join(cwd, ".fusion", "settings.json"));
|
const projectSettings = readJsonObject(join(cwd, ".fusion", "settings.json"));
|
||||||
const mergedSettings = { ...globalSettings, ...projectSettings };
|
const mergedSettings = { ...globalSettings, ...projectSettings };
|
||||||
const packageManager = new DefaultPackageManager({
|
const packageManager = new DefaultPackageManager({
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { existsSync, readFileSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { exec } from "node:child_process";
|
import { exec } from "node:child_process";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
import { join, relative, isAbsolute, resolve } from "node:path";
|
import { basename, dirname, join, relative, isAbsolute, resolve } from "node:path";
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
import {
|
import {
|
||||||
@@ -251,9 +251,29 @@ function readJsonObject(path: string): Record<string, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPackageManagerSettings(settings: Record<string, any>): boolean {
|
||||||
|
return Array.isArray(settings.packages) || Array.isArray(settings.npmCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
function siblingAgentDir(agentDir: string, siblingRoot: ".fusion" | ".pi"): string | undefined {
|
||||||
|
if (basename(agentDir) !== "agent") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return join(dirname(dirname(agentDir)), siblingRoot, "agent");
|
||||||
|
}
|
||||||
|
|
||||||
function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
||||||
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
||||||
const globalSettings = readJsonObject(join(agentDir, "settings.json"));
|
const fusionAgentDir = agentDir.includes(`${join(".fusion", "agent")}`)
|
||||||
|
? agentDir
|
||||||
|
: siblingAgentDir(agentDir, ".fusion");
|
||||||
|
const legacyAgentDir = agentDir.includes(`${join(".pi", "agent")}`)
|
||||||
|
? agentDir
|
||||||
|
: siblingAgentDir(agentDir, ".pi");
|
||||||
|
const legacyGlobalSettings = legacyAgentDir ? readJsonObject(join(legacyAgentDir, "settings.json")) : {};
|
||||||
|
const fusionGlobalSettings = fusionAgentDir ? readJsonObject(join(fusionAgentDir, "settings.json")) : {};
|
||||||
|
const directGlobalSettings = readJsonObject(join(agentDir, "settings.json"));
|
||||||
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
||||||
const fusionProjectSettings = readJsonObject(join(projectRoot, ".fusion", "settings.json"));
|
const fusionProjectSettings = readJsonObject(join(projectRoot, ".fusion", "settings.json"));
|
||||||
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
||||||
|
|
||||||
@@ -268,15 +288,17 @@ function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageMan
|
|||||||
|
|
||||||
function getPackageManagerAgentDir(): string {
|
function getPackageManagerAgentDir(): string {
|
||||||
const fusionAgentDir = getFusionAgentDir();
|
const fusionAgentDir = getFusionAgentDir();
|
||||||
if (
|
const legacyAgentDir = getLegacyPiAgentDir();
|
||||||
existsSync(join(fusionAgentDir, "settings.json")) ||
|
const fusionSettings = readJsonObject(join(fusionAgentDir, "settings.json"));
|
||||||
existsSync(join(fusionAgentDir, "extensions"))
|
const legacySettings = readJsonObject(join(legacyAgentDir, "settings.json"));
|
||||||
) {
|
|
||||||
|
if (hasPackageManagerSettings(fusionSettings) || !existsSync(legacyAgentDir)) {
|
||||||
return fusionAgentDir;
|
return fusionAgentDir;
|
||||||
}
|
}
|
||||||
|
if (hasPackageManagerSettings(legacySettings)) {
|
||||||
const legacyAgentDir = getLegacyPiAgentDir();
|
return legacyAgentDir;
|
||||||
return existsSync(legacyAgentDir) ? legacyAgentDir : fusionAgentDir;
|
}
|
||||||
|
return existsSync(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerExtensionProviders(cwd: string, modelRegistry: ModelRegistry): Promise<void> {
|
async function registerExtensionProviders(cwd: string, modelRegistry: ModelRegistry): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user