feat(pool): an admin's switch moves a user's Claude login at once
PUT /api/accounts/pins (and agenthub accounts pin) now writes a pinned Claude login into the user's home right away (claude-pool.switchHome): every home's newer pair is copied back first, the member's own login is never written over, and no pane is restarted — running sessions take the new pair at their next credentials read. A live home stays on the login it holds, so unpinning does not flip it back. The Users page calls it "Switch a user's account" and says what happened.
This commit is contained in:
@@ -10,7 +10,8 @@
|
||||
* Users-page route moves a saved switch-user profile in — the one this
|
||||
* machine's own Claude Code is logged into with its LIVE login, the host's
|
||||
* home then being one more placement the server refreshes. A pin of the
|
||||
* owner to another login waits while a live pane holds the first.
|
||||
* owner to another login waits while a live pane holds the first, until the
|
||||
* admin's switch writes it into the home (never over the member's own login).
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
@@ -189,7 +190,7 @@ describe("a pool login", () => {
|
||||
assert.equal(pool.__read("team"), null);
|
||||
});
|
||||
|
||||
it("follows the owner's pin, but never over another login a live pane holds", () => {
|
||||
it("follows the owner's pin, and an admin's switch moves the home's login now", async () => {
|
||||
const { db } = require("../db");
|
||||
const pins = require("../lib/account-pins");
|
||||
const miaId = db.prepare("SELECT id FROM users WHERE username = 'mia'").get().id;
|
||||
@@ -212,9 +213,22 @@ describe("a pool login", () => {
|
||||
`INSERT INTO dashboard_runs (id, mode, cwd, status, started_at, account, lane_id, tool)
|
||||
VALUES ('pin-run', 'interactive', ?, 'running', ?, 'alpha', ?, 'claude')`
|
||||
).run(path.join(HOME, "proj"), new Date().toISOString(), lane);
|
||||
assert.equal(pool.forSpawn(acct, miaId).name, "alpha");
|
||||
db.prepare("UPDATE dashboard_runs SET status = 'completed' WHERE id = 'pin-run'").run();
|
||||
assert.equal(pool.forSpawn(acct, miaId).name, "alpha", "a bare pin waits for the pane");
|
||||
// The admin's switch writes beta into the home now; the pane is not touched.
|
||||
assert.equal(await pins.applyPin(miaId, "claude"), "switched");
|
||||
assert.equal(diskCreds().refreshToken, oauth("b").refreshToken);
|
||||
assert.equal(pool.__read("alpha").placed.mia, undefined, "alpha no longer holds mia's home");
|
||||
assert.equal(pool.forSpawn(acct, miaId).name, "beta");
|
||||
assert.equal(await pins.applyPin(miaId, "claude"), "kept");
|
||||
// Unpinned with the pane still live: the home's login stays, no flip back.
|
||||
pins.setPin(miaId, "claude", null);
|
||||
assert.equal(pool.forSpawn(acct, miaId).name, "beta");
|
||||
// A login the member made themselves is never written over.
|
||||
fs.writeFileSync(CREDS, JSON.stringify({ claudeAiOauth: oauth("own") }));
|
||||
pins.setPin(miaId, "claude", "alpha");
|
||||
assert.equal(await pins.applyPin(miaId, "claude"), "own-login");
|
||||
assert.equal(diskCreds().refreshToken, oauth("own").refreshToken);
|
||||
pins.setPin(miaId, "claude", "beta");
|
||||
pool.removeAccount("beta");
|
||||
assert.equal(pins.pinFor(miaId, "claude"), null, "a removed account drops its pins");
|
||||
db.prepare("DELETE FROM dashboard_runs WHERE id = 'pin-run'").run();
|
||||
|
||||
Reference in New Issue
Block a user