mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-21 07:45:52 +00:00
之前 bypassPermissions 需要本地显式 opt-in(ACP_PERMISSION_MODE 环境变量、 CLAUDE_CODE_ACP_ALLOW_BYPASS_PERMISSIONS 环境变量、或 settings.permissions.defaultMode) 才会出现在 modes 列表里 —— 标准客户端看不到这个 mode,永远没法切换。 去掉 opt-in 后,只要进程级允许(非 root 或 IS_SANDBOX=1)就显示。 - permissionMode: isAcpBypassPermissionModeAvailable 只保留进程级检查,删除 isAcpBypassLocallyEnabled / isSettingsBypassPermissionMode / isTruthyEnv 等 只服务于 opt-in 的辅助函数 - createSessionMethod: 调用方去掉 settingsMode 参数 - agent.test: 反转所有依赖 "bypass 需要 opt-in" 的断言 Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
103 lines
3.0 KiB
TypeScript
103 lines
3.0 KiB
TypeScript
import type { PermissionMode } from '../../../types/permissions.js'
|
|
import { resolvePermissionMode } from '../utils.js'
|
|
|
|
export const permissionModeIds: readonly PermissionMode[] = [
|
|
'auto',
|
|
'default',
|
|
'acceptEdits',
|
|
'bypassPermissions',
|
|
'dontAsk',
|
|
'plan',
|
|
]
|
|
|
|
export function isPermissionMode(modeId: string): modeId is PermissionMode {
|
|
return (permissionModeIds as readonly string[]).includes(modeId)
|
|
}
|
|
|
|
export function resolveSessionPermissionMode(
|
|
metaMode: unknown,
|
|
hasMetaMode: boolean,
|
|
settingsMode: unknown,
|
|
): PermissionMode {
|
|
if (hasMetaMode) {
|
|
const metaResolved = resolveRequiredPermissionMode(
|
|
metaMode,
|
|
'_meta.permissionMode',
|
|
)
|
|
if (
|
|
metaResolved === 'bypassPermissions' &&
|
|
!isAcpBypassPermissionModeAvailable()
|
|
) {
|
|
throw new Error(
|
|
'Mode not available: bypassPermissions cannot run as root (start the agent as a non-root user, or set IS_SANDBOX=1).',
|
|
)
|
|
}
|
|
|
|
return metaResolved
|
|
}
|
|
|
|
const settingsResolved = resolveConfiguredPermissionMode(settingsMode)
|
|
return settingsResolved ?? 'default'
|
|
}
|
|
|
|
function resolveRequiredPermissionMode(
|
|
mode: unknown,
|
|
source: string,
|
|
): PermissionMode {
|
|
if (mode === undefined || mode === null) {
|
|
throw new Error(`Invalid ${source}: expected a string.`)
|
|
}
|
|
|
|
return resolvePermissionMode(mode, source) as PermissionMode
|
|
}
|
|
|
|
function resolveConfiguredPermissionMode(
|
|
mode: unknown,
|
|
): PermissionMode | undefined {
|
|
if (mode === undefined || mode === null) return undefined
|
|
|
|
try {
|
|
return resolvePermissionMode(
|
|
mode,
|
|
'permissions.defaultMode',
|
|
) as PermissionMode
|
|
} catch (err: unknown) {
|
|
const reason = err instanceof Error ? err.message : String(err)
|
|
console.error(
|
|
'[ACP] Invalid permissions.defaultMode, using default:',
|
|
reason,
|
|
)
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
export function hasOwnField(
|
|
value: Record<string, unknown> | null | undefined,
|
|
key: string,
|
|
): boolean {
|
|
return !!value && Object.hasOwn(value, key)
|
|
}
|
|
|
|
/**
|
|
* Whether bypassPermissions is selectable by ACP clients.
|
|
*
|
|
* The previous implementation required a local opt-in (ACP_PERMISSION_MODE env var,
|
|
* CLAUDE_CODE_ACP_ALLOW_BYPASS_PERMISSIONS env var, or settings.permissions.defaultMode).
|
|
* That gate made the mode invisible to standard clients unless the operator already
|
|
* pre-configured it — defeating the point of exposing it through the ACP mode list.
|
|
*
|
|
* The only remaining guard is the process-level one: bypass must not silently run
|
|
* as root (where every skipped permission check is a privilege boundary crossed),
|
|
* unless explicitly marked as a sandbox.
|
|
*/
|
|
export function isAcpBypassPermissionModeAvailable(): boolean {
|
|
return isProcessBypassPermissionModeAvailable()
|
|
}
|
|
|
|
function isProcessBypassPermissionModeAvailable(): boolean {
|
|
if (process.env.IS_SANDBOX) return true
|
|
if (typeof process.geteuid === 'function') return process.geteuid() !== 0
|
|
if (typeof process.getuid === 'function') return process.getuid() !== 0
|
|
return true
|
|
}
|