mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 14:04:58 +00:00
Auth labels: handle token refs and share Pi credential conversion
This commit is contained in:
committed by
Peter Steinberger
parent
e1301c31e7
commit
cec404225d
50
src/agents/model-auth-label.test.ts
Normal file
50
src/agents/model-auth-label.test.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const ensureAuthProfileStoreMock = vi.hoisted(() => vi.fn());
|
||||||
|
const resolveAuthProfileOrderMock = vi.hoisted(() => vi.fn());
|
||||||
|
const resolveAuthProfileDisplayLabelMock = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
|
vi.mock("./auth-profiles.js", () => ({
|
||||||
|
ensureAuthProfileStore: (...args: unknown[]) => ensureAuthProfileStoreMock(...args),
|
||||||
|
resolveAuthProfileOrder: (...args: unknown[]) => resolveAuthProfileOrderMock(...args),
|
||||||
|
resolveAuthProfileDisplayLabel: (...args: unknown[]) =>
|
||||||
|
resolveAuthProfileDisplayLabelMock(...args),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("./model-auth.js", () => ({
|
||||||
|
getCustomProviderApiKey: () => undefined,
|
||||||
|
resolveEnvApiKey: () => null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { resolveModelAuthLabel } = await import("./model-auth-label.js");
|
||||||
|
|
||||||
|
describe("resolveModelAuthLabel", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
ensureAuthProfileStoreMock.mockReset();
|
||||||
|
resolveAuthProfileOrderMock.mockReset();
|
||||||
|
resolveAuthProfileDisplayLabelMock.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not throw when token profile only has tokenRef", () => {
|
||||||
|
ensureAuthProfileStoreMock.mockReturnValue({
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
"github-copilot:default": {
|
||||||
|
type: "token",
|
||||||
|
provider: "github-copilot",
|
||||||
|
tokenRef: { source: "env", id: "GITHUB_TOKEN" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as never);
|
||||||
|
resolveAuthProfileOrderMock.mockReturnValue(["github-copilot:default"]);
|
||||||
|
resolveAuthProfileDisplayLabelMock.mockReturnValue("github-copilot:default");
|
||||||
|
|
||||||
|
const label = resolveModelAuthLabel({
|
||||||
|
provider: "github-copilot",
|
||||||
|
cfg: {},
|
||||||
|
sessionEntry: { authProfileOverride: "github-copilot:default" } as never,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(label).toContain("token ref(env:GITHUB_TOKEN)");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -19,6 +19,20 @@ function formatApiKeySnippet(apiKey: string): string {
|
|||||||
return `${head}…${tail}`;
|
return `${head}…${tail}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCredentialSnippet(params: {
|
||||||
|
value: string | undefined;
|
||||||
|
ref: { source: string; id: string } | undefined;
|
||||||
|
}): string {
|
||||||
|
const value = typeof params.value === "string" ? params.value.trim() : "";
|
||||||
|
if (value) {
|
||||||
|
return formatApiKeySnippet(value);
|
||||||
|
}
|
||||||
|
if (params.ref) {
|
||||||
|
return `ref(${params.ref.source}:${params.ref.id})`;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveModelAuthLabel(params: {
|
export function resolveModelAuthLabel(params: {
|
||||||
provider?: string;
|
provider?: string;
|
||||||
cfg?: OpenClawConfig;
|
cfg?: OpenClawConfig;
|
||||||
@@ -57,9 +71,13 @@ export function resolveModelAuthLabel(params: {
|
|||||||
return `oauth${label ? ` (${label})` : ""}`;
|
return `oauth${label ? ` (${label})` : ""}`;
|
||||||
}
|
}
|
||||||
if (profile.type === "token") {
|
if (profile.type === "token") {
|
||||||
return `token ${formatApiKeySnippet(profile.token)}${label ? ` (${label})` : ""}`;
|
return `token ${formatCredentialSnippet({ value: profile.token, ref: profile.tokenRef })}${
|
||||||
|
label ? ` (${label})` : ""
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
return `api-key ${formatApiKeySnippet(profile.key ?? "")}${label ? ` (${label})` : ""}`;
|
return `api-key ${formatCredentialSnippet({ value: profile.key, ref: profile.keyRef })}${
|
||||||
|
label ? ` (${label})` : ""
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envKey = resolveEnvApiKey(providerKey);
|
const envKey = resolveEnvApiKey(providerKey);
|
||||||
|
|||||||
88
src/agents/pi-auth-credentials.ts
Normal file
88
src/agents/pi-auth-credentials.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles.js";
|
||||||
|
import { normalizeProviderId } from "./model-selection.js";
|
||||||
|
|
||||||
|
export type PiApiKeyCredential = { type: "api_key"; key: string };
|
||||||
|
export type PiOAuthCredential = {
|
||||||
|
type: "oauth";
|
||||||
|
access: string;
|
||||||
|
refresh: string;
|
||||||
|
expires: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PiCredential = PiApiKeyCredential | PiOAuthCredential;
|
||||||
|
export type PiCredentialMap = Record<string, PiCredential>;
|
||||||
|
|
||||||
|
export function convertAuthProfileCredentialToPi(cred: AuthProfileCredential): PiCredential | null {
|
||||||
|
if (cred.type === "api_key") {
|
||||||
|
const key = typeof cred.key === "string" ? cred.key.trim() : "";
|
||||||
|
if (!key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { type: "api_key", key };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred.type === "token") {
|
||||||
|
const token = typeof cred.token === "string" ? cred.token.trim() : "";
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof cred.expires === "number" &&
|
||||||
|
Number.isFinite(cred.expires) &&
|
||||||
|
Date.now() >= cred.expires
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { type: "api_key", key: token };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred.type === "oauth") {
|
||||||
|
const access = typeof cred.access === "string" ? cred.access.trim() : "";
|
||||||
|
const refresh = typeof cred.refresh === "string" ? cred.refresh.trim() : "";
|
||||||
|
if (!access || !refresh || !Number.isFinite(cred.expires) || cred.expires <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "oauth",
|
||||||
|
access,
|
||||||
|
refresh,
|
||||||
|
expires: cred.expires,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolvePiCredentialMapFromStore(store: AuthProfileStore): PiCredentialMap {
|
||||||
|
const credentials: PiCredentialMap = {};
|
||||||
|
for (const credential of Object.values(store.profiles)) {
|
||||||
|
const provider = normalizeProviderId(String(credential.provider ?? "")).trim();
|
||||||
|
if (!provider || credentials[provider]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const converted = convertAuthProfileCredentialToPi(credential);
|
||||||
|
if (converted) {
|
||||||
|
credentials[provider] = converted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function piCredentialsEqual(a: PiCredential | undefined, b: PiCredential): boolean {
|
||||||
|
if (!a || typeof a !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (a.type !== b.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.type === "api_key" && b.type === "api_key") {
|
||||||
|
return a.key === b.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.type === "oauth" && b.type === "oauth") {
|
||||||
|
return a.access === b.access && a.refresh === b.refresh && a.expires === b.expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -1,25 +1,17 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||||
import type { AuthProfileCredential } from "./auth-profiles/types.js";
|
import {
|
||||||
import { normalizeProviderId } from "./model-selection.js";
|
piCredentialsEqual,
|
||||||
|
resolvePiCredentialMapFromStore,
|
||||||
|
type PiCredential,
|
||||||
|
} from "./pi-auth-credentials.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Legacy bridge for older flows that still expect `agentDir/auth.json`.
|
* @deprecated Legacy bridge for older flows that still expect `agentDir/auth.json`.
|
||||||
* Runtime auth resolution uses auth-profiles directly and should not depend on this module.
|
* Runtime auth resolution uses auth-profiles directly and should not depend on this module.
|
||||||
*/
|
*/
|
||||||
type AuthJsonCredential =
|
type AuthJsonCredential = PiCredential;
|
||||||
| {
|
|
||||||
type: "api_key";
|
|
||||||
key: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "oauth";
|
|
||||||
access: string;
|
|
||||||
refresh: string;
|
|
||||||
expires: number;
|
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
type AuthJsonShape = Record<string, AuthJsonCredential>;
|
type AuthJsonShape = Record<string, AuthJsonCredential>;
|
||||||
|
|
||||||
@@ -36,75 +28,6 @@ async function readAuthJson(filePath: string): Promise<AuthJsonShape> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an OpenClaw auth-profiles credential to pi-coding-agent auth.json format.
|
|
||||||
* Returns null if the credential cannot be converted.
|
|
||||||
*/
|
|
||||||
function convertCredential(cred: AuthProfileCredential): AuthJsonCredential | null {
|
|
||||||
if (cred.type === "api_key") {
|
|
||||||
const key = typeof cred.key === "string" ? cred.key.trim() : "";
|
|
||||||
if (!key) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: "api_key", key };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cred.type === "token") {
|
|
||||||
// pi-coding-agent treats static tokens as api_key type
|
|
||||||
const token = typeof cred.token === "string" ? cred.token.trim() : "";
|
|
||||||
if (!token) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const expires =
|
|
||||||
typeof (cred as { expires?: unknown }).expires === "number"
|
|
||||||
? (cred as { expires: number }).expires
|
|
||||||
: Number.NaN;
|
|
||||||
if (Number.isFinite(expires) && expires > 0 && Date.now() >= expires) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: "api_key", key: token };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cred.type === "oauth") {
|
|
||||||
const accessRaw = (cred as { access?: unknown }).access;
|
|
||||||
const refreshRaw = (cred as { refresh?: unknown }).refresh;
|
|
||||||
const expiresRaw = (cred as { expires?: unknown }).expires;
|
|
||||||
|
|
||||||
const access = typeof accessRaw === "string" ? accessRaw.trim() : "";
|
|
||||||
const refresh = typeof refreshRaw === "string" ? refreshRaw.trim() : "";
|
|
||||||
const expires = typeof expiresRaw === "number" ? expiresRaw : Number.NaN;
|
|
||||||
|
|
||||||
if (!access || !refresh || !Number.isFinite(expires) || expires <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: "oauth", access, refresh, expires };
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if two auth.json credentials are equivalent.
|
|
||||||
*/
|
|
||||||
function credentialsEqual(a: AuthJsonCredential | undefined, b: AuthJsonCredential): boolean {
|
|
||||||
if (!a || typeof a !== "object") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (a.type !== b.type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.type === "api_key" && b.type === "api_key") {
|
|
||||||
return a.key === b.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.type === "oauth" && b.type === "oauth") {
|
|
||||||
return a.access === b.access && a.refresh === b.refresh && a.expires === b.expires;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pi-coding-agent's ModelRegistry/AuthStorage expects credentials in auth.json.
|
* pi-coding-agent's ModelRegistry/AuthStorage expects credentials in auth.json.
|
||||||
*
|
*
|
||||||
@@ -123,31 +46,16 @@ export async function ensurePiAuthJsonFromAuthProfiles(agentDir: string): Promis
|
|||||||
}> {
|
}> {
|
||||||
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
||||||
const authPath = path.join(agentDir, "auth.json");
|
const authPath = path.join(agentDir, "auth.json");
|
||||||
|
const providerCredentials = resolvePiCredentialMapFromStore(store);
|
||||||
// Group profiles by provider, taking the first valid profile for each
|
if (Object.keys(providerCredentials).length === 0) {
|
||||||
const providerCredentials = new Map<string, AuthJsonCredential>();
|
|
||||||
|
|
||||||
for (const [, cred] of Object.entries(store.profiles)) {
|
|
||||||
const provider = normalizeProviderId(String(cred.provider ?? "")).trim();
|
|
||||||
if (!provider || providerCredentials.has(provider)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const converted = convertCredential(cred);
|
|
||||||
if (converted) {
|
|
||||||
providerCredentials.set(provider, converted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (providerCredentials.size === 0) {
|
|
||||||
return { wrote: false, authPath };
|
return { wrote: false, authPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = await readAuthJson(authPath);
|
const existing = await readAuthJson(authPath);
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
for (const [provider, cred] of providerCredentials) {
|
for (const [provider, cred] of Object.entries(providerCredentials)) {
|
||||||
if (!credentialsEqual(existing[provider], cred)) {
|
if (!piCredentialsEqual(existing[provider], cred)) {
|
||||||
existing[provider] = cred;
|
existing[provider] = cred;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,22 +5,10 @@ import {
|
|||||||
ModelRegistry,
|
ModelRegistry,
|
||||||
} from "@mariozechner/pi-coding-agent";
|
} from "@mariozechner/pi-coding-agent";
|
||||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||||
import type { AuthProfileCredential } from "./auth-profiles.js";
|
import { resolvePiCredentialMapFromStore, type PiCredentialMap } from "./pi-auth-credentials.js";
|
||||||
import { normalizeProviderId } from "./model-selection.js";
|
|
||||||
|
|
||||||
export { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
export { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
type PiApiKeyCredential = { type: "api_key"; key: string };
|
|
||||||
type PiOAuthCredential = {
|
|
||||||
type: "oauth";
|
|
||||||
access: string;
|
|
||||||
refresh: string;
|
|
||||||
expires: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PiCredential = PiApiKeyCredential | PiOAuthCredential;
|
|
||||||
type PiCredentialMap = Record<string, PiCredential>;
|
|
||||||
|
|
||||||
function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCredentialMap) {
|
function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCredentialMap) {
|
||||||
const withInMemory = AuthStorageLike as { inMemory?: (data?: unknown) => unknown };
|
const withInMemory = AuthStorageLike as { inMemory?: (data?: unknown) => unknown };
|
||||||
if (typeof withInMemory.inMemory === "function") {
|
if (typeof withInMemory.inMemory === "function") {
|
||||||
@@ -59,61 +47,9 @@ function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCred
|
|||||||
return withRuntimeOverride;
|
return withRuntimeOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertAuthProfileCredential(cred: AuthProfileCredential): PiCredential | null {
|
|
||||||
if (cred.type === "api_key") {
|
|
||||||
const key = typeof cred.key === "string" ? cred.key.trim() : "";
|
|
||||||
if (!key) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: "api_key", key };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cred.type === "token") {
|
|
||||||
const token = typeof cred.token === "string" ? cred.token.trim() : "";
|
|
||||||
if (!token) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof cred.expires === "number" &&
|
|
||||||
Number.isFinite(cred.expires) &&
|
|
||||||
Date.now() >= cred.expires
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: "api_key", key: token };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cred.type === "oauth") {
|
|
||||||
const access = typeof cred.access === "string" ? cred.access.trim() : "";
|
|
||||||
const refresh = typeof cred.refresh === "string" ? cred.refresh.trim() : "";
|
|
||||||
if (!access || !refresh || !Number.isFinite(cred.expires) || cred.expires <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
type: "oauth",
|
|
||||||
access,
|
|
||||||
refresh,
|
|
||||||
expires: cred.expires,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolvePiCredentials(agentDir: string): PiCredentialMap {
|
function resolvePiCredentials(agentDir: string): PiCredentialMap {
|
||||||
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
||||||
const credentials: PiCredentialMap = {};
|
return resolvePiCredentialMapFromStore(store);
|
||||||
for (const credential of Object.values(store.profiles)) {
|
|
||||||
const provider = normalizeProviderId(String(credential.provider ?? "")).trim();
|
|
||||||
if (!provider || credentials[provider]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const converted = convertAuthProfileCredential(credential);
|
|
||||||
if (converted) {
|
|
||||||
credentials[provider] = converted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return credentials;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compatibility helpers for pi-coding-agent 0.50+ (discover* helpers removed).
|
// Compatibility helpers for pi-coding-agent 0.50+ (discover* helpers removed).
|
||||||
|
|||||||
24
src/commands/models/list.auth-overview.test.ts
Normal file
24
src/commands/models/list.auth-overview.test.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
||||||
|
|
||||||
|
describe("resolveProviderAuthOverview", () => {
|
||||||
|
it("does not throw when token profile only has tokenRef", () => {
|
||||||
|
const overview = resolveProviderAuthOverview({
|
||||||
|
provider: "github-copilot",
|
||||||
|
cfg: {},
|
||||||
|
store: {
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
"github-copilot:default": {
|
||||||
|
type: "token",
|
||||||
|
provider: "github-copilot",
|
||||||
|
tokenRef: { source: "env", id: "GITHUB_TOKEN" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
modelsPath: "/tmp/models.json",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(overview.profiles.labels[0]).toContain("token:ref(env:GITHUB_TOKEN)");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -12,6 +12,22 @@ import { shortenHomePath } from "../../utils.js";
|
|||||||
import { maskApiKey } from "./list.format.js";
|
import { maskApiKey } from "./list.format.js";
|
||||||
import type { ProviderAuthOverview } from "./list.types.js";
|
import type { ProviderAuthOverview } from "./list.types.js";
|
||||||
|
|
||||||
|
function formatProfileSecretLabel(params: {
|
||||||
|
value: string | undefined;
|
||||||
|
ref: { source: string; id: string } | undefined;
|
||||||
|
kind: "api-key" | "token";
|
||||||
|
}): string {
|
||||||
|
const value = typeof params.value === "string" ? params.value.trim() : "";
|
||||||
|
if (value) {
|
||||||
|
return params.kind === "token" ? `token:${maskApiKey(value)}` : maskApiKey(value);
|
||||||
|
}
|
||||||
|
if (params.ref) {
|
||||||
|
const refLabel = `ref(${params.ref.source}:${params.ref.id})`;
|
||||||
|
return params.kind === "token" ? `token:${refLabel}` : refLabel;
|
||||||
|
}
|
||||||
|
return params.kind === "token" ? "token:missing" : "missing";
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveProviderAuthOverview(params: {
|
export function resolveProviderAuthOverview(params: {
|
||||||
provider: string;
|
provider: string;
|
||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
@@ -40,10 +56,24 @@ export function resolveProviderAuthOverview(params: {
|
|||||||
return `${profileId}=missing`;
|
return `${profileId}=missing`;
|
||||||
}
|
}
|
||||||
if (profile.type === "api_key") {
|
if (profile.type === "api_key") {
|
||||||
return withUnusableSuffix(`${profileId}=${maskApiKey(profile.key ?? "")}`, profileId);
|
return withUnusableSuffix(
|
||||||
|
`${profileId}=${formatProfileSecretLabel({
|
||||||
|
value: profile.key,
|
||||||
|
ref: profile.keyRef,
|
||||||
|
kind: "api-key",
|
||||||
|
})}`,
|
||||||
|
profileId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (profile.type === "token") {
|
if (profile.type === "token") {
|
||||||
return withUnusableSuffix(`${profileId}=token:${maskApiKey(profile.token)}`, profileId);
|
return withUnusableSuffix(
|
||||||
|
`${profileId}=${formatProfileSecretLabel({
|
||||||
|
value: profile.token,
|
||||||
|
ref: profile.tokenRef,
|
||||||
|
kind: "token",
|
||||||
|
})}`,
|
||||||
|
profileId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||||
const suffix =
|
const suffix =
|
||||||
|
|||||||
Reference in New Issue
Block a user