@agentify/cli 0.1.0
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/LICENSE +373 -0
- package/README.md +282 -0
- package/bin/agentify.js +46 -0
- package/bin/darwin-arm64/agentify +0 -0
- package/bin/darwin-x64/agentify +0 -0
- package/bin/linux-arm64/agentify +0 -0
- package/bin/linux-x64/agentify +0 -0
- package/bin/win32-arm64/agentify.exe +0 -0
- package/bin/win32-x64/agentify.exe +0 -0
- package/lib/metadata.js +70 -0
- package/lib/platform.js +77 -0
- package/lib/release.js +223 -0
- package/lib/stage.js +156 -0
- package/package.json +41 -0
- package/scripts/build-release-manifest.js +41 -0
- package/scripts/check-version-sync.js +28 -0
- package/scripts/postinstall.js +27 -0
- package/scripts/release-check.js +112 -0
- package/scripts/stage-binary.js +43 -0
- package/scripts/verify-package.js +33 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/metadata.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
function readPackageMetadata(options = {}) {
|
|
7
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
8
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
9
|
+
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
return {
|
|
12
|
+
name: String(parsed.name || ""),
|
|
13
|
+
version: String(parsed.version || "")
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readCargoMetadata(options = {}) {
|
|
18
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
19
|
+
const cargoTomlPath = path.join(packageRoot, "Cargo.toml");
|
|
20
|
+
const raw = fs.readFileSync(cargoTomlPath, "utf8");
|
|
21
|
+
|
|
22
|
+
const packageSection = raw.match(/(?:^|\n)\[package\]\s*([\s\S]*?)(?:\n\[|$)/);
|
|
23
|
+
if (!packageSection) {
|
|
24
|
+
throw new Error("Cargo.toml is missing a [package] section");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const body = packageSection[1];
|
|
28
|
+
const nameMatch = body.match(/^\s*name\s*=\s*"([^"]+)"\s*$/m);
|
|
29
|
+
const versionMatch = body.match(/^\s*version\s*=\s*"([^"]+)"\s*$/m);
|
|
30
|
+
if (!nameMatch) {
|
|
31
|
+
throw new Error("Cargo.toml package section is missing name");
|
|
32
|
+
}
|
|
33
|
+
if (!versionMatch) {
|
|
34
|
+
throw new Error("Cargo.toml package section is missing version");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
name: nameMatch[1],
|
|
39
|
+
version: versionMatch[1]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function verifyVersionSync(options = {}) {
|
|
44
|
+
const pkg = readPackageMetadata(options);
|
|
45
|
+
const cargo = readCargoMetadata(options);
|
|
46
|
+
|
|
47
|
+
if (!pkg.version) {
|
|
48
|
+
throw new Error("package.json version is empty");
|
|
49
|
+
}
|
|
50
|
+
if (!cargo.version) {
|
|
51
|
+
throw new Error("Cargo.toml version is empty");
|
|
52
|
+
}
|
|
53
|
+
if (pkg.version !== cargo.version) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Version mismatch: package.json=${pkg.version} Cargo.toml=${cargo.version}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
packageName: pkg.name,
|
|
61
|
+
cargoName: cargo.name,
|
|
62
|
+
version: pkg.version
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
readCargoMetadata,
|
|
68
|
+
readPackageMetadata,
|
|
69
|
+
verifyVersionSync
|
|
70
|
+
};
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const SUPPORTED_TARGETS = Object.freeze({
|
|
7
|
+
"darwin-arm64": { subdir: "darwin-arm64", binaryName: "agentify" },
|
|
8
|
+
"darwin-x64": { subdir: "darwin-x64", binaryName: "agentify" },
|
|
9
|
+
"linux-arm64": { subdir: "linux-arm64", binaryName: "agentify" },
|
|
10
|
+
"linux-x64": { subdir: "linux-x64", binaryName: "agentify" },
|
|
11
|
+
"win32-arm64": { subdir: "win32-arm64", binaryName: "agentify.exe" },
|
|
12
|
+
"win32-x64": { subdir: "win32-x64", binaryName: "agentify.exe" }
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function platformKey(platform = process.platform, arch = process.arch) {
|
|
16
|
+
return `${String(platform)}-${String(arch)}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveTargetInfo(platform = process.platform, arch = process.arch) {
|
|
20
|
+
const key = platformKey(platform, arch);
|
|
21
|
+
const target = SUPPORTED_TARGETS[key];
|
|
22
|
+
if (!target) {
|
|
23
|
+
const supported = Object.keys(SUPPORTED_TARGETS).sort().join(", ");
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Unsupported platform/arch for @agentify/cli: ${key}. Supported targets: ${supported}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return { key, ...target };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateExecutablePath(candidate, label) {
|
|
32
|
+
const trimmed = typeof candidate === "string" ? candidate.trim() : "";
|
|
33
|
+
if (!trimmed) {
|
|
34
|
+
throw new Error(`${label} is empty`);
|
|
35
|
+
}
|
|
36
|
+
const resolved = path.resolve(trimmed);
|
|
37
|
+
const stats = fs.statSync(resolved, { throwIfNoEntry: false });
|
|
38
|
+
if (!stats) {
|
|
39
|
+
throw new Error(`${label} not found: ${resolved}`);
|
|
40
|
+
}
|
|
41
|
+
if (!stats.isFile()) {
|
|
42
|
+
throw new Error(`${label} is not a file: ${resolved}`);
|
|
43
|
+
}
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolveBinaryPath(options = {}) {
|
|
48
|
+
const packageRoot =
|
|
49
|
+
options.packageRoot || path.resolve(__dirname, "..");
|
|
50
|
+
const env = options.env || process.env;
|
|
51
|
+
const platform = options.platform || process.platform;
|
|
52
|
+
const arch = options.arch || process.arch;
|
|
53
|
+
|
|
54
|
+
const override = typeof env.AGENTIFY_CLI_BIN === "string" ? env.AGENTIFY_CLI_BIN : "";
|
|
55
|
+
if (override.trim()) {
|
|
56
|
+
return validateExecutablePath(override, "AGENTIFY_CLI_BIN");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const target = resolveTargetInfo(platform, arch);
|
|
60
|
+
const candidate = path.join(packageRoot, "bin", target.subdir, target.binaryName);
|
|
61
|
+
return validateExecutablePath(candidate, `packaged ${target.key} binary`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function expectedBinaryPath(options = {}) {
|
|
65
|
+
const packageRoot =
|
|
66
|
+
options.packageRoot || path.resolve(__dirname, "..");
|
|
67
|
+
const target = resolveTargetInfo(options.platform, options.arch);
|
|
68
|
+
return path.join(packageRoot, "bin", target.subdir, target.binaryName);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
SUPPORTED_TARGETS,
|
|
73
|
+
expectedBinaryPath,
|
|
74
|
+
platformKey,
|
|
75
|
+
resolveBinaryPath,
|
|
76
|
+
resolveTargetInfo
|
|
77
|
+
};
|
package/lib/release.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const childProcess = require("node:child_process");
|
|
4
|
+
const crypto = require("node:crypto");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
const { SUPPORTED_TARGETS, expectedBinaryPath } = require("./platform");
|
|
9
|
+
|
|
10
|
+
function parseReleaseArgs(argv) {
|
|
11
|
+
const args = Array.isArray(argv) ? [...argv] : [];
|
|
12
|
+
const result = {
|
|
13
|
+
out: "",
|
|
14
|
+
requireAll: false,
|
|
15
|
+
help: false
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
while (args.length > 0) {
|
|
19
|
+
const token = args.shift();
|
|
20
|
+
if (token === "--help" || token === "-h") {
|
|
21
|
+
result.help = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (token === "--require-all") {
|
|
25
|
+
result.requireAll = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (token === "--out") {
|
|
29
|
+
const value = args.shift();
|
|
30
|
+
if (!value || value.startsWith("--")) {
|
|
31
|
+
throw new Error("--out requires a file path");
|
|
32
|
+
}
|
|
33
|
+
result.out = value;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unknown argument: ${token}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function sha256File(filePath) {
|
|
43
|
+
const hash = crypto.createHash("sha256");
|
|
44
|
+
const data = fs.readFileSync(filePath);
|
|
45
|
+
hash.update(data);
|
|
46
|
+
return hash.digest("hex");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function validateBinLayout(packageRoot) {
|
|
50
|
+
const binRoot = path.join(packageRoot, "bin");
|
|
51
|
+
const entries = fs.readdirSync(binRoot, { withFileTypes: true });
|
|
52
|
+
const supported = new Set(Object.values(SUPPORTED_TARGETS).map((target) => target.subdir));
|
|
53
|
+
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.name === "agentify.js" && entry.isFile()) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (entry.isDirectory() && supported.has(entry.name)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Unexpected entry under bin/: ${entry.name}. Only agentify.js and supported platform directories are allowed`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function collectStagedBinaries(options = {}) {
|
|
68
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
69
|
+
const requireAll = Boolean(options.requireAll);
|
|
70
|
+
|
|
71
|
+
validateBinLayout(packageRoot);
|
|
72
|
+
|
|
73
|
+
const staged = [];
|
|
74
|
+
const missing = [];
|
|
75
|
+
|
|
76
|
+
for (const key of Object.keys(SUPPORTED_TARGETS).sort()) {
|
|
77
|
+
const target = SUPPORTED_TARGETS[key];
|
|
78
|
+
const binaryPath = expectedBinaryPath({
|
|
79
|
+
packageRoot,
|
|
80
|
+
platform: key.split("-")[0],
|
|
81
|
+
arch: key.slice(key.indexOf("-") + 1)
|
|
82
|
+
});
|
|
83
|
+
const stats = fs.statSync(binaryPath, { throwIfNoEntry: false });
|
|
84
|
+
if (!stats) {
|
|
85
|
+
missing.push(key);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (!stats.isFile()) {
|
|
89
|
+
throw new Error(`Staged binary is not a file for ${key}: ${binaryPath}`);
|
|
90
|
+
}
|
|
91
|
+
if (stats.size <= 0) {
|
|
92
|
+
throw new Error(`Staged binary is empty for ${key}: ${binaryPath}`);
|
|
93
|
+
}
|
|
94
|
+
staged.push({
|
|
95
|
+
target: key,
|
|
96
|
+
binaryName: target.binaryName,
|
|
97
|
+
relativePath: path.relative(packageRoot, binaryPath),
|
|
98
|
+
size: stats.size,
|
|
99
|
+
sha256: sha256File(binaryPath)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (requireAll && missing.length > 0) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Missing staged binaries for required targets: ${missing.join(", ")}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { staged, missing };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildReleaseManifest(options = {}) {
|
|
113
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
114
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
115
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
116
|
+
const { staged, missing } = collectStagedBinaries({
|
|
117
|
+
packageRoot,
|
|
118
|
+
requireAll: options.requireAll
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
schemaVersion: "agentify.sh/release-manifest-v0.1",
|
|
123
|
+
package: {
|
|
124
|
+
name: String(packageJson.name || ""),
|
|
125
|
+
version: String(packageJson.version || "")
|
|
126
|
+
},
|
|
127
|
+
generatedAt: new Date().toISOString(),
|
|
128
|
+
stagedTargets: staged,
|
|
129
|
+
missingTargets: missing
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parsePackDryRun(stdout) {
|
|
134
|
+
const parsed = JSON.parse(stdout);
|
|
135
|
+
if (!Array.isArray(parsed) || parsed.length !== 1 || typeof parsed[0] !== "object" || !parsed[0]) {
|
|
136
|
+
throw new Error("npm pack --dry-run --json returned an unexpected payload shape");
|
|
137
|
+
}
|
|
138
|
+
const pack = parsed[0];
|
|
139
|
+
if (!Array.isArray(pack.files)) {
|
|
140
|
+
throw new Error("npm pack --dry-run --json payload is missing files[]");
|
|
141
|
+
}
|
|
142
|
+
return pack;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function verifyPackContents(manifest, pack) {
|
|
146
|
+
const files = new Map(
|
|
147
|
+
pack.files.map((entry) => [String(entry.path || ""), entry])
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (!files.has("bin/agentify.js")) {
|
|
151
|
+
throw new Error("pack archive is missing bin/agentify.js");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const target of manifest.stagedTargets || []) {
|
|
155
|
+
const entry = files.get(target.relativePath);
|
|
156
|
+
if (!entry) {
|
|
157
|
+
throw new Error(`pack archive is missing staged binary: ${target.relativePath}`);
|
|
158
|
+
}
|
|
159
|
+
if (Number(entry.size) !== Number(target.size)) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`pack archive size mismatch for ${target.relativePath}: expected ${target.size}, got ${entry.size}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
packedFileCount: pack.files.length,
|
|
168
|
+
verifiedTargets: (manifest.stagedTargets || []).map((target) => target.target)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function runPackDryRun(options = {}) {
|
|
173
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
174
|
+
const stdout = childProcess.execFileSync("npm", ["pack", "--dry-run", "--json"], {
|
|
175
|
+
cwd: packageRoot,
|
|
176
|
+
encoding: "utf8"
|
|
177
|
+
});
|
|
178
|
+
return parsePackDryRun(stdout);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function writeManifest(manifest, outPath) {
|
|
182
|
+
const trimmed = typeof outPath === "string" ? outPath.trim() : "";
|
|
183
|
+
if (!trimmed) {
|
|
184
|
+
throw new Error("manifest output path is empty");
|
|
185
|
+
}
|
|
186
|
+
const resolved = path.resolve(trimmed);
|
|
187
|
+
const existing = fs.statSync(resolved, { throwIfNoEntry: false });
|
|
188
|
+
if (existing && existing.isDirectory()) {
|
|
189
|
+
throw new Error(`manifest output path is a directory: ${resolved}`);
|
|
190
|
+
}
|
|
191
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
192
|
+
fs.writeFileSync(resolved, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
193
|
+
return resolved;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function helpText() {
|
|
197
|
+
return [
|
|
198
|
+
"Build a local release manifest for staged @agentify/cli binaries.",
|
|
199
|
+
"",
|
|
200
|
+
"Usage:",
|
|
201
|
+
" node ./scripts/build-release-manifest.js [--out ./release-manifest.json] [--require-all]",
|
|
202
|
+
"",
|
|
203
|
+
"Defaults:",
|
|
204
|
+
" Prints the manifest JSON to stdout when --out is omitted",
|
|
205
|
+
" Allows partial staged target sets unless --require-all is passed",
|
|
206
|
+
"",
|
|
207
|
+
"Examples:",
|
|
208
|
+
" npm run release:manifest",
|
|
209
|
+
" npm run release:manifest -- --out ./release-manifest.json",
|
|
210
|
+
" npm run release:manifest -- --require-all"
|
|
211
|
+
].join("\n");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
module.exports = {
|
|
215
|
+
buildReleaseManifest,
|
|
216
|
+
collectStagedBinaries,
|
|
217
|
+
helpText,
|
|
218
|
+
parseReleaseArgs,
|
|
219
|
+
parsePackDryRun,
|
|
220
|
+
runPackDryRun,
|
|
221
|
+
verifyPackContents,
|
|
222
|
+
writeManifest
|
|
223
|
+
};
|
package/lib/stage.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const { expectedBinaryPath, resolveTargetInfo } = require("./platform");
|
|
7
|
+
|
|
8
|
+
function defaultSourcePath(options = {}) {
|
|
9
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
10
|
+
const platform = options.platform || process.platform;
|
|
11
|
+
const arch = options.arch || process.arch;
|
|
12
|
+
const target = resolveTargetInfo(platform, arch);
|
|
13
|
+
return path.join(packageRoot, "target", "release", target.binaryName);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseStageArgs(argv) {
|
|
17
|
+
const args = Array.isArray(argv) ? [...argv] : [];
|
|
18
|
+
const result = {
|
|
19
|
+
source: "",
|
|
20
|
+
platform: process.platform,
|
|
21
|
+
arch: process.arch,
|
|
22
|
+
force: false,
|
|
23
|
+
help: false
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
while (args.length > 0) {
|
|
27
|
+
const token = args.shift();
|
|
28
|
+
if (token === "--help" || token === "-h") {
|
|
29
|
+
result.help = true;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (token === "--force") {
|
|
33
|
+
result.force = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (token === "--source") {
|
|
37
|
+
const value = args.shift();
|
|
38
|
+
if (!value || value.startsWith("--")) {
|
|
39
|
+
throw new Error("--source requires a file path");
|
|
40
|
+
}
|
|
41
|
+
result.source = value;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (token === "--platform") {
|
|
45
|
+
const value = args.shift();
|
|
46
|
+
if (!value || value.startsWith("--")) {
|
|
47
|
+
throw new Error("--platform requires a value");
|
|
48
|
+
}
|
|
49
|
+
result.platform = value;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (token === "--arch") {
|
|
53
|
+
const value = args.shift();
|
|
54
|
+
if (!value || value.startsWith("--")) {
|
|
55
|
+
throw new Error("--arch requires a value");
|
|
56
|
+
}
|
|
57
|
+
result.arch = value;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`Unknown argument: ${token}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateSourcePath(candidate) {
|
|
67
|
+
const trimmed = typeof candidate === "string" ? candidate.trim() : "";
|
|
68
|
+
if (!trimmed) {
|
|
69
|
+
throw new Error("stage source path is empty");
|
|
70
|
+
}
|
|
71
|
+
const resolved = path.resolve(trimmed);
|
|
72
|
+
const stats = fs.statSync(resolved, { throwIfNoEntry: false });
|
|
73
|
+
if (!stats) {
|
|
74
|
+
throw new Error(`stage source binary not found: ${resolved}`);
|
|
75
|
+
}
|
|
76
|
+
if (!stats.isFile()) {
|
|
77
|
+
throw new Error(`stage source is not a file: ${resolved}`);
|
|
78
|
+
}
|
|
79
|
+
return resolved;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function stageBinary(options = {}) {
|
|
83
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
84
|
+
const platform = options.platform || process.platform;
|
|
85
|
+
const arch = options.arch || process.arch;
|
|
86
|
+
const target = resolveTargetInfo(platform, arch);
|
|
87
|
+
const destination = expectedBinaryPath({ packageRoot, platform, arch });
|
|
88
|
+
const source = validateSourcePath(
|
|
89
|
+
options.source || defaultSourcePath({ packageRoot, platform, arch })
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const packageRootResolved = path.resolve(packageRoot);
|
|
93
|
+
const destinationResolved = path.resolve(destination);
|
|
94
|
+
const destinationRelative = path.relative(packageRootResolved, destinationResolved);
|
|
95
|
+
if (
|
|
96
|
+
!destinationRelative ||
|
|
97
|
+
destinationRelative.startsWith("..") ||
|
|
98
|
+
path.isAbsolute(destinationRelative)
|
|
99
|
+
) {
|
|
100
|
+
throw new Error(`refusing to stage binary outside package root: ${destinationResolved}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const destinationStats = fs.statSync(destinationResolved, { throwIfNoEntry: false });
|
|
104
|
+
if (destinationStats && destinationStats.isDirectory()) {
|
|
105
|
+
throw new Error(`stage destination is a directory: ${destinationResolved}`);
|
|
106
|
+
}
|
|
107
|
+
if (destinationStats && !options.force) {
|
|
108
|
+
const sourceReal = fs.realpathSync(source);
|
|
109
|
+
const destinationReal = fs.realpathSync(destinationResolved);
|
|
110
|
+
if (sourceReal !== destinationReal) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`staged binary already exists for ${target.key}: ${destinationResolved} (use --force to overwrite)`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fs.mkdirSync(path.dirname(destinationResolved), { recursive: true });
|
|
118
|
+
fs.copyFileSync(source, destinationResolved);
|
|
119
|
+
if (platform !== "win32") {
|
|
120
|
+
fs.chmodSync(destinationResolved, 0o755);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
target: target.key,
|
|
125
|
+
source,
|
|
126
|
+
destination: destinationResolved,
|
|
127
|
+
overwritten: Boolean(destinationStats),
|
|
128
|
+
binaryName: target.binaryName
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function helpText() {
|
|
133
|
+
return [
|
|
134
|
+
"Stage a built Rust agentify binary into the npm package layout.",
|
|
135
|
+
"",
|
|
136
|
+
"Usage:",
|
|
137
|
+
" node ./scripts/stage-binary.js [--source /abs/path/to/agentify] [--platform darwin] [--arch arm64] [--force]",
|
|
138
|
+
"",
|
|
139
|
+
"Defaults:",
|
|
140
|
+
" --source defaults to ./target/release/<binary-name> for the selected platform/arch",
|
|
141
|
+
" --platform defaults to the current platform",
|
|
142
|
+
" --arch defaults to the current architecture",
|
|
143
|
+
"",
|
|
144
|
+
"Examples:",
|
|
145
|
+
" npm run stage:bin",
|
|
146
|
+
" npm run stage:bin -- --force",
|
|
147
|
+
" npm run stage:bin -- --platform linux --arch x64 --source /tmp/agentify"
|
|
148
|
+
].join("\n");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
defaultSourcePath,
|
|
153
|
+
helpText,
|
|
154
|
+
parseStageArgs,
|
|
155
|
+
stageBinary
|
|
156
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentify/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agentify CLI for Ask, Replay, workflows, and local agent session tooling",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://agentify.sh",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"bin": {
|
|
9
|
+
"agentify": "bin/agentify.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"README.md",
|
|
13
|
+
"bin/",
|
|
14
|
+
"lib/",
|
|
15
|
+
"scripts/",
|
|
16
|
+
"package.json"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"check:versions": "node ./scripts/check-version-sync.js",
|
|
23
|
+
"postinstall": "node ./scripts/postinstall.js",
|
|
24
|
+
"prepublishOnly": "npm run release:check",
|
|
25
|
+
"release:check": "node ./scripts/release-check.js",
|
|
26
|
+
"release:manifest": "node ./scripts/build-release-manifest.js",
|
|
27
|
+
"stage:bin": "node ./scripts/stage-binary.js",
|
|
28
|
+
"test:npm": "node --test ./test/npm/*.test.js",
|
|
29
|
+
"verify:package": "node ./scripts/verify-package.js"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"agentify",
|
|
36
|
+
"cli",
|
|
37
|
+
"agentic",
|
|
38
|
+
"replay",
|
|
39
|
+
"workflows"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
buildReleaseManifest,
|
|
6
|
+
helpText,
|
|
7
|
+
parseReleaseArgs,
|
|
8
|
+
writeManifest
|
|
9
|
+
} = require("../lib/release");
|
|
10
|
+
|
|
11
|
+
function main() {
|
|
12
|
+
let args;
|
|
13
|
+
try {
|
|
14
|
+
args = parseReleaseArgs(process.argv.slice(2));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(error.message);
|
|
17
|
+
console.error("");
|
|
18
|
+
console.error(helpText());
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (args.help) {
|
|
23
|
+
console.log(helpText());
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const manifest = buildReleaseManifest({ requireAll: args.requireAll });
|
|
29
|
+
if (args.out) {
|
|
30
|
+
const resolved = writeManifest(manifest, args.out);
|
|
31
|
+
console.log(`Wrote release manifest to ${resolved}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(`Failed to build release manifest: ${error.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { verifyVersionSync } = require("../lib/metadata");
|
|
5
|
+
|
|
6
|
+
function main() {
|
|
7
|
+
try {
|
|
8
|
+
const result = verifyVersionSync();
|
|
9
|
+
console.log(
|
|
10
|
+
JSON.stringify(
|
|
11
|
+
{
|
|
12
|
+
schemaVersion: "agentify.sh/version-sync-v0.1",
|
|
13
|
+
ok: true,
|
|
14
|
+
packageName: result.packageName,
|
|
15
|
+
cargoName: result.cargoName,
|
|
16
|
+
version: result.version
|
|
17
|
+
},
|
|
18
|
+
null,
|
|
19
|
+
2
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`Version check failed: ${error.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
main();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { expectedBinaryPath, resolveBinaryPath, resolveTargetInfo } = require("../lib/platform");
|
|
5
|
+
|
|
6
|
+
function main() {
|
|
7
|
+
if (String(process.env.AGENTIFY_SKIP_POSTINSTALL || "").trim().toLowerCase() === "true") {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
resolveBinaryPath();
|
|
13
|
+
} catch (error) {
|
|
14
|
+
const target = resolveTargetInfo();
|
|
15
|
+
const expected = expectedBinaryPath();
|
|
16
|
+
console.warn(
|
|
17
|
+
[
|
|
18
|
+
`@agentify/cli postinstall warning: packaged binary not found for ${target.key}.`,
|
|
19
|
+
`Expected: ${expected}`,
|
|
20
|
+
`This is normal in local development before release assets are staged.`,
|
|
21
|
+
`You can set AGENTIFY_CLI_BIN to a local Rust build to use the npm wrapper now.`
|
|
22
|
+
].join("\n")
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
main();
|