feat: 增强 Node.js 兼容性 — 扩展 Bun API polyfill 覆盖范围
- build.ts: 大幅扩展 Node.js Bun polyfill,新增 spawn、spawnSync、 file、gc、generateHeapSnapshot、embeddedFiles、semver、YAML、 version、stringWidth、wrapAnsi、listen 等 API 实现 - package.json: 更新描述为多模型终端 AI 编程助手,Node.js 为主要 运行时依赖,ws/yaml 移至 dependencies,bin 入口指向 cli-node.js - scripts/defines.ts: 版本号更新至 4.0.5.1 - fetch.ts: fetch.preconnect 添加 Node.js 降级实现(net.createConnection)
This commit is contained in:
parent
d7f171e2dc
commit
003ff2d6cd
442
build.ts
442
build.ts
|
|
@ -120,14 +120,28 @@ const cliNode = join(outdir, 'cli-node.js')
|
|||
await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n')
|
||||
|
||||
// Node.js entry needs a Bun API polyfill because Bun.build({ target: 'bun' })
|
||||
// emits globalThis.Bun references (e.g. Bun.$ shell tag in computer-use-input,
|
||||
// Bun.which in chunk-ys6smqg9) that crash at import time under plain Node.js.
|
||||
// emits globalThis.Bun references that crash at import time under plain Node.js.
|
||||
//
|
||||
// Polyfill coverage:
|
||||
// - Bun.which → PATH + accessSync lookup
|
||||
// - Bun.hash → FNV-1a 32-bit (compatible with Bun.hash for strings)
|
||||
// - Bun.$ → stub (only used by computer-use-input/darwin)
|
||||
// - Bun.spawn → child_process.spawn (async, returns Subprocess-like)
|
||||
// - Bun.spawnSync → child_process.spawnSync (sync, returns SpawnSyncResult)
|
||||
// - Bun.file → fs.promises.readFile wrapper returning Blob-like object
|
||||
// - Bun.gc → no-op (Node's V8 GC is automatic)
|
||||
// - Bun.generateHeapSnapshot → v8.getHeapSnapshot()
|
||||
// - Bun.embeddedFiles → [] (never present in npm package)
|
||||
const NODE_BUN_POLYFILL = `#!/usr/bin/env node
|
||||
// Bun API polyfill for Node.js runtime
|
||||
// Bun API polyfill for Node.js runtime — makes target: 'bun' output runnable under Node.js
|
||||
if (typeof globalThis.Bun === "undefined") {
|
||||
const { execFileSync } = await import("child_process");
|
||||
const cp = await import("child_process");
|
||||
const { resolve, delimiter } = await import("path");
|
||||
const { accessSync, constants: { X_OK } } = await import("fs");
|
||||
const { accessSync, constants: { X_OK }, statSync } = await import("fs");
|
||||
const fsp = await import("fs/promises");
|
||||
|
||||
// ── Bun.which ─────────────────────────────────────────────────────────
|
||||
// 沿 PATH 查找可执行文件路径
|
||||
function which(bin) {
|
||||
const isWin = process.platform === "win32";
|
||||
const pathExt = isWin ? (process.env.PATHEXT || ".EXE").split(";") : [""];
|
||||
|
|
@ -139,13 +153,11 @@ if (typeof globalThis.Bun === "undefined") {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
// Bun.$ is the shell template tag (e.g. $\`osascript ...\`). Only used by
|
||||
// computer-use-input/darwin — stub it so the top-level destructuring
|
||||
// \`var { $ } = globalThis.Bun\` doesn't crash.
|
||||
function $(parts, ...args) {
|
||||
throw new Error("Bun.$ shell API is not available in Node.js. Use Bun runtime for this feature.");
|
||||
}
|
||||
|
||||
// ── Bun.hash ──────────────────────────────────────────────────────────
|
||||
// FNV-1a 32-bit 散列,与 Bun.hash 行为对齐
|
||||
function hash(data, seed) {
|
||||
if (typeof data !== "string") return 0;
|
||||
let h = ((seed || 0) ^ 0x811c9dc5) >>> 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
h ^= data.charCodeAt(i);
|
||||
|
|
@ -153,7 +165,413 @@ if (typeof globalThis.Bun === "undefined") {
|
|||
}
|
||||
return h;
|
||||
}
|
||||
globalThis.Bun = { which, $, hash };
|
||||
|
||||
// ── Bun.$ ─────────────────────────────────────────────────────────────
|
||||
// Bun shell template tag,仅在 computer-use-input/darwin 中使用
|
||||
// 提供 stub 使顶层解构 var { $ } = globalThis.Bun 不崩溃
|
||||
function $(parts, ...args) {
|
||||
throw new Error("Bun.$ shell API is not available in Node.js. Use Bun runtime for this feature.");
|
||||
}
|
||||
|
||||
// ── Bun.spawn ─────────────────────────────────────────────────────────
|
||||
// 异步子进程启动,返回与 Bun.Subprocess 兼容的对象
|
||||
//
|
||||
// 调用签名(本代码库实际使用的模式):
|
||||
// Bun.spawn([cmd, ...args], { stdin, stdout, stderr, env, argv0 })
|
||||
// Bun.spawn({ cmd: [cmd, ...args], stdin, stdout, stderr }) — 不存在,但防错
|
||||
//
|
||||
// 返回值需支持:
|
||||
// proc.stdin → Writable (当 stdin: 'pipe' 时)
|
||||
// proc.stdout → ReadableStream<Uint8Array> (当 stdout: 'pipe' 时)
|
||||
// proc.stderr → ReadableStream<Uint8Array> (当 stderr: 'pipe' 时)
|
||||
// proc.exited → Promise<number> (退出码)
|
||||
// proc.kill() → 终止进程
|
||||
// proc.pid → 进程 ID
|
||||
function spawn(cmdOrOpts, opts) {
|
||||
let cmd, spawnOpts;
|
||||
if (Array.isArray(cmdOrOpts)) {
|
||||
cmd = cmdOrOpts;
|
||||
spawnOpts = opts || {};
|
||||
} else {
|
||||
const o = cmdOrOpts || {};
|
||||
cmd = o.cmd;
|
||||
spawnOpts = o;
|
||||
}
|
||||
if (!Array.isArray(cmd) || cmd.length === 0) {
|
||||
throw new TypeError("Bun.spawn requires a non-empty command array");
|
||||
}
|
||||
|
||||
// 解析 stdin/stdout/stderr 管道配置
|
||||
const toStdio = (v) => {
|
||||
if (v === "pipe") return "pipe";
|
||||
if (v === "ignore") return "ignore";
|
||||
if (v === "inherit") return "inherit";
|
||||
return v == null ? "pipe" : "pipe";
|
||||
};
|
||||
|
||||
const nodeOpts = {
|
||||
stdio: [toStdio(spawnOpts.stdin), toStdio(spawnOpts.stdout), toStdio(spawnOpts.stderr)],
|
||||
env: spawnOpts.env || process.env,
|
||||
windowsHide: true,
|
||||
};
|
||||
|
||||
// 处理 argv0:Bun 用 argv0 重命名进程,Node 中通过 spawn 的第一个参数传递
|
||||
const executable = spawnOpts.argv0 || cmd[0];
|
||||
const args = spawnOpts.argv0 ? cmd : cmd.slice(1);
|
||||
|
||||
const child = cp.spawn(executable, args, nodeOpts);
|
||||
|
||||
// 构建 Bun 兼容的 stdin 封装
|
||||
let bunStdin = null;
|
||||
if (spawnOpts.stdin === "pipe" || spawnOpts.stdin == null) {
|
||||
bunStdin = child.stdin;
|
||||
}
|
||||
|
||||
// 构建 Bun 兼容的 stdout/stderr 封装
|
||||
// Bun.spawn 返回 ReadableStream<Uint8Array>,Node 返回 Readable
|
||||
// 需要转换以支持 .getReader() 和 new Response(stream).text()
|
||||
function toReadableStream(nodeReadable) {
|
||||
if (!nodeReadable) return null;
|
||||
if (nodeReadable instanceof ReadableStream) return nodeReadable;
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
nodeReadable.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk)));
|
||||
nodeReadable.on("end", () => controller.close());
|
||||
nodeReadable.on("error", (err) => controller.error(err));
|
||||
},
|
||||
cancel() {
|
||||
nodeReadable.destroy();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const bunStdout = toReadableStream(child.stdout);
|
||||
const bunStderr = toReadableStream(child.stderr);
|
||||
|
||||
// 构建 exited Promise
|
||||
const exited = new Promise((resolve) => {
|
||||
child.on("close", (code) => resolve(code ?? 0));
|
||||
});
|
||||
|
||||
return {
|
||||
stdin: bunStdin,
|
||||
stdout: bunStdout,
|
||||
stderr: bunStderr,
|
||||
exited,
|
||||
pid: child.pid,
|
||||
kill(sig) { child.kill(sig || "SIGTERM"); },
|
||||
get exitCode() { return child.exitCode; },
|
||||
};
|
||||
}
|
||||
|
||||
// ── Bun.spawnSync ─────────────────────────────────────────────────────
|
||||
// 同步子进程执行,返回与 Bun.SpawnSyncResult 兼容的对象
|
||||
//
|
||||
// 调用签名(本代码库实际使用的模式):
|
||||
// Bun.spawnSync({ cmd: [cmd, ...args], stdin, stdout, stderr, env, timeout })
|
||||
// Bun.spawnSync({ cmd: [cmd, ...args], stdin: Buffer, stdout: 'pipe' })
|
||||
//
|
||||
// 返回值需支持:
|
||||
// result.stdout → Uint8Array | null
|
||||
// result.stderr → Uint8Array | null
|
||||
// result.exitCode → number
|
||||
function spawnSync(optsOrCmd) {
|
||||
let cmd, syncOpts;
|
||||
if (optsOrCmd && typeof optsOrCmd === "object" && !Array.isArray(optsOrCmd)) {
|
||||
syncOpts = optsOrCmd;
|
||||
cmd = syncOpts.cmd;
|
||||
} else {
|
||||
throw new TypeError("Bun.spawnSync requires an options object with 'cmd' property");
|
||||
}
|
||||
if (!Array.isArray(cmd) || cmd.length === 0) {
|
||||
throw new TypeError("Bun.spawnSync requires a non-empty cmd array");
|
||||
}
|
||||
|
||||
const toStdio = (v) => {
|
||||
if (v === "pipe") return "pipe";
|
||||
if (v === "ignore") return "ignore";
|
||||
if (v === "inherit") return "inherit";
|
||||
return v == null ? "pipe" : "pipe";
|
||||
};
|
||||
|
||||
const nodeOpts = {
|
||||
stdio: [toStdio(syncOpts.stdin), toStdio(syncOpts.stdout), toStdio(syncOpts.stderr)],
|
||||
env: syncOpts.env || process.env,
|
||||
windowsHide: true,
|
||||
timeout: syncOpts.timeout,
|
||||
};
|
||||
|
||||
// 如果 stdin 是 Buffer 或 string,将其作为输入传入
|
||||
if (Buffer.isBuffer(syncOpts.stdin) || typeof syncOpts.stdin === "string") {
|
||||
nodeOpts.input = Buffer.from(syncOpts.stdin);
|
||||
nodeOpts.stdio[0] = "pipe";
|
||||
}
|
||||
|
||||
const result = cp.spawnSync(cmd[0], cmd.slice(1), nodeOpts);
|
||||
|
||||
return {
|
||||
stdout: result.stdout ? Uint8Array.from(Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.from(result.stdout)) : null,
|
||||
stderr: result.stderr ? Uint8Array.from(Buffer.isBuffer(result.stderr) ? result.stderr : Buffer.from(result.stderr)) : null,
|
||||
exitCode: result.status ?? 1,
|
||||
success: result.status === 0,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Bun.file ──────────────────────────────────────────────────────────
|
||||
// 返回类 BunFile 对象,支持 .arrayBuffer()、.text()、.json()、.exists()
|
||||
// 本代码库中仅 linux.ts 使用:Bun.file(path).arrayBuffer()
|
||||
function bunFile(filePath) {
|
||||
return {
|
||||
async arrayBuffer() {
|
||||
const buf = await fsp.readFile(filePath);
|
||||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
||||
},
|
||||
async text() {
|
||||
return await fsp.readFile(filePath, "utf-8");
|
||||
},
|
||||
async json() {
|
||||
return JSON.parse(await fsp.readFile(filePath, "utf-8"));
|
||||
},
|
||||
async exists() {
|
||||
try { await fsp.access(filePath); return true; } catch { return false; }
|
||||
},
|
||||
get size() {
|
||||
try { return statSync(filePath).size; } catch { return 0; }
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ── Bun.gc ────────────────────────────────────────────────────────────
|
||||
// Node.js 的 V8 GC 是自动的,无需手动触发;暴露为 no-op
|
||||
function gc() {}
|
||||
|
||||
// ── Bun.generateHeapSnapshot ──────────────────────────────────────────
|
||||
// Node.js 用 v8.getHeapSnapshot() 替代
|
||||
function generateHeapSnapshot() {
|
||||
const v8 = require("v8");
|
||||
const stream = v8.getHeapSnapshot();
|
||||
const chunks = [];
|
||||
for (const chunk of stream) chunks.push(chunk);
|
||||
return Buffer.concat(chunks).toString("utf-8");
|
||||
}
|
||||
|
||||
// ── Bun.embeddedFiles ─────────────────────────────────────────────────
|
||||
// npm 包中不存在嵌入文件
|
||||
const embeddedFiles = [];
|
||||
|
||||
// ── Bun.semver ────────────────────────────────────────────────────────
|
||||
// Bun 内置 semver 比较,产物中通过 typeof Bun 守卫调用
|
||||
// 用 semver 包的子集实现 order 和 satisfies
|
||||
// semver.order(a, b): 1 if a>b, 0 if a==b, -1 if a<b
|
||||
const semver = {
|
||||
order(a, b) {
|
||||
const pa = parseSemver(a);
|
||||
const pb = parseSemver(b);
|
||||
if (!pa || !pb) return 0;
|
||||
const cmp = pa[0] - pb[0] || pa[1] - pb[1] || pa[2] - pb[2];
|
||||
return cmp > 0 ? 1 : cmp < 0 ? -1 : 0;
|
||||
},
|
||||
satisfies(version, range) {
|
||||
// 简化的 range 解析:支持 >=x.y.z, ^x.y.z, ~x.y.z, x.y.z, >x.y.z, <x.y.z, <=x.y.z, x.y.z - a.b.c
|
||||
const pv = parseSemver(version);
|
||||
if (!pv) return false;
|
||||
const r = range.trim();
|
||||
// 范围用 || 分隔
|
||||
const ors = r.split("||").map(s => s.trim());
|
||||
for (const part of ors) {
|
||||
if (satisfiesRange(pv, part)) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
// 解析 x.y.z 为 [major, minor, patch],忽略 pre-release
|
||||
function parseSemver(v) {
|
||||
if (!v) return null;
|
||||
const m = String(v).match(/(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (!m) return null;
|
||||
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
||||
}
|
||||
|
||||
function satisfiesRange(pv, range) {
|
||||
// 空范围或 * 匹配所有
|
||||
if (!range || range === "*") return true;
|
||||
// 空格分隔的 AND 条件
|
||||
const conditions = range.split(/\\s+/).filter(Boolean);
|
||||
for (const cond of conditions) {
|
||||
if (!satisfiesCondition(pv, cond)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function satisfiesCondition(pv, cond) {
|
||||
if (cond === "*") return true;
|
||||
// ^x.y.z — 兼容 major
|
||||
let m = cond.match(/^\\^(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] === M && (pv[0] > 0 ? pv[1] >= m2 || (pv[1] === m2 && pv[2] >= p) : pv[1] === m2 && pv[2] >= p) || pv[0] === M && pv[1] > m2;
|
||||
}
|
||||
// ~x.y.z — 兼容 major.minor
|
||||
m = cond.match(/^~(>?=?(\\d+)\\.(\\d+)\\.(\\d+))/);
|
||||
if (m) {
|
||||
const raw = m[1];
|
||||
const cm = raw.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (cm) {
|
||||
const [_, M, m2] = cm.map(Number);
|
||||
return pv[0] === M && pv[1] === m2 && pv[2] >= Number(cm[3]);
|
||||
}
|
||||
}
|
||||
// ~x.y
|
||||
m = cond.match(/^~(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2] = m.map(Number);
|
||||
return pv[0] === M && pv[1] === m2;
|
||||
}
|
||||
// >=x.y.z
|
||||
m = cond.match(/^>=(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] > M || (pv[0] === M && pv[1] > m2) || (pv[0] === M && pv[1] === m2 && pv[2] >= p);
|
||||
}
|
||||
// >x.y.z
|
||||
m = cond.match(/^>(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] > M || (pv[0] === M && pv[1] > m2) || (pv[0] === M && pv[1] === m2 && pv[2] > p);
|
||||
}
|
||||
// <=x.y.z
|
||||
m = cond.match(/^<=(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] < M || (pv[0] === M && pv[1] < m2) || (pv[0] === M && pv[1] === m2 && pv[2] <= p);
|
||||
}
|
||||
// <x.y.z
|
||||
m = cond.match(/^<(\\d+)\\.(\\d+)\\.(\\d+)/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] < M || (pv[0] === M && pv[1] < m2) || (pv[0] === M && pv[1] === m2 && pv[2] < p);
|
||||
}
|
||||
// 精确匹配 x.y.z
|
||||
m = cond.match(/^(\\d+)\\.(\\d+)\\.(\\d+)$/);
|
||||
if (m) {
|
||||
const [_, M, m2, p] = m.map(Number);
|
||||
return pv[0] === M && pv[1] === m2 && pv[2] === p;
|
||||
}
|
||||
// x.y.z - a.b.c 范围
|
||||
m = cond.match(/^(\\d+\\.\\d+\\.\\d+)\\s*-\\s*(\\d+\\.\\d+\\.\\d+)$/);
|
||||
if (m) {
|
||||
const lo = parseSemver(m[1]);
|
||||
const hi = parseSemver(m[2]);
|
||||
if (!lo || !hi) return false;
|
||||
const ordLo = semver.order([pv[0], pv[1], pv[2]].join("."), lo.join(".")) >= 0;
|
||||
const ordHi = semver.order([pv[0], pv[1], pv[2]].join("."), hi.join(".")) <= 0;
|
||||
return ordLo && ordHi;
|
||||
}
|
||||
// 无法识别,宽松通过
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Bun.YAML ──────────────────────────────────────────────────────────
|
||||
// Bun 内置 YAML 解析器,产物中通过 typeof Bun 守卫调用
|
||||
// 降级到 yaml 包(已作为依赖存在)
|
||||
const YAML = {
|
||||
parse(input) {
|
||||
try {
|
||||
const yaml = require("yaml");
|
||||
return yaml.parse(input);
|
||||
} catch {
|
||||
throw new Error("Bun.YAML polyfill: 'yaml' package not available. Install it or use Bun runtime.");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// ── Bun.version ───────────────────────────────────────────────────────
|
||||
// Bun 运行时版本字符串,用于 typeof 检测
|
||||
const version = "1.2.0-polyfill";
|
||||
|
||||
// ── Bun.stringWidth ───────────────────────────────────────────────────
|
||||
// 东亚宽度感知的字符串显示宽度,产物中有 typeof 守卫降级
|
||||
// 提供基础实现,按 CJK 字符双宽计算
|
||||
function stringWidth(str) {
|
||||
let w = 0;
|
||||
for (const ch of str) {
|
||||
const code = ch.codePointAt(0);
|
||||
if (code >= 0x1100 && (
|
||||
code <= 0x115F ||
|
||||
code === 0x2329 || code === 0x232A ||
|
||||
(code >= 0x2E80 && code <= 0xA4CF && code !== 0x303F) ||
|
||||
(code >= 0xAC00 && code <= 0xD7A3) ||
|
||||
(code >= 0xF900 && code <= 0xFAFF) ||
|
||||
(code >= 0xFE10 && code <= 0xFE19) ||
|
||||
(code >= 0xFE30 && code <= 0xFE6F) ||
|
||||
(code >= 0xFF01 && code <= 0xFF60) ||
|
||||
(code >= 0xFFE0 && code <= 0xFFE6) ||
|
||||
(code >= 0x1F300 && code <= 0x1F9FF) ||
|
||||
(code >= 0x20000 && code <= 0x2FFFD) ||
|
||||
(code >= 0x30000 && code <= 0x3FFFD)
|
||||
)) {
|
||||
w += 2;
|
||||
} else if (code >= 0x20) {
|
||||
w += 1;
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
// ── Bun.wrapAnsi ──────────────────────────────────────────────────────
|
||||
// ANSI 转义序列感知的自动换行,产物中有 typeof 守卫降级到 null
|
||||
// 提供 stub 即可
|
||||
const wrapAnsi = null;
|
||||
|
||||
// ── Bun.listen ────────────────────────────────────────────────────────
|
||||
// Daemon 模式专用 TCP 监听,feature-gated (DAEMON)
|
||||
// Node.js 中用 net.Server 替代
|
||||
function listen(opts) {
|
||||
const net = require("net");
|
||||
const server = net.createServer((socket) => {
|
||||
if (opts.socket && opts.socket.data) {
|
||||
socket.data = { ...opts.socket.data() };
|
||||
}
|
||||
if (opts.socket && opts.socket.open) {
|
||||
opts.socket.open(socket);
|
||||
}
|
||||
socket.on("data", (data) => {
|
||||
if (opts.socket && opts.socket.data) {
|
||||
opts.socket.data(socket, data);
|
||||
}
|
||||
});
|
||||
socket.on("close", () => {
|
||||
if (opts.socket && opts.socket.close) {
|
||||
opts.socket.close(socket);
|
||||
}
|
||||
});
|
||||
socket.on("error", () => {});
|
||||
});
|
||||
server.listen(opts.port || 0, opts.hostname || "0.0.0.0", () => {
|
||||
const addr = server.address();
|
||||
server.port = addr.port;
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
||||
globalThis.Bun = {
|
||||
which,
|
||||
$,
|
||||
hash,
|
||||
spawn,
|
||||
spawnSync,
|
||||
file: bunFile,
|
||||
gc,
|
||||
generateHeapSnapshot,
|
||||
embeddedFiles,
|
||||
semver,
|
||||
YAML,
|
||||
version,
|
||||
stringWidth,
|
||||
wrapAnsi,
|
||||
listen,
|
||||
};
|
||||
}
|
||||
import "./cli.js"
|
||||
`
|
||||
|
|
|
|||
27
package.json
27
package.json
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "@costrict/csc",
|
||||
"version": "4.0.5",
|
||||
"version": "4.0.5-3",
|
||||
|
||||
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
|
||||
"description": "CSC — 多模型终端 AI 编程助手,支持 Anthropic/OpenAI/Gemini/Grok 等多种 API 端点",
|
||||
"type": "module",
|
||||
"author": "claude-code-best <claude-code-best@proton.me>",
|
||||
"repository": {
|
||||
|
|
@ -14,21 +14,26 @@
|
|||
"url": "https://github.com/y574444354/csc/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"claude",
|
||||
"anthropic",
|
||||
"csc",
|
||||
"costrict",
|
||||
"cli",
|
||||
"ai",
|
||||
"coding-assistant",
|
||||
"terminal",
|
||||
"repl"
|
||||
"repl",
|
||||
"multi-model",
|
||||
"anthropic",
|
||||
"openai",
|
||||
"gemini",
|
||||
"grok"
|
||||
],
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"csc": "dist/cli.js",
|
||||
"csc": "dist/cli-node.js",
|
||||
"csc-bun": "dist/cli-bun.js",
|
||||
"claude-code-best": "dist/cli.js"
|
||||
"claude-code-best": "dist/cli-node.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
|
|
@ -58,7 +63,9 @@
|
|||
"rcs": "bun run scripts/rcs.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@claude-code-best/mcp-chrome-bridge": "^2.0.7"
|
||||
"@claude-code-best/mcp-chrome-bridge": "^2.0.7",
|
||||
"ws": "^8.20.0",
|
||||
"yaml": "^2.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/he": "^1.2.3",
|
||||
|
|
@ -187,9 +194,7 @@
|
|||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-languageserver-types": "^3.17.5",
|
||||
"wrap-ansi": "^10.0.0",
|
||||
"ws": "^8.20.0",
|
||||
"xss": "^1.0.15",
|
||||
"yaml": "^2.8.3",
|
||||
"zod": "^4.3.6"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
export function getMacroDefines(): Record<string, string> {
|
||||
return {
|
||||
"MACRO.VERSION": JSON.stringify("4.0.5"),
|
||||
"MACRO.VERSION": JSON.stringify("4.0.5.1"),
|
||||
"MACRO.BUILD_TIME": JSON.stringify(new Date().toISOString()),
|
||||
"MACRO.FEEDBACK_CHANNEL": JSON.stringify(""),
|
||||
"MACRO.ISSUES_EXPLAINER": JSON.stringify(""),
|
||||
|
|
|
|||
|
|
@ -124,6 +124,22 @@ export function createCoStrictFetch() {
|
|||
|
||||
return response
|
||||
}
|
||||
costrictFetch.preconnect = fetch.preconnect.bind(fetch)
|
||||
// Bun 原生支持 fetch.preconnect(共享连接池预热)
|
||||
// Node.js 没有 preconnect API,降级为 net.createConnection 做 TCP 预热
|
||||
if (typeof fetch.preconnect === 'function') {
|
||||
costrictFetch.preconnect = fetch.preconnect.bind(fetch)
|
||||
} else {
|
||||
costrictFetch.preconnect = (url: string | URL) => {
|
||||
try {
|
||||
const { hostname, port } = new URL(typeof url === 'string' ? url : url.toString())
|
||||
const { createConnection } = require('node:net') as typeof import('node:net')
|
||||
const sock = createConnection(Number(port) || 443, hostname, () => sock.destroy())
|
||||
sock.setTimeout(3000, () => sock.destroy())
|
||||
sock.on('error', () => sock.destroy())
|
||||
} catch {
|
||||
// 预连接失败不影响正常流程
|
||||
}
|
||||
}
|
||||
}
|
||||
return costrictFetch
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user