@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.
Files changed (170) hide show
  1. package/dist/admin/admin.schemas.d.ts +5 -13
  2. package/dist/catalog/admin-catalog.controller.d.ts +39 -5
  3. package/dist/catalog/admin-catalog.controller.js +75 -6
  4. package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
  5. package/dist/catalog/admin-catalog.schemas.js +7 -0
  6. package/dist/catalog/appearance.schemas.d.ts +10 -0
  7. package/dist/catalog/appearance.schemas.js +20 -0
  8. package/dist/catalog/appearance.service.d.ts +105 -0
  9. package/dist/catalog/appearance.service.js +306 -0
  10. package/dist/catalog/catalog-content.d.ts +9 -1
  11. package/dist/catalog/catalog-content.js +2 -1
  12. package/dist/catalog/catalog-files.d.ts +3 -2
  13. package/dist/catalog/catalog-files.js +8 -5
  14. package/dist/catalog/catalog-import.module.js +3 -0
  15. package/dist/catalog/catalog-items.d.ts +11 -0
  16. package/dist/catalog/catalog-items.js +21 -0
  17. package/dist/catalog/catalog-scope.service.d.ts +7 -0
  18. package/dist/catalog/catalog-scope.service.js +55 -15
  19. package/dist/catalog/catalog.service.js +10 -2
  20. package/dist/catalog/cli.js +3 -0
  21. package/dist/catalog/component-scope.service.d.ts +14 -1
  22. package/dist/catalog/component-scope.service.js +12 -2
  23. package/dist/catalog/marketplaces.service.d.ts +1 -1
  24. package/dist/catalog/org-catalog.controller.d.ts +5 -4
  25. package/dist/catalog/org-catalog.controller.js +5 -4
  26. package/dist/catalog/org-catalog.schemas.d.ts +18 -2
  27. package/dist/catalog/org-catalog.schemas.js +16 -2
  28. package/dist/catalog/org-catalog.service.js +8 -2
  29. package/dist/catalog/org-extension.controller.d.ts +27 -7
  30. package/dist/catalog/org-extension.controller.js +48 -6
  31. package/dist/catalog/plugin-import.d.ts +28 -4
  32. package/dist/catalog/plugin-import.js +96 -20
  33. package/dist/catalog/plugin-package.d.ts +4 -2
  34. package/dist/catalog/plugin-package.js +66 -12
  35. package/dist/catalog/plugin-upgrade.d.ts +15 -1
  36. package/dist/catalog/plugin-upgrade.js +18 -5
  37. package/dist/channels/connections.controller.d.ts +1 -1
  38. package/dist/channels/connections.controller.js +1 -1
  39. package/dist/channels/connections.service.d.ts +22 -5
  40. package/dist/channels/connections.service.js +56 -10
  41. package/dist/channels/webhook.d.ts +21 -1
  42. package/dist/channels/webhook.js +39 -4
  43. package/dist/client/client-access.service.d.ts +101 -0
  44. package/dist/client/client-access.service.js +294 -0
  45. package/dist/client/client-builds.service.d.ts +109 -0
  46. package/dist/client/client-builds.service.js +229 -0
  47. package/dist/client/client-delivery.controller.d.ts +25 -0
  48. package/dist/client/client-delivery.controller.js +66 -0
  49. package/dist/client/client-files.d.ts +48 -0
  50. package/dist/client/client-files.js +136 -0
  51. package/dist/client/client-reports.cron.d.ts +13 -0
  52. package/dist/client/client-reports.cron.js +42 -0
  53. package/dist/client/client-reports.service.d.ts +30 -0
  54. package/dist/client/client-reports.service.js +102 -0
  55. package/dist/client/client.controller.d.ts +45 -0
  56. package/dist/client/client.controller.js +121 -0
  57. package/dist/client/client.module.d.ts +7 -0
  58. package/dist/client/client.module.js +26 -0
  59. package/dist/client/client.schemas.d.ts +55 -0
  60. package/dist/client/client.schemas.js +27 -0
  61. package/dist/ctx/create-ctx.d.ts +19 -4
  62. package/dist/ctx/create-ctx.js +51 -3
  63. package/dist/db/kernel-schema.d.ts +58 -2
  64. package/dist/errors.d.ts +33 -0
  65. package/dist/errors.js +33 -0
  66. package/dist/extensions/admin-ops.d.ts +4 -1
  67. package/dist/extensions/admin-ops.js +22 -1
  68. package/dist/extensions/admin-view.d.ts +8 -0
  69. package/dist/extensions/admin-view.js +78 -0
  70. package/dist/extensions/blobs.d.ts +14 -1
  71. package/dist/extensions/blobs.js +48 -5
  72. package/dist/extensions/guide-places.d.ts +66 -0
  73. package/dist/extensions/guide-places.js +152 -0
  74. package/dist/extensions/host.d.ts +6 -1
  75. package/dist/extensions/host.js +23 -12
  76. package/dist/extensions/instance.d.ts +5 -1
  77. package/dist/extensions/instance.js +8 -0
  78. package/dist/extensions/ledger.d.ts +1 -1
  79. package/dist/extensions/package.d.ts +4 -3
  80. package/dist/extensions/package.js +5 -1
  81. package/dist/extensions/permission-set.d.ts +35 -5
  82. package/dist/extensions/permission-set.js +36 -5
  83. package/dist/extensions/permission-view.d.ts +38 -7
  84. package/dist/extensions/permission-view.js +27 -8
  85. package/dist/extensions/plugin-guides.d.ts +207 -54
  86. package/dist/extensions/plugin-guides.js +436 -165
  87. package/dist/extensions/sources.d.ts +6 -0
  88. package/dist/extensions/sources.js +2 -0
  89. package/dist/extensions/state.module.js +9 -1
  90. package/dist/http/api.module.js +16 -0
  91. package/dist/identity/settings.d.ts +41 -26
  92. package/dist/identity/settings.js +87 -4
  93. package/dist/infra/lock/lock.service.d.ts +8 -0
  94. package/dist/infra/lock/lock.service.js +27 -0
  95. package/dist/infra/throttler/named-throttlers.d.ts +8 -1
  96. package/dist/infra/throttler/named-throttlers.js +30 -1
  97. package/dist/infra/throttler/throttler.guard.js +1 -1
  98. package/dist/kernel.module.js +3 -0
  99. package/dist/orgs/settings.service.d.ts +2 -26
  100. package/dist/overrides.d.ts +3 -1
  101. package/dist/overrides.js +2 -0
  102. package/dist/platform/exclusions.js +37 -6
  103. package/dist/platform/operation.js +4 -0
  104. package/dist/platform/ops/admin.ops.js +17 -2
  105. package/dist/platform/ops/catalog-admin.ops.d.ts +19 -1
  106. package/dist/platform/ops/catalog-admin.ops.js +85 -2
  107. package/dist/platform/ops/connectors.ops.js +2 -0
  108. package/dist/platform/ops/org-plugins.ops.d.ts +7 -1
  109. package/dist/platform/ops/org-plugins.ops.js +63 -4
  110. package/dist/platform/ops/org-settings.ops.js +2 -2
  111. package/dist/platform/ops/orgs.ops.js +1 -0
  112. package/dist/platform/ops/setup.ops.d.ts +24 -0
  113. package/dist/platform/ops/setup.ops.js +212 -0
  114. package/dist/platform/ops/ui.ops.js +1 -1
  115. package/dist/platform/platform-ops.module.d.ts +2 -1
  116. package/dist/platform/platform-ops.module.js +7 -0
  117. package/dist/platform/route-schemas.js +8 -0
  118. package/dist/roles/worker.module.js +4 -0
  119. package/dist/setup/kernel-checks.d.ts +91 -0
  120. package/dist/setup/kernel-checks.js +295 -0
  121. package/dist/setup/oauth-token-probe.d.ts +47 -0
  122. package/dist/setup/oauth-token-probe.js +108 -0
  123. package/dist/setup/redact-check.d.ts +18 -0
  124. package/dist/setup/redact-check.js +46 -0
  125. package/dist/setup/setup-check-registry.d.ts +86 -0
  126. package/dist/setup/setup-check-registry.js +75 -0
  127. package/dist/setup/setup-checks.controller.d.ts +28 -0
  128. package/dist/setup/setup-checks.controller.js +78 -0
  129. package/dist/setup/setup-checks.schemas.d.ts +38 -0
  130. package/dist/setup/setup-checks.schemas.js +43 -0
  131. package/dist/setup/setup-checks.service.d.ts +144 -0
  132. package/dist/setup/setup-checks.service.js +683 -0
  133. package/dist/setup/setup-guides.controller.d.ts +18 -0
  134. package/dist/setup/setup-guides.controller.js +48 -0
  135. package/dist/setup/setup-guides.schemas.d.ts +10 -0
  136. package/dist/setup/setup-guides.schemas.js +6 -0
  137. package/dist/setup/setup-guides.service.d.ts +126 -0
  138. package/dist/setup/setup-guides.service.js +315 -0
  139. package/dist/setup/setup.module.d.ts +8 -0
  140. package/dist/setup/setup.module.js +31 -0
  141. package/dist/themes/render-css.d.ts +2 -1
  142. package/dist/themes/render-css.js +5 -2
  143. package/dist/themes/theme-delivery.service.d.ts +4 -2
  144. package/dist/themes/theme-delivery.service.js +8 -3
  145. package/dist/themes/theme-state.d.ts +28 -7
  146. package/dist/themes/theme-state.js +24 -6
  147. package/dist/themes/themes-public.controller.d.ts +7 -2
  148. package/dist/themes/themes-public.controller.js +11 -5
  149. package/dist/themes/themes.service.js +2 -2
  150. package/dist/tools/connectors.controller.d.ts +28 -11
  151. package/dist/tools/connectors.controller.js +49 -14
  152. package/dist/tools/connectors.schemas.d.ts +4 -0
  153. package/dist/tools/connectors.schemas.js +4 -0
  154. package/dist/tools/connectors.service.d.ts +30 -2
  155. package/dist/tools/connectors.service.js +98 -3
  156. package/dist/tools/credentials.d.ts +20 -0
  157. package/dist/tools/credentials.js +90 -1
  158. package/dist/tools/oauth/tokens.d.ts +20 -0
  159. package/dist/tools/oauth/tokens.js +49 -24
  160. package/dist/tools/pipeline.d.ts +3 -2
  161. package/dist/ui/arrangement.d.ts +41 -50
  162. package/dist/ui/arrangement.js +88 -15
  163. package/dist/ui/registry.d.ts +13 -3
  164. package/dist/ui/registry.js +15 -4
  165. package/dist/ui/ui.schemas.d.ts +4 -0
  166. package/dist/ui/views.d.ts +36 -9
  167. package/dist/ui/views.js +58 -28
  168. package/package.json +3 -3
  169. package/src/migrations/0106_setup_guides_v2.sql +10 -0
  170. package/src/migrations/0107_extension_client_builds.sql +50 -0
