mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-03 18:23:51 +08:00
feat(desktop): add tooltip provider and model management
This commit is contained in:
@@ -13,9 +13,6 @@ import {
|
||||
SCOPES,
|
||||
} from "./google_oauth.ts";
|
||||
|
||||
export const CALLBACK_PORT = 51121;
|
||||
export const CALLBACK_PATH = "/oauth-callback";
|
||||
|
||||
const AUTHORIZATION_LIFETIME_MS = 5 * 60 * 1000;
|
||||
|
||||
type Session = { createdAtMs: number };
|
||||
@@ -151,7 +148,6 @@ export const antigravityAuthorizationCodeOAuth: OAuth2AuthorizationCodeAddMethod
|
||||
"en-US": "Authorize Antigravity with your Google Account for Gemini and Claude models.",
|
||||
"zh-CN": "使用 Google 账号完成 Antigravity 授权,以使用 Gemini 与 Claude 模型。",
|
||||
},
|
||||
callback: { port: CALLBACK_PORT, path: CALLBACK_PATH },
|
||||
begin,
|
||||
complete,
|
||||
};
|
||||
|
||||
@@ -33,6 +33,10 @@ function context(requests: Array<{ url: string; body?: string }>): PluginContext
|
||||
}
|
||||
|
||||
Deno.test("authorization URL uses Core-owned state, callback, and PKCE challenge", async () => {
|
||||
assert(
|
||||
antigravityAuthorizationCodeOAuth.callback?.port === undefined,
|
||||
"Antigravity must let Core allocate an available loopback port",
|
||||
);
|
||||
const result = await antigravityAuthorizationCodeOAuth.begin(
|
||||
{
|
||||
redirectUri: "http://127.0.0.1:51121/oauth-callback",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"apiVersion": 1,
|
||||
"id": "dev.cursorbyok.plugins.antigravity-auth",
|
||||
"name": "Antigravity",
|
||||
"version": "0.3.0",
|
||||
"author": "Antigravity",
|
||||
"version": "0.3.1",
|
||||
"author": "@masudranaxpert",
|
||||
"minAppVersion": "0.1.0",
|
||||
"icon": "assets/antigravity.svg",
|
||||
"entry": "main.ts",
|
||||
|
||||
@@ -177,6 +177,10 @@ pub fn api_router(service: ControlService) -> Router {
|
||||
"/__byok-api__/api/plugins/{plugin_id}/providers/{provider_id}/models/sync",
|
||||
post(plugins::sync_models),
|
||||
)
|
||||
.route(
|
||||
"/__byok-api__/api/plugins/{plugin_id}/providers/{provider_id}/models/enabled",
|
||||
put(plugins::set_model_enabled),
|
||||
)
|
||||
.route(
|
||||
"/__byok-api__/api/settings/observability",
|
||||
get(settings::get).put(settings::update),
|
||||
|
||||
@@ -105,6 +105,25 @@ pub async fn sync_models(
|
||||
Ok(Json(serde_json::json!({ "models": count })))
|
||||
}
|
||||
|
||||
pub async fn set_model_enabled(
|
||||
State(service): State<ControlService>,
|
||||
Path((plugin_id, provider_id)): Path<(String, String)>,
|
||||
Json(input): Json<serde_json::Value>,
|
||||
) -> Result<StatusCode> {
|
||||
let model_id = input
|
||||
.get("modelId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.ok_or_else(|| crate::Error::Config("modelId must be a string".into()))?;
|
||||
let enabled = input
|
||||
.get("enabled")
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
.ok_or_else(|| crate::Error::Config("enabled must be a boolean".into()))?;
|
||||
service
|
||||
.plugin_set_model_enabled(&plugin_id, &provider_id, model_id, enabled)
|
||||
.await?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
pub async fn runtime_status(
|
||||
State(service): State<ControlService>,
|
||||
) -> Result<Json<PluginRuntimeStatus>> {
|
||||
|
||||
@@ -241,6 +241,18 @@ impl ControlService {
|
||||
self.plugins.sync_models(plugin_id, provider_id).await
|
||||
}
|
||||
|
||||
pub async fn plugin_set_model_enabled(
|
||||
&self,
|
||||
plugin_id: &str,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<()> {
|
||||
self.plugins
|
||||
.set_model_enabled(plugin_id, provider_id, model_id, enabled)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove_plugin_configuration(&self, plugin_id: &str) -> Result<()> {
|
||||
self.plugins.remove(plugin_id).await
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ const DIFF_SINGLE_LIMIT: usize = 16_000;
|
||||
const PREVIOUS_COMMIT_LIMIT: usize = 12;
|
||||
const EXPLICIT_CONTEXT_LIMIT: usize = 20_000;
|
||||
const GENERATION_TIMEOUT: Duration = Duration::from_secs(180);
|
||||
const COMMIT_MAX_OUTPUT_TOKENS: u64 = 30_000;
|
||||
|
||||
pub async fn write_git_commit_message(
|
||||
State(registry): State<TransportRegistry>,
|
||||
@@ -142,7 +143,10 @@ fn build_invocation(
|
||||
instructions: settings.effective_prompt().to_owned(),
|
||||
tools: Vec::new(),
|
||||
},
|
||||
model: ModelSpec::new(model_id.to_owned()),
|
||||
model: ModelSpec {
|
||||
max_output_tokens: Some(COMMIT_MAX_OUTPUT_TOKENS),
|
||||
..ModelSpec::new(model_id.to_owned())
|
||||
},
|
||||
history: vec![ProjectedMessage {
|
||||
message_id: "commit-message".into(),
|
||||
role: Role::User,
|
||||
|
||||
@@ -831,6 +831,7 @@ mod tests {
|
||||
provider_type: "test".into(),
|
||||
max_output_tokens: None,
|
||||
images: false,
|
||||
enabled: true,
|
||||
});
|
||||
assert_eq!(details.model_id, "plugin:test/provider/model");
|
||||
let agent::model_details::Credentials::ApiKeyCredentials(credentials) =
|
||||
|
||||
@@ -121,6 +121,7 @@ pub struct PluginModelDescriptor {
|
||||
pub provider_type: String,
|
||||
pub max_output_tokens: Option<u64>,
|
||||
pub images: bool,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
@@ -221,6 +222,7 @@ impl PluginModelDescriptor {
|
||||
provider_type: provider.provider_type.clone(),
|
||||
max_output_tokens: model.max_output_tokens,
|
||||
images: model.images,
|
||||
enabled: model.enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ impl PluginRegistry {
|
||||
.models(&entry.manifest.id, &provider.id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
models.extend(stored.iter().map(|model| {
|
||||
models.extend(stored.iter().filter(|model| model.enabled).map(|model| {
|
||||
PluginModelDescriptor::new(
|
||||
&entry.manifest.id,
|
||||
&entry.manifest.name,
|
||||
@@ -326,6 +326,13 @@ impl PluginRegistry {
|
||||
"plugin '{plugin_id}' does not define OAuth method '{method_id}'"
|
||||
))
|
||||
})?;
|
||||
// 同一添加入口只有一个活跃生命周期。重新开始时先丢弃旧会话,
|
||||
// Drop 授权码会话中的 CallbackHandle 会立即释放 loopback listener。
|
||||
self.inner.oauth_sessions.lock().await.retain(|_, session| {
|
||||
session.plugin_id != plugin_id
|
||||
|| session.resource_type != resource_type
|
||||
|| session.method_id != method_id
|
||||
});
|
||||
let worker = self.worker(&entry, &executable).await;
|
||||
let session_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
@@ -876,6 +883,27 @@ impl PluginRegistry {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_model_enabled(
|
||||
&self,
|
||||
plugin_id: &str,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<()> {
|
||||
let executable = self.executable()?;
|
||||
let entry = self.find_entry(&executable, plugin_id).await?;
|
||||
let provider = find_provider(&entry, provider_id)?;
|
||||
if !provider.has_models {
|
||||
return Err(Error::Config(format!(
|
||||
"plugin provider '{provider_id}' does not enumerate models"
|
||||
)));
|
||||
}
|
||||
self.inner
|
||||
.state
|
||||
.set_model_enabled(plugin_id, provider_id, model_id, enabled)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove(&self, plugin_id: &str) -> Result<()> {
|
||||
if let Some(worker) = self.inner.workers.lock().await.remove(plugin_id) {
|
||||
worker.stop().await;
|
||||
|
||||
@@ -138,10 +138,16 @@ pub struct StoredModel {
|
||||
pub max_output_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub images: bool,
|
||||
#[serde(default = "default_model_enabled")]
|
||||
pub enabled: bool,
|
||||
#[serde(default)]
|
||||
pub private_data: serde_json::Value,
|
||||
}
|
||||
|
||||
fn default_model_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
impl StoredModel {
|
||||
pub fn from_definition(value: &serde_json::Value) -> Result<Self> {
|
||||
let object = value
|
||||
@@ -179,6 +185,7 @@ impl StoredModel {
|
||||
.get("maxOutputTokens")
|
||||
.and_then(serde_json::Value::as_u64),
|
||||
images: capability("images"),
|
||||
enabled: true,
|
||||
private_data: object
|
||||
.get("privateData")
|
||||
.cloned()
|
||||
@@ -328,6 +335,39 @@ impl PluginStateStore {
|
||||
provider_id: &str,
|
||||
models: &[StoredModel],
|
||||
) -> Result<()> {
|
||||
let previous = self.models(plugin_id, provider_id).await?;
|
||||
let models = models
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut model| {
|
||||
if let Some(old) = previous.iter().find(|old| old.id == model.id) {
|
||||
model.enabled = old.enabled;
|
||||
}
|
||||
model
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.data
|
||||
.update(
|
||||
plugin_id,
|
||||
&model_key(provider_id),
|
||||
&serde_json::to_value(models)?,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_model_enabled(
|
||||
&self,
|
||||
plugin_id: &str,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<()> {
|
||||
let mut models = self.models(plugin_id, provider_id).await?;
|
||||
let model = models
|
||||
.iter_mut()
|
||||
.find(|model| model.id == model_id)
|
||||
.ok_or_else(|| Error::RunNotFound(format!("plugin model {model_id}")))?;
|
||||
model.enabled = enabled;
|
||||
self.data
|
||||
.update(
|
||||
plugin_id,
|
||||
|
||||
Reference in New Issue
Block a user