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 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): void;
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
- if (e.isDirectory()) {
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 (e.isFile()) {
113
- const stat = fs.statSync(full);
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: stat.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
- curr[leaf] = {
138
- size: f.size,
139
- offset: String(currentOffset),
140
- executable: f.executable,
141
- };
142
- currentOffset += f.size;
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
  }
@@ -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
- const backupPath = `${asarPath}.bak`;
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paseo-acp-agy",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "ACP (Agent Client Protocol) provider for Google Antigravity in Paseo and Zed",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",