goatchain-cli 0.0.73-beta.4 → 0.0.73-beta.5

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.
Files changed (2) hide show
  1. package/bin/dim.mjs +133 -3
  2. package/package.json +6 -6
package/bin/dim.mjs CHANGED
@@ -38,9 +38,12 @@ const wrapperPm
38
38
 
39
39
  const args = process.argv.slice(2)
40
40
  const forwardArgs = args[0] === 'opentui' ? args.slice(1) : args
41
- runOpentui(forwardArgs)
41
+ runOpentui(forwardArgs).catch((err) => {
42
+ console.error('[dim] Unexpected error: ' + (err && err.stack ? err.stack : err))
43
+ process.exit(1)
44
+ })
42
45
 
43
- function runOpentui(restArgs) {
46
+ async function runOpentui(restArgs) {
44
47
  const envPath = process.env.DIMCODE_BIN_PATH
45
48
  if (envPath) {
46
49
  if (!fs.existsSync(envPath)) {
@@ -70,7 +73,12 @@ function runOpentui(restArgs) {
70
73
  console.error('[dim] Script dir: ' + scriptDir)
71
74
  }
72
75
 
73
- const resolved = findBinary(scriptDir, base, binary)
76
+ let resolved = findBinary(scriptDir, base, binary)
77
+
78
+ if (!resolved) {
79
+ resolved = await ensureCachedBinary(base, binary, wrapperVersion)
80
+ }
81
+
74
82
  if (!resolved) {
75
83
  console.error(
76
84
  '[dim] Could not find the opentui binary for your platform.\n'
@@ -92,6 +100,128 @@ function runOpentui(restArgs) {
92
100
  spawnBinary(resolved, restArgs)
93
101
  }
94
102
 
103
+ async function ensureCachedBinary(base, binary, version) {
104
+ if (!version) {
105
+ if (process.env.DIMCODE_DEBUG)
106
+ console.error('[dim] No wrapper version available; skipping registry fallback')
107
+ return null
108
+ }
109
+ if (process.env.DIMCODE_NO_DOWNLOAD === '1') {
110
+ if (process.env.DIMCODE_DEBUG)
111
+ console.error('[dim] DIMCODE_NO_DOWNLOAD=1; skipping registry fallback')
112
+ return null
113
+ }
114
+
115
+ const home = os.homedir()
116
+ const cacheDir = path.join(home, '.dimcode', 'binaries', base, version)
117
+ const cachedBin = path.join(cacheDir, 'bin', binary)
118
+ if (fs.existsSync(cachedBin)) {
119
+ if (process.env.DIMCODE_DEBUG)
120
+ console.error('[dim] Using cached binary: ' + cachedBin)
121
+ return cachedBin
122
+ }
123
+
124
+ if (typeof fetch !== 'function') {
125
+ console.error('[dim] Registry fallback requires Node 18+ (global fetch). Please reinstall after upgrading Node.')
126
+ return null
127
+ }
128
+
129
+ const registry = (process.env.DIMCODE_REGISTRY || 'https://registry.npmjs.org').replace(/\/+$/, '')
130
+ const url = registry + '/' + base + '/-/' + base + '-' + version + '.tgz'
131
+
132
+ console.error('[dim] Platform binary missing locally. Fetching ' + base + '@' + version + '...')
133
+ if (process.env.DIMCODE_DEBUG)
134
+ console.error('[dim] URL: ' + url)
135
+
136
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dimcode-dl-'))
137
+ const tgzPath = path.join(tmpDir, 'pkg.tgz')
138
+
139
+ try {
140
+ await downloadFile(url, tgzPath)
141
+ fs.mkdirSync(cacheDir, { recursive: true })
142
+ extractTarball(tgzPath, cacheDir)
143
+ if (!isWindows && fs.existsSync(cachedBin)) {
144
+ try { fs.chmodSync(cachedBin, 0o755) }
145
+ catch {}
146
+ }
147
+ }
148
+ catch (err) {
149
+ console.error('[dim] Failed to fetch platform binary: ' + (err && err.message ? err.message : err))
150
+ console.error('[dim] You can retry, set DIMCODE_BIN_PATH to a local binary, or reinstall with:')
151
+ console.error(' npm uninstall -g ' + wrapperName + ' && npm install -g ' + wrapperName + '@beta')
152
+ return null
153
+ }
154
+ finally {
155
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }) }
156
+ catch {}
157
+ }
158
+
159
+ if (!fs.existsSync(cachedBin)) {
160
+ console.error('[dim] Downloaded archive did not contain expected binary at ' + cachedBin)
161
+ return null
162
+ }
163
+
164
+ console.error('[dim] Cached at ' + cachedBin)
165
+ return cachedBin
166
+ }
167
+
168
+ async function downloadFile(url, dest) {
169
+ let currentUrl = url
170
+ for (let hop = 0; hop < 5; hop++) {
171
+ const res = await fetch(currentUrl, { redirect: 'manual' })
172
+ if (res.status >= 300 && res.status < 400 && res.headers.get('location')) {
173
+ currentUrl = new URL(res.headers.get('location'), currentUrl).toString()
174
+ continue
175
+ }
176
+ if (!res.ok)
177
+ throw new Error('HTTP ' + res.status + ' ' + res.statusText + ' for ' + currentUrl)
178
+
179
+ const total = Number(res.headers.get('content-length') || 0)
180
+ const out = fs.createWriteStream(dest)
181
+ let downloaded = 0
182
+ let lastShown = -1
183
+ const reader = res.body.getReader()
184
+ for (;;) {
185
+ const { done, value } = await reader.read()
186
+ if (done) break
187
+ out.write(Buffer.from(value))
188
+ downloaded += value.length
189
+ if (total && process.stderr.isTTY) {
190
+ const pct = Math.floor((downloaded / total) * 100)
191
+ if (pct !== lastShown && pct % 5 === 0) {
192
+ process.stderr.write('\r[dim] Downloading... ' + pct + '%')
193
+ lastShown = pct
194
+ }
195
+ }
196
+ }
197
+ out.end()
198
+ await new Promise((resolve, reject) => {
199
+ out.on('close', resolve)
200
+ out.on('error', reject)
201
+ })
202
+ if (process.stderr.isTTY)
203
+ process.stderr.write('\r[dim] Downloaded ' + (downloaded / 1024 / 1024).toFixed(1) + ' MB \n')
204
+ else
205
+ console.error('[dim] Downloaded ' + (downloaded / 1024 / 1024).toFixed(1) + ' MB')
206
+ return
207
+ }
208
+ throw new Error('Too many redirects')
209
+ }
210
+
211
+ function extractTarball(tgzPath, destDir) {
212
+ const result = childProcess.spawnSync(
213
+ 'tar',
214
+ ['-xzf', tgzPath, '-C', destDir, '--strip-components=1'],
215
+ { stdio: process.env.DIMCODE_DEBUG ? 'inherit' : 'pipe' },
216
+ )
217
+ if (result.error)
218
+ throw new Error('tar not available: ' + result.error.message)
219
+ if (result.status !== 0) {
220
+ const stderr = result.stderr ? result.stderr.toString() : ''
221
+ throw new Error('tar exited with status ' + result.status + (stderr ? ': ' + stderr.trim() : ''))
222
+ }
223
+ }
224
+
95
225
  function findBinary(startDir, base, binary) {
96
226
  let current = startDir
97
227
  for (;;) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goatchain-cli",
3
- "version": "0.0.73-beta.4",
3
+ "version": "0.0.73-beta.5",
4
4
  "description": "AI coding agent CLI and terminal coding assistant with an interactive TUI (beta channel)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,10 +11,10 @@
11
11
  "bin"
12
12
  ],
13
13
  "optionalDependencies": {
14
- "goatchain-cli-darwin-arm64": "0.0.73-beta.4",
15
- "goatchain-cli-darwin-x64": "0.0.73-beta.4",
16
- "goatchain-cli-linux-arm64": "0.0.73-beta.4",
17
- "goatchain-cli-linux-x64": "0.0.73-beta.4",
18
- "goatchain-cli-windows-x64": "0.0.73-beta.4"
14
+ "goatchain-cli-darwin-arm64": "0.0.73-beta.5",
15
+ "goatchain-cli-darwin-x64": "0.0.73-beta.5",
16
+ "goatchain-cli-linux-arm64": "0.0.73-beta.5",
17
+ "goatchain-cli-linux-x64": "0.0.73-beta.5",
18
+ "goatchain-cli-windows-x64": "0.0.73-beta.5"
19
19
  }
20
20
  }