@opengeni/api-router 2.6.3-canary.0 → 2.6.4-canary.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 +2 -1
- package/dist/app.js +3 -1
- package/dist/{chunk-XTLI3CBH.js → chunk-UOI7KAP3.js} +1010 -410
- package/dist/chunk-UOI7KAP3.js.map +1 -0
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/model-catalog.d.ts +5 -31
- package/package.json +18 -18
- package/src/app.ts +34 -9
- package/src/index.ts +13 -1
- package/src/integrations/slack-interactions.ts +77 -37
- package/src/mcp/server.ts +11 -2
- package/src/model-catalog.ts +45 -337
- package/src/routes/automations.ts +186 -19
- package/src/routes/connections.ts +271 -16
- package/src/routes/github.ts +0 -8
- package/src/routes/pr-review.ts +80 -4
- package/src/routes/scheduled-tasks.ts +40 -13
- package/src/routes/sessions.ts +72 -47
- package/src/routes/supergrok.ts +3 -2
- package/src/routes/workspaces.ts +373 -3
- package/dist/chunk-XTLI3CBH.js.map +0 -1
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
canonicalizeConfiguredModelId as canonicalizeConfiguredModelId2,
|
|
4
4
|
configuredAllowedModels,
|
|
5
5
|
configuredAllowedReasoningEfforts,
|
|
6
|
-
configuredModels as
|
|
6
|
+
configuredModels as configuredModels2,
|
|
7
7
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2,
|
|
8
|
-
withCodexCatalogProvider
|
|
9
|
-
withXaiSubscriptionCatalogProvider as
|
|
8
|
+
withCodexCatalogProvider,
|
|
9
|
+
withXaiSubscriptionCatalogProvider as withXaiSubscriptionCatalogProvider2
|
|
10
10
|
} from "@opengeni/config";
|
|
11
11
|
import {
|
|
12
12
|
ClientConfig,
|
|
@@ -116,6 +116,8 @@ import {
|
|
|
116
116
|
requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization3,
|
|
117
117
|
requirePermission as requirePermission9,
|
|
118
118
|
releaseManagedAuthRequestActorLease,
|
|
119
|
+
resolveCatalogSettings,
|
|
120
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings8,
|
|
119
121
|
validateManagedAuthRequestActorLease,
|
|
120
122
|
requireSessionAuthorization as requireSessionAuthorization9,
|
|
121
123
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError10,
|
|
@@ -2379,6 +2381,7 @@ import {
|
|
|
2379
2381
|
requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization2,
|
|
2380
2382
|
requireSessionAuthorization as requireSessionAuthorization2,
|
|
2381
2383
|
requireSessionAuthorizationListScope,
|
|
2384
|
+
resolveWorkspaceCatalogSettings,
|
|
2382
2385
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError3,
|
|
2383
2386
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError2,
|
|
2384
2387
|
saveWorkspaceMemoryWithSlackPublication,
|
|
@@ -19891,7 +19894,7 @@ function buildOpenGeniMcpServer(deps, grant, options = {}) {
|
|
|
19891
19894
|
async ({ id, triggerId }) => {
|
|
19892
19895
|
const task = await requireScheduledTask(deps.db, grant.workspaceId, id);
|
|
19893
19896
|
if (task.action.kind === "agent_turn") {
|
|
19894
|
-
await validateScheduledTaskTarget({
|
|
19897
|
+
const targetSession = await validateScheduledTaskTarget({
|
|
19895
19898
|
db: deps.db,
|
|
19896
19899
|
sessionAuthorization: deps.sessionAuthorization,
|
|
19897
19900
|
authorizationSurface: "first_party_mcp",
|
|
@@ -19903,8 +19906,14 @@ function buildOpenGeniMcpServer(deps, grant, options = {}) {
|
|
|
19903
19906
|
agentConfig: task.agentConfig,
|
|
19904
19907
|
missingTargetStatus: 404
|
|
19905
19908
|
});
|
|
19909
|
+
const catalogSourceSettings = deps.catalogSourceSettings ?? deps.settings;
|
|
19910
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings(deps.db, catalogSourceSettings, {
|
|
19911
|
+
accountId: grant.accountId,
|
|
19912
|
+
workspaceId: grant.workspaceId,
|
|
19913
|
+
...targetSession ? { retainedProductModelId: targetSession.model } : {}
|
|
19914
|
+
})).settings;
|
|
19906
19915
|
await validateScheduledTaskMachineTarget({
|
|
19907
|
-
settings:
|
|
19916
|
+
settings: catalogSettings,
|
|
19908
19917
|
db: deps.db,
|
|
19909
19918
|
grant,
|
|
19910
19919
|
runMode: task.runMode,
|
|
@@ -26078,9 +26087,9 @@ function registerCodexRoutes(app, deps) {
|
|
|
26078
26087
|
|
|
26079
26088
|
// src/routes/supergrok.ts
|
|
26080
26089
|
import {
|
|
26081
|
-
configuredModels
|
|
26090
|
+
configuredModels,
|
|
26082
26091
|
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3,
|
|
26083
|
-
withXaiSubscriptionCatalogProvider
|
|
26092
|
+
withXaiSubscriptionCatalogProvider
|
|
26084
26093
|
} from "@opengeni/config";
|
|
26085
26094
|
import {
|
|
26086
26095
|
WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1
|
|
@@ -26119,22 +26128,21 @@ import * as z10 from "zod/v4";
|
|
|
26119
26128
|
|
|
26120
26129
|
// src/model-catalog.ts
|
|
26121
26130
|
import {
|
|
26122
|
-
|
|
26123
|
-
|
|
26124
|
-
withCodexCatalogProvider,
|
|
26125
|
-
withXaiSubscriptionCatalogProvider,
|
|
26126
|
-
withWorkspaceGatewayCatalogProvider
|
|
26131
|
+
OPENROUTER_PROVIDER_ID,
|
|
26132
|
+
WORKSPACE_OPENROUTER_PROVIDER_ID
|
|
26127
26133
|
} from "@opengeni/config";
|
|
26128
26134
|
import {
|
|
26129
26135
|
ClientModel,
|
|
26130
|
-
WorkspaceModelCatalogResponse
|
|
26131
|
-
evaluateWorkspaceModelPolicy
|
|
26136
|
+
WorkspaceModelCatalogResponse
|
|
26132
26137
|
} from "@opengeni/contracts";
|
|
26133
|
-
|
|
26138
|
+
import { resolveWorkspaceModelSelection } from "@opengeni/core";
|
|
26139
|
+
import {
|
|
26140
|
+
MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS
|
|
26141
|
+
} from "@opengeni/core";
|
|
26134
26142
|
function projectClientModel(model) {
|
|
26135
26143
|
const anonymousProvider = model.credentialSource.kind === "deployment" && model.credentialSource.mechanism === "none";
|
|
26136
|
-
const source = model.credentialSource.kind === "connected_subscription" ? model.credentialSource.provider === "xai" ? "supergrok" : "codex" : model.credentialSource.kind === "workspace_connection" ? "workspace_gateway" : anonymousProvider ? void 0 : "opengeni";
|
|
26137
|
-
const publicProvider = anonymousProvider ? { provider: model.providerId, providerLabel: model.providerLabel } : source === "codex" ? { provider: "codex", providerLabel: "Codex" } : source === "supergrok" ? { provider: "supergrok", providerLabel: "SuperGrok" } : source === "workspace_gateway" ? { provider: "workspace-gateway", providerLabel: "Your Gateway" } : { provider: "opengeni", providerLabel: "OpenGeni" };
|
|
26144
|
+
const source = model.providerId === OPENROUTER_PROVIDER_ID || model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID ? void 0 : model.credentialSource.kind === "connected_subscription" ? model.credentialSource.provider === "xai" ? "supergrok" : "codex" : model.credentialSource.kind === "workspace_connection" ? "workspace_gateway" : anonymousProvider ? void 0 : "opengeni";
|
|
26145
|
+
const publicProvider = anonymousProvider ? { provider: model.providerId, providerLabel: model.providerLabel } : model.providerId === OPENROUTER_PROVIDER_ID ? { provider: "openrouter", providerLabel: "OpenRouter" } : model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID ? { provider: "workspace-openrouter", providerLabel: "Your OpenRouter" } : source === "codex" ? { provider: "codex", providerLabel: "Codex" } : source === "supergrok" ? { provider: "supergrok", providerLabel: "SuperGrok" } : source === "workspace_gateway" ? { provider: "workspace-gateway", providerLabel: "Your Gateway" } : { provider: "opengeni", providerLabel: "OpenGeni" };
|
|
26138
26146
|
return ClientModel.parse({
|
|
26139
26147
|
id: model.id,
|
|
26140
26148
|
label: model.label,
|
|
@@ -26147,217 +26155,19 @@ function projectClientModel(model) {
|
|
|
26147
26155
|
aliases: model.aliases,
|
|
26148
26156
|
executionLimits: model.executionLimits,
|
|
26149
26157
|
billing: model.billing,
|
|
26158
|
+
cost: model.cost,
|
|
26150
26159
|
capabilities: model.capabilities,
|
|
26151
26160
|
...model.pricing === void 0 ? {} : { pricing: model.pricing },
|
|
26152
26161
|
definitionVersion: model.definitionVersion
|
|
26153
26162
|
});
|
|
26154
26163
|
}
|
|
26155
|
-
function modelDefinitionRunnable(model) {
|
|
26156
|
-
return model.capabilities.inputModalities.includes("text") && model.capabilities.outputModalities.includes("text") && model.capabilities.transports.sse.runnable;
|
|
26157
|
-
}
|
|
26158
|
-
function observedCredentialReadiness(input) {
|
|
26159
|
-
if (!input.observation) {
|
|
26160
|
-
return {
|
|
26161
|
-
status: "not_ready",
|
|
26162
|
-
reason: "prerequisites_missing",
|
|
26163
|
-
basis: input.basis,
|
|
26164
|
-
checkedAt: null
|
|
26165
|
-
};
|
|
26166
|
-
}
|
|
26167
|
-
const checkedAtMs = Date.parse(input.observation.checkedAt);
|
|
26168
|
-
if (!Number.isFinite(checkedAtMs)) {
|
|
26169
|
-
return {
|
|
26170
|
-
status: "error",
|
|
26171
|
-
reason: "resolver_error",
|
|
26172
|
-
basis: input.basis,
|
|
26173
|
-
checkedAt: null
|
|
26174
|
-
};
|
|
26175
|
-
}
|
|
26176
|
-
const checkedAt = new Date(checkedAtMs).toISOString();
|
|
26177
|
-
if (Math.abs(input.nowMs - checkedAtMs) > input.maxAgeMs) {
|
|
26178
|
-
return {
|
|
26179
|
-
status: "not_ready",
|
|
26180
|
-
reason: "observation_stale",
|
|
26181
|
-
basis: input.basis,
|
|
26182
|
-
checkedAt
|
|
26183
|
-
};
|
|
26184
|
-
}
|
|
26185
|
-
if (input.observation.status === "ready") {
|
|
26186
|
-
return { status: "ready", reason: null, basis: input.basis, checkedAt };
|
|
26187
|
-
}
|
|
26188
|
-
if (input.observation.status === "not_ready") {
|
|
26189
|
-
return {
|
|
26190
|
-
status: "not_ready",
|
|
26191
|
-
reason: input.observation.reason === "needs_reauth" ? "needs_reauth" : "prerequisites_missing",
|
|
26192
|
-
basis: input.basis,
|
|
26193
|
-
checkedAt
|
|
26194
|
-
};
|
|
26195
|
-
}
|
|
26196
|
-
return {
|
|
26197
|
-
status: "error",
|
|
26198
|
-
reason: "resolver_error",
|
|
26199
|
-
basis: input.basis,
|
|
26200
|
-
checkedAt
|
|
26201
|
-
};
|
|
26202
|
-
}
|
|
26203
|
-
function credentialReadinessFor(input) {
|
|
26204
|
-
const source = input.model.credentialSource;
|
|
26205
|
-
if (source.kind === "connected_subscription") {
|
|
26206
|
-
const active = source.provider === "xai" ? input.xaiSubscriptionActive : input.codexSubscriptionActive;
|
|
26207
|
-
return active ? { status: "ready", reason: null, basis: "connection", checkedAt: null } : {
|
|
26208
|
-
status: "not_ready",
|
|
26209
|
-
reason: "needs_reauth",
|
|
26210
|
-
basis: "connection",
|
|
26211
|
-
checkedAt: null
|
|
26212
|
-
};
|
|
26213
|
-
}
|
|
26214
|
-
if (source.kind === "workspace_connection") {
|
|
26215
|
-
return input.workspaceGatewayConnectionActive ? { status: "ready", reason: null, basis: "connection", checkedAt: null } : {
|
|
26216
|
-
status: "not_ready",
|
|
26217
|
-
reason: "needs_reauth",
|
|
26218
|
-
basis: "connection",
|
|
26219
|
-
checkedAt: null
|
|
26220
|
-
};
|
|
26221
|
-
}
|
|
26222
|
-
if (source.kind === "deployment" && source.mechanism === "none") {
|
|
26223
|
-
return { status: "ready", reason: null, basis: "configuration", checkedAt: null };
|
|
26224
|
-
}
|
|
26225
|
-
if (source.kind === "deployment" && source.mechanism === "api_key") {
|
|
26226
|
-
return input.provider?.apiKey ? { status: "ready", reason: null, basis: "configuration", checkedAt: null } : {
|
|
26227
|
-
status: "not_ready",
|
|
26228
|
-
reason: "missing_credential",
|
|
26229
|
-
basis: "configuration",
|
|
26230
|
-
checkedAt: null
|
|
26231
|
-
};
|
|
26232
|
-
}
|
|
26233
|
-
return observedCredentialReadiness({
|
|
26234
|
-
observation: input.observation,
|
|
26235
|
-
basis: "resolver",
|
|
26236
|
-
nowMs: input.nowMs,
|
|
26237
|
-
maxAgeMs: input.maxAgeMs
|
|
26238
|
-
});
|
|
26239
|
-
}
|
|
26240
|
-
function isXaiGrokModel(model) {
|
|
26241
|
-
return model.providerId === "xai" && model.id.startsWith("xai/grok-");
|
|
26242
|
-
}
|
|
26243
|
-
function observationTimestamp(observation) {
|
|
26244
|
-
if (!observation || typeof observation.checkedAt !== "string") {
|
|
26245
|
-
return { checkedAt: null, checkedAtMs: null };
|
|
26246
|
-
}
|
|
26247
|
-
const checkedAtMs = Date.parse(observation.checkedAt);
|
|
26248
|
-
if (!Number.isFinite(checkedAtMs)) {
|
|
26249
|
-
return { checkedAt: null, checkedAtMs: null };
|
|
26250
|
-
}
|
|
26251
|
-
return { checkedAt: new Date(checkedAtMs).toISOString(), checkedAtMs };
|
|
26252
|
-
}
|
|
26253
|
-
function xaiGrokAvailabilityFor(input) {
|
|
26254
|
-
const { checkedAt, checkedAtMs } = observationTimestamp(input.observation);
|
|
26255
|
-
const freshSuccessfulObservation = input.observation?.status === "available" && input.observation.reason === null && checkedAtMs !== null && checkedAtMs <= input.nowMs && input.nowMs - checkedAtMs <= input.maxAgeMs;
|
|
26256
|
-
if (freshSuccessfulObservation) {
|
|
26257
|
-
return {
|
|
26258
|
-
status: "available",
|
|
26259
|
-
selectable: true,
|
|
26260
|
-
reason: null,
|
|
26261
|
-
checkedAt
|
|
26262
|
-
};
|
|
26263
|
-
}
|
|
26264
|
-
return {
|
|
26265
|
-
status: "unavailable",
|
|
26266
|
-
selectable: false,
|
|
26267
|
-
reason: input.observation?.status === "unavailable" ? input.observation.reason ?? "provider_unhealthy" : "provider_unhealthy",
|
|
26268
|
-
checkedAt
|
|
26269
|
-
};
|
|
26270
|
-
}
|
|
26271
|
-
function availabilityFor(input) {
|
|
26272
|
-
if (!modelDefinitionRunnable(input.model)) {
|
|
26273
|
-
return {
|
|
26274
|
-
status: "unavailable",
|
|
26275
|
-
selectable: false,
|
|
26276
|
-
reason: "unsupported",
|
|
26277
|
-
checkedAt: null
|
|
26278
|
-
};
|
|
26279
|
-
}
|
|
26280
|
-
if (input.credentialReadiness.status !== "ready") {
|
|
26281
|
-
return {
|
|
26282
|
-
status: "unavailable",
|
|
26283
|
-
selectable: false,
|
|
26284
|
-
reason: input.credentialReadiness.reason === "missing_credential" ? "missing_credential" : input.credentialReadiness.reason === "needs_reauth" ? "needs_reauth" : "credential_not_ready",
|
|
26285
|
-
checkedAt: input.credentialReadiness.checkedAt
|
|
26286
|
-
};
|
|
26287
|
-
}
|
|
26288
|
-
if (!input.policyAllowed) {
|
|
26289
|
-
return {
|
|
26290
|
-
status: "unavailable",
|
|
26291
|
-
selectable: false,
|
|
26292
|
-
reason: "policy_blocked",
|
|
26293
|
-
checkedAt: null
|
|
26294
|
-
};
|
|
26295
|
-
}
|
|
26296
|
-
if (isXaiGrokModel(input.model)) {
|
|
26297
|
-
return xaiGrokAvailabilityFor({
|
|
26298
|
-
observation: input.observation,
|
|
26299
|
-
nowMs: input.nowMs,
|
|
26300
|
-
maxAgeMs: input.maxAgeMs
|
|
26301
|
-
});
|
|
26302
|
-
}
|
|
26303
|
-
if (!input.observation) {
|
|
26304
|
-
return { status: "unknown", selectable: true, reason: null, checkedAt: null };
|
|
26305
|
-
}
|
|
26306
|
-
if (input.observation.status === "unavailable") {
|
|
26307
|
-
return {
|
|
26308
|
-
status: "unavailable",
|
|
26309
|
-
selectable: false,
|
|
26310
|
-
reason: input.observation.reason ?? "provider_unhealthy",
|
|
26311
|
-
checkedAt: input.observation.checkedAt
|
|
26312
|
-
};
|
|
26313
|
-
}
|
|
26314
|
-
return {
|
|
26315
|
-
status: input.observation.status,
|
|
26316
|
-
selectable: true,
|
|
26317
|
-
reason: null,
|
|
26318
|
-
checkedAt: input.observation.checkedAt
|
|
26319
|
-
};
|
|
26320
|
-
}
|
|
26321
26164
|
function buildWorkspaceModelCatalog(input) {
|
|
26322
|
-
const
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
);
|
|
26328
|
-
const requestedNowMs = input.now?.getTime();
|
|
26329
|
-
const nowMs = typeof requestedNowMs === "number" && Number.isFinite(requestedNowMs) ? requestedNowMs : Date.now();
|
|
26330
|
-
const maxAgeMs = typeof input.credentialReadinessMaxAgeMs === "number" && Number.isFinite(input.credentialReadinessMaxAgeMs) && input.credentialReadinessMaxAgeMs >= 0 ? input.credentialReadinessMaxAgeMs : MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS;
|
|
26331
|
-
const models = configuredModels(catalogSettings).map((model) => {
|
|
26332
|
-
const provider = providers.get(model.providerId);
|
|
26333
|
-
const policyAllowed = evaluateWorkspaceModelPolicy(input.policy, {
|
|
26334
|
-
providerId: model.providerId,
|
|
26335
|
-
modelId: model.id
|
|
26336
|
-
}).allowed;
|
|
26337
|
-
const credentialReadiness = credentialReadinessFor({
|
|
26338
|
-
model,
|
|
26339
|
-
provider,
|
|
26340
|
-
codexSubscriptionActive: input.codexSubscriptionActive,
|
|
26341
|
-
xaiSubscriptionActive: input.xaiSubscriptionActive === true,
|
|
26342
|
-
workspaceGatewayConnectionActive: input.workspaceGatewayConnectionActive === true,
|
|
26343
|
-
observation: input.credentialReadinessObservations?.[model.definitionVersion],
|
|
26344
|
-
nowMs,
|
|
26345
|
-
maxAgeMs
|
|
26346
|
-
});
|
|
26347
|
-
return {
|
|
26348
|
-
...projectClientModel(model),
|
|
26349
|
-
credentialReadiness,
|
|
26350
|
-
policyAllowed,
|
|
26351
|
-
availability: availabilityFor({
|
|
26352
|
-
model,
|
|
26353
|
-
credentialReadiness,
|
|
26354
|
-
policyAllowed,
|
|
26355
|
-
observation: input.observations?.[model.definitionVersion],
|
|
26356
|
-
nowMs,
|
|
26357
|
-
maxAgeMs
|
|
26358
|
-
})
|
|
26359
|
-
};
|
|
26360
|
-
});
|
|
26165
|
+
const models = resolveWorkspaceModelSelection(input).map((selection) => ({
|
|
26166
|
+
...projectClientModel(selection.model),
|
|
26167
|
+
credentialReadiness: selection.credentialReadiness,
|
|
26168
|
+
policyAllowed: selection.policyAllowed,
|
|
26169
|
+
availability: selection.availability
|
|
26170
|
+
}));
|
|
26361
26171
|
return WorkspaceModelCatalogResponse.parse({ models });
|
|
26362
26172
|
}
|
|
26363
26173
|
|
|
@@ -26771,7 +26581,8 @@ function registerSuperGrokRoutes(app, deps) {
|
|
|
26771
26581
|
} catch {
|
|
26772
26582
|
valid = false;
|
|
26773
26583
|
}
|
|
26774
|
-
const
|
|
26584
|
+
const catalogSettings = valid ? (await deps.resolveCatalogSettings()).settings : null;
|
|
26585
|
+
const catalog = catalogSettings ? configuredModels(withXaiSubscriptionCatalogProvider(catalogSettings)).filter(
|
|
26775
26586
|
(model) => model.credentialSource.kind === "connected_subscription" && model.credentialSource.provider === "xai"
|
|
26776
26587
|
).map(projectClientModel) : [];
|
|
26777
26588
|
return c.json({
|
|
@@ -26948,7 +26759,13 @@ function registerSuperGrokRoutes(app, deps) {
|
|
|
26948
26759
|
}
|
|
26949
26760
|
|
|
26950
26761
|
// src/routes/connections.ts
|
|
26951
|
-
import { createHash as createHash10 } from "crypto";
|
|
26762
|
+
import { createHash as createHash10, createHmac as createHmac4 } from "crypto";
|
|
26763
|
+
import {
|
|
26764
|
+
WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
|
|
26765
|
+
WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
26766
|
+
VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
26767
|
+
VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
26768
|
+
} from "@opengeni/config";
|
|
26952
26769
|
import {
|
|
26953
26770
|
ATLASSIAN_PROVIDER_DOMAIN as ATLASSIAN_PROVIDER_DOMAIN2,
|
|
26954
26771
|
AtlassianConnectionMetadata as AtlassianConnectionMetadata3,
|
|
@@ -26971,11 +26788,16 @@ import {
|
|
|
26971
26788
|
IntegrationClientMetadata,
|
|
26972
26789
|
ListSlackInstallationBindingsResponse,
|
|
26973
26790
|
ListConnectionsResponse,
|
|
26791
|
+
OPENROUTER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY,
|
|
26792
|
+
OPENROUTER_CREDENTIAL_OPERATION_ID_METADATA_KEY,
|
|
26974
26793
|
OpenGeniSlackBotInstallRequest,
|
|
26975
26794
|
OpenGeniSlackBotInstallStart,
|
|
26976
26795
|
OAuthStartRequest,
|
|
26977
26796
|
OAuthStartResponse as OAuthStartResponse4,
|
|
26978
|
-
UpdateConnectionRequest
|
|
26797
|
+
UpdateConnectionRequest,
|
|
26798
|
+
VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY,
|
|
26799
|
+
VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_ID_METADATA_KEY,
|
|
26800
|
+
stableJson as stableJson2
|
|
26979
26801
|
} from "@opengeni/contracts";
|
|
26980
26802
|
import {
|
|
26981
26803
|
bindConnectorDocumentDestination as bindConnectorDocumentDestination3,
|
|
@@ -27019,11 +26841,17 @@ import {
|
|
|
27019
26841
|
persistSlackBotInstallationWithSuccessAudit,
|
|
27020
26842
|
recordSlackBotInstallCallbackFailure,
|
|
27021
26843
|
revokeConnection,
|
|
26844
|
+
revokeWorkspaceOpenRouterConnections,
|
|
26845
|
+
revokeWorkspaceVercelAiGatewayConnections,
|
|
27022
26846
|
revokeConnectionWithSlackBotSuccessAudit,
|
|
26847
|
+
rotateWorkspaceVercelAiGatewayConnection,
|
|
26848
|
+
rotateWorkspaceOpenRouterConnection,
|
|
27023
26849
|
SlackBotLifecycleSuccessAuditError,
|
|
27024
26850
|
SlackInstallationBindingConflictError,
|
|
27025
26851
|
updateConnection as updateConnection5,
|
|
27026
|
-
updateSlackBotDocumentDestination
|
|
26852
|
+
updateSlackBotDocumentDestination,
|
|
26853
|
+
upsertWorkspaceVercelAiGatewayConnection,
|
|
26854
|
+
upsertWorkspaceOpenRouterConnection
|
|
27027
26855
|
} from "@opengeni/db";
|
|
27028
26856
|
import { HTTPException as HTTPException25 } from "hono/http-exception";
|
|
27029
26857
|
|
|
@@ -28183,13 +28011,59 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28183
28011
|
assertNotDirectGoogleDriveOAuth(providerDomain, payload.kind, payload.metadata);
|
|
28184
28012
|
assertNotDirectAtlassianOAuth(providerDomain, payload.kind, payload.metadata);
|
|
28185
28013
|
assertNotDirectPersonalGitHubOAuth(providerDomain, payload.kind, payload.metadata);
|
|
28186
|
-
const
|
|
28014
|
+
const credentialEncrypted = encryptCredentialBundle(key, payload.credential);
|
|
28015
|
+
const workspaceProviderKind = workspaceProviderApiKeyConnectionKind({
|
|
28016
|
+
subjectId,
|
|
28017
|
+
providerDomain,
|
|
28018
|
+
kind: payload.kind,
|
|
28019
|
+
metadata: payload.metadata
|
|
28020
|
+
});
|
|
28021
|
+
const connection = workspaceProviderKind ? await (async () => {
|
|
28022
|
+
const provider = workspaceProviderApiKeyConnectionSpec(workspaceProviderKind);
|
|
28023
|
+
if (!payload.operationId) {
|
|
28024
|
+
throw new HTTPException25(400, {
|
|
28025
|
+
message: `connecting ${provider.label} requires an operationId`
|
|
28026
|
+
});
|
|
28027
|
+
}
|
|
28028
|
+
const expiresAt = payload.expiresAt ? new Date(payload.expiresAt) : null;
|
|
28029
|
+
const metadata = workspaceProviderCredentialMetadata(
|
|
28030
|
+
workspaceProviderKind,
|
|
28031
|
+
payload.metadata
|
|
28032
|
+
);
|
|
28033
|
+
const input = {
|
|
28034
|
+
accountId: grant.accountId,
|
|
28035
|
+
workspaceId,
|
|
28036
|
+
operationId: payload.operationId,
|
|
28037
|
+
requestDigest: workspaceProviderCredentialRequestDigest(key, {
|
|
28038
|
+
action: "create",
|
|
28039
|
+
providerDomain,
|
|
28040
|
+
kind: payload.kind,
|
|
28041
|
+
subjectId,
|
|
28042
|
+
credential: payload.credential,
|
|
28043
|
+
grantedScopes: payload.grantedScopes,
|
|
28044
|
+
expiresAt: expiresAt?.toISOString() ?? null,
|
|
28045
|
+
metadata
|
|
28046
|
+
}),
|
|
28047
|
+
credentialEncrypted,
|
|
28048
|
+
grantedScopes: payload.grantedScopes,
|
|
28049
|
+
expiresAt,
|
|
28050
|
+
metadata,
|
|
28051
|
+
updatedBySubjectId: grant.subjectId
|
|
28052
|
+
};
|
|
28053
|
+
const created = workspaceProviderKind === "vercel_gateway" ? await upsertWorkspaceVercelAiGatewayConnection(db, input) : await upsertWorkspaceOpenRouterConnection(db, input);
|
|
28054
|
+
if (!created || created.status === "revoked") {
|
|
28055
|
+
throw new HTTPException25(409, {
|
|
28056
|
+
message: `${provider.label} is already connected; reload before replacing its key`
|
|
28057
|
+
});
|
|
28058
|
+
}
|
|
28059
|
+
return created;
|
|
28060
|
+
})() : await createConnection5(db, {
|
|
28187
28061
|
accountId: grant.accountId,
|
|
28188
28062
|
workspaceId,
|
|
28189
28063
|
subjectId,
|
|
28190
28064
|
providerDomain,
|
|
28191
28065
|
kind: payload.kind,
|
|
28192
|
-
credentialEncrypted
|
|
28066
|
+
credentialEncrypted,
|
|
28193
28067
|
grantedScopes: payload.grantedScopes,
|
|
28194
28068
|
expiresAt: payload.expiresAt ? new Date(payload.expiresAt) : null,
|
|
28195
28069
|
metadata: payload.metadata,
|
|
@@ -28723,6 +28597,18 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28723
28597
|
payload.providerDomain ?? existing.providerDomain
|
|
28724
28598
|
);
|
|
28725
28599
|
const kind = payload.kind ?? existing.kind;
|
|
28600
|
+
const subjectId2 = payload.subjectId === void 0 ? existing.subjectId : writableSubjectId(payload.subjectId, grant.subjectId);
|
|
28601
|
+
const targetWorkspaceProviderKind = workspaceProviderApiKeyConnectionKind({
|
|
28602
|
+
subjectId: subjectId2,
|
|
28603
|
+
providerDomain,
|
|
28604
|
+
kind,
|
|
28605
|
+
metadata: payload.metadata ?? existing.metadata
|
|
28606
|
+
});
|
|
28607
|
+
if (!workspaceProviderApiKeyConnectionKind(existing) && targetWorkspaceProviderKind) {
|
|
28608
|
+
throw new HTTPException25(422, {
|
|
28609
|
+
message: targetWorkspaceProviderKind === "vercel_gateway" ? "use the Vercel AI Gateway connect flow to create this connection" : "use the OpenRouter connect flow to create this connection"
|
|
28610
|
+
});
|
|
28611
|
+
}
|
|
28726
28612
|
assertNotDirectPersonalSlackOAuth(providerDomain, kind);
|
|
28727
28613
|
assertNotDirectGoogleDriveOAuth(providerDomain, kind, payload.metadata ?? existing.metadata);
|
|
28728
28614
|
assertNotDirectAtlassianOAuth(providerDomain, kind, payload.metadata ?? existing.metadata);
|
|
@@ -28744,6 +28630,65 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28744
28630
|
});
|
|
28745
28631
|
}
|
|
28746
28632
|
}
|
|
28633
|
+
const existingWorkspaceProviderKind = existing ? workspaceProviderApiKeyConnectionKind(existing) : null;
|
|
28634
|
+
if (existing && existingWorkspaceProviderKind) {
|
|
28635
|
+
const provider = workspaceProviderApiKeyConnectionSpec(existingWorkspaceProviderKind);
|
|
28636
|
+
const providerDomain = canonicalProviderDomain(
|
|
28637
|
+
payload.providerDomain ?? existing.providerDomain
|
|
28638
|
+
);
|
|
28639
|
+
const subjectId2 = payload.subjectId === void 0 ? existing.subjectId : writableSubjectId(payload.subjectId, grant.subjectId);
|
|
28640
|
+
const kind = payload.kind ?? existing.kind;
|
|
28641
|
+
if (subjectId2 !== null || providerDomain !== provider.providerDomain || kind !== "api_key" || payload.metadata?.credentialRole !== void 0 && payload.metadata.credentialRole !== provider.credentialRole) {
|
|
28642
|
+
throw new HTTPException25(422, {
|
|
28643
|
+
message: `${provider.label} connection identity cannot be changed`
|
|
28644
|
+
});
|
|
28645
|
+
}
|
|
28646
|
+
if (payload.credential === void 0) {
|
|
28647
|
+
throw new HTTPException25(400, {
|
|
28648
|
+
message: `updating a ${provider.label} connection requires a new credential`
|
|
28649
|
+
});
|
|
28650
|
+
}
|
|
28651
|
+
if (payload.expectedVersion === void 0 || payload.operationId === void 0) {
|
|
28652
|
+
throw new HTTPException25(400, {
|
|
28653
|
+
message: `updating a ${provider.label} connection requires expectedVersion and operationId`
|
|
28654
|
+
});
|
|
28655
|
+
}
|
|
28656
|
+
const key2 = requireEnvironmentEncryption8(settings);
|
|
28657
|
+
const grantedScopes2 = payload.grantedScopes ?? existing.grantedScopes;
|
|
28658
|
+
const expiresAt = payload.expiresAt !== void 0 ? payload.expiresAt ? new Date(payload.expiresAt) : null : existing.expiresAt ? new Date(existing.expiresAt) : null;
|
|
28659
|
+
const metadata = workspaceProviderCredentialMetadata(existingWorkspaceProviderKind, {
|
|
28660
|
+
...existing.metadata,
|
|
28661
|
+
...payload.metadata ?? {}
|
|
28662
|
+
});
|
|
28663
|
+
const input = {
|
|
28664
|
+
accountId: grant.accountId,
|
|
28665
|
+
workspaceId,
|
|
28666
|
+
connectionId: existing.id,
|
|
28667
|
+
expectedVersion: payload.expectedVersion,
|
|
28668
|
+
operationId: payload.operationId,
|
|
28669
|
+
requestDigest: workspaceProviderCredentialRequestDigest(key2, {
|
|
28670
|
+
action: "rotate",
|
|
28671
|
+
connectionId: existing.id,
|
|
28672
|
+
expectedVersion: payload.expectedVersion,
|
|
28673
|
+
credential: payload.credential,
|
|
28674
|
+
grantedScopes: grantedScopes2,
|
|
28675
|
+
expiresAt: expiresAt?.toISOString() ?? null,
|
|
28676
|
+
metadata
|
|
28677
|
+
}),
|
|
28678
|
+
credentialEncrypted: encryptCredentialBundle(key2, payload.credential),
|
|
28679
|
+
grantedScopes: grantedScopes2,
|
|
28680
|
+
expiresAt,
|
|
28681
|
+
metadata,
|
|
28682
|
+
updatedBySubjectId: grant.subjectId
|
|
28683
|
+
};
|
|
28684
|
+
const connection2 = existingWorkspaceProviderKind === "vercel_gateway" ? await rotateWorkspaceVercelAiGatewayConnection(db, input) : await rotateWorkspaceOpenRouterConnection(db, input);
|
|
28685
|
+
if (!connection2) {
|
|
28686
|
+
throw new HTTPException25(409, {
|
|
28687
|
+
message: `${provider.label} connection changed; reload before replacing its key`
|
|
28688
|
+
});
|
|
28689
|
+
}
|
|
28690
|
+
return c.json(ConnectionResponse.parse({ connection: connection2 }));
|
|
28691
|
+
}
|
|
28747
28692
|
const key = payload.credential === void 0 ? null : requireEnvironmentEncryption8(settings);
|
|
28748
28693
|
const subjectId = payload.subjectId === void 0 ? void 0 : writableSubjectId(payload.subjectId, grant.subjectId);
|
|
28749
28694
|
const connection = await updateConnection5(db, {
|
|
@@ -28785,6 +28730,7 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28785
28730
|
}
|
|
28786
28731
|
const isGoogleDrive = existing.subjectId === grant.subjectId && existing.providerDomain === GOOGLE_DRIVE_PROVIDER_DOMAIN2 && existing.kind === "oauth2" && GoogleDriveConnectionMetadata2.safeParse(existing.metadata).success;
|
|
28787
28732
|
const isAtlassian = existing.subjectId === grant.subjectId && existing.providerDomain === ATLASSIAN_PROVIDER_DOMAIN2 && existing.kind === "oauth2" && AtlassianConnectionMetadata3.safeParse(existing.metadata).success;
|
|
28733
|
+
const workspaceProviderKind = workspaceProviderApiKeyConnectionKind(existing);
|
|
28788
28734
|
const disconnectPayload = await c.req.json().catch(() => null);
|
|
28789
28735
|
const googleDriveDisconnect = isGoogleDrive ? GoogleDriveDisconnectRequest.safeParse(disconnectPayload) : null;
|
|
28790
28736
|
if (googleDriveDisconnect && !googleDriveDisconnect.success) {
|
|
@@ -28804,7 +28750,7 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28804
28750
|
message: personalGitHubDisconnect.error.issues[0]?.message ?? "invalid personal GitHub disconnect request"
|
|
28805
28751
|
});
|
|
28806
28752
|
}
|
|
28807
|
-
if (existing.status === "revoked" && !isGoogleDrive && !isAtlassian && !isPersonalGitHub) {
|
|
28753
|
+
if (existing.status === "revoked" && !isGoogleDrive && !isAtlassian && !isPersonalGitHub && !workspaceProviderKind) {
|
|
28808
28754
|
return c.json(ConnectionResponse.parse({ connection: existing }));
|
|
28809
28755
|
}
|
|
28810
28756
|
const connection = isGoogleDrive ? await disconnectGoogleDrive(deps, {
|
|
@@ -28831,6 +28777,18 @@ function registerConnectionRoutes(app, deps) {
|
|
|
28831
28777
|
credentialRole: OPENGENI_SLACK_BOT_CREDENTIAL_ROLE2,
|
|
28832
28778
|
credentialLabel: OPENGENI_SLACK_BOT_CREDENTIAL_LABEL2,
|
|
28833
28779
|
slackTeamId: openGeniSlackBotMetadata2(existing.metadata).slackTeamId
|
|
28780
|
+
}) : workspaceProviderKind ? workspaceProviderKind === "vercel_gateway" ? await revokeWorkspaceVercelAiGatewayConnections(db, {
|
|
28781
|
+
accountId: grant.accountId,
|
|
28782
|
+
workspaceId,
|
|
28783
|
+
connectionId,
|
|
28784
|
+
expectedVersion: existing.version,
|
|
28785
|
+
updatedBySubjectId: grant.subjectId
|
|
28786
|
+
}) : await revokeWorkspaceOpenRouterConnections(db, {
|
|
28787
|
+
accountId: grant.accountId,
|
|
28788
|
+
workspaceId,
|
|
28789
|
+
connectionId,
|
|
28790
|
+
expectedVersion: existing.version,
|
|
28791
|
+
updatedBySubjectId: grant.subjectId
|
|
28834
28792
|
}) : await revokeConnection(
|
|
28835
28793
|
db,
|
|
28836
28794
|
workspaceId,
|
|
@@ -29152,6 +29110,28 @@ function assertNotReservedPersonalGitHubMetadata(metadata) {
|
|
|
29152
29110
|
});
|
|
29153
29111
|
}
|
|
29154
29112
|
}
|
|
29113
|
+
function workspaceProviderApiKeyConnectionSpec(providerKind) {
|
|
29114
|
+
return providerKind === "vercel_gateway" ? {
|
|
29115
|
+
providerDomain: VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
29116
|
+
credentialRole: VERCEL_AI_GATEWAY_CONNECTION_ROLE,
|
|
29117
|
+
label: "Vercel AI Gateway"
|
|
29118
|
+
} : {
|
|
29119
|
+
providerDomain: WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
|
|
29120
|
+
credentialRole: WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
29121
|
+
label: "OpenRouter"
|
|
29122
|
+
};
|
|
29123
|
+
}
|
|
29124
|
+
function workspaceProviderApiKeyConnectionKind(input) {
|
|
29125
|
+
if (input.subjectId != null || input.kind !== "api_key") return null;
|
|
29126
|
+
const providerDomain = input.providerDomain.toLowerCase();
|
|
29127
|
+
if (providerDomain === VERCEL_AI_GATEWAY_CONNECTION_DOMAIN && input.metadata?.credentialRole === VERCEL_AI_GATEWAY_CONNECTION_ROLE) {
|
|
29128
|
+
return "vercel_gateway";
|
|
29129
|
+
}
|
|
29130
|
+
if (providerDomain === WORKSPACE_OPENROUTER_CONNECTION_DOMAIN && input.metadata?.credentialRole === WORKSPACE_OPENROUTER_CONNECTION_ROLE) {
|
|
29131
|
+
return "openrouter";
|
|
29132
|
+
}
|
|
29133
|
+
return null;
|
|
29134
|
+
}
|
|
29155
29135
|
function assertNotReservedSlackBotMetadata(metadata) {
|
|
29156
29136
|
if (hasReservedOpenGeniSlackBotMetadata(metadata)) {
|
|
29157
29137
|
throw new HTTPException25(422, {
|
|
@@ -29197,6 +29177,22 @@ function createConnectionSubjectId(payload, grantSubjectId) {
|
|
|
29197
29177
|
function encryptCredentialBundle(key, credential) {
|
|
29198
29178
|
return encryptEnvironmentValue9(key, JSON.stringify(credential));
|
|
29199
29179
|
}
|
|
29180
|
+
function workspaceProviderCredentialMetadata(providerKind, metadata) {
|
|
29181
|
+
const {
|
|
29182
|
+
[OPENROUTER_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _openRouterOperationId,
|
|
29183
|
+
[OPENROUTER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _openRouterOperationDigest,
|
|
29184
|
+
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _operationId,
|
|
29185
|
+
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _operationDigest,
|
|
29186
|
+
...effectiveMetadata
|
|
29187
|
+
} = metadata ?? {};
|
|
29188
|
+
return {
|
|
29189
|
+
...effectiveMetadata,
|
|
29190
|
+
credentialRole: workspaceProviderApiKeyConnectionSpec(providerKind).credentialRole
|
|
29191
|
+
};
|
|
29192
|
+
}
|
|
29193
|
+
function workspaceProviderCredentialRequestDigest(key, value) {
|
|
29194
|
+
return createHmac4("sha256", key).update(stableJson2(value), "utf8").digest("hex");
|
|
29195
|
+
}
|
|
29200
29196
|
function slackBotCredentialBundle(token) {
|
|
29201
29197
|
const headerName = ["author", "ization"].join("");
|
|
29202
29198
|
const scheme = ["Bear", "er"].join("");
|
|
@@ -33403,7 +33399,7 @@ import { retryWhileMissing as retryWhileMissing4 } from "@opengeni/storage";
|
|
|
33403
33399
|
import { HTTPException as HTTPException35 } from "hono/http-exception";
|
|
33404
33400
|
|
|
33405
33401
|
// src/browser-controller-authority.ts
|
|
33406
|
-
import { createHmac as
|
|
33402
|
+
import { createHmac as createHmac5 } from "crypto";
|
|
33407
33403
|
function deriveBrowserControllerAdminToken(scope) {
|
|
33408
33404
|
return derive(scope.rootSecret, "placement-admin.v1", [
|
|
33409
33405
|
scope.accountId,
|
|
@@ -33484,7 +33480,7 @@ function deriveComputerViewGrantToken(scope) {
|
|
|
33484
33480
|
}
|
|
33485
33481
|
function derive(rootSecret, domain, fields) {
|
|
33486
33482
|
if (!rootSecret) throw new Error("browser controller authority root is unavailable");
|
|
33487
|
-
const hmac2 =
|
|
33483
|
+
const hmac2 = createHmac5("sha256", rootSecret);
|
|
33488
33484
|
hmac2.update("opengeni.browser-controller-authority\0", "utf8");
|
|
33489
33485
|
writeField(hmac2, domain);
|
|
33490
33486
|
for (const field of fields) writeField(hmac2, String(field));
|
|
@@ -33643,7 +33639,7 @@ async function withInteractionHolderHeartbeat(deps, input, run) {
|
|
|
33643
33639
|
}
|
|
33644
33640
|
|
|
33645
33641
|
// src/browser-state-authority.ts
|
|
33646
|
-
import { createCipheriv, createDecipheriv, createHash as createHash11, createHmac as
|
|
33642
|
+
import { createCipheriv, createDecipheriv, createHash as createHash11, createHmac as createHmac6, randomBytes as randomBytes7 } from "crypto";
|
|
33647
33643
|
import { BROWSER_PROFILE_ARTIFACT_FORMAT } from "@opengeni/contracts";
|
|
33648
33644
|
var ROOT_KEY_BYTES = 32;
|
|
33649
33645
|
var DATA_KEY_BYTES = 32;
|
|
@@ -33654,7 +33650,7 @@ var SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
|
33654
33650
|
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
33655
33651
|
function deriveBrowserStateDataKey(rootKey, scopeInput) {
|
|
33656
33652
|
const scope = operationScope(scopeInput);
|
|
33657
|
-
return
|
|
33653
|
+
return createHmac6("sha256", root(rootKey)).update("opengeni.browser-state.data-key.v1\0", "utf8").update(canonicalJson2(scope), "utf8").digest();
|
|
33658
33654
|
}
|
|
33659
33655
|
function browserStateArtifactAad(scopeInput) {
|
|
33660
33656
|
const scope = baseArtifactScope(scopeInput);
|
|
@@ -33739,7 +33735,7 @@ function wrapAad(scope) {
|
|
|
33739
33735
|
return Buffer.from(`opengeni.browser-state.data-key-wrap.v1\0${canonicalJson2(scope)}`, "utf8");
|
|
33740
33736
|
}
|
|
33741
33737
|
function browserStateWrappingKey(rootKey, scope) {
|
|
33742
|
-
return
|
|
33738
|
+
return createHmac6("sha256", root(rootKey)).update("opengeni.browser-state.wrapping-key.v1\0", "utf8").update(canonicalJson2(scope), "utf8").digest();
|
|
33743
33739
|
}
|
|
33744
33740
|
function browserStateObjectKeyValue(value, workspaceId) {
|
|
33745
33741
|
if (typeof value !== "string" || Buffer.byteLength(value) > 2048 || !new RegExp(
|
|
@@ -33805,7 +33801,7 @@ function canonicalValue(value) {
|
|
|
33805
33801
|
}
|
|
33806
33802
|
|
|
33807
33803
|
// src/browser-auth-broker.ts
|
|
33808
|
-
import { createHmac as
|
|
33804
|
+
import { createHmac as createHmac7 } from "crypto";
|
|
33809
33805
|
var BrowserAuthCredentialError = class extends Error {
|
|
33810
33806
|
name = "BrowserAuthCredentialError";
|
|
33811
33807
|
};
|
|
@@ -33856,7 +33852,7 @@ function totpCode(secretValue, options = {}) {
|
|
|
33856
33852
|
const secret = decodeBase32(uri?.secret ?? secretValue);
|
|
33857
33853
|
const counter = Buffer.alloc(8);
|
|
33858
33854
|
counter.writeBigUInt64BE(BigInt(Math.floor(nowMs / (periodSeconds * 1e3))));
|
|
33859
|
-
const digest2 =
|
|
33855
|
+
const digest2 = createHmac7(algorithm, secret).update(counter).digest();
|
|
33860
33856
|
const offset = digest2[digest2.length - 1] & 15;
|
|
33861
33857
|
const binary = (digest2[offset] & 127) << 24 | digest2[offset + 1] << 16 | digest2[offset + 2] << 8 | digest2[offset + 3];
|
|
33862
33858
|
const code = String(binary % 10 ** digits).padStart(digits, "0");
|
|
@@ -34079,7 +34075,7 @@ function safeControlRequestId(value) {
|
|
|
34079
34075
|
}
|
|
34080
34076
|
|
|
34081
34077
|
// src/interaction-frame-proxy.ts
|
|
34082
|
-
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, createHmac as
|
|
34078
|
+
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, createHmac as createHmac8, randomBytes as randomBytes8 } from "crypto";
|
|
34083
34079
|
import { canonicalPublicOrigin as canonicalPublicOrigin2 } from "@opengeni/config";
|
|
34084
34080
|
import { WebSocket as UpstreamWebSocket } from "ws";
|
|
34085
34081
|
var INTERACTION_FRAME_PROXY_PATH = "/v1/interaction/frame-proxy";
|
|
@@ -34301,7 +34297,7 @@ function decryptGrant(token, rootSecret) {
|
|
|
34301
34297
|
}
|
|
34302
34298
|
function tokenKey(rootSecret) {
|
|
34303
34299
|
if (rootSecret.length === 0) throw new Error("proxy authority is empty");
|
|
34304
|
-
return
|
|
34300
|
+
return createHmac8("sha256", rootSecret).update(TOKEN_KEY_CONTEXT).digest();
|
|
34305
34301
|
}
|
|
34306
34302
|
function validateGrant(value) {
|
|
34307
34303
|
if (!value || typeof value !== "object") throw new Error("invalid proxy grant");
|
|
@@ -40261,14 +40257,6 @@ function registerGitHubRoutes(app, deps) {
|
|
|
40261
40257
|
if (candidates.length === 0) {
|
|
40262
40258
|
return redirectToGitHubInstallation(c, deps, selectionState);
|
|
40263
40259
|
}
|
|
40264
|
-
if (candidates.length === 1) {
|
|
40265
|
-
return redirectToExactGitHubAuthorization(
|
|
40266
|
-
c,
|
|
40267
|
-
deps,
|
|
40268
|
-
selectionState,
|
|
40269
|
-
candidates[0].installation.installationId
|
|
40270
|
-
);
|
|
40271
|
-
}
|
|
40272
40260
|
setGitHubStateCookie(c, deps, selectionState);
|
|
40273
40261
|
return c.html(
|
|
40274
40262
|
githubInstallationChooserHtml(
|
|
@@ -44161,16 +44149,18 @@ import {
|
|
|
44161
44149
|
UpdatePrReviewRepositoryBindingRequest
|
|
44162
44150
|
} from "@opengeni/contracts";
|
|
44163
44151
|
import {
|
|
44164
|
-
assertWorkspaceModelPolicyAllows,
|
|
44165
|
-
canonicalConfiguredModel,
|
|
44152
|
+
assertWorkspaceModelPolicyAllows as assertWorkspaceModelPolicyAllows2,
|
|
44153
|
+
canonicalConfiguredModel as canonicalConfiguredModel2,
|
|
44166
44154
|
defaultPrReviewProviderBaseUrl,
|
|
44167
44155
|
getCapabilityPack as getCapabilityPack2,
|
|
44156
|
+
workspaceCustomModelReference as workspaceCustomModelReference2,
|
|
44168
44157
|
prReviewWebhookAuthKind,
|
|
44169
44158
|
normalizePrReviewProviderBaseUrl,
|
|
44170
44159
|
prReviewPackConnectorId as prReviewPackConnectorId2,
|
|
44171
44160
|
PR_REVIEW_AUTOMATION_TEMPLATE_ID as PR_REVIEW_AUTOMATION_TEMPLATE_ID2,
|
|
44172
44161
|
requireAccessGrant as requireAccessGrant23,
|
|
44173
|
-
requirePermission as requirePermission6
|
|
44162
|
+
requirePermission as requirePermission6,
|
|
44163
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings3
|
|
44174
44164
|
} from "@opengeni/core";
|
|
44175
44165
|
import {
|
|
44176
44166
|
createPrReviewAppRegistration,
|
|
@@ -44183,6 +44173,8 @@ import {
|
|
|
44183
44173
|
getPackInstallation as getPackInstallation2,
|
|
44184
44174
|
listPrReviewAppRegistrations as listPrReviewAppRegistrations2,
|
|
44185
44175
|
listPrReviewRepositoryBindings as listPrReviewRepositoryBindings2,
|
|
44176
|
+
lockActiveWorkspaceGatewayCustomModelForAdmission as lockActiveWorkspaceGatewayCustomModelForAdmission2,
|
|
44177
|
+
lockActiveWorkspaceOpenRouterCustomModelForAdmission as lockActiveWorkspaceOpenRouterCustomModelForAdmission2,
|
|
44186
44178
|
nestedPostgresSqlState as nestedPostgresSqlState3,
|
|
44187
44179
|
recordAuditEvent as recordAuditEvent9,
|
|
44188
44180
|
updatePrReviewAppRegistration,
|
|
@@ -44435,7 +44427,7 @@ import {
|
|
|
44435
44427
|
TriggerAutomationManuallyRequest,
|
|
44436
44428
|
UpdateAutomationSourceRequest,
|
|
44437
44429
|
UpdateAutomationTriggerRequest,
|
|
44438
|
-
stableJson as
|
|
44430
|
+
stableJson as stableJson3
|
|
44439
44431
|
} from "@opengeni/contracts";
|
|
44440
44432
|
import {
|
|
44441
44433
|
AutomationDeliveryConflictError,
|
|
@@ -44451,17 +44443,25 @@ import {
|
|
|
44451
44443
|
listAutomationRuns,
|
|
44452
44444
|
listAutomationSources,
|
|
44453
44445
|
listAutomationTriggers,
|
|
44446
|
+
lockActiveWorkspaceGatewayCustomModelForAdmission,
|
|
44447
|
+
lockActiveWorkspaceOpenRouterCustomModelForAdmission,
|
|
44454
44448
|
recordAutomationEvent,
|
|
44455
44449
|
resolveAutomationWebhookEndpoint,
|
|
44456
44450
|
updateAutomationSource,
|
|
44457
|
-
updateAutomationTrigger
|
|
44451
|
+
updateAutomationTrigger,
|
|
44452
|
+
withWorkspaceGatewayCustomModelReadLock,
|
|
44453
|
+
withWorkspaceOpenRouterCustomModelReadLock
|
|
44458
44454
|
} from "@opengeni/db";
|
|
44459
44455
|
import {
|
|
44460
44456
|
automationRequestDigest,
|
|
44457
|
+
assertWorkspaceModelPolicyAllows,
|
|
44461
44458
|
buildAutomationAcceptedExecution,
|
|
44459
|
+
canonicalConfiguredModel,
|
|
44460
|
+
workspaceCustomModelReference,
|
|
44462
44461
|
requireAccessGrant as requireAccessGrant21,
|
|
44463
44462
|
requireAutomationAdapter,
|
|
44464
|
-
requirePermission as requirePermission4
|
|
44463
|
+
requirePermission as requirePermission4,
|
|
44464
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings2
|
|
44465
44465
|
} from "@opengeni/core";
|
|
44466
44466
|
import { HTTPException as HTTPException43 } from "hono/http-exception";
|
|
44467
44467
|
function registerAutomationRoutes(app, deps) {
|
|
@@ -44574,13 +44574,32 @@ function registerAutomationRoutes(app, deps) {
|
|
|
44574
44574
|
for (const permission of request.sessionTemplate.firstPartyMcpPermissions) {
|
|
44575
44575
|
requirePermission4(grant, permission);
|
|
44576
44576
|
}
|
|
44577
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings2(deps.db, deps.settings, {
|
|
44578
|
+
accountId: grant.accountId,
|
|
44579
|
+
workspaceId
|
|
44580
|
+
})).settings;
|
|
44581
|
+
const model = canonicalConfiguredModel(catalogSettings, request.sessionTemplate.model) ?? null;
|
|
44582
|
+
if (model) {
|
|
44583
|
+
await assertWorkspaceModelPolicyAllows(deps.db, catalogSettings, workspaceId, model);
|
|
44584
|
+
}
|
|
44585
|
+
const normalizedRequest = {
|
|
44586
|
+
...request,
|
|
44587
|
+
sessionTemplate: { ...request.sessionTemplate, model }
|
|
44588
|
+
};
|
|
44589
|
+
const beforeCreateCommit = model ? workspaceCustomModelCommitGuard({
|
|
44590
|
+
settings: catalogSettings,
|
|
44591
|
+
accountId: grant.accountId,
|
|
44592
|
+
workspaceId,
|
|
44593
|
+
modelId: model
|
|
44594
|
+
}) : void 0;
|
|
44577
44595
|
return c.json(
|
|
44578
44596
|
await createAutomationTrigger(deps.db, {
|
|
44579
44597
|
accountId: grant.accountId,
|
|
44580
44598
|
workspaceId,
|
|
44581
44599
|
createdBySubjectId: grant.subjectId,
|
|
44582
|
-
request,
|
|
44583
|
-
adapterId: adapter.id
|
|
44600
|
+
request: normalizedRequest,
|
|
44601
|
+
adapterId: adapter.id,
|
|
44602
|
+
...beforeCreateCommit ? { beforeCreateCommit } : {}
|
|
44584
44603
|
}),
|
|
44585
44604
|
201
|
|
44586
44605
|
);
|
|
@@ -44610,12 +44629,40 @@ function registerAutomationRoutes(app, deps) {
|
|
|
44610
44629
|
requirePermission4(grant, permission);
|
|
44611
44630
|
}
|
|
44612
44631
|
}
|
|
44632
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings2(deps.db, deps.settings, {
|
|
44633
|
+
accountId: grant.accountId,
|
|
44634
|
+
workspaceId
|
|
44635
|
+
})).settings;
|
|
44636
|
+
const normalizedRequest = request.sessionTemplate ? {
|
|
44637
|
+
...request,
|
|
44638
|
+
sessionTemplate: {
|
|
44639
|
+
...request.sessionTemplate,
|
|
44640
|
+
model: canonicalConfiguredModel(catalogSettings, request.sessionTemplate.model) ?? null
|
|
44641
|
+
}
|
|
44642
|
+
} : request;
|
|
44643
|
+
if (normalizedRequest.sessionTemplate?.model) {
|
|
44644
|
+
await assertWorkspaceModelPolicyAllows(
|
|
44645
|
+
deps.db,
|
|
44646
|
+
catalogSettings,
|
|
44647
|
+
workspaceId,
|
|
44648
|
+
normalizedRequest.sessionTemplate.model
|
|
44649
|
+
);
|
|
44650
|
+
}
|
|
44651
|
+
const effectiveModel = normalizedRequest.sessionTemplate ? normalizedRequest.sessionTemplate.model : existing.sessionTemplate.model;
|
|
44652
|
+
const materialExecutionChange = request.eventTypes !== void 0 || request.configuration !== void 0 || request.parameters !== void 0 || request.sessionTemplate !== void 0 || request.status === "active" && existing.status !== "active";
|
|
44653
|
+
const beforeUpdateCommit = materialExecutionChange && effectiveModel ? workspaceCustomModelCommitGuard({
|
|
44654
|
+
settings: catalogSettings,
|
|
44655
|
+
accountId: grant.accountId,
|
|
44656
|
+
workspaceId,
|
|
44657
|
+
modelId: effectiveModel
|
|
44658
|
+
}) : void 0;
|
|
44613
44659
|
try {
|
|
44614
44660
|
const trigger = await updateAutomationTrigger(deps.db, {
|
|
44615
44661
|
workspaceId,
|
|
44616
44662
|
triggerId: c.req.param("triggerId"),
|
|
44617
44663
|
subjectId: grant.subjectId,
|
|
44618
|
-
request
|
|
44664
|
+
request: normalizedRequest,
|
|
44665
|
+
...beforeUpdateCommit ? { beforeUpdateCommit } : {}
|
|
44619
44666
|
});
|
|
44620
44667
|
if (!trigger)
|
|
44621
44668
|
throw new HTTPException43(404, {
|
|
@@ -44689,7 +44736,7 @@ function registerAutomationRoutes(app, deps) {
|
|
|
44689
44736
|
resource: request.resource,
|
|
44690
44737
|
payload: request.payload
|
|
44691
44738
|
});
|
|
44692
|
-
const bytes = new TextEncoder().encode(
|
|
44739
|
+
const bytes = new TextEncoder().encode(stableJson3(request));
|
|
44693
44740
|
return c.json(
|
|
44694
44741
|
await acceptAutomationEvent(deps, source, {
|
|
44695
44742
|
deliveryKey: request.deliveryId ?? `manual:${automationRequestDigest(source.adapterId, bytes)}`,
|
|
@@ -44775,29 +44822,78 @@ async function acceptAutomationEvent(deps, source, input) {
|
|
|
44775
44822
|
workspaceId: source.workspaceId,
|
|
44776
44823
|
sourceId: source.id
|
|
44777
44824
|
});
|
|
44778
|
-
const
|
|
44825
|
+
const matchingByEvent = triggers.filter(
|
|
44779
44826
|
(trigger) => adapter.matches({ event: input.normalizedEvent, trigger })
|
|
44780
44827
|
);
|
|
44781
|
-
if (
|
|
44828
|
+
if (matchingByEvent.length > AUTOMATION_MAX_MATCHED_TRIGGERS) {
|
|
44782
44829
|
throw new HTTPException43(422, {
|
|
44783
44830
|
message: `automation event matches more than ${AUTOMATION_MAX_MATCHED_TRIGGERS} triggers`
|
|
44784
44831
|
});
|
|
44785
44832
|
}
|
|
44786
|
-
const stored = await recordAutomationEvent(deps.db, {
|
|
44833
|
+
const stored = matchingByEvent.length === 0 ? await recordAutomationEvent(deps.db, {
|
|
44787
44834
|
accountId: source.accountId,
|
|
44788
44835
|
workspaceId: source.workspaceId,
|
|
44789
44836
|
sourceId: source.id,
|
|
44790
44837
|
sourceVersion: source.version,
|
|
44791
44838
|
sourceConfiguration: source.configuration,
|
|
44792
|
-
matchedTriggerRevisions:
|
|
44793
|
-
triggerId: trigger.id,
|
|
44794
|
-
revision: trigger.revision
|
|
44795
|
-
})),
|
|
44839
|
+
matchedTriggerRevisions: [],
|
|
44796
44840
|
deliveryKey: input.deliveryKey,
|
|
44797
44841
|
requestDigest: input.requestDigest,
|
|
44798
44842
|
normalizedEvent: input.normalizedEvent,
|
|
44799
|
-
ignoredReason:
|
|
44800
|
-
})
|
|
44843
|
+
ignoredReason: "no_matching_triggers"
|
|
44844
|
+
}) : await withWorkspaceGatewayCustomModelReadLock(
|
|
44845
|
+
deps.db,
|
|
44846
|
+
{ accountId: source.accountId, workspaceId: source.workspaceId },
|
|
44847
|
+
async (gatewayLockedDb) => await withWorkspaceOpenRouterCustomModelReadLock(
|
|
44848
|
+
gatewayLockedDb,
|
|
44849
|
+
{ accountId: source.accountId, workspaceId: source.workspaceId },
|
|
44850
|
+
async (lockedDb) => {
|
|
44851
|
+
const catalogSourceSettings = deps.catalogSourceSettings ?? deps.settings;
|
|
44852
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings2(lockedDb, catalogSourceSettings, {
|
|
44853
|
+
accountId: source.accountId,
|
|
44854
|
+
workspaceId: source.workspaceId
|
|
44855
|
+
})).settings;
|
|
44856
|
+
const matchingAtAcceptance = [];
|
|
44857
|
+
for (const trigger of matchingByEvent) {
|
|
44858
|
+
try {
|
|
44859
|
+
const render = adapter.render({
|
|
44860
|
+
event: input.normalizedEvent,
|
|
44861
|
+
trigger,
|
|
44862
|
+
source
|
|
44863
|
+
});
|
|
44864
|
+
const requestedModel = render.sessionTemplate.model ?? catalogSettings.openaiModel;
|
|
44865
|
+
const model = canonicalConfiguredModel(catalogSettings, requestedModel);
|
|
44866
|
+
if (!model) continue;
|
|
44867
|
+
await assertWorkspaceModelPolicyAllows(
|
|
44868
|
+
lockedDb,
|
|
44869
|
+
catalogSettings,
|
|
44870
|
+
source.workspaceId,
|
|
44871
|
+
model
|
|
44872
|
+
);
|
|
44873
|
+
matchingAtAcceptance.push(trigger);
|
|
44874
|
+
} catch (error) {
|
|
44875
|
+
if (isUnprocessableEntity(error)) continue;
|
|
44876
|
+
throw error;
|
|
44877
|
+
}
|
|
44878
|
+
}
|
|
44879
|
+
return await recordAutomationEvent(lockedDb, {
|
|
44880
|
+
accountId: source.accountId,
|
|
44881
|
+
workspaceId: source.workspaceId,
|
|
44882
|
+
sourceId: source.id,
|
|
44883
|
+
sourceVersion: source.version,
|
|
44884
|
+
sourceConfiguration: source.configuration,
|
|
44885
|
+
matchedTriggerRevisions: matchingAtAcceptance.map((trigger) => ({
|
|
44886
|
+
triggerId: trigger.id,
|
|
44887
|
+
revision: trigger.revision
|
|
44888
|
+
})),
|
|
44889
|
+
deliveryKey: input.deliveryKey,
|
|
44890
|
+
requestDigest: input.requestDigest,
|
|
44891
|
+
normalizedEvent: input.normalizedEvent,
|
|
44892
|
+
ignoredReason: matchingAtAcceptance.length === 0 ? "no_executable_triggers" : null
|
|
44893
|
+
});
|
|
44894
|
+
}
|
|
44895
|
+
)
|
|
44896
|
+
);
|
|
44801
44897
|
const matching = await getAutomationTriggerRevisions(deps.db, {
|
|
44802
44898
|
workspaceId: source.workspaceId,
|
|
44803
44899
|
refs: stored.event.matchedTriggerRevisions
|
|
@@ -44854,6 +44950,29 @@ async function acceptAutomationEvent(deps, source, input) {
|
|
|
44854
44950
|
runIds
|
|
44855
44951
|
});
|
|
44856
44952
|
}
|
|
44953
|
+
function isUnprocessableEntity(error) {
|
|
44954
|
+
return error instanceof Error && "status" in error && error.status === 422;
|
|
44955
|
+
}
|
|
44956
|
+
function workspaceCustomModelCommitGuard(input) {
|
|
44957
|
+
const reference = workspaceCustomModelReference(input.settings, input.modelId);
|
|
44958
|
+
if (!reference) return void 0;
|
|
44959
|
+
return async (tx) => {
|
|
44960
|
+
const active = reference.providerKind === "openrouter" ? await lockActiveWorkspaceOpenRouterCustomModelForAdmission(tx, {
|
|
44961
|
+
accountId: input.accountId,
|
|
44962
|
+
workspaceId: input.workspaceId,
|
|
44963
|
+
upstreamModelId: reference.upstreamModelId
|
|
44964
|
+
}) : await lockActiveWorkspaceGatewayCustomModelForAdmission(tx, {
|
|
44965
|
+
accountId: input.accountId,
|
|
44966
|
+
workspaceId: input.workspaceId,
|
|
44967
|
+
upstreamModelId: reference.upstreamModelId
|
|
44968
|
+
});
|
|
44969
|
+
if (!active) {
|
|
44970
|
+
throw new HTTPException43(422, {
|
|
44971
|
+
message: `model is not available: ${input.modelId}`
|
|
44972
|
+
});
|
|
44973
|
+
}
|
|
44974
|
+
};
|
|
44975
|
+
}
|
|
44857
44976
|
async function requireSource(deps, accountId, workspaceId, sourceId) {
|
|
44858
44977
|
const source = await getAutomationSourceSecret(deps.db, {
|
|
44859
44978
|
accountId,
|
|
@@ -45826,8 +45945,18 @@ function registerPrReviewRoutes(app, deps) {
|
|
|
45826
45945
|
throw error;
|
|
45827
45946
|
}
|
|
45828
45947
|
}
|
|
45829
|
-
const
|
|
45830
|
-
|
|
45948
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings3(db, deps.settings, {
|
|
45949
|
+
accountId: grant.accountId,
|
|
45950
|
+
workspaceId
|
|
45951
|
+
})).settings;
|
|
45952
|
+
const model = payload.model ? canonicalConfiguredModel2(catalogSettings, payload.model) ?? null : null;
|
|
45953
|
+
if (model) await assertWorkspaceModelPolicyAllows2(db, catalogSettings, workspaceId, model);
|
|
45954
|
+
const beforeCreateCommit = model ? prReviewCustomModelCommitGuard({
|
|
45955
|
+
settings: catalogSettings,
|
|
45956
|
+
accountId: grant.accountId,
|
|
45957
|
+
workspaceId,
|
|
45958
|
+
modelId: model
|
|
45959
|
+
}) : void 0;
|
|
45831
45960
|
const template = getCapabilityPack2(OPENGENI_PR_REVIEW_PACK_ID2)?.automationTemplates?.find(
|
|
45832
45961
|
(candidate) => candidate.id === PR_REVIEW_AUTOMATION_TEMPLATE_ID2
|
|
45833
45962
|
);
|
|
@@ -45854,7 +45983,8 @@ function registerPrReviewRoutes(app, deps) {
|
|
|
45854
45983
|
adapterId: template.adapterId,
|
|
45855
45984
|
eventTypes: template.eventTypes,
|
|
45856
45985
|
configuration: template.configuration,
|
|
45857
|
-
sessionTemplate: template.sessionTemplate
|
|
45986
|
+
sessionTemplate: template.sessionTemplate,
|
|
45987
|
+
...beforeCreateCommit ? { beforeCreateCommit } : {}
|
|
45858
45988
|
}),
|
|
45859
45989
|
"This repository is already bound to the selected PR Review registration"
|
|
45860
45990
|
);
|
|
@@ -45878,8 +46008,22 @@ function registerPrReviewRoutes(app, deps) {
|
|
|
45878
46008
|
const grant = await requireAccessGrant23(c, deps, workspaceId, "workspace:admin");
|
|
45879
46009
|
await requirePrReviewPackActive(db, workspaceId);
|
|
45880
46010
|
const payload = UpdatePrReviewRepositoryBindingRequest.parse(await c.req.json());
|
|
45881
|
-
const
|
|
45882
|
-
|
|
46011
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings3(db, deps.settings, {
|
|
46012
|
+
accountId: grant.accountId,
|
|
46013
|
+
workspaceId
|
|
46014
|
+
})).settings;
|
|
46015
|
+
const model = payload.model === void 0 ? void 0 : payload.model === null ? null : canonicalConfiguredModel2(catalogSettings, payload.model) ?? null;
|
|
46016
|
+
if (model) await assertWorkspaceModelPolicyAllows2(db, catalogSettings, workspaceId, model);
|
|
46017
|
+
const beforeUpdateCommit = async (tx, context) => {
|
|
46018
|
+
const materialExecutionChange = payload.model !== void 0 || payload.additionalInstructions !== void 0 || context.currentStatus === "disabled" && context.nextStatus === "active";
|
|
46019
|
+
if (!materialExecutionChange || !context.nextModel) return;
|
|
46020
|
+
await prReviewCustomModelCommitGuard({
|
|
46021
|
+
settings: catalogSettings,
|
|
46022
|
+
accountId: grant.accountId,
|
|
46023
|
+
workspaceId,
|
|
46024
|
+
modelId: context.nextModel
|
|
46025
|
+
})?.(tx);
|
|
46026
|
+
};
|
|
45883
46027
|
const binding = await updatePrReviewRepositoryBinding(db, {
|
|
45884
46028
|
accountId: grant.accountId,
|
|
45885
46029
|
workspaceId,
|
|
@@ -45887,7 +46031,8 @@ function registerPrReviewRoutes(app, deps) {
|
|
|
45887
46031
|
subjectId: grant.subjectId,
|
|
45888
46032
|
...model !== void 0 ? { model } : {},
|
|
45889
46033
|
...payload.additionalInstructions !== void 0 ? { additionalInstructions: payload.additionalInstructions } : {},
|
|
45890
|
-
...payload.status !== void 0 ? { status: payload.status } : {}
|
|
46034
|
+
...payload.status !== void 0 ? { status: payload.status } : {},
|
|
46035
|
+
beforeUpdateCommit
|
|
45891
46036
|
});
|
|
45892
46037
|
if (!binding)
|
|
45893
46038
|
throw new HTTPException45(404, {
|
|
@@ -45929,6 +46074,26 @@ function registerPrReviewRoutes(app, deps) {
|
|
|
45929
46074
|
return c.body(null, 204);
|
|
45930
46075
|
});
|
|
45931
46076
|
}
|
|
46077
|
+
function prReviewCustomModelCommitGuard(input) {
|
|
46078
|
+
const reference = workspaceCustomModelReference2(input.settings, input.modelId);
|
|
46079
|
+
if (!reference) return void 0;
|
|
46080
|
+
return async (tx) => {
|
|
46081
|
+
const active = reference.providerKind === "openrouter" ? await lockActiveWorkspaceOpenRouterCustomModelForAdmission2(tx, {
|
|
46082
|
+
accountId: input.accountId,
|
|
46083
|
+
workspaceId: input.workspaceId,
|
|
46084
|
+
upstreamModelId: reference.upstreamModelId
|
|
46085
|
+
}) : await lockActiveWorkspaceGatewayCustomModelForAdmission2(tx, {
|
|
46086
|
+
accountId: input.accountId,
|
|
46087
|
+
workspaceId: input.workspaceId,
|
|
46088
|
+
upstreamModelId: reference.upstreamModelId
|
|
46089
|
+
});
|
|
46090
|
+
if (!active) {
|
|
46091
|
+
throw new HTTPException45(422, {
|
|
46092
|
+
message: `model is not available: ${input.modelId}`
|
|
46093
|
+
});
|
|
46094
|
+
}
|
|
46095
|
+
};
|
|
46096
|
+
}
|
|
45932
46097
|
function assertPrReviewSandboxBackend(deps) {
|
|
45933
46098
|
if (deps.settings.sandboxBackend === "selfhosted") {
|
|
45934
46099
|
throw new HTTPException45(409, {
|
|
@@ -45986,7 +46151,7 @@ import {
|
|
|
45986
46151
|
RegisterCapabilityPackRequest,
|
|
45987
46152
|
UninstallPackRequest,
|
|
45988
46153
|
UninstallPackResult,
|
|
45989
|
-
stableJson as
|
|
46154
|
+
stableJson as stableJson4
|
|
45990
46155
|
} from "@opengeni/contracts";
|
|
45991
46156
|
import {
|
|
45992
46157
|
adoptPackComponentReferences,
|
|
@@ -46161,7 +46326,7 @@ function registerPackRoutes(app, deps) {
|
|
|
46161
46326
|
...preview.variableSetId ? { variableSetId: preview.variableSetId } : {}
|
|
46162
46327
|
};
|
|
46163
46328
|
const requestDigest = sha2562(
|
|
46164
|
-
|
|
46329
|
+
stableJson4({
|
|
46165
46330
|
packId: pack.id,
|
|
46166
46331
|
manifestDigest: preview.manifestDigest,
|
|
46167
46332
|
expectedInstallationVersion: payload.expectedInstallationVersion ?? null,
|
|
@@ -46339,7 +46504,7 @@ function registerPackRoutes(app, deps) {
|
|
|
46339
46504
|
const pack = await requirePack(db, workspaceId, c.req.param("packId"));
|
|
46340
46505
|
const payload = UninstallPackRequest.parse(await c.req.json());
|
|
46341
46506
|
const requestDigest = sha2562(
|
|
46342
|
-
|
|
46507
|
+
stableJson4({
|
|
46343
46508
|
packId: pack.id,
|
|
46344
46509
|
expectedInstallationVersion: payload.expectedInstallationVersion
|
|
46345
46510
|
})
|
|
@@ -46642,7 +46807,7 @@ import {
|
|
|
46642
46807
|
PreviewPluginRequest,
|
|
46643
46808
|
UninstallPluginRequest,
|
|
46644
46809
|
UninstallPluginResult,
|
|
46645
|
-
stableJson as
|
|
46810
|
+
stableJson as stableJson5
|
|
46646
46811
|
} from "@opengeni/contracts";
|
|
46647
46812
|
import {
|
|
46648
46813
|
portableSkillCapabilityId,
|
|
@@ -46849,7 +47014,7 @@ function registerPluginRoutes(app, deps, overrides = {}) {
|
|
|
46849
47014
|
});
|
|
46850
47015
|
}
|
|
46851
47016
|
const requestDigest = sha2563(
|
|
46852
|
-
|
|
47017
|
+
stableJson5({
|
|
46853
47018
|
url: payload.url,
|
|
46854
47019
|
expectedManifestDigest: payload.expectedManifestDigest,
|
|
46855
47020
|
expectedComponents: [...expected.entries()].sort(),
|
|
@@ -46986,14 +47151,14 @@ async function resolvePluginPackage(input) {
|
|
|
46986
47151
|
message: error instanceof Error ? error.message : "Plugin manifest is invalid"
|
|
46987
47152
|
});
|
|
46988
47153
|
}
|
|
46989
|
-
const manifestDigest = sha2563(
|
|
47154
|
+
const manifestDigest = sha2563(stableJson5(manifest));
|
|
46990
47155
|
const components = [];
|
|
46991
47156
|
for (const component of manifest.components) {
|
|
46992
47157
|
if (component.kind === "skill") {
|
|
46993
47158
|
const resolved = await resolveSkillImport(component.url, input.github);
|
|
46994
47159
|
const capabilityId = portableSkillCapabilityId(resolved.preview);
|
|
46995
47160
|
const digest3 = sha2563(
|
|
46996
|
-
|
|
47161
|
+
stableJson5({
|
|
46997
47162
|
capabilityId,
|
|
46998
47163
|
sourceCommit: resolved.preview.sourceCommit,
|
|
46999
47164
|
sourcePath: resolved.preview.sourcePath,
|
|
@@ -47142,7 +47307,7 @@ async function resolvePluginPackage(input) {
|
|
|
47142
47307
|
continue;
|
|
47143
47308
|
}
|
|
47144
47309
|
const server = configuredMcpServer(input.deps.settings.mcpServers, component.serverId);
|
|
47145
|
-
const digest2 = sha2563(
|
|
47310
|
+
const digest2 = sha2563(stableJson5(safeMcpFacts(server)));
|
|
47146
47311
|
components.push({
|
|
47147
47312
|
preview: {
|
|
47148
47313
|
key: component.key,
|
|
@@ -47292,7 +47457,7 @@ function safeMcpFacts(server) {
|
|
|
47292
47457
|
}
|
|
47293
47458
|
function integrationDigest(resolved) {
|
|
47294
47459
|
return sha2563(
|
|
47295
|
-
|
|
47460
|
+
stableJson5({
|
|
47296
47461
|
capabilityId: resolved.preview.capabilityId,
|
|
47297
47462
|
revisionId: resolved.preview.revisionId,
|
|
47298
47463
|
contentSha256: resolved.preview.contentSha256,
|
|
@@ -47961,7 +48126,7 @@ import {
|
|
|
47961
48126
|
} from "@opengeni/contracts";
|
|
47962
48127
|
import { listScheduledTaskRuns as listScheduledTaskRuns2, listScheduledTasks as listScheduledTasks2 } from "@opengeni/db";
|
|
47963
48128
|
import { HTTPException as HTTPException51 } from "hono/http-exception";
|
|
47964
|
-
import { requireAccessGrant as requireAccessGrant29 } from "@opengeni/core";
|
|
48129
|
+
import { requireAccessGrant as requireAccessGrant29, resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings4 } from "@opengeni/core";
|
|
47965
48130
|
import { recordWorkspaceUsage as recordWorkspaceUsage6, requireLimit as requireLimit8 } from "@opengeni/core";
|
|
47966
48131
|
import {
|
|
47967
48132
|
captureScheduledTaskRestoreState as captureScheduledTaskRestoreState4,
|
|
@@ -47981,7 +48146,7 @@ import {
|
|
|
47981
48146
|
validatedScheduledTaskUpdate as validatedScheduledTaskUpdate2
|
|
47982
48147
|
} from "@opengeni/core";
|
|
47983
48148
|
function registerScheduledTaskRoutes(app, deps) {
|
|
47984
|
-
const {
|
|
48149
|
+
const { db, workflowClient, objectStorage } = deps;
|
|
47985
48150
|
app.post("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
|
|
47986
48151
|
const workspaceId = c.req.param("workspaceId");
|
|
47987
48152
|
const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
@@ -47993,6 +48158,10 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
47993
48158
|
});
|
|
47994
48159
|
}
|
|
47995
48160
|
const payload = parsedPayload.data;
|
|
48161
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings4(db, deps.settings, {
|
|
48162
|
+
accountId: grant.accountId,
|
|
48163
|
+
workspaceId
|
|
48164
|
+
})).settings;
|
|
47996
48165
|
await requireLimit8(deps, {
|
|
47997
48166
|
accountId: grant.accountId,
|
|
47998
48167
|
workspaceId,
|
|
@@ -48000,7 +48169,7 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
48000
48169
|
quantity: 1
|
|
48001
48170
|
});
|
|
48002
48171
|
const task = await createValidatedScheduledTask5({
|
|
48003
|
-
settings,
|
|
48172
|
+
settings: catalogSettings,
|
|
48004
48173
|
db,
|
|
48005
48174
|
objectStorage,
|
|
48006
48175
|
grant,
|
|
@@ -48038,8 +48207,12 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
48038
48207
|
});
|
|
48039
48208
|
}
|
|
48040
48209
|
const payload = parsedPayload.data;
|
|
48210
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings4(db, deps.settings, {
|
|
48211
|
+
accountId: grant.accountId,
|
|
48212
|
+
workspaceId
|
|
48213
|
+
})).settings;
|
|
48041
48214
|
const update = await validatedScheduledTaskUpdate2({
|
|
48042
|
-
settings,
|
|
48215
|
+
settings: catalogSettings,
|
|
48043
48216
|
db,
|
|
48044
48217
|
objectStorage,
|
|
48045
48218
|
grant,
|
|
@@ -48069,8 +48242,12 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
48069
48242
|
const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
48070
48243
|
const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
48071
48244
|
const previous = await captureScheduledTaskRestoreState4(db, existing);
|
|
48245
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings4(db, deps.settings, {
|
|
48246
|
+
accountId: grant.accountId,
|
|
48247
|
+
workspaceId
|
|
48248
|
+
})).settings;
|
|
48072
48249
|
const update = await validatedScheduledTaskUpdate2({
|
|
48073
|
-
settings,
|
|
48250
|
+
settings: catalogSettings,
|
|
48074
48251
|
db,
|
|
48075
48252
|
objectStorage,
|
|
48076
48253
|
grant,
|
|
@@ -48088,6 +48265,10 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
48088
48265
|
const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:run");
|
|
48089
48266
|
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
48090
48267
|
if (task.action.kind === "agent_turn") {
|
|
48268
|
+
const catalogSettings = (await resolveWorkspaceCatalogSettings4(db, deps.settings, {
|
|
48269
|
+
accountId: grant.accountId,
|
|
48270
|
+
workspaceId
|
|
48271
|
+
})).settings;
|
|
48091
48272
|
await validateScheduledTaskTarget2({
|
|
48092
48273
|
db,
|
|
48093
48274
|
sessionAuthorization: deps.sessionAuthorization,
|
|
@@ -48101,20 +48282,23 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
48101
48282
|
missingTargetStatus: 404
|
|
48102
48283
|
});
|
|
48103
48284
|
await validateScheduledTaskMachineTarget2({
|
|
48104
|
-
settings,
|
|
48285
|
+
settings: catalogSettings,
|
|
48105
48286
|
db,
|
|
48106
48287
|
grant,
|
|
48107
48288
|
runMode: task.runMode,
|
|
48108
48289
|
agentConfig: task.agentConfig,
|
|
48109
48290
|
requireOnline: true
|
|
48110
48291
|
});
|
|
48111
|
-
await requireLimit8(
|
|
48112
|
-
|
|
48113
|
-
|
|
48114
|
-
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
|
|
48292
|
+
await requireLimit8(
|
|
48293
|
+
{ ...deps, settings: catalogSettings },
|
|
48294
|
+
{
|
|
48295
|
+
accountId: grant.accountId,
|
|
48296
|
+
workspaceId,
|
|
48297
|
+
action: "agent_run:create",
|
|
48298
|
+
quantity: 1,
|
|
48299
|
+
model: task.agentConfig.model ?? catalogSettings.openaiModel
|
|
48300
|
+
}
|
|
48301
|
+
);
|
|
48118
48302
|
}
|
|
48119
48303
|
const body4 = await c.req.json().catch(() => ({}));
|
|
48120
48304
|
const { triggerId } = TriggerScheduledTaskRequest.parse(body4 ?? {});
|
|
@@ -48181,6 +48365,7 @@ import {
|
|
|
48181
48365
|
GatewayRealtimeConnectRequest,
|
|
48182
48366
|
ClientSessionEvent,
|
|
48183
48367
|
CompactSessionContextRequest,
|
|
48368
|
+
CreateSessionRequest,
|
|
48184
48369
|
DeleteSessionQueueItemRequest,
|
|
48185
48370
|
EditSessionQueueItemRequest,
|
|
48186
48371
|
EndSessionRealtimeRequest,
|
|
@@ -48216,6 +48401,7 @@ import {
|
|
|
48216
48401
|
SessionEventSemanticClass as SessionEventSemanticClass2,
|
|
48217
48402
|
SessionEventType as SessionEventType2,
|
|
48218
48403
|
SessionMcpServerId,
|
|
48404
|
+
SaveNewSessionDraftRequest,
|
|
48219
48405
|
compactSessionEventResult as compactSessionEventResult3,
|
|
48220
48406
|
sessionEventLatestClassToSemanticClass as sessionEventLatestClassToSemanticClass2,
|
|
48221
48407
|
SaveComposerDraftRequest,
|
|
@@ -49096,6 +49282,7 @@ import {
|
|
|
49096
49282
|
requirePermission as requirePermission8,
|
|
49097
49283
|
requireSessionAuthorization as requireSessionAuthorization7,
|
|
49098
49284
|
requireSessionAuthorizationListScope as requireSessionAuthorizationListScope2,
|
|
49285
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings5,
|
|
49099
49286
|
withResolvedSessionAuthorization,
|
|
49100
49287
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
49101
49288
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError7,
|
|
@@ -49552,6 +49739,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
49552
49739
|
}
|
|
49553
49740
|
let session;
|
|
49554
49741
|
try {
|
|
49742
|
+
CreateSessionRequest.parse(payload);
|
|
49555
49743
|
session = await createSessionForRequest(deps, grant, workspaceId, payload, authorization);
|
|
49556
49744
|
} catch (error) {
|
|
49557
49745
|
return sessionCreateErrorResponse(c, error);
|
|
@@ -49561,7 +49749,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
49561
49749
|
app.get("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
|
|
49562
49750
|
const workspaceId = c.req.param("workspaceId");
|
|
49563
49751
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
|
|
49564
|
-
|
|
49752
|
+
const catalog = await resolveWorkspaceCatalogSettings5(db, settings, {
|
|
49753
|
+
accountId: grant.accountId,
|
|
49754
|
+
workspaceId
|
|
49755
|
+
});
|
|
49756
|
+
return c.json(
|
|
49757
|
+
await getActorNewSessionDraft({ settings: catalog.settings, db }, grant, workspaceId)
|
|
49758
|
+
);
|
|
49565
49759
|
});
|
|
49566
49760
|
app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
|
|
49567
49761
|
const workspaceId = c.req.param("workspaceId");
|
|
@@ -49585,9 +49779,14 @@ function registerSessionRoutes(app, deps) {
|
|
|
49585
49779
|
);
|
|
49586
49780
|
}
|
|
49587
49781
|
try {
|
|
49782
|
+
SaveNewSessionDraftRequest.parse(payload);
|
|
49783
|
+
const catalog = await resolveWorkspaceCatalogSettings5(db, settings, {
|
|
49784
|
+
accountId: grant.accountId,
|
|
49785
|
+
workspaceId
|
|
49786
|
+
});
|
|
49588
49787
|
return c.json(
|
|
49589
49788
|
await saveActorNewSessionDraft(
|
|
49590
|
-
{ settings, db, objectStorage },
|
|
49789
|
+
{ settings: catalog.settings, db, objectStorage },
|
|
49591
49790
|
grant,
|
|
49592
49791
|
workspaceId,
|
|
49593
49792
|
payload,
|
|
@@ -51734,24 +51933,29 @@ function registerSessionRoutes(app, deps) {
|
|
|
51734
51933
|
const sessionId = c.req.param("sessionId");
|
|
51735
51934
|
await assertSessionExists(db, workspaceId, sessionId);
|
|
51736
51935
|
const payload = parseSteerSessionAdmission(await c.req.json().catch(() => null));
|
|
51737
|
-
|
|
51738
|
-
|
|
51739
|
-
|
|
51740
|
-
|
|
51741
|
-
|
|
51742
|
-
|
|
51743
|
-
|
|
51744
|
-
|
|
51745
|
-
|
|
51746
|
-
|
|
51747
|
-
|
|
51748
|
-
|
|
51749
|
-
|
|
51750
|
-
|
|
51751
|
-
|
|
51752
|
-
|
|
51753
|
-
|
|
51754
|
-
|
|
51936
|
+
let result;
|
|
51937
|
+
try {
|
|
51938
|
+
result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
|
|
51939
|
+
text: payload.text,
|
|
51940
|
+
annotations: payload.annotations,
|
|
51941
|
+
modelContext: payload.modelContext ?? null,
|
|
51942
|
+
resources: payload.resources,
|
|
51943
|
+
model: payload.model ?? null,
|
|
51944
|
+
reasoningEffort: payload.reasoningEffort ?? null,
|
|
51945
|
+
latencyMode: payload.latencyMode ?? null,
|
|
51946
|
+
mcpCredentialUpdates: payload.mcpCredentialUpdates ?? [],
|
|
51947
|
+
connectionAuthorities: payload.connectionAuthorities,
|
|
51948
|
+
...payload.personalResourceAttachment ? { personalResourceAttachment: payload.personalResourceAttachment } : {},
|
|
51949
|
+
authorization,
|
|
51950
|
+
delivery: "steer",
|
|
51951
|
+
origin: "human",
|
|
51952
|
+
...payload.controlEtag !== void 0 ? { controlEtag: payload.controlEtag } : {},
|
|
51953
|
+
...payload.expectedDraftRevision !== void 0 ? { expectedDraftRevision: payload.expectedDraftRevision } : {},
|
|
51954
|
+
...payload.clientEventId ? { clientEventId: payload.clientEventId } : {}
|
|
51955
|
+
});
|
|
51956
|
+
} catch (error) {
|
|
51957
|
+
return commandConflictResponse(c, error);
|
|
51958
|
+
}
|
|
51755
51959
|
return c.json(result, 202);
|
|
51756
51960
|
});
|
|
51757
51961
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/composer-draft/submit", async (c) => {
|
|
@@ -51800,23 +52004,28 @@ function registerSessionRoutes(app, deps) {
|
|
|
51800
52004
|
}
|
|
51801
52005
|
}
|
|
51802
52006
|
if (event.type === "user.message") {
|
|
51803
|
-
|
|
51804
|
-
|
|
51805
|
-
|
|
51806
|
-
|
|
51807
|
-
|
|
51808
|
-
|
|
51809
|
-
|
|
51810
|
-
|
|
51811
|
-
|
|
51812
|
-
|
|
51813
|
-
|
|
51814
|
-
|
|
51815
|
-
|
|
51816
|
-
|
|
51817
|
-
|
|
51818
|
-
|
|
51819
|
-
|
|
52007
|
+
let result;
|
|
52008
|
+
try {
|
|
52009
|
+
result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
|
|
52010
|
+
text: event.payload.text,
|
|
52011
|
+
annotations: event.payload.annotations,
|
|
52012
|
+
modelContext: event.payload.modelContext ?? null,
|
|
52013
|
+
resources: event.payload.resources ?? [],
|
|
52014
|
+
model: event.payload.model ?? null,
|
|
52015
|
+
reasoningEffort: event.payload.reasoningEffort ?? null,
|
|
52016
|
+
latencyMode: event.payload.latencyMode ?? null,
|
|
52017
|
+
mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],
|
|
52018
|
+
connectionAuthorities: event.payload.connectionAuthorities,
|
|
52019
|
+
...event.payload.personalResourceAttachment ? { personalResourceAttachment: event.payload.personalResourceAttachment } : {},
|
|
52020
|
+
authorization,
|
|
52021
|
+
...event.payload.controlEtag !== void 0 ? { controlEtag: event.payload.controlEtag } : {},
|
|
52022
|
+
...event.payload.expectedDraftRevision !== void 0 ? { expectedDraftRevision: event.payload.expectedDraftRevision } : {},
|
|
52023
|
+
...event.clientEventId ? { clientEventId: event.clientEventId } : {}
|
|
52024
|
+
});
|
|
52025
|
+
} catch (error) {
|
|
52026
|
+
return commandConflictResponse(c, error);
|
|
52027
|
+
}
|
|
52028
|
+
return c.json(result.accepted, 202);
|
|
51820
52029
|
}
|
|
51821
52030
|
if (event.type === "user.approvalDecision") {
|
|
51822
52031
|
const accepted = await acceptSessionApprovalDecision2(db, {
|
|
@@ -53674,6 +53883,7 @@ function socialHttpException(error) {
|
|
|
53674
53883
|
}
|
|
53675
53884
|
|
|
53676
53885
|
// src/routes/workspaces.ts
|
|
53886
|
+
import { createHash as createHash16 } from "crypto";
|
|
53677
53887
|
import {
|
|
53678
53888
|
AddWorkspaceMemberRequest,
|
|
53679
53889
|
CreateWorkspaceRequest,
|
|
@@ -53683,16 +53893,25 @@ import {
|
|
|
53683
53893
|
ListWorkspaceMembersResponse,
|
|
53684
53894
|
SetWorkspaceDefaultRigRequest,
|
|
53685
53895
|
UpdateWorkspaceMemberRequest,
|
|
53896
|
+
CreateWorkspaceGatewayCustomModelRequest,
|
|
53897
|
+
CreateWorkspaceOpenRouterCustomModelRequest,
|
|
53898
|
+
DeleteWorkspaceGatewayCustomModelRequest,
|
|
53899
|
+
DeleteWorkspaceOpenRouterCustomModelRequest,
|
|
53686
53900
|
UpdateWorkspaceModelPolicyRequest,
|
|
53687
53901
|
UpdateWorkspaceRequest,
|
|
53688
53902
|
UpdateWorkspaceSettingsRequest,
|
|
53689
53903
|
WORKSPACE_CONTROL_ACTOR_MAX_BYTES as WORKSPACE_CONTROL_ACTOR_MAX_BYTES2,
|
|
53690
53904
|
WorkspaceModelCatalogResponse as WorkspaceModelCatalogResponse2,
|
|
53905
|
+
WorkspaceGatewayCustomModel,
|
|
53906
|
+
WorkspaceGatewayCustomModelsResponse,
|
|
53907
|
+
WorkspaceOpenRouterCustomModel,
|
|
53908
|
+
WorkspaceOpenRouterCustomModelsResponse,
|
|
53691
53909
|
WorkspaceRealtimeModelCatalogResponse,
|
|
53692
53910
|
WorkspaceInferenceControlRequest,
|
|
53693
53911
|
Workspace,
|
|
53694
53912
|
WorkspaceMember,
|
|
53695
|
-
workspaceControlUtf8Bytes as workspaceControlUtf8Bytes2
|
|
53913
|
+
workspaceControlUtf8Bytes as workspaceControlUtf8Bytes2,
|
|
53914
|
+
stableJson as stableJson6
|
|
53696
53915
|
} from "@opengeni/contracts";
|
|
53697
53916
|
import {
|
|
53698
53917
|
allWorkspacePermissions,
|
|
@@ -53708,6 +53927,14 @@ import {
|
|
|
53708
53927
|
normalizeWorkspaceMembershipPermissions,
|
|
53709
53928
|
listWorkspaceControlEvents as listWorkspaceControlEvents2,
|
|
53710
53929
|
listSharedWorkspacesForAccount,
|
|
53930
|
+
listWorkspaceGatewayCustomModels,
|
|
53931
|
+
listWorkspaceOpenRouterCustomModels,
|
|
53932
|
+
createWorkspaceGatewayCustomModel,
|
|
53933
|
+
createWorkspaceOpenRouterCustomModel,
|
|
53934
|
+
deleteWorkspaceGatewayCustomModel,
|
|
53935
|
+
deleteWorkspaceOpenRouterCustomModel,
|
|
53936
|
+
replayWorkspaceGatewayCustomModelCreate,
|
|
53937
|
+
replayWorkspaceOpenRouterCustomModelCreate,
|
|
53711
53938
|
listWorkspacesForSubject,
|
|
53712
53939
|
nestedPostgresSqlState as nestedPostgresSqlState4,
|
|
53713
53940
|
removeWorkspaceMember,
|
|
@@ -53722,7 +53949,12 @@ import {
|
|
|
53722
53949
|
workspaceControlRequestLockTimeoutMs as workspaceControlRequestLockTimeoutMs2,
|
|
53723
53950
|
workspaceXaiSubscriptionActive,
|
|
53724
53951
|
workspaceVercelAiGatewayConnectionActive,
|
|
53952
|
+
workspaceOpenRouterConnectionActive,
|
|
53953
|
+
WorkspaceGatewayCustomModelHistoryLimitError,
|
|
53954
|
+
WorkspaceOpenRouterCustomModelHistoryLimitError,
|
|
53725
53955
|
WorkspaceExternalIdentityConflictError,
|
|
53956
|
+
WorkspaceGatewayCustomModelLimitError,
|
|
53957
|
+
WorkspaceOpenRouterCustomModelLimitError,
|
|
53726
53958
|
WorkspaceLimitExceededError
|
|
53727
53959
|
} from "@opengeni/db";
|
|
53728
53960
|
import { boundWorkspaceControlHttpPage } from "@opengeni/events";
|
|
@@ -53733,7 +53965,8 @@ import {
|
|
|
53733
53965
|
hasPermission as hasPermission19,
|
|
53734
53966
|
requireAccessContext as requireAccessContext3,
|
|
53735
53967
|
requireAccessGrant as requireAccessGrant32,
|
|
53736
|
-
requireFreshAccessGrant as requireFreshAccessGrant3
|
|
53968
|
+
requireFreshAccessGrant as requireFreshAccessGrant3,
|
|
53969
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings6
|
|
53737
53970
|
} from "@opengeni/core";
|
|
53738
53971
|
import { requireLimit as requireLimit9 } from "@opengeni/core";
|
|
53739
53972
|
import {
|
|
@@ -53825,7 +54058,14 @@ import {
|
|
|
53825
54058
|
CODEX_REALTIME_MODEL_ID,
|
|
53826
54059
|
SUPERGROK_REALTIME_MODEL_ID as SUPERGROK_REALTIME_MODEL_ID2,
|
|
53827
54060
|
canonicalizeConfiguredModelId,
|
|
53828
|
-
configuredStaticUsageLimits as configuredStaticUsageLimits2
|
|
54061
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits2,
|
|
54062
|
+
configuredGatewayUpstreamModelIds,
|
|
54063
|
+
configuredGatewayWorkspaceProductModelIds,
|
|
54064
|
+
configuredModelInputIdentities,
|
|
54065
|
+
configuredOpenRouterUpstreamModelIds,
|
|
54066
|
+
configuredOpenRouterWorkspaceProductModelIds,
|
|
54067
|
+
WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
|
|
54068
|
+
WORKSPACE_OPENROUTER_MODEL_ID_PREFIX
|
|
53829
54069
|
} from "@opengeni/config";
|
|
53830
54070
|
function canonicalWorkspacePolicyModelIds(settings, modelIds) {
|
|
53831
54071
|
if (modelIds === null || modelIds === void 0) {
|
|
@@ -53833,6 +54073,29 @@ function canonicalWorkspacePolicyModelIds(settings, modelIds) {
|
|
|
53833
54073
|
}
|
|
53834
54074
|
return [...new Set(modelIds.map((modelId) => canonicalizeConfiguredModelId(settings, modelId)))];
|
|
53835
54075
|
}
|
|
54076
|
+
function projectWorkspaceGatewayCustomModel(model) {
|
|
54077
|
+
return WorkspaceGatewayCustomModel.parse({
|
|
54078
|
+
id: model.id,
|
|
54079
|
+
upstreamModelId: model.upstreamModelId,
|
|
54080
|
+
label: model.label,
|
|
54081
|
+
version: model.version,
|
|
54082
|
+
createdAt: model.createdAt.toISOString(),
|
|
54083
|
+
updatedAt: model.updatedAt.toISOString()
|
|
54084
|
+
});
|
|
54085
|
+
}
|
|
54086
|
+
function projectWorkspaceOpenRouterCustomModel(model) {
|
|
54087
|
+
return WorkspaceOpenRouterCustomModel.parse({
|
|
54088
|
+
id: model.id,
|
|
54089
|
+
upstreamModelId: model.upstreamModelId,
|
|
54090
|
+
label: model.label,
|
|
54091
|
+
version: model.version,
|
|
54092
|
+
createdAt: model.createdAt.toISOString(),
|
|
54093
|
+
updatedAt: model.updatedAt.toISOString()
|
|
54094
|
+
});
|
|
54095
|
+
}
|
|
54096
|
+
function workspaceCustomModelRequestHash(value) {
|
|
54097
|
+
return createHash16("sha256").update(stableJson6(value), "utf8").digest("hex");
|
|
54098
|
+
}
|
|
53836
54099
|
function workspaceMembersResponse(members) {
|
|
53837
54100
|
return ListWorkspaceMembersResponse.parse({
|
|
53838
54101
|
members: members.map((member) => ({
|
|
@@ -53906,7 +54169,9 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
53906
54169
|
externalId: payload.externalId,
|
|
53907
54170
|
name: payload.name,
|
|
53908
54171
|
slug: payload.slug ?? null,
|
|
53909
|
-
...payload.agentInstructions !== void 0 ? {
|
|
54172
|
+
...payload.agentInstructions !== void 0 ? {
|
|
54173
|
+
agentInstructions: normalizeAgentInstructions(payload.agentInstructions)
|
|
54174
|
+
} : {},
|
|
53910
54175
|
maxWorkspacesPerAccount: workspaceLimit(deps)
|
|
53911
54176
|
});
|
|
53912
54177
|
const response = EnsureWorkspaceResponse.parse(result);
|
|
@@ -54012,29 +54277,304 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
54012
54277
|
const workspaceId = c.req.param("workspaceId");
|
|
54013
54278
|
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:read");
|
|
54014
54279
|
const [
|
|
54280
|
+
resolvedCatalog,
|
|
54015
54281
|
policy,
|
|
54016
54282
|
codexSubscriptionActive,
|
|
54017
54283
|
xaiSubscriptionActive,
|
|
54018
|
-
workspaceGatewayConnectionActive
|
|
54284
|
+
workspaceGatewayConnectionActive,
|
|
54285
|
+
workspaceGatewayCustomModels,
|
|
54286
|
+
openRouterConnectionActive,
|
|
54287
|
+
workspaceOpenRouterCustomModels
|
|
54019
54288
|
] = await Promise.all([
|
|
54289
|
+
deps.resolveCatalogSettings(),
|
|
54020
54290
|
getWorkspaceModelPolicy(deps.db, workspaceId),
|
|
54021
54291
|
workspaceCodexSubscriptionActive(deps.db, deps.settings, workspaceId),
|
|
54022
54292
|
workspaceXaiSubscriptionActive(deps.db, deps.settings, workspaceId, grant.subjectId),
|
|
54023
|
-
workspaceVercelAiGatewayConnectionActive(deps.db, workspaceId)
|
|
54293
|
+
workspaceVercelAiGatewayConnectionActive(deps.db, workspaceId),
|
|
54294
|
+
listWorkspaceGatewayCustomModels(deps.db, {
|
|
54295
|
+
accountId: grant.accountId,
|
|
54296
|
+
workspaceId
|
|
54297
|
+
}),
|
|
54298
|
+
workspaceOpenRouterConnectionActive(deps.db, workspaceId),
|
|
54299
|
+
listWorkspaceOpenRouterCustomModels(deps.db, {
|
|
54300
|
+
accountId: grant.accountId,
|
|
54301
|
+
workspaceId
|
|
54302
|
+
})
|
|
54024
54303
|
]);
|
|
54025
54304
|
c.header("cache-control", "private, no-store");
|
|
54026
54305
|
return c.json(
|
|
54027
54306
|
WorkspaceModelCatalogResponse2.parse(
|
|
54028
54307
|
buildWorkspaceModelCatalog({
|
|
54029
|
-
settings:
|
|
54308
|
+
settings: resolvedCatalog.settings,
|
|
54030
54309
|
policy,
|
|
54031
54310
|
codexSubscriptionActive,
|
|
54032
54311
|
xaiSubscriptionActive,
|
|
54033
|
-
workspaceGatewayConnectionActive
|
|
54312
|
+
workspaceGatewayConnectionActive,
|
|
54313
|
+
workspaceGatewayCustomModels,
|
|
54314
|
+
workspaceOpenRouterConnectionActive: openRouterConnectionActive,
|
|
54315
|
+
workspaceOpenRouterCustomModels
|
|
54034
54316
|
})
|
|
54035
54317
|
)
|
|
54036
54318
|
);
|
|
54037
54319
|
});
|
|
54320
|
+
app.get("/v1/workspaces/:workspaceId/gateway-custom-models", async (c) => {
|
|
54321
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54322
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:read");
|
|
54323
|
+
const models = await listWorkspaceGatewayCustomModels(deps.db, {
|
|
54324
|
+
accountId: grant.accountId,
|
|
54325
|
+
workspaceId
|
|
54326
|
+
});
|
|
54327
|
+
c.header("cache-control", "private, no-store");
|
|
54328
|
+
return c.json(
|
|
54329
|
+
WorkspaceGatewayCustomModelsResponse.parse({
|
|
54330
|
+
models: models.map(projectWorkspaceGatewayCustomModel)
|
|
54331
|
+
})
|
|
54332
|
+
);
|
|
54333
|
+
});
|
|
54334
|
+
app.post("/v1/workspaces/:workspaceId/gateway-custom-models", async (c) => {
|
|
54335
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54336
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:admin");
|
|
54337
|
+
const parsed = CreateWorkspaceGatewayCustomModelRequest.safeParse(
|
|
54338
|
+
await c.req.json().catch(() => null)
|
|
54339
|
+
);
|
|
54340
|
+
if (!parsed.success) {
|
|
54341
|
+
throw new HTTPException55(422, { message: "invalid Gateway custom model" });
|
|
54342
|
+
}
|
|
54343
|
+
const requestHash = workspaceCustomModelRequestHash({
|
|
54344
|
+
action: "create",
|
|
54345
|
+
upstreamModelId: parsed.data.upstreamModelId,
|
|
54346
|
+
label: parsed.data.label ?? null
|
|
54347
|
+
});
|
|
54348
|
+
const replay = await replayWorkspaceGatewayCustomModelCreate(deps.db, {
|
|
54349
|
+
accountId: grant.accountId,
|
|
54350
|
+
workspaceId,
|
|
54351
|
+
operationId: parsed.data.operationId,
|
|
54352
|
+
requestHash
|
|
54353
|
+
});
|
|
54354
|
+
if (replay.outcome === "conflict") {
|
|
54355
|
+
throw new HTTPException55(409, {
|
|
54356
|
+
message: "Gateway custom model operation conflicts with current state"
|
|
54357
|
+
});
|
|
54358
|
+
}
|
|
54359
|
+
if (replay.outcome === "success") {
|
|
54360
|
+
return c.json(projectWorkspaceGatewayCustomModel(replay.model), 201);
|
|
54361
|
+
}
|
|
54362
|
+
const catalog = await deps.resolveCatalogSettings();
|
|
54363
|
+
if (configuredGatewayUpstreamModelIds(catalog.settings).includes(parsed.data.upstreamModelId)) {
|
|
54364
|
+
throw new HTTPException55(422, {
|
|
54365
|
+
message: "Gateway model is already included in the deployment catalog"
|
|
54366
|
+
});
|
|
54367
|
+
}
|
|
54368
|
+
const customProductId = `${WORKSPACE_GATEWAY_MODEL_ID_PREFIX}${parsed.data.upstreamModelId}`;
|
|
54369
|
+
const deploymentProductIds = /* @__PURE__ */ new Set([
|
|
54370
|
+
...configuredModelInputIdentities(catalog.settings),
|
|
54371
|
+
...configuredGatewayWorkspaceProductModelIds(catalog.settings)
|
|
54372
|
+
]);
|
|
54373
|
+
if (deploymentProductIds.has(customProductId)) {
|
|
54374
|
+
throw new HTTPException55(422, {
|
|
54375
|
+
message: "Gateway model product id conflicts with the deployment catalog"
|
|
54376
|
+
});
|
|
54377
|
+
}
|
|
54378
|
+
try {
|
|
54379
|
+
const model = await createWorkspaceGatewayCustomModel(deps.db, {
|
|
54380
|
+
accountId: grant.accountId,
|
|
54381
|
+
workspaceId,
|
|
54382
|
+
upstreamModelId: parsed.data.upstreamModelId,
|
|
54383
|
+
label: parsed.data.label ?? null,
|
|
54384
|
+
operationId: parsed.data.operationId,
|
|
54385
|
+
requestHash,
|
|
54386
|
+
createdBySubjectId: grant.subjectId
|
|
54387
|
+
});
|
|
54388
|
+
if (!model || model.retiredAt) {
|
|
54389
|
+
throw new HTTPException55(409, {
|
|
54390
|
+
message: "Gateway custom model operation conflicts with current state"
|
|
54391
|
+
});
|
|
54392
|
+
}
|
|
54393
|
+
return c.json(projectWorkspaceGatewayCustomModel(model), 201);
|
|
54394
|
+
} catch (error) {
|
|
54395
|
+
if (error instanceof WorkspaceGatewayCustomModelLimitError || error instanceof WorkspaceGatewayCustomModelHistoryLimitError) {
|
|
54396
|
+
throw new HTTPException55(422, { message: error.message });
|
|
54397
|
+
}
|
|
54398
|
+
if (nestedPostgresSqlState4(error) === "23505") {
|
|
54399
|
+
throw new HTTPException55(422, {
|
|
54400
|
+
message: "Gateway custom model already exists"
|
|
54401
|
+
});
|
|
54402
|
+
}
|
|
54403
|
+
throw error;
|
|
54404
|
+
}
|
|
54405
|
+
});
|
|
54406
|
+
app.delete("/v1/workspaces/:workspaceId/gateway-custom-models/:customModelId", async (c) => {
|
|
54407
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54408
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:admin");
|
|
54409
|
+
const customModelId = c.req.param("customModelId");
|
|
54410
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
|
|
54411
|
+
customModelId
|
|
54412
|
+
)) {
|
|
54413
|
+
throw new HTTPException55(422, {
|
|
54414
|
+
message: "invalid Gateway custom model id"
|
|
54415
|
+
});
|
|
54416
|
+
}
|
|
54417
|
+
const parsed = DeleteWorkspaceGatewayCustomModelRequest.safeParse(
|
|
54418
|
+
await c.req.json().catch(() => null)
|
|
54419
|
+
);
|
|
54420
|
+
if (!parsed.success) {
|
|
54421
|
+
throw new HTTPException55(422, {
|
|
54422
|
+
message: "invalid Gateway custom model deletion"
|
|
54423
|
+
});
|
|
54424
|
+
}
|
|
54425
|
+
const removed = await deleteWorkspaceGatewayCustomModel(deps.db, {
|
|
54426
|
+
accountId: grant.accountId,
|
|
54427
|
+
workspaceId,
|
|
54428
|
+
customModelId,
|
|
54429
|
+
expectedVersion: parsed.data.expectedVersion,
|
|
54430
|
+
operationId: parsed.data.operationId,
|
|
54431
|
+
requestHash: workspaceCustomModelRequestHash({
|
|
54432
|
+
action: "delete",
|
|
54433
|
+
customModelId,
|
|
54434
|
+
expectedVersion: parsed.data.expectedVersion
|
|
54435
|
+
})
|
|
54436
|
+
});
|
|
54437
|
+
if (removed.outcome === "not_found") {
|
|
54438
|
+
throw new HTTPException55(404, {
|
|
54439
|
+
message: "Gateway custom model not found"
|
|
54440
|
+
});
|
|
54441
|
+
}
|
|
54442
|
+
if (removed.outcome === "conflict") {
|
|
54443
|
+
throw new HTTPException55(409, {
|
|
54444
|
+
message: "Gateway custom model changed; reload and retry"
|
|
54445
|
+
});
|
|
54446
|
+
}
|
|
54447
|
+
return c.body(null, 204);
|
|
54448
|
+
});
|
|
54449
|
+
app.get("/v1/workspaces/:workspaceId/openrouter-custom-models", async (c) => {
|
|
54450
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54451
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:read");
|
|
54452
|
+
const models = await listWorkspaceOpenRouterCustomModels(deps.db, {
|
|
54453
|
+
accountId: grant.accountId,
|
|
54454
|
+
workspaceId
|
|
54455
|
+
});
|
|
54456
|
+
c.header("cache-control", "private, no-store");
|
|
54457
|
+
return c.json(
|
|
54458
|
+
WorkspaceOpenRouterCustomModelsResponse.parse({
|
|
54459
|
+
models: models.map(projectWorkspaceOpenRouterCustomModel)
|
|
54460
|
+
})
|
|
54461
|
+
);
|
|
54462
|
+
});
|
|
54463
|
+
app.post("/v1/workspaces/:workspaceId/openrouter-custom-models", async (c) => {
|
|
54464
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54465
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:admin");
|
|
54466
|
+
const parsed = CreateWorkspaceOpenRouterCustomModelRequest.safeParse(
|
|
54467
|
+
await c.req.json().catch(() => null)
|
|
54468
|
+
);
|
|
54469
|
+
if (!parsed.success) {
|
|
54470
|
+
throw new HTTPException55(422, { message: "invalid OpenRouter custom model" });
|
|
54471
|
+
}
|
|
54472
|
+
const requestHash = workspaceCustomModelRequestHash({
|
|
54473
|
+
action: "create",
|
|
54474
|
+
upstreamModelId: parsed.data.upstreamModelId,
|
|
54475
|
+
label: parsed.data.label ?? null
|
|
54476
|
+
});
|
|
54477
|
+
const replay = await replayWorkspaceOpenRouterCustomModelCreate(deps.db, {
|
|
54478
|
+
accountId: grant.accountId,
|
|
54479
|
+
workspaceId,
|
|
54480
|
+
operationId: parsed.data.operationId,
|
|
54481
|
+
requestHash
|
|
54482
|
+
});
|
|
54483
|
+
if (replay.outcome === "conflict") {
|
|
54484
|
+
throw new HTTPException55(409, {
|
|
54485
|
+
message: "OpenRouter custom model operation conflicts with current state"
|
|
54486
|
+
});
|
|
54487
|
+
}
|
|
54488
|
+
if (replay.outcome === "success") {
|
|
54489
|
+
return c.json(projectWorkspaceOpenRouterCustomModel(replay.model), 201);
|
|
54490
|
+
}
|
|
54491
|
+
const catalog = await deps.resolveCatalogSettings();
|
|
54492
|
+
if (configuredOpenRouterUpstreamModelIds(catalog.settings).includes(parsed.data.upstreamModelId)) {
|
|
54493
|
+
throw new HTTPException55(422, {
|
|
54494
|
+
message: "OpenRouter model is already included in the deployment catalog"
|
|
54495
|
+
});
|
|
54496
|
+
}
|
|
54497
|
+
const customProductId = `${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${parsed.data.upstreamModelId}`;
|
|
54498
|
+
const deploymentProductIds = /* @__PURE__ */ new Set([
|
|
54499
|
+
...configuredModelInputIdentities(catalog.settings),
|
|
54500
|
+
...configuredOpenRouterWorkspaceProductModelIds(catalog.settings)
|
|
54501
|
+
]);
|
|
54502
|
+
if (deploymentProductIds.has(customProductId)) {
|
|
54503
|
+
throw new HTTPException55(422, {
|
|
54504
|
+
message: "OpenRouter model product id conflicts with the deployment catalog"
|
|
54505
|
+
});
|
|
54506
|
+
}
|
|
54507
|
+
try {
|
|
54508
|
+
const model = await createWorkspaceOpenRouterCustomModel(deps.db, {
|
|
54509
|
+
accountId: grant.accountId,
|
|
54510
|
+
workspaceId,
|
|
54511
|
+
upstreamModelId: parsed.data.upstreamModelId,
|
|
54512
|
+
label: parsed.data.label ?? null,
|
|
54513
|
+
operationId: parsed.data.operationId,
|
|
54514
|
+
requestHash,
|
|
54515
|
+
createdBySubjectId: grant.subjectId
|
|
54516
|
+
});
|
|
54517
|
+
if (!model || model.retiredAt) {
|
|
54518
|
+
throw new HTTPException55(409, {
|
|
54519
|
+
message: "OpenRouter custom model operation conflicts with current state"
|
|
54520
|
+
});
|
|
54521
|
+
}
|
|
54522
|
+
return c.json(projectWorkspaceOpenRouterCustomModel(model), 201);
|
|
54523
|
+
} catch (error) {
|
|
54524
|
+
if (error instanceof WorkspaceOpenRouterCustomModelLimitError || error instanceof WorkspaceOpenRouterCustomModelHistoryLimitError) {
|
|
54525
|
+
throw new HTTPException55(422, { message: error.message });
|
|
54526
|
+
}
|
|
54527
|
+
if (nestedPostgresSqlState4(error) === "23505") {
|
|
54528
|
+
throw new HTTPException55(422, {
|
|
54529
|
+
message: "OpenRouter custom model already exists"
|
|
54530
|
+
});
|
|
54531
|
+
}
|
|
54532
|
+
throw error;
|
|
54533
|
+
}
|
|
54534
|
+
});
|
|
54535
|
+
app.delete("/v1/workspaces/:workspaceId/openrouter-custom-models/:customModelId", async (c) => {
|
|
54536
|
+
const workspaceId = c.req.param("workspaceId");
|
|
54537
|
+
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:admin");
|
|
54538
|
+
const customModelId = c.req.param("customModelId");
|
|
54539
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
|
|
54540
|
+
customModelId
|
|
54541
|
+
)) {
|
|
54542
|
+
throw new HTTPException55(422, {
|
|
54543
|
+
message: "invalid OpenRouter custom model id"
|
|
54544
|
+
});
|
|
54545
|
+
}
|
|
54546
|
+
const parsed = DeleteWorkspaceOpenRouterCustomModelRequest.safeParse(
|
|
54547
|
+
await c.req.json().catch(() => null)
|
|
54548
|
+
);
|
|
54549
|
+
if (!parsed.success) {
|
|
54550
|
+
throw new HTTPException55(422, {
|
|
54551
|
+
message: "invalid OpenRouter custom model deletion"
|
|
54552
|
+
});
|
|
54553
|
+
}
|
|
54554
|
+
const removed = await deleteWorkspaceOpenRouterCustomModel(deps.db, {
|
|
54555
|
+
accountId: grant.accountId,
|
|
54556
|
+
workspaceId,
|
|
54557
|
+
customModelId,
|
|
54558
|
+
expectedVersion: parsed.data.expectedVersion,
|
|
54559
|
+
operationId: parsed.data.operationId,
|
|
54560
|
+
requestHash: workspaceCustomModelRequestHash({
|
|
54561
|
+
action: "delete",
|
|
54562
|
+
customModelId,
|
|
54563
|
+
expectedVersion: parsed.data.expectedVersion
|
|
54564
|
+
})
|
|
54565
|
+
});
|
|
54566
|
+
if (removed.outcome === "not_found") {
|
|
54567
|
+
throw new HTTPException55(404, {
|
|
54568
|
+
message: "OpenRouter custom model not found"
|
|
54569
|
+
});
|
|
54570
|
+
}
|
|
54571
|
+
if (removed.outcome === "conflict") {
|
|
54572
|
+
throw new HTTPException55(409, {
|
|
54573
|
+
message: "OpenRouter custom model changed; reload and retry"
|
|
54574
|
+
});
|
|
54575
|
+
}
|
|
54576
|
+
return c.body(null, 204);
|
|
54577
|
+
});
|
|
54038
54578
|
app.get("/v1/workspaces/:workspaceId/realtime-model-catalog", async (c) => {
|
|
54039
54579
|
const workspaceId = c.req.param("workspaceId");
|
|
54040
54580
|
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:read");
|
|
@@ -54100,11 +54640,15 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
54100
54640
|
const workspaceId = c.req.param("workspaceId");
|
|
54101
54641
|
const grant = await requireAccessGrant32(c, deps, workspaceId, "workspace:admin");
|
|
54102
54642
|
const payload = UpdateWorkspaceModelPolicyRequest.parse(await c.req.json());
|
|
54643
|
+
const catalog = await resolveWorkspaceCatalogSettings6(deps.db, deps.settings, {
|
|
54644
|
+
accountId: grant.accountId,
|
|
54645
|
+
workspaceId
|
|
54646
|
+
});
|
|
54103
54647
|
const policy = await upsertWorkspaceModelPolicy(deps.db, {
|
|
54104
54648
|
accountId: grant.accountId,
|
|
54105
54649
|
workspaceId,
|
|
54106
54650
|
allowedProviders: payload.allowedProviders ?? null,
|
|
54107
|
-
allowedModels: canonicalWorkspacePolicyModelIds(
|
|
54651
|
+
allowedModels: canonicalWorkspacePolicyModelIds(catalog.settings, payload.allowedModels)
|
|
54108
54652
|
});
|
|
54109
54653
|
return c.json(policy);
|
|
54110
54654
|
});
|
|
@@ -55288,7 +55832,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
55288
55832
|
}
|
|
55289
55833
|
|
|
55290
55834
|
// src/routes/company-brain.ts
|
|
55291
|
-
import { createHash as
|
|
55835
|
+
import { createHash as createHash18 } from "crypto";
|
|
55292
55836
|
import {
|
|
55293
55837
|
COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
|
|
55294
55838
|
COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
|
|
@@ -55565,7 +56109,7 @@ function serializeCompanyBrainOkf(value) {
|
|
|
55565
56109
|
}
|
|
55566
56110
|
|
|
55567
56111
|
// src/workspace-state-projection.ts
|
|
55568
|
-
import { createHash as
|
|
56112
|
+
import { createHash as createHash17 } from "crypto";
|
|
55569
56113
|
import {
|
|
55570
56114
|
KnowledgeMemoryKind,
|
|
55571
56115
|
KnowledgeMemoryStatus,
|
|
@@ -55579,7 +56123,7 @@ import {
|
|
|
55579
56123
|
WorkspaceStateResponse
|
|
55580
56124
|
} from "@opengeni/contracts";
|
|
55581
56125
|
function hashIdentities(values) {
|
|
55582
|
-
return
|
|
56126
|
+
return createHash17("sha256").update(values.join("\n"), "utf8").digest("hex");
|
|
55583
56127
|
}
|
|
55584
56128
|
function policyTargetKey(value) {
|
|
55585
56129
|
return `${value.kind}:${value.scope}:${value.roleKey ?? ""}`;
|
|
@@ -55922,7 +56466,7 @@ var inspectorQuery = z18.object({
|
|
|
55922
56466
|
message: "attemptId cannot be combined with cursor"
|
|
55923
56467
|
});
|
|
55924
56468
|
function receiptCursorScope(workspaceId, subjectId) {
|
|
55925
|
-
return
|
|
56469
|
+
return createHash18("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
|
|
55926
56470
|
}
|
|
55927
56471
|
function encodeReceiptCursor(workspaceId, subjectId, cursor) {
|
|
55928
56472
|
return Buffer.from(
|
|
@@ -56294,7 +56838,7 @@ import { getDocumentInventory as getDocumentInventory2 } from "@opengeni/documen
|
|
|
56294
56838
|
import { HTTPException as HTTPException61 } from "hono/http-exception";
|
|
56295
56839
|
|
|
56296
56840
|
// src/workspace-state-export.ts
|
|
56297
|
-
import { createHash as
|
|
56841
|
+
import { createHash as createHash19 } from "crypto";
|
|
56298
56842
|
import {
|
|
56299
56843
|
WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
|
|
56300
56844
|
WorkspaceStateExportResponse
|
|
@@ -56325,7 +56869,7 @@ function createWorkspaceStateExport(state) {
|
|
|
56325
56869
|
kind: "opengeni.workspace_state.sanitized_export",
|
|
56326
56870
|
schemaVersion: WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
|
|
56327
56871
|
generatedAt: state.generatedAt,
|
|
56328
|
-
stateSha256:
|
|
56872
|
+
stateSha256: createHash19("sha256").update(canonicalState, "utf8").digest("hex"),
|
|
56329
56873
|
omissions: WORKSPACE_STATE_EXPORT_OMISSIONS,
|
|
56330
56874
|
state
|
|
56331
56875
|
});
|
|
@@ -56425,7 +56969,7 @@ function registerWorkspaceStateRoutes(app, deps) {
|
|
|
56425
56969
|
}
|
|
56426
56970
|
|
|
56427
56971
|
// src/routes/workspace-artifacts.ts
|
|
56428
|
-
import { createHash as
|
|
56972
|
+
import { createHash as createHash20 } from "crypto";
|
|
56429
56973
|
import {
|
|
56430
56974
|
CreateWorkspaceArtifactRequest,
|
|
56431
56975
|
PublishWorkspaceArtifactVersionRequest,
|
|
@@ -56495,7 +57039,7 @@ function errorResponse(context, error) {
|
|
|
56495
57039
|
}
|
|
56496
57040
|
function contentMetadata(workspaceId, html) {
|
|
56497
57041
|
const bytes = encoder3.encode(html);
|
|
56498
|
-
const sha2564 =
|
|
57042
|
+
const sha2564 = createHash20("sha256").update(bytes).digest("hex");
|
|
56499
57043
|
return {
|
|
56500
57044
|
bytes,
|
|
56501
57045
|
contentSha256: sha2564,
|
|
@@ -56521,7 +57065,7 @@ function prepareHtml(deps, workspaceId, html) {
|
|
|
56521
57065
|
}
|
|
56522
57066
|
function provenance(subjectId, idempotencyKey) {
|
|
56523
57067
|
return {
|
|
56524
|
-
operationKey: `subject:${
|
|
57068
|
+
operationKey: `subject:${createHash20("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
|
|
56525
57069
|
actorSubjectId: subjectId,
|
|
56526
57070
|
sourceSessionId: null,
|
|
56527
57071
|
sourceTurnId: null,
|
|
@@ -56614,7 +57158,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
56614
57158
|
async () => deps.objectStorage.getObjectBytes(ref.contentKey)
|
|
56615
57159
|
);
|
|
56616
57160
|
if (!object10) throw new HTTPException62(503, { message: "Artifact content is unavailable" });
|
|
56617
|
-
const actualHash =
|
|
57161
|
+
const actualHash = createHash20("sha256").update(object10.bytes).digest("hex");
|
|
56618
57162
|
if (actualHash !== ref.version.contentSha256) {
|
|
56619
57163
|
throw new HTTPException62(503, { message: "Artifact content failed integrity verification" });
|
|
56620
57164
|
}
|
|
@@ -57181,7 +57725,7 @@ import { requireAccessGrant as requireAccessGrant43, TranscriptionServiceError a
|
|
|
57181
57725
|
import { getWorkspace as getWorkspace5 } from "@opengeni/db";
|
|
57182
57726
|
|
|
57183
57727
|
// src/routes/transcription-recordings.ts
|
|
57184
|
-
import { createHash as
|
|
57728
|
+
import { createHash as createHash21 } from "crypto";
|
|
57185
57729
|
import {
|
|
57186
57730
|
CreateTranscriptionRecordingRequest,
|
|
57187
57731
|
FinalizeTranscriptionRecordingRequest,
|
|
@@ -58004,7 +58548,7 @@ function valueAsUuid(value) {
|
|
|
58004
58548
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
|
|
58005
58549
|
return value;
|
|
58006
58550
|
}
|
|
58007
|
-
const hex2 =
|
|
58551
|
+
const hex2 = createHash21("sha256").update(value).digest("hex").slice(0, 32);
|
|
58008
58552
|
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-4${hex2.slice(13, 16)}-a${hex2.slice(17, 20)}-${hex2.slice(20, 32)}`;
|
|
58009
58553
|
}
|
|
58010
58554
|
function nonnegativeInteger(value) {
|
|
@@ -58068,7 +58612,7 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
58068
58612
|
return body4;
|
|
58069
58613
|
}
|
|
58070
58614
|
function sha256Hex3(bytes) {
|
|
58071
|
-
return
|
|
58615
|
+
return createHash21("sha256").update(bytes).digest("hex");
|
|
58072
58616
|
}
|
|
58073
58617
|
|
|
58074
58618
|
// src/routes/transcriptions.ts
|
|
@@ -61541,7 +62085,7 @@ async function exactAvailable(providers, providerId, context) {
|
|
|
61541
62085
|
}
|
|
61542
62086
|
|
|
61543
62087
|
// src/integrations/slack-interactions.ts
|
|
61544
|
-
import { createHash as
|
|
62088
|
+
import { createHash as createHash23, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
61545
62089
|
import {
|
|
61546
62090
|
approvalIdentifier,
|
|
61547
62091
|
ApproveSlackUserLinkAccessRequest,
|
|
@@ -61647,7 +62191,8 @@ import {
|
|
|
61647
62191
|
hasPermission as hasPermission24,
|
|
61648
62192
|
requireAccessContext as requireAccessContext4,
|
|
61649
62193
|
requireAccessGrant as requireAccessGrant45,
|
|
61650
|
-
requireSessionAuthorizationListScope as requireSessionAuthorizationListScope3
|
|
62194
|
+
requireSessionAuthorizationListScope as requireSessionAuthorizationListScope3,
|
|
62195
|
+
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings7
|
|
61651
62196
|
} from "@opengeni/core";
|
|
61652
62197
|
import { publishDurableSessionEvents as publishDurableSessionEvents4 } from "@opengeni/events";
|
|
61653
62198
|
import { HTTPException as HTTPException74 } from "hono/http-exception";
|
|
@@ -62018,7 +62563,7 @@ function record5(value) {
|
|
|
62018
62563
|
}
|
|
62019
62564
|
|
|
62020
62565
|
// src/slack-reaction-files.ts
|
|
62021
|
-
import { createHash as
|
|
62566
|
+
import { createHash as createHash22 } from "crypto";
|
|
62022
62567
|
import { completeFileUpload as completeFileUpload5, prepareGeneratedWorkspaceFile as prepareGeneratedWorkspaceFile2 } from "@opengeni/db";
|
|
62023
62568
|
import { retryWhileMissing as retryWhileMissing7 } from "@opengeni/storage";
|
|
62024
62569
|
var IMPORT_IDENTITY_VERSION = "slack-reaction-image-v1";
|
|
@@ -62035,7 +62580,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
|
|
|
62035
62580
|
if (image.bytes.byteLength < 1 || image.bytes.byteLength > 4 * 1024 * 1024) {
|
|
62036
62581
|
throw new Error("Slack reaction image size is invalid");
|
|
62037
62582
|
}
|
|
62038
|
-
const sha2564 =
|
|
62583
|
+
const sha2564 = createHash22("sha256").update(image.bytes).digest("hex");
|
|
62039
62584
|
const identity = slackReactionImageIdentity(source, image.fileId);
|
|
62040
62585
|
const fileId = deterministicUuid(`${identity}:file`);
|
|
62041
62586
|
const uploadId = deterministicUuid(`${identity}:upload`);
|
|
@@ -62129,7 +62674,7 @@ function assertStoredSlackImage(head, image, sha2564) {
|
|
|
62129
62674
|
}
|
|
62130
62675
|
}
|
|
62131
62676
|
function deterministicUuid(seed) {
|
|
62132
|
-
const bytes =
|
|
62677
|
+
const bytes = createHash22("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
62133
62678
|
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
62134
62679
|
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
62135
62680
|
const hex2 = bytes.toString("hex");
|
|
@@ -62229,7 +62774,7 @@ function verifySlackRequestSignature(input, signingSecret, nowMs = Date.now()) {
|
|
|
62229
62774
|
if (!Number.isSafeInteger(timestamp)) return false;
|
|
62230
62775
|
const nowSeconds = Math.floor(nowMs / 1e3);
|
|
62231
62776
|
if (Math.abs(nowSeconds - timestamp) > SLACK_SIGNATURE_REPLAY_WINDOW_SECONDS) return false;
|
|
62232
|
-
const expected = `v0=${
|
|
62777
|
+
const expected = `v0=${createHmac9("sha256", signingSecret).update(`v0:${input.timestamp}:${input.rawBody}`).digest("hex")}`;
|
|
62233
62778
|
const actualBytes = Buffer.from(input.signature, "utf8");
|
|
62234
62779
|
const expectedBytes = Buffer.from(expected, "utf8");
|
|
62235
62780
|
return actualBytes.length === expectedBytes.length && timingSafeEqual3(actualBytes, expectedBytes);
|
|
@@ -62288,7 +62833,7 @@ function slackReactionInboxEntry(payload, bot, settings) {
|
|
|
62288
62833
|
if (!settings.enabled || !event || event.type !== "reaction_added" || item?.type !== "message" || !teamId || !eventId || !userId || userId === bot.botUserId || !reaction || reaction !== settings.emoji || !channelId || !workspaceSlackReactionChannelAllowed(settings, channelId) || !timestamp) {
|
|
62289
62834
|
return null;
|
|
62290
62835
|
}
|
|
62291
|
-
const stableReactionIdentity =
|
|
62836
|
+
const stableReactionIdentity = createHash23("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
|
|
62292
62837
|
return {
|
|
62293
62838
|
providerEventId: eventId,
|
|
62294
62839
|
providerMessageId: `reaction:${stableReactionIdentity}`,
|
|
@@ -62478,7 +63023,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
62478
63023
|
const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
|
|
62479
63024
|
accountId: installation.accountId,
|
|
62480
63025
|
workspaceId,
|
|
62481
|
-
tokenDigest:
|
|
63026
|
+
tokenDigest: createHash23("sha256").update(payload.linkToken).digest("hex"),
|
|
62482
63027
|
connectionId: link.connectionId,
|
|
62483
63028
|
slackTeamId: link.slackTeamId,
|
|
62484
63029
|
slackUserId: link.slackUserId,
|
|
@@ -62685,7 +63230,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
62685
63230
|
const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
|
|
62686
63231
|
accountId: grant.accountId,
|
|
62687
63232
|
workspaceId,
|
|
62688
|
-
tokenDigest:
|
|
63233
|
+
tokenDigest: createHash23("sha256").update(linkToken).digest("hex"),
|
|
62689
63234
|
connectionId: link.connectionId,
|
|
62690
63235
|
slackTeamId: link.slackTeamId,
|
|
62691
63236
|
slackUserId: link.slackUserId,
|
|
@@ -62845,6 +63390,17 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
62845
63390
|
);
|
|
62846
63391
|
});
|
|
62847
63392
|
}
|
|
63393
|
+
async function withCatalogSettings(deps, grant) {
|
|
63394
|
+
const catalogSourceSettings = deps.catalogSourceSettings ?? deps.settings;
|
|
63395
|
+
return {
|
|
63396
|
+
...deps,
|
|
63397
|
+
catalogSourceSettings,
|
|
63398
|
+
settings: (await resolveWorkspaceCatalogSettings7(deps.db, catalogSourceSettings, {
|
|
63399
|
+
accountId: grant.accountId,
|
|
63400
|
+
workspaceId: grant.workspaceId
|
|
63401
|
+
})).settings
|
|
63402
|
+
};
|
|
63403
|
+
}
|
|
62848
63404
|
async function publishSlackAppHome(deps, installation, refresh, renewLease) {
|
|
62849
63405
|
const link = await getSlackBotUserLink(
|
|
62850
63406
|
deps.db,
|
|
@@ -63642,17 +64198,22 @@ async function processSlackInboxEntry(deps, entry2) {
|
|
|
63642
64198
|
preparedAttachments,
|
|
63643
64199
|
preparedModelContext
|
|
63644
64200
|
);
|
|
63645
|
-
session = await createSessionForRequest2(
|
|
63646
|
-
|
|
63647
|
-
|
|
63648
|
-
|
|
63649
|
-
|
|
63650
|
-
|
|
63651
|
-
|
|
63652
|
-
|
|
63653
|
-
|
|
63654
|
-
|
|
63655
|
-
|
|
64201
|
+
session = await createSessionForRequest2(
|
|
64202
|
+
await withCatalogSettings(deps, grant),
|
|
64203
|
+
grant,
|
|
64204
|
+
interaction.workspaceId,
|
|
64205
|
+
{
|
|
64206
|
+
requestedSessionId: interaction.sessionReservationId,
|
|
64207
|
+
initialMessage: prepared.entry.text,
|
|
64208
|
+
...prepared.modelContext ? { modelContext: prepared.modelContext } : {},
|
|
64209
|
+
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
64210
|
+
firstPartyMcpTools: slackTaskFirstPartyMcpTools(deps.settings),
|
|
64211
|
+
resources: preparedAttachments.resources,
|
|
64212
|
+
...preferredModel ? { model: preferredModel } : {},
|
|
64213
|
+
idempotencyKey: `slack:${entry2.connectionId}:${entry2.providerEventId}`,
|
|
64214
|
+
clientEventId: `slack:${entry2.providerEventId}`
|
|
64215
|
+
}
|
|
64216
|
+
);
|
|
63656
64217
|
} catch (error) {
|
|
63657
64218
|
if (error instanceof HTTPException74) {
|
|
63658
64219
|
await client.postMessage({
|
|
@@ -64069,21 +64630,26 @@ async function processSlackReactionInboxEntry(deps, entry2) {
|
|
|
64069
64630
|
const preparedEntry = slackReactionPreparedEntry(entry2, context, preparedTask);
|
|
64070
64631
|
let session;
|
|
64071
64632
|
try {
|
|
64072
|
-
session = await createSessionForRequest2(
|
|
64073
|
-
|
|
64074
|
-
|
|
64075
|
-
|
|
64076
|
-
|
|
64077
|
-
|
|
64078
|
-
|
|
64079
|
-
|
|
64080
|
-
|
|
64081
|
-
|
|
64082
|
-
|
|
64083
|
-
|
|
64084
|
-
|
|
64085
|
-
|
|
64086
|
-
|
|
64633
|
+
session = await createSessionForRequest2(
|
|
64634
|
+
await withCatalogSettings(deps, grant),
|
|
64635
|
+
grant,
|
|
64636
|
+
interaction.workspaceId,
|
|
64637
|
+
{
|
|
64638
|
+
requestedSessionId: interaction.sessionReservationId,
|
|
64639
|
+
initialMessage: preparedEntry.text,
|
|
64640
|
+
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
64641
|
+
// The exact reacted message and bounded containing thread are already in
|
|
64642
|
+
// the prompt; do not expose general Slack history tools for this trigger.
|
|
64643
|
+
firstPartyMcpTools: resolveFirstPartyMcpToolPolicy(deps.settings).default,
|
|
64644
|
+
resources: preparedTask.resources,
|
|
64645
|
+
...preferredModel ? { model: preferredModel } : {},
|
|
64646
|
+
// Every reaction entry converging on this route must use the same create
|
|
64647
|
+
// key. This closes the same-owner multi-event race while the owner check
|
|
64648
|
+
// above prevents a different subject from winning creation authority.
|
|
64649
|
+
idempotencyKey: `slack-interaction:${interaction.id}`,
|
|
64650
|
+
clientEventId: `slack:${entry2.providerEventId}`
|
|
64651
|
+
}
|
|
64652
|
+
);
|
|
64087
64653
|
await acceptSlackReactionTask(deps, grant, session.id, preparedEntry, preparedTask.resources);
|
|
64088
64654
|
} catch (error) {
|
|
64089
64655
|
if (error instanceof HTTPException74) {
|
|
@@ -64368,11 +64934,17 @@ async function acceptSlackReactionTask(deps, grant, sessionId, entry2, resources
|
|
|
64368
64934
|
}
|
|
64369
64935
|
return;
|
|
64370
64936
|
}
|
|
64371
|
-
await acceptSessionUserMessage2(
|
|
64372
|
-
|
|
64373
|
-
|
|
64374
|
-
|
|
64375
|
-
|
|
64937
|
+
await acceptSessionUserMessage2(
|
|
64938
|
+
await withCatalogSettings(deps, grant),
|
|
64939
|
+
grant,
|
|
64940
|
+
grant.workspaceId,
|
|
64941
|
+
sessionId,
|
|
64942
|
+
{
|
|
64943
|
+
text: entry2.text,
|
|
64944
|
+
resources,
|
|
64945
|
+
clientEventId
|
|
64946
|
+
}
|
|
64947
|
+
);
|
|
64376
64948
|
}
|
|
64377
64949
|
async function continueSlackSession(deps, grant, interaction, entry2, resources = [], options = {}) {
|
|
64378
64950
|
if (!interaction.sessionId || interaction.visibility === "private" && interaction.owningSubjectId !== grant.subjectId) {
|
|
@@ -64444,12 +65016,18 @@ async function continueSlackSession(deps, grant, interaction, entry2, resources
|
|
|
64444
65016
|
if (!hasPermission24(grant.permissions, "sessions:control")) {
|
|
64445
65017
|
throw new SlackInteractionPermanentError("sessions_control_denied");
|
|
64446
65018
|
}
|
|
64447
|
-
await acceptSessionUserMessage2(
|
|
64448
|
-
|
|
64449
|
-
|
|
64450
|
-
|
|
64451
|
-
|
|
64452
|
-
|
|
65019
|
+
await acceptSessionUserMessage2(
|
|
65020
|
+
await withCatalogSettings(deps, grant),
|
|
65021
|
+
grant,
|
|
65022
|
+
interaction.workspaceId,
|
|
65023
|
+
interaction.sessionId,
|
|
65024
|
+
{
|
|
65025
|
+
text: entry2.text,
|
|
65026
|
+
...options.modelContext ? { modelContext: options.modelContext } : {},
|
|
65027
|
+
resources,
|
|
65028
|
+
clientEventId: `slack:${entry2.providerEventId}`
|
|
65029
|
+
}
|
|
65030
|
+
);
|
|
64453
65031
|
}
|
|
64454
65032
|
var SLACK_ACTION_ID_BY_KIND = {
|
|
64455
65033
|
approval_approve: "opengeni.approval.approve",
|
|
@@ -65533,7 +66111,7 @@ function normalizedBlockActionInteraction(payload) {
|
|
|
65533
66111
|
if (!teamId || !userId || !channelId || !messageTs || !actionTs || !actionId || !SLACK_BLOCK_ACTION_IDS.has(actionId) || !handleId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(handleId) || !Array.isArray(payload.actions) || payload.actions.length !== 1) {
|
|
65534
66112
|
throw new HTTPException74(400, { message: "invalid Slack block action" });
|
|
65535
66113
|
}
|
|
65536
|
-
const identity =
|
|
66114
|
+
const identity = createHash23("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
|
|
65537
66115
|
return {
|
|
65538
66116
|
providerEventId: `block:${identity}`,
|
|
65539
66117
|
providerMessageId: `block:${identity}`,
|
|
@@ -65760,14 +66338,14 @@ function createSlackUserLinkToken(signingSecret, entry2, nowMs = Date.now()) {
|
|
|
65760
66338
|
}),
|
|
65761
66339
|
"utf8"
|
|
65762
66340
|
).toString("base64url");
|
|
65763
|
-
const signature =
|
|
66341
|
+
const signature = createHmac9("sha256", signingSecret).update(payload).digest("base64url");
|
|
65764
66342
|
return `${payload}.${signature}`;
|
|
65765
66343
|
}
|
|
65766
66344
|
function verifySlackUserLinkToken(signingSecret, token, nowMs = Date.now()) {
|
|
65767
66345
|
const [payload, signature, extra] = token.split(".");
|
|
65768
66346
|
if (!payload || !signature || extra || payload.length > 1500 || signature.length > 128)
|
|
65769
66347
|
return null;
|
|
65770
|
-
const expected =
|
|
66348
|
+
const expected = createHmac9("sha256", signingSecret).update(payload).digest("base64url");
|
|
65771
66349
|
const actualBytes = Buffer.from(signature, "utf8");
|
|
65772
66350
|
const expectedBytes = Buffer.from(expected, "utf8");
|
|
65773
66351
|
if (actualBytes.length !== expectedBytes.length || !timingSafeEqual3(actualBytes, expectedBytes)) {
|
|
@@ -65798,7 +66376,7 @@ function usesPrivateBotDm(interaction, entry2) {
|
|
|
65798
66376
|
return isDirectMessageShortcut(entry2) || interaction.visibility === "private" && entry2.triggerKind !== "dm";
|
|
65799
66377
|
}
|
|
65800
66378
|
function deterministicUuid2(value) {
|
|
65801
|
-
const bytes =
|
|
66379
|
+
const bytes = createHash23("sha256").update(value).digest().subarray(0, 16);
|
|
65802
66380
|
bytes[6] = bytes[6] & 15 | 80;
|
|
65803
66381
|
bytes[8] = bytes[8] & 63 | 128;
|
|
65804
66382
|
const hex2 = bytes.toString("hex");
|
|
@@ -65933,6 +66511,18 @@ var API_PUBLIC_ERROR_MESSAGE_MAX_BYTES = 512;
|
|
|
65933
66511
|
function createApp(deps) {
|
|
65934
66512
|
return createAppComposition(deps).app;
|
|
65935
66513
|
}
|
|
66514
|
+
async function resolveWorkspaceMcpRouteDeps(routeDeps, grant) {
|
|
66515
|
+
const catalogSourceSettings = routeDeps.catalogSourceSettings ?? routeDeps.settings;
|
|
66516
|
+
const resolvedCatalog = await resolveWorkspaceCatalogSettings8(
|
|
66517
|
+
routeDeps.db,
|
|
66518
|
+
catalogSourceSettings,
|
|
66519
|
+
{
|
|
66520
|
+
accountId: grant.accountId,
|
|
66521
|
+
workspaceId: grant.workspaceId
|
|
66522
|
+
}
|
|
66523
|
+
);
|
|
66524
|
+
return { ...routeDeps, catalogSourceSettings, settings: resolvedCatalog.settings };
|
|
66525
|
+
}
|
|
65936
66526
|
function createAppComposition(deps) {
|
|
65937
66527
|
configureWorkspaceControlRequestLockTimeoutMs(deps.settings.workspaceControlLockTimeoutMs);
|
|
65938
66528
|
configureChildLifecycleNotices({ enabled: deps.settings.childLifecycleNoticesEnabled });
|
|
@@ -66019,6 +66609,7 @@ function createAppComposition(deps) {
|
|
|
66019
66609
|
}) : deps.transcriptionSegmenter;
|
|
66020
66610
|
const routeDeps = {
|
|
66021
66611
|
...deps,
|
|
66612
|
+
resolveCatalogSettings: () => resolveCatalogSettings(deps.db, deps.settings),
|
|
66022
66613
|
observability,
|
|
66023
66614
|
githubStateSecret: deps.githubStateSecret ?? deps.settings.githubAppManifestStateSecret ?? crypto.randomUUID(),
|
|
66024
66615
|
managedAuth,
|
|
@@ -66374,8 +66965,10 @@ function createAppComposition(deps) {
|
|
|
66374
66965
|
);
|
|
66375
66966
|
app.get("/v1/config/client", async (c) => {
|
|
66376
66967
|
c.header("cache-control", "no-store");
|
|
66377
|
-
const
|
|
66378
|
-
const
|
|
66968
|
+
const resolvedCatalog = await resolveCatalogSettings(deps.db, deps.settings);
|
|
66969
|
+
const baseCatalogSettings = resolvedCatalog.settings;
|
|
66970
|
+
const codexCatalogSettings = baseCatalogSettings.codexSubscriptionEnabled ? withCodexCatalogProvider(baseCatalogSettings) : baseCatalogSettings;
|
|
66971
|
+
const catalogSettings = baseCatalogSettings.supergrokSubscriptionEnabled ? withXaiSubscriptionCatalogProvider2(codexCatalogSettings) : codexCatalogSettings;
|
|
66379
66972
|
return c.json(
|
|
66380
66973
|
ClientConfig.parse({
|
|
66381
66974
|
deploymentRevision: deps.settings.deploymentRevision,
|
|
@@ -66387,7 +66980,7 @@ function createAppComposition(deps) {
|
|
|
66387
66980
|
// union of the built-in allow-list and every registry provider's models, in
|
|
66388
66981
|
// selection order (default model first); project each to the client-safe
|
|
66389
66982
|
// provider-blind ClientModel shape (execution topology remains server-side).
|
|
66390
|
-
models:
|
|
66983
|
+
models: configuredModels2(catalogSettings).map(projectClientModel),
|
|
66391
66984
|
defaultReasoningEffort: deps.settings.openaiReasoningEffort,
|
|
66392
66985
|
allowedReasoningEfforts: configuredAllowedReasoningEfforts(deps.settings),
|
|
66393
66986
|
defaultSandboxBackend: deps.settings.sandboxBackend,
|
|
@@ -66484,7 +67077,8 @@ function createAppComposition(deps) {
|
|
|
66484
67077
|
const transport = new WebStandardStreamableHTTPServerTransport3({
|
|
66485
67078
|
enableJsonResponse: true
|
|
66486
67079
|
});
|
|
66487
|
-
const
|
|
67080
|
+
const mcpDeps = await resolveWorkspaceMcpRouteDeps(routeDeps, grant);
|
|
67081
|
+
const mcp = buildOpenGeniMcpServer(mcpDeps, grant, {
|
|
66488
67082
|
requestOrigin: new URL(c.req.url).origin,
|
|
66489
67083
|
workspaceMemoryEnabled,
|
|
66490
67084
|
workspaceMemoryPromptMode
|
|
@@ -66882,9 +67476,14 @@ function boundedCorrelationId(value) {
|
|
|
66882
67476
|
function readinessChecks(deps) {
|
|
66883
67477
|
const configuredNatsCheck = deps.readinessChecks?.nats;
|
|
66884
67478
|
return {
|
|
66885
|
-
db:
|
|
66886
|
-
|
|
66887
|
-
|
|
67479
|
+
db: async () => {
|
|
67480
|
+
if (deps.readinessChecks?.db) {
|
|
67481
|
+
await deps.readinessChecks.db();
|
|
67482
|
+
} else {
|
|
67483
|
+
await deps.db.execute(dbSql`select 1`);
|
|
67484
|
+
}
|
|
67485
|
+
await resolveCatalogSettings(deps.db, deps.settings);
|
|
67486
|
+
},
|
|
66888
67487
|
nats: async () => {
|
|
66889
67488
|
requireSessionEventDurableFanoutCapability2(deps.bus);
|
|
66890
67489
|
if (configuredNatsCheck) {
|
|
@@ -67762,6 +68361,7 @@ export {
|
|
|
67762
68361
|
API_MAX_REQUEST_BODY_BYTES,
|
|
67763
68362
|
apiRequestBodyLimitBytes,
|
|
67764
68363
|
createApp,
|
|
68364
|
+
resolveWorkspaceMcpRouteDeps,
|
|
67765
68365
|
createAppComposition,
|
|
67766
68366
|
appendVary,
|
|
67767
68367
|
workspaceActorContextExempt,
|
|
@@ -67780,4 +68380,4 @@ export {
|
|
|
67780
68380
|
withDefaultEnabledCapabilityMcpTools,
|
|
67781
68381
|
workflowIdForSession3 as workflowIdForSession
|
|
67782
68382
|
};
|
|
67783
|
-
//# sourceMappingURL=chunk-
|
|
68383
|
+
//# sourceMappingURL=chunk-UOI7KAP3.js.map
|