devtunnel-cli 3.1.2 → 3.1.4
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/core/RUN.js +71 -64
- package/src/core/index.js +384 -374
- package/src/core/start.js +762 -675
- package/src/utils/folder-picker.js +160 -140
|
@@ -1,140 +1,160 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
2
|
-
import { platform } from "os";
|
|
3
|
-
import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import { tmpdir } from "os";
|
|
6
|
-
|
|
7
|
-
// Cross-platform native folder picker
|
|
8
|
-
export async function selectFolder() {
|
|
9
|
-
const os = platform();
|
|
10
|
-
const tempFile = join(tmpdir(), `folder-picker-${Date.now()}.txt`);
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
if (os === "win32") {
|
|
14
|
-
// Windows - Use MODERN OpenFileDialog (like website file uploads)
|
|
15
|
-
const script = `
|
|
16
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
17
|
-
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
18
|
-
|
|
19
|
-
$dialog = New-Object System.Windows.Forms.OpenFileDialog
|
|
20
|
-
$dialog.Title = "Select your project folder"
|
|
21
|
-
$dialog.Filter = "All files (*.*)|*.*"
|
|
22
|
-
$dialog.CheckFileExists = $false
|
|
23
|
-
$dialog.CheckPathExists = $true
|
|
24
|
-
$dialog.ValidateNames = $false
|
|
25
|
-
$dialog.FileName = "Folder Selection"
|
|
26
|
-
$dialog.Multiselect = $false
|
|
27
|
-
$dialog.InitialDirectory = [Environment]::GetFolderPath("UserProfile")
|
|
28
|
-
|
|
29
|
-
$result = $dialog.ShowDialog()
|
|
30
|
-
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
|
|
31
|
-
$folderPath = Split-Path -Parent $dialog.FileName
|
|
32
|
-
if (-not $folderPath) {
|
|
33
|
-
$folderPath = $dialog.FileName
|
|
34
|
-
}
|
|
35
|
-
$folderPath | Out-File -FilePath "${tempFile.replace(/\\/g, '\\\\')}" -Encoding UTF8 -NoNewline
|
|
36
|
-
}
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
await runPowerShell(script);
|
|
40
|
-
|
|
41
|
-
} else if (os === "darwin") {
|
|
42
|
-
// macOS - Use osascript
|
|
43
|
-
const script = `
|
|
44
|
-
set folderPath to choose folder with prompt "Select your project folder"
|
|
45
|
-
set posixPath to POSIX path of folderPath
|
|
46
|
-
do shell script "echo " & quoted form of posixPath & " > '${tempFile}'"
|
|
47
|
-
`;
|
|
48
|
-
|
|
49
|
-
await runCommand("osascript", ["-e", script]);
|
|
50
|
-
|
|
51
|
-
} else {
|
|
52
|
-
// Linux - Try zenity first, then kdialog
|
|
53
|
-
try {
|
|
54
|
-
await runCommand("zenity", [
|
|
55
|
-
"--file-selection",
|
|
56
|
-
"--directory",
|
|
57
|
-
"--title=Select your project folder"
|
|
58
|
-
], tempFile);
|
|
59
|
-
} catch {
|
|
60
|
-
await runCommand("kdialog", [
|
|
61
|
-
"--getexistingdirectory",
|
|
62
|
-
process.env.HOME || "/",
|
|
63
|
-
"--title", "Select your project folder"
|
|
64
|
-
], tempFile);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Read the selected folder
|
|
69
|
-
if (existsSync(tempFile)) {
|
|
70
|
-
const folderPath = readFileSync(tempFile, "utf8").trim();
|
|
71
|
-
unlinkSync(tempFile);
|
|
72
|
-
return folderPath;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return null;
|
|
76
|
-
|
|
77
|
-
} catch (error) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
proc
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { platform } from "os";
|
|
3
|
+
import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
|
|
7
|
+
// Cross-platform native folder picker
|
|
8
|
+
export async function selectFolder() {
|
|
9
|
+
const os = platform();
|
|
10
|
+
const tempFile = join(tmpdir(), `folder-picker-${Date.now()}.txt`);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
if (os === "win32") {
|
|
14
|
+
// Windows - Use MODERN OpenFileDialog (like website file uploads)
|
|
15
|
+
const script = `
|
|
16
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
17
|
+
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
18
|
+
|
|
19
|
+
$dialog = New-Object System.Windows.Forms.OpenFileDialog
|
|
20
|
+
$dialog.Title = "Select your project folder"
|
|
21
|
+
$dialog.Filter = "All files (*.*)|*.*"
|
|
22
|
+
$dialog.CheckFileExists = $false
|
|
23
|
+
$dialog.CheckPathExists = $true
|
|
24
|
+
$dialog.ValidateNames = $false
|
|
25
|
+
$dialog.FileName = "Folder Selection"
|
|
26
|
+
$dialog.Multiselect = $false
|
|
27
|
+
$dialog.InitialDirectory = [Environment]::GetFolderPath("UserProfile")
|
|
28
|
+
|
|
29
|
+
$result = $dialog.ShowDialog()
|
|
30
|
+
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
|
|
31
|
+
$folderPath = Split-Path -Parent $dialog.FileName
|
|
32
|
+
if (-not $folderPath) {
|
|
33
|
+
$folderPath = $dialog.FileName
|
|
34
|
+
}
|
|
35
|
+
$folderPath | Out-File -FilePath "${tempFile.replace(/\\/g, '\\\\')}" -Encoding UTF8 -NoNewline
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
await runPowerShell(script);
|
|
40
|
+
|
|
41
|
+
} else if (os === "darwin") {
|
|
42
|
+
// macOS - Use osascript
|
|
43
|
+
const script = `
|
|
44
|
+
set folderPath to choose folder with prompt "Select your project folder"
|
|
45
|
+
set posixPath to POSIX path of folderPath
|
|
46
|
+
do shell script "echo " & quoted form of posixPath & " > '${tempFile}'"
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
await runCommand("osascript", ["-e", script]);
|
|
50
|
+
|
|
51
|
+
} else {
|
|
52
|
+
// Linux - Try zenity first, then kdialog
|
|
53
|
+
try {
|
|
54
|
+
await runCommand("zenity", [
|
|
55
|
+
"--file-selection",
|
|
56
|
+
"--directory",
|
|
57
|
+
"--title=Select your project folder"
|
|
58
|
+
], tempFile);
|
|
59
|
+
} catch {
|
|
60
|
+
await runCommand("kdialog", [
|
|
61
|
+
"--getexistingdirectory",
|
|
62
|
+
process.env.HOME || "/",
|
|
63
|
+
"--title", "Select your project folder"
|
|
64
|
+
], tempFile);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Read the selected folder
|
|
69
|
+
if (existsSync(tempFile)) {
|
|
70
|
+
const folderPath = readFileSync(tempFile, "utf8").trim();
|
|
71
|
+
unlinkSync(tempFile);
|
|
72
|
+
return folderPath;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return null;
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const msg = error && error.message ? error.message : String(error);
|
|
79
|
+
if (os === "win32" && (msg.includes("ENOENT") || msg.includes("spawn powershell"))) {
|
|
80
|
+
console.error("Folder picker: PowerShell not found. Run from a path or pass the project path as an argument.");
|
|
81
|
+
} else if (os !== "win32" && os !== "darwin" && (msg.includes("ENOENT") || msg.includes("spawn"))) {
|
|
82
|
+
console.error("Folder picker: zenity or kdialog not found. Install one (e.g. apt install zenity) or pass the project path as an argument.");
|
|
83
|
+
} else {
|
|
84
|
+
console.error("Folder picker error:", msg);
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Run PowerShell command
|
|
91
|
+
function runPowerShell(script) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const proc = spawn("powershell", [
|
|
94
|
+
"-NoProfile",
|
|
95
|
+
"-NonInteractive",
|
|
96
|
+
"-ExecutionPolicy", "Bypass",
|
|
97
|
+
"-Command", script
|
|
98
|
+
], {
|
|
99
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
+
shell: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
let stderr = "";
|
|
104
|
+
proc.stderr?.on("data", (data) => stderr += data.toString());
|
|
105
|
+
|
|
106
|
+
proc.on("close", (code, signal) => {
|
|
107
|
+
if (code === 0) {
|
|
108
|
+
resolve();
|
|
109
|
+
} else {
|
|
110
|
+
reject(new Error(stderr || `PowerShell exited with code ${code ?? signal ?? 1}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
proc.on("error", (err) => {
|
|
115
|
+
if (err.code === "ENOENT") {
|
|
116
|
+
reject(new Error("PowerShell not found. Install it or use a different method to select the folder."));
|
|
117
|
+
} else {
|
|
118
|
+
reject(err);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Run generic command
|
|
125
|
+
function runCommand(command, args, outputFile) {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const proc = spawn(command, args, {
|
|
128
|
+
stdio: outputFile ? ["ignore", "pipe", "pipe"] : "pipe",
|
|
129
|
+
shell: true
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
let stdout = "";
|
|
133
|
+
|
|
134
|
+
if (outputFile) {
|
|
135
|
+
proc.stdout?.on("data", (data) => {
|
|
136
|
+
stdout += data.toString();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
proc.on("close", (code, signal) => {
|
|
141
|
+
const exitCode = code ?? (signal ? 1 : 0);
|
|
142
|
+
if (exitCode === 0) {
|
|
143
|
+
if (outputFile && stdout) {
|
|
144
|
+
try { writeFileSync(outputFile, stdout.trim()); } catch (_) {}
|
|
145
|
+
}
|
|
146
|
+
resolve();
|
|
147
|
+
} else {
|
|
148
|
+
reject(new Error(`Command failed with code ${exitCode}`));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
proc.on("error", (err) => {
|
|
153
|
+
if (err.code === "ENOENT") {
|
|
154
|
+
reject(new Error(`Command not found: ${command}`));
|
|
155
|
+
} else {
|
|
156
|
+
reject(err);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|