@hippox/codegenie 3.11.2026-alpha.1 → 3.11.2026-alpha.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/bin/codegenie.js +49 -8
- package/bin/uninstall.js +6 -5
- package/lib/cleanup.js +30 -9
- package/lib/startup-hint.js +16 -4
- package/package.json +2 -2
package/bin/codegenie.js
CHANGED
|
@@ -1,27 +1,68 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { spawn } = require("child_process")
|
|
4
4
|
const { buildLaunchEnv } = require("../lib/launch.js")
|
|
5
5
|
const { resolveBinary } = require("../lib/resolve-binary.js")
|
|
6
6
|
const { shouldPrintStartupHint, startupHintText } = require("../lib/startup-hint.js")
|
|
7
7
|
|
|
8
|
+
const SPINNER = ["\u2807", "\u280B", "\u2819", "\u2838", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
|
|
9
|
+
|
|
8
10
|
function main() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const argv = process.argv.slice(2)
|
|
12
|
+
const showHint = shouldPrintStartupHint(argv)
|
|
13
|
+
|
|
14
|
+
let spinnerInterval = null
|
|
15
|
+
let spinnerIdx = 0
|
|
16
|
+
if (showHint && process.stderr.isTTY) {
|
|
17
|
+
process.stderr.write("\x1b[?25l")
|
|
18
|
+
const draw = () => {
|
|
19
|
+
process.stderr.write(`\r \x1b[36m\x1b[1mCodeGenie\x1b[0m \x1b[2m${SPINNER[spinnerIdx++ % SPINNER.length]} Loading...\x1b[0m`)
|
|
20
|
+
}
|
|
21
|
+
draw()
|
|
22
|
+
spinnerInterval = setInterval(draw, 100)
|
|
23
|
+
} else if (showHint) {
|
|
24
|
+
process.stderr.write(startupHintText() + "\n")
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
const resolved = resolveBinary()
|
|
14
|
-
|
|
28
|
+
|
|
29
|
+
const child = spawn(resolved.binaryPath, argv, {
|
|
15
30
|
stdio: "inherit",
|
|
16
31
|
env: buildLaunchEnv(resolved.packageName, resolved.platformDir, process.env),
|
|
17
32
|
})
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
child.on("spawn", () => {
|
|
35
|
+
if (spinnerInterval) {
|
|
36
|
+
clearInterval(spinnerInterval)
|
|
37
|
+
spinnerInterval = null
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
child.on("error", (err) => {
|
|
42
|
+
if (spinnerInterval) {
|
|
43
|
+
clearInterval(spinnerInterval)
|
|
44
|
+
}
|
|
45
|
+
process.stderr.write("\r\x1b[K\x1b[?25h")
|
|
46
|
+
console.error(`[codegenie] Failed to start ${resolved.packageName}: ${err.message}`)
|
|
21
47
|
process.exit(1)
|
|
22
|
-
}
|
|
48
|
+
})
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
child.on("exit", (code, signal) => {
|
|
51
|
+
if (spinnerInterval) {
|
|
52
|
+
clearInterval(spinnerInterval)
|
|
53
|
+
}
|
|
54
|
+
if (signal) {
|
|
55
|
+
process.kill(process.pid, signal)
|
|
56
|
+
} else {
|
|
57
|
+
process.exit(code ?? 1)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
function forwardSignal(sig) {
|
|
62
|
+
if (child.pid) child.kill(sig)
|
|
63
|
+
}
|
|
64
|
+
process.on("SIGINT", forwardSignal)
|
|
65
|
+
process.on("SIGTERM", forwardSignal)
|
|
25
66
|
}
|
|
26
67
|
|
|
27
68
|
main()
|
package/bin/uninstall.js
CHANGED
|
@@ -6,12 +6,13 @@ try {
|
|
|
6
6
|
const result = cleanupAfterNpmUninstall()
|
|
7
7
|
if (result.removed) {
|
|
8
8
|
console.log(`[codegenie] Removed runtime directory: ${result.rootDir}`)
|
|
9
|
-
} else if (result.reason === "standalone-active") {
|
|
10
|
-
console.log("[codegenie] Skip runtime cleanup because standalone installation is still active.")
|
|
11
9
|
} else if (result.reason === "package-mismatch") {
|
|
12
|
-
console.log(
|
|
13
|
-
} else if (result.reason === "
|
|
14
|
-
console.log("[codegenie]
|
|
10
|
+
console.log(`[codegenie] Skip runtime cleanup because install context belongs to another npm package.`)
|
|
11
|
+
} else if (result.reason === "missing") {
|
|
12
|
+
console.log("[codegenie] Runtime directory already removed.")
|
|
13
|
+
} else if (result.reason === "remove-failed") {
|
|
14
|
+
const hint = result.error && result.error.message ? ` (${result.error.message})` : ""
|
|
15
|
+
console.warn(`[codegenie] Failed to remove runtime directory${hint}. Remove manually: ${result.rootDir}`)
|
|
15
16
|
}
|
|
16
17
|
} catch (error) {
|
|
17
18
|
const message = error && error.message ? error.message : String(error)
|
package/lib/cleanup.js
CHANGED
|
@@ -36,6 +36,25 @@ function readInstallContext(homeDir = os.homedir()) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function removeWithRetry(targetPath, maxRetries = 2) {
|
|
40
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
41
|
+
try {
|
|
42
|
+
fs.rmSync(targetPath, { recursive: true, force: true })
|
|
43
|
+
return { success: true, error: null }
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const code = err && err.code
|
|
46
|
+
if ((code === "EBUSY" || code === "EPERM") && attempt < maxRetries) {
|
|
47
|
+
const delay = (attempt + 1) * 500
|
|
48
|
+
const end = Date.now() + delay
|
|
49
|
+
while (Date.now() < end) { /* busy wait */ }
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
return { success: false, error: err }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { success: false, error: null }
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
function cleanupAfterNpmUninstall(options = {}) {
|
|
40
59
|
const homeDir = options.homeDir || os.homedir()
|
|
41
60
|
const expectedPackageName =
|
|
@@ -43,28 +62,29 @@ function cleanupAfterNpmUninstall(options = {}) {
|
|
|
43
62
|
const paths = runtimePaths(homeDir)
|
|
44
63
|
const context = readInstallContext(homeDir)
|
|
45
64
|
|
|
46
|
-
if (context && context.installSource === "standalone") {
|
|
47
|
-
return { removed: false, reason: "standalone-active", rootDir: paths.rootDir }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
65
|
if (
|
|
51
66
|
context &&
|
|
52
67
|
expectedPackageName &&
|
|
53
68
|
context.npmPackageName &&
|
|
69
|
+
context.installSource === "npm" &&
|
|
54
70
|
!belongsToExpectedPackageFamily(context.npmPackageName, expectedPackageName)
|
|
55
71
|
) {
|
|
56
72
|
return { removed: false, reason: "package-mismatch", rootDir: paths.rootDir }
|
|
57
73
|
}
|
|
58
74
|
|
|
59
|
-
if (!context && fs.existsSync(paths.binaryPath)) {
|
|
60
|
-
return { removed: false, reason: "standalone-binary-present", rootDir: paths.rootDir }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
75
|
if (!fs.existsSync(paths.rootDir)) {
|
|
64
76
|
return { removed: false, reason: "missing", rootDir: paths.rootDir }
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
|
|
79
|
+
const result = removeWithRetry(paths.rootDir)
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
return {
|
|
82
|
+
removed: false,
|
|
83
|
+
reason: "remove-failed",
|
|
84
|
+
rootDir: paths.rootDir,
|
|
85
|
+
error: result.error,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
68
88
|
return { removed: true, reason: "removed", rootDir: paths.rootDir }
|
|
69
89
|
}
|
|
70
90
|
|
|
@@ -72,5 +92,6 @@ module.exports = {
|
|
|
72
92
|
belongsToExpectedPackageFamily,
|
|
73
93
|
cleanupAfterNpmUninstall,
|
|
74
94
|
readInstallContext,
|
|
95
|
+
removeWithRetry,
|
|
75
96
|
runtimePaths,
|
|
76
97
|
}
|
package/lib/startup-hint.js
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
|
+
const NON_INTERACTIVE_ARGS = new Set([
|
|
2
|
+
"--version", "-v", "--help", "-h",
|
|
3
|
+
"check-update", "upgrade", "rollback", "uninstall",
|
|
4
|
+
"login", "logout",
|
|
5
|
+
])
|
|
6
|
+
|
|
7
|
+
function isInteractiveLaunch(argv = []) {
|
|
8
|
+
if (argv.length === 0) return true
|
|
9
|
+
if (argv[0] === "run" || argv[0] === "--") return true
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
function shouldPrintStartupHint(argv = [], options = {}) {
|
|
2
14
|
const env = options.env || process.env
|
|
3
|
-
const platform = options.platform || process.platform
|
|
4
15
|
const isTTY = options.isTTY ?? process.stderr.isTTY
|
|
5
16
|
|
|
6
17
|
if (!isTTY) return false
|
|
7
|
-
if (platform !== "win32") return false
|
|
8
18
|
if (env.CODEGENIE_PREPARE_ONLY === "true" || env.CODEGENIE_WARM_STARTUP === "true") return false
|
|
9
|
-
|
|
19
|
+
if (argv.length > 0 && NON_INTERACTIVE_ARGS.has(argv[0])) return false
|
|
20
|
+
return isInteractiveLaunch(argv)
|
|
10
21
|
}
|
|
11
22
|
|
|
12
23
|
function startupHintText() {
|
|
13
|
-
return "[
|
|
24
|
+
return "\x1b[36m\x1b[1m CodeGenie\x1b[0m\x1b[2m Loading...\x1b[0m"
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
module.exports = {
|
|
28
|
+
isInteractiveLaunch,
|
|
17
29
|
shouldPrintStartupHint,
|
|
18
30
|
startupHintText,
|
|
19
31
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hippox/codegenie",
|
|
3
|
-
"version": "3.11.2026-alpha.
|
|
3
|
+
"version": "3.11.2026-alpha.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "HarmonyOS AI 开发 CLI,binary-first 发布并通过 npm 分发平台二进制",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"ai"
|
|
27
27
|
],
|
|
28
28
|
"optionalDependencies": {
|
|
29
|
-
"@hippox/codegenie-win32-x64": "3.11.2026-alpha.
|
|
29
|
+
"@hippox/codegenie-win32-x64": "3.11.2026-alpha.2"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|