mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-16 16:40:47 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
185
openclaw/extensions/chutes/index.ts
Normal file
185
openclaw/extensions/chutes/index.ts
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
resolveOAuthApiKeyMarker,
|
||||
type ProviderAuthContext,
|
||||
type ProviderAuthResult,
|
||||
} from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import { loginChutes } from "openclaw/plugin-sdk/provider-auth-login";
|
||||
import { normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
CHUTES_DEFAULT_MODEL_REF,
|
||||
applyChutesApiKeyConfig,
|
||||
applyChutesProviderConfig,
|
||||
} from "./onboard.js";
|
||||
import { buildChutesProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "chutes";
|
||||
|
||||
async function runChutesOAuth(ctx: ProviderAuthContext): Promise<ProviderAuthResult> {
|
||||
const isRemote = ctx.isRemote;
|
||||
const redirectUri =
|
||||
process.env.CHUTES_OAUTH_REDIRECT_URI?.trim() || "http://127.0.0.1:1456/oauth-callback";
|
||||
const scopes = process.env.CHUTES_OAUTH_SCOPES?.trim() || "openid profile chutes:invoke";
|
||||
const clientId =
|
||||
process.env.CHUTES_CLIENT_ID?.trim() ||
|
||||
(
|
||||
await ctx.prompter.text({
|
||||
message: "Enter Chutes OAuth client id",
|
||||
placeholder: "cid_xxx",
|
||||
validate: (value: string) => (value?.trim() ? undefined : "Required"),
|
||||
})
|
||||
).trim();
|
||||
const clientSecret = normalizeOptionalString(process.env.CHUTES_CLIENT_SECRET);
|
||||
|
||||
await ctx.prompter.note(
|
||||
isRemote
|
||||
? [
|
||||
"You are running in a remote/VPS environment.",
|
||||
"A URL will be shown for you to open in your LOCAL browser.",
|
||||
"After signing in, paste the redirect URL back here.",
|
||||
"",
|
||||
`Redirect URI: ${redirectUri}`,
|
||||
].join("\n")
|
||||
: [
|
||||
"Browser will open for Chutes authentication.",
|
||||
"If the callback doesn't auto-complete, paste the redirect URL.",
|
||||
"",
|
||||
`Redirect URI: ${redirectUri}`,
|
||||
].join("\n"),
|
||||
"Chutes OAuth",
|
||||
);
|
||||
|
||||
const progress = ctx.prompter.progress("Starting Chutes OAuth…");
|
||||
try {
|
||||
const { onAuth, onPrompt } = ctx.oauth.createVpsAwareHandlers({
|
||||
isRemote,
|
||||
prompter: ctx.prompter,
|
||||
runtime: ctx.runtime,
|
||||
spin: progress,
|
||||
openUrl: ctx.openUrl,
|
||||
localBrowserMessage: "Complete sign-in in browser…",
|
||||
});
|
||||
|
||||
const creds = await loginChutes({
|
||||
app: {
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectUri,
|
||||
scopes: scopes.split(/\s+/).filter(Boolean),
|
||||
},
|
||||
manual: isRemote,
|
||||
onAuth,
|
||||
onPrompt,
|
||||
onProgress: (message) => progress.update(message),
|
||||
});
|
||||
|
||||
progress.stop("Chutes OAuth complete");
|
||||
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: PROVIDER_ID,
|
||||
defaultModel: CHUTES_DEFAULT_MODEL_REF,
|
||||
access: creds.access,
|
||||
refresh: creds.refresh,
|
||||
expires: creds.expires,
|
||||
email: readStringValue(creds.email),
|
||||
credentialExtra: {
|
||||
clientId,
|
||||
...("accountId" in creds && typeof creds.accountId === "string"
|
||||
? { accountId: creds.accountId }
|
||||
: {}),
|
||||
},
|
||||
configPatch: applyChutesProviderConfig({}),
|
||||
notes: [
|
||||
"Chutes OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.",
|
||||
`Redirect URI: ${redirectUri}`,
|
||||
],
|
||||
});
|
||||
} catch (err) {
|
||||
progress.stop("Chutes OAuth failed");
|
||||
await ctx.prompter.note(
|
||||
[
|
||||
"Trouble with OAuth?",
|
||||
"Verify CHUTES_CLIENT_ID (and CHUTES_CLIENT_SECRET if required).",
|
||||
`Verify the OAuth app redirect URI includes: ${redirectUri}`,
|
||||
"Chutes docs: https://chutes.ai/docs/sign-in-with-chutes/overview",
|
||||
].join("\n"),
|
||||
"OAuth help",
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export default definePluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Chutes Provider",
|
||||
description: "Bundled Chutes.ai provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Chutes",
|
||||
docsPath: "/providers/chutes",
|
||||
envVars: ["CHUTES_API_KEY", "CHUTES_OAUTH_TOKEN"],
|
||||
auth: [
|
||||
{
|
||||
id: "oauth",
|
||||
label: "Chutes OAuth",
|
||||
hint: "Browser sign-in",
|
||||
kind: "oauth",
|
||||
wizard: {
|
||||
choiceId: "chutes",
|
||||
choiceLabel: "Chutes (OAuth)",
|
||||
choiceHint: "Browser sign-in",
|
||||
groupId: "chutes",
|
||||
groupLabel: "Chutes",
|
||||
groupHint: "OAuth + API key",
|
||||
},
|
||||
run: async (ctx) => await runChutesOAuth(ctx),
|
||||
},
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "Chutes API key",
|
||||
hint: "Open-source models including Llama, DeepSeek, and more",
|
||||
optionKey: "chutesApiKey",
|
||||
flagName: "--chutes-api-key",
|
||||
envVar: "CHUTES_API_KEY",
|
||||
promptMessage: "Enter Chutes API key",
|
||||
noteTitle: "Chutes",
|
||||
noteMessage: [
|
||||
"Chutes provides access to leading open-source models including Llama, DeepSeek, and more.",
|
||||
"Get your API key at: https://chutes.ai/settings/api-keys",
|
||||
].join("\n"),
|
||||
defaultModel: CHUTES_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["chutes"],
|
||||
applyConfig: (cfg) => applyChutesApiKeyConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "chutes-api-key",
|
||||
choiceLabel: "Chutes API key",
|
||||
groupId: "chutes",
|
||||
groupLabel: "Chutes",
|
||||
groupHint: "OAuth + API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "profile",
|
||||
run: async (ctx) => {
|
||||
const { apiKey, discoveryApiKey } = ctx.resolveProviderAuth(PROVIDER_ID, {
|
||||
oauthMarker: resolveOAuthApiKeyMarker(PROVIDER_ID),
|
||||
});
|
||||
if (!apiKey) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: {
|
||||
...(await buildChutesProvider(discoveryApiKey)),
|
||||
apiKey,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue