fix(opencode-plugin): fall back to config peerId when shared credentials define none (#4632)

loadConfig() unconditionally assigned config.peerId from resolved shared
credentials, and applyLegacyConnection() (the only reader of the config
file's peerId) is skipped whenever those credentials exist. When ovcli.conf
or environment variables authenticate without actor_peer_id, the project
config's peerId was silently dropped, so sessions and memories landed in
the shared user tree instead of the peer-scoped tree.

Apply the file config's peerId as a fallback when shared credentials carry
no peer, keeping the documented precedence: shared credentials >
extension config peerId > workspace-derived peer. Same defect class as
#3649 (Pi extension, addressed by #3653 for Pi only).

Fixes #4487

Co-authored-by: mac <bishopapril850965@yahoo.com>
This commit is contained in:
now-ing
2026-09-04 12:05:24 +08:00
committed by GitHub
co-authored by mac
parent dfb4e324d2
commit da94ac1afd
3 changed files with 102 additions and 2 deletions
+5 -1
View File
@@ -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.
+5 -1
View File
@@ -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 || ""
@@ -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)
}
})
})