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:
@@ -524,6 +524,46 @@ describe("pty-run", () => {
|
||||
assert.match(pty.paneArgv(null, "linux")[2], /exec "\$SHELL" -l$/);
|
||||
});
|
||||
|
||||
it("hands a long prompt to the pane through a file, never tmux's argv (issue #68)", (t) => {
|
||||
process.env.DASHBOARD_DATA_DIR = TMP;
|
||||
t.after(() => delete process.env.DASHBOARD_DATA_DIR);
|
||||
// tmux refuses a message past 16 KB (`command too long`).
|
||||
const prompt = `-${"x".repeat(20_000)}\nend`;
|
||||
const call = captureNewSession(() =>
|
||||
pty.spawnRun({ laneId: 15, cwd: "/tmp/repo", initialPrompt: prompt })
|
||||
);
|
||||
assert.ok(Buffer.byteLength(call.join("")) < 8 * 1024);
|
||||
const argv = paneArgv(call);
|
||||
assert.deepEqual([argv[0], argv[1], argv[3]], ["sh", "-c", "sh"]);
|
||||
const file = argv[4];
|
||||
assert.equal(fs.statSync(file).mode & 0o777, 0o600);
|
||||
assert.equal(argv[5], "claude");
|
||||
// Run the reader with a stand-in CLI: the prompt arrives as ONE last
|
||||
// argument, leading-dash guard included, and the file is gone after.
|
||||
const { execFileSync } = require("node:child_process");
|
||||
const out = execFileSync(argv[0], [
|
||||
...argv.slice(1, 5),
|
||||
process.execPath,
|
||||
"-e",
|
||||
"process.stdout.write(process.argv.at(-1))",
|
||||
]).toString();
|
||||
assert.equal(out, ` ${prompt}`);
|
||||
assert.ok(!fs.existsSync(file));
|
||||
});
|
||||
|
||||
it("refuses a prompt no CLI can take as one argument, before starting anything", () => {
|
||||
const calls = [];
|
||||
tmux.__setExecImpl((args) => {
|
||||
calls.push(args);
|
||||
return "";
|
||||
});
|
||||
assert.throws(
|
||||
() => pty.spawnRun({ laneId: 16, cwd: "/tmp/repo", initialPrompt: "é".repeat(70_000) }),
|
||||
{ code: "EBADPROMPT" }
|
||||
);
|
||||
assert.equal(calls.length, 0);
|
||||
});
|
||||
|
||||
it("spawnRun starts the CLI only once the pane has its real size, argv untouched", () => {
|
||||
// A claude drawn at tmux's birth size duplicated its banner on the first
|
||||
// resize; the argv rides as positional parameters, never script text.
|
||||
|
||||
Reference in New Issue
Block a user