chore: add buildOutput RTK filter, drop legacy cloud sync, internal cleanup
- feat(rtk): buildOutput filter + autodetect for npm/yarn/cargo logs - chore: remove unused cloud sync module and related routes - ui: hide deprecated providers (qwen, iflow, antigravity) - chore: minor tray/cli/internal adjustments
This commit is contained in:
parent
21ea744c72
commit
3cca2252a6
26 changed files with 871 additions and 535 deletions
|
|
@ -1,14 +1,16 @@
|
|||
// Provider definitions
|
||||
|
||||
const RISK_NOTICE = "⚠️ Risk Notice: This provider uses a subscription/OAuth session not officially licensed for proxy/router use. Account may be restricted or banned. Use at your own risk.";
|
||||
|
||||
// Free Providers (kiro first, iflow last)
|
||||
export const FREE_PROVIDERS = {
|
||||
kiro: { id: "kiro", alias: "kr", name: "Kiro AI", icon: "psychology_alt", color: "#FF6B35", website: "https://kiro.dev", notice: { signupUrl: "https://kiro.dev" } },
|
||||
qwen: { id: "qwen", alias: "qw", name: "Qwen Code", icon: "psychology", color: "#10B981", mediaPriority: 999, deprecated: true, deprecationNotice: "Qwen OAuth free tier was discontinued by Alibaba on 2026-04-15. New connections will not work.", website: "https://chat.qwen.ai", notice: { signupUrl: "https://chat.qwen.ai" }, serviceKinds: ["llm", "tts"], ttsConfig: { baseUrl: "http://localhost:8000/v1/audio/speech", authType: "none", authHeader: "none", format: "openai", models: [{ id: "qwen3-tts", name: "Qwen3 TTS" }] } },
|
||||
"gemini-cli": { id: "gemini-cli", alias: "gc", name: "Gemini CLI", icon: "terminal", color: "#4285F4", deprecated: true, deprecationNotice: "Gemini CLI is designed exclusively for Gemini CLI. Using it with other tools (OpenClaw, Claude, Codex...) may result in account restrictions or bans.", website: "https://github.com/google-gemini/gemini-cli", notice: { signupUrl: "https://github.com/google-gemini/gemini-cli" } },
|
||||
qwen: { id: "qwen", alias: "qw", name: "Qwen Code", icon: "psychology", color: "#10B981", mediaPriority: 999, hidden: true, deprecated: true, deprecationNotice: "Qwen OAuth free tier was discontinued by Alibaba on 2026-04-15. New connections will not work.", website: "https://chat.qwen.ai", notice: { signupUrl: "https://chat.qwen.ai" }, serviceKinds: ["llm", "tts"], ttsConfig: { baseUrl: "http://localhost:8000/v1/audio/speech", authType: "none", authHeader: "none", format: "openai", models: [{ id: "qwen3-tts", name: "Qwen3 TTS" }] } },
|
||||
"gemini-cli": { id: "gemini-cli", alias: "gc", name: "Gemini CLI", icon: "terminal", color: "#4285F4", deprecated: true, deprecationNotice: RISK_NOTICE, website: "https://github.com/google-gemini/gemini-cli", notice: { signupUrl: "https://github.com/google-gemini/gemini-cli" } },
|
||||
// gitlab: { id: "gitlab", alias: "gl", name: "GitLab Duo", icon: "code", color: "#FC6D26" },
|
||||
// codebuddy: { id: "codebuddy", alias: "cb", name: "CodeBuddy", icon: "smart_toy", color: "#006EFF" },
|
||||
// qoder: { id: "qoder", alias: "qd", name: "Qoder AI", icon: "water_drop", color: "#EC4899" },
|
||||
iflow: { id: "iflow", alias: "if", name: "iFlow AI", icon: "water_drop", color: "#6366F1", website: "https://iflow.cn", notice: { signupUrl: "https://iflow.cn" } },
|
||||
iflow: { id: "iflow", alias: "if", name: "iFlow AI", icon: "water_drop", color: "#6366F1", hidden: true, website: "https://iflow.cn", notice: { signupUrl: "https://iflow.cn" } },
|
||||
opencode: { id: "opencode", alias: "oc", name: "OpenCode Free", icon: "terminal", color: "#E87040", textIcon: "OC", noAuth: true, passthroughModels: true, modelsFetcher: { url: "https://opencode.ai/zen/v1/models", type: "opencode-free" } },
|
||||
};
|
||||
|
||||
|
|
@ -53,10 +55,10 @@ const MINIMAX_TTS_MODELS = [
|
|||
|
||||
// OAuth Providers
|
||||
export const OAUTH_PROVIDERS = {
|
||||
claude: { id: "claude", alias: "cc", name: "Claude Code", icon: "smart_toy", color: "#D97757", website: "https://claude.ai", notice: { signupUrl: "https://claude.ai" } },
|
||||
antigravity: { id: "antigravity", alias: "ag", name: "Antigravity", icon: "rocket_launch", color: "#F59E0B", deprecated: true, deprecationNotice: "AG is designed exclusively for Antigravity IDE. Using it with other tools (OpenClaw, Claude, Codex...) may result in account restrictions or bans.", website: "https://antigravity.google", notice: { signupUrl: "https://antigravity.google" } },
|
||||
codex: { id: "codex", alias: "cx", name: "OpenAI Codex", icon: "code", color: "#3B82F6", thinkingConfig: THINKING_CONFIG.effort, serviceKinds: ["llm", "image"], kindNotice: { image: "Requires a ChatGPT Plus (or higher) account. Free accounts are not supported for image generation." }, website: "https://chatgpt.com/codex", notice: { signupUrl: "https://chatgpt.com/codex" } },
|
||||
github: { id: "github", alias: "gh", name: "GitHub Copilot", icon: "code", color: "#333333", serviceKinds: ["llm", "embedding"], embeddingConfig: { baseUrl: "https://models.github.ai/inference/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "text-embedding-3-small", name: "Text Embedding 3 Small (GitHub)", dimensions: 1536 }, { id: "text-embedding-3-large", name: "Text Embedding 3 Large (GitHub)", dimensions: 3072 }] }, website: "https://github.com/features/copilot", notice: { signupUrl: "https://github.com/features/copilot" } },
|
||||
claude: { id: "claude", alias: "cc", name: "Claude Code", icon: "smart_toy", color: "#D97757", deprecated: true, deprecationNotice: RISK_NOTICE, website: "https://claude.ai", notice: { signupUrl: "https://claude.ai" } },
|
||||
antigravity: { id: "antigravity", alias: "ag", name: "Antigravity", icon: "rocket_launch", color: "#F59E0B", hidden: true, deprecated: true, deprecationNotice: "AG is designed exclusively for Antigravity IDE. Using it with other tools (OpenClaw, Claude, Codex...) may result in account restrictions or bans.", website: "https://antigravity.google", notice: { signupUrl: "https://antigravity.google" } },
|
||||
codex: { id: "codex", alias: "cx", name: "OpenAI Codex", icon: "code", color: "#3B82F6", deprecated: true, deprecationNotice: RISK_NOTICE, thinkingConfig: THINKING_CONFIG.effort, serviceKinds: ["llm", "image"], kindNotice: { image: "Requires a ChatGPT Plus (or higher) account. Free accounts are not supported for image generation." }, website: "https://chatgpt.com/codex", notice: { signupUrl: "https://chatgpt.com/codex" } },
|
||||
github: { id: "github", alias: "gh", name: "GitHub Copilot", icon: "code", color: "#333333", deprecated: true, deprecationNotice: RISK_NOTICE, serviceKinds: ["llm", "embedding"], embeddingConfig: { baseUrl: "https://models.github.ai/inference/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "text-embedding-3-small", name: "Text Embedding 3 Small (GitHub)", dimensions: 1536 }, { id: "text-embedding-3-large", name: "Text Embedding 3 Large (GitHub)", dimensions: 3072 }] }, website: "https://github.com/features/copilot", notice: { signupUrl: "https://github.com/features/copilot" } },
|
||||
cursor: { id: "cursor", alias: "cu", name: "Cursor IDE", icon: "edit_note", color: "#00D4AA", website: "https://cursor.com", notice: { signupUrl: "https://cursor.com" } },
|
||||
// "kimi-coding": { id: "kimi-coding", alias: "kmc", name: "Kimi Coding", icon: "psychology", color: "#1E40AF", textIcon: "KC" },
|
||||
kilocode: { id: "kilocode", alias: "kc", name: "Kilo Code", icon: "code", color: "#FF6B35", textIcon: "KC", website: "https://kilocode.ai", notice: { signupUrl: "https://kilocode.ai" } },
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
import { getConsistentMachineId } from "@/shared/utils/machineId";
|
||||
import { isCloudEnabled } from "@/lib/localDb";
|
||||
|
||||
const INTERNAL_BASE_URL =
|
||||
process.env.BASE_URL ||
|
||||
process.env.NEXT_PUBLIC_BASE_URL ||
|
||||
"http://localhost:20128";
|
||||
|
||||
/**
|
||||
* Cloud sync scheduler
|
||||
*/
|
||||
export class CloudSyncScheduler {
|
||||
constructor(machineId = null, intervalMinutes = 15) {
|
||||
this.machineId = machineId;
|
||||
this.intervalMinutes = intervalMinutes;
|
||||
this.intervalId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize machine ID if not provided
|
||||
*/
|
||||
async initializeMachineId() {
|
||||
if (!this.machineId) {
|
||||
this.machineId = await getConsistentMachineId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start periodic sync (delays first sync to allow server to be ready)
|
||||
*/
|
||||
async start() {
|
||||
if (this.intervalId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.initializeMachineId();
|
||||
|
||||
// Delay first sync by 30 seconds to ensure server is ready
|
||||
setTimeout(() => {
|
||||
this.syncWithRetry().catch(() => {});
|
||||
}, 30000);
|
||||
|
||||
// Then sync periodically
|
||||
this.intervalId = setInterval(() => {
|
||||
this.syncWithRetry().catch(() => {});
|
||||
}, this.intervalMinutes * 60 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop periodic sync
|
||||
*/
|
||||
stop() {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync with retry logic (exponential backoff)
|
||||
*/
|
||||
async syncWithRetry(maxRetries = 1) {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const result = await this.sync();
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (attempt === maxRetries) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const delay = Math.min(1000 * Math.pow(2, attempt), 10000); // Max 10s
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform sync via internal API route (handles token update to db.json)
|
||||
*/
|
||||
async sync() {
|
||||
// Check if cloud is enabled
|
||||
const enabled = await isCloudEnabled();
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.initializeMachineId();
|
||||
|
||||
// Call internal API route which handles both sync and token update
|
||||
const response = await fetch(`${INTERNAL_BASE_URL}/api/sync/cloud`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ machineId: this.machineId, action: "sync" })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || "Sync failed");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if scheduler is running
|
||||
*/
|
||||
isRunning() {
|
||||
return this.intervalId !== null;
|
||||
}
|
||||
}
|
||||
|
||||
// Export a singleton instance if needed
|
||||
let cloudSyncScheduler = null;
|
||||
|
||||
export async function getCloudSyncScheduler(machineId = null, intervalMinutes = 15) {
|
||||
if (!cloudSyncScheduler) {
|
||||
cloudSyncScheduler = new CloudSyncScheduler(machineId, intervalMinutes);
|
||||
}
|
||||
return cloudSyncScheduler;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* ========== CLOUD SYNC — COMMENTED OUT (replaced by Tunnel) ==========
|
||||
import { getCloudSyncScheduler } from "@/shared/services/cloudSyncScheduler";
|
||||
========== END CLOUD SYNC ========== */
|
||||
import { cleanupProviderConnections } from "@/lib/localDb";
|
||||
|
||||
/**
|
||||
* Initialize cloud sync scheduler
|
||||
* This should be called when the application starts
|
||||
*/
|
||||
export async function initializeCloudSync() {
|
||||
try {
|
||||
// Cleanup null fields from existing data
|
||||
await cleanupProviderConnections();
|
||||
|
||||
/* ========== CLOUD SYNC — COMMENTED OUT (replaced by Tunnel) ==========
|
||||
// Create scheduler instance with default 15-minute interval
|
||||
const scheduler = await getCloudSyncScheduler(null, 15);
|
||||
|
||||
// Start the scheduler
|
||||
await scheduler.start();
|
||||
|
||||
return scheduler;
|
||||
========== END CLOUD SYNC ========== */
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("[CloudSync] Error initializing scheduler:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// For development/testing purposes
|
||||
if (typeof require !== "undefined" && require.main === module) {
|
||||
initializeCloudSync().catch(console.log);
|
||||
}
|
||||
|
||||
export default initializeCloudSync;
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import { getMachineId } from "@/shared/utils/machine";
|
||||
|
||||
// Function to get cloud URL with machine ID
|
||||
export function getCloudUrl(machineId) {
|
||||
// Get from environment or default to localhost:8787
|
||||
const cloudUrl = process.env.NEXT_PUBLIC_CLOUD_URL || "http://localhost:8787";
|
||||
return `${cloudUrl}/${machineId}/v1/chat/completions`;
|
||||
}
|
||||
|
||||
// Function to call cloud with machine ID
|
||||
export async function callCloudWithMachineId(request) {
|
||||
const machineId = await getMachineId();
|
||||
if (!machineId) {
|
||||
throw new Error("Could not get machine ID");
|
||||
}
|
||||
|
||||
const cloudUrl = getCloudUrl(machineId);
|
||||
|
||||
// Get the original request body and headers
|
||||
const body = await request.json();
|
||||
const headers = new Headers(request.headers);
|
||||
|
||||
// Remove authorization header since cloud won't need it (uses machineId instead)
|
||||
headers.delete("authorization");
|
||||
|
||||
// Call the cloud with machine ID
|
||||
const response = await fetch(cloudUrl, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Function to periodically sync provider data to cloud (now a no-op)
|
||||
export function startProviderSync(cloudUrl, intervalMs = 900000) { // Default 15 minutes
|
||||
console.log("Frontend sync is disabled. Use backend sync instead.");
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,18 +1,6 @@
|
|||
import { getConsistentMachineId } from './machineId';
|
||||
import { getConsistentMachineId } from "./machineId";
|
||||
|
||||
// Get machine ID using node-machine-id with salt
|
||||
export async function getMachineId() {
|
||||
return await getConsistentMachineId();
|
||||
}
|
||||
|
||||
// Keep sync functions for backward compatibility but make them no-ops
|
||||
// (Frontend sync is disabled - use backend sync instead)
|
||||
export async function syncProviderDataToCloud(cloudUrl) {
|
||||
console.log("Frontend sync is disabled. Use backend sync instead.");
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
export async function getProvidersNeedingRefresh() {
|
||||
console.log("Frontend sync is disabled. Use backend sync instead.");
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue