@fabrika/provider-zerops 0.8.2 → 0.9.0-cloudflare.2

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 (128) hide show
  1. package/package.json +16 -14
  2. package/src/api/client.ts +6 -0
  3. package/src/api/types.ts +12 -0
  4. package/src/cli.ts +11 -0
  5. package/src/collaborators.ts +0 -14
  6. package/src/control.ts +33 -57
  7. package/src/execution.ts +113 -102
  8. package/src/installation/admin.ts +1 -1
  9. package/src/installation/deploy-options.ts +1 -2
  10. package/src/installation/deploy.ts +41 -75
  11. package/src/installation/generated/platform-console-schema.ts +3 -0
  12. package/src/installation/generated/platform-proxy-manifest.ts +21 -1
  13. package/src/installation/guide.md +9 -6
  14. package/src/installation/hosts.ts +1 -7
  15. package/src/installation/index.ts +0 -13
  16. package/src/installation/init-options.ts +2 -1
  17. package/src/installation/install-environment.ts +42 -106
  18. package/src/installation/install-options.ts +3 -1
  19. package/src/installation/install-secrets.ts +24 -68
  20. package/src/installation/install.ts +8 -4
  21. package/src/installation/sidecar.ts +5 -26
  22. package/src/installation/topology/setups.ts +5 -0
  23. package/src/installation/upgrade-options.ts +2 -2
  24. package/src/managed-env.ts +1 -174
  25. package/src/manifest.ts +11 -14
  26. package/src/namespace/claims.ts +56 -1
  27. package/src/namespace/errors.ts +1 -1
  28. package/src/namespace/manifest.ts +6 -19
  29. package/src/namespace/provisioning.ts +15 -3
  30. package/src/namespace/proxy-attempt.ts +43 -2
  31. package/src/namespace/proxy-reads.ts +12 -22
  32. package/src/namespace-cli.ts +152 -0
  33. package/src/plan.ts +2 -7
  34. package/src/provider.ts +128 -126
  35. package/src/proxy-manifest-carrier.ts +18 -0
  36. package/src/types.ts +0 -2
  37. package/types/src/api/client.d.ts +5 -0
  38. package/types/src/api/errors.d.ts +24 -0
  39. package/types/src/api/polling.d.ts +41 -0
  40. package/types/src/api/readers.d.ts +1115 -0
  41. package/types/src/api/service-env-limit.d.ts +17 -0
  42. package/types/src/api/service-env-response.d.ts +23 -0
  43. package/types/src/api/types.d.ts +687 -0
  44. package/types/src/api.d.ts +6 -0
  45. package/types/src/authoring.d.ts +9 -0
  46. package/types/src/cli-args.d.ts +19 -0
  47. package/types/src/cli.d.ts +13 -0
  48. package/types/src/collaborators.d.ts +16 -0
  49. package/types/src/compile.d.ts +91 -0
  50. package/types/src/control.d.ts +40 -0
  51. package/types/src/database-budget.d.ts +38 -0
  52. package/types/src/descriptor.d.ts +28 -0
  53. package/types/src/execution.d.ts +125 -0
  54. package/types/src/exposure.d.ts +47 -0
  55. package/types/src/index.d.ts +31 -0
  56. package/types/src/installation/admin-options.d.ts +24 -0
  57. package/types/src/installation/admin.d.ts +35 -0
  58. package/types/src/installation/checker-release.d.ts +12 -0
  59. package/types/src/installation/deploy-options.d.ts +37 -0
  60. package/types/src/installation/deploy.d.ts +136 -0
  61. package/types/src/installation/generated/platform-console-schema.d.ts +2 -0
  62. package/types/src/installation/generated/platform-proxy-manifest.d.ts +2 -0
  63. package/types/src/installation/hosts.d.ts +34 -0
  64. package/types/src/installation/index.d.ts +7 -0
  65. package/types/src/installation/init-options.d.ts +38 -0
  66. package/types/src/installation/init.d.ts +60 -0
  67. package/types/src/installation/install-collaborators.d.ts +24 -0
  68. package/types/src/installation/install-environment.d.ts +63 -0
  69. package/types/src/installation/install-options.d.ts +49 -0
  70. package/types/src/installation/install-project.d.ts +13 -0
  71. package/types/src/installation/install-provisioning.d.ts +58 -0
  72. package/types/src/installation/install-secrets.d.ts +17 -0
  73. package/types/src/installation/install-token.d.ts +10 -0
  74. package/types/src/installation/install.d.ts +23 -0
  75. package/types/src/installation/installation-source.d.ts +8 -0
  76. package/types/src/installation/log.d.ts +44 -0
  77. package/types/src/installation/machine-key.d.ts +19 -0
  78. package/types/src/installation/placement-options.d.ts +11 -0
  79. package/types/src/installation/platform-archive.d.ts +7 -0
  80. package/types/src/installation/platform-source.d.ts +8 -0
  81. package/types/src/installation/platform-upload.d.ts +3 -0
  82. package/types/src/installation/sidecar.d.ts +49 -0
  83. package/types/src/installation/source-options.d.ts +1 -0
  84. package/types/src/installation/topology/artifacts.d.ts +8 -0
  85. package/types/src/installation/topology/index.d.ts +5 -0
  86. package/types/src/installation/topology/installation-schemas.d.ts +2242 -0
  87. package/types/src/installation/topology/invariants.d.ts +23 -0
  88. package/types/src/installation/topology/setups.d.ts +59 -0
  89. package/types/src/installation/topology/topology.d.ts +153 -0
  90. package/types/src/installation/topology/validate.d.ts +11 -0
  91. package/types/src/installation/upgrade-options.d.ts +26 -0
  92. package/types/src/installation/upgrade-output.d.ts +6 -0
  93. package/types/src/installation/upgrade.d.ts +88 -0
  94. package/types/src/managed-env.d.ts +24 -0
  95. package/types/src/manifest.d.ts +204 -0
  96. package/types/src/namespace/claims.d.ts +16 -0
  97. package/types/src/namespace/codec.d.ts +334 -0
  98. package/types/src/namespace/constants.d.ts +25 -0
  99. package/types/src/namespace/errors.d.ts +15 -0
  100. package/types/src/namespace/manifest.d.ts +4 -0
  101. package/types/src/namespace/operator.d.ts +12 -0
  102. package/types/src/namespace/provisioning.d.ts +4 -0
  103. package/types/src/namespace/proxy-attempt.d.ts +39 -0
  104. package/types/src/namespace/proxy-reads.d.ts +22 -0
  105. package/types/src/namespace/proxy-secrets.d.ts +8 -0
  106. package/types/src/namespace/proxy-source.d.ts +74 -0
  107. package/types/src/namespace/topology.d.ts +9 -0
  108. package/types/src/namespace/types.d.ts +101 -0
  109. package/types/src/namespace/update.d.ts +6 -0
  110. package/types/src/namespace/validation.d.ts +12 -0
  111. package/types/src/namespace-cli.d.ts +2 -0
  112. package/types/src/namespace.d.ts +9 -0
  113. package/types/src/plan.d.ts +11 -0
  114. package/types/src/provider.d.ts +63 -0
  115. package/types/src/proxy-manifest-carrier.d.ts +24 -0
  116. package/types/src/schema.generated.d.ts +376 -0
  117. package/types/src/service-names.d.ts +4 -0
  118. package/types/src/service-references.d.ts +13 -0
  119. package/types/src/source/constants.d.ts +6 -0
  120. package/types/src/source/port.d.ts +60 -0
  121. package/types/src/source/types.d.ts +4 -0
  122. package/types/src/source/validation.d.ts +19 -0
  123. package/types/src/source.d.ts +2 -0
  124. package/types/src/types.d.ts +119 -0
  125. package/types/src/version-attempt.d.ts +8 -0
  126. package/src/installation/mcp.ts +0 -102
  127. package/src/installation/proxy-manifest.ts +0 -214
  128. package/src/installation/secrets.ts +0 -78
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabrika/provider-zerops",
3
- "version": "0.8.2",
3
+ "version": "0.9.0-cloudflare.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,27 +12,28 @@
12
12
  },
