From 232f7997dfacedb4a8e2d488f9d1616c6e01fba8 Mon Sep 17 00:00:00 2001 From: ruvnet Date: Wed, 4 Nov 2026 21:23:19 -0500 Subject: [PATCH 1/2] ruvbot 0.3.6: fix ESM import, web UI sends the API key ESM: `import('ruvbot')` failed in 0.3.4/0.3.5 for two reasons. dist/esm had no package.json with {"type":"module"} (the package root is CommonJS), and src/core/index.ts and src/integration/index.ts used directory re-exports ('./agent', './slack', ...) that tsc's "bundler" resolution accepts but Node ESM rejects ("Directory import ... is not supported"). The re-exports now name '/index.js', and scripts/fix-esm.js runs after build:esm: it writes dist/esm/package.json and fails the build if any relative ESM specifier does not resolve to a file. UI: when RUVBOT_API_KEY is set, /api/* needs "Authorization: Bearer ", which the bundled chat UI never sent. Its /api calls now go through a small client that, on a 401, asks for the key once, keeps it in sessionStorage and sends it as a Bearer header (never in a URL). With no key set server-side the UI behaves as before. Tests: tests/integration/package/pack-exports.test.ts packs the tarball, installs it in a scratch project and loads ruvbot, ./core, ./learning, ./integrations/slack and ./integrations/webhooks through require() and import(). tests/unit/api/ui-api-client.test.ts runs the UI client from the shipped index.html against a fake server. Co-Authored-By: claude-flow --- npm/packages/ruvbot/package.json | 4 +- npm/packages/ruvbot/scripts/fix-esm.js | 65 ++++++++ npm/packages/ruvbot/src/api/public/index.html | 103 +++++++++++- npm/packages/ruvbot/src/core/index.ts | 6 +- npm/packages/ruvbot/src/integration/index.ts | 6 +- .../integration/package/pack-exports.test.ts | 114 +++++++++++++ .../tests/unit/api/ui-api-client.test.ts | 155 ++++++++++++++++++ 7 files changed, 442 insertions(+), 11 deletions(-) create mode 100644 npm/packages/ruvbot/scripts/fix-esm.js create mode 100644 npm/packages/ruvbot/tests/integration/package/pack-exports.test.ts create mode 100644 npm/packages/ruvbot/tests/unit/api/ui-api-client.test.ts diff --git a/npm/packages/ruvbot/package.json b/npm/packages/ruvbot/package.json index 28294ab2f0..1a54d16b23 100644 --- a/npm/packages/ruvbot/package.json +++ b/npm/packages/ruvbot/package.json @@ -1,6 +1,6 @@ { "name": "ruvbot", - "version": "0.3.5", + "version": "0.3.6", "description": "Enterprise-grade self-learning AI assistant with military-strength security, 150x faster vector search, and 12+ LLM models", "main": "dist/index.js", "module": "dist/esm/index.js", @@ -39,7 +39,7 @@ "scripts": { "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc -p tsconfig.json", - "build:esm": "tsc -p tsconfig.esm.json", + "build:esm": "tsc -p tsconfig.esm.json && node scripts/fix-esm.js", "build:rvf": "node scripts/build-rvf.js", "run:rvf": "node scripts/run-rvf.js", "inspect:rvf": "node scripts/run-rvf.js --inspect", diff --git a/npm/packages/ruvbot/scripts/fix-esm.js b/npm/packages/ruvbot/scripts/fix-esm.js new file mode 100644 index 0000000000..69d59d8528 --- /dev/null +++ b/npm/packages/ruvbot/scripts/fix-esm.js @@ -0,0 +1,65 @@ +#!/usr/bin/env node +/** + * Post-build step for the ESM output (dist/esm). + * + * The package root is CommonJS (no "type" field), so Node treats every .js + * file as CJS unless a nearer package.json says otherwise. tsc does not emit + * one, which made `import('ruvbot')` fail. This script: + * + * 1. writes dist/esm/package.json with {"type":"module"} + * 2. verifies that every relative specifier in dist/esm resolves to a real + * file (ESM forbids extensionless and directory imports) and fails the + * build if one does not, so the bug cannot silently come back. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const esmDir = path.resolve(__dirname, '..', 'dist', 'esm'); + +if (!fs.existsSync(esmDir)) { + console.error(`fix-esm: ${esmDir} does not exist; run build:esm first`); + process.exit(1); +} + +fs.writeFileSync( + path.join(esmDir, 'package.json'), + JSON.stringify({ type: 'module' }, null, 2) + '\n' +); + +const SPECIFIER_RE = + /(?:\bfrom\s*|\bimport\s*\(\s*|^\s*import\s+)(['"])(\.{1,2}\/[^'"]*)\1/gm; + +function walk(dir, out) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walk(full, out); + else if (entry.name.endsWith('.js')) out.push(full); + } + return out; +} + +const problems = []; +for (const file of walk(esmDir, [])) { + const src = fs.readFileSync(file, 'utf8'); + for (const match of src.matchAll(SPECIFIER_RE)) { + const spec = match[2]; + const target = path.resolve(path.dirname(file), spec); + let ok = false; + try { + ok = fs.statSync(target).isFile(); + } catch { + ok = false; + } + if (!ok) problems.push(`${path.relative(esmDir, file)}: '${spec}'`); + } +} + +if (problems.length > 0) { + console.error('fix-esm: unresolvable relative ESM specifiers (use explicit ".js" / "/index.js"):'); + for (const p of problems) console.error(` ${p}`); + process.exit(1); +} + +console.log('fix-esm: wrote dist/esm/package.json; all relative ESM specifiers resolve'); diff --git a/npm/packages/ruvbot/src/api/public/index.html b/npm/packages/ruvbot/src/api/public/index.html index 0233c6c072..bd2df5eafe 100644 --- a/npm/packages/ruvbot/src/api/public/index.html +++ b/npm/packages/ruvbot/src/api/public/index.html @@ -664,6 +664,98 @@

Welcome to RuvBot

Health + +