codexmate 0.0.23 → 0.0.25
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 +32 -9
- package/README.zh.md +33 -9
- package/cli/auth-profiles.js +23 -7
- package/cli/builtin-proxy.js +35 -0
- package/cli/claude-proxy.js +24 -0
- package/cli/doctor-core.js +903 -0
- package/cli/import-skills-url.js +356 -0
- package/cli/openai-bridge.js +51 -4
- package/cli/session-usage.js +8 -2
- package/cli.js +1921 -399
- package/lib/automation.js +404 -0
- package/lib/cli-models-utils.js +0 -40
- package/lib/cli-network-utils.js +28 -2
- package/lib/cli-path-utils.js +21 -5
- package/lib/cli-sessions.js +32 -1
- package/lib/download-artifacts.js +17 -2
- package/lib/mcp-stdio.js +13 -0
- package/package.json +3 -3
- package/plugins/README.md +20 -0
- package/plugins/README.zh-CN.md +20 -0
- package/plugins/prompt-templates/comment-polish/index.mjs +25 -0
- package/plugins/prompt-templates/computed.mjs +253 -0
- package/plugins/prompt-templates/index.mjs +8 -0
- package/plugins/prompt-templates/manifest.mjs +15 -0
- package/plugins/prompt-templates/methods.mjs +619 -0
- package/plugins/prompt-templates/overview.mjs +90 -0
- package/plugins/prompt-templates/ownership.mjs +19 -0
- package/plugins/prompt-templates/rule-ack/index.mjs +21 -0
- package/plugins/prompt-templates/storage.mjs +64 -0
- package/plugins/registry.mjs +16 -0
- package/web-ui/app.js +21 -35
- package/web-ui/index.html +4 -3
- package/web-ui/logic.sessions.mjs +2 -2
- package/web-ui/modules/app.computed.dashboard.mjs +24 -22
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
- package/web-ui/modules/app.computed.session.mjs +17 -0
- package/web-ui/modules/app.methods.agents.mjs +91 -3
- package/web-ui/modules/app.methods.codex-config.mjs +153 -164
- package/web-ui/modules/app.methods.install.mjs +28 -0
- package/web-ui/modules/app.methods.navigation.mjs +34 -1
- package/web-ui/modules/app.methods.runtime.mjs +24 -2
- package/web-ui/modules/app.methods.session-actions.mjs +8 -1
- package/web-ui/modules/app.methods.session-browser.mjs +37 -6
- package/web-ui/modules/app.methods.session-trash.mjs +4 -2
- package/web-ui/modules/config-mode.computed.mjs +1 -3
- package/web-ui/modules/i18n.dict.mjs +2055 -0
- package/web-ui/modules/i18n.mjs +2 -1769
- package/web-ui/partials/index/layout-header.html +48 -34
- package/web-ui/partials/index/modal-config-template-agents.html +3 -4
- package/web-ui/partials/index/modal-health-check.html +33 -60
- package/web-ui/partials/index/panel-config-claude.html +35 -15
- package/web-ui/partials/index/panel-config-codex.html +47 -19
- package/web-ui/partials/index/panel-config-openclaw.html +8 -3
- package/web-ui/partials/index/panel-dashboard.html +186 -0
- package/web-ui/partials/index/panel-docs.html +1 -1
- package/web-ui/partials/index/panel-market.html +3 -0
- package/web-ui/partials/index/panel-orchestration.html +3 -0
- package/web-ui/partials/index/panel-plugins.html +16 -10
- package/web-ui/partials/index/panel-sessions.html +8 -3
- package/web-ui/partials/index/panel-settings.html +1 -1
- package/web-ui/partials/index/panel-usage.html +9 -1
- package/web-ui/res/logo-pack.webp +0 -0
- package/web-ui/styles/controls-forms.css +58 -4
- package/web-ui/styles/dashboard.css +274 -0
- package/web-ui/styles/layout-shell.css +3 -2
- package/web-ui/styles/responsive.css +0 -2
- package/web-ui/styles/sessions-list.css +5 -7
- package/web-ui/styles/sessions-toolbar-trash.css +4 -4
- package/web-ui/styles/sessions-usage.css +33 -0
- package/web-ui/styles.css +1 -0
- package/res/logo.png +0 -0
- /package/{res → web-ui/res}/json5.min.js +0 -0
- /package/{res → web-ui/res}/vue.global.prod.js +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="
|
|
3
|
+
<img src="site/.vitepress/public/images/logo.png" alt="Codex Mate logo" width="180" />
|
|
4
4
|
|
|
5
5
|
# Codex Mate
|
|
6
6
|
|
|
@@ -29,7 +29,7 @@ Codex Mate is a local-first CLI + Web UI for unified management of:
|
|
|
29
29
|
- Claude Code `CLAUDE.md` editing (writes to `~/.claude/CLAUDE.md`)
|
|
30
30
|
- OpenClaw JSON5 profiles and workspace `AGENTS.md`
|
|
31
31
|
- Local skills market for Codex / Claude Code (target switching, local skills management, cross-app import, ZIP distribution)
|
|
32
|
-
- Local Codex/Claude sessions (list/filter/export/delete) with Usage analytics overview
|
|
32
|
+
- Local Codex/Claude/Gemini CLI/CodeBuddy Code sessions (list/filter/export/delete) with Usage analytics overview
|
|
33
33
|
- Plugins (Prompt templates): reusable templates with variables and one-click copy
|
|
34
34
|
- Task orchestration: plan/queue/run/review local tasks
|
|
35
35
|
|
|
@@ -57,12 +57,20 @@ It works on local files directly and does not require cloud hosting. The skills
|
|
|
57
57
|
- OpenClaw JSON5 profile management
|
|
58
58
|
|
|
59
59
|
**Session Management**
|
|
60
|
-
- Unified Codex + Claude session list
|
|
60
|
+
- Unified Codex + Claude + Gemini CLI + CodeBuddy Code session list
|
|
61
|
+
- Session locations (local-first, configurable):
|
|
62
|
+
- Codex: `~/.codex/sessions/*.jsonl` (or `$CODEX_HOME/sessions`, `$XDG_CONFIG_HOME/codex/sessions`)
|
|
63
|
+
- Claude: `~/.claude/projects/**/**/*.jsonl` (or `$CLAUDE_HOME/projects`, `$XDG_CONFIG_HOME/claude/projects`)
|
|
64
|
+
- Gemini: `~/.gemini/tmp/*/chats/*.json` (or `$GEMINI_HOME/tmp`, `$XDG_CONFIG_HOME/gemini/tmp`)
|
|
65
|
+
- CodeBuddy: `~/.codebuddy/projects/**/**/*.jsonl` (or `$CODEBUDDY_CODE_HOME_DIR/projects`)
|
|
61
66
|
- Local session pinning with persistent pinned state and pinned-first ordering
|
|
62
|
-
- Keyword/source/cwd filters
|
|
67
|
+
- Keyword/source/cwd/role/time filters, plus shareable filter links
|
|
68
|
+
- Copy resume command (Codex/Gemini/CodeBuddy): `codex resume <sessionId>` / `gemini -r <sessionId>` / `codebuddy -r <sessionId>`
|
|
69
|
+
- Fast search UX: short-lived query result caching to avoid rescanning on each keystroke
|
|
63
70
|
- Usage subview with 7d / 30d session trends, message trends, source share, and top paths
|
|
64
|
-
- Markdown export
|
|
65
|
-
- Session-level and message-level delete (supports batch)
|
|
71
|
+
- Markdown export (Web UI + `codexmate export-session`, supports `--session-id` or `--file`)
|
|
72
|
+
- Session-level and message-level delete (supports batch), with a local recycle bin for restore/purge
|
|
73
|
+
- Large-session preview optimization (fast tail preview path)
|
|
66
74
|
|
|
67
75
|
**Skills Market**
|
|
68
76
|
- Switch the skills install target between Codex and Claude Code
|
|
@@ -76,10 +84,20 @@ It works on local files directly and does not require cloud hosting. The skills
|
|
|
76
84
|
|
|
77
85
|
**Engineering Utilities**
|
|
78
86
|
- MCP stdio domains (`tools`, `resources`, `prompts`)
|
|
87
|
+
- Automation hooks (`/hooks/*`) + outbound webhook notifiers
|
|
79
88
|
- Built-in proxy controls (`proxy`)
|
|
80
89
|
- Auth profile management (`auth`)
|
|
81
90
|
- Zip/unzip utilities
|
|
82
91
|
|
|
92
|
+
## Automation (signal → action)
|
|
93
|
+
|
|
94
|
+
When running `codexmate run`, you can accept external webhooks and convert them into queued tasks:
|
|
95
|
+
|
|
96
|
+
- Webhook entry: `POST /hooks/<source>` (currently `github`, `gitlab`)
|
|
97
|
+
- Rule config: `~/.codex/codexmate-automation.json`
|
|
98
|
+
- Supported action: `task.queue.add` (optionally `startQueue: true`)
|
|
99
|
+
- Notifications: `notifiers[]` supports `type: "webhook"` for Slack/Feishu-style incoming webhooks
|
|
100
|
+
|
|
83
101
|
## Architecture
|
|
84
102
|
|
|
85
103
|
### At a glance (what it does → what you get)
|
|
@@ -164,6 +182,12 @@ npm install -g @mmmbuto/codex-cli-termux@latest
|
|
|
164
182
|
|
|
165
183
|
# Claude Code
|
|
166
184
|
npm install -g @anthropic-ai/claude-code
|
|
185
|
+
|
|
186
|
+
# Gemini CLI
|
|
187
|
+
npm install -g @google/gemini-cli
|
|
188
|
+
|
|
189
|
+
# CodeBuddy Code
|
|
190
|
+
npm install -g @tencent-ai/codebuddy-code
|
|
167
191
|
```
|
|
168
192
|
|
|
169
193
|
### Run from source
|
|
@@ -190,7 +214,7 @@ npm run reset
|
|
|
190
214
|
npm run reset 79
|
|
191
215
|
```
|
|
192
216
|
|
|
193
|
-
- `npm run reset`:
|
|
217
|
+
- `npm run reset`: reset to default `origin/main`
|
|
194
218
|
- `npm run reset 79`: sync directly to the latest head snapshot of PR `#79`
|
|
195
219
|
- The script also handles local branch switching, workspace cleanup, untracked file cleanup, and final state validation
|
|
196
220
|
|
|
@@ -206,13 +230,12 @@ npm run reset 79
|
|
|
206
230
|
| `codexmate delete <name>` | Delete provider |
|
|
207
231
|
| `codexmate claude <BaseURL> <API_KEY> [model]` | Write Claude Code config |
|
|
208
232
|
| `codexmate auth <list\|import\|switch\|delete\|status>` | Auth profile management |
|
|
209
|
-
| `codexmate proxy <status\|set\|apply\|enable\|start\|stop>` | Built-in proxy management |
|
|
210
233
|
| `codexmate workflow <list\|get\|validate\|run\|runs>` | MCP workflow management |
|
|
211
234
|
| `codexmate codex [args...] [--follow-up <text> repeatable]` | Codex CLI passthrough entrypoint (auto-adds `--yolo`, supports queued follow-up appends) |
|
|
212
235
|
| `codexmate qwen [args...]` | Qwen CLI passthrough entrypoint |
|
|
213
236
|
| `codexmate run [--host <HOST>] [--no-browser]` | Start Web UI |
|
|
214
237
|
| `codexmate mcp serve [--read-only\|--allow-write]` | Start MCP stdio server |
|
|
215
|
-
| `codexmate export-session --source <codex\|claude> ...` | Export session to Markdown |
|
|
238
|
+
| `codexmate export-session --source <codex\|claude\|gemini\|codebuddy> ...` | Export session to Markdown |
|
|
216
239
|
| `codexmate zip <path> [--max:0-9]` / `codexmate unzip <zip> [out]` | Zip / unzip |
|
|
217
240
|
| `codexmate unzip-ext <zip-dir> [out] [--ext:suffix[,suffix...]] [--no-recursive]` | Extract files with target suffixes from ZIP files in a directory (default `.json`, recursive by default) |
|
|
218
241
|
|
package/README.zh.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="
|
|
3
|
+
<img src="site/.vitepress/public/images/logo.png" alt="Codex Mate logo" width="180" />
|
|
4
4
|
|
|
5
5
|
# Codex Mate
|
|
6
6
|
|
|
@@ -29,7 +29,7 @@ Codex Mate 提供一套本地优先的 CLI + Web UI,用于统一管理:
|
|
|
29
29
|
- Claude Code `CLAUDE.md` 编辑(写入 `~/.claude/CLAUDE.md`)
|
|
30
30
|
- OpenClaw JSON5 配置与 Workspace `AGENTS.md`
|
|
31
31
|
- Codex / Claude Code Skills 市场(安装目标切换、本地 skills 管理、跨应用导入、ZIP 分发)
|
|
32
|
-
- Codex / Claude 本地会话浏览、筛选、导出、删除与 Usage 统计概览
|
|
32
|
+
- Codex / Claude / Gemini CLI / CodeBuddy Code 本地会话浏览、筛选、导出、删除与 Usage 统计概览
|
|
33
33
|
- 插件(提示词模板):模板复用、变量填写、一键复制
|
|
34
34
|
- 任务编排:规划 / 排队 / 执行 / 回看
|
|
35
35
|
|
|
@@ -58,12 +58,20 @@ Codex Mate 提供一套本地优先的 CLI + Web UI,用于统一管理:
|
|
|
58
58
|
- OpenClaw JSON5 配置方案管理
|
|
59
59
|
|
|
60
60
|
**会话管理**
|
|
61
|
-
- 同页查看 Codex 与
|
|
61
|
+
- 同页查看 Codex、Claude、Gemini CLI 与 CodeBuddy Code 会话
|
|
62
|
+
- 会话来源与默认路径(本地优先,可通过环境变量覆盖):
|
|
63
|
+
- Codex:`~/.codex/sessions/*.jsonl`(或 `$CODEX_HOME/sessions`、`$XDG_CONFIG_HOME/codex/sessions`)
|
|
64
|
+
- Claude:`~/.claude/projects/**/**/*.jsonl`(或 `$CLAUDE_HOME/projects`、`$XDG_CONFIG_HOME/claude/projects`)
|
|
65
|
+
- Gemini:`~/.gemini/tmp/*/chats/*.json`(或 `$GEMINI_HOME/tmp`、`$XDG_CONFIG_HOME/gemini/tmp`)
|
|
66
|
+
- CodeBuddy:`~/.codebuddy/projects/**/**/*.jsonl`(或 `$CODEBUDDY_CODE_HOME_DIR/projects`)
|
|
62
67
|
- 支持本地会话置顶,置顶状态持久化保存并优先排序显示
|
|
63
|
-
- 关键词搜索、来源筛选、cwd
|
|
68
|
+
- 关键词搜索、来源筛选、cwd/角色/时间筛选,并支持复制筛选链接
|
|
69
|
+
- 复制恢复命令(Codex/Gemini/CodeBuddy):`codex resume <sessionId>` / `gemini -r <sessionId>` / `codebuddy -r <sessionId>`
|
|
70
|
+
- 搜索体验优化:短周期结果缓存,避免输入时重复扫描
|
|
64
71
|
- Usage 子页:近 7 天 / 近 30 天会话趋势、消息趋势、来源占比、高频路径
|
|
65
|
-
- 会话导出 Markdown
|
|
66
|
-
-
|
|
72
|
+
- 会话导出 Markdown(Web UI + `codexmate export-session`,支持 `--session-id` 或 `--file`)
|
|
73
|
+
- 会话与消息级删除(支持批量),并提供本地回收站用于恢复/彻底删除
|
|
74
|
+
- 大会话预览优化(快速 tail 预览路径)
|
|
67
75
|
|
|
68
76
|
**Skills 市场**
|
|
69
77
|
- 在 Codex 与 Claude Code 之间切换 skills 安装目标
|
|
@@ -81,8 +89,18 @@ Codex Mate 提供一套本地优先的 CLI + Web UI,用于统一管理:
|
|
|
81
89
|
|
|
82
90
|
**工程能力**
|
|
83
91
|
- MCP stdio 能力(tools/resources/prompts)
|
|
92
|
+
- 自动化钩子(`/hooks/*`)+ 外发 webhook 通知
|
|
84
93
|
- Zip 压缩/解压(优先系统工具,失败回退 JS 库)
|
|
85
94
|
|
|
95
|
+
## 自动化(信号 → 行动)
|
|
96
|
+
|
|
97
|
+
运行 `codexmate run` 后,可接收外部 webhook 并转为任务队列:
|
|
98
|
+
|
|
99
|
+
- 入口:`POST /hooks/<source>`(当前支持 `github`、`gitlab`)
|
|
100
|
+
- 规则:`~/.codex/codexmate-automation.json`
|
|
101
|
+
- 动作:`task.queue.add`(可选 `startQueue: true`)
|
|
102
|
+
- 通知:`notifiers[]` 支持 `type: "webhook"`(适配 Slack/飞书等入站 webhook)
|
|
103
|
+
|
|
86
104
|
## 架构总览
|
|
87
105
|
|
|
88
106
|
### 一图看懂(从“做什么”到“产生什么效果”)
|
|
@@ -154,7 +172,7 @@ codexmate run
|
|
|
154
172
|
|
|
155
173
|
> 安全提示:默认监听会在当前局域网暴露未鉴权的管理界面。若包含 API Key、provider 配置或 skills 管理,请仅在可信网络中使用;如需仅本机访问,可设置 `CODEXMATE_HOST=127.0.0.1` 或启动时传入 `--host 127.0.0.1`。
|
|
156
174
|
|
|
157
|
-
### 安装 Codex CLI / Claude Code(可选)
|
|
175
|
+
### 安装 Codex CLI / Claude Code / Gemini CLI / CodeBuddy Code(可选)
|
|
158
176
|
|
|
159
177
|
Codex Mate 支持透传调用官方 CLI(例如 `codexmate codex ...`),建议先安装:
|
|
160
178
|
|
|
@@ -167,6 +185,12 @@ npm install -g @mmmbuto/codex-cli-termux@latest
|
|
|
167
185
|
|
|
168
186
|
# Claude Code
|
|
169
187
|
npm install -g @anthropic-ai/claude-code
|
|
188
|
+
|
|
189
|
+
# Gemini CLI
|
|
190
|
+
npm install -g @google/gemini-cli
|
|
191
|
+
|
|
192
|
+
# CodeBuddy Code
|
|
193
|
+
npm install -g @tencent-ai/codebuddy-code
|
|
170
194
|
```
|
|
171
195
|
|
|
172
196
|
### 从源码运行
|
|
@@ -193,7 +217,7 @@ npm run reset
|
|
|
193
217
|
npm run reset 79
|
|
194
218
|
```
|
|
195
219
|
|
|
196
|
-
- `npm run reset
|
|
220
|
+
- `npm run reset`:直接重置到默认 `origin/main`
|
|
197
221
|
- `npm run reset 79`:直接同步到 PR `#79` 的最新 head 快照
|
|
198
222
|
- 脚本会自动完成本地分支切换、工作区清理、未跟踪文件清理与最终状态校验
|
|
199
223
|
|
|
@@ -213,7 +237,7 @@ npm run reset 79
|
|
|
213
237
|
| `codexmate qwen [args...]` | Qwen CLI 透传入口 |
|
|
214
238
|
| `codexmate run [--host <HOST>] [--no-browser]` | 启动 Web UI |
|
|
215
239
|
| `codexmate mcp serve [--read-only\|--allow-write]` | 启动 MCP stdio 服务 |
|
|
216
|
-
| `codexmate export-session --source <codex\|claude> ...` | 导出会话为 Markdown |
|
|
240
|
+
| `codexmate export-session --source <codex\|claude\|gemini\|codebuddy> ...` | 导出会话为 Markdown |
|
|
217
241
|
| `codexmate zip <path> [--max:0-9]` / `codexmate unzip <zip> [out]` | 压缩 / 解压 |
|
|
218
242
|
| `codexmate unzip-ext <zip-dir> [out] [--ext:suffix[,suffix...]] [--no-recursive]` | 批量提取目录下 ZIP 内指定后缀文件(默认 `.json`,默认递归) |
|
|
219
243
|
|
package/cli/auth-profiles.js
CHANGED
|
@@ -28,12 +28,17 @@ function createAuthProfileController(deps = {}) {
|
|
|
28
28
|
function normalizeAuthProfileName(value) {
|
|
29
29
|
const raw = typeof value === 'string' ? value.trim() : '';
|
|
30
30
|
if (!raw) return '';
|
|
31
|
-
|
|
31
|
+
return raw.slice(0, 120);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function sanitizeAuthProfileFileStem(value) {
|
|
35
|
+
const raw = typeof value === 'string' ? value.trim() : '';
|
|
36
|
+
if (!raw) return '';
|
|
37
|
+
return raw
|
|
32
38
|
.replace(/[\\/:*?"<>|]/g, '-')
|
|
33
39
|
.replace(/\s+/g, '-')
|
|
34
40
|
.replace(/^-+|-+$/g, '')
|
|
35
41
|
.slice(0, 120);
|
|
36
|
-
return sanitized;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
function normalizeAuthRegistry(raw) {
|
|
@@ -46,7 +51,7 @@ function createAuthProfileController(deps = {}) {
|
|
|
46
51
|
version: 1,
|
|
47
52
|
current: typeof raw.current === 'string' ? raw.current.trim() : '',
|
|
48
53
|
items: items.map((item) => ({
|
|
49
|
-
name:
|
|
54
|
+
name: item.name.trim(),
|
|
50
55
|
fileName: typeof item.fileName === 'string' ? path.basename(item.fileName) : '',
|
|
51
56
|
type: typeof item.type === 'string' ? item.type : '',
|
|
52
57
|
email: typeof item.email === 'string' ? item.email : '',
|
|
@@ -135,13 +140,24 @@ function createAuthProfileController(deps = {}) {
|
|
|
135
140
|
const sourceFile = typeof options.sourceFile === 'string' ? options.sourceFile : '';
|
|
136
141
|
const preferredName = normalizeAuthProfileName(options.name || '');
|
|
137
142
|
const profileName = preferredName || getAuthProfileNameFallback(safePayload, sourceFile);
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
let fileStem = sanitizeAuthProfileFileStem(profileName) || sanitizeAuthProfileFileStem(sourceFile) || `auth-${Date.now()}`;
|
|
144
|
+
|
|
145
|
+
const registry = readAuthRegistry();
|
|
146
|
+
const existed = registry.items.find((item) => item && item.name === profileName);
|
|
147
|
+
if (existed && existed.fileName) {
|
|
148
|
+
fileStem = path.basename(existed.fileName, path.extname(existed.fileName));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let fileName = `${fileStem}.json`;
|
|
152
|
+
let profilePath = path.join(AUTH_PROFILES_DIR, fileName);
|
|
153
|
+
if (!existed && fs.existsSync(profilePath)) {
|
|
154
|
+
fileStem = `${fileStem}-${Date.now().toString(16).slice(-6)}`;
|
|
155
|
+
fileName = `${fileStem}.json`;
|
|
156
|
+
profilePath = path.join(AUTH_PROFILES_DIR, fileName);
|
|
157
|
+
}
|
|
140
158
|
|
|
141
159
|
ensureDir(AUTH_PROFILES_DIR);
|
|
142
160
|
writeJsonAtomic(profilePath, safePayload);
|
|
143
|
-
|
|
144
|
-
const registry = readAuthRegistry();
|
|
145
161
|
const meta = buildAuthProfileSummary(profileName, safePayload, fileName);
|
|
146
162
|
meta.importedAt = toIsoTime(Date.now());
|
|
147
163
|
meta.sourceFile = sourceFile || '';
|
package/cli/builtin-proxy.js
CHANGED
|
@@ -674,6 +674,41 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
674
674
|
return;
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
const remoteAddr = req && req.socket ? req.socket.remoteAddress : '';
|
|
678
|
+
const isLoopback = !remoteAddr
|
|
679
|
+
|| remoteAddr === '127.0.0.1'
|
|
680
|
+
|| remoteAddr === '::1'
|
|
681
|
+
|| remoteAddr === '::ffff:127.0.0.1';
|
|
682
|
+
if (!isLoopback) {
|
|
683
|
+
const expected = typeof process.env.CODEXMATE_HTTP_TOKEN === 'string'
|
|
684
|
+
? process.env.CODEXMATE_HTTP_TOKEN.trim()
|
|
685
|
+
: '';
|
|
686
|
+
if (!expected) {
|
|
687
|
+
const body = JSON.stringify({ error: 'Remote access is disabled (set CODEXMATE_HTTP_TOKEN)' });
|
|
688
|
+
res.writeHead(403, {
|
|
689
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
690
|
+
'Content-Length': Buffer.byteLength(body, 'utf-8')
|
|
691
|
+
});
|
|
692
|
+
res.end(body, 'utf-8');
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const headers = req && req.headers && typeof req.headers === 'object' ? req.headers : {};
|
|
696
|
+
const rawAuth = typeof headers.authorization === 'string' ? headers.authorization.trim() : '';
|
|
697
|
+
const match = rawAuth ? rawAuth.match(/^bearer\s+(.+)$/i) : null;
|
|
698
|
+
const actual = match && match[1]
|
|
699
|
+
? match[1].trim()
|
|
700
|
+
: (rawAuth ? rawAuth : (typeof headers['x-codexmate-token'] === 'string' ? String(headers['x-codexmate-token']).trim() : ''));
|
|
701
|
+
if (!actual || actual !== expected) {
|
|
702
|
+
const body = JSON.stringify({ error: 'Unauthorized' });
|
|
703
|
+
res.writeHead(401, {
|
|
704
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
705
|
+
'Content-Length': Buffer.byteLength(body, 'utf-8')
|
|
706
|
+
});
|
|
707
|
+
res.end(body, 'utf-8');
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
677
712
|
const incomingPath = parsedIncoming.pathname || '/';
|
|
678
713
|
if (incomingPath === '/health' || incomingPath === '/status') {
|
|
679
714
|
const body = JSON.stringify({
|
package/cli/claude-proxy.js
CHANGED
|
@@ -864,6 +864,30 @@ function createBuiltinClaudeProxyRuntimeController(deps = {}) {
|
|
|
864
864
|
function createBuiltinClaudeProxyServer(settings, upstream) {
|
|
865
865
|
const connections = new Set();
|
|
866
866
|
const server = http.createServer((req, res) => {
|
|
867
|
+
const remoteAddr = req && req.socket ? req.socket.remoteAddress : '';
|
|
868
|
+
const isLoopback = !remoteAddr
|
|
869
|
+
|| remoteAddr === '127.0.0.1'
|
|
870
|
+
|| remoteAddr === '::1'
|
|
871
|
+
|| remoteAddr === '::ffff:127.0.0.1';
|
|
872
|
+
if (!isLoopback) {
|
|
873
|
+
const expected = typeof process.env.CODEXMATE_HTTP_TOKEN === 'string'
|
|
874
|
+
? process.env.CODEXMATE_HTTP_TOKEN.trim()
|
|
875
|
+
: '';
|
|
876
|
+
if (!expected) {
|
|
877
|
+
writeAnthropicProxyError(res, 403, 'Remote access is disabled (set CODEXMATE_HTTP_TOKEN)', 'authentication_error');
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const headers = req && req.headers && typeof req.headers === 'object' ? req.headers : {};
|
|
881
|
+
const rawAuth = typeof headers.authorization === 'string' ? headers.authorization.trim() : '';
|
|
882
|
+
const match = rawAuth ? rawAuth.match(/^bearer\s+(.+)$/i) : null;
|
|
883
|
+
const actual = match && match[1]
|
|
884
|
+
? match[1].trim()
|
|
885
|
+
: (rawAuth ? rawAuth : (typeof headers['x-codexmate-token'] === 'string' ? String(headers['x-codexmate-token']).trim() : ''));
|
|
886
|
+
if (!actual || actual !== expected) {
|
|
887
|
+
writeAnthropicProxyError(res, 401, 'Unauthorized', 'authentication_error');
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
867
891
|
handleBuiltinClaudeProxyRequest(req, res, settings, upstream).catch((err) => {
|
|
868
892
|
if (res.headersSent) {
|
|
869
893
|
try { res.destroy(err); } catch (_) {}
|