@yuanchuan/aivo 0.14.1 → 0.14.2
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 +4 -3
- package/bin/aivo.js +31 -17
- package/lib/launcher.js +27 -0
- package/lib/update.js +54 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +83 -17
package/README.md
CHANGED
|
@@ -9,17 +9,18 @@ npm install -g @yuanchuan/aivo
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
This package downloads the matching prebuilt binary for your platform during install.
|
|
12
|
+
If `aivo` is not recognized right away on Windows, open a new terminal and try again.
|
|
12
13
|
|
|
13
14
|
## Update
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
|
-
|
|
17
|
+
aivo update
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
If the npm-managed install needs repair, run:
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
npm
|
|
23
|
+
npm install -g @yuanchuan/aivo@latest
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
## Usage
|
package/bin/aivo.js
CHANGED
|
@@ -2,25 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
const { spawn } = require("node:child_process");
|
|
4
4
|
const os = require("node:os");
|
|
5
|
-
const { getInstalledBinaryPath } = require("../lib/paths");
|
|
5
|
+
const { getInstalledBinaryPath, getPackageRoot } = require("../lib/paths");
|
|
6
|
+
const { formatLaunchError } = require("../lib/launcher");
|
|
7
|
+
const { shouldDelegateWindowsNpmUpdate, spawnWindowsNpmUpdate } = require("../lib/update");
|
|
6
8
|
|
|
7
|
-
const
|
|
9
|
+
const args = process.argv.slice(2);
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
function forwardExit(child) {
|
|
12
|
+
child.on("exit", (code, signal) => {
|
|
13
|
+
if (signal) {
|
|
14
|
+
process.exitCode = 128 + (os.constants.signals[signal] || 1);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
+
process.exit(code ?? 1);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
if (shouldDelegateWindowsNpmUpdate(args, { packageRoot: getPackageRoot() })) {
|
|
23
|
+
const child = spawnWindowsNpmUpdate(spawn);
|
|
24
|
+
forwardExit(child);
|
|
25
|
+
child.on("error", (error) => {
|
|
26
|
+
console.error(`Failed to launch npm.cmd for update: ${error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
const binaryPath = getInstalledBinaryPath();
|
|
31
|
+
const child = spawn(binaryPath, args, {
|
|
32
|
+
stdio: "inherit"
|
|
33
|
+
});
|
|
21
34
|
|
|
22
|
-
child
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
35
|
+
forwardExit(child);
|
|
36
|
+
child.on("error", (error) => {
|
|
37
|
+
console.error(formatLaunchError(error, binaryPath));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
}
|
package/lib/launcher.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const REPAIR_COMMAND = "npm install -g @yuanchuan/aivo@latest";
|
|
4
|
+
|
|
5
|
+
function formatLaunchError(error, binaryPath, platform = process.platform) {
|
|
6
|
+
const lines = [
|
|
7
|
+
`aivo binary is not available at ${binaryPath}.`,
|
|
8
|
+
"This usually means the npm postinstall download did not complete.",
|
|
9
|
+
"If you installed with --ignore-scripts, reinstall without it.",
|
|
10
|
+
`Repair with: ${REPAIR_COMMAND}`
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
if (error && error.code) {
|
|
14
|
+
lines.push(`Launch error: ${error.code}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (platform === "win32") {
|
|
18
|
+
lines.push("If you just installed aivo, open a new terminal and try again.");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return lines.join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
REPAIR_COMMAND,
|
|
26
|
+
formatLaunchError
|
|
27
|
+
};
|
package/lib/update.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const WINDOWS_NPM_UPDATE_COMMAND = "npm.cmd install -g @yuanchuan/aivo@latest";
|
|
4
|
+
|
|
5
|
+
function normalizePathForMatch(value) {
|
|
6
|
+
return String(value || "")
|
|
7
|
+
.replace(/\\/g, "/")
|
|
8
|
+
.replace(/^\/\/\?\//, "")
|
|
9
|
+
.toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isNpmManagedPackageRoot(packageRoot) {
|
|
13
|
+
const normalized = normalizePathForMatch(packageRoot);
|
|
14
|
+
return normalized.includes("/node_modules/@yuanchuan/aivo");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function shouldDelegateWindowsNpmUpdate(argv, options = {}) {
|
|
18
|
+
const platform = options.platform || process.platform;
|
|
19
|
+
const packageRoot = options.packageRoot;
|
|
20
|
+
|
|
21
|
+
if (platform !== "win32") {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!isNpmManagedPackageRoot(packageRoot)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!Array.isArray(argv) || argv[0] !== "update") {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rest = argv.slice(1);
|
|
34
|
+
if (rest.length === 0) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return rest.every((arg) => arg === "--no-color");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function spawnWindowsNpmUpdate(spawnImpl, options = {}) {
|
|
42
|
+
const comspec = options.comspec || process.env.ComSpec || process.env.COMSPEC || "cmd.exe";
|
|
43
|
+
return spawnImpl(comspec, ["/d", "/s", "/c", WINDOWS_NPM_UPDATE_COMMAND], {
|
|
44
|
+
stdio: "inherit"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
WINDOWS_NPM_UPDATE_COMMAND,
|
|
50
|
+
isNpmManagedPackageRoot,
|
|
51
|
+
normalizePathForMatch,
|
|
52
|
+
shouldDelegateWindowsNpmUpdate,
|
|
53
|
+
spawnWindowsNpmUpdate
|
|
54
|
+
};
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -11,17 +11,22 @@ const { resolvePlatformAsset } = require("../lib/platform");
|
|
|
11
11
|
const pkg = require(path.join(getPackageRoot(), "package.json"));
|
|
12
12
|
|
|
13
13
|
const MAX_REDIRECTS = 5;
|
|
14
|
+
const REPAIR_COMMAND = "npm install -g @yuanchuan/aivo@latest";
|
|
14
15
|
|
|
15
|
-
async function main() {
|
|
16
|
-
|
|
16
|
+
async function main(options = {}) {
|
|
17
|
+
const platform = options.platform || process.platform;
|
|
18
|
+
const arch = options.arch || process.arch;
|
|
19
|
+
const env = options.env || process.env;
|
|
20
|
+
const fsImpl = options.fsImpl || fs;
|
|
21
|
+
const logger = options.logger || console;
|
|
22
|
+
|
|
23
|
+
if (env.AIVO_SKIP_POSTINSTALL === "1") {
|
|
17
24
|
return;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
const { assetName } = resolvePlatformAsset();
|
|
27
|
+
const { assetName } = resolvePlatformAsset(platform, arch);
|
|
21
28
|
const version = pkg.version;
|
|
22
|
-
const baseUrl =
|
|
23
|
-
process.env.AIVO_INSTALL_BASE_URL ||
|
|
24
|
-
`https://github.com/yuanchuan/aivo/releases/download/v${version}`;
|
|
29
|
+
const baseUrl = env.AIVO_INSTALL_BASE_URL || getReleaseBaseUrl(version);
|
|
25
30
|
const checksumUrl = `${baseUrl}/${assetName}.sha256`;
|
|
26
31
|
const binaryUrl = `${baseUrl}/${assetName}`;
|
|
27
32
|
|
|
@@ -41,21 +46,70 @@ async function main() {
|
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
const nativeDir = getNativeDir();
|
|
44
|
-
const binaryPath = getInstalledBinaryPath();
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const binaryPath = getInstalledBinaryPath(platform, arch);
|
|
50
|
+
installBinary({
|
|
51
|
+
binary,
|
|
52
|
+
binaryPath,
|
|
53
|
+
nativeDir,
|
|
54
|
+
platform,
|
|
55
|
+
fsImpl
|
|
56
|
+
});
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
logger.log(`Installed aivo ${version} (${assetName})`);
|
|
59
|
+
if (platform === "win32") {
|
|
60
|
+
logger.log("If `aivo` is not recognized yet, open a new terminal and try again.");
|
|
50
61
|
}
|
|
51
|
-
|
|
52
|
-
console.log(`Installed aivo ${version} (${assetName})`);
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
function downloadText(url) {
|
|
56
65
|
return downloadBuffer(url).then((buffer) => buffer.toString("utf8"));
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
function getReleaseBaseUrl(version) {
|
|
69
|
+
return `https://github.com/yuanchuan/aivo/releases/download/v${version}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function installBinary({ binary, binaryPath, nativeDir, platform, fsImpl = fs }) {
|
|
73
|
+
const tempPath = path.join(
|
|
74
|
+
nativeDir,
|
|
75
|
+
`${path.basename(binaryPath)}.tmp-${process.pid}-${Date.now()}`
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
fsImpl.mkdirSync(nativeDir, { recursive: true });
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
fsImpl.writeFileSync(tempPath, binary);
|
|
82
|
+
if (platform !== "win32") {
|
|
83
|
+
fsImpl.chmodSync(tempPath, 0o755);
|
|
84
|
+
}
|
|
85
|
+
fsImpl.renameSync(tempPath, binaryPath);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
cleanupTempFile(fsImpl, tempPath);
|
|
88
|
+
throw new Error(`Failed to install ${path.basename(binaryPath)}: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function cleanupTempFile(fsImpl, tempPath) {
|
|
93
|
+
if (typeof fsImpl.rmSync === "function") {
|
|
94
|
+
fsImpl.rmSync(tempPath, { force: true });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
fsImpl.unlinkSync(tempPath);
|
|
100
|
+
} catch {
|
|
101
|
+
// ignore cleanup failures
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatInstallError(error, platform = process.platform) {
|
|
106
|
+
const lines = [error.message, `Repair with: ${REPAIR_COMMAND}`];
|
|
107
|
+
if (platform === "win32") {
|
|
108
|
+
lines.push("If you just installed aivo, open a new terminal and try again.");
|
|
109
|
+
}
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
|
|
59
113
|
function downloadBuffer(url, redirectCount = 0) {
|
|
60
114
|
return new Promise((resolve, reject) => {
|
|
61
115
|
const proto = url.startsWith("https://") ? https : require("node:http");
|
|
@@ -106,7 +160,19 @@ function downloadBuffer(url, redirectCount = 0) {
|
|
|
106
160
|
});
|
|
107
161
|
}
|
|
108
162
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
163
|
+
if (require.main === module) {
|
|
164
|
+
main().catch((error) => {
|
|
165
|
+
console.error(formatInstallError(error));
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
REPAIR_COMMAND,
|
|
172
|
+
downloadBuffer,
|
|
173
|
+
downloadText,
|
|
174
|
+
formatInstallError,
|
|
175
|
+
getReleaseBaseUrl,
|
|
176
|
+
installBinary,
|
|
177
|
+
main
|
|
178
|
+
};
|