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

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 (192) 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/marketplaces.service.d.ts +1 -1
  16. package/dist/channels/channels.module.js +2 -2
  17. package/dist/channels/inbound.d.ts +8 -8
  18. package/dist/channels/inbound.js +31 -31
  19. package/dist/channels/phrases.d.ts +27 -27
  20. package/dist/channels/phrases.js +27 -27
  21. package/dist/config/docs.js +1 -1
  22. package/dist/config/schema.d.ts +2 -2
  23. package/dist/config/schema.js +21 -21
  24. package/dist/conversations/conversations.controller.d.ts +11 -11
  25. package/dist/conversations/conversations.controller.js +15 -15
  26. package/dist/conversations/conversations.module.d.ts +1 -1
  27. package/dist/conversations/conversations.module.js +7 -7
  28. package/dist/conversations/conversations.schemas.js +1 -1
  29. package/dist/conversations/conversations.service.d.ts +14 -14
  30. package/dist/conversations/conversations.service.js +19 -19
  31. package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
  32. package/dist/conversations/{environment-status.js → device-status.js} +18 -18
  33. package/dist/conversations/event-log.d.ts +1 -1
  34. package/dist/conversations/event-log.js +1 -1
  35. package/dist/conversations/events.d.ts +1 -1
  36. package/dist/conversations/events.js +2 -2
  37. package/dist/ctx/create-ctx.d.ts +10 -1
  38. package/dist/ctx/create-ctx.js +86 -25
  39. package/dist/db/kernel-schema.d.ts +26 -23
  40. package/dist/{environments → devices}/access.d.ts +19 -19
  41. package/dist/{environments → devices}/access.js +28 -28
  42. package/dist/devices/conversation-devices.d.ts +75 -0
  43. package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
  44. package/dist/devices/devices.controller.d.ts +36 -0
  45. package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
  46. package/dist/devices/devices.module.d.ts +5 -13
  47. package/dist/devices/devices.module.js +16 -29
  48. package/dist/devices/devices.schemas.d.ts +3 -0
  49. package/dist/devices/devices.schemas.js +14 -0
  50. package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
  51. package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
  52. package/dist/devices/dto.d.ts +13 -54
  53. package/dist/devices/dto.js +7 -39
  54. package/dist/devices/label.d.ts +11 -0
  55. package/dist/{environments → devices}/label.js +7 -7
  56. package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
  57. package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
  58. package/dist/errors.d.ts +25 -25
  59. package/dist/errors.js +25 -25
  60. package/dist/evals/recorded-tools.d.ts +2 -2
  61. package/dist/evals/recorded-tools.js +6 -6
  62. package/dist/extensions/admin-ops.js +1 -0
  63. package/dist/extensions/admin-view.js +2 -0
  64. package/dist/extensions/effective-config.d.ts +6 -0
  65. package/dist/extensions/effective-config.js +18 -7
  66. package/dist/extensions/host.d.ts +13 -2
  67. package/dist/extensions/host.js +24 -3
  68. package/dist/extensions/instance.d.ts +12 -1
  69. package/dist/extensions/instance.js +13 -0
  70. package/dist/extensions/permission-view.d.ts +10 -1
  71. package/dist/files/previewers.d.ts +1 -1
  72. package/dist/files/previewers.js +1 -1
  73. package/dist/http/api.module.js +10 -6
  74. package/dist/http/gateway-routes.d.ts +1 -1
  75. package/dist/http/gateway-routes.js +1 -1
  76. package/dist/identity/settings.d.ts +32 -0
  77. package/dist/identity/settings.js +26 -2
  78. package/dist/infra/config/kernel-config.service.js +4 -4
  79. package/dist/kernel.module.js +3 -3
  80. package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
  81. package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
  82. package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
  83. package/dist/{devices → local-devices}/device-auth.js +51 -53
  84. package/dist/local-devices/device-auth.module.d.ts +7 -0
  85. package/dist/{devices → local-devices}/device-auth.module.js +5 -5
  86. package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
  87. package/dist/{devices → local-devices}/device-routes.js +2 -2
  88. package/dist/local-devices/dto.d.ts +66 -0
  89. package/dist/local-devices/dto.js +44 -0
  90. package/dist/{devices → local-devices}/keys.d.ts +11 -11
  91. package/dist/{devices → local-devices}/keys.js +11 -11
  92. package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
  93. package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
  94. package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
  95. package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
  96. package/dist/local-devices/local-devices.module.d.ts +16 -0
  97. package/dist/local-devices/local-devices.module.js +44 -0
  98. package/dist/{devices → local-devices}/machines.d.ts +26 -26
  99. package/dist/{devices → local-devices}/machines.js +71 -71
  100. package/dist/{devices → local-devices}/relay.d.ts +15 -15
  101. package/dist/{devices → local-devices}/relay.js +55 -55
  102. package/dist/{devices → local-devices}/targets.d.ts +8 -8
  103. package/dist/{devices → local-devices}/targets.js +14 -14
  104. package/dist/orgs/offboarding.d.ts +6 -6
  105. package/dist/orgs/offboarding.js +12 -12
  106. package/dist/orgs/settings.service.d.ts +28 -0
  107. package/dist/orgs/settings.service.js +20 -1
  108. package/dist/platform/exclusions.js +38 -3
  109. package/dist/platform/operation.js +6 -2
  110. package/dist/platform/ops/admin.ops.js +8 -2
  111. package/dist/platform/ops/conversations.ops.d.ts +3 -3
  112. package/dist/platform/ops/conversations.ops.js +9 -9
  113. package/dist/platform/ops/devices.ops.d.ts +26 -0
  114. package/dist/platform/ops/devices.ops.js +311 -0
  115. package/dist/platform/ops/org-settings.ops.js +2 -2
  116. package/dist/platform/ops/orgs.ops.js +1 -0
  117. package/dist/platform/ops/ui.ops.d.ts +8 -0
  118. package/dist/platform/ops/ui.ops.js +41 -0
  119. package/dist/platform/platform-ops.module.d.ts +3 -2
  120. package/dist/platform/platform-ops.module.js +8 -5
  121. package/dist/platform/route-schemas.js +6 -3
  122. package/dist/roles/gateway-role.d.ts +3 -3
  123. package/dist/roles/gateway-role.js +4 -4
  124. package/dist/roles/gateway.module.d.ts +1 -1
  125. package/dist/roles/gateway.module.js +5 -5
  126. package/dist/roles/worker.module.js +1 -1
  127. package/dist/runtime/context/build.d.ts +1 -1
  128. package/dist/runtime/context/build.js +6 -6
  129. package/dist/runtime/context/compaction.js +1 -1
  130. package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
  131. package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
  132. package/dist/runtime/maintenance.d.ts +1 -1
  133. package/dist/sandbox/jobs.d.ts +13 -13
  134. package/dist/sandbox/jobs.js +20 -20
  135. package/dist/sandbox/leases.d.ts +9 -9
  136. package/dist/sandbox/leases.js +14 -14
  137. package/dist/sandbox/providers.d.ts +8 -8
  138. package/dist/sandbox/providers.js +8 -8
  139. package/dist/sandbox/sandbox.module.d.ts +1 -1
  140. package/dist/sandbox/sandbox.module.js +1 -1
  141. package/dist/sandbox/sandbox.service.d.ts +76 -76
  142. package/dist/sandbox/sandbox.service.js +230 -230
  143. package/dist/tools/builtins/builtin-tools.d.ts +2 -2
  144. package/dist/tools/builtins/builtin-tools.js +5 -5
  145. package/dist/tools/builtins/platform.js +1 -1
  146. package/dist/tools/builtins/sandbox/bash.js +6 -6
  147. package/dist/tools/builtins/sandbox/common.d.ts +25 -25
  148. package/dist/tools/builtins/sandbox/common.js +51 -51
  149. package/dist/tools/builtins/sandbox/edit.js +2 -2
  150. package/dist/tools/builtins/sandbox/index.d.ts +1 -1
  151. package/dist/tools/builtins/sandbox/index.js +1 -1
  152. package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
  153. package/dist/tools/builtins/sandbox/task-output.js +10 -10
  154. package/dist/tools/builtins/sandbox/task-stop.js +2 -2
  155. package/dist/tools/builtins/sandbox/write.js +2 -2
  156. package/dist/tools/builtins/skill-load.d.ts +1 -1
  157. package/dist/tools/builtins/skill-load.js +2 -2
  158. package/dist/tools/pipeline.js +4 -3
  159. package/dist/tools/registry.d.ts +2 -2
  160. package/dist/tools/tools.module.js +3 -3
  161. package/dist/ui/admin-ui.controller.d.ts +56 -0
  162. package/dist/ui/admin-ui.controller.js +192 -0
  163. package/dist/ui/arrangement.d.ts +74 -0
  164. package/dist/ui/arrangement.js +98 -0
  165. package/dist/ui/embed-token.d.ts +2 -2
  166. package/dist/ui/embed-token.js +30 -5
  167. package/dist/ui/registry.d.ts +29 -14
  168. package/dist/ui/registry.js +31 -10
  169. package/dist/ui/slots.controller.d.ts +11 -0
  170. package/dist/ui/slots.controller.js +25 -0
  171. package/dist/ui/ui-stream.d.ts +20 -3
  172. package/dist/ui/ui-stream.js +25 -7
  173. package/dist/ui/ui.schemas.d.ts +66 -0
  174. package/dist/ui/ui.schemas.js +65 -0
  175. package/dist/ui/views.controller.d.ts +35 -49
  176. package/dist/ui/views.controller.js +48 -70
  177. package/dist/ui/views.d.ts +84 -18
  178. package/dist/ui/views.js +201 -38
  179. package/package.json +3 -3
  180. package/src/migrations/0105_devices_rename.sql +136 -0
  181. package/dist/devices/device-auth.module.d.ts +0 -7
  182. package/dist/environments/conversation-environments.d.ts +0 -75
  183. package/dist/environments/dto.d.ts +0 -25
  184. package/dist/environments/dto.js +0 -12
  185. package/dist/environments/environments.controller.d.ts +0 -36
  186. package/dist/environments/environments.module.d.ts +0 -8
  187. package/dist/environments/environments.module.js +0 -31
  188. package/dist/environments/environments.schemas.d.ts +0 -3
  189. package/dist/environments/environments.schemas.js +0 -14
  190. package/dist/environments/label.d.ts +0 -11
  191. package/dist/platform/ops/environments.ops.d.ts +0 -26
  192. package/dist/platform/ops/environments.ops.js +0 -311
