cadet-agent 0.15.1 → 0.15.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/package.json +35 -35
- package/src/install.mjs +76 -4
- package/src/upgrades.mjs +77 -0
package/package.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "cadet-agent",
|
|
3
|
-
"version": "0.15.
|
|
4
|
-
"description": "Cross-IDE agent framework for Unity/C# game-development — one-command install",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"cadet-agent": "bin/cli.mjs"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "node --test test/*.test.mjs"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"bin/",
|
|
14
|
-
"src/"
|
|
15
|
-
],
|
|
16
|
-
"keywords": [
|
|
17
|
-
"cadet",
|
|
18
|
-
"cadet-agent",
|
|
19
|
-
"unity",
|
|
20
|
-
"game-development",
|
|
21
|
-
"ai-agent",
|
|
22
|
-
"copilot",
|
|
23
|
-
"cursor",
|
|
24
|
-
"claude-code"
|
|
25
|
-
],
|
|
26
|
-
"license": "CC-BY-4.0",
|
|
27
|
-
"repository": {
|
|
28
|
-
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/naishtech/cadet-agent.git"
|
|
30
|
-
},
|
|
31
|
-
"homepage": "https://github.com/naishtech/cadet-agent#readme",
|
|
32
|
-
"engines": {
|
|
33
|
-
"node": ">=18.0.0"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "cadet-agent",
|
|
3
|
+
"version": "0.15.4",
|
|
4
|
+
"description": "Cross-IDE agent framework for Unity/C# game-development — one-command install",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cadet-agent": "bin/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test test/*.test.mjs"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"src/"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cadet",
|
|
18
|
+
"cadet-agent",
|
|
19
|
+
"unity",
|
|
20
|
+
"game-development",
|
|
21
|
+
"ai-agent",
|
|
22
|
+
"copilot",
|
|
23
|
+
"cursor",
|
|
24
|
+
"claude-code"
|
|
25
|
+
],
|
|
26
|
+
"license": "CC-BY-4.0",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/naishtech/cadet-agent.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/naishtech/cadet-agent#readme",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/install.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { createWriteStream, mkdirSync, readFileSync, unlinkSync, existsSync, rea
|
|
|
2
2
|
import { join, dirname, relative } from 'node:path';
|
|
3
3
|
import { inflateRawSync } from 'node:zlib';
|
|
4
4
|
import { Readable } from 'node:stream';
|
|
5
|
+
import { runUpgrades } from './upgrades.mjs';
|
|
5
6
|
|
|
6
7
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
7
8
|
|
|
@@ -274,13 +275,13 @@ function walkDir(dir, fn) {
|
|
|
274
275
|
|
|
275
276
|
function deleteObsoleteManagedFiles(targetDir, managedPaths, zipFilenames) {
|
|
276
277
|
const normalizedZip = new Set(
|
|
277
|
-
zipFilenames.map(f => f.replace(
|
|
278
|
+
zipFilenames.map(f => f.replace(/^\.\//, '').replace(/\\/g, '/'))
|
|
278
279
|
);
|
|
279
280
|
const deleted = [];
|
|
280
281
|
|
|
281
282
|
for (const managed of managedPaths) {
|
|
282
|
-
const mn = managed.replace(
|
|
283
|
-
const managedPath = join(targetDir, managed.replace(
|
|
283
|
+
const mn = managed.replace(/^\.\//, '').replace(/\\/g, '/');
|
|
284
|
+
const managedPath = join(targetDir, managed.replace(/^\.\//, ''));
|
|
284
285
|
const stat = (() => { try { return statSync(managedPath); } catch { return null; } })();
|
|
285
286
|
if (!stat) continue;
|
|
286
287
|
|
|
@@ -340,7 +341,61 @@ async function extractZipWithManifest(buf, targetDir, { preserved, managed }) {
|
|
|
340
341
|
// Delete obsolete managed files no longer in the zip (renamed/removed managed paths)
|
|
341
342
|
const deleted = deleteObsoleteManagedFiles(targetDir, managed, zipFilenames);
|
|
342
343
|
|
|
343
|
-
return { updated, preserved: preserved_list, added, deleted };
|
|
344
|
+
return { updated, preserved: preserved_list, added, deleted, zipFilenames };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ── Removed-managed-path cleanup ─────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
export function findManagedPathsInZip(buf) {
|
|
350
|
+
try {
|
|
351
|
+
const eocdOff = findEocd(buf);
|
|
352
|
+
const cdSize = read32(buf, eocdOff + 12);
|
|
353
|
+
const cdOff = read32(buf, eocdOff + 16);
|
|
354
|
+
|
|
355
|
+
for (const entry of centralDirectoryEntries(buf, cdOff, cdSize)) {
|
|
356
|
+
const fn = entry.filename.replace(/^\.\//, '').replace(/\\/g, '/');
|
|
357
|
+
if (fn === '.cadet/agent/core/FrameworkManifest.json') {
|
|
358
|
+
// Extract just this one entry to read managedPaths
|
|
359
|
+
const lfhFilenameLen = read16(buf, entry.localHeaderOff + 26);
|
|
360
|
+
const lfhExtraLen = read16(buf, entry.localHeaderOff + 28);
|
|
361
|
+
const dataStart = entry.localHeaderOff + 30 + lfhFilenameLen + lfhExtraLen;
|
|
362
|
+
const compressed = buf.subarray(dataStart, dataStart + entry.compressedSize);
|
|
363
|
+
let data;
|
|
364
|
+
if (entry.method === 0) data = compressed;
|
|
365
|
+
else if (entry.method === 8) data = inflateRawSync(compressed);
|
|
366
|
+
else break;
|
|
367
|
+
const manifest = JSON.parse(data.toString('utf-8'));
|
|
368
|
+
return manifest.managedPaths || [];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
} catch {
|
|
372
|
+
// Not a valid zip or manifest not found
|
|
373
|
+
}
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function deleteRemovedManagedPaths(targetDir, oldManaged, newManaged) {
|
|
378
|
+
const newSet = new Set(newManaged.map(p => p.replace(/^\.\//, '').replace(/\\/g, '/')));
|
|
379
|
+
const deleted = [];
|
|
380
|
+
|
|
381
|
+
for (const old of oldManaged) {
|
|
382
|
+
const oldNorm = old.replace(/^\.\//, '').replace(/\\/g, '/');
|
|
383
|
+
if (newSet.has(oldNorm)) continue;
|
|
384
|
+
|
|
385
|
+
// This path was in the old manifest but is absent from the new one — remove it
|
|
386
|
+
const fullPath = join(targetDir, old.replace(/^\.\//, ''));
|
|
387
|
+
const st = (() => { try { return statSync(fullPath); } catch { return null; } })();
|
|
388
|
+
if (!st) continue;
|
|
389
|
+
|
|
390
|
+
if (st.isFile()) {
|
|
391
|
+
try { unlinkSync(fullPath); deleted.push(fullPath); } catch {}
|
|
392
|
+
} else if (st.isDirectory()) {
|
|
393
|
+
walkDir(fullPath, (filePath) => {
|
|
394
|
+
try { unlinkSync(filePath); deleted.push(filePath); } catch {}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return deleted;
|
|
344
399
|
}
|
|
345
400
|
|
|
346
401
|
// ── Public sync entry ───────────────────────────────────────────────────────
|
|
@@ -384,6 +439,23 @@ export async function sync(targetDir, opts = {}) {
|
|
|
384
439
|
managed: existingManifest.managedPaths || [],
|
|
385
440
|
});
|
|
386
441
|
|
|
442
|
+
// 4b. Find new managed paths from the zip and delete any old paths that were removed
|
|
443
|
+
const newManagedPaths = findManagedPathsInZip(zipBuf);
|
|
444
|
+
const removedDeleted = deleteRemovedManagedPaths(
|
|
445
|
+
targetDir,
|
|
446
|
+
existingManifest.managedPaths || [],
|
|
447
|
+
newManagedPaths
|
|
448
|
+
);
|
|
449
|
+
if (removedDeleted.length > 0) {
|
|
450
|
+
result.deleted.push(...removedDeleted);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 4c. Run version-based upgrades (migrations for renamed/removed files)
|
|
454
|
+
const upgradeDeleted = runUpgrades(targetDir, oldVersionNorm, newVersion);
|
|
455
|
+
if (upgradeDeleted.length > 0) {
|
|
456
|
+
result.deleted.push(...upgradeDeleted);
|
|
457
|
+
}
|
|
458
|
+
|
|
387
459
|
// 5. Report
|
|
388
460
|
console.log('');
|
|
389
461
|
console.log(`✅ Cadet-Agent synced: v${oldVersionNorm} → v${newVersion}`);
|
package/src/upgrades.mjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { unlinkSync, statSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
// ── Path helpers ────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
function walkDir(dir, fn) {
|
|
7
|
+
let entries;
|
|
8
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
const fullPath = join(dir, entry.name);
|
|
11
|
+
if (entry.isDirectory()) {
|
|
12
|
+
walkDir(fullPath, fn);
|
|
13
|
+
} else {
|
|
14
|
+
fn(fullPath);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function deletePath(targetDir, relativePath) {
|
|
20
|
+
const norm = relativePath.replace(/^\.\//, '').replace(/\\/g, '/');
|
|
21
|
+
const fullPath = join(targetDir, norm);
|
|
22
|
+
let st;
|
|
23
|
+
try { st = statSync(fullPath); } catch { return []; }
|
|
24
|
+
|
|
25
|
+
const deleted = [];
|
|
26
|
+
if (st.isFile()) {
|
|
27
|
+
try { unlinkSync(fullPath); deleted.push(fullPath); } catch {}
|
|
28
|
+
} else if (st.isDirectory()) {
|
|
29
|
+
walkDir(fullPath, (filePath) => {
|
|
30
|
+
try { unlinkSync(filePath); deleted.push(filePath); } catch {}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return deleted;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── Upgrade registry ────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
// Key = FROM version. Each upgrade runs when upgrading FROM a version
|
|
39
|
+
// older than or equal to the key, TO a version newer than the key.
|
|
40
|
+
// Upgrade functions receive (targetDir) and return string[] of deleted paths.
|
|
41
|
+
|
|
42
|
+
const upgrades = {
|
|
43
|
+
'0.15.3': (targetDir) => [
|
|
44
|
+
...deletePath(targetDir, '.cadet/orchestrator'),
|
|
45
|
+
...deletePath(targetDir, '.github/prompts'),
|
|
46
|
+
...deletePath(targetDir, 'AGENTS.md'),
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ── Runner ──────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
function semverGt(a, b) {
|
|
53
|
+
const pa = a.split('.').map(Number);
|
|
54
|
+
const pb = b.split('.').map(Number);
|
|
55
|
+
for (let i = 0; i < 3; i++) {
|
|
56
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
57
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function runUpgrades(targetDir, fromVersion, toVersion) {
|
|
63
|
+
const deleted = [];
|
|
64
|
+
|
|
65
|
+
for (const [ver, fn] of Object.entries(upgrades)) {
|
|
66
|
+
if (semverGt(ver, fromVersion) && !semverGt(ver, toVersion)) {
|
|
67
|
+
try {
|
|
68
|
+
const result = fn(targetDir);
|
|
69
|
+
if (Array.isArray(result)) deleted.push(...result);
|
|
70
|
+
} catch {
|
|
71
|
+
// Upgrade failure shouldn't block the sync
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return deleted;
|
|
77
|
+
}
|