@capybara-hq/kernel 0.1.0-rc.22 → 0.1.0-rc.24

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 (234) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.controller.d.ts +3 -3
  3. package/dist/admin/admin.controller.js +6 -2
  4. package/dist/admin/admin.module.d.ts +2 -1
  5. package/dist/admin/admin.module.js +7 -3
  6. package/dist/admin/admin.schemas.d.ts +13 -0
  7. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  8. package/dist/admin/outbox-dead.controller.js +62 -0
  9. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  10. package/dist/admin/outbox-dead.schemas.js +18 -0
  11. package/dist/admin/outbox-dead.service.d.ts +66 -0
  12. package/dist/admin/outbox-dead.service.js +179 -0
  13. package/dist/admin/overview.service.d.ts +40 -5
  14. package/dist/admin/overview.service.js +41 -7
  15. package/dist/agents/from-template.js +1 -1
  16. package/dist/background/finish.d.ts +5 -2
  17. package/dist/background/finish.js +14 -3
  18. package/dist/background/launch.d.ts +44 -5
  19. package/dist/background/launch.js +92 -9
  20. package/dist/background/session.d.ts +14 -2
  21. package/dist/background/session.js +26 -4
  22. package/dist/background/status.d.ts +1 -1
  23. package/dist/background/status.js +3 -3
  24. package/dist/background/stop.d.ts +0 -2
  25. package/dist/background/stop.js +1 -21
  26. package/dist/background/store.d.ts +8 -0
  27. package/dist/background/store.js +24 -0
  28. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  29. package/dist/catalog/admin-catalog.controller.js +45 -6
  30. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  31. package/dist/catalog/admin-catalog.schemas.js +8 -0
  32. package/dist/catalog/catalog-content.d.ts +5 -0
  33. package/dist/catalog/catalog-content.js +1 -0
  34. package/dist/catalog/catalog.module.d.ts +2 -1
  35. package/dist/catalog/catalog.module.js +5 -1
  36. package/dist/catalog/marketplaces.service.d.ts +1 -1
  37. package/dist/catalog/platform-import-update.d.ts +50 -0
  38. package/dist/catalog/platform-import-update.js +116 -0
  39. package/dist/catalog/plugin-import.d.ts +36 -1
  40. package/dist/catalog/plugin-import.js +23 -1
  41. package/dist/catalog/plugin-upgrade.d.ts +18 -0
  42. package/dist/catalog/plugin-upgrade.js +19 -0
  43. package/dist/catalog/requests.service.js +2 -2
  44. package/dist/channels/channels.module.d.ts +5 -2
  45. package/dist/channels/channels.module.js +9 -2
  46. package/dist/channels/deliver-job.d.ts +25 -0
  47. package/dist/channels/deliver-job.js +25 -0
  48. package/dist/channels/inbound.d.ts +1 -1
  49. package/dist/channels/inbound.js +2 -15
  50. package/dist/channels/jobs.d.ts +28 -18
  51. package/dist/channels/jobs.js +63 -26
  52. package/dist/config/schema.d.ts +3 -6
  53. package/dist/config/schema.js +20 -27
  54. package/dist/conversations/agent-tree.d.ts +16 -0
  55. package/dist/conversations/agent-tree.js +36 -0
  56. package/dist/conversations/conversations.service.d.ts +44 -14
  57. package/dist/conversations/conversations.service.js +52 -38
  58. package/dist/conversations/event-log.d.ts +7 -10
  59. package/dist/conversations/event-log.js +24 -15
  60. package/dist/conversations/events.js +2 -1
  61. package/dist/conversations/queue.service.d.ts +8 -3
  62. package/dist/conversations/queue.service.js +37 -13
  63. package/dist/conversations/titles.d.ts +21 -8
  64. package/dist/conversations/titles.js +42 -15
  65. package/dist/db/kernel-schema.d.ts +26 -8
  66. package/dist/devices/devices.service.d.ts +12 -3
  67. package/dist/devices/devices.service.js +30 -9
  68. package/dist/errors.d.ts +15 -0
  69. package/dist/errors.js +15 -0
  70. package/dist/evals/run-replay.js +14 -8
  71. package/dist/extensions/extensions.module.js +3 -0
  72. package/dist/extensions/lifecycle.d.ts +6 -1
  73. package/dist/extensions/lifecycle.js +9 -2
  74. package/dist/extensions/usage-subscribers.d.ts +43 -0
  75. package/dist/extensions/usage-subscribers.js +108 -0
  76. package/dist/http/api.module.js +2 -0
  77. package/dist/identity/settings.d.ts +53 -3
  78. package/dist/identity/settings.js +92 -9
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +1 -0
  81. package/dist/infra/cli/kernel-cli.module.js +5 -0
  82. package/dist/infra/config/kernel-config.service.d.ts +7 -0
  83. package/dist/infra/config/kernel-config.service.js +20 -0
  84. package/dist/infra/events/kernel-events.d.ts +9 -35
  85. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  86. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  87. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  88. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  89. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  90. package/dist/infra/outbox/outbox.module.js +50 -0
  91. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  92. package/dist/infra/outbox/outbox.relay.js +76 -0
  93. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  94. package/dist/infra/outbox/outbox.service.js +131 -0
  95. package/dist/infra/outbox/registry.d.ts +120 -0
  96. package/dist/infra/outbox/registry.js +108 -0
  97. package/dist/infra/queue/queue-names.d.ts +8 -0
  98. package/dist/infra/queue/queue-names.js +16 -0
  99. package/dist/kernel.module.js +3 -0
  100. package/dist/local-devices/device-policy.service.d.ts +4 -0
  101. package/dist/local-devices/device-policy.service.js +61 -3
  102. package/dist/local-devices/local-device-gateway.d.ts +4 -2
  103. package/dist/local-devices/local-device-gateway.js +39 -14
  104. package/dist/local-devices/machines.d.ts +25 -1
  105. package/dist/local-devices/machines.js +91 -15
  106. package/dist/local-devices/policy-summary.d.ts +44 -1
  107. package/dist/local-devices/policy-summary.js +69 -0
  108. package/dist/models/embeddings.d.ts +9 -9
  109. package/dist/models/embeddings.js +11 -18
  110. package/dist/models/models.module.js +3 -0
  111. package/dist/orgs/orgs.service.js +7 -0
  112. package/dist/orgs/settings.controller.js +2 -2
  113. package/dist/orgs/settings.service.d.ts +11 -5
  114. package/dist/orgs/settings.service.js +28 -14
  115. package/dist/overrides.d.ts +20 -3
  116. package/dist/overrides.js +10 -2
  117. package/dist/platform/exclusions.js +5 -0
  118. package/dist/platform/ops/admin.ops.js +50 -9
  119. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  120. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  121. package/dist/platform/ops/org-settings.ops.js +3 -3
  122. package/dist/platform/ops/orgs.ops.js +1 -0
  123. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  124. package/dist/platform/ops/outbox.ops.js +125 -0
  125. package/dist/platform/ops/platform.ops.js +1 -1
  126. package/dist/platform/ops/schedules.ops.js +40 -1
  127. package/dist/platform/platform-ops.module.d.ts +2 -1
  128. package/dist/platform/platform-ops.module.js +3 -0
  129. package/dist/platform/route-schemas.js +3 -0
  130. package/dist/roles/worker.module.js +9 -4
  131. package/dist/runtime/langgraph-loop.js +13 -5
  132. package/dist/runtime/limits.d.ts +15 -0
  133. package/dist/runtime/limits.js +25 -0
  134. package/dist/runtime/loop.d.ts +8 -3
  135. package/dist/runtime/loop.js +2 -1
  136. package/dist/runtime/maintenance.d.ts +4 -2
  137. package/dist/runtime/maintenance.js +17 -2
  138. package/dist/runtime/model-call.d.ts +14 -16
  139. package/dist/runtime/model-call.js +14 -24
  140. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  141. package/dist/runtime/queue-lost-jobs.js +51 -0
  142. package/dist/runtime/run-control.d.ts +42 -5
  143. package/dist/runtime/run-control.js +66 -12
  144. package/dist/runtime/run-finish.d.ts +39 -12
  145. package/dist/runtime/run-finish.js +97 -14
  146. package/dist/runtime/run-jobs.d.ts +95 -13
  147. package/dist/runtime/run-jobs.js +199 -28
  148. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  149. package/dist/runtime/run-orphan-check.job.js +339 -0
  150. package/dist/runtime/run-processors.d.ts +16 -9
  151. package/dist/runtime/run-processors.js +1 -1
  152. package/dist/runtime/run-queue.d.ts +64 -55
  153. package/dist/runtime/run-queue.js +147 -284
  154. package/dist/runtime/run-steps.d.ts +1 -2
  155. package/dist/runtime/run-steps.js +8 -11
  156. package/dist/runtime/run-worker.d.ts +6 -17
  157. package/dist/runtime/run-worker.js +32 -34
  158. package/dist/runtime/runtime.module.d.ts +2 -3
  159. package/dist/runtime/runtime.module.js +5 -6
  160. package/dist/runtime/step-log.d.ts +16 -4
  161. package/dist/runtime/step-log.js +26 -6
  162. package/dist/runtime/stream.controller.d.ts +2 -4
  163. package/dist/runtime/stream.controller.js +5 -11
  164. package/dist/runtime/subagents/child.js +1 -1
  165. package/dist/runtime/subagents/definition.d.ts +2 -1
  166. package/dist/runtime/subagents/definition.js +4 -2
  167. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  168. package/dist/runtime/subagents/deliveries.js +16 -7
  169. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  170. package/dist/runtime/subagents/dispatch.js +13 -38
  171. package/dist/runtime/subagents/messages.d.ts +5 -1
  172. package/dist/runtime/subagents/messages.js +11 -3
  173. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  174. package/dist/runtime/subagents/skill-fork.js +5 -5
  175. package/dist/runtime/subagents/subagents.service.js +1 -1
  176. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  177. package/dist/runtime/subagents/tree-jobs.js +86 -0
  178. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  179. package/dist/runtime/usage-report-jobs.js +67 -119
  180. package/dist/runtime/usage-report.d.ts +28 -77
  181. package/dist/runtime/usage-report.js +35 -213
  182. package/dist/runtime/waits.d.ts +48 -198
  183. package/dist/runtime/waits.js +103 -805
  184. package/dist/sandbox/platform-tiers.d.ts +86 -0
  185. package/dist/sandbox/platform-tiers.js +97 -0
  186. package/dist/sandbox/sandbox.service.d.ts +18 -5
  187. package/dist/sandbox/sandbox.service.js +75 -20
  188. package/dist/sandbox/tiers.d.ts +14 -2
  189. package/dist/sandbox/tiers.js +8 -0
  190. package/dist/schedules/calendar.d.ts +134 -0
  191. package/dist/schedules/calendar.js +410 -0
  192. package/dist/schedules/fire.d.ts +1 -3
  193. package/dist/schedules/fire.js +5 -10
  194. package/dist/schedules/run-tracker.d.ts +0 -5
  195. package/dist/schedules/run-tracker.js +0 -13
  196. package/dist/schedules/scan.job.js +15 -20
  197. package/dist/schedules/schedule-math.d.ts +42 -0
  198. package/dist/schedules/schedule-math.js +52 -0
  199. package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
  200. package/dist/schedules/scheduled-tasks.service.js +53 -0
  201. package/dist/schedules/schedules.controller.d.ts +5 -1
  202. package/dist/schedules/schedules.controller.js +15 -1
  203. package/dist/schedules/schedules.schemas.d.ts +35 -0
  204. package/dist/schedules/schedules.schemas.js +27 -0
  205. package/dist/schedules/store.d.ts +5 -0
  206. package/dist/schedules/store.js +20 -0
  207. package/dist/tools/approvals.service.d.ts +26 -7
  208. package/dist/tools/approvals.service.js +88 -10
  209. package/dist/tools/builtin-names.d.ts +5 -3
  210. package/dist/tools/builtin-names.js +12 -4
  211. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  212. package/dist/tools/builtins/builtin-tools.js +21 -17
  213. package/dist/tools/builtins/sandbox/bash.js +9 -3
  214. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  215. package/dist/tools/builtins/sandbox/common.js +14 -1
  216. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  217. package/dist/tools/builtins/sandbox/index.js +4 -1
  218. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  219. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  220. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  221. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  222. package/dist/tools/builtins/web/extract.d.ts +2 -4
  223. package/dist/tools/builtins/web/extract.js +2 -5
  224. package/dist/tools/pipeline.d.ts +1 -1
  225. package/dist/tools/pipeline.js +1 -1
  226. package/dist/tools/questions.service.d.ts +15 -6
  227. package/dist/tools/questions.service.js +20 -8
  228. package/dist/tools/registry.d.ts +4 -2
  229. package/dist/tools/registry.js +4 -3
  230. package/dist/tools/tools.module.js +4 -3
  231. package/package.json +3 -3
  232. package/src/migrations/0118_device_policy_ops.sql +6 -0
  233. package/src/migrations/0119_outbox.sql +22 -0
  234. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