package/dist/ui/views.js CHANGED
@@ -7,21 +7,27 @@ 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 { normalizePlace, validateUiView, } from "@capybara-hq/plugin-sdk";
10
+ import { UI_ARRANGEABLE_SLOTS, validateUiView, } from "@capybara-hq/plugin-sdk";
11
11
  import { Inject, Injectable } from "@nestjs/common";
12
12
  import { sql } from "kysely";
13
+ import { isAgentVisibleIn } from "../agents/agent-lookup.js";
13
14
  import { ConversationsService } from "../conversations/conversations.service.js";
14
15
  import { ApiError } from "../errors.js";
15
16
  import { EffectiveConfigs, NEEDS_CONFIG_REASON } from "../extensions/effective-config.js";
16
17
  import { ExtensionHost } from "../extensions/host.js";
17
18
  import { PreviewerRegistry } from "../files/previewers.js";
19
+ import { readUiArrangement } from "../identity/settings.js";
18
20
  import { KernelDb } from "../infra/database/kernel-db.js";
19
21
  import { KERNEL_LOGGER } from "../infra/tokens.js";
22
+ import { readOrgSettings } from "../orgs/settings.service.js";
23
+ import { arrange, arrangeBySlot, effectiveEntry, ORG_ARRANGEABLE, orderItems, } from "./arrangement.js";
20
24
  import { callExtension } from "./call-extension.js";
21
- import { canUsePlace, isConversationPlace, UUID_RE } from "./registry.js";
25
+ import { canUseSlot, UUID_RE } from "./registry.js";
22
26
  export { callExtension } from "./call-extension.js";
23
27
  /** 插件 render / action 的时限(spec §4.2:慢的插件只让那个标签失败)。 */
24
28
  export const UI_CALL_TIMEOUT_MS = 5_000;
29
+ /** The log field naming where a target is: its org, or the platform scope. */
30
+ const whereOf = (t) => t.scope === "platform" ? { scope: "platform" } : { orgId: t.orgId };
25
31
  /**
26
32
  * The card a `tool_result` of the conversation recorded for `callId`, or null (the inline-card
27
33
  * attestation of {@link UiViewsDeps.recordedCard}). Scans one conversation's tool results.
@@ -66,6 +72,37 @@ export function dropForeignOpens(view, panels) {
66
72
  const { open, ...rest } = view;
67
73
  return { ...rest, ...(open !== undefined && ok(open) && { open }), blocks };
68
74
  }
75
+ /**
76
+ * Lists contributions for a listing route: no alias, a view's render registered; base order
77
+ * (spec §5): extension name, then manifest order (stable).
78
+ */
79
+ function listed(entries) {
80
+ return entries
81
+ .filter((c) => c.contribution.aliasOf === undefined && (c.contribution.kind === "embed" || c.render))
82
+ .map((c, i) => ({ c, i }))
83
+ .sort((x, y) => (x.c.plugin === y.c.plugin ? x.i - y.i : x.c.plugin < y.c.plugin ? -1 : 1))
84
+ .map(({ c: { plugin, contribution: c, slot: s, after } }) => ({
85
+ plugin,
86
+ id: c.id,
87
+ title: c.title,
88
+ slot: s.id,
89
+ ...(after !== undefined && { after }),
90
+ kind: c.kind,
91
+ ...(c.kind === "embed" && c.path !== undefined && { path: c.path }),
92
+ icon: c.icon ?? "puzzle",
93
+ }));
94
+ }
95
+ /** The items in effective order, each marked hidden or not (hidden ones stay in place). */
96
+ function withHidden(items, entry) {
97
+ const hidden = new Set(entry.hidden);
98
+ return orderItems(items, entry).map((i) => ({ ...i, hidden: hidden.has(`${i.plugin}/${i.id}`) }));
99
+ }
100
+ /** A platform route's contribution: found, and in `platform.page`; else 404. */
101
+ function platformEntry(entry) {
102
+ if (entry === undefined || entry.slot.id !== "platform.page")
103
+ throw notFound();
104
+ return entry;
105
+ }
69
106
  const TOAST_MAX = 500;
70
107
  const MAX_LOGGED_PROBLEMS = 10;
71
108
  const notFound = () => new ApiError("ui.not_found", "UI contribution not found");
