@opengeni/db 0.27.7 → 0.27.9
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/{chunk-IHPCI4GV.js → chunk-2JFRTKTG.js} +1 -1
- package/dist/chunk-2JFRTKTG.js.map +1 -0
- package/dist/{chunk-EX7CDSGH.js → chunk-NX2JNJJP.js} +13 -4
- package/dist/chunk-NX2JNJJP.js.map +1 -0
- package/dist/codex-token-resolver.d.ts +50 -8
- package/dist/connection-token-resolver.d.ts +45 -7
- package/dist/database.d.ts +137 -0
- package/dist/index.d.ts +25 -154
- package/dist/index.js +2796 -2634
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +1 -1
- package/dist/memory-governance.d.ts +1 -1
- package/dist/new-session-drafts.d.ts +1 -1
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/schema.d.ts +44 -0
- package/dist/schema.js +1 -1
- package/dist/scoped-knowledge.d.ts +1 -1
- package/dist/session-control.d.ts +2 -1
- package/dist/session-queue-commands.d.ts +1 -1
- package/dist/session-realtime-context.d.ts +1 -1
- package/dist/session-realtime-ledger.d.ts +1 -1
- package/dist/session-realtime-mirror.d.ts +1 -1
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +1 -1
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/turn-initiator.d.ts +1 -1
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/drizzle/0170_session_control_wake_revision.sql +133 -0
- package/drizzle/0171_social_connection_subject_ownership.sql +53 -0
- package/package.json +3 -3
- package/src/codex-token-resolver.ts +102 -49
- package/src/connection-token-resolver.ts +67 -26
- package/src/database.ts +393 -0
- package/src/index.ts +439 -516
- package/src/insights.ts +2 -2
- package/src/memory-governance.ts +2 -2
- package/src/new-session-drafts.ts +1 -1
- package/src/preference-registry.ts +2 -2
- package/src/provision-roles.ts +1 -1
- package/src/runtime-posture.ts +1 -1
- package/src/schema.ts +16 -3
- package/src/scoped-knowledge.ts +2 -2
- package/src/session-control.ts +12 -5
- package/src/session-queue-commands.ts +4 -1
- package/src/session-realtime-context.ts +1 -1
- package/src/session-realtime-ledger.ts +1 -1
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-realtime-state.ts +1 -1
- package/src/session-realtime-terminal.ts +1 -1
- package/src/session-realtime.ts +1 -1
- package/src/session-tool-call-settlement.ts +1 -1
- package/src/turn-initiator.ts +1 -1
- package/src/workspace-artifacts.ts +2 -2
- package/src/workspace-instruction-policies.ts +2 -2
- package/dist/chunk-EX7CDSGH.js.map +0 -1
- package/dist/chunk-IHPCI4GV.js.map +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hoisted here from apps/worker/src/activities/codex-auth.ts so BOTH the worker
|
|
4
4
|
// (turn-time bearer for the streamed run) AND the api (the /wham/usage quota-bar
|
|
5
5
|
// reads) drive ONE resolver — no duplicated refresh/CAS/single-flight logic. The
|
|
6
|
-
// worker re-exports buildCodexTokenResolver from
|
|
6
|
+
// worker re-exports buildCodexTokenResolver from @opengeni/db for back-compat, so
|
|
7
7
|
// the agent-turn.ts call site is unchanged.
|
|
8
8
|
//
|
|
9
9
|
// Why @opengeni/db is the right home: the resolver only orchestrates accessors
|
|
@@ -35,15 +35,52 @@ import {
|
|
|
35
35
|
refreshCodexToken,
|
|
36
36
|
} from "@opengeni/codex";
|
|
37
37
|
import { encryptEnvironmentValue } from "./environment-crypto";
|
|
38
|
-
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
import type { Database } from "./database";
|
|
39
|
+
|
|
40
|
+
export type CodexCredentialTokens = {
|
|
41
|
+
accessToken: string;
|
|
42
|
+
refreshToken: string;
|
|
43
|
+
idToken: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type CodexCredentialForRun = {
|
|
47
|
+
id: string;
|
|
48
|
+
version: number;
|
|
49
|
+
workspaceId: string;
|
|
50
|
+
tokens: CodexCredentialTokens;
|
|
51
|
+
chatgptAccountId: string | null;
|
|
52
|
+
scopes: string | null;
|
|
53
|
+
planType: string | null;
|
|
54
|
+
isFedramp: boolean;
|
|
55
|
+
expiresAt: Date | null;
|
|
56
|
+
lastRefreshAt: Date | null;
|
|
57
|
+
status: string;
|
|
58
|
+
lastError: string | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type CodexAccountUsageSnapshot = {
|
|
62
|
+
primaryUsedPercent?: number | null;
|
|
63
|
+
primaryResetAt?: Date | null;
|
|
64
|
+
secondaryUsedPercent?: number | null;
|
|
65
|
+
secondaryResetAt?: Date | null;
|
|
66
|
+
checkedAt?: Date;
|
|
67
|
+
resetCreditAvailableCount?: number | null;
|
|
68
|
+
resetCreditsCheckedAt?: Date | null;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type CodexCredentialRefreshInput = {
|
|
72
|
+
id: string;
|
|
73
|
+
version: number;
|
|
74
|
+
workspaceId: string;
|
|
75
|
+
credentialEncrypted: string;
|
|
76
|
+
expiresAt: Date | null;
|
|
77
|
+
lastRefreshAt: Date;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type CodexCredentialStatusTarget = {
|
|
81
|
+
id: string;
|
|
82
|
+
version: number;
|
|
83
|
+
};
|
|
47
84
|
|
|
48
85
|
// Single-flight per CREDENTIAL INSTANCE (row id + version), process-module scope.
|
|
49
86
|
// Keying by the loaded credential's id+version — NOT by workspaceId alone (P1-b) —
|
|
@@ -142,25 +179,37 @@ export async function withCodexTokenDeadline<T>(
|
|
|
142
179
|
|
|
143
180
|
// Dependencies are injectable so the lifecycle logic (single-flight, staleness,
|
|
144
181
|
// needs_relogin transition) is unit-testable without a database. Production uses
|
|
145
|
-
// the real db + codex functions
|
|
182
|
+
// the root composition wrapper supplies the real db + codex functions.
|
|
146
183
|
export type CodexAuthDeps = {
|
|
147
|
-
loadCredential:
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
loadCredential: (
|
|
185
|
+
db: Database,
|
|
186
|
+
settings: Settings,
|
|
187
|
+
workspaceId: string,
|
|
188
|
+
credentialId: string,
|
|
189
|
+
) => Promise<CodexCredentialForRun | null>;
|
|
190
|
+
recordRefresh: (db: Database, input: CodexCredentialRefreshInput) => Promise<boolean>;
|
|
191
|
+
setStatus: (
|
|
192
|
+
db: Database,
|
|
193
|
+
workspaceId: string,
|
|
194
|
+
status: "active" | "needs_relogin" | "error",
|
|
195
|
+
lastError: string | null,
|
|
196
|
+
target: CodexCredentialStatusTarget,
|
|
197
|
+
) => Promise<boolean>;
|
|
150
198
|
refresh: typeof refreshCodexToken;
|
|
151
199
|
encrypt: typeof encryptEnvironmentValue;
|
|
152
200
|
keyBytes: typeof environmentsEncryptionKeyBytes;
|
|
153
|
-
withRefreshLock:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
201
|
+
withRefreshLock: <T>(
|
|
202
|
+
db: Database,
|
|
203
|
+
workspaceId: string,
|
|
204
|
+
credentialId: string,
|
|
205
|
+
fn: (lockedDb: Database) => Promise<T>,
|
|
206
|
+
) => Promise<T>;
|
|
207
|
+
recordUsage?: (
|
|
208
|
+
db: Database,
|
|
209
|
+
workspaceId: string,
|
|
210
|
+
credentialId: string,
|
|
211
|
+
snapshot: CodexAccountUsageSnapshot,
|
|
212
|
+
) => Promise<boolean>;
|
|
164
213
|
};
|
|
165
214
|
|
|
166
215
|
export function buildCodexTokenResolver(
|
|
@@ -173,7 +222,7 @@ export function buildCodexTokenResolver(
|
|
|
173
222
|
// 0 rows against the now-inactive row — so a refresh racing a switch can never
|
|
174
223
|
// clobber the newly-active account. The single-flight map needs zero change.
|
|
175
224
|
credentialId: string,
|
|
176
|
-
deps: CodexAuthDeps
|
|
225
|
+
deps: CodexAuthDeps,
|
|
177
226
|
): { getToken: () => Promise<CodexTokenSnapshot>; refresh: () => Promise<CodexTokenSnapshot> } {
|
|
178
227
|
const snapshot = (cred: CodexCredentialForRun): CodexTokenSnapshot => ({
|
|
179
228
|
accessToken: cred.tokens.accessToken,
|
|
@@ -336,9 +385,10 @@ export async function fetchCodexUsageForAccount(
|
|
|
336
385
|
settings: Settings,
|
|
337
386
|
workspaceId: string,
|
|
338
387
|
credentialId: string,
|
|
388
|
+
deps: CodexAuthDeps,
|
|
339
389
|
fetchImpl: CodexFetch = fetch,
|
|
340
390
|
): Promise<CodexUsagePayload> {
|
|
341
|
-
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId);
|
|
391
|
+
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId, deps);
|
|
342
392
|
let token: CodexTokenSnapshot;
|
|
343
393
|
try {
|
|
344
394
|
token = await resolver.getToken();
|
|
@@ -373,27 +423,29 @@ export async function fetchCodexUsageForAccount(
|
|
|
373
423
|
// cached without erasing or falsely refreshing the last valid quota truth.
|
|
374
424
|
// Cache-write is best-effort: a disconnect under us (false) or a transient
|
|
375
425
|
// write error must NOT sink the freshly-read result we are about to return.
|
|
376
|
-
await
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
426
|
+
await deps
|
|
427
|
+
.recordUsage?.(db, workspaceId, credentialId, {
|
|
428
|
+
...(parsedQuota
|
|
429
|
+
? {
|
|
430
|
+
primaryUsedPercent: normalized.fiveHour?.percent ?? null,
|
|
431
|
+
primaryResetAt: normalized.fiveHour?.resetAt
|
|
432
|
+
? new Date(normalized.fiveHour.resetAt)
|
|
433
|
+
: null,
|
|
434
|
+
secondaryUsedPercent: normalized.weekly?.percent ?? null,
|
|
435
|
+
secondaryResetAt: normalized.weekly?.resetAt
|
|
436
|
+
? new Date(normalized.weekly.resetAt)
|
|
437
|
+
: null,
|
|
438
|
+
checkedAt,
|
|
439
|
+
}
|
|
440
|
+
: {}),
|
|
441
|
+
...(normalized.rateLimitResetCredits
|
|
442
|
+
? {
|
|
443
|
+
resetCreditAvailableCount: normalized.rateLimitResetCredits.availableCount,
|
|
444
|
+
resetCreditsCheckedAt: checkedAt,
|
|
445
|
+
}
|
|
446
|
+
: {}),
|
|
447
|
+
})
|
|
448
|
+
.catch(() => undefined);
|
|
397
449
|
}
|
|
398
450
|
|
|
399
451
|
return normalized;
|
|
@@ -418,9 +470,10 @@ export async function fetchCodexRateLimitResetCreditsForAccount(
|
|
|
418
470
|
settings: Settings,
|
|
419
471
|
workspaceId: string,
|
|
420
472
|
credentialId: string,
|
|
473
|
+
deps: CodexAuthDeps,
|
|
421
474
|
fetchImpl: CodexFetch = fetch,
|
|
422
475
|
): Promise<CodexRateLimitResetCreditsAccountResult> {
|
|
423
|
-
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId);
|
|
476
|
+
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId, deps);
|
|
424
477
|
let token: CodexTokenSnapshot;
|
|
425
478
|
try {
|
|
426
479
|
token = await resolver.getToken();
|
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
type Settings,
|
|
5
5
|
} from "@opengeni/config";
|
|
6
6
|
import type {
|
|
7
|
+
ConnectionKind,
|
|
7
8
|
ConnectionCredentialsPort,
|
|
9
|
+
ConnectionStatus,
|
|
8
10
|
McpConnectionResourceScope,
|
|
9
11
|
McpCredentialAuthNeededReason,
|
|
10
12
|
McpCredentialsRequest,
|
|
@@ -24,14 +26,49 @@ export { isPrivateAddress } from "@opengeni/network";
|
|
|
24
26
|
import { Buffer } from "node:buffer";
|
|
25
27
|
import { isIP } from "node:net";
|
|
26
28
|
import { encryptEnvironmentValue } from "./environment-crypto";
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
import type { Database } from "./database";
|
|
30
|
+
|
|
31
|
+
export type ConnectionCredentialForBroker = {
|
|
32
|
+
id: string;
|
|
33
|
+
accountId: string;
|
|
34
|
+
workspaceId: string;
|
|
35
|
+
subjectId: string | null;
|
|
36
|
+
providerDomain: string;
|
|
37
|
+
kind: ConnectionKind;
|
|
38
|
+
status: ConnectionStatus;
|
|
39
|
+
credential: Record<string, unknown>;
|
|
40
|
+
grantedScopes: string[];
|
|
41
|
+
expiresAt: Date | null;
|
|
42
|
+
lastRefreshAt: Date | null;
|
|
43
|
+
version: number;
|
|
44
|
+
metadata: Record<string, unknown>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type ConnectionCredentialLookupInput = {
|
|
48
|
+
workspaceId: string;
|
|
49
|
+
connectionId?: string;
|
|
50
|
+
providerDomain: string;
|
|
51
|
+
kind?: ConnectionKind;
|
|
52
|
+
subjectId?: string | null;
|
|
53
|
+
allowSubjectOwned?: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type ConnectionTokenRefreshInput = {
|
|
57
|
+
id: string;
|
|
58
|
+
version: number;
|
|
59
|
+
workspaceId: string;
|
|
60
|
+
credentialEncrypted: string;
|
|
61
|
+
expiresAt: Date | null;
|
|
62
|
+
grantedScopes?: string[];
|
|
63
|
+
lastRefreshAt: Date;
|
|
64
|
+
subjectId?: string | null;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type ConnectionStatusGuard = {
|
|
68
|
+
id: string;
|
|
69
|
+
version: number;
|
|
70
|
+
subjectId?: string | null;
|
|
71
|
+
};
|
|
35
72
|
|
|
36
73
|
export type ResolveConnectionCredentialResult =
|
|
37
74
|
| {
|
|
@@ -334,10 +371,25 @@ function parseHostCredentialExpiry(value: string | null | undefined): Date | nul
|
|
|
334
371
|
}
|
|
335
372
|
|
|
336
373
|
export type ConnectionBrokerDeps = {
|
|
337
|
-
loadCredential:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
374
|
+
loadCredential: (
|
|
375
|
+
db: Database,
|
|
376
|
+
settings: Settings,
|
|
377
|
+
input: ConnectionCredentialLookupInput,
|
|
378
|
+
) => Promise<ConnectionCredentialForBroker | null>;
|
|
379
|
+
recordRefresh: (db: Database, input: ConnectionTokenRefreshInput) => Promise<boolean>;
|
|
380
|
+
setStatus: (
|
|
381
|
+
db: Database,
|
|
382
|
+
workspaceId: string,
|
|
383
|
+
status: ConnectionStatus,
|
|
384
|
+
lastError: string | null,
|
|
385
|
+
guard: ConnectionStatusGuard,
|
|
386
|
+
) => Promise<boolean>;
|
|
387
|
+
recordUsed: (
|
|
388
|
+
db: Database,
|
|
389
|
+
workspaceId: string,
|
|
390
|
+
connectionId: string,
|
|
391
|
+
subjectId?: string | null,
|
|
392
|
+
) => Promise<void>;
|
|
341
393
|
refresh: typeof refreshOAuthConnectionCredential;
|
|
342
394
|
encrypt: typeof encryptEnvironmentValue;
|
|
343
395
|
keyBytes: typeof environmentsEncryptionKeyBytes;
|
|
@@ -372,17 +424,6 @@ export type RefreshTransportOptions = {
|
|
|
372
424
|
dnsLookup?: DnsLookup;
|
|
373
425
|
};
|
|
374
426
|
|
|
375
|
-
const defaultDeps: ConnectionBrokerDeps = {
|
|
376
|
-
loadCredential: loadConnectionCredentialForBroker,
|
|
377
|
-
recordRefresh: recordConnectionTokenRefresh,
|
|
378
|
-
setStatus: setConnectionStatus,
|
|
379
|
-
recordUsed: recordConnectionUsed,
|
|
380
|
-
refresh: refreshOAuthConnectionCredential,
|
|
381
|
-
encrypt: encryptEnvironmentValue,
|
|
382
|
-
keyBytes: environmentsEncryptionKeyBytes,
|
|
383
|
-
now: () => new Date(),
|
|
384
|
-
};
|
|
385
|
-
|
|
386
427
|
const inflight = new Map<string, Promise<ConnectionCredentialForBroker>>();
|
|
387
428
|
const REFRESH_WINDOW_MS = 60_000;
|
|
388
429
|
const CONNECTION_REFRESH_TIMEOUT_MS = 10_000;
|
|
@@ -390,7 +431,7 @@ const CONNECTION_REFRESH_TIMEOUT_MS = 10_000;
|
|
|
390
431
|
export function buildConnectionTokenResolver(
|
|
391
432
|
db: Database,
|
|
392
433
|
settings: Settings,
|
|
393
|
-
deps: ConnectionBrokerDeps
|
|
434
|
+
deps: ConnectionBrokerDeps,
|
|
394
435
|
options: ConnectionTokenResolverOptions = {},
|
|
395
436
|
): (input: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult> {
|
|
396
437
|
type CredentialLookupInput = Pick<
|
|
@@ -404,7 +445,7 @@ export function buildConnectionTokenResolver(
|
|
|
404
445
|
if (subjectOwned && !input.subjectId) {
|
|
405
446
|
return null;
|
|
406
447
|
}
|
|
407
|
-
const request:
|
|
448
|
+
const request: ConnectionCredentialLookupInput = {
|
|
408
449
|
workspaceId: input.workspaceId,
|
|
409
450
|
providerDomain: input.connectionRef.providerDomain,
|
|
410
451
|
allowSubjectOwned: subjectOwned,
|
|
@@ -484,7 +525,7 @@ export function buildConnectionTokenResolver(
|
|
|
484
525
|
throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured");
|
|
485
526
|
}
|
|
486
527
|
const refreshed = await deps.refresh(cred, ref, settings, options.refreshTransport);
|
|
487
|
-
const refreshRecord:
|
|
528
|
+
const refreshRecord: ConnectionTokenRefreshInput = {
|
|
488
529
|
id: cred.id,
|
|
489
530
|
version: cred.version,
|
|
490
531
|
workspaceId: cred.workspaceId,
|