mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-10 00:11:19 +00:00
fix(auth): preserve copied auth profile order (#100833)
* fix(auth): preserve auth profile order when copying store to spawned agent * fix(auth): preserve copied profile order Co-authored-by: Vivek Behani <vivekbehani@me.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
3dd5339a53
commit
c432c8c014
6 changed files with 46 additions and 11 deletions
|
|
@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
|||
|
||||
### Fixes
|
||||
|
||||
- **Agent auth copy order:** preserve the source agent's portable auth-profile precedence when copying credentials to a new agent while excluding skipped profiles and transient auth state. (#100833) Thanks @machine3at.
|
||||
- **Cron edit delivery:** preserve each job's implicit delivery mode when applying partial delivery updates, so disabling best-effort delivery no longer turns detached job announcements off. (#100846) Thanks @machine3at.
|
||||
- **Control UI session creation:** keep newly created sessions at the front of the stable sidebar order after selecting another session. Thanks @shakkernerd.
|
||||
- **FTS-only memory startup:** skip plugin capability discovery when `memorySearch.provider` is explicitly `none`, avoiding an unnecessary cold-start scan.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export {
|
|||
suggestOAuthProfileIdForLegacyDefault,
|
||||
} from "./auth-profiles/repair.js";
|
||||
export {
|
||||
buildPortableAuthProfileSecretsStoreForAgentCopy,
|
||||
buildPortableAuthProfileStoreForAgentCopy,
|
||||
isAuthProfileCredentialPortableForAgentCopy,
|
||||
resolveAuthProfilePortability,
|
||||
type AuthProfilePortability,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildPortableAuthProfileSecretsStoreForAgentCopy,
|
||||
buildPortableAuthProfileStoreForAgentCopy,
|
||||
resolveAuthProfilePortability,
|
||||
} from "./portability.js";
|
||||
import type { AuthProfileCredential, AuthProfileStore } from "./types.js";
|
||||
|
|
@ -35,7 +35,7 @@ describe("auth profile portability", () => {
|
|||
},
|
||||
};
|
||||
|
||||
const portable = buildPortableAuthProfileSecretsStoreForAgentCopy(store);
|
||||
const portable = buildPortableAuthProfileStoreForAgentCopy(store);
|
||||
|
||||
expect(portable.copiedProfileIds).toEqual(["openai:api-key", "github-copilot:default"]);
|
||||
expect(portable.skippedProfileIds).toEqual(["openai:default"]);
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ export function isAuthProfileCredentialPortableForAgentCopy(
|
|||
return resolveAuthProfilePortability(credential).portable;
|
||||
}
|
||||
|
||||
/** Builds an agent-copy store containing only portable credentials. */
|
||||
export function buildPortableAuthProfileSecretsStoreForAgentCopy(store: AuthProfileStore): {
|
||||
store: AuthProfileSecretsStore;
|
||||
/** Builds an agent-copy store containing only portable credentials and their order. */
|
||||
export function buildPortableAuthProfileStoreForAgentCopy(store: AuthProfileStore): {
|
||||
store: AuthProfileStore;
|
||||
copiedProfileIds: string[];
|
||||
skippedProfileIds: string[];
|
||||
} {
|
||||
|
|
@ -79,8 +79,19 @@ export function buildPortableAuthProfileSecretsStoreForAgentCopy(store: AuthProf
|
|||
}),
|
||||
) as AuthProfileSecretsStore["profiles"];
|
||||
|
||||
const copiedSet = new Set(copiedProfileIds);
|
||||
const order = Object.fromEntries(
|
||||
Object.entries(store.order ?? {})
|
||||
.map(([provider, ids]) => [provider, ids.filter((id) => copiedSet.has(id))] as const)
|
||||
.filter(([, ids]) => ids.length > 0),
|
||||
);
|
||||
|
||||
return {
|
||||
store: { version: AUTH_STORE_VERSION, profiles },
|
||||
store: {
|
||||
version: AUTH_STORE_VERSION,
|
||||
profiles,
|
||||
...(Object.keys(order).length > 0 ? { order } : {}),
|
||||
},
|
||||
copiedProfileIds,
|
||||
skippedProfileIds,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|||
import path from "node:path";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { AUTH_STORE_VERSION } from "../agents/auth-profiles/constants.js";
|
||||
import { resolveAuthProfileOrder } from "../agents/auth-profiles/order.js";
|
||||
import { loadPersistedAuthProfileStore } from "../agents/auth-profiles/persisted.js";
|
||||
import { saveAuthProfileStore } from "../agents/auth-profiles/store.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
|
@ -237,6 +238,11 @@ describe("agents add command", () => {
|
|||
provider: "openai",
|
||||
key: "sk-test",
|
||||
},
|
||||
"openai:backup": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
key: "sk-backup",
|
||||
},
|
||||
"github-copilot:default": {
|
||||
type: "token",
|
||||
provider: "github-copilot",
|
||||
|
|
@ -250,6 +256,12 @@ describe("agents add command", () => {
|
|||
expires: Date.now() + 60_000,
|
||||
},
|
||||
},
|
||||
order: {
|
||||
openai: ["openai:oauth", "openai:backup", "openai:default"],
|
||||
"github-copilot": ["github-copilot:default"],
|
||||
},
|
||||
lastGood: { openai: "openai:default" },
|
||||
usageStats: { "openai:default": { lastUsed: 1_000 } },
|
||||
},
|
||||
sourceAgentDir,
|
||||
);
|
||||
|
|
@ -259,10 +271,21 @@ describe("agents add command", () => {
|
|||
destAgentDir,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ copied: 2, skipped: 1 });
|
||||
expect(result).toEqual({ copied: 3, skipped: 1 });
|
||||
const copied = loadPersistedAuthProfileStore(destAgentDir);
|
||||
expect(Object.keys(copied?.profiles ?? {}).toSorted()).toEqual([
|
||||
"github-copilot:default",
|
||||
"openai:backup",
|
||||
"openai:default",
|
||||
]);
|
||||
expect(copied?.order).toEqual({
|
||||
openai: ["openai:backup", "openai:default"],
|
||||
"github-copilot": ["github-copilot:default"],
|
||||
});
|
||||
expect(copied?.lastGood).toBeUndefined();
|
||||
expect(copied?.usageStats).toBeUndefined();
|
||||
expect(resolveAuthProfileOrder({ store: copied!, provider: "openai" })).toEqual([
|
||||
"openai:backup",
|
||||
"openai:default",
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
resolveDefaultAgentId,
|
||||
} from "../agents/agent-scope.js";
|
||||
import {
|
||||
buildPortableAuthProfileSecretsStoreForAgentCopy,
|
||||
buildPortableAuthProfileStoreForAgentCopy,
|
||||
ensureAuthProfileStore,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import { resolveAuthStorePath } from "../agents/auth-profiles/paths.js";
|
||||
|
|
@ -79,7 +79,7 @@ async function copyPortableAuthProfiles(params: {
|
|||
if (!sourceStore || Object.keys(sourceStore.profiles).length === 0) {
|
||||
return { copied: 0, skipped: 0 };
|
||||
}
|
||||
const portable = buildPortableAuthProfileSecretsStoreForAgentCopy(sourceStore);
|
||||
const portable = buildPortableAuthProfileStoreForAgentCopy(sourceStore);
|
||||
if (portable.copiedProfileIds.length === 0) {
|
||||
return { copied: 0, skipped: portable.skippedProfileIds.length };
|
||||
}
|
||||
|
|
@ -337,7 +337,7 @@ export async function agentsAddCommand(
|
|||
const sourceStore = loadPersistedAuthProfileStore(sourceAgentDir);
|
||||
const destStore = loadPersistedAuthProfileStore(agentDir);
|
||||
const portable = sourceStore
|
||||
? buildPortableAuthProfileSecretsStoreForAgentCopy(sourceStore)
|
||||
? buildPortableAuthProfileStoreForAgentCopy(sourceStore)
|
||||
: undefined;
|
||||
if (
|
||||
portable &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue