opencode-studio-server 1.20.0 → 1.21.0
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/index.js +119 -94
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1137,40 +1137,34 @@ async function ensureGitHubRepo(token, repoName) {
|
|
|
1137
1137
|
throw new Error(`Failed to check repo: ${err}`);
|
|
1138
1138
|
}
|
|
1139
1139
|
|
|
1140
|
-
function
|
|
1141
|
-
const
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
files[blobKey] = blob;
|
|
1165
|
-
tree.push(blob);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
return { sha: null, tree };
|
|
1170
|
-
}
|
|
1140
|
+
function collectBlobs(rootDir, basePath = '', blobs = []) {
|
|
1141
|
+
const dir = basePath || rootDir;
|
|
1142
|
+
if (!fs.existsSync(dir)) return blobs;
|
|
1143
|
+
|
|
1144
|
+
for (const name of fs.readdirSync(dir)) {
|
|
1145
|
+
const fullPath = path.join(dir, name);
|
|
1146
|
+
const stat = fs.statSync(fullPath);
|
|
1147
|
+
|
|
1148
|
+
if (stat.isDirectory()) {
|
|
1149
|
+
if (name === 'node_modules' || name === '.git' || name === '.next') continue;
|
|
1150
|
+
collectBlobs(rootDir, fullPath, blobs);
|
|
1151
|
+
} else {
|
|
1152
|
+
if (name.endsWith('.log') || name.endsWith('.tmp')) continue;
|
|
1153
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
1154
|
+
blobs.push({
|
|
1155
|
+
path: path.relative(rootDir, fullPath).replace(/\\/g, '/'),
|
|
1156
|
+
mode: '100644',
|
|
1157
|
+
type: 'blob',
|
|
1158
|
+
content
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return blobs;
|
|
1163
|
+
}
|
|
1171
1164
|
|
|
1172
|
-
async function createGitHubBlob(token, blob) {
|
|
1173
|
-
const
|
|
1165
|
+
async function createGitHubBlob(token, repoName, blob) {
|
|
1166
|
+
const [owner, repo] = repoName.split('/');
|
|
1167
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/blobs`, {
|
|
1174
1168
|
method: 'POST',
|
|
1175
1169
|
headers: {
|
|
1176
1170
|
'Authorization': `Bearer ${token}`,
|
|
@@ -1191,32 +1185,35 @@ async function createGitHubBlob(token, blob) {
|
|
|
1191
1185
|
return data.sha;
|
|
1192
1186
|
}
|
|
1193
1187
|
|
|
1194
|
-
async function createGitHubTree(token, repoName, treeItems) {
|
|
1195
|
-
const [owner, repo] = repoName.split('/');
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1188
|
+
async function createGitHubTree(token, repoName, treeItems, baseSha = null) {
|
|
1189
|
+
const [owner, repo] = repoName.split('/');
|
|
1190
|
+
const body = {
|
|
1191
|
+
tree: treeItems.map(item => ({
|
|
1192
|
+
path: item.path,
|
|
1193
|
+
mode: item.mode,
|
|
1194
|
+
type: item.type,
|
|
1195
|
+
sha: item.sha
|
|
1196
|
+
}))
|
|
1197
|
+
};
|
|
1198
|
+
if (baseSha) body.base_tree = baseSha;
|
|
1199
|
+
|
|
1200
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees`, {
|
|
1201
|
+
method: 'POST',
|
|
1202
|
+
headers: {
|
|
1203
|
+
'Authorization': `Bearer ${token}`,
|
|
1204
|
+
'Content-Type': 'application/json'
|
|
1205
|
+
},
|
|
1206
|
+
body: JSON.stringify(body)
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
if (!response.ok) {
|
|
1210
|
+
const err = await response.text();
|
|
1211
|
+
throw new Error(`Failed to create tree: ${err}`);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
const data = await response.json();
|
|
1215
|
+
return data.sha;
|
|
1216
|
+
}
|
|
1220
1217
|
|
|
1221
1218
|
async function createGitHubCommit(token, repoName, treeSha, message) {
|
|
1222
1219
|
const [owner, repo] = repoName.split('/');
|
|
@@ -1253,25 +1250,49 @@ async function createGitHubCommit(token, repoName, treeSha, message) {
|
|
|
1253
1250
|
return data.sha;
|
|
1254
1251
|
}
|
|
1255
1252
|
|
|
1256
|
-
async function updateGitHubRef(token, repoName, commitSha, branch = 'main') {
|
|
1257
|
-
const [owner, repo] = repoName.split('/');
|
|
1258
|
-
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1253
|
+
async function updateGitHubRef(token, repoName, commitSha, branch = 'main') {
|
|
1254
|
+
const [owner, repo] = repoName.split('/');
|
|
1255
|
+
|
|
1256
|
+
const checkRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`, {
|
|
1257
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
if (checkRes.status === 404) {
|
|
1261
|
+
const createRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs`, {
|
|
1262
|
+
method: 'POST',
|
|
1263
|
+
headers: {
|
|
1264
|
+
'Authorization': `Bearer ${token}`,
|
|
1265
|
+
'Content-Type': 'application/json'
|
|
1266
|
+
},
|
|
1267
|
+
body: JSON.stringify({
|
|
1268
|
+
ref: `refs/heads/${branch}`,
|
|
1269
|
+
sha: commitSha
|
|
1270
|
+
})
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
if (!createRes.ok) {
|
|
1274
|
+
const err = await createRes.text();
|
|
1275
|
+
throw new Error(`Failed to create ref: ${err}`);
|
|
1276
|
+
}
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`, {
|
|
1281
|
+
method: 'PATCH',
|
|
1282
|
+
headers: {
|
|
1283
|
+
'Authorization': `Bearer ${token}`,
|
|
1284
|
+
'Content-Type': 'application/json'
|
|
1285
|
+
},
|
|
1286
|
+
body: JSON.stringify({
|
|
1287
|
+
sha: commitSha
|
|
1288
|
+
})
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
if (!response.ok) {
|
|
1292
|
+
const err = await response.text();
|
|
1293
|
+
throw new Error(`Failed to update ref: ${err}`);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1275
1296
|
|
|
1276
1297
|
app.get('/api/github/backup/status', async (req, res) => {
|
|
1277
1298
|
try {
|
|
@@ -1328,22 +1349,26 @@ app.post('/api/github/backup', async (req, res) => {
|
|
|
1328
1349
|
const opencodeConfig = getConfigPath();
|
|
1329
1350
|
if (!opencodeConfig) return res.status(400).json({ error: 'No opencode config path found' });
|
|
1330
1351
|
|
|
1331
|
-
const opencodeDir = path.dirname(opencodeConfig);
|
|
1332
|
-
const studioDir = path.join(HOME_DIR, '.config', 'opencode-studio');
|
|
1333
|
-
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1352
|
+
const opencodeDir = path.dirname(opencodeConfig);
|
|
1353
|
+
const studioDir = path.join(HOME_DIR, '.config', 'opencode-studio');
|
|
1354
|
+
|
|
1355
|
+
const opencodeBlobs = collectBlobs(opencodeDir);
|
|
1356
|
+
const studioBlobs = collectBlobs(studioDir);
|
|
1357
|
+
|
|
1358
|
+
for (const blob of opencodeBlobs) {
|
|
1359
|
+
blob.sha = await createGitHubBlob(token, repoName, blob);
|
|
1360
|
+
blob.path = `opencode/${blob.path}`;
|
|
1361
|
+
delete blob.content;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
for (const blob of studioBlobs) {
|
|
1365
|
+
blob.sha = await createGitHubBlob(token, repoName, blob);
|
|
1366
|
+
blob.path = `opencode-studio/${blob.path}`;
|
|
1367
|
+
delete blob.content;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const allBlobs = [...opencodeBlobs, ...studioBlobs];
|
|
1371
|
+
const rootTreeSha = await createGitHubTree(token, repoName, allBlobs);
|
|
1347
1372
|
|
|
1348
1373
|
const timestamp = new Date().toISOString();
|
|
1349
1374
|
const commitMessage = `OpenCode Studio backup ${timestamp}`;
|