openrod 0.1.2-rc.1 → 0.1.2-rc.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/package.json +1 -1
- package/server/api.js +6 -3
- package/server/cli.js +5 -1
- package/server/gateway.js +3 -0
- package/server/openshell-install.js +3 -1
- package/server/remote-gateway.js +8 -1
package/package.json
CHANGED
package/server/api.js
CHANGED
|
@@ -433,8 +433,11 @@ export function createOpenShellApi({ httpServer, logger = console, security = cr
|
|
|
433
433
|
connections: remoteConnections, listSandboxes, listTemplates: listImageTemplates, logger,
|
|
434
434
|
defaultLabel: security.config.mode === 'local' ? 'Local' : 'Cloud',
|
|
435
435
|
})
|
|
436
|
-
|
|
437
|
-
|
|
436
|
+
let ready = Promise.resolve()
|
|
437
|
+
const start = () => { ready = begin(); return ready }
|
|
438
|
+
const begin = async () => {
|
|
439
|
+
if (closed) return
|
|
440
|
+
if (security.config.mode === 'local' && !contextConfigured()) { await remoteConnections?.autoSelect(); return }
|
|
438
441
|
if (remoteConnections && /^console-ssh-[a-f0-9]{24}$/.test(initialContext.gateway)) {
|
|
439
442
|
const remote = (await remoteConnections.locationSnapshot()).remote
|
|
440
443
|
if (remote?.gateway !== initialContext.gateway || remote.status !== 'connected') return
|
|
@@ -688,5 +691,5 @@ export function createOpenShellApi({ httpServer, logger = console, security = cr
|
|
|
688
691
|
const authenticate = () => security.middleware(req, res, () => route(req, res, next))
|
|
689
692
|
return localCloud ? localCloud.middleware(req, res, authenticate) : authenticate()
|
|
690
693
|
}
|
|
691
|
-
return { middleware, close, token, launchCode: gate?.launchCode }
|
|
694
|
+
return { middleware, close, token, launchCode: gate?.launchCode, ready: () => ready }
|
|
692
695
|
}
|
package/server/cli.js
CHANGED
|
@@ -171,7 +171,11 @@ export async function startConsole(options = parseOptions([]), logger = console)
|
|
|
171
171
|
// The link carries this launch's secret; it changes on every restart.
|
|
172
172
|
const url = tokenUrl(host, port, token)
|
|
173
173
|
logger.info(`OpenRod console (open this link): ${url}`)
|
|
174
|
-
if (options.open)
|
|
174
|
+
if (options.open) {
|
|
175
|
+
// A fresh console first connects to its local gateway, so the page opens on it.
|
|
176
|
+
await Promise.race([api.ready?.(), new Promise((resolve) => setTimeout(resolve, 5000).unref())])
|
|
177
|
+
openBrowser(tokenUrl(host, port, api.launchCode?.() ?? token), logger)
|
|
178
|
+
}
|
|
175
179
|
return { server, url, close }
|
|
176
180
|
}
|
|
177
181
|
|
package/server/gateway.js
CHANGED
|
@@ -32,6 +32,9 @@ let selected = {
|
|
|
32
32
|
let configured = Boolean(process.env.OPENSHELL_GATEWAY || saved.gateway)
|
|
33
33
|
let selectionSource = process.env.OPENSHELL_GATEWAY ? 'environment' : saved.gateway ? 'saved' : cliGateway ? 'cli' : 'default'
|
|
34
34
|
export const contextConfigured = () => configured
|
|
35
|
+
// A console nobody has pointed anywhere yet: no saved or cleared choice and no
|
|
36
|
+
// OPENSHELL_GATEWAY. Its candidate is the gateway the OpenShell CLI is set to.
|
|
37
|
+
export const unchosenCliGateway = () => !configured && !process.env.OPENSHELL_GATEWAY && cliGateway && !fs.existsSync(CONTEXT_FILE) ? cliGateway : null
|
|
35
38
|
|
|
36
39
|
const requestContext = new AsyncLocalStorage()
|
|
37
40
|
export const contextSelection = () => ({ ...(requestContext.getStore() ?? selected) })
|
|
@@ -146,7 +146,9 @@ export async function offerOpenShellInstall({
|
|
|
146
146
|
const needs = platform === 'darwin' ? 'OpenRod needs it to build sandbox images.' : "OpenShell's gateway won't start without it."
|
|
147
147
|
if (docker === 'missing') log.info(`Docker isn't installed. ${needs}`)
|
|
148
148
|
else if (!docker) log.info(`Docker isn't running. ${needs}`)
|
|
149
|
-
|
|
149
|
+
let answer = await ask(`Install OpenShell ${OPENSHELL_VERSION} now? ${platform === 'darwin' ? 'It uses Homebrew and runs sandboxes in VMs.' : 'It uses sudo and may ask for your password.'} [Y/n] `)
|
|
150
|
+
// Anything but yes or no (a key from another keyboard layout, say) asks again.
|
|
151
|
+
for (let tries = 0; answer !== null && !['', 'y', 'yes', 'n', 'no'].includes(answer.trim().toLowerCase()) && tries < 3; tries++) answer = await ask('Please answer y or n. [Y/n] ')
|
|
150
152
|
if (answer === null) return 'cancelled'
|
|
151
153
|
if (!['', 'y', 'yes'].includes(answer.trim().toLowerCase())) { manual('Skipped. To install it later, run:'); return 'skipped' }
|
|
152
154
|
|
package/server/remote-gateway.js
CHANGED
|
@@ -8,7 +8,7 @@ import { randomUUID } from 'node:crypto'
|
|
|
8
8
|
import { Transform } from 'node:stream'
|
|
9
9
|
import { pipeline } from 'node:stream/promises'
|
|
10
10
|
import { setTimeout as delay } from 'node:timers/promises'
|
|
11
|
-
import { CONFIG_DIR, clearConsoleContext, contextConfigured, contextSelection, defaultContextSelection, localGateways, gatewayWorkspaces, selectConsoleContext } from './gateway.js'
|
|
11
|
+
import { CONFIG_DIR, clearConsoleContext, contextConfigured, contextSelection, defaultContextSelection, localGateways, gatewayWorkspaces, selectConsoleContext, unchosenCliGateway } from './gateway.js'
|
|
12
12
|
import { stateDirectory } from './paths.js'
|
|
13
13
|
import { fail, findExecutable, openshellBinary, runCli, sshBinary } from './openshell-cli.js'
|
|
14
14
|
import { listSshHosts, probeHost, installDocker as installHostDocker, installRuntime, sshArgs } from './remote-hosts.js'
|
|
@@ -322,6 +322,13 @@ export function createRemoteConnections({ onSelected = () => {}, onDeselected =
|
|
|
322
322
|
return { hosts: await listSshHosts(), locals: localGateways().map(({ name, endpoint }) => ({ name, endpoint })), active: view(connection), job: view(job), tools: { ssh: Boolean(sshBinary()), gateway: Boolean(findExecutable('openshell-gateway')), openshell: Boolean(openshellBinary()), installCommand: installCommand(process.platform) } }
|
|
323
323
|
},
|
|
324
324
|
changing: () => disconnecting || job?.status === 'working',
|
|
325
|
+
// A fresh console connects to the CLI's local gateway the way "Use this
|
|
326
|
+
// computer" does, but only once it answers; otherwise setup stays up.
|
|
327
|
+
async autoSelect() {
|
|
328
|
+
const name = unchosenCliGateway()
|
|
329
|
+
if (!name || job?.status === 'working' || !localGateways().some(target => target.name === name)) return false
|
|
330
|
+
try { await select(name); return true } catch { return false }
|
|
331
|
+
},
|
|
325
332
|
architecture: () => active?.status === 'connected' ? active.architecture : null,
|
|
326
333
|
begin,
|
|
327
334
|
job: id => view(getJob(id)),
|