@opengeni/api-router 0.21.11 → 0.22.2
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 +1 -1
- package/dist/{chunk-J4JS2F7L.js → chunk-HWXJW5C7.js} +1210 -173
- package/dist/chunk-HWXJW5C7.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/routes/transcription-recordings.d.ts +3 -0
- package/dist/transcription/segmenter.d.ts +10 -0
- package/dist/transcription/service.d.ts +5 -0
- package/package.json +12 -12
- package/src/app.ts +36 -2
- package/src/mcp/server.ts +5 -80
- package/src/mcp/toolspace.ts +91 -69
- package/src/routes/codex.ts +138 -8
- package/src/routes/transcription-recordings.ts +722 -0
- package/src/routes/transcriptions.ts +2 -0
- package/src/transcription/providers/azure-openai.ts +4 -3
- package/src/transcription/providers/codex-subscription.ts +4 -1
- package/src/transcription/providers/openai.ts +7 -2
- package/src/transcription/segmenter.ts +260 -0
- package/src/transcription/service.ts +111 -10
- package/dist/chunk-J4JS2F7L.js.map +0 -1
package/src/routes/codex.ts
CHANGED
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
buildCodexTokenResolver,
|
|
36
36
|
claimCodexResetRedemption,
|
|
37
37
|
completeCodexResetRedemption,
|
|
38
|
+
clearCodexAppsCredential,
|
|
39
|
+
designateCodexAppsCredential,
|
|
38
40
|
disconnectAllCodexAccounts,
|
|
39
41
|
disconnectCodexAccount,
|
|
40
42
|
encryptEnvironmentValue,
|
|
@@ -44,6 +46,7 @@ import {
|
|
|
44
46
|
fenceCodexResetRedemptionSend,
|
|
45
47
|
getCodexResetRedemptionAttempt,
|
|
46
48
|
getCodexCredentialStatus,
|
|
49
|
+
getCodexAppsSettings,
|
|
47
50
|
getCodexRotationSettings,
|
|
48
51
|
listPendingCodexCapacityWakeTargets,
|
|
49
52
|
listCodexAccountStatuses,
|
|
@@ -68,7 +71,14 @@ const CODEX_PROVIDER_LABEL = "Codex subscription · no credits";
|
|
|
68
71
|
// The wire shape for one Codex account (metadata only; never the secret column).
|
|
69
72
|
// P2: fiveHour/weekly ride along, built from the CACHED usage columns (zero
|
|
70
73
|
// provider calls, zero decrypts) so the bars render instantly off this read.
|
|
71
|
-
function codexAccountJson(
|
|
74
|
+
function codexAccountJson(
|
|
75
|
+
row: CodexAccountStatus,
|
|
76
|
+
options: {
|
|
77
|
+
appsCredentialId?: string | null;
|
|
78
|
+
canManageApps?: boolean;
|
|
79
|
+
humanSubjectId?: string | null;
|
|
80
|
+
} = {},
|
|
81
|
+
) {
|
|
72
82
|
return {
|
|
73
83
|
id: row.id,
|
|
74
84
|
chatgptAccountId: row.chatgptAccountId,
|
|
@@ -98,6 +108,14 @@ function codexAccountJson(row: CodexAccountStatus) {
|
|
|
98
108
|
resetCreditsCheckedAt: row.resetCreditsCheckedAt,
|
|
99
109
|
// P3 rotation cooldown: when set and in the future, this account is cooling-down.
|
|
100
110
|
exhaustedUntil: row.exhaustedUntil,
|
|
111
|
+
appsDesignated: options.appsCredentialId === row.id,
|
|
112
|
+
canEnableApps:
|
|
113
|
+
options.appsCredentialId === null &&
|
|
114
|
+
options.canManageApps === true &&
|
|
115
|
+
options.humanSubjectId !== null &&
|
|
116
|
+
options.humanSubjectId !== undefined &&
|
|
117
|
+
row.connectedBySubjectId === options.humanSubjectId &&
|
|
118
|
+
row.status === "active",
|
|
101
119
|
};
|
|
102
120
|
}
|
|
103
121
|
|
|
@@ -242,6 +260,28 @@ async function requireRedemptionHuman(
|
|
|
242
260
|
return { human, accountId: grant.accountId };
|
|
243
261
|
}
|
|
244
262
|
|
|
263
|
+
async function requireCodexAppsHuman(
|
|
264
|
+
c: Context,
|
|
265
|
+
deps: ApiRouteDeps,
|
|
266
|
+
workspaceId: string,
|
|
267
|
+
): Promise<{ human: ManagedCookieHuman; accountId: string }> {
|
|
268
|
+
if (c.req.header("authorization")) {
|
|
269
|
+
throw new HTTPException(403, {
|
|
270
|
+
message: "authorization bearer is not allowed for Codex Apps designation",
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
requireSameOriginBrowserMutation(c, deps);
|
|
274
|
+
const human = await managedCookieHuman(c, deps);
|
|
275
|
+
if (!human) {
|
|
276
|
+
throw new HTTPException(401, { message: "managed browser session required" });
|
|
277
|
+
}
|
|
278
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "connections:write");
|
|
279
|
+
if (grant.subjectId !== human.subjectId) {
|
|
280
|
+
throw new HTTPException(403, { message: "managed browser identity mismatch" });
|
|
281
|
+
}
|
|
282
|
+
return { human, accountId: grant.accountId };
|
|
283
|
+
}
|
|
284
|
+
|
|
245
285
|
function cachedUsage(row: CodexAccountStatus): CodexUsagePayload | null {
|
|
246
286
|
const fiveHour = buildCodexUsageWindowFromCache(
|
|
247
287
|
row.primaryUsedPercent,
|
|
@@ -721,15 +761,33 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
721
761
|
// workspace active pointer + rotation settings. Read access.
|
|
722
762
|
app.get("/v1/workspaces/:workspaceId/codex/accounts", async (c) => {
|
|
723
763
|
const workspaceId = c.req.param("workspaceId");
|
|
724
|
-
await requireAccessGrant(c, deps, workspaceId, "workspace:read");
|
|
725
|
-
const [accounts, rotation] = await Promise.all([
|
|
764
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:read");
|
|
765
|
+
const [accounts, rotation, apps, human] = await Promise.all([
|
|
726
766
|
listCodexAccountStatuses(db, workspaceId),
|
|
727
767
|
getCodexRotationSettings(db, workspaceId),
|
|
768
|
+
getCodexAppsSettings(db, workspaceId),
|
|
769
|
+
managedCookieHuman(c, deps),
|
|
728
770
|
]);
|
|
729
771
|
const activeAccountId = rotation?.activeCredentialId ?? null;
|
|
772
|
+
const humanSubjectId = human?.subjectId === grant.subjectId ? human.subjectId : null;
|
|
773
|
+
const canManageApps =
|
|
774
|
+
humanSubjectId !== null && hasPermission(grant.permissions, "connections:write");
|
|
730
775
|
return c.json({
|
|
731
|
-
accounts: accounts.map(
|
|
776
|
+
accounts: accounts.map((account) =>
|
|
777
|
+
codexAccountJson(account, {
|
|
778
|
+
appsCredentialId: apps.credentialId,
|
|
779
|
+
canManageApps: settings.codexConnectedAppsEnabled && canManageApps,
|
|
780
|
+
humanSubjectId,
|
|
781
|
+
}),
|
|
782
|
+
),
|
|
732
783
|
activeAccountId,
|
|
784
|
+
apps: {
|
|
785
|
+
available: settings.codexConnectedAppsEnabled,
|
|
786
|
+
credentialId: apps.credentialId,
|
|
787
|
+
version: apps.version,
|
|
788
|
+
designatedAt: apps.designatedAt,
|
|
789
|
+
canDisable: canManageApps && apps.credentialId !== null,
|
|
790
|
+
},
|
|
733
791
|
settings: {
|
|
734
792
|
rotationEnabled: rotation?.rotationEnabled ?? false,
|
|
735
793
|
// sharded-rotation policy: rotation-enabled always behaves as sticky-sharded; report the
|
|
@@ -740,6 +798,78 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
740
798
|
});
|
|
741
799
|
});
|
|
742
800
|
|
|
801
|
+
app.post("/v1/workspaces/:workspaceId/codex/apps", async (c) => {
|
|
802
|
+
const workspaceId = c.req.param("workspaceId");
|
|
803
|
+
if (!settings.codexConnectedAppsEnabled) {
|
|
804
|
+
throw new HTTPException(409, { message: "Codex Apps is disabled for this deployment" });
|
|
805
|
+
}
|
|
806
|
+
const { human, accountId } = await requireCodexAppsHuman(c, deps, workspaceId);
|
|
807
|
+
const parsed = z
|
|
808
|
+
.object({
|
|
809
|
+
accountId: z.string().uuid(),
|
|
810
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
811
|
+
})
|
|
812
|
+
.safeParse(await c.req.json().catch(() => null));
|
|
813
|
+
if (!parsed.success) {
|
|
814
|
+
throw new HTTPException(400, { message: "accountId and expectedVersion are required" });
|
|
815
|
+
}
|
|
816
|
+
const result = await designateCodexAppsCredential(db, {
|
|
817
|
+
accountId,
|
|
818
|
+
workspaceId,
|
|
819
|
+
credentialId: parsed.data.accountId,
|
|
820
|
+
subjectId: human.subjectId,
|
|
821
|
+
expectedVersion: parsed.data.expectedVersion,
|
|
822
|
+
});
|
|
823
|
+
if (result.kind === "not_found") {
|
|
824
|
+
throw new HTTPException(404, { message: "codex account not found" });
|
|
825
|
+
}
|
|
826
|
+
if (result.kind === "not_owner") {
|
|
827
|
+
throw new HTTPException(403, {
|
|
828
|
+
message: "only the managed human who connected this subscription may designate it",
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
if (result.kind === "forbidden") {
|
|
832
|
+
throw new HTTPException(403, { message: "missing permission: connections:write" });
|
|
833
|
+
}
|
|
834
|
+
if (result.kind === "unavailable") {
|
|
835
|
+
throw new HTTPException(409, { message: "codex account requires relogin" });
|
|
836
|
+
}
|
|
837
|
+
const response = {
|
|
838
|
+
credentialId: result.credentialId,
|
|
839
|
+
version: result.version,
|
|
840
|
+
designatedAt: result.designatedAt,
|
|
841
|
+
changed: result.kind === "updated",
|
|
842
|
+
};
|
|
843
|
+
return result.kind === "updated" ? c.json(response) : c.json(response, 409);
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
app.delete("/v1/workspaces/:workspaceId/codex/apps", async (c) => {
|
|
847
|
+
const workspaceId = c.req.param("workspaceId");
|
|
848
|
+
const { human, accountId } = await requireCodexAppsHuman(c, deps, workspaceId);
|
|
849
|
+
const parsed = z
|
|
850
|
+
.object({ expectedVersion: z.number().int().nonnegative() })
|
|
851
|
+
.safeParse(await c.req.json().catch(() => null));
|
|
852
|
+
if (!parsed.success) {
|
|
853
|
+
throw new HTTPException(400, { message: "expectedVersion is required" });
|
|
854
|
+
}
|
|
855
|
+
const result = await clearCodexAppsCredential(db, {
|
|
856
|
+
accountId,
|
|
857
|
+
workspaceId,
|
|
858
|
+
subjectId: human.subjectId,
|
|
859
|
+
expectedVersion: parsed.data.expectedVersion,
|
|
860
|
+
});
|
|
861
|
+
if (result.kind === "forbidden") {
|
|
862
|
+
throw new HTTPException(403, { message: "missing permission: connections:write" });
|
|
863
|
+
}
|
|
864
|
+
const response = {
|
|
865
|
+
credentialId: result.credentialId,
|
|
866
|
+
version: result.version,
|
|
867
|
+
designatedAt: result.designatedAt,
|
|
868
|
+
changed: result.kind === "updated",
|
|
869
|
+
};
|
|
870
|
+
return result.kind === "conflict" ? c.json(response, 409) : c.json(response);
|
|
871
|
+
});
|
|
872
|
+
|
|
743
873
|
// Manually switch the workspace ACTIVE account (the one unpinned sessions use).
|
|
744
874
|
// Pure pointer flip; in-flight turns pick it up on their next token fetch.
|
|
745
875
|
app.post("/v1/workspaces/:workspaceId/codex/accounts/:accountId/activate", async (c) => {
|
|
@@ -872,13 +1002,13 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
872
1002
|
// row was active (FK ON DELETE SET NULL + re-pick in the same RLS txn).
|
|
873
1003
|
app.delete("/v1/workspaces/:workspaceId/codex/accounts/:accountId", async (c) => {
|
|
874
1004
|
const workspaceId = c.req.param("workspaceId");
|
|
875
|
-
await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
1005
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
876
1006
|
const accountId = c.req.param("accountId");
|
|
877
1007
|
const mutation = await withCodexCapacityMutation(
|
|
878
1008
|
db,
|
|
879
1009
|
{ workspaceId, reason: "codex_credential_disconnected" },
|
|
880
1010
|
async (tx) => {
|
|
881
|
-
const result = await disconnectCodexAccount(tx, workspaceId, accountId);
|
|
1011
|
+
const result = await disconnectCodexAccount(tx, workspaceId, accountId, grant.subjectId);
|
|
882
1012
|
return { result, changed: result.removed };
|
|
883
1013
|
},
|
|
884
1014
|
);
|
|
@@ -897,12 +1027,12 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
897
1027
|
// the by-id route above.
|
|
898
1028
|
app.delete("/v1/workspaces/:workspaceId/codex", async (c) => {
|
|
899
1029
|
const workspaceId = c.req.param("workspaceId");
|
|
900
|
-
await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
1030
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
901
1031
|
const mutation = await withCodexCapacityMutation(
|
|
902
1032
|
db,
|
|
903
1033
|
{ workspaceId, reason: "codex_credentials_disconnected" },
|
|
904
1034
|
async (tx) => {
|
|
905
|
-
const result = await disconnectAllCodexAccounts(tx, workspaceId);
|
|
1035
|
+
const result = await disconnectAllCodexAccounts(tx, workspaceId, grant.subjectId);
|
|
906
1036
|
return { result, changed: result.removed > 0 };
|
|
907
1037
|
},
|
|
908
1038
|
);
|