13
13
  "files": [
14
14
  "src",
15
- "!src/**/__tests__/**"
15
+ "!src/**/__tests__/**",
16
+ "types"
16
17
  ],
17
18
  "exports": {
18
19
  ".": {
19
20
  "bun": "./src/index.ts",
20
- "types": "./src/index.ts",
21
+ "types": "./types/src/index.d.ts",
21
22
  "default": "./src/index.ts"
22
23
  },
23
24
  "./cli": {
24
25
  "bun": "./src/cli.ts",
25
- "types": "./src/cli.ts",
26
+ "types": "./types/src/cli.d.ts",
26
27
  "default": "./src/cli.ts"
27
28
  },
28
29
  "./installation": {
29
30
  "bun": "./src/installation/index.ts",
30
- "types": "./src/installation/index.ts",
31
+ "types": "./types/src/installation/index.d.ts",
31
32
  "default": "./src/installation/index.ts"
32
33
  },
33
34
  "./installation/topology": {
34
35
  "bun": "./src/installation/topology/index.ts",
35
- "types": "./src/installation/topology/index.ts",
36
+ "types": "./types/src/installation/topology/index.d.ts",
36
37
  "default": "./src/installation/topology/index.ts"
37
38
  }
38
39
  },
@@ -43,14 +44,15 @@
43
44
  },
