Skip to content

Commit fd4fdea

Browse files
committed
feat: downgrade node version to 18
1 parent ab9fa89 commit fd4fdea

7 files changed

Lines changed: 86 additions & 25 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@openagentpack/sdk": patch
3+
---
4+
5+
Lower the SDK `engines` floor from Node `>=22` to `>=18.17.0`.
6+
7+
The SDK runtime only relies on APIs available since Node 18.17 (`node:fs`/`path`/`crypto`/`os`, global `fetch`/`FormData`, `structuredClone`, web streams), so the previous floor was a repository baseline rather than a runtime requirement. The tsup build target is pinned to `es2022` so emitted syntax stays parseable on the new floor, and CI now runs an SDK-only packed-install smoke on Node 18 and 20 (`smoke-packed.ts --sdk-only`) to enforce the contract. The `@openagentpack/cli` and `@openagentpack/playground` packages keep their Node `>=22` requirement.

.github/workflows/ci.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,23 @@ jobs:
5252
run: bun scripts/verify.ts full --step "${{ matrix.step }}"
5353

5454
package-compatibility:
55-
name: Package compatibility / Node ${{ matrix.node }}
55+
name: Package compatibility / Node ${{ matrix.node }}${{ matrix.scope == 'sdk' && ' (sdk-only)' || '' }}
5656
runs-on: ubuntu-latest
5757
strategy:
5858
fail-fast: false
5959
matrix:
60-
node: [22, 24]
60+
include:
61+
# Full consumer smoke (sdk + playground + cli) on the repo baseline.
62+
- node: 22
63+
scope: all
64+
- node: 24
65+
scope: all
66+
# SDK-only smoke on the sdk engines floor (>=18.17.0): cli/playground
67+
# require Node >=22, so only the sdk tarball is installed and imported.
68+
- node: 18
69+
scope: sdk
70+
- node: 20
71+
scope: sdk
6172
steps:
6273
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
6374
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
@@ -69,7 +80,10 @@ jobs:
6980
package-manager-cache: false
7081
- run: bun install --frozen-lockfile
7182
- run: bun scripts/verify.ts release --step build-packages
72-
- run: bun scripts/verify.ts release --step package-smoke
83+
- if: matrix.scope == 'all'
84+
run: bun scripts/verify.ts release --step package-smoke
85+
- if: matrix.scope == 'sdk'
86+
run: bun scripts/release/smoke-packed.ts --sdk-only
7387

7488
gate:
7589
name: Gate

packages/sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"url": "https://github.com/modelstudioai/OpenAgentPack/issues"
2121
},
2222
"engines": {
23-
"node": ">=22"
23+
"node": ">=18.17.0"
2424
},
2525
"type": "module",
2626
"main": "./dist/index.js",

packages/sdk/tsup.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ export default defineConfig({
1515
},
1616
},
1717
clean: true,
18-
target: "esnext",
18+
// Keep the emitted syntax parseable by Node 18.17 (the package's engines
19+
// floor): esnext would let future syntax (e.g. `using`) leak into dist.
20+
target: "es2022",
1921
});

scripts/open-source.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,16 @@ describe("open-source repository invariants", () => {
5353
});
5454

