@bash0816/claude-code 2.1.157 → 2.1.159-10

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 CHANGED
@@ -1,19 +1,17 @@
1
- # @bash0816/cluade-code
1
+ # @bash0816/claude-code
2
2
 
3
3
  Termux-native Claude Code wrapper package.
4
4
 
5
5
  Termux 向け Claude Code native wrapper package です。
6
6
 
7
- Legacy compatibility package:
7
+ This package is the current Termux wrapper package line.
8
8
 
9
- 互換維持用の legacy package:
10
-
11
- - canonical package: `@bash0816/claude-code`
9
+ この package は現行の Termux wrapper package 系です。
12
10
 
13
11
  ## Install / インストール
14
12
 
15
13
  ```sh
16
- npm install -g @bash0816/cluade-code@latest
14
+ npm install -g @bash0816/claude-code@latest
17
15
  ```
18
16
 
19
17
  If an older audited launcher from this repository already exists at `$PREFIX/bin/claude`, migrate through the updated launcher.
@@ -29,19 +27,23 @@ If npm reports `EEXIST` before package scripts run, use a one-time forced migrat
29
27
  package script 実行前に npm が `EEXIST` を返す場合は、初回だけ forced migration を使います。
30
28
 
31
29
  ```sh
32
- npm install -g --force @bash0816/cluade-code@latest
30
+ npm install -g --force @bash0816/claude-code@latest
33
31
  ```
34
32
 
35
33
  After npm owns the `claude` bin link, normal `npm install -g` updates work.
36
34
 
37
35
  npm が `claude` bin link を管理する状態になれば、通常の `npm install -g` 更新が使えます。
38
36
 
39
- Specific audited version:
37
+ Specific audited versions:
40
38
 
41
39
  監査済みの固定 version:
42
40
 
41
+ Current quick examples:
42
+
43
+ 現在の quick example:
44
+
43
45
  ```sh
44
- npm install -g @bash0816/cluade-code@2.1.123
46
+ npm install -g @bash0816/claude-code@2.1.159-10
45
47
  ```
46
48
 
47
49
  ## Update / 更新
@@ -50,17 +52,9 @@ npm install -g @bash0816/cluade-code@2.1.123
50
52
  claude update
51
53
  ```
52
54
 
53
- `claude update` checks the GitHub release manifest and installs the latest audited package version.
54
-
55
- `claude update` は GitHub release manifest を確認し、最新の監査済み package version を install します。
56
-
57
- For the final legacy bridge release, `claude update` migrates this typo package to the canonical package.
58
-
59
- 最終 legacy bridge release では、`claude update` がこの typo package から canonical package へ移行します。
60
-
61
- This final bridge package is frozen at package version `2.1.123` and launches audited native `2.1.150` by default from the synced metadata set.
55
+ `claude update` installs the latest audited package version.
62
56
 
63
- この最終 bridge package package version `2.1.123` で凍結し、同期済み metadata 集合から既定では監査済み native `2.1.150` を起動します。
57
+ `claude update` は最新の監査済み package version install します。
64
58
 
65
59
  ```sh
66
60
  npm install -g @bash0816/claude-code@<latest_audited_version>
@@ -74,10 +68,10 @@ Normal launch also checks the same manifest with a short timeout and prints a no
74
68
 
75
69
  - Only audited versions in `config/claude-native-audited-versions.json` can run.
76
70
  - `config/claude-native-audited-versions.json` にある監査済み version だけを実行できます。
