chore: move acp to sdk (#8556)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
dist/
|
||||
node_modules/
|
||||
|
||||
# Generated schema files (generated from Rust during build)
|
||||
acp-schema.json
|
||||
acp-meta.json
|
||||
@@ -0,0 +1,131 @@
|
||||
# @aaif/goose-sdk
|
||||
|
||||
TypeScript client library for the Goose Agent Client Protocol (ACP).
|
||||
|
||||
This package provides:
|
||||
- TypeScript types and Zod validators for Goose ACP extension methods
|
||||
- A client for communicating with the Goose ACP server
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @aaif/goose-sdk
|
||||
```
|
||||
|
||||
The native `goose` binaries are distributed as optional dependencies
|
||||
and will be automatically installed for your platform.
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 18+
|
||||
- Rust toolchain
|
||||
- (Optional) Cross-compilation toolchains for building all platforms
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build everything (schema + TypeScript)
|
||||
npm run build
|
||||
|
||||
# Build just the schema (requires Rust)
|
||||
npm run build:schema
|
||||
|
||||
# Build just the TypeScript
|
||||
npm run build:ts
|
||||
|
||||
# Build native binary for current platform
|
||||
npm run build:native
|
||||
|
||||
# Build native binaries for all platforms
|
||||
npm run build:native:all
|
||||
```
|
||||
|
||||
### Local Development with npm link
|
||||
|
||||
To use this package locally in another project (e.g., `@aaif/goose`):
|
||||
|
||||
```bash
|
||||
# In ui/sdk
|
||||
npm run build
|
||||
npm link
|
||||
|
||||
# In ui/text (or another project)
|
||||
npm link @aaif/goose-sdk
|
||||
```
|
||||
|
||||
### Schema Generation
|
||||
|
||||
The TypeScript types are generated from Rust schemas defined in `crates/goose-acp`.
|
||||
The build process:
|
||||
|
||||
1. Builds the `generate-acp-schema` Rust binary
|
||||
2. Runs it to generate `acp-schema.json` and `acp-meta.json`
|
||||
3. Uses `@hey-api/openapi-ts` to generate TypeScript types and Zod validators
|
||||
4. Generates a typed client in `src/generated/client.gen.ts`
|
||||
|
||||
To regenerate schemas after changing Rust types:
|
||||
|
||||
```bash
|
||||
npm run build:schema
|
||||
```
|
||||
|
||||
## Native Binary Packages
|
||||
|
||||
Platform-specific npm packages for the `goose` binary are located in
|
||||
`ui/goose-binary/`:
|
||||
|
||||
| Package | Platform |
|
||||
|---------|----------|
|
||||
| `@aaif/goose-binary-darwin-arm64` | macOS Apple Silicon |
|
||||
| `@aaif/goose-binary-darwin-x64` | macOS Intel |
|
||||
| `@aaif/goose-binary-linux-arm64` | Linux ARM64 |
|
||||
| `@aaif/goose-binary-linux-x64` | Linux x64 |
|
||||
| `@aaif/goose-binary-win32-x64` | Windows x64 |
|
||||
|
||||
These are published separately from `@aaif/goose-sdk`.
|
||||
|
||||
### Building Native Binaries
|
||||
|
||||
```bash
|
||||
# Build for current platform
|
||||
npm run build:native
|
||||
|
||||
# Build for all platforms (requires cross-compilation toolchains)
|
||||
npm run build:native:all
|
||||
|
||||
# Build for specific platform(s)
|
||||
npx tsx scripts/build-native.ts darwin-arm64 linux-x64
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
Publishing is handled by GitHub Actions. See `.github/workflows/publish-npm.yml`.
|
||||
|
||||
For manual publishing:
|
||||
|
||||
```bash
|
||||
# From repository root
|
||||
./ui/scripts/publish.sh --real
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Build and publish `@aaif/goose-sdk`
|
||||
2. Publish all native binary packages
|
||||
3. Publish `@aaif/goose` (which depends on the above)
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { GooseClient } from "@aaif/goose-sdk";
|
||||
|
||||
const client = new GooseClient({
|
||||
// ... configuration
|
||||
});
|
||||
|
||||
// Use the client
|
||||
const result = await client.someMethod({ ... });
|
||||
```
|
||||
|
||||
See the [main documentation](../../README.md) for more details.
|
||||
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generates TypeScript types + Zod validators for Goose custom extension methods.
|
||||
*
|
||||
* Usage:
|
||||
* npm run generate # build Rust schema, then generate TS
|
||||
*/
|
||||
|
||||
import { createClient } from "@hey-api/openapi-ts";
|
||||
import { execSync } from "child_process";
|
||||
import * as fs from "fs/promises";
|
||||
import { dirname, resolve } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import * as prettier from "prettier";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const ROOT = resolve(__dirname, "../..");
|
||||
const SCHEMA_PATH = resolve(ROOT, "crates/goose-acp/acp-schema.json");
|
||||
const META_PATH = resolve(ROOT, "crates/goose-acp/acp-meta.json");
|
||||
const OUTPUT_DIR = resolve(__dirname, "src/generated");
|
||||
|
||||
// Export the main function so it can be imported by build-schema.ts
|
||||
export default async function main() {
|
||||
const schemaSrc = await fs.readFile(SCHEMA_PATH, "utf8");
|
||||
const jsonSchema = JSON.parse(
|
||||
schemaSrc.replaceAll("#/$defs/", "#/components/schemas/"),
|
||||
);
|
||||
|
||||
const metaSrc = await fs.readFile(META_PATH, "utf8");
|
||||
const meta = JSON.parse(metaSrc);
|
||||
|
||||
await createClient({
|
||||
input: {
|
||||
openapi: "3.1.0",
|
||||
info: {
|
||||
title: "Goose Extensions",
|
||||
version: "1.0.0",
|
||||
},
|
||||
components: {
|
||||
schemas: jsonSchema.$defs,
|
||||
},
|
||||
},
|
||||
output: {
|
||||
path: OUTPUT_DIR,
|
||||
},
|
||||
plugins: ["zod", "@hey-api/typescript"],
|
||||
});
|
||||
|
||||
await postProcessTypes();
|
||||
await postProcessIndex(meta);
|
||||
|
||||
await generateClient(meta);
|
||||
|
||||
console.log(`\nGenerated Goose extension schema in ${OUTPUT_DIR}`);
|
||||
}
|
||||
|
||||
async function postProcessTypes() {
|
||||
const tsPath = resolve(OUTPUT_DIR, "types.gen.ts");
|
||||
let src = await fs.readFile(tsPath, "utf8");
|
||||
src = src.replace(/\nexport type ClientOptions =[\s\S]*?^};\n/m, "\n");
|
||||
await fs.writeFile(tsPath, src);
|
||||
}
|
||||
|
||||
async function postProcessIndex(meta: { methods: unknown[] }) {
|
||||
const indexPath = resolve(OUTPUT_DIR, "index.ts");
|
||||
let src = await fs.readFile(indexPath, "utf8");
|
||||
|
||||
src = src.replace(/,?\s*ClientOptions\s*,?/g, (match) => {
|
||||
if (match.startsWith(",") && match.endsWith(",")) return ",";
|
||||
if (match.startsWith(",")) return "";
|
||||
return "";
|
||||
});
|
||||
|
||||
src = fixRelativeImports(src);
|
||||
|
||||
const methodConstants = await prettier.format(
|
||||
`
|
||||
export const GOOSE_EXT_METHODS = ${JSON.stringify(meta.methods, null, 2)} as const;
|
||||
|
||||
export type GooseExtMethod = (typeof GOOSE_EXT_METHODS)[number];
|
||||
`,
|
||||
{ parser: "typescript" },
|
||||
);
|
||||
|
||||
await fs.writeFile(indexPath, `${src}\n${methodConstants}`);
|
||||
|
||||
for (const file of ["zod.gen.ts", "types.gen.ts"]) {
|
||||
const filePath = resolve(OUTPUT_DIR, file);
|
||||
try {
|
||||
const content = await fs.readFile(filePath, "utf8");
|
||||
const fixed = fixRelativeImports(content);
|
||||
if (fixed !== content) {
|
||||
await fs.writeFile(filePath, fixed);
|
||||
}
|
||||
} catch {
|
||||
// File may not exist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fixRelativeImports(src: string): string {
|
||||
return src.replace(
|
||||
/from\s+['"](\.[^'"]+)['"]/g,
|
||||
(_match, importPath: string) => {
|
||||
if (importPath.endsWith(".js") || importPath.endsWith(".json")) {
|
||||
return `from '${importPath}'`;
|
||||
}
|
||||
return `from '${importPath}.js'`;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
interface MethodMeta {
|
||||
method: string;
|
||||
requestType: string | null;
|
||||
responseType: string | null;
|
||||
}
|
||||
|
||||
function methodToCamelCase(method: string): string {
|
||||
return method
|
||||
.split(/[/_]/)
|
||||
.map((part, i) =>
|
||||
i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
async function generateClient(meta: { methods: MethodMeta[] }) {
|
||||
const typeImports = new Set<string>();
|
||||
const zodImports = new Set<string>();
|
||||
|
||||
const methodDefs: string[] = [];
|
||||
|
||||
for (const m of meta.methods) {
|
||||
const fnName = methodToCamelCase(m.method);
|
||||
const fullMethod = m.method;
|
||||
|
||||
let paramType = "";
|
||||
let paramArg = "";
|
||||
let callParams = "{}";
|
||||
if (m.requestType) {
|
||||
typeImports.add(m.requestType);
|
||||
paramType = m.requestType;
|
||||
paramArg = `params: ${paramType}`;
|
||||
callParams = "params";
|
||||
}
|
||||
|
||||
let returnType: string;
|
||||
let bodyLines: string[];
|
||||
|
||||
if (m.responseType && m.responseType !== "EmptyResponse") {
|
||||
typeImports.add(m.responseType);
|
||||
const zodName = `z${m.responseType}`;
|
||||
zodImports.add(zodName);
|
||||
returnType = m.responseType;
|
||||
bodyLines = [
|
||||
`const raw = await this.conn.extMethod("${fullMethod}", ${callParams});`,
|
||||
`return ${zodName}.parse(raw) as ${returnType};`,
|
||||
];
|
||||
} else if (m.responseType === "EmptyResponse") {
|
||||
returnType = "void";
|
||||
bodyLines = [
|
||||
`await this.conn.extMethod("${fullMethod}", ${callParams});`,
|
||||
];
|
||||
} else {
|
||||
returnType = "Record<string, unknown>";
|
||||
bodyLines = [
|
||||
`return await this.conn.extMethod("${fullMethod}", ${callParams ? callParams : "{}"});`,
|
||||
];
|
||||
}
|
||||
|
||||
methodDefs.push(`
|
||||
async ${fnName}(${paramArg}): Promise<${returnType}> {
|
||||
${bodyLines.join("\n ")}
|
||||
}`);
|
||||
}
|
||||
|
||||
const typeImportLine = typeImports.size
|
||||
? `import type { ${[...typeImports].sort().join(", ")} } from "./types.gen.js";`
|
||||
: "";
|
||||
const zodImportLine = zodImports.size
|
||||
? `import { ${[...zodImports].sort().join(", ")} } from "./zod.gen.js";`
|
||||
: "";
|
||||
|
||||
let src = `// This file is auto-generated — do not edit manually.
|
||||
|
||||
export interface ExtMethodProvider {
|
||||
extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
${typeImportLine}
|
||||
${zodImportLine}
|
||||
|
||||
export class GooseExtClient {
|
||||
constructor(private conn: ExtMethodProvider) {}
|
||||
${methodDefs.join("\n")}
|
||||
}
|
||||
`;
|
||||
|
||||
src = await prettier.format(src, { parser: "typescript" });
|
||||
src = fixRelativeImports(src);
|
||||
|
||||
const clientPath = resolve(OUTPUT_DIR, "client.gen.ts");
|
||||
await fs.writeFile(clientPath, src);
|
||||
}
|
||||
|
||||
// Run main if this file is executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@aaif/goose-sdk",
|
||||
"version": "0.16.0",
|
||||
"description": "Agent Client Protocol (ACP) SDK for Goose AI agent",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/aaif-goose/goose.git"
|
||||
},
|
||||
"keywords": [
|
||||
"goose",
|
||||
"ai",
|
||||
"agent",
|
||||
"acp",
|
||||
"agent-client-protocol"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run generate && npm run build:ts",
|
||||
"build:ts": "tsc",
|
||||
"build:native": "tsx scripts/build-native.ts",
|
||||
"build:native:all": "tsx scripts/build-native.ts --all",
|
||||
"generate": "tsx generate-schema.ts",
|
||||
"lint": "tsc --noEmit",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@agentclientprotocol/sdk": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@hey-api/openapi-ts": "^0.92.3",
|
||||
"@types/node": "^20.0.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Builds the goose binary for target platforms and places them
|
||||
* into the corresponding npm package directories under ui/goose-binary/.
|
||||
*
|
||||
* Usage:
|
||||
* npm run build:native # build for current platform only
|
||||
* npm run build:native:all # build for all platforms
|
||||
* tsx scripts/build-native.ts darwin-arm64 # build specific platform
|
||||
*
|
||||
* Prerequisites:
|
||||
* - Rust cross-compilation toolchains installed for each target
|
||||
*/
|
||||
|
||||
import { execSync } from "child_process";
|
||||
import { dirname, resolve } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { mkdirSync, copyFileSync, chmodSync, existsSync } from "fs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const ROOT = resolve(__dirname, "../../..");
|
||||
const NATIVE_DIR = resolve(ROOT, "ui/goose-binary");
|
||||
|
||||
const RUST_TARGETS: Record<string, string> = {
|
||||
"darwin-arm64": "aarch64-apple-darwin",
|
||||
"darwin-x64": "x86_64-apple-darwin",
|
||||
"linux-arm64": "aarch64-unknown-linux-gnu",
|
||||
"linux-x64": "x86_64-unknown-linux-gnu",
|
||||
"win32-x64": "x86_64-pc-windows-msvc",
|
||||
};
|
||||
|
||||
const PLATFORM_MAP: Record<string, string> = {
|
||||
"darwin-arm64": "darwin-arm64",
|
||||
"darwin-x64": "darwin-x64",
|
||||
"linux-arm64": "linux-arm64",
|
||||
"linux-x64": "linux-x64",
|
||||
"win32-x64": "win32-x64",
|
||||
};
|
||||
|
||||
function getCurrentPlatform(): string | null {
|
||||
const platform = process.platform;
|
||||
const arch = process.arch;
|
||||
const key = `${platform}-${arch}`;
|
||||
return PLATFORM_MAP[key] || null;
|
||||
}
|
||||
|
||||
function buildTarget(platform: string): void {
|
||||
const rustTarget = RUST_TARGETS[platform];
|
||||
if (!rustTarget) {
|
||||
throw new Error(`Unknown platform: ${platform}`);
|
||||
}
|
||||
|
||||
const pkgDir = resolve(NATIVE_DIR, `goose-binary-${platform}`);
|
||||
const binDir = resolve(pkgDir, "bin");
|
||||
|
||||
console.log(`==> Building goose for ${platform} (${rustTarget})`);
|
||||
|
||||
try {
|
||||
execSync(`cargo build --release --target ${rustTarget} --bin goose`, {
|
||||
cwd: ROOT,
|
||||
stdio: "inherit",
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Failed to build for ${platform}`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
mkdirSync(binDir, { recursive: true });
|
||||
|
||||
const ext = platform.startsWith("win32") ? ".exe" : "";
|
||||
const binaryName = `goose${ext}`;
|
||||
const srcPath = resolve(ROOT, "target", rustTarget, "release", binaryName);
|
||||
const destPath = resolve(binDir, binaryName);
|
||||
|
||||
if (!existsSync(srcPath)) {
|
||||
throw new Error(`Binary not found at ${srcPath}`);
|
||||
}
|
||||
|
||||
copyFileSync(srcPath, destPath);
|
||||
chmodSync(destPath, 0o755);
|
||||
|
||||
console.log(` ✅ Placed binary at ${destPath}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const buildAll = args.includes("--all");
|
||||
|
||||
if (buildAll) {
|
||||
console.log("==> Building for all platforms");
|
||||
for (const platform of Object.keys(RUST_TARGETS)) {
|
||||
try {
|
||||
buildTarget(platform);
|
||||
} catch (err) {
|
||||
console.error(`Failed to build ${platform}:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
} else if (args.length > 0 && !args[0].startsWith("--")) {
|
||||
// Build specific platforms
|
||||
for (const platform of args) {
|
||||
if (!RUST_TARGETS[platform]) {
|
||||
console.error(`Unknown platform: ${platform}`);
|
||||
console.error(
|
||||
`Valid platforms: ${Object.keys(RUST_TARGETS).join(", ")}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
buildTarget(platform);
|
||||
}
|
||||
} else {
|
||||
// Build for current platform only
|
||||
const currentPlatform = getCurrentPlatform();
|
||||
if (!currentPlatform) {
|
||||
console.error(
|
||||
`Unsupported platform: ${process.platform}-${process.arch}`,
|
||||
);
|
||||
console.error(`Valid platforms: ${Object.keys(RUST_TARGETS).join(", ")}`);
|
||||
console.error(`Use --all to build for all platforms`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`==> Building for current platform: ${currentPlatform}`);
|
||||
buildTarget(currentPlatform);
|
||||
}
|
||||
|
||||
console.log("==> Done. Native packages staged in ui/goose-binary/");
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,178 @@
|
||||
// This file is auto-generated — do not edit manually.
|
||||
|
||||
export interface ExtMethodProvider {
|
||||
extMethod(
|
||||
method: string,
|
||||
params: Record<string, unknown>,
|
||||
): Promise<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
import type {
|
||||
AddExtensionRequest,
|
||||
ArchiveSessionRequest,
|
||||
CheckSecretRequest,
|
||||
CheckSecretResponse,
|
||||
DeleteSessionRequest,
|
||||
ExportSessionRequest,
|
||||
ExportSessionResponse,
|
||||
GetExtensionsRequest,
|
||||
GetExtensionsResponse,
|
||||
GetProviderDetailsRequest,
|
||||
GetProviderDetailsResponse,
|
||||
GetProviderModelsRequest,
|
||||
GetProviderModelsResponse,
|
||||
GetToolsRequest,
|
||||
GetToolsResponse,
|
||||
ImportSessionRequest,
|
||||
ImportSessionResponse,
|
||||
ListProvidersRequest,
|
||||
ListProvidersResponse,
|
||||
ReadConfigRequest,
|
||||
ReadConfigResponse,
|
||||
ReadResourceRequest,
|
||||
ReadResourceResponse,
|
||||
RemoveConfigRequest,
|
||||
RemoveExtensionRequest,
|
||||
RemoveSecretRequest,
|
||||
UnarchiveSessionRequest,
|
||||
UpdateProviderRequest,
|
||||
UpdateProviderResponse,
|
||||
UpdateWorkingDirRequest,
|
||||
UpsertConfigRequest,
|
||||
UpsertSecretRequest,
|
||||
} from './types.gen.js';
|
||||
import {
|
||||
zCheckSecretResponse,
|
||||
zExportSessionResponse,
|
||||
zGetExtensionsResponse,
|
||||
zGetProviderDetailsResponse,
|
||||
zGetProviderModelsResponse,
|
||||
zGetToolsResponse,
|
||||
zImportSessionResponse,
|
||||
zListProvidersResponse,
|
||||
zReadConfigResponse,
|
||||
zReadResourceResponse,
|
||||
zUpdateProviderResponse,
|
||||
} from './zod.gen.js';
|
||||
|
||||
export class GooseExtClient {
|
||||
constructor(private conn: ExtMethodProvider) {}
|
||||
|
||||
async GooseExtensionsAdd(params: AddExtensionRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/extensions/add", params);
|
||||
}
|
||||
|
||||
async GooseExtensionsRemove(params: RemoveExtensionRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/extensions/remove", params);
|
||||
}
|
||||
|
||||
async GooseTools(params: GetToolsRequest): Promise<GetToolsResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/tools", params);
|
||||
return zGetToolsResponse.parse(raw) as GetToolsResponse;
|
||||
}
|
||||
|
||||
async GooseResourceRead(
|
||||
params: ReadResourceRequest,
|
||||
): Promise<ReadResourceResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/resource/read", params);
|
||||
return zReadResourceResponse.parse(raw) as ReadResourceResponse;
|
||||
}
|
||||
|
||||
async GooseWorkingDirUpdate(params: UpdateWorkingDirRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/working_dir/update", params);
|
||||
}
|
||||
|
||||
async sessionDelete(params: DeleteSessionRequest): Promise<void> {
|
||||
await this.conn.extMethod("session/delete", params);
|
||||
}
|
||||
|
||||
async GooseConfigExtensions(
|
||||
params: GetExtensionsRequest,
|
||||
): Promise<GetExtensionsResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/config/extensions", params);
|
||||
return zGetExtensionsResponse.parse(raw) as GetExtensionsResponse;
|
||||
}
|
||||
|
||||
async GooseSessionProviderUpdate(
|
||||
params: UpdateProviderRequest,
|
||||
): Promise<UpdateProviderResponse> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/session/provider/update",
|
||||
params,
|
||||
);
|
||||
return zUpdateProviderResponse.parse(raw) as UpdateProviderResponse;
|
||||
}
|
||||
|
||||
async GooseProvidersList(
|
||||
params: ListProvidersRequest,
|
||||
): Promise<ListProvidersResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/providers/list", params);
|
||||
return zListProvidersResponse.parse(raw) as ListProvidersResponse;
|
||||
}
|
||||
|
||||
async GooseProvidersDetails(
|
||||
params: GetProviderDetailsRequest,
|
||||
): Promise<GetProviderDetailsResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/providers/details", params);
|
||||
return zGetProviderDetailsResponse.parse(raw) as GetProviderDetailsResponse;
|
||||
}
|
||||
|
||||
async GooseProvidersModels(
|
||||
params: GetProviderModelsRequest,
|
||||
): Promise<GetProviderModelsResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/providers/models", params);
|
||||
return zGetProviderModelsResponse.parse(raw) as GetProviderModelsResponse;
|
||||
}
|
||||
|
||||
async GooseConfigRead(
|
||||
params: ReadConfigRequest,
|
||||
): Promise<ReadConfigResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/config/read", params);
|
||||
return zReadConfigResponse.parse(raw) as ReadConfigResponse;
|
||||
}
|
||||
|
||||
async GooseConfigUpsert(params: UpsertConfigRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/config/upsert", params);
|
||||
}
|
||||
|
||||
async GooseConfigRemove(params: RemoveConfigRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/config/remove", params);
|
||||
}
|
||||
|
||||
async GooseSecretCheck(
|
||||
params: CheckSecretRequest,
|
||||
): Promise<CheckSecretResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/secret/check", params);
|
||||
return zCheckSecretResponse.parse(raw) as CheckSecretResponse;
|
||||
}
|
||||
|
||||
async GooseSecretUpsert(params: UpsertSecretRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/secret/upsert", params);
|
||||
}
|
||||
|
||||
async GooseSecretRemove(params: RemoveSecretRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/secret/remove", params);
|
||||
}
|
||||
|
||||
async GooseSessionExport(
|
||||
params: ExportSessionRequest,
|
||||
): Promise<ExportSessionResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/session/export", params);
|
||||
return zExportSessionResponse.parse(raw) as ExportSessionResponse;
|
||||
}
|
||||
|
||||
async GooseSessionImport(
|
||||
params: ImportSessionRequest,
|
||||
): Promise<ImportSessionResponse> {
|
||||
const raw = await this.conn.extMethod("_goose/session/import", params);
|
||||
return zImportSessionResponse.parse(raw) as ImportSessionResponse;
|
||||
}
|
||||
|
||||
async GooseSessionArchive(params: ArchiveSessionRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/session/archive", params);
|
||||
}
|
||||
|
||||
async GooseSessionUnarchive(params: UnarchiveSessionRequest): Promise<void> {
|
||||
await this.conn.extMethod("_goose/session/unarchive", params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
export type { AddExtensionRequest, ArchiveSessionRequest, CheckSecretRequest, CheckSecretResponse, DeleteSessionRequest, EmptyResponse, ExportSessionRequest, ExportSessionResponse, ExtRequest, ExtResponse, GetExtensionsRequest, GetExtensionsResponse, GetProviderDetailsRequest, GetProviderDetailsResponse, GetProviderModelsRequest, GetProviderModelsResponse, GetToolsRequest, GetToolsResponse, ImportSessionRequest, ImportSessionResponse, ListProvidersRequest, ListProvidersResponse, ModelEntry, ProviderConfigKey, ProviderDetailEntry, ProviderListEntry, ReadConfigRequest, ReadConfigResponse, ReadResourceRequest, ReadResourceResponse, RemoveConfigRequest, RemoveExtensionRequest, RemoveSecretRequest, UnarchiveSessionRequest, UpdateProviderRequest, UpdateProviderResponse, UpdateWorkingDirRequest, UpsertConfigRequest, UpsertSecretRequest } from './types.gen.js';
|
||||
|
||||
export const GOOSE_EXT_METHODS = [
|
||||
{
|
||||
method: "_goose/extensions/add",
|
||||
requestType: "AddExtensionRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/extensions/remove",
|
||||
requestType: "RemoveExtensionRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/tools",
|
||||
requestType: "GetToolsRequest",
|
||||
responseType: "GetToolsResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/resource/read",
|
||||
requestType: "ReadResourceRequest",
|
||||
responseType: "ReadResourceResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/working_dir/update",
|
||||
requestType: "UpdateWorkingDirRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "session/delete",
|
||||
requestType: "DeleteSessionRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/config/extensions",
|
||||
requestType: "GetExtensionsRequest",
|
||||
responseType: "GetExtensionsResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/session/provider/update",
|
||||
requestType: "UpdateProviderRequest",
|
||||
responseType: "UpdateProviderResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/providers/list",
|
||||
requestType: "ListProvidersRequest",
|
||||
responseType: "ListProvidersResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/providers/details",
|
||||
requestType: "GetProviderDetailsRequest",
|
||||
responseType: "GetProviderDetailsResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/providers/models",
|
||||
requestType: "GetProviderModelsRequest",
|
||||
responseType: "GetProviderModelsResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/config/read",
|
||||
requestType: "ReadConfigRequest",
|
||||
responseType: "ReadConfigResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/config/upsert",
|
||||
requestType: "UpsertConfigRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/config/remove",
|
||||
requestType: "RemoveConfigRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/secret/check",
|
||||
requestType: "CheckSecretRequest",
|
||||
responseType: "CheckSecretResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/secret/upsert",
|
||||
requestType: "UpsertSecretRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/secret/remove",
|
||||
requestType: "RemoveSecretRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/session/export",
|
||||
requestType: "ExportSessionRequest",
|
||||
responseType: "ExportSessionResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/session/import",
|
||||
requestType: "ImportSessionRequest",
|
||||
responseType: "ImportSessionResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/session/archive",
|
||||
requestType: "ArchiveSessionRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
{
|
||||
method: "_goose/session/unarchive",
|
||||
requestType: "UnarchiveSessionRequest",
|
||||
responseType: "EmptyResponse",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type GooseExtMethod = (typeof GOOSE_EXT_METHODS)[number];
|
||||
@@ -0,0 +1,317 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
|
||||
/**
|
||||
* Add an extension to an active session.
|
||||
*/
|
||||
export type AddExtensionRequest = {
|
||||
sessionId: string;
|
||||
/**
|
||||
* Extension configuration (see ExtensionConfig variants: Stdio, StreamableHttp, Builtin, Platform).
|
||||
*/
|
||||
config?: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Empty success response for operations that return no data.
|
||||
*/
|
||||
export type EmptyResponse = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an extension from an active session.
|
||||
*/
|
||||
export type RemoveExtensionRequest = {
|
||||
sessionId: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* List all tools available in a session.
|
||||
*/
|
||||
export type GetToolsRequest = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tools response.
|
||||
*/
|
||||
export type GetToolsResponse = {
|
||||
/**
|
||||
* Array of tool info objects with `name`, `description`, `parameters`, and optional `permission`.
|
||||
*/
|
||||
tools: Array<unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a resource from an extension.
|
||||
*/
|
||||
export type ReadResourceRequest = {
|
||||
sessionId: string;
|
||||
uri: string;
|
||||
extensionName: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resource read response.
|
||||
*/
|
||||
export type ReadResourceResponse = {
|
||||
/**
|
||||
* The resource result from the extension (MCP ReadResourceResult).
|
||||
*/
|
||||
result?: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the working directory for a session.
|
||||
*/
|
||||
export type UpdateWorkingDirRequest = {
|
||||
sessionId: string;
|
||||
workingDir: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a session.
|
||||
*/
|
||||
export type DeleteSessionRequest = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* List configured extensions and any warnings.
|
||||
*/
|
||||
export type GetExtensionsRequest = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* List configured extensions and any warnings.
|
||||
*/
|
||||
export type GetExtensionsResponse = {
|
||||
/**
|
||||
* Array of ExtensionEntry objects with `enabled` flag and config details.
|
||||
*/
|
||||
extensions: Array<unknown>;
|
||||
warnings: Array<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atomically update the provider for a live session.
|
||||
*/
|
||||
export type UpdateProviderRequest = {
|
||||
sessionId: string;
|
||||
provider: string;
|
||||
model?: string | null;
|
||||
contextLimit?: number | null;
|
||||
requestParams?: {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provider update response.
|
||||
*/
|
||||
export type UpdateProviderResponse = {
|
||||
/**
|
||||
* Refreshed session config options after the provider/model change.
|
||||
*/
|
||||
configOptions: Array<unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* List providers available through goose, including the config-default sentinel.
|
||||
*/
|
||||
export type ListProvidersRequest = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provider list response.
|
||||
*/
|
||||
export type ListProvidersResponse = {
|
||||
providers: Array<ProviderListEntry>;
|
||||
};
|
||||
|
||||
export type ProviderListEntry = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* List providers with full metadata (config keys, setup steps, etc.).
|
||||
*/
|
||||
export type GetProviderDetailsRequest = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provider details response.
|
||||
*/
|
||||
export type GetProviderDetailsResponse = {
|
||||
providers: Array<ProviderDetailEntry>;
|
||||
};
|
||||
|
||||
export type ProviderDetailEntry = {
|
||||
name: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
defaultModel: string;
|
||||
isConfigured: boolean;
|
||||
providerType: string;
|
||||
configKeys: Array<ProviderConfigKey>;
|
||||
setupSteps?: Array<string>;
|
||||
knownModels?: Array<ModelEntry>;
|
||||
};
|
||||
|
||||
export type ProviderConfigKey = {
|
||||
name: string;
|
||||
required: boolean;
|
||||
secret: boolean;
|
||||
default?: string | null;
|
||||
oauthFlow?: boolean;
|
||||
deviceCodeFlow?: boolean;
|
||||
primary?: boolean;
|
||||
};
|
||||
|
||||
export type ModelEntry = {
|
||||
name: string;
|
||||
contextLimit: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the full list of models available for a specific provider.
|
||||
*/
|
||||
export type GetProviderModelsRequest = {
|
||||
providerName: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provider models response.
|
||||
*/
|
||||
export type GetProviderModelsResponse = {
|
||||
models: Array<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a single non-secret config value.
|
||||
*/
|
||||
export type ReadConfigRequest = {
|
||||
key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Config read response.
|
||||
*/
|
||||
export type ReadConfigResponse = {
|
||||
value?: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Upsert a single non-secret config value.
|
||||
*/
|
||||
export type UpsertConfigRequest = {
|
||||
key: string;
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a single non-secret config value.
|
||||
*/
|
||||
export type RemoveConfigRequest = {
|
||||
key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a secret exists. Never returns the actual value.
|
||||
*/
|
||||
export type CheckSecretRequest = {
|
||||
key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Secret check response.
|
||||
*/
|
||||
export type CheckSecretResponse = {
|
||||
exists: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a secret value (write-only).
|
||||
*/
|
||||
export type UpsertSecretRequest = {
|
||||
key: string;
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a secret.
|
||||
*/
|
||||
export type RemoveSecretRequest = {
|
||||
key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export a session as a JSON string.
|
||||
*/
|
||||
export type ExportSessionRequest = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export session response — raw JSON of the goose session with `conversation`.
|
||||
*/
|
||||
export type ExportSessionResponse = {
|
||||
data: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Import a session from a JSON string.
|
||||
*/
|
||||
export type ImportSessionRequest = {
|
||||
data: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Import session response — metadata about the newly created session.
|
||||
*/
|
||||
export type ImportSessionResponse = {
|
||||
sessionId: string;
|
||||
title?: string | null;
|
||||
updatedAt?: string | null;
|
||||
messageCount: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Archive a session (soft delete).
|
||||
*/
|
||||
export type ArchiveSessionRequest = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unarchive a previously archived session.
|
||||
*/
|
||||
export type UnarchiveSessionRequest = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
export type ExtRequest = {
|
||||
id: string;
|
||||
method: string;
|
||||
params?: AddExtensionRequest | RemoveExtensionRequest | GetToolsRequest | ReadResourceRequest | UpdateWorkingDirRequest | DeleteSessionRequest | GetExtensionsRequest | UpdateProviderRequest | ListProvidersRequest | GetProviderDetailsRequest | GetProviderModelsRequest | ReadConfigRequest | UpsertConfigRequest | RemoveConfigRequest | CheckSecretRequest | UpsertSecretRequest | RemoveSecretRequest | ExportSessionRequest | ImportSessionRequest | ArchiveSessionRequest | UnarchiveSessionRequest | {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type ExtResponse = {
|
||||
id: string;
|
||||
result?: EmptyResponse | GetToolsResponse | ReadResourceResponse | GetExtensionsResponse | UpdateProviderResponse | ListProvidersResponse | GetProviderDetailsResponse | GetProviderModelsResponse | ReadConfigResponse | CheckSecretResponse | ExportSessionResponse | ImportSessionResponse | unknown;
|
||||
} | {
|
||||
error: {
|
||||
code: number;
|
||||
message: string;
|
||||
data?: unknown;
|
||||
};
|
||||
id: string;
|
||||
};
|
||||
@@ -0,0 +1,356 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Add an extension to an active session.
|
||||
*/
|
||||
export const zAddExtensionRequest = z.object({
|
||||
sessionId: z.string(),
|
||||
config: z.unknown().optional().default(null)
|
||||
});
|
||||
|
||||
/**
|
||||
* Empty success response for operations that return no data.
|
||||
*/
|
||||
export const zEmptyResponse = z.record(z.unknown());
|
||||
|
||||
/**
|
||||
* Remove an extension from an active session.
|
||||
*/
|
||||
export const zRemoveExtensionRequest = z.object({
|
||||
sessionId: z.string(),
|
||||
name: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* List all tools available in a session.
|
||||
*/
|
||||
export const zGetToolsRequest = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Tools response.
|
||||
*/
|
||||
export const zGetToolsResponse = z.object({
|
||||
tools: z.array(z.unknown())
|
||||
});
|
||||
|
||||
/**
|
||||
* Read a resource from an extension.
|
||||
*/
|
||||
export const zReadResourceRequest = z.object({
|
||||
sessionId: z.string(),
|
||||
uri: z.string(),
|
||||
extensionName: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Resource read response.
|
||||
*/
|
||||
export const zReadResourceResponse = z.object({
|
||||
result: z.unknown().optional().default(null)
|
||||
});
|
||||
|
||||
/**
|
||||
* Update the working directory for a session.
|
||||
*/
|
||||
export const zUpdateWorkingDirRequest = z.object({
|
||||
sessionId: z.string(),
|
||||
workingDir: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete a session.
|
||||
*/
|
||||
export const zDeleteSessionRequest = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* List configured extensions and any warnings.
|
||||
*/
|
||||
export const zGetExtensionsRequest = z.record(z.unknown());
|
||||
|
||||
/**
|
||||
* List configured extensions and any warnings.
|
||||
*/
|
||||
export const zGetExtensionsResponse = z.object({
|
||||
extensions: z.array(z.unknown()),
|
||||
warnings: z.array(z.string())
|
||||
});
|
||||
|
||||
/**
|
||||
* Atomically update the provider for a live session.
|
||||
*/
|
||||
export const zUpdateProviderRequest = z.object({
|
||||
sessionId: z.string(),
|
||||
provider: z.string(),
|
||||
model: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
contextLimit: z.union([
|
||||
z.number().int().gte(0),
|
||||
z.null()
|
||||
]).optional(),
|
||||
requestParams: z.union([
|
||||
z.record(z.unknown()),
|
||||
z.null()
|
||||
]).optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider update response.
|
||||
*/
|
||||
export const zUpdateProviderResponse = z.object({
|
||||
configOptions: z.array(z.unknown())
|
||||
});
|
||||
|
||||
/**
|
||||
* List providers available through goose, including the config-default sentinel.
|
||||
*/
|
||||
export const zListProvidersRequest = z.record(z.unknown());
|
||||
|
||||
export const zProviderListEntry = z.object({
|
||||
id: z.string(),
|
||||
label: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider list response.
|
||||
*/
|
||||
export const zListProvidersResponse = z.object({
|
||||
providers: z.array(zProviderListEntry)
|
||||
});
|
||||
|
||||
/**
|
||||
* List providers with full metadata (config keys, setup steps, etc.).
|
||||
*/
|
||||
export const zGetProviderDetailsRequest = z.record(z.unknown());
|
||||
|
||||
export const zProviderConfigKey = z.object({
|
||||
name: z.string(),
|
||||
required: z.boolean(),
|
||||
secret: z.boolean(),
|
||||
default: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional().default(null),
|
||||
oauthFlow: z.boolean().optional().default(false),
|
||||
deviceCodeFlow: z.boolean().optional().default(false),
|
||||
primary: z.boolean().optional().default(false)
|
||||
});
|
||||
|
||||
export const zModelEntry = z.object({
|
||||
name: z.string(),
|
||||
contextLimit: z.number().int().gte(0)
|
||||
});
|
||||
|
||||
export const zProviderDetailEntry = z.object({
|
||||
name: z.string(),
|
||||
displayName: z.string(),
|
||||
description: z.string(),
|
||||
defaultModel: z.string(),
|
||||
isConfigured: z.boolean(),
|
||||
providerType: z.string(),
|
||||
configKeys: z.array(zProviderConfigKey),
|
||||
setupSteps: z.array(z.string()).optional().default([]),
|
||||
knownModels: z.array(zModelEntry).optional().default([])
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider details response.
|
||||
*/
|
||||
export const zGetProviderDetailsResponse = z.object({
|
||||
providers: z.array(zProviderDetailEntry)
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch the full list of models available for a specific provider.
|
||||
*/
|
||||
export const zGetProviderModelsRequest = z.object({
|
||||
providerName: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider models response.
|
||||
*/
|
||||
export const zGetProviderModelsResponse = z.object({
|
||||
models: z.array(z.string())
|
||||
});
|
||||
|
||||
/**
|
||||
* Read a single non-secret config value.
|
||||
*/
|
||||
export const zReadConfigRequest = z.object({
|
||||
key: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Config read response.
|
||||
*/
|
||||
export const zReadConfigResponse = z.object({
|
||||
value: z.unknown().optional().default(null)
|
||||
});
|
||||
|
||||
/**
|
||||
* Upsert a single non-secret config value.
|
||||
*/
|
||||
export const zUpsertConfigRequest = z.object({
|
||||
key: z.string(),
|
||||
value: z.unknown()
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a single non-secret config value.
|
||||
*/
|
||||
export const zRemoveConfigRequest = z.object({
|
||||
key: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Check whether a secret exists. Never returns the actual value.
|
||||
*/
|
||||
export const zCheckSecretRequest = z.object({
|
||||
key: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Secret check response.
|
||||
*/
|
||||
export const zCheckSecretResponse = z.object({
|
||||
exists: z.boolean()
|
||||
});
|
||||
|
||||
/**
|
||||
* Set a secret value (write-only).
|
||||
*/
|
||||
export const zUpsertSecretRequest = z.object({
|
||||
key: z.string(),
|
||||
value: z.unknown()
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a secret.
|
||||
*/
|
||||
export const zRemoveSecretRequest = z.object({
|
||||
key: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Export a session as a JSON string.
|
||||
*/
|
||||
export const zExportSessionRequest = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Export session response — raw JSON of the goose session with `conversation`.
|
||||
*/
|
||||
export const zExportSessionResponse = z.object({
|
||||
data: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Import a session from a JSON string.
|
||||
*/
|
||||
export const zImportSessionRequest = z.object({
|
||||
data: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Import session response — metadata about the newly created session.
|
||||
*/
|
||||
export const zImportSessionResponse = z.object({
|
||||
sessionId: z.string(),
|
||||
title: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
updatedAt: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
messageCount: z.number().int().gte(0)
|
||||
});
|
||||
|
||||
/**
|
||||
* Archive a session (soft delete).
|
||||
*/
|
||||
export const zArchiveSessionRequest = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Unarchive a previously archived session.
|
||||
*/
|
||||
export const zUnarchiveSessionRequest = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
export const zExtRequest = z.object({
|
||||
id: z.string(),
|
||||
method: z.string(),
|
||||
params: z.union([
|
||||
z.union([
|
||||
zAddExtensionRequest,
|
||||
zRemoveExtensionRequest,
|
||||
zGetToolsRequest,
|
||||
zReadResourceRequest,
|
||||
zUpdateWorkingDirRequest,
|
||||
zDeleteSessionRequest,
|
||||
zGetExtensionsRequest,
|
||||
zUpdateProviderRequest,
|
||||
zListProvidersRequest,
|
||||
zGetProviderDetailsRequest,
|
||||
zGetProviderModelsRequest,
|
||||
zReadConfigRequest,
|
||||
zUpsertConfigRequest,
|
||||
zRemoveConfigRequest,
|
||||
zCheckSecretRequest,
|
||||
zUpsertSecretRequest,
|
||||
zRemoveSecretRequest,
|
||||
zExportSessionRequest,
|
||||
zImportSessionRequest,
|
||||
zArchiveSessionRequest,
|
||||
zUnarchiveSessionRequest
|
||||
]),
|
||||
z.union([
|
||||
z.record(z.unknown()),
|
||||
z.null()
|
||||
])
|
||||
]).optional()
|
||||
});
|
||||
|
||||
export const zExtResponse = z.union([
|
||||
z.object({
|
||||
id: z.string(),
|
||||
result: z.union([
|
||||
z.union([
|
||||
zEmptyResponse,
|
||||
zGetToolsResponse,
|
||||
zReadResourceResponse,
|
||||
zGetExtensionsResponse,
|
||||
zUpdateProviderResponse,
|
||||
zListProvidersResponse,
|
||||
zGetProviderDetailsResponse,
|
||||
zGetProviderModelsResponse,
|
||||
zReadConfigResponse,
|
||||
zCheckSecretResponse,
|
||||
zExportSessionResponse,
|
||||
zImportSessionResponse
|
||||
]),
|
||||
z.unknown()
|
||||
]).optional()
|
||||
}),
|
||||
z.object({
|
||||
error: z.object({
|
||||
code: z.number().int(),
|
||||
message: z.string(),
|
||||
data: z.unknown().optional()
|
||||
}),
|
||||
id: z.string()
|
||||
})
|
||||
]);
|
||||
@@ -0,0 +1,123 @@
|
||||
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";
|
||||
import { createHttpStream } from "./http-stream.js";
|
||||
|
||||
export class GooseClient {
|
||||
private conn: ClientSideConnection;
|
||||
private ext: GooseExtClient;
|
||||
|
||||
constructor(toClient: () => Client, streamOrUrl: Stream | string) {
|
||||
const stream =
|
||||
typeof streamOrUrl === "string"
|
||||
? createHttpStream(streamOrUrl)
|
||||
: streamOrUrl;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import type { AnyMessage, Stream } from "@agentclientprotocol/sdk";
|
||||
|
||||
const ACP_SESSION_HEADER = "Acp-Session-Id";
|
||||
|
||||
export function createHttpStream(serverUrl: string): Stream {
|
||||
let sessionId: string | null = null;
|
||||
const incoming: AnyMessage[] = [];
|
||||
const waiters: Array<() => void> = [];
|
||||
const sseAbort = new AbortController();
|
||||
|
||||
function pushMessage(msg: AnyMessage) {
|
||||
incoming.push(msg);
|
||||
const w = waiters.shift();
|
||||
if (w) w();
|
||||
}
|
||||
|
||||
function waitForMessage(): Promise<void> {
|
||||
if (incoming.length > 0) return Promise.resolve();
|
||||
return new Promise<void>((r) => waiters.push(r));
|
||||
}
|
||||
|
||||
async function consumeSSE(response: Response) {
|
||||
if (!response.body) return;
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
const parts = buffer.split("\n\n");
|
||||
buffer = parts.pop() || "";
|
||||
|
||||
for (const part of parts) {
|
||||
for (const line of part.split("\n")) {
|
||||
if (line.startsWith("data: ")) {
|
||||
try {
|
||||
const msg = JSON.parse(line.slice(6)) as AnyMessage;
|
||||
pushMessage(msg);
|
||||
} catch {
|
||||
// ignore malformed JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof DOMException && e.name === "AbortError") return;
|
||||
}
|
||||
}
|
||||
|
||||
let isFirstRequest = true;
|
||||
|
||||
const readable = new ReadableStream<AnyMessage>({
|
||||
async pull(controller) {
|
||||
await waitForMessage();
|
||||
while (incoming.length > 0) {
|
||||
controller.enqueue(incoming.shift()!);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const writable = new WritableStream<AnyMessage>({
|
||||
async write(msg) {
|
||||
const isRequest =
|
||||
"method" in msg &&
|
||||
"id" in msg &&
|
||||
msg.id !== undefined &&
|
||||
msg.id !== null;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json, text/event-stream",
|
||||
};
|
||||
if (sessionId) {
|
||||
headers[ACP_SESSION_HEADER] = sessionId;
|
||||
}
|
||||
|
||||
if (isFirstRequest && isRequest) {
|
||||
isFirstRequest = false;
|
||||
|
||||
const response = await fetch(`${serverUrl}/acp`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(msg),
|
||||
signal: sseAbort.signal,
|
||||
});
|
||||
|
||||
const sid = response.headers.get(ACP_SESSION_HEADER);
|
||||
if (sid) sessionId = sid;
|
||||
|
||||
consumeSSE(response);
|
||||
} else if (isRequest) {
|
||||
const abort = new AbortController();
|
||||
fetch(`${serverUrl}/acp`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(msg),
|
||||
signal: abort.signal,
|
||||
}).catch(() => {});
|
||||
setTimeout(() => abort.abort(), 200);
|
||||
} else {
|
||||
await fetch(`${serverUrl}/acp`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(msg),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
sseAbort.abort();
|
||||
},
|
||||
});
|
||||
|
||||
return { readable, writable };
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export * from "./generated/types.gen.js";
|
||||
export * from "./generated/zod.gen.js";
|
||||
export { GooseClient } from "./goose-client.js";
|
||||
export { createHttpStream } from "./http-stream.js";
|
||||
|
||||
export {
|
||||
ClientSideConnection,
|
||||
type Client,
|
||||
type Stream,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
},
|
||||
"include": ["src"],
|
||||
}
|
||||
Reference in New Issue
Block a user