@simonepri/refined-antigravity-acp 0.1.0 → 0.1.2

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.
Files changed (82) hide show
  1. package/dist/bin/cli.d.ts +3 -0
  2. package/dist/bin/cli.d.ts.map +1 -0
  3. package/dist/bin/cli.js +56 -0
  4. package/dist/bin/cli.js.map +1 -0
  5. package/dist/index.server.d.ts +4 -0
  6. package/dist/index.server.d.ts.map +1 -0
  7. package/dist/index.server.js +7 -0
  8. package/dist/index.server.js.map +1 -0
  9. package/dist/src/agy-patch.d.ts +65 -0
  10. package/dist/src/agy-patch.d.ts.map +1 -0
  11. package/dist/src/agy-patch.js +351 -0
  12. package/dist/src/agy-patch.js.map +1 -0
  13. package/dist/src/auth.d.ts +27 -0
  14. package/dist/src/auth.d.ts.map +1 -0
  15. package/dist/src/auth.js +113 -0
  16. package/dist/src/auth.js.map +1 -0
  17. package/dist/src/command.d.ts +5 -0
  18. package/dist/src/command.d.ts.map +1 -0
  19. package/dist/src/command.js +74 -0
  20. package/dist/src/command.js.map +1 -0
  21. package/dist/src/connector.d.ts +188 -0
  22. package/dist/src/connector.d.ts.map +1 -0
  23. package/dist/src/connector.js +1138 -0
  24. package/dist/src/connector.js.map +1 -0
  25. package/dist/src/history.d.ts +12 -0
  26. package/dist/src/history.d.ts.map +1 -0
  27. package/dist/src/history.js +152 -0
  28. package/dist/src/history.js.map +1 -0
  29. package/dist/src/mcp-proxy.d.ts +83 -0
  30. package/dist/src/mcp-proxy.d.ts.map +1 -0
  31. package/dist/src/mcp-proxy.js +308 -0
  32. package/dist/src/mcp-proxy.js.map +1 -0
  33. package/dist/src/provider.d.ts +11 -0
  34. package/dist/src/provider.d.ts.map +1 -0
  35. package/dist/src/provider.js +31 -0
  36. package/dist/src/provider.js.map +1 -0
  37. package/dist/src/sanitize.d.ts +29 -0
  38. package/dist/src/sanitize.d.ts.map +1 -0
  39. package/dist/src/sanitize.js +237 -0
  40. package/dist/src/sanitize.js.map +1 -0
  41. package/dist/src/skills.d.ts +17 -0
  42. package/dist/src/skills.d.ts.map +1 -0
  43. package/dist/src/skills.js +140 -0
  44. package/dist/src/skills.js.map +1 -0
  45. package/dist/src/steering.d.ts +30 -0
  46. package/dist/src/steering.d.ts.map +1 -0
  47. package/dist/src/steering.js +174 -0
  48. package/dist/src/steering.js.map +1 -0
  49. package/package.json +20 -13
  50. package/paseo-plugin.json +0 -1
  51. package/readme.md +3 -5
  52. package/.fallowrc.json +0 -11
  53. package/.github/workflows/ci.yml +0 -24
  54. package/.github/workflows/release.yml +0 -41
  55. package/.oxlintignore +0 -2
  56. package/.oxlintrc.json +0 -152
  57. package/.release-please-manifest.json +0 -3
  58. package/bin/cli.ts +0 -58
  59. package/index.server.ts +0 -9
  60. package/pnpm-workspace.yaml +0 -44
  61. package/release-please-config.json +0 -13
  62. package/server/agy-patch.test.ts +0 -445
  63. package/server/agy-patch.ts +0 -445
  64. package/server/auth.test.ts +0 -51
  65. package/server/auth.ts +0 -122
  66. package/server/command.test.ts +0 -18
  67. package/server/command.ts +0 -92
  68. package/server/connector.test.ts +0 -1136
  69. package/server/connector.ts +0 -1386
  70. package/server/history.test.ts +0 -139
  71. package/server/history.ts +0 -157
  72. package/server/mcp-proxy.test.ts +0 -368
  73. package/server/mcp-proxy.ts +0 -375
  74. package/server/provider.test.ts +0 -11
  75. package/server/provider.ts +0 -37
  76. package/server/sanitize.test.ts +0 -174
  77. package/server/sanitize.ts +0 -286
  78. package/server/skills.test.ts +0 -121
  79. package/server/skills.ts +0 -183
  80. package/server/steering.test.ts +0 -405
  81. package/server/steering.ts +0 -199
  82. package/tsconfig.json +0 -14
