hordecode 0.0.0-stage → 0.1.1
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/LICENSE +23 -0
- package/README.md +125 -2
- package/bin/horde.exe +10 -0
- package/bin/hordecode.exe +10 -0
- package/package.json +49 -3
- package/postinstall.mjs +221 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MiMo Code, Xiaomi Corporation
|
|
4
|
+
Copyright (c) 2025 opencode
|
|
5
|
+
Copyright (c) 2026 Hordecode
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,126 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://hordecode.com"><img src="https://raw.githubusercontent.com/siliconstencil/hordecode/main/assets/logo.svg" alt="Hordecode" width="96" height="96"></a>
|
|
3
|
+
</p>
|
|
4
|
+
<h1 align="center">Hordecode</h1>
|
|
5
|
+
<p align="center">The AI coding agent built for the terminal.</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://hordecode.com">Website</a> |
|
|
8
|
+
<a href="https://hordecode.com/docs/">Docs</a> |
|
|
9
|
+
<a href="https://github.com/siliconstencil/hordecode">GitHub</a>
|
|
10
|
+
</p>
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/hordecode"><img alt="npm" src="https://img.shields.io/npm/v/hordecode?style=flat-square" /></a>
|
|
13
|
+
<a href="https://github.com/siliconstencil/hordecode/actions/workflows/fork-typecheck.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/siliconstencil/hordecode/fork-typecheck.yml?style=flat-square&branch=main" /></a>
|
|
14
|
+
<a href="https://github.com/siliconstencil/hordecode/blob/main/LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" /></a>
|
|
15
|
+
</p>
|
|
2
16
|
|
|
3
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
Hordecode is a terminal-native AI coding agent. It is a friendly fork of
|
|
20
|
+
[opencode](https://github.com/anomalyco/opencode) that tracks upstream, carries a
|
|
21
|
+
delta-port of [MiMo Code](https://github.com/XiaomiMiMo/MiMo-Code), and adds its own
|
|
22
|
+
layer: a supervisor for long-running work, a harness pipeline, an inbox, persistent
|
|
23
|
+
memory with an optional on-device embedding index, skills, workflows and cron.
|
|
24
|
+
|
|
25
|
+
Every opencode provider, plugin and config file keeps working. Hordecode is not
|
|
26
|
+
affiliated with or endorsed by either upstream project.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# npm (all platforms)
|
|
34
|
+
npm install -g hordecode
|
|
35
|
+
|
|
36
|
+
# One-line install (macOS / Linux)
|
|
37
|
+
curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash
|
|
38
|
+
|
|
39
|
+
# Run
|
|
40
|
+
horde
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`hordecode` is an alias of `horde`; both commands are installed. On first launch, add a
|
|
44
|
+
provider with `horde providers` (or `horde auth`), then start a session.
|
|
45
|
+
|
|
46
|
+
Upgrade an existing install with the method it was installed with:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
horde upgrade # latest
|
|
50
|
+
horde upgrade 0.1.1 # a specific version
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> [!WARNING]
|
|
54
|
+
> 0.1.x is a pre-release. APIs, config and storage may change without a migration path.
|
|
55
|
+
|
|
56
|
+
<details>
|
|
57
|
+
<summary><strong>Install script details</strong></summary>
|
|
58
|
+
|
|
59
|
+
The script installs into `~/.hordecode/bin` and adds that directory to your shell
|
|
60
|
+
profile unless you pass `--no-modify-path`.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash -s -- --version 0.1.1
|
|
64
|
+
curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash -s -- --no-modify-path
|
|
65
|
+
./install --binary /path/to/hordecode # install a locally built binary
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Set `HORDECODE_RELEASE_BASE` to install from a mirror of the GitHub releases.
|
|
69
|
+
|
|
70
|
+
</details>
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary><strong>Platforms</strong></summary>
|
|
74
|
+
|
|
75
|
+
| Target | npm package | Notes |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| linux-x64 | `hordecode-linux-x64` | verified target; ships the on-device embedding index |
|
|
78
|
+
| linux-x64 (no AVX2) | `hordecode-linux-x64-baseline` | |
|
|
79
|
+
| linux-x64 musl | `hordecode-linux-x64-musl`, `-baseline-musl` | Alpine and other musl systems |
|
|
80
|
+
| linux-arm64 | `hordecode-linux-arm64`, `-musl` | |
|
|
81
|
+
| darwin-arm64 | `hordecode-darwin-arm64` | |
|
|
82
|
+
| darwin-x64 | `hordecode-darwin-x64`, `-baseline` | no embedding index (no onnxruntime binding) |
|
|
83
|
+
| windows-x64 | `hordecode-windows-x64`, `-baseline` | use npm; the install script needs bash |
|
|
84
|
+
| windows-arm64 | `hordecode-windows-arm64` | use npm |
|
|
85
|
+
|
|
86
|
+
The `hordecode` npm package selects the right platform package at install time. On
|
|
87
|
+
targets without the embedding index, memory search falls back to BM25.
|
|
88
|
+
|
|
89
|
+
</details>
|
|
90
|
+
|
|
91
|
+
## What Hordecode adds
|
|
92
|
+
|
|
93
|
+
Fork features surface as tools the agent can call, not as extra CLI commands:
|
|
94
|
+
|
|
95
|
+
- **supervisor**: runs delegated work as supervised child sessions with spawn
|
|
96
|
+
references, task links and waiters, so multi-step work is tracked past one turn.
|
|
97
|
+
- **task** and **workflow**: units of work, and declared multi-step workflows.
|
|
98
|
+
- **memory-add / memory-recall / memory-update / memory-session / memory-status /
|
|
99
|
+
memory-dump / memory-gc**: persistent project memory. With `memory.embedding`
|
|
100
|
+
enabled (linux-x64) recall uses an on-device embedding index; otherwise BM25.
|
|
101
|
+
- **skill** and **skill-search**: discover and load skills from the user and project
|
|
102
|
+
skill directories; the bundled research pipeline ships as a skill set.
|
|
103
|
+
- **cron**: scheduled runs.
|
|
104
|
+
- **session**: manage sessions and worktrees, including a fleet view of running agents.
|
|
105
|
+
|
|
106
|
+
Behind the tools, the **inbox** carries messages between agents and sessions, and the
|
|
107
|
+
**harness** pipeline drives supervised runs.
|
|
108
|
+
|
|
109
|
+
The upstream tool set (read, edit, bash, grep, glob, lsp, MCP, plan, question, ...)
|
|
110
|
+
and the built-in **build**, **plan** and **general** agents are unchanged. Switch
|
|
111
|
+
agents with `Tab`.
|
|
112
|
+
|
|
113
|
+
### Memory search: keyword by default, semantic opt-in
|
|
114
|
+
|
|
115
|
+
Memory recall is keyword search (BM25) out of the box and needs no download. Semantic
|
|
116
|
+
(vector) search is opt-in. Enable it in `hordecode.json`:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{ "memory": { "embedding": true } }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
On first use Hordecode downloads the embedding model once (about 133 MB) into its cache
|
|
123
|
+
directory; recall stays keyword-only until the model is ready, then becomes hybrid
|
|
124
|
+
(keyword + semantic). The on-device index ships in the linux-x64 package; on other targets
|
|
125
|
+
the setting is accepted but recall stays keyword-only. `memory-status` reports the current
|
|
126
|
+
mode and vector count, and `horde debug embedding-check` verifies the pipeline.
|
package/bin/horde.exe
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
echo "Error: hordecode's postinstall script was not run." >&2
|
|
2
|
+
echo "" >&2
|
|
3
|
+
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
|
4
|
+
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
|
5
|
+
echo "" >&2
|
|
6
|
+
echo "To fix this, run the postinstall script manually:" >&2
|
|
7
|
+
echo " cd node_modules/hordecode && node postinstall.mjs" >&2
|
|
8
|
+
echo "" >&2
|
|
9
|
+
echo "Or reinstall hordecode without the --ignore-scripts flag." >&2
|
|
10
|
+
exit 1
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
echo "Error: hordecode's postinstall script was not run." >&2
|
|
2
|
+
echo "" >&2
|
|
3
|
+
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
|
4
|
+
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
|
5
|
+
echo "" >&2
|
|
6
|
+
echo "To fix this, run the postinstall script manually:" >&2
|
|
7
|
+
echo " cd node_modules/hordecode && node postinstall.mjs" >&2
|
|
8
|
+
echo "" >&2
|
|
9
|
+
echo "Or reinstall hordecode without the --ignore-scripts flag." >&2
|
|
10
|
+
exit 1
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hordecode",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"description": "Hordecode: AI coding agent for the terminal, a friendly fork of opencode with a MiMo Code delta-port",
|
|
4
|
+
"bin": {
|
|
5
|
+
"horde": "./bin/horde.exe",
|
|
6
|
+
"hordecode": "./bin/hordecode.exe"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node ./postinstall.mjs"
|
|
10
|
+
},
|
|
11
|
+
"version": "0.1.1",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://hordecode.com",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/siliconstencil/hordecode.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/siliconstencil/hordecode/issues"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ai",
|
|
23
|
+
"agent",
|
|
24
|
+
"cli",
|
|
25
|
+
"coding-assistant",
|
|
26
|
+
"opencode",
|
|
27
|
+
"hordecode"
|
|
28
|
+
],
|
|
29
|
+
"os": [
|
|
30
|
+
"darwin",
|
|
31
|
+
"linux",
|
|
32
|
+
"win32"
|
|
33
|
+
],
|
|
34
|
+
"cpu": [
|
|
35
|
+
"arm64",
|
|
36
|
+
"x64"
|
|
37
|
+
],
|
|
38
|
+
"optionalDependencies": {
|
|
39
|
+
"hordecode-linux-arm64": "0.1.1",
|
|
40
|
+
"hordecode-linux-x64": "0.1.1",
|
|
41
|
+
"hordecode-linux-x64-baseline": "0.1.1",
|
|
42
|
+
"hordecode-linux-arm64-musl": "0.1.1",
|
|
43
|
+
"hordecode-linux-x64-musl": "0.1.1",
|
|
44
|
+
"hordecode-linux-x64-baseline-musl": "0.1.1",
|
|
45
|
+
"hordecode-darwin-arm64": "0.1.1",
|
|
46
|
+
"hordecode-darwin-x64": "0.1.1",
|
|
47
|
+
"hordecode-darwin-x64-baseline": "0.1.1",
|
|
48
|
+
"hordecode-windows-arm64": "0.1.1",
|
|
49
|
+
"hordecode-windows-x64": "0.1.1",
|
|
50
|
+
"hordecode-windows-x64-baseline": "0.1.1"
|
|
51
|
+
}
|
|
6
52
|
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import childProcess from "child_process"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
import os from "os"
|
|
6
|
+
import path from "path"
|
|
7
|
+
import { createRequire } from "module"
|
|
8
|
+
import { fileURLToPath } from "url"
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
+
const require = createRequire(import.meta.url)
|
|
12
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"))
|
|
13
|
+
|
|
14
|
+
const platformMap = {
|
|
15
|
+
darwin: "darwin",
|
|
16
|
+
linux: "linux",
|
|
17
|
+
win32: "windows",
|
|
18
|
+
}
|
|
19
|
+
const archMap = {
|
|
20
|
+
x64: "x64",
|
|
21
|
+
arm64: "arm64",
|
|
22
|
+
arm: "arm",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const platform = platformMap[os.platform()] ?? os.platform()
|
|
26
|
+
const arch = archMap[os.arch()] ?? os.arch()
|
|
27
|
+
const base = `hordecode-${platform}-${arch}`
|
|
28
|
+
const sourceBinary = platform === "windows" ? "hordecode.exe" : "hordecode"
|
|
29
|
+
const legacySourceBinary = platform === "windows" ? "opencode.exe" : "opencode"
|
|
30
|
+
// The published package.json maps both commands to bin/<name>.exe on every platform
|
|
31
|
+
// (upstream postinstall.mjs:29 does the same), so the npm bin links resolve to the real binary.
|
|
32
|
+
const targetBinary = path.join(__dirname, "bin", "horde.exe")
|
|
33
|
+
const hordecodeTargetBinary = path.join(__dirname, "bin", "hordecode.exe")
|
|
34
|
+
|
|
35
|
+
function supportsAvx2() {
|
|
36
|
+
if (arch !== "x64") return false
|
|
37
|
+
|
|
38
|
+
if (platform === "linux") {
|
|
39
|
+
try {
|
|
40
|
+
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
41
|
+
} catch {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (platform === "darwin") {
|
|
47
|
+
try {
|
|
48
|
+
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
timeout: 1500,
|
|
51
|
+
})
|
|
52
|
+
if (result.status !== 0) return false
|
|
53
|
+
return (result.stdout || "").trim() === "1"
|
|
54
|
+
} catch {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (platform === "windows") {
|
|
60
|
+
const command =
|
|
61
|
+
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
62
|
+
|
|
63
|
+
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
64
|
+
try {
|
|
65
|
+
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
timeout: 3000,
|
|
68
|
+
windowsHide: true,
|
|
69
|
+
})
|
|
70
|
+
if (result.status !== 0) continue
|
|
71
|
+
const output = (result.stdout || "").trim().toLowerCase()
|
|
72
|
+
if (output === "true" || output === "1") return true
|
|
73
|
+
if (output === "false" || output === "0") return false
|
|
74
|
+
} catch {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isMusl() {
|
|
84
|
+
if (platform !== "linux") return false
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
if (fs.existsSync("/etc/alpine-release")) return true
|
|
88
|
+
} catch {
|
|
89
|
+
// Ignore filesystem probes that are blocked by the host.
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
94
|
+
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
|
|
95
|
+
} catch {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function packageNames() {
|
|
101
|
+
const baseline = arch === "x64" && !supportsAvx2()
|
|
102
|
+
|
|
103
|
+
if (platform === "linux") {
|
|
104
|
+
if (isMusl()) {
|
|
105
|
+
if (arch === "x64")
|
|
106
|
+
return baseline
|
|
107
|
+
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
108
|
+
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
109
|
+
return [`${base}-musl`, base]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (arch === "x64")
|
|
113
|
+
return baseline
|
|
114
|
+
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
115
|
+
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
116
|
+
return [base, `${base}-musl`]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (arch === "x64") return baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]
|
|
120
|
+
return [base]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveBinary(name) {
|
|
124
|
+
const packageJsonPath = require.resolve(`${name}/package.json`)
|
|
125
|
+
let binaryPath = path.join(path.dirname(packageJsonPath), "bin", sourceBinary)
|
|
126
|
+
if (fs.existsSync(binaryPath)) return binaryPath
|
|
127
|
+
binaryPath = path.join(path.dirname(packageJsonPath), "bin", legacySourceBinary)
|
|
128
|
+
if (fs.existsSync(binaryPath)) return binaryPath
|
|
129
|
+
throw new Error(`Binary not found at ${binaryPath}`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function installPackage(name) {
|
|
133
|
+
const version = packageJson.optionalDependencies?.[name]
|
|
134
|
+
if (!version) return
|
|
135
|
+
|
|
136
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "hordecode-install-"))
|
|
137
|
+
try {
|
|
138
|
+
const result = childProcess.spawnSync(
|
|
139
|
+
"npm",
|
|
140
|
+
["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", temp, `${name}@${version}`],
|
|
141
|
+
{ stdio: "inherit", windowsHide: true },
|
|
142
|
+
)
|
|
143
|
+
if (result.status !== 0) return
|
|
144
|
+
const packageDir = path.join(temp, "node_modules", name)
|
|
145
|
+
copyBinary(path.join(packageDir, "bin", sourceBinary), targetBinary)
|
|
146
|
+
copyRuntime(path.join(packageDir, "bin", sourceBinary), targetBinary)
|
|
147
|
+
return true
|
|
148
|
+
} finally {
|
|
149
|
+
fs.rmSync(temp, { recursive: true, force: true })
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// The memory embedding index loads its onnxruntime binding and wasm assets from
|
|
154
|
+
// <exe-dir>/runtime and <exe-dir>/vendor (packages/memory/src/embedding/service.ts). The
|
|
155
|
+
// platform package ships them next to its binary; carry them next to the installed one.
|
|
156
|
+
function copyRuntime(source, target) {
|
|
157
|
+
const from = path.dirname(source)
|
|
158
|
+
const to = path.dirname(target)
|
|
159
|
+
for (const extra of ["runtime", "vendor"]) {
|
|
160
|
+
const dir = path.join(from, extra)
|
|
161
|
+
if (!fs.existsSync(dir)) continue
|
|
162
|
+
fs.rmSync(path.join(to, extra), { recursive: true, force: true })
|
|
163
|
+
fs.cpSync(dir, path.join(to, extra), { recursive: true })
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function copyBinary(source, target) {
|
|
168
|
+
if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
|
|
169
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
170
|
+
if (fs.existsSync(target)) fs.unlinkSync(target)
|
|
171
|
+
try {
|
|
172
|
+
fs.linkSync(source, target)
|
|
173
|
+
} catch {
|
|
174
|
+
fs.copyFileSync(source, target)
|
|
175
|
+
}
|
|
176
|
+
fs.chmodSync(target, 0o755)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function verifyBinary() {
|
|
180
|
+
const result = childProcess.spawnSync(targetBinary, ["--version"], {
|
|
181
|
+
encoding: "utf8",
|
|
182
|
+
stdio: "ignore",
|
|
183
|
+
windowsHide: true,
|
|
184
|
+
})
|
|
185
|
+
return result.status === 0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function main() {
|
|
189
|
+
for (const name of packageNames()) {
|
|
190
|
+
try {
|
|
191
|
+
const src = resolveBinary(name)
|
|
192
|
+
copyBinary(src, targetBinary)
|
|
193
|
+
copyRuntime(src, targetBinary)
|
|
194
|
+
try {
|
|
195
|
+
copyBinary(src, hordecodeTargetBinary)
|
|
196
|
+
} catch {}
|
|
197
|
+
if (verifyBinary()) return
|
|
198
|
+
} catch {
|
|
199
|
+
if (installPackage(name) && verifyBinary()) {
|
|
200
|
+
try {
|
|
201
|
+
const src2 = resolveBinary(name)
|
|
202
|
+
copyBinary(src2, hordecodeTargetBinary)
|
|
203
|
+
} catch {}
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
throw new Error(
|
|
210
|
+
`It seems your package manager failed to install the right hordecode CLI package. Try manually installing ${packageNames()
|
|
211
|
+
.map((name) => JSON.stringify(name))
|
|
212
|
+
.join(" or ")}.`,
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
main()
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error(error.message)
|
|
220
|
+
process.exit(1)
|
|
221
|
+
}
|