Skip to content

Commit 5a7fb2f

Browse files
committed
feat: add npx compatibility with Node.js build target
Add build step that compiles TypeScript to Node.js-compatible JavaScript, enabling both `bunx pgai` and `npx pgai` to work. Users without Bun installed can now use the CLI via npx with Node.js runtime. - Add bun build with --target node for Node.js compatibility - Update shebang replacement in build script - Fix path resolution for SQL templates in init.ts - Update pgai wrapper to detect .ts vs .js and use appropriate runtime
1 parent 2d31b13 commit 5a7fb2f

5 files changed

Lines changed: 73 additions & 25 deletions

File tree

cli/lib/init.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,17 @@ export type InitPlan = {
159159
steps: InitStep[];
160160
};
161161

162-
function packageRootDir(): string {
163-
// lib/init.ts -> <pkg>/lib ; package root is ..
164-
return path.resolve(__dirname, "..");
165-
}
166-
167162
function sqlDir(): string {
168-
return path.join(packageRootDir(), "sql");
163+
// Handle both development (lib/init.ts) and production (dist/bin/postgres-ai.js) paths
164+
// Development: lib/ -> ../sql
165+
// Production (bundled): dist/bin/ -> ../sql (copied during build)
166+
const devPath = path.resolve(__dirname, "..", "sql");
167+
const prodPath = path.resolve(__dirname, "..", "sql");
168+
169+
if (fs.existsSync(devPath)) {
170+
return devPath;
171+
}
172+
return prodPath;
169173
}
170174

