@capybara-hq/kernel 0.1.0-rc.13 → 0.1.0-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin.schemas.d.ts +5 -13
- package/dist/catalog/admin-catalog.controller.d.ts +39 -5
- package/dist/catalog/admin-catalog.controller.js +75 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
- package/dist/catalog/admin-catalog.schemas.js +7 -0
- package/dist/catalog/appearance.schemas.d.ts +10 -0
- package/dist/catalog/appearance.schemas.js +20 -0
- package/dist/catalog/appearance.service.d.ts +105 -0
- package/dist/catalog/appearance.service.js +306 -0
- package/dist/catalog/catalog-content.d.ts +9 -1
- package/dist/catalog/catalog-content.js +2 -1
- package/dist/catalog/catalog-files.d.ts +3 -2
- package/dist/catalog/catalog-files.js +8 -5
- package/dist/catalog/catalog-import.module.js +3 -0
- package/dist/catalog/catalog-items.d.ts +11 -0
- package/dist/catalog/catalog-items.js +21 -0
- package/dist/catalog/catalog-scope.service.d.ts +7 -0
- package/dist/catalog/catalog-scope.service.js +55 -15
- package/dist/catalog/catalog.service.js +10 -2
- package/dist/catalog/cli.js +3 -0
- package/dist/catalog/component-scope.service.d.ts +14 -1
- package/dist/catalog/component-scope.service.js +12 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.controller.d.ts +5 -4
- package/dist/catalog/org-catalog.controller.js +5 -4
- package/dist/catalog/org-catalog.schemas.d.ts +18 -2
- package/dist/catalog/org-catalog.schemas.js +16 -2
- package/dist/catalog/org-catalog.service.js +8 -2
- package/dist/catalog/org-extension.controller.d.ts +27 -7
- package/dist/catalog/org-extension.controller.js +48 -6
- package/dist/catalog/plugin-import.d.ts +28 -4
- package/dist/catalog/plugin-import.js +96 -20
- package/dist/catalog/plugin-package.d.ts +4 -2
- package/dist/catalog/plugin-package.js +66 -12
- package/dist/catalog/plugin-upgrade.d.ts +15 -1
- package/dist/catalog/plugin-upgrade.js +18 -5
- package/dist/channels/connections.controller.d.ts +1 -1
- package/dist/channels/connections.controller.js +1 -1
- package/dist/channels/connections.service.d.ts +22 -5
- package/dist/channels/connections.service.js +56 -10
- package/dist/channels/webhook.d.ts +21 -1
- package/dist/channels/webhook.js +39 -4
- package/dist/client/client-access.service.d.ts +101 -0
- package/dist/client/client-access.service.js +294 -0
- package/dist/client/client-builds.service.d.ts +109 -0
- package/dist/client/client-builds.service.js +229 -0
- package/dist/client/client-delivery.controller.d.ts +25 -0
- package/dist/client/client-delivery.controller.js +66 -0
- package/dist/client/client-files.d.ts +48 -0
- package/dist/client/client-files.js +136 -0
- package/dist/client/client-reports.cron.d.ts +13 -0
- package/dist/client/client-reports.cron.js +42 -0
- package/dist/client/client-reports.service.d.ts +30 -0
- package/dist/client/client-reports.service.js +102 -0
- package/dist/client/client.controller.d.ts +45 -0
- package/dist/client/client.controller.js +121 -0
- package/dist/client/client.module.d.ts +7 -0
- package/dist/client/client.module.js +26 -0
- package/dist/client/client.schemas.d.ts +55 -0
- package/dist/client/client.schemas.js +27 -0
- package/dist/ctx/create-ctx.d.ts +19 -4
- package/dist/ctx/create-ctx.js +51 -3
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +33 -0
- package/dist/extensions/admin-ops.d.ts +4 -1
- package/dist/extensions/admin-ops.js +22 -1
- package/dist/extensions/admin-view.d.ts +8 -0
- package/dist/extensions/admin-view.js +78 -0
- package/dist/extensions/blobs.d.ts +14 -1
- package/dist/extensions/blobs.js +48 -5
- package/dist/extensions/guide-places.d.ts +66 -0
- package/dist/extensions/guide-places.js +152 -0
- package/dist/extensions/host.d.ts +6 -1
- package/dist/extensions/host.js +23 -12
- package/dist/extensions/instance.d.ts +5 -1
- package/dist/extensions/instance.js +8 -0
- package/dist/extensions/ledger.d.ts +1 -1
- package/dist/extensions/package.d.ts +4 -3
- package/dist/extensions/package.js +5 -1
- package/dist/extensions/permission-set.d.ts +35 -5
- package/dist/extensions/permission-set.js +36 -5
- package/dist/extensions/permission-view.d.ts +38 -7
- package/dist/extensions/permission-view.js +27 -8
- package/dist/extensions/plugin-guides.d.ts +207 -54
- package/dist/extensions/plugin-guides.js +436 -165
- package/dist/extensions/sources.d.ts +6 -0
- package/dist/extensions/sources.js +2 -0
- package/dist/extensions/state.module.js +9 -1
- package/dist/http/api.module.js +16 -0
- package/dist/identity/settings.d.ts +41 -26
- package/dist/identity/settings.js +87 -4
- package/dist/infra/lock/lock.service.d.ts +8 -0
- package/dist/infra/lock/lock.service.js +27 -0
- package/dist/infra/throttler/named-throttlers.d.ts +8 -1
- package/dist/infra/throttler/named-throttlers.js +30 -1
- package/dist/infra/throttler/throttler.guard.js +1 -1
- package/dist/kernel.module.js +3 -0
- package/dist/orgs/settings.service.d.ts +2 -26
- package/dist/overrides.d.ts +3 -1
- package/dist/overrides.js +2 -0
- package/dist/platform/exclusions.js +37 -6
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin.ops.js +17 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +19 -1
- package/dist/platform/ops/catalog-admin.ops.js +85 -2
- package/dist/platform/ops/connectors.ops.js +2 -0
- package/dist/platform/ops/org-plugins.ops.d.ts +7 -1
- package/dist/platform/ops/org-plugins.ops.js +63 -4
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/setup.ops.d.ts +24 -0
- package/dist/platform/ops/setup.ops.js +212 -0
- package/dist/platform/ops/ui.ops.js +1 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +7 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/worker.module.js +4 -0
- package/dist/setup/kernel-checks.d.ts +91 -0
- package/dist/setup/kernel-checks.js +295 -0
- package/dist/setup/oauth-token-probe.d.ts +47 -0
- package/dist/setup/oauth-token-probe.js +108 -0
- package/dist/setup/redact-check.d.ts +18 -0
- package/dist/setup/redact-check.js +46 -0
- package/dist/setup/setup-check-registry.d.ts +86 -0
- package/dist/setup/setup-check-registry.js +75 -0
- package/dist/setup/setup-checks.controller.d.ts +28 -0
- package/dist/setup/setup-checks.controller.js +78 -0
- package/dist/setup/setup-checks.schemas.d.ts +38 -0
- package/dist/setup/setup-checks.schemas.js +43 -0
- package/dist/setup/setup-checks.service.d.ts +144 -0
- package/dist/setup/setup-checks.service.js +683 -0
- package/dist/setup/setup-guides.controller.d.ts +18 -0
- package/dist/setup/setup-guides.controller.js +48 -0
- package/dist/setup/setup-guides.schemas.d.ts +10 -0
- package/dist/setup/setup-guides.schemas.js +6 -0
- package/dist/setup/setup-guides.service.d.ts +126 -0
- package/dist/setup/setup-guides.service.js +315 -0
- package/dist/setup/setup.module.d.ts +8 -0
- package/dist/setup/setup.module.js +31 -0
- package/dist/themes/render-css.d.ts +2 -1
- package/dist/themes/render-css.js +5 -2
- package/dist/themes/theme-delivery.service.d.ts +4 -2
- package/dist/themes/theme-delivery.service.js +8 -3
- package/dist/themes/theme-state.d.ts +28 -7
- package/dist/themes/theme-state.js +24 -6
- package/dist/themes/themes-public.controller.d.ts +7 -2
- package/dist/themes/themes-public.controller.js +11 -5
- package/dist/themes/themes.service.js +2 -2
- package/dist/tools/connectors.controller.d.ts +28 -11
- package/dist/tools/connectors.controller.js +49 -14
- package/dist/tools/connectors.schemas.d.ts +4 -0
- package/dist/tools/connectors.schemas.js +4 -0
- package/dist/tools/connectors.service.d.ts +30 -2
- package/dist/tools/connectors.service.js +98 -3
- package/dist/tools/credentials.d.ts +20 -0
- package/dist/tools/credentials.js +90 -1
- package/dist/tools/oauth/tokens.d.ts +20 -0
- package/dist/tools/oauth/tokens.js +49 -24
- package/dist/tools/pipeline.d.ts +3 -2
- package/dist/ui/arrangement.d.ts +41 -50
- package/dist/ui/arrangement.js +88 -15
- package/dist/ui/registry.d.ts +13 -3
- package/dist/ui/registry.js +15 -4
- package/dist/ui/ui.schemas.d.ts +4 -0
- package/dist/ui/views.d.ts +36 -9
- package/dist/ui/views.js +58 -28
- package/package.json +3 -3
- package/src/migrations/0106_setup_guides_v2.sql +10 -0
- package/src/migrations/0107_extension_client_builds.sql +50 -0
|
@@ -8,11 +8,13 @@ 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, pluginDescription, SDK_VERSION, SKILL_ENTRY_FILE, } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { appearanceIssues, PLUGIN_MANIFEST_FILE, parseSkillMd, pluginDescription, SDK_VERSION, SKILL_ENTRY_FILE, } from "@capybara-hq/plugin-sdk";
|
|
12
12
|
import { getQueueToken } from "@nestjs/bullmq";
|
|
13
13
|
import { Inject, Injectable } from "@nestjs/common";
|
|
14
14
|
import { sql } from "kysely";
|
|
15
15
|
import semver from "semver";
|
|
16
|
+
import { ClientBuilds } from "../client/client-builds.service.js";
|
|
17
|
+
import { checkClientFiles } from "../client/client-files.js";
|
|
16
18
|
import { ApiError } from "../errors.js";
|
|
17
19
|
import { ExtensionAdminOps } from "../extensions/admin-ops.js";
|
|
18
20
|
import { ExtensionAdmin } from "../extensions/admin-view.js";
|
|
@@ -112,14 +114,17 @@ const reviewStale = (review) => new ApiError("plugin.review_stale", `The review
|
|
|
112
114
|
const mismatch = (what) => new ApiError("marketplace.package_mismatch", `The downloaded package does not match the marketplace entry: ${what}`);
|
|
113
115
|
/**
|
|
114
116
|
* The API error of a plugin package that cannot be read in a catalog path: the pre-0.1.0 format ->
|
|
115
|
-
* `catalog.legacy_format`;
|
|
116
|
-
*
|
|
117
|
-
* anything else -> `catalog.invalid_manifest` (issues as
|
|
117
|
+
* `catalog.legacy_format`; undeclared client code -> `client.undeclared`; a failing theme (themes
|
|
118
|
+
* spec §5) -> `theme.invalid` (details `[{ theme, errors }]`) or `theme.unreadable` (details
|
|
119
|
+
* `[{ theme, issues }]`, the failing pairs); anything else -> `catalog.invalid_manifest` (issues as
|
|
120
|
+
* details).
|
|
118
121
|
*/
|
|
119
122
|
export function catalogPackageError(e) {
|
|
120
123
|
const legacy = e.code === "catalog.legacy_format" || e.code === "extension.legacy_format";
|
|
121
124
|
if (legacy)
|
|
122
125
|
return new ApiError("catalog.legacy_format", e.message, e.issues);
|
|
126
|
+
if (e.code === "client.undeclared")
|
|
127
|
+
return new ApiError("client.undeclared", e.message);
|
|
123
128
|
if (e.code === "theme.invalid" || e.code === "theme.unreadable") {
|
|
124
129
|
return themeApiError(e);
|
|
125
130
|
}
|
|
@@ -160,6 +165,7 @@ let PluginImport = class PluginImport {
|
|
|
160
165
|
txHost;
|
|
161
166
|
platform;
|
|
162
167
|
maintenance;
|
|
168
|
+
clientBuilds;
|
|
163
169
|
/** Where plugin files go (the kernel's object storage; the CLI's own, see {@link useCliStorage}). */
|
|
164
170
|
files;
|
|
165
171
|
/**
|
|
@@ -180,7 +186,9 @@ let PluginImport = class PluginImport {
|
|
|
180
186
|
/** The platform rows of a new item's components, written after the confirm's commit. */
|
|
181
187
|
platform,
|
|
182
188
|
/** A failed platform sync enqueues a run of the migration job here (it heals the item). */
|
|
183
|
-
maintenance
|
|
189
|
+
maintenance,
|
|
190
|
+
/** The client build of a dropped org upload's package is deleted with it. */
|
|
191
|
+
clientBuilds) {
|
|
184
192
|
this.kdb = kdb;
|
|
185
193
|
this.adminLock = adminLock;
|
|
186
194
|
this.orgLock = orgLock;
|
|
@@ -197,6 +205,7 @@ let PluginImport = class PluginImport {
|
|
|
197
205
|
this.txHost = txHost;
|
|
198
206
|
this.platform = platform;
|
|
199
207
|
this.maintenance = maintenance;
|
|
208
|
+
this.clientBuilds = clientBuilds;
|
|
200
209
|
this.files = storage;
|
|
201
210
|
}
|
|
202
211
|
/** The kernel-schema handle of the current context (the transaction, else the pool). */
|
|
@@ -341,6 +350,22 @@ let PluginImport = class PluginImport {
|
|
|
341
350
|
? await readingPackage(() => readExtensionPackage(bytes, { maxBytes, hostSdkVersion: SDK_VERSION }))
|
|
342
351
|
: undefined;
|
|
343
352
|
const content = this.checkContent(pkg, format);
|
|
353
|
+
// suggestedAppearance.theme against the themes the scan found (the manifest may not list them)
|
|
354
|
+
const appearance = appearanceIssues(m, pkg.themes.map((t) => t.id));
|
|
355
|
+
if (appearance.length > 0)
|
|
356
|
+
throw invalid(appearance);
|
|
357
|
+
// Client code (client extensions spec §9.1): the section re-checked and every file against it
|
|
358
|
+
// (keys, listed = present, sizes, sha384 recomputed, the CSS layer wrap); delivered from object
|
|
359
|
+
// storage, so refused without one. The store checks the bytes it writes again. A bootstrap
|
|
360
|
+
// plugin never carries it: a bootstrap run may have no object store (it may be a bootstrap
|
|
361
|
+
// plugin itself), and a required entry failing on that would stop the start.
|
|
362
|
+
if (ext?.section.client !== undefined) {
|
|
363
|
+
if (source.kind === "bootstrap") {
|
|
364
|
+
throw new ApiError("client.invalid", `Bootstrap plugin ${m.name} ships client code (extension.client), which a bootstrap plugin may not carry: its files are kept in object storage, which a bootstrap run may not have (the object store may be a bootstrap plugin itself); take ${m.name} out of bootstrap.json and import it into the catalog instead`);
|
|
365
|
+
}
|
|
366
|
+
checkClientFiles(ext.section.client, pkg.files);
|
|
367
|
+
this.files.assertAvailable();
|
|
368
|
+
}
|
|
344
369
|
// A name live for another owner (an org's for the super admin; the platform's or another
|
|
345
370
|
// org's for an org) -> 409 plugin.name_taken naming it (P3-14); the same owner adds a version
|
|
346
371
|
await assertNameFree(this.db, m.name, owner?.orgId ?? null);
|
|
@@ -475,13 +500,16 @@ let PluginImport = class PluginImport {
|
|
|
475
500
|
.sort()
|
|
476
501
|
: [];
|
|
477
502
|
const mismatch = permissionsMismatch(opts.expect?.marketplace?.permissions, ext ? keys : undefined);
|
|
503
|
+
const manifest = normalizedManifest(pkg);
|
|
504
|
+
const replacements = manifest.suggestedAppearance?.replacements;
|
|
478
505
|
const staged = {
|
|
479
506
|
itemId,
|
|
480
507
|
packageName,
|
|
481
508
|
version,
|
|
482
509
|
contentDigest: pkg.contentDigest,
|
|
483
510
|
size: tgz.size,
|
|
484
|
-
manifest
|
|
511
|
+
manifest,
|
|
512
|
+
...(replacements !== undefined && { appearanceOrder: Object.keys(replacements) }),
|
|
485
513
|
files: content.files.map(({ path, size, sha256 }) => ({ path, size, sha256 })),
|
|
486
514
|
...(mismatch !== null && { permissionsMismatch: mismatch }),
|
|
487
515
|
sourceFormat: format,
|
|
@@ -785,6 +813,22 @@ let PluginImport = class PluginImport {
|
|
|
785
813
|
// Checked again before this upload claims anything (ruling P3-51)
|
|
786
814
|
if (owner !== undefined)
|
|
787
815
|
await assertPackageFree(t, staged.packageName, owner.orgId);
|
|
816
|
+
// The item's name lock and its live row before the extension's install lock: the global lock
|
|
817
|
+
// order is the item, then the install lock (ruling P3-50; ExtensionAdminOps.install locks the
|
|
818
|
+
// source item row before the install lock, an org deletion its items before it). Taken after
|
|
819
|
+
// the install lock instead, a confirmation of an org upload racing a replacement upload
|
|
820
|
+
// deadlocks (40P01).
|
|
821
|
+
const m = manifest;
|
|
822
|
+
await this.itemLock.lock(m.name);
|
|
823
|
+
// The scope the audit records: the review's for a new item, the item's own for a new version
|
|
824
|
+
// of a live item (which keeps its scope)
|
|
825
|
+
const existing = await t
|
|
826
|
+
.selectFrom("catalog_items")
|
|
827
|
+
.select(["id", "scope", "awaiting_confirmation", "package_name"])
|
|
828
|
+
.where("name", "=", m.name)
|
|
829
|
+
.where("deleted_at", "is", null)
|
|
830
|
+
.forUpdate()
|
|
831
|
+
.executeTakeFirst();
|
|
788
832
|
let now;
|
|
789
833
|
let previous;
|
|
790
834
|
if (reviewed) {
|
|
@@ -817,8 +861,6 @@ let PluginImport = class PluginImport {
|
|
|
817
861
|
previous = await this.approve(actor, review, staged, now, stored, opts);
|
|
818
862
|
}
|
|
819
863
|
}
|
|
820
|
-
const m = manifest;
|
|
821
|
-
await this.itemLock.lock(m.name);
|
|
822
864
|
// Under the name's lock: the first confirm of a name wins, a later one of another owner is
|
|
823
865
|
// refused naming it, before anything of it is written (P3-14)
|
|
824
866
|
await assertNameFree(t, m.name, owner?.orgId ?? null);
|
|
@@ -826,15 +868,6 @@ let PluginImport = class PluginImport {
|
|
|
826
868
|
await assertNameNotReserved(t, m.name);
|
|
827
869
|
await this.resolveDependencies(m, owner);
|
|
828
870
|
}
|
|
829
|
-
// The scope the audit records: the review's for a new item, the item's own for a new version
|
|
830
|
-
// of a live item (which keeps its scope)
|
|
831
|
-
const existing = await t
|
|
832
|
-
.selectFrom("catalog_items")
|
|
833
|
-
.select(["id", "scope", "awaiting_confirmation", "package_name"])
|
|
834
|
-
.where("name", "=", m.name)
|
|
835
|
-
.where("deleted_at", "is", null)
|
|
836
|
-
.forUpdate()
|
|
837
|
-
.executeTakeFirst();
|
|
838
871
|
const actual = existing === undefined
|
|
839
872
|
? scope
|
|
840
873
|
: {
|
|
@@ -879,6 +912,7 @@ let PluginImport = class PluginImport {
|
|
|
879
912
|
version: staged.version,
|
|
880
913
|
contentDigest: staged.contentDigest,
|
|
881
914
|
manifest: m,
|
|
915
|
+
appearanceOrder: staged.appearanceOrder ?? null,
|
|
882
916
|
listing: {
|
|
883
917
|
publisher: m.publisher,
|
|
884
918
|
description: pluginDescription(m.description).en,
|
|
@@ -1307,8 +1341,27 @@ let PluginImport = class PluginImport {
|
|
|
1307
1341
|
.executeTakeFirst());
|
|
1308
1342
|
if (used !== undefined)
|
|
1309
1343
|
return;
|
|
1310
|
-
|
|
1344
|
+
const build = await t
|
|
1345
|
+
.selectFrom("extension_client_builds")
|
|
1346
|
+
.select("object_prefix")
|
|
1347
|
+
.where("package_sha256", "=", sha)
|
|
1348
|
+
.executeTakeFirst();
|
|
1349
|
+
// extension_blobs (a PG-stored package) and extension_client_builds go with the row (ON
|
|
1350
|
+
// DELETE CASCADE)
|
|
1311
1351
|
await t.deleteFrom("extension_packages").where("sha256", "=", sha).execute();
|
|
1352
|
+
if (build !== undefined) {
|
|
1353
|
+
afterCommit(async () => {
|
|
1354
|
+
try {
|
|
1355
|
+
await this.clientBuilds.deleteFiles(sha, build.object_prefix);
|
|
1356
|
+
}
|
|
1357
|
+
catch (err) {
|
|
1358
|
+
this.log.warn("cannot delete an expired org upload's client build objects", {
|
|
1359
|
+
sha256: sha,
|
|
1360
|
+
err,
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1312
1365
|
// The object only once the row is gone for good: a failure leaves an unreferenced object
|
|
1313
1366
|
// (harmless; a later upload of the same bytes rewrites it), never a row without its bytes
|
|
1314
1367
|
if (pkg.blob_store === "object") {
|
|
@@ -1377,7 +1430,7 @@ let PluginImport = class PluginImport {
|
|
|
1377
1430
|
if (stored.has(path))
|
|
1378
1431
|
objectPaths.add(path);
|
|
1379
1432
|
};
|
|
1380
|
-
// The plugin icon and the setup guide
|
|
1433
|
+
// The plugin icon and the setup guide documents (rendered by PluginGuides): display files
|
|
1381
1434
|
const displayPaths = displayFilePaths(m, pkg.connectorTemplates.flatMap((c) => (c.file.icon === undefined ? [] : [c.file.icon])));
|
|
1382
1435
|
for (const path of displayPaths)
|
|
1383
1436
|
keep(path);
|
|
@@ -1588,6 +1641,7 @@ let PluginImport = class PluginImport {
|
|
|
1588
1641
|
warnings,
|
|
1589
1642
|
reviewHash: extensionReviewHash(set, warnings),
|
|
1590
1643
|
sensitive: permissions.some((g) => g.sensitive) || warnings.length > 0,
|
|
1644
|
+
...(pkg.section.client !== undefined && { client: reviewClient(pkg) }),
|
|
1591
1645
|
};
|
|
1592
1646
|
}
|
|
1593
1647
|
async readStored(sha256) {
|
|
@@ -1654,7 +1708,8 @@ PluginImport = __decorate([
|
|
|
1654
1708
|
__param(13, Inject(KERNEL_LOGGER)),
|
|
1655
1709
|
__param(14, Inject(TransactionHost)),
|
|
1656
1710
|
__param(15, Inject(PlatformComponents)),
|
|
1657
|
-
__param(16, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
|
|
1711
|
+
__param(16, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME))),
|
|
1712
|
+
__param(17, Inject(ClientBuilds))
|
|
1658
1713
|
], PluginImport);
|
|
1659
1714
|
export { PluginImport };
|
|
1660
1715
|
/**
|
|
@@ -1751,6 +1806,27 @@ export function pluginReviewHash(r, files) {
|
|
|
1751
1806
|
]);
|
|
1752
1807
|
return createHash("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
1753
1808
|
}
|
|
1809
|
+
/** The client code of a read extension package for its review (sizes from the package's files). */
|
|
1810
|
+
function reviewClient(pkg) {
|
|
1811
|
+
const c = pkg.section.client;
|
|
1812
|
+
const sizes = new Map(pkg.files.map((f) => [f.path, f.size]));
|
|
1813
|
+
const files = [
|
|
1814
|
+
...(c.entry !== undefined ? [c.entry] : []),
|
|
1815
|
+
...(c.styles ?? []),
|
|
1816
|
+
...(c.assets ?? []),
|
|
1817
|
+
].map((path) => {
|
|
1818
|
+
// checkClientFiles ran first: every listed file is in the package
|
|
1819
|
+
const size = sizes.get(path.slice(2));
|
|
1820
|
+
if (size === undefined)
|
|
1821
|
+
throw new Error(`client file ${path} is not in the package`);
|
|
1822
|
+
return { path, size };
|
|
1823
|
+
});
|
|
1824
|
+
return {
|
|
1825
|
+
files,
|
|
1826
|
+
totalBytes: files.reduce((n, f) => n + f.size, 0),
|
|
1827
|
+
apiVersion: c.apiVersion,
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1754
1830
|
/**
|
|
1755
1831
|
* A version change that is a downgrade (the imported version below the installed one) whose
|
|
1756
1832
|
* version has migrations not run on this deployment -> `extension.downgrade_unsupported` (the same
|
|
@@ -16,9 +16,11 @@ export { LEGACY_FORMAT_MESSAGE, packagePath };
|
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* Codes of {@link PluginPackageError}: `theme.invalid` / `theme.unreadable` when the package's only
|
|
19
|
-
* problems are in its themes (themes spec §5: a failing theme refuses the whole plugin)
|
|
19
|
+
* problems are in its themes (themes spec §5: a failing theme refuses the whole plugin);
|
|
20
|
+
* `client.undeclared` for an `extension.client` section whose manifest lacks
|
|
21
|
+
* `permissions.client: true` (client extensions spec §5.4: client code is always reviewed).
|
|
20
22
|
*/
|
|
21
|
-
export type PluginPackageErrorCode = "catalog.invalid_manifest" | "catalog.legacy_format" | "theme.invalid" | "theme.unreadable";
|
|
23
|
+
export type PluginPackageErrorCode = "catalog.invalid_manifest" | "catalog.legacy_format" | "theme.invalid" | "theme.unreadable" | "client.undeclared";
|
|
22
24
|
/** The problems of one theme of a refused plugin (English; `unreadable:` lines for readability). */
|
|
23
25
|
export interface PluginThemeProblem {
|
|
24
26
|
/** The theme id (the file's base name). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { AGENT_TEMPLATE_LIMITS, CATALOG_ITEM_NAME_RE, CONNECTOR_TEMPLATE_LIMITS, hostSdkProblem, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_LIMITS, parseAgentTemplate, parseConnectorTemplateFile, parsePluginManifest, parseSkillMd, parseThemeFile, SKILL_ENTRY_FILE,
|
|
2
|
+
import { AGENT_TEMPLATE_LIMITS, CATALOG_ITEM_NAME_RE, CONNECTOR_TEMPLATE_LIMITS, hostSdkProblem, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_LIMITS, parseAgentTemplate, parseConnectorTemplateFile, parsePluginManifest, parseSkillMd, parseThemeFile, SKILL_ENTRY_FILE, setupGuideDocPath, setupGuideDocProblem, THEME_LIMITS, themeIdOf, validatePluginPackageJsonFor, validateSetupGuide, validateThemeAssets, } from "@capybara-hq/plugin-sdk";
|
|
3
3
|
import { ApiError } from "../errors.js";
|
|
4
4
|
import { HOST_ZOD_VERSION, LEGACY_FORMAT_MESSAGE, PackageTgzError, packagePath, parsePackageJsonFile, unpackPackageTgz, } from "../packages/tgz.js";
|
|
5
5
|
export { LEGACY_FORMAT_MESSAGE, packagePath };
|
|
@@ -12,9 +12,11 @@ export class PluginPackageError extends Error {
|
|
|
12
12
|
constructor(code, issues, themes) {
|
|
13
13
|
super(code === "catalog.legacy_format"
|
|
14
14
|
? (issues[0] ?? LEGACY_FORMAT_MESSAGE)
|
|
15
|
-
: code === "
|
|
16
|
-
?
|
|
17
|
-
:
|
|
15
|
+
: code === "client.undeclared"
|
|
16
|
+
? (issues[0] ?? code)
|
|
17
|
+
: code === "theme.invalid" || code === "theme.unreadable"
|
|
18
|
+
? `Theme ${themes?.[0]?.theme ?? "?"} ${code === "theme.invalid" ? "is invalid" : "is not readable"}: ${issues[0] ?? "unknown problem"}`
|
|
19
|
+
: `Invalid plugin package: ${issues[0] ?? "unknown problem"}`);
|
|
18
20
|
this.name = "PluginPackageError";
|
|
19
21
|
this.code = code;
|
|
20
22
|
this.issues = issues;
|
|
@@ -78,6 +80,27 @@ export function iconProblem(path, data) {
|
|
|
78
80
|
return /<svg[\s>/]/i.test(text) ? undefined : `${path} is not an SVG document`;
|
|
79
81
|
}
|
|
80
82
|
/** package.json parsed, or undefined when it is not valid (only used to name a legacy package). */
|
|
83
|
+
/**
|
|
84
|
+
* Client code the extension manifest does not declare (an `extension.client` section without
|
|
85
|
+
* `permissions.client: true`), read from the raw plugin.json before it is parsed, so the refusal
|
|
86
|
+
* names it (`client.undeclared`) instead of being one parse problem among others.
|
|
87
|
+
*/
|
|
88
|
+
function clientUndeclared(text) {
|
|
89
|
+
let raw;
|
|
90
|
+
try {
|
|
91
|
+
raw = JSON.parse(text);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const ext = isObj(raw) ? raw.extension : undefined;
|
|
97
|
+
if (!isObj(ext) || ext.client === undefined)
|
|
98
|
+
return undefined;
|
|
99
|
+
const perms = isObj(ext.manifest) ? ext.manifest.permissions : undefined;
|
|
100
|
+
if (isObj(perms) && perms.client === true)
|
|
101
|
+
return undefined;
|
|
102
|
+
return `${PLUGIN_MANIFEST_FILE}: extension.client ships client code, but the extension manifest does not declare permissions.client: true; client code must be declared so it is reviewed (capybara-plugin pack writes both)`;
|
|
103
|
+
}
|
|
81
104
|
const parsedOrUndefined = (data) => {
|
|
82
105
|
try {
|
|
83
106
|
return parsePackageJsonFile({ data });
|
|
@@ -201,6 +224,9 @@ export function readPluginFiles(files, opts) {
|
|
|
201
224
|
const text = decode(manifestBytes);
|
|
202
225
|
if (text === undefined)
|
|
203
226
|
return invalid([`${PLUGIN_MANIFEST_FILE} is not valid UTF-8 text`]);
|
|
227
|
+
const undeclared = clientUndeclared(text);
|
|
228
|
+
if (undeclared !== undefined)
|
|
229
|
+
throw new PluginPackageError("client.undeclared", [undeclared]);
|
|
204
230
|
const parsed = parsePluginManifest(text);
|
|
205
231
|
if ("errors" in parsed) {
|
|
206
232
|
return invalid(parsed.errors.map((e) => `${PLUGIN_MANIFEST_FILE}: ${e}`));
|
|
@@ -362,18 +388,46 @@ export function readPluginFiles(files, opts) {
|
|
|
362
388
|
issues.push(`icon: ${problem}`);
|
|
363
389
|
}
|
|
364
390
|
const ext = manifest.extension;
|
|
365
|
-
// Setup guides
|
|
366
|
-
//
|
|
367
|
-
// the manifest parser checks when the
|
|
391
|
+
// Setup guides v2 (spec 2026-11-09-setup-guides-v2-design.md §4.5): the documents are text
|
|
392
|
+
// within the limits and pass the SDK validator (an unknown variable is accepted: it renders as
|
|
393
|
+
// written); a channel guide needs a channel extension, which the manifest parser checks when the
|
|
394
|
+
// extension section is there; a template guide names one of the plugin's connector templates
|
|
395
|
+
const templateIds = new Set(connectorTemplates.map((t) => t.id));
|
|
368
396
|
manifest.setupGuides?.forEach((g, i) => {
|
|
397
|
+
const at = `setupGuides.${i} (${g.id})`;
|
|
369
398
|
if (g.place === "channel-connection" && ext === undefined) {
|
|
370
|
-
issues.push(
|
|
399
|
+
issues.push(`${at}: a channel-connection guide needs the plugin's extension to provide channel@1`);
|
|
371
400
|
}
|
|
372
|
-
|
|
373
|
-
|
|
401
|
+
// a listed template is checked by the manifest parser; scanned ones only here
|
|
402
|
+
if (g.template !== undefined &&
|
|
403
|
+
manifest.connectorTemplates === undefined &&
|
|
404
|
+
!templateIds.has(g.template)) {
|
|
405
|
+
issues.push(`${at}: template ${g.template} is not one of the plugin's connector templates`);
|
|
406
|
+
}
|
|
407
|
+
const docs = {};
|
|
408
|
+
for (const locale of ["en", "zh"]) {
|
|
409
|
+
const path = setupGuideDocPath(g.id, locale);
|
|
410
|
+
const data = files.get(path);
|
|
411
|
+
if (locale === "zh" && data === undefined)
|
|
412
|
+
continue;
|
|
413
|
+
const problem = setupGuideDocProblem(path, data);
|
|
374
414
|
if (problem !== undefined)
|
|
375
|
-
issues.push(
|
|
376
|
-
|
|
415
|
+
issues.push(`${at}: ${problem}`);
|
|
416
|
+
else
|
|
417
|
+
docs[locale] = new TextDecoder().decode(data);
|
|
418
|
+
}
|
|
419
|
+
if (docs.en === undefined)
|
|
420
|
+
return;
|
|
421
|
+
for (const issue of validateSetupGuide({
|
|
422
|
+
place: g.place,
|
|
423
|
+
en: docs.en,
|
|
424
|
+
...(docs.zh !== undefined && { zh: docs.zh }),
|
|
425
|
+
id: g.id,
|
|
426
|
+
allowUnknownVariables: true,
|
|
427
|
+
})) {
|
|
428
|
+
if (issue.level === "error")
|
|
429
|
+
issues.push(`${at}: ${issue.message}`);
|
|
430
|
+
}
|
|
377
431
|
});
|
|
378
432
|
if (ext !== undefined) {
|
|
379
433
|
if (!files.has(packagePath(ext.entry))) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PluginManifest } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { ExtensionAdminOps } from "../extensions/admin-ops.js";
|
|
3
3
|
import { ExtensionBlobs } from "../extensions/blobs.js";
|
|
4
|
+
import { PluginGuides, type TemplateGuideChange } from "../extensions/plugin-guides.js";
|
|
4
5
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
5
6
|
import type { SingleProviderNote } from "../extensions/single-provider.js";
|
|
6
7
|
import { SuperAdminLock } from "../identity/super-admin.js";
|
|
@@ -110,6 +111,13 @@ export interface PlatformUpdatePreview {
|
|
|
110
111
|
needsApproval: boolean;
|
|
111
112
|
} | null;
|
|
112
113
|
connectorOrgs: PlatformUpdateConnectorOrg[];
|
|
114
|
+
/**
|
|
115
|
+
* Connector templates whose setup guide the update changes (code blocks or checks of the admin
|
|
116
|
+
* text): "the setup guide of template X changed; org admins of N orgs will be asked to update
|
|
117
|
+
* their app" (spec 2026-11-09-setup-guides-v2-design.md §11). Counts only. Not covered by
|
|
118
|
+
* `diffHash` (nothing the confirm does depends on it).
|
|
119
|
+
*/
|
|
120
|
+
guideChanges: TemplateGuideChange[];
|
|
113
121
|
/**
|
|
114
122
|
* The themes the update removes that are on in some org (themes spec §6): the orgs whose default
|
|
115
123
|
* each is and how many people picked it. Absent when no such theme is removed.
|
|
@@ -211,14 +219,20 @@ export declare class PlatformPluginUpdater {
|
|
|
211
219
|
private readonly connectors;
|
|
212
220
|
private readonly sync;
|
|
213
221
|
private readonly log;
|
|
222
|
+
/** The connector template guides an update changes (the preview's `guideChanges`). */
|
|
223
|
+
private readonly guides;
|
|
214
224
|
constructor(kdb: KernelDb, txHost: TransactionHost, adminLock: SuperAdminLock, itemLock: CatalogItemLock, orgLock: OrgLock, locks: LockService, storage: StorageService, registry: ExtensionRegistry, ops: ExtensionAdminOps,
|
|
215
225
|
/** Reads the target's extension package (its permissions, for `needsApproval`). */
|
|
216
226
|
blobs: ExtensionBlobs, config: KernelConfigService, platform: PlatformComponents,
|
|
217
227
|
/** Template dependencies (the deployed connector kinds). */
|
|
218
|
-
installer: PluginInstaller, connectors: ConnectorsService, sync: PluginScopeSync, log: KernelLogger
|
|
228
|
+
installer: PluginInstaller, connectors: ConnectorsService, sync: PluginScopeSync, log: KernelLogger,
|
|
229
|
+
/** The connector template guides an update changes (the preview's `guideChanges`). */
|
|
230
|
+
guides: Pick<PluginGuides, "templateGuideChanges">);
|
|
219
231
|
private get db();
|
|
220
232
|
/** The update preview (see the class comment); writes nothing. */
|
|
221
233
|
preview(itemId: string, version: string): Promise<PlatformUpdatePreview>;
|
|
234
|
+
/** The preview of a diff with the template guides it changes. */
|
|
235
|
+
private withGuideChanges;
|
|
222
236
|
/**
|
|
223
237
|
* The update preview of a new version of an org's own plugin (ruling P3-12): the item must be
|
|
224
238
|
* owned by `orgId` (else 404, as for any item the org cannot see), the target carries no
|
|
@@ -20,6 +20,7 @@ import { ExtensionAdminOps } from "../extensions/admin-ops.js";
|
|
|
20
20
|
import { ExtensionBlobs } from "../extensions/blobs.js";
|
|
21
21
|
import { permissionSetOfPackage, readExtensionPackage } from "../extensions/package.js";
|
|
22
22
|
import { blockingKeys, diffPermissions } from "../extensions/permission-set.js";
|
|
23
|
+
import { PluginGuides } from "../extensions/plugin-guides.js";
|
|
23
24
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
24
25
|
import { audit } from "../identity/audit.js";
|
|
25
26
|
import { SuperAdminLock } from "../identity/super-admin.js";
|
|
@@ -143,11 +144,14 @@ let PlatformPluginUpdater = class PlatformPluginUpdater {
|
|
|
143
144
|
connectors;
|
|
144
145
|
sync;
|
|
145
146
|
log;
|
|
147
|
+
guides;
|
|
146
148
|
constructor(kdb, txHost, adminLock, itemLock, orgLock, locks, storage, registry, ops,
|
|
147
149
|
/** Reads the target's extension package (its permissions, for `needsApproval`). */
|
|
148
150
|
blobs, config, platform,
|
|
149
151
|
/** Template dependencies (the deployed connector kinds). */
|
|
150
|
-
installer, connectors, sync, log
|
|
152
|
+
installer, connectors, sync, log,
|
|
153
|
+
/** The connector template guides an update changes (the preview's `guideChanges`). */
|
|
154
|
+
guides) {
|
|
151
155
|
this.kdb = kdb;
|
|
152
156
|
this.txHost = txHost;
|
|
153
157
|
this.adminLock = adminLock;
|
|
@@ -164,13 +168,21 @@ let PlatformPluginUpdater = class PlatformPluginUpdater {
|
|
|
164
168
|
this.connectors = connectors;
|
|
165
169
|
this.sync = sync;
|
|
166
170
|
this.log = log;
|
|
171
|
+
this.guides = guides;
|
|
167
172
|
}
|
|
168
173
|
get db() {
|
|
169
174
|
return this.kdb.db;
|
|
170
175
|
}
|
|
171
176
|
/** The update preview (see the class comment); writes nothing. */
|
|
172
177
|
async preview(itemId, version) {
|
|
173
|
-
return
|
|
178
|
+
return this.withGuideChanges(await this.diff(itemId, version));
|
|
179
|
+
}
|
|
180
|
+
/** The preview of a diff with the template guides it changes. */
|
|
181
|
+
async withGuideChanges(d) {
|
|
182
|
+
return {
|
|
183
|
+
...previewOf(d),
|
|
184
|
+
guideChanges: await this.guides.templateGuideChanges(d.itemId, d.from, d.to),
|
|
185
|
+
};
|
|
174
186
|
}
|
|
175
187
|
/**
|
|
176
188
|
* The update preview of a new version of an org's own plugin (ruling P3-12): the item must be
|
|
@@ -186,7 +198,7 @@ let PlatformPluginUpdater = class PlatformPluginUpdater {
|
|
|
186
198
|
if (staged !== undefined && staged.version !== version) {
|
|
187
199
|
throw new Error(`internal: previewForOwner got the staged target of version ${staged.version} for version ${version}`);
|
|
188
200
|
}
|
|
189
|
-
const preview =
|
|
201
|
+
const preview = await this.withGuideChanges(await this.diff(itemId, version, {
|
|
190
202
|
owner: { orgId },
|
|
191
203
|
...(staged !== undefined && { target: staged }),
|
|
192
204
|
}));
|
|
@@ -1502,10 +1514,11 @@ PlatformPluginUpdater = __decorate([
|
|
|
1502
1514
|
__param(12, Inject(PluginInstaller)),
|
|
1503
1515
|
__param(13, Inject(ConnectorsService)),
|
|
1504
1516
|
__param(14, Inject(PluginScopeSync)),
|
|
1505
|
-
__param(15, Inject(KERNEL_LOGGER))
|
|
1517
|
+
__param(15, Inject(KERNEL_LOGGER)),
|
|
1518
|
+
__param(16, Inject(PluginGuides))
|
|
1506
1519
|
], PlatformPluginUpdater);
|
|
1507
1520
|
export { PlatformPluginUpdater };
|
|
1508
|
-
/** The preview of a diff. */
|
|
1521
|
+
/** The preview of a diff (without `guideChanges`, which reads PG). */
|
|
1509
1522
|
function previewOf(d) {
|
|
1510
1523
|
return {
|
|
1511
1524
|
itemId: d.itemId,
|
|
@@ -13,7 +13,7 @@ import { ChannelConnections, type ChannelConnectionView } from "./connections.se
|
|
|
13
13
|
* the URL of its plugin's icon on the org's catalog route (null without one, or when the org may
|
|
14
14
|
* not see the plugin's catalog item; maintainer decision 2026-11-08), and its plugin's setup guide
|
|
15
15
|
* with the drift since the connection was set up (`setupGuide`, `guideDrift`; spec
|
|
16
|
-
* 2026-11-
|
|
16
|
+
* 2026-11-09-setup-guides-v2-design.md §11).
|
|
17
17
|
*/
|
|
18
18
|
export type ChannelConnectionPage = ChannelConnectionView & ConnectionGuideState & {
|
|
19
19
|
pluginDisplayName: LocalizedName | null;
|
|
@@ -93,7 +93,7 @@ let ChannelConnectionsController = class ChannelConnectionsController {
|
|
|
93
93
|
*/
|
|
94
94
|
async ackGuide(principal, org, id, body) {
|
|
95
95
|
const p = requireUser(principal);
|
|
96
|
-
const view = await this.channels.ackGuide(org.orgId, id, body.hash, p.account.id, patTokenId(p));
|
|
96
|
+
const view = await this.channels.ackGuide({ orgId: org.orgId, role: org.role }, id, body.hash, p.account.id, patTokenId(p));
|
|
97
97
|
return { channel: await this.withPlugin(org, view) };
|
|
98
98
|
}
|
|
99
99
|
async remove(principal, org, id) {
|
|
@@ -12,6 +12,7 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
|
12
12
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
13
13
|
import { type KernelLogger } from "../log.js";
|
|
14
14
|
import { OrgLock } from "../orgs/org-lock.js";
|
|
15
|
+
import type { OrgRole } from "../orgs/roles.js";
|
|
15
16
|
import { ChannelJobs } from "./jobs.js";
|
|
16
17
|
import type { ChannelLocale } from "./phrases.js";
|
|
17
18
|
/** 连接配置里全部密钥字段存成一行:`{ 点路径: 值 }` 的 JSON。 */
|
|
@@ -112,7 +113,7 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
112
113
|
/** Secret values handled here (written or decrypted) are redacted from the logs (`addOrgSecrets`). */
|
|
113
114
|
private readonly log;
|
|
114
115
|
readonly txHost: TransactionHost;
|
|
115
|
-
/** The setup guide acks (spec 2026-11-
|
|
116
|
+
/** The setup guide acks (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
116
117
|
private readonly guides;
|
|
117
118
|
private readonly unsubscribe;
|
|
118
119
|
/** `<plugin>@<version>` whose invalid connection hints were warned about (a log dedupe only). */
|
|
@@ -126,7 +127,7 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
126
127
|
config: Pick<KernelConfigService, "get">, events: Pick<KernelEvents, "on">,
|
|
127
128
|
/** Secret values handled here (written or decrypted) are redacted from the logs (`addOrgSecrets`). */
|
|
128
129
|
log: Pick<KernelLogger, "warn" | "addOrgSecrets">, txHost: TransactionHost,
|
|
129
|
-
/** The setup guide acks (spec 2026-11-
|
|
130
|
+
/** The setup guide acks (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
130
131
|
guides: Pick<PluginGuides, "currentAck" | "ackDrifted">);
|
|
131
132
|
/** Unsubscribes after the stop phases: events emitted while they drain are still handled. */
|
|
132
133
|
onApplicationShutdown(): void;
|
|
@@ -186,11 +187,15 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
186
187
|
private updateIn;
|
|
187
188
|
/**
|
|
188
189
|
* 「我已更新应用」: records the plugin's current channel-connection guide for the connection's
|
|
189
|
-
* mode as the one it is set up with. `hash` is the
|
|
190
|
+
* mode as the one it is set up with. `hash` is the drift hash of the guide the person was
|
|
190
191
|
* shown; when the plugin has moved on since, 409 `channel.guide_changed` (show the new guide
|
|
191
|
-
* first). A plugin without a guide -> 404
|
|
192
|
+
* first). A plugin without a guide, or whose item the org's catalog does not show -> 404
|
|
193
|
+
* `catalog.guide_not_found`.
|
|
192
194
|
*/
|
|
193
|
-
ackGuide(
|
|
195
|
+
ackGuide(org: {
|
|
196
|
+
orgId: string;
|
|
197
|
+
role: OrgRole;
|
|
198
|
+
}, id: string, hash: string, actorAccountId: string, actorTokenId?: string): Promise<ChannelConnectionView>;
|
|
194
199
|
/** The row of {@link ackGuide}, in its own transaction. */
|
|
195
200
|
private ackIn;
|
|
196
201
|
/** 删除(软删除,同一事务删掉密钥);持有长连接的副本收到广播后断开。 */
|
|
@@ -211,6 +216,18 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
211
216
|
* 持有长连接的副本立即断开(扫描复核只是兜底)。只广播,失败只记日志。
|
|
212
217
|
*/
|
|
213
218
|
notifyOrgChanged(orgId: string, plugin?: string): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* For setup checks (setup guides v2 §10.4): the input revision of a live connection of the org
|
|
221
|
+
* on `plugin`: the `updated_at` of its row and of its secrets row (ISO), never a value. Missing,
|
|
222
|
+
* deleted, of another org or plugin -> `channel.not_found`.
|
|
223
|
+
*/
|
|
224
|
+
checkRevision(orgId: string, plugin: string, id: string): Promise<string>;
|
|
225
|
+
/**
|
|
226
|
+
* For setup checks (setup guides v2 §10.8): the stored secret values of a live connection of the
|
|
227
|
+
* org on `plugin` (every secret field, decrypted), what a check's result is redacted by. Missing,
|
|
228
|
+
* deleted, of another org or plugin -> `channel.not_found`. Never echo them.
|
|
229
|
+
*/
|
|
230
|
+
checkSecrets(orgId: string, plugin: string, id: string): Promise<string[]>;
|
|
214
231
|
/**
|
|
215
232
|
* The extension implementation and full config a connection needs to run (decrypted secrets
|
|
216
233
|
* included, validated against `connectionSchema`). Connection missing / deleted ->
|