@hippox/codegenie 3.11.2026-rc.2 → 3.11.2026-rc.3
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 +18 -2
- package/bin/codegenie.js +7 -5
- package/bin/postinstall.js +11 -1
- package/bin/uninstall.js +19 -0
- package/lib/cleanup.js +76 -0
- package/lib/launch.js +14 -0
- package/lib/prewarm.js +48 -0
- package/lib/startup-hint.js +19 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -17,11 +17,27 @@ codegenie --version
|
|
|
17
17
|
说明:
|
|
18
18
|
|
|
19
19
|
- npm 安装会直接创建全局 `codegenie` 命令
|
|
20
|
-
- npm 安装阶段会预热 runtime assets
|
|
21
|
-
-
|
|
20
|
+
- npm 安装阶段会预热 runtime assets;Windows 还会额外隐藏预热一次默认启动路径,因此安装时间会略长,但首次可见启动会更快
|
|
21
|
+
- 首次运行通常不再需要解包和安装依赖;首次进入某个工作区时,仍可能有少量项目级初始化
|
|
22
22
|
- 首次运行不会再把二进制复制到 `~/.codegenie/bin`
|
|
23
|
+
- npm 安装/升级会清理旧的 standalone 二进制和临时更新残留
|
|
23
24
|
- `upgrade` 在 npm 安装场景下会提示或委托执行 npm 升级命令,不直接覆写 npm 全局目录
|
|
24
25
|
|
|
26
|
+
## 卸载
|
|
27
|
+
|
|
28
|
+
推荐顺序:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
codegenie uninstall --yes
|
|
32
|
+
npm uninstall -g @hippox/codegenie
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
说明:
|
|
36
|
+
|
|
37
|
+
- `codegenie uninstall --yes` 删除 `~/.codegenie` 运行目录
|
|
38
|
+
- `npm uninstall -g @hippox/codegenie` 删除 npm 全局命令和包记录
|
|
39
|
+
- npm 卸载生命周期脚本会做最佳努力清理,但不同 npm 版本行为不完全一致,不应只依赖 `npm uninstall -g`
|
|
40
|
+
|
|
25
41
|
## 平台说明
|
|
26
42
|
|
|
27
43
|
当前 staged 平台包:`@hippox/codegenie-win32-x64`
|
package/bin/codegenie.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const { spawnSync } = require("child_process")
|
|
4
|
+
const { buildLaunchEnv } = require("../lib/launch.js")
|
|
4
5
|
const { resolveBinary } = require("../lib/resolve-binary.js")
|
|
6
|
+
const { shouldPrintStartupHint, startupHintText } = require("../lib/startup-hint.js")
|
|
5
7
|
|
|
6
8
|
function main() {
|
|
9
|
+
if (shouldPrintStartupHint(process.argv.slice(2))) {
|
|
10
|
+
console.error(startupHintText())
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
const resolved = resolveBinary()
|
|
8
14
|
const result = spawnSync(resolved.binaryPath, process.argv.slice(2), {
|
|
9
15
|
stdio: "inherit",
|
|
10
|
-
env:
|
|
11
|
-
...process.env,
|
|
12
|
-
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
13
|
-
CODEGENIE_NPM_PACKAGE: resolved.packageName,
|
|
14
|
-
},
|
|
16
|
+
env: buildLaunchEnv(resolved.packageName, process.env),
|
|
15
17
|
})
|
|
16
18
|
|
|
17
19
|
if (result.error) {
|
package/bin/postinstall.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const { resolveBinary } = require("../lib/resolve-binary.js")
|
|
4
|
-
const { prewarmBinary } = require("../lib/prewarm.js")
|
|
4
|
+
const { prewarmBinary, warmStartupBinary } = require("../lib/prewarm.js")
|
|
5
5
|
|
|
6
6
|
try {
|
|
7
7
|
const resolved = resolveBinary()
|
|
@@ -16,6 +16,16 @@ try {
|
|
|
16
16
|
} else {
|
|
17
17
|
console.log("[codegenie] Runtime assets are ready. First launch should be faster.")
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
if (process.platform === "win32") {
|
|
21
|
+
console.log("[codegenie] Finalizing hidden first-launch warmup...")
|
|
22
|
+
const startupWarm = warmStartupBinary(resolved.binaryPath)
|
|
23
|
+
if (startupWarm.error) {
|
|
24
|
+
console.warn(`[codegenie] Hidden startup warmup failed: ${startupWarm.error.message}`)
|
|
25
|
+
} else if (typeof startupWarm.status === "number" && startupWarm.status !== 0) {
|
|
26
|
+
console.warn(`[codegenie] Hidden startup warmup exited with code ${startupWarm.status}. First visible launch may still be slower.`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
19
29
|
} catch (error) {
|
|
20
30
|
const message = error && error.message ? error.message : String(error)
|
|
21
31
|
console.warn("[codegenie] npm install completed, but platform binary verification will be deferred to first launch.")
|
package/bin/uninstall.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { cleanupAfterNpmUninstall } = require("../lib/cleanup.js")
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const result = cleanupAfterNpmUninstall()
|
|
7
|
+
if (result.removed) {
|
|
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
|
+
} else if (result.reason === "package-mismatch") {
|
|
12
|
+
console.log("[codegenie] Skip runtime cleanup because install context belongs to another npm package.")
|
|
13
|
+
} else if (result.reason === "standalone-binary-present") {
|
|
14
|
+
console.log("[codegenie] Skip runtime cleanup because standalone binary is still present.")
|
|
15
|
+
}
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const message = error && error.message ? error.message : String(error)
|
|
18
|
+
console.warn(`[codegenie] Runtime cleanup during npm uninstall failed: ${message}`)
|
|
19
|
+
}
|
package/lib/cleanup.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const os = require("os")
|
|
3
|
+
const path = require("path")
|
|
4
|
+
|
|
5
|
+
function runtimePaths(homeDir = os.homedir()) {
|
|
6
|
+
const rootDir = path.join(homeDir, ".codegenie")
|
|
7
|
+
const binDir = path.join(rootDir, "bin")
|
|
8
|
+
return {
|
|
9
|
+
rootDir,
|
|
10
|
+
binDir,
|
|
11
|
+
binaryPath: path.join(binDir, process.platform === "win32" ? "codegenie.exe" : "codegenie"),
|
|
12
|
+
installContextPath: path.join(rootDir, "install-context.json"),
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function belongsToExpectedPackageFamily(actualPackageName, expectedPackageName) {
|
|
21
|
+
return actualPackageName === expectedPackageName || actualPackageName.startsWith(`${expectedPackageName}-`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readInstallContext(homeDir = os.homedir()) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = JSON.parse(fs.readFileSync(runtimePaths(homeDir).installContextPath, "utf-8"))
|
|
27
|
+
if (!isRecord(raw)) return null
|
|
28
|
+
if (raw.installSource !== "npm" && raw.installSource !== "standalone") return null
|
|
29
|
+
return {
|
|
30
|
+
installSource: raw.installSource,
|
|
31
|
+
npmPackageName:
|
|
32
|
+
typeof raw.npmPackageName === "string" && raw.npmPackageName.trim() ? raw.npmPackageName.trim() : null,
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function cleanupAfterNpmUninstall(options = {}) {
|
|
40
|
+
const homeDir = options.homeDir || os.homedir()
|
|
41
|
+
const expectedPackageName =
|
|
42
|
+
options.expectedPackageName || process.env.CODEGENIE_NPM_PACKAGE || process.env.npm_package_name || null
|
|
43
|
+
const paths = runtimePaths(homeDir)
|
|
44
|
+
const context = readInstallContext(homeDir)
|
|
45
|
+
|
|
46
|
+
if (context && context.installSource === "standalone") {
|
|
47
|
+
return { removed: false, reason: "standalone-active", rootDir: paths.rootDir }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
context &&
|
|
52
|
+
expectedPackageName &&
|
|
53
|
+
context.npmPackageName &&
|
|
54
|
+
!belongsToExpectedPackageFamily(context.npmPackageName, expectedPackageName)
|
|
55
|
+
) {
|
|
56
|
+
return { removed: false, reason: "package-mismatch", rootDir: paths.rootDir }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!context && fs.existsSync(paths.binaryPath)) {
|
|
60
|
+
return { removed: false, reason: "standalone-binary-present", rootDir: paths.rootDir }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(paths.rootDir)) {
|
|
64
|
+
return { removed: false, reason: "missing", rootDir: paths.rootDir }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fs.rmSync(paths.rootDir, { recursive: true, force: true })
|
|
68
|
+
return { removed: true, reason: "removed", rootDir: paths.rootDir }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
belongsToExpectedPackageFamily,
|
|
73
|
+
cleanupAfterNpmUninstall,
|
|
74
|
+
readInstallContext,
|
|
75
|
+
runtimePaths,
|
|
76
|
+
}
|
package/lib/launch.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const packageJson = require("../package.json")
|
|
2
|
+
|
|
3
|
+
function buildLaunchEnv(resolvedPackageName, baseEnv = process.env) {
|
|
4
|
+
return {
|
|
5
|
+
...baseEnv,
|
|
6
|
+
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
7
|
+
CODEGENIE_NPM_PACKAGE: packageJson.name,
|
|
8
|
+
CODEGENIE_NPM_PLATFORM_PACKAGE: resolvedPackageName,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
buildLaunchEnv,
|
|
14
|
+
}
|
package/lib/prewarm.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const os = require("os")
|
|
1
3
|
const { spawnSync } = require("child_process")
|
|
2
4
|
|
|
3
5
|
function buildPrewarmEnv(baseEnv = process.env) {
|
|
@@ -5,6 +7,8 @@ function buildPrewarmEnv(baseEnv = process.env) {
|
|
|
5
7
|
...baseEnv,
|
|
6
8
|
CODEGENIE_PREPARE_ONLY: "true",
|
|
7
9
|
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
10
|
+
CODEGENIE_NPM_PACKAGE:
|
|
11
|
+
baseEnv.CODEGENIE_NPM_PACKAGE || baseEnv.npm_package_name || "codegenie",
|
|
8
12
|
}
|
|
9
13
|
}
|
|
10
14
|
|
|
@@ -16,7 +20,51 @@ function prewarmBinary(binaryPath, spawnImpl = spawnSync, baseEnv = process.env)
|
|
|
16
20
|
})
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
function buildStartupWarmEnv(baseEnv = process.env) {
|
|
24
|
+
return {
|
|
25
|
+
...baseEnv,
|
|
26
|
+
CODEGENIE_WARM_STARTUP: "true",
|
|
27
|
+
CODEGENIE_WARM_STARTUP_TIMEOUT_MS: baseEnv.CODEGENIE_WARM_STARTUP_TIMEOUT_MS || "10000",
|
|
28
|
+
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
29
|
+
CODEGENIE_NPM_PACKAGE:
|
|
30
|
+
baseEnv.CODEGENIE_NPM_PACKAGE || baseEnv.npm_package_name || "codegenie",
|
|
31
|
+
OPENCODE_DISABLE_AUTOUPDATE: "true",
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveWarmupCwd(baseEnv = process.env, existsImpl = fs.existsSync) {
|
|
36
|
+
const initCwd = typeof baseEnv.INIT_CWD === "string" ? baseEnv.INIT_CWD.trim() : ""
|
|
37
|
+
if (initCwd && existsImpl(initCwd)) {
|
|
38
|
+
return initCwd
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const userProfile = typeof baseEnv.USERPROFILE === "string" ? baseEnv.USERPROFILE.trim() : ""
|
|
42
|
+
if (userProfile) {
|
|
43
|
+
return userProfile
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const home = typeof baseEnv.HOME === "string" ? baseEnv.HOME.trim() : ""
|
|
47
|
+
if (home) {
|
|
48
|
+
return home
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return os.homedir()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function warmStartupBinary(binaryPath, spawnImpl = spawnSync, baseEnv = process.env, existsImpl = fs.existsSync) {
|
|
55
|
+
return spawnImpl(binaryPath, [], {
|
|
56
|
+
stdio: "ignore",
|
|
57
|
+
timeout: 20000,
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
cwd: resolveWarmupCwd(baseEnv, existsImpl),
|
|
60
|
+
env: buildStartupWarmEnv(baseEnv),
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
19
64
|
module.exports = {
|
|
20
65
|
buildPrewarmEnv,
|
|
66
|
+
buildStartupWarmEnv,
|
|
21
67
|
prewarmBinary,
|
|
68
|
+
resolveWarmupCwd,
|
|
69
|
+
warmStartupBinary,
|
|
22
70
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function shouldPrintStartupHint(argv = [], options = {}) {
|
|
2
|
+
const env = options.env || process.env
|
|
3
|
+
const platform = options.platform || process.platform
|
|
4
|
+
const isTTY = options.isTTY ?? process.stderr.isTTY
|
|
5
|
+
|
|
6
|
+
if (!isTTY) return false
|
|
7
|
+
if (platform !== "win32") return false
|
|
8
|
+
if (env.CODEGENIE_PREPARE_ONLY === "true" || env.CODEGENIE_WARM_STARTUP === "true") return false
|
|
9
|
+
return argv.length === 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function startupHintText() {
|
|
13
|
+
return "[codegenie] Starting interface..."
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
shouldPrintStartupHint,
|
|
18
|
+
startupHintText,
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hippox/codegenie",
|
|
3
|
-
"version": "3.11.2026-rc.
|
|
3
|
+
"version": "3.11.2026-rc.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "HarmonyOS AI 开发 CLI,binary-first 发布并通过 npm 分发平台二进制",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"postinstall": "node ./bin/postinstall.js"
|
|
8
|
+
"postinstall": "node ./bin/postinstall.js",
|
|
9
|
+
"preuninstall": "node ./bin/uninstall.js",
|
|
10
|
+
"uninstall": "node ./bin/uninstall.js",
|
|
11
|
+
"postuninstall": "node ./bin/uninstall.js"
|
|
9
12
|
},
|
|
10
13
|
"bin": {
|
|
11
14
|
"codegenie": "bin/codegenie.js"
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
"ai"
|
|
24
27
|
],
|
|
25
28
|
"optionalDependencies": {
|
|
26
|
-
"@hippox/codegenie-win32-x64": "3.11.2026-rc.
|
|
29
|
+
"@hippox/codegenie-win32-x64": "3.11.2026-rc.3"
|
|
27
30
|
},
|
|
28
31
|
"publishConfig": {
|
|
29
32
|
"access": "public"
|