@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
@@ -1,61 +1,45 @@
1
- import { type UiActionResult, type UiView } from "@capybara-hq/plugin-sdk";
1
+ import type { UiActionResult, UiView } from "@capybara-hq/plugin-sdk";
2
2
  import type { FastifyReply, FastifyRequest } from "fastify";
3
- import { z } from "zod";
3
+ import type { z } from "zod";
4
4
  import { ApiTokenService } from "../identity/api-tokens.js";
5
5
  import { type Principal } from "../identity/principal.js";
6
6
  import { SessionService } from "../identity/sessions.js";
7
7
  import { type OrgContext } from "../orgs/org.guard.js";
8
8
  import { EmbedTokens } from "./embed-token.js";
9
+ import { ActionBody, ContributionParams, ContributionsQuery, EmbedTokenBody, ViewQuery } from "./ui.schemas.js";
9
10
  import { OrgUiStreams } from "./ui-stream.js";
10
- import { type UiContributionItem, UiViews } from "./views.js";
11
- /** The contribution named in a view / action / embed path (`:plugin/:id`). */
12
- declare const ContributionParams: z.ZodObject<{
13
- plugin: z.ZodString;
14
- id: z.ZodString;
15
- }, z.core.$strip>;
16
- declare const ViewQuery: z.ZodObject<{
17
- conversationId: z.ZodOptional<z.ZodString>;
18
- locale: z.ZodDefault<z.ZodEnum<{
19
- en: "en";
20
- zh: "zh";
21
- }>>;
22
- item: z.ZodOptional<z.ZodString>;
23
- callId: z.ZodOptional<z.ZodString>;
24
- key: z.ZodOptional<z.ZodString>;
25
- }, z.core.$strip>;
26
- declare const ActionBody: z.ZodObject<{
27
- actionId: z.ZodString;
28
- payload: z.ZodOptional<z.ZodUnknown>;
29
- answer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
30
- conversationId: z.ZodOptional<z.ZodString>;
31
- locale: z.ZodDefault<z.ZodEnum<{
32
- en: "en";
33
- zh: "zh";
34
- }>>;
35
- }, z.core.$strip>;
36
- declare const EmbedTokenBody: z.ZodDefault<z.ZodObject<{
37
- conversationId: z.ZodOptional<z.ZodString>;
38
- fileId: z.ZodOptional<z.ZodUUID>;
39
- }, z.core.$strip>>;
11
+ import { type UiArrangementSlot, type UiContributionItem, UiViews } from "./views.js";
12
+ export declare const BEARER_RE: RegExp;
13
+ /** 嵌入页的同源代理前缀(网页 `/plugin-ui/<plugin>/<rest>` → 内核 `/plugins/<plugin>/ui/<rest>`)。 */
14
+ export declare const embedSrc: (plugin: string, path: string) => string;
40
15
  /**
41
16
  * 插件界面(第一档:渲染 schema;第二档:嵌入应用),会话鉴权、`ui.use`(只给人用,服务令牌 403):
42
- * - `GET /api/v1/orgs/:orgId/ui/contributions` → `{ items: { plugin, id, title, place, legacyPlace?, kind, path?, icon }[] }`
43
- * (place normalized: the old `conversation` is `panel` with `legacyPlace`; aliases and file-preview
44
- * embeds are not listed)
45
- * (`path` 仅 embed);
46
- * - `GET /api/v1/orgs/:orgId/ui/views/:plugin/:id?conversationId=&locale=&item=&callId=&key=` → `{ view }`
47
- * (`item`: the panel item to scroll to; `callId` / `key`: an inline card's tool call);
48
- * - `POST /api/v1/orgs/:orgId/ui/actions/:plugin/:id`(`{ actionId, payload?, answer?, conversationId?, locale? }`)
49
- * → `{ toast }` | `{ view }`;
50
- * - `GET /api/v1/orgs/:orgId/ui/stream`:组织级刷新 SSE(`event: ui`,`data: {"plugin","contribution"}`);
51
- * - `POST /api/v1/orgs/:orgId/ui/embeds/:plugin/:id/token`(`{ conversationId? }`)
17
+ * - `GET /api/v1/orgs/:orgId/ui/contributions?slot=` →
18
+ * `{ items: { plugin, id, title, slot, after?, kind, path?, icon }[] }`: every org-reachable slot
19
+ * (all but `platform.page`) or one slot; `after` is the resolved anchor of a slot with anchors,
20
+ * `path` only for an embed; aliases and `file.preview` contributions are not listed; ordered by
21
+ * extension name, then manifest order;
22
+ * - `GET /api/v1/orgs/:orgId/ui/arrangement` → `{ slots: { slot, source, items }[] }`: every
23
+ * org-arrangeable slot with the contributions the caller's role may use, in effective order, each
24
+ * marked `hidden` (the listing for the 界面位置 page; the lists above omit hidden ones, the
25
+ * view / action / embed routes below ignore the arrangement);
26
+ * - `GET /api/v1/orgs/:orgId/ui/views/:plugin/:id?conversationId=&agentId=&locale=&item=&callId=&key=`
27
+ * → `{ view }`(`item`: the panel item to scroll to; `callId` / `key`: an inline card's tool call);
28
+ * - `POST /api/v1/orgs/:orgId/ui/actions/:plugin/:id`
29
+ * (`{ actionId, payload?, answer?, conversationId?, agentId?, locale? }`)→ `{ toast }` | `{ view }`;
30
+ * - `GET /api/v1/orgs/:orgId/ui/stream`:组织级刷新 SSE(`event: ui`,`data: {"plugin","contribution"}`),
31
+ * member and above;
32
+ * - `POST /api/v1/orgs/:orgId/ui/embeds/:plugin/:id/token`(`{ conversationId?, agentId?, fileId? }`)
52
33
  * → `{ token, src: "/plugin-ui/<plugin>/<path 去掉 /ui/>", expiresAt, expiresIn }`:嵌入令牌
53
- * (300 秒,`expiresIn` 为秒数,见 `embed-token.ts`)。
54
- * 插件须在组织启用、贡献须是 embed;对话嵌入要求能访问该对话(否则 404)。令牌只给插件路由用,
55
- * 内核鉴权守卫不认它(它不是 API 令牌)。
34
+ * (300 秒,`expiresIn` 为秒数,见 `embed-token.ts`;`agentId` goes into the claims)。
35
+ * 令牌只给插件路由用,内核鉴权守卫不认它(它不是 API 令牌)。
56
36
  * 对话视图的刷新走现有的会话流 `/conversations/:id/stream`。
57
- * 角色(A38):组织页面(`place: "page"`,含组织级刷新流)只给 member 以上,guest 一律 404 `ui.not_found`、
58
- * 列表里也不出现;对话位置的贡献给能访问该对话的任何成员。插件收到的 `role` 来自 OrgGuard 查出的成员关系。
37
+ * Access (UI slots spec §7): every route checks the slot of the contribution it names, its
38
+ * `minRole` (guest for conversation and agent slots, member for `org.page`, admin for
39
+ * `org.settings.section`; `platform.page` never through these routes), then its scope rule
40
+ * (conversation: whoever can read it; agent: whoever can read the agent, in this org; org: the
41
+ * role alone; file: the previewer's read grant). Anything refused is 404 `ui.not_found`, the same
42
+ * as a contribution that does not exist. 插件收到的 `role` 来自 OrgGuard 查出的成员关系。
59
43
  */
