@capybara-hq/kernel 0.1.0-rc.6 → 0.1.0-rc.7
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 +27 -3
- package/dist/admin/admin.controller.d.ts +8 -72
- package/dist/admin/admin.controller.js +21 -46
- package/dist/admin/admin.schemas.d.ts +57 -0
- package/dist/admin/admin.schemas.js +50 -0
- package/dist/admin/extensions.controller.d.ts +10 -0
- package/dist/admin/extensions.controller.js +18 -0
- package/dist/admin/extensions.schemas.d.ts +7 -0
- package/dist/admin/extensions.schemas.js +10 -0
- package/dist/admin/limit-defaults.d.ts +20 -0
- package/dist/admin/limit-defaults.js +10 -0
- package/dist/agents/agent-profile.service.d.ts +7 -5
- package/dist/agents/agent-profile.service.js +14 -9
- package/dist/agents/agents.controller.d.ts +7 -287
- package/dist/agents/agents.controller.js +7 -67
- package/dist/agents/agents.schemas.d.ts +389 -0
- package/dist/agents/agents.schemas.js +126 -0
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +67 -13
- package/dist/agents/definition.d.ts +47 -19
- package/dist/agents/definition.js +119 -38
- package/dist/agents/from-template.d.ts +2 -2
- package/dist/agents/from-template.js +76 -45
- package/dist/agents/from-template.service.d.ts +12 -0
- package/dist/agents/from-template.service.js +20 -3
- package/dist/agents/models.controller.d.ts +1 -2
- package/dist/agents/models.controller.js +1 -1
- package/dist/agents/org-templates.d.ts +13 -3
- package/dist/agents/org-templates.js +69 -28
- package/dist/agents/pending-connector-requests.d.ts +2 -0
- package/dist/agents/pending-connector-requests.js +1 -1
- package/dist/agents/pending-connectors.controller.d.ts +1 -1
- package/dist/agents/pending-connectors.controller.js +2 -2
- package/dist/agents/pending-connectors.service.d.ts +2 -2
- package/dist/agents/pending-connectors.service.js +19 -19
- package/dist/agents/profile.d.ts +18 -0
- package/dist/agents/profile.js +5 -1
- package/dist/agents/templates.controller.d.ts +8 -17
- package/dist/agents/templates.controller.js +13 -31
- package/dist/agents/templates.schemas.d.ts +20 -0
- package/dist/agents/templates.schemas.js +45 -0
- package/dist/background/background.controller.js +3 -2
- package/dist/background/background.schemas.d.ts +10 -0
- package/dist/background/background.schemas.js +16 -0
- package/dist/background/tasks.service.d.ts +1 -1
- package/dist/background/tasks.service.js +7 -2
- package/dist/catalog/admin-catalog.controller.d.ts +4 -57
- package/dist/catalog/admin-catalog.controller.js +8 -45
- package/dist/catalog/admin-catalog.schemas.d.ts +72 -0
- package/dist/catalog/admin-catalog.schemas.js +92 -0
- package/dist/catalog/catalog-content.d.ts +12 -2
- package/dist/catalog/catalog-scope.service.js +8 -2
- package/dist/catalog/catalog.service.d.ts +2 -0
- package/dist/catalog/catalog.service.js +3 -0
- package/dist/catalog/component-scope.controller.d.ts +3 -65
- package/dist/catalog/component-scope.controller.js +3 -56
- package/dist/catalog/component-scope.schemas.d.ts +114 -0
- package/dist/catalog/component-scope.schemas.js +97 -0
- package/dist/catalog/component-scope.service.d.ts +28 -4
- package/dist/catalog/component-scope.service.js +55 -3
- package/dist/catalog/component-state.d.ts +9 -2
- package/dist/catalog/component-state.js +22 -13
- package/dist/catalog/marketplaces.controller.d.ts +5 -35
- package/dist/catalog/marketplaces.controller.js +6 -34
- package/dist/catalog/marketplaces.schemas.d.ts +48 -0
- package/dist/catalog/marketplaces.schemas.js +53 -0
- package/dist/catalog/marketplaces.service.d.ts +15 -1
- package/dist/catalog/marketplaces.service.js +22 -1
- package/dist/catalog/org-catalog.controller.d.ts +6 -62
- package/dist/catalog/org-catalog.controller.js +12 -66
- package/dist/catalog/org-catalog.schemas.d.ts +79 -0
- package/dist/catalog/org-catalog.schemas.js +82 -0
- package/dist/catalog/org-catalog.service.d.ts +9 -2
- package/dist/catalog/org-catalog.service.js +38 -5
- package/dist/catalog/org-extension.controller.d.ts +1 -12
- package/dist/catalog/org-extension.controller.js +1 -10
- package/dist/catalog/org-extension.schemas.d.ts +15 -0
- package/dist/catalog/org-extension.schemas.js +22 -0
- package/dist/catalog/org-plugin-uploads.controller.d.ts +2 -19
- package/dist/catalog/org-plugin-uploads.controller.js +15 -28
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +20 -0
- package/dist/catalog/org-plugin-uploads.schemas.js +32 -0
- package/dist/catalog/owned-items.d.ts +6 -2
- package/dist/catalog/owned-items.js +16 -4
- package/dist/catalog/platform-components.d.ts +2 -0
- package/dist/catalog/platform-components.js +2 -0
- package/dist/catalog/plugin-import.js +1 -0
- package/dist/catalog/plugin-install.js +1 -1
- package/dist/catalog/requests.service.d.ts +2 -1
- package/dist/catalog/requests.service.js +6 -2
- package/dist/channels/channels.schemas.d.ts +33 -0
- package/dist/channels/channels.schemas.js +34 -0
- package/dist/channels/connections.controller.d.ts +2 -29
- package/dist/channels/connections.controller.js +1 -23
- package/dist/channels/connections.service.d.ts +8 -2
- package/dist/channels/connections.service.js +23 -5
- package/dist/channels/inbound.js +4 -0
- package/dist/conversations/conversations.controller.d.ts +16 -37
- package/dist/conversations/conversations.controller.js +16 -56
- package/dist/conversations/conversations.schemas.d.ts +52 -0
- package/dist/conversations/conversations.schemas.js +79 -0
- package/dist/conversations/conversations.service.d.ts +9 -1
- package/dist/conversations/conversations.service.js +23 -18
- package/dist/conversations/events.d.ts +80 -6
- package/dist/conversations/events.js +60 -5
- package/dist/conversations/message-context.d.ts +28 -0
- package/dist/conversations/message-context.js +56 -0
- package/dist/conversations/queue.d.ts +2 -1
- package/dist/conversations/queue.js +11 -2
- package/dist/db/kernel-schema.d.ts +20 -0
- package/dist/environments/environments.schemas.d.ts +3 -0
- package/dist/environments/environments.schemas.js +14 -0
- package/dist/environments/environments.service.d.ts +16 -4
- package/dist/environments/environments.service.js +58 -13
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +3 -0
- package/dist/evals/evals.schemas.d.ts +110 -0
- package/dist/evals/evals.schemas.js +107 -0
- package/dist/evals/reconstruct.js +18 -11
- package/dist/evals/recorded-tools.d.ts +2 -1
- package/dist/evals/recorded-tools.js +11 -3
- package/dist/evals/run-replay.d.ts +8 -1
- package/dist/evals/run-replay.js +13 -0
- package/dist/evals/runs.controller.d.ts +2 -57
- package/dist/evals/runs.controller.js +1 -42
- package/dist/evals/runs.service.d.ts +6 -6
- package/dist/evals/runs.service.js +32 -18
- package/dist/evals/sets.controller.d.ts +2 -28
- package/dist/evals/sets.controller.js +1 -31
- package/dist/evals/sets.service.d.ts +7 -7
- package/dist/evals/sets.service.js +23 -17
- package/dist/evals/snapshot.d.ts +3 -2
- package/dist/evals/snapshot.js +17 -4
- package/dist/extensions/admin-ops.d.ts +5 -3
- package/dist/extensions/admin-ops.js +17 -12
- package/dist/extensions/deployment-config.d.ts +16 -1
- package/dist/extensions/deployment-config.js +83 -0
- package/dist/extensions/extensions.module.js +3 -0
- package/dist/extensions/host.d.ts +33 -2
- package/dist/extensions/host.js +107 -1
- package/dist/extensions/installs.d.ts +5 -0
- package/dist/extensions/installs.js +12 -0
- package/dist/extensions/org-config.d.ts +17 -2
- package/dist/extensions/org-config.js +23 -4
- package/dist/extensions/point-defaults.controller.d.ts +2 -8
- package/dist/extensions/point-defaults.controller.js +1 -3
- package/dist/extensions/point-defaults.d.ts +3 -1
- package/dist/extensions/point-defaults.js +4 -2
- package/dist/extensions/point-defaults.schemas.d.ts +9 -0
- package/dist/extensions/point-defaults.schemas.js +14 -0
- package/dist/extensions/unavailable.d.ts +49 -0
- package/dist/extensions/unavailable.js +113 -0
- package/dist/files/previewers.controller.js +2 -3
- package/dist/files/previewers.d.ts +1 -1
- package/dist/files/previewers.js +4 -4
- package/dist/files/previewers.schemas.d.ts +10 -0
- package/dist/files/previewers.schemas.js +9 -0
- package/dist/http/api.module.js +2 -0
- package/dist/identity/accounts.d.ts +43 -4
- package/dist/identity/accounts.js +74 -6
- package/dist/identity/auth.guard.js +10 -1
- package/dist/identity/cookies.d.ts +6 -0
- package/dist/identity/cookies.js +14 -0
- package/dist/identity/identity.module.js +3 -0
- package/dist/identity/initial-passwords.controller.d.ts +22 -0
- package/dist/identity/initial-passwords.controller.js +49 -0
- package/dist/identity/initial-passwords.d.ts +61 -0
- package/dist/identity/initial-passwords.job.d.ts +16 -0
- package/dist/identity/initial-passwords.job.js +46 -0
- package/dist/identity/initial-passwords.js +159 -0
- package/dist/identity/me.controller.d.ts +14 -0
- package/dist/identity/me.controller.js +39 -1
- package/dist/identity/principal.d.ts +8 -0
- package/dist/identity/principal.js +8 -0
- package/dist/identity/settings.d.ts +3 -1
- package/dist/identity/settings.js +6 -4
- package/dist/inbox/inbox.schemas.d.ts +8 -0
- package/dist/inbox/inbox.schemas.js +11 -0
- package/dist/inbox/platform-inbox.controller.d.ts +1 -1
- package/dist/inbox/platform-inbox.controller.js +2 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/lifecycle/kernel-lifecycle.js +4 -0
- package/dist/models/enablement.d.ts +2 -0
- package/dist/models/enablement.js +1 -0
- package/dist/models/org-models.schemas.d.ts +8 -0
- package/dist/models/org-models.schemas.js +22 -0
- package/dist/models/prices.d.ts +3 -1
- package/dist/models/prices.js +7 -5
- package/dist/models/purposes.d.ts +12 -2
- package/dist/models/purposes.js +22 -8
- package/dist/orgs/orgs.controller.d.ts +2 -5
- package/dist/orgs/orgs.controller.js +3 -6
- package/dist/orgs/orgs.schemas.d.ts +8 -0
- package/dist/orgs/orgs.schemas.js +16 -0
- package/dist/orgs/orgs.service.d.ts +3 -1
- package/dist/orgs/orgs.service.js +6 -4
- package/dist/orgs/settings.service.d.ts +9 -1
- package/dist/orgs/settings.service.js +20 -5
- package/dist/overrides.d.ts +4 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/errors.d.ts +21 -4
- package/dist/platform/errors.js +45 -6
- package/dist/platform/exclusions.d.ts +2 -3
- package/dist/platform/exclusions.js +68 -849
- package/dist/platform/method.js +1 -1
- package/dist/platform/operation.d.ts +82 -5
- package/dist/platform/operation.js +62 -7
- package/dist/platform/ops/accounts.ops.d.ts +32 -0
- package/dist/platform/ops/accounts.ops.js +291 -0
- package/dist/platform/ops/admin.ops.d.ts +36 -0
- package/dist/platform/ops/admin.ops.js +389 -0
- package/dist/platform/ops/agent-skills.ops.d.ts +24 -0
- package/dist/platform/ops/agent-skills.ops.js +226 -0
- package/dist/platform/ops/agent-templates.ops.d.ts +4 -1
- package/dist/platform/ops/agent-templates.ops.js +194 -14
- package/dist/platform/ops/agents.ops.d.ts +19 -1
- package/dist/platform/ops/agents.ops.js +481 -58
- package/dist/platform/ops/approvals.ops.d.ts +30 -0
- package/dist/platform/ops/approvals.ops.js +116 -0
- package/dist/platform/ops/background.ops.d.ts +14 -0
- package/dist/platform/ops/background.ops.js +134 -0
- package/dist/platform/ops/catalog-admin.ops.d.ts +74 -0
- package/dist/platform/ops/catalog-admin.ops.js +587 -0
- package/dist/platform/ops/channels.ops.d.ts +16 -0
- package/dist/platform/ops/channels.ops.js +195 -0
- package/dist/platform/ops/components.ops.d.ts +46 -0
- package/dist/platform/ops/components.ops.js +853 -0
- package/dist/platform/ops/connectors.ops.d.ts +4 -2
- package/dist/platform/ops/connectors.ops.js +68 -6
- package/dist/platform/ops/conversations.ops.d.ts +30 -0
- package/dist/platform/ops/conversations.ops.js +470 -0
- package/dist/platform/ops/environments.ops.d.ts +26 -0
- package/dist/platform/ops/environments.ops.js +289 -0
- package/dist/platform/ops/errors.d.ts +10 -5
- package/dist/platform/ops/errors.js +19 -11
- package/dist/platform/ops/evals.ops.d.ts +28 -0
- package/dist/platform/ops/evals.ops.js +783 -0
- package/dist/platform/ops/extensions-admin.ops.d.ts +25 -0
- package/dist/platform/ops/extensions-admin.ops.js +344 -0
- package/dist/platform/ops/extensions.ops.d.ts +7 -1
- package/dist/platform/ops/extensions.ops.js +248 -17
- package/dist/platform/ops/inbox.ops.d.ts +25 -0
- package/dist/platform/ops/inbox.ops.js +378 -0
- package/dist/platform/ops/marketplaces.ops.d.ts +7 -1
- package/dist/platform/ops/marketplaces.ops.js +326 -17
- package/dist/platform/ops/models.ops.d.ts +25 -0
- package/dist/platform/ops/models.ops.js +232 -0
- package/dist/platform/ops/org-plugins.ops.d.ts +29 -0
- package/dist/platform/ops/org-plugins.ops.js +329 -0
- package/dist/platform/ops/org-settings.ops.d.ts +16 -0
- package/dist/platform/ops/org-settings.ops.js +102 -0
- package/dist/platform/ops/orgs.ops.d.ts +28 -0
- package/dist/platform/ops/orgs.ops.js +295 -0
- package/dist/platform/ops/other-orgs.d.ts +20 -0
- package/dist/platform/ops/other-orgs.js +72 -0
- package/dist/platform/ops/platform.ops.d.ts +4 -4
- package/dist/platform/ops/platform.ops.js +23 -24
- package/dist/platform/ops/plugins.ops.d.ts +10 -8
- package/dist/platform/ops/plugins.ops.js +176 -77
- package/dist/platform/ops/previewers.ops.d.ts +14 -0
- package/dist/platform/ops/previewers.ops.js +109 -0
- package/dist/platform/ops/requests.ops.js +57 -19
- package/dist/platform/ops/skills.ops.d.ts +5 -1
- package/dist/platform/ops/skills.ops.js +129 -11
- package/dist/platform/ops/unavailable.d.ts +23 -0
- package/dist/platform/ops/unavailable.js +111 -0
- package/dist/platform/parity.d.ts +33 -0
- package/dist/platform/parity.js +272 -0
- package/dist/platform/platform-ops.module.d.ts +32 -0
- package/dist/platform/platform-ops.module.js +78 -11
- package/dist/platform/route-schemas.d.ts +18 -0
- package/dist/platform/route-schemas.js +60 -0
- package/dist/roles/worker.module.js +2 -0
- package/dist/runtime/context/build.d.ts +2 -0
- package/dist/runtime/context/build.js +28 -2
- package/dist/runtime/context/compaction.js +10 -1
- package/dist/runtime/context/run-context.d.ts +65 -0
- package/dist/runtime/context/run-context.js +215 -0
- package/dist/runtime/model-call.js +2 -1
- package/dist/runtime/providers.js +8 -1
- package/dist/runtime/run-context.service.d.ts +65 -0
- package/dist/runtime/run-context.service.js +220 -0
- package/dist/runtime/run-worker.d.ts +5 -0
- package/dist/runtime/run-worker.js +31 -6
- package/dist/runtime/runtime.module.js +2 -0
- package/dist/runtime/subagents/definition.d.ts +15 -15
- package/dist/runtime/subagents/definition.js +46 -115
- package/dist/skills/changes.controller.js +2 -1
- package/dist/skills/changes.service.d.ts +1 -1
- package/dist/skills/changes.service.js +4 -4
- package/dist/skills/invocable.controller.d.ts +1 -1
- package/dist/skills/invocable.controller.js +2 -2
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/invoke.js +8 -1
- package/dist/skills/skills.controller.d.ts +4 -13
- package/dist/skills/skills.controller.js +8 -24
- package/dist/skills/skills.schemas.d.ts +48 -0
- package/dist/skills/skills.schemas.js +61 -0
- package/dist/skills/skills.service.d.ts +11 -1
- package/dist/skills/skills.service.js +37 -14
- package/dist/time-zone.d.ts +5 -0
- package/dist/time-zone.js +19 -0
- package/dist/tools/approvals.controller.js +2 -3
- package/dist/tools/approvals.schemas.d.ts +21 -0
- package/dist/tools/approvals.schemas.js +15 -0
- package/dist/tools/builtin-names.d.ts +10 -8
- package/dist/tools/builtin-names.js +16 -11
- package/dist/tools/builtins/builtin-tools.d.ts +3 -1
- package/dist/tools/builtins/builtin-tools.js +15 -3
- package/dist/tools/builtins/current-time.d.ts +26 -0
- package/dist/tools/builtins/current-time.js +99 -0
- package/dist/tools/builtins/platform.d.ts +19 -2
- package/dist/tools/builtins/platform.js +105 -16
- package/dist/tools/connectors.controller.d.ts +1 -2
- package/dist/tools/connectors.controller.js +1 -4
- package/dist/tools/connectors.schemas.d.ts +12 -0
- package/dist/tools/connectors.schemas.js +19 -0
- package/dist/tools/connectors.service.d.ts +5 -2
- package/dist/tools/connectors.service.js +7 -5
- package/dist/tools/org-tool-switches.d.ts +16 -2
- package/dist/tools/org-tool-switches.js +26 -9
- package/dist/tools/pipeline.js +3 -1
- package/dist/tools/registry.d.ts +48 -51
- package/dist/tools/registry.js +222 -233
- package/package.json +3 -3
- package/src/migrations/0090_initial_passwords.sql +27 -0
- package/src/migrations/0091_account_time_zone.sql +4 -0
- package/src/migrations/0092_tools_default_on.sql +27 -0
- package/dist/platform/redact-orgs.d.ts +0 -7
- package/dist/platform/redact-orgs.js +0 -52
package/dist/extensions/host.js
CHANGED
|
@@ -311,6 +311,11 @@ let ExtensionHost = class ExtensionHost {
|
|
|
311
311
|
* 安装记录不在这里:每次 converge 都按 PG 期望状态重新判定。
|
|
312
312
|
*/
|
|
313
313
|
pendingActivations = new Map();
|
|
314
|
+
/**
|
|
315
|
+
* The load parameters of each config-declared extension that loaded: when its deployment config
|
|
316
|
+
* is cleared and the rest is invalid it goes back to {@link pendingActivations} (pending config).
|
|
317
|
+
*/
|
|
318
|
+
declaredActivations = new Map();
|
|
314
319
|
/** 安装记录准备好的候选(按插件名;sha 变了就作废)。 */
|
|
315
320
|
prepared = new Map();
|
|
316
321
|
/** 闸门放行后导入过的安装记录真实清单(按插件名;部署配置 schema 用,含待配置而未加载的)。 */
|
|
@@ -1294,6 +1299,8 @@ let ExtensionHost = class ExtensionHost {
|
|
|
1294
1299
|
}
|
|
1295
1300
|
// 升级:替换加载参数(之后的依赖方连锁与 reload 用新版本),先登记新实例、再撤销旧实例
|
|
1296
1301
|
this.specs.set(name, spec);
|
|
1302
|
+
if (a.source !== "install")
|
|
1303
|
+
this.declaredActivations.set(name, a);
|
|
1297
1304
|
this.unloadedForDependency.delete(name);
|
|
1298
1305
|
const old = this.registry.activate(inst);
|
|
1299
1306
|
this.loggedFailures.delete(name);
|
|
@@ -1724,7 +1731,9 @@ let ExtensionHost = class ExtensionHost {
|
|
|
1724
1731
|
/**
|
|
1725
1732
|
* 部署配置保存后(`kernel.plugin-deployment-config`)本副本重读并校验(C10;与 unload / reload 串行):
|
|
1726
1733
|
* - 插件没在本副本生效 → `not_loaded`(配置声明的刷新后台的合法性判定;待配置的由 reconcile 加载);
|
|
1727
|
-
* - 不合法 → `invalid
|
|
1734
|
+
* - 不合法 → `invalid`:保持当前值,告警;但 PG 里已没有保存的配置(超级管理员清空了,`DELETE
|
|
1735
|
+
* /api/v1/admin/extensions/:name/config`)、剩下的来源(启动配置、默认值)又不够时 → `unloaded`:卸下实例、
|
|
1736
|
+
* 记为待配置(pending / config),依赖方连锁;重新配好后由 reconcile 加载;
|
|
1728
1737
|
* - 没变 → `unchanged`;只改了 hot 字段 → `hot`:就地替换,插件经 `ctx.config` 读到新值,不重新 setup;
|
|
1729
1738
|
* - 改了非 hot 字段 → `reload`:用新配置重新加载实例(先新后旧,排空在途调用),依赖方连锁;
|
|
1730
1739
|
* 重新加载失败旧实例保留(`reload_failed`,记 error)。
|
|
@@ -1763,6 +1772,10 @@ let ExtensionHost = class ExtensionHost {
|
|
|
1763
1772
|
}
|
|
1764
1773
|
const r = parseLoadGate(schema, raw);
|
|
1765
1774
|
if (!r.success) {
|
|
1775
|
+
if (await this.clearedInPg(plugin)) {
|
|
1776
|
+
await this.unloadForMissingConfig(inst, toIssues(r.error));
|
|
1777
|
+
return "unloaded";
|
|
1778
|
+
}
|
|
1766
1779
|
this.log.warn("plugin deployment config changed but is invalid on this replica; keeping the current config", {
|
|
1767
1780
|
plugin,
|
|
1768
1781
|
detail: toIssues(r.error)
|
|
@@ -1812,6 +1825,95 @@ let ExtensionHost = class ExtensionHost {
|
|
|
1812
1825
|
return "reload";
|
|
1813
1826
|
});
|
|
1814
1827
|
}
|
|
1828
|
+
/** PG holds no deployment config for the plugin (it was cleared); a read error counts as no. */
|
|
1829
|
+
async clearedInPg(plugin) {
|
|
1830
|
+
try {
|
|
1831
|
+
return !(await this.installs.hasStoredDeploymentConfig(plugin));
|
|
1832
|
+
}
|
|
1833
|
+
catch (err) {
|
|
1834
|
+
this.log.warn("cannot read whether the plugin has a stored deployment config", {
|
|
1835
|
+
plugin,
|
|
1836
|
+
err,
|
|
1837
|
+
});
|
|
1838
|
+
return false;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Unloads an extension whose deployment config was cleared and is now missing or invalid (in
|
|
1843
|
+
* the host's serial queue): drains it, records pending / config (a config-declared one goes back
|
|
1844
|
+
* to {@link pendingActivations}; an install record is decided again on every converge) and
|
|
1845
|
+
* cascades to its dependents. It loads again without a restart once it is configured.
|
|
1846
|
+
*/
|
|
1847
|
+
async unloadForMissingConfig(inst, issues) {
|
|
1848
|
+
const plugin = inst.name;
|
|
1849
|
+
const drainMs = this.drainMs();
|
|
1850
|
+
const gone = await this.doUnload(plugin, drainMs, null);
|
|
1851
|
+
if (!gone)
|
|
1852
|
+
return;
|
|
1853
|
+
await this.markPendingConfig({ plugin, version: gone.version, sha256: gone.sha256, source: gone.source }, issues, "extension unloaded: its deployment config was cleared and what remains is missing or invalid (set it in the admin console or the startup config; it loads without a restart)");
|
|
1854
|
+
await this.cascade(gone.manifest.provides, drainMs);
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Records an extension that is not loaded as pending configuration: a config-declared one goes
|
|
1858
|
+
* back to {@link pendingActivations} (a later save loads it); an install record is decided again
|
|
1859
|
+
* on every converge.
|
|
1860
|
+
*/
|
|
1861
|
+
async markPendingConfig(p, issues, message) {
|
|
1862
|
+
const detail = issues.map((i) => `${i.path || "<root>"}: ${i.message}`).join("; ");
|
|
1863
|
+
const declared = this.declaredActivations.get(p.plugin);
|
|
1864
|
+
if (p.source !== "install") {
|
|
1865
|
+
this.configValidity.set(p.plugin, false);
|
|
1866
|
+
if (declared)
|
|
1867
|
+
this.pendingActivations.set(p.plugin, { ...declared, fatal: false });
|
|
1868
|
+
}
|
|
1869
|
+
this.configWarned.set(p.plugin, detail);
|
|
1870
|
+
this.log.warn(message, { plugin: p.plugin, source: p.source, detail });
|
|
1871
|
+
await this.writeStatus({ ...p, state: "pending", reason: "config", detail });
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Before an extension unloaded for a dependency comes back ({@link cascade}): re-reads its
|
|
1875
|
+
* deployment config, which may have been saved or cleared while it was down (applyDeploymentConfig
|
|
1876
|
+
* only records validity then), and refreshes its load parameters, so it never returns on a stale
|
|
1877
|
+
* config or revoked secrets. Returns false when the config is now missing or invalid and PG holds
|
|
1878
|
+
* none: the extension is left pending configuration instead. A read error, or an invalid config
|
|
1879
|
+
* that is still stored, keeps the previous config (as for a loaded extension).
|
|
1880
|
+
*/
|
|
1881
|
+
async refreshConfigForRestore(name, spec) {
|
|
1882
|
+
const schema = spec.manifest.config?.deployment;
|
|
1883
|
+
if (!schema)
|
|
1884
|
+
return true;
|
|
1885
|
+
let raw;
|
|
1886
|
+
try {
|
|
1887
|
+
raw = await this.rawConfigOf(name, schema);
|
|
1888
|
+
}
|
|
1889
|
+
catch (err) {
|
|
1890
|
+
this.log.error("cannot read the stored deployment config of plugin; restoring it on its previous config", { plugin: name, err });
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
const r = parseLoadGate(schema, raw);
|
|
1894
|
+
if (!r.success) {
|
|
1895
|
+
if (!(await this.clearedInPg(name)))
|
|
1896
|
+
return true;
|
|
1897
|
+
await this.markPendingConfig({
|
|
1898
|
+
plugin: name,
|
|
1899
|
+
version: spec.manifest.version,
|
|
1900
|
+
sha256: spec.sha256,
|
|
1901
|
+
source: spec.source,
|
|
1902
|
+
}, toIssues(r.error), "extension not restored after its dependency came back: its deployment config was cleared and what remains is missing or invalid (set it in the admin console or the startup config; it loads without a restart)");
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
if (spec.source !== "install")
|
|
1906
|
+
this.configValidity.set(name, true);
|
|
1907
|
+
if (changedPaths(schema, spec.config, r.data).length > 0) {
|
|
1908
|
+
this.reportSecrets(schema, r.data);
|
|
1909
|
+
this.specs.set(name, {
|
|
1910
|
+
...spec,
|
|
1911
|
+
config: r.data,
|
|
1912
|
+
configHash: deploymentConfigHash(schema, r.data),
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
return true;
|
|
1916
|
+
}
|
|
1815
1917
|
/** 本次未加载、等待批准权限的插件(含因 `requires` 只能由它们满足而未加载的插件),按依赖拓扑序。 */
|
|
1816
1918
|
pending() {
|
|
1817
1919
|
return structuredClone(this.pendingPlugins);
|
|
@@ -2089,6 +2191,10 @@ let ExtensionHost = class ExtensionHost {
|
|
|
2089
2191
|
const available = providedNow();
|
|
2090
2192
|
if (!(spec.manifest.requires ?? []).every((c) => available.has(c)))
|
|
2091
2193
|
continue;
|
|
2194
|
+
if (!(await this.refreshConfigForRestore(name, spec))) {
|
|
2195
|
+
this.unloadedForDependency.delete(name);
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2092
2198
|
try {
|
|
2093
2199
|
await this.doReload(name, drainMs);
|
|
2094
2200
|
progress = true;
|
|
@@ -107,6 +107,11 @@ export declare class ExtensionInstalls {
|
|
|
107
107
|
* the fields of `schema` when it is given.
|
|
108
108
|
*/
|
|
109
109
|
effectiveDeploymentConfig(plugin: string, fileValue: unknown, schema?: core.$ZodType): Promise<unknown>;
|
|
110
|
+
/**
|
|
111
|
+
* Whether PG holds a deployment config row for the extension (a super admin saved one and has
|
|
112
|
+
* not cleared it since; `DELETE /api/v1/admin/extensions/:name/config` removes it).
|
|
113
|
+
*/
|
|
114
|
+
hasStoredDeploymentConfig(plugin: string): Promise<boolean>;
|
|
110
115
|
/** Every install record's extension name (any desired state). */
|
|
111
116
|
installedNames(): Promise<string[]>;
|
|
112
117
|
/**
|
|
@@ -76,6 +76,18 @@ let ExtensionInstalls = class ExtensionInstalls {
|
|
|
76
76
|
const fields = schema ? configFields(schema).map((f) => f.path) : undefined;
|
|
77
77
|
return mergeDeploymentConfig(stored, fileValue, fields);
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether PG holds a deployment config row for the extension (a super admin saved one and has
|
|
81
|
+
* not cleared it since; `DELETE /api/v1/admin/extensions/:name/config` removes it).
|
|
82
|
+
*/
|
|
83
|
+
async hasStoredDeploymentConfig(plugin) {
|
|
84
|
+
const row = await this.db
|
|
85
|
+
.selectFrom("extension_deployment_configs")
|
|
86
|
+
.select("plugin")
|
|
87
|
+
.where("plugin", "=", plugin)
|
|
88
|
+
.executeTakeFirst();
|
|
89
|
+
return row !== undefined;
|
|
90
|
+
}
|
|
79
91
|
/** Every install record's extension name (any desired state). */
|
|
80
92
|
async installedNames() {
|
|
81
93
|
const rows = await this.db.selectFrom("extension_installs").select("name").execute();
|
|
@@ -11,7 +11,8 @@ import { type JsonSchema } from "./config-json-schema.js";
|
|
|
11
11
|
import { EffectiveConfigs } from "./effective-config.js";
|
|
12
12
|
import { ExtensionHost } from "./host.js";
|
|
13
13
|
import { ExtensionInstalls } from "./installs.js";
|
|
14
|
-
import { type PermissionGroup } from "./permission-view.js";
|
|
14
|
+
import { type DeploymentExtensionStatus, type PermissionGroup } from "./permission-view.js";
|
|
15
|
+
import { ExtensionAvailability, type ExtensionUnavailableReason } from "./unavailable.js";
|
|
15
16
|
export { type ConfigIssue, toIssues };
|
|
16
17
|
/** 组织视角下一个已加载插件的状态;`config` 永远不含密钥字段,密钥只回显「是否已设置」。 */
|
|
17
18
|
export interface OrgExtensionView {
|
|
@@ -35,6 +36,16 @@ export interface OrgExtensionView {
|
|
|
35
36
|
* 组织的启用开关保留不变(不能修改),其余字段为空。
|
|
36
37
|
*/
|
|
37
38
|
available: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The extension's status on the deployment, as the admin console derives it
|
|
41
|
+
* (`deriveExtensionStatus`): `enabled` when it is loaded here; otherwise why not.
|
|
42
|
+
*/
|
|
43
|
+
status: DeploymentExtensionStatus;
|
|
44
|
+
/**
|
|
45
|
+
* Only when unavailable: why it cannot be used (spec §14.5 item 4), e.g.
|
|
46
|
+
* `needs_deployment_config`. State only, never a config value or field name.
|
|
47
|
+
*/
|
|
48
|
+
reason?: ExtensionUnavailableReason;
|
|
38
49
|
/**
|
|
39
50
|
* A required bootstrap plugin: enabled in every org (`enabled` is always true) and cannot be
|
|
40
51
|
* disabled here (`extension.required`); its org config, when it has one, can still be set.
|
|
@@ -121,6 +132,8 @@ export declare class OrgExtensionConfigs {
|
|
|
121
132
|
private readonly log;
|
|
122
133
|
/** The effective deployment config of each org (spec A §6): read for `needsConfig`. */
|
|
123
134
|
private readonly effective;
|
|
135
|
+
/** Why an installed extension is not loaded (`status` / `reason` of the unavailable ones). */
|
|
136
|
+
private readonly availability;
|
|
124
137
|
/** Test seam only: a deterministic interleaving point after the config rows are read, before the secrets. */
|
|
125
138
|
afterConfigRowRead: (() => Promise<void>) | undefined;
|
|
126
139
|
constructor(kdb: KernelDb, txHost: TransactionHost, keyring: Keyring, host: ExtensionHost,
|
|
@@ -133,7 +146,9 @@ export declare class OrgExtensionConfigs {
|
|
|
133
146
|
/** Secret values handled (written or decrypted) are masked in the logs after the commit. */
|
|
134
147
|
log: KernelLogger,
|
|
135
148
|
/** The effective deployment config of each org (spec A §6): read for `needsConfig`. */
|
|
136
|
-
effective: EffectiveConfigs
|
|
149
|
+
effective: EffectiveConfigs,
|
|
150
|
+
/** Why an installed extension is not loaded (`status` / `reason` of the unavailable ones). */
|
|
151
|
+
availability: ExtensionAvailability);
|
|
137
152
|
/** The kernel-schema handle of the current transaction. */
|
|
138
153
|
private get db();
|
|
139
154
|
/** 把一份组织配置里的密钥值登记到日志打码(无密钥时无操作);调用方须在事务提交后调用。 */
|
|
@@ -29,8 +29,9 @@ import { enabledExtensionsQuery, requiredExtensions } from "./enabled.js";
|
|
|
29
29
|
import { ExtensionHost } from "./host.js";
|
|
30
30
|
import { ExtensionInstalls } from "./installs.js";
|
|
31
31
|
import { fieldLevels } from "./org-levels.js";
|
|
32
|
-
import { permissionGroups } from "./permission-view.js";
|
|
32
|
+
import { permissionGroups, } from "./permission-view.js";
|
|
33
33
|
import { itemLocked, pluginItemsOf } from "./plugin-items.js";
|
|
34
|
+
import { ExtensionAvailability } from "./unavailable.js";
|
|
34
35
|
export { toIssues };
|
|
35
36
|
/** 内核自己的 secrets 命名空间(plugin-sdk 把 `kernel` 列为保留插件名,插件不会与之相撞)。 */
|
|
36
37
|
const SECRETS_NAMESPACE = "kernel";
|
|
@@ -104,6 +105,7 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
104
105
|
events;
|
|
105
106
|
log;
|
|
106
107
|
effective;
|
|
108
|
+
availability;
|
|
107
109
|
/** Test seam only: a deterministic interleaving point after the config rows are read, before the secrets. */
|
|
108
110
|
afterConfigRowRead;
|
|
109
111
|
constructor(kdb, txHost, keyring, host,
|
|
@@ -116,7 +118,9 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
116
118
|
/** Secret values handled (written or decrypted) are masked in the logs after the commit. */
|
|
117
119
|
log,
|
|
118
120
|
/** The effective deployment config of each org (spec A §6): read for `needsConfig`. */
|
|
119
|
-
effective
|
|
121
|
+
effective,
|
|
122
|
+
/** Why an installed extension is not loaded (`status` / `reason` of the unavailable ones). */
|
|
123
|
+
availability) {
|
|
120
124
|
this.kdb = kdb;
|
|
121
125
|
this.txHost = txHost;
|
|
122
126
|
this.keyring = keyring;
|
|
@@ -126,6 +130,7 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
126
130
|
this.events = events;
|
|
127
131
|
this.log = log;
|
|
128
132
|
this.effective = effective;
|
|
133
|
+
this.availability = availability;
|
|
129
134
|
}
|
|
130
135
|
/** The kernel-schema handle of the current transaction. */
|
|
131
136
|
get db() {
|
|
@@ -154,7 +159,16 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
154
159
|
name: r.name,
|
|
155
160
|
version: r.version,
|
|
156
161
|
}));
|
|
157
|
-
|
|
162
|
+
const views = await this.listSnapshot(orgId, installed);
|
|
163
|
+
const why = await this.availability.unavailable(views.filter((v) => !v.available).map((v) => v.plugin));
|
|
164
|
+
for (const v of views) {
|
|
165
|
+
const u = why.get(v.plugin);
|
|
166
|
+
if (v.available || u === undefined || u.status === null)
|
|
167
|
+
continue;
|
|
168
|
+
v.status = u.status;
|
|
169
|
+
v.reason = u.reason;
|
|
170
|
+
}
|
|
171
|
+
return this.withLayers(orgId, views);
|
|
158
172
|
}
|
|
159
173
|
/**
|
|
160
174
|
* Fills `overrideState` and the platform half of `needsConfig` (spec A §6) for the views of
|
|
@@ -219,6 +233,9 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
219
233
|
permissions: [],
|
|
220
234
|
orgConfigSchema: null,
|
|
221
235
|
available: false,
|
|
236
|
+
// filled in by `list` from the replicas' reports
|
|
237
|
+
status: "converging",
|
|
238
|
+
reason: "loading",
|
|
222
239
|
required: required.has(i.name),
|
|
223
240
|
...scopeFields(items.get(i.name), required.has(i.name)),
|
|
224
241
|
overrideState: "none",
|
|
@@ -619,6 +636,7 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
619
636
|
permissions: permissionGroups(this.permissionsOf(m.name)),
|
|
620
637
|
orgConfigSchema: schema ? this.formSchema(m.name, m.version, schema) : null,
|
|
621
638
|
available: true,
|
|
639
|
+
status: "enabled",
|
|
622
640
|
required,
|
|
623
641
|
...scopeFields(items, required),
|
|
624
642
|
overrideState: [...fieldLevels(m.config?.deployment).values()].includes("override")
|
|
@@ -682,7 +700,8 @@ OrgExtensionConfigs = __decorate([
|
|
|
682
700
|
__param(5, Inject(ExtensionInstalls)),
|
|
683
701
|
__param(6, Inject(KernelEvents)),
|
|
684
702
|
__param(7, Inject(KERNEL_LOGGER)),
|
|
685
|
-
__param(8, Inject(EffectiveConfigs))
|
|
703
|
+
__param(8, Inject(EffectiveConfigs)),
|
|
704
|
+
__param(9, Inject(ExtensionAvailability))
|
|
686
705
|
], OrgExtensionConfigs);
|
|
687
706
|
export { OrgExtensionConfigs };
|
|
688
707
|
/**
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
1
|
+
import type { z } from "zod";
|
|
2
2
|
import { type Principal } from "../identity/principal.js";
|
|
3
3
|
import { type OrgContext } from "../orgs/org.guard.js";
|
|
4
4
|
import { ExtensionPointDefaults, type OrgPointDefaultView } from "./point-defaults.js";
|
|
5
|
-
|
|
6
|
-
point: z.ZodString;
|
|
7
|
-
}, z.core.$strip>;
|
|
8
|
-
declare const DefaultBody: z.ZodObject<{
|
|
9
|
-
extension: z.ZodString;
|
|
10
|
-
}, z.core.$strict>;
|
|
5
|
+
import { ExtensionPointDefaultBody as DefaultBody, ExtensionPointParams as PointParams } from "./point-defaults.schemas.js";
|
|
11
6
|
/**
|
|
12
7
|
* The org default provider of `org-default` extension points (web tools spec §6):
|
|
13
8
|
* - `GET /api/v1/orgs/:orgId/extension-points` (`org.extensions.manage`) → `{ points }`: each
|
|
@@ -31,4 +26,3 @@ export declare class ExtensionPointDefaultsController {
|
|
|
31
26
|
}>;
|
|
32
27
|
setDefault(principal: Principal, org: OrgContext, { point }: z.infer<typeof PointParams>, body: z.infer<typeof DefaultBody>): Promise<void>;
|
|
33
28
|
}
|
|
34
|
-
export {};
|
|
@@ -8,14 +8,12 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Controller, Get, HttpCode, Inject, Put } from "@nestjs/common";
|
|
11
|
-
import { z } from "zod";
|
|
12
11
|
import { CurrentPrincipal, requireUser } from "../identity/principal.js";
|
|
13
12
|
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
14
13
|
import { ZBody, ZParam } from "../infra/validation/zod-params.js";
|
|
15
14
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
16
15
|
import { ExtensionPointDefaults } from "./point-defaults.js";
|
|
17
|
-
|
|
18
|
-
const DefaultBody = z.object({ extension: z.string().min(1).max(214) }).strict();
|
|
16
|
+
import { ExtensionPointDefaultBody as DefaultBody, ExtensionPointParams as PointParams, } from "./point-defaults.schemas.js";
|
|
19
17
|
class ExtensionPointParamsDto extends createZodDto(PointParams, "ExtensionPointParams") {
|
|
20
18
|
}
|
|
21
19
|
class ExtensionPointDefaultBodyDto extends createZodDto(DefaultBody, "ExtensionPointDefaultBody") {
|
|
@@ -62,7 +62,9 @@ export declare class ExtensionPointDefaults {
|
|
|
62
62
|
* the point is an `org-default` one and the extension provides it (loaded here) and is enabled
|
|
63
63
|
* in the org. Audited as `extension_point.default_set`.
|
|
64
64
|
*/
|
|
65
|
-
setDefault(orgId: string, point: string, extension: string, actorId: string
|
|
65
|
+
setDefault(orgId: string, point: string, extension: string, actorId: string,
|
|
66
|
+
/** Merged into the audit detail (a write made through the Platform tool). */
|
|
67
|
+
auditDetail?: Record<string, unknown>): Promise<void>;
|
|
66
68
|
/** The candidates of `point` in `orgId`, by extension name. */
|
|
67
69
|
private candidates;
|
|
68
70
|
private choose;
|
|
@@ -100,7 +100,9 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
100
100
|
* the point is an `org-default` one and the extension provides it (loaded here) and is enabled
|
|
101
101
|
* in the org. Audited as `extension_point.default_set`.
|
|
102
102
|
*/
|
|
103
|
-
async setDefault(orgId, point, extension, actorId
|
|
103
|
+
async setDefault(orgId, point, extension, actorId,
|
|
104
|
+
/** Merged into the audit detail (a write made through the Platform tool). */
|
|
105
|
+
auditDetail) {
|
|
104
106
|
const db = this.kdb.db;
|
|
105
107
|
if (!hasSelection(point)) {
|
|
106
108
|
throw new ApiError("validation_failed", `${point} is not an extension point with an organization default`);
|
|
@@ -123,7 +125,7 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
123
125
|
orgId,
|
|
124
126
|
action: "extension_point.default_set",
|
|
125
127
|
target: point,
|
|
126
|
-
detail: { point, extension },
|
|
128
|
+
detail: { point, extension, ...auditDetail },
|
|
127
129
|
});
|
|
128
130
|
}
|
|
129
131
|
/** The candidates of `point` in `orgId`, by extension name. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { RouteSchemas } from "../platform/route-schemas.js";
|
|
3
|
+
export declare const ExtensionPointParams: z.ZodObject<{
|
|
4
|
+
point: z.ZodString;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export declare const ExtensionPointDefaultBody: z.ZodObject<{
|
|
7
|
+
extension: z.ZodString;
|
|
8
|
+
}, z.core.$strict>;
|
|
9
|
+
export declare const EXTENSION_POINT_ROUTES: readonly RouteSchemas[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ExtensionPointParams = z.object({ point: z.string().min(1).max(100) });
|
|
3
|
+
export const ExtensionPointDefaultBody = z
|
|
4
|
+
.object({ extension: z.string().min(1).max(214) })
|
|
5
|
+
.strict();
|
|
6
|
+
export const EXTENSION_POINT_ROUTES = [
|
|
7
|
+
{ route: "GET /api/v1/orgs/:orgId/extension-points" },
|
|
8
|
+
{ route: "GET /api/v1/orgs/:orgId/extension-points/available" },
|
|
9
|
+
{
|
|
10
|
+
route: "PUT /api/v1/orgs/:orgId/extension-points/:point/default",
|
|
11
|
+
params: ExtensionPointParams,
|
|
12
|
+
body: ExtensionPointDefaultBody,
|
|
13
|
+
},
|
|
14
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
2
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
|
+
import { type DeploymentExtensionStatus } from "./permission-view.js";
|
|
4
|
+
import { ExtensionRegistry } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Why an extension cannot be used right now (Platform tool spec §14.2 problem 1, §14.5 item 4):
|
|
7
|
+
* - `not_deployed`: no install record on the platform (a catalog plugin whose extension was never
|
|
8
|
+
* installed);
|
|
9
|
+
* - `needs_deployment_config`: installed, its deployment config is missing or invalid;
|
|
10
|
+
* - `pending_approval`: its permissions wait for the super admin's approval;
|
|
11
|
+
* - `pending_migration` / `loading`: the platform is still bringing it up;
|
|
12
|
+
* - `dependency`: an extension point it requires has no loaded provider;
|
|
13
|
+
* - `failed`: it failed to load (or its package cannot be read);
|
|
14
|
+
* - `deployment_disabled`: the super admin disabled it on the platform.
|
|
15
|
+
* State only: never a config value or field name.
|
|
16
|
+
*/
|
|
17
|
+
export type ExtensionUnavailableReason = "not_deployed" | "needs_deployment_config" | "pending_approval" | "pending_migration" | "loading" | "dependency" | "failed" | "deployment_disabled";
|
|
18
|
+
/** The deployment status of an extension that is not loaded, and the reason it shows for it. */
|
|
19
|
+
export interface ExtensionUnavailability {
|
|
20
|
+
/** Null when it has no install record (`not_deployed`). */
|
|
21
|
+
status: DeploymentExtensionStatus | null;
|
|
22
|
+
reason: ExtensionUnavailableReason;
|
|
23
|
+
}
|
|
24
|
+
/** The reason an org or the Platform tool shows for a deployment status of a not-loaded extension. */
|
|
25
|
+
export declare function unavailableReason(status: DeploymentExtensionStatus): ExtensionUnavailableReason;
|
|
26
|
+
/**
|
|
27
|
+
* The state of extensions that are not loaded on this replica, derived like the admin console's
|
|
28
|
+
* status ({@link deriveExtensionStatus}) from the install record and the replicas' recent reports,
|
|
29
|
+
* without reading packages: a pending approval and a pending config come from the replicas' rows
|
|
30
|
+
* (`pending` with reason `approval` / `config`). Reads PG on every call (no in-process state).
|
|
31
|
+
*
|
|
32
|
+
* Its inputs differ slightly from the admin view's (`ExtensionAdmin`): there `pendingApproval`
|
|
33
|
+
* compares the package's permission set with the snapshot and `configValid` checks the deployment
|
|
34
|
+
* config, while here both come from what the replicas last reported. They agree once a replica
|
|
35
|
+
* has tried the current package; until then this view says `loading` where the admin view may
|
|
36
|
+
* already say `pending_approval` or `pending_config`.
|
|
37
|
+
*
|
|
38
|
+
* Which extensions count as loaded is this replica's registry: replica-local and transient. During
|
|
39
|
+
* a rollout, or right after an enable or upgrade, one replica may answer `loading` while another
|
|
40
|
+
* has it loaded; the next call (or the next reconcile) converges.
|
|
41
|
+
*/
|
|
42
|
+
export declare class ExtensionAvailability {
|
|
43
|
+
private readonly kdb;
|
|
44
|
+
private readonly config;
|
|
45
|
+
private readonly registry;
|
|
46
|
+
constructor(kdb: KernelDb, config: KernelConfigService, registry: ExtensionRegistry);
|
|
47
|
+
/** For each of `names` not loaded on this replica: its status and reason (loaded ones absent). */
|
|
48
|
+
unavailable(names: readonly string[]): Promise<Map<string, ExtensionUnavailability>>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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 { Inject, Injectable } from "@nestjs/common";
|
|
11
|
+
import { sql } from "kysely";
|
|
12
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
13
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
14
|
+
import { STALE_RECONCILE_PERIODS } from "./lifecycle-topics.js";
|
|
15
|
+
import { deriveExtensionStatus } from "./permission-view.js";
|
|
16
|
+
import { ExtensionRegistry } from "./registry.js";
|
|
17
|
+
/** The reason an org or the Platform tool shows for a deployment status of a not-loaded extension. */
|
|
18
|
+
export function unavailableReason(status) {
|
|
19
|
+
switch (status) {
|
|
20
|
+
case "pending_config":
|
|
21
|
+
return "needs_deployment_config";
|
|
22
|
+
case "pending_approval":
|
|
23
|
+
return "pending_approval";
|
|
24
|
+
case "pending_migration":
|
|
25
|
+
return "pending_migration";
|
|
26
|
+
case "dependency":
|
|
27
|
+
return "dependency";
|
|
28
|
+
case "failed":
|
|
29
|
+
case "legacy_format":
|
|
30
|
+
return "failed";
|
|
31
|
+
case "disabled":
|
|
32
|
+
return "deployment_disabled";
|
|
33
|
+
// loaded on another replica, or not yet on this one
|
|
34
|
+
case "converging":
|
|
35
|
+
case "enabled":
|
|
36
|
+
return "loading";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The state of extensions that are not loaded on this replica, derived like the admin console's
|
|
41
|
+
* status ({@link deriveExtensionStatus}) from the install record and the replicas' recent reports,
|
|
42
|
+
* without reading packages: a pending approval and a pending config come from the replicas' rows
|
|
43
|
+
* (`pending` with reason `approval` / `config`). Reads PG on every call (no in-process state).
|
|
44
|
+
*
|
|
45
|
+
* Its inputs differ slightly from the admin view's (`ExtensionAdmin`): there `pendingApproval`
|
|
46
|
+
* compares the package's permission set with the snapshot and `configValid` checks the deployment
|
|
47
|
+
* config, while here both come from what the replicas last reported. They agree once a replica
|
|
48
|
+
* has tried the current package; until then this view says `loading` where the admin view may
|
|
49
|
+
* already say `pending_approval` or `pending_config`.
|
|
50
|
+
*
|
|
51
|
+
* Which extensions count as loaded is this replica's registry: replica-local and transient. During
|
|
52
|
+
* a rollout, or right after an enable or upgrade, one replica may answer `loading` while another
|
|
53
|
+
* has it loaded; the next call (or the next reconcile) converges.
|
|
54
|
+
*/
|
|
55
|
+
let ExtensionAvailability = class ExtensionAvailability {
|
|
56
|
+
kdb;
|
|
57
|
+
config;
|
|
58
|
+
registry;
|
|
59
|
+
constructor(kdb, config, registry) {
|
|
60
|
+
this.kdb = kdb;
|
|
61
|
+
this.config = config;
|
|
62
|
+
this.registry = registry;
|
|
63
|
+
}
|
|
64
|
+
/** For each of `names` not loaded on this replica: its status and reason (loaded ones absent). */
|
|
65
|
+
async unavailable(names) {
|
|
66
|
+
const loaded = new Set(this.registry.manifests().map((m) => m.name));
|
|
67
|
+
const wanted = [...new Set(names)].filter((n) => !loaded.has(n));
|
|
68
|
+
const out = new Map();
|
|
69
|
+
if (wanted.length === 0)
|
|
70
|
+
return out;
|
|
71
|
+
const db = this.kdb.db;
|
|
72
|
+
const staleSeconds = STALE_RECONCILE_PERIODS * this.config.get().extensionHost.reconcileIntervalSeconds;
|
|
73
|
+
const [installs, replicas] = await Promise.all([
|
|
74
|
+
db
|
|
75
|
+
.selectFrom("extension_installs")
|
|
76
|
+
.select(["name", "sha256", "desired_state", "migrated_sha"])
|
|
77
|
+
.where("name", "in", wanted)
|
|
78
|
+
.execute(),
|
|
79
|
+
db
|
|
80
|
+
.selectFrom("extension_replica_status")
|
|
81
|
+
.select(["plugin", "state", "reason", "sha256"])
|
|
82
|
+
.where("plugin", "in", wanted)
|
|
83
|
+
.where("heartbeat_at", ">", sql `now() - make_interval(secs => ${staleSeconds})`)
|
|
84
|
+
.execute(),
|
|
85
|
+
]);
|
|
86
|
+
for (const name of wanted) {
|
|
87
|
+
const install = installs.find((i) => i.name === name);
|
|
88
|
+
if (install === undefined) {
|
|
89
|
+
out.set(name, { status: null, reason: "not_deployed" });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const rows = replicas.filter((r) => r.plugin === name);
|
|
93
|
+
const target = rows.filter((r) => r.sha256 === install.sha256);
|
|
94
|
+
const status = deriveExtensionStatus({
|
|
95
|
+
source: "install",
|
|
96
|
+
desired: { sha256: install.sha256, state: install.desired_state },
|
|
97
|
+
pendingApproval: target.some((r) => r.state === "pending" && r.reason === "approval"),
|
|
98
|
+
configValid: !target.some((r) => r.state === "pending" && r.reason === "config"),
|
|
99
|
+
migratedSha: install.migrated_sha,
|
|
100
|
+
replicas: rows,
|
|
101
|
+
});
|
|
102
|
+
out.set(name, { status, reason: unavailableReason(status) });
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
ExtensionAvailability = __decorate([
|
|
108
|
+
Injectable(),
|
|
109
|
+
__param(0, Inject(KernelDb)),
|
|
110
|
+
__param(1, Inject(KernelConfigService)),
|
|
111
|
+
__param(2, Inject(ExtensionRegistry))
|
|
112
|
+
], ExtensionAvailability);
|
|
113
|
+
export { ExtensionAvailability };
|
|
@@ -14,11 +14,10 @@ import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
|
14
14
|
import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
|
|
15
15
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
16
16
|
import { PreviewerRegistry } from "./previewers.js";
|
|
17
|
+
import { FilePreviewersOrderBody } from "./previewers.schemas.js";
|
|
17
18
|
const Id = z.string().min(1).max(100);
|
|
18
19
|
const PreviewQuery = z.object({ locale: z.enum(["zh", "en"]).default("en") });
|
|
19
|
-
const OrderBody =
|
|
20
|
-
items: z.array(z.object({ plugin: Id, id: Id, disabled: z.boolean().default(false) })).max(500),
|
|
21
|
-
});
|
|
20
|
+
const OrderBody = FilePreviewersOrderBody;
|
|
22
21
|
/** The previewer named in a preview path (the other path parameters are read as they are). */
|
|
23
22
|
const PreviewParams = z.object({ plugin: Id, previewerId: Id });
|
|
24
23
|
class FilePreviewQueryDto extends createZodDto(PreviewQuery, "FilePreviewQuery") {
|
|
@@ -61,7 +61,7 @@ export declare class PreviewerRegistry {
|
|
|
61
61
|
}[], actor?: {
|
|
62
62
|
accountId?: string;
|
|
63
63
|
tokenId?: string;
|
|
64
|
-
}): Promise<OrgPreviewerItem[]>;
|
|
64
|
+
}, auditDetail?: Record<string, unknown>): Promise<OrgPreviewerItem[]>;
|
|
65
65
|
/** Replaces the org's rows of the previewers this replica knows, in one transaction. */
|
|
66
66
|
private putIn;
|
|
67
67
|
/** Previewers that take this file (viewer must read the conversation), in the org's order. */
|
package/dist/files/previewers.js
CHANGED
|
@@ -141,7 +141,7 @@ let PreviewerRegistry = class PreviewerRegistry {
|
|
|
141
141
|
* extension that is disabled now (or not loaded here yet) keep their order and `disabled`.
|
|
142
142
|
* Audited as `org.file_previewers.updated`.
|
|
143
143
|
*/
|
|
144
|
-
async put(orgId, items, actor = {}) {
|
|
144
|
+
async put(orgId, items, actor = {}, auditDetail) {
|
|
145
145
|
const known = new Set((await this.ordered(orgId)).map((l) => `${l.plugin}/${l.previewer.id}`));
|
|
146
146
|
const seen = new Set();
|
|
147
147
|
for (const i of items) {
|
|
@@ -151,11 +151,11 @@ let PreviewerRegistry = class PreviewerRegistry {
|
|
|
151
151
|
}
|
|
152
152
|
seen.add(k);
|
|
153
153
|
}
|
|
154
|
-
await this.putIn(orgId, items, known, actor);
|
|
154
|
+
await this.putIn(orgId, items, known, actor, auditDetail);
|
|
155
155
|
return this.list(orgId);
|
|
156
156
|
}
|
|
157
157
|
/** Replaces the org's rows of the previewers this replica knows, in one transaction. */
|
|
158
|
-
async putIn(orgId, items, known, actor) {
|
|
158
|
+
async putIn(orgId, items, known, actor, auditDetail) {
|
|
159
159
|
if (known.size > 0) {
|
|
160
160
|
await this.d.db
|
|
161
161
|
.deleteFrom("file_previewers")
|
|
@@ -180,7 +180,7 @@ let PreviewerRegistry = class PreviewerRegistry {
|
|
|
180
180
|
...(actor.tokenId !== undefined && { actorTokenId: actor.tokenId }),
|
|
181
181
|
orgId,
|
|
182
182
|
action: "org.file_previewers.updated",
|
|
183
|
-
detail: { items },
|
|
183
|
+
detail: { items, ...auditDetail },
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
/** Previewers that take this file (viewer must read the conversation), in the org's order. */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { RouteSchemas } from "../platform/route-schemas.js";
|
|
3
|
+
export declare const FilePreviewersOrderBody: z.ZodObject<{
|
|
4
|
+
items: z.ZodArray<z.ZodObject<{
|
|
5
|
+
plugin: z.ZodString;
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
disabled: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
}, z.core.$strip>>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export declare const FILE_PREVIEWER_ROUTES: readonly RouteSchemas[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const Id = z.string().min(1).max(100);
|
|
3
|
+
export const FilePreviewersOrderBody = z.object({
|
|
4
|
+
items: z.array(z.object({ plugin: Id, id: Id, disabled: z.boolean().default(false) })).max(500),
|
|
5
|
+
});
|
|
6
|
+
export const FILE_PREVIEWER_ROUTES = [
|
|
7
|
+
{ route: "GET /api/v1/orgs/:orgId/file-previewers" },
|
|
8
|
+
{ route: "PUT /api/v1/orgs/:orgId/file-previewers", body: FilePreviewersOrderBody },
|
|
9
|
+
];
|