44
45
  "dependencies": {
45
46
  "zod": "^3.25.76",
46
- "@fabrika/auth": "0.8.2",
47
- "@fabrika/auth-core": "0.8.2",
48
- "@fabrika/email": "0.8.2",
49
- "@fabrika/iam-contract": "0.8.2",
50
- "@fabrika/installation-init": "0.8.2",
51
- "@fabrika/operations-contract": "0.8.2",
52
- "@fabrika/provider-contract": "0.8.2",
53
- "@fabrika/proxy-core": "0.8.2",
47
+ "@fabrika/app": "0.9.0-cloudflare.2",
48
+ "@fabrika/auth": "0.9.0-cloudflare.2",
49
+ "@fabrika/auth-core": "0.9.0-cloudflare.2",
50
+ "@fabrika/email": "0.9.0-cloudflare.2",
51
+ "@fabrika/iam-contract": "0.9.0-cloudflare.2",
52
+ "@fabrika/installation-init": "0.9.0-cloudflare.2",
53
+ "@fabrika/operations-contract": "0.9.0-cloudflare.2",
54
+ "@fabrika/provider-contract": "0.9.0-cloudflare.2",
55
+ "@fabrika/proxy-core": "0.9.0-cloudflare.2",
54
56
  "yaml": "^2.9.1"
55
57
  }
56
58
  }
package/src/api/client.ts CHANGED
@@ -347,6 +347,12 @@ export const createZeropsApi = (options: ZeropsApiOptions): ZeropsApi => {
347
347
  await request('enable subdomain access', 'PUT', `/service-stack/${serviceId}/enable-subdomain-access`, { signal })
348
348
  },
349
349
 
