@opengeni/core 1.4.5-canary.38533733713001 → 2.0.1
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/domain/vaults/provider-registry.d.ts +6 -3
- package/dist/domain/vaults/provider-sync.d.ts +44 -1
- package/dist/domain/vaults/provider-writes.d.ts +1 -1
- package/dist/goal-admission.d.ts +0 -1
- package/dist/index.js +387 -286
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/src/access/service-initiator.ts +1 -1
- package/src/domain/capabilities.ts +2 -2
- package/src/domain/sessions.ts +1 -1
- package/src/domain/vaults/provider-registry.ts +22 -5
- package/src/domain/vaults/provider-sync.ts +98 -1
- package/src/domain/vaults/provider-writes.ts +4 -4
- package/src/domain/vaults/secret-resolver.ts +39 -14
- package/src/domain/vaults/vault-service.ts +19 -2
package/dist/index.js
CHANGED
|
@@ -948,6 +948,7 @@ function preferredFikenConnection(connections) {
|
|
|
948
948
|
// src/domain/vaults/provider-registry.ts
|
|
949
949
|
import {
|
|
950
950
|
getConnectionMetadata,
|
|
951
|
+
getProviderConnectionHomeWorkspace,
|
|
951
952
|
loadConnectionCredentialForBroker
|
|
952
953
|
} from "@opengeni/db";
|
|
953
954
|
import {
|
|
@@ -994,12 +995,25 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
|
|
|
994
995
|
"provider_auth_invalid",
|
|
995
996
|
"The 1Password connection for this vault was removed or is not available to you; reconnect 1Password"
|
|
996
997
|
);
|
|
997
|
-
|
|
998
|
+
let homeWorkspaceId = input.workspaceId;
|
|
999
|
+
let connection = await getConnectionMetadata(
|
|
998
1000
|
db,
|
|
999
1001
|
input.workspaceId,
|
|
1000
1002
|
input.connectionId,
|
|
1001
1003
|
input.subjectId
|
|
1002
1004
|
);
|
|
1005
|
+
if (!connection) {
|
|
1006
|
+
const home = await getProviderConnectionHomeWorkspace(
|
|
1007
|
+
db,
|
|
1008
|
+
input.workspaceId,
|
|
1009
|
+
input.connectionId,
|
|
1010
|
+
input.subjectId
|
|
1011
|
+
);
|
|
1012
|
+
if (home && home !== input.workspaceId) {
|
|
1013
|
+
homeWorkspaceId = home;
|
|
1014
|
+
connection = await getConnectionMetadata(db, home, input.connectionId, input.subjectId);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1003
1017
|
if (!connection || !isOnePasswordConnection(connection)) throw unavailable();
|
|
1004
1018
|
if (connection.status !== "active") {
|
|
1005
1019
|
throw new VaultProviderError(
|
|
@@ -1009,7 +1023,7 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
|
|
|
1009
1023
|
}
|
|
1010
1024
|
const personal = connection.subjectId !== null;
|
|
1011
1025
|
const credential = await loadConnectionCredentialForBroker(db, settings, {
|
|
1012
|
-
workspaceId:
|
|
1026
|
+
workspaceId: homeWorkspaceId,
|
|
1013
1027
|
connectionId: connection.id,
|
|
1014
1028
|
providerDomain: ONEPASSWORD_PROVIDER_DOMAIN,
|
|
1015
1029
|
kind: "api_key",
|
|
@@ -2189,7 +2203,7 @@ function nativeConnectionCapabilityRecommendations(options = {}) {
|
|
|
2189
2203
|
kind: "api",
|
|
2190
2204
|
source: "built_in",
|
|
2191
2205
|
name: "Connected Machine",
|
|
2192
|
-
description: "Run this chat on the person's own computer or server (Mac, Linux or Windows), and optionally let agents use their Chrome through the
|
|
2206
|
+
description: "Run this chat on the person's own computer or server (Mac, Linux or Windows), and optionally let agents use their Chrome through the Opengeni Browser extension.",
|
|
2193
2207
|
category: "compute",
|
|
2194
2208
|
tags: [
|
|
2195
2209
|
"machine",
|
|
@@ -2217,7 +2231,7 @@ function nativeConnectionCapabilityRecommendations(options = {}) {
|
|
|
2217
2231
|
tools: [{ kind: "mcp", id: "opengeni" }],
|
|
2218
2232
|
runtime: {
|
|
2219
2233
|
available: true,
|
|
2220
|
-
notes: `The person runs a one-line connect command on their machine from the chat card. The
|
|
2234
|
+
notes: `The person runs a one-line connect command on their machine from the chat card. The Opengeni Browser Chrome extension (${OPENGENI_BROWSER_EXTENSION_URL}) works only after the machine is connected.`
|
|
2221
2235
|
},
|
|
2222
2236
|
lifecycle: {
|
|
2223
2237
|
status: "available",
|
|
@@ -6805,18 +6819,321 @@ import {
|
|
|
6805
6819
|
parseTotpInput,
|
|
6806
6820
|
referenceKey,
|
|
6807
6821
|
SecretReferenceError,
|
|
6808
|
-
VAULT_LIMITS
|
|
6822
|
+
VAULT_LIMITS as VAULT_LIMITS2
|
|
6809
6823
|
} from "@opengeni/contracts";
|
|
6810
6824
|
import {
|
|
6811
6825
|
decryptEnvironmentValue,
|
|
6826
|
+
listVaultsForSubject,
|
|
6812
6827
|
recordVaultSecretUse,
|
|
6813
6828
|
resolveSecretReferenceCandidates
|
|
6814
6829
|
} from "@opengeni/db";
|
|
6815
6830
|
import {
|
|
6816
6831
|
createProviderCallBudget,
|
|
6817
6832
|
EMPTY_VAULT_PROVIDER_REGISTRY,
|
|
6818
|
-
isVaultProviderError
|
|
6833
|
+
isVaultProviderError as isVaultProviderError2
|
|
6819
6834
|
} from "@opengeni/vaults";
|
|
6835
|
+
|
|
6836
|
+
// src/domain/vaults/provider-sync.ts
|
|
6837
|
+
import {
|
|
6838
|
+
isValidSecretName,
|
|
6839
|
+
VAULT_DEFAULT_PRIMARY_FIELD,
|
|
6840
|
+
VAULT_LIMITS
|
|
6841
|
+
} from "@opengeni/contracts";
|
|
6842
|
+
import {
|
|
6843
|
+
getProviderVaultSyncTarget,
|
|
6844
|
+
setVaultSyncStatus,
|
|
6845
|
+
upsertProviderVaultItems
|
|
6846
|
+
} from "@opengeni/db";
|
|
6847
|
+
import {
|
|
6848
|
+
isVaultProviderError,
|
|
6849
|
+
VaultProviderError as VaultProviderError2
|
|
6850
|
+
} from "@opengeni/vaults";
|
|
6851
|
+
var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
|
|
6852
|
+
var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
|
|
6853
|
+
function nameKey(name) {
|
|
6854
|
+
return name.normalize("NFC").trim().toLowerCase();
|
|
6855
|
+
}
|
|
6856
|
+
function truncateChars(value, max) {
|
|
6857
|
+
const chars = [...value];
|
|
6858
|
+
return chars.length > max ? chars.slice(0, max).join("") : value;
|
|
6859
|
+
}
|
|
6860
|
+
function providerSecretName(raw, fallback = "Untitled") {
|
|
6861
|
+
let name = truncateChars(
|
|
6862
|
+
raw.normalize("NFC").replace(FORBIDDEN_NAME_CHARACTERS, "-").trim(),
|
|
6863
|
+
VAULT_LIMITS.nameMaxChars
|
|
6864
|
+
).trim();
|
|
6865
|
+
if (!name) name = fallback;
|
|
6866
|
+
if (/^id:/iu.test(name)) name = `id-${name.slice(3)}`.trim() || fallback;
|
|
6867
|
+
return isValidSecretName(name) ? name : fallback;
|
|
6868
|
+
}
|
|
6869
|
+
function uniqueProviderName(name, taken) {
|
|
6870
|
+
let candidate = name;
|
|
6871
|
+
for (let n = 2; taken.has(nameKey(candidate)); n += 1) {
|
|
6872
|
+
const suffix = ` (${n})`;
|
|
6873
|
+
candidate = `${truncateChars(name, VAULT_LIMITS.nameMaxChars - suffix.length).trimEnd()}${suffix}`;
|
|
6874
|
+
}
|
|
6875
|
+
taken.add(nameKey(candidate));
|
|
6876
|
+
return candidate;
|
|
6877
|
+
}
|
|
6878
|
+
var PRIMARY_FIELD_FALLBACK = ["password", "value", "credential", "note"];
|
|
6879
|
+
function providerPrimaryFieldId(item) {
|
|
6880
|
+
const candidates = item.fields.filter((field) => field.kind !== "totp");
|
|
6881
|
+
const byName = (name) => candidates.find((field) => nameKey(field.name) === name)?.externalFieldId ?? null;
|
|
6882
|
+
const typeDefault = VAULT_DEFAULT_PRIMARY_FIELD[item.type];
|
|
6883
|
+
if (typeDefault) {
|
|
6884
|
+
const found = byName(typeDefault.toLowerCase());
|
|
6885
|
+
if (found) return found;
|
|
6886
|
+
}
|
|
6887
|
+
for (const name of PRIMARY_FIELD_FALLBACK) {
|
|
6888
|
+
const found = byName(name);
|
|
6889
|
+
if (found) return found;
|
|
6890
|
+
}
|
|
6891
|
+
if (item.type === "file") {
|
|
6892
|
+
const file = candidates.find((field) => field.kind === "file");
|
|
6893
|
+
if (file) return file.externalFieldId;
|
|
6894
|
+
}
|
|
6895
|
+
return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
|
|
6896
|
+
}
|
|
6897
|
+
function planProviderMirror(items, contentVersion) {
|
|
6898
|
+
const ordered = [...items].sort(
|
|
6899
|
+
(a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
|
|
6900
|
+
);
|
|
6901
|
+
const kept = ordered.slice(0, VAULT_LIMITS.itemsPerVault);
|
|
6902
|
+
const takenItems = /* @__PURE__ */ new Set();
|
|
6903
|
+
const mirrored = kept.map((item) => {
|
|
6904
|
+
const name = uniqueProviderName(providerSecretName(item.name), takenItems);
|
|
6905
|
+
const takenFields = /* @__PURE__ */ new Set();
|
|
6906
|
+
const fields = item.fields.slice(0, VAULT_LIMITS.fieldsPerItem).map((field, position) => {
|
|
6907
|
+
let fieldName = providerSecretName(
|
|
6908
|
+
field.name,
|
|
6909
|
+
field.kind === "totp" ? "one-time password" : "field"
|
|
6910
|
+
);
|
|
6911
|
+
if (nameKey(fieldName) === "otp") fieldName = "otp code";
|
|
6912
|
+
return {
|
|
6913
|
+
externalFieldId: field.externalFieldId,
|
|
6914
|
+
name: uniqueProviderName(fieldName, takenFields),
|
|
6915
|
+
kind: field.kind,
|
|
6916
|
+
position
|
|
6917
|
+
};
|
|
6918
|
+
});
|
|
6919
|
+
return {
|
|
6920
|
+
externalItemId: item.externalItemId,
|
|
6921
|
+
name,
|
|
6922
|
+
providerName: item.name,
|
|
6923
|
+
type: item.type,
|
|
6924
|
+
websites: item.websites.filter((website) => website.length <= VAULT_LIMITS.websiteMaxChars).slice(0, VAULT_LIMITS.websitesPerItem),
|
|
6925
|
+
fields,
|
|
6926
|
+
primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
|
|
6927
|
+
hasTotp: item.fields.some((field) => field.kind === "totp"),
|
|
6928
|
+
version: item.version,
|
|
6929
|
+
updatedAt: item.updatedAt
|
|
6930
|
+
};
|
|
6931
|
+
});
|
|
6932
|
+
return { contentVersion, items: mirrored, truncated: ordered.length - kept.length };
|
|
6933
|
+
}
|
|
6934
|
+
function providerSyncFailure(error, now) {
|
|
6935
|
+
const code = isVaultProviderError(error) ? error.code : "provider_unavailable";
|
|
6936
|
+
const message = isVaultProviderError(error) ? error.message : "the vault provider could not be reached";
|
|
6937
|
+
if (code === "provider_rate_limited") {
|
|
6938
|
+
const retryAfterMs = isVaultProviderError(error) && error.retryAfterMs && error.retryAfterMs > 0 ? error.retryAfterMs : PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS;
|
|
6939
|
+
return {
|
|
6940
|
+
syncStatus: "rate_limited",
|
|
6941
|
+
syncErrorCode: code,
|
|
6942
|
+
syncErrorMessage: message,
|
|
6943
|
+
rateLimitedUntil: new Date(now.getTime() + retryAfterMs),
|
|
6944
|
+
lastSyncedAt: null
|
|
6945
|
+
};
|
|
6946
|
+
}
|
|
6947
|
+
return {
|
|
6948
|
+
syncStatus: code === "provider_auth_expired" || code === "provider_auth_invalid" ? "auth_expired" : "error",
|
|
6949
|
+
syncErrorCode: code,
|
|
6950
|
+
syncErrorMessage: message,
|
|
6951
|
+
rateLimitedUntil: null,
|
|
6952
|
+
lastSyncedAt: null
|
|
6953
|
+
};
|
|
6954
|
+
}
|
|
6955
|
+
async function syncProviderVault(input) {
|
|
6956
|
+
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
6957
|
+
const { target } = input;
|
|
6958
|
+
if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime() && !input.force) {
|
|
6959
|
+
return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
|
|
6960
|
+
}
|
|
6961
|
+
try {
|
|
6962
|
+
const vaults = await input.provider.listVaults(input.ctx);
|
|
6963
|
+
const vault = vaults.find((candidate) => candidate.externalVaultId === target.externalVaultId);
|
|
6964
|
+
if (!vault) {
|
|
6965
|
+
const failure = providerSyncFailure(
|
|
6966
|
+
new VaultProviderError2(
|
|
6967
|
+
"provider_not_found",
|
|
6968
|
+
"the linked provider vault no longer exists or is no longer shared with this connection"
|
|
6969
|
+
),
|
|
6970
|
+
now()
|
|
6971
|
+
);
|
|
6972
|
+
await input.store.setSyncStatus(failure);
|
|
6973
|
+
return {
|
|
6974
|
+
status: "failed",
|
|
6975
|
+
syncStatus: failure.syncStatus,
|
|
6976
|
+
code: failure.syncErrorCode,
|
|
6977
|
+
rateLimitedUntil: null
|
|
6978
|
+
};
|
|
6979
|
+
}
|
|
6980
|
+
if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
|
|
6981
|
+
await input.store.setSyncStatus({
|
|
6982
|
+
syncStatus: "ok",
|
|
6983
|
+
syncErrorCode: null,
|
|
6984
|
+
syncErrorMessage: null,
|
|
6985
|
+
rateLimitedUntil: null,
|
|
6986
|
+
lastSyncedAt: now()
|
|
6987
|
+
});
|
|
6988
|
+
return { status: "unchanged", contentVersion: vault.contentVersion };
|
|
6989
|
+
}
|
|
6990
|
+
const items = await input.provider.listItems(input.ctx, target.externalVaultId);
|
|
6991
|
+
const plan = planProviderMirror(items, vault.contentVersion);
|
|
6992
|
+
await input.store.replaceMirror(plan);
|
|
6993
|
+
await input.store.setSyncStatus({
|
|
6994
|
+
syncStatus: "ok",
|
|
6995
|
+
syncErrorCode: null,
|
|
6996
|
+
syncErrorMessage: null,
|
|
6997
|
+
rateLimitedUntil: null,
|
|
6998
|
+
lastSyncedAt: now()
|
|
6999
|
+
});
|
|
7000
|
+
return {
|
|
7001
|
+
status: "synced",
|
|
7002
|
+
contentVersion: plan.contentVersion,
|
|
7003
|
+
itemCount: plan.items.length,
|
|
7004
|
+
truncated: plan.truncated
|
|
7005
|
+
};
|
|
7006
|
+
} catch (error) {
|
|
7007
|
+
if (!isVaultProviderError(error)) throw error;
|
|
7008
|
+
const failure = providerSyncFailure(error, now());
|
|
7009
|
+
await input.store.setSyncStatus(failure);
|
|
7010
|
+
return {
|
|
7011
|
+
status: "failed",
|
|
7012
|
+
syncStatus: failure.syncStatus,
|
|
7013
|
+
code: failure.syncErrorCode,
|
|
7014
|
+
rateLimitedUntil: failure.rateLimitedUntil
|
|
7015
|
+
};
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
function databaseProviderMirrorStore(db, context, vaultId) {
|
|
7019
|
+
return {
|
|
7020
|
+
async replaceMirror(plan) {
|
|
7021
|
+
await upsertProviderVaultItems(db, context, {
|
|
7022
|
+
vaultId,
|
|
7023
|
+
contentVersion: plan.contentVersion,
|
|
7024
|
+
items: plan.items.map((item) => ({
|
|
7025
|
+
providerItemRef: item.externalItemId,
|
|
7026
|
+
providerVersion: item.version,
|
|
7027
|
+
// Passkeys live only in hosted vaults (D20); providers never mirror them.
|
|
7028
|
+
type: item.type === "passkey" ? "login" : item.type,
|
|
7029
|
+
// The base name: the routine de-duplicates against the stored mirror
|
|
7030
|
+
// so names stay sticky per provider item.
|
|
7031
|
+
name: providerSecretName(item.providerName),
|
|
7032
|
+
websites: item.websites,
|
|
7033
|
+
primaryFieldRef: item.primaryExternalFieldId,
|
|
7034
|
+
fields: item.fields.map((field) => ({
|
|
7035
|
+
providerFieldRef: field.externalFieldId,
|
|
7036
|
+
name: field.name,
|
|
7037
|
+
kind: field.kind
|
|
7038
|
+
}))
|
|
7039
|
+
}))
|
|
7040
|
+
});
|
|
7041
|
+
},
|
|
7042
|
+
async setSyncStatus(update) {
|
|
7043
|
+
await setVaultSyncStatus(db, context, {
|
|
7044
|
+
vaultId,
|
|
7045
|
+
status: update.syncStatus,
|
|
7046
|
+
errorCode: update.syncErrorCode,
|
|
7047
|
+
errorMessage: update.syncErrorMessage,
|
|
7048
|
+
rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null
|
|
7049
|
+
});
|
|
7050
|
+
}
|
|
7051
|
+
};
|
|
7052
|
+
}
|
|
7053
|
+
async function syncLinkedProviderVault(input) {
|
|
7054
|
+
const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
|
|
7055
|
+
const store = databaseProviderMirrorStore(input.db, input.context, input.vaultId);
|
|
7056
|
+
let provider;
|
|
7057
|
+
try {
|
|
7058
|
+
provider = await input.providers.forVault({
|
|
7059
|
+
source: target.source,
|
|
7060
|
+
providerConnectionId: target.providerConnectionId
|
|
7061
|
+
});
|
|
7062
|
+
} catch (error) {
|
|
7063
|
+
if (!isVaultProviderError(error)) throw error;
|
|
7064
|
+
const failure = providerSyncFailure(error, (input.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
7065
|
+
await store.setSyncStatus(failure);
|
|
7066
|
+
return {
|
|
7067
|
+
status: "failed",
|
|
7068
|
+
syncStatus: failure.syncStatus,
|
|
7069
|
+
code: failure.syncErrorCode,
|
|
7070
|
+
rateLimitedUntil: failure.rateLimitedUntil
|
|
7071
|
+
};
|
|
7072
|
+
}
|
|
7073
|
+
return await syncProviderVault({
|
|
7074
|
+
provider,
|
|
7075
|
+
ctx: {
|
|
7076
|
+
connectionId: target.providerConnectionId,
|
|
7077
|
+
budget: input.budget ?? { take() {
|
|
7078
|
+
} }
|
|
7079
|
+
},
|
|
7080
|
+
target: {
|
|
7081
|
+
externalVaultId: target.providerVaultRef,
|
|
7082
|
+
mirroredContentVersion: target.providerContentVersion,
|
|
7083
|
+
rateLimitedUntil: target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null
|
|
7084
|
+
},
|
|
7085
|
+
store,
|
|
7086
|
+
...input.now ? { now: input.now } : {},
|
|
7087
|
+
...input.force ? { force: true } : {}
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
7090
|
+
var LINKED_VAULT_SEARCH_MAX_AGE_MS = 10 * 6e4;
|
|
7091
|
+
var LINKED_VAULT_MISS_MAX_AGE_MS = 2 * 6e4;
|
|
7092
|
+
var LINKED_VAULT_REFRESH_WAIT_MS = 8e3;
|
|
7093
|
+
function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
|
|
7094
|
+
if (vault.source === "opengeni") return false;
|
|
7095
|
+
if (vault.rateLimitedUntil && new Date(vault.rateLimitedUntil).getTime() > now.getTime()) {
|
|
7096
|
+
return false;
|
|
7097
|
+
}
|
|
7098
|
+
const last = vault.syncStatus === "ok" || vault.syncStatus === "syncing" ? vault.lastSyncedAt : vault.updatedAt;
|
|
7099
|
+
if (!last) return true;
|
|
7100
|
+
return now.getTime() - new Date(last).getTime() >= maxAgeMs;
|
|
7101
|
+
}
|
|
7102
|
+
var linkedVaultRefreshes = /* @__PURE__ */ new Map();
|
|
7103
|
+
async function refreshStaleLinkedVaults(input) {
|
|
7104
|
+
const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7105
|
+
const stale = input.vaults.filter((vault) => linkedVaultNeedsRefresh(vault, input.maxAgeMs, now));
|
|
7106
|
+
if (stale.length === 0) return [];
|
|
7107
|
+
const changed = [];
|
|
7108
|
+
const runs = stale.map((vault) => {
|
|
7109
|
+
let run = linkedVaultRefreshes.get(vault.id);
|
|
7110
|
+
if (!run) {
|
|
7111
|
+
run = syncLinkedProviderVault({
|
|
7112
|
+
db: input.db,
|
|
7113
|
+
context: input.context,
|
|
7114
|
+
vaultId: vault.id,
|
|
7115
|
+
providers: input.providers,
|
|
7116
|
+
...input.now ? { now: input.now } : {}
|
|
7117
|
+
}).catch(() => null).finally(() => linkedVaultRefreshes.delete(vault.id));
|
|
7118
|
+
linkedVaultRefreshes.set(vault.id, run);
|
|
7119
|
+
}
|
|
7120
|
+
return run.then((result) => {
|
|
7121
|
+
if (result?.status === "synced") changed.push(vault.id);
|
|
7122
|
+
});
|
|
7123
|
+
});
|
|
7124
|
+
let timer;
|
|
7125
|
+
await Promise.race([
|
|
7126
|
+
Promise.all(runs),
|
|
7127
|
+
new Promise((resolve) => {
|
|
7128
|
+
timer = setTimeout(resolve, input.waitMs ?? LINKED_VAULT_REFRESH_WAIT_MS);
|
|
7129
|
+
timer.unref?.();
|
|
7130
|
+
})
|
|
7131
|
+
]);
|
|
7132
|
+
if (timer) clearTimeout(timer);
|
|
7133
|
+
return [...changed];
|
|
7134
|
+
}
|
|
7135
|
+
|
|
7136
|
+
// src/domain/vaults/secret-resolver.ts
|
|
6820
7137
|
function lookupFor(reference, index) {
|
|
6821
7138
|
return reference.target.form === "id" ? {
|
|
6822
7139
|
index,
|
|
@@ -6971,12 +7288,12 @@ var attemptBudgets = /* @__PURE__ */ new Map();
|
|
|
6971
7288
|
var ATTEMPT_BUDGET_CACHE_LIMIT = 4096;
|
|
6972
7289
|
function sharedAttemptBudget(principal) {
|
|
6973
7290
|
if (principal.kind !== "attempt") {
|
|
6974
|
-
return createProviderCallBudget(
|
|
7291
|
+
return createProviderCallBudget(VAULT_LIMITS2.providerReadsPerAttempt);
|
|
6975
7292
|
}
|
|
6976
7293
|
const key = `${principal.attemptId}:${principal.executionGeneration}`;
|
|
6977
7294
|
let budget = attemptBudgets.get(key);
|
|
6978
7295
|
if (!budget) {
|
|
6979
|
-
budget = createProviderCallBudget(
|
|
7296
|
+
budget = createProviderCallBudget(VAULT_LIMITS2.providerReadsPerAttempt);
|
|
6980
7297
|
attemptBudgets.set(key, budget);
|
|
6981
7298
|
while (attemptBudgets.size > ATTEMPT_BUDGET_CACHE_LIMIT) {
|
|
6982
7299
|
attemptBudgets.delete(attemptBudgets.keys().next().value);
|
|
@@ -7005,20 +7322,40 @@ function createSecretResolver(deps, principal, opts) {
|
|
|
7005
7322
|
null
|
|
7006
7323
|
);
|
|
7007
7324
|
}
|
|
7008
|
-
const
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7325
|
+
const choose = async () => {
|
|
7326
|
+
const { meta: meta2, candidates } = await resolveSecretReferenceCandidates(deps.db, context, {
|
|
7327
|
+
refs: references.map((reference, index) => lookupFor(reference, index))
|
|
7328
|
+
});
|
|
7329
|
+
const failures2 = [];
|
|
7330
|
+
const chosen2 = [];
|
|
7331
|
+
references.forEach((reference, index) => {
|
|
7332
|
+
const outcome = chooseSecretCandidate(
|
|
7333
|
+
reference,
|
|
7334
|
+
candidates.filter((candidate) => candidate.refIndex === index),
|
|
7335
|
+
meta2
|
|
7336
|
+
);
|
|
7337
|
+
if ("failure" in outcome) failures2.push(outcome.failure);
|
|
7338
|
+
else chosen2.push({ reference, candidate: outcome.candidate });
|
|
7339
|
+
});
|
|
7340
|
+
return { meta: meta2, failures: failures2, chosen: chosen2 };
|
|
7341
|
+
};
|
|
7342
|
+
let { meta, failures, chosen } = await choose();
|
|
7343
|
+
if (failures.some((failure) => failure.code === "not_found")) {
|
|
7344
|
+
const registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId);
|
|
7345
|
+
if (registry) {
|
|
7346
|
+
const reachable = await listVaultsForSubject(deps.db, context, {
|
|
7347
|
+
workspaceId: principal.workspaceId
|
|
7348
|
+
});
|
|
7349
|
+
const changed = await refreshStaleLinkedVaults({
|
|
7350
|
+
db: deps.db,
|
|
7351
|
+
context,
|
|
7352
|
+
vaults: reachable,
|
|
7353
|
+
providers: registry,
|
|
7354
|
+
maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS
|
|
7355
|
+
});
|
|
7356
|
+
if (changed.length > 0) ({ meta, failures, chosen } = await choose());
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7022
7359
|
const hasActingHuman = meta.humanExpected && meta.humanValid;
|
|
7023
7360
|
if (failures.length > 0) {
|
|
7024
7361
|
throw new SecretReferenceError(
|
|
@@ -7051,7 +7388,7 @@ function createSecretResolver(deps, principal, opts) {
|
|
|
7051
7388
|
providerValues.set(`${first.itemId}|${value.externalFieldId}`, value.material);
|
|
7052
7389
|
}
|
|
7053
7390
|
} catch (error) {
|
|
7054
|
-
const code =
|
|
7391
|
+
const code = isVaultProviderError2(error) ? providerFailureCode(error.code) : "provider_unavailable";
|
|
7055
7392
|
for (const entry of group) {
|
|
7056
7393
|
failures.push({
|
|
7057
7394
|
raw: entry.reference.raw,
|
|
@@ -15483,261 +15820,6 @@ function createWebSearchBilling(deps) {
|
|
|
15483
15820
|
};
|
|
15484
15821
|
}
|
|
15485
15822
|
|
|
15486
|
-
// src/domain/vaults/provider-sync.ts
|
|
15487
|
-
import {
|
|
15488
|
-
isValidSecretName,
|
|
15489
|
-
VAULT_DEFAULT_PRIMARY_FIELD,
|
|
15490
|
-
VAULT_LIMITS as VAULT_LIMITS2
|
|
15491
|
-
} from "@opengeni/contracts";
|
|
15492
|
-
import {
|
|
15493
|
-
getProviderVaultSyncTarget,
|
|
15494
|
-
setVaultSyncStatus,
|
|
15495
|
-
upsertProviderVaultItems
|
|
15496
|
-
} from "@opengeni/db";
|
|
15497
|
-
import {
|
|
15498
|
-
isVaultProviderError as isVaultProviderError2,
|
|
15499
|
-
VaultProviderError as VaultProviderError2
|
|
15500
|
-
} from "@opengeni/vaults";
|
|
15501
|
-
var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
|
|
15502
|
-
var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
|
|
15503
|
-
function nameKey(name) {
|
|
15504
|
-
return name.normalize("NFC").trim().toLowerCase();
|
|
15505
|
-
}
|
|
15506
|
-
function truncateChars(value, max) {
|
|
15507
|
-
const chars = [...value];
|
|
15508
|
-
return chars.length > max ? chars.slice(0, max).join("") : value;
|
|
15509
|
-
}
|
|
15510
|
-
function providerSecretName(raw, fallback = "Untitled") {
|
|
15511
|
-
let name = truncateChars(
|
|
15512
|
-
raw.normalize("NFC").replace(FORBIDDEN_NAME_CHARACTERS, "-").trim(),
|
|
15513
|
-
VAULT_LIMITS2.nameMaxChars
|
|
15514
|
-
).trim();
|
|
15515
|
-
if (!name) name = fallback;
|
|
15516
|
-
if (/^id:/iu.test(name)) name = `id-${name.slice(3)}`.trim() || fallback;
|
|
15517
|
-
return isValidSecretName(name) ? name : fallback;
|
|
15518
|
-
}
|
|
15519
|
-
function uniqueProviderName(name, taken) {
|
|
15520
|
-
let candidate = name;
|
|
15521
|
-
for (let n = 2; taken.has(nameKey(candidate)); n += 1) {
|
|
15522
|
-
const suffix = ` (${n})`;
|
|
15523
|
-
candidate = `${truncateChars(name, VAULT_LIMITS2.nameMaxChars - suffix.length).trimEnd()}${suffix}`;
|
|
15524
|
-
}
|
|
15525
|
-
taken.add(nameKey(candidate));
|
|
15526
|
-
return candidate;
|
|
15527
|
-
}
|
|
15528
|
-
var PRIMARY_FIELD_FALLBACK = ["password", "value", "credential", "note"];
|
|
15529
|
-
function providerPrimaryFieldId(item) {
|
|
15530
|
-
const candidates = item.fields.filter((field) => field.kind !== "totp");
|
|
15531
|
-
const byName = (name) => candidates.find((field) => nameKey(field.name) === name)?.externalFieldId ?? null;
|
|
15532
|
-
const typeDefault = VAULT_DEFAULT_PRIMARY_FIELD[item.type];
|
|
15533
|
-
if (typeDefault) {
|
|
15534
|
-
const found = byName(typeDefault.toLowerCase());
|
|
15535
|
-
if (found) return found;
|
|
15536
|
-
}
|
|
15537
|
-
for (const name of PRIMARY_FIELD_FALLBACK) {
|
|
15538
|
-
const found = byName(name);
|
|
15539
|
-
if (found) return found;
|
|
15540
|
-
}
|
|
15541
|
-
if (item.type === "file") {
|
|
15542
|
-
const file = candidates.find((field) => field.kind === "file");
|
|
15543
|
-
if (file) return file.externalFieldId;
|
|
15544
|
-
}
|
|
15545
|
-
return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
|
|
15546
|
-
}
|
|
15547
|
-
function planProviderMirror(items, contentVersion) {
|
|
15548
|
-
const ordered = [...items].sort(
|
|
15549
|
-
(a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
|
|
15550
|
-
);
|
|
15551
|
-
const kept = ordered.slice(0, VAULT_LIMITS2.itemsPerVault);
|
|
15552
|
-
const takenItems = /* @__PURE__ */ new Set();
|
|
15553
|
-
const mirrored = kept.map((item) => {
|
|
15554
|
-
const name = uniqueProviderName(providerSecretName(item.name), takenItems);
|
|
15555
|
-
const takenFields = /* @__PURE__ */ new Set();
|
|
15556
|
-
const fields = item.fields.slice(0, VAULT_LIMITS2.fieldsPerItem).map((field, position) => {
|
|
15557
|
-
let fieldName = providerSecretName(
|
|
15558
|
-
field.name,
|
|
15559
|
-
field.kind === "totp" ? "one-time password" : "field"
|
|
15560
|
-
);
|
|
15561
|
-
if (nameKey(fieldName) === "otp") fieldName = "otp code";
|
|
15562
|
-
return {
|
|
15563
|
-
externalFieldId: field.externalFieldId,
|
|
15564
|
-
name: uniqueProviderName(fieldName, takenFields),
|
|
15565
|
-
kind: field.kind,
|
|
15566
|
-
position
|
|
15567
|
-
};
|
|
15568
|
-
});
|
|
15569
|
-
return {
|
|
15570
|
-
externalItemId: item.externalItemId,
|
|
15571
|
-
name,
|
|
15572
|
-
providerName: item.name,
|
|
15573
|
-
type: item.type,
|
|
15574
|
-
websites: item.websites.filter((website) => website.length <= VAULT_LIMITS2.websiteMaxChars).slice(0, VAULT_LIMITS2.websitesPerItem),
|
|
15575
|
-
fields,
|
|
15576
|
-
primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
|
|
15577
|
-
hasTotp: item.fields.some((field) => field.kind === "totp"),
|
|
15578
|
-
version: item.version,
|
|
15579
|
-
updatedAt: item.updatedAt
|
|
15580
|
-
};
|
|
15581
|
-
});
|
|
15582
|
-
return { contentVersion, items: mirrored, truncated: ordered.length - kept.length };
|
|
15583
|
-
}
|
|
15584
|
-
function providerSyncFailure(error, now) {
|
|
15585
|
-
const code = isVaultProviderError2(error) ? error.code : "provider_unavailable";
|
|
15586
|
-
const message = isVaultProviderError2(error) ? error.message : "the vault provider could not be reached";
|
|
15587
|
-
if (code === "provider_rate_limited") {
|
|
15588
|
-
const retryAfterMs = isVaultProviderError2(error) && error.retryAfterMs && error.retryAfterMs > 0 ? error.retryAfterMs : PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS;
|
|
15589
|
-
return {
|
|
15590
|
-
syncStatus: "rate_limited",
|
|
15591
|
-
syncErrorCode: code,
|
|
15592
|
-
syncErrorMessage: message,
|
|
15593
|
-
rateLimitedUntil: new Date(now.getTime() + retryAfterMs),
|
|
15594
|
-
lastSyncedAt: null
|
|
15595
|
-
};
|
|
15596
|
-
}
|
|
15597
|
-
return {
|
|
15598
|
-
syncStatus: code === "provider_auth_expired" || code === "provider_auth_invalid" ? "auth_expired" : "error",
|
|
15599
|
-
syncErrorCode: code,
|
|
15600
|
-
syncErrorMessage: message,
|
|
15601
|
-
rateLimitedUntil: null,
|
|
15602
|
-
lastSyncedAt: null
|
|
15603
|
-
};
|
|
15604
|
-
}
|
|
15605
|
-
async function syncProviderVault(input) {
|
|
15606
|
-
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
15607
|
-
const { target } = input;
|
|
15608
|
-
if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime() && !input.force) {
|
|
15609
|
-
return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
|
|
15610
|
-
}
|
|
15611
|
-
try {
|
|
15612
|
-
const vaults = await input.provider.listVaults(input.ctx);
|
|
15613
|
-
const vault = vaults.find((candidate) => candidate.externalVaultId === target.externalVaultId);
|
|
15614
|
-
if (!vault) {
|
|
15615
|
-
const failure = providerSyncFailure(
|
|
15616
|
-
new VaultProviderError2(
|
|
15617
|
-
"provider_not_found",
|
|
15618
|
-
"the linked provider vault no longer exists or is no longer shared with this connection"
|
|
15619
|
-
),
|
|
15620
|
-
now()
|
|
15621
|
-
);
|
|
15622
|
-
await input.store.setSyncStatus(failure);
|
|
15623
|
-
return {
|
|
15624
|
-
status: "failed",
|
|
15625
|
-
syncStatus: failure.syncStatus,
|
|
15626
|
-
code: failure.syncErrorCode,
|
|
15627
|
-
rateLimitedUntil: null
|
|
15628
|
-
};
|
|
15629
|
-
}
|
|
15630
|
-
if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
|
|
15631
|
-
await input.store.setSyncStatus({
|
|
15632
|
-
syncStatus: "ok",
|
|
15633
|
-
syncErrorCode: null,
|
|
15634
|
-
syncErrorMessage: null,
|
|
15635
|
-
rateLimitedUntil: null,
|
|
15636
|
-
lastSyncedAt: now()
|
|
15637
|
-
});
|
|
15638
|
-
return { status: "unchanged", contentVersion: vault.contentVersion };
|
|
15639
|
-
}
|
|
15640
|
-
const items = await input.provider.listItems(input.ctx, target.externalVaultId);
|
|
15641
|
-
const plan = planProviderMirror(items, vault.contentVersion);
|
|
15642
|
-
await input.store.replaceMirror(plan);
|
|
15643
|
-
await input.store.setSyncStatus({
|
|
15644
|
-
syncStatus: "ok",
|
|
15645
|
-
syncErrorCode: null,
|
|
15646
|
-
syncErrorMessage: null,
|
|
15647
|
-
rateLimitedUntil: null,
|
|
15648
|
-
lastSyncedAt: now()
|
|
15649
|
-
});
|
|
15650
|
-
return {
|
|
15651
|
-
status: "synced",
|
|
15652
|
-
contentVersion: plan.contentVersion,
|
|
15653
|
-
itemCount: plan.items.length,
|
|
15654
|
-
truncated: plan.truncated
|
|
15655
|
-
};
|
|
15656
|
-
} catch (error) {
|
|
15657
|
-
if (!isVaultProviderError2(error)) throw error;
|
|
15658
|
-
const failure = providerSyncFailure(error, now());
|
|
15659
|
-
await input.store.setSyncStatus(failure);
|
|
15660
|
-
return {
|
|
15661
|
-
status: "failed",
|
|
15662
|
-
syncStatus: failure.syncStatus,
|
|
15663
|
-
code: failure.syncErrorCode,
|
|
15664
|
-
rateLimitedUntil: failure.rateLimitedUntil
|
|
15665
|
-
};
|
|
15666
|
-
}
|
|
15667
|
-
}
|
|
15668
|
-
function databaseProviderMirrorStore(db, context, vaultId) {
|
|
15669
|
-
return {
|
|
15670
|
-
async replaceMirror(plan) {
|
|
15671
|
-
await upsertProviderVaultItems(db, context, {
|
|
15672
|
-
vaultId,
|
|
15673
|
-
contentVersion: plan.contentVersion,
|
|
15674
|
-
items: plan.items.map((item) => ({
|
|
15675
|
-
providerItemRef: item.externalItemId,
|
|
15676
|
-
providerVersion: item.version,
|
|
15677
|
-
// Passkeys live only in hosted vaults (D20); providers never mirror them.
|
|
15678
|
-
type: item.type === "passkey" ? "login" : item.type,
|
|
15679
|
-
// The base name: the routine de-duplicates against the stored mirror
|
|
15680
|
-
// so names stay sticky per provider item.
|
|
15681
|
-
name: providerSecretName(item.providerName),
|
|
15682
|
-
websites: item.websites,
|
|
15683
|
-
primaryFieldRef: item.primaryExternalFieldId,
|
|
15684
|
-
fields: item.fields.map((field) => ({
|
|
15685
|
-
providerFieldRef: field.externalFieldId,
|
|
15686
|
-
name: field.name,
|
|
15687
|
-
kind: field.kind
|
|
15688
|
-
}))
|
|
15689
|
-
}))
|
|
15690
|
-
});
|
|
15691
|
-
},
|
|
15692
|
-
async setSyncStatus(update) {
|
|
15693
|
-
await setVaultSyncStatus(db, context, {
|
|
15694
|
-
vaultId,
|
|
15695
|
-
status: update.syncStatus,
|
|
15696
|
-
errorCode: update.syncErrorCode,
|
|
15697
|
-
errorMessage: update.syncErrorMessage,
|
|
15698
|
-
rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null
|
|
15699
|
-
});
|
|
15700
|
-
}
|
|
15701
|
-
};
|
|
15702
|
-
}
|
|
15703
|
-
async function syncLinkedProviderVault(input) {
|
|
15704
|
-
const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
|
|
15705
|
-
const store = databaseProviderMirrorStore(input.db, input.context, input.vaultId);
|
|
15706
|
-
let provider;
|
|
15707
|
-
try {
|
|
15708
|
-
provider = await input.providers.forVault({
|
|
15709
|
-
source: target.source,
|
|
15710
|
-
providerConnectionId: target.providerConnectionId
|
|
15711
|
-
});
|
|
15712
|
-
} catch (error) {
|
|
15713
|
-
if (!isVaultProviderError2(error)) throw error;
|
|
15714
|
-
const failure = providerSyncFailure(error, (input.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
15715
|
-
await store.setSyncStatus(failure);
|
|
15716
|
-
return {
|
|
15717
|
-
status: "failed",
|
|
15718
|
-
syncStatus: failure.syncStatus,
|
|
15719
|
-
code: failure.syncErrorCode,
|
|
15720
|
-
rateLimitedUntil: failure.rateLimitedUntil
|
|
15721
|
-
};
|
|
15722
|
-
}
|
|
15723
|
-
return await syncProviderVault({
|
|
15724
|
-
provider,
|
|
15725
|
-
ctx: {
|
|
15726
|
-
connectionId: target.providerConnectionId,
|
|
15727
|
-
budget: input.budget ?? { take() {
|
|
15728
|
-
} }
|
|
15729
|
-
},
|
|
15730
|
-
target: {
|
|
15731
|
-
externalVaultId: target.providerVaultRef,
|
|
15732
|
-
mirroredContentVersion: target.providerContentVersion,
|
|
15733
|
-
rateLimitedUntil: target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null
|
|
15734
|
-
},
|
|
15735
|
-
store,
|
|
15736
|
-
...input.now ? { now: input.now } : {},
|
|
15737
|
-
...input.force ? { force: true } : {}
|
|
15738
|
-
});
|
|
15739
|
-
}
|
|
15740
|
-
|
|
15741
15823
|
// src/workflow-wake-contract.ts
|
|
15742
15824
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
15743
15825
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
|
|
@@ -18102,7 +18184,7 @@ import {
|
|
|
18102
18184
|
getVaultItem,
|
|
18103
18185
|
listVaultFieldVersions,
|
|
18104
18186
|
listVaultItems,
|
|
18105
|
-
listVaultsForSubject,
|
|
18187
|
+
listVaultsForSubject as listVaultsForSubject2,
|
|
18106
18188
|
vaultItemCursor,
|
|
18107
18189
|
moveVaultItem,
|
|
18108
18190
|
readVaultFieldCiphertext,
|
|
@@ -18173,7 +18255,7 @@ async function providerFieldWrites(fields, existing, resolveReference, generated
|
|
|
18173
18255
|
if (input.fileBase64 !== void 0 || kind === "file") {
|
|
18174
18256
|
throw new VaultError(
|
|
18175
18257
|
"provider_unavailable",
|
|
18176
|
-
"files in connected vaults are read-only in
|
|
18258
|
+
"files in connected vaults are read-only in Opengeni",
|
|
18177
18259
|
{ field: input.name }
|
|
18178
18260
|
);
|
|
18179
18261
|
}
|
|
@@ -18235,7 +18317,7 @@ async function refreshAndFind(deps, vault, providerItemRef) {
|
|
|
18235
18317
|
if (error instanceof VaultError && error.code === "vault_item_not_found") {
|
|
18236
18318
|
throw new VaultError(
|
|
18237
18319
|
"provider_unavailable",
|
|
18238
|
-
"saved in the provider;
|
|
18320
|
+
"saved in the provider; Opengeni will show it after the next sync",
|
|
18239
18321
|
{ vaultId: vault.id }
|
|
18240
18322
|
);
|
|
18241
18323
|
}
|
|
@@ -18246,7 +18328,7 @@ async function writeProviderVaultItem(deps, input) {
|
|
|
18246
18328
|
const { vault, existing, change } = input;
|
|
18247
18329
|
assertProviderVaultWritable(vault);
|
|
18248
18330
|
if ((change.type ?? existing?.type) === "passkey") {
|
|
18249
|
-
throw new VaultError("vault_permission_denied", "passkeys live only in
|
|
18331
|
+
throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
|
|
18250
18332
|
}
|
|
18251
18333
|
let externalItemId;
|
|
18252
18334
|
let expectedVersion;
|
|
@@ -18637,7 +18719,7 @@ function createVaultService(deps) {
|
|
|
18637
18719
|
return session?.agent?.capabilities.secrets ?? true;
|
|
18638
18720
|
}
|
|
18639
18721
|
async function agentVault(principal, nameOrId) {
|
|
18640
|
-
const vaults = await
|
|
18722
|
+
const vaults = await listVaultsForSubject2(deps.db, principalContext(principal), {
|
|
18641
18723
|
workspaceId: principal.workspaceId
|
|
18642
18724
|
});
|
|
18643
18725
|
const byId = vaults.find((vault) => vault.id === nameOrId.toLowerCase());
|
|
@@ -18729,14 +18811,14 @@ function createVaultService(deps) {
|
|
|
18729
18811
|
}
|
|
18730
18812
|
return {
|
|
18731
18813
|
async list(actor, workspaceId) {
|
|
18732
|
-
const rows = await
|
|
18814
|
+
const rows = await listVaultsForSubject2(deps.db, subjectContext(actor, workspaceId), {
|
|
18733
18815
|
workspaceId
|
|
18734
18816
|
});
|
|
18735
18817
|
const authority = authorityOf(actor);
|
|
18736
18818
|
return rows.map((row) => toVaultSummary(row, authority));
|
|
18737
18819
|
},
|
|
18738
18820
|
async get(actor, workspaceId, vaultId) {
|
|
18739
|
-
const rows = await
|
|
18821
|
+
const rows = await listVaultsForSubject2(deps.db, subjectContext(actor, workspaceId), {
|
|
18740
18822
|
workspaceId
|
|
18741
18823
|
});
|
|
18742
18824
|
const row = rows.find((candidate) => candidate.id === vaultId);
|
|
@@ -18935,9 +19017,23 @@ function createVaultService(deps) {
|
|
|
18935
19017
|
},
|
|
18936
19018
|
async searchForAgent(principal, input) {
|
|
18937
19019
|
const context = principalContext(principal);
|
|
18938
|
-
|
|
19020
|
+
let reachable = await listVaultsForSubject2(deps.db, context, {
|
|
18939
19021
|
workspaceId: principal.workspaceId
|
|
18940
19022
|
});
|
|
19023
|
+
if (deps.providers) {
|
|
19024
|
+
const changed = await refreshStaleLinkedVaults({
|
|
19025
|
+
db: deps.db,
|
|
19026
|
+
context,
|
|
19027
|
+
vaults: reachable,
|
|
19028
|
+
providers: deps.providers,
|
|
19029
|
+
maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS
|
|
19030
|
+
});
|
|
19031
|
+
if (changed.length > 0) {
|
|
19032
|
+
reachable = await listVaultsForSubject2(deps.db, context, {
|
|
19033
|
+
workspaceId: principal.workspaceId
|
|
19034
|
+
});
|
|
19035
|
+
}
|
|
19036
|
+
}
|
|
18941
19037
|
const byTier = (vaults2) => [...vaults2].sort(
|
|
18942
19038
|
(left, right) => left.tier - right.tier || left.name.localeCompare(right.name)
|
|
18943
19039
|
);
|
|
@@ -27870,6 +27966,9 @@ export {
|
|
|
27870
27966
|
KnowledgeVectorCreditsDisabledError,
|
|
27871
27967
|
KnowledgeVectorFundingError,
|
|
27872
27968
|
KnowledgeVectorQueryRejectedError,
|
|
27969
|
+
LINKED_VAULT_MISS_MAX_AGE_MS,
|
|
27970
|
+
LINKED_VAULT_REFRESH_WAIT_MS,
|
|
27971
|
+
LINKED_VAULT_SEARCH_MAX_AGE_MS,
|
|
27873
27972
|
MAX_CHECKS_PER_RIG,
|
|
27874
27973
|
MAX_CREDENTIAL_HOOKS_PER_RIG,
|
|
27875
27974
|
MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES,
|
|
@@ -28216,6 +28315,7 @@ export {
|
|
|
28216
28315
|
knowledgeContextForAccess,
|
|
28217
28316
|
knowledgeContextForGateway,
|
|
28218
28317
|
legacySessionAgentCapabilities,
|
|
28318
|
+
linkedVaultNeedsRefresh,
|
|
28219
28319
|
listExternalActorWorkspaces,
|
|
28220
28320
|
listFleet,
|
|
28221
28321
|
listGitHubActionPolicyActors,
|
|
@@ -28342,6 +28442,7 @@ export {
|
|
|
28342
28442
|
recordWorkspaceUsage,
|
|
28343
28443
|
refreshCoreCodexModelEntitlements,
|
|
28344
28444
|
refreshScheduledTaskAccess,
|
|
28445
|
+
refreshStaleLinkedVaults,
|
|
28345
28446
|
registerAutomationAdapter,
|
|
28346
28447
|
registerProductUsageMetricBaselines,
|
|
28347
28448
|
rejectSkill,
|