@@ -6,6 +6,11 @@ import { ApiError } from "../errors.js";
6
6
  export interface ThemeRef {
7
7
  /** The `platform_themes` id (what settings and picks store). */
8
8
  id: string;
9
+ /**
10
+ * `"<plugin>/<theme file id>"`: the name the web's `theme=` state token and `useTheme()` carry
11
+ * (client extensions spec §7.3). Stable across versions, unlike `id`.
12
+ */
13
+ key: string;
9
14
  /** Its current version. */
10
15
  version: string;
11
16
  name: LocalizedName;
@@ -19,7 +24,7 @@ export interface ThemeRef {
19
24
  wordmark: string | null;
20
25
  faviconUrl: string | null;
21
26
  } | null;
22
- /** `/api/v1/themes/<id>/<version>/theme.css`. */
27
+ /** `/api/v1/themes/<id>/<version>/r<THEME_CSS_REVISION>/theme.css`. */
23
28
  cssUrl: string;
24
29
  }
25
30
  /** The four colors a theme picker draws per scheme. */
@@ -34,24 +39,40 @@ export type ListedTheme = ThemeRef & {
34
39
  swatches: Partial<Record<ThemeScheme, ThemeSwatch>>;
35
40
  };
36
41
  /**
37
- * The delivery URL of a theme version (spec §8): its stylesheet, or with `asset` (a path inside the
42
+ * The render revision of theme stylesheets, a segment of the stylesheet URL (`r<N>/theme.css`).
43
+ * The stylesheet is cached as immutable for a year, keyed by its URL: bump this whenever
44
+ * `renderThemeCss`'s output format changes, so no browser keeps an older format under a URL
45
+ * that now means the new one. The delivery route serves only the current revision (404
46
+ * `theme.not_found` otherwise). History: (none, `<version>/theme.css`) plain rules; 2: one
47
+ * `@layer theme { … }` block (client extensions spec §7.2).
48
+ */
49
+ export declare const THEME_CSS_REVISION = 2;
50
+ /**
51
+ * The delivery URL of a theme version (spec §8): its stylesheet (with the render revision,
52
+ * {@link THEME_CSS_REVISION}), or with `asset` (a path inside the
38
53
  * plugin as the theme file names it, e.g. `./fonts/MiSans.woff2`) that asset, as one path segment
39
54
  * (`assets/fonts%2FMiSans.woff2`; the route decodes it back to the stored path `fonts/MiSans.woff2`).
40
55
  * The one builder of these URLs: the listing, the stylesheet and the asset routes all use it.
41
56
  */
