gent-cli 1.6.0 → 1.8.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.
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Repository Service - Handles all cloud repository operations
3
+ * Provides methods for creating, managing, and syncing repositories with the cloud
4
+ */
5
+
6
+ const apiClient = require('../utils/api-client');
7
+ const { API_ENDPOINTS } = require('../utils/constants');
8
+
9
+ /**
10
+ * Build URL with path parameters
11
+ * @param {string} template - URL template with placeholders
12
+ * @param {Object} params - Parameters to replace
13
+ * @returns {string} Built URL
14
+ */
15
+ function buildUrl(template, params = {}) {
16
+ let url = template;
17
+ Object.keys(params).forEach(key => {
18
+ url = url.replace(`{${key}}`, params[key]);
19
+ });
20
+ return url;
21
+ }
22
+
23
+ /**
24
+ * Create a new repository
25
+ * @param {string} name - Repository name
26
+ * @param {string} description - Repository description
27
+ * @param {boolean} isPrivate - Whether the repository is private
28
+ * @param {string} defaultBranch - Default branch name
29
+ * @returns {Promise<Object>} Created repository data
30
+ */
31
+ async function createRepository(name, description = '', isPrivate = false, defaultBranch = 'main') {
32
+ try {
33
+ const response = await apiClient.post(API_ENDPOINTS.REPOS_CREATE, {
34
+ name,
35
+ description,
36
+ is_private: isPrivate,
37
+ default_branch: defaultBranch
38
+ });
39
+ return response;
40
+ } catch (error) {
41
+ throw new Error(`Failed to create repository: ${error.response?.data?.message || error.message}`);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * List all repositories owned by the authenticated user
47
+ * @returns {Promise<Array>} List of repositories
48
+ */
49
+ async function listRepositories() {
50
+ try {
51
+ const response = await apiClient.get(API_ENDPOINTS.REPOS_LIST);
52
+ return response;
53
+ } catch (error) {
54
+ throw new Error(`Failed to list repositories: ${error.response?.data?.message || error.message}`);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Get repository details
60
+ * @param {number} ownerId - Owner user ID
61
+ * @param {string} repoName - Repository name
62
+ * @returns {Promise<Object>} Repository data
63
+ */
64
+ async function getRepository(ownerId, repoName) {
65
+ try {
66
+ const url = buildUrl(API_ENDPOINTS.REPOS_GET, { owner_id: ownerId, repo_name: repoName });
67
+ const response = await apiClient.get(url);
68
+ return response;
69
+ } catch (error) {
70
+ throw new Error(`Failed to get repository: ${error.response?.data?.message || error.message}`);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Delete a repository
76
+ * @param {number} ownerId - Owner user ID
77
+ * @param {string} repoName - Repository name
78
+ * @returns {Promise<Object>} Delete response
79
+ */
80
+ async function deleteRepository(ownerId, repoName) {
81
+ try {
82
+ const url = buildUrl(API_ENDPOINTS.REPOS_DELETE, { owner_id: ownerId, repo_name: repoName });
83
+ const response = await apiClient.delete(url);
84
+ return response;
85
+ } catch (error) {
86
+ throw new Error(`Failed to delete repository: ${error.response?.data?.message || error.message}`);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Create a blob (file content) in the repository
92
+ * @param {number} ownerId - Owner user ID
93
+ * @param {string} repoName - Repository name
94
+ * @param {string} content - File content
95
+ * @param {string} encoding - Encoding type (utf-8 or base64)
96
+ * @returns {Promise<Object>} Created blob data
97
+ */
98
+ async function createBlob(ownerId, repoName, content, encoding = 'utf-8') {
99
+ try {
100
+ const url = buildUrl(API_ENDPOINTS.BLOB_CREATE, { owner_id: ownerId, repo_name: repoName });
101
+ const response = await apiClient.post(url, {
102
+ content,
103
+ encoding
104
+ });
105
+ return response;
106
+ } catch (error) {
107
+ throw new Error(`Failed to create blob: ${error.response?.data?.message || error.message}`);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Get a blob by SHA
113
+ * @param {number} ownerId - Owner user ID
114
+ * @param {string} repoName - Repository name
115
+ * @param {string} sha - Blob SHA
116
+ * @returns {Promise<Object>} Blob data
117
+ */
118
+ async function getBlob(ownerId, repoName, sha) {
119
+ try {
120
+ const url = buildUrl(API_ENDPOINTS.BLOB_GET, { owner_id: ownerId, repo_name: repoName, sha });
121
+ const response = await apiClient.get(url);
122
+ return response;
123
+ } catch (error) {
124
+ throw new Error(`Failed to get blob: ${error.response?.data?.message || error.message}`);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Create a tree (directory structure) in the repository
130
+ * @param {number} ownerId - Owner user ID
131
+ * @param {string} repoName - Repository name
132
+ * @param {Array} entries - Tree entries
133
+ * @returns {Promise<Object>} Created tree data
134
+ */
135
+ async function createTree(ownerId, repoName, entries) {
136
+ try {
137
+ const url = buildUrl(API_ENDPOINTS.TREE_CREATE, { owner_id: ownerId, repo_name: repoName });
138
+ const response = await apiClient.post(url, {
139
+ entries
140
+ });
141
+ return response;
142
+ } catch (error) {
143
+ throw new Error(`Failed to create tree: ${error.response?.data?.message || error.message}`);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Get a tree by SHA
149
+ * @param {number} ownerId - Owner user ID
150
+ * @param {string} repoName - Repository name
151
+ * @param {string} sha - Tree SHA
152
+ * @returns {Promise<Object>} Tree data
153
+ */
154
+ async function getTree(ownerId, repoName, sha) {
155
+ try {
156
+ const url = buildUrl(API_ENDPOINTS.TREE_GET, { owner_id: ownerId, repo_name: repoName, sha });
157
+ const response = await apiClient.get(url);
158
+ return response;
159
+ } catch (error) {
160
+ throw new Error(`Failed to get tree: ${error.response?.data?.message || error.message}`);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Create a commit in the repository
166
+ * @param {number} ownerId - Owner user ID
167
+ * @param {string} repoName - Repository name
168
+ * @param {Object} commitData - Commit data (message, tree_sha, parent_shas, author_name, author_email, branch)
169
+ * @returns {Promise<Object>} Created commit data
170
+ */
171
+ async function createCommit(ownerId, repoName, commitData) {
172
+ try {
173
+ const url = buildUrl(API_ENDPOINTS.COMMITS_CREATE, { owner_id: ownerId, repo_name: repoName });
174
+ const response = await apiClient.post(url, commitData);
175
+ return response;
176
+ } catch (error) {
177
+ throw new Error(`Failed to create commit: ${error.response?.data?.message || error.message}`);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * List all commits in a repository
183
+ * @param {number} ownerId - Owner user ID
184
+ * @param {string} repoName - Repository name
185
+ * @returns {Promise<Array>} List of commits
186
+ */
187
+ async function listCommits(ownerId, repoName) {
188
+ try {
189
+ const url = buildUrl(API_ENDPOINTS.COMMITS_LIST, { owner_id: ownerId, repo_name: repoName });
190
+ const response = await apiClient.get(url);
191
+ return response;
192
+ } catch (error) {
193
+ throw new Error(`Failed to list commits: ${error.response?.data?.message || error.message}`);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Get a commit by SHA
199
+ * @param {number} ownerId - Owner user ID
200
+ * @param {string} repoName - Repository name
201
+ * @param {string} sha - Commit SHA
202
+ * @returns {Promise<Object>} Commit data
203
+ */
204
+ async function getCommit(ownerId, repoName, sha) {
205
+ try {
206
+ const url = buildUrl(API_ENDPOINTS.COMMITS_GET, { owner_id: ownerId, repo_name: repoName, sha });
207
+ const response = await apiClient.get(url);
208
+ return response;
209
+ } catch (error) {
210
+ throw new Error(`Failed to get commit: ${error.response?.data?.message || error.message}`);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Create a branch in the repository
216
+ * @param {number} ownerId - Owner user ID
217
+ * @param {string} repoName - Repository name
218
+ * @param {string} branchName - Branch name
219
+ * @param {string} commitSha - Commit SHA to point the branch to
220
+ * @returns {Promise<Object>} Created branch data
221
+ */
222
+ async function createBranch(ownerId, repoName, branchName, commitSha) {
223
+ try {
224
+ const url = buildUrl(API_ENDPOINTS.BRANCHES_CREATE, { owner_id: ownerId, repo_name: repoName });
225
+ const response = await apiClient.post(url, {
226
+ name: branchName,
227
+ commit_sha: commitSha
228
+ });
229
+ return response;
230
+ } catch (error) {
231
+ throw new Error(`Failed to create branch: ${error.response?.data?.message || error.message}`);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * List all branches in a repository
237
+ * @param {number} ownerId - Owner user ID
238
+ * @param {string} repoName - Repository name
239
+ * @returns {Promise<Array>} List of branches
240
+ */
241
+ async function listBranches(ownerId, repoName) {
242
+ try {
243
+ const url = buildUrl(API_ENDPOINTS.BRANCHES_LIST, { owner_id: ownerId, repo_name: repoName });
244
+ const response = await apiClient.get(url);
245
+ return response;
246
+ } catch (error) {
247
+ throw new Error(`Failed to list branches: ${error.response?.data?.message || error.message}`);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Get a branch by name
253
+ * @param {number} ownerId - Owner user ID
254
+ * @param {string} repoName - Repository name
255
+ * @param {string} branchName - Branch name
256
+ * @returns {Promise<Object>} Branch data
257
+ */
258
+ async function getBranch(ownerId, repoName, branchName) {
259
+ try {
260
+ const url = buildUrl(API_ENDPOINTS.BRANCHES_GET, { owner_id: ownerId, repo_name: repoName, branch_name: branchName });
261
+ const response = await apiClient.get(url);
262
+ return response;
263
+ } catch (error) {
264
+ throw new Error(`Failed to get branch: ${error.response?.data?.message || error.message}`);
265
+ }
266
+ }
267
+
268
+ module.exports = {
269
+ createRepository,
270
+ listRepositories,
271
+ getRepository,
272
+ deleteRepository,
273
+ createBlob,
274
+ getBlob,
275
+ createTree,
276
+ getTree,
277
+ createCommit,
278
+ listCommits,
279
+ getCommit,
280
+ createBranch,
281
+ listBranches,
282
+ getBranch
283
+ };
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Cloud Sync Utilities - Handles synchronization between local and cloud repositories
3
+ * Provides functions for uploading and downloading commits, blobs, and trees
4
+ */
5
+
6
+ const fs = require('fs').promises;
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const ora = require('ora');
10
+ const repoService = require('../services/repo-service');
11
+ const { GENT_DIR, OBJECTS_DIR } = require('./constants');
12
+ const { readJSON, writeJSON, pathExists } = require('./fileSystem');
13
+
14
+ /**
15
+ * Upload blobs (file contents) to cloud
16
+ * @param {Array} files - Array of file objects with path and content
17
+ * @param {number} ownerId - Owner user ID
18
+ * @param {string} repoName - Repository name
19
+ * @returns {Promise<Object>} Map of file paths to blob SHAs
20
+ */
21
+ async function uploadBlobs(files, ownerId, repoName) {
22
+ const spinner = ora('Uploading file contents...').start();
23
+ const blobMap = {};
24
+
25
+ try {
26
+ for (let i = 0; i < files.length; i++) {
27
+ const file = files[i];
28
+ spinner.text = `Uploading ${file.path} (${i + 1}/${files.length})`;
29
+
30
+ const blob = await repoService.createBlob(
31
+ ownerId,
32
+ repoName,
33
+ file.content,
34
+ 'utf-8'
35
+ );
36
+
37
+ blobMap[file.path] = blob.sha;
38
+ }
39
+
40
+ spinner.succeed(`Uploaded ${files.length} file(s)`);
41
+ return blobMap;
42
+ } catch (error) {
43
+ spinner.fail('Failed to upload files');
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Download blobs from cloud
50
+ * @param {Array} shas - Array of blob SHAs to download
51
+ * @param {number} ownerId - Owner user ID
52
+ * @param {string} repoName - Repository name
53
+ * @returns {Promise<Object>} Map of SHAs to content
54
+ */
55
+ async function downloadBlobs(shas, ownerId, repoName) {
56
+ const spinner = ora('Downloading file contents...').start();
57
+ const blobMap = {};
58
+
59
+ try {
60
+ for (let i = 0; i < shas.length; i++) {
61
+ const sha = shas[i];
62
+ spinner.text = `Downloading blob ${sha.substring(0, 7)} (${i + 1}/${shas.length})`;
63
+
64
+ const blob = await repoService.getBlob(ownerId, repoName, sha);
65
+ blobMap[sha] = blob.content;
66
+ }
67
+
68
+ spinner.succeed(`Downloaded ${shas.length} blob(s)`);
69
+ return blobMap;
70
+ } catch (error) {
71
+ spinner.fail('Failed to download blobs');
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Build tree structure from file list
78
+ * @param {Array} files - Array of file objects with path and SHA
79
+ * @returns {Array} Tree entries
80
+ */
81
+ function buildTreeEntries(files) {
82
+ return files.map(file => ({
83
+ path: file.path,
84
+ mode: '100644', // Regular file
85
+ type: 'blob',
86
+ sha: file.sha
87
+ }));
88
+ }
89
+
90
+ /**
91
+ * Upload a tree structure to cloud
92
+ * @param {Array} files - Array of file objects with path and SHA
93
+ * @param {number} ownerId - Owner user ID
94
+ * @param {string} repoName - Repository name
95
+ * @returns {Promise<Object>} Created tree data
96
+ */
97
+ async function uploadTree(files, ownerId, repoName) {
98
+ const spinner = ora('Creating tree structure...').start();
99
+
100
+ try {
101
+ const entries = buildTreeEntries(files);
102
+ const tree = await repoService.createTree(ownerId, repoName, entries);
103
+
104
+ spinner.succeed(`Created tree ${tree.sha.substring(0, 7)}`);
105
+ return tree;
106
+ } catch (error) {
107
+ spinner.fail('Failed to create tree');
108
+ throw error;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Upload a commit to cloud
114
+ * @param {Object} commit - Local commit object
115
+ * @param {string} treeSha - Tree SHA
116
+ * @param {number} ownerId - Owner user ID
117
+ * @param {string} repoName - Repository name
118
+ * @param {string} branchName - Branch name
119
+ * @returns {Promise<Object>} Created commit data
120
+ */
121
+ async function uploadCommit(commit, treeSha, ownerId, repoName, branchName) {
122
+ const spinner = ora('Creating commit...').start();
123
+
124
+ try {
125
+ const commitData = {
126
+ message: commit.message,
127
+ tree_sha: treeSha,
128
+ parent_shas: commit.parent || [],
129
+ author_name: commit.author.name,
130
+ author_email: commit.author.email,
131
+ branch: branchName
132
+ };
133
+
134
+ const cloudCommit = await repoService.createCommit(ownerId, repoName, commitData);
135
+
136
+ spinner.succeed(`Created commit ${cloudCommit.sha.substring(0, 7)}`);
137
+ return cloudCommit;
138
+ } catch (error) {
139
+ spinner.fail('Failed to create commit');
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Sync local commits to cloud
146
+ * @param {Array} commits - Array of local commits to upload
147
+ * @param {number} ownerId - Owner user ID
148
+ * @param {string} repoName - Repository name
149
+ * @param {string} branchName - Branch name
150
+ * @param {string} cwd - Current working directory
151
+ * @returns {Promise<Array>} Array of created cloud commits
152
+ */
153
+ async function syncCommitsToCloud(commits, ownerId, repoName, branchName, cwd) {
154
+ console.log(chalk.cyan(`\nPushing ${commits.length} commit(s) to ${ownerId}/${repoName}...`));
155
+
156
+ const cloudCommits = [];
157
+
158
+ for (let i = 0; i < commits.length; i++) {
159
+ const commit = commits[i];
160
+ console.log(chalk.gray(`\nProcessing commit ${commit.sha.substring(0, 7)}: ${commit.message}`));
161
+
162
+ // Read files from commit
163
+ const files = [];
164
+ for (const [filePath, fileSha] of Object.entries(commit.files || {})) {
165
+ const objectPath = path.join(cwd, GENT_DIR, OBJECTS_DIR, fileSha);
166
+ if (await pathExists(objectPath)) {
167
+ const content = await fs.readFile(objectPath, 'utf-8');
168
+ files.push({ path: filePath, content });
169
+ }
170
+ }
171
+
172
+ // Upload blobs
173
+ const blobMap = await uploadBlobs(files, ownerId, repoName);
174
+
175
+ // Build file list with SHAs
176
+ const fileList = Object.keys(blobMap).map(path => ({
177
+ path,
178
+ sha: blobMap[path]
179
+ }));
180
+
181
+ // Upload tree
182
+ const tree = await uploadTree(fileList, ownerId, repoName);
183
+
184
+ // Upload commit
185
+ const cloudCommit = await uploadCommit(commit, tree.sha, ownerId, repoName, branchName);
186
+
187
+ cloudCommits.push(cloudCommit);
188
+ }
189
+
190
+ console.log(chalk.green(`\n✓ Successfully pushed ${commits.length} commit(s)`));
191
+ return cloudCommits;
192
+ }
193
+
194
+ /**
195
+ * Download commits from cloud to local
196
+ * @param {number} ownerId - Owner user ID
197
+ * @param {string} repoName - Repository name
198
+ * @param {string} cwd - Current working directory
199
+ * @returns {Promise<Array>} Array of downloaded commits
200
+ */
201
+ async function syncCommitsFromCloud(ownerId, repoName, cwd) {
202
+ const spinner = ora('Fetching commits from cloud...').start();
203
+
204
+ try {
205
+ const cloudCommits = await repoService.listCommits(ownerId, repoName);
206
+ spinner.succeed(`Fetched ${cloudCommits.length} commit(s)`);
207
+
208
+ // Download trees and blobs for each commit
209
+ for (const commit of cloudCommits) {
210
+ console.log(chalk.gray(`Processing commit ${commit.sha.substring(0, 7)}: ${commit.message}`));
211
+
212
+ // Download tree
213
+ const tree = await repoService.getTree(ownerId, repoName, commit.tree_sha);
214
+
215
+ // Store entries for local metadata
216
+ commit.files = tree.entries.map(entry => ({
217
+ path: entry.path,
218
+ hash: entry.sha
219
+ }));
220
+
221
+ // Download blobs from tree entries
222
+ const blobShas = tree.entries.map(entry => entry.sha);
223
+ const blobMap = await downloadBlobs(blobShas, ownerId, repoName);
224
+
225
+ // Save blobs to local objects directory
226
+ const objectsDir = path.join(cwd, GENT_DIR, OBJECTS_DIR);
227
+ for (const [sha, content] of Object.entries(blobMap)) {
228
+ const objectPath = path.join(objectsDir, sha);
229
+ await fs.writeFile(objectPath, content, 'utf-8');
230
+ }
231
+ }
232
+
233
+ console.log(chalk.green(`\n✓ Successfully pulled ${cloudCommits.length} commit(s)`));
234
+ return cloudCommits;
235
+ } catch (error) {
236
+ spinner.fail('Failed to fetch commits');
237
+ throw error;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Get or create remote configuration
243
+ * @param {string} cwd - Current working directory
244
+ * @returns {Promise<Object>} Remote configuration
245
+ */
246
+ async function getRemoteConfig(cwd) {
247
+ const remotePath = path.join(cwd, GENT_DIR, 'remote.json');
248
+
249
+ if (await pathExists(remotePath)) {
250
+ return await readJSON(remotePath);
251
+ }
252
+
253
+ return { remotes: {} };
254
+ }
255
+
256
+ /**
257
+ * Save remote configuration
258
+ * @param {Object} config - Remote configuration
259
+ * @param {string} cwd - Current working directory
260
+ */
261
+ async function saveRemoteConfig(config, cwd) {
262
+ const remotePath = path.join(cwd, GENT_DIR, 'remote.json');
263
+ await writeJSON(remotePath, config);
264
+ }
265
+
266
+ /**
267
+ * Add a remote to configuration
268
+ * @param {string} name - Remote name
269
+ * @param {number} ownerId - Owner user ID
270
+ * @param {string} repoName - Repository name
271
+ * @param {string} cwd - Current working directory
272
+ */
273
+ async function addRemote(name, ownerId, repoName, cwd) {
274
+ const config = await getRemoteConfig(cwd);
275
+
276
+ config.remotes[name] = {
277
+ owner_id: ownerId,
278
+ repo_name: repoName
279
+ };
280
+
281
+ await saveRemoteConfig(config, cwd);
282
+ }
283
+
284
+ /**
285
+ * Remove a remote from configuration
286
+ * @param {string} name - Remote name
287
+ * @param {string} cwd - Current working directory
288
+ */
289
+ async function removeRemote(name, cwd) {
290
+ const config = await getRemoteConfig(cwd);
291
+
292
+ if (config.remotes[name]) {
293
+ delete config.remotes[name];
294
+ await saveRemoteConfig(config, cwd);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Get remote by name
300
+ * @param {string} name - Remote name
301
+ * @param {string} cwd - Current working directory
302
+ * @returns {Promise<Object|null>} Remote configuration or null
303
+ */
304
+ async function getRemote(name, cwd) {
305
+ const config = await getRemoteConfig(cwd);
306
+ return config.remotes[name] || null;
307
+ }
308
+
309
+ module.exports = {
310
+ uploadBlobs,
311
+ downloadBlobs,
312
+ buildTreeEntries,
313
+ uploadTree,
314
+ uploadCommit,
315
+ syncCommitsToCloud,
316
+ syncCommitsFromCloud,
317
+ getRemoteConfig,
318
+ saveRemoteConfig,
319
+ addRemote,
320
+ removeRemote,
321
+ getRemote
322
+ };
@@ -11,15 +11,42 @@ module.exports = {
11
11
  COMMITS_FILE: 'commits.json',
12
12
  HEAD_FILE: 'HEAD',
13
13
  AUTH_FILE: 'auth.json',
14
+ OBJECTS_DIR: 'objects',
15
+ REMOTE_CONFIG_FILE: 'remote.json',
14
16
 
15
17
  // API Configuration
16
18
  API_BASE_URL: 'https://gent-api.onrender.com',
17
19
  API_ENDPOINTS: {
20
+ // Auth endpoints
18
21
  LOGIN: '/api/auth/login/',
19
22
  REGISTER: '/api/auth/register/',
20
23
  LOGOUT: '/api/auth/logout/',
21
24
  REFRESH: '/api/auth/token/refresh/',
22
- PROFILE: '/api/auth/profile/'
25
+ PROFILE: '/api/auth/profile/',
26
+
27
+ // Repository endpoints
28
+ REPOS_LIST: '/api/repos/',
29
+ REPOS_CREATE: '/api/repos/create/',
30
+ REPOS_GET: '/api/repos/{owner_id}/{repo_name}/',
31
+ REPOS_DELETE: '/api/repos/{owner_id}/{repo_name}/delete/',
32
+
33
+ // Blob endpoints
34
+ BLOB_GET: '/api/repos/{owner_id}/{repo_name}/blob/{sha}/',
35
+ BLOB_CREATE: '/api/repos/{owner_id}/{repo_name}/blob/create/',
36
+
37
+ // Tree endpoints
38
+ TREE_GET: '/api/repos/{owner_id}/{repo_name}/tree/{sha}/',
39
+ TREE_CREATE: '/api/repos/{owner_id}/{repo_name}/tree/create/',
40
+
41
+ // Commit endpoints
42
+ COMMITS_LIST: '/api/repos/{owner_id}/{repo_name}/commits/',
43
+ COMMITS_GET: '/api/repos/{owner_id}/{repo_name}/commits/{sha}/',
44
+ COMMITS_CREATE: '/api/repos/{owner_id}/{repo_name}/commits/create/',
45
+
46
+ // Branch endpoints
47
+ BRANCHES_LIST: '/api/repos/{owner_id}/{repo_name}/branches/',
48
+ BRANCHES_GET: '/api/repos/{owner_id}/{repo_name}/branches/{branch_name}/',
49
+ BRANCHES_CREATE: '/api/repos/{owner_id}/{repo_name}/branches/create/'
23
50
  },
24
51
 
25
52
  // Default ignore patterns