diff --git a/examples/opencode-plugin/README.md b/examples/opencode-plugin/README.md index d7a2624d0..ea8f5c8b9 100644 --- a/examples/opencode-plugin/README.md +++ b/examples/opencode-plugin/README.md @@ -164,7 +164,11 @@ with an explicit actor peer so one person's memories are not recalled into another person's session. `OPENVIKING_API_KEY`, `OPENVIKING_ACCOUNT`, `OPENVIKING_USER`, -and `OPENVIKING_PEER_ID` take precedence over values in this file. +and `OPENVIKING_PEER_ID` take precedence over values in this file. The config +file's `peerId` still applies whenever shared credentials (ovcli.conf or +environment variables) do not carry a peer of their own, so an authenticated +setup keeps writing peer-scoped data instead of dropping into the shared user +tree. For advanced setups, `OPENVIKING_PLUGIN_CONFIG` can point to another config file path. diff --git a/examples/opencode-plugin/lib/config.mjs b/examples/opencode-plugin/lib/config.mjs index e32c7eb14..16a97155c 100644 --- a/examples/opencode-plugin/lib/config.mjs +++ b/examples/opencode-plugin/lib/config.mjs @@ -294,7 +294,11 @@ export function loadConfig(pluginRoot, projectDirectory) { config.apiKey = creds.apiKey config.account = creds.account config.user = creds.user - config.peerId = creds.peerId + // Shared credentials only carry a peer when ovcli.conf sets actor_peer_id + // (or OPENVIKING_PEER_ID is exported); without one the project config's + // peerId still applies so authenticated setups keep writing peer-scoped + // data instead of silently dropping into the shared user tree (#4487). + config.peerId = creds.peerId || str(fileConfig.peerId, "") config.mcpUrl = creds.mcpUrl config.credentialSource = creds.credentialSource config.credentialPath = creds.cliPath || creds.ovPath || "" diff --git a/examples/opencode-plugin/tests/config.test.mjs b/examples/opencode-plugin/tests/config.test.mjs index 445a7af6c..293ce752d 100644 --- a/examples/opencode-plugin/tests/config.test.mjs +++ b/examples/opencode-plugin/tests/config.test.mjs @@ -267,3 +267,95 @@ test("loadConfig defaults an invalid commit keep recent count", async () => { } }) }) + +test("loadConfig falls back to config peerId when shared credentials define none (#4487)", async () => { + const snapshot = { ...process.env } + await withTempDir("ov-oc-peer-fallback-", async (dir) => { + try { + for (const key of Object.keys(process.env)) { + if (key.startsWith("OPENVIKING_")) delete process.env[key] + } + const ovcli = join(dir, "ovcli.conf") + const project = join(dir, "project") + await mkdir(join(project, ".opencode"), { recursive: true }) + await writeFile(ovcli, JSON.stringify({ + url: "https://cli.example.com", + api_key: "cli-key", + account: "cli-account", + user: "cli-user", + })) + await writeFile(join(project, ".opencode", "openviking-config.json"), JSON.stringify({ + enabled: true, + peerId: "atomic-city", + workspacePeer: false, + recallPeerScope: "actor", + })) + process.env.OPENVIKING_CLI_CONFIG_FILE = ovcli + + const cfg = loadConfig(dir, project) + assert.equal(cfg.peerId, "atomic-city") + assert.deepEqual(cfg.effectivePeer, { peerId: "atomic-city", source: "explicit" }) + assert.equal(cfg.legacyCredentialsUsed, false) + } finally { + restoreOpenVikingEnv(snapshot) + } + }) +}) + +test("loadConfig keeps ovcli actor_peer_id over config peerId", async () => { + const snapshot = { ...process.env } + await withTempDir("ov-oc-peer-cli-wins-", async (dir) => { + try { + for (const key of Object.keys(process.env)) { + if (key.startsWith("OPENVIKING_")) delete process.env[key] + } + const ovcli = join(dir, "ovcli.conf") + const project = join(dir, "project") + await mkdir(join(project, ".opencode"), { recursive: true }) + await writeFile(ovcli, JSON.stringify({ + url: "https://cli.example.com", + api_key: "cli-key", + actor_peer_id: "cli-peer", + })) + await writeFile(join(project, ".opencode", "openviking-config.json"), JSON.stringify({ + peerId: "config-peer", + })) + process.env.OPENVIKING_CLI_CONFIG_FILE = ovcli + + const cfg = loadConfig(dir, project) + assert.equal(cfg.peerId, "cli-peer") + assert.deepEqual(cfg.effectivePeer, { peerId: "cli-peer", source: "explicit" }) + } finally { + restoreOpenVikingEnv(snapshot) + } + }) +}) + +test("loadConfig keeps env peer over config peerId when ovcli has none", async () => { + const snapshot = { ...process.env } + await withTempDir("ov-oc-peer-env-wins-", async (dir) => { + try { + for (const key of Object.keys(process.env)) { + if (key.startsWith("OPENVIKING_")) delete process.env[key] + } + const ovcli = join(dir, "ovcli.conf") + const project = join(dir, "project") + await mkdir(join(project, ".opencode"), { recursive: true }) + await writeFile(ovcli, JSON.stringify({ + url: "https://cli.example.com", + api_key: "cli-key", + })) + await writeFile(join(project, ".opencode", "openviking-config.json"), JSON.stringify({ + peerId: "config-peer", + })) + process.env.OPENVIKING_CLI_CONFIG_FILE = ovcli + process.env.OPENVIKING_PEER_ID = "env-peer" + + const cfg = loadConfig(dir, project) + assert.equal(cfg.peerId, "env-peer") + assert.deepEqual(cfg.effectivePeer, { peerId: "env-peer", source: "explicit" }) + } finally { + restoreOpenVikingEnv(snapshot) + } + }) +})