171175
function loadSqlTemplate(filename: string): string {

cli/package.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "postgresai",
33
"version": "0.0.0-dev.0",
4-
"description": "postgres_ai CLI (Bun)",
4+
"description": "postgres_ai CLI",
55
"license": "Apache-2.0",
66
"private": false,
77
"repository": {
@@ -13,13 +13,19 @@
1313
"url": "https://gitlab.com/postgres-ai/postgres_ai/-/issues"
1414
},
1515
"bin": {
16-
"postgres-ai": "./bin/postgres-ai.ts",
17-
"postgresai": "./bin/postgres-ai.ts",
18-
"pgai": "./bin/postgres-ai.ts"
16+
"postgres-ai": "./dist/bin/postgres-ai.js",
17+
"postgresai": "./dist/bin/postgres-ai.js",
18+
"pgai": "./dist/bin/postgres-ai.js"
1919
},
2020
"type": "module",
21+
"engines": {
22+
"node": ">=18"
23+
},
2124
"scripts": {
25+
"build": "bun build ./bin/postgres-ai.ts --outdir ./dist/bin --target node && sed -i '1s|#!/usr/bin/env bun|#!/usr/bin/env node|' ./dist/bin/postgres-ai.js && cp -r sql dist/",
26+
"prepublishOnly": "npm run build",
2227
"start": "bun ./bin/postgres-ai.ts --help",
28+
"start:node": "node ./dist/bin/postgres-ai.js --help",
2329
"dev": "bun --watch ./bin/postgres-ai.ts",
2430
"test": "bun test",
2531
"typecheck": "bunx tsc --noEmit"

cli/test/init.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ function runCli(args: string[], env: Record<string, string> = {}) {
1818
}
1919

2020
function runPgai(args: string[], env: Record<string, string> = {}) {
21-
const pgaiPath = resolve(import.meta.dir, "..", "..", "pgai", "bin", "pgai.ts");
22-
const result = Bun.spawnSync(["bun", pgaiPath, ...args], {
21+
// For testing, run the CLI directly since pgai is just a thin wrapper
22+
// In production, pgai wrapper will properly resolve and spawn the postgresai CLI
23+
const cliPath = resolve(import.meta.dir, "..", "bin", "postgres-ai.ts");
24+
const result = Bun.spawnSync(["bun", cliPath, ...args], {
2325
env: { ...process.env, ...env },
2426
});
2527
return {

pgai/bin/pgai.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
#!/usr/bin/env bun
22

3+
import { spawn } from "child_process";
34
import { resolve, dirname } from "path";
45
import { existsSync } from "fs";
6+
import { createRequire } from "module";
7+
import { fileURLToPath } from "url";
8+
9+
const __filename = fileURLToPath(import.meta.url);
10+
const __dirname = dirname(__filename);
11+
const require = createRequire(import.meta.url);
512

613
function die(msg: string): never {
714
process.stderr.write(`${msg}\n`);
@@ -12,14 +19,23 @@ let target: string;
1219

1320
// Try to find the postgresai package
1421
try {
15-
// First try to resolve from node_modules
16-
const postgreaiIndex = require.resolve("postgresai");
17-
target = resolve(dirname(postgreaiIndex), "..", "bin", "postgres-ai.ts");
22+
// First try to resolve from node_modules - look for the compiled dist version
23+
const postgresaiPkg = require.resolve("postgresai/package.json");
24+
target = resolve(dirname(postgresaiPkg), "dist", "bin", "postgres-ai.js");
25+
26+
// Fallback to source TS if dist doesn't exist (development)
27+
if (!existsSync(target)) {
28+
target = resolve(dirname(postgresaiPkg), "bin", "postgres-ai.ts");
29+
}
1830
} catch {
1931
// Dev-friendly fallback when running from the monorepo checkout (postgresai lives under ../cli).
20-
const fallback = resolve(import.meta.dir, "..", "..", "cli", "bin", "postgres-ai.ts");
21-
if (existsSync(fallback)) {
22-
target = fallback;
32+
const fallbackJs = resolve(__dirname, "..", "..", "cli", "dist", "bin", "postgres-ai.js");
33+
const fallbackTs = resolve(__dirname, "..", "..", "cli", "bin", "postgres-ai.ts");
34+
35+
if (existsSync(fallbackJs)) {
36+
target = fallbackJs;
37+
} else if (existsSync(fallbackTs)) {
38+
target = fallbackTs;
2339
} else {
2440
die(
2541
[
@@ -31,7 +47,22 @@ try {
3147
}
3248
}
3349

34-
// Import and run the main CLI
35-
const mainModule = await import(target);
50+
// Determine if we should use node or bun based on the file extension
51+
const isTsFile = target.endsWith(".ts");
52+
const runtime = isTsFile ? "bun" : process.execPath;
53+
54+
const child = spawn(runtime, [target, ...process.argv.slice(2)], {
55+
stdio: "inherit",
56+
});
57+
58+
child.on("exit", (code, signal) => {
59+
if (signal) {
60+
process.kill(process.pid, signal);
61+
return;
62+
}
63+
process.exit(code ?? 0);
64+
});
3665

37-
// The CLI parses process.argv and runs automatically, so we don't need to do anything else
66+
child.on("error", (err) => {
67+
die(`pgai: failed to run postgresai: ${err instanceof Error ? err.message : String(err)}`);
68+
});

pgai/package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "pgai",
33
"version": "0.0.0-dev.0",
4-
"description": "Thin wrapper for postgresai CLI (provides `bunx pgai ...`)",
4+
"description": "Thin wrapper for postgresai CLI (provides `npx pgai ...` or `bunx pgai ...`)",
55
"license": "Apache-2.0",
66
"private": false,
77
"repository": {
@@ -13,15 +13,20 @@
1313
"url": "https://gitlab.com/postgres-ai/postgres_ai/-/issues"
1414
},
1515
"bin": {
16-
"pgai": "./bin/pgai.ts"
16+
"pgai": "./dist/bin/pgai.js"
1717
},
1818
"type": "module",
19+
"engines": {
20+
"node": ">=18"
21+
},
22+
"scripts": {
23+
"build": "bun build ./bin/pgai.ts --outdir ./dist/bin --target node && sed -i '1s|#!/usr/bin/env bun|#!/usr/bin/env node|' ./dist/bin/pgai.js",
24+
"prepublishOnly": "npm run build"
25+
},
1926
"dependencies": {
2027
"postgresai": "0.0.0-dev.0"
2128
},
2229
"devDependencies": {
2330
"@types/bun": "^1.1.14"
2431
}
2532
}
26-
27-

0 commit comments

Comments
 (0)