@bodhiapp/bodhi-js-ext 0.0.15 → 0.0.17
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/dist/bodhi-ext.cjs.js +1 -1
- package/dist/bodhi-ext.esm.js +465 -421
- package/dist/bodhi-js-sdk/ext/src/constants.d.ts +6 -0
- package/dist/bodhi-js-sdk/ext/src/direct-client.d.ts +3 -2
- package/dist/bodhi-js-sdk/ext/src/ext-client.d.ts +5 -2
- package/dist/bodhi-js-sdk/ext/src/ext2ext-client.d.ts +3 -2
- package/dist/bodhi-js-sdk/ext/src/facade-client.d.ts +2 -0
- package/package.json +3 -3
package/dist/bodhi-ext.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DirectClientBase as
|
|
2
|
-
const
|
|
1
|
+
import { DirectClientBase as K, createStoragePrefixWithBasePath as L, STORAGE_PREFIXES as U, isApiResultOperationError as v, createOperationError as d, isApiResultError as M, isApiResultSuccess as O, getMissingToolsetScopeIds as D, getRequestedToolsetScopes as q, generateCodeVerifier as b, generateCodeChallenge as V, PENDING_EXTENSION_READY as w, Logger as P, NOOP_STATE_CALLBACK as F, createExtensionStateNotInitialized as H, BACKEND_SERVER_NOT_REACHABLE as z, createExtensionStateNotFound as Q, INITIAL_AUTH_STATE as G, createApiError as W, Chat as J, Models as Y, Embeddings as j, isAuthError as Z, BaseFacadeClient as ee, refreshAccessToken as te } from "@bodhiapp/bodhi-js-core";
|
|
2
|
+
const c = {
|
|
3
3
|
EXT2EXT_CLIENT_REQUEST: "EXT2EXT_CLIENT_REQUEST",
|
|
4
4
|
EXT2EXT_CLIENT_RESPONSE: "EXT2EXT_CLIENT_RESPONSE",
|
|
5
5
|
EXT2EXT_CLIENT_BROADCAST: "EXT2EXT_CLIENT_BROADCAST",
|
|
@@ -11,106 +11,113 @@ const h = {
|
|
|
11
11
|
EXT2EXT_CLIENT_STREAM_ERROR: "EXT2EXT_CLIENT_STREAM_ERROR",
|
|
12
12
|
EXT2EXT_CLIENT_STREAM_API_ERROR: "EXT2EXT_CLIENT_STREAM_API_ERROR",
|
|
13
13
|
EXT2EXT_CLIENT_STREAM_DONE: "EXT2EXT_CLIENT_STREAM_DONE"
|
|
14
|
-
},
|
|
14
|
+
}, $ = "ext2ext-client-stream", m = {
|
|
15
15
|
LOGIN: "login",
|
|
16
16
|
LOGOUT: "logout",
|
|
17
17
|
GET_AUTH_STATE: "getAuthState",
|
|
18
18
|
DISCOVER_EXTENSION: "discoverBodhiExtension",
|
|
19
19
|
GET_EXTENSION_ID: "get_extension_id",
|
|
20
20
|
SET_EXTENSION_ID: "setExtensionId"
|
|
21
|
-
},
|
|
22
|
-
function
|
|
23
|
-
return "error" in
|
|
21
|
+
}, re = 5e3, se = 3, oe = 500, ie = 500, ne = 3e4;
|
|
22
|
+
function ae(a) {
|
|
23
|
+
return "error" in a;
|
|
24
24
|
}
|
|
25
|
-
class
|
|
25
|
+
class ce extends K {
|
|
26
26
|
constructor(e, t) {
|
|
27
|
-
const r =
|
|
27
|
+
const r = L(
|
|
28
28
|
e.basePath,
|
|
29
|
-
|
|
30
|
-
),
|
|
29
|
+
U.EXT_DIRECT
|
|
30
|
+
), i = {
|
|
31
31
|
authClientId: e.authClientId,
|
|
32
32
|
authServerUrl: e.authServerUrl,
|
|
33
33
|
userScope: e.userScope,
|
|
34
34
|
storagePrefix: r,
|
|
35
35
|
logLevel: e.logLevel,
|
|
36
|
-
loggerPrefix: "DirectExtClient"
|
|
36
|
+
loggerPrefix: "DirectExtClient",
|
|
37
|
+
apiTimeoutMs: e.apiTimeoutMs
|
|
37
38
|
};
|
|
38
|
-
super(
|
|
39
|
+
super(i, t);
|
|
39
40
|
}
|
|
40
41
|
// ============================================================================
|
|
41
42
|
// Authentication (chrome.identity OAuth)
|
|
42
43
|
// ============================================================================
|
|
43
|
-
async login() {
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
return
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
throw
|
|
50
|
-
if (
|
|
51
|
-
const { message:
|
|
52
|
-
throw
|
|
44
|
+
async login(e) {
|
|
45
|
+
const t = await this.getAuthState();
|
|
46
|
+
if (t.status === "authenticated")
|
|
47
|
+
return t;
|
|
48
|
+
const r = await this.requestResourceAccess(e?.toolsetScopeIds, e?.version);
|
|
49
|
+
if (v(r))
|
|
50
|
+
throw d(r.error.message, r.error.type);
|
|
51
|
+
if (M(r)) {
|
|
52
|
+
const { message: T } = r.body.error;
|
|
53
|
+
throw d(T, "auth_error");
|
|
53
54
|
}
|
|
54
|
-
if (!
|
|
55
|
-
throw
|
|
56
|
-
const
|
|
57
|
-
await chrome.storage.session.set({ [this.storageKeys.RESOURCE_SCOPE]:
|
|
58
|
-
const
|
|
55
|
+
if (!O(r))
|
|
56
|
+
throw d(`Unexpected HTTP ${r.status}`, "auth_error");
|
|
57
|
+
const i = r.body.scope;
|
|
58
|
+
await chrome.storage.session.set({ [this.storageKeys.RESOURCE_SCOPE]: i });
|
|
59
|
+
const s = r.body.toolsets || [], o = D(e?.toolsetScopeIds, s);
|
|
60
|
+
if (o.length > 0)
|
|
61
|
+
throw d(
|
|
62
|
+
`toolsetScopeIds not received back from request-access call: [${o.join(", ")}], check developer console on configuring the toolset scopes correctly`,
|
|
63
|
+
"auth_error"
|
|
64
|
+
);
|
|
65
|
+
const n = q(e?.toolsetScopeIds, s), E = `openid profile email roles ${this.userScope} ${i} ${n}`.trim(), h = b(), l = await V(h), u = b();
|
|
59
66
|
await chrome.storage.session.set({
|
|
60
|
-
[this.storageKeys.CODE_VERIFIER]:
|
|
61
|
-
[this.storageKeys.STATE]:
|
|
67
|
+
[this.storageKeys.CODE_VERIFIER]: h,
|
|
68
|
+
[this.storageKeys.STATE]: u
|
|
62
69
|
});
|
|
63
|
-
const
|
|
64
|
-
return
|
|
70
|
+
const p = chrome.identity.getRedirectURL("callback"), g = new URL(this.authEndpoints.authorize);
|
|
71
|
+
return g.searchParams.set("client_id", this.authClientId), g.searchParams.set("response_type", "code"), g.searchParams.set("redirect_uri", p), g.searchParams.set("scope", E), g.searchParams.set("code_challenge", l), g.searchParams.set("code_challenge_method", "S256"), g.searchParams.set("state", u), new Promise((T, _) => {
|
|
65
72
|
chrome.identity.launchWebAuthFlow(
|
|
66
73
|
{
|
|
67
|
-
url:
|
|
74
|
+
url: g.toString(),
|
|
68
75
|
interactive: !0
|
|
69
76
|
},
|
|
70
|
-
async (
|
|
77
|
+
async (R) => {
|
|
71
78
|
if (chrome.runtime.lastError) {
|
|
72
79
|
await chrome.storage.session.remove([
|
|
73
80
|
this.storageKeys.CODE_VERIFIER,
|
|
74
81
|
this.storageKeys.STATE
|
|
75
|
-
]),
|
|
82
|
+
]), _(chrome.runtime.lastError);
|
|
76
83
|
return;
|
|
77
84
|
}
|
|
78
|
-
if (!
|
|
85
|
+
if (!R) {
|
|
79
86
|
await chrome.storage.session.remove([
|
|
80
87
|
this.storageKeys.CODE_VERIFIER,
|
|
81
88
|
this.storageKeys.STATE
|
|
82
|
-
]),
|
|
89
|
+
]), _(d("No redirect URL received", "oauth-error"));
|
|
83
90
|
return;
|
|
84
91
|
}
|
|
85
92
|
try {
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
93
|
+
const I = new URL(R), f = I.searchParams.get("code"), x = I.searchParams.get("state"), B = (await chrome.storage.session.get(this.storageKeys.STATE))[this.storageKeys.STATE];
|
|
94
|
+
if (x !== B) {
|
|
88
95
|
await chrome.storage.session.remove([
|
|
89
96
|
this.storageKeys.CODE_VERIFIER,
|
|
90
97
|
this.storageKeys.STATE
|
|
91
|
-
]),
|
|
98
|
+
]), _(d("State mismatch - possible CSRF", "oauth-error"));
|
|
92
99
|
return;
|
|
93
100
|
}
|
|
94
|
-
if (!
|
|
101
|
+
if (!f) {
|
|
95
102
|
await chrome.storage.session.remove([
|
|
96
103
|
this.storageKeys.CODE_VERIFIER,
|
|
97
104
|
this.storageKeys.STATE
|
|
98
|
-
]),
|
|
105
|
+
]), _(d("No authorization code received", "oauth-error"));
|
|
99
106
|
return;
|
|
100
107
|
}
|
|
101
|
-
await this.exchangeCodeForTokens(
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
throw
|
|
105
|
-
this.setAuthState(
|
|
108
|
+
await this.exchangeCodeForTokens(f);
|
|
109
|
+
const C = await this.getAuthState();
|
|
110
|
+
if (C.status !== "authenticated")
|
|
111
|
+
throw d("Login failed", "oauth-error");
|
|
112
|
+
this.setAuthState(C), await chrome.storage.session.remove([
|
|
106
113
|
this.storageKeys.CODE_VERIFIER,
|
|
107
114
|
this.storageKeys.STATE
|
|
108
|
-
]),
|
|
109
|
-
} catch (
|
|
115
|
+
]), T(C);
|
|
116
|
+
} catch (I) {
|
|
110
117
|
await chrome.storage.session.remove([
|
|
111
118
|
this.storageKeys.CODE_VERIFIER,
|
|
112
119
|
this.storageKeys.STATE
|
|
113
|
-
]),
|
|
120
|
+
]), _(I);
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
);
|
|
@@ -120,7 +127,7 @@ class te extends U {
|
|
|
120
127
|
const t = (await chrome.storage.session.get(this.storageKeys.REFRESH_TOKEN))[this.storageKeys.REFRESH_TOKEN];
|
|
121
128
|
if (t)
|
|
122
129
|
try {
|
|
123
|
-
const
|
|
130
|
+
const i = new URLSearchParams({
|
|
124
131
|
token: t,
|
|
125
132
|
client_id: this.authClientId,
|
|
126
133
|
token_type_hint: "refresh_token"
|
|
@@ -130,10 +137,10 @@ class te extends U {
|
|
|
130
137
|
headers: {
|
|
131
138
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
132
139
|
},
|
|
133
|
-
body:
|
|
140
|
+
body: i
|
|
134
141
|
});
|
|
135
|
-
} catch (
|
|
136
|
-
this.logger.warn("Token revocation failed:",
|
|
142
|
+
} catch (i) {
|
|
143
|
+
this.logger.warn("Token revocation failed:", i);
|
|
137
144
|
}
|
|
138
145
|
await chrome.storage.session.remove([
|
|
139
146
|
this.storageKeys.ACCESS_TOKEN,
|
|
@@ -153,7 +160,7 @@ class te extends U {
|
|
|
153
160
|
// OAuth Helper Methods
|
|
154
161
|
// ============================================================================
|
|
155
162
|
async exchangeCodeForTokens(e) {
|
|
156
|
-
const r = (await chrome.storage.session.get(this.storageKeys.CODE_VERIFIER))[this.storageKeys.CODE_VERIFIER],
|
|
163
|
+
const r = (await chrome.storage.session.get(this.storageKeys.CODE_VERIFIER))[this.storageKeys.CODE_VERIFIER], i = chrome.identity.getRedirectURL("callback"), s = await fetch(this.authEndpoints.token, {
|
|
157
164
|
method: "POST",
|
|
158
165
|
headers: {
|
|
159
166
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
@@ -161,20 +168,20 @@ class te extends U {
|
|
|
161
168
|
body: new URLSearchParams({
|
|
162
169
|
grant_type: "authorization_code",
|
|
163
170
|
code: e,
|
|
164
|
-
redirect_uri:
|
|
171
|
+
redirect_uri: i,
|
|
165
172
|
client_id: this.authClientId,
|
|
166
173
|
code_verifier: r
|
|
167
174
|
})
|
|
168
175
|
});
|
|
169
176
|
if (!s.ok) {
|
|
170
|
-
const
|
|
171
|
-
throw new Error(`Token exchange failed: ${s.status} ${
|
|
177
|
+
const E = await s.text();
|
|
178
|
+
throw new Error(`Token exchange failed: ${s.status} ${E}`);
|
|
172
179
|
}
|
|
173
|
-
const
|
|
180
|
+
const o = await s.json(), n = Date.now() + (o.expires_in || 3600) * 1e3;
|
|
174
181
|
await chrome.storage.session.set({
|
|
175
|
-
[this.storageKeys.ACCESS_TOKEN]:
|
|
176
|
-
[this.storageKeys.REFRESH_TOKEN]:
|
|
177
|
-
[this.storageKeys.EXPIRES_AT]:
|
|
182
|
+
[this.storageKeys.ACCESS_TOKEN]: o.access_token,
|
|
183
|
+
[this.storageKeys.REFRESH_TOKEN]: o.refresh_token,
|
|
184
|
+
[this.storageKeys.EXPIRES_AT]: n
|
|
178
185
|
}), await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER, this.storageKeys.STATE]);
|
|
179
186
|
}
|
|
180
187
|
// ============================================================================
|
|
@@ -194,13 +201,13 @@ class te extends U {
|
|
|
194
201
|
return chrome.identity.getRedirectURL("callback");
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
|
-
function
|
|
198
|
-
return
|
|
204
|
+
function he(a) {
|
|
205
|
+
return a !== null && typeof a == "object";
|
|
199
206
|
}
|
|
200
|
-
function
|
|
201
|
-
return
|
|
207
|
+
function Ee(a) {
|
|
208
|
+
return he(a) && "message" in a && typeof a.message == "string" && "type" in a && typeof a.type == "string";
|
|
202
209
|
}
|
|
203
|
-
const
|
|
210
|
+
const S = {
|
|
204
211
|
API_REQUEST: "BODHI_API_REQUEST",
|
|
205
212
|
API_RESPONSE: "BODHI_API_RESPONSE",
|
|
206
213
|
STREAM_REQUEST: "BODHI_STREAM_REQUEST",
|
|
@@ -211,36 +218,36 @@ const m = {
|
|
|
211
218
|
EXT_REQUEST: "BODHI_EXT_REQUEST",
|
|
212
219
|
EXT_RESPONSE: "BODHI_EXT_RESPONSE"
|
|
213
220
|
};
|
|
214
|
-
function
|
|
215
|
-
return
|
|
221
|
+
function ue(a) {
|
|
222
|
+
return a !== null && typeof a == "object" && typeof a.status == "number" && a.status >= 200 && a.status < 300 && "body" in a;
|
|
216
223
|
}
|
|
217
|
-
function
|
|
218
|
-
return
|
|
224
|
+
function de(a) {
|
|
225
|
+
return a !== null && typeof a == "object" && a.type === S.STREAM_CHUNK;
|
|
219
226
|
}
|
|
220
|
-
function
|
|
221
|
-
return
|
|
227
|
+
function le(a) {
|
|
228
|
+
return a !== null && typeof a == "object" && a.type === S.STREAM_API_ERROR;
|
|
222
229
|
}
|
|
223
|
-
function
|
|
224
|
-
return
|
|
230
|
+
function ge(a) {
|
|
231
|
+
return a !== null && typeof a == "object" && a.type === S.STREAM_ERROR;
|
|
225
232
|
}
|
|
226
|
-
function
|
|
227
|
-
return
|
|
233
|
+
function N(a) {
|
|
234
|
+
return a !== null && typeof a == "object" && "error" in a;
|
|
228
235
|
}
|
|
229
|
-
function
|
|
230
|
-
return
|
|
236
|
+
function Te(a) {
|
|
237
|
+
return a instanceof Error && "error" in a && !("response" in a) && Ee(a.error);
|
|
231
238
|
}
|
|
232
|
-
const
|
|
239
|
+
const _e = {
|
|
233
240
|
GET_EXTENSION_ID: "get_extension_id",
|
|
234
241
|
TEST_CONNECTION: "test_connection"
|
|
235
|
-
},
|
|
236
|
-
class
|
|
242
|
+
}, pe = "BODHI_STREAM_PORT";
|
|
243
|
+
class me {
|
|
237
244
|
constructor(e = {}, t) {
|
|
238
245
|
this.state = {
|
|
239
246
|
type: "extension",
|
|
240
247
|
extension: "not-initialized",
|
|
241
248
|
extensionId: null,
|
|
242
|
-
server:
|
|
243
|
-
}, this.extensionId = null, this.broadcastListenerActive = !1, this.config = e, this.logger = new
|
|
249
|
+
server: w
|
|
250
|
+
}, this.extensionId = null, this.broadcastListenerActive = !1, this.config = e, this.logger = new P("ExtClient", e?.logLevel || "warn"), this.onStateChange = t ?? F, this.apiTimeoutMs = e.apiTimeoutMs ?? ne;
|
|
244
251
|
}
|
|
245
252
|
/**
|
|
246
253
|
* Set client state and notify callback
|
|
@@ -267,7 +274,7 @@ class ue {
|
|
|
267
274
|
setupBroadcastListener() {
|
|
268
275
|
this.broadcastListenerActive || (this.broadcastListenerActive = !0, chrome.runtime.onMessage.addListener((e) => {
|
|
269
276
|
const t = e;
|
|
270
|
-
return t?.type ===
|
|
277
|
+
return t?.type === c.EXT2EXT_CLIENT_BROADCAST && t.event === "authStateChanged" && this.handleAuthStateChangedBroadcast(), !1;
|
|
271
278
|
}), this.logger.debug("Broadcast listener setup complete"));
|
|
272
279
|
}
|
|
273
280
|
/**
|
|
@@ -308,52 +315,52 @@ class ue {
|
|
|
308
315
|
async init(e = {}) {
|
|
309
316
|
if (!e.testConnection && !e.selectedConnection) {
|
|
310
317
|
this.logger.info("No testConnection or selectedConnection, returning not-initialized state");
|
|
311
|
-
const
|
|
312
|
-
return this.setState(
|
|
318
|
+
const i = H();
|
|
319
|
+
return this.setState(i), i;
|
|
313
320
|
}
|
|
314
321
|
if (this.extensionId && !e.testConnection)
|
|
315
322
|
return this.logger.debug("Already initialized with extensionId, skipping discovery"), this.state;
|
|
316
|
-
const t = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ??
|
|
323
|
+
const t = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ?? re, r = e.savedState?.extensionId;
|
|
317
324
|
try {
|
|
318
325
|
if (!this.extensionId) {
|
|
319
326
|
if (r)
|
|
320
327
|
this.logger.info("Restoring with known extensionId:", r), await this.sendExtMessageWithTimeout(
|
|
321
|
-
|
|
328
|
+
m.SET_EXTENSION_ID,
|
|
322
329
|
{ extensionId: r },
|
|
323
330
|
t
|
|
324
331
|
), this.extensionId = r;
|
|
325
332
|
else {
|
|
326
333
|
this.logger.info("Discovering bodhi-browser extension...");
|
|
327
|
-
const
|
|
334
|
+
const o = {
|
|
328
335
|
attempts: this.config.initParams?.extension?.attempts,
|
|
329
336
|
attemptWaitMs: this.config.initParams?.extension?.attemptWaitMs,
|
|
330
337
|
attemptTimeout: this.config.initParams?.extension?.attemptTimeout
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
}, n = await this.sendExtMessageWithTimeout(
|
|
339
|
+
m.DISCOVER_EXTENSION,
|
|
340
|
+
o,
|
|
334
341
|
t
|
|
335
342
|
);
|
|
336
|
-
this.extensionId =
|
|
343
|
+
this.extensionId = n.extensionId, this.logger.info("Extension discovered:", this.extensionId);
|
|
337
344
|
}
|
|
338
345
|
this.setupBroadcastListener();
|
|
339
346
|
}
|
|
340
|
-
const
|
|
347
|
+
const i = {
|
|
341
348
|
type: "extension",
|
|
342
349
|
extension: "ready",
|
|
343
350
|
extensionId: this.extensionId,
|
|
344
|
-
server:
|
|
351
|
+
server: w
|
|
345
352
|
};
|
|
346
|
-
let s =
|
|
353
|
+
let s = w;
|
|
347
354
|
if (e.testConnection)
|
|
348
355
|
try {
|
|
349
356
|
s = await this.getServerState(), this.logger.info("Server connectivity tested, state:", s.status);
|
|
350
|
-
} catch (
|
|
351
|
-
this.logger.error("Failed to get server state:",
|
|
357
|
+
} catch (o) {
|
|
358
|
+
this.logger.error("Failed to get server state:", o), s = z;
|
|
352
359
|
}
|
|
353
|
-
return this.setState({ ...
|
|
354
|
-
} catch (
|
|
355
|
-
this.logger.error("Failed to initialize extension:",
|
|
356
|
-
const s =
|
|
360
|
+
return this.setState({ ...i, server: s }), this.state;
|
|
361
|
+
} catch (i) {
|
|
362
|
+
this.logger.error("Failed to initialize extension:", i), this.extensionId = null;
|
|
363
|
+
const s = Q();
|
|
357
364
|
return this.setState(s), this.state;
|
|
358
365
|
}
|
|
359
366
|
}
|
|
@@ -361,10 +368,10 @@ class ue {
|
|
|
361
368
|
* Helper method to send ext message with timeout support
|
|
362
369
|
*/
|
|
363
370
|
async sendExtMessageWithTimeout(e, t, r = 1e4) {
|
|
364
|
-
const
|
|
365
|
-
(s,
|
|
371
|
+
const i = new Promise(
|
|
372
|
+
(s, o) => setTimeout(() => o(new Error("Timeout")), r)
|
|
366
373
|
);
|
|
367
|
-
return Promise.race([this.sendExtRequest(e, t),
|
|
374
|
+
return Promise.race([this.sendExtRequest(e, t), i]);
|
|
368
375
|
}
|
|
369
376
|
/**
|
|
370
377
|
* Send an EXT2EXT_CLIENT_REQUEST message and await EXT2EXT_CLIENT_RESPONSE
|
|
@@ -372,29 +379,29 @@ class ue {
|
|
|
372
379
|
*/
|
|
373
380
|
async sendExtRequest(e, t) {
|
|
374
381
|
try {
|
|
375
|
-
const r = this.generateRequestId(),
|
|
376
|
-
type:
|
|
382
|
+
const r = this.generateRequestId(), i = await chrome.runtime.sendMessage({
|
|
383
|
+
type: c.EXT2EXT_CLIENT_REQUEST,
|
|
377
384
|
requestId: r,
|
|
378
385
|
request: {
|
|
379
386
|
action: e,
|
|
380
387
|
params: t
|
|
381
388
|
}
|
|
382
389
|
});
|
|
383
|
-
if (!
|
|
384
|
-
throw
|
|
385
|
-
if (
|
|
386
|
-
throw
|
|
390
|
+
if (!i)
|
|
391
|
+
throw d("No response from background script", "extension_error");
|
|
392
|
+
if (i.type !== c.EXT2EXT_CLIENT_RESPONSE)
|
|
393
|
+
throw d(
|
|
387
394
|
"Invalid response type from background script",
|
|
388
395
|
"extension_error"
|
|
389
396
|
);
|
|
390
|
-
const s =
|
|
391
|
-
if (
|
|
392
|
-
const
|
|
393
|
-
throw
|
|
397
|
+
const s = i.response;
|
|
398
|
+
if (N(s)) {
|
|
399
|
+
const o = s.error.type || "extension_error";
|
|
400
|
+
throw d(s.error.message, o);
|
|
394
401
|
}
|
|
395
402
|
return s;
|
|
396
403
|
} catch (r) {
|
|
397
|
-
throw
|
|
404
|
+
throw Te(r) ? r : d(
|
|
398
405
|
r instanceof Error ? r.message : "Unknown error occurred",
|
|
399
406
|
"extension_error"
|
|
400
407
|
);
|
|
@@ -404,16 +411,16 @@ class ue {
|
|
|
404
411
|
* Send an API_REQUEST message and await API_RESPONSE (internal)
|
|
405
412
|
* Returns ext2ext-specific ExtClientApiResponseMessage
|
|
406
413
|
*/
|
|
407
|
-
async sendRawApiMessage(e, t, r,
|
|
408
|
-
const
|
|
414
|
+
async sendRawApiMessage(e, t, r, i, s) {
|
|
415
|
+
const o = this.generateRequestId();
|
|
409
416
|
return await chrome.runtime.sendMessage({
|
|
410
|
-
type:
|
|
411
|
-
requestId:
|
|
417
|
+
type: c.EXT2EXT_CLIENT_API_REQUEST,
|
|
418
|
+
requestId: o,
|
|
412
419
|
request: {
|
|
413
420
|
method: e,
|
|
414
421
|
endpoint: t,
|
|
415
422
|
body: r,
|
|
416
|
-
headers:
|
|
423
|
+
headers: i,
|
|
417
424
|
authenticated: s
|
|
418
425
|
}
|
|
419
426
|
});
|
|
@@ -421,55 +428,71 @@ class ue {
|
|
|
421
428
|
/**
|
|
422
429
|
* Send an API message and convert to protocol-agnostic ApiResponseResult
|
|
423
430
|
*/
|
|
424
|
-
async sendApiRequest(e, t, r,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
async sendApiRequest(e, t, r, i, s) {
|
|
432
|
+
try {
|
|
433
|
+
const o = new Promise(
|
|
434
|
+
(E, h) => setTimeout(
|
|
435
|
+
() => h(
|
|
436
|
+
new Error(
|
|
437
|
+
`[bodhi-js-sdk/ext] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`
|
|
438
|
+
)
|
|
439
|
+
),
|
|
440
|
+
this.apiTimeoutMs
|
|
441
|
+
)
|
|
442
|
+
), n = await Promise.race([
|
|
443
|
+
this.sendRawApiMessage(e, t, r, i, s),
|
|
444
|
+
o
|
|
445
|
+
]);
|
|
446
|
+
if (ae(n)) {
|
|
447
|
+
const E = n.error.type || "extension_error";
|
|
448
|
+
return {
|
|
449
|
+
error: {
|
|
450
|
+
message: n.error.message,
|
|
451
|
+
type: E
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
return n.response;
|
|
456
|
+
} catch (o) {
|
|
434
457
|
return {
|
|
435
458
|
error: {
|
|
436
|
-
message:
|
|
437
|
-
type:
|
|
459
|
+
message: o instanceof Error ? o.message : String(o),
|
|
460
|
+
type: "network_error"
|
|
438
461
|
}
|
|
439
462
|
};
|
|
440
463
|
}
|
|
441
|
-
return n.response;
|
|
442
464
|
}
|
|
443
465
|
/**
|
|
444
466
|
* Login user via OAuth
|
|
467
|
+
* @param options - Optional login parameters including toolsetScopeIds
|
|
445
468
|
* @throws ExtError if login fails
|
|
446
469
|
* @returns AuthState with login state and user info
|
|
447
470
|
*/
|
|
448
|
-
async login() {
|
|
449
|
-
return new Promise((
|
|
450
|
-
const
|
|
451
|
-
if (
|
|
452
|
-
chrome.runtime.onMessage.removeListener(
|
|
471
|
+
async login(e) {
|
|
472
|
+
return new Promise((t, r) => {
|
|
473
|
+
const i = async (s) => {
|
|
474
|
+
if (s && typeof s == "object" && "type" in s && s.type === "EXT2EXT_CLIENT_BROADCAST" && "event" in s && s.event === "authStateChanged") {
|
|
475
|
+
chrome.runtime.onMessage.removeListener(i);
|
|
453
476
|
try {
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
477
|
+
const o = await this.getAuthState();
|
|
478
|
+
if (Z(o)) {
|
|
479
|
+
r(
|
|
480
|
+
d(`Login failed: ${o.error?.message}`, "auth-error")
|
|
458
481
|
);
|
|
459
482
|
return;
|
|
460
483
|
}
|
|
461
|
-
if (
|
|
462
|
-
|
|
484
|
+
if (o.status !== "authenticated") {
|
|
485
|
+
r(d("Login failed: User is not logged in", "auth-error"));
|
|
463
486
|
return;
|
|
464
487
|
}
|
|
465
|
-
this.setAuthState(
|
|
466
|
-
} catch (
|
|
467
|
-
|
|
488
|
+
this.setAuthState(o), t(o);
|
|
489
|
+
} catch (o) {
|
|
490
|
+
r(o);
|
|
468
491
|
}
|
|
469
492
|
}
|
|
470
493
|
};
|
|
471
|
-
chrome.runtime.onMessage.addListener(
|
|
472
|
-
chrome.runtime.onMessage.removeListener(
|
|
494
|
+
chrome.runtime.onMessage.addListener(i), this.sendExtRequest(m.LOGIN, e).catch((s) => {
|
|
495
|
+
chrome.runtime.onMessage.removeListener(i), r(s);
|
|
473
496
|
});
|
|
474
497
|
});
|
|
475
498
|
}
|
|
@@ -479,7 +502,7 @@ class ue {
|
|
|
479
502
|
* @returns AuthLoggedOut with logged out state
|
|
480
503
|
*/
|
|
481
504
|
async logout() {
|
|
482
|
-
await this.sendExtRequest(
|
|
505
|
+
await this.sendExtRequest(m.LOGOUT);
|
|
483
506
|
const e = {
|
|
484
507
|
status: "unauthenticated",
|
|
485
508
|
user: null,
|
|
@@ -495,8 +518,8 @@ class ue {
|
|
|
495
518
|
*/
|
|
496
519
|
async getAuthState() {
|
|
497
520
|
return this.isClientInitialized() ? (await this.sendExtRequest(
|
|
498
|
-
|
|
499
|
-
)).authState :
|
|
521
|
+
m.GET_AUTH_STATE
|
|
522
|
+
)).authState : G;
|
|
500
523
|
}
|
|
501
524
|
/**
|
|
502
525
|
* Ping bodhi-browser-ext API via /ping endpoint
|
|
@@ -510,13 +533,13 @@ class ue {
|
|
|
510
533
|
*/
|
|
511
534
|
async getServerState() {
|
|
512
535
|
const e = await this.sendApiRequest("GET", "/bodhi/v1/info");
|
|
513
|
-
if (
|
|
536
|
+
if (v(e))
|
|
514
537
|
return {
|
|
515
538
|
status: "not-reachable",
|
|
516
539
|
version: null,
|
|
517
540
|
error: e.error
|
|
518
541
|
};
|
|
519
|
-
if (!
|
|
542
|
+
if (!O(e))
|
|
520
543
|
return {
|
|
521
544
|
status: "not-reachable",
|
|
522
545
|
version: null,
|
|
@@ -558,91 +581,91 @@ class ue {
|
|
|
558
581
|
/**
|
|
559
582
|
* Generic streaming method
|
|
560
583
|
*/
|
|
561
|
-
async *stream(e, t, r,
|
|
562
|
-
const
|
|
584
|
+
async *stream(e, t, r, i, s = !0) {
|
|
585
|
+
const o = this.generateRequestId();
|
|
563
586
|
this.logger.debug("Starting stream", {
|
|
564
587
|
method: e,
|
|
565
588
|
endpoint: t,
|
|
566
|
-
requestId:
|
|
589
|
+
requestId: o
|
|
567
590
|
});
|
|
568
|
-
const
|
|
569
|
-
start: (
|
|
570
|
-
|
|
571
|
-
if (
|
|
572
|
-
switch (
|
|
573
|
-
case
|
|
574
|
-
this.logger.debug("Stream complete", { requestId:
|
|
591
|
+
const n = chrome.runtime.connect({ name: $ }), h = new ReadableStream({
|
|
592
|
+
start: (l) => {
|
|
593
|
+
n.onMessage.addListener((u) => {
|
|
594
|
+
if (u.requestId === o)
|
|
595
|
+
switch (u.type) {
|
|
596
|
+
case c.EXT2EXT_CLIENT_STREAM_DONE:
|
|
597
|
+
this.logger.debug("Stream complete", { requestId: o }), l.close(), n.disconnect();
|
|
575
598
|
break;
|
|
576
|
-
case
|
|
599
|
+
case c.EXT2EXT_CLIENT_STREAM_ERROR:
|
|
577
600
|
this.logger.error("Stream error", {
|
|
578
|
-
requestId:
|
|
579
|
-
error: JSON.stringify(
|
|
580
|
-
}),
|
|
581
|
-
|
|
582
|
-
|
|
601
|
+
requestId: o,
|
|
602
|
+
error: JSON.stringify(u.error)
|
|
603
|
+
}), l.error(
|
|
604
|
+
d(
|
|
605
|
+
u.error.message,
|
|
583
606
|
"extension_error"
|
|
584
607
|
)
|
|
585
|
-
),
|
|
608
|
+
), n.disconnect();
|
|
586
609
|
break;
|
|
587
|
-
case
|
|
588
|
-
const
|
|
610
|
+
case c.EXT2EXT_CLIENT_STREAM_API_ERROR: {
|
|
611
|
+
const p = u;
|
|
589
612
|
this.logger.error("Stream API error", {
|
|
590
|
-
requestId:
|
|
591
|
-
error:
|
|
592
|
-
}),
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
613
|
+
requestId: o,
|
|
614
|
+
error: p.response.body?.error
|
|
615
|
+
}), l.error(
|
|
616
|
+
W(
|
|
617
|
+
p.response.body?.error?.message || "API error",
|
|
618
|
+
p.response.status,
|
|
619
|
+
p.response.body
|
|
597
620
|
)
|
|
598
|
-
),
|
|
621
|
+
), n.disconnect();
|
|
599
622
|
break;
|
|
600
623
|
}
|
|
601
|
-
case
|
|
602
|
-
const
|
|
603
|
-
|
|
624
|
+
case c.EXT2EXT_CLIENT_STREAM_CHUNK: {
|
|
625
|
+
const p = u;
|
|
626
|
+
ue(p.response) && l.enqueue(p.response.body);
|
|
604
627
|
break;
|
|
605
628
|
}
|
|
606
629
|
}
|
|
607
|
-
}),
|
|
608
|
-
this.logger.debug("Port disconnected", { requestId:
|
|
630
|
+
}), n.onDisconnect.addListener(() => {
|
|
631
|
+
this.logger.debug("Port disconnected", { requestId: o });
|
|
609
632
|
try {
|
|
610
|
-
|
|
611
|
-
|
|
633
|
+
l.error(
|
|
634
|
+
d("Connection closed unexpectedly", "extension_error")
|
|
612
635
|
);
|
|
613
636
|
} catch {
|
|
614
637
|
}
|
|
615
|
-
}),
|
|
616
|
-
type:
|
|
617
|
-
requestId:
|
|
618
|
-
request: { method: e, endpoint: t, body: r, headers:
|
|
638
|
+
}), n.postMessage({
|
|
639
|
+
type: c.EXT2EXT_CLIENT_STREAM_REQUEST,
|
|
640
|
+
requestId: o,
|
|
641
|
+
request: { method: e, endpoint: t, body: r, headers: i, authenticated: s }
|
|
619
642
|
});
|
|
620
643
|
}
|
|
621
644
|
}).getReader();
|
|
622
645
|
try {
|
|
623
646
|
for (; ; ) {
|
|
624
|
-
const { done:
|
|
625
|
-
if (
|
|
647
|
+
const { done: l, value: u } = await h.read();
|
|
648
|
+
if (l) {
|
|
626
649
|
this.logger.debug("Stream iteration complete");
|
|
627
650
|
break;
|
|
628
651
|
}
|
|
629
|
-
yield
|
|
652
|
+
yield u;
|
|
630
653
|
}
|
|
631
654
|
} finally {
|
|
632
|
-
|
|
655
|
+
h.releaseLock();
|
|
633
656
|
}
|
|
634
657
|
}
|
|
635
658
|
// ============================================================================
|
|
636
659
|
// OpenAI-Compatible Namespaced API
|
|
637
660
|
// ============================================================================
|
|
638
661
|
get chat() {
|
|
639
|
-
return this._chat ??= new
|
|
662
|
+
return this._chat ??= new J(this);
|
|
640
663
|
}
|
|
641
664
|
get models() {
|
|
642
|
-
return this._models ??= new
|
|
665
|
+
return this._models ??= new Y(this);
|
|
643
666
|
}
|
|
644
667
|
get embeddings() {
|
|
645
|
-
return this._embeddings ??= new
|
|
668
|
+
return this._embeddings ??= new j(this);
|
|
646
669
|
}
|
|
647
670
|
/**
|
|
648
671
|
* Serialize ext2ext client state for persistence
|
|
@@ -661,48 +684,54 @@ class ue {
|
|
|
661
684
|
};
|
|
662
685
|
}
|
|
663
686
|
}
|
|
664
|
-
class
|
|
687
|
+
class Re extends ee {
|
|
665
688
|
constructor(e, t, r) {
|
|
666
|
-
const
|
|
667
|
-
basePath:
|
|
668
|
-
authServerUrl:
|
|
669
|
-
userScope:
|
|
670
|
-
logLevel:
|
|
671
|
-
|
|
689
|
+
const i = t || {}, s = {
|
|
690
|
+
basePath: i.basePath || "/",
|
|
691
|
+
authServerUrl: i.authServerUrl || "https://id.getbodhi.app/realms/bodhi",
|
|
692
|
+
userScope: i.userScope || "scope_user_user",
|
|
693
|
+
logLevel: i.logLevel || "warn",
|
|
694
|
+
apiTimeoutMs: i.apiTimeoutMs,
|
|
695
|
+
initParams: i.initParams
|
|
672
696
|
};
|
|
673
697
|
super(e, s, r);
|
|
674
698
|
}
|
|
675
699
|
createLogger(e) {
|
|
676
|
-
return new
|
|
700
|
+
return new P("ExtUIClient", e.logLevel);
|
|
677
701
|
}
|
|
678
702
|
createStoragePrefix(e) {
|
|
679
|
-
return
|
|
703
|
+
return L(e.basePath, U.EXT);
|
|
680
704
|
}
|
|
681
705
|
createExtClient(e, t) {
|
|
682
|
-
return new
|
|
683
|
-
{
|
|
706
|
+
return new me(
|
|
707
|
+
{
|
|
708
|
+
logLevel: e.logLevel,
|
|
709
|
+
apiTimeoutMs: e.apiTimeoutMs,
|
|
710
|
+
initParams: e.initParams
|
|
711
|
+
},
|
|
684
712
|
t
|
|
685
713
|
);
|
|
686
714
|
}
|
|
687
715
|
createDirectClient(e, t, r) {
|
|
688
|
-
return new
|
|
716
|
+
return new ce(
|
|
689
717
|
{
|
|
690
718
|
authClientId: e,
|
|
691
719
|
authServerUrl: t.authServerUrl,
|
|
692
720
|
userScope: t.userScope,
|
|
693
721
|
logLevel: t.logLevel,
|
|
694
|
-
basePath: t.basePath
|
|
722
|
+
basePath: t.basePath,
|
|
723
|
+
apiTimeoutMs: t.apiTimeoutMs
|
|
695
724
|
},
|
|
696
725
|
r
|
|
697
726
|
);
|
|
698
727
|
}
|
|
699
728
|
}
|
|
700
|
-
const
|
|
729
|
+
const Se = ["ggedphdcbekjlomjaidbajglgihbeaon"], Ie = ["bjdjhiombmfbcoeojijpfckljjghmjbf"], A = "production", y = class y {
|
|
701
730
|
// ============================================================================
|
|
702
731
|
// Constructor
|
|
703
732
|
// ============================================================================
|
|
704
733
|
constructor(e, t) {
|
|
705
|
-
this.isAuthenticating = !1, this.state = "setup", this.listenersInitialized = !1, this.refreshPromise = null, this.activeStreamPorts = /* @__PURE__ */ new Map(), this.authClientId = e, this.authServerUrl = t?.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userScope = t?.userScope || "scope_user_user", this.extensionId = t?.extensionId, this.logger = new
|
|
734
|
+
this.isAuthenticating = !1, this.state = "setup", this.listenersInitialized = !1, this.refreshPromise = null, this.activeStreamPorts = /* @__PURE__ */ new Map(), this.authClientId = e, this.authServerUrl = t?.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userScope = t?.userScope || "scope_user_user", this.extensionId = t?.extensionId, this.logger = new P("BodhiExtClient", t?.logLevel || "warn"), this.attempts = t?.attempts ?? se, this.attemptWaitMs = t?.attemptWaitMs ?? oe, this.attemptTimeout = t?.attemptTimeout ?? ie, this.authEndpoints = {
|
|
706
735
|
authorize: `${this.authServerUrl}/protocol/openid-connect/auth`,
|
|
707
736
|
token: `${this.authServerUrl}/protocol/openid-connect/token`,
|
|
708
737
|
userinfo: `${this.authServerUrl}/protocol/openid-connect/userinfo`,
|
|
@@ -720,11 +749,11 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
720
749
|
}
|
|
721
750
|
static generateCodeVerifier() {
|
|
722
751
|
const e = new Uint8Array(32);
|
|
723
|
-
return crypto.getRandomValues(e),
|
|
752
|
+
return crypto.getRandomValues(e), y.base64UrlEncode(e.buffer);
|
|
724
753
|
}
|
|
725
754
|
static async generateCodeChallenge(e) {
|
|
726
|
-
const r = new TextEncoder().encode(e),
|
|
727
|
-
return
|
|
755
|
+
const r = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", r);
|
|
756
|
+
return y.base64UrlEncode(i);
|
|
728
757
|
}
|
|
729
758
|
// ============================================================================
|
|
730
759
|
// State Management
|
|
@@ -743,7 +772,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
743
772
|
* Get extension IDs for current environment
|
|
744
773
|
*/
|
|
745
774
|
getExtensionIdsForEnvironment() {
|
|
746
|
-
const t =
|
|
775
|
+
const t = A !== "production" ? Se : Ie;
|
|
747
776
|
return this.logger.info("[Ext2Ext/Registry] Environment: production"), this.logger.debug("[Ext2Ext/Registry] Using extension IDs:", t), t;
|
|
748
777
|
}
|
|
749
778
|
/**
|
|
@@ -753,34 +782,34 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
753
782
|
return this.logger.debug(
|
|
754
783
|
`[Ext2Ext/Discovery] Pinging extension: ${e} with timeout ${this.attemptTimeout}ms`
|
|
755
784
|
), new Promise((t, r) => {
|
|
756
|
-
const
|
|
785
|
+
const i = setTimeout(() => {
|
|
757
786
|
this.logger.debug(`[Ext2Ext/Discovery] Timeout waiting for extension ${e}`), r(new Error("Timeout"));
|
|
758
787
|
}, this.attemptTimeout);
|
|
759
788
|
try {
|
|
760
789
|
const s = {
|
|
761
|
-
type:
|
|
790
|
+
type: S.EXT_REQUEST,
|
|
762
791
|
requestId: crypto.randomUUID(),
|
|
763
792
|
request: {
|
|
764
793
|
action: "get_extension_id"
|
|
765
794
|
}
|
|
766
795
|
};
|
|
767
|
-
this.logger.debug(`[Ext2Ext/Discovery] Sending message to ${e}:`, s), chrome.runtime.sendMessage(e, s, (
|
|
768
|
-
if (clearTimeout(
|
|
796
|
+
this.logger.debug(`[Ext2Ext/Discovery] Sending message to ${e}:`, s), chrome.runtime.sendMessage(e, s, (o) => {
|
|
797
|
+
if (clearTimeout(i), chrome.runtime.lastError) {
|
|
769
798
|
this.logger.error(
|
|
770
799
|
`[Ext2Ext/Discovery] Error from extension ${e}:`,
|
|
771
800
|
chrome.runtime.lastError.message
|
|
772
801
|
), r(new Error(chrome.runtime.lastError.message));
|
|
773
802
|
return;
|
|
774
803
|
}
|
|
775
|
-
this.logger.debug(`[Ext2Ext/Discovery] Response from ${e}:`,
|
|
776
|
-
const
|
|
777
|
-
|
|
804
|
+
this.logger.debug(`[Ext2Ext/Discovery] Response from ${e}:`, o);
|
|
805
|
+
const n = o;
|
|
806
|
+
n && n.type === S.EXT_RESPONSE ? (this.logger.debug(`[Ext2Ext/Discovery] ✓ Extension ${e} responded`), t(!0)) : (this.logger.error(
|
|
778
807
|
`[Ext2Ext/Discovery] Invalid response from ${e}:`,
|
|
779
|
-
|
|
808
|
+
o
|
|
780
809
|
), r(new Error("Invalid response")));
|
|
781
810
|
});
|
|
782
811
|
} catch (s) {
|
|
783
|
-
this.logger.error(`[Ext2Ext/Discovery] Exception pinging ${e}:`, s), clearTimeout(
|
|
812
|
+
this.logger.error(`[Ext2Ext/Discovery] Exception pinging ${e}:`, s), clearTimeout(i), r(s);
|
|
784
813
|
}
|
|
785
814
|
});
|
|
786
815
|
}
|
|
@@ -795,39 +824,39 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
795
824
|
* @param params Resolved discovery params
|
|
796
825
|
*/
|
|
797
826
|
async discoverBodhiExtension(e) {
|
|
798
|
-
const { attempts: t, attemptWaitMs: r, attemptTimeout:
|
|
827
|
+
const { attempts: t, attemptWaitMs: r, attemptTimeout: i } = e;
|
|
799
828
|
this.logger.info(
|
|
800
|
-
`[Ext2Ext/Discovery] Starting discovery: ${t} attempts per ID, ${
|
|
829
|
+
`[Ext2Ext/Discovery] Starting discovery: ${t} attempts per ID, ${i}ms timeout, ${r}ms between attempts`
|
|
801
830
|
);
|
|
802
831
|
const s = this.getExtensionIdsForEnvironment();
|
|
803
832
|
this.logger.debug(
|
|
804
833
|
`[Ext2Ext/Discovery] Will try ${s.length} extension(s):`,
|
|
805
834
|
s
|
|
806
835
|
);
|
|
807
|
-
for (const
|
|
808
|
-
for (let
|
|
836
|
+
for (const E of s) {
|
|
837
|
+
for (let h = 1; h <= t; h++) {
|
|
809
838
|
this.logger.debug(
|
|
810
|
-
`[Ext2Ext/Discovery] Trying ${
|
|
839
|
+
`[Ext2Ext/Discovery] Trying ${E} - attempt ${h}/${t}`
|
|
811
840
|
);
|
|
812
841
|
try {
|
|
813
|
-
return await this.pingExtension(
|
|
842
|
+
return await this.pingExtension(E), this.logger.info(`[Ext2Ext/Discovery] ✓ Found: ${E} on attempt ${h}`), {
|
|
814
843
|
success: !0,
|
|
815
|
-
extensionId:
|
|
844
|
+
extensionId: E
|
|
816
845
|
};
|
|
817
|
-
} catch (
|
|
846
|
+
} catch (l) {
|
|
818
847
|
this.logger.debug(
|
|
819
|
-
`[Ext2Ext/Discovery] Attempt ${
|
|
820
|
-
),
|
|
848
|
+
`[Ext2Ext/Discovery] Attempt ${h} failed for ${E}: ${l instanceof Error ? l.message : "Unknown error"}`
|
|
849
|
+
), h < t && await this.sleep(r);
|
|
821
850
|
}
|
|
822
851
|
}
|
|
823
852
|
this.logger.warn(
|
|
824
|
-
`[Ext2Ext/Discovery] ✗ Not found: ${
|
|
853
|
+
`[Ext2Ext/Discovery] ✗ Not found: ${E} after ${t} attempts`
|
|
825
854
|
);
|
|
826
855
|
}
|
|
827
|
-
const
|
|
828
|
-
return this.logger.error(`[Ext2Ext/Discovery] ${
|
|
856
|
+
const o = s.join(", "), n = `Extension not found. Tried ${s.length} IDs with ${t} attempts each: ${o}`;
|
|
857
|
+
return this.logger.error(`[Ext2Ext/Discovery] ${n}`), {
|
|
829
858
|
success: !1,
|
|
830
|
-
error:
|
|
859
|
+
error: n
|
|
831
860
|
};
|
|
832
861
|
}
|
|
833
862
|
// ============================================================================
|
|
@@ -841,8 +870,8 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
841
870
|
this.logger.debug("[BodhiExtClient] Listeners already initialized, skipping");
|
|
842
871
|
return;
|
|
843
872
|
}
|
|
844
|
-
this.listenersInitialized = !0, chrome.runtime.onMessage.addListener((e, t, r) => e.type !==
|
|
845
|
-
type:
|
|
873
|
+
this.listenersInitialized = !0, chrome.runtime.onMessage.addListener((e, t, r) => e.type !== c.EXT2EXT_CLIENT_REQUEST && e.type !== c.EXT2EXT_CLIENT_API_REQUEST ? !1 : this.state !== "ready" && !(e.type === c.EXT2EXT_CLIENT_REQUEST && e.request.action === m.DISCOVER_EXTENSION) ? (e.type === c.EXT2EXT_CLIENT_REQUEST ? r({
|
|
874
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
846
875
|
requestId: e.requestId,
|
|
847
876
|
response: {
|
|
848
877
|
error: {
|
|
@@ -851,24 +880,24 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
851
880
|
}
|
|
852
881
|
}
|
|
853
882
|
}) : r({
|
|
854
|
-
type:
|
|
883
|
+
type: c.EXT2EXT_CLIENT_API_RESPONSE,
|
|
855
884
|
requestId: e.requestId,
|
|
856
885
|
error: {
|
|
857
886
|
message: `Client not initialized. Extension discovery not complete, cannot handle type:${e.type}, message:${JSON.stringify(e)}`,
|
|
858
887
|
type: "NOT_INITIALIZED"
|
|
859
888
|
}
|
|
860
889
|
}), !0) : (this.logger.debug(`[BodhiExtClient] Processing message.type=${e.type}`), (async () => {
|
|
861
|
-
const
|
|
862
|
-
r(
|
|
890
|
+
const i = await this.handleAction(e);
|
|
891
|
+
r(i);
|
|
863
892
|
})(), !0)), chrome.runtime.onConnect.addListener((e) => {
|
|
864
|
-
if (e.name !==
|
|
893
|
+
if (e.name !== $) {
|
|
865
894
|
this.logger.debug("[BodhiExtClient] Ignoring port with name:", e.name);
|
|
866
895
|
return;
|
|
867
896
|
}
|
|
868
897
|
this.logger.info("[BodhiExtClient] Streaming port connected"), e.onMessage.addListener(async (t) => {
|
|
869
|
-
if (t.type !==
|
|
898
|
+
if (t.type !== c.EXT2EXT_CLIENT_STREAM_REQUEST) {
|
|
870
899
|
this.logger.warn("[BodhiExtClient] Unknown stream message type:", t.type), e.postMessage({
|
|
871
|
-
type:
|
|
900
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
872
901
|
requestId: t.requestId,
|
|
873
902
|
error: {
|
|
874
903
|
message: "Unknown stream message type",
|
|
@@ -911,7 +940,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
911
940
|
*/
|
|
912
941
|
broadcastAuthStateChange() {
|
|
913
942
|
chrome.runtime.sendMessage({
|
|
914
|
-
type:
|
|
943
|
+
type: c.EXT2EXT_CLIENT_BROADCAST,
|
|
915
944
|
event: "authStateChanged"
|
|
916
945
|
}).catch((e) => {
|
|
917
946
|
this.logger.debug("[BodhiExtClient] No listeners for broadcast:", e.message);
|
|
@@ -941,7 +970,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
941
970
|
const s = await this._getAccessTokenRaw();
|
|
942
971
|
if (!s)
|
|
943
972
|
return {
|
|
944
|
-
type:
|
|
973
|
+
type: c.EXT2EXT_CLIENT_API_RESPONSE,
|
|
945
974
|
requestId: t,
|
|
946
975
|
error: {
|
|
947
976
|
message: "Not authenticated. Please log in first.",
|
|
@@ -953,20 +982,20 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
953
982
|
Authorization: `Bearer ${s}`
|
|
954
983
|
}, this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request");
|
|
955
984
|
}
|
|
956
|
-
const
|
|
985
|
+
const i = await this.sendApiRequest(
|
|
957
986
|
e.request.method,
|
|
958
987
|
e.request.endpoint,
|
|
959
988
|
e.request.body,
|
|
960
989
|
r
|
|
961
990
|
);
|
|
962
991
|
return {
|
|
963
|
-
type:
|
|
992
|
+
type: c.EXT2EXT_CLIENT_API_RESPONSE,
|
|
964
993
|
requestId: t,
|
|
965
|
-
response:
|
|
994
|
+
response: i
|
|
966
995
|
};
|
|
967
996
|
} catch (r) {
|
|
968
997
|
return this.logger.error("[BodhiExtClient] API request failed:", r), {
|
|
969
|
-
type:
|
|
998
|
+
type: c.EXT2EXT_CLIENT_API_RESPONSE,
|
|
970
999
|
requestId: t,
|
|
971
1000
|
error: {
|
|
972
1001
|
message: r instanceof Error ? r.message : "Unknown error",
|
|
@@ -982,74 +1011,76 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
982
1011
|
* @returns Action response message (success or error)
|
|
983
1012
|
*/
|
|
984
1013
|
async handleExtClientRequest(e) {
|
|
985
|
-
const { requestId: t, request: r } = e, { action:
|
|
986
|
-
this.logger.debug(`[BodhiExtClient] Handling action: ${
|
|
1014
|
+
const { requestId: t, request: r } = e, { action: i, params: s } = r;
|
|
1015
|
+
this.logger.debug(`[BodhiExtClient] Handling action: ${i}`);
|
|
987
1016
|
try {
|
|
988
|
-
let
|
|
989
|
-
switch (
|
|
990
|
-
case
|
|
991
|
-
const
|
|
992
|
-
await this.init(
|
|
1017
|
+
let o = {};
|
|
1018
|
+
switch (i) {
|
|
1019
|
+
case m.DISCOVER_EXTENSION: {
|
|
1020
|
+
const n = s;
|
|
1021
|
+
await this.init(n), this.logger.info("[BodhiExtClient] Discovery successful:", {
|
|
993
1022
|
extensionId: this.extensionId,
|
|
994
|
-
environment:
|
|
995
|
-
}),
|
|
1023
|
+
environment: A
|
|
1024
|
+
}), o = {
|
|
996
1025
|
extensionId: this.extensionId,
|
|
997
|
-
environment:
|
|
1026
|
+
environment: A
|
|
998
1027
|
};
|
|
999
1028
|
break;
|
|
1000
1029
|
}
|
|
1001
|
-
case
|
|
1002
|
-
const { extensionId:
|
|
1003
|
-
this.extensionId =
|
|
1030
|
+
case m.SET_EXTENSION_ID: {
|
|
1031
|
+
const { extensionId: n } = s;
|
|
1032
|
+
this.extensionId = n, this.state = "ready", this.logger.info("[BodhiExtClient] Extension ID set:", { extensionId: n }), o = { success: !0 };
|
|
1004
1033
|
break;
|
|
1005
1034
|
}
|
|
1006
|
-
case
|
|
1007
|
-
const
|
|
1008
|
-
return
|
|
1009
|
-
type:
|
|
1035
|
+
case m.GET_EXTENSION_ID: {
|
|
1036
|
+
const n = await this.sendExtRequestRaw(_e.GET_EXTENSION_ID, s);
|
|
1037
|
+
return N(n.response) ? {
|
|
1038
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1010
1039
|
requestId: t,
|
|
1011
1040
|
response: {
|
|
1012
1041
|
error: {
|
|
1013
|
-
message:
|
|
1014
|
-
type:
|
|
1042
|
+
message: n.response.error.message || `Extension request failed to get extension ID: ${JSON.stringify(n.response)}`,
|
|
1043
|
+
type: n.response.error.type
|
|
1015
1044
|
}
|
|
1016
1045
|
}
|
|
1017
1046
|
} : {
|
|
1018
|
-
type:
|
|
1047
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1019
1048
|
requestId: t,
|
|
1020
|
-
response:
|
|
1049
|
+
response: n.response
|
|
1021
1050
|
};
|
|
1022
1051
|
}
|
|
1023
|
-
case
|
|
1024
|
-
|
|
1052
|
+
case m.LOGIN: {
|
|
1053
|
+
const n = s;
|
|
1054
|
+
await this.login(n), this.broadcastAuthStateChange();
|
|
1025
1055
|
break;
|
|
1026
|
-
|
|
1056
|
+
}
|
|
1057
|
+
case m.LOGOUT:
|
|
1027
1058
|
await this.logout(), this.broadcastAuthStateChange();
|
|
1028
1059
|
break;
|
|
1029
|
-
case
|
|
1030
|
-
|
|
1060
|
+
case m.GET_AUTH_STATE:
|
|
1061
|
+
o = { authState: await this.getAuthState() };
|
|
1031
1062
|
break;
|
|
1032
1063
|
default:
|
|
1033
1064
|
return {
|
|
1034
|
-
type:
|
|
1065
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1035
1066
|
requestId: t,
|
|
1036
1067
|
response: {
|
|
1037
|
-
error: { message: `Unknown action: ${
|
|
1068
|
+
error: { message: `Unknown action: ${i}`, type: "UNKNOWN_ACTION" }
|
|
1038
1069
|
}
|
|
1039
1070
|
};
|
|
1040
1071
|
}
|
|
1041
1072
|
return {
|
|
1042
|
-
type:
|
|
1073
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1043
1074
|
requestId: t,
|
|
1044
|
-
response:
|
|
1075
|
+
response: o
|
|
1045
1076
|
};
|
|
1046
|
-
} catch (
|
|
1047
|
-
return this.logger.error("[BodhiExtClient] Unexpected error:",
|
|
1048
|
-
type:
|
|
1077
|
+
} catch (o) {
|
|
1078
|
+
return this.logger.error("[BodhiExtClient] Unexpected error:", o), {
|
|
1079
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1049
1080
|
requestId: t,
|
|
1050
1081
|
response: {
|
|
1051
1082
|
error: {
|
|
1052
|
-
message:
|
|
1083
|
+
message: o instanceof Error ? o.message : `Unexpected error: ${JSON.stringify(o)}`
|
|
1053
1084
|
}
|
|
1054
1085
|
}
|
|
1055
1086
|
};
|
|
@@ -1058,14 +1089,14 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1058
1089
|
// Implementation signature (must be compatible with all overloads)
|
|
1059
1090
|
async handleAction(e) {
|
|
1060
1091
|
switch (e.type) {
|
|
1061
|
-
case
|
|
1092
|
+
case c.EXT2EXT_CLIENT_API_REQUEST:
|
|
1062
1093
|
return this.handleApiRequest(e);
|
|
1063
|
-
case
|
|
1094
|
+
case c.EXT2EXT_CLIENT_REQUEST:
|
|
1064
1095
|
return this.handleExtClientRequest(e);
|
|
1065
1096
|
default: {
|
|
1066
1097
|
const { requestId: t } = e;
|
|
1067
1098
|
return this.logger.error("[BodhiExtClient] Unknown message type:", e.type), {
|
|
1068
|
-
type:
|
|
1099
|
+
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1069
1100
|
requestId: t,
|
|
1070
1101
|
response: {
|
|
1071
1102
|
error: {
|
|
@@ -1082,58 +1113,65 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1082
1113
|
// ============================================================================
|
|
1083
1114
|
/**
|
|
1084
1115
|
* Login user via OAuth2 + PKCE flow
|
|
1116
|
+
* @param options - Optional login options (toolsetScopeIds, version)
|
|
1085
1117
|
* @throws Error if login fails
|
|
1086
1118
|
*/
|
|
1087
|
-
async login() {
|
|
1119
|
+
async login(e) {
|
|
1088
1120
|
if (!(this.isAuthenticating || (await this.getAuthState()).status === "authenticated")) {
|
|
1089
1121
|
this.isAuthenticating = !0;
|
|
1090
1122
|
try {
|
|
1091
1123
|
if (!this.extensionId)
|
|
1092
1124
|
throw new Error("Extension not discovered. Please detect Bodhi extension before login.");
|
|
1093
|
-
const
|
|
1094
|
-
if (
|
|
1095
|
-
throw
|
|
1096
|
-
if (
|
|
1097
|
-
const { message:
|
|
1098
|
-
throw
|
|
1125
|
+
const r = await this.requestAccess(e?.toolsetScopeIds, e?.version);
|
|
1126
|
+
if (v(r))
|
|
1127
|
+
throw d(r.error.message, r.error.type);
|
|
1128
|
+
if (M(r)) {
|
|
1129
|
+
const { message: T } = r.body.error;
|
|
1130
|
+
throw d(T, "auth_error");
|
|
1099
1131
|
}
|
|
1100
|
-
if (!
|
|
1101
|
-
throw
|
|
1102
|
-
const
|
|
1132
|
+
if (!O(r))
|
|
1133
|
+
throw d(`Unexpected HTTP ${r.status}`, "auth_error");
|
|
1134
|
+
const i = r.body.scope, s = r.body.toolsets || [], o = D(e?.toolsetScopeIds, s);
|
|
1135
|
+
if (o.length > 0)
|
|
1136
|
+
throw d(
|
|
1137
|
+
`toolsetScopeIds not received back from request-access call: [${o.join(", ")}], check developer console on configuring the toolset scopes correctly`,
|
|
1138
|
+
"auth_error"
|
|
1139
|
+
);
|
|
1140
|
+
const n = q(e?.toolsetScopeIds, s), E = `openid profile email roles ${this.userScope} ${i} ${n}`.trim(), h = y.generateCodeVerifier(), l = await y.generateCodeChallenge(h), u = y.generateCodeVerifier();
|
|
1103
1141
|
await chrome.storage.session.set({
|
|
1104
|
-
codeVerifier:
|
|
1105
|
-
state:
|
|
1142
|
+
codeVerifier: h,
|
|
1143
|
+
state: u,
|
|
1106
1144
|
authInProgress: !0
|
|
1107
1145
|
});
|
|
1108
|
-
const
|
|
1109
|
-
return
|
|
1146
|
+
const p = chrome.identity.getRedirectURL("callback"), g = new URL(this.authEndpoints.authorize);
|
|
1147
|
+
return g.searchParams.set("client_id", this.authClientId), g.searchParams.set("response_type", "code"), g.searchParams.set("redirect_uri", p), g.searchParams.set("scope", E), g.searchParams.set("code_challenge", l), g.searchParams.set("code_challenge_method", "S256"), g.searchParams.set("state", u), new Promise((T, _) => {
|
|
1110
1148
|
chrome.identity.launchWebAuthFlow(
|
|
1111
1149
|
{
|
|
1112
|
-
url:
|
|
1150
|
+
url: g.toString(),
|
|
1113
1151
|
interactive: !0
|
|
1114
1152
|
},
|
|
1115
|
-
async (
|
|
1153
|
+
async (R) => {
|
|
1116
1154
|
if (await chrome.storage.session.set({ authInProgress: !1 }), chrome.runtime.lastError) {
|
|
1117
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]),
|
|
1155
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), _(chrome.runtime.lastError);
|
|
1118
1156
|
return;
|
|
1119
1157
|
}
|
|
1120
|
-
if (!
|
|
1121
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]),
|
|
1158
|
+
if (!R) {
|
|
1159
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), _(new Error("No redirect URL received"));
|
|
1122
1160
|
return;
|
|
1123
1161
|
}
|
|
1124
1162
|
try {
|
|
1125
|
-
const
|
|
1126
|
-
if (
|
|
1127
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]),
|
|
1163
|
+
const I = new URL(R), f = I.searchParams.get("code"), x = I.searchParams.get("state"), { state: X } = await chrome.storage.session.get("state");
|
|
1164
|
+
if (x !== X) {
|
|
1165
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), _(new Error("State mismatch - possible CSRF"));
|
|
1128
1166
|
return;
|
|
1129
1167
|
}
|
|
1130
|
-
if (!
|
|
1131
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]),
|
|
1168
|
+
if (!f) {
|
|
1169
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), _(new Error("No authorization code received"));
|
|
1132
1170
|
return;
|
|
1133
1171
|
}
|
|
1134
|
-
await this.exchangeCodeForTokens(
|
|
1135
|
-
} catch (
|
|
1136
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]),
|
|
1172
|
+
await this.exchangeCodeForTokens(f), await chrome.storage.session.remove(["codeVerifier", "state"]), T();
|
|
1173
|
+
} catch (I) {
|
|
1174
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), _(I);
|
|
1137
1175
|
}
|
|
1138
1176
|
}
|
|
1139
1177
|
);
|
|
@@ -1150,7 +1188,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1150
1188
|
async exchangeCodeForTokens(e) {
|
|
1151
1189
|
if ((await this.getAuthState()).status === "authenticated")
|
|
1152
1190
|
return;
|
|
1153
|
-
const { codeVerifier: r } = await chrome.storage.session.get("codeVerifier"),
|
|
1191
|
+
const { codeVerifier: r } = await chrome.storage.session.get("codeVerifier"), i = chrome.identity.getRedirectURL("callback"), s = await fetch(this.authEndpoints.token, {
|
|
1154
1192
|
method: "POST",
|
|
1155
1193
|
headers: {
|
|
1156
1194
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
@@ -1158,21 +1196,21 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1158
1196
|
body: new URLSearchParams({
|
|
1159
1197
|
grant_type: "authorization_code",
|
|
1160
1198
|
code: e,
|
|
1161
|
-
redirect_uri:
|
|
1199
|
+
redirect_uri: i,
|
|
1162
1200
|
client_id: this.authClientId,
|
|
1163
1201
|
code_verifier: r
|
|
1164
1202
|
})
|
|
1165
1203
|
});
|
|
1166
1204
|
if (!s.ok) {
|
|
1167
|
-
const
|
|
1168
|
-
throw new Error(`Token exchange failed: ${s.status} ${
|
|
1205
|
+
const n = await s.text();
|
|
1206
|
+
throw new Error(`Token exchange failed: ${s.status} ${n}`);
|
|
1169
1207
|
}
|
|
1170
|
-
const
|
|
1208
|
+
const o = await s.json();
|
|
1171
1209
|
await this.storeTokens({
|
|
1172
|
-
accessToken:
|
|
1173
|
-
refreshToken:
|
|
1174
|
-
idToken:
|
|
1175
|
-
expiresIn:
|
|
1210
|
+
accessToken: o.access_token,
|
|
1211
|
+
refreshToken: o.refresh_token,
|
|
1212
|
+
idToken: o.id_token,
|
|
1213
|
+
expiresIn: o.expires_in
|
|
1176
1214
|
});
|
|
1177
1215
|
}
|
|
1178
1216
|
/**
|
|
@@ -1236,7 +1274,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1236
1274
|
*/
|
|
1237
1275
|
async sendExtRequest(e, t) {
|
|
1238
1276
|
const r = await this.sendExtRequestRaw(e, t);
|
|
1239
|
-
if (
|
|
1277
|
+
if (N(r.response))
|
|
1240
1278
|
throw this.logger.error("[BodhiExtClient] Extension error:", r.response.error), new Error(
|
|
1241
1279
|
r.response.error.message || `Extension request failed: ${JSON.stringify(r.response)}`
|
|
1242
1280
|
);
|
|
@@ -1250,44 +1288,44 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1250
1288
|
* @param headers Optional headers
|
|
1251
1289
|
* @returns API response from LLM server via bodhi-browser-ext
|
|
1252
1290
|
*/
|
|
1253
|
-
async sendApiRequest(e, t, r,
|
|
1291
|
+
async sendApiRequest(e, t, r, i) {
|
|
1254
1292
|
if (!this.extensionId)
|
|
1255
1293
|
throw new Error(this.createErrorClientNotInitialized({ type: "api", method: e, endpoint: t }));
|
|
1256
1294
|
this.logger.debug(
|
|
1257
1295
|
`[BodhiExtClient] Sending API_REQUEST: method=${e}, endpoint=${t}`,
|
|
1258
1296
|
r ? { body: r } : ""
|
|
1259
1297
|
);
|
|
1260
|
-
const s = crypto.randomUUID(),
|
|
1261
|
-
type:
|
|
1298
|
+
const s = crypto.randomUUID(), o = {
|
|
1299
|
+
type: S.API_REQUEST,
|
|
1262
1300
|
requestId: s,
|
|
1263
1301
|
request: {
|
|
1264
1302
|
method: e,
|
|
1265
1303
|
endpoint: t,
|
|
1266
1304
|
body: r,
|
|
1267
|
-
headers:
|
|
1305
|
+
headers: i
|
|
1268
1306
|
}
|
|
1269
1307
|
};
|
|
1270
|
-
return this.logger.debug(`[BodhiExtClient] Request ID: ${s}, Extension: ${this.extensionId}`), new Promise((
|
|
1308
|
+
return this.logger.debug(`[BodhiExtClient] Request ID: ${s}, Extension: ${this.extensionId}`), new Promise((n, E) => {
|
|
1271
1309
|
try {
|
|
1272
|
-
chrome.runtime.sendMessage(this.extensionId,
|
|
1310
|
+
chrome.runtime.sendMessage(this.extensionId, o, (h) => {
|
|
1273
1311
|
if (chrome.runtime.lastError) {
|
|
1274
1312
|
this.logger.error(
|
|
1275
1313
|
`[BodhiExtClient] Chrome runtime error for request ${s}:`,
|
|
1276
1314
|
chrome.runtime.lastError
|
|
1277
|
-
),
|
|
1315
|
+
), E(new Error(chrome.runtime.lastError.message));
|
|
1278
1316
|
return;
|
|
1279
1317
|
}
|
|
1280
|
-
if (this.logger.debug(`[BodhiExtClient] Response for request ${s}:`,
|
|
1281
|
-
this.logger.error(`[BodhiExtClient] No response received for request ${s}`),
|
|
1318
|
+
if (this.logger.debug(`[BodhiExtClient] Response for request ${s}:`, h), !h) {
|
|
1319
|
+
this.logger.error(`[BodhiExtClient] No response received for request ${s}`), E(new Error("No response from extension"));
|
|
1282
1320
|
return;
|
|
1283
1321
|
}
|
|
1284
|
-
|
|
1322
|
+
h.type === S.API_RESPONSE && h.requestId === s ? "error" in h ? (this.logger.error(`[BodhiExtClient] API error for ${s}:`, h.error), E(new Error(h.error.message))) : (this.logger.debug(`[BodhiExtClient] ✓ Valid API_RESPONSE for ${s}`), n(h.response)) : (this.logger.error(
|
|
1285
1323
|
`[BodhiExtClient] Invalid response format for ${s}:`,
|
|
1286
|
-
|
|
1287
|
-
),
|
|
1324
|
+
h
|
|
1325
|
+
), E(new Error("Invalid response format")));
|
|
1288
1326
|
});
|
|
1289
|
-
} catch (
|
|
1290
|
-
this.logger.error(`[BodhiExtClient] Exception sending message for ${s}:`,
|
|
1327
|
+
} catch (h) {
|
|
1328
|
+
this.logger.error(`[BodhiExtClient] Exception sending message for ${s}:`, h), E(h);
|
|
1291
1329
|
}
|
|
1292
1330
|
});
|
|
1293
1331
|
}
|
|
@@ -1304,35 +1342,35 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1304
1342
|
`[BodhiExtClient] Sending EXT_REQUEST (raw): action=${e}`,
|
|
1305
1343
|
t ? { params: t } : ""
|
|
1306
1344
|
);
|
|
1307
|
-
const r = crypto.randomUUID(),
|
|
1308
|
-
type:
|
|
1345
|
+
const r = crypto.randomUUID(), i = {
|
|
1346
|
+
type: S.EXT_REQUEST,
|
|
1309
1347
|
requestId: r,
|
|
1310
1348
|
request: {
|
|
1311
1349
|
action: e,
|
|
1312
1350
|
params: t
|
|
1313
1351
|
}
|
|
1314
1352
|
};
|
|
1315
|
-
return this.logger.debug(`[BodhiExtClient] Request ID: ${r}, Extension: ${this.extensionId}`), new Promise((s,
|
|
1353
|
+
return this.logger.debug(`[BodhiExtClient] Request ID: ${r}, Extension: ${this.extensionId}`), new Promise((s, o) => {
|
|
1316
1354
|
try {
|
|
1317
|
-
chrome.runtime.sendMessage(this.extensionId,
|
|
1355
|
+
chrome.runtime.sendMessage(this.extensionId, i, (n) => {
|
|
1318
1356
|
if (chrome.runtime.lastError) {
|
|
1319
1357
|
this.logger.error(
|
|
1320
1358
|
`[BodhiExtClient] Chrome runtime error for request ${r}:`,
|
|
1321
1359
|
chrome.runtime.lastError
|
|
1322
|
-
),
|
|
1360
|
+
), o(new Error(chrome.runtime.lastError.message));
|
|
1323
1361
|
return;
|
|
1324
1362
|
}
|
|
1325
|
-
if (this.logger.debug(`[BodhiExtClient] Response for request ${r}:`,
|
|
1326
|
-
this.logger.error(`[BodhiExtClient] No response received for request ${r}`),
|
|
1363
|
+
if (this.logger.debug(`[BodhiExtClient] Response for request ${r}:`, n), !n) {
|
|
1364
|
+
this.logger.error(`[BodhiExtClient] No response received for request ${r}`), o(new Error("No response from extension"));
|
|
1327
1365
|
return;
|
|
1328
1366
|
}
|
|
1329
|
-
|
|
1367
|
+
n.type === S.EXT_RESPONSE && n.requestId === r ? (this.logger.debug(`[BodhiExtClient] ✓ Valid EXT_RESPONSE for ${r}`), s(n)) : (this.logger.error(
|
|
1330
1368
|
`[BodhiExtClient] Invalid response format for ${r}:`,
|
|
1331
|
-
|
|
1332
|
-
),
|
|
1369
|
+
n
|
|
1370
|
+
), o(new Error("Invalid response format")));
|
|
1333
1371
|
});
|
|
1334
|
-
} catch (
|
|
1335
|
-
this.logger.error(`[BodhiExtClient] Exception sending message for ${r}:`,
|
|
1372
|
+
} catch (n) {
|
|
1373
|
+
this.logger.error(`[BodhiExtClient] Exception sending message for ${r}:`, n), o(n);
|
|
1336
1374
|
}
|
|
1337
1375
|
});
|
|
1338
1376
|
}
|
|
@@ -1343,14 +1381,14 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1343
1381
|
* @param message Stream request message from UI
|
|
1344
1382
|
*/
|
|
1345
1383
|
async handleStreamRequest(e, t) {
|
|
1346
|
-
const { requestId: r, request:
|
|
1384
|
+
const { requestId: r, request: i } = t, { method: s, endpoint: o, body: n, headers: E, authenticated: h } = i;
|
|
1347
1385
|
this.logger.debug("[BodhiExtClient] Processing stream request:", {
|
|
1348
1386
|
requestId: r,
|
|
1349
1387
|
method: s,
|
|
1350
|
-
endpoint:
|
|
1351
|
-
authenticated:
|
|
1388
|
+
endpoint: o,
|
|
1389
|
+
authenticated: h
|
|
1352
1390
|
}), this.extensionId || e.postMessage({
|
|
1353
|
-
type:
|
|
1391
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1354
1392
|
requestId: r,
|
|
1355
1393
|
error: {
|
|
1356
1394
|
message: this.createErrorClientNotInitialized(t),
|
|
@@ -1358,12 +1396,12 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1358
1396
|
}
|
|
1359
1397
|
});
|
|
1360
1398
|
try {
|
|
1361
|
-
let
|
|
1362
|
-
if (
|
|
1399
|
+
let l = { ...E };
|
|
1400
|
+
if (h !== !1) {
|
|
1363
1401
|
const T = await this._getAccessTokenRaw();
|
|
1364
1402
|
if (!T) {
|
|
1365
1403
|
e.postMessage({
|
|
1366
|
-
type:
|
|
1404
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1367
1405
|
requestId: r,
|
|
1368
1406
|
error: {
|
|
1369
1407
|
message: "Not authenticated. Please log in first.",
|
|
@@ -1372,60 +1410,60 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1372
1410
|
});
|
|
1373
1411
|
return;
|
|
1374
1412
|
}
|
|
1375
|
-
|
|
1376
|
-
...
|
|
1413
|
+
l = {
|
|
1414
|
+
...l,
|
|
1377
1415
|
Authorization: `Bearer ${T}`
|
|
1378
1416
|
}, this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request");
|
|
1379
1417
|
}
|
|
1380
|
-
const
|
|
1381
|
-
name:
|
|
1418
|
+
const u = chrome.runtime.connect(this.extensionId, {
|
|
1419
|
+
name: pe
|
|
1382
1420
|
});
|
|
1383
|
-
this.activeStreamPorts.set(r,
|
|
1384
|
-
const
|
|
1421
|
+
this.activeStreamPorts.set(r, u);
|
|
1422
|
+
const p = setTimeout(() => {
|
|
1385
1423
|
this.activeStreamPorts.has(r) && (this.logger.error(`[BodhiExtClient] Stream timeout for ${r}`), e.postMessage({
|
|
1386
|
-
type:
|
|
1424
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1387
1425
|
requestId: r,
|
|
1388
1426
|
error: {
|
|
1389
1427
|
message: "Stream request timed out",
|
|
1390
1428
|
type: "timeout_error"
|
|
1391
1429
|
}
|
|
1392
1430
|
}), this.cleanupStreamPort(r));
|
|
1393
|
-
},
|
|
1394
|
-
|
|
1395
|
-
if (
|
|
1396
|
-
const
|
|
1397
|
-
|
|
1398
|
-
type:
|
|
1431
|
+
}, y.STREAM_TIMEOUT);
|
|
1432
|
+
u.onMessage.addListener((T) => {
|
|
1433
|
+
if (de(T)) {
|
|
1434
|
+
const _ = T.response, R = _.body;
|
|
1435
|
+
_.status >= 400 ? e.postMessage({
|
|
1436
|
+
type: c.EXT2EXT_CLIENT_STREAM_API_ERROR,
|
|
1399
1437
|
requestId: r,
|
|
1400
|
-
response:
|
|
1401
|
-
}) :
|
|
1402
|
-
type:
|
|
1438
|
+
response: _
|
|
1439
|
+
}) : R?.done ? (e.postMessage({
|
|
1440
|
+
type: c.EXT2EXT_CLIENT_STREAM_DONE,
|
|
1403
1441
|
requestId: r
|
|
1404
|
-
}), this.logger.info(`[BodhiExtClient] Stream complete for ${r}`), clearTimeout(
|
|
1405
|
-
type:
|
|
1442
|
+
}), this.logger.info(`[BodhiExtClient] Stream complete for ${r}`), clearTimeout(p), this.cleanupStreamPort(r)) : e.postMessage({
|
|
1443
|
+
type: c.EXT2EXT_CLIENT_STREAM_CHUNK,
|
|
1406
1444
|
requestId: r,
|
|
1407
|
-
response:
|
|
1445
|
+
response: _
|
|
1408
1446
|
});
|
|
1409
|
-
} else
|
|
1447
|
+
} else le(T) ? (this.logger.error(
|
|
1410
1448
|
`[BodhiExtClient] Stream API error for ${r}: ${T.response.status}`
|
|
1411
1449
|
), e.postMessage({
|
|
1412
|
-
type:
|
|
1450
|
+
type: c.EXT2EXT_CLIENT_STREAM_API_ERROR,
|
|
1413
1451
|
requestId: r,
|
|
1414
1452
|
response: T.response
|
|
1415
|
-
})) :
|
|
1453
|
+
})) : ge(T) && (this.logger.error(
|
|
1416
1454
|
`[BodhiExtClient] Stream error for ${r}:`,
|
|
1417
1455
|
T.error.message
|
|
1418
1456
|
), e.postMessage({
|
|
1419
|
-
type:
|
|
1457
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1420
1458
|
requestId: r,
|
|
1421
1459
|
error: {
|
|
1422
1460
|
message: `stream error: ${JSON.stringify(T)}`,
|
|
1423
1461
|
type: "extension_error"
|
|
1424
1462
|
}
|
|
1425
|
-
}), clearTimeout(
|
|
1426
|
-
}),
|
|
1427
|
-
clearTimeout(
|
|
1428
|
-
type:
|
|
1463
|
+
}), clearTimeout(p), this.cleanupStreamPort(r));
|
|
1464
|
+
}), u.onDisconnect.addListener(() => {
|
|
1465
|
+
clearTimeout(p), this.activeStreamPorts.has(r) && (this.logger.error(`[BodhiExtClient] Bodhi port disconnected for ${r}`), e.postMessage({
|
|
1466
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1429
1467
|
requestId: r,
|
|
1430
1468
|
error: {
|
|
1431
1469
|
message: "Connection to Bodhi extension closed unexpectedly",
|
|
@@ -1433,24 +1471,24 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1433
1471
|
}
|
|
1434
1472
|
}), this.activeStreamPorts.delete(r));
|
|
1435
1473
|
});
|
|
1436
|
-
const
|
|
1437
|
-
type:
|
|
1474
|
+
const g = {
|
|
1475
|
+
type: S.STREAM_REQUEST,
|
|
1438
1476
|
requestId: r,
|
|
1439
1477
|
request: {
|
|
1440
1478
|
method: s,
|
|
1441
|
-
endpoint:
|
|
1442
|
-
body:
|
|
1443
|
-
headers:
|
|
1479
|
+
endpoint: o,
|
|
1480
|
+
body: n,
|
|
1481
|
+
headers: l
|
|
1444
1482
|
}
|
|
1445
1483
|
};
|
|
1446
|
-
this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:",
|
|
1447
|
-
} catch (
|
|
1448
|
-
const
|
|
1449
|
-
this.logger.error("[BodhiExtClient] Stream error:", JSON.stringify(
|
|
1450
|
-
type:
|
|
1484
|
+
this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:", g), u.postMessage(g);
|
|
1485
|
+
} catch (l) {
|
|
1486
|
+
const u = l;
|
|
1487
|
+
this.logger.error("[BodhiExtClient] Stream error:", JSON.stringify(u.message)), e.postMessage({
|
|
1488
|
+
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1451
1489
|
requestId: r,
|
|
1452
1490
|
error: {
|
|
1453
|
-
message: `uncaught error: ${JSON.stringify({ error:
|
|
1491
|
+
message: `uncaught error: ${JSON.stringify({ error: u, message: u.message })}`,
|
|
1454
1492
|
type: "extension_error"
|
|
1455
1493
|
}
|
|
1456
1494
|
});
|
|
@@ -1477,11 +1515,16 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1477
1515
|
* Required for authenticated API access - token will include aud claim.
|
|
1478
1516
|
* @returns ApiResponseResult with scope or error
|
|
1479
1517
|
*/
|
|
1480
|
-
async requestAccess() {
|
|
1518
|
+
async requestAccess(e, t) {
|
|
1519
|
+
const r = {
|
|
1520
|
+
app_client_id: this.authClientId,
|
|
1521
|
+
...e && { toolset_scope_ids: e },
|
|
1522
|
+
...t && { version: t }
|
|
1523
|
+
};
|
|
1481
1524
|
return this.sendApiRequest(
|
|
1482
1525
|
"POST",
|
|
1483
1526
|
"/bodhi/v1/apps/request-access",
|
|
1484
|
-
|
|
1527
|
+
r
|
|
1485
1528
|
);
|
|
1486
1529
|
}
|
|
1487
1530
|
// ============================================================================
|
|
@@ -1529,7 +1572,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1529
1572
|
async _doRefreshToken(e) {
|
|
1530
1573
|
this.logger.debug("Refreshing access token");
|
|
1531
1574
|
try {
|
|
1532
|
-
const t = await
|
|
1575
|
+
const t = await te(
|
|
1533
1576
|
this.authEndpoints.token,
|
|
1534
1577
|
e,
|
|
1535
1578
|
this.authClientId
|
|
@@ -1541,7 +1584,7 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1541
1584
|
} catch (t) {
|
|
1542
1585
|
this.logger.warn("Token refresh failed:", t);
|
|
1543
1586
|
}
|
|
1544
|
-
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),
|
|
1587
|
+
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"), d(
|
|
1545
1588
|
"Access token expired and unable to refresh. Try logging out and logging in again.",
|
|
1546
1589
|
"token_refresh_failed"
|
|
1547
1590
|
);
|
|
@@ -1569,28 +1612,29 @@ const de = ["ggedphdcbekjlomjaidbajglgihbeaon"], le = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1569
1612
|
]);
|
|
1570
1613
|
}
|
|
1571
1614
|
parseJwt(e) {
|
|
1572
|
-
const r = e.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"),
|
|
1615
|
+
const r = e.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"), i = decodeURIComponent(
|
|
1573
1616
|
atob(r).split("").map((s) => "%" + ("00" + s.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
1574
1617
|
);
|
|
1575
|
-
return JSON.parse(
|
|
1618
|
+
return JSON.parse(i);
|
|
1576
1619
|
}
|
|
1577
1620
|
createErrorClientNotInitialized(e) {
|
|
1578
1621
|
return `Client not initialized. Extension discovery not triggered nor extensionId set, cannot handle request: ${JSON.stringify(e)}`;
|
|
1579
1622
|
}
|
|
1580
1623
|
};
|
|
1581
|
-
|
|
1582
|
-
let
|
|
1583
|
-
const
|
|
1624
|
+
y.STREAM_TIMEOUT = 6e4;
|
|
1625
|
+
let k = y;
|
|
1626
|
+
const fe = "production";
|
|
1584
1627
|
export {
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1628
|
+
k as BodhiExtClient,
|
|
1629
|
+
ne as DEFAULT_API_TIMEOUT_MS,
|
|
1630
|
+
se as DISCOVERY_ATTEMPTS,
|
|
1631
|
+
ie as DISCOVERY_ATTEMPT_TIMEOUT,
|
|
1632
|
+
oe as DISCOVERY_ATTEMPT_WAIT_MS,
|
|
1633
|
+
re as DISCOVERY_TIMEOUT_MS,
|
|
1634
|
+
m as EXT2EXT_CLIENT_ACTIONS,
|
|
1635
|
+
c as EXT2EXT_CLIENT_MESSAGE_TYPES,
|
|
1636
|
+
$ as EXT2EXT_CLIENT_STREAM_PORT,
|
|
1637
|
+
fe as EXT_BUILD_MODE,
|
|
1638
|
+
Re as ExtUIClient,
|
|
1639
|
+
ae as isExtClientApiError
|
|
1596
1640
|
};
|