Skip to content

Commit abd6fba

Browse files
spoons-and-mirrorsopencode
andcommitted
Fix CLI HTTPS support and improve data persistence
- Fix CLI to use https module instead of http for HTTPS URLs - Move vote/download storage to /tmp with env variable fallback - Add logging to track data persistence across deployments 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode <[email protected]>
1 parent 4e13a94 commit abd6fba

File tree

2 files changed

+181
-155
lines changed

2 files changed

+181
-155
lines changed

packages/openmodes/api/index.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,30 +116,41 @@ async function loadModeFromJSON(config, modeDir, dirName, dirFiles) {
116116
return mode;
117117
}
118118

119-
// File-based persistent storage (like Bun server)
119+
// File-based persistent storage with fallback to memory
120120
class DataManager {
121121
static data = { votes: {}, downloads: {} };
122122
static filePaths = {
123-
votes: path.join(__dirname, '..', 'dist', 'votes.json'),
124-
downloads: path.join(__dirname, '..', 'dist', 'downloads.json')
123+
votes: path.join('/tmp', 'openmodes-votes.json'),
124+
downloads: path.join('/tmp', 'openmodes-downloads.json')
125125
};
126126

127127
static async load(type) {
128128
try {
129129
const content = await fs.readFile(DataManager.filePaths[type], 'utf-8');
130130
DataManager.data[type] = JSON.parse(content);
131+
console.log(`Loaded ${type} data from file`);
131132
} catch (error) {
132-
// File doesn't exist, use empty object
133-
DataManager.data[type] = {};
133+
// File doesn't exist, try to load from environment variable as backup
134+
const envVar = `OPENMODES_${type.toUpperCase()}`;
135+
if (process.env[envVar]) {
136+
try {
137+
DataManager.data[type] = JSON.parse(process.env[envVar]);
138+
console.log(`Loaded ${type} data from environment`);
139+
} catch (e) {
140+
console.warn(`Failed to parse ${envVar} environment variable`);
141+
DataManager.data[type] = {};
142+
}
143+
} else {
144+
DataManager.data[type] = {};
145+
}
134146
}
135147
}
136148

137149
static async save(type) {
138150
try {
139-
await fs.writeFile(
140-
DataManager.filePaths[type],
141-
JSON.stringify(DataManager.data[type], null, 2)
142-
);
151+
const jsonData = JSON.stringify(DataManager.data[type], null, 2);
152+
await fs.writeFile(DataManager.filePaths[type], jsonData);
153+
console.log(`Saved ${type} data to file and memory`);
143154
} catch (error) {
144155
console.error(`Failed to save ${type}:`, error);
145156
}

packages/openmodes/cli/install.js

Lines changed: 161 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -2,176 +2,191 @@
22

33
import fs from 'fs';
44
import path from 'path';
5-
import http from 'http';
5+
import https from 'https';
66
import { fileURLToPath } from 'url';
77

88
const __dirname = path.dirname(fileURLToPath(import.meta.url));
99

1010
async function fetchJson(url) {
11-
return new Promise((resolve, reject) => {
12-
http.get(url, (res) => {
13-
let data = '';
14-
res.on('data', (chunk) => {
15-
data += chunk;
16-
});
17-
res.on('end', () => {
18-
try {
19-
resolve(JSON.parse(data));
20-
} catch (e) {
21-
reject(e);
22-
}
23-
});
24-
}).on('error', reject);
25-
});
11+
return new Promise((resolve, reject) => {
12+
https
13+
.get(url, (res) => {
14+
let data = '';
15+
res.on('data', (chunk) => {
16+
data += chunk;
17+
});
18+
res.on('end', () => {
19+
try {
20+
resolve(JSON.parse(data));
21+
} catch (e) {
22+
reject(e);
23+
}
24+
});
25+
})
26+
.on('error', reject);
27+
});
2628
}
2729

2830
function ensureDirectoryExists(dir) {
29-
if (!fs.existsSync(dir)) {
30-
fs.mkdirSync(dir, { recursive: true });
31-
}
31+
if (!fs.existsSync(dir)) {
32+
fs.mkdirSync(dir, { recursive: true });
33+
}
3234
}
3335

3436
function updateOrCreateOpenCodeJson(modeData, modeId) {
35-
const currentDir = process.cwd();
36-
const openCodePath = path.join(currentDir, 'opencode.json');
37-
38-
let openCodeConfig = {
39-
"$schema": "https://opencode.ai/config.json",
40-
"instructions": [],
41-
"mcp": {},
42-
"provider": {},
43-
"mode": {}
44-
};
45-
46-
if (fs.existsSync(openCodePath)) {
47-
try {
48-
openCodeConfig = JSON.parse(fs.readFileSync(openCodePath, 'utf8'));
49-
} catch (e) {
50-
console.warn('⚠️ Could not parse existing opencode.json, creating new one');
51-
}
52-
}
53-
54-
if (!openCodeConfig.mcp) openCodeConfig.mcp = {};
55-
if (!openCodeConfig.mode) openCodeConfig.mode = {};
56-
57-
if (modeData.opencode_config && modeData.opencode_config.mode) {
58-
Object.entries(modeData.opencode_config.mode).forEach(([key, config]) => {
59-
const updatedConfig = { ...config };
60-
61-
if (updatedConfig.prompt) {
62-
updatedConfig.prompt = `{file:./.opencode/mode/${key}/${key}.mode.md}`;
63-
}
64-
65-
if (updatedConfig.instructions && Array.isArray(updatedConfig.instructions)) {
66-
updatedConfig.instructions = updatedConfig.instructions.map(instruction => {
67-
const filename = instruction.replace('./', '');
68-
return `./.opencode/mode/${key}/${filename}`;
69-
});
70-
}
71-
72-
openCodeConfig.mode[key] = updatedConfig;
73-
});
74-
}
75-
76-
fs.writeFileSync(openCodePath, JSON.stringify(openCodeConfig, null, '\t'));
37+
const currentDir = process.cwd();
38+
const openCodePath = path.join(currentDir, 'opencode.json');
39+
40+
let openCodeConfig = {
41+
$schema: 'https://opencode.ai/config.json',
42+
instructions: [],
43+
mcp: {},
44+
provider: {},
45+
mode: {}
46+
};
47+
48+
if (fs.existsSync(openCodePath)) {
49+
try {
50+
openCodeConfig = JSON.parse(fs.readFileSync(openCodePath, 'utf8'));
51+
} catch (e) {
52+
console.warn(
53+
'⚠️ Could not parse existing opencode.json, creating new one'
54+
);
55+
}
56+
}
57+
58+
if (!openCodeConfig.mcp) openCodeConfig.mcp = {};
59+
if (!openCodeConfig.mode) openCodeConfig.mode = {};
60+
61+
if (modeData.opencode_config && modeData.opencode_config.mode) {
62+
Object.entries(modeData.opencode_config.mode).forEach(([key, config]) => {
63+
const updatedConfig = { ...config };
64+
65+
if (updatedConfig.prompt) {
66+
updatedConfig.prompt = `{file:./.opencode/mode/${key}/${key}.mode.md}`;
67+
}
68+
69+
if (
70+
updatedConfig.instructions &&
71+
Array.isArray(updatedConfig.instructions)
72+
) {
73+
updatedConfig.instructions = updatedConfig.instructions.map(
74+
(instruction) => {
75+
const filename = instruction.replace('./', '');
76+
return `./.opencode/mode/${key}/${filename}`;
77+
}
78+
);
79+
}
80+
81+
openCodeConfig.mode[key] = updatedConfig;
82+
});
83+
}
84+
85+
fs.writeFileSync(openCodePath, JSON.stringify(openCodeConfig, null, '\t'));
7786
}
7887

7988
async function installMode(modeId) {
80-
try {
81-
console.log(`📦 Installing mode: ${modeId}`);
82-
83-
const url = `https://openmodes.dev/mode/${modeId}`;
84-
const modeData = await fetchJson(url);
85-
86-
// Remove URL keys from MCP configurations
87-
if (modeData.opencode_config && modeData.opencode_config.mode) {
88-
Object.values(modeData.opencode_config.mode).forEach(modeConfig => {
89-
if (modeConfig.mcp) {
90-
Object.values(modeConfig.mcp).forEach(mcpConfig => {
91-
delete mcpConfig.url;
92-
});
93-
}
94-
});
95-
}
96-
97-
const currentDir = process.cwd();
98-
const modeDir = path.join(currentDir, '.opencode', 'mode', modeId);
99-
ensureDirectoryExists(modeDir);
100-
101-
// Write mode files
102-
if (modeData.mode_prompt) {
103-
const promptPath = path.join(modeDir, `${modeId}.mode.md`);
104-
fs.writeFileSync(promptPath, modeData.mode_prompt);
105-
}
106-
107-
if (modeData.context_instructions && Array.isArray(modeData.context_instructions)) {
108-
modeData.context_instructions.forEach((instruction) => {
109-
const filename = `${instruction.title.toLowerCase()}.instructions.md`;
110-
const instructionPath = path.join(modeDir, filename);
111-
fs.writeFileSync(instructionPath, instruction.content);
112-
});
113-
}
114-
115-
updateOrCreateOpenCodeJson(modeData, modeId);
116-
117-
console.log(`✅ Successfully installed mode "${modeId}"`);
118-
119-
} catch (error) {
120-
console.error(`❌ Error installing mode "${modeId}":`, error.message);
121-
process.exit(1);
122-
}
89+
try {
90+
console.log(`📦 Installing mode: ${modeId}`);
91+
92+
const url = `https://openmodes.vercel.app/mode/${modeId}`;
93+
const modeData = await fetchJson(url);
94+
95+
// Remove URL keys from MCP configurations
96+
if (modeData.opencode_config && modeData.opencode_config.mode) {
97+
Object.values(modeData.opencode_config.mode).forEach((modeConfig) => {
98+
if (modeConfig.mcp) {
99+
Object.values(modeConfig.mcp).forEach((mcpConfig) => {
100+
delete mcpConfig.url;
101+
});
102+
}
103+
});
104+
}
105+
106+
const currentDir = process.cwd();
107+
const modeDir = path.join(currentDir, '.opencode', 'mode', modeId);
108+
ensureDirectoryExists(modeDir);
109+
110+
// Write mode files
111+
if (modeData.mode_prompt) {
112+
const promptPath = path.join(modeDir, `${modeId}.mode.md`);
113+
fs.writeFileSync(promptPath, modeData.mode_prompt);
114+
}
115+
116+
if (
117+
modeData.context_instructions &&
118+
Array.isArray(modeData.context_instructions)
119+
) {
120+
modeData.context_instructions.forEach((instruction) => {
121+
const filename = `${instruction.title.toLowerCase()}.instructions.md`;
122+
const instructionPath = path.join(modeDir, filename);
123+
fs.writeFileSync(instructionPath, instruction.content);
124+
});
125+
}
126+
127+
updateOrCreateOpenCodeJson(modeData, modeId);
128+
129+
console.log(`✅ Successfully installed mode "${modeId}"`);
130+
} catch (error) {
131+
console.error(`❌ Error installing mode "${modeId}":`, error.message);
132+
process.exit(1);
133+
}
123134
}
124135

125136
function removeMode(modeId) {
126-
try {
127-
console.log(`🗑️ Removing mode: ${modeId}`);
128-
129-
const currentDir = process.cwd();
130-
const modeDir = path.join(currentDir, '.opencode', 'mode', modeId);
131-
const openCodePath = path.join(currentDir, 'opencode.json');
132-
133-
if (fs.existsSync(modeDir)) {
134-
fs.rmSync(modeDir, { recursive: true, force: true });
135-
}
136-
137-
if (fs.existsSync(openCodePath)) {
138-
try {
139-
const openCodeConfig = JSON.parse(fs.readFileSync(openCodePath, 'utf8'));
140-
141-
if (openCodeConfig.mode && openCodeConfig.mode[modeId]) {
142-
delete openCodeConfig.mode[modeId];
143-
fs.writeFileSync(openCodePath, JSON.stringify(openCodeConfig, null, '\t'));
144-
}
145-
} catch (e) {
146-
console.error('⚠️ Error updating opencode.json:', e.message);
147-
}
148-
}
149-
150-
console.log(`✅ Successfully removed mode "${modeId}"`);
151-
152-
} catch (error) {
153-
console.error(`❌ Error removing mode "${modeId}":`, error.message);
154-
process.exit(1);
155-
}
137+
try {
138+
console.log(`🗑️ Removing mode: ${modeId}`);
139+
140+
const currentDir = process.cwd();
141+
const modeDir = path.join(currentDir, '.opencode', 'mode', modeId);
142+
const openCodePath = path.join(currentDir, 'opencode.json');
143+
144+
if (fs.existsSync(modeDir)) {
145+
fs.rmSync(modeDir, { recursive: true, force: true });
146+
}
147+
148+
if (fs.existsSync(openCodePath)) {
149+
try {
150+
const openCodeConfig = JSON.parse(
151+
fs.readFileSync(openCodePath, 'utf8')
152+
);
153+
154+
if (openCodeConfig.mode && openCodeConfig.mode[modeId]) {
155+
delete openCodeConfig.mode[modeId];
156+
fs.writeFileSync(
157+
openCodePath,
158+
JSON.stringify(openCodeConfig, null, '\t')
159+
);
160+
}
161+
} catch (e) {
162+
console.error('⚠️ Error updating opencode.json:', e.message);
163+
}
164+
}
165+
166+
console.log(`✅ Successfully removed mode "${modeId}"`);
167+
} catch (error) {
168+
console.error(`❌ Error removing mode "${modeId}":`, error.message);
169+
process.exit(1);
170+
}
156171
}
157172

158173
const args = process.argv.slice(2);
159174
const command = args[0];
160175
const modeId = args[1];
161176

162177
if (command === 'install' && modeId) {
163-
installMode(modeId);
178+
installMode(modeId);
164179
} else if (command === 'remove' && modeId) {
165-
removeMode(modeId);
180+
removeMode(modeId);
166181
} else {
167-
console.log('Usage: openmodes <command> <mode-id>');
168-
console.log('');
169-
console.log('Commands:');
170-
console.log(' install <mode-id> Install a mode from openmodes.dev');
171-
console.log(' remove <mode-id> Remove an installed mode');
172-
console.log('');
173-
console.log('Examples:');
174-
console.log(' npx openmodes install archie');
175-
console.log(' npx openmodes remove archie');
176-
process.exit(1);
182+
console.log('Usage: openmodes <command> <mode-id>');
183+
console.log('');
184+
console.log('Commands:');
185+
console.log(' install <mode-id> Install a mode from openmodes.dev');
186+
console.log(' remove <mode-id> Remove an installed mode');
187+
console.log('');
188+
console.log('Examples:');
189+
console.log(' npx openmodes install archie');
190+
console.log(' npx openmodes remove archie');
191+
process.exit(1);
177192
}

0 commit comments

Comments
 (0)