mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-30 13:13:25 +00:00
fix(server): plugin daemon sessions see all providers by pinning the daemon appVersion (#3902)
* fix(server): expose all providers to plugin sessions * fix(app): route workspace actions semantically (#3896) * fix(app): route workspace actions semantically Separate Explorer visibility from view navigation, and give composer changes and pull request opens semantic routing owners. Unify diff destinations, keep compact PR navigation in Explorer, and derive maximize availability from visible workspace panes. * test(app): cover composer changes routing Update the browser journey for the Explorer-first composer action, then assert the second press follows the shared diff destination. * test(server): track plugin runtime dependency parameter --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Mohamed Boudra
paseo-bot[bot]
parent
0ad69d3eb0
commit
4de913fb82
@@ -609,7 +609,7 @@ export async function createPaseoDaemon(
|
||||
});
|
||||
const browserToolsPolicy = new DaemonConfigBrowserToolsPolicy(daemonConfigStore);
|
||||
const browserToolsBroker = new BrowserToolsBroker({});
|
||||
const pluginRuntime = new PluginService(logger, daemonConfigStore);
|
||||
const pluginRuntime = new PluginService(logger, daemonConfigStore, daemonVersion);
|
||||
|
||||
const serverId = getOrCreateServerId(config.paseoHome, { logger });
|
||||
const daemonKeyPair = await loadOrCreateDaemonKeyPair(config.paseoHome, logger);
|
||||
|
||||
@@ -36,10 +36,10 @@ function createStore(
|
||||
function createService(
|
||||
home: string,
|
||||
plugins: Record<string, { source: "directory"; path: string; enabled?: boolean }> = {},
|
||||
runtime?: ConstructorParameters<typeof PluginService>[2],
|
||||
runtime?: ConstructorParameters<typeof PluginService>[3],
|
||||
): PluginService {
|
||||
return bindTestSessionHost(
|
||||
new PluginService(pino({ level: "silent" }), createStore(home, plugins), runtime),
|
||||
new PluginService(pino({ level: "silent" }), createStore(home, plugins), "0.4.0", runtime),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,9 @@ describe("PluginService", () => {
|
||||
`export default function contribute(plugin: unknown) { void plugin; return () => undefined; }`,
|
||||
);
|
||||
const store = createStore(home);
|
||||
const service = bindTestSessionHost(new PluginService(pino({ level: "silent" }), store));
|
||||
const service = bindTestSessionHost(
|
||||
new PluginService(pino({ level: "silent" }), store, "0.4.0"),
|
||||
);
|
||||
await service.start();
|
||||
|
||||
await expect(
|
||||
@@ -266,7 +268,9 @@ export default function contribute(plugin: unknown) {
|
||||
}`,
|
||||
);
|
||||
const store = createStore(home);
|
||||
const service = bindTestSessionHost(new PluginService(pino({ level: "silent" }), store));
|
||||
const service = bindTestSessionHost(
|
||||
new PluginService(pino({ level: "silent" }), store, "0.4.0"),
|
||||
);
|
||||
await service.start();
|
||||
await service.installDirectory({ path: directory });
|
||||
|
||||
@@ -293,7 +297,9 @@ export default function contribute(plugin: unknown) {
|
||||
`export default function contribute(plugin: unknown) { void plugin; return () => undefined; }`,
|
||||
);
|
||||
const store = createStore(home);
|
||||
const service = bindTestSessionHost(new PluginService(pino({ level: "silent" }), store));
|
||||
const service = bindTestSessionHost(
|
||||
new PluginService(pino({ level: "silent" }), store, "0.4.0"),
|
||||
);
|
||||
await service.start();
|
||||
await service.installDirectory({ path: first });
|
||||
await service.installDirectory({ path: second });
|
||||
@@ -321,7 +327,7 @@ export default function contribute(plugin: unknown) {
|
||||
});
|
||||
store.patch({ pluginsEnabled: false });
|
||||
const paused = createPausedRuntime();
|
||||
const service = new PluginService(pino({ level: "silent" }), store, paused.runtime);
|
||||
const service = new PluginService(pino({ level: "silent" }), store, "0.4.0", paused.runtime);
|
||||
await service.start();
|
||||
|
||||
store.patch({ pluginsEnabled: true });
|
||||
@@ -343,7 +349,7 @@ export default function contribute(plugin: unknown) {
|
||||
slow: { source: "directory", path: "/plugins/slow", enabled: false },
|
||||
});
|
||||
const paused = createPausedRuntime();
|
||||
const service = new PluginService(pino({ level: "silent" }), store, paused.runtime);
|
||||
const service = new PluginService(pino({ level: "silent" }), store, "0.4.0", paused.runtime);
|
||||
await service.start();
|
||||
|
||||
const enabling = expect(service.enablePlugin("slow")).rejects.toThrow(
|
||||
@@ -366,7 +372,7 @@ export default function contribute(plugin: unknown) {
|
||||
slow: { source: "directory", path: "/plugins/slow", enabled: false },
|
||||
});
|
||||
const paused = createPluginSelectivePausedRuntime("occupier");
|
||||
const service = new PluginService(pino({ level: "silent" }), store, paused.runtime);
|
||||
const service = new PluginService(pino({ level: "silent" }), store, "0.4.0", paused.runtime);
|
||||
await service.start();
|
||||
|
||||
const occupying = service.enablePlugin("occupier");
|
||||
|
||||
@@ -34,7 +34,8 @@ export class PluginService {
|
||||
constructor(
|
||||
logger: pino.Logger,
|
||||
private readonly configStore: DaemonConfigStore,
|
||||
runtime: PluginRuntimePort = new PluginRuntime(logger),
|
||||
daemonVersion: string,
|
||||
runtime: PluginRuntimePort = new PluginRuntime(logger, daemonVersion),
|
||||
) {
|
||||
this.logger = logger.child({ module: "plugin-service" });
|
||||
this.runtime = runtime;
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { afterEach, expect, test } from "vitest";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { createTestAgentClient, createTestAgentClients } from "../test-utils/fake-agent-client.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
|
||||
@@ -30,6 +31,12 @@ const create = defineRpc({
|
||||
output: z.object({ workspaceId: z.string(), agentId: z.string() }),
|
||||
});
|
||||
|
||||
const list = defineRpc({
|
||||
name: "list",
|
||||
input: z.object({}),
|
||||
output: z.object({ agentIds: z.array(z.string()) }),
|
||||
});
|
||||
|
||||
export default function contribute(plugin: PluginContext) {
|
||||
plugin.handle(create, async ({ path }, { paseo }) => {
|
||||
const workspace = await paseo.workspaces.create({
|
||||
@@ -37,16 +44,22 @@ export default function contribute(plugin: PluginContext) {
|
||||
title: "Plugin workspace",
|
||||
});
|
||||
const agent = await workspace.agents.create({
|
||||
config: { provider: "codex/test" },
|
||||
config: { provider: "pi/test" },
|
||||
prompt: "Created by a plugin handler",
|
||||
});
|
||||
return { workspaceId: workspace.id, agentId: agent.id };
|
||||
});
|
||||
plugin.handle(list, async (_input, { paseo }) => {
|
||||
const result = await paseo.agents.list({ page: { limit: 100 } });
|
||||
return { agentIds: result.entries.map((entry) => entry.agent.id) };
|
||||
});
|
||||
return () => undefined;
|
||||
}`,
|
||||
);
|
||||
|
||||
const daemon = await createTestPaseoDaemon();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { ...createTestAgentClients(), pi: createTestAgentClient("pi") },
|
||||
});
|
||||
const client = new DaemonClient({
|
||||
url: `ws://127.0.0.1:${daemon.port}/ws`,
|
||||
appVersion: "0.4.0",
|
||||
@@ -71,6 +84,10 @@ export default function contribute(plugin: PluginContext) {
|
||||
if (typeof created !== "object" || created === null) {
|
||||
throw new Error("Plugin returned an invalid creation result");
|
||||
}
|
||||
const listed = await client.invokePluginRpc("paseo-api", "list", {});
|
||||
expect(listed).toEqual({
|
||||
agentIds: expect.arrayContaining([Reflect.get(created, "agentId")]),
|
||||
});
|
||||
await client.removePlugin("paseo-api");
|
||||
const workspaces = await client.fetchWorkspaces();
|
||||
const agents = await client.fetchAgents();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type PluginProcessRequest =
|
||||
| { type: "initialize"; pluginId: string; bundle: string }
|
||||
| { type: "initialize"; pluginId: string; bundle: string; appVersion: string }
|
||||
| { type: "invoke"; requestId: string; method: string; input: unknown }
|
||||
| { type: "shutdown" }
|
||||
| { type: "paseo_frame"; data: string | Uint8Array; isBinary: boolean }
|
||||
|
||||
@@ -99,6 +99,7 @@ async function initialize(message: Extract<PluginProcessRequest, { type: "initia
|
||||
url: `ipc://plugin/${encodeURIComponent(message.pluginId)}`,
|
||||
clientId: `plugin:${message.pluginId}`,
|
||||
clientType: "cli",
|
||||
appVersion: message.appVersion,
|
||||
reconnect: { enabled: false },
|
||||
transportFactory,
|
||||
});
|
||||
|
||||
@@ -63,10 +63,10 @@ function createReloadChild(name: string, events: string[], methods: string[] = [
|
||||
}
|
||||
|
||||
function createTestRuntime(
|
||||
dependencies: NonNullable<ConstructorParameters<typeof PluginRuntime>[1]> = {},
|
||||
dependencies: NonNullable<ConstructorParameters<typeof PluginRuntime>[2]> = {},
|
||||
logger = pino({ level: "silent" }),
|
||||
): PluginRuntime {
|
||||
return new PluginRuntime(logger, {
|
||||
return new PluginRuntime(logger, "0.4.0", {
|
||||
...dependencies,
|
||||
sessionHost: dependencies.sessionHost ?? {
|
||||
async attachPluginSocket(_pluginId, socket) {
|
||||
|
||||
@@ -204,7 +204,11 @@ export class PluginRuntime {
|
||||
private sessionHost: PluginPaseoSessionHost | null;
|
||||
private readonly listeners = new Set<(pluginId: string, error?: string) => void>();
|
||||
|
||||
constructor(logger: pino.Logger, dependencies: PluginRuntimeDependencies = {}) {
|
||||
constructor(
|
||||
logger: pino.Logger,
|
||||
private readonly daemonVersion: string,
|
||||
dependencies: PluginRuntimeDependencies = {},
|
||||
) {
|
||||
this.logger = logger.child({ module: "plugins" });
|
||||
this.spawnChild = dependencies.spawnChild ?? spawnPluginChild;
|
||||
this.sessionHost = dependencies.sessionHost ?? null;
|
||||
@@ -363,6 +367,7 @@ export class PluginRuntime {
|
||||
void send(child, {
|
||||
type: "initialize",
|
||||
pluginId,
|
||||
appVersion: this.daemonVersion,
|
||||
bundle: bundles.serverBundle,
|
||||
}).catch(fail);
|
||||
});
|
||||
|
||||
@@ -1281,3 +1281,10 @@ export function createTestAgentClients(
|
||||
opencode: new FakeAgentClient("opencode", options),
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestAgentClient(
|
||||
provider: string,
|
||||
options: TestAgentClientOptions = {},
|
||||
): AgentClient {
|
||||
return new FakeAgentClient(provider, options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user