60
44
  export declare class UiController {
61
45
  private readonly views;
@@ -64,9 +48,12 @@ export declare class UiController {
64
48
  private readonly tokens;
65
49
  private readonly embeds;
66
50
  constructor(views: UiViews, streams: OrgUiStreams, sessions: SessionService, tokens: ApiTokenService, embeds: EmbedTokens);
67
- contributions(principal: Principal, org: OrgContext): Promise<{
51
+ contributions(principal: Principal, org: OrgContext, q: z.infer<typeof ContributionsQuery>): Promise<{
68
52
  items: UiContributionItem[];
69
53
  }>;
54
+ arrangement(principal: Principal, org: OrgContext): Promise<{
55
+ slots: UiArrangementSlot[];
56
+ }>;
70
57
  view(principal: Principal, org: OrgContext, { plugin, id }: z.infer<typeof ContributionParams>, q: z.infer<typeof ViewQuery>): Promise<{
71
58
  view: UiView;
72
59
  }>;
@@ -80,4 +67,3 @@ export declare class UiController {
80
67
  /** 组织级刷新流:与会话流一样用 `reply.hijack()` 直接写原始响应;心跳与复核间隔同 `runtime.sse*`。 */
81
68
  stream(principal: Principal, org: OrgContext, req: FastifyRequest, reply: FastifyReply): Promise<void>;
82
69
  }
83
- export {};
@@ -7,9 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { UI_LIMITS } from "@capybara-hq/plugin-sdk";
11
10
  import { Controller, Get, HttpCode, Inject, Post, Req, Res } from "@nestjs/common";
12
- import { z } from "zod";
13
11
  import { ApiTokenService } from "../identity/api-tokens.js";
14
12
  import { credentialCheck } from "../identity/credential-check.js";
15
13
  import { CurrentPrincipal, requireUser } from "../identity/principal.js";
@@ -18,59 +16,14 @@ import { createZodDto } from "../infra/validation/create-zod-dto.js";
18
16
  import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
19
17
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
20
18
  import { EMBED_TOKEN_TTL_SECONDS, EmbedTokens } from "./embed-token.js";
19
+ import { ActionBody, ContributionParams, ContributionsQuery, EmbedTokenBody, ViewQuery, } from "./ui.schemas.js";
21
20
  import { OrgUiStreams } from "./ui-stream.js";
22
21
  import { UiViews } from "./views.js";
23
- const BEARER_RE = /^Bearer\s+(\S+)$/i;
24
- const Locale = z.enum(["zh", "en"]).default("en");
25
- const Id = z.string().min(1).max(100);
26
- /** The contribution named in a view / action / embed path (`:plugin/:id`). */
27
- const ContributionParams = z.object({ plugin: Id, id: Id });
28
- const ViewQuery = z
29
- .object({
30
- conversationId: z.string().min(1).max(100).optional(),
31
- locale: Locale,
32
- /** A panel opened from an open target: the item to scroll to. */
33
- item: z.string().min(1).max(200).optional(),
34
- /** An inline card: the tool call it hangs off, and its `ToolResult.card.key`. */
35
- callId: z.string().min(1).max(200).optional(),
36
- key: z.string().min(1).max(200).optional(),
37
- })
38
- .refine((q) => q.key === undefined || q.callId !== undefined, {
39
- message: "key needs callId",
40
- });
41
- /**
42
- * 表单答案(plugin-sdk 的 `FormAnswer`)。SDK 的 InputForm 没有单独的字段数 / 选项数上限,
43
- * 这里沿用视图的上限:键数与多选数组长度 ≤ `UI_LIMITS.tableRows`,单个文本 ≤ `UI_LIMITS.textChars`。
44
- */
45
- const AnswerText = z.string().max(UI_LIMITS.textChars);
46
- const FormAnswerSchema = z
47
- .record(z.string().max(100), z.union([AnswerText, z.number(), z.boolean(), z.array(AnswerText).max(UI_LIMITS.tableRows)]))
48
- .refine((a) => Object.keys(a).length <= UI_LIMITS.tableRows, {
49
- message: `answer must have at most ${UI_LIMITS.tableRows} fields`,
50
- });
51
- /** 按钮 payload 的 JSON 序列化后 UTF-8 字节数(与 `validateUiView` 对按钮 payload 的上限一致)。 */
52
- const payloadBytes = (v) => Buffer.byteLength(JSON.stringify(v) ?? "", "utf8");
53
- const ActionBody = z.object({
54
- actionId: z.string().min(1).max(200),
55
- payload: z
56
- .unknown()
57
- .optional()
58
- .refine((v) => v === undefined || payloadBytes(v) <= UI_LIMITS.payloadBytes, {
59
- message: `payload must be at most ${UI_LIMITS.payloadBytes} bytes as JSON`,
60
- }),
61
- answer: FormAnswerSchema.optional(),
62
- conversationId: z.string().min(1).max(100).optional(),
63
- locale: Locale,
64
- });
65
- const EmbedTokenBody = z
66
- .object({
67
- conversationId: z.string().min(1).max(100).optional(),
68
- /** Only a `file-preview` embed: the file version it previews. */
69
- fileId: z.uuid().optional(),
70
- })
71
- .default({});
22
+ export const BEARER_RE = /^Bearer\s+(\S+)$/i;
72
23
  /** 嵌入页的同源代理前缀(网页 `/plugin-ui/<plugin>/<rest>` → 内核 `/plugins/<plugin>/ui/<rest>`)。 */
73
- const embedSrc = (plugin, path) => `/plugin-ui/${plugin}/${path.replace(/^\/ui\//, "")}`;
24
+ export const embedSrc = (plugin, path) => `/plugin-ui/${plugin}/${path.replace(/^\/ui\//, "")}`;
25
+ class UiContributionsQueryDto extends createZodDto(ContributionsQuery, "UiContributionsQuery") {
26
+ }
74
27
  class UiContributionParamsDto extends createZodDto(ContributionParams, "UiContributionParams") {
75
28
  }
76
29
  class UiViewQueryDto extends createZodDto(ViewQuery, "UiViewQuery") {
@@ -81,23 +34,32 @@ class UiEmbedTokenBodyDto extends createZodDto(EmbedTokenBody, "UiEmbedTokenBody
81
34
  }
82
35
  /**
83
36
  * 插件界面(第一档:渲染 schema;第二档:嵌入应用),会话鉴权、`ui.use`(只给人用,服务令牌 403):
84
- * - `GET /api/v1/orgs/:orgId/ui/contributions` → `{ items: { plugin, id, title, place, legacyPlace?, kind, path?, icon }[] }`
85
- * (place normalized: the old `conversation` is `panel` with `legacyPlace`; aliases and file-preview
86
- * embeds are not listed)
87
- * (`path` 仅 embed);
88
- * - `GET /api/v1/orgs/:orgId/ui/views/:plugin/:id?conversationId=&locale=&item=&callId=&key=` → `{ view }`
89
- * (`item`: the panel item to scroll to; `callId` / `key`: an inline card's tool call);
90
- * - `POST /api/v1/orgs/:orgId/ui/actions/:plugin/:id`(`{ actionId, payload?, answer?, conversationId?, locale? }`)
91
- * → `{ toast }` | `{ view }`;
92
- * - `GET /api/v1/orgs/:orgId/ui/stream`:组织级刷新 SSE(`event: ui`,`data: {"plugin","contribution"}`);
93
- * - `POST /api/v1/orgs/:orgId/ui/embeds/:plugin/:id/token`(`{ conversationId? }`)
37
+ * - `GET /api/v1/orgs/:orgId/ui/contributions?slot=` →
38
+ * `{ items: { plugin, id, title, slot, after?, kind, path?, icon }[] }`: every org-reachable slot
39
+ * (all but `platform.page`) or one slot; `after` is the resolved anchor of a slot with anchors,
40
+ * `path` only for an embed; aliases and `file.preview` contributions are not listed; ordered by
41
+ * extension name, then manifest order;
42
+ * - `GET /api/v1/orgs/:orgId/ui/arrangement` → `{ slots: { slot, source, items }[] }`: every
43
+ * org-arrangeable slot with the contributions the caller's role may use, in effective order, each
44
+ * marked `hidden` (the listing for the 界面位置 page; the lists above omit hidden ones, the
45
+ * view / action / embed routes below ignore the arrangement);
46
+ * - `GET /api/v1/orgs/:orgId/ui/views/:plugin/:id?conversationId=&agentId=&locale=&item=&callId=&key=`
47
+ * → `{ view }`(`item`: the panel item to scroll to; `callId` / `key`: an inline card's tool call);
48
+ * - `POST /api/v1/orgs/:orgId/ui/actions/:plugin/:id`
49
+ * (`{ actionId, payload?, answer?, conversationId?, agentId?, locale? }`)→ `{ toast }` | `{ view }`;
50
+ * - `GET /api/v1/orgs/:orgId/ui/stream`:组织级刷新 SSE(`event: ui`,`data: {"plugin","contribution"}`),
51
+ * member and above;
52
+ * - `POST /api/v1/orgs/:orgId/ui/embeds/:plugin/:id/token`(`{ conversationId?, agentId?, fileId? }`)
94
53
  * → `{ token, src: "/plugin-ui/<plugin>/<path 去掉 /ui/>", expiresAt, expiresIn }`:嵌入令牌
95
- * (300 秒,`expiresIn` 为秒数,见 `embed-token.ts`)。
96
- * 插件须在组织启用、贡献须是 embed;对话嵌入要求能访问该对话(否则 404)。令牌只给插件路由用,
97
- * 内核鉴权守卫不认它(它不是 API 令牌)。
54
+ * (300 秒,`expiresIn` 为秒数,见 `embed-token.ts`;`agentId` goes into the claims)。
55
+ * 令牌只给插件路由用,内核鉴权守卫不认它(它不是 API 令牌)。
98
56
  * 对话视图的刷新走现有的会话流 `/conversations/:id/stream`。
99
- * 角色(A38):组织页面(`place: "page"`,含组织级刷新流)只给 member 以上,guest 一律 404 `ui.not_found`、
100
- * 列表里也不出现;对话位置的贡献给能访问该对话的任何成员。插件收到的 `role` 来自 OrgGuard 查出的成员关系。
57
+ * Access (UI slots spec §7): every route checks the slot of the contribution it names, its
58
+ * `minRole` (guest for conversation and agent slots, member for `org.page`, admin for
59
+ * `org.settings.section`; `platform.page` never through these routes), then its scope rule
60
+ * (conversation: whoever can read it; agent: whoever can read the agent, in this org; org: the
61
+ * role alone; file: the previewer's read grant). Anything refused is 404 `ui.not_found`, the same
62
+ * as a contribution that does not exist. 插件收到的 `role` 来自 OrgGuard 查出的成员关系。
101
63
  */
102
64
  let UiController = class UiController {
103
65
  views;
@@ -112,9 +74,13 @@ let UiController = class UiController {
112
74
  this.tokens = tokens;
113
75
  this.embeds = embeds;
114
76
  }
115
- async contributions(principal, org) {
77
+ async contributions(principal, org, q) {
116
78
  requireUser(principal);
117
- return { items: await this.views.contributions(org.orgId, org.role) };
79
+ return { items: await this.views.contributions(org.orgId, org.role, q.slot) };
80
+ }
81
+ async arrangement(principal, org) {
82
+ requireUser(principal);
83
+ return { slots: await this.views.arrangementView(org.orgId, org.role) };
118
84
  }
119
85
  async view(principal, org, { plugin, id }, q) {
120
86
  const p = requireUser(principal);
@@ -125,6 +91,7 @@ let UiController = class UiController {
125
91
  plugin,
126
92
  id,
127
93
  ...(q.conversationId !== undefined && { conversationId: q.conversationId }),
94
+ ...(q.agentId !== undefined && { agentId: q.agentId }),
128
95
  locale: q.locale,
129
96
  ...(q.item !== undefined && { item: q.item }),
130
97
  ...(q.callId !== undefined && {
@@ -142,6 +109,7 @@ let UiController = class UiController {
142
109
  plugin,
143
110
  id,
144
111
  ...(body.conversationId !== undefined && { conversationId: body.conversationId }),
112
+ ...(body.agentId !== undefined && { agentId: body.agentId }),
145
113
  locale: body.locale,
146
114
  }, {
147
115
  actionId: body.actionId,
@@ -158,14 +126,17 @@ let UiController = class UiController {
158
126
  plugin,
159
127
  id,
160
128
  ...(body.conversationId !== undefined && { conversationId: body.conversationId }),
129
+ ...(body.agentId !== undefined && { agentId: body.agentId }),
161
130
  ...(body.fileId !== undefined && { fileId: body.fileId }),
162
131
  });
163
132
  const { token, expiresAt } = this.embeds.issue({
164
133
  plugin: target.plugin,
134
+ scope: "org",
165
135
  orgId: org.orgId,
166
136
  accountId: p.account.id,
167
137
  role: org.role,
168
138
  ...(target.conversationId !== undefined && { conversationId: target.conversationId }),
139
+ ...(target.agentId !== undefined && { agentId: target.agentId }),
169
140
  ...(target.fileId !== undefined && { fileId: target.fileId }),
170
141
  contribution: target.contribution,
171
142
  });
@@ -221,8 +192,15 @@ __decorate([
221
192
  Get("contributions"),
222
193
  OrgAction("ui.use"),
223
194
  __param(0, CurrentPrincipal()),
224
- __param(1, CurrentOrg())
195
+ __param(1, CurrentOrg()),
196
+ __param(2, ZQuery(UiContributionsQueryDto))
225
197
  ], UiController.prototype, "contributions", null);
198
+ __decorate([
199
+ Get("arrangement"),
200
+ OrgAction("ui.use"),
201
+ __param(0, CurrentPrincipal()),
202
+ __param(1, CurrentOrg())
203
+ ], UiController.prototype, "arrangement", null);
226
204
  __decorate([
227
205
  Get("views/:plugin/:id"),
228
206
  OrgAction("ui.use"),
@@ -1,4 +1,4 @@
1
- import { type FormAnswer, type Logger, type UiActionResult, type UiContribution, type UiIcon, type UiView } from "@capybara-hq/plugin-sdk";
1
+ import { type FormAnswer, type Logger, type UiActionResult, type UiContribution, type UiIcon, type UiSlotId, type UiView } from "@capybara-hq/plugin-sdk";
2
2
  import { ConversationsService } from "../conversations/conversations.service.js";
3
3
  import type { KernelSchemaDb } from "../db/kernel-schema.js";
4
4
  import { EffectiveConfigs } from "../extensions/effective-config.js";
@@ -14,16 +14,24 @@ export interface UiContributionItem {
14
14
  plugin: string;
15
15
  id: string;
16
16
  title: UiContribution["title"];
17
- /** Normalized: the old `conversation` is listed as `panel`. */
18
- place: Exclude<UiContribution["place"], "conversation" | "file-preview">;
19
- /** `"conversation"` when the manifest still says the old place. */
20
- legacyPlace?: "conversation";
17
+ /** The catalog slot the contribution resolved to. */
18
+ slot: UiSlotId;
19
+ /** For a slot with anchors: the anchor it goes after (resolved: the slot's first by default). */
20
+ after?: string;
21
21
  kind: UiContribution["kind"];
22
22
  /** 仅 embed:插件路由下 `/ui/` 开头的路径(网页经 `/plugin-ui/<plugin>/...` 代理访问)。 */
23
23
  path?: string;
24
24
  /** From `UI_ICONS`; `puzzle` when the manifest names none. */
25
25
  icon: UiIcon;
26
26
  }
27
+ /** One slot of an arrangement listing: where its entry comes from and its items in order. */
28
+ export interface UiArrangementSlot {
29
+ slot: UiSlotId;
30
+ source: "org" | "platform" | "none";
31
+ items: (UiContributionItem & {
32
+ hidden: boolean;
33
+ })[];
34
+ }
27
35
  /** 嵌入应用的签发目标:已核对组织启用与对话访问。 */
28
36
  export interface UiEmbedTarget {
29
37
  plugin: string;
@@ -31,11 +39,14 @@ export interface UiEmbedTarget {
31
39
  /** 清单里的 `path`(`/ui/` 开头)。 */
32
40
  path: string;
33
41
  conversationId?: string;
34
- /** Only a `file-preview` embed: the one file the token is for. */
42
+ /** Only an agent embed: the agent the token is for. */
43
+ agentId?: string;
44
+ /** Only a `file.preview` embed: the one file the token is for. */
35
45
  fileId?: string;
36
46
  }
37
- /** 取视图 / 执行动作的调用者与目标(身份来自内核鉴权)。 */
38
- export interface UiTarget {
47
+ /** 取视图 / 执行动作的调用者与目标(身份来自内核鉴权): an org route's. */
48
+ export interface UiOrgTarget {
49
+ scope?: "org";
39
50
  orgId: string;
40
51
  accountId: string;
41
52
  /** 调用者在 `orgId` 的角色(OrgGuard 查出的成员关系)。 */
@@ -43,6 +54,8 @@ export interface UiTarget {
43
54
  plugin: string;
44
55
  id: string;
45
56
  conversationId?: string;
57
+ /** Only an agent view or embed (`agent.detail.section`). */
58
+ agentId?: string;
46
59
  locale: "zh" | "en";
47
60
  /** A panel opened from an open target: the item to scroll to. */
48
61
  item?: string;
@@ -52,6 +65,20 @@ export interface UiTarget {
52
65
  key?: string;
53
66
  };
54
67
  }
68
+ /** A super admin's request on the org-less admin UI routes (`platform.page` only). */
69
+ export interface UiPlatformTarget {
70
+ scope: "platform";
71
+ accountId: string;
72
+ plugin: string;
73
+ id: string;
74
+ locale: "zh" | "en";
75
+ }
76
+ /** The caller and target of a view, action or embed: an org route's or the platform's. */
77
+ export type UiTarget = UiOrgTarget | UiPlatformTarget;
78
+ /** {@link UiTarget} without `locale`, per variant (what `embed` takes). */
79
+ export type UiEmbedRequest = (Omit<UiOrgTarget, "locale"> & {
80
+ fileId?: string;
81
+ }) | Omit<UiPlatformTarget, "locale">;
55
82
  /**
56
83
  * The card a `tool_result` of the conversation recorded for `callId`, or null (the inline-card
57
84
  * attestation of {@link UiViewsDeps.recordedCard}). Scans one conversation's tool results.
@@ -69,9 +96,11 @@ export declare function dropForeignOpens(view: UiView, panels: ReadonlySet<strin
69
96
  /**
70
97
  * 插件视图(第一档:渲染 schema):列出贡献、调用插件 `render` / `action`。
71
98
  * - 只认在该组织已启用、且已在本副本加载的插件(启用状态每次查库);
72
- * - 对话视图要求调用者能访问这个对话(与 SSE 流一致),访问不了与不存在一样返回 404 `ui.not_found`;
73
- * - 组织页面(`place: "page"`,视图与嵌入)只给 member 以上:guest 列表里看不到、取视图 / 动作 / 令牌一律 404;
74
- * - 组织页面视图不接受 `conversationId`(带了 400,与对话视图缺它一致);
99
+ * - every request goes through one gate (`resolve`): the slot's `minRole` (never `platform.page`),
100
+ * then its scope rule (conversation: readable, like the SSE stream; agent: readable and in this
101
+ * org; org: the role alone); refused is 404 `ui.not_found`, the same as not existing;
102
+ * - a view takes only the context id of its scope (`conversationId` / `agentId`): a missing one or
103
+ * one of another scope is 400 `validation_failed`;
75
104
  * - 调用带 5 秒时限与中止信号:render 抛错 502 `ui.render_failed`、action 抛错 502 `ui.action_failed`、
76
105
  * 超时 504 `ui.timeout`、返回非法视图 502
77
106
  * `ui.invalid_view`,都记告警日志、不写对话日志(这些不是模型可见的内容)。
@@ -79,16 +108,37 @@ export declare function dropForeignOpens(view: UiView, panels: ReadonlySet<strin
79
108
  */
80
109
  export declare class UiViews {
81
110
  private readonly d;
82
- constructor(host: ExtensionHost, conversations: ConversationsService, previewers: PreviewerRegistry, db: KernelDb, log: Pick<Logger, "warn">, effective: Pick<EffectiveConfigs, "get">);
83
- /** 该组织、该角色可见的界面贡献:插件已启用且已加载;kind view 的要求插件注册了 render;组织页面要 member 以上。 */
84
- contributions(orgId: string, role: OrgRole): Promise<UiContributionItem[]>;
111
+ constructor(host: ExtensionHost, conversations: ConversationsService, previewers: PreviewerRegistry, db: KernelDb, log: Pick<Logger, "warn">, effective: Pick<EffectiveConfigs, "get" | "platform">);
112
+ /**
113
+ * The contributions the role may use in the org, of one slot or of every org-reachable slot:
114
+ * the extension enabled and loaded, a view's render registered, the slot's `minRole`
115
+ * ({@link canUseSlot}, so never `platform.page`); no alias, no `file.preview` contribution (the
116
+ * file previewers list those). Base order (spec §5): extension name, then manifest order.
117
+ */
118
+ contributions(orgId: string, role: OrgRole, slot?: UiSlotId): Promise<UiContributionItem[]>;
119
+ /** {@link contributions} before the arrangement: what the role may use, in base order. */
120
+ private orgBase;
121
+ /**
122
+ * The arrangement page of an org: every org-arrangeable slot with the contributions the role may
123
+ * use, in effective order with the hidden ones in place, and where the entry comes from.
124
+ */
125
+ arrangementView(orgId: string, role: OrgRole): Promise<UiArrangementSlot[]>;
126
+ /**
127
+ * The platform's arrangement page: every arrangeable slot with the contributions of every running
128
+ * extension (whatever its org enablement), ordered by the platform entry, hidden ones in place.
129
+ */
130
+ platformArrangementView(): Promise<UiArrangementSlot[]>;
131
+ /**
132
+ * The `platform.page` contributions of the running extensions, for the admin console (super
133
+ * admins; no org enablement applies): no alias, a view's render registered. Base order like
134
+ * {@link contributions}.
135
+ */
136
+ platformContributions(): Promise<UiContributionItem[]>;
85
137
  /**
86
138
  * 嵌入应用:核对贡献是 embed、插件在该组织启用;对话嵌入要求调用者能访问该对话(否则 404,与视图一致),
87
139
  * 组织页面不收 `conversationId`。返回签发令牌所需的目标(令牌由调用方签)。
88
140
  */
89
- embed(t: Omit<UiTarget, "locale"> & {
90
- fileId?: string;
91
- }): Promise<UiEmbedTarget>;
141
+ embed(t: UiEmbedRequest): Promise<UiEmbedTarget>;
92
142
  /** 取视图:调用插件 `render`,校验返回的视图。 */
93
143
  render(t: UiTarget): Promise<UiView>;
94
144
  /** 执行动作:调用插件 `action`(视图里的按钮 / 表单提交),校验返回的提示或新视图。 */
@@ -99,8 +149,24 @@ export declare class UiViews {
99
149
  }): Promise<UiActionResult>;
100
150
  /** Ids of the extension's live `panel` contributions (aliases of panels included). */
101
151
  private panels;
102
- /** 找贡献、核对组织启用与对话访问;返回传给插件的参数(不含 signal)。 */
152
+ /**
153
+ * The single access gate of the org UI routes (UI slots spec §7): finds the contribution (a
154
+ * retired alias answers with the one it names), then checks, in order, that its slot is reachable
155
+ * from an org (not `platform`), the role meets the slot's `minRole`, the extension is enabled in
156
+ * the org, its effective config is valid, and the slot's scope rule (conversation: readable;
157
+ * agent: readable and in this org; org: the role alone; file: a conversation the caller reads,
158
+ * the previewer's grant is checked by `embed`). A platform target (the admin routes) takes the
159
+ * platform gate instead ({@link platformArgs}): only a `platform.page` contribution. Returns the
160
+ * args passed to the extension (no signal). Anything refused for a contribution that exists is
161
+ * 404 `ui.not_found`.
162
+ */
103
163
  private resolve;
164
+ /**
165
+ * The platform gate (UI slots spec §7): the contribution is a `platform.page` of a running
166
+ * extension (no org enablement applies), given the platform's deployment config. The caller is a
167
+ * super admin (`@SuperAdmin` on the admin routes); anything else is 404 `ui.not_found`.
168
+ */
169
+ private platformArgs;
104
170
  /** 带时限调用插件:超时中止信号并 504;抛错 502(render / action 各一个码,不回显插件的异常信息)。 */
105
171
  private call;
106
172
  /** 非法结果:记告警(列出前几条问题)并 502 `ui.invalid_view`。 */