Skip to content

Commit 429f588

Browse files
Maselkovclaude
andauthored
Connect a computer: pick the scripts on the page, install starts them (#196)
The Status dialog's Connect a computer has a box per script; the command names the ticked ones, so gcm install asks nothing. Install no longer asks whether to start the scripts on boot (always yes) and reboots 3 seconds after it's done, except when the server check failed or a value was left blank, so the explanation stays on screen. Disabled range buttons now look disabled. Co-authored-by: Maselkov <25772111+Maselkov@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 4e0b55f commit 429f588

11 files changed

Lines changed: 205 additions & 42 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ The full list, including the OpenComputers config settings, is under
8484
there until its script reports). Check the address in the command is
8585
one the Minecraft server can reach (**Use a different address** changes
8686
it), then paste the command into that computer (each one, if you use
87-
several). It asks which scripts to install, checks that the computer
88-
can reach the server, and can start the scripts on every boot. If it can't reach it, see
87+
several), after ticking the scripts that computer should run. It
88+
installs them, checks that the computer can reach the server, starts
89+
them on every boot and reboots. If it can't reach it, see
8990
[Scripts can't reach the server](#scripts-cant-reach-the-server).
9091

9192
The page shows "waiting for data" until the scripts report. For settings,

‎docs/CONTEXT.md‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,10 +1644,18 @@ nothing else. Separate from craft_monitor on purpose: an update must
16441644
still work when the script it would fix has crashed.
16451645

16461646
Connecting a computer: the Status dialog's "Connect a computer" builds
1647-
one line, `wget ... gcm.lua && /tmp/gcm.lua install --server=<url>
1648-
--api-key=<key>` (OpenOS's shell chains with `&&`; checked in game), from
1649-
`/api/admin/connect` (the API key and `PUBLIC_URL`; the page's own origin
1650-
without it). The key is masked on screen but copied in full - the
1647+
one line, `wget ... gcm.lua && /tmp/gcm.lua install <scripts>
1648+
--server=<url> --api-key=<key>` (OpenOS's shell chains with `&&`; checked
1649+
in game), from `/api/admin/connect` (the API key and `PUBLIC_URL`; the
1650+
page's own origin without it). The scripts are the ones ticked above it
1651+
(`CONNECT_SCRIPTS` in agents.js, which a JS test holds to
1652+
oc/manifest.lua's components; the ticks kept in localStorage); naming them
1653+
skips gcm's question for each. That gcm.lua comes from main, not a
1654+
release, so installer changes reach new computers on merge. Install asks
1655+
nothing else: it always enables the scripts and gcm_agent in rc and
1656+
reboots 3 seconds later, except when the server check failed or a value
1657+
was left blank (`write_config` returns whether the config is usable),
1658+
because the reboot would clear the explanation off the screen. The key is masked on screen but copied in full - the
16511659
clipboard API needs HTTPS or localhost, so it falls back to
16521660
`execCommand('copy')` for a plain-HTTP LAN page. Before writing
16531661
config.lua, `gcm install` POSTs `/api/agent/ping` with the key and says

‎docs/in-game.md‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,26 @@ wget -f https://raw.githubusercontent.com/Maselkov/gtnh-craft-monitor/main/oc/gc
173173

174174
**Settings (⚙) → Status → Connect a computer** on the web page shows
175175
both as one command, joined with `&&`, with this server's address and API
176-
key filled in, ready to copy. Paste it into the computer with the middle
177-
mouse button or Insert.
176+
key filled in, ready to copy. Tick the scripts this computer should run
177+
first (Crafts, Power, Network; the page remembers your choice), and the
178+
command names them, so the computer doesn't ask. Paste it into the
179+
computer with the middle mouse button or Insert.
178180
The address is the page's own unless `PUBLIC_URL` is set; if the
179181
Minecraft server reaches the monitor some other way, press **Use a
180182
different address** there and type it in.
181183

182-
`gcm install` asks which scripts to install, and warns when a chosen
184+
Without script names, `gcm install` asks which to install. It warns when a chosen
183185
script's block isn't connected: an Adapter touching an ME controller or
184186
interface for craft and network, or a GregTech machine for power. For
185187
network it also notes when there's no optional ME Interface Terminal. The
186188
script is installed anyway, so the Adapter can go in later. Then it checks that the
187189
computer reaches the server with the key, saying what's wrong if it
188190
doesn't, and writes both to `/home/config.lua`. Without `--server` and
189-
`--api-key` it asks for them. It can also enable the scripts so they start
190-
on every boot, and offers to reboot to start them.
191+
`--api-key` it asks for them. Then it enables the scripts so they start
192+
on every boot and reboots after 3 seconds to start them. If the server
193+
check failed or a value was left blank, it doesn't reboot, so you can
194+
read what went wrong: fix `/home/config.lua` (or paste the command
195+
again), then reboot.
191196

192197
It also puts a GCM banner in `/etc/motd`, which OpenOS shows when the
193198
shell starts: the logo, the installed version, and each script with how it

‎oc/gcm.lua‎

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
-- Add --ref=<tag or branch> to install or update from something other
99
-- than the latest release, e.g. --ref=main to test unreleased changes.
1010
-- install --server=<url> --api-key=<key> skips asking for those two: the
11-
-- web page's Status dialog shows the whole command to paste. They also
12-
-- replace the ones in an existing config.lua.
11+
-- web page's Status dialog shows the whole command to paste, with the
12+
-- scripts picked there. They also replace the ones in an existing
13+
-- config.lua. install enables the scripts on boot and reboots to start
14+
-- them, unless the server check failed or a value was left blank.
1315
-- update --no-reboot skips the reboot question: gcm_agent.lua runs it
1416
-- that way for the web page's update button, and reboots itself.
1517
--
@@ -32,6 +34,9 @@ local REPO = "Maselkov/gtnh-craft-monitor"
3234
local STATE_PATH = "/etc/gcm.cfg"
3335
local CONNECT_TIMEOUT_SECONDS = 30
3436
local STALL_TIMEOUT_SECONDS = 30
37+
-- Long enough to see a warning about a missing Adapter go by; the boot
38+
-- banner and the web page's Status dialog show it afterwards too.
39+
local INSTALL_REBOOT_SECONDS = 3
3540
-- Length and checksum() of OpenOS's own /etc/motd
3641
-- (oc/tests/fixtures/openos_motd), which gcm may replace with its banner.
3742
local STOCK_MOTD_LENGTH = 1558
@@ -330,8 +335,8 @@ local function quoted_list(list)
330335
end
331336

332337
-- services: each component's rc service, for gcm_agent to report on.
333-
-- agent_set_up: gcm_agent has been enabled (or the user declined
334-
-- starting anything on boot), so updates don't enable it again.
338+
-- agent_set_up: gcm_agent has been enabled, so updates don't enable it
339+
-- again (and a player's `rc gcm_agent disable` sticks).
335340
local function write_state(state)
336341
local file = assert(io.open(STATE_PATH, "w"))
337342
file:write("-- Written by gcm. Lists what `gcm update` updates.\n")
@@ -409,7 +414,8 @@ local function is_local_host(url)
409414
end
410415

411416
-- Tries the URL and key on the server now, so a mistake shows here
412-
-- rather than as scripts that never report. Says what went wrong.
417+
-- rather than as scripts that never report. Says what went wrong, and
418+
-- returns whether the server accepted them.
413419
local function check_server(url, key)
414420
print("Checking " .. url .. " ...")
415421
local handle, err, code = open_request(url .. "/api/agent/ping", "{}",
@@ -443,6 +449,8 @@ end
443449
-- answers) filled in. After that, only the options given are written and
444450
-- the rest of the file is kept, so pasting the page's command again moves
445451
-- a computer to a new address or key; without them it's left alone.
452+
-- Returns whether the scripts can start with it: the server accepted the
453+
-- address and key, or an existing file was kept as it is.
446454
local function write_config(ref, cfg, opts)
447455
local url = opts.server and opts.server:gsub("/+$", "")
448456
local key = opts["api-key"]
@@ -451,7 +459,7 @@ local function write_config(ref, cfg, opts)
451459
if existing then
452460
if not (url or key) then
453461
print("Keeping existing " .. cfg.dst)
454-
return
462+
return true
455463
end
456464
local file = assert(io.open(cfg.dst, "r"))
457465
text = file:read("*a")
@@ -481,8 +489,9 @@ local function write_config(ref, cfg, opts)
481489
key = key or prompt("API key (on the web page: Settings > Status > Connect a computer)")
482490
checkUrl, checkKey = url, key
483491
end
492+
local accepted = false
484493
if type(checkUrl) == "string" and checkUrl ~= "" and type(checkKey) == "string" and checkKey ~= "" then
485-
check_server(checkUrl, checkKey)
494+
accepted = check_server(checkUrl, checkKey)
486495
end
487496
for _, setting in ipairs({ { "SERVER_URL", url }, { "API_KEY", key } }) do
488497
local name, value = setting[1], setting[2]
@@ -504,6 +513,7 @@ local function write_config(ref, cfg, opts)
504513
if not existing and (url == "" or key == "") then
505514
print("Edit " .. cfg.dst .. " to set the missing values before starting the scripts.")
506515
end
516+
return accepted
507517
end
508518

509519
-- rc caches loaded services, and http.lua/json.lua stay in package.loaded,
@@ -592,18 +602,25 @@ local function cmd_install(args, opts)
592602
local state = { version = ref, components = all, services = services_for(manifest, all),
593603
agent_set_up = previous and previous.agent_set_up }
594604
write_state(state)
595-
write_config(ref, manifest.config, opts)
605+
local ready = write_config(ref, manifest.config, opts)
596606

597607
print()
598-
if ask("Start the installed scripts automatically on every boot?", true) then
599-
for _, name in ipairs(chosen) do
600-
shell.execute("rc " .. find_component(manifest, name).service .. " enable")
601-
end
602-
if not state.agent_set_up then enable_agent() end
608+
for _, name in ipairs(chosen) do
609+
shell.execute("rc " .. find_component(manifest, name).service .. " enable")
603610
end
611+
if not state.agent_set_up then enable_agent() end
604612
state.agent_set_up = true
605613
write_state(state)
606-
offer_reboot()
614+
615+
-- Rebooting would clear the explanation of what to fix off the screen.
616+
print()
617+
if not ready then
618+
print("Not rebooting: fix " .. manifest.config.dst .. " first, then reboot to start the scripts.")
619+
return
620+
end
621+
print("Rebooting in " .. INSTALL_REBOOT_SECONDS .. " seconds to start the scripts.")
622+
os.sleep(INSTALL_REBOOT_SECONDS)
623+
computer.shutdown(true)
607624
end
608625

609626
local function cmd_update(opts, no_reboot)

‎oc/tests/gcm_test.lua‎

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ test("a failed update returns its message, for gcm_agent", function()
8484
end)
8585

8686
-- A fresh computer for `gcm install`: no config.lua yet, the monitor's
87-
-- ping answered by ping(req), and no to starting on boot and rebooting.
87+
-- ping answered by ping(req).
8888
local function install_env(ping)
8989
local env = github_env()
9090
os.remove(env:path("/home/config.lua"))
9191
env.server.routes["POST /api/agent/ping"] = ping
92-
env.stdin = { "n", "n" }
92+
env.stdin = {}
9393
return env
9494
end
9595

@@ -111,11 +111,38 @@ test("install --server --api-key saves both once the server accepts the key", fu
111111
t.eq(env:printed("Leave a value blank"), false)
112112
end)
113113

114+
test("install starts the scripts on boot and reboots without asking", function()
115+
local env = install_env({ ok = true })
116+
local started = env.now
117+
t.eq(gcm(env, "install", "craft", "power", "--server=https://gtnh.example.com", "--api-key=k3y"), nil)
118+
t.eq(count(env.shell_log, "rc craft_monitor enable"), 1)
119+
t.eq(count(env.shell_log, "rc power_monitor enable"), 1)
120+
t.eq(count(env.shell_log, "rc network_browser enable"), 0)
121+
t.eq(count(env.shell_log, "rc gcm_agent enable"), 1)
122+
t.eq(state(env).agent_set_up, true)
123+
t.eq(env:printed("[Y/n]"), false)
124+
t.truthy(env:printed("Rebooting in 3 seconds to start the scripts."))
125+
t.eq(env.shutdown.reboot, true)
126+
t.truthy(env.shutdown.at - started >= 3)
127+
128+
-- Adding a script later enables just that one; the agent stays as it is.
129+
env.shutdown = nil
130+
t.eq(gcm(env, "install", "network"), nil)
131+
t.eq(count(env.shell_log, "rc network_browser enable"), 1)
132+
t.eq(count(env.shell_log, "rc craft_monitor enable"), 1)
133+
t.eq(count(env.shell_log, "rc gcm_agent enable"), 1)
134+
t.truthy(env:printed("Keeping existing /home/config.lua"))
135+
t.eq(env.shutdown.reboot, true)
136+
end)
137+
114138
test("install says when the server refuses the key, and saves it anyway", function()
115139
local env = install_env({ status = 401, body = '{"error":"unauthorized"}' })
116140
t.eq(gcm(env, "install", "power", "--server=https://gtnh.example.com", "--api-key=wrong"), nil)
117141
t.truthy(env:printed("The server refused the API key."))
118142
t.eq(config(env).API_KEY, "wrong")
143+
t.eq(count(env.shell_log, "rc power_monitor enable"), 1)
144+
t.truthy(env:printed("Not rebooting: fix /home/config.lua first"))
145+
t.eq(env.shutdown, nil)
119146
end)
120147

121148
test("install points at OpenComputers' blacklist when a local address can't connect", function()
@@ -128,14 +155,33 @@ test("install points at OpenComputers' blacklist when a local address can't conn
128155
t.eq(gcm(public, "install", "power", "--server=https://gtnh.example.com", "--api-key=k3y"), nil)
129156
t.truthy(public:printed("The request comes from the Minecraft server"))
130157
t.eq(public:printed("OpenComputers blocks local addresses"), false)
158+
t.eq(public.shutdown, nil)
131159
end)
132160

133161
test("install asks for the address and key without the options", function()
134162
local env = install_env({ ok = true })
135-
env.stdin = { "http://gtnh.example.com", "k3y", "n", "n" }
163+
env.stdin = { "http://gtnh.example.com", "k3y" }
136164
t.eq(gcm(env, "install", "power"), nil)
137165
t.eq(config(env).SERVER_URL, "http://gtnh.example.com")
138166
t.truthy(env:printed("Leave a value blank"))
167+
t.eq(env.shutdown.reboot, true)
168+
end)
169+
170+
test("install doesn't reboot with a value left blank", function()
171+
local env = install_env({ ok = true })
172+
env.stdin = { "http://gtnh.example.com", "" }
173+
t.eq(gcm(env, "install", "power"), nil)
174+
t.eq(#env:requests_to("^/api/agent/ping$", "POST"), 0)
175+
t.truthy(env:printed("Edit /home/config.lua to set the missing values"))
176+
t.eq(env.shutdown, nil)
177+
end)
178+
179+
test("install with no scripts named asks for each", function()
180+
local env = install_env({ ok = true })
181+
env.stdin = { "n", "y", "n" }
182+
t.eq(gcm(env, "install", "--server=https://gtnh.example.com", "--api-key=k3y"), nil)
183+
t.eq(state(env).components, { "power" })
184+
t.eq(env.shutdown.reboot, true)
139185
end)
140186

141187
test("install --server --api-key replace them in an existing config.lua, keeping the rest", function()

‎server/index.html‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,16 @@ <h2>This base is private</h2>
509509
<div id="agentsList" class="agents-list"></div>
510510
<div class="status-heading connect-heading">Connect a computer</div>
511511
<div class="connect-computer">
512-
<div class="modal-note">On an OpenComputers computer with an Internet Card, run this command: copy it and paste it into the computer with the middle mouse button or Insert.</div>
512+
<div class="modal-note">On an OpenComputers computer with an Internet Card, run this command: pick the scripts it installs, copy it and paste it into the computer with the middle mouse button or Insert.</div>
513+
<div id="connectScripts" class="agent-settings connect-scripts"></div>
514+
<div id="connectNoScripts" class="agent-line agent-command failed" style="display:none;">Pick at least one script to install.</div>
513515
<div id="connectAddress" hidden>
514516
<label class="modal-label" for="connectServerUrl">This server's address, as the Minecraft server reaches it</label>
515517
<input id="connectServerUrl" class="modal-amount-input" spellcheck="false" autocomplete="off">
516518
</div>
517519
<div id="connectLocalNote" class="agent-line agent-command failed" style="display:none;">OpenComputers blocks local addresses like this one unless the Minecraft server's config allows them: see <a href="https://github.com/Maselkov/gtnh-craft-monitor#scripts-cant-reach-the-server" target="_blank" rel="noopener">Scripts can't reach the server</a>.</div>
518520
<div class="connect-command"><code id="connectCommand"></code><button class="range-btn" data-action="copy-connect">Copy</button></div>
519-
<div class="agent-line">It downloads the installer, which asks which scripts to install, checks that the computer reaches this server with the API key, and saves both.</div>
521+
<div class="agent-line">It downloads the installer, which installs the scripts, checks that the computer reaches this server with the API key and saves both, starts the scripts on every boot, and reboots to start them. If the server check fails it says why and doesn't reboot.</div>
520522
<div class="agent-buttons">
521523
<button class="range-btn" id="connectKeyBtn" data-action="toggle-connect-key">Show API key</button>
522524
<button class="range-btn" id="connectAddressBtn" data-action="edit-connect-address" title="If the Minecraft server reaches this server at another address than the one in the command">Use a different address</button>

‎server/static/app.css‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,10 @@ body:not(.is-admin) .admin-only, body.is-admin .non-admin-only {
785785
border-color: var(--accent);
786786
color: var(--accent);
787787
}
788+
.range-btn:disabled {
789+
opacity: 0.4;
790+
cursor: not-allowed;
791+
}
788792
.chart-wrap {
789793
background: var(--card);
790794
border-radius: 12px;
@@ -1378,6 +1382,9 @@ body.private-locked #networkTab {
13781382
.connect-heading {
13791383
margin-top: 16px;
13801384
}
1385+
.connect-scripts {
1386+
margin: 10px 0 0;
1387+
}
13811388
.connect-command {
13821389
display: flex;
13831390
align-items: flex-start;

0 commit comments

Comments
 (0)