@nemoobc/opencode-termux 1.20.1 → 1.20.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.
- package/README.md +266 -67
- package/agents/apk-builder.md +3 -2
- package/agents/autodev.md +422 -32
- package/agents/coder.md +171 -0
- package/agents/fixer.md +2 -1
- package/agents/orchestrator.md +165 -0
- package/agents/termux-coder.md +2 -1
- package/agents/tester.md +3 -2
- package/bin/opencode-termux.js +14 -11
- package/bin/opencode-termux.ts +179 -0
- package/commands/audit.md +6 -0
- package/commands/coder.md +24 -0
- package/commands/orchestrator.md +15 -0
- package/commands/test-all.md +6 -0
- package/config/opencode.json +1 -1
- package/config/plugins/README.md +39 -0
- package/config/plugins/strip-parameter.js +55 -0
- package/install.mjs +25 -1
- package/lib/alpine.ts +39 -0
- package/lib/integrity.ts +28 -0
- package/lib/net.mjs +6 -0
- package/lib/net.ts +51 -0
- package/package.json +13 -4
- package/prebuilt/README.md +77 -0
package/lib/integrity.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifikasi integritas file terhadap hash sha512 format registry npm
|
|
3
|
+
* ("sha512-<base64>"). Mencegah tarball korup/termanipulasi saat transit.
|
|
4
|
+
*/
|
|
5
|
+
import crypto from "crypto"
|
|
6
|
+
import fs from "fs"
|
|
7
|
+
|
|
8
|
+
export interface Packument {
|
|
9
|
+
versions?: Record<string, { dist?: { integrity?: string } }>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function expectedFromRegistry(packument: Packument, version: string): string | null {
|
|
13
|
+
const dist = packument?.versions?.[version]?.dist
|
|
14
|
+
return dist?.integrity?.startsWith("sha512-") ? dist.integrity.slice(7) : null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function verifySha512(filePath: string, expectedB64: string): true {
|
|
18
|
+
if (!expectedB64) throw new Error("tidak ada hash acuan (integrity) dari registry")
|
|
19
|
+
const h = crypto.createHash("sha512")
|
|
20
|
+
h.update(fs.readFileSync(filePath))
|
|
21
|
+
const got = h.digest("base64")
|
|
22
|
+
const a = Buffer.from(got)
|
|
23
|
+
const b = Buffer.from(expectedB64)
|
|
24
|
+
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
|
|
25
|
+
throw new Error(`integritas gagal: file ≠ sha512 registry (${filePath})`)
|
|
26
|
+
}
|
|
27
|
+
return true
|
|
28
|
+
}
|
package/lib/net.mjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Utilitas jaringan: fetch dengan retry + backoff eksponensial.
|
|
3
3
|
* fetchFn dapat disuntik untuk unit test.
|
|
4
4
|
*/
|
|
5
|
+
const RETRYABLE_ERRORS = new Set(["ENOTFOUND", "ECONNRESET", "ETIMEDOUT", "ENETUNREACH", "EAI_AGAIN"])
|
|
6
|
+
|
|
5
7
|
export async function fetchWithRetry(fetchFn, url, opts = {}, retries = 3, log = () => {}) {
|
|
6
8
|
let lastErr
|
|
7
9
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
@@ -13,6 +15,10 @@ export async function fetchWithRetry(fetchFn, url, opts = {}, retries = 3, log =
|
|
|
13
15
|
lastErr = new Error(`HTTP ${res.status} — ${url}`)
|
|
14
16
|
} catch (e) {
|
|
15
17
|
lastErr = e
|
|
18
|
+
const code = e?.cause?.code || e?.code
|
|
19
|
+
if (code && !RETRYABLE_ERRORS.has(code)) {
|
|
20
|
+
throw e
|
|
21
|
+
}
|
|
16
22
|
}
|
|
17
23
|
if (attempt < retries) {
|
|
18
24
|
const wait = Math.min(1000 * 2 ** (attempt - 1), 8000)
|
package/lib/net.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilitas jaringan: fetch dengan retry + backoff eksponensial.
|
|
3
|
+
* fetchFn dapat disuntik untuk unit test.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface ResponseLike {
|
|
7
|
+
ok: boolean
|
|
8
|
+
status: number
|
|
9
|
+
body: ReadableStream<Uint8Array> | null
|
|
10
|
+
text(): Promise<string>
|
|
11
|
+
json<T = unknown>(): Promise<T>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ErrorWithCode extends Error {
|
|
15
|
+
code?: string
|
|
16
|
+
cause?: { code?: string }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const RETRYABLE_ERRORS = new Set(["ENOTFOUND", "ECONNRESET", "ETIMEDOUT", "ENETUNREACH", "EAI_AGAIN"])
|
|
20
|
+
|
|
21
|
+
export type FetchFn = (url: string, options?: RequestInit) => Promise<ResponseLike | Response>
|
|
22
|
+
|
|
23
|
+
export async function fetchWithRetry(
|
|
24
|
+
fetchFn: FetchFn,
|
|
25
|
+
url: string,
|
|
26
|
+
opts: RequestInit = {},
|
|
27
|
+
retries = 3,
|
|
28
|
+
log: (msg: string) => void = () => {}
|
|
29
|
+
): Promise<ResponseLike | Response> {
|
|
30
|
+
let lastErr: unknown
|
|
31
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
32
|
+
try {
|
|
33
|
+
const res = await fetchFn(url, opts)
|
|
34
|
+
if (!res.ok && res.status < 500 && res.status !== 429) return res
|
|
35
|
+
if (res.ok) return res
|
|
36
|
+
lastErr = new Error(`HTTP ${res.status} — ${url}`)
|
|
37
|
+
} catch (e) {
|
|
38
|
+
lastErr = e
|
|
39
|
+
const code = (e as ErrorWithCode).cause?.code || (e as ErrorWithCode).code
|
|
40
|
+
if (code && !RETRYABLE_ERRORS.has(code)) {
|
|
41
|
+
throw e
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (attempt < retries) {
|
|
45
|
+
const wait = Math.min(1000 * 2 ** (attempt - 1), 8000)
|
|
46
|
+
log(`gagal (${attempt}/${retries}) — ulang dalam ${wait}ms`)
|
|
47
|
+
await new Promise(r => setTimeout(r, wait))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw lastErr
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nemoobc/opencode-termux",
|
|
3
|
-
"version": "1.20.
|
|
4
|
-
"description": "opencode CLI native untuk Termux/Android tanpa proot
|
|
3
|
+
"version": "1.20.5",
|
|
4
|
+
"description": "opencode CLI native untuk Termux/Android tanpa proot \u2014 membundel loader musl + binary opencode resmi (upstream opencode-ai)",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
7
|
"opencode-termux": "./bin/opencode-termux.js"
|
|
7
8
|
},
|
|
8
9
|
"scripts": {
|
|
9
10
|
"postinstall": "node install.mjs",
|
|
10
11
|
"test": "node test/run.mjs",
|
|
11
|
-
"test:e2e": "node test/run.mjs --e2e"
|
|
12
|
+
"test:e2e": "node test/run.mjs --e2e",
|
|
13
|
+
"lint": "echo 'No linter configured'",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"build": "tsc"
|
|
12
16
|
},
|
|
13
17
|
"keywords": [
|
|
14
18
|
"opencode",
|
|
@@ -32,7 +36,12 @@
|
|
|
32
36
|
"commands",
|
|
33
37
|
"config"
|
|
34
38
|
],
|
|
35
|
-
"
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^26.4.0",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"tsx": "^4.19.0"
|
|
43
|
+
},
|
|
44
|
+
"opencodeUpstream": "1.18.25",
|
|
36
45
|
"repository": {
|
|
37
46
|
"type": "git",
|
|
38
47
|
"url": "git+https://github.com/nemoobc/opencode-termux.git"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Prebuilt musl Loader Rebuild Guide
|
|
2
|
+
|
|
3
|
+
File `ld-musl-aarch64-termux.so` adalah custom musl loader yang dipatch agar:
|
|
4
|
+
- Membaca `/etc/resolv.conf` dan `/etc/hosts` dari `$PREFIX/etc/` (Termux prefix)
|
|
5
|
+
- Bukan dari `/etc/` (butuh root)
|
|
6
|
+
|
|
7
|
+
## Cara Rebuild
|
|
8
|
+
|
|
9
|
+
### Prasyarat
|
|
10
|
+
- Linux host (bisa WSL, VM, atau CI)
|
|
11
|
+
- Docker terinstall
|
|
12
|
+
- `aarch64-linux-musl` toolchain
|
|
13
|
+
|
|
14
|
+
### Langkah Build
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 1. Clone musl repo (versi yang kompatibel dengan Alpine 3.21)
|
|
18
|
+
git clone https://git.musl-libc.org/git/musl
|
|
19
|
+
cd musl
|
|
20
|
+
git checkout v1.2.5 # atau tag yang dipakai Alpine 3.21
|
|
21
|
+
|
|
22
|
+
# 2. Patch src/internal/dynlink.c untuk ganti path resolv.conf & hosts
|
|
23
|
+
# Cari baris yang define RESOLV_CONF dan HOSTS_PATH, ganti ke:
|
|
24
|
+
# #define RESOLV_CONF "/data/data/com.termux/files/usr/etc/resolv.conf"
|
|
25
|
+
# #define HOSTS_PATH "/data/data/com.termux/files/usr/etc/hosts"
|
|
26
|
+
|
|
27
|
+
# 3. Build dengan cross-compiler aarch64
|
|
28
|
+
./configure --prefix=/out --target=aarch64-linux-musl --disable-shared
|
|
29
|
+
make -j$(nproc)
|
|
30
|
+
make install
|
|
31
|
+
|
|
32
|
+
# 4. Ambil ld-musl-aarch64.so.1 dari /out/lib/
|
|
33
|
+
# Rename ke ld-musl-aarch64-termux.so
|
|
34
|
+
cp /out/lib/ld-musl-aarch64.so.1 ../prebuilt/ld-musl-aarch64-termux.so
|
|
35
|
+
chmod +x ../prebuilt/ld-musl-aarch64-termux.so
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Alternatif: Build via Docker (lebih bersih)
|
|
39
|
+
|
|
40
|
+
```dockerfile
|
|
41
|
+
# Dockerfile.build-musl
|
|
42
|
+
FROM alpine:3.21 AS builder
|
|
43
|
+
RUN apk add --no-cache build-base linux-headers git
|
|
44
|
+
WORKDIR /musl
|
|
45
|
+
RUN git clone https://git.musl-libc.org/git/musl . && git checkout v1.2.5
|
|
46
|
+
# Apply patch di sini (sed atau patch file)
|
|
47
|
+
RUN sed -i 's|/etc/resolv.conf|/data/data/com.termux/files/usr/etc/resolv.conf|g' src/internal/dynlink.c
|
|
48
|
+
RUN sed -i 's|/etc/hosts|/data/data/com.termux/files/usr/etc/hosts|g' src/internal/dynlink.c
|
|
49
|
+
RUN ./configure --prefix=/out --target=aarch64-linux-musl --disable-shared && make -j$(nproc) && make install
|
|
50
|
+
|
|
51
|
+
FROM scratch
|
|
52
|
+
COPY --from=builder /out/lib/ld-musl-aarch64.so.1 /ld-musl-aarch64-termux.so
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
docker build -f Dockerfile.build-musl -t musl-builder .
|
|
57
|
+
docker create --name temp musl-builder
|
|
58
|
+
docker cp temp:/ld-musl-aarch64-termux.so ./prebuilt/ld-musl-aarch64-termux.so
|
|
59
|
+
docker rm temp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Verifikasi
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Cek ELF
|
|
66
|
+
file prebuilt/ld-musl-aarch64-termux.so
|
|
67
|
+
# Output: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked
|
|
68
|
+
|
|
69
|
+
# Cek string patch
|
|
70
|
+
strings prebuilt/ld-musl-aarch64-termux.so | grep -E 'resolv.conf|hosts'
|
|
71
|
+
# Harus muncul path Termux prefix
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Catatan
|
|
75
|
+
- Loader ini **hanya untuk ARM64 (aarch64)**. Untuk x64, install.mjs otomatis ambil dari Alpine minirootfs.
|
|
76
|
+
- Jika Alpine naik versi major (mis. 3.22), mungkin perlu rebuild ulang loader agar kompatibel.
|
|
77
|
+
- Simpan binary hasil build ke `prebuilt/ld-musl-aarch64-termux.so` dan commit.
|