42
57
  export declare function themeUrl(id: string, version: string, asset?: string): string;
43
- /** The {@link ThemeRef} of version `version` of theme `id`. */
44
- export declare function themeRefOf(id: string, version: string, def: ParsedTheme): ThemeRef;
58
+ /**
59
+ * The {@link ThemeRef} of version `version` of theme `id` of plugin `plugin` (null only for a row
60
+ * without provenance: the key is then the file id alone).
61
+ */
62
+ export declare function themeRefOf(id: string, version: string, def: ParsedTheme, plugin: string | null): ThemeRef;
45
63
  /**
46
64
  * The swatches of each scheme the theme defines: its overrides merged onto the current built-in
47
65
  * values (a stored definition holds only the theme file's own values, themes spec §6).
48
66
  */
49
67
  export declare function themeSwatches(def: ParsedTheme): Partial<Record<ThemeScheme, ThemeSwatch>>;
50
- /** The current definitions of live themes `ids`, by id. */
51
- export declare function themeDefinitions(db: KernelSchemaDb, ids: readonly string[]): Promise<Map<string, {
68
+ /** A live theme's current definition and its plugin (null: no provenance). */
69
+ export interface ThemeDefinition {
52
70
  version: string;
53
71
  definition: ParsedTheme;
54
- }>>;
72
+ plugin: string | null;
73
+ }
74
+ /** The current definitions of live themes `ids`, by id. */
75
+ export declare function themeDefinitions(db: KernelSchemaDb, ids: readonly string[]): Promise<Map<string, ThemeDefinition>>;
55
76
  /** The live themes on in the org, sorted by id. */
56
77
  export declare function themesOnIn(db: KernelSchemaDb, orgId: string): Promise<ComponentRow[]>;
57
78
  /** The live theme `id` if it can be the platform default, else null. */
@@ -9,7 +9,17 @@ import { packagePath } from "../packages/tgz.js";
9
9
  /** The schemes in the order they are listed. */
10
10
  const SCHEMES = ["dark", "light"];