77
- - This legacy package is frozen at `2.1.123` and exists only as a migration bridge to `@bash0816/claude-code`.
78
- - この legacy package は `2.1.123` で凍結しており、`@bash0816/claude-code` への migration bridge としてのみ存在します。
79
- - The metadata file remains the source of truth for the audited version set.
80
- - 監査済み version 集合の source of truth は引き続き metadata file です。
71
+ - `2.1.118`, `2.1.119`, `2.1.121`, `2.1.122`, `2.1.123`, `2.1.126`, `2.1.128`, `2.1.136`, `2.1.137`, `2.1.138`, `2.1.139`, `2.1.140`, `2.1.141`, `2.1.142`, `2.1.143`, `2.1.144`, `2.1.150`, `2.1.150-1`, `2.1.150-2`, `2.1.150-3`, `2.1.153`, `2.1.153-1`, `2.1.153-2`, `2.1.153-3`, `2.1.153-4`, `2.1.157`, `2.1.159-4`, `2.1.159-8`, `2.1.159-10` are included in the package metadata.
72
+ - `2.1.118`、`2.1.119`、`2.1.121`、`2.1.122`、`2.1.123`、`2.1.126`、`2.1.128`、`2.1.136`、`2.1.137`、`2.1.138`、`2.1.139`、`2.1.140`、`2.1.141`、`2.1.142`、`2.1.143`、`2.1.144`、`2.1.150`、`2.1.150-1`、`2.1.150-2`、`2.1.150-3`、`2.1.153`、`2.1.153-1`、`2.1.153-2`、`2.1.153-3`、`2.1.153-4`、`2.1.157`、`2.1.159-4`、`2.1.159-8`、`2.1.159-10` package metadata に含めています。
73
+ - The metadata file is the source of truth for the currently audited set.
74
+ - 現在の監査済み version 集合の source of truth metadata file です。
81
75
  - Native artifacts are cached under `${HOME}/.claude-termux-native-package`.
82
76
  - native artifact は `${HOME}/.claude-termux-native-package` に cache します。
83
77
  - If native preparation fails, the command exits with an error.
package/bin/claude CHANGED
@@ -14,8 +14,7 @@ done
14
14
  PACKAGE_DIR=$(CDPATH= cd -- "$(dirname -- "${SELF}")/.." && pwd)
15
15
  CONFIG_FILE="${PACKAGE_DIR}/config/claude-native-audited-versions.json"
16
16
  PACKAGE_VERSION=$(node -e 'const fs=require("fs"); const path=require("path"); const p=JSON.parse(fs.readFileSync(path.join(process.argv[1],"package.json"),"utf8")); process.stdout.write(p.version)' "${PACKAGE_DIR}")
17
- DEFAULT_NATIVE_VERSION=$(node -e 'const fs=require("fs"); const path=require("path"); const p=JSON.parse(fs.readFileSync(path.join(process.argv[1],"config","claude-termux-release-manifest.json"),"utf8")); process.stdout.write(String(p.default_native_version || p.latest_audited_version || ""))' "${PACKAGE_DIR}")
18
- CLAUDE_VERSION="${CLAUDE_TERMUX_CLAUDE_VERSION:-${DEFAULT_NATIVE_VERSION:-${PACKAGE_VERSION}}}"
17
+ CLAUDE_VERSION="${CLAUDE_TERMUX_CLAUDE_VERSION:-${PACKAGE_VERSION}}"
19
18
  UPDATE_HELPER="${PACKAGE_DIR}/lib/check-updates.js"
20
19
 
21
20
  if [ "${1:-}" = "update" ]; then
@@ -58,5 +57,6 @@ WORKDIR="${CACHE_DIR}/launcher-workdir" \
58
57
  ENTRY_JS_OFFSET="${ENTRY_JS_OFFSET}" \
59
58
  ENTRY_END_OFFSET="${ENTRY_END_OFFSET}" \
60
59
  CURRENT_CLAUDE_VERSION="${CLAUDE_VERSION}" \
60
+ CLAUDE_TERMUX_PACKAGE_DIR="${PACKAGE_DIR}" \
61
61
  CLAUDE_NATIVE_UPDATE_CHECK=0 \
62
62
  exec sh "${PACKAGE_DIR}/lib/termux-run-claude-native.sh" "$@"
@@ -175,26 +175,57 @@
175
175
  "entry_end_offset": 237958345,
176
176
  "status": "termux_verified"
177
177
  },
