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:
@@ -56,7 +56,7 @@ before(async () => {
|
||||
BASE = `http://127.0.0.1:${server.address().port}`;
|
||||
ids.boss = (await auth.createUser("boss", BOSS_PASS, "admin")).id;
|
||||
ids.ana = (await auth.createUser("ana", "analyst-pass-1", "analyst")).id;
|
||||
ids.mia = (await auth.createUser("mia", "member-pass-1", "member")).id;
|
||||
ids.mia = (await auth.createUser("mia", "member-pass-1", "developer")).id;
|
||||
ids.lane = db
|
||||
.prepare("INSERT INTO lanes (cwd, tool) VALUES ('/w/ownerless', 'claude')")
|
||||
.run().lastInsertRowid;
|
||||
@@ -76,14 +76,14 @@ describe("managing accounts from the dashboard", () => {
|
||||
body: {
|
||||
username: "neo",
|
||||
password: "neo-pass-123",
|
||||
role: "member",
|
||||
role: "developer",
|
||||
currentPassword: BOSS_PASS,
|
||||
},
|
||||
});
|
||||
assert.equal(r.status, 201);
|
||||
assert.deepEqual(
|
||||
{ username: r.body.user.username, role: r.body.user.role },
|
||||
{ username: "neo", role: "member" }
|
||||
{ username: "neo", role: "developer" }
|
||||
);
|
||||
assert.ok(!JSON.stringify(r.body).includes("scrypt"), "no password hash");
|
||||
assert.ok(await login("neo", "neo-pass-123"), "the new user can log in");
|
||||
@@ -179,14 +179,14 @@ describe("managing accounts from the dashboard", () => {
|
||||
assert.equal(bad.status, 400);
|
||||
const none = await call("PATCH", "/api/users/9999", {
|
||||
cookie: boss,
|
||||
body: { role: "member", currentPassword: BOSS_PASS },
|
||||
body: { role: "developer", currentPassword: BOSS_PASS },
|
||||
});
|
||||
assert.equal(none.status, 404);
|
||||
});
|
||||
|
||||
it("never lets an admin demote or disable themselves", async () => {
|
||||
const boss = await login("boss", BOSS_PASS);
|
||||
for (const body of [{ role: "member" }, { disabled: true }]) {
|
||||
for (const body of [{ role: "developer" }, { disabled: true }]) {
|
||||
const r = await call("PATCH", `/api/users/${ids.boss}`, {
|
||||
cookie: boss,
|
||||
body: { ...body, currentPassword: BOSS_PASS },
|
||||
@@ -239,7 +239,7 @@ describe("managing accounts from the dashboard", () => {
|
||||
}
|
||||
assert.equal(last.status, 429);
|
||||
assert.equal(last.body.error.code, "ETOOMANY");
|
||||
assert.equal(auth.findUser("mia").role, "member");
|
||||
assert.equal(auth.findUser("mia").role, "developer");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user