-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin.ts
More file actions
131 lines (125 loc) · 3.95 KB
/
Copy pathplugin.ts
File metadata and controls
131 lines (125 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import type { Hooks, PluginInput } from "@opencode-ai/plugin";
import {
FALLBACK_MODELS,
POOLSIDE_API_KEY_ENV,
POOLSIDE_BASE_URL,
PROVIDER_ID,
PROVIDER_NAME,
} from "./src/constants.ts";
import {
ensureProviderConfig,
fetchModels,
modelsToConfigMap,
} from "./src/models.ts";
import type { OpenCodeProviderConfig } from "./src/types.ts";
/**
* OpenCode plugin that registers the Poolside AI model provider.
*
* The plugin:
* 1. Registers the `poolside` provider using `@ai-sdk/openai-compatible`
* with the correct base URL and environment variable.
* 2. Discovers live models from the Poolside API at config time, falling
* back to a static catalog when the API is unreachable.
* 3. Handles API key management via the auth hook.
*
* Usage in `opencode.json`:
* ```json
* {
* "plugin": ["opencode-provider-poolside"],
* "provider": {
* "poolside": {
* "npm": "@ai-sdk/openai-compatible",
* "name": "Poolside",
* "env": ["POOLSIDE_API_KEY"]
* }
* }
* }
* ```
*/
export default async function poolsidePlugin(
_input: PluginInput
): Promise<Hooks> {
return {
/**
* Config hook: registers the Poolside provider and discovers models.
*
* Called once during OpenCode startup with the full config object.
* The hook mutates the config in place to add or update the `poolside`
* provider entry.
*/
config: async (config): Promise<void> => {
const providerConfig = ensureProviderConfig(
config as Record<string, unknown>,
PROVIDER_ID
);
// Set provider metadata if not already configured.
if (!providerConfig.npm) {
providerConfig.npm = "@ai-sdk/openai-compatible";
}
if (!providerConfig.name) {
providerConfig.name = PROVIDER_NAME;
}
if (!providerConfig.env || !Array.isArray(providerConfig.env)) {
providerConfig.env = [POOLSIDE_API_KEY_ENV];
}
// Set the OpenAI-compatible base URL.
if (!providerConfig.options) {
providerConfig.options = {};
}
if (!providerConfig.options.baseURL) {
providerConfig.options.baseURL = POOLSIDE_BASE_URL;
}
// Preserve any models the user has already configured so they
// are never overwritten by fallbacks or discovery.
const userModels = providerConfig.models ?? {};
const fallbackModels = modelsToConfigMap(FALLBACK_MODELS);
// Attempt live model discovery using the API key from the environment.
const apiKey = process.env[POOLSIDE_API_KEY_ENV];
if (apiKey) {
try {
const discovered = await fetchModels(apiKey);
const discoveredMap = modelsToConfigMap(discovered);
// Merge: fallback first, then discovered (overwrites fallback),
// then user models (never overwritten).
providerConfig.models = {
...fallbackModels,
...discoveredMap,
...userModels,
};
} catch {
// Keep the fallback catalog through transient failures.
providerConfig.models = { ...fallbackModels, ...userModels };
}
} else {
// No API key: use fallback models alongside any user models.
providerConfig.models = { ...fallbackModels, ...userModels };
}
},
/**
* Auth hook: manages Poolside API key credentials.
*
* Supports the standard API key auth method. The loader returns the
* key as `{ apiKey }` so OpenCode can inject it into the provider's
* environment.
*/
auth: {
provider: PROVIDER_ID,
methods: [
{
type: "api" as const,
label: "API Key",
},
],
loader: async (getAuth): Promise<Record<string, unknown>> => {
try {
const auth = await getAuth();
if (!auth) return {};
if (auth.type === "api" && auth.key) return { apiKey: auth.key };
return {};
} catch {
return {};
}
},
},
};
}