opencode-gateway 0.2.0 → 0.2.1

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 CHANGED
@@ -12,13 +12,14 @@ npx opencode-gateway init
12
12
 
13
13
  This ensures:
14
14
 
15
- - `plugin: ["opencode-gateway"]` exists in `opencode.json`
16
- - `opencode-gateway.toml` exists next to `opencode.json`
15
+ - `plugin` contains `opencode-gateway@latest`
16
+ - `opencode-gateway.toml` exists next to the preferred OpenCode config file
17
17
 
18
18
  By default the CLI uses `OPENCODE_CONFIG_DIR` when it is set, otherwise it
19
19
  writes to:
20
20
 
21
- - `~/.config/opencode/opencode.json`
21
+ - an existing `~/.config/opencode/opencode.jsonc` or `~/.config/opencode/opencode.json`
22
+ - otherwise a new `~/.config/opencode/opencode.jsonc`
22
23
  - `~/.config/opencode/opencode-gateway.toml`
23
24
 
24
25
  Check what it resolved:
@@ -1,18 +1,19 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { GATEWAY_CONFIG_FILE, OPENCODE_CONFIG_FILE, resolveGatewayWorkspacePath } from "../config/paths";
4
- import { parseOpencodeConfig } from "./opencode-config";
3
+ import { GATEWAY_CONFIG_FILE, resolveGatewayWorkspacePath } from "../config/paths";
4
+ import { inspectGatewayPlugin, parseOpencodeConfig } from "./opencode-config";
5
+ import { resolveOpencodeConfigFile } from "./opencode-config-file";
5
6
  import { pathExists, resolveCliConfigDir } from "./paths";
