@opengeni/db 5.0.1-canary.2 → 5.0.1-canary.3
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/capability-components.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +235 -34
- package/dist/index.js.map +1 -1
- package/dist/plugin-packages.d.ts +9 -12
- package/dist/skill-source-release-impact.d.ts +18 -0
- package/dist/skill-source-release.d.ts +9 -0
- package/drizzle/0475_session_event_history_statistics.sql +15 -0
- package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
- package/package.json +6 -6
- package/src/capability-components.ts +2 -0
- package/src/index.ts +1 -0
- package/src/plugin-packages.ts +232 -42
- package/src/skill-source-release-impact.ts +33 -0
- package/src/skill-source-release.ts +67 -26
|
@@ -36,4 +36,4 @@ export declare function assertCapabilityComponentVersionCanChange(db: Database,
|
|
|
36
36
|
* have claimed a component while its installation is pending or repairing,
|
|
37
37
|
* even though that owner is intentionally hidden from runtime reads.
|
|
38
38
|
*/
|
|
39
|
-
export declare function cleanupOrphanedCapabilityComponents(db: Database, workspaceId: string, facetInstallationIds: string[], skillActor?: SkillActor): Promise<SkillSourceReleaseReceipt[]>;
|
|
39
|
+
export declare function cleanupOrphanedCapabilityComponents(db: Database, workspaceId: string, facetInstallationIds: string[], skillActor?: SkillActor, lockedSkillHeadIds?: readonly string[]): Promise<SkillSourceReleaseReceipt[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -10532,7 +10532,7 @@ export { buildHostConnectionTokenResolver, buildHostGatewayConnectionTokenResolv
|
|
|
10532
10532
|
export { CapabilityComponentVersionConflictError, cleanupOrphanedCapabilityComponents, effectiveCapabilityOwnerSql, type CapabilityComponentOwnerIdentity, } from "./capability-components.js";
|
|
10533
10533
|
export { adoptPackComponentReferences, finalizePackComponentOwnership, listPackInstallationComponents, PackComponentResolutionError, previewPackComponentRelease, recordPackInlineSkillComponent, releasePackComponents, resolvePackComponentReferences, resolvePackInlineSkillReferences, type PackInlineSkillRequirement, type StoredPackInstallationComponent, } from "./pack-components.js";
|
|
10534
10534
|
export { deferPackInstallationOperation, finalizePackInstallationOperation, finalizePackUninstallOperation, PackManifestChangedError, PackOperationClaimLostError, PackOperationInProgressError, PackInstallationVersionConflictError, PackInstallationVersionRequiredError, PackOperationIdempotencyError, preparePackInstallationOperation, preparePackUninstallOperation, touchPackInstallationOperation, type PreparedPackInstallation, } from "./pack-installations.js";
|
|
10535
|
-
export { checkpointPluginPackageOperation, deferPluginPackageOperation, finalizePluginPackageInstall, getInstalledPluginPackage, getPluginPackageUninstallPreview, installPluginMcpReference, listInstalledPluginPackages, PluginInstallationVersionConflictError, PluginInstallationVersionRequiredError, PluginOperationIdempotencyError, preparePluginPackageInstall, uninstallPluginPackage, type InstalledPluginPackage, type InstalledPluginPackageSummary, type PluginBomComponent, type PreparedPluginPackage, } from "./plugin-packages.js";
|
|
10535
|
+
export { checkpointPluginPackageOperation, deferPluginPackageOperation, finalizePluginPackageInstall, getInstalledPluginPackage, getPluginPackageUninstallPreview, PluginUninstallPreviewChangedError, installPluginMcpReference, listInstalledPluginPackages, PluginInstallationVersionConflictError, PluginInstallationVersionRequiredError, PluginOperationIdempotencyError, preparePluginPackageInstall, uninstallPluginPackage, type InstalledPluginPackage, type InstalledPluginPackageSummary, type PluginBomComponent, type PreparedPluginPackage, } from "./plugin-packages.js";
|
|
10536
10536
|
export * from "./workspace-artifacts.js";
|
|
10537
10537
|
export * from "./mcp-oauth.js";
|
|
10538
10538
|
export * from "./tool-gateway-approvals.js";
|
package/dist/index.js
CHANGED
|
@@ -3798,7 +3798,40 @@ async function getPreferenceRegistryFullContent(db, claims, handle) {
|
|
|
3798
3798
|
});
|
|
3799
3799
|
}
|
|
3800
3800
|
|
|
3801
|
+
// src/skill-source-release-impact.ts
|
|
3802
|
+
function classifySkillSourceRelease(head, workspaceId) {
|
|
3803
|
+
if (head.status !== "active" || !head.active_revision_id)
|
|
3804
|
+
return { disposition: "inactive", retentionReasons: [] };
|
|
3805
|
+
const retentionReasons = [];
|
|
3806
|
+
if (head.provenance_source !== "portable_skill") retentionReasons.push("customized");
|
|
3807
|
+
if (head.scope !== "workspace" || head.scope_workspace_id !== workspaceId)
|
|
3808
|
+
retentionReasons.push("re_scoped");
|
|
3809
|
+
return {
|
|
3810
|
+
disposition: retentionReasons.length ? "retained" : "removed",
|
|
3811
|
+
retentionReasons
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3801
3815
|
// src/skill-source-release.ts
|
|
3816
|
+
async function readSkillSourceReleaseHeads(db, workspaceId, facetInstallationIds) {
|
|
3817
|
+
if (!facetInstallationIds.length) return [];
|
|
3818
|
+
return rawRows(
|
|
3819
|
+
db,
|
|
3820
|
+
sql12`
|
|
3821
|
+
SELECT DISTINCT h.id,h.account_id,h.scope,h.scope_workspace_id,h.scope_version,
|
|
3822
|
+
h.status,h.active_revision_id,r.provenance_source,r.title
|
|
3823
|
+
FROM skill_source_bindings b
|
|
3824
|
+
JOIN capability_facet_installations fi ON fi.facet_id=b.skill_facet_id
|
|
3825
|
+
AND fi.account_id=b.account_id AND fi.workspace_id=b.workspace_id
|
|
3826
|
+
JOIN preference_registry_preferences h ON h.id=b.preference_id AND h.account_id=b.account_id
|
|
3827
|
+
LEFT JOIN preference_registry_revisions r ON r.id=h.active_revision_id
|
|
3828
|
+
AND r.preference_id=h.id AND r.account_id=h.account_id
|
|
3829
|
+
WHERE b.workspace_id=${workspaceId}::uuid
|
|
3830
|
+
AND fi.id=ANY(string_to_array(${facetInstallationIds.join(",")},',')::uuid[])
|
|
3831
|
+
ORDER BY h.id
|
|
3832
|
+
`
|
|
3833
|
+
);
|
|
3834
|
+
}
|
|
3802
3835
|
var SkillSourceRemovalAuthorityError = class extends Error {
|
|
3803
3836
|
code = "skill_source_removal_requires_human";
|
|
3804
3837
|
constructor() {
|
|
@@ -3808,6 +3841,12 @@ var SkillSourceRemovalAuthorityError = class extends Error {
|
|
|
3808
3841
|
this.name = "SkillSourceRemovalAuthorityError";
|
|
3809
3842
|
}
|
|
3810
3843
|
};
|
|
3844
|
+
var SkillSourceReleaseSnapshotChangedError = class extends Error {
|
|
3845
|
+
constructor() {
|
|
3846
|
+
super("Skill source visibility changed during removal; review a refreshed preview.");
|
|
3847
|
+
this.name = "SkillSourceReleaseSnapshotChangedError";
|
|
3848
|
+
}
|
|
3849
|
+
};
|
|
3811
3850
|
async function releaseOrphanedSkillHeads(db, input) {
|
|
3812
3851
|
if (!input.facetInstallationIds.length) return [];
|
|
3813
3852
|
const bindings = await rawRows(
|
|
@@ -3827,24 +3866,30 @@ async function releaseOrphanedSkillHeads(db, input) {
|
|
|
3827
3866
|
throw new SkillSourceRemovalAuthorityError();
|
|
3828
3867
|
}
|
|
3829
3868
|
return withWorkspaceSubjectRls(db, input.workspaceId, actor.subjectId, async (tx) => {
|
|
3830
|
-
|
|
3869
|
+
const visibleHeads = await readSkillSourceReleaseHeads(
|
|
3870
|
+
tx,
|
|
3871
|
+
input.workspaceId,
|
|
3872
|
+
input.facetInstallationIds
|
|
3873
|
+
);
|
|
3874
|
+
const lockedHeadIds = new Set(input.lockedHeadIds ?? visibleHeads.map((head) => head.id));
|
|
3875
|
+
if (visibleHeads.some((head) => !lockedHeadIds.has(head.id)))
|
|
3876
|
+
throw new SkillSourceReleaseSnapshotChangedError();
|
|
3877
|
+
for (const binding of input.lockedHeadIds === void 0 ? visibleHeads : []) {
|
|
3831
3878
|
await tx.execute(
|
|
3832
3879
|
sql12`SELECT preference_id FROM preference_registry_lock_heads(ARRAY[${binding.id}::uuid])`
|
|
3833
3880
|
);
|
|
3834
3881
|
}
|
|
3835
|
-
const rows = await
|
|
3882
|
+
const rows = await readSkillSourceReleaseHeads(
|
|
3836
3883
|
tx,
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
FROM preference_registry_preferences h LEFT JOIN preference_registry_revisions r
|
|
3840
|
-
ON r.id=h.active_revision_id AND r.preference_id=h.id AND r.account_id=h.account_id
|
|
3841
|
-
WHERE h.id=ANY(string_to_array(${bindings.map((binding) => binding.id).join(",")},',')::uuid[])
|
|
3842
|
-
ORDER BY h.id
|
|
3843
|
-
`
|
|
3884
|
+
input.workspaceId,
|
|
3885
|
+
input.facetInstallationIds
|
|
3844
3886
|
);
|
|
3887
|
+
if (rows.some((head) => !lockedHeadIds.has(head.id)))
|
|
3888
|
+
throw new SkillSourceReleaseSnapshotChangedError();
|
|
3845
3889
|
const receipts = [];
|
|
3846
3890
|
for (const head of rows) {
|
|
3847
|
-
|
|
3891
|
+
const impact = classifySkillSourceRelease(head, input.workspaceId);
|
|
3892
|
+
if (impact.disposition === "inactive") {
|
|
3848
3893
|
receipts.push({
|
|
3849
3894
|
skillId: head.id,
|
|
3850
3895
|
revisionId: head.active_revision_id,
|
|
@@ -3852,7 +3897,7 @@ async function releaseOrphanedSkillHeads(db, input) {
|
|
|
3852
3897
|
eventId: null,
|
|
3853
3898
|
warning: null
|
|
3854
3899
|
});
|
|
3855
|
-
} else if (
|
|
3900
|
+
} else if (impact.disposition === "retained") {
|
|
3856
3901
|
receipts.push({
|
|
3857
3902
|
skillId: head.id,
|
|
3858
3903
|
revisionId: head.active_revision_id,
|
|
@@ -10179,7 +10224,7 @@ async function assertCapabilityComponentVersionCanChange(db, input) {
|
|
|
10179
10224
|
throw new CapabilityComponentVersionConflictError(input.pluginKey);
|
|
10180
10225
|
}
|
|
10181
10226
|
}
|
|
10182
|
-
async function cleanupOrphanedCapabilityComponents(db, workspaceId, facetInstallationIds, skillActor) {
|
|
10227
|
+
async function cleanupOrphanedCapabilityComponents(db, workspaceId, facetInstallationIds, skillActor, lockedSkillHeadIds) {
|
|
10183
10228
|
const uniqueIds = [...new Set(facetInstallationIds)];
|
|
10184
10229
|
if (uniqueIds.length === 0) return [];
|
|
10185
10230
|
await db.execute(sql26`SELECT p.id FROM capability_plugin_installations p
|
|
@@ -10204,7 +10249,8 @@ async function cleanupOrphanedCapabilityComponents(db, workspaceId, facetInstall
|
|
|
10204
10249
|
const skillReleases = await releaseOrphanedSkillHeads(db, {
|
|
10205
10250
|
workspaceId,
|
|
10206
10251
|
facetInstallationIds: orphanRows.map((row) => row.facetInstallationId),
|
|
10207
|
-
...skillActor ? { skillActor } : {}
|
|
10252
|
+
...skillActor ? { skillActor } : {},
|
|
10253
|
+
...lockedSkillHeadIds !== void 0 ? { lockedHeadIds: lockedSkillHeadIds } : {}
|
|
10208
10254
|
});
|
|
10209
10255
|
await db.delete(capabilityFacetInstallations).where(
|
|
10210
10256
|
inArray5(
|
|
@@ -33932,7 +33978,9 @@ function mapPackInstallation(row) {
|
|
|
33932
33978
|
|
|
33933
33979
|
// src/plugin-packages.ts
|
|
33934
33980
|
import { createHash as createHash27 } from "crypto";
|
|
33935
|
-
import {
|
|
33981
|
+
import {
|
|
33982
|
+
stableJson as stableJson14
|
|
33983
|
+
} from "@opengeni/contracts";
|
|
33936
33984
|
import { and as and40, asc as asc18, eq as eq41, inArray as inArray23, ne as ne6, or as or9, sql as sql66 } from "drizzle-orm";
|
|
33937
33985
|
var PluginOperationIdempotencyError = class extends Error {
|
|
33938
33986
|
name = "PluginOperationIdempotencyError";
|
|
@@ -33940,6 +33988,14 @@ var PluginOperationIdempotencyError = class extends Error {
|
|
|
33940
33988
|
var PluginInstallationVersionConflictError = class extends Error {
|
|
33941
33989
|
name = "PluginInstallationVersionConflictError";
|
|
33942
33990
|
};
|
|
33991
|
+
var PluginUninstallPreviewChangedError = class extends Error {
|
|
33992
|
+
constructor(preview) {
|
|
33993
|
+
super("Plugin removal impact changed. Review the refreshed preview before confirming again.");
|
|
33994
|
+
this.preview = preview;
|
|
33995
|
+
this.name = "PluginUninstallPreviewChangedError";
|
|
33996
|
+
}
|
|
33997
|
+
code = "plugin_uninstall_preview_changed";
|
|
33998
|
+
};
|
|
33943
33999
|
var PluginInstallationVersionRequiredError = class extends Error {
|
|
33944
34000
|
name = "PluginInstallationVersionRequiredError";
|
|
33945
34001
|
};
|
|
@@ -34533,22 +34589,20 @@ async function installPluginMcpReferenceInScope(db, input) {
|
|
|
34533
34589
|
}
|
|
34534
34590
|
);
|
|
34535
34591
|
}
|
|
34536
|
-
async function getPluginPackageUninstallPreview(db, workspaceId, pluginKey) {
|
|
34592
|
+
async function getPluginPackageUninstallPreview(db, workspaceId, pluginKey, subjectId) {
|
|
34537
34593
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
34594
|
+
if (subjectId) await setSubjectRlsContext(scopedDb, subjectId);
|
|
34538
34595
|
const plugin = await pluginPackageInScope(scopedDb, workspaceId, pluginKey);
|
|
34539
34596
|
if (!plugin || plugin.status === "disabled") {
|
|
34540
34597
|
return { installed: false, version: null, installationVersion: null, components: [] };
|
|
34541
34598
|
}
|
|
34542
|
-
const
|
|
34543
|
-
scopedDb,
|
|
34544
|
-
workspaceId,
|
|
34545
|
-
plugin.pluginInstallationId
|
|
34546
|
-
);
|
|
34599
|
+
const impact = await pluginOwnedComponents(scopedDb, workspaceId, plugin.pluginInstallationId);
|
|
34547
34600
|
return {
|
|
34548
34601
|
installed: true,
|
|
34549
34602
|
version: plugin.version,
|
|
34550
34603
|
installationVersion: plugin.installationVersion,
|
|
34551
|
-
components
|
|
34604
|
+
components: impact.components,
|
|
34605
|
+
previewToken: pluginUninstallPreviewToken(plugin, impact.stateToken)
|
|
34552
34606
|
};
|
|
34553
34607
|
});
|
|
34554
34608
|
}
|
|
@@ -34556,7 +34610,8 @@ async function uninstallPluginPackage(db, input) {
|
|
|
34556
34610
|
const requestDigest4 = sha2567(
|
|
34557
34611
|
stableJson14({
|
|
34558
34612
|
pluginKey: input.pluginKey,
|
|
34559
|
-
expectedInstallationVersion: input.expectedInstallationVersion
|
|
34613
|
+
expectedInstallationVersion: input.expectedInstallationVersion,
|
|
34614
|
+
...input.expectedPreviewToken ? { expectedPreviewToken: input.expectedPreviewToken } : {}
|
|
34560
34615
|
})
|
|
34561
34616
|
);
|
|
34562
34617
|
return await withRlsContext(
|
|
@@ -34566,6 +34621,7 @@ async function uninstallPluginPackage(db, input) {
|
|
|
34566
34621
|
await setSubjectRlsContext(scopedDb, input.subjectId);
|
|
34567
34622
|
return await scopedDb.transaction(async (txRaw) => {
|
|
34568
34623
|
const tx = txRaw;
|
|
34624
|
+
await lockSkillPublication(tx, input.workspaceId);
|
|
34569
34625
|
await tx.execute(
|
|
34570
34626
|
sql66`select pg_advisory_xact_lock(hashtextextended(${`capability-operation:${input.workspaceId}:${input.idempotencyKey}`}, 0))`
|
|
34571
34627
|
);
|
|
@@ -34603,11 +34659,69 @@ async function uninstallPluginPackage(db, input) {
|
|
|
34603
34659
|
if (plugin.installationVersion !== input.expectedInstallationVersion) {
|
|
34604
34660
|
throw new PluginInstallationVersionConflictError("Plugin installation changed");
|
|
34605
34661
|
}
|
|
34606
|
-
|
|
34662
|
+
await tx.execute(sql66`SELECT p.id FROM capability_plugin_installations p
|
|
34663
|
+
WHERE p.workspace_id=${input.workspaceId}::uuid AND p.id IN (
|
|
34664
|
+
SELECT fi.plugin_installation_id FROM capability_facet_installations fi
|
|
34665
|
+
JOIN capability_component_owners o ON o.facet_installation_id=fi.id
|
|
34666
|
+
WHERE o.workspace_id=${input.workspaceId}::uuid AND o.owner_kind='plugin'
|
|
34667
|
+
AND o.owner_id=${plugin.pluginInstallationId}
|
|
34668
|
+
) ORDER BY p.id FOR UPDATE`);
|
|
34669
|
+
try {
|
|
34670
|
+
await tx.execute(sql66`SELECT fi.id FROM capability_facet_installations fi
|
|
34671
|
+
WHERE fi.workspace_id=${input.workspaceId}::uuid AND EXISTS (
|
|
34672
|
+
SELECT 1 FROM capability_component_owners own
|
|
34673
|
+
WHERE own.facet_installation_id=fi.id AND own.owner_kind='plugin'
|
|
34674
|
+
AND own.owner_id=${plugin.pluginInstallationId}
|
|
34675
|
+
) ORDER BY fi.id FOR UPDATE NOWAIT`);
|
|
34676
|
+
await tx.execute(sql66`SELECT o.id FROM capability_component_owners o
|
|
34677
|
+
WHERE o.workspace_id=${input.workspaceId}::uuid AND EXISTS (
|
|
34678
|
+
SELECT 1 FROM capability_component_owners own
|
|
34679
|
+
WHERE own.facet_installation_id=o.facet_installation_id
|
|
34680
|
+
AND own.owner_kind='plugin' AND own.owner_id=${plugin.pluginInstallationId}
|
|
34681
|
+
) ORDER BY o.id FOR UPDATE NOWAIT`);
|
|
34682
|
+
} catch (error) {
|
|
34683
|
+
if (nestedPostgresSqlState(error) === "55P03")
|
|
34684
|
+
throw new PluginUninstallPreviewChangedError();
|
|
34685
|
+
throw error;
|
|
34686
|
+
}
|
|
34687
|
+
const skillBindings = await rawRows(
|
|
34688
|
+
tx,
|
|
34689
|
+
sql66`
|
|
34690
|
+
SELECT DISTINCT h.id FROM skill_source_bindings b
|
|
34691
|
+
JOIN capability_facet_installations fi ON fi.facet_id=b.skill_facet_id
|
|
34692
|
+
AND fi.account_id=b.account_id AND fi.workspace_id=b.workspace_id
|
|
34693
|
+
JOIN capability_component_owners o ON o.facet_installation_id=fi.id
|
|
34694
|
+
JOIN preference_registry_preferences h ON h.id=b.preference_id AND h.account_id=b.account_id
|
|
34695
|
+
WHERE b.workspace_id=${input.workspaceId}::uuid AND o.owner_kind='plugin'
|
|
34696
|
+
AND o.owner_id=${plugin.pluginInstallationId} ORDER BY h.id`
|
|
34697
|
+
);
|
|
34698
|
+
for (const head of skillBindings) {
|
|
34699
|
+
try {
|
|
34700
|
+
await tx.execute(
|
|
34701
|
+
sql66`SELECT preference_id FROM preference_registry_lock_heads(ARRAY[${head.id}::uuid])`
|
|
34702
|
+
);
|
|
34703
|
+
} catch (error) {
|
|
34704
|
+
if (nestedPostgresSqlState(error) === "42501")
|
|
34705
|
+
throw new PluginUninstallPreviewChangedError();
|
|
34706
|
+
throw error;
|
|
34707
|
+
}
|
|
34708
|
+
}
|
|
34709
|
+
const impact = await pluginOwnedComponents(
|
|
34607
34710
|
tx,
|
|
34608
34711
|
input.workspaceId,
|
|
34609
34712
|
plugin.pluginInstallationId
|
|
34610
34713
|
);
|
|
34714
|
+
const previewToken = pluginUninstallPreviewToken(plugin, impact.stateToken);
|
|
34715
|
+
if (input.expectedPreviewToken && input.expectedPreviewToken !== previewToken) {
|
|
34716
|
+
throw new PluginUninstallPreviewChangedError({
|
|
34717
|
+
pluginKey: input.pluginKey,
|
|
34718
|
+
installed: true,
|
|
34719
|
+
version: plugin.version,
|
|
34720
|
+
installationVersion: plugin.installationVersion,
|
|
34721
|
+
previewToken,
|
|
34722
|
+
components: impact.components
|
|
34723
|
+
});
|
|
34724
|
+
}
|
|
34611
34725
|
await removeIntegrationFacetBindingOwnersForOwner(tx, {
|
|
34612
34726
|
workspaceId: input.workspaceId,
|
|
34613
34727
|
owner: { kind: "plugin", id: plugin.pluginInstallationId }
|
|
@@ -34619,18 +34733,26 @@ async function uninstallPluginPackage(db, input) {
|
|
|
34619
34733
|
eq41(capabilityComponentOwners.ownerId, plugin.pluginInstallationId)
|
|
34620
34734
|
)
|
|
34621
34735
|
).returning({ facetInstallationId: capabilityComponentOwners.facetInstallationId });
|
|
34622
|
-
|
|
34623
|
-
|
|
34624
|
-
|
|
34625
|
-
|
|
34626
|
-
|
|
34627
|
-
|
|
34736
|
+
let skillReleases;
|
|
34737
|
+
try {
|
|
34738
|
+
skillReleases = await cleanupOrphanedCapabilityComponents(
|
|
34739
|
+
tx,
|
|
34740
|
+
input.workspaceId,
|
|
34741
|
+
owned.map((row) => row.facetInstallationId),
|
|
34742
|
+
input.skillActor,
|
|
34743
|
+
skillBindings.map((head) => head.id)
|
|
34744
|
+
);
|
|
34745
|
+
} catch (error) {
|
|
34746
|
+
if (error instanceof SkillSourceReleaseSnapshotChangedError)
|
|
34747
|
+
throw new PluginUninstallPreviewChangedError();
|
|
34748
|
+
throw error;
|
|
34749
|
+
}
|
|
34628
34750
|
await tx.update(capabilityPluginInstallations).set({
|
|
34629
34751
|
status: "disabled",
|
|
34630
34752
|
version: plugin.installationVersion + 1,
|
|
34631
34753
|
updatedAt: /* @__PURE__ */ new Date()
|
|
34632
34754
|
}).where(eq41(capabilityPluginInstallations.id, plugin.pluginInstallationId));
|
|
34633
|
-
const retainedComponents = components.filter((component) => component.
|
|
34755
|
+
const retainedComponents = impact.components.filter((component) => component.disposition === "retained").map((component) => component.capabilityId);
|
|
34634
34756
|
await completeInlineOperation(tx, input, requestDigest4, {
|
|
34635
34757
|
status: "uninstalled",
|
|
34636
34758
|
retainedComponents,
|
|
@@ -34676,6 +34798,7 @@ async function pluginOwnedComponents(db, workspaceId, ownerPluginInstallationId)
|
|
|
34676
34798
|
facetInstallationId: capabilityFacetInstallations.id,
|
|
34677
34799
|
facetKind: capabilityFacets.kind,
|
|
34678
34800
|
skillCapabilityId: capabilitySkillFacets.capabilityId,
|
|
34801
|
+
name: capabilityPlugins.name,
|
|
34679
34802
|
manifest: capabilityPluginVersions.manifest
|
|
34680
34803
|
}).from(capabilityComponentOwners).innerJoin(
|
|
34681
34804
|
capabilityFacetInstallations,
|
|
@@ -34695,6 +34818,9 @@ async function pluginOwnedComponents(db, workspaceId, ownerPluginInstallationId)
|
|
|
34695
34818
|
).innerJoin(
|
|
34696
34819
|
capabilityPluginVersions,
|
|
34697
34820
|
eq41(capabilityPluginVersions.id, capabilityPluginInstallations.pluginVersionId)
|
|
34821
|
+
).innerJoin(
|
|
34822
|
+
capabilityPlugins,
|
|
34823
|
+
eq41(capabilityPlugins.id, capabilityPluginInstallations.pluginId)
|
|
34698
34824
|
).leftJoin(
|
|
34699
34825
|
capabilitySkillFacets,
|
|
34700
34826
|
eq41(capabilitySkillFacets.facetId, capabilityFacets.id)
|
|
@@ -34713,12 +34839,23 @@ async function pluginOwnedComponents(db, workspaceId, ownerPluginInstallationId)
|
|
|
34713
34839
|
if (!capabilityId || !kind) continue;
|
|
34714
34840
|
const existing = components.get(capabilityId);
|
|
34715
34841
|
if (existing) existing.facetIds.push(row.facetInstallationId);
|
|
34716
|
-
else
|
|
34842
|
+
else
|
|
34843
|
+
components.set(capabilityId, {
|
|
34844
|
+
capabilityId,
|
|
34845
|
+
kind,
|
|
34846
|
+
name: row.name,
|
|
34847
|
+
facetIds: [row.facetInstallationId]
|
|
34848
|
+
});
|
|
34717
34849
|
}
|
|
34718
34850
|
const result = [];
|
|
34851
|
+
const state = [];
|
|
34719
34852
|
for (const component of components.values()) {
|
|
34720
|
-
const
|
|
34853
|
+
const owners = await db.select({
|
|
34854
|
+
kind: capabilityComponentOwners.ownerKind,
|
|
34855
|
+
id: capabilityComponentOwners.ownerId
|
|
34856
|
+
}).from(capabilityComponentOwners).where(
|
|
34721
34857
|
and40(
|
|
34858
|
+
eq41(capabilityComponentOwners.workspaceId, workspaceId),
|
|
34722
34859
|
inArray23(capabilityComponentOwners.facetInstallationId, component.facetIds),
|
|
34723
34860
|
// Retention is deliberately physical, not runtime-effective. A Plugin
|
|
34724
34861
|
// in needs_attention may be resumable and must keep shared component
|
|
@@ -34731,13 +34868,76 @@ async function pluginOwnedComponents(db, workspaceId, ownerPluginInstallationId)
|
|
|
34731
34868
|
)
|
|
34732
34869
|
)
|
|
34733
34870
|
);
|
|
34871
|
+
const remainingOwners = [];
|
|
34872
|
+
for (const owner of owners.sort(
|
|
34873
|
+
(a, b) => `${a.kind}:${a.id}`.localeCompare(`${b.kind}:${b.id}`)
|
|
34874
|
+
)) {
|
|
34875
|
+
let name = owner.kind === "direct" ? "Direct installation" : owner.kind === "migration" ? "Migrated installation" : "Another installed owner";
|
|
34876
|
+
if (owner.kind === "plugin") {
|
|
34877
|
+
const [named] = await db.select({ manifest: capabilityPluginVersions.manifest }).from(capabilityPluginInstallations).innerJoin(
|
|
34878
|
+
capabilityPluginVersions,
|
|
34879
|
+
eq41(
|
|
34880
|
+
capabilityPluginVersions.id,
|
|
34881
|
+
capabilityPluginInstallations.pluginVersionId
|
|
34882
|
+
)
|
|
34883
|
+
).where(
|
|
34884
|
+
and40(
|
|
34885
|
+
eq41(capabilityPluginInstallations.workspaceId, workspaceId),
|
|
34886
|
+
sql66`${capabilityPluginInstallations.id}::text = ${owner.id}`
|
|
34887
|
+
)
|
|
34888
|
+
);
|
|
34889
|
+
name = stringValue5(objectValue3(named?.manifest).name) ?? name;
|
|
34890
|
+
} else if (owner.kind === "pack") {
|
|
34891
|
+
const [named] = await db.select({
|
|
34892
|
+
manifest: packInstallations.manifestSnapshot,
|
|
34893
|
+
packId: packInstallations.packId
|
|
34894
|
+
}).from(packInstallations).where(
|
|
34895
|
+
and40(
|
|
34896
|
+
eq41(packInstallations.workspaceId, workspaceId),
|
|
34897
|
+
or9(
|
|
34898
|
+
sql66`${packInstallations.id}::text = ${owner.id}`,
|
|
34899
|
+
eq41(packInstallations.packId, owner.id)
|
|
34900
|
+
)
|
|
34901
|
+
)
|
|
34902
|
+
);
|
|
34903
|
+
name = stringValue5(objectValue3(named?.manifest).name) ?? named?.packId ?? name;
|
|
34904
|
+
}
|
|
34905
|
+
if (!remainingOwners.some((item) => item.kind === owner.kind && item.name === name))
|
|
34906
|
+
remainingOwners.push({
|
|
34907
|
+
kind: owner.kind,
|
|
34908
|
+
name
|
|
34909
|
+
});
|
|
34910
|
+
}
|
|
34911
|
+
const heads = component.kind === "skill" ? await readSkillSourceReleaseHeads(db, workspaceId, component.facetIds) : [];
|
|
34912
|
+
const head = heads[0];
|
|
34913
|
+
const skillImpact = head ? classifySkillSourceRelease(head, workspaceId) : null;
|
|
34914
|
+
const retainedByOtherOwners = owners.length > 0;
|
|
34915
|
+
const retentionReasons = [];
|
|
34916
|
+
if (retainedByOtherOwners) retentionReasons.push("other_owners");
|
|
34917
|
+
if (skillImpact) retentionReasons.push(...skillImpact.retentionReasons);
|
|
34918
|
+
else if (component.kind === "skill") retentionReasons.push("registry_unavailable");
|
|
34919
|
+
state.push({ facetIds: component.facetIds, owners, heads });
|
|
34734
34920
|
result.push({
|
|
34735
34921
|
capabilityId: component.capabilityId,
|
|
34736
34922
|
kind: component.kind,
|
|
34737
|
-
|
|
34923
|
+
name: head?.title ?? component.name,
|
|
34924
|
+
retainedByOtherOwners,
|
|
34925
|
+
disposition: retainedByOtherOwners ? "retained" : skillImpact?.disposition ?? (component.kind === "skill" ? "retained" : "removed"),
|
|
34926
|
+
retentionReasons,
|
|
34927
|
+
remainingOwners,
|
|
34928
|
+
...head ? { skillId: head.id } : {}
|
|
34738
34929
|
});
|
|
34739
34930
|
}
|
|
34740
|
-
return result;
|
|
34931
|
+
return { components: result, stateToken: sha2567(stableJson14({ state, components: result })) };
|
|
34932
|
+
}
|
|
34933
|
+
function pluginUninstallPreviewToken(plugin, stateToken) {
|
|
34934
|
+
return sha2567(
|
|
34935
|
+
stableJson14({
|
|
34936
|
+
installationId: plugin.pluginInstallationId,
|
|
34937
|
+
version: plugin.installationVersion,
|
|
34938
|
+
stateToken
|
|
34939
|
+
})
|
|
34940
|
+
);
|
|
34741
34941
|
}
|
|
34742
34942
|
async function completeInlineOperation(db, input, requestDigest4, result) {
|
|
34743
34943
|
await db.insert(capabilityOperations).values({
|
|
@@ -94043,6 +94243,7 @@ export {
|
|
|
94043
94243
|
PluginInstallationVersionConflictError,
|
|
94044
94244
|
PluginInstallationVersionRequiredError,
|
|
94045
94245
|
PluginOperationIdempotencyError,
|
|
94246
|
+
PluginUninstallPreviewChangedError,
|
|
94046
94247
|
PortableSkillInstallationVersionConflictError,
|
|
94047
94248
|
PortableSkillInstallationVersionRequiredError,
|
|
94048
94249
|
PortableSkillSourcePathConflictError,
|