@bash0816/copilot-termux 1.0.63
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 +683 -0
- package/README.md +76 -0
- package/THIRD-PARTY-LICENSES/COPILOT-LICENSE.md +35 -0
- package/THIRD-PARTY-LICENSES/GCC-RUNTIME-EXCEPTION.txt +73 -0
- package/THIRD-PARTY-LICENSES/GPL-3.0.txt +674 -0
- package/THIRD-PARTY-LICENSES/LGPL-2.1.txt +501 -0
- package/THIRD-PARTY-LICENSES/NODE-LICENSE.txt +2946 -0
- package/THIRD-PARTY-LICENSES/NODE-PTY-LICENSE.txt +69 -0
- package/THIRD-PARTY-NOTICES.md +66 -0
- package/bin/copilot +70 -0
- package/bin/copilot-termux +9 -0
- package/config/manifest.json +7 -0
- package/lib/bionic-compat.so +0 -0
- package/lib/native/pty.node +0 -0
- package/lib/platform-patch.js +451 -0
- package/lib/setup.js +143 -0
- package/package.json +24 -0
- package/scripts/bionic-compat.c +82 -0
package/lib/setup.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const https = require('https');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
8
|
+
const manifest = require('../config/manifest.json');
|
|
9
|
+
|
|
10
|
+
const REGISTRY = 'https://registry.npmjs.org';
|
|
11
|
+
const CACHE_DIR = path.join(os.homedir(), '.copilot-termux');
|
|
12
|
+
|
|
13
|
+
function httpsGet(url, headers) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const parsedUrl = new URL(url);
|
|
16
|
+
const opts = {
|
|
17
|
+
hostname: parsedUrl.hostname,
|
|
18
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
19
|
+
headers: headers || {},
|
|
20
|
+
};
|
|
21
|
+
https.get(opts, res => {
|
|
22
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
23
|
+
resolve(httpsGet(res.headers.location, headers));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (res.statusCode !== 200) {
|
|
27
|
+
reject(new Error(`HTTP ${res.statusCode} from ${url}`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const chunks = [];
|
|
31
|
+
res.on('data', c => chunks.push(c));
|
|
32
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
33
|
+
res.on('error', reject);
|
|
34
|
+
}).on('error', reject);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function fetchWithIntegrity(url, expectedIntegrity) {
|
|
39
|
+
const buf = await httpsGet(url);
|
|
40
|
+
const [algo, expected] = expectedIntegrity.split('-');
|
|
41
|
+
const actual = crypto.createHash(algo).update(buf).digest('base64');
|
|
42
|
+
const expectedBuf = Buffer.from(expected, 'base64');
|
|
43
|
+
const actualBuf = Buffer.from(actual, 'base64');
|
|
44
|
+
if (expectedBuf.length !== actualBuf.length || !crypto.timingSafeEqual(expectedBuf, actualBuf)) {
|
|
45
|
+
throw new Error(`integrity check failed: expected ${expectedIntegrity}, got ${algo}-${actual}`);
|
|
46
|
+
}
|
|
47
|
+
return buf;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function setup() {
|
|
51
|
+
const { version, integrity } = manifest.copilot;
|
|
52
|
+
const versionDir = path.join(CACHE_DIR, version);
|
|
53
|
+
const stagingDir = `${versionDir}.staging`;
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(path.join(versionDir, 'index.js'))) {
|
|
56
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
57
|
+
console.log(`@github/copilot@${version} already installed, refreshing symlink...`);
|
|
58
|
+
} else {
|
|
59
|
+
console.log(`Fetching @github/copilot@${version} metadata...`);
|
|
60
|
+
const metaBuf = await httpsGet(`${REGISTRY}/@github/copilot/${version}`, { Accept: 'application/json' });
|
|
61
|
+
const meta = JSON.parse(metaBuf.toString());
|
|
62
|
+
const tarballUrl = meta.dist.tarball;
|
|
63
|
+
|
|
64
|
+
console.log(`Downloading @github/copilot@${version}...`);
|
|
65
|
+
const tarball = await fetchWithIntegrity(tarballUrl, integrity);
|
|
66
|
+
|
|
67
|
+
const tarballPath = path.join(CACHE_DIR, `copilot-${version}.tgz`);
|
|
68
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
72
|
+
fs.writeFileSync(tarballPath, tarball);
|
|
73
|
+
|
|
74
|
+
console.log('Extracting...');
|
|
75
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
76
|
+
execFileSync('tar', ['-xzf', tarballPath, '-C', stagingDir, '--strip-components=1']);
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(path.join(stagingDir, 'index.js'))) {
|
|
79
|
+
throw new Error(`installation incomplete: missing ${path.join(stagingDir, 'index.js')}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (fs.existsSync(versionDir)) {
|
|
83
|
+
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
fs.renameSync(stagingDir, versionDir);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
88
|
+
throw err;
|
|
89
|
+
} finally {
|
|
90
|
+
fs.rmSync(tarballPath, { force: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const currentLink = path.join(CACHE_DIR, 'current');
|
|
95
|
+
const tmp = path.join(CACHE_DIR, `current.tmp.${process.pid}`);
|
|
96
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
97
|
+
fs.symlinkSync(versionDir, tmp);
|
|
98
|
+
fs.renameSync(tmp, currentLink);
|
|
99
|
+
|
|
100
|
+
await setupGlibcWrap();
|
|
101
|
+
console.log(`✓ copilot ${version} ready`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function setupGlibcWrap() {
|
|
105
|
+
const PREFIX = process.env.PREFIX || '/data/data/com.termux/files/usr';
|
|
106
|
+
const glibcDir = path.join(PREFIX, 'glibc', 'lib');
|
|
107
|
+
const LD = path.join(glibcDir, 'ld-linux-aarch64.so.1');
|
|
108
|
+
if (!fs.existsSync(LD)) {
|
|
109
|
+
throw new Error(`[copilot-termux] glibc not found at ${glibcDir}. Please install glibc-repo: pkg install glibc-repo && pkg install glibc`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const wrapLibsDir = path.join(CACHE_DIR, 'glibc-wrap-libs');
|
|
113
|
+
const mxcWrapDir = path.join(CACHE_DIR, 'mxc-wrap', 'arm64');
|
|
114
|
+
|
|
115
|
+
fs.rmSync(wrapLibsDir, { recursive: true, force: true });
|
|
116
|
+
fs.mkdirSync(wrapLibsDir, { recursive: true });
|
|
117
|
+
fs.mkdirSync(mxcWrapDir, { recursive: true });
|
|
118
|
+
|
|
119
|
+
for (const lib of ['ld-linux-aarch64.so.1', 'libc.so.6', 'libgcc_s.so.1']) {
|
|
120
|
+
const src = path.join(glibcDir, lib);
|
|
121
|
+
if (!fs.existsSync(src)) throw new Error(`[copilot-termux] Missing glibc lib: ${src}`);
|
|
122
|
+
fs.copyFileSync(src, path.join(wrapLibsDir, lib));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const libcSo = path.join(wrapLibsDir, 'libc.so');
|
|
126
|
+
try { fs.unlinkSync(libcSo); } catch (_) {}
|
|
127
|
+
fs.symlinkSync('libc.so.6', libcSo);
|
|
128
|
+
|
|
129
|
+
const wrapperPath = path.join(mxcWrapDir, 'lxc-exec');
|
|
130
|
+
const wrapperContent = [
|
|
131
|
+
'#!/bin/sh',
|
|
132
|
+
'_CACHE="${HOME}/.copilot-termux"',
|
|
133
|
+
'_LIBS="${_CACHE}/glibc-wrap-libs"',
|
|
134
|
+
'_LD="${_LIBS}/ld-linux-aarch64.so.1"',
|
|
135
|
+
'_LXC="${_CACHE}/current/mxc-bin/arm64/lxc-exec"',
|
|
136
|
+
'exec env -u LD_PRELOAD "$_LD" --library-path "$_LIBS" "$_LXC" "$@"',
|
|
137
|
+
].join('\n') + '\n';
|
|
138
|
+
fs.writeFileSync(wrapperPath, wrapperContent, { mode: 0o755 });
|
|
139
|
+
|
|
140
|
+
console.log('✓ glibc wrap for lxc-exec ready');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { setup };
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bash0816/copilot-termux",
|
|
3
|
+
"version": "1.0.63",
|
|
4
|
+
"description": "GitHub Copilot CLI for Termux (Android aarch64)",
|
|
5
|
+
"license": "GPL-3.0-only",
|
|
6
|
+
"readme": "README.md",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/bash0816/Github-Copilot-Termux.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/bash0816/Github-Copilot-Termux#readme",
|
|
12
|
+
"keywords": ["termux", "android", "github-copilot", "copilot-cli", "aarch64"],
|
|
13
|
+
"bin": {
|
|
14
|
+
"copilot": "bin/copilot",
|
|
15
|
+
"copilot-termux": "bin/copilot-termux"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {},
|
|
18
|
+
"files": ["bin", "lib/platform-patch.js", "lib/setup.js", "lib/bionic-compat.so", "lib/native/pty.node", "scripts/bionic-compat.c", "config", "LICENSE", "THIRD-PARTY-NOTICES.md", "THIRD-PARTY-LICENSES/", "README.md"],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"os": ["android", "linux"],
|
|
23
|
+
"cpu": ["arm64"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* bionic-compat.so: provides glibc/musl symbols missing from Android bionic,
|
|
3
|
+
* so that linuxmusl-arm64/runtime.node can be dlopen'd on Termux.
|
|
4
|
+
* Load as LD_PRELOAD before Node.js.
|
|
5
|
+
*/
|
|
6
|
+
#define _GNU_SOURCE
|
|
7
|
+
#include <string.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <errno.h>
|
|
10
|
+
#include <spawn.h>
|
|
11
|
+
#include <stddef.h>
|
|
12
|
+
#include <unistd.h>
|
|
13
|
+
#include <dlfcn.h>
|
|
14
|
+
#include <math.h>
|
|
15
|
+
|
|
16
|
+
/* bionic exports __errno() instead of __errno_location() */
|
|
17
|
+
extern int *__errno(void);
|
|
18
|
+
int *__errno_location(void) { return __errno(); }
|
|
19
|
+
|
|
20
|
+
int bcmp(const void *s1, const void *s2, size_t n) { return memcmp(s1, s2, n); }
|
|
21
|
+
|
|
22
|
+
/* jemalloc sized dealloc - bionic uses scudo, delegate to free */
|
|
23
|
+
void sdallocx(void *ptr, size_t size, int flags) {
|
|
24
|
+
(void)size; (void)flags;
|
|
25
|
+
free(ptr);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* XSI strerror_r (int-returning); bionic's strerror_r returns char* */
|
|
29
|
+
int __xpg_strerror_r(int errnum, char *buf, size_t buflen) {
|
|
30
|
+
const char *s = strerror(errnum);
|
|
31
|
+
if (!s) { *__errno() = EINVAL; return EINVAL; }
|
|
32
|
+
size_t len = strlen(s);
|
|
33
|
+
if (len >= buflen) { *__errno() = ERANGE; return ERANGE; }
|
|
34
|
+
memcpy(buf, s, len + 1);
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* pidfd APIs absent from this Android kernel version */
|
|
39
|
+
int pidfd_getpid(int fd) { (void)fd; *__errno() = ENOSYS; return -1; }
|
|
40
|
+
|
|
41
|
+
int pidfd_spawnp(int *pidfd,
|
|
42
|
+
const char *path,
|
|
43
|
+
const posix_spawn_file_actions_t *fa,
|
|
44
|
+
const posix_spawnattr_t *attr,
|
|
45
|
+
char *const argv[],
|
|
46
|
+
char *const envp[]) {
|
|
47
|
+
(void)pidfd; (void)path; (void)fa; (void)attr; (void)argv; (void)envp;
|
|
48
|
+
*__errno() = ENOSYS;
|
|
49
|
+
return -1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* musl bundles math in libc.so; bionic splits to libm.so.
|
|
53
|
+
* Since we are LD_PRELOAD, RTLD_NEXT finds the real libm.so symbols. */
|
|
54
|
+
typedef double (*fn_dd)(double);
|
|
55
|
+
typedef double (*fn_ddd)(double, double);
|
|
56
|
+
typedef float (*fn_ff)(float);
|
|
57
|
+
|
|
58
|
+
static fn_ddd _pow;
|
|
59
|
+
static fn_dd _log;
|
|
60
|
+
static fn_dd _log2;
|
|
61
|
+
static fn_ff _expf;
|
|
62
|
+
static fn_ff _log10f;
|
|
63
|
+
static fn_ff _sinf;
|
|
64
|
+
|
|
65
|
+
__attribute__((constructor))
|
|
66
|
+
static void compat_init(void) {
|
|
67
|
+
_pow = (fn_ddd)dlsym(RTLD_NEXT, "pow");
|
|
68
|
+
_log = (fn_dd) dlsym(RTLD_NEXT, "log");
|
|
69
|
+
_log2 = (fn_dd) dlsym(RTLD_NEXT, "log2");
|
|
70
|
+
_expf = (fn_ff) dlsym(RTLD_NEXT, "expf");
|
|
71
|
+
_log10f = (fn_ff) dlsym(RTLD_NEXT, "log10f");
|
|
72
|
+
_sinf = (fn_ff) dlsym(RTLD_NEXT, "sinf");
|
|
73
|
+
/* NULL is expected in non-node processes (shells) that inherit LD_PRELOAD
|
|
74
|
+
* but do not link libm.so. Those processes never invoke math stubs. */
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
double pow(double x, double y) { if (!_pow) abort(); return _pow(x, y); }
|
|
78
|
+
double log(double x) { if (!_log) abort(); return _log(x); }
|
|
79
|
+
double log2(double x) { if (!_log2) abort(); return _log2(x); }
|
|
80
|
+
float expf(float x) { if (!_expf) abort(); return _expf(x); }
|
|
81
|
+
float log10f(float x) { if (!_log10f) abort(); return _log10f(x); }
|
|
82
|
+
float sinf(float x) { if (!_sinf) abort(); return _sinf(x); }
|