fix(desktop): stop backend writing into the .app bundle

This commit is contained in:
zhayujie
2026-07-08 01:59:08 +08:00
parent efbabfcace
commit ca404aeb24
3 changed files with 31 additions and 4 deletions

2
.gitignore vendored
View File

@@ -60,3 +60,5 @@ desktop/build/*
# Icon authoring scratch dir: intermediate assets used to produce the final # Icon authoring scratch dir: intermediate assets used to produce the final
# icons. Only the finished icons under desktop/resources/ should be committed. # icons. Only the finished icons under desktop/resources/ should be committed.
desktop/resources/.icon-work/ desktop/resources/.icon-work/
.wrangler/

View File

@@ -688,8 +688,11 @@ class CowCliPlugin(Plugin):
if resolved: if resolved:
updates["bot_type"] = resolved updates["bot_type"] = resolved
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Write to the data dir (~/.cow in desktop, project root otherwise), not
config_path = os.path.join(project_root, "config.json") # __file__-relative — the latter lives inside the read-only .app bundle
# when frozen and writing there breaks the macOS code-signature seal.
from config import get_data_root
config_path = os.path.join(get_data_root(), "config.json")
try: try:
with open(config_path, "r", encoding="utf-8") as f: with open(config_path, "r", encoding="utf-8") as f:
file_config = _json.load(f) file_config = _json.load(f)
@@ -1630,8 +1633,9 @@ class CowCliPlugin(Plugin):
conf()["knowledge"] = enabled conf()["knowledge"] = enabled
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Data dir (~/.cow in desktop), not __file__-relative — see _config_set.
config_path = os.path.join(project_root, "config.json") from config import get_data_root
config_path = os.path.join(get_data_root(), "config.json")
try: try:
with open(config_path, "r", encoding="utf-8") as f: with open(config_path, "r", encoding="utf-8") as f:
file_config = _json.load(f) file_config = _json.load(f)

View File

@@ -202,6 +202,26 @@ class PluginManager:
return True return True
return False return False
# IM-channel-only plugins that make no sense in the single-user desktop app
# and, worse, write config.json into their bundle dir on init — which breaks
# the macOS code-signature seal of the packaged .app. cow_cli stays enabled
# so desktop chat commands (/status, /help, ...) keep working.
DESKTOP_DISABLED_PLUGINS = {
"GODCMD", "KEYWORD", "BANWORDS", "ROLE", "DUNGEON", "HELLO", "FINISH",
}
def _apply_desktop_plugin_denylist(self):
"""In desktop mode, force-disable IM-only plugins regardless of what
plugins.json (bundle default or user copy) says. Runs after scan (which
syncs enabled from plugins.json) and before activate, so denied plugins
are never instantiated and never write into the read-only bundle."""
if os.environ.get("COW_DESKTOP") != "1":
return
for name in list(self.plugins.keys()):
if name.upper() in self.DESKTOP_DISABLED_PLUGINS and self.plugins[name].enabled:
self.plugins[name].enabled = False
logger.info("[desktop] plugin %s disabled (not needed in desktop client)" % name)
def load_plugins(self): def load_plugins(self):
self.load_config() self.load_config()
self.scan_plugins() self.scan_plugins()
@@ -212,6 +232,7 @@ class PluginManager:
for name, plugin in pconf["plugins"].items(): for name, plugin in pconf["plugins"].items():
if name.upper() not in self.plugins: if name.upper() not in self.plugins:
logger.error("Plugin %s not found, but found in plugins.json" % name) logger.error("Plugin %s not found, but found in plugins.json" % name)
self._apply_desktop_plugin_denylist()
self.activate_plugins() self.activate_plugins()
def emit_event(self, e_context: EventContext, *args, **kwargs): def emit_event(self, e_context: EventContext, *args, **kwargs):