@vymalo/opencode-oauth2 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/dist/cache.d.ts +9 -0
  4. package/dist/cache.js +91 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/config.d.ts +97 -0
  7. package/dist/config.js +179 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/index.js +8 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/logging.d.ts +11 -0
  13. package/dist/logging.js +59 -0
  14. package/dist/logging.js.map +1 -0
  15. package/dist/model-discovery.d.ts +9 -0
  16. package/dist/model-discovery.js +74 -0
  17. package/dist/model-discovery.js.map +1 -0
  18. package/dist/model-normalization.d.ts +4 -0
  19. package/dist/model-normalization.js +86 -0
  20. package/dist/model-normalization.js.map +1 -0
  21. package/dist/oauth/browser.d.ts +1 -0
  22. package/dist/oauth/browser.js +29 -0
  23. package/dist/oauth/browser.js.map +1 -0
  24. package/dist/oauth/client.d.ts +56 -0
  25. package/dist/oauth/client.js +436 -0
  26. package/dist/oauth/client.js.map +1 -0
  27. package/dist/oauth/device-code.d.ts +22 -0
  28. package/dist/oauth/device-code.js +240 -0
  29. package/dist/oauth/device-code.js.map +1 -0
  30. package/dist/oauth/discovery.d.ts +8 -0
  31. package/dist/oauth/discovery.js +38 -0
  32. package/dist/oauth/discovery.js.map +1 -0
  33. package/dist/oauth/http-utils.d.ts +33 -0
  34. package/dist/oauth/http-utils.js +129 -0
  35. package/dist/oauth/http-utils.js.map +1 -0
  36. package/dist/oauth/local-callback.d.ts +10 -0
  37. package/dist/oauth/local-callback.js +82 -0
  38. package/dist/oauth/local-callback.js.map +1 -0
  39. package/dist/oauth/pkce.d.ts +5 -0
  40. package/dist/oauth/pkce.js +13 -0
  41. package/dist/oauth/pkce.js.map +1 -0
  42. package/dist/oauth/subject-token.d.ts +20 -0
  43. package/dist/oauth/subject-token.js +86 -0
  44. package/dist/oauth/subject-token.js.map +1 -0
  45. package/dist/opencode.d.ts +11 -0
  46. package/dist/opencode.js +347 -0
  47. package/dist/opencode.js.map +1 -0
  48. package/dist/plugin.d.ts +47 -0
  49. package/dist/plugin.js +220 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/scheduler.d.ts +11 -0
  52. package/dist/scheduler.js +44 -0
  53. package/dist/scheduler.js.map +1 -0
  54. package/dist/types.d.ts +37 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vymalo contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # @vymalo/opencode-oauth2
