opencode-morphllm 0.0.6 → 0.0.8

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.
@@ -3,80 +3,78 @@ import { join } from 'node:path';
3
3
  import { getOpenCodeConfigDir } from './opencode-config-dir';
4
4
  const MORPH_PLUGIN_NAME = 'morph';
5
5
  export function getMorphPluginConfigPath() {
6
- const configDir = getOpenCodeConfigDir({ binary: 'opencode' });
7
- return join(configDir, `${MORPH_PLUGIN_NAME}.json`);
6
+ const configDir = getOpenCodeConfigDir({ binary: 'opencode' });
7
+ return join(configDir, `${MORPH_PLUGIN_NAME}.json`);
8
8
  }
9
9
  function parseJsonc(content) {
10
- try {
11
- // A simple JSONC parser that removes comments
12
- const cleanedContent = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
13
- return JSON.parse(cleanedContent);
14
- } catch {
15
- return null;
16
- }
10
+ try {
11
+ // A simple JSONC parser that removes comments
12
+ const cleanedContent = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
13
+ return JSON.parse(cleanedContent);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
17
18
  }
18
19
  export function loadMorphPluginConfig() {
19
- const jsoncPath = getMorphPluginConfigPath().replace('.json', '.jsonc');
20
- const jsonPath = getMorphPluginConfigPath();
21
- if (existsSync(jsoncPath)) {
22
- try {
23
- const content = readFileSync(jsoncPath, 'utf-8');
24
- return parseJsonc(content);
25
- } catch {
26
- return null;
20
+ const jsoncPath = getMorphPluginConfigPath().replace('.json', '.jsonc');
21
+ const jsonPath = getMorphPluginConfigPath();
22
+ if (existsSync(jsoncPath)) {
23
+ try {
24
+ const content = readFileSync(jsoncPath, 'utf-8');
25
+ return parseJsonc(content);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
27
30
  }
28
- }
29
- if (existsSync(jsonPath)) {
30
- try {
31
- const content = readFileSync(jsonPath, 'utf-8');
32
- return JSON.parse(content);
33
- } catch {
34
- return null;
31
+ if (existsSync(jsonPath)) {
32
+ try {
33
+ const content = readFileSync(jsonPath, 'utf-8');
34
+ return JSON.parse(content);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
35
39
  }
36
- }
37
- return null;
40
+ return null;
38
41
  }
39
- export function loadMorphPluginConfigWithProjectOverride(
40
- projectDir = process.cwd()
41
- ) {
42
- const userConfig = loadMorphPluginConfig() ?? {};
43
- const projectBasePath = join(projectDir, '.opencode', MORPH_PLUGIN_NAME);
44
- const projectJsoncPath = `${projectBasePath}.jsonc`;
45
- const projectJsonPath = `${projectBasePath}.json`;
46
- let projectConfig = {};
47
- if (existsSync(projectJsoncPath)) {
48
- try {
49
- const content = readFileSync(projectJsoncPath, 'utf-8');
50
- projectConfig = parseJsonc(content) ?? {};
51
- } catch {
52
- // Ignore parse errors
42
+ export function loadMorphPluginConfigWithProjectOverride(projectDir = process.cwd()) {
43
+ const userConfig = loadMorphPluginConfig() ?? {};
44
+ const projectBasePath = join(projectDir, '.opencode', MORPH_PLUGIN_NAME);
45
+ const projectJsoncPath = `${projectBasePath}.jsonc`;
46
+ const projectJsonPath = `${projectBasePath}.json`;
47
+ let projectConfig = {};
48
+ if (existsSync(projectJsoncPath)) {
49
+ try {
50
+ const content = readFileSync(projectJsoncPath, 'utf-8');
51
+ projectConfig = parseJsonc(content) ?? {};
52
+ }
53
+ catch {
54
+ // Ignore parse errors
55
+ }
53
56
  }
54
- } else if (existsSync(projectJsonPath)) {
55
- try {
56
- const content = readFileSync(projectJsonPath, 'utf-8');
57
- projectConfig = JSON.parse(content);
58
- } catch {
59
- // Ignore parse errors
57
+ else if (existsSync(projectJsonPath)) {
58
+ try {
59
+ const content = readFileSync(projectJsonPath, 'utf-8');
60
+ projectConfig = JSON.parse(content);
61
+ }
62
+ catch {
63
+ // Ignore parse errors
64
+ }
60
65
  }
61
- }
62
- return { ...userConfig, ...projectConfig };
66
+ return { ...userConfig, ...projectConfig };
63
67
  }
64
68
  const config = loadMorphPluginConfigWithProjectOverride();
65
69
  const routerConfigs = config.MORPH_ROUTER_CONFIGS || {};
66
70
  export const API_KEY = config.MORPH_API_KEY || '';
67
- export const MORPH_MODEL_EASY =
68
- routerConfigs.MORPH_MODEL_EASY || config.MORPH_MODEL_EASY || '';
69
- export const MORPH_MODEL_MEDIUM =
70
- routerConfigs.MORPH_MODEL_MEDIUM || config.MORPH_MODEL_MEDIUM || '';
71
- export const MORPH_MODEL_HARD =
72
- routerConfigs.MORPH_MODEL_HARD || config.MORPH_MODEL_HARD || '';
73
- export const MORPH_MODEL_DEFAULT =
74
- routerConfigs.MORPH_MODEL_DEFAULT ||
75
- config.MORPH_MODEL_DEFAULT ||
76
- MORPH_MODEL_MEDIUM;
77
- export const MORPH_ROUTER_ENABLED =
78
- routerConfigs.MORPH_ROUTER_ENABLED ?? config.MORPH_ROUTER_ENABLED ?? true;
79
- export const MORPH_ROUTER_ONLY_FIRST_MESSAGE =
80
- routerConfigs.MORPH_ROUTER_ONLY_FIRST_MESSAGE ??
81
- config.MORPH_ROUTER_ONLY_FIRST_MESSAGE ??
82
- false;
71
+ export const MORPH_MODEL_EASY = routerConfigs.MORPH_MODEL_EASY || config.MORPH_MODEL_EASY || '';
72
+ export const MORPH_MODEL_MEDIUM = routerConfigs.MORPH_MODEL_MEDIUM || config.MORPH_MODEL_MEDIUM || '';
73
+ export const MORPH_MODEL_HARD = routerConfigs.MORPH_MODEL_HARD || config.MORPH_MODEL_HARD || '';
74
+ export const MORPH_MODEL_DEFAULT = routerConfigs.MORPH_MODEL_DEFAULT ||
75
+ config.MORPH_MODEL_DEFAULT ||
76
+ MORPH_MODEL_MEDIUM;
77
+ export const MORPH_ROUTER_ENABLED = routerConfigs.MORPH_ROUTER_ENABLED ?? config.MORPH_ROUTER_ENABLED ?? true;
78
+ export const MORPH_ROUTER_PROMPT_CACHING_AWARE = routerConfigs.MORPH_ROUTER_PROMPT_CACHING_AWARE ??
79
+ config.MORPH_ROUTER_PROMPT_CACHING_AWARE ??
80
+ false;
@@ -1,204 +1,201 @@
1
- import {
2
- describe,
3
- it,
4
- expect,
5
- beforeEach,
6
- vi,
7
- beforeAll,
8
- afterAll,
9
- } from 'bun:test';
10
- import { existsSync, writeFileSync, rmSync, mkdirSync } from 'node:fs';
1
+ import { describe, it, expect, beforeEach, beforeAll, afterAll, } from 'bun:test';
2
+ import { existsSync, writeFileSync, rmSync, mkdirSync, } from 'node:fs';
11
3
  import { join } from 'node:path';
12
4
  import { tmpdir } from 'node:os';
13
- // Mock the opencode-config-dir module before importing config
14
- const mockConfigDir = join(tmpdir(), 'mock-opencode-config-test');
5
+ import { env } from 'node:process';
6
+ // Use a real temporary directory for testing - no mocking needed
7
+ const mockConfigDir = join(tmpdir(), 'mock-morph-config-test');
15
8
  const mockMorphJson = join(mockConfigDir, 'morph.json');
16
9
  const mockMorphJsonc = join(mockConfigDir, 'morph.jsonc');
17
- vi.mock('./opencode-config-dir', () => ({
18
- getOpenCodeConfigDir: vi.fn(() => mockConfigDir),
19
- }));
20
- import {
21
- getMorphPluginConfigPath,
22
- loadMorphPluginConfig,
23
- loadMorphPluginConfigWithProjectOverride,
24
- } from './config';
10
+ // Save original environment
11
+ let originalEnv;
12
+ import { getMorphPluginConfigPath, loadMorphPluginConfig, loadMorphPluginConfigWithProjectOverride, } from './config';
25
13
  describe('config.ts', () => {
26
- beforeAll(() => {
27
- // Create mock config directory
28
- if (!existsSync(mockConfigDir)) {
29
- mkdirSync(mockConfigDir, { recursive: true });
30
- }
31
- });
32
- afterAll(() => {
33
- // Cleanup
34
- try {
35
- if (existsSync(mockMorphJson)) rmSync(mockMorphJson);
36
- if (existsSync(mockMorphJsonc)) rmSync(mockMorphJsonc);
37
- if (existsSync(mockConfigDir)) rmSync(mockConfigDir, { recursive: true });
38
- } catch {
39
- // Ignore cleanup errors
40
- }
41
- });
42
- beforeEach(() => {
43
- // Clean up any existing config files before each test
44
- try {
45
- if (existsSync(mockMorphJson)) rmSync(mockMorphJson);
46
- if (existsSync(mockMorphJsonc)) rmSync(mockMorphJsonc);
47
- } catch {
48
- // Ignore cleanup errors
49
- }
50
- });
51
- describe('getMorphPluginConfigPath', () => {
52
- it('should return path to morph.json in config directory', () => {
53
- const path = getMorphPluginConfigPath();
54
- expect(path).toContain('morph.json');
55
- expect(path).toContain(mockConfigDir);
14
+ beforeAll(() => {
15
+ // Save original environment and set custom config dir
16
+ originalEnv = {
17
+ OPENCODE_CONFIG_DIR: env.OPENCODE_CONFIG_DIR,
18
+ };
19
+ env.OPENCODE_CONFIG_DIR = mockConfigDir;
20
+ // Create mock config directory
21
+ if (!existsSync(mockConfigDir)) {
22
+ mkdirSync(mockConfigDir, { recursive: true });
23
+ }
24
+ });
25
+ afterAll(() => {
26
+ // Restore original environment
27
+ if (originalEnv.OPENCODE_CONFIG_DIR !== undefined) {
28
+ env.OPENCODE_CONFIG_DIR = originalEnv.OPENCODE_CONFIG_DIR;
29
+ }
30
+ else {
31
+ delete env.OPENCODE_CONFIG_DIR;
32
+ }
33
+ // Cleanup
34
+ try {
35
+ if (existsSync(mockMorphJson))
36
+ rmSync(mockMorphJson);
37
+ if (existsSync(mockMorphJsonc))
38
+ rmSync(mockMorphJsonc);
39
+ if (existsSync(mockConfigDir))
40
+ rmSync(mockConfigDir, { recursive: true });
41
+ }
42
+ catch {
43
+ // Ignore cleanup errors
44
+ }
45
+ });
46
+ beforeEach(() => {
47
+ // Clean up any existing config files before each test
48
+ try {
49
+ if (existsSync(mockMorphJson))
50
+ rmSync(mockMorphJson);
51
+ if (existsSync(mockMorphJsonc))
52
+ rmSync(mockMorphJsonc);
53
+ }
54
+ catch {
55
+ // Ignore cleanup errors
56
+ }
56
57
  });
57
- });
58
- describe('loadMorphPluginConfig', () => {
59
- it('should return null when no config files exist', () => {
60
- const result = loadMorphPluginConfig();
61
- expect(result).toBeNull();
58
+ describe('getMorphPluginConfigPath', () => {
59
+ it('should return path to morph.json in config directory', () => {
60
+ const path = getMorphPluginConfigPath();
61
+ expect(path).toContain('morph.json');
62
+ expect(path).toContain(mockConfigDir);
63
+ });
62
64
  });
63
- it('should load config from .jsonc file', () => {
64
- const content = `{
65
+ describe('loadMorphPluginConfig', () => {
66
+ it('should return null when no config files exist', () => {
67
+ const result = loadMorphPluginConfig();
68
+ expect(result).toBeNull();
69
+ });
70
+ it('should load config from .jsonc file', () => {
71
+ const content = `{
65
72
  "MORPH_API_KEY": "test-key-123",
66
73
  "MORPH_ROUTER_ENABLED": false
67
74
  }`;
68
- writeFileSync(mockMorphJsonc, content);
69
- const result = loadMorphPluginConfig();
70
- expect(result).not.toBeNull();
71
- expect(result?.MORPH_API_KEY).toBe('test-key-123');
72
- expect(result?.MORPH_ROUTER_ENABLED).toBe(false);
73
- });
74
- it('should load config from .json file', () => {
75
- const content = JSON.stringify({
76
- MORPH_API_KEY: 'json-key-456',
77
- MORPH_MODEL_EASY: 'provider/model-easy',
78
- });
79
- writeFileSync(mockMorphJson, content);
80
- const result = loadMorphPluginConfig();
81
- expect(result).not.toBeNull();
82
- expect(result?.MORPH_API_KEY).toBe('json-key-456');
83
- expect(result?.MORPH_MODEL_EASY).toBe('provider/model-easy');
84
- });
85
- it('should prefer .jsonc over .json when both exist', () => {
86
- const jsoncContent = '{"MORPH_API_KEY": "from-jsonc"}';
87
- const jsonContent = '{"MORPH_API_KEY": "from-json"}';
88
- writeFileSync(mockMorphJsonc, jsoncContent);
89
- writeFileSync(mockMorphJson, jsonContent);
90
- const result = loadMorphPluginConfig();
91
- expect(result?.MORPH_API_KEY).toBe('from-jsonc');
92
- });
93
- it('should return null for invalid .jsonc content', () => {
94
- writeFileSync(mockMorphJsonc, 'invalid json content');
95
- const result = loadMorphPluginConfig();
96
- expect(result).toBeNull();
97
- });
98
- it('should return null for invalid .json content', () => {
99
- writeFileSync(mockMorphJson, 'invalid json content');
100
- const result = loadMorphPluginConfig();
101
- expect(result).toBeNull();
102
- });
103
- it('should handle all MORPH config fields', () => {
104
- const content = JSON.stringify({
105
- MORPH_API_KEY: 'api-key',
106
- MORPH_ROUTER_ENABLED: true,
107
- MORPH_MODEL_EASY: 'easy/provider/model',
108
- MORPH_MODEL_MEDIUM: 'medium/provider/model',
109
- MORPH_MODEL_HARD: 'hard/provider/model',
110
- MORPH_MODEL_DEFAULT: 'default/provider/model',
111
- });
112
- writeFileSync(mockMorphJson, content);
113
- const result = loadMorphPluginConfig();
114
- expect(result).toEqual({
115
- MORPH_API_KEY: 'api-key',
116
- MORPH_ROUTER_ENABLED: true,
117
- MORPH_MODEL_EASY: 'easy/provider/model',
118
- MORPH_MODEL_MEDIUM: 'medium/provider/model',
119
- MORPH_MODEL_HARD: 'hard/provider/model',
120
- MORPH_MODEL_DEFAULT: 'default/provider/model',
121
- });
122
- });
123
- it('should handle nested MORPH_ROUTER_CONFIGS', () => {
124
- const content = JSON.stringify({
125
- MORPH_API_KEY: 'api-key',
126
- MORPH_ROUTER_CONFIGS: {
127
- MORPH_ROUTER_ENABLED: true,
128
- MORPH_MODEL_EASY: 'easy/provider/model',
129
- MORPH_MODEL_MEDIUM: 'medium/provider/model',
130
- MORPH_MODEL_HARD: 'hard/provider/model',
131
- MORPH_MODEL_DEFAULT: 'default/provider/model',
132
- },
133
- });
134
- writeFileSync(mockMorphJson, content);
135
- const result = loadMorphPluginConfig();
136
- expect(result?.MORPH_API_KEY).toBe('api-key');
137
- expect(result?.MORPH_ROUTER_CONFIGS).toEqual({
138
- MORPH_ROUTER_ENABLED: true,
139
- MORPH_MODEL_EASY: 'easy/provider/model',
140
- MORPH_MODEL_MEDIUM: 'medium/provider/model',
141
- MORPH_MODEL_HARD: 'hard/provider/model',
142
- MORPH_MODEL_DEFAULT: 'default/provider/model',
143
- });
144
- });
145
- });
146
- describe('loadMorphPluginConfigWithProjectOverride', () => {
147
- it('should return empty object when no config exists', () => {
148
- const result =
149
- loadMorphPluginConfigWithProjectOverride('/non-existent-path');
150
- expect(result).toEqual({});
151
- });
152
- it('should merge user config with project config', () => {
153
- // Create user config
154
- writeFileSync(
155
- mockMorphJson,
156
- JSON.stringify({
157
- MORPH_API_KEY: 'user-api-key',
158
- MORPH_MODEL_EASY: 'user-easy-model',
159
- })
160
- );
161
- // Create project config directory
162
- const projectDir = join(tmpdir(), 'test-project');
163
- const projectConfigDir = join(projectDir, '.opencode');
164
- const projectConfigPath = join(projectConfigDir, 'morph.json');
165
- mkdirSync(projectConfigDir, { recursive: true });
166
- writeFileSync(
167
- projectConfigPath,
168
- JSON.stringify({
169
- MORPH_API_KEY: 'project-api-key',
170
- MORPH_MODEL_HARD: 'project-hard-model',
171
- })
172
- );
173
- const result = loadMorphPluginConfigWithProjectOverride(projectDir);
174
- // Project config should override user config
175
- expect(result.MORPH_API_KEY).toBe('project-api-key');
176
- expect(result.MORPH_MODEL_EASY).toBe('user-easy-model');
177
- expect(result.MORPH_MODEL_HARD).toBe('project-hard-model');
75
+ writeFileSync(mockMorphJsonc, content);
76
+ const result = loadMorphPluginConfig();
77
+ expect(result).not.toBeNull();
78
+ expect(result?.MORPH_API_KEY).toBe('test-key-123');
79
+ expect(result?.MORPH_ROUTER_ENABLED).toBe(false);
80
+ });
81
+ it('should load config from .json file', () => {
82
+ const content = JSON.stringify({
83
+ MORPH_API_KEY: 'json-key-456',
84
+ MORPH_MODEL_EASY: 'provider/model-easy',
85
+ });
86
+ writeFileSync(mockMorphJson, content);
87
+ const result = loadMorphPluginConfig();
88
+ expect(result).not.toBeNull();
89
+ expect(result?.MORPH_API_KEY).toBe('json-key-456');
90
+ expect(result?.MORPH_MODEL_EASY).toBe('provider/model-easy');
91
+ });
92
+ it('should prefer .jsonc over .json when both exist', () => {
93
+ const jsoncContent = '{"MORPH_API_KEY": "from-jsonc"}';
94
+ const jsonContent = '{"MORPH_API_KEY": "from-json"}';
95
+ writeFileSync(mockMorphJsonc, jsoncContent);
96
+ writeFileSync(mockMorphJson, jsonContent);
97
+ const result = loadMorphPluginConfig();
98
+ expect(result?.MORPH_API_KEY).toBe('from-jsonc');
99
+ });
100
+ it('should return null for invalid .jsonc content', () => {
101
+ writeFileSync(mockMorphJsonc, 'invalid json content');
102
+ const result = loadMorphPluginConfig();
103
+ expect(result).toBeNull();
104
+ });
105
+ it('should return null for invalid .json content', () => {
106
+ writeFileSync(mockMorphJson, 'invalid json content');
107
+ const result = loadMorphPluginConfig();
108
+ expect(result).toBeNull();
109
+ });
110
+ it('should handle all MORPH config fields', () => {
111
+ const content = JSON.stringify({
112
+ MORPH_API_KEY: 'api-key',
113
+ MORPH_ROUTER_ENABLED: true,
114
+ MORPH_MODEL_EASY: 'easy/provider/model',
115
+ MORPH_MODEL_MEDIUM: 'medium/provider/model',
116
+ MORPH_MODEL_HARD: 'hard/provider/model',
117
+ MORPH_MODEL_DEFAULT: 'default/provider/model',
118
+ });
119
+ writeFileSync(mockMorphJson, content);
120
+ const result = loadMorphPluginConfig();
121
+ expect(result).toEqual({
122
+ MORPH_API_KEY: 'api-key',
123
+ MORPH_ROUTER_ENABLED: true,
124
+ MORPH_MODEL_EASY: 'easy/provider/model',
125
+ MORPH_MODEL_MEDIUM: 'medium/provider/model',
126
+ MORPH_MODEL_HARD: 'hard/provider/model',
127
+ MORPH_MODEL_DEFAULT: 'default/provider/model',
128
+ });
129
+ });
130
+ it('should handle nested MORPH_ROUTER_CONFIGS', () => {
131
+ const content = JSON.stringify({
132
+ MORPH_API_KEY: 'api-key',
133
+ MORPH_ROUTER_CONFIGS: {
134
+ MORPH_ROUTER_ENABLED: true,
135
+ MORPH_MODEL_EASY: 'easy/provider/model',
136
+ MORPH_MODEL_MEDIUM: 'medium/provider/model',
137
+ MORPH_MODEL_HARD: 'hard/provider/model',
138
+ MORPH_MODEL_DEFAULT: 'default/provider/model',
139
+ },
140
+ });
141
+ writeFileSync(mockMorphJson, content);
142
+ const result = loadMorphPluginConfig();
143
+ expect(result?.MORPH_API_KEY).toBe('api-key');
144
+ expect(result?.MORPH_ROUTER_CONFIGS).toEqual({
145
+ MORPH_ROUTER_ENABLED: true,
146
+ MORPH_MODEL_EASY: 'easy/provider/model',
147
+ MORPH_MODEL_MEDIUM: 'medium/provider/model',
148
+ MORPH_MODEL_HARD: 'hard/provider/model',
149
+ MORPH_MODEL_DEFAULT: 'default/provider/model',
150
+ });
151
+ });
178
152
  });
179
- it('should handle project .jsonc config', () => {
180
- // Create user config
181
- writeFileSync(
182
- mockMorphJson,
183
- JSON.stringify({
184
- MORPH_API_KEY: 'user-key',
185
- })
186
- );
187
- // Create project config with .jsonc
188
- const projectDir = join(tmpdir(), 'test-project2');
189
- const projectConfigDir = join(projectDir, '.opencode');
190
- const projectConfigPath = join(projectConfigDir, 'morph.jsonc');
191
- mkdirSync(projectConfigDir, { recursive: true });
192
- const jsoncContent = `
153
+ describe('loadMorphPluginConfigWithProjectOverride', () => {
154
+ it('should return empty object when no config exists', () => {
155
+ const result = loadMorphPluginConfigWithProjectOverride('/non-existent-path');
156
+ expect(result).toEqual({});
157
+ });
158
+ it('should merge user config with project config', () => {
159
+ // Create user config
160
+ writeFileSync(mockMorphJson, JSON.stringify({
161
+ MORPH_API_KEY: 'user-api-key',
162
+ MORPH_MODEL_EASY: 'user-easy-model',
163
+ }));
164
+ // Create project config directory
165
+ const projectDir = join(tmpdir(), 'test-project');
166
+ const projectConfigDir = join(projectDir, '.opencode');
167
+ const projectConfigPath = join(projectConfigDir, 'morph.json');
168
+ mkdirSync(projectConfigDir, { recursive: true });
169
+ writeFileSync(projectConfigPath, JSON.stringify({
170
+ MORPH_API_KEY: 'project-api-key',
171
+ MORPH_MODEL_HARD: 'project-hard-model',
172
+ }));
173
+ const result = loadMorphPluginConfigWithProjectOverride(projectDir);
174
+ // Project config should override user config
175
+ expect(result.MORPH_API_KEY).toBe('project-api-key');
176
+ expect(result.MORPH_MODEL_EASY).toBe('user-easy-model');
177
+ expect(result.MORPH_MODEL_HARD).toBe('project-hard-model');
178
+ });
179
+ it('should handle project .jsonc config', () => {
180
+ // Create user config
181
+ writeFileSync(mockMorphJson, JSON.stringify({
182
+ MORPH_API_KEY: 'user-key',
183
+ }));
184
+ // Create project config with .jsonc
185
+ const projectDir = join(tmpdir(), 'test-project2');
186
+ const projectConfigDir = join(projectDir, '.opencode');
187
+ const projectConfigPath = join(projectConfigDir, 'morph.jsonc');
188
+ mkdirSync(projectConfigDir, { recursive: true });
189
+ const jsoncContent = `
193
190
  // Project config with comments
194
191
  {
195
192
  "MORPH_MODEL_MEDIUM": "project-medium-model"
196
193
  }
197
194
  `;
198
- writeFileSync(projectConfigPath, jsoncContent);
199
- const result = loadMorphPluginConfigWithProjectOverride(projectDir);
200
- expect(result.MORPH_API_KEY).toBe('user-key');
201
- expect(result.MORPH_MODEL_MEDIUM).toBe('project-medium-model');
195
+ writeFileSync(projectConfigPath, jsoncContent);
196
+ const result = loadMorphPluginConfigWithProjectOverride(projectDir);
197
+ expect(result.MORPH_API_KEY).toBe('user-key');
198
+ expect(result.MORPH_MODEL_MEDIUM).toBe('project-medium-model');
199
+ });
202
200
  });
203
- });
204
201
  });
@@ -1,9 +1,19 @@
1
- interface OpenCodeConfigDirOptions {
2
- binary: 'opencode' | 'opencode-desktop';
3
- version?: string | null;
4
- checkExisting?: boolean;
1
+ export type OpenCodeBinaryType = 'opencode' | 'opencode-desktop';
2
+ export interface OpenCodeConfigDirOptions {
3
+ binary: OpenCodeBinaryType;
4
+ version?: string | null;
5
+ checkExisting?: boolean;
5
6
  }
6
- export declare function getOpenCodeConfigDir(
7
- options: OpenCodeConfigDirOptions
8
- ): string;
9
- export {};
7
+ export interface OpenCodeConfigPaths {
8
+ configDir: string;
9
+ configJson: string;
10
+ configJsonc: string;
11
+ packageJson: string;
12
+ omoConfig: string;
13
+ }
14
+ export declare const TAURI_APP_IDENTIFIER = "ai.opencode.desktop";
15
+ export declare const TAURI_APP_IDENTIFIER_DEV = "ai.opencode.desktop.dev";
16
+ export declare function isDevBuild(version: string | null | undefined): boolean;
17
+ export declare function getOpenCodeConfigDir(options: OpenCodeConfigDirOptions): string;
18
+ export declare function getOpenCodeConfigPaths(options: OpenCodeConfigDirOptions): OpenCodeConfigPaths;
19
+ export declare function detectExistingConfigDir(binary: OpenCodeBinaryType, version?: string | null): string | null;