diff --git a/gui/main.qml b/gui/main.qml index c714043..6b5f3b9 100644 --- a/gui/main.qml +++ b/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 { diff --git a/gui/ollie9pclient.cpp b/gui/ollie9pclient.cpp index 7adc160..231e121 100644 --- a/gui/ollie9pclient.cpp +++ b/gui/ollie9pclient.cpp @@ -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; diff --git a/gui/ollie9pclient.h b/gui/ollie9pclient.h index 452399a..aa7e6d6 100644 --- a/gui/ollie9pclient.h +++ b/gui/ollie9pclient.h @@ -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);