@opengeni/db 0.28.9 → 0.36.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/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +299 -0
- package/dist/browser-downloads.d.ts +51 -0
- package/dist/browser-identities.d.ts +137 -0
- package/dist/browser-sessions.d.ts +354 -0
- package/dist/browser-state-artifacts.d.ts +91 -0
- package/dist/canonical-human-identities.d.ts +48 -0
- package/dist/canonical-human-identities.js +28 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +194 -0
- package/dist/chunk-5KTIEDX7.js +13200 -0
- package/dist/chunk-5KTIEDX7.js.map +1 -0
- package/dist/chunk-CQZ4QSP5.js +995 -0
- package/dist/chunk-CQZ4QSP5.js.map +1 -0
- package/dist/chunk-F32YT7MP.js +117 -0
- package/dist/chunk-F32YT7MP.js.map +1 -0
- package/dist/chunk-JC5QPLUM.js +666 -0
- package/dist/chunk-JC5QPLUM.js.map +1 -0
- package/dist/chunk-P3RE4D2B.js +3535 -0
- package/dist/chunk-P3RE4D2B.js.map +1 -0
- package/dist/chunk-TAUTWOS5.js +259 -0
- package/dist/chunk-TAUTWOS5.js.map +1 -0
- package/dist/chunk-VKLB2ZGI.js +2389 -0
- package/dist/chunk-VKLB2ZGI.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 +57 -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 +1122 -126
- package/dist/index.js +42329 -18407
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +73 -10
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-facets.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +14 -0
- package/dist/interaction-schema.d.ts +6036 -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 +19 -6
- package/dist/schema.d.ts +22654 -9359
- package/dist/schema.js +209 -3
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +33 -7
- 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-tenancy.d.ts +44 -0
- package/dist/session-tenancy.js +16 -0
- package/dist/session-tenancy.js.map +1 -0
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-task-policy-schema.d.ts +847 -0
- package/dist/slack-task-policy.d.ts +65 -0
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/task-notes-schema.d.ts +924 -0
- package/dist/task-notes.d.ts +85 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +225 -0
- package/dist/video-generation.js +68 -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/dist/xai-subscription.d.ts +326 -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_browser_state_transfer_hardening.sql +560 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_browser_interaction_authority.sql +67 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_browser_download_saves.sql +34 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_browser_controller_host.sql +385 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/drizzle/0217_external_browser_auth_operations.sql +22 -0
- package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
- package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
- package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
- package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
- package/drizzle/0220_session_channels.sql +54 -0
- package/drizzle/0221_sessions_channel_index.sql +5 -0
- package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
- package/drizzle/0222_sessions_channel_fk.sql +89 -0
- package/drizzle/0223_pending_tool_event_output.sql +55 -0
- package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
- package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
- package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
- package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
- package/drizzle/0227_slack_native_actions.sql +145 -0
- package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
- package/drizzle/0228_slack_task_policy.sql +400 -0
- package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
- package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
- package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
- package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
- package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
- package/drizzle/0234_xai_subscription_authority.sql +1139 -0
- package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
- package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
- package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
- package/drizzle/0237_interaction_transition_reaper.sql +263 -0
- package/drizzle/0238_goal_persistence_policy.sql +471 -0
- package/drizzle/0238_supergrok_realtime_model.sql +29 -0
- package/drizzle/0239_supergrok_video_funding.sql +50 -0
- package/drizzle/0239_task_tree_notes.sql +990 -0
- package/package.json +26 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +4373 -0
- package/src/browser-downloads.ts +517 -0
- package/src/browser-identities.ts +1385 -0
- package/src/browser-sessions.ts +2294 -0
- package/src/browser-state-artifacts.ts +449 -0
- package/src/canonical-human-identities.ts +317 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1571 -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 +468 -24
- 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 +9963 -1465
- package/src/insights.ts +251 -23
- package/src/integration-bindings.ts +455 -0
- package/src/integration-facets.ts +862 -0
- package/src/interaction-revisions.ts +70 -0
- package/src/interaction-schema.ts +1606 -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 +886 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +534 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +721 -5
- package/src/schema.ts +6270 -3159
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +130 -42
- package/src/session-queue-commands.ts +265 -15
- 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-tenancy.ts +188 -0
- package/src/session-tool-call-settlement.ts +18 -3
- package/src/slack-task-policy-schema.ts +130 -0
- package/src/slack-task-policy.ts +341 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/task-notes-schema.ts +135 -0
- package/src/task-notes.ts +178 -0
- package/src/video-generation.ts +1530 -0
- package/src/workspace-artifacts.ts +1 -1
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/src/xai-subscription.ts +1887 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js +0 -1219
- 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 IntegrationFacetBindingOwner = {
|
|
11
|
+
kind: "direct" | "plugin" | "pack" | "migration";
|
|
12
|
+
id: string;
|
|
13
|
+
removable: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type UpsertIntegrationFacetBindingInput = {
|
|
17
|
+
accountId: string;
|
|
18
|
+
workspaceId: string;
|
|
19
|
+
integrationFacetInstallationId: string;
|
|
20
|
+
facetDefinitionId: 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: IntegrationFacetBindingOwner;
|
|
28
|
+
expectedVersion?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export class IntegrationFacetBindingVersionConflictError extends Error {
|
|
32
|
+
readonly name = "IntegrationFacetBindingVersionConflictError";
|
|
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 IntegrationFacetBindingVersionRequiredError extends Error {
|
|
46
|
+
readonly name = "IntegrationFacetBindingVersionRequiredError";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class IntegrationFacetBindingOwnershipConflictError extends Error {
|
|
50
|
+
readonly name = "IntegrationFacetBindingOwnershipConflictError";
|
|
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 ensureIntegrationFacetDefinition(
|
|
78
|
+
db: Database,
|
|
79
|
+
input: {
|
|
80
|
+
integrationFacetId: string;
|
|
81
|
+
facetKey: 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.integrationFacetDefinitions.$inferSelect> {
|
|
92
|
+
const [existing] = await db
|
|
93
|
+
.select()
|
|
94
|
+
.from(schema.integrationFacetDefinitions)
|
|
95
|
+
.where(
|
|
96
|
+
and(
|
|
97
|
+
eq(schema.integrationFacetDefinitions.integrationFacetId, input.integrationFacetId),
|
|
98
|
+
eq(schema.integrationFacetDefinitions.facetKey, input.facetKey),
|
|
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 facet ${input.facetKey} conflicts with stored definition`);
|
|
111
|
+
}
|
|
112
|
+
return existing;
|
|
113
|
+
}
|
|
114
|
+
const [created] = await db
|
|
115
|
+
.insert(schema.integrationFacetDefinitions)
|
|
116
|
+
.values({
|
|
117
|
+
integrationFacetId: input.integrationFacetId,
|
|
118
|
+
facetKey: input.facetKey,
|
|
119
|
+
kind: input.kind,
|
|
120
|
+
configSchema,
|
|
121
|
+
capabilities,
|
|
122
|
+
})
|
|
123
|
+
.returning();
|
|
124
|
+
if (!created) throw new Error(`Failed to create Integration facet ${input.facetKey}`);
|
|
125
|
+
return created;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function upsertIntegrationFacetBinding(
|
|
129
|
+
db: Database,
|
|
130
|
+
input: UpsertIntegrationFacetBindingInput,
|
|
131
|
+
): Promise<{ row: typeof schema.integrationFacetBindings.$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.facetDefinitionId}:${input.bindingKey}`}`}, 0))`,
|
|
138
|
+
);
|
|
139
|
+
const [existing] = await db
|
|
140
|
+
.select()
|
|
141
|
+
.from(schema.integrationFacetBindings)
|
|
142
|
+
.where(
|
|
143
|
+
and(
|
|
144
|
+
eq(schema.integrationFacetBindings.workspaceId, input.workspaceId),
|
|
145
|
+
input.runtimeKey
|
|
146
|
+
? eq(schema.integrationFacetBindings.runtimeKey, input.runtimeKey)
|
|
147
|
+
: and(
|
|
148
|
+
eq(
|
|
149
|
+
schema.integrationFacetBindings.integrationFacetInstallationId,
|
|
150
|
+
input.integrationFacetInstallationId,
|
|
151
|
+
),
|
|
152
|
+
eq(schema.integrationFacetBindings.facetDefinitionId, input.facetDefinitionId),
|
|
153
|
+
eq(schema.integrationFacetBindings.bindingKey, input.bindingKey),
|
|
154
|
+
),
|
|
155
|
+
),
|
|
156
|
+
)
|
|
157
|
+
.for("update")
|
|
158
|
+
.limit(1);
|
|
159
|
+
if (!existing) {
|
|
160
|
+
if (input.expectedVersion !== undefined) {
|
|
161
|
+
throw new IntegrationFacetBindingVersionConflictError(
|
|
162
|
+
input.bindingKey,
|
|
163
|
+
input.expectedVersion,
|
|
164
|
+
0,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const [created] = await db
|
|
168
|
+
.insert(schema.integrationFacetBindings)
|
|
169
|
+
.values({
|
|
170
|
+
accountId: input.accountId,
|
|
171
|
+
workspaceId: input.workspaceId,
|
|
172
|
+
integrationFacetInstallationId: input.integrationFacetInstallationId,
|
|
173
|
+
facetDefinitionId: input.facetDefinitionId,
|
|
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 addIntegrationFacetBindingOwner(db, created.id, input);
|
|
185
|
+
return { row: created, changed: true };
|
|
186
|
+
}
|
|
187
|
+
if (existing.bindingKey !== input.bindingKey) {
|
|
188
|
+
throw new IntegrationFacetBindingOwnershipConflictError(
|
|
189
|
+
"Integration runtime identity is already assigned to another instance",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const changed =
|
|
193
|
+
existing.integrationFacetInstallationId !== input.integrationFacetInstallationId ||
|
|
194
|
+
existing.facetDefinitionId !== input.facetDefinitionId ||
|
|
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 IntegrationFacetBindingVersionConflictError(
|
|
204
|
+
input.bindingKey,
|
|
205
|
+
input.expectedVersion,
|
|
206
|
+
existing.version,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (input.owner.kind === "direct" && input.expectedVersion === undefined) {
|
|
210
|
+
throw new IntegrationFacetBindingVersionRequiredError(
|
|
211
|
+
"Updating an Integration instance requires its previewed version",
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const owners = await listIntegrationFacetBindingOwners(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 IntegrationFacetBindingOwnershipConflictError(
|
|
220
|
+
"Integration instance is shared by another owner and cannot be changed in place",
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const [updated] = await db
|
|
224
|
+
.update(schema.integrationFacetBindings)
|
|
225
|
+
.set({
|
|
226
|
+
integrationFacetInstallationId: input.integrationFacetInstallationId,
|
|
227
|
+
facetDefinitionId: input.facetDefinitionId,
|
|
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.integrationFacetBindings.id, existing.id))
|
|
239
|
+
.returning();
|
|
240
|
+
if (!updated) throw new Error("Failed to update Integration instance");
|
|
241
|
+
row = updated;
|
|
242
|
+
}
|
|
243
|
+
await addIntegrationFacetBindingOwner(db, row.id, input);
|
|
244
|
+
return { row, changed };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function listIntegrationFacetBindingOwners(
|
|
248
|
+
db: Database,
|
|
249
|
+
bindingId: string,
|
|
250
|
+
): Promise<IntegrationFacetBindingOwner[]> {
|
|
251
|
+
return await loadIntegrationFacetBindingOwners(db, bindingId, true);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function loadIntegrationFacetBindingOwners(
|
|
255
|
+
db: Database,
|
|
256
|
+
bindingId: string,
|
|
257
|
+
effectiveOnly: boolean,
|
|
258
|
+
): Promise<IntegrationFacetBindingOwner[]> {
|
|
259
|
+
const rows = await db
|
|
260
|
+
.select({
|
|
261
|
+
kind: schema.integrationFacetBindingOwners.ownerKind,
|
|
262
|
+
id: schema.integrationFacetBindingOwners.ownerId,
|
|
263
|
+
removable: schema.integrationFacetBindingOwners.removable,
|
|
264
|
+
})
|
|
265
|
+
.from(schema.integrationFacetBindingOwners)
|
|
266
|
+
.where(
|
|
267
|
+
and(
|
|
268
|
+
eq(schema.integrationFacetBindingOwners.bindingId, bindingId),
|
|
269
|
+
effectiveOnly
|
|
270
|
+
? effectiveCapabilityOwnerSql(
|
|
271
|
+
schema.integrationFacetBindingOwners.ownerKind,
|
|
272
|
+
schema.integrationFacetBindingOwners.ownerId,
|
|
273
|
+
)
|
|
274
|
+
: sql`true`,
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
.orderBy(
|
|
278
|
+
asc(schema.integrationFacetBindingOwners.ownerKind),
|
|
279
|
+
asc(schema.integrationFacetBindingOwners.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 removeIntegrationFacetBindingOwner(
|
|
295
|
+
db: Database,
|
|
296
|
+
input: {
|
|
297
|
+
workspaceId: string;
|
|
298
|
+
bindingId: string;
|
|
299
|
+
owner: Pick<IntegrationFacetBindingOwner, "kind" | "id">;
|
|
300
|
+
expectedVersion?: number;
|
|
301
|
+
},
|
|
302
|
+
): Promise<{
|
|
303
|
+
binding: typeof schema.integrationFacetBindings.$inferSelect | null;
|
|
304
|
+
remainingOwners: IntegrationFacetBindingOwner[];
|
|
305
|
+
}> {
|
|
306
|
+
const [binding] = await db
|
|
307
|
+
.select()
|
|
308
|
+
.from(schema.integrationFacetBindings)
|
|
309
|
+
.where(
|
|
310
|
+
and(
|
|
311
|
+
eq(schema.integrationFacetBindings.workspaceId, input.workspaceId),
|
|
312
|
+
eq(schema.integrationFacetBindings.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 IntegrationFacetBindingVersionConflictError(
|
|
320
|
+
binding.bindingKey,
|
|
321
|
+
input.expectedVersion,
|
|
322
|
+
binding.version,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
await db
|
|
326
|
+
.delete(schema.integrationFacetBindingOwners)
|
|
327
|
+
.where(
|
|
328
|
+
and(
|
|
329
|
+
eq(schema.integrationFacetBindingOwners.workspaceId, input.workspaceId),
|
|
330
|
+
eq(schema.integrationFacetBindingOwners.bindingId, binding.id),
|
|
331
|
+
eq(schema.integrationFacetBindingOwners.ownerKind, input.owner.kind),
|
|
332
|
+
eq(schema.integrationFacetBindingOwners.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 loadIntegrationFacetBindingOwners(db, binding.id, false);
|
|
338
|
+
if (remainingOwners.length > 0) return { binding, remainingOwners };
|
|
339
|
+
const [disabled] = await db
|
|
340
|
+
.update(schema.integrationFacetBindings)
|
|
341
|
+
.set({ status: "disabled", version: binding.version + 1, updatedAt: new Date() })
|
|
342
|
+
.where(eq(schema.integrationFacetBindings.id, binding.id))
|
|
343
|
+
.returning();
|
|
344
|
+
return { binding: disabled ?? binding, remainingOwners: [] };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export async function removeIntegrationFacetBindingOwnersForOwner(
|
|
348
|
+
db: Database,
|
|
349
|
+
input: {
|
|
350
|
+
workspaceId: string;
|
|
351
|
+
owner: Pick<IntegrationFacetBindingOwner, "kind" | "id">;
|
|
352
|
+
},
|
|
353
|
+
): Promise<{ disabledBindingIds: string[]; retainedBindingIds: string[] }> {
|
|
354
|
+
const deleted = await db
|
|
355
|
+
.delete(schema.integrationFacetBindingOwners)
|
|
356
|
+
.where(
|
|
357
|
+
and(
|
|
358
|
+
eq(schema.integrationFacetBindingOwners.workspaceId, input.workspaceId),
|
|
359
|
+
eq(schema.integrationFacetBindingOwners.ownerKind, input.owner.kind),
|
|
360
|
+
eq(schema.integrationFacetBindingOwners.ownerId, input.owner.id),
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
.returning({ bindingId: schema.integrationFacetBindingOwners.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 loadIntegrationFacetBindingOwners(db, bindingId, false);
|
|
368
|
+
if (owners.length > 0) {
|
|
369
|
+
retainedBindingIds.push(bindingId);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
await db
|
|
373
|
+
.update(schema.integrationFacetBindings)
|
|
374
|
+
.set({
|
|
375
|
+
status: "disabled",
|
|
376
|
+
version: sql`${schema.integrationFacetBindings.version} + 1`,
|
|
377
|
+
updatedAt: new Date(),
|
|
378
|
+
})
|
|
379
|
+
.where(
|
|
380
|
+
and(
|
|
381
|
+
eq(schema.integrationFacetBindings.workspaceId, input.workspaceId),
|
|
382
|
+
eq(schema.integrationFacetBindings.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<IntegrationFacetBindingOwner, "kind" | "id">;
|
|
396
|
+
},
|
|
397
|
+
): Promise<boolean> {
|
|
398
|
+
const [row] = await db
|
|
399
|
+
.select({ id: schema.integrationFacetBindingOwners.id })
|
|
400
|
+
.from(schema.integrationFacetBindingOwners)
|
|
401
|
+
.innerJoin(
|
|
402
|
+
schema.integrationFacetBindings,
|
|
403
|
+
eq(schema.integrationFacetBindings.id, schema.integrationFacetBindingOwners.bindingId),
|
|
404
|
+
)
|
|
405
|
+
.innerJoin(
|
|
406
|
+
schema.capabilityFacetInstallations,
|
|
407
|
+
eq(
|
|
408
|
+
schema.capabilityFacetInstallations.id,
|
|
409
|
+
schema.integrationFacetBindings.integrationFacetInstallationId,
|
|
410
|
+
),
|
|
411
|
+
)
|
|
412
|
+
.where(
|
|
413
|
+
and(
|
|
414
|
+
eq(schema.integrationFacetBindingOwners.workspaceId, input.workspaceId),
|
|
415
|
+
eq(schema.integrationFacetBindingOwners.ownerKind, input.owner.kind),
|
|
416
|
+
eq(schema.integrationFacetBindingOwners.ownerId, input.owner.id),
|
|
417
|
+
eq(schema.capabilityFacetInstallations.pluginInstallationId, input.pluginInstallationId),
|
|
418
|
+
or(
|
|
419
|
+
eq(schema.integrationFacetBindings.status, "active"),
|
|
420
|
+
eq(schema.integrationFacetBindings.status, "needs_attention"),
|
|
421
|
+
eq(schema.integrationFacetBindings.status, "paused"),
|
|
422
|
+
),
|
|
423
|
+
),
|
|
424
|
+
)
|
|
425
|
+
.limit(1);
|
|
426
|
+
return Boolean(row);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export async function addIntegrationFacetBindingOwner(
|
|
430
|
+
db: Database,
|
|
431
|
+
bindingId: string,
|
|
432
|
+
input: Pick<UpsertIntegrationFacetBindingInput, "accountId" | "workspaceId" | "owner">,
|
|
433
|
+
): Promise<void> {
|
|
434
|
+
await db
|
|
435
|
+
.insert(schema.integrationFacetBindingOwners)
|
|
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
|
+
}
|