350
+ startService: async ({ serviceId, signal }) =>
351
+ readResponse('start service', processSchema, await request('start service', 'PUT', `/service-stack/${serviceId}/start`, { signal })),
352
+
353
+ stopService: async ({ serviceId, signal }) =>
354
+ readResponse('stop service', processSchema, await request('stop service', 'PUT', `/service-stack/${serviceId}/stop`, { signal })),
355
+
350
356
  configureAutoscaling: async ({ serviceId, autoscalingProfileId, signal }) => {
351
357
  const payload = await request('configure autoscaling', 'PUT', `/service-stack/${serviceId}/autoscaling`, {
352
358
  body: { autoscalingProfileId },
package/src/api/types.ts CHANGED
@@ -500,6 +500,18 @@ export interface ZeropsApi {
500
500
  */
501
501
  enableSubdomainAccess(input: { serviceId: string; signal: AbortSignal }): Promise<void>
502
502
 
503
+ // ── service-stack: running state ──────────────────────────────────────────────
504
+
505
+ /**
506
+ * Start a stopped service. VERIFIED AS A SHAPE: `PUT /service-stack/{id}/start` (`StartServiceStack`), no body,
507
+ * `ResponseProcess`. Measured 2026-10-21: a stopped Bun service served again about 9 s after the call. Files written
508
+ * at runtime survive a stop and a start; a deploy replaces the container and loses them.
509
+ */
510
+ startService(input: { serviceId: string; signal: AbortSignal }): Promise<ZeropsProcess>
511
+
512
+ /** Stop a service. VERIFIED AS A SHAPE: `PUT /service-stack/{id}/stop` (`StopServiceStack`), no body, `ResponseProcess`. */
513
+ stopService(input: { serviceId: string; signal: AbortSignal }): Promise<ZeropsProcess>
514
+
503
515
  // ── service-stack: autoscaling ────────────────────────────────────────────────
504
516
 
505
517
  /**
package/src/cli.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bun
2
+ import type { AppCli } from '@fabrika/provider-contract/app-cli'
2
3
  import { constants } from 'node:fs'
3
4
  import { open } from 'node:fs/promises'
4
5
  import { resolve } from 'node:path'
@@ -12,6 +13,7 @@ import {
12
13
  manifestServiceHostnames,
13
14
  ZEROPS_SOURCE_DESCRIPTOR_MAX_BYTES,
14
15
  } from './manifest'
16
+ import { zeropsNamespaceCli } from './namespace-cli'
15
17
  import type { ZeropsAppConfig } from './types'
16
18
 
17
19
  const USAGE = `Zerops app tools
@@ -106,6 +108,15 @@ export const runZeropsCli = async (argv: readonly string[]): Promise<void> => {
106
108
  console.info(`wrote ${output} (${manifestServiceHostnames(manifest).length} service(s))`)
107
109
  }
108
110
 
111
+ /** What `fabrika app build`, `app register` and `fabrika namespace` run for an application on Zerops. */
112
+ export const appCli: AppCli = {
113
+ provider: 'zerops',
114
+ entryRuntime: 'bun',
115
+ build: (argv) => runZeropsCli(['build', ...argv]),
116
+ registrationManifest: buildZeropsManifestText,
117
+ namespaces: zeropsNamespaceCli,
118
+ }
119
+
109
120
  if (import.meta.main) {
110
121
  await runZeropsCli(process.argv.slice(2)).catch((error: unknown) => {
111
122
  console.error(error instanceof Error ? error.message : String(error))
@@ -1,5 +1,3 @@
1
- import { reconcileSchema } from '@fabrika/auth'
2
- import type { SchemaReconciler } from '@fabrika/provider-contract'
3
1
  import { createZeropsApi, type ZeropsApi } from './api'
4
2
  import { type ExposureAlerter, sendExposureAlert } from './exposure'
5
3
  import type { ZeropsSourcePort } from './source/port'
@@ -28,7 +26,6 @@ export interface ZeropsCollaborators {
28
26
  api: ZeropsApi
29
27
  /** Injected by the Zerops control composition; dry runs do not require it. */
30
28
  source?: ZeropsSourcePort
31
- reconcileSchema: SchemaReconciler
32
29
  /** Raises the Operations alert for a refused activation; best-effort. */
33
30
  alertExposure: ExposureAlerter
34
31
  sleep: Sleeper
@@ -36,19 +33,8 @@ export interface ZeropsCollaborators {
36
33
 
37
34
  export type ZeropsCollaboratorFactory = (target: ZeropsRuntimeTarget) => ZeropsCollaborators
38
35
 
39
- const defaultReconcileSchema: SchemaReconciler = (input) =>
40
- reconcileSchema({
41
- url: input.url,
42
- app: input.app,
43
- schema: input.schema,
44
- ...(input.returnOrigins === undefined ? {} : { returnOrigins: input.returnOrigins }),
45
- adminKey: input.adminKey,
46
- signal: input.signal,
47
- })
48
-
49
36
  export const defaultZeropsCollaborators: ZeropsCollaboratorFactory = (target) => ({
50
37
  api: createZeropsApi({ token: target.accessToken, baseUrl: target.apiBaseUrl }),
51
- reconcileSchema: defaultReconcileSchema,
52
38
  alertExposure: sendExposureAlert,
53
39
  sleep: defaultSleep,
54
40
  })
package/src/control.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { appEnvironmentId } from '@fabrika/auth-core'
2
1
  import type {
3
2
  ControlProvider,
4
3
  JsonValue,
@@ -8,6 +7,7 @@ import type {
8
7
  ProviderDeployInput,
9
8
  ProviderDeploymentNamespace,
10
9
  ProviderDeployOutcome,
10
+ ProviderDevEnvironmentInput,
11
11
  ProviderEnvelope,
12
12
  ProviderEnvironment,
13
13
  ProviderReconcileOutcome,
@@ -16,7 +16,6 @@ import type {
16
16
  ProviderSourceBinding,
17
17
  ProviderSourceResolution,
18
18
  ProviderSourceResolutionInput,
19
- SchemaReconciler,
20
19
  } from '@fabrika/provider-contract'
21
20
  import { decodeEnvelope, ProviderSecretError } from '@fabrika/provider-contract'
22
21
  import { z } from 'zod'
@@ -31,9 +30,8 @@ import {
31
30
  } from './api'
32
31
  import { defaultSleep, defaultZeropsCollaborators, type Sleeper } from './collaborators'
33
32
  import { createdServices, type ExposureAlerter, isObjectStorageType } from './exposure'
34
- import { assertAuthoredManagedInputs, assertAuthoredManagedName, assertNoFrameworkReference, parseManagedEnvironment } from './managed-env'
33
+ import { assertAuthoredManagedInputs, assertAuthoredManagedName, assertNoFrameworkReference } from './managed-env'
35
34
  import {
36
- approvedSchemaOf,
37
35
  assertFabrikaManifestCoordinates,
38
36
  type FabrikaManifest,
39
37
  manifestServiceHostnames,
@@ -90,11 +88,8 @@ const envelope = <T>(codec: ProviderCodec<T>, value: T): ProviderEnvelope => ({
90
88
  export interface ZeropsControlProviderOptions {
91
89
  readonly accessToken: string
92
90
  readonly apiBaseUrl?: string
93
- readonly propustkaUrl?: string
94
- readonly adminKey?: string
95
91
  readonly api?: ZeropsApi
96
92
  readonly source: ZeropsSourcePort
97
- readonly reconcileSchema?: SchemaReconciler
98
93
  readonly alertExposure?: ExposureAlerter
99
94
  readonly sleep?: Sleeper
100
95
  readonly namespaces?: {
@@ -228,7 +223,6 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
228
223
  return {
229
224
  api,
230
225
  source: options.source,
231
- reconcileSchema: options.reconcileSchema ?? defaults.reconcileSchema,
232
226
  alertExposure: options.alertExposure ?? defaults.alertExposure,
233
227
  sleep: options.sleep ?? defaultSleep,
234
228
  }
@@ -262,34 +256,6 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
262
256
  }
263
257
  }
264
258
 
265
- const reconcileActiveSchema = async (
266
- input: { environment: ProviderEnvironment; returnOrigins?: readonly string[] },
267
- signal: AbortSignal,
268
- ): Promise<void> => {
269
- const artifact = artifactFor(input.environment)
270
- const schema = approvedSchemaOf(input.environment)
271
- if (schema === undefined || options.propustkaUrl === undefined) return
272
- const placement = resolvedEnvironment(input.environment, { requireReady: false })
273
- const runtimeTarget: ZeropsRuntimeTarget = {
274
- projectId: placement.projectId,
275
- serviceId: placement.target.serviceId,
276
- accessToken: options.accessToken,
277
- ...(options.apiBaseUrl !== undefined ? { apiBaseUrl: options.apiBaseUrl } : {}),
278
- propustkaUrl: options.propustkaUrl,
279
- ...(options.adminKey !== undefined ? { adminKey: options.adminKey } : {}),
280
- }
281
- const reconcileSchema = options.reconcileSchema ?? defaultZeropsCollaborators(runtimeTarget).reconcileSchema
282
- signal.throwIfAborted()
283
- await reconcileSchema({
284
- url: options.propustkaUrl,
285
- app: appEnvironmentId(artifact.app.id, artifact.app.env),
286
- schema,
287
- ...(input.returnOrigins === undefined || input.returnOrigins.length === 0 ? {} : { returnOrigins: input.returnOrigins }),
288
- adminKey: options.adminKey,
289
- signal,
290
- })
291
- }
292
-
293
259
  const assertVersionCoordinates = (environment: ProviderEnvironment, appVersionId: string, version: ZeropsAppVersion): void => {
294
260
  const serviceId = decodeEnvironment(environment).target.serviceId
295
261
  if (version.id !== appVersionId || (version.serviceStackId !== undefined && version.serviceStackId !== serviceId)) {
@@ -324,9 +290,31 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
324
290
  return { commitSha: result.commitSha }
325
291
  }
326
292
 
293
+ /** Stop or start the service of a `dev` environment outside a run, and wait for the platform's process. */
294
+ const runningState = async (input: ProviderDevEnvironmentInput, action: 'start' | 'stop'): Promise<void> => {
295
+ if (input.environment.kind !== 'dev') throw new Error('zerops: only a dev environment is stopped and started outside a run')
296
+ const placement = resolvedEnvironment(input.environment, { requireReady: false })
297
+ const serviceId = placement.target.serviceId
298
+ // Idempotent: Control's record of a stopped service can lag behind a start from elsewhere.
299
+ const status = (await api.getService({ serviceId, signal: input.signal })).status
300
+ if (status === (action === 'stop' ? 'STOPPED' : 'ACTIVE')) return
301
+ const process = action === 'stop'
302
+ ? await api.stopService({ serviceId, signal: input.signal })
303
+ : await api.startService({ serviceId, signal: input.signal })
304
+ await waitForProcess({
305
+ api,
306
+ processId: process.id,
307
+ sleep: options.sleep ?? defaultSleep,
308
+ signal: input.signal,
309
+ label: `the ${action} of ${input.environment.appId}/${input.environment.env}`,
310
+ })
311
+ }
312
+
327
313
  const controlProvider: ControlProvider = {
328
314
  id: 'zerops',
329
315
  normalizeRegistration,
316
+ // The deploy reads `zerops.yaml` from the repository, so a workspace may not change it (ADR-0049 D6).
317
+ authoring: { protectedFiles: ['zerops.yaml', 'zerops.yml'] },
330
318
  source: { binding: 'connection' },
331
319
  /**
332
320
  * A Zerops artifact IS the compiled manifest, so the declaration is right here and core can refuse
@@ -349,8 +337,6 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
349
337
  deploy: async (input: ProviderDeployInput): Promise<ProviderDeployOutcome> => {
350
338
  input.signal.throwIfAborted()
351
339
  assertAuthoredManagedInputs({ variableNames: Object.keys(input.vars), secretNames: Object.keys(input.secrets) })
352
- const handed = parseManagedEnvironment(input.managedEnvironment)
353
- const managedEnvironment = { ...handed.values, ...handed.familyVariables }
354
340
  const binding = privateBinding(input.sourceBinding)
355
341
  assertEnvironmentOfApp(input.app, input.environment)
356
342
  const placement = resolvedEnvironment(input.environment, { requireReady: true })
@@ -376,16 +362,14 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
376
362
  ...(placement.target.createdBuckets === undefined ? {} : { createdBuckets: placement.target.createdBuckets }),
377
363
  ...(source === undefined ? {} : { source }),
378
364
  ...(options.apiBaseUrl !== undefined ? { apiBaseUrl: options.apiBaseUrl } : {}),
379
- ...(options.propustkaUrl !== undefined ? { propustkaUrl: options.propustkaUrl } : {}),
380
- ...(options.adminKey !== undefined ? { adminKey: options.adminKey } : {}),
381
365
  }
382
366
  const result = await deployRun({
383
367
  appId: input.app.id,
384
368
  env: input.environment.env,
385
- ...(input.returnOrigins === undefined ? {} : { returnOrigins: input.returnOrigins }),
369
+ ...(input.environment.kind === 'dev' && source !== undefined ? { dev: { head: source.commitSha } } : {}),
386
370
  secrets: input.secrets,
387
371
  vars: input.vars,
388
- managedEnvironment,
372
+ managedEnvironment: input.managedEnvironment,
389
373
  target: runtimeTarget,
390
374
  artifact,
391
375
  ...(input.environment.access === undefined ? {} : { access: input.environment.access }),
@@ -397,6 +381,10 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
397
381
  input.signal.throwIfAborted()
398
382
  return { state: result.status, ...(result.artifactState === undefined ? {} : { artifactState: result.artifactState }) }
399
383
  },
384
+ devEnvironments: {
385
+ stop: (input) => runningState(input, 'stop'),
386
+ start: (input) => runningState(input, 'start'),
387
+ },
400
388
  cancel: async (input): Promise<ProviderCancelOutcome> => {
401
389
  const signal = input.signal
402
390
  signal.throwIfAborted()
@@ -414,17 +402,8 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
414
402
  const version = await getVersion({ appVersionId: checkpoint.appVersionId, signal })
415
403
  if (version === null) return { state: 'settled', outcome: 'failed' }
416
404
  assertVersionCoordinates(input.environment, checkpoint.appVersionId, version)
417
- // Cancellation never implies rollback: a version that activated first makes the run a success,
418
- // which finishes the same IAM touchpoint a deploy does.
419
- if (version.status === ZEROPS_ACTIVE) {
420
- try {
421
- await reconcileActiveSchema(input, signal)
422
- } catch {
423
- signal.throwIfAborted()
424
- return { state: 'pending' }
425
- }
426
- return { state: 'settled', outcome: 'succeeded' }
427
- }
405
+ // Cancellation never implies rollback: a version that activated first makes the run a success.
406
+ if (version.status === ZEROPS_ACTIVE) return { state: 'settled', outcome: 'succeeded' }
428
407
  if (version.status !== undefined && ZEROPS_TERMINAL.has(version.status)) return { state: 'settled', outcome: 'failed' }
429
408
  await cancelBuildQuietly(checkpoint.appVersionId, signal)
430
409
  // A build request that never landed leaves the version UPLOADING for good; the deadline settles it.
@@ -461,10 +440,7 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
461
440
  }
462
441
  if (version === null) return { state: 'failed' }
463
442
  assertVersionCoordinates(input.environment, checkpoint.appVersionId, version)
464
- if (version.status === ZEROPS_ACTIVE) {
465
- await reconcileActiveSchema(input, signal)
466
- return { state: 'succeeded' }
467
- }
443
+ if (version.status === ZEROPS_ACTIVE) return { state: 'succeeded' }
468
444
  if (version.status !== undefined && ZEROPS_TERMINAL.has(version.status)) return { state: 'failed' }
469
445
  if (pastDeadline(input.startedAtMs)) {
470
446
  await cancelBuildQuietly(checkpoint.appVersionId, signal)