@capybara-hq/kernel 0.1.0-rc.12 → 0.1.0-rc.14

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 (267) hide show
  1. package/dist/admin/admin.schemas.d.ts +13 -0
  2. package/dist/agents/agent-lookup.d.ts +5 -0
  3. package/dist/agents/agent-lookup.js +15 -0
  4. package/dist/background/background.module.js +2 -2
  5. package/dist/background/launch-lock.d.ts +1 -1
  6. package/dist/background/launch-lock.js +1 -1
  7. package/dist/background/poll.job.d.ts +2 -2
  8. package/dist/background/poll.job.js +10 -10
  9. package/dist/background/stop.d.ts +1 -1
  10. package/dist/background/stop.js +3 -3
  11. package/dist/background/store.d.ts +5 -5
  12. package/dist/background/store.js +5 -5
  13. package/dist/background/tasks.service.d.ts +6 -6
  14. package/dist/background/tasks.service.js +13 -13
  15. package/dist/catalog/admin-catalog.controller.d.ts +13 -2
  16. package/dist/catalog/admin-catalog.controller.js +24 -3
  17. package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
  18. package/dist/catalog/admin-catalog.schemas.js +7 -0
  19. package/dist/catalog/catalog-content.d.ts +9 -1
  20. package/dist/catalog/catalog-content.js +2 -1
  21. package/dist/catalog/catalog-files.d.ts +3 -2
  22. package/dist/catalog/catalog-files.js +8 -5
  23. package/dist/catalog/component-scope.service.d.ts +14 -1
  24. package/dist/catalog/component-scope.service.js +12 -2
  25. package/dist/catalog/marketplaces.service.d.ts +1 -1
  26. package/dist/catalog/org-catalog.controller.d.ts +5 -4
  27. package/dist/catalog/org-catalog.controller.js +5 -4
  28. package/dist/catalog/org-catalog.schemas.d.ts +18 -2
  29. package/dist/catalog/org-catalog.schemas.js +16 -2
  30. package/dist/catalog/org-catalog.service.js +8 -2
  31. package/dist/catalog/plugin-import.js +1 -1
  32. package/dist/catalog/plugin-package.js +37 -9
  33. package/dist/catalog/plugin-upgrade.d.ts +15 -1
  34. package/dist/catalog/plugin-upgrade.js +18 -5
  35. package/dist/channels/channels.module.js +2 -2
  36. package/dist/channels/connections.controller.d.ts +1 -1
  37. package/dist/channels/connections.controller.js +1 -1
  38. package/dist/channels/connections.service.d.ts +22 -5
  39. package/dist/channels/connections.service.js +56 -10
  40. package/dist/channels/inbound.d.ts +8 -8
  41. package/dist/channels/inbound.js +31 -31
  42. package/dist/channels/phrases.d.ts +27 -27
  43. package/dist/channels/phrases.js +27 -27
  44. package/dist/channels/webhook.d.ts +21 -1
  45. package/dist/channels/webhook.js +39 -4
  46. package/dist/config/docs.js +1 -1
  47. package/dist/config/schema.d.ts +2 -2
  48. package/dist/config/schema.js +21 -21
  49. package/dist/conversations/conversations.controller.d.ts +11 -11
  50. package/dist/conversations/conversations.controller.js +15 -15
  51. package/dist/conversations/conversations.module.d.ts +1 -1
  52. package/dist/conversations/conversations.module.js +7 -7
  53. package/dist/conversations/conversations.schemas.js +1 -1
  54. package/dist/conversations/conversations.service.d.ts +14 -14
  55. package/dist/conversations/conversations.service.js +19 -19
  56. package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
  57. package/dist/conversations/{environment-status.js → device-status.js} +18 -18
  58. package/dist/conversations/event-log.d.ts +1 -1
  59. package/dist/conversations/event-log.js +1 -1
  60. package/dist/conversations/events.d.ts +1 -1
  61. package/dist/conversations/events.js +2 -2
  62. package/dist/ctx/create-ctx.d.ts +26 -3
  63. package/dist/ctx/create-ctx.js +134 -26
  64. package/dist/db/kernel-schema.d.ts +37 -25
  65. package/dist/{environments → devices}/access.d.ts +19 -19
  66. package/dist/{environments → devices}/access.js +28 -28
  67. package/dist/devices/conversation-devices.d.ts +75 -0
  68. package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
  69. package/dist/devices/devices.controller.d.ts +36 -0
  70. package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
  71. package/dist/devices/devices.module.d.ts +5 -13
  72. package/dist/devices/devices.module.js +16 -29
  73. package/dist/devices/devices.schemas.d.ts +3 -0
  74. package/dist/devices/devices.schemas.js +14 -0
  75. package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
  76. package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
  77. package/dist/devices/dto.d.ts +13 -54
  78. package/dist/devices/dto.js +7 -39
  79. package/dist/devices/label.d.ts +11 -0
  80. package/dist/{environments → devices}/label.js +7 -7
  81. package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
  82. package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
  83. package/dist/errors.d.ts +29 -25
  84. package/dist/errors.js +29 -25
  85. package/dist/evals/recorded-tools.d.ts +2 -2
  86. package/dist/evals/recorded-tools.js +6 -6
  87. package/dist/extensions/admin-ops.js +1 -0
  88. package/dist/extensions/admin-view.js +2 -0
  89. package/dist/extensions/effective-config.d.ts +6 -0
  90. package/dist/extensions/effective-config.js +18 -7
  91. package/dist/extensions/guide-places.d.ts +66 -0
  92. package/dist/extensions/guide-places.js +152 -0
  93. package/dist/extensions/host.d.ts +13 -2
  94. package/dist/extensions/host.js +27 -6
  95. package/dist/extensions/instance.d.ts +17 -2
  96. package/dist/extensions/instance.js +21 -0
  97. package/dist/extensions/ledger.d.ts +1 -1
  98. package/dist/extensions/permission-set.d.ts +16 -2
  99. package/dist/extensions/permission-set.js +17 -3
  100. package/dist/extensions/permission-view.d.ts +10 -1
  101. package/dist/extensions/plugin-guides.d.ts +207 -54
  102. package/dist/extensions/plugin-guides.js +436 -165
  103. package/dist/files/previewers.d.ts +1 -1
  104. package/dist/files/previewers.js +1 -1
  105. package/dist/http/api.module.js +16 -6
  106. package/dist/http/gateway-routes.d.ts +1 -1
  107. package/dist/http/gateway-routes.js +1 -1
  108. package/dist/identity/settings.d.ts +32 -0
  109. package/dist/identity/settings.js +26 -2
  110. package/dist/infra/config/kernel-config.service.js +4 -4
  111. package/dist/infra/lock/lock.service.d.ts +8 -0
  112. package/dist/infra/lock/lock.service.js +27 -0
  113. package/dist/infra/throttler/named-throttlers.d.ts +4 -1
  114. package/dist/infra/throttler/named-throttlers.js +16 -1
  115. package/dist/infra/throttler/throttler.guard.js +1 -1
  116. package/dist/kernel.module.js +3 -3
  117. package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
  118. package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
  119. package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
  120. package/dist/{devices → local-devices}/device-auth.js +51 -53
  121. package/dist/local-devices/device-auth.module.d.ts +7 -0
  122. package/dist/{devices → local-devices}/device-auth.module.js +5 -5
  123. package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
  124. package/dist/{devices → local-devices}/device-routes.js +2 -2
  125. package/dist/local-devices/dto.d.ts +66 -0
  126. package/dist/local-devices/dto.js +44 -0
  127. package/dist/{devices → local-devices}/keys.d.ts +11 -11
  128. package/dist/{devices → local-devices}/keys.js +11 -11
  129. package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
  130. package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
  131. package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
  132. package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
  133. package/dist/local-devices/local-devices.module.d.ts +16 -0
  134. package/dist/local-devices/local-devices.module.js +44 -0
  135. package/dist/{devices → local-devices}/machines.d.ts +26 -26
  136. package/dist/{devices → local-devices}/machines.js +71 -71
  137. package/dist/{devices → local-devices}/relay.d.ts +15 -15
  138. package/dist/{devices → local-devices}/relay.js +55 -55
  139. package/dist/{devices → local-devices}/targets.d.ts +8 -8
  140. package/dist/{devices → local-devices}/targets.js +14 -14
  141. package/dist/orgs/offboarding.d.ts +6 -6
  142. package/dist/orgs/offboarding.js +12 -12
  143. package/dist/orgs/settings.service.d.ts +28 -0
  144. package/dist/orgs/settings.service.js +20 -1
  145. package/dist/overrides.d.ts +3 -1
  146. package/dist/overrides.js +2 -0
  147. package/dist/platform/exclusions.js +48 -8
  148. package/dist/platform/operation.js +10 -2
  149. package/dist/platform/ops/admin.ops.js +8 -2
  150. package/dist/platform/ops/connectors.ops.js +2 -0
  151. package/dist/platform/ops/conversations.ops.d.ts +3 -3
  152. package/dist/platform/ops/conversations.ops.js +9 -9
  153. package/dist/platform/ops/devices.ops.d.ts +26 -0
  154. package/dist/platform/ops/devices.ops.js +311 -0
  155. package/dist/platform/ops/org-settings.ops.js +2 -2
  156. package/dist/platform/ops/orgs.ops.js +1 -0
  157. package/dist/platform/ops/setup.ops.d.ts +24 -0
  158. package/dist/platform/ops/setup.ops.js +212 -0
  159. package/dist/platform/ops/ui.ops.d.ts +8 -0
  160. package/dist/platform/ops/ui.ops.js +41 -0
  161. package/dist/platform/platform-ops.module.d.ts +4 -2
  162. package/dist/platform/platform-ops.module.js +13 -5
  163. package/dist/platform/route-schemas.js +11 -3
  164. package/dist/roles/gateway-role.d.ts +3 -3
  165. package/dist/roles/gateway-role.js +4 -4
  166. package/dist/roles/gateway.module.d.ts +1 -1
  167. package/dist/roles/gateway.module.js +5 -5
  168. package/dist/roles/worker.module.js +1 -1
  169. package/dist/runtime/context/build.d.ts +1 -1
  170. package/dist/runtime/context/build.js +6 -6
  171. package/dist/runtime/context/compaction.js +1 -1
  172. package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
  173. package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
  174. package/dist/runtime/maintenance.d.ts +1 -1
  175. package/dist/sandbox/jobs.d.ts +13 -13
  176. package/dist/sandbox/jobs.js +20 -20
  177. package/dist/sandbox/leases.d.ts +9 -9
  178. package/dist/sandbox/leases.js +14 -14
  179. package/dist/sandbox/providers.d.ts +8 -8
  180. package/dist/sandbox/providers.js +8 -8
  181. package/dist/sandbox/sandbox.module.d.ts +1 -1
  182. package/dist/sandbox/sandbox.module.js +1 -1
  183. package/dist/sandbox/sandbox.service.d.ts +76 -76
  184. package/dist/sandbox/sandbox.service.js +230 -230
  185. package/dist/setup/kernel-checks.d.ts +91 -0
  186. package/dist/setup/kernel-checks.js +295 -0
  187. package/dist/setup/oauth-token-probe.d.ts +47 -0
  188. package/dist/setup/oauth-token-probe.js +108 -0
  189. package/dist/setup/redact-check.d.ts +18 -0
  190. package/dist/setup/redact-check.js +46 -0
  191. package/dist/setup/setup-check-registry.d.ts +86 -0
  192. package/dist/setup/setup-check-registry.js +75 -0
  193. package/dist/setup/setup-checks.controller.d.ts +28 -0
  194. package/dist/setup/setup-checks.controller.js +78 -0
  195. package/dist/setup/setup-checks.schemas.d.ts +38 -0
  196. package/dist/setup/setup-checks.schemas.js +43 -0
  197. package/dist/setup/setup-checks.service.d.ts +144 -0
  198. package/dist/setup/setup-checks.service.js +683 -0
  199. package/dist/setup/setup-guides.controller.d.ts +18 -0
  200. package/dist/setup/setup-guides.controller.js +48 -0
  201. package/dist/setup/setup-guides.schemas.d.ts +10 -0
  202. package/dist/setup/setup-guides.schemas.js +6 -0
  203. package/dist/setup/setup-guides.service.d.ts +126 -0
  204. package/dist/setup/setup-guides.service.js +315 -0
  205. package/dist/setup/setup.module.d.ts +8 -0
  206. package/dist/setup/setup.module.js +31 -0
  207. package/dist/tools/builtins/builtin-tools.d.ts +2 -2
  208. package/dist/tools/builtins/builtin-tools.js +5 -5
  209. package/dist/tools/builtins/platform.js +1 -1
  210. package/dist/tools/builtins/sandbox/bash.js +6 -6
  211. package/dist/tools/builtins/sandbox/common.d.ts +25 -25
  212. package/dist/tools/builtins/sandbox/common.js +51 -51
  213. package/dist/tools/builtins/sandbox/edit.js +2 -2
  214. package/dist/tools/builtins/sandbox/index.d.ts +1 -1
  215. package/dist/tools/builtins/sandbox/index.js +1 -1
  216. package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
  217. package/dist/tools/builtins/sandbox/task-output.js +10 -10
  218. package/dist/tools/builtins/sandbox/task-stop.js +2 -2
  219. package/dist/tools/builtins/sandbox/write.js +2 -2
  220. package/dist/tools/builtins/skill-load.d.ts +1 -1
  221. package/dist/tools/builtins/skill-load.js +2 -2
  222. package/dist/tools/connectors.controller.d.ts +28 -11
  223. package/dist/tools/connectors.controller.js +49 -14
  224. package/dist/tools/connectors.schemas.d.ts +4 -0
  225. package/dist/tools/connectors.schemas.js +4 -0
  226. package/dist/tools/connectors.service.d.ts +30 -2
  227. package/dist/tools/connectors.service.js +98 -3
  228. package/dist/tools/credentials.d.ts +20 -0
  229. package/dist/tools/credentials.js +90 -1
  230. package/dist/tools/oauth/tokens.d.ts +20 -0
  231. package/dist/tools/oauth/tokens.js +49 -24
  232. package/dist/tools/pipeline.js +4 -3
  233. package/dist/tools/registry.d.ts +2 -2
  234. package/dist/tools/tools.module.js +3 -3
  235. package/dist/ui/admin-ui.controller.d.ts +56 -0
  236. package/dist/ui/admin-ui.controller.js +192 -0
  237. package/dist/ui/arrangement.d.ts +74 -0
  238. package/dist/ui/arrangement.js +98 -0
  239. package/dist/ui/embed-token.d.ts +2 -2
  240. package/dist/ui/embed-token.js +30 -5
  241. package/dist/ui/registry.d.ts +29 -14
  242. package/dist/ui/registry.js +31 -10
  243. package/dist/ui/slots.controller.d.ts +11 -0
  244. package/dist/ui/slots.controller.js +25 -0
  245. package/dist/ui/ui-stream.d.ts +20 -3
  246. package/dist/ui/ui-stream.js +25 -7
  247. package/dist/ui/ui.schemas.d.ts +66 -0
  248. package/dist/ui/ui.schemas.js +65 -0
  249. package/dist/ui/views.controller.d.ts +35 -49
  250. package/dist/ui/views.controller.js +48 -70
  251. package/dist/ui/views.d.ts +84 -18
  252. package/dist/ui/views.js +201 -38
  253. package/package.json +3 -3
  254. package/src/migrations/0105_devices_rename.sql +136 -0
  255. package/src/migrations/0106_setup_guides_v2.sql +10 -0
  256. package/dist/devices/device-auth.module.d.ts +0 -7
  257. package/dist/environments/conversation-environments.d.ts +0 -75
  258. package/dist/environments/dto.d.ts +0 -25
  259. package/dist/environments/dto.js +0 -12
  260. package/dist/environments/environments.controller.d.ts +0 -36
  261. package/dist/environments/environments.module.d.ts +0 -8
  262. package/dist/environments/environments.module.js +0 -31
  263. package/dist/environments/environments.schemas.d.ts +0 -3
  264. package/dist/environments/environments.schemas.js +0 -14
  265. package/dist/environments/label.d.ts +0 -11
  266. package/dist/platform/ops/environments.ops.d.ts +0 -26
  267. package/dist/platform/ops/environments.ops.js +0 -311