6
7
  export async function runDoctor(options, env) {
7
8
  const configDir = resolveCliConfigDir(options, env);
8
- const opencodeConfigPath = join(configDir, OPENCODE_CONFIG_FILE);
9
9
  const gatewayConfigPath = join(configDir, GATEWAY_CONFIG_FILE);
10
10
  const workspaceDirPath = resolveGatewayWorkspacePath(gatewayConfigPath);
11
- const opencodeStatus = await inspectOpencodeConfig(opencodeConfigPath);
11
+ const opencodeConfig = await resolveOpencodeConfigFile(configDir);
12
+ const opencodeStatus = await inspectOpencodeConfig(opencodeConfig.path);
12
13
  const gatewayOverride = env.OPENCODE_GATEWAY_CONFIG?.trim() || null;
13
14
  console.log("doctor report");
14
15
  console.log(` config dir: ${configDir}`);
15
- console.log(` opencode config: ${await describePath(opencodeConfigPath)}`);
16
+ console.log(` opencode config: ${await describePath(opencodeConfig.path)}`);
16
17
  console.log(` gateway config: ${await describePath(gatewayConfigPath)}`);
17
18
  console.log(` gateway workspace: ${await describePath(workspaceDirPath)}`);
18
19
  console.log(` gateway config override: ${gatewayOverride ?? "not set"}`);
@@ -34,21 +35,8 @@ async function inspectOpencodeConfig(path) {
34
35
  }
35
36
  try {
36
37
  const parsed = parseOpencodeConfig(await readFile(path, "utf8"), path);
37
- const plugins = parsed.plugin;
38
- if (plugins === undefined) {
39
- return {
40
- pluginConfigured: "no",
41
- error: null,
42
- };
43
- }
44
- if (!Array.isArray(plugins)) {
45
- return {
46
- pluginConfigured: "invalid",
47
- error: "`plugin` is not an array",
48
- };
49
- }
50
38
  return {
51
- pluginConfigured: plugins.some((entry) => entry === "opencode-gateway") ? "yes" : "no",
39
+ pluginConfigured: inspectGatewayPlugin(parsed),
52
40
  error: null,
53
41
  };
54
42
  }
package/dist/cli/init.js CHANGED
@@ -1,18 +1,20 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
- import { defaultGatewayStateDbPath, GATEWAY_CONFIG_FILE, OPENCODE_CONFIG_FILE, resolveGatewayWorkspacePath, } from "../config/paths";
3
+ import { defaultGatewayStateDbPath, GATEWAY_CONFIG_FILE, resolveGatewayWorkspacePath } from "../config/paths";
4
4
  import { createDefaultOpencodeConfig, ensureGatewayPlugin, parseOpencodeConfig, stringifyOpencodeConfig, } from "./opencode-config";
5
+ import { resolveOpencodeConfigFile } from "./opencode-config-file";
5
6
  import { pathExists, resolveCliConfigDir } from "./paths";
6
7
  import { buildGatewayConfigTemplate } from "./templates";
7
8
  export async function runInit(options, env) {
8
9
  const configDir = resolveCliConfigDir(options, env);
9
- const opencodeConfigPath = join(configDir, OPENCODE_CONFIG_FILE);
10
10
  const gatewayConfigPath = join(configDir, GATEWAY_CONFIG_FILE);
11
11
  const workspaceDirPath = resolveGatewayWorkspacePath(gatewayConfigPath);
12
+ const opencodeConfig = await resolveOpencodeConfigFile(configDir);
13
+ const opencodeConfigPath = opencodeConfig.path;
12
14
  await mkdir(configDir, { recursive: true });
13
15
  await mkdir(workspaceDirPath, { recursive: true });
14
16
  let opencodeStatus = "already present";
15
- if (!(await pathExists(opencodeConfigPath))) {
17
+ if (!opencodeConfig.exists) {
16
18
  await writeFile(opencodeConfigPath, stringifyOpencodeConfig(createDefaultOpencodeConfig(options.managed)));
17
19
  opencodeStatus = "created";
18
20
  }
@@ -0,0 +1,5 @@
1
+ export type ResolvedOpencodeConfigFile = {
2
+ path: string;
3
+ exists: boolean;
4
+ };
5
+ export declare function resolveOpencodeConfigFile(configDir: string): Promise<ResolvedOpencodeConfigFile>;
@@ -0,0 +1,18 @@
1
+ import { join } from "node:path";
2
+ import { OPENCODE_CONFIG_FILE_CANDIDATES, OPENCODE_CONFIG_FILE_JSONC } from "../config/paths";
3
+ import { pathExists } from "./paths";
4
+ export async function resolveOpencodeConfigFile(configDir) {
5
+ for (const fileName of OPENCODE_CONFIG_FILE_CANDIDATES) {
6
+ const path = join(configDir, fileName);
7
+ if (await pathExists(path)) {
8
+ return {
9
+ path,
10
+ exists: true,
11
+ };
12
+ }
13
+ }
14
+ return {
15
+ path: join(configDir, OPENCODE_CONFIG_FILE_JSONC),
16
+ exists: false,
17
+ };
18
+ }
@@ -3,8 +3,10 @@ export type EnsurePluginResult = {
3
3
  changed: boolean;
4
4
  document: OpencodeConfigDocument;
5
5
  };
6
+ export type GatewayPluginStatus = "yes" | "no" | "needs_update";
6
7
  export declare function createDefaultOpencodeConfig(managed: boolean): OpencodeConfigDocument;
7
8
  export declare function ensureGatewayPlugin(document: OpencodeConfigDocument): EnsurePluginResult;
8
9
  export declare function parseOpencodeConfig(source: string, path: string): OpencodeConfigDocument;
9
10
  export declare function stringifyOpencodeConfig(document: OpencodeConfigDocument): string;
11
+ export declare function inspectGatewayPlugin(document: OpencodeConfigDocument): GatewayPluginStatus;
10
12
  export {};
@@ -1,9 +1,10 @@
1
1
  const OPENCODE_SCHEMA_URL = "https://opencode.ai/config.json";
2
2
  const PACKAGE_NAME = "opencode-gateway";
3
+ const PACKAGE_SPEC = "opencode-gateway@latest";
3
4
  export function createDefaultOpencodeConfig(managed) {
4
5
  const document = {
5
6
  $schema: OPENCODE_SCHEMA_URL,
6
- plugin: [PACKAGE_NAME],
7
+ plugin: [PACKAGE_SPEC],
7
8
  };
8
9
  if (managed) {
9
10
  document.server = {
@@ -14,20 +15,17 @@ export function createDefaultOpencodeConfig(managed) {
14
15
  return document;
15
16
  }
16
17
  export function ensureGatewayPlugin(document) {
17
- const plugins = document.plugin;
18
+ const plugins = readPluginArray(document);
18
19
  if (plugins === undefined) {
19
20
  return {
20
21
  changed: true,
21
22
  document: {
22
23
  ...document,
23
- plugin: [PACKAGE_NAME],
24
+ plugin: [PACKAGE_SPEC],
24
25
  },
25
26
  };
26
27
  }
27
- if (!Array.isArray(plugins)) {
28
- throw new Error("opencode.json field `plugin` must be an array when present");
29
- }
30
- if (plugins.some((entry) => entry === PACKAGE_NAME)) {
28
+ if (plugins.includes(PACKAGE_SPEC)) {
31
29
  return {
32
30
  changed: false,
33
31
  document,
@@ -37,14 +35,14 @@ export function ensureGatewayPlugin(document) {
37
35
  changed: true,
38
36
  document: {
39
37
  ...document,
40
- plugin: [...plugins, PACKAGE_NAME],
38
+ plugin: [...plugins, PACKAGE_SPEC],
41
39
  },
42
40
  };
43
41
  }
44
42
  export function parseOpencodeConfig(source, path) {
45
43
  let parsed;
46
44
  try {
47
- parsed = JSON.parse(source);
45
+ parsed = JSON.parse(toStrictJson(source));
48
46
  }
49
47
  catch (error) {
50
48
  throw new Error(`failed to parse opencode config ${path}: ${formatError(error)}`);
@@ -57,6 +55,140 @@ export function parseOpencodeConfig(source, path) {
57
55
  export function stringifyOpencodeConfig(document) {
58
56
  return `${JSON.stringify(document, null, 2)}\n`;
59
57
  }
58
+ export function inspectGatewayPlugin(document) {
59
+ const plugins = readPluginArray(document);
60
+ if (plugins === undefined) {
61
+ return "no";
62
+ }
63
+ if (plugins.includes(PACKAGE_SPEC)) {
64
+ return "yes";
65
+ }
66
+ return plugins.some((entry) => isGatewayPluginReference(entry)) ? "needs_update" : "no";
67
+ }
60
68
  function formatError(error) {
61
69
  return error instanceof Error ? error.message : String(error);
62
70
  }
71
+ function readPluginArray(document) {
72
+ const plugins = document.plugin;
73
+ if (plugins === undefined) {
74
+ return undefined;
75
+ }
76
+ if (!Array.isArray(plugins)) {
77
+ throw new Error("opencode config field `plugin` must be an array when present");
78
+ }
79
+ const normalized = [];
80
+ for (const [index, entry] of plugins.entries()) {
81
+ if (typeof entry !== "string") {
82
+ throw new Error(`opencode config field \`plugin[${index}]\` must be a string`);
83
+ }
84
+ normalized.push(entry);
85
+ }
86
+ return normalized;
87
+ }
88
+ function toStrictJson(source) {
89
+ return stripTrailingCommas(stripJsonComments(source));
90
+ }
91
+ function stripJsonComments(source) {
92
+ let result = "";
93
+ let inString = false;
94
+ let escaped = false;
95
+ let inLineComment = false;
96
+ let inBlockComment = false;
97
+ for (let index = 0; index < source.length; index += 1) {
98
+ const current = source[index];
99
+ const next = source[index + 1];
100
+ if (inLineComment) {
101
+ if (current === "\n") {
102
+ inLineComment = false;
103
+ result += current;
104
+ }
105
+ continue;
106
+ }
107
+ if (inBlockComment) {
108
+ if (current === "*" && next === "/") {
109
+ inBlockComment = false;
110
+ index += 1;
111
+ }
112
+ else if (current === "\n") {
113
+ result += current;
114
+ }
115
+ continue;
116
+ }
117
+ if (inString) {
118
+ result += current;
119
+ if (escaped) {
120
+ escaped = false;
121
+ }
122
+ else if (current === "\\") {
123
+ escaped = true;
124
+ }
125
+ else if (current === '"') {
126
+ inString = false;
127
+ }
128
+ continue;
129
+ }
130
+ if (current === '"') {
131
+ inString = true;
132
+ result += current;
133
+ continue;
134
+ }
135
+ if (current === "/" && next === "/") {
136
+ inLineComment = true;
137
+ index += 1;
138
+ continue;
139
+ }
140
+ if (current === "/" && next === "*") {
141
+ inBlockComment = true;
142
+ index += 1;
143
+ continue;
144
+ }
145
+ result += current;
146
+ }
147
+ return result;
148
+ }
149
+ function stripTrailingCommas(source) {
150
+ let result = "";
151
+ let inString = false;
152
+ let escaped = false;
153
+ for (let index = 0; index < source.length; index += 1) {
154
+ const current = source[index];
155
+ if (inString) {
156
+ result += current;
157
+ if (escaped) {
158
+ escaped = false;
159
+ }
160
+ else if (current === "\\") {
161
+ escaped = true;
162
+ }
163
+ else if (current === '"') {
164
+ inString = false;
165
+ }
166
+ continue;
167
+ }
168
+ if (current === '"') {
169
+ inString = true;
170
+ result += current;
171
+ continue;
172
+ }
173
+ if (current === ",") {
174
+ const nextSignificant = findNextSignificantCharacter(source, index + 1);
175
+ if (nextSignificant === "]" || nextSignificant === "}") {
176
+ continue;
177
+ }
178
+ }
179
+ result += current;
180
+ }
181
+ return result;
182
+ }
183
+ function findNextSignificantCharacter(source, startIndex) {
184
+ for (let index = startIndex; index < source.length; index += 1) {
185
+ const current = source[index];
186
+ if (!/\s/.test(current)) {
187
+ return current;
188
+ }
189
+ }
190
+ return null;
191
+ }
192
+ function isGatewayPluginReference(entry) {
193
+ return entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`);
194
+ }
package/dist/cli.js CHANGED
@@ -58,13 +58,15 @@ function formatCliHelp() {
58
58
 
59
59
  // src/cli/doctor.ts
60
60
  import { readFile } from "node:fs/promises";
61
- import { join as join2 } from "node:path";
61
+ import { join as join3 } from "node:path";
62
62
 
63
63
  // src/config/paths.ts
64
64
  import { homedir } from "node:os";
65
65
  import { dirname, join, resolve as resolve2 } from "node:path";
66
66
  var GATEWAY_CONFIG_FILE = "opencode-gateway.toml";
67
67
  var OPENCODE_CONFIG_FILE = "opencode.json";
68
+ var OPENCODE_CONFIG_FILE_JSONC = "opencode.jsonc";
69
+ var OPENCODE_CONFIG_FILE_CANDIDATES = [OPENCODE_CONFIG_FILE_JSONC, OPENCODE_CONFIG_FILE];
68
70
  var GATEWAY_WORKSPACE_DIR = "opencode-gateway-workspace";
69
71
  function resolveOpencodeConfigDir(env) {
70
72
  const explicit = env.OPENCODE_CONFIG_DIR;
@@ -95,10 +97,11 @@ function defaultOpencodeConfigDir(env) {
95
97
  // src/cli/opencode-config.ts
96
98
  var OPENCODE_SCHEMA_URL = "https://opencode.ai/config.json";
97
99
  var PACKAGE_NAME = "opencode-gateway";
100
+ var PACKAGE_SPEC = "opencode-gateway@latest";
98
101
  function createDefaultOpencodeConfig(managed) {
99
102
  const document = {
100
103
  $schema: OPENCODE_SCHEMA_URL,
101
- plugin: [PACKAGE_NAME]
104
+ plugin: [PACKAGE_SPEC]
102
105
  };
103
106
  if (managed) {
104
107
  document.server = {
@@ -109,20 +112,17 @@ function createDefaultOpencodeConfig(managed) {
109
112
  return document;
110
113
  }
111
114
  function ensureGatewayPlugin(document) {
112
- const plugins = document.plugin;
115
+ const plugins = readPluginArray(document);
113
116
  if (plugins === undefined) {
114
117
  return {
115
118
  changed: true,
116
119
  document: {
117
120
  ...document,
118
- plugin: [PACKAGE_NAME]
121
+ plugin: [PACKAGE_SPEC]
119
122
  }
120
123
  };
121
124
  }
122
- if (!Array.isArray(plugins)) {
123
- throw new Error("opencode.json field `plugin` must be an array when present");
124
- }
125
- if (plugins.some((entry) => entry === PACKAGE_NAME)) {
125
+ if (plugins.includes(PACKAGE_SPEC)) {
126
126
  return {
127
127
  changed: false,
128
128
  document
@@ -132,14 +132,14 @@ function ensureGatewayPlugin(document) {
132
132
  changed: true,
133
133
  document: {
134
134
  ...document,
135
- plugin: [...plugins, PACKAGE_NAME]
135
+ plugin: [...plugins, PACKAGE_SPEC]
136
136
  }
137
137
  };
138
138
  }
139
139
  function parseOpencodeConfig(source, path) {
140
140
  let parsed;
141
141
  try {
142
- parsed = JSON.parse(source);
142
+ parsed = JSON.parse(toStrictJson(source));
143
143
  } catch (error) {
144
144
  throw new Error(`failed to parse opencode config ${path}: ${formatError(error)}`);
145
145
  }
@@ -152,9 +152,143 @@ function stringifyOpencodeConfig(document) {
152
152
  return `${JSON.stringify(document, null, 2)}
153
153
  `;
154
154
  }
155
+ function inspectGatewayPlugin(document) {
156
+ const plugins = readPluginArray(document);
157
+ if (plugins === undefined) {
158
+ return "no";
159
+ }
160
+ if (plugins.includes(PACKAGE_SPEC)) {
161
+ return "yes";
162
+ }
163
+ return plugins.some((entry) => isGatewayPluginReference(entry)) ? "needs_update" : "no";
164
+ }
155
165
  function formatError(error) {
156
166
  return error instanceof Error ? error.message : String(error);
157
167
  }
168
+ function readPluginArray(document) {
169
+ const plugins = document.plugin;
170
+ if (plugins === undefined) {
171
+ return;
172
+ }
173
+ if (!Array.isArray(plugins)) {
174
+ throw new Error("opencode config field `plugin` must be an array when present");
175
+ }
176
+ const normalized = [];
177
+ for (const [index, entry] of plugins.entries()) {
178
+ if (typeof entry !== "string") {
179
+ throw new Error(`opencode config field \`plugin[${index}]\` must be a string`);
180
+ }
181
+ normalized.push(entry);
182
+ }
183
+ return normalized;
184
+ }
185
+ function toStrictJson(source) {
186
+ return stripTrailingCommas(stripJsonComments(source));
187
+ }
188
+ function stripJsonComments(source) {
189
+ let result = "";
190
+ let inString = false;
191
+ let escaped = false;
192
+ let inLineComment = false;
193
+ let inBlockComment = false;
194
+ for (let index = 0;index < source.length; index += 1) {
195
+ const current = source[index];
196
+ const next = source[index + 1];
197
+ if (inLineComment) {
198
+ if (current === `
199
+ `) {
200
+ inLineComment = false;
201
+ result += current;
202
+ }
203
+ continue;
204
+ }
205
+ if (inBlockComment) {
206
+ if (current === "*" && next === "/") {
207
+ inBlockComment = false;
208
+ index += 1;
209
+ } else if (current === `
210
+ `) {
211
+ result += current;
212
+ }
213
+ continue;
214
+ }
215
+ if (inString) {
216
+ result += current;
217
+ if (escaped) {
218
+ escaped = false;
219
+ } else if (current === "\\") {
220
+ escaped = true;
221
+ } else if (current === '"') {
222
+ inString = false;
223
+ }
224
+ continue;
225
+ }
226
+ if (current === '"') {
227
+ inString = true;
228
+ result += current;
229
+ continue;
230
+ }
231
+ if (current === "/" && next === "/") {
232
+ inLineComment = true;
233
+ index += 1;
234
+ continue;
235
+ }
236
+ if (current === "/" && next === "*") {
237
+ inBlockComment = true;
238
+ index += 1;
239
+ continue;
240
+ }
241
+ result += current;
242
+ }
243
+ return result;
244
+ }
245
+ function stripTrailingCommas(source) {
246
+ let result = "";
247
+ let inString = false;
248
+ let escaped = false;
249
+ for (let index = 0;index < source.length; index += 1) {
250
+ const current = source[index];
251
+ if (inString) {
252
+ result += current;
253
+ if (escaped) {
254
+ escaped = false;
255
+ } else if (current === "\\") {
256
+ escaped = true;
257
+ } else if (current === '"') {
258
+ inString = false;
259
+ }
260
+ continue;
261
+ }
262
+ if (current === '"') {
263
+ inString = true;
264
+ result += current;
265
+ continue;
266
+ }
267
+ if (current === ",") {
268
+ const nextSignificant = findNextSignificantCharacter(source, index + 1);
269
+ if (nextSignificant === "]" || nextSignificant === "}") {
270
+ continue;
271
+ }
272
+ }
273
+ result += current;
274
+ }
275
+ return result;
276
+ }
277
+ function findNextSignificantCharacter(source, startIndex) {
278
+ for (let index = startIndex;index < source.length; index += 1) {
279
+ const current = source[index];
280
+ if (!/\s/.test(current)) {
281
+ return current;
282
+ }
283
+ }
284
+ return null;
285
+ }
286
+ function isGatewayPluginReference(entry) {
287
+ return entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`);
288
+ }
289
+
290
+ // src/cli/opencode-config-file.ts
291
+ import { join as join2 } from "node:path";
158
292
 
159
293
  // src/cli/paths.ts
160
294
  import { constants } from "node:fs";
@@ -178,17 +312,34 @@ async function pathExists(path) {
178
312
  }
179
313
  }
180
314
 
315
+ // src/cli/opencode-config-file.ts
316
+ async function resolveOpencodeConfigFile(configDir) {
317
+ for (const fileName of OPENCODE_CONFIG_FILE_CANDIDATES) {
318
+ const path = join2(configDir, fileName);
319
+ if (await pathExists(path)) {
320
+ return {
321
+ path,
322
+ exists: true
323
+ };
324
+ }
325
+ }
326
+ return {
327
+ path: join2(configDir, OPENCODE_CONFIG_FILE_JSONC),
328
+ exists: false
329
+ };
330
+ }
331
+
181
332
  // src/cli/doctor.ts
182
333
  async function runDoctor(options, env) {
183
334
  const configDir = resolveCliConfigDir(options, env);
184
- const opencodeConfigPath = join2(configDir, OPENCODE_CONFIG_FILE);
185
- const gatewayConfigPath = join2(configDir, GATEWAY_CONFIG_FILE);
335
+ const gatewayConfigPath = join3(configDir, GATEWAY_CONFIG_FILE);
186
336
  const workspaceDirPath = resolveGatewayWorkspacePath(gatewayConfigPath);
187
- const opencodeStatus = await inspectOpencodeConfig(opencodeConfigPath);
337
+ const opencodeConfig = await resolveOpencodeConfigFile(configDir);
338
+ const opencodeStatus = await inspectOpencodeConfig(opencodeConfig.path);
188
339
  const gatewayOverride = env.OPENCODE_GATEWAY_CONFIG?.trim() || null;
189
340
  console.log("doctor report");
190
341
  console.log(` config dir: ${configDir}`);
191
- console.log(` opencode config: ${await describePath(opencodeConfigPath)}`);
342
+ console.log(` opencode config: ${await describePath(opencodeConfig.path)}`);
192
343
  console.log(` gateway config: ${await describePath(gatewayConfigPath)}`);
193
344
  console.log(` gateway workspace: ${await describePath(workspaceDirPath)}`);
194
345
  console.log(` gateway config override: ${gatewayOverride ?? "not set"}`);
@@ -210,21 +361,8 @@ async function inspectOpencodeConfig(path) {
210
361
  }
211
362
  try {
212
363
  const parsed = parseOpencodeConfig(await readFile(path, "utf8"), path);
213
- const plugins = parsed.plugin;
214
- if (plugins === undefined) {
215
- return {
216
- pluginConfigured: "no",
217
- error: null
218
- };
219
- }
220
- if (!Array.isArray(plugins)) {
221
- return {
222
- pluginConfigured: "invalid",
223
- error: "`plugin` is not an array"
224
- };
225
- }
226
364
  return {
227
- pluginConfigured: plugins.some((entry) => entry === "opencode-gateway") ? "yes" : "no",
365
+ pluginConfigured: inspectGatewayPlugin(parsed),
228
366
  error: null
229
367
  };
230
368
  } catch (error) {
@@ -237,7 +375,7 @@ async function inspectOpencodeConfig(path) {
237
375
 
238
376
  // src/cli/init.ts
239
377
  import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
240
- import { dirname as dirname2, join as join3 } from "node:path";
378
+ import { dirname as dirname2, join as join4 } from "node:path";
241
379
 
242
380
  // src/cli/templates.ts
243
381
  function buildGatewayConfigTemplate(stateDbPath) {
@@ -286,13 +424,14 @@ function escapeTomlString(value) {
286
424
  // src/cli/init.ts
287
425
  async function runInit(options, env) {
288
426
  const configDir = resolveCliConfigDir(options, env);
289
- const opencodeConfigPath = join3(configDir, OPENCODE_CONFIG_FILE);
290
- const gatewayConfigPath = join3(configDir, GATEWAY_CONFIG_FILE);
427
+ const gatewayConfigPath = join4(configDir, GATEWAY_CONFIG_FILE);
291
428
  const workspaceDirPath = resolveGatewayWorkspacePath(gatewayConfigPath);
429
+ const opencodeConfig = await resolveOpencodeConfigFile(configDir);
430
+ const opencodeConfigPath = opencodeConfig.path;
292
431
  await mkdir(configDir, { recursive: true });
293
432
  await mkdir(workspaceDirPath, { recursive: true });
294
433
  let opencodeStatus = "already present";
295
- if (!await pathExists(opencodeConfigPath)) {
434
+ if (!opencodeConfig.exists) {
296
435
  await writeFile(opencodeConfigPath, stringifyOpencodeConfig(createDefaultOpencodeConfig(options.managed)));
297
436
  opencodeStatus = "created";
298
437
  } else {
@@ -1,5 +1,7 @@
1
1
  export declare const GATEWAY_CONFIG_FILE = "opencode-gateway.toml";
2
2
  export declare const OPENCODE_CONFIG_FILE = "opencode.json";
3
+ export declare const OPENCODE_CONFIG_FILE_JSONC = "opencode.jsonc";
4
+ export declare const OPENCODE_CONFIG_FILE_CANDIDATES: readonly ["opencode.jsonc", "opencode.json"];
3
5
  export declare const GATEWAY_WORKSPACE_DIR = "opencode-gateway-workspace";
4
6
  type EnvSource = Record<string, string | undefined>;
5
7
  export declare function resolveGatewayConfigPath(env: EnvSource): string;
@@ -2,6 +2,8 @@ import { homedir } from "node:os";
2
2
  import { dirname, join, resolve } from "node:path";
3
3
  export const GATEWAY_CONFIG_FILE = "opencode-gateway.toml";
4
4
  export const OPENCODE_CONFIG_FILE = "opencode.json";
5
+ export const OPENCODE_CONFIG_FILE_JSONC = "opencode.jsonc";
6
+ export const OPENCODE_CONFIG_FILE_CANDIDATES = [OPENCODE_CONFIG_FILE_JSONC, OPENCODE_CONFIG_FILE];
5
7
  export const GATEWAY_WORKSPACE_DIR = "opencode-gateway-workspace";
6
8
  export function resolveGatewayConfigPath(env) {
7
9
  const explicit = env.OPENCODE_GATEWAY_CONFIG;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gateway",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Gateway plugin for OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",