@foldrun/core 3.0.0 → 4.0.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.
@@ -12,7 +12,13 @@
12
12
  // pip and npm need no root and run here, as the agent, into a per-step
13
13
  // directory already on PATH / PYTHONPATH / NODE_PATH (stepInstallEnv), so the
14
14
  // next command finds what this one installed. Inside a run container all of
15
- // it goes when the container does. Every install is logged with the line to
15
+ // it goes when the container does.
16
+ //
17
+ // No package runs its own code on the way in (FOL-98, 28 Oct 2026): npm
18
+ // installs with --ignore-scripts (no preinstall/postinstall), and pip takes
19
+ // built wheels only (--only-binary :all:) — an sdist's setup.py is code. A
20
+ // package that has no wheel says so, and `runtime: packages:` builds it
21
+ // before the step instead, where the build is declared, cached and seen. Every install is logged with the line to
16
22
  // add to the agent's `runtime:` to have it before the step starts next time.
17
23
  import fs from "node:fs";
18
24
  import net from "node:net";
@@ -86,27 +92,30 @@ export function buildInstallTool(opts) {
86
92
  fs.mkdirSync(target, { recursive: true });
87
93
  const uv = findUv();
88
94
  const res = uv
89
- ? spawnSync(uv, ["pip", "install", "-q", "--python", opts.python, "--target", target, ...names], {
95
+ ? spawnSync(uv, ["pip", "install", "-q", "--only-binary", ":all:", "--python", opts.python, "--target", target, ...names], {
90
96
  env: { ...opts.env, UV_CACHE_DIR: path.join(opts.cacheRoot, ".uv-cache"), UV_LINK_MODE: "copy", UV_NO_PROGRESS: "1" },
91
97
  encoding: "utf8",
92
98
  timeout: 600_000,
93
99
  })
94
- : spawnSync(opts.python, ["-m", "pip", "install", "-q", "--disable-pip-version-check", "--target", target, ...names], {
100
+ : spawnSync(opts.python, ["-m", "pip", "install", "-q", "--disable-pip-version-check", "--only-binary", ":all:", "--target", target, ...names], {
95
101
  env: opts.env,
96
102
  encoding: "utf8",
97
103
  timeout: 600_000,
98
104
  });
99
105
  if (res.status !== 0) {
100
- const msg = summarizeInstallFailure(uv ? "uv pip install" : "pip install", `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? res.error.message : ""}`);
106
+ const raw = `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? res.error.message : ""}`;
107
+ const msg = summarizeInstallFailure(uv ? "uv pip install" : "pip install", raw);
101
108
  opts.emit("error", `install: pip ${names.join(" ")} — ${msg.split("\n")[0]}`);
102
- return text(msg, true);
109
+ // Wheels only: one that exists only as source is not built here.
110
+ const noWheel = /no matching distribution|only-binary|no wheels? (?:with|for)|binary.*not available|doesn't have a wheel|has no wheel/i.test(raw);
111
+ return text(noWheel ? `${msg}\n\nOnly built wheels are installed mid-step — a source package's build runs its own code. Declare it in the agent's runtime: packages: [${names.join(", ")}] to have it built before the step.` : msg, true);
103
112
  }
104
113
  how = `pip, ${Math.round((Date.now() - t0) / 1000)}s`;
105
114
  }
106
115
  else {
107
116
  const prefix = path.join(opts.dir, "npm");
108
117
  fs.mkdirSync(prefix, { recursive: true });
109
- const res = spawnSync("npm", ["install", "--no-fund", "--no-audit", "--no-progress", "--prefix", prefix, ...names], {
118
+ const res = spawnSync("npm", ["install", "--ignore-scripts", "--no-fund", "--no-audit", "--no-progress", "--prefix", prefix, ...names], {
110
119
  env: { ...opts.env, npm_config_cache: path.join(opts.cacheRoot, ".npm-cache") },
111
120
  encoding: "utf8",
112
121
  timeout: 600_000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foldrun/core",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Agents are just folders. The format, the runner, the checks.",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",
@@ -12,7 +12,13 @@
12
12
  // pip and npm need no root and run here, as the agent, into a per-step
13
13
  // directory already on PATH / PYTHONPATH / NODE_PATH (stepInstallEnv), so the
14
14
  // next command finds what this one installed. Inside a run container all of
15
- // it goes when the container does. Every install is logged with the line to
15
+ // it goes when the container does.
16
+ //
17
+ // No package runs its own code on the way in (FOL-98, 28 Oct 2026): npm
18
+ // installs with --ignore-scripts (no preinstall/postinstall), and pip takes
19
+ // built wheels only (--only-binary :all:) — an sdist's setup.py is code. A
20
+ // package that has no wheel says so, and `runtime: packages:` builds it
21
+ // before the step instead, where the build is declared, cached and seen. Every install is logged with the line to
16
22
  // add to the agent's `runtime:` to have it before the step starts next time.
17
23
 
18
24
  import fs from "node:fs";
@@ -106,26 +112,29 @@ export function buildInstallTool(opts: {
106
112
  fs.mkdirSync(target, { recursive: true });
107
113
  const uv = findUv();
108
114
  const res = uv
109
- ? spawnSync(uv, ["pip", "install", "-q", "--python", opts.python, "--target", target, ...names], {
115
+ ? spawnSync(uv, ["pip", "install", "-q", "--only-binary", ":all:", "--python", opts.python, "--target", target, ...names], {
110
116
  env: { ...opts.env, UV_CACHE_DIR: path.join(opts.cacheRoot, ".uv-cache"), UV_LINK_MODE: "copy", UV_NO_PROGRESS: "1" } as unknown as NodeJS.ProcessEnv,
111
117
  encoding: "utf8",
112
118
  timeout: 600_000,
113
119
  })
114
- : spawnSync(opts.python, ["-m", "pip", "install", "-q", "--disable-pip-version-check", "--target", target, ...names], {
120
+ : spawnSync(opts.python, ["-m", "pip", "install", "-q", "--disable-pip-version-check", "--only-binary", ":all:", "--target", target, ...names], {
115
121
  env: opts.env as unknown as NodeJS.ProcessEnv,
116
122
  encoding: "utf8",
117
123
  timeout: 600_000,
118
124
  });
119
125
  if (res.status !== 0) {
120
- const msg = summarizeInstallFailure(uv ? "uv pip install" : "pip install", `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? res.error.message : ""}`);
126
+ const raw = `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? res.error.message : ""}`;
127
+ const msg = summarizeInstallFailure(uv ? "uv pip install" : "pip install", raw);
121
128
  opts.emit("error", `install: pip ${names.join(" ")} — ${msg.split("\n")[0]}`);
122
- return text(msg, true);
129
+ // Wheels only: one that exists only as source is not built here.
130
+ const noWheel = /no matching distribution|only-binary|no wheels? (?:with|for)|binary.*not available|doesn't have a wheel|has no wheel/i.test(raw);
131
+ return text(noWheel ? `${msg}\n\nOnly built wheels are installed mid-step — a source package's build runs its own code. Declare it in the agent's runtime: packages: [${names.join(", ")}] to have it built before the step.` : msg, true);
123
132
  }
124
133
  how = `pip, ${Math.round((Date.now() - t0) / 1000)}s`;
125
134
  } else {
126
135
  const prefix = path.join(opts.dir, "npm");
127
136
  fs.mkdirSync(prefix, { recursive: true });
128
- const res = spawnSync("npm", ["install", "--no-fund", "--no-audit", "--no-progress", "--prefix", prefix, ...names], {
137
+ const res = spawnSync("npm", ["install", "--ignore-scripts", "--no-fund", "--no-audit", "--no-progress", "--prefix", prefix, ...names], {
129
138
  env: { ...opts.env, npm_config_cache: path.join(opts.cacheRoot, ".npm-cache") } as unknown as NodeJS.ProcessEnv,
130
139
  encoding: "utf8",
131
140
  timeout: 600_000,