This commit is contained in:
Raven Scott
2026-07-30 23:25:19 -04:00
parent 501d1e5cfd
commit 92cb4b9554
24 changed files with 909 additions and 118 deletions
+105 -8
View File
@@ -8,11 +8,14 @@ const { SquidManager } = require('./lib/squid-manager')
const { createWorld, listWorlds, resolveWorldPaths } = require('./lib/worlds')
const { worldsRoot } = require('./lib/paths')
const { ensureDir } = require('./lib/worlds')
const { WorldTunnelHost, WorldTunnelClient } = require('./lib/world-tunnel')
const { loadOrCreateTunnelKeys } = require('./lib/tunnel-keys')
const { encodeInvite, decodeInvite, decodeKey } = require('./lib/invite')
/**
* Flying Jib Bare application shell.
* - Optional pear-runtime worker for OTA (hello-pear-bare pattern)
* - Squid runs in this process under Bare (ADR-0013)
* - Squid + HyperDHT tunnels run in this process under Bare (ADR-0013)
*/
module.exports = class App extends ReadyResource {
constructor({ dir, appPath, updates, version, upgrade, name }) {
@@ -26,14 +29,16 @@ module.exports = class App extends ReadyResource {
this.IPC = null
this.pipe = null
this.squid = null
this.tunnelHost = null
this.tunnelClient = null
this.activeWorld = null
this._shuttingDown = false
}
_open() {
ensureDir(worldsRoot(this.dir))
ensureDir(path.join(this.dir, 'corestore'))
// Pear OTA worker (optional — disabled when --no-updates and no upgrade key)
const enableWorker =
this.updates !== false && this.upgrade && !String(this.upgrade).includes('<YOUR_KEY')
@@ -58,10 +63,19 @@ module.exports = class App extends ReadyResource {
}
async _close() {
if (this.tunnelClient) {
await this.tunnelClient.close().catch(() => {})
this.tunnelClient = null
}
if (this.tunnelHost) {
await this.tunnelHost.close().catch(() => {})
this.tunnelHost = null
}
if (this.squid) {
await this.squid.close().catch(() => {})
this.squid = null
}
this.activeWorld = null
const pipe = this.pipe
const IPC = this.IPC
this.pipe = null
@@ -117,7 +131,7 @@ module.exports = class App extends ReadyResource {
port,
version,
motd: meta.motd || `Flying Jib — ${meta.name}`,
logging: true
logging: false
})
this.squid.on('error', (err) => this.emit('error', err))
@@ -130,24 +144,107 @@ module.exports = class App extends ReadyResource {
return this.squid.status
}
/**
* Start Squid + HyperDHT host tunnel; return status + invite string.
* @param {{ world: string, port?: number, version?: string }} opts
*/
async hostWorld(opts) {
const squidStatus = await this.startWorld(opts)
const keys = loadOrCreateTunnelKeys(this.dir, this.activeWorld.id)
this.tunnelHost = new WorldTunnelHost({
localPort: squidStatus.port,
seed: keys.seed,
cap: keys.cap
})
this.tunnelHost.on('connection', () => {
this.emit('message', 'Remote peer connected via tunnel')
})
this.tunnelHost.on('error', (err) => this.emit('error', err))
await this.tunnelHost.ready()
const invite = encodeInvite({
type: 'private-world',
worldKey: keys.publicKeyZ32,
cap: keys.capZ32,
name: this.activeWorld.name,
mcVersion: this.activeWorld.version,
portHint: squidStatus.port
})
return {
squid: squidStatus,
tunnel: this.tunnelHost.status,
invite
}
}
/**
* Join a private world via fj1. invite (client tunnel only; no local Squid).
* @param {{ invite: string, localPort?: number }} opts
*/
async joinWorld(opts) {
if (this.tunnelClient) {
throw new Error('Already joined a remote world; stop first')
}
const inv = decodeInvite(opts.invite)
const publicKey = decodeKey(inv.worldKey)
const cap = decodeKey(inv.cap)
this.tunnelClient = new WorldTunnelClient({
publicKey,
cap,
localPort: opts.localPort != null ? Number(opts.localPort) : 0
})
this.tunnelClient.on('error', (err) => this.emit('error', err))
await this.tunnelClient.ready()
return {
invite: inv,
tunnel: this.tunnelClient.status
}
}
async stopWorld() {
if (!this.squid) return false
await this.squid.close()
this.squid = null
let stopped = false
if (this.tunnelClient) {
await this.tunnelClient.close().catch(() => {})
this.tunnelClient = null
stopped = true
}
if (this.tunnelHost) {
await this.tunnelHost.close().catch(() => {})
this.tunnelHost = null
stopped = true
}
if (this.squid) {
await this.squid.close().catch(() => {})
this.squid = null
stopped = true
}
this.activeWorld = null
return true
return stopped
}
getStatus() {
return {
storage: this.dir,
world: this.activeWorld,
squid: this.squid ? this.squid.status : { running: false }
squid: this.squid ? this.squid.status : { running: false },
tunnelHost: this.tunnelHost ? this.tunnelHost.status : null,
tunnelClient: this.tunnelClient ? this.tunnelClient.status : null
}
}
async exit(code = 0) {
if (this._shuttingDown) return
this._shuttingDown = true
if (typeof Bare !== 'undefined') Bare.exitCode = code
try {
await this.stopWorld()
} catch {
// ignore
}
await this.close()
}
}