feat: TUI client of goose-acp (#7362)
This commit is contained in:
@@ -28,7 +28,6 @@ main().catch((err) => {
|
||||
async function main() {
|
||||
const schemaSrc = await fs.readFile(SCHEMA_PATH, "utf8");
|
||||
const jsonSchema = JSON.parse(
|
||||
// Convert JSON Schema $defs refs to OpenAPI component refs
|
||||
schemaSrc.replaceAll("#/$defs/", "#/components/schemas/"),
|
||||
);
|
||||
|
||||
@@ -63,7 +62,6 @@ async function main() {
|
||||
async function postProcessTypes() {
|
||||
const tsPath = resolve(OUTPUT_DIR, "types.gen.ts");
|
||||
let src = await fs.readFile(tsPath, "utf8");
|
||||
// Remove the ClientOptions type block injected by @hey-api (not part of our schema)
|
||||
src = src.replace(/\nexport type ClientOptions =[\s\S]*?^};\n/m, "\n");
|
||||
await fs.writeFile(tsPath, src);
|
||||
}
|
||||
@@ -72,17 +70,14 @@ async function postProcessIndex(meta: { methods: unknown[] }) {
|
||||
const indexPath = resolve(OUTPUT_DIR, "index.ts");
|
||||
let src = await fs.readFile(indexPath, "utf8");
|
||||
|
||||
// Strip ClientOptions from re-exports
|
||||
src = src.replace(/,?\s*ClientOptions\s*,?/g, (match) => {
|
||||
if (match.startsWith(",") && match.endsWith(",")) return ",";
|
||||
if (match.startsWith(",")) return "";
|
||||
return "";
|
||||
});
|
||||
|
||||
// Fix bare relative imports to use .js extensions (required by nodenext consumers)
|
||||
src = fixRelativeImports(src);
|
||||
|
||||
// Append method constants
|
||||
const methodConstants = await prettier.format(
|
||||
`
|
||||
export const GOOSE_EXT_METHODS = ${JSON.stringify(meta.methods, null, 2)} as const;
|
||||
@@ -94,7 +89,6 @@ export type GooseExtMethod = (typeof GOOSE_EXT_METHODS)[number];
|
||||
|
||||
await fs.writeFile(indexPath, `${src}\n${methodConstants}`);
|
||||
|
||||
// Also fix imports in zod.gen.ts (it may import from types.gen)
|
||||
for (const file of ["zod.gen.ts", "types.gen.ts"]) {
|
||||
const filePath = resolve(OUTPUT_DIR, file);
|
||||
try {
|
||||
@@ -127,9 +121,6 @@ interface MethodMeta {
|
||||
responseType: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a method path like "session/list" or "working_dir/update" to camelCase "sessionList", "workingDirUpdate".
|
||||
*/
|
||||
function methodToCamelCase(method: string): string {
|
||||
return method
|
||||
.split(/[/_]/)
|
||||
@@ -139,10 +130,6 @@ function methodToCamelCase(method: string): string {
|
||||
.join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a typed GooseClient class that wraps ClientSideConnection.extMethod()
|
||||
* with proper TypeScript types and Zod runtime validation.
|
||||
*/
|
||||
async function generateClient(meta: { methods: MethodMeta[] }) {
|
||||
const typeImports = new Set<string>();
|
||||
const zodImports = new Set<string>();
|
||||
@@ -153,7 +140,6 @@ async function generateClient(meta: { methods: MethodMeta[] }) {
|
||||
const fnName = methodToCamelCase(m.method);
|
||||
const fullMethod = `_goose/${m.method}`;
|
||||
|
||||
// Build param type and arg
|
||||
let paramType = "";
|
||||
let paramArg = "";
|
||||
let callParams = "{}";
|
||||
@@ -164,7 +150,6 @@ async function generateClient(meta: { methods: MethodMeta[] }) {
|
||||
callParams = "params";
|
||||
}
|
||||
|
||||
// Build return type and validation
|
||||
let returnType: string;
|
||||
let bodyLines: string[];
|
||||
|
||||
@@ -183,7 +168,6 @@ async function generateClient(meta: { methods: MethodMeta[] }) {
|
||||
`await this.conn.extMethod("${fullMethod}", ${callParams});`,
|
||||
];
|
||||
} else {
|
||||
// Both request and response are untyped (serde_json::Value)
|
||||
returnType = "Record<string, unknown>";
|
||||
bodyLines = [
|
||||
`return await this.conn.extMethod("${fullMethod}", ${callParams ? callParams : "{}"});`,
|
||||
@@ -212,11 +196,7 @@ export interface ExtMethodProvider {
|
||||
${typeImportLine}
|
||||
${zodImportLine}
|
||||
|
||||
/**
|
||||
* Typed client for Goose custom extension methods.
|
||||
* Wraps an ExtMethodProvider (e.g. ClientSideConnection) with proper types and Zod validation.
|
||||
*/
|
||||
export class GooseClient {
|
||||
export class GooseExtClient {
|
||||
constructor(private conn: ExtMethodProvider) {}
|
||||
${methodDefs.join("\n")}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
* Typed client for Goose custom extension methods.
|
||||
* Wraps an ExtMethodProvider (e.g. ClientSideConnection) with proper types and Zod validation.
|
||||
*/
|
||||
export class GooseClient {
|
||||
export class GooseExtClient {
|
||||
constructor(private conn: ExtMethodProvider) {}
|
||||
|
||||
async extensionsAdd(params: AddExtensionRequest): Promise<void> {
|
||||
@@ -98,32 +98,4 @@ export class GooseClient {
|
||||
const raw = await this.conn.extMethod("_goose/config/extensions", {});
|
||||
return zGetExtensionsResponse.parse(raw) as GetExtensionsResponse;
|
||||
}
|
||||
|
||||
async toolCall(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/tool/call", {});
|
||||
}
|
||||
|
||||
async providerUpdate(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/provider/update", {});
|
||||
}
|
||||
|
||||
async containerSet(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/container/set", {});
|
||||
}
|
||||
|
||||
async appsList(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/apps/list", {});
|
||||
}
|
||||
|
||||
async appsExport(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/apps/export", {});
|
||||
}
|
||||
|
||||
async appsImport(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/apps/import", {});
|
||||
}
|
||||
|
||||
async configProviders(): Promise<Record<string, unknown>> {
|
||||
return await this.conn.extMethod("_goose/config/providers", {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,41 +58,6 @@ export const GOOSE_EXT_METHODS = [
|
||||
requestType: null,
|
||||
responseType: "GetExtensionsResponse",
|
||||
},
|
||||
{
|
||||
method: "tool/call",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "provider/update",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "container/set",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "apps/list",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "apps/export",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "apps/import",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
{
|
||||
method: "config/providers",
|
||||
requestType: null,
|
||||
responseType: null,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type GooseExtMethod = (typeof GOOSE_EXT_METHODS)[number];
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
ClientSideConnection,
|
||||
type Client,
|
||||
type Stream,
|
||||
type InitializeRequest,
|
||||
type InitializeResponse,
|
||||
type NewSessionRequest,
|
||||
type NewSessionResponse,
|
||||
type LoadSessionRequest,
|
||||
type LoadSessionResponse,
|
||||
type PromptRequest,
|
||||
type PromptResponse,
|
||||
type CancelNotification,
|
||||
type AuthenticateRequest,
|
||||
type AuthenticateResponse,
|
||||
type SetSessionModeRequest,
|
||||
type SetSessionModeResponse,
|
||||
type SetSessionConfigOptionRequest,
|
||||
type SetSessionConfigOptionResponse,
|
||||
type ForkSessionRequest,
|
||||
type ForkSessionResponse,
|
||||
type ListSessionsRequest,
|
||||
type ListSessionsResponse,
|
||||
type ResumeSessionRequest,
|
||||
type ResumeSessionResponse,
|
||||
type SetSessionModelRequest,
|
||||
type SetSessionModelResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import { GooseExtClient } from "./generated/client.gen.js";
|
||||
|
||||
export class GooseClient {
|
||||
private conn: ClientSideConnection;
|
||||
private ext: GooseExtClient;
|
||||
|
||||
constructor(toClient: () => Client, stream: Stream) {
|
||||
this.conn = new ClientSideConnection(toClient, stream);
|
||||
this.ext = new GooseExtClient(this.conn);
|
||||
}
|
||||
|
||||
get signal(): AbortSignal {
|
||||
return this.conn.signal;
|
||||
}
|
||||
|
||||
get closed(): Promise<void> {
|
||||
return this.conn.closed;
|
||||
}
|
||||
|
||||
initialize(params: InitializeRequest): Promise<InitializeResponse> {
|
||||
return this.conn.initialize(params);
|
||||
}
|
||||
|
||||
newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
||||
return this.conn.newSession(params);
|
||||
}
|
||||
|
||||
loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
||||
return this.conn.loadSession(params);
|
||||
}
|
||||
|
||||
prompt(params: PromptRequest): Promise<PromptResponse> {
|
||||
return this.conn.prompt(params);
|
||||
}
|
||||
|
||||
cancel(params: CancelNotification): Promise<void> {
|
||||
return this.conn.cancel(params);
|
||||
}
|
||||
|
||||
authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse> {
|
||||
return this.conn.authenticate(params);
|
||||
}
|
||||
|
||||
setSessionMode(
|
||||
params: SetSessionModeRequest,
|
||||
): Promise<SetSessionModeResponse> {
|
||||
return this.conn.setSessionMode(params);
|
||||
}
|
||||
|
||||
setSessionConfigOption(
|
||||
params: SetSessionConfigOptionRequest,
|
||||
): Promise<SetSessionConfigOptionResponse> {
|
||||
return this.conn.setSessionConfigOption(params);
|
||||
}
|
||||
|
||||
unstable_forkSession(
|
||||
params: ForkSessionRequest,
|
||||
): Promise<ForkSessionResponse> {
|
||||
return this.conn.unstable_forkSession(params);
|
||||
}
|
||||
|
||||
unstable_listSessions(
|
||||
params: ListSessionsRequest,
|
||||
): Promise<ListSessionsResponse> {
|
||||
return this.conn.unstable_listSessions(params);
|
||||
}
|
||||
|
||||
unstable_resumeSession(
|
||||
params: ResumeSessionRequest,
|
||||
): Promise<ResumeSessionResponse> {
|
||||
return this.conn.unstable_resumeSession(params);
|
||||
}
|
||||
|
||||
unstable_setSessionModel(
|
||||
params: SetSessionModelRequest,
|
||||
): Promise<SetSessionModelResponse> {
|
||||
return this.conn.unstable_setSessionModel(params);
|
||||
}
|
||||
|
||||
extMethod(
|
||||
method: string,
|
||||
params: Record<string, unknown>,
|
||||
): Promise<Record<string, unknown>> {
|
||||
return this.conn.extMethod(method, params);
|
||||
}
|
||||
|
||||
get goose(): GooseExtClient {
|
||||
return this.ext;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,3 +1,9 @@
|
||||
export * from "./generated/index.js";
|
||||
export * from "./generated/types.gen.js";
|
||||
export * from "./generated/zod.gen.js";
|
||||
export { GooseClient } from "./generated/client.gen.js";
|
||||
export { GooseClient } from "./goose-client.js";
|
||||
|
||||
export {
|
||||
ClientSideConnection,
|
||||
type Client,
|
||||
type Stream,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
|
||||
Reference in New Issue
Block a user