@opengeni/api-router 0.21.11 → 0.21.14
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.js +1 -1
- package/dist/{chunk-J4JS2F7L.js → chunk-R5PDSH2A.js} +177 -99
- package/dist/chunk-R5PDSH2A.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/package.json +12 -12
- package/src/mcp/server.ts +4 -79
- package/src/mcp/toolspace.ts +91 -69
- package/src/routes/codex.ts +138 -8
- package/dist/chunk-J4JS2F7L.js.map +0 -1
package/dist/app.js
CHANGED
|
@@ -347,7 +347,6 @@ import {
|
|
|
347
347
|
getPreferenceRegistryFullContent,
|
|
348
348
|
getVariableSet,
|
|
349
349
|
getVariableSetByName,
|
|
350
|
-
areGitHubRepositoriesAllowedForWorkspace,
|
|
351
350
|
listScheduledTaskRuns,
|
|
352
351
|
listScheduledTasks,
|
|
353
352
|
listSessionEventPage,
|
|
@@ -386,7 +385,6 @@ import {
|
|
|
386
385
|
import { appendAndPublishEvents as appendAndPublishEvents2, publishDurableSessionEvents } from "@opengeni/events";
|
|
387
386
|
import {
|
|
388
387
|
createSignedState as createSignedState3,
|
|
389
|
-
createGitHubAppInstallationToken,
|
|
390
388
|
GitHubAppConfigurationError,
|
|
391
389
|
githubAppMissingSettings
|
|
392
390
|
} from "@opengeni/github";
|
|
@@ -6253,7 +6251,6 @@ var FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
6253
6251
|
variable_set_set_variable: { allOf: ["variable-sets:manage"] },
|
|
6254
6252
|
environment_set_variable: { allOf: ["variable-sets:manage"] },
|
|
6255
6253
|
github_connect_link: { allOf: ["github:use"] },
|
|
6256
|
-
github_token: { sessionRequired: true, allOf: ["github:use"] },
|
|
6257
6254
|
github_repositories_list: { allOf: ["github:use"] },
|
|
6258
6255
|
social_connections_list: { allOf: ["connections:read"] },
|
|
6259
6256
|
social_posts_recent: { allOf: ["connections:read"] },
|
|
@@ -6411,9 +6408,6 @@ function buildOpenGeniMcpServer(deps, grant, options = {}) {
|
|
|
6411
6408
|
registerVariableSetTools(server, deps, grant, can, json);
|
|
6412
6409
|
if (can("github:use")) {
|
|
6413
6410
|
registerGitHubConnectTool(server, deps, grant, options, json);
|
|
6414
|
-
if (sessionId !== null) {
|
|
6415
|
-
registerGitHubTokenTool(server, deps, grant, sessionId, json);
|
|
6416
|
-
}
|
|
6417
6411
|
}
|
|
6418
6412
|
if (!toolspaceMode || can("github:use")) {
|
|
6419
6413
|
server.registerTool(
|
|
@@ -8508,50 +8502,6 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
|
|
|
8508
8502
|
}
|
|
8509
8503
|
);
|
|
8510
8504
|
}
|
|
8511
|
-
function registerGitHubTokenTool(server, deps, grant, sessionId, json) {
|
|
8512
|
-
server.registerTool(
|
|
8513
|
-
"github_token",
|
|
8514
|
-
{
|
|
8515
|
-
description: "Mint a fresh short-lived GitHub token for this session's repositories. Write it to $OPENGENI_GIT_TOKEN_FILE (default $HOME/.opengeni/git-token) to refresh git auth before the current token expires.",
|
|
8516
|
-
inputSchema: {}
|
|
8517
|
-
},
|
|
8518
|
-
async () => {
|
|
8519
|
-
const session = await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
8520
|
-
const selected = (session.resources ?? []).flatMap((resource) => {
|
|
8521
|
-
if (resource.kind !== "repository") {
|
|
8522
|
-
return [];
|
|
8523
|
-
}
|
|
8524
|
-
const installationId2 = resource.githubInstallationId;
|
|
8525
|
-
const repositoryId = resource.githubRepositoryId;
|
|
8526
|
-
return typeof installationId2 === "number" && installationId2 > 0 && typeof repositoryId === "number" && repositoryId > 0 ? [{ installationId: installationId2, repositoryId }] : [];
|
|
8527
|
-
});
|
|
8528
|
-
if (selected.length === 0) {
|
|
8529
|
-
throw new Error("this session has no GitHub App repository resources to mint a token for");
|
|
8530
|
-
}
|
|
8531
|
-
const installationId = selected[0].installationId;
|
|
8532
|
-
if (selected.some((item) => item.installationId !== installationId)) {
|
|
8533
|
-
throw new Error("GitHub App repository resources must belong to one installation");
|
|
8534
|
-
}
|
|
8535
|
-
const repositoryIds = selected.map((item) => item.repositoryId);
|
|
8536
|
-
if (!await areGitHubRepositoriesAllowedForWorkspace(
|
|
8537
|
-
deps.db,
|
|
8538
|
-
grant.workspaceId,
|
|
8539
|
-
installationId,
|
|
8540
|
-
repositoryIds
|
|
8541
|
-
)) {
|
|
8542
|
-
throw new Error("this workspace no longer authorizes the session's GitHub repositories");
|
|
8543
|
-
}
|
|
8544
|
-
const token = await createGitHubAppInstallationToken(deps.settings, {
|
|
8545
|
-
installationId,
|
|
8546
|
-
repositoryIds
|
|
8547
|
-
});
|
|
8548
|
-
return json({
|
|
8549
|
-
token,
|
|
8550
|
-
tokenFile: "$OPENGENI_GIT_TOKEN_FILE (default $HOME/.opengeni/git-token)"
|
|
8551
|
-
});
|
|
8552
|
-
}
|
|
8553
|
-
);
|
|
8554
|
-
}
|
|
8555
8505
|
function requireVariableSetsUseForMcpAttachment(grant, variableSetId) {
|
|
8556
8506
|
if (variableSetId !== void 0 && !hasPermission4(grant.permissions, "variable-sets:use")) {
|
|
8557
8507
|
throw new Error("missing permission: variable-sets:use");
|
|
@@ -8937,6 +8887,8 @@ import {
|
|
|
8937
8887
|
boundedParallelMap,
|
|
8938
8888
|
cancelMcpResponseBody,
|
|
8939
8889
|
guardedMcpFetch,
|
|
8890
|
+
mcpJsonRpcErrorPayloadForRequest,
|
|
8891
|
+
mcpRequestReplayInfo,
|
|
8940
8892
|
mcpSerializedSizeBytes
|
|
8941
8893
|
} from "@opengeni/runtime/mcp-network";
|
|
8942
8894
|
import { Buffer as Buffer4 } from "buffer";
|
|
@@ -8948,8 +8900,12 @@ var TOOLSPACE_AUTH_NEEDED_ERROR = {
|
|
|
8948
8900
|
code: 40101,
|
|
8949
8901
|
message: "Authentication required - a connection link was posted to the session."
|
|
8950
8902
|
};
|
|
8903
|
+
var TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR = {
|
|
8904
|
+
code: 40102,
|
|
8905
|
+
message: "Tool outcome uncertain: the provider returned 401 after receiving the request. OpenGeni did not replay this call. Do not retry automatically; verify provider state before any new attempt."
|
|
8906
|
+
};
|
|
8951
8907
|
var TOOLSPACE_NO_ACTIVE_TURN_MESSAGE = "no active turn - toolspace calls require an in-flight turn";
|
|
8952
|
-
var FIRST_PARTY_PROXY_IDS = /* @__PURE__ */ new Set(["files", "docs"]);
|
|
8908
|
+
var FIRST_PARTY_PROXY_IDS = /* @__PURE__ */ new Set(["files", "docs", "codex_apps"]);
|
|
8953
8909
|
var TOOLSPACE_TOOL_LIST_TTL_MS = 3e4;
|
|
8954
8910
|
var TOOLSPACE_TOOL_LIST_CACHE_MAX_ENTRIES = 2e3;
|
|
8955
8911
|
var TOOLSPACE_TOOL_LIST_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
@@ -9462,6 +9418,9 @@ async function callRemoteTool(deps, server, toolName, args) {
|
|
|
9462
9418
|
if (isToolspaceAuthNeededError(error)) {
|
|
9463
9419
|
return mcpError(TOOLSPACE_AUTH_NEEDED_ERROR.message);
|
|
9464
9420
|
}
|
|
9421
|
+
if (isToolspaceOutcomeUncertainError(error)) {
|
|
9422
|
+
return mcpError(TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR.message);
|
|
9423
|
+
}
|
|
9465
9424
|
deps.observability?.warn("toolspace upstream tool call failed", {
|
|
9466
9425
|
serverId: server.config.id,
|
|
9467
9426
|
toolName,
|
|
@@ -9588,7 +9547,7 @@ function connectionBrokerFetch(baseFetch, input) {
|
|
|
9588
9547
|
ownerHasWorkspaceMembership: delegatedOwnerHasMembership
|
|
9589
9548
|
});
|
|
9590
9549
|
return async (requestInput, init) => {
|
|
9591
|
-
const request = await
|
|
9550
|
+
const request = await mcpRequestReplayInfo(requestInput, init);
|
|
9592
9551
|
const destinationUrl = mcpRequestDestinationUrl(requestInput);
|
|
9593
9552
|
const resolverSubjectId = connectionRef.subjectScope !== "subject" && hostCredentialPort ? input.grant.subjectId : void 0;
|
|
9594
9553
|
const resolve = async (forceRefresh) => {
|
|
@@ -9613,10 +9572,22 @@ function connectionBrokerFetch(baseFetch, input) {
|
|
|
9613
9572
|
);
|
|
9614
9573
|
if (response.status === 401) {
|
|
9615
9574
|
await cancelMcpResponseBody(response);
|
|
9616
|
-
|
|
9575
|
+
let refreshed;
|
|
9576
|
+
try {
|
|
9577
|
+
refreshed = await resolve(true);
|
|
9578
|
+
} catch {
|
|
9579
|
+
refreshed = authNeededFromStatus(input.config, first, "refresh_failed");
|
|
9580
|
+
}
|
|
9617
9581
|
if (refreshed.status === "auth_needed") {
|
|
9582
|
+
if (!request.replaySafeAfter401) {
|
|
9583
|
+
await publishToolspaceAuthNeeded(input, request, refreshed);
|
|
9584
|
+
return toolspaceMcpOutcomeUncertainResponse(request);
|
|
9585
|
+
}
|
|
9618
9586
|
return await authNeededFetchResponse(input, request, refreshed);
|
|
9619
9587
|
}
|
|
9588
|
+
if (!request.replaySafeAfter401) {
|
|
9589
|
+
return toolspaceMcpOutcomeUncertainResponse(request);
|
|
9590
|
+
}
|
|
9620
9591
|
const retry = await baseFetch(
|
|
9621
9592
|
fetchInputForAttempt(requestInput),
|
|
9622
9593
|
withConnectionHeaders(requestInput, init, refreshed.headers)
|
|
@@ -9664,6 +9635,13 @@ function authNeededFromStatus(config, first, reason) {
|
|
|
9664
9635
|
};
|
|
9665
9636
|
}
|
|
9666
9637
|
async function authNeededFetchResponse(input, request, auth) {
|
|
9638
|
+
await publishToolspaceAuthNeeded(input, request, auth);
|
|
9639
|
+
if (request.method === "tools/call") {
|
|
9640
|
+
return toolspaceMcpErrorResponse(request.id, TOOLSPACE_AUTH_NEEDED_ERROR);
|
|
9641
|
+
}
|
|
9642
|
+
return new Response("Authentication required for MCP server connection", { status: 401 });
|
|
9643
|
+
}
|
|
9644
|
+
async function publishToolspaceAuthNeeded(input, request, auth) {
|
|
9667
9645
|
await appendAndPublishEvents3(
|
|
9668
9646
|
input.deps.db,
|
|
9669
9647
|
input.deps.bus,
|
|
@@ -9689,42 +9667,30 @@ async function authNeededFetchResponse(input, request, auth) {
|
|
|
9689
9667
|
}
|
|
9690
9668
|
]
|
|
9691
9669
|
).catch(() => void 0);
|
|
9692
|
-
if (request.method === "tools/call") {
|
|
9693
|
-
return new Response(
|
|
9694
|
-
JSON.stringify({
|
|
9695
|
-
jsonrpc: "2.0",
|
|
9696
|
-
id: request.id ?? null,
|
|
9697
|
-
error: {
|
|
9698
|
-
code: TOOLSPACE_AUTH_NEEDED_ERROR.code,
|
|
9699
|
-
message: TOOLSPACE_AUTH_NEEDED_ERROR.message
|
|
9700
|
-
}
|
|
9701
|
-
}),
|
|
9702
|
-
{
|
|
9703
|
-
status: 200,
|
|
9704
|
-
headers: { "content-type": "application/json" }
|
|
9705
|
-
}
|
|
9706
|
-
);
|
|
9707
|
-
}
|
|
9708
|
-
return new Response("Authentication required for MCP server connection", { status: 401 });
|
|
9709
9670
|
}
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9671
|
+
function toolspaceMcpErrorResponse(id, error) {
|
|
9672
|
+
return new Response(
|
|
9673
|
+
JSON.stringify({
|
|
9674
|
+
jsonrpc: "2.0",
|
|
9675
|
+
id: id ?? null,
|
|
9676
|
+
error
|
|
9677
|
+
}),
|
|
9678
|
+
{
|
|
9679
|
+
status: 200,
|
|
9680
|
+
headers: { "content-type": "application/json" }
|
|
9681
|
+
}
|
|
9682
|
+
);
|
|
9683
|
+
}
|
|
9684
|
+
function toolspaceMcpOutcomeUncertainResponse(request) {
|
|
9685
|
+
return new Response(
|
|
9686
|
+
JSON.stringify(
|
|
9687
|
+
mcpJsonRpcErrorPayloadForRequest(request, TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR)
|
|
9688
|
+
),
|
|
9689
|
+
{
|
|
9690
|
+
status: 200,
|
|
9691
|
+
headers: { "content-type": "application/json" }
|
|
9692
|
+
}
|
|
9693
|
+
);
|
|
9728
9694
|
}
|
|
9729
9695
|
function withConnectionHeaders(input, init, authHeaders) {
|
|
9730
9696
|
const headers = new Headers(
|
|
@@ -9745,6 +9711,13 @@ function isToolspaceAuthNeededError(error) {
|
|
|
9745
9711
|
const code = error.code;
|
|
9746
9712
|
return code === TOOLSPACE_AUTH_NEEDED_ERROR.code && (error.message === TOOLSPACE_AUTH_NEEDED_ERROR.message || error.message === `MCP error ${TOOLSPACE_AUTH_NEEDED_ERROR.code}: ${TOOLSPACE_AUTH_NEEDED_ERROR.message}`);
|
|
9747
9713
|
}
|
|
9714
|
+
function isToolspaceOutcomeUncertainError(error) {
|
|
9715
|
+
if (!(error instanceof Error)) {
|
|
9716
|
+
return false;
|
|
9717
|
+
}
|
|
9718
|
+
const code = error.code;
|
|
9719
|
+
return code === TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR.code && (error.message === TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR.message || error.message === `MCP error ${TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR.code}: ${TOOLSPACE_TOOL_OUTCOME_UNCERTAIN_ERROR.message}`);
|
|
9720
|
+
}
|
|
9748
9721
|
|
|
9749
9722
|
// src/app.ts
|
|
9750
9723
|
import { boundedMcpRequest, McpPayloadTooLargeError as McpPayloadTooLargeError2 } from "@opengeni/runtime/mcp-network";
|
|
@@ -10166,6 +10139,8 @@ import {
|
|
|
10166
10139
|
buildCodexTokenResolver,
|
|
10167
10140
|
claimCodexResetRedemption,
|
|
10168
10141
|
completeCodexResetRedemption,
|
|
10142
|
+
clearCodexAppsCredential,
|
|
10143
|
+
designateCodexAppsCredential,
|
|
10169
10144
|
disconnectAllCodexAccounts,
|
|
10170
10145
|
disconnectCodexAccount,
|
|
10171
10146
|
encryptEnvironmentValue as encryptEnvironmentValue3,
|
|
@@ -10175,6 +10150,7 @@ import {
|
|
|
10175
10150
|
fenceCodexResetRedemptionSend,
|
|
10176
10151
|
getCodexResetRedemptionAttempt,
|
|
10177
10152
|
getCodexCredentialStatus,
|
|
10153
|
+
getCodexAppsSettings,
|
|
10178
10154
|
getCodexRotationSettings,
|
|
10179
10155
|
listPendingCodexCapacityWakeTargets,
|
|
10180
10156
|
listCodexAccountStatuses,
|
|
@@ -10255,7 +10231,7 @@ async function verifyCodexRedemptionConfirmation(secret, token, now = Date.now()
|
|
|
10255
10231
|
|
|
10256
10232
|
// src/routes/codex.ts
|
|
10257
10233
|
var CODEX_PROVIDER_LABEL = "Codex subscription \xB7 no credits";
|
|
10258
|
-
function codexAccountJson(row) {
|
|
10234
|
+
function codexAccountJson(row, options = {}) {
|
|
10259
10235
|
return {
|
|
10260
10236
|
id: row.id,
|
|
10261
10237
|
chatgptAccountId: row.chatgptAccountId,
|
|
@@ -10284,7 +10260,9 @@ function codexAccountJson(row) {
|
|
|
10284
10260
|
resetCreditAvailableCount: row.resetCreditAvailableCount,
|
|
10285
10261
|
resetCreditsCheckedAt: row.resetCreditsCheckedAt,
|
|
10286
10262
|
// P3 rotation cooldown: when set and in the future, this account is cooling-down.
|
|
10287
|
-
exhaustedUntil: row.exhaustedUntil
|
|
10263
|
+
exhaustedUntil: row.exhaustedUntil,
|
|
10264
|
+
appsDesignated: options.appsCredentialId === row.id,
|
|
10265
|
+
canEnableApps: options.appsCredentialId === null && options.canManageApps === true && options.humanSubjectId !== null && options.humanSubjectId !== void 0 && row.connectedBySubjectId === options.humanSubjectId && row.status === "active"
|
|
10288
10266
|
};
|
|
10289
10267
|
}
|
|
10290
10268
|
function codexUsageJson(payload) {
|
|
@@ -10378,6 +10356,23 @@ async function requireRedemptionHuman(c, deps, workspaceId) {
|
|
|
10378
10356
|
}
|
|
10379
10357
|
return { human, accountId: grant.accountId };
|
|
10380
10358
|
}
|
|
10359
|
+
async function requireCodexAppsHuman(c, deps, workspaceId) {
|
|
10360
|
+
if (c.req.header("authorization")) {
|
|
10361
|
+
throw new HTTPException10(403, {
|
|
10362
|
+
message: "authorization bearer is not allowed for Codex Apps designation"
|
|
10363
|
+
});
|
|
10364
|
+
}
|
|
10365
|
+
requireSameOriginBrowserMutation(c, deps);
|
|
10366
|
+
const human = await managedCookieHuman(c, deps);
|
|
10367
|
+
if (!human) {
|
|
10368
|
+
throw new HTTPException10(401, { message: "managed browser session required" });
|
|
10369
|
+
}
|
|
10370
|
+
const grant = await requireAccessGrant2(c, deps, workspaceId, "connections:write");
|
|
10371
|
+
if (grant.subjectId !== human.subjectId) {
|
|
10372
|
+
throw new HTTPException10(403, { message: "managed browser identity mismatch" });
|
|
10373
|
+
}
|
|
10374
|
+
return { human, accountId: grant.accountId };
|
|
10375
|
+
}
|
|
10381
10376
|
function cachedUsage(row) {
|
|
10382
10377
|
const fiveHour = buildCodexUsageWindowFromCache(
|
|
10383
10378
|
row.primaryUsedPercent,
|
|
@@ -10733,15 +10728,32 @@ function registerCodexRoutes(app, deps) {
|
|
|
10733
10728
|
});
|
|
10734
10729
|
app.get("/v1/workspaces/:workspaceId/codex/accounts", async (c) => {
|
|
10735
10730
|
const workspaceId = c.req.param("workspaceId");
|
|
10736
|
-
await requireAccessGrant2(c, deps, workspaceId, "workspace:read");
|
|
10737
|
-
const [accounts, rotation] = await Promise.all([
|
|
10731
|
+
const grant = await requireAccessGrant2(c, deps, workspaceId, "workspace:read");
|
|
10732
|
+
const [accounts, rotation, apps, human] = await Promise.all([
|
|
10738
10733
|
listCodexAccountStatuses(db, workspaceId),
|
|
10739
|
-
getCodexRotationSettings(db, workspaceId)
|
|
10734
|
+
getCodexRotationSettings(db, workspaceId),
|
|
10735
|
+
getCodexAppsSettings(db, workspaceId),
|
|
10736
|
+
managedCookieHuman(c, deps)
|
|
10740
10737
|
]);
|
|
10741
10738
|
const activeAccountId = rotation?.activeCredentialId ?? null;
|
|
10739
|
+
const humanSubjectId = human?.subjectId === grant.subjectId ? human.subjectId : null;
|
|
10740
|
+
const canManageApps = humanSubjectId !== null && hasPermission6(grant.permissions, "connections:write");
|
|
10742
10741
|
return c.json({
|
|
10743
|
-
accounts: accounts.map(
|
|
10742
|
+
accounts: accounts.map(
|
|
10743
|
+
(account) => codexAccountJson(account, {
|
|
10744
|
+
appsCredentialId: apps.credentialId,
|
|
10745
|
+
canManageApps: settings.codexConnectedAppsEnabled && canManageApps,
|
|
10746
|
+
humanSubjectId
|
|
10747
|
+
})
|
|
10748
|
+
),
|
|
10744
10749
|
activeAccountId,
|
|
10750
|
+
apps: {
|
|
10751
|
+
available: settings.codexConnectedAppsEnabled,
|
|
10752
|
+
credentialId: apps.credentialId,
|
|
10753
|
+
version: apps.version,
|
|
10754
|
+
designatedAt: apps.designatedAt,
|
|
10755
|
+
canDisable: canManageApps && apps.credentialId !== null
|
|
10756
|
+
},
|
|
10745
10757
|
settings: {
|
|
10746
10758
|
rotationEnabled: rotation?.rotationEnabled ?? false,
|
|
10747
10759
|
// sharded-rotation policy: rotation-enabled always behaves as sticky-sharded; report the
|
|
@@ -10751,6 +10763,72 @@ function registerCodexRoutes(app, deps) {
|
|
|
10751
10763
|
}
|
|
10752
10764
|
});
|
|
10753
10765
|
});
|
|
10766
|
+
app.post("/v1/workspaces/:workspaceId/codex/apps", async (c) => {
|
|
10767
|
+
const workspaceId = c.req.param("workspaceId");
|
|
10768
|
+
if (!settings.codexConnectedAppsEnabled) {
|
|
10769
|
+
throw new HTTPException10(409, { message: "Codex Apps is disabled for this deployment" });
|
|
10770
|
+
}
|
|
10771
|
+
const { human, accountId } = await requireCodexAppsHuman(c, deps, workspaceId);
|
|
10772
|
+
const parsed = z.object({
|
|
10773
|
+
accountId: z.string().uuid(),
|
|
10774
|
+
expectedVersion: z.number().int().nonnegative()
|
|
10775
|
+
}).safeParse(await c.req.json().catch(() => null));
|
|
10776
|
+
if (!parsed.success) {
|
|
10777
|
+
throw new HTTPException10(400, { message: "accountId and expectedVersion are required" });
|
|
10778
|
+
}
|
|
10779
|
+
const result = await designateCodexAppsCredential(db, {
|
|
10780
|
+
accountId,
|
|
10781
|
+
workspaceId,
|
|
10782
|
+
credentialId: parsed.data.accountId,
|
|
10783
|
+
subjectId: human.subjectId,
|
|
10784
|
+
expectedVersion: parsed.data.expectedVersion
|
|
10785
|
+
});
|
|
10786
|
+
if (result.kind === "not_found") {
|
|
10787
|
+
throw new HTTPException10(404, { message: "codex account not found" });
|
|
10788
|
+
}
|
|
10789
|
+
if (result.kind === "not_owner") {
|
|
10790
|
+
throw new HTTPException10(403, {
|
|
10791
|
+
message: "only the managed human who connected this subscription may designate it"
|
|
10792
|
+
});
|
|
10793
|
+
}
|
|
10794
|
+
if (result.kind === "forbidden") {
|
|
10795
|
+
throw new HTTPException10(403, { message: "missing permission: connections:write" });
|
|
10796
|
+
}
|
|
10797
|
+
if (result.kind === "unavailable") {
|
|
10798
|
+
throw new HTTPException10(409, { message: "codex account requires relogin" });
|
|
10799
|
+
}
|
|
10800
|
+
const response = {
|
|
10801
|
+
credentialId: result.credentialId,
|
|
10802
|
+
version: result.version,
|
|
10803
|
+
designatedAt: result.designatedAt,
|
|
10804
|
+
changed: result.kind === "updated"
|
|
10805
|
+
};
|
|
10806
|
+
return result.kind === "updated" ? c.json(response) : c.json(response, 409);
|
|
10807
|
+
});
|
|
10808
|
+
app.delete("/v1/workspaces/:workspaceId/codex/apps", async (c) => {
|
|
10809
|
+
const workspaceId = c.req.param("workspaceId");
|
|
10810
|
+
const { human, accountId } = await requireCodexAppsHuman(c, deps, workspaceId);
|
|
10811
|
+
const parsed = z.object({ expectedVersion: z.number().int().nonnegative() }).safeParse(await c.req.json().catch(() => null));
|
|
10812
|
+
if (!parsed.success) {
|
|
10813
|
+
throw new HTTPException10(400, { message: "expectedVersion is required" });
|
|
10814
|
+
}
|
|
10815
|
+
const result = await clearCodexAppsCredential(db, {
|
|
10816
|
+
accountId,
|
|
10817
|
+
workspaceId,
|
|
10818
|
+
subjectId: human.subjectId,
|
|
10819
|
+
expectedVersion: parsed.data.expectedVersion
|
|
10820
|
+
});
|
|
10821
|
+
if (result.kind === "forbidden") {
|
|
10822
|
+
throw new HTTPException10(403, { message: "missing permission: connections:write" });
|
|
10823
|
+
}
|
|
10824
|
+
const response = {
|
|
10825
|
+
credentialId: result.credentialId,
|
|
10826
|
+
version: result.version,
|
|
10827
|
+
designatedAt: result.designatedAt,
|
|
10828
|
+
changed: result.kind === "updated"
|
|
10829
|
+
};
|
|
10830
|
+
return result.kind === "conflict" ? c.json(response, 409) : c.json(response);
|
|
10831
|
+
});
|
|
10754
10832
|
app.post("/v1/workspaces/:workspaceId/codex/accounts/:accountId/activate", async (c) => {
|
|
10755
10833
|
const workspaceId = c.req.param("workspaceId");
|
|
10756
10834
|
await requireAccessGrant2(c, deps, workspaceId, "workspace:admin");
|
|
@@ -10859,13 +10937,13 @@ function registerCodexRoutes(app, deps) {
|
|
|
10859
10937
|
});
|
|
10860
10938
|
app.delete("/v1/workspaces/:workspaceId/codex/accounts/:accountId", async (c) => {
|
|
10861
10939
|
const workspaceId = c.req.param("workspaceId");
|
|
10862
|
-
await requireAccessGrant2(c, deps, workspaceId, "workspace:admin");
|
|
10940
|
+
const grant = await requireAccessGrant2(c, deps, workspaceId, "workspace:admin");
|
|
10863
10941
|
const accountId = c.req.param("accountId");
|
|
10864
10942
|
const mutation = await withCodexCapacityMutation(
|
|
10865
10943
|
db,
|
|
10866
10944
|
{ workspaceId, reason: "codex_credential_disconnected" },
|
|
10867
10945
|
async (tx) => {
|
|
10868
|
-
const result2 = await disconnectCodexAccount(tx, workspaceId, accountId);
|
|
10946
|
+
const result2 = await disconnectCodexAccount(tx, workspaceId, accountId, grant.subjectId);
|
|
10869
10947
|
return { result: result2, changed: result2.removed };
|
|
10870
10948
|
}
|
|
10871
10949
|
);
|
|
@@ -10880,12 +10958,12 @@ function registerCodexRoutes(app, deps) {
|
|
|
10880
10958
|
});
|
|
10881
10959
|
app.delete("/v1/workspaces/:workspaceId/codex", async (c) => {
|
|
10882
10960
|
const workspaceId = c.req.param("workspaceId");
|
|
10883
|
-
await requireAccessGrant2(c, deps, workspaceId, "workspace:admin");
|
|
10961
|
+
const grant = await requireAccessGrant2(c, deps, workspaceId, "workspace:admin");
|
|
10884
10962
|
const mutation = await withCodexCapacityMutation(
|
|
10885
10963
|
db,
|
|
10886
10964
|
{ workspaceId, reason: "codex_credentials_disconnected" },
|
|
10887
10965
|
async (tx) => {
|
|
10888
|
-
const result2 = await disconnectAllCodexAccounts(tx, workspaceId);
|
|
10966
|
+
const result2 = await disconnectAllCodexAccounts(tx, workspaceId, grant.subjectId);
|
|
10889
10967
|
return { result: result2, changed: result2.removed > 0 };
|
|
10890
10968
|
}
|
|
10891
10969
|
);
|
|
@@ -26617,4 +26695,4 @@ export {
|
|
|
26617
26695
|
withDefaultEnabledCapabilityMcpTools,
|
|
26618
26696
|
workflowIdForSession2 as workflowIdForSession
|
|
26619
26697
|
};
|
|
26620
|
-
//# sourceMappingURL=chunk-
|
|
26698
|
+
//# sourceMappingURL=chunk-R5PDSH2A.js.map
|