11
11
  /**
12
- * The delivery URL of a theme version (spec §8): its stylesheet, or with `asset` (a path inside the
12
+ * The render revision of theme stylesheets, a segment of the stylesheet URL (`r<N>/theme.css`).
13
+ * The stylesheet is cached as immutable for a year, keyed by its URL: bump this whenever
14
+ * `renderThemeCss`'s output format changes, so no browser keeps an older format under a URL
15
+ * that now means the new one. The delivery route serves only the current revision (404
16
+ * `theme.not_found` otherwise). History: (none, `<version>/theme.css`) plain rules; 2: one
17
+ * `@layer theme { … }` block (client extensions spec §7.2).
18
+ */
19
+ export const THEME_CSS_REVISION = 2;
20
+ /**
21
+ * The delivery URL of a theme version (spec §8): its stylesheet (with the render revision,
22
+ * {@link THEME_CSS_REVISION}), or with `asset` (a path inside the
13
23
  * plugin as the theme file names it, e.g. `./fonts/MiSans.woff2`) that asset, as one path segment
14
24
  * (`assets/fonts%2FMiSans.woff2`; the route decodes it back to the stored path `fonts/MiSans.woff2`).
15
25
  * The one builder of these URLs: the listing, the stylesheet and the asset routes all use it.
@@ -17,14 +27,18 @@ const SCHEMES = ["dark", "light"];
17
27
  export function themeUrl(id, version, asset) {
18
28
  const base = `/api/v1/themes/${encodeURIComponent(id)}/${encodeURIComponent(version)}`;
19
29
  return asset === undefined
20
- ? `${base}/theme.css`
30
+ ? `${base}/r${THEME_CSS_REVISION}/theme.css`
21
31
  : `${base}/assets/${encodeURIComponent(packagePath(asset))}`;
22
32
  }
23
- /** The {@link ThemeRef} of version `version` of theme `id`. */
24
- export function themeRefOf(id, version, def) {
33
+ /**
34
+ * The {@link ThemeRef} of version `version` of theme `id` of plugin `plugin` (null only for a row
35
+ * without provenance: the key is then the file id alone).
36
+ */
37
+ export function themeRefOf(id, version, def, plugin) {
25
38
  const url = (ref) => (ref === undefined ? null : themeUrl(id, version, ref));
26
39
  return {
27
40
  id,
41
+ key: plugin === null ? def.id : `${plugin}/${def.id}`,
28
42
  version,
29
43
  name: def.name,
30
44
  schemes: SCHEMES.filter((s) => def.schemes[s] !== undefined),
@@ -62,12 +76,16 @@ export async function themeDefinitions(db, ids) {
62
76
  const rows = await db
63
77
  .selectFrom("platform_themes as p")
64
78
  .innerJoin("platform_theme_versions as v", (j) => j.onRef("v.theme_id", "=", "p.id").onRef("v.version", "=", "p.current_version"))
65
- .select(["p.id", "p.current_version", "v.definition"])
79
+ .select(["p.id", "p.current_version", "p.plugin_name", "v.definition"])
66
80
  .where("p.id", "in", [...ids])
67
81
  .where("p.deleted_at", "is", null)
68
82
  .execute();
69
83
  for (const r of rows) {
70
- out.set(r.id, { version: String(r.current_version), definition: r.definition });
84
+ out.set(r.id, {
85
+ version: String(r.current_version),
86
+ definition: r.definition,
87
+ plugin: r.plugin_name,
88
+ });
71
89
  }
72
90
  return out;
73
91
  }
@@ -10,8 +10,13 @@ import { ThemesService } from "./themes.service.js";
10
10
  export declare class ThemeDeliveryController {
11
11
  private readonly delivery;
12
12
  constructor(delivery: ThemeDeliveryService);
13
- /** The version's stylesheet: scheme overrides, `@font-face` rules and font stacks. */
14
- css(id: string, version: string, reply: FastifyReply): Promise<void>;
13
+ /**
14
+ * The version's stylesheet: scheme overrides, `@font-face` rules and font stacks, in the render
15
+ * revision `revision` (`r<THEME_CSS_REVISION>`; only the current one is served). The URL without
16
+ * a revision (before revision 2) has no route: 404, so a browser's cached unlayered copy is never
17
+ * answered with the new format under an immutable cache.
18
+ */
19
+ css(id: string, version: string, revision: string, reply: FastifyReply): Promise<void>;
15
20
  /**
16
21
  * A font (`font/woff2`) or logo (`image/svg+xml`, sanitized at import, served under a CSP
17
22
  * sandbox; `image/png`) the version names; `file` is its plugin path as one segment
@@ -23,9 +23,14 @@ let ThemeDeliveryController = class ThemeDeliveryController {
23
23
  constructor(delivery) {
24
24
  this.delivery = delivery;
25
25
  }
26
- /** The version's stylesheet: scheme overrides, `@font-face` rules and font stacks. */
27
- async css(id, version, reply) {
28
- const css = await this.delivery.stylesheet(id, version);
26
+ /**
27
+ * The version's stylesheet: scheme overrides, `@font-face` rules and font stacks, in the render
28
+ * revision `revision` (`r<THEME_CSS_REVISION>`; only the current one is served). The URL without
29
+ * a revision (before revision 2) has no route: 404, so a browser's cached unlayered copy is never
30
+ * answered with the new format under an immutable cache.
31
+ */
32
+ async css(id, version, revision, reply) {
33
+ const css = await this.delivery.stylesheet(id, version, revision);
29
34
  await reply
30
35
  .header("content-type", "text/css; charset=utf-8")
31
36
  .header("x-content-type-options", "nosniff")
@@ -51,10 +56,11 @@ let ThemeDeliveryController = class ThemeDeliveryController {
51
56
  };
52
57
  __decorate([
53
58
  Public(),
54
- Get("theme.css"),
59
+ Get(":revision/theme.css"),
55
60
  __param(0, Param("id")),
56
61
  __param(1, Param("version")),
57
- __param(2, Res())
62
+ __param(2, Param("revision")),
63
+ __param(3, Res())
58
64
  ], ThemeDeliveryController.prototype, "css", null);
59
65
  __decorate([
60
66
  Public(),
@@ -32,7 +32,7 @@ let ThemesService = class ThemesService {
32
32
  if (!d)
33
33
  continue;
34
34
  out.push({
35
- ...themeRefOf(c.id, d.version, d.definition),
35
+ ...themeRefOf(c.id, d.version, d.definition, d.plugin),
36
36
  swatches: themeSwatches(d.definition),
37
37
  });
38
38
  }
@@ -53,7 +53,7 @@ let ThemesService = class ThemesService {
53
53
  if (id === null || (await eligiblePlatformDefault(this.db.db, id)) === null)
54
54
  return null;
55
55
  const d = (await themeDefinitions(this.db.db, [id])).get(id);
56
- return d ? themeRefOf(id, d.version, d.definition) : null;
56
+ return d ? themeRefOf(id, d.version, d.definition, d.plugin) : null;
57
57
  }
58
58
  /**
59
59
  * The listing, the viewer's resolved theme and the org's setting. Separate reads, not one
@@ -1,9 +1,10 @@
1
1
  import { z } from "zod";
2
+ import type { ConnectionGuideState } from "../extensions/plugin-guides.js";
2
3
  import { PluginIcons } from "../extensions/plugin-icons.js";
3
4
  import { type Principal } from "../identity/principal.js";
4
5
  import { type OrgContext } from "../orgs/org.guard.js";
5
6
  import { ConnectorAccountPolicies, type MemberToolPolicyView } from "./connector-account-policies.js";
6
- import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
7
+ import { ConnectorGuideAckSchema, CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
7
8
  import { type ConnectorDetail, type ConnectorPreview, type ConnectorSummary, ConnectorsService, type ToolIssue } from "./connectors.service.js";
8
9
  declare const CreateBody: z.ZodObject<{
9
10
  name: z.ZodString;
@@ -169,6 +170,14 @@ declare const UpdateBody: z.ZodObject<{
169
170
  }, z.core.$strict>>>;
170
171
  allowUnannotated: z.ZodOptional<z.ZodBoolean>;
171
172
  }, z.core.$strict>;
173
+ /**
174
+ * A connector as the org's pages show it: with its plugin's icon (`icon`, see `withIcons`), its
175
+ * template's setup guide and the drift since it was set up (`setupGuide`, `guideDrift`; spec
176
+ * 2026-11-09-setup-guides-v2-design.md §9.3, §11).
177
+ */
178
+ export type ConnectorPage<T extends ConnectorSummary = ConnectorDetail> = T & ConnectionGuideState & {
179
+ icon: string | null;
180
+ };
172
181
  /**
173
182
  * 连接器(`/api/v1/orgs/:orgId/connectors`):读与个人凭据用 `connectors.use`(访客起),安装 / 修改 / 删除 / 刷新 /
174
183
  * 组织凭据用 `connectors.manage`(管理员起);两者都仅限用户主体。
@@ -185,10 +194,11 @@ export declare class ConnectorsController {
185
194
  */
186
195
  private withIcons;
187
196
  list(org: OrgContext): Promise<{
188
- connectors: (ConnectorSummary & {
189
- icon: string | null;
190
- })[];
197
+ connectors: ConnectorPage<ConnectorSummary>[];
191
198
  }>;
199
+ /** Adds the icon and the setup guide fields to connector views (one lookup each for all). */
200
+ private withGuides;
201
+ private withGuide;
192
202
  /**
193
203
  * The connector kinds the org can create connectors of, each with the plugin providing it (the
194
204
  * install wizard offers only these; an empty list means no connector extension is enabled).
@@ -200,7 +210,7 @@ export declare class ConnectorsController {
200
210
  }[];
201
211
  }>;
202
212
  create(principal: Principal, org: OrgContext, body: z.infer<typeof CreateBody>): Promise<{
203
- connector: ConnectorDetail;
213
+ connector: ConnectorPage;
204
214
  issues: ToolIssue[];
205
215
  }>;
206
216
  /**
@@ -209,16 +219,14 @@ export declare class ConnectorsController {
209
219
  */
210
220
  preview(principal: Principal, org: OrgContext, body: z.infer<typeof PreviewBody>): Promise<ConnectorPreview>;
211
221
  get(principal: Principal, org: OrgContext, connectorId: string): Promise<{
212
- connector: ConnectorDetail & {
213
- icon: string | null;
214
- };
222
+ connector: ConnectorPage;
215
223
  }>;
216
224
  update(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof UpdateBody>): Promise<{
217
- connector: ConnectorDetail;
225
+ connector: ConnectorPage;
218
226
  }>;
219
227
  remove(principal: Principal, org: OrgContext, connectorId: string): Promise<void>;
220
228
  refresh(principal: Principal, org: OrgContext, connectorId: string): Promise<{
221
- connector: ConnectorDetail;
229
+ connector: ConnectorPage;
222
230
  issues: ToolIssue[];
223
231
  changes: {
224
232
  added: string[];
@@ -228,7 +236,16 @@ export declare class ConnectorsController {
228
236
  }>;
229
237
  /** 复核待复核的工具(漂移检测,spec 7.3):approve 采用新定义并启用,reject 停用并保留旧定义。 */
230
238
  review(principal: Principal, org: OrgContext, connectorId: string, name: string, body: z.infer<typeof ReviewBody>): Promise<{
231
- connector: ConnectorDetail;
239
+ connector: ConnectorPage;
240
+ }>;
241
+ /**
242
+ * 「我已更新应用」: the org admin updated the third-party app to the connector template's current
243
+ * setup guide (`{ hash }` of the guide shown) -> 200 `{ connector }`; 409
244
+ * `connector.guide_changed` when the guide moved on since, 404 `catalog.guide_not_found` for a
245
+ * connector without a template guide.
246
+ */
247
+ ackGuide(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof ConnectorGuideAckSchema>): Promise<{
248
+ connector: ConnectorPage;
232
249
  }>;
233
250
  setOrgCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
234
251
  setMyCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
@@ -17,7 +17,7 @@ import { ZBody } from "../infra/validation/zod-params.js";
17
17
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
18
18
  import { can } from "../orgs/roles.js";
19
19
  import { ConnectorAccountPolicies, } from "./connector-account-policies.js";
20
- import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
20
+ import { ConnectorGuideAckSchema, CredentialValue, MemberToolPoliciesBody, } from "./connectors.schemas.js";
21
21
  import { ConnectorsService, } from "./connectors.service.js";
22
22
  import { publicEndpoint } from "./endpoint.js";
23
23
  /**
@@ -88,6 +88,8 @@ class ConnectorCredentialBody extends createZodDto(CredentialValue, "ConnectorCr
88
88
  }
89
89
  class MemberToolPoliciesDto extends createZodDto(MemberToolPoliciesBody, "MemberToolPoliciesBody") {
90
90
  }
91
+ class ConnectorGuideAckBody extends createZodDto(ConnectorGuideAckSchema, "ConnectorGuideAckBody") {
92
+ }
91
93
  /**
92
94
  * 连接器(`/api/v1/orgs/:orgId/connectors`):读与个人凭据用 `connectors.use`(访客起),安装 / 修改 / 删除 / 刷新 /
93
95
  * 组织凭据用 `connectors.manage`(管理员起);两者都仅限用户主体。
@@ -115,8 +117,22 @@ let ConnectorsController = class ConnectorsController {
115
117
  }));
116
118
  }
117
119
  async list(org) {
118
- const rows = (await this.connectors.list(org.orgId)).map((a) => forViewer(org, a));
119
- return { connectors: await this.withIcons(org, rows) };
120
+ const list = (await this.connectors.list(org.orgId)).map((a) => forViewer(org, a));
121
+ return { connectors: await this.withGuides(org, list) };
122
+ }
123
+ /** Adds the icon and the setup guide fields to connector views (one lookup each for all). */
124
+ async withGuides(org, views) {
125
+ const [withIcons, states] = await Promise.all([
126
+ this.withIcons(org, views),
127
+ this.connectors.guideStates(org, views.map((v) => v.id)),
128
+ ]);
129
+ return withIcons.map((v) => ({
130
+ ...v,
131
+ ...(states.get(v.id) ?? { setupGuide: null, guideDrift: null }),
132
+ }));
133
+ }
134
+ async withGuide(org, view) {
135
+ return (await this.withGuides(org, [view]))[0];
120
136
  }
121
137
  /**
122
138
  * The connector kinds the org can create connectors of, each with the plugin providing it (the
@@ -127,7 +143,8 @@ let ConnectorsController = class ConnectorsController {
127
143
  }
128
144
  async create(principal, org, body) {
129
145
  const p = requireUser(principal);
130
- return this.connectors.create(org.orgId, p.account.id, body, patTokenId(p));
146
+ const r = await this.connectors.create(org.orgId, p.account.id, body, patTokenId(p));
147
+ return { ...r, connector: await this.withGuide(org, r.connector) };
131
148
  }
132
149
  /**
133
150
  * 预览(安装向导):读端点描述(认证提示、操作清单)并在能认证时列出工具,什么都不写。
@@ -139,15 +156,13 @@ let ConnectorsController = class ConnectorsController {
139
156
  }
140
157
  async get(principal, org, connectorId) {
141
158
  const p = requireUser(principal);
142
- const c = forViewer(org, await this.connectors.get(org.orgId, connectorId, p.account.id));
143
- const [connector] = await this.withIcons(org, [c]);
144
- return { connector: connector };
159
+ const view = forViewer(org, await this.connectors.get(org.orgId, connectorId, p.account.id));
160
+ return { connector: await this.withGuide(org, view) };
145
161
  }
146
162
  async update(principal, org, connectorId, body) {
147
163
  const p = requireUser(principal);
148
- return {
149
- connector: await this.connectors.update(org.orgId, connectorId, p.account.id, body, patTokenId(p)),
150
- };
164
+ const view = await this.connectors.update(org.orgId, connectorId, p.account.id, body, patTokenId(p));
165
+ return { connector: await this.withGuide(org, view) };
151
166
  }
152
167
  async remove(principal, org, connectorId) {
153
168
  const p = requireUser(principal);
@@ -155,14 +170,25 @@ let ConnectorsController = class ConnectorsController {
155
170
  }
156
171
  async refresh(principal, org, connectorId) {
157
172
  const p = requireUser(principal);
158
- return this.connectors.refresh(org.orgId, connectorId, p.account.id, patTokenId(p));
173
+ const r = await this.connectors.refresh(org.orgId, connectorId, p.account.id, patTokenId(p));
174
+ return { ...r, connector: await this.withGuide(org, r.connector) };
159
175
  }
160
176
  /** 复核待复核的工具(漂移检测,spec 7.3):approve 采用新定义并启用,reject 停用并保留旧定义。 */
161
177
  async review(principal, org, connectorId, name, body) {
162
178
  const p = requireUser(principal);
163
- return {
164
- connector: await this.connectors.reviewTool(org.orgId, connectorId, name, body.action, p.account.id, patTokenId(p)),
165
- };
179
+ const view = await this.connectors.reviewTool(org.orgId, connectorId, name, body.action, p.account.id, patTokenId(p));
180
+ return { connector: await this.withGuide(org, view) };
181
+ }
182
+ /**
183
+ * 「我已更新应用」: the org admin updated the third-party app to the connector template's current
184
+ * setup guide (`{ hash }` of the guide shown) -> 200 `{ connector }`; 409
185
+ * `connector.guide_changed` when the guide moved on since, 404 `catalog.guide_not_found` for a
186
+ * connector without a template guide.
187
+ */
188
+ async ackGuide(principal, org, connectorId, body) {
189
+ const p = requireUser(principal);
190
+ const view = await this.connectors.ackGuide({ orgId: org.orgId, role: org.role }, connectorId, body.hash, p.account.id, patTokenId(p));
191
+ return { connector: await this.withGuide(org, view) };
166
192
  }
167
193
  async setOrgCredential(principal, org, connectorId, body) {
168
194
  const p = requireUser(principal);
@@ -263,6 +289,15 @@ __decorate([
263
289
  __param(3, Param("name")),
264
290
  __param(4, ZBody(ConnectorToolReviewBody))
265
291
  ], ConnectorsController.prototype, "review", null);
292
+ __decorate([
293
+ Post(":connectorId/guide-ack"),
294
+ HttpCode(200),
295
+ OrgAction("connectors.manage"),
296
+ __param(0, CurrentPrincipal()),
297
+ __param(1, CurrentOrg()),
298
+ __param(2, Param("connectorId")),
299
+ __param(3, ZBody(ConnectorGuideAckBody))
300
+ ], ConnectorsController.prototype, "ackGuide", null);
266
301
  __decorate([
267
302
  Put(":connectorId/credentials/org"),
268
303
  HttpCode(204),
@@ -5,6 +5,10 @@ import type { RouteSchemas } from "../platform/route-schemas.js";
5
5
  * echoed); this only bounds its size.
6
6
  */
7
7
  export declare const CredentialValue: z.ZodRecord<z.ZodString, z.ZodUnknown>;
8
+ /** 「我已更新应用」: the admin drift hash of the connector template guide the person was shown. */
9
+ export declare const ConnectorGuideAckSchema: z.ZodObject<{
10
+ hash: z.ZodString;
11
+ }, z.core.$strict>;
8
12
  /** `:connectorId` as the routes take it (`@Param`). */
9
13
  export declare const ConnectorPathParams: z.ZodObject<{
10
14
  connectorId: z.ZodString;
@@ -6,6 +6,10 @@ import { z } from "zod";
6
6
  export const CredentialValue = z
7
7
  .record(z.string().max(100), z.unknown())
8
8
  .refine((v) => JSON.stringify(v).length <= 16_384, { message: "Credential is too large" });
9
+ /** 「我已更新应用」: the admin drift hash of the connector template guide the person was shown. */
10
+ export const ConnectorGuideAckSchema = z
11
+ .object({ hash: z.string().regex(/^[0-9a-f]{64}$/) })
12
+ .strict();
9
13
  /** `:connectorId` as the routes take it (`@Param`). */
10
14
  export const ConnectorPathParams = z.object({ connectorId: z.string().min(1) });
11
15
  export const CONNECTOR_ROUTES = [
@@ -1,9 +1,11 @@
1
1
  import { type AuthHint, type ConnectorOperation, type ToolAnnotations } from "@capybara-hq/plugin-sdk";
2
+ import { type ConnectionGuideState, PluginGuides } from "../extensions/plugin-guides.js";
2
3
  import { ExtensionRegistry } from "../extensions/registry.js";
3
4
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
4
5
  import { KernelDb } from "../infra/database/kernel-db.js";
5
6
  import { TransactionHost } from "../infra/database/transaction-host.js";
6
7
  import { OrgLock } from "../orgs/org-lock.js";
8
+ import type { OrgRole } from "../orgs/roles.js";
7
9
  import { type ConnectorAuth, type CredentialOwner, Credentials, type StaticSecret } from "./credentials.js";
8
10
  import { type DriftChanges, ToolDrift, type ToolIssue } from "./drift.js";
9
11
  import { type OAuthClientRecord, type OAuthTokenRecord } from "./oauth/tokens.js";
@@ -212,11 +214,15 @@ export declare class ConnectorsService {
212
214
  /** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
213
215
  private readonly orgLock;
214
216
  readonly txHost: TransactionHost;
217
+ /** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
218
+ private readonly guides;
215
219
  constructor(kdb: KernelDb, registry: ToolRegistry,
216
220
  /** Extension manifests (a slug may not take an extension's name). */
217
221
  extensions: Pick<ExtensionRegistry, "manifests">, credentials: Credentials, config: Pick<KernelConfigService, "get">, drift: ToolDrift,
218
222
  /** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
219
- orgLock: Pick<OrgLock, "lockAndAuthorize">, txHost: TransactionHost);
223
+ orgLock: Pick<OrgLock, "lockAndAuthorize">, txHost: TransactionHost,
224
+ /** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
225
+ guides: Pick<PluginGuides, "templateAck" | "connectorStates">);
220
226
  /** The kernel-schema handle of the current context (its transaction, else the pool). */
221
227
  private get db();
222
228
  /**
@@ -305,7 +311,29 @@ export declare class ConnectorsService {
305
311
  private row;
306
312
  private summary;
307
313
  private detail;
308
- /** 本组织未删除的连接器(按创建时间)。 */
314
+ /**
315
+ * The setup guide fields of the org's connectors (`setupGuide`, `guideDrift`; spec
316
+ * 2026-11-09-setup-guides-v2-design.md §9.3, §11): resolved through each connector's platform
317
+ * template to its item's guide for that template; custom connectors get nulls.
318
+ */
319
+ guideStates(org: {
320
+ orgId: string;
321
+ role: OrgRole;
322
+ }, connectorIds: string[]): Promise<Map<string, ConnectionGuideState>>;
323
+ /**
324
+ * 「我已更新应用」 for a connector made from a template: records the template's current guide as
325
+ * the one it is set up with. `hash` is the admin drift hash of the guide the person was shown;
326
+ * when the plugin moved on since, 409 `connector.guide_changed` (show the new guide first). A
327
+ * connector without a template guide, or whose item the org's catalog does not show -> 404
328
+ * `catalog.guide_not_found`.
329
+ */
330
+ ackGuide(org: {
331
+ orgId: string;
332
+ role: OrgRole;
333
+ }, connectorId: string, hash: string, actorAccountId: string, actorTokenId?: string): Promise<ConnectorDetail>;
334
+ /** The row and audit of {@link ackGuide}, in one transaction under the org lock. */
335
+ private ackIn;
336
+ /** The org's connectors that are not deleted (by creation time). */
309
337
  list(orgId: string): Promise<ConnectorSummary[]>;
310
338
  get(orgId: string, connectorId: string, viewerAccountId: string): Promise<ConnectorDetail>;
311
339
  /** 改名、启停连接器、工具启停与默认策略、批量把无注解工具改为 allow(`connectors.manage`)。 */
@@ -15,6 +15,7 @@ import { z } from "zod";
15
15
  import { connectorTemplateState } from "../catalog/component-state.js";
16
16
  import { assertItemInstallable } from "../catalog/request-rules.js";
17
17
  import { ApiError } from "../errors.js";
18
+ import { PluginGuides, } from "../extensions/plugin-guides.js";
18
19
  import { ExtensionRegistry } from "../extensions/registry.js";
19
20
  import { audit } from "../identity/audit.js";
20
21
  import { assertCustomAllowed } from "../identity/settings.js";
@@ -93,11 +94,14 @@ let ConnectorsService = class ConnectorsService {
93
94
  drift;
94
95
  orgLock;
95
96
  txHost;
97
+ guides;
96
98
  constructor(kdb, registry,
97
99
  /** Extension manifests (a slug may not take an extension's name). */
98
100
  extensions, credentials, config, drift,
99
101
  /** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
100
- orgLock, txHost) {
102
+ orgLock, txHost,
103
+ /** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
104
+ guides) {
101
105
  this.kdb = kdb;
102
106
  this.registry = registry;
103
107
  this.extensions = extensions;
@@ -106,6 +110,7 @@ let ConnectorsService = class ConnectorsService {
106
110
  this.drift = drift;
107
111
  this.orgLock = orgLock;
108
112
  this.txHost = txHost;
113
+ this.guides = guides;
109
114
  }
110
115
  /** The kernel-schema handle of the current context (its transaction, else the pool). */
111
116
  get db() {
@@ -366,6 +371,11 @@ let ConnectorsService = class ConnectorsService {
366
371
  return this.credentials.injectionFor(id, auth, secret);
367
372
  }, { orgId, slug });
368
373
  }
374
+ // the guide the wizard and 补充配置 showed: the template's current one (spec §11)
375
+ const tpl = input.template;
376
+ const guideAck = tpl?.templateId !== undefined
377
+ ? await this.guides.templateAck(tpl.itemId, tpl.templateId)
378
+ : null;
369
379
  return this.createIn(orgId, actorAccountId, input, actorTokenId, {
370
380
  id,
371
381
  slug,
@@ -374,6 +384,7 @@ let ConnectorsService = class ConnectorsService {
374
384
  secret,
375
385
  oauth,
376
386
  listed,
387
+ guideAck,
377
388
  });
378
389
  }
379
390
  /** The org-locked, re-checked insert of {@link create} (the outbound checks ran before). */
@@ -434,6 +445,8 @@ let ConnectorsService = class ConnectorsService {
434
445
  catalog_item_id: input.template?.itemId ?? null,
435
446
  catalog_version: input.template?.version ?? null,
436
447
  platform_template_id: platformTemplate?.id ?? null,
448
+ // linked to its template only through the platform component (drift is judged there)
449
+ guide_ack: platformTemplate !== undefined && c.guideAck !== null ? JSON.stringify(c.guideAck) : null,
437
450
  // 授权码连接器安装时没有快照:模板的默认策略留到首个快照时应用
438
451
  template_tool_policies: input.template?.defaultToolPolicies
439
452
  ? JSON.stringify(input.template.defaultToolPolicies)
@@ -826,7 +839,85 @@ let ConnectorsService = class ConnectorsService {
826
839
  credential: { ownership: auth.type === "none" ? null : auth.ownership, status },
827
840
  };
828
841
  }
829
- /** 本组织未删除的连接器(按创建时间)。 */
842
+ /**
843
+ * The setup guide fields of the org's connectors (`setupGuide`, `guideDrift`; spec
844
+ * 2026-11-09-setup-guides-v2-design.md §9.3, §11): resolved through each connector's platform
845
+ * template to its item's guide for that template; custom connectors get nulls.
846
+ */
847
+ async guideStates(org, connectorIds) {
848
+ if (connectorIds.length === 0)
849
+ return new Map();
850
+ const rows = await this.db
851
+ .selectFrom("connectors as c")
852
+ .leftJoin("platform_connector_templates as t", "t.id", "c.platform_template_id")
853
+ .select(["c.id", "c.guide_ack", "t.catalog_item_id", "t.template_id"])
854
+ .where("c.org_id", "=", org.orgId)
855
+ .where("c.id", "in", [...new Set(connectorIds)])
856
+ .where("c.deleted_at", "is", null)
857
+ .execute();
858
+ return this.guides.connectorStates(rows.map((r) => ({
859
+ id: r.id,
860
+ itemId: r.catalog_item_id ?? null,
861
+ templateId: r.template_id ?? null,
862
+ guideAck: r.guide_ack ?? null,
863
+ })), org);
864
+ }
865
+ /**
866
+ * 「我已更新应用」 for a connector made from a template: records the template's current guide as
867
+ * the one it is set up with. `hash` is the admin drift hash of the guide the person was shown;
868
+ * when the plugin moved on since, 409 `connector.guide_changed` (show the new guide first). A
869
+ * connector without a template guide, or whose item the org's catalog does not show -> 404
870
+ * `catalog.guide_not_found`.
871
+ */
872
+ async ackGuide(org, connectorId, hash, actorAccountId, actorTokenId) {
873
+ const { orgId } = org;
874
+ const r = await this.db
875
+ .selectFrom("connectors as c")
876
+ .leftJoin("platform_connector_templates as t", "t.id", "c.platform_template_id")
877
+ .select(["c.id", "t.catalog_item_id", "t.template_id"])
878
+ .where("c.id", "=", connectorId)
879
+ .where("c.org_id", "=", orgId)
880
+ .where("c.deleted_at", "is", null)
881
+ .executeTakeFirst();
882
+ if (!r)
883
+ throw notFound();
884
+ const ack = r.catalog_item_id && r.template_id
885
+ ? // the guide read's visibility: an item the org's catalog does not show has no guide
886
+ await this.guides.templateAck(r.catalog_item_id, r.template_id, org)
887
+ : null;
888
+ if (ack === null) {
889
+ throw new ApiError("catalog.guide_not_found", "The connector has no template setup guide");
890
+ }
891
+ if (ack.hash !== hash) {
892
+ throw new ApiError("connector.guide_changed", "The connector template's setup guide changed since it was shown; review the new guide first");
893
+ }
894
+ return this.ackIn(orgId, connectorId, ack, actorAccountId, actorTokenId);
895
+ }
896
+ /** The row and audit of {@link ackGuide}, in one transaction under the org lock. */
897
+ async ackIn(orgId, connectorId, ack, actorAccountId, actorTokenId) {
898
+ const t = this.db;
899
+ await this.orgLock.lockAndAuthorize(orgId, actorAccountId, "connectors.manage");
900
+ const updated = await t
901
+ .updateTable("connectors")
902
+ .set({ guide_ack: JSON.stringify(ack), updated_at: sql `now()` })
903
+ .where("id", "=", connectorId)
904
+ .where("org_id", "=", orgId)
905
+ .where("deleted_at", "is", null)
906
+ .returning("id")
907
+ .executeTakeFirst();
908
+ if (!updated)
909
+ throw notFound();
910
+ await audit(t, {
911
+ actorAccountId,
912
+ ...(actorTokenId !== undefined && { actorTokenId }),
913
+ orgId,
914
+ action: "connector.guide_confirmed",
915
+ target: connectorId,
916
+ detail: { guideId: ack.guideId, version: ack.version },
917
+ });
918
+ return this.detail(t, orgId, connectorId, actorAccountId);
919
+ }
920
+ /** The org's connectors that are not deleted (by creation time). */
830
921
  async list(orgId) {
831
922
  const rows = await this.db
832
923
  .selectFrom("connectors")
@@ -1236,6 +1327,9 @@ __decorate([
1236
1327
  __decorate([
1237
1328
  Transactional({ propagation: "mandatory" })
1238
1329
  ], ConnectorsService.prototype, "applyTemplateIn", null);
1330
+ __decorate([
1331
+ Transactional()
1332
+ ], ConnectorsService.prototype, "ackIn", null);
1239
1333
  __decorate([
1240
1334
  Transactional()
1241
1335
  ], ConnectorsService.prototype, "update", null);
@@ -1269,6 +1363,7 @@ ConnectorsService = __decorate([
1269
1363
  __param(4, Inject(KernelConfigService)),
1270
1364
  __param(5, Inject(ToolDrift)),
1271
1365
  __param(6, Inject(OrgLock)),
1272
- __param(7, Inject(TransactionHost))
1366
+ __param(7, Inject(TransactionHost)),
1367
+ __param(8, Inject(PluginGuides))
1273
1368
  ], ConnectorsService);
1274
1369
  export { ConnectorsService };