@wordbricks/cva 0.1.1

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/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # @wordbricks/cva
2
+
3
+ Install the Codex Virtual Assistant CLI:
4
+
5
+ ```bash
6
+ npm install -g @wordbricks/cva
7
+ ```
8
+
9
+ This package installs a small Node wrapper and downloads the native `cva` binary for the current platform from GitHub Releases.
10
+
11
+ After installation:
12
+
13
+ ```bash
14
+ cva version
15
+ cva start
16
+ ```
17
+
18
+ Notes:
19
+
20
+ - The package name is scoped because the unscoped `cva` package is already taken on npm.
21
+ - The installed command is still `cva`.
22
+ - The downloader expects a matching GitHub release tag in the form `v<package-version>`.
package/bin/cva.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawnSync } = require("node:child_process");
4
+ const fs = require("node:fs");
5
+
6
+ const { binaryPath, currentTarget } = require("../lib/platform");
7
+ const { ensureBinary } = require("../lib/install");
8
+
9
+ async function main() {
10
+ const target = currentTarget();
11
+ let executable = binaryPath(target);
12
+ if (!fs.existsSync(executable)) {
13
+ executable = await ensureBinary({ quiet: false });
14
+ }
15
+ if (!executable || !fs.existsSync(executable)) {
16
+ console.error("The native cva binary is not installed.");
17
+ console.error("Try reinstalling the package or run with CVA_SKIP_DOWNLOAD unset.");
18
+ process.exit(1);
19
+ }
20
+
21
+ const result = spawnSync(executable, process.argv.slice(2), {
22
+ stdio: "inherit",
23
+ env: process.env
24
+ });
25
+ if (result.error) {
26
+ console.error(result.error.message);
27
+ process.exit(1);
28
+ }
29
+ process.exit(result.status === null ? 1 : result.status);
30
+ }
31
+
32
+ main().catch((error) => {
33
+ console.error(error.message);
34
+ process.exit(1);
35
+ });
package/lib/install.js ADDED
@@ -0,0 +1,78 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const https = require("node:https");
4
+ const { pipeline } = require("node:stream/promises");
5
+
6
+ const {
7
+ binaryPath,
8
+ currentTarget,
9
+ downloadURL,
10
+ packageRoot
11
+ } = require("./platform");
12
+
13
+ async function downloadFile(url, destination) {
14
+ const tempFile = `${destination}.tmp`;
15
+ await new Promise((resolve, reject) => {
16
+ const request = https.get(url, (response) => {
17
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
18
+ response.resume();
19
+ downloadFile(response.headers.location, destination).then(resolve).catch(reject);
20
+ return;
21
+ }
22
+ if (response.statusCode !== 200) {
23
+ response.resume();
24
+ reject(new Error(`Download failed with HTTP ${response.statusCode}: ${url}`));
25
+ return;
26
+ }
27
+
28
+ const output = fs.createWriteStream(tempFile, { mode: 0o755 });
29
+ pipeline(response, output)
30
+ .then(resolve)
31
+ .catch(reject);
32
+ });
33
+ request.on("error", reject);
34
+ });
35
+
36
+ fs.renameSync(tempFile, destination);
37
+ }
38
+
39
+ async function ensureBinary(options = {}) {
40
+ const { force = false, quiet = false } = options;
41
+ if (process.env.CVA_SKIP_DOWNLOAD === "1") {
42
+ if (!quiet) {
43
+ console.warn("Skipping cva binary download because CVA_SKIP_DOWNLOAD=1.");
44
+ }
45
+ return null;
46
+ }
47
+
48
+ const target = currentTarget();
49
+ const destination = binaryPath(target);
50
+ if (!force && fs.existsSync(destination)) {
51
+ return destination;
52
+ }
53
+
54
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
55
+ const url = downloadURL(target);
56
+ if (!quiet) {
57
+ console.log(`Downloading cva binary from ${url}`);
58
+ }
59
+ await downloadFile(url, destination);
60
+ if (target.platform !== "win32") {
61
+ fs.chmodSync(destination, 0o755);
62
+ }
63
+ return destination;
64
+ }
65
+
66
+ if (require.main === module) {
67
+ ensureBinary()
68
+ .catch((error) => {
69
+ const relativeRoot = packageRoot();
70
+ console.error(`Failed to install the native cva binary into ${relativeRoot}`);
71
+ console.error(error.message);
72
+ process.exit(1);
73
+ });
74
+ }
75
+
76
+ module.exports = {
77
+ ensureBinary
78
+ };
@@ -0,0 +1,66 @@
1
+ const os = require("node:os");
2
+ const path = require("node:path");
3
+
4
+ function packageRoot() {
5
+ return path.resolve(__dirname, "..");
6
+ }
7
+
8
+ function readPackageMetadata() {
9
+ // eslint-disable-next-line global-require, import/no-dynamic-require
10
+ return require(path.join(packageRoot(), "package.json"));
11
+ }
12
+
13
+ function currentTarget() {
14
+ const platform = os.platform();
15
+ const arch = os.arch();
16
+
17
+ const supported = new Set([
18
+ "linux:x64",
19
+ "linux:arm64",
20
+ "darwin:x64",
21
+ "darwin:arm64",
22
+ "win32:x64",
23
+ "win32:arm64"
24
+ ]);
25
+
26
+ if (!supported.has(`${platform}:${arch}`)) {
27
+ throw new Error(`Unsupported platform for cva: ${platform}/${arch}`);
28
+ }
29
+
30
+ return { platform, arch };
31
+ }
32
+
33
+ function executableName(platform) {
34
+ return platform === "win32" ? "cva.exe" : "cva";
35
+ }
36
+
37
+ function assetName(target) {
38
+ const metadata = readPackageMetadata();
39
+ const prefix = metadata.cva && metadata.cva.assetPrefix ? metadata.cva.assetPrefix : "cva";
40
+ return `${prefix}-${target.platform}-${target.arch}${target.platform === "win32" ? ".exe" : ""}`;
41
+ }
42
+
43
+ function binaryPath(target = currentTarget()) {
44
+ return path.join(packageRoot(), "vendor", target.platform, target.arch, executableName(target.platform));
45
+ }
46
+
47
+ function releaseTag(metadata = readPackageMetadata()) {
48
+ return `v${metadata.version}`;
49
+ }
50
+
51
+ function downloadURL(target = currentTarget(), metadata = readPackageMetadata()) {
52
+ const owner = metadata.cva && metadata.cva.owner ? metadata.cva.owner : "wordbricks";
53
+ const repo = metadata.cva && metadata.cva.repo ? metadata.cva.repo : "codex-virtual-assistant";
54
+ return `https://github.com/${owner}/${repo}/releases/download/${releaseTag(metadata)}/${assetName(target)}`;
55
+ }
56
+
57
+ module.exports = {
58
+ assetName,
59
+ binaryPath,
60
+ currentTarget,
61
+ downloadURL,
62
+ executableName,
63
+ packageRoot,
64
+ readPackageMetadata,
65
+ releaseTag
66
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@wordbricks/cva",
3
+ "version": "0.1.1",
4
+ "description": "Codex Virtual Assistant CLI wrapper that installs the native cva binary.",
5
+ "homepage": "https://github.com/wordbricks/codex-virtual-assistant",
6
+ "bugs": {
7
+ "url": "https://github.com/wordbricks/codex-virtual-assistant/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/wordbricks/codex-virtual-assistant.git"
12
+ },
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "bin": {
20
+ "cva": "bin/cva.js"
21
+ },
22
+ "scripts": {
23
+ "postinstall": "node ./lib/install.js"
24
+ },
25
+ "files": [
26
+ "README.md",
27
+ "bin",
28
+ "lib"
29
+ ],
30
+ "keywords": [
31
+ "agent",
32
+ "assistant",
33
+ "cli",
34
+ "codex",
35
+ "cva"
36
+ ],
37
+ "cva": {
38
+ "owner": "wordbricks",
39
+ "repo": "codex-virtual-assistant",
40
+ "assetPrefix": "cva"
41
+ }
42
+ }