mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-24 15:41:40 +00:00
fix: prefer Claude CLI in Anthropic onboarding
This commit is contained in:
@@ -3,7 +3,7 @@ summary: "OAuth in OpenClaw: token exchange, storage, and multi-account patterns
|
||||
read_when:
|
||||
- You want to understand OpenClaw OAuth end-to-end
|
||||
- You hit token invalidation / logout issues
|
||||
- You want setup-token or OAuth auth flows
|
||||
- You want setup-token, Claude CLI, or OAuth auth flows
|
||||
- You want multiple accounts or profile routing
|
||||
title: "OAuth"
|
||||
---
|
||||
@@ -153,10 +153,14 @@ Claude CLI path:
|
||||
3. store no new auth profile; switch model selection to `claude-cli/...`
|
||||
4. keep existing Anthropic auth profiles for rollback
|
||||
|
||||
Wizard paths:
|
||||
Interactive assistant path:
|
||||
|
||||
- `openclaw onboard` → auth choice `anthropic-cli`
|
||||
- `openclaw onboard` → auth choice `setup-token` (Anthropic)
|
||||
- `openclaw onboard` / `openclaw configure` → auth choice `anthropic-cli`
|
||||
|
||||
Manual setup-token path:
|
||||
|
||||
- `openclaw models auth setup-token --provider anthropic`
|
||||
- `openclaw models auth paste-token --provider anthropic`
|
||||
|
||||
### OpenAI Codex (ChatGPT OAuth)
|
||||
|
||||
|
||||
@@ -158,6 +158,10 @@ Onboarding shortcut:
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
Interactive `openclaw onboard` and `openclaw configure` prefer Claude CLI for
|
||||
Anthropic and do not show setup-token in the assistant picker. Setup-token
|
||||
remains supported through the manual commands above.
|
||||
|
||||
## Checking model auth status
|
||||
|
||||
```bash
|
||||
|
||||
@@ -590,7 +590,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How does Anthropic setup-token auth work?">
|
||||
`claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Choose **Anthropic token (paste setup-token)** in onboarding or paste it with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
|
||||
`claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Interactive onboarding/configure no longer shows setup-token as an assistant choice; use `openclaw models auth setup-token --provider anthropic` or paste an existing token with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Where do I find an Anthropic setup-token?">
|
||||
@@ -600,17 +600,17 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
claude setup-token
|
||||
```
|
||||
|
||||
Copy the token it prints, then choose **Anthropic token (paste setup-token)** in onboarding. If you want to run it on the gateway host, use `openclaw models auth setup-token --provider anthropic`. If you ran `claude setup-token` elsewhere, paste it on the gateway host with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
|
||||
Copy the token it prints, then either run `openclaw models auth setup-token --provider anthropic` on the gateway host or paste it there with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do you support Claude subscription auth (Claude Pro or Max)?">
|
||||
Yes. You can either:
|
||||
|
||||
- use a **setup-token**
|
||||
- reuse a local **Claude CLI** login on the gateway host with `openclaw models auth login --provider anthropic --method cli --set-default`
|
||||
- use a **setup-token** manually with `openclaw models auth setup-token --provider anthropic`
|
||||
|
||||
Setup-token is still supported. Claude CLI migration is simpler when the gateway host already runs Claude Code. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
|
||||
Claude CLI is the preferred interactive Anthropic path. Setup-token is still supported for manual config. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
|
||||
|
||||
Important: Anthropic changed third-party harness billing on **April 4, 2026
|
||||
at 12:00 PM PT / 8:00 PM BST**. Anthropic says Claude subscription limits no
|
||||
|
||||
@@ -305,6 +305,11 @@ Or in onboarding:
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
Interactive `openclaw onboard` and `openclaw configure` now prefer **Anthropic
|
||||
Claude CLI** first and **Anthropic API key** second. The setup-token flow
|
||||
remains supported through manual auth commands, but is not shown in the
|
||||
assistant picker.
|
||||
|
||||
What this does:
|
||||
|
||||
- verifies Claude CLI is already signed in on the gateway host
|
||||
@@ -339,7 +344,7 @@ you need to.
|
||||
|
||||
More details: [/gateway/cli-backends](/gateway/cli-backends)
|
||||
|
||||
## Option C: Claude setup-token
|
||||
## Option C: Claude setup-token (manual)
|
||||
|
||||
**Best for:** using your Claude subscription with Anthropic **Extra Usage**
|
||||
enabled, or while transitioning to API-key billing.
|
||||
@@ -352,25 +357,18 @@ Setup-tokens are created by the **Claude Code CLI**, not the Anthropic Console.
|
||||
claude setup-token
|
||||
```
|
||||
|
||||
Paste the token into OpenClaw (wizard: **Anthropic token (paste setup-token)**), or run it on the gateway host:
|
||||
Then either run it on the gateway host:
|
||||
|
||||
```bash
|
||||
openclaw models auth setup-token --provider anthropic
|
||||
```
|
||||
|
||||
If you generated the token on a different machine, paste it:
|
||||
Or if you generated the token on a different machine, paste it:
|
||||
|
||||
```bash
|
||||
openclaw models auth paste-token --provider anthropic
|
||||
```
|
||||
|
||||
### CLI setup (setup-token)
|
||||
|
||||
```bash
|
||||
# Paste a setup-token during setup
|
||||
openclaw onboard --auth-choice setup-token
|
||||
```
|
||||
|
||||
### Config snippet (setup-token)
|
||||
|
||||
```json5
|
||||
|
||||
@@ -31,8 +31,8 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
|
||||
</Step>
|
||||
<Step title="Model/Auth">
|
||||
- **Anthropic API key**: uses `ANTHROPIC_API_KEY` if present or prompts for a key, then saves it for daemon use.
|
||||
- **Anthropic Claude CLI**: on macOS onboarding checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present and switches model selection to `claude-cli/...`.
|
||||
- **Anthropic token (paste setup-token)**: run `claude setup-token` on any machine, then paste the token (you can name it; blank = default).
|
||||
- **Anthropic Claude CLI**: preferred Anthropic assistant choice in onboarding/configure. On macOS onboarding checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present and switches model selection to `claude-cli/...`.
|
||||
- **Anthropic setup-token**: still supported as a manual auth flow with `openclaw models auth setup-token --provider anthropic` or `openclaw models auth paste-token --provider anthropic`, but no longer shown in the interactive assistant picker.
|
||||
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, onboarding can reuse it. Reused Codex CLI credentials stay managed by Codex CLI; OpenClaw re-reads that source on expiry instead of rotating the copied refresh token itself.
|
||||
- **OpenAI Code (Codex) subscription (OAuth)**: browser flow; paste the `code#state`.
|
||||
- Sets `agents.defaults.model` to `openai-codex/gpt-5.2` when model is unset or `openai/*`.
|
||||
|
||||
@@ -51,6 +51,19 @@ openclaw onboard --non-interactive \
|
||||
## Provider-specific examples
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Anthropic Claude CLI example">
|
||||
```bash
|
||||
openclaw onboard --non-interactive \
|
||||
--mode local \
|
||||
--auth-choice anthropic-cli \
|
||||
--gateway-port 18789 \
|
||||
--gateway-bind loopback
|
||||
```
|
||||
|
||||
Requires Claude CLI already installed and signed in on the same gateway
|
||||
host.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Gemini example">
|
||||
```bash
|
||||
openclaw onboard --non-interactive \
|
||||
@@ -182,6 +195,11 @@ openclaw onboard --non-interactive \
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
Anthropic setup-token remains supported for manual flows, but interactive
|
||||
onboarding/configure no longer offers it as an assistant choice. Use
|
||||
`openclaw models auth setup-token --provider anthropic` or
|
||||
`openclaw models auth paste-token --provider anthropic` when you need it.
|
||||
|
||||
## Add another agent
|
||||
|
||||
Use `openclaw agents add <name>` to create a separate agent with its own workspace,
|
||||
|
||||
@@ -16,7 +16,7 @@ For the short guide, see [Onboarding (CLI)](/start/wizard).
|
||||
|
||||
Local mode (default) walks you through:
|
||||
|
||||
- Model and auth setup (OpenAI Code subscription OAuth, Anthropic API key or setup token, plus MiniMax, GLM, Ollama, Moonshot, StepFun, and AI Gateway options)
|
||||
- Model and auth setup (OpenAI Code subscription OAuth, Anthropic Claude CLI or API key, plus MiniMax, GLM, Ollama, Moonshot, StepFun, and AI Gateway options)
|
||||
- Workspace location and bootstrap files
|
||||
- Gateway settings (port, bind, auth, tailscale)
|
||||
- Channels and providers (Telegram, WhatsApp, Discord, Google Chat, Mattermost plugin, Signal)
|
||||
@@ -130,15 +130,22 @@ What you set:
|
||||
Reuses a local Claude CLI login on the gateway host and switches model
|
||||
selection to `claude-cli/...`.
|
||||
|
||||
This is the preferred interactive Anthropic path in `openclaw onboard` and
|
||||
`openclaw configure`.
|
||||
|
||||
- macOS: checks Keychain item "Claude Code-credentials"
|
||||
- Linux and Windows: reuses `~/.claude/.credentials.json` if present
|
||||
|
||||
On macOS, choose "Always Allow" so launchd starts do not block.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Anthropic token (setup-token paste)">
|
||||
Run `claude setup-token` on any machine, then paste the token.
|
||||
You can name it; blank uses default.
|
||||
<Accordion title="Anthropic setup-token (manual)">
|
||||
Supported for manual config, not shown as an interactive assistant choice.
|
||||
|
||||
- Generate on any machine: `claude setup-token`
|
||||
- Run on the gateway host: `openclaw models auth setup-token --provider anthropic`
|
||||
- Or paste an existing token: `openclaw models auth paste-token --provider anthropic`
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="OpenAI Code subscription (Codex CLI reuse)">
|
||||
If `~/.codex/auth.json` exists, the wizard can reuse it.
|
||||
|
||||
@@ -66,12 +66,13 @@ Onboarding starts with **QuickStart** (defaults) vs **Advanced** (full control).
|
||||
|
||||
**Local mode (default)** walks you through these steps:
|
||||
|
||||
1. **Model/Auth** — choose any supported provider/auth flow (API key, OAuth, or setup-token), including Custom Provider
|
||||
1. **Model/Auth** — choose any supported provider/auth flow (API key, OAuth, or provider-specific manual auth), including Custom Provider
|
||||
(OpenAI-compatible, Anthropic-compatible, or Unknown auto-detect). Pick a default model.
|
||||
Security note: if this agent will run tools or process webhook/hooks content, prefer the strongest latest-generation model available and keep tool policy strict. Weaker/older tiers are easier to prompt-inject.
|
||||
For non-interactive runs, `--secret-input-mode ref` stores env-backed refs in auth profiles instead of plaintext API key values.
|
||||
In non-interactive `ref` mode, the provider env var must be set; passing inline key flags without that env var fails fast.
|
||||
In interactive runs, choosing secret reference mode lets you point at either an environment variable or a configured provider ref (`file` or `exec`), with a fast preflight validation before saving.
|
||||
For Anthropic, interactive onboarding/configure prefers **Anthropic Claude CLI** first, then **Anthropic API key**. The **setup-token** flow remains supported through manual auth commands.
|
||||
2. **Workspace** — Location for agent files (default `~/.openclaw/workspace`). Seeds bootstrap files.
|
||||
3. **Gateway** — Port, bind address, auth mode, Tailscale exposure.
|
||||
In interactive token mode, choose default plaintext token storage or opt into SecretRef.
|
||||
|
||||
@@ -382,6 +382,7 @@ export default definePluginEntry({
|
||||
choiceId: "anthropic-cli",
|
||||
choiceLabel: "Anthropic Claude CLI",
|
||||
choiceHint: "Reuse a local Claude CLI login on this host",
|
||||
assistantPriority: -20,
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
groupHint: "Claude CLI + setup-token + API key",
|
||||
@@ -409,6 +410,7 @@ export default definePluginEntry({
|
||||
choiceId: "token",
|
||||
choiceLabel: "Anthropic token (paste setup-token)",
|
||||
choiceHint: "Run `claude setup-token` elsewhere, then paste the token here",
|
||||
assistantVisibility: "manual-only",
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
groupHint: "Claude CLI + setup-token + API key",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"deprecatedChoiceIds": ["claude-cli"],
|
||||
"choiceLabel": "Anthropic Claude CLI",
|
||||
"choiceHint": "Reuse a local Claude CLI login on this host",
|
||||
"assistantPriority": -20,
|
||||
"groupId": "anthropic",
|
||||
"groupLabel": "Anthropic",
|
||||
"groupHint": "Claude CLI + setup-token + API key"
|
||||
@@ -27,6 +28,7 @@
|
||||
"choiceId": "token",
|
||||
"choiceLabel": "Anthropic token (paste setup-token)",
|
||||
"choiceHint": "Run `claude setup-token` elsewhere, then paste the token here",
|
||||
"assistantVisibility": "manual-only",
|
||||
"groupId": "anthropic",
|
||||
"groupLabel": "Anthropic",
|
||||
"groupHint": "Claude CLI + setup-token + API key"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { resolveLegacyAuthChoiceAliasesForCli } from "./auth-choice-legacy.js";
|
||||
import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
|
||||
import { resolveLegacyAuthChoiceAliasesForCli } from "./auth-choice-legacy.js";
|
||||
|
||||
export type { AuthChoiceGroupId };
|
||||
|
||||
@@ -10,6 +10,8 @@ export type AuthChoiceOption = {
|
||||
groupId?: AuthChoiceGroupId;
|
||||
groupLabel?: string;
|
||||
groupHint?: string;
|
||||
assistantPriority?: number;
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
};
|
||||
|
||||
export type AuthChoiceGroup = {
|
||||
|
||||
@@ -378,6 +378,51 @@ describe("buildAuthChoiceOptions", () => {
|
||||
expect(litellmGroup?.options.some((opt) => opt.value === "litellm-api-key")).toBe(true);
|
||||
});
|
||||
|
||||
it("prefers Anthropic Claude CLI over API key and hides manual-only setup-token in grouped selection", () => {
|
||||
resolveManifestProviderAuthChoices.mockReturnValue([
|
||||
{
|
||||
pluginId: "anthropic",
|
||||
providerId: "anthropic",
|
||||
methodId: "api-key",
|
||||
choiceId: "apiKey",
|
||||
choiceLabel: "Anthropic API key",
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
},
|
||||
{
|
||||
pluginId: "anthropic",
|
||||
providerId: "anthropic",
|
||||
methodId: "cli",
|
||||
choiceId: "anthropic-cli",
|
||||
choiceLabel: "Anthropic Claude CLI",
|
||||
assistantPriority: -20,
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
},
|
||||
{
|
||||
pluginId: "anthropic",
|
||||
providerId: "anthropic",
|
||||
methodId: "setup-token",
|
||||
choiceId: "token",
|
||||
choiceLabel: "Anthropic token (paste setup-token)",
|
||||
assistantVisibility: "manual-only",
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
},
|
||||
]);
|
||||
const { groups } = buildAuthChoiceGroups({
|
||||
store: EMPTY_STORE,
|
||||
includeSkip: false,
|
||||
});
|
||||
const anthropicGroup = groups.find((group) => group.value === "anthropic");
|
||||
|
||||
expect(anthropicGroup).toBeDefined();
|
||||
expect(anthropicGroup?.options.map((option) => option.value)).toEqual([
|
||||
"anthropic-cli",
|
||||
"apiKey",
|
||||
]);
|
||||
});
|
||||
|
||||
it("groups OpenCode Zen and Go under one OpenCode entry", () => {
|
||||
resolveManifestProviderAuthChoices.mockReturnValue([
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
|
||||
import {
|
||||
resolveManifestProviderSetupFlowContributions,
|
||||
resolveProviderSetupFlowContributions,
|
||||
@@ -10,12 +11,17 @@ import {
|
||||
type AuthChoiceOption,
|
||||
formatStaticAuthChoiceChoicesForCli,
|
||||
} from "./auth-choice-options.static.js";
|
||||
import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
|
||||
|
||||
function compareOptionLabels(a: AuthChoiceOption, b: AuthChoiceOption): number {
|
||||
return a.label.localeCompare(b.label);
|
||||
}
|
||||
|
||||
function compareAssistantOptions(a: AuthChoiceOption, b: AuthChoiceOption): number {
|
||||
const priorityA = a.assistantPriority ?? 0;
|
||||
const priorityB = b.assistantPriority ?? 0;
|
||||
return priorityA - priorityB || compareOptionLabels(a, b);
|
||||
}
|
||||
|
||||
function compareGroupLabels(a: AuthChoiceGroup, b: AuthChoiceGroup): number {
|
||||
return a.label.localeCompare(b.label);
|
||||
}
|
||||
@@ -63,6 +69,7 @@ export function formatAuthChoiceChoicesForCli(params?: {
|
||||
export function buildAuthChoiceOptions(params: {
|
||||
store: AuthProfileStore;
|
||||
includeSkip: boolean;
|
||||
assistantVisibleOnly?: boolean;
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
@@ -80,9 +87,11 @@ export function buildAuthChoiceOptions(params: {
|
||||
optionByValue.set(option.value, option);
|
||||
}
|
||||
|
||||
const options: AuthChoiceOption[] = Array.from(optionByValue.values()).toSorted(
|
||||
compareOptionLabels,
|
||||
);
|
||||
const options: AuthChoiceOption[] = Array.from(optionByValue.values())
|
||||
.toSorted(compareOptionLabels)
|
||||
.filter((option) =>
|
||||
params.assistantVisibleOnly ? option.assistantVisibility !== "manual-only" : true,
|
||||
);
|
||||
|
||||
if (params.includeSkip) {
|
||||
options.push({ value: "skip", label: "Skip for now" });
|
||||
@@ -104,6 +113,7 @@ export function buildAuthChoiceGroups(params: {
|
||||
const options = buildAuthChoiceOptions({
|
||||
...params,
|
||||
includeSkip: false,
|
||||
assistantVisibleOnly: true,
|
||||
});
|
||||
const groupsById = new Map<AuthChoiceGroupId, AuthChoiceGroup>();
|
||||
|
||||
@@ -126,7 +136,7 @@ export function buildAuthChoiceGroups(params: {
|
||||
const groups = Array.from(groupsById.values())
|
||||
.map((group) => ({
|
||||
...group,
|
||||
options: [...group.options].toSorted(compareOptionLabels),
|
||||
options: [...group.options].toSorted(compareAssistantOptions),
|
||||
}))
|
||||
.toSorted(compareGroupLabels);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ProviderPlugin } from "../plugins/types.js";
|
||||
import type { FlowContribution, FlowOption } from "./types.js";
|
||||
import { resolveManifestProviderAuthChoices } from "../plugins/provider-auth-choices.js";
|
||||
import {
|
||||
resolveProviderModelPickerEntries,
|
||||
resolveProviderWizardOptions,
|
||||
} from "../plugins/provider-wizard.js";
|
||||
import { resolvePluginProviders } from "../plugins/providers.runtime.js";
|
||||
import type { ProviderPlugin } from "../plugins/types.js";
|
||||
import type { FlowContribution, FlowOption } from "./types.js";
|
||||
import { mergeFlowContributions, sortFlowContributionsByLabel } from "./types.js";
|
||||
|
||||
export type ProviderFlowScope = "text-inference" | "image-generation";
|
||||
@@ -95,6 +95,10 @@ export function resolveManifestProviderSetupFlowContributions(params?: {
|
||||
value: choice.choiceId,
|
||||
label: choice.choiceLabel,
|
||||
...(choice.choiceHint ? { hint: choice.choiceHint } : {}),
|
||||
...(choice.assistantPriority !== undefined
|
||||
? { assistantPriority: choice.assistantPriority }
|
||||
: {}),
|
||||
...(choice.assistantVisibility ? { assistantVisibility: choice.assistantVisibility } : {}),
|
||||
...(choice.groupId && choice.groupLabel
|
||||
? {
|
||||
group: {
|
||||
@@ -142,6 +146,10 @@ export function resolveRuntimeFallbackProviderSetupFlowContributions(params?: {
|
||||
value: option.value,
|
||||
label: option.label,
|
||||
...(option.hint ? { hint: option.hint } : {}),
|
||||
...(option.assistantPriority !== undefined
|
||||
? { assistantPriority: option.assistantPriority }
|
||||
: {}),
|
||||
...(option.assistantVisibility ? { assistantVisibility: option.assistantVisibility } : {}),
|
||||
group: {
|
||||
id: option.groupId,
|
||||
label: option.groupLabel,
|
||||
|
||||
@@ -19,6 +19,8 @@ export type FlowOption<Value extends string = string> = {
|
||||
hint?: string;
|
||||
group?: FlowOptionGroup;
|
||||
docs?: FlowDocsLink;
|
||||
assistantPriority?: number;
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
};
|
||||
|
||||
export type FlowContribution<Value extends string = string> = {
|
||||
|
||||
@@ -2,11 +2,11 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { PluginCandidate } from "./discovery.js";
|
||||
import type { OpenClawPackageManifest } from "./manifest.js";
|
||||
import {
|
||||
clearPluginManifestRegistryCache,
|
||||
loadPluginManifestRegistry,
|
||||
} from "./manifest-registry.js";
|
||||
import type { OpenClawPackageManifest } from "./manifest.js";
|
||||
import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js";
|
||||
|
||||
vi.unmock("../version.js");
|
||||
@@ -389,6 +389,8 @@ describe("loadPluginManifestRegistry", () => {
|
||||
method: "api-key",
|
||||
choiceId: "openai-api-key",
|
||||
choiceLabel: "OpenAI API key",
|
||||
assistantPriority: 10,
|
||||
assistantVisibility: "visible",
|
||||
},
|
||||
],
|
||||
configSchema: { type: "object" },
|
||||
@@ -411,6 +413,8 @@ describe("loadPluginManifestRegistry", () => {
|
||||
method: "api-key",
|
||||
choiceId: "openai-api-key",
|
||||
choiceLabel: "OpenAI API key",
|
||||
assistantPriority: 10,
|
||||
assistantVisibility: "visible",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import JSON5 from "json5";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import JSON5 from "json5";
|
||||
import type { ChannelConfigRuntimeSchema } from "../channels/plugins/types.plugin.js";
|
||||
import type { PluginConfigUiHint, PluginKind } from "./types.js";
|
||||
import { MANIFEST_KEY } from "../compat/legacy-names.js";
|
||||
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import type { PluginConfigUiHint, PluginKind } from "./types.js";
|
||||
|
||||
export const PLUGIN_MANIFEST_FILENAME = "openclaw.plugin.json";
|
||||
export const PLUGIN_MANIFEST_FILENAMES = [PLUGIN_MANIFEST_FILENAME] as const;
|
||||
@@ -91,6 +91,10 @@ export type PluginManifestProviderAuthChoice = {
|
||||
/** Optional user-facing choice label/hint for grouped onboarding UI. */
|
||||
choiceLabel?: string;
|
||||
choiceHint?: string;
|
||||
/** Lower values sort earlier in interactive assistant pickers. */
|
||||
assistantPriority?: number;
|
||||
/** Keep the choice out of interactive assistant pickers while preserving manual CLI support. */
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
/** Legacy choice ids that should point users at this replacement choice. */
|
||||
deprecatedChoiceIds?: string[];
|
||||
/** Optional grouping metadata for auth-choice pickers. */
|
||||
@@ -202,6 +206,14 @@ function normalizeProviderAuthChoices(
|
||||
}
|
||||
const choiceLabel = typeof entry.choiceLabel === "string" ? entry.choiceLabel.trim() : "";
|
||||
const choiceHint = typeof entry.choiceHint === "string" ? entry.choiceHint.trim() : "";
|
||||
const assistantPriority =
|
||||
typeof entry.assistantPriority === "number" && Number.isFinite(entry.assistantPriority)
|
||||
? entry.assistantPriority
|
||||
: undefined;
|
||||
const assistantVisibility =
|
||||
entry.assistantVisibility === "manual-only" || entry.assistantVisibility === "visible"
|
||||
? entry.assistantVisibility
|
||||
: undefined;
|
||||
const deprecatedChoiceIds = normalizeStringList(entry.deprecatedChoiceIds);
|
||||
const groupId = typeof entry.groupId === "string" ? entry.groupId.trim() : "";
|
||||
const groupLabel = typeof entry.groupLabel === "string" ? entry.groupLabel.trim() : "";
|
||||
@@ -221,6 +233,8 @@ function normalizeProviderAuthChoices(
|
||||
choiceId,
|
||||
...(choiceLabel ? { choiceLabel } : {}),
|
||||
...(choiceHint ? { choiceHint } : {}),
|
||||
...(assistantPriority !== undefined ? { assistantPriority } : {}),
|
||||
...(assistantVisibility ? { assistantVisibility } : {}),
|
||||
...(deprecatedChoiceIds.length > 0 ? { deprecatedChoiceIds } : {}),
|
||||
...(groupId ? { groupId } : {}),
|
||||
...(groupLabel ? { groupLabel } : {}),
|
||||
@@ -320,7 +334,7 @@ export function loadPluginManifest(
|
||||
}
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = JSON5.parse(fs.readFileSync(opened.fd, "utf-8")) as unknown;
|
||||
raw = JSON5.parse(fs.readFileSync(opened.fd, "utf-8"));
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
|
||||
@@ -59,6 +59,8 @@ describe("provider auth choice manifest helpers", () => {
|
||||
method: "api-key",
|
||||
choiceId: "openai-api-key",
|
||||
choiceLabel: "OpenAI API key",
|
||||
assistantPriority: 10,
|
||||
assistantVisibility: "visible",
|
||||
onboardingScopes: ["text-inference"],
|
||||
optionKey: "openaiApiKey",
|
||||
cliFlag: "--openai-api-key",
|
||||
@@ -74,6 +76,8 @@ describe("provider auth choice manifest helpers", () => {
|
||||
methodId: "api-key",
|
||||
choiceId: "openai-api-key",
|
||||
choiceLabel: "OpenAI API key",
|
||||
assistantPriority: 10,
|
||||
assistantVisibility: "visible",
|
||||
onboardingScopes: ["text-inference"],
|
||||
optionKey: "openaiApiKey",
|
||||
cliFlag: "--openai-api-key",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderIdForAuth } from "../agents/model-selection.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { normalizeProviderIdForAuth } from "../agents/model-selection.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
|
||||
@@ -10,6 +10,8 @@ export type ProviderAuthChoiceMetadata = {
|
||||
choiceId: string;
|
||||
choiceLabel: string;
|
||||
choiceHint?: string;
|
||||
assistantPriority?: number;
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
deprecatedChoiceIds?: string[];
|
||||
groupId?: string;
|
||||
groupLabel?: string;
|
||||
@@ -59,6 +61,12 @@ export function resolveManifestProviderAuthChoices(params?: {
|
||||
choiceId: choice.choiceId,
|
||||
choiceLabel: choice.choiceLabel ?? choice.choiceId,
|
||||
...(choice.choiceHint ? { choiceHint: choice.choiceHint } : {}),
|
||||
...(choice.assistantPriority !== undefined
|
||||
? { assistantPriority: choice.assistantPriority }
|
||||
: {}),
|
||||
...(choice.assistantVisibility
|
||||
? { assistantVisibility: choice.assistantVisibility }
|
||||
: {}),
|
||||
...(choice.deprecatedChoiceIds
|
||||
? { deprecatedChoiceIds: choice.deprecatedChoiceIds }
|
||||
: {}),
|
||||
|
||||
@@ -105,6 +105,14 @@ function normalizeProviderWizardSetup(params: {
|
||||
...(normalizeText(params.setup.choiceHint)
|
||||
? { choiceHint: normalizeText(params.setup.choiceHint) }
|
||||
: {}),
|
||||
...(typeof params.setup.assistantPriority === "number" &&
|
||||
Number.isFinite(params.setup.assistantPriority)
|
||||
? { assistantPriority: params.setup.assistantPriority }
|
||||
: {}),
|
||||
...(params.setup.assistantVisibility === "manual-only" ||
|
||||
params.setup.assistantVisibility === "visible"
|
||||
? { assistantVisibility: params.setup.assistantVisibility }
|
||||
: {}),
|
||||
...(normalizeText(params.setup.groupId)
|
||||
? { groupId: normalizeText(params.setup.groupId) }
|
||||
: {}),
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import {
|
||||
buildPluginSnapshotCacheEnvKey,
|
||||
resolvePluginSnapshotCacheTtlMs,
|
||||
shouldUsePluginSnapshotCache,
|
||||
} from "./cache-controls.js";
|
||||
import { resolvePluginProviders } from "./providers.runtime.js";
|
||||
import type {
|
||||
ProviderAuthMethod,
|
||||
ProviderPlugin,
|
||||
ProviderPluginWizardModelPicker,
|
||||
ProviderPluginWizardSetup,
|
||||
} from "./types.js";
|
||||
import { DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import {
|
||||
buildPluginSnapshotCacheEnvKey,
|
||||
resolvePluginSnapshotCacheTtlMs,
|
||||
shouldUsePluginSnapshotCache,
|
||||
} from "./cache-controls.js";
|
||||
import { resolvePluginProviders } from "./providers.runtime.js";
|
||||
|
||||
export const PROVIDER_PLUGIN_CHOICE_PREFIX = "provider-plugin:";
|
||||
type ProviderWizardCacheEntry = {
|
||||
@@ -45,6 +45,8 @@ export type ProviderWizardOption = {
|
||||
groupLabel: string;
|
||||
groupHint?: string;
|
||||
onboardingScopes?: Array<"text-inference" | "image-generation">;
|
||||
assistantPriority?: number;
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
};
|
||||
|
||||
export type ProviderModelPickerEntry = {
|
||||
@@ -114,6 +116,13 @@ function buildSetupOptionForMethod(params: {
|
||||
groupLabel: params.wizard.groupLabel?.trim() || params.provider.label,
|
||||
groupHint: params.wizard.groupHint?.trim(),
|
||||
...(params.wizard.onboardingScopes ? { onboardingScopes: params.wizard.onboardingScopes } : {}),
|
||||
...(typeof params.wizard.assistantPriority === "number" &&
|
||||
Number.isFinite(params.wizard.assistantPriority)
|
||||
? { assistantPriority: params.wizard.assistantPriority }
|
||||
: {}),
|
||||
...(params.wizard.assistantVisibility
|
||||
? { assistantVisibility: params.wizard.assistantVisibility }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { StreamFn } from "@mariozechner/pi-agent-core";
|
||||
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
|
||||
import type { Command } from "commander";
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import type {
|
||||
ApiKeyCredential,
|
||||
AuthProfileCredential,
|
||||
@@ -925,6 +925,8 @@ export type ProviderPluginWizardSetup = {
|
||||
choiceId?: string;
|
||||
choiceLabel?: string;
|
||||
choiceHint?: string;
|
||||
assistantPriority?: number;
|
||||
assistantVisibility?: "visible" | "manual-only";
|
||||
groupId?: string;
|
||||
groupLabel?: string;
|
||||
groupHint?: string;
|
||||
|
||||
Reference in New Issue
Block a user