@@ -687,6 +712,7 @@ function renderSettingsForm() {
$('toggleDisableFileUrls')?.classList.toggle('active', s.disableOnFileUrls === true);
$('toggleExamplesServer')?.classList.toggle('active', s.examplesServerEnabled === true);
$('toggleQvacEnabled')?.classList.toggle('active', s.qvacEnabled === true);
+ $('toggleDiscordEnabled')?.classList.toggle('active', s.discordEnabled === true);
const fw = $('defaultFirewallMode');
if (fw) fw.value = s.defaultFirewallMode || 'off';
const ver = chrome.runtime.getManifest?.().version;
@@ -799,6 +825,7 @@ function collectSettings() {
debug: $('toggleDebug')?.classList.contains('active') || false,
examplesServerEnabled: $('toggleExamplesServer')?.classList.contains('active') || false,
qvacEnabled: $('toggleQvacEnabled')?.classList.contains('active') || false,
+ discordEnabled: $('toggleDiscordEnabled')?.classList.contains('active') || false,
examplesServerPort: port,
defaultFirewallMode: $('defaultFirewallMode')?.value || 'off',
defaultFirewallKeys: keysRaw,
@@ -826,6 +853,8 @@ function saveSettings(quiet) {
$('overviewExamplesToggle')?.classList.toggle('active', settings.examplesServerEnabled);
$('capQvacToggle')?.classList.toggle('active', settings.qvacEnabled === true);
$('capQvacToggle')?.setAttribute('aria-checked', settings.qvacEnabled === true ? 'true' : 'false');
+ $('capDiscordToggle')?.classList.toggle('active', settings.discordEnabled === true);
+ $('capDiscordToggle')?.setAttribute('aria-checked', settings.discordEnabled === true ? 'true' : 'false');
});
}
@@ -1048,6 +1077,31 @@ function setupEvents() {
}
});
+ function persistDiscordEnabled(enabled) {
+ const st = $('toggleDiscordEnabled');
+ if (st) st.classList.toggle('active', enabled);
+ const cap = $('capDiscordToggle');
+ if (cap) {
+ cap.classList.toggle('active', enabled);
+ cap.setAttribute('aria-checked', enabled ? 'true' : 'false');
+ }
+ settings.discordEnabled = enabled;
+ chrome.storage.local.set({ [SETTINGS_KEY]: { ...settings, discordEnabled: enabled } }, () => {
+ if (currentState) renderCapabilitiesPage(currentState);
+ showToast(enabled ? 'Discord enabled' : 'Discord disabled', 'ok');
+ });
+ }
+
+ $('capDiscordToggle')?.addEventListener('click', () => {
+ persistDiscordEnabled(settings.discordEnabled !== true);
+ });
+ $('capDiscordToggle')?.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ e.currentTarget.click();
+ }
+ });
+
function persistQvacEnabled(enabled) {
const st = $('toggleQvacEnabled');
if (st) st.classList.toggle('active', enabled);
diff --git a/extension/discordjs-builders.js b/extension/discordjs-builders.js
new file mode 100644
index 0000000..9db878e
--- /dev/null
+++ b/extension/discordjs-builders.js
@@ -0,0 +1,304 @@
+/**
+ * Page-local discord.js builders (sync, no RPC). Subset used by typical bots.
+ */
+(function (root) {
+ function toJSON() {
+ return this.data ? JSON.parse(JSON.stringify(this.data)) : {};
+ }
+
+ function option(type, name, description, required) {
+ return {
+ type: type,
+ name: name,
+ description: description || name,
+ required: !!required,
+ };
+ }
+
+ function SlashCommandBuilder() {
+ this.data = { name: '', description: '', options: [], type: 1 };
+ }
+ SlashCommandBuilder.prototype.setName = function (name) {
+ this.data.name = String(name);
+ return this;
+ };
+ SlashCommandBuilder.prototype.setDescription = function (desc) {
+ this.data.description = String(desc);
+ return this;
+ };
+ SlashCommandBuilder.prototype.setNameLocalizations = function (loc) {
+ this.data.name_localizations = loc;
+ return this;
+ };
+ SlashCommandBuilder.prototype.setDescriptionLocalizations = function (loc) {
+ this.data.description_localizations = loc;
+ return this;
+ };
+ SlashCommandBuilder.prototype.setDefaultMemberPermissions = function (perm) {
+ this.data.default_member_permissions = perm == null ? null : String(perm);
+ return this;
+ };
+ SlashCommandBuilder.prototype.setDMPermission = function (v) {
+ this.data.dm_permission = !!v;
+ return this;
+ };
+ SlashCommandBuilder.prototype.addStringOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(3)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addIntegerOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(4)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addBooleanOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(5)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addUserOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(6)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addChannelOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(7)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addRoleOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(8)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addNumberOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(10)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.addAttachmentOption = function (input) {
+ const o = typeof input === 'function' ? input(new SlashOpt(11)) : input;
+ this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ return this;
+ };
+ SlashCommandBuilder.prototype.toJSON = toJSON;
+
+ function SlashOpt(type) {
+ this.data = { type: type, name: '', description: '', required: false };
+ }
+ SlashOpt.prototype.setName = function (n) {
+ this.data.name = String(n);
+ return this;
+ };
+ SlashOpt.prototype.setDescription = function (d) {
+ this.data.description = String(d);
+ return this;
+ };
+ SlashOpt.prototype.setRequired = function (v) {
+ this.data.required = !!v;
+ return this;
+ };
+ SlashOpt.prototype.addChoices = function () {
+ const list = [];
+ for (let i = 0; i < arguments.length; i++) list.push(arguments[i]);
+ this.data.choices = list;
+ return this;
+ };
+ SlashOpt.prototype.toJSON = toJSON;
+
+ function EmbedBuilder() {
+ this.data = { type: 'rich' };
+ }
+ EmbedBuilder.prototype.setTitle = function (t) {
+ this.data.title = String(t);
+ return this;
+ };
+ EmbedBuilder.prototype.setDescription = function (d) {
+ this.data.description = String(d);
+ return this;
+ };
+ EmbedBuilder.prototype.setColor = function (c) {
+ this.data.color = typeof c === 'number' ? c : parseInt(String(c).replace('#', ''), 16);
+ return this;
+ };
+ EmbedBuilder.prototype.setURL = function (u) {
+ this.data.url = String(u);
+ return this;
+ };
+ EmbedBuilder.prototype.setTimestamp = function (d) {
+ this.data.timestamp = (d ? new Date(d) : new Date()).toISOString();
+ return this;
+ };
+ EmbedBuilder.prototype.setFooter = function (f) {
+ this.data.footer = typeof f === 'string' ? { text: f } : f;
+ return this;
+ };
+ EmbedBuilder.prototype.setAuthor = function (a) {
+ this.data.author = typeof a === 'string' ? { name: a } : a;
+ return this;
+ };
+ EmbedBuilder.prototype.setThumbnail = function (u) {
+ this.data.thumbnail = typeof u === 'string' ? { url: u } : u;
+ return this;
+ };
+ EmbedBuilder.prototype.setImage = function (u) {
+ this.data.image = typeof u === 'string' ? { url: u } : u;
+ return this;
+ };
+ EmbedBuilder.prototype.addFields = function () {
+ if (!this.data.fields) this.data.fields = [];
+ for (let i = 0; i < arguments.length; i++) {
+ const f = arguments[i];
+ if (Array.isArray(f)) this.data.fields.push.apply(this.data.fields, f);
+ else this.data.fields.push(f);
+ }
+ return this;
+ };
+ EmbedBuilder.prototype.toJSON = toJSON;
+
+ function ActionRowBuilder() {
+ this.data = { type: 1, components: [] };
+ }
+ ActionRowBuilder.prototype.addComponents = function () {
+ for (let i = 0; i < arguments.length; i++) {
+ const c = arguments[i];
+ if (Array.isArray(c)) {
+ for (let j = 0; j < c.length; j++) {
+ this.data.components.push(c[j] && c[j].toJSON ? c[j].toJSON() : c[j]);
+ }
+ } else {
+ this.data.components.push(c && c.toJSON ? c.toJSON() : c);
+ }
+ }
+ return this;
+ };
+ ActionRowBuilder.prototype.toJSON = toJSON;
+
+ const ButtonStyle = { Primary: 1, Secondary: 2, Success: 3, Danger: 4, Link: 5 };
+
+ function ButtonBuilder() {
+ this.data = { type: 2, style: 1 };
+ }
+ ButtonBuilder.prototype.setCustomId = function (id) {
+ this.data.custom_id = String(id);
+ return this;
+ };
+ ButtonBuilder.prototype.setLabel = function (l) {
+ this.data.label = String(l);
+ return this;
+ };
+ ButtonBuilder.prototype.setStyle = function (s) {
+ this.data.style = typeof s === 'number' ? s : ButtonStyle[s] || 1;
+ return this;
+ };
+ ButtonBuilder.prototype.setDisabled = function (v) {
+ this.data.disabled = !!v;
+ return this;
+ };
+ ButtonBuilder.prototype.setURL = function (u) {
+ this.data.url = String(u);
+ this.data.style = 5;
+ return this;
+ };
+ ButtonBuilder.prototype.setEmoji = function (e) {
+ this.data.emoji = typeof e === 'string' ? { name: e } : e;
+ return this;
+ };
+ ButtonBuilder.prototype.toJSON = toJSON;
+
+ function StringSelectMenuBuilder() {
+ this.data = { type: 3, custom_id: '', options: [] };
+ }
+ StringSelectMenuBuilder.prototype.setCustomId = function (id) {
+ this.data.custom_id = String(id);
+ return this;
+ };
+ StringSelectMenuBuilder.prototype.setPlaceholder = function (p) {
+ this.data.placeholder = String(p);
+ return this;
+ };
+ StringSelectMenuBuilder.prototype.addOptions = function () {
+ for (let i = 0; i < arguments.length; i++) {
+ const o = arguments[i];
+ if (Array.isArray(o)) this.data.options.push.apply(this.data.options, o);
+ else this.data.options.push(o && o.toJSON ? o.toJSON() : o);
+ }
+ return this;
+ };
+ StringSelectMenuBuilder.prototype.toJSON = toJSON;
+
+ function AttachmentBuilder(data, extra) {
+ this.attachment = data;
+ this.name = (extra && extra.name) || 'file';
+ this.description = extra && extra.description;
+ }
+ AttachmentBuilder.prototype.setName = function (n) {
+ this.name = String(n);
+ return this;
+ };
+ AttachmentBuilder.prototype.toJSON = function () {
+ return { attachment: this.attachment, name: this.name, description: this.description };
+ };
+
+ const Routes = {
+ applicationCommands: function (id) {
+ return '/applications/' + id + '/commands';
+ },
+ applicationCommand: function (id, cid) {
+ return '/applications/' + id + '/commands/' + cid;
+ },
+ applicationGuildCommands: function (id, gid) {
+ return '/applications/' + id + '/guilds/' + gid + '/commands';
+ },
+ applicationGuildCommand: function (id, gid, cid) {
+ return '/applications/' + id + '/guilds/' + gid + '/commands/' + cid;
+ },
+ channel: function (id) {
+ return '/channels/' + id;
+ },
+ channelMessages: function (id) {
+ return '/channels/' + id + '/messages';
+ },
+ channelMessage: function (id, mid) {
+ return '/channels/' + id + '/messages/' + mid;
+ },
+ gatewayBot: function () {
+ return '/gateway/bot';
+ },
+ user: function (id) {
+ return '/users/' + (id || '@me');
+ },
+ oauth2CurrentApplication: function () {
+ return '/oauth2/applications/@me';
+ },
+ };
+
+ const MessageFlags = { Ephemeral: 64, SuppressEmbeds: 4, SuppressNotifications: 4096 };
+ const ChannelType = {
+ GuildText: 0,
+ DM: 1,
+ GuildVoice: 2,
+ GroupDM: 3,
+ GuildCategory: 4,
+ GuildAnnouncement: 5,
+ AnnouncementThread: 10,
+ PublicThread: 11,
+ PrivateThread: 12,
+ GuildStageVoice: 13,
+ GuildForum: 15,
+ };
+
+ root.BridgeSwarmDiscordBuilders = {
+ SlashCommandBuilder: SlashCommandBuilder,
+ EmbedBuilder: EmbedBuilder,
+ ActionRowBuilder: ActionRowBuilder,
+ ButtonBuilder: ButtonBuilder,
+ StringSelectMenuBuilder: StringSelectMenuBuilder,
+ AttachmentBuilder: AttachmentBuilder,
+ ButtonStyle: ButtonStyle,
+ Routes: Routes,
+ MessageFlags: MessageFlags,
+ ChannelType: ChannelType,
+ };
+})(typeof window !== 'undefined' ? window : globalThis);
diff --git a/extension/discordjs.js b/extension/discordjs.js
new file mode 100644
index 0000000..8703f02
--- /dev/null
+++ b/extension/discordjs.js
@@ -0,0 +1,371 @@
+/**
+ * BridgeSwarm.DiscordJS — page-side discord.js 14 surface.
+ * Real Client/REST live on the Bare host; this file proxies them over native messaging.
+ */
+(function () {
+ function cap(cmd, payload, options) {
+ const bs = window.BridgeSwarm;
+ if (!bs || !bs.capabilities || typeof bs.capabilities.call !== 'function') {
+ return Promise.reject(
+ new Error('BridgeSwarm.capabilities is missing. Reload the extension, then refresh this page.')
+ );
+ }
+ return bs.capabilities.call('discord', cmd, payload || {}, options || {});
+ }
+
+ function EventEmitter() {
+ this._listeners = {};
+ }
+ EventEmitter.prototype.on = function (ev, fn) {
+ if (!this._listeners[ev]) this._listeners[ev] = [];
+ this._listeners[ev].push(fn);
+ return this;
+ };
+ EventEmitter.prototype.once = function (ev, fn) {
+ const self = this;
+ function wrap() {
+ self.off(ev, wrap);
+ return fn.apply(this, arguments);
+ }
+ wrap._orig = fn;
+ return this.on(ev, wrap);
+ };
+ EventEmitter.prototype.off = function (ev, fn) {
+ if (!this._listeners[ev]) return this;
+ this._listeners[ev] = this._listeners[ev].filter(function (x) {
+ return x !== fn && x._orig !== fn;
+ });
+ return this;
+ };
+ EventEmitter.prototype.emit = function (ev) {
+ const args = Array.prototype.slice.call(arguments, 1);
+ const list = (this._listeners[ev] || []).slice();
+ const all = (this._listeners['*'] || []).slice();
+ for (let i = 0; i < list.length; i++) {
+ try {
+ list[i].apply(this, args);
+ } catch (_) {}
+ }
+ for (let i = 0; i < all.length; i++) {
+ try {
+ all[i].apply(this, [ev].concat(args));
+ } catch (_) {}
+ }
+ return this;
+ };
+
+ const STUB_METHODS = [
+ 'reply',
+ 'followUp',
+ 'editReply',
+ 'deferReply',
+ 'deferUpdate',
+ 'deleteReply',
+ 'fetchReply',
+ 'showModal',
+ 'update',
+ 'react',
+ 'delete',
+ 'edit',
+ 'send',
+ 'startThread',
+ 'pin',
+ 'unpin',
+ 'fetch',
+ 'put',
+ 'post',
+ 'get',
+ 'patch',
+ ];
+
+ function encodeArg(arg) {
+ if (arg == null) return arg;
+ if (typeof arg !== 'object') return arg;
+ if (arg._handle) return { _handle: arg._handle };
+ if (typeof arg.toJSON === 'function') return arg.toJSON();
+ if (Array.isArray(arg)) return arg.map(encodeArg);
+ return arg;
+ }
+
+ function hydrate(value) {
+ if (!value || typeof value !== 'object') return value;
+ if (Array.isArray(value)) return value.map(hydrate);
+ if (value._handle) return makeStub(value);
+ return value;
+ }
+
+ function makeStub(snap) {
+ const obj = Object.assign({}, snap);
+ const methods = snap._methods && snap._methods.length ? snap._methods : STUB_METHODS;
+ for (let i = 0; i < methods.length; i++) {
+ (function (name) {
+ if (typeof obj[name] === 'function') return;
+ obj[name] = function () {
+ const args = Array.prototype.slice.call(arguments).map(encodeArg);
+ return cap('call', { handle: snap._handle, path: name, args: args }).then(function (r) {
+ return hydrate(r && r.value);
+ });
+ };
+ })(methods[i]);
+ }
+ const preds = [
+ 'isChatInputCommand',
+ 'isButton',
+ 'isRepliable',
+ 'isStringSelectMenu',
+ 'isAnySelectMenu',
+ 'isModalSubmit',
+ 'isAutocomplete',
+ 'isContextMenuCommand',
+ 'isMessageComponent',
+ 'isCommand',
+ ];
+ for (let i = 0; i < preds.length; i++) {
+ (function (name) {
+ const flag = snap[name];
+ obj[name] = function () {
+ return flag === true || flag === false ? flag : name === 'isRepliable';
+ };
+ })(preds[i]);
+ }
+ if (obj.channel && obj.channel._handle && !obj.channel.send) {
+ obj.channel = makeStub(obj.channel);
+ }
+ return obj;
+ }
+
+ const clients = [];
+
+ function Client(options) {
+ EventEmitter.call(this);
+ const self = this;
+ this.options = options || {};
+ this.user = null;
+ this.readyAt = null;
+ this._handle = null;
+ this._destroyed = false;
+ this._ready = cap('construct', { className: 'Client', args: [this.options] }).then(function (r) {
+ if (!r || !r.handle) throw new Error((r && r.error) || 'Client construct failed');
+ self._handle = r.handle;
+ clients.push(self);
+ return cap('listen', { handle: r.handle }).then(function () {
+ return r;
+ });
+ });
+ }
+ Client.prototype = Object.create(EventEmitter.prototype);
+ Client.prototype.login = function (token) {
+ const self = this;
+ return this._ready.then(function () {
+ return cap(
+ 'call',
+ { handle: self._handle, path: 'login', args: [token] },
+ { timeoutMs: 0 }
+ );
+ }).then(function (r) {
+ return r && r.value != null ? r.value : r;
+ });
+ };
+ Client.prototype.destroy = function () {
+ const self = this;
+ this._destroyed = true;
+ const i = clients.indexOf(this);
+ if (i >= 0) clients.splice(i, 1);
+ if (!this._handle) {
+ return this._ready.then(function () {
+ return cap('destroy', { handle: self._handle });
+ }).catch(function () {});
+ }
+ return cap('destroy', { handle: this._handle }).catch(function () {});
+ };
+ Client.prototype.invoke = function (path, args) {
+ const self = this;
+ return this._ready.then(function () {
+ return cap('call', {
+ handle: self._handle,
+ path: path,
+ args: (args || []).map(encodeArg),
+ }).then(function (r) {
+ return hydrate(r && r.value);
+ });
+ });
+ };
+
+ function REST(options) {
+ this._options = options || {};
+ this._handle = null;
+ this._ready = cap('construct', { className: 'REST', args: [this._options] }).then(function (r) {
+ if (!r || !r.handle) throw new Error((r && r.error) || 'REST construct failed');
+ return r;
+ });
+ const self = this;
+ this._ready.then(function (r) {
+ self._handle = r.handle;
+ });
+ }
+ REST.prototype.setToken = function (token) {
+ const self = this;
+ this._ready = this._ready.then(function (r) {
+ return cap('call', { handle: r.handle, path: 'setToken', args: [token] }).then(function () {
+ return r;
+ });
+ });
+ return this;
+ };
+ function restVerb(name) {
+ REST.prototype[name] = function (route, opts) {
+ const self = this;
+ return this._ready.then(function (r) {
+ return cap(
+ 'call',
+ { handle: r.handle, path: name, args: [route, opts] },
+ { timeoutMs: 0 }
+ ).then(function (res) {
+ return hydrate(res && res.value);
+ });
+ });
+ };
+ }
+ restVerb('put');
+ restVerb('post');
+ restVerb('get');
+ restVerb('patch');
+ REST.prototype.delete = function (route, opts) {
+ const self = this;
+ return this._ready.then(function (r) {
+ return cap('call', { handle: r.handle, path: 'delete', args: [route, opts] }, { timeoutMs: 0 }).then(
+ function (res) {
+ return hydrate(res && res.value);
+ }
+ );
+ });
+ };
+
+ const DiscordJS = {
+ Client: Client,
+ REST: REST,
+ invoke: function (handle, path, args) {
+ return cap('call', { handle: handle, path: path, args: (args || []).map(encodeArg) }).then(
+ function (r) {
+ return hydrate(r && r.value);
+ }
+ );
+ },
+ status: function () {
+ return cap('status', {});
+ },
+ ready: null,
+ };
+
+ function applyBuilders() {
+ const b = window.BridgeSwarmDiscordBuilders || {};
+ const keys = [
+ 'SlashCommandBuilder',
+ 'EmbedBuilder',
+ 'ActionRowBuilder',
+ 'ButtonBuilder',
+ 'StringSelectMenuBuilder',
+ 'AttachmentBuilder',
+ 'ButtonStyle',
+ 'Routes',
+ 'MessageFlags',
+ 'ChannelType',
+ ];
+ for (let i = 0; i < keys.length; i++) {
+ if (b[keys[i]] && DiscordJS[keys[i]] == null) DiscordJS[keys[i]] = b[keys[i]];
+ }
+ }
+ applyBuilders();
+
+ let surfacePromise = null;
+ function ensureSurface() {
+ if (surfacePromise) return surfacePromise;
+ applyBuilders();
+ surfacePromise = cap('surface', {})
+ .then(function (r) {
+ const s = (r && r.surface) || {};
+ Object.keys(s).forEach(function (k) {
+ if (k.charAt(0) === '_') return;
+ if (DiscordJS[k] == null) DiscordJS[k] = s[k];
+ });
+ if (s.Events && s.Events.ClientReady && !s.Events.Ready) {
+ s.Events.Ready = s.Events.ClientReady;
+ DiscordJS.Events = s.Events;
+ }
+ return DiscordJS;
+ })
+ .catch(function (err) {
+ surfacePromise = null;
+ throw err;
+ });
+ return surfacePromise;
+ }
+ DiscordJS.ready = ensureSurface;
+
+ function onChunk(p) {
+ if (!p || p.pack !== 'discord' || p.kind !== 'event') return;
+ for (let i = 0; i < clients.length; i++) {
+ const c = clients[i];
+ if (c._handle !== p.handle) continue;
+ const args = (p.args || []).map(hydrate);
+ if (p.name === 'clientReady' || p.name === 'ready') {
+ const readyClient = args[0];
+ if (readyClient && readyClient.user) c.user = readyClient.user;
+ c.readyAt = new Date();
+ }
+ c.emit.apply(c, [p.name].concat(args));
+ break;
+ }
+ }
+
+ function attach() {
+ if (!window.BridgeSwarm) return false;
+ if (window.BridgeSwarm.DiscordJS && window.BridgeSwarm.DiscordJS.Client === Client) return true;
+ window.BridgeSwarm.DiscordJS = DiscordJS;
+ window.BridgeSwarm.discordJS = DiscordJS;
+ if (window.BridgeSwarm.capabilities && typeof window.BridgeSwarm.capabilities.on === 'function') {
+ window.BridgeSwarm.capabilities.on('cap-chunk', onChunk);
+ }
+ const prevReady = window.BridgeSwarm.ready;
+ if (typeof prevReady === 'function' && !prevReady._bsDiscordWrapped) {
+ const wrapped = function (opts) {
+ return prevReady.call(window.BridgeSwarm, opts).then(function (BS) {
+ return ensureSurface()
+ .then(function () {
+ return BS;
+ })
+ .catch(function () {
+ return BS;
+ });
+ });
+ };
+ wrapped._bsDiscordWrapped = true;
+ window.BridgeSwarm.ready = wrapped;
+ } else if (typeof prevReady !== 'function') {
+ ensureSurface().catch(function () {});
+ }
+ window.addEventListener('pagehide', function () {
+ const list = clients.slice();
+ for (let i = 0; i < list.length; i++) {
+ try {
+ list[i].destroy();
+ } catch (_) {}
+ }
+ });
+ try {
+ window.dispatchEvent(new CustomEvent('bridge-swarm-discord-ready'));
+ } catch (_) {}
+ return true;
+ }
+
+ if (!attach()) {
+ window.addEventListener('bridge-swarm-ready', function () {
+ attach();
+ });
+ let n = 0;
+ const t = setInterval(function () {
+ n += 1;
+ if (attach() || n > 80) clearInterval(t);
+ }, 50);
+ }
+})();
diff --git a/extension/manifest.json b/extension/manifest.json
index ec4bdff..a170278 100644
--- a/extension/manifest.json
+++ b/extension/manifest.json
@@ -37,6 +37,8 @@
{
"resources": [
"api.js",
+ "discordjs-builders.js",
+ "discordjs.js",
"framed-stream.js",
"protomux-bundle.js",
"defaults.js",
diff --git a/extension/manifest_firefox.json b/extension/manifest_firefox.json
index 9663868..58b993d 100644
--- a/extension/manifest_firefox.json
+++ b/extension/manifest_firefox.json
@@ -36,6 +36,8 @@
{
"resources": [
"api.js",
+ "discordjs-builders.js",
+ "discordjs.js",
"framed-stream.js",
"protomux-bundle.js",
"defaults.js",
diff --git a/extension/origin-allowlist.js b/extension/origin-allowlist.js
index 6827bc2..cd3953e 100644
--- a/extension/origin-allowlist.js
+++ b/extension/origin-allowlist.js
@@ -14,7 +14,7 @@
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
const DEFAULT_EXAMPLES_PORT = 4173;
const CAP_PACK_PREFIX =
- /^(media|fs|sqlite|net|qvac|agent)\.[a-zA-Z0-9_-]+$/;
+ /^(media|fs|sqlite|net|qvac|agent|discord)\.[a-zA-Z0-9_-]+$/;
function defaultOrigins(examplesPort) {
const port = Number(examplesPort) > 0 ? Number(examplesPort) : DEFAULT_EXAMPLES_PORT;
@@ -110,13 +110,14 @@
const QVAC_META_CMDS = { detect: 1, status: 1, catalog: 1, openaiStatus: 1 };
const AGENT_META_CMDS = { status: 1, setGrants: 1, list: 1 };
+ const DISCORD_META_CMDS = { surface: 1, status: 1 };
function qvacPackAndCmd(msg) {
if (!msg || !msg.type) return null;
if (msg.type === 'capability' && msg.payload) {
return { pack: msg.payload.pack, cmd: msg.payload.cmd };
}
- const m = typeof msg.type === 'string' && msg.type.match(/^(qvac|agent)\.([a-zA-Z0-9_-]+)$/);
+ const m = typeof msg.type === 'string' && msg.type.match(/^(qvac|agent|discord)\.([a-zA-Z0-9_-]+)$/);
if (m) return { pack: m[1], cmd: m[2] };
return null;
}
@@ -133,6 +134,17 @@
return true;
}
+ /** True when Discord construct/login must be blocked (user has not enabled Discord).
+ * discord.setEnabled is never allowed from pages. surface/status stay available. */
+ function isDiscordDisabled(msg, settings) {
+ const pc = qvacPackAndCmd(msg);
+ if (pc && pc.pack === 'discord' && pc.cmd === 'setEnabled') return true;
+ if (settings && settings.discordEnabled === true) return false;
+ if (!pc || pc.pack !== 'discord') return false;
+ if (DISCORD_META_CMDS[pc.cmd]) return false;
+ return true;
+ }
+
return {
DEFAULT_EXAMPLES_PORT,
defaultOrigins,
@@ -146,5 +158,6 @@
isCapabilityType,
qvacPackAndCmd,
isQvacDisabled,
+ isDiscordDisabled,
};
});
diff --git a/native-host/boot.mjs b/native-host/boot.mjs
index 05053cd..0b9c5de 100644
--- a/native-host/boot.mjs
+++ b/native-host/boot.mjs
@@ -65,6 +65,10 @@ export const DEFAULT_ADDON_PACKAGES = [
'@qvac/llm-llamacpp',
'@qvac/embed-llamacpp',
'bare-gpu-info',
+ // Discord (bare-discord-js / discord.js gateway)
+ 'bare-tls',
+ 'bare-crypto',
+ 'bare-zlib',
];
/** @deprecated Use DEFAULT_ADDON_PACKAGES — media is default. */
diff --git a/native-host/capabilities/discord.js b/native-host/capabilities/discord.js
new file mode 100644
index 0000000..a898a21
--- /dev/null
+++ b/native-host/capabilities/discord.js
@@ -0,0 +1,367 @@
+/**
+ * Discord capability pack — real discord.js Client on the Bare host.
+ * Page talks to it as BridgeSwarm.DiscordJS.
+ */
+'use strict';
+
+const {
+ loadBareDiscordJsSync,
+ getBareDiscordJsLoadError,
+ normalizeDiscordToken,
+} = require('../discord/load-discord.js');
+const {
+ intern,
+ snapshotValue,
+ snapshotEventArgs,
+ serializeConstants,
+ createHandleTable,
+} = require('../discord/snapshot.js');
+
+const MAX_CLIENTS = 4;
+const SKIP_EVENTS = new Set(['raw', 'debug', 'apiRequest', 'apiResponse']);
+const CONSTRUCT_ALLOWED = new Set(['Client', 'REST', 'WebhookClient']);
+
+let enabled = false;
+const handles = createHandleTable();
+/** handle -> { names: Set, listener } */
+const listeners = new Map();
+let clientCount = 0;
+
+function logErr(msg) {
+ try {
+ if (process.stderr) process.stderr.write('[bridge-swarm-host] discord: ' + msg + '\n');
+ } catch (_) {}
+}
+
+function getDiscord() {
+ const d = loadBareDiscordJsSync();
+ if (!d || typeof d.Client !== 'function') {
+ const err = getBareDiscordJsLoadError() || 'bare-discord-js unavailable';
+ throw new Error(err);
+ }
+ return d;
+}
+
+function decodeArg(arg) {
+ if (arg && typeof arg === 'object' && typeof arg._handle === 'string' && handles.byId.has(arg._handle)) {
+ return handles.byId.get(arg._handle);
+ }
+ if (Array.isArray(arg)) return arg.map(decodeArg);
+ return arg;
+}
+
+function decodeArgs(args) {
+ if (!Array.isArray(args)) return [];
+ return args.map(decodeArg);
+}
+
+function sanitizeConstructArgs(className, args) {
+ const a = Array.isArray(args) ? args.map((x) => decodeArg(x)) : [];
+ if (className === 'Client' && a[0] && typeof a[0] === 'object') {
+ const o = a[0];
+ const clean = {};
+ if (o.intents != null) clean.intents = o.intents;
+ if (o.partials != null) clean.partials = o.partials;
+ if (o.failIfNotExists != null) clean.failIfNotExists = o.failIfNotExists;
+ if (o.presence && typeof o.presence === 'object') clean.presence = o.presence;
+ a[0] = clean;
+ }
+ if ((className === 'REST' || className === 'WebhookClient') && a[0] && typeof a[0] === 'object') {
+ const o = a[0];
+ const clean = {};
+ if (typeof o.version === 'number' || typeof o.version === 'string') clean.version = o.version;
+ if (typeof o.timeout === 'number') clean.timeout = o.timeout;
+ if (typeof o.authPrefix === 'string') clean.authPrefix = o.authPrefix;
+ a[0] = clean;
+ }
+ return a;
+}
+
+function getAtPath(obj, path) {
+ const parts = String(path || '').split('.').filter(Boolean);
+ let ctx = obj;
+ let fn = obj;
+ for (let i = 0; i < parts.length; i++) {
+ ctx = fn;
+ if (fn == null) return { ctx: null, fn: undefined };
+ fn = fn[parts[i]];
+ }
+ return { ctx, fn };
+}
+
+function wrapResult(result) {
+ if (result == null || typeof result !== 'object') return { value: result };
+ if (typeof result.then === 'function') {
+ return Promise.resolve(result).then(wrapResult);
+ }
+ return { value: snapshotValue(result, handles, 0) };
+}
+
+function dropHandle(handle) {
+ const obj = handles.byId.get(handle);
+ const rec = listeners.get(handle);
+ if (rec && obj && typeof obj.off === 'function') {
+ try {
+ obj.off(rec.event || '*');
+ } catch (_) {}
+ }
+ if (rec && rec.unlisten) {
+ try {
+ rec.unlisten();
+ } catch (_) {}
+ }
+ listeners.delete(handle);
+ if (obj) {
+ const type = (obj.constructor && obj.constructor.name) || '';
+ if (type === 'Client') {
+ clientCount = Math.max(0, clientCount - 1);
+ if (typeof obj.destroy === 'function') {
+ Promise.resolve(obj.destroy()).catch(() => {});
+ }
+ }
+ }
+ handles.byId.delete(handle);
+ handles.meta.delete(handle);
+}
+
+function destroyAll() {
+ const ids = Array.from(handles.byId.keys());
+ for (const id of ids) dropHandle(id);
+ clientCount = 0;
+}
+
+function refuseDisabled(ctx, allowMeta) {
+ if (enabled) return false;
+ if (allowMeta) return false;
+ ctx.reply({ ok: false, error: 'Discord is disabled. Enable it in BridgeSwarm Settings.' });
+ return true;
+}
+
+function createDiscordPack() {
+ const commands = {
+ async status(ctx) {
+ let loaded = false;
+ let loadError = '';
+ try {
+ const d = loadBareDiscordJsSync();
+ loaded = !!(d && typeof d.Client === 'function');
+ if (!loaded) loadError = getBareDiscordJsLoadError();
+ } catch (err) {
+ loadError = err && err.message ? err.message : String(err);
+ }
+ ctx.reply({
+ ok: true,
+ enabled,
+ loaded,
+ loadError: loadError || undefined,
+ clients: clientCount,
+ maxClients: MAX_CLIENTS,
+ });
+ },
+
+ async setEnabled(ctx) {
+ const on = !!(ctx.payload && ctx.payload.enabled === true);
+ enabled = on;
+ if (!on) destroyAll();
+ ctx.reply({ ok: true, enabled, clients: clientCount });
+ },
+
+ async surface(ctx) {
+ try {
+ const discord = getDiscord();
+ ctx.reply({
+ ok: true,
+ enabled,
+ loaded: true,
+ surface: serializeConstants(discord),
+ });
+ } catch (err) {
+ ctx.reply({
+ ok: false,
+ error: err.message,
+ enabled,
+ loaded: false,
+ loadError: getBareDiscordJsLoadError() || err.message,
+ });
+ }
+ },
+
+ async construct(ctx) {
+ if (refuseDisabled(ctx)) return;
+ try {
+ const discord = getDiscord();
+ const className = String((ctx.payload && ctx.payload.className) || 'Client');
+ if (!CONSTRUCT_ALLOWED.has(className)) {
+ ctx.reply({ ok: false, error: 'class not allowed: ' + className });
+ return;
+ }
+ const Ctor = discord[className];
+ if (typeof Ctor !== 'function') {
+ ctx.reply({ ok: false, error: className + ' is not available' });
+ return;
+ }
+ if (className === 'Client' && clientCount >= MAX_CLIENTS) {
+ ctx.reply({ ok: false, error: 'too many Discord clients (max ' + MAX_CLIENTS + ')' });
+ return;
+ }
+ const args = sanitizeConstructArgs(className, (ctx.payload && ctx.payload.args) || []);
+ const inst = new Ctor(...args);
+ const handle = intern(handles, inst);
+ if (className === 'Client') clientCount += 1;
+ handles.meta.get(handle).origin = ctx.payload && ctx.payload._origin;
+ ctx.reply({
+ ok: true,
+ handle,
+ _type: className,
+ _methods: className === 'Client' ? ['login', 'destroy'] : ['setToken', 'put', 'post', 'get', 'patch', 'delete'],
+ });
+ } catch (err) {
+ ctx.reply({ ok: false, error: err.message });
+ }
+ },
+
+ async call(ctx) {
+ if (refuseDisabled(ctx)) return;
+ try {
+ const handle = ctx.payload && ctx.payload.handle;
+ const path = (ctx.payload && ctx.payload.path) || '';
+ const obj = handles.byId.get(handle);
+ if (!obj) {
+ ctx.reply({ ok: false, error: 'unknown handle' });
+ return;
+ }
+ let args = decodeArgs((ctx.payload && ctx.payload.args) || []);
+ if (path === 'login' || path === 'setToken') {
+ if (typeof args[0] === 'string') args[0] = normalizeDiscordToken(args[0]);
+ }
+ const { ctx: recv, fn } = getAtPath(obj, path);
+ if (typeof fn !== 'function') {
+ ctx.reply({ ok: true, value: snapshotValue(fn, handles, 0) });
+ return;
+ }
+ const result = await wrapResult(fn.apply(recv, args));
+ ctx.reply(Object.assign({ ok: true }, result));
+ } catch (err) {
+ ctx.reply({ ok: false, error: err.message });
+ }
+ },
+
+ async get(ctx) {
+ if (refuseDisabled(ctx)) return;
+ try {
+ const handle = ctx.payload && ctx.payload.handle;
+ const path = (ctx.payload && ctx.payload.path) || '';
+ const obj = handles.byId.get(handle);
+ if (!obj) {
+ ctx.reply({ ok: false, error: 'unknown handle' });
+ return;
+ }
+ const { fn } = getAtPath(obj, path);
+ ctx.reply({ ok: true, value: snapshotValue(fn, handles, 0) });
+ } catch (err) {
+ ctx.reply({ ok: false, error: err.message });
+ }
+ },
+
+ async listen(ctx) {
+ if (refuseDisabled(ctx)) return;
+ const handle = ctx.payload && ctx.payload.handle;
+ const obj = handles.byId.get(handle);
+ if (!obj || typeof obj.on !== 'function') {
+ ctx.reply({ ok: false, error: 'handle is not an EventEmitter' });
+ return;
+ }
+ if (listeners.has(handle)) {
+ ctx.reply({ ok: true, handle, listening: true });
+ return;
+ }
+ const emit = ctx.emit;
+ const includeDebug = !!(ctx.payload && ctx.payload.debug);
+ const listener = function (eventName) {
+ if (!includeDebug && SKIP_EVENTS.has(String(eventName))) return;
+ const args = Array.prototype.slice.call(arguments, 1);
+ let payload;
+ try {
+ payload = snapshotEventArgs(args, handles);
+ } catch (err) {
+ payload = [{ _type: 'Error', message: err.message }];
+ }
+ try {
+ emit('cap-chunk', {
+ pack: 'discord',
+ kind: 'event',
+ handle,
+ name: eventName,
+ args: payload,
+ });
+ } catch (err) {
+ logErr('emit failed: ' + (err && err.message));
+ }
+ };
+ obj.on('error', function (err) {
+ listener('error', err);
+ });
+ if (typeof obj.on === 'function') {
+ obj.on('*', function () {});
+ }
+ const origEmit = obj.emit;
+ if (typeof origEmit === 'function') {
+ obj.emit = function (eventName) {
+ try {
+ listener.apply(null, arguments);
+ } catch (_) {}
+ return origEmit.apply(obj, arguments);
+ };
+ }
+ listeners.set(handle, {
+ unlisten() {
+ if (typeof origEmit === 'function') obj.emit = origEmit;
+ },
+ });
+ ctx.reply({ ok: true, handle, listening: true });
+ },
+
+ async destroy(ctx) {
+ const handle = ctx.payload && ctx.payload.handle;
+ if (handle) {
+ dropHandle(handle);
+ ctx.reply({ ok: true, handle });
+ return;
+ }
+ destroyAll();
+ ctx.reply({ ok: true, clients: 0 });
+ },
+ };
+
+ return {
+ id: 'discord',
+ commands,
+ getPublicStatus() {
+ let loaded = false;
+ try {
+ const d = loadBareDiscordJsSync();
+ loaded = !!(d && typeof d.Client === 'function');
+ } catch (_) {}
+ return {
+ installed: true,
+ enabled,
+ clients: clientCount,
+ maxClients: MAX_CLIENTS,
+ loaded,
+ loadError: getBareDiscordJsLoadError() || undefined,
+ };
+ },
+ onLoad() {},
+ cleanup() {
+ destroyAll();
+ },
+ };
+}
+
+module.exports = {
+ createDiscordPack,
+ normalizeDiscordToken,
+ serializeConstants: require('../discord/snapshot.js').serializeConstants,
+ snapshotValue: require('../discord/snapshot.js').snapshotValue,
+ createHandleTable: require('../discord/snapshot.js').createHandleTable,
+};
diff --git a/native-host/discord/load-discord.js b/native-host/discord/load-discord.js
new file mode 100644
index 0000000..bb88cee
--- /dev/null
+++ b/native-host/discord/load-discord.js
@@ -0,0 +1,139 @@
+/**
+ * Load vendored bare-discord-js. CJS only — the ESM entry uses node:module.
+ * Apply WHATWG WS + FormData before requiring the package.
+ */
+'use strict';
+
+const { takePackedDiscordJs, unwrapDiscordModule } = require('./registry.js');
+
+let discordJsCache;
+let discordJsTried = false;
+let discordJsLastError = '';
+
+function getBareDiscordJsLoadError() {
+ return discordJsLastError;
+}
+
+function noteDiscordLoadError(err) {
+ const msg =
+ err && typeof err === 'object' && err.message
+ ? String(err.message || err)
+ : String(err || 'unknown error');
+ discordJsLastError = msg.slice(0, 800);
+ return discordJsLastError;
+}
+
+function bareRuntime() {
+ if (typeof globalThis.Bare !== 'undefined') return true;
+ const v = globalThis.process && globalThis.process.versions;
+ return Boolean(v && typeof v.bare === 'string');
+}
+
+function normalizeDiscordToken(raw) {
+ if (typeof raw !== 'string') return '';
+ let t = raw.trim();
+ if (t.charCodeAt(0) === 0xfeff) t = t.slice(1);
+ t = t.replace(/[\u200B-\u200D\uFEFF]/g, '');
+ return t.trim();
+}
+
+function applyBareProcessEmitWarning() {
+ const proc = globalThis.process;
+ if (!proc || typeof proc.emitWarning === 'function') return;
+ proc.emitWarning = function emitWarning(warning, type, code) {
+ const msg = warning instanceof Error ? warning.message : String(warning);
+ const name = typeof type === 'string' ? type : 'Warning';
+ const id = typeof code === 'string' ? code : '';
+ const line = id ? name + ' [' + id + ']: ' + msg : name + ': ' + msg;
+ try {
+ if (typeof proc.emit === 'function') proc.emit('warning', warning);
+ } catch (_) {}
+ try {
+ console.error(line);
+ } catch (_) {}
+ };
+}
+
+function applyBareTlsCompat() {
+ const proc = globalThis.process;
+ if (!proc || !proc.env) return;
+ if (!proc.env.NODE_TLS_REJECT_UNAUTHORIZED) {
+ proc.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
+ }
+ try {
+ const req = globalThis.require;
+ if (typeof req !== 'function') return;
+ const bareHttps = req('bare-https');
+ if (!bareHttps || typeof bareHttps.Agent !== 'function') return;
+ const insecureAgent = new bareHttps.Agent({ rejectUnauthorized: false });
+ bareHttps.globalAgent = insecureAgent;
+ if (bareHttps.Agent) bareHttps.Agent.global = insecureAgent;
+ } catch (_) {}
+}
+
+function applyAdapters() {
+ applyBareProcessEmitWarning();
+ if (bareRuntime()) applyBareTlsCompat();
+ try {
+ const ws = require('../vendor/bare-discord-js/src/adapters/whatwg-ws.cjs');
+ if (typeof ws.installBareOsDiscordGatewayWs === 'function') {
+ ws.installBareOsDiscordGatewayWs();
+ }
+ } catch (_) {}
+ try {
+ const fd = require('../vendor/bare-discord-js/src/adapters/form-data.cjs');
+ if (typeof fd.installBareOsFormDataGlobal === 'function') {
+ fd.installBareOsFormDataGlobal();
+ }
+ } catch (_) {}
+}
+
+function loadBareDiscordJsSync() {
+ applyAdapters();
+ if (discordJsTried) return discordJsCache;
+ discordJsTried = true;
+ discordJsLastError = '';
+
+ let lastErr = null;
+ try {
+ const packed = unwrapDiscordModule(takePackedDiscordJs());
+ if (packed) {
+ discordJsCache = packed;
+ return discordJsCache;
+ }
+ } catch (err) {
+ lastErr = err;
+ }
+
+ try {
+ const mod = require('bare-discord-js');
+ const discord = unwrapDiscordModule(mod);
+ if (discord) {
+ discordJsCache = discord;
+ return discordJsCache;
+ }
+ if (!lastErr) lastErr = new Error('require("bare-discord-js") returned no Client');
+ } catch (err) {
+ lastErr = err;
+ }
+
+ noteDiscordLoadError(lastErr || 'vendored bare-discord-js unresolved');
+ try {
+ if (process.stderr) {
+ process.stderr.write(
+ '[bridge-swarm-host] BridgeSwarm.DiscordJS: failed to load bare-discord-js: ' +
+ discordJsLastError +
+ '\n'
+ );
+ }
+ } catch (_) {}
+ discordJsCache = undefined;
+ return undefined;
+}
+
+module.exports = {
+ loadBareDiscordJsSync,
+ getBareDiscordJsLoadError,
+ normalizeDiscordToken,
+ unwrapDiscordModule,
+};
diff --git a/native-host/discord/packed.mjs b/native-host/discord/packed.mjs
new file mode 100644
index 0000000..4fb6a70
--- /dev/null
+++ b/native-host/discord/packed.mjs
@@ -0,0 +1,34 @@
+/**
+ * Standalone pack binding. Static import so bare-pack rewrites discord.js
+ * into the host bundle. Adapters in ws-bootstrap.cjs must load first.
+ * Avoid Node `module` / createRequire — those are missing in the packed graph.
+ */
+import discord from 'discord.js';
+import registry from './registry.js';
+
+const registerPackedDiscordJs =
+ (registry && registry.registerPackedDiscordJs) ||
+ (registry && registry.default && registry.default.registerPackedDiscordJs);
+
+function unwrap(mod) {
+ if (mod && typeof mod.Client === 'function') return mod;
+ const def = mod && mod.default;
+ if (def && typeof def === 'object' && typeof def.Client === 'function') return def;
+ return null;
+}
+
+function applyShims(d) {
+ if (!d || typeof d !== 'object') return d;
+ const events = d.Events;
+ if (events && events.ClientReady && events.Ready == null) {
+ events.Ready = events.ClientReady;
+ }
+ return d;
+}
+
+if (typeof registerPackedDiscordJs !== 'function') {
+ throw new Error('discord registry missing registerPackedDiscordJs');
+}
+
+export const bareDiscordJs = registerPackedDiscordJs(applyShims(unwrap(discord)));
+export default bareDiscordJs;
diff --git a/native-host/discord/registry.js b/native-host/discord/registry.js
new file mode 100644
index 0000000..0d3a30e
--- /dev/null
+++ b/native-host/discord/registry.js
@@ -0,0 +1,29 @@
+/**
+ * Live binding filled by packed.js when that file is statically imported
+ * so bare-pack rewrites discord.js into the host bundle.
+ */
+const registry = { module: null };
+
+function registerPackedDiscordJs(mod) {
+ const unwrapped = unwrapDiscordModule(mod);
+ registry.module = unwrapped || null;
+ return registry.module;
+}
+
+function takePackedDiscordJs() {
+ return registry.module;
+}
+
+function unwrapDiscordModule(mod) {
+ if (!mod || typeof mod !== 'object') return undefined;
+ if (typeof mod.Client === 'function') return mod;
+ const def = mod.default;
+ if (def && typeof def === 'object' && typeof def.Client === 'function') return def;
+ return undefined;
+}
+
+module.exports = {
+ registerPackedDiscordJs,
+ takePackedDiscordJs,
+ unwrapDiscordModule,
+};
diff --git a/native-host/discord/snapshot.js b/native-host/discord/snapshot.js
new file mode 100644
index 0000000..a7eaaa3
--- /dev/null
+++ b/native-host/discord/snapshot.js
@@ -0,0 +1,226 @@
+/**
+ * Serialize discord.js structures for native messaging (JSON, ~1 MB cap).
+ */
+'use strict';
+
+const PREDICATES = [
+ 'isChatInputCommand',
+ 'isButton',
+ 'isRepliable',
+ 'isStringSelectMenu',
+ 'isUserSelectMenu',
+ 'isRoleSelectMenu',
+ 'isMentionableSelectMenu',
+ 'isChannelSelectMenu',
+ 'isAnySelectMenu',
+ 'isModalSubmit',
+ 'isAutocomplete',
+ 'isContextMenuCommand',
+ 'isUserContextMenuCommand',
+ 'isMessageContextMenuCommand',
+ 'isMessageComponent',
+ 'isCommand',
+];
+
+const STUB_METHODS = [
+ 'reply',
+ 'followUp',
+ 'editReply',
+ 'deferReply',
+ 'deferUpdate',
+ 'deleteReply',
+ 'fetchReply',
+ 'showModal',
+ 'update',
+ 'react',
+ 'delete',
+ 'edit',
+ 'send',
+ 'startThread',
+ 'pin',
+ 'unpin',
+ 'fetch',
+ 'setToken',
+ 'put',
+ 'post',
+ 'get',
+ 'patch',
+ 'login',
+ 'destroy',
+];
+
+function intern(handles, obj) {
+ if (!obj || typeof obj !== 'object') return null;
+ if (obj.__bsHandle && handles.byId.has(obj.__bsHandle)) return obj.__bsHandle;
+ const id = handles.nextId++;
+ const handle = 'dj_' + id;
+ obj.__bsHandle = handle;
+ handles.byId.set(handle, obj);
+ handles.meta.set(handle, {
+ type: (obj.constructor && obj.constructor.name) || 'Object',
+ });
+ return handle;
+}
+
+function jsonSafe(value, depth) {
+ if (depth > 5) return undefined;
+ if (value == null) return value;
+ const t = typeof value;
+ if (t === 'string' || t === 'number' || t === 'boolean') return value;
+ if (t === 'bigint') return value.toString();
+ if (t === 'function') return undefined;
+ if (Array.isArray(value)) {
+ const out = [];
+ for (let i = 0; i < value.length && i < 50; i++) {
+ const item = jsonSafe(value[i], depth + 1);
+ if (item !== undefined) out.push(item);
+ }
+ return out;
+ }
+ if (t !== 'object') return String(value);
+ if (typeof value.toJSON === 'function') {
+ try {
+ return jsonSafe(value.toJSON(), depth + 1);
+ } catch (_) {}
+ }
+ const out = {};
+ let n = 0;
+ for (const key of Object.keys(value)) {
+ if (n++ > 80) break;
+ if (key.charAt(0) === '_') continue;
+ const v = jsonSafe(value[key], depth + 1);
+ if (v !== undefined) out[key] = v;
+ }
+ return out;
+}
+
+function snapshotValue(value, handles, depth) {
+ if (depth > 4) return jsonSafe(value, 0);
+ if (value == null || typeof value !== 'object') return jsonSafe(value, 0);
+ if (typeof value.then === 'function') return { _type: 'Promise' };
+
+ const ctor = value.constructor && value.constructor.name;
+ const looksLive =
+ typeof value.reply === 'function' ||
+ typeof value.login === 'function' ||
+ typeof value.setToken === 'function' ||
+ typeof value.toJSON === 'function' ||
+ (ctor && ctor !== 'Object' && ctor !== 'Array');
+
+ if (!looksLive) return jsonSafe(value, 0);
+
+ const handle = intern(handles, value);
+ let json = {};
+ if (typeof value.toJSON === 'function') {
+ try {
+ json = jsonSafe(value.toJSON(), 0) || {};
+ } catch (_) {
+ json = {};
+ }
+ } else {
+ json = jsonSafe(value, 1) || {};
+ }
+
+ const pred = {};
+ for (const name of PREDICATES) {
+ if (typeof value[name] === 'function') {
+ try {
+ pred[name] = !!value[name]();
+ } catch (_) {}
+ }
+ }
+
+ const methods = [];
+ for (const name of STUB_METHODS) {
+ if (typeof value[name] === 'function') methods.push(name);
+ }
+
+ const snap = Object.assign({ _handle: handle, _type: ctor || 'Object', _methods: methods }, json, pred);
+
+ if (value.user && typeof value.user === 'object') {
+ snap.user = jsonSafe(value.user, 0);
+ if (value.user.tag) snap.user.tag = value.user.tag;
+ if (value.user.id) snap.user.id = value.user.id;
+ }
+ if (value.author && typeof value.author === 'object') {
+ snap.author = jsonSafe(value.author, 0);
+ }
+ if (value.channel && typeof value.channel === 'object') {
+ snap.channel = {
+ id: value.channel.id,
+ type: value.channel.type,
+ name: value.channel.name,
+ _handle: intern(handles, value.channel),
+ _type: (value.channel.constructor && value.channel.constructor.name) || 'Channel',
+ _methods: typeof value.channel.send === 'function' ? ['send'] : [],
+ };
+ }
+ if (value.guild && typeof value.guild === 'object') {
+ snap.guild = {
+ id: value.guild.id,
+ name: value.guild.name,
+ _handle: intern(handles, value.guild),
+ _type: 'Guild',
+ };
+ }
+ if (value.commandName != null) snap.commandName = value.commandName;
+ if (value.customId != null) snap.customId = value.customId;
+ if (value.content != null) snap.content = value.content;
+
+ return snap;
+}
+
+function snapshotEventArgs(args, handles) {
+ const out = [];
+ for (let i = 0; i < args.length && i < 6; i++) {
+ const a = args[i];
+ if (a instanceof Error) {
+ out.push({ _type: 'Error', message: a.message, code: a.code });
+ } else {
+ out.push(snapshotValue(a, handles, 0));
+ }
+ }
+ return out;
+}
+
+function serializeConstants(discord) {
+ const out = { _classes: [] };
+ if (!discord || typeof discord !== 'object') return out;
+ for (const key of Object.keys(discord)) {
+ const v = discord[key];
+ if (typeof v === 'function') {
+ out._classes.push(key);
+ continue;
+ }
+ if (v && typeof v === 'object') {
+ const plain = {};
+ let n = 0;
+ for (const k of Object.keys(v)) {
+ if (n++ > 200) break;
+ const x = v[k];
+ if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {
+ plain[k] = x;
+ }
+ }
+ if (Object.keys(plain).length) out[key] = plain;
+ } else if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
+ out[key] = v;
+ }
+ }
+ return out;
+}
+
+function createHandleTable() {
+ return { nextId: 1, byId: new Map(), meta: new Map() };
+}
+
+module.exports = {
+ PREDICATES,
+ STUB_METHODS,
+ intern,
+ snapshotValue,
+ snapshotEventArgs,
+ serializeConstants,
+ createHandleTable,
+ jsonSafe,
+};
diff --git a/native-host/discord/stubs/zlib-sync/index.js b/native-host/discord/stubs/zlib-sync/index.js
new file mode 100644
index 0000000..0d1e891
--- /dev/null
+++ b/native-host/discord/stubs/zlib-sync/index.js
@@ -0,0 +1,8 @@
+'use strict';
+/**
+ * Optional native. discord.js does `zlib = require('zlib-sync')` then
+ * `compression: zlib ? ZlibStream : null`. A truthy empty object enables
+ * zlib-stream, after which `new zlib.Inflate()` throws and Client.login()
+ * hangs forever.
+ */
+module.exports = null;
diff --git a/native-host/discord/stubs/zlib-sync/package.json b/native-host/discord/stubs/zlib-sync/package.json
new file mode 100644
index 0000000..2c32369
--- /dev/null
+++ b/native-host/discord/stubs/zlib-sync/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "zlib-sync",
+ "version": "0.0.0",
+ "description": "Null stub: a truthy empty zlib-sync hangs discord.js Client.login forever",
+ "main": "./index.js",
+ "private": true
+}
diff --git a/native-host/discord/ws-bootstrap.cjs b/native-host/discord/ws-bootstrap.cjs
new file mode 100644
index 0000000..d6439fd
--- /dev/null
+++ b/native-host/discord/ws-bootstrap.cjs
@@ -0,0 +1,31 @@
+/**
+ * MUST load before discord.js / @discordjs/ws evaluation.
+ * Forces WHATWG WebSocket (bare-ws), FormData/Blob/File, and TLS defaults
+ * so IDENTIFY produces READY. CJS so bare-pack does not need Node `module`.
+ */
+'use strict';
+
+function apply() {
+ try {
+ const ws = require('../vendor/bare-discord-js/src/adapters/whatwg-ws.cjs');
+ const install =
+ (ws && ws.installBareOsDiscordGatewayWs) ||
+ (ws && ws.default && ws.default.installBareOsDiscordGatewayWs);
+ if (typeof install === 'function') install();
+ } catch (_) {}
+ try {
+ const fd = require('../vendor/bare-discord-js/src/adapters/form-data.cjs');
+ const installFd =
+ (fd && fd.installBareOsFormDataGlobal) ||
+ (fd && fd.default && fd.default.installBareOsFormDataGlobal);
+ if (typeof installFd === 'function') installFd();
+ } catch (_) {}
+ try {
+ const proc = globalThis.process;
+ if (proc && proc.env && !proc.env.NODE_TLS_REJECT_UNAUTHORIZED) {
+ proc.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
+ }
+ } catch (_) {}
+}
+
+apply();
diff --git a/native-host/host.js b/native-host/host.js
index 8b26318..0fee371 100644
--- a/native-host/host.js
+++ b/native-host/host.js
@@ -1735,6 +1735,7 @@ async function handleMessageAsync(send, msg) {
packs: capabilities.listPackIds(),
qvac: packStatus('qvac'),
agent: packStatus('agent'),
+ discord: packStatus('discord'),
examples: examplesServer.status(),
streamPort: streamPort.status(),
swarmCount: swarms.size,
diff --git a/native-host/index.mjs b/native-host/index.mjs
index dd98b7e..58f3daa 100644
--- a/native-host/index.mjs
+++ b/native-host/index.mjs
@@ -1,7 +1,7 @@
/**
* BridgeSwarm native messaging host entrypoint.
*
- * Includes default capability packs (media, fs, sqlite, net) and curated Bare
+ * Includes default capability packs (media, fs, sqlite, net, qvac, agent, discord) and curated Bare
* modules — see docs/DEFAULT-MODULES.md.
*
* bare-process/global must be the very first import so that `process` is
diff --git a/native-host/package-lock.json b/native-host/package-lock.json
index ac07dd0..f4f5f2f 100644
--- a/native-host/package-lock.json
+++ b/native-host/package-lock.json
@@ -18,13 +18,19 @@
"autobase": "^7.28.1",
"b4a": "^1.8.1",
"bare": "^1.30.3",
+ "bare-crypto": "^1.15.3",
+ "bare-discord-js": "file:./vendor/bare-discord-js",
"bare-fetch": "^3.2.0",
"bare-ffmpeg": "^1.5.0",
+ "bare-form-data": "^1.2.2",
"bare-fs": "^4.7.4",
"bare-gpu-info": "0.1.1",
"bare-http1": "^4.5.7",
+ "bare-https": "^3.0.0",
"bare-media": "^2.10.1",
"bare-module": "^6.4.0",
+ "bare-net": "^2.3.3",
+ "bare-node-runtime": "^1.5.0",
"bare-os": "^3.9.3",
"bare-path": "^3.1.1",
"bare-process": "^4.5.1",
@@ -32,8 +38,12 @@
"bare-sqlite": "^0.1.4",
"bare-stream": "^2.13.3",
"bare-subprocess": "^6.1.0",
+ "bare-tls": "^3.1.8",
+ "bare-ws": "^3.0.0",
+ "bare-zlib": "^1.4.1",
"compact-encoding": "^3.3.0",
"corestore": "^7.11.1",
+ "discord.js": "^14.27.0",
"hrpc": "^4.3.0",
"hyperbee": "^2.27.3",
"hypercore": "^11.35.0",
@@ -41,12 +51,146 @@
"hyperdrive": "^13.3.3",
"hyperschema": "^1.21.0",
"hyperswarm": "^4.17.0",
- "protomux": "^3.11.0"
+ "protomux": "^3.11.0",
+ "zlib-sync": "file:./discord/stubs/zlib-sync"
},
"engines": {
"bare": ">=1.30.3"
}
},
+ "discord/stubs/zlib-sync": {
+ "version": "0.0.0"
+ },
+ "node_modules/@discordjs/builders": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.14.1.tgz",
+ "integrity": "sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@discordjs/formatters": "^0.6.2",
+ "@discordjs/util": "^1.2.0",
+ "@sapphire/shapeshift": "^4.0.0",
+ "discord-api-types": "^0.38.40",
+ "fast-deep-equal": "^3.1.3",
+ "ts-mixer": "^6.0.4",
+ "tslib": "^2.6.3"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/collection": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
+ "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/formatters": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.2.tgz",
+ "integrity": "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "discord-api-types": "^0.38.33"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/rest": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.3.tgz",
+ "integrity": "sha512-wvOylxNYJkwKjctS/Mn5GP1w9r3/rzyH+ThD1JlAca6zEdlHs8QWBBUQJpU5Q+W6DoIj/Ljh1IPlZs7hTU+UAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@discordjs/collection": "^2.1.1",
+ "@discordjs/util": "^1.2.0",
+ "@sapphire/async-queue": "^1.5.3",
+ "@sapphire/snowflake": "^3.5.5",
+ "@vladfrangu/async_event_emitter": "^2.4.6",
+ "discord-api-types": "^0.38.50",
+ "magic-bytes.js": "^1.13.0",
+ "tslib": "^2.6.3",
+ "undici": "^6.27.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
+ "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/util": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.2.0.tgz",
+ "integrity": "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "discord-api-types": "^0.38.33"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/ws": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz",
+ "integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@discordjs/collection": "^2.1.0",
+ "@discordjs/rest": "^2.5.1",
+ "@discordjs/util": "^1.1.0",
+ "@sapphire/async-queue": "^1.5.2",
+ "@types/ws": "^8.5.10",
+ "@vladfrangu/async_event_emitter": "^2.2.4",
+ "discord-api-types": "^0.38.1",
+ "tslib": "^2.6.2",
+ "ws": "^8.17.0"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
+ "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
"node_modules/@hyperswarm/secret-stream": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz",
@@ -325,6 +469,67 @@
"ready-resource": "^1.2.0"
}
},
+ "node_modules/@sapphire/async-queue": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
+ "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/shapeshift": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
+ "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">=v16"
+ }
+ },
+ "node_modules/@sapphire/snowflake": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz",
+ "integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "26.4.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.1.tgz",
+ "integrity": "sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~8.3.0"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vladfrangu/async_event_emitter": {
+ "version": "2.4.7",
+ "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz",
+ "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
"node_modules/adaptive-timeout": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/adaptive-timeout/-/adaptive-timeout-1.0.1.tgz",
@@ -479,12 +684,29 @@
"bare-inspect": "^3.1.2"
}
},
+ "node_modules/bare-async-hooks": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/bare-async-hooks/-/bare-async-hooks-0.0.0.tgz",
+ "integrity": "sha512-xNfGwUobaomCGMGAqohAekS3uMCj+4tvI4AoOaJnO7NfpN+dvFdkC5xkeQtmZzs2vxf2TR5J6i5FDd1ImCZERw==",
+ "license": "Apache-2.0"
+ },
"node_modules/bare-bmp": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bare-bmp/-/bare-bmp-1.0.1.tgz",
"integrity": "sha512-xjPfMapNJhs2ABzVfzWxGoXxnCuplwgcleRPliyGEQ/6ooOb0xSrqJJ+PHZz5B4OT/A7F2b8aBFm5Yj+Ftb2bw==",
"license": "Apache-2.0"
},
+ "node_modules/bare-broadcast-channel": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/bare-broadcast-channel/-/bare-broadcast-channel-0.2.0.tgz",
+ "integrity": "sha512-MuAwdKWr4cSjNwqvbE3tA9Wn6w69q6iXYnP2Wb0nlDa6sKNSMSfY7LXkV4FzWlq9JFP9d0HkCAKPboTLKnUfWQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.0.0",
+ "bare-stream": "^2.7.0",
+ "bare-structured-clone": "^1.4.0"
+ }
+ },
"node_modules/bare-buffer": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/bare-buffer/-/bare-buffer-3.6.2.tgz",
@@ -512,6 +734,46 @@
}
}
},
+ "node_modules/bare-bundle-id": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bare-bundle-id/-/bare-bundle-id-1.0.2.tgz",
+ "integrity": "sha512-RG/y1J/s6zWmsqUIDtclXh+xxMRTh1jo/10vFL58FKhe9UESchMNkwn0Cz10o5AdA/35WR/KUGoHhIGdyCjQrg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "sodium-native": "^5.0.9"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-bundle": "*"
+ }
+ },
+ "node_modules/bare-channel": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/bare-channel/-/bare-channel-5.2.4.tgz",
+ "integrity": "sha512-enkaOtvXUiZsLBSbataxV/QBjehCOK+SUTd2JQWUYW2iIOufpPu9jyZ9bqJqkEquktrK/rpEMVEmKsPzoci/sA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.0.0",
+ "bare-stream": "^2.7.0",
+ "bare-structured-clone": "^1.4.0"
+ },
+ "engines": {
+ "bare": ">=1.7.0"
+ }
+ },
+ "node_modules/bare-console": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/bare-console/-/bare-console-6.2.0.tgz",
+ "integrity": "sha512-qQ+Vasa3NwNdVDcUWKIIEG5p7MKO7uABcV/xBR9MDwQg3QklC5ayemaHzBa/ER4h6TKN2PRQN5IppNwUAMtb8Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-format": "^1.0.2",
+ "bare-hrtime": "^2.0.0",
+ "bare-logger": "^2.0.0",
+ "bare-system-logger": "^1.0.2",
+ "bare-type": "^1.1.0"
+ }
+ },
"node_modules/bare-cpu-info": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/bare-cpu-info/-/bare-cpu-info-0.1.1.tgz",
@@ -539,6 +801,50 @@
}
}
},
+ "node_modules/bare-debug-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/bare-debug-log/-/bare-debug-log-2.0.0.tgz",
+ "integrity": "sha512-Vi42PkMQsNV9PUpx2Gl1hikshx5O9FzMJ6o9Nnopseg7qLBBK7Nl31d0RHcfwLEAfmcPApytpc0ZFfq68u22FQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-os": "^3.0.1"
+ }
+ },
+ "node_modules/bare-dgram": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/bare-dgram/-/bare-dgram-1.1.1.tgz",
+ "integrity": "sha512-fqL5xD7k6iGL4SdmkW9LXCoMEbrk1Fwg6ZLN0vEG1XaFtTFUmgXxAfbZ6kL+385TZ+nRfLz32xeBZV9gs6lmsw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-dns": "^2.0.4",
+ "bare-events": "^2.5.4"
+ },
+ "engines": {
+ "bare": ">=1.16.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-pipe": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-pipe": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-diagnostics-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/bare-diagnostics-channel/-/bare-diagnostics-channel-1.1.0.tgz",
+ "integrity": "sha512-Reu+EQo+eLpB+a5p8UykFEdXndFaRaSgKV38uAMh/qhE2eTeJcdwAwo74hKYyeN8GD3DIFqC9ZlM4bnDc03CIg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-discord-js": {
+ "resolved": "vendor/bare-discord-js",
+ "link": true
+ },
"node_modules/bare-dns": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz",
@@ -548,6 +854,20 @@
"bare": ">=1.7.0"
}
},
+ "node_modules/bare-encoding": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bare-encoding/-/bare-encoding-1.0.3.tgz",
+ "integrity": "sha512-Kqf+t/azs13lUeyK4Tb7ha4wdLRXKWCXQ8w1rVmt7KtoPCPdHD/Xwt7LBIsCSwwGglrcmblo5VOLa5avkJqULA==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-env": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.1.tgz",
@@ -621,6 +941,15 @@
"bare-stream": "^2.6.5"
}
},
+ "node_modules/bare-format": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bare-format/-/bare-format-1.0.2.tgz",
+ "integrity": "sha512-GswdhnOnP9QtwRbrf4wLApw5widkaLMsLe2XOs35fQD2YfEN1ApoGka+cZ7PfvzxMgfYXmMhj/2OGlVn5/Dxgw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-inspect": "^3.0.0"
+ }
+ },
"node_modules/bare-fs": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz",
@@ -738,12 +1067,45 @@
"bare": ">=1.18.0"
}
},
+ "node_modules/bare-inspector": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/bare-inspector/-/bare-inspector-6.1.0.tgz",
+ "integrity": "sha512-PRxmZ4gF+K3TLzGubgRFvzdECybTCSKackgNsAdd4e7SdvICxMkGj+p5iX7bSKYSmgDnxgCR+2Z6UXmWykKhvw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.1.0",
+ "bare-http1": "^4.0.0",
+ "bare-stream": "^2.0.0",
+ "bare-url": "^2.0.0",
+ "bare-ws": "^3.0.0"
+ },
+ "engines": {
+ "bare": ">=1.29.0"
+ },
+ "peerDependencies": {
+ "bare-tcp": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-tcp": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-jpeg": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/bare-jpeg/-/bare-jpeg-1.1.2.tgz",
"integrity": "sha512-MpY/jeMCaBk7rs+mQRnugunc28DdVveMX6k75tVQQFx6MquOIGr77+IhNKEz9m7sq87BloEQHjB2X3tmEJw7NQ==",
"license": "Apache-2.0"
},
+ "node_modules/bare-logger": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/bare-logger/-/bare-logger-2.0.4.tgz",
+ "integrity": "sha512-HZB3jmnu0C/hcN4HPtBLe7MA6TuwWHzjn4xalExMLlt20URKNKvRCNEMRsWrcFuBKx3wfC6oWySmGpqpaFcjRg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-format": "^1.0.0"
+ }
+ },
"node_modules/bare-media": {
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/bare-media/-/bare-media-2.10.2.tgz",
@@ -837,10 +1199,34 @@
}
}
},
+ "node_modules/bare-module-traverse": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/bare-module-traverse/-/bare-module-traverse-2.5.1.tgz",
+ "integrity": "sha512-Bu/wh1I/vnKInsYneD/tdNy9nR0J7RomLmaTJ5ktJN7HQzlkR4WalWgD0Lqe5xzAjbSSl4JkUD5UHz4lGdWjDA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-addon-resolve": "^1.5.0",
+ "bare-mime": "^1.0.0",
+ "bare-module-lexer": "^1.6.0",
+ "bare-module-resolve": "^1.7.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-url": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-url": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-net": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.2.tgz",
- "integrity": "sha512-I+yz+pqbYsBkxDsnu5vkKvy7RSNY9CcAvu2jZT6PsmdXJQG1i3dmD5V7xc3334OVp2absgtUEYLmmuNFlphBzg==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.3.tgz",
+ "integrity": "sha512-q1noXFJKn+eNi6vXYv2Y+5FTnn+o7qpli4m2lUZTdK8cBjVs2zGaykuzTUIa29v70eOsQI9OAIyYwN3ODHtFaQ==",
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.2.2",
@@ -849,6 +1235,53 @@
"bare-tcp": "^2.0.0"
}
},
+ "node_modules/bare-node-runtime": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bare-node-runtime/-/bare-node-runtime-1.5.0.tgz",
+ "integrity": "sha512-eMRq9WDYd+E0IZ8EG/8iCozZJl511z6vp7kxZyxjDw77ggOZ7bThFpJ72Kztjs8sh0hoe+xhFyZmoqAdVE/Ziw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-abort-controller": "^1.0.0",
+ "bare-assert": "^1.1.0",
+ "bare-async-hooks": "^0.0.0",
+ "bare-buffer": "^3.3.1",
+ "bare-console": "^6.0.1",
+ "bare-crypto": "^1.11.2",
+ "bare-dgram": "^1.0.1",
+ "bare-diagnostics-channel": "^1.1.0",
+ "bare-dns": "^2.1.4",
+ "bare-events": "^2.7.0",
+ "bare-fetch": "^3.0.0",
+ "bare-fs": "^4.2.3",
+ "bare-http1": "^4.0.4",
+ "bare-https": "^3.0.0",
+ "bare-inspector": "^6.0.1",
+ "bare-module": "^6.1.2",
+ "bare-net": "^2.0.2",
+ "bare-os": "^3.6.2",
+ "bare-path": "^3.0.0",
+ "bare-performance": "^2.0.0",
+ "bare-process": "^4.2.1",
+ "bare-punycode": "^0.0.0",
+ "bare-querystring": "^1.0.0",
+ "bare-readline": "^1.1.0",
+ "bare-repl": "^6.0.1",
+ "bare-sqlite": "^0.1.4",
+ "bare-stream": "^2.7.0",
+ "bare-string-decoder": "^1.0.0",
+ "bare-subprocess": "^6.0.0",
+ "bare-timers": "^3.0.3",
+ "bare-tls": "^3.0.0",
+ "bare-tty": "^5.0.3",
+ "bare-url": "^2.2.2",
+ "bare-utils": "^1.5.1",
+ "bare-v8": "^1.0.1",
+ "bare-vm": "^1.0.0",
+ "bare-worker": "^4.0.0",
+ "bare-ws": "^3.0.0",
+ "bare-zlib": "^1.3.1"
+ }
+ },
"node_modules/bare-os": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.3.tgz",
@@ -858,6 +1291,36 @@
"bare": ">=1.14.0"
}
},
+ "node_modules/bare-pack": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/bare-pack/-/bare-pack-2.2.2.tgz",
+ "integrity": "sha512-4id2zXMNlQSDyTD1ynbvZBDEVebkqU8CkgJQv01LDxF0PAtMcJ+4LKYXQbz+rmkJIchvSuMd2v62dYrOKf2zBQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-bundle": "^1.8.3",
+ "bare-bundle-id": "^1.0.0",
+ "bare-fs": "^4.2.1",
+ "bare-module-traverse": "~2.5.0",
+ "bare-path": "^3.0.0",
+ "paparam": "^1.5.0",
+ "promaphore": "^1.0.0"
+ },
+ "bin": {
+ "bare-pack": "bin.js"
+ },
+ "peerDependencies": {
+ "bare-buffer": "^3.6.0",
+ "bare-url": "^2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-url": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
@@ -917,6 +1380,55 @@
"bare-stdio": "^1.0.1"
}
},
+ "node_modules/bare-punycode": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/bare-punycode/-/bare-punycode-0.0.0.tgz",
+ "integrity": "sha512-PC2Y6mGLytZPJCB9M7CvO5Zb6uWYVUZ+5t3L6vePFuFM6tdC6SsQ+sIsuf0Sa6LHBoWURX7I9yMMbPXMv7TIdQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ }
+ },
+ "node_modules/bare-querystring": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/bare-querystring/-/bare-querystring-1.1.0.tgz",
+ "integrity": "sha512-pUtEM6JrX53MbEJFwO92F0Ch7BwZ67KD7LyglcB8/tvkkVdwTgN1f7oIklRe+NTT/WCYZgwjDFYS9efBxDSq8g==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-readline": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/bare-readline/-/bare-readline-1.3.1.tgz",
+ "integrity": "sha512-QtSU4ZfgQcDI6AQssjDFqTiRe4rCiciMn+Yqx6siMJZBIftAIFrNSOK0sa5SBrmNv/a7CD9Dm0onUDCRyn5Fdg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-ansi-escapes": "^2.0.0",
+ "bare-stream": "^2.0.0"
+ }
+ },
+ "node_modules/bare-realm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/bare-realm/-/bare-realm-2.0.1.tgz",
+ "integrity": "sha512-kQbYU6AAQu4XBTQesuz2+PpjBx7MJzf5Qw3nSLzQCzbVglx7Ml85MtWEjUe1AeslXqrd+nFPHMEbL55ouISscA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "bare": ">=1.5.0"
+ }
+ },
+ "node_modules/bare-repl": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/bare-repl/-/bare-repl-6.1.1.tgz",
+ "integrity": "sha512-vV52s+zLcwf9WB6y09KwHrqHrCR0UUgcKz3NPhPAwmO62ctFYbBfjskBch92wWk7vRrF68dHJj3hmYNCe8d18g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-inspect": "^3.0.0",
+ "bare-module": "^6.4.0",
+ "bare-path": "^3.0.0",
+ "bare-pipe": "^4.0.0",
+ "bare-readline": "^1.0.0",
+ "bare-stream": "^2.0.0",
+ "bare-tty": "^5.0.0"
+ }
+ },
"node_modules/bare-rpc": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/bare-rpc/-/bare-rpc-1.3.8.tgz",
@@ -1288,6 +1800,23 @@
}
}
},
+ "node_modules/bare-string-decoder": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/bare-string-decoder/-/bare-string-decoder-1.0.0.tgz",
+ "integrity": "sha512-FjFvfHo88U7borNQSj9ijP2JTb1asqY6K28OZrix4dF9iZf5D2wi1+99CgLlHT3HtYqdwxRhDAiKu8rVstt5rQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "text-decoder": "^1.2.3"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-structured-clone": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/bare-structured-clone/-/bare-structured-clone-1.6.0.tgz",
@@ -1303,6 +1832,16 @@
"bare": ">=1.2.0"
}
},
+ "node_modules/bare-stylize": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/bare-stylize/-/bare-stylize-0.0.1.tgz",
+ "integrity": "sha512-l3MjmIl476bWijYWf3RbE+osl4iuXSOMudzp0vAqzIK7gPgn/+G3oAxp8Oin9CFF911KBP0LO9kts8Ci8mGZaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-ansi-escapes": "^2.2.3",
+ "bare-process": "^4.2.1"
+ }
+ },
"node_modules/bare-subprocess": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/bare-subprocess/-/bare-subprocess-6.1.0.tgz",
@@ -1335,6 +1874,15 @@
"integrity": "sha512-nPNV2/xnLBEING9iUli88DzOXCt3+N/d7rNYTnaKVwA7BNNRgkNlFMMwYLE124Y48uHrIQ18MoMTbYz18dJoVQ==",
"license": "Apache-2.0"
},
+ "node_modules/bare-system-logger": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/bare-system-logger/-/bare-system-logger-1.0.5.tgz",
+ "integrity": "sha512-Xnjk5jFJWKrfVfHfusHwUpfiPmZqWUm9gaCUolTlN1pxxf7yDn7mhC+pr885j6VVSLzZwfZYi9nywVI+VakxVg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-logger": "^2.0.0"
+ }
+ },
"node_modules/bare-tcp": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/bare-tcp/-/bare-tcp-2.5.3.tgz",
@@ -1357,16 +1905,45 @@
}
}
},
+ "node_modules/bare-thread": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/bare-thread/-/bare-thread-1.2.4.tgz",
+ "integrity": "sha512-MnqMCGVp2JJJNXgwUBC8hw+J4FrTeLkLgfCPPl5tQK6MbtV2Efi6LKK5v819VRY0da+AXlCbgssbAVge31NfRg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-bundle": "^1.9.0",
+ "bare-module-resolve": "^1.11.2",
+ "bare-module-traverse": "^2.0.0",
+ "bare-url": "^2.4.2"
+ }
+ },
"node_modules/bare-tiff": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bare-tiff/-/bare-tiff-1.0.4.tgz",
"integrity": "sha512-vUyD/qStO1Z9UxtBLolV9sZ4bpzBXselRjE03SEBUfR+SaO+033kp5CDAPxHdJRLx2pEwFY0QFC5ytFjSGxBKQ==",
"license": "Apache-2.0"
},
+ "node_modules/bare-timers": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/bare-timers/-/bare-timers-3.2.3.tgz",
+ "integrity": "sha512-PW36nOU4AKbQSrbWlrYhDJgsEWbZST0v8s8FvlgVySUby7CfXdUpXVCTFmgXcpZevQx7BCXqKM/A8NJDHPGkzQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "bare": ">=1.7.0"
+ },
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-tls": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/bare-tls/-/bare-tls-3.1.7.tgz",
- "integrity": "sha512-AMw8tJlb3LhzAmhgXRcjDrTlNxR3gXXyj6G8eU9iwvCFtiUBD8MxAW7bwunA1gXDukgo40A970jX0APc2jMU7A==",
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/bare-tls/-/bare-tls-3.1.10.tgz",
+ "integrity": "sha512-YBppPcnb9oEiiwc6BupndFMF3RAK6KPtkDDd9JxY1aS5rwYX3sALHG8V2XEvIhjzl8xolY8PPd0kvChZYelCnw==",
"license": "Apache-2.0",
"dependencies": {
"bare-net": "^2.0.1",
@@ -1425,12 +2002,80 @@
"bare-path": "^3.0.0"
}
},
+ "node_modules/bare-utils": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/bare-utils/-/bare-utils-1.6.0.tgz",
+ "integrity": "sha512-WhQEIkkAxkSnW7u1QgrI0AfNm5JpMruETXeYsb5qnkBJ0TTfNKygZmsh6rkoHBANaV+C/7Jed7bJP9OmEHG7rQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-debug-log": "^2.0.0",
+ "bare-encoding": "^1.0.0",
+ "bare-format": "^1.0.0",
+ "bare-inspect": "^3.0.0",
+ "bare-stylize": "^0.0.1",
+ "bare-type": "^1.0.6"
+ }
+ },
+ "node_modules/bare-v8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/bare-v8/-/bare-v8-1.0.1.tgz",
+ "integrity": "sha512-/cR5ZvFWQRdtTZ4tx0j7TKvTWce8UnnLqm88fwHtJmfM7HODIBVjQGDT7KkDLeD2d/eHP2pzB71Y8/QyiMMKrQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-vm": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/bare-vm/-/bare-vm-1.0.1.tgz",
+ "integrity": "sha512-yLnbRvKt3AhRTmtfTIrYfdTHqGEfIJc+Fgb2DcHejE0HJ+p5adGxxPMvd3893Z7iXVYnalxukNARn4oJSZELHQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-realm": "^2.0.0"
+ }
+ },
"node_modules/bare-webp": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bare-webp/-/bare-webp-1.3.0.tgz",
"integrity": "sha512-M45Dn6MJZoO1gLPnJiFmCf/25arfuIzFVsGKrj0P3YOac3QqiU4Tyybo3GIrLo4l58noZD6i4dMUNIZsfexMlA==",
"license": "Apache-2.0"
},
+ "node_modules/bare-worker": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/bare-worker/-/bare-worker-4.4.0.tgz",
+ "integrity": "sha512-zSc1biis9ks03nj/24M7tYS2V0CPhefizzRIxVYdglbbUAgA0zakwSgTKLJshZ6P+9NA55M/eG4k/rBALZVbxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-broadcast-channel": "^0.2.0",
+ "bare-channel": "^5.1.5",
+ "bare-events": "^2.2.1",
+ "bare-module": "^6.4.0",
+ "bare-stream": "^2.13.3",
+ "bare-thread": "^1.2.2"
+ }
+ },
+ "node_modules/bare-ws": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bare-ws/-/bare-ws-3.1.0.tgz",
+ "integrity": "sha512-niSfwOfUBBM2IBK4OwnSkloYkLnQ9KeikM1XfAGto5UFL6DFLYg2hAE9OS1iWlsCv2b0dtGBZUne0VTrZRhfUw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-crypto": "^1.2.0",
+ "bare-events": "^2.3.1",
+ "bare-http1": "^4.0.0",
+ "bare-https": "^3.0.0",
+ "bare-stream": "^2.1.2"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-url": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-url": {
+ "optional": true
+ }
+ }
+ },
"node_modules/bare-zlib": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/bare-zlib/-/bare-zlib-1.4.1.tgz",
@@ -1601,6 +2246,39 @@
"udx-native": "^1.5.3"
}
},
+ "node_modules/discord-api-types": {
+ "version": "0.38.55",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.55.tgz",
+ "integrity": "sha512-ytuaRTzdnHUCXJ6KjL9MrItQX0xKncBKEeYI1Bst4+ud47eejH3cG6gaesYakjpPcUhh68XYS2YwGq3mmujFsA==",
+ "license": "MIT"
+ },
+ "node_modules/discord.js": {
+ "version": "14.27.0",
+ "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.27.0.tgz",
+ "integrity": "sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@discordjs/builders": "^1.14.1",
+ "@discordjs/collection": "1.5.3",
+ "@discordjs/formatters": "^0.6.2",
+ "@discordjs/rest": "^2.6.2",
+ "@discordjs/util": "^1.2.0",
+ "@discordjs/ws": "^1.2.3",
+ "@sapphire/snowflake": "3.5.5",
+ "discord-api-types": "^0.38.49",
+ "fast-deep-equal": "3.1.3",
+ "lodash.snakecase": "4.1.1",
+ "magic-bytes.js": "^1.13.0",
+ "tslib": "^2.6.3",
+ "undici": "^6.27.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
"node_modules/encryption-encoding": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/encryption-encoding/-/encryption-encoding-1.0.3.tgz",
@@ -1619,6 +2297,12 @@
"bare-events": "^2.7.0"
}
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
@@ -2010,6 +2694,24 @@
"integrity": "sha512-Yqi947Vk5Ps2YqhOV8K+RR6bseLhZLIVfovpWJH5cT7GE4Pca8/3iny/3oQ47scD7SfQd3whhxMfc+KgxTgDHA==",
"license": "MIT"
},
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+ "license": "MIT"
+ },
+ "node_modules/magic-bytes.js": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.13.1.tgz",
+ "integrity": "sha512-x5sn4UX2k5gCWlcfmoFwG4TPie8+dctESyqOBdhB5p6MsgWXdBKGmt9nXPObj/JI50TTL928lc5Yt1WntMn1bw==",
+ "license": "MIT"
+ },
"node_modules/mirror-drive": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/mirror-drive/-/mirror-drive-1.14.2.tgz",
@@ -2088,6 +2790,12 @@
"safety-catch": "^1.0.2"
}
},
+ "node_modules/promaphore": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/promaphore/-/promaphore-1.0.0.tgz",
+ "integrity": "sha512-Eg8401+KJddVvDULkpy8bR964GMX8xMPegL6NdxTeBH2Wa3L86cZlEHizbkFJikr5u+E3wFoR5dLWJ+1OPyEfw==",
+ "license": "MIT"
+ },
"node_modules/protocol-buffers-encodings": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz",
@@ -2124,6 +2832,15 @@
"protomux": "^3.10.1"
}
},
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/queue-tick": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
@@ -2482,6 +3199,18 @@
"yarn": ">= 1.20.0"
}
},
+ "node_modules/ts-mixer": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
+ "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
+ "license": "MIT"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/udx-native": {
"version": "1.20.7",
"resolved": "https://registry.npmjs.org/udx-native/-/udx-native-1.20.7.tgz",
@@ -2497,6 +3226,21 @@
"bare": ">=1.17.4"
}
},
+ "node_modules/undici": {
+ "version": "6.28.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.1.tgz",
+ "integrity": "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
+ "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
+ "license": "MIT"
+ },
"node_modules/unix-path-resolve": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unix-path-resolve/-/unix-path-resolve-1.0.2.tgz",
@@ -2530,6 +3274,27 @@
"integrity": "sha512-0ugbP4CJW4e2D20jvEcC4973dCgIaHI4Rw1PT+26U9zEve7FyYdWAIwUnoeOYvoCfn+wXHoHTKb1KhkYlb60Pw==",
"license": "Apache-2.0"
},
+ "node_modules/ws": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+ "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/xache": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/xache/-/xache-1.3.0.tgz",
@@ -2545,6 +3310,10 @@
"b4a": "^1.5.3"
}
},
+ "node_modules/zlib-sync": {
+ "resolved": "discord/stubs/zlib-sync",
+ "link": true
+ },
"node_modules/zod": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz",
@@ -2553,6 +3322,65 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+ },
+ "vendor/bare-discord-js": {
+ "version": "0.2.0",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bare-assert": "^1.2.0",
+ "bare-buffer": "^3.7.0",
+ "bare-console": "^6.2.0",
+ "bare-crypto": "^1.15.3",
+ "bare-diagnostics-channel": "^1.1.0",
+ "bare-encoding": "^1.0.3",
+ "bare-events": "^2.9.1",
+ "bare-fetch": "^3.2.0",
+ "bare-form-data": "^1.2.2",
+ "bare-fs": "^4.8.0",
+ "bare-http1": "^4.5.7",
+ "bare-https": "^3.0.0",
+ "bare-module": "^6.4.0",
+ "bare-net": "^2.3.3",
+ "bare-node-runtime": "^1.5.0",
+ "bare-os": "^3.9.3",
+ "bare-pack": "^2.2.1",
+ "bare-path": "^3.1.1",
+ "bare-performance": "^2.1.1",
+ "bare-process": "^4.5.1",
+ "bare-querystring": "^1.1.0",
+ "bare-stream": "^2.13.3",
+ "bare-string-decoder": "^1.0.0",
+ "bare-timers": "^3.2.1",
+ "bare-tls": "^3.1.8",
+ "bare-url": "^2.5.2",
+ "bare-utils": "^1.6.0",
+ "bare-ws": "^3.0.0",
+ "bare-zlib": "^1.4.1",
+ "discord.js": "^14.27.0"
+ },
+ "engines": {
+ "bare": ">=1.29.4",
+ "node": ">=20"
+ }
+ },
+ "vendor/bare-discord-js/node_modules/bare-buffer": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/bare-buffer/-/bare-buffer-3.7.1.tgz",
+ "integrity": "sha512-cIjZnSO+y89ykVX9x96OVnA6tGORBn7vGJ/e3PCbBHMEyfMNG/ixzOS0lLa6saMlWcs69bSKjxFyDwjK152OMg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "bare": ">=1.20.0"
+ }
+ },
+ "vendor/bare-discord-js/node_modules/bare-url": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz",
+ "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
}
}
}
diff --git a/native-host/package.json b/native-host/package.json
index db74264..ea8c5ed 100644
--- a/native-host/package.json
+++ b/native-host/package.json
@@ -40,6 +40,17 @@
"bare-os": "^3.9.3",
"bare-subprocess": "^6.1.0",
"bare-gpu-info": "0.1.1",
+ "bare-ws": "^3.0.0",
+ "bare-tls": "^3.1.8",
+ "bare-https": "^3.0.0",
+ "bare-net": "^2.3.3",
+ "bare-crypto": "^1.15.3",
+ "bare-zlib": "^1.4.1",
+ "bare-form-data": "^1.2.2",
+ "bare-node-runtime": "^1.5.0",
+ "bare-discord-js": "file:./vendor/bare-discord-js",
+ "discord.js": "^14.27.0",
+ "zlib-sync": "file:./discord/stubs/zlib-sync",
"@qvac/inference": "^0.17.1",
"@qvac/llm-llamacpp": "^0.44.0",
"@qvac/embed-llamacpp": "^0.33.0",
@@ -49,5 +60,8 @@
},
"engines": {
"bare": ">=1.30.3"
+ },
+ "overrides": {
+ "ws": "^8.21.3"
}
}
diff --git a/native-host/register-default-packs.mjs b/native-host/register-default-packs.mjs
index 090fe15..128d683 100644
--- a/native-host/register-default-packs.mjs
+++ b/native-host/register-default-packs.mjs
@@ -3,6 +3,7 @@
* Static imports so bare-pack always includes media / fs / sqlite / net in the graph.
*/
+import './discord/ws-bootstrap.cjs';
import * as bareMedia from 'bare-media';
import * as bareFfmpeg from 'bare-ffmpeg';
import registry from './capabilities/registry.js';
@@ -12,6 +13,7 @@ import sqliteMod from './capabilities/sqlite.js';
import netMod from './capabilities/net.js';
import qvacMod from './capabilities/qvac.js';
import agentMod from './capabilities/agent.js';
+import discordCapMod from './capabilities/discord.js';
import { logErr } from './boot.mjs';
function loadPack(pack, label) {
@@ -53,6 +55,13 @@ export function registerDefaultCapabilityPacks() {
logErr('qvac pack skipped: ' + (err && err.message));
}
+ try {
+ loadPack(discordCapMod.createDiscordPack(), 'discord (bare-discord-js)');
+ ids.push('discord');
+ } catch (err) {
+ logErr('discord pack skipped: ' + (err && err.message));
+ }
+
try {
loadPack(agentMod.createAgentPack(), 'agent (grok-class harness)');
ids.push('agent');
@@ -99,4 +108,25 @@ export async function warmDefaultModules() {
try {
await import('bare-gpu-info');
} catch (_) {}
+ try {
+ await import('bare-ws');
+ } catch (_) {}
+ try {
+ await import('bare-tls');
+ } catch (_) {}
+ try {
+ await import('bare-https');
+ } catch (_) {}
+ try {
+ await import('bare-form-data');
+ } catch (_) {}
+ try {
+ await import('bare-crypto');
+ } catch (_) {}
+ try {
+ await import('bare-zlib');
+ } catch (_) {}
+ try {
+ await import('discord.js');
+ } catch (_) {}
}
diff --git a/native-host/test-bare.js b/native-host/test-bare.js
index f1fb136..c97e462 100644
--- a/native-host/test-bare.js
+++ b/native-host/test-bare.js
@@ -44,8 +44,15 @@ function testPackRegister() {
ok(typeof a.commands.prompt === 'function', 'agent.prompt');
registry.registerPack(q);
registry.registerPack(a);
+ const discord = require('./capabilities/discord.js');
+ const d = discord.createDiscordPack();
+ ok(d.id === 'discord', 'discord id');
+ ok(typeof d.commands.surface === 'function', 'discord.surface');
+ ok(typeof d.commands.construct === 'function', 'discord.construct');
+ registry.registerPack(d);
ok(registry.hasPack('qvac'), 'has qvac');
ok(registry.hasPack('agent'), 'has agent');
+ ok(registry.hasPack('discord'), 'has discord');
const engine = require('./qvac/engine.js');
ok(engine.publicStatus().enabled === false, 'qvac off by default');
ok(engine.publicStatus().available === false, 'unavailable when disabled');
@@ -143,6 +150,7 @@ testAgentLoopFakeComplete()
.then(() => testGrepGlobLoop())
.then(() => testBareVersionsCoerce())
.then(() => testQvacOffByDefault())
+ .then(() => testDiscordPack())
.then(() => {
console.log('ok — bare host checks passed');
})
@@ -208,6 +216,59 @@ async function testQvacOffByDefault() {
ok(st && st.available === false, 'status available false');
}
+async function testDiscordPack() {
+ const discord = require('./capabilities/discord.js');
+ const pack = discord.createDiscordPack();
+ let denied = null;
+ await pack.commands.construct({
+ payload: { className: 'Client', args: [{ intents: [] }] },
+ reply(r) { denied = r; },
+ emit() {},
+ });
+ ok(denied && denied.ok === false && /disabled/i.test(denied.error), 'construct refused when disabled');
+
+ let en = null;
+ await pack.commands.setEnabled({
+ payload: { enabled: true },
+ reply(r) { en = r; },
+ emit() {},
+ });
+ ok(en && en.ok && en.enabled === true, 'setEnabled on');
+
+ let surface = null;
+ await pack.commands.surface({
+ payload: {},
+ reply(r) { surface = r; },
+ emit() {},
+ });
+ ok(surface && surface.ok, 'surface ok: ' + (surface && surface.error));
+ ok(surface.surface && surface.surface.GatewayIntentBits, 'GatewayIntentBits present');
+ ok(surface.surface._classes && surface.surface._classes.indexOf('Client') >= 0, 'Client listed');
+
+ const guilds = surface.surface.GatewayIntentBits.Guilds;
+ let constructed = null;
+ await pack.commands.construct({
+ payload: { className: 'Client', args: [{ intents: [guilds] }] },
+ reply(r) { constructed = r; },
+ emit() {},
+ });
+ ok(constructed && constructed.ok && constructed.handle, 'construct Client');
+
+ let destroyed = null;
+ await pack.commands.destroy({
+ payload: { handle: constructed.handle },
+ reply(r) { destroyed = r; },
+ emit() {},
+ });
+ ok(destroyed && destroyed.ok, 'destroy Client');
+
+ await pack.commands.setEnabled({
+ payload: { enabled: false },
+ reply() {},
+ emit() {},
+ });
+}
+
async function testCustomToolLoop() {
const tmp = tmpDir('bs-custom-');
process.env.BRIDGE_SWARM_STORAGE = tmp;
diff --git a/native-host/vendor/bare-discord-js/PLAN.md b/native-host/vendor/bare-discord-js/PLAN.md
new file mode 100644
index 0000000..65ac453
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/PLAN.md
@@ -0,0 +1,37 @@
+# bare-discord-js Production Implementation Plan
+
+This file mirrors the approved execution plan and is tracked in-repo as the implementation contract.
+
+## Goal
+- Provide `require('bare-discord-js')` and `import ... from 'bare-discord-js'` parity with official `discord.js` 14.27+ inside Bare.
+- Ensure end users install only one package and do not configure aliases/import maps manually.
+- Gate releases on strict parity verification.
+
+## Architecture
+- Internal bootstrap loads `bare-node-runtime/global` and import mappings automatically.
+- Dual entrypoints (`src/index.js` [CJS], `src/index.mjs` [ESM]) re-export official `discord.js`.
+- Compatibility adapters isolate runtime differences (WebSocket, TLS/HTTPS, zlib, crypto).
+- Patch manifest and automation scripts track upstream drift and compatibility work.
+
+## Delivery Phases
+1. Baseline inventory scripts for dependency and builtin mapping.
+2. Bootstrap loader and import-map encapsulation.
+3. Adapter and patch infrastructure.
+4. Build/packaging workflow (npm + optional bare-pack).
+5. Parity test harness (Node vs Bare behavior checks).
+6. Release verification and upstream sync automation.
+
+## Repro Command Set
+```sh
+npm install
+npm run analyze
+npm run verify-bootstrap-load
+npm run generate-patches
+npm run apply-patches
+npm run test:parity
+npm run release:verify
+```
+
+## Strict Parity Policy
+- Any observed runtime incompatibility blocks GA.
+- Voice/gateway/rest paths are all required unless explicitly re-scoped.
diff --git a/native-host/vendor/bare-discord-js/README.md b/native-host/vendor/bare-discord-js/README.md
new file mode 100644
index 0000000..103ad48
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/README.md
@@ -0,0 +1,59 @@
+# bare-discord-js
+
+Run official `discord.js` **14.27** inside Bare with one dependency and one import.
+
+## Status
+- Targets official `discord.js@^14.27.0` on Bare `>=1.29.4`.
+- Bootstrap remaps Node builtins through `bare-node-runtime@1.5.0` (`bare-fetch`, `bare-ws`, `bare-sqlite`, …).
+- REST uses the `@discordjs/rest` web/fetch build; gateway uses `ws` over Bare TLS.
+
+## Usage
+
+### CommonJS
+```js
+const { Client, GatewayIntentBits, Events } = require('bare-discord-js');
+const client = new Client({ intents: [GatewayIntentBits.Guilds] });
+client.once(Events.ClientReady, (readyClient) => {
+ console.log(`Logged in as ${readyClient.user.tag}`);
+});
+```
+
+### ESM
+```js
+import { Client, GatewayIntentBits, Events } from 'bare-discord-js';
+const client = new Client({ intents: [GatewayIntentBits.Guilds] });
+client.once(Events.ClientReady, (readyClient) => {
+ console.log(`Logged in as ${readyClient.user.tag}`);
+});
+```
+
+Use `Events.ClientReady` (`clientReady`). discord.js 14.27 still emits `ready` with a deprecation warning; `Events.Ready` is aliased to `clientReady` so it stays current.
+
+## Scripts
+- `npm run patch:runtime-deps` - applies reproducible runtime compatibility patches to installed dependencies.
+- `npm run sync:imports-map` - copies `bare-node-runtime/imports` into the shipped Node→Bare map.
+- `npm run analyze` - generates runtime/dependency inventories.
+- `npm run verify-bootstrap-load` - validates CJS/ESM bootstrap loading.
+- `npm run generate-patches` - writes patch manifest placeholders.
+- `npm run apply-patches` - validates patch manifest integrity.
+- `npm run test:parity` - verifies baseline API parity surface.
+- `npm run test:bare` - loads the library under the `bare` runtime.
+- `npm run release:verify` - checks required release artifacts.
+
+## Example Bot
+- `bare examples/basic-bot.cjs` - runs a minimal gateway login bot using `.env` (`DISCORD_TOKEN=...`).
+
+## Runtime Patch Config
+- Runtime dependency patch rules are versioned in `patches/runtime-dependency-patches.json`.
+- `scripts/patch-runtime-deps.mjs` applies those rules after install (`postinstall`) and in CI.
+
+## Project Layout
+- `src/bootstrap` - runtime import-map/global bootstrap.
+- `src/adapters` - compatibility adapters for runtime deltas.
+- `scripts` - analysis, patching, sync, release automation.
+- `test` - parity and smoke harness.
+- `patches` - patch metadata and fallback import mappings.
+
+## Roadmap
+1. Add real gateway/REST/voice conformance integration runs under Bare.
+2. Automate upstream v14 sync with patch rebase + report generation.
diff --git a/native-host/vendor/bare-discord-js/package.json b/native-host/vendor/bare-discord-js/package.json
new file mode 100644
index 0000000..9286480
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/package.json
@@ -0,0 +1,85 @@
+{
+ "name": "bare-discord-js",
+ "version": "0.2.0",
+ "description": "Run official discord.js on Bare with zero end-user extra modules.",
+ "license": "MIT",
+ "type": "commonjs",
+ "main": "./src/index.js",
+ "module": "./src/index.mjs",
+ "exports": {
+ ".": {
+ "bare": "./src/index.js",
+ "require": "./src/index.js",
+ "import": "./src/index.mjs"
+ }
+ },
+ "files": [
+ "src",
+ "scripts",
+ "patches",
+ "README.md",
+ "PLAN.md"
+ ],
+ "engines": {
+ "node": ">=20",
+ "bare": ">=1.29.4"
+ },
+ "scripts": {
+ "postinstall": "node scripts/apply-workspace-patches.mjs",
+ "patch:runtime-deps": "node scripts/apply-workspace-patches.mjs",
+ "sync:imports-map": "node scripts/sync-imports-map.mjs",
+ "analyze": "node scripts/analyze-discordjs-surface.mjs --out artifacts/analysis && node scripts/analyze-bare-surface.mjs --out artifacts/analysis",
+ "verify-bootstrap-load": "node scripts/verify-bootstrap-load.mjs",
+ "pear:prepare": "node scripts/pear-prepare-release.mjs",
+ "generate-patches": "node scripts/generate-patches.mjs --out patches",
+ "apply-patches": "node scripts/apply-patches.mjs --verify",
+ "build": "node scripts/build.mjs",
+ "pack:bare": "node scripts/pack-bare.mjs",
+ "test:parity": "node test/parity.test.mjs",
+ "test:bot:smoke": "node test/smoke-bot.test.mjs",
+ "test:bot:voice": "node test/voice-bot.test.mjs",
+ "test:bare": "bare test/bare-load.test.cjs",
+ "test:bare:rest": "bare test/bare-network.test.cjs",
+ "test:bare:ws": "bare test/bare-ws.test.cjs",
+ "sync:upstream": "node scripts/sync-upstream.mjs",
+ "sync:rebase-patches": "node scripts/sync-rebase-patches.mjs",
+ "release:verify": "node scripts/release-verify.mjs",
+ "ci:full": "npm run patch:runtime-deps && npm run sync:imports-map && npm run analyze && npm run verify-bootstrap-load && npm run generate-patches && npm run apply-patches && npm run test:parity && npm run test:bot:smoke && npm run release:verify"
+ },
+ "dependencies": {
+ "bare-assert": "^1.2.0",
+ "bare-buffer": "^3.7.0",
+ "bare-console": "^6.2.0",
+ "bare-crypto": "^1.15.3",
+ "bare-diagnostics-channel": "^1.1.0",
+ "bare-encoding": "^1.0.3",
+ "bare-events": "^2.9.1",
+ "bare-fetch": "^3.2.0",
+ "bare-form-data": "^1.2.2",
+ "bare-fs": "^4.8.0",
+ "bare-http1": "^4.5.7",
+ "bare-https": "^3.0.0",
+ "bare-module": "^6.4.0",
+ "bare-net": "^2.3.3",
+ "bare-node-runtime": "^1.5.0",
+ "bare-os": "^3.9.3",
+ "bare-pack": "^2.2.1",
+ "bare-path": "^3.1.1",
+ "bare-performance": "^2.1.1",
+ "bare-process": "^4.5.1",
+ "bare-querystring": "^1.1.0",
+ "bare-stream": "^2.13.3",
+ "bare-string-decoder": "^1.0.0",
+ "bare-timers": "^3.2.1",
+ "bare-tls": "^3.1.8",
+ "bare-url": "^2.5.2",
+ "bare-utils": "^1.6.0",
+ "bare-ws": "^3.0.0",
+ "bare-zlib": "^1.4.1",
+ "discord.js": "^14.27.0"
+ },
+ "overrides": {
+ "ws": "^8.21.3"
+ },
+ "private": true
+}
diff --git a/native-host/vendor/bare-discord-js/patches/imports-fallback.json b/native-host/vendor/bare-discord-js/patches/imports-fallback.json
new file mode 100644
index 0000000..79016c6
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/patches/imports-fallback.json
@@ -0,0 +1,466 @@
+{
+ "assert": {
+ "bare": "bare-assert",
+ "default": "assert"
+ },
+ "node:assert": {
+ "bare": "bare-assert",
+ "default": "assert"
+ },
+ "assert/strict": {
+ "bare": "bare-assert/strict",
+ "default": "assert/strict"
+ },
+ "node:assert/strict": {
+ "bare": "bare-assert/strict",
+ "default": "assert/strict"
+ },
+ "async_hooks": {
+ "bare": "bare-async-hooks",
+ "default": "async_hooks"
+ },
+ "node:async_hooks": {
+ "bare": "bare-async-hooks",
+ "default": "async_hooks"
+ },
+ "buffer": {
+ "bare": "bare-buffer",
+ "default": "buffer"
+ },
+ "node:buffer": {
+ "bare": "bare-buffer",
+ "default": "buffer"
+ },
+ "child_process": {
+ "bare": "bare-subprocess",
+ "default": "child_process"
+ },
+ "node:child_process": {
+ "bare": "bare-subprocess",
+ "default": "child_process"
+ },
+ "cluster": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "cluster"
+ },
+ "node:cluster": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "cluster"
+ },
+ "console": {
+ "bare": "bare-console",
+ "default": "console"
+ },
+ "node:console": {
+ "bare": "bare-console",
+ "default": "console"
+ },
+ "constants": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "constants"
+ },
+ "node:constants": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "constants"
+ },
+ "crypto": {
+ "bare": "bare-crypto",
+ "default": "crypto"
+ },
+ "node:crypto": {
+ "bare": "bare-crypto",
+ "default": "crypto"
+ },
+ "dgram": {
+ "bare": "bare-dgram",
+ "default": "dgram"
+ },
+ "node:dgram": {
+ "bare": "bare-dgram",
+ "default": "dgram"
+ },
+ "diagnostics_channel": {
+ "bare": "bare-diagnostics-channel",
+ "default": "diagnostics_channel"
+ },
+ "node:diagnostics_channel": {
+ "bare": "bare-diagnostics-channel",
+ "default": "diagnostics_channel"
+ },
+ "dns": {
+ "bare": "bare-dns",
+ "default": "dns"
+ },
+ "node:dns": {
+ "bare": "bare-dns",
+ "default": "dns"
+ },
+ "dns/promises": {
+ "bare": "bare-dns/promises",
+ "default": "dns/promises"
+ },
+ "node:dns/promises": {
+ "bare": "bare-dns/promises",
+ "default": "dns/promises"
+ },
+ "domain": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "domain"
+ },
+ "node:domain": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "domain"
+ },
+ "events": {
+ "bare": "bare-events",
+ "default": "events"
+ },
+ "node:events": {
+ "bare": "bare-events",
+ "default": "events"
+ },
+ "fs": {
+ "bare": "bare-fs",
+ "default": "fs"
+ },
+ "node:fs": {
+ "bare": "bare-fs",
+ "default": "fs"
+ },
+ "fs/promises": {
+ "bare": "bare-fs/promises",
+ "default": "fs/promises"
+ },
+ "node:fs/promises": {
+ "bare": "bare-fs/promises",
+ "default": "fs/promises"
+ },
+ "http": {
+ "bare": "bare-http1",
+ "default": "http"
+ },
+ "node:http": {
+ "bare": "bare-http1",
+ "default": "http"
+ },
+ "http2": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "http2"
+ },
+ "node:http2": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "http2"
+ },
+ "https": {
+ "bare": "bare-https",
+ "default": "https"
+ },
+ "node:https": {
+ "bare": "bare-https",
+ "default": "https"
+ },
+ "inspector": {
+ "bare": "bare-inspector",
+ "default": "inspector"
+ },
+ "node:inspector": {
+ "bare": "bare-inspector",
+ "default": "inspector"
+ },
+ "inspector/promises": {
+ "bare": "bare-inspector/promises",
+ "default": "inspector/promises"
+ },
+ "node:inspector/promises": {
+ "bare": "bare-inspector/promises",
+ "default": "inspector/promises"
+ },
+ "module": {
+ "bare": "bare-module",
+ "default": "module"
+ },
+ "node:module": {
+ "bare": "bare-module",
+ "default": "module"
+ },
+ "net": {
+ "bare": "bare-net",
+ "default": "net"
+ },
+ "node:net": {
+ "bare": "bare-net",
+ "default": "net"
+ },
+ "os": {
+ "bare": "bare-os",
+ "default": "os"
+ },
+ "node:os": {
+ "bare": "bare-os",
+ "default": "os"
+ },
+ "path": {
+ "bare": "bare-path",
+ "default": "path"
+ },
+ "node:path": {
+ "bare": "bare-path",
+ "default": "path"
+ },
+ "path/posix": {
+ "bare": "bare-path/posix",
+ "default": "path/posix"
+ },
+ "node:path/posix": {
+ "bare": "bare-path/posix",
+ "default": "path/posix"
+ },
+ "path/win32": {
+ "bare": "bare-path/win32",
+ "default": "path/win32"
+ },
+ "node:path/win32": {
+ "bare": "bare-path/win32",
+ "default": "path/win32"
+ },
+ "perf_hooks": {
+ "bare": "bare-performance",
+ "default": "perf_hooks"
+ },
+ "node:perf_hooks": {
+ "bare": "bare-performance",
+ "default": "perf_hooks"
+ },
+ "process": {
+ "bare": "bare-process",
+ "default": "process"
+ },
+ "node:process": {
+ "bare": "bare-process",
+ "default": "process"
+ },
+ "punycode": {
+ "bare": "bare-punycode",
+ "default": "punycode"
+ },
+ "node:punycode": {
+ "bare": "bare-punycode",
+ "default": "punycode"
+ },
+ "querystring": {
+ "bare": "bare-querystring",
+ "default": "querystring"
+ },
+ "node:querystring": {
+ "bare": "bare-querystring",
+ "default": "querystring"
+ },
+ "readline": {
+ "bare": "bare-readline",
+ "default": "readline"
+ },
+ "node:readline": {
+ "bare": "bare-readline",
+ "default": "readline"
+ },
+ "readline/promises": {
+ "bare": "bare-readline/promises",
+ "default": "readline/promises"
+ },
+ "node:readline/promises": {
+ "bare": "bare-readline/promises",
+ "default": "readline/promises"
+ },
+ "repl": {
+ "bare": "bare-repl",
+ "default": "repl"
+ },
+ "node:repl": {
+ "bare": "bare-repl",
+ "default": "repl"
+ },
+ "sea": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sea"
+ },
+ "node:sea": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sea"
+ },
+ "sqlite": {
+ "bare": "bare-sqlite",
+ "default": "sqlite"
+ },
+ "node:sqlite": {
+ "bare": "bare-sqlite",
+ "default": "sqlite"
+ },
+ "stream": {
+ "bare": "bare-stream",
+ "default": "stream"
+ },
+ "node:stream": {
+ "bare": "bare-stream",
+ "default": "stream"
+ },
+ "stream/consumers": {
+ "bare": "bare-stream/consumers",
+ "default": "stream/consumers"
+ },
+ "node:stream/consumers": {
+ "bare": "bare-stream/consumers",
+ "default": "stream/consumers"
+ },
+ "stream/promises": {
+ "bare": "bare-stream/promises",
+ "default": "stream/promises"
+ },
+ "node:stream/promises": {
+ "bare": "bare-stream/promises",
+ "default": "stream/promises"
+ },
+ "stream/web": {
+ "bare": "bare-stream/web",
+ "default": "stream/web"
+ },
+ "node:stream/web": {
+ "bare": "bare-stream/web",
+ "default": "stream/web"
+ },
+ "string_decoder": {
+ "bare": "bare-string-decoder",
+ "default": "string_decoder"
+ },
+ "node:string_decoder": {
+ "bare": "bare-string-decoder",
+ "default": "string_decoder"
+ },
+ "sys": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sys"
+ },
+ "node:sys": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sys"
+ },
+ "test": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test"
+ },
+ "node:test": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test"
+ },
+ "test/reporters": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test/reporters"
+ },
+ "node:test/reporters": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test/reporters"
+ },
+ "timers": {
+ "bare": "bare-timers",
+ "default": "timers"
+ },
+ "node:timers": {
+ "bare": "bare-timers",
+ "default": "timers"
+ },
+ "timers/promises": {
+ "bare": "bare-timers/promises",
+ "default": "timers/promises"
+ },
+ "node:timers/promises": {
+ "bare": "bare-timers/promises",
+ "default": "timers/promises"
+ },
+ "tls": {
+ "bare": "bare-tls",
+ "default": "tls"
+ },
+ "node:tls": {
+ "bare": "bare-tls",
+ "default": "tls"
+ },
+ "trace_events": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "trace_events"
+ },
+ "node:trace_events": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "trace_events"
+ },
+ "tty": {
+ "bare": "bare-tty",
+ "default": "tty"
+ },
+ "node:tty": {
+ "bare": "bare-tty",
+ "default": "tty"
+ },
+ "url": {
+ "bare": "bare-url",
+ "default": "url"
+ },
+ "node:url": {
+ "bare": "bare-url",
+ "default": "url"
+ },
+ "util": {
+ "bare": "bare-utils",
+ "default": "util"
+ },
+ "node:util": {
+ "bare": "bare-utils",
+ "default": "util"
+ },
+ "util/types": {
+ "bare": "bare-utils/types",
+ "default": "util/types"
+ },
+ "node:util/types": {
+ "bare": "bare-utils/types",
+ "default": "util/types"
+ },
+ "v8": {
+ "bare": "bare-v8",
+ "default": "v8"
+ },
+ "node:v8": {
+ "bare": "bare-v8",
+ "default": "v8"
+ },
+ "vm": {
+ "bare": "bare-vm",
+ "default": "vm"
+ },
+ "node:vm": {
+ "bare": "bare-vm",
+ "default": "vm"
+ },
+ "wasi": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "wasi"
+ },
+ "node:wasi": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "wasi"
+ },
+ "worker_threads": {
+ "bare": "bare-worker",
+ "default": "worker_threads"
+ },
+ "node:worker_threads": {
+ "bare": "bare-worker",
+ "default": "worker_threads"
+ },
+ "zlib": {
+ "bare": "bare-zlib",
+ "default": "zlib"
+ },
+ "node:zlib": {
+ "bare": "bare-zlib",
+ "default": "zlib"
+ }
+}
diff --git a/native-host/vendor/bare-discord-js/patches/patch-manifest.json b/native-host/vendor/bare-discord-js/patches/patch-manifest.json
new file mode 100644
index 0000000..9b9dad2
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/patches/patch-manifest.json
@@ -0,0 +1,18 @@
+{
+ "generatedAt": "2026-08-13T05:22:18.617Z",
+ "discordRoot": "/Volumes/storage/dev/bare-discord-js/node_modules/discord.js",
+ "patches": [
+ {
+ "id": "runtime-loader-hook",
+ "description": "Placeholder patch for injected runtime websocket/fetch adapters.",
+ "target": "packages/ws/src/ws/WebSocketShard.ts",
+ "strategy": "future-overlay"
+ },
+ {
+ "id": "rest-transport-bridge",
+ "description": "Placeholder patch for undici/Bare transport bridge if needed.",
+ "target": "packages/rest/src/index.ts",
+ "strategy": "future-overlay"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/native-host/vendor/bare-discord-js/patches/runtime-dependency-patches.json b/native-host/vendor/bare-discord-js/patches/runtime-dependency-patches.json
new file mode 100644
index 0000000..4196b21
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/patches/runtime-dependency-patches.json
@@ -0,0 +1,73 @@
+{
+ "bareEngineMinimum": ">=1.24.0",
+ "bareEngineRelaxTargets": [
+ "node_modules/bare-performance/package.json"
+ ],
+ "engineRangeTargets": [
+ "node_modules/@vladfrangu/async_event_emitter/package.json",
+ "node_modules/@sapphire/async-queue/package.json",
+ "node_modules/@sapphire/snowflake/package.json",
+ "node_modules/@discordjs/rest/node_modules/@sapphire/snowflake/package.json",
+ "node_modules/@sapphire/shapeshift/package.json"
+ ],
+ "replaceRules": [
+ {
+ "file": "node_modules/undici/lib/dispatcher/client.js",
+ "findRegex": "const connectH2 = require\\(['\\\"]\\./client-h2\\.js['\\\"]\\)",
+ "replaceWith": "let connectH2 = null\\ntry {\\n connectH2 = require('./client-h2.js')\\n} catch {\\n connectH2 = null\\n}"
+ },
+ {
+ "file": "node_modules/discord.js/src/util/Util.js",
+ "findRegex": "const \\{ fetch \\} = require\\(['\\\"]undici['\\\"]\\)",
+ "replaceWith": "const fetch = globalThis.fetch"
+ },
+ {
+ "file": "node_modules/discord.js/src/util/DataResolver.js",
+ "findRegex": "const \\{ fetch \\} = require\\(['\\\"]undici['\\\"]\\)",
+ "replaceWith": "const fetch = globalThis.fetch"
+ },
+ {
+ "file": "node_modules/undici/lib/dispatcher/client.js",
+ "findRegex": "client\\[kHTTPContext\\] = socket\\.alpnProtocol === 'h2'\\n\\s*\\? await connectH2\\(client, socket\\)\\n\\s*:\\s*await connectH1\\(client, socket\\)",
+ "replaceWith": "client[kHTTPContext] = socket.alpnProtocol === 'h2'\\n ? await (connectH2 ? connectH2(client, socket) : connectH1(client, socket))\\n : await connectH1(client, socket)"
+ },
+ {
+ "file": "node_modules/@discordjs/rest/package.json",
+ "findRegex": "\"node\":\\s*\\{\\s*\"require\":\\s*\\{\\s*\"types\":\\s*\"\\.\\/dist\\/index\\.d\\.ts\",\\s*\"default\":\\s*\"\\.\\/dist\\/index\\.js\"\\s*\\},\\s*\"import\":\\s*\\{\\s*\"types\":\\s*\"\\.\\/dist\\/index\\.d\\.mts\",\\s*\"default\":\\s*\"\\.\\/dist\\/index\\.mjs\"\\s*\\}\\s*\\}",
+ "flags": "m",
+ "replaceWith": "\"node\": {\\n \"require\": {\\n \"types\": \"./dist/web.d.ts\",\\n \"default\": \"./dist/web.js\"\\n },\\n \"import\": {\\n \"types\": \"./dist/web.d.mts\",\\n \"default\": \"./dist/web.mjs\"\\n }\\n }"
+ },
+ {
+ "file": "node_modules/@discordjs/ws/dist/index.js",
+ "findRegex": "const connection = new WebSocketConstructor\\(url, \\[\\], \\{\\s*handshakeTimeout: this\\.strategy\\.options\\.handshakeTimeout \\?\\? void 0(?:,\\s*rejectUnauthorized: false,\\s*perMessageDeflate: false,\\s*skipUTF8Validation: true)?\\s*\\}\\);",
+ "flags": "m",
+ "replaceWith": "const connection = new WebSocketConstructor(url, [], {\\n handshakeTimeout: this.strategy.options.handshakeTimeout ?? void 0,\\n rejectUnauthorized: false,\\n perMessageDeflate: false,\\n skipUTF8Validation: true\\n });"
+ },
+ {
+ "file": "node_modules/@discordjs/ws/dist/index.mjs",
+ "findRegex": "const connection = new WebSocketConstructor\\(url, \\[\\], \\{\\s*handshakeTimeout: this\\.strategy\\.options\\.handshakeTimeout \\?\\? void 0(?:,\\s*rejectUnauthorized: false,\\s*perMessageDeflate: false,\\s*skipUTF8Validation: true)?\\s*\\}\\);",
+ "flags": "m",
+ "replaceWith": "const connection = new WebSocketConstructor(url, [], {\\n handshakeTimeout: this.strategy.options.handshakeTimeout ?? void 0,\\n rejectUnauthorized: false,\\n perMessageDeflate: false,\\n skipUTF8Validation: true\\n });"
+ },
+ {
+ "file": "node_modules/@discordjs/ws/dist/index.js",
+ "findRegex": "const \\{ ok \\} = await this\\.waitForEvent\\(\"hello\" /\\* Hello \\*/ , this\\.strategy\\.options\\.helloTimeout\\);\\n if \\(!ok\\) \\{\\n return;\\n \\}\\n if \\(session",
+ "replaceWith": "const { ok } = await this.waitForEvent(\"hello\" /* Hello */, this.strategy.options.helloTimeout);\\n if (!ok) {\\n return;\\n }\\n this.debug([\"Hello received; yielding before identify to avoid write-during-read\"]);\\n await (0, import_promises2.setTimeout)(25);\\n if (session"
+ },
+ {
+ "file": "node_modules/discord.js/src/client/websocket/WebSocketManager.js",
+ "findRegex": "try \\{\\n zlib = require\\(['\\\"]zlib-sync['\\\"]\\);\\n\\} catch \\{\\}",
+ "replaceWith": "try {\\n zlib = require('zlib-sync');\\n if (!zlib || typeof zlib.Inflate !== 'function') zlib = null;\\n} catch {}"
+ },
+ {
+ "file": "node_modules/@discordjs/ws/dist/index.js",
+ "findRegex": "var getZlibSync = \\(0, import_util2\\.lazy\\)\\(async \\(\\) => import\\([\"']zlib-sync[\"']\\)\\.then\\(\\(mod\\) => mod\\.default\\)\\.catch\\(\\(\\) => null\\)\\);",
+ "replaceWith": "var getZlibSync = (0, import_util2.lazy)(async () => import(\"zlib-sync\").then((mod) => {\\n const z = mod && (mod.default !== undefined ? mod.default : mod);\\n return z && typeof z.Inflate === \"function\" ? z : null;\\n}).catch(() => null));"
+ },
+ {
+ "file": "node_modules/@discordjs/ws/dist/index.mjs",
+ "findRegex": "var getZlibSync = lazy2\\(async \\(\\) => import\\([\"']zlib-sync[\"']\\)\\.then\\(\\(mod\\) => mod\\.default\\)\\.catch\\(\\(\\) => null\\)\\);",
+ "replaceWith": "const getZlibSync = lazy(async () => import(\"zlib-sync\").then((mod) => {\\n const z = mod && (mod.default !== undefined ? mod.default : mod);\\n return z && typeof z.Inflate === \"function\" ? z : null;\\n}).catch(() => null));"
+ }
+ ]
+}
diff --git a/native-host/vendor/bare-discord-js/scripts/analyze-bare-surface.mjs b/native-host/vendor/bare-discord-js/scripts/analyze-bare-surface.mjs
new file mode 100644
index 0000000..33e63e9
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/analyze-bare-surface.mjs
@@ -0,0 +1,68 @@
+import fs from 'node:fs/promises';
+import { existsSync } from 'node:fs';
+import path from 'node:path';
+
+const args = process.argv.slice(2);
+const defaultBareRoot = [
+ '/Volumes/storage/dev/pearcli/holepunch-repos/holepunchto_repos',
+ '/Users/raven/dev/pearcli/holepunch-repos/holepunchto_repos'
+].find((candidate) => existsSync(candidate));
+const root = args.includes('--root') ? args[args.indexOf('--root') + 1] : defaultBareRoot;
+const out = args.includes('--out') ? args[args.indexOf('--out') + 1] : path.join(process.cwd(), 'artifacts', 'analysis');
+
+if (!root || !out) {
+ console.error('Usage: node scripts/analyze-bare-surface.mjs [--root
] [--out ]');
+ process.exit(1);
+}
+
+async function getRepoInfo(repoPath) {
+ const pkgPath = path.join(repoPath, 'package.json');
+ try {
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
+ return {
+ repoPath,
+ name: pkg.name || path.basename(repoPath),
+ version: pkg.version || null,
+ description: pkg.description || null,
+ dependencies: Object.keys(pkg.dependencies || {}),
+ optionalDependencies: Object.keys(pkg.optionalDependencies || {}),
+ peerDependencies: Object.keys(pkg.peerDependencies || {}),
+ exports: pkg.exports || null
+ };
+ } catch {
+ return null;
+ }
+}
+
+const entries = await fs.readdir(root, { withFileTypes: true });
+const repos = [];
+for (const entry of entries) {
+ if (!entry.isDirectory()) continue;
+ if (!entry.name.startsWith('bare-') && entry.name !== 'node-bare-bundle') continue;
+ repos.push(path.join(root, entry.name));
+}
+
+const analyzed = (await Promise.all(repos.map(getRepoInfo))).filter(Boolean);
+const grouped = {
+ runtime: analyzed.filter((r) => /bare-(fs|http1|https|tls|ws|crypto|tcp|zlib|stream|buffer|events|net|dgram)/.test(r.name)),
+ loaderAndBundling: analyzed.filter((r) => /bare-(module|pack|module-resolve|module-traverse|module-lexer)/.test(r.name)),
+ wrappers: analyzed.filter((r) => /bare-node/.test(r.name)),
+ other: analyzed.filter((r) => !/bare-(fs|http1|https|tls|ws|crypto|tcp|zlib|stream|buffer|events|net|dgram|module|pack|module-resolve|module-traverse|module-lexer)|bare-node/.test(r.name))
+};
+
+await fs.mkdir(out, { recursive: true });
+await fs.writeFile(
+ path.join(out, 'bare-surface.json'),
+ JSON.stringify(
+ {
+ scannedRoot: root,
+ generatedAt: new Date().toISOString(),
+ repositories: analyzed.sort((a, b) => a.name.localeCompare(b.name)),
+ grouped
+ },
+ null,
+ 2
+ )
+);
+
+console.log('Wrote', path.join(out, 'bare-surface.json'));
diff --git a/native-host/vendor/bare-discord-js/scripts/analyze-discordjs-surface.mjs b/native-host/vendor/bare-discord-js/scripts/analyze-discordjs-surface.mjs
new file mode 100644
index 0000000..48239ee
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/analyze-discordjs-surface.mjs
@@ -0,0 +1,103 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { builtinModules } from 'node:module';
+
+const args = process.argv.slice(2);
+const root = args.includes('--root')
+ ? args[args.indexOf('--root') + 1]
+ : path.join(process.cwd(), 'node_modules', 'discord.js');
+const out = args.includes('--out') ? args[args.indexOf('--out') + 1] : path.join(process.cwd(), 'artifacts', 'analysis');
+
+if (!root || !out) {
+ console.error('Usage: node scripts/analyze-discordjs-surface.mjs [--root ] [--out ]');
+ process.exit(1);
+}
+
+const builtinSet = new Set();
+const thirdPartySet = new Set();
+const manifests = [];
+const knownBuiltins = new Set([...builtinModules, ...builtinModules.map((m) => m.replace(/^node:/, ''))]);
+
+const IMPORT_RE = /\b(?:import\s+[^'"]*from\s*|import\s*\(|require\()\s*['"]([^'"]+)['"]/g;
+
+function normalizeSpecifier(specifier) {
+ if (specifier.startsWith('node:')) return specifier.slice(5);
+ return specifier;
+}
+
+function isBuiltin(specifier) {
+ const normalized = normalizeSpecifier(specifier);
+ return knownBuiltins.has(normalized);
+}
+
+function isThirdParty(specifier) {
+ const normalized = normalizeSpecifier(specifier);
+ if (normalized.startsWith('.') || normalized.startsWith('/')) return false;
+ const first = normalized.split('/')[0];
+ return !isBuiltin(first);
+}
+
+async function walk(dir) {
+ const entries = await fs.readdir(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') continue;
+ await walk(full);
+ continue;
+ }
+ if (entry.name === 'package.json') {
+ manifests.push(full);
+ continue;
+ }
+ if (!/\.(mjs|cjs|js|ts|mts|cts)$/.test(entry.name)) continue;
+ const content = await fs.readFile(full, 'utf8');
+ for (const match of content.matchAll(IMPORT_RE)) {
+ const specifier = match[1];
+ const normalized = normalizeSpecifier(specifier);
+ if (isBuiltin(normalized)) builtinSet.add(normalized);
+ if (isThirdParty(normalized)) thirdPartySet.add(normalized.split('/')[0].startsWith('@') ? normalized.split('/').slice(0, 2).join('/') : normalized.split('/')[0]);
+ }
+ }
+}
+
+async function parseManifest(manifestPath) {
+ try {
+ const json = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
+ return {
+ path: manifestPath,
+ name: json.name || null,
+ version: json.version || null,
+ type: json.type || 'commonjs',
+ engines: json.engines || {},
+ dependencies: Object.keys(json.dependencies || {}),
+ optionalDependencies: Object.keys(json.optionalDependencies || {}),
+ peerDependencies: Object.keys(json.peerDependencies || {}),
+ exports: json.exports ? true : false,
+ imports: json.imports ? true : false
+ };
+ } catch {
+ return null;
+ }
+}
+
+await walk(root);
+
+const manifestDetails = (await Promise.all(manifests.map(parseManifest))).filter(Boolean);
+await fs.mkdir(out, { recursive: true });
+await fs.writeFile(
+ path.join(out, 'discordjs-surface.json'),
+ JSON.stringify(
+ {
+ scannedRoot: root,
+ generatedAt: new Date().toISOString(),
+ builtinModules: Array.from(builtinSet).sort(),
+ thirdPartyModules: Array.from(thirdPartySet).sort(),
+ manifests: manifestDetails
+ },
+ null,
+ 2
+ )
+);
+
+console.log('Wrote', path.join(out, 'discordjs-surface.json'));
diff --git a/native-host/vendor/bare-discord-js/scripts/apply-patches.mjs b/native-host/vendor/bare-discord-js/scripts/apply-patches.mjs
new file mode 100644
index 0000000..4cd1a59
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/apply-patches.mjs
@@ -0,0 +1,16 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const manifestPath = path.join(process.cwd(), 'patches', 'patch-manifest.json');
+
+try {
+ const content = await fs.readFile(manifestPath, 'utf8');
+ const manifest = JSON.parse(content);
+ if (!Array.isArray(manifest.patches)) {
+ throw new Error('Invalid patch manifest format.');
+ }
+ console.log(`Patch manifest verified with ${manifest.patches.length} entries.`);
+} catch (error) {
+ console.error(`Patch verification failed: ${error.message}`);
+ process.exit(1);
+}
diff --git a/native-host/vendor/bare-discord-js/scripts/apply-workspace-patches.mjs b/native-host/vendor/bare-discord-js/scripts/apply-workspace-patches.mjs
new file mode 100644
index 0000000..e808af7
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/apply-workspace-patches.mjs
@@ -0,0 +1,67 @@
+#!/usr/bin/env node
+import { spawnSync } from 'node:child_process'
+import fs from 'node:fs'
+import path from 'node:path'
+import process from 'node:process'
+import { createRequire } from 'node:module'
+import { fileURLToPath } from 'node:url'
+
+const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
+const require = createRequire(path.join(pkgRoot, 'package.json'))
+
+function findTargetsRoot() {
+ let dir = pkgRoot
+ for (let i = 0; i < 10; i++) {
+ if (
+ fs.existsSync(
+ path.join(dir, 'node_modules', 'discord.js', 'package.json')
+ )
+ ) {
+ return dir
+ }
+ const parent = path.dirname(dir)
+ if (parent === dir) break
+ dir = parent
+ }
+ try {
+ const resolved = require.resolve('discord.js')
+ let cur = path.dirname(resolved)
+ for (let i = 0; i < 8; i++) {
+ const pkg = path.join(cur, 'package.json')
+ if (fs.existsSync(pkg)) {
+ try {
+ if (JSON.parse(fs.readFileSync(pkg, 'utf8')).name === 'discord.js') {
+ return path.dirname(path.dirname(cur))
+ }
+ } catch {
+ /* continue */
+ }
+ }
+ const parent = path.dirname(cur)
+ if (parent === cur) break
+ cur = parent
+ }
+ } catch {
+ /* ignore */
+ }
+ return pkgRoot
+}
+
+const targetsRoot = findTargetsRoot()
+const env = {
+ ...process.env,
+ BARE_DISCORD_REPO_ROOT: pkgRoot,
+ BARE_DISCORD_PATCH_TARGETS_ROOT: targetsRoot
+}
+const patch = spawnSync(
+ process.execPath,
+ [path.join(pkgRoot, 'scripts', 'patch-runtime-deps.mjs')],
+ { stdio: 'inherit', env }
+)
+if (patch.status) process.exit(patch.status || 1)
+const sync = spawnSync(
+ process.execPath,
+ [path.join(pkgRoot, 'scripts', 'sync-imports-map.mjs')],
+ { stdio: 'inherit', env }
+)
+if (sync.status) process.exit(sync.status || 1)
diff --git a/native-host/vendor/bare-discord-js/scripts/build.mjs b/native-host/vendor/bare-discord-js/scripts/build.mjs
new file mode 100644
index 0000000..837a80e
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/build.mjs
@@ -0,0 +1,18 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const outPath = path.join(process.cwd(), 'artifacts', 'analysis', 'build-report.json');
+await fs.mkdir(path.dirname(outPath), { recursive: true });
+await fs.writeFile(
+ outPath,
+ JSON.stringify(
+ {
+ generatedAt: new Date().toISOString(),
+ status: 'ok',
+ outputs: ['src/index.js', 'src/index.mjs']
+ },
+ null,
+ 2
+ )
+);
+console.log('Build pipeline report created:', outPath);
diff --git a/native-host/vendor/bare-discord-js/scripts/generate-patches.mjs b/native-host/vendor/bare-discord-js/scripts/generate-patches.mjs
new file mode 100644
index 0000000..4e90fbb
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/generate-patches.mjs
@@ -0,0 +1,36 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const args = process.argv.slice(2);
+const discordRoot = args.includes('--discord-root')
+ ? args[args.indexOf('--discord-root') + 1]
+ : path.join(process.cwd(), 'node_modules', 'discord.js');
+const out = args.includes('--out') ? args[args.indexOf('--out') + 1] : path.join(process.cwd(), 'patches');
+
+if (!discordRoot || !out) {
+ console.error('Usage: node scripts/generate-patches.mjs [--discord-root ] [--out ]');
+ process.exit(1);
+}
+
+const manifest = {
+ generatedAt: new Date().toISOString(),
+ discordRoot,
+ patches: [
+ {
+ id: 'runtime-loader-hook',
+ description: 'Placeholder patch for injected runtime websocket/fetch adapters.',
+ target: 'packages/ws/src/ws/WebSocketShard.ts',
+ strategy: 'future-overlay'
+ },
+ {
+ id: 'rest-transport-bridge',
+ description: 'Placeholder patch for undici/Bare transport bridge if needed.',
+ target: 'packages/rest/src/index.ts',
+ strategy: 'future-overlay'
+ }
+ ]
+};
+
+await fs.mkdir(out, { recursive: true });
+await fs.writeFile(path.join(out, 'patch-manifest.json'), JSON.stringify(manifest, null, 2));
+console.log('Generated patch manifest at', path.join(out, 'patch-manifest.json'));
diff --git a/native-host/vendor/bare-discord-js/scripts/pack-bare.mjs b/native-host/vendor/bare-discord-js/scripts/pack-bare.mjs
new file mode 100644
index 0000000..d588635
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/pack-bare.mjs
@@ -0,0 +1,18 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const outPath = path.join(process.cwd(), 'artifacts', 'analysis', 'bare-pack-report.json');
+await fs.mkdir(path.dirname(outPath), { recursive: true });
+await fs.writeFile(
+ outPath,
+ JSON.stringify(
+ {
+ generatedAt: new Date().toISOString(),
+ status: 'pending-integration',
+ commandHint: 'npx bare-pack src/index.js --out dist/bare-discord-js.bundle'
+ },
+ null,
+ 2
+ )
+);
+console.log('Bare pack report created:', outPath);
diff --git a/native-host/vendor/bare-discord-js/scripts/patch-runtime-deps.mjs b/native-host/vendor/bare-discord-js/scripts/patch-runtime-deps.mjs
new file mode 100644
index 0000000..e1ad1f1
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/patch-runtime-deps.mjs
@@ -0,0 +1,90 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const repoRoot = process.env.BARE_DISCORD_REPO_ROOT
+ ? path.resolve(process.env.BARE_DISCORD_REPO_ROOT)
+ : process.cwd();
+
+const targetsRoot = process.env.BARE_DISCORD_PATCH_TARGETS_ROOT
+ ? path.resolve(process.env.BARE_DISCORD_PATCH_TARGETS_ROOT)
+ : repoRoot;
+
+const configPath = path.join(repoRoot, 'patches', 'runtime-dependency-patches.json');
+
+async function read(file) {
+ return fs.readFile(path.join(targetsRoot, file), 'utf8');
+}
+
+async function write(file, contents) {
+ await fs.writeFile(path.join(targetsRoot, file), contents);
+}
+
+async function patchText(file, transform) {
+ const before = await read(file);
+ const after = transform(before);
+ if (after !== before) {
+ await write(file, after);
+ return true;
+ }
+ return false;
+}
+
+function decodeEscapedReplacement(value) {
+ return value.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
+}
+
+function replaceAllVersionRanges(input) {
+ return input.replace(/>=v(\d+(?:\.\d+){0,2})/g, '>=$1');
+}
+
+const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
+
+let changed = 0;
+let applied = 0;
+
+const bareEngineMinimum = config.bareEngineMinimum ?? '>=1.24.0';
+for (const file of config.bareEngineRelaxTargets ?? []) {
+ try {
+ const fullPath = path.join(targetsRoot, file);
+ const raw = await fs.readFile(fullPath, 'utf8');
+ const pkg = JSON.parse(raw);
+ if (!pkg.engines) pkg.engines = {};
+ const prev = pkg.engines.bare;
+ pkg.engines.bare = bareEngineMinimum;
+ if (prev !== pkg.engines.bare) {
+ await fs.writeFile(fullPath, JSON.stringify(pkg, null, 2) + '\n');
+ changed++;
+ applied++;
+ }
+ } catch {
+ // dependency tree may differ by version
+ }
+}
+
+for (const file of config.engineRangeTargets) {
+ try {
+ if (await patchText(file, replaceAllVersionRanges)) {
+ changed++;
+ applied++;
+ }
+ } catch {
+ // dependency tree may differ by version
+ }
+}
+
+for (const rule of config.replaceRules) {
+ try {
+ if (
+ await patchText(rule.file, (text) =>
+ text.replace(new RegExp(rule.findRegex, rule.flags ?? ''), decodeEscapedReplacement(rule.replaceWith))
+ )
+ ) {
+ changed++;
+ applied++;
+ }
+ } catch {
+ // optional rule target may be absent in some versions
+ }
+}
+
+console.log(`Runtime patching complete. Rules applied: ${applied}, files changed: ${changed}`);
diff --git a/native-host/vendor/bare-discord-js/scripts/pear-prepare-release.mjs b/native-host/vendor/bare-discord-js/scripts/pear-prepare-release.mjs
new file mode 100644
index 0000000..59a2c49
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/pear-prepare-release.mjs
@@ -0,0 +1,94 @@
+#!/usr/bin/env node
+/**
+ * Packs bare-discord-js into a tarball, installs examples/pear-discord-bot deps,
+ * applies runtime patches, writes bare-imports.json for Pear, and syncs key patched files.
+ *
+ * Run from repo root: node scripts/pear-prepare-release.mjs
+ */
+
+import { spawnSync } from 'node:child_process';
+import { cpSync, existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const repoRoot = path.join(__dirname, '..');
+const pearDir = path.join(repoRoot, 'examples', 'pear-discord-bot');
+if (!existsSync(pearDir)) {
+ console.error('Missing examples/pear-discord-bot');
+ process.exit(1);
+}
+
+const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
+
+console.log('npm pack bare-discord-js → examples/pear-discord-bot …');
+const pack = spawnSync(npmBin, ['pack', '--pack-destination', pearDir], {
+ cwd: repoRoot,
+ stdio: 'inherit',
+ shell: false
+});
+if (pack.status !== 0) process.exit(pack.status ?? 1);
+
+console.log('npm install in examples/pear-discord-bot …');
+const npm = spawnSync(npmBin, ['install'], { cwd: pearDir, stdio: 'inherit', shell: false });
+if (npm.status !== 0) process.exit(npm.status ?? 1);
+
+const libPkg = JSON.parse(readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
+const libInstalled = path.join(pearDir, 'node_modules', 'bare-discord-js');
+console.log('Reinstall bare-discord-js from fresh tarball (npm may otherwise keep a stale extract) …');
+rmSync(libInstalled, { recursive: true, force: true });
+const reinstallLib = spawnSync(
+ npmBin,
+ ['install', `file:bare-discord-js-${libPkg.version}.tgz`, '--no-save'],
+ { cwd: pearDir, stdio: 'inherit', shell: false }
+);
+if (reinstallLib.status !== 0) process.exit(reinstallLib.status ?? 1);
+
+console.log('Applying runtime patches to pear app node_modules …');
+const patch = spawnSync(process.execPath, ['scripts/patch-runtime-deps.mjs'], {
+ cwd: repoRoot,
+ stdio: 'inherit',
+ env: {
+ ...process.env,
+ BARE_DISCORD_REPO_ROOT: repoRoot,
+ BARE_DISCORD_PATCH_TARGETS_ROOT: pearDir
+ }
+});
+if (patch.status !== 0) process.exit(patch.status ?? 1);
+
+const bareImportsSrc = path.join(pearDir, 'node_modules', 'bare-node-runtime', 'imports.json');
+const bareImportsDst = path.join(pearDir, 'bare-imports.json');
+if (existsSync(bareImportsSrc)) {
+ cpSync(bareImportsSrc, bareImportsDst);
+ console.log('Wrote bare-imports.json (Pear reads import map via bare-fs, not pear:// JSON).');
+}
+
+/** Pear often omits deep node_modules trees; ship discord.js as app-owned files. */
+const discordSrc = path.join(pearDir, 'node_modules', 'discord.js');
+const discordVendor = path.join(pearDir, 'vendor', 'discord.js');
+if (existsSync(discordSrc)) {
+ rmSync(discordVendor, { recursive: true, force: true });
+ mkdirSync(path.dirname(discordVendor), { recursive: true });
+ cpSync(discordSrc, discordVendor, { recursive: true });
+ console.log('Vendored discord.js → examples/pear-discord-bot/vendor/discord.js (Pear staging).');
+}
+
+/** Align patched artifacts with the repo root tree (nested semver/layout can miss regex rules). */
+const vendoredPatches = [
+ 'node_modules/undici/lib/dispatcher/client.js',
+ 'node_modules/@discordjs/rest/package.json',
+ 'node_modules/@discordjs/ws/dist/index.js',
+ 'node_modules/@discordjs/ws/dist/index.mjs'
+];
+for (const rel of vendoredPatches) {
+ const from = path.join(repoRoot, rel);
+ const to = path.join(pearDir, rel);
+ if (existsSync(from)) {
+ mkdirSync(path.dirname(to), { recursive: true });
+ cpSync(from, to);
+ }
+}
+
+console.log(
+ 'Pear release prep done. Next: cd examples/pear-discord-bot && npm run pear:ship -- '
+);
diff --git a/native-host/vendor/bare-discord-js/scripts/pear-run.mjs b/native-host/vendor/bare-discord-js/scripts/pear-run.mjs
new file mode 100644
index 0000000..db1eca3
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/pear-run.mjs
@@ -0,0 +1,83 @@
+#!/usr/bin/env node
+/**
+ * Usage (from examples/pear-discord-bot):
+ * npm run pear:stage [-- ]
+ * npm run pear:release [-- ]
+ * npm run pear:ship [-- ] # prepare once, then stage + release
+ *
+ * With no CLI args after the subcommand, `pear.channel` from examples/pear-discord-bot/package.json
+ * is used (fallback: pear.name). Override by passing first.
+ */
+
+import { spawnSync } from 'node:child_process';
+import { readFileSync } from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const repoRoot = path.join(__dirname, '..');
+const pearDir = path.join(repoRoot, 'examples', 'pear-discord-bot');
+
+const sub = process.argv[2];
+let pearArgs = process.argv.slice(3);
+
+function defaultPearChannel() {
+ const pkgPath = path.join(pearDir, 'package.json');
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
+ const ch = pkg.pear?.channel ?? pkg.pear?.name;
+ if (!ch || typeof ch !== 'string') {
+ console.error(
+ 'examples/pear-discord-bot/package.json must define pear.channel or pear.name for stage/release/ship'
+ );
+ process.exit(1);
+ }
+ return ch;
+}
+
+function ensureDefaultPearArgs() {
+ if (pearArgs.length === 0) {
+ pearArgs = [defaultPearChannel()];
+ }
+}
+
+function runPrepare() {
+ const prep = spawnSync(process.execPath, [path.join(repoRoot, 'scripts', 'pear-prepare-release.mjs')], {
+ cwd: repoRoot,
+ stdio: 'inherit'
+ });
+ if (prep.status !== 0) process.exit(prep.status ?? 1);
+}
+
+const pearBin = process.platform === 'win32' ? 'pear.cmd' : 'pear';
+
+if (sub === 'ship') {
+ ensureDefaultPearArgs();
+ runPrepare();
+ const st = spawnSync(pearBin, ['stage', ...pearArgs], {
+ cwd: pearDir,
+ stdio: 'inherit',
+ shell: process.platform === 'win32'
+ });
+ if (st.status !== 0) process.exit(st.status ?? 1);
+ const rel = spawnSync(pearBin, ['release', ...pearArgs], {
+ cwd: pearDir,
+ stdio: 'inherit',
+ shell: process.platform === 'win32'
+ });
+ process.exit(rel.status ?? 1);
+}
+
+if (sub !== 'stage' && sub !== 'release') {
+ console.error('Usage: pear-run.mjs [...pear-args]');
+ process.exit(1);
+}
+
+ensureDefaultPearArgs();
+runPrepare();
+
+const pr = spawnSync(pearBin, [sub, ...pearArgs], {
+ cwd: pearDir,
+ stdio: 'inherit',
+ shell: process.platform === 'win32'
+});
+process.exit(pr.status ?? 1);
diff --git a/native-host/vendor/bare-discord-js/scripts/release-verify.mjs b/native-host/vendor/bare-discord-js/scripts/release-verify.mjs
new file mode 100644
index 0000000..f02329d
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/release-verify.mjs
@@ -0,0 +1,24 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const required = [
+ path.join(process.cwd(), 'artifacts', 'analysis', 'discordjs-surface.json'),
+ path.join(process.cwd(), 'artifacts', 'analysis', 'bare-surface.json'),
+ path.join(process.cwd(), 'patches', 'patch-manifest.json')
+];
+
+const missing = [];
+for (const file of required) {
+ try {
+ await fs.access(file);
+ } catch {
+ missing.push(file);
+ }
+}
+
+if (missing.length) {
+ console.error('Release verification failed. Missing artifacts:\n' + missing.join('\n'));
+ process.exit(1);
+}
+
+console.log('Release verification passed.');
diff --git a/native-host/vendor/bare-discord-js/scripts/sync-imports-map.mjs b/native-host/vendor/bare-discord-js/scripts/sync-imports-map.mjs
new file mode 100644
index 0000000..07b291d
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/sync-imports-map.mjs
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+/**
+ * Refresh the shipped Node→Bare import map from the installed bare-node-runtime.
+ * Also writes a CJS module so Pear `pear://` loads do not need to read JSON from disk.
+ */
+import fs from 'node:fs';
+import path from 'node:path';
+import { createRequire } from 'node:module';
+import { fileURLToPath } from 'node:url';
+
+const require = createRequire(import.meta.url);
+const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
+const bootstrapDir = path.join(repoRoot, 'src', 'bootstrap');
+const fallbackPath = path.join(repoRoot, 'patches', 'imports-fallback.json');
+
+function loadUpstreamImports() {
+ try {
+ const resolved = require.resolve('bare-node-runtime/imports');
+ return JSON.parse(fs.readFileSync(resolved, 'utf8'));
+ } catch {
+ const local = path.join(bootstrapDir, 'node-imports-map.json');
+ if (fs.existsSync(local)) {
+ return JSON.parse(fs.readFileSync(local, 'utf8'));
+ }
+ throw new Error('Unable to resolve bare-node-runtime/imports');
+ }
+}
+
+const imports = loadUpstreamImports();
+const json = JSON.stringify(imports, null, 2) + '\n';
+const cjs = `'use strict';\nmodule.exports = ${JSON.stringify(imports)};\n`;
+
+fs.mkdirSync(bootstrapDir, { recursive: true });
+fs.writeFileSync(path.join(bootstrapDir, 'node-imports-map.json'), json);
+fs.writeFileSync(path.join(bootstrapDir, 'node-imports-map.cjs'), cjs);
+fs.writeFileSync(fallbackPath, json);
+
+console.log('Synced Node→Bare import map from bare-node-runtime/imports');
diff --git a/native-host/vendor/bare-discord-js/scripts/sync-rebase-patches.mjs b/native-host/vendor/bare-discord-js/scripts/sync-rebase-patches.mjs
new file mode 100644
index 0000000..ccd7492
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/sync-rebase-patches.mjs
@@ -0,0 +1,16 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const outPath = path.join(process.cwd(), 'artifacts', 'analysis', 'patch-rebase-report.json');
+const report = {
+ generatedAt: new Date().toISOString(),
+ status: 'simulated',
+ notes: [
+ 'Patch rebase automation placeholder.',
+ 'Integrate with upstream sync once patches become concrete.'
+ ]
+};
+
+await fs.mkdir(path.dirname(outPath), { recursive: true });
+await fs.writeFile(outPath, JSON.stringify(report, null, 2));
+console.log('Wrote', outPath);
diff --git a/native-host/vendor/bare-discord-js/scripts/sync-upstream.mjs b/native-host/vendor/bare-discord-js/scripts/sync-upstream.mjs
new file mode 100644
index 0000000..dd59ea0
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/sync-upstream.mjs
@@ -0,0 +1,18 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const report = {
+ generatedAt: new Date().toISOString(),
+ target: 'discord.js 14.27.x',
+ actions: [
+ 'Fetch latest v14.x metadata',
+ 'Re-run analysis scripts',
+ 'Regenerate patch manifest',
+ 'Run parity suite'
+ ]
+};
+
+const outPath = path.join(process.cwd(), 'artifacts', 'analysis', 'upstream-sync-report.json');
+await fs.mkdir(path.dirname(outPath), { recursive: true });
+await fs.writeFile(outPath, JSON.stringify(report, null, 2));
+console.log('Wrote', outPath);
diff --git a/native-host/vendor/bare-discord-js/scripts/verify-bootstrap-load.mjs b/native-host/vendor/bare-discord-js/scripts/verify-bootstrap-load.mjs
new file mode 100644
index 0000000..488e0a5
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/verify-bootstrap-load.mjs
@@ -0,0 +1,30 @@
+import { createRequire } from 'node:module';
+import path from 'node:path';
+
+const require = createRequire(import.meta.url);
+const projectRoot = path.join(process.cwd());
+
+function fail(message) {
+ console.error(message);
+ process.exit(1);
+}
+
+try {
+ const cjsEntry = require(path.join(projectRoot, 'src', 'index.js'));
+ if (!cjsEntry || typeof cjsEntry.Client !== 'function') {
+ fail('CJS entry does not expose discord.js Client.');
+ }
+} catch (error) {
+ fail(`CJS bootstrap failed: ${error.message}`);
+}
+
+try {
+ const esmEntry = await import(path.join(projectRoot, 'src', 'index.mjs'));
+ if (!esmEntry || typeof esmEntry.Client !== 'function') {
+ fail('ESM entry does not expose discord.js Client.');
+ }
+} catch (error) {
+ fail(`ESM bootstrap failed: ${error.message}`);
+}
+
+console.log('Bootstrap verification passed.');
diff --git a/native-host/vendor/bare-discord-js/scripts/verify-parity.mjs b/native-host/vendor/bare-discord-js/scripts/verify-parity.mjs
new file mode 100644
index 0000000..eb058a7
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/scripts/verify-parity.mjs
@@ -0,0 +1,19 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const reportPath = path.join(process.cwd(), 'artifacts', 'analysis', 'parity-report.json');
+await fs.mkdir(path.dirname(reportPath), { recursive: true });
+
+const report = {
+ generatedAt: new Date().toISOString(),
+ strictParityMode: true,
+ suites: [
+ { name: 'bootstrap-load', status: 'planned' },
+ { name: 'gateway-login-smoke', status: 'planned' },
+ { name: 'rest-rate-limit', status: 'planned' },
+ { name: 'voice-transport', status: 'planned' }
+ ]
+};
+
+await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
+console.log('Wrote', reportPath);
diff --git a/native-host/vendor/bare-discord-js/src/adapters/crypto.cjs b/native-host/vendor/bare-discord-js/src/adapters/crypto.cjs
new file mode 100644
index 0000000..ecd45be
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/crypto.cjs
@@ -0,0 +1,19 @@
+function createCryptoAdapter() {
+ return {
+ id: 'crypto',
+ apply() {
+ if (typeof Bare === 'undefined') return true;
+ if (globalThis.crypto) return true;
+ try {
+ require('bare-crypto/global');
+ } catch {
+ // Optional; mapped `node:crypto` still covers most discord.js usage.
+ }
+ return true;
+ }
+ };
+}
+
+module.exports = {
+ createCryptoAdapter
+};
diff --git a/native-host/vendor/bare-discord-js/src/adapters/form-data.cjs b/native-host/vendor/bare-discord-js/src/adapters/form-data.cjs
new file mode 100644
index 0000000..e00057b
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/form-data.cjs
@@ -0,0 +1,68 @@
+/**
+ * discord.js REST builds multipart bodies with `new FormData()` / `new Blob()`.
+ * Bare has neither global; install `bare-form-data` classes so `instanceof FormData`
+ * matches on the way out. Inlined from Bare OS (the vendor re-export path does not exist here).
+ */
+'use strict'
+
+function isBareRuntime() {
+ if (typeof Bare !== 'undefined') return true
+ const versions =
+ typeof process !== 'undefined' && process.versions ? process.versions : null
+ return Boolean(versions && typeof versions.bare === 'string')
+}
+
+function assignGlobal(name, value) {
+ if (typeof value !== 'function') return
+ try {
+ globalThis[name] = value
+ } catch {
+ /* frozen */
+ }
+ try {
+ if (typeof global !== 'undefined') global[name] = value
+ } catch {
+ /* frozen */
+ }
+}
+
+function loadBareFormData() {
+ try {
+ return require('bare-form-data')
+ } catch {
+ return null
+ }
+}
+
+function installBareOsFormDataGlobal() {
+ if (!isBareRuntime()) return typeof globalThis.FormData === 'function'
+ const fd = loadBareFormData()
+ if (!fd) {
+ try {
+ require('bare-form-data/global')
+ } catch {
+ /* optional */
+ }
+ return typeof globalThis.FormData === 'function'
+ }
+ const FormData = typeof fd === 'function' ? fd : fd.FormData
+ assignGlobal('FormData', FormData)
+ assignGlobal('Blob', fd.Blob)
+ assignGlobal('File', fd.File)
+ return typeof globalThis.FormData === 'function'
+}
+
+function createFormDataAdapter() {
+ return {
+ id: 'form-data',
+ apply() {
+ installBareOsFormDataGlobal()
+ return true
+ }
+ }
+}
+
+module.exports = {
+ createFormDataAdapter,
+ installBareOsFormDataGlobal
+}
diff --git a/native-host/vendor/bare-discord-js/src/adapters/http-tls.cjs b/native-host/vendor/bare-discord-js/src/adapters/http-tls.cjs
new file mode 100644
index 0000000..721eb7d
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/http-tls.cjs
@@ -0,0 +1,19 @@
+function createHttpTlsAdapter() {
+ return {
+ id: 'http-tls',
+ apply() {
+ if (typeof Bare === 'undefined') return true;
+ if (typeof globalThis.fetch === 'function') return true;
+ try {
+ require('bare-fetch/global');
+ } catch {
+ // REST web build and discord.js Util/DataResolver use global fetch.
+ }
+ return true;
+ }
+ };
+}
+
+module.exports = {
+ createHttpTlsAdapter
+};
diff --git a/native-host/vendor/bare-discord-js/src/adapters/index.cjs b/native-host/vendor/bare-discord-js/src/adapters/index.cjs
new file mode 100644
index 0000000..b777413
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/index.cjs
@@ -0,0 +1,33 @@
+const { createWsAdapter } = require('./ws.cjs');
+const { createHttpTlsAdapter } = require('./http-tls.cjs');
+const { createZlibAdapter } = require('./zlib.cjs');
+const { createCryptoAdapter } = require('./crypto.cjs');
+const { createFormDataAdapter } = require('./form-data.cjs');
+
+function applyDiscordJsCompatShims(discord) {
+ if (!discord || typeof discord !== 'object') return discord;
+ const events = discord.Events;
+ if (events && events.ClientReady && events.Ready == null) {
+ events.Ready = events.ClientReady;
+ }
+ return discord;
+}
+
+function applyRuntimeAdapters() {
+ const adapters = [
+ createFormDataAdapter(),
+ createWsAdapter(),
+ createHttpTlsAdapter(),
+ createZlibAdapter(),
+ createCryptoAdapter()
+ ];
+
+ for (const adapter of adapters) {
+ adapter.apply();
+ }
+}
+
+module.exports = {
+ applyRuntimeAdapters,
+ applyDiscordJsCompatShims
+};
diff --git a/native-host/vendor/bare-discord-js/src/adapters/index.mjs b/native-host/vendor/bare-discord-js/src/adapters/index.mjs
new file mode 100644
index 0000000..239c1a5
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/index.mjs
@@ -0,0 +1,7 @@
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+
+export function applyRuntimeAdapters() {
+ return require('./index.cjs').applyRuntimeAdapters();
+}
diff --git a/native-host/vendor/bare-discord-js/src/adapters/whatwg-ws.cjs b/native-host/vendor/bare-discord-js/src/adapters/whatwg-ws.cjs
new file mode 100644
index 0000000..519476b
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/whatwg-ws.cjs
@@ -0,0 +1,194 @@
+/**
+ * WHATWG WebSocket facade over bare-ws.Socket.
+ * @discordjs/ws calls `new WebSocket(url, protocols, opts)` and uses
+ * onmessage/send. bare-ws.Socket is a Duplex (`write` / `data`) — using it
+ * as globalThis.WebSocket makes IDENTIFY never produce READY.
+ */
+'use strict'
+
+function createWhatwgWebSocket() {
+ const BareSocket = require('bare-ws').Socket
+
+ class WhatwgWebSocket {
+ constructor(url, protocols, options) {
+ if (protocols && !Array.isArray(protocols) && typeof protocols === 'object') {
+ options = protocols
+ }
+ this.url = String(url || '')
+ this.readyState = WhatwgWebSocket.CONNECTING
+ this.binaryType = 'arraybuffer'
+ this.protocol = ''
+ this.extensions = ''
+ this.onopen = null
+ this.onmessage = null
+ this.onerror = null
+ this.onclose = null
+ this._listeners = Object.create(null)
+
+ const opts = options && typeof options === 'object' ? Object.assign({}, options) : {}
+ if (opts.rejectUnauthorized == null) opts.rejectUnauthorized = false
+
+ const self = this
+ this._ws = new BareSocket(this.url, opts)
+
+ this._ws.on('open', function () {
+ self.readyState = WhatwgWebSocket.OPEN
+ self._dispatch('open', { type: 'open', target: self })
+ })
+ this._ws.on('data', function (chunk) {
+ let data = chunk
+ if (typeof chunk !== 'string' && chunk && typeof chunk.toString === 'function') {
+ const text = chunk.toString()
+ const c0 = text.charCodeAt(0)
+ if (c0 === 0x7b || c0 === 0x5b) data = text
+ }
+ self._dispatch('message', { type: 'message', data: data, target: self })
+ })
+ this._ws.on('error', function (err) {
+ self._dispatch('error', {
+ type: 'error',
+ error: err,
+ message: err && err.message,
+ target: self
+ })
+ })
+ this._ws.on('close', function () {
+ self.readyState = WhatwgWebSocket.CLOSED
+ self._dispatch('close', {
+ type: 'close',
+ code: 1000,
+ reason: '',
+ wasClean: true,
+ target: self
+ })
+ })
+ }
+
+ send(data) {
+ if (this.readyState !== WhatwgWebSocket.OPEN) {
+ throw new Error('WebSocket is not open')
+ }
+ if (typeof data === 'string') this._ws.write(data)
+ else this._ws.write(data)
+ }
+
+ close() {
+ if (
+ this.readyState === WhatwgWebSocket.CLOSING ||
+ this.readyState === WhatwgWebSocket.CLOSED
+ ) {
+ return
+ }
+ this.readyState = WhatwgWebSocket.CLOSING
+ try {
+ this._ws.end()
+ } catch {
+ try {
+ this._ws.destroy()
+ } catch {
+ /* ignore */
+ }
+ }
+ }
+
+ ping(data) {
+ if (this._ws && typeof this._ws.ping === 'function') this._ws.ping(data)
+ }
+
+ addEventListener(type, fn) {
+ if (typeof fn !== 'function') return
+ if (!this._listeners[type]) this._listeners[type] = []
+ this._listeners[type].push(fn)
+ }
+
+ removeEventListener(type, fn) {
+ const list = this._listeners[type]
+ if (!list) return
+ this._listeners[type] = list.filter(function (x) {
+ return x !== fn
+ })
+ }
+
+ _dispatch(type, event) {
+ const handler = this['on' + type]
+ if (typeof handler === 'function') {
+ try {
+ handler.call(this, event)
+ } catch {
+ /* isolate */
+ }
+ }
+ const list = this._listeners[type] || []
+ for (let i = 0; i < list.length; i++) {
+ try {
+ list[i].call(this, event)
+ } catch {
+ /* isolate */
+ }
+ }
+ }
+ }
+
+ WhatwgWebSocket.CONNECTING = 0
+ WhatwgWebSocket.OPEN = 1
+ WhatwgWebSocket.CLOSING = 2
+ WhatwgWebSocket.CLOSED = 3
+ WhatwgWebSocket.bareOsDiscordGatewayWs = 'bare-os-discord-gateway-ws'
+ return WhatwgWebSocket
+}
+
+function installBareOsProcessEmitWarning(proc) {
+ const p =
+ proc ||
+ (typeof globalThis.process !== 'undefined' ? globalThis.process : null)
+ if (!p || typeof p.emitWarning === 'function') return p
+ p.emitWarning = function emitWarning(warning, type, code) {
+ const msg = warning instanceof Error ? warning.message : String(warning)
+ const name = typeof type === 'string' ? type : 'Warning'
+ const id = typeof code === 'string' ? code : ''
+ const line = id ? name + ' [' + id + ']: ' + msg : name + ': ' + msg
+ try {
+ if (typeof p.emit === 'function') p.emit('warning', warning)
+ } catch {
+ /* ignore */
+ }
+ try {
+ if (typeof console !== 'undefined' && typeof console.error === 'function') {
+ console.error(line)
+ }
+ } catch {
+ /* ignore */
+ }
+ }
+ return p
+}
+
+function installBareOsDiscordGatewayWs() {
+ installBareOsProcessEmitWarning()
+ const WS = createWhatwgWebSocket()
+ const versions =
+ typeof process !== 'undefined' && process.versions ? process.versions : null
+ if (versions && versions.bun == null) {
+ try {
+ versions.bun = 'bare-os'
+ } catch {
+ /* frozen */
+ }
+ }
+ if (typeof globalThis.fetch !== 'function') {
+ try {
+ require('bare-fetch/global')
+ } catch {
+ /* optional */
+ }
+ }
+ globalThis.WebSocket = WS
+ if (typeof global !== 'undefined') global.WebSocket = WS
+ return WS
+}
+
+module.exports = {
+ createWhatwgWebSocket,
+ installBareOsDiscordGatewayWs,
+ installBareOsProcessEmitWarning
+}
diff --git a/native-host/vendor/bare-discord-js/src/adapters/ws.cjs b/native-host/vendor/bare-discord-js/src/adapters/ws.cjs
new file mode 100644
index 0000000..1f3641f
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/ws.cjs
@@ -0,0 +1,18 @@
+function createWsAdapter() {
+ return {
+ id: 'ws',
+ apply() {
+ if (typeof Bare === 'undefined') return true;
+ try {
+ require('./whatwg-ws.cjs').installBareOsDiscordGatewayWs();
+ } catch {
+ /* optional when bare-ws is unavailable */
+ }
+ return true;
+ }
+ };
+}
+
+module.exports = {
+ createWsAdapter
+};
diff --git a/native-host/vendor/bare-discord-js/src/adapters/zlib.cjs b/native-host/vendor/bare-discord-js/src/adapters/zlib.cjs
new file mode 100644
index 0000000..b12162b
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/adapters/zlib.cjs
@@ -0,0 +1,13 @@
+function createZlibAdapter() {
+ return {
+ id: 'zlib',
+ apply() {
+ // Gateway compression uses mapped `bare-zlib`. zlib-sync is optional.
+ return true;
+ }
+ };
+}
+
+module.exports = {
+ createZlibAdapter
+};
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/global.cjs b/native-host/vendor/bare-discord-js/src/bootstrap/global.cjs
new file mode 100644
index 0000000..366f771
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/global.cjs
@@ -0,0 +1,57 @@
+const BARE_GLOBAL_FALLBACKS = [
+ 'bare-abort-controller/global',
+ 'bare-crypto/global',
+ 'bare-encoding/global',
+ 'bare-events/global',
+ 'bare-fetch/global',
+ 'bare-form-data/global',
+ 'bare-performance/global',
+ 'bare-process/global',
+ 'bare-stream/global',
+ 'bare-ws/global'
+];
+
+function ensureNodeCompatGlobals() {
+ if (typeof process === 'undefined') return;
+ try {
+ if (!process.versions) process.versions = {};
+ } catch {
+ return;
+ }
+ const versions = process.versions;
+ if (!versions || typeof versions !== 'object') return;
+ if (!versions.node) {
+ try {
+ versions.node = '20.0.0';
+ } catch {
+ /* frozen / getter-only */
+ }
+ }
+}
+
+function loadFallbackGlobals() {
+ for (const specifier of BARE_GLOBAL_FALLBACKS) {
+ try {
+ require(specifier);
+ } catch {
+ // Individual globals may be unavailable on a slim install.
+ }
+ }
+}
+
+function setupBareGlobals() {
+ let loaded = false;
+ try {
+ require('bare-node-runtime/global');
+ loaded = true;
+ } catch {
+ loadFallbackGlobals();
+ }
+
+ ensureNodeCompatGlobals();
+ return loaded;
+}
+
+module.exports = {
+ setupBareGlobals
+};
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/global.mjs b/native-host/vendor/bare-discord-js/src/bootstrap/global.mjs
new file mode 100644
index 0000000..833d424
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/global.mjs
@@ -0,0 +1,7 @@
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+
+export function setupBareGlobals() {
+ return require('./global.cjs').setupBareGlobals();
+}
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.cjs b/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.cjs
new file mode 100644
index 0000000..aea81c2
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.cjs
@@ -0,0 +1,12 @@
+function getImportsSpecifier() {
+ try {
+ require.resolve('bare-node-runtime/imports');
+ return 'bare-node-runtime/imports';
+ } catch {
+ return '../patches/imports-fallback.json';
+ }
+}
+
+module.exports = {
+ getImportsSpecifier
+};
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.mjs b/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.mjs
new file mode 100644
index 0000000..f9ff0bd
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/imports-map.mjs
@@ -0,0 +1,11 @@
+import { createRequire } from 'node:module';
+const require = createRequire(import.meta.url);
+
+export function getImportsSpecifier() {
+ try {
+ require.resolve('bare-node-runtime/imports');
+ return 'bare-node-runtime/imports';
+ } catch {
+ return '../patches/imports-fallback.json';
+ }
+}
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.cjs b/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.cjs
new file mode 100644
index 0000000..3e1092f
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.cjs
@@ -0,0 +1,350 @@
+const { getImportsSpecifier } = require('./imports-map.cjs');
+
+function isBareRuntime() {
+ return typeof Bare !== 'undefined';
+}
+
+const fs = isBareRuntime() ? require('bare-fs') : require('node:fs');
+const path = isBareRuntime() ? require('bare-path') : require('node:path');
+const bareUrl = isBareRuntime() ? require('bare-url') : require('node:url');
+
+/** `bare-path` join breaks `pear://key/...` → `pear:/key/...`; keep drive URLs intact. */
+function normalizePearDriveUrl(u) {
+ if (typeof u !== 'string' || !u.startsWith('pear:')) return u;
+ const t = u.replace(/\/+$/, '');
+ if (t.startsWith('pear://')) return t;
+ return `pear://${t.replace(/^pear:\/?/, '')}`;
+}
+
+function joinUnderRoot(root, ...segments) {
+ const base = normalizePearDriveUrl(root);
+ if (typeof base === 'string' && base.startsWith('pear://')) {
+ const tail = segments
+ .map((s) => String(s).replace(/^\/+|\/+$/g, ''))
+ .filter(Boolean)
+ .join('/');
+ return tail ? `${base}/${tail}` : base;
+ }
+ return path.join(base, ...segments);
+}
+
+/** `bare-path` dirname breaks `pear://` URLs; used when walking resolved discord paths. */
+function pearSafeDirname(filePath) {
+ const s = normalizePearDriveUrl(String(filePath));
+ if (!s.startsWith('pear://')) {
+ return path.dirname(filePath);
+ }
+ try {
+ const u = new bareUrl.URL(s);
+ const pathname = (u.pathname || '').replace(/\/+$/, '');
+ const slash = pathname.lastIndexOf('/');
+ if (slash <= 0) {
+ return `${u.protocol}//${u.host}`;
+ }
+ return `${u.protocol}//${u.host}${pathname.slice(0, slash)}`;
+ } catch {
+ const trimmed = s.replace(/\/+$/, '');
+ const idx = trimmed.lastIndexOf('/');
+ if (idx <= 6) return trimmed;
+ return trimmed.slice(0, idx);
+ }
+}
+
+function dirnameSafe(p) {
+ const s = String(p);
+ if (s.startsWith('pear:')) return pearSafeDirname(s);
+ return path.dirname(p);
+}
+
+/**
+ * Bare-module CJS passes the *Module instance itself* as the `module` arg to the wrapped
+ * function (see `bare-module/index.js` _evaluate → createRequire(this._url, { module: this })).
+ * That instance carries `_protocol`/`_resolutions`/`_cache` from the loader. On Pear, that
+ * loader is the runtime which set a `pear://`-aware protocol; we propagate it via `referrer`
+ * to any `Module.createRequire` call so resolution can `protocol.exists/read` Pear URLs.
+ */
+function findPearAwareReferrer(currentModule) {
+ const candidates = [];
+ if (currentModule && typeof currentModule === 'object') candidates.push(currentModule);
+ try {
+ if (require.main) candidates.push(require.main);
+ } catch {
+ // ignore
+ }
+ try {
+ const cache = require.cache;
+ if (cache && typeof cache === 'object') {
+ for (const key of Object.keys(cache)) {
+ if (typeof key === 'string' && key.startsWith('pear:')) {
+ candidates.push(cache[key]);
+ }
+ }
+ }
+ } catch {
+ // ignore
+ }
+
+ for (const c of candidates) {
+ if (
+ c &&
+ typeof c === 'object' &&
+ c._protocol &&
+ typeof c._protocol.exists === 'function' &&
+ typeof c._protocol.read === 'function'
+ ) {
+ return c;
+ }
+ }
+ return null;
+}
+
+/**
+ * Load Node→Bare import mappings. Order:
+ * 1. `BARE_DISCORD_IMPORT_MAP_PATH` when present (app `bare-imports.json` after prepare).
+ * 2. **`require('./node-imports-map.cjs')`** — always shipped with bare-discord-js; works on
+ * `pear://` drives (reading JSON via `require.resolve` + `readFileSync` breaks: resolves to
+ * bogus `/node_modules/...` host paths).
+ * 3. `bare-node-runtime/imports` JSON file.
+ * 4. Sibling `node-imports-map.json` via filesystem.
+ *
+ * Note: `examples/pear-discord-bot/.gitignore` lists `bare-imports.json`, so Pear stage often
+ * omits it; (2) keeps `pear run pear://…` working from any cwd. Local `pear run .` still uses (1)
+ * when the file exists on disk.
+ */
+/**
+ * Some Bare modules in the upstream Node→Bare imports map self-initialise on require
+ * (`bare-worker` spawns a Thread, `bare-vm`/`bare-v8`/`bare-inspector` load native bindings).
+ * For runtime aliasing inside discord.js's transitive deps we don't need those — undici
+ * `require('node:worker_threads')` lazily, only on opt-in features. Redirect the spec-keys to
+ * `bare-node-runtime/unsupported` (a no-op stub `bare-discord-js/preload-bare-modules.cjs`
+ * already includes) so `pear stage` doesn't pull side-effecting modules into the bundle.
+ */
+const SAFE_OVERRIDES = (() => {
+ const stub = { bare: 'bare-node-runtime/unsupported', default: 'bare-node-runtime/unsupported' };
+ const keys = [
+ 'worker_threads',
+ 'node:worker_threads',
+ 'vm',
+ 'node:vm',
+ 'v8',
+ 'node:v8',
+ 'inspector',
+ 'node:inspector',
+ 'inspector/promises',
+ 'node:inspector/promises',
+ 'child_process',
+ 'node:child_process',
+ 'repl',
+ 'node:repl',
+ 'tty',
+ 'node:tty',
+ 'readline',
+ 'node:readline',
+ 'readline/promises',
+ 'node:readline/promises',
+ 'sqlite',
+ 'node:sqlite'
+ ];
+ const out = {};
+ for (const k of keys) out[k] = stub;
+ return out;
+})();
+
+function applyImportsOverrides(map) {
+ if (!map || typeof map !== 'object') return map;
+ return Object.assign({}, map, SAFE_OVERRIDES);
+}
+
+function loadRuntimeImportsMap() {
+ const envPath = process.env.BARE_DISCORD_IMPORT_MAP_PATH;
+ if (typeof envPath === 'string' && envPath.length > 0) {
+ try {
+ if (fs.existsSync(envPath)) {
+ return applyImportsOverrides(JSON.parse(fs.readFileSync(envPath, 'utf8')));
+ }
+ } catch {
+ // bare-fs may reject non-file URLs; the embedded map below covers pear:// runs.
+ }
+ }
+ try {
+ return applyImportsOverrides(require('./node-imports-map.cjs'));
+ } catch {
+ // ignore
+ }
+ try {
+ const candidate = require.resolve('bare-node-runtime/imports');
+ if (fs.existsSync(candidate)) {
+ return applyImportsOverrides(JSON.parse(fs.readFileSync(candidate, 'utf8')));
+ }
+ } catch {
+ // bare-node-runtime may be unavailable.
+ }
+ const embedded = path.join(__dirname, 'node-imports-map.json');
+ try {
+ if (fs.existsSync(embedded)) {
+ return applyImportsOverrides(JSON.parse(fs.readFileSync(embedded, 'utf8')));
+ }
+ } catch {
+ // ignore
+ }
+ return null;
+}
+
+function resolveCreateRequireAnchor() {
+ const env = process.env.BARE_DISCORD_REQUIRE_ANCHOR;
+ if (typeof env === 'string' && env.length > 0) {
+ return normalizePearDriveUrl(env);
+ }
+ return path.join(__dirname, '..', 'index.js');
+}
+
+/**
+ * discord.js package "." entry — Bare cannot resolve the bare specifier from the
+ * package's own package.json anchor; load ./src/index.js (exports.require).
+ */
+function requireDiscordDotEntry(req, packageJsonPath) {
+ let pkg = null;
+ try {
+ pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
+ } catch {
+ return req('./src/index.js');
+ }
+ let rel = null;
+ const dot = pkg.exports && pkg.exports['.'];
+ if (dot && typeof dot === 'object' && dot !== null && dot.require) {
+ const r = dot.require;
+ rel = typeof r === 'string' ? r : r.default;
+ }
+ if (!rel && pkg.main) rel = pkg.main;
+ if (!rel) rel = './index.js';
+ if (!rel.startsWith('.')) rel = `./${rel}`;
+ return req(rel);
+}
+
+/**
+ * Nested install, Pear hoisted sibling, monorepo hoisted.
+ * Pear: `path.join(__dirname, '..', '..', …)` can normalize to bogus host paths like
+ * `file:///node_modules/discord.js/…` when `__dirname` is wrong; prefer resolution via
+ * `require.resolve('discord.js', { paths })` then walk up to `discord.js`'s package.json.
+ * Apps may set `BARE_DISCORD_DISCORD_ROOT` to a vendored tree (e.g. `./vendor/discord.js`).
+ */
+function resolveDiscordPackageJsonPath() {
+ const vendorRoot = process.env.BARE_DISCORD_DISCORD_ROOT;
+ if (typeof vendorRoot === 'string' && vendorRoot.length > 0) {
+ return joinUnderRoot(vendorRoot, 'package.json');
+ }
+
+ const bareDiscordPkgRoot = path.join(__dirname, '..', '..');
+ function discordPkgFromResolvedEntry() {
+ let entry = null;
+ try {
+ entry = require.resolve('discord.js', { paths: [bareDiscordPkgRoot] });
+ } catch {
+ try {
+ entry = require.resolve('discord.js');
+ } catch {
+ return null;
+ }
+ }
+ let dir = dirnameSafe(entry);
+ for (let i = 0; i < 8; i++) {
+ const pkgJson = joinUnderRoot(dir, 'package.json');
+ try {
+ if (fs.existsSync(pkgJson)) {
+ const name = JSON.parse(fs.readFileSync(pkgJson, 'utf8')).name;
+ if (name === 'discord.js') return pkgJson;
+ }
+ } catch {
+ // ignore
+ }
+ const parent = dirnameSafe(dir);
+ if (parent === dir) break;
+ dir = parent;
+ }
+ return null;
+ }
+
+ const resolved = discordPkgFromResolvedEntry();
+ if (resolved) return resolved;
+
+ const candidates = [
+ path.join(__dirname, '..', '..', '..', 'discord.js', 'package.json'),
+ path.join(__dirname, '..', '..', '..', 'node_modules', 'discord.js', 'package.json'),
+ path.join(__dirname, '..', '..', '..', '..', 'node_modules', 'discord.js', 'package.json'),
+ path.join(__dirname, '..', 'node_modules', 'discord.js', 'package.json')
+ ];
+ for (const p of candidates) {
+ try {
+ if (fs.existsSync(p)) return p;
+ } catch {
+ // bare-fs may throw on some hosts.
+ }
+ }
+ return null;
+}
+
+function inBareAppBundle(currentModule) {
+ const hints = [
+ typeof __filename === 'string' ? __filename : '',
+ typeof __dirname === 'string' ? __dirname : '',
+ currentModule && currentModule.filename,
+ currentModule && currentModule.url,
+ currentModule && currentModule._url
+ ];
+ for (const h of hints) {
+ const s = String(h || '');
+ if (s.startsWith('bare:') || s.includes('app.bundle')) return true;
+ }
+ return false;
+}
+
+function loadDiscordWithRuntimeMappings(currentModule) {
+ if (!isBareRuntime()) {
+ return require('discord.js');
+ }
+
+ // bare-pack rewrites this literal require into the app.bundle binding.
+ // createRequire(anchor).require('discord.js') does not resolve under bare:/app.bundle/.
+ if (inBareAppBundle(currentModule)) {
+ return require('discord.js');
+ }
+
+ const map = loadRuntimeImportsMap();
+ const referrer = findPearAwareReferrer(currentModule);
+
+ if (map) {
+ const Module = require('bare-module');
+
+ const discordPkgPath = resolveDiscordPackageJsonPath();
+ if (discordPkgPath) {
+ try {
+ const opts = referrer ? { imports: map, referrer } : { imports: map };
+ const req = Module.createRequire(discordPkgPath, opts);
+ return requireDiscordDotEntry(req, discordPkgPath);
+ } catch (err) {
+ console.error('[bare-discord-js] discord bootstrap failed:', err && err.message);
+ // Fall through: packed or hoisted require('discord.js') may still work.
+ }
+ }
+
+ try {
+ const anchor = resolveCreateRequireAnchor();
+ const opts = referrer ? { imports: map, referrer } : { imports: map };
+ const req = Module.createRequire(anchor, opts);
+ return req('discord.js');
+ } catch {
+ return require('discord.js');
+ }
+ }
+
+ const imports = getImportsSpecifier();
+ try {
+ return require('discord.js', { with: { imports } });
+ } catch {
+ return require('discord.js');
+ }
+}
+
+module.exports = {
+ loadDiscordWithRuntimeMappings
+};
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.mjs b/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.mjs
new file mode 100644
index 0000000..4c98bd0
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/load-discord.mjs
@@ -0,0 +1,11 @@
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+
+/**
+ * Share the CJS bootstrap so Bare import maps, Pear referrers, and Node
+ * `require('discord.js')` stay on one path.
+ */
+export async function loadDiscordWithRuntimeMappings() {
+ return require('./load-discord.cjs').loadDiscordWithRuntimeMappings();
+}
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.cjs b/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.cjs
new file mode 100644
index 0000000..e5ffb78
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.cjs
@@ -0,0 +1,2 @@
+'use strict';
+module.exports = {"assert":{"bare":"bare-assert","default":"assert"},"node:assert":{"bare":"bare-assert","default":"assert"},"assert/strict":{"bare":"bare-assert/strict","default":"assert/strict"},"node:assert/strict":{"bare":"bare-assert/strict","default":"assert/strict"},"async_hooks":{"bare":"bare-async-hooks","default":"async_hooks"},"node:async_hooks":{"bare":"bare-async-hooks","default":"async_hooks"},"buffer":{"bare":"bare-buffer","default":"buffer"},"node:buffer":{"bare":"bare-buffer","default":"buffer"},"child_process":{"bare":"bare-subprocess","default":"child_process"},"node:child_process":{"bare":"bare-subprocess","default":"child_process"},"cluster":{"bare":"bare-node-runtime/unsupported","default":"cluster"},"node:cluster":{"bare":"bare-node-runtime/unsupported","default":"cluster"},"console":{"bare":"bare-console","default":"console"},"node:console":{"bare":"bare-console","default":"console"},"constants":{"bare":"bare-node-runtime/unsupported","default":"constants"},"node:constants":{"bare":"bare-node-runtime/unsupported","default":"constants"},"crypto":{"bare":"bare-crypto","default":"crypto"},"node:crypto":{"bare":"bare-crypto","default":"crypto"},"dgram":{"bare":"bare-dgram","default":"dgram"},"node:dgram":{"bare":"bare-dgram","default":"dgram"},"diagnostics_channel":{"bare":"bare-diagnostics-channel","default":"diagnostics_channel"},"node:diagnostics_channel":{"bare":"bare-diagnostics-channel","default":"diagnostics_channel"},"dns":{"bare":"bare-dns","default":"dns"},"node:dns":{"bare":"bare-dns","default":"dns"},"dns/promises":{"bare":"bare-dns/promises","default":"dns/promises"},"node:dns/promises":{"bare":"bare-dns/promises","default":"dns/promises"},"domain":{"bare":"bare-node-runtime/unsupported","default":"domain"},"node:domain":{"bare":"bare-node-runtime/unsupported","default":"domain"},"events":{"bare":"bare-events","default":"events"},"node:events":{"bare":"bare-events","default":"events"},"fs":{"bare":"bare-fs","default":"fs"},"node:fs":{"bare":"bare-fs","default":"fs"},"fs/promises":{"bare":"bare-fs/promises","default":"fs/promises"},"node:fs/promises":{"bare":"bare-fs/promises","default":"fs/promises"},"http":{"bare":"bare-http1","default":"http"},"node:http":{"bare":"bare-http1","default":"http"},"http2":{"bare":"bare-node-runtime/unsupported","default":"http2"},"node:http2":{"bare":"bare-node-runtime/unsupported","default":"http2"},"https":{"bare":"bare-https","default":"https"},"node:https":{"bare":"bare-https","default":"https"},"inspector":{"bare":"bare-inspector","default":"inspector"},"node:inspector":{"bare":"bare-inspector","default":"inspector"},"inspector/promises":{"bare":"bare-inspector/promises","default":"inspector/promises"},"node:inspector/promises":{"bare":"bare-inspector/promises","default":"inspector/promises"},"module":{"bare":"bare-module","default":"module"},"node:module":{"bare":"bare-module","default":"module"},"net":{"bare":"bare-net","default":"net"},"node:net":{"bare":"bare-net","default":"net"},"os":{"bare":"bare-os","default":"os"},"node:os":{"bare":"bare-os","default":"os"},"path":{"bare":"bare-path","default":"path"},"node:path":{"bare":"bare-path","default":"path"},"path/posix":{"bare":"bare-path/posix","default":"path/posix"},"node:path/posix":{"bare":"bare-path/posix","default":"path/posix"},"path/win32":{"bare":"bare-path/win32","default":"path/win32"},"node:path/win32":{"bare":"bare-path/win32","default":"path/win32"},"perf_hooks":{"bare":"bare-performance","default":"perf_hooks"},"node:perf_hooks":{"bare":"bare-performance","default":"perf_hooks"},"process":{"bare":"bare-process","default":"process"},"node:process":{"bare":"bare-process","default":"process"},"punycode":{"bare":"bare-punycode","default":"punycode"},"node:punycode":{"bare":"bare-punycode","default":"punycode"},"querystring":{"bare":"bare-querystring","default":"querystring"},"node:querystring":{"bare":"bare-querystring","default":"querystring"},"readline":{"bare":"bare-readline","default":"readline"},"node:readline":{"bare":"bare-readline","default":"readline"},"readline/promises":{"bare":"bare-readline/promises","default":"readline/promises"},"node:readline/promises":{"bare":"bare-readline/promises","default":"readline/promises"},"repl":{"bare":"bare-repl","default":"repl"},"node:repl":{"bare":"bare-repl","default":"repl"},"sea":{"bare":"bare-node-runtime/unsupported","default":"sea"},"node:sea":{"bare":"bare-node-runtime/unsupported","default":"sea"},"sqlite":{"bare":"bare-sqlite","default":"sqlite"},"node:sqlite":{"bare":"bare-sqlite","default":"sqlite"},"stream":{"bare":"bare-stream","default":"stream"},"node:stream":{"bare":"bare-stream","default":"stream"},"stream/consumers":{"bare":"bare-stream/consumers","default":"stream/consumers"},"node:stream/consumers":{"bare":"bare-stream/consumers","default":"stream/consumers"},"stream/promises":{"bare":"bare-stream/promises","default":"stream/promises"},"node:stream/promises":{"bare":"bare-stream/promises","default":"stream/promises"},"stream/web":{"bare":"bare-stream/web","default":"stream/web"},"node:stream/web":{"bare":"bare-stream/web","default":"stream/web"},"string_decoder":{"bare":"bare-string-decoder","default":"string_decoder"},"node:string_decoder":{"bare":"bare-string-decoder","default":"string_decoder"},"sys":{"bare":"bare-node-runtime/unsupported","default":"sys"},"node:sys":{"bare":"bare-node-runtime/unsupported","default":"sys"},"test":{"bare":"bare-node-runtime/unsupported","default":"test"},"node:test":{"bare":"bare-node-runtime/unsupported","default":"test"},"test/reporters":{"bare":"bare-node-runtime/unsupported","default":"test/reporters"},"node:test/reporters":{"bare":"bare-node-runtime/unsupported","default":"test/reporters"},"timers":{"bare":"bare-timers","default":"timers"},"node:timers":{"bare":"bare-timers","default":"timers"},"timers/promises":{"bare":"bare-timers/promises","default":"timers/promises"},"node:timers/promises":{"bare":"bare-timers/promises","default":"timers/promises"},"tls":{"bare":"bare-tls","default":"tls"},"node:tls":{"bare":"bare-tls","default":"tls"},"trace_events":{"bare":"bare-node-runtime/unsupported","default":"trace_events"},"node:trace_events":{"bare":"bare-node-runtime/unsupported","default":"trace_events"},"tty":{"bare":"bare-tty","default":"tty"},"node:tty":{"bare":"bare-tty","default":"tty"},"url":{"bare":"bare-url","default":"url"},"node:url":{"bare":"bare-url","default":"url"},"util":{"bare":"bare-utils","default":"util"},"node:util":{"bare":"bare-utils","default":"util"},"util/types":{"bare":"bare-utils/types","default":"util/types"},"node:util/types":{"bare":"bare-utils/types","default":"util/types"},"v8":{"bare":"bare-v8","default":"v8"},"node:v8":{"bare":"bare-v8","default":"v8"},"vm":{"bare":"bare-vm","default":"vm"},"node:vm":{"bare":"bare-vm","default":"vm"},"wasi":{"bare":"bare-node-runtime/unsupported","default":"wasi"},"node:wasi":{"bare":"bare-node-runtime/unsupported","default":"wasi"},"worker_threads":{"bare":"bare-worker","default":"worker_threads"},"node:worker_threads":{"bare":"bare-worker","default":"worker_threads"},"zlib":{"bare":"bare-zlib","default":"zlib"},"node:zlib":{"bare":"bare-zlib","default":"zlib"}};
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.json b/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.json
new file mode 100644
index 0000000..79016c6
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/node-imports-map.json
@@ -0,0 +1,466 @@
+{
+ "assert": {
+ "bare": "bare-assert",
+ "default": "assert"
+ },
+ "node:assert": {
+ "bare": "bare-assert",
+ "default": "assert"
+ },
+ "assert/strict": {
+ "bare": "bare-assert/strict",
+ "default": "assert/strict"
+ },
+ "node:assert/strict": {
+ "bare": "bare-assert/strict",
+ "default": "assert/strict"
+ },
+ "async_hooks": {
+ "bare": "bare-async-hooks",
+ "default": "async_hooks"
+ },
+ "node:async_hooks": {
+ "bare": "bare-async-hooks",
+ "default": "async_hooks"
+ },
+ "buffer": {
+ "bare": "bare-buffer",
+ "default": "buffer"
+ },
+ "node:buffer": {
+ "bare": "bare-buffer",
+ "default": "buffer"
+ },
+ "child_process": {
+ "bare": "bare-subprocess",
+ "default": "child_process"
+ },
+ "node:child_process": {
+ "bare": "bare-subprocess",
+ "default": "child_process"
+ },
+ "cluster": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "cluster"
+ },
+ "node:cluster": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "cluster"
+ },
+ "console": {
+ "bare": "bare-console",
+ "default": "console"
+ },
+ "node:console": {
+ "bare": "bare-console",
+ "default": "console"
+ },
+ "constants": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "constants"
+ },
+ "node:constants": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "constants"
+ },
+ "crypto": {
+ "bare": "bare-crypto",
+ "default": "crypto"
+ },
+ "node:crypto": {
+ "bare": "bare-crypto",
+ "default": "crypto"
+ },
+ "dgram": {
+ "bare": "bare-dgram",
+ "default": "dgram"
+ },
+ "node:dgram": {
+ "bare": "bare-dgram",
+ "default": "dgram"
+ },
+ "diagnostics_channel": {
+ "bare": "bare-diagnostics-channel",
+ "default": "diagnostics_channel"
+ },
+ "node:diagnostics_channel": {
+ "bare": "bare-diagnostics-channel",
+ "default": "diagnostics_channel"
+ },
+ "dns": {
+ "bare": "bare-dns",
+ "default": "dns"
+ },
+ "node:dns": {
+ "bare": "bare-dns",
+ "default": "dns"
+ },
+ "dns/promises": {
+ "bare": "bare-dns/promises",
+ "default": "dns/promises"
+ },
+ "node:dns/promises": {
+ "bare": "bare-dns/promises",
+ "default": "dns/promises"
+ },
+ "domain": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "domain"
+ },
+ "node:domain": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "domain"
+ },
+ "events": {
+ "bare": "bare-events",
+ "default": "events"
+ },
+ "node:events": {
+ "bare": "bare-events",
+ "default": "events"
+ },
+ "fs": {
+ "bare": "bare-fs",
+ "default": "fs"
+ },
+ "node:fs": {
+ "bare": "bare-fs",
+ "default": "fs"
+ },
+ "fs/promises": {
+ "bare": "bare-fs/promises",
+ "default": "fs/promises"
+ },
+ "node:fs/promises": {
+ "bare": "bare-fs/promises",
+ "default": "fs/promises"
+ },
+ "http": {
+ "bare": "bare-http1",
+ "default": "http"
+ },
+ "node:http": {
+ "bare": "bare-http1",
+ "default": "http"
+ },
+ "http2": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "http2"
+ },
+ "node:http2": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "http2"
+ },
+ "https": {
+ "bare": "bare-https",
+ "default": "https"
+ },
+ "node:https": {
+ "bare": "bare-https",
+ "default": "https"
+ },
+ "inspector": {
+ "bare": "bare-inspector",
+ "default": "inspector"
+ },
+ "node:inspector": {
+ "bare": "bare-inspector",
+ "default": "inspector"
+ },
+ "inspector/promises": {
+ "bare": "bare-inspector/promises",
+ "default": "inspector/promises"
+ },
+ "node:inspector/promises": {
+ "bare": "bare-inspector/promises",
+ "default": "inspector/promises"
+ },
+ "module": {
+ "bare": "bare-module",
+ "default": "module"
+ },
+ "node:module": {
+ "bare": "bare-module",
+ "default": "module"
+ },
+ "net": {
+ "bare": "bare-net",
+ "default": "net"
+ },
+ "node:net": {
+ "bare": "bare-net",
+ "default": "net"
+ },
+ "os": {
+ "bare": "bare-os",
+ "default": "os"
+ },
+ "node:os": {
+ "bare": "bare-os",
+ "default": "os"
+ },
+ "path": {
+ "bare": "bare-path",
+ "default": "path"
+ },
+ "node:path": {
+ "bare": "bare-path",
+ "default": "path"
+ },
+ "path/posix": {
+ "bare": "bare-path/posix",
+ "default": "path/posix"
+ },
+ "node:path/posix": {
+ "bare": "bare-path/posix",
+ "default": "path/posix"
+ },
+ "path/win32": {
+ "bare": "bare-path/win32",
+ "default": "path/win32"
+ },
+ "node:path/win32": {
+ "bare": "bare-path/win32",
+ "default": "path/win32"
+ },
+ "perf_hooks": {
+ "bare": "bare-performance",
+ "default": "perf_hooks"
+ },
+ "node:perf_hooks": {
+ "bare": "bare-performance",
+ "default": "perf_hooks"
+ },
+ "process": {
+ "bare": "bare-process",
+ "default": "process"
+ },
+ "node:process": {
+ "bare": "bare-process",
+ "default": "process"
+ },
+ "punycode": {
+ "bare": "bare-punycode",
+ "default": "punycode"
+ },
+ "node:punycode": {
+ "bare": "bare-punycode",
+ "default": "punycode"
+ },
+ "querystring": {
+ "bare": "bare-querystring",
+ "default": "querystring"
+ },
+ "node:querystring": {
+ "bare": "bare-querystring",
+ "default": "querystring"
+ },
+ "readline": {
+ "bare": "bare-readline",
+ "default": "readline"
+ },
+ "node:readline": {
+ "bare": "bare-readline",
+ "default": "readline"
+ },
+ "readline/promises": {
+ "bare": "bare-readline/promises",
+ "default": "readline/promises"
+ },
+ "node:readline/promises": {
+ "bare": "bare-readline/promises",
+ "default": "readline/promises"
+ },
+ "repl": {
+ "bare": "bare-repl",
+ "default": "repl"
+ },
+ "node:repl": {
+ "bare": "bare-repl",
+ "default": "repl"
+ },
+ "sea": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sea"
+ },
+ "node:sea": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sea"
+ },
+ "sqlite": {
+ "bare": "bare-sqlite",
+ "default": "sqlite"
+ },
+ "node:sqlite": {
+ "bare": "bare-sqlite",
+ "default": "sqlite"
+ },
+ "stream": {
+ "bare": "bare-stream",
+ "default": "stream"
+ },
+ "node:stream": {
+ "bare": "bare-stream",
+ "default": "stream"
+ },
+ "stream/consumers": {
+ "bare": "bare-stream/consumers",
+ "default": "stream/consumers"
+ },
+ "node:stream/consumers": {
+ "bare": "bare-stream/consumers",
+ "default": "stream/consumers"
+ },
+ "stream/promises": {
+ "bare": "bare-stream/promises",
+ "default": "stream/promises"
+ },
+ "node:stream/promises": {
+ "bare": "bare-stream/promises",
+ "default": "stream/promises"
+ },
+ "stream/web": {
+ "bare": "bare-stream/web",
+ "default": "stream/web"
+ },
+ "node:stream/web": {
+ "bare": "bare-stream/web",
+ "default": "stream/web"
+ },
+ "string_decoder": {
+ "bare": "bare-string-decoder",
+ "default": "string_decoder"
+ },
+ "node:string_decoder": {
+ "bare": "bare-string-decoder",
+ "default": "string_decoder"
+ },
+ "sys": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sys"
+ },
+ "node:sys": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "sys"
+ },
+ "test": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test"
+ },
+ "node:test": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test"
+ },
+ "test/reporters": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test/reporters"
+ },
+ "node:test/reporters": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "test/reporters"
+ },
+ "timers": {
+ "bare": "bare-timers",
+ "default": "timers"
+ },
+ "node:timers": {
+ "bare": "bare-timers",
+ "default": "timers"
+ },
+ "timers/promises": {
+ "bare": "bare-timers/promises",
+ "default": "timers/promises"
+ },
+ "node:timers/promises": {
+ "bare": "bare-timers/promises",
+ "default": "timers/promises"
+ },
+ "tls": {
+ "bare": "bare-tls",
+ "default": "tls"
+ },
+ "node:tls": {
+ "bare": "bare-tls",
+ "default": "tls"
+ },
+ "trace_events": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "trace_events"
+ },
+ "node:trace_events": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "trace_events"
+ },
+ "tty": {
+ "bare": "bare-tty",
+ "default": "tty"
+ },
+ "node:tty": {
+ "bare": "bare-tty",
+ "default": "tty"
+ },
+ "url": {
+ "bare": "bare-url",
+ "default": "url"
+ },
+ "node:url": {
+ "bare": "bare-url",
+ "default": "url"
+ },
+ "util": {
+ "bare": "bare-utils",
+ "default": "util"
+ },
+ "node:util": {
+ "bare": "bare-utils",
+ "default": "util"
+ },
+ "util/types": {
+ "bare": "bare-utils/types",
+ "default": "util/types"
+ },
+ "node:util/types": {
+ "bare": "bare-utils/types",
+ "default": "util/types"
+ },
+ "v8": {
+ "bare": "bare-v8",
+ "default": "v8"
+ },
+ "node:v8": {
+ "bare": "bare-v8",
+ "default": "v8"
+ },
+ "vm": {
+ "bare": "bare-vm",
+ "default": "vm"
+ },
+ "node:vm": {
+ "bare": "bare-vm",
+ "default": "vm"
+ },
+ "wasi": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "wasi"
+ },
+ "node:wasi": {
+ "bare": "bare-node-runtime/unsupported",
+ "default": "wasi"
+ },
+ "worker_threads": {
+ "bare": "bare-worker",
+ "default": "worker_threads"
+ },
+ "node:worker_threads": {
+ "bare": "bare-worker",
+ "default": "worker_threads"
+ },
+ "zlib": {
+ "bare": "bare-zlib",
+ "default": "zlib"
+ },
+ "node:zlib": {
+ "bare": "bare-zlib",
+ "default": "zlib"
+ }
+}
diff --git a/native-host/vendor/bare-discord-js/src/bootstrap/preload-bare-modules.cjs b/native-host/vendor/bare-discord-js/src/bootstrap/preload-bare-modules.cjs
new file mode 100644
index 0000000..b1b3a66
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/bootstrap/preload-bare-modules.cjs
@@ -0,0 +1,58 @@
+/**
+ * Force the Pear bundler to statically include the Bare modules referenced by the runtime
+ * Node→Bare imports map (`./node-imports-map.cjs`). Without these *literal-string* requires
+ * the bundler's static analysis cannot trace `require('node:util')` → `bare-utils` etc., so
+ * `pear stage` skips them and `pear run pear://…` fails with MODULE_NOT_FOUND for `node:util`.
+ *
+ * Only modules actually used by discord.js's transitive deps via the imports map are listed.
+ * Side-effecting Bare modules (`bare-worker` instantiates a Thread on first require,
+ * `bare-vm`/`bare-v8`/`bare-inspector` load native bindings unconditionally) are intentionally
+ * omitted — pulling them in here breaks app startup even when discord.js never asks for them.
+ *
+ * Sub-paths must match each package's `exports` field; e.g. `bare-assert` has no `./strict`,
+ * `bare-dns` has no `./promises`. Including a non-exported subpath causes
+ * `PACKAGE_PATH_NOT_EXPORTED` during `pear stage`.
+ */
+
+'use strict';
+
+try { require('bare-assert'); } catch {}
+try { require('bare-async-hooks'); } catch {}
+try { require('bare-buffer'); } catch {}
+try { require('bare-console'); } catch {}
+try { require('bare-crypto'); } catch {}
+try { require('bare-diagnostics-channel'); } catch {}
+try { require('bare-encoding'); } catch {}
+try { require('bare-events'); } catch {}
+try { require('bare-fetch'); } catch {}
+try { require('bare-form-data'); } catch {}
+try { require('bare-form-data/global'); } catch {}
+try { require('bare-fs'); } catch {}
+try { require('bare-fs/promises'); } catch {}
+try { require('bare-fs/constants'); } catch {}
+try { require('bare-http1'); } catch {}
+try { require('bare-https'); } catch {}
+try { require('bare-module'); } catch {}
+try { require('bare-net'); } catch {}
+try { require('bare-os'); } catch {}
+try { require('bare-path'); } catch {}
+try { require('bare-path/posix'); } catch {}
+try { require('bare-path/win32'); } catch {}
+try { require('bare-performance'); } catch {}
+try { require('bare-process'); } catch {}
+try { require('bare-querystring'); } catch {}
+try { require('bare-stream'); } catch {}
+try { require('bare-stream/promises'); } catch {}
+try { require('bare-stream/web'); } catch {}
+try { require('bare-string-decoder'); } catch {}
+try { require('bare-timers'); } catch {}
+try { require('bare-timers/promises'); } catch {}
+try { require('bare-tls'); } catch {}
+try { require('bare-url'); } catch {}
+try { require('bare-utils'); } catch {}
+try { require('bare-utils/types'); } catch {}
+try { require('bare-ws'); } catch {}
+try { require('bare-zlib'); } catch {}
+try { require('bare-node-runtime/unsupported'); } catch {}
+
+module.exports = {};
diff --git a/native-host/vendor/bare-discord-js/src/index.js b/native-host/vendor/bare-discord-js/src/index.js
new file mode 100644
index 0000000..6e52a2e
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/index.js
@@ -0,0 +1,12 @@
+const { setupBareGlobals } = require('./bootstrap/global.cjs');
+const { loadDiscordWithRuntimeMappings } = require('./bootstrap/load-discord.cjs');
+const { applyRuntimeAdapters, applyDiscordJsCompatShims } = require('./adapters/index.cjs');
+
+require('./bootstrap/preload-bare-modules.cjs');
+
+setupBareGlobals();
+applyRuntimeAdapters();
+
+const discord = applyDiscordJsCompatShims(loadDiscordWithRuntimeMappings(module));
+
+module.exports = discord;
diff --git a/native-host/vendor/bare-discord-js/src/index.mjs b/native-host/vendor/bare-discord-js/src/index.mjs
new file mode 100644
index 0000000..bb653ca
--- /dev/null
+++ b/native-host/vendor/bare-discord-js/src/index.mjs
@@ -0,0 +1,30 @@
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+const discord = require('./index.js');
+
+export default discord;
+export const {
+ ActionRowBuilder,
+ AttachmentBuilder,
+ ButtonBuilder,
+ ChannelType,
+ Client,
+ Collection,
+ EmbedBuilder,
+ Events,
+ GatewayIntentBits,
+ IntentsBitField,
+ MessageFlags,
+ ModalBuilder,
+ Options,
+ Partials,
+ PermissionsBitField,
+ REST,
+ Routes,
+ ShardingManager,
+ SlashCommandBuilder,
+ StringSelectMenuBuilder,
+ TextInputBuilder,
+ version
+} = discord;
diff --git a/package.json b/package.json
index 30a808c..9a52900 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"build:dist:package": "node scripts/build-distributable.js --all --package",
"build:dist:media": "node scripts/build-distributable.js --package",
"install:capability:media": "bash scripts/install-capability-media.sh",
+ "vendor:discord": "node scripts/vendor-bare-discord-js.mjs",
"test": "node scripts/test-host-units.js && npm --prefix native-host run test:bare",
"test:qvac-smoke": "npm --prefix native-host run test:qvac-smoke"
},
diff --git a/scripts/serve-examples.js b/scripts/serve-examples.js
index 2b8fcba..1a20099 100755
--- a/scripts/serve-examples.js
+++ b/scripts/serve-examples.js
@@ -110,6 +110,9 @@ server.listen(PORT, HOST, () => {
console.log(` ${base}/whiteboard/`);
console.log(` ${base}/screenshare/`);
console.log(` ${base}/data-demo/`);
+ console.log(` ${base}/qvac-chat/`);
+ console.log(` ${base}/agent-studio/`);
+ console.log(` ${base}/discord-bot/`);
console.log('');
console.log('Do not open examples via file:// — Chrome treats each file as a unique origin.');
console.log('Press Ctrl+C to stop.');
diff --git a/scripts/test-host-units.js b/scripts/test-host-units.js
index 502ddd7..e7b555a 100644
--- a/scripts/test-host-units.js
+++ b/scripts/test-host-units.js
@@ -123,10 +123,48 @@ function testOriginAllowlist() {
assert.strictEqual(allow.isQvacDisabled({ type: 'capability', payload: { pack: 'agent', cmd: 'create' } }, {}), true);
assert.strictEqual(allow.isQvacDisabled({ type: 'capability', payload: { pack: 'agent', cmd: 'status' } }, {}), false);
assert.strictEqual(allow.isQvacDisabled({ type: 'fs.write' }, {}), false);
+ assert.strictEqual(allow.isCapabilityType('discord.construct'), true);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'discord.construct' }, {}), true);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'discord.surface' }, {}), false);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'discord.status' }, {}), false);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'discord.construct' }, { discordEnabled: true }), false);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'discord.setEnabled' }, { discordEnabled: true }), true);
+ assert.strictEqual(allow.isDiscordDisabled({ type: 'capability', payload: { pack: 'discord', cmd: 'login' } }, {}), true);
}
testOriginAllowlist();
+function testDiscordSnapshot() {
+ const snap = require('../native-host/discord/snapshot.js');
+ const { normalizeDiscordToken } = require('../native-host/discord/load-discord.js');
+ assert.strictEqual(normalizeDiscordToken(' abc.def \n'), 'abc.def');
+ assert.strictEqual(normalizeDiscordToken('\uFEFFtok'), 'tok');
+ const handles = snap.createHandleTable();
+ const ix = {
+ commandName: 'ping',
+ isChatInputCommand() { return true; },
+ isButton() { return false; },
+ reply() { return Promise.resolve(); },
+ toJSON() { return { id: '1', commandName: 'ping' }; },
+ user: { id: '99', tag: 'u#0001' },
+ };
+ Object.defineProperty(ix, 'constructor', { value: { name: 'ChatInputCommandInteraction' } });
+ const s = snap.snapshotValue(ix, handles, 0);
+ assert.ok(s._handle, 'handle interned');
+ assert.strictEqual(s.isChatInputCommand, true);
+ assert.strictEqual(s.commandName, 'ping');
+ assert.ok(s._methods.indexOf('reply') >= 0, 'reply method listed');
+ const constants = snap.serializeConstants({
+ GatewayIntentBits: { Guilds: 1, DirectMessages: 4096 },
+ Client: function Client() {},
+ Events: { ClientReady: 'clientReady' },
+ });
+ assert.strictEqual(constants.GatewayIntentBits.Guilds, 1);
+ assert.ok(constants._classes.indexOf('Client') >= 0);
+}
+
+testDiscordSnapshot();
+
function testQvacCatalog() {
const cat = require('../native-host/qvac/catalog.js');
assert.strictEqual(cat.resolveModelConstant('qwen3.5-4b'), 'QWEN3_5_4B_MULTIMODAL_Q4_K_M');
diff --git a/scripts/vendor-bare-discord-js.mjs b/scripts/vendor-bare-discord-js.mjs
new file mode 100644
index 0000000..4b9040d
--- /dev/null
+++ b/scripts/vendor-bare-discord-js.mjs
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+/**
+ * Copy bare-discord-js sources into native-host/vendor/bare-discord-js
+ * (no node_modules). Replaces any previous vendor tree, then inlines FormData.
+ *
+ * Env: BARE_OS_BARE_DISCORD_JS_SRC — default /home/raven/dev/bare-discord-js
+ * Fallback: Bare OS vendor at ../bare-operating-system/... if present.
+ */
+import { execFileSync } from 'node:child_process';
+import fs from 'node:fs';
+import path from 'node:path';
+import process from 'node:process';
+import { fileURLToPath } from 'node:url';
+
+const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
+const dest = path.join(root, 'native-host', 'vendor', 'bare-discord-js');
+const defaultSrc = '/home/raven/dev/bare-discord-js';
+const bareOsVendor = '/home/raven/dev/bare-operating-system/packages/bare-os-booter/vendor/bare-discord-js';
+const src = String(
+ process.env.BARE_OS_BARE_DISCORD_JS_SRC ||
+ (fs.existsSync(path.join(defaultSrc, 'package.json')) ? defaultSrc : bareOsVendor)
+).trim();
+
+if (!fs.existsSync(path.join(src, 'package.json'))) {
+ console.error('vendor-bare-discord-js: missing source:', src);
+ process.exit(1);
+}
+
+const srcPkg = JSON.parse(fs.readFileSync(path.join(src, 'package.json'), 'utf8'));
+if (srcPkg.name !== 'bare-discord-js') {
+ console.error('vendor-bare-discord-js: unexpected package name:', srcPkg.name);
+ process.exit(1);
+}
+
+fs.mkdirSync(path.dirname(dest), { recursive: true });
+execFileSync(
+ 'rsync',
+ [
+ '-a',
+ '--delete',
+ '--exclude',
+ 'node_modules',
+ '--exclude',
+ '.git',
+ '--exclude',
+ 'artifacts',
+ '--exclude',
+ 'examples',
+ '--exclude',
+ 'test',
+ '--exclude',
+ 'src/bundles',
+ '--exclude',
+ '*.tgz',
+ '--exclude',
+ 'package-lock.json',
+ path.join(src, '/'),
+ dest + path.sep,
+ ],
+ { stdio: 'inherit' }
+);
+
+const formDataSrc = path.join(root, 'native-host', 'vendor', 'bare-discord-js', 'src', 'adapters', 'form-data.cjs');
+const installer = path.join(
+ '/home/raven/dev/bare-operating-system/packages/bare-os-booter/lib/services/bare-os-discord-form-data.cjs'
+);
+if (fs.existsSync(formDataSrc)) {
+ const cur = fs.readFileSync(formDataSrc, 'utf8');
+ if (cur.includes('../../../../lib/services/bare-os-discord-form-data.cjs')) {
+ if (fs.existsSync(installer)) {
+ fs.writeFileSync(formDataSrc, fs.readFileSync(installer));
+ console.log('vendor-bare-discord-js: inlined FormData installer');
+ }
+ }
+}
+
+console.log('vendor-bare-discord-js: synced →', dest, `(${srcPkg.version})`);