@@ -13,30 +13,10 @@ import { KernelDb } from "../infra/database/kernel-db.js";
13
13
  import { KERNEL_LOGGER } from "../infra/tokens.js";
14
14
  import { SandboxService } from "../sandbox/sandbox.service.js";
15
15
  import { BackgroundTaskFinisher, } from "./finish.js";
16
- import { TASKS_DIR } from "./launch.js";
17
- import { actingAs, assertTaskRef, decodeTail, killSession, tailCommand, taskActor, } from "./session.js";
16
+ import { actingAs, assertTaskRef, decodeTail, killSession, TERM_EXEC_TIMEOUT_MS, tailCommand, taskActor, termScript, } from "./session.js";
18
17
  import { BackgroundTaskStore } from "./store.js";
19
- /** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
20
- export const STOP_GRACE_MS = 5000;
21
- /** The time limit of the SIGTERM exec: the grace period plus some room. */
22
- const TERM_EXEC_TIMEOUT_MS = STOP_GRACE_MS + 5000;
23
18
  /** The time limit of reading the last lines after a stop. */
24
19
  const TAIL_EXEC_TIMEOUT_MS = 10_000;
25
- /**
26
- * Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
27
- * else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
28
- * prints `term`. Relative to the workspace root.
29
- */
30
- function termScript(id, sid) {
31
- const f = `${TASKS_DIR}/${id}`;
32
- const tries = Math.ceil(STOP_GRACE_MS / 200);
33
- return [
34
- `if [ -s ${f}.exit ]; then echo "exited $(cat ${f}.exit)"; exit 0; fi`,
35
- `pkill -TERM -s ${sid} >/dev/null 2>&1`,
36
- `i=0; while [ "$i" -lt ${tries} ] && pgrep -s ${sid} >/dev/null 2>&1; do sleep 0.2; i=$((i+1)); done`,
37
- "echo term",
38
- ].join("\n");
39
- }
40
20
  /** How a task that exited by itself ends (spec §4.3): exit 137 past its deadline timed out. */
