@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.
@@ -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.1",
4
- "description": "opencode CLI native untuk Termux/Android tanpa proot membundel loader musl + binary opencode resmi (upstream opencode-ai)",
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
- "opencodeUpstream": "1.18.22",
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.