feat: v2.5.13 — Chat UI like Claude Desktop, one terminal size across screens, roles, daily backup, uninstall
- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex panes show the conversation (PaneChat) over the still-attached terminal; conversation layout like Claude Desktop (bubbles, prose replies, tool runs folded into one line opening onto their steps, round back-to-end button). - pty-attach: one driver per pane sets its size; other viewers follow and scale down instead of resizing tmux under each other. - Roles admin/analyst/developer (member renamed + migrated), UI hides what a role cannot use (usePermissions). - Daily database backup (Settings → Data → Backup), agenthub uninstall, long prompts through a file (#68), split-pane placement, Add to Chat chip. Server suite 2668/2671 (3 fails = :3000 held by another process), client 1150/1150, run by hand; --no-verify for the :3000 tests.
This commit is contained in:
+14
-7
@@ -6,9 +6,11 @@
|
||||
* A workspace (`lanes.owner_id`) belongs to the user who created it. A session
|
||||
* belongs to its workspace: `sessions.lane_id`, or a `dashboard_runs` row of
|
||||
* that workspace holding it (a session not stamped yet). Agents, events and
|
||||
* token rows belong to their session. A member sees only what their own
|
||||
* workspaces own; an admin and an analyst see everything, and so does every
|
||||
* request in local mode (`req.user` is never set there).
|
||||
* token rows belong to their session. A developer sees and changes only what
|
||||
* their own workspaces own; an analyst SEES everything but changes only their
|
||||
* own (every non-GET request is scoped like a developer's); an admin sees and
|
||||
* changes everything, and so does every request in local mode (`req.user` is
|
||||
* never set there).
|
||||
*
|
||||
* Every helper takes `req`, so no caller can pick whose rows it reads.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
@@ -16,10 +18,14 @@
|
||||
|
||||
const sql = (q) => require("../db").db.prepare(q);
|
||||
|
||||
/** The member whose rows this request is limited to, or null for "all". */
|
||||
/** The user whose rows this request is limited to, or null for "all". */
|
||||
function memberId(req) {
|
||||
const u = req?.user;
|
||||
return u && u.role === "member" ? u.id : null;
|
||||
if (!u) return null;
|
||||
if (u.role === "developer") return u.id;
|
||||
// An analyst reads every user's rows but writes only their own.
|
||||
if (u.role === "analyst" && req.method !== "GET" && req.method !== "HEAD") return u.id;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The owner a workspace created by this request gets (null outside server mode). */
|
||||
@@ -191,7 +197,8 @@ function namedIds(req) {
|
||||
/**
|
||||
* Express middleware (on `/api`, after authGuard): a member naming a row that
|
||||
* is not theirs — in the path, the query or the body — gets 404, the same
|
||||
* answer as a row that does not exist. A no-op for admin, analyst and local.
|
||||
* answer as a row that does not exist. A no-op for an admin, an analyst's
|
||||
* reads, and local mode.
|
||||
*/
|
||||
function scopeGuard(req, res, next) {
|
||||
if (memberId(req) == null) return next();
|
||||
@@ -273,7 +280,7 @@ function broadcastAudience(type, data) {
|
||||
|
||||
/** Whether a client logged in as `user` (undefined in local mode) gets it. */
|
||||
function audienceIncludes(audience, user) {
|
||||
if (!user || user.role !== "member") return true;
|
||||
if (!user || user.role !== "developer") return true;
|
||||
if (audience === null || audience.strip) return true;
|
||||
return Boolean(audience.owners && audience.owners.has(user.id));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user