@opengeni/core 3.1.3-canary.38890816741001 → 3.3.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/access/index.d.ts +13 -0
- package/dist/application/site-session-cancellation.d.ts +14 -0
- package/dist/dependencies.d.ts +3 -0
- package/dist/domain/insights-usage.d.ts +24 -1
- package/dist/domain/vaults/external-provider-connections.d.ts +126 -0
- package/dist/domain/vaults/index.d.ts +2 -1
- package/dist/domain/vaults/mapping.d.ts +18 -2
- package/dist/domain/vaults/provider-registry.d.ts +4 -1
- package/dist/domain/vaults/provider-sync.d.ts +135 -21
- package/dist/domain/vaults/provider-writes.d.ts +102 -12
- package/dist/domain/vaults/remote-provider-runtime.d.ts +7 -0
- package/dist/domain/vaults/secret-resolver.d.ts +29 -1
- package/dist/goal-admission.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1578 -394
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/src/access/index.ts +100 -0
- package/src/application/site-session-cancellation.ts +47 -0
- package/src/dependencies.ts +3 -0
- package/src/domain/file-owner.ts +1 -0
- package/src/domain/insights-usage.ts +20 -1
- package/src/domain/realtime-voice-billing.ts +4 -1
- package/src/domain/vaults/external-provider-connections.ts +395 -0
- package/src/domain/vaults/index.ts +8 -0
- package/src/domain/vaults/mapping.ts +95 -1
- package/src/domain/vaults/provider-registry.ts +29 -0
- package/src/domain/vaults/provider-sync.ts +492 -136
- package/src/domain/vaults/provider-writes.ts +415 -71
- package/src/domain/vaults/remote-provider-runtime.ts +129 -0
- package/src/domain/vaults/secret-resolver.ts +203 -33
- package/src/domain/vaults/vault-service.ts +226 -34
- package/src/domain/voice-input-billing.ts +5 -1
- package/src/index.ts +3 -0
- package/src/sandbox/fleet.ts +1 -0
- package/src/session-authorization.ts +21 -0
package/dist/index.js
CHANGED
|
@@ -156,6 +156,7 @@ import {
|
|
|
156
156
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
157
157
|
import {
|
|
158
158
|
currentSessionRlsActorIdentityKey,
|
|
159
|
+
currentSiteViewerRlsContext,
|
|
159
160
|
getSessionAuthorityProjection,
|
|
160
161
|
getSessionAuthorizationTargetProjection,
|
|
161
162
|
getSession as getSession2,
|
|
@@ -395,7 +396,8 @@ function relatedSessionAccessForAgentAttempt(actor, targetSessionId) {
|
|
|
395
396
|
function sessionRlsActorForAuthorization(authorization) {
|
|
396
397
|
return authorization.actor.kind === "agent_attempt" ? {
|
|
397
398
|
subjectId: authorization.actor.subjectId,
|
|
398
|
-
initiatingHumanSubjectId: authorization.actor.initiatingHumanSubjectId
|
|
399
|
+
initiatingHumanSubjectId: authorization.actor.initiatingHumanSubjectId,
|
|
400
|
+
agentRootSessionId: authorization.actor.callerRootSessionId
|
|
399
401
|
} : { subjectId: authorization.actor.subjectId };
|
|
400
402
|
}
|
|
401
403
|
async function withResolvedSessionAuthorization(authorization, fn) {
|
|
@@ -426,6 +428,13 @@ async function requireSessionAuthorization(deps, grant, input) {
|
|
|
426
428
|
if (!authority && input.surface !== "core") {
|
|
427
429
|
throw new SessionAuthorizationDeniedError("not_found");
|
|
428
430
|
}
|
|
431
|
+
if (authority?.siteAccessRootId) {
|
|
432
|
+
const viewer = currentSiteViewerRlsContext();
|
|
433
|
+
const resolved = await resolveSessionAuthorizationActor(deps.db, grant, reads);
|
|
434
|
+
const actor2 = resolved.actor;
|
|
435
|
+
const allowed = actor2.kind === "agent_attempt" ? authority.siteOwnerSubjectId !== null && authority.siteOwnerSubjectId === actor2.initiatingHumanSubjectId || actor2.callerRootSessionId === authority.rootSessionId && authority.siteAccessRootId === authority.rootSessionId : authority.siteOwnerSubjectId === actor2.subjectId || actor2.subjectId === `site:${authority.siteId}` && viewer?.siteId === authority.siteId && viewer.principal === authority.siteViewerPrincipal;
|
|
436
|
+
if (!allowed) throw new SessionAuthorizationDeniedError("not_found");
|
|
437
|
+
}
|
|
429
438
|
const siteOrigin = currentSiteSessionOrigin();
|
|
430
439
|
if (siteOrigin && authority && !input.operation.endsWith(".read")) {
|
|
431
440
|
const session = await getSession2(deps.db, grant.workspaceId, input.sessionId);
|
|
@@ -1011,10 +1020,10 @@ import { requireSession as requireSession3, requireWorkspace as requireWorkspace
|
|
|
1011
1020
|
// src/domain/capabilities.ts
|
|
1012
1021
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1013
1022
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1014
|
-
import { isDeepStrictEqual } from "util";
|
|
1023
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
1015
1024
|
import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
|
|
1016
1025
|
import {
|
|
1017
|
-
environmentsEncryptionKeyBytes,
|
|
1026
|
+
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2,
|
|
1018
1027
|
findIntegrationsOauthClient,
|
|
1019
1028
|
parseIntegrationsOauthClientsJson
|
|
1020
1029
|
} from "@opengeni/config";
|
|
@@ -1041,7 +1050,7 @@ import {
|
|
|
1041
1050
|
encryptVariableSetValue,
|
|
1042
1051
|
getCapabilityCatalogItem,
|
|
1043
1052
|
getCapabilityInstallation,
|
|
1044
|
-
getConnectionMetadata as
|
|
1053
|
+
getConnectionMetadata as getConnectionMetadata3,
|
|
1045
1054
|
readCodexCutoverDisposition,
|
|
1046
1055
|
resolveSubscriptionCoreCodexAppsDesignation,
|
|
1047
1056
|
rlsContextForWorkspace,
|
|
@@ -1099,14 +1108,263 @@ function preferredFikenConnection(connections) {
|
|
|
1099
1108
|
|
|
1100
1109
|
// src/domain/vaults/provider-registry.ts
|
|
1101
1110
|
import {
|
|
1111
|
+
getConnectionMetadata as getConnectionMetadata2,
|
|
1112
|
+
getProviderConnectionHomeWorkspace as getProviderConnectionHomeWorkspace2,
|
|
1113
|
+
loadConnectionCredentialForBroker as loadConnectionCredentialForBroker2
|
|
1114
|
+
} from "@opengeni/db";
|
|
1115
|
+
import {
|
|
1116
|
+
createVaultProviderRegistry,
|
|
1117
|
+
VaultProviderError as VaultProviderError2
|
|
1118
|
+
} from "@opengeni/vaults";
|
|
1119
|
+
|
|
1120
|
+
// src/domain/vaults/external-provider-connections.ts
|
|
1121
|
+
import { environmentsEncryptionKeyBytes } from "@opengeni/config";
|
|
1122
|
+
import { isDeepStrictEqual } from "util";
|
|
1123
|
+
import {
|
|
1124
|
+
ExternalVaultProviderKind,
|
|
1125
|
+
VaultProviderCredentials,
|
|
1126
|
+
VaultProviderOperations
|
|
1127
|
+
} from "@opengeni/contracts";
|
|
1128
|
+
import {
|
|
1129
|
+
encryptEnvironmentValue,
|
|
1102
1130
|
getConnectionMetadata,
|
|
1103
1131
|
getProviderConnectionHomeWorkspace,
|
|
1104
|
-
loadConnectionCredentialForBroker
|
|
1132
|
+
loadConnectionCredentialForBroker,
|
|
1133
|
+
updateConnection
|
|
1105
1134
|
} from "@opengeni/db";
|
|
1106
1135
|
import {
|
|
1107
|
-
|
|
1136
|
+
providerOperations,
|
|
1108
1137
|
VaultProviderError
|
|
1109
1138
|
} from "@opengeni/vaults";
|
|
1139
|
+
import { z } from "zod";
|
|
1140
|
+
var EXTERNAL_VAULT_CREDENTIAL_ROLE = "external_vault_provider";
|
|
1141
|
+
var ExternalVaultConnectionDetails = z.object({
|
|
1142
|
+
version: z.literal(1),
|
|
1143
|
+
provider: ExternalVaultProviderKind,
|
|
1144
|
+
ownership: z.enum(["user", "workspace", "organization"]),
|
|
1145
|
+
accountId: z.string().min(1).max(300).nullable(),
|
|
1146
|
+
accountName: z.string().max(300).nullable(),
|
|
1147
|
+
label: z.string().max(120).nullable(),
|
|
1148
|
+
verifiedAt: z.string().datetime(),
|
|
1149
|
+
operations: VaultProviderOperations,
|
|
1150
|
+
verificationStatus: z.enum(["ready", "pending"]).optional()
|
|
1151
|
+
}).strict();
|
|
1152
|
+
function externalVaultProviderDomain(provider) {
|
|
1153
|
+
return `vault:${provider}`;
|
|
1154
|
+
}
|
|
1155
|
+
function externalVaultConnectionDetails(metadata) {
|
|
1156
|
+
if (metadata?.credentialRole !== EXTERNAL_VAULT_CREDENTIAL_ROLE) return null;
|
|
1157
|
+
const result = ExternalVaultConnectionDetails.safeParse(metadata.vaultProvider);
|
|
1158
|
+
return result.success ? result.data : null;
|
|
1159
|
+
}
|
|
1160
|
+
function isExternalVaultConnection(connection) {
|
|
1161
|
+
const details = externalVaultConnectionDetails(connection.metadata);
|
|
1162
|
+
return details !== null && connection.kind === "api_key" && connection.providerDomain === externalVaultProviderDomain(details.provider) && details.ownership === "user" === (connection.subjectId !== null);
|
|
1163
|
+
}
|
|
1164
|
+
var StoredVaultProviderCredentials = z.union([
|
|
1165
|
+
VaultProviderCredentials.options[0],
|
|
1166
|
+
VaultProviderCredentials.options[1],
|
|
1167
|
+
VaultProviderCredentials.options[2],
|
|
1168
|
+
z.object({
|
|
1169
|
+
provider: z.literal("keeper"),
|
|
1170
|
+
configuration: z.record(z.string().max(100), z.string().max(65536)).refine((value) => Object.keys(value).length <= 32)
|
|
1171
|
+
}).strict()
|
|
1172
|
+
]);
|
|
1173
|
+
var PendingVaultCredentialEnvelope = z.object({
|
|
1174
|
+
vaultCredentialEnvelope: z.literal(1),
|
|
1175
|
+
current: StoredVaultProviderCredentials.nullable(),
|
|
1176
|
+
pending: StoredVaultProviderCredentials
|
|
1177
|
+
}).strict();
|
|
1178
|
+
function pendingVaultCredentialBundle(current, pending) {
|
|
1179
|
+
return { vaultCredentialEnvelope: 1, current, pending };
|
|
1180
|
+
}
|
|
1181
|
+
function externalVaultConnectionMetadata(details) {
|
|
1182
|
+
return {
|
|
1183
|
+
credentialRole: EXTERNAL_VAULT_CREDENTIAL_ROLE,
|
|
1184
|
+
vaultProvider: ExternalVaultConnectionDetails.parse(details)
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
function unavailable() {
|
|
1188
|
+
return new VaultProviderError(
|
|
1189
|
+
"provider_auth_invalid",
|
|
1190
|
+
"This vault connection was removed or is not available to you. Reconnect it in Vaults."
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
async function loadExternalVaultConnection(db, settings, input) {
|
|
1194
|
+
let connection = await getConnectionMetadata(
|
|
1195
|
+
db,
|
|
1196
|
+
input.workspaceId,
|
|
1197
|
+
input.connectionId,
|
|
1198
|
+
input.subjectId
|
|
1199
|
+
);
|
|
1200
|
+
if (!connection) {
|
|
1201
|
+
const home = await getProviderConnectionHomeWorkspace(
|
|
1202
|
+
db,
|
|
1203
|
+
input.workspaceId,
|
|
1204
|
+
input.connectionId,
|
|
1205
|
+
input.subjectId
|
|
1206
|
+
);
|
|
1207
|
+
if (home && home !== input.workspaceId)
|
|
1208
|
+
connection = await getConnectionMetadata(db, home, input.connectionId, input.subjectId);
|
|
1209
|
+
}
|
|
1210
|
+
if (!connection || !isExternalVaultConnection(connection)) throw unavailable();
|
|
1211
|
+
const details = externalVaultConnectionDetails(connection.metadata);
|
|
1212
|
+
if (details.provider !== input.provider) throw unavailable();
|
|
1213
|
+
if (connection.status !== "active" && !(input.allowPendingVerification && (connection.status === "needs_reauth" || connection.status === "error")))
|
|
1214
|
+
throw new VaultProviderError(
|
|
1215
|
+
"provider_auth_expired",
|
|
1216
|
+
"This vault connection needs to be reconnected."
|
|
1217
|
+
);
|
|
1218
|
+
if (connection.subjectId !== null && connection.subjectId !== input.subjectId)
|
|
1219
|
+
throw unavailable();
|
|
1220
|
+
const loaded = await loadConnectionCredentialForBroker(db, settings, {
|
|
1221
|
+
workspaceId: connection.workspaceId,
|
|
1222
|
+
connectionId: connection.id,
|
|
1223
|
+
providerDomain: externalVaultProviderDomain(input.provider),
|
|
1224
|
+
kind: "api_key",
|
|
1225
|
+
...connection.subjectId !== null ? { allowSubjectOwned: true, subjectId: input.subjectId } : {}
|
|
1226
|
+
});
|
|
1227
|
+
if (!loaded || loaded.status !== connection.status || loaded.version !== connection.version || loaded.accountId !== connection.accountId || loaded.subjectId !== connection.subjectId || loaded.providerDomain !== connection.providerDomain || loaded.kind !== "api_key" || externalVaultConnectionDetails(loaded.metadata)?.provider !== input.provider)
|
|
1228
|
+
throw unavailable();
|
|
1229
|
+
const envelope = PendingVaultCredentialEnvelope.safeParse(loaded.credential);
|
|
1230
|
+
const raw = envelope.success ? input.allowPendingVerification ? envelope.data.pending : envelope.data.current : loaded.credential;
|
|
1231
|
+
const parsed = StoredVaultProviderCredentials.safeParse(raw);
|
|
1232
|
+
if (!parsed.success || parsed.data.provider !== input.provider) throw unavailable();
|
|
1233
|
+
if (envelope.success && (envelope.data.pending.provider !== input.provider || envelope.data.current !== null && envelope.data.current.provider !== input.provider))
|
|
1234
|
+
throw unavailable();
|
|
1235
|
+
return {
|
|
1236
|
+
connection,
|
|
1237
|
+
details,
|
|
1238
|
+
credentials: parsed.data,
|
|
1239
|
+
currentCredentials: envelope.success ? envelope.data.current : parsed.data,
|
|
1240
|
+
pendingCredentials: envelope.success ? envelope.data.pending : null
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
function bindVaultProviderAuthority(provider, input) {
|
|
1244
|
+
async function context(ctx) {
|
|
1245
|
+
ctx.signal?.throwIfAborted();
|
|
1246
|
+
await input.revalidate();
|
|
1247
|
+
ctx.signal?.throwIfAborted();
|
|
1248
|
+
return { ...ctx, connectionId: input.connectionId, authority: { ...input.authority } };
|
|
1249
|
+
}
|
|
1250
|
+
return {
|
|
1251
|
+
id: provider.id,
|
|
1252
|
+
capabilities: () => ({
|
|
1253
|
+
...provider.capabilities(),
|
|
1254
|
+
operations: providerOperations(provider.capabilities(), { operations: input.operations })
|
|
1255
|
+
}),
|
|
1256
|
+
validate: async (ctx) => provider.validate(await context(ctx)),
|
|
1257
|
+
listVaults: async (ctx) => provider.listVaults(await context(ctx)),
|
|
1258
|
+
listItems: async (ctx, vaultId) => provider.listItems(await context(ctx), vaultId),
|
|
1259
|
+
readFields: async (ctx, vaultId, itemId, fields) => provider.readFields(await context(ctx), vaultId, itemId, fields),
|
|
1260
|
+
writeItem: async (ctx, vaultId, item) => provider.writeItem(await context(ctx), vaultId, item),
|
|
1261
|
+
archiveItem: async (ctx, vaultId, itemId) => provider.archiveItem(await context(ctx), vaultId, itemId),
|
|
1262
|
+
...provider.listVaultPage ? { listVaultPage: async (ctx, page) => provider.listVaultPage(await context(ctx), page) } : {},
|
|
1263
|
+
...provider.listItemPage ? {
|
|
1264
|
+
listItemPage: async (ctx, vaultId, page) => provider.listItemPage(await context(ctx), vaultId, page)
|
|
1265
|
+
} : {},
|
|
1266
|
+
...provider.createVault ? { createVault: async (ctx, value) => provider.createVault(await context(ctx), value) } : {}
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
async function externalVaultProviderForConnection(input) {
|
|
1270
|
+
const loaded = await loadExternalVaultConnection(input.db, input.settings, input);
|
|
1271
|
+
let connection = loaded.connection;
|
|
1272
|
+
const prepared = await input.factory({
|
|
1273
|
+
credentials: loaded.credentials,
|
|
1274
|
+
connectionId: connection.id,
|
|
1275
|
+
signal: AbortSignal.timeout(3e4),
|
|
1276
|
+
operations: loaded.details.operations,
|
|
1277
|
+
persistCredentials: async (next) => {
|
|
1278
|
+
const parsed = StoredVaultProviderCredentials.safeParse(next);
|
|
1279
|
+
if (!parsed.success || parsed.data.provider !== loaded.details.provider) throw unavailable();
|
|
1280
|
+
const credentials = parsed.data;
|
|
1281
|
+
const key = environmentsEncryptionKeyBytes(input.settings);
|
|
1282
|
+
if (!key)
|
|
1283
|
+
throw new VaultProviderError(
|
|
1284
|
+
"provider_unavailable",
|
|
1285
|
+
"Vault credential encryption is unavailable."
|
|
1286
|
+
);
|
|
1287
|
+
const updated = await updateConnection(input.db, {
|
|
1288
|
+
workspaceId: connection.workspaceId,
|
|
1289
|
+
connectionId: connection.id,
|
|
1290
|
+
visibleToSubjectId: input.subjectId,
|
|
1291
|
+
expectedVersion: connection.version,
|
|
1292
|
+
credentialEncrypted: encryptEnvironmentValue(
|
|
1293
|
+
key,
|
|
1294
|
+
JSON.stringify(
|
|
1295
|
+
loaded.pendingCredentials ? pendingVaultCredentialBundle(credentials, loaded.pendingCredentials) : credentials
|
|
1296
|
+
)
|
|
1297
|
+
)
|
|
1298
|
+
});
|
|
1299
|
+
if (updated) {
|
|
1300
|
+
connection = updated;
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
const live = await loadExternalVaultConnection(input.db, input.settings, input);
|
|
1304
|
+
if (isDeepStrictEqual(
|
|
1305
|
+
rotationConnectionIdentity(live.connection),
|
|
1306
|
+
rotationConnectionIdentity(connection)
|
|
1307
|
+
) && isDeepStrictEqual(live.currentCredentials, credentials) && isDeepStrictEqual(live.pendingCredentials, loaded.pendingCredentials)) {
|
|
1308
|
+
connection = live.connection;
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
throw new VaultProviderError(
|
|
1312
|
+
"provider_conflict",
|
|
1313
|
+
"This vault connection changed while its credentials were renewed."
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
if (prepared.provider.id !== input.provider) throw unavailable();
|
|
1318
|
+
return bindVaultProviderAuthority(prepared.provider, {
|
|
1319
|
+
connectionId: connection.id,
|
|
1320
|
+
authority: {
|
|
1321
|
+
accountId: connection.accountId,
|
|
1322
|
+
workspaceId: input.workspaceId,
|
|
1323
|
+
connectionWorkspaceId: connection.workspaceId,
|
|
1324
|
+
subjectId: input.subjectId,
|
|
1325
|
+
...input.executionContext
|
|
1326
|
+
},
|
|
1327
|
+
operations: loaded.details.operations,
|
|
1328
|
+
revalidate: async () => {
|
|
1329
|
+
if (connection.workspaceId !== input.workspaceId) {
|
|
1330
|
+
const home = await getProviderConnectionHomeWorkspace(
|
|
1331
|
+
input.db,
|
|
1332
|
+
input.workspaceId,
|
|
1333
|
+
connection.id,
|
|
1334
|
+
input.subjectId
|
|
1335
|
+
);
|
|
1336
|
+
if (home !== connection.workspaceId) throw unavailable();
|
|
1337
|
+
}
|
|
1338
|
+
const live = await getConnectionMetadata(
|
|
1339
|
+
input.db,
|
|
1340
|
+
connection.workspaceId,
|
|
1341
|
+
connection.id,
|
|
1342
|
+
input.subjectId
|
|
1343
|
+
);
|
|
1344
|
+
if (!live || !isExternalVaultConnection(live) || live.status !== "active" || live.accountId !== connection.accountId || live.subjectId !== connection.subjectId || live.providerDomain !== connection.providerDomain)
|
|
1345
|
+
throw unavailable();
|
|
1346
|
+
if (live.version !== connection.version)
|
|
1347
|
+
throw new VaultProviderError(
|
|
1348
|
+
"provider_conflict",
|
|
1349
|
+
"This vault connection changed. Reload it before starting another operation."
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
function rotationConnectionIdentity(connection) {
|
|
1355
|
+
const {
|
|
1356
|
+
version: _version,
|
|
1357
|
+
connectionAuthorityGeneration: _authorityGeneration,
|
|
1358
|
+
updatedAt: _updatedAt,
|
|
1359
|
+
updatedBySubjectId: _updatedBy,
|
|
1360
|
+
lastUsedAt: _lastUsedAt,
|
|
1361
|
+
lastRefreshAt: _lastRefreshAt,
|
|
1362
|
+
...identity
|
|
1363
|
+
} = connection;
|
|
1364
|
+
return identity;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/domain/vaults/provider-registry.ts
|
|
1110
1368
|
function vaultErrorCodeForProvider(code, operation) {
|
|
1111
1369
|
if (code === "provider_not_found") {
|
|
1112
1370
|
return operation === "read" ? "vault_field_not_found" : "vault_not_found";
|
|
@@ -1143,19 +1401,19 @@ function onePasswordCredentialBundle(token) {
|
|
|
1143
1401
|
return { token };
|
|
1144
1402
|
}
|
|
1145
1403
|
async function loadOnePasswordConnectionToken(db, settings, input) {
|
|
1146
|
-
const
|
|
1404
|
+
const unavailable2 = () => new VaultProviderError2(
|
|
1147
1405
|
"provider_auth_invalid",
|
|
1148
1406
|
"The 1Password connection for this vault was removed or is not available to you; reconnect 1Password"
|
|
1149
1407
|
);
|
|
1150
1408
|
let homeWorkspaceId = input.workspaceId;
|
|
1151
|
-
let connection = await
|
|
1409
|
+
let connection = await getConnectionMetadata2(
|
|
1152
1410
|
db,
|
|
1153
1411
|
input.workspaceId,
|
|
1154
1412
|
input.connectionId,
|
|
1155
1413
|
input.subjectId
|
|
1156
1414
|
);
|
|
1157
1415
|
if (!connection) {
|
|
1158
|
-
const home = await
|
|
1416
|
+
const home = await getProviderConnectionHomeWorkspace2(
|
|
1159
1417
|
db,
|
|
1160
1418
|
input.workspaceId,
|
|
1161
1419
|
input.connectionId,
|
|
@@ -1163,18 +1421,18 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
|
|
|
1163
1421
|
);
|
|
1164
1422
|
if (home && home !== input.workspaceId) {
|
|
1165
1423
|
homeWorkspaceId = home;
|
|
1166
|
-
connection = await
|
|
1424
|
+
connection = await getConnectionMetadata2(db, home, input.connectionId, input.subjectId);
|
|
1167
1425
|
}
|
|
1168
1426
|
}
|
|
1169
|
-
if (!connection || !isOnePasswordConnection(connection)) throw
|
|
1427
|
+
if (!connection || !isOnePasswordConnection(connection)) throw unavailable2();
|
|
1170
1428
|
if (connection.status !== "active") {
|
|
1171
|
-
throw new
|
|
1429
|
+
throw new VaultProviderError2(
|
|
1172
1430
|
"provider_auth_expired",
|
|
1173
1431
|
"The 1Password connection needs a new service account token"
|
|
1174
1432
|
);
|
|
1175
1433
|
}
|
|
1176
1434
|
const personal = connection.subjectId !== null;
|
|
1177
|
-
const credential = await
|
|
1435
|
+
const credential = await loadConnectionCredentialForBroker2(db, settings, {
|
|
1178
1436
|
workspaceId: homeWorkspaceId,
|
|
1179
1437
|
connectionId: connection.id,
|
|
1180
1438
|
providerDomain: ONEPASSWORD_PROVIDER_DOMAIN,
|
|
@@ -1182,7 +1440,7 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
|
|
|
1182
1440
|
...personal ? { allowSubjectOwned: true, subjectId: connection.subjectId } : {}
|
|
1183
1441
|
});
|
|
1184
1442
|
const token = credential?.credential?.token;
|
|
1185
|
-
if (!credential || typeof token !== "string" || !token) throw
|
|
1443
|
+
if (!credential || typeof token !== "string" || !token) throw unavailable2();
|
|
1186
1444
|
return { token, metadata: onePasswordConnectionMetadata(connection.metadata) };
|
|
1187
1445
|
}
|
|
1188
1446
|
function createWorkspaceVaultProviderRegistry(input) {
|
|
@@ -1192,7 +1450,7 @@ function createWorkspaceVaultProviderRegistry(input) {
|
|
|
1192
1450
|
...factory ? {
|
|
1193
1451
|
async onepassword(connectionId) {
|
|
1194
1452
|
if (!connectionId) {
|
|
1195
|
-
throw new
|
|
1453
|
+
throw new VaultProviderError2(
|
|
1196
1454
|
"provider_unavailable",
|
|
1197
1455
|
"This 1Password vault has no connection"
|
|
1198
1456
|
);
|
|
@@ -1209,7 +1467,29 @@ function createWorkspaceVaultProviderRegistry(input) {
|
|
|
1209
1467
|
});
|
|
1210
1468
|
}
|
|
1211
1469
|
} : {},
|
|
1212
|
-
...host ? { host: async () => host } : {}
|
|
1470
|
+
...host ? { host: async () => host } : {},
|
|
1471
|
+
...Object.fromEntries(
|
|
1472
|
+
Object.entries(input.externalVaultProviders ?? {}).map(([provider, providerFactory]) => [
|
|
1473
|
+
provider,
|
|
1474
|
+
async (connectionId) => {
|
|
1475
|
+
if (!connectionId || !providerFactory)
|
|
1476
|
+
throw new VaultProviderError2(
|
|
1477
|
+
"provider_unavailable",
|
|
1478
|
+
"This vault has no available connection."
|
|
1479
|
+
);
|
|
1480
|
+
return externalVaultProviderForConnection({
|
|
1481
|
+
db: input.db,
|
|
1482
|
+
settings: input.settings,
|
|
1483
|
+
workspaceId: input.workspaceId,
|
|
1484
|
+
subjectId: input.subjectId,
|
|
1485
|
+
connectionId,
|
|
1486
|
+
provider,
|
|
1487
|
+
factory: providerFactory,
|
|
1488
|
+
...input.executionContext ? { executionContext: input.executionContext } : {}
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
])
|
|
1492
|
+
)
|
|
1213
1493
|
});
|
|
1214
1494
|
}
|
|
1215
1495
|
|
|
@@ -1440,7 +1720,7 @@ async function prepareCapabilityEnable(input) {
|
|
|
1440
1720
|
item,
|
|
1441
1721
|
installationConfig
|
|
1442
1722
|
);
|
|
1443
|
-
if (!current || !
|
|
1723
|
+
if (!current || !isDeepStrictEqual2(current, unchanged)) {
|
|
1444
1724
|
throw new HTTPException5(409, {
|
|
1445
1725
|
message: "Capability installation changed; reload before reconciling"
|
|
1446
1726
|
});
|
|
@@ -1494,7 +1774,7 @@ async function validateVerifiedMcpConnection(input, item) {
|
|
|
1494
1774
|
const ref = input.payload.connectionRef;
|
|
1495
1775
|
if (!proof || !ref || item.endpointUrl !== proof.endpointUrl || ref.resource !== proof.endpointUrl)
|
|
1496
1776
|
throw new HTTPException5(409, { message: "MCP verification no longer matches the connection" });
|
|
1497
|
-
const connection = await
|
|
1777
|
+
const connection = await getConnectionMetadata3(
|
|
1498
1778
|
input.db,
|
|
1499
1779
|
input.workspaceId,
|
|
1500
1780
|
proof.id,
|
|
@@ -1521,7 +1801,7 @@ async function unchangedMcpInstallation(input, item, requestedConfig) {
|
|
|
1521
1801
|
const config = { ...requestedConfig, ...connectionRef ? { connectionRef } : {} };
|
|
1522
1802
|
const { mcpConnectivity: _storedConnectivity, ...storedMetadata } = existing.metadata;
|
|
1523
1803
|
const { mcpConnectivity: _requestedConnectivity, ...requestedMetadata } = input.payload.metadata;
|
|
1524
|
-
if (!
|
|
1804
|
+
if (!isDeepStrictEqual2(config, storedConfig) || !isDeepStrictEqual2(requestedMetadata, storedMetadata))
|
|
1525
1805
|
return null;
|
|
1526
1806
|
const provided = normalizedMcpCredentialHeaders(input.payload.headers);
|
|
1527
1807
|
if (provided) {
|
|
@@ -1529,7 +1809,7 @@ async function unchangedMcpInstallation(input, item, requestedConfig) {
|
|
|
1529
1809
|
{ ...input, payload: { ...input.payload, headers: {} } },
|
|
1530
1810
|
item
|
|
1531
1811
|
);
|
|
1532
|
-
if (!
|
|
1812
|
+
if (!isDeepStrictEqual2(provided, stored2)) return null;
|
|
1533
1813
|
}
|
|
1534
1814
|
return existing;
|
|
1535
1815
|
}
|
|
@@ -1630,7 +1910,7 @@ async function validateMcpCapabilityConnectionRef(input, item, ref) {
|
|
|
1630
1910
|
assertNativeMcpConnectionRef(normalized);
|
|
1631
1911
|
return normalized;
|
|
1632
1912
|
}
|
|
1633
|
-
let connection = normalized.connectionId ? await
|
|
1913
|
+
let connection = normalized.connectionId ? await getConnectionMetadata3(
|
|
1634
1914
|
input.db,
|
|
1635
1915
|
input.workspaceId,
|
|
1636
1916
|
normalized.connectionId,
|
|
@@ -1712,7 +1992,7 @@ function requiredCapabilityHeaders(metadata) {
|
|
|
1712
1992
|
return value.filter((name) => typeof name === "string" && name.trim().length > 0).map((name) => name.trim());
|
|
1713
1993
|
}
|
|
1714
1994
|
function requireCapabilityHeaderEncryption(settings) {
|
|
1715
|
-
const key =
|
|
1995
|
+
const key = environmentsEncryptionKeyBytes2(settings);
|
|
1716
1996
|
if (!key) {
|
|
1717
1997
|
throw new HTTPException5(503, {
|
|
1718
1998
|
message: "MCP credential headers require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"
|
|
@@ -2001,7 +2281,7 @@ function settingsWithMcpCapabilityServers(settings, enabled) {
|
|
|
2001
2281
|
if (enabled.length === 0) {
|
|
2002
2282
|
return settings;
|
|
2003
2283
|
}
|
|
2004
|
-
const encryptionKey =
|
|
2284
|
+
const encryptionKey = environmentsEncryptionKeyBytes2(settings);
|
|
2005
2285
|
const existingIds = new Set(settings.mcpServers.map((server) => server.id));
|
|
2006
2286
|
const dynamicServers = enabled.filter((server) => !existingIds.has(server.id)).flatMap((server) => {
|
|
2007
2287
|
const headers = decryptedCapabilityHeaders(server, encryptionKey);
|
|
@@ -3040,7 +3320,7 @@ import {
|
|
|
3040
3320
|
getSessionTurnPersonalConnectionDelegations,
|
|
3041
3321
|
getSessionTurnMcpAccountBindings,
|
|
3042
3322
|
getSessionTurnConnectionAccountIntent,
|
|
3043
|
-
getConnectionMetadata as
|
|
3323
|
+
getConnectionMetadata as getConnectionMetadata4,
|
|
3044
3324
|
getSocialConnection,
|
|
3045
3325
|
listConnectionsMetadata as listConnectionsMetadata2,
|
|
3046
3326
|
listSocialConnections as listSocialConnections2,
|
|
@@ -3478,7 +3758,7 @@ async function listOwnConnectionMetadata(db, input) {
|
|
|
3478
3758
|
);
|
|
3479
3759
|
const connections = [];
|
|
3480
3760
|
for (const account of accounts) {
|
|
3481
|
-
const connection = await
|
|
3761
|
+
const connection = await getConnectionMetadata4(
|
|
3482
3762
|
db,
|
|
3483
3763
|
account.originWorkspaceId,
|
|
3484
3764
|
account.connectionId,
|
|
@@ -3941,7 +4221,7 @@ async function visibleMcpAccountConnections(db, input) {
|
|
|
3941
4221
|
async function visibleMcpAccountConnectionsByIds(db, input) {
|
|
3942
4222
|
const workspace = [];
|
|
3943
4223
|
for (const connectionId of new Set(input.workspaceConnectionIds)) {
|
|
3944
|
-
const connection = await
|
|
4224
|
+
const connection = await getConnectionMetadata4(db, input.workspaceId, connectionId, null);
|
|
3945
4225
|
if (connection && connection.subjectId === null) workspace.push(connection);
|
|
3946
4226
|
}
|
|
3947
4227
|
const personalIds = new Set(input.personalConnectionIds);
|
|
@@ -4159,11 +4439,12 @@ import {
|
|
|
4159
4439
|
listWorkspacesForSubject,
|
|
4160
4440
|
findActiveApiKeyByHash,
|
|
4161
4441
|
getWorkspaceGrant,
|
|
4442
|
+
subjectHasLiveWorkspaceAuthorityInScope,
|
|
4162
4443
|
requireWorkspace,
|
|
4163
4444
|
resolveNamedManagedPersonalWorkspaceGrant
|
|
4164
4445
|
} from "@opengeni/db";
|
|
4165
4446
|
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
4166
|
-
import { z } from "zod";
|
|
4447
|
+
import { z as z2 } from "zod";
|
|
4167
4448
|
|
|
4168
4449
|
// src/access/service-initiator.ts
|
|
4169
4450
|
import { ServiceTurnInitiatorContext } from "@opengeni/contracts";
|
|
@@ -4273,19 +4554,20 @@ function requireExplicitPermissionDelegation(grant, requested) {
|
|
|
4273
4554
|
}
|
|
4274
4555
|
}
|
|
4275
4556
|
var canonicalLocalHumanContexts = /* @__PURE__ */ new WeakSet();
|
|
4276
|
-
var delegatedHumanAuthorizationSchema =
|
|
4277
|
-
organizationId:
|
|
4278
|
-
subjectId:
|
|
4279
|
-
permissions:
|
|
4557
|
+
var delegatedHumanAuthorizationSchema = z2.object({
|
|
4558
|
+
organizationId: z2.string().uuid(),
|
|
4559
|
+
subjectId: z2.string().max(1024).regex(/^user:[^\s\u0000-\u001f\u007f]+$/),
|
|
4560
|
+
permissions: z2.array(Permission),
|
|
4280
4561
|
workspaceScope: OrganizationWorkspaceScope
|
|
4281
4562
|
});
|
|
4282
4563
|
var delegatedHumanRequests = /* @__PURE__ */ new WeakMap();
|
|
4283
4564
|
var delegatedHumanContexts = /* @__PURE__ */ new WeakMap();
|
|
4284
|
-
var
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4565
|
+
var verifiedSiteViewerProofs = /* @__PURE__ */ new WeakSet();
|
|
4566
|
+
var delegatedHumanProfileSchema = z2.object({
|
|
4567
|
+
id: z2.string(),
|
|
4568
|
+
name: z2.string().nullable(),
|
|
4569
|
+
email: z2.string(),
|
|
4570
|
+
emailVerified: z2.boolean().optional()
|
|
4289
4571
|
});
|
|
4290
4572
|
var delegatedHumanProfiles = /* @__PURE__ */ new WeakMap();
|
|
4291
4573
|
var verifiedDelegatedHumanAuthorizations = /* @__PURE__ */ new WeakMap();
|
|
@@ -4310,6 +4592,27 @@ function stampDelegatedHumanAuthorization(request, input) {
|
|
|
4310
4592
|
Object.freeze(proof.permissions);
|
|
4311
4593
|
delegatedHumanRequests.set(request, Object.freeze(proof));
|
|
4312
4594
|
}
|
|
4595
|
+
function stampSiteViewerSessionAuthorization(request, input) {
|
|
4596
|
+
if (input.subjectId.startsWith("user:")) {
|
|
4597
|
+
stampDelegatedHumanAuthorization(request, {
|
|
4598
|
+
...input,
|
|
4599
|
+
workspaceScope: { kind: "selected", workspaceIds: [input.workspaceId] }
|
|
4600
|
+
});
|
|
4601
|
+
return;
|
|
4602
|
+
}
|
|
4603
|
+
if (delegatedHumanRequests.has(request) || accessResolvedRequests.has(request))
|
|
4604
|
+
throw new HTTPException8(403, { message: "request access authorization already resolved" });
|
|
4605
|
+
const parsed = delegatedHumanAuthorizationSchema.extend({
|
|
4606
|
+
subjectId: z2.string().max(1024).regex(/^external_user:[^\s\u0000-\u001f\u007f]+$/)
|
|
4607
|
+
}).parse({ ...input, workspaceScope: { kind: "selected", workspaceIds: [input.workspaceId] } });
|
|
4608
|
+
if (parsed.workspaceScope.kind !== "selected") throw new Error("Site workspace scope required");
|
|
4609
|
+
Object.freeze(parsed.permissions);
|
|
4610
|
+
Object.freeze(parsed.workspaceScope.workspaceIds);
|
|
4611
|
+
Object.freeze(parsed.workspaceScope);
|
|
4612
|
+
const proof = Object.freeze(parsed);
|
|
4613
|
+
verifiedSiteViewerProofs.add(proof);
|
|
4614
|
+
delegatedHumanRequests.set(request, proof);
|
|
4615
|
+
}
|
|
4313
4616
|
var PERSONAL_API_KEY_TOKEN_PREFIX = "ogp_";
|
|
4314
4617
|
async function stampPersonalApiKeyRequest(request, deps) {
|
|
4315
4618
|
const authorization = request.headers.get("authorization");
|
|
@@ -4991,6 +5294,55 @@ async function resolveDelegatedHumanAccessContext(deps, proof) {
|
|
|
4991
5294
|
message: "delegated humans require native organization access"
|
|
4992
5295
|
});
|
|
4993
5296
|
}
|
|
5297
|
+
if (verifiedSiteViewerProofs.has(proof)) {
|
|
5298
|
+
const workspaceId = proof.workspaceScope.kind === "selected" && proof.workspaceScope.workspaceIds.length === 1 ? proof.workspaceScope.workspaceIds[0] : null;
|
|
5299
|
+
if (!workspaceId)
|
|
5300
|
+
throw new HTTPException8(403, { message: "Site viewer workspace unavailable" });
|
|
5301
|
+
const grant = await withWorkspaceSubjectRls(
|
|
5302
|
+
deps.db,
|
|
5303
|
+
workspaceId,
|
|
5304
|
+
proof.subjectId,
|
|
5305
|
+
async (tx) => {
|
|
5306
|
+
if (!await subjectHasLiveWorkspaceAuthorityInScope(tx, {
|
|
5307
|
+
accountId: proof.organizationId,
|
|
5308
|
+
workspaceId,
|
|
5309
|
+
subjectId: proof.subjectId
|
|
5310
|
+
}))
|
|
5311
|
+
return null;
|
|
5312
|
+
return getWorkspaceGrant(tx, proof.subjectId, workspaceId, {
|
|
5313
|
+
principalKind: "human_session"
|
|
5314
|
+
});
|
|
5315
|
+
}
|
|
5316
|
+
);
|
|
5317
|
+
if (!grant || grant.accountId !== proof.organizationId)
|
|
5318
|
+
throw new HTTPException8(403, { message: "Site viewer membership unavailable" });
|
|
5319
|
+
const bounded = Object.freeze({
|
|
5320
|
+
...grant,
|
|
5321
|
+
permissionMode: "explicit",
|
|
5322
|
+
permissions: Object.freeze(
|
|
5323
|
+
explicitPermissions(
|
|
5324
|
+
Permission.options.filter(
|
|
5325
|
+
(permission) => hasPermission(grant.permissions, permission) && hasPermission(proof.permissions, permission, "explicit")
|
|
5326
|
+
)
|
|
5327
|
+
)
|
|
5328
|
+
)
|
|
5329
|
+
});
|
|
5330
|
+
const accountGrant2 = Object.freeze({
|
|
5331
|
+
accountId: proof.organizationId,
|
|
5332
|
+
subjectId: proof.subjectId,
|
|
5333
|
+
permissions: Object.freeze([])
|
|
5334
|
+
});
|
|
5335
|
+
const context2 = Object.freeze({
|
|
5336
|
+
mode: "managed",
|
|
5337
|
+
subjectId: proof.subjectId,
|
|
5338
|
+
accountGrants: Object.freeze([accountGrant2]),
|
|
5339
|
+
workspaceGrants: Object.freeze([bounded]),
|
|
5340
|
+
defaultAccountId: proof.organizationId,
|
|
5341
|
+
defaultWorkspaceId: workspaceId
|
|
5342
|
+
});
|
|
5343
|
+
delegatedHumanContexts.set(context2, proof);
|
|
5344
|
+
return context2;
|
|
5345
|
+
}
|
|
4994
5346
|
const userId = proof.subjectId.slice("user:".length);
|
|
4995
5347
|
const profiles = await getManagedUserProfilesByIds(deps.db, [userId]);
|
|
4996
5348
|
const parsedProfile = delegatedHumanProfileSchema.safeParse(
|
|
@@ -5525,6 +5877,7 @@ async function fileOwnerContextForAgent(deps, grant, permission) {
|
|
|
5525
5877
|
},
|
|
5526
5878
|
subjectId: grant.subjectId,
|
|
5527
5879
|
initiatingHumanSubjectId: attempt.initiatingHumanSubjectId,
|
|
5880
|
+
agentRootSessionId: attempt.callerRootSessionId,
|
|
5528
5881
|
privateFileOwnerSubjectId: snapshot.defaultScope === "personal" ? snapshot.subjectId : null
|
|
5529
5882
|
};
|
|
5530
5883
|
}
|
|
@@ -6494,7 +6847,7 @@ function sessionUsageLimitRefusalFromError(error) {
|
|
|
6494
6847
|
}
|
|
6495
6848
|
|
|
6496
6849
|
// src/sandbox/fleet.ts
|
|
6497
|
-
import { environmentsEncryptionKeyBytes as
|
|
6850
|
+
import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3 } from "@opengeni/config";
|
|
6498
6851
|
import {
|
|
6499
6852
|
agentCapabilityEnabled
|
|
6500
6853
|
} from "@opengeni/contracts";
|
|
@@ -7026,6 +7379,7 @@ import {
|
|
|
7026
7379
|
isValidTotpInput,
|
|
7027
7380
|
parseTotpInput,
|
|
7028
7381
|
referenceKey,
|
|
7382
|
+
secretNameKey,
|
|
7029
7383
|
SecretReferenceError,
|
|
7030
7384
|
VAULT_LIMITS as VAULT_LIMITS2
|
|
7031
7385
|
} from "@opengeni/contracts";
|
|
@@ -7049,14 +7403,42 @@ import {
|
|
|
7049
7403
|
} from "@opengeni/contracts";
|
|
7050
7404
|
import {
|
|
7051
7405
|
getProviderVaultSyncTarget,
|
|
7052
|
-
|
|
7053
|
-
|
|
7406
|
+
isVaultError,
|
|
7407
|
+
setVaultSyncStatus
|
|
7054
7408
|
} from "@opengeni/db";
|
|
7055
7409
|
import {
|
|
7056
7410
|
isVaultProviderError,
|
|
7057
|
-
|
|
7411
|
+
providerOperations as providerOperations2,
|
|
7412
|
+
readProviderItemInventory,
|
|
7413
|
+
readProviderVaultInventory,
|
|
7414
|
+
VaultProviderError as VaultProviderError3
|
|
7058
7415
|
} from "@opengeni/vaults";
|
|
7059
7416
|
var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
|
|
7417
|
+
function incompleteInventory() {
|
|
7418
|
+
return new VaultProviderError3(
|
|
7419
|
+
"provider_inventory_incomplete",
|
|
7420
|
+
`This vault has more than ${VAULT_LIMITS.itemsPerVault} items, more than Opengeni mirrors. The previous inventory was kept.`
|
|
7421
|
+
);
|
|
7422
|
+
}
|
|
7423
|
+
function oversizedItem() {
|
|
7424
|
+
return new VaultProviderError3(
|
|
7425
|
+
"provider_inventory_incomplete",
|
|
7426
|
+
`An item in this vault has more than ${VAULT_LIMITS.fieldsPerItem} fields or ${VAULT_LIMITS.websitesPerItem} websites (or a website over ${VAULT_LIMITS.websiteMaxChars} characters), more than Opengeni mirrors. The previous inventory was kept.`
|
|
7427
|
+
);
|
|
7428
|
+
}
|
|
7429
|
+
function unprovenInventory(listing) {
|
|
7430
|
+
return new VaultProviderError3(
|
|
7431
|
+
"provider_inventory_incomplete",
|
|
7432
|
+
listing === "items" ? "The provider listed this vault in several pages without a snapshot, so Opengeni cannot tell which items were removed. The previous inventory was kept." : "The provider listed its vaults in several pages without a snapshot and this vault was not among them, so Opengeni cannot confirm it was removed."
|
|
7433
|
+
);
|
|
7434
|
+
}
|
|
7435
|
+
function assertMirrorable(item) {
|
|
7436
|
+
if (!Array.isArray(item.fields) || !Array.isArray(item.websites) || item.fields.length > VAULT_LIMITS.fieldsPerItem || item.websites.length > VAULT_LIMITS.websitesPerItem || item.websites.some(
|
|
7437
|
+
(website) => typeof website !== "string" || website.length > VAULT_LIMITS.websiteMaxChars
|
|
7438
|
+
)) {
|
|
7439
|
+
throw oversizedItem();
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7060
7442
|
var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
|
|
7061
7443
|
function nameKey(name) {
|
|
7062
7444
|
return name.normalize("NFC").trim().toLowerCase();
|
|
@@ -7103,15 +7485,16 @@ function providerPrimaryFieldId(item) {
|
|
|
7103
7485
|
return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
|
|
7104
7486
|
}
|
|
7105
7487
|
function planProviderMirror(items, contentVersion) {
|
|
7488
|
+
if (items.length > VAULT_LIMITS.itemsPerVault) throw incompleteInventory();
|
|
7489
|
+
for (const item of items) assertMirrorable(item);
|
|
7106
7490
|
const ordered = [...items].sort(
|
|
7107
7491
|
(a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
|
|
7108
7492
|
);
|
|
7109
|
-
const kept = ordered.slice(0, VAULT_LIMITS.itemsPerVault);
|
|
7110
7493
|
const takenItems = /* @__PURE__ */ new Set();
|
|
7111
|
-
const mirrored =
|
|
7494
|
+
const mirrored = ordered.map((item) => {
|
|
7112
7495
|
const name = uniqueProviderName(providerSecretName(item.name), takenItems);
|
|
7113
7496
|
const takenFields = /* @__PURE__ */ new Set();
|
|
7114
|
-
const fields = item.fields.
|
|
7497
|
+
const fields = item.fields.map((field, position) => {
|
|
7115
7498
|
let fieldName = providerSecretName(
|
|
7116
7499
|
field.name,
|
|
7117
7500
|
field.kind === "totp" ? "one-time password" : "field"
|
|
@@ -7129,7 +7512,7 @@ function planProviderMirror(items, contentVersion) {
|
|
|
7129
7512
|
name,
|
|
7130
7513
|
providerName: item.name,
|
|
7131
7514
|
type: item.type,
|
|
7132
|
-
websites: item.websites
|
|
7515
|
+
websites: [...item.websites],
|
|
7133
7516
|
fields,
|
|
7134
7517
|
primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
|
|
7135
7518
|
hasTotp: item.fields.some((field) => field.kind === "totp"),
|
|
@@ -7137,7 +7520,32 @@ function planProviderMirror(items, contentVersion) {
|
|
|
7137
7520
|
updatedAt: item.updatedAt
|
|
7138
7521
|
};
|
|
7139
7522
|
});
|
|
7140
|
-
return { contentVersion, items: mirrored, truncated:
|
|
7523
|
+
return { contentVersion, items: mirrored, truncated: 0 };
|
|
7524
|
+
}
|
|
7525
|
+
function validTime(value) {
|
|
7526
|
+
if (typeof value !== "string" || value.length > 64) return null;
|
|
7527
|
+
const time = Date.parse(value);
|
|
7528
|
+
return Number.isFinite(time) ? time : null;
|
|
7529
|
+
}
|
|
7530
|
+
function aggregateProviderFreshness(observations, checkedAt) {
|
|
7531
|
+
if (observations.length === 0) return null;
|
|
7532
|
+
let cache = false;
|
|
7533
|
+
let unknown = false;
|
|
7534
|
+
let unknownTime = false;
|
|
7535
|
+
let oldest = null;
|
|
7536
|
+
for (const observation of observations) {
|
|
7537
|
+
const source = observation?.source;
|
|
7538
|
+
if (source === "cache") cache = true;
|
|
7539
|
+
else if (source !== "live") unknown = true;
|
|
7540
|
+
const time = validTime(observation?.sourceSyncedAt);
|
|
7541
|
+
if (time !== null) oldest = oldest === null ? time : Math.min(oldest, time);
|
|
7542
|
+
else if (source !== "live") unknownTime = true;
|
|
7543
|
+
}
|
|
7544
|
+
const syncedAt = unknownTime || oldest === null ? null : new Date(Math.min(oldest, checkedAt.getTime()));
|
|
7545
|
+
return {
|
|
7546
|
+
source: cache ? "cache" : unknown ? "unknown" : "live",
|
|
7547
|
+
sourceSyncedAt: syncedAt ? syncedAt.toISOString() : null
|
|
7548
|
+
};
|
|
7141
7549
|
}
|
|
7142
7550
|
function providerSyncFailure(error, now) {
|
|
7143
7551
|
const code = isVaultProviderError(error) ? error.code : "provider_unavailable";
|
|
@@ -7160,24 +7568,59 @@ function providerSyncFailure(error, now) {
|
|
|
7160
7568
|
lastSyncedAt: null
|
|
7161
7569
|
};
|
|
7162
7570
|
}
|
|
7571
|
+
async function providerStep(call) {
|
|
7572
|
+
try {
|
|
7573
|
+
return await call();
|
|
7574
|
+
} catch (error) {
|
|
7575
|
+
if (isVaultProviderError(error)) throw error;
|
|
7576
|
+
throw new VaultProviderError3(
|
|
7577
|
+
"provider_unavailable",
|
|
7578
|
+
"the vault provider could not be reached or gave an invalid answer"
|
|
7579
|
+
);
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7163
7582
|
async function syncProviderVault(input) {
|
|
7164
7583
|
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
7165
7584
|
const { target } = input;
|
|
7166
|
-
if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime()
|
|
7585
|
+
if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime()) {
|
|
7167
7586
|
return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
|
|
7168
7587
|
}
|
|
7588
|
+
const observations = [];
|
|
7589
|
+
const outer = input.ctx.onFreshness;
|
|
7590
|
+
const ctx = {
|
|
7591
|
+
...input.ctx,
|
|
7592
|
+
...target.cacheEpoch !== void 0 && input.ctx.cacheEpoch === void 0 ? { cacheEpoch: target.cacheEpoch } : {},
|
|
7593
|
+
...input.force ? { forceRefresh: true } : {},
|
|
7594
|
+
onFreshness(freshness) {
|
|
7595
|
+
observations.push(freshness);
|
|
7596
|
+
outer?.(freshness);
|
|
7597
|
+
}
|
|
7598
|
+
};
|
|
7599
|
+
const succeeded = () => {
|
|
7600
|
+
const checkedAt = now();
|
|
7601
|
+
return {
|
|
7602
|
+
syncStatus: "ok",
|
|
7603
|
+
syncErrorCode: null,
|
|
7604
|
+
syncErrorMessage: null,
|
|
7605
|
+
rateLimitedUntil: null,
|
|
7606
|
+
lastSyncedAt: checkedAt,
|
|
7607
|
+
freshness: aggregateProviderFreshness(observations, checkedAt)
|
|
7608
|
+
};
|
|
7609
|
+
};
|
|
7169
7610
|
try {
|
|
7170
|
-
const vaults = await input.provider
|
|
7171
|
-
const vault = vaults.find(
|
|
7611
|
+
const vaults = await providerStep(() => readProviderVaultInventory(input.provider, ctx));
|
|
7612
|
+
const vault = vaults.items.find(
|
|
7613
|
+
(candidate) => candidate.externalVaultId === target.externalVaultId
|
|
7614
|
+
);
|
|
7172
7615
|
if (!vault) {
|
|
7173
7616
|
const failure = providerSyncFailure(
|
|
7174
|
-
new
|
|
7617
|
+
vaults.consistent ? new VaultProviderError3(
|
|
7175
7618
|
"provider_not_found",
|
|
7176
7619
|
"the linked provider vault no longer exists or is no longer shared with this connection"
|
|
7177
|
-
),
|
|
7620
|
+
) : unprovenInventory("vaults"),
|
|
7178
7621
|
now()
|
|
7179
7622
|
);
|
|
7180
|
-
await input.store.
|
|
7623
|
+
if (!await input.store.settleRefresh(failure)) return { status: "superseded" };
|
|
7181
7624
|
return {
|
|
7182
7625
|
status: "failed",
|
|
7183
7626
|
syncStatus: failure.syncStatus,
|
|
@@ -7185,26 +7628,19 @@ async function syncProviderVault(input) {
|
|
|
7185
7628
|
rateLimitedUntil: null
|
|
7186
7629
|
};
|
|
7187
7630
|
}
|
|
7631
|
+
const operations = providerOperations2(input.provider.capabilities(), vault);
|
|
7188
7632
|
if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
|
|
7189
|
-
await input.store.
|
|
7190
|
-
|
|
7191
|
-
syncErrorCode: null,
|
|
7192
|
-
syncErrorMessage: null,
|
|
7193
|
-
rateLimitedUntil: null,
|
|
7194
|
-
lastSyncedAt: now()
|
|
7195
|
-
});
|
|
7633
|
+
if (!await input.store.settleRefresh({ ...succeeded(), operations }))
|
|
7634
|
+
return { status: "superseded" };
|
|
7196
7635
|
return { status: "unchanged", contentVersion: vault.contentVersion };
|
|
7197
7636
|
}
|
|
7198
|
-
const
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
rateLimitedUntil: null,
|
|
7206
|
-
lastSyncedAt: now()
|
|
7207
|
-
});
|
|
7637
|
+
const inventory = await providerStep(
|
|
7638
|
+
() => readProviderItemInventory(input.provider, ctx, target.externalVaultId)
|
|
7639
|
+
);
|
|
7640
|
+
if (!inventory.consistent) throw unprovenInventory("items");
|
|
7641
|
+
const plan = planProviderMirror(inventory.items, vault.contentVersion);
|
|
7642
|
+
if (!await input.store.settleRefresh({ ...succeeded(), operations }, plan))
|
|
7643
|
+
return { status: "superseded" };
|
|
7208
7644
|
return {
|
|
7209
7645
|
status: "synced",
|
|
7210
7646
|
contentVersion: plan.contentVersion,
|
|
@@ -7214,7 +7650,7 @@ async function syncProviderVault(input) {
|
|
|
7214
7650
|
} catch (error) {
|
|
7215
7651
|
if (!isVaultProviderError(error)) throw error;
|
|
7216
7652
|
const failure = providerSyncFailure(error, now());
|
|
7217
|
-
await input.store.
|
|
7653
|
+
if (!await input.store.settleRefresh(failure)) return { status: "superseded" };
|
|
7218
7654
|
return {
|
|
7219
7655
|
status: "failed",
|
|
7220
7656
|
syncStatus: failure.syncStatus,
|
|
@@ -7223,11 +7659,10 @@ async function syncProviderVault(input) {
|
|
|
7223
7659
|
};
|
|
7224
7660
|
}
|
|
7225
7661
|
}
|
|
7226
|
-
function databaseProviderMirrorStore(db, context, vaultId) {
|
|
7662
|
+
function databaseProviderMirrorStore(db, context, vaultId, expectedCacheEpoch) {
|
|
7227
7663
|
return {
|
|
7228
|
-
async
|
|
7229
|
-
|
|
7230
|
-
vaultId,
|
|
7664
|
+
async settleRefresh(update, plan) {
|
|
7665
|
+
const mirror = plan ? {
|
|
7231
7666
|
contentVersion: plan.contentVersion,
|
|
7232
7667
|
items: plan.items.map((item) => ({
|
|
7233
7668
|
providerItemRef: item.externalItemId,
|
|
@@ -7245,22 +7680,41 @@ function databaseProviderMirrorStore(db, context, vaultId) {
|
|
|
7245
7680
|
kind: field.kind
|
|
7246
7681
|
}))
|
|
7247
7682
|
}))
|
|
7248
|
-
}
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7683
|
+
} : void 0;
|
|
7684
|
+
try {
|
|
7685
|
+
await setVaultSyncStatus(db, context, {
|
|
7686
|
+
vaultId,
|
|
7687
|
+
expectedCacheEpoch,
|
|
7688
|
+
...mirror ? { mirror } : {},
|
|
7689
|
+
status: update.syncStatus,
|
|
7690
|
+
errorCode: update.syncErrorCode,
|
|
7691
|
+
errorMessage: update.syncErrorMessage,
|
|
7692
|
+
rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null,
|
|
7693
|
+
...update.operations !== void 0 ? { operations: update.operations } : {},
|
|
7694
|
+
...update.freshness !== void 0 ? { freshness: update.freshness } : {}
|
|
7695
|
+
});
|
|
7696
|
+
return true;
|
|
7697
|
+
} catch (error) {
|
|
7698
|
+
if (isVaultError(error) && error.code === "provider_conflict" && error.details?.superseded === true)
|
|
7699
|
+
return false;
|
|
7700
|
+
throw error;
|
|
7701
|
+
}
|
|
7258
7702
|
}
|
|
7259
7703
|
};
|
|
7260
7704
|
}
|
|
7261
7705
|
async function syncLinkedProviderVault(input) {
|
|
7262
7706
|
const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
|
|
7263
|
-
const store = databaseProviderMirrorStore(
|
|
7707
|
+
const store = databaseProviderMirrorStore(
|
|
7708
|
+
input.db,
|
|
7709
|
+
input.context,
|
|
7710
|
+
input.vaultId,
|
|
7711
|
+
target.providerCacheEpoch ?? "0"
|
|
7712
|
+
);
|
|
7713
|
+
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
7714
|
+
const rateLimitedUntil = target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null;
|
|
7715
|
+
if (rateLimitedUntil && rateLimitedUntil.getTime() > now().getTime()) {
|
|
7716
|
+
return { status: "skipped_rate_limited", rateLimitedUntil };
|
|
7717
|
+
}
|
|
7264
7718
|
let provider;
|
|
7265
7719
|
try {
|
|
7266
7720
|
provider = await input.providers.forVault({
|
|
@@ -7269,8 +7723,8 @@ async function syncLinkedProviderVault(input) {
|
|
|
7269
7723
|
});
|
|
7270
7724
|
} catch (error) {
|
|
7271
7725
|
if (!isVaultProviderError(error)) throw error;
|
|
7272
|
-
const failure = providerSyncFailure(error,
|
|
7273
|
-
await store.
|
|
7726
|
+
const failure = providerSyncFailure(error, now());
|
|
7727
|
+
if (!await store.settleRefresh(failure)) return { status: "superseded" };
|
|
7274
7728
|
return {
|
|
7275
7729
|
status: "failed",
|
|
7276
7730
|
syncStatus: failure.syncStatus,
|
|
@@ -7283,12 +7737,16 @@ async function syncLinkedProviderVault(input) {
|
|
|
7283
7737
|
ctx: {
|
|
7284
7738
|
connectionId: target.providerConnectionId,
|
|
7285
7739
|
budget: input.budget ?? { take() {
|
|
7286
|
-
} }
|
|
7740
|
+
} },
|
|
7741
|
+
cacheEpoch: target.providerCacheEpoch ?? null,
|
|
7742
|
+
...input.signal ? { signal: input.signal } : {},
|
|
7743
|
+
...input.onFreshness ? { onFreshness: input.onFreshness } : {}
|
|
7287
7744
|
},
|
|
7288
7745
|
target: {
|
|
7289
7746
|
externalVaultId: target.providerVaultRef,
|
|
7290
7747
|
mirroredContentVersion: target.providerContentVersion,
|
|
7291
|
-
rateLimitedUntil
|
|
7748
|
+
rateLimitedUntil,
|
|
7749
|
+
cacheEpoch: target.providerCacheEpoch ?? null
|
|
7292
7750
|
},
|
|
7293
7751
|
store,
|
|
7294
7752
|
...input.now ? { now: input.now } : {},
|
|
@@ -7299,6 +7757,7 @@ var LINKED_VAULT_SEARCH_MAX_AGE_MS = 10 * 6e4;
|
|
|
7299
7757
|
var LINKED_VAULT_MISS_MAX_AGE_MS = 2 * 6e4;
|
|
7300
7758
|
var LINKED_VAULT_REFRESH_WAIT_MS = 8e3;
|
|
7301
7759
|
var LINKED_VAULT_OPEN_WAIT_MS = 3e3;
|
|
7760
|
+
var LINKED_VAULT_REFRESH_TIMEOUT_MS = 6e4;
|
|
7302
7761
|
function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
|
|
7303
7762
|
if (vault.source === "opengeni") return false;
|
|
7304
7763
|
if (vault.rateLimitedUntil && new Date(vault.rateLimitedUntil).getTime() > now.getTime()) {
|
|
@@ -7308,38 +7767,124 @@ function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
|
|
|
7308
7767
|
if (!last) return true;
|
|
7309
7768
|
return now.getTime() - new Date(last).getTime() >= maxAgeMs;
|
|
7310
7769
|
}
|
|
7770
|
+
function isoOrNull(value) {
|
|
7771
|
+
const time = validTime(value);
|
|
7772
|
+
return time === null ? null : new Date(time).toISOString();
|
|
7773
|
+
}
|
|
7774
|
+
function providerVaultFreshness(vault, now, options = {}) {
|
|
7775
|
+
if (vault.source === "opengeni") return void 0;
|
|
7776
|
+
const stored2 = vault.providerFreshness ?? null;
|
|
7777
|
+
const failed = vault.syncStatus !== "ok" && vault.syncStatus !== "syncing";
|
|
7778
|
+
const base = {
|
|
7779
|
+
lastCheckedAt: isoOrNull(vault.lastSyncedAt),
|
|
7780
|
+
sourceSyncedAt: isoOrNull(stored2?.sourceSyncedAt),
|
|
7781
|
+
source: stored2?.source === "live" || stored2?.source === "cache" ? stored2.source : "unknown",
|
|
7782
|
+
errorCode: failed && vault.syncErrorCode ? vault.syncErrorCode.slice(0, 100) : null,
|
|
7783
|
+
retryAt: null
|
|
7784
|
+
};
|
|
7785
|
+
if (options.refreshing || vault.syncStatus === "syncing") return { state: "refreshing", ...base };
|
|
7786
|
+
if (vault.syncStatus === "rate_limited") {
|
|
7787
|
+
const retryAt = isoOrNull(vault.rateLimitedUntil);
|
|
7788
|
+
if (retryAt !== null && Date.parse(retryAt) > now.getTime()) {
|
|
7789
|
+
return { state: "unavailable", ...base, retryAt };
|
|
7790
|
+
}
|
|
7791
|
+
return { state: "stale", ...base };
|
|
7792
|
+
}
|
|
7793
|
+
if (failed) return { state: "unavailable", ...base };
|
|
7794
|
+
const checked = base.lastCheckedAt === null ? null : Date.parse(base.lastCheckedAt);
|
|
7795
|
+
const maxAgeMs = options.maxAgeMs ?? LINKED_VAULT_SEARCH_MAX_AGE_MS;
|
|
7796
|
+
if (checked === null || now.getTime() - checked >= maxAgeMs) return { state: "stale", ...base };
|
|
7797
|
+
return { state: "current", ...base };
|
|
7798
|
+
}
|
|
7799
|
+
function vaultRefreshScopeKey(context) {
|
|
7800
|
+
if (context.kind === "subject") {
|
|
7801
|
+
return `${context.accountId}|${context.workspaceId}|subject|${context.subjectId}`;
|
|
7802
|
+
}
|
|
7803
|
+
const principal = context.principal;
|
|
7804
|
+
switch (principal.kind) {
|
|
7805
|
+
case "attempt":
|
|
7806
|
+
return `${principal.accountId}|${principal.workspaceId}|attempt|${principal.attemptId}|${principal.executionGeneration}`;
|
|
7807
|
+
case "site_viewer":
|
|
7808
|
+
return `${principal.accountId}|${principal.workspaceId}|site|${principal.siteId}|${principal.siteVersion}|${principal.subjectId}`;
|
|
7809
|
+
case "human":
|
|
7810
|
+
return `${principal.accountId}|${principal.workspaceId}|human|${principal.subjectId}|${principal.viaApiKey ? "key" : principal.verifiedHuman ? "verified" : "unverified"}`;
|
|
7811
|
+
}
|
|
7812
|
+
}
|
|
7311
7813
|
var linkedVaultRefreshes = /* @__PURE__ */ new Map();
|
|
7312
|
-
async function
|
|
7814
|
+
async function refreshLinkedVaults(input) {
|
|
7313
7815
|
const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7314
|
-
const
|
|
7315
|
-
|
|
7816
|
+
const maxAgeMs = input.maxAgeMs ?? LINKED_VAULT_SEARCH_MAX_AGE_MS;
|
|
7817
|
+
const scope = vaultRefreshScopeKey(input.context);
|
|
7818
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
7316
7819
|
const changed = [];
|
|
7317
|
-
const runs =
|
|
7318
|
-
|
|
7820
|
+
const runs = [];
|
|
7821
|
+
for (const vault of input.vaults) {
|
|
7822
|
+
if (outcomes.has(vault.id)) continue;
|
|
7823
|
+
const limited = vault.rateLimitedUntil !== null && new Date(vault.rateLimitedUntil).getTime() > now.getTime();
|
|
7824
|
+
const due = vault.source !== "opengeni" && !limited && (input.force || linkedVaultNeedsRefresh(vault, maxAgeMs, now));
|
|
7825
|
+
const outcome = {
|
|
7826
|
+
vaultId: vault.id,
|
|
7827
|
+
state: due ? "pending" : "not_due",
|
|
7828
|
+
result: null
|
|
7829
|
+
};
|
|
7830
|
+
outcomes.set(vault.id, outcome);
|
|
7831
|
+
if (!due) continue;
|
|
7832
|
+
const key = `${scope}|${vault.id}|${input.force ? "force" : "stale"}`;
|
|
7833
|
+
let run = linkedVaultRefreshes.get(key);
|
|
7319
7834
|
if (!run) {
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7835
|
+
let timer;
|
|
7836
|
+
run = Promise.race([
|
|
7837
|
+
syncLinkedProviderVault({
|
|
7838
|
+
db: input.db,
|
|
7839
|
+
context: input.context,
|
|
7840
|
+
vaultId: vault.id,
|
|
7841
|
+
providers: input.providers,
|
|
7842
|
+
signal: AbortSignal.timeout(LINKED_VAULT_REFRESH_TIMEOUT_MS),
|
|
7843
|
+
...input.force ? { force: true } : {},
|
|
7844
|
+
...input.now ? { now: input.now } : {}
|
|
7845
|
+
}),
|
|
7846
|
+
new Promise((resolve) => {
|
|
7847
|
+
timer = setTimeout(() => resolve(null), LINKED_VAULT_REFRESH_TIMEOUT_MS);
|
|
7848
|
+
timer.unref?.();
|
|
7849
|
+
})
|
|
7850
|
+
]).catch(() => {
|
|
7851
|
+
outcome.state = "failed";
|
|
7852
|
+
return null;
|
|
7853
|
+
}).finally(() => {
|
|
7854
|
+
if (timer) clearTimeout(timer);
|
|
7855
|
+
linkedVaultRefreshes.delete(key);
|
|
7856
|
+
});
|
|
7857
|
+
linkedVaultRefreshes.set(key, run);
|
|
7328
7858
|
}
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7859
|
+
runs.push(
|
|
7860
|
+
run.then((result) => {
|
|
7861
|
+
if (outcome.state === "pending") outcome.state = result ? "done" : "failed";
|
|
7862
|
+
outcome.result = result;
|
|
7863
|
+
if (result?.status === "synced") changed.push(vault.id);
|
|
7864
|
+
})
|
|
7865
|
+
);
|
|
7866
|
+
}
|
|
7867
|
+
if (runs.length > 0) {
|
|
7868
|
+
let timer;
|
|
7869
|
+
await Promise.race([
|
|
7870
|
+
Promise.all(runs),
|
|
7871
|
+
new Promise((resolve) => {
|
|
7872
|
+
timer = setTimeout(resolve, input.waitMs ?? LINKED_VAULT_REFRESH_WAIT_MS);
|
|
7873
|
+
timer.unref?.();
|
|
7874
|
+
})
|
|
7875
|
+
]);
|
|
7876
|
+
if (timer) clearTimeout(timer);
|
|
7877
|
+
}
|
|
7878
|
+
const list = [...outcomes.values()].map((outcome) => ({ ...outcome }));
|
|
7879
|
+
return {
|
|
7880
|
+
outcomes: list,
|
|
7881
|
+
changed: [...changed],
|
|
7882
|
+
pending: list.filter((outcome) => outcome.state === "pending").map((outcome) => outcome.vaultId)
|
|
7883
|
+
};
|
|
7884
|
+
}
|
|
7885
|
+
async function refreshStaleLinkedVaults(input) {
|
|
7886
|
+
const report = await refreshLinkedVaults(input);
|
|
7887
|
+
return report.changed;
|
|
7343
7888
|
}
|
|
7344
7889
|
|
|
7345
7890
|
// src/domain/vaults/secret-resolver.ts
|
|
@@ -7473,6 +8018,51 @@ function providerFailureCode(code) {
|
|
|
7473
8018
|
if (code === "provider_permission_denied") return "denied";
|
|
7474
8019
|
return "provider_unavailable";
|
|
7475
8020
|
}
|
|
8021
|
+
function relevantProviderVaults(reference, candidates, reachable) {
|
|
8022
|
+
const connected = reachable.filter((vault) => vault.source !== "opengeni");
|
|
8023
|
+
if (connected.length === 0) return [];
|
|
8024
|
+
const where = possibleProviderVaults(reference, reachable, connected);
|
|
8025
|
+
const live = candidates.filter((candidate) => !candidate.archived);
|
|
8026
|
+
if (live.length === 0) return where;
|
|
8027
|
+
const deciding = Math.min(...live.map((candidate) => candidate.tier));
|
|
8028
|
+
const lacking = new Set(
|
|
8029
|
+
live.filter(
|
|
8030
|
+
(candidate) => candidate.tier <= deciding && candidate.source !== "opengeni" && (candidate.missing === "field" || candidate.missing === "totp_field")
|
|
8031
|
+
).map((candidate) => candidate.vaultId)
|
|
8032
|
+
);
|
|
8033
|
+
const matched = new Set(live.map((candidate) => candidate.vaultId));
|
|
8034
|
+
const unmatched = new Set(
|
|
8035
|
+
where.filter((vault) => vault.tier <= deciding && !matched.has(vault.id)).map((vault) => vault.id)
|
|
8036
|
+
);
|
|
8037
|
+
return connected.filter((vault) => lacking.has(vault.id) || unmatched.has(vault.id));
|
|
8038
|
+
}
|
|
8039
|
+
function possibleProviderVaults(reference, reachable, connected) {
|
|
8040
|
+
if (reference.target.form !== "path") return [];
|
|
8041
|
+
const segments = reference.target.segments;
|
|
8042
|
+
if (segments.length === 1) return [...connected];
|
|
8043
|
+
const nameKey2 = secretNameKey(segments[0]);
|
|
8044
|
+
const named = reachable.filter((vault) => secretNameKey(vault.name) === nameKey2);
|
|
8045
|
+
if (segments.length === 2 && named.length === 0) return [...connected];
|
|
8046
|
+
return named.filter((vault) => vault.source !== "opengeni");
|
|
8047
|
+
}
|
|
8048
|
+
function uncheckedSourceFailure(reference, unchecked) {
|
|
8049
|
+
const authExpired = unchecked.filter(({ vault }) => vault.syncStatus === "auth_expired");
|
|
8050
|
+
const rateLimited = unchecked.filter(
|
|
8051
|
+
({ freshness }) => freshness.state === "unavailable" && freshness.retryAt !== null
|
|
8052
|
+
);
|
|
8053
|
+
const [code, named, why] = authExpired.length > 0 ? ["provider_auth_expired", authExpired, "needs to be reconnected"] : rateLimited.length > 0 ? ["provider_rate_limited", rateLimited, "is rate limited by its provider"] : [
|
|
8054
|
+
"provider_unavailable",
|
|
8055
|
+
unchecked,
|
|
8056
|
+
unchecked.every(({ freshness }) => freshness.state === "refreshing") ? "is still refreshing" : "could not be checked"
|
|
8057
|
+
];
|
|
8058
|
+
const names = named.slice(0, 3).map(({ vault }) => vault.name).join(", ");
|
|
8059
|
+
const retryAt = rateLimited.map(({ freshness }) => freshness.retryAt).sort().at(0);
|
|
8060
|
+
return {
|
|
8061
|
+
raw: reference.raw,
|
|
8062
|
+
code,
|
|
8063
|
+
message: `not found in the vaults that could be checked, but ${names} ${why}, so it may be there` + (code === "provider_rate_limited" && retryAt ? `; try again after ${retryAt}` : "; try again later")
|
|
8064
|
+
};
|
|
8065
|
+
}
|
|
7476
8066
|
function decodeBase64(value) {
|
|
7477
8067
|
return new Uint8Array(Buffer.from(value, "base64"));
|
|
7478
8068
|
}
|
|
@@ -7535,35 +8125,74 @@ function createSecretResolver(deps, principal, opts) {
|
|
|
7535
8125
|
const { meta: meta2, candidates } = await resolveSecretReferenceCandidates(deps.db, context, {
|
|
7536
8126
|
refs: references.map((reference, index) => lookupFor(reference, index))
|
|
7537
8127
|
});
|
|
7538
|
-
const
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
const outcome = chooseSecretCandidate(
|
|
7542
|
-
reference,
|
|
7543
|
-
candidates.filter((candidate) => candidate.refIndex === index),
|
|
7544
|
-
meta2
|
|
7545
|
-
);
|
|
7546
|
-
if ("failure" in outcome) failures2.push(outcome.failure);
|
|
7547
|
-
else chosen2.push({ reference, candidate: outcome.candidate });
|
|
8128
|
+
const outcomes2 = references.map((reference, index) => {
|
|
8129
|
+
const own = candidates.filter((candidate) => candidate.refIndex === index);
|
|
8130
|
+
return { reference, candidates: own, outcome: chooseSecretCandidate(reference, own, meta2) };
|
|
7548
8131
|
});
|
|
7549
|
-
return { meta: meta2,
|
|
8132
|
+
return { meta: meta2, outcomes: outcomes2 };
|
|
7550
8133
|
};
|
|
7551
|
-
let { meta,
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
if (registry) {
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
8134
|
+
let { meta, outcomes } = await choose();
|
|
8135
|
+
let registry;
|
|
8136
|
+
const providers = () => {
|
|
8137
|
+
if (registry === void 0) {
|
|
8138
|
+
registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId) ?? null;
|
|
8139
|
+
}
|
|
8140
|
+
return registry;
|
|
8141
|
+
};
|
|
8142
|
+
const misses = () => outcomes.filter(
|
|
8143
|
+
(entry) => "failure" in entry.outcome && entry.outcome.failure.code === "not_found"
|
|
8144
|
+
);
|
|
8145
|
+
let reachable = null;
|
|
8146
|
+
let refreshing = /* @__PURE__ */ new Set();
|
|
8147
|
+
if (misses().length > 0) {
|
|
8148
|
+
reachable = await listVaultsForSubject(deps.db, context, {
|
|
8149
|
+
workspaceId: principal.workspaceId
|
|
8150
|
+
});
|
|
8151
|
+
const relevant = /* @__PURE__ */ new Map();
|
|
8152
|
+
for (const entry of misses()) {
|
|
8153
|
+
for (const vault of relevantProviderVaults(entry.reference, entry.candidates, reachable)) {
|
|
8154
|
+
relevant.set(vault.id, vault);
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
8157
|
+
const refreshProviders = relevant.size > 0 ? providers() : null;
|
|
8158
|
+
if (refreshProviders) {
|
|
8159
|
+
const report = await refreshLinkedVaults({
|
|
7559
8160
|
db: deps.db,
|
|
7560
8161
|
context,
|
|
7561
|
-
vaults:
|
|
7562
|
-
providers:
|
|
8162
|
+
vaults: [...relevant.values()],
|
|
8163
|
+
providers: refreshProviders,
|
|
7563
8164
|
maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS
|
|
7564
8165
|
});
|
|
7565
|
-
|
|
8166
|
+
refreshing = new Set(report.pending);
|
|
8167
|
+
if (report.changed.length > 0 || report.outcomes.some((outcome) => outcome.result?.status === "superseded"))
|
|
8168
|
+
({ meta, outcomes } = await choose());
|
|
8169
|
+
if (report.outcomes.some((outcome) => outcome.state === "done" || outcome.state === "failed")) {
|
|
8170
|
+
reachable = await listVaultsForSubject(deps.db, context, {
|
|
8171
|
+
workspaceId: principal.workspaceId
|
|
8172
|
+
});
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
}
|
|
8176
|
+
const failures = [];
|
|
8177
|
+
const chosen = [];
|
|
8178
|
+
const checkedAt = /* @__PURE__ */ new Date();
|
|
8179
|
+
for (const entry of outcomes) {
|
|
8180
|
+
if ("candidate" in entry.outcome) {
|
|
8181
|
+
chosen.push({ reference: entry.reference, candidate: entry.outcome.candidate });
|
|
8182
|
+
continue;
|
|
8183
|
+
}
|
|
8184
|
+
let failure = entry.outcome.failure;
|
|
8185
|
+
if (failure.code === "not_found" && reachable) {
|
|
8186
|
+
const unchecked = relevantProviderVaults(entry.reference, entry.candidates, reachable).map((vault) => ({
|
|
8187
|
+
vault,
|
|
8188
|
+
freshness: providerVaultFreshness(vault, checkedAt, {
|
|
8189
|
+
maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS,
|
|
8190
|
+
refreshing: refreshing.has(vault.id)
|
|
8191
|
+
})
|
|
8192
|
+
})).filter(({ freshness }) => freshness.state !== "current");
|
|
8193
|
+
if (unchecked.length > 0) failure = uncheckedSourceFailure(entry.reference, unchecked);
|
|
7566
8194
|
}
|
|
8195
|
+
failures.push(failure);
|
|
7567
8196
|
}
|
|
7568
8197
|
const hasActingHuman = meta.humanExpected && meta.humanValid;
|
|
7569
8198
|
if (failures.length > 0) {
|
|
@@ -7582,13 +8211,16 @@ function createSecretResolver(deps, principal, opts) {
|
|
|
7582
8211
|
for (const group of providerGroups.values()) {
|
|
7583
8212
|
const first = group[0].candidate;
|
|
7584
8213
|
try {
|
|
7585
|
-
const
|
|
7586
|
-
const provider = await registry.forVault({
|
|
8214
|
+
const provider = await (providers() ?? EMPTY_VAULT_PROVIDER_REGISTRY).forVault({
|
|
7587
8215
|
source: first.source,
|
|
7588
8216
|
providerConnectionId: first.providerConnectionId
|
|
7589
8217
|
});
|
|
7590
8218
|
const values = await provider.readFields(
|
|
7591
|
-
{
|
|
8219
|
+
{
|
|
8220
|
+
connectionId: first.providerConnectionId,
|
|
8221
|
+
budget,
|
|
8222
|
+
cacheEpoch: first.providerCacheEpoch ?? null
|
|
8223
|
+
},
|
|
7592
8224
|
first.providerVaultRef ?? "",
|
|
7593
8225
|
first.providerItemRef ?? "",
|
|
7594
8226
|
[...new Set(group.map((entry) => entry.candidate.providerFieldRef ?? ""))]
|
|
@@ -7602,7 +8234,7 @@ function createSecretResolver(deps, principal, opts) {
|
|
|
7602
8234
|
failures.push({
|
|
7603
8235
|
raw: entry.reference.raw,
|
|
7604
8236
|
code,
|
|
7605
|
-
message: code === "provider_rate_limited" ? "the connected vault is rate limited; try again later" : code === "provider_auth_expired" ? "the connected vault needs to be reconnected" : "the connected vault is unavailable"
|
|
8237
|
+
message: code === "provider_rate_limited" ? "the connected vault is rate limited; try again later" : code === "provider_auth_expired" ? "the connected vault needs to be reconnected" : code === "not_found" ? "the connected vault no longer has this item" : "the connected vault is unavailable"
|
|
7606
8238
|
});
|
|
7607
8239
|
}
|
|
7608
8240
|
}
|
|
@@ -8416,7 +9048,7 @@ async function swapActiveSandbox(services, ctx, target, workingDir) {
|
|
|
8416
9048
|
async function createRunOnSecretResolver(services, attempt, providersForSubject) {
|
|
8417
9049
|
const session = await getSession3(services.db, attempt.workspaceId, attempt.sessionId);
|
|
8418
9050
|
return createSecretResolver(
|
|
8419
|
-
{ db: services.db, encryptionKey:
|
|
9051
|
+
{ db: services.db, encryptionKey: environmentsEncryptionKeyBytes3(services.settings) ?? null },
|
|
8420
9052
|
{ kind: "attempt", ...attempt },
|
|
8421
9053
|
{
|
|
8422
9054
|
capabilityEnabled: session?.agent ? agentCapabilityEnabled(session.agent.capabilities, "secrets") : true,
|
|
@@ -8439,7 +9071,8 @@ function runOnOperationAdmission(services, enrollment) {
|
|
|
8439
9071
|
operationResourcePolicy: enrollment.operationPolicy,
|
|
8440
9072
|
operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
|
|
8441
9073
|
operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
|
|
8442
|
-
transactionalFsWriteSupported: enrollment.agentCapabilities.transactionalFsWrite === true
|
|
9074
|
+
transactionalFsWriteSupported: enrollment.agentCapabilities.transactionalFsWrite === true,
|
|
9075
|
+
browserControlHttpSupported: enrollment.agentCapabilities.browserControlHttp === true
|
|
8443
9076
|
};
|
|
8444
9077
|
}
|
|
8445
9078
|
function hasSecretFileReference(carry) {
|
|
@@ -10603,10 +11236,10 @@ async function validateSubmittedTimelineAnnotations(db, workspaceId, sessionId,
|
|
|
10603
11236
|
}
|
|
10604
11237
|
|
|
10605
11238
|
// src/domain/encryption-key.ts
|
|
10606
|
-
import { environmentsEncryptionKeyBytes as
|
|
11239
|
+
import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes4 } from "@opengeni/config";
|
|
10607
11240
|
import { HTTPException as HTTPException19 } from "hono/http-exception";
|
|
10608
11241
|
function requireEnvironmentEncryption(settings) {
|
|
10609
|
-
const key =
|
|
11242
|
+
const key = environmentsEncryptionKeyBytes4(settings);
|
|
10610
11243
|
if (!key) {
|
|
10611
11244
|
throw new HTTPException19(503, {
|
|
10612
11245
|
message: "secrets require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"
|
|
@@ -10630,7 +11263,7 @@ import {
|
|
|
10630
11263
|
areOpenGeniSlackBotScopesAccepted
|
|
10631
11264
|
} from "@opengeni/contracts";
|
|
10632
11265
|
import {
|
|
10633
|
-
getConnectionMetadata as
|
|
11266
|
+
getConnectionMetadata as getConnectionMetadata5,
|
|
10634
11267
|
availableSlackBotConnectionMetadata,
|
|
10635
11268
|
requireWorkspace as requireWorkspace4
|
|
10636
11269
|
} from "@opengeni/db";
|
|
@@ -10651,7 +11284,7 @@ function hasReservedOpenGeniSlackBotSessionMetadata(metadata) {
|
|
|
10651
11284
|
);
|
|
10652
11285
|
}
|
|
10653
11286
|
async function requireOpenGeniSlackBotConnection(db, workspaceId, connectionId) {
|
|
10654
|
-
const local = await
|
|
11287
|
+
const local = await getConnectionMetadata5(db, workspaceId, connectionId, null);
|
|
10655
11288
|
const workspace = await requireWorkspace4(db, workspaceId);
|
|
10656
11289
|
const connection = local ?? (await availableSlackBotConnectionMetadata(db, { accountId: workspace.accountId, workspaceId })).find((candidate) => candidate.id === connectionId) ?? null;
|
|
10657
11290
|
if (!connection || !isOpenGeniSlackBotConnection(connection)) {
|
|
@@ -12621,9 +13254,9 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
12621
13254
|
const payload = CreateSessionRequest.parse(rawPayload);
|
|
12622
13255
|
await assertSiteAgentMayChooseCompute(unresolvedDeps.db, grant, payload);
|
|
12623
13256
|
if (payload.firstPartyMcpTools) {
|
|
12624
|
-
const
|
|
13257
|
+
const unavailable2 = deploymentUnavailableFirstPartyMcpTools(unresolvedDeps.settings);
|
|
12625
13258
|
payload.firstPartyMcpTools = payload.firstPartyMcpTools.filter(
|
|
12626
|
-
(tool) => !
|
|
13259
|
+
(tool) => !unavailable2.has(tool)
|
|
12627
13260
|
);
|
|
12628
13261
|
}
|
|
12629
13262
|
const surface = resolveTurnSurface({
|
|
@@ -15710,7 +16343,10 @@ function createRealtimeVoiceBilling(deps) {
|
|
|
15710
16343
|
};
|
|
15711
16344
|
}
|
|
15712
16345
|
const attribution = input.callerSubjectId === facts.ownerSubjectId ? input.attribution : { kind: "unknown" };
|
|
15713
|
-
const cost = realtimeVoiceMinuteCreditMicros(
|
|
16346
|
+
const cost = realtimeVoiceMinuteCreditMicros(
|
|
16347
|
+
voice.pricing,
|
|
16348
|
+
deps.settings.creditMarginBpsOverride
|
|
16349
|
+
);
|
|
15714
16350
|
const minutes = facts.connections.flatMap(
|
|
15715
16351
|
(connection) => Array.from(
|
|
15716
16352
|
{ length: realtimeVoiceStartedMinutes(connection.startedAt, connection.observedUntil) },
|
|
@@ -15898,7 +16534,11 @@ function createVoiceInputBilling(deps) {
|
|
|
15898
16534
|
usage: input.usage,
|
|
15899
16535
|
trustedDurationSeconds: input.billing.trustedDurationSeconds
|
|
15900
16536
|
});
|
|
15901
|
-
const cost = calculateVoiceInputCost(
|
|
16537
|
+
const cost = calculateVoiceInputCost(
|
|
16538
|
+
input.pricing,
|
|
16539
|
+
usage,
|
|
16540
|
+
deps.settings.creditMarginBpsOverride
|
|
16541
|
+
);
|
|
15902
16542
|
return await settleVoiceInputUsage(deps.db, {
|
|
15903
16543
|
accountId: input.accountId,
|
|
15904
16544
|
workspaceId: input.workspaceId,
|
|
@@ -16106,22 +16746,137 @@ function createWebSearchBilling(deps) {
|
|
|
16106
16746
|
};
|
|
16107
16747
|
}
|
|
16108
16748
|
|
|
16109
|
-
// src/
|
|
16110
|
-
|
|
16111
|
-
|
|
16112
|
-
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
import {
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
16749
|
+
// src/domain/vaults/remote-provider-runtime.ts
|
|
16750
|
+
import {
|
|
16751
|
+
DestinationPolicyError,
|
|
16752
|
+
ResponseBodyLimitError,
|
|
16753
|
+
isLocalTestEnvironment,
|
|
16754
|
+
pinnedFetch as pinnedFetch2,
|
|
16755
|
+
readResponseTextBounded,
|
|
16756
|
+
validateHttpUrl
|
|
16757
|
+
} from "@opengeni/network";
|
|
16758
|
+
import {
|
|
16759
|
+
RemoteVaultTransportError,
|
|
16760
|
+
VaultProviderError as VaultProviderError4,
|
|
16761
|
+
createRemoteVaultProvider
|
|
16762
|
+
} from "@opengeni/vaults";
|
|
16763
|
+
function createPinnedRemoteVaultTransport(settings, fetchImpl = pinnedFetch2) {
|
|
16764
|
+
return {
|
|
16765
|
+
async post(request) {
|
|
16766
|
+
if (request.signal?.aborted) throw new RemoteVaultTransportError("aborted", false);
|
|
16767
|
+
const controller = new AbortController();
|
|
16768
|
+
let timedOut = false;
|
|
16769
|
+
const timer = setTimeout(() => {
|
|
16770
|
+
timedOut = true;
|
|
16771
|
+
controller.abort();
|
|
16772
|
+
}, request.timeoutMs);
|
|
16773
|
+
const onAbort = () => controller.abort();
|
|
16774
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
16775
|
+
let rejectAborted;
|
|
16776
|
+
try {
|
|
16777
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
16778
|
+
rejectAborted = () => reject(new RemoteVaultTransportError(timedOut ? "timeout" : "aborted", true));
|
|
16779
|
+
controller.signal.addEventListener("abort", rejectAborted, { once: true });
|
|
16780
|
+
if (controller.signal.aborted) rejectAborted();
|
|
16781
|
+
});
|
|
16782
|
+
const exchange = async () => {
|
|
16783
|
+
const response = await fetchImpl(
|
|
16784
|
+
request.url,
|
|
16785
|
+
{
|
|
16786
|
+
method: "POST",
|
|
16787
|
+
headers: { ...request.headers },
|
|
16788
|
+
body: request.body,
|
|
16789
|
+
redirect: "manual",
|
|
16790
|
+
signal: controller.signal
|
|
16791
|
+
},
|
|
16792
|
+
settings,
|
|
16793
|
+
{ label: "Vault provider", requireHttpsOutsideLocalTest: true }
|
|
16794
|
+
);
|
|
16795
|
+
if (response.status >= 300 && response.status < 400) {
|
|
16796
|
+
await response.body?.cancel().catch(() => void 0);
|
|
16797
|
+
throw new RemoteVaultTransportError("redirect", true);
|
|
16798
|
+
}
|
|
16799
|
+
return {
|
|
16800
|
+
status: response.status,
|
|
16801
|
+
headers: response.headers,
|
|
16802
|
+
body: await readResponseTextBounded(
|
|
16803
|
+
response,
|
|
16804
|
+
request.maxResponseBytes,
|
|
16805
|
+
"Vault provider",
|
|
16806
|
+
{ signal: controller.signal }
|
|
16807
|
+
)
|
|
16808
|
+
};
|
|
16809
|
+
};
|
|
16810
|
+
return await Promise.race([exchange(), aborted]);
|
|
16811
|
+
} catch (error) {
|
|
16812
|
+
if (error instanceof RemoteVaultTransportError) throw error;
|
|
16813
|
+
if (error instanceof DestinationPolicyError)
|
|
16814
|
+
throw new RemoteVaultTransportError("refused", false);
|
|
16815
|
+
if (error instanceof ResponseBodyLimitError)
|
|
16816
|
+
throw new RemoteVaultTransportError("too_large", true);
|
|
16817
|
+
throw new RemoteVaultTransportError(
|
|
16818
|
+
timedOut ? "timeout" : controller.signal.aborted ? "aborted" : "network",
|
|
16819
|
+
true
|
|
16820
|
+
);
|
|
16821
|
+
} finally {
|
|
16822
|
+
clearTimeout(timer);
|
|
16823
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
16824
|
+
if (rejectAborted) controller.signal.removeEventListener("abort", rejectAborted);
|
|
16825
|
+
}
|
|
16826
|
+
}
|
|
16827
|
+
};
|
|
16828
|
+
}
|
|
16829
|
+
function createRemoteExternalVaultFactories(settings) {
|
|
16830
|
+
const transport = createPinnedRemoteVaultTransport(settings);
|
|
16831
|
+
const factory = async ({
|
|
16832
|
+
credentials,
|
|
16833
|
+
operations
|
|
16834
|
+
}) => {
|
|
16835
|
+
if (credentials.provider !== "external" && credentials.provider !== "bitwarden")
|
|
16836
|
+
throw new VaultProviderError4("provider_auth_invalid", "Vault provider does not match.");
|
|
16837
|
+
let endpoint2;
|
|
16838
|
+
try {
|
|
16839
|
+
endpoint2 = validateHttpUrl(credentials.endpoint, {
|
|
16840
|
+
allowLoopbackHttp: isLocalTestEnvironment(settings.environment),
|
|
16841
|
+
label: "Vault provider"
|
|
16842
|
+
});
|
|
16843
|
+
} catch {
|
|
16844
|
+
throw new VaultProviderError4(
|
|
16845
|
+
"provider_auth_invalid",
|
|
16846
|
+
"Use an HTTPS vault endpoint without URL credentials or a fragment."
|
|
16847
|
+
);
|
|
16848
|
+
}
|
|
16849
|
+
return {
|
|
16850
|
+
credentials: { ...credentials, endpoint: endpoint2 },
|
|
16851
|
+
provider: createRemoteVaultProvider({
|
|
16852
|
+
provider: credentials.provider,
|
|
16853
|
+
endpoint: endpoint2,
|
|
16854
|
+
signingSecret: credentials.signingSecret,
|
|
16855
|
+
transport,
|
|
16856
|
+
allowInsecureHttp: isLocalTestEnvironment(settings.environment),
|
|
16857
|
+
...operations ? { operations } : {}
|
|
16858
|
+
})
|
|
16859
|
+
};
|
|
16860
|
+
};
|
|
16861
|
+
return { external: factory, bitwarden: factory };
|
|
16862
|
+
}
|
|
16863
|
+
|
|
16864
|
+
// src/workflow-wake-contract.ts
|
|
16865
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
16866
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
|
|
16867
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
16868
|
+
var SESSION_WORKFLOW_WAKE_DISPATCH_REQUESTED_SIGNAL = "dispatchRequested";
|
|
16869
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS = 1e4;
|
|
16870
|
+
var TURN_ACTIVITY_CANCELLATION_HEARTBEAT_INTERVAL_MS = 500;
|
|
16871
|
+
|
|
16872
|
+
// src/user-presence.ts
|
|
16873
|
+
import { recordUserActivityPresence } from "@opengeni/db";
|
|
16874
|
+
var USER_PRESENCE_THROTTLE_MS = 6e4;
|
|
16875
|
+
var USER_PRESENCE_FLUSH_DELAY_MS = 5e3;
|
|
16876
|
+
var USER_PRESENCE_MAX_TRACKED = 5e4;
|
|
16877
|
+
var USER_PRESENCE_SUBJECT = /^user:[A-Za-z0-9_-]{8,128}$/;
|
|
16878
|
+
var PRESENCE_FLUSH_METRIC = {
|
|
16879
|
+
name: "opengeni_user_presence_flushes_total",
|
|
16125
16880
|
help: "Batched user presence writes from this API process by outcome."
|
|
16126
16881
|
};
|
|
16127
16882
|
function createUserPresenceRecorder(options) {
|
|
@@ -16916,7 +17671,7 @@ import { RotateSessionMcpCredentialsRequest } from "@opengeni/contracts";
|
|
|
16916
17671
|
import {
|
|
16917
17672
|
encryptVariableSetValue as encryptVariableSetValue3,
|
|
16918
17673
|
getWorkspaceGrant as getWorkspaceGrant3,
|
|
16919
|
-
subjectHasLiveWorkspaceAuthorityInScope
|
|
17674
|
+
subjectHasLiveWorkspaceAuthorityInScope as subjectHasLiveWorkspaceAuthorityInScope2
|
|
16920
17675
|
} from "@opengeni/db";
|
|
16921
17676
|
import {
|
|
16922
17677
|
rotateSessionMcpCredentialsAtomically,
|
|
@@ -17061,7 +17816,7 @@ async function rotateSessionMcpCredentialsForRequest(deps, authorization, sessio
|
|
|
17061
17816
|
} else if (!hasVerifiedOwningUserAuthorization(authorization)) {
|
|
17062
17817
|
throw new HTTPException24(403, { message: "credential rotation authority changed" });
|
|
17063
17818
|
}
|
|
17064
|
-
if (!await
|
|
17819
|
+
if (!await subjectHasLiveWorkspaceAuthorityInScope2(tx, scope)) {
|
|
17065
17820
|
throw new HTTPException24(403, { message: "credential rotation authority changed" });
|
|
17066
17821
|
}
|
|
17067
17822
|
}
|
|
@@ -17182,7 +17937,7 @@ async function executeConnectOperation(input) {
|
|
|
17182
17937
|
|
|
17183
17938
|
// src/application/prepared-mcp-connection.ts
|
|
17184
17939
|
import { createHash as createHash3 } from "crypto";
|
|
17185
|
-
import { isDeepStrictEqual as
|
|
17940
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
17186
17941
|
import {
|
|
17187
17942
|
CapabilityCatalogItem as CapabilityCatalogItem2,
|
|
17188
17943
|
McpServerConnectionRef,
|
|
@@ -17266,7 +18021,7 @@ async function prepareMcpConnectionActivation(input) {
|
|
|
17266
18021
|
const installation = await prepared.commit(db);
|
|
17267
18022
|
const savedRef = McpServerConnectionRef.safeParse(installation.config.connectionRef);
|
|
17268
18023
|
const retainedExactPin = savedRef.success && savedRef.data.authoritySource !== "host" && savedRef.data.connectionId === connection.id && savedRef.data.providerDomain === connectionRef.providerDomain && (savedRef.data.subjectScope ?? "workspace") === connectionRef.subjectScope && (savedRef.data.kind ?? connection.kind) === connection.kind && (!savedRef.data.resource || savedRef.data.resource === endpoint2);
|
|
17269
|
-
if (installation.status !== "active" || !retainedExactPin && !
|
|
18024
|
+
if (installation.status !== "active" || !retainedExactPin && !isDeepStrictEqual3(installation.config.connectionRef, connectionRef)) {
|
|
17270
18025
|
throw new HTTPException26(409, {
|
|
17271
18026
|
message: "MCP account selection changed; review the existing connection before setup"
|
|
17272
18027
|
});
|
|
@@ -17977,7 +18732,7 @@ function sha256Hex2(bytes) {
|
|
|
17977
18732
|
}
|
|
17978
18733
|
|
|
17979
18734
|
// src/domain/skill-search.ts
|
|
17980
|
-
import { pinnedFetch as
|
|
18735
|
+
import { pinnedFetch as pinnedFetch3, readResponseJsonBounded } from "@opengeni/network";
|
|
17981
18736
|
var PublicSkillSearchError = class extends Error {
|
|
17982
18737
|
constructor(code, message, retryAfterSeconds = null) {
|
|
17983
18738
|
super(message);
|
|
@@ -17992,7 +18747,7 @@ var maxResponseBytes = 256 * 1024;
|
|
|
17992
18747
|
var maxResults = 20;
|
|
17993
18748
|
var ownerPattern = /^[a-z0-9](?:[a-z0-9-]{0,38})$/iu;
|
|
17994
18749
|
var segmentPattern = /^[a-z0-9](?:[a-z0-9._-]{0,98}[a-z0-9])?$/iu;
|
|
17995
|
-
function createPublicSkillSearchClient(settings, fetcher =
|
|
18750
|
+
function createPublicSkillSearchClient(settings, fetcher = pinnedFetch3) {
|
|
17996
18751
|
return {
|
|
17997
18752
|
async search(input) {
|
|
17998
18753
|
const query = input.query.trim();
|
|
@@ -18119,7 +18874,7 @@ function parseRetryAfter(value) {
|
|
|
18119
18874
|
|
|
18120
18875
|
// src/domain/github-skill-source.ts
|
|
18121
18876
|
import { HTTPException as HTTPException28 } from "hono/http-exception";
|
|
18122
|
-
import { pinnedFetch as
|
|
18877
|
+
import { pinnedFetch as pinnedFetch4, readResponseJsonBounded as readResponseJsonBounded2, readResponseTextBounded as readResponseTextBounded2 } from "@opengeni/network";
|
|
18123
18878
|
import {
|
|
18124
18879
|
PORTABLE_SKILL_MAX_FILES as PORTABLE_SKILL_MAX_FILES2,
|
|
18125
18880
|
PORTABLE_SKILL_MAX_FILE_BYTES as PORTABLE_SKILL_MAX_FILE_BYTES2,
|
|
@@ -18220,7 +18975,7 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
|
|
|
18220
18975
|
}
|
|
18221
18976
|
async function downloadSkillSnapshot(settings, owner, repository, slug) {
|
|
18222
18977
|
const signal = AbortSignal.timeout(githubRequestTimeoutMs);
|
|
18223
|
-
const response = await
|
|
18978
|
+
const response = await pinnedFetch4(
|
|
18224
18979
|
`https://skills.sh/api/download/${[owner, repository, slug].map(encodeURIComponent).join("/")}`,
|
|
18225
18980
|
{ headers: { accept: "application/json" }, credentials: "omit", redirect: "manual", signal },
|
|
18226
18981
|
settings,
|
|
@@ -18255,7 +19010,7 @@ async function githubJson(settings, path, maxBytes, label) {
|
|
|
18255
19010
|
const controller = new AbortController();
|
|
18256
19011
|
const timeout = setTimeout(() => controller.abort(), githubRequestTimeoutMs);
|
|
18257
19012
|
try {
|
|
18258
|
-
const response = await
|
|
19013
|
+
const response = await pinnedFetch4(
|
|
18259
19014
|
`${githubApiBase}${path}`,
|
|
18260
19015
|
{
|
|
18261
19016
|
method: "GET",
|
|
@@ -18270,7 +19025,7 @@ async function githubJson(settings, path, maxBytes, label) {
|
|
|
18270
19025
|
{ label, requireHttpsOutsideLocalTest: true }
|
|
18271
19026
|
);
|
|
18272
19027
|
if (!response.ok) {
|
|
18273
|
-
await
|
|
19028
|
+
await readResponseTextBounded2(response, 8192, `${label} error`).catch(() => void 0);
|
|
18274
19029
|
if (response.status === 404) throw new Error(`${label} was not found or is not public`);
|
|
18275
19030
|
if (response.status === 403 || response.status === 429) {
|
|
18276
19031
|
throw new HTTPException28(429, {
|
|
@@ -18298,18 +19053,70 @@ function stringValue(value) {
|
|
|
18298
19053
|
|
|
18299
19054
|
// src/domain/vaults/index.ts
|
|
18300
19055
|
import { buildMissingSecretAskCard as buildMissingSecretAskCard2 } from "@opengeni/contracts";
|
|
18301
|
-
import { isVaultError, VaultError as VaultError3, vaultErrorFromDatabase } from "@opengeni/db";
|
|
19056
|
+
import { isVaultError as isVaultError3, VaultError as VaultError3, vaultErrorFromDatabase } from "@opengeni/db";
|
|
18302
19057
|
|
|
18303
19058
|
// src/domain/vaults/mapping.ts
|
|
18304
19059
|
import {
|
|
18305
19060
|
formatSecretReference as formatSecretReference2,
|
|
18306
|
-
secretNameKey
|
|
19061
|
+
secretNameKey as secretNameKey2,
|
|
19062
|
+
VaultProviderOperations as VaultProviderOperations2
|
|
18307
19063
|
} from "@opengeni/contracts";
|
|
18308
19064
|
import {
|
|
18309
19065
|
decryptEnvironmentValue as decryptEnvironmentValue2
|
|
18310
19066
|
} from "@opengeni/db";
|
|
19067
|
+
function withoutProviderWrites(operations) {
|
|
19068
|
+
return {
|
|
19069
|
+
...operations,
|
|
19070
|
+
createItemTypes: [],
|
|
19071
|
+
updateItem: false,
|
|
19072
|
+
removeField: false,
|
|
19073
|
+
archiveItem: false,
|
|
19074
|
+
archiveKind: "none",
|
|
19075
|
+
deleteItem: false,
|
|
19076
|
+
restoreItem: false,
|
|
19077
|
+
writeFiles: false,
|
|
19078
|
+
createTotp: false,
|
|
19079
|
+
updateTotp: false,
|
|
19080
|
+
createVault: false,
|
|
19081
|
+
restoreFieldHistory: false
|
|
19082
|
+
};
|
|
19083
|
+
}
|
|
19084
|
+
function intersectProviderOperations(left, right) {
|
|
19085
|
+
const archiveItem = left.archiveItem && right.archiveItem && left.archiveKind === right.archiveKind && left.archiveKind !== "none";
|
|
19086
|
+
return {
|
|
19087
|
+
createItemTypes: left.createItemTypes.filter(
|
|
19088
|
+
(type) => type !== "passkey" && right.createItemTypes.includes(type)
|
|
19089
|
+
),
|
|
19090
|
+
updateItem: left.updateItem && right.updateItem,
|
|
19091
|
+
removeField: left.removeField && right.removeField,
|
|
19092
|
+
archiveItem,
|
|
19093
|
+
archiveKind: archiveItem ? left.archiveKind : "none",
|
|
19094
|
+
deleteItem: false,
|
|
19095
|
+
restoreItem: left.restoreItem && right.restoreItem,
|
|
19096
|
+
readFiles: left.readFiles && right.readFiles,
|
|
19097
|
+
writeFiles: left.writeFiles && right.writeFiles,
|
|
19098
|
+
createTotp: left.createTotp && right.createTotp,
|
|
19099
|
+
updateTotp: left.updateTotp && right.updateTotp,
|
|
19100
|
+
createVault: left.createVault && right.createVault,
|
|
19101
|
+
restoreFieldHistory: left.restoreFieldHistory && right.restoreFieldHistory
|
|
19102
|
+
};
|
|
19103
|
+
}
|
|
19104
|
+
function storedProviderOperations(row) {
|
|
19105
|
+
if (row.source === "opengeni" || !row.providerOperations) return null;
|
|
19106
|
+
const parsed = VaultProviderOperations2.safeParse(row.providerOperations);
|
|
19107
|
+
return parsed.success ? providerOperationsForMode(parsed.data, row.mode) : null;
|
|
19108
|
+
}
|
|
19109
|
+
function providerOperationsForMode(operations, mode) {
|
|
19110
|
+
const normalized = intersectProviderOperations(operations, operations);
|
|
19111
|
+
return mode === "read_write" ? normalized : withoutProviderWrites(normalized);
|
|
19112
|
+
}
|
|
19113
|
+
function providerAllowsAnyWrite(operations) {
|
|
19114
|
+
if (!operations) return true;
|
|
19115
|
+
return operations.createItemTypes.length > 0 || operations.updateItem || operations.archiveItem;
|
|
19116
|
+
}
|
|
18311
19117
|
function vaultCanWrite(row, authority) {
|
|
18312
19118
|
if (row.mode !== "read_write" || !authority.write) return false;
|
|
19119
|
+
if (!providerAllowsAnyWrite(storedProviderOperations(row))) return false;
|
|
18313
19120
|
if (row.ownerKind === "user") return row.ownedByCaller;
|
|
18314
19121
|
return true;
|
|
18315
19122
|
}
|
|
@@ -18325,7 +19132,11 @@ function vaultCanManage(row, authority) {
|
|
|
18325
19132
|
return authority.write;
|
|
18326
19133
|
}
|
|
18327
19134
|
}
|
|
18328
|
-
function toVaultSummary(row, authority) {
|
|
19135
|
+
function toVaultSummary(row, authority, options = {}) {
|
|
19136
|
+
const operations = storedProviderOperations(row);
|
|
19137
|
+
const freshness = providerVaultFreshness(row, options.now ?? /* @__PURE__ */ new Date(), {
|
|
19138
|
+
...options.refreshing ? { refreshing: true } : {}
|
|
19139
|
+
});
|
|
18329
19140
|
return {
|
|
18330
19141
|
id: row.id,
|
|
18331
19142
|
workspaceId: row.workspaceId,
|
|
@@ -18342,6 +19153,8 @@ function toVaultSummary(row, authority) {
|
|
|
18342
19153
|
lastSyncedAt: row.lastSyncedAt,
|
|
18343
19154
|
rateLimitedUntil: row.rateLimitedUntil,
|
|
18344
19155
|
providerConnectionId: row.providerConnectionId,
|
|
19156
|
+
...operations ? { providerOperations: operations } : {},
|
|
19157
|
+
...freshness ? { freshness } : {},
|
|
18345
19158
|
availability: row.availability,
|
|
18346
19159
|
isDefault: row.isDefault,
|
|
18347
19160
|
itemCount: Number(row.itemCount),
|
|
@@ -18381,7 +19194,7 @@ function vaultItemReferences(item) {
|
|
|
18381
19194
|
}
|
|
18382
19195
|
function usernameOf(row, key) {
|
|
18383
19196
|
const field = row.fields.find(
|
|
18384
|
-
(candidate) => candidate.kind === "text" &&
|
|
19197
|
+
(candidate) => candidate.kind === "text" && secretNameKey2(candidate.name) === "username"
|
|
18385
19198
|
);
|
|
18386
19199
|
return field ? decryptOrNull(key, field.valueEncrypted) : null;
|
|
18387
19200
|
}
|
|
@@ -18418,7 +19231,7 @@ function toVaultItemField(item, field, key) {
|
|
|
18418
19231
|
}
|
|
18419
19232
|
function toVaultItem(row, key) {
|
|
18420
19233
|
const textField = (name) => {
|
|
18421
|
-
const field = row.fields.find((candidate) =>
|
|
19234
|
+
const field = row.fields.find((candidate) => secretNameKey2(candidate.name) === name);
|
|
18422
19235
|
return field ? decryptOrNull(key, field.valueEncrypted) : null;
|
|
18423
19236
|
};
|
|
18424
19237
|
const credentialId = row.type === "passkey" ? textField("credentialid") ?? "" : "";
|
|
@@ -18441,68 +19254,25 @@ function toVaultItem(row, key) {
|
|
|
18441
19254
|
};
|
|
18442
19255
|
}
|
|
18443
19256
|
|
|
18444
|
-
// src/domain/vaults/vault-service.ts
|
|
18445
|
-
import { createHash as createHash6 } from "crypto";
|
|
18446
|
-
import {
|
|
18447
|
-
generatePassword as generatePassword2,
|
|
18448
|
-
isValidTotpInput as isValidTotpInput3,
|
|
18449
|
-
parseSecretReference,
|
|
18450
|
-
parseTotpInput as parseTotpInput2,
|
|
18451
|
-
secretNameKey as secretNameKey3,
|
|
18452
|
-
SecretReferenceError as SecretReferenceError2,
|
|
18453
|
-
totpCodeSync,
|
|
18454
|
-
totpRemainingMs,
|
|
18455
|
-
systemSecretClock,
|
|
18456
|
-
VAULT_LIMITS as VAULT_LIMITS3
|
|
18457
|
-
} from "@opengeni/contracts";
|
|
18458
|
-
import {
|
|
18459
|
-
archiveVaultItem,
|
|
18460
|
-
createVault,
|
|
18461
|
-
decryptEnvironmentValue as decryptEnvironmentValue3,
|
|
18462
|
-
deleteVault,
|
|
18463
|
-
deleteVaultItemPermanently,
|
|
18464
|
-
encryptEnvironmentValue,
|
|
18465
|
-
ensureDefaultVault,
|
|
18466
|
-
ensureTaskVault,
|
|
18467
|
-
getSession as getSession6,
|
|
18468
|
-
getSessionSecretsView,
|
|
18469
|
-
getVault,
|
|
18470
|
-
getVaultItem,
|
|
18471
|
-
listVaultFieldVersions,
|
|
18472
|
-
listVaultItems,
|
|
18473
|
-
listVaultsForSubject as listVaultsForSubject2,
|
|
18474
|
-
vaultItemCursor,
|
|
18475
|
-
moveVaultItem,
|
|
18476
|
-
readVaultFieldCiphertext,
|
|
18477
|
-
resolveSecretReferenceCandidates as resolveSecretReferenceCandidates2,
|
|
18478
|
-
restoreVaultItem,
|
|
18479
|
-
setSessionVaultPolicy,
|
|
18480
|
-
updateVault,
|
|
18481
|
-
upsertVaultItem,
|
|
18482
|
-
VaultError as VaultError2
|
|
18483
|
-
} from "@opengeni/db";
|
|
18484
|
-
import {
|
|
18485
|
-
createProviderCallBudget as createProviderCallBudget2,
|
|
18486
|
-
EMPTY_VAULT_PROVIDER_REGISTRY as EMPTY_VAULT_PROVIDER_REGISTRY2,
|
|
18487
|
-
isVaultProviderError as isVaultProviderError4
|
|
18488
|
-
} from "@opengeni/vaults";
|
|
18489
|
-
|
|
18490
19257
|
// src/domain/vaults/provider-writes.ts
|
|
19258
|
+
import { randomUUID } from "crypto";
|
|
18491
19259
|
import {
|
|
18492
19260
|
generatePassword,
|
|
18493
19261
|
isValidTotpInput as isValidTotpInput2,
|
|
18494
|
-
secretNameKey as
|
|
19262
|
+
secretNameKey as secretNameKey3
|
|
18495
19263
|
} from "@opengeni/contracts";
|
|
18496
19264
|
import {
|
|
18497
19265
|
getProviderVaultItemRef,
|
|
18498
19266
|
VaultError
|
|
18499
19267
|
} from "@opengeni/db";
|
|
18500
19268
|
import {
|
|
18501
|
-
isVaultProviderError as isVaultProviderError3
|
|
19269
|
+
isVaultProviderError as isVaultProviderError3,
|
|
19270
|
+
providerOperations as providerOperations3,
|
|
19271
|
+
readProviderItemInventory as readProviderItemInventory2
|
|
18502
19272
|
} from "@opengeni/vaults";
|
|
18503
19273
|
var TEXT_FIELD_NAMES = ["username", "user", "email", "login", "account"];
|
|
18504
19274
|
function defaultKind(name) {
|
|
18505
|
-
return TEXT_FIELD_NAMES.includes(
|
|
19275
|
+
return TEXT_FIELD_NAMES.includes(secretNameKey3(name)) ? "text" : "concealed";
|
|
18506
19276
|
}
|
|
18507
19277
|
function assertProviderVaultWritable(vault) {
|
|
18508
19278
|
if (vault.source === "opengeni") {
|
|
@@ -18515,11 +19285,43 @@ function assertProviderVaultWritable(vault) {
|
|
|
18515
19285
|
throw new VaultError("vault_not_found", "vault not found", { vaultId: vault.id });
|
|
18516
19286
|
}
|
|
18517
19287
|
}
|
|
18518
|
-
|
|
18519
|
-
|
|
18520
|
-
|
|
19288
|
+
var NO_PROVIDER_OPERATIONS = withoutProviderWrites({
|
|
19289
|
+
createItemTypes: [],
|
|
19290
|
+
updateItem: false,
|
|
19291
|
+
removeField: false,
|
|
19292
|
+
archiveItem: false,
|
|
19293
|
+
archiveKind: "none",
|
|
19294
|
+
deleteItem: false,
|
|
19295
|
+
restoreItem: false,
|
|
19296
|
+
readFiles: false,
|
|
19297
|
+
writeFiles: false,
|
|
19298
|
+
createTotp: false,
|
|
19299
|
+
updateTotp: false,
|
|
19300
|
+
createVault: false,
|
|
19301
|
+
restoreFieldHistory: false
|
|
19302
|
+
});
|
|
19303
|
+
function effectiveProviderOperations(vault, provider) {
|
|
19304
|
+
if (vault.source === "opengeni") return NO_PROVIDER_OPERATIONS;
|
|
19305
|
+
const stored2 = storedProviderOperations(vault);
|
|
19306
|
+
const live = provider ? providerOperations3(provider.capabilities()) : null;
|
|
19307
|
+
const operations = live && stored2 ? intersectProviderOperations(live, stored2) : stored2 ?? live;
|
|
19308
|
+
return operations ? providerOperationsForMode(operations, vault.mode) : NO_PROVIDER_OPERATIONS;
|
|
19309
|
+
}
|
|
19310
|
+
function refuse(vault, operation, message, details = {}) {
|
|
19311
|
+
throw new VaultError("vault_read_only", message, { vaultId: vault.id, operation, ...details });
|
|
19312
|
+
}
|
|
19313
|
+
function refuseProviderOperation(vault, operation) {
|
|
19314
|
+
const messages = {
|
|
19315
|
+
deleteItem: `Opengeni never permanently deletes items in ${vault.name}. Archive the item, or delete it in the provider itself.`,
|
|
19316
|
+
restoreItem: `Items archived in ${vault.name} are restored in the provider itself; the restored item appears after the next sync.`,
|
|
19317
|
+
restoreFieldHistory: `${vault.name} keeps field history in the provider; restore an earlier value there.`
|
|
19318
|
+
};
|
|
19319
|
+
return refuse(vault, operation, messages[operation]);
|
|
19320
|
+
}
|
|
19321
|
+
function planFieldWrites(vault, operations, creating, fields, existing) {
|
|
19322
|
+
return fields.map((input) => {
|
|
18521
19323
|
const current = existing.find(
|
|
18522
|
-
(field) => input.fieldId !== void 0 && field.id === input.fieldId ||
|
|
19324
|
+
(field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey3(field.name) === secretNameKey3(input.name)
|
|
18523
19325
|
);
|
|
18524
19326
|
const kind = input.kind ?? current?.kind ?? defaultKind(input.name);
|
|
18525
19327
|
const base = {
|
|
@@ -18527,24 +19329,51 @@ async function providerFieldWrites(fields, existing, resolveReference, generated
|
|
|
18527
19329
|
name: input.name,
|
|
18528
19330
|
kind
|
|
18529
19331
|
};
|
|
18530
|
-
if (input.remove) {
|
|
18531
|
-
writes.push({ ...base, remove: true });
|
|
18532
|
-
continue;
|
|
18533
|
-
}
|
|
18534
19332
|
if (input.restoreVersion !== void 0) {
|
|
18535
|
-
|
|
18536
|
-
"provider_unavailable",
|
|
18537
|
-
"connected vaults keep their history in the provider; restore it there",
|
|
18538
|
-
{ field: input.name }
|
|
18539
|
-
);
|
|
19333
|
+
return refuseProviderOperation(vault, "restoreFieldHistory");
|
|
18540
19334
|
}
|
|
18541
|
-
if (input.fileBase64 !== void 0 || kind === "file") {
|
|
19335
|
+
if (input.fileBase64 !== void 0 || kind === "file" || current?.kind === "file") {
|
|
18542
19336
|
throw new VaultError(
|
|
18543
19337
|
"provider_unavailable",
|
|
18544
19338
|
"files in connected vaults are read-only in Opengeni",
|
|
18545
|
-
{ field: input.name }
|
|
19339
|
+
{ field: input.name, vaultId: vault.id, operation: "writeFiles" }
|
|
18546
19340
|
);
|
|
18547
19341
|
}
|
|
19342
|
+
const totp = kind === "totp" || current?.kind === "totp";
|
|
19343
|
+
if (input.remove) {
|
|
19344
|
+
if (!operations.removeField) {
|
|
19345
|
+
refuse(vault, "removeField", `${vault.name} does not allow removing fields here`, {
|
|
19346
|
+
field: input.name
|
|
19347
|
+
});
|
|
19348
|
+
}
|
|
19349
|
+
if (totp && !operations.updateTotp) {
|
|
19350
|
+
refuse(vault, "updateTotp", `${vault.name} does not allow changing one-time passwords`, {
|
|
19351
|
+
field: input.name
|
|
19352
|
+
});
|
|
19353
|
+
}
|
|
19354
|
+
return { input, base, kind };
|
|
19355
|
+
}
|
|
19356
|
+
if (totp) {
|
|
19357
|
+
const allowed = creating ? operations.createTotp : operations.updateTotp;
|
|
19358
|
+
if (!allowed) {
|
|
19359
|
+
refuse(
|
|
19360
|
+
vault,
|
|
19361
|
+
creating ? "createTotp" : "updateTotp",
|
|
19362
|
+
`${vault.name} does not allow ${creating ? "saving" : "changing"} one-time passwords`,
|
|
19363
|
+
{ field: input.name }
|
|
19364
|
+
);
|
|
19365
|
+
}
|
|
19366
|
+
}
|
|
19367
|
+
return { input, base, kind };
|
|
19368
|
+
});
|
|
19369
|
+
}
|
|
19370
|
+
async function providerFieldValues(planned, resolveReference, generated) {
|
|
19371
|
+
const writes = [];
|
|
19372
|
+
for (const { input, base, kind } of planned) {
|
|
19373
|
+
if (input.remove) {
|
|
19374
|
+
writes.push({ ...base, remove: true });
|
|
19375
|
+
continue;
|
|
19376
|
+
}
|
|
18548
19377
|
let value;
|
|
18549
19378
|
if (input.generate !== void 0) {
|
|
18550
19379
|
value = generatePassword(input.generate);
|
|
@@ -18574,6 +19403,16 @@ function providerFailure(error) {
|
|
|
18574
19403
|
}
|
|
18575
19404
|
throw error;
|
|
18576
19405
|
}
|
|
19406
|
+
function mutationFailure(vault, error, what) {
|
|
19407
|
+
if (isVaultProviderError3(error) && error.code !== "provider_outcome_unknown") {
|
|
19408
|
+
return providerFailure(error);
|
|
19409
|
+
}
|
|
19410
|
+
throw new VaultError(
|
|
19411
|
+
"provider_outcome_unknown",
|
|
19412
|
+
`${vault.name} did not confirm ${what}. It may have been applied: check the vault before trying again.`,
|
|
19413
|
+
{ vaultId: vault.id }
|
|
19414
|
+
);
|
|
19415
|
+
}
|
|
18577
19416
|
async function providerFor(deps, vault) {
|
|
18578
19417
|
try {
|
|
18579
19418
|
return await deps.providers.forVault({
|
|
@@ -18584,38 +19423,68 @@ async function providerFor(deps, vault) {
|
|
|
18584
19423
|
return providerFailure(error);
|
|
18585
19424
|
}
|
|
18586
19425
|
}
|
|
19426
|
+
function mutationContext(deps, vault) {
|
|
19427
|
+
return {
|
|
19428
|
+
connectionId: vault.providerConnectionId,
|
|
19429
|
+
budget: deps.budget ?? { take() {
|
|
19430
|
+
} },
|
|
19431
|
+
cacheEpoch: vault.providerCacheEpoch ?? null,
|
|
19432
|
+
// Read-before-write must see the provider's current item, never a cached copy.
|
|
19433
|
+
forceRefresh: true,
|
|
19434
|
+
idempotencyKey: randomUUID()
|
|
19435
|
+
};
|
|
19436
|
+
}
|
|
19437
|
+
async function refreshAfterWrite(deps, vault) {
|
|
19438
|
+
try {
|
|
19439
|
+
const result = await syncLinkedProviderVault({
|
|
19440
|
+
db: deps.db,
|
|
19441
|
+
context: deps.context,
|
|
19442
|
+
vaultId: vault.id,
|
|
19443
|
+
providers: deps.providers,
|
|
19444
|
+
force: true,
|
|
19445
|
+
...deps.budget ? { budget: deps.budget } : {}
|
|
19446
|
+
});
|
|
19447
|
+
return result.status;
|
|
19448
|
+
} catch {
|
|
19449
|
+
return "error";
|
|
19450
|
+
}
|
|
19451
|
+
}
|
|
18587
19452
|
async function refreshAndFind(deps, vault, providerItemRef) {
|
|
18588
|
-
await
|
|
18589
|
-
db: deps.db,
|
|
18590
|
-
context: deps.context,
|
|
18591
|
-
vaultId: vault.id,
|
|
18592
|
-
providers: deps.providers,
|
|
18593
|
-
force: true,
|
|
18594
|
-
...deps.budget ? { budget: deps.budget } : {}
|
|
18595
|
-
});
|
|
19453
|
+
const sync = await refreshAfterWrite(deps, vault);
|
|
18596
19454
|
try {
|
|
18597
19455
|
const ref = await getProviderVaultItemRef(deps.db, deps.context, {
|
|
18598
19456
|
vaultId: vault.id,
|
|
18599
19457
|
providerItemRef
|
|
18600
19458
|
});
|
|
18601
|
-
return ref.itemId;
|
|
19459
|
+
return { status: "listed", itemId: ref.itemId };
|
|
18602
19460
|
} catch (error) {
|
|
18603
19461
|
if (error instanceof VaultError && error.code === "vault_item_not_found") {
|
|
18604
|
-
|
|
18605
|
-
"provider_unavailable",
|
|
18606
|
-
"saved in the provider; Opengeni will show it after the next sync",
|
|
18607
|
-
{ vaultId: vault.id }
|
|
18608
|
-
);
|
|
19462
|
+
return { status: "saved_not_listed", vaultId: vault.id, sync };
|
|
18609
19463
|
}
|
|
18610
19464
|
throw error;
|
|
18611
19465
|
}
|
|
18612
19466
|
}
|
|
18613
|
-
async function
|
|
19467
|
+
async function writeProviderVaultItemOutcome(deps, input) {
|
|
18614
19468
|
const { vault, existing, change } = input;
|
|
18615
19469
|
assertProviderVaultWritable(vault);
|
|
18616
|
-
|
|
19470
|
+
const type = change.type ?? existing?.type ?? "login";
|
|
19471
|
+
if (type === "passkey") {
|
|
18617
19472
|
throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
|
|
18618
19473
|
}
|
|
19474
|
+
const provider = await providerFor(deps, vault);
|
|
19475
|
+
const operations = effectiveProviderOperations(vault, provider);
|
|
19476
|
+
if (existing) {
|
|
19477
|
+
if (!operations.updateItem) {
|
|
19478
|
+
refuse(vault, "updateItem", `${vault.name} does not allow changing items here`);
|
|
19479
|
+
}
|
|
19480
|
+
} else if (!operations.createItemTypes.includes(type)) {
|
|
19481
|
+
refuse(
|
|
19482
|
+
vault,
|
|
19483
|
+
"createItem",
|
|
19484
|
+
operations.createItemTypes.length === 0 ? `${vault.name} does not allow creating items here` : `${vault.name} cannot hold ${type} items; it accepts ${operations.createItemTypes.join(", ")}`,
|
|
19485
|
+
{ itemType: type, allowedItemTypes: operations.createItemTypes }
|
|
19486
|
+
);
|
|
19487
|
+
}
|
|
18619
19488
|
let externalItemId;
|
|
18620
19489
|
let expectedVersion;
|
|
18621
19490
|
if (existing) {
|
|
@@ -18632,60 +19501,190 @@ async function writeProviderVaultItem(deps, input) {
|
|
|
18632
19501
|
externalItemId = ref.providerItemRef;
|
|
18633
19502
|
expectedVersion = ref.providerVersion;
|
|
18634
19503
|
}
|
|
18635
|
-
const
|
|
19504
|
+
const planned = planFieldWrites(
|
|
19505
|
+
vault,
|
|
19506
|
+
operations,
|
|
19507
|
+
existing === null,
|
|
18636
19508
|
change.fields ?? [],
|
|
18637
|
-
existing?.fields ?? []
|
|
18638
|
-
deps.resolveReference,
|
|
18639
|
-
input.generated ?? []
|
|
19509
|
+
existing?.fields ?? []
|
|
18640
19510
|
);
|
|
18641
|
-
const
|
|
19511
|
+
const fields = await providerFieldValues(planned, deps.resolveReference, input.generated ?? []);
|
|
19512
|
+
const name = change.name ?? existing?.name ?? "";
|
|
18642
19513
|
let written;
|
|
18643
19514
|
try {
|
|
18644
|
-
written = await provider.writeItem(
|
|
18645
|
-
{
|
|
18646
|
-
|
|
18647
|
-
|
|
19515
|
+
written = await provider.writeItem(mutationContext(deps, vault), vault.providerVaultRef ?? "", {
|
|
19516
|
+
...externalItemId ? { externalItemId, expectedVersion: expectedVersion ?? null } : {},
|
|
19517
|
+
name,
|
|
19518
|
+
type,
|
|
19519
|
+
websites: change.websites ?? existing?.websites ?? [],
|
|
19520
|
+
fields
|
|
19521
|
+
});
|
|
19522
|
+
} catch (error) {
|
|
19523
|
+
return mutationFailure(vault, error, existing ? `the change to ${name}` : `saving ${name}`);
|
|
19524
|
+
}
|
|
19525
|
+
return await refreshAndFind(deps, vault, written.externalItemId);
|
|
19526
|
+
}
|
|
19527
|
+
function savedNotListedError(vault, itemName, outcome) {
|
|
19528
|
+
return new VaultError(
|
|
19529
|
+
"provider_inventory_incomplete",
|
|
19530
|
+
`Saved ${itemName} in ${vault.name}, but Opengeni could not list it yet. Do not save it again: it appears after the next successful sync.`,
|
|
19531
|
+
{ saved: true, vaultId: outcome.vaultId, itemName, sync: outcome.sync }
|
|
19532
|
+
);
|
|
19533
|
+
}
|
|
19534
|
+
async function writeProviderVaultItem(deps, input) {
|
|
19535
|
+
const outcome = await writeProviderVaultItemOutcome(deps, input);
|
|
19536
|
+
if (outcome.status === "listed") return outcome.itemId;
|
|
19537
|
+
throw savedNotListedError(
|
|
19538
|
+
input.vault,
|
|
19539
|
+
input.change.name ?? input.existing?.name ?? "the item",
|
|
19540
|
+
outcome
|
|
19541
|
+
);
|
|
19542
|
+
}
|
|
19543
|
+
function providerArchiveKind(vault, provider) {
|
|
19544
|
+
const operations = effectiveProviderOperations(vault, provider);
|
|
19545
|
+
if (!operations.archiveItem) return null;
|
|
19546
|
+
return operations.archiveKind === "archive" || operations.archiveKind === "trash" ? operations.archiveKind : null;
|
|
19547
|
+
}
|
|
19548
|
+
async function assertProviderVaultAccepts(deps, vault, item) {
|
|
19549
|
+
assertProviderVaultWritable(vault);
|
|
19550
|
+
if (item.type === "passkey") {
|
|
19551
|
+
throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
|
|
19552
|
+
}
|
|
19553
|
+
if (item.fields.some((field) => field.kind === "file")) {
|
|
19554
|
+
throw new VaultError(
|
|
19555
|
+
"provider_unavailable",
|
|
19556
|
+
"files in connected vaults are read-only in Opengeni; copy the item into an Opengeni vault instead",
|
|
19557
|
+
{ vaultId: vault.id, operation: "writeFiles" }
|
|
19558
|
+
);
|
|
19559
|
+
}
|
|
19560
|
+
const operations = effectiveProviderOperations(vault, await providerFor(deps, vault));
|
|
19561
|
+
if (!operations.createItemTypes.includes(item.type)) {
|
|
19562
|
+
refuse(
|
|
19563
|
+
vault,
|
|
19564
|
+
"createItem",
|
|
19565
|
+
operations.createItemTypes.length === 0 ? `${vault.name} does not allow creating items here` : `${vault.name} cannot hold ${item.type} items; it accepts ${operations.createItemTypes.join(", ")}`,
|
|
19566
|
+
{ itemType: item.type, allowedItemTypes: operations.createItemTypes }
|
|
19567
|
+
);
|
|
19568
|
+
}
|
|
19569
|
+
if (item.fields.some((field) => field.kind === "totp") && !operations.createTotp) {
|
|
19570
|
+
refuse(vault, "createTotp", `${vault.name} does not allow saving one-time passwords`);
|
|
19571
|
+
}
|
|
19572
|
+
}
|
|
19573
|
+
async function assertProviderVaultReleases(deps, vault) {
|
|
19574
|
+
assertProviderVaultWritable(vault);
|
|
19575
|
+
const archiveKind = providerArchiveKind(vault, await providerFor(deps, vault));
|
|
19576
|
+
if (archiveKind === null) {
|
|
19577
|
+
refuse(
|
|
19578
|
+
vault,
|
|
19579
|
+
"archiveItem",
|
|
19580
|
+
`${vault.name} cannot archive the original recoverably, so the item cannot be moved out of it; copy it instead`
|
|
19581
|
+
);
|
|
19582
|
+
}
|
|
19583
|
+
return archiveKind;
|
|
19584
|
+
}
|
|
19585
|
+
async function observeProviderItemVersion(deps, input) {
|
|
19586
|
+
const { vault } = input;
|
|
19587
|
+
const provider = await providerFor(deps, vault);
|
|
19588
|
+
try {
|
|
19589
|
+
const inventory = await readProviderItemInventory2(
|
|
19590
|
+
provider,
|
|
18648
19591
|
{
|
|
18649
|
-
|
|
18650
|
-
|
|
18651
|
-
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
}
|
|
19592
|
+
connectionId: vault.providerConnectionId,
|
|
19593
|
+
budget: deps.budget ?? { take() {
|
|
19594
|
+
} },
|
|
19595
|
+
cacheEpoch: vault.providerCacheEpoch ?? null,
|
|
19596
|
+
forceRefresh: true
|
|
19597
|
+
},
|
|
19598
|
+
vault.providerVaultRef ?? ""
|
|
18655
19599
|
);
|
|
19600
|
+
const item = inventory.items.find((meta) => meta.externalItemId === input.providerItemRef);
|
|
19601
|
+
if (!item) return { listed: false, consistent: inventory.consistent };
|
|
19602
|
+
return {
|
|
19603
|
+
listed: true,
|
|
19604
|
+
version: typeof item.version === "string" && item.version.length > 0 ? item.version : null
|
|
19605
|
+
};
|
|
18656
19606
|
} catch (error) {
|
|
18657
|
-
|
|
19607
|
+
if (isVaultProviderError3(error)) {
|
|
19608
|
+
throw new VaultError(vaultErrorCodeForProvider(error.code, "vault"), error.message, {
|
|
19609
|
+
vaultId: vault.id,
|
|
19610
|
+
...error.retryAfterMs ? { retryAfterMs: error.retryAfterMs } : {}
|
|
19611
|
+
});
|
|
19612
|
+
}
|
|
19613
|
+
throw error;
|
|
18658
19614
|
}
|
|
18659
|
-
return await refreshAndFind(deps, vault, written.externalItemId);
|
|
18660
19615
|
}
|
|
18661
19616
|
async function archiveProviderVaultItem(deps, input) {
|
|
18662
|
-
|
|
19617
|
+
const { vault } = input;
|
|
19618
|
+
assertProviderVaultWritable(vault);
|
|
18663
19619
|
const ref = await getProviderVaultItemRef(deps.db, deps.context, {
|
|
18664
|
-
vaultId:
|
|
19620
|
+
vaultId: vault.id,
|
|
18665
19621
|
itemId: input.itemId
|
|
18666
19622
|
});
|
|
18667
|
-
const provider = await providerFor(deps,
|
|
19623
|
+
const provider = await providerFor(deps, vault);
|
|
19624
|
+
const archiveKind = providerArchiveKind(vault, provider);
|
|
19625
|
+
if (archiveKind === null) {
|
|
19626
|
+
refuse(vault, "archiveItem", `${vault.name} does not allow archiving items here`);
|
|
19627
|
+
}
|
|
18668
19628
|
try {
|
|
18669
19629
|
await provider.archiveItem(
|
|
18670
|
-
|
|
18671
|
-
|
|
18672
|
-
input.vault.providerVaultRef ?? "",
|
|
19630
|
+
mutationContext(deps, vault),
|
|
19631
|
+
vault.providerVaultRef ?? "",
|
|
18673
19632
|
ref.providerItemRef
|
|
18674
19633
|
);
|
|
18675
19634
|
} catch (error) {
|
|
18676
|
-
|
|
19635
|
+
mutationFailure(vault, error, "archiving the item");
|
|
18677
19636
|
}
|
|
18678
|
-
await
|
|
18679
|
-
|
|
18680
|
-
context: deps.context,
|
|
18681
|
-
vaultId: input.vault.id,
|
|
18682
|
-
providers: deps.providers,
|
|
18683
|
-
force: true,
|
|
18684
|
-
...deps.budget ? { budget: deps.budget } : {}
|
|
18685
|
-
});
|
|
19637
|
+
await refreshAfterWrite(deps, vault);
|
|
19638
|
+
return { archiveKind };
|
|
18686
19639
|
}
|
|
18687
19640
|
|
|
18688
19641
|
// src/domain/vaults/vault-service.ts
|
|
19642
|
+
import { createHash as createHash6 } from "crypto";
|
|
19643
|
+
import {
|
|
19644
|
+
generatePassword as generatePassword2,
|
|
19645
|
+
isValidTotpInput as isValidTotpInput3,
|
|
19646
|
+
parseSecretReference,
|
|
19647
|
+
parseTotpInput as parseTotpInput2,
|
|
19648
|
+
secretNameKey as secretNameKey4,
|
|
19649
|
+
SecretReferenceError as SecretReferenceError2,
|
|
19650
|
+
totpCodeSync,
|
|
19651
|
+
totpRemainingMs,
|
|
19652
|
+
systemSecretClock,
|
|
19653
|
+
VAULT_LIMITS as VAULT_LIMITS3
|
|
19654
|
+
} from "@opengeni/contracts";
|
|
19655
|
+
import {
|
|
19656
|
+
archiveVaultItem,
|
|
19657
|
+
createVault,
|
|
19658
|
+
decryptEnvironmentValue as decryptEnvironmentValue3,
|
|
19659
|
+
deleteVault,
|
|
19660
|
+
deleteVaultItemPermanently,
|
|
19661
|
+
encryptEnvironmentValue as encryptEnvironmentValue2,
|
|
19662
|
+
ensureDefaultVault,
|
|
19663
|
+
ensureTaskVault,
|
|
19664
|
+
getSession as getSession6,
|
|
19665
|
+
getSessionSecretsView,
|
|
19666
|
+
getProviderVaultItemRef as getProviderVaultItemRef2,
|
|
19667
|
+
getVault,
|
|
19668
|
+
getVaultItem,
|
|
19669
|
+
isVaultError as isVaultError2,
|
|
19670
|
+
listVaultFieldVersions,
|
|
19671
|
+
listVaultItems,
|
|
19672
|
+
listVaultsForSubject as listVaultsForSubject2,
|
|
19673
|
+
vaultItemCursor,
|
|
19674
|
+
moveVaultItem,
|
|
19675
|
+
readVaultFieldCiphertext,
|
|
19676
|
+
resolveSecretReferenceCandidates as resolveSecretReferenceCandidates2,
|
|
19677
|
+
restoreVaultItem,
|
|
19678
|
+
setSessionVaultPolicy,
|
|
19679
|
+
updateVault,
|
|
19680
|
+
upsertVaultItem,
|
|
19681
|
+
VaultError as VaultError2
|
|
19682
|
+
} from "@opengeni/db";
|
|
19683
|
+
import {
|
|
19684
|
+
createProviderCallBudget as createProviderCallBudget2,
|
|
19685
|
+
EMPTY_VAULT_PROVIDER_REGISTRY as EMPTY_VAULT_PROVIDER_REGISTRY2,
|
|
19686
|
+
isVaultProviderError as isVaultProviderError4
|
|
19687
|
+
} from "@opengeni/vaults";
|
|
18689
19688
|
function subjectContext(actor, workspaceId) {
|
|
18690
19689
|
return {
|
|
18691
19690
|
kind: "subject",
|
|
@@ -18722,13 +19721,13 @@ function requireKey(deps) {
|
|
|
18722
19721
|
return deps.encryptionKey;
|
|
18723
19722
|
}
|
|
18724
19723
|
function defaultFieldKind(name) {
|
|
18725
|
-
return ["username", "user", "email", "login", "account", "rpid"].includes(
|
|
19724
|
+
return ["username", "user", "email", "login", "account", "rpid"].includes(secretNameKey4(name)) ? "text" : "concealed";
|
|
18726
19725
|
}
|
|
18727
19726
|
function createVaultService(deps) {
|
|
18728
19727
|
const clock = deps.clock ?? systemSecretClock;
|
|
18729
19728
|
async function fieldWrite(input, existing, resolveReference, generated) {
|
|
18730
19729
|
const current = existing?.fields.find(
|
|
18731
|
-
(field) => input.fieldId !== void 0 && field.id === input.fieldId ||
|
|
19730
|
+
(field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey4(field.name) === secretNameKey4(input.name)
|
|
18732
19731
|
);
|
|
18733
19732
|
const name = current?.name ?? input.name;
|
|
18734
19733
|
const write = { name };
|
|
@@ -18781,7 +19780,7 @@ function createVaultService(deps) {
|
|
|
18781
19780
|
return {
|
|
18782
19781
|
...write,
|
|
18783
19782
|
kind,
|
|
18784
|
-
valueEncrypted:
|
|
19783
|
+
valueEncrypted: encryptEnvironmentValue2(requireKey(deps), plaintext),
|
|
18785
19784
|
valueSize: size
|
|
18786
19785
|
};
|
|
18787
19786
|
}
|
|
@@ -18848,6 +19847,11 @@ function createVaultService(deps) {
|
|
|
18848
19847
|
function key() {
|
|
18849
19848
|
return deps.encryptionKey;
|
|
18850
19849
|
}
|
|
19850
|
+
async function refuseForConnectedItem(context, itemId, operation) {
|
|
19851
|
+
const item = await getVaultItem(deps.db, context, { itemId });
|
|
19852
|
+
if (item.vaultSource === "opengeni") return;
|
|
19853
|
+
refuseProviderOperation(await getVault(deps.db, context, { vaultId: item.vaultId }), operation);
|
|
19854
|
+
}
|
|
18851
19855
|
async function moveAcrossProviders(input) {
|
|
18852
19856
|
const source = await getVaultItem(deps.db, input.context, { itemId: input.itemId });
|
|
18853
19857
|
const target = await getVault(deps.db, input.context, { vaultId: input.targetVaultId });
|
|
@@ -18873,6 +19877,46 @@ function createVaultService(deps) {
|
|
|
18873
19877
|
}
|
|
18874
19878
|
);
|
|
18875
19879
|
}
|
|
19880
|
+
if (target.source !== "opengeni") {
|
|
19881
|
+
await assertProviderVaultAccepts(input.providerWrites, target, source);
|
|
19882
|
+
}
|
|
19883
|
+
if (input.mode === "move" && source.vaultSource !== "opengeni") {
|
|
19884
|
+
await assertProviderVaultReleases(input.providerWrites, sourceVault);
|
|
19885
|
+
}
|
|
19886
|
+
let pinned = null;
|
|
19887
|
+
if (input.mode === "move" && source.vaultSource !== "opengeni") {
|
|
19888
|
+
const mirrored = await getProviderVaultItemRef2(deps.db, input.context, {
|
|
19889
|
+
vaultId: sourceVault.id,
|
|
19890
|
+
itemId: source.id
|
|
19891
|
+
});
|
|
19892
|
+
const { providerItemRef } = mirrored;
|
|
19893
|
+
const observed = await observeProviderItemVersion(input.providerWrites, {
|
|
19894
|
+
vault: sourceVault,
|
|
19895
|
+
providerItemRef
|
|
19896
|
+
});
|
|
19897
|
+
if (!observed.listed) {
|
|
19898
|
+
throw new VaultError2(
|
|
19899
|
+
"vault_item_not_found",
|
|
19900
|
+
`${source.name} is not listed in ${sourceVault.name} right now; sync the vault and try again`,
|
|
19901
|
+
{ itemId: source.id, vaultId: sourceVault.id }
|
|
19902
|
+
);
|
|
19903
|
+
}
|
|
19904
|
+
if (observed.version !== null) {
|
|
19905
|
+
if (mirrored.providerVersion !== observed.version || mirrored.generation !== source.generation) {
|
|
19906
|
+
throw new VaultError2(
|
|
19907
|
+
"provider_conflict",
|
|
19908
|
+
`${source.name} changed in ${sourceVault.name} since Opengeni last synced it, so its fields here may be out of date. Nothing was moved: sync ${sourceVault.name}, then move it again.`,
|
|
19909
|
+
{
|
|
19910
|
+
itemId: source.id,
|
|
19911
|
+
vaultId: sourceVault.id,
|
|
19912
|
+
mirroredVersion: mirrored.providerVersion,
|
|
19913
|
+
providerVersion: observed.version
|
|
19914
|
+
}
|
|
19915
|
+
);
|
|
19916
|
+
}
|
|
19917
|
+
pinned = { providerItemRef, version: observed.version };
|
|
19918
|
+
}
|
|
19919
|
+
}
|
|
18876
19920
|
const name = input.newName ?? source.name;
|
|
18877
19921
|
const fields = [];
|
|
18878
19922
|
for (const field of source.fields) {
|
|
@@ -18899,14 +19943,19 @@ function createVaultService(deps) {
|
|
|
18899
19943
|
...source.expiresAt ? { expiresAt: source.expiresAt } : {},
|
|
18900
19944
|
fields
|
|
18901
19945
|
};
|
|
18902
|
-
let created;
|
|
19946
|
+
let created = null;
|
|
19947
|
+
let notListed = null;
|
|
18903
19948
|
if (target.source !== "opengeni") {
|
|
18904
|
-
const
|
|
19949
|
+
const outcome = await writeProviderVaultItemOutcome(input.providerWrites, {
|
|
18905
19950
|
vault: target,
|
|
18906
19951
|
existing: null,
|
|
18907
19952
|
change
|
|
18908
19953
|
});
|
|
18909
|
-
|
|
19954
|
+
if (outcome.status === "listed") {
|
|
19955
|
+
created = await getVaultItem(deps.db, input.context, { itemId: outcome.itemId });
|
|
19956
|
+
} else {
|
|
19957
|
+
notListed = outcome;
|
|
19958
|
+
}
|
|
18910
19959
|
} else {
|
|
18911
19960
|
const write = await itemWrite(
|
|
18912
19961
|
change,
|
|
@@ -18921,17 +19970,54 @@ function createVaultService(deps) {
|
|
|
18921
19970
|
});
|
|
18922
19971
|
}
|
|
18923
19972
|
if (input.mode === "move") {
|
|
18924
|
-
|
|
18925
|
-
|
|
18926
|
-
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
19973
|
+
try {
|
|
19974
|
+
if (source.vaultSource !== "opengeni") {
|
|
19975
|
+
if (pinned) await assertSourceUnchanged(input.providerWrites, sourceVault, pinned);
|
|
19976
|
+
await archiveProviderVaultItem(input.providerWrites, {
|
|
19977
|
+
vault: sourceVault,
|
|
19978
|
+
itemId: source.id
|
|
19979
|
+
});
|
|
19980
|
+
} else {
|
|
19981
|
+
await archiveVaultItem(deps.db, input.context, { itemId: source.id });
|
|
19982
|
+
}
|
|
19983
|
+
} catch (error) {
|
|
19984
|
+
const changed = isVaultError2(error) && error.details?.sourceChanged === true;
|
|
19985
|
+
throw new VaultError2(
|
|
19986
|
+
isVaultError2(error) ? error.code : "provider_outcome_unknown",
|
|
19987
|
+
changed ? `Saved ${name} in ${target.name} with the values that were read, but the original in ${sourceVault.name} ${error.message}, so it was kept. Do not move it again: compare the two, then update the copy or archive the original in ${sourceVault.name}.` : `Saved ${name} in ${target.name}, but archiving the original in ${sourceVault.name} did not complete${isVaultError2(error) ? ` (${error.message})` : ""}. Do not move it again: check ${sourceVault.name} and archive the original there if it is still listed.`,
|
|
19988
|
+
{
|
|
19989
|
+
saved: true,
|
|
19990
|
+
sourceArchived: false,
|
|
19991
|
+
...changed ? { sourceChanged: true } : {},
|
|
19992
|
+
vaultId: target.id,
|
|
19993
|
+
...created ? { itemId: created.id } : {},
|
|
19994
|
+
sourceItemId: source.id,
|
|
19995
|
+
sourceVaultId: sourceVault.id
|
|
19996
|
+
}
|
|
19997
|
+
);
|
|
18931
19998
|
}
|
|
18932
19999
|
}
|
|
20000
|
+
if (notListed) {
|
|
20001
|
+
const error = savedNotListedError(target, name, notListed);
|
|
20002
|
+
throw new VaultError2(error.code, error.message, {
|
|
20003
|
+
...error.details,
|
|
20004
|
+
...input.mode === "move" ? { sourceArchived: true } : {}
|
|
20005
|
+
});
|
|
20006
|
+
}
|
|
18933
20007
|
return created;
|
|
18934
20008
|
}
|
|
20009
|
+
async function assertSourceUnchanged(providerWrites, vault, pinned) {
|
|
20010
|
+
const now = await observeProviderItemVersion(providerWrites, {
|
|
20011
|
+
vault,
|
|
20012
|
+
providerItemRef: pinned.providerItemRef
|
|
20013
|
+
});
|
|
20014
|
+
if (now.listed && now.version === pinned.version) return;
|
|
20015
|
+
throw new VaultError2(
|
|
20016
|
+
now.listed ? "vault_generation_conflict" : "vault_item_not_found",
|
|
20017
|
+
now.listed ? "changed after its values were read" : "is no longer listed at the version that was read",
|
|
20018
|
+
{ sourceChanged: true, vaultId: vault.id }
|
|
20019
|
+
);
|
|
20020
|
+
}
|
|
18935
20021
|
function toItem(row) {
|
|
18936
20022
|
return toVaultItem(row, key());
|
|
18937
20023
|
}
|
|
@@ -18951,7 +20037,12 @@ function createVaultService(deps) {
|
|
|
18951
20037
|
});
|
|
18952
20038
|
const budget = createProviderCallBudget2(VAULT_LIMITS3.providerReadsPerAttempt);
|
|
18953
20039
|
const [value] = await provider.readFields(
|
|
18954
|
-
|
|
20040
|
+
// The durable epoch invalidates provider value caches after a refresh anywhere.
|
|
20041
|
+
{
|
|
20042
|
+
connectionId: read.providerConnectionId,
|
|
20043
|
+
budget,
|
|
20044
|
+
cacheEpoch: read.providerCacheEpoch ?? null
|
|
20045
|
+
},
|
|
18955
20046
|
read.providerVaultRef ?? "",
|
|
18956
20047
|
read.providerItemRef ?? "",
|
|
18957
20048
|
[read.providerFieldRef ?? ""]
|
|
@@ -19008,9 +20099,12 @@ function createVaultService(deps) {
|
|
|
19008
20099
|
const vaults = await listVaultsForSubject2(deps.db, principalContext(principal), {
|
|
19009
20100
|
workspaceId: principal.workspaceId
|
|
19010
20101
|
});
|
|
20102
|
+
return agentVaultFrom(vaults, nameOrId);
|
|
20103
|
+
}
|
|
20104
|
+
function agentVaultFrom(vaults, nameOrId) {
|
|
19011
20105
|
const byId = vaults.find((vault) => vault.id === nameOrId.toLowerCase());
|
|
19012
20106
|
if (byId) return byId;
|
|
19013
|
-
const matches = vaults.filter((vault) =>
|
|
20107
|
+
const matches = vaults.filter((vault) => secretNameKey4(vault.name) === secretNameKey4(nameOrId));
|
|
19014
20108
|
if (matches.length === 0) {
|
|
19015
20109
|
throw new VaultError2("vault_not_found", `no reachable vault is named ${nameOrId}`);
|
|
19016
20110
|
}
|
|
@@ -19237,12 +20331,14 @@ function createVaultService(deps) {
|
|
|
19237
20331
|
await archiveVaultItem(deps.db, context, { itemId });
|
|
19238
20332
|
},
|
|
19239
20333
|
async restoreItem(actor, workspaceId, itemId) {
|
|
19240
|
-
|
|
19241
|
-
|
|
19242
|
-
);
|
|
20334
|
+
const context = subjectContext(actor, workspaceId);
|
|
20335
|
+
await refuseForConnectedItem(context, itemId, "restoreItem");
|
|
20336
|
+
return toItem(await restoreVaultItem(deps.db, context, { itemId }));
|
|
19243
20337
|
},
|
|
19244
20338
|
async deleteItemPermanently(actor, workspaceId, itemId) {
|
|
19245
|
-
|
|
20339
|
+
const context = subjectContext(actor, workspaceId);
|
|
20340
|
+
await refuseForConnectedItem(context, itemId, "deleteItem");
|
|
20341
|
+
await deleteVaultItemPermanently(deps.db, context, { itemId });
|
|
19246
20342
|
},
|
|
19247
20343
|
async moveItem(actor, workspaceId, itemId, req) {
|
|
19248
20344
|
const crossed = await moveAcrossProviders({
|
|
@@ -19282,6 +20378,12 @@ function createVaultService(deps) {
|
|
|
19282
20378
|
async restoreFieldVersion(actor, workspaceId, itemId, fieldId, version) {
|
|
19283
20379
|
const context = subjectContext(actor, workspaceId);
|
|
19284
20380
|
const existing = await getVaultItem(deps.db, context, { itemId });
|
|
20381
|
+
if (existing.vaultSource !== "opengeni") {
|
|
20382
|
+
refuseProviderOperation(
|
|
20383
|
+
await getVault(deps.db, context, { vaultId: existing.vaultId }),
|
|
20384
|
+
"restoreFieldHistory"
|
|
20385
|
+
);
|
|
20386
|
+
}
|
|
19285
20387
|
const field = existing.fields.find((candidate) => candidate.id === fieldId);
|
|
19286
20388
|
if (!field) throw new VaultError2("vault_field_not_found", "field not found", { fieldId });
|
|
19287
20389
|
return toItem(
|
|
@@ -19318,26 +20420,37 @@ function createVaultService(deps) {
|
|
|
19318
20420
|
let reachable = await listVaultsForSubject2(deps.db, context, {
|
|
19319
20421
|
workspaceId: principal.workspaceId
|
|
19320
20422
|
});
|
|
19321
|
-
|
|
19322
|
-
|
|
20423
|
+
let scoped = input.vault !== void 0 ? [agentVaultFrom(reachable, input.vault)] : reachable;
|
|
20424
|
+
let refreshing = /* @__PURE__ */ new Set();
|
|
20425
|
+
const connected = scoped.filter((vault) => vault.source !== "opengeni");
|
|
20426
|
+
if (deps.providers && connected.length > 0) {
|
|
20427
|
+
const report = await refreshLinkedVaults({
|
|
19323
20428
|
db: deps.db,
|
|
19324
20429
|
context,
|
|
19325
|
-
vaults:
|
|
20430
|
+
vaults: connected,
|
|
19326
20431
|
providers: deps.providers,
|
|
19327
|
-
maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS
|
|
20432
|
+
maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS,
|
|
20433
|
+
...input.refresh && input.cursor === void 0 ? { force: true } : {}
|
|
19328
20434
|
});
|
|
19329
|
-
|
|
20435
|
+
refreshing = new Set(report.pending);
|
|
20436
|
+
if (report.outcomes.some((outcome) => outcome.state === "done" || outcome.state === "failed")) {
|
|
19330
20437
|
reachable = await listVaultsForSubject2(deps.db, context, {
|
|
19331
20438
|
workspaceId: principal.workspaceId
|
|
19332
20439
|
});
|
|
20440
|
+
scoped = scoped.map((vault) => reachable.find((row) => row.id === vault.id) ?? vault);
|
|
19333
20441
|
}
|
|
19334
20442
|
}
|
|
20443
|
+
const checkedAt = /* @__PURE__ */ new Date();
|
|
20444
|
+
const freshnessOf = (vault) => providerVaultFreshness(vault, checkedAt, { refreshing: refreshing.has(vault.id) });
|
|
20445
|
+
const sources = scoped.flatMap((vault) => {
|
|
20446
|
+
const freshness = freshnessOf(vault);
|
|
20447
|
+
return freshness ? [{ vaultId: vault.id, freshness }] : [];
|
|
20448
|
+
});
|
|
20449
|
+
const complete = sources.every((source) => source.freshness.state === "current");
|
|
19335
20450
|
const byTier = (vaults2) => [...vaults2].sort(
|
|
19336
20451
|
(left, right) => left.tier - right.tier || left.name.localeCompare(right.name)
|
|
19337
20452
|
);
|
|
19338
|
-
const vaults = byTier(
|
|
19339
|
-
input.vault !== void 0 ? [await agentVault(principal, input.vault)] : reachable
|
|
19340
|
-
);
|
|
20453
|
+
const vaults = byTier(scoped);
|
|
19341
20454
|
const limit = input.limit ?? 20;
|
|
19342
20455
|
const filters = agentSearchFilterKey(input);
|
|
19343
20456
|
let startIndex = 0;
|
|
@@ -19403,15 +20516,21 @@ function createVaultService(deps) {
|
|
|
19403
20516
|
truncated: next !== null,
|
|
19404
20517
|
nextCursor: next === null ? null : encodeAgentSearchCursor(next),
|
|
19405
20518
|
...input.cursor === void 0 ? {
|
|
19406
|
-
vaults: byTier(reachable).map((vault) =>
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
20519
|
+
vaults: byTier(reachable).map((vault) => {
|
|
20520
|
+
const freshness = freshnessOf(vault);
|
|
20521
|
+
return {
|
|
20522
|
+
id: vault.id,
|
|
20523
|
+
name: vault.name,
|
|
20524
|
+
ownerKind: vault.ownerKind,
|
|
20525
|
+
source: vault.source,
|
|
20526
|
+
mode: vault.mode,
|
|
20527
|
+
itemCount: vault.itemCount,
|
|
20528
|
+
...freshness ? { freshness } : {}
|
|
20529
|
+
};
|
|
20530
|
+
})
|
|
20531
|
+
} : {},
|
|
20532
|
+
complete,
|
|
20533
|
+
sources
|
|
19415
20534
|
};
|
|
19416
20535
|
function agentSearchResult(vault, row) {
|
|
19417
20536
|
const summary = toVaultItemSummary(row, key());
|
|
@@ -19446,7 +20565,7 @@ function createVaultService(deps) {
|
|
|
19446
20565
|
q: input.item,
|
|
19447
20566
|
limit: VAULT_LIMITS3.searchResultsMax
|
|
19448
20567
|
});
|
|
19449
|
-
const existing = existingPage.items.find((item) =>
|
|
20568
|
+
const existing = existingPage.items.find((item) => secretNameKey4(item.name) === secretNameKey4(input.item)) ?? null;
|
|
19450
20569
|
if (input.mode === "create" && existing) {
|
|
19451
20570
|
throw new VaultError2(
|
|
19452
20571
|
"vault_item_name_conflict",
|
|
@@ -19554,7 +20673,7 @@ function createVaultService(deps) {
|
|
|
19554
20673
|
const current = await getVaultItem(deps.db, context, { itemId: target.itemId });
|
|
19555
20674
|
if (current.vaultSource !== "opengeni") {
|
|
19556
20675
|
const vault = await getVault(deps.db, context, { vaultId: current.vaultId });
|
|
19557
|
-
await archiveProviderVaultItem(
|
|
20676
|
+
const { archiveKind } = await archiveProviderVaultItem(
|
|
19558
20677
|
{
|
|
19559
20678
|
db: deps.db,
|
|
19560
20679
|
context,
|
|
@@ -19565,7 +20684,7 @@ function createVaultService(deps) {
|
|
|
19565
20684
|
);
|
|
19566
20685
|
return {
|
|
19567
20686
|
archived: true,
|
|
19568
|
-
restoreHint: `A person can restore ${current.name} from the
|
|
20687
|
+
restoreHint: `A person can restore ${current.name} from the ${archiveKind === "trash" ? "trash" : "archive"} in ${vault.name}'s provider; it reappears here after the next sync.`
|
|
19569
20688
|
};
|
|
19570
20689
|
}
|
|
19571
20690
|
const row = await archiveVaultItem(deps.db, context, {
|
|
@@ -20040,39 +21159,39 @@ import {
|
|
|
20040
21159
|
AutomationSessionTemplate,
|
|
20041
21160
|
OPENGENI_PR_REVIEW_SESSION_ROLE
|
|
20042
21161
|
} from "@opengeni/contracts";
|
|
20043
|
-
import { z as
|
|
21162
|
+
import { z as z3 } from "zod";
|
|
20044
21163
|
var PR_REVIEW_AUTOMATION_ADAPTER_ID = "source-control.pull-request.v1";
|
|
20045
|
-
var PrReviewSourceConfiguration =
|
|
20046
|
-
provider:
|
|
20047
|
-
providerBaseUrl:
|
|
20048
|
-
registrationId:
|
|
20049
|
-
webhookUsername:
|
|
21164
|
+
var PrReviewSourceConfiguration = z3.object({
|
|
21165
|
+
provider: z3.enum(["github", "gitlab", "azure_devops"]),
|
|
21166
|
+
providerBaseUrl: z3.string().url().max(2048),
|
|
21167
|
+
registrationId: z3.string().uuid(),
|
|
21168
|
+
webhookUsername: z3.string().min(1).max(512).nullable().default(null)
|
|
20050
21169
|
}).strict();
|
|
20051
|
-
var PrReviewTriggerParameters =
|
|
20052
|
-
registrationId:
|
|
20053
|
-
repositoryBindingId:
|
|
20054
|
-
provider:
|
|
20055
|
-
repositoryUri:
|
|
20056
|
-
repositoryFullName:
|
|
20057
|
-
providerRepositoryId:
|
|
20058
|
-
installationId:
|
|
20059
|
-
projectId:
|
|
20060
|
-
model:
|
|
20061
|
-
additionalInstructions:
|
|
21170
|
+
var PrReviewTriggerParameters = z3.object({
|
|
21171
|
+
registrationId: z3.string().uuid(),
|
|
21172
|
+
repositoryBindingId: z3.string().uuid(),
|
|
21173
|
+
provider: z3.enum(["github", "gitlab", "azure_devops"]),
|
|
21174
|
+
repositoryUri: z3.string().url().max(2048),
|
|
21175
|
+
repositoryFullName: z3.string().min(1).max(1024),
|
|
21176
|
+
providerRepositoryId: z3.string().min(1).max(512),
|
|
21177
|
+
installationId: z3.string().min(1).max(512).nullable(),
|
|
21178
|
+
projectId: z3.string().min(1).max(512).nullable(),
|
|
21179
|
+
model: z3.string().min(1).max(512).nullable(),
|
|
21180
|
+
additionalInstructions: z3.string().max(16384).nullable()
|
|
20062
21181
|
}).strict();
|
|
20063
|
-
var PrReviewEventPayload =
|
|
20064
|
-
provider:
|
|
20065
|
-
eventName:
|
|
20066
|
-
action:
|
|
20067
|
-
providerRepositoryId:
|
|
20068
|
-
installationId:
|
|
20069
|
-
projectId:
|
|
20070
|
-
pullRequestId:
|
|
20071
|
-
headSha:
|
|
20072
|
-
baseSha:
|
|
20073
|
-
headRef:
|
|
20074
|
-
baseRef:
|
|
20075
|
-
ignoredReason:
|
|
21182
|
+
var PrReviewEventPayload = z3.object({
|
|
21183
|
+
provider: z3.enum(["github", "gitlab", "azure_devops"]),
|
|
21184
|
+
eventName: z3.string().max(200),
|
|
21185
|
+
action: z3.string().max(200),
|
|
21186
|
+
providerRepositoryId: z3.string().max(512).nullable(),
|
|
21187
|
+
installationId: z3.string().max(512).nullable(),
|
|
21188
|
+
projectId: z3.string().max(512).nullable(),
|
|
21189
|
+
pullRequestId: z3.string().max(512).nullable(),
|
|
21190
|
+
headSha: z3.string().regex(/^[0-9a-f]{40}$/).nullable(),
|
|
21191
|
+
baseSha: z3.string().regex(/^[0-9a-f]{40}$/).nullable(),
|
|
21192
|
+
headRef: z3.string().max(1024).nullable(),
|
|
21193
|
+
baseRef: z3.string().max(1024).nullable(),
|
|
21194
|
+
ignoredReason: z3.string().max(200).nullable()
|
|
20076
21195
|
}).strict();
|
|
20077
21196
|
var OPENGENI_PR_REVIEW_SKILL = {
|
|
20078
21197
|
name: "pr-review",
|
|
@@ -21044,7 +22163,7 @@ import {
|
|
|
21044
22163
|
saveAgentLearningSettings as saveAgentLearningSettings2,
|
|
21045
22164
|
dbSql,
|
|
21046
22165
|
deleteScheduledTask,
|
|
21047
|
-
getConnectionMetadata as
|
|
22166
|
+
getConnectionMetadata as getConnectionMetadata6,
|
|
21048
22167
|
getEnrollment as getEnrollment3,
|
|
21049
22168
|
getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
|
|
21050
22169
|
getKnowledgeSourceForSyncAuthority,
|
|
@@ -21070,7 +22189,7 @@ import {
|
|
|
21070
22189
|
import { HTTPException as HTTPException32 } from "hono/http-exception";
|
|
21071
22190
|
import { GOOGLE_DRIVE_PUBLICATION_SERVER_ID as GOOGLE_DRIVE_PUBLICATION_SERVER_ID3 } from "@opengeni/contracts/google-drive";
|
|
21072
22191
|
import { PERSONAL_GITHUB_CONNECTION_SURFACE_ID as PERSONAL_GITHUB_CONNECTION_SURFACE_ID3 } from "@opengeni/contracts/personal-github";
|
|
21073
|
-
import { isDeepStrictEqual as
|
|
22192
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
21074
22193
|
|
|
21075
22194
|
// src/domain/scheduled-task-audience.ts
|
|
21076
22195
|
import {
|
|
@@ -21185,7 +22304,7 @@ async function resolveScheduledTaskVisibility(input) {
|
|
|
21185
22304
|
|
|
21186
22305
|
// src/domain/scheduled-task-account-audit.ts
|
|
21187
22306
|
import { recordAuditEvent as recordAuditEvent2 } from "@opengeni/db";
|
|
21188
|
-
import { isDeepStrictEqual as
|
|
22307
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
21189
22308
|
var SCHEDULED_TASK_ACCOUNT_CHOICES_AUDIT_ACTION = "scheduled_task.account_choices_changed";
|
|
21190
22309
|
function choicesOf(config) {
|
|
21191
22310
|
return {
|
|
@@ -21205,7 +22324,7 @@ function scheduledTaskAccountChoiceChanges(before, after) {
|
|
|
21205
22324
|
const next = choicesOf(after);
|
|
21206
22325
|
const changes = {};
|
|
21207
22326
|
for (const key of ["pinnedAccounts", "pinnedAccountsRequired", "connectors"]) {
|
|
21208
|
-
if (!
|
|
22327
|
+
if (!isDeepStrictEqual4(prior[key], next[key]))
|
|
21209
22328
|
changes[key] = { before: prior[key], after: next[key] };
|
|
21210
22329
|
}
|
|
21211
22330
|
return Object.keys(changes).length > 0 ? changes : null;
|
|
@@ -22193,7 +23312,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
|
|
|
22193
23312
|
throw new HTTPException32(422, { message: "Source ingestion uses an ordinary agent task" });
|
|
22194
23313
|
}
|
|
22195
23314
|
const knowledgeSource = input.payload.agentConfig?.knowledgeSource ?? scheduledTaskKnowledgeSource(input.existing);
|
|
22196
|
-
if (knowledgeSource && requestedKnowledgeSource && !
|
|
23315
|
+
if (knowledgeSource && requestedKnowledgeSource && !isDeepStrictEqual5(requestedKnowledgeSource, existingKnowledgeSource)) {
|
|
22197
23316
|
await validateKnowledgeSourceSyncAction({
|
|
22198
23317
|
db: input.db,
|
|
22199
23318
|
grant: input.grant,
|
|
@@ -22330,7 +23449,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
|
|
|
22330
23449
|
const pins = nextRunMode === "existing_session" ? [] : input.payload.pinnedAccounts ?? input.existing.agentConfig.pinnedAccounts ?? [];
|
|
22331
23450
|
const required = input.payload.pinnedAccountsRequired ?? input.existing.agentConfig.pinnedAccountsRequired === true;
|
|
22332
23451
|
const next = withScheduledTaskPins(base, pins, required);
|
|
22333
|
-
if (update.agentConfig || !
|
|
23452
|
+
if (update.agentConfig || !isDeepStrictEqual5(next, input.existing.agentConfig))
|
|
22334
23453
|
update.agentConfig = next;
|
|
22335
23454
|
}
|
|
22336
23455
|
const nextAgentConfig = {
|
|
@@ -22359,7 +23478,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
|
|
|
22359
23478
|
verifySlackChannel: input.verifySlackChannel
|
|
22360
23479
|
});
|
|
22361
23480
|
const authorityTargetChanged = nextRunMode !== input.existing.runMode || nextTargetSessionId !== input.existing.targetSessionId || input.payload.variableSetId !== void 0 && input.payload.variableSetId !== input.existing.variableSetId || input.payload.rigId !== void 0 && input.payload.rigId !== input.existing.rigId;
|
|
22362
|
-
const materialExecutionChange = authorityTargetChanged || pinsProvided || !
|
|
23481
|
+
const materialExecutionChange = authorityTargetChanged || pinsProvided || !isDeepStrictEqual5(nextAgentConfig, input.existing.agentConfig) || input.payload.action !== void 0 && !isDeepStrictEqual5(input.payload.action, input.existing.action) || input.payload.schedule !== void 0 && !isDeepStrictEqual5(input.payload.schedule, input.existing.schedule) || input.payload.overlapPolicy !== void 0 && input.payload.overlapPolicy !== input.existing.overlapPolicy || input.payload.metadata !== void 0 && !isDeepStrictEqual5(input.payload.metadata, input.existing.metadata) || input.existing.status === "paused" && input.payload.status === "active";
|
|
22363
23482
|
if (materialExecutionChange && nextRunMode !== "existing_session" && (!materializedReusable || nextAgentConfig.model !== input.existing.agentConfig.model)) {
|
|
22364
23483
|
const beforeUpdateCommit = workspaceCustomModelCommitGuard({
|
|
22365
23484
|
settings: input.settings,
|
|
@@ -22512,7 +23631,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
|
|
|
22512
23631
|
rigId: input.payload.rigId !== void 0 ? input.payload.rigId : input.existing.rigId,
|
|
22513
23632
|
agentConfig: update.agentConfig ?? input.existing.agentConfig
|
|
22514
23633
|
});
|
|
22515
|
-
if (!knowledgeSource && !(materializedReusable &&
|
|
23634
|
+
if (!knowledgeSource && !(materializedReusable && isDeepStrictEqual5(nextAgentConfig.machineTarget, input.existing.agentConfig.machineTarget) && nextAgentConfig.sandboxBackend === input.existing.agentConfig.sandboxBackend)) {
|
|
22516
23635
|
await validateScheduledTaskMachineTarget({
|
|
22517
23636
|
settings: input.settings,
|
|
22518
23637
|
db: input.db,
|
|
@@ -22590,7 +23709,7 @@ async function validateKnowledgeSourceSyncAction(input) {
|
|
|
22590
23709
|
message: "knowledge source authority or generation changed"
|
|
22591
23710
|
});
|
|
22592
23711
|
}
|
|
22593
|
-
const connection = await
|
|
23712
|
+
const connection = await getConnectionMetadata6(
|
|
22594
23713
|
input.db,
|
|
22595
23714
|
input.grant.workspaceId,
|
|
22596
23715
|
input.action.connection.connectionId,
|
|
@@ -22681,7 +23800,7 @@ async function requireUnchangedScheduledTaskForCompensation(db, expected) {
|
|
|
22681
23800
|
);
|
|
22682
23801
|
if (!current) throw new ScheduledTaskHeadChangedError();
|
|
22683
23802
|
const { deletedAt, ...task } = current;
|
|
22684
|
-
if (deletedAt !== null || !
|
|
23803
|
+
if (deletedAt !== null || !isDeepStrictEqual5(task, expected))
|
|
22685
23804
|
throw new ScheduledTaskHeadChangedError();
|
|
22686
23805
|
}
|
|
22687
23806
|
async function syncUpdatedScheduledTask(input) {
|
|
@@ -22748,7 +23867,7 @@ async function validateScheduledTaskAgentConfig(input) {
|
|
|
22748
23867
|
},
|
|
22749
23868
|
toolsProvided: true
|
|
22750
23869
|
};
|
|
22751
|
-
const unchangedCreationField = (key) => input.retainedCreationConfig !== void 0 &&
|
|
23870
|
+
const unchangedCreationField = (key) => input.retainedCreationConfig !== void 0 && isDeepStrictEqual5(input.payload.agentConfig[key], input.retainedCreationConfig[key]);
|
|
22752
23871
|
const actor = scheduledTaskInitiatorForGrant(input.grant).actor;
|
|
22753
23872
|
const parent = actor && !existingChat ? await getSession8(input.db, input.workspaceId, actor.sessionId) : null;
|
|
22754
23873
|
if (!existingChat && actor && (!parent || parent.accountId !== input.grant.accountId)) {
|
|
@@ -22871,7 +23990,7 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
|
|
|
22871
23990
|
});
|
|
22872
23991
|
}
|
|
22873
23992
|
for (const required of preflight.requiredResources) {
|
|
22874
|
-
if (!agentConfig.resources.some((selected) =>
|
|
23993
|
+
if (!agentConfig.resources.some((selected) => isDeepStrictEqual5(selected, required))) {
|
|
22875
23994
|
throw new HTTPException32(422, {
|
|
22876
23995
|
message: "incidentTelemetryPreflight.requiredResources must be exact selected resources"
|
|
22877
23996
|
});
|
|
@@ -22971,7 +24090,7 @@ import {
|
|
|
22971
24090
|
ScheduledTaskHeadChangedError as ScheduledTaskHeadChangedError2
|
|
22972
24091
|
} from "@opengeni/db";
|
|
22973
24092
|
import { HTTPException as HTTPException33 } from "hono/http-exception";
|
|
22974
|
-
import { z as
|
|
24093
|
+
import { z as z4 } from "zod";
|
|
22975
24094
|
var ALWAYS_ATTACHED_CONNECTOR = "opengeni";
|
|
22976
24095
|
var PRESERVED_ACCOUNT_SURFACES = /* @__PURE__ */ new Set([
|
|
22977
24096
|
GOOGLE_DRIVE_PUBLICATION_SERVER_ID4,
|
|
@@ -22986,10 +24105,10 @@ function uniqueById(items) {
|
|
|
22986
24105
|
return items.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true)).slice(0, SCHEDULED_TASK_ACCESS_CONNECTORS_MAX);
|
|
22987
24106
|
}
|
|
22988
24107
|
function planScheduledTaskConnectors(input) {
|
|
22989
|
-
const
|
|
24108
|
+
const unavailable2 = input.taskTools.filter(
|
|
22990
24109
|
(tool) => tool.id !== ALWAYS_ATTACHED_CONNECTOR && !input.availableServerIds.has(tool.id)
|
|
22991
24110
|
);
|
|
22992
|
-
const unavailableIds = new Set(
|
|
24111
|
+
const unavailableIds = new Set(unavailable2.map((tool) => tool.id));
|
|
22993
24112
|
const kept = input.taskTools.filter((tool) => !unavailableIds.has(tool.id));
|
|
22994
24113
|
const keptIds = new Set(kept.map((tool) => tool.id));
|
|
22995
24114
|
const missing = input.defaultTools.filter(
|
|
@@ -22998,7 +24117,7 @@ function planScheduledTaskConnectors(input) {
|
|
|
22998
24117
|
return {
|
|
22999
24118
|
tools: mergeToolRefs3([...kept], [...missing]),
|
|
23000
24119
|
missing: uniqueById(missing.map((tool) => connector(tool.id, input.names))),
|
|
23001
|
-
unavailable: uniqueById(
|
|
24120
|
+
unavailable: uniqueById(unavailable2.map((tool) => connector(tool.id, input.names)))
|
|
23002
24121
|
};
|
|
23003
24122
|
}
|
|
23004
24123
|
function planScheduledTaskPinnedAccounts(input) {
|
|
@@ -23009,7 +24128,7 @@ function planScheduledTaskPinnedAccounts(input) {
|
|
|
23009
24128
|
)
|
|
23010
24129
|
);
|
|
23011
24130
|
const kept = [];
|
|
23012
|
-
const
|
|
24131
|
+
const unavailable2 = [];
|
|
23013
24132
|
const dropped = [];
|
|
23014
24133
|
for (const pin of input.pins) {
|
|
23015
24134
|
if (PRESERVED_ACCOUNT_SURFACES.has(pin.serverId)) {
|
|
@@ -23022,11 +24141,11 @@ function planScheduledTaskPinnedAccounts(input) {
|
|
|
23022
24141
|
}
|
|
23023
24142
|
kept.push(pin);
|
|
23024
24143
|
if (!available.has(JSON.stringify([pin.serverId, pin.connectionId])))
|
|
23025
|
-
|
|
24144
|
+
unavailable2.push(connector(pin.serverId, input.names));
|
|
23026
24145
|
}
|
|
23027
24146
|
return {
|
|
23028
24147
|
kept,
|
|
23029
|
-
unavailable: uniqueById(
|
|
24148
|
+
unavailable: uniqueById(unavailable2),
|
|
23030
24149
|
dropped: uniqueById(dropped)
|
|
23031
24150
|
};
|
|
23032
24151
|
}
|
|
@@ -23081,13 +24200,13 @@ function planScheduledTaskOpenGeniTools(input) {
|
|
|
23081
24200
|
policy: unchanged ? null : { firstPartyMcpTools: tools, firstPartyMcpPermissions: permissions }
|
|
23082
24201
|
};
|
|
23083
24202
|
}
|
|
23084
|
-
var AuthNeededFact =
|
|
23085
|
-
serverId:
|
|
23086
|
-
canonicalServerId:
|
|
23087
|
-
providerDomain:
|
|
24203
|
+
var AuthNeededFact = z4.object({
|
|
24204
|
+
serverId: z4.string().min(1),
|
|
24205
|
+
canonicalServerId: z4.string().min(1).optional(),
|
|
24206
|
+
providerDomain: z4.string().min(1),
|
|
23088
24207
|
reason: ToolAuthNeededReason,
|
|
23089
|
-
capability:
|
|
23090
|
-
setupRequest:
|
|
24208
|
+
capability: z4.unknown().optional(),
|
|
24209
|
+
setupRequest: z4.unknown().optional()
|
|
23091
24210
|
});
|
|
23092
24211
|
function scheduledTaskRunAccessFailures(events, names) {
|
|
23093
24212
|
const byKey = /* @__PURE__ */ new Map();
|
|
@@ -24323,7 +25442,14 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
|
|
|
24323
25442
|
}
|
|
24324
25443
|
|
|
24325
25444
|
// src/domain/insights-usage.ts
|
|
24326
|
-
import {
|
|
25445
|
+
import {
|
|
25446
|
+
readInsightsCalls,
|
|
25447
|
+
readInsightsUsage,
|
|
25448
|
+
readInsightsSiteUsage
|
|
25449
|
+
} from "@opengeni/db";
|
|
25450
|
+
async function getInsightsSiteUsage(db, input) {
|
|
25451
|
+
return readInsightsSiteUsage(db, { ...input, now: input.now ?? /* @__PURE__ */ new Date() });
|
|
25452
|
+
}
|
|
24327
25453
|
async function getInsightsUsage(db, input) {
|
|
24328
25454
|
return await readInsightsUsage(db, { ...input, now: input.now ?? /* @__PURE__ */ new Date() });
|
|
24329
25455
|
}
|
|
@@ -27246,6 +28372,40 @@ async function controlHumanWorkspaceTimer(deps, context, input) {
|
|
|
27246
28372
|
]);
|
|
27247
28373
|
}
|
|
27248
28374
|
|
|
28375
|
+
// src/application/site-session-cancellation.ts
|
|
28376
|
+
import { cancelSiteRuntimeConversation } from "@opengeni/db";
|
|
28377
|
+
import {
|
|
28378
|
+
publishDurableSessionEvents as publishDurableSessionEvents4,
|
|
28379
|
+
publishDurableWorkspaceControlEvent as publishDurableWorkspaceControlEvent3
|
|
28380
|
+
} from "@opengeni/events";
|
|
28381
|
+
async function cancelPublishedSiteSession(deps, input) {
|
|
28382
|
+
const result = await cancelSiteRuntimeConversation(deps.db, input);
|
|
28383
|
+
if (!result) return null;
|
|
28384
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
28385
|
+
for (const event of result.events) {
|
|
28386
|
+
const group = bySession.get(event.sessionId) ?? [];
|
|
28387
|
+
group.push(event);
|
|
28388
|
+
bySession.set(event.sessionId, group);
|
|
28389
|
+
}
|
|
28390
|
+
const effects = await Promise.allSettled([
|
|
28391
|
+
...[...bySession].map(
|
|
28392
|
+
([sessionId, events]) => publishDurableSessionEvents4(deps.bus, input.workspaceId, sessionId, events)
|
|
28393
|
+
),
|
|
28394
|
+
...result.workspaceEvent ? [publishDurableWorkspaceControlEvent3(deps.bus, input.workspaceId, result.workspaceEvent)] : [],
|
|
28395
|
+
...result.wakeCount > 0 ? [deps.workflowClient.requestSessionWorkflowWakeDispatch()] : []
|
|
28396
|
+
]);
|
|
28397
|
+
if (effects.some((effect) => effect.status === "rejected"))
|
|
28398
|
+
console.warn(
|
|
28399
|
+
"[site-lifecycle] post-commit fanout failed; durable events and wake outbox retained"
|
|
28400
|
+
);
|
|
28401
|
+
return {
|
|
28402
|
+
receiptId: result.receiptId,
|
|
28403
|
+
replay: result.replay,
|
|
28404
|
+
cancelledSessionCount: result.cancelledSessionCount,
|
|
28405
|
+
cancelledTurnCount: result.cancelledTurnCount
|
|
28406
|
+
};
|
|
28407
|
+
}
|
|
28408
|
+
|
|
27249
28409
|
// src/application/archived-session-imports.ts
|
|
27250
28410
|
import {
|
|
27251
28411
|
AppendArchivedSessionEventsRequest,
|
|
@@ -27260,7 +28420,7 @@ import {
|
|
|
27260
28420
|
importArchivedSession,
|
|
27261
28421
|
withSessionRlsActorContext as withSessionRlsActorContext7
|
|
27262
28422
|
} from "@opengeni/db";
|
|
27263
|
-
import { publishDurableSessionEvents as
|
|
28423
|
+
import { publishDurableSessionEvents as publishDurableSessionEvents5 } from "@opengeni/events";
|
|
27264
28424
|
import { HTTPException as HTTPException38 } from "hono/http-exception";
|
|
27265
28425
|
import { ArchivedSessionImportError as ArchivedSessionImportError2 } from "@opengeni/db";
|
|
27266
28426
|
function requireImportAuthorization(authorization, workspaceId, permission) {
|
|
@@ -27291,7 +28451,7 @@ function importerScope(authorization, workspaceId, permission, fileOwnerSubjectI
|
|
|
27291
28451
|
async function publishImportedEvents(deps, workspaceId, sessionId, events) {
|
|
27292
28452
|
if (!events.length) return;
|
|
27293
28453
|
try {
|
|
27294
|
-
await
|
|
28454
|
+
await publishDurableSessionEvents5(deps.bus, workspaceId, sessionId, events);
|
|
27295
28455
|
} catch {
|
|
27296
28456
|
console.warn("[session-import] committed timeline fanout deferred to replay", { sessionId });
|
|
27297
28457
|
}
|
|
@@ -27860,7 +29020,7 @@ import { createPinnedIntegrationTransport as createPinnedIntegrationTransport2 }
|
|
|
27860
29020
|
import { mcpToolRecommendsApproval } from "@opengeni/contracts";
|
|
27861
29021
|
import {
|
|
27862
29022
|
buildConnectionTokenResolver,
|
|
27863
|
-
getConnectionMetadata as
|
|
29023
|
+
getConnectionMetadata as getConnectionMetadata7,
|
|
27864
29024
|
listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers3,
|
|
27865
29025
|
listConnectorToolPermissionPolicies,
|
|
27866
29026
|
listChangedConnectorToolPermissions,
|
|
@@ -27913,7 +29073,7 @@ async function resolveTarget2(input) {
|
|
|
27913
29073
|
const server2 = settingsWithApiIntegrationServers({ ...input.settings, mcpServers: [] }, [
|
|
27914
29074
|
integration
|
|
27915
29075
|
]).mcpServers[0];
|
|
27916
|
-
const connection2 = integration.connectionRef?.connectionId ? await
|
|
29076
|
+
const connection2 = integration.connectionRef?.connectionId ? await getConnectionMetadata7(
|
|
27917
29077
|
input.db,
|
|
27918
29078
|
input.workspaceId,
|
|
27919
29079
|
integration.connectionRef.connectionId,
|
|
@@ -28269,6 +29429,7 @@ export {
|
|
|
28269
29429
|
EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION,
|
|
28270
29430
|
EDITABLE_ARTIFACT_ORIGINAL_IMPORT_MAX_BYTES,
|
|
28271
29431
|
EXTERNAL_FIRST_USE_MEMBER_PERMISSIONS,
|
|
29432
|
+
EXTERNAL_VAULT_CREDENTIAL_ROLE,
|
|
28272
29433
|
EditableArtifactAgentApplication,
|
|
28273
29434
|
EditableArtifactApplication,
|
|
28274
29435
|
EditableArtifactCausalChainError,
|
|
@@ -28309,6 +29470,7 @@ export {
|
|
|
28309
29470
|
KnowledgeVectorQueryRejectedError,
|
|
28310
29471
|
LINKED_VAULT_MISS_MAX_AGE_MS,
|
|
28311
29472
|
LINKED_VAULT_OPEN_WAIT_MS,
|
|
29473
|
+
LINKED_VAULT_REFRESH_TIMEOUT_MS,
|
|
28312
29474
|
LINKED_VAULT_REFRESH_WAIT_MS,
|
|
28313
29475
|
LINKED_VAULT_SEARCH_MAX_AGE_MS,
|
|
28314
29476
|
MAX_CHECKS_PER_RIG,
|
|
@@ -28378,6 +29540,7 @@ export {
|
|
|
28378
29540
|
SessionTenancyManagedHumanRequiredError,
|
|
28379
29541
|
SessionTenancyPersistenceOutcomeUnknownError,
|
|
28380
29542
|
SessionUsageLimitHttpError,
|
|
29543
|
+
StoredVaultProviderCredentials,
|
|
28381
29544
|
SystemEditableArtifactClock,
|
|
28382
29545
|
SystemEditableArtifactLiveClock,
|
|
28383
29546
|
SystemEditableArtifactLiveScheduler,
|
|
@@ -28413,6 +29576,7 @@ export {
|
|
|
28413
29576
|
agentAccessPermitsCrossTreeAccess,
|
|
28414
29577
|
agentConfigParentFor,
|
|
28415
29578
|
agentRunAdmissionDenial,
|
|
29579
|
+
aggregateProviderFreshness,
|
|
28416
29580
|
apiIntegrationsMatchingDelegations,
|
|
28417
29581
|
appendArchivedSessionEventsForRequest,
|
|
28418
29582
|
appendRigSetupCommand,
|
|
@@ -28455,12 +29619,14 @@ export {
|
|
|
28455
29619
|
automationRequestDigest,
|
|
28456
29620
|
availableMcpAccountBindings,
|
|
28457
29621
|
availableToolRefs,
|
|
29622
|
+
bindVaultProviderAuthority,
|
|
28458
29623
|
buildApiIntegrationMcpServers,
|
|
28459
29624
|
buildAutomationAcceptedExecution,
|
|
28460
29625
|
buildCapabilityCatalog,
|
|
28461
29626
|
buildFleetContextForSession,
|
|
28462
29627
|
buildMissingSecretAskCard2 as buildMissingSecretAskCard,
|
|
28463
29628
|
cancelExternalWorkspaceMemberGrantForRequest,
|
|
29629
|
+
cancelPublishedSiteSession,
|
|
28464
29630
|
canonicalConfiguredModel,
|
|
28465
29631
|
canonicalConversationDeliveryCommandJson,
|
|
28466
29632
|
canonicalConversationDeliveryOutcomeJson,
|
|
@@ -28499,8 +29665,10 @@ export {
|
|
|
28499
29665
|
createCompanyProfileAgentAdminRouter,
|
|
28500
29666
|
createCompanyProfileDurableLearningAdapter,
|
|
28501
29667
|
createGitHubSkillSourceClient,
|
|
29668
|
+
createPinnedRemoteVaultTransport,
|
|
28502
29669
|
createPublicSkillSearchClient,
|
|
28503
29670
|
createRealtimeVoiceBilling,
|
|
29671
|
+
createRemoteExternalVaultFactories,
|
|
28504
29672
|
createRigForApi,
|
|
28505
29673
|
createRigVersionForApi,
|
|
28506
29674
|
createRunOnSecretResolver,
|
|
@@ -28566,6 +29734,7 @@ export {
|
|
|
28566
29734
|
editableArtifactTransactionId,
|
|
28567
29735
|
effectiveFirstPartyMcpToolCeiling,
|
|
28568
29736
|
effectiveOrganizationPrivacy,
|
|
29737
|
+
effectiveProviderOperations,
|
|
28569
29738
|
enableCapability,
|
|
28570
29739
|
enabledCapabilityMcpToolRefs,
|
|
28571
29740
|
encodeEditableArtifactBrokerHint,
|
|
@@ -28581,6 +29750,10 @@ export {
|
|
|
28581
29750
|
externalActorContinuationForAuthorization,
|
|
28582
29751
|
externalAttributionForAuthorization,
|
|
28583
29752
|
externalContinuationCommitAuthorizer,
|
|
29753
|
+
externalVaultConnectionDetails,
|
|
29754
|
+
externalVaultConnectionMetadata,
|
|
29755
|
+
externalVaultProviderDomain,
|
|
29756
|
+
externalVaultProviderForConnection,
|
|
28584
29757
|
fikenConnectionMetadata,
|
|
28585
29758
|
fileOwnerContextForAccess,
|
|
28586
29759
|
fileOwnerContextForAgent,
|
|
@@ -28596,6 +29769,7 @@ export {
|
|
|
28596
29769
|
getAutomationAdapter,
|
|
28597
29770
|
getConnectorToolPermissions,
|
|
28598
29771
|
getHumanComposerDraft,
|
|
29772
|
+
getInsightsSiteUsage,
|
|
28599
29773
|
getInsightsUsage,
|
|
28600
29774
|
getManagedAuthRequestActorAbortSignal,
|
|
28601
29775
|
getManagedAuthRequestActorAdmissionStamp,
|
|
@@ -28637,6 +29811,7 @@ export {
|
|
|
28637
29811
|
installSkill,
|
|
28638
29812
|
integrationKeyForConnectProvider,
|
|
28639
29813
|
integrationSourceForOrganizationPolicy,
|
|
29814
|
+
intersectProviderOperations,
|
|
28640
29815
|
isAcceptedMimeType,
|
|
28641
29816
|
isAuthenticatedPersonAuthorization,
|
|
28642
29817
|
isAuthoritativeGitHubRepositorySelectionError,
|
|
@@ -28644,6 +29819,7 @@ export {
|
|
|
28644
29819
|
isDeveloperSetupAuthorization,
|
|
28645
29820
|
isDeveloperSetupDelegatedPermissionAllowed,
|
|
28646
29821
|
isDeveloperSetupGrant,
|
|
29822
|
+
isExternalVaultConnection,
|
|
28647
29823
|
isFikenConnection,
|
|
28648
29824
|
isOnePasswordConnection,
|
|
28649
29825
|
isOpenGeniSlackBotConnection,
|
|
@@ -28651,7 +29827,7 @@ export {
|
|
|
28651
29827
|
isTerminalVideoGenerationState,
|
|
28652
29828
|
isTrustedScheduledSlackBotSession,
|
|
28653
29829
|
isUserMember,
|
|
28654
|
-
isVaultError,
|
|
29830
|
+
isVaultError3 as isVaultError,
|
|
28655
29831
|
isVerifiedDelegatedHumanAuthorization,
|
|
28656
29832
|
isVerifiedOrganizationServiceAuthorization,
|
|
28657
29833
|
isWorkspaceCustomModelId,
|
|
@@ -28677,6 +29853,7 @@ export {
|
|
|
28677
29853
|
listSkills,
|
|
28678
29854
|
loadCoreRoutedClaudeCatalogReadiness,
|
|
28679
29855
|
loadCoreRoutedXaiCatalogReadiness,
|
|
29856
|
+
loadExternalVaultConnection,
|
|
28680
29857
|
loadOnePasswordConnectionToken,
|
|
28681
29858
|
loadWorkspaceClaudeSubscriptionReadiness,
|
|
28682
29859
|
loadWorkspaceCodexCatalogReadiness,
|
|
@@ -28731,6 +29908,7 @@ export {
|
|
|
28731
29908
|
parseSkillSource,
|
|
28732
29909
|
pausePrivateScheduledTaskAsAdmin,
|
|
28733
29910
|
pauseScheduledTaskAsAdmin,
|
|
29911
|
+
pendingVaultCredentialBundle,
|
|
28734
29912
|
personalAtlassianDelegationsFromVisibleConnections,
|
|
28735
29913
|
personalConnectionDelegationForServer,
|
|
28736
29914
|
personalConnectionDelegationSourceForGrant,
|
|
@@ -28771,10 +29949,12 @@ export {
|
|
|
28771
29949
|
promoteSetupAppendChange,
|
|
28772
29950
|
promoteVerifiedDefinitionEditChangeForApi,
|
|
28773
29951
|
proposeRigChangeForApi,
|
|
29952
|
+
providerOperationsForMode,
|
|
28774
29953
|
providerPrimaryFieldId,
|
|
28775
29954
|
providerSecretName,
|
|
28776
29955
|
providerSettingsForSessionSandboxRuntime,
|
|
28777
29956
|
providerSyncFailure,
|
|
29957
|
+
providerVaultFreshness,
|
|
28778
29958
|
provisionSandbox,
|
|
28779
29959
|
publishGovernedLearningEventToSlack,
|
|
28780
29960
|
readSessionAttachmentFiles,
|
|
@@ -28790,6 +29970,7 @@ export {
|
|
|
28790
29970
|
recordUserMessageAccepted,
|
|
28791
29971
|
recordWorkspaceUsage,
|
|
28792
29972
|
refreshCoreCodexModelEntitlements,
|
|
29973
|
+
refreshLinkedVaults,
|
|
28793
29974
|
refreshScheduledTaskAccess,
|
|
28794
29975
|
refreshStaleLinkedVaults,
|
|
28795
29976
|
registerAutomationAdapter,
|
|
@@ -28969,9 +30150,11 @@ export {
|
|
|
28969
30150
|
stableJson,
|
|
28970
30151
|
stampDelegatedHumanAuthorization,
|
|
28971
30152
|
stampPersonalApiKeyRequest,
|
|
30153
|
+
stampSiteViewerSessionAuthorization,
|
|
28972
30154
|
statusForVoiceInputError,
|
|
28973
30155
|
steerAgentSession,
|
|
28974
30156
|
steerHumanQueuePrompt,
|
|
30157
|
+
storedProviderOperations,
|
|
28975
30158
|
submitComposerDraftForRequest,
|
|
28976
30159
|
suggestCapabilityCatalogItems,
|
|
28977
30160
|
swapActiveSandbox,
|
|
@@ -29028,6 +30211,7 @@ export {
|
|
|
29028
30211
|
vaultErrorCodeForProvider,
|
|
29029
30212
|
vaultErrorFromDatabase,
|
|
29030
30213
|
vaultItemReferences,
|
|
30214
|
+
vaultRefreshScopeKey,
|
|
29031
30215
|
verifiedDelegatedHumanAuthorizationForRequest,
|
|
29032
30216
|
verifyDirectModelAccess,
|
|
29033
30217
|
verifyPrReviewWebhook,
|