@moinulmoin/eden 0.7.3 → 0.8.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.7.3
15
+ npm install --global @moinulmoin/eden@0.8.0
16
16
  ```
17
17
 
18
18
  ```sh
19
- pnpm add --global @moinulmoin/eden@0.7.3
19
+ pnpm add --global @moinulmoin/eden@0.8.0
20
20
  ```
21
21
 
22
22
  ```sh
23
- bun add --global @moinulmoin/eden@0.7.3
23
+ bun add --global @moinulmoin/eden@0.8.0
24
24
  ```
25
25
 
26
26
  Then confirm the CLI is available:
@@ -92,8 +92,8 @@ eden deploy \
92
92
  --env-file ~/.config/my-eve-app/preview.env
93
93
  ```
94
94
 
95
- `eden preflight` is an optional read-only diagnostic. You do not need to run it
96
- before `deploy`:
95
+ `eden preflight` is an optional diagnostic that changes nothing on
96
+ Cloudflare. You do not need to run it before `deploy`:
97
97
 
98
98
  ```sh
99
99
  eden preflight \
@@ -151,6 +151,11 @@ Run `eden <command> --help` for command-specific options.
151
151
  - [Documentation index](https://github.com/moinulmoin/eden/blob/main/docs/README.md)
152
152
  - [Installation](https://github.com/moinulmoin/eden/blob/main/docs/install.md)
153
153
  - [Deploying Eve projects](https://github.com/moinulmoin/eden/blob/main/docs/deploy.md)
154
+ - [How Eden works](https://github.com/moinulmoin/eden/blob/main/docs/how-it-works.md)
155
+ - [Security](https://github.com/moinulmoin/eden/blob/main/docs/security.md)
156
+ - [FAQ](https://github.com/moinulmoin/eden/blob/main/docs/faq.md)
157
+ - [Reference](https://github.com/moinulmoin/eden/blob/main/docs/reference.md)
158
+ - [Troubleshooting](https://github.com/moinulmoin/eden/blob/main/docs/troubleshooting.md)
154
159
  - [Validation and cleanup](https://github.com/moinulmoin/eden/blob/main/docs/validation.md)
155
160
 
156
161
  ## License
@@ -214,10 +214,82 @@ function pooledPostgresReason(connectionString) {
214
214
  return undefined;
215
215
  }
216
216
  /**
217
- * Non-blocking World warnings. The container wipes its disk on sleep, so the
218
- * default local World loses pending approvals and sessions; and a pooled
219
- * Postgres URL breaks the World streamer, which relies on LISTEN/NOTIFY.
217
+ * Recognizes the generated channel, not arbitrary user authentication policies.
218
+ * The calls below match the Eve 0.82 initializer's agent/channels/eve.ts.
220
219
  */
220
+ async function collectEveSetupWarnings(projectRoot) {
221
+ const warnings = [];
222
+ const source = async (path) => {
223
+ try {
224
+ return await readFile(join(projectRoot, path), "utf8");
225
+ }
226
+ catch {
227
+ return "";
228
+ }
229
+ };
230
+ const channel = (await source("agent/channels/eve.ts"))
231
+ .replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/gu, "")
232
+ .replace(/\s+/gu, "");
233
+ if (/exportdefaulteveChannel\(\{auth:\[vercelOidc\(\),localDev\(\),placeholderAuth\(\),?\],?\}\);?$/u
234
+ .test(channel)) {
235
+ warnings.push({
236
+ id: "EVE_AUTH_SCAFFOLD",
237
+ message: "Eve's generated channel auth does not grant Cloudflare callers access.",
238
+ remediation: "Replace agent/channels/eve.ts auth with your app's authenticator; put credentials in --env-file.",
239
+ });
240
+ }
241
+ try {
242
+ const manifest = JSON.parse(await source("package.json"));
243
+ const agent = await source("agent/agent.ts");
244
+ if (/export\s+default\s+defineAgent\s*\(\s*\{\s*model\s*:\s*["'][^"']+["']\s*,\s*reasoning\s*:\s*["'][^"']+["']\s*,?\s*\}\s*\)/u.test(agent)) {
245
+ warnings.push({
246
+ id: "EVE_WORLD_LOCAL",
247
+ message: "Local World stores memory on container disk; updates or a lost sleep snapshot reset sessions and approvals.",
248
+ remediation: "Configure @workflow/world-postgres; see docs/deploy.md#durable-state-postgres-world.",
249
+ });
250
+ }
251
+ if (manifest.dependencies?.["just-bash"] === undefined &&
252
+ agent.length > 0 &&
253
+ !/\bsandbox\s*:/u.test(agent)) {
254
+ const command = manifest.packageManager?.startsWith("pnpm@")
255
+ ? "pnpm add just-bash"
256
+ : "npm install just-bash";
257
+ warnings.push({
258
+ id: "EVE_JUST_BASH_MISSING",
259
+ message: "Eve's default sandbox needs the just-bash production dependency in Eden's builder.",
260
+ remediation: `Run ${command}, commit the updated manifest and lockfile, then retry.`,
261
+ });
262
+ }
263
+ if (!manifest.packageManager?.startsWith("pnpm@")) {
264
+ const lockText = await source("package-lock.json");
265
+ // Same trust level as the manifest above: npm wrote it; packaging validates it.
266
+ const lock = JSON.parse(lockText === "" ? "{}" : lockText);
267
+ const entries = lock.packages ?? {};
268
+ const skipped = new Set();
269
+ for (const [path, entry] of Object.entries(entries)) {
270
+ const at = path.lastIndexOf("node_modules/");
271
+ if (at !== -1 &&
272
+ entry.hasInstallScript === true &&
273
+ entry.dev !== true &&
274
+ entry.optional !== true &&
275
+ entry.devOptional !== true) {
276
+ skipped.add(path.slice(at + "node_modules/".length));
277
+ }
278
+ }
279
+ if (skipped.size > 0) {
280
+ warnings.push({
281
+ id: "EVE_NPM_INSTALL_SCRIPTS_SKIPPED",
282
+ message: `Eden's npm install skips dependency install scripts; these production dependencies have one and may fail at runtime: ${[...skipped].sort().join(", ")}.`,
283
+ remediation: "Use packages that need no install script, or switch to exact-pinned pnpm and list them under allowBuilds in pnpm-workspace.yaml.",
284
+ });
285
+ }
286
+ }
287
+ }
288
+ catch {
289
+ // Packaging reports invalid manifests; setup hints must not mask that result.
290
+ }
291
+ return warnings;
292
+ }
221
293
  async function collectEveWorldWarnings(candidate, runtimeConfig) {
222
294
  const warnings = [];
223
295
  if (candidate !== undefined) {
@@ -227,7 +299,7 @@ async function collectEveWorldWarnings(candidate, runtimeConfig) {
227
299
  world === "@workflow/world-local") {
228
300
  warnings.push({
229
301
  id: "EVE_WORLD_LOCAL",
230
- message: "This project uses Eve's default local Workflow World; workflow state lives on the container disk, survives sleep, and resets when you update the agent.",
302
+ message: "Local World stores memory on container disk; updates or a lost sleep snapshot reset sessions and approvals.",
231
303
  remediation: "Configure a durable Workflow World such as @workflow/world-postgres; see docs/deploy.md#durable-state-postgres-world.",
232
304
  });
233
305
  }
@@ -1981,6 +2053,7 @@ async function collectEvePreflight(request, options) {
1981
2053
  runtimeConfig,
1982
2054
  };
1983
2055
  }
2056
+ warnings.push(...await collectEveSetupWarnings(request.projectRoot));
1984
2057
  eveProgress(options, "packaging project — snapshot, frozen install, eve build");
1985
2058
  const nodeImage = options.nodeImage ?? DEFAULT_EVE_NODE_IMAGE;
1986
2059
  const artifactRoot = options.artifactRoot ??
@@ -2087,6 +2160,10 @@ async function collectEvePreflight(request, options) {
2087
2160
  if (workflowWorld === "@moinulmoin/eden-world-cloudflare") {
2088
2161
  checks.push(check("EVE-WORLD", "passed", "world: Cloudflare Durable Object"));
2089
2162
  }
2163
+ // Compiled configuration supersedes the early generated-source hint.
2164
+ const localHint = warnings.findIndex((warning) => warning.id === "EVE_WORLD_LOCAL");
2165
+ if (localHint !== -1)
2166
+ warnings.splice(localHint, 1);
2090
2167
  warnings.push(...(await collectEveWorldWarnings(candidate, undefined)));
2091
2168
  const schedules = await readCompiledSchedules(candidate);
2092
2169
  const expressible = schedules.filter((schedule) => parseEveScheduleCron(schedule.cron) !== undefined);
@@ -19,18 +19,15 @@ export interface EveNodeImage {
19
19
  readonly reference: string;
20
20
  readonly digest: string;
21
21
  }
22
+ export type EvePackageManager = "pnpm" | "npm";
23
+ export type EveInstallCommand = readonly ["corepack", "pnpm", "install", "--frozen-lockfile"] | readonly ["npm", "ci", "--ignore-scripts", "--no-audit", "--no-fund"];
22
24
  export interface EveProjectBuilderRequest {
23
25
  readonly generationRoot: string;
24
26
  readonly snapshotRoot: string;
25
27
  readonly inputManifestPath: string;
26
28
  readonly dockerfilePath: string | undefined;
27
29
  readonly packageManagerVersion: string;
28
- readonly installCommand: readonly [
29
- "corepack",
30
- "pnpm",
31
- "install",
32
- "--frozen-lockfile"
33
- ];
30
+ readonly installCommand: EveInstallCommand;
34
31
  readonly buildCommand: readonly ["./node_modules/.bin/eve", "build"];
35
32
  readonly platform: "linux/amd64";
36
33
  readonly sourceDigest: string;
@@ -75,7 +72,7 @@ export interface EveProjectInputManifest {
75
72
  readonly requestedRoot: string;
76
73
  readonly canonicalRoot: string;
77
74
  readonly projectId: string;
78
- readonly packageManager: "pnpm";
75
+ readonly packageManager: EvePackageManager;
79
76
  readonly packageManagerVersion: string;
80
77
  readonly packageJsonSha256: string;
81
78
  readonly lockfileSha256: string;
@@ -123,14 +120,9 @@ export interface EveProjectBuildCandidate {
123
120
  readonly generationRoot: string;
124
121
  readonly snapshotRoot: string;
125
122
  readonly inputManifestPath: string;
126
- readonly packageManager: "pnpm";
123
+ readonly packageManager: EvePackageManager;
127
124
  readonly packageManagerVersion: string;
128
- readonly installCommand: readonly [
129
- "corepack",
130
- "pnpm",
131
- "install",
132
- "--frozen-lockfile"
133
- ];
125
+ readonly installCommand: EveInstallCommand;
134
126
  readonly buildCommand: readonly ["./node_modules/.bin/eve", "build"];
135
127
  readonly eveExecutable: "node_modules/.bin/eve";
136
128
  readonly eveVersion: string;
@@ -145,14 +137,9 @@ export interface EveProjectBuildCandidate {
145
137
  }
146
138
  export interface EveProjectToolchain {
147
139
  readonly nodeVersion: "24.17.0";
148
- readonly packageManager: "pnpm";
140
+ readonly packageManager: EvePackageManager;
149
141
  readonly packageManagerVersion: string;
150
- readonly installCommand: readonly [
151
- "corepack",
152
- "pnpm",
153
- "install",
154
- "--frozen-lockfile"
155
- ];
142
+ readonly installCommand: EveInstallCommand;
156
143
  readonly buildCommand: readonly ["./node_modules/.bin/eve", "build"];
157
144
  readonly startCommand: readonly [
158
145
  "./node_modules/.bin/eve",
@@ -227,7 +214,7 @@ export interface EveProjectPackagingResult {
227
214
  readonly sha256: string;
228
215
  };
229
216
  readonly lockfile: {
230
- readonly path: "pnpm-lock.yaml";
217
+ readonly path: "pnpm-lock.yaml" | "package-lock.json";
231
218
  readonly sha256: string;
232
219
  };
233
220
  readonly sourceDigest: string;
@@ -167,6 +167,9 @@ const INSTALL_COMMAND = [
167
167
  "install",
168
168
  "--frozen-lockfile",
169
169
  ];
170
+ const NPM_INSTALL_COMMAND = ["npm", "ci", "--ignore-scripts", "--no-audit", "--no-fund"];
171
+ // Install this exact isolated manager without consulting project npm configuration.
172
+ const NPM_VERSION = "11.13.0";
170
173
  const BUILD_COMMAND = ["./node_modules/.bin/eve", "build"];
171
174
  const EVE_ENTRYPOINT = ".output/server/index.mjs";
172
175
  const EVE_EXCLUDED_DIRECTORY_NAMES = new Set([
@@ -201,6 +204,7 @@ const EVE_COMPETING_LOCKFILES = new Set([
201
204
  "bun.lock",
202
205
  "bun.lockb",
203
206
  "npm-shrinkwrap.json",
207
+ "pnpm-lock.yaml",
204
208
  "package-lock.json",
205
209
  "yarn.lock",
206
210
  ]);
@@ -548,37 +552,76 @@ function validateProjectContract(inputs) {
548
552
  remediation: "Add a non-empty package.json name and retry.",
549
553
  });
550
554
  }
551
- const packageManagerVersion = parsePnpmVersion(packageValue.packageManager);
552
- const lockFiles = inputs.files.filter((file) => file.relativePath === "pnpm-lock.yaml" ||
553
- file.relativePath.endsWith("/pnpm-lock.yaml"));
554
- if (lockFiles.length !== 1 || lockFiles[0]?.relativePath !== "pnpm-lock.yaml") {
555
+ const npmLock = inputs.files.find((file) => file.relativePath === "package-lock.json");
556
+ const packageManager = npmLock === undefined ? "pnpm" : "npm";
557
+ const lockfilePath = packageManager === "npm" ? "package-lock.json" : "pnpm-lock.yaml";
558
+ const npmPin = typeof packageValue.packageManager === "string"
559
+ ? /^npm@((?:[7-9]|[1-9][0-9]+)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*))$/u.exec(packageValue.packageManager)?.[1]
560
+ : undefined;
561
+ const packageManagerVersion = packageManager === "pnpm"
562
+ ? parsePnpmVersion(packageValue.packageManager)
563
+ : npmPin ?? NPM_VERSION;
564
+ const lockFiles = inputs.files.filter((file) => file.relativePath === lockfilePath || file.relativePath.endsWith(`/${lockfilePath}`));
565
+ if (lockFiles.length !== 1 || lockFiles[0]?.relativePath !== lockfilePath) {
555
566
  throw new EvePackagingError({
556
567
  code: "DEPENDENCY_AMBIGUITY",
557
- subject: "pnpm-lock.yaml",
558
- reason: "The selected Eve root must contain exactly one regular root pnpm-lock.yaml.",
568
+ subject: lockfilePath,
569
+ reason: "The selected Eve root must contain exactly one regular root lockfile.",
559
570
  remediation: "Remove competing or nested lockfiles and retry with the matching root lockfile.",
560
571
  });
561
572
  }
562
- const competing = inputs.files.find((file) => EVE_COMPETING_LOCKFILES.has(basename(file.relativePath)));
573
+ const competing = inputs.files.find((file) => EVE_COMPETING_LOCKFILES.has(basename(file.relativePath)) && file.relativePath !== lockfilePath);
563
574
  if (competing !== undefined) {
564
575
  throw new EvePackagingError({
565
576
  code: "DEPENDENCY_AMBIGUITY",
566
577
  subject: competing.relativePath,
567
578
  reason: "A competing package-manager lockfile was found at the selected Eve root.",
568
- remediation: "Keep only the pinned pnpm toolchain and its matching root lockfile.",
579
+ remediation: "Keep only one supported package manager and its matching root lockfile.",
580
+ });
581
+ }
582
+ if (packageManager === "npm" && packageValue.packageManager !== undefined &&
583
+ npmPin === undefined) {
584
+ throw new EvePackagingError({
585
+ code: "UNSUPPORTED_TOOLCHAIN",
586
+ subject: "package.json.packageManager",
587
+ reason: "npm projects may omit packageManager or declare an exact npm version (major 7 or newer).",
588
+ remediation: `Remove the optional packageManager field to use npm@${NPM_VERSION}, or declare an exact npm@<major>.<minor>.<patch> pin.`,
569
589
  });
570
590
  }
571
591
  const lockfile = lockFiles[0];
572
- if (lockfile === undefined || !lockfileMatchesPnpm(packageManagerVersion, lockfile.bytes)) {
592
+ let npmLockValid = false;
593
+ if (packageManager === "npm" && lockfile !== undefined) {
594
+ try {
595
+ const parsed = JSON.parse(lockfile.bytes.toString("utf8"));
596
+ npmLockValid = (parsed.lockfileVersion === 2 || parsed.lockfileVersion === 3) &&
597
+ parsed.packages !== null && typeof parsed.packages === "object" &&
598
+ !Array.isArray(parsed.packages) && parsed.packages[""] !== undefined;
599
+ for (const entry of Object.values(parsed.packages ?? {})) {
600
+ const dependency = entry;
601
+ if (typeof dependency.resolved === "string" &&
602
+ !dependency.resolved.startsWith("https://registry.npmjs.org/"))
603
+ npmLockValid = false;
604
+ }
605
+ }
606
+ catch {
607
+ npmLockValid = false;
608
+ }
609
+ }
610
+ if (lockfile === undefined || (packageManager === "pnpm"
611
+ ? !lockfileMatchesPnpm(packageManagerVersion, lockfile.bytes)
612
+ : !npmLockValid)) {
573
613
  throw new EvePackagingError({
574
614
  code: "DEPENDENCY_AMBIGUITY",
575
- subject: "pnpm-lock.yaml",
576
- reason: "The root pnpm-lock.yaml is malformed or does not match the exact packageManager pin.",
577
- remediation: "Regenerate the lockfile with the declared pnpm version, without changing it during packaging.",
615
+ subject: lockfilePath,
616
+ reason: "The root lockfile is malformed or incompatible with the selected package manager or public registry.",
617
+ remediation: "Regenerate the root lockfile with the selected manager and public npm registry; private registries and project .npmrc configuration are not supported.",
578
618
  });
579
619
  }
580
620
  return {
581
621
  projectId,
622
+ packageManager,
623
+ lockfilePath,
624
+ installCommand: packageManager === "npm" ? NPM_INSTALL_COMMAND : INSTALL_COMMAND,
582
625
  packageManagerVersion,
583
626
  packageJsonSha256: packageFile.sha256,
584
627
  lockfileSha256: lockfile.sha256,
@@ -1077,7 +1120,7 @@ export async function eveSnapshotSourceEntries(snapshotRoot) {
1077
1120
  }
1078
1121
  async function writeDockerBuildFiles(options) {
1079
1122
  const image = imageReference(options.nodeImage, "The Linux/amd64 builder requires an immutable Node 24 image digest.", "Supply a verified Node 24 image reference and sha256 digest.");
1080
- const installSources = ["package.json", "pnpm-lock.yaml"];
1123
+ const installSources = ["package.json", options.lockfilePath];
1081
1124
  if ((await lstat(join(options.snapshotRoot, "pnpm-workspace.yaml"))
1082
1125
  .catch(() => undefined))?.isFile() === true) {
1083
1126
  installSources.push("pnpm-workspace.yaml");
@@ -1085,17 +1128,27 @@ async function writeDockerBuildFiles(options) {
1085
1128
  const runtimeSourceCopies = (await eveSnapshotSourceEntries(options.snapshotRoot))
1086
1129
  .map((name) => `COPY --from=builder ${JSON.stringify([`/workspace/${name}`, `/app/${name}`])}`)
1087
1130
  .join("\n");
1131
+ const npm = options.packageManager === "npm";
1132
+ const lockCheck = `test "$(sha256sum ${options.lockfilePath} | cut -d ' ' -f1)" = "${options.lockfileSha256}"`;
1133
+ const managerSetup = npm
1134
+ ? `npm install --global npm@${options.packageManagerVersion} --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org/ && test "$(npm --version)" = "${options.packageManagerVersion}"`
1135
+ : `corepack enable && corepack prepare pnpm@${options.packageManagerVersion} --activate && test "$(corepack pnpm --version)" = "${options.packageManagerVersion}"`;
1136
+ const install = npm
1137
+ ? "npm ci --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org/"
1138
+ : "corepack pnpm install --frozen-lockfile --config.node-linker=hoisted --config.strict-dep-builds=false";
1139
+ const productionInstall = npm
1140
+ ? `${install} --omit=dev`
1141
+ : "corepack pnpm install --frozen-lockfile --prod --config.node-linker=hoisted --config.strict-dep-builds=false";
1142
+ const policy = npm ? "" : ` && ${IGNORED_BUILDS_GUARD}`;
1088
1143
  const dockerfile = `# syntax=docker/dockerfile:1
1089
1144
  FROM --platform=linux/amd64 ${image} AS builder
1090
1145
  WORKDIR /workspace
1091
1146
  COPY ${JSON.stringify([...installSources, "./"])}
1092
- RUN corepack enable \\
1093
- && corepack prepare pnpm@${options.packageManagerVersion} --activate \\
1094
- && test "$(corepack pnpm --version)" = "${options.packageManagerVersion}" \\
1095
- && test "$(sha256sum pnpm-lock.yaml | cut -d ' ' -f1)" = "${options.lockfileSha256}" \\
1096
- && corepack pnpm install --frozen-lockfile --config.node-linker=hoisted --config.strict-dep-builds=false \\
1097
- && ${IGNORED_BUILDS_GUARD}
1098
- ${EVE_SANDBOX_RESUME_PATCH_DOCKERFILE_STEP}RUN test "$(sha256sum pnpm-lock.yaml | cut -d ' ' -f1)" = "${options.lockfileSha256}"
1147
+ RUN ${managerSetup} \\
1148
+ && ${lockCheck} \\
1149
+ && ${install}${policy} \\
1150
+ && ${lockCheck}
1151
+ ${EVE_SANDBOX_RESUME_PATCH_DOCKERFILE_STEP}RUN ${lockCheck}
1099
1152
  COPY . ./
1100
1153
  RUN test -x ./node_modules/.bin/eve \\
1101
1154
  && ./node_modules/.bin/eve build \\
@@ -1103,10 +1156,10 @@ RUN test -x ./node_modules/.bin/eve \\
1103
1156
 
1104
1157
  FROM builder AS runtime-deps
1105
1158
  RUN rm -rf node_modules \\
1106
- && corepack pnpm install --frozen-lockfile --prod --config.node-linker=hoisted --config.strict-dep-builds=false \\
1107
- && ${IGNORED_BUILDS_GUARD} \\
1159
+ && ${productionInstall}${policy} \\
1160
+ && ${lockCheck} \\
1108
1161
  && for l in node_modules/.bin/*; do t=$(readlink "$l") || continue; case "$t" in /*) ln -sfn "$(realpath --relative-to=node_modules/.bin "$t")" "$l" ;; esac; done
1109
- ${EVE_SANDBOX_RESUME_PATCH_DOCKERFILE_STEP}
1162
+ ${EVE_SANDBOX_RESUME_PATCH_DOCKERFILE_STEP}RUN ${lockCheck}
1110
1163
 
1111
1164
  FROM --platform=linux/amd64 ${image} AS runtime
1112
1165
  WORKDIR /app
@@ -1627,13 +1680,24 @@ function classifyPnpmInstallFailure(stderr) {
1627
1680
  }
1628
1681
  return undefined;
1629
1682
  }
1683
+ function classifyNpmInstallFailure(stderr) {
1684
+ if (!/\bnpm (?:ci|install|error|ERR!)/u.test(stderr))
1685
+ return undefined;
1686
+ const code = /npm (?:error|ERR!) code ([A-Z0-9_]+)/u.exec(stderr)?.[1];
1687
+ return new EvePackagingError({
1688
+ code: "DEPENDENCY_AMBIGUITY",
1689
+ subject: code === undefined ? "frozen npm install" : `npm ${code}`,
1690
+ reason: "The isolated pinned npm install could not complete with the captured lockfile and disabled lifecycle scripts.",
1691
+ remediation: "Fix the reported npm failure; regenerate package-lock.json with the selected exact npm version if it is out of sync. Use public registry dependencies without .npmrc credentials or dependency build scripts.",
1692
+ });
1693
+ }
1630
1694
  function classifyEveBuildFailure(stderr) {
1631
1695
  if (/\bjust-bash\b/u.test(stderr)) {
1632
1696
  return new EvePackagingError({
1633
1697
  code: "EVE_BUILD_FAILED",
1634
1698
  subject: "eve build",
1635
1699
  reason: "The project-local Eve build failed inside the isolated Linux/amd64 builder because Eve's default sandbox needs just-bash where no Docker daemon or /dev/kvm exists.",
1636
- remediation: "Declare just-bash as a production dependency of the Eve project, regenerate pnpm-lock.yaml, and retry.",
1700
+ remediation: "Declare just-bash as a production dependency of the Eve project, regenerate its package-manager lockfile, and retry.",
1637
1701
  });
1638
1702
  }
1639
1703
  return new EvePackagingError({
@@ -1655,6 +1719,9 @@ function classifyDockerBuildFailure(error) {
1655
1719
  if (/\beve\b/u.test(step) && /\bbuild\b/u.test(step)) {
1656
1720
  return classifyEveBuildFailure(stderr);
1657
1721
  }
1722
+ if (/\bnpm\b/u.test(step)) {
1723
+ return classifyNpmInstallFailure(`npm ci\n${stderr}`);
1724
+ }
1658
1725
  if (/pnpm|corepack/u.test(step)) {
1659
1726
  const pnpmFailure = classifyPnpmInstallFailure(stderr);
1660
1727
  if (pnpmFailure !== undefined)
@@ -1670,6 +1737,9 @@ function classifyDockerBuildFailure(error) {
1670
1737
  const pnpmFailure = classifyPnpmInstallFailure(stderr);
1671
1738
  if (pnpmFailure !== undefined)
1672
1739
  return pnpmFailure;
1740
+ const npmFailure = classifyNpmInstallFailure(stderr);
1741
+ if (npmFailure !== undefined)
1742
+ return npmFailure;
1673
1743
  if (/\bjust-bash\b|Cannot find package/u.test(stderr)) {
1674
1744
  return classifyEveBuildFailure(stderr);
1675
1745
  }
@@ -1915,7 +1985,7 @@ export async function buildEveProjectSnapshot(options) {
1915
1985
  requestedRoot: roots.requestedRoot,
1916
1986
  canonicalRoot: roots.canonicalRoot,
1917
1987
  projectId: contract.projectId,
1918
- packageManager: "pnpm",
1988
+ packageManager: contract.packageManager,
1919
1989
  packageManagerVersion: contract.packageManagerVersion,
1920
1990
  packageJsonSha256: contract.packageJsonSha256,
1921
1991
  lockfileSha256: contract.lockfileSha256,
@@ -1965,6 +2035,8 @@ export async function buildEveProjectSnapshot(options) {
1965
2035
  snapshotRoot,
1966
2036
  dockerfilePath,
1967
2037
  nodeImage,
2038
+ packageManager: contract.packageManager,
2039
+ lockfilePath: contract.lockfilePath,
1968
2040
  packageManagerVersion: contract.packageManagerVersion,
1969
2041
  lockfileSha256: contract.lockfileSha256,
1970
2042
  });
@@ -1977,7 +2049,7 @@ export async function buildEveProjectSnapshot(options) {
1977
2049
  inputManifestPath,
1978
2050
  dockerfilePath: actualDockerfilePath,
1979
2051
  packageManagerVersion: contract.packageManagerVersion,
1980
- installCommand: INSTALL_COMMAND,
2052
+ installCommand: contract.installCommand,
1981
2053
  buildCommand: BUILD_COMMAND,
1982
2054
  platform: "linux/amd64",
1983
2055
  sourceDigest: initialInputs.sourceDigest,
@@ -2009,14 +2081,14 @@ export async function buildEveProjectSnapshot(options) {
2009
2081
  const cleanupError = await discardBuiltCandidate();
2010
2082
  return blockedResult(cleanupError ?? afterBuildRace, { writtenPaths });
2011
2083
  }
2012
- const snapshotLockfile = await readStableFile(join(snapshotRoot, "pnpm-lock.yaml"), snapshotRoot);
2084
+ const snapshotLockfile = await readStableFile(join(snapshotRoot, contract.lockfilePath), snapshotRoot);
2013
2085
  if (snapshotLockfile.sha256 !== contract.lockfileSha256 ||
2014
2086
  !snapshotLockfile.bytes.equals(contract.lockfileBytes)) {
2015
2087
  const dependencyError = new EvePackagingError({
2016
2088
  code: "DEPENDENCY_AMBIGUITY",
2017
- subject: "pnpm-lock.yaml",
2089
+ subject: contract.lockfilePath,
2018
2090
  reason: "The frozen install changed the captured lockfile bytes.",
2019
- remediation: "Use the exact declared pnpm version with pnpm install --frozen-lockfile.",
2091
+ remediation: "Use the selected exact package manager with its frozen install command.",
2020
2092
  });
2021
2093
  const cleanupError = await discardBuiltCandidate();
2022
2094
  return blockedResult(cleanupError ?? dependencyError, { writtenPaths });
@@ -2031,9 +2103,9 @@ export async function buildEveProjectSnapshot(options) {
2031
2103
  await writeJson(runtimeManifestPath, {
2032
2104
  version: 1,
2033
2105
  generationId: basename(generationRoot),
2034
- packageManager: "pnpm",
2106
+ packageManager: contract.packageManager,
2035
2107
  packageManagerVersion: contract.packageManagerVersion,
2036
- installCommand: INSTALL_COMMAND,
2108
+ installCommand: contract.installCommand,
2037
2109
  buildCommand: BUILD_COMMAND,
2038
2110
  startCommand: START_COMMAND,
2039
2111
  sourceDigest: initialInputs.sourceDigest,
@@ -2093,7 +2165,7 @@ export async function buildEveProjectSnapshot(options) {
2093
2165
  sha256: contract.packageJsonSha256,
2094
2166
  },
2095
2167
  lockfile: {
2096
- path: "pnpm-lock.yaml",
2168
+ path: contract.lockfilePath,
2097
2169
  sha256: contract.lockfileSha256,
2098
2170
  },
2099
2171
  sourceDigest: initialInputs.sourceDigest,
@@ -2104,9 +2176,9 @@ export async function buildEveProjectSnapshot(options) {
2104
2176
  generationRoot,
2105
2177
  snapshotRoot,
2106
2178
  inputManifestPath,
2107
- packageManager: "pnpm",
2179
+ packageManager: contract.packageManager,
2108
2180
  packageManagerVersion: contract.packageManagerVersion,
2109
- installCommand: INSTALL_COMMAND,
2181
+ installCommand: contract.installCommand,
2110
2182
  buildCommand: BUILD_COMMAND,
2111
2183
  eveExecutable: "node_modules/.bin/eve",
2112
2184
  eveVersion: eve.version,
@@ -2137,9 +2209,9 @@ export async function buildEveProjectSnapshot(options) {
2137
2209
  snapshot,
2138
2210
  toolchain: {
2139
2211
  nodeVersion: "24.17.0",
2140
- packageManager: "pnpm",
2212
+ packageManager: contract.packageManager,
2141
2213
  packageManagerVersion: contract.packageManagerVersion,
2142
- installCommand: INSTALL_COMMAND,
2214
+ installCommand: contract.installCommand,
2143
2215
  buildCommand: BUILD_COMMAND,
2144
2216
  startCommand: START_COMMAND,
2145
2217
  eveExecutable: eve.path,
@@ -2172,7 +2244,7 @@ export async function buildEveProjectSnapshot(options) {
2172
2244
  id: "VAL-BUILD-001",
2173
2245
  status: "pass",
2174
2246
  subject: "package-manager",
2175
- reason: "The exact pnpm pin and one matching regular root lockfile were verified.",
2247
+ reason: "The selected package manager and one matching regular root lockfile were verified.",
2176
2248
  remediation: null,
2177
2249
  },
2178
2250
  {
@@ -2287,7 +2359,7 @@ export async function revalidateEveProjectCandidateInputs(candidate, runtimeConf
2287
2359
  }
2288
2360
  const observedLatest = latest ?? fail();
2289
2361
  const packageFile = observedLatest.files.find((file) => file.relativePath === "package.json");
2290
- const lockfile = observedLatest.files.find((file) => file.relativePath === "pnpm-lock.yaml");
2362
+ const lockfile = observedLatest.files.find((file) => file.relativePath === (candidate.packageManager === "npm" ? "package-lock.json" : "pnpm-lock.yaml"));
2291
2363
  if (packageFile === undefined ||
2292
2364
  lockfile === undefined ||
2293
2365
  packageFile.sha256 !== candidate.packageJsonSha256 ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moinulmoin/eden",
3
- "version": "0.7.3",
3
+ "version": "0.8.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.7.3",
34
+ "@moinulmoin/eden-runtime-cloudflare": "0.8.0",
35
35
  "wrangler": "4.147.0"
36
36
  },
37
37
  "devDependencies": {