mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-30 17:28:07 +08:00
* feat(plugins): add Agent Plugins 1.0 portable package Add agent-plugins/, an Agent Plugins 1.0 conformant package (https://agent-plugins.org/specification) that any conforming client can load: plugin.json manifest, an openviking-memory skill teaching the hook-less recall + persist loop, and an mcp.json stdio entry running a stdio -> streamable-HTTP proxy that resolves credentials from OPENVIKING_* env -> ~/.openviking/ovcli.conf -> ~/.openviking/ov.conf, same as the ov CLI. servers/shared/* are generated copies of memory-plugin-shared/lib, wired into sync.mjs / sync.test.mjs TARGETS so they cannot drift silently. config.mjs / debug-log.mjs / mcp-proxy.mjs are adapted from claude-code-memory-plugin with the hook-tuning knobs dropped. plugin.test.mjs validates spec conformance (schema URLs and matching spec versions, name rules, closed manifest root, semver, skill frontmatter, referenced files staying inside the plugin root, node --check on all .mjs) and runs in CI via pr.yml. The skill treats tree/write/edit as optional, since they only exist on servers that carry #3936. Docs: docs/{en,zh}/agent-integrations/15-agent-plugins.md, registered in the VitePress sidebar and the integration overview tables, plus a link from the three root READMEs. The docs recommend the per-client plugin whenever the harness has hooks, with the shared installer one-liner. Based on #3994 by @ZaynJarvis. Co-Authored-By: Zayn Jarvis <zaynjarvis@gmail.com> * docs(agent-plugins): pluralize README title --------- Co-authored-by: Zayn Jarvis <zaynjarvis@gmail.com>
153 lines
5.0 KiB
JavaScript
153 lines
5.0 KiB
JavaScript
/**
|
|
* Connection config loader for the OpenViking Agent Plugins 1.0 package.
|
|
*
|
|
* VENDORED (adapted) from examples/claude-code-memory-plugin/scripts/config.mjs —
|
|
* keep the resolution semantics in sync with that file. This copy is trimmed to
|
|
* the fields consumed by servers/mcp-proxy.mjs: Agent Plugins 1.0 has no hooks,
|
|
* so all of the hook-tuning knobs (recall/capture/commit/profile) are dropped,
|
|
* as is the Claude-Code-specific `claude_code` section of ov.conf.
|
|
*
|
|
* Resolution priority (highest → lowest):
|
|
* 1. Environment variables (OPENVIKING_*)
|
|
* 2. ovcli.conf (CLI client config: url, api_key, account, user)
|
|
* 3. ov.conf `server` section (local server config; url/host/port/root_api_key)
|
|
* 4. Built-in defaults (http://127.0.0.1:1933)
|
|
*
|
|
* Env vars covered:
|
|
* Connection / identity:
|
|
* OPENVIKING_URL / OPENVIKING_BASE_URL, OPENVIKING_API_KEY / OPENVIKING_BEARER_TOKEN,
|
|
* OPENVIKING_ACCOUNT, OPENVIKING_USER, OPENVIKING_PEER_ID, OPENVIKING_WORKSPACE_PEER
|
|
* Config file overrides:
|
|
* OPENVIKING_CONFIG_FILE (ov.conf), OPENVIKING_CLI_CONFIG_FILE (ovcli.conf)
|
|
* Misc:
|
|
* OPENVIKING_TIMEOUT_MS, OPENVIKING_DEBUG, OPENVIKING_DEBUG_LOG
|
|
*/
|
|
|
|
import { readFileSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { resolve as resolvePath, join } from "node:path";
|
|
|
|
import { buildUserAgent, readManifestVersion } from "./shared/credentials.mjs";
|
|
|
|
const DEFAULT_OV_CONF_PATH = join(homedir(), ".openviking", "ov.conf");
|
|
const DEFAULT_OVCLI_CONF_PATH = join(homedir(), ".openviking", "ovcli.conf");
|
|
const USER_AGENT = buildUserAgent(
|
|
"agent-plugins",
|
|
readManifestVersion(new URL("../plugin.json", import.meta.url)),
|
|
);
|
|
|
|
function num(val, fallback) {
|
|
if (typeof val === "number" && Number.isFinite(val)) return val;
|
|
if (typeof val === "string" && val.trim()) {
|
|
const n = Number(val);
|
|
if (Number.isFinite(n)) return n;
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
function str(val, fallback) {
|
|
if (typeof val === "string" && val.trim()) return val.trim();
|
|
return fallback;
|
|
}
|
|
|
|
function envBool(name) {
|
|
const v = process.env[name];
|
|
if (v == null || v === "") return undefined;
|
|
const lower = v.trim().toLowerCase();
|
|
if (lower === "0" || lower === "false" || lower === "no") return false;
|
|
if (lower === "1" || lower === "true" || lower === "yes") return true;
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Try to load and parse a JSON config file. Returns { configPath, file } or null.
|
|
*/
|
|
function tryLoadJsonFile(envVar, defaultPath) {
|
|
const configPath = resolvePath(
|
|
(process.env[envVar] || defaultPath).replace(/^~/, homedir()),
|
|
);
|
|
|
|
let raw;
|
|
try {
|
|
raw = readFileSync(configPath, "utf-8");
|
|
} catch {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return { configPath, file: JSON.parse(raw) };
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load the connection configuration for the stdio MCP proxy.
|
|
*
|
|
* Resolution: env vars → ovcli.conf → ov.conf → defaults.
|
|
*/
|
|
export function loadConfig() {
|
|
const ovConf = tryLoadJsonFile("OPENVIKING_CONFIG_FILE", DEFAULT_OV_CONF_PATH);
|
|
const cliConf = tryLoadJsonFile("OPENVIKING_CLI_CONFIG_FILE", DEFAULT_OVCLI_CONF_PATH);
|
|
|
|
const ovFile = ovConf?.file || {};
|
|
const cliFile = cliConf?.file || {};
|
|
const configPath = cliConf?.configPath || ovConf?.configPath || null;
|
|
|
|
const server = ovFile.server || {};
|
|
|
|
// baseUrl: env → ovcli.url → ov.server.url → http://{host}:{port}
|
|
const envUrl = str(process.env.OPENVIKING_URL, null) || str(process.env.OPENVIKING_BASE_URL, null);
|
|
let baseUrl;
|
|
if (envUrl) {
|
|
baseUrl = envUrl.replace(/\/+$/, "");
|
|
} else if (cliFile.url) {
|
|
baseUrl = str(cliFile.url, "").replace(/\/+$/, "");
|
|
} else if (server.url) {
|
|
baseUrl = str(server.url, "").replace(/\/+$/, "");
|
|
} else {
|
|
const host = str(server.host, "127.0.0.1").replace("0.0.0.0", "127.0.0.1");
|
|
const port = Math.floor(num(server.port, 1933));
|
|
baseUrl = `http://${host}:${port}`;
|
|
}
|
|
|
|
// apiKey: env → ovcli.api_key → ov.server.root_api_key
|
|
// Accepts OPENVIKING_BEARER_TOKEN or OPENVIKING_API_KEY (sent as Bearer either way).
|
|
const apiKey = str(process.env.OPENVIKING_BEARER_TOKEN, null)
|
|
|| str(process.env.OPENVIKING_API_KEY, null)
|
|
|| str(cliFile.api_key, null)
|
|
|| str(server.root_api_key, "");
|
|
|
|
const accountId = str(process.env.OPENVIKING_ACCOUNT, null)
|
|
|| str(cliFile.account, "");
|
|
|
|
const userId = str(process.env.OPENVIKING_USER, null)
|
|
|| str(cliFile.user, "");
|
|
|
|
const peerId = str(process.env.OPENVIKING_PEER_ID, "");
|
|
const workspacePeer = envBool("OPENVIKING_WORKSPACE_PEER") ?? true;
|
|
|
|
const timeoutMs = Math.max(1000, Math.floor(num(
|
|
process.env.OPENVIKING_TIMEOUT_MS,
|
|
15000,
|
|
)));
|
|
|
|
const debug = envBool("OPENVIKING_DEBUG") ?? false;
|
|
const defaultLogPath = join(homedir(), ".openviking", "logs", "agent-plugins.log");
|
|
const debugLogPath = str(process.env.OPENVIKING_DEBUG_LOG, defaultLogPath);
|
|
|
|
return {
|
|
configPath,
|
|
baseUrl,
|
|
apiKey,
|
|
accountId,
|
|
userId,
|
|
peerId,
|
|
workspacePeer,
|
|
timeoutMs,
|
|
userAgent: USER_AGENT,
|
|
debug,
|
|
debugLogPath,
|
|
};
|
|
}
|