paseo-acp-agy 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/asar.d.ts +11 -2
- package/dist/asar.js +65 -20
- package/dist/paseo-patcher.js +38 -4
- package/package.json +1 -1
package/dist/asar.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export interface AsarNode {
|
|
|
4
4
|
offset?: string;
|
|
5
5
|
executable?: boolean;
|
|
6
6
|
unpacked?: boolean;
|
|
7
|
+
link?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ExtractResult {
|
|
10
|
+
unpackedPaths: string[];
|
|
11
|
+
originalHeader: AsarNode;
|
|
7
12
|
}
|
|
8
13
|
/**
|
|
9
14
|
* Reads and parses the ASAR archive header and returns the root node and base offset for data.
|
|
@@ -20,9 +25,13 @@ export declare function listPackage(archivePath: string): string[];
|
|
|
20
25
|
* Pure TypeScript, zero-dependency ASAR archive extractor.
|
|
21
26
|
* Compatible with all Node.js versions (Node 18, 20, 22, 24+) across Windows, macOS, and Linux.
|
|
22
27
|
*/
|
|
23
|
-
export declare function extractAll(archivePath: string, destDir: string):
|
|
28
|
+
export declare function extractAll(archivePath: string, destDir: string): ExtractResult;
|
|
29
|
+
export interface CreatePackageOptions {
|
|
30
|
+
unpackedPaths?: string[];
|
|
31
|
+
originalHeader?: AsarNode;
|
|
32
|
+
}
|
|
24
33
|
/**
|
|
25
34
|
* Pure TypeScript, zero-dependency ASAR archive builder.
|
|
26
35
|
* Compatible with all Node.js versions (Node 18, 20, 22, 24+) across Windows, macOS, and Linux.
|
|
27
36
|
*/
|
|
28
|
-
export declare function createPackage(srcDir: string, destFile: string): Promise<void>;
|
|
37
|
+
export declare function createPackage(srcDir: string, destFile: string, options?: CreatePackageOptions): Promise<void>;
|
package/dist/asar.js
CHANGED
|
@@ -61,6 +61,7 @@ export function listPackage(archivePath) {
|
|
|
61
61
|
export function extractAll(archivePath, destDir) {
|
|
62
62
|
const { header, dataBaseOffset } = readArchiveHeader(archivePath);
|
|
63
63
|
const fd = fs.openSync(archivePath, "r");
|
|
64
|
+
const unpackedPaths = [];
|
|
64
65
|
try {
|
|
65
66
|
function extractNode(node, currentPath, relParts = []) {
|
|
66
67
|
if (node.files) {
|
|
@@ -69,6 +70,20 @@ export function extractAll(archivePath, destDir) {
|
|
|
69
70
|
extractNode(child, path.join(currentPath, name), [...relParts, name]);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
else if (node.link) {
|
|
74
|
+
fs.mkdirSync(path.dirname(currentPath), { recursive: true });
|
|
75
|
+
try {
|
|
76
|
+
fs.unlinkSync(currentPath);
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
fs.symlinkSync(node.link, currentPath);
|
|
80
|
+
}
|
|
81
|
+
else if (node.unpacked) {
|
|
82
|
+
// Unpacked files belong in archive.unpacked and must NOT be copied into destDir,
|
|
83
|
+
// so that re-packing does not duplicate external binaries into the asar payload.
|
|
84
|
+
const relPath = relParts.join("/");
|
|
85
|
+
unpackedPaths.push(relPath);
|
|
86
|
+
}
|
|
72
87
|
else if (node.size !== undefined && node.offset !== undefined) {
|
|
73
88
|
const fileOffset = dataBaseOffset + parseInt(node.offset, 10);
|
|
74
89
|
const fileBuf = Buffer.alloc(node.size);
|
|
@@ -80,15 +95,9 @@ export function extractAll(archivePath, destDir) {
|
|
|
80
95
|
mode: node.executable ? 0o755 : 0o644,
|
|
81
96
|
});
|
|
82
97
|
}
|
|
83
|
-
else if (node.unpacked) {
|
|
84
|
-
const unpackedSrc = path.join(archivePath + ".unpacked", ...relParts);
|
|
85
|
-
if (fs.existsSync(unpackedSrc)) {
|
|
86
|
-
fs.mkdirSync(path.dirname(currentPath), { recursive: true });
|
|
87
|
-
fs.copyFileSync(unpackedSrc, currentPath);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
98
|
}
|
|
91
99
|
extractNode(header, destDir);
|
|
100
|
+
return { unpackedPaths, originalHeader: header };
|
|
92
101
|
}
|
|
93
102
|
finally {
|
|
94
103
|
fs.closeSync(fd);
|
|
@@ -98,7 +107,7 @@ export function extractAll(archivePath, destDir) {
|
|
|
98
107
|
* Pure TypeScript, zero-dependency ASAR archive builder.
|
|
99
108
|
* Compatible with all Node.js versions (Node 18, 20, 22, 24+) across Windows, macOS, and Linux.
|
|
100
109
|
*/
|
|
101
|
-
export async function createPackage(srcDir, destFile) {
|
|
110
|
+
export async function createPackage(srcDir, destFile, options) {
|
|
102
111
|
const files = [];
|
|
103
112
|
function walk(dir, rel = "") {
|
|
104
113
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -106,16 +115,25 @@ export async function createPackage(srcDir, destFile) {
|
|
|
106
115
|
for (const e of entries) {
|
|
107
116
|
const entryRel = rel ? `${rel}/${e.name}` : e.name;
|
|
108
117
|
const full = path.join(dir, e.name);
|
|
109
|
-
|
|
118
|
+
const lstat = fs.lstatSync(full);
|
|
119
|
+
if (lstat.isSymbolicLink()) {
|
|
120
|
+
files.push({
|
|
121
|
+
relPath: entryRel,
|
|
122
|
+
fullPath: full,
|
|
123
|
+
size: 0,
|
|
124
|
+
isLink: true,
|
|
125
|
+
linkTarget: fs.readlinkSync(full),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else if (lstat.isDirectory()) {
|
|
110
129
|
walk(full, entryRel);
|
|
111
130
|
}
|
|
112
|
-
else if (
|
|
113
|
-
const
|
|
114
|
-
const executable = Boolean(stat.mode & 0o111);
|
|
131
|
+
else if (lstat.isFile()) {
|
|
132
|
+
const executable = Boolean(lstat.mode & 0o111);
|
|
115
133
|
files.push({
|
|
116
134
|
relPath: entryRel,
|
|
117
135
|
fullPath: full,
|
|
118
|
-
size:
|
|
136
|
+
size: lstat.size,
|
|
119
137
|
executable: executable || undefined,
|
|
120
138
|
});
|
|
121
139
|
}
|
|
@@ -134,12 +152,39 @@ export async function createPackage(srcDir, destFile) {
|
|
|
134
152
|
curr = curr[parts[i]].files;
|
|
135
153
|
}
|
|
136
154
|
const leaf = parts[parts.length - 1];
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
155
|
+
if (f.isLink) {
|
|
156
|
+
curr[leaf] = { link: f.linkTarget };
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
curr[leaf] = {
|
|
160
|
+
size: f.size,
|
|
161
|
+
offset: String(currentOffset),
|
|
162
|
+
executable: f.executable,
|
|
163
|
+
};
|
|
164
|
+
currentOffset += f.size;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Restore unpacked file references from original archive if provided
|
|
168
|
+
if (options?.unpackedPaths && options?.originalHeader) {
|
|
169
|
+
for (const relPath of options.unpackedPaths) {
|
|
170
|
+
const parts = relPath.split("/");
|
|
171
|
+
let origNode = options.originalHeader;
|
|
172
|
+
for (const p of parts) {
|
|
173
|
+
origNode = origNode?.files?.[p];
|
|
174
|
+
}
|
|
175
|
+
let curr = header.files;
|
|
176
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
177
|
+
if (!curr[parts[i]]) {
|
|
178
|
+
curr[parts[i]] = { files: {} };
|
|
179
|
+
}
|
|
180
|
+
curr = curr[parts[i]].files;
|
|
181
|
+
}
|
|
182
|
+
const leaf = parts[parts.length - 1];
|
|
183
|
+
curr[leaf] = {
|
|
184
|
+
size: origNode?.size ?? 0,
|
|
185
|
+
unpacked: true,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
143
188
|
}
|
|
144
189
|
const jsonBuf = Buffer.from(JSON.stringify(header), "utf8");
|
|
145
190
|
const alignedJsonLen = (jsonBuf.length + 3) & ~3;
|
|
@@ -159,7 +204,7 @@ export async function createPackage(srcDir, destFile) {
|
|
|
159
204
|
fs.writeSync(fd, sizePickle);
|
|
160
205
|
fs.writeSync(fd, headerPickle);
|
|
161
206
|
for (const f of files) {
|
|
162
|
-
if (f.size > 0) {
|
|
207
|
+
if (!f.isLink && f.size > 0) {
|
|
163
208
|
const content = fs.readFileSync(f.fullPath);
|
|
164
209
|
fs.writeSync(fd, content);
|
|
165
210
|
}
|
package/dist/paseo-patcher.js
CHANGED
|
@@ -798,7 +798,7 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
798
798
|
return { success: false, changes: [], error: `Asar archive not found: ${asarPath}` };
|
|
799
799
|
}
|
|
800
800
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "paseo-asar-extract-"));
|
|
801
|
-
extractAll(asarPath, tempDir);
|
|
801
|
+
const { unpackedPaths, originalHeader } = extractAll(asarPath, tempDir);
|
|
802
802
|
// Look for server directory in extracted files
|
|
803
803
|
const serverCandidates = [
|
|
804
804
|
path.join(tempDir, "node_modules", "@getpaseo", "server"),
|
|
@@ -841,8 +841,27 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
841
841
|
return { success: true, changes: [] };
|
|
842
842
|
}
|
|
843
843
|
changes.push(...patchResult.changes);
|
|
844
|
-
// Create backup if not already present
|
|
845
|
-
|
|
844
|
+
// Create backup if not already present.
|
|
845
|
+
// On macOS, never write backup inside .app bundle (e.g. app.asar.bak) because it breaks code signing seals.
|
|
846
|
+
let backupPath;
|
|
847
|
+
if (process.platform === "darwin" && asarPath.includes(".app")) {
|
|
848
|
+
const backupDir = path.join(os.homedir(), ".paseo", "backups");
|
|
849
|
+
if (!fs.existsSync(backupDir)) {
|
|
850
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
851
|
+
}
|
|
852
|
+
backupPath = path.join(backupDir, "app.asar.bak");
|
|
853
|
+
// Clean up any orphan backup inside the .app bundle from previous runs
|
|
854
|
+
const orphanBak = `${asarPath}.bak`;
|
|
855
|
+
if (fs.existsSync(orphanBak)) {
|
|
856
|
+
try {
|
|
857
|
+
fs.unlinkSync(orphanBak);
|
|
858
|
+
}
|
|
859
|
+
catch { }
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
backupPath = `${asarPath}.bak`;
|
|
864
|
+
}
|
|
846
865
|
if (!fs.existsSync(backupPath)) {
|
|
847
866
|
try {
|
|
848
867
|
fs.copyFileSync(asarPath, backupPath);
|
|
@@ -853,7 +872,7 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
853
872
|
}
|
|
854
873
|
}
|
|
855
874
|
tempAsar = path.join(os.tmpdir(), `app-${Date.now()}.asar`);
|
|
856
|
-
await createPackage(tempDir, tempAsar);
|
|
875
|
+
await createPackage(tempDir, tempAsar, { unpackedPaths, originalHeader });
|
|
857
876
|
// Replace original archive with locked file handling for Windows
|
|
858
877
|
try {
|
|
859
878
|
fs.copyFileSync(tempAsar, asarPath);
|
|
@@ -875,6 +894,21 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
875
894
|
throw copyErr;
|
|
876
895
|
}
|
|
877
896
|
}
|
|
897
|
+
// On macOS, re-sign application bundle if inside an .app directory to satisfy Gatekeeper
|
|
898
|
+
if (process.platform === "darwin" && asarPath.includes(".app")) {
|
|
899
|
+
const appIndex = asarPath.indexOf(".app");
|
|
900
|
+
const appBundlePath = asarPath.slice(0, appIndex + 4);
|
|
901
|
+
try {
|
|
902
|
+
execFileSync("codesign", ["--force", "--deep", "--sign", "-", appBundlePath], {
|
|
903
|
+
stdio: "ignore",
|
|
904
|
+
timeout: 15000,
|
|
905
|
+
});
|
|
906
|
+
changes.push(`Re-signed macOS bundle at ${appBundlePath}`);
|
|
907
|
+
}
|
|
908
|
+
catch (signErr) {
|
|
909
|
+
logger.warn(`Could not re-sign ${appBundlePath}`, { error: String(signErr) });
|
|
910
|
+
}
|
|
911
|
+
}
|
|
878
912
|
return { success: true, changes };
|
|
879
913
|
}
|
|
880
914
|
catch (err) {
|