Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d8b8705
docs: prioritize docs and Termux Codex install
ymkiux Apr 20, 2026
6eb76a1
ui(docs): default to docs and polish install layout
ymkiux Apr 20, 2026
05342a1
ui: suppress models fetch failure toast
ymkiux Apr 20, 2026
e9c1acb
ui(usage): add spacing above loading skeleton
ymkiux Apr 20, 2026
6e592a3
ui(sessions): refine toolbar layout and spacing
ymkiux Apr 20, 2026
fe99d26
ui(docs): always show Termux Codex install command
ymkiux Apr 20, 2026
598d654
ui(docs): avoid text cursor on non-editable areas
ymkiux Apr 20, 2026
a98a451
ui(i18n): add language switch and translate navigation
ymkiux Apr 20, 2026
723165e
ui(i18n): translate docs/sessions/usage and more
ymkiux Apr 20, 2026
8de10ca
ui(i18n): translate settings panel
ymkiux Apr 20, 2026
3b649ce
ui(i18n): translate skills market panel
ymkiux Apr 20, 2026
c8b47f3
ui(i18n): translate claude/openclaw config panels
ymkiux Apr 20, 2026
7cde993
feat(ui): align mobile layout with desktop breakpoints
ymkiux Apr 20, 2026
7412b65
style(ui): refine sidebar language switch
ymkiux Apr 20, 2026
7e23d54
feat(i18n): add additional language options
ymkiux Apr 20, 2026
25f55f1
fix(ui): keep language switch visible on mobile
ymkiux Apr 20, 2026
62d8af8
fix(ui): pin language switch to bottom-left
ymkiux Apr 20, 2026
f52ae36
revert(i18n): limit language toggle to zh/en
ymkiux Apr 20, 2026
8fc8da6
style(ui): show language options inline
ymkiux Apr 20, 2026
c63e4c2
feat(i18n): localize sessions and usage tabs
ymkiux Apr 20, 2026
d58e831
feat(i18n): localize plugins and modal dialogs
ymkiux Apr 20, 2026
31deeeb
fix(i18n): localize editor labels
ymkiux Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
[![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?label=stars&style=flat)](https://github.com/SakuraByteCore/codexmate/stargazers)
[![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?label=issues&style=flat)](https://github.com/SakuraByteCore/codexmate/issues)

[Quick Start](#quick-start) · [Commands](#command-reference) · [Web UI](#web-ui) · [MCP](#mcp) · [中文](README.zh.md)
[Docs](https://sakurabytecore.github.io/codexmate/) · [Quick Start](#quick-start) · [Commands](#command-reference) · [Web UI](#web-ui) · [MCP](#mcp) · [中文](README.zh.md)

</div>

Expand Down Expand Up @@ -150,6 +150,21 @@ Default listen address is `0.0.0.0:3737` for LAN access, and browser auto-open i

> Safety note: the unauthenticated management UI is exposed to your current LAN by default. Use trusted networks only; for local-only access, set `CODEXMATE_HOST=127.0.0.1` or pass `--host 127.0.0.1`.

### Install Codex CLI / Claude Code CLI (optional)

Codex Mate can pass through to the official CLIs (e.g. `codexmate codex ...`). Install them first:

```bash
# Codex CLI (default)
npm install -g @openai/codex

# Codex CLI on Termux (Android)
npm install -g @mmmbuto/codex-cli-termux@latest
Comment on lines +155 to +162

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect npm metadata for the documented Codex packages.
# Expected: Confirm whether `@mmmbuto/codex-cli-termux` is officially maintained before calling it official.

npm view `@mmmbuto/codex-cli-termux` name version repository.url homepage maintainers --json
npm view `@openai/codex` name version repository.url homepage maintainers --json

Repository: SakuraByteCore/codexmate

Length of output: 1035


Clarify package provenance in installation instructions.

Lines 155–162 state "official CLIs", but only @openai/codex is officially maintained by OpenAI. The @mmmbuto/codex-cli-termux package is a third-party Termux-compatible build maintained by a community contributor, not OpenAI. This distinction should be reflected in the documentation to avoid misleading users into treating both packages as equally official.

Suggested wording adjustment
-Codex Mate can pass through to the official CLIs (e.g. `codexmate codex ...`). Install them first:
+Codex Mate can pass through to CLI tools (e.g. `codexmate codex ...`). Install the package that matches your platform:

-# Codex CLI on Termux (Android)
+# Termux-compatible Codex CLI build (Android/Termux)
 npm install -g `@mmmbuto/codex-cli-termux`@latest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 155 - 162, The README currently calls both packages
"official CLIs"; update the wording to distinguish provenance by labeling
`@openai/codex` as the official OpenAI-maintained CLI and marking
`@mmmbuto/codex-cli-termux` as a third‑party/community-maintained
Termux-compatible build maintained by a contributor, e.g., change "official
CLIs" to "CLI options" and add a short parenthetical note after each package
name indicating "official" for `@openai/codex` and "community/third‑party" for
`@mmmbuto/codex-cli-termux` so readers aren’t misled about maintenance status.


# Claude Code
npm install -g @anthropic-ai/claude-code
```

### Run from source

```bash
Expand Down
17 changes: 16 additions & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
[![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?label=stars&style=flat)](https://github.com/SakuraByteCore/codexmate/stargazers)
[![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?label=issues&style=flat)](https://github.com/SakuraByteCore/codexmate/issues)

[快速开始](#快速开始) · [命令速查](#命令速查) · [Web 界面](#web-界面) · [MCP](#mcp) · [English](README.md)
[文档](https://sakurabytecore.github.io/codexmate/) · [快速开始](#快速开始) · [命令速查](#命令速查) · [Web 界面](#web-界面) · [MCP](#mcp) · [English](README.md)

</div>

Expand Down Expand Up @@ -153,6 +153,21 @@ codexmate run

> 安全提示:默认监听会在当前局域网暴露未鉴权的管理界面。若包含 API Key、provider 配置或 skills 管理,请仅在可信网络中使用;如需仅本机访问,可设置 `CODEXMATE_HOST=127.0.0.1` 或启动时传入 `--host 127.0.0.1`。

### 安装 Codex CLI / Claude Code(可选)

Codex Mate 支持透传调用官方 CLI(例如 `codexmate codex ...`),建议先安装:

```bash
# Codex CLI(默认)
npm install -g @openai/codex

# Termux(Android)上的 Codex CLI
npm install -g @mmmbuto/codex-cli-termux@latest
Comment on lines +158 to +165

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

@mmmbuto/codex-cli-termux npm package official

💡 Result:

The npm package @mmmbuto/codex-cli-termux is a legitimate, actively maintained package for running OpenAI Codex CLI in Android Termux (ARM64). It is not "official" from OpenAI but is a community-maintained Termux port/fork of the upstream OpenAI Codex project (https://github.com/openai/codex). Key details: - Latest version: 0.121.0-termux (published Apr 16, 2026) - Publisher/Author: DioNanos (npm lists DioNanos; scoped under @mmmbuto) - Repository: https://github.com/DioNanos/codex-termux (fork of openai/codex) - License: Apache-2.0 (matching upstream) - Installation (Termux): pkg update && pkg upgrade -y && pkg install nodejs-lts -y && npm install -g @mmmbuto/codex-cli-termux@latest && codex --version && codex login - Weekly downloads: ~1.8K - Purpose: Packages upstream Rust binaries (e.g., rust-v0.121.0) for Termux, with patches like disabling voice/realtime audio to avoid Android linker issues. It has an LTS variant (@mmmbuto/codex-cli-lts) for broader platforms (Linux/macOS). Releases are documented on GitHub with 79+ npm versions since late 2025.

Citations:


🌐 Web query:

@openai/codex package npm official OpenAI

💡 Result:

The @openai/codex package is the official NPM package for OpenAI's Codex CLI, a local coding agent that runs on your computer. It is actively maintained, with the latest version 0.120.0 published on 2026-04-16. Install globally via npm install -g @openai/codex or brew install --cask codex. Run codex to start. There is also @openai/codex-sdk (latest 0.114.0 as of 2026-04-11) for embedding Codex in TypeScript/Node.js apps: npm install @openai/codex-sdk. Note: This is distinct from the original OpenAI Codex models (e.g., code-davinci-002), which were deprecated in 2023. The current Codex is a new local/open-source agent launched around April 2025, with CLI, SDK, and integrations for VS Code/Cursor.

Citations:


🏁 Script executed:

curl -s "https://registry.npmjs.org/@mmmbuto/codex-cli-termux" 2>/dev/null | head -50 || echo "Package info lookup attempted"

Repository: SakuraByteCore/codexmate

Length of output: 50381


🏁 Script executed:

curl -s "https://registry.npmjs.org/@openai/codex" 2>/dev/null | head -50 || echo "Package info lookup attempted"

Repository: SakuraByteCore/codexmate

Length of output: 50382


Clarify that the Termux package is a community-maintained build, not official.

Line 158 introduces "官方 CLI" (official CLI), which refers to @openai/codex. However, line 165 then installs @mmmbuto/codex-cli-termux, which is a community-maintained Termux port by DioNanos, not an official OpenAI package. The current structure risks misleading users into thinking both packages are official.

Suggested wording adjustment
-Codex Mate 支持透传调用官方 CLI(例如 `codexmate codex ...`),建议先安装:
+Codex Mate 支持透传调用对应的 CLI 工具(例如 `codexmate codex ...`),建议按平台安装:

-# Termux(Android)上的 Codex CLI
+# Termux(Android)兼容的 Codex CLI 构建
 npm install -g `@mmmbuto/codex-cli-termux`@latest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.zh.md` around lines 158 - 165, Update the README text around the CLI
install section to clarify that `@openai/codex` is the official CLI whereas
`@mmmbuto/codex-cli-termux` is a community-maintained Termux build (not an
official OpenAI package); adjust the phrasing around the install commands so the
line mentioning `@mmmbuto/codex-cli-termux@latest` explicitly notes it is a
third-party/community port and credit the maintainer (DioNanos) to avoid
implying both are official.


# Claude Code
npm install -g @anthropic-ai/claude-code
```

### 从源码运行

```bash
Expand Down
63 changes: 33 additions & 30 deletions tests/unit/config-tabs-ui.test.mjs

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions tests/unit/provider-switch-regression.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,7 @@ test('performProviderSwitch keeps provider when provider models fail to load', a
['loadModelsForProvider', 'beta'],
['applyCodexConfigDirect', { silent: true }]
]);
assert.deepStrictEqual(context.messages, [{
text: '模型列表获取失败,但已切换提供商;请检查 URL/密钥或手动设置模型',
type: 'error'
}]);
assert.deepStrictEqual(context.messages, []);
});

test('updateProvider keeps existing key when edit key input is blank', async () => {
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/web-ui-behavior-parity.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
'sessionListVisibleCount'
];
allowedExtraCurrentKeys.push(
'lang',
'configTemplateDiffVisible',
'configTemplateDiffLoading',
'configTemplateDiffError',
Expand Down Expand Up @@ -412,6 +413,10 @@ test('captured bundled app skeleton only exposes expected data key drift versus
const extraCurrentMethodKeys = currentMethodKeys.filter((key) => !headMethodKeys.includes(key)).sort();
const missingCurrentMethodKeys = headMethodKeys.filter((key) => !currentMethodKeys.includes(key)).sort();
const allowedExtraCurrentMethodKeys = [
'initI18n',
'normalizeLang',
'setLang',
't',
'cancelScheduledSessionListViewportFill',
'canSubmitProvider',
'expandVisibleSessionList',
Expand Down
7 changes: 6 additions & 1 deletion web-ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ document.addEventListener('DOMContentLoaded', () => {
const app = createApp({
data() {
return {
mainTab: 'config',
lang: 'zh',
// 默认选中首个主标签:Docs
mainTab: 'docs',
configMode: 'codex',
currentProvider: '',
currentModel: '',
Expand Down Expand Up @@ -419,6 +421,9 @@ document.addEventListener('DOMContentLoaded', () => {
},

mounted() {
if (typeof this.initI18n === 'function') {
this.initI18n();
}
this.initSessionStandalone();
this.updateCompactLayoutMode();
if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
Expand Down
18 changes: 11 additions & 7 deletions web-ui/logic.runtime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,20 @@ export function shouldForceCompactLayoutMode(options = {}) {
const coarsePointer = !!options.coarsePointer;
const noHover = !!options.noHover;
const isSmallPhysicalScreen = shortEdge > 0 && shortEdge <= 920;
const isNarrowViewport = viewportWidth > 0 && viewportWidth <= 960;
const isNarrowViewport = viewportWidth > 0 && viewportWidth <= 720;
const pointerSuggestsTouchOnly = coarsePointer && noHover;

if (isMobileUa) {
return isNarrowViewport || isSmallPhysicalScreen;
}
if (!pointerSuggestsTouchOnly) {
return false;
if (viewportWidth > 0) {
if (!isNarrowViewport) {
return false;
}
if (isMobileUa) {
return true;
}
return pointerSuggestsTouchOnly && maxTouchPoints > 0;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
if (maxTouchPoints <= 0) {

if (!pointerSuggestsTouchOnly || maxTouchPoints <= 0) {
return false;
}
return isSmallPhysicalScreen;
Expand Down
18 changes: 11 additions & 7 deletions web-ui/modules/app.computed.dashboard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ export function createDashboardComputed() {
const action = this.normalizeInstallAction(this.installCommandAction);
return targets.map((target) => {
const id = target && typeof target.id === 'string' ? target.id : '';
const termuxCommand = id === 'codex'
? this.getInstallCommand(id, action, 'termux')
: '';
return {
...target,
command: this.getInstallCommand(id, action)
command: this.getInstallCommand(id, action),
termuxCommand
};
});
},
Expand Down Expand Up @@ -107,15 +111,15 @@ export function createDashboardComputed() {
const platform = this.resolveInstallPlatform();
if (platform === 'win32') {
return [
'PowerShell 报权限不足(EACCES/EPERM)时,请以管理员身份执行安装命令。',
'安装后若仍提示找不到命令,重开终端并执行:where codex / where claude。',
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
this.t('docs.tip.win.1'),
this.t('docs.tip.win.2'),
this.t('docs.tip.win.3')
];
}
return [
'出现 EACCES 权限错误时,优先修复 Node 全局目录权限,不建议直接 sudo npm。',
'安装后若命令未生效,重开终端并执行:which codex / which claude。',
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
this.t('docs.tip.unix.1'),
this.t('docs.tip.unix.2'),
this.t('docs.tip.unix.3')
];
}
};
Expand Down
48 changes: 24 additions & 24 deletions web-ui/modules/app.computed.main-tabs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,34 +132,34 @@ function createTaskDraftReadiness(metrics) {
export function createMainTabsComputed() {
return {
mainTabKicker() {
if (this.mainTab === 'config') return 'Configuration';
if (this.mainTab === 'sessions') return 'Sessions';
if (this.mainTab === 'usage') return 'Usage';
if (this.mainTab === 'orchestration') return 'Tasks';
if (this.mainTab === 'market') return 'Skills';
if (this.mainTab === 'plugins') return 'Plugins';
if (this.mainTab === 'docs') return 'Docs';
return 'Settings';
if (this.mainTab === 'config') return this.t('kicker.config');
if (this.mainTab === 'sessions') return this.t('kicker.sessions');
if (this.mainTab === 'usage') return this.t('kicker.usage');
if (this.mainTab === 'orchestration') return this.t('kicker.orchestration');
if (this.mainTab === 'market') return this.t('kicker.market');
if (this.mainTab === 'plugins') return this.t('kicker.plugins');
if (this.mainTab === 'docs') return this.t('kicker.docs');
return this.t('kicker.settings');
},
mainTabTitle() {
if (this.mainTab === 'config') return '本地配置控制台';
if (this.mainTab === 'sessions') return '会话与导出';
if (this.mainTab === 'usage') return '本地用量与趋势';
if (this.mainTab === 'orchestration') return '任务编排';
if (this.mainTab === 'market') return 'Skills 安装与同步';
if (this.mainTab === 'plugins') return '插件与模板';
if (this.mainTab === 'docs') return 'CLI 安装与文档';
return '系统与数据设置';
if (this.mainTab === 'config') return this.t('title.config');
if (this.mainTab === 'sessions') return this.t('title.sessions');
if (this.mainTab === 'usage') return this.t('title.usage');
if (this.mainTab === 'orchestration') return this.t('title.orchestration');
if (this.mainTab === 'market') return this.t('title.market');
if (this.mainTab === 'plugins') return this.t('title.plugins');
if (this.mainTab === 'docs') return this.t('title.docs');
return this.t('title.settings');
},
mainTabSubtitle() {
if (this.mainTab === 'config') return '管理本地配置与模型。';
if (this.mainTab === 'sessions') return '浏览与导出会话。';
if (this.mainTab === 'usage') return '查看近 7 / 30 天用量。';
if (this.mainTab === 'orchestration') return '规划、排队、执行与回看本地任务。';
if (this.mainTab === 'market') return '管理本地 Skills。';
if (this.mainTab === 'plugins') return '管理模板化 prompt 与可复用插件。';
if (this.mainTab === 'docs') return '查看 CLI 安装命令与排障。';
return '管理下载、目录与回收站。';
if (this.mainTab === 'config') return this.t('subtitle.config');
if (this.mainTab === 'sessions') return this.t('subtitle.sessions');
if (this.mainTab === 'usage') return this.t('subtitle.usage');
if (this.mainTab === 'orchestration') return this.t('subtitle.orchestration');
if (this.mainTab === 'market') return this.t('subtitle.market');
if (this.mainTab === 'plugins') return this.t('subtitle.plugins');
if (this.mainTab === 'docs') return this.t('subtitle.docs');
return this.t('subtitle.settings');
},
taskOrchestrationSelectedRun() {
return this.taskOrchestration && this.taskOrchestration.selectedRunDetail
Expand Down
3 changes: 0 additions & 3 deletions web-ui/modules/app.methods.codex-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,6 @@ export function createCodexConfigMethods(options = {}) {
await modelsTask;

if (this.currentProvider === name) {
if (this.modelsSource === 'error') {
this.showMessage('模型列表获取失败,但已切换提供商;请检查 URL/密钥或手动设置模型', 'error');
}
if (this.modelsSource === 'remote' && this.models.length > 0 && !this.models.includes(this.currentModel)) {
this.currentModel = this.models[0];
this.modelsHasCurrent = true;
Expand Down
2 changes: 2 additions & 0 deletions web-ui/modules/app.methods.index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { createSessionTrashMethods } from './app.methods.session-trash.mjs';
import { createStartupClaudeMethods } from './app.methods.startup-claude.mjs';
import { createSkillsMethods } from './skills.methods.mjs';
import { createPluginsMethods } from './plugins.methods.mjs';
import { createI18nMethods } from './i18n.mjs';
import {
CONFIG_MODE_SET,
getProviderConfigModeMeta
Expand All @@ -41,6 +42,7 @@ import {

export function createAppMethods() {
return {
...createI18nMethods(),
...createStartupClaudeMethods({
api,
defaultModelContextWindow: DEFAULT_MODEL_CONTEXT_WINDOW,
Expand Down
36 changes: 24 additions & 12 deletions web-ui/modules/app.methods.install.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export function createInstallMethods() {
},

resolveInstallPlatform() {
const navUserAgent = typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string'
? navigator.userAgent.trim().toLowerCase()
: '';
// Termux runs on Android; Codex CLI needs a Termux-friendly build.
if (navUserAgent.includes('termux') || navUserAgent.includes('android')) {
return 'termux';
}
Comment on lines +81 to +87

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether the app already exposes server/runtime platform information that can replace browser-UA detection.
# Expected: Find a backend/runtime platform source, or confirm one needs to be added.

rg -n -C3 "resolveInstallPlatform|navigator\.userAgent|navigator\.platform|process\.platform|installTargetCards|installStatusTargets" --iglob '*.js' --iglob '*.mjs' --iglob '*.html'

Repository: SakuraByteCore/codexmate

Length of output: 15198


🏁 Script executed:

# Check if server exposes platform information to web-UI
rg -n "app\.platform|process\.platform|installStatusTargets|resolveInstallPlatform" --iglob '*.js' --iglob '*.mjs' -A2 -B2 | head -100

Repository: SakuraByteCore/codexmate

Length of output: 5794


🏁 Script executed:

# Look for API endpoints or middleware that provides platform info
rg -n "express|app\.get|app\.post|router\." lib/ --iglob '*.js' | grep -i platform

Repository: SakuraByteCore/codexmate

Length of output: 50


🏁 Script executed:

# Check app.js initialization around installStatusTargets
sed -n '240,280p' web-ui/app.js

Repository: SakuraByteCore/codexmate

Length of output: 1878


🏁 Script executed:

# Check server entry point and any initialization that provides platform info to web-UI
find . -maxdepth 2 -name "index.js" -o -name "server.js" -o -name "app.js" | grep -v node_modules | grep -v web-ui

Repository: SakuraByteCore/codexmate

Length of output: 50


🏁 Script executed:

# Check if there's any middleware or route that sends platform to client
rg -n "res\.json|res\.send|platform" lib/ --iglob '*.js' | grep -E "(platform|json|send)" -A1 -B1 | head -80

Repository: SakuraByteCore/codexmate

Length of output: 342


🏁 Script executed:

# Check the full buildInstallCommandMatrix function to see full scope of the issue
sed -n '96,140p' web-ui/modules/app.methods.install.mjs

Repository: SakuraByteCore/codexmate

Length of output: 2505


Don't infer the install target platform from the browser user agent.

Line 85 switches to the Termux package whenever the viewing browser is Android. Since the web-UI is LAN-accessible, users can view a non-Termux host from Android—or a Termux host from desktop—so the generated commands target the wrong environment. The hardcoded package name in installStatusTargets (@openai/codex) also diverges from the dynamically selected package in buildInstallCommandMatrix() (lines 99–100, 138), creating inconsistency between displayed and installed packages.

Replace browser-UA detection with either a server-reported runtime platform sent to the client on initialization, or an explicit platform selector in the UI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.install.mjs` around lines 81 - 87, The client
should not infer Termux/Android from navigator.userAgent (navUserAgent) or flip
the install target; remove the navUserAgent.includes('termux'|'android') branch
and instead use a server-provided runtime platform value (sent on init and read
by the same module) or require an explicit UI platform selector to set the
install target used by buildInstallCommandMatrix; also ensure
installStatusTargets and the package name used in buildInstallCommandMatrix are
driven from the same single source of truth (a shared variable or init payload)
so the displayed package and the generated commands remain consistent (update
references to installStatusTargets, buildInstallCommandMatrix, and any hardcoded
'@openai/codex' to read that shared platform/package value).

const navPlatform = typeof navigator !== 'undefined' && typeof navigator.platform === 'string'
? navigator.platform.trim().toLowerCase()
: '';
Expand All @@ -86,8 +93,11 @@ export function createInstallMethods() {
return 'linux';
},

buildInstallCommandMatrix(packageManager) {
buildInstallCommandMatrix(packageManager, platformOverride = '') {
const manager = this.normalizeInstallPackageManager(packageManager);
const platform = platformOverride ? String(platformOverride).trim().toLowerCase() : this.resolveInstallPlatform();
const codexPackage = platform === 'termux' ? '@mmmbuto/codex-cli-termux' : '@openai/codex';
const codexInstallPackage = platform === 'termux' ? '@mmmbuto/codex-cli-termux@latest' : '@openai/codex';
const matrix = {
claude: {
install: '',
Expand All @@ -104,34 +114,36 @@ export function createInstallMethods() {
matrix.claude.install = 'pnpm add -g @anthropic-ai/claude-code';
matrix.claude.update = 'pnpm up -g @anthropic-ai/claude-code';
matrix.claude.uninstall = 'pnpm remove -g @anthropic-ai/claude-code';
matrix.codex.install = 'pnpm add -g @openai/codex';
matrix.codex.update = 'pnpm up -g @openai/codex';
matrix.codex.uninstall = 'pnpm remove -g @openai/codex';
matrix.codex.install = `pnpm add -g ${codexInstallPackage}`;
matrix.codex.update = `pnpm up -g ${codexPackage}`;
matrix.codex.uninstall = `pnpm remove -g ${codexPackage}`;
return matrix;
}
if (manager === 'bun') {
matrix.claude.install = 'bun add -g @anthropic-ai/claude-code';
matrix.claude.update = 'bun update -g @anthropic-ai/claude-code';
matrix.claude.uninstall = 'bun remove -g @anthropic-ai/claude-code';
matrix.codex.install = 'bun add -g @openai/codex';
matrix.codex.update = 'bun update -g @openai/codex';
matrix.codex.uninstall = 'bun remove -g @openai/codex';
matrix.codex.install = `bun add -g ${codexInstallPackage}`;
matrix.codex.update = `bun update -g ${codexPackage}`;
matrix.codex.uninstall = `bun remove -g ${codexPackage}`;
return matrix;
}
matrix.claude.install = 'npm install -g @anthropic-ai/claude-code';
matrix.claude.update = 'npm update -g @anthropic-ai/claude-code';
matrix.claude.uninstall = 'npm uninstall -g @anthropic-ai/claude-code';
matrix.codex.install = 'npm install -g @openai/codex';
matrix.codex.update = 'npm update -g @openai/codex';
matrix.codex.uninstall = 'npm uninstall -g @openai/codex';
matrix.codex.install = `npm install -g ${codexInstallPackage}`;
matrix.codex.update = platform === 'termux'
? `npm install -g ${codexInstallPackage}`
: `npm update -g ${codexPackage}`;
matrix.codex.uninstall = `npm uninstall -g ${codexPackage}`;
return matrix;
},

getInstallCommand(targetId, actionName) {
getInstallCommand(targetId, actionName, platformOverride = '') {
const targetKey = typeof targetId === 'string' ? targetId.trim() : '';
if (!targetKey) return '';
const action = this.normalizeInstallAction(actionName);
const currentMap = this.buildInstallCommandMatrix(this.installPackageManager);
const currentMap = this.buildInstallCommandMatrix(this.installPackageManager, platformOverride);
const current = currentMap[targetKey] && typeof currentMap[targetKey][action] === 'string'
? currentMap[targetKey][action]
: '';
Expand Down
7 changes: 0 additions & 7 deletions web-ui/modules/app.methods.startup-claude.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export function createStartupClaudeMethods(options = {}) {
},

async loadModelsForProvider(providerName, options = {}) {
const silentError = !!options.silentError;
const targetProvider = typeof providerName === 'string' ? providerName.trim() : '';
const requestSeq = (Number(this.codexModelsRequestSeq) || 0) + 1;
this.codexModelsRequestSeq = requestSeq;
Expand Down Expand Up @@ -131,9 +130,6 @@ export function createStartupClaudeMethods(options = {}) {
return;
}
if (res.error) {
if (!silentError) {
this.showMessage('获取模型列表失败', 'error');
}
this.models = [];
this.modelsSource = 'error';
this.modelsHasCurrent = true;
Expand All @@ -147,9 +143,6 @@ export function createStartupClaudeMethods(options = {}) {
if (!isLatestRequest()) {
return;
}
if (!silentError) {
this.showMessage('获取模型列表失败', 'error');
}
this.models = [];
this.modelsSource = 'error';
this.modelsHasCurrent = true;
Expand Down
Loading
Loading