@moinulmoin/eden 0.11.1 → 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 +3 -3
- package/dist/SKILL.md +4 -1
- package/dist/eve-control-plane.d.ts +7 -4
- package/dist/eve-control-plane.js +210 -49
- package/dist/eve-packaging.js +34 -7
- package/dist/eve-runtime-config.d.ts +1 -1
- package/dist/eve-runtime-config.js +10 -2
- package/dist/eve-runtime-image.js +13 -3
- package/package.json +2 -2
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.
|
|
15
|
+
npm install --global @moinulmoin/eden@0.11.3
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
pnpm add --global @moinulmoin/eden@0.11.
|
|
19
|
+
pnpm add --global @moinulmoin/eden@0.11.3
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
|
-
bun add --global @moinulmoin/eden@0.11.
|
|
23
|
+
bun add --global @moinulmoin/eden@0.11.3
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Then confirm the CLI is available:
|
package/dist/SKILL.md
CHANGED
|
@@ -70,7 +70,10 @@ save a build.
|
|
|
70
70
|
`agent/channels/eve.ts` (`vercelOidc()`, `localDev()`, `placeholderAuth()`)
|
|
71
71
|
rejects every Cloudflare caller with 401. Replace it with a real
|
|
72
72
|
authenticator, for example a bearer token read from the env file. Keep
|
|
73
|
-
`localDev()` so `eve dev` still works locally
|
|
73
|
+
`localDev()` so `eve dev` still works locally. Never set `EVE_DEV`,
|
|
74
|
+
`VERCEL`, `VERCEL_ENV`, or `VERCEL_TARGET_ENV` in the deployment env file:
|
|
75
|
+
they switch Eve into development mode and can let unauthenticated callers
|
|
76
|
+
in. Eden rejects these names with `EVE_ENV_RESERVED_VARIABLE`:
|
|
74
77
|
|
|
75
78
|
```ts
|
|
76
79
|
import { localDev, type AuthFn } from "eve/channels/auth";
|
|
@@ -88,16 +88,17 @@ export interface EveCloudflareReadResult {
|
|
|
88
88
|
* Provider-backed facts about the live target, read through the
|
|
89
89
|
* Cloudflare API (not self-asserted response headers, which any Worker
|
|
90
90
|
* can forge): the Container application's Durable Object namespace and
|
|
91
|
-
* the `EDEN_EVE_DEPLOYMENT_ID` plain-text
|
|
92
|
-
* Worker it publishes. `settingsReadable` tells
|
|
93
|
-
* whether a missing var is evidence of a foreign
|
|
94
|
-
* unreadable settings call.
|
|
91
|
+
* the `EDEN_EVE_DEPLOYMENT_ID` and `EDEN_EVE_RELEASE_ID` plain-text vars
|
|
92
|
+
* Eden stamps into every Worker it publishes. `settingsReadable` tells
|
|
93
|
+
* the ownership resolver whether a missing var is evidence of a foreign
|
|
94
|
+
* Worker or just an unreadable settings call.
|
|
95
95
|
*/
|
|
96
96
|
readonly providerEvidence?: {
|
|
97
97
|
readonly containerApplicationId?: string;
|
|
98
98
|
readonly containerSchedulingPolicy?: string;
|
|
99
99
|
readonly containerNamespaceId?: string;
|
|
100
100
|
readonly workerDeploymentId?: string;
|
|
101
|
+
readonly workerReleaseId?: string;
|
|
101
102
|
readonly workerContainerNamespaceId?: string;
|
|
102
103
|
readonly settingsReadable?: boolean;
|
|
103
104
|
};
|
|
@@ -284,6 +285,8 @@ export interface EvePreflightOptions {
|
|
|
284
285
|
}
|
|
285
286
|
export interface EveDestroyTargetRead {
|
|
286
287
|
readonly workerExists: boolean;
|
|
288
|
+
readonly inventoryComplete?: boolean;
|
|
289
|
+
readonly providerEvidence?: EveCloudflareReadResult["target"]["providerEvidence"];
|
|
287
290
|
readonly containerApplicationId?: string;
|
|
288
291
|
readonly accountId?: string;
|
|
289
292
|
/**
|
|
@@ -2,8 +2,8 @@ import { setTimeout as waitForActivation } from "node:timers/promises";
|
|
|
2
2
|
import { createRequire, } from "node:module";
|
|
3
3
|
import { createHash, randomUUID, } from "node:crypto";
|
|
4
4
|
import { join, dirname, relative, } from "node:path";
|
|
5
|
-
import { lstat, mkdir, readFile, readdir, realpath, rename, rm, symlink, writeFile, } from "node:fs/promises";
|
|
6
|
-
import { homedir, } from "node:os";
|
|
5
|
+
import { lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, symlink, writeFile, } from "node:fs/promises";
|
|
6
|
+
import { homedir, tmpdir, } from "node:os";
|
|
7
7
|
import { createServer, } from "node:net";
|
|
8
8
|
import { execFile, spawn, } from "node:child_process";
|
|
9
9
|
import { fileURLToPath, } from "node:url";
|
|
@@ -26,7 +26,6 @@ export const DEFAULT_EVE_HOST_REQUIREMENTS = Object.freeze({
|
|
|
26
26
|
network: "supported",
|
|
27
27
|
durableLocalFilesystem: false,
|
|
28
28
|
});
|
|
29
|
-
const wranglerCwd = dirname(wranglerEntrypoint);
|
|
30
29
|
export const DEFAULT_EVE_NODE_IMAGE = Object.freeze({
|
|
31
30
|
reference: "node:24.17.0-bookworm-slim",
|
|
32
31
|
digest: "sha256:862263c612aa437e3037674b85419622a9d93bff80aa1eee5398dfe686375532",
|
|
@@ -223,11 +222,15 @@ async function checkEveWorldPairing(candidate, world) {
|
|
|
223
222
|
return undefined;
|
|
224
223
|
const eveManifest = await readInstalledManifest(candidate.snapshotRoot, "eve");
|
|
225
224
|
const worldManifest = await readInstalledManifest(candidate.snapshotRoot, world);
|
|
225
|
+
if (world === "@moinulmoin/eden-world-cloudflare" &&
|
|
226
|
+
worldManifest?.eden?.capabilities?.worldRpcAuth !== true) {
|
|
227
|
+
return check("EVE_WORLD_PAIRING", "failed", "The installed Cloudflare World lets the agent's sandbox read and change every session's workflow state.", "Install @moinulmoin/eden-world-cloudflare@0.11.2 or later; see docs/troubleshooting.md.");
|
|
228
|
+
}
|
|
226
229
|
if (world === "@moinulmoin/eden-world-cloudflare" &&
|
|
227
230
|
worldManifest !== undefined &&
|
|
228
231
|
eveRequiresAtomicStartHook(eveManifest?.version) &&
|
|
229
232
|
worldManifest.eden?.capabilities?.atomicStartHook !== true) {
|
|
230
|
-
return check("EVE_WORLD_PAIRING", "failed", `Eve ${eveManifest?.version} needs a World with atomic start hooks, and the installed ${world} ${worldManifest.version ?? "(unknown version)"} does not have them; every new session would fail with HTTP 500.`, `Install ${world}@0.11.
|
|
233
|
+
return check("EVE_WORLD_PAIRING", "failed", `Eve ${eveManifest?.version} needs a World with atomic start hooks, and the installed ${world} ${worldManifest.version ?? "(unknown version)"} does not have them; every new session would fail with HTTP 500.`, `Install ${world}@0.11.2 or later; see docs/troubleshooting.md.`);
|
|
231
234
|
}
|
|
232
235
|
const expectedRange = eveManifest === undefined
|
|
233
236
|
? undefined
|
|
@@ -580,14 +583,15 @@ function candidateMetadata(candidate, imageDigest) {
|
|
|
580
583
|
...(imageDigest === undefined ? {} : { imageDigest }),
|
|
581
584
|
};
|
|
582
585
|
}
|
|
583
|
-
function safeWranglerEnvironment() {
|
|
586
|
+
function safeWranglerEnvironment(cwd) {
|
|
584
587
|
return {
|
|
585
|
-
|
|
586
|
-
HOME: process.env.HOME,
|
|
587
|
-
TMPDIR: process.env.TMPDIR,
|
|
588
|
-
LANG: process.env.LANG,
|
|
589
|
-
LC_ALL: process.env.LC_ALL,
|
|
588
|
+
...safeDockerEnvironment(),
|
|
590
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
|
+
CLOUDFLARE_API_BASE_URL: "https://api.cloudflare.com/client/v4",
|
|
591
595
|
NODE_OPTIONS: undefined,
|
|
592
596
|
};
|
|
593
597
|
}
|
|
@@ -600,16 +604,37 @@ function safeDockerEnvironment() {
|
|
|
600
604
|
LC_ALL: process.env.LC_ALL,
|
|
601
605
|
DOCKER_HOST: process.env.DOCKER_HOST,
|
|
602
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,
|
|
603
616
|
};
|
|
604
617
|
}
|
|
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.
|
|
621
|
+
const cwd = await mkdtemp(join(tmpdir(), "eden-wrangler-"));
|
|
622
|
+
try {
|
|
623
|
+
await writeFile(join(cwd, "wrangler.json"), "{}\n", { mode: 0o600 });
|
|
624
|
+
return await operation(cwd);
|
|
625
|
+
}
|
|
626
|
+
finally {
|
|
627
|
+
await rm(cwd, { recursive: true, force: true });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
605
630
|
async function readWranglerJson(args, timeoutMs = 30_000) {
|
|
606
631
|
try {
|
|
607
|
-
const result = await execFileAsync(process.execPath, [wranglerEntrypoint, ...args], {
|
|
608
|
-
env: safeWranglerEnvironment(),
|
|
609
|
-
cwd
|
|
632
|
+
const result = await withWranglerDirectory((cwd) => execFileAsync(process.execPath, [wranglerEntrypoint, ...args, "--config", join(cwd, "wrangler.json")], {
|
|
633
|
+
env: safeWranglerEnvironment(cwd),
|
|
634
|
+
cwd,
|
|
610
635
|
maxBuffer: 2 * 1024 * 1024,
|
|
611
636
|
timeout: timeoutMs,
|
|
612
|
-
});
|
|
637
|
+
}));
|
|
613
638
|
const parsed = JSON.parse(result.stdout.trim());
|
|
614
639
|
return { value: parsed, failed: false };
|
|
615
640
|
}
|
|
@@ -635,7 +660,7 @@ function jsonCollection(value) {
|
|
|
635
660
|
}
|
|
636
661
|
return undefined;
|
|
637
662
|
}
|
|
638
|
-
function exactContainerEntries(value, name) {
|
|
663
|
+
function exactContainerEntries(value, name, complete = false) {
|
|
639
664
|
const collection = jsonCollection(value);
|
|
640
665
|
if (collection === undefined)
|
|
641
666
|
return undefined;
|
|
@@ -655,7 +680,8 @@ function exactContainerEntries(value, name) {
|
|
|
655
680
|
});
|
|
656
681
|
}
|
|
657
682
|
}
|
|
658
|
-
|
|
683
|
+
// Without proven pagination, a full page that lacks the name proves nothing.
|
|
684
|
+
if (entries.length === 0 && !complete && collection.length >= 100) {
|
|
659
685
|
return undefined;
|
|
660
686
|
}
|
|
661
687
|
return entries;
|
|
@@ -684,16 +710,25 @@ function authenticatedAccountId(value) {
|
|
|
684
710
|
const account = accounts[0];
|
|
685
711
|
if (typeof account !== "object" ||
|
|
686
712
|
account === null ||
|
|
713
|
+
!("id" in account) ||
|
|
687
714
|
typeof account.id !== "string") {
|
|
688
715
|
return undefined;
|
|
689
716
|
}
|
|
717
|
+
const selectedAccount = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
718
|
+
if (selectedAccount && selectedAccount !== account.id) {
|
|
719
|
+
return undefined;
|
|
720
|
+
}
|
|
690
721
|
return account.id;
|
|
691
722
|
}
|
|
692
723
|
async function readWranglerOAuthToken() {
|
|
693
|
-
const
|
|
724
|
+
const legacyRoot = join(homedir(), ".wrangler");
|
|
725
|
+
const legacyExists = (await lstat(legacyRoot).catch(() => undefined))?.isDirectory();
|
|
726
|
+
const configHome = process.env.XDG_CONFIG_HOME ??
|
|
694
727
|
(process.platform === "darwin"
|
|
695
|
-
? join(homedir(), "Library", "Preferences"
|
|
696
|
-
: join(homedir(), ".config"
|
|
728
|
+
? join(homedir(), "Library", "Preferences")
|
|
729
|
+
: join(homedir(), ".config"));
|
|
730
|
+
const root = process.env.WRANGLER_HOME ??
|
|
731
|
+
(legacyExists ? legacyRoot : join(configHome, ".wrangler"));
|
|
697
732
|
const raw = await readFile(join(root, "config", "default.toml"), "utf8")
|
|
698
733
|
.catch(() => undefined);
|
|
699
734
|
if (raw === undefined)
|
|
@@ -743,17 +778,71 @@ async function readWorkersDevSubdomain(accountId) {
|
|
|
743
778
|
export function exactTargetContainerEntries(value, name) {
|
|
744
779
|
return exactContainerEntries(value, boundedResourceName(name, "container"));
|
|
745
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
|
+
}
|
|
746
837
|
async function readExactTargetInventory(workerName, containerApp) {
|
|
747
|
-
const containers = await
|
|
748
|
-
"containers", "list", "--json", "--per-page", "100",
|
|
749
|
-
]);
|
|
838
|
+
const containers = await readTargetContainers(containerApp);
|
|
750
839
|
const deployments = await readWranglerJson([
|
|
751
840
|
"deployments", "list", "--name", workerName, "--json",
|
|
752
841
|
]);
|
|
753
842
|
return {
|
|
754
843
|
containers,
|
|
755
844
|
deployments,
|
|
756
|
-
containerEntries: exactContainerEntries(containers.value, containerApp),
|
|
845
|
+
containerEntries: containers.failed ? undefined : exactContainerEntries(containers.value, containerApp, true),
|
|
757
846
|
deploymentEntries: exactDeploymentEntries(deployments.value),
|
|
758
847
|
};
|
|
759
848
|
}
|
|
@@ -883,6 +972,11 @@ async function readProviderEvidence(workerName, accountId, workerPresent, contai
|
|
|
883
972
|
if (typeof deploymentBinding?.text === "string") {
|
|
884
973
|
evidence.workerDeploymentId = deploymentBinding.text;
|
|
885
974
|
}
|
|
975
|
+
const releaseBinding = bindings.find((value) => value.type === "plain_text" &&
|
|
976
|
+
value.name === "EDEN_EVE_RELEASE_ID");
|
|
977
|
+
if (typeof releaseBinding?.text === "string") {
|
|
978
|
+
evidence.workerReleaseId = releaseBinding.text;
|
|
979
|
+
}
|
|
886
980
|
const containerBinding = bindings.find((value) => value.type === "durable_object_namespace" &&
|
|
887
981
|
value.name === "EVE_CONTAINER");
|
|
888
982
|
if (typeof containerBinding?.namespace_id === "string") {
|
|
@@ -901,12 +995,9 @@ async function readProviderEvidence(workerName, accountId, workerPresent, contai
|
|
|
901
995
|
return evidence;
|
|
902
996
|
}
|
|
903
997
|
/**
|
|
904
|
-
* Resolves
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
* Eden deployed — a same-name Worker recreated by something else after the
|
|
908
|
-
* target was destroyed (or a forged local record) must never be upgraded to
|
|
909
|
-
* `owned`.
|
|
998
|
+
* Resolves local deployment history against authenticated provider state.
|
|
999
|
+
* This detects name reuse and stale records, but deployment IDs are public:
|
|
1000
|
+
* a malicious forged local record is not an independent ownership proof.
|
|
910
1001
|
*/
|
|
911
1002
|
async function resolveEveTargetOwnership(request, result) {
|
|
912
1003
|
if (result.target.state !== "unowned")
|
|
@@ -930,10 +1021,18 @@ async function resolveEveTargetOwnership(request, result) {
|
|
|
930
1021
|
// its own settings: unreadable settings fail closed.
|
|
931
1022
|
const evidence = result.target.providerEvidence;
|
|
932
1023
|
const recordedDeploymentIds = new Set(record.deploymentIds);
|
|
1024
|
+
// Every Eden project on the same Eve version publishes the same affinity
|
|
1025
|
+
// deployment ID, so the per-publish release ID is what tells a same-name
|
|
1026
|
+
// Worker from another project apart. Workers from before release IDs
|
|
1027
|
+
// existed used the release ID as their deployment ID.
|
|
1028
|
+
const recordedReleaseIds = new Set(record.releaseIds);
|
|
1029
|
+
const workerReleaseId = evidence?.workerReleaseId ?? evidence?.workerDeploymentId;
|
|
933
1030
|
const workerProven = result.target.observed?.worker !== true ||
|
|
934
1031
|
(evidence?.settingsReadable === true &&
|
|
935
1032
|
evidence.workerDeploymentId !== undefined &&
|
|
936
|
-
recordedDeploymentIds.has(evidence.workerDeploymentId)
|
|
1033
|
+
recordedDeploymentIds.has(evidence.workerDeploymentId) &&
|
|
1034
|
+
workerReleaseId !== undefined &&
|
|
1035
|
+
recordedReleaseIds.has(workerReleaseId));
|
|
937
1036
|
// The Container proof is the application running the durable_object
|
|
938
1037
|
// scheduling policy with a namespace that matches the Worker binding's
|
|
939
1038
|
// namespace; anything else fails closed.
|
|
@@ -954,6 +1053,8 @@ async function resolveEveTargetOwnership(request, result) {
|
|
|
954
1053
|
failures.push("Worker deployment stamp absent");
|
|
955
1054
|
else if (!recordedDeploymentIds.has(evidence.workerDeploymentId))
|
|
956
1055
|
failures.push("Worker deployment stamp differs from recorded identities");
|
|
1056
|
+
else if (workerReleaseId === undefined || !recordedReleaseIds.has(workerReleaseId))
|
|
1057
|
+
failures.push("Worker release stamp differs from recorded releases");
|
|
957
1058
|
}
|
|
958
1059
|
if (result.target.observed?.container === true) {
|
|
959
1060
|
if (evidence?.containerSchedulingPolicy !== "durable_object")
|
|
@@ -1252,11 +1353,14 @@ async function writeEveDeploymentArtifacts(candidate, hostConfig, workerSource)
|
|
|
1252
1353
|
const WRANGLER_OPERATION_TIMEOUT_MS = 120_000;
|
|
1253
1354
|
/** Deadline for `wrangler deploy`, whose Container image preparation alone can approach two minutes. */
|
|
1254
1355
|
const WRANGLER_PUBLICATION_TIMEOUT_MS = 600_000;
|
|
1255
|
-
function runWranglerWithInput(args,
|
|
1256
|
-
|
|
1257
|
-
|
|
1356
|
+
async function runWranglerWithInput(args, input, timeoutMs = WRANGLER_OPERATION_TIMEOUT_MS) {
|
|
1357
|
+
// The project's TypeScript lib predates Promise.withResolvers.
|
|
1358
|
+
return withWranglerDirectory((cwd) => new Promise((resolveResult) => {
|
|
1359
|
+
const child = spawn(process.execPath, [wranglerEntrypoint, ...args, ...(args.includes("--config")
|
|
1360
|
+
? []
|
|
1361
|
+
: ["--config", join(cwd, "wrangler.json")])], {
|
|
1258
1362
|
cwd,
|
|
1259
|
-
env: safeWranglerEnvironment(),
|
|
1363
|
+
env: safeWranglerEnvironment(cwd),
|
|
1260
1364
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1261
1365
|
});
|
|
1262
1366
|
let stdout = "";
|
|
@@ -1298,7 +1402,7 @@ function runWranglerWithInput(args, cwd, input, timeoutMs = WRANGLER_OPERATION_T
|
|
|
1298
1402
|
child.stdin.end();
|
|
1299
1403
|
else
|
|
1300
1404
|
child.stdin.end(input);
|
|
1301
|
-
});
|
|
1405
|
+
}));
|
|
1302
1406
|
}
|
|
1303
1407
|
function defaultProtectedStore(workerName) {
|
|
1304
1408
|
return {
|
|
@@ -1310,7 +1414,7 @@ function defaultProtectedStore(workerName) {
|
|
|
1310
1414
|
name,
|
|
1311
1415
|
"--name",
|
|
1312
1416
|
workerName,
|
|
1313
|
-
],
|
|
1417
|
+
], request.values[name] === undefined ? "" : `${request.values[name]}\n`);
|
|
1314
1418
|
if (result.exitCode !== 0) {
|
|
1315
1419
|
throw new EveRuntimeConfigError({
|
|
1316
1420
|
code: "EVE_RUNTIME_PROTECTED_UPLOAD_FAILED",
|
|
@@ -1332,7 +1436,7 @@ async function defaultImagePublicationRunner(request) {
|
|
|
1332
1436
|
cwd: request.generationRoot,
|
|
1333
1437
|
maxBuffer: 512 * 1024,
|
|
1334
1438
|
});
|
|
1335
|
-
const pushed = await runWranglerWithInput(["containers", "push", request.localImageTag]
|
|
1439
|
+
const pushed = await runWranglerWithInput(["containers", "push", request.localImageTag]);
|
|
1336
1440
|
if (pushed.exitCode !== 0) {
|
|
1337
1441
|
return {
|
|
1338
1442
|
status: "indeterminate",
|
|
@@ -1398,7 +1502,7 @@ function defaultPublicationRunner() {
|
|
|
1398
1502
|
"--env",
|
|
1399
1503
|
request.identity.environment,
|
|
1400
1504
|
"--strict",
|
|
1401
|
-
],
|
|
1505
|
+
], undefined, WRANGLER_PUBLICATION_TIMEOUT_MS);
|
|
1402
1506
|
if (result.exitCode !== 0) {
|
|
1403
1507
|
return {
|
|
1404
1508
|
status: "indeterminate",
|
|
@@ -1795,15 +1899,32 @@ async function runEveDeployment(request, options, collected) {
|
|
|
1795
1899
|
...(priorIdentity.runtimeRevisionHandle === undefined ? {} : { runtimeRevisionHandle: priorIdentity.runtimeRevisionHandle }),
|
|
1796
1900
|
};
|
|
1797
1901
|
}
|
|
1798
|
-
const matchingDeployment = Object.entries(retainedDeployments).find(([, entry]) => entry.eveVersion === candidate.eveVersion && entry.affinityContract === EDEN_AFFINITY_CONTRACT)?.[0];
|
|
1902
|
+
const matchingDeployment = Object.entries(retainedDeployments).find(([, entry]) => entry.worldRpcAuth === true && entry.eveVersion === candidate.eveVersion && entry.affinityContract === EDEN_AFFINITY_CONTRACT)?.[0];
|
|
1903
|
+
const affinityDeploymentId = `eden-affinity-${createHash("sha256").update(`${candidate.eveVersion}:${EDEN_AFFINITY_CONTRACT}`).digest("hex").slice(0, 24)}`;
|
|
1799
1904
|
const deploymentId = !affinityEnabled ? releaseId : matchingDeployment ??
|
|
1800
|
-
|
|
1905
|
+
(affinityDeploymentId in retainedDeployments ? `${affinityDeploymentId}-${randomUUID().slice(0, 8)}` : affinityDeploymentId);
|
|
1801
1906
|
const retiringDeployments = { ...priorIdentity?.pendingRetirements };
|
|
1802
1907
|
delete retiringDeployments[deploymentId];
|
|
1803
1908
|
if (priorIdentity?.deploymentId !== undefined)
|
|
1804
1909
|
delete retiringDeployments[priorIdentity.deploymentId];
|
|
1805
1910
|
for (const id of prior?.protectedDeploymentIds ?? [])
|
|
1806
1911
|
delete retiringDeployments[id];
|
|
1912
|
+
// Old containers can access every session's World without authentication.
|
|
1913
|
+
// Drop their routes even when live sessions or transfers would prevent drain.
|
|
1914
|
+
const insecureRetirements = affinityEnabled
|
|
1915
|
+
? Object.entries(retainedDeployments).filter(([id, entry]) => id !== deploymentId && entry.worldRpcAuth !== true)
|
|
1916
|
+
: [];
|
|
1917
|
+
for (const [id, entry] of insecureRetirements) {
|
|
1918
|
+
retiringDeployments[id] = entry;
|
|
1919
|
+
delete retainedDeployments[id];
|
|
1920
|
+
}
|
|
1921
|
+
if (insecureRetirements.length > 0) {
|
|
1922
|
+
resultWarnings.push({
|
|
1923
|
+
id: "EVE_UNAUTHENTICATED_VERSIONS_RETIRED",
|
|
1924
|
+
message: `${insecureRetirements.length} kept version${insecureRetirements.length === 1 ? " was" : "s were"} retired because World RPC was unauthenticated; their sessions ended.`,
|
|
1925
|
+
remediation: 'Old sessions now fail; Eve may report "Failed to send the session message." Start new sessions on the current deployment.',
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1807
1928
|
const hasRetainedNoncurrentDeployments = priorIdentity?.retainedDeployments !== undefined &&
|
|
1808
1929
|
Object.keys(retainedDeployments).some((id) => id !== priorIdentity.deploymentId);
|
|
1809
1930
|
const controlToken = `${randomUUID()}${randomUUID()}`;
|
|
@@ -2256,10 +2377,11 @@ async function runEveDeployment(request, options, collected) {
|
|
|
2256
2377
|
}
|
|
2257
2378
|
completedRetirements.push(id);
|
|
2258
2379
|
}
|
|
2259
|
-
supersededImageCleanup = check("VAL-LIFE-007", "passed", "
|
|
2380
|
+
supersededImageCleanup = check("VAL-LIFE-007", "passed", "Retired deployment instances and their owned registry repositories were removed.");
|
|
2260
2381
|
}
|
|
2261
|
-
catch {
|
|
2262
|
-
|
|
2382
|
+
catch (error) {
|
|
2383
|
+
const reason = error instanceof Error ? safeText(error.message) : "Unknown retirement failure.";
|
|
2384
|
+
supersededImageCleanup = check("VAL-LIFE-007", "failed", `The deployment is healthy, but resource retirement was not proven: ${reason} Exact pending retirement records were retained for retry.`, "Retry deploy to finish only recorded retired resource cleanup.");
|
|
2263
2385
|
}
|
|
2264
2386
|
if (completedRetirements.length > 0) {
|
|
2265
2387
|
// Keep the immutable deployment record as evidence, but exclude proven
|
|
@@ -2959,6 +3081,7 @@ async function readEveDeploymentRecord(projectRoot, environment, name, accountId
|
|
|
2959
3081
|
const entries = await readdir(generationsRoot).catch(() => []);
|
|
2960
3082
|
const scanned = [];
|
|
2961
3083
|
const deploymentIds = new Set();
|
|
3084
|
+
const releaseIds = new Set();
|
|
2962
3085
|
for (const entry of entries) {
|
|
2963
3086
|
const generationRoot = join(generationsRoot, entry);
|
|
2964
3087
|
const recordPath = await deploymentRecordPath(generationRoot);
|
|
@@ -2981,6 +3104,9 @@ async function readEveDeploymentRecord(projectRoot, environment, name, accountId
|
|
|
2981
3104
|
const deploymentId = parsed.deploymentId;
|
|
2982
3105
|
if (deploymentId !== undefined)
|
|
2983
3106
|
deploymentIds.add(deploymentId);
|
|
3107
|
+
const releaseId = parsed.identity.releaseId ?? deploymentId;
|
|
3108
|
+
if (releaseId !== undefined)
|
|
3109
|
+
releaseIds.add(releaseId);
|
|
2984
3110
|
}
|
|
2985
3111
|
}
|
|
2986
3112
|
scanned.sort((left, right) => right.modifiedAt - left.modifiedAt);
|
|
@@ -3037,6 +3163,7 @@ async function readEveDeploymentRecord(projectRoot, environment, name, accountId
|
|
|
3037
3163
|
protectedDeploymentIds: [...protectedDeploymentIds],
|
|
3038
3164
|
status: live.record.status,
|
|
3039
3165
|
deploymentIds: [...deploymentIds],
|
|
3166
|
+
releaseIds: [...releaseIds],
|
|
3040
3167
|
worldCloudflare: live.record.worldCloudflare,
|
|
3041
3168
|
durableObjects: historySource?.record.durableObjects,
|
|
3042
3169
|
};
|
|
@@ -3137,10 +3264,12 @@ async function parseEveDeploymentRecord(recordPath, environment, name) {
|
|
|
3137
3264
|
}
|
|
3138
3265
|
async function defaultDestroyTargetRead(request) {
|
|
3139
3266
|
const account = await readWranglerJson(["whoami", "--json"]);
|
|
3140
|
-
const { deployments, containerEntries: entries, deploymentEntries } = await readExactTargetInventory(request.workerName, request.containerApplicationName);
|
|
3267
|
+
const { containers, deployments, containerEntries: entries, deploymentEntries: listedDeployments } = await readExactTargetInventory(request.workerName, request.containerApplicationName);
|
|
3141
3268
|
const accountId = authenticatedAccountId(account.value);
|
|
3142
|
-
const
|
|
3143
|
-
(
|
|
3269
|
+
const deploymentEntries = deployments.failed &&
|
|
3270
|
+
/(?:does not exist|not found|10007)/iu.test(deployments.stderr ?? "")
|
|
3271
|
+
? [] : deployments.failed ? undefined : listedDeployments;
|
|
3272
|
+
const workerExists = (deploymentEntries?.length ?? 0) > 0;
|
|
3144
3273
|
const firstContainer = entries === undefined ? undefined : entries[0];
|
|
3145
3274
|
const ownedRepositories = request.registryRepositories;
|
|
3146
3275
|
let registryTagsPresent;
|
|
@@ -3165,6 +3294,10 @@ async function defaultDestroyTargetRead(request) {
|
|
|
3165
3294
|
}
|
|
3166
3295
|
return {
|
|
3167
3296
|
workerExists,
|
|
3297
|
+
inventoryComplete: !account.failed && accountId !== undefined &&
|
|
3298
|
+
!containers.failed && entries !== undefined && entries.length <= 1 &&
|
|
3299
|
+
deploymentEntries !== undefined,
|
|
3300
|
+
providerEvidence: await readProviderEvidence(request.workerName, accountId, workerExists, firstContainer),
|
|
3168
3301
|
...(firstContainer === undefined
|
|
3169
3302
|
? {}
|
|
3170
3303
|
: { containerApplicationId: firstContainer.id }),
|
|
@@ -3204,7 +3337,7 @@ function registryTagsByRepository(value) {
|
|
|
3204
3337
|
return tagsByRepository;
|
|
3205
3338
|
}
|
|
3206
3339
|
async function defaultWorkerDelete(request) {
|
|
3207
|
-
const result = await runWranglerWithInput(["delete", "--name", request.name, "--force"],
|
|
3340
|
+
const result = await runWranglerWithInput(["delete", "--name", request.name, "--force"], "y\n");
|
|
3208
3341
|
if (result.exitCode === 0)
|
|
3209
3342
|
return "deleted";
|
|
3210
3343
|
if (/(?:does not exist|not found|10007)/iu.test(result.stderr)) {
|
|
@@ -3213,7 +3346,7 @@ async function defaultWorkerDelete(request) {
|
|
|
3213
3346
|
return "indeterminate";
|
|
3214
3347
|
}
|
|
3215
3348
|
async function defaultContainerDelete(request) {
|
|
3216
|
-
const result = await runWranglerWithInput(["containers", "delete", request.applicationId]
|
|
3349
|
+
const result = await runWranglerWithInput(["containers", "delete", request.applicationId]);
|
|
3217
3350
|
if (result.exitCode === 0)
|
|
3218
3351
|
return "deleted";
|
|
3219
3352
|
if (/(?:does not exist|not found|not supported|10007)/iu.test(result.stderr)) {
|
|
@@ -3256,7 +3389,7 @@ async function defaultRegistryRepositoryDelete(request) {
|
|
|
3256
3389
|
"delete",
|
|
3257
3390
|
`${request.repository}:${tag}`,
|
|
3258
3391
|
"--skip-confirmation",
|
|
3259
|
-
]
|
|
3392
|
+
]);
|
|
3260
3393
|
if (result.exitCode === 0) {
|
|
3261
3394
|
deletedAny = true;
|
|
3262
3395
|
continue;
|
|
@@ -3421,6 +3554,7 @@ async function runEveDestroyLocked(request, options, checks, emit, fail) {
|
|
|
3421
3554
|
const expectedWorker = record.identity.workerName;
|
|
3422
3555
|
const expectedContainer = record.identity.containerApplicationName;
|
|
3423
3556
|
if (expectedWorker !== request.name ||
|
|
3557
|
+
expectedContainer !== boundedResourceName(request.name, "container") ||
|
|
3424
3558
|
typeof record.identity.accountId !== "string") {
|
|
3425
3559
|
fail("EVE_DESTROY_RECORD_MISMATCH", "The immutable deployment record does not match the selected target.");
|
|
3426
3560
|
}
|
|
@@ -3435,6 +3569,9 @@ async function runEveDestroyLocked(request, options, checks, emit, fail) {
|
|
|
3435
3569
|
const reader = options.destroyCloudflareRead ?? defaultDestroyTargetRead;
|
|
3436
3570
|
eveProgress(options, "reading the exact remote inventory");
|
|
3437
3571
|
const before = await readTarget();
|
|
3572
|
+
if (before.inventoryComplete === false) {
|
|
3573
|
+
fail("EVE_DESTROY_INDETERMINATE", "The exact remote inventory could not be read completely; nothing was deleted. Retry the read. If Wrangler returned a full Container page without the exact target, inspect and delete only the named Container application in the Cloudflare dashboard, then retry destroy.");
|
|
3574
|
+
}
|
|
3438
3575
|
if (before.accountId !== record.identity.accountId) {
|
|
3439
3576
|
fail("EVE_DESTROY_OWNERSHIP_UNPROVEN", "The authenticated account does not own the recorded deployment account.");
|
|
3440
3577
|
}
|
|
@@ -3455,6 +3592,27 @@ async function runEveDestroyLocked(request, options, checks, emit, fail) {
|
|
|
3455
3592
|
});
|
|
3456
3593
|
return;
|
|
3457
3594
|
}
|
|
3595
|
+
if (before.workerExists) {
|
|
3596
|
+
const ownership = await resolveEveTargetOwnership({
|
|
3597
|
+
projectRoot: request.projectRoot,
|
|
3598
|
+
environment: request.environment,
|
|
3599
|
+
name: request.name,
|
|
3600
|
+
projectId: record.identity.projectId ?? "unknown-project",
|
|
3601
|
+
sourceDigest: "unknown",
|
|
3602
|
+
}, {
|
|
3603
|
+
accountAccess: "available",
|
|
3604
|
+
containerAccess: "available",
|
|
3605
|
+
accountId: before.accountId,
|
|
3606
|
+
target: {
|
|
3607
|
+
state: "unowned",
|
|
3608
|
+
observed: { worker: true, container: before.containerApplicationId !== undefined },
|
|
3609
|
+
...(before.providerEvidence === undefined ? {} : { providerEvidence: before.providerEvidence }),
|
|
3610
|
+
},
|
|
3611
|
+
});
|
|
3612
|
+
if (ownership.target.state !== "owned") {
|
|
3613
|
+
fail("EVE_DESTROY_OWNERSHIP_UNPROVEN", "Live provider settings do not match the recorded Worker and Container identity; nothing was deleted.");
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3458
3616
|
const deleteWorker = options.deleteWorker ?? defaultWorkerDelete;
|
|
3459
3617
|
if (before.workerExists) {
|
|
3460
3618
|
eveProgress(options, `deleting Worker ${expectedWorker}`);
|
|
@@ -3519,6 +3677,9 @@ async function runEveDestroyLocked(request, options, checks, emit, fail) {
|
|
|
3519
3677
|
}
|
|
3520
3678
|
eveProgress(options, "verifying the exact target is absent");
|
|
3521
3679
|
const after = await readTarget();
|
|
3680
|
+
if (after.inventoryComplete === false) {
|
|
3681
|
+
fail("EVE_DESTROY_INDETERMINATE", "The exact remote inventory could not be read completely after deletion; absence is unproven.");
|
|
3682
|
+
}
|
|
3522
3683
|
if (after.workerExists || after.containerApplicationId !== undefined) {
|
|
3523
3684
|
emit({
|
|
3524
3685
|
ok: false,
|
package/dist/eve-packaging.js
CHANGED
|
@@ -363,8 +363,20 @@ 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",
|
|
369
|
+
".dev.vars",
|
|
370
|
+
".netrc",
|
|
371
|
+
".git-credentials",
|
|
372
|
+
"id_rsa",
|
|
373
|
+
"id_ecdsa",
|
|
374
|
+
"id_ed25519",
|
|
375
|
+
"id_dsa",
|
|
376
|
+
"id_ecdsa_sk",
|
|
377
|
+
"id_ed25519_sk",
|
|
378
|
+
"id_xmss",
|
|
379
|
+
"secrets.json",
|
|
368
380
|
"credentials.json",
|
|
369
381
|
"service-account.json",
|
|
370
382
|
]);
|
|
@@ -419,6 +431,24 @@ function isAllowedSystemAlias(path) {
|
|
|
419
431
|
const normalized = resolve(path);
|
|
420
432
|
return normalized === "/var" || normalized === "/tmp";
|
|
421
433
|
}
|
|
434
|
+
function isEnvironmentFile(name) {
|
|
435
|
+
const normalizedName = name.toLowerCase();
|
|
436
|
+
return normalizedName.startsWith(".env") || normalizedName.endsWith(".env");
|
|
437
|
+
}
|
|
438
|
+
function isCredentialPath(relativePath) {
|
|
439
|
+
const parts = relativePath.split("/");
|
|
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) ||
|
|
446
|
+
name.startsWith(".dev.vars.") ||
|
|
447
|
+
name.endsWith(".pem") || name.endsWith(".key") ||
|
|
448
|
+
name.endsWith(".p12") || name.endsWith(".pfx") ||
|
|
449
|
+
name.endsWith(".tfvars") ||
|
|
450
|
+
(name === "credentials" && parts[parts.length - 2]?.toLowerCase() === ".aws"));
|
|
451
|
+
}
|
|
422
452
|
function isPathExcluded(relativePath, runtimeEnvRelativePath) {
|
|
423
453
|
const parts = relativePath.split("/");
|
|
424
454
|
const name = parts[parts.length - 1];
|
|
@@ -437,11 +467,10 @@ function isPathExcluded(relativePath, runtimeEnvRelativePath) {
|
|
|
437
467
|
if (runtimeEnvRelativePath !== undefined && relativePath === runtimeEnvRelativePath) {
|
|
438
468
|
return { excluded: true, category: "runtime-env" };
|
|
439
469
|
}
|
|
440
|
-
if (name !== undefined && name
|
|
470
|
+
if (name !== undefined && isEnvironmentFile(name)) {
|
|
441
471
|
return { excluded: true, category: "runtime-env" };
|
|
442
472
|
}
|
|
443
|
-
if ((
|
|
444
|
-
(name !== undefined && (name.endsWith(".pem") || name.endsWith(".key")))) {
|
|
473
|
+
if (isCredentialPath(relativePath)) {
|
|
445
474
|
return { excluded: true, category: "credentials" };
|
|
446
475
|
}
|
|
447
476
|
if (name !== undefined &&
|
|
@@ -1736,10 +1765,8 @@ async function assertGeneratedTreesExcludeSecrets(snapshotRoot) {
|
|
|
1736
1765
|
? entry.name
|
|
1737
1766
|
: `${relativeDirectory}/${entry.name}`;
|
|
1738
1767
|
const candidate = join(directory, entry.name);
|
|
1739
|
-
if (entry.name
|
|
1740
|
-
|
|
1741
|
-
entry.name.endsWith(".pem") ||
|
|
1742
|
-
entry.name.endsWith(".key")) {
|
|
1768
|
+
if (isEnvironmentFile(entry.name) ||
|
|
1769
|
+
isCredentialPath(relativePath)) {
|
|
1743
1770
|
throw new EvePackagingError({
|
|
1744
1771
|
code: "SECRET_EXCLUSION_FAILED",
|
|
1745
1772
|
subject: relativePath,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const EVE_RESERVED_HOST_VARIABLES: readonly ["HOST", "NITRO_HOST", "PORT", "NITRO_PORT", "NODE_ENV", "NODE_EXTRA_CA_CERTS", "WORKFLOW_LOCAL_BASE_URL", "EDEN_WORLD_URL", "EDEN_WORLD_TOKEN", "EDEN_EVE_WORLD_RPC_AUTH", "EDEN_EVE_DEPLOYMENT_ID", "EDEN_EVE_GENERATION_ID", "EDEN_EVE_RELEASE_ID", "EDEN_EVE_DEPLOYMENTS", "EDEN_AFFINITY_CONTRACT", "EVE_SCHEDULES_DISABLED", "EDEN_EVE_CONTROL_TOKEN"];
|
|
1
|
+
export declare const EVE_RESERVED_HOST_VARIABLES: readonly ["HOST", "NITRO_HOST", "PORT", "NITRO_PORT", "NODE_ENV", "EVE_DEV", "VERCEL", "VERCEL_ENV", "VERCEL_TARGET_ENV", "NODE_EXTRA_CA_CERTS", "WORKFLOW_LOCAL_BASE_URL", "EDEN_WORLD_URL", "EDEN_WORLD_TOKEN", "EDEN_EVE_WORLD_RPC_AUTH", "EDEN_EVE_DEPLOYMENT_ID", "EDEN_EVE_GENERATION_ID", "EDEN_EVE_RELEASE_ID", "EDEN_EVE_DEPLOYMENTS", "EDEN_AFFINITY_CONTRACT", "EVE_SCHEDULES_DISABLED", "EDEN_EVE_CONTROL_TOKEN"];
|
|
2
2
|
export type EveReservedHostVariable = (typeof EVE_RESERVED_HOST_VARIABLES)[number];
|
|
3
3
|
export declare const EVE_START_COMMAND: readonly ["./node_modules/.bin/eve", "start", "--host", "0.0.0.0", "--port", "8080"];
|
|
4
4
|
export declare const EVE_HOST_ENVIRONMENT: Readonly<{
|
|
@@ -7,6 +7,10 @@ export const EVE_RESERVED_HOST_VARIABLES = Object.freeze([
|
|
|
7
7
|
"PORT",
|
|
8
8
|
"NITRO_PORT",
|
|
9
9
|
"NODE_ENV",
|
|
10
|
+
"EVE_DEV",
|
|
11
|
+
"VERCEL",
|
|
12
|
+
"VERCEL_ENV",
|
|
13
|
+
"VERCEL_TARGET_ENV",
|
|
10
14
|
"NODE_EXTRA_CA_CERTS",
|
|
11
15
|
"WORKFLOW_LOCAL_BASE_URL",
|
|
12
16
|
"EDEN_WORLD_URL",
|
|
@@ -191,10 +195,14 @@ function parseEnvironmentText(text) {
|
|
|
191
195
|
variableName: name,
|
|
192
196
|
});
|
|
193
197
|
}
|
|
194
|
-
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)) {
|
|
195
200
|
throw new EveRuntimeConfigError({
|
|
196
201
|
code: "EVE_ENV_RESERVED_VARIABLE",
|
|
197
|
-
message: `The explicit Eve environment file attempts to override reserved host variable ${name}
|
|
202
|
+
message: `The explicit Eve environment file attempts to override reserved host variable ${name}.` +
|
|
203
|
+
(["EVE_DEV", "VERCEL", "VERCEL_ENV", "VERCEL_TARGET_ENV"].includes(name)
|
|
204
|
+
? " Remove it: Eve development-mode variables can let unauthenticated callers in."
|
|
205
|
+
: ""),
|
|
198
206
|
source: EVE_ENV_FILE_SOURCE,
|
|
199
207
|
variableName: name,
|
|
200
208
|
});
|
|
@@ -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: { ...
|
|
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
|
}
|
|
@@ -1000,7 +1010,7 @@ export async function buildEveRuntimeImage(request) {
|
|
|
1000
1010
|
"--label",
|
|
1001
1011
|
state.imageLabel,
|
|
1002
1012
|
"--publish",
|
|
1003
|
-
|
|
1013
|
+
`127.0.0.1:${healthPort}:${INTERNAL_PORT}`,
|
|
1004
1014
|
...(request.runtimeInjection === undefined
|
|
1005
1015
|
? Object.entries(runtimeEnvironment).flatMap(([name, value]) => [
|
|
1006
1016
|
"--env",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moinulmoin/eden",
|
|
3
|
-
"version": "0.11.
|
|
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.
|
|
35
|
+
"@moinulmoin/eden-runtime-cloudflare": "0.11.3",
|
|
36
36
|
"wrangler": "4.147.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|