synabun 2026.4.20-beta.6 → 2026.4.20-beta.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.
|
@@ -1080,6 +1080,7 @@ function buildSetupTab(setupStatus) {
|
|
|
1080
1080
|
const claudeConnected = setupStatus.claude?.connected || false;
|
|
1081
1081
|
const geminiConnected = setupStatus.gemini?.connected || false;
|
|
1082
1082
|
const codexConnected = setupStatus.codex?.connected || false;
|
|
1083
|
+
const opencodeConnected = setupStatus.opencode?.connected || false;
|
|
1083
1084
|
|
|
1084
1085
|
const statusBadge = (on) => `<span class="setup-status-badge ${on ? 'active' : 'inactive'}">${on ? 'Connected' : 'Off'}</span>`;
|
|
1085
1086
|
|
|
@@ -1184,6 +1185,40 @@ function buildSetupTab(setupStatus) {
|
|
|
1184
1185
|
</div>
|
|
1185
1186
|
</div>
|
|
1186
1187
|
|
|
1188
|
+
<!-- OPENCODE -->
|
|
1189
|
+
<div class="iface-section collapsed" data-collapsible id="setup-opencode">
|
|
1190
|
+
<div class="gfx-group-title">
|
|
1191
|
+
<span style="display:flex;align-items:center;gap:8px">
|
|
1192
|
+
${CHEVRON_ICON}
|
|
1193
|
+
${OPENCODE_ICON}
|
|
1194
|
+
<span>OpenCode</span>
|
|
1195
|
+
${statusBadge(opencodeConnected)}
|
|
1196
|
+
</span>
|
|
1197
|
+
</div>
|
|
1198
|
+
<div class="cc-section-body">
|
|
1199
|
+
<div class="cc-integration-item${opencodeConnected ? ' enabled' : ''}" id="setup-opencode-mcp-row">
|
|
1200
|
+
<div class="cc-integration-info">
|
|
1201
|
+
<div class="cc-integration-label">SynaBun MCP</div>
|
|
1202
|
+
<div class="cc-integration-path" id="setup-opencode-mcp-status">${opencodeConnected ? 'Registered in ~/.config/opencode/config.json' : 'Not connected'}</div>
|
|
1203
|
+
</div>
|
|
1204
|
+
<button class="cc-toggle${opencodeConnected ? ' on' : ''}" id="setup-opencode-mcp-toggle"></button>
|
|
1205
|
+
</div>
|
|
1206
|
+
|
|
1207
|
+
<div style="margin-top:12px">
|
|
1208
|
+
<div class="cc-greeting-label" style="margin-bottom:4px">Manual Config <span style="color:var(--t-faint)">(~/.config/opencode/config.json)</span></div>
|
|
1209
|
+
<div class="cc-ruleset-preview" id="setup-opencode-config-preview" style="max-height:120px">Loading...</div>
|
|
1210
|
+
<button class="cc-copy-btn" id="setup-opencode-config-copy" style="margin-top:4px">${COPY_ICON} Copy JSON Config</button>
|
|
1211
|
+
</div>
|
|
1212
|
+
|
|
1213
|
+
<div style="margin-top:12px">
|
|
1214
|
+
<div class="cc-greeting-label" style="margin-bottom:4px">AGENTS.md Ruleset</div>
|
|
1215
|
+
<div class="cc-ruleset-preview" id="setup-opencode-ruleset-preview">Loading...</div>
|
|
1216
|
+
<button class="cc-copy-btn" id="setup-opencode-ruleset-copy" style="margin-top:4px">${COPY_ICON} Copy Ruleset</button>
|
|
1217
|
+
<div class="setup-hint">Paste into your project's <code>AGENTS.md</code></div>
|
|
1218
|
+
</div>
|
|
1219
|
+
</div>
|
|
1220
|
+
</div>
|
|
1221
|
+
|
|
1187
1222
|
<!-- COEXISTENCE RULES -->
|
|
1188
1223
|
<div class="iface-section collapsed" data-collapsible id="setup-coexistence">
|
|
1189
1224
|
<div class="gfx-group-title">
|
|
@@ -2855,7 +2890,7 @@ export async function openSettingsModal() {
|
|
|
2855
2890
|
let greetingConfig = { defaults: {}, projects: {}, global: {} };
|
|
2856
2891
|
let codexGreetingConfig = { enabled: true, defaults: {}, projects: {}, global: {} };
|
|
2857
2892
|
let opencodeGreetingConfig = { enabled: false, defaults: {}, projects: {}, global: {} };
|
|
2858
|
-
let setupStatus = { claude: {}, gemini: {}, codex: {}, paths: {} };
|
|
2893
|
+
let setupStatus = { claude: {}, gemini: {}, codex: {}, opencode: {}, paths: {} };
|
|
2859
2894
|
let cliConfig = {};
|
|
2860
2895
|
let toolPermissions = {};
|
|
2861
2896
|
let toolCategories = [];
|
|
@@ -2944,7 +2979,7 @@ export async function openSettingsModal() {
|
|
|
2944
2979
|
<nav class="settings-nav">
|
|
2945
2980
|
${buildNavHTML(variantTabs, {
|
|
2946
2981
|
server: settings.storage === 'sqlite' ? 'connected' : 'disconnected',
|
|
2947
|
-
setup: (setupStatus.claude?.connected || setupStatus.gemini?.connected || setupStatus.codex?.connected) ? 'connected' : 'disconnected',
|
|
2982
|
+
setup: (setupStatus.claude?.connected || setupStatus.gemini?.connected || setupStatus.codex?.connected || setupStatus.opencode?.connected) ? 'connected' : 'disconnected',
|
|
2948
2983
|
discord: discordConfig.botToken ? 'connected' : 'disconnected',
|
|
2949
2984
|
})}
|
|
2950
2985
|
</nav>
|
|
@@ -4989,6 +5024,38 @@ export async function openSettingsModal() {
|
|
|
4989
5024
|
}
|
|
4990
5025
|
wireRulesetPreview('codex', 'codex');
|
|
4991
5026
|
|
|
5027
|
+
// ── OpenCode ──
|
|
5028
|
+
wireSetupMcpToggle('opencode', '/api/opencode/mcp', '~/.config/opencode/config.json');
|
|
5029
|
+
// Config preview — OpenCode stores MCP servers under the "mcp" key of config.json
|
|
5030
|
+
{
|
|
5031
|
+
const preview = overlay.querySelector('#setup-opencode-config-preview');
|
|
5032
|
+
let cachedConfig = '';
|
|
5033
|
+
if (preview) {
|
|
5034
|
+
const mp = setupStatus.paths?.mcpIndexPath || '<path-to>/mcp-server/run.mjs';
|
|
5035
|
+
const ep = setupStatus.paths?.envPath || '<path-to>/synabun/.env';
|
|
5036
|
+
fetch('/api/opencode/mcp').then(r => r.json()).then(data => {
|
|
5037
|
+
if (data.ok && data.data && Object.keys(data.data).length) {
|
|
5038
|
+
cachedConfig = JSON.stringify({ mcp: data.data }, null, 2);
|
|
5039
|
+
preview.textContent = cachedConfig;
|
|
5040
|
+
} else if (data.ok) {
|
|
5041
|
+
cachedConfig = JSON.stringify({
|
|
5042
|
+
mcp: {
|
|
5043
|
+
SynaBun: {
|
|
5044
|
+
type: 'stdio',
|
|
5045
|
+
command: 'node',
|
|
5046
|
+
args: [mp],
|
|
5047
|
+
env: { DOTENV_PATH: ep },
|
|
5048
|
+
},
|
|
5049
|
+
},
|
|
5050
|
+
}, null, 2);
|
|
5051
|
+
preview.textContent = cachedConfig;
|
|
5052
|
+
} else { preview.textContent = 'Could not load config.'; }
|
|
5053
|
+
}).catch(() => { preview.textContent = 'Failed to load.'; });
|
|
5054
|
+
}
|
|
5055
|
+
wireCopyBtn('setup-opencode-config-copy', () => cachedConfig, 'Copy JSON Config');
|
|
5056
|
+
}
|
|
5057
|
+
wireRulesetPreview('opencode', 'generic');
|
|
5058
|
+
|
|
4992
5059
|
// ── Coexistence rules ──
|
|
4993
5060
|
wireRulesetPreview('coexistence', 'coexistence');
|
|
4994
5061
|
}
|
|
@@ -9841,9 +9841,18 @@ function isInsideSynabun(binPath) {
|
|
|
9841
9841
|
}
|
|
9842
9842
|
}
|
|
9843
9843
|
|
|
9844
|
+
// Versions of @anthropic-ai/claude-code that SynaBun has historically shipped
|
|
9845
|
+
// as a bundled dependency. If a `claude --version` probe returns one of these,
|
|
9846
|
+
// it's almost certainly the stale bundle (a real user-global install would be
|
|
9847
|
+
// the current 2.1.116+). Reject hard regardless of where the binary path looks
|
|
9848
|
+
// to come from. Add new versions here if SynaBun ever ships claude-code again.
|
|
9849
|
+
const KNOWN_STALE_CLAUDE_VERSIONS = new Set(['2.1.71', '2.1.89', '2.1.91']);
|
|
9850
|
+
|
|
9844
9851
|
// Aggressively delete any stale bundled CLI tool packages left over in our
|
|
9845
9852
|
// own node_modules. Idempotent — runs on every startup. Belt-and-suspenders
|
|
9846
9853
|
// against `npm install` not auto-pruning packages removed from package.json.
|
|
9854
|
+
// Logs every found package (deleted or not) so Windows users can confirm the
|
|
9855
|
+
// prune is actually firing on their box.
|
|
9847
9856
|
function pruneBundledCliTools() {
|
|
9848
9857
|
const stalePkgs = ['@anthropic-ai/claude-code', '@openai/codex', 'opencode-ai', '@google/gemini-cli'];
|
|
9849
9858
|
const moduleRoots = [
|
|
@@ -9897,6 +9906,20 @@ async function checkToolVersions() {
|
|
|
9897
9906
|
} catch { /* not installed or errored */ }
|
|
9898
9907
|
}
|
|
9899
9908
|
|
|
9909
|
+
// Nuclear safety net for the recurring claude phantom-version bug:
|
|
9910
|
+
// if the probe returned a known historical bundled version of claude-code,
|
|
9911
|
+
// reject it even if the binary-path detection failed. A real user-global
|
|
9912
|
+
// install of claude is always current (2.1.116+), so seeing 2.1.89 etc.
|
|
9913
|
+
// means we're reading a stale bundle SynaBun shipped in a prior release.
|
|
9914
|
+
if (tool.key === 'claude-code' && installed && KNOWN_STALE_CLAUDE_VERSIONS.has(installed)) {
|
|
9915
|
+
console.warn(
|
|
9916
|
+
` [version-guard] Rejected claude v${installed} from ${probedBinPath || '(unknown path)'}` +
|
|
9917
|
+
` — matches known-stale SynaBun bundle. Treating as not installed. ` +
|
|
9918
|
+
`If you actually have this version installed, remove it from KNOWN_STALE_CLAUDE_VERSIONS.`
|
|
9919
|
+
);
|
|
9920
|
+
installed = null;
|
|
9921
|
+
}
|
|
9922
|
+
|
|
9900
9923
|
let latest = null;
|
|
9901
9924
|
try {
|
|
9902
9925
|
const controller = new AbortController();
|
|
@@ -13864,7 +13887,17 @@ app.get('/api/setup/status', (req, res) => {
|
|
|
13864
13887
|
} catch {}
|
|
13865
13888
|
codex.cliCommand = `codex --full-auto mcp add -- node "${mcpIndexPath}"`;
|
|
13866
13889
|
|
|
13867
|
-
|
|
13890
|
+
// OpenCode
|
|
13891
|
+
let opencode = { connected: false };
|
|
13892
|
+
try {
|
|
13893
|
+
const ocPath = getOpencodeConfigPath();
|
|
13894
|
+
if (existsSync(ocPath)) {
|
|
13895
|
+
const oc = JSON.parse(readFileSync(ocPath, 'utf-8'));
|
|
13896
|
+
opencode.connected = !!(oc?.mcp && oc.mcp.SynaBun);
|
|
13897
|
+
}
|
|
13898
|
+
} catch {}
|
|
13899
|
+
|
|
13900
|
+
res.json({ ok: true, claude, gemini, codex, opencode, paths: { mcpIndexPath, envPath } });
|
|
13868
13901
|
} catch (err) {
|
|
13869
13902
|
res.status(500).json({ error: err.message });
|
|
13870
13903
|
}
|