@hippox/codegenie 4.11.26-demo → 4.12.26-demo-alpha
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 +15 -57
- package/bin/codegenie.js +66 -53
- package/bin/postinstall.js +54 -65
- package/bin/uninstall.js +9 -25
- package/package.json +38 -40
- package/platform-map.json +5 -5
- package/lib/cleanup.js +0 -121
- package/lib/launch.js +0 -18
- package/lib/prewarm.js +0 -78
- package/lib/resolve-binary.js +0 -107
- package/lib/startup-hint.js +0 -31
package/README.md
CHANGED
|
@@ -1,57 +1,15 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @hippox/codegenie
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
安装完成后即可直接执行:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
codegenie --version
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
说明:
|
|
25
|
-
|
|
26
|
-
- npm 安装会直接创建全局 `codegenie` 命令
|
|
27
|
-
- 如果 npm 因 optionalDependencies 配置跳过了平台包,安装脚本会自动补装当前平台包
|
|
28
|
-
- npm 安装阶段会预热 runtime assets;Windows 还会额外隐藏预热一次默认启动路径,因此安装时间会略长,但首次可见启动会更快
|
|
29
|
-
- 首次运行通常不再需要解包和安装依赖;首次进入某个工作区时,仍可能有少量项目级初始化
|
|
30
|
-
- 首次运行不会再把二进制复制到 `~/.codegenie/bin`
|
|
31
|
-
- npm 安装/升级会清理旧安装残留和临时更新残留
|
|
32
|
-
- `upgrade` 在 npm 安装场景下会提示或委托执行 npm 升级命令,不直接覆写 npm 全局目录
|
|
33
|
-
|
|
34
|
-
## 卸载
|
|
35
|
-
|
|
36
|
-
推荐顺序:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
codegenie uninstall --yes
|
|
40
|
-
npm uninstall -g @hippox/codegenie
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
说明:
|
|
44
|
-
|
|
45
|
-
- `codegenie uninstall --yes` 删除 `~/.codegenie` 运行目录
|
|
46
|
-
- `npm uninstall -g @hippox/codegenie` 删除 npm 全局命令和包记录
|
|
47
|
-
- npm 卸载生命周期脚本会做最佳努力清理,但不同 npm 版本行为不完全一致,不应只依赖 `npm uninstall -g`
|
|
48
|
-
|
|
49
|
-
## 平台说明
|
|
50
|
-
|
|
51
|
-
当前 staged 平台包:`@hippox/codegenie-darwin-arm64`, `@hippox/codegenie-darwin-x64`, `@hippox/codegenie-win32-x64`
|
|
52
|
-
|
|
53
|
-
- `baseline` 兼容构建不进入 npm 正式发布范围
|
|
54
|
-
|
|
55
|
-
## 发布说明
|
|
56
|
-
|
|
57
|
-
仓库中的 `packages/npm/codegenie` 为元包模板。正式发布前请先运行仓库脚本生成 `dist/npm/` 下的 publishable 包,再按发布顺序上传各平台包和元包。
|
|
1
|
+
# @hippox/codegenie
|
|
2
|
+
|
|
3
|
+
HarmonyOS AI 开发 CLI 工具。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @hippox/codegenie@alpha
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 支持平台
|
|
12
|
+
|
|
13
|
+
- macOS ARM64 (Apple Silicon)
|
|
14
|
+
- macOS x64 (Intel)
|
|
15
|
+
- Windows x64
|
package/bin/codegenie.js
CHANGED
|
@@ -1,68 +1,81 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
3
|
+
const childProcess = require("child_process")
|
|
4
|
+
const fs = require("fs")
|
|
5
|
+
const path = require("path")
|
|
6
|
+
const os = require("os")
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function run(target) {
|
|
9
|
+
const result = childProcess.spawnSync(target, process.argv.slice(2), {
|
|
10
|
+
stdio: "inherit",
|
|
11
|
+
})
|
|
12
|
+
if (result.error) {
|
|
13
|
+
console.error(result.error.message)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
const code = typeof result.status === "number" ? result.status : 0
|
|
17
|
+
process.exit(code)
|
|
18
|
+
}
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
const envPath = process.env.CODEGENIE_BIN_PATH
|
|
21
|
+
if (envPath) {
|
|
22
|
+
run(envPath)
|
|
23
|
+
}
|
|
13
24
|
|
|
14
|
-
|
|
15
|
-
|
|
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")
|
|
25
|
-
}
|
|
25
|
+
const scriptPath = fs.realpathSync(__filename)
|
|
26
|
+
const scriptDir = path.dirname(scriptPath)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
const cached = path.join(scriptDir, ".codegenie")
|
|
29
|
+
if (fs.existsSync(cached)) {
|
|
30
|
+
run(cached)
|
|
31
|
+
}
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const platformMap = {
|
|
34
|
+
darwin: "darwin",
|
|
35
|
+
linux: "linux",
|
|
36
|
+
win32: "win32",
|
|
37
|
+
}
|
|
38
|
+
const archMap = {
|
|
39
|
+
x64: "x64",
|
|
40
|
+
arm64: "arm64",
|
|
41
|
+
arm: "arm",
|
|
42
|
+
}
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
let platform = platformMap[os.platform()]
|
|
45
|
+
if (!platform) {
|
|
46
|
+
platform = os.platform()
|
|
47
|
+
}
|
|
48
|
+
let arch = archMap[os.arch()]
|
|
49
|
+
if (!arch) {
|
|
50
|
+
arch = os.arch()
|
|
51
|
+
}
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
clearInterval(spinnerInterval)
|
|
44
|
-
}
|
|
45
|
-
process.stderr.write("\r\x1b[K\x1b[?25h")
|
|
46
|
-
console.error(`[codegenie] Failed to start ${resolved.packageName}: ${err.message}`)
|
|
47
|
-
process.exit(1)
|
|
48
|
-
})
|
|
53
|
+
const scopedName = "@hippox/codegenie-" + platform + "-" + arch
|
|
54
|
+
const binary = platform === "win32" ? "codegenie.exe" : "codegenie"
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
function findBinary(startDir) {
|
|
57
|
+
let current = startDir
|
|
58
|
+
for (;;) {
|
|
59
|
+
const modules = path.join(current, "node_modules")
|
|
60
|
+
if (fs.existsSync(modules)) {
|
|
61
|
+
const candidate = path.join(modules, "@hippox", "codegenie-" + platform + "-" + arch, "bin", binary)
|
|
62
|
+
if (fs.existsSync(candidate)) return candidate
|
|
53
63
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
process.exit(code ?? 1)
|
|
64
|
+
const parent = path.dirname(current)
|
|
65
|
+
if (parent === current) {
|
|
66
|
+
return
|
|
58
67
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
function forwardSignal(sig) {
|
|
62
|
-
if (child.pid) child.kill(sig)
|
|
68
|
+
current = parent
|
|
63
69
|
}
|
|
64
|
-
process.on("SIGINT", forwardSignal)
|
|
65
|
-
process.on("SIGTERM", forwardSignal)
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
const resolved = findBinary(scriptDir)
|
|
73
|
+
if (!resolved) {
|
|
74
|
+
console.error(
|
|
75
|
+
"It seems that your package manager failed to install the right version of the codegenie CLI for your platform. " +
|
|
76
|
+
"You can try manually installing \"" + scopedName + "\" package"
|
|
77
|
+
)
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
run(resolved)
|
package/bin/postinstall.js
CHANGED
|
@@ -1,83 +1,72 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const packageJson = require("../package.json")
|
|
3
|
+
const fs = require("fs")
|
|
4
|
+
const path = require("path")
|
|
5
|
+
const os = require("os")
|
|
7
6
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
function detectPlatformAndArch() {
|
|
8
|
+
const platformMap = { darwin: "darwin", linux: "linux", win32: "win32" }
|
|
9
|
+
const archMap = { x64: "x64", arm64: "arm64", arm: "arm" }
|
|
10
|
+
const platform = platformMap[os.platform()] || os.platform()
|
|
11
|
+
const arch = archMap[os.arch()] || os.arch()
|
|
12
|
+
return { platform, arch }
|
|
13
|
+
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
15
|
+
function findBinary() {
|
|
16
|
+
const { platform, arch } = detectPlatformAndArch()
|
|
17
|
+
const scopedDir = path.join("@hippox", "codegenie-" + platform + "-" + arch)
|
|
18
|
+
const binaryName = platform === "win32" ? "codegenie.exe" : "codegenie"
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return false
|
|
20
|
+
let current = path.resolve(__dirname, "..")
|
|
21
|
+
for (;;) {
|
|
22
|
+
const candidate = path.join(current, "node_modules", scopedDir, "bin", binaryName)
|
|
23
|
+
if (fs.existsSync(candidate)) {
|
|
24
|
+
return { binaryPath: candidate, binaryName, binDir: path.dirname(candidate) }
|
|
25
|
+
}
|
|
26
|
+
const parent = path.dirname(current)
|
|
27
|
+
if (parent === current) break
|
|
28
|
+
current = parent
|
|
30
29
|
}
|
|
30
|
+
|
|
31
|
+
throw new Error("Could not find platform package " + scopedDir)
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
function
|
|
34
|
-
let resolved
|
|
34
|
+
async function main() {
|
|
35
35
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const attempted = tryInstallPlatformPackage()
|
|
39
|
-
if (!attempted) {
|
|
40
|
-
const message = firstError && firstError.message ? firstError.message : String(firstError)
|
|
41
|
-
console.warn("[codegenie] npm install completed, but platform binary verification will be deferred to first launch.")
|
|
42
|
-
console.warn(message)
|
|
36
|
+
if (os.platform() === "win32") {
|
|
37
|
+
console.log("Windows detected: binary setup not needed (using packaged .exe)")
|
|
43
38
|
return
|
|
44
39
|
}
|
|
40
|
+
|
|
41
|
+
const { binaryPath, binDir } = findBinary()
|
|
42
|
+
const target = path.join(__dirname, ".codegenie")
|
|
43
|
+
if (fs.existsSync(target)) fs.unlinkSync(target)
|
|
45
44
|
try {
|
|
46
|
-
|
|
47
|
-
} catch
|
|
48
|
-
|
|
49
|
-
console.warn("[codegenie] npm install completed, but platform binary verification will be deferred to first launch.")
|
|
50
|
-
console.warn(message)
|
|
51
|
-
return
|
|
45
|
+
fs.linkSync(binaryPath, target)
|
|
46
|
+
} catch {
|
|
47
|
+
fs.copyFileSync(binaryPath, target)
|
|
52
48
|
}
|
|
53
|
-
|
|
49
|
+
fs.chmodSync(target, 0o755)
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
result = prewarmBinary(resolved.binaryPath, resolved.platformDir)
|
|
63
|
-
}
|
|
64
|
-
if (result.error) {
|
|
65
|
-
console.warn(`[codegenie] Runtime prewarm failed: ${result.error.message}`)
|
|
66
|
-
} else if (typeof result.status === "number" && result.status !== 0) {
|
|
67
|
-
console.warn(`[codegenie] Runtime prewarm exited with code ${result.status}. First launch will finish initialization.`)
|
|
68
|
-
} else {
|
|
69
|
-
console.log("[codegenie] Runtime assets are ready. First launch should be faster.")
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (process.platform === "win32" && process.env.CODEGENIE_SKIP_WARM_STARTUP !== "true") {
|
|
73
|
-
console.log("[codegenie] Finalizing hidden first-launch warmup...")
|
|
74
|
-
const startupWarm = warmStartupBinary(resolved.binaryPath, resolved.platformDir)
|
|
75
|
-
if (startupWarm.error) {
|
|
76
|
-
console.warn(`[codegenie] Hidden startup warmup failed: ${startupWarm.error.message}`)
|
|
77
|
-
} else if (typeof startupWarm.status === "number" && startupWarm.status !== 0) {
|
|
78
|
-
console.warn(`[codegenie] Hidden startup warmup exited with code ${startupWarm.status}. First visible launch may still be slower.`)
|
|
51
|
+
const nativeSrc = path.join(binDir, "mcp-bridge-native")
|
|
52
|
+
const nativeDst = path.join(__dirname, "mcp-bridge-native")
|
|
53
|
+
if (fs.existsSync(nativeSrc)) {
|
|
54
|
+
if (!fs.existsSync(nativeDst)) fs.mkdirSync(nativeDst, { recursive: true })
|
|
55
|
+
for (const file of fs.readdirSync(nativeSrc)) {
|
|
56
|
+
fs.copyFileSync(path.join(nativeSrc, file), path.join(nativeDst, file))
|
|
57
|
+
}
|
|
79
58
|
}
|
|
59
|
+
|
|
60
|
+
console.log("codegenie binary linked successfully")
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Failed to setup codegenie binary:", error.message)
|
|
63
|
+
process.exit(1)
|
|
80
64
|
}
|
|
81
65
|
}
|
|
82
66
|
|
|
83
|
-
|
|
67
|
+
try {
|
|
68
|
+
main()
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("Postinstall script error:", error.message)
|
|
71
|
+
process.exit(0)
|
|
72
|
+
}
|
package/bin/uninstall.js
CHANGED
|
@@ -1,30 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const fs = require("fs")
|
|
4
|
+
const path = require("path")
|
|
4
5
|
|
|
6
|
+
const cached = path.join(__dirname, ".codegenie")
|
|
5
7
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
console.log(`[codegenie] Removed runtime directory: ${result.rootDir}`)
|
|
9
|
-
} else if (result.reason === "package-mismatch") {
|
|
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}`)
|
|
16
|
-
}
|
|
8
|
+
if (fs.existsSync(cached)) fs.unlinkSync(cached)
|
|
9
|
+
} catch {}
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} else if (legacyState.reason === "remove-failed") {
|
|
23
|
-
const hint = legacyState.error && legacyState.error.message ? ` (${legacyState.error.message})` : ""
|
|
24
|
-
console.warn(`[codegenie] Failed to remove legacy login state${hint}. Remove manually: ${legacyState.path}`)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
} catch (error) {
|
|
28
|
-
const message = error && error.message ? error.message : String(error)
|
|
29
|
-
console.warn(`[codegenie] Runtime cleanup during npm uninstall failed: ${message}`)
|
|
30
|
-
}
|
|
11
|
+
const nativeDir = path.join(__dirname, "mcp-bridge-native")
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(nativeDir)) fs.rmSync(nativeDir, { recursive: true })
|
|
14
|
+
} catch {}
|
package/package.json
CHANGED
|
@@ -1,40 +1,38 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hippox/codegenie",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"private": false,
|
|
5
|
-
"description": "HarmonyOS AI 开发 CLI,通过 npm 分发平台二进制",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"postinstall": "node ./bin/postinstall.js",
|
|
9
|
-
"preuninstall": "node ./bin/uninstall.js"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"@hippox/codegenie-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@hippox/codegenie",
|
|
3
|
+
"version": "4.12.26-demo-alpha",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "HarmonyOS AI 开发 CLI,通过 npm 分发平台二进制",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"postinstall": "node ./bin/postinstall.js",
|
|
9
|
+
"preuninstall": "node ./bin/uninstall.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"codegenie": "bin/codegenie.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin",
|
|
16
|
+
"lib",
|
|
17
|
+
"README.md",
|
|
18
|
+
"platform-map.json"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"cli",
|
|
22
|
+
"harmonyos",
|
|
23
|
+
"codegenie",
|
|
24
|
+
"ai"
|
|
25
|
+
],
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"@hippox/codegenie-darwin-arm64": "4.12.26-demo-alpha",
|
|
28
|
+
"@hippox/codegenie-darwin-x64": "4.12.26-demo-alpha",
|
|
29
|
+
"@hippox/codegenie-win32-x64": "4.12.26-demo-alpha"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://gitcode.com/codegenie/codegenie.git"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/platform-map.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
{
|
|
2
|
-
"darwin-arm64": "@hippox/codegenie-darwin-arm64",
|
|
3
|
-
"darwin-x64": "@hippox/codegenie-darwin-x64",
|
|
4
|
-
"win32-x64": "@hippox/codegenie-win32-x64"
|
|
5
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"darwin-arm64": "@hippox/codegenie-darwin-arm64",
|
|
3
|
+
"darwin-x64": "@hippox/codegenie-darwin-x64",
|
|
4
|
+
"win32-x64": "@hippox/codegenie-win32-x64"
|
|
5
|
+
}
|
package/lib/cleanup.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
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 legacyLoginStatePath(homeDir = os.homedir()) {
|
|
17
|
-
return path.join(homeDir, ".config", "ts-login")
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function isRecord(value) {
|
|
21
|
-
return typeof value === "object" && value !== null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function belongsToExpectedPackageFamily(actualPackageName, expectedPackageName) {
|
|
25
|
-
return actualPackageName === expectedPackageName || actualPackageName.startsWith(`${expectedPackageName}-`)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function readInstallContext(homeDir = os.homedir()) {
|
|
29
|
-
try {
|
|
30
|
-
const raw = JSON.parse(fs.readFileSync(runtimePaths(homeDir).installContextPath, "utf-8"))
|
|
31
|
-
if (!isRecord(raw)) return null
|
|
32
|
-
if (raw.installSource !== "npm" && raw.installSource !== "standalone") return null
|
|
33
|
-
return {
|
|
34
|
-
installSource: raw.installSource,
|
|
35
|
-
npmPackageName:
|
|
36
|
-
typeof raw.npmPackageName === "string" && raw.npmPackageName.trim() ? raw.npmPackageName.trim() : null,
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function removeWithRetry(targetPath, maxRetries = 2) {
|
|
44
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
45
|
-
try {
|
|
46
|
-
fs.rmSync(targetPath, { recursive: true, force: true })
|
|
47
|
-
return { success: true, error: null }
|
|
48
|
-
} catch (err) {
|
|
49
|
-
const code = err && err.code
|
|
50
|
-
if ((code === "EBUSY" || code === "EPERM") && attempt < maxRetries) {
|
|
51
|
-
const delay = (attempt + 1) * 500
|
|
52
|
-
const end = Date.now() + delay
|
|
53
|
-
while (Date.now() < end) { /* busy wait */ }
|
|
54
|
-
continue
|
|
55
|
-
}
|
|
56
|
-
return { success: false, error: err }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return { success: false, error: null }
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function cleanupAfterNpmUninstall(options = {}) {
|
|
63
|
-
const homeDir = options.homeDir || os.homedir()
|
|
64
|
-
const expectedPackageName =
|
|
65
|
-
options.expectedPackageName || process.env.CODEGENIE_NPM_PACKAGE || process.env.npm_package_name || null
|
|
66
|
-
const paths = runtimePaths(homeDir)
|
|
67
|
-
const context = readInstallContext(homeDir)
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
context &&
|
|
71
|
-
expectedPackageName &&
|
|
72
|
-
context.npmPackageName &&
|
|
73
|
-
context.installSource === "npm" &&
|
|
74
|
-
!belongsToExpectedPackageFamily(context.npmPackageName, expectedPackageName)
|
|
75
|
-
) {
|
|
76
|
-
return { removed: false, reason: "package-mismatch", rootDir: paths.rootDir }
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!fs.existsSync(paths.rootDir)) {
|
|
80
|
-
return { removed: false, reason: "missing", rootDir: paths.rootDir }
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const result = removeWithRetry(paths.rootDir)
|
|
84
|
-
if (!result.success) {
|
|
85
|
-
return {
|
|
86
|
-
removed: false,
|
|
87
|
-
reason: "remove-failed",
|
|
88
|
-
rootDir: paths.rootDir,
|
|
89
|
-
error: result.error,
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return { removed: true, reason: "removed", rootDir: paths.rootDir }
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function cleanupLegacyLoginState(options = {}) {
|
|
96
|
-
const homeDir = options.homeDir || os.homedir()
|
|
97
|
-
const targetPath = legacyLoginStatePath(homeDir)
|
|
98
|
-
if (!fs.existsSync(targetPath)) {
|
|
99
|
-
return { removed: false, reason: "missing", path: targetPath }
|
|
100
|
-
}
|
|
101
|
-
const result = removeWithRetry(targetPath)
|
|
102
|
-
if (!result.success) {
|
|
103
|
-
return {
|
|
104
|
-
removed: false,
|
|
105
|
-
reason: "remove-failed",
|
|
106
|
-
path: targetPath,
|
|
107
|
-
error: result.error,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return { removed: true, reason: "removed", path: targetPath }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
module.exports = {
|
|
114
|
-
belongsToExpectedPackageFamily,
|
|
115
|
-
cleanupAfterNpmUninstall,
|
|
116
|
-
cleanupLegacyLoginState,
|
|
117
|
-
legacyLoginStatePath,
|
|
118
|
-
readInstallContext,
|
|
119
|
-
removeWithRetry,
|
|
120
|
-
runtimePaths,
|
|
121
|
-
}
|
package/lib/launch.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const packageJson = require("../package.json")
|
|
2
|
-
|
|
3
|
-
function buildLaunchEnv(resolvedPackageName, platformDir, baseEnv = process.env) {
|
|
4
|
-
const env = {
|
|
5
|
-
...baseEnv,
|
|
6
|
-
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
7
|
-
CODEGENIE_NPM_PACKAGE: packageJson.name,
|
|
8
|
-
CODEGENIE_NPM_PLATFORM_PACKAGE: resolvedPackageName,
|
|
9
|
-
}
|
|
10
|
-
if (platformDir) {
|
|
11
|
-
env.CODEGENIE_NPM_PLATFORM_DIR = platformDir
|
|
12
|
-
}
|
|
13
|
-
return env
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
module.exports = {
|
|
17
|
-
buildLaunchEnv,
|
|
18
|
-
}
|
package/lib/prewarm.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const fs = require("fs")
|
|
2
|
-
const os = require("os")
|
|
3
|
-
const { spawnSync } = require("child_process")
|
|
4
|
-
|
|
5
|
-
function buildPrewarmEnv(platformDir, baseEnv = process.env) {
|
|
6
|
-
const env = {
|
|
7
|
-
...baseEnv,
|
|
8
|
-
CODEGENIE_PREPARE_ONLY: "true",
|
|
9
|
-
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
10
|
-
CODEGENIE_NPM_PACKAGE:
|
|
11
|
-
baseEnv.CODEGENIE_NPM_PACKAGE || baseEnv.npm_package_name || "codegenie",
|
|
12
|
-
}
|
|
13
|
-
if (platformDir) {
|
|
14
|
-
env.CODEGENIE_NPM_PLATFORM_DIR = platformDir
|
|
15
|
-
}
|
|
16
|
-
return env
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function prewarmBinary(binaryPath, platformDir, spawnImpl = spawnSync, baseEnv = process.env) {
|
|
20
|
-
return spawnImpl(binaryPath, [], {
|
|
21
|
-
stdio: "ignore",
|
|
22
|
-
timeout: 120000,
|
|
23
|
-
env: buildPrewarmEnv(platformDir, baseEnv),
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function buildStartupWarmEnv(platformDir, baseEnv = process.env) {
|
|
28
|
-
const env = {
|
|
29
|
-
...baseEnv,
|
|
30
|
-
CODEGENIE_WARM_STARTUP: "true",
|
|
31
|
-
CODEGENIE_WARM_STARTUP_TIMEOUT_MS: baseEnv.CODEGENIE_WARM_STARTUP_TIMEOUT_MS || "10000",
|
|
32
|
-
CODEGENIE_INSTALL_SOURCE: "npm",
|
|
33
|
-
CODEGENIE_NPM_PACKAGE:
|
|
34
|
-
baseEnv.CODEGENIE_NPM_PACKAGE || baseEnv.npm_package_name || "codegenie",
|
|
35
|
-
OPENCODE_DISABLE_AUTOUPDATE: "true",
|
|
36
|
-
}
|
|
37
|
-
if (platformDir) {
|
|
38
|
-
env.CODEGENIE_NPM_PLATFORM_DIR = platformDir
|
|
39
|
-
}
|
|
40
|
-
return env
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function resolveWarmupCwd(baseEnv = process.env, existsImpl = fs.existsSync) {
|
|
44
|
-
const initCwd = typeof baseEnv.INIT_CWD === "string" ? baseEnv.INIT_CWD.trim() : ""
|
|
45
|
-
if (initCwd && existsImpl(initCwd)) {
|
|
46
|
-
return initCwd
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const userProfile = typeof baseEnv.USERPROFILE === "string" ? baseEnv.USERPROFILE.trim() : ""
|
|
50
|
-
if (userProfile) {
|
|
51
|
-
return userProfile
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const home = typeof baseEnv.HOME === "string" ? baseEnv.HOME.trim() : ""
|
|
55
|
-
if (home) {
|
|
56
|
-
return home
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return os.homedir()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function warmStartupBinary(binaryPath, platformDir, spawnImpl = spawnSync, baseEnv = process.env, existsImpl = fs.existsSync) {
|
|
63
|
-
return spawnImpl(binaryPath, [], {
|
|
64
|
-
stdio: "ignore",
|
|
65
|
-
timeout: 20000,
|
|
66
|
-
windowsHide: true,
|
|
67
|
-
cwd: resolveWarmupCwd(baseEnv, existsImpl),
|
|
68
|
-
env: buildStartupWarmEnv(platformDir, baseEnv),
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
buildPrewarmEnv,
|
|
74
|
-
buildStartupWarmEnv,
|
|
75
|
-
prewarmBinary,
|
|
76
|
-
resolveWarmupCwd,
|
|
77
|
-
warmStartupBinary,
|
|
78
|
-
}
|
package/lib/resolve-binary.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const fs = require("fs")
|
|
3
|
-
const path = require("path")
|
|
4
|
-
|
|
5
|
-
function loadPlatformMap() {
|
|
6
|
-
const platformMapPath = path.join(__dirname, "..", "platform-map.json")
|
|
7
|
-
return JSON.parse(fs.readFileSync(platformMapPath, "utf8"))
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function detectLibc() {
|
|
11
|
-
if (process.platform !== "linux") return null
|
|
12
|
-
if (process.report && typeof process.report.getReport === "function") {
|
|
13
|
-
const report = process.report.getReport()
|
|
14
|
-
if (report && report.header && report.header.glibcVersionRuntime) {
|
|
15
|
-
return "glibc"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return "musl"
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function resolvePackageName() {
|
|
22
|
-
const platformMap = loadPlatformMap()
|
|
23
|
-
const libc = detectLibc()
|
|
24
|
-
|
|
25
|
-
let platformKey = null
|
|
26
|
-
if (process.platform === "darwin" && process.arch === "arm64") platformKey = "darwin-arm64"
|
|
27
|
-
else if (process.platform === "darwin" && process.arch === "x64") platformKey = "darwin-x64"
|
|
28
|
-
else if (process.platform === "linux" && process.arch === "arm64" && libc === "musl") platformKey = "linux-arm64-musl"
|
|
29
|
-
else if (process.platform === "linux" && process.arch === "arm64") platformKey = "linux-arm64"
|
|
30
|
-
else if (process.platform === "linux" && process.arch === "x64" && libc === "musl") platformKey = "linux-x64-musl"
|
|
31
|
-
else if (process.platform === "linux" && process.arch === "x64") platformKey = "linux-x64"
|
|
32
|
-
else if (process.platform === "win32" && process.arch === "x64") platformKey = "win32-x64"
|
|
33
|
-
|
|
34
|
-
if (platformKey && platformMap[platformKey]) {
|
|
35
|
-
return platformMap[platformKey]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const libcSuffix = libc ? `/${libc}` : ""
|
|
39
|
-
throw new Error(`Unsupported npm platform: ${process.platform}/${process.arch}${libcSuffix}`)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function resolvePlatformPackageDir(packageName) {
|
|
43
|
-
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
|
44
|
-
return path.dirname(packageJsonPath)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function ensureExecutable(binaryPath) {
|
|
48
|
-
if (process.platform === "win32") {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
const stats = fs.statSync(binaryPath)
|
|
53
|
-
const mode = stats.mode & 0o777
|
|
54
|
-
if (mode !== 0o755 && mode !== 0o775 && mode !== 0o777) {
|
|
55
|
-
fs.chmodSync(binaryPath, 0o755)
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
// ignore permission check failure
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function resolveBinary() {
|
|
63
|
-
const packageName = resolvePackageName()
|
|
64
|
-
let platformDir = ""
|
|
65
|
-
try {
|
|
66
|
-
platformDir = resolvePlatformPackageDir(packageName)
|
|
67
|
-
} catch (error) {
|
|
68
|
-
const metaPackageName = (() => {
|
|
69
|
-
try { return require("../package.json").name } catch { return null }
|
|
70
|
-
})() || process.env.npm_package_name || "@hippox/codegenie"
|
|
71
|
-
const hint = [
|
|
72
|
-
`[codegenie] Failed to locate installed binary package for ${process.platform}/${process.arch}.`,
|
|
73
|
-
`[codegenie] Expected package: ${packageName}`,
|
|
74
|
-
`[codegenie] Reinstall with \`npm install -g ${metaPackageName}\`.`,
|
|
75
|
-
`[codegenie] If your npm disables optionalDependencies, install \`${packageName}\` manually and retry.`,
|
|
76
|
-
].join("\n")
|
|
77
|
-
const wrapped = new Error(hint)
|
|
78
|
-
wrapped.cause = error
|
|
79
|
-
throw wrapped
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const binaryPath = path.join(
|
|
83
|
-
platformDir,
|
|
84
|
-
"bin",
|
|
85
|
-
process.platform === "win32" ? "codegenie.exe" : "codegenie",
|
|
86
|
-
)
|
|
87
|
-
if (!fs.existsSync(binaryPath)) {
|
|
88
|
-
throw new Error(`[codegenie] Binary file missing from ${packageName}: ${binaryPath}`)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
ensureExecutable(binaryPath)
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
packageName,
|
|
95
|
-
binaryPath,
|
|
96
|
-
platformDir,
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
module.exports = {
|
|
101
|
-
detectLibc,
|
|
102
|
-
resolveBinary,
|
|
103
|
-
resolvePlatformPackageDir,
|
|
104
|
-
resolvePackageName,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
package/lib/startup-hint.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
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
|
-
|
|
13
|
-
function shouldPrintStartupHint(argv = [], options = {}) {
|
|
14
|
-
const env = options.env || process.env
|
|
15
|
-
const isTTY = options.isTTY ?? process.stderr.isTTY
|
|
16
|
-
|
|
17
|
-
if (!isTTY) return false
|
|
18
|
-
if (env.CODEGENIE_PREPARE_ONLY === "true" || env.CODEGENIE_WARM_STARTUP === "true") return false
|
|
19
|
-
if (argv.length > 0 && NON_INTERACTIVE_ARGS.has(argv[0])) return false
|
|
20
|
-
return isInteractiveLaunch(argv)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function startupHintText() {
|
|
24
|
-
return "\x1b[36m\x1b[1m CodeGenie\x1b[0m\x1b[2m Loading...\x1b[0m"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
isInteractiveLaunch,
|
|
29
|
-
shouldPrintStartupHint,
|
|
30
|
-
startupHintText,
|
|
31
|
-
}
|