apply.cjs
5.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Idempotent post-install patches for running @deepseek-ai/dsh on Termux/Android (bionic).
// Re-run safely after every `npm install`. If dsh's upstream source changes shape,
// each patch just logs a skip instead of corrupting the file.
'use strict';
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const projectRoot = path.join(__dirname, '..');
const nodeModules = path.join(projectRoot, 'node_modules');
function patchFile(file, oldStr, newStr, label) {
if (!fs.existsSync(file)) {
console.log(`[dsh-patch] ${label}: file not found, skip (${file})`);
return;
}
const content = fs.readFileSync(file, 'utf8');
if (content.includes(newStr)) {
console.log(`[dsh-patch] ${label}: already patched, skip`);
return;
}
if (!content.includes(oldStr)) {
console.error(`[dsh-patch] ${label}: expected original text not found - dsh internals may have changed, skipping patch`);
return;
}
fs.writeFileSync(file, content.replace(oldStr, newStr));
console.log(`[dsh-patch] ${label}: patched`);
}
// --- Patch 1: dsh-app-boot's internalModules() unconditionally requires the
// native "node-addon-require-builtin" addon, which has no Android prebuild and
// deliberately refuses to compile one locally. cordis-plugin-loader already
// has a --expose-internals fast path elsewhere; mirror it here. ---
patchFile(
path.join(nodeModules, '@deepseek-ai/dsh-app-boot/lib/index.js'),
`function internalModules() {
const addon = createRequire(import.meta.url)("node-addon-require-builtin");
const esmModule = addon.requireBuiltin("internal/modules/esm/loader");
const cjsModule = addon.requireBuiltin("internal/modules/cjs/loader");
const cjsHelpers = addon.requireBuiltin("internal/modules/helpers");
const esmUtils = addon.requireBuiltin("internal/modules/esm/utils");
const esmResolve = addon.requireBuiltin("internal/modules/esm/resolve");`,
`function internalModules() {
const req = createRequire(import.meta.url);
const requireBuiltin = process.execArgv.includes("--expose-internals")
? (id) => req(id)
: (() => { const addon = req("node-addon-require-builtin"); return (id) => addon.requireBuiltin(id); })();
const esmModule = requireBuiltin("internal/modules/esm/loader");
const cjsModule = requireBuiltin("internal/modules/cjs/loader");
const cjsHelpers = requireBuiltin("internal/modules/helpers");
const esmUtils = requireBuiltin("internal/modules/esm/utils");
const esmResolve = requireBuiltin("internal/modules/esm/resolve");`,
'dsh-app-boot internalModules() --expose-internals fallback',
);
// --- Patch 2: node-addon-system's flock.js only allowlists linux/darwin.
// Add an android branch that loads a locally-compiled binding instead of a
// (nonexistent) @deepseek-ai/node-addon-system-android-arm64 package. ---
const flockFile = path.join(nodeModules, '@deepseek-ai/node-addon-system/lib/flock.js');
patchFile(
flockFile,
`function loadBinding() {
if (binding)
return binding;
const { platform, arch } = process;
if (platform !== 'linux' && platform !== 'darwin') {`,
`function loadBinding() {
if (binding)
return binding;
const { platform, arch } = process;
if (platform === 'android') {
const require = createRequire(import.meta.url);
binding = require(join(import.meta.dirname, '..', 'bin', 'android-arm64', 'system.node'));
return binding;
}
if (platform !== 'linux' && platform !== 'darwin') {`,
'node-addon-system flock.js android branch',
);
// --- Patch 3: compile flock.c (published in the package, BSD-3-Clause) into
// bin/android-arm64/system.node with Termux's own toolchain. Node-gyp's
// bundled common.gypi references ${android_ndk_path} unconditionally on
// Android hosts; Termux's own CMake/gyp Android detection additionally needs
// $PREFIX set so it can find $PREFIX/include/android/api-level.h. ---
const systemPkgDir = path.join(nodeModules, '@deepseek-ai/node-addon-system');
const srcFile = path.join(systemPkgDir, 'src/flock.c');
const outBinary = path.join(systemPkgDir, 'bin', 'android-arm64', 'system.node');
if (process.platform !== 'android') {
console.log('[dsh-patch] not running on Android - skipping flock native build');
} else if (!fs.existsSync(srcFile)) {
console.log(`[dsh-patch] flock.c not found (${srcFile}), skip native build`);
} else if (fs.existsSync(outBinary)) {
console.log('[dsh-patch] flock system.node already built, skip');
} else {
const prefix = process.env.PREFIX || '/data/data/com.termux/files/usr';
const buildDir = path.join(__dirname, '.flock-build');
fs.mkdirSync(buildDir, { recursive: true });
fs.copyFileSync(srcFile, path.join(buildDir, 'flock.c'));
fs.writeFileSync(
path.join(buildDir, 'binding.gyp'),
JSON.stringify({ targets: [{ target_name: 'system', sources: ['flock.c'], defines: ['NAPI_VERSION=8'] }] }, null, 2),
);
fs.writeFileSync(
path.join(buildDir, 'package.json'),
JSON.stringify({ name: 'flock-build', version: '1.0.0', private: true, gypfile: true }, null, 2),
);
const nodeGyp = path.join(prefix, 'lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js');
const env = { ...process.env, PREFIX: prefix, npm_config_android_ndk_path: prefix };
console.log('[dsh-patch] compiling flock native addon for android-arm64...');
const result = spawnSync(process.execPath, [nodeGyp, 'rebuild'], { cwd: buildDir, env, stdio: 'inherit' });
if (result.status === 0) {
fs.mkdirSync(path.dirname(outBinary), { recursive: true });
fs.copyFileSync(path.join(buildDir, 'build/Release/system.node'), outBinary);
console.log('[dsh-patch] flock system.node: built and installed');
} else {
console.error('[dsh-patch] WARNING: flock native build failed; session file-locking (flock) will not work');
}
}