@moinulmoin/eden 0.4.1 → 0.5.0

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.
package/README.md CHANGED
@@ -12,15 +12,15 @@ If you already have an Eve project, start with `eden deploy`.
12
12
  Use any one of these package managers:
13
13
 
14
14
  ```sh
15
- npm install --global @moinulmoin/eden@0.4.1
15
+ npm install --global @moinulmoin/eden@0.5.0
16
16
  ```
17
17
 
18
18
  ```sh
19
- pnpm add --global @moinulmoin/eden@0.4.1
19
+ pnpm add --global @moinulmoin/eden@0.5.0
20
20
  ```
21
21
 
22
22
  ```sh
23
- bun add --global @moinulmoin/eden@0.4.1
23
+ bun add --global @moinulmoin/eden@0.5.0
24
24
  ```
25
25
 
26
26
  Then confirm the CLI is available:
@@ -21,6 +21,12 @@ export interface EvePreflightCandidate {
21
21
  export type EveDeploymentStatus = "deployed" | "failed" | "indeterminate";
22
22
  export interface EveDeploymentMetadata {
23
23
  readonly status: EveDeploymentStatus;
24
+ /**
25
+ * `create` is a first deploy onto an absent target; `update` is an
26
+ * in-place redeploy of a provably Eden-owned target that keeps the Worker,
27
+ * Durable Object classes, and durable state.
28
+ */
29
+ readonly operation: "create" | "update";
24
30
  readonly targetKey: string;
25
31
  readonly deploymentId: string;
26
32
  readonly workerName: string;
@@ -28,6 +34,9 @@ export interface EveDeploymentMetadata {
28
34
  readonly stableContainerInstanceName: string;
29
35
  readonly stableWorkersDevOrigin: string;
30
36
  readonly imageReference: string;
37
+ /** Present on updates: the superseded generation and its registry image. */
38
+ readonly supersededGenerationId?: string;
39
+ readonly supersededImageReference?: string;
31
40
  readonly runtimeVariableNames: readonly string[];
32
41
  readonly evidenceRetained: true;
33
42
  }
@@ -50,6 +59,7 @@ export type EveCloudflareTargetState = "absent" | "owned" | "unowned" | "mismatc
50
59
  export interface EveCloudflareReadRequest {
51
60
  readonly projectId: string;
52
61
  readonly sourceDigest: string;
62
+ readonly projectRoot: string;
53
63
  readonly environment: EveCliEnvironment;
54
64
  readonly name: string;
55
65
  }
@@ -65,6 +75,29 @@ export interface EveCloudflareReadResult {
65
75
  readonly message?: string;
66
76
  readonly remediation?: string;
67
77
  readonly identity?: EveDeploymentIdentityProof;
78
+ /**
79
+ * Which exact resources the read observed. `undefined` means the
80
+ * inventory could not be proven; `worker`/`container` booleans mean the
81
+ * exact names were present or absent without any ownership claim.
82
+ */
83
+ readonly observed?: {
84
+ readonly worker: boolean;
85
+ readonly container: boolean;
86
+ };
87
+ /**
88
+ * Provider-backed facts about the live target, read through the
89
+ * Cloudflare API (not self-asserted response headers, which any Worker
90
+ * can forge): the Container application's configured image and the
91
+ * `EDEN_EVE_DEPLOYMENT_ID` plain-text var Eden stamps into every Worker
92
+ * it publishes. `settingsReadable` tells the ownership resolver whether
93
+ * a missing var is evidence of a foreign Worker or just an unreadable
94
+ * settings call.
95
+ */
96
+ readonly providerEvidence?: {
97
+ readonly containerImage?: string;
98
+ readonly workerDeploymentId?: string;
99
+ readonly settingsReadable?: boolean;
100
+ };
68
101
  };
69
102
  }
70
103
  export type EveCloudflareReadRunner = (request: EveCloudflareReadRequest) => EveCloudflareReadResult | Promise<EveCloudflareReadResult>;
@@ -104,7 +137,10 @@ export interface EveDeploymentIdentity {
104
137
  readonly runtimeVariableNames: readonly string[];
105
138
  readonly runtimeRevisionHandle?: string;
106
139
  }
107
- export type EveDeploymentIdentityProof = Partial<EveDeploymentIdentity>;
140
+ export type EveDeploymentIdentityProof = Partial<EveDeploymentIdentity> & {
141
+ /** The deployment the serving Container verifiably started under; set only by a deployed Eden host that proves its running image before stamping. */
142
+ readonly startedDeploymentId?: string;
143
+ };
108
144
  export interface EveDeploymentPublicationRequest {
109
145
  readonly identity: EveDeploymentIdentity;
110
146
  readonly hostConfig: EveHostConfig;
@@ -252,6 +288,7 @@ export interface EveDestroyCloudflareReadRequest {
252
288
  export declare function exactTargetContainerEntries(value: unknown, name: string): readonly {
253
289
  readonly id: string;
254
290
  readonly name: string;
291
+ readonly image?: string;
255
292
  }[] | undefined;
256
293
  export declare function runEveControlPlane(request: EveCliExecutionRequest, options?: EvePreflightOptions): Promise<void>;
257
294
  export interface EveDestroyOutcome {
@@ -290,7 +290,7 @@ function humanEveResultLines(result, startedAt, eveVersion) {
290
290
  if (result.ok) {
291
291
  if (result.command === "eve deploy") {
292
292
  const url = result.deployment?.stableWorkersDevOrigin;
293
- lines.push("", `✓ deployed ${result.name} (${result.environment})` +
293
+ lines.push("", `✓ ${result.deployment?.operation === "update" ? "updated" : "deployed"} ${result.name} (${result.environment})` +
294
294
  (eveVersion === undefined ? "" : ` · eve ${eveVersion}`));
295
295
  if (url !== undefined)
296
296
  lines.push(url);
@@ -306,7 +306,7 @@ function humanEveResultLines(result, startedAt, eveVersion) {
306
306
  lines.push("", `✗ ${commandLabel} ${result.deployment.status}`, ` → ${result.deployment.status === "indeterminate"
307
307
  ? "The outcome is indeterminate; inspect the retained ownership evidence before retrying the same exact target."
308
308
  : result.deployment.status === "deployed"
309
- ? "The deployment is live; only local runtime-image cleanup failed, so do not retry the deployment."
309
+ ? "The deployment is live; only image cleanup failed (local runtime image or the superseded registry tag), so do not retry the deployment."
310
310
  : "The deployment failed before promotion; the exact target was not claimed."}`);
311
311
  }
312
312
  else if (firstFailure !== undefined) {
@@ -442,8 +442,21 @@ function exactContainerEntries(value, name) {
442
442
  }
443
443
  const id = entry.id;
444
444
  const entryName = entry.name;
445
- if (entryName === name)
446
- entries.push({ id, name: entryName });
445
+ if (entryName === name) {
446
+ const configuration = entry.configuration;
447
+ const image = typeof entry.image === "string"
448
+ ? entry.image
449
+ : typeof configuration === "object" &&
450
+ configuration !== null &&
451
+ typeof configuration.image === "string"
452
+ ? configuration.image
453
+ : undefined;
454
+ entries.push({
455
+ id,
456
+ name: entryName,
457
+ ...(image === undefined ? {} : { image }),
458
+ });
459
+ }
447
460
  }
448
461
  if (entries.length === 0 && collection.length >= 100) {
449
462
  return undefined;
@@ -595,6 +608,9 @@ async function defaultCloudflareRead(request) {
595
608
  target: { state: "absent" },
596
609
  };
597
610
  }
611
+ const workerPresent = deploymentEntries.length > 0;
612
+ const containerPresent = (containerEntries?.length ?? 0) > 0;
613
+ const providerEvidence = await readProviderEvidence(request.name, accountId, workerPresent, containerPresent ? containerEntries?.[0]?.image : undefined);
598
614
  return {
599
615
  accountAccess,
600
616
  containerAccess,
@@ -605,16 +621,141 @@ async function defaultCloudflareRead(request) {
605
621
  ? {}
606
622
  : { workersDevSubdomain }),
607
623
  target: {
608
- state: "ambiguous",
609
- message: "The exact target could not be proven absent or owned from the read-only Cloudflare inventory.",
610
- remediation: "Resolve the exact Worker and Container ownership state before deployment; no target was claimed or changed.",
624
+ // The read-only inventory proves the exact target exists but cannot
625
+ // prove who created it; Eden's immutable deployment record resolves
626
+ // ownership next.
627
+ state: "unowned",
628
+ observed: { worker: workerPresent, container: containerPresent },
629
+ ...(providerEvidence === undefined ? {} : { providerEvidence }),
630
+ message: "The exact Worker or Container target already exists, and the read-only inventory cannot prove Eden owns it.",
631
+ remediation: "Deploy under a different --name or remove the conflicting Worker/Container yourself; Eden never adopts or replaces resources it cannot prove it created.",
632
+ },
633
+ };
634
+ }
635
+ /**
636
+ * Reads provider-backed facts about the live target: the Container
637
+ * application's configured image (from `wrangler containers list`) and the
638
+ * `EDEN_EVE_DEPLOYMENT_ID` plain-text var Eden stamps into every Worker it
639
+ * publishes (from the script settings API). Both are provider state — a
640
+ * foreign Worker can forge `x-eden-*` response headers but cannot set a var
641
+ * Eden recorded at publish time.
642
+ */
643
+ async function readProviderEvidence(workerName, accountId, workerPresent, containerImage) {
644
+ const evidence = {
645
+ ...(containerImage === undefined ? {} : { containerImage }),
646
+ };
647
+ if (workerPresent && accountId !== undefined) {
648
+ const token = process.env.CLOUDFLARE_API_TOKEN ??
649
+ (await readWranglerOAuthToken());
650
+ if (token !== undefined) {
651
+ try {
652
+ const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/scripts/${encodeURIComponent(workerName)}/settings`, {
653
+ headers: { authorization: `Bearer ${token}` },
654
+ signal: AbortSignal.timeout(20_000),
655
+ });
656
+ if (response.ok) {
657
+ evidence.settingsReadable = true;
658
+ const parsed = (await response.json());
659
+ const binding = parsed.result?.bindings?.find((value) => value.type === "plain_text" &&
660
+ value.name === "EDEN_EVE_DEPLOYMENT_ID");
661
+ if (typeof binding?.text === "string") {
662
+ evidence.workerDeploymentId = binding.text;
663
+ }
664
+ }
665
+ else if (response.status !== 404) {
666
+ evidence.settingsReadable = false;
667
+ }
668
+ }
669
+ catch {
670
+ evidence.settingsReadable = false;
671
+ }
672
+ }
673
+ }
674
+ return evidence;
675
+ }
676
+ /**
677
+ * Resolves `unowned` reads against Eden's immutable deployment records AND
678
+ * the live remote target. The local record proves Eden once created the
679
+ * target; the remote Worker's stamped identity proves it is still the one
680
+ * Eden deployed — a same-name Worker recreated by something else after the
681
+ * target was destroyed (or a forged local record) must never be upgraded to
682
+ * `owned`.
683
+ */
684
+ async function resolveEveTargetOwnership(request, result) {
685
+ if (result.target.state !== "unowned")
686
+ return result;
687
+ const record = await readEveDeploymentRecord(request.projectRoot, request.environment, request.name, result.accountId, request.projectId === "unknown-project" ? undefined : request.projectId);
688
+ if (record === undefined ||
689
+ record.identity.workerName !== request.name ||
690
+ record.identity.accountId !== result.accountId ||
691
+ (request.projectId !== "unknown-project" &&
692
+ record.identity.projectId !== undefined &&
693
+ record.identity.projectId !== request.projectId) ||
694
+ (result.target.observed?.container === true &&
695
+ record.identity.containerApplicationName !==
696
+ boundedResourceName(request.name, "container"))) {
697
+ return result;
698
+ }
699
+ // Remote proof must come from provider state. A foreign Worker can echo
700
+ // `x-eden-*` response headers, but it cannot plant the plain-text var Eden
701
+ // publishes into script settings, nor point the Container application at an
702
+ // image digest Eden recorded. The Worker is proven only by its own settings:
703
+ // a matching Container image proves the Container, never the Worker, so
704
+ // unreadable settings fail closed.
705
+ const evidence = result.target.providerEvidence;
706
+ const recordedDeploymentIds = new Set(record.deploymentIds);
707
+ const workerProven = result.target.observed?.worker !== true ||
708
+ (evidence?.settingsReadable === true &&
709
+ evidence.workerDeploymentId !== undefined &&
710
+ recordedDeploymentIds.has(evidence.workerDeploymentId));
711
+ const containerProven = result.target.observed?.container !== true ||
712
+ (evidence?.containerImage !== undefined &&
713
+ record.images.includes(evidence.containerImage));
714
+ const remoteProven = evidence !== undefined && workerProven && containerProven;
715
+ if (!remoteProven) {
716
+ return {
717
+ ...result,
718
+ target: {
719
+ ...result.target,
720
+ message: "The exact Worker or Container target exists, and although a local Eden record matches, provider state cannot prove it is the one Eden deployed — the target may have been recreated outside Eden.",
721
+ remediation: "Inspect the Worker at its workers.dev origin and the recorded deployment history before redeploying; if it is not Eden's, deploy under a different --name or delete it yourself.",
722
+ },
723
+ };
724
+ }
725
+ const priorIdentity = {
726
+ workerName: record.identity.workerName,
727
+ containerApplicationName: record.identity.containerApplicationName,
728
+ stableContainerInstanceName: record.identity.stableContainerInstanceName,
729
+ accountId: record.identity.accountId,
730
+ environment: request.environment,
731
+ name: request.name,
732
+ ...(record.identity.projectId === undefined
733
+ ? {}
734
+ : { projectId: record.identity.projectId }),
735
+ ...(record.identity.generationId === undefined
736
+ ? {}
737
+ : { generationId: record.identity.generationId }),
738
+ ...(record.identity.containerImage === undefined
739
+ ? {}
740
+ : { containerImage: record.identity.containerImage }),
741
+ };
742
+ return {
743
+ ...result,
744
+ target: {
745
+ state: "owned",
746
+ identity: priorIdentity,
747
+ ...(result.target.observed === undefined
748
+ ? {}
749
+ : { observed: result.target.observed }),
750
+ message: "The immutable deployment record proves Eden owns the exact target; deploy proceeds as an in-place update.",
611
751
  },
612
752
  };
613
753
  }
614
754
  async function readEveCloudflareTarget(request, options) {
615
- return options.cloudflareRead === undefined
616
- ? defaultCloudflareRead(request)
617
- : options.cloudflareRead(request);
755
+ const result = options.cloudflareRead === undefined
756
+ ? await defaultCloudflareRead(request)
757
+ : await options.cloudflareRead(request);
758
+ return resolveEveTargetOwnership(request, result);
618
759
  }
619
760
  async function resolvedCloudflareOrigin(result, workerName) {
620
761
  if (result.accountId === undefined ||
@@ -713,12 +854,12 @@ function cloudflareChecks(result) {
713
854
  ? undefined
714
855
  : "Use an authenticated read-only adapter with account and Container access; preflight never uploads, publishes, or changes remote state.";
715
856
  const target = result.target;
716
- const targetPasses = target.state === "absent" ||
717
- (target.state === "owned" && target.matchesCandidate === true);
857
+ const targetPasses = target.state === "absent" || target.state === "owned";
718
858
  const targetMessage = targetPasses
719
859
  ? target.state === "absent"
720
860
  ? "The exact target read is absent and is eligible without claiming ownership."
721
- : "The exact target is already owned by this candidate identity; no ownership claim was attempted."
861
+ : target.message ??
862
+ "The exact target is provably Eden-owned; deploy proceeds as an in-place update."
722
863
  : target.message ??
723
864
  "The exact target read is unowned, mismatched, cross-environment, or ambiguous.";
724
865
  const targetRemediation = targetPasses
@@ -1062,6 +1203,10 @@ function responseIdentity(response, body) {
1062
1203
  read("workerName", "x-eden-eve-worker-name");
1063
1204
  read("containerApplicationName", "x-eden-eve-container-application");
1064
1205
  read("stableContainerInstanceName", "x-eden-eve-container-instance");
1206
+ const started = response.headers.get("x-eden-eve-started-deployment");
1207
+ if (started !== null) {
1208
+ identity["startedDeploymentId"] = started;
1209
+ }
1065
1210
  read("containerImage", "x-eden-eve-container-image");
1066
1211
  return identity;
1067
1212
  }
@@ -1105,9 +1250,20 @@ function defaultHealthRunner() {
1105
1250
  body.state === "ready" ||
1106
1251
  body.ready === true);
1107
1252
  if (ready) {
1253
+ const observedIdentity = responseIdentity(response, body);
1254
+ // A Worker activates before its image rollout completes; promotion
1255
+ // must wait until the *served container* verifiably runs this
1256
+ // generation's image (the host stamps the header only after
1257
+ // inspecting the running instance).
1258
+ if (observedIdentity.startedDeploymentId !== request.identity.deploymentId) {
1259
+ lastReason =
1260
+ "The served Container has not verifiably started this deployment's image yet; waiting for the rollout to land.";
1261
+ await wait();
1262
+ continue;
1263
+ }
1108
1264
  return {
1109
1265
  status: "ready",
1110
- identity: responseIdentity(response, body),
1266
+ identity: observedIdentity,
1111
1267
  };
1112
1268
  }
1113
1269
  lastReason =
@@ -1161,8 +1317,13 @@ async function releaseEveTargetLock(lock) {
1161
1317
  await rm(lock.path, { force: true });
1162
1318
  return (await lstat(lock.path).catch(() => undefined)) === undefined;
1163
1319
  }
1164
- async function promoteEveTargetPointer(deploymentRoot, targetKey, generationRoot) {
1165
- const targetRoot = join(deploymentRoot, "targets", targetKey);
1320
+ /**
1321
+ * Atomically re-points the target's CURRENT symlink at a promoted immutable
1322
+ * generation. The pointer key is `<environment>-<name>` — the same path
1323
+ * `destroy` clears and the update ownership resolver reads.
1324
+ */
1325
+ async function promoteEveTargetPointer(deploymentRoot, pointerKey, generationRoot) {
1326
+ const targetRoot = join(deploymentRoot, "targets", pointerKey);
1166
1327
  const artifactBoundary = await realpath(dirname(deploymentRoot)).catch(() => undefined);
1167
1328
  const generationCanonical = await realpath(generationRoot).catch(() => undefined);
1168
1329
  if (artifactBoundary === undefined ||
@@ -1191,7 +1352,7 @@ async function promoteEveTargetPointer(deploymentRoot, targetKey, generationRoot
1191
1352
  throw new EveCliError({
1192
1353
  code: "EVE_DEPLOY_PROMOTION_FAILED",
1193
1354
  message: "The Eve target pointer directory could not be verified safely.",
1194
- source: targetKey,
1355
+ source: pointerKey,
1195
1356
  });
1196
1357
  }
1197
1358
  const currentPath = join(targetRoot, "CURRENT");
@@ -1208,7 +1369,7 @@ async function promoteEveTargetPointer(deploymentRoot, targetKey, generationRoot
1208
1369
  throw new EveCliError({
1209
1370
  code: "EVE_DEPLOY_PROMOTION_FAILED",
1210
1371
  message: "The Eve target pointer did not resolve to the promoted generation.",
1211
- source: targetKey,
1372
+ source: pointerKey,
1212
1373
  });
1213
1374
  }
1214
1375
  return currentPath;
@@ -1242,9 +1403,10 @@ async function writeEveIndeterminateEvidence(candidate, evidence, extra = {}) {
1242
1403
  flag: "wx",
1243
1404
  });
1244
1405
  }
1245
- function deploymentMetadata(identity, targetKey, status) {
1406
+ function deploymentMetadata(identity, targetKey, status, operation, superseded) {
1246
1407
  return {
1247
1408
  status,
1409
+ operation,
1248
1410
  targetKey,
1249
1411
  deploymentId: identity.deploymentId,
1250
1412
  workerName: identity.workerName,
@@ -1252,6 +1414,12 @@ function deploymentMetadata(identity, targetKey, status) {
1252
1414
  stableContainerInstanceName: identity.stableContainerInstanceName,
1253
1415
  stableWorkersDevOrigin: identity.stableWorkersDevOrigin,
1254
1416
  imageReference: identity.containerImage,
1417
+ ...(superseded?.generationId === undefined
1418
+ ? {}
1419
+ : { supersededGenerationId: superseded.generationId }),
1420
+ ...(superseded?.containerImage === undefined
1421
+ ? {}
1422
+ : { supersededImageReference: superseded.containerImage }),
1255
1423
  runtimeVariableNames: identity.runtimeVariableNames,
1256
1424
  evidenceRetained: true,
1257
1425
  };
@@ -1273,6 +1441,46 @@ async function runEveDeployment(request, options, collected) {
1273
1441
  collected.projectId === undefined) {
1274
1442
  throw deploymentFailure("EVE_DEPLOY_CHECKS_FAILED", "The inline Eve deploy checks did not return a complete immutable candidate.");
1275
1443
  }
1444
+ const isUpdate = cloudflare.target.state === "owned";
1445
+ const operation = isUpdate ? "update" : "create";
1446
+ const prior = collected.priorDeployment;
1447
+ if (isUpdate && prior === undefined) {
1448
+ throw deploymentFailure("EVE_UPDATE_RECORD_UNAVAILABLE", "The exact target is provably Eden-owned, but its immutable deployment record is unreadable; refusing to update without the recorded Durable Object history.");
1449
+ }
1450
+ if (isUpdate && prior?.durableObjects === undefined) {
1451
+ throw deploymentFailure("EVE_UPDATE_RECORD_UNAVAILABLE", "The prior deployment record does not carry a readable Durable Object history; refusing to update because replacing it could emit a divergent migration list.");
1452
+ }
1453
+ const priorIdentity = prior?.identity;
1454
+ const priorDurableObjects = prior?.durableObjects;
1455
+ const superseded = isUpdate
1456
+ ? {
1457
+ generationId: priorIdentity?.generationId,
1458
+ containerImage: priorIdentity?.containerImage,
1459
+ }
1460
+ : undefined;
1461
+ const resultWarnings = collected.result.warnings;
1462
+ if (isUpdate) {
1463
+ const world = collected.workflowWorld;
1464
+ const durableWorld = world !== undefined &&
1465
+ world !== "local" &&
1466
+ world !== "@workflow/world-local";
1467
+ if (durableWorld) {
1468
+ resultWarnings.push({
1469
+ id: "EVE_UPDATE_IN_FLIGHT",
1470
+ message: "This update replaces the running Container; durable World runs in flight resume on the new code — Cloudflare has no deployment pinning, so keep step names and payload shapes backward compatible.",
1471
+ remediation: "Let pending approvals and long runs settle before updating, or keep step names, queue names, and serialized payloads compatible across the change.",
1472
+ });
1473
+ }
1474
+ if (prior?.worldCloudflare !==
1475
+ (world === "@moinulmoin/eden-world-cloudflare")) {
1476
+ resultWarnings.push({
1477
+ id: "EVE_UPDATE_WORLD_SWITCH",
1478
+ message: "The compiled Workflow World changed since the last deploy; existing sessions and pending approvals stay in the previous World and do not migrate to the new one.",
1479
+ remediation: "Existing Durable Object data is preserved (classes and bindings are never removed), but runs started before this update remain where they were created.",
1480
+ });
1481
+ }
1482
+ eveProgress(options, "update: Durable Object data is preserved; the running Container restarts on its next request to serve the new image");
1483
+ }
1276
1484
  const accountId = cloudflare.accountId ??
1277
1485
  cloudflare.target.identity?.accountId;
1278
1486
  const workersDevSubdomain = cloudflare.workersDevSubdomain ??
@@ -1421,6 +1629,13 @@ async function runEveDeployment(request, options, collected) {
1421
1629
  ...(collected.workflowWorld === undefined
1422
1630
  ? {}
1423
1631
  : { workflowWorld: collected.workflowWorld }),
1632
+ // In-place updates carry the prior Durable Object bindings and
1633
+ // migration history forward verbatim: classes are append-only, never
1634
+ // re-declared, and never deleted (a deleted_classes migration destroys
1635
+ // the data).
1636
+ ...(isUpdate && priorDurableObjects !== undefined
1637
+ ? { durableObjects: priorDurableObjects }
1638
+ : {}),
1424
1639
  ...(process.env.EDEN_EVE_CONTAINER_SLEEP_AFTER === undefined
1425
1640
  ? {}
1426
1641
  : { containerSleepAfter: process.env.EDEN_EVE_CONTAINER_SLEEP_AFTER }),
@@ -1448,7 +1663,7 @@ async function runEveDeployment(request, options, collected) {
1448
1663
  }
1449
1664
  catch {
1450
1665
  await writeEveDeploymentRecord(candidate, identity, hostConfig, "indeterminate", { evidence: "publication-threw-before-terminal-result" });
1451
- const deployment = deploymentMetadata(identity, targetKey, "indeterminate");
1666
+ const deployment = deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded);
1452
1667
  return {
1453
1668
  ...collected.result,
1454
1669
  ok: false,
@@ -1460,7 +1675,7 @@ async function runEveDeployment(request, options, collected) {
1460
1675
  return {
1461
1676
  ...collected.result,
1462
1677
  ok: false,
1463
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1678
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1464
1679
  };
1465
1680
  }
1466
1681
  if (publication.status === "failed" ||
@@ -1472,7 +1687,7 @@ async function runEveDeployment(request, options, collected) {
1472
1687
  return {
1473
1688
  ...collected.result,
1474
1689
  ok: false,
1475
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1690
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1476
1691
  };
1477
1692
  }
1478
1693
  if (publication.status === "failed" &&
@@ -1483,7 +1698,7 @@ async function runEveDeployment(request, options, collected) {
1483
1698
  return {
1484
1699
  ...collected.result,
1485
1700
  ok: false,
1486
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1701
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1487
1702
  };
1488
1703
  }
1489
1704
  if (publication.status === "failed" &&
@@ -1501,7 +1716,7 @@ async function runEveDeployment(request, options, collected) {
1501
1716
  return {
1502
1717
  ...collected.result,
1503
1718
  ok: false,
1504
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1719
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1505
1720
  };
1506
1721
  }
1507
1722
  }
@@ -1513,7 +1728,7 @@ async function runEveDeployment(request, options, collected) {
1513
1728
  return {
1514
1729
  ...collected.result,
1515
1730
  ok: false,
1516
- deployment: deploymentMetadata(identity, targetKey, "failed"),
1731
+ deployment: deploymentMetadata(identity, targetKey, "failed", operation, superseded),
1517
1732
  };
1518
1733
  }
1519
1734
  let health;
@@ -1529,7 +1744,7 @@ async function runEveDeployment(request, options, collected) {
1529
1744
  return {
1530
1745
  ...collected.result,
1531
1746
  ok: false,
1532
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1747
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1533
1748
  };
1534
1749
  }
1535
1750
  if (health.status === "indeterminate") {
@@ -1537,7 +1752,7 @@ async function runEveDeployment(request, options, collected) {
1537
1752
  return {
1538
1753
  ...collected.result,
1539
1754
  ok: false,
1540
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1755
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1541
1756
  };
1542
1757
  }
1543
1758
  const observedHealthIdentity = health.status === "ready"
@@ -1554,7 +1769,7 @@ async function runEveDeployment(request, options, collected) {
1554
1769
  return {
1555
1770
  ...collected.result,
1556
1771
  ok: false,
1557
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1772
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1558
1773
  };
1559
1774
  }
1560
1775
  if (publication.createdByAttempt === true &&
@@ -1564,7 +1779,7 @@ async function runEveDeployment(request, options, collected) {
1564
1779
  return {
1565
1780
  ...collected.result,
1566
1781
  ok: false,
1567
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1782
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1568
1783
  };
1569
1784
  }
1570
1785
  if (publication.createdByAttempt === true &&
@@ -1578,7 +1793,7 @@ async function runEveDeployment(request, options, collected) {
1578
1793
  return {
1579
1794
  ...collected.result,
1580
1795
  ok: false,
1581
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1796
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1582
1797
  };
1583
1798
  }
1584
1799
  }
@@ -1590,7 +1805,7 @@ async function runEveDeployment(request, options, collected) {
1590
1805
  return {
1591
1806
  ...collected.result,
1592
1807
  ok: false,
1593
- deployment: deploymentMetadata(identity, targetKey, "failed"),
1808
+ deployment: deploymentMetadata(identity, targetKey, "failed", operation, superseded),
1594
1809
  };
1595
1810
  }
1596
1811
  try {
@@ -1609,7 +1824,7 @@ async function runEveDeployment(request, options, collected) {
1609
1824
  return {
1610
1825
  ...collected.result,
1611
1826
  ok: false,
1612
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1827
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1613
1828
  };
1614
1829
  }
1615
1830
  await writeEveDeploymentRecord(candidate, identity, hostConfig, "failed", {
@@ -1620,7 +1835,7 @@ async function runEveDeployment(request, options, collected) {
1620
1835
  return {
1621
1836
  ...collected.result,
1622
1837
  ok: false,
1623
- deployment: deploymentMetadata(identity, targetKey, "failed"),
1838
+ deployment: deploymentMetadata(identity, targetKey, "failed", operation, superseded),
1624
1839
  };
1625
1840
  }
1626
1841
  await writeEveDeploymentRecord(candidate, identity, hostConfig, "indeterminate", {
@@ -1631,7 +1846,7 @@ async function runEveDeployment(request, options, collected) {
1631
1846
  return {
1632
1847
  ...collected.result,
1633
1848
  ok: false,
1634
- deployment: deploymentMetadata(identity, targetKey, "indeterminate"),
1849
+ deployment: deploymentMetadata(identity, targetKey, "indeterminate", operation, superseded),
1635
1850
  };
1636
1851
  }
1637
1852
  await writeEveDeploymentRecord(candidate, identity, hostConfig, "deployed", {
@@ -1641,8 +1856,33 @@ async function runEveDeployment(request, options, collected) {
1641
1856
  identity: "exact",
1642
1857
  },
1643
1858
  });
1644
- await promoteEveTargetPointer(join(request.projectRoot, ".eden", "eve-deploy"), targetKey, candidate.generationRoot);
1859
+ await promoteEveTargetPointer(join(request.projectRoot, ".eden", "eve-deploy"), `${request.environment}-${request.name}`, candidate.generationRoot);
1645
1860
  await options.afterPromotion?.();
1861
+ let supersededImageCleanup;
1862
+ if (isUpdate) {
1863
+ // The superseded generation's registry repository is generation-scoped
1864
+ // and no longer referenced once CURRENT promotes this generation; only
1865
+ // then is it safe to delete.
1866
+ const previousImage = priorIdentity === undefined
1867
+ ? undefined
1868
+ : ownedRegistryImageRef(priorIdentity, request.environment, request.name, accountId);
1869
+ if (previousImage !== undefined) {
1870
+ eveProgress(options, `deleting superseded registry image ${previousImage}`);
1871
+ let deleted = "indeterminate";
1872
+ try {
1873
+ deleted = await (options.deleteRegistryImage ??
1874
+ defaultRegistryImageDelete)({ image: previousImage });
1875
+ }
1876
+ catch {
1877
+ deleted = "indeterminate";
1878
+ }
1879
+ supersededImageCleanup = deleted === "indeterminate"
1880
+ ? check("VAL-LIFE-007", "failed", `The update is healthy and promoted, but the superseded registry image ${previousImage} could not be proven deleted.`, `Remove only the recorded image ref ${previousImage}; do not retry or run broad registry cleanup.`)
1881
+ : check("VAL-LIFE-007", "passed", deleted === "deleted"
1882
+ ? "The superseded generation's registry image was deleted after promotion."
1883
+ : "The superseded generation's registry image was already absent.");
1884
+ }
1885
+ }
1646
1886
  let runtimeImageCleanup;
1647
1887
  if (runtimeEvidence.cleanup.imageRetained) {
1648
1888
  if (options.retainRuntimeImage === true) {
@@ -1666,13 +1906,18 @@ async function runEveDeployment(request, options, collected) {
1666
1906
  : check("VAL-CROSS-004", "failed", "The remote deployment is healthy, but exact local runtime-image cleanup could not be verified.", `Remove only the recorded image ${imageDigest}; do not retry or run broad Docker cleanup.`);
1667
1907
  }
1668
1908
  }
1909
+ const postPromotionChecks = [
1910
+ ...(supersededImageCleanup === undefined ? [] : [supersededImageCleanup]),
1911
+ ...(runtimeImageCleanup === undefined ? [] : [runtimeImageCleanup]),
1912
+ ];
1669
1913
  return {
1670
1914
  ...collected.result,
1671
- ok: runtimeImageCleanup?.status !== "failed",
1672
- ...(runtimeImageCleanup === undefined
1915
+ ok: runtimeImageCleanup?.status !== "failed" &&
1916
+ supersededImageCleanup?.status !== "failed",
1917
+ ...(postPromotionChecks.length === 0
1673
1918
  ? {}
1674
- : { checks: [...collected.result.checks, runtimeImageCleanup] }),
1675
- deployment: deploymentMetadata(identity, targetKey, "deployed"),
1919
+ : { checks: [...collected.result.checks, ...postPromotionChecks] }),
1920
+ deployment: deploymentMetadata(identity, targetKey, "deployed", operation, superseded),
1676
1921
  };
1677
1922
  }
1678
1923
  async function collectEvePreflight(request, options) {
@@ -1848,12 +2093,14 @@ async function collectEvePreflight(request, options) {
1848
2093
  }
1849
2094
  let cloudflare;
1850
2095
  let publicOrigin;
2096
+ let priorDeployment;
1851
2097
  if (request.command === "deploy") {
1852
2098
  eveProgress(options, "checking Cloudflare access and exact target");
1853
2099
  try {
1854
2100
  cloudflare = await readEveCloudflareTarget({
1855
2101
  projectId: packaging.project?.projectId ?? "unknown-project",
1856
2102
  sourceDigest: candidate.sourceDigest,
2103
+ projectRoot: request.projectRoot,
1857
2104
  environment: request.environment,
1858
2105
  name: request.name,
1859
2106
  }, options);
@@ -1875,6 +2122,17 @@ async function collectEvePreflight(request, options) {
1875
2122
  };
1876
2123
  }
1877
2124
  checks.push(...cloudflareChecks(cloudflare));
2125
+ // An owned target also requires its immutable record so the update can
2126
+ // carry the published Durable Object history forward append-only.
2127
+ if (cloudflare.target.state === "owned") {
2128
+ const record = await readEveDeploymentRecord(request.projectRoot, request.environment, request.name, cloudflare.accountId, packaging.project?.projectId === "unknown-project"
2129
+ ? undefined
2130
+ : packaging.project?.projectId);
2131
+ priorDeployment = record ?? undefined;
2132
+ if (record === undefined || record.durableObjects === undefined) {
2133
+ checks.push(check("VAL-CLI-007-TARGET-CONFLICT", "failed", "The exact target is Eden-owned, but its immutable deployment record does not carry a readable Durable Object history.", "Re-run the last successful deploy generation or destroy the exact target; updating without the recorded migration history could diverge Durable Object declarations."));
2134
+ }
2135
+ }
1878
2136
  publicOrigin = await resolvedCloudflareOrigin(cloudflare, request.name);
1879
2137
  if (publicOrigin === undefined) {
1880
2138
  checks.push(check("VAL-HOST-006", "failed", "The authenticated Cloudflare read did not prove the exact workers.dev origin before Eve initialization.", "Return one authenticated account ID, one workers.dev subdomain, and the exact Worker-origin binding."));
@@ -2006,6 +2264,7 @@ async function collectEvePreflight(request, options) {
2006
2264
  cloudflare = await readEveCloudflareTarget({
2007
2265
  projectId: packaging.project?.projectId ?? "unknown-project",
2008
2266
  sourceDigest: candidate.sourceDigest,
2267
+ projectRoot: request.projectRoot,
2009
2268
  environment: request.environment,
2010
2269
  name: request.name,
2011
2270
  }, options);
@@ -2049,6 +2308,7 @@ async function collectEvePreflight(request, options) {
2049
2308
  // reported as EVE_SCHEDULE_UNSUPPORTED and fire only while awake.
2050
2309
  schedules: expressible,
2051
2310
  cloudflare,
2311
+ ...(priorDeployment === undefined ? {} : { priorDeployment }),
2052
2312
  ...(workflowWorld === undefined ? {} : { workflowWorld }),
2053
2313
  };
2054
2314
  }
@@ -2095,7 +2355,7 @@ export async function runEveControlPlane(request, options = {}) {
2095
2355
  message: status === "indeterminate"
2096
2356
  ? "The Eve deployment outcome is indeterminate; inspect the retained exact ownership evidence before retrying."
2097
2357
  : status === "deployed"
2098
- ? "The Eve deployment is healthy and promoted, but exact local runtime-image cleanup failed; do not retry the deployment."
2358
+ ? "The Eve deployment is healthy and promoted, but exact image cleanup failed; do not retry the deployment."
2099
2359
  : "The Eve deployment failed before exact target promotion.",
2100
2360
  });
2101
2361
  }
@@ -2223,6 +2483,172 @@ async function parseEveDestroyRecord(recordPath, environment, name) {
2223
2483
  identity: verified,
2224
2484
  };
2225
2485
  }
2486
+ /**
2487
+ * Reads the immutable records for this target. The live identity comes from
2488
+ * CURRENT first, then the newest `deployed` record, then the newest record
2489
+ * of any status; the Durable Object history comes from the newest record
2490
+ * that carries one, healthy or not, so a failed update's published tags are
2491
+ * never dropped.
2492
+ */
2493
+ async function readEveDeploymentRecord(projectRoot, environment, name, accountId, projectId) {
2494
+ const targetsRoot = join(projectRoot, ".eden", "eve-deploy", "targets", `${environment}-${name}`);
2495
+ const generationsRoot = join(projectRoot, ".eden", "eve-deploy", "generations");
2496
+ const entries = await readdir(generationsRoot).catch(() => []);
2497
+ const scanned = [];
2498
+ const deploymentIds = new Set();
2499
+ const images = new Set();
2500
+ for (const entry of entries) {
2501
+ const generationRoot = join(generationsRoot, entry);
2502
+ const recordPath = join(generationRoot, "deployment.json");
2503
+ const details = await lstat(recordPath).catch(() => undefined);
2504
+ if (details === undefined || !details.isFile())
2505
+ continue;
2506
+ const parsed = await parseEveDeploymentRecord(recordPath, environment, name);
2507
+ if (parsed !== undefined) {
2508
+ // Only records for this account and (when known) this project may
2509
+ // arbitrate ownership or supply migration history.
2510
+ if ((accountId !== undefined && parsed.identity.accountId !== accountId) ||
2511
+ (projectId !== undefined && parsed.identity.projectId !== projectId)) {
2512
+ continue;
2513
+ }
2514
+ scanned.push({
2515
+ record: parsed,
2516
+ generationRoot,
2517
+ modifiedAt: details.mtimeMs,
2518
+ });
2519
+ const deploymentId = parsed.deploymentId;
2520
+ if (deploymentId !== undefined)
2521
+ deploymentIds.add(deploymentId);
2522
+ if (typeof parsed.identity.containerImage === "string") {
2523
+ images.add(parsed.identity.containerImage);
2524
+ }
2525
+ }
2526
+ }
2527
+ scanned.sort((left, right) => right.modifiedAt - left.modifiedAt);
2528
+ const current = await lstat(join(targetsRoot, "CURRENT")).catch(() => undefined);
2529
+ let live = scanned.find((entry) => entry.record.status === "deployed") ?? scanned[0];
2530
+ if (current !== undefined && current.isSymbolicLink()) {
2531
+ const generationRoot = await realpath(join(targetsRoot, "CURRENT")).catch(() => undefined);
2532
+ if (generationRoot !== undefined) {
2533
+ const pinned = scanned.find((entry) => entry.generationRoot === generationRoot);
2534
+ if (pinned !== undefined) {
2535
+ live = pinned;
2536
+ }
2537
+ else {
2538
+ const parsed = await parseEveDeploymentRecord(join(generationRoot, "deployment.json"), environment, name);
2539
+ if (parsed !== undefined &&
2540
+ !((accountId !== undefined &&
2541
+ parsed.identity.accountId !== accountId) ||
2542
+ (projectId !== undefined &&
2543
+ parsed.identity.projectId !== projectId))) {
2544
+ live = { record: parsed, generationRoot, modifiedAt: 0 };
2545
+ }
2546
+ }
2547
+ }
2548
+ }
2549
+ if (live === undefined)
2550
+ return undefined;
2551
+ // Newest record carrying a usable DO history wins, regardless of its
2552
+ // deployment outcome: a tag it published must never be replayed.
2553
+ const historySource = scanned.find((entry) => entry.record.durableObjects !== undefined);
2554
+ return {
2555
+ recordPath: live.record.recordPath,
2556
+ generationRoot: live.generationRoot,
2557
+ identity: live.record.identity,
2558
+ status: live.record.status,
2559
+ deploymentIds: [...deploymentIds],
2560
+ images: [...images],
2561
+ worldCloudflare: live.record.worldCloudflare,
2562
+ durableObjects: historySource?.record.durableObjects,
2563
+ };
2564
+ }
2565
+ async function parseEveDeploymentRecord(recordPath, environment, name) {
2566
+ const base = await parseEveDestroyRecord(recordPath, environment, name);
2567
+ if (base === undefined)
2568
+ return undefined;
2569
+ let parsed;
2570
+ try {
2571
+ parsed = JSON.parse(await readFile(recordPath, "utf8"));
2572
+ }
2573
+ catch {
2574
+ return undefined;
2575
+ }
2576
+ if (typeof parsed !== "object" || parsed === null)
2577
+ return undefined;
2578
+ const record = parsed;
2579
+ const identityRaw = parsed.identity;
2580
+ const deploymentId = typeof identityRaw === "object" &&
2581
+ identityRaw !== null &&
2582
+ typeof identityRaw
2583
+ .deploymentId === "string"
2584
+ ? identityRaw.deploymentId
2585
+ : undefined;
2586
+ const status = typeof record.status === "string" &&
2587
+ ["deployed", "failed", "indeterminate"].includes(record.status)
2588
+ ? record.status
2589
+ : undefined;
2590
+ const worker = typeof record.worker === "object" && record.worker !== null
2591
+ ? record.worker
2592
+ : undefined;
2593
+ const rawBindings = worker !== undefined && "durable_objects" in worker &&
2594
+ typeof worker.durable_objects === "object" &&
2595
+ worker.durable_objects !== null &&
2596
+ "bindings" in worker.durable_objects
2597
+ ? worker.durable_objects.bindings
2598
+ : undefined;
2599
+ const rawMigrations = worker !== undefined && "migrations" in worker
2600
+ ? worker.migrations
2601
+ : undefined;
2602
+ const rawVars = worker !== undefined && "vars" in worker &&
2603
+ typeof worker.vars === "object" && worker.vars !== null
2604
+ ? worker.vars
2605
+ : undefined;
2606
+ const bindings = [];
2607
+ const migrations = [];
2608
+ const durableObjectsValid = Array.isArray(rawBindings) &&
2609
+ Array.isArray(rawMigrations);
2610
+ if (durableObjectsValid) {
2611
+ for (const binding of rawBindings) {
2612
+ if (typeof binding !== "object" || binding === null ||
2613
+ typeof binding.name !== "string" ||
2614
+ typeof binding.class_name !== "string") {
2615
+ return {
2616
+ recordPath,
2617
+ identity: base.identity,
2618
+ status,
2619
+ deploymentId,
2620
+ worldCloudflare: rawVars?.EDEN_EVE_WORLD_CLOUDFLARE === true,
2621
+ durableObjects: undefined,
2622
+ };
2623
+ }
2624
+ bindings.push(binding);
2625
+ }
2626
+ for (const migration of rawMigrations) {
2627
+ if (typeof migration !== "object" || migration === null ||
2628
+ typeof migration.tag !== "string" ||
2629
+ !Array.isArray(migration.new_sqlite_classes) ||
2630
+ migration.new_sqlite_classes.some((className) => typeof className !== "string")) {
2631
+ return {
2632
+ recordPath,
2633
+ identity: base.identity,
2634
+ status,
2635
+ deploymentId,
2636
+ worldCloudflare: rawVars?.EDEN_EVE_WORLD_CLOUDFLARE === true,
2637
+ durableObjects: undefined,
2638
+ };
2639
+ }
2640
+ migrations.push(migration);
2641
+ }
2642
+ }
2643
+ return {
2644
+ recordPath,
2645
+ identity: base.identity,
2646
+ status,
2647
+ deploymentId,
2648
+ worldCloudflare: rawVars?.EDEN_EVE_WORLD_CLOUDFLARE === true,
2649
+ durableObjects: durableObjectsValid ? { bindings, migrations } : undefined,
2650
+ };
2651
+ }
2226
2652
  async function defaultDestroyTargetRead(request) {
2227
2653
  const account = await readWranglerJson(["whoami", "--json"]);
2228
2654
  const deployments = await readWranglerJson([
package/dist/eve.js CHANGED
@@ -49,7 +49,7 @@ Options:
49
49
  `,
50
50
  deploy: `Usage: eden deploy [--project <path>] [--env <preview|production>] [--name <name>] [--env-file <path>] [--json]
51
51
 
52
- Deploy the selected Eve project to one exact target after host checks pass.
52
+ Deploy the selected Eve project to one exact target after host checks pass. When the exact target already exists and Eden's immutable record proves it owns it, deploy updates the Worker in place and keeps Durable Object data; a target Eden cannot prove it owns still fails the conflict check.
53
53
 
54
54
  Options:
55
55
  --project <path> Eve project root (default: current directory)
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export type { EveCliCommand, EveCliEnvironment, EveCliExecutionRequest, EveCliHe
10
10
  export type { EveNodeImage, EvePackagingCheck, EvePackagingCode, EveProjectBuilder, EveProjectBuilderRequest, EveProjectBuilderResult, EveProjectBuildCandidate, EveProjectFile, EveProjectImage, EveProjectInputManifest, EveProjectOutput, EveProjectPackagingResult, EveProjectRuntime, EveProjectSnapshot, EveProjectSnapshotOptions, EveProjectToolchain, EveRuntimeConfigExclusion, } from "./eve-packaging.js";
11
11
  export type { EveHostRequirements, EveRuntimeClosure, EveRuntimeClosureFile, EveRuntimeCleanup, EveRuntimeImage, EveRuntimeImageDiscardRequest, EveRuntimeImageMetadata, EveRuntimeImageRequest, EveRuntimeImageResult, EveRuntimeNativeModule, EveRuntimeStatus, } from "./eve-runtime-image.js";
12
12
  export { DEFAULT_EVE_HOST_REQUIREMENTS, DEFAULT_EVE_NODE_IMAGE, runEveControlPlane, runEveDestroy, } from "./eve-control-plane.js";
13
- export type { EveCloudflareReadRequest, EveCloudflareReadResult, EveCloudflareReadRunner, EveCloudflareTargetState, EveContainerDeleteRunner, EveDeploymentCompensationRequest, EveDeploymentCompensationRunner, EveDeploymentHealthRequest, EveDeploymentHealthResult, EveDeploymentHealthRunner, EveDeploymentIdentity, EveDeploymentIdentityProof, EveDeploymentMetadata, EveDeploymentPublicationRequest, EveDeploymentPublicationResult, EveDeploymentPublicationRunner, EveDeploymentStatus, EveDestroyCloudflareReadRequest, EveDestroyCloudflareReadRunner, EveDestroyOutcome, EveDestroyTargetRead, EveImagePublicationRequest, EveImagePublicationResult, EveImagePublicationRunner, EvePreflightCandidate, EvePreflightResult, EvePreflightRuntimeEvidence, EvePreflightWarning, EvePreflightRuntimeRunner, EvePreflightRuntimeRunnerRequest, EveRuntimeConfigLoader, EveRuntimeImageDiscardRunner, EveWorkerDeleteRunner, } from "./eve-control-plane.js";
13
+ export type { EveCloudflareReadRequest, EveCloudflareReadResult, EveCloudflareReadRunner, EveCloudflareTargetState, EveContainerDeleteRunner, EveDeploymentCompensationRequest, EveDeploymentCompensationRunner, EveDeploymentHealthRequest, EveDeploymentHealthResult, EveDeploymentHealthRunner, EveDeploymentIdentity, EveDeploymentIdentityProof, EveDeploymentMetadata, EveDeploymentPublicationRequest, EveDeploymentPublicationResult, EveDeploymentPublicationRunner, EveDeploymentStatus, EveDestroyCloudflareReadRequest, EveDestroyCloudflareReadRunner, EveDestroyOutcome, EveDestroyTargetRead, EveImagePublicationRequest, EveImagePublicationResult, EveImagePublicationRunner, EvePreflightCandidate, EvePreflightResult, EvePreflightRuntimeEvidence, EvePreflightWarning, EvePreflightRuntimeRunner, EvePreflightRuntimeRunnerRequest, EveRegistryImageDeleteRunner, EveRuntimeConfigLoader, EveRuntimeImageDiscardRunner, EveWorkerDeleteRunner, } from "./eve-control-plane.js";
14
14
  export declare const EDEN_CLI_COMMANDS: readonly ["preflight", "deploy", "destroy"];
15
15
  export type EdenCliCommand = (typeof EDEN_CLI_COMMANDS)[number];
16
16
  export interface EdenCliRunOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moinulmoin/eden",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Deploy existing Eve projects to your own Cloudflare account.",
5
5
  "keywords": [
6
6
  "ai",
@@ -31,7 +31,7 @@
31
31
  "NOTICE"
32
32
  ],
33
33
  "dependencies": {
34
- "@moinulmoin/eden-runtime-cloudflare": "0.4.1",
34
+ "@moinulmoin/eden-runtime-cloudflare": "0.5.0",
35
35
  "wrangler": "4.120.0"
36
36
  },
37
37
  "devDependencies": {