@@ -1,445 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import zlib from "node:zlib";
5
- import { describe, expect, it, afterEach } from "vitest";
6
- import {
7
- EVENT_PROCESSOR_ENTRY,
8
- ORIGINAL_ANCHOR,
9
- PROMPT_ANCHOR,
10
- PYCACHE_PREFIX,
11
- REPLACEMENT_CODE,
12
- classifyBinary,
13
- classifyBuffer,
14
- ensurePatchedBinary,
15
- gcCache,
16
- getCacheKey,
17
- parseZipArchive,
18
- patchBinary,
19
- patchBuffer,
20
- } from "./agy-patch.js";
21
-
22
- const DUMMY_MACHO_PREFIX = Buffer.from([
23
- 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00,
24
- 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25
- ]);
26
-
27
- const SIBLING_PYC_ENTRY = `${PYCACHE_PREFIX}cpython-314.pyc`;
28
-
29
- const DECORATIVE_COMMENTS = [
30
- " # ---------------------------------------------------------------------------",
31
- " # Dynamic policy evaluation",
32
- " # ---------------------------------------------------------------------------",
33
- ].join("\n");
34
-
35
- function createEventProcessorContent(anchor = ORIGINAL_ANCHOR): string {
36
- return [
37
- "# Copyright 2026 Google LLC",
38
- "# Antigravity event processor",
39
- "class EventProcessor:",
40
- " async def _process(self, tsu):",
41
- " if False:",
42
- " pass",
43
- anchor,
44
- ' if tsu.HasField("error"): await self.step_queue.put(None)',
45
- "",
46
- DECORATIVE_COMMENTS,
47
- "",
48
- " async def _finish(self):",
49
- " return True",
50
- ].join("\n");
51
- }
52
-
53
- interface SyntheticZipEntry {
54
- name: string;
55
- data: string | Buffer;
56
- method?: number;
57
- }
58
-
59
- interface BuildZipOptions {
60
- prefix?: Buffer;
61
- trailer?: Buffer;
62
- }
63
-
64
- function buildSyntheticZip(entries: SyntheticZipEntry[], options: BuildZipOptions = {}): Buffer {
65
- const prefix = options.prefix ?? DUMMY_MACHO_PREFIX;
66
- const trailer = options.trailer ?? Buffer.alloc(0);
67
-
68
- const localChunks: Buffer[] = [];
69
- const cdChunks: Buffer[] = [];
70
- let currentOffset = 0;
71
-
72
- const cdEntries: Array<{
73
- nameBuf: Buffer;
74
- dataLen: number;
75
- crc: number;
76
- method: number;
77
- localOffset: number;
78
- }> = [];
79
-
80
- for (const entry of entries) {
81
- const nameBuf = Buffer.from(entry.name, "utf8");
82
- const dataBuf = Buffer.isBuffer(entry.data) ? entry.data : Buffer.from(entry.data, "utf8");
83
- const crc = zlib.crc32(dataBuf);
84
- const method = entry.method ?? 0;
85
-
86
- const locHdr = Buffer.alloc(30);
87
- locHdr.writeUInt32LE(0x04034b50, 0);
88
- locHdr.writeUInt16LE(20, 4);
89
- locHdr.writeUInt16LE(0, 6);
90
- locHdr.writeUInt16LE(method, 8);
91
- locHdr.writeUInt16LE(0, 10);
92
- locHdr.writeUInt16LE(0, 12);
93
- locHdr.writeUInt32LE(crc, 14);
94
- locHdr.writeUInt32LE(dataBuf.length, 18);
95
- locHdr.writeUInt32LE(dataBuf.length, 22);
96
- locHdr.writeUInt16LE(nameBuf.length, 26);
97
- locHdr.writeUInt16LE(0, 28);
98
-
99
- const localOffset = currentOffset;
100
- localChunks.push(locHdr, nameBuf, dataBuf);
101
- currentOffset += 30 + nameBuf.length + dataBuf.length;
102
-
103
- cdEntries.push({
104
- nameBuf,
105
- dataLen: dataBuf.length,
106
- crc,
107
- method,
108
- localOffset,
109
- });
110
- }
111
-
112
- const cdStart = currentOffset;
113
- let cdSize = 0;
114
- for (const cde of cdEntries) {
115
- const cdHdr = Buffer.alloc(46);
116
- cdHdr.writeUInt32LE(0x02014b50, 0);
117
- cdHdr.writeUInt16LE(0x031e, 4);
118
- cdHdr.writeUInt16LE(20, 6);
119
- cdHdr.writeUInt16LE(0, 8);
120
- cdHdr.writeUInt16LE(cde.method, 10);
121
- cdHdr.writeUInt16LE(0, 12);
122
- cdHdr.writeUInt16LE(0, 14);
123
- cdHdr.writeUInt32LE(cde.crc, 16);
124
- cdHdr.writeUInt32LE(cde.dataLen, 20);
125
- cdHdr.writeUInt32LE(cde.dataLen, 24);
126
- cdHdr.writeUInt16LE(cde.nameBuf.length, 28);
127
- cdHdr.writeUInt16LE(0, 30);
128
- cdHdr.writeUInt16LE(0, 32);
129
- cdHdr.writeUInt16LE(0, 34);
130
- cdHdr.writeUInt16LE(0, 36);
131
- cdHdr.writeUInt32LE((0o100644 << 16) >>> 0, 38);
132
- cdHdr.writeUInt32LE(cde.localOffset, 42);
133
-
134
- cdChunks.push(cdHdr, cde.nameBuf);
135
- cdSize += 46 + cde.nameBuf.length;
136
- }
137
-
138
- const eocd = Buffer.alloc(22);
139
- eocd.writeUInt32LE(0x06054b50, 0);
140
- eocd.writeUInt16LE(0, 4);
141
- eocd.writeUInt16LE(0, 6);
142
- eocd.writeUInt16LE(entries.length, 8);
143
- eocd.writeUInt16LE(entries.length, 10);
144
- eocd.writeUInt32LE(cdSize, 12);
145
- eocd.writeUInt32LE(cdStart, 16);
146
- eocd.writeUInt16LE(0, 20);
147
-
148
- return Buffer.concat([prefix, ...localChunks, ...cdChunks, eocd, trailer]);
149
- }
150
-
151
- describe("agy-patch binary engine", () => {
152
- const tempDirs: string[] = [];
153
-
154
- function makeTempDir(prefix = "agy-patch-test"): string {
155
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), `${prefix}-`));
156
- tempDirs.push(dir);
157
- return dir;
158
- }
159
-
160
- afterEach(() => {
161
- delete process.env.PASEO_AGY_NO_PATCH;
162
- for (const dir of tempDirs) {
163
- fs.rmSync(dir, { recursive: true, force: true });
164
- }
165
- tempDirs.length = 0;
166
- });
167
-
168
- describe("classification", () => {
169
- it("classifies valid unpatched fixture as patchable", () => {
170
- const buf = buildSyntheticZip([
171
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
172
- { name: SIBLING_PYC_ENTRY, data: Buffer.from("dummy-pyc-bytecode") },
173
- ]);
174
- expect(classifyBuffer(buf)).toBe("patchable");
175
- });
176
-
177
- it("classifies fixed fixture as already-fixed", () => {
178
- const fixedContent = createEventProcessorContent(REPLACEMENT_CODE);
179
- const buf = buildSyntheticZip([{ name: EVENT_PROCESSOR_ENTRY, data: fixedContent }]);
180
- expect(classifyBuffer(buf)).toBe("already-fixed");
181
- });
182
-
183
- it("classifies missing event_processor.py as entry-missing", () => {
184
- const buf = buildSyntheticZip([{ name: "some/other/file.py", data: "print(123)" }]);
185
- expect(classifyBuffer(buf)).toBe("entry-missing");
186
- });
187
-
188
- it("classifies file without anchor or replacement as unrecognized", () => {
189
- const buf = buildSyntheticZip([{ name: EVENT_PROCESSOR_ENTRY, data: "def hello(): pass" }]);
190
- expect(classifyBuffer(buf)).toBe("unrecognized");
191
- });
192
-
193
- it("classifies non-stored (compressed) entry as unrecognized", () => {
194
- const buf = buildSyntheticZip([
195
- {
196
- name: EVENT_PROCESSOR_ENTRY,
197
- data: createEventProcessorContent(),
198
- method: 8,
199
- },
200
- ]);
201
- expect(classifyBuffer(buf)).toBe("unrecognized");
202
- });
203
-
204
- it("classifies entry without decorative comments as unrecognized", () => {
205
- const noComments = ["class EventProcessor:", ORIGINAL_ANCHOR, " pass"].join("\n");
206
- const buf = buildSyntheticZip([{ name: EVENT_PROCESSOR_ENTRY, data: noComments }]);
207
- expect(classifyBuffer(buf)).toBe("unrecognized");
208
- });
209
-
210
- it("classifies non-zip buffer as unrecognized", () => {
211
- const buf = Buffer.from("not a zip archive");
212
- expect(classifyBuffer(buf)).toBe("unrecognized");
213
- });
214
- });
215
-
216
- describe("patchBuffer", () => {
217
- it("patches in-place, preserving identical length, updating CRC, and renaming .pyc", () => {
218
- const originalContent = createEventProcessorContent();
219
- const pycData = Buffer.from("precompiled-cpython-bytecode");
220
-
221
- const buf = buildSyntheticZip(
222
- [
223
- { name: EVENT_PROCESSOR_ENTRY, data: originalContent },
224
- { name: SIBLING_PYC_ENTRY, data: pycData },
225
- ],
226
- {
227
- prefix: Buffer.alloc(128, 0xef),
228
- trailer: Buffer.alloc(64, 0x99),
229
- },
230
- );
231
-
232
- const originalLength = buf.length;
233
- const initialClassification = classifyBuffer(buf);
234
- expect(initialClassification).toBe("patchable");
235
-
236
- const result = patchBuffer(buf);
237
- expect(result.patched).toBe(true);
238
- expect(result.classification).toBe("already-fixed");
239
-
240
- // 1. Identical file length
241
- expect(buf.length).toBe(originalLength);
242
-
243
- // 2. Re-classification returns already-fixed
244
- expect(classifyBuffer(buf)).toBe("already-fixed");
245
-
246
- // 3. Parse zip and verify headers
247
- const zip = parseZipArchive(buf);
248
- expect(zip).not.toBeNull();
249
-
250
- // event_processor.py checks
251
- const pyEntry = zip!.entries.get(EVENT_PROCESSOR_ENTRY);
252
- expect(pyEntry).toBeDefined();
253
-
254
- const absLocalHdr = zip!.baseOffset + pyEntry!.localHeaderOffset;
255
- const localCrc = buf.readUInt32LE(absLocalHdr + 14);
256
- const cdCrc = pyEntry!.crc32;
257
- expect(localCrc).toBe(cdCrc);
258
-
259
- // Extract modified entry data and verify CRC matches zlib.crc32
260
- const dataOffset = absLocalHdr + 30 + pyEntry!.nameLength + pyEntry!.extraLength;
261
- const modifiedData = buf.subarray(dataOffset, dataOffset + pyEntry!.uncompSize);
262
- const computedCrc = zlib.crc32(modifiedData);
263
- expect(cdCrc).toBe(computedCrc);
264
-
265
- const modifiedText = modifiedData.toString("utf8");
266
- expect(modifiedText).toContain("STATE_WAITING_FOR_TASKS");
267
- expect(modifiedText).not.toContain(ORIGINAL_ANCHOR);
268
-
269
- // 4. Sibling .pyc is renamed to .bak in both CD and Local Header
270
- const expectedBakName = SIBLING_PYC_ENTRY.replace(/\.pyc$/, ".bak");
271
- const bakEntry = zip!.entries.get(expectedBakName);
272
- expect(bakEntry).toBeDefined();
273
-
274
- const bakLocalHdr = zip!.baseOffset + bakEntry!.localHeaderOffset;
275
- const bakLocalName = buf
276
- .subarray(bakLocalHdr + 30, bakLocalHdr + 30 + bakEntry!.nameLength)
277
- .toString("utf8");
278
- expect(bakLocalName).toBe(expectedBakName);
279
-
280
- // Original .pyc entry should no longer be in zip map
281
- expect(zip!.entries.get(SIBLING_PYC_ENTRY)).toBeUndefined();
282
- });
283
-
284
- it("is idempotent and avoids re-patching an already-fixed buffer", () => {
285
- const buf = buildSyntheticZip([
286
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
287
- { name: SIBLING_PYC_ENTRY, data: Buffer.from("dummy") },
288
- ]);
289
-
290
- const first = patchBuffer(buf);
291
- expect(first.patched).toBe(true);
292
-
293
- const second = patchBuffer(buf);
294
- expect(second.patched).toBe(false);
295
- expect(second.classification).toBe("already-fixed");
296
- });
297
-
298
- it("supports prompt anchor formatting with decorative comments", () => {
299
- const promptContent = ["class EventProcessor:", PROMPT_ANCHOR, DECORATIVE_COMMENTS].join(
300
- "\n",
301
- );
302
-
303
- const buf = buildSyntheticZip([{ name: EVENT_PROCESSOR_ENTRY, data: promptContent }]);
304
-
305
- expect(classifyBuffer(buf)).toBe("patchable");
306
- const res = patchBuffer(buf);
307
- expect(res.patched).toBe(true);
308
- expect(classifyBuffer(buf)).toBe("already-fixed");
309
- });
310
- });
311
-
312
- describe("patchBinary and caching", () => {
313
- it("respects PASEO_AGY_NO_PATCH=1 escape hatch", () => {
314
- process.env.PASEO_AGY_NO_PATCH = "1";
315
- const dir = makeTempDir();
316
- const binPath = path.join(dir, "agy_acp_server.par");
317
- const buf = buildSyntheticZip([
318
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
319
- ]);
320
- fs.writeFileSync(binPath, buf);
321
-
322
- const res = patchBinary(binPath, { skipSign: true });
323
- expect(res.patched).toBe(false);
324
- expect(res.path).toBe(binPath);
325
- });
326
-
327
- it("creates a clone in cache dir, patches clone, and leaves source intact", () => {
328
- const dir = makeTempDir();
329
- const cacheBase = path.join(dir, "cache");
330
- const binPath = path.join(dir, "agy_acp_server.par");
331
-
332
- const originalBuf = buildSyntheticZip([
333
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
334
- { name: SIBLING_PYC_ENTRY, data: Buffer.from("bytecode") },
335
- ]);
336
- fs.writeFileSync(binPath, originalBuf);
337
-
338
- const res = patchBinary(binPath, {
339
- cacheBaseDir: cacheBase,
340
- skipSign: true,
341
- });
342
-
343
- expect(res.patched).toBe(true);
344
- expect(res.cached).toBe(false);
345
- expect(res.classification).toBe("already-fixed");
346
- expect(res.path).not.toBe(binPath);
347
- expect(fs.existsSync(res.path)).toBe(true);
348
-
349
- // Verify original binary is unmodified
350
- const srcBuf = fs.readFileSync(binPath);
351
- expect(classifyBuffer(srcBuf)).toBe("patchable");
352
-
353
- // Verify cached clone is patched
354
- const cloneBuf = fs.readFileSync(res.path);
355
- expect(classifyBuffer(cloneBuf)).toBe("already-fixed");
356
- });
357
-
358
- it("hits cache on second call and avoids re-patching", () => {
359
- const dir = makeTempDir();
360
- const cacheBase = path.join(dir, "cache");
361
- const binPath = path.join(dir, "agy_acp_server.par");
362
-
363
- const originalBuf = buildSyntheticZip([
364
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
365
- { name: SIBLING_PYC_ENTRY, data: Buffer.from("bytecode") },
366
- ]);
367
- fs.writeFileSync(binPath, originalBuf);
368
-
369
- const first = patchBinary(binPath, {
370
- cacheBaseDir: cacheBase,
371
- skipSign: true,
372
- });
373
- expect(first.patched).toBe(true);
374
- expect(first.cached).toBe(false);
375
-
376
- const second = patchBinary(binPath, {
377
- cacheBaseDir: cacheBase,
378
- skipSign: true,
379
- });
380
- expect(second.patched).toBe(false);
381
- expect(second.cached).toBe(true);
382
- expect(second.path).toBe(first.path);
383
-
384
- // ensurePatchedBinary returns the cached path
385
- const ensured = ensurePatchedBinary(binPath, {
386
- cacheBaseDir: cacheBase,
387
- skipSign: true,
388
- });
389
- expect(ensured).toBe(first.path);
390
- });
391
-
392
- it("returns original path without patching for unrecognized binaries", () => {
393
- const dir = makeTempDir();
394
- const binPath = path.join(dir, "agy_acp_server.par");
395
- fs.writeFileSync(binPath, Buffer.from("random unpatchable data"));
396
-
397
- const res = patchBinary(binPath, { skipSign: true });
398
- expect(res.patched).toBe(false);
399
- expect(res.classification).toBe("unrecognized");
400
- expect(res.path).toBe(binPath);
401
- });
402
-
403
- it("garbage collects old cache directories while retaining the active key", () => {
404
- const dir = makeTempDir();
405
- const cacheBase = path.join(dir, "cache");
406
- const activeKey = "active-key-123";
407
- const oldKey1 = "old-key-456";
408
- const oldKey2 = "old-key-789";
409
-
410
- fs.mkdirSync(path.join(cacheBase, activeKey), { recursive: true });
411
- fs.mkdirSync(path.join(cacheBase, oldKey1), { recursive: true });
412
- fs.mkdirSync(path.join(cacheBase, oldKey2), { recursive: true });
413
-
414
- gcCache(cacheBase, activeKey);
415
-
416
- expect(fs.existsSync(path.join(cacheBase, activeKey))).toBe(true);
417
- expect(fs.existsSync(path.join(cacheBase, oldKey1))).toBe(false);
418
- expect(fs.existsSync(path.join(cacheBase, oldKey2))).toBe(false);
419
- });
420
-
421
- it("generates deterministic cache keys", () => {
422
- const dir = makeTempDir();
423
- const testFile = path.join(dir, "binary");
424
- fs.writeFileSync(testFile, "test");
425
- const stat = fs.statSync(testFile);
426
-
427
- const key1 = getCacheKey(testFile, stat);
428
- const key2 = getCacheKey(testFile, stat);
429
- expect(key1).toBe(key2);
430
- expect(typeof key1).toBe("string");
431
- expect(key1.length).toBe(32);
432
- });
433
-
434
- it("correctly classifies binary from filesystem via classifyBinary", () => {
435
- const dir = makeTempDir();
436
- const binPath = path.join(dir, "agy_acp_server.par");
437
- const buf = buildSyntheticZip([
438
- { name: EVENT_PROCESSOR_ENTRY, data: createEventProcessorContent() },
439
- ]);
440
- fs.writeFileSync(binPath, buf);
441
-
442
- expect(classifyBinary(binPath)).toBe("patchable");
443
- });
444
- });
445
- });