remove the email residue the extraction left in the platform
a sweep for remnants after 2026-08-18. nothing was broken - no dangling import, symbol, route or permission key anywhere in src/ - but the platform was still speaking the plugin's vocabulary in a dozen places. the one that mattered is live code rather than prose: ChatPanelWrapper hardcodes workspace.id === 'email' to send context: 'email', which is the frontend half of the pair whose backend half (resolveEmailCwd) carries a fifteen-line justification. the two ends now both say so, including that adding a SECOND id there is the signal the platform needs a real way for a plugin to declare a chat context. the rest: two dead imports left by removing the settings section, an empty 'Email' type-section header, a protocol.ts section header with no members naming the plugin's SSE design, and a queue comment pointing at sidecar/email/sync-runner.ts - a path in another repository now. CLAUDE.md, AGENTS.md and working-on-officer.md described email as a platform feature, an app permission and a pm2 process. those are the files a fresh agent reads first, so they were the highest-risk staleness in the tree. TODO.md's whole Email section moved to the plugin's own repository. it held OPEN items pointing at sidecar/email/store.ts, which is exactly how a leftover list sends someone to edit files that do not exist. the delete-account dialog promised it would remove email accounts and playlists. both tables belong to plugins now, so that cascade only happens where the plugin is installed - it now says 'anything an installed plugin keeps for them' instead of naming two it cannot guarantee. imapflow and mailparser STAY in package.json. the plugin has no package.json of its own and resolves from the platform's node_modules, so removing them would break the installed plugin's sidecar. the fix is giving plugins their own dependency manifest, not deleting these. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,13 +19,13 @@ None of those paths is configured. `src/servers/data-path.ts` derives the root a
|
||||
`resolve(process.cwd(), '..')` and hangs the rest off it, which is why the pm2 `cwd` pin matters and
|
||||
why `assertInstallLayout` refuses to boot from the wrong directory.
|
||||
|
||||
Officer is a self-hosted platform: an AI agent, a terminal, a file browser, a code editor, email, a
|
||||
Officer is a self-hosted platform: an AI agent, a terminal, a file browser, a code editor, a
|
||||
bitcoin wallet, a remote desktop and dashboards, behind one web app. **It is built around one owner**
|
||||
— user id 1, role `Super Admin`, who bypasses every permission check — and since 2026-08-07 also
|
||||
admits **additional accounts holding a strict subset of it**, governed by per-role permission grants.
|
||||
|
||||
So "which user" has three answers depending on the surface. For the **app** permissions (gitea,
|
||||
music, photos, email, calendar…) it is a real question with a real answer. For **confined** ones —
|
||||
music, photos, calendar…) it is a real question with a real answer. For **confined** ones —
|
||||
terminal, chat, files — it is also real, because the account has its own Linux user and the kernel
|
||||
enforces the boundary; a grant there means nothing without that user, and `authorize.ts` drops it.
|
||||
For **execution** — tasks, items, desktop, browser — it is still always the owner, and those can
|
||||
@@ -71,7 +71,7 @@ does, renaming a category — none of that needs a code change or a restart.
|
||||
**`platform/` — only when the mechanism itself is missing.** If a task needs a form control that
|
||||
doesn't exist, or an endpoint that isn't there, that's platform work. Adding a _permission_ is not.
|
||||
|
||||
**`data/` — never edit by hand.** `DATA_PATH`. Holds the owner's managed home, per-account email
|
||||
**`data/` — never edit by hand.** `DATA_PATH`. Holds the owner's managed home, per-account plugin
|
||||
SQLite stores, job logs, the queue, sidecar state. It is not backed up by git; deleting things here
|
||||
destroys the only copy.
|
||||
|
||||
@@ -96,7 +96,7 @@ Commit messages: simple lowercase, no prefixes, explaining _why_.
|
||||
## Running and checking your work
|
||||
|
||||
The server runs under pm2 as `officer`, plus sidecars (`officer-anthropic-proxy`, `officer-claude-code`,
|
||||
`officer-opencode`, `officer-email`, `officer-pty`, `officer-vnc`, `officer-music`, `officer-vault`,
|
||||
`officer-opencode`, `officer-pty`, `officer-vnc`, `officer-music`, `officer-vault`,
|
||||
`officer-slskd`, `officer-transmission`, `officer-invoiceshelf`, `officer-wallet`, and a process per
|
||||
installed plugin — `officer-offscale`, `officer-music`).
|
||||
`pm2 list` shows them; `pm2 logs officer` follows.
|
||||
@@ -107,7 +107,7 @@ and proxies API traffic; `officer-claude-code` is the process that actually runs
|
||||
**Which process to restart.** A change under `src/servers/sidecar/<name>/` needs that sidecar restarted;
|
||||
a change anywhere else needs `officer`. Both, if you changed the wire between them. Restarting `officer`
|
||||
no longer costs you a running agent session or a terminal — every sidecar is a PM2 peer, and the terminal
|
||||
and email sidecars serve their own listeners, so `officer` is not in their data path at all.
|
||||
and pty sidecars serve their own listeners, so `officer` is not in their data path at all.
|
||||
|
||||
**Don't restart the owner's server to test.** Boot your own on a spare port instead — the running
|
||||
instance holds `PORT` from `.env` (9010):
|
||||
|
||||
Reference in New Issue
Block a user