server-studio 1.2.0 → 1.2.2
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/CHANGELOG.md +16 -0
- package/bin/cli.js +8 -0
- package/package.json +1 -1
- package/src/index.html +48 -8
- package/src/server.js +6 -1
- package/src/telemetry.js +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes to Server Studio. Dates are release dates.
|
|
4
4
|
|
|
5
|
+
## 1.2.2 — 2026-08-30
|
|
6
|
+
|
|
7
|
+
- The version badge no longer keeps offering an update you have already installed. Once you start
|
|
8
|
+
one it reads **restart to finish**, because the running process keeps the old code until it is
|
|
9
|
+
reopened. It returns to a plain version once you restart.
|
|
10
|
+
|
|
11
|
+
## 1.2.1 — 2026-08-30
|
|
12
|
+
|
|
13
|
+
- **Fixed:** an installed app reported its version as `0.0.0`, because the code looks for
|
|
14
|
+
`package.json` one level up and the app bundle does not contain one. The badge showed `v0.0.0`,
|
|
15
|
+
the update prompt appeared permanently even straight after updating, and telemetry recorded every
|
|
16
|
+
install as version 0.0.0. The installer now stamps the version beside the code.
|
|
17
|
+
- **Fixed:** if the local server was not responding, clicking **Update**, **Run** or **Stop** did
|
|
18
|
+
nothing at all. The failed request threw and was never caught, so there was no error either. Those
|
|
19
|
+
actions now say what went wrong.
|
|
20
|
+
|
|
5
21
|
## 1.2.0 — 2026-08-30
|
|
6
22
|
|
|
7
23
|
### Folders replace categories
|
package/bin/cli.js
CHANGED
|
@@ -69,6 +69,14 @@ function install() {
|
|
|
69
69
|
// cpSync does not preserve the exec bit on the launcher.
|
|
70
70
|
// src/ is the single source of truth, copied into the bundle at install time.
|
|
71
71
|
fs.cpSync(SRC, path.join(APP_DEST, 'Contents', 'Resources'), { recursive: true });
|
|
72
|
+
// The bundle has no package.json, so stamp the version beside the code. Without this
|
|
73
|
+
// an installed app reports 0.0.0 and therefore thinks an update is always available.
|
|
74
|
+
try {
|
|
75
|
+
fs.writeFileSync(
|
|
76
|
+
path.join(APP_DEST, 'Contents', 'Resources', 'version.json'),
|
|
77
|
+
JSON.stringify({ version: require(path.join(ROOT, 'package.json')).version }) + '\n'
|
|
78
|
+
);
|
|
79
|
+
} catch (e) {}
|
|
72
80
|
fs.chmodSync(path.join(APP_DEST, 'Contents', 'MacOS', 'ServerStudio'), 0o755);
|
|
73
81
|
} else skip('app skipped (--no-app)');
|
|
74
82
|
|
package/package.json
CHANGED
package/src/index.html
CHANGED
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
.ver.has-update:hover { background: rgba(62,207,142,.24); }
|
|
114
114
|
.ver .dot-new { width: 6px; height: 6px; border-radius: 50%; background: var(--green); flex: none; }
|
|
115
115
|
.ver.checking { opacity: .55; }
|
|
116
|
+
.ver.pending { color: var(--amber); border-color: rgba(245,179,67,.4); background: rgba(245,179,67,.12); padding: 3px 9px; margin-left: -10px; }
|
|
116
117
|
@keyframes verPulse {
|
|
117
118
|
0%, 100% { box-shadow: 0 0 0 0 rgba(62,207,142,0); }
|
|
118
119
|
50% { box-shadow: 0 0 0 4px rgba(62,207,142,.18); }
|
|
@@ -831,6 +832,15 @@ function ssPrompt(title, opts) {
|
|
|
831
832
|
/* ---------- version + update ---------- */
|
|
832
833
|
let updateInfo = null;
|
|
833
834
|
|
|
835
|
+
function isNewerVersion(a, b) {
|
|
836
|
+
const pa = String(a).split('.').map(Number), pb = String(b).split('.').map(Number);
|
|
837
|
+
for (let i = 0; i < 3; i++) {
|
|
838
|
+
const x = pa[i] || 0, y = pb[i] || 0;
|
|
839
|
+
if (x !== y) return x > y;
|
|
840
|
+
}
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
|
|
834
844
|
async function checkUpdate() {
|
|
835
845
|
const pill = document.getElementById('verPill');
|
|
836
846
|
if (!pill) return;
|
|
@@ -840,6 +850,22 @@ async function checkUpdate() {
|
|
|
840
850
|
updateInfo = r;
|
|
841
851
|
pill.classList.remove('checking');
|
|
842
852
|
|
|
853
|
+
// An update was kicked off from here and this process is still the old one.
|
|
854
|
+
let pending = null;
|
|
855
|
+
try { pending = localStorage.getItem('ss_updating_to'); } catch (e) {}
|
|
856
|
+
if (pending && !isNewerVersion(pending, r.current)) {
|
|
857
|
+
// Already running it, so the restart happened. Clear the flag.
|
|
858
|
+
try { localStorage.removeItem('ss_updating_to'); } catch (e) {}
|
|
859
|
+
pending = null;
|
|
860
|
+
}
|
|
861
|
+
if (pending) {
|
|
862
|
+
pill.className = 'ver pending';
|
|
863
|
+
pill.textContent = 'restart to finish ' + pending;
|
|
864
|
+
pill.title = 'Version ' + pending + ' has been installed. Quit and reopen Server Studio to run it.';
|
|
865
|
+
pill.onclick = null;
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
843
869
|
if (r.newer) {
|
|
844
870
|
pill.className = 'ver has-update';
|
|
845
871
|
pill.innerHTML = '<span class="dot-new"></span>v' + esc(r.current) + ' → ' + esc(r.latest);
|
|
@@ -859,10 +885,19 @@ async function runUpdate() {
|
|
|
859
885
|
'You are on ' + updateInfo.current + '. This runs npm install -g server-studio in a terminal window.\n\nYour saved servers and folders are not touched. Restart Server Studio when it finishes.',
|
|
860
886
|
{ okText: 'Update' });
|
|
861
887
|
if (!ok) return;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
888
|
+
let r = null;
|
|
889
|
+
try {
|
|
890
|
+
r = await api('/api/run', { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
891
|
+
body: JSON.stringify({ cwd: '', command: 'npm install -g server-studio' }) });
|
|
892
|
+
} catch (e) { /* server gone: fall through to the error toast rather than doing nothing */ }
|
|
893
|
+
if (r && r.ok) {
|
|
894
|
+
// The running process keeps the old code until it restarts, so stop offering the
|
|
895
|
+
// update. Continuing to show it after a successful update reads as a failure.
|
|
896
|
+
try { localStorage.setItem('ss_updating_to', updateInfo.latest); } catch (e) {}
|
|
897
|
+
checkUpdate();
|
|
898
|
+
toast('Updating in ' + TERM_NAME + '. Restart Server Studio when it finishes.');
|
|
899
|
+
}
|
|
900
|
+
else toast('Could not start the update. Run this yourself: npm install -g server-studio', true);
|
|
866
901
|
}
|
|
867
902
|
|
|
868
903
|
function moveToFolder(serverId, folderId) {
|
|
@@ -988,8 +1023,11 @@ async function runServer(id) {
|
|
|
988
1023
|
setTimeout(() => { const el = document.getElementById('f_cwd'); if (el) el.focus(); }, 120);
|
|
989
1024
|
return;
|
|
990
1025
|
}
|
|
991
|
-
|
|
992
|
-
|
|
1026
|
+
let r = null;
|
|
1027
|
+
try {
|
|
1028
|
+
r = await api('/api/run', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cwd: s.cwd || '', command: s.command }) });
|
|
1029
|
+
} catch (e) {}
|
|
1030
|
+
if (r && r.ok) {
|
|
993
1031
|
toast('Running "' + (s.name || 'server') + '" in ' + TERM_NAME);
|
|
994
1032
|
if (portOf(s)) { s._status = 'checking'; render(); }
|
|
995
1033
|
setTimeout(() => refreshStatus(), 3500);
|
|
@@ -1000,8 +1038,10 @@ async function stopServer(id) {
|
|
|
1000
1038
|
const port = portOf(s);
|
|
1001
1039
|
if (!port) return;
|
|
1002
1040
|
s._status = 'checking'; render();
|
|
1003
|
-
|
|
1004
|
-
|
|
1041
|
+
try {
|
|
1042
|
+
await api('/api/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ port }) });
|
|
1043
|
+
toast('Stopped whatever was on port ' + port);
|
|
1044
|
+
} catch (e) { toast('Could not stop it, the app is not responding.', true); }
|
|
1005
1045
|
setTimeout(() => refreshStatus(), 700);
|
|
1006
1046
|
}
|
|
1007
1047
|
function copyUrl(url, btn) {
|
package/src/server.js
CHANGED
|
@@ -88,7 +88,12 @@ function portFromUrl(url) {
|
|
|
88
88
|
|
|
89
89
|
/* ---------- version check ---------- */
|
|
90
90
|
function appVersion() {
|
|
91
|
-
|
|
91
|
+
// Running from a checkout, package.json is one level up. Inside the installed app
|
|
92
|
+
// bundle there is no package.json, so the installer stamps version.json beside the
|
|
93
|
+
// code. Try both before giving up.
|
|
94
|
+
try { return require('./version.json').version || '0.0.0'; } catch (e) {}
|
|
95
|
+
try { return require('../package.json').version || '0.0.0'; } catch (e) {}
|
|
96
|
+
return '0.0.0';
|
|
92
97
|
}
|
|
93
98
|
function isNewer(a, b) {
|
|
94
99
|
const pa = String(a).split('.').map(Number), pb = String(b).split('.').map(Number);
|
package/src/telemetry.js
CHANGED
|
@@ -28,7 +28,12 @@ function optedOut() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function appVersion() {
|
|
31
|
-
|
|
31
|
+
// Running from a checkout, package.json is one level up. Inside the installed app
|
|
32
|
+
// bundle there is no package.json, so the installer stamps version.json beside the
|
|
33
|
+
// code. Try both before giving up.
|
|
34
|
+
try { return require('./version.json').version || '0.0.0'; } catch (e) {}
|
|
35
|
+
try { return require('../package.json').version || '0.0.0'; } catch (e) {}
|
|
36
|
+
return '0.0.0';
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
function dataDir() {
|