@sagentlab/navarch-runtime 0.1.118 → 0.1.121
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 +15 -0
- package/bin/worktree-guard-hook.cjs +72 -1
- package/dist/adapters/opencode-providers.cjs +110 -0
- package/dist/adapters/opencode.cjs +86 -32
- package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/node_modules/jsonc-parser/LICENSE.md +21 -0
- package/node_modules/jsonc-parser/README.md +364 -0
- package/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
- package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
- package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
- package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
- package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
- package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
- package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
- package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
- package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
- package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
- package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
- package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
- package/node_modules/jsonc-parser/package.json +37 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -815,6 +815,21 @@ to OpenCode's remote-server config with OAuth disabled and environment-backed
|
|
|
815
815
|
headers. Missing accounting fields stay absent rather than becoming a
|
|
816
816
|
fabricated zero.
|
|
817
817
|
|
|
818
|
+
Host OpenCode sessions preserve operator provider definitions from
|
|
819
|
+
`$XDG_CONFIG_HOME/opencode/opencode.json(c)` (default
|
|
820
|
+
`~/.config/opencode/opencode.json(c)`), then `OPENCODE_CONFIG`, configs in
|
|
821
|
+
`OPENCODE_CONFIG_DIR`, and finally `OPENCODE_CONFIG_CONTENT`. JSONC comments
|
|
822
|
+
and trailing commas are supported. Nested provider settings merge in that
|
|
823
|
+
order; later arrays replace earlier arrays. Legacy `provider` entries work
|
|
824
|
+
with V1 and V2; native `providers` entries are included for V2. Custom model
|
|
825
|
+
IDs, metadata, and variants are preserved, so a pin such as
|
|
826
|
+
`--model magpie/openrouter/deepseek/deepseek-v4.1-flash-1017` works without
|
|
827
|
+
duplicating the provider configuration. API keys and header values become
|
|
828
|
+
child environment references, and file references resolve relative to their
|
|
829
|
+
original config file. Provider settings are the only imported configuration:
|
|
830
|
+
Navarch still generates the session's MCP and permissions and disables
|
|
831
|
+
project config discovery. Docker sessions do not import host providers.
|
|
832
|
+
|
|
818
833
|
OpenCode also runs with `--print-logs --log-level ERROR` by default (see its
|
|
819
834
|
[CLI flags](https://opencode.ai/docs/cli/#global-flags)), so provider errors
|
|
820
835
|
that are missing from assistant messages still reach the runtime. A rate or
|
|
@@ -214,6 +214,73 @@ function isInsideOpeningQuote(command, start) {
|
|
|
214
214
|
return beforeQuote === undefined || /[\s=(<>;|&]/.test(beforeQuote);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Returns [open, close, segmentStart] for the quoted word containing `index`,
|
|
219
|
+
* or null when it sits outside quotes or the quote state cannot be trusted.
|
|
220
|
+
* `segmentStart` follows the last unquoted, unescaped `;`, `|`, `&` or newline
|
|
221
|
+
* before the word, so a separator inside quotes never starts a segment. Only
|
|
222
|
+
* plain single and double quotes are tracked. Constructs that nest or hide
|
|
223
|
+
* quotes (backticks, `$(`, `$'`, heredocs, shell comments) make the scan bail
|
|
224
|
+
* out, so a misread quote never exempts a token.
|
|
225
|
+
*/
|
|
226
|
+
function quotedSpanAt(command, index) {
|
|
227
|
+
let quote = "";
|
|
228
|
+
let open = -1;
|
|
229
|
+
let segmentStart = 0;
|
|
230
|
+
for (let i = 0; i < command.length; i++) {
|
|
231
|
+
const ch = command[i];
|
|
232
|
+
if (quote === "'") {
|
|
233
|
+
if (ch === "'") {
|
|
234
|
+
if (index > open && index < i) return [open, i, segmentStart];
|
|
235
|
+
quote = "";
|
|
236
|
+
}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (ch === "`" || (ch === "$" && (command[i + 1] === "(" || command[i + 1] === "'"))) return null;
|
|
240
|
+
if (ch === "\\") {
|
|
241
|
+
i++;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (quote === '"') {
|
|
245
|
+
if (ch === '"') {
|
|
246
|
+
if (index > open && index < i) return [open, i, segmentStart];
|
|
247
|
+
quote = "";
|
|
248
|
+
}
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (/[;|&\n]/.test(ch)) segmentStart = i + 1;
|
|
252
|
+
if (ch === "<" && command[i + 1] === "<") return null;
|
|
253
|
+
if (ch === "#" && (i === 0 || /[\s;|&(]/.test(command[i - 1]))) return null;
|
|
254
|
+
if (ch === "'" || ch === '"') {
|
|
255
|
+
quote = ch;
|
|
256
|
+
open = i;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* True for the jq alternative operator (`//`, `//=`) used as its own word inside
|
|
264
|
+
* a quoted jq program: `jq '.a // "x"' f.json`, `gh ... --jq '(.x) // "none"'`.
|
|
265
|
+
* Narrow on purpose. The token must be whitespace-delimited, sit inside a
|
|
266
|
+
* plain-quoted word in a shell segment whose command word is `jq`, or is `gh`
|
|
267
|
+
* with the word given to `--jq`/`-q`. Unquoted tokens (`find // -x`),
|
|
268
|
+
* quoted bare roots (`ls "//"`) and `//` in other quoted text stay screened.
|
|
269
|
+
*/
|
|
270
|
+
function isJqAlternativeOperator(command, start, token) {
|
|
271
|
+
if (token !== "//" && token !== "//=") return false;
|
|
272
|
+
const next = command[start + token.length];
|
|
273
|
+
if (!/\s/.test(command[start - 1] ?? "") || next === undefined || !/\s/.test(next)) return false;
|
|
274
|
+
const span = quotedSpanAt(command, start);
|
|
275
|
+
if (!span) return false;
|
|
276
|
+
const segment = command.slice(span[2], span[0]);
|
|
277
|
+
const commandWord = /^\s*\(?\s*(?:[A-Za-z_]\w*=\S*\s+)*/;
|
|
278
|
+
if (new RegExp(commandWord.source + "jq\\s").test(segment)) return true;
|
|
279
|
+
return (
|
|
280
|
+
new RegExp(commandWord.source + "gh\\s").test(segment) && /\s(?:--jq|-q)(?:=|\s+)$/.test(segment)
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
217
284
|
/**
|
|
218
285
|
* True when a "/"-leading token is comment or markup text rather than a path:
|
|
219
286
|
* a closing tag (`</p>`), a block-comment opener (`/**`, `/*`), or a line
|
|
@@ -222,12 +289,16 @@ function isInsideOpeningQuote(command, start) {
|
|
|
222
289
|
* checked, `/*` and `//` only count at a comment position (line start, right
|
|
223
290
|
* after an opening quote, or after `;`) so `rm -rf /*`, `find // -name x`
|
|
224
291
|
* and `rm -rf "" /*` stay blocked, and a block comment needs a closing
|
|
225
|
-
* `*` + `/` later in the text.
|
|
292
|
+
* `*` + `/` later in the text. The jq alternative operator inside a quoted jq
|
|
293
|
+
* program is exempt under the separate, narrower rule in
|
|
294
|
+
* isJqAlternativeOperator.
|
|
226
295
|
*/
|
|
227
296
|
function isCommentOrMarkupToken(command, start, token) {
|
|
228
297
|
const end = start + token.length;
|
|
229
298
|
const next = command[end];
|
|
230
299
|
|
|
300
|
+
if (isJqAlternativeOperator(command, start, token)) return true;
|
|
301
|
+
|
|
231
302
|
if (command[start - 1] === "<" && next === ">" && /^\/[A-Za-z][\w:.-]*$/.test(token)) {
|
|
232
303
|
return !fs.existsSync(token);
|
|
233
304
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadOpenCodeProviders = loadOpenCodeProviders;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const jsonc_parser_1 = require("jsonc-parser");
|
|
11
|
+
function isObject(value) {
|
|
12
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function merge(base, override) {
|
|
15
|
+
return Object.fromEntries([...new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => {
|
|
16
|
+
const previous = base[key];
|
|
17
|
+
const next = override[key];
|
|
18
|
+
if (!Object.hasOwn(override, key))
|
|
19
|
+
return [key, previous];
|
|
20
|
+
return [key, isObject(previous) && isObject(next) ? merge(previous, next) : next];
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
/** Import operator providers only, before the adapter replaces OpenCode's paths.
|
|
24
|
+
* OpenCode V2 accepts legacy `provider` entries alongside native `providers`;
|
|
25
|
+
* let its own compatibility loader handle model capabilities and variants.
|
|
26
|
+
* Never discover repository config or import its plugins, MCP, or permissions.
|
|
27
|
+
*/
|
|
28
|
+
async function loadOpenCodeProviders(env, generation) {
|
|
29
|
+
const setting = (name) => env[name] ?? process.env[name];
|
|
30
|
+
const home = setting("HOME") || node_os_1.default.homedir();
|
|
31
|
+
const expand = (file) => file.startsWith("~/") ? node_path_1.default.join(home, file.slice(2)) : file;
|
|
32
|
+
const globalDir = node_path_1.default.join(setting("XDG_CONFIG_HOME") || node_path_1.default.join(home, ".config"), "opencode");
|
|
33
|
+
let result = {};
|
|
34
|
+
let secretIndex = 0;
|
|
35
|
+
// Resolve file references against their original config, then keep credentials
|
|
36
|
+
// out of both the generated file and OPENCODE_CONFIG_CONTENT. Existing env
|
|
37
|
+
// references remain references and are resolved by the OpenCode child.
|
|
38
|
+
async function prepare(value, directory, sensitive = false) {
|
|
39
|
+
if (Array.isArray(value))
|
|
40
|
+
return Promise.all(value.map((item) => prepare(item, directory, sensitive)));
|
|
41
|
+
if (isObject(value)) {
|
|
42
|
+
return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, item]) => [
|
|
43
|
+
key, await prepare(item, directory, sensitive || /^(?:api[_-]?key|token|password|secret|headers)$/i.test(key)),
|
|
44
|
+
])));
|
|
45
|
+
}
|
|
46
|
+
if (typeof value !== "string")
|
|
47
|
+
return value;
|
|
48
|
+
const fromFile = /\{file:[^}]+\}/.test(value);
|
|
49
|
+
if (!fromFile && (!sensitive || !value || /^\{env:[^}]+\}$/.test(value)))
|
|
50
|
+
return value;
|
|
51
|
+
// Mixed strings such as "Bearer {env:TOKEN}" need expansion before moving
|
|
52
|
+
// the whole header into another env var; substitutions are not recursive.
|
|
53
|
+
let resolved = value.replace(/\{env:([^}]+)\}/g, (_match, name) => setting(name) ?? "");
|
|
54
|
+
for (const match of value.matchAll(/\{file:([^}]+)\}/g)) {
|
|
55
|
+
const file = node_path_1.default.resolve(directory, expand(match[1]));
|
|
56
|
+
const contents = (await node_fs_1.promises.readFile(file, "utf8")).trim();
|
|
57
|
+
resolved = resolved.replace(match[0], () => contents);
|
|
58
|
+
}
|
|
59
|
+
let name;
|
|
60
|
+
do {
|
|
61
|
+
name = `NAVARCH_OPENCODE_PROVIDER_${secretIndex++}`;
|
|
62
|
+
} while (setting(name) !== undefined);
|
|
63
|
+
env[name] = resolved;
|
|
64
|
+
return `{env:${name}}`;
|
|
65
|
+
}
|
|
66
|
+
async function include(text, source, directory) {
|
|
67
|
+
const errors = [];
|
|
68
|
+
const config = (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true });
|
|
69
|
+
if (errors.length || !isObject(config)) {
|
|
70
|
+
// Parser diagnostics can contain literal credentials; report only source.
|
|
71
|
+
throw new Error(`Invalid OpenCode JSON/JSONC configuration in ${source}.`);
|
|
72
|
+
}
|
|
73
|
+
const picked = {};
|
|
74
|
+
for (const key of generation === 1 ? ["provider"] : ["provider", "providers"]) {
|
|
75
|
+
if (config[key] === undefined)
|
|
76
|
+
continue;
|
|
77
|
+
if (!isObject(config[key]))
|
|
78
|
+
throw new Error(`OpenCode ${key} must be an object in ${source}.`);
|
|
79
|
+
picked[key] = await prepare(config[key], directory);
|
|
80
|
+
}
|
|
81
|
+
result = merge(result, picked);
|
|
82
|
+
}
|
|
83
|
+
async function file(filePath, optional) {
|
|
84
|
+
const absolute = node_path_1.default.resolve(expand(filePath));
|
|
85
|
+
let text;
|
|
86
|
+
try {
|
|
87
|
+
text = await node_fs_1.promises.readFile(absolute, "utf8");
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (optional && error.code === "ENOENT")
|
|
91
|
+
return;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
await include(text, absolute, node_path_1.default.dirname(absolute));
|
|
95
|
+
}
|
|
96
|
+
for (const name of ["opencode.json", "opencode.jsonc"])
|
|
97
|
+
await file(node_path_1.default.join(globalDir, name), true);
|
|
98
|
+
const explicit = setting("OPENCODE_CONFIG");
|
|
99
|
+
if (explicit)
|
|
100
|
+
await file(explicit, false);
|
|
101
|
+
const directory = setting("OPENCODE_CONFIG_DIR");
|
|
102
|
+
if (directory) {
|
|
103
|
+
for (const name of ["opencode.json", "opencode.jsonc"])
|
|
104
|
+
await file(node_path_1.default.join(expand(directory), name), true);
|
|
105
|
+
}
|
|
106
|
+
const inline = setting("OPENCODE_CONFIG_CONTENT");
|
|
107
|
+
if (inline)
|
|
108
|
+
await include(inline, "OPENCODE_CONFIG_CONTENT", process.cwd());
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
@@ -12,12 +12,20 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const config_cjs_1 = require("../config.cjs");
|
|
14
14
|
const kill_agent_cjs_1 = require("../kill-agent.cjs");
|
|
15
|
+
const opencode_providers_cjs_1 = require("./opencode-providers.cjs");
|
|
15
16
|
/**
|
|
16
|
-
* Headless OpenCode adapter, fixture-pinned to the OpenCode JSON
|
|
17
|
-
* contract shared by V1 and V2 (verified against 2.0.6):
|
|
17
|
+
* Headless OpenCode adapter, fixture-pinned to the OpenCode JSON event
|
|
18
|
+
* contract shared by V1 and V2 (verified against 1.19.1, 2.0.6 and 2.0.17):
|
|
18
19
|
*
|
|
19
20
|
* opencode run --format json [--model provider/model[#variant]] < prompt
|
|
20
21
|
*
|
|
22
|
+
* V1 (still npm's `latest`) differs in two ways the adapter must follow: its
|
|
23
|
+
* config takes V1 `permission` rules and flat `mcp` entries, and it rejects
|
|
24
|
+
* V2's `permissions`/`mcp.servers` outright ("V2 permissions are not
|
|
25
|
+
* supported by OpenCode V1"); and it takes the reasoning variant as
|
|
26
|
+
* `--variant`, reporting `model#variant` as an unknown model. A `--version`
|
|
27
|
+
* probe picks the dialect per session, defaulting to V2.
|
|
28
|
+
*
|
|
21
29
|
* The prompt goes in on stdin, which `opencode run` reads as the message when
|
|
22
30
|
* none is given (verified against 2.0.17). Linux rejects any single argv
|
|
23
31
|
* string over 128 KiB (`spawn E2BIG`), and an argv prompt also let an
|
|
@@ -27,8 +35,9 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
|
|
|
27
35
|
* so each Navarch session gets an isolated XDG/config directory, disables
|
|
28
36
|
* project discovery, and supplies its complete policy as both a private file
|
|
29
37
|
* and the highest-precedence inline config. Provider credentials remain in
|
|
30
|
-
* OpenCode's data store or the child environment
|
|
31
|
-
*
|
|
38
|
+
* OpenCode's data store or the child environment. Operator provider definitions
|
|
39
|
+
* are copied without importing unrelated global config; credential and MCP
|
|
40
|
+
* values use env references in the generated file.
|
|
32
41
|
*/
|
|
33
42
|
async function runOpenCodeAdapter(options) {
|
|
34
43
|
if (options.opencodeHostGuard && !options.dockerExec) {
|
|
@@ -45,16 +54,20 @@ async function runOpenCodeAdapter(options) {
|
|
|
45
54
|
// supervisor's PWD in the inherited environment (often the home directory).
|
|
46
55
|
if (!options.dockerExec && options.cwd)
|
|
47
56
|
env.PWD = node_path_1.default.resolve(options.cwd);
|
|
48
|
-
|
|
57
|
+
const probe = (probeArgs) => {
|
|
58
|
+
const probeOptions = { ...options, env, timeoutMs: 10_000, onProcessStarted: undefined };
|
|
59
|
+
return options.dockerExec ? runViaDocker(probeOptions, probeArgs) : runOnHost(probeOptions, probeArgs);
|
|
60
|
+
};
|
|
61
|
+
// Neither generation loads config for --version, so the probe runs before
|
|
62
|
+
// the config is written in the matching dialect.
|
|
63
|
+
const generation = openCodeGeneration(await probe(["--version"]));
|
|
64
|
+
await prepareOpenCodeConfig(options.mcpConfigPath, env, Boolean(options.dockerExec), generation);
|
|
49
65
|
// V2's default background service owns its own cwd, environment and MCP
|
|
50
66
|
// configuration. A client process's isolated config does not isolate that
|
|
51
67
|
// service. Probe help without starting a turn, so older in-process CLIs
|
|
52
68
|
// remain supported and upgrades are detected on the next session.
|
|
53
69
|
const runOptions = { ...options, env };
|
|
54
|
-
const
|
|
55
|
-
const help = options.dockerExec
|
|
56
|
-
? await runViaDocker(probeOptions, ["run", "--help"])
|
|
57
|
-
: await runOnHost(probeOptions, ["run", "--help"]);
|
|
70
|
+
const help = await probe(["run", "--help"]);
|
|
58
71
|
if (help.exitCode !== 0 || help.timedOut || help.killedByLeaseLoss) {
|
|
59
72
|
return { ...help, stderr: `OpenCode CLI capability check failed. ${help.stderr}` };
|
|
60
73
|
}
|
|
@@ -76,10 +89,18 @@ async function runOpenCodeAdapter(options) {
|
|
|
76
89
|
if (options.model && options.model !== "default" && !hasArg(options.extraArgs, "--model", "-m")) {
|
|
77
90
|
// OpenCode V2 folds the reasoning variant into the model reference
|
|
78
91
|
// (`provider/model#variant`) and hard-fails on a variant the model does
|
|
79
|
-
// not advertise
|
|
80
|
-
// advertised variants, so a present value is
|
|
81
|
-
// use the model's own default.
|
|
82
|
-
|
|
92
|
+
// not advertise; V1 takes it as `--variant`. The claim route clamps the
|
|
93
|
+
// effort to the model's advertised variants, so a present value is
|
|
94
|
+
// always valid; absent means use the model's own default.
|
|
95
|
+
if (generation === 1) {
|
|
96
|
+
args.push("--model", options.model);
|
|
97
|
+
if (options.reasoningEffort && !hasArg(options.extraArgs, "--variant")) {
|
|
98
|
+
args.push("--variant", options.reasoningEffort);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
args.push("--model", options.reasoningEffort ? `${options.model}#${options.reasoningEffort}` : options.model);
|
|
103
|
+
}
|
|
83
104
|
}
|
|
84
105
|
const run = (runArgs) => runOptions.dockerExec
|
|
85
106
|
? runViaDocker(runOptions, runArgs, options.prompt)
|
|
@@ -110,10 +131,20 @@ function isLogLevelRejection(stderr) {
|
|
|
110
131
|
function resetOpenCodeLogLevelCache() {
|
|
111
132
|
openCodeLogLevels.clear();
|
|
112
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* V1 prints a bare `1.19.1`, V2 `opencode v2.0.17`. Anything unrecognized
|
|
136
|
+
* keeps the V2 dialect the adapter has always written.
|
|
137
|
+
*/
|
|
138
|
+
function openCodeGeneration(version) {
|
|
139
|
+
if (version.exitCode !== 0)
|
|
140
|
+
return 2;
|
|
141
|
+
const major = version.stdout.match(/^\s*(?:opencode\s+)?v?(\d+)\.\d+\.\d+/i)?.[1];
|
|
142
|
+
return major !== undefined && Number(major) < 2 ? 1 : 2;
|
|
143
|
+
}
|
|
113
144
|
function hasArg(args, ...flags) {
|
|
114
145
|
return args.some((arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
|
|
115
146
|
}
|
|
116
|
-
async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker) {
|
|
147
|
+
async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker, generation) {
|
|
117
148
|
if (!mcpConfigPath) {
|
|
118
149
|
throw new Error("OpenCode requires a per-session MCP config path for isolated execution.");
|
|
119
150
|
}
|
|
@@ -143,28 +174,51 @@ async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker) {
|
|
|
143
174
|
mcp[name] = {
|
|
144
175
|
type: "remote",
|
|
145
176
|
url,
|
|
146
|
-
disabled: false,
|
|
177
|
+
...(generation === 1 ? { enabled: true } : { disabled: false }),
|
|
147
178
|
oauth: false,
|
|
148
179
|
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
149
180
|
};
|
|
150
181
|
}
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
const managedDirectories = await openCodeManagedDirectories(env, inDocker);
|
|
183
|
+
// Host provider endpoints and credentials belong to the operator's host.
|
|
184
|
+
// Docker continues to use its own provider setup, never the host's files.
|
|
185
|
+
const providers = inDocker ? {} : await (0, opencode_providers_cjs_1.loadOpenCodeProviders)(env, generation);
|
|
186
|
+
// Same policy in both dialects; in each the last matching rule wins.
|
|
187
|
+
const config = generation === 1
|
|
188
|
+
? {
|
|
189
|
+
$schema: "https://opencode.ai/config.json",
|
|
190
|
+
...providers,
|
|
191
|
+
autoupdate: false,
|
|
192
|
+
share: "disabled",
|
|
193
|
+
plugin: [],
|
|
194
|
+
mcp,
|
|
195
|
+
permission: {
|
|
196
|
+
"*": "allow",
|
|
197
|
+
external_directory: {
|
|
198
|
+
"*": "deny",
|
|
199
|
+
...Object.fromEntries(managedDirectories.map((resource) => [resource, "allow"])),
|
|
200
|
+
},
|
|
201
|
+
question: "deny",
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
: {
|
|
205
|
+
$schema: "https://opencode.ai/config.json",
|
|
206
|
+
...providers,
|
|
207
|
+
update: "disable",
|
|
208
|
+
share: "disabled",
|
|
209
|
+
plugins: [],
|
|
210
|
+
mcp: { servers: mcp },
|
|
211
|
+
permissions: [
|
|
212
|
+
{ action: "*", resource: "*", effect: "allow" },
|
|
213
|
+
{ action: "external_directory", resource: "*", effect: "deny" },
|
|
214
|
+
...managedDirectories.map((resource) => ({
|
|
215
|
+
action: "external_directory",
|
|
216
|
+
resource,
|
|
217
|
+
effect: "allow",
|
|
218
|
+
})),
|
|
219
|
+
{ action: "question", resource: "*", effect: "deny" },
|
|
220
|
+
],
|
|
221
|
+
};
|
|
168
222
|
const configPath = `${mcpConfigPath}.opencode.json`;
|
|
169
223
|
const configDir = `${mcpConfigPath}.opencode-config`;
|
|
170
224
|
const xdgConfigHome = `${mcpConfigPath}.opencode-xdg`;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
3.3.0 2022-06-24
|
|
2
|
+
=================
|
|
3
|
+
- `JSONVisitor.onObjectBegin` and `JSONVisitor.onArrayBegin` can now return `false` to instruct the visitor that no children should be visited.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
3.2.0 2022-08-30
|
|
7
|
+
=================
|
|
8
|
+
- update the version of the bundled Javascript files to `es2020`.
|
|
9
|
+
- include all `const enum` values in the bundled JavaScript files (`ScanError`, `SyntaxKind`, `ParseErrorCode`).
|
|
10
|
+
|
|
11
|
+
3.1.0 2022-07-07
|
|
12
|
+
==================
|
|
13
|
+
* added new API `FormattingOptions.keepLines` : It leaves the initial line positions in the formatting.
|
|
14
|
+
|
|
15
|
+
3.0.0 2020-11-13
|
|
16
|
+
==================
|
|
17
|
+
* fixed API spec for `parseTree`. Can return `undefine` for empty input.
|
|
18
|
+
* added new API `FormattingOptions.insertFinalNewline`.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
2.3.0 2020-07-03
|
|
22
|
+
==================
|
|
23
|
+
* new API `ModificationOptions.isArrayInsertion`: If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then `modify` will insert a new item at that location instead of overwriting its contents.
|
|
24
|
+
* `ModificationOptions.formattingOptions` is now optional. If not set, newly inserted content will not be formatted.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
2.2.0 2019-10-25
|
|
28
|
+
==================
|
|
29
|
+
* added `ParseOptions.allowEmptyContent`. Default is `false`.
|
|
30
|
+
* new API `getNodeType`: Returns the type of a value returned by parse.
|
|
31
|
+
* `parse`: Fix issue with empty property name
|
|
32
|
+
|
|
33
|
+
2.1.0 2019-03-29
|
|
34
|
+
==================
|
|
35
|
+
* `JSONScanner` and `JSONVisitor` return lineNumber / character.
|
|
36
|
+
|
|
37
|
+
2.0.0 2018-04-12
|
|
38
|
+
==================
|
|
39
|
+
* renamed `Node.columnOffset` to `Node.colonOffset`
|
|
40
|
+
* new API `getNodePath`: Gets the JSON path of the given JSON DOM node
|
|
41
|
+
* new API `findNodeAtOffset`: Finds the most inner node at the given offset. If `includeRightBound` is set, also finds nodes that end at the given offset.
|
|
42
|
+
|
|
43
|
+
1.0.3 2018-03-07
|
|
44
|
+
==================
|
|
45
|
+
* provide ems modules
|
|
46
|
+
|
|
47
|
+
1.0.2 2018-03-05
|
|
48
|
+
==================
|
|
49
|
+
* added the `visit.onComment` API, reported when comments are allowed.
|
|
50
|
+
* added the `ParseErrorCode.InvalidCommentToken` enum value, reported when comments are disallowed.
|
|
51
|
+
|
|
52
|
+
1.0.1
|
|
53
|
+
==================
|
|
54
|
+
* added the `format` API: computes edits to format a JSON document.
|
|
55
|
+
* added the `modify` API: computes edits to insert, remove or replace a property or value in a JSON document.
|
|
56
|
+
* added the `allyEdits` API: applies edits to a document
|
|
57
|
+
|
|
58
|
+
1.0.0
|
|
59
|
+
==================
|
|
60
|
+
* remove nls dependency (remove `getParseErrorMessage`)
|
|
61
|
+
|
|
62
|
+
0.4.2 / 2017-05-05
|
|
63
|
+
==================
|
|
64
|
+
* added `ParseError.offset` & `ParseError.length`
|
|
65
|
+
|
|
66
|
+
0.4.1 / 2017-04-02
|
|
67
|
+
==================
|
|
68
|
+
* added `ParseOptions.allowTrailingComma`
|
|
69
|
+
|
|
70
|
+
0.4.0 / 2017-02-23
|
|
71
|
+
==================
|
|
72
|
+
* fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']`
|
|
73
|
+
|
|
74
|
+
0.3.0 / 2017-01-17
|
|
75
|
+
==================
|
|
76
|
+
* Updating to typescript 2.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|