// A rig's server through the panel's client API: state, power, a console command, and files. // // node tools/rig.js state // node tools/rig.js power // node tools/rig.js cmd "" (fire and forget; console.js reads the reply) // node tools/rig.js ls // node tools/rig.js read (TAIL=n prints the last n lines) // node tools/rig.js write // node tools/rig.js rm // // Paths are the server's, from its root: /oxide/plugins/RunicNPC.cs, /carbon/plugins/RunicNPC.cs. // // A directory the panel's file API creates is NOT writable by the game (docs/runicnpc/PLAN.md // ยง1.5). Let the plugin create its own data directory, then write files into it. const fs = require('fs') const { load, serverId, unmsys } = require('./panel') const config = load() const [rig, op, a, b] = process.argv.slice(2).map(unmsys) const base = `${config.panel}/api/client/servers/${serverId(config, rig)}` const headers = (type = 'application/json') => ({ Authorization: 'Bearer ' + config.key, Accept: 'application/json', 'Content-Type': type, }) async function main() { let r switch (op) { case 'state': r = await fetch(`${base}/resources`, { headers: headers() }) break case 'power': r = await fetch(`${base}/power`, { method: 'POST', headers: headers(), body: JSON.stringify({ signal: a }) }) break case 'cmd': r = await fetch(`${base}/command`, { method: 'POST', headers: headers(), body: JSON.stringify({ command: a }) }) break case 'ls': r = await fetch(`${base}/files/list?directory=${encodeURIComponent(a)}`, { headers: headers() }) break case 'read': r = await fetch(`${base}/files/contents?file=${encodeURIComponent(a)}`, { headers: headers() }) break case 'write': r = await fetch(`${base}/files/write?file=${encodeURIComponent(a)}`, { method: 'POST', headers: headers('text/plain'), body: fs.readFileSync(b), }) break case 'rm': r = await fetch(`${base}/files/delete`, { method: 'POST', headers: headers(), body: JSON.stringify({ root: a.slice(0, a.lastIndexOf('/')) || '/', files: [a.slice(a.lastIndexOf('/') + 1)] }), }) break default: console.error('usage: node tools/rig.js state|power|cmd|ls|read|write|rm ...') process.exit(2) } const text = await r.text() if (!r.ok) { console.error(r.status, text) process.exit(1) } if (op === 'state') return console.log(JSON.parse(text).attributes.current_state) if (op === 'ls') { const rows = (JSON.parse(text).data || []).map( (f) => `${f.attributes.is_file ? 'f' : 'd'} ${f.attributes.size}\t${f.attributes.name}` ) return console.log(rows.join('\n')) } if (op === 'read' && process.env.TAIL) return console.log(text.split('\n').slice(-Number(process.env.TAIL)).join('\n')) console.log(op === 'read' ? text : r.status) } main().catch((e) => { console.error(e.message || e) process.exit(1) })