@moinulmoin/eden 0.11.2 → 0.11.3

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.11.2
15
+ npm install --global @moinulmoin/eden@0.11.3
16
16
  ```
17
17
 
18
18
  ```sh
19
- pnpm add --global @moinulmoin/eden@0.11.2
19
+ pnpm add --global @moinulmoin/eden@0.11.3
20
20
  ```
21
21
 
22
22
  ```sh
23
- bun add --global @moinulmoin/eden@0.11.2
23
+ bun add --global @moinulmoin/eden@0.11.3
24
24
  ```
25
25
 
26
26
  Then confirm the CLI is available:
@@ -583,14 +583,14 @@ function candidateMetadata(candidate, imageDigest) {
583
583
  ...(imageDigest === undefined ? {} : { imageDigest }),
584
584
  };
585
585
  }
586
- function safeWranglerEnvironment() {
586
+ function safeWranglerEnvironment(cwd) {
587
587
  return {
588
- PATH: process.env.PATH,
589
- HOME: process.env.HOME,
590
- TMPDIR: process.env.TMPDIR,
591
- LANG: process.env.LANG,
592
- LC_ALL: process.env.LC_ALL,
588
+ ...safeDockerEnvironment(),
593
589
  CLOUDFLARE_API_TOKEN: process.env.CLOUDFLARE_API_TOKEN,
590
+ CLOUDFLARE_ACCOUNT_ID: process.env.CLOUDFLARE_ACCOUNT_ID,
591
+ NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS,
592
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
593
+ WRANGLER_CACHE_DIR: join(cwd, "cache"),
594
594
  CLOUDFLARE_API_BASE_URL: "https://api.cloudflare.com/client/v4",
595
595
  NODE_OPTIONS: undefined,
596
596
  };
@@ -604,11 +604,23 @@ function safeDockerEnvironment() {
604
604
  LC_ALL: process.env.LC_ALL,
605
605
  DOCKER_HOST: process.env.DOCKER_HOST,
606
606
  DOCKER_CONTEXT: process.env.DOCKER_CONTEXT,
607
+ DOCKER_CONFIG: process.env.DOCKER_CONFIG,
608
+ DOCKER_CERT_PATH: process.env.DOCKER_CERT_PATH,
609
+ DOCKER_TLS_VERIFY: process.env.DOCKER_TLS_VERIFY,
610
+ HTTP_PROXY: process.env.HTTP_PROXY,
611
+ HTTPS_PROXY: process.env.HTTPS_PROXY,
612
+ NO_PROXY: process.env.NO_PROXY,
613
+ http_proxy: process.env.http_proxy,
614
+ https_proxy: process.env.https_proxy,
615
+ no_proxy: process.env.no_proxy,
607
616
  };
608
617
  }
609
618
  async function withWranglerDirectory(operation) {
619
+ // An explicit --config and WRANGLER_CACHE_DIR inside this private folder
620
+ // stop Wrangler from searching its (possibly shared) parent directories.
610
621
  const cwd = await mkdtemp(join(tmpdir(), "eden-wrangler-"));
611
622
  try {
623
+ await writeFile(join(cwd, "wrangler.json"), "{}\n", { mode: 0o600 });
612
624
  return await operation(cwd);
613
625
  }
614
626
  finally {
@@ -617,8 +629,8 @@ async function withWranglerDirectory(operation) {
617
629
  }
618
630
  async function readWranglerJson(args, timeoutMs = 30_000) {
619
631
  try {
620
- const result = await withWranglerDirectory((cwd) => execFileAsync(process.execPath, [wranglerEntrypoint, ...args], {
621
- env: safeWranglerEnvironment(),
632
+ const result = await withWranglerDirectory((cwd) => execFileAsync(process.execPath, [wranglerEntrypoint, ...args, "--config", join(cwd, "wrangler.json")], {
633
+ env: safeWranglerEnvironment(cwd),
622
634
  cwd,
623
635
  maxBuffer: 2 * 1024 * 1024,
624
636
  timeout: timeoutMs,
@@ -648,7 +660,7 @@ function jsonCollection(value) {
648
660
  }
649
661
  return undefined;
650
662
  }
651
- function exactContainerEntries(value, name) {
663
+ function exactContainerEntries(value, name, complete = false) {
652
664
  const collection = jsonCollection(value);
653
665
  if (collection === undefined)
654
666
  return undefined;
@@ -668,7 +680,8 @@ function exactContainerEntries(value, name) {
668
680
  });
669
681
  }
670
682
  }
671
- if (entries.length === 0 && collection.length >= 100) {
683
+ // Without proven pagination, a full page that lacks the name proves nothing.
684
+ if (entries.length === 0 && !complete && collection.length >= 100) {
672
685
  return undefined;
673
686
  }
674
687
  return entries;
@@ -697,16 +710,25 @@ function authenticatedAccountId(value) {
697
710
  const account = accounts[0];
698
711
  if (typeof account !== "object" ||
699
712
  account === null ||
713
+ !("id" in account) ||
700
714
  typeof account.id !== "string") {
701
715
  return undefined;
702
716
  }
717
+ const selectedAccount = process.env.CLOUDFLARE_ACCOUNT_ID;
718
+ if (selectedAccount && selectedAccount !== account.id) {
719
+ return undefined;
720
+ }
703
721
  return account.id;
704
722
  }
705
723
  async function readWranglerOAuthToken() {
706
- const root = process.env.WRANGLER_HOME ??
724
+ const legacyRoot = join(homedir(), ".wrangler");
725
+ const legacyExists = (await lstat(legacyRoot).catch(() => undefined))?.isDirectory();
726
+ const configHome = process.env.XDG_CONFIG_HOME ??
707
727
  (process.platform === "darwin"
708
- ? join(homedir(), "Library", "Preferences", ".wrangler")
709
- : join(homedir(), ".config", ".wrangler"));
728
+ ? join(homedir(), "Library", "Preferences")
729
+ : join(homedir(), ".config"));
730
+ const root = process.env.WRANGLER_HOME ??
731
+ (legacyExists ? legacyRoot : join(configHome, ".wrangler"));
710
732
  const raw = await readFile(join(root, "config", "default.toml"), "utf8")
711
733
  .catch(() => undefined);
712
734
  if (raw === undefined)
@@ -756,19 +778,71 @@ async function readWorkersDevSubdomain(accountId) {
756
778
  export function exactTargetContainerEntries(value, name) {
757
779
  return exactContainerEntries(value, boundedResourceName(name, "container"));
758
780
  }
781
+ async function readTargetContainers(name) {
782
+ const account = await readWranglerJson(["whoami", "--json"]);
783
+ const accountId = authenticatedAccountId(account.value);
784
+ const token = process.env.CLOUDFLARE_API_TOKEN || await readWranglerOAuthToken();
785
+ if (accountId === undefined || !token)
786
+ return { failed: true };
787
+ // Unlike `containers list --json`, this endpoint supports an exact name
788
+ // filter and exposes result_info.next_page_token. No per_page means all.
789
+ const entries = [];
790
+ const seenTokens = new Set();
791
+ let pageToken;
792
+ try {
793
+ do {
794
+ const url = new URL(`https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/containers/applications`);
795
+ url.searchParams.set("name", name);
796
+ if (pageToken !== undefined)
797
+ url.searchParams.set("page_token", pageToken);
798
+ const response = await fetch(url, {
799
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
800
+ signal: AbortSignal.timeout(30_000),
801
+ });
802
+ if (!response.ok)
803
+ return { failed: true };
804
+ const body = await response.json();
805
+ if (typeof body !== "object" || body === null ||
806
+ !("success" in body) || body.success !== true ||
807
+ !("result" in body) || !Array.isArray(body.result))
808
+ return { failed: true };
809
+ const info = "result_info" in body ? body.result_info : undefined;
810
+ if (info !== undefined &&
811
+ (typeof info !== "object" || info === null || Array.isArray(info))) {
812
+ return { failed: true };
813
+ }
814
+ const next = info !== undefined && "next_page_token" in info
815
+ ? info.next_page_token
816
+ : undefined;
817
+ if (next !== undefined && next !== null && typeof next !== "string") {
818
+ return { failed: true };
819
+ }
820
+ // Missing pagination metadata on a full page cannot prove absence.
821
+ if (info === undefined && body.result.length >= 100)
822
+ return { failed: true };
823
+ entries.push(...body.result);
824
+ pageToken = typeof next === "string" && next.length > 0 ? next : undefined;
825
+ if (pageToken !== undefined) {
826
+ if (seenTokens.has(pageToken))
827
+ return { failed: true };
828
+ seenTokens.add(pageToken);
829
+ }
830
+ } while (pageToken !== undefined);
831
+ return { failed: false, value: entries };
832
+ }
833
+ catch {
834
+ return { failed: true };
835
+ }
836
+ }
759
837
  async function readExactTargetInventory(workerName, containerApp) {
760
- // Wrangler's JSON output does not expose a pagination cursor. A full page
761
- // without the exact name remains indeterminate in exactContainerEntries.
762
- const containers = await readWranglerJson([
763
- "containers", "list", "--json", "--per-page", "100",
764
- ]);
838
+ const containers = await readTargetContainers(containerApp);
765
839
  const deployments = await readWranglerJson([
766
840
  "deployments", "list", "--name", workerName, "--json",
767
841
  ]);
768
842
  return {
769
843
  containers,
770
844
  deployments,
771
- containerEntries: containers.failed ? undefined : exactContainerEntries(containers.value, containerApp),
845
+ containerEntries: containers.failed ? undefined : exactContainerEntries(containers.value, containerApp, true),
772
846
  deploymentEntries: exactDeploymentEntries(deployments.value),
773
847
  };
774
848
  }
@@ -1282,9 +1356,11 @@ const WRANGLER_PUBLICATION_TIMEOUT_MS = 600_000;
1282
1356
  async function runWranglerWithInput(args, input, timeoutMs = WRANGLER_OPERATION_TIMEOUT_MS) {
1283
1357
  // The project's TypeScript lib predates Promise.withResolvers.
1284
1358
  return withWranglerDirectory((cwd) => new Promise((resolveResult) => {
1285
- const child = spawn(process.execPath, [wranglerEntrypoint, ...args], {
1359
+ const child = spawn(process.execPath, [wranglerEntrypoint, ...args, ...(args.includes("--config")
1360
+ ? []
1361
+ : ["--config", join(cwd, "wrangler.json")])], {
1286
1362
  cwd,
1287
- env: safeWranglerEnvironment(),
1363
+ env: safeWranglerEnvironment(cwd),
1288
1364
  stdio: ["pipe", "pipe", "pipe"],
1289
1365
  });
1290
1366
  let stdout = "";
@@ -363,6 +363,7 @@ const EVE_EXCLUDED_FILE_NAMES = new Set([
363
363
  ".pnpmrc",
364
364
  ".pypirc",
365
365
  ".bunfig.toml",
366
+ "bunfig.toml",
366
367
  ".yarnrc",
367
368
  ".yarnrc.yml",
368
369
  ".dev.vars",
@@ -372,6 +373,9 @@ const EVE_EXCLUDED_FILE_NAMES = new Set([
372
373
  "id_ecdsa",
373
374
  "id_ed25519",
374
375
  "id_dsa",
376
+ "id_ecdsa_sk",
377
+ "id_ed25519_sk",
378
+ "id_xmss",
375
379
  "secrets.json",
376
380
  "credentials.json",
377
381
  "service-account.json",
@@ -427,15 +431,23 @@ function isAllowedSystemAlias(path) {
427
431
  const normalized = resolve(path);
428
432
  return normalized === "/var" || normalized === "/tmp";
429
433
  }
434
+ function isEnvironmentFile(name) {
435
+ const normalizedName = name.toLowerCase();
436
+ return normalizedName.startsWith(".env") || normalizedName.endsWith(".env");
437
+ }
430
438
  function isCredentialPath(relativePath) {
431
439
  const parts = relativePath.split("/");
432
- const name = parts[parts.length - 1];
433
- return name !== undefined && (EVE_EXCLUDED_FILE_NAMES.has(name) ||
440
+ const originalName = parts[parts.length - 1];
441
+ if (originalName === undefined)
442
+ return false;
443
+ const name = originalName.toLowerCase();
444
+ return (EVE_EXCLUDED_FILE_NAMES.has(originalName) ||
445
+ EVE_EXCLUDED_FILE_NAMES.has(name) ||
434
446
  name.startsWith(".dev.vars.") ||
435
447
  name.endsWith(".pem") || name.endsWith(".key") ||
436
448
  name.endsWith(".p12") || name.endsWith(".pfx") ||
437
449
  name.endsWith(".tfvars") ||
438
- (name === "credentials" && parts[parts.length - 2] === ".aws"));
450
+ (name === "credentials" && parts[parts.length - 2]?.toLowerCase() === ".aws"));
439
451
  }
440
452
  function isPathExcluded(relativePath, runtimeEnvRelativePath) {
441
453
  const parts = relativePath.split("/");
@@ -455,7 +467,7 @@ function isPathExcluded(relativePath, runtimeEnvRelativePath) {
455
467
  if (runtimeEnvRelativePath !== undefined && relativePath === runtimeEnvRelativePath) {
456
468
  return { excluded: true, category: "runtime-env" };
457
469
  }
458
- if (name !== undefined && name.startsWith(".env")) {
470
+ if (name !== undefined && isEnvironmentFile(name)) {
459
471
  return { excluded: true, category: "runtime-env" };
460
472
  }
461
473
  if (isCredentialPath(relativePath)) {
@@ -1753,7 +1765,7 @@ async function assertGeneratedTreesExcludeSecrets(snapshotRoot) {
1753
1765
  ? entry.name
1754
1766
  : `${relativeDirectory}/${entry.name}`;
1755
1767
  const candidate = join(directory, entry.name);
1756
- if (entry.name.startsWith(".env") ||
1768
+ if (isEnvironmentFile(entry.name) ||
1757
1769
  isCredentialPath(relativePath)) {
1758
1770
  throw new EvePackagingError({
1759
1771
  code: "SECRET_EXCLUSION_FAILED",
@@ -195,7 +195,8 @@ function parseEnvironmentText(text) {
195
195
  variableName: name,
196
196
  });
197
197
  }
198
- if (EVE_RESERVED_HOST_VARIABLES.includes(name)) {
198
+ if (EVE_RESERVED_HOST_VARIABLES.includes(name) ||
199
+ /^(DOCKER_|BUILDKIT_|BUILDX_|COMPOSE_|LD_|DYLD_)/u.test(name)) {
199
200
  throw new EveRuntimeConfigError({
200
201
  code: "EVE_ENV_RESERVED_VARIABLE",
201
202
  message: `The explicit Eve environment file attempts to override reserved host variable ${name}.` +
@@ -556,6 +556,15 @@ function safeDockerEnvironment() {
556
556
  LC_ALL: process.env.LC_ALL,
557
557
  DOCKER_HOST: process.env.DOCKER_HOST,
558
558
  DOCKER_CONTEXT: process.env.DOCKER_CONTEXT,
559
+ DOCKER_CONFIG: process.env.DOCKER_CONFIG,
560
+ DOCKER_CERT_PATH: process.env.DOCKER_CERT_PATH,
561
+ DOCKER_TLS_VERIFY: process.env.DOCKER_TLS_VERIFY,
562
+ HTTP_PROXY: process.env.HTTP_PROXY,
563
+ HTTPS_PROXY: process.env.HTTPS_PROXY,
564
+ NO_PROXY: process.env.NO_PROXY,
565
+ http_proxy: process.env.http_proxy,
566
+ https_proxy: process.env.https_proxy,
567
+ no_proxy: process.env.no_proxy,
559
568
  };
560
569
  }
561
570
  /**
@@ -567,7 +576,7 @@ function safeDockerEnvironment() {
567
576
  */
568
577
  async function docker(state, args, options = {}) {
569
578
  return execFileAsync(state.command, [...args], {
570
- env: { ...state.env, ...options.environment },
579
+ env: { ...options.environment, ...state.env },
571
580
  cwd: state.generationRoot,
572
581
  maxBuffer: options.maxBuffer ?? 4 * 1024 * 1024,
573
582
  });
@@ -987,7 +996,8 @@ export async function buildEveRuntimeImage(request) {
987
996
  run: (startRequest) => startRequest.env,
988
997
  });
989
998
  if (request.runtimeInjection !== undefined) {
990
- const collision = Object.keys(runtimeEnvironment).find((name) => DOCKER_CLI_OWNED_VARIABLES.includes(name));
999
+ const collision = Object.keys(runtimeEnvironment).find((name) => DOCKER_CLI_OWNED_VARIABLES.includes(name) ||
1000
+ /^(DOCKER_|BUILDKIT_|BUILDX_|COMPOSE_|LD_|DYLD_)/u.test(name));
991
1001
  if (collision !== undefined) {
992
1002
  throw packagingError("DOCKER_PLATFORM_BLOCKED", "runtime variable name", `The runtime variable ${collision} would replace the local Docker CLI's own ${collision}.`, `Rename ${collision} in --env-file; ${DOCKER_CLI_OWNED_VARIABLES.join(", ")} are reserved for the local Docker CLI.`);
993
1003
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moinulmoin/eden",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "Deploy existing Eve projects to your own Cloudflare account.",
5
5
  "keywords": [
6
6
  "ai",
@@ -32,7 +32,7 @@
32
32
  "NOTICE"
33
33
  ],
34
34
  "dependencies": {
35
- "@moinulmoin/eden-runtime-cloudflare": "0.11.2",
35
+ "@moinulmoin/eden-runtime-cloudflare": "0.11.3",
36
36
  "wrangler": "4.147.0"
37
37
  },
38
38
  "devDependencies": {