@capybara-hq/kernel 0.1.0-rc.3 → 0.1.0-rc.5
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/bootstrap.json +61 -5
- package/dist/admin/admin.controller.d.ts +47 -16
- package/dist/admin/admin.controller.js +44 -19
- package/dist/admin/extensions.controller.d.ts +24 -3
- package/dist/admin/extensions.controller.js +48 -8
- package/dist/admin/setup-lock.d.ts +1 -1
- package/dist/admin/setup-lock.js +1 -1
- package/dist/admin/setup.controller.d.ts +1 -1
- package/dist/admin/setup.controller.js +1 -1
- package/dist/admin/setup.service.d.ts +4 -4
- package/dist/admin/setup.service.js +8 -8
- package/dist/agents/agent-lookup.d.ts +19 -0
- package/dist/agents/agent-lookup.js +21 -0
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +168 -3
- package/dist/agents/agents.controller.js +201 -12
- package/dist/agents/agents.module.d.ts +4 -1
- package/dist/agents/agents.module.js +36 -6
- package/dist/agents/agents.service.d.ts +64 -27
- package/dist/agents/agents.service.js +199 -74
- package/dist/agents/definition.d.ts +53 -0
- package/dist/agents/definition.js +55 -0
- package/dist/agents/from-template.d.ts +106 -0
- package/dist/agents/from-template.js +649 -0
- package/dist/agents/from-template.service.d.ts +60 -0
- package/dist/agents/from-template.service.js +161 -0
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/org-templates.d.ts +176 -0
- package/dist/agents/org-templates.js +827 -0
- package/dist/agents/pending-connector-requests.d.ts +77 -0
- package/dist/agents/pending-connector-requests.js +215 -0
- package/dist/agents/pending-connectors.controller.d.ts +39 -0
- package/dist/agents/pending-connectors.controller.js +91 -0
- package/dist/agents/pending-connectors.service.d.ts +62 -0
- package/dist/agents/pending-connectors.service.js +282 -0
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.controller.d.ts +86 -5
- package/dist/agents/templates.controller.js +171 -7
- package/dist/agents/templates.d.ts +127 -23
- package/dist/agents/templates.js +476 -53
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +39 -0
- package/dist/background/finish.d.ts +44 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +15 -0
- package/dist/background/launch-lock.js +40 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +56 -0
- package/dist/background/poll.job.js +190 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +48 -0
- package/dist/background/session.js +79 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +68 -0
- package/dist/background/stop.js +243 -0
- package/dist/background/store.d.ts +162 -0
- package/dist/background/store.js +357 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +77 -0
- package/dist/background/tasks.service.js +211 -0
- package/dist/background/wake-rules.d.ts +66 -0
- package/dist/background/wake-rules.js +113 -0
- package/dist/background/wake.d.ts +30 -0
- package/dist/background/wake.js +63 -0
- package/dist/catalog/admin-catalog.controller.d.ts +129 -30
- package/dist/catalog/admin-catalog.controller.js +213 -42
- package/dist/catalog/catalog-content.d.ts +58 -32
- package/dist/catalog/catalog-content.js +34 -8
- package/dist/catalog/catalog-import.module.d.ts +7 -0
- package/dist/catalog/catalog-import.module.js +60 -0
- package/dist/catalog/catalog-items.d.ts +111 -7
- package/dist/catalog/catalog-items.js +279 -13
- package/dist/catalog/catalog-scope.service.d.ts +260 -0
- package/dist/catalog/catalog-scope.service.js +1116 -0
- package/dist/catalog/catalog.module.d.ts +7 -9
- package/dist/catalog/catalog.module.js +41 -31
- package/dist/catalog/catalog.service.d.ts +101 -29
- package/dist/catalog/catalog.service.js +104 -106
- package/dist/catalog/claude/agents.js +2 -0
- package/dist/catalog/cli.d.ts +11 -1
- package/dist/catalog/cli.js +26 -1
- package/dist/catalog/component-promotion.d.ts +96 -0
- package/dist/catalog/component-promotion.js +395 -0
- package/dist/catalog/component-scope.controller.d.ts +152 -0
- package/dist/catalog/component-scope.controller.js +287 -0
- package/dist/catalog/component-scope.service.d.ts +303 -0
- package/dist/catalog/component-scope.service.js +936 -0
- package/dist/catalog/component-state.d.ts +174 -0
- package/dist/catalog/component-state.js +437 -0
- package/dist/catalog/job-ids.d.ts +30 -0
- package/dist/catalog/job-ids.js +25 -0
- package/dist/catalog/marketplace-fetch.d.ts +1 -1
- package/dist/catalog/marketplaces.controller.d.ts +22 -5
- package/dist/catalog/marketplaces.controller.js +56 -7
- package/dist/catalog/marketplaces.service.d.ts +54 -14
- package/dist/catalog/marketplaces.service.js +71 -22
- package/dist/catalog/org-catalog.controller.d.ts +125 -34
- package/dist/catalog/org-catalog.controller.js +181 -65
- package/dist/catalog/org-catalog.service.d.ts +65 -33
- package/dist/catalog/org-catalog.service.js +324 -157
- package/dist/catalog/org-extension-switch.d.ts +41 -0
- package/dist/catalog/org-extension-switch.js +115 -0
- package/dist/catalog/org-extension.controller.d.ts +119 -0
- package/dist/catalog/org-extension.controller.js +266 -0
- package/dist/catalog/org-plugin-uploads.controller.d.ts +98 -0
- package/dist/catalog/org-plugin-uploads.controller.js +213 -0
- package/dist/catalog/org-plugin-uploads.d.ts +63 -0
- package/dist/catalog/org-plugin-uploads.js +116 -0
- package/dist/catalog/owned-items.d.ts +184 -0
- package/dist/catalog/owned-items.js +789 -0
- package/dist/catalog/owned-plugin-cleanup.d.ts +56 -0
- package/dist/catalog/owned-plugin-cleanup.js +194 -0
- package/dist/catalog/ownership.d.ts +172 -0
- package/dist/catalog/ownership.js +326 -0
- package/dist/catalog/platform-components.d.ts +221 -0
- package/dist/catalog/platform-components.js +761 -0
- package/dist/catalog/platform-migration.d.ts +115 -0
- package/dist/catalog/platform-migration.js +582 -0
- package/dist/catalog/plugin-import.d.ts +178 -34
- package/dist/catalog/plugin-import.js +623 -83
- package/dist/catalog/plugin-install.d.ts +185 -41
- package/dist/catalog/plugin-install.js +758 -126
- package/dist/catalog/plugin-upgrade.d.ts +260 -225
- package/dist/catalog/plugin-upgrade.js +1148 -1137
- package/dist/catalog/request-rules.d.ts +65 -0
- package/dist/catalog/request-rules.js +217 -0
- package/dist/catalog/requests.service.d.ts +193 -118
- package/dist/catalog/requests.service.js +526 -343
- package/dist/catalog/reserved-names.d.ts +8 -0
- package/dist/catalog/reserved-names.js +42 -0
- package/dist/catalog/scope-orgs.d.ts +21 -0
- package/dist/catalog/scope-orgs.js +67 -0
- package/dist/catalog/scope-state.d.ts +35 -0
- package/dist/catalog/scope-state.js +87 -0
- package/dist/catalog/scope-sync-workers.d.ts +7 -0
- package/dist/catalog/scope-sync-workers.js +29 -0
- package/dist/catalog/scope-sync.d.ts +176 -0
- package/dist/catalog/scope-sync.js +576 -0
- package/dist/catalog/scope.d.ts +42 -0
- package/dist/catalog/scope.js +42 -0
- package/dist/catalog/unshare.d.ts +22 -0
- package/dist/catalog/unshare.js +118 -0
- package/dist/channels/actions.js +1 -0
- package/dist/channels/cards.js +21 -3
- package/dist/channels/channels.module.js +2 -0
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/inbound.d.ts +9 -1
- package/dist/channels/inbound.js +67 -5
- package/dist/channels/phrases.d.ts +35 -0
- package/dist/channels/phrases.js +29 -0
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/docs.js +3 -1
- package/dist/config/schema.d.ts +24 -2
- package/dist/config/schema.js +145 -19
- package/dist/conversations/access.d.ts +6 -4
- package/dist/conversations/access.js +29 -5
- package/dist/conversations/agent-tree.d.ts +46 -0
- package/dist/conversations/agent-tree.js +92 -0
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +79 -23
- package/dist/conversations/conversations.controller.js +141 -50
- package/dist/conversations/conversations.module.d.ts +1 -1
- package/dist/conversations/conversations.module.js +10 -7
- package/dist/conversations/conversations.service.d.ts +108 -9
- package/dist/conversations/conversations.service.js +237 -30
- package/dist/conversations/environment-status.d.ts +24 -0
- package/dist/conversations/environment-status.js +71 -0
- package/dist/conversations/event-log.d.ts +21 -6
- package/dist/conversations/event-log.js +70 -1
- package/dist/conversations/events.d.ts +140 -0
- package/dist/conversations/events.js +142 -13
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.d.ts +1 -1
- package/dist/conversations/queue.service.js +8 -1
- package/dist/conversations/titles.d.ts +12 -3
- package/dist/conversations/titles.js +36 -8
- package/dist/conversations/tree.d.ts +17 -0
- package/dist/conversations/tree.js +42 -0
- package/dist/ctx/create-ctx.d.ts +5 -0
- package/dist/ctx/create-ctx.js +15 -5
- package/dist/db/kernel-schema.d.ts +420 -20
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/environments/access.d.ts +41 -0
- package/dist/environments/access.js +77 -0
- package/dist/environments/conversation-environments.d.ts +61 -0
- package/dist/environments/conversation-environments.js +170 -0
- package/dist/environments/dto.d.ts +25 -0
- package/dist/environments/dto.js +12 -0
- package/dist/environments/environments.controller.d.ts +33 -0
- package/dist/environments/environments.controller.js +110 -0
- package/dist/environments/environments.module.d.ts +8 -0
- package/dist/environments/environments.module.js +27 -0
- package/dist/environments/environments.service.d.ts +153 -0
- package/dist/environments/environments.service.js +541 -0
- package/dist/environments/legacy-cleanup.job.d.ts +34 -0
- package/dist/environments/legacy-cleanup.job.js +141 -0
- package/dist/errors.d.ts +90 -2
- package/dist/errors.js +90 -2
- package/dist/evals/reconstruct.d.ts +18 -9
- package/dist/evals/reconstruct.js +26 -7
- package/dist/evals/run-replay.d.ts +7 -2
- package/dist/evals/run-replay.js +42 -7
- package/dist/evals/step-replay.d.ts +2 -5
- package/dist/evals/step-replay.js +13 -7
- package/dist/evals/trajectory.js +1 -1
- package/dist/evals/trajectory.service.d.ts +8 -3
- package/dist/evals/trajectory.service.js +26 -14
- package/dist/evals/worker.d.ts +5 -5
- package/dist/evals/worker.js +9 -3
- package/dist/extensions/admin-ops.d.ts +36 -10
- package/dist/extensions/admin-ops.js +67 -42
- package/dist/extensions/admin-view.d.ts +39 -10
- package/dist/extensions/admin-view.js +111 -20
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/bootstrap.d.ts +17 -4
- package/dist/extensions/bootstrap.js +70 -9
- package/dist/extensions/cli.d.ts +12 -0
- package/dist/extensions/cli.js +11 -2
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +78 -8
- package/dist/extensions/deployment-config.js +172 -34
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/effective-config.d.ts +127 -0
- package/dist/extensions/effective-config.js +368 -0
- package/dist/extensions/extensions.module.js +14 -2
- package/dist/extensions/hooks.d.ts +37 -4
- package/dist/extensions/hooks.js +82 -16
- package/dist/extensions/host.d.ts +45 -2
- package/dist/extensions/host.js +112 -16
- package/dist/extensions/installs.d.ts +20 -8
- package/dist/extensions/installs.js +37 -15
- package/dist/extensions/org-config.d.ts +81 -6
- package/dist/extensions/org-config.js +195 -44
- package/dist/extensions/org-levels.d.ts +37 -0
- package/dist/extensions/org-levels.js +71 -0
- package/dist/extensions/org-overrides.d.ts +151 -0
- package/dist/extensions/org-overrides.js +524 -0
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +19 -4
- package/dist/extensions/permission-view.js +4 -4
- package/dist/extensions/plugin-items.d.ts +21 -0
- package/dist/extensions/plugin-items.js +42 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +71 -0
- package/dist/extensions/point-defaults.js +234 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/extensions/resolve.d.ts +5 -1
- package/dist/extensions/resolve.js +15 -2
- package/dist/files/previewers.d.ts +8 -1
- package/dist/files/previewers.js +35 -6
- package/dist/http/api.module.js +31 -3
- package/dist/http/app.d.ts +11 -2
- package/dist/http/app.js +31 -5
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/accounts.d.ts +29 -29
- package/dist/identity/accounts.js +47 -47
- package/dist/identity/audit.d.ts +2 -1
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/auth.guard.d.ts +13 -5
- package/dist/identity/auth.guard.js +26 -9
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/identity.module.d.ts +1 -1
- package/dist/identity/identity.module.js +4 -4
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/principal.d.ts +3 -3
- package/dist/identity/principal.js +3 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/identity/settings.d.ts +22 -11
- package/dist/identity/settings.js +53 -18
- package/dist/identity/{enterprise-admin.d.ts → super-admin.d.ts} +5 -5
- package/dist/identity/{enterprise-admin.js → super-admin.js} +24 -24
- package/dist/inbox/inbox.module.d.ts +4 -1
- package/dist/inbox/inbox.module.js +7 -3
- package/dist/inbox/inbox.service.d.ts +62 -3
- package/dist/inbox/inbox.service.js +74 -4
- package/dist/inbox/org-notices.d.ts +63 -0
- package/dist/inbox/org-notices.js +91 -0
- package/dist/inbox/platform-inbox.controller.d.ts +23 -0
- package/dist/inbox/platform-inbox.controller.js +58 -0
- package/dist/inbox/platform-inbox.service.d.ts +151 -0
- package/dist/inbox/platform-inbox.service.js +263 -0
- package/dist/inbox/platform-notices.d.ts +19 -0
- package/dist/inbox/platform-notices.js +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/infra/cli/kernel-cli.module.js +9 -1
- package/dist/infra/config/kernel-config.service.js +27 -1
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/database/xact-lock.d.ts +1 -1
- package/dist/infra/database/xact-lock.js +1 -1
- package/dist/infra/events/kernel-events.d.ts +19 -2
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/add-job-once.d.ts +14 -0
- package/dist/infra/queue/add-job-once.js +16 -0
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/kernel.module.js +6 -0
- package/dist/lifecycle/kernel-lifecycle.d.ts +1 -1
- package/dist/lifecycle/kernel-lifecycle.js +4 -4
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/prices.d.ts +5 -5
- package/dist/models/prices.js +4 -4
- package/dist/models/purposes.controller.d.ts +6 -4
- package/dist/models/purposes.controller.js +13 -7
- package/dist/models/purposes.d.ts +43 -9
- package/dist/models/purposes.js +48 -18
- package/dist/orgs/directory-sync.d.ts +14 -5
- package/dist/orgs/directory-sync.js +41 -17
- package/dist/orgs/offboarding.d.ts +12 -8
- package/dist/orgs/offboarding.js +54 -33
- package/dist/orgs/org-lock.d.ts +5 -0
- package/dist/orgs/org-lock.js +13 -0
- package/dist/orgs/org.guard.d.ts +10 -0
- package/dist/orgs/org.guard.js +43 -32
- package/dist/orgs/orgs.module.js +3 -0
- package/dist/orgs/orgs.service.d.ts +20 -7
- package/dist/orgs/orgs.service.js +32 -12
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +5 -1
- package/dist/orgs/settings.controller.d.ts +1 -1
- package/dist/orgs/settings.service.d.ts +20 -3
- package/dist/orgs/settings.service.js +19 -4
- package/dist/overrides.d.ts +3 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/actor.d.ts +48 -0
- package/dist/platform/actor.js +100 -0
- package/dist/platform/agent-docs.d.ts +18 -0
- package/dist/platform/agent-docs.js +59 -0
- package/dist/platform/availability.d.ts +14 -0
- package/dist/platform/availability.js +43 -0
- package/dist/platform/errors.d.ts +11 -0
- package/dist/platform/errors.js +26 -0
- package/dist/platform/exclusions.d.ts +12 -0
- package/dist/platform/exclusions.js +1387 -0
- package/dist/platform/method.d.ts +6 -0
- package/dist/platform/method.js +15 -0
- package/dist/platform/operation.d.ts +92 -0
- package/dist/platform/operation.js +35 -0
- package/dist/platform/ops/agent-templates.ops.d.ts +14 -0
- package/dist/platform/ops/agent-templates.ops.js +92 -0
- package/dist/platform/ops/agents.ops.d.ts +19 -0
- package/dist/platform/ops/agents.ops.js +294 -0
- package/dist/platform/ops/connectors.ops.d.ts +15 -0
- package/dist/platform/ops/connectors.ops.js +126 -0
- package/dist/platform/ops/errors.d.ts +11 -0
- package/dist/platform/ops/errors.js +22 -0
- package/dist/platform/ops/extensions.ops.d.ts +19 -0
- package/dist/platform/ops/extensions.ops.js +116 -0
- package/dist/platform/ops/marketplaces.ops.d.ts +16 -0
- package/dist/platform/ops/marketplaces.ops.js +157 -0
- package/dist/platform/ops/paging.d.ts +14 -0
- package/dist/platform/ops/paging.js +20 -0
- package/dist/platform/ops/platform.ops.d.ts +23 -0
- package/dist/platform/ops/platform.ops.js +158 -0
- package/dist/platform/ops/plugins.ops.d.ts +33 -0
- package/dist/platform/ops/plugins.ops.js +377 -0
- package/dist/platform/ops/requests.ops.d.ts +14 -0
- package/dist/platform/ops/requests.ops.js +153 -0
- package/dist/platform/ops/role.d.ts +4 -0
- package/dist/platform/ops/role.js +10 -0
- package/dist/platform/ops/skills.ops.d.ts +13 -0
- package/dist/platform/ops/skills.ops.js +98 -0
- package/dist/platform/ops/summary.d.ts +10 -0
- package/dist/platform/ops/summary.js +18 -0
- package/dist/platform/platform-ops.module.d.ts +7 -0
- package/dist/platform/platform-ops.module.js +55 -0
- package/dist/platform/platform.module.d.ts +8 -0
- package/dist/platform/platform.module.js +25 -0
- package/dist/platform/redact-orgs.d.ts +7 -0
- package/dist/platform/redact-orgs.js +52 -0
- package/dist/platform/registry.d.ts +30 -0
- package/dist/platform/registry.js +111 -0
- package/dist/roles/worker-role.d.ts +6 -4
- package/dist/roles/worker-role.js +14 -7
- package/dist/roles/worker.module.d.ts +1 -1
- package/dist/roles/worker.module.js +15 -1
- package/dist/runtime/context/build.d.ts +11 -7
- package/dist/runtime/context/build.js +46 -19
- package/dist/runtime/context/claude-tools.d.ts +3 -1
- package/dist/runtime/context/claude-tools.js +8 -6
- package/dist/runtime/context/compaction.js +13 -0
- package/dist/runtime/context/reminders.d.ts +2 -0
- package/dist/runtime/context/reminders.js +2 -0
- package/dist/runtime/context/skills.js +9 -2
- package/dist/runtime/context/tool-changes.d.ts +27 -0
- package/dist/runtime/context/tool-changes.js +60 -0
- package/dist/runtime/hook-calls.js +2 -0
- package/dist/runtime/langgraph-loop.d.ts +3 -1
- package/dist/runtime/langgraph-loop.js +36 -17
- package/dist/runtime/loop.d.ts +34 -5
- package/dist/runtime/maintenance.d.ts +9 -4
- package/dist/runtime/maintenance.js +35 -8
- package/dist/runtime/model-call.d.ts +46 -8
- package/dist/runtime/model-call.js +72 -21
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-control.d.ts +11 -1
- package/dist/runtime/run-control.js +12 -0
- package/dist/runtime/run-drafts.d.ts +85 -0
- package/dist/runtime/run-drafts.js +230 -0
- package/dist/runtime/run-jobs.d.ts +3 -5
- package/dist/runtime/run-jobs.js +3 -7
- package/dist/runtime/run-queue.d.ts +59 -2
- package/dist/runtime/run-queue.js +311 -38
- package/dist/runtime/run-worker.d.ts +11 -5
- package/dist/runtime/run-worker.js +69 -33
- package/dist/runtime/runtime.module.js +11 -3
- package/dist/runtime/step-log.d.ts +15 -4
- package/dist/runtime/step-log.js +24 -7
- package/dist/runtime/stream.controller.d.ts +23 -1
- package/dist/runtime/stream.controller.js +60 -3
- package/dist/runtime/stream.d.ts +4 -1
- package/dist/runtime/stream.js +27 -3
- package/dist/runtime/subagents/child.d.ts +55 -7
- package/dist/runtime/subagents/child.js +105 -7
- package/dist/runtime/subagents/definition.d.ts +12 -3
- package/dist/runtime/subagents/definition.js +44 -28
- package/dist/runtime/subagents/deliveries.d.ts +71 -0
- package/dist/runtime/subagents/deliveries.js +228 -0
- package/dist/runtime/subagents/dispatch.d.ts +115 -26
- package/dist/runtime/subagents/dispatch.js +212 -141
- package/dist/runtime/subagents/limits.d.ts +24 -5
- package/dist/runtime/subagents/limits.js +40 -6
- package/dist/runtime/subagents/messages.d.ts +95 -0
- package/dist/runtime/subagents/messages.js +328 -0
- package/dist/runtime/subagents/preamble.d.ts +4 -4
- package/dist/runtime/subagents/preamble.js +9 -7
- package/dist/runtime/subagents/progress.d.ts +8 -6
- package/dist/runtime/subagents/progress.js +11 -6
- package/dist/runtime/subagents/render.d.ts +30 -0
- package/dist/runtime/subagents/render.js +53 -0
- package/dist/runtime/subagents/roster.d.ts +24 -7
- package/dist/runtime/subagents/roster.js +47 -11
- package/dist/runtime/subagents/skill-fork.d.ts +3 -2
- package/dist/runtime/subagents/skill-fork.js +8 -4
- package/dist/runtime/subagents/status.d.ts +14 -0
- package/dist/runtime/subagents/status.js +23 -0
- package/dist/runtime/subagents/subagents.service.d.ts +7 -4
- package/dist/runtime/subagents/subagents.service.js +11 -6
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +19 -13
- package/dist/runtime/waits.d.ts +145 -36
- package/dist/runtime/waits.js +423 -109
- package/dist/sandbox/jobs.d.ts +40 -13
- package/dist/sandbox/jobs.js +57 -20
- package/dist/sandbox/leases.d.ts +37 -54
- package/dist/sandbox/leases.js +71 -115
- package/dist/sandbox/providers.d.ts +2 -2
- package/dist/sandbox/providers.js +1 -1
- package/dist/sandbox/sandbox.module.d.ts +3 -2
- package/dist/sandbox/sandbox.module.js +10 -4
- package/dist/sandbox/sandbox.service.d.ts +282 -129
- package/dist/sandbox/sandbox.service.js +736 -381
- package/dist/skills/catalog.d.ts +3 -2
- package/dist/skills/catalog.js +21 -43
- package/dist/skills/changes.controller.d.ts +7 -0
- package/dist/skills/changes.controller.js +19 -1
- package/dist/skills/invoke.js +8 -3
- package/dist/skills/package.d.ts +20 -1
- package/dist/skills/package.js +28 -1
- package/dist/skills/skills.controller.d.ts +39 -4
- package/dist/skills/skills.controller.js +88 -10
- package/dist/skills/skills.module.js +10 -1
- package/dist/skills/skills.service.d.ts +106 -26
- package/dist/skills/skills.service.js +421 -75
- package/dist/skills/usable.d.ts +81 -0
- package/dist/skills/usable.js +379 -0
- package/dist/tools/approvals.service.d.ts +20 -2
- package/dist/tools/approvals.service.js +25 -4
- package/dist/tools/builtin-names.d.ts +41 -7
- package/dist/tools/builtin-names.js +59 -7
- package/dist/tools/builtins/agent.d.ts +23 -21
- package/dist/tools/builtins/agent.js +62 -55
- package/dist/tools/builtins/builtin-tools.d.ts +19 -1
- package/dist/tools/builtins/builtin-tools.js +59 -2
- package/dist/tools/builtins/handback.d.ts +28 -0
- package/dist/tools/builtins/handback.js +53 -0
- package/dist/tools/builtins/platform.d.ts +34 -0
- package/dist/tools/builtins/platform.js +362 -0
- package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
- package/dist/tools/builtins/sandbox/bash.js +241 -4
- package/dist/tools/builtins/sandbox/common.d.ts +69 -27
- package/dist/tools/builtins/sandbox/common.js +87 -48
- package/dist/tools/builtins/sandbox/deliver.js +1 -1
- package/dist/tools/builtins/sandbox/edit.js +2 -2
- package/dist/tools/builtins/sandbox/glob.js +2 -2
- package/dist/tools/builtins/sandbox/grep.js +2 -4
- package/dist/tools/builtins/sandbox/index.d.ts +3 -1
- package/dist/tools/builtins/sandbox/index.js +7 -1
- package/dist/tools/builtins/sandbox/read.js +1 -1
- package/dist/tools/builtins/sandbox/task-output.d.ts +74 -0
- package/dist/tools/builtins/sandbox/task-output.js +329 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +113 -0
- package/dist/tools/builtins/sandbox/write.js +2 -2
- package/dist/tools/builtins/send-message.d.ts +56 -0
- package/dist/tools/builtins/send-message.js +100 -0
- package/dist/tools/builtins/skill-load.d.ts +22 -14
- package/dist/tools/builtins/skill-load.js +86 -57
- package/dist/tools/builtins/skill-write.js +1 -1
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +271 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +175 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +210 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/connectors.controller.d.ts +10 -0
- package/dist/tools/connectors.controller.js +12 -0
- package/dist/tools/connectors.service.d.ts +46 -5
- package/dist/tools/connectors.service.js +94 -15
- package/dist/tools/extension-tools.controller.d.ts +14 -0
- package/dist/tools/extension-tools.controller.js +37 -0
- package/dist/tools/info.d.ts +42 -1
- package/dist/tools/info.js +64 -1
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/org-tool-switches.d.ts +74 -0
- package/dist/tools/org-tool-switches.js +184 -0
- package/dist/tools/pipeline.d.ts +46 -40
- package/dist/tools/pipeline.js +202 -364
- package/dist/tools/policy.d.ts +1 -1
- package/dist/tools/policy.js +36 -16
- package/dist/tools/registry.d.ts +120 -8
- package/dist/tools/registry.js +112 -19
- package/dist/tools/tools.module.js +24 -2
- package/dist/tools/validate.js +1 -1
- package/dist/ui/views.d.ts +2 -1
- package/dist/ui/views.js +17 -3
- package/package.json +3 -3
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
- package/src/migrations/0074_subagent_tree.sql +50 -0
- package/src/migrations/0075_plugin_scope.sql +61 -0
- package/src/migrations/0076_skills_created_by_nullable.sql +6 -0
- package/src/migrations/0077_scope_requests.sql +52 -0
- package/src/migrations/0078_scope_grant_switches.sql +18 -0
- package/src/migrations/0079_scope_live_install_switches.sql +19 -0
- package/src/migrations/0080_rename_enterprise_admin.sql +18 -0
- package/src/migrations/0081_platform_components.sql +129 -0
- package/src/migrations/0082_platform_migration.sql +33 -0
- package/src/migrations/0083_plugin_scope_sync_failures.sql +15 -0
- package/src/migrations/0084_org_owned_items.sql +85 -0
- package/src/migrations/0085_agent_templates_phase4.sql +45 -0
- package/src/migrations/0086_org_layering.sql +30 -0
- package/src/migrations/0087_approval_summary.sql +6 -0
- package/src/migrations/0088_environments.sql +80 -0
- package/dist/catalog/upgrade-targets.d.ts +0 -56
- package/dist/catalog/upgrade-targets.js +0 -90
- package/dist/conversations/sandbox-status.d.ts +0 -24
- package/dist/conversations/sandbox-status.js +0 -52
- package/dist/extensions/org-config.controller.d.ts +0 -24
- package/dist/extensions/org-config.controller.js +0 -62
|
@@ -0,0 +1,1116 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { getQueueToken } from "@nestjs/bullmq";
|
|
12
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
13
|
+
import { sql } from "kysely";
|
|
14
|
+
import { ApiError } from "../errors.js";
|
|
15
|
+
import { ExtensionAdminOps } from "../extensions/admin-ops.js";
|
|
16
|
+
import { ExtensionAdmin } from "../extensions/admin-view.js";
|
|
17
|
+
import { ExtensionLifecycle } from "../extensions/lifecycle.js";
|
|
18
|
+
import { PluginInstallLock } from "../extensions/locks.js";
|
|
19
|
+
import { audit } from "../identity/audit.js";
|
|
20
|
+
import { SuperAdminLock } from "../identity/super-admin.js";
|
|
21
|
+
import { addOrgNotice, itemDisplayName, notifyItemShared } from "../inbox/org-notices.js";
|
|
22
|
+
import { afterCommit } from "../infra/database/after-commit.js";
|
|
23
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
24
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
25
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
26
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
27
|
+
import { OrgLock } from "../orgs/org-lock.js";
|
|
28
|
+
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
29
|
+
import { CatalogService } from "./catalog.service.js";
|
|
30
|
+
import { assertOrgsExist, markVersionApplied } from "./catalog-items.js";
|
|
31
|
+
import { nameConflictError } from "./component-scope.service.js";
|
|
32
|
+
import { agentsUsing, COMPONENT_KINDS, componentOffered, componentOn, listedIn, loadComponents, loadScopeState, pluginFactsOf, skillNameClashes, switchOf, } from "./component-state.js";
|
|
33
|
+
import { ownedPluginCleanupJob } from "./job-ids.js";
|
|
34
|
+
import { assertNotAwaiting, assertOwnerListed, assertShareable, assertShareableTarget, itemRemovedError, newlySharedWith, ownerOf, ownersOf, PLATFORM_OWNER, packageOwner, pendingDigest, releaseExtensionClaim, } from "./ownership.js";
|
|
35
|
+
import { PluginImport } from "./plugin-import.js";
|
|
36
|
+
import { PluginInstaller } from "./plugin-install.js";
|
|
37
|
+
import { PlatformPluginUpdater } from "./plugin-upgrade.js";
|
|
38
|
+
import { cancelPendingRequests } from "./request-rules.js";
|
|
39
|
+
import { wantedOn } from "./scope.js";
|
|
40
|
+
import { addScopeOrg } from "./scope-orgs.js";
|
|
41
|
+
import { PluginScopeSync, reconcileWithin } from "./scope-sync.js";
|
|
42
|
+
import { narrowComponentOnUnshare, narrowItemOnUnshare } from "./unshare.js";
|
|
43
|
+
/** How long the super admin's confirmation waits for this replica to load the extension. */
|
|
44
|
+
const CONFIRM_RECONCILE_WAIT_MS = 5_000;
|
|
45
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
46
|
+
const LIVE = ["installing", "installed", "upgrading"];
|
|
47
|
+
const notFound = () => new ApiError("catalog.item_not_found", "Catalog item not found");
|
|
48
|
+
const notPending = (name) => new ApiError("extension.not_pending", `Plugin ${name} has no upload waiting for confirmation`);
|
|
49
|
+
const pendingStale = (name) => new ApiError("extension.approval_stale", `The pending upload of ${name} changed since it was reviewed; review it again`);
|
|
50
|
+
const builtinError = () => new ApiError("plugin.builtin", "This plugin is built in: it is on in every organization and its scope cannot be changed or disabled");
|
|
51
|
+
const staleError = () => new ApiError("plugin.scope_stale", "What this change affects has changed since the preview; preview it again and confirm the new hash");
|
|
52
|
+
/** The hash of a preview: sha256 over its sorted JSON, the target included. */
|
|
53
|
+
function scopeHash(target, p) {
|
|
54
|
+
return createHash("sha256")
|
|
55
|
+
.update(JSON.stringify({
|
|
56
|
+
target: { ...target, orgs: [...target.orgs].sort() },
|
|
57
|
+
affected: [...p.affected]
|
|
58
|
+
.sort((a, b) => a.orgId.localeCompare(b.orgId))
|
|
59
|
+
.map((a) => ({
|
|
60
|
+
...a,
|
|
61
|
+
agents: [...a.agents].sort((x, y) => x.id.localeCompare(y.id)),
|
|
62
|
+
})),
|
|
63
|
+
needsConfig: [...p.needsConfig].sort(),
|
|
64
|
+
replaced: [...p.replaced].sort((a, b) => a.id.localeCompare(b.id)),
|
|
65
|
+
conflicts: [...p.conflicts].sort((a, b) => a.orgId.localeCompare(b.orgId)),
|
|
66
|
+
}))
|
|
67
|
+
.digest("hex");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The super admin's plugin scope and deployment-level disable (plugin and component management
|
|
71
|
+
* spec §4.1, §5 rules 2-4 and 8), and the import confirm that enqueues the scope sync of the item it
|
|
72
|
+
* wrote. A change is previewed, then confirmed with the preview's `scopeHash`, recomputed under the
|
|
73
|
+
* item row lock (a difference -> 409 `plugin.scope_stale`); it bumps `scope_version`, audits
|
|
74
|
+
* `catalog.scope_changed` and, after the commit, enqueues the item's scope sync, which installs or
|
|
75
|
+
* uninstalls in every org. Disabling keeps the scope, its org list and the org switches, so
|
|
76
|
+
* re-enabling (no hash needed) reinstalls where it was on. Builtin items -> 409 `plugin.builtin`.
|
|
77
|
+
* Stateless, shared by every replica.
|
|
78
|
+
*/
|
|
79
|
+
let CatalogScopeService = class CatalogScopeService {
|
|
80
|
+
kdb;
|
|
81
|
+
adminLock;
|
|
82
|
+
catalog;
|
|
83
|
+
pluginImport;
|
|
84
|
+
installer;
|
|
85
|
+
extensions;
|
|
86
|
+
ops;
|
|
87
|
+
sync;
|
|
88
|
+
updater;
|
|
89
|
+
orgLock;
|
|
90
|
+
installLock;
|
|
91
|
+
lifecycle;
|
|
92
|
+
maintenance;
|
|
93
|
+
log;
|
|
94
|
+
txHost;
|
|
95
|
+
constructor(kdb, adminLock, catalog, pluginImport, installer, extensions, ops, sync, updater,
|
|
96
|
+
/** The owner org's lock: an org admin withdrawing its pending upload (ruling P3-10). */
|
|
97
|
+
orgLock,
|
|
98
|
+
/** The per-name install lock a reject / withdraw releases the name claim under (M1). */
|
|
99
|
+
installLock,
|
|
100
|
+
/** This replica's reconcile once a confirmed extension is enabled (before the scope sync). */
|
|
101
|
+
lifecycle,
|
|
102
|
+
/** The removed org plugin's cleanup job, backing the take-out of P3-21 (pre-flight F9). */
|
|
103
|
+
maintenance, log, txHost) {
|
|
104
|
+
this.kdb = kdb;
|
|
105
|
+
this.adminLock = adminLock;
|
|
106
|
+
this.catalog = catalog;
|
|
107
|
+
this.pluginImport = pluginImport;
|
|
108
|
+
this.installer = installer;
|
|
109
|
+
this.extensions = extensions;
|
|
110
|
+
this.ops = ops;
|
|
111
|
+
this.sync = sync;
|
|
112
|
+
this.updater = updater;
|
|
113
|
+
this.orgLock = orgLock;
|
|
114
|
+
this.installLock = installLock;
|
|
115
|
+
this.lifecycle = lifecycle;
|
|
116
|
+
this.maintenance = maintenance;
|
|
117
|
+
this.log = log;
|
|
118
|
+
this.txHost = txHost;
|
|
119
|
+
}
|
|
120
|
+
get db() {
|
|
121
|
+
return this.kdb.db;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Confirms a staged import ({@link PluginImport.confirm}: installs and enables the extension,
|
|
125
|
+
* writes the new item's scope), then enqueues the item's scope sync when its scope turns it on
|
|
126
|
+
* by itself (`all` or `named`; an `optional` item is on only where an org admin switches it on).
|
|
127
|
+
*/
|
|
128
|
+
async confirmImport(actor, stagingId, input, opts = {}) {
|
|
129
|
+
const r = await this.pluginImport.confirm(actor, stagingId, input, opts);
|
|
130
|
+
if (r.item.scope !== "optional")
|
|
131
|
+
await this.enqueue(r.item.id);
|
|
132
|
+
return r;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Confirms the permissions of an item's extension (the admin's 待确认): approves the current
|
|
136
|
+
* permission set with snapshot source `web` (`permissionsHash` is the item view's, the extension
|
|
137
|
+
* review digest; a changed set -> 409 `extension.approval_stale`); an extension that was never
|
|
138
|
+
* deployed (no install record) is installed and enabled first (the package is in the package
|
|
139
|
+
* store from the import). Nothing waiting (a covered snapshot a person approved already, an item
|
|
140
|
+
* without an extension) -> 409 `extension.not_pending`. Enqueues the item's scope sync after.
|
|
141
|
+
*
|
|
142
|
+
* An org-owned item is confirmed only while an upload of it waits (ruling P3-10, see
|
|
143
|
+
* {@link confirmOrgUpload}); otherwise -> 409 `extension.not_pending`.
|
|
144
|
+
*/
|
|
145
|
+
async confirmPermissions(actor, itemId, permissionsHash, opts = {}) {
|
|
146
|
+
if (!UUID_RE.test(itemId))
|
|
147
|
+
throw notFound();
|
|
148
|
+
const item = await this.db
|
|
149
|
+
.selectFrom("catalog_items")
|
|
150
|
+
.select([
|
|
151
|
+
"id",
|
|
152
|
+
"name",
|
|
153
|
+
"latest_version",
|
|
154
|
+
"builtin",
|
|
155
|
+
"owner_org_id",
|
|
156
|
+
"awaiting_confirmation",
|
|
157
|
+
"pending_version",
|
|
158
|
+
])
|
|
159
|
+
.where("id", "=", itemId)
|
|
160
|
+
.where("deleted_at", "is", null)
|
|
161
|
+
.executeTakeFirst();
|
|
162
|
+
if (!item)
|
|
163
|
+
throw notFound();
|
|
164
|
+
if (item.owner_org_id !== null) {
|
|
165
|
+
return this.confirmOrgUpload(actor, { id: item.id, name: item.name }, permissionsHash, opts);
|
|
166
|
+
}
|
|
167
|
+
const version = await this.db
|
|
168
|
+
.selectFrom("catalog_item_versions")
|
|
169
|
+
.select("extension_sha256")
|
|
170
|
+
.where("item_id", "=", itemId)
|
|
171
|
+
.where("version", "=", item.latest_version)
|
|
172
|
+
.executeTakeFirst();
|
|
173
|
+
const sha256 = version?.extension_sha256 ?? null;
|
|
174
|
+
if (sha256 === null) {
|
|
175
|
+
throw new ApiError("extension.not_pending", `Plugin ${item.name} has no extension to confirm`);
|
|
176
|
+
}
|
|
177
|
+
const stale = () => new ApiError("extension.approval_stale", `The permissions of ${item.name} changed since they were reviewed; review them again`);
|
|
178
|
+
const installed = await this.extensions
|
|
179
|
+
.get(item.name, { allowUnreadable: true })
|
|
180
|
+
.then(() => true)
|
|
181
|
+
.catch((e) => {
|
|
182
|
+
if (e instanceof ApiError && e.code === "extension.not_found")
|
|
183
|
+
return false;
|
|
184
|
+
throw e;
|
|
185
|
+
});
|
|
186
|
+
if (!installed) {
|
|
187
|
+
if ((await this.extensions.packageReviewHash(sha256)) !== permissionsHash)
|
|
188
|
+
throw stale();
|
|
189
|
+
await this.ops.install(item.name, sha256, actor, opts.actorTokenId, {
|
|
190
|
+
catalogItemId: itemId,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const current = await this.extensions.get(item.name);
|
|
194
|
+
// A disabled install is what an earlier confirm that stopped before the enable left behind
|
|
195
|
+
const disabled = current.desired?.state === "disabled";
|
|
196
|
+
try {
|
|
197
|
+
await this.extensions.approve(actor, item.name, { sha256: current.sha256, reviewHash: permissionsHash }, { ...opts, confirmBootstrap: item.builtin });
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
// Already approved but not enabled yet: carry on to the enable; else nothing to confirm
|
|
201
|
+
if (!disabled || !(e instanceof ApiError && e.code === "extension.not_pending"))
|
|
202
|
+
throw e;
|
|
203
|
+
}
|
|
204
|
+
if (disabled)
|
|
205
|
+
await this.ops.enable(item.name, actor, opts.actorTokenId);
|
|
206
|
+
await this.enqueue(itemId);
|
|
207
|
+
return (await this.catalog.get(itemId)).item;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The super admin's confirmation of an org upload's pending version (rulings P3-10, P3-11,
|
|
211
|
+
* P3-21): `permissionsHash` must be the digest of that version's package ({@link pendingDigest};
|
|
212
|
+
* another -> 409 `extension.approval_stale`, also for a version a later upload replaced); no
|
|
213
|
+
* pending version (withdrawn, rejected, replaced by one without an extension) -> 409
|
|
214
|
+
* `extension.not_pending`.
|
|
215
|
+
*
|
|
216
|
+
* A first version: its extension installed, its permissions approved, enabled, then (one
|
|
217
|
+
* transaction) the item stops waiting and the owner is told (`upload_confirmed`); after the
|
|
218
|
+
* commit its platform components are written and the scope sync enqueued. Each step re-reads
|
|
219
|
+
* the item under its row lock: removed meanwhile -> 409 `catalog.item_removed`, the pending
|
|
220
|
+
* version replaced -> `extension.approval_stale`; the extension this confirmation installed is
|
|
221
|
+
* then taken out again. A new version: the platform update to it
|
|
222
|
+
* ({@link PlatformPluginUpdater.updateToPending}, which installs or upgrades the extension,
|
|
223
|
+
* clears the pending state, tells the owner and the orgs using a shared plugin), then its
|
|
224
|
+
* permissions approved.
|
|
225
|
+
*/
|
|
226
|
+
async confirmOrgUpload(actor, item, permissionsHash, opts) {
|
|
227
|
+
// Every step under the item's update lock (ruling P3-35): a second confirmation waits its
|
|
228
|
+
// turn out as 409 plugin.install_in_progress and never undoes the first one's install
|
|
229
|
+
const first = await this.updater.withItemLock(item.id, () => this.confirmLocked(actor, item.id, item.name, permissionsHash, opts));
|
|
230
|
+
if (first !== null) {
|
|
231
|
+
// Loaded here before the scope sync plans its install, waited for at most a few seconds
|
|
232
|
+
// (the enable does not wait for the reconcile); a sync on a replica that has not loaded it
|
|
233
|
+
// yet defers and retries (ruling P3-38)
|
|
234
|
+
await reconcileWithin(this.lifecycle, `confirmed:${item.name}`, CONFIRM_RECONCILE_WAIT_MS, this.log);
|
|
235
|
+
await this.pluginImport.writePlatformComponents(item.id, first);
|
|
236
|
+
await this.enqueue(item.id);
|
|
237
|
+
}
|
|
238
|
+
return (await this.catalog.get(item.id)).item;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* {@link confirmOrgUpload} under the item lock; returns the confirmed first version (its
|
|
242
|
+
* platform components and scope sync follow after), null for a new version.
|
|
243
|
+
*/
|
|
244
|
+
async confirmLocked(actor, itemId, name, permissionsHash, opts) {
|
|
245
|
+
// Read again under the lock (the caller's read was outside it)
|
|
246
|
+
const item = await this.db
|
|
247
|
+
.selectFrom("catalog_items")
|
|
248
|
+
.select([
|
|
249
|
+
"deleted_at",
|
|
250
|
+
"owner_org_id",
|
|
251
|
+
"awaiting_confirmation",
|
|
252
|
+
"pending_version",
|
|
253
|
+
"pending_by",
|
|
254
|
+
"pending_at",
|
|
255
|
+
"package_name",
|
|
256
|
+
"platform_synced_version",
|
|
257
|
+
])
|
|
258
|
+
.where("id", "=", itemId)
|
|
259
|
+
.executeTakeFirst();
|
|
260
|
+
if (!item || item.deleted_at !== null || item.owner_org_id === null) {
|
|
261
|
+
throw itemRemovedError(name);
|
|
262
|
+
}
|
|
263
|
+
const version = item.pending_version;
|
|
264
|
+
if (version === null)
|
|
265
|
+
throw notPending(name);
|
|
266
|
+
const row = await this.db
|
|
267
|
+
.selectFrom("catalog_item_versions")
|
|
268
|
+
.select("extension_sha256")
|
|
269
|
+
.where("item_id", "=", itemId)
|
|
270
|
+
.where("version", "=", version)
|
|
271
|
+
.executeTakeFirst();
|
|
272
|
+
const sha = row?.extension_sha256 ?? null;
|
|
273
|
+
if (sha === null)
|
|
274
|
+
throw notPending(name);
|
|
275
|
+
const reviewHash = await this.extensions.packageReviewHash(sha);
|
|
276
|
+
if (reviewHash === null || pendingDigest(sha, reviewHash) !== permissionsHash) {
|
|
277
|
+
throw pendingStale(name);
|
|
278
|
+
}
|
|
279
|
+
// An install of this name that is not this item's (left by an earlier item, of another
|
|
280
|
+
// package or of the same package name under another owner): the org's code never inherits its
|
|
281
|
+
// deployment config, secrets or data (rulings P3-36, P3-51). This item's own install, or one
|
|
282
|
+
// its owner's removed item of the same package left, is upgraded in place.
|
|
283
|
+
const install = await this.db
|
|
284
|
+
.selectFrom("extension_installs as x")
|
|
285
|
+
.innerJoin("extension_packages as p", "p.sha256", "x.sha256")
|
|
286
|
+
.leftJoin("catalog_items as ci", "ci.id", "x.catalog_item_id")
|
|
287
|
+
.select(["x.sha256", "x.catalog_item_id", "p.package_name", "ci.owner_org_id"])
|
|
288
|
+
.where("x.name", "=", name)
|
|
289
|
+
.executeTakeFirst();
|
|
290
|
+
const own = install === undefined ||
|
|
291
|
+
(install.package_name === item.package_name &&
|
|
292
|
+
(install.catalog_item_id === itemId ||
|
|
293
|
+
(install.catalog_item_id !== null && install.owner_org_id === item.owner_org_id)));
|
|
294
|
+
if (install !== undefined && !own) {
|
|
295
|
+
// who holds it: the install's own item's owner, else the package's (none: the platform)
|
|
296
|
+
const holderOrg = install.catalog_item_id === null ? null : install.owner_org_id;
|
|
297
|
+
const holder = install.catalog_item_id !== null
|
|
298
|
+
? ownerOf(holderOrg, await ownersOf(this.db, holderOrg === null ? [] : [holderOrg]))
|
|
299
|
+
: install.package_name !== item.package_name
|
|
300
|
+
? await packageOwner(this.db, install.package_name, name)
|
|
301
|
+
: PLATFORM_OWNER;
|
|
302
|
+
throw new ApiError("extension.name_conflict", install.package_name !== item.package_name
|
|
303
|
+
? `Extension ${name} is installed from package ${install.package_name}; the upload's package ${item.package_name ?? "(none)"} cannot take it over`
|
|
304
|
+
: `Extension ${name} is installed for another plugin of package ${install.package_name}; this upload cannot take it over`, [{ owner: holder }]);
|
|
305
|
+
}
|
|
306
|
+
if (!item.awaiting_confirmation) {
|
|
307
|
+
const pending = {
|
|
308
|
+
version,
|
|
309
|
+
by: item.pending_by,
|
|
310
|
+
at: item.pending_at,
|
|
311
|
+
};
|
|
312
|
+
// A new version: applied (unless an earlier confirmation applied it and stopped before the
|
|
313
|
+
// approval), then its permissions approved; an approval that fails puts the pending state
|
|
314
|
+
// back, so the version shows in 待确认 again (ruling P3-37)
|
|
315
|
+
if (item.platform_synced_version !== version) {
|
|
316
|
+
try {
|
|
317
|
+
await this.updater.updateToPendingLocked(actor, itemId, version, opts);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
if (e instanceof ApiError && e.code === "catalog.item_not_found") {
|
|
321
|
+
throw itemRemovedError(name);
|
|
322
|
+
}
|
|
323
|
+
throw e;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
await this.clearPending(itemId, version);
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
await this.approvePackage(actor, name, sha, reviewHash, opts);
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
await this.restorePending(itemId, pending);
|
|
334
|
+
throw e;
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
await this.stillPending(itemId, name, version);
|
|
339
|
+
let installedHere = false;
|
|
340
|
+
try {
|
|
341
|
+
if (install?.sha256 !== sha) {
|
|
342
|
+
await this.ops.install(name, sha, actor, opts.actorTokenId, { catalogItemId: itemId });
|
|
343
|
+
installedHere = install === undefined;
|
|
344
|
+
}
|
|
345
|
+
await this.stillPending(itemId, name, version);
|
|
346
|
+
await this.approvePackage(actor, name, sha, reviewHash, opts);
|
|
347
|
+
await this.stillPending(itemId, name, version);
|
|
348
|
+
await this.ops.enable(name, actor, opts.actorTokenId);
|
|
349
|
+
await this.finishFirstVersion(actor, itemId, version, opts);
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
// Taken out only when this confirmation did not win: the item gone or still waiting
|
|
353
|
+
if (installedHere && !(await this.confirmed(itemId))) {
|
|
354
|
+
await this.takeOut(itemId, name, actor, opts);
|
|
355
|
+
}
|
|
356
|
+
throw e;
|
|
357
|
+
}
|
|
358
|
+
return version;
|
|
359
|
+
}
|
|
360
|
+
/** The item is live and its first version confirmed (no longer awaiting). */
|
|
361
|
+
async confirmed(itemId) {
|
|
362
|
+
const row = await this.db
|
|
363
|
+
.selectFrom("catalog_items")
|
|
364
|
+
.select(["deleted_at", "awaiting_confirmation"])
|
|
365
|
+
.where("id", "=", itemId)
|
|
366
|
+
.executeTakeFirst();
|
|
367
|
+
return row !== undefined && row.deleted_at === null && !row.awaiting_confirmation;
|
|
368
|
+
}
|
|
369
|
+
/** Clears the pending state of a version an earlier confirmation applied already. */
|
|
370
|
+
async clearPending(itemId, version) {
|
|
371
|
+
await this.db
|
|
372
|
+
.updateTable("catalog_items")
|
|
373
|
+
.set({ pending_version: null, pending_by: null, pending_at: null })
|
|
374
|
+
.where("id", "=", itemId)
|
|
375
|
+
.where("pending_version", "=", version)
|
|
376
|
+
.execute();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Puts a confirmed new version's pending state back after its approval failed (ruling P3-37):
|
|
380
|
+
* the version applied, its permissions still waiting, 待确认 shows it again. Not when another
|
|
381
|
+
* upload is pending meanwhile.
|
|
382
|
+
*/
|
|
383
|
+
async restorePending(itemId, p) {
|
|
384
|
+
await this.db
|
|
385
|
+
.updateTable("catalog_items")
|
|
386
|
+
.set({ pending_version: p.version, pending_by: p.by, pending_at: p.at })
|
|
387
|
+
.where("id", "=", itemId)
|
|
388
|
+
.where("deleted_at", "is", null)
|
|
389
|
+
.where("pending_version", "is", null)
|
|
390
|
+
.execute();
|
|
391
|
+
}
|
|
392
|
+
/** Approves the permissions of the installed package `sha` (already covered: nothing to do). */
|
|
393
|
+
async approvePackage(actor, name, sha, reviewHash, opts) {
|
|
394
|
+
try {
|
|
395
|
+
await this.extensions.approve(actor, name, { sha256: sha, reviewHash }, opts);
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
if (!(e instanceof ApiError && e.code === "extension.not_pending"))
|
|
399
|
+
throw e;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* An extension a confirmation installed whose item was removed or replaced meanwhile is taken
|
|
404
|
+
* out again (disabled, uninstalled; P3-21), and the name claim released when the item is gone
|
|
405
|
+
* (ruling P3-37). Inline and best effort (a failure is logged); a removed item's cleanup job
|
|
406
|
+
* (`owned-plugin-cleanup-<itemId>`, idempotent) is enqueued as well, so a failed take-out is
|
|
407
|
+
* retried (pre-flight F9).
|
|
408
|
+
*/
|
|
409
|
+
async takeOut(itemId, name, actor, opts) {
|
|
410
|
+
try {
|
|
411
|
+
await this.ops.disable(name, actor, opts.actorTokenId);
|
|
412
|
+
await this.ops.uninstall(name, actor, opts.actorTokenId);
|
|
413
|
+
await this.releaseIfGone(itemId, name);
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
this.log.error("taking out the extension of a removed org upload failed", { name, err });
|
|
417
|
+
}
|
|
418
|
+
const gone = await this.db
|
|
419
|
+
.selectFrom("catalog_items")
|
|
420
|
+
.select("id")
|
|
421
|
+
.where("id", "=", itemId)
|
|
422
|
+
.where("deleted_at", "is not", null)
|
|
423
|
+
.executeTakeFirst();
|
|
424
|
+
if (gone === undefined)
|
|
425
|
+
return;
|
|
426
|
+
const job = ownedPluginCleanupJob(itemId);
|
|
427
|
+
try {
|
|
428
|
+
await this.maintenance.add(job.name, job.data, job.opts);
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
this.log.warn("enqueueing the owned plugin cleanup failed", { itemId, err });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/** Releases the name claim of a removed item (nothing installed under it any more). */
|
|
435
|
+
async releaseIfGone(itemId, name) {
|
|
436
|
+
const row = await this.db
|
|
437
|
+
.selectFrom("catalog_items")
|
|
438
|
+
.select(["deleted_at", "package_name"])
|
|
439
|
+
.where("id", "=", itemId)
|
|
440
|
+
.forUpdate()
|
|
441
|
+
.executeTakeFirst();
|
|
442
|
+
if (row === undefined || row.deleted_at !== null) {
|
|
443
|
+
await releaseExtensionClaim(this.db, name, row?.package_name ?? null);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/** The item is still live and pending with `version` (row lock), else 409 (P3-21). */
|
|
447
|
+
async stillPending(itemId, name, version) {
|
|
448
|
+
await this.pendingRow(itemId, name, version);
|
|
449
|
+
}
|
|
450
|
+
/** {@link stillPending} in the caller's transaction; returns the owner org. */
|
|
451
|
+
async pendingRow(itemId, name, version) {
|
|
452
|
+
const row = await this.db
|
|
453
|
+
.selectFrom("catalog_items")
|
|
454
|
+
.select(["deleted_at", "pending_version", "owner_org_id"])
|
|
455
|
+
.where("id", "=", itemId)
|
|
456
|
+
.forUpdate()
|
|
457
|
+
.executeTakeFirst();
|
|
458
|
+
if (!row || row.deleted_at !== null || row.owner_org_id === null) {
|
|
459
|
+
throw itemRemovedError(name);
|
|
460
|
+
}
|
|
461
|
+
if (row.pending_version !== version)
|
|
462
|
+
throw pendingStale(name);
|
|
463
|
+
return row.owner_org_id;
|
|
464
|
+
}
|
|
465
|
+
/** A confirmed first version: the item stops waiting, the owner is told (`upload_confirmed`). */
|
|
466
|
+
async finishFirstVersion(actor, itemId, version, opts) {
|
|
467
|
+
await this.adminLock.lockAndAuthorize(actor);
|
|
468
|
+
const named = await this.db
|
|
469
|
+
.selectFrom("catalog_items")
|
|
470
|
+
.select("name")
|
|
471
|
+
.where("id", "=", itemId)
|
|
472
|
+
.executeTakeFirstOrThrow();
|
|
473
|
+
const owner = await this.pendingRow(itemId, named.name, version);
|
|
474
|
+
await this.db
|
|
475
|
+
.updateTable("catalog_items")
|
|
476
|
+
.set({
|
|
477
|
+
awaiting_confirmation: false,
|
|
478
|
+
pending_version: null,
|
|
479
|
+
pending_by: null,
|
|
480
|
+
pending_at: null,
|
|
481
|
+
// the confirmed version is the item's latest (ruling P3-34)
|
|
482
|
+
latest_version: version,
|
|
483
|
+
updated_at: sql `now()`,
|
|
484
|
+
})
|
|
485
|
+
.where("id", "=", itemId)
|
|
486
|
+
.execute();
|
|
487
|
+
await markVersionApplied(this.db, itemId, version);
|
|
488
|
+
await addOrgNotice(this.db, owner, "upload_confirmed", {
|
|
489
|
+
itemId,
|
|
490
|
+
name: named.name,
|
|
491
|
+
displayName: await itemDisplayName(this.db, itemId),
|
|
492
|
+
version,
|
|
493
|
+
});
|
|
494
|
+
await audit(this.db, {
|
|
495
|
+
actorAccountId: actor,
|
|
496
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
497
|
+
action: "catalog.upload_confirmed",
|
|
498
|
+
target: itemId,
|
|
499
|
+
detail: { name: named.name, version, ownerOrgId: owner },
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* The super admin rejects an org upload's pending version (ruling P3-10, `reason` shown to the
|
|
504
|
+
* owner in `upload_rejected`): a first version is removed (soft delete, its requests
|
|
505
|
+
* cancelled), a new version's pending state cleared (the version stays imported, never applied);
|
|
506
|
+
* the extension name claim is released when nothing is installed under it (pre-flight F10).
|
|
507
|
+
* Nothing pending, or a platform-owned item (pre-flight F11) -> 409 `extension.not_pending`.
|
|
508
|
+
*/
|
|
509
|
+
async reject(actor, itemId, reason, opts = {}) {
|
|
510
|
+
if (!UUID_RE.test(itemId))
|
|
511
|
+
throw notFound();
|
|
512
|
+
await this.dropPending(actor, itemId, { kind: "reject", reason }, opts);
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* The owner org withdraws its pending upload (ruling P3-10): as {@link reject}, without a
|
|
516
|
+
* notice. Another org's or a platform item -> 404; nothing pending -> 409
|
|
517
|
+
* `extension.not_pending`.
|
|
518
|
+
*/
|
|
519
|
+
async withdraw(orgId, actor, itemId, opts = {}) {
|
|
520
|
+
if (!UUID_RE.test(itemId))
|
|
521
|
+
throw notFound();
|
|
522
|
+
await this.dropPending(actor, itemId, { kind: "withdraw", orgId }, opts);
|
|
523
|
+
}
|
|
524
|
+
async dropPending(actor, itemId, how, opts) {
|
|
525
|
+
if (how.kind === "reject")
|
|
526
|
+
await this.adminLock.lockAndAuthorize(actor);
|
|
527
|
+
else
|
|
528
|
+
await this.orgLock.lockAndAuthorize(how.orgId, actor, "catalog.upload");
|
|
529
|
+
const t = this.db;
|
|
530
|
+
const item = await t
|
|
531
|
+
.selectFrom("catalog_items")
|
|
532
|
+
.select([
|
|
533
|
+
"id",
|
|
534
|
+
"name",
|
|
535
|
+
"owner_org_id",
|
|
536
|
+
"awaiting_confirmation",
|
|
537
|
+
"pending_version",
|
|
538
|
+
"package_name",
|
|
539
|
+
])
|
|
540
|
+
.where("id", "=", itemId)
|
|
541
|
+
.where("deleted_at", "is", null)
|
|
542
|
+
.forUpdate()
|
|
543
|
+
.executeTakeFirst();
|
|
544
|
+
if (!item)
|
|
545
|
+
throw notFound();
|
|
546
|
+
if (how.kind === "withdraw" && item.owner_org_id !== how.orgId)
|
|
547
|
+
throw notFound();
|
|
548
|
+
if (item.owner_org_id === null || item.pending_version === null)
|
|
549
|
+
throw notPending(item.name);
|
|
550
|
+
const now = sql `now()`;
|
|
551
|
+
await t
|
|
552
|
+
.updateTable("catalog_items")
|
|
553
|
+
.set({
|
|
554
|
+
pending_version: null,
|
|
555
|
+
pending_by: null,
|
|
556
|
+
pending_at: null,
|
|
557
|
+
updated_at: now,
|
|
558
|
+
// A first version is removed: the name is free again (P3-10)
|
|
559
|
+
...(item.awaiting_confirmation && { deleted_at: now }),
|
|
560
|
+
})
|
|
561
|
+
.where("id", "=", itemId)
|
|
562
|
+
.execute();
|
|
563
|
+
if (item.awaiting_confirmation) {
|
|
564
|
+
await cancelPendingRequests(t, { itemId }, actor, "item_removed", opts);
|
|
565
|
+
}
|
|
566
|
+
if (how.kind === "reject") {
|
|
567
|
+
// never usable afterwards, whoever owns the item later (ruling P3-52)
|
|
568
|
+
await t
|
|
569
|
+
.updateTable("catalog_item_versions")
|
|
570
|
+
.set({ rejected_at: now })
|
|
571
|
+
.where("item_id", "=", itemId)
|
|
572
|
+
.where("version", "=", item.pending_version)
|
|
573
|
+
.execute();
|
|
574
|
+
}
|
|
575
|
+
// Under the per-name install lock, as CatalogService.removeIn: a confirm's install still
|
|
576
|
+
// committing is seen, so its claim is never released under it
|
|
577
|
+
await this.installLock.lock(item.name);
|
|
578
|
+
await releaseExtensionClaim(t, item.name, item.package_name);
|
|
579
|
+
if (how.kind === "reject") {
|
|
580
|
+
await addOrgNotice(t, item.owner_org_id, "upload_rejected", {
|
|
581
|
+
itemId,
|
|
582
|
+
name: item.name,
|
|
583
|
+
displayName: await itemDisplayName(t, itemId),
|
|
584
|
+
version: item.pending_version,
|
|
585
|
+
reason: how.reason,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
await audit(t, {
|
|
589
|
+
actorAccountId: actor,
|
|
590
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
591
|
+
action: how.kind === "reject" ? "catalog.upload_rejected" : "catalog.upload_withdrawn",
|
|
592
|
+
target: itemId,
|
|
593
|
+
detail: {
|
|
594
|
+
name: item.name,
|
|
595
|
+
version: item.pending_version,
|
|
596
|
+
ownerOrgId: item.owner_org_id,
|
|
597
|
+
removed: item.awaiting_confirmation,
|
|
598
|
+
...(how.kind === "reject" && { reason: how.reason }),
|
|
599
|
+
},
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
/** The preview of a scope change or a disable (nothing written). */
|
|
603
|
+
async preview(itemId, target) {
|
|
604
|
+
const item = await this.item(itemId, false);
|
|
605
|
+
return this.compute(item, await this.targetOf(item, target));
|
|
606
|
+
}
|
|
607
|
+
/** Changes the scope after its preview (`scopeHash`); returns the item. */
|
|
608
|
+
async setScope(actor, itemId, body, opts = {}) {
|
|
609
|
+
if (!UUID_RE.test(itemId))
|
|
610
|
+
throw notFound();
|
|
611
|
+
const target = { scope: body.scope, ...(body.orgs !== undefined && { orgs: body.orgs }) };
|
|
612
|
+
await this.writeScope(actor, itemId, target, body.scopeHash, opts);
|
|
613
|
+
return (await this.catalog.get(itemId)).item;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Disables the plugin on the whole deployment (after its preview, `scopeHash` required) or
|
|
617
|
+
* enables it again (no hash); returns the item.
|
|
618
|
+
*/
|
|
619
|
+
async setDisabled(actor, itemId, body, opts = {}) {
|
|
620
|
+
if (!UUID_RE.test(itemId))
|
|
621
|
+
throw notFound();
|
|
622
|
+
if (body.disabled && body.scopeHash === undefined) {
|
|
623
|
+
throw new ApiError("validation_failed", "scopeHash: required when disabling (from POST scope/preview with disabled: true)", [{ path: "scopeHash", message: "required when disabling" }]);
|
|
624
|
+
}
|
|
625
|
+
await this.writeDisabled(actor, itemId, body.disabled, body.scopeHash, opts);
|
|
626
|
+
return (await this.catalog.get(itemId)).item;
|
|
627
|
+
}
|
|
628
|
+
/** Shows or hides the item to orgs outside its `named` list; returns the item. */
|
|
629
|
+
async setShown(actor, itemId, shown, opts = {}) {
|
|
630
|
+
if (!UUID_RE.test(itemId))
|
|
631
|
+
throw notFound();
|
|
632
|
+
await this.writeShown(actor, itemId, shown, opts);
|
|
633
|
+
return (await this.catalog.get(itemId)).item;
|
|
634
|
+
}
|
|
635
|
+
async writeShown(actor, itemId, shown, opts) {
|
|
636
|
+
await this.adminLock.lockAndAuthorize(actor);
|
|
637
|
+
// A builtin item is `all`: the flag is irrelevant there but harmless, so it is not refused
|
|
638
|
+
const item = await this.db
|
|
639
|
+
.selectFrom("catalog_items")
|
|
640
|
+
.select(["name", "shown_to_others", "owner_org_id", "shareable", "awaiting_confirmation"])
|
|
641
|
+
.where("id", "=", itemId)
|
|
642
|
+
.where("deleted_at", "is", null)
|
|
643
|
+
.forUpdate()
|
|
644
|
+
.executeTakeFirst();
|
|
645
|
+
if (!item)
|
|
646
|
+
throw notFound();
|
|
647
|
+
// A first version waiting for the super admin is nobody's to show yet (P3-9)
|
|
648
|
+
assertNotAwaiting(item);
|
|
649
|
+
// Showing goes through the share gate (P3-16); hiding is always allowed
|
|
650
|
+
if (shown)
|
|
651
|
+
assertShareable(item);
|
|
652
|
+
if (item.shown_to_others === shown)
|
|
653
|
+
return;
|
|
654
|
+
await this.db
|
|
655
|
+
.updateTable("catalog_items")
|
|
656
|
+
.set({ shown_to_others: shown, updated_at: sql `now()` })
|
|
657
|
+
.where("id", "=", itemId)
|
|
658
|
+
.execute();
|
|
659
|
+
await audit(this.db, {
|
|
660
|
+
actorAccountId: actor,
|
|
661
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
662
|
+
action: "catalog.shown_changed",
|
|
663
|
+
target: itemId,
|
|
664
|
+
detail: { name: item.name, from: item.shown_to_others, to: shown },
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
async writeScope(actor, itemId, target, hash, opts) {
|
|
668
|
+
await this.adminLock.lockAndAuthorize(actor);
|
|
669
|
+
const item = await this.item(itemId, true);
|
|
670
|
+
assertNotAwaiting(item);
|
|
671
|
+
const t = await this.targetOf(item, target);
|
|
672
|
+
const from = await this.listed(itemId);
|
|
673
|
+
// The owner stays listed (P3-17); widening goes through the share gate (P3-16)
|
|
674
|
+
assertOwnerListed(item, t);
|
|
675
|
+
assertShareableTarget(item, { scope: item.scope, orgs: from }, t);
|
|
676
|
+
const p = await this.compute(item, t);
|
|
677
|
+
if (p.scopeHash !== hash)
|
|
678
|
+
throw staleError();
|
|
679
|
+
if (p.conflicts.length > 0)
|
|
680
|
+
throw nameConflictError(p.conflicts);
|
|
681
|
+
if (item.scope === t.scope && from.join() === t.orgs.join() && p.replaced.length === 0)
|
|
682
|
+
return;
|
|
683
|
+
await this.followAgain(actor, itemId, p.replaced, opts);
|
|
684
|
+
await this.db
|
|
685
|
+
.updateTable("catalog_items")
|
|
686
|
+
.set({
|
|
687
|
+
scope: t.scope,
|
|
688
|
+
scope_version: sql `scope_version + 1`,
|
|
689
|
+
updated_at: sql `now()`,
|
|
690
|
+
})
|
|
691
|
+
.where("id", "=", itemId)
|
|
692
|
+
.execute();
|
|
693
|
+
await this.db.deleteFrom("catalog_item_scope_orgs").where("item_id", "=", itemId).execute();
|
|
694
|
+
if (t.orgs.length > 0) {
|
|
695
|
+
await this.db
|
|
696
|
+
.insertInto("catalog_item_scope_orgs")
|
|
697
|
+
.values(t.orgs.map((org_id) => ({ item_id: itemId, org_id })))
|
|
698
|
+
.execute();
|
|
699
|
+
}
|
|
700
|
+
const shared = newlySharedWith({ scope: item.scope, orgs: from }, t);
|
|
701
|
+
if (shared === "all_orgs" || shared.length > 0) {
|
|
702
|
+
await notifyItemShared(this.db, item.owner_org_id, { kind: "plugin", id: itemId }, item.name, shared);
|
|
703
|
+
}
|
|
704
|
+
await audit(this.db, {
|
|
705
|
+
actorAccountId: actor,
|
|
706
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
707
|
+
action: "catalog.scope_changed",
|
|
708
|
+
target: itemId,
|
|
709
|
+
detail: {
|
|
710
|
+
name: item.name,
|
|
711
|
+
from: { scope: item.scope, orgs: from },
|
|
712
|
+
to: { scope: t.scope, orgs: t.orgs },
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
afterCommit(() => this.enqueue(itemId));
|
|
716
|
+
}
|
|
717
|
+
async writeDisabled(actor, itemId, disabled, hash, opts) {
|
|
718
|
+
await this.adminLock.lockAndAuthorize(actor);
|
|
719
|
+
const item = await this.item(itemId, true);
|
|
720
|
+
if (item.disabled === disabled)
|
|
721
|
+
return;
|
|
722
|
+
const p = await this.compute(item, disabled
|
|
723
|
+
? await this.targetOf(item, { disabled: true })
|
|
724
|
+
: { scope: item.scope, orgs: await this.listed(itemId), disabled, scopeChange: false });
|
|
725
|
+
if (disabled && p.scopeHash !== hash)
|
|
726
|
+
throw staleError();
|
|
727
|
+
if (p.conflicts.length > 0)
|
|
728
|
+
throw nameConflictError(p.conflicts);
|
|
729
|
+
await this.db
|
|
730
|
+
.updateTable("catalog_items")
|
|
731
|
+
.set({
|
|
732
|
+
disabled,
|
|
733
|
+
scope_version: sql `scope_version + 1`,
|
|
734
|
+
updated_at: sql `now()`,
|
|
735
|
+
})
|
|
736
|
+
.where("id", "=", itemId)
|
|
737
|
+
.execute();
|
|
738
|
+
await audit(this.db, {
|
|
739
|
+
actorAccountId: actor,
|
|
740
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
741
|
+
action: "catalog.scope_changed",
|
|
742
|
+
target: itemId,
|
|
743
|
+
detail: { name: item.name, disabled: { from: item.disabled, to: disabled } },
|
|
744
|
+
});
|
|
745
|
+
afterCommit(() => this.enqueue(itemId));
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Writes an org-owned plugin's 可共享 flag and copies it to its component rows (ruling P3-2).
|
|
749
|
+
* Turning it off narrows an `optional` / `all` plugin, and each component with its own such
|
|
750
|
+
* scope, to the owner plus the orgs where it is on now (ruling P3-25), and enqueues the item's
|
|
751
|
+
* scope sync after the commit. The caller authorizes (the owner org's admin, Task 7). A platform
|
|
752
|
+
* item (no owner) or a missing one -> 404.
|
|
753
|
+
*/
|
|
754
|
+
async setShareable(itemId, shareable, actor = null) {
|
|
755
|
+
if (!UUID_RE.test(itemId))
|
|
756
|
+
throw notFound();
|
|
757
|
+
const item = await this.db
|
|
758
|
+
.selectFrom("catalog_items")
|
|
759
|
+
.select(["id", "name", "scope", "builtin", "owner_org_id", "shareable"])
|
|
760
|
+
.where("id", "=", itemId)
|
|
761
|
+
.where("deleted_at", "is", null)
|
|
762
|
+
.forUpdate()
|
|
763
|
+
.executeTakeFirst();
|
|
764
|
+
if (!item || item.owner_org_id === null)
|
|
765
|
+
throw notFound();
|
|
766
|
+
await this.db
|
|
767
|
+
.updateTable("catalog_items")
|
|
768
|
+
.set({ shareable })
|
|
769
|
+
.where("id", "=", itemId)
|
|
770
|
+
.execute();
|
|
771
|
+
for (const table of [
|
|
772
|
+
"platform_skills",
|
|
773
|
+
"platform_agent_templates",
|
|
774
|
+
"platform_connector_templates",
|
|
775
|
+
]) {
|
|
776
|
+
await this.db
|
|
777
|
+
.updateTable(table)
|
|
778
|
+
.set({ shareable })
|
|
779
|
+
.where("catalog_item_id", "=", itemId)
|
|
780
|
+
.where("deleted_at", "is", null)
|
|
781
|
+
.execute();
|
|
782
|
+
}
|
|
783
|
+
if (shareable)
|
|
784
|
+
return;
|
|
785
|
+
const narrowed = await narrowItemOnUnshare(this.db, actor, item);
|
|
786
|
+
for (const kind of COMPONENT_KINDS) {
|
|
787
|
+
for (const c of await loadComponents(this.db, kind, { itemId, forUpdate: true })) {
|
|
788
|
+
await narrowComponentOnUnshare(this.db, actor, c);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (narrowed)
|
|
792
|
+
afterCommit(() => this.enqueue(itemId));
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Adds one org to a `named` item's list in the caller's transaction (an approved `access`
|
|
796
|
+
* request, spec §5.1 rule 2): nobody loses access, so no preview. The item row is locked; it
|
|
797
|
+
* must be live and not builtin (404 / `plugin.builtin`). Bumps `scope_version`, audits
|
|
798
|
+
* `catalog.scope_changed` and enqueues the item's scope sync after the commit. Returns whether
|
|
799
|
+
* the list changed (false: not `named`, or listed already).
|
|
800
|
+
*/
|
|
801
|
+
async addOrg(actor, itemId, orgId, opts = {}) {
|
|
802
|
+
const item = await this.item(itemId, true);
|
|
803
|
+
assertNotAwaiting(item);
|
|
804
|
+
const changed = await addScopeOrg(this.db, actor, item, orgId, opts);
|
|
805
|
+
if (changed)
|
|
806
|
+
afterCommit(() => this.enqueue(itemId));
|
|
807
|
+
return changed;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Enqueues the item's scope sync after a commit made elsewhere (an org upload's confirm,
|
|
811
|
+
* `OrgPluginUploads`); a failure only warns.
|
|
812
|
+
*/
|
|
813
|
+
async enqueueSync(itemId) {
|
|
814
|
+
await this.enqueue(itemId);
|
|
815
|
+
}
|
|
816
|
+
/** Enqueues the item's scope sync; a failure only warns (the next change or org sync converges). */
|
|
817
|
+
async enqueue(itemId) {
|
|
818
|
+
try {
|
|
819
|
+
await this.sync.enqueueItem(itemId);
|
|
820
|
+
}
|
|
821
|
+
catch (err) {
|
|
822
|
+
this.log.warn("enqueueing the plugin scope sync failed", { itemId, err });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/** The live item (row locked with `forUpdate`); missing -> 404, builtin -> `plugin.builtin`. */
|
|
826
|
+
async item(itemId, forUpdate) {
|
|
827
|
+
if (!UUID_RE.test(itemId))
|
|
828
|
+
throw notFound();
|
|
829
|
+
let q = this.db
|
|
830
|
+
.selectFrom("catalog_items")
|
|
831
|
+
.select([
|
|
832
|
+
"id",
|
|
833
|
+
"name",
|
|
834
|
+
"scope",
|
|
835
|
+
"builtin",
|
|
836
|
+
"disabled",
|
|
837
|
+
"latest_version",
|
|
838
|
+
"owner_org_id",
|
|
839
|
+
"shareable",
|
|
840
|
+
"awaiting_confirmation",
|
|
841
|
+
])
|
|
842
|
+
.where("id", "=", itemId)
|
|
843
|
+
.where("deleted_at", "is", null);
|
|
844
|
+
if (forUpdate)
|
|
845
|
+
q = q.forUpdate();
|
|
846
|
+
const row = await q.executeTakeFirst();
|
|
847
|
+
if (!row)
|
|
848
|
+
throw notFound();
|
|
849
|
+
if (row.builtin)
|
|
850
|
+
throw builtinError();
|
|
851
|
+
return row;
|
|
852
|
+
}
|
|
853
|
+
/** The `named` list of the item, sorted. */
|
|
854
|
+
async listed(itemId) {
|
|
855
|
+
const rows = await this.db
|
|
856
|
+
.selectFrom("catalog_item_scope_orgs")
|
|
857
|
+
.select("org_id")
|
|
858
|
+
.where("item_id", "=", itemId)
|
|
859
|
+
.orderBy("org_id")
|
|
860
|
+
.execute();
|
|
861
|
+
return rows.map((r) => r.org_id);
|
|
862
|
+
}
|
|
863
|
+
/** The state after the change: `orgs` only with `named`, each a live org. */
|
|
864
|
+
async targetOf(item, target) {
|
|
865
|
+
if ("disabled" in target) {
|
|
866
|
+
return {
|
|
867
|
+
scope: item.scope,
|
|
868
|
+
orgs: await this.listed(item.id),
|
|
869
|
+
disabled: true,
|
|
870
|
+
scopeChange: false,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
if (target.orgs !== undefined && target.scope !== "named") {
|
|
874
|
+
throw new ApiError("validation_failed", "orgs: only allowed with scope named", [
|
|
875
|
+
{ path: "orgs", message: "only allowed with scope named" },
|
|
876
|
+
]);
|
|
877
|
+
}
|
|
878
|
+
const orgs = [...new Set(target.orgs ?? [])].sort();
|
|
879
|
+
await assertOrgsExist(this.db, orgs);
|
|
880
|
+
return { scope: target.scope, orgs, disabled: item.disabled, scopeChange: true };
|
|
881
|
+
}
|
|
882
|
+
/** The preview of moving `item` to `target` (read through the current context). */
|
|
883
|
+
async compute(item, target) {
|
|
884
|
+
const db = this.db;
|
|
885
|
+
// one after the other: `db` may be a transaction's single connection
|
|
886
|
+
const orgs = await db
|
|
887
|
+
.selectFrom("orgs")
|
|
888
|
+
.select(["id", "name"])
|
|
889
|
+
.where("deleted_at", "is", null)
|
|
890
|
+
.orderBy("id")
|
|
891
|
+
.execute();
|
|
892
|
+
const listedNow = new Set(await this.listed(item.id));
|
|
893
|
+
const switches = await db
|
|
894
|
+
.selectFrom("catalog_item_org_switches")
|
|
895
|
+
.select(["org_id", "enabled"])
|
|
896
|
+
.where("item_id", "=", item.id)
|
|
897
|
+
.execute();
|
|
898
|
+
const installs = await db
|
|
899
|
+
.selectFrom("plugin_installs")
|
|
900
|
+
.select(["id", "org_id"])
|
|
901
|
+
.where("item_id", "=", item.id)
|
|
902
|
+
.where("status", "in", [...LIVE])
|
|
903
|
+
.execute();
|
|
904
|
+
const sw = new Map(switches.map((s) => [s.org_id, s.enabled]));
|
|
905
|
+
const live = new Map(installs.map((a) => [a.org_id, a.id]));
|
|
906
|
+
const listedAfter = new Set(target.orgs);
|
|
907
|
+
const after = { scope: target.scope, disabled: target.disabled, builtin: false };
|
|
908
|
+
const affected = [];
|
|
909
|
+
const needsConfig = [];
|
|
910
|
+
/** The orgs the plugin will be on in after the change (a live install kept or made). */
|
|
911
|
+
const onAfter = new Set();
|
|
912
|
+
for (const org of orgs) {
|
|
913
|
+
const s = sw.get(org.id) ?? null;
|
|
914
|
+
const wanted = wantedOn(after, listedAfter.has(org.id), s);
|
|
915
|
+
const installId = live.get(org.id);
|
|
916
|
+
if (installId !== undefined) {
|
|
917
|
+
// A live install another one depends on stays (spec §5 rule 9, as the sync does)
|
|
918
|
+
if (wanted || (await this.sync.dependedOn(org.id, installId))) {
|
|
919
|
+
onAfter.add(org.id);
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
const p = await this.installer.previewUninstall(org.id, installId);
|
|
923
|
+
affected.push({
|
|
924
|
+
orgId: org.id,
|
|
925
|
+
orgName: org.name,
|
|
926
|
+
agents: p.agents.map((a) => ({ id: a.id, name: a.name })),
|
|
927
|
+
// The sync's uninstall keeps the connectors the install created as the org's own
|
|
928
|
+
connectorsKept: p.components.filter((c) => c.kind === "connector" && c.created).length,
|
|
929
|
+
});
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (!wanted || wantedOn(item, listedNow.has(org.id), s))
|
|
933
|
+
continue;
|
|
934
|
+
let needs = false;
|
|
935
|
+
try {
|
|
936
|
+
needs = await this.sync.needsInput(org.id, item.id, item.latest_version);
|
|
937
|
+
}
|
|
938
|
+
catch (e) {
|
|
939
|
+
// Not installable as it is (a dependency unresolved): the sync reports it. Anything else
|
|
940
|
+
// (a database error) propagates: inside the write's transaction it is not recoverable
|
|
941
|
+
if (!(e instanceof ApiError))
|
|
942
|
+
throw e;
|
|
943
|
+
}
|
|
944
|
+
if (needs)
|
|
945
|
+
needsConfig.push(org.id);
|
|
946
|
+
else
|
|
947
|
+
onAfter.add(org.id);
|
|
948
|
+
}
|
|
949
|
+
const { replaced, conflicts } = await this.componentChanges(item, target, orgs, onAfter, affected);
|
|
950
|
+
const p = { affected, needsConfig, replaced, conflicts };
|
|
951
|
+
return { ...p, scopeHash: scopeHash(target, p) };
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* The component side of a plugin scope change or disable (spec §5 rules 2, 8, 10): the
|
|
955
|
+
* components with their own scope a scope change replaces; the orgs where a skill or agent
|
|
956
|
+
* template is on now and would be off after (agents merged into `affected`: a component with its
|
|
957
|
+
* own scope that goes back to following the plugin, or one cut by the disable); and the orgs a
|
|
958
|
+
* skill would newly be offered to whose own skills use its name. `onAfter`: the orgs the plugin
|
|
959
|
+
* is on in after the change.
|
|
960
|
+
*/
|
|
961
|
+
async componentChanges(item, target, orgs, onAfter, affected) {
|
|
962
|
+
const db = this.db;
|
|
963
|
+
const replaced = [];
|
|
964
|
+
const conflicts = [];
|
|
965
|
+
const lose = new Map();
|
|
966
|
+
for (const kind of COMPONENT_KINDS) {
|
|
967
|
+
const comps = await loadComponents(db, kind, { itemId: item.id });
|
|
968
|
+
if (comps.length === 0)
|
|
969
|
+
continue;
|
|
970
|
+
if (target.scopeChange) {
|
|
971
|
+
for (const c of comps) {
|
|
972
|
+
if (c.scopeMode === "own")
|
|
973
|
+
replaced.push({ kind, id: c.id, name: c.name });
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (kind === "connector_template")
|
|
977
|
+
continue;
|
|
978
|
+
const st = await loadScopeState(db, kind, comps);
|
|
979
|
+
for (const c of comps) {
|
|
980
|
+
const cAfter = target.scopeChange ? { ...c, scopeMode: "follow" } : c;
|
|
981
|
+
for (const org of orgs) {
|
|
982
|
+
const pNow = pluginFactsOf(st, c.itemId, org.id);
|
|
983
|
+
const pAfter = pNow && {
|
|
984
|
+
...pNow,
|
|
985
|
+
scope: target.scope,
|
|
986
|
+
disabled: target.disabled,
|
|
987
|
+
listed: target.orgs.includes(org.id),
|
|
988
|
+
installed: onAfter.has(org.id),
|
|
989
|
+
};
|
|
990
|
+
const listed = listedIn(st, c.id, org.id);
|
|
991
|
+
const s = switchOf(st, c.id, org.id);
|
|
992
|
+
if (componentOn(c, pNow, listed, s) && !componentOn(cAfter, pAfter, listed, s)) {
|
|
993
|
+
for (const a of await agentsUsing(db, org.id, kind, [c.id])) {
|
|
994
|
+
const m = lose.get(org.id) ?? new Map();
|
|
995
|
+
m.set(a.id, a.name);
|
|
996
|
+
lose.set(org.id, m);
|
|
997
|
+
}
|
|
998
|
+
if (!lose.has(org.id))
|
|
999
|
+
lose.set(org.id, new Map());
|
|
1000
|
+
}
|
|
1001
|
+
if (kind === "skill" &&
|
|
1002
|
+
componentOffered(cAfter, pAfter, listed) &&
|
|
1003
|
+
!componentOffered(c, pNow, listed)) {
|
|
1004
|
+
const clash = await skillNameClashes(db, org.id, c, c.name);
|
|
1005
|
+
if (clash.skills.length > 0) {
|
|
1006
|
+
conflicts.push({ orgId: org.id, orgName: org.name, name: c.name, ...clash });
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
for (const org of orgs) {
|
|
1013
|
+
const m = lose.get(org.id);
|
|
1014
|
+
if (!m)
|
|
1015
|
+
continue;
|
|
1016
|
+
let e = affected.find((a) => a.orgId === org.id);
|
|
1017
|
+
if (!e) {
|
|
1018
|
+
e = { orgId: org.id, orgName: org.name, agents: [], connectorsKept: 0 };
|
|
1019
|
+
affected.push(e);
|
|
1020
|
+
}
|
|
1021
|
+
for (const [id, name] of m) {
|
|
1022
|
+
if (!e.agents.some((a) => a.id === id))
|
|
1023
|
+
e.agents.push({ id, name });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return { replaced, conflicts };
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Sends the components of the item with their own scope back to following it (a plugin scope
|
|
1030
|
+
* change, spec §5 rule 2): `scope_mode = follow`, their own lists dropped, `scope_version`
|
|
1031
|
+
* bumped, one `component.scope_changed` audit each. In the caller's transaction.
|
|
1032
|
+
*/
|
|
1033
|
+
async followAgain(actor, itemId, replaced, opts) {
|
|
1034
|
+
for (const r of replaced) {
|
|
1035
|
+
const table = r.kind === "skill"
|
|
1036
|
+
? "platform_skills"
|
|
1037
|
+
: r.kind === "agent_template"
|
|
1038
|
+
? "platform_agent_templates"
|
|
1039
|
+
: "platform_connector_templates";
|
|
1040
|
+
await this.db
|
|
1041
|
+
.updateTable(table)
|
|
1042
|
+
.set({
|
|
1043
|
+
scope_mode: "follow",
|
|
1044
|
+
scope_version: sql `scope_version + 1`,
|
|
1045
|
+
updated_at: sql `now()`,
|
|
1046
|
+
})
|
|
1047
|
+
.where("id", "=", r.id)
|
|
1048
|
+
.where("scope_mode", "=", "own")
|
|
1049
|
+
.execute();
|
|
1050
|
+
await this.db
|
|
1051
|
+
.deleteFrom("component_scope_orgs")
|
|
1052
|
+
.where("kind", "=", r.kind)
|
|
1053
|
+
.where("component_id", "=", r.id)
|
|
1054
|
+
.execute();
|
|
1055
|
+
await audit(this.db, {
|
|
1056
|
+
actorAccountId: actor,
|
|
1057
|
+
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
1058
|
+
action: "component.scope_changed",
|
|
1059
|
+
target: r.id,
|
|
1060
|
+
detail: { kind: r.kind, name: r.name, itemId, to: { scopeMode: "follow" }, via: "plugin" },
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
__decorate([
|
|
1066
|
+
Transactional()
|
|
1067
|
+
], CatalogScopeService.prototype, "clearPending", null);
|
|
1068
|
+
__decorate([
|
|
1069
|
+
Transactional()
|
|
1070
|
+
], CatalogScopeService.prototype, "restorePending", null);
|
|
1071
|
+
__decorate([
|
|
1072
|
+
Transactional()
|
|
1073
|
+
], CatalogScopeService.prototype, "releaseIfGone", null);
|
|
1074
|
+
__decorate([
|
|
1075
|
+
Transactional()
|
|
1076
|
+
], CatalogScopeService.prototype, "stillPending", null);
|
|
1077
|
+
__decorate([
|
|
1078
|
+
Transactional()
|
|
1079
|
+
], CatalogScopeService.prototype, "finishFirstVersion", null);
|
|
1080
|
+
__decorate([
|
|
1081
|
+
Transactional()
|
|
1082
|
+
], CatalogScopeService.prototype, "dropPending", null);
|
|
1083
|
+
__decorate([
|
|
1084
|
+
Transactional()
|
|
1085
|
+
], CatalogScopeService.prototype, "writeShown", null);
|
|
1086
|
+
__decorate([
|
|
1087
|
+
Transactional()
|
|
1088
|
+
], CatalogScopeService.prototype, "writeScope", null);
|
|
1089
|
+
__decorate([
|
|
1090
|
+
Transactional()
|
|
1091
|
+
], CatalogScopeService.prototype, "writeDisabled", null);
|
|
1092
|
+
__decorate([
|
|
1093
|
+
Transactional()
|
|
1094
|
+
], CatalogScopeService.prototype, "setShareable", null);
|
|
1095
|
+
__decorate([
|
|
1096
|
+
Transactional({ propagation: "mandatory" })
|
|
1097
|
+
], CatalogScopeService.prototype, "addOrg", null);
|
|
1098
|
+
CatalogScopeService = __decorate([
|
|
1099
|
+
Injectable(),
|
|
1100
|
+
__param(0, Inject(KernelDb)),
|
|
1101
|
+
__param(1, Inject(SuperAdminLock)),
|
|
1102
|
+
__param(2, Inject(CatalogService)),
|
|
1103
|
+
__param(3, Inject(PluginImport)),
|
|
1104
|
+
__param(4, Inject(PluginInstaller)),
|
|
1105
|
+
__param(5, Inject(ExtensionAdmin)),
|
|
1106
|
+
__param(6, Inject(ExtensionAdminOps)),
|
|
1107
|
+
__param(7, Inject(PluginScopeSync)),
|
|
1108
|
+
__param(8, Inject(PlatformPluginUpdater)),
|
|
1109
|
+
__param(9, Inject(OrgLock)),
|
|
1110
|
+
__param(10, Inject(PluginInstallLock)),
|
|
1111
|
+
__param(11, Inject(ExtensionLifecycle)),
|
|
1112
|
+
__param(12, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME))),
|
|
1113
|
+
__param(13, Inject(KERNEL_LOGGER)),
|
|
1114
|
+
__param(14, Inject(TransactionHost))
|
|
1115
|
+
], CatalogScopeService);
|
|
1116
|
+
export { CatalogScopeService };
|