This commit is contained in:
@@ -68,6 +68,7 @@ export const COREUTILS_COMMANDS = [
|
||||
'hypershell-board',
|
||||
'iconv',
|
||||
'id',
|
||||
'init-discord',
|
||||
'install',
|
||||
'irc',
|
||||
'join',
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "init-discord",
|
||||
"section": 1,
|
||||
"title": "init-discord",
|
||||
"synopsis": [
|
||||
"init-discord [--token TOKEN] [--guild ID] [--whitelist ID,ID] [--yes] [--force] [--plugin-only]"
|
||||
],
|
||||
"description": "Set up the stock Discord bot on this session. Creates ~/.discord and ~/.discord/plugins, writes ~/.discord/.env (DISCORD_TOKEN, optional DISCORD_GUILD_ID and DISCORD_ID_WHITELIST), and installs the hello-world plugin ~/.discord/plugins/hello.json (/hello). Interactive by default: prompts for token, guild id, and whitelist. Never prints the full token. After setup run discord-bot --check and start bare-os-discord.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "--token TOKEN",
|
||||
"meaning": "Bot token (Developer Portal → Bot → Token). Prefer the interactive prompt so it is not stored in shell history."
|
||||
},
|
||||
{
|
||||
"flag": "--guild ID",
|
||||
"meaning": "Guild id for instant slash-command registration."
|
||||
},
|
||||
{
|
||||
"flag": "--whitelist ID,ID",
|
||||
"meaning": "Comma-separated Discord user ids allowed to use the bot."
|
||||
},
|
||||
{
|
||||
"flag": "--yes",
|
||||
"meaning": "Non-interactive. Requires --token or an existing ~/.discord/.env token."
|
||||
},
|
||||
{
|
||||
"flag": "--force",
|
||||
"meaning": "Replace an existing token and overwrite hello.json."
|
||||
},
|
||||
{
|
||||
"flag": "--plugin-only",
|
||||
"meaning": "Only create ~/.discord/plugins/hello.json; do not change .env."
|
||||
}
|
||||
],
|
||||
"keywords": ["discord", "bot", "setup", "init", "plugin"],
|
||||
"environment": [
|
||||
"Writes DISCORD_TOKEN, DISCORD_GUILD_ID, DISCORD_ID_WHITELIST into ~/.discord/.env and the current session env."
|
||||
],
|
||||
"seeAlso": [
|
||||
{
|
||||
"name": "discord-bot",
|
||||
"section": 1
|
||||
},
|
||||
{
|
||||
"name": "devguide-21-discord-bots",
|
||||
"section": 7
|
||||
}
|
||||
],
|
||||
"bareOsNotes": "Guest VFS paths. The initd unit bare-os-discord appears in systemctl list only after ~/.discord/.env exists with a token.",
|
||||
"examples": [
|
||||
{
|
||||
"caption": "interactive setup",
|
||||
"code": "init-discord"
|
||||
},
|
||||
{
|
||||
"caption": "scripted setup (avoid --token on a recorded TTY if you can)",
|
||||
"code": "init-discord --yes --token \"$DISCORD_TOKEN\" --guild 123 --whitelist 456"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)",
|
||||
"scripts": {
|
||||
"build": "node ./scripts/ensure-man-pages.mjs && node ./build.mjs",
|
||||
"test": "node ./test/clear-sequence.test.mjs && node ./test/help-bin-list.test.mjs && node ./test/whois-rdap.test.mjs && node ./test/edit-key-parse.test.mjs && node ./test/edit-teardown.test.mjs && node ./test/edit-tui-sdk.test.mjs && node ./test/baresay-say-bundle.test.mjs && node ./test/baretop-bundle.test.mjs && node ./test/baretop-tui-sdk.test.mjs && node ./test/baretop-fixture.test.mjs && node ./test/baretop-compose.test.mjs && node ./test/baretop-incremental.test.mjs && node ./test/baretop-interaction-latency.test.mjs && node ./test/baretop-missing-signals.test.mjs && node ./test/baretop-stress-snapshot.test.mjs && node ./test/baretop-ui-helpers.test.mjs && node ./test/baretop-perf-baseline.test.mjs && node ./test/irc-parse.test.mjs && node ./test/irc-client.test.mjs && node ./test/irc-dial.test.mjs && node ./test/irc-commands.test.mjs && node ./test/irc-tui-sdk.test.mjs && node ./test/summon-engine.test.mjs && node ./test/summon-tui-sdk.test.mjs && node ./test/posix-test-int-compare.test.mjs && node ./test/posix-test-bracket-argv.test.mjs && node ./test/posix-utils-edge.test.mjs && node ./test/posix-golden-issue7.test.mjs && node ./test/getconf-pathconf-union-mirror.test.mjs && node ./test/getconf-posix-shm.test.mjs && node ./test/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.test.mjs && node ./test/xcu-issue7-sweep.test.mjs && node ./test/expand-tab-stops.test.mjs && node ./test/pkg-swarm-index-pathcap.test.mjs && node ./test/sha224-sha384-sum.test.mjs && node ./test/hardening.test.mjs && node ./test/agent-sse-parse.test.mjs && node ./test/agent-web-fetch.test.mjs && node ./test/agent-helpers.test.mjs && node ./test/agent-tools-run-command.test.mjs && node ./test/agent-workspace.test.mjs && node ./test/agent-skills.test.mjs && node ./test/agent-config-surface.test.mjs && node ./test/agent-trim-ctx.test.mjs && node ./test/agent-qvac.test.mjs && node ./test/telnet-protocol.test.mjs && node ./test/telnet-cli.test.mjs && node ./test/login.test.mjs && node ./test/p2p-suite-bundles.test.mjs && node ./test/p2p-suite-tui-sdk.test.mjs && node ./test/chat-tui-sdk.test.mjs && node ./test/swarmtop-peer-visibility.test.mjs",
|
||||
"test": "node ./test/clear-sequence.test.mjs && node ./test/init-discord.test.mjs && node ./test/help-bin-list.test.mjs && node ./test/whois-rdap.test.mjs && node ./test/edit-key-parse.test.mjs && node ./test/edit-teardown.test.mjs && node ./test/edit-tui-sdk.test.mjs && node ./test/baresay-say-bundle.test.mjs && node ./test/baretop-bundle.test.mjs && node ./test/baretop-tui-sdk.test.mjs && node ./test/baretop-fixture.test.mjs && node ./test/baretop-compose.test.mjs && node ./test/baretop-incremental.test.mjs && node ./test/baretop-interaction-latency.test.mjs && node ./test/baretop-missing-signals.test.mjs && node ./test/baretop-stress-snapshot.test.mjs && node ./test/baretop-ui-helpers.test.mjs && node ./test/baretop-perf-baseline.test.mjs && node ./test/irc-parse.test.mjs && node ./test/irc-client.test.mjs && node ./test/irc-dial.test.mjs && node ./test/irc-commands.test.mjs && node ./test/irc-tui-sdk.test.mjs && node ./test/summon-engine.test.mjs && node ./test/summon-tui-sdk.test.mjs && node ./test/posix-test-int-compare.test.mjs && node ./test/posix-test-bracket-argv.test.mjs && node ./test/posix-utils-edge.test.mjs && node ./test/posix-golden-issue7.test.mjs && node ./test/getconf-pathconf-union-mirror.test.mjs && node ./test/getconf-posix-shm.test.mjs && node ./test/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.test.mjs && node ./test/xcu-issue7-sweep.test.mjs && node ./test/expand-tab-stops.test.mjs && node ./test/pkg-swarm-index-pathcap.test.mjs && node ./test/sha224-sha384-sum.test.mjs && node ./test/hardening.test.mjs && node ./test/agent-sse-parse.test.mjs && node ./test/agent-web-fetch.test.mjs && node ./test/agent-helpers.test.mjs && node ./test/agent-tools-run-command.test.mjs && node ./test/agent-workspace.test.mjs && node ./test/agent-skills.test.mjs && node ./test/agent-config-surface.test.mjs && node ./test/agent-trim-ctx.test.mjs && node ./test/agent-qvac.test.mjs && node ./test/telnet-protocol.test.mjs && node ./test/telnet-cli.test.mjs && node ./test/login.test.mjs && node ./test/p2p-suite-bundles.test.mjs && node ./test/p2p-suite-tui-sdk.test.mjs && node ./test/chat-tui-sdk.test.mjs && node ./test/swarmtop-peer-visibility.test.mjs",
|
||||
"perf:baretop": "node ./test/baretop-perf-baseline.test.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Interactive Discord bot setup: ~/.discord/.env + hello-world plugin.
|
||||
*/
|
||||
|
||||
var INIT_DISCORD_HELLO_JSON =
|
||||
'{\n' +
|
||||
' "name": "hello",\n' +
|
||||
' "description": "Greet someone from Bare OS",\n' +
|
||||
' "version": "1.0.0",\n' +
|
||||
' "options": [\n' +
|
||||
' {\n' +
|
||||
' "name": "who",\n' +
|
||||
' "description": "Name to greet",\n' +
|
||||
' "required": false\n' +
|
||||
' }\n' +
|
||||
' ],\n' +
|
||||
' "run": "echo Hello, ${who:-world} — from $USER on $HOSTNAME",\n' +
|
||||
' "title": "hello"\n' +
|
||||
'}\n'
|
||||
|
||||
function initDiscordHasFlag(argv, names) {
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = String(argv[i] || '')
|
||||
for (let n = 0; n < names.length; n++) {
|
||||
if (a === names[n]) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function initDiscordArg(argv, names) {
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = String(argv[i] || '')
|
||||
for (let n = 0; n < names.length; n++) {
|
||||
const name = names[n]
|
||||
if (a === name) return argv[i + 1] != null ? String(argv[i + 1]) : ''
|
||||
if (a.indexOf(name + '=') === 0) return a.slice(name.length + 1)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function initDiscordParseEnv(text) {
|
||||
const out = {}
|
||||
const lines = String(text || '').split(/\r?\n/)
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let s = lines[i].replace(/^\uFEFF/, '').trim()
|
||||
if (!s || s.charAt(0) === '#') continue
|
||||
if (s.indexOf('export ') === 0) s = s.slice(7).trim()
|
||||
const eq = s.indexOf('=')
|
||||
if (eq < 1) continue
|
||||
let val = s.slice(eq + 1).trim()
|
||||
if (
|
||||
(val.charAt(0) === '"' && val.charAt(val.length - 1) === '"') ||
|
||||
(val.charAt(0) === "'" && val.charAt(val.length - 1) === "'")
|
||||
) {
|
||||
val = val.slice(1, -1)
|
||||
}
|
||||
out[s.slice(0, eq).trim()] = val
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function initDiscordMaskToken(tok) {
|
||||
const t = String(tok || '')
|
||||
if (t.length < 8) return '(set)'
|
||||
return t.slice(0, 4) + '…' + t.slice(-4)
|
||||
}
|
||||
|
||||
function initDiscordUpsertEnv(text, key, value) {
|
||||
const line = key + '=' + value
|
||||
const re = new RegExp('^\\s*(?:export\\s+)?' + key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&') + '=.*$', 'm')
|
||||
if (re.test(text)) return text.replace(re, line)
|
||||
const body = text && !/\n$/.test(text) ? text + '\n' : text || ''
|
||||
return body + line + '\n'
|
||||
}
|
||||
|
||||
async function initDiscordReadText(ctx, path) {
|
||||
try {
|
||||
const buf = await ctx.vfs.readFile(path)
|
||||
if (!buf) return ''
|
||||
if (ctx.b4a && typeof ctx.b4a.toString === 'function') return ctx.b4a.toString(buf)
|
||||
return String(buf)
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
async function initDiscordWriteText(ctx, path, text) {
|
||||
const buf = ctx.b4a && typeof ctx.b4a.from === 'function' ? ctx.b4a.from(text) : text
|
||||
await ctx.vfs.writeFile(path, buf)
|
||||
}
|
||||
|
||||
async function initDiscordMkdir(ctx, path) {
|
||||
if (!ctx.vfs || typeof ctx.vfs.mkdir !== 'function') return
|
||||
try {
|
||||
await ctx.vfs.mkdir(path, { recursive: true })
|
||||
} catch {
|
||||
/* exists */
|
||||
}
|
||||
}
|
||||
|
||||
async function initDiscordAsk(ctx, prompt, fallback) {
|
||||
if (typeof ctx.readLine !== 'function') return fallback || ''
|
||||
const hint = fallback ? ' [' + fallback + ']' : ''
|
||||
const raw = await ctx.readLine(prompt + hint + ': ')
|
||||
const s = String(raw == null ? '' : raw).trim()
|
||||
return s || fallback || ''
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const argv0 = argv[0] || 'init-discord'
|
||||
if (initDiscordHasFlag(argv, ['-h', '--help'])) {
|
||||
ctx.console.log(
|
||||
'usage: ' +
|
||||
argv0 +
|
||||
' [--token TOKEN] [--guild ID] [--whitelist ID,ID] [--yes] [--force] [--plugin-only]\n\n' +
|
||||
'Create ~/.discord, write ~/.discord/.env, and install a hello-world plugin.\n' +
|
||||
'Interactive by default (asks for token, guild id, whitelist). Use flags for scripts.\n\n' +
|
||||
' --token TOKEN Bot token (Developer Portal → Bot). Never logged.\n' +
|
||||
' --guild ID Guild id for instant slash registration.\n' +
|
||||
' --whitelist ID,ID Discord user ids allowed to use the bot.\n' +
|
||||
' --yes Non-interactive: require --token (or an existing .env).\n' +
|
||||
' --force Overwrite an existing token / hello.json.\n' +
|
||||
' --plugin-only Only ensure the hello plugin; leave .env alone.\n\n' +
|
||||
'Then: discord-bot --check --env ~/.discord/.env\n' +
|
||||
' systemctl daemon-reload && systemctl start bare-os-discord'
|
||||
)
|
||||
ctx.exitCode = 0
|
||||
return
|
||||
}
|
||||
if (!ctx.vfs || typeof ctx.vfs.writeFile !== 'function') {
|
||||
ctx.console.error(argv0 + ': vfs.writeFile unavailable')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
const yes = initDiscordHasFlag(argv, ['--yes', '-y'])
|
||||
const force = initDiscordHasFlag(argv, ['--force'])
|
||||
const pluginOnly = initDiscordHasFlag(argv, ['--plugin-only'])
|
||||
let tokenFlag = initDiscordArg(argv, ['--token'])
|
||||
let guildFlag = initDiscordArg(argv, ['--guild', '--guild-id'])
|
||||
let whiteFlag = initDiscordArg(argv, ['--whitelist', '--id-whitelist'])
|
||||
|
||||
await initDiscordMkdir(ctx, '~/.discord')
|
||||
await initDiscordMkdir(ctx, '~/.discord/plugins')
|
||||
|
||||
const envPath = '~/.discord/.env'
|
||||
const prevText = await initDiscordReadText(ctx, envPath)
|
||||
const prev = initDiscordParseEnv(prevText)
|
||||
const prevToken = prev.DISCORD_TOKEN || prev.BOT_TOKEN || ''
|
||||
const prevGuild = prev.DISCORD_GUILD_ID || ''
|
||||
const prevWhite = prev.DISCORD_ID_WHITELIST || ''
|
||||
|
||||
if (!pluginOnly) {
|
||||
let token = tokenFlag
|
||||
let guild = guildFlag
|
||||
let white = whiteFlag
|
||||
if (!yes) {
|
||||
ctx.console.log('Bare OS Discord setup')
|
||||
ctx.console.log('Token from Developer Portal → Bot → Reset Token (not the OAuth2 client secret).')
|
||||
if (prevToken && !token) {
|
||||
ctx.console.log('Existing token: ' + initDiscordMaskToken(prevToken) + ' (Enter keeps it)')
|
||||
}
|
||||
token = await initDiscordAsk(ctx, 'DISCORD_TOKEN', token || '')
|
||||
if (!token && prevToken) token = prevToken
|
||||
guild = await initDiscordAsk(
|
||||
ctx,
|
||||
'DISCORD_GUILD_ID (optional, instant slash commands)',
|
||||
guild || prevGuild
|
||||
)
|
||||
white = await initDiscordAsk(
|
||||
ctx,
|
||||
'DISCORD_ID_WHITELIST (your user id, comma-separated)',
|
||||
white || prevWhite
|
||||
)
|
||||
} else if (!token) {
|
||||
token = prevToken
|
||||
}
|
||||
if (!token) {
|
||||
ctx.console.error(argv0 + ': DISCORD_TOKEN is required (flag --token or interactive prompt)')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (prevToken && token !== prevToken && !force && yes) {
|
||||
ctx.console.error(argv0 + ': ~/.discord/.env already has a token (pass --force to replace)')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
let next = prevText || '# Bare OS Discord bot\n'
|
||||
next = initDiscordUpsertEnv(next, 'DISCORD_TOKEN', token)
|
||||
if (guild) next = initDiscordUpsertEnv(next, 'DISCORD_GUILD_ID', guild)
|
||||
if (white) next = initDiscordUpsertEnv(next, 'DISCORD_ID_WHITELIST', white)
|
||||
await initDiscordWriteText(ctx, envPath, next)
|
||||
if (ctx.env) {
|
||||
ctx.env.DISCORD_TOKEN = token
|
||||
if (guild) ctx.env.DISCORD_GUILD_ID = guild
|
||||
if (white) ctx.env.DISCORD_ID_WHITELIST = white
|
||||
}
|
||||
if (ctx.vfs.env) {
|
||||
ctx.vfs.env.DISCORD_TOKEN = token
|
||||
if (guild) ctx.vfs.env.DISCORD_GUILD_ID = guild
|
||||
if (white) ctx.vfs.env.DISCORD_ID_WHITELIST = white
|
||||
}
|
||||
ctx.console.log('Wrote ' + envPath + ' (token ' + initDiscordMaskToken(token) + ')')
|
||||
if (guild) ctx.console.log('Guild id ' + guild)
|
||||
if (white) ctx.console.log('Whitelist ' + white)
|
||||
}
|
||||
|
||||
const pluginPath = '~/.discord/plugins/hello.json'
|
||||
const existingPlugin = await initDiscordReadText(ctx, pluginPath)
|
||||
if (existingPlugin && !force) {
|
||||
ctx.console.log('Kept existing ' + pluginPath)
|
||||
} else {
|
||||
await initDiscordWriteText(ctx, pluginPath, INIT_DISCORD_HELLO_JSON)
|
||||
ctx.console.log('Installed example plugin ' + pluginPath + ' (/hello)')
|
||||
}
|
||||
|
||||
ctx.console.log('')
|
||||
ctx.console.log('Next:')
|
||||
ctx.console.log(' discord-bot --check --env ~/.discord/.env')
|
||||
ctx.console.log(' discord-bot --env ~/.discord/.env')
|
||||
ctx.console.log(' systemctl daemon-reload && systemctl start bare-os-discord')
|
||||
ctx.console.log(' /plugins reload (after the bot is online)')
|
||||
ctx.exitCode = 0
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'brittle'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
|
||||
async function loadBin() {
|
||||
const runtime = await readFile(path.join(__dirname, '../lib/runtime.js'), 'utf8')
|
||||
const body = await readFile(path.join(__dirname, '../src/init-discord.js'), 'utf8')
|
||||
return new AsyncFunction(
|
||||
'ctx',
|
||||
'argv',
|
||||
`${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n`
|
||||
)
|
||||
}
|
||||
|
||||
function makeCtx(tree) {
|
||||
const env = { USER: 'alice', HOME: '/home/alice' }
|
||||
const logs = []
|
||||
const errs = []
|
||||
return {
|
||||
logs,
|
||||
errs,
|
||||
env,
|
||||
exitCode: 0,
|
||||
console: {
|
||||
log: (m) => logs.push(String(m)),
|
||||
error: (m) => errs.push(String(m))
|
||||
},
|
||||
b4a: {
|
||||
toString: (b) => Buffer.from(b).toString('utf8'),
|
||||
from: (s) => Buffer.from(String(s))
|
||||
},
|
||||
vfs: {
|
||||
env,
|
||||
mkdir: async (p) => {
|
||||
if (!tree[p]) tree[p] = []
|
||||
},
|
||||
readFile: async (p) => {
|
||||
if (typeof tree[p] !== 'string') {
|
||||
const e = new Error('ENOENT')
|
||||
e.code = 'ENOENT'
|
||||
throw e
|
||||
}
|
||||
return Buffer.from(tree[p])
|
||||
},
|
||||
writeFile: async (p, buf) => {
|
||||
tree[p] = Buffer.from(buf).toString('utf8')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test('init-discord --help', async (t) => {
|
||||
const run = await loadBin()
|
||||
const tree = {}
|
||||
const ctx = makeCtx(tree)
|
||||
await run(ctx, ['init-discord', '--help'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(ctx.logs.some((l) => /--token/.test(l)))
|
||||
})
|
||||
|
||||
test('init-discord --yes writes env and hello plugin without echoing the token', async (t) => {
|
||||
const run = await loadBin()
|
||||
const tree = {}
|
||||
const ctx = makeCtx(tree)
|
||||
await run(ctx, [
|
||||
'init-discord',
|
||||
'--yes',
|
||||
'--token',
|
||||
'abc.def.secret-token',
|
||||
'--guild',
|
||||
'111',
|
||||
'--whitelist',
|
||||
'222,333'
|
||||
])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(/DISCORD_TOKEN=abc\.def\.secret-token/.test(tree['~/.discord/.env']))
|
||||
t.ok(/DISCORD_GUILD_ID=111/.test(tree['~/.discord/.env']))
|
||||
t.ok(/DISCORD_ID_WHITELIST=222,333/.test(tree['~/.discord/.env']))
|
||||
t.ok(/"name": "hello"/.test(tree['~/.discord/plugins/hello.json']))
|
||||
t.ok(ctx.logs.some((l) => /hello\.json/.test(l)))
|
||||
t.absent(ctx.logs.some((l) => /secret-token/.test(l)), 'never prints full token')
|
||||
t.is(ctx.env.DISCORD_GUILD_ID, '111')
|
||||
})
|
||||
|
||||
test('init-discord --yes refuses to clobber a token without --force', async (t) => {
|
||||
const run = await loadBin()
|
||||
const tree = {
|
||||
'~/.discord/.env': 'DISCORD_TOKEN=old.token.value\n'
|
||||
}
|
||||
const ctx = makeCtx(tree)
|
||||
await run(ctx, ['init-discord', '--yes', '--token', 'new.token.value'])
|
||||
t.is(ctx.exitCode, 1)
|
||||
t.ok(/old\.token\.value/.test(tree['~/.discord/.env']))
|
||||
await run(ctx, ['init-discord', '--yes', '--force', '--token', 'new.token.value'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(/new\.token\.value/.test(tree['~/.discord/.env']))
|
||||
})
|
||||
|
||||
test('init-discord --plugin-only installs hello without requiring a token', async (t) => {
|
||||
const run = await loadBin()
|
||||
const tree = {}
|
||||
const ctx = makeCtx(tree)
|
||||
await run(ctx, ['init-discord', '--plugin-only', '--yes'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(/hello/.test(tree['~/.discord/plugins/hello.json']))
|
||||
t.absent(tree['~/.discord/.env'])
|
||||
})
|
||||
Reference in New Issue
Block a user