cursor-opencode-provider 0.2.7 → 0.2.9
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/README.md +22 -6
- package/dist/agent-url.d.ts +8 -5
- package/dist/auth.js +59 -27
- package/dist/context/agents.d.ts +5 -1
- package/dist/context/agents.js +90 -30
- package/dist/context/build.js +63 -8
- package/dist/context/paths.d.ts +31 -6
- package/dist/context/paths.js +56 -18
- package/dist/context/plugins.js +16 -12
- package/dist/context/rules.js +26 -23
- package/dist/context/skills.js +8 -3
- package/dist/deadline.d.ts +8 -0
- package/dist/deadline.js +25 -0
- package/dist/language-model.js +206 -70
- package/dist/models.d.ts +2 -0
- package/dist/plugin.js +8 -1
- package/dist/protocol/client-version.js +7 -6
- package/dist/protocol/messages.js +24 -1
- package/dist/protocol/struct.d.ts +18 -0
- package/dist/protocol/struct.js +82 -0
- package/dist/protocol/tool-call-bridge.js +22 -2
- package/dist/protocol/tools.d.ts +67 -0
- package/dist/protocol/tools.js +342 -18
- package/dist/replay-safety.d.ts +23 -0
- package/dist/replay-safety.js +126 -0
- package/dist/session.d.ts +5 -0
- package/dist/transport/connect.d.ts +2 -0
- package/dist/transport/connect.js +106 -82
- package/dist/web-tools.d.ts +34 -0
- package/dist/web-tools.js +127 -0
- package/package.json +2 -2
package/dist/context/paths.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { trace } from "../debug.js";
|
|
7
|
+
/** Host-neutral bridge installed by OCP before an unchanged provider is loaded. */
|
|
8
|
+
export const OPENCODE_PATH_BRIDGE = Symbol.for("opencode.compat.path-bridge");
|
|
9
|
+
function pathBridge() {
|
|
10
|
+
const value = globalThis[OPENCODE_PATH_BRIDGE];
|
|
11
|
+
if (!value || typeof value !== "object")
|
|
12
|
+
return undefined;
|
|
13
|
+
const bridge = value;
|
|
14
|
+
return typeof bridge.projectConfigDirs === "function" && typeof bridge.globalConfigDirs === "function"
|
|
15
|
+
? bridge
|
|
16
|
+
: undefined;
|
|
17
|
+
}
|
|
18
|
+
export function opencodeProjectConfigDirs(workspaceRoot) {
|
|
19
|
+
return pathBridge()?.projectConfigDirs(path.resolve(workspaceRoot)) ?? [
|
|
20
|
+
path.join(path.resolve(workspaceRoot), ".opencode"),
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
export function opencodeGlobalConfigDirs() {
|
|
24
|
+
return pathBridge()?.globalConfigDirs() ?? [opencodeGlobalConfigDir()];
|
|
25
|
+
}
|
|
26
|
+
export function opencodeConfigFileNames() {
|
|
27
|
+
return pathBridge()?.configFileNames?.length
|
|
28
|
+
? [...pathBridge().configFileNames]
|
|
29
|
+
: ["opencode.json", "opencode.jsonc"];
|
|
30
|
+
}
|
|
6
31
|
/** Explicit host cache root (e.g. Effect v2 `Path.cache`, or `createCursor({ cacheDir })`). */
|
|
7
32
|
let hostCacheDirOverride;
|
|
8
33
|
function resolveHome(env = process.env) {
|
|
@@ -13,11 +38,6 @@ function xdgCacheHome(env = process.env) {
|
|
|
13
38
|
return env.XDG_CACHE_HOME;
|
|
14
39
|
return path.join(resolveHome(env), ".cache");
|
|
15
40
|
}
|
|
16
|
-
function xdgConfigHome(env = process.env) {
|
|
17
|
-
if (env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0)
|
|
18
|
-
return env.XDG_CONFIG_HOME;
|
|
19
|
-
return path.join(resolveHome(env), ".config");
|
|
20
|
-
}
|
|
21
41
|
/**
|
|
22
42
|
* Pin the process-wide cache root. Highest precedence for {@link opencodeGlobalCacheDir}.
|
|
23
43
|
* Use for host-injected `Path.cache` or an explicit `createCursor({ cacheDir })`.
|
|
@@ -30,23 +50,35 @@ export function getHostCacheDirOverride() {
|
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* Resolve the host cache directory without an override.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
53
|
+
*
|
|
54
|
+
* Explicit host environment wins. Otherwise, an installed provider inherits
|
|
55
|
+
* the host-named cache containing its module. A source checkout or otherwise
|
|
56
|
+
* unidentifiable install defaults to OpenCode. Merely having another host's
|
|
57
|
+
* config directory installed is not evidence that it owns this process.
|
|
35
58
|
*/
|
|
36
|
-
export function resolveHostCacheDir(env = process.env) {
|
|
59
|
+
export function resolveHostCacheDir(env = process.env, moduleUrl = import.meta.url) {
|
|
37
60
|
const mimoHome = env.MIMOCODE_HOME;
|
|
38
61
|
if (mimoHome && mimoHome.length > 0) {
|
|
39
62
|
return path.join(mimoHome, "cache");
|
|
40
63
|
}
|
|
41
|
-
const configHome = xdgConfigHome(env);
|
|
42
64
|
const cacheHome = xdgCacheHome(env);
|
|
43
65
|
const kiloConfig = env.KILO_CONFIG_DIR;
|
|
44
|
-
|
|
45
|
-
if ((kiloConfig && kiloConfig.length > 0) || existsSync(path.join(configHome, "kilo"))) {
|
|
66
|
+
if (kiloConfig && kiloConfig.length > 0) {
|
|
46
67
|
return path.join(cacheHome, "kilo");
|
|
47
68
|
}
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
let modulePath;
|
|
70
|
+
try {
|
|
71
|
+
modulePath = moduleUrl.startsWith("file:") ? fileURLToPath(moduleUrl) : path.resolve(moduleUrl);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
modulePath = undefined;
|
|
75
|
+
}
|
|
76
|
+
if (modulePath) {
|
|
77
|
+
for (const host of ["mimocode", "kilo", "opencode"]) {
|
|
78
|
+
const root = path.resolve(cacheHome, host);
|
|
79
|
+
if (modulePath === root || modulePath.startsWith(`${root}${path.sep}`))
|
|
80
|
+
return root;
|
|
81
|
+
}
|
|
50
82
|
}
|
|
51
83
|
return path.join(cacheHome, "opencode");
|
|
52
84
|
}
|
|
@@ -54,12 +86,18 @@ export function resolveHostCacheDir(env = process.env) {
|
|
|
54
86
|
* Best-effort: if `@opencode-compat/profile` is installed, adopt `detect().profile.paths.cacheDir`
|
|
55
87
|
* when the host is supported. No-op when OCP is absent or detection fails.
|
|
56
88
|
*/
|
|
57
|
-
export async function adoptCompatHostCacheDir() {
|
|
89
|
+
export async function adoptCompatHostCacheDir(detector) {
|
|
90
|
+
if (hostCacheDirOverride)
|
|
91
|
+
return hostCacheDirOverride;
|
|
58
92
|
try {
|
|
59
|
-
const
|
|
93
|
+
const detect = detector ?? (await import("@opencode-compat/profile")).detect;
|
|
60
94
|
const result = detect();
|
|
61
95
|
if (!result.supported || result.id === "unknown")
|
|
62
96
|
return undefined;
|
|
97
|
+
if (!result.source || !["env", "binary", "package"].includes(result.source)) {
|
|
98
|
+
trace(`host-cache: ignored weak OCP detect host=${result.id} source=${result.source ?? "unknown"}`);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
63
101
|
const cacheDir = result.profile.paths.cacheDir;
|
|
64
102
|
if (!cacheDir || cacheDir.length === 0)
|
|
65
103
|
return undefined;
|
|
@@ -80,8 +118,8 @@ export function opencodeGlobalConfigDir() {
|
|
|
80
118
|
*
|
|
81
119
|
* Precedence:
|
|
82
120
|
* 1. {@link setHostCacheDirOverride} / `createCursor({ cacheDir })` (host `Path.cache`)
|
|
83
|
-
* 2. OCP `detect()` when {@link adoptCompatHostCacheDir} ran successfully
|
|
84
|
-
* 3.
|
|
121
|
+
* 2. Strong OCP `detect()` identity when {@link adoptCompatHostCacheDir} ran successfully
|
|
122
|
+
* 3. Explicit host environment / provider install path ({@link resolveHostCacheDir})
|
|
85
123
|
*/
|
|
86
124
|
export function opencodeGlobalCacheDir() {
|
|
87
125
|
if (hostCacheDirOverride)
|
package/dist/context/plugins.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdir, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { opencodeGlobalConfigDirs, opencodeProjectConfigDirs } from "./paths.js";
|
|
4
4
|
async function listLocalPlugins(dir) {
|
|
5
5
|
try {
|
|
6
6
|
await stat(dir);
|
|
@@ -34,17 +34,21 @@ export async function collectPlugins(workspaceRoot, config) {
|
|
|
34
34
|
seen.add(id);
|
|
35
35
|
out.push({ id, source: "npm" });
|
|
36
36
|
}
|
|
37
|
-
for (const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
for (const configDir of opencodeProjectConfigDirs(workspaceRoot)) {
|
|
38
|
+
for (const p of await listLocalPlugins(path.join(configDir, "plugins"))) {
|
|
39
|
+
if (seen.has(p.id))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(p.id);
|
|
42
|
+
out.push(p);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const configDir of opencodeGlobalConfigDirs()) {
|
|
46
|
+
for (const p of await listLocalPlugins(path.join(configDir, "plugins"))) {
|
|
47
|
+
if (seen.has(p.id))
|
|
48
|
+
continue;
|
|
49
|
+
seen.add(p.id);
|
|
50
|
+
out.push(p);
|
|
51
|
+
}
|
|
48
52
|
}
|
|
49
53
|
return out;
|
|
50
54
|
}
|
package/dist/context/rules.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { opencodeConfigFileNames, opencodeGlobalConfigDirs, opencodeProjectConfigDirs, resolveHomeRelative } from "./paths.js";
|
|
5
5
|
async function exists(file) {
|
|
6
6
|
try {
|
|
7
7
|
await stat(file);
|
|
@@ -12,7 +12,7 @@ async function exists(file) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
async function readJsonConfig(dir) {
|
|
15
|
-
for (const name of
|
|
15
|
+
for (const name of opencodeConfigFileNames()) {
|
|
16
16
|
const file = path.join(dir, name);
|
|
17
17
|
if (!(await exists(file)))
|
|
18
18
|
continue;
|
|
@@ -151,29 +151,30 @@ export async function fetchRemoteInstruction(url, timeoutMs = 5000) {
|
|
|
151
151
|
clearTimeout(timer);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
const globalConfig = await readJsonConfig(opencodeGlobalConfigDir());
|
|
156
|
-
if (isProjectConfigDisabled()) {
|
|
157
|
-
return {
|
|
158
|
-
...globalConfig,
|
|
159
|
-
instructions: [...(globalConfig.instructions ?? [])],
|
|
160
|
-
plugin: [...(globalConfig.plugin ?? [])],
|
|
161
|
-
plugins: [...(globalConfig.plugins ?? [])],
|
|
162
|
-
mcp: { ...(globalConfig.mcp ?? {}) },
|
|
163
|
-
permission: globalConfig.permission,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const projectConfig = await readJsonConfig(workspaceRoot);
|
|
154
|
+
function mergeConfig(base, overlay) {
|
|
167
155
|
return {
|
|
168
|
-
...
|
|
169
|
-
...
|
|
170
|
-
instructions: [...(
|
|
171
|
-
plugin: [...new Set([...(
|
|
172
|
-
plugins: [...new Set([...(
|
|
173
|
-
mcp: { ...(
|
|
174
|
-
permission:
|
|
156
|
+
...base,
|
|
157
|
+
...overlay,
|
|
158
|
+
instructions: [...(base.instructions ?? []), ...(overlay.instructions ?? [])],
|
|
159
|
+
plugin: [...new Set([...(base.plugin ?? []), ...(overlay.plugin ?? [])])],
|
|
160
|
+
plugins: [...new Set([...(base.plugins ?? []), ...(overlay.plugins ?? [])])],
|
|
161
|
+
mcp: { ...(base.mcp ?? {}), ...(overlay.mcp ?? {}) },
|
|
162
|
+
permission: overlay.permission ?? base.permission,
|
|
175
163
|
};
|
|
176
164
|
}
|
|
165
|
+
export async function loadMergedConfig(workspaceRoot) {
|
|
166
|
+
const globalConfig = await readJsonConfig(opencodeGlobalConfigDirs()[0] ?? "");
|
|
167
|
+
if (isProjectConfigDisabled())
|
|
168
|
+
return mergeConfig({}, globalConfig);
|
|
169
|
+
// The bridge supplies native project config roots for an unchanged plugin:
|
|
170
|
+
// .opencode on OpenCode, .mimocode on MiMo, and .kilo/.kilocode on Kilo.
|
|
171
|
+
// Later roots have higher precedence, matching the host's native ordering.
|
|
172
|
+
let projectConfig = await readJsonConfig(workspaceRoot);
|
|
173
|
+
for (const configDir of opencodeProjectConfigDirs(workspaceRoot)) {
|
|
174
|
+
projectConfig = mergeConfig(projectConfig, await readJsonConfig(configDir));
|
|
175
|
+
}
|
|
176
|
+
return mergeConfig(globalConfig, projectConfig);
|
|
177
|
+
}
|
|
177
178
|
/**
|
|
178
179
|
* Collect OpenCode instruction files.
|
|
179
180
|
*/
|
|
@@ -205,7 +206,9 @@ export async function collectRules(workspaceRoot) {
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
|
|
209
|
+
for (const globalDir of opencodeGlobalConfigDirs()) {
|
|
210
|
+
await add(path.join(globalDir, "AGENTS.md"));
|
|
211
|
+
}
|
|
209
212
|
await add(path.join(homedir(), ".claude", "CLAUDE.md"));
|
|
210
213
|
for (const raw of config.instructions ?? []) {
|
|
211
214
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
package/dist/context/skills.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import {
|
|
4
|
+
import { opencodeGlobalConfigDirs, opencodeProjectConfigDirs } from "./paths.js";
|
|
5
5
|
async function exists(p) {
|
|
6
6
|
try {
|
|
7
7
|
await stat(p);
|
|
@@ -94,8 +94,13 @@ async function walkAncestorsFor(dir, rel, stop, out) {
|
|
|
94
94
|
export async function collectSkills(workspaceRoot, worktree) {
|
|
95
95
|
const out = new Map();
|
|
96
96
|
const home = homedir();
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
for (const projectDir of opencodeProjectConfigDirs(workspaceRoot)) {
|
|
98
|
+
const relative = path.relative(workspaceRoot, projectDir);
|
|
99
|
+
await walkAncestorsFor(workspaceRoot, path.join(relative, "skills"), worktree, out);
|
|
100
|
+
}
|
|
101
|
+
for (const globalDir of opencodeGlobalConfigDirs()) {
|
|
102
|
+
await scanSkillsRoot(path.join(globalDir, "skills"), out);
|
|
103
|
+
}
|
|
99
104
|
await walkAncestorsFor(workspaceRoot, path.join(".claude", "skills"), worktree, out);
|
|
100
105
|
await scanSkillsRoot(path.join(home, ".claude", "skills"), out);
|
|
101
106
|
await walkAncestorsFor(workspaceRoot, path.join(".agents", "skills"), worktree, out);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound a complete async operation, not only fetch(). Response body readers can
|
|
3
|
+
* ignore abort signals, so Promise.race remains the authoritative deadline.
|
|
4
|
+
*
|
|
5
|
+
* Rejects with `timeoutError()` before aborting so callers observe the domain
|
|
6
|
+
* timeout error rather than a generic AbortError.
|
|
7
|
+
*/
|
|
8
|
+
export declare function withAbortDeadline<T>(timeoutMs: number, timeoutError: () => Error, run: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
package/dist/deadline.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound a complete async operation, not only fetch(). Response body readers can
|
|
3
|
+
* ignore abort signals, so Promise.race remains the authoritative deadline.
|
|
4
|
+
*
|
|
5
|
+
* Rejects with `timeoutError()` before aborting so callers observe the domain
|
|
6
|
+
* timeout error rather than a generic AbortError.
|
|
7
|
+
*/
|
|
8
|
+
export async function withAbortDeadline(timeoutMs, timeoutError, run) {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
let timer;
|
|
11
|
+
const deadline = new Promise((_, reject) => {
|
|
12
|
+
timer = setTimeout(() => {
|
|
13
|
+
reject(timeoutError());
|
|
14
|
+
controller.abort();
|
|
15
|
+
}, timeoutMs);
|
|
16
|
+
timer.unref?.();
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
return await Promise.race([run(controller.signal), deadline]);
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
if (timer)
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
}
|
|
25
|
+
}
|