5555
test("public packages require a maintained Node.js baseline", () => {
56-
for (const pkg of ["sdk", "playground", "cli"]) {
56+
const nodeBaselines: Record<string, string> = {
57+
sdk: ">=18.17.0",
58+
playground: ">=22",
59+
cli: ">=22",
60+
};
61+
for (const [pkg, expectedNodeRange] of Object.entries(nodeBaselines)) {
5762
const manifest = JSON.parse(readFileSync(resolve(root, `packages/${pkg}/package.json`), "utf8")) as {
5863
engines?: { node?: string };
5964
};
60-
expect(manifest.engines?.node).toBe(">=22");
65+
expect(manifest.engines?.node).toBe(expectedNodeRange);
6166
}
6267
});
6368

scripts/release/smoke-packed.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from "bun:test";
2-
import { packedFilename } from "./smoke-packed.ts";
2+
import { isSdkOnly, packedFilename, smokePackages } from "./smoke-packed.ts";
33

44
describe("npm pack output", () => {
55
test("accepts the npm 10 and 11 array format", () => {
@@ -14,3 +14,19 @@ describe("npm pack output", () => {
1414
expect(() => packedFilename("[]", "sdk")).toThrow("Unexpected npm pack output for sdk");
1515
});
1616
});
17+
18+
describe("--sdk-only mode", () => {
19+
test("detects the flag among CLI arguments", () => {
20+
expect(isSdkOnly(["--sdk-only"])).toBe(true);
21+
expect(isSdkOnly([])).toBe(false);
22+
expect(isSdkOnly(["--verbose"])).toBe(false);
23+
});
24+
25+
test("restricts the package set to sdk when enabled", () => {
26+
expect(smokePackages(true)).toEqual(["sdk"]);
27+
});
28+
29+
test("keeps the full package set when disabled", () => {
30+
expect(smokePackages(false)).toEqual(["sdk", "playground", "cli"]);
31+
});
32+
});

scripts/release/smoke-packed.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
/**
22
* Pack the exact publish manifests, install them as an external npm consumer,
33
* then exercise every public SDK entry point plus the CLI and Playground bins.
4+
*
5+
* `--sdk-only` restricts the run to the SDK package: pack/install only the sdk
6+
* tarball (still with --engine-strict) and execute the SDK entry-point imports.
7+
* This is the mode CI uses on Node versions below the cli/playground engines
8+
* floor (>=22) to enforce the SDK's own >=18.17.0 engines contract.
49
*/
510

611
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
@@ -18,6 +23,14 @@ import {
1823

1924
const root = resolve(import.meta.dirname, "../..");
2025

26+
export function isSdkOnly(argv: readonly string[]): boolean {
27+
return argv.includes("--sdk-only");
28+
}
29+
30+
export function smokePackages(sdkOnly: boolean): readonly (typeof PACKAGES)[number][] {
31+
return sdkOnly ? PACKAGES.filter((pkg) => pkg === "sdk") : PACKAGES;
32+
}
33+
2134
type PackedPackage = { filename: string };
2235

2336
export function packedFilename(raw: string, pkg: string): string {
@@ -33,9 +46,9 @@ function run(command: string[], cwd: string, stdout: "inherit" | "pipe" = "inher
3346
return stdout === "pipe" ? (result.stdout?.toString().trim() ?? "") : "";
3447
}
3548

36-
function packPackages(destination: string): string[] {
49+
function packPackages(destination: string, packages: readonly (typeof PACKAGES)[number][]): string[] {
3750
const versions = workspaceVersions();
38-
return PACKAGES.map((pkg) => {
51+
return packages.map((pkg) => {
3952
const pkgDir = join(root, "packages", pkg);
4053
let originalLicense: string | undefined;
4154
let originalManifest: string | undefined;
@@ -104,6 +117,8 @@ async function smokePlayground(consumer: string): Promise<void> {
104117
}
105118

106119
async function main(): Promise<void> {
120+
const sdkOnly = isSdkOnly(process.argv.slice(2));
121+
const packages = smokePackages(sdkOnly);
107122
const temporaryRoot = mkdtempSync(join(tmpdir(), "openagentpack-consumer-"));
108123
try {
109124
const tarballsDir = join(temporaryRoot, "tarballs");
@@ -112,7 +127,7 @@ async function main(): Promise<void> {
112127
mkdirSync(consumer);
113128
writeFileSync(join(consumer, "package.json"), '{"name":"agents-package-smoke","private":true,"type":"module"}\n');
114129

115-
const tarballs = packPackages(tarballsDir);
130+
const tarballs = packPackages(tarballsDir, packages);
116131
run(
117132
[
118133
"npm",
@@ -126,7 +141,7 @@ async function main(): Promise<void> {
126141
],
127142
consumer,
128143
);
129-
for (const pkg of PACKAGES) {
144+
for (const pkg of packages) {
130145
const license = readFileSync(join(consumer, `node_modules/@openagentpack/${pkg}/LICENSE`), "utf8");
131146
if (license !== readFileSync(join(root, "LICENSE"), "utf8")) {
132147
throw new Error(`${pkg} package is missing the repository license`);
@@ -143,21 +158,23 @@ async function main(): Promise<void> {
143158
consumer,
144159
);
145160

146-
const expectedVersion = JSON.parse(readFileSync(join(root, "packages/cli/package.json"), "utf8")) as {
147-
version: string;
148-
};
149-
const cliVersion = run(
150-
["node", join(consumer, "node_modules/@openagentpack/cli/dist/bin/agents.js"), "--version"],
151-
consumer,
152-
"pipe",
153-
);
154-
if (cliVersion !== expectedVersion.version) {
155-
throw new Error(`Packed CLI version mismatch: expected ${expectedVersion.version}, received ${cliVersion}`);
156-
}
161+
if (!sdkOnly) {
162+
const expectedVersion = JSON.parse(readFileSync(join(root, "packages/cli/package.json"), "utf8")) as {
163+
version: string;
164+
};
165+
const cliVersion = run(
166+
["node", join(consumer, "node_modules/@openagentpack/cli/dist/bin/agents.js"), "--version"],
167+
consumer,
168+
"pipe",
169+
);
170+
if (cliVersion !== expectedVersion.version) {
171+
throw new Error(`Packed CLI version mismatch: expected ${expectedVersion.version}, received ${cliVersion}`);
172+
}
157173

158-
await smokePlayground(consumer);
174+
await smokePlayground(consumer);
175+
}
159176
const nodeVersion = run(["node", "--version"], consumer, "pipe");
160-
console.log(`✓ Packed packages install and run under ${nodeVersion}`);
177+
console.log(`✓ Packed ${sdkOnly ? "SDK package installs" : "packages install"} and run under ${nodeVersion}`);
161178
} finally {
162179
rmSync(temporaryRoot, { recursive: true, force: true });
163180
}

0 commit comments

Comments
 (0)