techeduspace 0.0.0-stage → 0.2.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/bin/techeduspace.js +212 -0
- package/package.json +24 -4
- package/README.md +0 -3
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TechEduSpace CLI Workstation Launcher
|
|
5
|
+
* Supports: npx techeduspace or npm i -g techeduspace
|
|
6
|
+
* Author: Acadure Private Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawn, execSync } = require("child_process")
|
|
10
|
+
const fs = require("fs")
|
|
11
|
+
const os = require("os")
|
|
12
|
+
const path = require("path")
|
|
13
|
+
const https = require("https")
|
|
14
|
+
|
|
15
|
+
const REPO = "acadure/acadure"
|
|
16
|
+
const RELEASE_TAG = "tes-exam-app-stable"
|
|
17
|
+
const BASE_DOWNLOAD_URL = `https://github.com/${REPO}/releases/download/${RELEASE_TAG}`
|
|
18
|
+
|
|
19
|
+
function log(msg) {
|
|
20
|
+
console.log(`\x1b[36m[TechEduSpace]\x1b[0m ${msg}`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function err(msg) {
|
|
24
|
+
console.error(`\x1b[31m[TechEduSpace Error]\x1b[0m ${msg}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findInstalledApp() {
|
|
28
|
+
const platform = os.platform()
|
|
29
|
+
if (platform === "win32") {
|
|
30
|
+
const candidates = [
|
|
31
|
+
path.join(process.env.LOCALAPPDATA || "", "Programs", "TechEduSpace", "TechEduSpace.exe"),
|
|
32
|
+
path.join(process.env.ProgramFiles || "", "TechEduSpace", "TechEduSpace.exe"),
|
|
33
|
+
path.join(process.env["ProgramFiles(x86)"] || "", "TechEduSpace", "TechEduSpace.exe"),
|
|
34
|
+
]
|
|
35
|
+
for (const c of candidates) {
|
|
36
|
+
if (fs.existsSync(c)) return c
|
|
37
|
+
}
|
|
38
|
+
} else if (platform === "darwin") {
|
|
39
|
+
const candidates = [
|
|
40
|
+
"/Applications/TechEduSpace Beta.app",
|
|
41
|
+
"/Applications/TechEduSpace.app",
|
|
42
|
+
path.join(os.homedir(), "Applications", "TechEduSpace Beta.app"),
|
|
43
|
+
path.join(os.homedir(), "Applications", "TechEduSpace.app"),
|
|
44
|
+
path.resolve(__dirname, "../../dist/mac-arm64/TechEduSpace Beta.app"),
|
|
45
|
+
path.resolve(__dirname, "../../dist/mac/TechEduSpace.app"),
|
|
46
|
+
path.resolve(process.cwd(), "agents/tes-exam-app/dist/mac-arm64/TechEduSpace Beta.app"),
|
|
47
|
+
path.resolve(process.cwd(), "dist/mac-arm64/TechEduSpace Beta.app"),
|
|
48
|
+
]
|
|
49
|
+
for (const c of candidates) {
|
|
50
|
+
if (fs.existsSync(c)) return c
|
|
51
|
+
}
|
|
52
|
+
} else if (platform === "linux") {
|
|
53
|
+
try {
|
|
54
|
+
const p = execSync("which techeduspace 2>/dev/null").toString().trim()
|
|
55
|
+
if (p && fs.existsSync(p)) return p
|
|
56
|
+
} catch {}
|
|
57
|
+
const defaultAppImage = path.join(os.homedir(), ".local", "bin", "TESExamApp.AppImage")
|
|
58
|
+
if (fs.existsSync(defaultAppImage)) return defaultAppImage
|
|
59
|
+
}
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function launchApp(appPath) {
|
|
64
|
+
const platform = os.platform()
|
|
65
|
+
log(`Launching TechEduSpace workstation...`)
|
|
66
|
+
|
|
67
|
+
let executable = appPath
|
|
68
|
+
if (platform === "darwin" && appPath.endsWith(".app")) {
|
|
69
|
+
const binName = path.basename(appPath, ".app")
|
|
70
|
+
const innerBin = path.join(appPath, "Contents", "MacOS", binName)
|
|
71
|
+
if (fs.existsSync(innerBin)) {
|
|
72
|
+
executable = innerBin
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const env = { ...process.env }
|
|
77
|
+
delete env.ELECTRON_RUN_AS_NODE
|
|
78
|
+
|
|
79
|
+
const child = spawn(executable, [], { detached: true, stdio: "ignore", env })
|
|
80
|
+
child.unref()
|
|
81
|
+
process.exit(0)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function downloadFile(url, dest) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
log(`Downloading installer from ${url}...`)
|
|
87
|
+
const file = fs.createWriteStream(dest)
|
|
88
|
+
|
|
89
|
+
function get(currentUrl) {
|
|
90
|
+
https.get(currentUrl, (response) => {
|
|
91
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
92
|
+
get(response.headers.location)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
if (response.statusCode !== 200) {
|
|
96
|
+
reject(new Error(`Failed to download: HTTP ${response.statusCode}`))
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
response.pipe(file)
|
|
100
|
+
file.on("finish", () => {
|
|
101
|
+
file.close(() => resolve(dest))
|
|
102
|
+
})
|
|
103
|
+
}).on("error", (err) => {
|
|
104
|
+
fs.unlink(dest, () => {})
|
|
105
|
+
reject(err)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
get(url)
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function main() {
|
|
113
|
+
log("Initializing TechEduSpace Examination Workstation...")
|
|
114
|
+
|
|
115
|
+
const installed = findInstalledApp()
|
|
116
|
+
if (installed) {
|
|
117
|
+
log(`Found installed workstation at: ${installed}`)
|
|
118
|
+
launchApp(installed)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const platform = os.platform()
|
|
123
|
+
if (platform === "win32") {
|
|
124
|
+
log("TechEduSpace is not installed. Initiating automated Windows install...")
|
|
125
|
+
const tempInstaller = path.join(os.tmpdir(), "TESExamApp-windows-x64.exe")
|
|
126
|
+
const url = `${BASE_DOWNLOAD_URL}/TESExamApp-windows-x64.exe`
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await downloadFile(url, tempInstaller)
|
|
130
|
+
log("Running silent installer (/S)...")
|
|
131
|
+
execSync(`"${tempInstaller}" /S`, { stdio: "inherit" })
|
|
132
|
+
fs.unlinkSync(tempInstaller)
|
|
133
|
+
|
|
134
|
+
const newlyInstalled = findInstalledApp()
|
|
135
|
+
if (newlyInstalled) {
|
|
136
|
+
log("Installation complete! Launching TechEduSpace...")
|
|
137
|
+
launchApp(newlyInstalled)
|
|
138
|
+
} else {
|
|
139
|
+
log("Installation completed. Launch 'TechEduSpace' from Start Menu.")
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
err(`Automated install failed: ${e.message}`)
|
|
143
|
+
log("You can download directly from: " + url)
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
} else if (platform === "darwin") {
|
|
147
|
+
log("TechEduSpace is not installed. Downloading macOS installer...")
|
|
148
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64"
|
|
149
|
+
const dmgName = `TESExamApp-darwin-${arch}.dmg`
|
|
150
|
+
const tempDmg = path.join(os.tmpdir(), dmgName)
|
|
151
|
+
const url = `${BASE_DOWNLOAD_URL}/${dmgName}`
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await downloadFile(url, tempDmg)
|
|
155
|
+
log("Mounting installer image...")
|
|
156
|
+
const mountPoint = path.join(os.tmpdir(), "TechEduSpaceMount")
|
|
157
|
+
try {
|
|
158
|
+
try { execSync(`hdiutil detach "${mountPoint}" -quiet 2>/dev/null`) } catch {}
|
|
159
|
+
execSync(`hdiutil attach "${tempDmg}" -nobrowse -mountpoint "${mountPoint}" -quiet`)
|
|
160
|
+
|
|
161
|
+
let foundApp = null
|
|
162
|
+
for (const name of ["TechEduSpace Beta.app", "TechEduSpace.app"]) {
|
|
163
|
+
const p = path.join(mountPoint, name)
|
|
164
|
+
if (fs.existsSync(p)) {
|
|
165
|
+
foundApp = p
|
|
166
|
+
break
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const targetDir = fs.existsSync("/Applications") ? "/Applications" : path.join(os.homedir(), "Applications")
|
|
171
|
+
const targetApp = path.join(targetDir, foundApp ? path.basename(foundApp) : "TechEduSpace Beta.app")
|
|
172
|
+
|
|
173
|
+
if (foundApp) {
|
|
174
|
+
log(`Installing TechEduSpace to ${targetApp}...`)
|
|
175
|
+
execSync(`rm -rf "${targetApp}" && cp -R "${foundApp}" "${targetApp}"`)
|
|
176
|
+
}
|
|
177
|
+
execSync(`hdiutil detach "${mountPoint}" -quiet`)
|
|
178
|
+
fs.unlinkSync(tempDmg)
|
|
179
|
+
|
|
180
|
+
log("Installation complete! Launching TechEduSpace...")
|
|
181
|
+
launchApp(targetApp)
|
|
182
|
+
} catch (mountErr) {
|
|
183
|
+
log("Opening DMG for manual install...")
|
|
184
|
+
execSync(`open "${tempDmg}"`)
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
err(`Automated download failed: ${e.message}`)
|
|
188
|
+
log("You can download directly from: https://techeduspace.com/downloads")
|
|
189
|
+
process.exit(1)
|
|
190
|
+
}
|
|
191
|
+
} else if (platform === "linux") {
|
|
192
|
+
const appImage = path.join(os.homedir(), ".local", "bin", "TESExamApp.AppImage")
|
|
193
|
+
fs.mkdirSync(path.dirname(appImage), { recursive: true })
|
|
194
|
+
const url = `${BASE_DOWNLOAD_URL}/TESExamApp-linux-x64.AppImage`
|
|
195
|
+
try {
|
|
196
|
+
await downloadFile(url, appImage)
|
|
197
|
+
fs.chmodSync(appImage, 0o755)
|
|
198
|
+
log("Launching TechEduSpace AppImage...")
|
|
199
|
+
launchApp(appImage)
|
|
200
|
+
} catch (e) {
|
|
201
|
+
err(`Download failed: ${e.message}`)
|
|
202
|
+
process.exit(1)
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
log(`Please download TechEduSpace for ${platform} from https://techeduspace.com/downloads`)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
main().catch((e) => {
|
|
210
|
+
err(e.message)
|
|
211
|
+
process.exit(1)
|
|
212
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "techeduspace",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Cross-platform CLI installer and workstation launcher for TechEduSpace",
|
|
5
|
+
"bin": {
|
|
6
|
+
"techeduspace": "./bin/techeduspace.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"techeduspace",
|
|
10
|
+
"exam",
|
|
11
|
+
"proctoring",
|
|
12
|
+
"acadure",
|
|
13
|
+
"assessment",
|
|
14
|
+
"coding-test"
|
|
15
|
+
],
|
|
16
|
+
"author": "Acadure Private Limited <contact@acadure.com>",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/acadure/acadure.git",
|
|
20
|
+
"directory": "agents/tes-exam-app/cli"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
package/README.md
DELETED