@@ -0,0 +1,152 @@
1
+ // The kernel side of the setup guide places (spec 2026-11-09-setup-guides-v2-design.md §6.3, §9):
2
+ // the SDK registry (`SETUP_GUIDE_PLACE_INFO`) says which modes, audiences and variables a place
3
+ // has; this module adds, per place, the org action each audience needs, the target lookup (a live
4
+ // object of the org on the guide's plugin, else 404) and the variable values with the reason a
5
+ // known variable has no value. No value ever comes from a target's config, a secret, a token or a
6
+ // client id: only names, ids, versions and the two URL settings.
7
+ import { SETUP_GUIDE_PLACE_INFO, } from "@capybara-hq/plugin-sdk";
8
+ import { webhookBase, webhookUrlOf } from "../channels/webhook-url.js";
9
+ import { ApiError } from "../errors.js";
10
+ import { OAUTH_CALLBACK_PATH } from "../tools/oauth/flow-paths.js";
11
+ /** The org action each audience of a place needs besides `catalog.read` (spec §9.1). */
12
+ export const PLACE_ACTIONS = {
13
+ "channel-connection": { admin: "channels.manage" },
14
+ "connector-template": { admin: "connectors.manage", member: "connectors.use" },
15
+ };
16
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17
+ const invalidQuery = (path, message) => new ApiError("validation_failed", "Request validation failed", [{ path, message }]);
18
+ /** The target parameters each place takes; another place's parameter is 400. */
19
+ const TARGET_PARAMS = {
20
+ "channel-connection": ["connectionId", "name"],
21
+ "connector-template": ["connectorId"],
22
+ };
23
+ /**
24
+ * The variable values of a place for one rendering (spec §6.3): the common ones, then the place's
25
+ * own from its target. Throws 400 `validation_failed` for another place's target parameter and 404
26
+ * (`channel.not_found`, `connector.not_found`) for a target that is not a live object of the org on
27
+ * the guide's plugin.
28
+ */
29
+ export async function resolvePlace(ctx) {
30
+ const place = ctx.guide.place;
31
+ for (const key of Object.keys(ctx.query)) {
32
+ if (ctx.query[key] !== undefined && !TARGET_PARAMS[place].includes(key)) {
33
+ throw invalidQuery(key, `${key} is not a target of a ${place} guide`);
34
+ }
35
+ }
36
+ const publicUrl = ctx.config.http.publicUrl?.replace(/\/+$/, "") ?? null;
37
+ const values = {
38
+ publicUrl,
39
+ orgName: ctx.org?.name ?? null,
40
+ pluginName: ctx.item.name,
41
+ pluginVersion: ctx.item.version,
42
+ };
43
+ const reasons = {};
44
+ const signIn = publicUrl === null
45
+ ? null
46
+ : `${publicUrl}/api/v1/auth/sso/${encodeURIComponent(ctx.item.name)}/callback`;
47
+ let target = null;
48
+ let storedMode = null;
49
+ if (place === "channel-connection") {
50
+ const connection = ctx.query.connectionId === undefined
51
+ ? undefined
52
+ : await liveConnection(ctx, ctx.query.connectionId);
53
+ const base = webhookBase(ctx.config);
54
+ values.webhookUrl =
55
+ connection !== undefined && base !== undefined ? webhookUrlOf(base, connection.id) : null;
56
+ reasons.webhookUrl = connection === undefined ? "not_saved" : "webhook_base";
57
+ values.signInCallbackUrl = signIn;
58
+ const typed = ctx.query.name?.trim();
59
+ values.connectionName =
60
+ connection?.name ?? (typed === undefined || typed === "" ? null : typed);
61
+ reasons.connectionName = "not_saved";
62
+ if (connection !== undefined) {
63
+ target = { kind: "channel-connection", id: connection.id };
64
+ storedMode = connection.inbound_mode;
65
+ }
66
+ }
67
+ else {
68
+ const connector = ctx.query.connectorId === undefined
69
+ ? undefined
70
+ : await liveConnector(ctx, ctx.query.connectorId);
71
+ values.oauthCallbackUrl = publicUrl === null ? null : `${publicUrl}${OAUTH_CALLBACK_PATH}`;
72
+ values.signInCallbackUrl = signIn;
73
+ values.templateId = ctx.guide.template ?? null;
74
+ values.templateName = await templateName(ctx);
75
+ values.connectorName = connector?.name ?? null;
76
+ reasons.connectorName = "no_connector";
77
+ if (connector !== undefined)
78
+ target = { kind: "connector", id: connector.id };
79
+ }
80
+ reasons.publicUrl = "public_url";
81
+ reasons.signInCallbackUrl = "public_url";
82
+ reasons.oauthCallbackUrl = "public_url";
83
+ reasons.orgName = "no_org";
84
+ // keep only the reasons of variables that are the place's and have no value
85
+ const known = SETUP_GUIDE_PLACE_INFO[place].variables;
86
+ for (const name of Object.keys(reasons)) {
87
+ if (!Object.hasOwn(known, name) || values[name] !== null)
88
+ delete reasons[name];
89
+ }
90
+ return { values, reasons, target, storedMode };
91
+ }
92
+ /** A live connection of the org on the guide's plugin; otherwise `channel.not_found`. */
93
+ async function liveConnection(ctx, id) {
94
+ const notFound = () => new ApiError("channel.not_found", "Channel connection not found");
95
+ if (ctx.org === null || !UUID_RE.test(id))
96
+ throw notFound();
97
+ const c = await ctx.db
98
+ .selectFrom("channel_connections")
99
+ .select(["id", "name", "inbound_mode"])
100
+ .where("id", "=", id)
101
+ .where("org_id", "=", ctx.org.orgId)
102
+ .where("plugin", "=", ctx.item.name)
103
+ .where("deleted_at", "is", null)
104
+ .executeTakeFirst();
105
+ if (!c)
106
+ throw notFound();
107
+ return c;
108
+ }
109
+ /**
110
+ * A live connector of the org made from the guide's template (`connectors.platform_template_id` ->
111
+ * the platform component of the item's template, org-owned plugins included); otherwise
112
+ * `connector.not_found`, also for a custom connector or one made from another template.
113
+ */
114
+ async function liveConnector(ctx, id) {
115
+ const notFound = () => new ApiError("connector.not_found", "Connector not found");
116
+ const template = ctx.guide.template;
117
+ if (ctx.org === null || template === undefined || !UUID_RE.test(id))
118
+ throw notFound();
119
+ const c = await ctx.db
120
+ .selectFrom("connectors as c")
121
+ .innerJoin("platform_connector_templates as t", "t.id", "c.platform_template_id")
122
+ .select(["c.id", "c.name"])
123
+ .where("c.id", "=", id)
124
+ .where("c.org_id", "=", ctx.org.orgId)
125
+ .where("c.deleted_at", "is", null)
126
+ .where("t.catalog_item_id", "=", ctx.item.id)
127
+ .where("t.template_id", "=", template)
128
+ .executeTakeFirst();
129
+ if (!c)
130
+ throw notFound();
131
+ return c;
132
+ }
133
+ /**
134
+ * The template's display name in the document's language, from the platform component made from
135
+ * it at import (`platform_connector_templates.file`); the template id when there is none.
136
+ */
137
+ async function templateName(ctx) {
138
+ const id = ctx.guide.template;
139
+ if (id === undefined)
140
+ return null;
141
+ const row = await ctx.db
142
+ .selectFrom("platform_connector_templates")
143
+ .select("file")
144
+ .where("catalog_item_id", "=", ctx.item.id)
145
+ .where("template_id", "=", id)
146
+ .where("deleted_at", "is", null)
147
+ .executeTakeFirst();
148
+ const name = row?.file
149
+ ?.displayName;
150
+ const localized = ctx.locale === "zh" ? name?.zh : name?.en;
151
+ return typeof localized === "string" && localized !== "" ? localized : id;
152
+ }
@@ -1,4 +1,4 @@
1
- import { type ExtensionManifest, type ExtensionPointRef, type LocalizedName, type ToolHandler, type ToolSpec, type UiContribution } from "@capybara-hq/plugin-sdk";
1
+ import { type DroppedUiContribution, type ExtensionManifest, type ExtensionPointRef, type LocalizedName, type ToolHandler, type ToolSpec, type UiContribution, type UiSlotSpec } from "@capybara-hq/plugin-sdk";
2
2
  import type { Redis } from "ioredis";
