gitxelectron 1.1.7 → 1.1.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.
package/out/main/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  const electron = require("electron");
3
3
  const path = require("path");
4
- const simpleGit = require("simple-git");
5
4
  const fs = require("fs/promises");
5
+ const simpleGit = require("simple-git");
6
6
  const crypto = require("crypto");
7
7
  const generativeAi = require("@google/generative-ai");
8
8
  const OpenAI = require("openai");
@@ -288,6 +288,68 @@ async function getUserRepositories(sessionId) {
288
288
  }
289
289
  return await res.json();
290
290
  }
291
+ async function getCollaboratedRepos(sessionId) {
292
+ const token = getToken(sessionId);
293
+ if (!token) throw new Error("Not authenticated in this session");
294
+ const headers = {
295
+ Authorization: `Bearer ${token}`,
296
+ Accept: "application/vnd.github.v3+json",
297
+ "User-Agent": "gitelectron-App"
298
+ };
299
+ const res = await fetch(
300
+ "https://api.github.com/user/repos?affiliation=owner,collaborator,organization_member&per_page=100&sort=updated",
301
+ { headers }
302
+ );
303
+ if (!res.ok) {
304
+ const errorData = await res.json().catch(() => ({}));
305
+ throw new Error(errorData.message || "Failed to fetch collaborated repositories");
306
+ }
307
+ const allRepos = await res.json();
308
+ const account = getActiveAccount(sessionId);
309
+ const currentUsername = account?.username?.toLowerCase() || "";
310
+ const externalCollabRepos = [];
311
+ const ownedRepos = [];
312
+ for (const repo of allRepos) {
313
+ const ownerLogin = repo.owner?.login?.toLowerCase() || "";
314
+ if (ownerLogin !== currentUsername) {
315
+ externalCollabRepos.push(repo);
316
+ } else {
317
+ ownedRepos.push(repo);
318
+ }
319
+ }
320
+ const ownedCollabChecks = await Promise.allSettled(
321
+ ownedRepos.map(async (repo) => {
322
+ try {
323
+ const [collabRes, invRes] = await Promise.all([
324
+ fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}/collaborators`, { headers }),
325
+ fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}/invitations`, { headers })
326
+ ]);
327
+ const collabs = collabRes.ok ? await collabRes.json() : [];
328
+ const invs = invRes.ok ? await invRes.json() : [];
329
+ const hasOtherCollabs = Array.isArray(collabs) && collabs.some(
330
+ (c) => c.login && c.login.toLowerCase() !== currentUsername
331
+ );
332
+ const hasPendingInvs = Array.isArray(invs) && invs.length > 0;
333
+ if (hasOtherCollabs || hasPendingInvs) {
334
+ return {
335
+ ...repo,
336
+ collaboratorsList: collabs,
337
+ hasPendingInvitations: hasPendingInvs
338
+ };
339
+ }
340
+ } catch (err) {
341
+ }
342
+ return null;
343
+ })
344
+ );
345
+ const ownedCollabRepos = [];
346
+ for (const result of ownedCollabChecks) {
347
+ if (result.status === "fulfilled" && result.value) {
348
+ ownedCollabRepos.push(result.value);
349
+ }
350
+ }
351
+ return [...externalCollabRepos, ...ownedCollabRepos];
352
+ }
291
353
  async function createGitHubRepository(sessionId, name, description, isPrivate, autoInit = false, gitignoreTemplate = "", licenseTemplate = "") {
292
354
  const token = getToken(sessionId);
293
355
  if (!token) throw new Error("Not authenticated in this session");
@@ -390,7 +452,9 @@ async function removeCollaborator(sessionId, remoteUrl, usernameOrId, isInvitati
390
452
  async function searchGitHubUser(sessionId, username) {
391
453
  const token = getToken(sessionId);
392
454
  if (!token) throw new Error("Not authenticated");
393
- const res = await fetch(`https://api.github.com/users/${username}`, {
455
+ const trimmed = username.trim();
456
+ if (!trimmed) return null;
457
+ const res = await fetch(`https://api.github.com/users/${encodeURIComponent(trimmed)}`, {
394
458
  headers: {
395
459
  Authorization: `Bearer ${token}`,
396
460
  Accept: "application/vnd.github.v3+json",
@@ -407,17 +471,32 @@ async function searchGitHubUser(sessionId, username) {
407
471
  async function searchGitHubUsers(sessionId, query) {
408
472
  const token = getToken(sessionId);
409
473
  if (!token) throw new Error("Not authenticated");
410
- if (!query) return [];
411
- const res = await fetch(`https://api.github.com/search/users?q=${encodeURIComponent(query)}&per_page=5`, {
474
+ const trimmed = query.trim();
475
+ if (!trimmed) return [];
476
+ const searchPromise = fetch(
477
+ `https://api.github.com/search/users?q=${encodeURIComponent(trimmed)}+type:user&per_page=15`,
478
+ {
479
+ headers: {
480
+ Authorization: `Bearer ${token}`,
481
+ Accept: "application/vnd.github.v3+json",
482
+ "User-Agent": "gitelectron-App"
483
+ }
484
+ }
485
+ ).then((r) => r.ok ? r.json() : { items: [] }).catch(() => ({ items: [] }));
486
+ const exactPromise = /^[a-zA-Z0-9-]+$/.test(trimmed) ? fetch(`https://api.github.com/users/${encodeURIComponent(trimmed)}`, {
412
487
  headers: {
413
488
  Authorization: `Bearer ${token}`,
414
489
  Accept: "application/vnd.github.v3+json",
415
490
  "User-Agent": "gitelectron-App"
416
491
  }
417
- });
418
- if (!res.ok) throw new Error("Failed to search users");
419
- const data = await res.json();
420
- return data.items || [];
492
+ }).then((r) => r.ok ? r.json() : null).catch(() => null) : Promise.resolve(null);
493
+ const [searchData, exactUser] = await Promise.all([searchPromise, exactPromise]);
494
+ let items = searchData.items || [];
495
+ if (exactUser && exactUser.login) {
496
+ items = items.filter((u) => u.login.toLowerCase() !== exactUser.login.toLowerCase());
497
+ items.unshift(exactUser);
498
+ }
499
+ return items;
421
500
  }
422
501
  async function getRepositoryBranches(sessionId, owner, repo) {
423
502
  const token = getToken(sessionId);
@@ -1009,6 +1088,22 @@ async function executeRawCommand(sessionId, args) {
1009
1088
  const { git } = getGitSession(sessionId);
1010
1089
  return await git.raw(args);
1011
1090
  }
1091
+ async function cloneRepository(sessionId, remoteUrl, destPath) {
1092
+ const authUrl = injectAuthToken(sessionId, remoteUrl);
1093
+ await fs__namespace.mkdir(path__namespace.dirname(destPath), { recursive: true });
1094
+ const git = simpleGit.simpleGit().env({
1095
+ ...process.env,
1096
+ GCM_INTERACTIVE: "false",
1097
+ GIT_TERMINAL_PROMPT: "0",
1098
+ GIT_ASKPASS: "echo"
1099
+ });
1100
+ await git.clone(authUrl, destPath);
1101
+ try {
1102
+ await simpleGit.simpleGit().raw(["config", "--global", "--add", "safe.directory", destPath]);
1103
+ } catch (e) {
1104
+ }
1105
+ return destPath;
1106
+ }
1012
1107
  const SYSTEM_PROMPT = `You are an autonomous Git Healer Agent for the "GitElectron" app.
1013
1108
  The user has encountered a Git error during a push, pull, or merge operation and you are executing as a background process to silently fix it.
1014
1109
  You will receive the Git Status and the Raw Error Message.
@@ -1378,6 +1473,9 @@ electron.app.whenReady().then(async () => {
1378
1473
  electron.ipcMain.handle("get-user-repositories", async (event) => {
1379
1474
  return await getUserRepositories(event.sender.id);
1380
1475
  });
1476
+ electron.ipcMain.handle("get-collaborated-repos", async (event) => {
1477
+ return await getCollaboratedRepos(event.sender.id);
1478
+ });
1381
1479
  electron.ipcMain.handle(
1382
1480
  "create-github-repository",
1383
1481
  async (event, name, description, isPrivate, autoInit, gitignoreTemplate, licenseTemplate) => {
@@ -1497,6 +1595,37 @@ electron.app.whenReady().then(async () => {
1497
1595
  electron.ipcMain.handle("execute-raw-git", async (event, args) => {
1498
1596
  return await executeRawCommand(event.sender.id, args);
1499
1597
  });
1598
+ electron.ipcMain.handle("clone-repository", async (event, remoteUrl, destPath) => {
1599
+ return await cloneRepository(event.sender.id, remoteUrl, destPath);
1600
+ });
1601
+ electron.ipcMain.handle("check-path-exists", async (_event, targetPath) => {
1602
+ if (!targetPath) return false;
1603
+ try {
1604
+ const stats = await fs__namespace.stat(targetPath);
1605
+ return stats.isDirectory();
1606
+ } catch {
1607
+ return false;
1608
+ }
1609
+ });
1610
+ electron.ipcMain.handle("upload-files-to-repo", async (event, repoPath) => {
1611
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
1612
+ if (!repoPath) throw new Error("No repository path specified");
1613
+ const result = await electron.dialog.showOpenDialog(window || void 0, {
1614
+ title: "Select Files to Upload to Repository",
1615
+ properties: ["openFile", "multiSelections"]
1616
+ });
1617
+ if (result.canceled || !result.filePaths.length) {
1618
+ return { success: false, count: 0, files: [] };
1619
+ }
1620
+ const copiedFiles = [];
1621
+ for (const filePath of result.filePaths) {
1622
+ const fileName = path__namespace.basename(filePath);
1623
+ const destPath = path__namespace.join(repoPath, fileName);
1624
+ await fs__namespace.copyFile(filePath, destPath);
1625
+ copiedFiles.push(fileName);
1626
+ }
1627
+ return { success: true, count: copiedFiles.length, files: copiedFiles };
1628
+ });
1500
1629
  electron.ipcMain.on("toggle-auto-sync", (event, repoPath, enabled) => {
1501
1630
  toggleAutoSync(event.sender.id, repoPath, enabled, (msg) => {
1502
1631
  event.sender.send("auto-sync-log", msg);
@@ -40,6 +40,7 @@ const api = {
40
40
  switchAccount: (accountId) => electron.ipcRenderer.invoke("switch-account", accountId),
41
41
  removeAccount: (accountId) => electron.ipcRenderer.invoke("remove-account", accountId),
42
42
  getUserRepositories: () => electron.ipcRenderer.invoke("get-user-repositories"),
43
+ getCollaboratedRepos: () => electron.ipcRenderer.invoke("get-collaborated-repos"),
43
44
  createGitHubRepository: (name, description, isPrivate, autoInit = false, gitignoreTemplate = "", licenseTemplate = "") => electron.ipcRenderer.invoke(
44
45
  "create-github-repository",
45
46
  name,
@@ -68,6 +69,9 @@ const api = {
68
69
  setupUniversalWorkspace: (remoteUrl, branch, baseBranch, dest) => electron.ipcRenderer.invoke("setup-universal-workspace", remoteUrl, branch, baseBranch, dest),
69
70
  checkUniversalCollisions: (workspacePath, sourceFiles, dest) => electron.ipcRenderer.invoke("check-universal-collisions", workspacePath, sourceFiles, dest),
70
71
  getUniversalDiff: (workspacePath, file) => electron.ipcRenderer.invoke("get-universal-diff", workspacePath, file),
72
+ cloneRepository: (remoteUrl, destPath) => electron.ipcRenderer.invoke("clone-repository", remoteUrl, destPath),
73
+ checkPathExists: (targetPath) => electron.ipcRenderer.invoke("check-path-exists", targetPath),
74
+ uploadFilesToRepo: (repoPath) => electron.ipcRenderer.invoke("upload-files-to-repo", repoPath),
71
75
  commitUniversalWorkspace: (workspacePath, msg, branch) => electron.ipcRenderer.invoke("commit-universal-workspace", workspacePath, msg, branch),
72
76
  cleanupUniversalWorkspace: (workspacePath) => electron.ipcRenderer.invoke("cleanup-universal-workspace", workspacePath),
73
77
  toggleAutoSync: (repoPath, enabled) => electron.ipcRenderer.send("toggle-auto-sync", repoPath, enabled),