Commit Graph
45 Commits
Author SHA1 Message Date
zhayujieandcowagent 23afdd100d fix: trim batch comments and condense the batch tests
Neutralize remote-side wording in comments and drop the test that depended
on an optional client module.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-10-07 21:10:13 +08:00
c020627 284f1e139a fix(plugins): decide what counts as a new plugin by name, not by class
`scan_plugins` reloads every plugin it has already imported, and reloading
re-runs the plugin's `@PluginManager().register(...)` decorator, which builds
a brand new class object and writes it into the registry. The scan then
decided which plugins were new with `list(set(news) - set(raws))` over the
classes themselves -- an identity comparison that the reload guarantees to
differ.

So every scan reported every plugin it had just reloaded as newly installed.
`#scanp` (`plugins/godcmd/godcmd.py:389`) hands that list straight to the user
as "发现新插件", so asking what had just been installed produced an answer
naming the entire plugins directory, and the same thing happened on every
subsequent scan.

Compare the registry keys instead, which is what identity means here:
`register()` writes `name.upper()` into the registry, and a reload writes the
same key it wrote before.
2026-10-07 13:29:29 +08:00
zhayujieandcowagent f069da0472 fix: trim batch comments and tests, drop the unused think-tag flag
Reuse providers.mask_key for channel secrets and compact the new tests.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-10-05 14:52:00 +08:00
c020627 ef3a07cdb3 fix(plugins): keep a plugin's configuration when reloading it
`reload_plugin` dropped the plugin's entry from the in-memory config on the way
in, so the fresh instance would re-read its settings. That only ever worked for
a plugin carrying its own `config.json` in its bundle: `Plugin.load_config`
falls back to that file and never looks at `plugins/config.json`, which is
where a plugin configured at runtime actually lives. For such a plugin the
entry was simply gone and nothing put it back.

Measured through the real `PluginManager` and the real `Godcmd`:

    before reload : {'password': 's3cret', 'admin_users': ['alice']}
    reload_plugin returned: True
      live instance : password='' admin_users=[] temp='9623'

`#reloadp Godcmd` answered "插件配置已重载" while clearing the admin password
and the admin list, and logged a random four-digit temporary password in their
place - the admin has locked themselves out until the process restarts. The
removal also ran before the membership check, so `#reloadp <a name that is not
loaded>` erased that plugin's settings before discovering there was nothing to
reload.

Reloading a plugin's code is not the operation that reloads its configuration;
`#reconf` is, and it is what a user reaches for after editing config.json. So
the reload keeps the configuration it already has.

`remove_plugin_config` had no other caller, so its import goes with it. The
function itself is left alone in `config.py` in case something outside this
repository imports it.
2026-10-04 02:38:25 +08:00
zhayujieandcowagent abd6b3ee4f fix: trim redundant comments after batch-4 merges
Co-authored-by: cowagent <cow@cowagent.ai>
2026-10-01 15:54:03 +08:00
Aniruddha Adak 54e623f226 fix(plugins): do not register the same listener twice when a plugin is re-enabled 2026-09-30 12:30:02 +05:30
Lengshuang 4ace28048c fix(plugins): normalize damaged entries instead of losing every plugin
load_config sorted plugins.json with a bare v["priority"], and scan_plugins
reads ["enabled"] the same way, so one hand-edited entry that lost or
mis-typed a key raised out of the plugin load -- the failure the surrounding
guards for a truncated or shapeless store were written to prevent.
2026-09-29 19:38:45 +08:00
zhayujieandcowagent 9340abe57c refactor: share one atomic file write helper across stores
Replace the per-module sibling-file-and-rename copies with
common/atomic_write.py, which uses a unique dot-prefixed temp name,
fsyncs, and keeps the target's permissions.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-29 14:51:02 +08:00
c020627 d98bddd885 fix(plugins): write plugins.json atomically, and survive a damaged one
save_config wrote straight into plugins.json, so anything that failed partway
through -- a full disk, an update interrupted mid-copy -- left a truncated
document where the plugin states used to be. load_config then read it with a
bare json.load and no guard:

    with open(cfg_path, "r", encoding="utf-8") as f:
        pconf = json.load(f)
        pconf["plugins"] = SortedDict(...)

A truncated file raises JSONDecodeError there; a file that still parses but lost
its "plugins" key raises KeyError on the very next line. Either one escapes
load_plugins, which app.py:252 calls on first start:

    if first_start:
        if DESKTOP_MODE:
            threading.Thread(target=PluginManager().load_plugins, daemon=True).start()
        else:
            PluginManager().load_plugins()

On a source deployment that is a failed start. On the desktop client the same
raise happens on a daemon thread, so it is never caught and never logged: the
plugin system simply never comes up -- /status, /help and the rest stop
answering -- with nothing in the log to say why. And the store is not obscure:
enabled and priority live only there, and six call sites write it (enable,
disable, set priority, install, uninstall, first scan), so a routine click in
the console is enough to put it at risk.

save_config now builds the store in a sibling .tmp file and os.replace()s it in
-- the shape task_store.py:87 (_write_atomic), recipient_store.py:67
(_save_unlocked), channel/weixin/weixin_channel.py:80 (_save_credentials) and
channel/web/api/agents.py:422 already use. A failure removes the temp file and
re-raises, leaving the previous store exactly as it was.

load_config keeps the fallback it already documents ("prefer the writable copy,
fall back to the one shipped in the resource dir") but stops choosing between
the two with os.path.exists. A damaged file passes that check, which is exactly
the case the fallback exists for, so the fallback was unreachable in the one
situation that needed it. Both copies now go through _read_plugin_store, which
answers None for "not there" and for "there but unusable": a bad writable copy
falls through to the shipped default, and when neither is usable the load starts
from an empty store instead of aborting. Whenever the writable copy was not the
source, a usable one is written back so the next start does not fall back again.

_read_plugin_store also rejects a store whose "plugins" is missing or not a
mapping -- valid JSON is not the same as a valid store, and that shape is what
turned into the KeyError above.

Also drop the unused conf import in this module: the lint gate only inspects
changed files, so touching this file would otherwise turn the build red.

Covered by tests/test_plugin_store_atomic_write.py: a truncated store and one
that parsed without its "plugins" mapping must both load, a damaged writable
copy must fall back to the shipped default and be repaired, a save that dies
partway must leave the previous file byte-identical and no .tmp behind, and a
healthy store must load without being rewritten.
2026-09-23 18:29:15 +08:00
zhayujie ca404aeb24 fix(desktop): stop backend writing into the .app bundle 2026-07-08 01:59:08 +08:00
zhayujie 538281da51 feat(desktop): support plugin commands 2026-06-27 12:19:12 +08:00
zhayujie 8f608223d7 perf(feishu): tune streaming render speed 2026-05-05 14:53:30 +08:00
zhayujie 46fa07e4a9 feat: optimize agent configuration and memory 2026-02-02 11:48:53 +08:00
Saboteur7 16324e7283 Merge pull request #2407 from ayasa520/fix_reloadp
fix(plugin): fix reloadp command not taking effect
2024-12-13 15:39:33 +08:00
rikka 2a351ca415 fix(reloadp): clear handlers when reloading plugin to avoid memory leaks 2024-12-05 00:33:00 +08:00
rikka 49b7106d71 fix: Memory leak caused by scanp command due to handler's reference to plugin instance.
close #2412
2024-12-03 22:39:56 +08:00
Rikka c567641c5c fix(plugin): fix reloadp command not taking effect
- Use write_plugin_config() instead of directly modifying plugin_config dict
- Add remove_plugin_config() to clear plugin config before reload
- Update plugins to use pconf() and write_plugin_config() for better config management
2024-12-02 16:38:21 +08:00
13476573407 15e0702329 解决使用scanp重载时会重新生成godcmd的实例,导致auth权限被清空 2024-03-20 10:52:34 +08:00
13476573407 a2bc22c37d 由于使用#scanp和#reloadp扫描插件时,当更新插件以后并不会实现重载新的插件
所以取消了已载入的插件判断重载除Godcmd以外的所有插件来实现不需要重启项目即可更新插件
2024-03-18 14:40:01 +08:00
zhayujie aae9b64833 fix: reduce unnecessary error traceback logs 2023-07-20 14:46:41 +08:00
zhayujie 954e55f4b4 feat: add plugin global config to support docker volumes 2023-07-20 11:36:02 +08:00
lanvent 419a3e518e feat: make plugin compatible with LINKAI in most cases 2023-06-10 23:42:43 +08:00
lanvent 2133c1d6af fix(Dockerfile): create /home/noroot directory and change ownership of it 2023-05-09 23:08:20 +08:00
lanvent 618c94edb8 formatting: run precommit on all files 2023-04-22 12:01:29 +08:00
lanvent 2f732e5493 fix: toolhub request_timeout should be str 2023-04-17 12:00:28 +08:00
lanvent 8f72e8c3e6 formatting code 2023-04-17 01:01:02 +08:00
lanvent 4278d2b8ef feat: add updatep command 2023-04-07 23:31:07 +08:00
lanvent 9aeb60f66d feat: add replicate to source.json 2023-04-07 15:15:40 +08:00
lanvent c9306633b2 fix: read source.json with utf-8 2023-04-07 01:15:31 +08:00
lanvent 898aa30b1d godcmd: add temp passwd 2023-04-06 21:57:02 +08:00
lanvent 8b73a74609 fix: bug when reinstall plugin 2023-04-06 21:54:38 +08:00
lanvent 3c6d42b22e feat: add installp/uninstallp command 2023-04-06 21:54:38 +08:00
lanvent 5bd9f50818 feat: disable plugin when init failed 2023-04-05 18:05:28 +08:00
lanvent cc881adda6 Merge Pull Request #686 into master 2023-04-05 04:18:06 +08:00
lanvent 4881f7b01c fix: use relpath in plugin manager 2023-03-25 23:35:23 +08:00
zhayujie 8c4a62b9c6 fix: use try catch instead of config 2023-03-24 00:59:26 +08:00
zhayujie c1d1e923cd feat: add plugins config 2023-03-24 00:22:09 +08:00
lanvent 2568322879 plugin: ignore cases when manage plugins 2023-03-14 18:02:07 +08:00
lanvent 300b7b9687 plugins: support reload plugin 2023-03-14 15:59:52 +08:00
lanvent 1dc3f85a66 plugin: support priority to decide trigger order 2023-03-13 15:32:28 +08:00
lanvent cb7bf446e3 plugin: godcmd support manage plugins 2023-03-13 01:50:37 +08:00
lanvent 475ada22e7 catch thread exception 2023-03-12 22:49:07 +08:00
lanvent 8847b5b674 create bot when first need 2023-03-12 13:25:23 +08:00
lanvent 73de429af1 import file with the same name as plugin 2023-03-12 12:57:27 +08:00
lanvent 0fcf0824dc feat: support plugins 2023-03-12 11:53:06 +08:00