3
3
  import type { core } from "zod";
4
4
  import type { KernelAppOptions } from "../app.js";
@@ -24,10 +24,17 @@ import { type PermissionSet } from "./permission-set.js";
24
24
  import { type ExtensionPointEntry, ExtensionRegistry } from "./registry.js";
25
25
  import { type ExtensionSource, ReplicaStatusStore } from "./sources.js";
26
26
  export { deploymentConfigHash };
27
- /** One item of {@link ExtensionHost.uiContributions}: a UI contribution from the manifest with its registered view / action (embeds have none). */
27
+ /**
28
+ * One item of {@link ExtensionHost.uiContributions}: a UI contribution from the manifest whose slot
29
+ * resolved at load (dropped ones never appear), with its registered view / action (embeds have none).
30
+ */
28
31
  export interface LoadedUiContribution {
29
32
  plugin: string;
30
33
  contribution: UiContribution;
34
+ /** The catalog slot the contribution resolved to (from `slot`, or the deprecated `place`). */
35
+ slot: UiSlotSpec;
36
+ /** For a slot with anchors: the anchor it goes after (see `resolveUiSlots`). */
37
+ after?: string;
31
38
  render?: UiRender;
32
39
  action?: UiActionHandler;
33
40
  }
@@ -494,6 +501,8 @@ export declare class ExtensionHost {
494
501
  * 配置声明的插件启动时不设时限(见 {@link load})。
495
502
  */
496
503
  private setup;
504
+ /** Logs once per load what slot resolution left out or flagged (UI slots spec §3.2). */
505
+ private logUiResolution;
497
506
  /** 取能力实现:单选能力无需 providerName,多选能力必须指定。 */
498
507
  get<T>(capability: ExtensionPointRef, providerName?: string): T;
499
508
  /**
@@ -607,6 +616,8 @@ export declare class ExtensionHost {
607
616
  * 是否在某组织启用由调用方另查(每次查询,不缓存)。
608
617
  */
609
618
  uiContributions(): LoadedUiContribution[];
619
+ /** The UI contributions the loaded extension left out at load (UI slots spec §3.2); `[]` when not loaded. */
620
+ uiDropped(name: string): DroppedUiContribution[];
610
621
  /** 串行执行生命周期操作(unload / reload 互不交错);宿主关闭后拒绝。 */
611
622
  private serial;
612
623
  /**
@@ -38,7 +38,7 @@ import { ExtensionInstalls } from "./installs.js";
38
38
  import { ImportTracker, importExtensionModule, ManifestMismatchError, matchesSection, setupInstance, warnUpgrades, } from "./instance.js";
39
39
  import { parseLoadGate } from "./org-levels.js";
40
40
  import { declaredDisplayName, permissionSetOfSection, readMigrationSql } from "./package.js";
41
- import { blockingKeys, computePermissionSet, UI_VIEW_KEY_PREFIX, } from "./permission-set.js";
41
+ import { blockingKeys, computePermissionSet, SETUP_CHECK_KEY_PREFIX, UI_VIEW_KEY_PREFIX, } from "./permission-set.js";
42
42
  import { ExtensionRegistry } from "./registry.js";
43
43
  import { ExtensionResolveError, electSingleProviders, PROVIDER_CONFLICT, providerConflictMessage, resolveExtensions, } from "./resolve.js";
44
44
  import { ReplicaStatusStore, } from "./sources.js";
@@ -1291,9 +1291,9 @@ let ExtensionHost = class ExtensionHost {
1291
1291
  // 权限变少、不变(版本变了)或只多出视图(ui.view:*,不卡加载):直接加载,快照跟上当前清单。
1292
1292
  // 补进视图不写审计(与 autoApprove 一样,Ruling A4),只记日志
1293
1293
  const updated = await approvals.refresh(name, manifest.version, a.set);
1294
- const merged = decision.added.filter((k) => k.startsWith(UI_VIEW_KEY_PREFIX));
1294
+ const merged = decision.added.filter((k) => k.startsWith(UI_VIEW_KEY_PREFIX) || k.startsWith(SETUP_CHECK_KEY_PREFIX));
1295
1295
  if (updated && merged.length > 0 && blockingKeys(decision.added).length === 0) {
1296
- this.log.info("plugin ui views merged into the permission snapshot", {
1296
+ this.log.info("plugin ui views and setup checks merged into the permission snapshot", {
1297
1297
  plugin: name,
1298
1298
  version: manifest.version,
1299
1299
  merged,
@@ -1712,7 +1712,7 @@ let ExtensionHost = class ExtensionHost {
1712
1712
  },
1713
1713
  });
1714
1714
  if (timeoutMs === undefined)
1715
- return p;
1715
+ return p.then((inst) => this.logUiResolution(inst));
1716
1716
  let timer;
1717
1717
  let onClose = () => { };
1718
1718
  const stop = new Promise((resolve) => {
@@ -1725,8 +1725,9 @@ let ExtensionHost = class ExtensionHost {
1725
1725
  });
1726
1726
  try {
1727
1727
  const r = await Promise.race([p.then((inst) => ({ inst })), stop]);
1728
+ // a late instance that is disposed right away never logs its UI resolution
1728
1729
  if (typeof r === "object")
1729
- return r.inst;
1730
+ return this.logUiResolution(r.inst);
1730
1731
  // setup 迟迟不返回:关闭它的 ctx(此后它的登记立即撤销),迟到完成的实例直接撤销
1731
1732
  p.then((inst) => this.disposeQuietly(inst, 0), () => { });
1732
1733
  if (this.settingUp === ctx)
@@ -1743,6 +1744,20 @@ let ExtensionHost = class ExtensionHost {
1743
1744
  this.abort.signal.removeEventListener("abort", onClose);
1744
1745
  }
1745
1746
  }
1747
+ /** Logs once per load what slot resolution left out or flagged (UI slots spec §3.2). */
1748
+ logUiResolution(inst) {
1749
+ const at = { plugin: inst.name, version: inst.version };
1750
+ if (inst.uiDropped.length > 0) {
1751
+ this.log.warn("plugin ui contributions dropped", { ...at, dropped: inst.uiDropped });
1752
+ }
1753
+ if (inst.uiUnknownAnchors.length > 0) {
1754
+ this.log.warn("plugin ui anchors unknown", { ...at, anchors: inst.uiUnknownAnchors });
1755
+ }
1756
+ if (inst.uiDeprecatedPlace.length > 0) {
1757
+ this.log.info("plugin ui uses the deprecated place", { ...at, ids: inst.uiDeprecatedPlace });
1758
+ }
1759
+ return inst;
1760
+ }
1746
1761
  /** 取能力实现:单选能力无需 providerName,多选能力必须指定。 */
1747
1762
  get(capability, providerName) {
1748
1763
  const providers = this.registry.all().filter((i) => i.provided.has(capability));
@@ -2087,17 +2102,23 @@ let ExtensionHost = class ExtensionHost {
2087
2102
  * 是否在某组织启用由调用方另查(每次查询,不缓存)。
2088
2103
  */
2089
2104
  uiContributions() {
2090
- return this.registry.all().flatMap((i) => (i.manifest.ui ?? []).map((c) => {
2105
+ return this.registry.all().flatMap((i) => i.ui.map(({ contribution: c, slot, after }) => {
2091
2106
  const render = c.kind === "view" ? i.uiViews.get(c.id) : undefined;
2092
2107
  const action = c.kind === "view" ? i.uiActions.get(c.id) : undefined;
2093
2108
  return {
2094
2109
  plugin: i.name,
2095
2110
  contribution: c,
2111
+ slot,
2112
+ ...(after !== undefined && { after }),
2096
2113
  ...(render !== undefined && { render }),
2097
2114
  ...(action !== undefined && { action }),
2098
2115
  };
2099
2116
  }));
2100
2117
  }
2118
+ /** The UI contributions the loaded extension left out at load (UI slots spec §3.2); `[]` when not loaded. */
2119
+ uiDropped(name) {
2120
+ return [...(this.registry.current(name)?.uiDropped ?? [])];
2121
+ }
2101
2122
  /** 串行执行生命周期操作(unload / reload 互不交错);宿主关闭后拒绝。 */
2102
2123
  serial(fn) {
2103
2124
  const guarded = () => {
@@ -1,5 +1,5 @@
1
- import { type ExtensionManifest, type ExtensionPointRef, type StaticExtensionManifest } from "@capybara-hq/plugin-sdk";
2
- import { type ExtensionPointAccess, type KernelDeps, type KernelExtensionContext, LiveConfig, type RegisteredTool, type Route } from "../ctx/create-ctx.js";
1
+ import { type DroppedUiContribution, type ExtensionManifest, type ExtensionPointRef, type ResolvedUiContribution, type StaticExtensionManifest } from "@capybara-hq/plugin-sdk";
2
+ import { type ExtensionPointAccess, type KernelDeps, type KernelExtensionContext, LiveConfig, type RegisteredSetupCheck, type RegisteredTool, type Route } from "../ctx/create-ctx.js";
3
3
  import type { UiActionHandler, UiRender } from "../ui/registry.js";
4
4
  import type { RegisteredHook } from "./hooks.js";
5
5
  import { InflightTracker } from "./inflight.js";
@@ -44,6 +44,17 @@ export declare class ExtensionInstance {
44
44
  readonly generation: number;
45
45
  readonly manifest: ExtensionManifest;
46
46
  readonly permissions: PermissionSet;
47
+ /** The manifest's UI contributions whose slot the catalog knows (UI slots spec §3.2), in manifest order. */
48
+ readonly ui: ResolvedUiContribution[];
49
+ /** The manifest's UI contributions left out at load (unknown slot or slot version). */
50
+ readonly uiDropped: DroppedUiContribution[];
51
+ /** `after` values that are not anchors of their slot (the contribution keeps the default position). */
52
+ readonly uiUnknownAnchors: {
53
+ id: string;
54
+ after: string;
55
+ }[];
56
+ /** Ids of contributions that still name the deprecated `place`. */
57
+ readonly uiDeprecatedPlace: string[];
47
58
  private hash;
48
59
  private readonly live;
49
60
  readonly entryUrl: string;
@@ -68,6 +79,10 @@ export declare class ExtensionInstance {
68
79
  get hooks(): readonly RegisteredHook[];
69
80
  get uiViews(): ReadonlyMap<string, UiRender>;
70
81
  get uiActions(): ReadonlyMap<string, UiActionHandler>;
82
+ /** Setup checks registered through `ctx.setup.check` (empty after revocation). */
83
+ get setupChecks(): ReadonlyMap<string, RegisteredSetupCheck>;
84
+ /** The instance's `ctx.signal`: aborted when it is revoked or disposed. */
85
+ get signal(): AbortSignal;
71
86
  /** 账上仍在的登记(撤销后为空)。 */
72
87
  registrations(): Registration[];
73
88
  get disposed(): boolean;
@@ -60,6 +60,14 @@ export class ExtensionInstance {
60
60
  generation;
61
61
  manifest;
62
62
  permissions;
63
+ /** The manifest's UI contributions whose slot the catalog knows (UI slots spec §3.2), in manifest order. */
64
+ ui;
65
+ /** The manifest's UI contributions left out at load (unknown slot or slot version). */
66
+ uiDropped;
67
+ /** `after` values that are not anchors of their slot (the contribution keeps the default position). */
68
+ uiUnknownAnchors;
69
+ /** Ids of contributions that still name the deprecated `place`. */
70
+ uiDeprecatedPlace;
63
71
  hash;
64
72
  live;
65
73
  entryUrl;
@@ -77,6 +85,11 @@ export class ExtensionInstance {
77
85
  this.source = init.source;
78
86
  this.generation = init.generation;
79
87
  this.permissions = init.permissions;
88
+ const ui = init.ctx.uiResolution;
89
+ this.ui = ui.resolved;
90
+ this.uiDropped = ui.dropped;
91
+ this.uiUnknownAnchors = ui.unknownAnchors;
92
+ this.uiDeprecatedPlace = ui.deprecatedPlace;
80
93
  this.hash = init.configHash;
81
94
  this.live = init.config;
82
95
  this.entryUrl = init.entryUrl;
@@ -117,6 +130,14 @@ export class ExtensionInstance {
117
130
  get uiActions() {
118
131
  return this.ctx.uiActions;
119
132
  }
133
+ /** Setup checks registered through `ctx.setup.check` (empty after revocation). */
134
+ get setupChecks() {
135
+ return this.ctx.setupChecks;
136
+ }
137
+ /** The instance's `ctx.signal`: aborted when it is revoked or disposed. */
138
+ get signal() {
139
+ return this.ctx.signal;
140
+ }
120
141
  /** 账上仍在的登记(撤销后为空)。 */
121
142
  registrations() {
122
143
  return this.ctx.ledger.list();
@@ -1,5 +1,5 @@
1
1
  /** 插件经 ctx 做的一项登记的种类(C17)。 */
2
- export type RegistrationKind = "tool" | "route" | "queue" | "bus" | "lease" | "ui.view" | "ui.action" | "provide" | "hook";
2
+ export type RegistrationKind = "tool" | "route" | "queue" | "bus" | "lease" | "ui.view" | "ui.action" | "provide" | "hook" | "setup.check";
3
3
  /** 账上的一项:撤销时调用 `dispose()`。 */
4
4
  export interface Registration {
5
5
  kind: RegistrationKind;
@@ -49,6 +49,12 @@ export interface PermissionSet {
49
49
  views: string[];
50
50
  embeds: string[];
51
51
  };
52
+ /**
53
+ * Setup check ids (`permissions.setupChecks`, setup guides v2 §10.2): recorded and shown at
54
+ * review but never blocking loading, like `ui.views` (a check gets only data the extension
55
+ * already gets for the same target). Absent when none are declared (and in older snapshots).
56
+ */
57
+ setupChecks?: string[];
52
58
  };
53
59
  /**
54
60
  * 安装记录包迁移警告的摘要(`warningsDigest`,Ruling P4);没有警告或不是安装记录时省略。
@@ -106,8 +112,16 @@ export declare const POLICY_HOOK_KEY_PREFIX = "permissions.hooks.policy:";
106
112
  /** 渲染 schema 视图的权限键前缀:记入快照但不卡加载(见 {@link blockingKeys})。 */
107
113
  export declare const UI_VIEW_KEY_PREFIX = "ui.view:";
108
114
  /**
109
- * 新增项里需要批准(卡加载)的部分:除 `ui.view:*` 之外的全部。视图只是把插件已有的数据交给内核组件来画,
110
- * 不给插件新能力(spec §4.1),升级多出视图时插件照常加载、快照自动补上。
115
+ * Key prefix of a setup check id (`permissions.setupChecks`): recorded but never blocking
116
+ * ({@link blockingKeys}). Lower-case letters and dots before the colon, as catalog permission keys
117
+ * require (`PERMISSION_KEY_RE` of the marketplace format).
118
+ */
119
+ export declare const SETUP_CHECK_KEY_PREFIX = "setup.check:";
120
+ /**
121
+ * 新增项里需要批准(卡加载)的部分:除 `ui.view:*` 与 `setup.check:*` 之外的全部。视图只是把插件已有的数据交给
122
+ * 内核组件来画,不给插件新能力(spec §4.1);setup checks get only data the extension already gets
123
+ * for the same target (setup guides v2 §10.2). An upgrade adding either loads as usual and the
124
+ * snapshot takes them.
111
125
  */
112
126
  export declare function blockingKeys(added: readonly string[]): string[];
113
127
  /** 两份权限集合的差异(按 {@link permissionKeys});`prev` 为空(无快照)时全部算新增。 */
@@ -108,6 +108,10 @@ export function buildPermissionSet(input) {
108
108
  .sort((a, b) => cmp(a.purpose, b.purpose)),
109
109
  network: sortedUnique(p.network),
110
110
  ui: { views: sortedUnique(p.ui?.views), embeds: sortedUnique(p.ui?.embeds) },
111
+ // only when declared: sets (and snapshots) of plugins without setup checks stay as they were
112
+ ...(p.setupChecks && p.setupChecks.length > 0
113
+ ? { setupChecks: sortedUnique(p.setupChecks) }
114
+ : {}),
111
115
  },
112
116
  ...(input.warningsDigest ? { warningsDigest: input.warningsDigest } : {}),
113
117
  };
@@ -155,6 +159,8 @@ export function permissionKeys(set) {
155
159
  keys.push(`${UI_VIEW_KEY_PREFIX}${v}`);
156
160
  for (const e of p.ui?.embeds ?? [])
157
161
  keys.push(`ui.embed:${e}`);
162
+ for (const c of p.setupChecks ?? [])
163
+ keys.push(`${SETUP_CHECK_KEY_PREFIX}${c}`);
158
164
  if (s.warningsDigest)
159
165
  keys.push(`${WARNINGS_KEY_PREFIX}${s.warningsDigest}`);
160
166
  return sortedUnique(keys);
@@ -167,11 +173,19 @@ export const POLICY_HOOK_KEY_PREFIX = "permissions.hooks.policy:";
167
173
  /** 渲染 schema 视图的权限键前缀:记入快照但不卡加载(见 {@link blockingKeys})。 */
168
174
  export const UI_VIEW_KEY_PREFIX = "ui.view:";
169
175
  /**
170
- * 新增项里需要批准(卡加载)的部分:除 `ui.view:*` 之外的全部。视图只是把插件已有的数据交给内核组件来画,
171
- * 不给插件新能力(spec §4.1),升级多出视图时插件照常加载、快照自动补上。
176
+ * Key prefix of a setup check id (`permissions.setupChecks`): recorded but never blocking
177
+ * ({@link blockingKeys}). Lower-case letters and dots before the colon, as catalog permission keys
178
+ * require (`PERMISSION_KEY_RE` of the marketplace format).
179
+ */
180
+ export const SETUP_CHECK_KEY_PREFIX = "setup.check:";
181
+ /**
182
+ * 新增项里需要批准(卡加载)的部分:除 `ui.view:*` 与 `setup.check:*` 之外的全部。视图只是把插件已有的数据交给
183
+ * 内核组件来画,不给插件新能力(spec §4.1);setup checks get only data the extension already gets
184
+ * for the same target (setup guides v2 §10.2). An upgrade adding either loads as usual and the
185
+ * snapshot takes them.
172
186
  */
173
187
  export function blockingKeys(added) {
174
- return added.filter((k) => !k.startsWith(UI_VIEW_KEY_PREFIX));
188
+ return added.filter((k) => !k.startsWith(UI_VIEW_KEY_PREFIX) && !k.startsWith(SETUP_CHECK_KEY_PREFIX));
175
189
  }
176
190
  /** 两份权限集合的差异(按 {@link permissionKeys});`prev` 为空(无快照)时全部算新增。 */
177
191
  export function diffPermissions(prev, next) {
@@ -1,4 +1,4 @@
1
- import type { LocalizedName } from "@capybara-hq/plugin-sdk";
1
+ import type { LocalizedName, UiDropReason } from "@capybara-hq/plugin-sdk";
2
2
  import type { PluginScope } from "../catalog/scope.js";
3
3
  import { type PermissionSet } from "./permission-set.js";
4
4
  import type { ExtensionSource } from "./sources.js";
@@ -142,6 +142,15 @@ export interface AdminExtensionView {
142
142
  builtin: boolean;
143
143
  /** Where the install record came from (`bootstrap` for the bootstrap step); null for config-declared. */
144
144
  installSource: "npm" | "file" | "catalog" | "bootstrap" | null;
145
+ /**
146
+ * UI contributions this replica's loaded instance left out at load (UI slots spec §3.2): a slot the
147
+ * catalog does not know, or not at that version. `[]` when the extension is not loaded here.
148
+ */
149
+ uiDropped: {
150
+ id: string;
151
+ ref: string;
152
+ reason: UiDropReason;
153
+ }[];
145
154
  /**
146
155
  * Only with status `conflict`: the single-provider extension point and the other extensions that
147
156
  * provide it (they keep this one from loading; disabling them loads it, no restart).