Merge remote-tracking branch 'origin/main' into pr-36-generic-agent-config

# Conflicts:
#	docs/config.html
#	docs/config.md
#	src/cli.test.ts
#	src/cli.ts
#	src/client/src/components/settings/SettingsSessiondPanel.ts
#	src/client/src/components/settings/settingsConfigDraft.test.ts
#	src/client/src/components/settings/settingsConfigDraft.ts
#	src/server/app.test.ts
#	src/server/app.ts
#	src/server/configRoutes.test.ts
#	src/server/configRoutes.ts
#	src/server/piWebPluginService.test.ts
#	src/server/piWebPluginService.ts
#	src/server/piWebStatus.test.ts
#	src/server/piWebStatus.ts
#	src/server/piWebStatusCache.ts
#	src/server/sessions/authService.test.ts
#	src/server/sessions/piSessionService.ts
#	src/server/sessions/sessionRoutes.test.ts
This commit is contained in:
Federico Jaramillo Martinez
2026-07-13 20:30:08 +02:00
289 changed files with 29164 additions and 6419 deletions
+70
View File
@@ -0,0 +1,70 @@
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import type { PiWebStatusResponse } from "../shared/apiTypes.js";
import { appTestContext, registerAppTestHooks } from "./app.testSupport.js";
registerAppTestHooks();
describe("buildApp agent config", () => {
it.each(["/api/config", "/api/machines/local/config"])("uses the latest configured agent dir for status after writes through %s", async (configRoute) => {
const originalEnv = captureEnv([
"PI_WEB_SKIP_VERSION_CHECK",
"PI_WEB_DOCKER_RUNTIME",
"PI_WEB_AGENT_COMMAND",
"PI_WEB_AGENT_DIR",
"PI_CODING_AGENT_DIR",
]);
process.env["PI_WEB_SKIP_VERSION_CHECK"] = "1";
process.env["PI_WEB_DOCKER_RUNTIME"] = "0";
Reflect.deleteProperty(process.env, "PI_WEB_AGENT_COMMAND");
Reflect.deleteProperty(process.env, "PI_WEB_AGENT_DIR");
Reflect.deleteProperty(process.env, "PI_CODING_AGENT_DIR");
try {
const initialAgentDir = join(appTestContext.tempDir, "initial-agent");
const updatedAgentDir = join(appTestContext.tempDir, "updated-agent");
appTestContext.piWebConfig = { agent: { command: "pi", dir: initialAgentDir } };
await mkdir(initialAgentDir, { recursive: true });
await installConfiguredPiWebPackage(updatedAgentDir);
const initialStatus = await appTestContext.app.inject({ method: "GET", url: "/api/pi-web/status" });
expect(initialStatus.statusCode).toBe(200);
expect(initialStatus.json<PiWebStatusResponse>().components.web.installation?.kind).not.toBe("pi-package");
const updateResponse = await appTestContext.app.inject({
method: "PUT",
url: configRoute,
payload: { config: { agent: { command: "pi", dir: updatedAgentDir } } },
});
expect(updateResponse.statusCode).toBe(200);
const refreshedStatus = await appTestContext.app.inject({ method: "GET", url: "/api/pi-web/status" });
expect(refreshedStatus.statusCode).toBe(200);
expect(refreshedStatus.json<PiWebStatusResponse>().components.web.installation).toMatchObject({
kind: "pi-package",
source: process.cwd(),
scope: "user",
});
} finally {
restoreEnv(originalEnv);
}
});
});
async function installConfiguredPiWebPackage(agentDir: string): Promise<void> {
await mkdir(agentDir, { recursive: true });
await writeFile(join(agentDir, "settings.json"), `${JSON.stringify({ packages: [process.cwd()] }, null, 2)}\n`, "utf8");
}
function captureEnv(keys: readonly string[]): Map<string, string | undefined> {
return new Map(keys.map((key) => [key, process.env[key]]));
}
function restoreEnv(values: ReadonlyMap<string, string | undefined>): void {
for (const [key, value] of values) {
if (value === undefined) Reflect.deleteProperty(process.env, key);
else process.env[key] = value;
}
}