178
- "2.1.154": {
179
- "wrapper_spec": "@anthropic-ai/claude-code@2.1.154",
180
- "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.154",
181
- "entry_js_offset": 222957396,
182
- "entry_end_offset": 238482842,
183
- "status": "termux_verified"
184
- },
185
- "2.1.156": {
186
- "wrapper_spec": "@anthropic-ai/claude-code@2.1.156",
187
- "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.156",
188
- "entry_js_offset": 222957812,
189
- "entry_end_offset": 238483519,
190
- "status": "termux_verified"
191
- },
192
178
  "2.1.157": {
193
179
  "wrapper_spec": "@anthropic-ai/claude-code@2.1.157",
194
180
  "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.157",
195
181
  "entry_js_offset": 223210916,
196
182
  "entry_end_offset": 238725072,
197
183
  "status": "termux_verified"
184
+ },
185
+ "2.1.159-2": {
186
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.159",
187
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.159",
188
+ "entry_js_offset": 223232708,
189
+ "entry_end_offset": 238749397,
190
+ "tarball_integrity": "sha512-c8dXbuQdrotGWll46GlnXm5IPpORK2VrBSosCmI6f8t7Snc/3F58fX0MbIjJ/ycXpY0aaKuOnufCuxf7wi1Xqw==",
191
+ "tarball_sha256": "0b551634f11742310ba3b6344fb7912ba1ed8ee0ef4702eab0099d31b4eeb070",
192
+ "status": "offset_discovered"
193
+ },
194
+ "2.1.159-4": {
195
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.159",
196
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.159",
197
+ "entry_js_offset": 223232708,
198
+ "entry_end_offset": 238749397,
199
+ "tarball_integrity": "sha512-c8dXbuQdrotGWll46GlnXm5IPpORK2VrBSosCmI6f8t7Snc/3F58fX0MbIjJ/ycXpY0aaKuOnufCuxf7wi1Xqw==",
200
+ "tarball_sha256": "0b551634f11742310ba3b6344fb7912ba1ed8ee0ef4702eab0099d31b4eeb070",
201
+ "status": "termux_verified"
202
+ },
203
+ "2.1.159-5": {
204
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.159",
205
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.159",
206
+ "entry_js_offset": 223232708,
207
+ "entry_end_offset": 238749397,
208
+ "tarball_integrity": "sha512-c8dXbuQdrotGWll46GlnXm5IPpORK2VrBSosCmI6f8t7Snc/3F58fX0MbIjJ/ycXpY0aaKuOnufCuxf7wi1Xqw==",
209
+ "tarball_sha256": "0b551634f11742310ba3b6344fb7912ba1ed8ee0ef4702eab0099d31b4eeb070",
210
+ "status": "offset_discovered"
211
+ },
212
+ "2.1.159-8": {
213
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.159",
214
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.159",
215
+ "entry_js_offset": 223232708,
216
+ "entry_end_offset": 238749397,
217
+ "tarball_integrity": "sha512-c8dXbuQdrotGWll46GlnXm5IPpORK2VrBSosCmI6f8t7Snc/3F58fX0MbIjJ/ycXpY0aaKuOnufCuxf7wi1Xqw==",
218
+ "tarball_sha256": "0b551634f11742310ba3b6344fb7912ba1ed8ee0ef4702eab0099d31b4eeb070",
219
+ "status": "termux_verified"
220
+ },
221
+ "2.1.159-10": {
222
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.159",
223
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.159",
224
+ "entry_js_offset": 223232708,
225
+ "entry_end_offset": 238749397,
226
+ "tarball_integrity": "sha512-c8dXbuQdrotGWll46GlnXm5IPpORK2VrBSosCmI6f8t7Snc/3F58fX0MbIjJ/ycXpY0aaKuOnufCuxf7wi1Xqw==",
227
+ "tarball_sha256": "0b551634f11742310ba3b6344fb7912ba1ed8ee0ef4702eab0099d31b4eeb070",
228
+ "status": "termux_verified"
198
229
  }
