@openviking/opencode-plugin 2026.10.13 → 2026.10.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL-ZH.md +13 -3
- package/INSTALL.md +13 -3
- package/README.md +11 -1
- package/lib/memory-session.mjs +16 -1
- package/lib/shared/ov-http.mjs +22 -1
- package/package.json +2 -2
package/INSTALL-ZH.md
CHANGED
|
@@ -28,9 +28,19 @@ openviking-server --config ~/.openviking/ov.conf
|
|
|
28
28
|
curl http://localhost:1933/health
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## 安装方式一:一键安装(推荐)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
OpenCode 与其他记忆插件共用同一个安装器。它会询问要安装的宿主和 OpenViking 凭据:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -fsSL https://openviking.ai/install | bash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
安装器会把插件作为本地文件插件复制到 `~/.config/opencode/plugins/`,把 `openviking` MCP server 条目写进 `~/.config/opencode/opencode.json`,并配置 `~/.openviking/ovcli.conf`。
|
|
40
|
+
|
|
41
|
+
## 安装方式二:发布包安装
|
|
42
|
+
|
|
43
|
+
也可以通过 OpenCode 的 package plugin 机制启用:
|
|
34
44
|
|
|
35
45
|
```json
|
|
36
46
|
{
|
|
@@ -40,7 +50,7 @@ curl http://localhost:1933/health
|
|
|
40
50
|
|
|
41
51
|
这份 `plugin` 配置同时兼容 v1 和 v2;v2 会在加载时规范化为原生的 `plugins`,无需手工迁移配置。
|
|
42
52
|
|
|
43
|
-
##
|
|
53
|
+
## 安装方式三:源码安装
|
|
44
54
|
|
|
45
55
|
用于开发调试或 PR 测试。OpenCode 推荐插件目录:
|
|
46
56
|
|
package/INSTALL.md
CHANGED
|
@@ -28,9 +28,19 @@ Check the service:
|
|
|
28
28
|
curl http://localhost:1933/health
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
## Installation Method 1:
|
|
31
|
+
## Installation Method 1: One-line Installer (Recommended)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
OpenCode shares the unified installer with the other memory plugins. It asks which harnesses to install and for your OpenViking credentials:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -fsSL https://openviking.ai/install | bash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The installer copies the plugin into `~/.config/opencode/plugins/` as a local file plugin, writes the `openviking` MCP server entry into `~/.config/opencode/opencode.json`, and configures `~/.openviking/ovcli.conf`.
|
|
40
|
+
|
|
41
|
+
## Installation Method 2: Published Package
|
|
42
|
+
|
|
43
|
+
To enable it through OpenCode's package plugin mechanism instead:
|
|
34
44
|
|
|
35
45
|
```json
|
|
36
46
|
{
|
|
@@ -40,7 +50,7 @@ Normal users are recommended to enable it through OpenCode's package plugin mech
|
|
|
40
50
|
|
|
41
51
|
This `plugin` entry works on both v1 and v2. OpenCode v2 normalizes it to the native `plugins` form at load time, so no manual config migration is required.
|
|
42
52
|
|
|
43
|
-
## Installation Method
|
|
53
|
+
## Installation Method 3: Source Install
|
|
44
54
|
|
|
45
55
|
Use this method for development, debugging, or PR testing. OpenCode's recommended plugin directory is:
|
|
46
56
|
|
package/README.md
CHANGED
|
@@ -68,9 +68,19 @@ openviking-server --config ~/.openviking/ov.conf
|
|
|
68
68
|
|
|
69
69
|
## Installation
|
|
70
70
|
|
|
71
|
+
### One-line Installer (Recommended)
|
|
72
|
+
|
|
73
|
+
OpenCode shares the unified installer with the other memory plugins. It asks which harnesses to install and for your OpenViking credentials:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
curl -fsSL https://openviking.ai/install | bash
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The installer copies the plugin into `~/.config/opencode/plugins/` as a local file plugin, writes the `openviking` MCP server entry into `~/.config/opencode/opencode.json`, and configures `~/.openviking/ovcli.conf`.
|
|
80
|
+
|
|
71
81
|
### Published Package
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
To enable it through OpenCode's package plugin mechanism instead:
|
|
74
84
|
|
|
75
85
|
The published npm package is `@openviking/opencode-plugin`; verify availability with:
|
|
76
86
|
|
package/lib/memory-session.mjs
CHANGED
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
safeStringify,
|
|
26
26
|
} from "./utils.mjs"
|
|
27
27
|
|
|
28
|
+
const STALE_SESSION_STATE_MS = 7 * 24 * 60 * 60 * 1000
|
|
29
|
+
|
|
28
30
|
export function createMemorySessionManager({ config, pluginRoot }) {
|
|
29
31
|
const sessions = new Map()
|
|
30
32
|
const statePath = path.join(pluginRoot, "openviking-session-state.json")
|
|
@@ -77,10 +79,17 @@ export function createMemorySessionManager({ config, pluginRoot }) {
|
|
|
77
79
|
log("ERROR", "persistence", "Unsupported session map version", { version: data.version })
|
|
78
80
|
return
|
|
79
81
|
}
|
|
82
|
+
const staleBefore = Date.now() - STALE_SESSION_STATE_MS
|
|
83
|
+
let pruned = 0
|
|
80
84
|
for (const [opencodeSessionId, persisted] of Object.entries(data.sessions ?? {})) {
|
|
85
|
+
if (isPrunable(persisted, staleBefore)) {
|
|
86
|
+
pruned += 1
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
81
89
|
sessions.set(opencodeSessionId, deserializeSessionState(persisted))
|
|
82
90
|
}
|
|
83
|
-
log("INFO", "persistence", "Session state loaded", { count: sessions.size })
|
|
91
|
+
log("INFO", "persistence", "Session state loaded", { count: sessions.size, pruned })
|
|
92
|
+
if (pruned > 0) await enqueueSave()
|
|
84
93
|
} catch (error) {
|
|
85
94
|
log("ERROR", "persistence", "Failed to load session state", { error: error?.message })
|
|
86
95
|
if (fs.existsSync(statePath)) {
|
|
@@ -133,6 +142,12 @@ export function createMemorySessionManager({ config, pluginRoot }) {
|
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
|
|
145
|
+
// A stale session whose messages are all captured holds nothing left to send.
|
|
146
|
+
function isPrunable(persisted, staleBefore) {
|
|
147
|
+
if ((persisted.lastActivityAt ?? 0) >= staleBefore) return false
|
|
148
|
+
return (persisted.messages ?? []).every(([, message]) => message.captured)
|
|
149
|
+
}
|
|
150
|
+
|
|
136
151
|
function deserializeSessionState(persisted) {
|
|
137
152
|
return {
|
|
138
153
|
ovSessionId: persisted.ovSessionId,
|
package/lib/shared/ov-http.mjs
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
// that asks for less than a second gets a second.
|
|
18
18
|
const MIN_TIMEOUT_MS = 1000;
|
|
19
19
|
|
|
20
|
+
import { parseExtraHeaders } from "./mcp-proxy-config.mjs";
|
|
21
|
+
|
|
20
22
|
/**
|
|
21
23
|
* The headers every harness puts on every OpenViking request.
|
|
22
24
|
*
|
|
@@ -59,6 +61,12 @@ export function createOvHttp(cfg = {}, {
|
|
|
59
61
|
} = {}) {
|
|
60
62
|
// dsh and pi name this field `endpoint`; every other harness names it `baseUrl`.
|
|
61
63
|
const baseUrl = cfg.baseUrl || cfg.endpoint || "";
|
|
64
|
+
// A caller that owns its header map passes it in; every other harness gets the
|
|
65
|
+
// operator's escape hatch from the environment. A deployment behind an
|
|
66
|
+
// authenticating gateway (Cloudflare Access, oauth2-proxy, mTLS-terminating proxy)
|
|
67
|
+
// needs the same headers on the turn hooks as on the MCP path, and wiring each
|
|
68
|
+
// harness by hand is how the header blocks drifted in the first place.
|
|
69
|
+
const resolvedExtraHeaders = extraHeaders ?? parseExtraHeaders(process.env.OPENVIKING_EXTRA_HEADERS);
|
|
62
70
|
return async function fetchJSON(path, init = {}, options = {}) {
|
|
63
71
|
const timeoutMs = Math.max(MIN_TIMEOUT_MS, Number(options.timeoutMs) || Number(defaultTimeoutMs) || 0);
|
|
64
72
|
const controller = new AbortController();
|
|
@@ -72,7 +80,7 @@ export function createOvHttp(cfg = {}, {
|
|
|
72
80
|
...buildOvHeaders(cfg, {
|
|
73
81
|
actorPeerId: options.actorPeerId ?? resolveActorPeerId(),
|
|
74
82
|
identityHeaders,
|
|
75
|
-
extraHeaders,
|
|
83
|
+
extraHeaders: resolvedExtraHeaders,
|
|
76
84
|
}),
|
|
77
85
|
...(init.headers || {}),
|
|
78
86
|
};
|
|
@@ -81,6 +89,19 @@ export function createOvHttp(cfg = {}, {
|
|
|
81
89
|
if (!(error instanceof SyntaxError)) throw error;
|
|
82
90
|
return null;
|
|
83
91
|
});
|
|
92
|
+
const contentType = response.headers.get("content-type") || "";
|
|
93
|
+
if (body === null && response.ok && contentType && !/json/i.test(contentType)) {
|
|
94
|
+
// A 2xx that is not JSON is not OpenViking. An authenticating gateway's login
|
|
95
|
+
// page, a captive portal or a WAF page answers with 200 and HTML; calling that a
|
|
96
|
+
// success makes a harness believe it is connected while every capture and recall
|
|
97
|
+
// is quietly dropped.
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
status: response.status,
|
|
101
|
+
result: null,
|
|
102
|
+
error: { message: `non-JSON response (${contentType})` },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
84
105
|
if (requireJsonBody && !body) {
|
|
85
106
|
return {
|
|
86
107
|
ok: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openviking/opencode-plugin",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.15",
|
|
4
4
|
"author": "@openviking",
|
|
5
5
|
"description": "Unified OpenCode plugin for OpenViking repository retrieval and long-term memory",
|
|
6
6
|
"type": "module",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"code-search"
|
|
47
47
|
],
|
|
48
48
|
"license": "Apache-2.0",
|
|
49
|
-
"openvikingSourceCommit": "
|
|
49
|
+
"openvikingSourceCommit": "60629f92bfd7d05c67bc50aa452f852acbc77c39"
|
|
50
50
|
}
|