mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 17:52:49 +00:00
* Revert "Remove the unreleased Antigravity provider plugin (#5781)"
This reverts commit 9d9d9af993.
* fix(providers): commit plugin notices emitted during session opening
* feat(providers): restore Antigravity with full access and session warnings
* fix(providers): warn once when opening Antigravity sessions
Driver respawn retains the live transcript and unchanged permission posture. Emit the warning only when opening a session, including resume after a daemon restart.
172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import {
|
|
negotiateProviderCapabilities,
|
|
requireProviderCapabilities,
|
|
type ProviderCatalog,
|
|
type ProviderConnection,
|
|
type ProviderEvent,
|
|
type ProviderInput,
|
|
type ProviderLaunch,
|
|
type ProviderRegistration,
|
|
} from "@getpaseo/plugin/server/provider";
|
|
import { getCatalog, getStatus } from "./internal/catalog.js";
|
|
import { Session } from "./internal/session.js";
|
|
import { AntigravityError } from "./internal/wire.js";
|
|
|
|
const capabilities = [
|
|
"prompt.message",
|
|
"prompt.image",
|
|
"session.configure",
|
|
"session.persistence",
|
|
] as const;
|
|
|
|
export function createAntigravityProvider(): ProviderRegistration {
|
|
return {
|
|
id: "antigravity",
|
|
label: "Antigravity",
|
|
icon: "icon.svg",
|
|
command: ["agy"],
|
|
description: "Antigravity CLI on your host",
|
|
async status({ launch }) {
|
|
if (!launch)
|
|
return {
|
|
available: false,
|
|
diagnostic: "agy not found on PATH. Install Antigravity, then run `agy` and sign in.",
|
|
};
|
|
return getStatus(launch);
|
|
},
|
|
async getCatalogCacheKey({ launch }) {
|
|
if (!launch) return undefined;
|
|
const env = Object.entries(launch.env).sort(([left], [right]) => left.localeCompare(right));
|
|
return createHash("sha256")
|
|
.update(JSON.stringify([launch.command, launch.args, env]))
|
|
.digest("hex");
|
|
},
|
|
async connect(request) {
|
|
if (!request.versions.includes(1))
|
|
throw new AntigravityError("Provider protocol version 1 is required");
|
|
if (!request.launch)
|
|
throw new AntigravityError("The daemon must supply the Antigravity launch");
|
|
const negotiated = negotiateProviderCapabilities(request.capabilities, capabilities);
|
|
return createConnection(request.launch, negotiated);
|
|
},
|
|
};
|
|
}
|
|
|
|
function createConnection(
|
|
launch: ProviderLaunch,
|
|
negotiated: readonly string[],
|
|
): ProviderConnection {
|
|
const sessions = new Map<string, Session>();
|
|
const listeners = new Set<(event: ProviderEvent) => void>();
|
|
let discovered: ProviderCatalog | null = null;
|
|
let closed = false;
|
|
let pending = Promise.resolve();
|
|
|
|
function emit(event: ProviderEvent): void {
|
|
if (closed) return;
|
|
for (const listener of listeners) listener(event);
|
|
}
|
|
function catalog(): ProviderCatalog {
|
|
if (discovered === null)
|
|
throw new AntigravityError("Antigravity catalog has not been discovered");
|
|
return discovered;
|
|
}
|
|
function session(id: string): Session {
|
|
const found = sessions.get(id);
|
|
if (!found) throw new AntigravityError(`Unknown Antigravity session: ${id}`);
|
|
return found;
|
|
}
|
|
async function dispatch(input: ProviderInput): Promise<void> {
|
|
switch (input.type) {
|
|
case "catalog":
|
|
discovered = await getCatalog(launch, input.cwd);
|
|
emit({
|
|
type: "catalog",
|
|
requestId: input.requestId,
|
|
catalog: catalog(),
|
|
});
|
|
return;
|
|
case "session.open": {
|
|
if (sessions.has(input.sessionId))
|
|
throw new AntigravityError(`Session already exists: ${input.sessionId}`);
|
|
if (discovered === null) discovered = await getCatalog(launch, input.config.cwd);
|
|
const opened = new Session({
|
|
id: input.sessionId,
|
|
config: input.config,
|
|
persistence: input.persistence,
|
|
catalog,
|
|
launch,
|
|
emit,
|
|
});
|
|
sessions.set(input.sessionId, opened);
|
|
try {
|
|
await opened.open(input.requestId, negotiated);
|
|
} catch (error) {
|
|
sessions.delete(input.sessionId);
|
|
await opened.close();
|
|
throw error;
|
|
}
|
|
return;
|
|
}
|
|
case "session.prompt":
|
|
await session(input.sessionId).prompt(input.prompt);
|
|
return;
|
|
case "session.configure":
|
|
session(input.sessionId).configure(input.changes);
|
|
break;
|
|
case "session.interrupt":
|
|
await session(input.sessionId).interrupt();
|
|
break;
|
|
case "session.close": {
|
|
await session(input.sessionId).close();
|
|
sessions.delete(input.sessionId);
|
|
emit({ type: "session.closed", sessionId: input.sessionId });
|
|
break;
|
|
}
|
|
default:
|
|
throw new AntigravityError(`Unsupported Antigravity operation: ${input.type}`);
|
|
}
|
|
emit({ type: "request.completed", requestId: input.requestId });
|
|
}
|
|
function failed(input: ProviderInput, error: unknown): void {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const code = error instanceof AntigravityError ? error.code : "ANTIGRAVITY_ERROR";
|
|
if (input.type === "session.prompt")
|
|
emit({
|
|
type: "session.prompt_result",
|
|
sessionId: input.sessionId,
|
|
clientMessageId: input.prompt.clientMessageId,
|
|
result: { type: "failed", error: { message, code } },
|
|
});
|
|
else if ("requestId" in input)
|
|
emit({ type: "request.failed", requestId: input.requestId, error: { message, code } });
|
|
}
|
|
return {
|
|
version: 1,
|
|
capabilities: negotiated,
|
|
async send(input) {
|
|
if (closed) throw new AntigravityError("Antigravity connection is closed");
|
|
requireProviderCapabilities(negotiated, input);
|
|
pending = pending
|
|
.then(async () => {
|
|
if (!closed) await dispatch(input);
|
|
return undefined;
|
|
})
|
|
.catch((error) => failed(input, error));
|
|
},
|
|
onEvent(listener) {
|
|
listeners.add(listener);
|
|
return () => listeners.delete(listener);
|
|
},
|
|
async close() {
|
|
if (closed) return;
|
|
closed = true;
|
|
await pending;
|
|
await Promise.all([...sessions.values()].map((opened) => opened.close()));
|
|
sessions.clear();
|
|
listeners.clear();
|
|
},
|
|
};
|
|
}
|