199
230
  }
200
231
  }
@@ -1,13 +1,8 @@
1
1
  {
2
2
  "manifest_version": 1,
3
3
  "package_name": "@bash0816/claude-code",
4
- "canonical_package_name": "@bash0816/claude-code",
5
- "canonical_manifest_url": "https://raw.githubusercontent.com/bash0816/ClaudeCode-Termux/main/config/claude-termux-release-manifest.json",
6
- "canonical_repository_url": "https://github.com/bash0816/ClaudeCode-Termux",
7
- "bridge_package_version": "2.1.157",
8
- "default_native_version": "2.1.157",
9
- "latest_audited_version": "2.1.157",
10
- "latest_candidate_version": "2.1.157",
11
- "previous_stable_version": "2.1.156",
4
+ "latest_audited_version": "2.1.159-10",
5
+ "latest_candidate_version": "2.1.159-10",
6
+ "previous_stable_version": "2.1.159-8",
12
7
  "manifest_url": "https://raw.githubusercontent.com/bash0816/ClaudeCode-Termux/main/config/claude-termux-release-manifest.json"
13
8
  }
@@ -10,6 +10,7 @@ const path = require('path');
10
10
  const packageDir = path.resolve(__dirname, '..');
11
11
  const pkg = require(path.join(packageDir, 'package.json'));
12
12
  const localManifest = require(path.join(packageDir, 'config', 'claude-termux-release-manifest.json'));
13
+ const { compareVersions } = require('./version-utils');
13
14
  const mode = process.argv[2] || 'notify';
14
15
  const currentVersion = process.argv[3] || pkg.version;
15
16
  const dryRun = process.argv.includes('--dry-run');
@@ -19,26 +20,6 @@ const timeoutMs = Number(process.env.CLAUDE_TERMUX_UPDATE_TIMEOUT_MS || '1200');
19
20
  const ttlMs = Number(process.env.CLAUDE_TERMUX_UPDATE_CACHE_TTL_MS || String(24 * 60 * 60 * 1000));
20
21
  const skipCheck = process.env.CLAUDE_TERMUX_SKIP_UPDATE_CHECK === '1';
21
22
  const manifestUrl = process.env.CLAUDE_TERMUX_MANIFEST_URL || localManifest.manifest_url;
22
- const canonicalManifestUrl = process.env.CLAUDE_TERMUX_CANONICAL_MANIFEST_URL || localManifest.canonical_manifest_url || '';
23
- const canonicalPackageName = process.env.CLAUDE_TERMUX_CANONICAL_PACKAGE_NAME || localManifest.canonical_package_name || '';
24
- const canonicalRepositoryUrl = process.env.CLAUDE_TERMUX_CANONICAL_REPOSITORY_URL || localManifest.canonical_repository_url || '';
25
-
26
- function compareVersions(a, b) {
27
- const parse = v => {
28
- const [main, patch] = String(v).split('-');
29
- const parts = main.split('.').map(Number);
30
- parts.push(patch !== undefined ? Number(patch) : 0);
31
- return parts;
32
- };
33
- const aParts = parse(a);
34
- const bParts = parse(b);
35
- const len = Math.max(aParts.length, bParts.length);
36
- for (let index = 0; index < len; index += 1) {
37
- const diff = (aParts[index] || 0) - (bParts[index] || 0);
38
- if (diff !== 0) return diff;
39
- }
40
- return 0;
41
- }
42
23
 
