@askfaro/cli 0.1.0 → 0.4.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/bin/askfaro.js +125 -0
- package/package.json +11 -5
- package/bin/faro.js +0 -92
package/bin/askfaro.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execFileSync, spawnSync } = require("child_process");
|
|
5
|
+
const { existsSync } = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
|
|
8
|
+
const PYPI_PACKAGE = "askfaro-cli";
|
|
9
|
+
const MIN_VERSION = "0.4.0";
|
|
10
|
+
const BIN_NAME = "askfaro";
|
|
11
|
+
|
|
12
|
+
function parseVersion(s) {
|
|
13
|
+
const m = String(s).match(/(\d+)\.(\d+)\.(\d+)/);
|
|
14
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function versionAtLeast(a, b) {
|
|
18
|
+
const va = parseVersion(a);
|
|
19
|
+
const vb = parseVersion(b);
|
|
20
|
+
if (!va || !vb) return false;
|
|
21
|
+
for (let i = 0; i < 3; i++) {
|
|
22
|
+
if (va[i] !== vb[i]) return va[i] > vb[i];
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getVersion(bin) {
|
|
28
|
+
try {
|
|
29
|
+
const r = spawnSync(bin, ["--version"], { encoding: "utf8" });
|
|
30
|
+
if (r.status === 0) return (r.stdout || "").trim();
|
|
31
|
+
} catch {}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findFaro() {
|
|
36
|
+
// Check if `askfaro` is already on PATH (e.g. installed via pip/pipx separately)
|
|
37
|
+
if (getVersion(BIN_NAME) !== null) return BIN_NAME;
|
|
38
|
+
|
|
39
|
+
// Check pipx-installed binary
|
|
40
|
+
const pipxBin = path.join(
|
|
41
|
+
process.env.HOME || "",
|
|
42
|
+
".local",
|
|
43
|
+
"bin",
|
|
44
|
+
BIN_NAME
|
|
45
|
+
);
|
|
46
|
+
if (existsSync(pipxBin)) return pipxBin;
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasPipx() {
|
|
52
|
+
try {
|
|
53
|
+
const r = spawnSync("pipx", ["--version"], { encoding: "utf8" });
|
|
54
|
+
return r.status === 0;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hasPython() {
|
|
61
|
+
for (const cmd of ["python3", "python"]) {
|
|
62
|
+
try {
|
|
63
|
+
const r = spawnSync(cmd, ["--version"], { encoding: "utf8" });
|
|
64
|
+
if (r.status === 0) return cmd;
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function install({ upgrade = false } = {}) {
|
|
71
|
+
const verb = upgrade ? "Upgrading" : "Installing";
|
|
72
|
+
console.error(`${verb} ${PYPI_PACKAGE} via pipx or pip...`);
|
|
73
|
+
|
|
74
|
+
if (hasPipx()) {
|
|
75
|
+
const args = upgrade ? ["upgrade", PYPI_PACKAGE] : ["install", PYPI_PACKAGE];
|
|
76
|
+
const r = spawnSync("pipx", args, { stdio: "inherit" });
|
|
77
|
+
if (r.status === 0) return;
|
|
78
|
+
console.error(`pipx ${args[0]} failed, falling back to pip.`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const python = hasPython();
|
|
82
|
+
if (!python) {
|
|
83
|
+
console.error(
|
|
84
|
+
"Error: Python 3 is required to use the Faro CLI.\n" +
|
|
85
|
+
"Install it from https://python.org or via your package manager, then re-run this command."
|
|
86
|
+
);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const pipArgs = ["-m", "pip", "install", "--user"];
|
|
91
|
+
if (upgrade) pipArgs.push("--upgrade");
|
|
92
|
+
pipArgs.push(PYPI_PACKAGE);
|
|
93
|
+
const r = spawnSync(python, pipArgs, { stdio: "inherit" });
|
|
94
|
+
if (r.status !== 0) {
|
|
95
|
+
console.error(`Failed to install ${PYPI_PACKAGE}. Try: pip install --upgrade ${PYPI_PACKAGE}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let faroBin = findFaro();
|
|
101
|
+
if (!faroBin) {
|
|
102
|
+
install();
|
|
103
|
+
faroBin = findFaro();
|
|
104
|
+
if (!faroBin) {
|
|
105
|
+
console.error(
|
|
106
|
+
`Installed ${PYPI_PACKAGE} but could not find the '${BIN_NAME}' binary on PATH.\n` +
|
|
107
|
+
"You may need to add ~/.local/bin to your PATH:\n" +
|
|
108
|
+
' export PATH="$HOME/.local/bin:$PATH"'
|
|
109
|
+
);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Existing install — check it meets MIN_VERSION and upgrade if not.
|
|
114
|
+
const current = getVersion(faroBin);
|
|
115
|
+
if (current && !versionAtLeast(current, MIN_VERSION)) {
|
|
116
|
+
console.error(
|
|
117
|
+
`${PYPI_PACKAGE} ${current} is below the minimum ${MIN_VERSION} required by this wrapper.`
|
|
118
|
+
);
|
|
119
|
+
install({ upgrade: true });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Forward all arguments to the real askfaro binary
|
|
124
|
+
const result = spawnSync(faroBin, process.argv.slice(2), { stdio: "inherit" });
|
|
125
|
+
process.exit(result.status ?? 1);
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askfaro/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Faro AI
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Faro CLI: the tools your AI agent can't run on its own",
|
|
5
5
|
"bin": {
|
|
6
|
-
"
|
|
6
|
+
"askfaro": "./bin/askfaro.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "node bin/
|
|
9
|
+
"test": "node bin/askfaro.js --version"
|
|
10
10
|
},
|
|
11
|
-
"keywords": [
|
|
11
|
+
"keywords": [
|
|
12
|
+
"faro",
|
|
13
|
+
"ai",
|
|
14
|
+
"tools",
|
|
15
|
+
"marketplace",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
12
18
|
"license": "MIT",
|
|
13
19
|
"engines": {
|
|
14
20
|
"node": ">=16"
|
package/bin/faro.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const { execFileSync, spawnSync } = require("child_process");
|
|
5
|
-
const { existsSync } = require("fs");
|
|
6
|
-
const path = require("path");
|
|
7
|
-
|
|
8
|
-
const PYPI_PACKAGE = "askfaro-cli";
|
|
9
|
-
const MIN_VERSION = "0.1.0";
|
|
10
|
-
|
|
11
|
-
function findFaro() {
|
|
12
|
-
// Check if `faro` is already on PATH (e.g. installed via pip/pipx separately)
|
|
13
|
-
try {
|
|
14
|
-
const result = spawnSync("faro", ["--version"], { encoding: "utf8" });
|
|
15
|
-
if (result.status === 0) return "faro";
|
|
16
|
-
} catch {}
|
|
17
|
-
|
|
18
|
-
// Check pipx-installed binary
|
|
19
|
-
const pipxBin = path.join(
|
|
20
|
-
process.env.HOME || "",
|
|
21
|
-
".local",
|
|
22
|
-
"bin",
|
|
23
|
-
"faro"
|
|
24
|
-
);
|
|
25
|
-
if (existsSync(pipxBin)) return pipxBin;
|
|
26
|
-
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function hasPipx() {
|
|
31
|
-
try {
|
|
32
|
-
const r = spawnSync("pipx", ["--version"], { encoding: "utf8" });
|
|
33
|
-
return r.status === 0;
|
|
34
|
-
} catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function hasPython() {
|
|
40
|
-
for (const cmd of ["python3", "python"]) {
|
|
41
|
-
try {
|
|
42
|
-
const r = spawnSync(cmd, ["--version"], { encoding: "utf8" });
|
|
43
|
-
if (r.status === 0) return cmd;
|
|
44
|
-
} catch {}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function install() {
|
|
50
|
-
console.error(`Installing ${PYPI_PACKAGE} via pipx or pip...`);
|
|
51
|
-
|
|
52
|
-
if (hasPipx()) {
|
|
53
|
-
const r = spawnSync("pipx", ["install", PYPI_PACKAGE], { stdio: "inherit" });
|
|
54
|
-
if (r.status === 0) return;
|
|
55
|
-
console.error("pipx install failed, falling back to pip.");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const python = hasPython();
|
|
59
|
-
if (!python) {
|
|
60
|
-
console.error(
|
|
61
|
-
"Error: Python 3 is required to use the Faro CLI.\n" +
|
|
62
|
-
"Install it from https://python.org or via your package manager, then re-run this command."
|
|
63
|
-
);
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const r = spawnSync(python, ["-m", "pip", "install", "--user", PYPI_PACKAGE], {
|
|
68
|
-
stdio: "inherit",
|
|
69
|
-
});
|
|
70
|
-
if (r.status !== 0) {
|
|
71
|
-
console.error(`Failed to install ${PYPI_PACKAGE}. Try: pip install ${PYPI_PACKAGE}`);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let faroBin = findFaro();
|
|
77
|
-
if (!faroBin) {
|
|
78
|
-
install();
|
|
79
|
-
faroBin = findFaro();
|
|
80
|
-
if (!faroBin) {
|
|
81
|
-
console.error(
|
|
82
|
-
`Installed ${PYPI_PACKAGE} but could not find the 'faro' binary on PATH.\n` +
|
|
83
|
-
"You may need to add ~/.local/bin to your PATH:\n" +
|
|
84
|
-
' export PATH="$HOME/.local/bin:$PATH"'
|
|
85
|
-
);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Forward all arguments to the real faro binary
|
|
91
|
-
const result = spawnSync(faroBin, process.argv.slice(2), { stdio: "inherit" });
|
|
92
|
-
process.exit(result.status ?? 1);
|