agentradar-cli 0.2.5 → 0.2.6
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/skill-install.js +29 -2
- package/dist/vendor-runner/feeder.mjs +17 -1
- package/package.json +1 -1
package/dist/skill-install.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmod, lstat, mkdir, readFile, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
1
|
+
import { chmod, lstat, mkdir, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { dirname, isAbsolute, join, normalize, sep } from "node:path";
|
|
4
4
|
import { awpRequest, envValue } from "./tool-run.js";
|
|
@@ -50,6 +50,21 @@ const isManaged = async (dir) => (await lstat(dir).then((info) => info.isDirecto
|
|
|
50
50
|
* untrusted input to this box, so names and paths are checked, each skill is staged then renamed into place,
|
|
51
51
|
* a folder without the marker (the vendor's own) is never touched, and a managed skill no longer listed goes.
|
|
52
52
|
*/
|
|
53
|
+
const sameSkill = async (dir, files) => {
|
|
54
|
+
try {
|
|
55
|
+
const have = [];
|
|
56
|
+
for (const p of await readdir(dir, { recursive: true }))
|
|
57
|
+
if (p !== MANAGED_MARK && (await stat(join(dir, p))).isFile())
|
|
58
|
+
have.push(p);
|
|
59
|
+
for (const f of files)
|
|
60
|
+
if (await readFile(join(dir, f.path), "utf8") !== f.content)
|
|
61
|
+
return false;
|
|
62
|
+
return have.length === files.length;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
53
68
|
export async function writeManagedSkills(skillsDir, skills) {
|
|
54
69
|
await mkdir(skillsDir, { recursive: true });
|
|
55
70
|
const written = new Set();
|
|
@@ -64,7 +79,13 @@ export async function writeManagedSkills(skillsDir, skills) {
|
|
|
64
79
|
if (files.some((f) => typeof f?.path !== "string" || typeof f?.content !== "string" || isAbsolute(f.path)
|
|
65
80
|
|| normalize(f.path).split(sep).includes("..") || [".", MANAGED_MARK].includes(normalize(f.path))))
|
|
66
81
|
continue;
|
|
82
|
+
// An unchanged skill is not rewritten: the agent's CLI may be reading it.
|
|
83
|
+
if (await sameSkill(target, files)) {
|
|
84
|
+
written.add(name);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
67
87
|
const staging = join(skillsDir, `.${name}.sync-${process.pid}`);
|
|
88
|
+
const old = join(skillsDir, `.${name}.old-${process.pid}`);
|
|
68
89
|
try {
|
|
69
90
|
await rm(staging, { recursive: true, force: true });
|
|
70
91
|
for (const f of files) {
|
|
@@ -72,12 +93,18 @@ export async function writeManagedSkills(skillsDir, skills) {
|
|
|
72
93
|
await writeFile(join(staging, f.path), f.content);
|
|
73
94
|
}
|
|
74
95
|
await writeFile(join(staging, MANAGED_MARK), "Written by the AgentRadar runner; replaced on every sync.\n");
|
|
75
|
-
|
|
96
|
+
// Rename aside, then into place: the skill is missing for two renames, not for a recursive delete.
|
|
97
|
+
await rm(old, { recursive: true, force: true });
|
|
98
|
+
await rename(target, old).catch((err) => { if (err?.code !== "ENOENT")
|
|
99
|
+
throw err; });
|
|
76
100
|
await rename(staging, target);
|
|
101
|
+
await rm(old, { recursive: true, force: true });
|
|
77
102
|
written.add(name);
|
|
78
103
|
}
|
|
79
104
|
catch {
|
|
80
105
|
await rm(staging, { recursive: true, force: true });
|
|
106
|
+
if (!(await lstat(target).then(() => true, () => false)))
|
|
107
|
+
await rename(old, target).catch(() => { });
|
|
81
108
|
if (await isManaged(target))
|
|
82
109
|
written.add(name);
|
|
83
110
|
}
|
|
@@ -663,6 +663,14 @@ const isManaged = (dir) => {
|
|
|
663
663
|
const st = fs.lstatSync(dir, { throwIfNoEntry: false });
|
|
664
664
|
return Boolean(st?.isDirectory() && fs.existsSync(join(dir, MANAGED_MARK)));
|
|
665
665
|
};
|
|
666
|
+
const sameSkill = (dir, files) => {
|
|
667
|
+
try {
|
|
668
|
+
const have = fs.readdirSync(dir, { recursive: true }).filter((p) => p !== MANAGED_MARK && fs.statSync(join(dir, p)).isFile());
|
|
669
|
+
return have.length === files.length && files.every((f) => fs.readFileSync(join(dir, f.path), "utf8") === f.content);
|
|
670
|
+
} catch {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
};
|
|
666
674
|
|
|
667
675
|
export function writeManagedSkills(skillsDir, skills) {
|
|
668
676
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
@@ -679,7 +687,11 @@ export function writeManagedSkills(skillsDir, skills) {
|
|
|
679
687
|
const bad = files.find((f) => typeof f?.path !== "string" || typeof f?.content !== "string" || isAbsolute(f.path)
|
|
680
688
|
|| normalize(f.path).split(sep).includes("..") || [".", MANAGED_MARK].includes(normalize(f.path)));
|
|
681
689
|
if (bad) { console.warn(`[feeder] skill sync: ${name} has an unsafe file path, skipped`); continue; }
|
|
690
|
+
// An unchanged skill is not rewritten: the agent's CLI may be reading it (Codex skipped a SKILL.md that a
|
|
691
|
+
// periodic resync had just removed).
|
|
692
|
+
if (sameSkill(target, files)) { written.add(name); continue; }
|
|
682
693
|
const staging = join(skillsDir, `.${name}.sync-${process.pid}`);
|
|
694
|
+
const old = join(skillsDir, `.${name}.old-${process.pid}`);
|
|
683
695
|
try {
|
|
684
696
|
fs.rmSync(staging, { recursive: true, force: true });
|
|
685
697
|
for (const f of files) {
|
|
@@ -687,12 +699,16 @@ export function writeManagedSkills(skillsDir, skills) {
|
|
|
687
699
|
fs.writeFileSync(join(staging, f.path), f.content);
|
|
688
700
|
}
|
|
689
701
|
fs.writeFileSync(join(staging, MANAGED_MARK), "Written by the AgentRadar runner; replaced on every sync.\n");
|
|
690
|
-
|
|
702
|
+
// Rename aside, then into place: the skill is missing for two renames, not for a recursive delete.
|
|
703
|
+
fs.rmSync(old, { recursive: true, force: true });
|
|
704
|
+
if (fs.existsSync(target)) fs.renameSync(target, old);
|
|
691
705
|
fs.renameSync(staging, target);
|
|
706
|
+
fs.rmSync(old, { recursive: true, force: true });
|
|
692
707
|
written.add(name);
|
|
693
708
|
} catch (err) {
|
|
694
709
|
// One skill that cannot be written keeps its previous copy and does not stop the rest of the sync.
|
|
695
710
|
fs.rmSync(staging, { recursive: true, force: true });
|
|
711
|
+
if (!fs.existsSync(target) && fs.existsSync(old)) fs.renameSync(old, target);
|
|
696
712
|
if (isManaged(target)) written.add(name);
|
|
697
713
|
console.warn(`[feeder] skill sync: ${name} not written: ${err instanceof Error ? err.message : String(err)}`);
|
|
698
714
|
}
|