@opengeni/db 0.28.9 → 0.31.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/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +91 -0
- package/dist/browser-identities.d.ts +128 -0
- package/dist/browser-sessions.d.ts +272 -0
- package/dist/browser-state-artifacts.d.ts +17 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +192 -0
- package/dist/chunk-F5FT7BEZ.js +3535 -0
- package/dist/chunk-F5FT7BEZ.js.map +1 -0
- package/dist/chunk-ISI2TIUG.js +639 -0
- package/dist/chunk-ISI2TIUG.js.map +1 -0
- package/dist/chunk-MWDVXQOL.js +11639 -0
- package/dist/chunk-MWDVXQOL.js.map +1 -0
- package/dist/chunk-NBE6CHRI.js +955 -0
- package/dist/chunk-NBE6CHRI.js.map +1 -0
- package/dist/{chunk-VHBFHMPC.js → chunk-YHZXQ7HP.js} +754 -11
- package/dist/chunk-YHZXQ7HP.js.map +1 -0
- package/dist/codemode-operations.d.ts +117 -0
- package/dist/codex-token-resolver.d.ts +2 -2
- package/dist/company-profile-schema.d.ts +798 -0
- package/dist/company-profile.d.ts +268 -0
- package/dist/computer-sessions.d.ts +109 -0
- package/dist/connection-token-resolver.d.ts +9 -4
- package/dist/database.d.ts +52 -3
- package/dist/editable-artifact-durable-export.d.ts +153 -0
- package/dist/editable-artifact-durable-export.js +788 -0
- package/dist/editable-artifact-durable-export.js.map +1 -0
- package/dist/editable-artifact-materialization.d.ts +123 -0
- package/dist/editable-artifacts-schema.d.ts +4152 -0
- package/dist/editable-artifacts.d.ts +610 -0
- package/dist/editable-artifacts.js +26 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/generated-images.d.ts +139 -0
- package/dist/index.d.ts +698 -111
- package/dist/index.js +27573 -10322
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +51 -4
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-features.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +3 -0
- package/dist/interaction-schema.d.ts +5372 -0
- package/dist/knowledge-source-sync-schema.d.ts +1679 -0
- package/dist/knowledge-source-sync.d.ts +274 -0
- package/dist/memory-governance.d.ts +2 -2
- package/dist/memory-slack-delivery.d.ts +101 -0
- package/dist/new-session-drafts.d.ts +2 -2
- package/dist/pack-components.d.ts +76 -0
- package/dist/pack-installations.d.ts +86 -0
- package/dist/persistence-errors.d.ts +3 -3
- package/dist/plugin-packages.d.ts +126 -0
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +9 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +18 -6
- package/dist/schema.d.ts +18342 -9099
- package/dist/schema.js +165 -1
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +4 -4
- package/dist/session-queue-commands.d.ts +19 -11
- package/dist/session-realtime-context.d.ts +2 -2
- package/dist/session-realtime-ledger.d.ts +4 -4
- package/dist/session-realtime-mirror.d.ts +2 -2
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +7 -7
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +216 -0
- package/dist/video-generation.js +66 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/dist/workspace-learning-policy-schema.d.ts +932 -0
- package/dist/workspace-learning-policy.d.ts +161 -0
- package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
- package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
- package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
- package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
- package/drizzle/0187_generated_image_artifacts.sql +173 -0
- package/drizzle/0188_image_generation_retention_failure.sql +25 -0
- package/drizzle/0189_retained_session_image_formats.sql +13 -0
- package/drizzle/0190_timeline_annotations.sql +31 -0
- package/drizzle/0191_editable_artifact_engine.sql +3853 -0
- package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
- package/drizzle/0193_editable_artifact_authorization.sql +190 -0
- package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
- package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
- package/drizzle/0196_rig_provider_images.sql +152 -0
- package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
- package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
- package/drizzle/0199_workspace_learning_policy.sql +980 -0
- package/drizzle/0201_company_profile_authority.sql +849 -0
- package/drizzle/0202_document_index_checkpoints.sql +85 -0
- package/drizzle/0203_durable_video_generation.sql +353 -0
- package/drizzle/0204_video_generation_funding.sql +44 -0
- package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
- package/drizzle/0206_browser_sessions.sql +646 -0
- package/drizzle/0207_browser_identities.sql +379 -0
- package/drizzle/0208_attached_browser_devices.sql +165 -0
- package/drizzle/0209_computer_sessions.sql +575 -0
- package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
- package/drizzle/0211_editable_artifact_session_links.sql +58 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/package.json +18 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +1896 -0
- package/src/browser-identities.ts +1168 -0
- package/src/browser-sessions.ts +2005 -0
- package/src/browser-state-artifacts.ts +59 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1663 -0
- package/src/codemode-operations.ts +674 -0
- package/src/company-profile-schema.ts +143 -0
- package/src/company-profile.ts +968 -0
- package/src/computer-sessions.ts +1130 -0
- package/src/connection-token-resolver.ts +270 -25
- package/src/database.ts +427 -23
- package/src/editable-artifact-durable-export.ts +1201 -0
- package/src/editable-artifact-materialization.ts +893 -0
- package/src/editable-artifacts-schema.ts +879 -0
- package/src/editable-artifacts.ts +5404 -0
- package/src/generated-images.ts +740 -0
- package/src/index.ts +5917 -1182
- package/src/insights.ts +160 -13
- package/src/integration-bindings.ts +455 -0
- package/src/integration-features.ts +859 -0
- package/src/interaction-revisions.ts +40 -0
- package/src/interaction-schema.ts +1303 -0
- package/src/knowledge-source-sync-schema.ts +192 -0
- package/src/knowledge-source-sync.ts +1922 -0
- package/src/memory-slack-delivery.ts +801 -0
- package/src/pack-components.ts +954 -0
- package/src/pack-installations.ts +929 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +302 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +508 -5
- package/src/schema.ts +5117 -3119
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +8 -5
- package/src/session-queue-commands.ts +187 -14
- package/src/session-realtime-context.ts +2 -2
- package/src/session-realtime-ledger.ts +3 -3
- package/src/session-realtime.ts +7 -7
- package/src/session-tool-call-settlement.ts +9 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/video-generation.ts +1452 -0
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js.map +0 -1
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { stableJson } from "@opengeni/contracts";
|
|
4
|
+
import { and, asc, eq, or, sql } from "drizzle-orm";
|
|
5
|
+
|
|
6
|
+
import type { Database } from "./database";
|
|
7
|
+
import { effectiveCapabilityOwnerSql } from "./capability-components";
|
|
8
|
+
import * as schema from "./schema";
|
|
9
|
+
|
|
10
|
+
export type IntegrationFeatureBindingOwner = {
|
|
11
|
+
kind: "direct" | "plugin" | "pack" | "migration";
|
|
12
|
+
id: string;
|
|
13
|
+
removable: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type UpsertIntegrationFeatureBindingInput = {
|
|
17
|
+
accountId: string;
|
|
18
|
+
workspaceId: string;
|
|
19
|
+
integrationFacetInstallationId: string;
|
|
20
|
+
featureFacetId: string;
|
|
21
|
+
bindingKey: string;
|
|
22
|
+
displayName: string;
|
|
23
|
+
runtimeKey?: string | null;
|
|
24
|
+
connectionId?: string | null;
|
|
25
|
+
config: Record<string, unknown>;
|
|
26
|
+
createdBySubjectId: string;
|
|
27
|
+
owner: IntegrationFeatureBindingOwner;
|
|
28
|
+
expectedVersion?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export class IntegrationFeatureBindingVersionConflictError extends Error {
|
|
32
|
+
readonly name = "IntegrationFeatureBindingVersionConflictError";
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
readonly bindingKey: string,
|
|
36
|
+
readonly expectedVersion: number,
|
|
37
|
+
readonly actualVersion: number,
|
|
38
|
+
) {
|
|
39
|
+
super(
|
|
40
|
+
`Integration instance ${bindingKey} changed: expected version ${expectedVersion}, current version ${actualVersion}`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class IntegrationFeatureBindingVersionRequiredError extends Error {
|
|
46
|
+
readonly name = "IntegrationFeatureBindingVersionRequiredError";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class IntegrationFeatureBindingOwnershipConflictError extends Error {
|
|
50
|
+
readonly name = "IntegrationFeatureBindingOwnershipConflictError";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function integrationBindingKey(
|
|
54
|
+
connectionId: string | null | undefined,
|
|
55
|
+
requested?: string | null,
|
|
56
|
+
): string {
|
|
57
|
+
const explicit = requested?.trim().toLowerCase();
|
|
58
|
+
if (explicit) {
|
|
59
|
+
assertBindingKey(explicit);
|
|
60
|
+
return explicit;
|
|
61
|
+
}
|
|
62
|
+
if (!connectionId) return "default";
|
|
63
|
+
return `account-${sha256(`connection\0${connectionId}`).slice(0, 24)}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function integrationRuntimeKey(baseServerId: string, bindingKey: string): string {
|
|
67
|
+
assertBindingKey(bindingKey);
|
|
68
|
+
const suffix = sha256(`integration-instance\0${baseServerId}\0${bindingKey}`).slice(0, 16);
|
|
69
|
+
const head = baseServerId.slice(0, 110).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
70
|
+
const runtimeKey = `${head}_${suffix}`;
|
|
71
|
+
if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(runtimeKey)) {
|
|
72
|
+
throw new Error("Integration instance runtime key is invalid");
|
|
73
|
+
}
|
|
74
|
+
return runtimeKey;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function ensureIntegrationFeatureFacet(
|
|
78
|
+
db: Database,
|
|
79
|
+
input: {
|
|
80
|
+
integrationFacetId: string;
|
|
81
|
+
featureKey: string;
|
|
82
|
+
kind:
|
|
83
|
+
| "tools"
|
|
84
|
+
| "knowledge_source"
|
|
85
|
+
| "inbound_trigger"
|
|
86
|
+
| "delivery_destination"
|
|
87
|
+
| "identity_link";
|
|
88
|
+
configSchema?: Record<string, unknown>;
|
|
89
|
+
capabilities?: Record<string, unknown>;
|
|
90
|
+
},
|
|
91
|
+
): Promise<typeof schema.integrationFeatureFacets.$inferSelect> {
|
|
92
|
+
const [existing] = await db
|
|
93
|
+
.select()
|
|
94
|
+
.from(schema.integrationFeatureFacets)
|
|
95
|
+
.where(
|
|
96
|
+
and(
|
|
97
|
+
eq(schema.integrationFeatureFacets.integrationFacetId, input.integrationFacetId),
|
|
98
|
+
eq(schema.integrationFeatureFacets.featureKey, input.featureKey),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
.limit(1);
|
|
102
|
+
const configSchema = input.configSchema ?? {};
|
|
103
|
+
const capabilities = input.capabilities ?? {};
|
|
104
|
+
if (existing) {
|
|
105
|
+
if (
|
|
106
|
+
existing.kind !== input.kind ||
|
|
107
|
+
stableJson(existing.configSchema) !== stableJson(configSchema) ||
|
|
108
|
+
stableJson(existing.capabilities) !== stableJson(capabilities)
|
|
109
|
+
) {
|
|
110
|
+
throw new Error(`Integration feature ${input.featureKey} conflicts with stored definition`);
|
|
111
|
+
}
|
|
112
|
+
return existing;
|
|
113
|
+
}
|
|
114
|
+
const [created] = await db
|
|
115
|
+
.insert(schema.integrationFeatureFacets)
|
|
116
|
+
.values({
|
|
117
|
+
integrationFacetId: input.integrationFacetId,
|
|
118
|
+
featureKey: input.featureKey,
|
|
119
|
+
kind: input.kind,
|
|
120
|
+
configSchema,
|
|
121
|
+
capabilities,
|
|
122
|
+
})
|
|
123
|
+
.returning();
|
|
124
|
+
if (!created) throw new Error(`Failed to create Integration feature ${input.featureKey}`);
|
|
125
|
+
return created;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function upsertIntegrationFeatureBinding(
|
|
129
|
+
db: Database,
|
|
130
|
+
input: UpsertIntegrationFeatureBindingInput,
|
|
131
|
+
): Promise<{ row: typeof schema.integrationFeatureBindings.$inferSelect; changed: boolean }> {
|
|
132
|
+
assertBindingKey(input.bindingKey);
|
|
133
|
+
if (input.displayName.trim().length === 0 || input.displayName.trim().length > 200) {
|
|
134
|
+
throw new Error("Integration instance display name must contain 1-200 characters");
|
|
135
|
+
}
|
|
136
|
+
await db.execute(
|
|
137
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`integration-binding:${input.workspaceId}:${input.runtimeKey ?? `${input.featureFacetId}:${input.bindingKey}`}`}, 0))`,
|
|
138
|
+
);
|
|
139
|
+
const [existing] = await db
|
|
140
|
+
.select()
|
|
141
|
+
.from(schema.integrationFeatureBindings)
|
|
142
|
+
.where(
|
|
143
|
+
and(
|
|
144
|
+
eq(schema.integrationFeatureBindings.workspaceId, input.workspaceId),
|
|
145
|
+
input.runtimeKey
|
|
146
|
+
? eq(schema.integrationFeatureBindings.runtimeKey, input.runtimeKey)
|
|
147
|
+
: and(
|
|
148
|
+
eq(
|
|
149
|
+
schema.integrationFeatureBindings.integrationFacetInstallationId,
|
|
150
|
+
input.integrationFacetInstallationId,
|
|
151
|
+
),
|
|
152
|
+
eq(schema.integrationFeatureBindings.featureFacetId, input.featureFacetId),
|
|
153
|
+
eq(schema.integrationFeatureBindings.bindingKey, input.bindingKey),
|
|
154
|
+
),
|
|
155
|
+
),
|
|
156
|
+
)
|
|
157
|
+
.for("update")
|
|
158
|
+
.limit(1);
|
|
159
|
+
if (!existing) {
|
|
160
|
+
if (input.expectedVersion !== undefined) {
|
|
161
|
+
throw new IntegrationFeatureBindingVersionConflictError(
|
|
162
|
+
input.bindingKey,
|
|
163
|
+
input.expectedVersion,
|
|
164
|
+
0,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const [created] = await db
|
|
168
|
+
.insert(schema.integrationFeatureBindings)
|
|
169
|
+
.values({
|
|
170
|
+
accountId: input.accountId,
|
|
171
|
+
workspaceId: input.workspaceId,
|
|
172
|
+
integrationFacetInstallationId: input.integrationFacetInstallationId,
|
|
173
|
+
featureFacetId: input.featureFacetId,
|
|
174
|
+
bindingKey: input.bindingKey,
|
|
175
|
+
displayName: input.displayName.trim(),
|
|
176
|
+
runtimeKey: input.runtimeKey ?? null,
|
|
177
|
+
connectionId: input.connectionId ?? null,
|
|
178
|
+
status: "active",
|
|
179
|
+
config: input.config,
|
|
180
|
+
createdBySubjectId: input.createdBySubjectId,
|
|
181
|
+
})
|
|
182
|
+
.returning();
|
|
183
|
+
if (!created) throw new Error("Failed to create Integration instance");
|
|
184
|
+
await addIntegrationFeatureBindingOwner(db, created.id, input);
|
|
185
|
+
return { row: created, changed: true };
|
|
186
|
+
}
|
|
187
|
+
if (existing.bindingKey !== input.bindingKey) {
|
|
188
|
+
throw new IntegrationFeatureBindingOwnershipConflictError(
|
|
189
|
+
"Integration runtime identity is already assigned to another instance",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const changed =
|
|
193
|
+
existing.integrationFacetInstallationId !== input.integrationFacetInstallationId ||
|
|
194
|
+
existing.featureFacetId !== input.featureFacetId ||
|
|
195
|
+
existing.displayName !== input.displayName.trim() ||
|
|
196
|
+
existing.runtimeKey !== (input.runtimeKey ?? null) ||
|
|
197
|
+
existing.connectionId !== (input.connectionId ?? null) ||
|
|
198
|
+
existing.status !== "active" ||
|
|
199
|
+
stableJson(existing.config) !== stableJson(input.config);
|
|
200
|
+
let row = existing;
|
|
201
|
+
if (changed) {
|
|
202
|
+
if (input.expectedVersion !== undefined && existing.version !== input.expectedVersion) {
|
|
203
|
+
throw new IntegrationFeatureBindingVersionConflictError(
|
|
204
|
+
input.bindingKey,
|
|
205
|
+
input.expectedVersion,
|
|
206
|
+
existing.version,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (input.owner.kind === "direct" && input.expectedVersion === undefined) {
|
|
210
|
+
throw new IntegrationFeatureBindingVersionRequiredError(
|
|
211
|
+
"Updating an Integration instance requires its previewed version",
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const owners = await listIntegrationFeatureBindingOwners(db, existing.id);
|
|
215
|
+
const otherOwners = owners.filter(
|
|
216
|
+
(owner) => owner.kind !== input.owner.kind || owner.id !== input.owner.id,
|
|
217
|
+
);
|
|
218
|
+
if (otherOwners.length > 0) {
|
|
219
|
+
throw new IntegrationFeatureBindingOwnershipConflictError(
|
|
220
|
+
"Integration instance is shared by another owner and cannot be changed in place",
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const [updated] = await db
|
|
224
|
+
.update(schema.integrationFeatureBindings)
|
|
225
|
+
.set({
|
|
226
|
+
integrationFacetInstallationId: input.integrationFacetInstallationId,
|
|
227
|
+
featureFacetId: input.featureFacetId,
|
|
228
|
+
displayName: input.displayName.trim(),
|
|
229
|
+
runtimeKey: input.runtimeKey ?? null,
|
|
230
|
+
connectionId: input.connectionId ?? null,
|
|
231
|
+
status: "active",
|
|
232
|
+
config: input.config,
|
|
233
|
+
cursor: {},
|
|
234
|
+
version: existing.version + 1,
|
|
235
|
+
lastErrorCode: null,
|
|
236
|
+
updatedAt: new Date(),
|
|
237
|
+
})
|
|
238
|
+
.where(eq(schema.integrationFeatureBindings.id, existing.id))
|
|
239
|
+
.returning();
|
|
240
|
+
if (!updated) throw new Error("Failed to update Integration instance");
|
|
241
|
+
row = updated;
|
|
242
|
+
}
|
|
243
|
+
await addIntegrationFeatureBindingOwner(db, row.id, input);
|
|
244
|
+
return { row, changed };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function listIntegrationFeatureBindingOwners(
|
|
248
|
+
db: Database,
|
|
249
|
+
bindingId: string,
|
|
250
|
+
): Promise<IntegrationFeatureBindingOwner[]> {
|
|
251
|
+
return await loadIntegrationFeatureBindingOwners(db, bindingId, true);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function loadIntegrationFeatureBindingOwners(
|
|
255
|
+
db: Database,
|
|
256
|
+
bindingId: string,
|
|
257
|
+
effectiveOnly: boolean,
|
|
258
|
+
): Promise<IntegrationFeatureBindingOwner[]> {
|
|
259
|
+
const rows = await db
|
|
260
|
+
.select({
|
|
261
|
+
kind: schema.integrationFeatureBindingOwners.ownerKind,
|
|
262
|
+
id: schema.integrationFeatureBindingOwners.ownerId,
|
|
263
|
+
removable: schema.integrationFeatureBindingOwners.removable,
|
|
264
|
+
})
|
|
265
|
+
.from(schema.integrationFeatureBindingOwners)
|
|
266
|
+
.where(
|
|
267
|
+
and(
|
|
268
|
+
eq(schema.integrationFeatureBindingOwners.bindingId, bindingId),
|
|
269
|
+
effectiveOnly
|
|
270
|
+
? effectiveCapabilityOwnerSql(
|
|
271
|
+
schema.integrationFeatureBindingOwners.ownerKind,
|
|
272
|
+
schema.integrationFeatureBindingOwners.ownerId,
|
|
273
|
+
)
|
|
274
|
+
: sql`true`,
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
.orderBy(
|
|
278
|
+
asc(schema.integrationFeatureBindingOwners.ownerKind),
|
|
279
|
+
asc(schema.integrationFeatureBindingOwners.ownerId),
|
|
280
|
+
);
|
|
281
|
+
return rows.map((row) => {
|
|
282
|
+
if (
|
|
283
|
+
row.kind !== "direct" &&
|
|
284
|
+
row.kind !== "plugin" &&
|
|
285
|
+
row.kind !== "pack" &&
|
|
286
|
+
row.kind !== "migration"
|
|
287
|
+
) {
|
|
288
|
+
throw new Error(`Unknown Integration instance owner kind: ${row.kind}`);
|
|
289
|
+
}
|
|
290
|
+
return { kind: row.kind, id: row.id, removable: row.removable };
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function removeIntegrationFeatureBindingOwner(
|
|
295
|
+
db: Database,
|
|
296
|
+
input: {
|
|
297
|
+
workspaceId: string;
|
|
298
|
+
bindingId: string;
|
|
299
|
+
owner: Pick<IntegrationFeatureBindingOwner, "kind" | "id">;
|
|
300
|
+
expectedVersion?: number;
|
|
301
|
+
},
|
|
302
|
+
): Promise<{
|
|
303
|
+
binding: typeof schema.integrationFeatureBindings.$inferSelect | null;
|
|
304
|
+
remainingOwners: IntegrationFeatureBindingOwner[];
|
|
305
|
+
}> {
|
|
306
|
+
const [binding] = await db
|
|
307
|
+
.select()
|
|
308
|
+
.from(schema.integrationFeatureBindings)
|
|
309
|
+
.where(
|
|
310
|
+
and(
|
|
311
|
+
eq(schema.integrationFeatureBindings.workspaceId, input.workspaceId),
|
|
312
|
+
eq(schema.integrationFeatureBindings.id, input.bindingId),
|
|
313
|
+
),
|
|
314
|
+
)
|
|
315
|
+
.for("update")
|
|
316
|
+
.limit(1);
|
|
317
|
+
if (!binding) return { binding: null, remainingOwners: [] };
|
|
318
|
+
if (input.expectedVersion !== undefined && binding.version !== input.expectedVersion) {
|
|
319
|
+
throw new IntegrationFeatureBindingVersionConflictError(
|
|
320
|
+
binding.bindingKey,
|
|
321
|
+
input.expectedVersion,
|
|
322
|
+
binding.version,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
await db
|
|
326
|
+
.delete(schema.integrationFeatureBindingOwners)
|
|
327
|
+
.where(
|
|
328
|
+
and(
|
|
329
|
+
eq(schema.integrationFeatureBindingOwners.workspaceId, input.workspaceId),
|
|
330
|
+
eq(schema.integrationFeatureBindingOwners.bindingId, binding.id),
|
|
331
|
+
eq(schema.integrationFeatureBindingOwners.ownerKind, input.owner.kind),
|
|
332
|
+
eq(schema.integrationFeatureBindingOwners.ownerId, input.owner.id),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
// Pending or repairing owners are not runtime-effective yet, but they still
|
|
336
|
+
// preserve the shared binding so their operation can resume safely.
|
|
337
|
+
const remainingOwners = await loadIntegrationFeatureBindingOwners(db, binding.id, false);
|
|
338
|
+
if (remainingOwners.length > 0) return { binding, remainingOwners };
|
|
339
|
+
const [disabled] = await db
|
|
340
|
+
.update(schema.integrationFeatureBindings)
|
|
341
|
+
.set({ status: "disabled", version: binding.version + 1, updatedAt: new Date() })
|
|
342
|
+
.where(eq(schema.integrationFeatureBindings.id, binding.id))
|
|
343
|
+
.returning();
|
|
344
|
+
return { binding: disabled ?? binding, remainingOwners: [] };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export async function removeIntegrationFeatureBindingOwnersForOwner(
|
|
348
|
+
db: Database,
|
|
349
|
+
input: {
|
|
350
|
+
workspaceId: string;
|
|
351
|
+
owner: Pick<IntegrationFeatureBindingOwner, "kind" | "id">;
|
|
352
|
+
},
|
|
353
|
+
): Promise<{ disabledBindingIds: string[]; retainedBindingIds: string[] }> {
|
|
354
|
+
const deleted = await db
|
|
355
|
+
.delete(schema.integrationFeatureBindingOwners)
|
|
356
|
+
.where(
|
|
357
|
+
and(
|
|
358
|
+
eq(schema.integrationFeatureBindingOwners.workspaceId, input.workspaceId),
|
|
359
|
+
eq(schema.integrationFeatureBindingOwners.ownerKind, input.owner.kind),
|
|
360
|
+
eq(schema.integrationFeatureBindingOwners.ownerId, input.owner.id),
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
.returning({ bindingId: schema.integrationFeatureBindingOwners.bindingId });
|
|
364
|
+
const disabledBindingIds: string[] = [];
|
|
365
|
+
const retainedBindingIds: string[] = [];
|
|
366
|
+
for (const bindingId of [...new Set(deleted.map((row) => row.bindingId))]) {
|
|
367
|
+
const owners = await loadIntegrationFeatureBindingOwners(db, bindingId, false);
|
|
368
|
+
if (owners.length > 0) {
|
|
369
|
+
retainedBindingIds.push(bindingId);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
await db
|
|
373
|
+
.update(schema.integrationFeatureBindings)
|
|
374
|
+
.set({
|
|
375
|
+
status: "disabled",
|
|
376
|
+
version: sql`${schema.integrationFeatureBindings.version} + 1`,
|
|
377
|
+
updatedAt: new Date(),
|
|
378
|
+
})
|
|
379
|
+
.where(
|
|
380
|
+
and(
|
|
381
|
+
eq(schema.integrationFeatureBindings.workspaceId, input.workspaceId),
|
|
382
|
+
eq(schema.integrationFeatureBindings.id, bindingId),
|
|
383
|
+
),
|
|
384
|
+
);
|
|
385
|
+
disabledBindingIds.push(bindingId);
|
|
386
|
+
}
|
|
387
|
+
return { disabledBindingIds, retainedBindingIds };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export async function integrationDefinitionHasBindingOwner(
|
|
391
|
+
db: Database,
|
|
392
|
+
input: {
|
|
393
|
+
workspaceId: string;
|
|
394
|
+
pluginInstallationId: string;
|
|
395
|
+
owner: Pick<IntegrationFeatureBindingOwner, "kind" | "id">;
|
|
396
|
+
},
|
|
397
|
+
): Promise<boolean> {
|
|
398
|
+
const [row] = await db
|
|
399
|
+
.select({ id: schema.integrationFeatureBindingOwners.id })
|
|
400
|
+
.from(schema.integrationFeatureBindingOwners)
|
|
401
|
+
.innerJoin(
|
|
402
|
+
schema.integrationFeatureBindings,
|
|
403
|
+
eq(schema.integrationFeatureBindings.id, schema.integrationFeatureBindingOwners.bindingId),
|
|
404
|
+
)
|
|
405
|
+
.innerJoin(
|
|
406
|
+
schema.capabilityFacetInstallations,
|
|
407
|
+
eq(
|
|
408
|
+
schema.capabilityFacetInstallations.id,
|
|
409
|
+
schema.integrationFeatureBindings.integrationFacetInstallationId,
|
|
410
|
+
),
|
|
411
|
+
)
|
|
412
|
+
.where(
|
|
413
|
+
and(
|
|
414
|
+
eq(schema.integrationFeatureBindingOwners.workspaceId, input.workspaceId),
|
|
415
|
+
eq(schema.integrationFeatureBindingOwners.ownerKind, input.owner.kind),
|
|
416
|
+
eq(schema.integrationFeatureBindingOwners.ownerId, input.owner.id),
|
|
417
|
+
eq(schema.capabilityFacetInstallations.pluginInstallationId, input.pluginInstallationId),
|
|
418
|
+
or(
|
|
419
|
+
eq(schema.integrationFeatureBindings.status, "active"),
|
|
420
|
+
eq(schema.integrationFeatureBindings.status, "needs_attention"),
|
|
421
|
+
eq(schema.integrationFeatureBindings.status, "paused"),
|
|
422
|
+
),
|
|
423
|
+
),
|
|
424
|
+
)
|
|
425
|
+
.limit(1);
|
|
426
|
+
return Boolean(row);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export async function addIntegrationFeatureBindingOwner(
|
|
430
|
+
db: Database,
|
|
431
|
+
bindingId: string,
|
|
432
|
+
input: Pick<UpsertIntegrationFeatureBindingInput, "accountId" | "workspaceId" | "owner">,
|
|
433
|
+
): Promise<void> {
|
|
434
|
+
await db
|
|
435
|
+
.insert(schema.integrationFeatureBindingOwners)
|
|
436
|
+
.values({
|
|
437
|
+
accountId: input.accountId,
|
|
438
|
+
workspaceId: input.workspaceId,
|
|
439
|
+
bindingId,
|
|
440
|
+
ownerKind: input.owner.kind,
|
|
441
|
+
ownerId: input.owner.id,
|
|
442
|
+
removable: input.owner.removable,
|
|
443
|
+
})
|
|
444
|
+
.onConflictDoNothing();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function assertBindingKey(value: string): void {
|
|
448
|
+
if (!/^[a-z0-9](?:[a-z0-9._/-]*[a-z0-9])?$/.test(value) || value.length > 128) {
|
|
449
|
+
throw new Error("Integration instance key is invalid");
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function sha256(value: string): string {
|
|
454
|
+
return createHash("sha256").update(value).digest("hex");
|
|
455
|
+
}
|