fix: improve /status auth label
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
- Auto-reply: fix /status usage summary filtering for the active provider.
|
- Auto-reply: fix /status usage summary filtering for the active provider.
|
||||||
- Status: show provider prefix in /status model display. (#506) — thanks @mcinteerj
|
- Status: show provider prefix in /status model display. (#506) — thanks @mcinteerj
|
||||||
- Status: compact /status with session token usage + estimated cost, add `/cost` per-response usage lines (tokens-only for OAuth).
|
- Status: compact /status with session token usage + estimated cost, add `/cost` per-response usage lines (tokens-only for OAuth).
|
||||||
|
- Status: show active auth profile and key snippet in /status.
|
||||||
- macOS: package ClawdbotKit resources and Swift 6.2 compatibility dylib to avoid launch/tool crashes. (#473) — thanks @gupsammy
|
- macOS: package ClawdbotKit resources and Swift 6.2 compatibility dylib to avoid launch/tool crashes. (#473) — thanks @gupsammy
|
||||||
- WhatsApp: group `/model list` output by provider for scannability. (#456) - thanks @mcinteerj
|
- WhatsApp: group `/model list` output by provider for scannability. (#456) - thanks @mcinteerj
|
||||||
- Hooks: allow per-hook model overrides for webhook/Gmail runs (e.g. GPT 5 Mini).
|
- Hooks: allow per-hook model overrides for webhook/Gmail runs (e.g. GPT 5 Mini).
|
||||||
|
|||||||
@@ -218,6 +218,70 @@ describe("trigger handling", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reports active auth profile and key snippet in status", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const cfg = makeCfg(home);
|
||||||
|
const agentDir = join(home, ".clawdbot", "agents", "main", "agent");
|
||||||
|
await fs.mkdir(agentDir, { recursive: true });
|
||||||
|
await fs.writeFile(
|
||||||
|
join(agentDir, "auth-profiles.json"),
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
"anthropic:work": {
|
||||||
|
type: "api_key",
|
||||||
|
provider: "anthropic",
|
||||||
|
key: "sk-test-1234567890abcdef",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lastGood: { anthropic: "anthropic:work" },
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const sessionKey = resolveSessionKey("per-sender", {
|
||||||
|
From: "+1002",
|
||||||
|
To: "+2000",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
} as Parameters<typeof resolveSessionKey>[1]);
|
||||||
|
await fs.writeFile(
|
||||||
|
cfg.session.store,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
[sessionKey]: {
|
||||||
|
sessionId: "session-auth",
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
authProfileOverride: "anthropic:work",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{
|
||||||
|
Body: "/status",
|
||||||
|
From: "+1002",
|
||||||
|
To: "+2000",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
SenderE164: "+1002",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
|
expect(text).toContain("🔑 api-key");
|
||||||
|
expect(text).toContain("…");
|
||||||
|
expect(text).toContain("(anthropic:work)");
|
||||||
|
expect(text).not.toContain("mixed");
|
||||||
|
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("ignores inline /status and runs the agent", async () => {
|
it("ignores inline /status and runs the agent", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ensureAuthProfileStore,
|
ensureAuthProfileStore,
|
||||||
listProfilesForProvider,
|
resolveAuthProfileDisplayLabel,
|
||||||
|
resolveAuthProfileOrder,
|
||||||
} from "../../agents/auth-profiles.js";
|
} from "../../agents/auth-profiles.js";
|
||||||
import {
|
import {
|
||||||
getCustomProviderApiKey,
|
getCustomProviderApiKey,
|
||||||
@@ -92,32 +93,65 @@ export type CommandContext = {
|
|||||||
to?: string;
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function formatApiKeySnippet(apiKey: string): string {
|
||||||
|
const compact = apiKey.replace(/\s+/g, "");
|
||||||
|
if (!compact) return "unknown";
|
||||||
|
const edge = compact.length >= 12 ? 6 : 4;
|
||||||
|
const head = compact.slice(0, edge);
|
||||||
|
const tail = compact.slice(-edge);
|
||||||
|
return `${head}…${tail}`;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveModelAuthLabel(
|
function resolveModelAuthLabel(
|
||||||
provider?: string,
|
provider?: string,
|
||||||
cfg?: ClawdbotConfig,
|
cfg?: ClawdbotConfig,
|
||||||
|
sessionEntry?: SessionEntry,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const resolved = provider?.trim();
|
const resolved = provider?.trim();
|
||||||
if (!resolved) return undefined;
|
if (!resolved) return undefined;
|
||||||
|
|
||||||
|
const providerKey = normalizeProviderId(resolved);
|
||||||
const store = ensureAuthProfileStore();
|
const store = ensureAuthProfileStore();
|
||||||
const profiles = listProfilesForProvider(store, resolved);
|
const profileOverride = sessionEntry?.authProfileOverride?.trim();
|
||||||
if (profiles.length > 0) {
|
const lastGood =
|
||||||
const modes = new Set(
|
store.lastGood?.[providerKey] ?? store.lastGood?.[resolved];
|
||||||
profiles
|
const order = resolveAuthProfileOrder({
|
||||||
.map((id) => store.profiles[id]?.type)
|
cfg,
|
||||||
.filter((mode): mode is "api_key" | "oauth" => Boolean(mode)),
|
store,
|
||||||
);
|
provider: providerKey,
|
||||||
if (modes.has("oauth") && modes.has("api_key")) return "mixed";
|
preferredProfile: profileOverride,
|
||||||
if (modes.has("oauth")) return "oauth";
|
});
|
||||||
if (modes.has("api_key")) return "api-key";
|
const candidates = [
|
||||||
|
profileOverride,
|
||||||
|
lastGood,
|
||||||
|
...order,
|
||||||
|
].filter(Boolean) as string[];
|
||||||
|
|
||||||
|
for (const profileId of candidates) {
|
||||||
|
const profile = store.profiles[profileId];
|
||||||
|
if (!profile || normalizeProviderId(profile.provider) !== providerKey) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const label = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||||
|
if (profile.type === "oauth") {
|
||||||
|
return `oauth${label ? ` (${label})` : ""}`;
|
||||||
|
}
|
||||||
|
const snippet = formatApiKeySnippet(profile.key);
|
||||||
|
return `api-key ${snippet}${label ? ` (${label})` : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envKey = resolveEnvApiKey(resolved);
|
const envKey = resolveEnvApiKey(providerKey);
|
||||||
if (envKey?.apiKey) {
|
if (envKey?.apiKey) {
|
||||||
return envKey.source.includes("OAUTH_TOKEN") ? "oauth" : "api-key";
|
if (envKey.source.includes("OAUTH_TOKEN")) {
|
||||||
|
return `oauth (${envKey.source})`;
|
||||||
|
}
|
||||||
|
return `api-key ${formatApiKeySnippet(envKey.apiKey)} (${envKey.source})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCustomProviderApiKey(cfg, resolved)) return "api-key";
|
const customKey = getCustomProviderApiKey(cfg, providerKey);
|
||||||
|
if (customKey) {
|
||||||
|
return `api-key ${formatApiKeySnippet(customKey)} (models.json)`;
|
||||||
|
}
|
||||||
|
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
@@ -469,7 +503,7 @@ export async function handleCommands(params: {
|
|||||||
resolvedVerbose: resolvedVerboseLevel,
|
resolvedVerbose: resolvedVerboseLevel,
|
||||||
resolvedReasoning: resolvedReasoningLevel,
|
resolvedReasoning: resolvedReasoningLevel,
|
||||||
resolvedElevated: resolvedElevatedLevel,
|
resolvedElevated: resolvedElevatedLevel,
|
||||||
modelAuth: resolveModelAuthLabel(provider, cfg),
|
modelAuth: resolveModelAuthLabel(provider, cfg, sessionEntry),
|
||||||
usageLine: usageLine ?? undefined,
|
usageLine: usageLine ?? undefined,
|
||||||
queue: {
|
queue: {
|
||||||
mode: queueSettings.mode,
|
mode: queueSettings.mode,
|
||||||
|
|||||||
Reference in New Issue
Block a user