chore: archive AgentDock v1 implementation

This commit is contained in:
leefer
2026-08-24 17:14:08 +08:00
commit f512bd58ec
66 changed files with 10663 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import path from "node:path";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const HOME = process.env.HOME || "/home/ai";
const CC_SWITCH_DATABASE = path.join(HOME, ".cc-switch", "cc-switch.db");
const ANSI = /\u001b\[[0-?]*[ -/]*[@-~]/g;
const SAFE_ID = /^[a-zA-Z0-9._-]{1,128}$/;
export const CC_SWITCH_APPS = Object.freeze([
{ id: "claude", toolId: "claude", name: "Claude Code" },
{ id: "codex", toolId: "codex", name: "Codex" },
{ id: "open-code", toolId: "opencode", name: "OpenCode" },
]);
export const MULTICA_TOOL_IDS = Object.freeze(["claude", "codex", "codebuddy", "kimi", "opencode"]);
function clean(value) {
return String(value || "").replace(ANSI, "").trim();
}
function publicEndpoint(value) {
const candidate = clean(value);
if (!candidate || candidate === "N/A") return null;
try {
const url = new URL(candidate);
return url.origin;
} catch {
return "configured";
}
}
export function parseCcProviderList(output) {
const providers = [];
let currentProviderId = null;
for (const rawLine of clean(output).split(/\r?\n/)) {
const line = rawLine.trim();
const current = line.match(/^→\s*Current:\s*(\S+)/);
if (current) currentProviderId = current[1];
if (!line.startsWith("│") || !line.includes("┆")) continue;
const columns = line.slice(1, line.endsWith("│") ? -1 : undefined).split("┆").map(clean);
if (columns.length < 4 || columns[1] === "ID") continue;
const [marker, id, name, endpoint] = columns;
if (!SAFE_ID.test(id)) continue;
providers.push({
id,
name: name || id,
endpoint: publicEndpoint(endpoint),
active: marker.includes("✓"),
});
}
if (!currentProviderId) currentProviderId = providers.find((provider) => provider.active)?.id || null;
return { currentProviderId, providers };
}
export function parseMulticaConfig(output) {
const values = {};
for (const line of clean(output).split(/\r?\n/)) {
const match = line.match(/^([a-z_]+):\s*(.*)$/);
if (match) values[match[1]] = match[2] === "(not set)" ? null : match[2];
}
return {
serverUrl: values.server_url || null,
appUrl: values.app_url || null,
workspaceId: values.workspace_id || null,
configured: Boolean(values.server_url && values.workspace_id),
};
}
async function run(command, args, options = {}) {
const { stdout, stderr } = await execFileAsync(command, args, {
timeout: options.timeout || 10000,
maxBuffer: options.maxBuffer || 1024 * 1024,
env: { ...process.env, NO_COLOR: "1", TERM: "dumb" },
});
return clean(stdout || stderr);
}
async function versionOf(command, args = ["--version"]) {
try {
const output = await run(command, args, { timeout: 8000 });
return { installed: true, version: output.split(/\r?\n/)[0] || "installed" };
} catch (error) {
if (error.code === "ENOENT") return { installed: false, version: null };
return { installed: true, version: "unknown", error: error.message };
}
}
async function ccSwitchInitialized() {
try {
await fs.access(CC_SWITCH_DATABASE);
return true;
} catch {
return false;
}
}
async function ccSwitchSnapshot() {
const availability = await versionOf("cc-switch");
const initialized = availability.installed && await ccSwitchInitialized();
const apps = await Promise.all(CC_SWITCH_APPS.map(async (app) => {
if (!initialized) return { ...app, currentProviderId: null, providers: [] };
try {
return { ...app, ...parseCcProviderList(await run("cc-switch", ["--app", app.id, "provider", "list"])) };
} catch (error) {
return { ...app, currentProviderId: null, providers: [], error: error.message };
}
}));
return {
...availability,
initialized,
apps,
unsupportedToolIds: ["codebuddy", "kimi", "qwen", "dsh"],
};
}
async function multicaSnapshot() {
const availability = await versionOf("multica", ["version"]);
if (!availability.installed) {
return { ...availability, daemon: { status: "unavailable" }, config: { configured: false }, supportedToolIds: MULTICA_TOOL_IDS };
}
let daemon = { status: "unknown" };
let config = { configured: false, serverUrl: null, appUrl: null, workspaceId: null };
try {
daemon = JSON.parse(await run("multica", ["daemon", "status", "--output", "json"]));
} catch (error) {
daemon = { status: "unknown", error: error.message };
}
try {
config = parseMulticaConfig(await run("multica", ["config", "show"]));
} catch (error) {
config = { ...config, error: error.message };
}
return { ...availability, daemon, config, supportedToolIds: MULTICA_TOOL_IDS };
}
let cachedSnapshot = null;
let cachedAt = 0;
export async function integrationSnapshot({ force = false } = {}) {
if (!force && cachedSnapshot && Date.now() - cachedAt < 5000) return cachedSnapshot;
const [ccSwitch, multica] = await Promise.all([ccSwitchSnapshot(), multicaSnapshot()]);
cachedSnapshot = { ccSwitch, multica };
cachedAt = Date.now();
return cachedSnapshot;
}
function validateCcSwitchApp(appId) {
if (!CC_SWITCH_APPS.some((app) => app.id === appId)) throw new Error("Unsupported CC Switch application");
}
let mutationQueue = Promise.resolve();
function serializeMutation(operation) {
const result = mutationQueue.then(operation, operation);
mutationQueue = result.catch(() => {});
return result;
}
export async function switchCcProvider(appId, providerId) {
validateCcSwitchApp(appId);
if (!SAFE_ID.test(providerId || "")) throw new Error("Invalid provider ID");
return serializeMutation(async () => {
const output = await run("cc-switch", ["--app", appId, "provider", "switch", providerId], { timeout: 30000 });
cachedSnapshot = null;
return { ok: true, output: output.split(/\r?\n/).slice(0, 4).join("\n") };
});
}
export async function importCcLiveConfig(appId) {
validateCcSwitchApp(appId);
return serializeMutation(async () => {
const output = await run("cc-switch", ["--app", appId, "provider", "import-live"], { timeout: 30000 });
cachedSnapshot = null;
return { ok: true, output: output.split(/\r?\n/).slice(0, 6).join("\n") };
});
}