9 Commits
Author SHA1 Message Date
ffd6c3086c feat(channels): 打通通道插件安装与运行生命周期 (#1823)
* feat: bridge plugin-contributed channel kinds (infra + API)

Octop-harness plugins can now contribute channel kinds (kind: channel)
that become first-class channels in the gateway and dashboard.

- infra/gateway/plugin_channels.py: apply_plugin_channels() replays
  plugin channel registrations into octop_gateway.register_channel_kind
  (idempotent, first-wins, per-plugin failure isolation);
  plugin_channel_kinds() summarizes them for the dashboard catalogue.
- server.py: apply plugin channel kinds right after load_installed()
  so kinds exist before gateway boot rebuilds DB channels.
- api/routers/channels.py: kind validation now accepts plugin kinds
  (case-normalized) in create/patch/probe bodies, tolerant of harness
  releases without all_channels(); new GET /channels/plugin-kinds
  endpoint exposing the plugin channel catalogue.

Tests: 8 cases in tests/unit/test_plugin_channels.py (7 run on released
harness; the fixture end-to-end test auto-skips until the updated
harness is installed).

* feat(dashboard): merge plugin-contributed channel kinds into the catalogue

Fetch GET /channels/plugin-kinds once per mount and register each
returned kind into the channel catalogue at runtime (keys, labels,
icons, colors, per-kind config schema). Builtin kinds stay compile-time
constants; registration is idempotent and builtin kinds always win.

Kinds registered without a schema fall back to the drawer's existing
raw-JSON editing path, so any kind:channel plugin is configurable
without further frontend work. Fetch failure keeps a builtin-only
catalogue (older backends without the endpoint).

* docs: add plugin channel support entry to CHANGELOG

* fix(channels): expose registration errors and reload only changed kinds

* fix(plugins): support released harness and gateway in CI

* fix(channels): isolate persistent registration failures by kind

* fix: retry failed plugin channel removals without blocking refresh

---------

Co-authored-by: Chovy <chovy@local>
Co-authored-by: ideshun <28327099+ideshun@users.noreply.github.com>
Co-authored-by: 薄生 <67680641+Bosheng0422@users.noreply.github.com>
2026-10-16 20:55:34 +08:00
6f2bc50b98 fix: load plugins that still import harness_agent (#1260)
Marketplace plugins written before the octop_harness rename fail to install
and enable with ModuleNotFoundError. Alias that import onto the installed package.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 10:48:56 +08:00
2209cdc90f chore(deps): switch runtime packages to octop-* 1.0.0 (#1136)
Replace orcakit-harness-agent / harness-* with octop-harness, octop-gateway, octop-memory, and octop-browser, and align docs, UI copy, and generated paths.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 06:44:24 +08:00
e02174e358 feat: plugin marketplace and related dashboard polish (#815)
* feat: add in-tree plugin marketplace and related dashboard polish

Ship a catalog install flow (market API + Admin UI), stop auto-seeding new
bundled plugins, and include storage rename, plugin tools listing, agent
lifecycle locking, and history/trajectory path cleanup in the same change.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(dashboard): use ToolsTabs in Experts tools drawer

Align the Experts tools catalog with Personalization so builtin, plugin,
and ACP tabs are available in the same drawer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: per-tool chat icons and init seed expectation

Show distinct Lucide icons for common built-in tools in chat tool rows,
and stop expecting octop init to auto-copy catalog plugins after the
marketplace seed change.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(test): drain captcha siteverify POST bodies on Windows

Unread form bodies from the mock HTTP server could leave the connection
half-closed and flake reCAPTCHA v3 multi-call assertions under Windows CI.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-19 19:35:36 +08:00
HUANG Cheng c736e50685 fix(config): never rewrite an unparseable config.json as empty (#730) (#738)
* fix(config): never rewrite an unparseable config.json as empty (#730)

Four call sites read config.json, treated a parse failure as an empty dict and
wrote that back, destroying every other key. One trailing comma plus one
`octop run --port 8088` wiped 17 of 18 keys including the whole `database`
section; the next boot silently came up as a greenfield SQLite instance (fresh
setup-wizard password, zero warnings) while the PostgreSQL data sat unreachable.
Losing `bind_host` the same way is what makes #730's reporter unable to reach
the server from the LAN.

A torn write creates the same precondition with no user error: three of the four
sites wrote config.json with a bare write_text, so a crash / OOM / full disk
mid-write left invalid JSON that the next plugin toggle then wiped.

One shared policy in a new leaf helper, infra/utils/json_file.py:

  absent file            -> {} and write (first run, unchanged)
  valid JSON object      -> merge and write (unchanged)
  unparseable/non-object -> raise CONFIG_FILE_CORRUPT, write nothing

The error names the file and the parser line/column but never the contents,
which hold database credentials. Writes now go through write_json_atomic (temp +
os.replace, fd closed on every path, temp removed on failure) so no reader can
observe a torn file.

Sites: cli/commands/run.py (_save_configfile_overrides, plus
_load_configfile_overrides which used to fall back to launch defaults),
infra/agents/plugins/manager.py (_write_global_plugin_enabled = the dashboard
toggle), infra/setup/service.py (persist_bind_options),
infra/agents/plugins/seed.py. `octop run` / `octop service start` report the
error and exit 1 via fail_octop (now NoReturn) instead of a traceback.

Supersedes the pinned "treat a malformed file as empty so the next run rewrites
it cleanly" contract: its fixture (`{not valid json`) had nothing to lose, so
the test could not observe the destruction. Rewritten as
test_run_reports_corrupt_config_cleanly and
test_run_does_not_overwrite_corrupt_config.

Verified end-to-end against a real OctopServer + real PostgreSQL. Before: one
`octop run --port` flipped driver postgresql -> sqlite, the instance reverted to
the first-run wizard, and users=1/agents=1 were orphaned in PG with no warning.
After: exit 1, config.json byte-identical, boot fails loudly instead of
switching backends. Mutation check: restoring the lenient policy in the helper
reddens 9 tests spanning all four sites.

make all green (3299 passed, 16 skipped); mypy --strict clean; dashboard
`npx tsc --noEmit` clean.

* fix(config): name the file when config.json is corrupt (#730)

`load_config` let a bare `json.JSONDecodeError` escape: it reports a line and
column but never says *which* file failed, and this entry point is reached from
boot, backup and several CLI commands — not only `octop run`. Raise a ValueError
naming the path and the parser position, refuse a non-object document, and never
echo file contents (config.json holds database credentials).

Same policy as infra/utils/json_file.py, duplicated here because octop.config
must stay free of infra imports (AGENTS.md §5).

`octop run` already exits cleanly on a corrupt config through resolve_bind; this
covers the remaining entry points.

make all green (3301 passed, 16 skipped); mypy --strict clean.

* fix(config): preserve config.json permissions in atomic writes

Self-review against CONTRIBUTING.md / AGENTS.md §7 caught a regression in the
atomic writer added earlier in this branch: tempfile.mkstemp creates the temp
file 0600, so replacing config.json silently tightened an existing 0644 file to
0600. Under `octop service start --scope system` that runs as root and writes
into the service user's home, a root-owned 0600 config.json is unreadable by the
service user and the unit cannot start; the bare write_text it replaced produced
the umask default.

write_json_atomic now chmods the temp file to the mode of the file being
replaced, or to `0o666 & ~umask` for a new file — matching the two pre-existing
config.json writers (infra/db/rebind.py, infra/backup/auto.py; both
write_text + replace) rather than mkstemp's 0600. POSIX-guarded, so Windows
behaviour is unchanged and no test asserts mode bits off POSIX (AGENTS.md §7).

Also adds the missing CHANGELOG line for the load_config error-message change.

Verified: new file -> 0644, existing 0644 -> 0644, existing 0600 -> 0600.
make all green (3303 passed, 16 skipped); make typecheck-frontend green;
eslint 0 errors (67 pre-existing react-hooks warnings).

Note: `make check-all` is red on develop itself — prettier --check fails on
dashboard/src/pages/Agent/Skills/components/SkillsTabs.tsx exactly as committed
by 83378876. Left untouched here; confirmed pre-existing by checking develop's
version of that file in isolation.
2026-09-17 15:36:40 +08:00
22bf41be14 feat(plugins): per-agent tool toggles, plugin reload, UI assets, and tool catalog (#387)
- Add server-wide plugin enable/disable (PATCH /plugins/{id}) and a
  reload endpoint to pick up CLI installs without a full restart.
- Serve read-only plugin UI assets with traversal checks.
- Persist per-agent plugin tool enable flags and hot-sync the harness
  denylist so disables take effect on the next turn (no full reload).
- Add a tool catalog and plugin market UI plus a tool settings API.
- Ship demo plugins: ui-card, server-status, bilibili-anime.
- Update i18n bundles and API docs.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: CodeBuddy <noreply@codebuddy.ai>
2026-08-23 21:07:14 +08:00
Hongchujie 37bf0f497b feat: 插件管理页支持从本地 ZIP 上传安装插件 (#226)
* feat: support installing plugins from a local ZIP archive

Add a POST /plugins/upload endpoint (admin-only) that installs a plugin
from an uploaded ZIP, plus a local-file picker in the installed-plugins
panel with an optional overwrite checkbox. Extract install_archive() from
install_url() so both paths share zip validation and path-traversal
protection.

* style: fix ruff formatting of install_url error message
2026-08-10 22:16:15 +08:00
jubaoliangandCursor 247efa9c21 feat(plugins): harden plugin install errors, MCP validation, and stream i18n
Improve plugin manager/CLI diagnostics, custom MCP checks, and gateway stream
error localization; ship demo plugins and drop local editable harness-memory.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 15:05:01 +08:00
jubaoliang 748d998cf2 first commit 2026-07-09 14:32:36 +00:00