This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new db34f8e  fix(agents): say when a switched Codex account needs the 
agent restarted (#232)
db34f8e is described below

commit db34f8ec3ce2bcf6b6c0628666a3222f181faa92
Author: qioo777 <[email protected]>
AuthorDate: Fri Sep 18 18:29:57 2026 +0800

    fix(agents): say when a switched Codex account needs the agent restarted 
(#232)
---
 controllers/agent_account.go    | 36 +++++++++++++++++++++++++-----------
 web/src/Setting.tsx             |  4 +++-
 web/src/backend/AgentBackend.ts |  3 ++-
 web/src/lib/agents.ts           | 29 +++++++++++++++++++++++------
 web/src/locales/en/data.json    |  1 +
 web/src/locales/zh/data.json    |  1 +
 web/src/types.ts                |  6 ++++++
 7 files changed, 61 insertions(+), 19 deletions(-)

diff --git a/controllers/agent_account.go b/controllers/agent_account.go
index e25e542..039d579 100644
--- a/controllers/agent_account.go
+++ b/controllers/agent_account.go
@@ -23,6 +23,7 @@ import (
        "github.com/apache/casbin-gateway/agent"
        "github.com/apache/casbin-gateway/agentauth"
        "github.com/apache/casbin-gateway/agentpatch"
+       "github.com/apache/casbin-gateway/agentprocess"
        "github.com/apache/casbin-gateway/object"
 )
 
@@ -33,6 +34,15 @@ type agentAccountView struct {
        Current bool `json:"current"`
 }
 
+// agentAccountSwitchView is the answer to a swap: the account now in the
+// credential file, and whether the agent has to be restarted before it uses 
it.
+type agentAccountSwitchView struct {
+       agentAccountView
+       // Restart marks a swap made while the agent was running. An agent 
reads its
+       // sign-in when it starts, so what is running keeps the account it read 
then.
+       Restart bool `json:"restart"`
+}
+
 // agentAccountsView is the accounts of one agent and what it is signed in to,
 // which may be an account Gateway has not been given a copy of yet.
 type agentAccountsView struct {
@@ -124,7 +134,7 @@ func (c *ApiController) SaveAgentAccount() {
                return
        }
 
-       form, home, ok := c.readAgentAccountForm()
+       form, _, home, ok := c.readAgentAccountForm()
        if !ok {
                return
        }
@@ -153,7 +163,7 @@ func (c *ApiController) AddAgentAccount() {
                return
        }
 
-       form, _, ok := c.readAgentAccountForm()
+       form, _, _, ok := c.readAgentAccountForm()
        if !ok {
                return
        }
@@ -178,7 +188,7 @@ func (c *ApiController) SwitchAgentAccount() {
                return
        }
 
-       form, home, ok := c.readAgentAccountForm()
+       form, installation, home, ok := c.readAgentAccountForm()
        if !ok {
                return
        }
@@ -216,7 +226,10 @@ func (c *ApiController) SwitchAgentAccount() {
                c.ResponseError(err.Error())
                return
        }
-       c.ResponseOk(agentAccountView{AgentAccount: account, Current: true})
+       c.ResponseOk(agentAccountSwitchView{
+               agentAccountView: agentAccountView{AgentAccount: account, 
Current: true},
+               Restart:          
agentprocess.StatusOf(processTarget(installation)).Running,
+       })
 }
 
 // UpdateAgentAccount renames one stored account in the lists.
@@ -322,27 +335,28 @@ func (c *ApiController) CancelAgentSignin() {
 }
 
 // readAgentAccountForm resolves the body against the installations a scan 
found
-// and answers with the directory its sign-in is read from. Writing there is
-// writing into somebody's home, so an unverified body would name any of them.
-func (c *ApiController) readAgentAccountForm() (agentAccountForm, string, 
bool) {
+// and answers with the installation and the directory its sign-in is read 
from.
+// Writing there is writing into somebody's home, so an unverified body would
+// name any of them.
+func (c *ApiController) readAgentAccountForm() (agentAccountForm, 
agent.Installation, string, bool) {
        var form agentAccountForm
        if err := json.Unmarshal(c.Ctx.Input.RequestBody, &form); err != nil {
                c.ResponseError(err.Error())
-               return form, "", false
+               return form, agent.Installation{}, "", false
        }
 
        installation, err := findInstallation(form.AgentId, form.Path, 
form.Owner)
        if err != nil {
                c.ResponseError(err.Error())
-               return form, "", false
+               return form, agent.Installation{}, "", false
        }
        home, err := agentauth.HomeOf(installation.AgentId, installation.Path, 
installation.Owner)
        if err != nil {
                c.ResponseError(err.Error())
-               return form, "", false
+               return form, agent.Installation{}, "", false
        }
        form.AgentId = installation.AgentId
-       return form, home, true
+       return form, installation, home, true
 }
 
 // codexLoginProgram is the Codex program a sign-in is run with. The desktop 
app
diff --git a/web/src/Setting.tsx b/web/src/Setting.tsx
index d6d1c68..8d2ac02 100644
--- a/web/src/Setting.tsx
+++ b/web/src/Setting.tsx
@@ -102,7 +102,7 @@ export function goToLink(link: string) {
   window.location.href = link;
 }
 
-export function showMessage(type: "" | "success" | "error" | "info", text: 
string) {
+export function showMessage(type: "" | "success" | "warning" | "error" | 
"info", text: string) {
   // Backend messages are worth pasting into a bug report, and a toast is gone
   // before it can be selected by hand, so every one carries a copy button.
   const options = {action: <CopyButton value={text} className="text-current" 
/>};
@@ -111,6 +111,8 @@ export function showMessage(type: "" | "success" | "error" 
| "info", text: strin
     return;
   } else if (type === "success") {
     toast.success(text, options);
+  } else if (type === "warning") {
+    toast.warning(text, options);
   } else if (type === "error") {
     toast.error(text, options);
   } else {
diff --git a/web/src/backend/AgentBackend.ts b/web/src/backend/AgentBackend.ts
index 5004b51..1c3701c 100644
--- a/web/src/backend/AgentBackend.ts
+++ b/web/src/backend/AgentBackend.ts
@@ -32,6 +32,7 @@ import type {
   BrowseListing,
   SavedAccount,
   SavedAccounts,
+  SwitchedAccount,
 } from "@/types";
 
 export interface PatchTarget {
@@ -222,7 +223,7 @@ export function addAgentAccount(target: PatchTarget, 
apiKey: string, displayName
 
 /** Puts one stored sign-in back into the agent, saving what it replaces. */
 export function switchAgentAccount(target: PatchTarget, name: string) {
-  return request<SavedAccount>("/api/switch-agent-account", "POST", 
{...target, name: name});
+  return request<SwitchedAccount>("/api/switch-agent-account", "POST", 
{...target, name: name});
 }
 
 export function renameAgentAccount(name: string, displayName: string) {
diff --git a/web/src/lib/agents.ts b/web/src/lib/agents.ts
index 2412925..6973b41 100644
--- a/web/src/lib/agents.ts
+++ b/web/src/lib/agents.ts
@@ -35,6 +35,7 @@ import type {
   Provider,
   SavedAccount,
   SavedAccounts,
+  SwitchedAccount,
 } from "@/types";
 
 /** How long a started app is given before its process is looked for again. */
@@ -1133,17 +1134,21 @@ export function useAgentAccounts(agents: Agent[], 
enabled = true) {
   // withUsage is for the calls that change which sign-in is stored or in use; 
a
   // rename changes neither, so it asks the vendor nothing.
   const call = React.useCallback(
-    (
+    <T,>(
       key: string,
-      run: () => Promise<{status: string; msg?: string}>,
-      done: string,
+      run: () => Promise<{status: string; msg?: string; data?: T}>,
+      // A string is reported as it stands; a call whose wording depends on the
+      // answer reports it itself.
+      done: string | ((data?: T) => void),
       withUsage = false,
     ) => {
       setBusyKey(key);
       return run()
         .then(res => {
           if (res.status === "ok") {
-            if (done !== "") {
+            if (typeof done === "function") {
+              done(res.data);
+            } else if (done !== "") {
               Setting.showMessage("success", done);
             }
             load();
@@ -1163,10 +1168,22 @@ export function useAgentAccounts(agents: Agent[], 
enabled = true) {
   /** Puts one stored sign-in back into the agent, saving what it replaces. */
   const switchTo = React.useCallback(
     (agent: Agent, account: SavedAccount) =>
-      call(
+      call<SwitchedAccount>(
         account.name,
         () => AgentBackend.switchAgentAccount(targetOf(agent), account.name),
-        `${i18next.t("agent:Account switched")}: ${accountName(account)}`,
+        switched => {
+          const done = `${i18next.t("agent:Account switched")}: 
${accountName(account)}`;
+          // A running agent keeps the sign-in it started with, and saying the
+          // swap is done would be saying it is already answering as the new 
one.
+          if (switched?.restart) {
+            Setting.showMessage(
+              "warning",
+              `${done} - ${i18next.t("agent:Restart the agent for it to take 
effect")}`,
+            );
+          } else {
+            Setting.showMessage("success", done);
+          }
+        },
         true,
       ),
     [call],
diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json
index 9d8ca85..d900334 100644
--- a/web/src/locales/en/data.json
+++ b/web/src/locales/en/data.json
@@ -267,6 +267,7 @@
     "Remove the line to clear it": "Remove that line, then start the agent 
again.",
     "Reported from": "Reported from",
     "Resets {time}": "resets {time}",
+    "Restart the agent for it to take effect": "restart the agent for it to 
take effect",
     "Restore configuration": "Restore configuration",
     "Restore confirm hint": "Puts back the provider settings this agent had 
before Gateway first wrote them, and leaves every other setting untouched.",
     "Restore the configuration of {agent}?": "Restore the configuration of 
{agent}?",
diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json
index 3dea2ab..a6c74d8 100644
--- a/web/src/locales/zh/data.json
+++ b/web/src/locales/zh/data.json
@@ -267,6 +267,7 @@
     "Remove the line to clear it": "请删除该行,然后重新启动该Agent。",
     "Reported from": "上报来源",
     "Resets {time}": "{time}重置",
+    "Restart the agent for it to take effect": "重启Agent后生效",
     "Restore configuration": "还原配置",
     "Restore confirm hint": "把该Agent在Gateway首次写入之前的供应商配置还原回去,其他配置保持不变。",
     "Restore the configuration of {agent}?": "还原{agent}的配置?",
diff --git a/web/src/types.ts b/web/src/types.ts
index 10c624c..6186a3e 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -239,6 +239,12 @@ export interface SavedAccount {
   current?: boolean;
 }
 
+/** The account a swap put in place, and what it takes for the agent to use 
it. */
+export interface SwitchedAccount extends SavedAccount {
+  /** Set when the agent was running: it keeps the sign-in it started with. */
+  restart?: boolean;
+}
+
 /** One rate limit window of an account: what is left of it, and when it 
fills. */
 export interface AccountUsageWindow {
   /** What is left of the window, 0 to 100. */


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to