refactor: replace tsx with bun for TypeScript execution (#278)
This commit is contained in:
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -52,32 +52,21 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Setup Node.js
|
||||
if: matrix.runtime == 'node'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
check-latest: true
|
||||
|
||||
- name: Setup Bun
|
||||
if: matrix.runtime == 'bun'
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
# bun.sh downloads currently fail with:
|
||||
# "Failed to list releases from GitHub: 401" -> "Unexpected HTTP response: 400"
|
||||
bun-download-url: "https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip"
|
||||
|
||||
- name: Setup Node.js (tooling for bun)
|
||||
if: matrix.runtime == 'bun'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
check-latest: true
|
||||
bun-version: latest
|
||||
|
||||
- name: Runtime versions
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
if [ "${{ matrix.runtime }}" = "bun" ]; then bun -v; fi
|
||||
bun -v
|
||||
|
||||
- name: Capture node path
|
||||
run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- Install deps: `pnpm install`
|
||||
- Run CLI in dev: `pnpm clawdbot ...` (tsx entry) or `pnpm dev` for `src/index.ts`.
|
||||
- Run CLI in dev: `pnpm clawdbot ...` (bun entry) or `pnpm dev` for `src/index.ts`.
|
||||
- Type-check/build: `pnpm build` (tsc)
|
||||
- Lint/format: `pnpm lint` (biome check), `pnpm format` (biome format)
|
||||
- Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage`
|
||||
|
||||
@@ -14,7 +14,7 @@ read_when:
|
||||
Script: `scripts/bench-model.ts`
|
||||
|
||||
Usage:
|
||||
- `source ~/.profile && pnpm tsx scripts/bench-model.ts --runs 10`
|
||||
- `source ~/.profile && bun scripts/bench-model.ts --runs 10`
|
||||
- Optional env: `MINIMAX_API_KEY`, `MINIMAX_BASE_URL`, `MINIMAX_MODEL`, `ANTHROPIC_API_KEY`
|
||||
- Default prompt: “Reply with a single word: ok. No punctuation or extra text.”
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ Mapping options (summary):
|
||||
- `hooks.mappings` lets you define `match`, `action`, and templates in config.
|
||||
- `hooks.transformsDir` + `transform.module` loads a JS/TS module for custom logic.
|
||||
- Use `match.source` to keep a generic ingest endpoint (payload-driven routing).
|
||||
- TS transforms require a TS loader (e.g. `tsx`) or precompiled `.js` at runtime.
|
||||
- TS transforms require a TS loader (e.g. `bun`) or precompiled `.js` at runtime.
|
||||
- `clawdbot hooks gmail setup` writes `hooks.gmail` config for `clawdbot hooks gmail run`.
|
||||
|
||||
## Responses
|
||||
|
||||
23
package.json
23
package.json
@@ -44,20 +44,20 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "tsx src/entry.ts",
|
||||
"dev": "bun src/entry.ts",
|
||||
"postinstall": "node scripts/postinstall.js",
|
||||
"docs:list": "tsx scripts/docs-list.ts",
|
||||
"docs:list": "bun scripts/docs-list.ts",
|
||||
"docs:dev": "cd docs && mint dev",
|
||||
"docs:build": "cd docs && pnpm dlx mint broken-links",
|
||||
"build": "tsc -p tsconfig.json && tsx scripts/canvas-a2ui-copy.ts",
|
||||
"release:check": "tsx scripts/release-check.ts",
|
||||
"build": "tsc -p tsconfig.json && bun scripts/canvas-a2ui-copy.ts",
|
||||
"release:check": "bun scripts/release-check.ts",
|
||||
"ui:install": "pnpm -C ui install",
|
||||
"ui:dev": "pnpm -C ui dev",
|
||||
"ui:build": "pnpm -C ui build",
|
||||
"start": "tsx src/entry.ts",
|
||||
"clawdbot": "tsx src/entry.ts",
|
||||
"gateway:watch": "tsx watch --clear-screen=false --include 'src/**/*.ts' src/entry.ts gateway --force",
|
||||
"clawdbot:rpc": "tsx src/entry.ts agent --mode rpc --json",
|
||||
"start": "bun src/entry.ts",
|
||||
"clawdbot": "bun src/entry.ts",
|
||||
"gateway:watch": "bun --watch src/entry.ts gateway --force",
|
||||
"clawdbot:rpc": "bun src/entry.ts agent --mode rpc --json",
|
||||
"lint": "biome check src test && oxlint --type-aware src test --ignore-pattern src/canvas-host/a2ui/a2ui.bundle.js",
|
||||
"lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)",
|
||||
"lint:fix": "biome check --write --unsafe src && biome format --write src",
|
||||
@@ -65,12 +65,12 @@
|
||||
"format:swift": "swiftformat --lint --config .swiftformat apps/macos/Sources apps/ios/Sources apps/shared/ClawdbotKit/Sources",
|
||||
"format:fix": "biome format src --write",
|
||||
"test": "vitest",
|
||||
"test:force": "tsx scripts/test-force.ts",
|
||||
"test:force": "bun scripts/test-force.ts",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
||||
"test:docker:qr": "bash scripts/e2e/qr-import-docker.sh",
|
||||
"protocol:gen": "tsx scripts/protocol-gen.ts",
|
||||
"protocol:gen:swift": "tsx scripts/protocol-gen-swift.ts",
|
||||
"protocol:gen": "bun scripts/protocol-gen.ts",
|
||||
"protocol:gen:swift": "bun scripts/protocol-gen-swift.ts",
|
||||
"protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift",
|
||||
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh"
|
||||
},
|
||||
@@ -142,7 +142,6 @@
|
||||
"quicktype-core": "^23.2.6",
|
||||
"rolldown": "1.0.0-beta.58",
|
||||
"signal-utils": "^0.21.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.0.16",
|
||||
"wireit": "^0.14.12"
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -194,9 +194,6 @@ importers:
|
||||
signal-utils:
|
||||
specifier: ^0.21.1
|
||||
version: 0.21.1(signal-polyfill@0.2.2)
|
||||
tsx:
|
||||
specifier: ^4.21.0
|
||||
version: 4.21.0
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
@@ -4792,6 +4789,7 @@ snapshots:
|
||||
get-tsconfig@4.13.0:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
optional: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
@@ -5553,7 +5551,8 @@ snapshots:
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
resolve-pkg-maps@1.0.0:
|
||||
optional: true
|
||||
|
||||
retry@0.12.0: {}
|
||||
|
||||
@@ -5877,6 +5876,7 @@ snapshots:
|
||||
get-tsconfig: 4.13.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
optional: true
|
||||
|
||||
type-is@2.0.1:
|
||||
dependencies:
|
||||
|
||||
2
scripts/docs-list.ts
Normal file → Executable file
2
scripts/docs-list.ts
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env tsx
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
|
||||
2
scripts/release-check.ts
Normal file → Executable file
2
scripts/release-check.ts
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env tsx
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
|
||||
2
scripts/test-force.ts
Normal file → Executable file
2
scripts/test-force.ts
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env tsx
|
||||
#!/usr/bin/env bun
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
@@ -15,7 +15,7 @@ let resolvingA2uiRoot: Promise<string | null> | null = null;
|
||||
async function resolveA2uiRoot(): Promise<string | null> {
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const candidates = [
|
||||
// Running from source (tsx) or dist (tsc + copied assets).
|
||||
// Running from source (bun) or dist (tsc + copied assets).
|
||||
path.resolve(here, "a2ui"),
|
||||
// Running from dist without copied assets (fallback to source).
|
||||
path.resolve(here, "../../src/canvas-host/a2ui"),
|
||||
|
||||
@@ -90,10 +90,8 @@ describe("gateway SIGTERM", () => {
|
||||
const err: string[] = [];
|
||||
|
||||
child = spawn(
|
||||
process.execPath,
|
||||
"bun",
|
||||
[
|
||||
"--import",
|
||||
"tsx",
|
||||
"src/index.ts",
|
||||
"gateway",
|
||||
"--port",
|
||||
|
||||
@@ -11,6 +11,11 @@ function isNodeRuntime(execPath: string): boolean {
|
||||
return base === "node" || base === "node.exe";
|
||||
}
|
||||
|
||||
function isBunRuntime(execPath: string): boolean {
|
||||
const base = path.basename(execPath).toLowerCase();
|
||||
return base === "bun" || base === "bun.exe";
|
||||
}
|
||||
|
||||
async function resolveCliEntrypointPathForService(): Promise<string> {
|
||||
const argv1 = process.argv[1];
|
||||
if (!argv1) throw new Error("Unable to resolve CLI entrypoint path");
|
||||
@@ -108,16 +113,16 @@ function resolveRepoRootForDev(): string {
|
||||
return parts.slice(0, srcIndex).join(path.sep);
|
||||
}
|
||||
|
||||
async function resolveTsxCliPath(repoRoot: string): Promise<string> {
|
||||
const candidate = path.join(
|
||||
repoRoot,
|
||||
"node_modules",
|
||||
"tsx",
|
||||
"dist",
|
||||
"cli.mjs",
|
||||
);
|
||||
await fs.access(candidate);
|
||||
return candidate;
|
||||
async function resolveBunPath(): Promise<string> {
|
||||
// Bun is expected to be in PATH, resolve via which/where
|
||||
const { execSync } = await import("node:child_process");
|
||||
try {
|
||||
const bunPath = execSync("which bun", { encoding: "utf8" }).trim();
|
||||
await fs.access(bunPath);
|
||||
return bunPath;
|
||||
} catch {
|
||||
throw new Error("Bun not found in PATH. Install bun: https://bun.sh");
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveGatewayProgramArguments(params: {
|
||||
@@ -125,28 +130,40 @@ export async function resolveGatewayProgramArguments(params: {
|
||||
dev?: boolean;
|
||||
}): Promise<GatewayProgramArgs> {
|
||||
const gatewayArgs = ["gateway-daemon", "--port", String(params.port)];
|
||||
const nodePath = process.execPath;
|
||||
const execPath = process.execPath;
|
||||
|
||||
if (!params.dev) {
|
||||
try {
|
||||
const cliEntrypointPath = await resolveCliEntrypointPathForService();
|
||||
return {
|
||||
programArguments: [nodePath, cliEntrypointPath, ...gatewayArgs],
|
||||
programArguments: [execPath, cliEntrypointPath, ...gatewayArgs],
|
||||
};
|
||||
} catch (error) {
|
||||
if (!isNodeRuntime(nodePath)) {
|
||||
return { programArguments: [nodePath, ...gatewayArgs] };
|
||||
// If running under bun or another runtime that can execute TS directly
|
||||
if (!isNodeRuntime(execPath)) {
|
||||
return { programArguments: [execPath, ...gatewayArgs] };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Dev mode: use bun to run TypeScript directly
|
||||
const repoRoot = resolveRepoRootForDev();
|
||||
const tsxCliPath = await resolveTsxCliPath(repoRoot);
|
||||
const devCliPath = path.join(repoRoot, "src", "index.ts");
|
||||
await fs.access(devCliPath);
|
||||
|
||||
// If already running under bun, use current execPath
|
||||
if (isBunRuntime(execPath)) {
|
||||
return {
|
||||
programArguments: [execPath, devCliPath, ...gatewayArgs],
|
||||
workingDirectory: repoRoot,
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise resolve bun from PATH
|
||||
const bunPath = await resolveBunPath();
|
||||
return {
|
||||
programArguments: [nodePath, tsxCliPath, devCliPath, ...gatewayArgs],
|
||||
programArguments: [bunPath, devCliPath, ...gatewayArgs],
|
||||
workingDirectory: repoRoot,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -118,10 +118,8 @@ const spawnGatewayInstance = async (name: string): Promise<GatewayInstance> => {
|
||||
|
||||
try {
|
||||
child = spawn(
|
||||
process.execPath,
|
||||
"bun",
|
||||
[
|
||||
"--import",
|
||||
"tsx",
|
||||
"src/index.ts",
|
||||
"gateway",
|
||||
"--port",
|
||||
@@ -218,15 +216,11 @@ const runCliJson = async (
|
||||
): Promise<unknown> => {
|
||||
const stdout: string[] = [];
|
||||
const stderr: string[] = [];
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
["--import", "tsx", "src/index.ts", ...args],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: { ...process.env, ...env },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
const child = spawn("bun", ["src/index.ts", ...args], {
|
||||
cwd: process.cwd(),
|
||||
env: { ...process.env, ...env },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
child.stdout?.setEncoding("utf8");
|
||||
child.stderr?.setEncoding("utf8");
|
||||
child.stdout?.on("data", (d) => stdout.push(String(d)));
|
||||
|
||||
Reference in New Issue
Block a user