@bash0816/copilot-termux 1.0.65 → 1.0.68-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/lib/setup.js CHANGED
@@ -91,13 +91,16 @@ async function setup() {
91
91
  }
92
92
  }
93
93
 
94
+ await setupGlibcWrap();
95
+ await setupGlibcRuntime(versionDir, version);
96
+ await setupGlibcNode();
97
+
94
98
  const currentLink = path.join(CACHE_DIR, 'current');
95
99
  const tmp = path.join(CACHE_DIR, `current.tmp.${process.pid}`);
96
100
  try { fs.unlinkSync(tmp); } catch (_) {}
97
101
  fs.symlinkSync(versionDir, tmp);
98
102
  fs.renameSync(tmp, currentLink);
99
103
 
100
- await setupGlibcWrap();
101
104
  console.log(`✓ copilot ${version} ready`);
102
105
  }
103
106
 
@@ -140,4 +143,141 @@ async function setupGlibcWrap() {
140
143
  console.log('✓ glibc wrap for lxc-exec ready');
141
144
  }
142
145
 
146
+ async function setupGlibcRuntime(versionDir, version) {
147
+ const linux64Dir = path.join(versionDir, 'prebuilds', 'linux-arm64');
148
+ const runtimeNode = path.join(linux64Dir, 'runtime.node');
149
+ const cliNativeNode = path.join(linux64Dir, 'cli-native.node');
150
+ if (fs.existsSync(runtimeNode) && fs.existsSync(cliNativeNode)) {
151
+ return;
152
+ }
153
+
154
+ const glibcPkg = '@github/copilot-linux-arm64';
155
+ console.log(`Fetching ${glibcPkg}@${version} metadata...`);
156
+ const metaBuf = await httpsGet(`${REGISTRY}/${glibcPkg}/${version}`, { Accept: 'application/json' });
157
+ const meta = JSON.parse(metaBuf.toString());
158
+ const tarballUrl = meta.dist.tarball;
159
+ const integrity = meta.dist.integrity;
160
+
161
+ console.log(`Downloading ${glibcPkg}@${version} (glibc runtime)...`);
162
+ const tarball = await fetchWithIntegrity(tarballUrl, integrity);
163
+
164
+ const tarballPath = path.join(CACHE_DIR, `copilot-glibc-${version}.tgz`);
165
+ try {
166
+ fs.writeFileSync(tarballPath, tarball);
167
+ fs.mkdirSync(linux64Dir, { recursive: true });
168
+
169
+ console.log('Extracting glibc runtime.node...');
170
+ execFileSync('tar', [
171
+ '-xzf', tarballPath,
172
+ '-C', path.join(versionDir, 'prebuilds'),
173
+ '--strip-components=2',
174
+ 'package/prebuilds/linux-arm64/runtime.node',
175
+ 'package/prebuilds/linux-arm64/cli-native.node',
176
+ ]);
177
+ } finally {
178
+ fs.rmSync(tarballPath, { force: true });
179
+ }
180
+ console.log('✓ glibc runtime.node ready');
181
+ }
182
+
183
+ async function setupGlibcNode() {
184
+ const { version, sha256: expectedSha256 } = manifest.glibcNode;
185
+ const nodeVersionDir = path.join(CACHE_DIR, `glibc-node-${version}`);
186
+ const nodeBin = path.join(nodeVersionDir, 'node');
187
+ const currentLink = path.join(CACHE_DIR, 'glibc-node');
188
+
189
+ const PREFIX = process.env.PREFIX || '/data/data/com.termux/files/usr';
190
+ const glibcLd = path.join(PREFIX, 'glibc', 'lib', 'ld-linux-aarch64.so.1');
191
+ const glibcLibs = path.join(PREFIX, 'glibc', 'lib');
192
+
193
+ // glibc動的リンカ経由でないとTermux(bionic)上ではglibc Nodeバイナリを実行できないため、
194
+ // ld-linux 経由で `node -v` を実行しバージョン文字列を確認する。
195
+ function verifyNodeBinary(binPath) {
196
+ // bin/copilot と同様、Termux由来の LD_PRELOAD (bionic向けexecフック) を明示的にクリアしないと
197
+ // glibc loaderがロードするlibc.soがinvalid ELF headerエラーになる(2026-07-06実機確認)。
198
+ const out = execFileSync(glibcLd, ['--library-path', glibcLibs, binPath, '-v'], {
199
+ encoding: 'utf8',
200
+ env: { ...process.env, LD_PRELOAD: '' },
201
+ }).trim();
202
+ return out === `v${version}`;
203
+ }
204
+
205
+ let needsDownload = true;
206
+ if (fs.existsSync(nodeBin)) {
207
+ try {
208
+ if (verifyNodeBinary(nodeBin)) {
209
+ needsDownload = false;
210
+ } else {
211
+ console.log(`glibc node at ${nodeVersionDir} failed version check, re-downloading...`);
212
+ fs.rmSync(nodeVersionDir, { recursive: true, force: true });
213
+ }
214
+ } catch (e) {
215
+ // 破損バイナリ・実行不可などpartial stateからの自己修復
216
+ console.log(`glibc node at ${nodeVersionDir} failed to execute (${e.message}), re-downloading...`);
217
+ fs.rmSync(nodeVersionDir, { recursive: true, force: true });
218
+ }
219
+ }
220
+
221
+ if (needsDownload) {
222
+ const tarballUrl = `https://nodejs.org/dist/v${version}/node-v${version}-linux-arm64.tar.gz`;
223
+ console.log(`Downloading Node.js v${version} (glibc)...`);
224
+ const tarball = await httpsGet(tarballUrl);
225
+
226
+ const actualSha256 = crypto.createHash('sha256').update(tarball).digest('hex');
227
+ if (actualSha256 !== expectedSha256) {
228
+ throw new Error(`Node.js integrity check failed: expected ${expectedSha256}, got ${actualSha256}`);
229
+ }
230
+
231
+ const stagingDir = `${nodeVersionDir}.staging.${process.pid}`;
232
+ const tarballPath = path.join(CACHE_DIR, `node-glibc-${version}.tgz`);
233
+ try {
234
+ fs.rmSync(stagingDir, { recursive: true, force: true });
235
+ fs.writeFileSync(tarballPath, tarball);
236
+ fs.mkdirSync(stagingDir, { recursive: true });
237
+
238
+ console.log('Extracting node binary...');
239
+ execFileSync('tar', [
240
+ '-xzf', tarballPath,
241
+ '-C', stagingDir,
242
+ '--strip-components=2',
243
+ `node-v${version}-linux-arm64/bin/node`,
244
+ ]);
245
+ const stagingBin = path.join(stagingDir, 'node');
246
+ fs.chmodSync(stagingBin, 0o755);
247
+
248
+ // staging側で実行検証(glibcローダー経由)。失敗すれば例外がそのまま
249
+ // 呼び出し元に伝播し、stagingはcatchで削除、nodeVersionDirには一切触れない。
250
+ if (!verifyNodeBinary(stagingBin)) {
251
+ throw new Error(`Downloaded glibc node binary failed version check (expected v${version})`);
252
+ }
253
+
254
+ // 既存の nodeVersionDir が壊れた状態で残っている場合に備え、rename前にクリア
255
+ // (ここまで到達した時点で staging 側の検証は完了済みなので安全)
256
+ fs.rmSync(nodeVersionDir, { recursive: true, force: true });
257
+ fs.renameSync(stagingDir, nodeVersionDir);
258
+ } catch (err) {
259
+ fs.rmSync(stagingDir, { recursive: true, force: true });
260
+ throw err;
261
+ } finally {
262
+ fs.rmSync(tarballPath, { force: true });
263
+ }
264
+ }
265
+
266
+ // Migration: 1.0.68以前の glibc-node は実ディレクトリだった。
267
+ // 新バージョンの用意(既存流用 or 新規ダウンロード、どちらもverifyNodeBinary通過済み)が
268
+ // 完全に成功した後(ここまで到達した時点)でのみ、旧実ディレクトリを削除する。
269
+ let currentLinkStat = null;
270
+ try { currentLinkStat = fs.lstatSync(currentLink); } catch (_) {}
271
+ if (currentLinkStat && currentLinkStat.isDirectory() && !currentLinkStat.isSymbolicLink()) {
272
+ fs.rmSync(currentLink, { recursive: true, force: true });
273
+ }
274
+
275
+ const tmpLink = path.join(CACHE_DIR, `glibc-node.tmp.${process.pid}`);
276
+ try { fs.unlinkSync(tmpLink); } catch (_) {}
277
+ fs.symlinkSync(nodeVersionDir, tmpLink);
278
+ fs.renameSync(tmpLink, currentLink);
279
+
280
+ console.log('✓ glibc node ready');
281
+ }
282
+
143
283
  module.exports = { setup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bash0816/copilot-termux",
3
- "version": "1.0.65",
3
+ "version": "1.0.68-1",
4
4
  "description": "GitHub Copilot CLI for Termux (Android aarch64)",
5
5
  "license": "GPL-3.0-only",
6
6
  "readme": "README.md",
@@ -25,6 +25,7 @@
25
25
  "bin",
26
26
  "lib/platform-patch.js",
27
27
  "lib/setup.js",
28
+ "lib/check-updates.js",
28
29
  "lib/bionic-compat.so",
29
30
  "lib/native/pty.node",
30
31
  "scripts/bionic-compat.c",