gui: separate session and agent creation, add agent context menu
- '+' button now creates an empty session directly (no dialog) - Dialog becomes 'New Agent' (session name field removed) - Session context menu: 'New Agent...' + 'Kill Session' - Agent context menu: 'Kill' (writes kill to agent ctl) - New C++ methods: createQuickSession(), createAgent(), killAgent()
This commit is contained in:
parent
042f287796
commit
84fb53332c
66
gui/main.qml
66
gui/main.qml
|
|
@ -92,6 +92,11 @@ ApplicationWindow {
|
|||
|
||||
// Session ID to kill — set on right-click, consumed by context menu
|
||||
property string killTargetSessionId: ""
|
||||
// Target session for "New Agent" dialog
|
||||
property string newAgentTargetSessionId: ""
|
||||
// Agent kill target — set on right-click
|
||||
property string killTargetAgentSessionId: ""
|
||||
property string killTargetAgentId: ""
|
||||
|
||||
// Session tree model — flat list with level info
|
||||
ListModel {
|
||||
|
|
@ -355,10 +360,10 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
// --- New Session Dialog ---
|
||||
// --- New Agent Dialog ---
|
||||
Dialog {
|
||||
id: newSessionDialog
|
||||
title: "New Session"
|
||||
id: newAgentDialog
|
||||
title: "New Agent"
|
||||
anchors.centerIn: parent
|
||||
width: 420
|
||||
modal: true
|
||||
|
|
@ -368,20 +373,6 @@ ApplicationWindow {
|
|||
spacing: 16
|
||||
Layout.margins: 16
|
||||
|
||||
// --- Session ---
|
||||
Label { text: "Session"; font.bold: true; font.pointSize: theme.fontSize > 0 ? theme.fontSize + 1 : Qt.application.font.pointSize + 1 }
|
||||
|
||||
// Name
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label { text: "Name:"; Layout.preferredWidth: 80 }
|
||||
TextField {
|
||||
id: nameField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "(auto-generated)"
|
||||
}
|
||||
}
|
||||
|
||||
// --- Agent ---
|
||||
Label { text: "Agent"; font.bold: true; font.pointSize: theme.fontSize > 0 ? theme.fontSize + 1 : Qt.application.font.pointSize + 1 }
|
||||
|
||||
|
|
@ -503,21 +494,20 @@ ApplicationWindow {
|
|||
onAccepted: {
|
||||
var cwd = dirField.text.trim()
|
||||
if (cwd === "") {
|
||||
console.log("New session: directory is required")
|
||||
console.log("New agent: directory is required")
|
||||
return
|
||||
}
|
||||
var name = nameField.text.trim()
|
||||
var backend = backendCombo.currentText.trim()
|
||||
var model = modelCombo.currentIndex >= 0 ? modelCombo.currentText.trim() : ""
|
||||
var agent = agentCombo.currentText
|
||||
var remote = remoteField.text.trim()
|
||||
var agentAlias = agentAliasField.text.trim()
|
||||
|
||||
var ok = ollie.createSession(cwd, name, backend, model, agent, remote, agentAlias)
|
||||
var ok = ollie.createAgent(root.newAgentTargetSessionId, cwd, backend, model, agent, remote, agentAlias)
|
||||
if (ok) {
|
||||
ollie.refreshSessions()
|
||||
} else {
|
||||
console.log("New session: creation failed")
|
||||
console.log("New agent: creation failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -538,7 +528,10 @@ ApplicationWindow {
|
|||
Label { text: "Sessions"; Layout.fillWidth: true; font.bold: true; leftPadding: 8 }
|
||||
ToolButton {
|
||||
text: "\u2795"
|
||||
onClicked: newSessionDialog.open()
|
||||
onClicked: {
|
||||
if (ollie.createQuickSession())
|
||||
ollie.refreshSessions()
|
||||
}
|
||||
ToolTip.text: "New Session"
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
|
|
@ -599,6 +592,13 @@ ApplicationWindow {
|
|||
if (item)
|
||||
root.killTargetSessionId = item.id
|
||||
sessionCtxMenu.popup()
|
||||
} else if (type === "agent") {
|
||||
var agentItem = sessionTreeModel.get(parent.modelIndex)
|
||||
if (agentItem) {
|
||||
root.killTargetAgentSessionId = agentItem.parentId
|
||||
root.killTargetAgentId = agentItem.agentId
|
||||
}
|
||||
agentCtxMenu.popup()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -619,8 +619,14 @@ ApplicationWindow {
|
|||
Menu {
|
||||
id: sessionCtxMenu
|
||||
MenuItem {
|
||||
text: "Kill"
|
||||
icon.color: "#f44336"
|
||||
text: "New Agent..."
|
||||
onClicked: {
|
||||
root.newAgentTargetSessionId = root.killTargetSessionId
|
||||
newAgentDialog.open()
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Kill Session"
|
||||
onClicked: {
|
||||
if (root.killTargetSessionId)
|
||||
ollie.killSession(root.killTargetSessionId)
|
||||
|
|
@ -628,6 +634,18 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: agentCtxMenu
|
||||
MenuItem {
|
||||
text: "Kill"
|
||||
onClicked: {
|
||||
if (root.killTargetAgentSessionId && root.killTargetAgentId)
|
||||
ollie.killAgent(root.killTargetAgentSessionId, root.killTargetAgentId)
|
||||
ollie.refreshSessions()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
|
|
|
|||
|
|
@ -593,6 +593,68 @@ bool Ollie9pClient::createSession(const QString &cwd, const QString &name, const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Ollie9pClient::createQuickSession()
|
||||
{
|
||||
QProcess proc;
|
||||
proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "session/new"});
|
||||
proc.waitForStarted(3000);
|
||||
proc.write("\n");
|
||||
proc.closeWriteChannel();
|
||||
proc.waitForFinished(5000);
|
||||
return proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool Ollie9pClient::createAgent(const QString &sessionId, const QString &cwd, const QString &backend, const QString &model, const QString &agent, const QString &remote, const QString &agentAlias)
|
||||
{
|
||||
if (sessionId.isEmpty() || cwd.isEmpty()) return false;
|
||||
|
||||
QString sessName = sessionNameForId(sessionId);
|
||||
if (sessName.isEmpty()) sessName = sessionId;
|
||||
|
||||
QStringList agentArgs;
|
||||
agentArgs << "cwd=" + cwd;
|
||||
if (!backend.isEmpty()) agentArgs << "backend=" + backend;
|
||||
if (!model.isEmpty()) agentArgs << "model=" + model;
|
||||
if (!agent.isEmpty()) agentArgs << "agent=" + agent;
|
||||
if (!remote.isEmpty()) agentArgs << "remote=" + remote;
|
||||
if (!agentAlias.isEmpty()) agentArgs << "name=" + agentAlias;
|
||||
|
||||
QProcess proc;
|
||||
proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "session/" + sessName + "/agent/new"});
|
||||
proc.waitForStarted(3000);
|
||||
proc.write((agentArgs.join(" ") + "\n").toUtf8());
|
||||
proc.closeWriteChannel();
|
||||
proc.waitForFinished(5000);
|
||||
|
||||
if (proc.exitCode() != 0) {
|
||||
QString err = QString::fromUtf8(proc.readAllStandardError()).trimmed();
|
||||
if (err.isEmpty())
|
||||
err = QString::fromUtf8(proc.readAllStandardOutput()).trimmed();
|
||||
qDebug() << "createAgent failed:" << err;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ollie9pClient::killAgent(const QString &sessionId, const QString &agentId)
|
||||
{
|
||||
if (sessionId.isEmpty() || agentId.isEmpty()) return false;
|
||||
|
||||
QString sessName = sessionNameForId(sessionId);
|
||||
if (sessName.isEmpty()) sessName = sessionId;
|
||||
QString agentName = agentNameForId(sessionId, agentId);
|
||||
if (agentName.isEmpty()) agentName = agentId;
|
||||
|
||||
QString path = "session/" + sessName + "/agent/" + agentName + "/ctl";
|
||||
QProcess proc;
|
||||
proc.start(ninepBin(), {"-a", serverAddr(), "write", path});
|
||||
proc.waitForStarted(3000);
|
||||
proc.write("kill");
|
||||
proc.closeWriteChannel();
|
||||
proc.waitForFinished(3000);
|
||||
return proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool Ollie9pClient::renameSession(const QString &sessionId, const QString &newName)
|
||||
{
|
||||
if (sessionId.isEmpty() || newName.isEmpty()) return false;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ public:
|
|||
Q_INVOKABLE void loadRootAgents();
|
||||
Q_INVOKABLE QStringList getAvailableModels(const QString &backend) const;
|
||||
Q_INVOKABLE bool createSession(const QString &cwd, const QString &name, const QString &backend, const QString &model, const QString &agent, const QString &remote, const QString &agentAlias);
|
||||
Q_INVOKABLE bool createQuickSession();
|
||||
Q_INVOKABLE bool createAgent(const QString &sessionId, const QString &cwd, const QString &backend, const QString &model, const QString &agent, const QString &remote, const QString &agentAlias);
|
||||
Q_INVOKABLE bool killAgent(const QString &sessionId, const QString &agentId);
|
||||
Q_INVOKABLE bool renameSession(const QString &sessionId, const QString &newName);
|
||||
Q_INVOKABLE bool renameAgent(const QString &sessionId, const QString &agentId, const QString &newName);
|
||||
|
||||
|
|
|
|||
Reference in New Issue