41
21
  export function exitedAs(task, code, now) {
42
22
  if (code === 0)
@@ -137,6 +137,14 @@ export declare class BackgroundTaskStore {
137
137
  runningStarted(): Promise<BackgroundTaskRow[]>;
138
138
  /** The running tasks one conversation owns (not those of its sub-agents), oldest first. */
139
139
  runningOwnedBy(conversationId: string): Promise<BackgroundTaskRow[]>;
140
+ /**
141
+ * The finished tasks one conversation owns (not those of its sub-agents), the most recently
142
+ * finished first, at most `limit` of them, with how many it owns in all (`TaskList`).
143
+ */
144
+ finishedOwnedBy(conversationId: string, limit: number): Promise<{
145
+ rows: BackgroundTaskRow[];
146
+ total: number;
147
+ }>;
140
148
  /** The running tasks of one root conversation's tree (what `maxTasks` limits). */
141
149
  countRunning(sandboxConversationId: string): Promise<number>;
142
150
  /** The tasks owned by any of the conversations, newest first. */
@@ -258,6 +258,30 @@ let BackgroundTaskStore = class BackgroundTaskStore {
258
258
  .execute();
259
259
  return rows.map(toRow);
260
260
  }
261
+ /**
262
+ * The finished tasks one conversation owns (not those of its sub-agents), the most recently
263
+ * finished first, at most `limit` of them, with how many it owns in all (`TaskList`).
264
+ */
265
+ async finishedOwnedBy(conversationId, limit) {
266
+ const [rows, count] = await Promise.all([
267
+ this.db
268
+ .selectFrom("background_tasks")
269
+ .selectAll()
270
+ .where("conversation_id", "=", conversationId)
271
+ .where("status", "<>", "running")
272
+ .orderBy("finished_at", "desc")
273
+ .orderBy("id", "desc")
274
+ .limit(limit)
275
+ .execute(),
276
+ this.db
277
+ .selectFrom("background_tasks")
278
+ .select((eb) => eb.fn.countAll().as("n"))
279
+ .where("conversation_id", "=", conversationId)
280
+ .where("status", "<>", "running")
281
+ .executeTakeFirstOrThrow(),
282
+ ]);
283
+ return { rows: rows.map(toRow), total: Number(count.n) };
284
+ }
261
285
  /** The running tasks of one root conversation's tree (what `maxTasks` limits). */
262
286
  async countRunning(sandboxConversationId) {
263
287
  const r = await this.db
@@ -5,12 +5,13 @@ import { type ClientBuildSummary, ClientBuilds } from "../client/client-builds.s
5
5
  import { PluginGuides, type RenderedSetupGuide } from "../extensions/plugin-guides.js";
6
6
  import { PluginIcons } from "../extensions/plugin-icons.js";
7
7
  import { type Principal } from "../identity/principal.js";
8
- import { AdminCatalogGuideQuery, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogNpmImportBody as NpmBody, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody } from "./admin-catalog.schemas.js";
8
+ import { AdminCatalogGuideQuery, CatalogImportUpdateBody, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogNpmImportBody as NpmBody, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody } from "./admin-catalog.schemas.js";
9
9
  import { AppearanceApplyBody } from "./appearance.schemas.js";
10
10
  import { type AppearanceResult, AppearanceService } from "./appearance.service.js";
11
11
  import { type CatalogItemDetail, type CatalogItemView, CatalogService, type PermissionsMismatch } from "./catalog.service.js";
12
12
  import { CatalogScopeService, type ScopePreview } from "./catalog-scope.service.js";
13
13
  import { OrgCatalogService } from "./org-catalog.service.js";
14
+ import { PlatformImportUpdate, type PlatformImportUpdateResult } from "./platform-import-update.js";
14
15
  import { type PluginConfirmResult, PluginImport, type PluginReview } from "./plugin-import.js";
15
16
  import { PlatformPluginUpdater, type PlatformUpdatePreview, type PlatformUpdateResult } from "./plugin-upgrade.js";
16
17
  declare const ConfirmBody: z.ZodObject<{
@@ -41,11 +42,12 @@ export declare class AdminCatalogController {
41
42
  private readonly orgCatalog;
42
43
  private readonly scope;
43
44
  private readonly updater;
45
+ private readonly importUpdate;
44
46
  private readonly icons;
45
47
  private readonly appearance;
46
48
  private readonly clientBuilds;
47
49
  private readonly guides;
48
- constructor(catalog: CatalogService, pluginImport: PluginImport, orgCatalog: OrgCatalogService, scope: CatalogScopeService, updater: PlatformPluginUpdater, icons: PluginIcons, appearance: AppearanceService, clientBuilds: ClientBuilds, guides: PluginGuides);
50
+ constructor(catalog: CatalogService, pluginImport: PluginImport, orgCatalog: OrgCatalogService, scope: CatalogScopeService, updater: PlatformPluginUpdater, importUpdate: PlatformImportUpdate, icons: PluginIcons, appearance: AppearanceService, clientBuilds: ClientBuilds, guides: PluginGuides);
49
51
  /**
50
52
  * Prepares the import of an uploaded plugin (an npm-pack tgz as the raw body,
51
53
  * `application/octet-stream`, at most `packages.maxBytes`): reads and checks the whole plugin
@@ -108,6 +110,23 @@ export declare class AdminCatalogController {
108
110
  * is an unknown field (400 `validation_failed`).
109
111
  */
110
112
  confirm(principal: Principal, stagingId: string, body: z.infer<typeof ConfirmBody>): Promise<PluginConfirmResult>;
113
+ /**
114
+ * What confirming a staged new version of an installed platform plugin would do as the platform
115
+ * update (`PlatformImportUpdate`, the one 更新到 wizard): the update preview of the staged
116
+ * version, nothing written (cancelling the wizard leaves only the staging row). Not a platform
117
+ * plugin yet -> 409 `plugin.upgrade_target_invalid` (reason `not_installed`); staging missing ->
118
+ * 404 `plugin.staging_not_found`; otherwise as `items/:id/update/preview`.
119
+ */
120
+ importUpdatePreview(stagingId: string): Promise<PlatformUpdatePreview>;
121
+ /**
122
+ * Imports the staged version and updates the platform plugin to it, confirmed with the import
123
+ * review's `reviewHash` and the update preview's `diffHash` (201 `{ item, update }`). Stale review
124
+ * -> 409 `plugin.review_stale`, stale preview -> 409 `plugin.upgrade_stale`, another update
125
+ * running -> 409 `plugin.install_in_progress`: nothing written. The update failing after the
126
+ * import -> 409 `plugin.imported_not_updated` (the version stays in the catalog, the platform on
127
+ * its version, the extension's approval put back; details name the cause).
128
+ */
129
+ importAndUpdate(principal: Principal, stagingId: string, body: z.infer<typeof CatalogImportUpdateBody>): Promise<PlatformImportUpdateResult>;
111
130
  /**
112
131
  * The item list (`includeDeleted=true` includes deleted items). Paging: `limit` (default 200, at
113
132
  * most 1000) and `offset`; `nextOffset` is the next page's offset, null when there is none.
@@ -16,13 +16,14 @@ import { PluginIcons } from "../extensions/plugin-icons.js";
16
16
  import { CurrentPrincipal, patTokenId, requireUser, SuperAdmin, } from "../identity/principal.js";
17
17
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
18
18
  import { ZBody, ZQuery } from "../infra/validation/zod-params.js";
19
- import { AdminCatalogGuideQuery, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogHash as Hash, CatalogNpmImportBody as NpmBody, CatalogScopeOrgs as Orgs, CatalogScope as Scope, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody, } from "./admin-catalog.schemas.js";
19
+ import { AdminCatalogGuideQuery, CatalogImportUpdateBody, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogHash as Hash, CatalogNpmImportBody as NpmBody, CatalogScopeOrgs as Orgs, CatalogScope as Scope, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody, } from "./admin-catalog.schemas.js";
20
20
  import { AppearanceApplyBody } from "./appearance.schemas.js";
21
21
  import { AppearanceService, suggestedAppearanceOf, } from "./appearance.service.js";
22
22
  import { CatalogService, } from "./catalog.service.js";
23
23
  import { CatalogScopeService } from "./catalog-scope.service.js";
24
24
  import { sendCatalogIcon } from "./org-catalog.controller.js";
25
25
  import { OrgCatalogService } from "./org-catalog.service.js";
26
+ import { PlatformImportUpdate } from "./platform-import-update.js";
26
27
  import { PluginImport } from "./plugin-import.js";
27
28
  import { PlatformPluginUpdater, } from "./plugin-upgrade.js";
28
29
  const ConfirmBody = z
@@ -66,6 +67,8 @@ class CatalogUpdatePreviewBodyDto extends createZodDto(CatalogUpdatePreviewBody,
66
67
  }
67
68
  class CatalogUpdateBodyDto extends createZodDto(CatalogUpdateBody, "CatalogUpdateBody") {
68
69
  }
70
+ class CatalogImportUpdateBodyDto extends createZodDto(CatalogImportUpdateBody, "CatalogImportUpdateBody") {
71
+ }
69
72
  class AppearanceApplyBodyDto extends createZodDto(AppearanceApplyBody, "AppearanceApplyBody") {
70
73
  }
71
74
  /**
@@ -80,16 +83,18 @@ let AdminCatalogController = class AdminCatalogController {
80
83
  orgCatalog;
81
84
  scope;
82
85
  updater;
86
+ importUpdate;
83
87
  icons;
84
88
  appearance;
85
89
  clientBuilds;
86
90
  guides;
87
- constructor(catalog, pluginImport, orgCatalog, scope, updater, icons, appearance, clientBuilds, guides) {
91
+ constructor(catalog, pluginImport, orgCatalog, scope, updater, importUpdate, icons, appearance, clientBuilds, guides) {
88
92
  this.catalog = catalog;
89
93
  this.pluginImport = pluginImport;
90
94
  this.orgCatalog = orgCatalog;
91
95
  this.scope = scope;
92
96
  this.updater = updater;
97
+ this.importUpdate = importUpdate;
93
98
  this.icons = icons;
94
99
  this.appearance = appearance;
95
100
  this.clientBuilds = clientBuilds;
@@ -169,6 +174,28 @@ let AdminCatalogController = class AdminCatalogController {
169
174
  ...(body.orgs !== undefined && { orgs: body.orgs }),
170
175
  }, actorOpts(actor));
171
176
  }
177
+ /**
178
+ * What confirming a staged new version of an installed platform plugin would do as the platform
179
+ * update (`PlatformImportUpdate`, the one 更新到 wizard): the update preview of the staged
180
+ * version, nothing written (cancelling the wizard leaves only the staging row). Not a platform
181
+ * plugin yet -> 409 `plugin.upgrade_target_invalid` (reason `not_installed`); staging missing ->
182
+ * 404 `plugin.staging_not_found`; otherwise as `items/:id/update/preview`.
183
+ */
184
+ importUpdatePreview(stagingId) {
185
+ return this.importUpdate.preview(stagingId);
186
+ }
187
+ /**
188
+ * Imports the staged version and updates the platform plugin to it, confirmed with the import
189
+ * review's `reviewHash` and the update preview's `diffHash` (201 `{ item, update }`). Stale review
190
+ * -> 409 `plugin.review_stale`, stale preview -> 409 `plugin.upgrade_stale`, another update
191
+ * running -> 409 `plugin.install_in_progress`: nothing written. The update failing after the
192
+ * import -> 409 `plugin.imported_not_updated` (the version stays in the catalog, the platform on
193
+ * its version, the extension's approval put back; details name the cause).
194
+ */
195
+ importAndUpdate(principal, stagingId, body) {
196
+ const actor = requireUser(principal);
197
+ return this.importUpdate.confirm(actor.account.id, stagingId, body, actorOpts(actor));
198
+ }
172
199
  /**
173
200
  * The item list (`includeDeleted=true` includes deleted items). Paging: `limit` (default 200, at
174
201
  * most 1000) and `offset`; `nextOffset` is the next page's offset, null when there is none.
@@ -367,6 +394,17 @@ __decorate([
367
394
  __param(1, Param("stagingId")),
368
395
  __param(2, ZBody(CatalogImportConfirmBody))
369
396
  ], AdminCatalogController.prototype, "confirm", null);
397
+ __decorate([
398
+ Get("imports/:stagingId/update-preview"),
399
+ __param(0, Param("stagingId"))
400
+ ], AdminCatalogController.prototype, "importUpdatePreview", null);
401
+ __decorate([
402
+ Post("imports/:stagingId/update"),
403
+ HttpCode(201),
404
+ __param(0, CurrentPrincipal()),
405
+ __param(1, Param("stagingId")),
406
+ __param(2, ZBody(CatalogImportUpdateBodyDto))
407
+ ], AdminCatalogController.prototype, "importAndUpdate", null);
370
408
  __decorate([
371
409
  Get("items"),
372
410
  __param(0, ZQuery(CatalogItemsQueryDto))
@@ -457,9 +495,10 @@ AdminCatalogController = __decorate([
457
495
  __param(2, Inject(OrgCatalogService)),
458
496
  __param(3, Inject(CatalogScopeService)),
459
497
  __param(4, Inject(PlatformPluginUpdater)),
460
- __param(5, Inject(PluginIcons)),
461
- __param(6, Inject(AppearanceService)),
462
- __param(7, Inject(ClientBuilds)),
463
- __param(8, Inject(PluginGuides))
498
+ __param(5, Inject(PlatformImportUpdate)),
499
+ __param(6, Inject(PluginIcons)),
500
+ __param(7, Inject(AppearanceService)),
501
+ __param(8, Inject(ClientBuilds)),
502
+ __param(9, Inject(PluginGuides))
464
503
  ], AdminCatalogController);
465
504
  export { AdminCatalogController };
@@ -20,6 +20,11 @@ export declare const CatalogUpdateBody: z.ZodObject<{
20
20
  version: z.ZodString;
21
21
  diffHash: z.ZodString;
22
22
  }, z.core.$strict>;
23
+ /** `POST imports/:stagingId/update`: the import review's hash and the update preview's. */
24
+ export declare const CatalogImportUpdateBody: z.ZodObject<{
25
+ reviewHash: z.ZodString;
26
+ diffHash: z.ZodString;
27
+ }, z.core.$strict>;
23
28
  /** `:id` as the routes take it (`@Param`). */
24
29
  export declare const CatalogIdPathParams: z.ZodObject<{
25
30
  id: z.ZodString;
@@ -13,6 +13,10 @@ export const CatalogUpdatePreviewBody = z.object({ version: CatalogVersion }).st
13
13
  export const CatalogUpdateBody = z
14
14
  .object({ version: CatalogVersion, diffHash: CatalogHash })
15
15
  .strict();
16
+ /** `POST imports/:stagingId/update`: the import review's hash and the update preview's. */
17
+ export const CatalogImportUpdateBody = z
18
+ .object({ reviewHash: CatalogHash, diffHash: CatalogHash })
19
+ .strict();
16
20
  /** `:id` as the routes take it (`@Param`). */
17
21
  export const CatalogIdPathParams = z.object({ id: z.string().min(1) });
18
22
  /** `:stagingId` of a staged import as the route takes it (`@Param`). */
@@ -75,6 +79,10 @@ export const ADMIN_CATALOG_ROUTES = [
75
79
  { route: "POST /api/v1/admin/catalog/import/npm", body: CatalogNpmImportBody },
76
80
  { route: "POST /api/v1/admin/catalog/import/git", body: CatalogGitImportBody },
77
81
  { route: "GET /api/v1/admin/catalog/imports/:stagingId", params: CatalogStagingPathParams },
82
+ {
83
+ route: "GET /api/v1/admin/catalog/imports/:stagingId/update-preview",
84
+ params: CatalogStagingPathParams,
85
+ },
78
86
  {
79
87
  route: "POST /api/v1/admin/catalog/items/:id/scope/preview",
80
88
  params: CatalogIdPathParams,
@@ -72,6 +72,11 @@ export interface OrgCatalogTemplate {
72
72
  editableEndpoint: string[];
73
73
  auth: Record<string, unknown>;
74
74
  ownership: "org" | "personal" | null;
75
+ /**
76
+ * The template file says `defaultEnabled: false`: it starts switched off in every org, so the
77
+ * install wizard does not ask for it (an org admin sets it up when switching it on).
78
+ */
79
+ defaultOff: boolean;
75
80
  /**
76
81
  * The template's setup guide in the item's latest version (`GET …/catalog/:itemId/guides/
77
82
  * :guideId`), null without one (spec 2026-11-09-setup-guides-v2-design.md §9.3).
@@ -29,6 +29,7 @@ export function orgTemplateView(id, file, guide = null) {
29
29
  auth: t.auth,
30
30
  ownership: t.ownership ??
31
31
  (typeof t.auth.ownership === "string" ? t.auth.ownership : null),
32
+ defaultOff: file.defaultEnabled === false,
32
33
  guide,
33
34
  };
34
35
  }
@@ -2,7 +2,8 @@ import { CatalogImportModule } from "./catalog-import.module.js";
2
2
  export { CatalogImportModule };
3
3
  /**
4
4
  * The plugin catalog (spec §4, §10): {@link CatalogImportModule}, catalog requests, org installs
5
- * with their dependencies, the platform plugin update (`PlatformPluginUpdater`), the admin's scope
5
+ * with their dependencies, the platform plugin update (`PlatformPluginUpdater`) and the update to a
6
+ * staged version (`PlatformImportUpdate`), the admin's scope
6
7
  * and disable
7
8
  * (`CatalogScopeService`), org admins' plugin uploads (`OrgPluginUploads`) and their own plugins (`OwnedItems`), and the producer of the plugin scope sync
8
9
  * (`PluginScopeSync`; its consumer is the worker role's `PluginScopeWorkersModule`). The catalog's periodic jobs (`CatalogJobs`) are
@@ -21,6 +21,7 @@ import { OrgCatalogService } from "./org-catalog.service.js";
21
21
  import { OrgExtensionSwitch } from "./org-extension-switch.js";
22
22
  import { OrgPluginUploads } from "./org-plugin-uploads.js";
23
23
  import { OwnedItems } from "./owned-items.js";
24
+ import { PlatformImportUpdate } from "./platform-import-update.js";
24
25
  import { PluginInstaller } from "./plugin-install.js";
25
26
  import { PlatformPluginUpdater } from "./plugin-upgrade.js";
26
27
  import { CatalogRequestsService } from "./requests.service.js";
@@ -32,7 +33,8 @@ const PLUGIN_SCOPE_QUEUE_MODULE = QueueModule.registerQueue(PLUGIN_SCOPE_QUEUE);
32
33
  const MAINTENANCE_QUEUE_MODULE = QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME);
33
34
  /**
34
35
  * The plugin catalog (spec §4, §10): {@link CatalogImportModule}, catalog requests, org installs
35
- * with their dependencies, the platform plugin update (`PlatformPluginUpdater`), the admin's scope
36
+ * with their dependencies, the platform plugin update (`PlatformPluginUpdater`) and the update to a
37
+ * staged version (`PlatformImportUpdate`), the admin's scope
36
38
  * and disable
37
39
  * (`CatalogScopeService`), org admins' plugin uploads (`OrgPluginUploads`) and their own plugins (`OwnedItems`), and the producer of the plugin scope sync
38
40
  * (`PluginScopeSync`; its consumer is the worker role's `PluginScopeWorkersModule`). The catalog's periodic jobs (`CatalogJobs`) are
@@ -69,6 +71,7 @@ CatalogModule = __decorate([
69
71
  ComponentScopeService,
70
72
  OrgPluginUploads,
71
73
  OwnedItems,
74
+ PlatformImportUpdate,
72
75
  ],
73
76
  exports: [
74
77
  PLUGIN_SCOPE_QUEUE_MODULE,
@@ -84,6 +87,7 @@ CatalogModule = __decorate([
84
87
  ComponentScopeService,
85
88
  OrgPluginUploads,
86
89
  OwnedItems,
90
+ PlatformImportUpdate,
87
91
  ],
88
92
  })
89
93
  ], CatalogModule);
@@ -188,7 +188,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
188
188
  /** The host versions (an entry's `minKernel` is judged against them). */
189
189
  export declare const HOST_VERSIONS: {
190
190
  readonly kernel: string;
191
- readonly sdk: "0.1.0-rc.16";
191
+ readonly sdk: "0.1.0-rc.18";
192
192
  };
193
193
  /** Whether this deployment satisfies the entry's `minKernel`. */
194
194
  export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
@@ -0,0 +1,50 @@
1
+ import type { KernelLogger } from "../log.js";
2
+ import type { ActorOpts, CatalogItemView } from "./catalog.service.js";
3
+ import { PluginImport } from "./plugin-import.js";
4
+ import { PlatformPluginUpdater, type PlatformUpdatePreview, type PlatformUpdateResult } from "./plugin-upgrade.js";
5
+ /** `POST /api/v1/admin/catalog/imports/:stagingId/update`: the import and the update it applied. */
6
+ export interface PlatformImportUpdateResult {
7
+ item: CatalogItemView;
8
+ update: PlatformUpdateResult;
9
+ }
10
+ /**
11
+ * The platform's update of an installed plugin to a version staged for import (the one wizard of
12
+ * the admin plugin pages, maintainer decision 2026-11-13): the import review (prepared as any
13
+ * import, `PluginImport.prepare`) and the platform update preview of the staged version
14
+ * ({@link preview}: `PlatformPluginUpdater.previewStaged`, the target read from the staging) are
15
+ * confirmed together ({@link confirm} with the review's `reviewHash` and the preview's `diffHash`).
16
+ * Nothing is written before that confirm: cancelling the wizard leaves only the staging row, which
17
+ * expires like any staged import.
18
+ *
19
+ * The confirm holds the item's update lock (`PlatformPluginUpdater.withItemLock`, one per item
20
+ * across replicas: another update -> 409 `plugin.install_in_progress`), recomputes the preview
21
+ * (another `diffHash` -> 409 `plugin.upgrade_stale`, nothing written), commits the import without
22
+ * deploying its extension (the permission snapshot is written as the review approved it), then
23
+ * runs the platform update, which moves the extension with its own rollback and writes the
24
+ * components in one transaction. When that update fails, the platform stays on its version, the
25
+ * extension where it was and its previous approval is put back; the imported version stays in the
26
+ * catalog (its 版本 tab, and 更新到 retries the update with the plain update preview): 409
27
+ * `plugin.imported_not_updated` says so, with the cause in the details. No in-process state.
28
+ */
29
+ export declare class PlatformImportUpdate {
30
+ private readonly pluginImport;
31
+ private readonly updater;
32
+ private readonly log;
33
+ constructor(pluginImport: PluginImport, updater: PlatformPluginUpdater, log: KernelLogger);
34
+ /**
35
+ * The platform update the staged version would apply (writes nothing). The staging must be a
36
+ * platform staging (else 404 `plugin.staging_not_found`) of a new version of a live
37
+ * platform-owned plugin (else 409 `plugin.upgrade_target_invalid`, reason `not_installed`); the
38
+ * updater's refusals apply as for any update preview. `needsApproval` is false: the confirm
39
+ * approves the permissions the review shows.
40
+ */
41
+ preview(stagingId: string): Promise<PlatformUpdatePreview>;
42
+ /** Confirms the import and the update together (see the class comment). */
43
+ confirm(actor: string, stagingId: string, input: {
44
+ reviewHash: string;
45
+ diffHash: string;
46
+ }, opts?: ActorOpts): Promise<PlatformImportUpdateResult>;
47
+ /** The staged target, which must be a new version of a live platform-owned plugin. */
48
+ private target;
49
+ private previewOf;
50
+ }
@@ -0,0 +1,116 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Inject, Injectable } from "@nestjs/common";
11
+ import { ApiError } from "../errors.js";
12
+ import { KERNEL_LOGGER } from "../infra/tokens.js";
13
+ import { PluginImport } from "./plugin-import.js";
14
+ import { PlatformPluginUpdater, } from "./plugin-upgrade.js";
15
+ const notInstalled = (name) => new ApiError("plugin.upgrade_target_invalid", `Plugin ${name} is not a platform plugin of this deployment yet; install it with the import review instead`, [{ reason: "not_installed" }]);
16
+ /**
17
+ * The platform's update of an installed plugin to a version staged for import (the one wizard of
18
+ * the admin plugin pages, maintainer decision 2026-11-13): the import review (prepared as any
19
+ * import, `PluginImport.prepare`) and the platform update preview of the staged version
20
+ * ({@link preview}: `PlatformPluginUpdater.previewStaged`, the target read from the staging) are
21
+ * confirmed together ({@link confirm} with the review's `reviewHash` and the preview's `diffHash`).
22
+ * Nothing is written before that confirm: cancelling the wizard leaves only the staging row, which
23
+ * expires like any staged import.
24
+ *
25
+ * The confirm holds the item's update lock (`PlatformPluginUpdater.withItemLock`, one per item
26
+ * across replicas: another update -> 409 `plugin.install_in_progress`), recomputes the preview
27
+ * (another `diffHash` -> 409 `plugin.upgrade_stale`, nothing written), commits the import without
28
+ * deploying its extension (the permission snapshot is written as the review approved it), then
29
+ * runs the platform update, which moves the extension with its own rollback and writes the
30
+ * components in one transaction. When that update fails, the platform stays on its version, the
31
+ * extension where it was and its previous approval is put back; the imported version stays in the
32
+ * catalog (its 版本 tab, and 更新到 retries the update with the plain update preview): 409
33
+ * `plugin.imported_not_updated` says so, with the cause in the details. No in-process state.
34
+ */
35
+ let PlatformImportUpdate = class PlatformImportUpdate {
36
+ pluginImport;
37
+ updater;
38
+ log;
39
+ constructor(pluginImport, updater, log) {
40
+ this.pluginImport = pluginImport;
41
+ this.updater = updater;
42
+ this.log = log;
43
+ }
44
+ /**
45
+ * The platform update the staged version would apply (writes nothing). The staging must be a
46
+ * platform staging (else 404 `plugin.staging_not_found`) of a new version of a live
47
+ * platform-owned plugin (else 409 `plugin.upgrade_target_invalid`, reason `not_installed`); the
48
+ * updater's refusals apply as for any update preview. `needsApproval` is false: the confirm
49
+ * approves the permissions the review shows.
50
+ */
51
+ async preview(stagingId) {
52
+ const st = await this.target(stagingId);
53
+ return this.previewOf(st);
54
+ }
55
+ /** Confirms the import and the update together (see the class comment). */
56
+ async confirm(actor, stagingId, input, opts = {}) {
57
+ const st = await this.target(stagingId);
58
+ return this.updater.withItemLock(st.itemId, async () => {
59
+ const preview = await this.previewOf(st);
60
+ if (preview.diffHash !== input.diffHash) {
61
+ throw new ApiError("plugin.upgrade_stale", `What updating plugin ${preview.name} to ${st.version} does changed since the preview; preview it again`);
62
+ }
63
+ // The review hash is checked by the import's confirm (409 plugin.review_stale, nothing
64
+ // written)
65
+ const imported = await this.pluginImport.confirm(actor, stagingId, { reviewHash: input.reviewHash, deploy: false }, opts);
66
+ try {
67
+ const update = await this.updater.updateHeld(actor, st.itemId, { version: st.version, diffHash: input.diffHash }, opts);
68
+ return { item: await this.pluginImport.view(st.itemId), update };
69
+ }
70
+ catch (e) {
71
+ let restored = imported.approval === undefined;
72
+ if (imported.approval !== undefined) {
73
+ try {
74
+ restored = await this.pluginImport.restoreApproval(actor, imported.approval, opts);
75
+ }
76
+ catch (err) {
77
+ this.log.error("cannot restore an extension's approval after a failed update", {
78
+ itemId: st.itemId,
79
+ err,
80
+ });
81
+ }
82
+ }
83
+ const cause = e instanceof ApiError ? e : undefined;
84
+ throw new ApiError("plugin.imported_not_updated", `Version ${st.version} of plugin ${preview.name} was imported, but updating the platform to it failed (${cause?.code ?? "internal"}); the platform stays on ${preview.from ?? "(none)"}. Update to ${st.version} from the plugin's page to retry`, [
85
+ {
86
+ version: st.version,
87
+ from: preview.from,
88
+ code: cause?.code ?? "internal",
89
+ ...(cause !== undefined && { message: cause.message }),
90
+ ...(cause?.details !== undefined && { causeDetails: cause.details }),
91
+ approvalRestored: restored,
92
+ },
93
+ ]);
94
+ }
95
+ });
96
+ }
97
+ /** The staged target, which must be a new version of a live platform-owned plugin. */
98
+ async target(stagingId) {
99
+ const st = await this.pluginImport.stagedTarget(stagingId);
100
+ if (!st.existing) {
101
+ throw notInstalled((await this.pluginImport.staged(stagingId)).review.name);
102
+ }
103
+ return st;
104
+ }
105
+ async previewOf(st) {
106
+ const p = await this.updater.previewStaged(st.itemId, st.version, st.target);
107
+ return p.extension === null ? p : { ...p, extension: { ...p.extension, needsApproval: false } };
108
+ }
109
+ };
110
+ PlatformImportUpdate = __decorate([
111
+ Injectable(),
112
+ __param(0, Inject(PluginImport)),
113
+ __param(1, Inject(PlatformPluginUpdater)),
114
+ __param(2, Inject(KERNEL_LOGGER))
115
+ ], PlatformImportUpdate);
116
+ export { PlatformImportUpdate };
@@ -8,6 +8,7 @@ import { ExtensionBlobs } from "../extensions/blobs.js";
8
8
  import { ExtensionInstalls } from "../extensions/installs.js";
9
9
  import { PluginApproveLock, PluginInstallLock } from "../extensions/locks.js";
10
10
  import { ExtensionPackageError } from "../extensions/package.js";
11
+ import { type PermissionSet } from "../extensions/permission-set.js";
11
12
  import { type PackageWarning, type PermissionGroup, type PermissionItem } from "../extensions/permission-view.js";
12
13
  import { ExtensionRegistry } from "../extensions/registry.js";
13
14
  import type { SingleProviderBlock } from "../extensions/single-provider.js";
@@ -317,6 +318,18 @@ export interface PluginConfirmResult {
317
318
  * extension (applied at once, plan Task 6).
318
319
  */
319
320
  firstVersion: boolean;
321
+ /**
322
+ * A confirm with `deploy: false` that approved the extension's permissions (its snapshot
323
+ * written): what {@link PluginImport.restoreApproval} needs to put the previous approval back
324
+ * when the step that was to deploy it fails. Never sent over HTTP (the route always deploys).
325
+ */
326
+ approval?: HeldApproval;
327
+ }
328
+ /** The approval a confirm without its deploy wrote (see {@link PluginConfirmResult.approval}). */
329
+ export interface HeldApproval {
330
+ review: PluginReview;
331
+ extension: PluginReviewExtension;
332
+ previous: PreviousSnapshot | null;
320
333
  }
321
334
  /** What prepare checks besides the package itself (npm and marketplace imports). */
322
335
  export interface PluginPrepareExpect {
@@ -338,6 +351,14 @@ export interface ReviewProvenance {
338
351
  provenance: ProvenanceResult;
339
352
  warning?: "repository_mismatch";
340
353
  }
354
+ /** The snapshot overwritten at confirm (restored when deploying an upgrade fails). */
355
+ export interface PreviousSnapshot {
356
+ version: string;
357
+ permissions: PermissionSet;
358
+ approvedBy: string | null;
359
+ approvedAt: Date;
360
+ source: "auto" | "web" | "cli" | "bootstrap";
361
+ }
341
362
  /** What an org's staged upload targets (see {@link PluginImport.stagedTarget}). */
342
363
  export interface StagedTarget {
343
364
  itemId: string;
@@ -521,8 +542,10 @@ export declare class PluginImport {
521
542
  * whether the item exists (a new version of the org's own plugin) or the version is imported
522
543
  * already (F4), and the target as the platform updater reads it: the staged manifest and files,
523
544
  * or the imported version's for a retry. Another org's staging -> 404 `plugin.staging_not_found`.
545
+ * Without `owner`: a platform staging, and `existing` means a live platform-owned item (the
546
+ * platform's import and update, `PlatformImportUpdate`).
524
547
  */
525
- stagedTarget(stagingId: string, owner: ImportOwner): Promise<StagedTarget>;
548
+ stagedTarget(stagingId: string, owner?: ImportOwner): Promise<StagedTarget>;
526
549
  /** The catalog item's view (as a confirm returns it). */
527
550
  view(itemId: string): Promise<CatalogItemView>;
528
551
  /**
@@ -553,6 +576,13 @@ export declare class PluginImport {
553
576
  orgs?: string[];
554
577
  /** An org upload's new item: shared with other orgs by its owner (default false, P3-16). */
555
578
  shareable?: boolean;
579
+ /**
580
+ * Deploy the extension after the commit (default true). false (the platform's import and
581
+ * update, `PlatformImportUpdate`) leaves the deployment to the platform update that follows,
582
+ * which moves the extension with its own rollback; the approval written is returned in
583
+ * `approval`.
584
+ */
585
+ deploy?: boolean;
556
586
  }, opts?: ActorOpts & {
557
587
  owner?: ImportOwner;
558
588
  }): Promise<PluginConfirmResult>;
@@ -603,6 +633,11 @@ export declare class PluginImport {
603
633
  * admin (the package is already in the package store) with the sha256 from the result.
604
634
  */
605
635
  private deploy;
636
+ /**
637
+ * Puts back the approval a confirm with `deploy: false` wrote, after the step that was to deploy
638
+ * the extension failed (see {@link restoreSnapshot}; false when someone changed it meanwhile).
639
+ */
640
+ restoreApproval(actor: string | null, held: HeldApproval, opts?: ActorOpts): Promise<boolean>;
606
641
  /**
607
642
  * Restores the snapshot overwritten at confirm (deletes it when there was none before), audits
608
643
  * `extension.permissions_restored`. Compare-and-write (row locked): restores only while the