@opengeni/api-router 0.26.1 → 0.30.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/dist/app.d.ts +1 -0
- package/dist/app.js +5 -3
- package/dist/auth/canonical-human-session-admission.d.ts +27 -0
- package/dist/browser-auth-broker.d.ts +20 -0
- package/dist/browser-controller-authority.d.ts +19 -1
- package/dist/browser-network-route.d.ts +8 -0
- package/dist/{chunk-JIKNR5YL.js → chunk-2PQMSRCB.js} +15518 -6863
- package/dist/chunk-2PQMSRCB.js.map +1 -0
- package/dist/controller-data-plane.d.ts +12 -0
- package/dist/http/auth.d.ts +1 -1
- package/dist/http/sse.d.ts +18 -1
- package/dist/index.js +1 -1
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/fiken.d.ts +233 -0
- package/dist/integrations/google-drive.d.ts +6 -6
- package/dist/integrations/provider-oauth.d.ts +0 -2
- package/dist/integrations/slack-bot.d.ts +171 -1
- package/dist/integrations/slack-interactions.d.ts +17 -5
- package/dist/interaction-holder-heartbeat.d.ts +17 -0
- package/dist/interaction-metrics.d.ts +11 -0
- package/dist/mcp/server.d.ts +3 -2
- package/dist/model-catalog.d.ts +1 -0
- package/dist/routes/canonical-human-identities.d.ts +3 -0
- package/dist/routes/channels.d.ts +3 -0
- package/dist/routes/integration-facets.d.ts +3 -0
- package/dist/routes/interaction-resources.d.ts +5 -0
- package/dist/routes/organization-memberships.d.ts +3 -0
- package/dist/routes/sessions.d.ts +2 -1
- package/dist/routes/slack-task-policy.d.ts +3 -0
- package/dist/routes/supergrok.d.ts +3 -0
- package/dist/sandbox/channel-a.d.ts +8 -1
- package/dist/sandbox/machines.d.ts +2 -2
- package/dist/sandbox/viewer.d.ts +3 -1
- package/dist/slack-reaction-files.d.ts +27 -0
- package/dist/transcription/providers/xai-subscription.d.ts +8 -0
- package/dist/xai-realtime.d.ts +26 -0
- package/dist/xai-subscription-auth.d.ts +23 -0
- package/package.json +17 -16
- package/src/app.ts +352 -49
- package/src/auth/canonical-human-session-admission.ts +116 -0
- package/src/auth/managed-auth.ts +101 -0
- package/src/browser-auth-broker.ts +155 -0
- package/src/browser-controller-authority.ts +50 -1
- package/src/browser-network-route.ts +34 -0
- package/src/controller-data-plane.ts +35 -0
- package/src/http/auth.ts +27 -9
- package/src/http/sse.ts +229 -5
- package/src/integrations/api-integrations.ts +57 -42
- package/src/integrations/fiken.ts +1230 -0
- package/src/integrations/google-drive.ts +31 -26
- package/src/integrations/provider-oauth.ts +70 -91
- package/src/integrations/slack-bot.ts +638 -9
- package/src/integrations/slack-interactions.ts +1583 -73
- package/src/interaction-holder-heartbeat.ts +95 -0
- package/src/interaction-metrics.ts +159 -0
- package/src/mcp/documents.ts +115 -6
- package/src/mcp/server.ts +865 -242
- package/src/model-catalog.ts +19 -6
- package/src/routes/api-integrations.ts +32 -29
- package/src/routes/browser-identities.ts +24 -0
- package/src/routes/browser-sessions.ts +2122 -183
- package/src/routes/canonical-human-identities.ts +156 -0
- package/src/routes/channels.ts +90 -0
- package/src/routes/computer-sessions.ts +441 -86
- package/src/routes/connections.ts +141 -4
- package/src/routes/{integration-features.ts → integration-facets.ts} +63 -63
- package/src/routes/interaction-resources.ts +595 -0
- package/src/routes/organization-memberships.ts +53 -0
- package/src/routes/plugins.ts +2 -1
- package/src/routes/sessions.ts +419 -43
- package/src/routes/skills.ts +92 -10
- package/src/routes/slack-task-policy.ts +115 -0
- package/src/routes/supergrok.ts +717 -0
- package/src/routes/transcription-recordings.ts +9 -2
- package/src/routes/transcriptions.ts +2 -1
- package/src/routes/video-generation.ts +34 -4
- package/src/routes/workspaces.ts +22 -4
- package/src/sandbox/channel-a.ts +29 -2
- package/src/sandbox/machines.ts +5 -5
- package/src/sandbox/viewer.ts +10 -3
- package/src/slack-reaction-files.ts +181 -0
- package/src/transcription/providers/xai-subscription.ts +110 -0
- package/src/transcription/service.ts +17 -2
- package/src/xai-realtime.ts +216 -0
- package/src/xai-subscription-auth.ts +132 -0
- package/dist/chunk-JIKNR5YL.js.map +0 -1
- package/dist/routes/integration-features.d.ts +0 -3
|
@@ -14,6 +14,7 @@ import type { Database } from "@opengeni/db";
|
|
|
14
14
|
import { createAzureOpenAiTranscriptionProvider } from "./providers/azure-openai";
|
|
15
15
|
import { createCodexSubscriptionTranscriptionProvider } from "./providers/codex-subscription";
|
|
16
16
|
import { createOpenAiTranscriptionProvider } from "./providers/openai";
|
|
17
|
+
import { createXaiSubscriptionTranscriptionProvider } from "./providers/xai-subscription";
|
|
17
18
|
|
|
18
19
|
export function createTranscriptionService(input: {
|
|
19
20
|
settings: Settings;
|
|
@@ -46,6 +47,12 @@ export function createTranscriptionService(input: {
|
|
|
46
47
|
...(input.codexFetch ? { fetch: input.codexFetch } : {}),
|
|
47
48
|
...(input.probeCodex ? { probe: input.probeCodex } : {}),
|
|
48
49
|
});
|
|
50
|
+
case "supergrok-subscription":
|
|
51
|
+
return createXaiSubscriptionTranscriptionProvider({
|
|
52
|
+
settings: input.settings,
|
|
53
|
+
db: input.db,
|
|
54
|
+
...(input.fetch ? { fetch: input.fetch } : {}),
|
|
55
|
+
});
|
|
49
56
|
default:
|
|
50
57
|
throw new Error("Unsupported voice-input provider.");
|
|
51
58
|
}
|
|
@@ -95,8 +102,14 @@ export function createTranscriptionService(input: {
|
|
|
95
102
|
});
|
|
96
103
|
}
|
|
97
104
|
const provider = request.providerId
|
|
98
|
-
? await exactAvailable(providers, request.providerId, {
|
|
99
|
-
|
|
105
|
+
? await exactAvailable(providers, request.providerId, {
|
|
106
|
+
workspaceId: request.workspaceId,
|
|
107
|
+
subjectId: request.subjectId,
|
|
108
|
+
})
|
|
109
|
+
: await firstAvailable(providers, {
|
|
110
|
+
workspaceId: request.workspaceId,
|
|
111
|
+
subjectId: request.subjectId,
|
|
112
|
+
});
|
|
100
113
|
if (!provider) {
|
|
101
114
|
throw new TranscriptionServiceError({
|
|
102
115
|
code: "unavailable",
|
|
@@ -131,6 +144,8 @@ export function createTranscriptionService(input: {
|
|
|
131
144
|
mimeType,
|
|
132
145
|
filename: filenameForMimeType(mimeType),
|
|
133
146
|
workspaceId: request.workspaceId,
|
|
147
|
+
accountId: request.accountId,
|
|
148
|
+
subjectId: request.subjectId,
|
|
134
149
|
requestId: request.requestId,
|
|
135
150
|
signal: deadline.signal,
|
|
136
151
|
});
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { SUPERGROK_REALTIME_MODEL_ID, type Settings } from "@opengeni/config";
|
|
2
|
+
import type { GatewayRealtimeInitialItem } from "@opengeni/contracts";
|
|
3
|
+
import {
|
|
4
|
+
getActiveSessionHistoryItems,
|
|
5
|
+
getSessionRealtimeContinuityEntries,
|
|
6
|
+
getXaiSessionAccountPin,
|
|
7
|
+
resolveXaiProviderAccountAuthoritySnapshotForAcceptance,
|
|
8
|
+
setXaiSessionAccountPin,
|
|
9
|
+
type Database,
|
|
10
|
+
} from "@opengeni/db";
|
|
11
|
+
import {
|
|
12
|
+
XAI_CLIENT_MODE,
|
|
13
|
+
XAI_CLIENT_VERSION,
|
|
14
|
+
XAI_PUBLIC_API_BASE_URL,
|
|
15
|
+
type XaiFetch,
|
|
16
|
+
} from "@opengeni/xai-subscription";
|
|
17
|
+
|
|
18
|
+
import { openGeniRealtimeInstructions } from "./codex-realtime";
|
|
19
|
+
import { projectSessionRealtimeInitialItems } from "./session-realtime-context";
|
|
20
|
+
import { buildXaiSubscriptionAuthorization } from "./xai-subscription-auth";
|
|
21
|
+
|
|
22
|
+
const UPSTREAM_MODEL_ID = "grok-voice-think-fast-2.0";
|
|
23
|
+
|
|
24
|
+
export class XaiRealtimeBrokerError extends Error {
|
|
25
|
+
constructor(
|
|
26
|
+
readonly code:
|
|
27
|
+
| "model_unavailable"
|
|
28
|
+
| "credential_unavailable"
|
|
29
|
+
| "provider_error"
|
|
30
|
+
| "invalid_provider_response",
|
|
31
|
+
message: string,
|
|
32
|
+
readonly providerStatus: number | null = null,
|
|
33
|
+
) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "XaiRealtimeBrokerError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type XaiRealtimeConnectionSecret = {
|
|
40
|
+
token: string;
|
|
41
|
+
url: string;
|
|
42
|
+
upstreamModelId: string;
|
|
43
|
+
expiresAt: number | null;
|
|
44
|
+
initialItems: GatewayRealtimeInitialItem[];
|
|
45
|
+
instructions: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export async function createXaiRealtimeConnectionSecret(input: {
|
|
49
|
+
db: Database;
|
|
50
|
+
settings: Settings;
|
|
51
|
+
accountId: string;
|
|
52
|
+
workspaceId: string;
|
|
53
|
+
subjectId: string;
|
|
54
|
+
sessionId: string;
|
|
55
|
+
model: string;
|
|
56
|
+
fetchImpl?: typeof fetch;
|
|
57
|
+
}): Promise<XaiRealtimeConnectionSecret> {
|
|
58
|
+
if (input.model !== SUPERGROK_REALTIME_MODEL_ID) {
|
|
59
|
+
throw new XaiRealtimeBrokerError(
|
|
60
|
+
"model_unavailable",
|
|
61
|
+
"The selected model is not a connected SuperGrok realtime model",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const authoritySnapshot = await resolveXaiProviderAccountAuthoritySnapshotForAcceptance(
|
|
65
|
+
input.db,
|
|
66
|
+
{ workspaceId: input.workspaceId, subjectId: input.subjectId },
|
|
67
|
+
);
|
|
68
|
+
let pin = await getXaiSessionAccountPin(input.db, {
|
|
69
|
+
workspaceId: input.workspaceId,
|
|
70
|
+
subjectId: input.subjectId,
|
|
71
|
+
sessionId: input.sessionId,
|
|
72
|
+
authoritySnapshot,
|
|
73
|
+
});
|
|
74
|
+
let auth: Awaited<ReturnType<typeof buildXaiSubscriptionAuthorization>>;
|
|
75
|
+
try {
|
|
76
|
+
auth = await buildXaiSubscriptionAuthorization({
|
|
77
|
+
db: input.db,
|
|
78
|
+
settings: input.settings,
|
|
79
|
+
accountId: input.accountId,
|
|
80
|
+
workspaceId: input.workspaceId,
|
|
81
|
+
subjectId: input.subjectId,
|
|
82
|
+
shardKey: input.sessionId,
|
|
83
|
+
pinnedCredentialId: pin?.pinnedCredentialId ?? null,
|
|
84
|
+
pinSource: pin?.pinSource === "manual" || pin?.pinSource === "policy" ? pin.pinSource : null,
|
|
85
|
+
authoritySnapshot,
|
|
86
|
+
sessionId: input.sessionId,
|
|
87
|
+
...(input.fetchImpl ? { fetch: input.fetchImpl as XaiFetch } : {}),
|
|
88
|
+
});
|
|
89
|
+
if (
|
|
90
|
+
auth.rotationEnabled &&
|
|
91
|
+
pin?.pinSource !== "manual" &&
|
|
92
|
+
pin?.pinnedCredentialId !== auth.credentialId
|
|
93
|
+
) {
|
|
94
|
+
try {
|
|
95
|
+
pin = await setXaiSessionAccountPin(input.db, {
|
|
96
|
+
accountId: input.accountId,
|
|
97
|
+
workspaceId: input.workspaceId,
|
|
98
|
+
subjectId: input.subjectId,
|
|
99
|
+
sessionId: input.sessionId,
|
|
100
|
+
authoritySnapshot,
|
|
101
|
+
credentialId: auth.credentialId,
|
|
102
|
+
pinSource: "policy",
|
|
103
|
+
expectedVersion: pin?.version ?? null,
|
|
104
|
+
});
|
|
105
|
+
} catch {
|
|
106
|
+
const current = await getXaiSessionAccountPin(input.db, {
|
|
107
|
+
workspaceId: input.workspaceId,
|
|
108
|
+
subjectId: input.subjectId,
|
|
109
|
+
sessionId: input.sessionId,
|
|
110
|
+
authoritySnapshot,
|
|
111
|
+
});
|
|
112
|
+
if (current?.pinSource === "manual" && current.pinnedCredentialId !== auth.credentialId) {
|
|
113
|
+
auth = await buildXaiSubscriptionAuthorization({
|
|
114
|
+
db: input.db,
|
|
115
|
+
settings: input.settings,
|
|
116
|
+
accountId: input.accountId,
|
|
117
|
+
workspaceId: input.workspaceId,
|
|
118
|
+
subjectId: input.subjectId,
|
|
119
|
+
shardKey: input.sessionId,
|
|
120
|
+
pinnedCredentialId: current.pinnedCredentialId,
|
|
121
|
+
pinSource: "manual",
|
|
122
|
+
authoritySnapshot,
|
|
123
|
+
sessionId: input.sessionId,
|
|
124
|
+
...(input.fetchImpl ? { fetch: input.fetchImpl as XaiFetch } : {}),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
throw new XaiRealtimeBrokerError(
|
|
131
|
+
"credential_unavailable",
|
|
132
|
+
"No eligible connected SuperGrok account is available",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const [history, continuity, minted] = await Promise.all([
|
|
137
|
+
getActiveSessionHistoryItems(input.db, input.workspaceId, input.sessionId),
|
|
138
|
+
getSessionRealtimeContinuityEntries(input.db, input.workspaceId, input.sessionId),
|
|
139
|
+
mintXaiClientSecret({
|
|
140
|
+
auth,
|
|
141
|
+
fetchImpl: input.fetchImpl ?? fetch,
|
|
142
|
+
}),
|
|
143
|
+
]);
|
|
144
|
+
return {
|
|
145
|
+
...minted,
|
|
146
|
+
upstreamModelId: UPSTREAM_MODEL_ID,
|
|
147
|
+
initialItems: projectSessionRealtimeInitialItems(history, continuity),
|
|
148
|
+
instructions: openGeniRealtimeInstructions(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function mintXaiClientSecret(input: {
|
|
153
|
+
auth: Awaited<ReturnType<typeof buildXaiSubscriptionAuthorization>>;
|
|
154
|
+
fetchImpl: typeof fetch;
|
|
155
|
+
}): Promise<{ token: string; url: string; expiresAt: number | null }> {
|
|
156
|
+
const request = async (accessToken: string) =>
|
|
157
|
+
await input.fetchImpl(`${XAI_PUBLIC_API_BASE_URL}/realtime/client_secrets`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: {
|
|
160
|
+
authorization: `Bearer ${accessToken}`,
|
|
161
|
+
"content-type": "application/json",
|
|
162
|
+
"user-agent": `opengeni/${XAI_CLIENT_VERSION}`,
|
|
163
|
+
"x-grok-client-version": XAI_CLIENT_VERSION,
|
|
164
|
+
"x-grok-client-identifier": "opengeni",
|
|
165
|
+
"x-grok-client-mode": XAI_CLIENT_MODE,
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({ expires_after: { seconds: 120 } }),
|
|
168
|
+
});
|
|
169
|
+
let response: Response;
|
|
170
|
+
try {
|
|
171
|
+
let token = await input.auth.context.getToken();
|
|
172
|
+
response = await request(token.accessToken);
|
|
173
|
+
if (response.status === 401) {
|
|
174
|
+
await response.body?.cancel().catch(() => undefined);
|
|
175
|
+
token = await input.auth.context.refresh();
|
|
176
|
+
response = await request(token.accessToken);
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
throw new XaiRealtimeBrokerError("provider_error", "xAI realtime token request failed");
|
|
180
|
+
}
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
throw new XaiRealtimeBrokerError(
|
|
183
|
+
response.status === 401 || response.status === 403
|
|
184
|
+
? "credential_unavailable"
|
|
185
|
+
: "provider_error",
|
|
186
|
+
response.status === 401 || response.status === 403
|
|
187
|
+
? "SuperGrok credentials were rejected"
|
|
188
|
+
: "xAI realtime token request failed",
|
|
189
|
+
response.status,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const body = (await response.json().catch(() => null)) as Record<string, unknown> | null;
|
|
193
|
+
const token = body?.value;
|
|
194
|
+
const expiresAt = normalizeExpiry(body?.expires_at ?? body?.expiresAt);
|
|
195
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
196
|
+
throw new XaiRealtimeBrokerError(
|
|
197
|
+
"invalid_provider_response",
|
|
198
|
+
"xAI returned an invalid realtime token",
|
|
199
|
+
response.status,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const url = new URL("wss://api.x.ai/v1/realtime");
|
|
203
|
+
url.searchParams.set("model", UPSTREAM_MODEL_ID);
|
|
204
|
+
return { token, url: url.toString(), expiresAt };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function normalizeExpiry(value: unknown): number | null {
|
|
208
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
209
|
+
return Math.trunc(value < 10_000_000_000 ? value * 1_000 : value);
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === "string") {
|
|
212
|
+
const parsed = Date.parse(value);
|
|
213
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
2
|
+
import type { XaiProviderAccountAuthoritySnapshotV1 } from "@opengeni/contracts";
|
|
3
|
+
import {
|
|
4
|
+
materializeXaiCredentialForRun,
|
|
5
|
+
refreshXaiSubscriptionCredentialSerialized,
|
|
6
|
+
resolveXaiProviderAccountAuthoritySnapshotForAcceptance,
|
|
7
|
+
selectXaiCredentialForUse,
|
|
8
|
+
type Database,
|
|
9
|
+
} from "@opengeni/db";
|
|
10
|
+
import {
|
|
11
|
+
refreshXaiToken,
|
|
12
|
+
XAI_CLIENT_VERSION,
|
|
13
|
+
XaiSubscriptionReloginRequired,
|
|
14
|
+
xaiAccessTokenExpiry,
|
|
15
|
+
type XaiFetch,
|
|
16
|
+
type XaiProxyAuthContext,
|
|
17
|
+
} from "@opengeni/xai-subscription";
|
|
18
|
+
|
|
19
|
+
export type XaiSubscriptionAuthorization = {
|
|
20
|
+
context: XaiProxyAuthContext;
|
|
21
|
+
credentialId: string;
|
|
22
|
+
authoritySnapshot: XaiProviderAccountAuthoritySnapshotV1;
|
|
23
|
+
rotationEnabled: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export async function buildXaiSubscriptionAuthorization(input: {
|
|
27
|
+
db: Database;
|
|
28
|
+
settings: Settings;
|
|
29
|
+
accountId: string;
|
|
30
|
+
workspaceId: string;
|
|
31
|
+
subjectId: string;
|
|
32
|
+
shardKey: string;
|
|
33
|
+
pinnedCredentialId?: string | null;
|
|
34
|
+
pinSource?: "manual" | "policy" | null;
|
|
35
|
+
authoritySnapshot?: XaiProviderAccountAuthoritySnapshotV1;
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
fetch?: XaiFetch;
|
|
38
|
+
}): Promise<XaiSubscriptionAuthorization> {
|
|
39
|
+
const encryptionKey = environmentsEncryptionKeyBytes(input.settings);
|
|
40
|
+
if (!encryptionKey) {
|
|
41
|
+
throw new Error("SuperGrok subscriptions require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY");
|
|
42
|
+
}
|
|
43
|
+
const authoritySnapshot =
|
|
44
|
+
input.authoritySnapshot ??
|
|
45
|
+
(await resolveXaiProviderAccountAuthoritySnapshotForAcceptance(input.db, {
|
|
46
|
+
workspaceId: input.workspaceId,
|
|
47
|
+
subjectId: input.subjectId,
|
|
48
|
+
}));
|
|
49
|
+
const selected = await selectXaiCredentialForUse(input.db, {
|
|
50
|
+
accountId: input.accountId,
|
|
51
|
+
workspaceId: input.workspaceId,
|
|
52
|
+
subjectId: input.subjectId,
|
|
53
|
+
authoritySnapshot,
|
|
54
|
+
shardKey: input.shardKey,
|
|
55
|
+
pinnedCredentialId: input.pinnedCredentialId ?? null,
|
|
56
|
+
pinSource: input.pinSource ?? null,
|
|
57
|
+
});
|
|
58
|
+
if (!selected.credentialId) {
|
|
59
|
+
throw new Error("No eligible SuperGrok subscription account is available");
|
|
60
|
+
}
|
|
61
|
+
const credentialId = selected.credentialId;
|
|
62
|
+
let credential = await materializeXaiCredentialForRun(input.db, {
|
|
63
|
+
workspaceId: input.workspaceId,
|
|
64
|
+
subjectId: input.subjectId,
|
|
65
|
+
credentialId,
|
|
66
|
+
authoritySnapshot,
|
|
67
|
+
encryptionKey,
|
|
68
|
+
});
|
|
69
|
+
const tokenSnapshot = () => {
|
|
70
|
+
if (!credential.secret.accessToken || !credential.providerAccountId) {
|
|
71
|
+
throw new XaiSubscriptionReloginRequired(
|
|
72
|
+
"The SuperGrok connection is missing a verified token identity. Reconnect the account.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
accessToken: credential.secret.accessToken,
|
|
77
|
+
userId: credential.providerAccountId,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
credentialId,
|
|
82
|
+
authoritySnapshot,
|
|
83
|
+
rotationEnabled: selected.rotationEnabled,
|
|
84
|
+
context: {
|
|
85
|
+
clientVersion: XAI_CLIENT_VERSION,
|
|
86
|
+
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
87
|
+
getToken: async () => tokenSnapshot(),
|
|
88
|
+
refresh: async () => {
|
|
89
|
+
const observedAccessToken = credential.secret.accessToken;
|
|
90
|
+
const observedRefreshToken = credential.secret.refreshToken;
|
|
91
|
+
if (!observedRefreshToken) {
|
|
92
|
+
throw new XaiSubscriptionReloginRequired(
|
|
93
|
+
"The SuperGrok connection cannot be refreshed. Reconnect the account.",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const result = await refreshXaiSubscriptionCredentialSerialized(input.db, {
|
|
97
|
+
accountId: input.accountId,
|
|
98
|
+
workspaceId: input.workspaceId,
|
|
99
|
+
subjectId: input.subjectId,
|
|
100
|
+
credentialId,
|
|
101
|
+
authoritySnapshot,
|
|
102
|
+
encryptionKey,
|
|
103
|
+
observedAccessToken,
|
|
104
|
+
observedRefreshToken,
|
|
105
|
+
refresh: async (current) => {
|
|
106
|
+
const refreshToken = current.secret.refreshToken;
|
|
107
|
+
if (!refreshToken) {
|
|
108
|
+
throw new XaiSubscriptionReloginRequired(
|
|
109
|
+
"The SuperGrok connection cannot be refreshed. Reconnect the account.",
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const tokens = await refreshXaiToken(refreshToken, {
|
|
113
|
+
...(input.fetch ? { fetch: input.fetch } : {}),
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
secret: {
|
|
117
|
+
version: 1,
|
|
118
|
+
accessToken: tokens.accessToken,
|
|
119
|
+
refreshToken: tokens.refreshToken,
|
|
120
|
+
},
|
|
121
|
+
expiresAt:
|
|
122
|
+
xaiAccessTokenExpiry(tokens.accessToken) ??
|
|
123
|
+
new Date(Date.now() + tokens.expiresInSeconds * 1_000),
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
credential = result.credential;
|
|
128
|
+
return tokenSnapshot();
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|