@capybara-hq/kernel 0.1.0-rc.4 → 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 +39 -13
- package/dist/admin/admin.controller.js +33 -17
- package/dist/admin/extensions.controller.d.ts +16 -2
- package/dist/admin/extensions.controller.js +26 -6
- 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 +16 -0
- package/dist/agents/agent-lookup.js +11 -0
- package/dist/agents/agents.controller.d.ts +76 -1
- package/dist/agents/agents.controller.js +90 -11
- package/dist/agents/agents.module.d.ts +4 -1
- package/dist/agents/agents.module.js +32 -6
- package/dist/agents/agents.service.d.ts +51 -22
- package/dist/agents/agents.service.js +172 -70
- package/dist/agents/definition.d.ts +49 -0
- package/dist/agents/definition.js +45 -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/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/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 +475 -52
- package/dist/background/background.module.js +8 -1
- package/dist/background/finish.d.ts +3 -2
- package/dist/background/launch-lock.d.ts +4 -3
- package/dist/background/launch-lock.js +4 -3
- package/dist/background/poll.job.d.ts +9 -6
- package/dist/background/poll.job.js +30 -22
- package/dist/background/session.d.ts +13 -2
- package/dist/background/session.js +13 -0
- package/dist/background/stop.d.ts +12 -6
- package/dist/background/stop.js +57 -39
- package/dist/background/store.d.ts +18 -6
- package/dist/background/store.js +12 -6
- package/dist/background/tasks.service.d.ts +15 -5
- package/dist/background/tasks.service.js +42 -17
- package/dist/background/wake-rules.d.ts +27 -18
- package/dist/background/wake-rules.js +52 -22
- package/dist/background/wake.d.ts +13 -11
- package/dist/background/wake.js +20 -14
- 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/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 +622 -82
- 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 +1147 -1136
- 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 +20 -2
- package/dist/channels/channels.module.js +2 -0
- 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/config/docs.js +3 -1
- package/dist/config/schema.d.ts +6 -2
- package/dist/config/schema.js +46 -22
- package/dist/conversations/access.d.ts +6 -4
- package/dist/conversations/access.js +27 -5
- package/dist/conversations/agent-tree.d.ts +46 -0
- package/dist/conversations/agent-tree.js +92 -0
- package/dist/conversations/conversations.controller.d.ts +43 -18
- package/dist/conversations/conversations.controller.js +79 -38
- package/dist/conversations/conversations.module.d.ts +1 -1
- package/dist/conversations/conversations.module.js +7 -7
- package/dist/conversations/conversations.service.d.ts +69 -12
- package/dist/conversations/conversations.service.js +106 -27
- package/dist/conversations/environment-status.d.ts +24 -0
- package/dist/conversations/environment-status.js +71 -0
- package/dist/conversations/event-log.d.ts +20 -5
- package/dist/conversations/event-log.js +60 -0
- package/dist/conversations/events.d.ts +84 -2
- package/dist/conversations/events.js +91 -17
- package/dist/conversations/queue.service.d.ts +1 -1
- package/dist/conversations/queue.service.js +5 -0
- package/dist/conversations/titles.d.ts +5 -3
- package/dist/conversations/titles.js +21 -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 +362 -18
- 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 +82 -2
- package/dist/errors.js +82 -2
- package/dist/evals/reconstruct.d.ts +18 -9
- package/dist/evals/reconstruct.js +26 -7
- package/dist/evals/run-replay.d.ts +4 -2
- package/dist/evals/run-replay.js +16 -4
- package/dist/evals/step-replay.d.ts +2 -5
- package/dist/evals/step-replay.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 +65 -42
- package/dist/extensions/admin-view.d.ts +37 -10
- package/dist/extensions/admin-view.js +103 -20
- 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/deployment-config.d.ts +28 -5
- package/dist/extensions/deployment-config.js +48 -7
- package/dist/extensions/effective-config.d.ts +127 -0
- package/dist/extensions/effective-config.js +368 -0
- package/dist/extensions/extensions.module.js +8 -2
- package/dist/extensions/hooks.d.ts +37 -4
- package/dist/extensions/hooks.js +82 -16
- package/dist/extensions/host.d.ts +19 -1
- package/dist/extensions/host.js +30 -7
- package/dist/extensions/installs.d.ts +20 -8
- package/dist/extensions/installs.js +37 -15
- package/dist/extensions/org-config.d.ts +75 -6
- package/dist/extensions/org-config.js +184 -32
- 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/permission-view.d.ts +16 -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.d.ts +12 -5
- package/dist/extensions/point-defaults.js +37 -9
- 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 +23 -3
- package/dist/http/app.d.ts +11 -2
- package/dist/http/app.js +31 -5
- 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.guard.d.ts +13 -5
- package/dist/identity/auth.guard.js +26 -9
- package/dist/identity/identity.module.d.ts +1 -1
- package/dist/identity/identity.module.js +4 -4
- package/dist/identity/me.controller.js +1 -1
- package/dist/identity/principal.d.ts +3 -3
- package/dist/identity/principal.js +3 -3
- 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 +1 -1
- package/dist/index.js +1 -1
- package/dist/infra/cli/kernel-cli.module.js +9 -1
- package/dist/infra/config/kernel-config.service.js +21 -1
- 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 +11 -2
- package/dist/infra/queue/add-job-once.d.ts +14 -0
- package/dist/infra/queue/add-job-once.js +16 -0
- 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/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 +20 -9
- package/dist/models/purposes.js +17 -8
- package/dist/orgs/directory-sync.d.ts +14 -5
- package/dist/orgs/directory-sync.js +41 -17
- package/dist/orgs/offboarding.d.ts +9 -7
- package/dist/orgs/offboarding.js +28 -17
- 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 +15 -3
- package/dist/orgs/settings.service.js +6 -2
- 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 +9 -1
- package/dist/runtime/context/build.d.ts +6 -4
- package/dist/runtime/context/build.js +26 -7
- package/dist/runtime/context/claude-tools.d.ts +2 -1
- package/dist/runtime/context/claude-tools.js +5 -4
- package/dist/runtime/context/compaction.js +9 -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 +35 -17
- package/dist/runtime/loop.d.ts +32 -4
- package/dist/runtime/maintenance.d.ts +7 -4
- package/dist/runtime/maintenance.js +23 -8
- package/dist/runtime/model-call.d.ts +30 -8
- package/dist/runtime/model-call.js +69 -19
- 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 +37 -3
- package/dist/runtime/run-queue.js +188 -11
- package/dist/runtime/run-worker.d.ts +11 -5
- package/dist/runtime/run-worker.js +54 -31
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/step-log.d.ts +9 -4
- package/dist/runtime/step-log.js +8 -5
- 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 +43 -29
- 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.js +17 -12
- package/dist/runtime/waits.d.ts +140 -37
- package/dist/runtime/waits.js +394 -106
- 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 +4 -3
- package/dist/sandbox/sandbox.service.d.ts +229 -134
- package/dist/sandbox/sandbox.service.js +630 -409
- 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 +23 -4
- package/dist/tools/builtin-names.d.ts +21 -5
- package/dist/tools/builtin-names.js +30 -5
- 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 +6 -2
- package/dist/tools/builtins/builtin-tools.js +37 -12
- 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.js +13 -3
- package/dist/tools/builtins/sandbox/common.d.ts +43 -24
- package/dist/tools/builtins/sandbox/common.js +80 -45
- 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 +1 -1
- package/dist/tools/builtins/sandbox/index.js +1 -1
- package/dist/tools/builtins/sandbox/read.js +1 -1
- package/dist/tools/builtins/sandbox/task-output.d.ts +14 -3
- package/dist/tools/builtins/sandbox/task-output.js +30 -5
- package/dist/tools/builtins/sandbox/task-stop.js +2 -1
- 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/extract.js +5 -1
- package/dist/tools/builtins/web/fetch.js +1 -0
- package/dist/tools/builtins/web/search.js +1 -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/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 +187 -364
- package/dist/tools/policy.d.ts +1 -1
- package/dist/tools/policy.js +36 -16
- package/dist/tools/registry.d.ts +98 -5
- package/dist/tools/registry.js +87 -9
- package/dist/tools/tools.module.js +12 -2
- package/dist/ui/views.d.ts +2 -1
- package/dist/ui/views.js +17 -3
- package/package.json +3 -3
- 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 -32
- package/dist/extensions/org-config.controller.js +0 -70
|
@@ -8,14 +8,16 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
-
import { PLUGIN_MANIFEST_FILE, parseSkillMd, SDK_VERSION, SKILL_ENTRY_FILE, } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { PLUGIN_MANIFEST_FILE, parseSkillMd, pluginDescription, SDK_VERSION, SKILL_ENTRY_FILE, } from "@capybara-hq/plugin-sdk";
|
|
12
|
+
import { getQueueToken } from "@nestjs/bullmq";
|
|
12
13
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
14
|
import { sql } from "kysely";
|
|
14
15
|
import semver from "semver";
|
|
15
16
|
import { ApiError } from "../errors.js";
|
|
16
17
|
import { ExtensionAdminOps } from "../extensions/admin-ops.js";
|
|
18
|
+
import { ExtensionAdmin } from "../extensions/admin-view.js";
|
|
17
19
|
import { writeSnapshot } from "../extensions/approvals.js";
|
|
18
|
-
import { ExtensionBlobs } from "../extensions/blobs.js";
|
|
20
|
+
import { blobKey, ExtensionBlobs } from "../extensions/blobs.js";
|
|
19
21
|
import { ExtensionInstalls } from "../extensions/installs.js";
|
|
20
22
|
import { PluginApproveLock, PluginInstallLock } from "../extensions/locks.js";
|
|
21
23
|
import { unrunMigrations } from "../extensions/migrate-lock.js";
|
|
@@ -24,28 +26,40 @@ import { diffPermissions, permissionKeys, WARNINGS_KEY_PREFIX, } from "../extens
|
|
|
24
26
|
import { reviewHash as extensionReviewHash, migrationWarnings, permissionGroups, permissionKeyItems, } from "../extensions/permission-view.js";
|
|
25
27
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
26
28
|
import { audit } from "../identity/audit.js";
|
|
27
|
-
import {
|
|
29
|
+
import { assertCustomAllowed } from "../identity/settings.js";
|
|
30
|
+
import { SuperAdminLock } from "../identity/super-admin.js";
|
|
28
31
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
32
|
+
import { afterCommit } from "../infra/database/after-commit.js";
|
|
29
33
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
30
34
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
31
35
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
32
36
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
37
|
+
import { OrgLock } from "../orgs/org-lock.js";
|
|
33
38
|
import { packPackageTgz } from "../packages/tgz.js";
|
|
39
|
+
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
34
40
|
import { checkSkillFiles, SkillArchiveError } from "../skills/package.js";
|
|
35
41
|
import { StorageService } from "../storage/storage.service.js";
|
|
36
42
|
import { canonicalJson } from "../tools/canonical-json.js";
|
|
37
43
|
import { endpointCredentialIssues } from "../tools/endpoint.js";
|
|
38
44
|
import { findConnectorIn } from "./catalog-content.js";
|
|
39
|
-
import { assertPluginNameOwner, catalogObjectPrefix, itemView, permissionsMismatch, precheckCatalogImport, putCatalogFiles, templateProblems, writeCatalogVersion, } from "./catalog-items.js";
|
|
45
|
+
import { assertOrgsExist, assertPluginNameOwner, catalogObjectPrefix, itemConfirmations, itemScopeFacts, itemView, markVersionApplied, permissionsMismatch, precheckCatalogImport, putCatalogFiles, templateProblems, unappliedOwnVersion, writeCatalogVersion, } from "./catalog-items.js";
|
|
40
46
|
import { CLAUDE_PLUGIN_FILE, claudeContentDigest, } from "./claude/manifest.js";
|
|
41
47
|
import { CLAUDE_MCP_CONNECTOR } from "./claude/mcp.js";
|
|
42
48
|
import { item as reportItem, sortItems, } from "./claude/report.js";
|
|
43
49
|
import { isClaudePluginTree, translateClaudePlugin } from "./claude/translate.js";
|
|
44
50
|
import { CatalogItemLock } from "./locks.js";
|
|
51
|
+
import { assertNameFree, assertPackageFree, assertShareable, packageOwner, releaseExtensionClaim, shareGate, usableBy, } from "./ownership.js";
|
|
52
|
+
import { PlatformComponents } from "./platform-components.js";
|
|
53
|
+
import { PLATFORM_MIGRATION_JOB } from "./platform-migration.js";
|
|
45
54
|
import { PluginPackageError, packagePath, readPluginFiles, readPluginPackage, } from "./plugin-package.js";
|
|
55
|
+
import { hasImportRequests, importRequestOf, resolveImportRequests } from "./request-rules.js";
|
|
56
|
+
import { assertNameNotReserved } from "./reserved-names.js";
|
|
57
|
+
import { addScopeOrg } from "./scope-orgs.js";
|
|
46
58
|
import { maxSatisfying, satisfiesRange } from "./versions.js";
|
|
59
|
+
/** How long after a failed post-import platform sync the migration job runs for the item. */
|
|
60
|
+
export const PLATFORM_SYNC_RETRY_MS = 60_000;
|
|
47
61
|
/**
|
|
48
|
-
*
|
|
62
|
+
* Platform import of a plugin (package ecosystem spec §4.3): one review for every plugin, whatever
|
|
49
63
|
* it contains (an extension, skills, connector templates, dependencies), confirmed once.
|
|
50
64
|
*
|
|
51
65
|
* prepare: the tgz is always hostile input, and **nothing is written until every check passes**
|
|
@@ -55,7 +69,7 @@ import { maxSatisfying, satisfiesRange } from "./versions.js";
|
|
|
55
69
|
* refused; name ownership; a downgrade to a version with unrun migrations is refused), each skill by
|
|
56
70
|
* the skill package rules, each connector template against the connector loaded on this replica (or
|
|
57
71
|
* statically when the connector is provided by the plugin's own extension or by a dependency), the
|
|
58
|
-
* catalog name / version conflicts and the actor still
|
|
72
|
+
* catalog name / version conflicts and the actor still a super admin. Dependencies are only
|
|
59
73
|
* resolved against the imported catalog versions for the review (an unresolved one does not block
|
|
60
74
|
* the import, spec §4.1); they are never fetched. Only then is the extension handed to
|
|
61
75
|
* `ExtensionInstalls.ingest` (source `catalog`), the skill / template / icon files written to object
|
|
@@ -64,13 +78,13 @@ import { maxSatisfying, satisfiesRange } from "./versions.js";
|
|
|
64
78
|
* confirm: `reviewHash` must match the staging; in one transaction it deletes the staging row
|
|
65
79
|
* (confirm works once), locks the extension name and recomputes its deployment state (installed /
|
|
66
80
|
* upgraded / swapped after the review -> `plugin.review_stale`), writes the extension's permission
|
|
67
|
-
* snapshot when it is installed by this import (
|
|
68
|
-
* `extension.permissions_approved` carrying the plugin), and creates the item
|
|
69
|
-
* (`manifest` = the normalized plugin.json,
|
|
70
|
-
*
|
|
71
|
-
* deployment and enabled (`ExtensionAdminOps.install` + `enable`;
|
|
72
|
-
* restart); a failure is returned, not thrown, and an upgrade that was
|
|
73
|
-
* snapshot back.
|
|
81
|
+
* snapshot when it is installed or upgraded by this import (source web, audit
|
|
82
|
+
* `extension.permissions_approved` carrying the plugin), and creates the item (with the scope the
|
|
83
|
+
* admin chose, default `optional`) or adds the version (`manifest` = the normalized plugin.json,
|
|
84
|
+
* `extension_sha256`, the files under the staging prefix). After the commit the plugin's own
|
|
85
|
+
* extension is installed on the deployment and enabled (`ExtensionAdminOps.install` + `enable`;
|
|
86
|
+
* each replica reconciles, no restart); a failure is returned, not thrown, and an upgrade that was
|
|
87
|
+
* not installed gets its old snapshot back.
|
|
74
88
|
*
|
|
75
89
|
* Expired staging is cleaned by the maintenance job `app-staging-cleanup`
|
|
76
90
|
* ({@link PluginImport.cleanupExpired}): it deletes the staging row and its object prefix; a stored
|
|
@@ -123,16 +137,20 @@ const fileOf = (path, data) => ({
|
|
|
123
137
|
let PluginImport = class PluginImport {
|
|
124
138
|
kdb;
|
|
125
139
|
adminLock;
|
|
140
|
+
orgLock;
|
|
126
141
|
installLock;
|
|
127
142
|
approveLock;
|
|
128
143
|
itemLock;
|
|
129
144
|
config;
|
|
130
145
|
installs;
|
|
131
146
|
ops;
|
|
147
|
+
admin;
|
|
132
148
|
blobs;
|
|
133
149
|
registry;
|
|
134
150
|
log;
|
|
135
151
|
txHost;
|
|
152
|
+
platform;
|
|
153
|
+
maintenance;
|
|
136
154
|
/** Where plugin files go (the kernel's object storage; the CLI's own, see {@link useCliStorage}). */
|
|
137
155
|
files;
|
|
138
156
|
/**
|
|
@@ -142,24 +160,34 @@ let PluginImport = class PluginImport {
|
|
|
142
160
|
*/
|
|
143
161
|
connectorsLoaded = true;
|
|
144
162
|
constructor(kdb,
|
|
145
|
-
/** The
|
|
146
|
-
adminLock,
|
|
163
|
+
/** The super admin lock (identity/super-admin.ts): joins the caller's transaction. */
|
|
164
|
+
adminLock,
|
|
165
|
+
/** The owner org's lock: an org upload re-checks its admin under it (ruling P3-7). */
|
|
166
|
+
orgLock, installLock, approveLock, itemLock, config, storage, installs, ops, admin,
|
|
147
167
|
/** Reads a package from the package store (confirm recomputes the permission set). */
|
|
148
168
|
blobs,
|
|
149
169
|
/** Config-declared names, loaded connectors and extension versions (the extension registry). */
|
|
150
|
-
registry, log, txHost
|
|
170
|
+
registry, log, txHost,
|
|
171
|
+
/** The platform rows of a new item's components, written after the confirm's commit. */
|
|
172
|
+
platform,
|
|
173
|
+
/** A failed platform sync enqueues a run of the migration job here (it heals the item). */
|
|
174
|
+
maintenance) {
|
|
151
175
|
this.kdb = kdb;
|
|
152
176
|
this.adminLock = adminLock;
|
|
177
|
+
this.orgLock = orgLock;
|
|
153
178
|
this.installLock = installLock;
|
|
154
179
|
this.approveLock = approveLock;
|
|
155
180
|
this.itemLock = itemLock;
|
|
156
181
|
this.config = config;
|
|
157
182
|
this.installs = installs;
|
|
158
183
|
this.ops = ops;
|
|
184
|
+
this.admin = admin;
|
|
159
185
|
this.blobs = blobs;
|
|
160
186
|
this.registry = registry;
|
|
161
187
|
this.log = log;
|
|
162
188
|
this.txHost = txHost;
|
|
189
|
+
this.platform = platform;
|
|
190
|
+
this.maintenance = maintenance;
|
|
163
191
|
this.files = storage;
|
|
164
192
|
}
|
|
165
193
|
/** The kernel-schema handle of the current context (the transaction, else the pool). */
|
|
@@ -174,21 +202,29 @@ let PluginImport = class PluginImport {
|
|
|
174
202
|
useCliStorage(storage) {
|
|
175
203
|
this.files = storage;
|
|
176
204
|
this.connectorsLoaded = false;
|
|
205
|
+
this.platform.useCliStorage(storage);
|
|
177
206
|
}
|
|
178
207
|
/**
|
|
179
|
-
* The
|
|
208
|
+
* The super-admin re-check under the lock, in the caller's transaction. `actor` null is the
|
|
180
209
|
* CLI (the operator of the deployment: no account to re-check, audits carry no actor), like
|
|
181
|
-
* `ExtensionInstalls.ingest`.
|
|
210
|
+
* `ExtensionInstalls.ingest`. An org upload (`owner`) re-checks the actor as an admin of the
|
|
211
|
+
* owner org under the org row lock instead (`catalog.upload`; an org deleted meanwhile -> 404
|
|
212
|
+
* `org.not_found`).
|
|
182
213
|
*/
|
|
183
|
-
async authorizeActor(actor) {
|
|
184
|
-
if (
|
|
214
|
+
async authorizeActor(actor, owner) {
|
|
215
|
+
if (owner !== undefined) {
|
|
216
|
+
if (actor === null)
|
|
217
|
+
throw new Error("an org upload has an actor");
|
|
218
|
+
await this.orgLock.lockAndAuthorize(owner.orgId, actor, "catalog.upload");
|
|
219
|
+
}
|
|
220
|
+
else if (actor === null)
|
|
185
221
|
await this.adminLock.lock();
|
|
186
222
|
else
|
|
187
223
|
await this.adminLock.lockAndAuthorize(actor);
|
|
188
224
|
}
|
|
189
225
|
/** The re-check alone, in a transaction of its own (before anything is written). */
|
|
190
|
-
async authorize(actor) {
|
|
191
|
-
await this.authorizeActor(actor);
|
|
226
|
+
async authorize(actor, owner) {
|
|
227
|
+
await this.authorizeActor(actor, owner);
|
|
192
228
|
}
|
|
193
229
|
/**
|
|
194
230
|
* Reads, checks and stages a plugin (see the file header). Returns the staging id and review.
|
|
@@ -279,6 +315,10 @@ let PluginImport = class PluginImport {
|
|
|
279
315
|
*/
|
|
280
316
|
async prepareContents(actor, pkg, tgz, source, opts) {
|
|
281
317
|
const maxBytes = this.config.get().packages.maxBytes;
|
|
318
|
+
const owner = opts.owner;
|
|
319
|
+
if (owner !== undefined && source.kind !== "file") {
|
|
320
|
+
throw new Error("an org upload is a file (ruling P3-7)");
|
|
321
|
+
}
|
|
282
322
|
// 1. Every check (nothing written)
|
|
283
323
|
const m = pkg.manifest;
|
|
284
324
|
const packageName = packageNameOf(pkg);
|
|
@@ -292,14 +332,34 @@ let PluginImport = class PluginImport {
|
|
|
292
332
|
? await readingPackage(() => readExtensionPackage(bytes, { maxBytes, hostSdkVersion: SDK_VERSION }))
|
|
293
333
|
: undefined;
|
|
294
334
|
const content = this.checkContent(pkg, format);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
335
|
+
// A name live for another owner (an org's for the super admin; the platform's or another
|
|
336
|
+
// org's for an org) -> 409 plugin.name_taken naming it (P3-14); the same owner adds a version
|
|
337
|
+
await assertNameFree(this.db, m.name, owner?.orgId ?? null);
|
|
338
|
+
if (owner !== undefined) {
|
|
339
|
+
// a bootstrap or marketplace plugin's name stays free for the platform (ruling P3-28)
|
|
340
|
+
await assertNameNotReserved(this.db, m.name);
|
|
341
|
+
// its npm package is an identity of this org only (ruling P3-51)
|
|
342
|
+
await assertPackageFree(this.db, packageName, owner.orgId);
|
|
343
|
+
await assertCustomContent(this.db, content);
|
|
344
|
+
}
|
|
345
|
+
// An org's own version imported but not applied: its update is retried (pre-flight F4)
|
|
346
|
+
const retry = owner !== undefined && !m.extension
|
|
347
|
+
? await unappliedOwnVersion(this.db, m.name, owner.orgId, {
|
|
348
|
+
packageName,
|
|
349
|
+
version: m.version,
|
|
350
|
+
contentDigest: pkg.contentDigest,
|
|
351
|
+
})
|
|
352
|
+
: undefined;
|
|
353
|
+
const { existing, version } = retry !== undefined
|
|
354
|
+
? { existing: { id: retry.itemId }, version: retry.version }
|
|
355
|
+
: await precheckCatalogImport(this.db, m.name, {
|
|
356
|
+
packageName,
|
|
357
|
+
version: m.version,
|
|
358
|
+
contentDigest: pkg.contentDigest,
|
|
359
|
+
...(source.integrity !== undefined && { integrity: source.integrity }),
|
|
360
|
+
...(source.detail !== undefined && { detail: source.detail }),
|
|
361
|
+
...(source.marketplaceId !== undefined && { marketplaceId: source.marketplaceId }),
|
|
362
|
+
});
|
|
303
363
|
// The catalog name space is the extension name space: a declarative plugin cannot take a
|
|
304
364
|
// config-declared or another package's extension name either
|
|
305
365
|
this.assertNotDeclared(m.name);
|
|
@@ -318,7 +378,7 @@ let PluginImport = class PluginImport {
|
|
|
318
378
|
...(source.marketplaceId !== undefined && { marketplaceId: source.marketplaceId }),
|
|
319
379
|
...(source.detail !== undefined && { detail: source.detail }),
|
|
320
380
|
};
|
|
321
|
-
const dependencies = await this.resolveDependencies(m);
|
|
381
|
+
const dependencies = await this.resolveDependencies(m, owner);
|
|
322
382
|
let translation;
|
|
323
383
|
if (opts.claude !== undefined) {
|
|
324
384
|
const added = [
|
|
@@ -348,7 +408,8 @@ let PluginImport = class PluginImport {
|
|
|
348
408
|
contentDigest: pkg.contentDigest,
|
|
349
409
|
packageName,
|
|
350
410
|
publisher: m.publisher,
|
|
351
|
-
description: m.description,
|
|
411
|
+
description: pluginDescription(m.description).en,
|
|
412
|
+
descriptionZh: pluginDescription(m.description).zh ?? null,
|
|
352
413
|
...(m.displayName !== undefined && { displayName: m.displayName }),
|
|
353
414
|
...(m.license !== undefined && { license: m.license }),
|
|
354
415
|
...(m.homepage !== undefined && { homepage: m.homepage }),
|
|
@@ -364,8 +425,9 @@ let PluginImport = class PluginImport {
|
|
|
364
425
|
reviewHash: "",
|
|
365
426
|
};
|
|
366
427
|
review.reviewHash = pluginReviewHash(review, content.files);
|
|
367
|
-
// Re-check under the lock that the actor is still an
|
|
368
|
-
|
|
428
|
+
// Re-check under the lock that the actor is still a super admin (an org upload: an admin of
|
|
429
|
+
// the owner org) before writing
|
|
430
|
+
await this.authorize(actor, owner);
|
|
369
431
|
// 2. Write: the extension into the package store, files into object storage, the staging row
|
|
370
432
|
const stagingId = randomUUID();
|
|
371
433
|
const itemId = existing?.id ?? randomUUID();
|
|
@@ -379,10 +441,12 @@ let PluginImport = class PluginImport {
|
|
|
379
441
|
}, {
|
|
380
442
|
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
381
443
|
...(bootstrap && { store: "pg" }),
|
|
444
|
+
// an org upload only stores the package (nothing runs until the super admin confirms)
|
|
445
|
+
...(owner !== undefined && { owner }),
|
|
382
446
|
});
|
|
383
447
|
}
|
|
384
448
|
let objectPrefix = null;
|
|
385
|
-
if (content.files.length > 0) {
|
|
449
|
+
if (content.files.length > 0 && retry === undefined) {
|
|
386
450
|
objectPrefix = catalogObjectPrefix(itemId, version, stagingId);
|
|
387
451
|
await putCatalogFiles(this.files, objectPrefix, content.files);
|
|
388
452
|
}
|
|
@@ -405,15 +469,16 @@ let PluginImport = class PluginImport {
|
|
|
405
469
|
...(translation !== undefined && { translation }),
|
|
406
470
|
...(license.text !== undefined && { licenseText: license.text }),
|
|
407
471
|
...(license.truncated && { licenseTruncated: true }),
|
|
472
|
+
...(retry !== undefined && { retry: true }),
|
|
408
473
|
};
|
|
409
|
-
await this.writeStaging(actor, stagingId, review, staged, objectPrefix, src, opts);
|
|
474
|
+
await this.writeStaging(actor, stagingId, review, staged, objectPrefix, src, opts, owner);
|
|
410
475
|
return { stagingId, review, permissionKeys: keys, permissionsMismatch: mismatch };
|
|
411
476
|
}
|
|
412
477
|
/** The staging row and the `catalog.plugin_staged` audit, the actor re-checked under the lock. */
|
|
413
|
-
async writeStaging(actor, stagingId, review, staged, objectPrefix, src, opts) {
|
|
478
|
+
async writeStaging(actor, stagingId, review, staged, objectPrefix, src, opts, owner) {
|
|
414
479
|
const { extension } = review;
|
|
415
480
|
const t = this.db;
|
|
416
|
-
await this.authorizeActor(actor);
|
|
481
|
+
await this.authorizeActor(actor, owner);
|
|
417
482
|
await t
|
|
418
483
|
.insertInto("plugin_import_staging")
|
|
419
484
|
.values({
|
|
@@ -424,6 +489,8 @@ let PluginImport = class PluginImport {
|
|
|
424
489
|
source: JSON.stringify(src),
|
|
425
490
|
created_by: actor,
|
|
426
491
|
expires_at: new Date(Date.now() + PLUGIN_STAGING_TTL_MS),
|
|
492
|
+
// the org's own staging (P3-8); null = the super admin's
|
|
493
|
+
org_id: owner?.orgId ?? null,
|
|
427
494
|
})
|
|
428
495
|
.execute();
|
|
429
496
|
await audit(t, {
|
|
@@ -443,6 +510,7 @@ let PluginImport = class PluginImport {
|
|
|
443
510
|
...(extension && {
|
|
444
511
|
extension: { sha256: extension.sha256, action: extension.action },
|
|
445
512
|
}),
|
|
513
|
+
...(owner !== undefined && { orgId: owner.orgId }),
|
|
446
514
|
},
|
|
447
515
|
});
|
|
448
516
|
}
|
|
@@ -465,9 +533,10 @@ let PluginImport = class PluginImport {
|
|
|
465
533
|
/**
|
|
466
534
|
* A staged import's review, as prepare returned it, with a marketplace import's permission
|
|
467
535
|
* mismatch when there is one (the import page loads `?staging=<id>` with it). Unknown, expired or
|
|
468
|
-
* already confirmed -> 404 `plugin.staging_not_found`.
|
|
536
|
+
* already confirmed -> 404 `plugin.staging_not_found`. Each sees only its own staging (P3-8):
|
|
537
|
+
* the super admin's routes the platform's (no `owner`), an org's routes that org's.
|
|
469
538
|
*/
|
|
470
|
-
async staged(stagingId) {
|
|
539
|
+
async staged(stagingId, owner) {
|
|
471
540
|
if (!UUID_RE.test(stagingId))
|
|
472
541
|
throw notFound();
|
|
473
542
|
const row = await this.db
|
|
@@ -475,6 +544,7 @@ let PluginImport = class PluginImport {
|
|
|
475
544
|
.select(["id", "review", "manifest"])
|
|
476
545
|
.where("id", "=", stagingId)
|
|
477
546
|
.where("expires_at", ">", sql `now()`)
|
|
547
|
+
.where(ownStaging(owner))
|
|
478
548
|
.executeTakeFirst();
|
|
479
549
|
if (!row)
|
|
480
550
|
throw notFound();
|
|
@@ -486,17 +556,117 @@ let PluginImport = class PluginImport {
|
|
|
486
556
|
};
|
|
487
557
|
}
|
|
488
558
|
/**
|
|
489
|
-
*
|
|
490
|
-
*
|
|
559
|
+
* What an org's staged upload targets (P3-12, pre-flight F3): its item, version and review hash,
|
|
560
|
+
* whether the item exists (a new version of the org's own plugin) or the version is imported
|
|
561
|
+
* already (F4), and the target as the platform updater reads it: the staged manifest and files,
|
|
562
|
+
* or the imported version's for a retry. Another org's staging -> 404 `plugin.staging_not_found`.
|
|
563
|
+
*/
|
|
564
|
+
async stagedTarget(stagingId, owner) {
|
|
565
|
+
if (!UUID_RE.test(stagingId))
|
|
566
|
+
throw notFound();
|
|
567
|
+
const row = await this.db
|
|
568
|
+
.selectFrom("plugin_import_staging")
|
|
569
|
+
.select(["review", "manifest", "object_prefix"])
|
|
570
|
+
.where("id", "=", stagingId)
|
|
571
|
+
.where("expires_at", ">", sql `now()`)
|
|
572
|
+
.where(ownStaging(owner))
|
|
573
|
+
.executeTakeFirst();
|
|
574
|
+
if (!row)
|
|
575
|
+
throw notFound();
|
|
576
|
+
const review = row.review;
|
|
577
|
+
const staged = row.manifest;
|
|
578
|
+
const item = await this.db
|
|
579
|
+
.selectFrom("catalog_items")
|
|
580
|
+
.select(["id", "awaiting_confirmation"])
|
|
581
|
+
.where("id", "=", staged.itemId)
|
|
582
|
+
.where("deleted_at", "is", null)
|
|
583
|
+
.where("owner_org_id", "=", owner.orgId)
|
|
584
|
+
.executeTakeFirst();
|
|
585
|
+
const imported = staged.retry === true
|
|
586
|
+
? await this.db
|
|
587
|
+
.selectFrom("catalog_item_versions")
|
|
588
|
+
.select(["version", "manifest", "object_prefix", "files", "extension_sha256"])
|
|
589
|
+
.where("item_id", "=", staged.itemId)
|
|
590
|
+
.where("version", "=", staged.version)
|
|
591
|
+
.executeTakeFirst()
|
|
592
|
+
: undefined;
|
|
593
|
+
return {
|
|
594
|
+
itemId: staged.itemId,
|
|
595
|
+
version: staged.version,
|
|
596
|
+
reviewHash: review.reviewHash,
|
|
597
|
+
extension: review.extension !== undefined,
|
|
598
|
+
existing: item !== undefined,
|
|
599
|
+
awaiting: item?.awaiting_confirmation === true,
|
|
600
|
+
retry: staged.retry === true,
|
|
601
|
+
target: imported !== undefined
|
|
602
|
+
? {
|
|
603
|
+
version: imported.version,
|
|
604
|
+
manifest: imported.manifest,
|
|
605
|
+
object_prefix: imported.object_prefix,
|
|
606
|
+
files: imported.files,
|
|
607
|
+
extension_sha256: imported.extension_sha256,
|
|
608
|
+
}
|
|
609
|
+
: {
|
|
610
|
+
version: staged.version,
|
|
611
|
+
manifest: staged.manifest,
|
|
612
|
+
object_prefix: row.object_prefix,
|
|
613
|
+
files: staged.files,
|
|
614
|
+
extension_sha256: review.extension?.sha256 ?? null,
|
|
615
|
+
},
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
/** The catalog item's view (as a confirm returns it). */
|
|
619
|
+
async view(itemId) {
|
|
620
|
+
return this.itemView(itemId);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Confirms a staged import (see the file header). Returns the committed catalog item and the
|
|
624
|
+
* extension's deploy result (a failed deploy is returned, not thrown). `scope` (default
|
|
625
|
+
* `optional`) and `orgs` (only with `named`; each an existing org, else `validation_failed`) are
|
|
626
|
+
* written when this confirm creates the item; a new version of an existing item keeps its scope.
|
|
627
|
+
* The caller enqueues the scope sync of the item after this returns.
|
|
628
|
+
*
|
|
629
|
+
* An org upload (`opts.owner`, spec §14): only that org's staging; the actor re-checked as its
|
|
630
|
+
* admin; `scope` / `orgs` are not taken: a new item is owned by the org, scope `named: [owner]`,
|
|
631
|
+
* not shown to other orgs, `shareable` as given (default false). Custom deny, the name owner and
|
|
632
|
+
* the dependencies are checked again under the item name's lock. A package with an extension
|
|
633
|
+
* is stored and its extension name claimed, nothing approved or installed: the version waits
|
|
634
|
+
* for the super admin (`pending`, ruling P3-10; see {@link commit}).
|
|
491
635
|
*/
|
|
492
636
|
async confirm(actor, stagingId, input, opts = {}) {
|
|
493
637
|
if (!UUID_RE.test(stagingId))
|
|
494
638
|
throw notFound();
|
|
639
|
+
const owner = opts.owner;
|
|
640
|
+
if (owner !== undefined && (input.scope !== undefined || input.orgs !== undefined)) {
|
|
641
|
+
throw new Error("an org upload's scope is its owner's");
|
|
642
|
+
}
|
|
643
|
+
// A staging prepared by approving an org's `import` request defaults to that org (spec §5.1
|
|
644
|
+
// rule 3); the admin may still choose any scope
|
|
645
|
+
const linked = input.scope === undefined && owner === undefined
|
|
646
|
+
? await importRequestOf(this.db, stagingId)
|
|
647
|
+
: undefined;
|
|
648
|
+
const scope = owner !== undefined
|
|
649
|
+
? "named"
|
|
650
|
+
: (input.scope ?? (linked !== undefined ? "named" : "optional"));
|
|
651
|
+
const orgs = owner !== undefined
|
|
652
|
+
? [owner.orgId]
|
|
653
|
+
: (input.orgs ?? (linked !== undefined ? [linked.orgId] : undefined));
|
|
654
|
+
if (input.orgs !== undefined && scope !== "named") {
|
|
655
|
+
throw new ApiError("validation_failed", "orgs: only allowed with scope named", [
|
|
656
|
+
{ path: "orgs", message: "only allowed with scope named" },
|
|
657
|
+
]);
|
|
658
|
+
}
|
|
659
|
+
if (scope === "named" && (orgs ?? []).length === 0) {
|
|
660
|
+
throw new ApiError("validation_failed", "orgs: scope named needs at least one organization", [
|
|
661
|
+
{ path: "orgs", message: "scope named needs at least one organization" },
|
|
662
|
+
]);
|
|
663
|
+
}
|
|
495
664
|
const row = await this.db
|
|
496
665
|
.selectFrom("plugin_import_staging")
|
|
497
666
|
.selectAll()
|
|
498
667
|
.where("id", "=", stagingId)
|
|
499
668
|
.where("expires_at", ">", sql `now()`)
|
|
669
|
+
.where(ownStaging(owner))
|
|
500
670
|
.executeTakeFirst();
|
|
501
671
|
if (!row)
|
|
502
672
|
throw notFound();
|
|
@@ -507,34 +677,92 @@ let PluginImport = class PluginImport {
|
|
|
507
677
|
throw reviewStale(review);
|
|
508
678
|
const manifest = withPinned(staged.manifest, review, input.pinnedSuggestions);
|
|
509
679
|
const reviewed = review.extension;
|
|
510
|
-
// Read the stored package outside the transaction (unless it was already installed as is
|
|
511
|
-
|
|
512
|
-
const
|
|
680
|
+
// Read the stored package outside the transaction (unless it was already installed as is, or
|
|
681
|
+
// an org upload, which approves nothing)
|
|
682
|
+
const stored = reviewed && reviewed.action !== "keep" && owner === undefined
|
|
683
|
+
? await this.readStored(reviewed.sha256)
|
|
684
|
+
: undefined;
|
|
685
|
+
const committed = await this.commit(actor, { stagingId, review, staged, src, manifest, objectPrefix: row.object_prefix }, {
|
|
686
|
+
scope,
|
|
687
|
+
orgs: [...new Set(orgs ?? [])].sort(),
|
|
688
|
+
...(owner !== undefined && { owner, shareable: input.shareable ?? false }),
|
|
689
|
+
}, stored, opts);
|
|
513
690
|
const { now } = committed;
|
|
514
|
-
|
|
691
|
+
if (committed.pending) {
|
|
692
|
+
// Waits for the super admin (P3-10): no platform components, nothing deployed
|
|
693
|
+
return {
|
|
694
|
+
item: await this.itemView(committed.itemId),
|
|
695
|
+
created: committed.created,
|
|
696
|
+
deploy: null,
|
|
697
|
+
pending: true,
|
|
698
|
+
firstVersion: false,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
// A new item's skills, agent templates and connector templates become platform components
|
|
702
|
+
// (plugin scope phase 2); a new version of an existing item leaves them to the platform
|
|
703
|
+
// update. After the commit: it copies files in object storage
|
|
704
|
+
if (committed.firstVersion)
|
|
705
|
+
await this.syncPlatform(committed.itemId, staged.version);
|
|
706
|
+
const deploy = now && now.action !== "keep"
|
|
515
707
|
? await this.deploy(actor, review, committed.itemId, now, committed.previous, opts, input.enable ?? true)
|
|
516
708
|
: null;
|
|
517
|
-
return {
|
|
709
|
+
return {
|
|
710
|
+
item: await this.itemView(committed.itemId),
|
|
711
|
+
created: committed.created,
|
|
712
|
+
deploy,
|
|
713
|
+
pending: false,
|
|
714
|
+
firstVersion: committed.firstVersion,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Writes the platform components of an item's first usable version after a commit made
|
|
719
|
+
* elsewhere (the super admin's confirmation of an org upload, `CatalogScopeService`); a failure
|
|
720
|
+
* is logged and retried by the migration job (see {@link syncPlatform}).
|
|
721
|
+
*/
|
|
722
|
+
async writePlatformComponents(itemId, version) {
|
|
723
|
+
await this.syncPlatform(itemId, version);
|
|
518
724
|
}
|
|
519
725
|
/**
|
|
520
726
|
* The confirm's transaction: the staging row consumed, the extension's action recomputed under
|
|
521
727
|
* its install lock (stale -> `plugin.review_stale`), its snapshot approved, and the catalog
|
|
522
728
|
* version written under the item name's lock.
|
|
729
|
+
*
|
|
730
|
+
* An org upload with an extension (ruling P3-10) approves nothing: under the install lock it
|
|
731
|
+
* claims the extension name for its package (another package's -> 409
|
|
732
|
+
* `extension.name_conflict` naming its owner, before anything is written), and the version
|
|
733
|
+
* written becomes the item's pending one (`pending_version` / `_by` / `_at`; a first version
|
|
734
|
+
* also `awaiting_confirmation`). A later upload replaces the pending version (P3-11). An org
|
|
735
|
+
* upload without an extension for an item whose first version still waits (plan Task 6) replaces
|
|
736
|
+
* that upload and applies at once: the item stops waiting and the claim is released (nothing
|
|
737
|
+
* was installed under it).
|
|
523
738
|
*/
|
|
524
|
-
async commit(actor, c,
|
|
739
|
+
async commit(actor, c, scope, stored, opts) {
|
|
525
740
|
const { stagingId, review, staged, src, manifest } = c;
|
|
526
741
|
const reviewed = review.extension;
|
|
527
742
|
const stale = () => reviewStale(review);
|
|
528
|
-
|
|
743
|
+
const { owner } = scope;
|
|
744
|
+
await this.authorizeActor(actor, owner);
|
|
529
745
|
const t = this.db;
|
|
530
746
|
const used = await t
|
|
531
747
|
.deleteFrom("plugin_import_staging")
|
|
532
748
|
.where("id", "=", stagingId)
|
|
533
749
|
.where("expires_at", ">", sql `now()`)
|
|
750
|
+
.where(ownStaging(owner))
|
|
534
751
|
.returning("id")
|
|
535
752
|
.executeTakeFirst();
|
|
536
753
|
if (!used)
|
|
537
754
|
throw notFound();
|
|
755
|
+
if (owner !== undefined) {
|
|
756
|
+
await assertCustomContent(t, {
|
|
757
|
+
skills: review.skills,
|
|
758
|
+
agentTemplates: review.agentTemplates ?? [],
|
|
759
|
+
templates: review.connectorTemplates,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
await assertOrgsExist(t, scope.orgs);
|
|
763
|
+
// Checked again before this upload claims anything (ruling P3-51)
|
|
764
|
+
if (owner !== undefined)
|
|
765
|
+
await assertPackageFree(t, staged.packageName, owner.orgId);
|
|
538
766
|
let now;
|
|
539
767
|
let previous;
|
|
540
768
|
if (reviewed) {
|
|
@@ -544,23 +772,84 @@ let PluginImport = class PluginImport {
|
|
|
544
772
|
await this.installLock.lock(reviewed.name);
|
|
545
773
|
now = {
|
|
546
774
|
...reviewed,
|
|
547
|
-
...actionOf(await this.extensionState(reviewed.name, staged.packageName), reviewed.sha256),
|
|
775
|
+
...actionOf(await this.extensionState(reviewed.name, staged.packageName), reviewed.sha256, reviewed.version),
|
|
548
776
|
};
|
|
549
777
|
if (pluginReviewHash({ ...review, extension: now }, staged.files) !== review.reviewHash) {
|
|
550
778
|
throw stale();
|
|
551
779
|
}
|
|
780
|
+
if (owner !== undefined) {
|
|
781
|
+
// An org upload only claims the name (P3-10): the super admin approves and installs it
|
|
782
|
+
await t
|
|
783
|
+
.insertInto("extension_name_owners")
|
|
784
|
+
.values({ name: reviewed.name, package_name: staged.packageName })
|
|
785
|
+
.onConflict((oc) => oc.column("name").doNothing())
|
|
786
|
+
.execute();
|
|
787
|
+
await assertPluginNameOwner(t, reviewed.name, staged.packageName);
|
|
788
|
+
}
|
|
552
789
|
// A foreseeable deploy failure (a downgrade to a version with unrun migrations) is refused
|
|
553
790
|
// before anything is written
|
|
554
|
-
if (
|
|
791
|
+
if (now.action === "upgrade" && stored) {
|
|
555
792
|
await assertDowngradable(t, now, stored.migrationSql);
|
|
556
793
|
}
|
|
557
|
-
if (now.action === "install" ||
|
|
794
|
+
if (owner === undefined && (now.action === "install" || now.action === "upgrade")) {
|
|
558
795
|
previous = await this.approve(actor, review, staged, now, stored, opts);
|
|
559
796
|
}
|
|
560
797
|
}
|
|
561
798
|
const m = manifest;
|
|
562
799
|
await this.itemLock.lock(m.name);
|
|
563
|
-
|
|
800
|
+
// Under the name's lock: the first confirm of a name wins, a later one of another owner is
|
|
801
|
+
// refused naming it, before anything of it is written (P3-14)
|
|
802
|
+
await assertNameFree(t, m.name, owner?.orgId ?? null);
|
|
803
|
+
if (owner !== undefined) {
|
|
804
|
+
await assertNameNotReserved(t, m.name);
|
|
805
|
+
await this.resolveDependencies(m, owner);
|
|
806
|
+
}
|
|
807
|
+
// The scope the audit records: the review's for a new item, the item's own for a new version
|
|
808
|
+
// of a live item (which keeps its scope)
|
|
809
|
+
const existing = await t
|
|
810
|
+
.selectFrom("catalog_items")
|
|
811
|
+
.select(["id", "scope", "awaiting_confirmation", "package_name"])
|
|
812
|
+
.where("name", "=", m.name)
|
|
813
|
+
.where("deleted_at", "is", null)
|
|
814
|
+
.forUpdate()
|
|
815
|
+
.executeTakeFirst();
|
|
816
|
+
const actual = existing === undefined
|
|
817
|
+
? scope
|
|
818
|
+
: {
|
|
819
|
+
scope: existing.scope,
|
|
820
|
+
orgs: (await t
|
|
821
|
+
.selectFrom("catalog_item_scope_orgs")
|
|
822
|
+
.select("org_id")
|
|
823
|
+
.where("item_id", "=", existing.id)
|
|
824
|
+
.orderBy("org_id")
|
|
825
|
+
.execute()).map((r) => r.org_id),
|
|
826
|
+
};
|
|
827
|
+
if (staged.retry === true) {
|
|
828
|
+
// An org's own version imported already (pre-flight F4): nothing to write, the caller
|
|
829
|
+
// retries its update; it must still be there as reviewed
|
|
830
|
+
const v = existing === undefined
|
|
831
|
+
? undefined
|
|
832
|
+
: await t
|
|
833
|
+
.selectFrom("catalog_item_versions")
|
|
834
|
+
.select("version")
|
|
835
|
+
.where("item_id", "=", existing.id)
|
|
836
|
+
.where("version", "=", staged.version)
|
|
837
|
+
.where("content_digest", "=", staged.contentDigest)
|
|
838
|
+
.executeTakeFirst();
|
|
839
|
+
if (existing === undefined || existing.id !== staged.itemId || v === undefined)
|
|
840
|
+
throw stale();
|
|
841
|
+
return {
|
|
842
|
+
itemId: existing.id,
|
|
843
|
+
created: false,
|
|
844
|
+
firstVersion: false,
|
|
845
|
+
pending: false,
|
|
846
|
+
now,
|
|
847
|
+
previous,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const { id: itemId, created } = await writeCatalogVersion(t, {
|
|
851
|
+
// an org's version becomes its latest only when applied or confirmed (P3-34)
|
|
852
|
+
...(owner !== undefined && { keepLatest: true }),
|
|
564
853
|
actor,
|
|
565
854
|
...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
|
|
566
855
|
name: m.name,
|
|
@@ -570,7 +859,7 @@ let PluginImport = class PluginImport {
|
|
|
570
859
|
manifest: m,
|
|
571
860
|
listing: {
|
|
572
861
|
publisher: m.publisher,
|
|
573
|
-
description: m.description,
|
|
862
|
+
description: pluginDescription(m.description).en,
|
|
574
863
|
displayName: m.displayName ?? null,
|
|
575
864
|
},
|
|
576
865
|
newItemId: staged.itemId,
|
|
@@ -591,12 +880,144 @@ let PluginImport = class PluginImport {
|
|
|
591
880
|
licenseTruncated: staged.licenseTruncated === true,
|
|
592
881
|
auditDetail: {
|
|
593
882
|
stagingId,
|
|
594
|
-
|
|
883
|
+
scope: actual.scope,
|
|
884
|
+
...(actual.scope === "named" && { orgs: actual.orgs }),
|
|
595
885
|
suggestedPinned: pinnedNames(manifest, review),
|
|
596
886
|
...(now && { extension: { name: now.name, version: now.version, action: now.action } }),
|
|
597
887
|
},
|
|
598
888
|
});
|
|
599
|
-
|
|
889
|
+
// A new item gets the scope chosen in the review; a new version keeps the item's scope, its
|
|
890
|
+
// org list and its scope version (prepare reuses the live item's id, so the id cannot tell)
|
|
891
|
+
if (created) {
|
|
892
|
+
await t
|
|
893
|
+
.updateTable("catalog_items")
|
|
894
|
+
.set({
|
|
895
|
+
scope: scope.scope,
|
|
896
|
+
// an org upload: the org owns it, nobody else sees it until shared and shown (P3-16)
|
|
897
|
+
...(owner !== undefined && {
|
|
898
|
+
owner_org_id: owner.orgId,
|
|
899
|
+
shareable: scope.shareable === true,
|
|
900
|
+
shown_to_others: false,
|
|
901
|
+
}),
|
|
902
|
+
})
|
|
903
|
+
.where("id", "=", itemId)
|
|
904
|
+
.execute();
|
|
905
|
+
if (scope.scope === "named" && scope.orgs.length > 0) {
|
|
906
|
+
await t
|
|
907
|
+
.insertInto("catalog_item_scope_orgs")
|
|
908
|
+
.values(scope.orgs.map((org_id) => ({ item_id: itemId, org_id })))
|
|
909
|
+
.onConflict((oc) => oc.doNothing())
|
|
910
|
+
.execute();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
// The org requests this staging answers resolve with the confirm; on an item that existed
|
|
914
|
+
// already (imported meanwhile) a `named` scope gains their orgs. An org-owned item held back by
|
|
915
|
+
// the share gate fails closed before any request is marked (ruling P3-26; P3-14 refuses a
|
|
916
|
+
// super admin import of an org-owned name, so this should not be reached)
|
|
917
|
+
const item = created
|
|
918
|
+
? undefined
|
|
919
|
+
: await t
|
|
920
|
+
.selectFrom("catalog_items")
|
|
921
|
+
.select(["id", "name", "scope", "builtin", "owner_org_id", "shareable"])
|
|
922
|
+
.where("id", "=", itemId)
|
|
923
|
+
.forUpdate()
|
|
924
|
+
.executeTakeFirstOrThrow();
|
|
925
|
+
if (item !== undefined && !shareGate(item) && (await hasImportRequests(t, stagingId))) {
|
|
926
|
+
assertShareable(item);
|
|
927
|
+
}
|
|
928
|
+
const requestOrgs = await resolveImportRequests(t, stagingId, itemId, actor, opts);
|
|
929
|
+
if (item !== undefined) {
|
|
930
|
+
for (const org of requestOrgs)
|
|
931
|
+
await addScopeOrg(t, actor, item, org, opts);
|
|
932
|
+
}
|
|
933
|
+
const pending = owner !== undefined && reviewed !== undefined;
|
|
934
|
+
// An org's first version without an extension is installed at once: applied (P3-52)
|
|
935
|
+
if (owner !== undefined && created && !pending)
|
|
936
|
+
await markVersionApplied(t, itemId, staged.version);
|
|
937
|
+
if (pending) {
|
|
938
|
+
// Waits for the super admin; a later upload replaces the pending version (P3-10, P3-11)
|
|
939
|
+
await t
|
|
940
|
+
.updateTable("catalog_items")
|
|
941
|
+
.set({
|
|
942
|
+
...(created && { awaiting_confirmation: true }),
|
|
943
|
+
pending_version: staged.version,
|
|
944
|
+
pending_by: actor,
|
|
945
|
+
pending_at: sql `now()`,
|
|
946
|
+
})
|
|
947
|
+
.where("id", "=", itemId)
|
|
948
|
+
.execute();
|
|
949
|
+
}
|
|
950
|
+
// A first version still waiting replaced by one without an extension: applied at once
|
|
951
|
+
const replacedAwaiting = owner !== undefined && !pending && existing?.awaiting_confirmation === true;
|
|
952
|
+
if (replacedAwaiting) {
|
|
953
|
+
await t
|
|
954
|
+
.updateTable("catalog_items")
|
|
955
|
+
.set({
|
|
956
|
+
awaiting_confirmation: false,
|
|
957
|
+
pending_version: null,
|
|
958
|
+
pending_by: null,
|
|
959
|
+
pending_at: null,
|
|
960
|
+
latest_version: staged.version,
|
|
961
|
+
})
|
|
962
|
+
.where("id", "=", itemId)
|
|
963
|
+
.execute();
|
|
964
|
+
await markVersionApplied(t, itemId, staged.version);
|
|
965
|
+
await releaseExtensionClaim(t, m.name, existing.package_name);
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
itemId,
|
|
969
|
+
created,
|
|
970
|
+
firstVersion: (created && !pending) || replacedAwaiting,
|
|
971
|
+
pending,
|
|
972
|
+
now,
|
|
973
|
+
previous,
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* The platform components of a new item. The item is committed already, so a failure is logged
|
|
978
|
+
* and not thrown (like a failed deploy, the import itself succeeded); a run of the platform
|
|
979
|
+
* components migration job (which writes what is missing) is enqueued on `cap-kernel-maintenance`
|
|
980
|
+
* after {@link PLATFORM_SYNC_RETRY_MS}, one per item (deterministic job id). If that run fails
|
|
981
|
+
* too, its `itemsFailed` shows in the super admin's inbox; the daily run and every worker start
|
|
982
|
+
* try again.
|
|
983
|
+
*/
|
|
984
|
+
async syncPlatform(itemId, version) {
|
|
985
|
+
try {
|
|
986
|
+
await this.platform.syncFromVersion(itemId, version, { initial: true });
|
|
987
|
+
}
|
|
988
|
+
catch (err) {
|
|
989
|
+
// Removed meanwhile (an org deletion racing a confirmation, P3-20): nothing to write
|
|
990
|
+
const live = await this.db
|
|
991
|
+
.selectFrom("catalog_items")
|
|
992
|
+
.select("id")
|
|
993
|
+
.where("id", "=", itemId)
|
|
994
|
+
.where("deleted_at", "is", null)
|
|
995
|
+
.executeTakeFirst();
|
|
996
|
+
if (live === undefined) {
|
|
997
|
+
this.log.info("catalog item removed before its platform components were written", {
|
|
998
|
+
itemId,
|
|
999
|
+
version,
|
|
1000
|
+
});
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
this.log.error("writing the platform components of a new catalog item failed", {
|
|
1004
|
+
itemId,
|
|
1005
|
+
version,
|
|
1006
|
+
err,
|
|
1007
|
+
});
|
|
1008
|
+
try {
|
|
1009
|
+
await this.maintenance.add(PLATFORM_MIGRATION_JOB, {}, {
|
|
1010
|
+
jobId: `${PLATFORM_MIGRATION_JOB}-retry-${itemId}`,
|
|
1011
|
+
delay: PLATFORM_SYNC_RETRY_MS,
|
|
1012
|
+
attempts: 1,
|
|
1013
|
+
removeOnComplete: true,
|
|
1014
|
+
removeOnFail: { age: 86_400 },
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
catch (e) {
|
|
1018
|
+
this.log.warn("cannot enqueue the platform components migration", { itemId, err: e });
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
600
1021
|
}
|
|
601
1022
|
/**
|
|
602
1023
|
* Writes the extension's permission snapshot for the version being imported (source web; cli
|
|
@@ -786,7 +1207,7 @@ let PluginImport = class PluginImport {
|
|
|
786
1207
|
const t = this.db;
|
|
787
1208
|
const locked = await t
|
|
788
1209
|
.selectFrom("plugin_import_staging")
|
|
789
|
-
.select("id")
|
|
1210
|
+
.select(["id", "org_id", "review", "manifest"])
|
|
790
1211
|
.where("id", "=", r.id)
|
|
791
1212
|
.where("expires_at", "<=", sql `now()`)
|
|
792
1213
|
.forUpdate()
|
|
@@ -797,8 +1218,67 @@ let PluginImport = class PluginImport {
|
|
|
797
1218
|
if (r.object_prefix)
|
|
798
1219
|
await this.files.deployment().deletePrefix(r.object_prefix);
|
|
799
1220
|
await t.deleteFrom("plugin_import_staging").where("id", "=", r.id).execute();
|
|
1221
|
+
await this.dropOrgPackage(locked);
|
|
800
1222
|
return true;
|
|
801
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* An expired org staging's extension package (an org upload stores it at prepare, P3-10): removed
|
|
1226
|
+
* with the staging unless something else uses it — an install (running or migrated), a catalog
|
|
1227
|
+
* version, another staging — or it was stored by anyone else (the package row's source must be
|
|
1228
|
+
* this staging's own `catalog` ref). A platform staging keeps its package, as before. Its object
|
|
1229
|
+
* is deleted after the commit.
|
|
1230
|
+
*/
|
|
1231
|
+
async dropOrgPackage(row) {
|
|
1232
|
+
const sha = row.review.extension?.sha256;
|
|
1233
|
+
if (row.org_id === null || sha === undefined)
|
|
1234
|
+
return;
|
|
1235
|
+
const staged = row.manifest;
|
|
1236
|
+
const t = this.db;
|
|
1237
|
+
const pkg = await t
|
|
1238
|
+
.selectFrom("extension_packages")
|
|
1239
|
+
.select("blob_store")
|
|
1240
|
+
.where("sha256", "=", sha)
|
|
1241
|
+
.where("source", "=", "catalog")
|
|
1242
|
+
.where("source_ref", "=", `${staged.itemId}@${staged.version}`)
|
|
1243
|
+
.forUpdate()
|
|
1244
|
+
.executeTakeFirst();
|
|
1245
|
+
if (!pkg)
|
|
1246
|
+
return;
|
|
1247
|
+
const used = (await t
|
|
1248
|
+
.selectFrom("extension_installs")
|
|
1249
|
+
.select("name")
|
|
1250
|
+
.where((eb) => eb.or([eb("sha256", "=", sha), eb("migrated_sha", "=", sha)]))
|
|
1251
|
+
.executeTakeFirst()) ??
|
|
1252
|
+
(await t
|
|
1253
|
+
.selectFrom("catalog_item_versions")
|
|
1254
|
+
.select("version")
|
|
1255
|
+
.where("extension_sha256", "=", sha)
|
|
1256
|
+
.executeTakeFirst()) ??
|
|
1257
|
+
(await t
|
|
1258
|
+
.selectFrom("plugin_import_staging")
|
|
1259
|
+
.select("id")
|
|
1260
|
+
.where(sql `review->'extension'->>'sha256' = ${sha}`)
|
|
1261
|
+
.executeTakeFirst());
|
|
1262
|
+
if (used !== undefined)
|
|
1263
|
+
return;
|
|
1264
|
+
// extension_blobs (a PG-stored package) goes with the row (ON DELETE CASCADE)
|
|
1265
|
+
await t.deleteFrom("extension_packages").where("sha256", "=", sha).execute();
|
|
1266
|
+
// The object only once the row is gone for good: a failure leaves an unreferenced object
|
|
1267
|
+
// (harmless; a later upload of the same bytes rewrites it), never a row without its bytes
|
|
1268
|
+
if (pkg.blob_store === "object") {
|
|
1269
|
+
afterCommit(async () => {
|
|
1270
|
+
try {
|
|
1271
|
+
await this.files.deployment().delete(blobKey(sha));
|
|
1272
|
+
}
|
|
1273
|
+
catch (err) {
|
|
1274
|
+
this.log.warn("cannot delete an expired org upload's extension package object", {
|
|
1275
|
+
sha256: sha,
|
|
1276
|
+
err,
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
802
1282
|
// ---------------------------------------------------------------------------------------------
|
|
803
1283
|
/** npm / marketplace imports: the package must be the one that was asked for. */
|
|
804
1284
|
checkExpected(pkg, expect, format = "capybara") {
|
|
@@ -943,26 +1423,52 @@ let PluginImport = class PluginImport {
|
|
|
943
1423
|
}
|
|
944
1424
|
/**
|
|
945
1425
|
* The highest imported, non-deleted catalog version satisfying each dependency's range (null when
|
|
946
|
-
* none does).
|
|
1426
|
+
* none does). An org upload (`owner`, P3-13) resolves only against items the org can use
|
|
1427
|
+
* ({@link usableBy}: confirmed, not disabled, offered to it), and any dependency left unresolved
|
|
1428
|
+
* -> 409 `plugin.dependency_unavailable` (details `[{ dependencies }]`, their names): an org
|
|
1429
|
+
* upload never brings in anything the org could not install itself.
|
|
947
1430
|
*/
|
|
948
|
-
async resolveDependencies(m) {
|
|
1431
|
+
async resolveDependencies(m, owner) {
|
|
949
1432
|
const deps = m.dependencies ?? [];
|
|
950
1433
|
if (deps.length === 0)
|
|
951
1434
|
return [];
|
|
952
|
-
const
|
|
1435
|
+
const names = deps.map((d) => d.name);
|
|
1436
|
+
const versions = this.db
|
|
953
1437
|
.selectFrom("catalog_items as i")
|
|
954
1438
|
.innerJoin("catalog_item_versions as v", "v.item_id", "i.id")
|
|
955
|
-
.select(["i.name", "v.version", "v.imported_at"])
|
|
956
1439
|
.where("i.deleted_at", "is", null)
|
|
957
|
-
.where("i.name", "in",
|
|
958
|
-
|
|
959
|
-
|
|
1440
|
+
.where("i.name", "in", names);
|
|
1441
|
+
const rows = owner === undefined
|
|
1442
|
+
? await versions.select(["i.name", "v.version", "v.imported_at"]).execute()
|
|
1443
|
+
: (await versions
|
|
1444
|
+
// the org's own row of a `named` list (usableBy reads whether it is listed)
|
|
1445
|
+
.leftJoin("catalog_item_scope_orgs as so", (j) => j.onRef("so.item_id", "=", "i.id").on("so.org_id", "=", owner.orgId))
|
|
1446
|
+
.select([
|
|
1447
|
+
"i.name",
|
|
1448
|
+
"v.version",
|
|
1449
|
+
"v.imported_at",
|
|
1450
|
+
"i.scope",
|
|
1451
|
+
"i.builtin",
|
|
1452
|
+
"i.disabled",
|
|
1453
|
+
"i.deleted_at",
|
|
1454
|
+
"i.awaiting_confirmation",
|
|
1455
|
+
"i.owner_org_id",
|
|
1456
|
+
"i.shareable",
|
|
1457
|
+
"so.org_id as listed",
|
|
1458
|
+
])
|
|
1459
|
+
.execute()).filter((r) => usableBy(r, owner.orgId, r.listed !== null));
|
|
1460
|
+
const resolved = deps.map((d) => ({
|
|
960
1461
|
name: d.name,
|
|
961
1462
|
range: d.version,
|
|
962
1463
|
resolved: maxSatisfying(rows
|
|
963
1464
|
.filter((r) => r.name === d.name)
|
|
964
1465
|
.map((r) => ({ version: r.version, importedAt: r.imported_at })), d.version)?.version ?? null,
|
|
965
1466
|
}));
|
|
1467
|
+
const missing = resolved.filter((d) => d.resolved === null).map((d) => d.name);
|
|
1468
|
+
if (owner !== undefined && missing.length > 0) {
|
|
1469
|
+
throw new ApiError("plugin.dependency_unavailable", `Dependencies not available to this organization: ${missing.join(", ")}`, [{ dependencies: missing }]);
|
|
1470
|
+
}
|
|
1471
|
+
return resolved;
|
|
966
1472
|
}
|
|
967
1473
|
/**
|
|
968
1474
|
* The extension's state on the deployment; installed from another npm package ->
|
|
@@ -980,7 +1486,7 @@ let PluginImport = class PluginImport {
|
|
|
980
1486
|
.where("extension_installs.name", "=", name)
|
|
981
1487
|
.executeTakeFirst();
|
|
982
1488
|
if (r && r.package_name !== packageName) {
|
|
983
|
-
throw new ApiError("extension.name_conflict", `Extension ${name} is already installed from package ${r.package_name}; package ${packageName} cannot use it
|
|
1489
|
+
throw new ApiError("extension.name_conflict", `Extension ${name} is already installed from package ${r.package_name}; package ${packageName} cannot use it`, [{ owner: await packageOwner(this.db, r.package_name, name) }]);
|
|
984
1490
|
}
|
|
985
1491
|
return r
|
|
986
1492
|
? { kind: "installed", version: r.version, sha256: r.sha256, packageName: r.package_name }
|
|
@@ -1002,7 +1508,7 @@ let PluginImport = class PluginImport {
|
|
|
1002
1508
|
name,
|
|
1003
1509
|
version: pkg.section.version,
|
|
1004
1510
|
sha256: pkg.sha256,
|
|
1005
|
-
...actionOf(state, pkg.sha256),
|
|
1511
|
+
...actionOf(state, pkg.sha256, pkg.section.version),
|
|
1006
1512
|
permissions,
|
|
1007
1513
|
added,
|
|
1008
1514
|
addedItems: permissionKeyItems(added),
|
|
@@ -1027,10 +1533,11 @@ let PluginImport = class PluginImport {
|
|
|
1027
1533
|
.executeTakeFirstOrThrow();
|
|
1028
1534
|
const versions = await this.db
|
|
1029
1535
|
.selectFrom("catalog_item_versions")
|
|
1030
|
-
.select(["version", "source", "imported_at"])
|
|
1536
|
+
.select(["version", "source", "imported_at", "extension_sha256", "manifest", "source_format"])
|
|
1031
1537
|
.where("item_id", "=", itemId)
|
|
1032
1538
|
.execute();
|
|
1033
|
-
|
|
1539
|
+
const facts = await itemScopeFacts(this.db, [item], await itemConfirmations(this.admin, [item], new Map([[item.id, versions]])));
|
|
1540
|
+
return itemView(item, versions, 0, undefined, facts.get(item.id));
|
|
1034
1541
|
}
|
|
1035
1542
|
};
|
|
1036
1543
|
__decorate([
|
|
@@ -1054,21 +1561,28 @@ __decorate([
|
|
|
1054
1561
|
__decorate([
|
|
1055
1562
|
Transactional()
|
|
1056
1563
|
], PluginImport.prototype, "cleanupOne", null);
|
|
1564
|
+
__decorate([
|
|
1565
|
+
Transactional({ propagation: "mandatory" })
|
|
1566
|
+
], PluginImport.prototype, "dropOrgPackage", null);
|
|
1057
1567
|
PluginImport = __decorate([
|
|
1058
1568
|
Injectable(),
|
|
1059
1569
|
__param(0, Inject(KernelDb)),
|
|
1060
|
-
__param(1, Inject(
|
|
1061
|
-
__param(2, Inject(
|
|
1062
|
-
__param(3, Inject(
|
|
1063
|
-
__param(4, Inject(
|
|
1064
|
-
__param(5, Inject(
|
|
1065
|
-
__param(6, Inject(
|
|
1066
|
-
__param(7, Inject(
|
|
1067
|
-
__param(8, Inject(
|
|
1068
|
-
__param(9, Inject(
|
|
1069
|
-
__param(10, Inject(
|
|
1070
|
-
__param(11, Inject(
|
|
1071
|
-
__param(12, Inject(
|
|
1570
|
+
__param(1, Inject(SuperAdminLock)),
|
|
1571
|
+
__param(2, Inject(OrgLock)),
|
|
1572
|
+
__param(3, Inject(PluginInstallLock)),
|
|
1573
|
+
__param(4, Inject(PluginApproveLock)),
|
|
1574
|
+
__param(5, Inject(CatalogItemLock)),
|
|
1575
|
+
__param(6, Inject(KernelConfigService)),
|
|
1576
|
+
__param(7, Inject(StorageService)),
|
|
1577
|
+
__param(8, Inject(ExtensionInstalls)),
|
|
1578
|
+
__param(9, Inject(ExtensionAdminOps)),
|
|
1579
|
+
__param(10, Inject(ExtensionAdmin)),
|
|
1580
|
+
__param(11, Inject(ExtensionBlobs)),
|
|
1581
|
+
__param(12, Inject(ExtensionRegistry)),
|
|
1582
|
+
__param(13, Inject(KERNEL_LOGGER)),
|
|
1583
|
+
__param(14, Inject(TransactionHost)),
|
|
1584
|
+
__param(15, Inject(PlatformComponents)),
|
|
1585
|
+
__param(16, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
|
|
1072
1586
|
], PluginImport);
|
|
1073
1587
|
export { PluginImport };
|
|
1074
1588
|
/**
|
|
@@ -1097,13 +1611,20 @@ function packageNameOf(pkg) {
|
|
|
1097
1611
|
return typeof name === "string" ? name : null;
|
|
1098
1612
|
}
|
|
1099
1613
|
/** The extension's deployment state -> action and current version. */
|
|
1100
|
-
function actionOf(s, sha256) {
|
|
1614
|
+
function actionOf(s, sha256, version) {
|
|
1101
1615
|
if (s.kind === "installed") {
|
|
1616
|
+
// A version below the deployed one is recorded in the catalog only: an import never
|
|
1617
|
+
// downgrades the deployment (ruling H)
|
|
1618
|
+
const older = s.sha256 !== sha256 &&
|
|
1619
|
+
semver.valid(version) !== null &&
|
|
1620
|
+
semver.valid(s.version) !== null &&
|
|
1621
|
+
semver.lt(version, s.version);
|
|
1102
1622
|
return {
|
|
1103
|
-
action: s.sha256 === sha256 ? "keep" : "upgrade",
|
|
1623
|
+
action: s.sha256 === sha256 || older ? "keep" : "upgrade",
|
|
1104
1624
|
deployed: true,
|
|
1105
1625
|
currentVersion: s.version,
|
|
1106
1626
|
currentSha256: s.sha256,
|
|
1627
|
+
...(older && { olderThanDeployed: true }),
|
|
1107
1628
|
};
|
|
1108
1629
|
}
|
|
1109
1630
|
return { action: "install", deployed: false, currentVersion: null, currentSha256: null };
|
|
@@ -1206,3 +1727,22 @@ function pinnedNames(m, review) {
|
|
|
1206
1727
|
const byPath = new Map(review.skills.map((s) => [s.path, s.name]));
|
|
1207
1728
|
return (m.skills ?? []).filter((s) => s.pinned === true).map((s) => byPath.get(s.path) ?? s.path);
|
|
1208
1729
|
}
|
|
1730
|
+
/**
|
|
1731
|
+
* The staging rows a route may see (P3-8): the platform's (`org_id` null) without an owner, else
|
|
1732
|
+
* the owner org's.
|
|
1733
|
+
*/
|
|
1734
|
+
function ownStaging(owner) {
|
|
1735
|
+
return (eb) => owner === undefined ? eb("org_id", "is", null) : eb("org_id", "=", owner.orgId);
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* An org upload's custom deny, by content (ruling P3-15'): skills or agent templates ->
|
|
1739
|
+
* `customSkills`, connector templates -> `customConnectors`; each `deny` -> 403
|
|
1740
|
+
* `catalog.custom_disabled`. A package carrying neither passes.
|
|
1741
|
+
*/
|
|
1742
|
+
async function assertCustomContent(db, content) {
|
|
1743
|
+
if (content.skills.length > 0 || content.agentTemplates.length > 0) {
|
|
1744
|
+
await assertCustomAllowed(db, "customSkills");
|
|
1745
|
+
}
|
|
1746
|
+
if (content.templates.length > 0)
|
|
1747
|
+
await assertCustomAllowed(db, "customConnectors");
|
|
1748
|
+
}
|