Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 10 additions & 3 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,8 @@ function isPlainObject(value) {
return !!value && typeof value === 'object' && !Array.isArray(value);
}

const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode']);
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false });
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode', 'openclaw']);
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false, openclaw: false });
let toolConfigWriteGuardDepth = 0;

function enterToolConfigWriteGuard() {
Expand Down Expand Up @@ -938,7 +938,8 @@ function normalizeToolConfigPermissions(value) {
codex: source.codex === true,
claude: source.claude === true,
opencode: source.opencode === true,
kilocode: source.kilocode === true
kilocode: source.kilocode === true,
openclaw: source.openclaw === true
};
}

Expand Down Expand Up @@ -1197,10 +1198,16 @@ function getApiToolConfigWriteTarget(action) {
'apply-kilocode-config',
'start-kilocode'
]);
const openclawWriteActions = new Set([
'apply-openclaw-config',
'apply-openclaw-agents-file',
'apply-openclaw-workspace-file'
]);
if (codexWriteActions.has(name)) return 'codex';
if (claudeWriteActions.has(name)) return 'claude';
if (opencodeWriteActions.has(name)) return 'opencode';
if (kilocodeWriteActions.has(name)) return 'kilocode';
if (openclawWriteActions.has(name)) return 'openclaw';
return '';
}

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codexmate",
"version": "0.1.3",
"version": "0.1.4",
"description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
"main": "cli.js",
"bin": {
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e/test-openclaw.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ module.exports = async function testOpenclaw(ctx) {
assert(openclawReadEmpty.authProfilesByProvider['openai-codex'].editable === true, 'get-openclaw-config missing editable auth profile flag');

// ========== Apply OpenClaw Config Tests ==========
const enableOpenclawWrites = await api('set-tool-config-permission', { target: 'openclaw', allowWrite: true });
assert(enableOpenclawWrites.success === true, 'set-tool-config-permission(openclaw) should succeed');

const openclawInvalid = await api('apply-openclaw-config', { content: '', lineEnding: '\n' });
assert(openclawInvalid.success === false, 'apply-openclaw-config should reject empty content');

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/config-tabs-ui.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ test('config template keeps expected config tabs in top and side navigation', ()
);
assert.match(
html,
/:class="\['card', \{ active: currentOpenclawConfig === name \}\]"[\s\S]*@click="applyOpenclawConfig\(name\)"[\s\S]*@keydown\.enter\.self\.prevent="applyOpenclawConfig\(name\)"[\s\S]*@keydown\.space\.self\.prevent="applyOpenclawConfig\(name\)"[\s\S]*tabindex="0"[\s\S]*role="button"[\s\S]*:aria-current="currentOpenclawConfig === name \? 'true' : null"/
/:class="\['card', \{ active: currentOpenclawConfig === name, disabled: !isToolConfigWriteAllowed\('openclaw'\) \}\]"[\s\S]*@click="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*@keydown\.enter\.self\.prevent="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*@keydown\.space\.self\.prevent="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*:tabindex="isToolConfigWriteAllowed\('openclaw'\) \? 0 : -1"[\s\S]*role="button"[\s\S]*:aria-disabled="!isToolConfigWriteAllowed\('openclaw'\) \? 'true' : null"[\s\S]*:aria-current="currentOpenclawConfig === name \? 'true' : null"/
);
assert.match(html, /class="session-item-copy session-item-pin"/);
assert.doesNotMatch(sessionsPanel, /sessionsViewMode/);
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/opencode-config-ui.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ test('opencode backend actions are permission guarded in cli source', () => {
const cli = readProjectFile('cli.js');
assert.match(cli, /const OPENCODE_GLOBAL_JSONC_CONFIG_FILE = path\.join\(OPENCODE_CONFIG_DIR, 'opencode\.jsonc'\)/);
assert.match(cli, /const OPENCODE_CONFIG_ENV_FILE = process\.env\.OPENCODE_CONFIG/);
assert.match(cli, /const TOOL_CONFIG_PERMISSION_TARGETS = new Set\(\['codex', 'claude', 'opencode', 'kilocode'\]\)/);
assert.match(cli, /const TOOL_CONFIG_PERMISSION_TARGETS = new Set\(\['codex', 'claude', 'opencode', 'kilocode', 'openclaw'\]\)/);
assert.match(cli, /function applyOpencodeConfigRaw\(params = \{\}\) \{[\s\S]*assertToolConfigWriteAllowed\('opencode'\)/);
assert.match(cli, /function updateOpencodeSelection\(params = \{\}\) \{[\s\S]*assertToolConfigWriteAllowed\('opencode'\)/);
assert.match(cli, /case 'get-opencode-config':/);
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/web-ui-behavior-parity.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
'showPromptTemplateVarModal',
'brandHovered',
'promptsHint',
'currentInstalledCommandCards',
];
allowedExtraCurrentKeys.push(
'lang',
Expand Down Expand Up @@ -991,7 +992,8 @@ test('captured bundled app skeleton only exposes expected data key drift versus
'localProxyUpstreamOptions',
'currentClaudeHaikuModel',
'currentClaudeSonnetModel',
'currentClaudeOpusModel'
'currentClaudeOpusModel',
'currentInstalledCommandCards'
];
if (parityAgainstHead) {
const allowedExtraComputedKeySet = new Set(allowedExtraCurrentComputedKeys);
Expand Down
5 changes: 3 additions & 2 deletions tests/unit/web-ui-preferences.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,15 @@ test('web UI preference debounce preserves nested pending overrides', async () =
test('web UI preferences preserve KiloCode write permission on reload', () => {
const context = createContext([], createMemoryStorage());
context.applyWebUiPreferences({
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: true }
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: true, openclaw: false }
}, { applyNavigation: false });

assert.deepStrictEqual(context.toolConfigPermissions, {
codex: false,
claude: false,
opencode: false,
kilocode: true
kilocode: true,
openclaw: false
});
});

Expand Down
4 changes: 2 additions & 2 deletions web-ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,8 @@ document.addEventListener('DOMContentLoaded', () => {
providerCacheError: '',
providerCacheRequestSeq: 0,
settingsTab: 'general',
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false },
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false },
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false },
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false },
sessionTrashEnabled: true,
sessionTrashItems: [],
sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
Expand Down
4 changes: 0 additions & 4 deletions web-ui/modules/app.computed.dashboard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,6 @@ export function createDashboardComputed() {
};
});
},
currentInstalledCommandCards() {
const targets = Array.isArray(this.installTargetCards) ? this.installTargetCards : [];
return targets.filter((target) => target && target.installed === true);
},
installRegistryPreview() {
return this.resolveInstallRegistryUrl(this.installRegistryPreset, this.installRegistryCustom);
},
Expand Down
3 changes: 2 additions & 1 deletion web-ui/modules/app.methods.startup-claude.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ export function createStartupClaudeMethods(options = {}) {
codex: statusRes.toolConfigPermissions.codex === true,
claude: statusRes.toolConfigPermissions.claude === true,
opencode: statusRes.toolConfigPermissions.opencode === true,
kilocode: statusRes.toolConfigPermissions.kilocode === true
kilocode: statusRes.toolConfigPermissions.kilocode === true,
openclaw: statusRes.toolConfigPermissions.openclaw === true
};
}
this.providersList = listRes.providers;
Expand Down
5 changes: 3 additions & 2 deletions web-ui/modules/app.methods.tool-config-permissions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export function createToolConfigPermissionMethods(options = {}) {

function normalizeTarget(value) {
const target = typeof value === 'string' ? value.trim().toLowerCase() : '';
return target === 'codex' || target === 'claude' || target === 'opencode' || target === 'kilocode' ? target : '';
return target === 'codex' || target === 'claude' || target === 'opencode' || target === 'kilocode' || target === 'openclaw' ? target : '';
}

function normalizePermissions(value) {
Expand All @@ -12,7 +12,8 @@ export function createToolConfigPermissionMethods(options = {}) {
codex: source.codex === true,
claude: source.claude === true,
opencode: source.opencode === true,
kilocode: source.kilocode === true
kilocode: source.kilocode === true,
openclaw: source.openclaw === true
};
}

Expand Down
3 changes: 2 additions & 1 deletion web-ui/modules/app.methods.web-ui-preferences.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ export function createWebUiPreferencesMethods(options = {}) {
codex: source.toolConfigPermissions.codex === true,
claude: source.toolConfigPermissions.claude === true,
opencode: source.toolConfigPermissions.opencode === true,
kilocode: source.toolConfigPermissions.kilocode === true
kilocode: source.toolConfigPermissions.kilocode === true,
openclaw: source.toolConfigPermissions.openclaw === true
};
}
if (Array.isArray(source.deletedClaudeSettingsImports)) {
Expand Down
13 changes: 5 additions & 8 deletions web-ui/modules/i18n/locales/en.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -619,23 +619,15 @@ const en = Object.freeze({

// Docs panel
'docs.title': 'CLI Install',
'docs.subtitle': 'Install commands for Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI.',
'docs.section.commands': 'Commands',
'docs.section.commandsNote': 'Copy and run directly.',
'docs.installed.title': 'Currently installed commands',
'docs.installed.count': '{count} installed',
'docs.installed.empty': 'No installed commands detected',
'docs.section.faq': 'FAQ',
'docs.section.faqNote': 'Common issues and tips.',
'docs.command.aria': '{name} command',
'docs.registryHintPrefix': 'Command will append:',
'docs.registryHintCustom': 'Enter a full URL (http/https) to append as registry.',
'docs.registry.tencent': 'Tencent Cloud',
'docs.meta.bin': 'bin: {bin}',
'docs.termuxLabel': 'Termux',
'docs.termuxAria': 'Termux Codex CLI command',
'docs.rule.1': 'Commands depend on package manager, registry and action.',
'docs.rule.2': 'Custom registry is used for install/update only.',
'docs.tip.win.1': 'If PowerShell reports permission errors (EACCES/EPERM), run the install command as Administrator.',
'docs.tip.win.2': 'If the command is still not found after install, reopen the terminal and run: where codex / where claude / where gemini / where codebuddy.',
'docs.tip.win.3': 'If your network blocks npm, try switching registry presets (npmmirror / Tencent / Custom).',
Expand Down Expand Up @@ -1544,6 +1536,11 @@ const en = Object.freeze({
'toolConfig.kilocode.lockedTitle': 'Read-only mode',
'toolConfig.kilocode.lockedDesc': 'Enable write access to auto-sync config or launch KiloCode.',
'toolConfig.kilocode.confirmMessage': 'When enabled, actions in the KiloCode tab can write ~/.config/kilo/kilo.jsonc or an existing kilo.json, and can launch local KiloCode.',
'toolConfig.openclaw.title': 'OpenClaw config writes',
'toolConfig.openclaw.desc': 'Read-only by default; enable to apply OpenClaw configs and write AGENTS.md and other workspace files.',
'toolConfig.openclaw.confirmMessage': 'When enabled, actions in the OpenClaw tab can write openclaw.json and OpenClaw workspace files (AGENTS.md, SOUL.md, etc.).',
'toolConfig.openclaw.lockedTitle': 'OpenClaw is read-only',
'toolConfig.openclaw.lockedDesc': 'OpenClaw config is not written here. To apply configs or edit workspace files, enable write access on this tab first.',
'kilocode.providerModel.title': 'KiloCode provider / model',
'kilocode.connection.title': 'KiloCode connection',
'kilocode.targetFile': 'Active KiloCode config: {path} · {status}',
Expand Down
13 changes: 5 additions & 8 deletions web-ui/modules/i18n/locales/ja.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -621,23 +621,15 @@ const ja = Object.freeze({

// Docs panel
'docs.title': 'CLI インストールドキュメント',
'docs.subtitle': 'Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI コマンドを表示。',
'docs.section.commands': 'インストールコマンド',
'docs.section.commandsNote': 'コマンドは直接コピーできます。',
'docs.installed.title': '現在インストール済みのコマンド',
'docs.installed.count': '{count} 個インストール済み',
'docs.installed.empty': 'インストール済みコマンドは検出されていません',
'docs.section.faq': 'よくある質問',
'docs.section.faqNote': 'よくある質問は以下をご覧ください。',
'docs.command.aria': '{name} コマンド',
'docs.registryHintPrefix': 'コマンドに付加:',
'docs.registryHintCustom': '完全な URL(http/https 含む)を入力してください。コマンドに自動付加されます。',
'docs.registry.tencent': 'Tencent Cloud',
'docs.meta.bin': 'bin: {bin}',
'docs.termuxLabel': 'Termux',
'docs.termuxAria': 'Termux Codex CLI command',
'docs.rule.1': 'コマンドは現在のパッケージマネージャー、ミラー、操作に基づいて生成されます。',
'docs.rule.2': 'カスタムミラーはインストールとアップグレードにのみ使用されます。',
'docs.tip.win.1': 'PowerShell で権限不足(EACCES/EPERM)エラーが発生した場合は、管理者としてインストールコマンドを実行してください。',
'docs.tip.win.2': 'インストール後にコマンドが見つからない場合は、ターミナルを再起動して実行:where codex / where claude / where gemini / where codebuddy。',
'docs.tip.win.3': '企業ネットワークで制限がある場合は、ミラーソース(npmmirror / Tencent Cloud / カスタム)を切り替えてください。',
Expand Down Expand Up @@ -1531,6 +1523,11 @@ const ja = Object.freeze({
'toolConfig.kilocode.lockedTitle': '読み取り専用モード',
'toolConfig.kilocode.lockedDesc': '書き込み権限を有効化すると、設定の自動同期または KiloCode の起動ができます。',
'toolConfig.kilocode.confirmMessage': '有効化すると、KiloCode タブ内の操作が ~/.config/kilo/kilo.jsonc または既存の kilo.json に書き込み、ローカル KiloCode を起動できます。',
'toolConfig.openclaw.title': 'OpenClaw 設定の書き込み',
'toolConfig.openclaw.desc': '既定は読み取り専用。有効化すると OpenClaw 設定を適用し、AGENTS.md などのワークスペースファイルを書き込みできます。',
'toolConfig.openclaw.confirmMessage': '有効化すると、OpenClaw タブ内の操作が openclaw.json およびワークスペースファイル(AGENTS.md、SOUL.md など)に書き込みできます。',
'toolConfig.openclaw.lockedTitle': 'OpenClaw は読み取り専用です',
'toolConfig.openclaw.lockedDesc': 'ここでは OpenClaw 設定を書き込みません。設定を適用またはワークスペースファイルを編集するには、このタブの書き込みスイッチを有効化してください。',
'kilocode.providerModel.title': 'KiloCode プロバイダー / モデル',
'kilocode.connection.title': 'KiloCode 接続',
'kilocode.targetFile': '有効な KiloCode 設定:{path} · {status}',
Expand Down
13 changes: 5 additions & 8 deletions web-ui/modules/i18n/locales/vi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -826,23 +826,15 @@ const vi = Object.freeze({
'modal.openclaw.quick.optionsHint': 'Khi tắt ghi đè, chỉ điền các trường còn thiếu.',
'modal.openclaw.quick.writeToEditor': 'Ghi vào editor',
'docs.title': 'Cài CLI',
'docs.subtitle': 'Lệnh cài đặt cho Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI.',
'docs.section.commands': 'Lệnh',
'docs.section.commandsNote': 'Sao chép và chạy trực tiếp.',
'docs.installed.title': 'Lệnh hiện đã cài đặt',
'docs.installed.count': 'Đã cài {count}',
'docs.installed.empty': 'Chưa phát hiện lệnh đã cài',
'docs.section.faq': 'FAQ',
'docs.section.faqNote': 'Vấn đề phổ biến và mẹo.',
'docs.command.aria': 'Lệnh {name}',
'docs.registryHintPrefix': 'Lệnh sẽ thêm:',
'docs.registryHintCustom': 'Nhập URL đầy đủ (http/https) để thêm làm registry.',
'docs.registry.tencent': 'Tencent Cloud',
'docs.meta.bin': 'bin: {bin}',
'docs.termuxLabel': 'Termux',
'docs.termuxAria': 'Lệnh Codex CLI cho Termux',
'docs.rule.1': 'Lệnh phụ thuộc vào trình quản lý gói, registry và hành động.',
'docs.rule.2': 'Registry tùy chỉnh chỉ dùng cho cài đặt/cập nhật.',
'docs.tip.win.1': 'Nếu PowerShell báo lỗi quyền (EACCES/EPERM), chạy lệnh cài đặt với quyền Administrator.',
'docs.tip.win.2': 'Nếu lệnh không tìm thấy sau cài đặt, mở lại terminal và chạy: where codex / where claude.',
'docs.tip.win.3': 'Nếu mạng chặn npm, thử chuyển registry (npmmirror / Tencent / Tùy chỉnh).',
Expand Down Expand Up @@ -1516,6 +1508,11 @@ const vi = Object.freeze({
'toolConfig.kilocode.lockedTitle': 'Chế độ chỉ đọc',
'toolConfig.kilocode.lockedDesc': 'Bật quyền ghi để tự động đồng bộ cấu hình hoặc khởi chạy KiloCode.',
'toolConfig.kilocode.confirmMessage': 'Khi bật, các thao tác trong tab KiloCode có thể ghi ~/.config/kilo/kilo.jsonc hoặc kilo.json hiện có, và có thể khởi chạy KiloCode cục bộ.',
'toolConfig.openclaw.title': 'Ghi cấu hình OpenClaw',
'toolConfig.openclaw.desc': 'Mặc định chỉ đọc; bật để áp dụng cấu hình OpenClaw và ghi AGENTS.md cùng các tệp workspace khác.',
'toolConfig.openclaw.confirmMessage': 'Khi bật, các thao tác trong tab OpenClaw có thể ghi openclaw.json và các tệp workspace (AGENTS.md, SOUL.md, v.v.).',
'toolConfig.openclaw.lockedTitle': 'OpenClaw đang ở chế độ chỉ đọc',
'toolConfig.openclaw.lockedDesc': 'Không ghi cấu hình OpenClaw ở đây. Để áp dụng cấu hình hoặc sửa tệp workspace, hãy bật quyền ghi trên tab này trước.',
'kilocode.providerModel.title': 'Nhà cung cấp / mô hình KiloCode',
'kilocode.connection.title': 'Kết nối KiloCode',
'kilocode.targetFile': 'Cấu hình KiloCode đang dùng: {path} · {status}',
Expand Down
Loading
Loading