@@ -91,9 +128,11 @@ function actionResultProblems(r) {
91
128
  /**
92
129
  * 插件视图(第一档:渲染 schema):列出贡献、调用插件 `render` / `action`。
93
130
  * - 只认在该组织已启用、且已在本副本加载的插件(启用状态每次查库);
94
- * - 对话视图要求调用者能访问这个对话(与 SSE 流一致),访问不了与不存在一样返回 404 `ui.not_found`;
95
- * - 组织页面(`place: "page"`,视图与嵌入)只给 member 以上:guest 列表里看不到、取视图 / 动作 / 令牌一律 404;
96
- * - 组织页面视图不接受 `conversationId`(带了 400,与对话视图缺它一致);
131
+ * - every request goes through one gate (`resolve`): the slot's `minRole` (never `platform.page`),
132
+ * then its scope rule (conversation: readable, like the SSE stream; agent: readable and in this
133
+ * org; org: the role alone); refused is 404 `ui.not_found`, the same as not existing;
134
+ * - a view takes only the context id of its scope (`conversationId` / `agentId`): a missing one or
135
+ * one of another scope is 400 `validation_failed`;
97
136
  * - 调用带 5 秒时限与中止信号:render 抛错 502 `ui.render_failed`、action 抛错 502 `ui.action_failed`、
98
137
  * 超时 504 `ui.timeout`、返回非法视图 502
99
138
  * `ui.invalid_view`,都记告警日志、不写对话日志(这些不是模型可见的内容)。
@@ -108,57 +147,125 @@ let UiViews = class UiViews {
108
147
  // org config rows plus the required bootstrap plugins
109
148
  enabledPlugins: (orgId) => host.enabledPlugins(orgId),
110
149
  canAccessConversation: (orgId, id, accountId) => conversations.isAccessible(orgId, id, accountId),
150
+ canReadAgent: (orgId, agentId, accountId) => isAgentVisibleIn(db.db, orgId, agentId, accountId),
111
151
  checkWritable: async (orgId, id, accountId) => {
112
152
  await conversations.writable(orgId, id, accountId);
113
153
  },
114
154
  previewEmbedAllowed: (t) => previewers.embedAllowed(t),
115
155
  recordedCard: (conversationId, callId) => recordedCard(db.db, conversationId, callId),
156
+ orgArrangement: async (orgId) => (await readOrgSettings(db.db, orgId)).uiArrangement,
157
+ platformArrangement: () => readUiArrangement(db.db),
116
158
  log,
117
159
  };
118
160
  }
119
- /** 该组织、该角色可见的界面贡献:插件已启用且已加载;kind view 的要求插件注册了 render;组织页面要 member 以上。 */
120
- async contributions(orgId, role) {
161
+ /**
162
+ * The contributions the role may use in the org, of one slot or of every org-reachable slot:
163
+ * the extension enabled and loaded, a view's render registered, the slot's `minRole`
164
+ * ({@link canUseSlot}, so never `platform.page`); no alias, no `file.preview` contribution (the
165
+ * file previewers list those). Base order (spec §5): extension name, then manifest order.
166
+ */
167
+ async contributions(orgId, role, slot) {
168
+ const items = await this.orgBase(orgId, role, slot);
169
+ if (items.length === 0)
170
+ return items;
171
+ const [org, platform] = await Promise.all([
172
+ this.d.orgArrangement(orgId),
173
+ this.d.platformArrangement(),
174
+ ]);
175
+ return arrangeBySlot(items, (s) => effectiveEntry(org, platform, s).entry);
176
+ }
177
+ /** {@link contributions} before the arrangement: what the role may use, in base order. */
178
+ async orgBase(orgId, role, slot) {
121
179
  const all = this.d.host.uiContributions();
122
180
  if (all.length === 0)
123
181
  return [];
124
182
  const enabled = await this.d.enabledPlugins(orgId);
125
- return all
126
- .filter((c) => enabled.has(c.plugin) &&
127
- c.contribution.aliasOf === undefined &&
128
- c.contribution.place !== "file-preview" &&
129
- (c.contribution.kind === "embed" || c.render) &&
130
- canUsePlace(c.contribution.place, role))
131
- .map(({ plugin, contribution: c }) => ({
132
- plugin,
133
- id: c.id,
134
- title: c.title,
135
- place: normalizePlace(c.place),
136
- ...(c.place === "conversation" && { legacyPlace: "conversation" }),
137
- kind: c.kind,
138
- ...(c.kind === "embed" && c.path !== undefined && { path: c.path }),
139
- icon: c.icon ?? "puzzle",
140
- }));
183
+ return listed(all.filter((c) => enabled.has(c.plugin) &&
184
+ c.slot.id !== "file.preview" &&
185
+ (slot === undefined || c.slot.id === slot) &&
186
+ canUseSlot(c.slot, role)));
187
+ }
188
+ /**
189
+ * The arrangement page of an org: every org-arrangeable slot with the contributions the role may
190
+ * use, in effective order with the hidden ones in place, and where the entry comes from.
191
+ */
192
+ async arrangementView(orgId, role) {
193
+ // the page is for people who arrange or at least see the org's UI, not for a guest
194
+ if (role === "guest")
195
+ throw notFound();
196
+ const [base, org, platform] = await Promise.all([
197
+ this.orgBase(orgId, role, undefined),
198
+ this.d.orgArrangement(orgId),
199
+ this.d.platformArrangement(),
200
+ ]);
201
+ return ORG_ARRANGEABLE.map((slot) => {
202
+ const { entry, source } = effectiveEntry(org, platform, slot);
203
+ return {
204
+ slot,
205
+ source,
206
+ items: withHidden(base.filter((i) => i.slot === slot), entry),
207
+ };
208
+ });
209
+ }
210
+ /**
211
+ * The platform's arrangement page: every arrangeable slot with the contributions of every running
212
+ * extension (whatever its org enablement), ordered by the platform entry, hidden ones in place.
213
+ */
214
+ async platformArrangementView() {
215
+ const [base, platform] = await Promise.all([
216
+ listed(this.d.host.uiContributions().filter((c) => c.slot.id !== "file.preview")),
217
+ this.d.platformArrangement(),
218
+ ]);
219
+ return UI_ARRANGEABLE_SLOTS.map((slot) => {
220
+ const { entry, source } = effectiveEntry(undefined, platform, slot);
221
+ return {
222
+ slot,
223
+ source,
224
+ items: withHidden(base.filter((i) => i.slot === slot), entry),
225
+ };
226
+ });
227
+ }
228
+ /**
229
+ * The `platform.page` contributions of the running extensions, for the admin console (super
230
+ * admins; no org enablement applies): no alias, a view's render registered. Base order like
231
+ * {@link contributions}.
232
+ */
233
+ async platformContributions() {
234
+ const items = listed(this.d.host.uiContributions().filter((c) => c.slot.id === "platform.page"));
235
+ const { entry } = effectiveEntry(undefined, await this.d.platformArrangement(), "platform.page");
236
+ return arrange(items, entry);
141
237
  }
142
238
  /**
143
239
  * 嵌入应用:核对贡献是 embed、插件在该组织启用;对话嵌入要求调用者能访问该对话(否则 404,与视图一致),
144
240
  * 组织页面不收 `conversationId`。返回签发令牌所需的目标(令牌由调用方签)。
145
241
  */
146
242
  async embed(t) {
147
- const { fileId, ...target } = t;
148
- const { entry, args } = await this.resolve({ ...target, locale: "en" }, "embed");
243
+ let fileId;
244
+ let target;
245
+ if (t.scope === "platform")
246
+ target = { ...t, locale: "en" };
247
+ else {
248
+ const { fileId: f, ...org } = t;
249
+ fileId = f;
250
+ target = { ...org, locale: "en" };
251
+ }
252
+ const { entry, args } = await this.resolve(target, "embed");
149
253
  const path = entry.contribution.path;
150
254
  if (path === undefined)
151
255
  throw notFound();
152
- const preview = entry.contribution.place === "file-preview";
256
+ const preview = entry.slot.id === "file.preview";
153
257
  if (preview !== (fileId !== undefined)) {
154
258
  throw new ApiError("validation_failed", preview
155
259
  ? "fileId is required for a file-preview embed"
156
260
  : "fileId is only allowed for a file-preview embed");
157
261
  }
158
262
  if (fileId !== undefined) {
263
+ // a file-preview embed is an org's (a platform target never carries a fileId)
264
+ if (args.orgId === undefined)
265
+ throw notFound();
159
266
  const allowed = this.d.previewEmbedAllowed !== undefined &&
160
267
  (await this.d.previewEmbedAllowed({
161
- orgId: t.orgId,
268
+ orgId: args.orgId,
162
269
  accountId: t.accountId,
163
270
  conversationId: args.conversationId,
164
271
  fileId,
@@ -173,6 +280,7 @@ let UiViews = class UiViews {
173
280
  contribution: t.id,
174
281
  path,
175
282
  ...(args.conversationId !== undefined && { conversationId: args.conversationId }),
283
+ ...(args.agentId !== undefined && { agentId: args.agentId }),
176
284
  ...(fileId !== undefined && { fileId }),
177
285
  };
178
286
  }
@@ -205,10 +313,20 @@ let UiViews = class UiViews {
205
313
  panels(plugin) {
206
314
  return new Set(this.d.host
207
315
  .uiContributions()
208
- .filter((c) => c.plugin === plugin && normalizePlace(c.contribution.place) === "panel")
316
+ .filter((c) => c.plugin === plugin && c.slot.id === "conversation.panel")
209
317
  .map((c) => c.contribution.id));
210
318
  }
211
- /** 找贡献、核对组织启用与对话访问;返回传给插件的参数(不含 signal)。 */
319
+ /**
320
+ * The single access gate of the org UI routes (UI slots spec §7): finds the contribution (a
321
+ * retired alias answers with the one it names), then checks, in order, that its slot is reachable
322
+ * from an org (not `platform`), the role meets the slot's `minRole`, the extension is enabled in
323
+ * the org, its effective config is valid, and the slot's scope rule (conversation: readable;
324
+ * agent: readable and in this org; org: the role alone; file: a conversation the caller reads,
325
+ * the previewer's grant is checked by `embed`). A platform target (the admin routes) takes the
326
+ * platform gate instead ({@link platformArgs}): only a `platform.page` contribution. Returns the
327
+ * args passed to the extension (no signal). Anything refused for a contribution that exists is
328
+ * 404 `ui.not_found`.
329
+ */
212
330
  async resolve(t, need) {
213
331
  const loaded = this.d.host.uiContributions();
214
332
  const named = loaded.find((c) => c.plugin === t.plugin && c.contribution.id === t.id);
@@ -221,8 +339,13 @@ let UiViews = class UiViews {
221
339
  (need === "embed"
222
340
  ? c.contribution.kind === "embed"
223
341
  : c.contribution.kind === "view" && c[need] !== undefined));
224
- if (!entry || !canUsePlace(entry.contribution.place, t.role))
342
+ if (t.scope === "platform") {
343
+ const found = platformEntry(entry);
344
+ return { entry: found, args: this.platformArgs(t, found) };
345
+ }
346
+ if (!entry || entry.slot.scope === "platform" || !canUseSlot(entry.slot, t.role)) {
225
347
  throw notFound();
348
+ }
226
349
  if (!(await this.d.enabledPlugins(t.orgId)).has(t.plugin))
227
350
  throw notFound();
228
351
  // spec A §6: the extension is not called for an org whose effective config is not valid
@@ -230,25 +353,46 @@ let UiViews = class UiViews {
230
353
  if (!effective.ok) {
231
354
  throw new ApiError("extension.needs_config", `Extension ${t.plugin} ${NEEDS_CONFIG_REASON}`);
232
355
  }
356
+ const scope = entry.slot.scope;
233
357
  const base = {
234
358
  orgId: t.orgId,
235
359
  accountId: t.accountId,
236
360
  role: t.role,
361
+ slot: entry.slot.id,
362
+ scope,
237
363
  locale: t.locale,
238
364
  config: effective.config,
239
365
  };
240
- if (!isConversationPlace(entry.contribution.place)) {
241
- if (t.item !== undefined || t.card !== undefined) {
242
- throw new ApiError("validation_failed", "item and callId are for conversation views");
366
+ const what = need === "embed" ? "embed" : "view";
367
+ const conversational = scope === "conversation" || scope === "file";
368
+ if (!conversational && (t.item !== undefined || t.card !== undefined)) {
369
+ throw new ApiError("validation_failed", "item and callId are for conversation views");
370
+ }
371
+ if (scope !== "agent" && t.agentId !== undefined) {
372
+ throw new ApiError("validation_failed", `agentId is only allowed for an agent ${what}`);
373
+ }
374
+ if (scope === "agent") {
375
+ if (t.conversationId !== undefined) {
376
+ throw new ApiError("validation_failed", `conversationId is not allowed for an agent ${what}`);
377
+ }
378
+ const agentId = t.agentId;
379
+ if (agentId === undefined) {
380
+ throw new ApiError("validation_failed", `agentId is required for an agent ${what}`);
381
+ }
382
+ if (!UUID_RE.test(agentId) || !(await this.d.canReadAgent(t.orgId, agentId, t.accountId))) {
383
+ throw notFound();
243
384
  }
385
+ return { entry, args: { ...base, agentId } };
386
+ }
387
+ if (!conversational) {
244
388
  if (t.conversationId !== undefined) {
245
- throw new ApiError("validation_failed", `conversationId is not allowed for an organization page ${need === "embed" ? "embed" : "view"}`);
389
+ throw new ApiError("validation_failed", `conversationId is not allowed for an organization page ${what}`);
246
390
  }
247
391
  return { entry, args: base };
248
392
  }
249
393
  const conversationId = t.conversationId;
250
394
  if (conversationId === undefined) {
251
- throw new ApiError("validation_failed", `conversationId is required for a conversation ${need === "embed" ? "embed" : "view"}`);
395
+ throw new ApiError("validation_failed", `conversationId is required for a conversation ${what}`);
252
396
  }
253
397
  if (!UUID_RE.test(conversationId) ||
254
398
  !(await this.d.canAccessConversation(t.orgId, conversationId, t.accountId))) {
@@ -260,7 +404,7 @@ let UiViews = class UiViews {
260
404
  if (t.card !== undefined) {
261
405
  // args.card is attested: an inline view, and a tool_result of this conversation recorded this
262
406
  // very card (plugin, the requested id or its alias target, key); else it is not found
263
- const recorded = normalizePlace(entry.contribution.place) === "inline" && this.d.recordedCard !== undefined
407
+ const recorded = entry.slot.id === "conversation.tool-result.card" && this.d.recordedCard !== undefined
264
408
  ? await this.d.recordedCard(conversationId, t.card.callId)
265
409
  : null;
266
410
  if (recorded === null ||
@@ -280,13 +424,32 @@ let UiViews = class UiViews {
280
424
  },
281
425
  };
282
426
  }
427
+ /**
428
+ * The platform gate (UI slots spec §7): the contribution is a `platform.page` of a running
429
+ * extension (no org enablement applies), given the platform's deployment config. The caller is a
430
+ * super admin (`@SuperAdmin` on the admin routes); anything else is 404 `ui.not_found`.
431
+ */
432
+ platformArgs(t, entry) {
433
+ const effective = this.d.effective.platform(t.plugin);
434
+ // `platform()` fails only when the extension is not running here: not found
435
+ if (!effective.ok)
436
+ throw notFound();
437
+ return {
438
+ scope: "platform",
439
+ accountId: t.accountId,
440
+ role: "superAdmin",
441
+ slot: entry.slot.id,
442
+ locale: t.locale,
443
+ config: effective.config,
444
+ };
445
+ }
283
446
  /** 带时限调用插件:超时中止信号并 504;抛错 502(render / action 各一个码,不回显插件的异常信息)。 */
284
447
  call(t, what, fn) {
285
448
  return callExtension({
286
449
  log: this.d.log,
287
450
  plugin: t.plugin,
288
451
  what,
289
- fields: { plugin: t.plugin, contribution: t.id, orgId: t.orgId },
452
+ fields: { plugin: t.plugin, contribution: t.id, ...whereOf(t) },
290
453
  timeoutMs: UI_CALL_TIMEOUT_MS,
291
454
  failCode: what === "render" ? "ui.render_failed" : "ui.action_failed",
292
455
  failMessage: `Plugin ${t.plugin} failed to ${what} ${t.id}`,
@@ -300,7 +463,7 @@ let UiViews = class UiViews {
300
463
  this.d.log.warn(`plugin ui ${what} returned an invalid view`, {
301
464
  plugin: t.plugin,
302
465
  contribution: t.id,
303
- orgId: t.orgId,
466
+ ...whereOf(t),
304
467
  problems: problems.slice(0, MAX_LOGGED_PROBLEMS),
305
468
  });
306
469
  throw new ApiError("ui.invalid_view", `Plugin ${t.plugin} returned an invalid view`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.12",
3
+ "version": "0.1.0-rc.13",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,7 +65,7 @@
65
65
  "yaml": "^2.9.1",
66
66
  "yauzl": "3.4.0",
67
67
  "zod": "^4.6.7",
68
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.8"
68
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.9"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",
@@ -74,7 +74,7 @@
74
74
  "@types/ws": "^8.18.2",
75
75
  "@types/yauzl": "3.4.0",
76
76
  "ws": "^8.22.0",
77
- "@capybara-hq/testing": "0.1.0-rc.11"
77
+ "@capybara-hq/testing": "0.1.0-rc.12"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "tsc -b"
@@ -0,0 +1,136 @@
1
+ -- One name: devices (spec 2026-11-07-environments-design §15.2). Every environment table, column,
2
+ -- index and constraint takes the device name; a connected machine's source 'device' becomes
3
+ -- 'local', the kernel-internal provider '@device' becomes '@local', and the log event
4
+ -- 'environment_paths' becomes 'device_paths' (in the log and in eval case snapshots). The kernel
5
+ -- is renamed in the same release; before 0.1.0, so no compatibility layer and no rolling deploy
6
+ -- across this migration. Every statement is guarded, so a rerun changes nothing.
7
+
8
+ -- capybara:allow-destructive table rename; the kernel is renamed in the same release (not released, no compatibility layer)
9
+ ALTER TABLE IF EXISTS environments RENAME TO devices;
10
+ -- capybara:allow-destructive table rename; the kernel is renamed in the same release (not released, no compatibility layer)
11
+ ALTER TABLE IF EXISTS environment_instances RENAME TO device_instances;
12
+ -- capybara:allow-destructive table rename; the kernel is renamed in the same release (not released, no compatibility layer)
13
+ ALTER TABLE IF EXISTS environment_defaults RENAME TO device_defaults;
14
+ -- capybara:allow-destructive table rename; the kernel is renamed in the same release (not released, no compatibility layer)
15
+ ALTER TABLE IF EXISTS environment_machines RENAME TO device_machines;
16
+
17
+ -- RENAME COLUMN has no IF EXISTS: checked against information_schema first.
18
+ -- capybara:allow-destructive column rename environment_id -> device_id; the kernel is renamed in the same release (not released)
19
+ DO $$
20
+ DECLARE
21
+ t text;
22
+ BEGIN
23
+ FOREACH t IN ARRAY ARRAY['device_instances', 'device_defaults', 'device_machines',
24
+ 'conversations', 'background_tasks'] LOOP
25
+ IF EXISTS (
26
+ SELECT 1 FROM information_schema.columns
27
+ WHERE table_schema = 'capybara' AND table_name = t AND column_name = 'environment_id'
28
+ ) THEN
29
+ EXECUTE format('ALTER TABLE capybara.%I RENAME COLUMN environment_id TO device_id', t);
30
+ END IF;
31
+ END LOOP;
32
+ END
33
+ $$;
34
+
35
+ -- Indexes, including those behind the primary keys and unique constraints (renaming such an
36
+ -- index renames its constraint too).
37
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
38
+ ALTER INDEX IF EXISTS environments_pkey RENAME TO devices_pkey;
39
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
40
+ ALTER INDEX IF EXISTS environments_default_uq RENAME TO devices_default_uq;
41
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
42
+ ALTER INDEX IF EXISTS environments_name_uq RENAME TO devices_name_uq;
43
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
44
+ ALTER INDEX IF EXISTS environment_instances_pkey RENAME TO device_instances_pkey;
45
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
46
+ ALTER INDEX IF EXISTS environment_instances_running_idx RENAME TO device_instances_running_idx;
47
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
48
+ ALTER INDEX IF EXISTS environment_instances_live_idx RENAME TO device_instances_live_idx;
49
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
50
+ ALTER INDEX IF EXISTS environment_instances_org_idx RENAME TO device_instances_org_idx;
51
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
52
+ ALTER INDEX IF EXISTS environment_defaults_pkey RENAME TO device_defaults_pkey;
53
+ -- capybara:allow-destructive index rename; names follow the column rename (not released)
54
+ ALTER INDEX IF EXISTS background_tasks_environment_running_idx RENAME TO background_tasks_device_running_idx;
55
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
56
+ ALTER INDEX IF EXISTS environment_machines_pkey RENAME TO device_machines_pkey;
57
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
58
+ ALTER INDEX IF EXISTS environment_machines_credential_hash_key RENAME TO device_machines_credential_hash_key;
59
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
60
+ ALTER INDEX IF EXISTS environment_machines_active_uq RENAME TO device_machines_active_uq;
61
+ -- capybara:allow-destructive index rename; names follow the table rename (not released)
62
+ ALTER INDEX IF EXISTS environment_machines_org_idx RENAME TO device_machines_org_idx;
63
+
64
+ -- NOT NULL constraints are unnamed on PostgreSQL 17 (the target), so nothing to rename there.
65
+ -- The other CHECK and foreign key constraints: RENAME CONSTRAINT has no IF EXISTS, so each is
66
+ -- checked against pg_constraint first. The source CHECK is not renamed here: it is replaced
67
+ -- below with the new value list.
68
+ -- capybara:allow-destructive constraint rename; names follow the table and column renames (not released)
69
+ DO $$
70
+ DECLARE
71
+ r record;
72
+ BEGIN
73
+ FOR r IN
74
+ SELECT * FROM (VALUES
75
+ ('devices', 'environments_org_id_fkey', 'devices_org_id_fkey'),
76
+ ('devices', 'environments_owner_account_id_fkey', 'devices_owner_account_id_fkey'),
77
+ ('devices', 'environments_owner_kind_check', 'devices_owner_kind_check'),
78
+ ('device_instances', 'environment_instances_fs_status_check', 'device_instances_fs_status_check'),
79
+ ('device_instances', 'environment_instances_status_check', 'device_instances_status_check'),
80
+ ('device_instances', 'environment_instances_environment_id_fkey', 'device_instances_device_id_fkey'),
81
+ ('device_defaults', 'environment_defaults_environment_id_fkey', 'device_defaults_device_id_fkey'),
82
+ ('conversations', 'conversations_environment_id_fkey', 'conversations_device_id_fkey'),
83
+ ('device_machines', 'environment_machines_mode_check', 'device_machines_mode_check'),
84
+ ('device_machines', 'environment_machines_org_id_fkey', 'device_machines_org_id_fkey'),
85
+ ('device_machines', 'environment_machines_environment_id_fkey', 'device_machines_device_id_fkey')
86
+ ) AS v(tbl, old_name, new_name)
87
+ LOOP
88
+ IF EXISTS (
89
+ SELECT 1 FROM pg_constraint c
90
+ JOIN pg_namespace s ON s.oid = c.connamespace
91
+ WHERE s.nspname = 'capybara' AND c.conname = r.old_name
92
+ ) THEN
93
+ EXECUTE format('ALTER TABLE capybara.%I RENAME CONSTRAINT %I TO %I',
94
+ r.tbl, r.old_name, r.new_name);
95
+ END IF;
96
+ END LOOP;
97
+ END
98
+ $$;
99
+
100
+ -- A connected machine's source 'device' becomes 'local': the old CHECK goes, the rows change, and
101
+ -- the CHECK comes back under the new name with the new list.
102
+ ALTER TABLE devices DROP CONSTRAINT IF EXISTS environments_source_check;
103
+ ALTER TABLE devices DROP CONSTRAINT IF EXISTS devices_source_check;
104
+ UPDATE devices SET source = 'local' WHERE source = 'device';
105
+ ALTER TABLE devices
106
+ ADD CONSTRAINT devices_source_check CHECK (source IN ('cloud', 'local'));
107
+
108
+ -- The kernel-internal provider '@device' becomes '@local' wherever a provider name is stored.
109
+ UPDATE devices SET provider = '@local' WHERE provider = '@device';
110
+ UPDATE device_instances SET provider = '@local' WHERE provider = '@device';
111
+ UPDATE device_instances SET prev_provider = '@local' WHERE prev_provider = '@device';
112
+
113
+ -- The log event 'environment_paths' becomes 'device_paths' (its idempotency key prefix too); the
114
+ -- rows are updated first, so the old partial index (0094) finds them, then the index follows.
115
+ UPDATE conversation_events
116
+ SET type = 'device_paths',
117
+ idem_key = CASE
118
+ WHEN idem_key LIKE 'environment_paths:%' THEN 'device_paths:' || substr(idem_key, 19)
119
+ ELSE idem_key
120
+ END
121
+ WHERE type = 'environment_paths';
122
+ DROP INDEX IF EXISTS conversation_events_environment_paths_idx;
123
+ CREATE INDEX IF NOT EXISTS conversation_events_device_paths_idx
124
+ ON conversation_events (conversation_id, seq)
125
+ WHERE type = 'device_paths';
126
+
127
+ -- Eval case snapshots carry the events of their run: the same event type change, order kept.
128
+ -- snapshot_bytes is not recomputed: only type names change (a few bytes shorter), harmless.
129
+ UPDATE eval_cases
130
+ SET snapshot = jsonb_set(snapshot, '{events}', (
131
+ SELECT jsonb_agg(
132
+ CASE WHEN e->>'type' = 'environment_paths' THEN jsonb_set(e, '{type}', '"device_paths"')
133
+ ELSE e END
134
+ ORDER BY ord)
135
+ FROM jsonb_array_elements(snapshot->'events') WITH ORDINALITY AS t(e, ord)))
136
+ WHERE snapshot @? '$.events[*] ? (@.type == "environment_paths")';
@@ -1,7 +0,0 @@
1
- /**
2
- * Device sign-in (environments spec §13.3): {@link DeviceAuthService} behind
3
- * `DeviceAuthController`. Kept apart from {@link DevicesModule}, which `EnvironmentsModule`
4
- * imports (deleting an environment revokes its machine), while sign-in needs both.
5
- */
6
- export declare class DeviceAuthModule {
7
- }
@@ -1,75 +0,0 @@
1
- import { ApiError } from "../errors.js";
2
- import { KernelDb } from "../infra/database/kernel-db.js";
3
- import { TransactionHost } from "../infra/database/transaction-host.js";
4
- import { SandboxProviders } from "../sandbox/providers.js";
5
- import { EnvironmentAccess } from "./access.js";
6
- import { EnvironmentsService } from "./environments.service.js";
7
- /** `details[0].reason` of the error an anonymous IM conversation's sandbox call gets. */
8
- export declare const ANONYMOUS_NO_ENVIRONMENT = "anonymous";
9
- /** Anonymous IM senders have no environment (spec §4.2 item 8): their sandbox tools are off. */
10
- export declare const anonymousNoEnvironment: () => ApiError;
11
- /** How a new conversation picks its environment. */
12
- export interface NewConversationEnvironment {
13
- /** The environment the person chose (the composer's picker); checked with `usable`. */
14
- environmentId?: string;
15
- /** An IM thread: the person's IM default (`environment_defaults`) when still usable. */
16
- im?: boolean;
17
- }
18
- /**
19
- * Which environment a conversation works in (spec 2026-11-07-environments-design §4.1 items 1, 3;
20
- * §4.2 items 8, 9, 12). A conversation is bound once, to an environment of its owner
21
- * (`conversations.account_id`): the one chosen at its first send, else the owner's IM default
22
- * (IM threads), else the owner's default environment. Its sub-agents use the root's. Access is
23
- * checked when it is bound and again on every sandbox tool call ({@link forTool}); only the owner
24
- * may use a personal environment, so a conversation never reaches another person's.
25
- */
26
- export declare class ConversationEnvironments {
27
- private readonly db;
28
- readonly txHost: TransactionHost;
29
- private readonly providers;
30
- private readonly access;
31
- private readonly environments;
32
- constructor(db: KernelDb, txHost: TransactionHost, providers: Pick<SandboxProviders, "cloud"> & Partial<Pick<SandboxProviders, "forName">>, access: Pick<EnvironmentAccess, "usable">, environments: Pick<EnvironmentsService, "ensureDefault">);
33
- /**
34
- * The environment a new conversation of `accountId` is bound to: the chosen one when it is
35
- * usable by the person (else `environment.not_found` / `environment.unavailable`, and nothing
36
- * is created); without a choice the IM default (IM only) or the person's default environment.
37
- * Null (unbound) for anonymous IM senders, and without a cloud sandbox provider when no usable
38
- * IM default (a connected machine's environment, say) applies.
39
- */
40
- forNew(orgId: string, accountId: string, pick?: NewConversationEnvironment): Promise<string | null>;
41
- /**
42
- * The environment a sandbox tool call of the conversation tree rooted at `rootConversationId`
43
- * enters, checked for its owner now. A conversation still unbound (created before
44
- * environments, or while no provider existed) is bound first, to the owner's IM default when it
45
- * is an IM thread, else to the owner's default environment; a racing call binds it once.
46
- * Throws `sandbox.unavailable` for a cloud environment (or none to bind) without a cloud
47
- * provider and for an anonymous IM conversation, and `environment.not_found` /
48
- * `environment.unavailable` when the owner may not use it now. A conversation bound to a
49
- * connected machine's environment (`@device`) does not need a cloud provider.
50
- */
51
- forTool(orgId: string, rootConversationId: string): Promise<string>;
52
- /**
53
- * Whether a sandbox tool call of the tree rooted at `rootConversationId` has a provider to run
54
- * on, for tool listings (ruling R8; no binding, no access check, never throws): a cloud
55
- * provider is loaded, or the environment the conversation is bound to (an unbound IM thread:
56
- * its owner's IM default) runs on a registered kernel provider (`@device`). A forbidden or gone
57
- * environment still counts: the call then says why.
58
- */
59
- usableNow(orgId: string, rootConversationId: string): Promise<boolean>;
60
- /**
61
- * The person's IM default when `im` and it is still theirs, live, in this org and usable (the
62
- * table has no such guard), else their default environment (created when missing); null when
63
- * that is needed and there is no cloud provider to make or use it. The owner having left the org
64
- * reads as `environment.not_found`.
65
- */
66
- private defaultFor;
67
- /** Binds an unbound conversation to `environmentId`; returns what it is bound to now. */
68
- private bind;
69
- /** The environment runs on a kernel-internal provider (`@device`). */
70
- private isInternal;
71
- /** The account is the org's anonymous IM guest (no person behind it). */
72
- private isAnonymous;
73
- /** The conversation is (or was) an IM thread's. */
74
- private isImThread;
75
- }
@@ -1,25 +0,0 @@
1
- import { z } from "zod";
2
- /** An environment's name: trimmed, 1-64 characters (unique per person, case-insensitive). */
3
- export declare const EnvironmentName: z.ZodString;
4
- export declare const EnvironmentNameBody: z.ZodObject<{
5
- name: z.ZodString;
6
- }, z.core.$strict>;
7
- export declare const EnvironmentParams: z.ZodObject<{
8
- id: z.ZodString;
9
- }, z.core.$strip>;
10
- declare const EnvironmentCreateBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
11
- name: z.ZodString;
12
- }, z.core.$strict>>;
13
- export declare class EnvironmentCreateBodyDto extends EnvironmentCreateBodyDto_base {
14
- }
15
- declare const EnvironmentRenameBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
16
- name: z.ZodString;
17
- }, z.core.$strict>>;
18
- export declare class EnvironmentRenameBodyDto extends EnvironmentRenameBodyDto_base {
19
- }
20
- declare const EnvironmentParamsDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
21
- id: z.ZodString;
22
- }, z.core.$strip>>;
23
- export declare class EnvironmentParamsDto extends EnvironmentParamsDto_base {
24
- }
25
- export {};