@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,954 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
stableJson,
|
|
5
|
+
type CapabilityPackComponentReference,
|
|
6
|
+
type PackComponentResolution,
|
|
7
|
+
} from "@opengeni/contracts";
|
|
8
|
+
import { and, asc, eq, inArray, ne, notInArray, or, sql } from "drizzle-orm";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
cleanupOrphanedCapabilityComponents,
|
|
12
|
+
effectiveCapabilityOwnerSql,
|
|
13
|
+
} from "./capability-components";
|
|
14
|
+
import {
|
|
15
|
+
listInstalledApiIntegrationsInRlsContext,
|
|
16
|
+
type ApiIntegrationRuntime,
|
|
17
|
+
} from "./capability-integrations";
|
|
18
|
+
import { withRlsContext, withWorkspaceRls, type Database } from "./database";
|
|
19
|
+
import {
|
|
20
|
+
addIntegrationFeatureBindingOwner,
|
|
21
|
+
removeIntegrationFeatureBindingOwner,
|
|
22
|
+
removeIntegrationFeatureBindingOwnersForOwner,
|
|
23
|
+
} from "./integration-bindings";
|
|
24
|
+
import * as schema from "./schema";
|
|
25
|
+
|
|
26
|
+
type PackComponentKind = PackComponentResolution["kind"];
|
|
27
|
+
|
|
28
|
+
export type PackInlineSkillRequirement = {
|
|
29
|
+
key: string;
|
|
30
|
+
capabilityId: string;
|
|
31
|
+
name: string;
|
|
32
|
+
contentSha256: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type ResolvedPackComponentTarget = {
|
|
36
|
+
resolution: PackComponentResolution;
|
|
37
|
+
facetInstallationIds: string[];
|
|
38
|
+
bindingIds: string[];
|
|
39
|
+
metadata: Record<string, unknown>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type StoredPackInstallationComponent = {
|
|
43
|
+
id: string;
|
|
44
|
+
packInstallationId: string;
|
|
45
|
+
key: string;
|
|
46
|
+
kind: PackComponentKind;
|
|
47
|
+
capabilityId: string;
|
|
48
|
+
resolvedId: string;
|
|
49
|
+
digest: string;
|
|
50
|
+
metadata: Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export class PackComponentResolutionError extends Error {
|
|
54
|
+
readonly name = "PackComponentResolutionError";
|
|
55
|
+
|
|
56
|
+
constructor(
|
|
57
|
+
readonly componentKey: string,
|
|
58
|
+
readonly status: "missing" | "mismatch",
|
|
59
|
+
) {
|
|
60
|
+
super(`Pack component ${componentKey} is ${status}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function resolvePackComponentReferences(
|
|
65
|
+
db: Database,
|
|
66
|
+
workspaceId: string,
|
|
67
|
+
references: readonly CapabilityPackComponentReference[],
|
|
68
|
+
): Promise<PackComponentResolution[]> {
|
|
69
|
+
return await withWorkspaceRls(
|
|
70
|
+
db,
|
|
71
|
+
workspaceId,
|
|
72
|
+
async (scopedDb) =>
|
|
73
|
+
await resolvePackComponentTargets(scopedDb, workspaceId, references).then((targets) =>
|
|
74
|
+
targets.map((target) => target.resolution),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Resolve inline Pack Skills against effective owners by case-insensitive
|
|
81
|
+
* runtime name. Exact content is shareable; different content under the same
|
|
82
|
+
* active name is a conflict. The current Pack owner may be excluded so an
|
|
83
|
+
* update can replace its own previous inline Skill safely.
|
|
84
|
+
*/
|
|
85
|
+
export async function resolvePackInlineSkillReferences(
|
|
86
|
+
db: Database,
|
|
87
|
+
workspaceId: string,
|
|
88
|
+
requirements: readonly PackInlineSkillRequirement[],
|
|
89
|
+
excludePackInstallationId?: string,
|
|
90
|
+
): Promise<PackComponentResolution[]> {
|
|
91
|
+
if (requirements.length === 0) return [];
|
|
92
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
93
|
+
const normalizedNames = [
|
|
94
|
+
...new Set(requirements.map((requirement) => requirement.name.toLowerCase())),
|
|
95
|
+
];
|
|
96
|
+
const rows = await scopedDb
|
|
97
|
+
.select({
|
|
98
|
+
facetInstallationId: schema.capabilityFacetInstallations.id,
|
|
99
|
+
name: schema.capabilitySkillFacets.name,
|
|
100
|
+
contentSha256: schema.capabilitySkillFacets.contentSha256,
|
|
101
|
+
})
|
|
102
|
+
.from(schema.capabilitySkillFacets)
|
|
103
|
+
.innerJoin(
|
|
104
|
+
schema.capabilityFacetInstallations,
|
|
105
|
+
eq(schema.capabilityFacetInstallations.facetId, schema.capabilitySkillFacets.facetId),
|
|
106
|
+
)
|
|
107
|
+
.innerJoin(
|
|
108
|
+
schema.capabilityPluginInstallations,
|
|
109
|
+
eq(
|
|
110
|
+
schema.capabilityPluginInstallations.id,
|
|
111
|
+
schema.capabilityFacetInstallations.pluginInstallationId,
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
.where(
|
|
115
|
+
and(
|
|
116
|
+
eq(schema.capabilityFacetInstallations.workspaceId, workspaceId),
|
|
117
|
+
eq(schema.capabilityFacetInstallations.status, "active"),
|
|
118
|
+
eq(schema.capabilityPluginInstallations.status, "active"),
|
|
119
|
+
inArray(sql<string>`lower(${schema.capabilitySkillFacets.name})`, normalizedNames),
|
|
120
|
+
sql`exists (
|
|
121
|
+
select 1 from ${schema.capabilityComponentOwners} owner
|
|
122
|
+
where owner.facet_installation_id = ${schema.capabilityFacetInstallations.id}
|
|
123
|
+
and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
|
|
124
|
+
and ${
|
|
125
|
+
excludePackInstallationId
|
|
126
|
+
? sql`not (
|
|
127
|
+
owner.owner_kind = 'pack'
|
|
128
|
+
and owner.owner_id = ${excludePackInstallationId}
|
|
129
|
+
)`
|
|
130
|
+
: sql`true`
|
|
131
|
+
}
|
|
132
|
+
)`,
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
.orderBy(
|
|
136
|
+
asc(schema.capabilitySkillFacets.name),
|
|
137
|
+
asc(schema.capabilitySkillFacets.contentSha256),
|
|
138
|
+
);
|
|
139
|
+
return requirements.map((requirement) => {
|
|
140
|
+
const candidates = rows.filter(
|
|
141
|
+
(row) => row.name.toLowerCase() === requirement.name.toLowerCase(),
|
|
142
|
+
);
|
|
143
|
+
const exact = candidates.find(
|
|
144
|
+
(candidate) => candidate.contentSha256 === requirement.contentSha256,
|
|
145
|
+
);
|
|
146
|
+
const mismatch = candidates[0] ?? null;
|
|
147
|
+
return {
|
|
148
|
+
key: requirement.key,
|
|
149
|
+
kind: "inline_skill" as const,
|
|
150
|
+
capabilityId: requirement.capabilityId,
|
|
151
|
+
required: true,
|
|
152
|
+
status: candidates.length === 0 || exact ? "ready" : "mismatch",
|
|
153
|
+
expectedDigest: requirement.contentSha256,
|
|
154
|
+
actualDigest: exact?.contentSha256 ?? mismatch?.contentSha256 ?? requirement.contentSha256,
|
|
155
|
+
resolvedId:
|
|
156
|
+
candidates.length === 0 ? requirement.capabilityId : (exact?.facetInstallationId ?? null),
|
|
157
|
+
label: requirement.name,
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function adoptPackComponentReferences(
|
|
164
|
+
db: Database,
|
|
165
|
+
input: {
|
|
166
|
+
accountId: string;
|
|
167
|
+
workspaceId: string;
|
|
168
|
+
packInstallationId: string;
|
|
169
|
+
references: readonly CapabilityPackComponentReference[];
|
|
170
|
+
},
|
|
171
|
+
): Promise<{
|
|
172
|
+
components: StoredPackInstallationComponent[];
|
|
173
|
+
retainedFacetInstallationIds: string[];
|
|
174
|
+
retainedBindingIds: string[];
|
|
175
|
+
}> {
|
|
176
|
+
return await withRlsContext(
|
|
177
|
+
db,
|
|
178
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
179
|
+
async (scopedDb) =>
|
|
180
|
+
await scopedDb.transaction(async (txRaw) => {
|
|
181
|
+
const tx = txRaw as unknown as Database;
|
|
182
|
+
await tx.execute(
|
|
183
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`pack-components:${input.workspaceId}:${input.packInstallationId}`}, 0))`,
|
|
184
|
+
);
|
|
185
|
+
const [installation] = await tx
|
|
186
|
+
.select({ id: schema.packInstallations.id })
|
|
187
|
+
.from(schema.packInstallations)
|
|
188
|
+
.where(
|
|
189
|
+
and(
|
|
190
|
+
eq(schema.packInstallations.workspaceId, input.workspaceId),
|
|
191
|
+
eq(schema.packInstallations.id, input.packInstallationId),
|
|
192
|
+
),
|
|
193
|
+
)
|
|
194
|
+
.for("update")
|
|
195
|
+
.limit(1);
|
|
196
|
+
if (!installation) throw new Error("Pack installation was not found");
|
|
197
|
+
|
|
198
|
+
const targets = await resolvePackComponentTargets(tx, input.workspaceId, input.references);
|
|
199
|
+
const retainedFacetInstallationIds: string[] = [];
|
|
200
|
+
const retainedBindingIds: string[] = [];
|
|
201
|
+
const components: StoredPackInstallationComponent[] = [];
|
|
202
|
+
for (const target of targets) {
|
|
203
|
+
if (target.resolution.status !== "ready") {
|
|
204
|
+
if (target.resolution.required) {
|
|
205
|
+
throw new PackComponentResolutionError(
|
|
206
|
+
target.resolution.key,
|
|
207
|
+
target.resolution.status,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
for (const facetInstallationId of target.facetInstallationIds) {
|
|
213
|
+
await tx
|
|
214
|
+
.insert(schema.capabilityComponentOwners)
|
|
215
|
+
.values({
|
|
216
|
+
accountId: input.accountId,
|
|
217
|
+
workspaceId: input.workspaceId,
|
|
218
|
+
facetInstallationId,
|
|
219
|
+
ownerKind: "pack",
|
|
220
|
+
ownerId: input.packInstallationId,
|
|
221
|
+
removable: true,
|
|
222
|
+
})
|
|
223
|
+
.onConflictDoNothing();
|
|
224
|
+
retainedFacetInstallationIds.push(facetInstallationId);
|
|
225
|
+
}
|
|
226
|
+
for (const bindingId of target.bindingIds) {
|
|
227
|
+
await addIntegrationFeatureBindingOwner(tx, bindingId, {
|
|
228
|
+
accountId: input.accountId,
|
|
229
|
+
workspaceId: input.workspaceId,
|
|
230
|
+
owner: { kind: "pack", id: input.packInstallationId, removable: true },
|
|
231
|
+
});
|
|
232
|
+
retainedBindingIds.push(bindingId);
|
|
233
|
+
}
|
|
234
|
+
const [row] = await tx
|
|
235
|
+
.insert(schema.packInstallationComponents)
|
|
236
|
+
.values({
|
|
237
|
+
accountId: input.accountId,
|
|
238
|
+
workspaceId: input.workspaceId,
|
|
239
|
+
packInstallationId: input.packInstallationId,
|
|
240
|
+
componentKey: target.resolution.key,
|
|
241
|
+
kind: target.resolution.kind,
|
|
242
|
+
capabilityId: target.resolution.capabilityId,
|
|
243
|
+
resolvedId: target.resolution.resolvedId!,
|
|
244
|
+
digest: target.resolution.actualDigest!,
|
|
245
|
+
metadata: {
|
|
246
|
+
...target.metadata,
|
|
247
|
+
facetInstallationIds: [...new Set(target.facetInstallationIds)],
|
|
248
|
+
bindingIds: [...new Set(target.bindingIds)],
|
|
249
|
+
},
|
|
250
|
+
updatedAt: new Date(),
|
|
251
|
+
})
|
|
252
|
+
.onConflictDoUpdate({
|
|
253
|
+
target: [
|
|
254
|
+
schema.packInstallationComponents.packInstallationId,
|
|
255
|
+
schema.packInstallationComponents.componentKey,
|
|
256
|
+
],
|
|
257
|
+
set: {
|
|
258
|
+
kind: target.resolution.kind,
|
|
259
|
+
capabilityId: target.resolution.capabilityId,
|
|
260
|
+
resolvedId: target.resolution.resolvedId!,
|
|
261
|
+
digest: target.resolution.actualDigest!,
|
|
262
|
+
metadata: {
|
|
263
|
+
...target.metadata,
|
|
264
|
+
facetInstallationIds: [...new Set(target.facetInstallationIds)],
|
|
265
|
+
bindingIds: [...new Set(target.bindingIds)],
|
|
266
|
+
},
|
|
267
|
+
updatedAt: new Date(),
|
|
268
|
+
},
|
|
269
|
+
})
|
|
270
|
+
.returning();
|
|
271
|
+
if (!row) throw new Error(`Failed to record Pack component ${target.resolution.key}`);
|
|
272
|
+
components.push(mapStoredPackComponent(row));
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
components,
|
|
276
|
+
retainedFacetInstallationIds: [...new Set(retainedFacetInstallationIds)],
|
|
277
|
+
retainedBindingIds: [...new Set(retainedBindingIds)],
|
|
278
|
+
};
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export async function listPackInstallationComponents(
|
|
284
|
+
db: Database,
|
|
285
|
+
workspaceId: string,
|
|
286
|
+
packInstallationId: string,
|
|
287
|
+
): Promise<StoredPackInstallationComponent[]> {
|
|
288
|
+
return await withWorkspaceRls(
|
|
289
|
+
db,
|
|
290
|
+
workspaceId,
|
|
291
|
+
async (scopedDb) =>
|
|
292
|
+
await listPackInstallationComponentsInRlsContext(scopedDb, workspaceId, packInstallationId),
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function recordPackInlineSkillComponent(
|
|
297
|
+
db: Database,
|
|
298
|
+
input: {
|
|
299
|
+
accountId: string;
|
|
300
|
+
workspaceId: string;
|
|
301
|
+
packInstallationId: string;
|
|
302
|
+
componentKey: string;
|
|
303
|
+
capabilityId: string;
|
|
304
|
+
facetInstallationId: string;
|
|
305
|
+
contentSha256: string;
|
|
306
|
+
name: string;
|
|
307
|
+
},
|
|
308
|
+
): Promise<StoredPackInstallationComponent> {
|
|
309
|
+
return await withRlsContext(
|
|
310
|
+
db,
|
|
311
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
312
|
+
async (scopedDb) => {
|
|
313
|
+
const [row] = await scopedDb
|
|
314
|
+
.insert(schema.packInstallationComponents)
|
|
315
|
+
.values({
|
|
316
|
+
accountId: input.accountId,
|
|
317
|
+
workspaceId: input.workspaceId,
|
|
318
|
+
packInstallationId: input.packInstallationId,
|
|
319
|
+
componentKey: input.componentKey,
|
|
320
|
+
kind: "inline_skill",
|
|
321
|
+
capabilityId: input.capabilityId,
|
|
322
|
+
resolvedId: input.facetInstallationId,
|
|
323
|
+
digest: input.contentSha256,
|
|
324
|
+
metadata: {
|
|
325
|
+
name: input.name,
|
|
326
|
+
facetInstallationIds: [input.facetInstallationId],
|
|
327
|
+
bindingIds: [],
|
|
328
|
+
},
|
|
329
|
+
updatedAt: new Date(),
|
|
330
|
+
})
|
|
331
|
+
.onConflictDoUpdate({
|
|
332
|
+
target: [
|
|
333
|
+
schema.packInstallationComponents.packInstallationId,
|
|
334
|
+
schema.packInstallationComponents.componentKey,
|
|
335
|
+
],
|
|
336
|
+
set: {
|
|
337
|
+
kind: "inline_skill",
|
|
338
|
+
capabilityId: input.capabilityId,
|
|
339
|
+
resolvedId: input.facetInstallationId,
|
|
340
|
+
digest: input.contentSha256,
|
|
341
|
+
metadata: {
|
|
342
|
+
name: input.name,
|
|
343
|
+
facetInstallationIds: [input.facetInstallationId],
|
|
344
|
+
bindingIds: [],
|
|
345
|
+
},
|
|
346
|
+
updatedAt: new Date(),
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
.returning();
|
|
350
|
+
if (!row) throw new Error(`Failed to record inline Pack Skill ${input.componentKey}`);
|
|
351
|
+
return mapStoredPackComponent(row);
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export async function finalizePackComponentOwnership(
|
|
357
|
+
db: Database,
|
|
358
|
+
input: {
|
|
359
|
+
accountId: string;
|
|
360
|
+
workspaceId: string;
|
|
361
|
+
packInstallationId: string;
|
|
362
|
+
retainedComponentKeys: string[];
|
|
363
|
+
retainedFacetInstallationIds: string[];
|
|
364
|
+
retainedBindingIds: string[];
|
|
365
|
+
},
|
|
366
|
+
): Promise<void> {
|
|
367
|
+
await withRlsContext(
|
|
368
|
+
db,
|
|
369
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
370
|
+
async (scopedDb) =>
|
|
371
|
+
await scopedDb.transaction(async (txRaw) => {
|
|
372
|
+
const tx = txRaw as unknown as Database;
|
|
373
|
+
const retainedFacets = new Set(input.retainedFacetInstallationIds);
|
|
374
|
+
const ownedFacets = await tx
|
|
375
|
+
.select({
|
|
376
|
+
id: schema.capabilityComponentOwners.id,
|
|
377
|
+
facetInstallationId: schema.capabilityComponentOwners.facetInstallationId,
|
|
378
|
+
})
|
|
379
|
+
.from(schema.capabilityComponentOwners)
|
|
380
|
+
.where(
|
|
381
|
+
and(
|
|
382
|
+
eq(schema.capabilityComponentOwners.workspaceId, input.workspaceId),
|
|
383
|
+
eq(schema.capabilityComponentOwners.ownerKind, "pack"),
|
|
384
|
+
eq(schema.capabilityComponentOwners.ownerId, input.packInstallationId),
|
|
385
|
+
),
|
|
386
|
+
);
|
|
387
|
+
const staleFacets = ownedFacets.filter(
|
|
388
|
+
(owner) => !retainedFacets.has(owner.facetInstallationId),
|
|
389
|
+
);
|
|
390
|
+
if (staleFacets.length > 0) {
|
|
391
|
+
await tx.delete(schema.capabilityComponentOwners).where(
|
|
392
|
+
inArray(
|
|
393
|
+
schema.capabilityComponentOwners.id,
|
|
394
|
+
staleFacets.map((owner) => owner.id),
|
|
395
|
+
),
|
|
396
|
+
);
|
|
397
|
+
await cleanupOrphanedCapabilityComponents(
|
|
398
|
+
tx,
|
|
399
|
+
input.workspaceId,
|
|
400
|
+
staleFacets.map((owner) => owner.facetInstallationId),
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
const retainedBindings = new Set(input.retainedBindingIds);
|
|
404
|
+
const ownedBindings = await tx
|
|
405
|
+
.select({ bindingId: schema.integrationFeatureBindingOwners.bindingId })
|
|
406
|
+
.from(schema.integrationFeatureBindingOwners)
|
|
407
|
+
.where(
|
|
408
|
+
and(
|
|
409
|
+
eq(schema.integrationFeatureBindingOwners.workspaceId, input.workspaceId),
|
|
410
|
+
eq(schema.integrationFeatureBindingOwners.ownerKind, "pack"),
|
|
411
|
+
eq(schema.integrationFeatureBindingOwners.ownerId, input.packInstallationId),
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
for (const bindingId of [
|
|
415
|
+
...new Set(
|
|
416
|
+
ownedBindings
|
|
417
|
+
.map((owner) => owner.bindingId)
|
|
418
|
+
.filter((ownedBindingId) => !retainedBindings.has(ownedBindingId)),
|
|
419
|
+
),
|
|
420
|
+
]) {
|
|
421
|
+
await removeIntegrationFeatureBindingOwner(tx, {
|
|
422
|
+
workspaceId: input.workspaceId,
|
|
423
|
+
bindingId,
|
|
424
|
+
owner: { kind: "pack", id: input.packInstallationId },
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
const retainedKeys = [...new Set(input.retainedComponentKeys)];
|
|
428
|
+
await tx
|
|
429
|
+
.delete(schema.packInstallationComponents)
|
|
430
|
+
.where(
|
|
431
|
+
and(
|
|
432
|
+
eq(schema.packInstallationComponents.workspaceId, input.workspaceId),
|
|
433
|
+
eq(schema.packInstallationComponents.packInstallationId, input.packInstallationId),
|
|
434
|
+
retainedKeys.length > 0
|
|
435
|
+
? notInArray(schema.packInstallationComponents.componentKey, retainedKeys)
|
|
436
|
+
: sql`true`,
|
|
437
|
+
),
|
|
438
|
+
);
|
|
439
|
+
}),
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export async function previewPackComponentRelease(
|
|
444
|
+
db: Database,
|
|
445
|
+
workspaceId: string,
|
|
446
|
+
packInstallationId: string,
|
|
447
|
+
): Promise<
|
|
448
|
+
Array<{
|
|
449
|
+
key: string;
|
|
450
|
+
kind: PackComponentKind;
|
|
451
|
+
capabilityId: string;
|
|
452
|
+
retainedByOtherOwners: boolean;
|
|
453
|
+
}>
|
|
454
|
+
> {
|
|
455
|
+
return await withWorkspaceRls(
|
|
456
|
+
db,
|
|
457
|
+
workspaceId,
|
|
458
|
+
async (scopedDb) =>
|
|
459
|
+
await previewPackComponentReleaseInRlsContext(scopedDb, workspaceId, packInstallationId),
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function previewPackComponentReleaseInRlsContext(
|
|
464
|
+
scopedDb: Database,
|
|
465
|
+
workspaceId: string,
|
|
466
|
+
packInstallationId: string,
|
|
467
|
+
): Promise<
|
|
468
|
+
Array<{
|
|
469
|
+
key: string;
|
|
470
|
+
kind: PackComponentKind;
|
|
471
|
+
capabilityId: string;
|
|
472
|
+
retainedByOtherOwners: boolean;
|
|
473
|
+
}>
|
|
474
|
+
> {
|
|
475
|
+
const components = await listPackInstallationComponentsInRlsContext(
|
|
476
|
+
scopedDb,
|
|
477
|
+
workspaceId,
|
|
478
|
+
packInstallationId,
|
|
479
|
+
);
|
|
480
|
+
const result = [];
|
|
481
|
+
for (const component of components) {
|
|
482
|
+
const facetIds = stringArray(component.metadata.facetInstallationIds);
|
|
483
|
+
const bindingIds = stringArray(component.metadata.bindingIds);
|
|
484
|
+
const [facetOwner] =
|
|
485
|
+
facetIds.length === 0
|
|
486
|
+
? []
|
|
487
|
+
: await scopedDb
|
|
488
|
+
.select({ id: schema.capabilityComponentOwners.id })
|
|
489
|
+
.from(schema.capabilityComponentOwners)
|
|
490
|
+
.where(
|
|
491
|
+
and(
|
|
492
|
+
inArray(schema.capabilityComponentOwners.facetInstallationId, facetIds),
|
|
493
|
+
effectiveCapabilityOwnerSql(
|
|
494
|
+
schema.capabilityComponentOwners.ownerKind,
|
|
495
|
+
schema.capabilityComponentOwners.ownerId,
|
|
496
|
+
),
|
|
497
|
+
or(
|
|
498
|
+
ne(schema.capabilityComponentOwners.ownerKind, "pack"),
|
|
499
|
+
ne(schema.capabilityComponentOwners.ownerId, packInstallationId),
|
|
500
|
+
),
|
|
501
|
+
),
|
|
502
|
+
)
|
|
503
|
+
.limit(1);
|
|
504
|
+
const [bindingOwner] =
|
|
505
|
+
bindingIds.length === 0
|
|
506
|
+
? []
|
|
507
|
+
: await scopedDb
|
|
508
|
+
.select({ id: schema.integrationFeatureBindingOwners.id })
|
|
509
|
+
.from(schema.integrationFeatureBindingOwners)
|
|
510
|
+
.where(
|
|
511
|
+
and(
|
|
512
|
+
inArray(schema.integrationFeatureBindingOwners.bindingId, bindingIds),
|
|
513
|
+
effectiveCapabilityOwnerSql(
|
|
514
|
+
schema.integrationFeatureBindingOwners.ownerKind,
|
|
515
|
+
schema.integrationFeatureBindingOwners.ownerId,
|
|
516
|
+
),
|
|
517
|
+
or(
|
|
518
|
+
ne(schema.integrationFeatureBindingOwners.ownerKind, "pack"),
|
|
519
|
+
ne(schema.integrationFeatureBindingOwners.ownerId, packInstallationId),
|
|
520
|
+
),
|
|
521
|
+
),
|
|
522
|
+
)
|
|
523
|
+
.limit(1);
|
|
524
|
+
result.push({
|
|
525
|
+
key: component.key,
|
|
526
|
+
kind: component.kind,
|
|
527
|
+
capabilityId: component.capabilityId,
|
|
528
|
+
retainedByOtherOwners: Boolean(facetOwner || bindingOwner),
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export async function releasePackComponents(
|
|
535
|
+
db: Database,
|
|
536
|
+
input: { accountId: string; workspaceId: string; packInstallationId: string },
|
|
537
|
+
): Promise<{ retainedComponents: string[] }> {
|
|
538
|
+
return await withRlsContext(
|
|
539
|
+
db,
|
|
540
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
541
|
+
async (scopedDb) =>
|
|
542
|
+
await scopedDb.transaction(async (txRaw) => {
|
|
543
|
+
const tx = txRaw as unknown as Database;
|
|
544
|
+
const preview = await previewPackComponentReleaseInRlsContext(
|
|
545
|
+
tx,
|
|
546
|
+
input.workspaceId,
|
|
547
|
+
input.packInstallationId,
|
|
548
|
+
);
|
|
549
|
+
await removeIntegrationFeatureBindingOwnersForOwner(tx, {
|
|
550
|
+
workspaceId: input.workspaceId,
|
|
551
|
+
owner: { kind: "pack", id: input.packInstallationId },
|
|
552
|
+
});
|
|
553
|
+
const deletedOwners = await tx
|
|
554
|
+
.delete(schema.capabilityComponentOwners)
|
|
555
|
+
.where(
|
|
556
|
+
and(
|
|
557
|
+
eq(schema.capabilityComponentOwners.workspaceId, input.workspaceId),
|
|
558
|
+
eq(schema.capabilityComponentOwners.ownerKind, "pack"),
|
|
559
|
+
eq(schema.capabilityComponentOwners.ownerId, input.packInstallationId),
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
.returning({ facetInstallationId: schema.capabilityComponentOwners.facetInstallationId });
|
|
563
|
+
await cleanupOrphanedCapabilityComponents(
|
|
564
|
+
tx,
|
|
565
|
+
input.workspaceId,
|
|
566
|
+
deletedOwners.map((row) => row.facetInstallationId),
|
|
567
|
+
);
|
|
568
|
+
await tx
|
|
569
|
+
.delete(schema.packInstallationComponents)
|
|
570
|
+
.where(
|
|
571
|
+
and(
|
|
572
|
+
eq(schema.packInstallationComponents.workspaceId, input.workspaceId),
|
|
573
|
+
eq(schema.packInstallationComponents.packInstallationId, input.packInstallationId),
|
|
574
|
+
),
|
|
575
|
+
);
|
|
576
|
+
return {
|
|
577
|
+
retainedComponents: preview
|
|
578
|
+
.filter((component) => component.retainedByOtherOwners)
|
|
579
|
+
.map((component) => component.capabilityId),
|
|
580
|
+
};
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async function resolvePackComponentTargets(
|
|
586
|
+
db: Database,
|
|
587
|
+
workspaceId: string,
|
|
588
|
+
references: readonly CapabilityPackComponentReference[],
|
|
589
|
+
): Promise<ResolvedPackComponentTarget[]> {
|
|
590
|
+
const integrations = await listInstalledApiIntegrationsInRlsContext(db, workspaceId);
|
|
591
|
+
const targets: ResolvedPackComponentTarget[] = [];
|
|
592
|
+
for (const reference of references) {
|
|
593
|
+
if (reference.kind === "plugin") {
|
|
594
|
+
targets.push(await resolvePluginReference(db, workspaceId, reference));
|
|
595
|
+
} else if (reference.kind === "skill") {
|
|
596
|
+
targets.push(await resolveSkillReference(db, workspaceId, reference));
|
|
597
|
+
} else if (reference.kind === "integration") {
|
|
598
|
+
targets.push(await resolveIntegrationReference(db, workspaceId, reference, integrations));
|
|
599
|
+
} else {
|
|
600
|
+
targets.push(await resolveFeatureReference(db, workspaceId, reference, integrations));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return targets;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async function resolvePluginReference(
|
|
607
|
+
db: Database,
|
|
608
|
+
workspaceId: string,
|
|
609
|
+
reference: Extract<CapabilityPackComponentReference, { kind: "plugin" }>,
|
|
610
|
+
): Promise<ResolvedPackComponentTarget> {
|
|
611
|
+
const [row] = await db
|
|
612
|
+
.select({
|
|
613
|
+
pluginInstallationId: schema.capabilityPluginInstallations.id,
|
|
614
|
+
status: schema.capabilityPluginInstallations.status,
|
|
615
|
+
version: schema.capabilityPluginVersions.version,
|
|
616
|
+
manifestDigest: schema.capabilityPluginVersions.manifestDigest,
|
|
617
|
+
name: schema.capabilityPlugins.name,
|
|
618
|
+
})
|
|
619
|
+
.from(schema.capabilityPluginInstallations)
|
|
620
|
+
.innerJoin(
|
|
621
|
+
schema.capabilityPlugins,
|
|
622
|
+
eq(schema.capabilityPlugins.id, schema.capabilityPluginInstallations.pluginId),
|
|
623
|
+
)
|
|
624
|
+
.innerJoin(
|
|
625
|
+
schema.capabilityPluginVersions,
|
|
626
|
+
eq(schema.capabilityPluginVersions.id, schema.capabilityPluginInstallations.pluginVersionId),
|
|
627
|
+
)
|
|
628
|
+
.where(
|
|
629
|
+
and(
|
|
630
|
+
eq(schema.capabilityPluginInstallations.workspaceId, workspaceId),
|
|
631
|
+
eq(schema.capabilityPlugins.pluginKey, reference.pluginKey),
|
|
632
|
+
),
|
|
633
|
+
)
|
|
634
|
+
.limit(1);
|
|
635
|
+
const expectedDigest = reference.manifestDigest;
|
|
636
|
+
const exact =
|
|
637
|
+
row?.status === "active" &&
|
|
638
|
+
row.version === reference.version &&
|
|
639
|
+
row.manifestDigest === reference.manifestDigest;
|
|
640
|
+
const facetInstallationIds = !row
|
|
641
|
+
? []
|
|
642
|
+
: (
|
|
643
|
+
await db
|
|
644
|
+
.select({ id: schema.capabilityComponentOwners.facetInstallationId })
|
|
645
|
+
.from(schema.capabilityComponentOwners)
|
|
646
|
+
.where(
|
|
647
|
+
and(
|
|
648
|
+
eq(schema.capabilityComponentOwners.workspaceId, workspaceId),
|
|
649
|
+
eq(schema.capabilityComponentOwners.ownerKind, "plugin"),
|
|
650
|
+
eq(schema.capabilityComponentOwners.ownerId, row.pluginInstallationId),
|
|
651
|
+
),
|
|
652
|
+
)
|
|
653
|
+
).map((entry) => entry.id);
|
|
654
|
+
const bindingIds = !row
|
|
655
|
+
? []
|
|
656
|
+
: (
|
|
657
|
+
await db
|
|
658
|
+
.select({ id: schema.integrationFeatureBindingOwners.bindingId })
|
|
659
|
+
.from(schema.integrationFeatureBindingOwners)
|
|
660
|
+
.where(
|
|
661
|
+
and(
|
|
662
|
+
eq(schema.integrationFeatureBindingOwners.workspaceId, workspaceId),
|
|
663
|
+
eq(schema.integrationFeatureBindingOwners.ownerKind, "plugin"),
|
|
664
|
+
eq(schema.integrationFeatureBindingOwners.ownerId, row.pluginInstallationId),
|
|
665
|
+
),
|
|
666
|
+
)
|
|
667
|
+
).map((entry) => entry.id);
|
|
668
|
+
return {
|
|
669
|
+
resolution: {
|
|
670
|
+
key: reference.key,
|
|
671
|
+
kind: "plugin",
|
|
672
|
+
capabilityId: `plugin:${reference.pluginKey}`,
|
|
673
|
+
required: reference.required,
|
|
674
|
+
status: !row ? "missing" : exact ? "ready" : "mismatch",
|
|
675
|
+
expectedDigest,
|
|
676
|
+
actualDigest: row?.manifestDigest ?? null,
|
|
677
|
+
resolvedId: exact ? row.pluginInstallationId : null,
|
|
678
|
+
label: row?.name ?? reference.pluginKey,
|
|
679
|
+
},
|
|
680
|
+
facetInstallationIds: exact ? facetInstallationIds : [],
|
|
681
|
+
bindingIds: exact ? bindingIds : [],
|
|
682
|
+
metadata: { pluginKey: reference.pluginKey, version: reference.version },
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async function resolveSkillReference(
|
|
687
|
+
db: Database,
|
|
688
|
+
workspaceId: string,
|
|
689
|
+
reference: Extract<CapabilityPackComponentReference, { kind: "skill" }>,
|
|
690
|
+
): Promise<ResolvedPackComponentTarget> {
|
|
691
|
+
const rows = await db
|
|
692
|
+
.select({
|
|
693
|
+
facetInstallationId: schema.capabilityFacetInstallations.id,
|
|
694
|
+
contentSha256: schema.capabilitySkillFacets.contentSha256,
|
|
695
|
+
name: schema.capabilitySkillFacets.name,
|
|
696
|
+
})
|
|
697
|
+
.from(schema.capabilitySkillFacets)
|
|
698
|
+
.innerJoin(
|
|
699
|
+
schema.capabilityFacetInstallations,
|
|
700
|
+
eq(schema.capabilityFacetInstallations.facetId, schema.capabilitySkillFacets.facetId),
|
|
701
|
+
)
|
|
702
|
+
.innerJoin(
|
|
703
|
+
schema.capabilityPluginInstallations,
|
|
704
|
+
eq(
|
|
705
|
+
schema.capabilityPluginInstallations.id,
|
|
706
|
+
schema.capabilityFacetInstallations.pluginInstallationId,
|
|
707
|
+
),
|
|
708
|
+
)
|
|
709
|
+
.where(
|
|
710
|
+
and(
|
|
711
|
+
eq(schema.capabilityFacetInstallations.workspaceId, workspaceId),
|
|
712
|
+
eq(schema.capabilitySkillFacets.capabilityId, reference.capabilityId),
|
|
713
|
+
eq(schema.capabilityFacetInstallations.status, "active"),
|
|
714
|
+
eq(schema.capabilityPluginInstallations.status, "active"),
|
|
715
|
+
sql`exists (
|
|
716
|
+
select 1 from ${schema.capabilityComponentOwners} owner
|
|
717
|
+
where owner.facet_installation_id = ${schema.capabilityFacetInstallations.id}
|
|
718
|
+
and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
|
|
719
|
+
)`,
|
|
720
|
+
),
|
|
721
|
+
)
|
|
722
|
+
.limit(8);
|
|
723
|
+
const exact = rows.find((row) => row.contentSha256 === reference.contentSha256);
|
|
724
|
+
const actual = exact ?? rows[0] ?? null;
|
|
725
|
+
return {
|
|
726
|
+
resolution: {
|
|
727
|
+
key: reference.key,
|
|
728
|
+
kind: "skill",
|
|
729
|
+
capabilityId: reference.capabilityId,
|
|
730
|
+
required: reference.required,
|
|
731
|
+
status: rows.length === 0 ? "missing" : exact ? "ready" : "mismatch",
|
|
732
|
+
expectedDigest: reference.contentSha256,
|
|
733
|
+
actualDigest: actual?.contentSha256 ?? null,
|
|
734
|
+
resolvedId: exact?.facetInstallationId ?? null,
|
|
735
|
+
label: actual?.name ?? reference.capabilityId,
|
|
736
|
+
},
|
|
737
|
+
facetInstallationIds: exact ? [exact.facetInstallationId] : [],
|
|
738
|
+
bindingIds: [],
|
|
739
|
+
metadata: {},
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
async function resolveIntegrationReference(
|
|
744
|
+
db: Database,
|
|
745
|
+
workspaceId: string,
|
|
746
|
+
reference: Extract<CapabilityPackComponentReference, { kind: "integration" }>,
|
|
747
|
+
integrations: readonly ApiIntegrationRuntime[],
|
|
748
|
+
): Promise<ResolvedPackComponentTarget> {
|
|
749
|
+
const candidates = integrations.filter(
|
|
750
|
+
(integration) =>
|
|
751
|
+
integration.capabilityId === reference.capabilityId &&
|
|
752
|
+
integration.instanceKey === reference.instanceKey,
|
|
753
|
+
);
|
|
754
|
+
const exact = candidates.find(
|
|
755
|
+
(integration) =>
|
|
756
|
+
integration.revision.id === reference.revisionId &&
|
|
757
|
+
integration.revision.contentSha256 === reference.contentSha256,
|
|
758
|
+
);
|
|
759
|
+
const actual = exact ?? candidates[0] ?? null;
|
|
760
|
+
const facetInstallationIds = exact
|
|
761
|
+
? await integrationFacetInstallationIds(db, exact.pluginInstallationId)
|
|
762
|
+
: [];
|
|
763
|
+
return {
|
|
764
|
+
resolution: {
|
|
765
|
+
key: reference.key,
|
|
766
|
+
kind: "integration",
|
|
767
|
+
capabilityId: reference.capabilityId,
|
|
768
|
+
required: reference.required,
|
|
769
|
+
status: candidates.length === 0 ? "missing" : exact ? "ready" : "mismatch",
|
|
770
|
+
expectedDigest: reference.contentSha256,
|
|
771
|
+
actualDigest: actual?.revision.contentSha256 ?? null,
|
|
772
|
+
resolvedId: exact?.instanceId ?? null,
|
|
773
|
+
label: actual?.displayName ?? reference.capabilityId,
|
|
774
|
+
},
|
|
775
|
+
facetInstallationIds,
|
|
776
|
+
bindingIds: exact ? [exact.instanceId] : [],
|
|
777
|
+
metadata: {
|
|
778
|
+
instanceKey: reference.instanceKey,
|
|
779
|
+
revisionId: reference.revisionId,
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
async function resolveFeatureReference(
|
|
785
|
+
db: Database,
|
|
786
|
+
workspaceId: string,
|
|
787
|
+
reference: Extract<CapabilityPackComponentReference, { kind: "feature" }>,
|
|
788
|
+
integrations: readonly ApiIntegrationRuntime[],
|
|
789
|
+
): Promise<ResolvedPackComponentTarget> {
|
|
790
|
+
const integration = integrations.find(
|
|
791
|
+
(candidate) =>
|
|
792
|
+
candidate.capabilityId === reference.capabilityId &&
|
|
793
|
+
candidate.instanceKey === reference.instanceKey,
|
|
794
|
+
);
|
|
795
|
+
if (!integration) {
|
|
796
|
+
return missingTarget(reference, "feature", reference.capabilityId, reference.configDigest);
|
|
797
|
+
}
|
|
798
|
+
const [integrationFacetInstallationId] = await integrationFacetInstallationIds(
|
|
799
|
+
db,
|
|
800
|
+
integration.pluginInstallationId,
|
|
801
|
+
"integration",
|
|
802
|
+
);
|
|
803
|
+
if (!integrationFacetInstallationId) {
|
|
804
|
+
return missingTarget(reference, "feature", reference.capabilityId, reference.configDigest);
|
|
805
|
+
}
|
|
806
|
+
const [binding] = await db
|
|
807
|
+
.select({
|
|
808
|
+
id: schema.integrationFeatureBindings.id,
|
|
809
|
+
config: schema.integrationFeatureBindings.config,
|
|
810
|
+
status: schema.integrationFeatureBindings.status,
|
|
811
|
+
displayName: schema.integrationFeatureBindings.displayName,
|
|
812
|
+
})
|
|
813
|
+
.from(schema.integrationFeatureBindings)
|
|
814
|
+
.innerJoin(
|
|
815
|
+
schema.integrationFeatureFacets,
|
|
816
|
+
eq(schema.integrationFeatureFacets.id, schema.integrationFeatureBindings.featureFacetId),
|
|
817
|
+
)
|
|
818
|
+
.where(
|
|
819
|
+
and(
|
|
820
|
+
eq(schema.integrationFeatureBindings.workspaceId, workspaceId),
|
|
821
|
+
eq(
|
|
822
|
+
schema.integrationFeatureBindings.integrationFacetInstallationId,
|
|
823
|
+
integrationFacetInstallationId,
|
|
824
|
+
),
|
|
825
|
+
eq(schema.integrationFeatureFacets.featureKey, reference.featureKey),
|
|
826
|
+
eq(schema.integrationFeatureBindings.bindingKey, reference.bindingKey),
|
|
827
|
+
),
|
|
828
|
+
)
|
|
829
|
+
.limit(1);
|
|
830
|
+
const actualDigest = binding ? sha256(stableJson(binding.config)) : null;
|
|
831
|
+
const exact =
|
|
832
|
+
binding?.status === "active" &&
|
|
833
|
+
actualDigest !== null &&
|
|
834
|
+
actualDigest === reference.configDigest;
|
|
835
|
+
return {
|
|
836
|
+
resolution: {
|
|
837
|
+
key: reference.key,
|
|
838
|
+
kind: "feature",
|
|
839
|
+
capabilityId: reference.capabilityId,
|
|
840
|
+
required: reference.required,
|
|
841
|
+
status: !binding ? "missing" : exact ? "ready" : "mismatch",
|
|
842
|
+
expectedDigest: reference.configDigest,
|
|
843
|
+
actualDigest,
|
|
844
|
+
resolvedId: exact ? binding.id : null,
|
|
845
|
+
label: binding?.displayName ?? reference.featureKey,
|
|
846
|
+
},
|
|
847
|
+
facetInstallationIds: exact ? [integrationFacetInstallationId] : [],
|
|
848
|
+
bindingIds: exact ? [binding.id] : [],
|
|
849
|
+
metadata: {
|
|
850
|
+
instanceKey: reference.instanceKey,
|
|
851
|
+
featureKey: reference.featureKey,
|
|
852
|
+
bindingKey: reference.bindingKey,
|
|
853
|
+
},
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
async function integrationFacetInstallationIds(
|
|
858
|
+
db: Database,
|
|
859
|
+
pluginInstallationId: string,
|
|
860
|
+
onlyKind?: "integration" | "api",
|
|
861
|
+
): Promise<string[]> {
|
|
862
|
+
const rows = await db
|
|
863
|
+
.select({ id: schema.capabilityFacetInstallations.id, kind: schema.capabilityFacets.kind })
|
|
864
|
+
.from(schema.capabilityFacetInstallations)
|
|
865
|
+
.innerJoin(
|
|
866
|
+
schema.capabilityFacets,
|
|
867
|
+
eq(schema.capabilityFacets.id, schema.capabilityFacetInstallations.facetId),
|
|
868
|
+
)
|
|
869
|
+
.where(
|
|
870
|
+
and(
|
|
871
|
+
eq(schema.capabilityFacetInstallations.pluginInstallationId, pluginInstallationId),
|
|
872
|
+
onlyKind
|
|
873
|
+
? eq(schema.capabilityFacets.kind, onlyKind)
|
|
874
|
+
: inArray(schema.capabilityFacets.kind, ["integration", "api"]),
|
|
875
|
+
),
|
|
876
|
+
);
|
|
877
|
+
return rows.map((row) => row.id);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
async function listPackInstallationComponentsInRlsContext(
|
|
881
|
+
scopedDb: Database,
|
|
882
|
+
workspaceId: string,
|
|
883
|
+
packInstallationId: string,
|
|
884
|
+
): Promise<StoredPackInstallationComponent[]> {
|
|
885
|
+
const rows = await scopedDb
|
|
886
|
+
.select()
|
|
887
|
+
.from(schema.packInstallationComponents)
|
|
888
|
+
.where(
|
|
889
|
+
and(
|
|
890
|
+
eq(schema.packInstallationComponents.workspaceId, workspaceId),
|
|
891
|
+
eq(schema.packInstallationComponents.packInstallationId, packInstallationId),
|
|
892
|
+
),
|
|
893
|
+
)
|
|
894
|
+
.orderBy(asc(schema.packInstallationComponents.componentKey));
|
|
895
|
+
return rows.map(mapStoredPackComponent);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function missingTarget(
|
|
899
|
+
reference: { key: string; required: boolean },
|
|
900
|
+
kind: PackComponentKind,
|
|
901
|
+
capabilityId: string,
|
|
902
|
+
expectedDigest: string,
|
|
903
|
+
): ResolvedPackComponentTarget {
|
|
904
|
+
return {
|
|
905
|
+
resolution: {
|
|
906
|
+
key: reference.key,
|
|
907
|
+
kind,
|
|
908
|
+
capabilityId,
|
|
909
|
+
required: reference.required,
|
|
910
|
+
status: "missing",
|
|
911
|
+
expectedDigest,
|
|
912
|
+
actualDigest: null,
|
|
913
|
+
resolvedId: null,
|
|
914
|
+
label: capabilityId,
|
|
915
|
+
},
|
|
916
|
+
facetInstallationIds: [],
|
|
917
|
+
bindingIds: [],
|
|
918
|
+
metadata: {},
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function mapStoredPackComponent(
|
|
923
|
+
row: typeof schema.packInstallationComponents.$inferSelect,
|
|
924
|
+
): StoredPackInstallationComponent {
|
|
925
|
+
if (
|
|
926
|
+
row.kind !== "plugin" &&
|
|
927
|
+
row.kind !== "skill" &&
|
|
928
|
+
row.kind !== "integration" &&
|
|
929
|
+
row.kind !== "feature" &&
|
|
930
|
+
row.kind !== "inline_skill"
|
|
931
|
+
) {
|
|
932
|
+
throw new Error(`Unknown Pack component kind: ${row.kind}`);
|
|
933
|
+
}
|
|
934
|
+
return {
|
|
935
|
+
id: row.id,
|
|
936
|
+
packInstallationId: row.packInstallationId,
|
|
937
|
+
key: row.componentKey,
|
|
938
|
+
kind: row.kind,
|
|
939
|
+
capabilityId: row.capabilityId,
|
|
940
|
+
resolvedId: row.resolvedId,
|
|
941
|
+
digest: row.digest,
|
|
942
|
+
metadata: row.metadata,
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function stringArray(value: unknown): string[] {
|
|
947
|
+
return Array.isArray(value)
|
|
948
|
+
? value.filter((entry): entry is string => typeof entry === "string")
|
|
949
|
+
: [];
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function sha256(value: string): string {
|
|
953
|
+
return createHash("sha256").update(value).digest("hex");
|
|
954
|
+
}
|