gitshift 2.0.0 → 2.1.1
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/package.json +1 -1
- package/src/commands/add.js +23 -22
- package/src/commands/auto.js +39 -0
- package/src/commands/current.js +7 -27
- package/src/commands/doctor.js +6 -28
- package/src/commands/link.js +136 -0
- package/src/commands/links.js +18 -0
- package/src/commands/remove.js +6 -24
- package/src/commands/scan.js +13 -24
- package/src/commands/unlink.js +9 -0
- package/src/commands/use.js +11 -41
- package/src/server.js +36 -4
- package/src/services/git.js +1 -4
- package/src/services/profile.js +34 -8
- package/src/services/scan.js +1 -3
- package/src/services/ssh.js +7 -23
- package/src/utils/logger.js +3 -6
package/package.json
CHANGED
package/src/commands/add.js
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
confirm,
|
|
3
|
-
input,
|
|
4
|
-
} from "@inquirer/prompts";
|
|
5
|
-
|
|
1
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
6
2
|
import ora from "ora";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
saveProfile,
|
|
11
|
-
} from "../services/profile.js";
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
generateSSHKey,
|
|
15
|
-
} from "../services/ssh.js";
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
error,
|
|
19
|
-
success,
|
|
20
|
-
} from "../utils/logger.js";
|
|
3
|
+
import { getProfile, saveProfile } from "../services/profile.js";
|
|
4
|
+
import { generateSSHKey } from "../services/ssh.js";
|
|
5
|
+
import { error, success } from "../utils/logger.js";
|
|
21
6
|
|
|
22
7
|
export async function addCommand() {
|
|
23
8
|
try {
|
|
@@ -25,6 +10,12 @@ export async function addCommand() {
|
|
|
25
10
|
message: "Profile Name",
|
|
26
11
|
});
|
|
27
12
|
|
|
13
|
+
if (!name.trim()) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Profile name is required"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
28
19
|
if (getProfile(name)) {
|
|
29
20
|
error(`Profile "${name}" already exists`);
|
|
30
21
|
|
|
@@ -36,10 +27,22 @@ export async function addCommand() {
|
|
|
36
27
|
message: "GitHub Username",
|
|
37
28
|
});
|
|
38
29
|
|
|
30
|
+
if (!username.trim()) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"GitHub username is required"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
39
36
|
const email = await input({
|
|
40
37
|
message: "Email",
|
|
41
38
|
});
|
|
42
39
|
|
|
40
|
+
if (!email.trim()) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
"Email is required"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
43
46
|
const createSSH = await confirm({
|
|
44
47
|
message: "Generate SSH key automatically?",
|
|
45
48
|
default: true,
|
|
@@ -80,9 +83,7 @@ export async function addCommand() {
|
|
|
80
83
|
sshKey,
|
|
81
84
|
});
|
|
82
85
|
|
|
83
|
-
success(
|
|
84
|
-
`Profile "${name}" saved locally`
|
|
85
|
-
);
|
|
86
|
+
success(`Profile "${name}" saved locally`);
|
|
86
87
|
} catch (err) {
|
|
87
88
|
if (err && err.name === "ExitPromptError") {
|
|
88
89
|
// User canceled the prompt (Ctrl+C / SIGINT). Exit gracefully.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import { setGitUser } from "../services/git.js";
|
|
3
|
+
import { getFolderMappings, getProfile, setCurrentProfile } from "../services/profile.js";
|
|
4
|
+
import { success } from "../utils/logger.js";
|
|
5
|
+
|
|
6
|
+
export async function autoCommand() {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
|
|
9
|
+
const mappings = getFolderMappings();
|
|
10
|
+
|
|
11
|
+
const matched = mappings
|
|
12
|
+
.sort(
|
|
13
|
+
(a, b) =>
|
|
14
|
+
b.path.length -
|
|
15
|
+
a.path.length
|
|
16
|
+
)
|
|
17
|
+
.find((mapping) =>
|
|
18
|
+
cwd.startsWith(
|
|
19
|
+
mapping.path
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (!matched) {
|
|
24
|
+
console.log("\nNo matching profile\n");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const profile = getProfile(matched.profile);
|
|
29
|
+
|
|
30
|
+
if (!profile) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await setGitUser(profile.username, profile.email);
|
|
35
|
+
|
|
36
|
+
setCurrentProfile(profile.name);
|
|
37
|
+
|
|
38
|
+
success(`Switched to ${profile.name}`);
|
|
39
|
+
}
|
package/src/commands/current.js
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getCurrentProfile,
|
|
3
|
-
getProfile,
|
|
4
|
-
} from "../services/profile.js";
|
|
5
|
-
|
|
1
|
+
import { getCurrentProfile, getProfile, } from "../services/profile.js";
|
|
6
2
|
import { info } from "../utils/logger.js";
|
|
7
3
|
|
|
8
4
|
export async function currentCommand() {
|
|
9
|
-
const current =
|
|
10
|
-
getCurrentProfile();
|
|
5
|
+
const current = getCurrentProfile();
|
|
11
6
|
|
|
12
7
|
if (!current) {
|
|
13
8
|
info("No active profile");
|
|
14
9
|
return;
|
|
15
10
|
}
|
|
16
11
|
|
|
17
|
-
const profile =
|
|
18
|
-
getProfile(current);
|
|
12
|
+
const profile = getProfile(current);
|
|
19
13
|
|
|
20
14
|
if (!profile) {
|
|
21
15
|
info("Profile not found");
|
|
@@ -23,23 +17,9 @@ export async function currentCommand() {
|
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
console.log();
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
console.log(
|
|
32
|
-
`Username: ${profile.username}`
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
console.log(
|
|
36
|
-
`Email : ${profile.email}`
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
40
|
-
`SSH Key : ${profile.sshKey || "Not configured"
|
|
41
|
-
}`
|
|
42
|
-
);
|
|
43
|
-
|
|
20
|
+
console.log(`Profile : ${profile.name}`);
|
|
21
|
+
console.log(`Username: ${profile.username}`);
|
|
22
|
+
console.log(`Email : ${profile.email}`);
|
|
23
|
+
console.log(`SSH Key : ${profile.sshKey || "Not configured"}`);
|
|
44
24
|
console.log();
|
|
45
25
|
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
import { execa } from "execa";
|
|
2
|
+
import { error, success } from "../utils/logger.js";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
error,
|
|
5
|
-
success,
|
|
6
|
-
} from "../utils/logger.js";
|
|
7
|
-
|
|
8
|
-
async function check(
|
|
9
|
-
name,
|
|
10
|
-
command,
|
|
11
|
-
args = []
|
|
12
|
-
) {
|
|
4
|
+
async function check(name, command, args = []) {
|
|
13
5
|
try {
|
|
14
6
|
await execa(command, args);
|
|
15
|
-
|
|
16
7
|
success(`${name} installed`);
|
|
17
|
-
|
|
18
8
|
return true;
|
|
19
9
|
} catch {
|
|
20
10
|
error(`${name} missing`);
|
|
21
|
-
|
|
22
11
|
return false;
|
|
23
12
|
}
|
|
24
13
|
}
|
|
@@ -26,25 +15,14 @@ async function check(
|
|
|
26
15
|
export async function doctorCommand() {
|
|
27
16
|
console.log();
|
|
28
17
|
|
|
29
|
-
await check("Git", "git", [
|
|
30
|
-
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
await check("SSH", "ssh", [
|
|
34
|
-
"-V",
|
|
35
|
-
]);
|
|
18
|
+
await check("Git", "git", ["--version",]);
|
|
19
|
+
await check("SSH", "ssh", ["-V",]);
|
|
36
20
|
|
|
37
|
-
const ghInstalled = await check(
|
|
38
|
-
"GitHub CLI",
|
|
39
|
-
"gh",
|
|
40
|
-
["--version"]
|
|
41
|
-
);
|
|
21
|
+
const ghInstalled = await check("GitHub CLI", "gh", ["--version"]);
|
|
42
22
|
|
|
43
23
|
if (!ghInstalled) {
|
|
44
24
|
console.log();
|
|
45
|
-
console.log(
|
|
46
|
-
"Hint: Install GitHub CLI (macOS): `brew install gh`"
|
|
47
|
-
);
|
|
25
|
+
console.log("Hint: Install GitHub CLI (macOS): `brew install gh`");
|
|
48
26
|
console.log("Then authenticate with: `gh auth login`");
|
|
49
27
|
}
|
|
50
28
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { confirm, input, select } from "@inquirer/prompts";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { addFolderMapping, getProfile, getProfiles, saveProfile } from "../services/profile.js";
|
|
6
|
+
import { generateSSHKey } from "../services/ssh.js";
|
|
7
|
+
import { error, success } from "../utils/logger.js";
|
|
8
|
+
|
|
9
|
+
export async function linkCommand(folder) {
|
|
10
|
+
|
|
11
|
+
const fullPath = path.resolve(folder);
|
|
12
|
+
const exists = await fs.pathExists(fullPath);
|
|
13
|
+
|
|
14
|
+
if (!exists) {
|
|
15
|
+
error("Folder does not exist");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let profiles = getProfiles();
|
|
20
|
+
let selectedProfile;
|
|
21
|
+
|
|
22
|
+
if (profiles.length === 0) {
|
|
23
|
+
console.log("\nNo profiles found.\n");
|
|
24
|
+
selectedProfile = await createProfile();
|
|
25
|
+
} else {
|
|
26
|
+
const choice = await select({
|
|
27
|
+
message: "Select Profile",
|
|
28
|
+
choices: [
|
|
29
|
+
...profiles.map(
|
|
30
|
+
(profile) => ({
|
|
31
|
+
name: `${profile.name} (${profile.username})`,
|
|
32
|
+
value:
|
|
33
|
+
profile.name,
|
|
34
|
+
})
|
|
35
|
+
),
|
|
36
|
+
{
|
|
37
|
+
name: "+ Create New Profile",
|
|
38
|
+
value: "__create__",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (choice === "__create__") {
|
|
44
|
+
selectedProfile = await createProfile();
|
|
45
|
+
} else {
|
|
46
|
+
selectedProfile = choice;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
addFolderMapping(selectedProfile, fullPath);
|
|
52
|
+
success(`Linked ${fullPath}`);
|
|
53
|
+
success(`Profile: ${selectedProfile}`);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
error(err.message);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function createProfile() {
|
|
60
|
+
const name = await input({
|
|
61
|
+
message: "Profile Name",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!name.trim()) {
|
|
65
|
+
throw new Error("Profile name is required");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (getProfile(name)) {
|
|
69
|
+
error(`Profile "${name}" already exists`);
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const username =
|
|
75
|
+
await input({
|
|
76
|
+
message: "GitHub Username",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!username.trim()) {
|
|
80
|
+
throw new Error("GitHub username is required");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const email =
|
|
84
|
+
await input({
|
|
85
|
+
message: "Email",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!email.trim()) {
|
|
89
|
+
throw new Error("Email is required");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const shouldCreateSSH =
|
|
93
|
+
await confirm({
|
|
94
|
+
message: "Generate SSH Key?",
|
|
95
|
+
default: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let sshKey = null;
|
|
99
|
+
|
|
100
|
+
if (shouldCreateSSH) {
|
|
101
|
+
const spinner = ora("Generating SSH key...").start();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
sshKey = await generateSSHKey(name, email);
|
|
105
|
+
spinner.succeed("SSH key generated");
|
|
106
|
+
} catch (err) {
|
|
107
|
+
spinner.fail("Unable to generate SSH key");
|
|
108
|
+
|
|
109
|
+
error("Could not create SSH key. Ensure OpenSSH is installed and available.");
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
err &&
|
|
113
|
+
typeof err === "object" &&
|
|
114
|
+
"shortMessage" in err &&
|
|
115
|
+
err.shortMessage
|
|
116
|
+
) {
|
|
117
|
+
error(String(err.shortMessage));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
saveProfile({
|
|
126
|
+
name,
|
|
127
|
+
username,
|
|
128
|
+
email,
|
|
129
|
+
sshKey,
|
|
130
|
+
source: "manual",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
success(`Profile "${name}" created`);
|
|
134
|
+
|
|
135
|
+
return name;
|
|
136
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getFolderMappings } from "../services/profile.js";
|
|
2
|
+
|
|
3
|
+
export async function linksCommand() {
|
|
4
|
+
const mappings = getFolderMappings();
|
|
5
|
+
|
|
6
|
+
if (!mappings.length) {
|
|
7
|
+
console.log("\nNo folder mappings\n");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
console.log();
|
|
12
|
+
|
|
13
|
+
mappings.forEach((mapping) => {
|
|
14
|
+
console.log(`${mapping.profile} → ${mapping.path}`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
console.log();
|
|
18
|
+
}
|
package/src/commands/remove.js
CHANGED
|
@@ -1,37 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
getProfile,
|
|
4
|
-
removeProfile,
|
|
5
|
-
setCurrentProfile,
|
|
6
|
-
} from "../services/profile.js";
|
|
1
|
+
import { getCurrentProfile, getProfile, removeProfile, setCurrentProfile } from "../services/profile.js";
|
|
2
|
+
import { error, success } from "../utils/logger.js";
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
success,
|
|
11
|
-
} from "../utils/logger.js";
|
|
12
|
-
|
|
13
|
-
export async function removeCommand(
|
|
14
|
-
profileName
|
|
15
|
-
) {
|
|
16
|
-
const profile =
|
|
17
|
-
getProfile(profileName);
|
|
4
|
+
export async function removeCommand(profileName) {
|
|
5
|
+
const profile = getProfile(profileName);
|
|
18
6
|
|
|
19
7
|
if (!profile) {
|
|
20
8
|
error("Profile not found");
|
|
21
|
-
|
|
22
9
|
return;
|
|
23
10
|
}
|
|
24
11
|
|
|
25
12
|
removeProfile(profileName);
|
|
26
13
|
|
|
27
|
-
if (
|
|
28
|
-
getCurrentProfile() ===
|
|
29
|
-
profileName
|
|
30
|
-
) {
|
|
14
|
+
if (getCurrentProfile() === profileName) {
|
|
31
15
|
setCurrentProfile(null);
|
|
32
16
|
}
|
|
33
17
|
|
|
34
|
-
success(
|
|
35
|
-
`Profile "${profileName}" removed`
|
|
36
|
-
);
|
|
18
|
+
success(`Profile "${profileName}" removed`);
|
|
37
19
|
}
|
package/src/commands/scan.js
CHANGED
|
@@ -1,24 +1,8 @@
|
|
|
1
|
+
import { input, select } from "@inquirer/prompts";
|
|
1
2
|
import path from "path";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
select,
|
|
6
|
-
} from "@inquirer/prompts";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
findSSHKeys,
|
|
10
|
-
} from "../services/scan.js";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
getProfiles,
|
|
14
|
-
saveProfile,
|
|
15
|
-
} from "../services/profile.js";
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
error,
|
|
19
|
-
info,
|
|
20
|
-
success,
|
|
21
|
-
} from "../utils/logger.js";
|
|
3
|
+
import { getProfiles, saveProfile } from "../services/profile.js";
|
|
4
|
+
import { findSSHKeys } from "../services/scan.js";
|
|
5
|
+
import { error, info, success } from "../utils/logger.js";
|
|
22
6
|
|
|
23
7
|
export async function scanCommand() {
|
|
24
8
|
try {
|
|
@@ -30,9 +14,7 @@ export async function scanCommand() {
|
|
|
30
14
|
}
|
|
31
15
|
|
|
32
16
|
const existing = getProfiles();
|
|
33
|
-
|
|
34
17
|
const importedPaths = existing.map((profile) => profile.sshKey);
|
|
35
|
-
|
|
36
18
|
const available = keys.filter((key) => !importedPaths.includes(key));
|
|
37
19
|
|
|
38
20
|
if (!available.length) {
|
|
@@ -57,18 +39,25 @@ export async function scanCommand() {
|
|
|
57
39
|
message: "GitHub Username",
|
|
58
40
|
});
|
|
59
41
|
|
|
42
|
+
if (!username.trim()) {
|
|
43
|
+
throw new Error("GitHub username is required");
|
|
44
|
+
}
|
|
45
|
+
|
|
60
46
|
const email = await input({
|
|
61
47
|
message: "Email",
|
|
62
48
|
});
|
|
63
49
|
|
|
50
|
+
if (!email.trim()) {
|
|
51
|
+
throw new Error("Email is required");
|
|
52
|
+
}
|
|
53
|
+
|
|
64
54
|
try {
|
|
65
55
|
saveProfile({
|
|
66
56
|
name: profileName,
|
|
67
57
|
username,
|
|
68
58
|
email,
|
|
69
59
|
sshKey: selected,
|
|
70
|
-
source:
|
|
71
|
-
"imported",
|
|
60
|
+
source: "imported",
|
|
72
61
|
});
|
|
73
62
|
|
|
74
63
|
success(`Existing SSH Key Imported "${profileName}"`);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { removeFolderMapping } from "../services/profile.js";
|
|
3
|
+
import { success } from "../utils/logger.js";
|
|
4
|
+
|
|
5
|
+
export async function unlinkCommand(folder) {
|
|
6
|
+
const fullPath = path.resolve(folder);
|
|
7
|
+
removeFolderMapping(fullPath);
|
|
8
|
+
success(`Removed ${fullPath}`);
|
|
9
|
+
}
|
package/src/commands/use.js
CHANGED
|
@@ -1,57 +1,27 @@
|
|
|
1
1
|
import ora from "ora";
|
|
2
|
+
import { setGitUser } from "../services/git.js";
|
|
3
|
+
import { getProfile, setCurrentProfile } from "../services/profile.js";
|
|
4
|
+
import { error, success } from "../utils/logger.js";
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
getProfile
|
|
5
|
-
setCurrentProfile,
|
|
6
|
-
} from "../services/profile.js";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
setGitUser,
|
|
10
|
-
} from "../services/git.js";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
error,
|
|
14
|
-
success,
|
|
15
|
-
} from "../utils/logger.js";
|
|
16
|
-
|
|
17
|
-
export async function useCommand(
|
|
18
|
-
profileName
|
|
19
|
-
) {
|
|
20
|
-
const profile =
|
|
21
|
-
getProfile(profileName);
|
|
6
|
+
export async function useCommand(profileName) {
|
|
7
|
+
const profile = getProfile(profileName);
|
|
22
8
|
|
|
23
9
|
if (!profile) {
|
|
24
|
-
error(
|
|
25
|
-
`Profile "${profileName}" not found`
|
|
26
|
-
);
|
|
27
|
-
|
|
10
|
+
error(`Profile "${profileName}" not found`);
|
|
28
11
|
return;
|
|
29
12
|
}
|
|
30
13
|
|
|
31
|
-
const spinner = ora(
|
|
32
|
-
"Switching profile..."
|
|
33
|
-
).start();
|
|
14
|
+
const spinner = ora("Switching profile...").start();
|
|
34
15
|
|
|
35
16
|
try {
|
|
36
|
-
await setGitUser(
|
|
37
|
-
profile.username,
|
|
38
|
-
profile.email
|
|
39
|
-
);
|
|
40
|
-
|
|
17
|
+
await setGitUser(profile.username, profile.email);
|
|
41
18
|
setCurrentProfile(profile.name);
|
|
42
19
|
|
|
43
|
-
spinner.succeed(
|
|
44
|
-
|
|
45
|
-
);
|
|
20
|
+
spinner.succeed("Profile switched");
|
|
21
|
+
success(`Current profile: ${profile.name}`);
|
|
46
22
|
|
|
47
|
-
success(
|
|
48
|
-
`Current profile: ${profile.name}`
|
|
49
|
-
);
|
|
50
23
|
} catch (err) {
|
|
51
|
-
spinner.fail(
|
|
52
|
-
"Unable to switch profile"
|
|
53
|
-
);
|
|
54
|
-
|
|
24
|
+
spinner.fail("Unable to switch profile");
|
|
55
25
|
error(err.message);
|
|
56
26
|
}
|
|
57
27
|
}
|
package/src/server.js
CHANGED
|
@@ -5,11 +5,15 @@ import chalk from "chalk";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { addCommand } from "./commands/add.js";
|
|
8
|
+
import { autoCommand } from "./commands/auto.js";
|
|
8
9
|
import { currentCommand } from "./commands/current.js";
|
|
9
10
|
import { doctorCommand } from "./commands/doctor.js";
|
|
11
|
+
import { linkCommand } from "./commands/link.js";
|
|
12
|
+
import { linksCommand } from "./commands/links.js";
|
|
10
13
|
import { listCommand } from "./commands/list.js";
|
|
11
14
|
import { removeCommand } from "./commands/remove.js";
|
|
12
15
|
import { scanCommand } from "./commands/scan.js";
|
|
16
|
+
import { unlinkCommand } from "./commands/unlink.js";
|
|
13
17
|
import { useCommand } from "./commands/use.js";
|
|
14
18
|
|
|
15
19
|
const require = createRequire(import.meta.url);
|
|
@@ -58,9 +62,7 @@ async function checkForUpdates() {
|
|
|
58
62
|
const latestVersion = response.data.version;
|
|
59
63
|
|
|
60
64
|
if (latestVersion && compareVersions(version, latestVersion) < 0) {
|
|
61
|
-
console.log(
|
|
62
|
-
`A new version of ${name} is available: ${version} -> ${latestVersion}`,
|
|
63
|
-
);
|
|
65
|
+
console.log(`A new version of ${name} is available: ${version} -> ${latestVersion}`);
|
|
64
66
|
console.log(`Run: npm install -g ${name}`);
|
|
65
67
|
}
|
|
66
68
|
} catch (error) {
|
|
@@ -113,7 +115,7 @@ async function main() {
|
|
|
113
115
|
program
|
|
114
116
|
.command("remove <profile>")
|
|
115
117
|
.description(
|
|
116
|
-
"
|
|
118
|
+
"Remove profile"
|
|
117
119
|
)
|
|
118
120
|
.action(removeCommand);
|
|
119
121
|
|
|
@@ -124,6 +126,36 @@ async function main() {
|
|
|
124
126
|
)
|
|
125
127
|
.action(scanCommand);
|
|
126
128
|
|
|
129
|
+
program
|
|
130
|
+
.command(
|
|
131
|
+
"link <folder>"
|
|
132
|
+
)
|
|
133
|
+
.description(
|
|
134
|
+
"Link folder to profile"
|
|
135
|
+
)
|
|
136
|
+
.action(linkCommand);
|
|
137
|
+
|
|
138
|
+
program
|
|
139
|
+
.command("unlink <folder>")
|
|
140
|
+
.description(
|
|
141
|
+
"Remove folder mapping"
|
|
142
|
+
)
|
|
143
|
+
.action(unlinkCommand);
|
|
144
|
+
|
|
145
|
+
program
|
|
146
|
+
.command("links")
|
|
147
|
+
.description(
|
|
148
|
+
"List folder mappings"
|
|
149
|
+
)
|
|
150
|
+
.action(linksCommand);
|
|
151
|
+
|
|
152
|
+
program
|
|
153
|
+
.command("auto")
|
|
154
|
+
.description(
|
|
155
|
+
"Auto switch profile"
|
|
156
|
+
)
|
|
157
|
+
.action(autoCommand);
|
|
158
|
+
|
|
127
159
|
program
|
|
128
160
|
.command("doctor")
|
|
129
161
|
.description(
|
package/src/services/git.js
CHANGED
package/src/services/profile.js
CHANGED
|
@@ -9,20 +9,27 @@ export function getProfiles() {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function saveProfile(profile) {
|
|
12
|
-
const profiles = getProfiles();
|
|
13
12
|
|
|
13
|
+
if (
|
|
14
|
+
!profile.name?.trim() ||
|
|
15
|
+
!profile.username?.trim() ||
|
|
16
|
+
!profile.email?.trim()
|
|
17
|
+
) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"Invalid profile data"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const profiles = getProfiles();
|
|
14
24
|
const exists = profiles.find(
|
|
15
25
|
(item) => item.name === profile.name
|
|
16
26
|
);
|
|
17
27
|
|
|
18
28
|
if (exists) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
`Profile "${profile.name}" already exists`
|
|
21
|
-
);
|
|
29
|
+
throw new Error(`Profile "${profile.name}" already exists`);
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
profiles.push(profile);
|
|
25
|
-
|
|
26
33
|
config.set("profiles", profiles);
|
|
27
34
|
}
|
|
28
35
|
|
|
@@ -44,9 +51,7 @@ export function removeProfile(name) {
|
|
|
44
51
|
config.set("profiles", filtered);
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
export function setCurrentProfile(
|
|
48
|
-
name
|
|
49
|
-
) {
|
|
54
|
+
export function setCurrentProfile(name) {
|
|
50
55
|
if (!name) {
|
|
51
56
|
config.delete("current");
|
|
52
57
|
return;
|
|
@@ -57,4 +62,25 @@ export function setCurrentProfile(
|
|
|
57
62
|
|
|
58
63
|
export function getCurrentProfile() {
|
|
59
64
|
return config.get("current", null);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getFolderMappings() {
|
|
68
|
+
return config.get("folderMappings", []);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function addFolderMapping(profile, folderPath) {
|
|
72
|
+
const mappings = getFolderMappings();
|
|
73
|
+
const exists = mappings.find((item) => item.path === folderPath);
|
|
74
|
+
|
|
75
|
+
if (exists) {
|
|
76
|
+
throw new Error("Folder already mapped");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
mappings.push({ profile, path: folderPath, });
|
|
80
|
+
config.set("folderMappings", mappings);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function removeFolderMapping(folderPath) {
|
|
84
|
+
const mappings = getFolderMappings();
|
|
85
|
+
config.set("folderMappings", mappings.filter((item) => item.path !== folderPath));
|
|
60
86
|
}
|
package/src/services/scan.js
CHANGED
package/src/services/ssh.js
CHANGED
|
@@ -13,32 +13,21 @@ function toSafeKeyName(profileName) {
|
|
|
13
13
|
return normalized || "profile";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export async function generateSSHKey(
|
|
17
|
-
profileName
|
|
18
|
-
email
|
|
19
|
-
) {
|
|
20
|
-
const safeProfileName = toSafeKeyName(
|
|
21
|
-
profileName
|
|
22
|
-
);
|
|
23
|
-
|
|
16
|
+
export async function generateSSHKey(profileName, email) {
|
|
17
|
+
const safeProfileName = toSafeKeyName(profileName);
|
|
24
18
|
const keyPath = path.join(
|
|
25
19
|
os.homedir(),
|
|
26
20
|
".ssh",
|
|
27
21
|
`gitshift-${safeProfileName}`
|
|
28
22
|
);
|
|
29
23
|
|
|
30
|
-
const exists = await fs.pathExists(
|
|
31
|
-
keyPath
|
|
32
|
-
);
|
|
24
|
+
const exists = await fs.pathExists(keyPath);
|
|
33
25
|
|
|
34
26
|
if (exists) {
|
|
35
27
|
return keyPath;
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
await fs.ensureDir(
|
|
39
|
-
path.dirname(keyPath)
|
|
40
|
-
);
|
|
41
|
-
|
|
30
|
+
await fs.ensureDir(path.dirname(keyPath));
|
|
42
31
|
await execa("ssh-keygen", [
|
|
43
32
|
"-t",
|
|
44
33
|
"ed25519",
|
|
@@ -53,14 +42,9 @@ export async function generateSSHKey(
|
|
|
53
42
|
return keyPath;
|
|
54
43
|
}
|
|
55
44
|
|
|
56
|
-
export async function getPublicKey(
|
|
57
|
-
privateKeyPath
|
|
58
|
-
)
|
|
59
|
-
const publicKey =
|
|
60
|
-
`${privateKeyPath}.pub`;
|
|
61
|
-
|
|
62
|
-
const exists =
|
|
63
|
-
await fs.pathExists(publicKey);
|
|
45
|
+
export async function getPublicKey(privateKeyPath) {
|
|
46
|
+
const publicKey = `${privateKeyPath}.pub`;
|
|
47
|
+
const exists = await fs.pathExists(publicKey);
|
|
64
48
|
|
|
65
49
|
if (!exists) {
|
|
66
50
|
return null;
|
package/src/utils/logger.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
|
|
3
|
-
export const success = (msg) =>
|
|
4
|
-
console.log(chalk.green(`✓ ${msg}`));
|
|
3
|
+
export const success = (msg) => console.log(chalk.green(`✓ ${msg}`));
|
|
5
4
|
|
|
6
|
-
export const error = (msg) =>
|
|
7
|
-
console.log(chalk.red(`✗ ${msg}`));
|
|
5
|
+
export const error = (msg) => console.log(chalk.red(`✗ ${msg}`));
|
|
8
6
|
|
|
9
|
-
export const info = (msg) =>
|
|
10
|
-
console.log(chalk.cyan(msg));
|
|
7
|
+
export const info = (msg) => console.log(chalk.cyan(msg));
|