mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
feat(models): list auth profiles
This commit is contained in:
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
|
- Models/auth: add `openclaw models auth list [--provider <id>] [--json]` so users can inspect saved per-agent auth profiles without dumping secrets or hitting the old “too many arguments” path. Thanks @vincentkoc.
|
||||||
- Control UI/header: show the active agent name in dashboard breadcrumbs without adding the current session key, keeping non-chat views oriented without crowding the topbar.
|
- Control UI/header: show the active agent name in dashboard breadcrumbs without adding the current session key, keeping non-chat views oriented without crowding the topbar.
|
||||||
- Control UI/cron: make the New Job sidebar collapsible so the jobs list can reclaim space while keeping the form one click away. Thanks @BunsDev.
|
- Control UI/cron: make the New Job sidebar collapsible so the jobs list can reclaim space while keeping the form one click away. Thanks @BunsDev.
|
||||||
- Gateway/startup: keep model-catalog test helpers, run-session lookup code, QR pairing helpers, and TypeBox memory-tool schema construction out of hot startup import paths, reducing default gateway benchmark plugin-load and memory pressure.
|
- Gateway/startup: keep model-catalog test helpers, run-session lookup code, QR pairing helpers, and TypeBox memory-tool schema construction out of hot startup import paths, reducing default gateway benchmark plugin-load and memory pressure.
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ openclaw models fallbacks list
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
openclaw models auth add
|
openclaw models auth add
|
||||||
|
openclaw models auth list [--provider <id>] [--json]
|
||||||
openclaw models auth login --provider <id>
|
openclaw models auth login --provider <id>
|
||||||
openclaw models auth setup-token --provider <id>
|
openclaw models auth setup-token --provider <id>
|
||||||
openclaw models auth paste-token
|
openclaw models auth paste-token
|
||||||
@@ -171,16 +172,22 @@ openclaw models auth paste-token
|
|||||||
flow (OAuth/API key) or guide you into manual token paste, depending on the
|
flow (OAuth/API key) or guide you into manual token paste, depending on the
|
||||||
provider you choose.
|
provider you choose.
|
||||||
|
|
||||||
|
`models auth list` lists saved auth profiles for the selected agent without
|
||||||
|
printing token, API-key, or OAuth secret material. Use `--provider <id>` to
|
||||||
|
filter to one provider, such as `openai-codex`, and `--json` for scripting.
|
||||||
|
|
||||||
`models auth login` runs a provider plugin’s auth flow (OAuth/API key). Use
|
`models auth login` runs a provider plugin’s auth flow (OAuth/API key). Use
|
||||||
`openclaw plugins list` to see which providers are installed.
|
`openclaw plugins list` to see which providers are installed.
|
||||||
Use `openclaw models auth --agent <id> <subcommand>` to write auth results to a
|
Use `openclaw models auth --agent <id> <subcommand>` to write auth results to a
|
||||||
specific configured agent store. The parent `--agent` flag is honored by
|
specific configured agent store. The parent `--agent` flag is honored by
|
||||||
`add`, `login`, `setup-token`, `paste-token`, and `login-github-copilot`.
|
`add`, `list`, `login`, `setup-token`, `paste-token`, and
|
||||||
|
`login-github-copilot`.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openclaw models auth login --provider openai-codex --set-default
|
openclaw models auth login --provider openai-codex --set-default
|
||||||
|
openclaw models auth list --provider openai-codex
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const mocks = vi.hoisted(() => ({
|
|||||||
modelsSetImageCommand: vi.fn().mockResolvedValue(undefined),
|
modelsSetImageCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
noopAsync: vi.fn(async () => undefined),
|
noopAsync: vi.fn(async () => undefined),
|
||||||
modelsAuthAddCommand: vi.fn().mockResolvedValue(undefined),
|
modelsAuthAddCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
|
modelsAuthListCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
modelsAuthLoginCommand: vi.fn().mockResolvedValue(undefined),
|
modelsAuthLoginCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
modelsAuthPasteTokenCommand: vi.fn().mockResolvedValue(undefined),
|
modelsAuthPasteTokenCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
modelsAuthSetupTokenCommand: vi.fn().mockResolvedValue(undefined),
|
modelsAuthSetupTokenCommand: vi.fn().mockResolvedValue(undefined),
|
||||||
@@ -16,6 +17,7 @@ const mocks = vi.hoisted(() => ({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
modelsAuthAddCommand,
|
modelsAuthAddCommand,
|
||||||
|
modelsAuthListCommand,
|
||||||
modelsAuthLoginCommand,
|
modelsAuthLoginCommand,
|
||||||
modelsAuthPasteTokenCommand,
|
modelsAuthPasteTokenCommand,
|
||||||
modelsAuthSetupTokenCommand,
|
modelsAuthSetupTokenCommand,
|
||||||
@@ -36,6 +38,9 @@ vi.mock("../commands/models/auth.js", () => ({
|
|||||||
modelsAuthPasteTokenCommand: mocks.modelsAuthPasteTokenCommand,
|
modelsAuthPasteTokenCommand: mocks.modelsAuthPasteTokenCommand,
|
||||||
modelsAuthSetupTokenCommand: mocks.modelsAuthSetupTokenCommand,
|
modelsAuthSetupTokenCommand: mocks.modelsAuthSetupTokenCommand,
|
||||||
}));
|
}));
|
||||||
|
vi.mock("../commands/models/auth-list.js", () => ({
|
||||||
|
modelsAuthListCommand: mocks.modelsAuthListCommand,
|
||||||
|
}));
|
||||||
vi.mock("../commands/models/auth-order.js", () => ({
|
vi.mock("../commands/models/auth-order.js", () => ({
|
||||||
modelsAuthOrderClearCommand: mocks.noopAsync,
|
modelsAuthOrderClearCommand: mocks.noopAsync,
|
||||||
modelsAuthOrderGetCommand: mocks.noopAsync,
|
modelsAuthOrderGetCommand: mocks.noopAsync,
|
||||||
@@ -71,6 +76,7 @@ vi.mock("../commands/models/set-image.js", () => ({
|
|||||||
describe("models cli", () => {
|
describe("models cli", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
modelsAuthAddCommand.mockClear();
|
modelsAuthAddCommand.mockClear();
|
||||||
|
modelsAuthListCommand.mockClear();
|
||||||
modelsAuthLoginCommand.mockClear();
|
modelsAuthLoginCommand.mockClear();
|
||||||
modelsAuthPasteTokenCommand.mockClear();
|
modelsAuthPasteTokenCommand.mockClear();
|
||||||
modelsAuthSetupTokenCommand.mockClear();
|
modelsAuthSetupTokenCommand.mockClear();
|
||||||
@@ -138,6 +144,12 @@ describe("models cli", () => {
|
|||||||
command: modelsAuthAddCommand,
|
command: modelsAuthAddCommand,
|
||||||
expected: { agent: "poe" },
|
expected: { agent: "poe" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "list",
|
||||||
|
args: ["models", "auth", "--agent", "poe", "list", "--provider", "openai-codex"],
|
||||||
|
command: modelsAuthListCommand,
|
||||||
|
expected: { agent: "poe", provider: "openai-codex" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "login",
|
label: "login",
|
||||||
args: ["models", "auth", "--agent", "poe", "login", "--provider", "openai-codex"],
|
args: ["models", "auth", "--agent", "poe", "login", "--provider", "openai-codex"],
|
||||||
@@ -168,6 +180,15 @@ describe("models cli", () => {
|
|||||||
expect(command).toHaveBeenCalledWith(expect.objectContaining(expected), expect.any(Object));
|
expect(command).toHaveBeenCalledWith(expect.objectContaining(expected), expect.any(Object));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("passes list-specific --agent and --json to models auth list", async () => {
|
||||||
|
await runModelsCommand(["models", "auth", "list", "--agent", "poe", "--json"]);
|
||||||
|
|
||||||
|
expect(modelsAuthListCommand).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ agent: "poe", json: true }),
|
||||||
|
expect.any(Object),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
{
|
{
|
||||||
label: "set",
|
label: "set",
|
||||||
|
|||||||
@@ -301,6 +301,28 @@ export function registerModelsCli(program: Command) {
|
|||||||
auth.help();
|
auth.help();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auth
|
||||||
|
.command("list")
|
||||||
|
.description("List saved auth profiles")
|
||||||
|
.option("--provider <id>", "Filter by provider id")
|
||||||
|
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||||
|
.option("--json", "Output JSON", false)
|
||||||
|
.action(async (opts, command) => {
|
||||||
|
const agent =
|
||||||
|
resolveOptionFromCommand<string>(command, "agent") ?? (opts.agent as string | undefined);
|
||||||
|
await runModelsCommand(async () => {
|
||||||
|
const { modelsAuthListCommand } = await import("../commands/models/auth-list.js");
|
||||||
|
await modelsAuthListCommand(
|
||||||
|
{
|
||||||
|
provider: opts.provider as string | undefined,
|
||||||
|
agent,
|
||||||
|
json: Boolean(opts.json),
|
||||||
|
},
|
||||||
|
defaultRuntime,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
auth
|
auth
|
||||||
.command("add")
|
.command("add")
|
||||||
.description("Interactive auth helper (provider auth or paste token)")
|
.description("Interactive auth helper (provider auth or paste token)")
|
||||||
|
|||||||
127
src/commands/models/auth-list.test.ts
Normal file
127
src/commands/models/auth-list.test.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
|
||||||
|
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||||
|
import type { OutputRuntimeEnv } from "../../runtime.js";
|
||||||
|
import { modelsAuthListCommand } from "./auth-list.js";
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
ensureAuthProfileStore: vi.fn(),
|
||||||
|
externalCliDiscoveryForProviderAuth: vi.fn(() => ({ kind: "none" })),
|
||||||
|
loadModelsConfig: vi.fn(),
|
||||||
|
resolveAuthProfileDisplayLabel: vi.fn(({ profileId }: { profileId: string }) => profileId),
|
||||||
|
resolveKnownAgentId: vi.fn(({ rawAgentId }: { rawAgentId?: string }) => rawAgentId ?? undefined),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../../agents/agent-scope.js", () => ({
|
||||||
|
resolveAgentDir: (_cfg: OpenClawConfig, agentId: string) => `/tmp/openclaw/agents/${agentId}`,
|
||||||
|
resolveDefaultAgentId: () => "main",
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../../agents/auth-profiles.js", () => ({
|
||||||
|
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
|
||||||
|
externalCliDiscoveryForProviderAuth: mocks.externalCliDiscoveryForProviderAuth,
|
||||||
|
resolveAuthProfileDisplayLabel: mocks.resolveAuthProfileDisplayLabel,
|
||||||
|
resolveAuthStatePathForDisplay: (agentDir: string) => `${agentDir}/auth-state.json`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("./load-config.js", () => ({
|
||||||
|
loadModelsConfig: mocks.loadModelsConfig,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("./shared.js", () => ({
|
||||||
|
resolveKnownAgentId: mocks.resolveKnownAgentId,
|
||||||
|
}));
|
||||||
|
|
||||||
|
function createRuntime(): OutputRuntimeEnv & { logs: string[]; jsonPayloads: unknown[] } {
|
||||||
|
const logs: string[] = [];
|
||||||
|
const jsonPayloads: unknown[] = [];
|
||||||
|
return {
|
||||||
|
logs,
|
||||||
|
jsonPayloads,
|
||||||
|
log: (...args: unknown[]) => {
|
||||||
|
logs.push(args.map((value) => String(value)).join(" "));
|
||||||
|
},
|
||||||
|
error: vi.fn(),
|
||||||
|
exit: vi.fn((code: number) => {
|
||||||
|
throw new Error(`exit ${code}`);
|
||||||
|
}),
|
||||||
|
writeStdout: vi.fn(),
|
||||||
|
writeJson: (value: unknown) => {
|
||||||
|
jsonPayloads.push(value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("modelsAuthListCommand", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.loadModelsConfig.mockReset().mockResolvedValue({} as OpenClawConfig);
|
||||||
|
mocks.ensureAuthProfileStore.mockReset();
|
||||||
|
mocks.externalCliDiscoveryForProviderAuth.mockClear();
|
||||||
|
mocks.resolveAuthProfileDisplayLabel.mockClear();
|
||||||
|
mocks.resolveKnownAgentId.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters profiles by provider and redacts credential material in JSON output", async () => {
|
||||||
|
const store: AuthProfileStore = {
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
"openai-codex:user@example.com": {
|
||||||
|
type: "oauth",
|
||||||
|
provider: "openai-codex",
|
||||||
|
access: "access-secret",
|
||||||
|
refresh: "refresh-secret",
|
||||||
|
expires: 1_800_000_000_000,
|
||||||
|
email: "user@example.com",
|
||||||
|
},
|
||||||
|
"anthropic:manual": {
|
||||||
|
type: "token",
|
||||||
|
provider: "anthropic",
|
||||||
|
token: "token-secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usageStats: {
|
||||||
|
"openai-codex:user@example.com": {
|
||||||
|
cooldownUntil: 1_800_000_010_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mocks.ensureAuthProfileStore.mockReturnValue(store);
|
||||||
|
const runtime = createRuntime();
|
||||||
|
|
||||||
|
await modelsAuthListCommand({ provider: "OpenAI-Codex", agent: "coder", json: true }, runtime);
|
||||||
|
|
||||||
|
expect(mocks.externalCliDiscoveryForProviderAuth).toHaveBeenCalledWith({
|
||||||
|
cfg: {},
|
||||||
|
provider: "openai-codex",
|
||||||
|
});
|
||||||
|
expect(runtime.jsonPayloads).toHaveLength(1);
|
||||||
|
expect(JSON.stringify(runtime.jsonPayloads[0])).not.toContain("secret");
|
||||||
|
expect(runtime.jsonPayloads[0]).toMatchObject({
|
||||||
|
agentId: "coder",
|
||||||
|
provider: "openai-codex",
|
||||||
|
profiles: [
|
||||||
|
{
|
||||||
|
id: "openai-codex:user@example.com",
|
||||||
|
provider: "openai-codex",
|
||||||
|
type: "oauth",
|
||||||
|
email: "user@example.com",
|
||||||
|
expiresAt: "2027-01-15T08:00:00.000Z",
|
||||||
|
cooldownUntil: "2027-01-15T08:00:10.000Z",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prints an empty profile list without failing", async () => {
|
||||||
|
mocks.ensureAuthProfileStore.mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
const runtime = createRuntime();
|
||||||
|
|
||||||
|
await modelsAuthListCommand({}, runtime);
|
||||||
|
|
||||||
|
expect(runtime.logs).toEqual([
|
||||||
|
"Agent: main",
|
||||||
|
"Auth state file: /tmp/openclaw/agents/main/auth-state.json",
|
||||||
|
"Profiles: (none)",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
144
src/commands/models/auth-list.ts
Normal file
144
src/commands/models/auth-list.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { resolveAgentDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||||
|
import {
|
||||||
|
ensureAuthProfileStore,
|
||||||
|
externalCliDiscoveryForProviderAuth,
|
||||||
|
resolveAuthProfileDisplayLabel,
|
||||||
|
resolveAuthStatePathForDisplay,
|
||||||
|
type AuthProfileCredential,
|
||||||
|
type AuthProfileStore,
|
||||||
|
type ProfileUsageStats,
|
||||||
|
} from "../../agents/auth-profiles.js";
|
||||||
|
import { normalizeProviderId } from "../../agents/model-selection.js";
|
||||||
|
import { type RuntimeEnv, writeRuntimeJson } from "../../runtime.js";
|
||||||
|
import { shortenHomePath } from "../../utils.js";
|
||||||
|
import { loadModelsConfig } from "./load-config.js";
|
||||||
|
import { resolveKnownAgentId } from "./shared.js";
|
||||||
|
|
||||||
|
type AuthProfileSummary = {
|
||||||
|
id: string;
|
||||||
|
provider: string;
|
||||||
|
type: AuthProfileCredential["type"];
|
||||||
|
label: string;
|
||||||
|
email?: string;
|
||||||
|
displayName?: string;
|
||||||
|
expiresAt?: string;
|
||||||
|
cooldownUntil?: string;
|
||||||
|
disabledUntil?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolveTargetAgent(
|
||||||
|
cfg: Awaited<ReturnType<typeof loadModelsConfig>>,
|
||||||
|
raw?: string,
|
||||||
|
): {
|
||||||
|
agentId: string;
|
||||||
|
agentDir: string;
|
||||||
|
} {
|
||||||
|
const agentId = resolveKnownAgentId({ cfg, rawAgentId: raw }) ?? resolveDefaultAgentId(cfg);
|
||||||
|
const agentDir = resolveAgentDir(cfg, agentId);
|
||||||
|
return { agentId, agentDir };
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTimestamp(value: number | undefined): string | undefined {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return new Date(value).toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveProfileExpiry(profile: AuthProfileCredential): string | undefined {
|
||||||
|
return profile.type === "api_key" ? undefined : formatTimestamp(profile.expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeProfile(params: {
|
||||||
|
cfg: Awaited<ReturnType<typeof loadModelsConfig>>;
|
||||||
|
store: AuthProfileStore;
|
||||||
|
profileId: string;
|
||||||
|
profile: AuthProfileCredential;
|
||||||
|
usage?: ProfileUsageStats;
|
||||||
|
}): AuthProfileSummary {
|
||||||
|
return {
|
||||||
|
id: params.profileId,
|
||||||
|
provider: normalizeProviderId(params.profile.provider),
|
||||||
|
type: params.profile.type,
|
||||||
|
label: resolveAuthProfileDisplayLabel({
|
||||||
|
cfg: params.cfg,
|
||||||
|
store: params.store,
|
||||||
|
profileId: params.profileId,
|
||||||
|
}),
|
||||||
|
...(params.profile.email ? { email: params.profile.email } : {}),
|
||||||
|
...(params.profile.displayName ? { displayName: params.profile.displayName } : {}),
|
||||||
|
...(resolveProfileExpiry(params.profile)
|
||||||
|
? { expiresAt: resolveProfileExpiry(params.profile) }
|
||||||
|
: {}),
|
||||||
|
...(formatTimestamp(params.usage?.cooldownUntil)
|
||||||
|
? { cooldownUntil: formatTimestamp(params.usage?.cooldownUntil) }
|
||||||
|
: {}),
|
||||||
|
...(formatTimestamp(params.usage?.disabledUntil)
|
||||||
|
? { disabledUntil: formatTimestamp(params.usage?.disabledUntil) }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProfileLine(profile: AuthProfileSummary): string {
|
||||||
|
const details = [`${profile.provider}/${profile.type}`];
|
||||||
|
if (profile.expiresAt) {
|
||||||
|
details.push(`expires ${profile.expiresAt}`);
|
||||||
|
}
|
||||||
|
if (profile.cooldownUntil) {
|
||||||
|
details.push(`cooldown until ${profile.cooldownUntil}`);
|
||||||
|
}
|
||||||
|
if (profile.disabledUntil) {
|
||||||
|
details.push(`disabled until ${profile.disabledUntil}`);
|
||||||
|
}
|
||||||
|
return `- ${profile.label} [${details.join("; ")}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function modelsAuthListCommand(
|
||||||
|
opts: { provider?: string; agent?: string; json?: boolean },
|
||||||
|
runtime: RuntimeEnv,
|
||||||
|
) {
|
||||||
|
const cfg = await loadModelsConfig({ commandName: "models auth list", runtime });
|
||||||
|
const { agentId, agentDir } = resolveTargetAgent(cfg, opts.agent);
|
||||||
|
const provider = opts.provider?.trim() ? normalizeProviderId(opts.provider) : undefined;
|
||||||
|
const store = ensureAuthProfileStore(
|
||||||
|
agentDir,
|
||||||
|
provider ? { externalCli: externalCliDiscoveryForProviderAuth({ cfg, provider }) } : undefined,
|
||||||
|
);
|
||||||
|
const profiles = Object.entries(store.profiles)
|
||||||
|
.map(([profileId, profile]) =>
|
||||||
|
summarizeProfile({
|
||||||
|
cfg,
|
||||||
|
store,
|
||||||
|
profileId,
|
||||||
|
profile,
|
||||||
|
usage: store.usageStats?.[profileId],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter((profile) => !provider || profile.provider === provider)
|
||||||
|
.toSorted((a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id));
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
writeRuntimeJson(runtime, {
|
||||||
|
agentId,
|
||||||
|
agentDir: shortenHomePath(agentDir),
|
||||||
|
authStatePath: shortenHomePath(resolveAuthStatePathForDisplay(agentDir)),
|
||||||
|
provider: provider ?? null,
|
||||||
|
profiles,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.log(`Agent: ${agentId}`);
|
||||||
|
runtime.log(`Auth state file: ${shortenHomePath(resolveAuthStatePathForDisplay(agentDir))}`);
|
||||||
|
if (provider) {
|
||||||
|
runtime.log(`Provider: ${provider}`);
|
||||||
|
}
|
||||||
|
if (profiles.length === 0) {
|
||||||
|
runtime.log("Profiles: (none)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runtime.log("Profiles:");
|
||||||
|
for (const profile of profiles) {
|
||||||
|
runtime.log(formatProfileLine(profile));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user