43
24
  function readCache() {
44
25
  try {
@@ -111,64 +92,10 @@ async function resolveManifest(forceRefresh) {
111
92
  }
112
93
  }
113
94
 
114
- async function resolveCanonicalManifest() {
115
- if (!canonicalManifestUrl || !canonicalPackageName || canonicalPackageName === pkg.name) {
116
- return null;
117
- }
118
-
119
- if (skipCheck) {
120
- return {
121
- packageName: canonicalPackageName,
122
- latestVersion: localManifest.latest_audited_version || pkg.version,
123
- repositoryUrl: canonicalRepositoryUrl,
124
- };
125
- }
126
-
127
- try {
128
- const manifest = await fetchManifest(canonicalManifestUrl);
129
- return {
130
- packageName: manifest.package_name || canonicalPackageName,
131
- latestVersion: manifest.latest_audited_version || localManifest.latest_audited_version || pkg.version,
132
- repositoryUrl: canonicalRepositoryUrl,
133
- };
134
- } catch {
135
- return {
136
- packageName: canonicalPackageName,
137
- latestVersion: localManifest.latest_audited_version || pkg.version,
138
- repositoryUrl: canonicalRepositoryUrl,
139
- };
140
- }
141
- }
142
-
143
- async function resolveTarget(forceRefresh) {
144
- const base = await resolveManifest(forceRefresh);
145
- const canonical = await resolveCanonicalManifest();
146
- if (!canonical) {
147
- return {
148
- manifest: base.manifest,
149
- cache: base.cache,
150
- packageName: base.manifest.package_name || pkg.name,
151
- targetVersion: base.manifest.latest_audited_version || localManifest.latest_audited_version || pkg.version,
152
- repositoryUrl: '',
153
- migration: false,
154
- };
155
- }
156
-
157
- return {
158
- manifest: base.manifest,
159
- cache: base.cache,
160
- packageName: canonical.packageName,
161
- targetVersion: canonical.latestVersion,
162
- repositoryUrl: canonical.repositoryUrl,
163
- migration: canonical.packageName !== pkg.name,
164
- };
165
- }
166
-
167
- function updateNoticeCache(cache, packageName, latestVersion) {
95
+ function updateNoticeCache(cache, latestVersion) {
168
96
  const nextCache = {
169
97
  ...cache,
170
98
  last_notice: {
171
- package_name: packageName,
172
99
  current_version: currentVersion,
173
100
  latest_audited_version: latestVersion,
174
101
  notified_at: Date.now(),
@@ -177,10 +104,9 @@ function updateNoticeCache(cache, packageName, latestVersion) {
177
104
  writeCache(nextCache);
178
105
  }
179
106
 
180
- function shouldNotify(cache, packageName, latestVersion) {
107
+ function shouldNotify(cache, latestVersion) {
181
108
  const lastNotice = cache.last_notice;
182
109
  if (!lastNotice) return true;
183
- if (lastNotice.package_name !== packageName) return true;
184
110
  if (lastNotice.current_version !== currentVersion) return true;
185
111
  if (lastNotice.latest_audited_version !== latestVersion) return true;
186
112
  return Date.now() - Number(lastNotice.notified_at || 0) >= ttlMs;
@@ -204,40 +130,26 @@ function installTarget(packageName, targetVersion) {
204
130
  }
205
131
 
206
132
  async function runNotify() {
207
- const target = await resolveTarget(false);
208
- const latest = target.targetVersion;
209
- const packageName = target.packageName;
210
- const migration = target.migration;
211
- if (!migration && compareVersions(currentVersion, latest) >= 0) return 0;
212
- if (!shouldNotify(target.cache, packageName, latest)) return 0;
213
-
214
- if (migration) {
215
- console.error(`Legacy package detected: ${pkg.name}`);
216
- console.error(`Canonical package available: ${packageName}@${latest}`);
217
- if (target.repositoryUrl) {
218
- console.error(`Canonical repository: ${target.repositoryUrl}`);
219
- }
220
- console.error('Run: claude update');
221
- updateNoticeCache(target.cache, packageName, latest);
222
- return 0;
223
- }
133
+ const { manifest, cache } = await resolveManifest(false);
134
+ const latest = manifest.latest_audited_version || localManifest.latest_audited_version || pkg.version;
135
+ if (compareVersions(currentVersion, latest) >= 0) return 0;
136
+ if (!shouldNotify(cache, latest)) return 0;
224
137
 
225
138
  console.error(`Audited update available: ${currentVersion} -> ${latest}`);
226
139
  console.error('Run: claude update');
227
- updateNoticeCache(target.cache, packageName, latest);
140
+ updateNoticeCache(cache, latest);
228
141
  return 0;
229
142
  }
230
143
 
231
144
  async function runUpdate() {
232
- const target = await resolveTarget(true);
233
- if (!target.migration && compareVersions(currentVersion, target.targetVersion) >= 0) {
145
+ const { manifest } = await resolveManifest(true);
146
+ const packageName = manifest.package_name || localManifest.package_name;
147
+ const target = manifest.latest_audited_version || localManifest.latest_audited_version || pkg.version;
148
+ if (compareVersions(currentVersion, target) >= 0) {
234
149
  console.error(`Already on latest audited version: ${currentVersion}`);
235
150
  return 0;
236
151
  }
237
- if (target.migration) {
238
- console.error(`Migrating from legacy package ${pkg.name} to canonical package ${target.packageName}@${target.targetVersion}`);
239
- }
240
- return installTarget(target.packageName, target.targetVersion);
152
+ return installTarget(packageName, target);
241
153
  }
242
154
 
243
155
  async function main() {
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const OFFICIAL_PACKAGE = '@anthropic-ai/claude-code';
7
+ const BLOCK_MESSAGE = [
8
+ 'Official native self-update is disabled on Termux.',
9
+ 'Run: claude update',
10
+ ].join('\n');
11
+
12
+ function lastPathSegment(value) {
13
+ return String(value || '').replace(/\\/g, '/').split('/').pop();
14
+ }
15
+
16
+ function isNpmCommand(command) {
17
+ const text = String(command || '');
18
+ const leaf = lastPathSegment(text);
19
+ return leaf === 'npm' || leaf === 'npm-cli.js';
20
+ }
21
+
22
+ function isPnpmCommand(command) {
23
+ return lastPathSegment(command) === 'pnpm';
24
+ }
25
+
26
+ function isYarnCommand(command) {
27
+ const leaf = lastPathSegment(command);
28
+ return leaf === 'yarn' || leaf === 'yarnpkg';
29
+ }
30
+
31
+ function isNpxCommand(command) {
32
+ const leaf = lastPathSegment(command);
33
+ return leaf === 'npx' || leaf === 'pnpx';
34
+ }
35
+
36
+ function isEnvCommand(command) {
37
+ return lastPathSegment(command) === 'env';
38
+ }
39
+
40
+ function isCorepackCommand(command) {
41
+ return lastPathSegment(command) === 'corepack';
42
+ }
43
+
44
+ function isShellCommand(command) {
45
+ const leaf = lastPathSegment(command);
46
+ return leaf === 'sh' || leaf === 'bash';
47
+ }
48
+
49
+ function isSupportedOperation(value) {
50
+ return ['install', 'i', 'add', 'update', 'up', 'upgrade'].includes(String(value || ''));
51
+ }
52
+
53
+ function isOfficialTarget(value) {
54
+ return String(value || '') === OFFICIAL_PACKAGE || String(value || '').startsWith(`${OFFICIAL_PACKAGE}@`);
55
+ }
56
+
57
+ function normalizeCommand(command, args) {
58
+ let normalizedCommand = command;
59
+ let normalizedArgs = Array.isArray(args) ? args.slice() : [];
60
+
61
+ if (isEnvCommand(normalizedCommand)) {
62
+ while (normalizedArgs.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(String(normalizedArgs[0]))) {
63
+ normalizedArgs.shift();
64
+ }
65
+ if (normalizedArgs.length === 0) return { command: normalizedCommand, args: normalizedArgs };
66
+ normalizedCommand = normalizedArgs.shift();
67
+ }
68
+
69
+ if (isCorepackCommand(normalizedCommand)) {
70
+ if (normalizedArgs.length === 0) return { command: normalizedCommand, args: normalizedArgs };
71
+ normalizedCommand = normalizedArgs.shift();
72
+ }
73
+
74
+ return { command: normalizedCommand, args: normalizedArgs };
75
+ }
76
+
77
+ function hasOfficialTarget(args) {
78
+ return args.some(isOfficialTarget);
79
+ }
80
+
81
+ function shouldBlockNpmLike(command, args) {
82
+ if (!(isNpmCommand(command) || isPnpmCommand(command))) return false;
83
+ if (!args.find(isSupportedOperation)) return false;
84
+ return hasOfficialTarget(args);
85
+ }
86
+
87
+ function shouldBlockYarn(command, args) {
88
+ if (!isYarnCommand(command)) return false;
89
+ return String(args[0] || '') === 'global' && String(args[1] || '') === 'add' && hasOfficialTarget(args.slice(2));
90
+ }
91
+
92
+ function shouldBlockNpx(command, args) {
93
+ if (!isNpxCommand(command)) return false;
94
+ for (let index = 0; index < args.length; index += 1) {
95
+ const token = String(args[index] || '');
96
+ if (token === '-p' || token === '--package') {
97
+ if (isOfficialTarget(args[index + 1])) return true;
98
+ index += 1;
99
+ continue;
100
+ }
101
+ if (token.startsWith('--package=')) {
102
+ if (isOfficialTarget(token.slice('--package='.length))) return true;
103
+ continue;
104
+ }
105
+ if (!token.startsWith('-')) {
106
+ return isOfficialTarget(token);
107
+ }
108
+ }
109
+ return false;
110
+ }
111
+
112
+ function shouldBlockCommand(command, args) {
113
+ if (!Array.isArray(args)) return false;
114
+ const normalized = normalizeCommand(command, args);
115
+ return (
116
+ shouldBlockNpmLike(normalized.command, normalized.args) ||
117
+ shouldBlockYarn(normalized.command, normalized.args) ||
118
+ shouldBlockNpx(normalized.command, normalized.args)
119
+ );
120
+ }
121
+
122
+ function normalizeTokensForExec(tokens) {
123
+ if (tokens.length === 0) return tokens;
124
+ const normalized = normalizeCommand(tokens[0], tokens.slice(1));
125
+ return [normalized.command, ...normalized.args].filter(value => value !== undefined);
126
+ }
127
+
128
+ function unwrapShellCommand(command, args) {
129
+ if (!isShellCommand(command) || !Array.isArray(args) || args.length < 2) return null;
130
+ const first = String(args[0] || '');
131
+ const second = String(args[1] || '');
132
+ if ((first === '-c' || first === '-lc') && second) return second;
133
+ if (first === '-l' && String(args[1] || '') === '-c' && String(args[2] || '')) return String(args[2]);
134
+ return null;
135
+ }
136
+
137
+ function tokenizeShellString(command) {
138
+ const text = String(command || '').trim();
139
+ if (!text) return [];
140
+ return text.match(/(?:[^\s'"]+|'[^']*'|"[^"]*")+/g) || [];
141
+ }
142
+
143
+ function unquote(token) {
144
+ if (
145
+ (token.startsWith('"') && token.endsWith('"')) ||
146
+ (token.startsWith("'") && token.endsWith("'"))
147
+ ) {
148
+ return token.slice(1, -1);
149
+ }
150
+ return token;
151
+ }
152
+
153
+ function shouldBlockExecString(command) {
154
+ const tokens = normalizeTokensForExec(tokenizeShellString(command).map(unquote));
155
+ if (tokens.length === 0) return false;
156
+ const shellInner = unwrapShellCommand(tokens[0], tokens.slice(1));
157
+ if (shellInner) return shouldBlockExecString(shellInner);
158
+ return shouldBlockCommand(tokens[0], tokens.slice(1));
159
+ }
160
+
161
+ function createBlockedError(blockedStderr) {
162
+ const error = new Error(BLOCK_MESSAGE);
163
+ error.code = 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED';
164
+ error.status = 1;
165
+ error.stdout = Buffer.from('');
166
+ error.stderr = Buffer.from(blockedStderr);
167
+ return error;
168
+ }
169
+
170
+ function createBlockedSpawn(blockedStderr, stderrWriter, options) {
171
+ const child = new EventEmitter();
172
+ child.stdout = null;
173
+ child.stderr = null;
174
+ child.stdin = null;
175
+ child.pid = 0;
176
+ child.killed = false;
177
+ child.kill = () => false;
178
+ process.nextTick(() => {
179
+ if (!(options && options.stdio === 'ignore')) {
180
+ stderrWriter(blockedStderr);
181
+ }
182
+ child.emit('close', 1, null);
183
+ child.emit('exit', 1, null);
184
+ });
185
+ return child;
186
+ }
187
+
188
+ function createBlockedSpawnSync(blockedStderr, stderrWriter) {
189
+ stderrWriter(blockedStderr);
190
+ return {
191
+ pid: 0,
192
+ output: [null, Buffer.from(''), Buffer.from(blockedStderr)],
193
+ stdout: Buffer.from(''),
194
+ stderr: Buffer.from(blockedStderr),
195
+ status: 1,
196
+ signal: null,
197
+ error: createBlockedError(blockedStderr),
198
+ };
199
+ }
200
+
201
+ function createBlockedExecResult(blockedStderr, stderrWriter, callback) {
202
+ const child = createBlockedSpawn(blockedStderr, stderrWriter);
203
+ const error = createBlockedError(blockedStderr);
204
+ process.nextTick(() => {
205
+ if (typeof callback === 'function') {
206
+ callback(error, '', blockedStderr);
207
+ }
208
+ });
209
+ return child;
210
+ }
211
+
212
+ function shouldBlockSpawnArgs(args) {
213
+ if (!Array.isArray(args) || args.length === 0) return false;
214
+ const command = args[0];
215
+ const commandArgs = Array.isArray(args[1]) ? args[1] : [];
216
+ const shellInner = unwrapShellCommand(command, commandArgs);
217
+ if (shellInner) return shouldBlockExecString(shellInner);
218
+ return shouldBlockCommand(command, commandArgs);
219
+ }
220
+
221
+ function createGuardedChildProcess(realChild, stderrWriter = value => process.stderr.write(value)) {
222
+ const blockedStderr = `${BLOCK_MESSAGE}\n`;
223
+ return {
224
+ spawn: (...args) => {
225
+ if (shouldBlockSpawnArgs(args)) return createBlockedSpawn(blockedStderr, stderrWriter, args[2]);
226
+ return realChild.spawn(...args);
227
+ },
228
+ execFile: (...args) => {
229
+ if (shouldBlockSpawnArgs(args)) return createBlockedExecResult(blockedStderr, stderrWriter, args[args.length - 1]);
230
+ return realChild.execFile(...args);
231
+ },
232
+ exec: (...args) => {
233
+ if (shouldBlockExecString(args[0])) return createBlockedExecResult(blockedStderr, stderrWriter, args[args.length - 1]);
234
+ return realChild.exec(...args);
235
+ },
236
+ spawnSync: (...args) => {
237
+ if (shouldBlockSpawnArgs(args)) return createBlockedSpawnSync(blockedStderr, stderrWriter);
238
+ return realChild.spawnSync(...args);
239
+ },
240
+ execFileSync: (...args) => {
241
+ if (shouldBlockSpawnArgs(args)) throw createBlockedError(blockedStderr);
242
+ return realChild.execFileSync(...args);
243
+ },
244
+ execSync: (...args) => {
245
+ if (shouldBlockExecString(args[0])) throw createBlockedError(blockedStderr);
246
+ return realChild.execSync(...args);
247
+ },
248
+ };
249
+ }
250
+
251
+ module.exports = {
252
+ BLOCK_MESSAGE,
253
+ OFFICIAL_PACKAGE,
254
+ createGuardedChildProcess,
255
+ shouldBlockCommand,
256
+ shouldBlockExecString,
257
+ };