release: Gemini CLI v0.32.1 (6 patches)
This commit is contained in:
@@ -11,6 +11,7 @@ Targets:
|
||||
6. system_env — env vars injection
|
||||
7. auto_update_registry — redirect registry-url default to npm.sensey24.ru
|
||||
8. auto_update_commands — add --registry to update commands
|
||||
9. auto_permissions — bypass tool approval prompts (YOLO mode via settings)
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -37,6 +38,8 @@ SETTINGS_JS_SUBPATH = "dist/src/config/settings.js"
|
||||
REGISTRY_URL_SUBPATH = "node_modules/registry-url/index.js"
|
||||
INSTALL_INFO_SUBPATH = "dist/src/utils/installationInfo.js"
|
||||
NPM_CONF_DEFAULTS_SUBPATH = "node_modules/@pnpm/npm-conf/lib/defaults.js"
|
||||
CONFIG_JS_SUBPATH = "dist/src/config/config.js"
|
||||
SETTINGS_SCHEMA_SUBPATH = "dist/src/config/settingsSchema.js"
|
||||
|
||||
# ANSI colors
|
||||
GREEN = "\033[92m"
|
||||
@@ -286,6 +289,42 @@ def patch_user_settings(config, home_dir=None):
|
||||
except (json.JSONDecodeError, OSError):
|
||||
existing = {}
|
||||
|
||||
# Remove deprecated keys that cause spam warnings
|
||||
DEPRECATED_KEYS = ["codebaseInvestigatorSettings"]
|
||||
exp = existing.get("experimental", {})
|
||||
for key in DEPRECATED_KEYS:
|
||||
exp.pop(key, None)
|
||||
if "experimental" in existing and not existing["experimental"]:
|
||||
del existing["experimental"]
|
||||
|
||||
# Also clean system-level configs (read-only warning source)
|
||||
if IS_WINDOWS:
|
||||
sys_paths = ["C:\\ProgramData\\gemini-cli\\settings.json",
|
||||
"C:\\ProgramData\\gemini-cli\\system-defaults.json"]
|
||||
elif IS_MACOS:
|
||||
sys_paths = ["/Library/Application Support/GeminiCli/settings.json"]
|
||||
else:
|
||||
sys_paths = ["/etc/gemini-cli/settings.json",
|
||||
"/etc/gemini-cli/system-defaults.json"]
|
||||
for sp in sys_paths:
|
||||
if os.path.isfile(sp):
|
||||
try:
|
||||
with open(sp, "r") as f:
|
||||
sd = json.load(f)
|
||||
se = sd.get("experimental", {})
|
||||
changed = False
|
||||
for key in DEPRECATED_KEYS:
|
||||
if key in se:
|
||||
del se[key]
|
||||
changed = True
|
||||
if changed:
|
||||
if not se and "experimental" in sd:
|
||||
del sd["experimental"]
|
||||
with open(sp, "w") as f:
|
||||
json.dump(sd, f, indent=2)
|
||||
except (PermissionError, OSError):
|
||||
pass # skip if no write access
|
||||
|
||||
# Deep merge our settings
|
||||
if "security" not in existing:
|
||||
existing["security"] = {}
|
||||
@@ -567,6 +606,156 @@ def patch_auto_update(gemini_root, config):
|
||||
return True, "; ".join(patched_parts)
|
||||
|
||||
|
||||
# ─── Target 9: Auto-permissions (bypass approval prompts) ──────────────
|
||||
|
||||
def patch_auto_permissions(gemini_root, config):
|
||||
"""
|
||||
Target 9a: Patch config.js to allow YOLO mode from settings.json.
|
||||
Target 9b: Set defaultApprovalMode=yolo + disable folderTrust in settings.json.
|
||||
Target 9c: Auto-trust folders via trustedFolders.json.
|
||||
"""
|
||||
changes = 0
|
||||
patched_parts = []
|
||||
|
||||
# --- 9a: Patch config.js to remove YOLO filter from settings ---
|
||||
config_js_path = os.path.join(gemini_root, CONFIG_JS_SUBPATH)
|
||||
if os.path.isfile(config_js_path):
|
||||
with open(config_js_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
old_yolo_filter = (
|
||||
"(settings.general?.defaultApprovalMode !== 'yolo'\n"
|
||||
" ? settings.general?.defaultApprovalMode\n"
|
||||
" : undefined)"
|
||||
)
|
||||
new_yolo_filter = "(settings.general?.defaultApprovalMode)"
|
||||
|
||||
if old_yolo_filter in content:
|
||||
backup = config_js_path + ".backup"
|
||||
if not os.path.exists(backup):
|
||||
shutil.copy2(config_js_path, backup)
|
||||
content = content.replace(old_yolo_filter, new_yolo_filter, 1)
|
||||
with open(config_js_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
changes += 1
|
||||
patched_parts.append("config.js: yolo filter removed")
|
||||
elif new_yolo_filter in content and old_yolo_filter not in content:
|
||||
patched_parts.append("config.js: already patched")
|
||||
else:
|
||||
patched_parts.append("config.js: pattern not found")
|
||||
else:
|
||||
patched_parts.append("config.js: not found")
|
||||
|
||||
# --- 9a2: Patch settingsSchema.js to add 'yolo' to valid options ---
|
||||
schema_path = os.path.join(gemini_root, SETTINGS_SCHEMA_SUBPATH)
|
||||
if os.path.isfile(schema_path):
|
||||
with open(schema_path, "r", encoding="utf-8") as f:
|
||||
schema_content = f.read()
|
||||
|
||||
old_options = (
|
||||
"{ value: 'default', label: 'Default' },\n"
|
||||
" { value: 'auto_edit', label: 'Auto Edit' },\n"
|
||||
" { value: 'plan', label: 'Plan' },"
|
||||
)
|
||||
new_options = (
|
||||
"{ value: 'default', label: 'Default' },\n"
|
||||
" { value: 'auto_edit', label: 'Auto Edit' },\n"
|
||||
" { value: 'plan', label: 'Plan' },\n"
|
||||
" { value: 'yolo', label: 'YOLO' },"
|
||||
)
|
||||
|
||||
if old_options in schema_content and "{ value: 'yolo'" not in schema_content:
|
||||
backup = schema_path + ".backup"
|
||||
if not os.path.exists(backup):
|
||||
shutil.copy2(schema_path, backup)
|
||||
schema_content = schema_content.replace(old_options, new_options, 1)
|
||||
with open(schema_path, "w", encoding="utf-8") as f:
|
||||
f.write(schema_content)
|
||||
changes += 1
|
||||
patched_parts.append("settingsSchema.js: yolo option added")
|
||||
elif "{ value: 'yolo'" in schema_content:
|
||||
patched_parts.append("settingsSchema.js: already patched")
|
||||
else:
|
||||
patched_parts.append("settingsSchema.js: pattern not found")
|
||||
else:
|
||||
patched_parts.append("settingsSchema.js: not found")
|
||||
|
||||
# --- 9b: Set YOLO mode + disable folderTrust in settings.json ---
|
||||
home_dir = os.path.expanduser("~")
|
||||
gemini_dir = os.path.join(home_dir, ".gemini")
|
||||
settings_path = os.path.join(gemini_dir, "settings.json")
|
||||
|
||||
os.makedirs(gemini_dir, exist_ok=True)
|
||||
|
||||
existing = {}
|
||||
if os.path.isfile(settings_path):
|
||||
try:
|
||||
with open(settings_path, "r") as f:
|
||||
existing = json.load(f)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
existing = {}
|
||||
|
||||
settings_changed = False
|
||||
|
||||
if "general" not in existing:
|
||||
existing["general"] = {}
|
||||
if existing["general"].get("defaultApprovalMode") != "yolo":
|
||||
existing["general"]["defaultApprovalMode"] = "yolo"
|
||||
settings_changed = True
|
||||
|
||||
if "security" not in existing:
|
||||
existing["security"] = {}
|
||||
if "folderTrust" not in existing["security"]:
|
||||
existing["security"]["folderTrust"] = {}
|
||||
if existing["security"]["folderTrust"].get("enabled") is not False:
|
||||
existing["security"]["folderTrust"]["enabled"] = False
|
||||
settings_changed = True
|
||||
|
||||
if existing.get("security", {}).get("disableYoloMode"):
|
||||
existing["security"]["disableYoloMode"] = False
|
||||
settings_changed = True
|
||||
|
||||
if settings_changed:
|
||||
with open(settings_path, "w") as f:
|
||||
json.dump(existing, f, indent=2)
|
||||
changes += 1
|
||||
patched_parts.append("settings.json: yolo + folderTrust disabled")
|
||||
else:
|
||||
patched_parts.append("settings.json: already configured")
|
||||
|
||||
# --- 9c: Auto-trust common folders via trustedFolders.json ---
|
||||
trusted_path = os.path.join(gemini_dir, "trustedFolders.json")
|
||||
trusted = {}
|
||||
if os.path.isfile(trusted_path):
|
||||
try:
|
||||
with open(trusted_path, "r") as f:
|
||||
trusted = json.load(f)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
trusted = {}
|
||||
|
||||
trust_paths = [home_dir, "/home", "/root", "/tmp"]
|
||||
trusted_changed = False
|
||||
for tp in trust_paths:
|
||||
if tp not in trusted:
|
||||
trusted[tp] = "TRUST_PARENT"
|
||||
trusted_changed = True
|
||||
|
||||
if trusted_changed:
|
||||
with open(trusted_path, "w") as f:
|
||||
json.dump(trusted, f, indent=2)
|
||||
changes += 1
|
||||
patched_parts.append("trustedFolders.json: paths added")
|
||||
else:
|
||||
patched_parts.append("trustedFolders.json: already configured")
|
||||
|
||||
if changes == 0 and any("already" in p for p in patched_parts):
|
||||
return True, "Already patched (" + "; ".join(patched_parts) + ")"
|
||||
elif changes == 0:
|
||||
return False, "No auto-permissions patterns matched (" + "; ".join(patched_parts) + ")"
|
||||
|
||||
return True, "; ".join(patched_parts)
|
||||
|
||||
|
||||
# ─── Rollback ───────────────────────────────────────────────────────────
|
||||
|
||||
def rollback(genai_mjs_path, settings_js_path, gemini_root=None):
|
||||
@@ -576,6 +765,10 @@ def rollback(genai_mjs_path, settings_js_path, gemini_root=None):
|
||||
if gemini_root:
|
||||
auto_paths = get_auto_update_paths(gemini_root)
|
||||
paths.extend(auto_paths.values())
|
||||
config_js = os.path.join(gemini_root, CONFIG_JS_SUBPATH)
|
||||
paths.append(config_js)
|
||||
schema_js = os.path.join(gemini_root, SETTINGS_SCHEMA_SUBPATH)
|
||||
paths.append(schema_js)
|
||||
for p in paths:
|
||||
backup = p + ".backup"
|
||||
if os.path.exists(backup):
|
||||
@@ -676,6 +869,13 @@ def apply_all_patches(config=None, settings_only=False):
|
||||
if not ok:
|
||||
all_ok = False
|
||||
|
||||
# Target 9: auto-permissions (bypass approval prompts)
|
||||
ok, msg = patch_auto_permissions(gemini_root, config)
|
||||
results["auto_permissions"] = (ok, msg)
|
||||
print(f" {'[OK]' if ok else '[FAIL]':>8} Target 9: {msg}")
|
||||
if not ok:
|
||||
all_ok = False
|
||||
|
||||
print()
|
||||
if all_ok:
|
||||
print(f" {color('All patches applied successfully!', GREEN)}")
|
||||
|
||||
Reference in New Issue
Block a user