2
+
3
+ OAuth2/OIDC model sync plugin for OpenCode. This is the canonical configuration reference for the plugin. Long-form usage guides (CI cookbooks, Kubernetes manifests, troubleshooting) live in [`/docs/`](../../docs/) at the repo root.
4
+
5
+ ## What It Does
6
+
7
+ - Registers OpenAI-compatible providers into OpenCode config
8
+ - Supports five OAuth2 grants:
9
+ - `authorization_code` — interactive PKCE login (default)
10
+ - `device_code` — RFC 8628, for browserless user auth
11
+ - `client_credentials` — machine-to-machine via `clientSecret`
12
+ - `jwt_bearer` — RFC 7523, federated identity (e.g. GitHub Actions OIDC, K8s SA tokens) — no long-lived secret in CI
13
+ - `token_exchange` — RFC 8693, federated identity with explicit audience targeting
14
+ - Stores and refreshes provider access tokens
15
+ - Fetches and normalizes provider model catalogs
16
+ - Injects `Authorization` headers at chat request time
17
+
18
+ ## Install
19
+
20
+ Add plugin package to OpenCode:
21
+
22
+ ```jsonc
23
+ {
24
+ "$schema": "https://opencode.ai/config.json",
25
+ "plugin": ["@vymalo/opencode-oauth2"]
26
+ }
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ ### Option A: Provider-embedded OAuth config (recommended)
32
+
33
+ ```jsonc
34
+ {
35
+ "$schema": "https://opencode.ai/config.json",
36
+ "plugin": ["@vymalo/opencode-oauth2"],
37
+ "provider": {
38
+ "example-ai": {
39
+ "name": "Example AI",
40
+ "options": {
41
+ "baseURL": "https://api.example.com/v1",
42
+ "oauth2": {
43
+ "issuer": "https://auth.example.com",
44
+ "clientId": "opencode-client",
45
+ "scopes": ["openid", "profile", "offline_access"],
46
+ "syncIntervalMinutes": 60,
47
+ "nameOverrides": {
48
+ "glm-5": "GLM 5"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### Option B: `pluginConfig.oauth2ModelSync.servers`
58
+
59
+ ```jsonc
60
+ {
61
+ "$schema": "https://opencode.ai/config.json",
62
+ "plugin": ["@vymalo/opencode-oauth2"],
63
+ "pluginConfig": {
64
+ "oauth2ModelSync": {
65
+ "servers": [
66
+ {
67
+ "id": "example-ai",
68
+ "name": "Example AI",
69
+ "issuer": "https://auth.example.com",
70
+ "baseURL": "https://api.example.com/v1",
71
+ "clientId": "opencode-client",
72
+ "scopes": ["openid", "profile", "offline_access"],
73
+ "syncIntervalMinutes": 60,
74
+ "nameOverrides": {
75
+ "glm-5": "GLM 5"
76
+ }
77
+ }
78
+ ]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Optional fields
85
+
86
+ These apply to both config shapes above.
87
+
88
+ | Field | Default | Notes |
89
+ | --- | --- | --- |
90
+ | `clientSecret` | _(unset)_ | For confidential clients. Sent as `client_secret` on every token-endpoint POST. Never logged. Required for `authFlow: "client_credentials"`. Optional but commonly required for `jwt_bearer` / `token_exchange` with confidential clients. PKCE is still required for `authorization_code`. |
91
+ | `authFlow` | `"authorization_code"` | One of `"authorization_code"`, `"device_code"`, `"client_credentials"`, `"jwt_bearer"`, `"token_exchange"`. |
92
+ | `subjectTokenSource` | _(required for `jwt_bearer` / `token_exchange`)_ | Where to read the platform JWT to present as the subject token. See [Federated identity](#federated-identity-no-long-lived-secrets-in-ci) below. |
93
+ | `tokenExchangeAudience` | _(unset)_ | Optional `audience` parameter for the `token_exchange` grant. |
94
+ | `deviceAuthorizationEndpoint` | discovered | Override for the device authorization endpoint. Otherwise discovered from `device_authorization_endpoint` in the OIDC metadata. Only used when `authFlow === "device_code"`. |
95
+ | `authorizationEndpoint` | discovered | Override for the authorization endpoint. |
96
+ | `tokenEndpoint` | discovered | Override for the token endpoint. |
97
+ | `redirectPort` | random | Fixed port for the local callback server (authorization-code only). |
98
+ | `nameOverrides` | `{}` | Map of model id → friendly display name. Applied during catalog normalization. |
99
+ | `syncIntervalMinutes` | `60` | Per-server scheduler interval. Failures preserve the last-known-good model list. |
100
+ | `jwksUri` | _(unset)_ | Reserved; not currently used at runtime. |
101
+
102
+ Plus the top-level `pluginConfig.oauth2ModelSync` block accepts:
103
+
104
+ | Field | Default | Notes |
105
+ | --- | --- | --- |
106
+ | `cacheNamespace` | `"opencode-oauth2-model-sync"` (OpenCode-hosted) / `"oauth2-model-sync"` (standalone) | Subdirectory under the OS cache root. See [architecture.md](../../docs/architecture.md#cache-layout) for the path table per OS. |
107
+ | `httpTimeoutMs` | `15000` | Timeout for token-endpoint / `/models` round trips. |
108
+ | `tokenExpirySkewMs` | `30000` | Treat a token as expired this many ms before its real `expiresAt`. |
109
+
110
+ ## Federated identity (no long-lived secrets in CI)
111
+
112
+ For CI runners and Kubernetes workloads, the modern best practice is to skip stored client secrets entirely and use the platform's own short-lived OIDC token to authenticate. `@vymalo/opencode-oauth2` supports this via the **`jwt_bearer`** (RFC 7523) and **`token_exchange`** (RFC 8693) grants.
113
+
114
+ The plugin reads the platform JWT at token-acquisition time (never caches it) and presents it to your OAuth server as proof of identity. The OAuth server validates the JWT signature against the platform's JWKS, applies your IdP's policy, and returns an access token.
115
+
116
+ `subjectTokenSource` tells the plugin where to read the JWT:
117
+
118
+ | `type` | Reads from | Required fields |
119
+ | --- | --- | --- |
120
+ | `github_actions` | `ACTIONS_ID_TOKEN_REQUEST_URL` + `ACTIONS_ID_TOKEN_REQUEST_TOKEN` env vars | `audience` |
121
+ | `kubernetes_sa` | Projected service-account token file (default `/var/run/secrets/tokens/oauth2/token`) | _(optional `tokenPath`)_ |
122
+ | `file` | Arbitrary file path | `path` |
123
+ | `env` | Environment variable (dev/test only) | `var` |
124
+
125
+ End-to-end recipes:
126
+
127
+ - **GitHub Actions** — see [`docs/github-actions.md`](../../docs/github-actions.md) for the Keycloak / Auth0 / Okta setup walkthroughs, the reusable workflow at [`.github/workflows/opencode-run.yml`](../../.github/workflows/opencode-run.yml), matrix builds, audience pinning, and fork-PR limitations.
128
+ - **Kubernetes** — see [`docs/kubernetes.md`](../../docs/kubernetes.md) for the `CronJob` (headline), `Job`, and `Deployment` manifests, multi-provider pods, IdP setup with Keycloak/Dex, and RBAC notes (spoiler: you need almost none).
129
+
130
+ ### Quick GHA reference
131
+
132
+ ```jsonc
133
+ {
134
+ "provider": {
135
+ "example-ai": {
136
+ "options": {
137
+ "baseURL": "https://api.example.com/v1",
138
+ "oauth2": {
139
+ "issuer": "https://auth.example.com/realms/example",
140
+ "clientId": "ci-runner",
141
+ "scopes": ["openid"],
142
+ "authFlow": "jwt_bearer",
143
+ "subjectTokenSource": {
144
+ "type": "github_actions",
145
+ "audience": "https://auth.example.com/realms/example"
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ Workflow needs `permissions: { id-token: write }`. No `clientSecret` anywhere.
155
+
156
+ ### Quick Kubernetes reference
157
+
158
+ ```jsonc
159
+ {
160
+ "provider": {
161
+ "example-ai": {
162
+ "options": {
163
+ "baseURL": "https://api.example.com/v1",
164
+ "oauth2": {
165
+ "issuer": "https://auth.example.com/realms/example",
166
+ "clientId": "k8s-runner",
167
+ "scopes": ["openid"],
168
+ "authFlow": "jwt_bearer",
169
+ "subjectTokenSource": {
170
+ "type": "kubernetes_sa"
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ The pod must mount a projected `serviceAccountToken` at `/var/run/secrets/tokens/oauth2/token` with the IdP's expected `audience`. The projected token rotates automatically (kubelet refreshes it); the plugin re-reads on every access-token expiry, so rotation is transparent. Full manifests in [`docs/kubernetes.md`](../../docs/kubernetes.md).
180
+
181
+ ### Choosing between `jwt_bearer` and `token_exchange`
182
+
183
+ - **`jwt_bearer`** is the standard federated grant. Single POST: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`, `assertion=<JWT>`. Keycloak, Auth0, Okta all support it. **Start here.**
184
+ - **`token_exchange`** (RFC 8693) is more general — supports `subject_token_type`, `actor_token`, `requested_token_type`, and an explicit `audience` claim. Use only when your IdP requires it or when you need the audience targeting (set `tokenExchangeAudience`).
185
+
186
+ ## OAuth Token Requirements
187
+
188
+ Refresh tokens are mandatory for the flows that issue them (`authorization_code`, `device_code`).
189
+
190
+ - Initial token exchange for those flows must return `refresh_token`; missing → rejected.
191
+ - Cached tokens missing `refreshToken` are invalidated on load (unless the flow doesn't issue one — `client_credentials`, `jwt_bearer`, `token_exchange`).
192
+ - Refresh flow preserves the previous refresh token when providers omit it in refresh responses.
193
+
194
+ ## Hooks Used
195
+
196
+ - `config`: register/patch provider config and merge cached discovered models
197
+ - `chat.headers`: ensure valid token and set `Authorization` header
198
+
199
+ See [architecture.md](../../docs/architecture.md#the-two-hooks) for the full hook semantics.
200
+
201
+ ## Development
202
+
203
+ ```sh
204
+ pnpm --filter @vymalo/opencode-oauth2 typecheck
205
+ pnpm --filter @vymalo/opencode-oauth2 test
206
+ pnpm --filter @vymalo/opencode-oauth2 build
207
+ ```
208
+
209
+ ## Exports
210
+
211
+ - `OpencodeOauth2Plugin` (default OpenCode plugin export)
212
+ - `createOpencodeOauth2Plugin()` (factory for testing / custom wiring)
213
+ - `OAuth2ModelSyncPlugin` (runtime orchestrator — useful for embedding outside OpenCode)
@@ -0,0 +1,9 @@
1
+ import type { CachedServerState } from "./types.js";
2
+ export declare function resolveCacheDir(namespace: string): string;
3
+ export declare class FileCacheStore {
4
+ private readonly baseDir;
5
+ constructor(baseDir: string);
6
+ ensureReady(): Promise<void>;
7
+ loadServerState(serverId: string): Promise<CachedServerState | undefined>;
8
+ saveServerState(state: CachedServerState): Promise<void>;
9
+ }
package/dist/cache.js ADDED
@@ -0,0 +1,91 @@
1
+ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { constants as fsConstants } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ function resolveDefaultCacheRoot() {
6
+ if (process.platform === "win32") {
7
+ return process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
8
+ }
9
+ if (process.platform === "darwin") {
10
+ return join(homedir(), "Library", "Caches");
11
+ }
12
+ return process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
13
+ }
14
+ export function resolveCacheDir(namespace) {
15
+ return join(resolveDefaultCacheRoot(), "opencode-oauth2", namespace);
16
+ }
17
+ function statePath(baseDir, serverId) {
18
+ return join(baseDir, `${serverId}.json`);
19
+ }
20
+ function hasValidTokenShape(token) {
21
+ if (!token || typeof token !== "object" || Array.isArray(token)) {
22
+ return false;
23
+ }
24
+ const candidate = token;
25
+ if (typeof candidate.accessToken !== "string" ||
26
+ candidate.accessToken.length === 0 ||
27
+ typeof candidate.tokenType !== "string" ||
28
+ candidate.tokenType.length === 0) {
29
+ return false;
30
+ }
31
+ // refreshToken is optional: client_credentials grant never returns one.
32
+ // When present, it must be a non-empty string.
33
+ if (candidate.refreshToken !== undefined) {
34
+ return typeof candidate.refreshToken === "string" && candidate.refreshToken.length > 0;
35
+ }
36
+ return true;
37
+ }
38
+ export class FileCacheStore {
39
+ baseDir;
40
+ constructor(baseDir) {
41
+ this.baseDir = baseDir;
42
+ }
43
+ async ensureReady() {
44
+ await mkdir(this.baseDir, { recursive: true, mode: 0o700 });
45
+ }
46
+ async loadServerState(serverId) {
47
+ const filePath = statePath(this.baseDir, serverId);
48
+ try {
49
+ const content = await readFile(filePath, "utf8");
50
+ const parsed = JSON.parse(content);
51
+ if (!parsed || parsed.serverId !== serverId) {
52
+ return undefined;
53
+ }
54
+ if (!Array.isArray(parsed.models) || !Array.isArray(parsed.rawModels)) {
55
+ return undefined;
56
+ }
57
+ if (parsed.token && !hasValidTokenShape(parsed.token)) {
58
+ parsed.token = undefined;
59
+ }
60
+ return parsed;
61
+ }
62
+ catch (error) {
63
+ if (typeof error === "object" &&
64
+ error &&
65
+ "code" in error &&
66
+ error.code === "ENOENT") {
67
+ return undefined;
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ async saveServerState(state) {
73
+ const filePath = statePath(this.baseDir, state.serverId);
74
+ const tempPath = `${filePath}.tmp`;
75
+ await mkdir(dirname(filePath), { recursive: true, mode: 0o700 });
76
+ const serialized = JSON.stringify(state, null, 2);
77
+ await writeFile(tempPath, serialized, {
78
+ encoding: "utf8",
79
+ mode: 0o600,
80
+ flag: fsConstants.O_CREAT | fsConstants.O_TRUNC | fsConstants.O_WRONLY
81
+ });
82
+ await rename(tempPath, filePath);
83
+ try {
84
+ await chmod(filePath, 0o600);
85
+ }
86
+ catch {
87
+ // Some filesystems may ignore chmod semantics; keep operation non-fatal.
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAIlC,SAAS,uBAAuB;IAC9B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,IAAI,CAAC,uBAAuB,EAAE,EAAE,iBAAiB,EAAE,SAAS,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,SAAS,CAAC,OAAe,EAAE,QAAgB;IAClD,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,KAAgC,CAAC;IACnD,IACE,OAAO,SAAS,CAAC,WAAW,KAAK,QAAQ;QACzC,SAAS,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QAClC,OAAO,SAAS,CAAC,SAAS,KAAK,QAAQ;QACvC,SAAS,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAChC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wEAAwE;IACxE,+CAA+C;IAC/C,IAAI,SAAS,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACzC,OAAO,OAAO,SAAS,CAAC,YAAY,KAAK,QAAQ,IAAI,SAAS,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEhD,KAAK,CAAC,WAAW;QACf,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAgB;QACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEnD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC;YAExD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC5C,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtE,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtD,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;YAC3B,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK;gBACL,MAAM,IAAI,KAAK;gBACd,KAA0B,CAAC,IAAI,KAAK,QAAQ,EAC7C,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAwB;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,GAAG,QAAQ,MAAM,CAAC;QAEnC,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEjE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE;YACpC,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,KAAK;YACX,IAAI,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,QAAQ;SACvE,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAEjC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;QAC3E,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,97 @@
1
+ export declare const DEFAULT_SYNC_INTERVAL_MINUTES = 60;
2
+ export declare const DEFAULT_HTTP_TIMEOUT_MS = 15000;
3
+ export declare const DEFAULT_TOKEN_EXPIRY_SKEW_MS = 30000;
4
+ export type OAuthAuthFlow = "authorization_code" | "device_code" | "client_credentials" | "jwt_bearer" | "token_exchange";
5
+ export declare const DEFAULT_AUTH_FLOW: OAuthAuthFlow;
6
+ /**
7
+ * Where to read the platform-supplied JWT that the plugin presents as the
8
+ * subject token / assertion for the `jwt_bearer` and `token_exchange` flows.
9
+ *
10
+ * - `github_actions` — fetches the OIDC token from the GitHub Actions runtime
11
+ * via `ACTIONS_ID_TOKEN_REQUEST_URL` + `ACTIONS_ID_TOKEN_REQUEST_TOKEN`,
12
+ * with the workflow-declared `audience`. The `id-token: write` permission
13
+ * is required on the job.
14
+ * - `kubernetes_sa` — reads a projected service-account token from the pod
15
+ * filesystem. Default path `/var/run/secrets/tokens/oauth2/token`; mount a
16
+ * `projected.sources.serviceAccountToken` volume with the OIDC issuer as
17
+ * the audience.
18
+ * - `file` — reads any JWT from disk. Useful when an external sidecar
19
+ * refreshes the token to a fixed path.
20
+ * - `env` — reads the JWT from a named environment variable. Mostly useful
21
+ * for tests and ad-hoc shells.
22
+ */
23
+ export type SubjectTokenSource = {
24
+ type: "github_actions";
25
+ audience: string;
26
+ } | {
27
+ type: "kubernetes_sa";
28
+ tokenPath?: string;
29
+ } | {
30
+ type: "file";
31
+ path: string;
32
+ } | {
33
+ type: "env";
34
+ var: string;
35
+ };
36
+ export declare const DEFAULT_K8S_SA_TOKEN_PATH = "/var/run/secrets/tokens/oauth2/token";
37
+ export interface OAuthServerConfigInput {
38
+ id: string;
39
+ name?: string;
40
+ issuer: string;
41
+ baseURL: string;
42
+ clientId: string;
43
+ clientSecret?: string;
44
+ scopes: string[];
45
+ syncIntervalMinutes?: number;
46
+ nameOverrides?: Record<string, string>;
47
+ authorizationEndpoint?: string;
48
+ tokenEndpoint?: string;
49
+ deviceAuthorizationEndpoint?: string;
50
+ jwksUri?: string;
51
+ redirectPort?: number;
52
+ authFlow?: OAuthAuthFlow;
53
+ /**
54
+ * Required for `jwt_bearer` and `token_exchange`. Tells the plugin where to
55
+ * read the platform JWT it should present as the subject token.
56
+ */
57
+ subjectTokenSource?: SubjectTokenSource;
58
+ /**
59
+ * Optional `audience` parameter for the `token_exchange` grant — the
60
+ * intended recipient of the resulting access token. Set this when the
61
+ * OAuth server expects an explicit audience claim distinct from the
62
+ * issuer.
63
+ */
64
+ tokenExchangeAudience?: string;
65
+ }
66
+ export interface OAuth2ModelSyncConfigInput {
67
+ servers: OAuthServerConfigInput[];
68
+ cacheNamespace?: string;
69
+ httpTimeoutMs?: number;
70
+ tokenExpirySkewMs?: number;
71
+ }
72
+ export interface OAuthServerConfig {
73
+ id: string;
74
+ name: string;
75
+ issuer: string;
76
+ baseURL: string;
77
+ clientId: string;
78
+ clientSecret?: string;
79
+ scopes: string[];
80
+ syncIntervalMinutes: number;
81
+ nameOverrides: Record<string, string>;
82
+ authorizationEndpoint?: string;
83
+ tokenEndpoint?: string;
84
+ deviceAuthorizationEndpoint?: string;
85
+ jwksUri?: string;
86
+ redirectPort?: number;
87
+ authFlow: OAuthAuthFlow;
88
+ subjectTokenSource?: SubjectTokenSource;
89
+ tokenExchangeAudience?: string;
90
+ }
91
+ export interface OAuth2ModelSyncConfig {
92
+ servers: OAuthServerConfig[];
93
+ cacheNamespace: string;
94
+ httpTimeoutMs: number;
95
+ tokenExpirySkewMs: number;
96
+ }
97
+ export declare function validateConfig(input: OAuth2ModelSyncConfigInput): OAuth2ModelSyncConfig;
package/dist/config.js ADDED
@@ -0,0 +1,179 @@
1
+ export const DEFAULT_SYNC_INTERVAL_MINUTES = 60;
2
+ export const DEFAULT_HTTP_TIMEOUT_MS = 15_000;
3
+ export const DEFAULT_TOKEN_EXPIRY_SKEW_MS = 30_000;
4
+ export const DEFAULT_AUTH_FLOW = "authorization_code";
5
+ export const DEFAULT_K8S_SA_TOKEN_PATH = "/var/run/secrets/tokens/oauth2/token";
6
+ function ensureString(value, path) {
7
+ if (typeof value !== "string" || value.trim().length === 0) {
8
+ throw new Error(`${path} must be a non-empty string`);
9
+ }
10
+ return value.trim();
11
+ }
12
+ function ensureStringArray(value, path) {
13
+ if (!Array.isArray(value) || value.length === 0) {
14
+ throw new Error(`${path} must be a non-empty array of strings`);
15
+ }
16
+ return value.map((item, index) => ensureString(item, `${path}[${index}]`));
17
+ }
18
+ function validateRedirectPort(value, path) {
19
+ if (value === undefined || value === null) {
20
+ return undefined;
21
+ }
22
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0 || value >= 65536) {
23
+ throw new Error(`${path} must be a positive integer less than 65536`);
24
+ }
25
+ return value;
26
+ }
27
+ function validateAuthFlow(value, path) {
28
+ if (value === undefined || value === null) {
29
+ return DEFAULT_AUTH_FLOW;
30
+ }
31
+ if (value === "authorization_code" ||
32
+ value === "device_code" ||
33
+ value === "client_credentials" ||
34
+ value === "jwt_bearer" ||
35
+ value === "token_exchange") {
36
+ return value;
37
+ }
38
+ throw new Error(`${path} must be one of "authorization_code" | "device_code" | "client_credentials" | "jwt_bearer" | "token_exchange" (received ${JSON.stringify(value)})`);
39
+ }
40
+ function validateSubjectTokenSource(value, path) {
41
+ if (value === undefined || value === null) {
42
+ return undefined;
43
+ }
44
+ if (typeof value !== "object" || Array.isArray(value)) {
45
+ throw new Error(`${path} must be an object`);
46
+ }
47
+ const record = value;
48
+ const type = record.type;
49
+ switch (type) {
50
+ case "github_actions": {
51
+ const audience = record.audience;
52
+ if (typeof audience !== "string" || audience.trim().length === 0) {
53
+ throw new Error(`${path}.audience must be a non-empty string when type is "github_actions"`);
54
+ }
55
+ return { type: "github_actions", audience: audience.trim() };
56
+ }
57
+ case "kubernetes_sa": {
58
+ const tokenPath = record.tokenPath;
59
+ if (tokenPath !== undefined && tokenPath !== null) {
60
+ if (typeof tokenPath !== "string" || tokenPath.trim().length === 0) {
61
+ throw new Error(`${path}.tokenPath must be a non-empty string when provided`);
62
+ }
63
+ return { type: "kubernetes_sa", tokenPath: tokenPath.trim() };
64
+ }
65
+ return { type: "kubernetes_sa" };
66
+ }
67
+ case "file": {
68
+ const filePath = record.path;
69
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
70
+ throw new Error(`${path}.path must be a non-empty string when type is "file"`);
71
+ }
72
+ return { type: "file", path: filePath.trim() };
73
+ }
74
+ case "env": {
75
+ const varName = record.var;
76
+ if (typeof varName !== "string" || varName.trim().length === 0) {
77
+ throw new Error(`${path}.var must be a non-empty string when type is "env"`);
78
+ }
79
+ return { type: "env", var: varName.trim() };
80
+ }
81
+ default:
82
+ throw new Error(`${path}.type must be one of "github_actions" | "kubernetes_sa" | "file" | "env" (received ${JSON.stringify(type)})`);
83
+ }
84
+ }
85
+ function normalizeServerConfig(input, index) {
86
+ const path = `servers[${index}]`;
87
+ const id = ensureString(input.id, `${path}.id`);
88
+ const name = input.name && input.name.trim().length > 0 ? input.name.trim() : id;
89
+ const issuer = ensureString(input.issuer, `${path}.issuer`);
90
+ const baseURL = ensureString(input.baseURL, `${path}.baseURL`);
91
+ const clientId = ensureString(input.clientId, `${path}.clientId`);
92
+ const scopes = ensureStringArray(input.scopes, `${path}.scopes`);
93
+ const syncIntervalMinutes = typeof input.syncIntervalMinutes === "number" &&
94
+ Number.isFinite(input.syncIntervalMinutes) &&
95
+ input.syncIntervalMinutes > 0
96
+ ? input.syncIntervalMinutes
97
+ : DEFAULT_SYNC_INTERVAL_MINUTES;
98
+ const redirectPort = validateRedirectPort(input.redirectPort, `${path}.redirectPort`);
99
+ const authFlow = validateAuthFlow(input.authFlow, `${path}.authFlow`);
100
+ const subjectTokenSource = validateSubjectTokenSource(input.subjectTokenSource, `${path}.subjectTokenSource`);
101
+ let clientSecret;
102
+ if (input.clientSecret !== undefined && input.clientSecret !== null) {
103
+ if (typeof input.clientSecret !== "string" || input.clientSecret.length === 0) {
104
+ throw new Error(`${path}.clientSecret must be a non-empty string when provided`);
105
+ }
106
+ clientSecret = input.clientSecret;
107
+ }
108
+ if (authFlow === "client_credentials" && !clientSecret) {
109
+ throw new Error(`${path}.clientSecret is required when authFlow is "client_credentials"`);
110
+ }
111
+ if ((authFlow === "jwt_bearer" || authFlow === "token_exchange") && !subjectTokenSource) {
112
+ throw new Error(`${path}.subjectTokenSource is required when authFlow is "${authFlow}" — set it to {type: "github_actions" | "kubernetes_sa" | "file" | "env", ...}`);
113
+ }
114
+ let tokenExchangeAudience;
115
+ if (input.tokenExchangeAudience !== undefined && input.tokenExchangeAudience !== null) {
116
+ if (typeof input.tokenExchangeAudience !== "string" ||
117
+ input.tokenExchangeAudience.trim().length === 0) {
118
+ throw new Error(`${path}.tokenExchangeAudience must be a non-empty string when provided`);
119
+ }
120
+ tokenExchangeAudience = input.tokenExchangeAudience.trim();
121
+ }
122
+ return {
123
+ id,
124
+ name,
125
+ issuer,
126
+ baseURL,
127
+ clientId,
128
+ clientSecret,
129
+ scopes,
130
+ syncIntervalMinutes,
131
+ nameOverrides: input.nameOverrides ?? {},
132
+ authorizationEndpoint: input.authorizationEndpoint,
133
+ tokenEndpoint: input.tokenEndpoint,
134
+ deviceAuthorizationEndpoint: input.deviceAuthorizationEndpoint,
135
+ jwksUri: input.jwksUri,
136
+ redirectPort,
137
+ authFlow,
138
+ subjectTokenSource,
139
+ tokenExchangeAudience
140
+ };
141
+ }
142
+ export function validateConfig(input) {
143
+ if (!input || typeof input !== "object") {
144
+ throw new Error("plugin config must be an object");
145
+ }
146
+ if (!Array.isArray(input.servers) || input.servers.length === 0) {
147
+ throw new Error("servers must be a non-empty array");
148
+ }
149
+ const normalizedServers = input.servers.map(normalizeServerConfig);
150
+ const ids = new Set();
151
+ for (const server of normalizedServers) {
152
+ if (ids.has(server.id)) {
153
+ throw new Error(`duplicate server id detected: ${server.id}`);
154
+ }
155
+ ids.add(server.id);
156
+ }
157
+ let tokenExpirySkewMs = DEFAULT_TOKEN_EXPIRY_SKEW_MS;
158
+ if (input.tokenExpirySkewMs !== undefined && input.tokenExpirySkewMs !== null) {
159
+ if (typeof input.tokenExpirySkewMs !== "number" ||
160
+ !Number.isFinite(input.tokenExpirySkewMs) ||
161
+ input.tokenExpirySkewMs <= 0) {
162
+ throw new Error("tokenExpirySkewMs must be a positive number");
163
+ }
164
+ tokenExpirySkewMs = input.tokenExpirySkewMs;
165
+ }
166
+ return {
167
+ servers: normalizedServers,
168
+ cacheNamespace: typeof input.cacheNamespace === "string" && input.cacheNamespace.trim().length > 0
169
+ ? input.cacheNamespace.trim()
170
+ : "oauth2-model-sync",
171
+ httpTimeoutMs: typeof input.httpTimeoutMs === "number" &&
172
+ Number.isFinite(input.httpTimeoutMs) &&
173
+ input.httpTimeoutMs > 0
174
+ ? input.httpTimeoutMs
175
+ : DEFAULT_HTTP_TIMEOUT_MS,
176
+ tokenExpirySkewMs
177
+ };
178
+ }
179
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AASnD,MAAM,CAAC,MAAM,iBAAiB,GAAkB,oBAAoB,CAAC;AAyBrE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sCAAsC,CAAC;AAkEhF,SAAS,YAAY,CAAC,KAAc,EAAE,IAAY;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6BAA6B,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE,IAAY;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uCAAuC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAc,EAAE,IAAY;IACxD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1F,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6CAA6C,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAE,IAAY;IACpD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,IACE,KAAK,KAAK,oBAAoB;QAC9B,KAAK,KAAK,aAAa;QACvB,KAAK,KAAK,oBAAoB;QAC9B,KAAK,KAAK,YAAY;QACtB,KAAK,KAAK,gBAAgB,EAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,2HAA2H,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAC3J,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAc,EAAE,IAAY;IAC9D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oBAAoB,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,oEAAoE,CAC5E,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/D,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;YACnC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;gBAClD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACnE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,qDAAqD,CAAC,CAAC;gBAChF,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YAChE,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;QACnC,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC;YAC7B,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,sDAAsD,CAAC,CAAC;YACjF,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC;YAC3B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/D,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oDAAoD,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,sFAAsF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CACrH,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAA6B,EAAE,KAAa;IACzE,MAAM,IAAI,GAAG,WAAW,KAAK,GAAG,CAAC;IAEjC,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,UAAU,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,IAAI,WAAW,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;IAEjE,MAAM,mBAAmB,GACvB,OAAO,KAAK,CAAC,mBAAmB,KAAK,QAAQ;QAC7C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC;QAC1C,KAAK,CAAC,mBAAmB,GAAG,CAAC;QAC3B,CAAC,CAAC,KAAK,CAAC,mBAAmB;QAC3B,CAAC,CAAC,6BAA6B,CAAC;IAEpC,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,IAAI,eAAe,CAAC,CAAC;IACtF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,IAAI,WAAW,CAAC,CAAC;IACtE,MAAM,kBAAkB,GAAG,0BAA0B,CACnD,KAAK,CAAC,kBAAkB,EACxB,GAAG,IAAI,qBAAqB,CAC7B,CAAC;IAEF,IAAI,YAAgC,CAAC;IACrC,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACpE,IAAI,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wDAAwD,CAAC,CAAC;QACnF,CAAC;QACD,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IACpC,CAAC;IAED,IAAI,QAAQ,KAAK,oBAAoB,IAAI,CAAC,YAAY,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,iEAAiE,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,gBAAgB,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,qDAAqD,QAAQ,gFAAgF,CACrJ,CAAC;IACJ,CAAC;IAED,IAAI,qBAAyC,CAAC;IAC9C,IAAI,KAAK,CAAC,qBAAqB,KAAK,SAAS,IAAI,KAAK,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;QACtF,IACE,OAAO,KAAK,CAAC,qBAAqB,KAAK,QAAQ;YAC/C,KAAK,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAC/C,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,iEAAiE,CAAC,CAAC;QAC5F,CAAC;QACD,qBAAqB,GAAG,KAAK,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IAC7D,CAAC;IAED,OAAO;QACL,EAAE;QACF,IAAI;QACJ,MAAM;QACN,OAAO;QACP,QAAQ;QACR,YAAY;QACZ,MAAM;QACN,mBAAmB;QACnB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,EAAE;QACxC,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,2BAA2B,EAAE,KAAK,CAAC,2BAA2B;QAC9D,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,YAAY;QACZ,QAAQ;QACR,kBAAkB;QAClB,qBAAqB;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAiC;IAC9D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9B,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,iBAAiB,GAAG,4BAA4B,CAAC;IACrD,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS,IAAI,KAAK,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;QAC9E,IACE,OAAO,KAAK,CAAC,iBAAiB,KAAK,QAAQ;YAC3C,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACzC,KAAK,CAAC,iBAAiB,IAAI,CAAC,EAC5B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,OAAO,EAAE,iBAAiB;QAC1B,cAAc,EACZ,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAChF,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE;YAC7B,CAAC,CAAC,mBAAmB;QACzB,aAAa,EACX,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ;YACvC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC;YACpC,KAAK,CAAC,aAAa,GAAG,CAAC;YACrB,CAAC,CAAC,KAAK,CAAC,aAAa;YACrB,CAAC,CAAC,uBAAuB;QAC7B,iBAAiB;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { DEFAULT_HTTP_TIMEOUT_MS, DEFAULT_SYNC_INTERVAL_MINUTES, type OAuth2ModelSyncConfig, type OAuth2ModelSyncConfigInput, type OAuthServerConfig, type OAuthServerConfigInput, validateConfig } from "./config.js";
2
+ export { createJsonConsoleLogger, type LogLevel, type Logger } from "./logging.js";
3
+ export { buildModelsUrl, fetchModels } from "./model-discovery.js";
4
+ export { diffModels, normalizeModelId, normalizeModelList } from "./model-normalization.js";
5
+ export { OAuth2ModelSyncPlugin, type PluginOptions } from "./plugin.js";
6
+ export { resolveCacheDir, FileCacheStore } from "./cache.js";
7
+ export { createOpencodeOauth2Plugin, OpencodeOauth2Plugin, OpencodeOauth2Plugin as default } from "./opencode.js";
8
+ export type { OpenCodePluginFactoryOptions } from "./opencode.js";
9
+ export type { CachedServerState, ModelDiff, NormalizedModel, RawModel, ServerSnapshot, TokenSet } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { DEFAULT_HTTP_TIMEOUT_MS, DEFAULT_SYNC_INTERVAL_MINUTES, validateConfig } from "./config.js";
2
+ export { createJsonConsoleLogger } from "./logging.js";
3
+ export { buildModelsUrl, fetchModels } from "./model-discovery.js";
4
+ export { diffModels, normalizeModelId, normalizeModelList } from "./model-normalization.js";
5
+ export { OAuth2ModelSyncPlugin } from "./plugin.js";
6
+ export { resolveCacheDir, FileCacheStore } from "./cache.js";
7
+ export { createOpencodeOauth2Plugin, OpencodeOauth2Plugin, OpencodeOauth2Plugin as default } from "./opencode.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAK7B,cAAc,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,uBAAuB,EAA8B,MAAM,cAAc,CAAC;AAEnF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE5F,OAAO,EAAE,qBAAqB,EAAsB,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,IAAI,OAAO,EAChC,MAAM,eAAe,CAAC"}