@vymalo/opencode-lightbridge 0.18.0 → 0.19.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.
- package/README.md +5 -2
- package/dist/lib.d.ts +1 -2
- package/dist/lib.js +1 -2
- package/dist/lib.js.map +1 -1
- package/dist/opencode.d.ts +3 -1
- package/dist/opencode.js +4 -4
- package/dist/opencode.js.map +1 -1
- package/dist/plugin.d.ts +37 -41
- package/dist/plugin.js +64 -85
- package/dist/plugin.js.map +1 -1
- package/package.json +4 -4
- package/dist/migration.d.ts +0 -31
- package/dist/migration.js +0 -91
- package/dist/migration.js.map +0 -1
package/README.md
CHANGED
|
@@ -83,8 +83,9 @@ Omitting `gateway`, `otel` and `register` is a valid, inert config — the plugi
|
|
|
83
83
|
|
|
84
84
|
**One login, shared with oauth2.** Configure the same `id`/`issuer`/`clientId` as an
|
|
85
85
|
`@vymalo/opencode-oauth2` server entry and logging in through either plugin makes the token
|
|
86
|
-
available to both — no second device-code/browser flow.
|
|
87
|
-
|
|
86
|
+
available to both — no second device-code/browser flow. The token lives in the durable OS **state**
|
|
87
|
+
directory, not the cache directory ([ADR-0022](https://github.com/ADORSYS-GIS/lightbridge-opencode-toolbeit/blob/main/docs/adr/0022-credentials-in-state-dir.md)).
|
|
88
|
+
**Upgrading to that release needs one re-login** (no migration, no fallback read). See
|
|
88
89
|
[`docs/lightbridge.md`](https://github.com/ADORSYS-GIS/lightbridge-opencode-toolbeit/blob/main/docs/lightbridge.md#one-login-shared-cache-with-oauth2-adr-0017).
|
|
89
90
|
|
|
90
91
|
## Scope: gateway + OTEL, not MCP
|
|
@@ -119,6 +120,8 @@ MIT
|
|
|
119
120
|
[ADR-0019](https://github.com/ADORSYS-GIS/lightbridge-opencode-toolbeit/blob/main/docs/adr/0019-opencode-v2-cutover.md)
|
|
120
121
|
for why there is no dual-support build.
|
|
121
122
|
|
|
123
|
+
**Staying on v1:** `0.17.0` is the last v1-compatible release and stays on npm; install it with `@0.17.0` (or the `opencode-v1` dist-tag, once published). It is frozen: no fixes are backported, so this defers the upgrade, it does not avoid it. See [Staying on v1](https://github.com/ADORSYS-GIS/lightbridge-opencode-toolbeit/blob/main/docs/migration-v2.md#staying-on-v1).
|
|
124
|
+
|
|
122
125
|
| v1 | v2 |
|
|
123
126
|
| --- | --- |
|
|
124
127
|
| `"plugin": [["@vymalo/opencode-lightbridge", { ... }]]` | `"plugins": [{ "package": "@vymalo/opencode-lightbridge", "options": { ... } }]` |
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { createLightbridgePlugin, LightbridgePlugin, PLUGIN_ID, type LightbridgePluginFactoryOptions, type LightbridgeRegisterHandle } from "./opencode.js";
|
|
2
|
-
export { LightbridgeRuntime, LIGHTBRIDGE_IDENTITY,
|
|
2
|
+
export { LightbridgeRuntime, LIGHTBRIDGE_IDENTITY, EXCHANGE_STATE_NAMESPACE, DEFAULT_PROJECT_KEY, ROOT_CACHE_NAMESPACE, ROOT_CACHE_SEGMENT, lightbridgeStateDir, rootCacheDir, rootStateDir, type LightbridgeRuntimeFactory, type LightbridgeRuntimeLike, type LightbridgeRuntimeOptions } from "./plugin.js";
|
|
3
3
|
export { hasOAuth2Conflict, needsProjectToken, parseLightbridgeOptions, type LightbridgeGatewayOptions, type LightbridgeOptions, type LightbridgeRegisterOptions, type ParsedLightbridgeOptions, type ProviderLookup } from "./config.js";
|
|
4
|
-
export { migrateRootTokenIfNeeded } from "./migration.js";
|
package/dist/lib.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { createLightbridgePlugin, LightbridgePlugin, PLUGIN_ID } from "./opencode.js";
|
|
2
|
-
export { LightbridgeRuntime, LIGHTBRIDGE_IDENTITY,
|
|
2
|
+
export { LightbridgeRuntime, LIGHTBRIDGE_IDENTITY, EXCHANGE_STATE_NAMESPACE, DEFAULT_PROJECT_KEY, ROOT_CACHE_NAMESPACE, ROOT_CACHE_SEGMENT, lightbridgeStateDir, rootCacheDir, rootStateDir } from "./plugin.js";
|
|
3
3
|
export { hasOAuth2Conflict, needsProjectToken, parseLightbridgeOptions } from "./config.js";
|
|
4
|
-
export { migrateRootTokenIfNeeded } from "./migration.js";
|
|
5
4
|
|
|
6
5
|
//# sourceMappingURL=lib.js.map
|
package/dist/lib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,SACE,yBACA,mBACA,iBAGK;AAEP,SACE,oBACA,sBACA,
|
|
1
|
+
{"mappings":"AAAA,SACE,yBACA,mBACA,iBAGK;AAEP,SACE,oBACA,sBACA,0BACA,qBACA,sBACA,oBACA,qBACA,cACA,oBAIK;AAEP,SACE,mBACA,mBACA,+BAMK","names":[],"sources":["../src/lib.ts"],"version":3,"file":"lib.js","sourceRoot":""}
|
package/dist/opencode.d.ts
CHANGED
|
@@ -18,8 +18,10 @@ export interface LightbridgePluginFactoryOptions {
|
|
|
18
18
|
/** Shared-runtime HTTP client (auth-core `TokenRuntime`). Tests only. */
|
|
19
19
|
fetchImpl?: typeof fetch;
|
|
20
20
|
onAuthorizationUrl?: (url: string) => Promise<void> | void;
|
|
21
|
-
/** Override the
|
|
21
|
+
/** Override the CACHE root for the register module's disposable model list (defaults to the OS cache dir). */
|
|
22
22
|
cacheDir?: string;
|
|
23
|
+
/** Override the STATE root for credentials — tokens + refresh locks (defaults to the OS state dir, ADR-0022). */
|
|
24
|
+
stateDir?: string;
|
|
23
25
|
tokenExpirySkewMs?: number;
|
|
24
26
|
/**
|
|
25
27
|
* Build the shared `LightbridgeRuntimeLike` for `(auth, projectId)`.
|
package/dist/opencode.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { hostname } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
2
|
import { Plugin } from "@opencode/plugin/effect";
|
|
4
3
|
import { defaultTextMapSetter } from "@opentelemetry/api";
|
|
5
4
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
@@ -9,7 +8,7 @@ import { Effect, Stream } from "effect";
|
|
|
9
8
|
import { hasOAuth2Conflict, needsProjectToken, parseLightbridgeOptions } from "./config.js";
|
|
10
9
|
import { captureHostContext, createEffectLogger, DEFAULT_LOG_LEVEL, resolveLogLevel, runInHost } from "./logging.js";
|
|
11
10
|
import { adaptContext, adaptPermission, adaptPrompt, adaptToolAfter, adaptToolBefore, OtelEventAdapter } from "./otel-adapter.js";
|
|
12
|
-
import { LightbridgeRuntime, ROOT_CACHE_NAMESPACE, ROOT_CACHE_SEGMENT } from "./plugin.js";
|
|
11
|
+
import { LightbridgeRuntime, ROOT_CACHE_NAMESPACE, ROOT_CACHE_SEGMENT, rootCacheDir, rootStateDir } from "./plugin.js";
|
|
13
12
|
/** Stable plugin id. v2 requires one and keys storage/registrations by it. */
|
|
14
13
|
export const PLUGIN_ID = "vymalo.lightbridge";
|
|
15
14
|
function errorMessage(error) {
|
|
@@ -253,7 +252,7 @@ export function createLightbridgePlugin(factoryOptions = {}) {
|
|
|
253
252
|
logger,
|
|
254
253
|
fetchImpl: factoryOptions.fetchImpl,
|
|
255
254
|
onAuthorizationUrl: factoryOptions.onAuthorizationUrl,
|
|
256
|
-
|
|
255
|
+
stateDir: factoryOptions.stateDir,
|
|
257
256
|
tokenExpirySkewMs: factoryOptions.tokenExpirySkewMs,
|
|
258
257
|
exchange: parsed.gateway?.exchange ?? false
|
|
259
258
|
}) : undefined;
|
|
@@ -285,7 +284,8 @@ export function createLightbridgePlugin(factoryOptions = {}) {
|
|
|
285
284
|
logger,
|
|
286
285
|
fetchImpl: factoryOptions.fetchImpl,
|
|
287
286
|
onAuthorizationUrl: factoryOptions.onAuthorizationUrl,
|
|
288
|
-
cacheDir: factoryOptions.cacheDir ?
|
|
287
|
+
cacheDir: factoryOptions.cacheDir ? rootCacheDir(factoryOptions.cacheDir) : undefined,
|
|
288
|
+
stateDir: factoryOptions.stateDir ? rootStateDir(factoryOptions.stateDir) : undefined,
|
|
289
289
|
cacheNamespaceSegment: ROOT_CACHE_SEGMENT,
|
|
290
290
|
serviceLabel: "opencode-lightbridge"
|
|
291
291
|
});
|
package/dist/opencode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,SAAS,gBAAgB;
|
|
1
|
+
{"mappings":"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","names":[],"sources":["../src/opencode.ts"],"version":3,"file":"opencode.js","sourceRoot":""}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import { type AuthServerConfig, type Logger, type TokenSet } from "@vymalo/opencode-auth-core/lib";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* cache key has always been derived from this literal, independent of the
|
|
7
|
-
* configured IdP's `id`.
|
|
3
|
+
* Identity for the exchanged project-token store. Kept as a constant, not
|
|
4
|
+
* `auth.id`, because the exchanged-token key has always been derived from this
|
|
5
|
+
* literal, independent of the configured IdP's `id`.
|
|
8
6
|
*/
|
|
9
7
|
export declare const LIGHTBRIDGE_IDENTITY = "lightbridge";
|
|
10
|
-
/** Own cache namespace for the exchanged-token store — separate from oauth2/repo-auth/otel's. */
|
|
11
|
-
export declare const DEFAULT_CACHE_NAMESPACE = "opencode-lightbridge";
|
|
12
8
|
/**
|
|
13
|
-
*
|
|
9
|
+
* Own namespace for the exchanged-token store — separate from oauth2/repo-auth/
|
|
10
|
+
* otel's. It sits under the durable STATE root (ADR-0022): a project token is a
|
|
11
|
+
* credential, and is re-exchangeable only while the human root token survives.
|
|
12
|
+
*/
|
|
13
|
+
export declare const EXCHANGE_STATE_NAMESPACE = "opencode-lightbridge";
|
|
14
|
+
/**
|
|
15
|
+
* ADR-0017: the human ROOT token lives in the SAME credential file
|
|
14
16
|
* `@vymalo/opencode-oauth2` uses for a server of this id —
|
|
15
|
-
* `<
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* same issuer/clientId) log in ONCE
|
|
19
|
-
* on-disk
|
|
20
|
-
* `@vymalo/opencode-oauth2`, which
|
|
21
|
-
* `docs/adr/0017-lightbridge-all-in-one.md`
|
|
17
|
+
* `<stateRoot>/opencode-oauth2/opencode-oauth2-model-sync/<id>.json`
|
|
18
|
+
* (ADR-0022 moved the root from the cache dir to the state dir; the relative
|
|
19
|
+
* path is unchanged). This is what lets a developer who configures the same
|
|
20
|
+
* IdP (`auth.id` == an oauth2 `servers[].id`, same issuer/clientId) log in ONCE
|
|
21
|
+
* and have both plugins read/write the same on-disk credential. The literals
|
|
22
|
+
* are pinned here (not imported from `@vymalo/opencode-oauth2`, which
|
|
23
|
+
* lightbridge does not depend on) — see `docs/adr/0017-lightbridge-all-in-one.md`
|
|
24
|
+
* for the shared-contract rationale. The discovered-model list for that server
|
|
25
|
+
* is a separate record in the CACHE root, owned by whichever engine syncs it.
|
|
22
26
|
*/
|
|
23
27
|
export declare const ROOT_CACHE_SEGMENT = "opencode-oauth2";
|
|
24
28
|
export declare const ROOT_CACHE_NAMESPACE = "opencode-oauth2-model-sync";
|
|
@@ -33,10 +37,10 @@ export interface LightbridgeRuntimeOptions {
|
|
|
33
37
|
logger?: Logger;
|
|
34
38
|
fetchImpl?: typeof fetch;
|
|
35
39
|
onAuthorizationUrl?: (url: string) => Promise<void> | void;
|
|
36
|
-
/** Override the
|
|
37
|
-
* the shared root-token store and the exchange-only store are joined
|
|
38
|
-
* this. Test seam; production callers omit it. */
|
|
39
|
-
|
|
40
|
+
/** Override the STATE root (defaults to the OS state dir convention, ADR-0022) —
|
|
41
|
+
* both the shared root-token store and the exchange-only store are joined
|
|
42
|
+
* from this. Test seam; production callers omit it. */
|
|
43
|
+
stateDir?: string;
|
|
40
44
|
tokenExpirySkewMs?: number;
|
|
41
45
|
/**
|
|
42
46
|
* Whether to perform the RFC 8693 exchange (ADR-0012) rather than use the
|
|
@@ -68,12 +72,11 @@ export type LightbridgeRuntimeFactory = (auth: AuthServerConfig, projectId: stri
|
|
|
68
72
|
* BOTH the gateway (`chat.headers`) and OTEL (`TokenSource.headers()`).
|
|
69
73
|
*
|
|
70
74
|
* The human root token itself is no longer lightbridge-exclusive storage: it
|
|
71
|
-
* lives in the SAME
|
|
72
|
-
* of this id (`ROOT_CACHE_SEGMENT`/`ROOT_CACHE_NAMESPACE
|
|
73
|
-
* both plugins against the same
|
|
74
|
-
* `servers[].id`, same issuer/clientId) means
|
|
75
|
-
*
|
|
76
|
-
* `migration.ts`.
|
|
75
|
+
* lives in the SAME credential file `@vymalo/opencode-oauth2` writes for a
|
|
76
|
+
* server of this id (`ROOT_CACHE_SEGMENT`/`ROOT_CACHE_NAMESPACE`, under the
|
|
77
|
+
* durable state root — ADR-0022), so configuring both plugins against the same
|
|
78
|
+
* IdP (`auth.id` matching an oauth2 `servers[].id`, same issuer/clientId) means
|
|
79
|
+
* logging in once.
|
|
77
80
|
*/
|
|
78
81
|
export declare class LightbridgeRuntime implements LightbridgeRuntimeLike {
|
|
79
82
|
private readonly projectId;
|
|
@@ -83,20 +86,12 @@ export declare class LightbridgeRuntime implements LightbridgeRuntimeLike {
|
|
|
83
86
|
private readonly logger;
|
|
84
87
|
private readonly tokenExpirySkewMs;
|
|
85
88
|
private readonly exchange;
|
|
86
|
-
private readonly
|
|
89
|
+
private readonly stateRoot;
|
|
87
90
|
private readonly serverId;
|
|
88
91
|
/** Token-cache key: the project id, or `DEFAULT_PROJECT_KEY` when unset. */
|
|
89
92
|
private readonly projectKey;
|
|
90
93
|
private inFlightExchange?;
|
|
91
|
-
private migration?;
|
|
92
94
|
constructor(auth: AuthServerConfig, projectId: string | undefined, options?: LightbridgeRuntimeOptions);
|
|
93
|
-
/**
|
|
94
|
-
* One-time (per instance) migration of a pre-ADR-0017 root token into the
|
|
95
|
-
* shared cache file — see `migration.ts`. Idempotent and cheap on every
|
|
96
|
-
* call after the first (the underlying function also short-circuits once
|
|
97
|
-
* the new location has ANY state).
|
|
98
|
-
*/
|
|
99
|
-
private ensureMigrated;
|
|
100
95
|
/** Non-network read of the cached project token, if any. */
|
|
101
96
|
getCachedProjectToken(): Promise<TokenSet | undefined>;
|
|
102
97
|
/**
|
|
@@ -128,14 +123,15 @@ export declare class LightbridgeRuntime implements LightbridgeRuntimeLike {
|
|
|
128
123
|
private performExchange;
|
|
129
124
|
/**
|
|
130
125
|
* Drop the cached tokens for this identity: the exchanged project token
|
|
131
|
-
* entirely (its own exclusive store), and the shared root
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* survive a lightbridge-side logout.
|
|
126
|
+
* entirely (its own exclusive store), and the shared root credential. The
|
|
127
|
+
* discovered-model record is a separate file in the cache root and is not
|
|
128
|
+
* touched, so oauth2's models survive a lightbridge-side logout.
|
|
135
129
|
*/
|
|
136
130
|
reset(): Promise<void>;
|
|
137
131
|
}
|
|
138
|
-
/** Absolute path to the lightbridge-exclusive (exchanged-token)
|
|
139
|
-
export declare function
|
|
140
|
-
/** Absolute path to the SHARED root-
|
|
132
|
+
/** Absolute path to the lightbridge-exclusive (exchanged-token) STATE directory. */
|
|
133
|
+
export declare function lightbridgeStateDir(stateRoot: string): string;
|
|
134
|
+
/** Absolute path to the SHARED root-credential STATE directory (ADR-0017, ADR-0022). */
|
|
135
|
+
export declare function rootStateDir(stateRoot: string): string;
|
|
136
|
+
/** Absolute path to the SHARED discovered-model CACHE directory (disposable; ADR-0022). */
|
|
141
137
|
export declare function rootCacheDir(cacheRoot: string): string;
|
package/dist/plugin.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { createJsonConsoleLogger, DEFAULT_TOKEN_EXPIRY_SKEW_MS,
|
|
3
|
-
import {
|
|
4
|
-
import { migrateRootTokenIfNeeded } from "./migration.js";
|
|
2
|
+
import { createJsonConsoleLogger, DEFAULT_TOKEN_EXPIRY_SKEW_MS, resolveStateRoot, TokenRuntime } from "@vymalo/opencode-auth-core/lib";
|
|
3
|
+
import { ServerCredentialStore } from "@vymalo/opencode-provider-sync/lib";
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* cache key has always been derived from this literal, independent of the
|
|
10
|
-
* configured IdP's `id`.
|
|
5
|
+
* Identity for the exchanged project-token store. Kept as a constant, not
|
|
6
|
+
* `auth.id`, because the exchanged-token key has always been derived from this
|
|
7
|
+
* literal, independent of the configured IdP's `id`.
|
|
11
8
|
*/
|
|
12
9
|
export const LIGHTBRIDGE_IDENTITY = "lightbridge";
|
|
13
|
-
/** Own cache namespace for the exchanged-token store — separate from oauth2/repo-auth/otel's. */
|
|
14
|
-
export const DEFAULT_CACHE_NAMESPACE = "opencode-lightbridge";
|
|
15
10
|
/**
|
|
16
|
-
*
|
|
11
|
+
* Own namespace for the exchanged-token store — separate from oauth2/repo-auth/
|
|
12
|
+
* otel's. It sits under the durable STATE root (ADR-0022): a project token is a
|
|
13
|
+
* credential, and is re-exchangeable only while the human root token survives.
|
|
14
|
+
*/
|
|
15
|
+
export const EXCHANGE_STATE_NAMESPACE = "opencode-lightbridge";
|
|
16
|
+
/**
|
|
17
|
+
* ADR-0017: the human ROOT token lives in the SAME credential file
|
|
17
18
|
* `@vymalo/opencode-oauth2` uses for a server of this id —
|
|
18
|
-
* `<
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* same issuer/clientId) log in ONCE
|
|
22
|
-
* on-disk
|
|
23
|
-
* `@vymalo/opencode-oauth2`, which
|
|
24
|
-
* `docs/adr/0017-lightbridge-all-in-one.md`
|
|
19
|
+
* `<stateRoot>/opencode-oauth2/opencode-oauth2-model-sync/<id>.json`
|
|
20
|
+
* (ADR-0022 moved the root from the cache dir to the state dir; the relative
|
|
21
|
+
* path is unchanged). This is what lets a developer who configures the same
|
|
22
|
+
* IdP (`auth.id` == an oauth2 `servers[].id`, same issuer/clientId) log in ONCE
|
|
23
|
+
* and have both plugins read/write the same on-disk credential. The literals
|
|
24
|
+
* are pinned here (not imported from `@vymalo/opencode-oauth2`, which
|
|
25
|
+
* lightbridge does not depend on) — see `docs/adr/0017-lightbridge-all-in-one.md`
|
|
26
|
+
* for the shared-contract rationale. The discovered-model list for that server
|
|
27
|
+
* is a separate record in the CACHE root, owned by whichever engine syncs it.
|
|
25
28
|
*/
|
|
26
29
|
export const ROOT_CACHE_SEGMENT = "opencode-oauth2";
|
|
27
30
|
export const ROOT_CACHE_NAMESPACE = "opencode-oauth2-model-sync";
|
|
@@ -48,14 +51,19 @@ function isProjectTokenUsable(token, skewMs) {
|
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
50
53
|
* Build the ONE shared root `TokenRuntime` for `serverId`, persisting through
|
|
51
|
-
* the SAME
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* `ProviderModelSyncEngine.buildTokenRuntime
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
54
|
+
* the SAME credential record `ProviderModelSyncEngine` uses (see
|
|
55
|
+
* `ROOT_CACHE_SEGMENT`/`ROOT_CACHE_NAMESPACE` above) — mirroring the override
|
|
56
|
+
* pattern in `@vymalo/opencode-provider-sync`'s
|
|
57
|
+
* `ProviderModelSyncEngine.buildTokenRuntime`, but WITHOUT running that
|
|
58
|
+
* engine's scheduler/model-discovery — this is a read/refresh-only participant
|
|
59
|
+
* in the shared credential file, not a second poller (see ADR-0017's
|
|
60
|
+
* double-scheduling guard, enforced inside the engine itself for the `register`
|
|
61
|
+
* path). It never touches the model-cache record, so a lightbridge-side write
|
|
62
|
+
* can no longer clobber oauth2's discovered models.
|
|
63
|
+
*
|
|
64
|
+
* `stateDir` is the credential store's directory, so the cross-process refresh
|
|
65
|
+
* lock (`<stateDir>/locks/<id>.lock`) sits beside the token it guards and is
|
|
66
|
+
* the very lock oauth2's engine takes for the same id.
|
|
59
67
|
*
|
|
60
68
|
* Unlike `ProviderModelSyncEngine`, this performs no cross-process
|
|
61
69
|
* "adopt-if-newer" reconciliation — every `getCached()` call here reads disk
|
|
@@ -65,28 +73,22 @@ function isProjectTokenUsable(token, skewMs) {
|
|
|
65
73
|
* fallback), and refreshes are still coordinated via `TokenRuntime`'s own
|
|
66
74
|
* cross-process file lock.
|
|
67
75
|
*/
|
|
68
|
-
function buildSharedRootTokenRuntime(serverId, auth,
|
|
76
|
+
function buildSharedRootTokenRuntime(serverId, auth, credentials, options) {
|
|
69
77
|
return new TokenRuntime(serverId, auth, {
|
|
70
78
|
logger: options.logger,
|
|
71
79
|
fetchImpl: options.fetchImpl,
|
|
72
80
|
onAuthorizationUrl: options.onAuthorizationUrl,
|
|
73
81
|
tokenExpirySkewMs: options.tokenExpirySkewMs,
|
|
74
82
|
serviceLabel: "opencode-lightbridge",
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return state?.token;
|
|
78
|
-
},
|
|
83
|
+
stateDir: credentials.directory,
|
|
84
|
+
getCached: async () => (await credentials.load(serverId))?.token,
|
|
79
85
|
setCached: async (token) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
rawModels: existing?.rawModels ?? [],
|
|
87
|
-
token
|
|
88
|
-
};
|
|
89
|
-
await sharedStore.saveServerState(next);
|
|
86
|
+
await credentials.save(serverId, token, Date.now());
|
|
87
|
+
},
|
|
88
|
+
// Compare-and-clear of a refresh token the IdP rejected is done by
|
|
89
|
+
// TokenRuntime under the lock; this only performs the removal.
|
|
90
|
+
clearCached: async () => {
|
|
91
|
+
await credentials.remove(serverId);
|
|
90
92
|
}
|
|
91
93
|
});
|
|
92
94
|
}
|
|
@@ -98,12 +100,11 @@ function buildSharedRootTokenRuntime(serverId, auth, sharedStore, options) {
|
|
|
98
100
|
* BOTH the gateway (`chat.headers`) and OTEL (`TokenSource.headers()`).
|
|
99
101
|
*
|
|
100
102
|
* The human root token itself is no longer lightbridge-exclusive storage: it
|
|
101
|
-
* lives in the SAME
|
|
102
|
-
* of this id (`ROOT_CACHE_SEGMENT`/`ROOT_CACHE_NAMESPACE
|
|
103
|
-
* both plugins against the same
|
|
104
|
-
* `servers[].id`, same issuer/clientId) means
|
|
105
|
-
*
|
|
106
|
-
* `migration.ts`.
|
|
103
|
+
* lives in the SAME credential file `@vymalo/opencode-oauth2` writes for a
|
|
104
|
+
* server of this id (`ROOT_CACHE_SEGMENT`/`ROOT_CACHE_NAMESPACE`, under the
|
|
105
|
+
* durable state root — ADR-0022), so configuring both plugins against the same
|
|
106
|
+
* IdP (`auth.id` matching an oauth2 `servers[].id`, same issuer/clientId) means
|
|
107
|
+
* logging in once.
|
|
107
108
|
*/
|
|
108
109
|
export class LightbridgeRuntime {
|
|
109
110
|
projectId;
|
|
@@ -113,21 +114,20 @@ export class LightbridgeRuntime {
|
|
|
113
114
|
logger;
|
|
114
115
|
tokenExpirySkewMs;
|
|
115
116
|
exchange;
|
|
116
|
-
|
|
117
|
+
stateRoot;
|
|
117
118
|
serverId;
|
|
118
119
|
/** Token-cache key: the project id, or `DEFAULT_PROJECT_KEY` when unset. */
|
|
119
120
|
projectKey;
|
|
120
121
|
inFlightExchange;
|
|
121
|
-
migration;
|
|
122
122
|
constructor(auth, projectId, options = {}) {
|
|
123
123
|
this.projectId = projectId;
|
|
124
124
|
this.serverId = auth.id;
|
|
125
125
|
this.projectKey = projectId ?? DEFAULT_PROJECT_KEY;
|
|
126
126
|
this.logger = options.logger ?? createJsonConsoleLogger("info");
|
|
127
127
|
this.exchange = options.exchange ?? false;
|
|
128
|
-
this.
|
|
128
|
+
this.stateRoot = options.stateDir ?? resolveStateRoot();
|
|
129
129
|
this.tokenExpirySkewMs = typeof options.tokenExpirySkewMs === "number" && Number.isFinite(options.tokenExpirySkewMs) && options.tokenExpirySkewMs > 0 ? options.tokenExpirySkewMs : DEFAULT_TOKEN_EXPIRY_SKEW_MS;
|
|
130
|
-
this.sharedStore = new
|
|
130
|
+
this.sharedStore = new ServerCredentialStore(rootStateDir(this.stateRoot), this.logger);
|
|
131
131
|
this.rootRuntime = buildSharedRootTokenRuntime(this.serverId, auth, this.sharedStore, options);
|
|
132
132
|
// The exchange-only runtime keeps its OWN dedicated, lightbridge-exclusive
|
|
133
133
|
// store (unchanged location/shape) — there is no oauth2 equivalent for a
|
|
@@ -136,27 +136,11 @@ export class LightbridgeRuntime {
|
|
|
136
136
|
logger: this.logger,
|
|
137
137
|
fetchImpl: options.fetchImpl,
|
|
138
138
|
onAuthorizationUrl: options.onAuthorizationUrl,
|
|
139
|
-
|
|
139
|
+
stateDir: lightbridgeStateDir(this.stateRoot),
|
|
140
140
|
tokenExpirySkewMs: this.tokenExpirySkewMs,
|
|
141
141
|
serviceLabel: "opencode-lightbridge"
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
|
-
/**
|
|
145
|
-
* One-time (per instance) migration of a pre-ADR-0017 root token into the
|
|
146
|
-
* shared cache file — see `migration.ts`. Idempotent and cheap on every
|
|
147
|
-
* call after the first (the underlying function also short-circuits once
|
|
148
|
-
* the new location has ANY state).
|
|
149
|
-
*/
|
|
150
|
-
ensureMigrated() {
|
|
151
|
-
if (!this.migration) {
|
|
152
|
-
this.migration = migrateRootTokenIfNeeded(this.cacheRoot, ROOT_CACHE_SEGMENT, ROOT_CACHE_NAMESPACE, this.serverId, this.logger).catch((error) => {
|
|
153
|
-
// Never fatal: worst case is a fresh login, same as a first-ever
|
|
154
|
-
// install. Swallow so a migration hiccup can't break normal auth.
|
|
155
|
-
this.logger.debug("lightbridge_root_migration_error", { error: error instanceof Error ? error.message : String(error) });
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
return this.migration;
|
|
159
|
-
}
|
|
160
144
|
/** Non-network read of the cached project token, if any. */
|
|
161
145
|
async getCachedProjectToken() {
|
|
162
146
|
return this.exchangeRuntime.getExchangedByKey(this.projectKey);
|
|
@@ -185,7 +169,6 @@ export class LightbridgeRuntime {
|
|
|
185
169
|
* fail closed (no header / empty `headers()`).
|
|
186
170
|
*/
|
|
187
171
|
async getProjectToken(options = {}) {
|
|
188
|
-
await this.ensureMigrated();
|
|
189
172
|
if (!this.exchange) {
|
|
190
173
|
return this.rootRuntime.ensure({ interactive: options.interactive });
|
|
191
174
|
}
|
|
@@ -227,28 +210,24 @@ export class LightbridgeRuntime {
|
|
|
227
210
|
}
|
|
228
211
|
/**
|
|
229
212
|
* Drop the cached tokens for this identity: the exchanged project token
|
|
230
|
-
* entirely (its own exclusive store), and the shared root
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* survive a lightbridge-side logout.
|
|
213
|
+
* entirely (its own exclusive store), and the shared root credential. The
|
|
214
|
+
* discovered-model record is a separate file in the cache root and is not
|
|
215
|
+
* touched, so oauth2's models survive a lightbridge-side logout.
|
|
234
216
|
*/
|
|
235
217
|
async reset() {
|
|
236
218
|
await this.exchangeRuntime.reset();
|
|
237
|
-
|
|
238
|
-
if (existing?.token) {
|
|
239
|
-
await this.sharedStore.saveServerState({
|
|
240
|
-
...existing,
|
|
241
|
-
updatedAt: Date.now(),
|
|
242
|
-
token: undefined
|
|
243
|
-
});
|
|
244
|
-
}
|
|
219
|
+
await this.sharedStore.remove(this.serverId);
|
|
245
220
|
}
|
|
246
221
|
}
|
|
247
|
-
/** Absolute path to the lightbridge-exclusive (exchanged-token)
|
|
248
|
-
export function
|
|
249
|
-
return join(
|
|
222
|
+
/** Absolute path to the lightbridge-exclusive (exchanged-token) STATE directory. */
|
|
223
|
+
export function lightbridgeStateDir(stateRoot) {
|
|
224
|
+
return join(stateRoot, EXCHANGE_STATE_NAMESPACE);
|
|
225
|
+
}
|
|
226
|
+
/** Absolute path to the SHARED root-credential STATE directory (ADR-0017, ADR-0022). */
|
|
227
|
+
export function rootStateDir(stateRoot) {
|
|
228
|
+
return join(stateRoot, ROOT_CACHE_SEGMENT, ROOT_CACHE_NAMESPACE);
|
|
250
229
|
}
|
|
251
|
-
/** Absolute path to the SHARED
|
|
230
|
+
/** Absolute path to the SHARED discovered-model CACHE directory (disposable; ADR-0022). */
|
|
252
231
|
export function rootCacheDir(cacheRoot) {
|
|
253
232
|
return join(cacheRoot, ROOT_CACHE_SEGMENT, ROOT_CACHE_NAMESPACE);
|
|
254
233
|
}
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,SAAS,YAAY;AAErB,SACE,yBACA,8BACA,kBACA,oBAIK;AACP,
|
|
1
|
+
{"mappings":"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","names":[],"sources":["../src/plugin.ts"],"version":3,"file":"plugin.js","sourceRoot":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vymalo/opencode-lightbridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Umbrella OpenCode plugin for ADR-0012: one shared credential (TokenRuntime) drives both the LLM gateway bearer and OTEL export, so a developer authenticates once and every egress rides the same project-scoped token.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vymalo contributors",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"@opentelemetry/api": "^1.9.1",
|
|
55
55
|
"@opentelemetry/api-logs": "^0.222.0",
|
|
56
56
|
"@opentelemetry/core": "^2.11.0",
|
|
57
|
-
"@vymalo/opencode-auth-core": "0.
|
|
58
|
-
"@vymalo/opencode-core-otel": "0.
|
|
59
|
-
"@vymalo/opencode-provider-sync": "0.
|
|
57
|
+
"@vymalo/opencode-auth-core": "0.19.0",
|
|
58
|
+
"@vymalo/opencode-core-otel": "0.19.0",
|
|
59
|
+
"@vymalo/opencode-provider-sync": "0.19.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@opentelemetry/sdk-logs": "^0.222.0",
|
package/dist/migration.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type Logger } from "@vymalo/opencode-auth-core/lib";
|
|
2
|
-
/**
|
|
3
|
-
* One-time migration read-path (ADR-0017, mandatory — not optional polish).
|
|
4
|
-
*
|
|
5
|
-
* v0.17 moves lightbridge's human root token from its own bespoke file
|
|
6
|
-
* (`<cacheRoot>/opencode-lightbridge/lightbridge.json`, a bare `TokenSet`) to
|
|
7
|
-
* the SAME fused `CachedServerState` file `@vymalo/opencode-oauth2` uses
|
|
8
|
-
* (`<cacheRoot>/<sharedSegment>/<sharedNamespace>/<serverId>.json`) so the two
|
|
9
|
-
* plugins can share one login (ADR-0017 §"one login, shared cache"). An
|
|
10
|
-
* install that already has a valid old-format token must NOT be forced
|
|
11
|
-
* through a fresh device-code/browser login just because the file moved.
|
|
12
|
-
*
|
|
13
|
-
* Behaviour:
|
|
14
|
-
* - If the NEW location already has a persisted state (with or without a
|
|
15
|
-
* token), this is a no-op — never clobbers a fresher state (e.g. one
|
|
16
|
-
* oauth2 has already been writing to, with real discovered models).
|
|
17
|
-
* - Else, read the OLD location. If it holds a structurally usable token
|
|
18
|
-
* (`accessToken` + `tokenType` present — possibly expired; an expired
|
|
19
|
-
* token with a refresh token is still worth adopting, since `TokenRuntime`
|
|
20
|
-
* will refresh it transparently), write it into the NEW location as a
|
|
21
|
-
* fresh `CachedServerState` (empty `models`/`rawModels` — nothing to carry
|
|
22
|
-
* over, since the old shape never tracked models).
|
|
23
|
-
* - Else (no old file, or it's empty/malformed) this is a no-op; the caller
|
|
24
|
-
* proceeds to a normal login.
|
|
25
|
-
*
|
|
26
|
-
* The old file is left in place (non-destructive) — this function only ever
|
|
27
|
-
* ADDS the new file, never deletes/mutates the old one. Safe to call more
|
|
28
|
-
* than once: the "new location already has a state" check makes every call
|
|
29
|
-
* after the first a cheap no-op.
|
|
30
|
-
*/
|
|
31
|
-
export declare function migrateRootTokenIfNeeded(cacheRoot: string, sharedSegment: string, sharedNamespace: string, serverId: string, logger: Logger): Promise<void>;
|
package/dist/migration.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { FileCacheStore as LegacyFileCacheStore } from "@vymalo/opencode-auth-core/lib";
|
|
3
|
-
import { FileCacheStore as SharedFileCacheStore } from "@vymalo/opencode-provider-sync/lib";
|
|
4
|
-
/**
|
|
5
|
-
* The pre-ADR-0017 on-disk location of lightbridge's human root token:
|
|
6
|
-
* `<cacheRoot>/opencode-lightbridge/lightbridge.json`, a bare `TokenSet`
|
|
7
|
-
* (see the OLD `LIGHTBRIDGE_IDENTITY`/`DEFAULT_CACHE_NAMESPACE` in `plugin.ts`,
|
|
8
|
-
* which are still used verbatim for the exchanged project-token store — only
|
|
9
|
-
* the ROOT token relocated).
|
|
10
|
-
*/
|
|
11
|
-
const OLD_ROOT_CACHE_NAMESPACE = "opencode-lightbridge";
|
|
12
|
-
const OLD_ROOT_KEY = "lightbridge";
|
|
13
|
-
function hasUsableTokenShape(value) {
|
|
14
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
const candidate = value;
|
|
18
|
-
return typeof candidate.accessToken === "string" && candidate.accessToken.length > 0 && typeof candidate.tokenType === "string" && candidate.tokenType.length > 0;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* One-time migration read-path (ADR-0017, mandatory — not optional polish).
|
|
22
|
-
*
|
|
23
|
-
* v0.17 moves lightbridge's human root token from its own bespoke file
|
|
24
|
-
* (`<cacheRoot>/opencode-lightbridge/lightbridge.json`, a bare `TokenSet`) to
|
|
25
|
-
* the SAME fused `CachedServerState` file `@vymalo/opencode-oauth2` uses
|
|
26
|
-
* (`<cacheRoot>/<sharedSegment>/<sharedNamespace>/<serverId>.json`) so the two
|
|
27
|
-
* plugins can share one login (ADR-0017 §"one login, shared cache"). An
|
|
28
|
-
* install that already has a valid old-format token must NOT be forced
|
|
29
|
-
* through a fresh device-code/browser login just because the file moved.
|
|
30
|
-
*
|
|
31
|
-
* Behaviour:
|
|
32
|
-
* - If the NEW location already has a persisted state (with or without a
|
|
33
|
-
* token), this is a no-op — never clobbers a fresher state (e.g. one
|
|
34
|
-
* oauth2 has already been writing to, with real discovered models).
|
|
35
|
-
* - Else, read the OLD location. If it holds a structurally usable token
|
|
36
|
-
* (`accessToken` + `tokenType` present — possibly expired; an expired
|
|
37
|
-
* token with a refresh token is still worth adopting, since `TokenRuntime`
|
|
38
|
-
* will refresh it transparently), write it into the NEW location as a
|
|
39
|
-
* fresh `CachedServerState` (empty `models`/`rawModels` — nothing to carry
|
|
40
|
-
* over, since the old shape never tracked models).
|
|
41
|
-
* - Else (no old file, or it's empty/malformed) this is a no-op; the caller
|
|
42
|
-
* proceeds to a normal login.
|
|
43
|
-
*
|
|
44
|
-
* The old file is left in place (non-destructive) — this function only ever
|
|
45
|
-
* ADDS the new file, never deletes/mutates the old one. Safe to call more
|
|
46
|
-
* than once: the "new location already has a state" check makes every call
|
|
47
|
-
* after the first a cheap no-op.
|
|
48
|
-
*/
|
|
49
|
-
export async function migrateRootTokenIfNeeded(cacheRoot, sharedSegment, sharedNamespace, serverId, logger) {
|
|
50
|
-
const sharedStore = new SharedFileCacheStore(join(cacheRoot, sharedSegment, sharedNamespace), logger);
|
|
51
|
-
let alreadyMigrated;
|
|
52
|
-
try {
|
|
53
|
-
alreadyMigrated = await sharedStore.loadServerState(serverId);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
logger.debug("lightbridge_root_migration_check_failed", { error: error instanceof Error ? error.message : String(error) });
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (alreadyMigrated) {
|
|
59
|
-
// New location already has SOME state (with or without a token) — either
|
|
60
|
-
// a previous migration ran, or oauth2/register already populated it.
|
|
61
|
-
// Never overwrite it from the old file.
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const legacyStore = new LegacyFileCacheStore(join(cacheRoot, OLD_ROOT_CACHE_NAMESPACE), logger);
|
|
65
|
-
let legacyToken;
|
|
66
|
-
try {
|
|
67
|
-
legacyToken = await legacyStore.load(OLD_ROOT_KEY);
|
|
68
|
-
} catch (error) {
|
|
69
|
-
logger.debug("lightbridge_root_migration_legacy_read_failed", { error: error instanceof Error ? error.message : String(error) });
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (!hasUsableTokenShape(legacyToken)) {
|
|
73
|
-
logger.trace("lightbridge_root_migration_nothing_to_adopt", { serverId });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const migrated = {
|
|
77
|
-
serverId,
|
|
78
|
-
updatedAt: Date.now(),
|
|
79
|
-
models: [],
|
|
80
|
-
rawModels: [],
|
|
81
|
-
token: legacyToken
|
|
82
|
-
};
|
|
83
|
-
await sharedStore.ensureReady();
|
|
84
|
-
await sharedStore.saveServerState(migrated);
|
|
85
|
-
logger.info("lightbridge_root_migration_adopted", {
|
|
86
|
-
serverId,
|
|
87
|
-
hadRefreshToken: Boolean(legacyToken.refreshToken)
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
//# sourceMappingURL=migration.js.map
|
package/dist/migration.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":"AAAA,SAAS,YAAY;AAErB,SACE,kBAAkB,4BAGb;AACP,SACE,kBAAkB,4BAEb;;;;;;;;AASP,MAAM,2BAA2B;AACjC,MAAM,eAAe;AAErB,SAAS,oBAAoB,OAAmC;CAC9D,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EAC/D,OAAO;CACT;CACA,MAAM,YAAY;CAClB,OACE,OAAO,UAAU,gBAAgB,YACjC,UAAU,YAAY,SAAS,KAC/B,OAAO,UAAU,cAAc,YAC/B,UAAU,UAAU,SAAS;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,OAAO,eAAe,yBACpB,WACA,eACA,iBACA,UACA,QACe;CACf,MAAM,cAAc,IAAI,qBACtB,KAAK,WAAW,eAAe,eAAe,GAC9C,MACF;CAEA,IAAI;CACJ,IAAI;EACF,kBAAkB,MAAM,YAAY,gBAAgB,QAAQ;CAC9D,SAAS,OAAO;EACd,OAAO,MAAM,2CAA2C,EACtD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAC9D,CAAC;EACD;CACF;CACA,IAAI,iBAAiB;;;;EAInB;CACF;CAEA,MAAM,cAAc,IAAI,qBAAqB,KAAK,WAAW,wBAAwB,GAAG,MAAM;CAC9F,IAAI;CACJ,IAAI;EACF,cAAc,MAAM,YAAY,KAAe,YAAY;CAC7D,SAAS,OAAO;EACd,OAAO,MAAM,iDAAiD,EAC5D,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAC9D,CAAC;EACD;CACF;CAEA,IAAI,CAAC,oBAAoB,WAAW,GAAG;EACrC,OAAO,MAAM,+CAA+C,EAAE,SAAS,CAAC;EACxE;CACF;CAEA,MAAM,WAA8B;EAClC;EACA,WAAW,KAAK,IAAI;EACpB,QAAQ,CAAC;EACT,WAAW,CAAC;EACZ,OAAO;CACT;CAEA,MAAM,YAAY,YAAY;CAC9B,MAAM,YAAY,gBAAgB,QAAQ;CAC1C,OAAO,KAAK,sCAAsC;EAChD;EACA,iBAAiB,QAAQ,YAAY,YAAY;CACnD,CAAC;AACH","names":[],"sources":["../src/migration.ts"],"version":3,"file":"migration.js","sourceRoot":""}
|