Slim release package (#1446)
This commit is contained in:
@@ -58,6 +58,10 @@ module.exports = {
|
|||||||
'!**/tests/**/*',
|
'!**/tests/**/*',
|
||||||
'!**/example/**/*',
|
'!**/example/**/*',
|
||||||
'!**/examples/**/*',
|
'!**/examples/**/*',
|
||||||
|
'!node_modules/**/docs/**/*',
|
||||||
|
'!node_modules/**/doc/**/*',
|
||||||
|
'!node_modules/**/benchmark/**/*',
|
||||||
|
'!node_modules/**/benchmarks/**/*',
|
||||||
// Renderer-only packages are compiled into dist by Vite. Keep them
|
// Renderer-only packages are compiled into dist by Vite. Keep them
|
||||||
// installed for npm run dev/build, but do not ship the duplicate source
|
// installed for npm run dev/build, but do not ship the duplicate source
|
||||||
// packages in release artifacts.
|
// packages in release artifacts.
|
||||||
@@ -103,7 +107,15 @@ module.exports = {
|
|||||||
// other coding agents: Netcatty discovers and passes the user's
|
// other coding agents: Netcatty discovers and passes the user's
|
||||||
// installed CLI path to the SDK. Keep the small SDK wrapper, but do not
|
// installed CLI path to the SDK. Keep the small SDK wrapper, but do not
|
||||||
// bundle the full CodeBuddy CLI payload (rg vendors + web UI).
|
// bundle the full CodeBuddy CLI payload (rg vendors + web UI).
|
||||||
'!node_modules/@tencent-ai/agent-sdk/cli/**/*'
|
'!node_modules/@tencent-ai/agent-sdk/cli/**/*',
|
||||||
|
// Netcatty loads Cursor SDK through ESM dynamic import, so the duplicate
|
||||||
|
// CommonJS build and type metadata are not needed at runtime.
|
||||||
|
'!node_modules/@cursor/sdk/dist/cjs/**/*',
|
||||||
|
'!node_modules/@cursor/sdk/dist/**/*.d.ts',
|
||||||
|
'!node_modules/@cursor/sdk/dist/**/*.d.ts.map',
|
||||||
|
// sqlite3 rebuilds a native module for Electron; its upstream source
|
||||||
|
// tarball is build-time payload only.
|
||||||
|
'!node_modules/sqlite3/deps/**/*'
|
||||||
],
|
],
|
||||||
asarUnpack: [
|
asarUnpack: [
|
||||||
'node_modules/node-pty/**/*',
|
'node_modules/node-pty/**/*',
|
||||||
@@ -111,7 +123,6 @@ module.exports = {
|
|||||||
'node_modules/cpu-features/**/*',
|
'node_modules/cpu-features/**/*',
|
||||||
'node_modules/@vscode/windows-process-tree/**/*',
|
'node_modules/@vscode/windows-process-tree/**/*',
|
||||||
'node_modules/@anthropic-ai/claude-agent-sdk/**/*',
|
'node_modules/@anthropic-ai/claude-agent-sdk/**/*',
|
||||||
'node_modules/@cursor/sdk/**/*',
|
|
||||||
'node_modules/@cursor/sdk-*/**/*',
|
'node_modules/@cursor/sdk-*/**/*',
|
||||||
'node_modules/sqlite3/**/*',
|
'node_modules/sqlite3/**/*',
|
||||||
'node_modules/@modelcontextprotocol/sdk/**/*',
|
'node_modules/@modelcontextprotocol/sdk/**/*',
|
||||||
|
|||||||
@@ -123,8 +123,182 @@ function adHocSignAppBundle(appPath, options = {}) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ELECTRON_BUILDER_ARCH_NAMES = {
|
||||||
|
0: "ia32",
|
||||||
|
1: "x64",
|
||||||
|
2: "armv7l",
|
||||||
|
3: "arm64",
|
||||||
|
4: "universal",
|
||||||
|
};
|
||||||
|
|
||||||
|
function archNameFromContext(context) {
|
||||||
|
const arch = context?.arch;
|
||||||
|
if (typeof arch === "string") return arch;
|
||||||
|
if (typeof arch === "number" && ELECTRON_BUILDER_ARCH_NAMES[arch]) {
|
||||||
|
return ELECTRON_BUILDER_ARCH_NAMES[arch];
|
||||||
|
}
|
||||||
|
return process.arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cursorPlatformPackageBases(platform) {
|
||||||
|
if (platform === "darwin") return ["sdk-darwin-arm64", "sdk-darwin-x64"];
|
||||||
|
if (platform === "linux") return ["sdk-linux-arm64", "sdk-linux-x64"];
|
||||||
|
if (platform === "win32") return ["sdk-win32-x64"];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function cursorPackagesToKeep(platform, archName) {
|
||||||
|
if (platform === "darwin" && archName === "universal") {
|
||||||
|
return new Set(["sdk-darwin-arm64", "sdk-darwin-x64"]);
|
||||||
|
}
|
||||||
|
if (platform === "darwin" && (archName === "arm64" || archName === "x64")) {
|
||||||
|
return new Set([`sdk-darwin-${archName}`]);
|
||||||
|
}
|
||||||
|
if (platform === "linux" && (archName === "arm64" || archName === "x64")) {
|
||||||
|
return new Set([`sdk-linux-${archName}`]);
|
||||||
|
}
|
||||||
|
if (platform === "win32" && archName === "x64") {
|
||||||
|
return new Set(["sdk-win32-x64"]);
|
||||||
|
}
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
function appResourcesDir(context) {
|
||||||
|
if (context.electronPlatformName === "darwin") {
|
||||||
|
const productFilename = context.packager.appInfo.productFilename;
|
||||||
|
return path.join(context.appOutDir, `${productFilename}.app`, "Contents", "Resources");
|
||||||
|
}
|
||||||
|
return path.join(context.appOutDir, "resources");
|
||||||
|
}
|
||||||
|
|
||||||
|
function readAsarHeader(asarPath) {
|
||||||
|
const fd = fs.openSync(asarPath, "r");
|
||||||
|
try {
|
||||||
|
const sizeBuf = Buffer.alloc(8);
|
||||||
|
if (fs.readSync(fd, sizeBuf, 0, sizeBuf.length, 0) !== sizeBuf.length) {
|
||||||
|
throw new Error(`[afterPack] Unable to read ASAR header size: ${asarPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sizePicklePayloadSize = sizeBuf.readUInt32LE(0);
|
||||||
|
if (sizePicklePayloadSize !== 4) {
|
||||||
|
throw new Error(`[afterPack] Unsupported ASAR size pickle in ${asarPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerSize = sizeBuf.readUInt32LE(4);
|
||||||
|
const headerBuf = Buffer.alloc(headerSize);
|
||||||
|
if (fs.readSync(fd, headerBuf, 0, headerSize, 8) !== headerSize) {
|
||||||
|
throw new Error(`[afterPack] Unable to read ASAR header: ${asarPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerPicklePayloadSize = headerBuf.readUInt32LE(0);
|
||||||
|
if (headerPicklePayloadSize !== headerSize - 4) {
|
||||||
|
throw new Error(`[afterPack] Unsupported ASAR header pickle in ${asarPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerStringLength = headerBuf.readInt32LE(4);
|
||||||
|
const headerString = headerBuf.subarray(8, 8 + headerStringLength).toString("utf8");
|
||||||
|
return { header: JSON.parse(headerString), headerSize };
|
||||||
|
} finally {
|
||||||
|
fs.closeSync(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeAsarHeaderPreservingDataOffset(asarPath, header, headerSize) {
|
||||||
|
const headerString = JSON.stringify(header);
|
||||||
|
const headerStringLength = Buffer.byteLength(headerString);
|
||||||
|
const fixedPrefixSize = 8; // payload size uint32 + string length int32
|
||||||
|
if (fixedPrefixSize + headerStringLength > headerSize) {
|
||||||
|
throw new Error(
|
||||||
|
`[afterPack] Updated ASAR header is larger than the original header for ${asarPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerBuf = Buffer.alloc(headerSize);
|
||||||
|
headerBuf.writeUInt32LE(headerSize - 4, 0);
|
||||||
|
headerBuf.writeInt32LE(headerStringLength, 4);
|
||||||
|
headerBuf.write(headerString, fixedPrefixSize, headerStringLength, "utf8");
|
||||||
|
|
||||||
|
const fd = fs.openSync(asarPath, "r+");
|
||||||
|
try {
|
||||||
|
fs.writeSync(fd, headerBuf, 0, headerBuf.length, 8);
|
||||||
|
} finally {
|
||||||
|
fs.closeSync(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAsarHeaderEntry(header, entryPath) {
|
||||||
|
const segments = entryPath.split(/[\\/]+/).filter(Boolean);
|
||||||
|
if (segments.length === 0) return false;
|
||||||
|
|
||||||
|
let node = header;
|
||||||
|
for (const segment of segments.slice(0, -1)) {
|
||||||
|
node = node.files?.[segment];
|
||||||
|
if (!node) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaf = segments[segments.length - 1];
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(node.files || {}, leaf)) return false;
|
||||||
|
delete node.files[leaf];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneAsarHeaderEntries(asarPath, entryPaths) {
|
||||||
|
if (!fs.existsSync(asarPath) || entryPaths.length === 0) return [];
|
||||||
|
|
||||||
|
const { header, headerSize } = readAsarHeader(asarPath);
|
||||||
|
const removed = entryPaths.filter((entryPath) => removeAsarHeaderEntry(header, entryPath));
|
||||||
|
if (removed.length > 0) {
|
||||||
|
writeAsarHeaderPreservingDataOffset(asarPath, header, headerSize);
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneCursorSdkPlatformPackages(context) {
|
||||||
|
const platform = context.electronPlatformName;
|
||||||
|
const candidates = cursorPlatformPackageBases(platform);
|
||||||
|
if (candidates.length === 0) return [];
|
||||||
|
|
||||||
|
const keep = cursorPackagesToKeep(platform, archNameFromContext(context));
|
||||||
|
if (keep.size === 0) return [];
|
||||||
|
|
||||||
|
const cursorRoot = path.join(
|
||||||
|
appResourcesDir(context),
|
||||||
|
"app.asar.unpacked",
|
||||||
|
"node_modules",
|
||||||
|
"@cursor",
|
||||||
|
);
|
||||||
|
if (!fs.existsSync(cursorRoot)) return [];
|
||||||
|
|
||||||
|
const removed = [];
|
||||||
|
const asarHeaderEntriesToRemove = [];
|
||||||
|
for (const baseName of candidates) {
|
||||||
|
if (keep.has(baseName)) continue;
|
||||||
|
const dir = path.join(cursorRoot, baseName);
|
||||||
|
if (!fs.existsSync(dir)) continue;
|
||||||
|
removed.push(baseName);
|
||||||
|
asarHeaderEntriesToRemove.push(`node_modules/@cursor/${baseName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed.length === 0) return [];
|
||||||
|
|
||||||
|
const appAsar = path.join(appResourcesDir(context), "app.asar");
|
||||||
|
pruneAsarHeaderEntries(appAsar, asarHeaderEntriesToRemove);
|
||||||
|
|
||||||
|
for (const baseName of removed) {
|
||||||
|
fs.rmSync(path.join(cursorRoot, baseName), { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
/** @param {import('electron-builder').AfterPackContext} context */
|
/** @param {import('electron-builder').AfterPackContext} context */
|
||||||
async function afterPack(context) {
|
async function afterPack(context) {
|
||||||
|
const removedCursorPackages = pruneCursorSdkPlatformPackages(context);
|
||||||
|
if (removedCursorPackages.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`[afterPack] Removed unused Cursor SDK platform package(s): ${removedCursorPackages.join(", ")}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (context.electronPlatformName !== "darwin") return;
|
if (context.electronPlatformName !== "darwin") return;
|
||||||
|
|
||||||
const appId = context.packager.appInfo.id || "com.netcatty.app";
|
const appId = context.packager.appInfo.id || "com.netcatty.app";
|
||||||
@@ -173,3 +347,6 @@ module.exports.formatUuid = formatUuid;
|
|||||||
module.exports.patchMachOBuffer = patchMachOBuffer;
|
module.exports.patchMachOBuffer = patchMachOBuffer;
|
||||||
module.exports.patchMachOFile = patchMachOFile;
|
module.exports.patchMachOFile = patchMachOFile;
|
||||||
module.exports.adHocSignAppBundle = adHocSignAppBundle;
|
module.exports.adHocSignAppBundle = adHocSignAppBundle;
|
||||||
|
module.exports.readAsarHeader = readAsarHeader;
|
||||||
|
module.exports.pruneAsarHeaderEntries = pruneAsarHeaderEntries;
|
||||||
|
module.exports.pruneCursorSdkPlatformPackages = pruneCursorSdkPlatformPackages;
|
||||||
|
|||||||
@@ -5,12 +5,37 @@ const {
|
|||||||
adHocSignAppBundle,
|
adHocSignAppBundle,
|
||||||
deriveUuid,
|
deriveUuid,
|
||||||
patchMachOBuffer,
|
patchMachOBuffer,
|
||||||
|
pruneAsarHeaderEntries,
|
||||||
|
pruneCursorSdkPlatformPackages,
|
||||||
|
readAsarHeader,
|
||||||
} = require("./afterPackMacUuid.cjs");
|
} = require("./afterPackMacUuid.cjs");
|
||||||
|
|
||||||
const LC_UUID = 0x1b;
|
const LC_UUID = 0x1b;
|
||||||
const LC_OTHER = 0x19;
|
const LC_OTHER = 0x19;
|
||||||
const MH_MAGIC_64 = 0xfeedfacf;
|
const MH_MAGIC_64 = 0xfeedfacf;
|
||||||
|
|
||||||
|
function align4(value) {
|
||||||
|
return value + ((4 - (value % 4)) % 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeFakeAsar(asarPath, header, payload = Buffer.from("packed-payload")) {
|
||||||
|
const headerString = JSON.stringify(header);
|
||||||
|
const headerStringLength = Buffer.byteLength(headerString);
|
||||||
|
const headerPayloadSize = 4 + align4(headerStringLength);
|
||||||
|
const headerSize = 4 + headerPayloadSize;
|
||||||
|
const sizeBuf = Buffer.alloc(8);
|
||||||
|
const headerBuf = Buffer.alloc(headerSize);
|
||||||
|
|
||||||
|
sizeBuf.writeUInt32LE(4, 0);
|
||||||
|
sizeBuf.writeUInt32LE(headerSize, 4);
|
||||||
|
headerBuf.writeUInt32LE(headerPayloadSize, 0);
|
||||||
|
headerBuf.writeInt32LE(headerStringLength, 4);
|
||||||
|
headerBuf.write(headerString, 8, headerStringLength, "utf8");
|
||||||
|
|
||||||
|
require("node:fs").writeFileSync(asarPath, Buffer.concat([sizeBuf, headerBuf, payload]));
|
||||||
|
return headerSize;
|
||||||
|
}
|
||||||
|
|
||||||
// Build a minimal thin little-endian 64-bit Mach-O with two load commands:
|
// Build a minimal thin little-endian 64-bit Mach-O with two load commands:
|
||||||
// one dummy command and one LC_UUID carrying `uuidBytes`.
|
// one dummy command and one LC_UUID carrying `uuidBytes`.
|
||||||
function buildThinMachO(uuidBytes) {
|
function buildThinMachO(uuidBytes) {
|
||||||
@@ -157,3 +182,159 @@ test("adHocSignAppBundle skips non-macOS hosts", () => {
|
|||||||
assert.equal(didSign, false);
|
assert.equal(didSign, false);
|
||||||
assert.equal(called, false);
|
assert.equal(called, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("pruneAsarHeaderEntries removes package records without moving packed payload", (t) => {
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const os = require("node:os");
|
||||||
|
const path = require("node:path");
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-prune-asar-"));
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }));
|
||||||
|
|
||||||
|
const asarPath = path.join(tempDir, "app.asar");
|
||||||
|
const payload = Buffer.from("packed-payload");
|
||||||
|
const headerSize = writeFakeAsar(
|
||||||
|
asarPath,
|
||||||
|
{
|
||||||
|
files: {
|
||||||
|
node_modules: {
|
||||||
|
files: {
|
||||||
|
"@cursor": {
|
||||||
|
files: {
|
||||||
|
"sdk-darwin-arm64": {
|
||||||
|
files: {
|
||||||
|
"package.json": { size: 2, unpacked: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sdk-darwin-x64": {
|
||||||
|
files: {
|
||||||
|
"package.json": { size: 2, unpacked: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packed.txt": { size: payload.length, offset: "0" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
|
||||||
|
const removed = pruneAsarHeaderEntries(asarPath, ["node_modules/@cursor/sdk-darwin-x64"]);
|
||||||
|
const { header, headerSize: updatedHeaderSize } = readAsarHeader(asarPath);
|
||||||
|
const packedPayload = fs.readFileSync(asarPath).subarray(8 + headerSize);
|
||||||
|
|
||||||
|
assert.deepEqual(removed, ["node_modules/@cursor/sdk-darwin-x64"]);
|
||||||
|
assert.equal(updatedHeaderSize, headerSize);
|
||||||
|
assert.ok(header.files.node_modules.files["@cursor"].files["sdk-darwin-arm64"]);
|
||||||
|
assert.equal(header.files.node_modules.files["@cursor"].files["sdk-darwin-x64"], undefined);
|
||||||
|
assert.equal(packedPayload.toString("utf8"), payload.toString("utf8"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pruneCursorSdkPlatformPackages keeps only the target macOS arch package", (t) => {
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const os = require("node:os");
|
||||||
|
const path = require("node:path");
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-prune-cursor-"));
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }));
|
||||||
|
const cursorRoot = path.join(
|
||||||
|
tempDir,
|
||||||
|
"Netcatty.app",
|
||||||
|
"Contents",
|
||||||
|
"Resources",
|
||||||
|
"app.asar.unpacked",
|
||||||
|
"node_modules",
|
||||||
|
"@cursor",
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-darwin-arm64"), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-darwin-x64"), { recursive: true });
|
||||||
|
writeFakeAsar(path.join(tempDir, "Netcatty.app", "Contents", "Resources", "app.asar"), {
|
||||||
|
files: {
|
||||||
|
node_modules: {
|
||||||
|
files: {
|
||||||
|
"@cursor": {
|
||||||
|
files: {
|
||||||
|
"sdk-darwin-arm64": { files: { "package.json": { size: 2, unpacked: true } } },
|
||||||
|
"sdk-darwin-x64": { files: { "package.json": { size: 2, unpacked: true } } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const removed = pruneCursorSdkPlatformPackages({
|
||||||
|
electronPlatformName: "darwin",
|
||||||
|
arch: 3,
|
||||||
|
appOutDir: tempDir,
|
||||||
|
packager: { appInfo: { productFilename: "Netcatty" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(removed, ["sdk-darwin-x64"]);
|
||||||
|
assert.ok(fs.existsSync(path.join(cursorRoot, "sdk-darwin-arm64")));
|
||||||
|
assert.ok(!fs.existsSync(path.join(cursorRoot, "sdk-darwin-x64")));
|
||||||
|
|
||||||
|
const { header } = readAsarHeader(
|
||||||
|
path.join(tempDir, "Netcatty.app", "Contents", "Resources", "app.asar"),
|
||||||
|
);
|
||||||
|
assert.ok(header.files.node_modules.files["@cursor"].files["sdk-darwin-arm64"]);
|
||||||
|
assert.equal(header.files.node_modules.files["@cursor"].files["sdk-darwin-x64"], undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pruneCursorSdkPlatformPackages keeps both macOS packages for universal builds", (t) => {
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const os = require("node:os");
|
||||||
|
const path = require("node:path");
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-prune-cursor-"));
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }));
|
||||||
|
const cursorRoot = path.join(
|
||||||
|
tempDir,
|
||||||
|
"Netcatty.app",
|
||||||
|
"Contents",
|
||||||
|
"Resources",
|
||||||
|
"app.asar.unpacked",
|
||||||
|
"node_modules",
|
||||||
|
"@cursor",
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-darwin-arm64"), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-darwin-x64"), { recursive: true });
|
||||||
|
|
||||||
|
const removed = pruneCursorSdkPlatformPackages({
|
||||||
|
electronPlatformName: "darwin",
|
||||||
|
arch: 4,
|
||||||
|
appOutDir: tempDir,
|
||||||
|
packager: { appInfo: { productFilename: "Netcatty" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(removed, []);
|
||||||
|
assert.ok(fs.existsSync(path.join(cursorRoot, "sdk-darwin-arm64")));
|
||||||
|
assert.ok(fs.existsSync(path.join(cursorRoot, "sdk-darwin-x64")));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pruneCursorSdkPlatformPackages keeps only the target Linux arch package", (t) => {
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const os = require("node:os");
|
||||||
|
const path = require("node:path");
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-prune-cursor-"));
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }));
|
||||||
|
const cursorRoot = path.join(
|
||||||
|
tempDir,
|
||||||
|
"resources",
|
||||||
|
"app.asar.unpacked",
|
||||||
|
"node_modules",
|
||||||
|
"@cursor",
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-linux-arm64"), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(cursorRoot, "sdk-linux-x64"), { recursive: true });
|
||||||
|
|
||||||
|
const removed = pruneCursorSdkPlatformPackages({
|
||||||
|
electronPlatformName: "linux",
|
||||||
|
arch: 1,
|
||||||
|
appOutDir: tempDir,
|
||||||
|
packager: { appInfo: { productFilename: "netcatty" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(removed, ["sdk-linux-arm64"]);
|
||||||
|
assert.ok(!fs.existsSync(path.join(cursorRoot, "sdk-linux-arm64")));
|
||||||
|
assert.ok(fs.existsSync(path.join(cursorRoot, "sdk-linux-x64")));
|
||||||
|
});
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ test("asarUnpack keeps MCP server runtime deps unpacked", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("asarUnpack keeps Cursor SDK runtime deps unpacked", () => {
|
test("asarUnpack keeps Cursor SDK runtime deps unpacked", () => {
|
||||||
assert.ok(config.asarUnpack.includes("node_modules/@cursor/sdk/**/*"));
|
assert.ok(
|
||||||
|
!config.asarUnpack.includes("node_modules/@cursor/sdk/**/*"),
|
||||||
|
"Cursor SDK JavaScript can load from app.asar and should not be duplicated into app.asar.unpacked",
|
||||||
|
);
|
||||||
assert.ok(config.asarUnpack.includes("node_modules/@cursor/sdk-*/**/*"));
|
assert.ok(config.asarUnpack.includes("node_modules/@cursor/sdk-*/**/*"));
|
||||||
assert.ok(config.asarUnpack.includes("node_modules/sqlite3/**/*"));
|
assert.ok(config.asarUnpack.includes("node_modules/sqlite3/**/*"));
|
||||||
});
|
});
|
||||||
@@ -64,6 +67,22 @@ test("beforePack installs missing Cursor SDK platform runtime packages", () => {
|
|||||||
assert.equal(config.beforePack, "./scripts/beforePackCursorSdk.cjs");
|
assert.equal(config.beforePack, "./scripts/beforePackCursorSdk.cjs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("build.files trims release-only dependency payloads", () => {
|
||||||
|
const files = config.files;
|
||||||
|
for (const glob of [
|
||||||
|
"!node_modules/@cursor/sdk/dist/cjs/**/*",
|
||||||
|
"!node_modules/@cursor/sdk/dist/**/*.d.ts",
|
||||||
|
"!node_modules/@cursor/sdk/dist/**/*.d.ts.map",
|
||||||
|
"!node_modules/sqlite3/deps/**/*",
|
||||||
|
"!node_modules/**/docs/**/*",
|
||||||
|
"!node_modules/**/doc/**/*",
|
||||||
|
"!node_modules/**/benchmark/**/*",
|
||||||
|
"!node_modules/**/benchmarks/**/*",
|
||||||
|
]) {
|
||||||
|
assert.ok(files.includes(glob), `build.files must exclude release-only payload: ${glob}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("linux packaging uses multi-size build/icons instead of a single 1024px override", async () => {
|
test("linux packaging uses multi-size build/icons instead of a single 1024px override", async () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
config.linux.icon,
|
config.linux.icon,
|
||||||
|
|||||||
Reference in New Issue
Block a user