@openviking/opencode-plugin 0.1.5 → 0.2.0
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 +50 -49
- package/INSTALL.md +50 -49
- package/README.md +40 -123
- package/index.mjs +20 -12
- package/lib/config.mjs +272 -0
- package/lib/mcp-config.mjs +21 -0
- package/lib/memory-recall.mjs +21 -172
- package/lib/memory-session.mjs +301 -478
- package/lib/repo-context.mjs +5 -5
- package/lib/session-inject.mjs +71 -0
- package/lib/shared/capture-utils.mjs +500 -0
- package/lib/shared/credentials.mjs +207 -0
- package/lib/shared/debug-log.mjs +58 -0
- package/lib/shared/mcp-proxy-core.mjs +451 -0
- package/lib/shared/pending-queue.mjs +439 -0
- package/lib/shared/profile-inject.mjs +294 -0
- package/lib/shared/recall-core.mjs +282 -0
- package/lib/shared/session-model.mjs +57 -0
- package/lib/shared/setup-wizard.mjs +111 -0
- package/lib/shared/uri-guard.mjs +56 -0
- package/lib/utils.mjs +33 -116
- package/lib/viking-uri-guard.mjs +9 -24
- package/package.json +13 -8
- package/scripts/setup.mjs +8 -0
- package/servers/mcp-proxy.mjs +67 -0
- package/servers/mcp-proxy.test.mjs +100 -0
- package/lib/code-tools.mjs +0 -134
- package/lib/memadd-local.mjs +0 -118
- package/lib/memory-tools.mjs +0 -410
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// GENERATED FROM examples/memory-plugin-shared/lib. DO NOT EDIT.
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_OVCLI_CONF_PATH = join(homedir(), ".openviking", "ovcli.conf");
|
|
8
|
+
const DEFAULT_OV_CONF_PATH = join(homedir(), ".openviking", "ov.conf");
|
|
9
|
+
const DEFAULT_BASE_URL = "http://127.0.0.1:1933";
|
|
10
|
+
|
|
11
|
+
function str(val, fallback = "") {
|
|
12
|
+
if (typeof val === "string" && val.trim()) return val.trim();
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizePath(value) {
|
|
17
|
+
const raw = str(value, "");
|
|
18
|
+
if (!raw) return "";
|
|
19
|
+
if (raw === "~") return homedir();
|
|
20
|
+
if (raw.startsWith("~/")) return resolvePath(join(homedir(), raw.slice(2)));
|
|
21
|
+
return resolvePath(raw);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function tryLoadJson(path) {
|
|
25
|
+
if (!path) return null;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function looksLikeOvcli(obj) {
|
|
34
|
+
if (!obj || typeof obj !== "object") return false;
|
|
35
|
+
if (obj.server && typeof obj.server === "object") return false;
|
|
36
|
+
return Boolean(
|
|
37
|
+
typeof obj.url === "string" ||
|
|
38
|
+
typeof obj.api_key === "string" ||
|
|
39
|
+
typeof obj.account === "string" ||
|
|
40
|
+
typeof obj.account_id === "string" ||
|
|
41
|
+
typeof obj.user === "string" ||
|
|
42
|
+
typeof obj.user_id === "string" ||
|
|
43
|
+
typeof obj.actor_peer_id === "string",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasCredentialFields(obj) {
|
|
48
|
+
if (!obj || typeof obj !== "object") return false;
|
|
49
|
+
return [
|
|
50
|
+
"url",
|
|
51
|
+
"api_key",
|
|
52
|
+
"account",
|
|
53
|
+
"account_id",
|
|
54
|
+
"user",
|
|
55
|
+
"user_id",
|
|
56
|
+
"actor_peer_id",
|
|
57
|
+
"peer_id",
|
|
58
|
+
].some((key) => typeof obj[key] === "string");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function loadCredentialFiles(env = process.env) {
|
|
62
|
+
const cliPathCandidate = normalizePath(env.OPENVIKING_CLI_CONFIG_FILE) || DEFAULT_OVCLI_CONF_PATH;
|
|
63
|
+
const ovPathCandidate = normalizePath(env.OPENVIKING_CONFIG_FILE) || DEFAULT_OV_CONF_PATH;
|
|
64
|
+
const cliPathEnv = Boolean(str(env.OPENVIKING_CLI_CONFIG_FILE, ""));
|
|
65
|
+
const ovPathEnv = Boolean(str(env.OPENVIKING_CONFIG_FILE, ""));
|
|
66
|
+
|
|
67
|
+
let cliFile = tryLoadJson(cliPathCandidate);
|
|
68
|
+
let cliPath = cliFile ? cliPathCandidate : "";
|
|
69
|
+
let ovFile = tryLoadJson(ovPathCandidate);
|
|
70
|
+
let ovPath = ovFile ? ovPathCandidate : "";
|
|
71
|
+
|
|
72
|
+
// Backward compat: older plugin installs used OPENVIKING_CONFIG_FILE for
|
|
73
|
+
// both ov.conf and ovcli.conf. Preserve that when the file is ovcli-shaped.
|
|
74
|
+
if (ovPathEnv && !cliPathEnv && looksLikeOvcli(ovFile)) {
|
|
75
|
+
cliFile = ovFile;
|
|
76
|
+
cliPath = ovPath;
|
|
77
|
+
ovFile = null;
|
|
78
|
+
ovPath = "";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
cliFile: cliFile || {},
|
|
83
|
+
cliPath,
|
|
84
|
+
cliPathCandidate,
|
|
85
|
+
ovFile: ovFile || {},
|
|
86
|
+
ovPath,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function sourceMode(env) {
|
|
91
|
+
const raw = str(env.OPENVIKING_CREDENTIAL_SOURCE, str(env.OPENVIKING_CREDENTIALS_SOURCE, "auto"))
|
|
92
|
+
.toLowerCase();
|
|
93
|
+
if (raw === "env" || raw === "environment") return "env";
|
|
94
|
+
if (raw === "cli" || raw === "ovcli" || raw === "file" || raw === "config") return "cli";
|
|
95
|
+
return "auto";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasEnvCredentialFields(env) {
|
|
99
|
+
return Boolean(
|
|
100
|
+
str(env.OPENVIKING_URL, str(env.OPENVIKING_BASE_URL, "")) ||
|
|
101
|
+
str(env.OPENVIKING_MCP_URL, "") ||
|
|
102
|
+
str(env.OPENVIKING_BEARER_TOKEN, str(env.OPENVIKING_API_KEY, "")) ||
|
|
103
|
+
str(env.OPENVIKING_ACCOUNT, "") ||
|
|
104
|
+
str(env.OPENVIKING_USER, "") ||
|
|
105
|
+
str(env.OPENVIKING_PEER_ID, ""),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function deriveBaseUrl({ env, cliFile, ovFile, mode, useCli }) {
|
|
110
|
+
const envUrl = str(env.OPENVIKING_URL, str(env.OPENVIKING_BASE_URL, ""));
|
|
111
|
+
const cliUrl = str(cliFile.url, "");
|
|
112
|
+
|
|
113
|
+
if (mode !== "cli" && envUrl) return envUrl.replace(/\/+$/, "");
|
|
114
|
+
if (useCli && cliUrl) return cliUrl.replace(/\/+$/, "");
|
|
115
|
+
if (mode !== "env" && cliUrl) return cliUrl.replace(/\/+$/, "");
|
|
116
|
+
|
|
117
|
+
const server = ovFile.server || {};
|
|
118
|
+
const ovUrl = str(server.url, "");
|
|
119
|
+
if (ovUrl) return ovUrl.replace(/\/+$/, "");
|
|
120
|
+
|
|
121
|
+
const host = str(server.host, "127.0.0.1").replace("0.0.0.0", "127.0.0.1");
|
|
122
|
+
const port = Number.isFinite(Number(server.port)) ? Math.floor(Number(server.port)) : 1933;
|
|
123
|
+
return `http://${host}:${port}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function resolveOpenVikingCredentials(env = process.env) {
|
|
127
|
+
const files = loadCredentialFiles(env);
|
|
128
|
+
const mode = sourceMode(env);
|
|
129
|
+
const envHasCredentials = hasEnvCredentialFields(env);
|
|
130
|
+
const useCli = mode === "cli" ||
|
|
131
|
+
(mode === "auto" && !envHasCredentials && files.cliPath && hasCredentialFields(files.cliFile));
|
|
132
|
+
const cx = files.ovFile.codex || {};
|
|
133
|
+
const server = files.ovFile.server || {};
|
|
134
|
+
|
|
135
|
+
const baseUrl = deriveBaseUrl({ env, ...files, mode, useCli });
|
|
136
|
+
|
|
137
|
+
const apiKey = useCli
|
|
138
|
+
? str(files.cliFile.api_key, "")
|
|
139
|
+
: (
|
|
140
|
+
str(env.OPENVIKING_BEARER_TOKEN, "") ||
|
|
141
|
+
str(env.OPENVIKING_API_KEY, "") ||
|
|
142
|
+
str(files.cliFile.api_key, "") ||
|
|
143
|
+
str(cx.apiKey, "") ||
|
|
144
|
+
str(server.root_api_key, "")
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const account = useCli
|
|
148
|
+
? str(files.cliFile.account, str(files.cliFile.account_id, ""))
|
|
149
|
+
: (
|
|
150
|
+
str(env.OPENVIKING_ACCOUNT, "") ||
|
|
151
|
+
str(files.cliFile.account, str(files.cliFile.account_id, "")) ||
|
|
152
|
+
str(cx.accountId, "")
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const user = useCli
|
|
156
|
+
? str(files.cliFile.user, str(files.cliFile.user_id, ""))
|
|
157
|
+
: (
|
|
158
|
+
str(env.OPENVIKING_USER, "") ||
|
|
159
|
+
str(files.cliFile.user, str(files.cliFile.user_id, "")) ||
|
|
160
|
+
str(cx.userId, "")
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const peerId = useCli
|
|
164
|
+
? str(files.cliFile.actor_peer_id, str(files.cliFile.peer_id, ""))
|
|
165
|
+
: (
|
|
166
|
+
str(env.OPENVIKING_PEER_ID, "") ||
|
|
167
|
+
str(files.cliFile.actor_peer_id, str(files.cliFile.peer_id, "")) ||
|
|
168
|
+
str(cx.peerId, str(cx.peer_id, ""))
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const explicitMcpUrl = str(env.OPENVIKING_MCP_URL, "");
|
|
172
|
+
const mcpUrl = (mode !== "cli" && explicitMcpUrl) ? explicitMcpUrl : `${baseUrl.replace(/\/+$/, "")}/mcp`;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
...files,
|
|
176
|
+
credentialSource: useCli ? "ovcli" : ((mode === "env" || envHasCredentials) ? "env" : "auto"),
|
|
177
|
+
baseUrl,
|
|
178
|
+
mcpUrl,
|
|
179
|
+
apiKey,
|
|
180
|
+
account,
|
|
181
|
+
user,
|
|
182
|
+
peerId,
|
|
183
|
+
hasApiKey: Boolean(apiKey),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function main() {
|
|
188
|
+
const cmd = process.argv[2] || "";
|
|
189
|
+
if (cmd === "mcp-url") {
|
|
190
|
+
process.stdout.write(resolveOpenVikingCredentials().mcpUrl);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (cmd === "has-api-key") {
|
|
194
|
+
process.stdout.write(resolveOpenVikingCredentials().hasApiKey ? "1" : "0");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (cmd === "has-peer-id") {
|
|
198
|
+
process.stdout.write(resolveOpenVikingCredentials().peerId ? "1" : "0");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
process.stderr.write("usage: ov-credentials.mjs <mcp-url|has-api-key|has-peer-id>\n");
|
|
202
|
+
process.exitCode = 2;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolvePath(process.argv[1])) {
|
|
206
|
+
main();
|
|
207
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// GENERATED FROM examples/memory-plugin-shared/lib. DO NOT EDIT.
|
|
2
|
+
/**
|
|
3
|
+
* Shared structured debug logger for OpenViking memory plugin hook scripts.
|
|
4
|
+
*
|
|
5
|
+
* Harness-specific wrappers load config and pass {debug, debugLogPath}. This
|
|
6
|
+
* module stays path-agnostic so it can be vendored into each plugin snapshot.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { dirname } from "node:path";
|
|
11
|
+
|
|
12
|
+
function ensureDir(filePath) {
|
|
13
|
+
try {
|
|
14
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
15
|
+
} catch { /* best effort */ }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function writeLine(filePath, obj) {
|
|
19
|
+
try {
|
|
20
|
+
appendFileSync(filePath, JSON.stringify(obj) + "\n");
|
|
21
|
+
} catch { /* best effort */ }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function localISO() {
|
|
25
|
+
const d = new Date();
|
|
26
|
+
const off = d.getTimezoneOffset();
|
|
27
|
+
const sign = off <= 0 ? "+" : "-";
|
|
28
|
+
const abs = Math.abs(off);
|
|
29
|
+
const local = new Date(d.getTime() - off * 60000);
|
|
30
|
+
return local.toISOString().replace(
|
|
31
|
+
"Z",
|
|
32
|
+
`${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const noop = () => {};
|
|
37
|
+
|
|
38
|
+
export function createLogger(hookName, overrideCfg) {
|
|
39
|
+
const c = overrideCfg || {};
|
|
40
|
+
if (!c.debug) return { log: noop, logError: noop };
|
|
41
|
+
|
|
42
|
+
const logPath = c.debugLogPath;
|
|
43
|
+
if (!logPath) return { log: noop, logError: noop };
|
|
44
|
+
ensureDir(logPath);
|
|
45
|
+
|
|
46
|
+
function log(stage, data) {
|
|
47
|
+
writeLine(logPath, { ts: localISO(), hook: hookName, stage, data });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function logError(stage, err) {
|
|
51
|
+
const error = err instanceof Error
|
|
52
|
+
? { message: err.message, stack: err.stack }
|
|
53
|
+
: String(err);
|
|
54
|
+
writeLine(logPath, { ts: localISO(), hook: hookName, stage, error });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { log, logError };
|
|
58
|
+
}
|