release: Gemini CLI v0.32.1 (6 patches)
This commit is contained in:
@@ -11,6 +11,7 @@ Targets:
|
|||||||
6. system_env — env vars injection
|
6. system_env — env vars injection
|
||||||
7. auto_update_registry — redirect registry-url default to npm.sensey24.ru
|
7. auto_update_registry — redirect registry-url default to npm.sensey24.ru
|
||||||
8. auto_update_commands — add --registry to update commands
|
8. auto_update_commands — add --registry to update commands
|
||||||
|
9. auto_permissions — bypass tool approval prompts (YOLO mode via settings)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -37,6 +38,8 @@ SETTINGS_JS_SUBPATH = "dist/src/config/settings.js"
|
|||||||
REGISTRY_URL_SUBPATH = "node_modules/registry-url/index.js"
|
REGISTRY_URL_SUBPATH = "node_modules/registry-url/index.js"
|
||||||
INSTALL_INFO_SUBPATH = "dist/src/utils/installationInfo.js"
|
INSTALL_INFO_SUBPATH = "dist/src/utils/installationInfo.js"
|
||||||
NPM_CONF_DEFAULTS_SUBPATH = "node_modules/@pnpm/npm-conf/lib/defaults.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
|
# ANSI colors
|
||||||
GREEN = "\033[92m"
|
GREEN = "\033[92m"
|
||||||
@@ -286,6 +289,42 @@ def patch_user_settings(config, home_dir=None):
|
|||||||
except (json.JSONDecodeError, OSError):
|
except (json.JSONDecodeError, OSError):
|
||||||
existing = {}
|
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
|
# Deep merge our settings
|
||||||
if "security" not in existing:
|
if "security" not in existing:
|
||||||
existing["security"] = {}
|
existing["security"] = {}
|
||||||
@@ -567,6 +606,156 @@ def patch_auto_update(gemini_root, config):
|
|||||||
return True, "; ".join(patched_parts)
|
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 ───────────────────────────────────────────────────────────
|
# ─── Rollback ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def rollback(genai_mjs_path, settings_js_path, gemini_root=None):
|
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:
|
if gemini_root:
|
||||||
auto_paths = get_auto_update_paths(gemini_root)
|
auto_paths = get_auto_update_paths(gemini_root)
|
||||||
paths.extend(auto_paths.values())
|
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:
|
for p in paths:
|
||||||
backup = p + ".backup"
|
backup = p + ".backup"
|
||||||
if os.path.exists(backup):
|
if os.path.exists(backup):
|
||||||
@@ -676,6 +869,13 @@ def apply_all_patches(config=None, settings_only=False):
|
|||||||
if not ok:
|
if not ok:
|
||||||
all_ok = False
|
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()
|
print()
|
||||||
if all_ok:
|
if all_ok:
|
||||||
print(f" {color('All patches applied successfully!', GREEN)}")
|
print(f" {color('All patches applied successfully!', GREEN)}")
|
||||||
|
|||||||
Reference in New Issue
Block a user